diff options
-rw-r--r-- | .builds/alpine.yml | 1 | ||||
-rw-r--r-- | .builds/archlinux.yml | 1 | ||||
-rw-r--r-- | .builds/freebsd.yml | 1 | ||||
-rw-r--r-- | include/sway/commands.h | 1 | ||||
-rw-r--r-- | include/sway/config.h | 3 | ||||
-rw-r--r-- | include/sway/output.h | 2 | ||||
-rw-r--r-- | sway/commands/output.c | 1 | ||||
-rw-r--r-- | sway/commands/output/color_profile.c | 101 | ||||
-rw-r--r-- | sway/config/output.c | 18 | ||||
-rw-r--r-- | sway/desktop/output.c | 5 | ||||
-rw-r--r-- | sway/meson.build | 1 | ||||
-rw-r--r-- | sway/sway-output.5.scd | 12 | ||||
-rw-r--r-- | sway/tree/output.c | 1 |
13 files changed, 147 insertions, 1 deletions
diff --git a/.builds/alpine.yml b/.builds/alpine.yml index 055e5ffa..7a1fa58e 100644 --- a/.builds/alpine.yml +++ b/.builds/alpine.yml | |||
@@ -4,6 +4,7 @@ packages: | |||
4 | - eudev-dev | 4 | - eudev-dev |
5 | - gdk-pixbuf-dev | 5 | - gdk-pixbuf-dev |
6 | - json-c-dev | 6 | - json-c-dev |
7 | - lcms2-dev | ||
7 | - libdisplay-info-dev | 8 | - libdisplay-info-dev |
8 | - libevdev-dev | 9 | - libevdev-dev |
9 | - libinput-dev | 10 | - libinput-dev |
diff --git a/.builds/archlinux.yml b/.builds/archlinux.yml index 9972c01a..e249571e 100644 --- a/.builds/archlinux.yml +++ b/.builds/archlinux.yml | |||
@@ -3,6 +3,7 @@ packages: | |||
3 | - cairo | 3 | - cairo |
4 | - gdk-pixbuf2 | 4 | - gdk-pixbuf2 |
5 | - json-c | 5 | - json-c |
6 | - lcms2 | ||
6 | - libdisplay-info | 7 | - libdisplay-info |
7 | - libegl | 8 | - libegl |
8 | - libinput | 9 | - libinput |
diff --git a/.builds/freebsd.yml b/.builds/freebsd.yml index 29c6312a..8084574c 100644 --- a/.builds/freebsd.yml +++ b/.builds/freebsd.yml | |||
@@ -8,6 +8,7 @@ packages: | |||
8 | - devel/pkgconf | 8 | - devel/pkgconf |
9 | - graphics/cairo | 9 | - graphics/cairo |
10 | - graphics/gdk-pixbuf2 | 10 | - graphics/gdk-pixbuf2 |
11 | - graphics/lcms2 | ||
11 | - graphics/wayland | 12 | - graphics/wayland |
12 | - graphics/wayland-protocols | 13 | - graphics/wayland-protocols |
13 | - textproc/scdoc | 14 | - textproc/scdoc |
diff --git a/include/sway/commands.h b/include/sway/commands.h index 27058587..0a9fdc70 100644 --- a/include/sway/commands.h +++ b/include/sway/commands.h | |||
@@ -283,6 +283,7 @@ sway_cmd input_cmd_xkb_variant; | |||
283 | 283 | ||
284 | sway_cmd output_cmd_adaptive_sync; | 284 | sway_cmd output_cmd_adaptive_sync; |
285 | sway_cmd output_cmd_background; | 285 | sway_cmd output_cmd_background; |
286 | sway_cmd output_cmd_color_profile; | ||
286 | sway_cmd output_cmd_disable; | 287 | sway_cmd output_cmd_disable; |
287 | sway_cmd output_cmd_dpms; | 288 | sway_cmd output_cmd_dpms; |
288 | sway_cmd output_cmd_enable; | 289 | sway_cmd output_cmd_enable; |
diff --git a/include/sway/config.h b/include/sway/config.h index 5ccc3e77..3e3a104e 100644 --- a/include/sway/config.h +++ b/include/sway/config.h | |||
@@ -7,6 +7,7 @@ | |||
7 | #include <wlr/interfaces/wlr_switch.h> | 7 | #include <wlr/interfaces/wlr_switch.h> |
8 | #include <wlr/types/wlr_tablet_tool.h> | 8 | #include <wlr/types/wlr_tablet_tool.h> |
9 | #include <wlr/util/box.h> | 9 | #include <wlr/util/box.h> |
10 | #include <wlr/render/color.h> | ||
10 | #include <xkbcommon/xkbcommon.h> | 11 | #include <xkbcommon/xkbcommon.h> |
11 | #include <xf86drmMode.h> | 12 | #include <xf86drmMode.h> |
12 | #include "../include/config.h" | 13 | #include "../include/config.h" |
@@ -285,6 +286,8 @@ struct output_config { | |||
285 | int max_render_time; // In milliseconds | 286 | int max_render_time; // In milliseconds |
286 | int adaptive_sync; | 287 | int adaptive_sync; |
287 | enum render_bit_depth render_bit_depth; | 288 | enum render_bit_depth render_bit_depth; |
289 | bool set_color_transform; | ||
290 | struct wlr_color_transform *color_transform; | ||
288 | 291 | ||
289 | char *background; | 292 | char *background; |
290 | char *background_option; | 293 | char *background_option; |
diff --git a/include/sway/output.h b/include/sway/output.h index d546d488..2189c6e8 100644 --- a/include/sway/output.h +++ b/include/sway/output.h | |||
@@ -66,6 +66,8 @@ struct sway_output { | |||
66 | struct wl_signal disable; | 66 | struct wl_signal disable; |
67 | } events; | 67 | } events; |
68 | 68 | ||
69 | struct wlr_color_transform *color_transform; | ||
70 | |||
69 | struct timespec last_presentation; | 71 | struct timespec last_presentation; |
70 | uint32_t refresh_nsec; | 72 | uint32_t refresh_nsec; |
71 | int max_render_time; // In milliseconds | 73 | int max_render_time; // In milliseconds |
diff --git a/sway/commands/output.c b/sway/commands/output.c index 5e5d31b3..b822e770 100644 --- a/sway/commands/output.c +++ b/sway/commands/output.c | |||
@@ -10,6 +10,7 @@ static const struct cmd_handler output_handlers[] = { | |||
10 | { "adaptive_sync", output_cmd_adaptive_sync }, | 10 | { "adaptive_sync", output_cmd_adaptive_sync }, |
11 | { "background", output_cmd_background }, | 11 | { "background", output_cmd_background }, |
12 | { "bg", output_cmd_background }, | 12 | { "bg", output_cmd_background }, |
13 | { "color_profile", output_cmd_color_profile }, | ||
13 | { "disable", output_cmd_disable }, | 14 | { "disable", output_cmd_disable }, |
14 | { "dpms", output_cmd_dpms }, | 15 | { "dpms", output_cmd_dpms }, |
15 | { "enable", output_cmd_enable }, | 16 | { "enable", output_cmd_enable }, |
diff --git a/sway/commands/output/color_profile.c b/sway/commands/output/color_profile.c new file mode 100644 index 00000000..792bd55f --- /dev/null +++ b/sway/commands/output/color_profile.c | |||
@@ -0,0 +1,101 @@ | |||
1 | #include <fcntl.h> | ||
2 | #include <strings.h> | ||
3 | #include <sys/stat.h> | ||
4 | #include <unistd.h> | ||
5 | #include <wlr/render/color.h> | ||
6 | #include "sway/commands.h" | ||
7 | #include "sway/config.h" | ||
8 | |||
9 | static bool read_file_into_buf(const char *path, void **buf, size_t *size) { | ||
10 | /* Why not use fopen/fread directly? glibc will succesfully open directories, | ||
11 | * not just files, and supports seeking on them. Instead, we directly | ||
12 | * work with file descriptors and use the more consistent open/fstat/read. */ | ||
13 | int fd = open(path, O_RDONLY | O_NOCTTY | O_CLOEXEC); | ||
14 | if (fd == -1) { | ||
15 | return false; | ||
16 | } | ||
17 | char *b = NULL; | ||
18 | struct stat info; | ||
19 | if (fstat(fd, &info) == -1) { | ||
20 | goto fail; | ||
21 | } | ||
22 | // only regular files, to avoid issues with e.g. opening pipes | ||
23 | if (!S_ISREG(info.st_mode)) { | ||
24 | goto fail; | ||
25 | } | ||
26 | off_t s = info.st_size; | ||
27 | if (s <= 0) { | ||
28 | goto fail; | ||
29 | } | ||
30 | b = calloc(1, s); | ||
31 | if (!b) { | ||
32 | goto fail; | ||
33 | } | ||
34 | size_t nread = 0; | ||
35 | while (nread < (size_t)s) { | ||
36 | size_t to_read = (size_t)s - nread; | ||
37 | ssize_t r = read(fd, b + nread, to_read); | ||
38 | if ((r == -1 && errno != EINTR) || r == 0) { | ||
39 | goto fail; | ||
40 | } | ||
41 | nread += (size_t)r; | ||
42 | } | ||
43 | close(fd); | ||
44 | *buf = b; | ||
45 | *size = (size_t)s; | ||
46 | return true; // success | ||
47 | fail: | ||
48 | free(b); | ||
49 | close(fd); | ||
50 | return false; | ||
51 | } | ||
52 | |||
53 | struct cmd_results *output_cmd_color_profile(int argc, char **argv) { | ||
54 | if (!config->handler_context.output_config) { | ||
55 | return cmd_results_new(CMD_FAILURE, "Missing output config"); | ||
56 | } | ||
57 | if (!argc) { | ||
58 | return cmd_results_new(CMD_INVALID, "Missing color_profile first argument."); | ||
59 | } | ||
60 | |||
61 | if (strcmp(*argv, "srgb") == 0) { | ||
62 | wlr_color_transform_unref(config->handler_context.output_config->color_transform); | ||
63 | config->handler_context.output_config->color_transform = NULL; | ||
64 | config->handler_context.output_config->set_color_transform = true; | ||
65 | |||
66 | config->handler_context.leftovers.argc = argc - 1; | ||
67 | config->handler_context.leftovers.argv = argv + 1; | ||
68 | } else if (strcmp(*argv, "icc") == 0) { | ||
69 | if (argc < 2) { | ||
70 | return cmd_results_new(CMD_INVALID, | ||
71 | "Invalid color profile specification: icc type requires a file"); | ||
72 | } | ||
73 | void *data = NULL; | ||
74 | size_t size = 0; | ||
75 | if (!read_file_into_buf(argv[1], &data, &size)) { | ||
76 | return cmd_results_new(CMD_FAILURE, | ||
77 | "Failed to load color profile: could not read ICC file"); | ||
78 | } | ||
79 | |||
80 | struct wlr_color_transform *tmp = | ||
81 | wlr_color_transform_init_linear_to_icc(data, size); | ||
82 | if (!tmp) { | ||
83 | free(data); | ||
84 | return cmd_results_new(CMD_FAILURE, | ||
85 | "Failed to load color profile: failed to initialize transform from ICC"); | ||
86 | } | ||
87 | free(data); | ||
88 | |||
89 | wlr_color_transform_unref(config->handler_context.output_config->color_transform); | ||
90 | config->handler_context.output_config->color_transform = tmp; | ||
91 | config->handler_context.output_config->set_color_transform = true; | ||
92 | |||
93 | config->handler_context.leftovers.argc = argc - 2; | ||
94 | config->handler_context.leftovers.argv = argv + 2; | ||
95 | } else { | ||
96 | return cmd_results_new(CMD_INVALID, | ||
97 | "Invalid color profile specification: first argument should be icc|srgb"); | ||
98 | } | ||
99 | |||
100 | return NULL; | ||
101 | } | ||
diff --git a/sway/config/output.c b/sway/config/output.c index 9a447388..bcd21b9b 100644 --- a/sway/config/output.c +++ b/sway/config/output.c | |||
@@ -76,6 +76,8 @@ struct output_config *new_output_config(const char *name) { | |||
76 | oc->max_render_time = -1; | 76 | oc->max_render_time = -1; |
77 | oc->adaptive_sync = -1; | 77 | oc->adaptive_sync = -1; |
78 | oc->render_bit_depth = RENDER_BIT_DEPTH_DEFAULT; | 78 | oc->render_bit_depth = RENDER_BIT_DEPTH_DEFAULT; |
79 | oc->set_color_transform = false; | ||
80 | oc->color_transform = NULL; | ||
79 | oc->power = -1; | 81 | oc->power = -1; |
80 | return oc; | 82 | return oc; |
81 | } | 83 | } |
@@ -191,6 +193,14 @@ static void merge_output_config(struct output_config *dst, struct output_config | |||
191 | if (src->render_bit_depth != RENDER_BIT_DEPTH_DEFAULT) { | 193 | if (src->render_bit_depth != RENDER_BIT_DEPTH_DEFAULT) { |
192 | dst->render_bit_depth = src->render_bit_depth; | 194 | dst->render_bit_depth = src->render_bit_depth; |
193 | } | 195 | } |
196 | if (src->set_color_transform) { | ||
197 | if (src->color_transform) { | ||
198 | wlr_color_transform_ref(src->color_transform); | ||
199 | } | ||
200 | wlr_color_transform_unref(dst->color_transform); | ||
201 | dst->set_color_transform = true; | ||
202 | dst->color_transform = src->color_transform; | ||
203 | } | ||
194 | if (src->background) { | 204 | if (src->background) { |
195 | free(dst->background); | 205 | free(dst->background); |
196 | dst->background = strdup(src->background); | 206 | dst->background = strdup(src->background); |
@@ -557,6 +567,13 @@ static bool finalize_output_config(struct output_config *oc, struct sway_output | |||
557 | output->max_render_time = oc->max_render_time; | 567 | output->max_render_time = oc->max_render_time; |
558 | } | 568 | } |
559 | 569 | ||
570 | if (oc && oc->set_color_transform) { | ||
571 | if (oc->color_transform) { | ||
572 | wlr_color_transform_ref(oc->color_transform); | ||
573 | } | ||
574 | wlr_color_transform_unref(output->color_transform); | ||
575 | output->color_transform = oc->color_transform; | ||
576 | } | ||
560 | return true; | 577 | return true; |
561 | } | 578 | } |
562 | 579 | ||
@@ -997,6 +1014,7 @@ void free_output_config(struct output_config *oc) { | |||
997 | free(oc->name); | 1014 | free(oc->name); |
998 | free(oc->background); | 1015 | free(oc->background); |
999 | free(oc->background_option); | 1016 | free(oc->background_option); |
1017 | wlr_color_transform_unref(oc->color_transform); | ||
1000 | free(oc); | 1018 | free(oc); |
1001 | } | 1019 | } |
1002 | 1020 | ||
diff --git a/sway/desktop/output.c b/sway/desktop/output.c index 2722e556..cfa53021 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c | |||
@@ -269,7 +269,10 @@ static int output_repaint_timer_handler(void *data) { | |||
269 | return 0; | 269 | return 0; |
270 | } | 270 | } |
271 | 271 | ||
272 | wlr_scene_output_commit(output->scene_output, NULL); | 272 | struct wlr_scene_output_state_options opts = { |
273 | .color_transform = output->color_transform, | ||
274 | }; | ||
275 | wlr_scene_output_commit(output->scene_output, &opts); | ||
273 | return 0; | 276 | return 0; |
274 | } | 277 | } |
275 | 278 | ||
diff --git a/sway/meson.build b/sway/meson.build index 47b51d0c..a189fe9a 100644 --- a/sway/meson.build +++ b/sway/meson.build | |||
@@ -202,6 +202,7 @@ sway_sources = files( | |||
202 | 'commands/output/toggle.c', | 202 | 'commands/output/toggle.c', |
203 | 'commands/output/transform.c', | 203 | 'commands/output/transform.c', |
204 | 'commands/output/unplug.c', | 204 | 'commands/output/unplug.c', |
205 | 'commands/output/color_profile.c', | ||
205 | 206 | ||
206 | 'tree/arrange.c', | 207 | 'tree/arrange.c', |
207 | 'tree/container.c', | 208 | 'tree/container.c', |
diff --git a/sway/sway-output.5.scd b/sway/sway-output.5.scd index 7d088d5d..6d7c0860 100644 --- a/sway/sway-output.5.scd +++ b/sway/sway-output.5.scd | |||
@@ -178,6 +178,18 @@ must be separated by one space. For example: | |||
178 | updated to work with different bit depths. This command is experimental, | 178 | updated to work with different bit depths. This command is experimental, |
179 | and may be removed or changed in the future. | 179 | and may be removed or changed in the future. |
180 | 180 | ||
181 | *output* <name> color_profile srgb|[icc <file>] | ||
182 | Sets the color profile for an output. The default is _srgb_. <file> should be a | ||
183 | path to a display ICC profile. | ||
184 | |||
185 | Not all renderers support this feature; currently it only works with the | ||
186 | the Vulkan renderer. Even where supported, the application of the color | ||
187 | profile may be inaccurate. | ||
188 | |||
189 | This command is experimental, and may be removed or changed in the future. It | ||
190 | may have no effect or produce unexpected output when used together with future | ||
191 | HDR support features. | ||
192 | |||
181 | # SEE ALSO | 193 | # SEE ALSO |
182 | 194 | ||
183 | *sway*(5) *sway-input*(5) | 195 | *sway*(5) *sway-input*(5) |
diff --git a/sway/tree/output.c b/sway/tree/output.c index 2d11195e..6c8dd6dc 100644 --- a/sway/tree/output.c +++ b/sway/tree/output.c | |||
@@ -279,6 +279,7 @@ void output_destroy(struct sway_output *output) { | |||
279 | list_free(output->workspaces); | 279 | list_free(output->workspaces); |
280 | list_free(output->current.workspaces); | 280 | list_free(output->current.workspaces); |
281 | wl_event_source_remove(output->repaint_timer); | 281 | wl_event_source_remove(output->repaint_timer); |
282 | wlr_color_transform_unref(output->color_transform); | ||
282 | free(output); | 283 | free(output); |
283 | } | 284 | } |
284 | 285 | ||