aboutsummaryrefslogtreecommitdiffstats
path: root/sway
diff options
context:
space:
mode:
authorLibravatar emersion <contact@emersion.fr>2017-12-06 12:36:06 +0100
committerLibravatar emersion <contact@emersion.fr>2017-12-06 12:36:06 +0100
commitaaae59026ff3751190b93277ac6d7566e373c892 (patch)
tree2dc365f9ceeb6a08a98e08d2f79fcc940624b199 /sway
parentMerge pull request #1498 from emersion/config (diff)
downloadsway-aaae59026ff3751190b93277ac6d7566e373c892.tar.gz
sway-aaae59026ff3751190b93277ac6d7566e373c892.tar.zst
sway-aaae59026ff3751190b93277ac6d7566e373c892.zip
Add output config
Diffstat (limited to 'sway')
-rw-r--r--sway/commands.c1
-rw-r--r--sway/commands/output.c253
-rw-r--r--sway/config/output.c166
-rw-r--r--sway/main.c4
-rw-r--r--sway/meson.build2
-rw-r--r--sway/tree/container.c83
6 files changed, 507 insertions, 2 deletions
diff --git a/sway/commands.c b/sway/commands.c
index 17638129..961cb867 100644
--- a/sway/commands.c
+++ b/sway/commands.c
@@ -95,6 +95,7 @@ static struct cmd_handler handlers[] = {
95 { "exec", cmd_exec }, 95 { "exec", cmd_exec },
96 { "exec_always", cmd_exec_always }, 96 { "exec_always", cmd_exec_always },
97 { "exit", cmd_exit }, 97 { "exit", cmd_exit },
98 { "output", cmd_output },
98}; 99};
99 100
100static int handler_compare(const void *_a, const void *_b) { 101static int handler_compare(const void *_a, const void *_b) {
diff --git a/sway/commands/output.c b/sway/commands/output.c
new file mode 100644
index 00000000..c964bef7
--- /dev/null
+++ b/sway/commands/output.c
@@ -0,0 +1,253 @@
1#define _XOPEN_SOURCE 500
2#include <ctype.h>
3#include <libgen.h>
4#include <stdlib.h>
5#include <string.h>
6#include <strings.h>
7#include <unistd.h>
8#include <wordexp.h>
9#include "sway/commands.h"
10#include "sway/config.h"
11#include "list.h"
12#include "log.h"
13#include "stringop.h"
14
15static char *bg_options[] = {
16 "stretch",
17 "center",
18 "fill",
19 "fit",
20 "tile",
21};
22
23struct cmd_results *cmd_output(int argc, char **argv) {
24 struct cmd_results *error = NULL;
25 if ((error = checkarg(argc, "output", EXPECTED_AT_LEAST, 1))) {
26 return error;
27 }
28 const char *name = argv[0];
29
30 struct output_config *output = calloc(1, sizeof(struct output_config));
31 if (!output) {
32 return cmd_results_new(CMD_FAILURE, "output", "Unable to allocate output config");
33 }
34 output->x = output->y = output->width = output->height = -1;
35 output->name = strdup(name);
36 output->enabled = -1;
37 output->scale = 1;
38
39 // TODO: atoi doesn't handle invalid numbers
40
41 int i;
42 for (i = 1; i < argc; ++i) {
43 const char *command = argv[i];
44
45 if (strcasecmp(command, "disable") == 0) {
46 output->enabled = 0;
47 } else if (strcasecmp(command, "resolution") == 0 || strcasecmp(command, "res") == 0) {
48 if (++i >= argc) {
49 error = cmd_results_new(CMD_INVALID, "output", "Missing resolution argument.");
50 goto fail;
51 }
52 char *res = argv[i];
53 char *x = strchr(res, 'x');
54 int width = -1, height = -1;
55 if (x != NULL) {
56 // Format is 1234x4321
57 *x = '\0';
58 width = atoi(res);
59 height = atoi(x + 1);
60 *x = 'x';
61 } else {
62 // Format is 1234 4321
63 width = atoi(res);
64 if (++i >= argc) {
65 error = cmd_results_new(CMD_INVALID, "output", "Missing resolution argument (height).");
66 goto fail;
67 }
68 res = argv[i];
69 height = atoi(res);
70 }
71 output->width = width;
72 output->height = height;
73 } else if (strcasecmp(command, "refresh_rate") == 0) {
74 if (++i >= argc) {
75 error = cmd_results_new(CMD_INVALID, "output", "Missing refresh_rate argument.");
76 goto fail;
77 }
78 output->refresh_rate = atof(argv[i]);
79 } else if (strcasecmp(command, "position") == 0 || strcasecmp(command, "pos") == 0) {
80 if (++i >= argc) {
81 error = cmd_results_new(CMD_INVALID, "output", "Missing position argument.");
82 goto fail;
83 }
84 char *res = argv[i];
85 char *c = strchr(res, ',');
86 int x = -1, y = -1;
87 if (c != NULL) {
88 // Format is 1234,4321
89 *c = '\0';
90 x = atoi(res);
91 y = atoi(c + 1);
92 *c = ',';
93 } else {
94 // Format is 1234 4321
95 x = atoi(res);
96 if (++i >= argc) {
97 error = cmd_results_new(CMD_INVALID, "output", "Missing position argument (y).");
98 goto fail;
99 }
100 res = argv[i];
101 y = atoi(res);
102 }
103 output->x = x;
104 output->y = y;
105 } else if (strcasecmp(command, "scale") == 0) {
106 if (++i >= argc) {
107 error = cmd_results_new(CMD_INVALID, "output", "Missing scale parameter.");
108 goto fail;
109 }
110 output->scale = atoi(argv[i]);
111 } else if (strcasecmp(command, "transform") == 0) {
112 if (++i >= argc) {
113 error = cmd_results_new(CMD_INVALID, "output", "Missing transform parameter.");
114 goto fail;
115 }
116 char *value = argv[i];
117 if (strcmp(value, "normal") == 0) {
118 output->transform = WL_OUTPUT_TRANSFORM_NORMAL;
119 } else if (strcmp(value, "90") == 0) {
120 output->transform = WL_OUTPUT_TRANSFORM_90;
121 } else if (strcmp(value, "180") == 0) {
122 output->transform = WL_OUTPUT_TRANSFORM_180;
123 } else if (strcmp(value, "270") == 0) {
124 output->transform = WL_OUTPUT_TRANSFORM_270;
125 } else if (strcmp(value, "flipped") == 0) {
126 output->transform = WL_OUTPUT_TRANSFORM_FLIPPED;
127 } else if (strcmp(value, "flipped-90") == 0) {
128 output->transform = WL_OUTPUT_TRANSFORM_FLIPPED_90;
129 } else if (strcmp(value, "flipped-180") == 0) {
130 output->transform = WL_OUTPUT_TRANSFORM_FLIPPED_180;
131 } else if (strcmp(value, "flipped-270") == 0) {
132 output->transform = WL_OUTPUT_TRANSFORM_FLIPPED_270;
133 } else {
134 error = cmd_results_new(CMD_INVALID, "output", "Invalid output transform.");
135 goto fail;
136 }
137 } else if (strcasecmp(command, "background") == 0 || strcasecmp(command, "bg") == 0) {
138 wordexp_t p;
139 if (++i >= argc) {
140 error = cmd_results_new(CMD_INVALID, "output", "Missing background file or color specification.");
141 goto fail;
142 }
143 if (i + 1 >= argc) {
144 error = cmd_results_new(CMD_INVALID, "output", "Missing background scaling mode or `solid_color`.");
145 goto fail;
146 }
147 if (strcasecmp(argv[i + 1], "solid_color") == 0) {
148 output->background = strdup(argv[argc - 2]);
149 output->background_option = strdup("solid_color");
150 } else {
151 // argv[i+j]=bg_option
152 bool valid = false;
153 char *mode;
154 size_t j;
155 for (j = 0; j < (size_t) (argc - i); ++j) {
156 mode = argv[i + j];
157 for (size_t k = 0; k < sizeof(bg_options) / sizeof(char *); ++k) {
158 if (strcasecmp(mode, bg_options[k]) == 0) {
159 valid = true;
160 break;
161 }
162 }
163 if (valid) {
164 break;
165 }
166 }
167 if (!valid) {
168 error = cmd_results_new(CMD_INVALID, "output", "Missing background scaling mode.");
169 goto fail;
170 }
171
172 char *src = join_args(argv + i, j);
173 if (wordexp(src, &p, 0) != 0 || p.we_wordv[0] == NULL) {
174 error = cmd_results_new(CMD_INVALID, "output", "Invalid syntax (%s)", src);
175 goto fail;
176 }
177 free(src);
178 src = p.we_wordv[0];
179 if (config->reading && *src != '/') {
180 char *conf = strdup(config->current_config);
181 if (conf) {
182 char *conf_path = dirname(conf);
183 src = malloc(strlen(conf_path) + strlen(src) + 2);
184 if (src) {
185 sprintf(src, "%s/%s", conf_path, p.we_wordv[0]);
186 } else {
187 sway_log(L_ERROR, "Unable to allocate background source");
188 }
189 free(conf);
190 } else {
191 sway_log(L_ERROR, "Unable to allocate background source");
192 }
193 }
194 if (!src || access(src, F_OK) == -1) {
195 error = cmd_results_new(CMD_INVALID, "output", "Background file unreadable (%s)", src);
196 wordfree(&p);
197 goto fail;
198 }
199
200 output->background = strdup(src);
201 output->background_option = strdup(mode);
202 if (src != p.we_wordv[0]) {
203 free(src);
204 }
205 wordfree(&p);
206
207 i += j;
208 }
209 }
210 }
211
212 i = list_seq_find(config->output_configs, output_name_cmp, name);
213 if (i >= 0) {
214 // merge existing config
215 struct output_config *oc = config->output_configs->items[i];
216 merge_output_config(oc, output);
217 free_output_config(output);
218 output = oc;
219 } else {
220 list_add(config->output_configs, output);
221 }
222
223 sway_log(L_DEBUG, "Config stored for output %s (enabled:%d) (%d x %d @ "
224 "%d, %d scale %d transform %d refresh_rate %f) (bg %s %s)",
225 output->name, output->enabled, output->width,
226 output->height, output->x, output->y, output->scale,
227 output->transform, output->refresh_rate,
228 output->background, output->background_option);
229
230 if (output->name) {
231 // Try to find the output container and apply configuration now. If
232 // this is during startup then there will be no container and config
233 // will be applied during normal "new output" event from wlc.
234 swayc_t *cont = NULL;
235 for (int i = 0; i < root_container.children->length; ++i) {
236 cont = root_container.children->items[i];
237 if (cont->name && ((strcmp(cont->name, output->name) == 0) || (strcmp(output->name, "*") == 0))) {
238 apply_output_config(output, cont);
239
240 if (strcmp(output->name, "*") != 0) {
241 // stop looking if the output config isn't applicable to all outputs
242 break;
243 }
244 }
245 }
246 }
247
248 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
249
250fail:
251 free_output_config(output);
252 return error;
253}
diff --git a/sway/config/output.c b/sway/config/output.c
new file mode 100644
index 00000000..02e18e59
--- /dev/null
+++ b/sway/config/output.c
@@ -0,0 +1,166 @@
1#define _XOPEN_SOURCE 700
2#include <string.h>
3#include <assert.h>
4#include <wlr/types/wlr_output.h>
5#include "sway/config.h"
6#include "sway/output.h"
7#include "log.h"
8
9int output_name_cmp(const void *item, const void *data) {
10 const struct output_config *output = item;
11 const char *name = data;
12
13 return strcmp(output->name, name);
14}
15
16void merge_output_config(struct output_config *dst, struct output_config *src) {
17 if (src->name) {
18 if (dst->name) {
19 free(dst->name);
20 }
21 dst->name = strdup(src->name);
22 }
23 if (src->enabled != -1) {
24 dst->enabled = src->enabled;
25 }
26 if (src->width != -1) {
27 dst->width = src->width;
28 }
29 if (src->height != -1) {
30 dst->height = src->height;
31 }
32 if (src->x != -1) {
33 dst->x = src->x;
34 }
35 if (src->y != -1) {
36 dst->y = src->y;
37 }
38 if (src->scale != -1) {
39 dst->scale = src->scale;
40 }
41 if (src->background) {
42 if (dst->background) {
43 free(dst->background);
44 }
45 dst->background = strdup(src->background);
46 }
47 if (src->background_option) {
48 if (dst->background_option) {
49 free(dst->background_option);
50 }
51 dst->background_option = strdup(src->background_option);
52 }
53}
54
55static void set_mode(struct wlr_output *output, int width, int height,
56 float refresh_rate) {
57 struct wlr_output_mode *mode, *best = NULL;
58 int mhz = (int)(refresh_rate * 1000);
59 wl_list_for_each(mode, &output->modes, link) {
60 if (mode->width == width && mode->height == height) {
61 if (mode->refresh == mhz) {
62 best = mode;
63 break;
64 }
65 best = mode;
66 }
67 }
68 if (!best) {
69 sway_log(L_ERROR, "Configured mode for %s not available", output->name);
70 } else {
71 sway_log(L_DEBUG, "Assigning configured mode to %s", output->name);
72 wlr_output_set_mode(output, best);
73 }
74}
75
76void apply_output_config(struct output_config *oc, swayc_t *output) {
77 assert(output->type == C_OUTPUT);
78
79 if (oc && oc->enabled == 0) {
80 destroy_output(output);
81 return;
82 }
83
84 struct wlr_output *wlr_output = output->sway_output->wlr_output;
85 if (oc && oc->width > 0 && oc->height > 0) {
86 set_mode(wlr_output, oc->width, oc->height, oc->refresh_rate);
87 }
88 if (oc && oc->scale > 0) {
89 wlr_output->scale = oc->scale;
90 }
91 if (oc && oc->transform != WL_OUTPUT_TRANSFORM_NORMAL) {
92 wlr_output_transform(wlr_output, oc->transform);
93 }
94
95 // Find position for it
96 if (oc && oc->x != -1 && oc->y != -1) {
97 sway_log(L_DEBUG, "Set %s position to %d, %d", oc->name, oc->x, oc->y);
98 output->x = oc->x;
99 output->y = oc->y;
100 } else {
101 int x = 0;
102 for (int i = 0; i < root_container.children->length; ++i) {
103 swayc_t *c = root_container.children->items[i];
104 if (c->type == C_OUTPUT) {
105 if (c->width + c->x > x) {
106 x = c->width + c->x;
107 }
108 }
109 }
110 output->x = x;
111 }
112
113 if (!oc || !oc->background) {
114 // Look for a * config for background
115 int i = list_seq_find(config->output_configs, output_name_cmp, "*");
116 if (i >= 0) {
117 oc = config->output_configs->items[i];
118 } else {
119 oc = NULL;
120 }
121 }
122
123 int output_i;
124 for (output_i = 0; output_i < root_container.children->length; ++output_i) {
125 if (root_container.children->items[output_i] == output) {
126 break;
127 }
128 }
129
130 if (oc && oc->background) {
131 // TODO: swaybg
132 /*if (output->bg_pid != 0) {
133 terminate_swaybg(output->bg_pid);
134 }
135
136 sway_log(L_DEBUG, "Setting background for output %d to %s", output_i, oc->background);
137
138 size_t bufsize = 12;
139 char output_id[bufsize];
140 snprintf(output_id, bufsize, "%d", output_i);
141 output_id[bufsize-1] = 0;
142
143 char *const cmd[] = {
144 "swaybg",
145 output_id,
146 oc->background,
147 oc->background_option,
148 NULL,
149 };
150
151 output->bg_pid = fork();
152 if (output->bg_pid == 0) {
153 execvp(cmd[0], cmd);
154 }*/
155 }
156}
157
158void free_output_config(struct output_config *oc) {
159 if (!oc) {
160 return;
161 }
162 free(oc->name);
163 free(oc->background);
164 free(oc->background_option);
165 free(oc);
166}
diff --git a/sway/main.c b/sway/main.c
index bc843591..8952f997 100644
--- a/sway/main.c
+++ b/sway/main.c
@@ -404,6 +404,10 @@ int main(int argc, char **argv) {
404 404
405 security_sanity_check(); 405 security_sanity_check();
406 406
407 // TODO: wait for server to be ready
408 // TODO: consume config->cmd_queue
409 config->active = true;
410
407 if (!terminate_request) { 411 if (!terminate_request) {
408 server_run(&server); 412 server_run(&server);
409 } 413 }
diff --git a/sway/meson.build b/sway/meson.build
index 84f48137..5ae7fbb3 100644
--- a/sway/meson.build
+++ b/sway/meson.build
@@ -5,7 +5,9 @@ sway_sources = files(
5 'commands/exit.c', 5 'commands/exit.c',
6 'commands/exec.c', 6 'commands/exec.c',
7 'commands/exec_always.c', 7 'commands/exec_always.c',
8 'commands/output.c',
8 'config.c', 9 'config.c',
10 'config/output.c',
9 'ipc-json.c', 11 'ipc-json.c',
10 'ipc-server.c', 12 'ipc-server.c',
11 'desktop/output.c', 13 'desktop/output.c',
diff --git a/sway/tree/container.c b/sway/tree/container.c
index e205fbcf..7720718f 100644
--- a/sway/tree/container.c
+++ b/sway/tree/container.c
@@ -2,7 +2,9 @@
2#include <stdint.h> 2#include <stdint.h>
3#include <stdlib.h> 3#include <stdlib.h>
4#include <string.h> 4#include <string.h>
5#include <strings.h>
5#include <wlr/types/wlr_output_layout.h> 6#include <wlr/types/wlr_output_layout.h>
7#include "sway/config.h"
6#include "sway/container.h" 8#include "sway/container.h"
7#include "sway/layout.h" 9#include "sway/layout.h"
8#include "sway/output.h" 10#include "sway/output.h"
@@ -23,6 +25,30 @@ void swayc_descendants_of_type(swayc_t *root, enum swayc_types type,
23 } 25 }
24} 26}
25 27
28static void update_root_geometry() {
29 int width = 0;
30 int height = 0;
31 swayc_t *child;
32 int child_width;
33 int child_height;
34
35 for (int i = 0; i < root_container.children->length; ++i) {
36 child = root_container.children->items[i];
37 child_width = child->width + child->x;
38 child_height = child->height + child->y;
39 if (child_width > width) {
40 width = child_width;
41 }
42
43 if (child_height > height) {
44 height = child_height;
45 }
46 }
47
48 root_container.width = width;
49 root_container.height = height;
50}
51
26static swayc_t *new_swayc(enum swayc_types type) { 52static swayc_t *new_swayc(enum swayc_types type) {
27 // next id starts at 1 because 0 is assigned to root_container in layout.c 53 // next id starts at 1 because 0 is assigned to root_container in layout.c
28 static size_t next_id = 1; 54 static size_t next_id = 1;
@@ -44,10 +70,33 @@ static swayc_t *new_swayc(enum swayc_types type) {
44 70
45swayc_t *new_output(struct sway_output *sway_output) { 71swayc_t *new_output(struct sway_output *sway_output) {
46 struct wlr_box size; 72 struct wlr_box size;
47 wlr_output_effective_resolution( 73 wlr_output_effective_resolution(sway_output->wlr_output, &size.width,
48 sway_output->wlr_output, &size.width, &size.height); 74 &size.height);
49 const char *name = sway_output->wlr_output->name; 75 const char *name = sway_output->wlr_output->name;
50 76
77 struct output_config *oc = NULL, *all = NULL;
78 for (int i = 0; i < config->output_configs->length; ++i) {
79 struct output_config *cur = config->output_configs->items[i];
80 if (strcasecmp(name, cur->name) == 0) {
81 sway_log(L_DEBUG, "Matched output config for %s", name);
82 oc = cur;
83 }
84 if (strcasecmp("*", cur->name) == 0) {
85 sway_log(L_DEBUG, "Matched wildcard output config for %s", name);
86 all = cur;
87 }
88
89 if (oc && all) {
90 break;
91 }
92 }
93 if (!oc) {
94 oc = all;
95 }
96 if (oc && !oc->enabled) {
97 return NULL;
98 }
99
51 swayc_t *output = new_swayc(C_OUTPUT); 100 swayc_t *output = new_swayc(C_OUTPUT);
52 output->sway_output = sway_output; 101 output->sway_output = sway_output;
53 output->name = name ? strdup(name) : NULL; 102 output->name = name ? strdup(name) : NULL;
@@ -58,6 +107,8 @@ swayc_t *new_output(struct sway_output *sway_output) {
58 wlr_output_layout_add_auto(root_container.output_layout, 107 wlr_output_layout_add_auto(root_container.output_layout,
59 sway_output->wlr_output); 108 sway_output->wlr_output);
60 109
110 apply_output_config(oc, output);
111
61 add_child(&root_container, output); 112 add_child(&root_container, output);
62 113
63 // Create workspace 114 // Create workspace
@@ -139,6 +190,34 @@ static void free_swayc(swayc_t *cont) {
139 free(cont); 190 free(cont);
140} 191}
141 192
193swayc_t *destroy_output(swayc_t *output) {
194 if (!sway_assert(output, "null output passed to destroy_output")) {
195 return NULL;
196 }
197 if (output->children->length > 0) {
198 // TODO save workspaces when there are no outputs.
199 // TODO also check if there will ever be no outputs except for exiting
200 // program
201 if (root_container.children->length > 1) {
202 int p = root_container.children->items[0] == output;
203 // Move workspace from this output to another output
204 while (output->children->length) {
205 swayc_t *child = output->children->items[0];
206 remove_child(child);
207 add_child(root_container.children->items[p], child);
208 }
209 sort_workspaces(root_container.children->items[p]);
210 // TODO WLR: is this needed anymore?
211 //update_visibility(root_container.children->items[p]);
212 arrange_windows(root_container.children->items[p], -1, -1);
213 }
214 }
215 sway_log(L_DEBUG, "OUTPUT: Destroying output '%s'", output->name);
216 free_swayc(output);
217 update_root_geometry();
218 return &root_container;
219}
220
142swayc_t *destroy_view(swayc_t *view) { 221swayc_t *destroy_view(swayc_t *view) {
143 if (!sway_assert(view, "null view passed to destroy_view")) { 222 if (!sway_assert(view, "null view passed to destroy_view")) {
144 return NULL; 223 return NULL;