mirror of
https://github.com/swaywm/sway.git
synced 2024-11-25 09:21:28 +00:00
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.
This commit is contained in:
parent
2e9139df66
commit
40ca4150b2
|
@ -4,6 +4,7 @@ packages:
|
||||||
- eudev-dev
|
- eudev-dev
|
||||||
- gdk-pixbuf-dev
|
- gdk-pixbuf-dev
|
||||||
- json-c-dev
|
- json-c-dev
|
||||||
|
- lcms2-dev
|
||||||
- libdisplay-info-dev
|
- libdisplay-info-dev
|
||||||
- libevdev-dev
|
- libevdev-dev
|
||||||
- libinput-dev
|
- libinput-dev
|
||||||
|
|
|
@ -3,6 +3,7 @@ packages:
|
||||||
- cairo
|
- cairo
|
||||||
- gdk-pixbuf2
|
- gdk-pixbuf2
|
||||||
- json-c
|
- json-c
|
||||||
|
- lcms2
|
||||||
- libdisplay-info
|
- libdisplay-info
|
||||||
- libegl
|
- libegl
|
||||||
- libinput
|
- libinput
|
||||||
|
|
|
@ -8,6 +8,7 @@ packages:
|
||||||
- devel/pkgconf
|
- devel/pkgconf
|
||||||
- graphics/cairo
|
- graphics/cairo
|
||||||
- graphics/gdk-pixbuf2
|
- graphics/gdk-pixbuf2
|
||||||
|
- graphics/lcms2
|
||||||
- graphics/wayland
|
- graphics/wayland
|
||||||
- graphics/wayland-protocols
|
- graphics/wayland-protocols
|
||||||
- textproc/scdoc
|
- textproc/scdoc
|
||||||
|
|
|
@ -283,6 +283,7 @@ sway_cmd input_cmd_xkb_variant;
|
||||||
|
|
||||||
sway_cmd output_cmd_adaptive_sync;
|
sway_cmd output_cmd_adaptive_sync;
|
||||||
sway_cmd output_cmd_background;
|
sway_cmd output_cmd_background;
|
||||||
|
sway_cmd output_cmd_color_profile;
|
||||||
sway_cmd output_cmd_disable;
|
sway_cmd output_cmd_disable;
|
||||||
sway_cmd output_cmd_dpms;
|
sway_cmd output_cmd_dpms;
|
||||||
sway_cmd output_cmd_enable;
|
sway_cmd output_cmd_enable;
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
#include <wlr/interfaces/wlr_switch.h>
|
#include <wlr/interfaces/wlr_switch.h>
|
||||||
#include <wlr/types/wlr_tablet_tool.h>
|
#include <wlr/types/wlr_tablet_tool.h>
|
||||||
#include <wlr/util/box.h>
|
#include <wlr/util/box.h>
|
||||||
|
#include <wlr/render/color.h>
|
||||||
#include <xkbcommon/xkbcommon.h>
|
#include <xkbcommon/xkbcommon.h>
|
||||||
#include <xf86drmMode.h>
|
#include <xf86drmMode.h>
|
||||||
#include "../include/config.h"
|
#include "../include/config.h"
|
||||||
|
@ -285,6 +286,8 @@ struct output_config {
|
||||||
int max_render_time; // In milliseconds
|
int max_render_time; // In milliseconds
|
||||||
int adaptive_sync;
|
int adaptive_sync;
|
||||||
enum render_bit_depth render_bit_depth;
|
enum render_bit_depth render_bit_depth;
|
||||||
|
bool set_color_transform;
|
||||||
|
struct wlr_color_transform *color_transform;
|
||||||
|
|
||||||
char *background;
|
char *background;
|
||||||
char *background_option;
|
char *background_option;
|
||||||
|
|
|
@ -66,6 +66,8 @@ struct sway_output {
|
||||||
struct wl_signal disable;
|
struct wl_signal disable;
|
||||||
} events;
|
} events;
|
||||||
|
|
||||||
|
struct wlr_color_transform *color_transform;
|
||||||
|
|
||||||
struct timespec last_presentation;
|
struct timespec last_presentation;
|
||||||
uint32_t refresh_nsec;
|
uint32_t refresh_nsec;
|
||||||
int max_render_time; // In milliseconds
|
int max_render_time; // In milliseconds
|
||||||
|
|
|
@ -10,6 +10,7 @@ static const struct cmd_handler output_handlers[] = {
|
||||||
{ "adaptive_sync", output_cmd_adaptive_sync },
|
{ "adaptive_sync", output_cmd_adaptive_sync },
|
||||||
{ "background", output_cmd_background },
|
{ "background", output_cmd_background },
|
||||||
{ "bg", output_cmd_background },
|
{ "bg", output_cmd_background },
|
||||||
|
{ "color_profile", output_cmd_color_profile },
|
||||||
{ "disable", output_cmd_disable },
|
{ "disable", output_cmd_disable },
|
||||||
{ "dpms", output_cmd_dpms },
|
{ "dpms", output_cmd_dpms },
|
||||||
{ "enable", output_cmd_enable },
|
{ "enable", output_cmd_enable },
|
||||||
|
|
101
sway/commands/output/color_profile.c
Normal file
101
sway/commands/output/color_profile.c
Normal file
|
@ -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->max_render_time = -1;
|
||||||
oc->adaptive_sync = -1;
|
oc->adaptive_sync = -1;
|
||||||
oc->render_bit_depth = RENDER_BIT_DEPTH_DEFAULT;
|
oc->render_bit_depth = RENDER_BIT_DEPTH_DEFAULT;
|
||||||
|
oc->set_color_transform = false;
|
||||||
|
oc->color_transform = NULL;
|
||||||
oc->power = -1;
|
oc->power = -1;
|
||||||
return oc;
|
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) {
|
if (src->render_bit_depth != RENDER_BIT_DEPTH_DEFAULT) {
|
||||||
dst->render_bit_depth = src->render_bit_depth;
|
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) {
|
if (src->background) {
|
||||||
free(dst->background);
|
free(dst->background);
|
||||||
dst->background = strdup(src->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;
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -997,6 +1014,7 @@ void free_output_config(struct output_config *oc) {
|
||||||
free(oc->name);
|
free(oc->name);
|
||||||
free(oc->background);
|
free(oc->background);
|
||||||
free(oc->background_option);
|
free(oc->background_option);
|
||||||
|
wlr_color_transform_unref(oc->color_transform);
|
||||||
free(oc);
|
free(oc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -269,7 +269,10 @@ static int output_repaint_timer_handler(void *data) {
|
||||||
return 0;
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -202,6 +202,7 @@ sway_sources = files(
|
||||||
'commands/output/toggle.c',
|
'commands/output/toggle.c',
|
||||||
'commands/output/transform.c',
|
'commands/output/transform.c',
|
||||||
'commands/output/unplug.c',
|
'commands/output/unplug.c',
|
||||||
|
'commands/output/color_profile.c',
|
||||||
|
|
||||||
'tree/arrange.c',
|
'tree/arrange.c',
|
||||||
'tree/container.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,
|
updated to work with different bit depths. This command is experimental,
|
||||||
and may be removed or changed in the future.
|
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
|
# SEE ALSO
|
||||||
|
|
||||||
*sway*(5) *sway-input*(5)
|
*sway*(5) *sway-input*(5)
|
||||||
|
|
|
@ -279,6 +279,7 @@ void output_destroy(struct sway_output *output) {
|
||||||
list_free(output->workspaces);
|
list_free(output->workspaces);
|
||||||
list_free(output->current.workspaces);
|
list_free(output->current.workspaces);
|
||||||
wl_event_source_remove(output->repaint_timer);
|
wl_event_source_remove(output->repaint_timer);
|
||||||
|
wlr_color_transform_unref(output->color_transform);
|
||||||
free(output);
|
free(output);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue