diff options
Diffstat (limited to 'sway/commands/output/color_profile.c')
-rw-r--r-- | sway/commands/output/color_profile.c | 113 |
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 | |||
10 | static 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 | ||
48 | fail: | ||
49 | free(b); | ||
50 | close(fd); | ||
51 | return false; | ||
52 | } | ||
53 | |||
54 | struct 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 | } | ||