aboutsummaryrefslogtreecommitdiffstats
path: root/sway/commands/output/color_profile.c
diff options
context:
space:
mode:
Diffstat (limited to 'sway/commands/output/color_profile.c')
-rw-r--r--sway/commands/output/color_profile.c113
1 files changed, 113 insertions, 0 deletions
diff --git a/sway/commands/output/color_profile.c b/sway/commands/output/color_profile.c
new file mode 100644
index 00000000..98481329
--- /dev/null
+++ b/sway/commands/output/color_profile.c
@@ -0,0 +1,113 @@
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#include "stringop.h"
9
10static bool read_file_into_buf(const char *path, void **buf, size_t *size) {
11 /* Why not use fopen/fread directly? glibc will succesfully open directories,
12 * not just files, and supports seeking on them. Instead, we directly
13 * work with file descriptors and use the more consistent open/fstat/read. */
14 int fd = open(path, O_RDONLY | O_NOCTTY | O_CLOEXEC);
15 if (fd == -1) {
16 return false;
17 }
18 char *b = NULL;
19 struct stat info;
20 if (fstat(fd, &info) == -1) {
21 goto fail;
22 }
23 // only regular files, to avoid issues with e.g. opening pipes
24 if (!S_ISREG(info.st_mode)) {
25 goto fail;
26 }
27 off_t s = info.st_size;
28 if (s <= 0) {
29 goto fail;
30 }
31 b = calloc(1, s);
32 if (!b) {
33 goto fail;
34 }
35 size_t nread = 0;
36 while (nread < (size_t)s) {
37 size_t to_read = (size_t)s - nread;
38 ssize_t r = read(fd, b + nread, to_read);
39 if ((r == -1 && errno != EINTR) || r == 0) {
40 goto fail;
41 }
42 nread += (size_t)r;
43 }
44 close(fd);
45 *buf = b;
46 *size = (size_t)s;
47 return true; // success
48fail:
49 free(b);
50 close(fd);
51 return false;
52}
53
54struct cmd_results *output_cmd_color_profile(int argc, char **argv) {
55 if (!config->handler_context.output_config) {
56 return cmd_results_new(CMD_FAILURE, "Missing output config");
57 }
58 if (!argc) {
59 return cmd_results_new(CMD_INVALID, "Missing color_profile first argument.");
60 }
61
62 if (strcmp(*argv, "srgb") == 0) {
63 wlr_color_transform_unref(config->handler_context.output_config->color_transform);
64 config->handler_context.output_config->color_transform = NULL;
65 config->handler_context.output_config->set_color_transform = true;
66
67 config->handler_context.leftovers.argc = argc - 1;
68 config->handler_context.leftovers.argv = argv + 1;
69 } else if (strcmp(*argv, "icc") == 0) {
70 if (argc < 2) {
71 return cmd_results_new(CMD_INVALID,
72 "Invalid color profile specification: icc type requires a file");
73 }
74
75 char *icc_path = strdup(argv[1]);
76 if (!expand_path(&icc_path)) {
77 struct cmd_results *cmd_res = cmd_results_new(CMD_INVALID,
78 "Invalid color profile specification: invalid file path");
79 free(icc_path);
80 return cmd_res;
81 }
82
83 void *data = NULL;
84 size_t size = 0;
85 if (!read_file_into_buf(icc_path, &data, &size)) {
86 free(icc_path);
87 return cmd_results_new(CMD_FAILURE,
88 "Failed to load color profile: could not read ICC file");
89 }
90 free(icc_path);
91
92 struct wlr_color_transform *tmp =
93 wlr_color_transform_init_linear_to_icc(data, size);
94 if (!tmp) {
95 free(data);
96 return cmd_results_new(CMD_FAILURE,
97 "Failed to load color profile: failed to initialize transform from ICC");
98 }
99 free(data);
100
101 wlr_color_transform_unref(config->handler_context.output_config->color_transform);
102 config->handler_context.output_config->color_transform = tmp;
103 config->handler_context.output_config->set_color_transform = true;
104
105 config->handler_context.leftovers.argc = argc - 2;
106 config->handler_context.leftovers.argv = argv + 2;
107 } else {
108 return cmd_results_new(CMD_INVALID,
109 "Invalid color profile specification: first argument should be icc|srgb");
110 }
111
112 return NULL;
113}