mirror of
https://github.com/swaywm/sway.git
synced 2024-11-21 15:31: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
|
||||
- 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 },
|
||||
|
|
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->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…
Reference in a new issue