aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/sway/commands.h9
-rw-r--r--include/sway/config.h5
-rw-r--r--sway/commands/output.c299
-rw-r--r--sway/commands/output/background.c105
-rw-r--r--sway/commands/output/disable.c13
-rw-r--r--sway/commands/output/dpms.c24
-rw-r--r--sway/commands/output/enable.c14
-rw-r--r--sway/commands/output/mode.c55
-rw-r--r--sway/commands/output/position.c46
-rw-r--r--sway/commands/output/scale.c23
-rw-r--r--sway/commands/output/transform.c39
-rw-r--r--sway/desktop/layer_shell.c5
-rw-r--r--sway/desktop/output.c19
-rw-r--r--sway/desktop/xwayland.c15
-rw-r--r--sway/input/cursor.c2
-rw-r--r--sway/input/seat.c17
-rw-r--r--sway/meson.build9
-rw-r--r--sway/tree/container.c10
-rw-r--r--sway/tree/layout.c16
-rw-r--r--sway/tree/output.c10
-rw-r--r--sway/tree/view.c4
21 files changed, 447 insertions, 292 deletions
diff --git a/include/sway/commands.h b/include/sway/commands.h
index 593ae0f1..7ca0bda8 100644
--- a/include/sway/commands.h
+++ b/include/sway/commands.h
@@ -216,6 +216,15 @@ sway_cmd input_cmd_xkb_options;
216sway_cmd input_cmd_xkb_rules; 216sway_cmd input_cmd_xkb_rules;
217sway_cmd input_cmd_xkb_variant; 217sway_cmd input_cmd_xkb_variant;
218 218
219sway_cmd output_cmd_background;
220sway_cmd output_cmd_disable;
221sway_cmd output_cmd_dpms;
222sway_cmd output_cmd_enable;
223sway_cmd output_cmd_mode;
224sway_cmd output_cmd_position;
225sway_cmd output_cmd_scale;
226sway_cmd output_cmd_transform;
227
219sway_cmd seat_cmd_attach; 228sway_cmd seat_cmd_attach;
220sway_cmd seat_cmd_fallback; 229sway_cmd seat_cmd_fallback;
221sway_cmd seat_cmd_cursor; 230sway_cmd seat_cmd_cursor;
diff --git a/include/sway/config.h b/include/sway/config.h
index b597da75..81e9c382 100644
--- a/include/sway/config.h
+++ b/include/sway/config.h
@@ -372,10 +372,15 @@ struct sway_config {
372 // Context for command handlers 372 // Context for command handlers
373 struct { 373 struct {
374 struct input_config *input_config; 374 struct input_config *input_config;
375 struct output_config *output_config;
375 struct seat_config *seat_config; 376 struct seat_config *seat_config;
376 struct sway_seat *seat; 377 struct sway_seat *seat;
377 struct sway_container *current_container; 378 struct sway_container *current_container;
378 bool using_criteria; 379 bool using_criteria;
380 struct {
381 int argc;
382 char **argv;
383 } leftovers;
379 } handler_context; 384 } handler_context;
380}; 385};
381 386
diff --git a/sway/commands/output.c b/sway/commands/output.c
index e8881f77..bc12310e 100644
--- a/sway/commands/output.c
+++ b/sway/commands/output.c
@@ -1,254 +1,24 @@
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" 1#include "sway/commands.h"
10#include "sway/config.h" 2#include "sway/config.h"
11#include "list.h" 3#include "list.h"
12#include "log.h" 4#include "log.h"
13#include "stringop.h"
14 5
15static char *bg_options[] = { 6// must be in order for the bsearch
16 "stretch", 7static struct cmd_handler output_handlers[] = {
17 "center", 8 { "background", output_cmd_background },
18 "fill", 9 { "bg", output_cmd_background },
19 "fit", 10 { "disable", output_cmd_disable },
20 "tile", 11 { "dpms", output_cmd_dpms },
12 { "enable", output_cmd_enable },
13 { "mode", output_cmd_mode },
14 { "pos", output_cmd_position },
15 { "position", output_cmd_position },
16 { "res", output_cmd_mode },
17 { "resolution", output_cmd_mode },
18 { "scale", output_cmd_scale },
19 { "transform", output_cmd_transform },
21}; 20};
22 21
23static struct cmd_results *cmd_output_dpms(struct output_config *output,
24 int *i, int argc, char **argv) {
25
26 if (++*i >= argc) {
27 return cmd_results_new(CMD_INVALID, "output", "Missing dpms argument.");
28 }
29
30 char *value = argv[*i];
31 if (strcmp(value, "on") == 0) {
32 output->dpms_state = DPMS_ON;
33 } else if (strcmp(value, "off") == 0) {
34 output->dpms_state = DPMS_OFF;
35 } else {
36 return cmd_results_new(CMD_INVALID, "output",
37 "Invalid dpms state, valid states are on/off.");
38 }
39 return NULL;
40}
41
42static struct cmd_results *cmd_output_mode(struct output_config *output,
43 int *i, int argc, char **argv) {
44 if (++*i >= argc) {
45 return cmd_results_new(CMD_INVALID, "output", "Missing mode argument.");
46 }
47
48 char *end;
49 output->width = strtol(argv[*i], &end, 10);
50 if (*end) {
51 // Format is 1234x4321
52 if (*end != 'x') {
53 return cmd_results_new(CMD_INVALID, "output",
54 "Invalid mode width.");
55 }
56 ++end;
57 output->height = strtol(end, &end, 10);
58 if (*end) {
59 if (*end != '@') {
60 return cmd_results_new(CMD_INVALID, "output",
61 "Invalid mode height.");
62 }
63 ++end;
64 output->refresh_rate = strtof(end, &end);
65 if (strcasecmp("Hz", end) != 0) {
66 return cmd_results_new(CMD_INVALID, "output",
67 "Invalid mode refresh rate.");
68 }
69 }
70 } else {
71 // Format is 1234 4321
72 if (++*i >= argc) {
73 return cmd_results_new(CMD_INVALID, "output",
74 "Missing mode argument (height).");
75 }
76 output->height = strtol(argv[*i], &end, 10);
77 if (*end) {
78 return cmd_results_new(CMD_INVALID, "output",
79 "Invalid mode height.");
80 }
81 }
82
83 return NULL;
84}
85
86static struct cmd_results *cmd_output_position(struct output_config *output,
87 int *i, int argc, char **argv) {
88 if (++*i >= argc) {
89 return cmd_results_new(CMD_INVALID, "output",
90 "Missing position argument.");
91 }
92
93 char *end;
94 output->x = strtol(argv[*i], &end, 10);
95 if (*end) {
96 // Format is 1234,4321
97 if (*end != ',') {
98 return cmd_results_new(CMD_INVALID, "output",
99 "Invalid position x.");
100 }
101 ++end;
102 output->y = strtol(end, &end, 10);
103 if (*end) {
104 return cmd_results_new(CMD_INVALID, "output",
105 "Invalid position y.");
106 }
107 } else {
108 // Format is 1234 4321 (legacy)
109 if (++*i >= argc) {
110 return cmd_results_new(CMD_INVALID, "output",
111 "Missing position argument (y).");
112 }
113 output->y = strtol(argv[*i], &end, 10);
114 if (*end) {
115 return cmd_results_new(CMD_INVALID, "output",
116 "Invalid position y.");
117 }
118 }
119
120 return NULL;
121}
122
123static struct cmd_results *cmd_output_scale(struct output_config *output,
124 int *i, int argc, char **argv) {
125 if (++*i >= argc) {
126 return cmd_results_new(CMD_INVALID, "output",
127 "Missing scale argument.");
128 }
129
130 char *end;
131 output->scale = strtof(argv[*i], &end);
132 if (*end) {
133 return cmd_results_new(CMD_INVALID, "output", "Invalid scale.");
134 }
135
136 return NULL;
137}
138
139static struct cmd_results *cmd_output_transform(struct output_config *output,
140 int *i, int argc, char **argv) {
141 if (++*i >= argc) {
142 return cmd_results_new(CMD_INVALID, "output",
143 "Missing transform argument.");
144 }
145
146 char *value = argv[*i];
147 if (strcmp(value, "normal") == 0) {
148 output->transform = WL_OUTPUT_TRANSFORM_NORMAL;
149 } else if (strcmp(value, "90") == 0) {
150 output->transform = WL_OUTPUT_TRANSFORM_90;
151 } else if (strcmp(value, "180") == 0) {
152 output->transform = WL_OUTPUT_TRANSFORM_180;
153 } else if (strcmp(value, "270") == 0) {
154 output->transform = WL_OUTPUT_TRANSFORM_270;
155 } else if (strcmp(value, "flipped") == 0) {
156 output->transform = WL_OUTPUT_TRANSFORM_FLIPPED;
157 } else if (strcmp(value, "flipped-90") == 0) {
158 output->transform = WL_OUTPUT_TRANSFORM_FLIPPED_90;
159 } else if (strcmp(value, "flipped-180") == 0) {
160 output->transform = WL_OUTPUT_TRANSFORM_FLIPPED_180;
161 } else if (strcmp(value, "flipped-270") == 0) {
162 output->transform = WL_OUTPUT_TRANSFORM_FLIPPED_270;
163 } else {
164 return cmd_results_new(CMD_INVALID, "output",
165 "Invalid output transform.");
166 }
167
168 return NULL;
169}
170
171static struct cmd_results *cmd_output_background(struct output_config *output,
172 int *i, int argc, char **argv) {
173 if (++*i >= argc) {
174 return cmd_results_new(CMD_INVALID, "output",
175 "Missing background file or color specification.");
176 }
177 const char *background = argv[*i];
178 if (*i + 1 >= argc) {
179 return cmd_results_new(CMD_INVALID, "output",
180 "Missing background scaling mode or `solid_color`.");
181 }
182 const char *background_option = argv[*i];
183
184 if (strcasecmp(background_option, "solid_color") == 0) {
185 output->background = strdup(background);
186 output->background_option = strdup("solid_color");
187 } else {
188 bool valid = false;
189 char *mode;
190 size_t j;
191 for (j = 0; j < (size_t)(argc - *i); ++j) {
192 mode = argv[*i + j];
193 size_t n = sizeof(bg_options) / sizeof(char *);
194 for (size_t k = 0; k < n; ++k) {
195 if (strcasecmp(mode, bg_options[k]) == 0) {
196 valid = true;
197 break;
198 }
199 }
200 if (valid) {
201 break;
202 }
203 }
204 if (!valid) {
205 return cmd_results_new(CMD_INVALID, "output",
206 "Missing background scaling mode.");
207 }
208
209 wordexp_t p;
210 char *src = join_args(argv + *i, j);
211 if (wordexp(src, &p, 0) != 0 || p.we_wordv[0] == NULL) {
212 return cmd_results_new(CMD_INVALID, "output",
213 "Invalid syntax (%s).", src);
214 }
215 free(src);
216 src = p.we_wordv[0];
217 if (config->reading && *src != '/') {
218 char *conf = strdup(config->current_config);
219 if (conf) {
220 char *conf_path = dirname(conf);
221 src = malloc(strlen(conf_path) + strlen(src) + 2);
222 if (src) {
223 sprintf(src, "%s/%s", conf_path, p.we_wordv[0]);
224 } else {
225 wlr_log(L_ERROR,
226 "Unable to allocate background source");
227 }
228 free(conf);
229 } else {
230 wlr_log(L_ERROR, "Unable to allocate background source");
231 }
232 }
233 if (!src || access(src, F_OK) == -1) {
234 wordfree(&p);
235 return cmd_results_new(CMD_INVALID, "output",
236 "Background file unreadable (%s).", src);
237 }
238
239 output->background = strdup(src);
240 output->background_option = strdup(mode);
241 if (src != p.we_wordv[0]) {
242 free(src);
243 }
244 wordfree(&p);
245
246 *i += j;
247 }
248
249 return NULL;
250}
251
252struct cmd_results *cmd_output(int argc, char **argv) { 22struct cmd_results *cmd_output(int argc, char **argv) {
253 struct cmd_results *error = checkarg(argc, "output", EXPECTED_AT_LEAST, 1); 23 struct cmd_results *error = checkarg(argc, "output", EXPECTED_AT_LEAST, 1);
254 if (error != NULL) { 24 if (error != NULL) {
@@ -260,40 +30,34 @@ struct cmd_results *cmd_output(int argc, char **argv) {
260 wlr_log(L_ERROR, "Failed to allocate output config"); 30 wlr_log(L_ERROR, "Failed to allocate output config");
261 return NULL; 31 return NULL;
262 } 32 }
33 argc--; argv++;
34
35 config->handler_context.output_config = output;
263 36
264 for (int i = 1; i < argc; ++i) { 37 while (argc > 0) {
265 const char *command = argv[i]; 38 config->handler_context.leftovers.argc = 0;
39 config->handler_context.leftovers.argv = NULL;
266 40
267 if (strcasecmp(command, "enable") == 0) { 41 if (find_handler(*argv, output_handlers, sizeof(output_handlers))) {
268 output->enabled = 1; 42 error = config_subcommand(argv, argc, output_handlers,
269 } else if (strcasecmp(command, "disable") == 0) { 43 sizeof(output_handlers));
270 output->enabled = 0;
271 } else if (strcasecmp(command, "mode") == 0 ||
272 strcasecmp(command, "resolution") == 0 ||
273 strcasecmp(command, "res") == 0) {
274 error = cmd_output_mode(output, &i, argc, argv);
275 } else if (strcasecmp(command, "position") == 0 ||
276 strcasecmp(command, "pos") == 0) {
277 error = cmd_output_position(output, &i, argc, argv);
278 } else if (strcasecmp(command, "scale") == 0) {
279 error = cmd_output_scale(output, &i, argc, argv);
280 } else if (strcasecmp(command, "transform") == 0) {
281 error = cmd_output_transform(output, &i, argc, argv);
282 } else if (strcasecmp(command, "background") == 0 ||
283 strcasecmp(command, "bg") == 0) {
284 error = cmd_output_background(output, &i, argc, argv);
285 } else if (strcasecmp(command, "dpms") == 0) {
286 error = cmd_output_dpms(output, &i, argc, argv);
287 } else { 44 } else {
288 error = cmd_results_new(CMD_INVALID, "output", 45 error = cmd_results_new(CMD_INVALID, "output",
289 "Invalid output subcommand: %s.", command); 46 "Invalid output subcommand: %s.", *argv);
290 } 47 }
291 48
292 if (error != NULL) { 49 if (error != NULL) {
293 goto fail; 50 goto fail;
294 } 51 }
52
53 argc = config->handler_context.leftovers.argc;
54 argv = config->handler_context.leftovers.argv;
295 } 55 }
296 56
57 config->handler_context.output_config = NULL;
58 config->handler_context.leftovers.argc = 0;
59 config->handler_context.leftovers.argv = NULL;
60
297 int i = list_seq_find(config->output_configs, output_name_cmp, output->name); 61 int i = list_seq_find(config->output_configs, output_name_cmp, output->name);
298 if (i >= 0) { 62 if (i >= 0) {
299 // Merge existing config 63 // Merge existing config
@@ -338,6 +102,7 @@ struct cmd_results *cmd_output(int argc, char **argv) {
338 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 102 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
339 103
340fail: 104fail:
105 config->handler_context.output_config = NULL;
341 free_output_config(output); 106 free_output_config(output);
342 return error; 107 return error;
343} 108}
diff --git a/sway/commands/output/background.c b/sway/commands/output/background.c
new file mode 100644
index 00000000..0c5c164f
--- /dev/null
+++ b/sway/commands/output/background.c
@@ -0,0 +1,105 @@
1#define _XOPEN_SOURCE 500
2#include <libgen.h>
3#include <strings.h>
4#include <unistd.h>
5#include <wordexp.h>
6#include "sway/commands.h"
7#include "sway/config.h"
8#include "log.h"
9#include "stringop.h"
10
11static const char *bg_options[] = {
12 "stretch",
13 "center",
14 "fill",
15 "fit",
16 "tile",
17};
18
19struct cmd_results *output_cmd_background(int argc, char **argv) {
20 if (!config->handler_context.output_config) {
21 return cmd_results_new(CMD_FAILURE, "output", "Missing output config");
22 }
23 if (!argc) {
24 return cmd_results_new(CMD_INVALID, "output",
25 "Missing background file or color specification.");
26 }
27 if (argc < 2) {
28 return cmd_results_new(CMD_INVALID, "output",
29 "Missing background scaling mode or `solid_color`.");
30 }
31
32 struct output_config *output = config->handler_context.output_config;
33
34 if (strcasecmp(argv[1], "solid_color") == 0) {
35 output->background = calloc(1, strlen(argv[0]) + 3);
36 snprintf(output->background, strlen(argv[0]) + 3, "\"%s\"", argv[0]);
37 output->background_option = strdup("solid_color");
38 argc -= 2; argv += 2;
39 } else {
40 bool valid = false;
41 char *mode;
42 size_t j;
43 for (j = 0; j < (size_t)argc; ++j) {
44 mode = argv[j];
45 size_t n = sizeof(bg_options) / sizeof(char *);
46 for (size_t k = 0; k < n; ++k) {
47 if (strcasecmp(mode, bg_options[k]) == 0) {
48 valid = true;
49 break;
50 }
51 }
52 if (valid) {
53 break;
54 }
55 }
56 if (!valid) {
57 return cmd_results_new(CMD_INVALID, "output",
58 "Missing background scaling mode.");
59 }
60
61 wordexp_t p;
62 char *src = join_args(argv, j);
63 if (wordexp(src, &p, 0) != 0 || p.we_wordv[0] == NULL) {
64 return cmd_results_new(CMD_INVALID, "output",
65 "Invalid syntax (%s).", src);
66 }
67 free(src);
68 src = p.we_wordv[0];
69 if (config->reading && *src != '/') {
70 char *conf = strdup(config->current_config);
71 if (conf) {
72 char *conf_path = dirname(conf);
73 src = malloc(strlen(conf_path) + strlen(src) + 2);
74 if (src) {
75 sprintf(src, "%s/%s", conf_path, p.we_wordv[0]);
76 } else {
77 wlr_log(L_ERROR,
78 "Unable to allocate background source");
79 }
80 free(conf);
81 } else {
82 wlr_log(L_ERROR, "Unable to allocate background source");
83 }
84 }
85 if (!src || access(src, F_OK) == -1) {
86 wordfree(&p);
87 return cmd_results_new(CMD_INVALID, "output",
88 "Background file unreadable (%s).", src);
89 }
90
91 output->background = strdup(src);
92 output->background_option = strdup(mode);
93 if (src != p.we_wordv[0]) {
94 free(src);
95 }
96 wordfree(&p);
97
98 argc -= j + 1; argv += j + 1;
99 }
100
101 config->handler_context.leftovers.argc = argc;
102 config->handler_context.leftovers.argv = argv;
103 return NULL;
104}
105
diff --git a/sway/commands/output/disable.c b/sway/commands/output/disable.c
new file mode 100644
index 00000000..65517c49
--- /dev/null
+++ b/sway/commands/output/disable.c
@@ -0,0 +1,13 @@
1#include "sway/commands.h"
2#include "sway/config.h"
3
4struct cmd_results *output_cmd_disable(int argc, char **argv) {
5 if (!config->handler_context.output_config) {
6 return cmd_results_new(CMD_FAILURE, "output", "Missing output config");
7 }
8 config->handler_context.output_config->enabled = 0;
9
10 config->handler_context.leftovers.argc = argc;
11 config->handler_context.leftovers.argv = argv;
12 return NULL;
13}
diff --git a/sway/commands/output/dpms.c b/sway/commands/output/dpms.c
new file mode 100644
index 00000000..0959ea6b
--- /dev/null
+++ b/sway/commands/output/dpms.c
@@ -0,0 +1,24 @@
1#include "sway/commands.h"
2#include "sway/config.h"
3
4struct cmd_results *output_cmd_dpms(int argc, char **argv) {
5 if (!config->handler_context.output_config) {
6 return cmd_results_new(CMD_FAILURE, "output", "Missing output config");
7 }
8 if (!argc) {
9 return cmd_results_new(CMD_INVALID, "output", "Missing dpms argument.");
10 }
11
12 if (strcmp(*argv, "on") == 0) {
13 config->handler_context.output_config->dpms_state = DPMS_ON;
14 } else if (strcmp(*argv, "off") == 0) {
15 config->handler_context.output_config->dpms_state = DPMS_OFF;
16 } else {
17 return cmd_results_new(CMD_INVALID, "output",
18 "Invalid dpms state, valid states are on/off.");
19 }
20
21 config->handler_context.leftovers.argc = argc - 1;
22 config->handler_context.leftovers.argv = argv + 1;
23 return NULL;
24}
diff --git a/sway/commands/output/enable.c b/sway/commands/output/enable.c
new file mode 100644
index 00000000..8e3314f8
--- /dev/null
+++ b/sway/commands/output/enable.c
@@ -0,0 +1,14 @@
1#include "sway/commands.h"
2#include "sway/config.h"
3
4struct cmd_results *output_cmd_enable(int argc, char **argv) {
5 if (!config->handler_context.output_config) {
6 return cmd_results_new(CMD_FAILURE, "output", "Missing output config");
7 }
8 config->handler_context.output_config->enabled = 1;
9
10 config->handler_context.leftovers.argc = argc;
11 config->handler_context.leftovers.argv = argv;
12 return NULL;
13}
14
diff --git a/sway/commands/output/mode.c b/sway/commands/output/mode.c
new file mode 100644
index 00000000..daec6d44
--- /dev/null
+++ b/sway/commands/output/mode.c
@@ -0,0 +1,55 @@
1#include <strings.h>
2#include "sway/commands.h"
3#include "sway/config.h"
4
5struct cmd_results *output_cmd_mode(int argc, char **argv) {
6 if (!config->handler_context.output_config) {
7 return cmd_results_new(CMD_FAILURE, "output", "Missing output config");
8 }
9 if (!argc) {
10 return cmd_results_new(CMD_INVALID, "output", "Missing mode argument.");
11 }
12
13 struct output_config *output = config->handler_context.output_config;
14
15 char *end;
16 output->width = strtol(*argv, &end, 10);
17 if (*end) {
18 // Format is 1234x4321
19 if (*end != 'x') {
20 return cmd_results_new(CMD_INVALID, "output",
21 "Invalid mode width.");
22 }
23 ++end;
24 output->height = strtol(end, &end, 10);
25 if (*end) {
26 if (*end != '@') {
27 return cmd_results_new(CMD_INVALID, "output",
28 "Invalid mode height.");
29 }
30 ++end;
31 output->refresh_rate = strtof(end, &end);
32 if (strcasecmp("Hz", end) != 0) {
33 return cmd_results_new(CMD_INVALID, "output",
34 "Invalid mode refresh rate.");
35 }
36 }
37 } else {
38 // Format is 1234 4321
39 if (!argc) {
40 return cmd_results_new(CMD_INVALID, "output",
41 "Missing mode argument (height).");
42 }
43 argc--; argv++;
44 output->height = strtol(*argv, &end, 10);
45 if (*end) {
46 return cmd_results_new(CMD_INVALID, "output",
47 "Invalid mode height.");
48 }
49 }
50
51 config->handler_context.leftovers.argc = argc - 1;
52 config->handler_context.leftovers.argv = argv + 1;
53 return NULL;
54}
55
diff --git a/sway/commands/output/position.c b/sway/commands/output/position.c
new file mode 100644
index 00000000..c2aeb281
--- /dev/null
+++ b/sway/commands/output/position.c
@@ -0,0 +1,46 @@
1#include <strings.h>
2#include "sway/commands.h"
3#include "sway/config.h"
4
5struct cmd_results *output_cmd_position(int argc, char **argv) {
6 if (!config->handler_context.output_config) {
7 return cmd_results_new(CMD_FAILURE, "output", "Missing output config");
8 }
9 if (!argc) {
10 return cmd_results_new(CMD_INVALID, "output",
11 "Missing position argument.");
12 }
13
14 char *end;
15 config->handler_context.output_config->x = strtol(*argv, &end, 10);
16 if (*end) {
17 // Format is 1234,4321
18 if (*end != ',') {
19 return cmd_results_new(CMD_INVALID, "output",
20 "Invalid position x.");
21 }
22 ++end;
23 config->handler_context.output_config->y = strtol(end, &end, 10);
24 if (*end) {
25 return cmd_results_new(CMD_INVALID, "output",
26 "Invalid position y.");
27 }
28 } else {
29 // Format is 1234 4321 (legacy)
30 if (!argc) {
31 return cmd_results_new(CMD_INVALID, "output",
32 "Missing position argument (y).");
33 }
34 argc--; argv++;
35 config->handler_context.output_config->y = strtol(*argv, &end, 10);
36 if (*end) {
37 return cmd_results_new(CMD_INVALID, "output",
38 "Invalid position y.");
39 }
40 }
41
42 config->handler_context.leftovers.argc = argc - 1;
43 config->handler_context.leftovers.argv = argv + 1;
44 return NULL;
45}
46
diff --git a/sway/commands/output/scale.c b/sway/commands/output/scale.c
new file mode 100644
index 00000000..0b4cc131
--- /dev/null
+++ b/sway/commands/output/scale.c
@@ -0,0 +1,23 @@
1#include <strings.h>
2#include "sway/commands.h"
3#include "sway/config.h"
4
5struct cmd_results *output_cmd_scale(int argc, char **argv) {
6 if (!config->handler_context.output_config) {
7 return cmd_results_new(CMD_FAILURE, "output", "Missing output config");
8 }
9 if (!argc) {
10 return cmd_results_new(CMD_INVALID, "output",
11 "Missing scale argument.");
12 }
13
14 char *end;
15 config->handler_context.output_config->scale = strtof(*argv, &end);
16 if (*end) {
17 return cmd_results_new(CMD_INVALID, "output", "Invalid scale.");
18 }
19
20 config->handler_context.leftovers.argc = argc - 1;
21 config->handler_context.leftovers.argv = argv + 1;
22 return NULL;
23}
diff --git a/sway/commands/output/transform.c b/sway/commands/output/transform.c
new file mode 100644
index 00000000..f9a94d64
--- /dev/null
+++ b/sway/commands/output/transform.c
@@ -0,0 +1,39 @@
1#include <string.h>
2#include "sway/commands.h"
3#include "sway/config.h"
4
5struct cmd_results *output_cmd_transform(int argc, char **argv) {
6 if (!config->handler_context.output_config) {
7 return cmd_results_new(CMD_FAILURE, "output", "Missing output config");
8 }
9 if (!argc) {
10 return cmd_results_new(CMD_INVALID, "output",
11 "Missing transform argument.");
12 }
13
14 struct output_config *output = config->handler_context.output_config;
15 if (strcmp(*argv, "normal") == 0) {
16 output->transform = WL_OUTPUT_TRANSFORM_NORMAL;
17 } else if (strcmp(*argv, "90") == 0) {
18 output->transform = WL_OUTPUT_TRANSFORM_90;
19 } else if (strcmp(*argv, "180") == 0) {
20 output->transform = WL_OUTPUT_TRANSFORM_180;
21 } else if (strcmp(*argv, "270") == 0) {
22 output->transform = WL_OUTPUT_TRANSFORM_270;
23 } else if (strcmp(*argv, "flipped") == 0) {
24 output->transform = WL_OUTPUT_TRANSFORM_FLIPPED;
25 } else if (strcmp(*argv, "flipped-90") == 0) {
26 output->transform = WL_OUTPUT_TRANSFORM_FLIPPED_90;
27 } else if (strcmp(*argv, "flipped-180") == 0) {
28 output->transform = WL_OUTPUT_TRANSFORM_FLIPPED_180;
29 } else if (strcmp(*argv, "flipped-270") == 0) {
30 output->transform = WL_OUTPUT_TRANSFORM_FLIPPED_270;
31 } else {
32 return cmd_results_new(CMD_INVALID, "output",
33 "Invalid output transform.");
34 }
35
36 config->handler_context.leftovers.argc = argc - 1;
37 config->handler_context.leftovers.argv = argv + 1;
38 return NULL;
39}
diff --git a/sway/desktop/layer_shell.c b/sway/desktop/layer_shell.c
index b60aa487..2d355b74 100644
--- a/sway/desktop/layer_shell.c
+++ b/sway/desktop/layer_shell.c
@@ -257,6 +257,11 @@ static void unmap(struct sway_layer_surface *sway_layer) {
257 } 257 }
258 output_damage_surface(output, sway_layer->geo.x, sway_layer->geo.y, 258 output_damage_surface(output, sway_layer->geo.x, sway_layer->geo.y,
259 sway_layer->layer_surface->surface, true); 259 sway_layer->layer_surface->surface, true);
260
261 struct sway_seat *seat = input_manager_current_seat(input_manager);
262 if (seat->focused_layer == sway_layer->layer_surface) {
263 seat_set_focus_layer(seat, NULL);
264 }
260} 265}
261 266
262static void handle_destroy(struct wl_listener *listener, void *data) { 267static void handle_destroy(struct wl_listener *listener, void *data) {
diff --git a/sway/desktop/output.c b/sway/desktop/output.c
index a9ff9782..acc9caae 100644
--- a/sway/desktop/output.c
+++ b/sway/desktop/output.c
@@ -846,11 +846,24 @@ static void render_output(struct sway_output *output, struct timespec *when,
846 846
847 wlr_renderer_begin(renderer, wlr_output->width, wlr_output->height); 847 wlr_renderer_begin(renderer, wlr_output->width, wlr_output->height);
848 848
849 bool damage_whole_before_swap = false;
849 if (!pixman_region32_not_empty(damage)) { 850 if (!pixman_region32_not_empty(damage)) {
850 // Output isn't damaged but needs buffer swap 851 // Output isn't damaged but needs buffer swap
851 goto renderer_end; 852 goto renderer_end;
852 } 853 }
853 854
855 const char *damage_debug = getenv("SWAY_DAMAGE_DEBUG");
856 if (damage_debug != NULL) {
857 if (strcmp(damage_debug, "highlight") == 0) {
858 wlr_renderer_clear(renderer, (float[]){1, 1, 0, 1});
859 damage_whole_before_swap = true;
860 } else if (strcmp(damage_debug, "rerender") == 0) {
861 int width, height;
862 wlr_output_transformed_resolution(wlr_output, &width, &height);
863 pixman_region32_union_rect(damage, damage, 0, 0, width, height);
864 }
865 }
866
854 struct sway_container *workspace = output_get_active_workspace(output); 867 struct sway_container *workspace = output_get_active_workspace(output);
855 868
856 if (workspace->sway_workspace->fullscreen) { 869 if (workspace->sway_workspace->fullscreen) {
@@ -905,6 +918,12 @@ renderer_end:
905 wlr_output->transform_matrix, 0, 0, 1); 918 wlr_output->transform_matrix, 0, 0, 1);
906 } 919 }
907 920
921 if (damage_whole_before_swap || root_container.sway_root->debug_tree) {
922 int width, height;
923 wlr_output_transformed_resolution(wlr_output, &width, &height);
924 pixman_region32_union_rect(damage, damage, 0, 0, width, height);
925 }
926
908 wlr_renderer_scissor(renderer, NULL); 927 wlr_renderer_scissor(renderer, NULL);
909 wlr_renderer_end(renderer); 928 wlr_renderer_end(renderer);
910 if (!wlr_output_damage_swap_buffers(output->damage, when, damage)) { 929 if (!wlr_output_damage_swap_buffers(output->damage, when, damage)) {
diff --git a/sway/desktop/xwayland.c b/sway/desktop/xwayland.c
index 75bfb7b2..6447b711 100644
--- a/sway/desktop/xwayland.c
+++ b/sway/desktop/xwayland.c
@@ -77,6 +77,21 @@ static void unmanaged_handle_unmap(struct wl_listener *listener, void *data) {
77 desktop_damage_surface(xsurface->surface, xsurface->x, xsurface->y, true); 77 desktop_damage_surface(xsurface->surface, xsurface->x, xsurface->y, true);
78 wl_list_remove(&surface->link); 78 wl_list_remove(&surface->link);
79 wl_list_remove(&surface->commit.link); 79 wl_list_remove(&surface->commit.link);
80
81 if (!wlr_xwayland_surface_is_unmanaged(xsurface)) {
82 struct sway_seat *seat = input_manager_current_seat(input_manager);
83 if (seat->wlr_seat->keyboard_state.focused_surface ==
84 xsurface->surface) {
85 // Restore focus
86 struct sway_container *previous =
87 seat_get_focus_inactive(seat, &root_container);
88 if (previous) {
89 // Hack to get seat to re-focus the return value of get_focus
90 seat_set_focus(seat, previous->parent);
91 seat_set_focus(seat, previous);
92 }
93 }
94 }
80} 95}
81 96
82static void unmanaged_handle_destroy(struct wl_listener *listener, void *data) { 97static void unmanaged_handle_destroy(struct wl_listener *listener, void *data) {
diff --git a/sway/input/cursor.c b/sway/input/cursor.c
index 16e5427b..d6e17ae1 100644
--- a/sway/input/cursor.c
+++ b/sway/input/cursor.c
@@ -264,7 +264,7 @@ void dispatch_cursor_button(struct sway_cursor *cursor,
264 if (new_ws != old_ws) { 264 if (new_ws != old_ws) {
265 seat_set_focus(cursor->seat, cont); 265 seat_set_focus(cursor->seat, cont);
266 } 266 }
267 } else { 267 } else if (cont) {
268 seat_set_focus(cursor->seat, cont); 268 seat_set_focus(cursor->seat, cont);
269 } 269 }
270 270
diff --git a/sway/input/seat.c b/sway/input/seat.c
index d35cbeef..071ef020 100644
--- a/sway/input/seat.c
+++ b/sway/input/seat.c
@@ -658,7 +658,8 @@ void seat_set_focus_layer(struct sway_seat *seat,
658 struct wlr_layer_surface *layer) { 658 struct wlr_layer_surface *layer) {
659 if (!layer && seat->focused_layer) { 659 if (!layer && seat->focused_layer) {
660 seat->focused_layer = NULL; 660 seat->focused_layer = NULL;
661 struct sway_container *previous = seat_get_focus(seat); 661 struct sway_container *previous =
662 seat_get_focus_inactive(seat, &root_container);
662 if (previous) { 663 if (previous) {
663 wlr_log(L_DEBUG, "Returning focus to %p %s '%s'", previous, 664 wlr_log(L_DEBUG, "Returning focus to %p %s '%s'", previous,
664 container_type_to_str(previous->type), previous->name); 665 container_type_to_str(previous->type), previous->name);
@@ -728,14 +729,14 @@ struct sway_container *seat_get_focus_inactive(struct sway_seat *seat,
728 729
729struct sway_container *seat_get_active_child(struct sway_seat *seat, 730struct sway_container *seat_get_active_child(struct sway_seat *seat,
730 struct sway_container *container) { 731 struct sway_container *container) {
731 struct sway_container *focus = seat_get_focus_inactive(seat, container); 732 struct sway_seat_container *current = NULL;
732 if (!focus) { 733 wl_list_for_each(current, &seat->focus_stack, link) {
733 return NULL; 734 if (current->container->parent == container &&
734 } 735 current->container->layout != L_FLOATING) {
735 while (focus->parent != container) { 736 return current->container;
736 focus = focus->parent; 737 }
737 } 738 }
738 return focus; 739 return NULL;
739} 740}
740 741
741struct sway_container *seat_get_focus(struct sway_seat *seat) { 742struct sway_container *seat_get_focus(struct sway_seat *seat) {
diff --git a/sway/meson.build b/sway/meson.build
index 4c038583..b6bb02a7 100644
--- a/sway/meson.build
+++ b/sway/meson.build
@@ -120,6 +120,15 @@ sway_sources = files(
120 'commands/input/xkb_rules.c', 120 'commands/input/xkb_rules.c',
121 'commands/input/xkb_variant.c', 121 'commands/input/xkb_variant.c',
122 122
123 'commands/output/background.c',
124 'commands/output/disable.c',
125 'commands/output/dpms.c',
126 'commands/output/enable.c',
127 'commands/output/mode.c',
128 'commands/output/position.c',
129 'commands/output/scale.c',
130 'commands/output/transform.c',
131
123 'tree/arrange.c', 132 'tree/arrange.c',
124 'tree/container.c', 133 'tree/container.c',
125 'tree/layout.c', 134 'tree/layout.c',
diff --git a/sway/tree/container.c b/sway/tree/container.c
index 9e70da09..d0d26631 100644
--- a/sway/tree/container.c
+++ b/sway/tree/container.c
@@ -204,9 +204,17 @@ static struct sway_container *container_workspace_destroy(
204 } 204 }
205 } 205 }
206 206
207 free(workspace->sway_workspace); 207 struct sway_workspace *sway_workspace = workspace->sway_workspace;
208
209 // This emits the destroy event and also destroys the swayc.
208 _container_destroy(workspace); 210 _container_destroy(workspace);
209 211
212 // Clean up the floating container
213 sway_workspace->floating->parent = NULL;
214 _container_destroy(sway_workspace->floating);
215
216 free(sway_workspace);
217
210 if (output) { 218 if (output) {
211 output_damage_whole(output->sway_output); 219 output_damage_whole(output->sway_output);
212 } 220 }
diff --git a/sway/tree/layout.c b/sway/tree/layout.c
index d88948dc..79e7c87e 100644
--- a/sway/tree/layout.c
+++ b/sway/tree/layout.c
@@ -412,19 +412,15 @@ void container_move(struct sway_container *container,
412 } 412 }
413 case C_WORKSPACE: 413 case C_WORKSPACE:
414 if (!is_parallel(current->layout, move_dir)) { 414 if (!is_parallel(current->layout, move_dir)) {
415 if (current->children->length > 2) { 415 if (current->children->length >= 2) {
416 wlr_log(L_DEBUG, "Rejiggering the workspace (%d kiddos)", 416 wlr_log(L_DEBUG, "Rejiggering the workspace (%d kiddos)",
417 current->children->length); 417 current->children->length);
418 workspace_rejigger(current, container, move_dir); 418 workspace_rejigger(current, container, move_dir);
419 } else if (current->children->length == 2) { 419 return;
420 wlr_log(L_DEBUG, "Changing workspace layout"); 420 } else {
421 current->layout = 421 wlr_log(L_DEBUG, "Selecting output");
422 move_dir == MOVE_LEFT || move_dir == MOVE_RIGHT ? 422 current = current->parent;
423 L_HORIZ : L_VERT;
424 container_insert_child(current, container, offs < 0 ? 0 : 1);
425 arrange_workspace(current);
426 } 423 }
427 return;
428 } else if (current->layout == L_TABBED 424 } else if (current->layout == L_TABBED
429 || current->layout == L_STACKED) { 425 || current->layout == L_STACKED) {
430 wlr_log(L_DEBUG, "Rejiggering out of tabs/stacks"); 426 wlr_log(L_DEBUG, "Rejiggering out of tabs/stacks");
@@ -520,7 +516,7 @@ void container_move(struct sway_container *container,
520 wlr_log(L_DEBUG, "Reparenting container (perpendicular)"); 516 wlr_log(L_DEBUG, "Reparenting container (perpendicular)");
521 struct sway_container *focus_inactive = seat_get_focus_inactive( 517 struct sway_container *focus_inactive = seat_get_focus_inactive(
522 config->handler_context.seat, sibling); 518 config->handler_context.seat, sibling);
523 if (focus_inactive) { 519 if (focus_inactive && focus_inactive != sibling) {
524 while (focus_inactive->parent != sibling) { 520 while (focus_inactive->parent != sibling) {
525 focus_inactive = focus_inactive->parent; 521 focus_inactive = focus_inactive->parent;
526 } 522 }
diff --git a/sway/tree/output.c b/sway/tree/output.c
index 6c7044a2..8823eba0 100644
--- a/sway/tree/output.c
+++ b/sway/tree/output.c
@@ -8,10 +8,6 @@
8 8
9struct sway_container *output_create( 9struct sway_container *output_create(
10 struct sway_output *sway_output) { 10 struct sway_output *sway_output) {
11 struct wlr_box size;
12 wlr_output_effective_resolution(sway_output->wlr_output, &size.width,
13 &size.height);
14
15 const char *name = sway_output->wlr_output->name; 11 const char *name = sway_output->wlr_output->name;
16 char identifier[128]; 12 char identifier[128];
17 output_get_identifier(identifier, sizeof(identifier), sway_output); 13 output_get_identifier(identifier, sizeof(identifier), sway_output);
@@ -54,6 +50,12 @@ struct sway_container *output_create(
54 container_add_child(&root_container, output); 50 container_add_child(&root_container, output);
55 load_swaybars(); 51 load_swaybars();
56 52
53 struct wlr_box size;
54 wlr_output_effective_resolution(sway_output->wlr_output, &size.width,
55 &size.height);
56 output->width = size.width;
57 output->height = size.height;
58
57 // Create workspace 59 // Create workspace
58 char *ws_name = workspace_next_name(output->name); 60 char *ws_name = workspace_next_name(output->name);
59 wlr_log(L_DEBUG, "Creating default workspace %s", ws_name); 61 wlr_log(L_DEBUG, "Creating default workspace %s", ws_name);
diff --git a/sway/tree/view.c b/sway/tree/view.c
index 79441d49..c9c82405 100644
--- a/sway/tree/view.c
+++ b/sway/tree/view.c
@@ -790,8 +790,10 @@ void view_update_title(struct sway_view *view, bool force) {
790 view->swayc->formatted_title = NULL; 790 view->swayc->formatted_title = NULL;
791 } 791 }
792 container_calculate_title_height(view->swayc); 792 container_calculate_title_height(view->swayc);
793 container_update_title_textures(view->swayc);
794 config_update_font_height(false); 793 config_update_font_height(false);
794
795 // Update title after the global font height is updated
796 container_update_title_textures(view->swayc);
795} 797}
796 798
797static bool find_by_mark_iterator(struct sway_container *con, 799static bool find_by_mark_iterator(struct sway_container *con,