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.
master
Manuel Stoeckl 1 year ago committed by Simon Ser
parent 2e9139df66
commit 40ca4150b2

@ -4,6 +4,7 @@ packages:
- eudev-dev
- gdk-pixbuf-dev
- json-c-dev
- lcms2-dev
- libdisplay-info-dev
- libevdev-dev
- libinput-dev

@ -3,6 +3,7 @@ packages:
- cairo
- gdk-pixbuf2
- json-c
- lcms2
- libdisplay-info
- libegl
- libinput

@ -8,6 +8,7 @@ packages:
- devel/pkgconf
- graphics/cairo
- graphics/gdk-pixbuf2
- graphics/lcms2
- graphics/wayland
- graphics/wayland-protocols
- textproc/scdoc

@ -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;

@ -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;

@ -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

@ -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 },

@ -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;
}

@ -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);
}

@ -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;
}

@ -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',

@ -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)

@ -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);
}

Loading…
Cancel
Save