aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorManuel Stoeckl <code@mstoeckl.com>2023-07-17 21:40:28 -0400
committerSimon Ser <contact@emersion.fr>2024-06-07 19:01:49 +0200
commit40ca4150b27a5b94938b6c3d744f74bb26d347f7 (patch)
treea162451f9508eed3a0f7935d6b2e3615e20f03d2
parent2e9139df664f1e2dbe14b5df4a9646411b924c66 (diff)
sway/commands/output: Add command to set color profile
This makes it possible to render output buffers in a different color space, by specifying an ICC profile for the output.
-rw-r--r--.builds/alpine.yml1
-rw-r--r--.builds/archlinux.yml1
-rw-r--r--.builds/freebsd.yml1
-rw-r--r--include/sway/commands.h1
-rw-r--r--include/sway/config.h3
-rw-r--r--include/sway/output.h2
-rw-r--r--sway/commands/output.c1
-rw-r--r--sway/commands/output/color_profile.c101
-rw-r--r--sway/config/output.c18
-rw-r--r--sway/desktop/output.c5
-rw-r--r--sway/meson.build1
-rw-r--r--sway/sway-output.5.scd12
-rw-r--r--sway/tree/output.c1
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:
- eudev-dev
- gdk-pixbuf-dev
- json-c-dev
+ - lcms2-dev
- libdisplay-info-dev
- libevdev-dev
- 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:
- cairo
- gdk-pixbuf2
- json-c
+ - lcms2
- libdisplay-info
- libegl
- 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:
- devel/pkgconf
- graphics/cairo
- graphics/gdk-pixbuf2
+- graphics/lcms2
- graphics/wayland
- graphics/wayland-protocols
- 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;
sway_cmd output_cmd_adaptive_sync;
sway_cmd output_cmd_background;
+sway_cmd output_cmd_color_profile;
sway_cmd output_cmd_disable;
sway_cmd output_cmd_dpms;
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 @@
#include <wlr/interfaces/wlr_switch.h>
#include <wlr/types/wlr_tablet_tool.h>
#include <wlr/util/box.h>
+#include <wlr/render/color.h>
#include <xkbcommon/xkbcommon.h>
#include <xf86drmMode.h>
#include "../include/config.h"
@@ -285,6 +286,8 @@ struct output_config {
int max_render_time; // In milliseconds
int adaptive_sync;
enum render_bit_depth render_bit_depth;
+ bool set_color_transform;
+ struct wlr_color_transform *color_transform;
char *background;
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 {
struct wl_signal disable;
} events;
+ struct wlr_color_transform *color_transform;
+
struct timespec last_presentation;
uint32_t refresh_nsec;
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[] = {
{ "adaptive_sync", output_cmd_adaptive_sync },
{ "background", output_cmd_background },
{ "bg", output_cmd_background },
+ { "color_profile", output_cmd_color_profile },
{ "disable", output_cmd_disable },
{ "dpms", output_cmd_dpms },
{ "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 @@
+#include <fcntl.h>
+#include <strings.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <wlr/render/color.h>
+#include "sway/commands.h"
+#include "sway/config.h"
+
+static bool read_file_into_buf(const char *path, void **buf, size_t *size) {
+ /* Why not use fopen/fread directly? glibc will succesfully open directories,
+ * not just files, and supports seeking on them. Instead, we directly
+ * work with file descriptors and use the more consistent open/fstat/read. */
+ int fd = open(path, O_RDONLY | O_NOCTTY | O_CLOEXEC);
+ if (fd == -1) {
+ return false;
+ }
+ char *b = NULL;
+ struct stat info;
+ if (fstat(fd, &info) == -1) {
+ goto fail;
+ }
+ // only regular files, to avoid issues with e.g. opening pipes
+ if (!S_ISREG(info.st_mode)) {
+ goto fail;
+ }
+ off_t s = info.st_size;
+ if (s <= 0) {
+ goto fail;
+ }
+ b = calloc(1, s);
+ if (!b) {
+ goto fail;
+ }
+ size_t nread = 0;
+ while (nread < (size_t)s) {
+ size_t to_read = (size_t)s - nread;
+ ssize_t r = read(fd, b + nread, to_read);
+ if ((r == -1 && errno != EINTR) || r == 0) {
+ goto fail;
+ }
+ nread += (size_t)r;
+ }
+ close(fd);
+ *buf = b;
+ *size = (size_t)s;
+ return true; // success
+fail:
+ free(b);
+ close(fd);
+ return false;
+}
+
+struct cmd_results *output_cmd_color_profile(int argc, char **argv) {
+ if (!config->handler_context.output_config) {
+ return cmd_results_new(CMD_FAILURE, "Missing output config");
+ }
+ if (!argc) {
+ return cmd_results_new(CMD_INVALID, "Missing color_profile first argument.");
+ }
+
+ if (strcmp(*argv, "srgb") == 0) {
+ wlr_color_transform_unref(config->handler_context.output_config->color_transform);
+ config->handler_context.output_config->color_transform = NULL;
+ config->handler_context.output_config->set_color_transform = true;
+
+ config->handler_context.leftovers.argc = argc - 1;
+ config->handler_context.leftovers.argv = argv + 1;
+ } else if (strcmp(*argv, "icc") == 0) {
+ if (argc < 2) {
+ return cmd_results_new(CMD_INVALID,
+ "Invalid color profile specification: icc type requires a file");
+ }
+ void *data = NULL;
+ size_t size = 0;
+ if (!read_file_into_buf(argv[1], &data, &size)) {
+ return cmd_results_new(CMD_FAILURE,
+ "Failed to load color profile: could not read ICC file");
+ }
+
+ struct wlr_color_transform *tmp =
+ wlr_color_transform_init_linear_to_icc(data, size);
+ if (!tmp) {
+ free(data);
+ return cmd_results_new(CMD_FAILURE,
+ "Failed to load color profile: failed to initialize transform from ICC");
+ }
+ free(data);
+
+ wlr_color_transform_unref(config->handler_context.output_config->color_transform);
+ config->handler_context.output_config->color_transform = tmp;
+ config->handler_context.output_config->set_color_transform = true;
+
+ config->handler_context.leftovers.argc = argc - 2;
+ config->handler_context.leftovers.argv = argv + 2;
+ } else {
+ return cmd_results_new(CMD_INVALID,
+ "Invalid color profile specification: first argument should be icc|srgb");
+ }
+
+ return NULL;
+}
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) {
oc->max_render_time = -1;
oc->adaptive_sync = -1;
oc->render_bit_depth = RENDER_BIT_DEPTH_DEFAULT;
+ oc->set_color_transform = false;
+ oc->color_transform = NULL;
oc->power = -1;
return oc;
}
@@ -191,6 +193,14 @@ static void merge_output_config(struct output_config *dst, struct output_config
if (src->render_bit_depth != RENDER_BIT_DEPTH_DEFAULT) {
dst->render_bit_depth = src->render_bit_depth;
}
+ if (src->set_color_transform) {
+ if (src->color_transform) {
+ wlr_color_transform_ref(src->color_transform);
+ }
+ wlr_color_transform_unref(dst->color_transform);
+ dst->set_color_transform = true;
+ dst->color_transform = src->color_transform;
+ }
if (src->background) {
free(dst->background);
dst->background = strdup(src->background);
@@ -557,6 +567,13 @@ static bool finalize_output_config(struct output_config *oc, struct sway_output
output->max_render_time = oc->max_render_time;
}
+ if (oc && oc->set_color_transform) {
+ if (oc->color_transform) {
+ wlr_color_transform_ref(oc->color_transform);
+ }
+ wlr_color_transform_unref(output->color_transform);
+ output->color_transform = oc->color_transform;
+ }
return true;
}
@@ -997,6 +1014,7 @@ void free_output_config(struct output_config *oc) {
free(oc->name);
free(oc->background);
free(oc->background_option);
+ wlr_color_transform_unref(oc->color_transform);
free(oc);
}
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) {
return 0;
}
- wlr_scene_output_commit(output->scene_output, NULL);
+ struct wlr_scene_output_state_options opts = {
+ .color_transform = output->color_transform,
+ };
+ wlr_scene_output_commit(output->scene_output, &opts);
return 0;
}
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(
'commands/output/toggle.c',
'commands/output/transform.c',
'commands/output/unplug.c',
+ 'commands/output/color_profile.c',
'tree/arrange.c',
'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:
updated to work with different bit depths. This command is experimental,
and may be removed or changed in the future.
+*output* <name> color_profile srgb|[icc <file>]
+ Sets the color profile for an output. The default is _srgb_. <file> should be a
+ path to a display ICC profile.
+
+ Not all renderers support this feature; currently it only works with the
+ the Vulkan renderer. Even where supported, the application of the color
+ profile may be inaccurate.
+
+ This command is experimental, and may be removed or changed in the future. It
+ may have no effect or produce unexpected output when used together with future
+ HDR support features.
+
# SEE ALSO
*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) {
list_free(output->workspaces);
list_free(output->current.workspaces);
wl_event_source_remove(output->repaint_timer);
+ wlr_color_transform_unref(output->color_transform);
free(output);
}