diff options
Diffstat (limited to 'sway')
90 files changed, 4423 insertions, 2359 deletions
diff --git a/sway/commands.c b/sway/commands.c index fe1e98b5..2160a970 100644 --- a/sway/commands.c +++ b/sway/commands.c | |||
@@ -42,15 +42,17 @@ struct cmd_results *checkarg(int argc, const char *name, enum expected_args type | |||
42 | } | 42 | } |
43 | 43 | ||
44 | /* Keep alphabetized */ | 44 | /* Keep alphabetized */ |
45 | static struct cmd_handler handlers[] = { | 45 | static const struct cmd_handler handlers[] = { |
46 | { "assign", cmd_assign }, | 46 | { "assign", cmd_assign }, |
47 | { "bar", cmd_bar }, | 47 | { "bar", cmd_bar }, |
48 | { "bindcode", cmd_bindcode }, | 48 | { "bindcode", cmd_bindcode }, |
49 | { "bindgesture", cmd_bindgesture }, | ||
49 | { "bindswitch", cmd_bindswitch }, | 50 | { "bindswitch", cmd_bindswitch }, |
50 | { "bindsym", cmd_bindsym }, | 51 | { "bindsym", cmd_bindsym }, |
51 | { "client.background", cmd_client_noop }, | 52 | { "client.background", cmd_client_noop }, |
52 | { "client.focused", cmd_client_focused }, | 53 | { "client.focused", cmd_client_focused }, |
53 | { "client.focused_inactive", cmd_client_focused_inactive }, | 54 | { "client.focused_inactive", cmd_client_focused_inactive }, |
55 | { "client.focused_tab_title", cmd_client_focused_tab_title }, | ||
54 | { "client.placeholder", cmd_client_noop }, | 56 | { "client.placeholder", cmd_client_noop }, |
55 | { "client.unfocused", cmd_client_unfocused }, | 57 | { "client.unfocused", cmd_client_unfocused }, |
56 | { "client.urgent", cmd_client_urgent }, | 58 | { "client.urgent", cmd_client_urgent }, |
@@ -91,6 +93,7 @@ static struct cmd_handler handlers[] = { | |||
91 | { "titlebar_border_thickness", cmd_titlebar_border_thickness }, | 93 | { "titlebar_border_thickness", cmd_titlebar_border_thickness }, |
92 | { "titlebar_padding", cmd_titlebar_padding }, | 94 | { "titlebar_padding", cmd_titlebar_padding }, |
93 | { "unbindcode", cmd_unbindcode }, | 95 | { "unbindcode", cmd_unbindcode }, |
96 | { "unbindgesture", cmd_unbindgesture }, | ||
94 | { "unbindswitch", cmd_unbindswitch }, | 97 | { "unbindswitch", cmd_unbindswitch }, |
95 | { "unbindsym", cmd_unbindsym }, | 98 | { "unbindsym", cmd_unbindsym }, |
96 | { "workspace", cmd_workspace }, | 99 | { "workspace", cmd_workspace }, |
@@ -98,7 +101,7 @@ static struct cmd_handler handlers[] = { | |||
98 | }; | 101 | }; |
99 | 102 | ||
100 | /* Config-time only commands. Keep alphabetized */ | 103 | /* Config-time only commands. Keep alphabetized */ |
101 | static struct cmd_handler config_handlers[] = { | 104 | static const struct cmd_handler config_handlers[] = { |
102 | { "default_orientation", cmd_default_orientation }, | 105 | { "default_orientation", cmd_default_orientation }, |
103 | { "include", cmd_include }, | 106 | { "include", cmd_include }, |
104 | { "swaybg_command", cmd_swaybg_command }, | 107 | { "swaybg_command", cmd_swaybg_command }, |
@@ -108,7 +111,7 @@ static struct cmd_handler config_handlers[] = { | |||
108 | }; | 111 | }; |
109 | 112 | ||
110 | /* Runtime-only commands. Keep alphabetized */ | 113 | /* Runtime-only commands. Keep alphabetized */ |
111 | static struct cmd_handler command_handlers[] = { | 114 | static const struct cmd_handler command_handlers[] = { |
112 | { "border", cmd_border }, | 115 | { "border", cmd_border }, |
113 | { "create_output", cmd_create_output }, | 116 | { "create_output", cmd_create_output }, |
114 | { "exit", cmd_exit }, | 117 | { "exit", cmd_exit }, |
@@ -144,22 +147,22 @@ static int handler_compare(const void *_a, const void *_b) { | |||
144 | return strcasecmp(a->command, b->command); | 147 | return strcasecmp(a->command, b->command); |
145 | } | 148 | } |
146 | 149 | ||
147 | struct cmd_handler *find_handler(char *line, struct cmd_handler *handlers, | 150 | const struct cmd_handler *find_handler(char *line, |
148 | size_t handlers_size) { | 151 | const struct cmd_handler *handlers, size_t handlers_size) { |
149 | if (!handlers || !handlers_size) { | 152 | if (!handlers || !handlers_size) { |
150 | return NULL; | 153 | return NULL; |
151 | } | 154 | } |
152 | struct cmd_handler query = { .command = line }; | 155 | const struct cmd_handler query = { .command = line }; |
153 | return bsearch(&query, handlers, | 156 | return bsearch(&query, handlers, |
154 | handlers_size / sizeof(struct cmd_handler), | 157 | handlers_size / sizeof(struct cmd_handler), |
155 | sizeof(struct cmd_handler), handler_compare); | 158 | sizeof(struct cmd_handler), handler_compare); |
156 | } | 159 | } |
157 | 160 | ||
158 | static struct cmd_handler *find_handler_ex(char *line, | 161 | static const struct cmd_handler *find_handler_ex(char *line, |
159 | struct cmd_handler *config_handlers, size_t config_handlers_size, | 162 | const struct cmd_handler *config_handlers, size_t config_handlers_size, |
160 | struct cmd_handler *command_handlers, size_t command_handlers_size, | 163 | const struct cmd_handler *command_handlers, size_t command_handlers_size, |
161 | struct cmd_handler *handlers, size_t handlers_size) { | 164 | const struct cmd_handler *handlers, size_t handlers_size) { |
162 | struct cmd_handler *handler = NULL; | 165 | const struct cmd_handler *handler = NULL; |
163 | if (config->reading) { | 166 | if (config->reading) { |
164 | handler = find_handler(line, config_handlers, config_handlers_size); | 167 | handler = find_handler(line, config_handlers, config_handlers_size); |
165 | } else if (config->active) { | 168 | } else if (config->active) { |
@@ -168,16 +171,17 @@ static struct cmd_handler *find_handler_ex(char *line, | |||
168 | return handler ? handler : find_handler(line, handlers, handlers_size); | 171 | return handler ? handler : find_handler(line, handlers, handlers_size); |
169 | } | 172 | } |
170 | 173 | ||
171 | static struct cmd_handler *find_core_handler(char *line) { | 174 | static const struct cmd_handler *find_core_handler(char *line) { |
172 | return find_handler_ex(line, config_handlers, sizeof(config_handlers), | 175 | return find_handler_ex(line, config_handlers, sizeof(config_handlers), |
173 | command_handlers, sizeof(command_handlers), | 176 | command_handlers, sizeof(command_handlers), |
174 | handlers, sizeof(handlers)); | 177 | handlers, sizeof(handlers)); |
175 | } | 178 | } |
176 | 179 | ||
177 | static void set_config_node(struct sway_node *node) { | 180 | static void set_config_node(struct sway_node *node, bool node_overridden) { |
178 | config->handler_context.node = node; | 181 | config->handler_context.node = node; |
179 | config->handler_context.container = NULL; | 182 | config->handler_context.container = NULL; |
180 | config->handler_context.workspace = NULL; | 183 | config->handler_context.workspace = NULL; |
184 | config->handler_context.node_overridden = node_overridden; | ||
181 | 185 | ||
182 | if (node == NULL) { | 186 | if (node == NULL) { |
183 | return; | 187 | return; |
@@ -186,7 +190,7 @@ static void set_config_node(struct sway_node *node) { | |||
186 | switch (node->type) { | 190 | switch (node->type) { |
187 | case N_CONTAINER: | 191 | case N_CONTAINER: |
188 | config->handler_context.container = node->sway_container; | 192 | config->handler_context.container = node->sway_container; |
189 | config->handler_context.workspace = node->sway_container->workspace; | 193 | config->handler_context.workspace = node->sway_container->pending.workspace; |
190 | break; | 194 | break; |
191 | case N_WORKSPACE: | 195 | case N_WORKSPACE: |
192 | config->handler_context.workspace = node->sway_workspace; | 196 | config->handler_context.workspace = node->sway_workspace; |
@@ -202,6 +206,7 @@ list_t *execute_command(char *_exec, struct sway_seat *seat, | |||
202 | char *cmd; | 206 | char *cmd; |
203 | char matched_delim = ';'; | 207 | char matched_delim = ';'; |
204 | list_t *containers = NULL; | 208 | list_t *containers = NULL; |
209 | bool using_criteria = false; | ||
205 | 210 | ||
206 | if (seat == NULL) { | 211 | if (seat == NULL) { |
207 | // passing a NULL seat means we just pick the default seat | 212 | // passing a NULL seat means we just pick the default seat |
@@ -225,7 +230,7 @@ list_t *execute_command(char *_exec, struct sway_seat *seat, | |||
225 | for (; isspace(*head); ++head) {} | 230 | for (; isspace(*head); ++head) {} |
226 | // Extract criteria (valid for this command list only). | 231 | // Extract criteria (valid for this command list only). |
227 | if (matched_delim == ';') { | 232 | if (matched_delim == ';') { |
228 | config->handler_context.using_criteria = false; | 233 | using_criteria = false; |
229 | if (*head == '[') { | 234 | if (*head == '[') { |
230 | char *error = NULL; | 235 | char *error = NULL; |
231 | struct criteria *criteria = criteria_parse(head, &error); | 236 | struct criteria *criteria = criteria_parse(head, &error); |
@@ -239,7 +244,7 @@ list_t *execute_command(char *_exec, struct sway_seat *seat, | |||
239 | containers = criteria_get_containers(criteria); | 244 | containers = criteria_get_containers(criteria); |
240 | head += strlen(criteria->raw); | 245 | head += strlen(criteria->raw); |
241 | criteria_destroy(criteria); | 246 | criteria_destroy(criteria); |
242 | config->handler_context.using_criteria = true; | 247 | using_criteria = true; |
243 | // Skip leading whitespace | 248 | // Skip leading whitespace |
244 | for (; isspace(*head); ++head) {} | 249 | for (; isspace(*head); ++head) {} |
245 | } | 250 | } |
@@ -265,7 +270,7 @@ list_t *execute_command(char *_exec, struct sway_seat *seat, | |||
265 | } | 270 | } |
266 | } | 271 | } |
267 | } | 272 | } |
268 | struct cmd_handler *handler = find_core_handler(argv[0]); | 273 | const struct cmd_handler *handler = find_core_handler(argv[0]); |
269 | if (!handler) { | 274 | if (!handler) { |
270 | list_add(res_list, cmd_results_new(CMD_INVALID, | 275 | list_add(res_list, cmd_results_new(CMD_INVALID, |
271 | "Unknown/invalid command '%s'", argv[0])); | 276 | "Unknown/invalid command '%s'", argv[0])); |
@@ -278,11 +283,14 @@ list_t *execute_command(char *_exec, struct sway_seat *seat, | |||
278 | argv[i] = do_var_replacement(argv[i]); | 283 | argv[i] = do_var_replacement(argv[i]); |
279 | } | 284 | } |
280 | 285 | ||
281 | if (!config->handler_context.using_criteria) { | 286 | |
282 | // The container or workspace which this command will run on. | 287 | if (!using_criteria) { |
283 | struct sway_node *node = con ? &con->node : | 288 | if (con) { |
284 | seat_get_focus_inactive(seat, &root->node); | 289 | set_config_node(&con->node, true); |
285 | set_config_node(node); | 290 | } else { |
291 | set_config_node(seat_get_focus_inactive(seat, &root->node), | ||
292 | false); | ||
293 | } | ||
286 | struct cmd_results *res = handler->handle(argc-1, argv+1); | 294 | struct cmd_results *res = handler->handle(argc-1, argv+1); |
287 | list_add(res_list, res); | 295 | list_add(res_list, res); |
288 | if (res->status == CMD_INVALID) { | 296 | if (res->status == CMD_INVALID) { |
@@ -296,7 +304,7 @@ list_t *execute_command(char *_exec, struct sway_seat *seat, | |||
296 | struct cmd_results *fail_res = NULL; | 304 | struct cmd_results *fail_res = NULL; |
297 | for (int i = 0; i < containers->length; ++i) { | 305 | for (int i = 0; i < containers->length; ++i) { |
298 | struct sway_container *container = containers->items[i]; | 306 | struct sway_container *container = containers->items[i]; |
299 | set_config_node(&container->node); | 307 | set_config_node(&container->node, true); |
300 | struct cmd_results *res = handler->handle(argc-1, argv+1); | 308 | struct cmd_results *res = handler->handle(argc-1, argv+1); |
301 | if (res->status == CMD_SUCCESS) { | 309 | if (res->status == CMD_SUCCESS) { |
302 | free_cmd_results(res); | 310 | free_cmd_results(res); |
@@ -370,7 +378,7 @@ struct cmd_results *config_command(char *exec, char **new_block) { | |||
370 | 378 | ||
371 | // Determine the command handler | 379 | // Determine the command handler |
372 | sway_log(SWAY_INFO, "Config command: %s", exec); | 380 | sway_log(SWAY_INFO, "Config command: %s", exec); |
373 | struct cmd_handler *handler = find_core_handler(argv[0]); | 381 | const struct cmd_handler *handler = find_core_handler(argv[0]); |
374 | if (!handler || !handler->handle) { | 382 | if (!handler || !handler->handle) { |
375 | const char *error = handler | 383 | const char *error = handler |
376 | ? "Command '%s' is shimmed, but unimplemented" | 384 | ? "Command '%s' is shimmed, but unimplemented" |
@@ -401,6 +409,7 @@ struct cmd_results *config_command(char *exec, char **new_block) { | |||
401 | && handler->handle != cmd_bindsym | 409 | && handler->handle != cmd_bindsym |
402 | && handler->handle != cmd_bindcode | 410 | && handler->handle != cmd_bindcode |
403 | && handler->handle != cmd_bindswitch | 411 | && handler->handle != cmd_bindswitch |
412 | && handler->handle != cmd_bindgesture | ||
404 | && handler->handle != cmd_set | 413 | && handler->handle != cmd_set |
405 | && handler->handle != cmd_for_window | 414 | && handler->handle != cmd_for_window |
406 | && (*argv[i] == '\"' || *argv[i] == '\'')) { | 415 | && (*argv[i] == '\"' || *argv[i] == '\'')) { |
@@ -418,12 +427,12 @@ cleanup: | |||
418 | } | 427 | } |
419 | 428 | ||
420 | struct cmd_results *config_subcommand(char **argv, int argc, | 429 | struct cmd_results *config_subcommand(char **argv, int argc, |
421 | struct cmd_handler *handlers, size_t handlers_size) { | 430 | const struct cmd_handler *handlers, size_t handlers_size) { |
422 | char *command = join_args(argv, argc); | 431 | char *command = join_args(argv, argc); |
423 | sway_log(SWAY_DEBUG, "Subcommand: %s", command); | 432 | sway_log(SWAY_DEBUG, "Subcommand: %s", command); |
424 | free(command); | 433 | free(command); |
425 | 434 | ||
426 | struct cmd_handler *handler = find_handler(argv[0], handlers, | 435 | const struct cmd_handler *handler = find_handler(argv[0], handlers, |
427 | handlers_size); | 436 | handlers_size); |
428 | if (!handler) { | 437 | if (!handler) { |
429 | return cmd_results_new(CMD_INVALID, | 438 | return cmd_results_new(CMD_INVALID, |
@@ -453,41 +462,13 @@ struct cmd_results *config_commands_command(char *exec) { | |||
453 | goto cleanup; | 462 | goto cleanup; |
454 | } | 463 | } |
455 | 464 | ||
456 | struct cmd_handler *handler = find_handler(cmd, NULL, 0); | 465 | const struct cmd_handler *handler = find_handler(cmd, NULL, 0); |
457 | if (!handler && strcmp(cmd, "*") != 0) { | 466 | if (!handler && strcmp(cmd, "*") != 0) { |
458 | results = cmd_results_new(CMD_INVALID, | 467 | results = cmd_results_new(CMD_INVALID, |
459 | "Unknown/invalid command '%s'", cmd); | 468 | "Unknown/invalid command '%s'", cmd); |
460 | goto cleanup; | 469 | goto cleanup; |
461 | } | 470 | } |
462 | 471 | ||
463 | enum command_context context = 0; | ||
464 | |||
465 | struct { | ||
466 | char *name; | ||
467 | enum command_context context; | ||
468 | } context_names[] = { | ||
469 | { "config", CONTEXT_CONFIG }, | ||
470 | { "binding", CONTEXT_BINDING }, | ||
471 | { "ipc", CONTEXT_IPC }, | ||
472 | { "criteria", CONTEXT_CRITERIA }, | ||
473 | { "all", CONTEXT_ALL }, | ||
474 | }; | ||
475 | |||
476 | for (int i = 1; i < argc; ++i) { | ||
477 | size_t j; | ||
478 | for (j = 0; j < sizeof(context_names) / sizeof(context_names[0]); ++j) { | ||
479 | if (strcmp(context_names[j].name, argv[i]) == 0) { | ||
480 | break; | ||
481 | } | ||
482 | } | ||
483 | if (j == sizeof(context_names) / sizeof(context_names[0])) { | ||
484 | results = cmd_results_new(CMD_INVALID, | ||
485 | "Invalid command context %s", argv[i]); | ||
486 | goto cleanup; | ||
487 | } | ||
488 | context |= context_names[j].context; | ||
489 | } | ||
490 | |||
491 | results = cmd_results_new(CMD_SUCCESS, NULL); | 472 | results = cmd_results_new(CMD_SUCCESS, NULL); |
492 | 473 | ||
493 | cleanup: | 474 | cleanup: |
@@ -504,13 +485,19 @@ struct cmd_results *cmd_results_new(enum cmd_status status, | |||
504 | } | 485 | } |
505 | results->status = status; | 486 | results->status = status; |
506 | if (format) { | 487 | if (format) { |
507 | char *error = malloc(256); | 488 | char *error = NULL; |
508 | va_list args; | 489 | va_list args; |
509 | va_start(args, format); | 490 | va_start(args, format); |
510 | if (error) { | 491 | int slen = vsnprintf(NULL, 0, format, args); |
511 | vsnprintf(error, 256, format, args); | ||
512 | } | ||
513 | va_end(args); | 492 | va_end(args); |
493 | if (slen > 0) { | ||
494 | error = malloc(slen + 1); | ||
495 | if (error != NULL) { | ||
496 | va_start(args, format); | ||
497 | vsnprintf(error, slen + 1, format, args); | ||
498 | va_end(args); | ||
499 | } | ||
500 | } | ||
514 | results->error = error; | 501 | results->error = error; |
515 | } else { | 502 | } else { |
516 | results->error = NULL; | 503 | results->error = NULL; |
diff --git a/sway/commands/bar.c b/sway/commands/bar.c index d42b7fc2..8571d282 100644 --- a/sway/commands/bar.c +++ b/sway/commands/bar.c | |||
@@ -8,7 +8,7 @@ | |||
8 | #include "log.h" | 8 | #include "log.h" |
9 | 9 | ||
10 | // Must be in alphabetical order for bsearch | 10 | // Must be in alphabetical order for bsearch |
11 | static struct cmd_handler bar_handlers[] = { | 11 | static const struct cmd_handler bar_handlers[] = { |
12 | { "bindcode", bar_cmd_bindcode }, | 12 | { "bindcode", bar_cmd_bindcode }, |
13 | { "binding_mode_indicator", bar_cmd_binding_mode_indicator }, | 13 | { "binding_mode_indicator", bar_cmd_binding_mode_indicator }, |
14 | { "bindsym", bar_cmd_bindsym }, | 14 | { "bindsym", bar_cmd_bindsym }, |
@@ -41,7 +41,7 @@ static struct cmd_handler bar_handlers[] = { | |||
41 | }; | 41 | }; |
42 | 42 | ||
43 | // Must be in alphabetical order for bsearch | 43 | // Must be in alphabetical order for bsearch |
44 | static struct cmd_handler bar_config_handlers[] = { | 44 | static const struct cmd_handler bar_config_handlers[] = { |
45 | { "id", bar_cmd_id }, | 45 | { "id", bar_cmd_id }, |
46 | { "swaybar_command", bar_cmd_swaybar_command }, | 46 | { "swaybar_command", bar_cmd_swaybar_command }, |
47 | }; | 47 | }; |
@@ -116,6 +116,7 @@ struct cmd_results *cmd_bar(int argc, char **argv) { | |||
116 | if (res && res->status != CMD_SUCCESS) { | 116 | if (res && res->status != CMD_SUCCESS) { |
117 | if (id) { | 117 | if (id) { |
118 | free_bar_config(config->current_bar); | 118 | free_bar_config(config->current_bar); |
119 | config->current_bar = NULL; | ||
119 | id = NULL; | 120 | id = NULL; |
120 | } | 121 | } |
121 | return res; | 122 | return res; |
diff --git a/sway/commands/bar/colors.c b/sway/commands/bar/colors.c index 2d5b22bf..275fa3c6 100644 --- a/sway/commands/bar/colors.c +++ b/sway/commands/bar/colors.c | |||
@@ -4,7 +4,7 @@ | |||
4 | #include "util.h" | 4 | #include "util.h" |
5 | 5 | ||
6 | // Must be in alphabetical order for bsearch | 6 | // Must be in alphabetical order for bsearch |
7 | static struct cmd_handler bar_colors_handlers[] = { | 7 | static const struct cmd_handler bar_colors_handlers[] = { |
8 | { "active_workspace", bar_colors_cmd_active_workspace }, | 8 | { "active_workspace", bar_colors_cmd_active_workspace }, |
9 | { "background", bar_colors_cmd_background }, | 9 | { "background", bar_colors_cmd_background }, |
10 | { "binding_mode", bar_colors_cmd_binding_mode }, | 10 | { "binding_mode", bar_colors_cmd_binding_mode }, |
diff --git a/sway/commands/bar/font.c b/sway/commands/bar/font.c index 62987f3e..891c87af 100644 --- a/sway/commands/bar/font.c +++ b/sway/commands/bar/font.c | |||
@@ -11,7 +11,20 @@ struct cmd_results *bar_cmd_font(int argc, char **argv) { | |||
11 | } | 11 | } |
12 | char *font = join_args(argv, argc); | 12 | char *font = join_args(argv, argc); |
13 | free(config->current_bar->font); | 13 | free(config->current_bar->font); |
14 | config->current_bar->font = font; | 14 | |
15 | if (strncmp(font, "pango:", 6) == 0) { | ||
16 | if (config->current_bar->pango_markup == PANGO_MARKUP_DEFAULT) { | ||
17 | config->current_bar->pango_markup = true; | ||
18 | } | ||
19 | config->current_bar->font = strdup(font + 6); | ||
20 | } else { | ||
21 | if (config->current_bar->pango_markup == PANGO_MARKUP_DEFAULT) { | ||
22 | config->current_bar->pango_markup = false; | ||
23 | } | ||
24 | config->current_bar->font = strdup(font); | ||
25 | } | ||
26 | |||
27 | free(font); | ||
15 | sway_log(SWAY_DEBUG, "Settings font '%s' for bar: %s", | 28 | sway_log(SWAY_DEBUG, "Settings font '%s' for bar: %s", |
16 | config->current_bar->font, config->current_bar->id); | 29 | config->current_bar->font, config->current_bar->id); |
17 | return cmd_results_new(CMD_SUCCESS, NULL); | 30 | return cmd_results_new(CMD_SUCCESS, NULL); |
diff --git a/sway/commands/bar/hidden_state.c b/sway/commands/bar/hidden_state.c index 1f08a5d2..8b661e3a 100644 --- a/sway/commands/bar/hidden_state.c +++ b/sway/commands/bar/hidden_state.c | |||
@@ -54,7 +54,7 @@ struct cmd_results *bar_cmd_hidden_state(int argc, char **argv) { | |||
54 | } | 54 | } |
55 | 55 | ||
56 | const char *state = argv[0]; | 56 | const char *state = argv[0]; |
57 | if (config->reading) { | 57 | if (config->current_bar) { |
58 | error = bar_set_hidden_state(config->current_bar, state); | 58 | error = bar_set_hidden_state(config->current_bar, state); |
59 | } else { | 59 | } else { |
60 | const char *id = argc == 2 ? argv[1] : NULL; | 60 | const char *id = argc == 2 ? argv[1] : NULL; |
diff --git a/sway/commands/bar/mode.c b/sway/commands/bar/mode.c index 8b3fb275..7c2f423b 100644 --- a/sway/commands/bar/mode.c +++ b/sway/commands/bar/mode.c | |||
@@ -58,7 +58,7 @@ struct cmd_results *bar_cmd_mode(int argc, char **argv) { | |||
58 | } | 58 | } |
59 | 59 | ||
60 | const char *mode = argv[0]; | 60 | const char *mode = argv[0]; |
61 | if (config->reading) { | 61 | if (config->current_bar) { |
62 | error = bar_set_mode(config->current_bar, mode); | 62 | error = bar_set_mode(config->current_bar, mode); |
63 | } else { | 63 | } else { |
64 | const char *id = argc == 2 ? argv[1] : NULL; | 64 | const char *id = argc == 2 ? argv[1] : NULL; |
diff --git a/sway/commands/bind.c b/sway/commands/bind.c index f6e58d99..c0b383db 100644 --- a/sway/commands/bind.c +++ b/sway/commands/bind.c | |||
@@ -8,6 +8,7 @@ | |||
8 | #include <wlr/types/wlr_cursor.h> | 8 | #include <wlr/types/wlr_cursor.h> |
9 | #include "sway/commands.h" | 9 | #include "sway/commands.h" |
10 | #include "sway/config.h" | 10 | #include "sway/config.h" |
11 | #include "sway/desktop/transaction.h" | ||
11 | #include "sway/input/cursor.h" | 12 | #include "sway/input/cursor.h" |
12 | #include "sway/input/keyboard.h" | 13 | #include "sway/input/keyboard.h" |
13 | #include "sway/ipc-server.h" | 14 | #include "sway/ipc-server.h" |
@@ -46,7 +47,7 @@ static bool binding_switch_compare(struct sway_switch_binding *binding_a, | |||
46 | if (binding_a->type != binding_b->type) { | 47 | if (binding_a->type != binding_b->type) { |
47 | return false; | 48 | return false; |
48 | } | 49 | } |
49 | if (binding_a->state != binding_b->state) { | 50 | if (binding_a->trigger != binding_b->trigger) { |
50 | return false; | 51 | return false; |
51 | } | 52 | } |
52 | if ((binding_a->flags & BINDING_LOCKED) != | 53 | if ((binding_a->flags & BINDING_LOCKED) != |
@@ -371,6 +372,7 @@ static struct cmd_results *cmd_bindsym_or_bindcode(int argc, char **argv, | |||
371 | strlen("--input-device=")) == 0) { | 372 | strlen("--input-device=")) == 0) { |
372 | free(binding->input); | 373 | free(binding->input); |
373 | binding->input = strdup(argv[0] + strlen("--input-device=")); | 374 | binding->input = strdup(argv[0] + strlen("--input-device=")); |
375 | strip_quotes(binding->input); | ||
374 | } else if (strcmp("--no-warn", argv[0]) == 0) { | 376 | } else if (strcmp("--no-warn", argv[0]) == 0) { |
375 | warn = false; | 377 | warn = false; |
376 | } else if (strcmp("--no-repeat", argv[0]) == 0) { | 378 | } else if (strcmp("--no-repeat", argv[0]) == 0) { |
@@ -550,17 +552,17 @@ struct cmd_results *cmd_bind_or_unbind_switch(int argc, char **argv, | |||
550 | "unknown switch %s)", bindtype, split->items[0]); | 552 | "unknown switch %s)", bindtype, split->items[0]); |
551 | } | 553 | } |
552 | if (strcmp(split->items[1], "on") == 0) { | 554 | if (strcmp(split->items[1], "on") == 0) { |
553 | binding->state = WLR_SWITCH_STATE_ON; | 555 | binding->trigger = SWAY_SWITCH_TRIGGER_ON; |
554 | } else if (strcmp(split->items[1], "off") == 0) { | 556 | } else if (strcmp(split->items[1], "off") == 0) { |
555 | binding->state = WLR_SWITCH_STATE_OFF; | 557 | binding->trigger = SWAY_SWITCH_TRIGGER_OFF; |
556 | } else if (strcmp(split->items[1], "toggle") == 0) { | 558 | } else if (strcmp(split->items[1], "toggle") == 0) { |
557 | binding->state = WLR_SWITCH_STATE_TOGGLE; | 559 | binding->trigger = SWAY_SWITCH_TRIGGER_TOGGLE; |
558 | } else { | 560 | } else { |
559 | free_switch_binding(binding); | 561 | free_switch_binding(binding); |
560 | return cmd_results_new(CMD_FAILURE, | 562 | return cmd_results_new(CMD_FAILURE, |
561 | "Invalid %s command " | 563 | "Invalid %s command " |
562 | "(expected switch state: unknown state %d)", | 564 | "(expected switch state: unknown state %s)", |
563 | bindtype, split->items[0]); | 565 | bindtype, split->items[1]); |
564 | } | 566 | } |
565 | list_free_items_and_destroy(split); | 567 | list_free_items_and_destroy(split); |
566 | 568 | ||
@@ -642,6 +644,8 @@ void seat_execute_command(struct sway_seat *seat, struct sway_binding *binding) | |||
642 | if (success) { | 644 | if (success) { |
643 | ipc_event_binding(binding); | 645 | ipc_event_binding(binding); |
644 | } | 646 | } |
647 | |||
648 | transaction_commit_dirty(); | ||
645 | } | 649 | } |
646 | 650 | ||
647 | /** | 651 | /** |
diff --git a/sway/commands/border.c b/sway/commands/border.c index 647663ac..7818fc96 100644 --- a/sway/commands/border.c +++ b/sway/commands/border.c | |||
@@ -19,11 +19,11 @@ static void set_border(struct sway_container *con, | |||
19 | view_set_csd_from_server(con->view, false); | 19 | view_set_csd_from_server(con->view, false); |
20 | } else if (!con->view->using_csd && new_border == B_CSD) { | 20 | } else if (!con->view->using_csd && new_border == B_CSD) { |
21 | view_set_csd_from_server(con->view, true); | 21 | view_set_csd_from_server(con->view, true); |
22 | con->saved_border = con->border; | 22 | con->saved_border = con->pending.border; |
23 | } | 23 | } |
24 | } | 24 | } |
25 | if (new_border != B_CSD || container_is_floating(con)) { | 25 | if (new_border != B_CSD || container_is_floating(con)) { |
26 | con->border = new_border; | 26 | con->pending.border = new_border; |
27 | } | 27 | } |
28 | if (con->view) { | 28 | if (con->view) { |
29 | con->view->using_csd = new_border == B_CSD; | 29 | con->view->using_csd = new_border == B_CSD; |
@@ -35,7 +35,7 @@ static void border_toggle(struct sway_container *con) { | |||
35 | set_border(con, B_NONE); | 35 | set_border(con, B_NONE); |
36 | return; | 36 | return; |
37 | } | 37 | } |
38 | switch (con->border) { | 38 | switch (con->pending.border) { |
39 | case B_NONE: | 39 | case B_NONE: |
40 | set_border(con, B_PIXEL); | 40 | set_border(con, B_PIXEL); |
41 | break; | 41 | break; |
@@ -88,7 +88,7 @@ struct cmd_results *cmd_border(int argc, char **argv) { | |||
88 | "or 'border pixel <px>'"); | 88 | "or 'border pixel <px>'"); |
89 | } | 89 | } |
90 | if (argc == 2) { | 90 | if (argc == 2) { |
91 | container->border_thickness = atoi(argv[1]); | 91 | container->pending.border_thickness = atoi(argv[1]); |
92 | } | 92 | } |
93 | 93 | ||
94 | if (container_is_floating(container)) { | 94 | if (container_is_floating(container)) { |
diff --git a/sway/commands/client.c b/sway/commands/client.c index dd0694df..77263145 100644 --- a/sway/commands/client.c +++ b/sway/commands/client.c | |||
@@ -18,6 +18,12 @@ static struct cmd_results *handle_command(int argc, char **argv, char *cmd_name, | |||
18 | return error; | 18 | return error; |
19 | } | 19 | } |
20 | 20 | ||
21 | if (argc > 3 && strcmp(cmd_name, "client.focused_tab_title") == 0) { | ||
22 | sway_log(SWAY_ERROR, | ||
23 | "Warning: indicator and child_border colors have no effect for %s", | ||
24 | cmd_name); | ||
25 | } | ||
26 | |||
21 | struct border_colors colors = {0}; | 27 | struct border_colors colors = {0}; |
22 | const char *ind_hex = argc > 3 ? argv[3] : default_indicator; | 28 | const char *ind_hex = argc > 3 ? argv[3] : default_indicator; |
23 | const char *child_hex = argc > 4 ? argv[4] : argv[1]; // def to background | 29 | const char *child_hex = argc > 4 ? argv[4] : argv[1]; // def to background |
@@ -80,3 +86,13 @@ struct cmd_results *cmd_client_noop(int argc, char **argv) { | |||
80 | sway_log(SWAY_INFO, "Warning: %s is ignored by sway", argv[-1]); | 86 | sway_log(SWAY_INFO, "Warning: %s is ignored by sway", argv[-1]); |
81 | return cmd_results_new(CMD_SUCCESS, NULL); | 87 | return cmd_results_new(CMD_SUCCESS, NULL); |
82 | } | 88 | } |
89 | |||
90 | struct cmd_results *cmd_client_focused_tab_title(int argc, char **argv) { | ||
91 | struct cmd_results *result = handle_command(argc, argv, | ||
92 | "client.focused_tab_title", | ||
93 | &config->border_colors.focused_tab_title, "#2e9ef4ff"); | ||
94 | if (result && result->status == CMD_SUCCESS) { | ||
95 | config->has_focused_tab_title = true; | ||
96 | } | ||
97 | return result; | ||
98 | } | ||
diff --git a/sway/commands/exec_always.c b/sway/commands/exec_always.c index 39e48a44..b35065c1 100644 --- a/sway/commands/exec_always.c +++ b/sway/commands/exec_always.c | |||
@@ -7,6 +7,7 @@ | |||
7 | #include <signal.h> | 7 | #include <signal.h> |
8 | #include "sway/commands.h" | 8 | #include "sway/commands.h" |
9 | #include "sway/config.h" | 9 | #include "sway/config.h" |
10 | #include "sway/server.h" | ||
10 | #include "sway/tree/container.h" | 11 | #include "sway/tree/container.h" |
11 | #include "sway/tree/root.h" | 12 | #include "sway/tree/root.h" |
12 | #include "sway/tree/workspace.h" | 13 | #include "sway/tree/workspace.h" |
@@ -26,7 +27,7 @@ struct cmd_results *cmd_exec_validate(int argc, char **argv) { | |||
26 | 27 | ||
27 | struct cmd_results *cmd_exec_process(int argc, char **argv) { | 28 | struct cmd_results *cmd_exec_process(int argc, char **argv) { |
28 | struct cmd_results *error = NULL; | 29 | struct cmd_results *error = NULL; |
29 | char *tmp = NULL; | 30 | char *cmd = NULL; |
30 | if (strcmp(argv[0], "--no-startup-id") == 0) { | 31 | if (strcmp(argv[0], "--no-startup-id") == 0) { |
31 | sway_log(SWAY_INFO, "exec switch '--no-startup-id' not supported, ignored."); | 32 | sway_log(SWAY_INFO, "exec switch '--no-startup-id' not supported, ignored."); |
32 | --argc; ++argv; | 33 | --argc; ++argv; |
@@ -36,17 +37,12 @@ struct cmd_results *cmd_exec_process(int argc, char **argv) { | |||
36 | } | 37 | } |
37 | 38 | ||
38 | if (argc == 1 && (argv[0][0] == '\'' || argv[0][0] == '"')) { | 39 | if (argc == 1 && (argv[0][0] == '\'' || argv[0][0] == '"')) { |
39 | tmp = strdup(argv[0]); | 40 | cmd = strdup(argv[0]); |
40 | strip_quotes(tmp); | 41 | strip_quotes(cmd); |
41 | } else { | 42 | } else { |
42 | tmp = join_args(argv, argc); | 43 | cmd = join_args(argv, argc); |
43 | } | 44 | } |
44 | 45 | ||
45 | // Put argument into cmd array | ||
46 | char cmd[4096]; | ||
47 | strncpy(cmd, tmp, sizeof(cmd) - 1); | ||
48 | cmd[sizeof(cmd) - 1] = 0; | ||
49 | free(tmp); | ||
50 | sway_log(SWAY_DEBUG, "Executing %s", cmd); | 46 | sway_log(SWAY_DEBUG, "Executing %s", cmd); |
51 | 47 | ||
52 | int fd[2]; | 48 | int fd[2]; |
@@ -58,15 +54,18 @@ struct cmd_results *cmd_exec_process(int argc, char **argv) { | |||
58 | // Fork process | 54 | // Fork process |
59 | if ((pid = fork()) == 0) { | 55 | if ((pid = fork()) == 0) { |
60 | // Fork child process again | 56 | // Fork child process again |
57 | restore_nofile_limit(); | ||
61 | setsid(); | 58 | setsid(); |
62 | sigset_t set; | 59 | sigset_t set; |
63 | sigemptyset(&set); | 60 | sigemptyset(&set); |
64 | sigprocmask(SIG_SETMASK, &set, NULL); | 61 | sigprocmask(SIG_SETMASK, &set, NULL); |
62 | signal(SIGPIPE, SIG_DFL); | ||
65 | close(fd[0]); | 63 | close(fd[0]); |
66 | if ((child = fork()) == 0) { | 64 | if ((child = fork()) == 0) { |
67 | close(fd[1]); | 65 | close(fd[1]); |
68 | execl("/bin/sh", "/bin/sh", "-c", cmd, (void *)NULL); | 66 | execlp("sh", "sh", "-c", cmd, (void *)NULL); |
69 | _exit(0); | 67 | sway_log_errno(SWAY_ERROR, "execlp failed"); |
68 | _exit(1); | ||
70 | } | 69 | } |
71 | ssize_t s = 0; | 70 | ssize_t s = 0; |
72 | while ((size_t)s < sizeof(pid_t)) { | 71 | while ((size_t)s < sizeof(pid_t)) { |
@@ -75,10 +74,12 @@ struct cmd_results *cmd_exec_process(int argc, char **argv) { | |||
75 | close(fd[1]); | 74 | close(fd[1]); |
76 | _exit(0); // Close child process | 75 | _exit(0); // Close child process |
77 | } else if (pid < 0) { | 76 | } else if (pid < 0) { |
77 | free(cmd); | ||
78 | close(fd[0]); | 78 | close(fd[0]); |
79 | close(fd[1]); | 79 | close(fd[1]); |
80 | return cmd_results_new(CMD_FAILURE, "fork() failed"); | 80 | return cmd_results_new(CMD_FAILURE, "fork() failed"); |
81 | } | 81 | } |
82 | free(cmd); | ||
82 | close(fd[1]); // close write | 83 | close(fd[1]); // close write |
83 | ssize_t s = 0; | 84 | ssize_t s = 0; |
84 | while ((size_t)s < sizeof(pid_t)) { | 85 | while ((size_t)s < sizeof(pid_t)) { |
diff --git a/sway/commands/floating.c b/sway/commands/floating.c index ce123345..74f6522c 100644 --- a/sway/commands/floating.c +++ b/sway/commands/floating.c | |||
@@ -40,8 +40,8 @@ struct cmd_results *cmd_floating(int argc, char **argv) { | |||
40 | // If the container is in a floating split container, | 40 | // If the container is in a floating split container, |
41 | // operate on the split container instead of the child. | 41 | // operate on the split container instead of the child. |
42 | if (container_is_floating_or_child(container)) { | 42 | if (container_is_floating_or_child(container)) { |
43 | while (container->parent) { | 43 | while (container->pending.parent) { |
44 | container = container->parent; | 44 | container = container->pending.parent; |
45 | } | 45 | } |
46 | } | 46 | } |
47 | 47 | ||
@@ -51,8 +51,8 @@ struct cmd_results *cmd_floating(int argc, char **argv) { | |||
51 | container_set_floating(container, wants_floating); | 51 | container_set_floating(container, wants_floating); |
52 | 52 | ||
53 | // Floating containers in the scratchpad should be ignored | 53 | // Floating containers in the scratchpad should be ignored |
54 | if (container->workspace) { | 54 | if (container->pending.workspace) { |
55 | arrange_workspace(container->workspace); | 55 | arrange_workspace(container->pending.workspace); |
56 | } | 56 | } |
57 | 57 | ||
58 | return cmd_results_new(CMD_SUCCESS, NULL); | 58 | return cmd_results_new(CMD_SUCCESS, NULL); |
diff --git a/sway/commands/focus.c b/sway/commands/focus.c index 79b7aed5..facd82de 100644 --- a/sway/commands/focus.c +++ b/sway/commands/focus.c | |||
@@ -54,7 +54,7 @@ static bool get_direction_from_next_prev(struct sway_container *container, | |||
54 | } else { | 54 | } else { |
55 | return false; | 55 | return false; |
56 | } | 56 | } |
57 | 57 | ||
58 | return true; | 58 | return true; |
59 | } | 59 | } |
60 | 60 | ||
@@ -141,9 +141,9 @@ static struct sway_node *node_get_in_direction_tiling( | |||
141 | struct sway_container *wrap_candidate = NULL; | 141 | struct sway_container *wrap_candidate = NULL; |
142 | struct sway_container *current = container; | 142 | struct sway_container *current = container; |
143 | while (current) { | 143 | while (current) { |
144 | if (current->fullscreen_mode == FULLSCREEN_WORKSPACE) { | 144 | if (current->pending.fullscreen_mode == FULLSCREEN_WORKSPACE) { |
145 | // Fullscreen container with a direction - go straight to outputs | 145 | // Fullscreen container with a direction - go straight to outputs |
146 | struct sway_output *output = current->workspace->output; | 146 | struct sway_output *output = current->pending.workspace->output; |
147 | struct sway_output *new_output = | 147 | struct sway_output *new_output = |
148 | output_get_in_direction(output, dir); | 148 | output_get_in_direction(output, dir); |
149 | if (!new_output) { | 149 | if (!new_output) { |
@@ -151,7 +151,7 @@ static struct sway_node *node_get_in_direction_tiling( | |||
151 | } | 151 | } |
152 | return get_node_in_output_direction(new_output, dir); | 152 | return get_node_in_output_direction(new_output, dir); |
153 | } | 153 | } |
154 | if (current->fullscreen_mode == FULLSCREEN_GLOBAL) { | 154 | if (current->pending.fullscreen_mode == FULLSCREEN_GLOBAL) { |
155 | return NULL; | 155 | return NULL; |
156 | } | 156 | } |
157 | 157 | ||
@@ -202,11 +202,11 @@ static struct sway_node *node_get_in_direction_tiling( | |||
202 | } | 202 | } |
203 | } | 203 | } |
204 | 204 | ||
205 | current = current->parent; | 205 | current = current->pending.parent; |
206 | } | 206 | } |
207 | 207 | ||
208 | // Check a different output | 208 | // Check a different output |
209 | struct sway_output *output = container->workspace->output; | 209 | struct sway_output *output = container->pending.workspace->output; |
210 | struct sway_output *new_output = output_get_in_direction(output, dir); | 210 | struct sway_output *new_output = output_get_in_direction(output, dir); |
211 | if ((config->focus_wrapping != WRAP_WORKSPACE || | 211 | if ((config->focus_wrapping != WRAP_WORKSPACE || |
212 | container->node.type == N_WORKSPACE) && new_output) { | 212 | container->node.type == N_WORKSPACE) && new_output) { |
@@ -226,23 +226,23 @@ static struct sway_node *node_get_in_direction_tiling( | |||
226 | static struct sway_node *node_get_in_direction_floating( | 226 | static struct sway_node *node_get_in_direction_floating( |
227 | struct sway_container *con, struct sway_seat *seat, | 227 | struct sway_container *con, struct sway_seat *seat, |
228 | enum wlr_direction dir) { | 228 | enum wlr_direction dir) { |
229 | double ref_lx = con->x + con->width / 2; | 229 | double ref_lx = con->pending.x + con->pending.width / 2; |
230 | double ref_ly = con->y + con->height / 2; | 230 | double ref_ly = con->pending.y + con->pending.height / 2; |
231 | double closest_distance = DBL_MAX; | 231 | double closest_distance = DBL_MAX; |
232 | struct sway_container *closest_con = NULL; | 232 | struct sway_container *closest_con = NULL; |
233 | 233 | ||
234 | if (!con->workspace) { | 234 | if (!con->pending.workspace) { |
235 | return NULL; | 235 | return NULL; |
236 | } | 236 | } |
237 | 237 | ||
238 | for (int i = 0; i < con->workspace->floating->length; i++) { | 238 | for (int i = 0; i < con->pending.workspace->floating->length; i++) { |
239 | struct sway_container *floater = con->workspace->floating->items[i]; | 239 | struct sway_container *floater = con->pending.workspace->floating->items[i]; |
240 | if (floater == con) { | 240 | if (floater == con) { |
241 | continue; | 241 | continue; |
242 | } | 242 | } |
243 | float distance = dir == WLR_DIRECTION_LEFT || dir == WLR_DIRECTION_RIGHT | 243 | float distance = dir == WLR_DIRECTION_LEFT || dir == WLR_DIRECTION_RIGHT |
244 | ? (floater->x + floater->width / 2) - ref_lx | 244 | ? (floater->pending.x + floater->pending.width / 2) - ref_lx |
245 | : (floater->y + floater->height / 2) - ref_ly; | 245 | : (floater->pending.y + floater->pending.height / 2) - ref_ly; |
246 | if (dir == WLR_DIRECTION_LEFT || dir == WLR_DIRECTION_UP) { | 246 | if (dir == WLR_DIRECTION_LEFT || dir == WLR_DIRECTION_UP) { |
247 | distance = -distance; | 247 | distance = -distance; |
248 | } | 248 | } |
@@ -267,6 +267,11 @@ static struct cmd_results *focus_mode(struct sway_workspace *ws, | |||
267 | new_focus = seat_get_focus_inactive_tiling(seat, ws); | 267 | new_focus = seat_get_focus_inactive_tiling(seat, ws); |
268 | } | 268 | } |
269 | if (new_focus) { | 269 | if (new_focus) { |
270 | struct sway_container *new_focus_view = | ||
271 | seat_get_focus_inactive_view(seat, &new_focus->node); | ||
272 | if (new_focus_view) { | ||
273 | new_focus = new_focus_view; | ||
274 | } | ||
270 | seat_set_focus_container(seat, new_focus); | 275 | seat_set_focus_container(seat, new_focus); |
271 | 276 | ||
272 | // If we're on the floating layer and the floating container area | 277 | // If we're on the floating layer and the floating container area |
@@ -280,7 +285,7 @@ static struct cmd_results *focus_mode(struct sway_workspace *ws, | |||
280 | } | 285 | } |
281 | } else { | 286 | } else { |
282 | return cmd_results_new(CMD_FAILURE, | 287 | return cmd_results_new(CMD_FAILURE, |
283 | "Failed to find a %s container in workspace", | 288 | "Failed to find a %s container in workspace.", |
284 | floating ? "floating" : "tiling"); | 289 | floating ? "floating" : "tiling"); |
285 | } | 290 | } |
286 | return cmd_results_new(CMD_SUCCESS, NULL); | 291 | return cmd_results_new(CMD_SUCCESS, NULL); |
@@ -290,7 +295,7 @@ static struct cmd_results *focus_output(struct sway_seat *seat, | |||
290 | int argc, char **argv) { | 295 | int argc, char **argv) { |
291 | if (!argc) { | 296 | if (!argc) { |
292 | return cmd_results_new(CMD_INVALID, | 297 | return cmd_results_new(CMD_INVALID, |
293 | "Expected 'focus output <direction|name>'"); | 298 | "Expected 'focus output <direction|name>'."); |
294 | } | 299 | } |
295 | char *identifier = join_args(argv, argc); | 300 | char *identifier = join_args(argv, argc); |
296 | struct sway_output *output = output_by_name_or_id(identifier); | 301 | struct sway_output *output = output_by_name_or_id(identifier); |
@@ -300,13 +305,13 @@ static struct cmd_results *focus_output(struct sway_seat *seat, | |||
300 | if (!parse_direction(identifier, &direction)) { | 305 | if (!parse_direction(identifier, &direction)) { |
301 | free(identifier); | 306 | free(identifier); |
302 | return cmd_results_new(CMD_INVALID, | 307 | return cmd_results_new(CMD_INVALID, |
303 | "There is no output with that name"); | 308 | "There is no output with that name."); |
304 | } | 309 | } |
305 | struct sway_workspace *ws = seat_get_focused_workspace(seat); | 310 | struct sway_workspace *ws = seat_get_focused_workspace(seat); |
306 | if (!ws) { | 311 | if (!ws) { |
307 | free(identifier); | 312 | free(identifier); |
308 | return cmd_results_new(CMD_FAILURE, | 313 | return cmd_results_new(CMD_FAILURE, |
309 | "No focused workspace to base directions off of"); | 314 | "No focused workspace to base directions off of."); |
310 | } | 315 | } |
311 | output = output_get_in_direction(ws->output, direction); | 316 | output = output_get_in_direction(ws->output, direction); |
312 | 317 | ||
@@ -334,7 +339,7 @@ static struct cmd_results *focus_output(struct sway_seat *seat, | |||
334 | static struct cmd_results *focus_parent(void) { | 339 | static struct cmd_results *focus_parent(void) { |
335 | struct sway_seat *seat = config->handler_context.seat; | 340 | struct sway_seat *seat = config->handler_context.seat; |
336 | struct sway_container *con = config->handler_context.container; | 341 | struct sway_container *con = config->handler_context.container; |
337 | if (!con || con->fullscreen_mode) { | 342 | if (!con || con->pending.fullscreen_mode) { |
338 | return cmd_results_new(CMD_SUCCESS, NULL); | 343 | return cmd_results_new(CMD_SUCCESS, NULL); |
339 | } | 344 | } |
340 | struct sway_node *parent = node_get_parent(&con->node); | 345 | struct sway_node *parent = node_get_parent(&con->node); |
@@ -370,13 +375,24 @@ struct cmd_results *cmd_focus(int argc, char **argv) { | |||
370 | struct sway_seat *seat = config->handler_context.seat; | 375 | struct sway_seat *seat = config->handler_context.seat; |
371 | if (node->type < N_WORKSPACE) { | 376 | if (node->type < N_WORKSPACE) { |
372 | return cmd_results_new(CMD_FAILURE, | 377 | return cmd_results_new(CMD_FAILURE, |
373 | "Command 'focus' cannot be used above the workspace level"); | 378 | "Command 'focus' cannot be used above the workspace level."); |
374 | } | 379 | } |
375 | 380 | ||
376 | if (argc == 0 && container) { | 381 | if (argc == 0) { |
382 | if (!container) { | ||
383 | return cmd_results_new(CMD_FAILURE, "No container to focus was specified."); | ||
384 | } | ||
385 | |||
377 | if (container_is_scratchpad_hidden_or_child(container)) { | 386 | if (container_is_scratchpad_hidden_or_child(container)) { |
378 | root_scratchpad_show(container); | 387 | root_scratchpad_show(container); |
379 | } | 388 | } |
389 | // if we are switching to a container under a fullscreen window, we first | ||
390 | // need to exit fullscreen so that the newly focused container becomes visible | ||
391 | struct sway_container *obstructing = container_obstructing_fullscreen_container(container); | ||
392 | if (obstructing) { | ||
393 | container_fullscreen_disable(obstructing); | ||
394 | arrange_root(); | ||
395 | } | ||
380 | seat_set_focus_container(seat, container); | 396 | seat_set_focus_container(seat, container); |
381 | seat_consider_warp_to_focus(seat); | 397 | seat_consider_warp_to_focus(seat); |
382 | container_raise_floating(container); | 398 | container_raise_floating(container); |
@@ -439,7 +455,8 @@ struct cmd_results *cmd_focus(int argc, char **argv) { | |||
439 | return cmd_results_new(CMD_FAILURE, ""); | 455 | return cmd_results_new(CMD_FAILURE, ""); |
440 | } | 456 | } |
441 | struct sway_node *next_focus = NULL; | 457 | struct sway_node *next_focus = NULL; |
442 | if (container_is_floating(container)) { | 458 | if (container_is_floating(container) && |
459 | container->pending.fullscreen_mode == FULLSCREEN_NONE) { | ||
443 | next_focus = node_get_in_direction_floating(container, seat, direction); | 460 | next_focus = node_get_in_direction_floating(container, seat, direction); |
444 | } else { | 461 | } else { |
445 | next_focus = node_get_in_direction_tiling(container, seat, direction, descend); | 462 | next_focus = node_get_in_direction_tiling(container, seat, direction, descend); |
diff --git a/sway/commands/font.c b/sway/commands/font.c index c54365b5..74bb6b9f 100644 --- a/sway/commands/font.c +++ b/sway/commands/font.c | |||
@@ -4,6 +4,7 @@ | |||
4 | #include "sway/config.h" | 4 | #include "sway/config.h" |
5 | #include "log.h" | 5 | #include "log.h" |
6 | #include "stringop.h" | 6 | #include "stringop.h" |
7 | #include <pango/pangocairo.h> | ||
7 | 8 | ||
8 | struct cmd_results *cmd_font(int argc, char **argv) { | 9 | struct cmd_results *cmd_font(int argc, char **argv) { |
9 | struct cmd_results *error = NULL; | 10 | struct cmd_results *error = NULL; |
@@ -16,12 +17,34 @@ struct cmd_results *cmd_font(int argc, char **argv) { | |||
16 | if (strncmp(font, "pango:", 6) == 0) { | 17 | if (strncmp(font, "pango:", 6) == 0) { |
17 | config->pango_markup = true; | 18 | config->pango_markup = true; |
18 | config->font = strdup(font + 6); | 19 | config->font = strdup(font + 6); |
20 | free(font); | ||
19 | } else { | 21 | } else { |
20 | config->pango_markup = false; | 22 | config->pango_markup = false; |
21 | config->font = strdup(font); | 23 | config->font = font; |
22 | } | 24 | } |
23 | 25 | ||
24 | free(font); | 26 | // Parse the font early so we can reject it if it's not valid for pango. |
25 | config_update_font_height(true); | 27 | // Also avoids re-parsing each time we render text. |
28 | PangoFontDescription *font_description = pango_font_description_from_string(config->font); | ||
29 | |||
30 | const char *family = pango_font_description_get_family(font_description); | ||
31 | if (family == NULL) { | ||
32 | pango_font_description_free(font_description); | ||
33 | return cmd_results_new(CMD_FAILURE, "Invalid font family."); | ||
34 | } | ||
35 | |||
36 | const gint size = pango_font_description_get_size(font_description); | ||
37 | if (size == 0) { | ||
38 | pango_font_description_free(font_description); | ||
39 | return cmd_results_new(CMD_FAILURE, "Invalid font size."); | ||
40 | } | ||
41 | |||
42 | if (config->font_description != NULL) { | ||
43 | pango_font_description_free(config->font_description); | ||
44 | } | ||
45 | |||
46 | config->font_description = font_description; | ||
47 | config_update_font_height(); | ||
48 | |||
26 | return cmd_results_new(CMD_SUCCESS, NULL); | 49 | return cmd_results_new(CMD_SUCCESS, NULL); |
27 | } | 50 | } |
diff --git a/sway/commands/fullscreen.c b/sway/commands/fullscreen.c index 3392a7f7..21c1e9a0 100644 --- a/sway/commands/fullscreen.c +++ b/sway/commands/fullscreen.c | |||
@@ -18,30 +18,19 @@ struct cmd_results *cmd_fullscreen(int argc, char **argv) { | |||
18 | return cmd_results_new(CMD_FAILURE, | 18 | return cmd_results_new(CMD_FAILURE, |
19 | "Can't run this command while there's no outputs connected."); | 19 | "Can't run this command while there's no outputs connected."); |
20 | } | 20 | } |
21 | struct sway_node *node = config->handler_context.node; | ||
22 | struct sway_container *container = config->handler_context.container; | 21 | struct sway_container *container = config->handler_context.container; |
23 | struct sway_workspace *workspace = config->handler_context.workspace; | ||
24 | if (node->type == N_WORKSPACE && workspace->tiling->length == 0) { | ||
25 | return cmd_results_new(CMD_FAILURE, | ||
26 | "Can't fullscreen an empty workspace"); | ||
27 | } | ||
28 | 22 | ||
29 | // If in the scratchpad, operate on the highest container | 23 | if (!container) { |
30 | if (container && !container->workspace) { | 24 | // If the focus is not a container, do nothing successfully |
31 | while (container->parent) { | 25 | return cmd_results_new(CMD_SUCCESS, NULL); |
32 | container = container->parent; | 26 | } else if (!container->pending.workspace) { |
33 | } | 27 | // If in the scratchpad, operate on the highest container |
34 | } | 28 | while (container->pending.parent) { |
35 | 29 | container = container->pending.parent; | |
36 | bool is_fullscreen = false; | ||
37 | for (struct sway_container *curr = container; curr; curr = curr->parent) { | ||
38 | if (curr->fullscreen_mode != FULLSCREEN_NONE) { | ||
39 | container = curr; | ||
40 | is_fullscreen = true; | ||
41 | break; | ||
42 | } | 30 | } |
43 | } | 31 | } |
44 | 32 | ||
33 | bool is_fullscreen = container->pending.fullscreen_mode != FULLSCREEN_NONE; | ||
45 | bool global = false; | 34 | bool global = false; |
46 | bool enable = !is_fullscreen; | 35 | bool enable = !is_fullscreen; |
47 | 36 | ||
@@ -57,13 +46,6 @@ struct cmd_results *cmd_fullscreen(int argc, char **argv) { | |||
57 | global = strcasecmp(argv[1], "global") == 0; | 46 | global = strcasecmp(argv[1], "global") == 0; |
58 | } | 47 | } |
59 | 48 | ||
60 | if (enable && node->type == N_WORKSPACE) { | ||
61 | // Wrap the workspace's children in a container so we can fullscreen it | ||
62 | container = workspace_wrap_children(workspace); | ||
63 | workspace->layout = L_HORIZ; | ||
64 | seat_set_focus_container(config->handler_context.seat, container); | ||
65 | } | ||
66 | |||
67 | enum sway_fullscreen_mode mode = FULLSCREEN_NONE; | 49 | enum sway_fullscreen_mode mode = FULLSCREEN_NONE; |
68 | if (enable) { | 50 | if (enable) { |
69 | mode = global ? FULLSCREEN_GLOBAL : FULLSCREEN_WORKSPACE; | 51 | mode = global ? FULLSCREEN_GLOBAL : FULLSCREEN_WORKSPACE; |
diff --git a/sway/commands/gaps.c b/sway/commands/gaps.c index 021df843..1deeb56e 100644 --- a/sway/commands/gaps.c +++ b/sway/commands/gaps.c | |||
@@ -11,7 +11,8 @@ | |||
11 | enum gaps_op { | 11 | enum gaps_op { |
12 | GAPS_OP_SET, | 12 | GAPS_OP_SET, |
13 | GAPS_OP_ADD, | 13 | GAPS_OP_ADD, |
14 | GAPS_OP_SUBTRACT | 14 | GAPS_OP_SUBTRACT, |
15 | GAPS_OP_TOGGLE | ||
15 | }; | 16 | }; |
16 | 17 | ||
17 | struct gaps_data { | 18 | struct gaps_data { |
@@ -102,6 +103,9 @@ static void apply_gaps_op(int *prop, enum gaps_op op, int amount) { | |||
102 | case GAPS_OP_SUBTRACT: | 103 | case GAPS_OP_SUBTRACT: |
103 | *prop -= amount; | 104 | *prop -= amount; |
104 | break; | 105 | break; |
106 | case GAPS_OP_TOGGLE: | ||
107 | *prop = *prop ? 0 : amount; | ||
108 | break; | ||
105 | } | 109 | } |
106 | } | 110 | } |
107 | 111 | ||
@@ -133,9 +137,9 @@ static void configure_gaps(struct sway_workspace *ws, void *_data) { | |||
133 | } | 137 | } |
134 | 138 | ||
135 | // gaps inner|outer|horizontal|vertical|top|right|bottom|left current|all | 139 | // gaps inner|outer|horizontal|vertical|top|right|bottom|left current|all |
136 | // set|plus|minus <px> | 140 | // set|plus|minus|toggle <px> |
137 | static const char expected_runtime[] = "'gaps inner|outer|horizontal|vertical|" | 141 | static const char expected_runtime[] = "'gaps inner|outer|horizontal|vertical|" |
138 | "top|right|bottom|left current|all set|plus|minus <px>'"; | 142 | "top|right|bottom|left current|all set|plus|minus|toggle <px>'"; |
139 | static struct cmd_results *gaps_set_runtime(int argc, char **argv) { | 143 | static struct cmd_results *gaps_set_runtime(int argc, char **argv) { |
140 | struct cmd_results *error = checkarg(argc, "gaps", EXPECTED_EQUAL_TO, 4); | 144 | struct cmd_results *error = checkarg(argc, "gaps", EXPECTED_EQUAL_TO, 4); |
141 | if (error) { | 145 | if (error) { |
@@ -180,6 +184,8 @@ static struct cmd_results *gaps_set_runtime(int argc, char **argv) { | |||
180 | data.operation = GAPS_OP_ADD; | 184 | data.operation = GAPS_OP_ADD; |
181 | } else if (strcasecmp(argv[2], "minus") == 0) { | 185 | } else if (strcasecmp(argv[2], "minus") == 0) { |
182 | data.operation = GAPS_OP_SUBTRACT; | 186 | data.operation = GAPS_OP_SUBTRACT; |
187 | } else if (strcasecmp(argv[2], "toggle") == 0) { | ||
188 | data.operation = GAPS_OP_TOGGLE; | ||
183 | } else { | 189 | } else { |
184 | return cmd_results_new(CMD_INVALID, "Expected %s", expected_runtime); | 190 | return cmd_results_new(CMD_INVALID, "Expected %s", expected_runtime); |
185 | } | 191 | } |
@@ -200,7 +206,7 @@ static struct cmd_results *gaps_set_runtime(int argc, char **argv) { | |||
200 | } | 206 | } |
201 | 207 | ||
202 | // gaps inner|outer|<dir>|<side> <px> - sets defaults for workspaces | 208 | // gaps inner|outer|<dir>|<side> <px> - sets defaults for workspaces |
203 | // gaps inner|outer|<dir>|<side> current|all set|plus|minus <px> - runtime only | 209 | // gaps inner|outer|<dir>|<side> current|all set|plus|minus|toggle <px> - runtime only |
204 | // <dir> = horizontal|vertical | 210 | // <dir> = horizontal|vertical |
205 | // <side> = top|right|bottom|left | 211 | // <side> = top|right|bottom|left |
206 | struct cmd_results *cmd_gaps(int argc, char **argv) { | 212 | struct cmd_results *cmd_gaps(int argc, char **argv) { |
diff --git a/sway/commands/gesture.c b/sway/commands/gesture.c new file mode 100644 index 00000000..d4442cc3 --- /dev/null +++ b/sway/commands/gesture.c | |||
@@ -0,0 +1,166 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include "sway/config.h" | ||
3 | |||
4 | #include "gesture.h" | ||
5 | #include "log.h" | ||
6 | #include "stringop.h" | ||
7 | #include "sway/commands.h" | ||
8 | |||
9 | void free_gesture_binding(struct sway_gesture_binding *binding) { | ||
10 | if (!binding) { | ||
11 | return; | ||
12 | } | ||
13 | free(binding->input); | ||
14 | free(binding->command); | ||
15 | free(binding); | ||
16 | } | ||
17 | |||
18 | /** | ||
19 | * Returns true if the bindings have the same gesture type, direction, etc | ||
20 | */ | ||
21 | static bool binding_gesture_equal(struct sway_gesture_binding *binding_a, | ||
22 | struct sway_gesture_binding *binding_b) { | ||
23 | if (strcmp(binding_a->input, binding_b->input) != 0) { | ||
24 | return false; | ||
25 | } | ||
26 | |||
27 | if (!gesture_equal(&binding_a->gesture, &binding_b->gesture)) { | ||
28 | return false; | ||
29 | } | ||
30 | |||
31 | if ((binding_a->flags & BINDING_EXACT) != | ||
32 | (binding_b->flags & BINDING_EXACT)) { | ||
33 | return false; | ||
34 | } | ||
35 | return true; | ||
36 | } | ||
37 | |||
38 | /** | ||
39 | * Add gesture binding to config | ||
40 | */ | ||
41 | static struct cmd_results *gesture_binding_add( | ||
42 | struct sway_gesture_binding *binding, | ||
43 | const char *gesturecombo, bool warn) { | ||
44 | list_t *mode_bindings = config->current_mode->gesture_bindings; | ||
45 | // overwrite the binding if it already exists | ||
46 | bool overwritten = false; | ||
47 | for (int i = 0; i < mode_bindings->length; ++i) { | ||
48 | struct sway_gesture_binding *config_binding = mode_bindings->items[i]; | ||
49 | if (binding_gesture_equal(binding, config_binding)) { | ||
50 | sway_log(SWAY_INFO, "Overwriting binding '%s' to `%s` from `%s`", | ||
51 | gesturecombo, binding->command, config_binding->command); | ||
52 | if (warn) { | ||
53 | config_add_swaynag_warning("Overwriting binding" | ||
54 | "'%s' to `%s` from `%s`", | ||
55 | gesturecombo, binding->command, | ||
56 | config_binding->command); | ||
57 | } | ||
58 | free_gesture_binding(config_binding); | ||
59 | mode_bindings->items[i] = binding; | ||
60 | overwritten = true; | ||
61 | } | ||
62 | } | ||
63 | |||
64 | if (!overwritten) { | ||
65 | list_add(mode_bindings, binding); | ||
66 | sway_log(SWAY_DEBUG, "bindgesture - Bound %s to command `%s`", | ||
67 | gesturecombo, binding->command); | ||
68 | } | ||
69 | |||
70 | return cmd_results_new(CMD_SUCCESS, NULL); | ||
71 | } | ||
72 | |||
73 | /** | ||
74 | * Remove gesture binding from config | ||
75 | */ | ||
76 | static struct cmd_results *gesture_binding_remove( | ||
77 | struct sway_gesture_binding *binding, const char *gesturecombo) { | ||
78 | list_t *mode_bindings = config->current_mode->gesture_bindings; | ||
79 | for (int i = 0; i < mode_bindings->length; ++i) { | ||
80 | struct sway_gesture_binding *config_binding = mode_bindings->items[i]; | ||
81 | if (binding_gesture_equal(binding, config_binding)) { | ||
82 | free_gesture_binding(config_binding); | ||
83 | free_gesture_binding(binding); | ||
84 | list_del(mode_bindings, i); | ||
85 | sway_log(SWAY_DEBUG, "unbindgesture - Unbound %s gesture", | ||
86 | gesturecombo); | ||
87 | return cmd_results_new(CMD_SUCCESS, NULL); | ||
88 | } | ||
89 | } | ||
90 | |||
91 | free_gesture_binding(binding); | ||
92 | return cmd_results_new(CMD_FAILURE, "Could not find gesture binding `%s`", | ||
93 | gesturecombo); | ||
94 | } | ||
95 | |||
96 | /** | ||
97 | * Parse and execute bindgesture or unbindgesture command. | ||
98 | */ | ||
99 | static struct cmd_results *cmd_bind_or_unbind_gesture(int argc, char **argv, bool unbind) { | ||
100 | int minargs = 2; | ||
101 | char *bindtype = "bindgesture"; | ||
102 | if (unbind) { | ||
103 | minargs--; | ||
104 | bindtype = "unbindgesture"; | ||
105 | } | ||
106 | |||
107 | struct cmd_results *error = NULL; | ||
108 | if ((error = checkarg(argc, bindtype, EXPECTED_AT_LEAST, minargs))) { | ||
109 | return error; | ||
110 | } | ||
111 | struct sway_gesture_binding *binding = calloc(1, sizeof(struct sway_gesture_binding)); | ||
112 | if (!binding) { | ||
113 | return cmd_results_new(CMD_FAILURE, "Unable to allocate binding"); | ||
114 | } | ||
115 | binding->input = strdup("*"); | ||
116 | |||
117 | bool warn = true; | ||
118 | |||
119 | // Handle flags | ||
120 | while (argc > 0) { | ||
121 | if (strcmp("--exact", argv[0]) == 0) { | ||
122 | binding->flags |= BINDING_EXACT; | ||
123 | } else if (strcmp("--no-warn", argv[0]) == 0) { | ||
124 | warn = false; | ||
125 | } else if (strncmp("--input-device=", argv[0], | ||
126 | strlen("--input-device=")) == 0) { | ||
127 | free(binding->input); | ||
128 | binding->input = strdup(argv[0] + strlen("--input-device=")); | ||
129 | } else { | ||
130 | break; | ||
131 | } | ||
132 | argv++; | ||
133 | argc--; | ||
134 | } | ||
135 | |||
136 | if (argc < minargs) { | ||
137 | free(binding); | ||
138 | return cmd_results_new(CMD_FAILURE, | ||
139 | "Invalid %s command (expected at least %d " | ||
140 | "non-option arguments, got %d)", bindtype, minargs, argc); | ||
141 | } | ||
142 | |||
143 | char* errmsg = NULL; | ||
144 | if ((errmsg = gesture_parse(argv[0], &binding->gesture))) { | ||
145 | free(binding); | ||
146 | struct cmd_results *final = cmd_results_new(CMD_FAILURE, | ||
147 | "Invalid %s command (%s)", | ||
148 | bindtype, errmsg); | ||
149 | free(errmsg); | ||
150 | return final; | ||
151 | } | ||
152 | |||
153 | if (unbind) { | ||
154 | return gesture_binding_remove(binding, argv[0]); | ||
155 | } | ||
156 | binding->command = join_args(argv + 1, argc - 1); | ||
157 | return gesture_binding_add(binding, argv[0], warn); | ||
158 | } | ||
159 | |||
160 | struct cmd_results *cmd_bindgesture(int argc, char **argv) { | ||
161 | return cmd_bind_or_unbind_gesture(argc, argv, false); | ||
162 | } | ||
163 | |||
164 | struct cmd_results *cmd_unbindgesture(int argc, char **argv) { | ||
165 | return cmd_bind_or_unbind_gesture(argc, argv, true); | ||
166 | } | ||
diff --git a/sway/commands/input.c b/sway/commands/input.c index c9bb8e06..77acb671 100644 --- a/sway/commands/input.c +++ b/sway/commands/input.c | |||
@@ -7,7 +7,7 @@ | |||
7 | #include "stringop.h" | 7 | #include "stringop.h" |
8 | 8 | ||
9 | // must be in order for the bsearch | 9 | // must be in order for the bsearch |
10 | static struct cmd_handler input_handlers[] = { | 10 | static const struct cmd_handler input_handlers[] = { |
11 | { "accel_profile", input_cmd_accel_profile }, | 11 | { "accel_profile", input_cmd_accel_profile }, |
12 | { "calibration_matrix", input_cmd_calibration_matrix }, | 12 | { "calibration_matrix", input_cmd_calibration_matrix }, |
13 | { "click_method", input_cmd_click_method }, | 13 | { "click_method", input_cmd_click_method }, |
@@ -40,7 +40,7 @@ static struct cmd_handler input_handlers[] = { | |||
40 | }; | 40 | }; |
41 | 41 | ||
42 | // must be in order for the bsearch | 42 | // must be in order for the bsearch |
43 | static struct cmd_handler input_config_handlers[] = { | 43 | static const struct cmd_handler input_config_handlers[] = { |
44 | { "xkb_capslock", input_cmd_xkb_capslock }, | 44 | { "xkb_capslock", input_cmd_xkb_capslock }, |
45 | { "xkb_numlock", input_cmd_xkb_numlock }, | 45 | { "xkb_numlock", input_cmd_xkb_numlock }, |
46 | }; | 46 | }; |
diff --git a/sway/commands/input/map_to_region.c b/sway/commands/input/map_to_region.c index e85495e5..284b57d0 100644 --- a/sway/commands/input/map_to_region.c +++ b/sway/commands/input/map_to_region.c | |||
@@ -1,7 +1,6 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | 1 | #define _POSIX_C_SOURCE 200809L |
2 | #include <stdlib.h> | 2 | #include <stdlib.h> |
3 | #include <string.h> | 3 | #include <string.h> |
4 | #include <wlr/types/wlr_box.h> | ||
5 | #include "sway/commands.h" | 4 | #include "sway/commands.h" |
6 | #include "sway/config.h" | 5 | #include "sway/config.h" |
7 | 6 | ||
diff --git a/sway/commands/input/xkb_switch_layout.c b/sway/commands/input/xkb_switch_layout.c index d6548a68..3cce4ec8 100644 --- a/sway/commands/input/xkb_switch_layout.c +++ b/sway/commands/input/xkb_switch_layout.c | |||
@@ -1,10 +1,16 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | 1 | #define _POSIX_C_SOURCE 200809L |
2 | #include <assert.h> | 2 | #include <assert.h> |
3 | #include <wlr/interfaces/wlr_keyboard.h> | ||
3 | #include "sway/config.h" | 4 | #include "sway/config.h" |
4 | #include "sway/commands.h" | 5 | #include "sway/commands.h" |
5 | #include "sway/input/input-manager.h" | 6 | #include "sway/input/input-manager.h" |
6 | #include "log.h" | 7 | #include "log.h" |
7 | 8 | ||
9 | struct xkb_switch_layout_action { | ||
10 | struct wlr_keyboard *keyboard; | ||
11 | xkb_layout_index_t layout; | ||
12 | }; | ||
13 | |||
8 | static void switch_layout(struct wlr_keyboard *kbd, xkb_layout_index_t idx) { | 14 | static void switch_layout(struct wlr_keyboard *kbd, xkb_layout_index_t idx) { |
9 | xkb_layout_index_t num_layouts = xkb_keymap_num_layouts(kbd->keymap); | 15 | xkb_layout_index_t num_layouts = xkb_keymap_num_layouts(kbd->keymap); |
10 | if (idx >= num_layouts) { | 16 | if (idx >= num_layouts) { |
@@ -28,10 +34,10 @@ static xkb_layout_index_t get_current_layout_index(struct wlr_keyboard *kbd) { | |||
28 | return layout_idx; | 34 | return layout_idx; |
29 | } | 35 | } |
30 | 36 | ||
31 | static void switch_layout_relative(struct wlr_keyboard *kbd, int dir) { | 37 | static xkb_layout_index_t get_layout_relative(struct wlr_keyboard *kbd, int dir) { |
32 | xkb_layout_index_t num_layouts = xkb_keymap_num_layouts(kbd->keymap); | 38 | xkb_layout_index_t num_layouts = xkb_keymap_num_layouts(kbd->keymap); |
33 | xkb_layout_index_t idx = get_current_layout_index(kbd); | 39 | xkb_layout_index_t idx = get_current_layout_index(kbd); |
34 | switch_layout(kbd, (idx + num_layouts + dir) % num_layouts); | 40 | return (idx + num_layouts + dir) % num_layouts; |
35 | } | 41 | } |
36 | 42 | ||
37 | struct cmd_results *input_cmd_xkb_switch_layout(int argc, char **argv) { | 43 | struct cmd_results *input_cmd_xkb_switch_layout(int argc, char **argv) { |
@@ -66,6 +72,18 @@ struct cmd_results *input_cmd_xkb_switch_layout(int argc, char **argv) { | |||
66 | relative = 0; | 72 | relative = 0; |
67 | } | 73 | } |
68 | 74 | ||
75 | struct xkb_switch_layout_action *actions = calloc( | ||
76 | wl_list_length(&server.input->devices), | ||
77 | sizeof(struct xkb_switch_layout_action)); | ||
78 | size_t actions_len = 0; | ||
79 | |||
80 | if (!actions) { | ||
81 | return cmd_results_new(CMD_FAILURE, "Unable to allocate actions"); | ||
82 | } | ||
83 | |||
84 | /* Calculate new indexes first because switching a layout in one | ||
85 | keyboard may result in a change on other keyboards as well because | ||
86 | of keyboard groups. */ | ||
69 | struct sway_input_device *dev; | 87 | struct sway_input_device *dev; |
70 | wl_list_for_each(dev, &server.input->devices, link) { | 88 | wl_list_for_each(dev, &server.input->devices, link) { |
71 | if (strcmp(ic->identifier, "*") != 0 && | 89 | if (strcmp(ic->identifier, "*") != 0 && |
@@ -76,12 +94,22 @@ struct cmd_results *input_cmd_xkb_switch_layout(int argc, char **argv) { | |||
76 | if (dev->wlr_device->type != WLR_INPUT_DEVICE_KEYBOARD) { | 94 | if (dev->wlr_device->type != WLR_INPUT_DEVICE_KEYBOARD) { |
77 | continue; | 95 | continue; |
78 | } | 96 | } |
97 | |||
98 | struct xkb_switch_layout_action *action = | ||
99 | &actions[actions_len++]; | ||
100 | |||
101 | action->keyboard = wlr_keyboard_from_input_device(dev->wlr_device); | ||
79 | if (relative) { | 102 | if (relative) { |
80 | switch_layout_relative(dev->wlr_device->keyboard, relative); | 103 | action->layout = get_layout_relative(action->keyboard, relative); |
81 | } else { | 104 | } else { |
82 | switch_layout(dev->wlr_device->keyboard, layout); | 105 | action->layout = layout; |
83 | } | 106 | } |
84 | } | 107 | } |
85 | 108 | ||
109 | for (size_t i = 0; i < actions_len; i++) { | ||
110 | switch_layout(actions[i].keyboard, actions[i].layout); | ||
111 | } | ||
112 | free(actions); | ||
113 | |||
86 | return cmd_results_new(CMD_SUCCESS, NULL); | 114 | return cmd_results_new(CMD_SUCCESS, NULL); |
87 | } | 115 | } |
diff --git a/sway/commands/layout.c b/sway/commands/layout.c index f2af183b..2ba61b38 100644 --- a/sway/commands/layout.c +++ b/sway/commands/layout.c | |||
@@ -133,7 +133,7 @@ struct cmd_results *cmd_layout(int argc, char **argv) { | |||
133 | 133 | ||
134 | // Operate on parent container, like i3. | 134 | // Operate on parent container, like i3. |
135 | if (container) { | 135 | if (container) { |
136 | container = container->parent; | 136 | container = container->pending.parent; |
137 | } | 137 | } |
138 | 138 | ||
139 | // We could be working with a container OR a workspace. These are different | 139 | // We could be working with a container OR a workspace. These are different |
@@ -142,10 +142,10 @@ struct cmd_results *cmd_layout(int argc, char **argv) { | |||
142 | enum sway_container_layout new_layout = L_NONE; | 142 | enum sway_container_layout new_layout = L_NONE; |
143 | enum sway_container_layout old_layout = L_NONE; | 143 | enum sway_container_layout old_layout = L_NONE; |
144 | if (container) { | 144 | if (container) { |
145 | old_layout = container->layout; | 145 | old_layout = container->pending.layout; |
146 | new_layout = get_layout(argc, argv, | 146 | new_layout = get_layout(argc, argv, |
147 | container->layout, container->prev_split_layout, | 147 | container->pending.layout, container->prev_split_layout, |
148 | container->workspace->output); | 148 | container->pending.workspace->output); |
149 | } else { | 149 | } else { |
150 | old_layout = workspace->layout; | 150 | old_layout = workspace->layout; |
151 | new_layout = get_layout(argc, argv, | 151 | new_layout = get_layout(argc, argv, |
@@ -160,13 +160,13 @@ struct cmd_results *cmd_layout(int argc, char **argv) { | |||
160 | if (old_layout != L_TABBED && old_layout != L_STACKED) { | 160 | if (old_layout != L_TABBED && old_layout != L_STACKED) { |
161 | container->prev_split_layout = old_layout; | 161 | container->prev_split_layout = old_layout; |
162 | } | 162 | } |
163 | container->layout = new_layout; | 163 | container->pending.layout = new_layout; |
164 | container_update_representation(container); | 164 | container_update_representation(container); |
165 | } else if (config->handler_context.container) { | 165 | } else if (config->handler_context.container) { |
166 | // i3 avoids changing workspace layouts with a new container | 166 | // i3 avoids changing workspace layouts with a new container |
167 | // https://github.com/i3/i3/blob/3cd1c45eba6de073bc4300eebb4e1cc1a0c4479a/src/con.c#L1817 | 167 | // https://github.com/i3/i3/blob/3cd1c45eba6de073bc4300eebb4e1cc1a0c4479a/src/con.c#L1817 |
168 | container = workspace_wrap_children(workspace); | 168 | container = workspace_wrap_children(workspace); |
169 | container->layout = new_layout; | 169 | container->pending.layout = new_layout; |
170 | container_update_representation(container); | 170 | container_update_representation(container); |
171 | } else { | 171 | } else { |
172 | if (old_layout != L_TABBED && old_layout != L_STACKED) { | 172 | if (old_layout != L_TABBED && old_layout != L_STACKED) { |
diff --git a/sway/commands/mode.c b/sway/commands/mode.c index a5871dab..7263efcb 100644 --- a/sway/commands/mode.c +++ b/sway/commands/mode.c | |||
@@ -9,12 +9,14 @@ | |||
9 | #include "stringop.h" | 9 | #include "stringop.h" |
10 | 10 | ||
11 | // Must be in order for the bsearch | 11 | // Must be in order for the bsearch |
12 | static struct cmd_handler mode_handlers[] = { | 12 | static const struct cmd_handler mode_handlers[] = { |
13 | { "bindcode", cmd_bindcode }, | 13 | { "bindcode", cmd_bindcode }, |
14 | { "bindgesture", cmd_bindgesture }, | ||
14 | { "bindswitch", cmd_bindswitch }, | 15 | { "bindswitch", cmd_bindswitch }, |
15 | { "bindsym", cmd_bindsym }, | 16 | { "bindsym", cmd_bindsym }, |
16 | { "set", cmd_set }, | 17 | { "set", cmd_set }, |
17 | { "unbindcode", cmd_unbindcode }, | 18 | { "unbindcode", cmd_unbindcode }, |
19 | { "unbindgesture", cmd_unbindgesture }, | ||
18 | { "unbindswitch", cmd_unbindswitch }, | 20 | { "unbindswitch", cmd_unbindswitch }, |
19 | { "unbindsym", cmd_unbindsym }, | 21 | { "unbindsym", cmd_unbindsym }, |
20 | }; | 22 | }; |
@@ -59,6 +61,7 @@ struct cmd_results *cmd_mode(int argc, char **argv) { | |||
59 | mode->keycode_bindings = create_list(); | 61 | mode->keycode_bindings = create_list(); |
60 | mode->mouse_bindings = create_list(); | 62 | mode->mouse_bindings = create_list(); |
61 | mode->switch_bindings = create_list(); | 63 | mode->switch_bindings = create_list(); |
64 | mode->gesture_bindings = create_list(); | ||
62 | mode->pango = pango; | 65 | mode->pango = pango; |
63 | list_add(config->modes, mode); | 66 | list_add(config->modes, mode); |
64 | } | 67 | } |
diff --git a/sway/commands/move.c b/sway/commands/move.c index f8f89f18..0d0d9727 100644 --- a/sway/commands/move.c +++ b/sway/commands/move.c | |||
@@ -113,8 +113,8 @@ static void container_move_to_container_from_direction( | |||
113 | struct sway_container *container, struct sway_container *destination, | 113 | struct sway_container *container, struct sway_container *destination, |
114 | enum wlr_direction move_dir) { | 114 | enum wlr_direction move_dir) { |
115 | if (destination->view) { | 115 | if (destination->view) { |
116 | if (destination->parent == container->parent && | 116 | if (destination->pending.parent == container->pending.parent && |
117 | destination->workspace == container->workspace) { | 117 | destination->pending.workspace == container->pending.workspace) { |
118 | sway_log(SWAY_DEBUG, "Swapping siblings"); | 118 | sway_log(SWAY_DEBUG, "Swapping siblings"); |
119 | list_t *siblings = container_get_siblings(container); | 119 | list_t *siblings = container_get_siblings(container); |
120 | int container_index = list_find(siblings, container); | 120 | int container_index = list_find(siblings, container); |
@@ -126,28 +126,28 @@ static void container_move_to_container_from_direction( | |||
126 | int offset = | 126 | int offset = |
127 | move_dir == WLR_DIRECTION_LEFT || move_dir == WLR_DIRECTION_UP; | 127 | move_dir == WLR_DIRECTION_LEFT || move_dir == WLR_DIRECTION_UP; |
128 | int index = container_sibling_index(destination) + offset; | 128 | int index = container_sibling_index(destination) + offset; |
129 | if (destination->parent) { | 129 | if (destination->pending.parent) { |
130 | container_insert_child(destination->parent, container, index); | 130 | container_insert_child(destination->pending.parent, container, index); |
131 | } else { | 131 | } else { |
132 | workspace_insert_tiling(destination->workspace, | 132 | workspace_insert_tiling(destination->pending.workspace, |
133 | container, index); | 133 | container, index); |
134 | } | 134 | } |
135 | container->width = container->height = 0; | 135 | container->pending.width = container->pending.height = 0; |
136 | container->width_fraction = container->height_fraction = 0; | 136 | container->width_fraction = container->height_fraction = 0; |
137 | workspace_squash(destination->workspace); | 137 | workspace_squash(destination->pending.workspace); |
138 | } | 138 | } |
139 | return; | 139 | return; |
140 | } | 140 | } |
141 | 141 | ||
142 | if (is_parallel(destination->layout, move_dir)) { | 142 | if (is_parallel(destination->pending.layout, move_dir)) { |
143 | sway_log(SWAY_DEBUG, "Reparenting container (parallel)"); | 143 | sway_log(SWAY_DEBUG, "Reparenting container (parallel)"); |
144 | int index = | 144 | int index = |
145 | move_dir == WLR_DIRECTION_RIGHT || move_dir == WLR_DIRECTION_DOWN ? | 145 | move_dir == WLR_DIRECTION_RIGHT || move_dir == WLR_DIRECTION_DOWN ? |
146 | 0 : destination->children->length; | 146 | 0 : destination->pending.children->length; |
147 | container_insert_child(destination, container, index); | 147 | container_insert_child(destination, container, index); |
148 | container->width = container->height = 0; | 148 | container->pending.width = container->pending.height = 0; |
149 | container->width_fraction = container->height_fraction = 0; | 149 | container->width_fraction = container->height_fraction = 0; |
150 | workspace_squash(destination->workspace); | 150 | workspace_squash(destination->pending.workspace); |
151 | return; | 151 | return; |
152 | } | 152 | } |
153 | 153 | ||
@@ -168,7 +168,7 @@ static void container_move_to_container_from_direction( | |||
168 | static void container_move_to_workspace_from_direction( | 168 | static void container_move_to_workspace_from_direction( |
169 | struct sway_container *container, struct sway_workspace *workspace, | 169 | struct sway_container *container, struct sway_workspace *workspace, |
170 | enum wlr_direction move_dir) { | 170 | enum wlr_direction move_dir) { |
171 | container->width = container->height = 0; | 171 | container->pending.width = container->pending.height = 0; |
172 | container->width_fraction = container->height_fraction = 0; | 172 | container->width_fraction = container->height_fraction = 0; |
173 | 173 | ||
174 | if (is_parallel(workspace->layout, move_dir)) { | 174 | if (is_parallel(workspace->layout, move_dir)) { |
@@ -188,8 +188,8 @@ static void container_move_to_workspace_from_direction( | |||
188 | workspace_add_tiling(workspace, container); | 188 | workspace_add_tiling(workspace, container); |
189 | return; | 189 | return; |
190 | } | 190 | } |
191 | while (focus_inactive->parent) { | 191 | while (focus_inactive->pending.parent) { |
192 | focus_inactive = focus_inactive->parent; | 192 | focus_inactive = focus_inactive->pending.parent; |
193 | } | 193 | } |
194 | container_move_to_container_from_direction(container, focus_inactive, | 194 | container_move_to_container_from_direction(container, focus_inactive, |
195 | move_dir); | 195 | move_dir); |
@@ -197,25 +197,25 @@ static void container_move_to_workspace_from_direction( | |||
197 | 197 | ||
198 | static void container_move_to_workspace(struct sway_container *container, | 198 | static void container_move_to_workspace(struct sway_container *container, |
199 | struct sway_workspace *workspace) { | 199 | struct sway_workspace *workspace) { |
200 | if (container->workspace == workspace) { | 200 | if (container->pending.workspace == workspace) { |
201 | return; | 201 | return; |
202 | } | 202 | } |
203 | struct sway_workspace *old_workspace = container->workspace; | 203 | struct sway_workspace *old_workspace = container->pending.workspace; |
204 | if (container_is_floating(container)) { | 204 | if (container_is_floating(container)) { |
205 | struct sway_output *old_output = container->workspace->output; | 205 | struct sway_output *old_output = container->pending.workspace->output; |
206 | container_detach(container); | 206 | container_detach(container); |
207 | workspace_add_floating(workspace, container); | 207 | workspace_add_floating(workspace, container); |
208 | container_handle_fullscreen_reparent(container); | 208 | container_handle_fullscreen_reparent(container); |
209 | // If changing output, center it within the workspace | 209 | // If changing output, center it within the workspace |
210 | if (old_output != workspace->output && !container->fullscreen_mode) { | 210 | if (old_output != workspace->output && !container->pending.fullscreen_mode) { |
211 | container_floating_move_to_center(container); | 211 | container_floating_move_to_center(container); |
212 | } | 212 | } |
213 | } else { | 213 | } else { |
214 | container_detach(container); | 214 | container_detach(container); |
215 | if (workspace_is_empty(workspace) && container->children) { | 215 | if (workspace_is_empty(workspace) && container->pending.children) { |
216 | workspace_unwrap_children(workspace, container); | 216 | workspace_unwrap_children(workspace, container); |
217 | } else { | 217 | } else { |
218 | container->width = container->height = 0; | 218 | container->pending.width = container->pending.height = 0; |
219 | container->width_fraction = container->height_fraction = 0; | 219 | container->width_fraction = container->height_fraction = 0; |
220 | workspace_add_tiling(workspace, container); | 220 | workspace_add_tiling(workspace, container); |
221 | } | 221 | } |
@@ -237,13 +237,13 @@ static void container_move_to_container(struct sway_container *container, | |||
237 | return; | 237 | return; |
238 | } | 238 | } |
239 | if (container_is_floating(container)) { | 239 | if (container_is_floating(container)) { |
240 | container_move_to_workspace(container, destination->workspace); | 240 | container_move_to_workspace(container, destination->pending.workspace); |
241 | return; | 241 | return; |
242 | } | 242 | } |
243 | struct sway_workspace *old_workspace = container->workspace; | 243 | struct sway_workspace *old_workspace = container->pending.workspace; |
244 | 244 | ||
245 | container_detach(container); | 245 | container_detach(container); |
246 | container->width = container->height = 0; | 246 | container->pending.width = container->pending.height = 0; |
247 | container->width_fraction = container->height_fraction = 0; | 247 | container->width_fraction = container->height_fraction = 0; |
248 | 248 | ||
249 | if (destination->view) { | 249 | if (destination->view) { |
@@ -256,12 +256,12 @@ static void container_move_to_container(struct sway_container *container, | |||
256 | ipc_event_window(container, "move"); | 256 | ipc_event_window(container, "move"); |
257 | } | 257 | } |
258 | 258 | ||
259 | if (destination->workspace) { | 259 | if (destination->pending.workspace) { |
260 | workspace_focus_fullscreen(destination->workspace); | 260 | workspace_focus_fullscreen(destination->pending.workspace); |
261 | workspace_detect_urgent(destination->workspace); | 261 | workspace_detect_urgent(destination->pending.workspace); |
262 | } | 262 | } |
263 | 263 | ||
264 | if (old_workspace && old_workspace != destination->workspace) { | 264 | if (old_workspace && old_workspace != destination->pending.workspace) { |
265 | workspace_detect_urgent(old_workspace); | 265 | workspace_detect_urgent(old_workspace); |
266 | } | 266 | } |
267 | } | 267 | } |
@@ -275,7 +275,7 @@ static bool container_move_to_next_output(struct sway_container *container, | |||
275 | if (!sway_assert(ws, "Expected output to have a workspace")) { | 275 | if (!sway_assert(ws, "Expected output to have a workspace")) { |
276 | return false; | 276 | return false; |
277 | } | 277 | } |
278 | switch (container->fullscreen_mode) { | 278 | switch (container->pending.fullscreen_mode) { |
279 | case FULLSCREEN_NONE: | 279 | case FULLSCREEN_NONE: |
280 | container_move_to_workspace_from_direction(container, ws, move_dir); | 280 | container_move_to_workspace_from_direction(container, ws, move_dir); |
281 | return true; | 281 | return true; |
@@ -293,12 +293,12 @@ static bool container_move_to_next_output(struct sway_container *container, | |||
293 | static bool container_move_in_direction(struct sway_container *container, | 293 | static bool container_move_in_direction(struct sway_container *container, |
294 | enum wlr_direction move_dir) { | 294 | enum wlr_direction move_dir) { |
295 | // If moving a fullscreen view, only consider outputs | 295 | // If moving a fullscreen view, only consider outputs |
296 | switch (container->fullscreen_mode) { | 296 | switch (container->pending.fullscreen_mode) { |
297 | case FULLSCREEN_NONE: | 297 | case FULLSCREEN_NONE: |
298 | break; | 298 | break; |
299 | case FULLSCREEN_WORKSPACE: | 299 | case FULLSCREEN_WORKSPACE: |
300 | return container_move_to_next_output(container, | 300 | return container_move_to_next_output(container, |
301 | container->workspace->output, move_dir); | 301 | container->pending.workspace->output, move_dir); |
302 | case FULLSCREEN_GLOBAL: | 302 | case FULLSCREEN_GLOBAL: |
303 | return false; | 303 | return false; |
304 | } | 304 | } |
@@ -317,26 +317,26 @@ static bool container_move_in_direction(struct sway_container *container, | |||
317 | while (!ancestor) { | 317 | while (!ancestor) { |
318 | // Don't allow containers to move out of their | 318 | // Don't allow containers to move out of their |
319 | // fullscreen or floating parent | 319 | // fullscreen or floating parent |
320 | if (current->fullscreen_mode || container_is_floating(current)) { | 320 | if (current->pending.fullscreen_mode || container_is_floating(current)) { |
321 | return false; | 321 | return false; |
322 | } | 322 | } |
323 | 323 | ||
324 | enum sway_container_layout parent_layout = container_parent_layout(current); | 324 | enum sway_container_layout parent_layout = container_parent_layout(current); |
325 | if (!is_parallel(parent_layout, move_dir)) { | 325 | if (!is_parallel(parent_layout, move_dir)) { |
326 | if (!current->parent) { | 326 | if (!current->pending.parent) { |
327 | // No parallel parent, so we reorient the workspace | 327 | // No parallel parent, so we reorient the workspace |
328 | current = workspace_wrap_children(current->workspace); | 328 | current = workspace_wrap_children(current->pending.workspace); |
329 | current->workspace->layout = | 329 | current->pending.workspace->layout = |
330 | move_dir == WLR_DIRECTION_LEFT || | 330 | move_dir == WLR_DIRECTION_LEFT || |
331 | move_dir == WLR_DIRECTION_RIGHT ? | 331 | move_dir == WLR_DIRECTION_RIGHT ? |
332 | L_HORIZ : L_VERT; | 332 | L_HORIZ : L_VERT; |
333 | container->height = container->width = 0; | 333 | container->pending.height = container->pending.width = 0; |
334 | container->height_fraction = container->width_fraction = 0; | 334 | container->height_fraction = container->width_fraction = 0; |
335 | workspace_update_representation(current->workspace); | 335 | workspace_update_representation(current->pending.workspace); |
336 | wrapped = true; | 336 | wrapped = true; |
337 | } else { | 337 | } else { |
338 | // Keep looking for a parallel parent | 338 | // Keep looking for a parallel parent |
339 | current = current->parent; | 339 | current = current->pending.parent; |
340 | } | 340 | } |
341 | continue; | 341 | continue; |
342 | } | 342 | } |
@@ -356,14 +356,14 @@ static bool container_move_in_direction(struct sway_container *container, | |||
356 | container_move_to_container_from_direction(container, | 356 | container_move_to_container_from_direction(container, |
357 | target, move_dir); | 357 | target, move_dir); |
358 | return true; | 358 | return true; |
359 | } else if (!container->parent) { | 359 | } else if (!container->pending.parent) { |
360 | // Container is at workspace level so we move it to the | 360 | // Container is at workspace level so we move it to the |
361 | // next workspace if possible | 361 | // next workspace if possible |
362 | return container_move_to_next_output(container, | 362 | return container_move_to_next_output(container, |
363 | current->workspace->output, move_dir); | 363 | current->pending.workspace->output, move_dir); |
364 | } else { | 364 | } else { |
365 | // Container has escaped its immediate parallel parent | 365 | // Container has escaped its immediate parallel parent |
366 | current = current->parent; | 366 | current = current->pending.parent; |
367 | continue; | 367 | continue; |
368 | } | 368 | } |
369 | } | 369 | } |
@@ -377,31 +377,31 @@ static bool container_move_in_direction(struct sway_container *container, | |||
377 | container_move_to_container_from_direction(container, | 377 | container_move_to_container_from_direction(container, |
378 | target, move_dir); | 378 | target, move_dir); |
379 | return true; | 379 | return true; |
380 | } else if (!wrapped && !container->parent->parent && | 380 | } else if (!wrapped && !container->pending.parent->pending.parent && |
381 | container->parent->children->length == 1) { | 381 | container->pending.parent->pending.children->length == 1) { |
382 | // Treat singleton children as if they are at workspace level like i3 | 382 | // Treat singleton children as if they are at workspace level like i3 |
383 | // https://github.com/i3/i3/blob/1d9160f2d247dbaa83fb62f02fd7041dec767fc2/src/move.c#L367 | 383 | // https://github.com/i3/i3/blob/1d9160f2d247dbaa83fb62f02fd7041dec767fc2/src/move.c#L367 |
384 | return container_move_to_next_output(container, | 384 | return container_move_to_next_output(container, |
385 | ancestor->workspace->output, move_dir); | 385 | ancestor->pending.workspace->output, move_dir); |
386 | } else { | 386 | } else { |
387 | // Container will be promoted | 387 | // Container will be promoted |
388 | struct sway_container *old_parent = container->parent; | 388 | struct sway_container *old_parent = container->pending.parent; |
389 | if (ancestor->parent) { | 389 | if (ancestor->pending.parent) { |
390 | // Container will move in with its parent | 390 | // Container will move in with its parent |
391 | container_insert_child(ancestor->parent, container, | 391 | container_insert_child(ancestor->pending.parent, container, |
392 | index + (offs < 0 ? 0 : 1)); | 392 | index + (offs < 0 ? 0 : 1)); |
393 | } else { | 393 | } else { |
394 | // Container will move to workspace level, | 394 | // Container will move to workspace level, |
395 | // may be re-split by workspace_layout | 395 | // may be re-split by workspace_layout |
396 | workspace_insert_tiling(ancestor->workspace, container, | 396 | workspace_insert_tiling(ancestor->pending.workspace, container, |
397 | index + (offs < 0 ? 0 : 1)); | 397 | index + (offs < 0 ? 0 : 1)); |
398 | } | 398 | } |
399 | ancestor->height = ancestor->width = 0; | 399 | ancestor->pending.height = ancestor->pending.width = 0; |
400 | ancestor->height_fraction = ancestor->width_fraction = 0; | 400 | ancestor->height_fraction = ancestor->width_fraction = 0; |
401 | if (old_parent) { | 401 | if (old_parent) { |
402 | container_reap_empty(old_parent); | 402 | container_reap_empty(old_parent); |
403 | } | 403 | } |
404 | workspace_squash(container->workspace); | 404 | workspace_squash(container->pending.workspace); |
405 | return true; | 405 | return true; |
406 | } | 406 | } |
407 | } | 407 | } |
@@ -427,14 +427,14 @@ static struct cmd_results *cmd_move_container(bool no_auto_back_and_forth, | |||
427 | container = workspace_wrap_children(workspace); | 427 | container = workspace_wrap_children(workspace); |
428 | } | 428 | } |
429 | 429 | ||
430 | if (container->fullscreen_mode == FULLSCREEN_GLOBAL) { | 430 | if (container->pending.fullscreen_mode == FULLSCREEN_GLOBAL) { |
431 | return cmd_results_new(CMD_FAILURE, | 431 | return cmd_results_new(CMD_FAILURE, |
432 | "Can't move fullscreen global container"); | 432 | "Can't move fullscreen global container"); |
433 | } | 433 | } |
434 | 434 | ||
435 | struct sway_seat *seat = config->handler_context.seat; | 435 | struct sway_seat *seat = config->handler_context.seat; |
436 | struct sway_container *old_parent = container->parent; | 436 | struct sway_container *old_parent = container->pending.parent; |
437 | struct sway_workspace *old_ws = container->workspace; | 437 | struct sway_workspace *old_ws = container->pending.workspace; |
438 | struct sway_output *old_output = old_ws ? old_ws->output : NULL; | 438 | struct sway_output *old_output = old_ws ? old_ws->output : NULL; |
439 | struct sway_node *destination = NULL; | 439 | struct sway_node *destination = NULL; |
440 | 440 | ||
@@ -508,7 +508,7 @@ static struct cmd_results *cmd_move_container(bool no_auto_back_and_forth, | |||
508 | destination = dst ? &dst->node : &ws->node; | 508 | destination = dst ? &dst->node : &ws->node; |
509 | } else if (strcasecmp(argv[0], "output") == 0) { | 509 | } else if (strcasecmp(argv[0], "output") == 0) { |
510 | struct sway_output *new_output = output_in_direction(argv[1], | 510 | struct sway_output *new_output = output_in_direction(argv[1], |
511 | old_output, container->x, container->y); | 511 | old_output, container->pending.x, container->pending.y); |
512 | if (!new_output) { | 512 | if (!new_output) { |
513 | return cmd_results_new(CMD_FAILURE, | 513 | return cmd_results_new(CMD_FAILURE, |
514 | "Can't find output with name/direction '%s'", argv[1]); | 514 | "Can't find output with name/direction '%s'", argv[1]); |
@@ -706,12 +706,12 @@ static struct cmd_results *cmd_move_in_direction( | |||
706 | "Cannot move workspaces in a direction"); | 706 | "Cannot move workspaces in a direction"); |
707 | } | 707 | } |
708 | if (container_is_floating(container)) { | 708 | if (container_is_floating(container)) { |
709 | if (container->fullscreen_mode) { | 709 | if (container->pending.fullscreen_mode) { |
710 | return cmd_results_new(CMD_FAILURE, | 710 | return cmd_results_new(CMD_FAILURE, |
711 | "Cannot move fullscreen floating container"); | 711 | "Cannot move fullscreen floating container"); |
712 | } | 712 | } |
713 | double lx = container->x; | 713 | double lx = container->pending.x; |
714 | double ly = container->y; | 714 | double ly = container->pending.y; |
715 | switch (direction) { | 715 | switch (direction) { |
716 | case WLR_DIRECTION_LEFT: | 716 | case WLR_DIRECTION_LEFT: |
717 | lx -= move_amt; | 717 | lx -= move_amt; |
@@ -729,8 +729,8 @@ static struct cmd_results *cmd_move_in_direction( | |||
729 | container_floating_move_to(container, lx, ly); | 729 | container_floating_move_to(container, lx, ly); |
730 | return cmd_results_new(CMD_SUCCESS, NULL); | 730 | return cmd_results_new(CMD_SUCCESS, NULL); |
731 | } | 731 | } |
732 | struct sway_workspace *old_ws = container->workspace; | 732 | struct sway_workspace *old_ws = container->pending.workspace; |
733 | struct sway_container *old_parent = container->parent; | 733 | struct sway_container *old_parent = container->pending.parent; |
734 | 734 | ||
735 | if (!container_move_in_direction(container, direction)) { | 735 | if (!container_move_in_direction(container, direction)) { |
736 | // Container didn't move | 736 | // Container didn't move |
@@ -744,7 +744,7 @@ static struct cmd_results *cmd_move_in_direction( | |||
744 | workspace_consider_destroy(old_ws); | 744 | workspace_consider_destroy(old_ws); |
745 | } | 745 | } |
746 | 746 | ||
747 | struct sway_workspace *new_ws = container->workspace; | 747 | struct sway_workspace *new_ws = container->pending.workspace; |
748 | 748 | ||
749 | if (root->fullscreen_global) { | 749 | if (root->fullscreen_global) { |
750 | arrange_root(); | 750 | arrange_root(); |
@@ -781,22 +781,22 @@ static struct cmd_results *cmd_move_to_position_pointer( | |||
781 | } | 781 | } |
782 | struct wlr_cursor *cursor = seat->cursor->cursor; | 782 | struct wlr_cursor *cursor = seat->cursor->cursor; |
783 | /* Determine where to put the window. */ | 783 | /* Determine where to put the window. */ |
784 | double lx = cursor->x - container->width / 2; | 784 | double lx = cursor->x - container->pending.width / 2; |
785 | double ly = cursor->y - container->height / 2; | 785 | double ly = cursor->y - container->pending.height / 2; |
786 | 786 | ||
787 | /* Correct target coordinates to be in bounds (on screen). */ | 787 | /* Correct target coordinates to be in bounds (on screen). */ |
788 | struct wlr_output *output = wlr_output_layout_output_at( | 788 | struct wlr_output *output = wlr_output_layout_output_at( |
789 | root->output_layout, cursor->x, cursor->y); | 789 | root->output_layout, cursor->x, cursor->y); |
790 | if (output) { | 790 | if (output) { |
791 | struct wlr_box *box = | 791 | struct wlr_box box; |
792 | wlr_output_layout_get_box(root->output_layout, output); | 792 | wlr_output_layout_get_box(root->output_layout, output, &box); |
793 | lx = fmax(lx, box->x); | 793 | lx = fmax(lx, box.x); |
794 | ly = fmax(ly, box->y); | 794 | ly = fmax(ly, box.y); |
795 | if (lx + container->width > box->x + box->width) { | 795 | if (lx + container->pending.width > box.x + box.width) { |
796 | lx = box->x + box->width - container->width; | 796 | lx = box.x + box.width - container->pending.width; |
797 | } | 797 | } |
798 | if (ly + container->height > box->y + box->height) { | 798 | if (ly + container->pending.height > box.y + box.height) { |
799 | ly = box->y + box->height - container->height; | 799 | ly = box.y + box.height - container->pending.height; |
800 | } | 800 | } |
801 | } | 801 | } |
802 | 802 | ||
@@ -846,16 +846,16 @@ static struct cmd_results *cmd_move_to_position(int argc, char **argv) { | |||
846 | } else if (strcmp(argv[0], "center") == 0) { | 846 | } else if (strcmp(argv[0], "center") == 0) { |
847 | double lx, ly; | 847 | double lx, ly; |
848 | if (absolute) { | 848 | if (absolute) { |
849 | lx = root->x + (root->width - container->width) / 2; | 849 | lx = root->x + (root->width - container->pending.width) / 2; |
850 | ly = root->y + (root->height - container->height) / 2; | 850 | ly = root->y + (root->height - container->pending.height) / 2; |
851 | } else { | 851 | } else { |
852 | struct sway_workspace *ws = container->workspace; | 852 | struct sway_workspace *ws = container->pending.workspace; |
853 | if (!ws) { | 853 | if (!ws) { |
854 | struct sway_seat *seat = config->handler_context.seat; | 854 | struct sway_seat *seat = config->handler_context.seat; |
855 | ws = seat_get_focused_workspace(seat); | 855 | ws = seat_get_focused_workspace(seat); |
856 | } | 856 | } |
857 | lx = ws->x + (ws->width - container->width) / 2; | 857 | lx = ws->x + (ws->width - container->pending.width) / 2; |
858 | ly = ws->y + (ws->height - container->height) / 2; | 858 | ly = ws->y + (ws->height - container->pending.height) / 2; |
859 | } | 859 | } |
860 | container_floating_move_to(container, lx, ly); | 860 | container_floating_move_to(container, lx, ly); |
861 | return cmd_results_new(CMD_SUCCESS, NULL); | 861 | return cmd_results_new(CMD_SUCCESS, NULL); |
@@ -874,6 +874,10 @@ static struct cmd_results *cmd_move_to_position(int argc, char **argv) { | |||
874 | return cmd_results_new(CMD_INVALID, "Invalid x position specified"); | 874 | return cmd_results_new(CMD_INVALID, "Invalid x position specified"); |
875 | } | 875 | } |
876 | 876 | ||
877 | if (argc < 1) { | ||
878 | return cmd_results_new(CMD_FAILURE, expected_position_syntax); | ||
879 | } | ||
880 | |||
877 | struct movement_amount ly = { .amount = 0, .unit = MOVEMENT_UNIT_INVALID }; | 881 | struct movement_amount ly = { .amount = 0, .unit = MOVEMENT_UNIT_INVALID }; |
878 | // Y direction | 882 | // Y direction |
879 | num_consumed_args = parse_movement_amount(argc, argv, &ly); | 883 | num_consumed_args = parse_movement_amount(argc, argv, &ly); |
@@ -886,7 +890,7 @@ static struct cmd_results *cmd_move_to_position(int argc, char **argv) { | |||
886 | return cmd_results_new(CMD_INVALID, "Invalid y position specified"); | 890 | return cmd_results_new(CMD_INVALID, "Invalid y position specified"); |
887 | } | 891 | } |
888 | 892 | ||
889 | struct sway_workspace *ws = container->workspace; | 893 | struct sway_workspace *ws = container->pending.workspace; |
890 | if (!ws) { | 894 | if (!ws) { |
891 | struct sway_seat *seat = config->handler_context.seat; | 895 | struct sway_seat *seat = config->handler_context.seat; |
892 | ws = seat_get_focused_workspace(seat); | 896 | ws = seat_get_focused_workspace(seat); |
@@ -960,14 +964,14 @@ static struct cmd_results *cmd_move_to_scratchpad(void) { | |||
960 | // If the container is in a floating split container, | 964 | // If the container is in a floating split container, |
961 | // operate on the split container instead of the child. | 965 | // operate on the split container instead of the child. |
962 | if (container_is_floating_or_child(con)) { | 966 | if (container_is_floating_or_child(con)) { |
963 | while (con->parent) { | 967 | while (con->pending.parent) { |
964 | con = con->parent; | 968 | con = con->pending.parent; |
965 | } | 969 | } |
966 | } | 970 | } |
967 | 971 | ||
968 | if (!con->scratchpad) { | 972 | if (!con->scratchpad) { |
969 | root_scratchpad_add_container(con, NULL); | 973 | root_scratchpad_add_container(con, NULL); |
970 | } else if (con->workspace) { | 974 | } else if (con->pending.workspace) { |
971 | root_scratchpad_hide(con); | 975 | root_scratchpad_hide(con); |
972 | } | 976 | } |
973 | return cmd_results_new(CMD_SUCCESS, NULL); | 977 | return cmd_results_new(CMD_SUCCESS, NULL); |
diff --git a/sway/commands/output.c b/sway/commands/output.c index 5186a2ba..c102344d 100644 --- a/sway/commands/output.c +++ b/sway/commands/output.c | |||
@@ -6,7 +6,7 @@ | |||
6 | #include "log.h" | 6 | #include "log.h" |
7 | 7 | ||
8 | // must be in order for the bsearch | 8 | // must be in order for the bsearch |
9 | static struct cmd_handler output_handlers[] = { | 9 | static const struct cmd_handler output_handlers[] = { |
10 | { "adaptive_sync", output_cmd_adaptive_sync }, | 10 | { "adaptive_sync", output_cmd_adaptive_sync }, |
11 | { "background", output_cmd_background }, | 11 | { "background", output_cmd_background }, |
12 | { "bg", output_cmd_background }, | 12 | { "bg", output_cmd_background }, |
@@ -15,8 +15,11 @@ static struct cmd_handler output_handlers[] = { | |||
15 | { "enable", output_cmd_enable }, | 15 | { "enable", output_cmd_enable }, |
16 | { "max_render_time", output_cmd_max_render_time }, | 16 | { "max_render_time", output_cmd_max_render_time }, |
17 | { "mode", output_cmd_mode }, | 17 | { "mode", output_cmd_mode }, |
18 | { "modeline", output_cmd_modeline }, | ||
18 | { "pos", output_cmd_position }, | 19 | { "pos", output_cmd_position }, |
19 | { "position", output_cmd_position }, | 20 | { "position", output_cmd_position }, |
21 | { "power", output_cmd_power }, | ||
22 | { "render_bit_depth", output_cmd_render_bit_depth }, | ||
20 | { "res", output_cmd_mode }, | 23 | { "res", output_cmd_mode }, |
21 | { "resolution", output_cmd_mode }, | 24 | { "resolution", output_cmd_mode }, |
22 | { "scale", output_cmd_scale }, | 25 | { "scale", output_cmd_scale }, |
@@ -32,9 +35,9 @@ struct cmd_results *cmd_output(int argc, char **argv) { | |||
32 | return error; | 35 | return error; |
33 | } | 36 | } |
34 | 37 | ||
35 | // The NOOP-1 output is a dummy output used when there's no outputs | 38 | // The HEADLESS-1 output is a dummy output used when there's no outputs |
36 | // connected. It should never be configured. | 39 | // connected. It should never be configured. |
37 | if (strcasecmp(argv[0], root->noop_output->wlr_output->name) == 0) { | 40 | if (strcasecmp(argv[0], root->fallback_output->wlr_output->name) == 0) { |
38 | return cmd_results_new(CMD_FAILURE, | 41 | return cmd_results_new(CMD_FAILURE, |
39 | "Refusing to configure the no op output"); | 42 | "Refusing to configure the no op output"); |
40 | } | 43 | } |
@@ -51,7 +54,7 @@ struct cmd_results *cmd_output(int argc, char **argv) { | |||
51 | if (!sway_output) { | 54 | if (!sway_output) { |
52 | return cmd_results_new(CMD_FAILURE, "Unknown output"); | 55 | return cmd_results_new(CMD_FAILURE, "Unknown output"); |
53 | } | 56 | } |
54 | if (sway_output == root->noop_output) { | 57 | if (sway_output == root->fallback_output) { |
55 | return cmd_results_new(CMD_FAILURE, | 58 | return cmd_results_new(CMD_FAILURE, |
56 | "Refusing to configure the no op output"); | 59 | "Refusing to configure the no op output"); |
57 | } | 60 | } |
diff --git a/sway/commands/output/background.c b/sway/commands/output/background.c index 68ee9fe1..67f212ff 100644 --- a/sway/commands/output/background.c +++ b/sway/commands/output/background.c | |||
@@ -102,19 +102,19 @@ struct cmd_results *output_cmd_background(int argc, char **argv) { | |||
102 | } | 102 | } |
103 | 103 | ||
104 | char *conf_path = dirname(conf); | 104 | char *conf_path = dirname(conf); |
105 | char *rel_path = src; | 105 | char *real_src = malloc(strlen(conf_path) + strlen(src) + 2); |
106 | src = malloc(strlen(conf_path) + strlen(src) + 2); | 106 | if (!real_src) { |
107 | if (!src) { | 107 | free(src); |
108 | free(rel_path); | ||
109 | free(conf); | 108 | free(conf); |
110 | sway_log(SWAY_ERROR, "Unable to allocate memory"); | 109 | sway_log(SWAY_ERROR, "Unable to allocate memory"); |
111 | return cmd_results_new(CMD_FAILURE, | 110 | return cmd_results_new(CMD_FAILURE, |
112 | "Unable to allocate resources"); | 111 | "Unable to allocate resources"); |
113 | } | 112 | } |
114 | 113 | ||
115 | sprintf(src, "%s/%s", conf_path, rel_path); | 114 | snprintf(real_src, strlen(conf_path) + strlen(src) + 2, "%s/%s", conf_path, src); |
116 | free(rel_path); | 115 | free(src); |
117 | free(conf); | 116 | free(conf); |
117 | src = real_src; | ||
118 | } | 118 | } |
119 | 119 | ||
120 | bool can_access = access(src, F_OK) != -1; | 120 | bool can_access = access(src, F_OK) != -1; |
diff --git a/sway/commands/output/dpms.c b/sway/commands/output/dpms.c index 9d75a80e..c7adbd58 100644 --- a/sway/commands/output/dpms.c +++ b/sway/commands/output/dpms.c | |||
@@ -1,22 +1,8 @@ | |||
1 | #include "log.h" | ||
1 | #include "sway/commands.h" | 2 | #include "sway/commands.h" |
2 | #include "sway/config.h" | ||
3 | #include "util.h" | ||
4 | 3 | ||
5 | struct cmd_results *output_cmd_dpms(int argc, char **argv) { | 4 | struct cmd_results *output_cmd_dpms(int argc, char **argv) { |
6 | if (!config->handler_context.output_config) { | 5 | sway_log(SWAY_INFO, "The \"output dpms\" command is deprecated, " |
7 | return cmd_results_new(CMD_FAILURE, "Missing output config"); | 6 | "use \"output power\" instead"); |
8 | } | 7 | return output_cmd_power(argc, argv); |
9 | if (!argc) { | ||
10 | return cmd_results_new(CMD_INVALID, "Missing dpms argument."); | ||
11 | } | ||
12 | |||
13 | if (parse_boolean(argv[0], true)) { | ||
14 | config->handler_context.output_config->dpms_state = DPMS_ON; | ||
15 | } else { | ||
16 | config->handler_context.output_config->dpms_state = DPMS_OFF; | ||
17 | } | ||
18 | |||
19 | config->handler_context.leftovers.argc = argc - 1; | ||
20 | config->handler_context.leftovers.argv = argv + 1; | ||
21 | return NULL; | ||
22 | } | 8 | } |
diff --git a/sway/commands/output/mode.c b/sway/commands/output/mode.c index 5b710713..019d625a 100644 --- a/sway/commands/output/mode.c +++ b/sway/commands/output/mode.c | |||
@@ -20,6 +20,9 @@ struct cmd_results *output_cmd_mode(int argc, char **argv) { | |||
20 | output->custom_mode = 0; | 20 | output->custom_mode = 0; |
21 | } | 21 | } |
22 | 22 | ||
23 | // Reset custom modeline, if any | ||
24 | output->drm_mode.type = 0; | ||
25 | |||
23 | char *end; | 26 | char *end; |
24 | output->width = strtol(*argv, &end, 10); | 27 | output->width = strtol(*argv, &end, 10); |
25 | if (*end) { | 28 | if (*end) { |
@@ -58,3 +61,58 @@ struct cmd_results *output_cmd_mode(int argc, char **argv) { | |||
58 | return NULL; | 61 | return NULL; |
59 | } | 62 | } |
60 | 63 | ||
64 | static bool parse_modeline(char **argv, drmModeModeInfo *mode) { | ||
65 | mode->type = DRM_MODE_TYPE_USERDEF; | ||
66 | mode->clock = strtof(argv[0], NULL) * 1000; | ||
67 | mode->hdisplay = strtol(argv[1], NULL, 10); | ||
68 | mode->hsync_start = strtol(argv[2], NULL, 10); | ||
69 | mode->hsync_end = strtol(argv[3], NULL, 10); | ||
70 | mode->htotal = strtol(argv[4], NULL, 10); | ||
71 | mode->vdisplay = strtol(argv[5], NULL, 10); | ||
72 | mode->vsync_start = strtol(argv[6], NULL, 10); | ||
73 | mode->vsync_end = strtol(argv[7], NULL, 10); | ||
74 | mode->vtotal = strtol(argv[8], NULL, 10); | ||
75 | |||
76 | mode->vrefresh = mode->clock * 1000.0 * 1000.0 | ||
77 | / mode->htotal / mode->vtotal; | ||
78 | if (strcasecmp(argv[9], "+hsync") == 0) { | ||
79 | mode->flags |= DRM_MODE_FLAG_PHSYNC; | ||
80 | } else if (strcasecmp(argv[9], "-hsync") == 0) { | ||
81 | mode->flags |= DRM_MODE_FLAG_NHSYNC; | ||
82 | } else { | ||
83 | return false; | ||
84 | } | ||
85 | |||
86 | if (strcasecmp(argv[10], "+vsync") == 0) { | ||
87 | mode->flags |= DRM_MODE_FLAG_PVSYNC; | ||
88 | } else if (strcasecmp(argv[10], "-vsync") == 0) { | ||
89 | mode->flags |= DRM_MODE_FLAG_NVSYNC; | ||
90 | } else { | ||
91 | return false; | ||
92 | } | ||
93 | |||
94 | snprintf(mode->name, sizeof(mode->name), "%dx%d@%d", | ||
95 | mode->hdisplay, mode->vdisplay, mode->vrefresh / 1000); | ||
96 | |||
97 | return true; | ||
98 | } | ||
99 | |||
100 | struct cmd_results *output_cmd_modeline(int argc, char **argv) { | ||
101 | if (!config->handler_context.output_config) { | ||
102 | return cmd_results_new(CMD_FAILURE, "Missing output config"); | ||
103 | } | ||
104 | if (!argc) { | ||
105 | return cmd_results_new(CMD_INVALID, "Missing modeline argument."); | ||
106 | } | ||
107 | |||
108 | struct output_config *output = config->handler_context.output_config; | ||
109 | |||
110 | if (argc != 11 || !parse_modeline(argv, &output->drm_mode)) { | ||
111 | return cmd_results_new(CMD_INVALID, "Invalid modeline"); | ||
112 | } | ||
113 | |||
114 | config->handler_context.leftovers.argc = argc - 12; | ||
115 | config->handler_context.leftovers.argv = argv + 12; | ||
116 | return NULL; | ||
117 | } | ||
118 | |||
diff --git a/sway/commands/output/power.c b/sway/commands/output/power.c new file mode 100644 index 00000000..e6ae2852 --- /dev/null +++ b/sway/commands/output/power.c | |||
@@ -0,0 +1,43 @@ | |||
1 | #include <strings.h> | ||
2 | #include "sway/commands.h" | ||
3 | #include "sway/config.h" | ||
4 | #include "sway/output.h" | ||
5 | #include "util.h" | ||
6 | |||
7 | struct cmd_results *output_cmd_power(int argc, char **argv) { | ||
8 | if (!config->handler_context.output_config) { | ||
9 | return cmd_results_new(CMD_FAILURE, "Missing output config"); | ||
10 | } | ||
11 | if (argc == 0) { | ||
12 | return cmd_results_new(CMD_INVALID, "Missing power argument"); | ||
13 | } | ||
14 | |||
15 | bool current = true; | ||
16 | if (strcasecmp(argv[0], "toggle") == 0) { | ||
17 | const char *oc_name = config->handler_context.output_config->name; | ||
18 | if (strcmp(oc_name, "*") == 0) { | ||
19 | return cmd_results_new(CMD_INVALID, | ||
20 | "Cannot apply toggle to all outputs"); | ||
21 | } | ||
22 | |||
23 | struct sway_output *sway_output = all_output_by_name_or_id(oc_name); | ||
24 | if (!sway_output || !sway_output->wlr_output) { | ||
25 | return cmd_results_new(CMD_FAILURE, | ||
26 | "Cannot apply toggle to unknown output %s", oc_name); | ||
27 | } | ||
28 | |||
29 | if (sway_output->enabled && !sway_output->wlr_output->enabled) { | ||
30 | current = false; | ||
31 | } | ||
32 | } | ||
33 | |||
34 | if (parse_boolean(argv[0], current)) { | ||
35 | config->handler_context.output_config->power = 1; | ||
36 | } else { | ||
37 | config->handler_context.output_config->power = 0; | ||
38 | } | ||
39 | |||
40 | config->handler_context.leftovers.argc = argc - 1; | ||
41 | config->handler_context.leftovers.argv = argv + 1; | ||
42 | return NULL; | ||
43 | } | ||
diff --git a/sway/commands/output/render_bit_depth.c b/sway/commands/output/render_bit_depth.c new file mode 100644 index 00000000..c419321e --- /dev/null +++ b/sway/commands/output/render_bit_depth.c | |||
@@ -0,0 +1,29 @@ | |||
1 | #include <drm_fourcc.h> | ||
2 | #include <strings.h> | ||
3 | #include "sway/commands.h" | ||
4 | #include "sway/config.h" | ||
5 | |||
6 | struct cmd_results *output_cmd_render_bit_depth(int argc, char **argv) { | ||
7 | if (!config->handler_context.output_config) { | ||
8 | return cmd_results_new(CMD_FAILURE, "Missing output config"); | ||
9 | } | ||
10 | if (!argc) { | ||
11 | return cmd_results_new(CMD_INVALID, "Missing bit depth argument."); | ||
12 | } | ||
13 | |||
14 | if (strcmp(*argv, "8") == 0) { | ||
15 | config->handler_context.output_config->render_bit_depth = | ||
16 | RENDER_BIT_DEPTH_8; | ||
17 | } else if (strcmp(*argv, "10") == 0) { | ||
18 | config->handler_context.output_config->render_bit_depth = | ||
19 | RENDER_BIT_DEPTH_10; | ||
20 | } else { | ||
21 | return cmd_results_new(CMD_INVALID, | ||
22 | "Invalid bit depth. Must be a value in (8|10)."); | ||
23 | } | ||
24 | |||
25 | config->handler_context.leftovers.argc = argc - 1; | ||
26 | config->handler_context.leftovers.argv = argv + 1; | ||
27 | return NULL; | ||
28 | } | ||
29 | |||
diff --git a/sway/commands/reload.c b/sway/commands/reload.c index 3c994d54..76f14bba 100644 --- a/sway/commands/reload.c +++ b/sway/commands/reload.c | |||
@@ -48,7 +48,6 @@ static void do_reload(void *data) { | |||
48 | } | 48 | } |
49 | list_free_items_and_destroy(bar_ids); | 49 | list_free_items_and_destroy(bar_ids); |
50 | 50 | ||
51 | config_update_font_height(true); | ||
52 | root_for_each_container(rebuild_textures_iterator, NULL); | 51 | root_for_each_container(rebuild_textures_iterator, NULL); |
53 | 52 | ||
54 | arrange_root(); | 53 | arrange_root(); |
diff --git a/sway/commands/resize.c b/sway/commands/resize.c index ca36e858..425069de 100644 --- a/sway/commands/resize.c +++ b/sway/commands/resize.c | |||
@@ -57,7 +57,7 @@ struct sway_container *container_find_resize_parent(struct sway_container *con, | |||
57 | (allow_last || index < siblings->length - 1)) { | 57 | (allow_last || index < siblings->length - 1)) { |
58 | return con; | 58 | return con; |
59 | } | 59 | } |
60 | con = con->parent; | 60 | con = con->pending.parent; |
61 | } | 61 | } |
62 | 62 | ||
63 | return NULL; | 63 | return NULL; |
@@ -115,13 +115,13 @@ void container_resize_tiled(struct sway_container *con, | |||
115 | int sibling_amount = prev ? ceil((double)amount / 2.0) : amount; | 115 | int sibling_amount = prev ? ceil((double)amount / 2.0) : amount; |
116 | 116 | ||
117 | if (is_horizontal(axis)) { | 117 | if (is_horizontal(axis)) { |
118 | if (con->width + amount < MIN_SANE_W) { | 118 | if (con->pending.width + amount < MIN_SANE_W) { |
119 | return; | 119 | return; |
120 | } | 120 | } |
121 | if (next->width - sibling_amount < MIN_SANE_W) { | 121 | if (next->pending.width - sibling_amount < MIN_SANE_W) { |
122 | return; | 122 | return; |
123 | } | 123 | } |
124 | if (prev && prev->width - sibling_amount < MIN_SANE_W) { | 124 | if (prev && prev->pending.width - sibling_amount < MIN_SANE_W) { |
125 | return; | 125 | return; |
126 | } | 126 | } |
127 | if (con->child_total_width <= 0) { | 127 | if (con->child_total_width <= 0) { |
@@ -133,7 +133,7 @@ void container_resize_tiled(struct sway_container *con, | |||
133 | list_t *siblings = container_get_siblings(con); | 133 | list_t *siblings = container_get_siblings(con); |
134 | for (int i = 0; i < siblings->length; ++i) { | 134 | for (int i = 0; i < siblings->length; ++i) { |
135 | struct sway_container *con = siblings->items[i]; | 135 | struct sway_container *con = siblings->items[i]; |
136 | con->width_fraction = con->width / con->child_total_width; | 136 | con->width_fraction = con->pending.width / con->child_total_width; |
137 | } | 137 | } |
138 | 138 | ||
139 | double amount_fraction = (double)amount / con->child_total_width; | 139 | double amount_fraction = (double)amount / con->child_total_width; |
@@ -146,13 +146,13 @@ void container_resize_tiled(struct sway_container *con, | |||
146 | prev->width_fraction -= sibling_amount_fraction; | 146 | prev->width_fraction -= sibling_amount_fraction; |
147 | } | 147 | } |
148 | } else { | 148 | } else { |
149 | if (con->height + amount < MIN_SANE_H) { | 149 | if (con->pending.height + amount < MIN_SANE_H) { |
150 | return; | 150 | return; |
151 | } | 151 | } |
152 | if (next->height - sibling_amount < MIN_SANE_H) { | 152 | if (next->pending.height - sibling_amount < MIN_SANE_H) { |
153 | return; | 153 | return; |
154 | } | 154 | } |
155 | if (prev && prev->height - sibling_amount < MIN_SANE_H) { | 155 | if (prev && prev->pending.height - sibling_amount < MIN_SANE_H) { |
156 | return; | 156 | return; |
157 | } | 157 | } |
158 | if (con->child_total_height <= 0) { | 158 | if (con->child_total_height <= 0) { |
@@ -164,7 +164,7 @@ void container_resize_tiled(struct sway_container *con, | |||
164 | list_t *siblings = container_get_siblings(con); | 164 | list_t *siblings = container_get_siblings(con); |
165 | for (int i = 0; i < siblings->length; ++i) { | 165 | for (int i = 0; i < siblings->length; ++i) { |
166 | struct sway_container *con = siblings->items[i]; | 166 | struct sway_container *con = siblings->items[i]; |
167 | con->height_fraction = con->height / con->child_total_height; | 167 | con->height_fraction = con->pending.height / con->child_total_height; |
168 | } | 168 | } |
169 | 169 | ||
170 | double amount_fraction = (double)amount / con->child_total_height; | 170 | double amount_fraction = (double)amount / con->child_total_height; |
@@ -178,10 +178,10 @@ void container_resize_tiled(struct sway_container *con, | |||
178 | } | 178 | } |
179 | } | 179 | } |
180 | 180 | ||
181 | if (con->parent) { | 181 | if (con->pending.parent) { |
182 | arrange_container(con->parent); | 182 | arrange_container(con->pending.parent); |
183 | } else { | 183 | } else { |
184 | arrange_workspace(con->workspace); | 184 | arrange_workspace(con->pending.workspace); |
185 | } | 185 | } |
186 | } | 186 | } |
187 | 187 | ||
@@ -203,15 +203,15 @@ static struct cmd_results *resize_adjust_floating(uint32_t axis, | |||
203 | int min_width, max_width, min_height, max_height; | 203 | int min_width, max_width, min_height, max_height; |
204 | floating_calculate_constraints(&min_width, &max_width, | 204 | floating_calculate_constraints(&min_width, &max_width, |
205 | &min_height, &max_height); | 205 | &min_height, &max_height); |
206 | if (con->width + grow_width < min_width) { | 206 | if (con->pending.width + grow_width < min_width) { |
207 | grow_width = min_width - con->width; | 207 | grow_width = min_width - con->pending.width; |
208 | } else if (con->width + grow_width > max_width) { | 208 | } else if (con->pending.width + grow_width > max_width) { |
209 | grow_width = max_width - con->width; | 209 | grow_width = max_width - con->pending.width; |
210 | } | 210 | } |
211 | if (con->height + grow_height < min_height) { | 211 | if (con->pending.height + grow_height < min_height) { |
212 | grow_height = min_height - con->height; | 212 | grow_height = min_height - con->pending.height; |
213 | } else if (con->height + grow_height > max_height) { | 213 | } else if (con->pending.height + grow_height > max_height) { |
214 | grow_height = max_height - con->height; | 214 | grow_height = max_height - con->pending.height; |
215 | } | 215 | } |
216 | int grow_x = 0, grow_y = 0; | 216 | int grow_x = 0, grow_y = 0; |
217 | 217 | ||
@@ -227,15 +227,15 @@ static struct cmd_results *resize_adjust_floating(uint32_t axis, | |||
227 | if (grow_width == 0 && grow_height == 0) { | 227 | if (grow_width == 0 && grow_height == 0) { |
228 | return cmd_results_new(CMD_INVALID, "Cannot resize any further"); | 228 | return cmd_results_new(CMD_INVALID, "Cannot resize any further"); |
229 | } | 229 | } |
230 | con->x += grow_x; | 230 | con->pending.x += grow_x; |
231 | con->y += grow_y; | 231 | con->pending.y += grow_y; |
232 | con->width += grow_width; | 232 | con->pending.width += grow_width; |
233 | con->height += grow_height; | 233 | con->pending.height += grow_height; |
234 | 234 | ||
235 | con->content_x += grow_x; | 235 | con->pending.content_x += grow_x; |
236 | con->content_y += grow_y; | 236 | con->pending.content_y += grow_y; |
237 | con->content_width += grow_width; | 237 | con->pending.content_width += grow_width; |
238 | con->content_height += grow_height; | 238 | con->pending.content_height += grow_height; |
239 | 239 | ||
240 | arrange_container(con); | 240 | arrange_container(con); |
241 | 241 | ||
@@ -256,9 +256,9 @@ static struct cmd_results *resize_adjust_tiled(uint32_t axis, | |||
256 | float pct = amount->amount / 100.0f; | 256 | float pct = amount->amount / 100.0f; |
257 | 257 | ||
258 | if (is_horizontal(axis)) { | 258 | if (is_horizontal(axis)) { |
259 | amount->amount = (float)current->width * pct; | 259 | amount->amount = (float)current->pending.width * pct; |
260 | } else { | 260 | } else { |
261 | amount->amount = (float)current->height * pct; | 261 | amount->amount = (float)current->pending.height * pct; |
262 | } | 262 | } |
263 | } | 263 | } |
264 | 264 | ||
@@ -281,20 +281,20 @@ static struct cmd_results *resize_set_tiled(struct sway_container *con, | |||
281 | if (width->unit == MOVEMENT_UNIT_PPT || | 281 | if (width->unit == MOVEMENT_UNIT_PPT || |
282 | width->unit == MOVEMENT_UNIT_DEFAULT) { | 282 | width->unit == MOVEMENT_UNIT_DEFAULT) { |
283 | // Convert to px | 283 | // Convert to px |
284 | struct sway_container *parent = con->parent; | 284 | struct sway_container *parent = con->pending.parent; |
285 | while (parent && parent->layout != L_HORIZ) { | 285 | while (parent && parent->pending.layout != L_HORIZ) { |
286 | parent = parent->parent; | 286 | parent = parent->pending.parent; |
287 | } | 287 | } |
288 | if (parent) { | 288 | if (parent) { |
289 | width->amount = parent->width * width->amount / 100; | 289 | width->amount = parent->pending.width * width->amount / 100; |
290 | } else { | 290 | } else { |
291 | width->amount = con->workspace->width * width->amount / 100; | 291 | width->amount = con->pending.workspace->width * width->amount / 100; |
292 | } | 292 | } |
293 | width->unit = MOVEMENT_UNIT_PX; | 293 | width->unit = MOVEMENT_UNIT_PX; |
294 | } | 294 | } |
295 | if (width->unit == MOVEMENT_UNIT_PX) { | 295 | if (width->unit == MOVEMENT_UNIT_PX) { |
296 | container_resize_tiled(con, AXIS_HORIZONTAL, | 296 | container_resize_tiled(con, AXIS_HORIZONTAL, |
297 | width->amount - con->width); | 297 | width->amount - con->pending.width); |
298 | } | 298 | } |
299 | } | 299 | } |
300 | 300 | ||
@@ -302,20 +302,20 @@ static struct cmd_results *resize_set_tiled(struct sway_container *con, | |||
302 | if (height->unit == MOVEMENT_UNIT_PPT || | 302 | if (height->unit == MOVEMENT_UNIT_PPT || |
303 | height->unit == MOVEMENT_UNIT_DEFAULT) { | 303 | height->unit == MOVEMENT_UNIT_DEFAULT) { |
304 | // Convert to px | 304 | // Convert to px |
305 | struct sway_container *parent = con->parent; | 305 | struct sway_container *parent = con->pending.parent; |
306 | while (parent && parent->layout != L_VERT) { | 306 | while (parent && parent->pending.layout != L_VERT) { |
307 | parent = parent->parent; | 307 | parent = parent->pending.parent; |
308 | } | 308 | } |
309 | if (parent) { | 309 | if (parent) { |
310 | height->amount = parent->height * height->amount / 100; | 310 | height->amount = parent->pending.height * height->amount / 100; |
311 | } else { | 311 | } else { |
312 | height->amount = con->workspace->height * height->amount / 100; | 312 | height->amount = con->pending.workspace->height * height->amount / 100; |
313 | } | 313 | } |
314 | height->unit = MOVEMENT_UNIT_PX; | 314 | height->unit = MOVEMENT_UNIT_PX; |
315 | } | 315 | } |
316 | if (height->unit == MOVEMENT_UNIT_PX) { | 316 | if (height->unit == MOVEMENT_UNIT_PX) { |
317 | container_resize_tiled(con, AXIS_VERTICAL, | 317 | container_resize_tiled(con, AXIS_VERTICAL, |
318 | height->amount - con->height); | 318 | height->amount - con->pending.height); |
319 | } | 319 | } |
320 | } | 320 | } |
321 | 321 | ||
@@ -339,15 +339,15 @@ static struct cmd_results *resize_set_floating(struct sway_container *con, | |||
339 | "Cannot resize a hidden scratchpad container by ppt"); | 339 | "Cannot resize a hidden scratchpad container by ppt"); |
340 | } | 340 | } |
341 | // Convert to px | 341 | // Convert to px |
342 | width->amount = con->workspace->width * width->amount / 100; | 342 | width->amount = con->pending.workspace->width * width->amount / 100; |
343 | width->unit = MOVEMENT_UNIT_PX; | 343 | width->unit = MOVEMENT_UNIT_PX; |
344 | // Falls through | 344 | // Falls through |
345 | case MOVEMENT_UNIT_PX: | 345 | case MOVEMENT_UNIT_PX: |
346 | case MOVEMENT_UNIT_DEFAULT: | 346 | case MOVEMENT_UNIT_DEFAULT: |
347 | width->amount = fmax(min_width, fmin(width->amount, max_width)); | 347 | width->amount = fmax(min_width, fmin(width->amount, max_width)); |
348 | grow_width = width->amount - con->width; | 348 | grow_width = width->amount - con->pending.width; |
349 | con->x -= grow_width / 2; | 349 | con->pending.x -= grow_width / 2; |
350 | con->width = width->amount; | 350 | con->pending.width = width->amount; |
351 | break; | 351 | break; |
352 | case MOVEMENT_UNIT_INVALID: | 352 | case MOVEMENT_UNIT_INVALID: |
353 | sway_assert(false, "invalid width unit"); | 353 | sway_assert(false, "invalid width unit"); |
@@ -363,15 +363,15 @@ static struct cmd_results *resize_set_floating(struct sway_container *con, | |||
363 | "Cannot resize a hidden scratchpad container by ppt"); | 363 | "Cannot resize a hidden scratchpad container by ppt"); |
364 | } | 364 | } |
365 | // Convert to px | 365 | // Convert to px |
366 | height->amount = con->workspace->height * height->amount / 100; | 366 | height->amount = con->pending.workspace->height * height->amount / 100; |
367 | height->unit = MOVEMENT_UNIT_PX; | 367 | height->unit = MOVEMENT_UNIT_PX; |
368 | // Falls through | 368 | // Falls through |
369 | case MOVEMENT_UNIT_PX: | 369 | case MOVEMENT_UNIT_PX: |
370 | case MOVEMENT_UNIT_DEFAULT: | 370 | case MOVEMENT_UNIT_DEFAULT: |
371 | height->amount = fmax(min_height, fmin(height->amount, max_height)); | 371 | height->amount = fmax(min_height, fmin(height->amount, max_height)); |
372 | grow_height = height->amount - con->height; | 372 | grow_height = height->amount - con->pending.height; |
373 | con->y -= grow_height / 2; | 373 | con->pending.y -= grow_height / 2; |
374 | con->height = height->amount; | 374 | con->pending.height = height->amount; |
375 | break; | 375 | break; |
376 | case MOVEMENT_UNIT_INVALID: | 376 | case MOVEMENT_UNIT_INVALID: |
377 | sway_assert(false, "invalid height unit"); | 377 | sway_assert(false, "invalid height unit"); |
@@ -379,10 +379,10 @@ static struct cmd_results *resize_set_floating(struct sway_container *con, | |||
379 | } | 379 | } |
380 | } | 380 | } |
381 | 381 | ||
382 | con->content_x -= grow_width / 2; | 382 | con->pending.content_x -= grow_width / 2; |
383 | con->content_y -= grow_height / 2; | 383 | con->pending.content_y -= grow_height / 2; |
384 | con->content_width += grow_width; | 384 | con->pending.content_width += grow_width; |
385 | con->content_height += grow_height; | 385 | con->pending.content_height += grow_height; |
386 | 386 | ||
387 | arrange_container(con); | 387 | arrange_container(con); |
388 | 388 | ||
@@ -437,10 +437,10 @@ static struct cmd_results *cmd_resize_set(int argc, char **argv) { | |||
437 | // If 0, don't resize that dimension | 437 | // If 0, don't resize that dimension |
438 | struct sway_container *con = config->handler_context.container; | 438 | struct sway_container *con = config->handler_context.container; |
439 | if (width.amount <= 0) { | 439 | if (width.amount <= 0) { |
440 | width.amount = con->width; | 440 | width.amount = con->pending.width; |
441 | } | 441 | } |
442 | if (height.amount <= 0) { | 442 | if (height.amount <= 0) { |
443 | height.amount = con->height; | 443 | height.amount = con->pending.height; |
444 | } | 444 | } |
445 | 445 | ||
446 | if (container_is_floating(con)) { | 446 | if (container_is_floating(con)) { |
diff --git a/sway/commands/scratchpad.c b/sway/commands/scratchpad.c index 34871bc6..c995f2f0 100644 --- a/sway/commands/scratchpad.c +++ b/sway/commands/scratchpad.c | |||
@@ -21,8 +21,8 @@ static void scratchpad_toggle_auto(void) { | |||
21 | // If the focus is in a floating split container, | 21 | // If the focus is in a floating split container, |
22 | // operate on the split container instead of the child. | 22 | // operate on the split container instead of the child. |
23 | if (focus && container_is_floating_or_child(focus)) { | 23 | if (focus && container_is_floating_or_child(focus)) { |
24 | while (focus->parent) { | 24 | while (focus->pending.parent) { |
25 | focus = focus->parent; | 25 | focus = focus->pending.parent; |
26 | } | 26 | } |
27 | } | 27 | } |
28 | 28 | ||
@@ -52,7 +52,7 @@ static void scratchpad_toggle_auto(void) { | |||
52 | // In this case we move it to the current workspace. | 52 | // In this case we move it to the current workspace. |
53 | for (int i = 0; i < root->scratchpad->length; ++i) { | 53 | for (int i = 0; i < root->scratchpad->length; ++i) { |
54 | struct sway_container *con = root->scratchpad->items[i]; | 54 | struct sway_container *con = root->scratchpad->items[i]; |
55 | if (con->parent) { | 55 | if (con->pending.parent) { |
56 | sway_log(SWAY_DEBUG, | 56 | sway_log(SWAY_DEBUG, |
57 | "Moving a visible scratchpad window (%s) to this workspace", | 57 | "Moving a visible scratchpad window (%s) to this workspace", |
58 | con->title); | 58 | con->title); |
@@ -80,7 +80,7 @@ static void scratchpad_toggle_container(struct sway_container *con) { | |||
80 | struct sway_seat *seat = input_manager_current_seat(); | 80 | struct sway_seat *seat = input_manager_current_seat(); |
81 | struct sway_workspace *ws = seat_get_focused_workspace(seat); | 81 | struct sway_workspace *ws = seat_get_focused_workspace(seat); |
82 | // Check if it matches a currently visible scratchpad window and hide it. | 82 | // Check if it matches a currently visible scratchpad window and hide it. |
83 | if (con->workspace && ws == con->workspace) { | 83 | if (con->pending.workspace && ws == con->pending.workspace) { |
84 | root_scratchpad_hide(con); | 84 | root_scratchpad_hide(con); |
85 | return; | 85 | return; |
86 | } | 86 | } |
@@ -105,21 +105,22 @@ struct cmd_results *cmd_scratchpad(int argc, char **argv) { | |||
105 | return cmd_results_new(CMD_INVALID, "Scratchpad is empty"); | 105 | return cmd_results_new(CMD_INVALID, "Scratchpad is empty"); |
106 | } | 106 | } |
107 | 107 | ||
108 | if (config->handler_context.using_criteria) { | 108 | if (config->handler_context.node_overridden) { |
109 | struct sway_container *con = config->handler_context.container; | 109 | struct sway_container *con = config->handler_context.container; |
110 | 110 | ||
111 | // If the container is in a floating split container, | 111 | // If the container is in a floating split container, |
112 | // operate on the split container instead of the child. | 112 | // operate on the split container instead of the child. |
113 | if (container_is_floating_or_child(con)) { | 113 | if (con && container_is_floating_or_child(con)) { |
114 | while (con->parent) { | 114 | while (con->pending.parent) { |
115 | con = con->parent; | 115 | con = con->pending.parent; |
116 | } | 116 | } |
117 | } | 117 | } |
118 | 118 | ||
119 | // If using criteria, this command is executed for every container which | 119 | // If using criteria, this command is executed for every container which |
120 | // matches the criteria. If this container isn't in the scratchpad, | 120 | // matches the criteria. If this container isn't in the scratchpad, |
121 | // we'll just silently return a success. | 121 | // we'll just silently return a success. The same is true if the |
122 | if (!con->scratchpad) { | 122 | // overridden node is not a container. |
123 | if (!con || !con->scratchpad) { | ||
123 | return cmd_results_new(CMD_SUCCESS, NULL); | 124 | return cmd_results_new(CMD_SUCCESS, NULL); |
124 | } | 125 | } |
125 | scratchpad_toggle_container(con); | 126 | scratchpad_toggle_container(con); |
diff --git a/sway/commands/seat.c b/sway/commands/seat.c index 84c6ba53..2d197b69 100644 --- a/sway/commands/seat.c +++ b/sway/commands/seat.c | |||
@@ -8,13 +8,13 @@ | |||
8 | 8 | ||
9 | // must be in order for the bsearch | 9 | // must be in order for the bsearch |
10 | // these handlers perform actions on the seat | 10 | // these handlers perform actions on the seat |
11 | static struct cmd_handler seat_action_handlers[] = { | 11 | static const struct cmd_handler seat_action_handlers[] = { |
12 | { "cursor", seat_cmd_cursor }, | 12 | { "cursor", seat_cmd_cursor }, |
13 | }; | 13 | }; |
14 | 14 | ||
15 | // must be in order for the bsearch | 15 | // must be in order for the bsearch |
16 | // these handlers alter the seat config | 16 | // these handlers alter the seat config |
17 | static struct cmd_handler seat_handlers[] = { | 17 | static const struct cmd_handler seat_handlers[] = { |
18 | { "attach", seat_cmd_attach }, | 18 | { "attach", seat_cmd_attach }, |
19 | { "fallback", seat_cmd_fallback }, | 19 | { "fallback", seat_cmd_fallback }, |
20 | { "hide_cursor", seat_cmd_hide_cursor }, | 20 | { "hide_cursor", seat_cmd_hide_cursor }, |
diff --git a/sway/commands/seat/attach.c b/sway/commands/seat/attach.c index 7615eef9..00bfdab6 100644 --- a/sway/commands/seat/attach.c +++ b/sway/commands/seat/attach.c | |||
@@ -12,7 +12,7 @@ struct cmd_results *seat_cmd_attach(int argc, char **argv) { | |||
12 | if (!config->handler_context.seat_config) { | 12 | if (!config->handler_context.seat_config) { |
13 | return cmd_results_new(CMD_FAILURE, "No seat defined"); | 13 | return cmd_results_new(CMD_FAILURE, "No seat defined"); |
14 | } | 14 | } |
15 | if (config->reading) { | 15 | if (!config->active) { |
16 | return cmd_results_new(CMD_DEFER, NULL); | 16 | return cmd_results_new(CMD_DEFER, NULL); |
17 | } | 17 | } |
18 | 18 | ||
diff --git a/sway/commands/seat/cursor.c b/sway/commands/seat/cursor.c index 749235eb..504a9f5e 100644 --- a/sway/commands/seat/cursor.c +++ b/sway/commands/seat/cursor.c | |||
@@ -111,8 +111,8 @@ static struct cmd_results *press_or_release(struct sway_cursor *cursor, | |||
111 | : WLR_AXIS_ORIENTATION_HORIZONTAL; | 111 | : WLR_AXIS_ORIENTATION_HORIZONTAL; |
112 | double delta = (button == SWAY_SCROLL_UP || button == SWAY_SCROLL_LEFT) | 112 | double delta = (button == SWAY_SCROLL_UP || button == SWAY_SCROLL_LEFT) |
113 | ? -1 : 1; | 113 | ? -1 : 1; |
114 | struct wlr_event_pointer_axis event = { | 114 | struct wlr_pointer_axis_event event = { |
115 | .device = NULL, | 115 | .pointer = NULL, |
116 | .time_msec = 0, | 116 | .time_msec = 0, |
117 | .source = WLR_AXIS_SOURCE_WHEEL, | 117 | .source = WLR_AXIS_SOURCE_WHEEL, |
118 | .orientation = orientation, | 118 | .orientation = orientation, |
diff --git a/sway/commands/smart_gaps.c b/sway/commands/smart_gaps.c index b27f9ccd..a6d165dc 100644 --- a/sway/commands/smart_gaps.c +++ b/sway/commands/smart_gaps.c | |||
@@ -15,7 +15,12 @@ struct cmd_results *cmd_smart_gaps(int argc, char **argv) { | |||
15 | return error; | 15 | return error; |
16 | } | 16 | } |
17 | 17 | ||
18 | config->smart_gaps = parse_boolean(argv[0], config->smart_gaps); | 18 | if (strcmp(argv[0], "inverse_outer") == 0) { |
19 | config->smart_gaps = SMART_GAPS_INVERSE_OUTER; | ||
20 | } else { | ||
21 | config->smart_gaps = parse_boolean(argv[0], config->smart_gaps) | ||
22 | ? SMART_GAPS_ON : SMART_GAPS_OFF; | ||
23 | } | ||
19 | 24 | ||
20 | arrange_root(); | 25 | arrange_root(); |
21 | 26 | ||
diff --git a/sway/commands/split.c b/sway/commands/split.c index 782bab02..c8a2cfc1 100644 --- a/sway/commands/split.c +++ b/sway/commands/split.c | |||
@@ -14,7 +14,7 @@ static struct cmd_results *do_split(int layout) { | |||
14 | struct sway_workspace *ws = config->handler_context.workspace; | 14 | struct sway_workspace *ws = config->handler_context.workspace; |
15 | if (con) { | 15 | if (con) { |
16 | if (container_is_scratchpad_hidden_or_child(con) && | 16 | if (container_is_scratchpad_hidden_or_child(con) && |
17 | con->fullscreen_mode != FULLSCREEN_GLOBAL) { | 17 | con->pending.fullscreen_mode != FULLSCREEN_GLOBAL) { |
18 | return cmd_results_new(CMD_FAILURE, | 18 | return cmd_results_new(CMD_FAILURE, |
19 | "Cannot split a hidden scratchpad container"); | 19 | "Cannot split a hidden scratchpad container"); |
20 | } | 20 | } |
@@ -32,6 +32,24 @@ static struct cmd_results *do_split(int layout) { | |||
32 | return cmd_results_new(CMD_SUCCESS, NULL); | 32 | return cmd_results_new(CMD_SUCCESS, NULL); |
33 | } | 33 | } |
34 | 34 | ||
35 | static struct cmd_results *do_unsplit() { | ||
36 | struct sway_container *con = config->handler_context.container; | ||
37 | struct sway_workspace *ws = config->handler_context.workspace; | ||
38 | |||
39 | if (con && con->pending.parent && con->pending.parent->pending.children->length == 1) { | ||
40 | container_flatten(con->pending.parent); | ||
41 | } else { | ||
42 | return cmd_results_new(CMD_FAILURE, "Can only flatten a child container with no siblings"); | ||
43 | } | ||
44 | |||
45 | if (root->fullscreen_global) { | ||
46 | arrange_root(); | ||
47 | } else { | ||
48 | arrange_workspace(ws); | ||
49 | } | ||
50 | return cmd_results_new(CMD_SUCCESS, NULL); | ||
51 | } | ||
52 | |||
35 | struct cmd_results *cmd_split(int argc, char **argv) { | 53 | struct cmd_results *cmd_split(int argc, char **argv) { |
36 | struct cmd_results *error = NULL; | 54 | struct cmd_results *error = NULL; |
37 | if ((error = checkarg(argc, "split", EXPECTED_EQUAL_TO, 1))) { | 55 | if ((error = checkarg(argc, "split", EXPECTED_EQUAL_TO, 1))) { |
@@ -55,6 +73,9 @@ struct cmd_results *cmd_split(int argc, char **argv) { | |||
55 | } else { | 73 | } else { |
56 | return do_split(L_VERT); | 74 | return do_split(L_VERT); |
57 | } | 75 | } |
76 | } else if (strcasecmp(argv[0], "n") == 0 || | ||
77 | strcasecmp(argv[0], "none") == 0) { | ||
78 | return do_unsplit(); | ||
58 | } else { | 79 | } else { |
59 | return cmd_results_new(CMD_FAILURE, | 80 | return cmd_results_new(CMD_FAILURE, |
60 | "Invalid split command (expected either horizontal or vertical)."); | 81 | "Invalid split command (expected either horizontal or vertical)."); |
diff --git a/sway/commands/sticky.c b/sway/commands/sticky.c index 3c93a276..9b09a0f9 100644 --- a/sway/commands/sticky.c +++ b/sway/commands/sticky.c | |||
@@ -29,14 +29,14 @@ struct cmd_results *cmd_sticky(int argc, char **argv) { | |||
29 | !container_is_scratchpad_hidden(container)) { | 29 | !container_is_scratchpad_hidden(container)) { |
30 | // move container to active workspace | 30 | // move container to active workspace |
31 | struct sway_workspace *active_workspace = | 31 | struct sway_workspace *active_workspace = |
32 | output_get_active_workspace(container->workspace->output); | 32 | output_get_active_workspace(container->pending.workspace->output); |
33 | if (!sway_assert(active_workspace, | 33 | if (!sway_assert(active_workspace, |
34 | "Expected output to have a workspace")) { | 34 | "Expected output to have a workspace")) { |
35 | return cmd_results_new(CMD_FAILURE, | 35 | return cmd_results_new(CMD_FAILURE, |
36 | "Expected output to have a workspace"); | 36 | "Expected output to have a workspace"); |
37 | } | 37 | } |
38 | if (container->workspace != active_workspace) { | 38 | if (container->pending.workspace != active_workspace) { |
39 | struct sway_workspace *old_workspace = container->workspace; | 39 | struct sway_workspace *old_workspace = container->pending.workspace; |
40 | container_detach(container); | 40 | container_detach(container); |
41 | workspace_add_floating(active_workspace, container); | 41 | workspace_add_floating(active_workspace, container); |
42 | container_handle_fullscreen_reparent(container); | 42 | container_handle_fullscreen_reparent(container); |
diff --git a/sway/commands/swap.c b/sway/commands/swap.c index a7f9691b..9355944d 100644 --- a/sway/commands/swap.c +++ b/sway/commands/swap.c | |||
@@ -16,46 +16,46 @@ static const char expected_syntax[] = | |||
16 | static void swap_places(struct sway_container *con1, | 16 | static void swap_places(struct sway_container *con1, |
17 | struct sway_container *con2) { | 17 | struct sway_container *con2) { |
18 | struct sway_container *temp = malloc(sizeof(struct sway_container)); | 18 | struct sway_container *temp = malloc(sizeof(struct sway_container)); |
19 | temp->x = con1->x; | 19 | temp->pending.x = con1->pending.x; |
20 | temp->y = con1->y; | 20 | temp->pending.y = con1->pending.y; |
21 | temp->width = con1->width; | 21 | temp->pending.width = con1->pending.width; |
22 | temp->height = con1->height; | 22 | temp->pending.height = con1->pending.height; |
23 | temp->width_fraction = con1->width_fraction; | 23 | temp->width_fraction = con1->width_fraction; |
24 | temp->height_fraction = con1->height_fraction; | 24 | temp->height_fraction = con1->height_fraction; |
25 | temp->parent = con1->parent; | 25 | temp->pending.parent = con1->pending.parent; |
26 | temp->workspace = con1->workspace; | 26 | temp->pending.workspace = con1->pending.workspace; |
27 | bool temp_floating = container_is_floating(con1); | 27 | bool temp_floating = container_is_floating(con1); |
28 | 28 | ||
29 | con1->x = con2->x; | 29 | con1->pending.x = con2->pending.x; |
30 | con1->y = con2->y; | 30 | con1->pending.y = con2->pending.y; |
31 | con1->width = con2->width; | 31 | con1->pending.width = con2->pending.width; |
32 | con1->height = con2->height; | 32 | con1->pending.height = con2->pending.height; |
33 | con1->width_fraction = con2->width_fraction; | 33 | con1->width_fraction = con2->width_fraction; |
34 | con1->height_fraction = con2->height_fraction; | 34 | con1->height_fraction = con2->height_fraction; |
35 | 35 | ||
36 | con2->x = temp->x; | 36 | con2->pending.x = temp->pending.x; |
37 | con2->y = temp->y; | 37 | con2->pending.y = temp->pending.y; |
38 | con2->width = temp->width; | 38 | con2->pending.width = temp->pending.width; |
39 | con2->height = temp->height; | 39 | con2->pending.height = temp->pending.height; |
40 | con2->width_fraction = temp->width_fraction; | 40 | con2->width_fraction = temp->width_fraction; |
41 | con2->height_fraction = temp->height_fraction; | 41 | con2->height_fraction = temp->height_fraction; |
42 | 42 | ||
43 | int temp_index = container_sibling_index(con1); | 43 | int temp_index = container_sibling_index(con1); |
44 | if (con2->parent) { | 44 | if (con2->pending.parent) { |
45 | container_insert_child(con2->parent, con1, | 45 | container_insert_child(con2->pending.parent, con1, |
46 | container_sibling_index(con2)); | 46 | container_sibling_index(con2)); |
47 | } else if (container_is_floating(con2)) { | 47 | } else if (container_is_floating(con2)) { |
48 | workspace_add_floating(con2->workspace, con1); | 48 | workspace_add_floating(con2->pending.workspace, con1); |
49 | } else { | 49 | } else { |
50 | workspace_insert_tiling(con2->workspace, con1, | 50 | workspace_insert_tiling(con2->pending.workspace, con1, |
51 | container_sibling_index(con2)); | 51 | container_sibling_index(con2)); |
52 | } | 52 | } |
53 | if (temp->parent) { | 53 | if (temp->pending.parent) { |
54 | container_insert_child(temp->parent, con2, temp_index); | 54 | container_insert_child(temp->pending.parent, con2, temp_index); |
55 | } else if (temp_floating) { | 55 | } else if (temp_floating) { |
56 | workspace_add_floating(temp->workspace, con2); | 56 | workspace_add_floating(temp->pending.workspace, con2); |
57 | } else { | 57 | } else { |
58 | workspace_insert_tiling(temp->workspace, con2, temp_index); | 58 | workspace_insert_tiling(temp->pending.workspace, con2, temp_index); |
59 | } | 59 | } |
60 | 60 | ||
61 | free(temp); | 61 | free(temp); |
@@ -65,8 +65,8 @@ static void swap_focus(struct sway_container *con1, | |||
65 | struct sway_container *con2, struct sway_seat *seat, | 65 | struct sway_container *con2, struct sway_seat *seat, |
66 | struct sway_container *focus) { | 66 | struct sway_container *focus) { |
67 | if (focus == con1 || focus == con2) { | 67 | if (focus == con1 || focus == con2) { |
68 | struct sway_workspace *ws1 = con1->workspace; | 68 | struct sway_workspace *ws1 = con1->pending.workspace; |
69 | struct sway_workspace *ws2 = con2->workspace; | 69 | struct sway_workspace *ws2 = con2->pending.workspace; |
70 | enum sway_container_layout layout1 = container_parent_layout(con1); | 70 | enum sway_container_layout layout1 = container_parent_layout(con1); |
71 | enum sway_container_layout layout2 = container_parent_layout(con2); | 71 | enum sway_container_layout layout2 = container_parent_layout(con2); |
72 | if (focus == con1 && (layout2 == L_TABBED || layout2 == L_STACKED)) { | 72 | if (focus == con1 && (layout2 == L_TABBED || layout2 == L_STACKED)) { |
@@ -125,11 +125,11 @@ void container_swap(struct sway_container *con1, struct sway_container *con2) { | |||
125 | root_scratchpad_remove_container(con2); | 125 | root_scratchpad_remove_container(con2); |
126 | } | 126 | } |
127 | 127 | ||
128 | enum sway_fullscreen_mode fs1 = con1->fullscreen_mode; | 128 | enum sway_fullscreen_mode fs1 = con1->pending.fullscreen_mode; |
129 | enum sway_fullscreen_mode fs2 = con2->fullscreen_mode; | ||
130 | if (fs1) { | 129 | if (fs1) { |
131 | container_fullscreen_disable(con1); | 130 | container_fullscreen_disable(con1); |
132 | } | 131 | } |
132 | enum sway_fullscreen_mode fs2 = con2->pending.fullscreen_mode; | ||
133 | if (fs2) { | 133 | if (fs2) { |
134 | container_fullscreen_disable(con2); | 134 | container_fullscreen_disable(con2); |
135 | } | 135 | } |
@@ -137,9 +137,9 @@ void container_swap(struct sway_container *con1, struct sway_container *con2) { | |||
137 | struct sway_seat *seat = config->handler_context.seat; | 137 | struct sway_seat *seat = config->handler_context.seat; |
138 | struct sway_container *focus = seat_get_focused_container(seat); | 138 | struct sway_container *focus = seat_get_focused_container(seat); |
139 | struct sway_workspace *vis1 = | 139 | struct sway_workspace *vis1 = |
140 | output_get_active_workspace(con1->workspace->output); | 140 | output_get_active_workspace(con1->pending.workspace->output); |
141 | struct sway_workspace *vis2 = | 141 | struct sway_workspace *vis2 = |
142 | output_get_active_workspace(con2->workspace->output); | 142 | output_get_active_workspace(con2->pending.workspace->output); |
143 | if (!sway_assert(vis1 && vis2, "con1 or con2 are on an output without a" | 143 | if (!sway_assert(vis1 && vis2, "con1 or con2 are on an output without a" |
144 | "workspace. This should not happen")) { | 144 | "workspace. This should not happen")) { |
145 | return; | 145 | return; |
@@ -247,6 +247,9 @@ struct cmd_results *cmd_swap(int argc, char **argv) { | |||
247 | } else if (!current) { | 247 | } else if (!current) { |
248 | error = cmd_results_new(CMD_FAILURE, | 248 | error = cmd_results_new(CMD_FAILURE, |
249 | "Can only swap with containers and views"); | 249 | "Can only swap with containers and views"); |
250 | } else if (current == other) { | ||
251 | error = cmd_results_new(CMD_FAILURE, | ||
252 | "Cannot swap a container with itself"); | ||
250 | } else if (container_has_ancestor(current, other) | 253 | } else if (container_has_ancestor(current, other) |
251 | || container_has_ancestor(other, current)) { | 254 | || container_has_ancestor(other, current)) { |
252 | error = cmd_results_new(CMD_FAILURE, | 255 | error = cmd_results_new(CMD_FAILURE, |
diff --git a/sway/commands/title_format.c b/sway/commands/title_format.c index 9d312470..a2446b7e 100644 --- a/sway/commands/title_format.c +++ b/sway/commands/title_format.c | |||
@@ -23,6 +23,5 @@ struct cmd_results *cmd_title_format(int argc, char **argv) { | |||
23 | } | 23 | } |
24 | view->title_format = format; | 24 | view->title_format = format; |
25 | view_update_title(view, true); | 25 | view_update_title(view, true); |
26 | config_update_font_height(true); | ||
27 | return cmd_results_new(CMD_SUCCESS, NULL); | 26 | return cmd_results_new(CMD_SUCCESS, NULL); |
28 | } | 27 | } |
diff --git a/sway/commands/unmark.c b/sway/commands/unmark.c index cedfcfb2..19274dfb 100644 --- a/sway/commands/unmark.c +++ b/sway/commands/unmark.c | |||
@@ -21,7 +21,7 @@ static void remove_all_marks_iterator(struct sway_container *con, void *data) { | |||
21 | struct cmd_results *cmd_unmark(int argc, char **argv) { | 21 | struct cmd_results *cmd_unmark(int argc, char **argv) { |
22 | // Determine the container | 22 | // Determine the container |
23 | struct sway_container *con = NULL; | 23 | struct sway_container *con = NULL; |
24 | if (config->handler_context.using_criteria) { | 24 | if (config->handler_context.node_overridden) { |
25 | con = config->handler_context.container; | 25 | con = config->handler_context.container; |
26 | } | 26 | } |
27 | 27 | ||
diff --git a/sway/commands/workspace.c b/sway/commands/workspace.c index 9ff1c97d..79f3667a 100644 --- a/sway/commands/workspace.c +++ b/sway/commands/workspace.c | |||
@@ -182,21 +182,16 @@ struct cmd_results *cmd_workspace(int argc, char **argv) { | |||
182 | "Can't switch workspaces while fullscreen global"); | 182 | "Can't switch workspaces while fullscreen global"); |
183 | } | 183 | } |
184 | 184 | ||
185 | bool no_auto_back_and_forth = false; | 185 | bool auto_back_and_forth = true; |
186 | while (strcasecmp(argv[0], "--no-auto-back-and-forth") == 0) { | 186 | while (strcasecmp(argv[0], "--no-auto-back-and-forth") == 0) { |
187 | no_auto_back_and_forth = true; | 187 | auto_back_and_forth = false; |
188 | if ((error = checkarg(--argc, "workspace", EXPECTED_AT_LEAST, 1))) { | 188 | if ((error = checkarg(--argc, "workspace", EXPECTED_AT_LEAST, 1))) { |
189 | return error; | 189 | return error; |
190 | } | 190 | } |
191 | ++argv; | 191 | ++argv; |
192 | } | 192 | } |
193 | 193 | ||
194 | bool create = argc > 1 && strcasecmp(argv[1], "--create") == 0; | ||
195 | struct sway_seat *seat = config->handler_context.seat; | 194 | struct sway_seat *seat = config->handler_context.seat; |
196 | struct sway_workspace *current = seat_get_focused_workspace(seat); | ||
197 | if (!current) { | ||
198 | return cmd_results_new(CMD_FAILURE, "No workspace to switch from"); | ||
199 | } | ||
200 | 195 | ||
201 | struct sway_workspace *ws = NULL; | 196 | struct sway_workspace *ws = NULL; |
202 | if (strcasecmp(argv[0], "number") == 0) { | 197 | if (strcasecmp(argv[0], "number") == 0) { |
@@ -213,14 +208,15 @@ struct cmd_results *cmd_workspace(int argc, char **argv) { | |||
213 | ws = workspace_create(NULL, name); | 208 | ws = workspace_create(NULL, name); |
214 | free(name); | 209 | free(name); |
215 | } | 210 | } |
211 | if (ws && auto_back_and_forth) { | ||
212 | ws = workspace_auto_back_and_forth(ws); | ||
213 | } | ||
216 | } else if (strcasecmp(argv[0], "next") == 0 || | 214 | } else if (strcasecmp(argv[0], "next") == 0 || |
217 | strcasecmp(argv[0], "prev") == 0 || | 215 | strcasecmp(argv[0], "prev") == 0 || |
216 | strcasecmp(argv[0], "next_on_output") == 0 || | ||
217 | strcasecmp(argv[0], "prev_on_output") == 0 || | ||
218 | strcasecmp(argv[0], "current") == 0) { | 218 | strcasecmp(argv[0], "current") == 0) { |
219 | ws = workspace_by_name(argv[0]); | 219 | ws = workspace_by_name(argv[0]); |
220 | } else if (strcasecmp(argv[0], "next_on_output") == 0) { | ||
221 | ws = workspace_output_next(current, create); | ||
222 | } else if (strcasecmp(argv[0], "prev_on_output") == 0) { | ||
223 | ws = workspace_output_prev(current, create); | ||
224 | } else if (strcasecmp(argv[0], "back_and_forth") == 0) { | 220 | } else if (strcasecmp(argv[0], "back_and_forth") == 0) { |
225 | if (!seat->prev_workspace_name) { | 221 | if (!seat->prev_workspace_name) { |
226 | return cmd_results_new(CMD_INVALID, | 222 | return cmd_results_new(CMD_INVALID, |
@@ -235,11 +231,14 @@ struct cmd_results *cmd_workspace(int argc, char **argv) { | |||
235 | ws = workspace_create(NULL, name); | 231 | ws = workspace_create(NULL, name); |
236 | } | 232 | } |
237 | free(name); | 233 | free(name); |
234 | if (ws && auto_back_and_forth) { | ||
235 | ws = workspace_auto_back_and_forth(ws); | ||
236 | } | ||
238 | } | 237 | } |
239 | if (!ws) { | 238 | if (!ws) { |
240 | return cmd_results_new(CMD_FAILURE, "No workspace to switch to"); | 239 | return cmd_results_new(CMD_FAILURE, "No workspace to switch to"); |
241 | } | 240 | } |
242 | workspace_switch(ws, no_auto_back_and_forth); | 241 | workspace_switch(ws); |
243 | seat_consider_warp_to_focus(seat); | 242 | seat_consider_warp_to_focus(seat); |
244 | } | 243 | } |
245 | return cmd_results_new(CMD_SUCCESS, NULL); | 244 | return cmd_results_new(CMD_SUCCESS, NULL); |
diff --git a/sway/config.c b/sway/config.c index 6e665434..b41dd871 100644 --- a/sway/config.c +++ b/sway/config.c | |||
@@ -26,7 +26,7 @@ | |||
26 | #include "sway/tree/arrange.h" | 26 | #include "sway/tree/arrange.h" |
27 | #include "sway/tree/root.h" | 27 | #include "sway/tree/root.h" |
28 | #include "sway/tree/workspace.h" | 28 | #include "sway/tree/workspace.h" |
29 | #include "cairo.h" | 29 | #include "cairo_util.h" |
30 | #include "pango.h" | 30 | #include "pango.h" |
31 | #include "stringop.h" | 31 | #include "stringop.h" |
32 | #include "list.h" | 32 | #include "list.h" |
@@ -82,6 +82,12 @@ static void free_mode(struct sway_mode *mode) { | |||
82 | } | 82 | } |
83 | list_free(mode->switch_bindings); | 83 | list_free(mode->switch_bindings); |
84 | } | 84 | } |
85 | if (mode->gesture_bindings) { | ||
86 | for (int i = 0; i < mode->gesture_bindings->length; i++) { | ||
87 | free_gesture_binding(mode->gesture_bindings->items[i]); | ||
88 | } | ||
89 | list_free(mode->gesture_bindings); | ||
90 | } | ||
85 | free(mode); | 91 | free(mode); |
86 | } | 92 | } |
87 | 93 | ||
@@ -222,6 +228,7 @@ static void config_defaults(struct sway_config *config) { | |||
222 | if (!(config->current_mode->keycode_bindings = create_list())) goto cleanup; | 228 | if (!(config->current_mode->keycode_bindings = create_list())) goto cleanup; |
223 | if (!(config->current_mode->mouse_bindings = create_list())) goto cleanup; | 229 | if (!(config->current_mode->mouse_bindings = create_list())) goto cleanup; |
224 | if (!(config->current_mode->switch_bindings = create_list())) goto cleanup; | 230 | if (!(config->current_mode->switch_bindings = create_list())) goto cleanup; |
231 | if (!(config->current_mode->gesture_bindings = create_list())) goto cleanup; | ||
225 | list_add(config->modes, config->current_mode); | 232 | list_add(config->modes, config->current_mode); |
226 | 233 | ||
227 | config->floating_mod = 0; | 234 | config->floating_mod = 0; |
@@ -236,7 +243,7 @@ static void config_defaults(struct sway_config *config) { | |||
236 | config->default_layout = L_NONE; | 243 | config->default_layout = L_NONE; |
237 | config->default_orientation = L_NONE; | 244 | config->default_orientation = L_NONE; |
238 | if (!(config->font = strdup("monospace 10"))) goto cleanup; | 245 | if (!(config->font = strdup("monospace 10"))) goto cleanup; |
239 | config->font_height = 17; // height of monospace 10 | 246 | config->font_description = pango_font_description_from_string(config->font); |
240 | config->urgent_timeout = 500; | 247 | config->urgent_timeout = 500; |
241 | config->focus_on_window_activation = FOWA_URGENT; | 248 | config->focus_on_window_activation = FOWA_URGENT; |
242 | config->popup_during_fullscreen = POPUP_SMART; | 249 | config->popup_during_fullscreen = POPUP_SMART; |
@@ -267,7 +274,7 @@ static void config_defaults(struct sway_config *config) { | |||
267 | config->tiling_drag = true; | 274 | config->tiling_drag = true; |
268 | config->tiling_drag_threshold = 9; | 275 | config->tiling_drag_threshold = 9; |
269 | 276 | ||
270 | config->smart_gaps = false; | 277 | config->smart_gaps = SMART_GAPS_OFF; |
271 | config->gaps_inner = 0; | 278 | config->gaps_inner = 0; |
272 | config->gaps_outer.top = 0; | 279 | config->gaps_outer.top = 0; |
273 | config->gaps_outer.right = 0; | 280 | config->gaps_outer.right = 0; |
@@ -291,6 +298,8 @@ static void config_defaults(struct sway_config *config) { | |||
291 | config->hide_edge_borders_smart = ESMART_OFF; | 298 | config->hide_edge_borders_smart = ESMART_OFF; |
292 | config->hide_lone_tab = false; | 299 | config->hide_lone_tab = false; |
293 | 300 | ||
301 | config->has_focused_tab_title = false; | ||
302 | |||
294 | // border colors | 303 | // border colors |
295 | color_to_rgba(config->border_colors.focused.border, 0x4C7899FF); | 304 | color_to_rgba(config->border_colors.focused.border, 0x4C7899FF); |
296 | color_to_rgba(config->border_colors.focused.background, 0x285577FF); | 305 | color_to_rgba(config->border_colors.focused.background, 0x285577FF); |
@@ -338,35 +347,62 @@ static bool file_exists(const char *path) { | |||
338 | return path && access(path, R_OK) != -1; | 347 | return path && access(path, R_OK) != -1; |
339 | } | 348 | } |
340 | 349 | ||
350 | static char *config_path(const char *prefix, const char *config_folder) { | ||
351 | if (!prefix || !prefix[0] || !config_folder || !config_folder[0]) { | ||
352 | return NULL; | ||
353 | } | ||
354 | |||
355 | const char *filename = "config"; | ||
356 | |||
357 | size_t size = 3 + strlen(prefix) + strlen(config_folder) + strlen(filename); | ||
358 | char *path = calloc(size, sizeof(char)); | ||
359 | snprintf(path, size, "%s/%s/%s", prefix, config_folder, filename); | ||
360 | return path; | ||
361 | } | ||
362 | |||
341 | static char *get_config_path(void) { | 363 | static char *get_config_path(void) { |
342 | static const char *config_paths[] = { | 364 | char *path = NULL; |
343 | "$HOME/.sway/config", | 365 | const char *home = getenv("HOME"); |
344 | "$XDG_CONFIG_HOME/sway/config", | 366 | char *config_home_fallback = NULL; |
345 | "$HOME/.i3/config", | 367 | |
346 | "$XDG_CONFIG_HOME/i3/config", | 368 | const char *config_home = getenv("XDG_CONFIG_HOME"); |
347 | SYSCONFDIR "/sway/config", | 369 | if ((config_home == NULL || config_home[0] == '\0') && home != NULL) { |
348 | SYSCONFDIR "/i3/config", | 370 | size_t size_fallback = 1 + strlen(home) + strlen("/.config"); |
371 | config_home_fallback = calloc(size_fallback, sizeof(char)); | ||
372 | if (config_home_fallback != NULL) | ||
373 | snprintf(config_home_fallback, size_fallback, "%s/.config", home); | ||
374 | config_home = config_home_fallback; | ||
375 | } | ||
376 | |||
377 | struct config_path { | ||
378 | const char *prefix; | ||
379 | const char *config_folder; | ||
349 | }; | 380 | }; |
350 | 381 | ||
351 | char *config_home = getenv("XDG_CONFIG_HOME"); | 382 | struct config_path config_paths[] = { |
352 | if (!config_home || !*config_home) { | 383 | { .prefix = home, .config_folder = ".sway"}, |
353 | config_paths[1] = "$HOME/.config/sway/config"; | 384 | { .prefix = config_home, .config_folder = "sway"}, |
354 | config_paths[3] = "$HOME/.config/i3/config"; | 385 | { .prefix = home, .config_folder = ".i3"}, |
355 | } | 386 | { .prefix = config_home, .config_folder = "i3"}, |
387 | { .prefix = SYSCONFDIR, .config_folder = "sway"}, | ||
388 | { .prefix = SYSCONFDIR, .config_folder = "i3"} | ||
389 | }; | ||
356 | 390 | ||
357 | for (size_t i = 0; i < sizeof(config_paths) / sizeof(char *); ++i) { | 391 | size_t num_config_paths = sizeof(config_paths)/sizeof(config_paths[0]); |
358 | wordexp_t p; | 392 | for (size_t i = 0; i < num_config_paths; i++) { |
359 | if (wordexp(config_paths[i], &p, WRDE_UNDEF) == 0) { | 393 | path = config_path(config_paths[i].prefix, config_paths[i].config_folder); |
360 | char *path = strdup(p.we_wordv[0]); | 394 | if (!path) { |
361 | wordfree(&p); | 395 | continue; |
362 | if (file_exists(path)) { | 396 | } |
363 | return path; | 397 | if (file_exists(path)) { |
364 | } | 398 | break; |
365 | free(path); | ||
366 | } | 399 | } |
400 | free(path); | ||
401 | path = NULL; | ||
367 | } | 402 | } |
368 | 403 | ||
369 | return NULL; | 404 | free(config_home_fallback); |
405 | return path; | ||
370 | } | 406 | } |
371 | 407 | ||
372 | static bool load_config(const char *path, struct sway_config *config, | 408 | static bool load_config(const char *path, struct sway_config *config, |
@@ -514,6 +550,9 @@ bool load_main_config(const char *file, bool is_active, bool validating) { | |||
514 | return success; | 550 | return success; |
515 | } | 551 | } |
516 | 552 | ||
553 | // Only really necessary if not explicitly `font` is set in the config. | ||
554 | config_update_font_height(); | ||
555 | |||
517 | if (is_active && !validating) { | 556 | if (is_active && !validating) { |
518 | input_manager_verify_fallback_seat(); | 557 | input_manager_verify_fallback_seat(); |
519 | 558 | ||
@@ -964,31 +1003,11 @@ int workspace_output_cmp_workspace(const void *a, const void *b) { | |||
964 | return lenient_strcmp(wsa->workspace, wsb->workspace); | 1003 | return lenient_strcmp(wsa->workspace, wsb->workspace); |
965 | } | 1004 | } |
966 | 1005 | ||
967 | static void find_font_height_iterator(struct sway_container *con, void *data) { | ||
968 | size_t amount_below_baseline = con->title_height - con->title_baseline; | ||
969 | size_t extended_height = config->font_baseline + amount_below_baseline; | ||
970 | if (extended_height > config->font_height) { | ||
971 | config->font_height = extended_height; | ||
972 | } | ||
973 | } | ||
974 | |||
975 | static void find_baseline_iterator(struct sway_container *con, void *data) { | ||
976 | bool *recalculate = data; | ||
977 | if (*recalculate) { | ||
978 | container_calculate_title_height(con); | ||
979 | } | ||
980 | if (con->title_baseline > config->font_baseline) { | ||
981 | config->font_baseline = con->title_baseline; | ||
982 | } | ||
983 | } | ||
984 | 1006 | ||
985 | void config_update_font_height(bool recalculate) { | 1007 | void config_update_font_height(void) { |
986 | size_t prev_max_height = config->font_height; | 1008 | int prev_max_height = config->font_height; |
987 | config->font_height = 0; | ||
988 | config->font_baseline = 0; | ||
989 | 1009 | ||
990 | root_for_each_container(find_baseline_iterator, &recalculate); | 1010 | get_text_metrics(config->font_description, &config->font_height, &config->font_baseline); |
991 | root_for_each_container(find_font_height_iterator, NULL); | ||
992 | 1011 | ||
993 | if (config->font_height != prev_max_height) { | 1012 | if (config->font_height != prev_max_height) { |
994 | arrange_root(); | 1013 | arrange_root(); |
diff --git a/sway/config/bar.c b/sway/config/bar.c index 767534a6..d1b342e6 100644 --- a/sway/config/bar.c +++ b/sway/config/bar.c | |||
@@ -91,7 +91,7 @@ struct bar_config *default_bar_config(void) { | |||
91 | } | 91 | } |
92 | bar->outputs = NULL; | 92 | bar->outputs = NULL; |
93 | bar->position = strdup("bottom"); | 93 | bar->position = strdup("bottom"); |
94 | bar->pango_markup = false; | 94 | bar->pango_markup = PANGO_MARKUP_DEFAULT; |
95 | bar->swaybar_command = NULL; | 95 | bar->swaybar_command = NULL; |
96 | bar->font = NULL; | 96 | bar->font = NULL; |
97 | bar->height = 0; | 97 | bar->height = 0; |
@@ -217,6 +217,9 @@ static void invoke_swaybar(struct bar_config *bar) { | |||
217 | sigset_t set; | 217 | sigset_t set; |
218 | sigemptyset(&set); | 218 | sigemptyset(&set); |
219 | sigprocmask(SIG_SETMASK, &set, NULL); | 219 | sigprocmask(SIG_SETMASK, &set, NULL); |
220 | signal(SIGPIPE, SIG_DFL); | ||
221 | |||
222 | restore_nofile_limit(); | ||
220 | 223 | ||
221 | pid = fork(); | 224 | pid = fork(); |
222 | if (pid < 0) { | 225 | if (pid < 0) { |
diff --git a/sway/config/output.c b/sway/config/output.c index c9ec6745..b3e8371e 100644 --- a/sway/config/output.c +++ b/sway/config/output.c | |||
@@ -1,5 +1,6 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | 1 | #define _POSIX_C_SOURCE 200809L |
2 | #include <assert.h> | 2 | #include <assert.h> |
3 | #include <drm_fourcc.h> | ||
3 | #include <stdbool.h> | 4 | #include <stdbool.h> |
4 | #include <string.h> | 5 | #include <string.h> |
5 | #include <sys/socket.h> | 6 | #include <sys/socket.h> |
@@ -8,6 +9,7 @@ | |||
8 | #include <wlr/types/wlr_cursor.h> | 9 | #include <wlr/types/wlr_cursor.h> |
9 | #include <wlr/types/wlr_output_layout.h> | 10 | #include <wlr/types/wlr_output_layout.h> |
10 | #include <wlr/types/wlr_output.h> | 11 | #include <wlr/types/wlr_output.h> |
12 | #include <wlr/backend/drm.h> | ||
11 | #include "sway/config.h" | 13 | #include "sway/config.h" |
12 | #include "sway/input/cursor.h" | 14 | #include "sway/input/cursor.h" |
13 | #include "sway/output.h" | 15 | #include "sway/output.h" |
@@ -25,8 +27,10 @@ int output_name_cmp(const void *item, const void *data) { | |||
25 | void output_get_identifier(char *identifier, size_t len, | 27 | void output_get_identifier(char *identifier, size_t len, |
26 | struct sway_output *output) { | 28 | struct sway_output *output) { |
27 | struct wlr_output *wlr_output = output->wlr_output; | 29 | struct wlr_output *wlr_output = output->wlr_output; |
28 | snprintf(identifier, len, "%s %s %s", wlr_output->make, wlr_output->model, | 30 | snprintf(identifier, len, "%s %s %s", |
29 | wlr_output->serial); | 31 | wlr_output->make ? wlr_output->make : "Unknown", |
32 | wlr_output->model ? wlr_output->model : "Unknown", | ||
33 | wlr_output->serial ? wlr_output->serial : "Unknown"); | ||
30 | } | 34 | } |
31 | 35 | ||
32 | const char *sway_output_scale_filter_to_string(enum scale_filter_mode scale_filter) { | 36 | const char *sway_output_scale_filter_to_string(enum scale_filter_mode scale_filter) { |
@@ -58,6 +62,7 @@ struct output_config *new_output_config(const char *name) { | |||
58 | oc->width = oc->height = -1; | 62 | oc->width = oc->height = -1; |
59 | oc->refresh_rate = -1; | 63 | oc->refresh_rate = -1; |
60 | oc->custom_mode = -1; | 64 | oc->custom_mode = -1; |
65 | oc->drm_mode.type = -1; | ||
61 | oc->x = oc->y = -1; | 66 | oc->x = oc->y = -1; |
62 | oc->scale = -1; | 67 | oc->scale = -1; |
63 | oc->scale_filter = SCALE_FILTER_DEFAULT; | 68 | oc->scale_filter = SCALE_FILTER_DEFAULT; |
@@ -65,6 +70,8 @@ struct output_config *new_output_config(const char *name) { | |||
65 | oc->subpixel = WL_OUTPUT_SUBPIXEL_UNKNOWN; | 70 | oc->subpixel = WL_OUTPUT_SUBPIXEL_UNKNOWN; |
66 | oc->max_render_time = -1; | 71 | oc->max_render_time = -1; |
67 | oc->adaptive_sync = -1; | 72 | oc->adaptive_sync = -1; |
73 | oc->render_bit_depth = RENDER_BIT_DEPTH_DEFAULT; | ||
74 | oc->power = -1; | ||
68 | return oc; | 75 | return oc; |
69 | } | 76 | } |
70 | 77 | ||
@@ -99,6 +106,9 @@ void merge_output_config(struct output_config *dst, struct output_config *src) { | |||
99 | if (src->custom_mode != -1) { | 106 | if (src->custom_mode != -1) { |
100 | dst->custom_mode = src->custom_mode; | 107 | dst->custom_mode = src->custom_mode; |
101 | } | 108 | } |
109 | if (src->drm_mode.type != (uint32_t) -1) { | ||
110 | memcpy(&dst->drm_mode, &src->drm_mode, sizeof(src->drm_mode)); | ||
111 | } | ||
102 | if (src->transform != -1) { | 112 | if (src->transform != -1) { |
103 | dst->transform = src->transform; | 113 | dst->transform = src->transform; |
104 | } | 114 | } |
@@ -108,6 +118,9 @@ void merge_output_config(struct output_config *dst, struct output_config *src) { | |||
108 | if (src->adaptive_sync != -1) { | 118 | if (src->adaptive_sync != -1) { |
109 | dst->adaptive_sync = src->adaptive_sync; | 119 | dst->adaptive_sync = src->adaptive_sync; |
110 | } | 120 | } |
121 | if (src->render_bit_depth != RENDER_BIT_DEPTH_DEFAULT) { | ||
122 | dst->render_bit_depth = src->render_bit_depth; | ||
123 | } | ||
111 | if (src->background) { | 124 | if (src->background) { |
112 | free(dst->background); | 125 | free(dst->background); |
113 | dst->background = strdup(src->background); | 126 | dst->background = strdup(src->background); |
@@ -120,8 +133,8 @@ void merge_output_config(struct output_config *dst, struct output_config *src) { | |||
120 | free(dst->background_fallback); | 133 | free(dst->background_fallback); |
121 | dst->background_fallback = strdup(src->background_fallback); | 134 | dst->background_fallback = strdup(src->background_fallback); |
122 | } | 135 | } |
123 | if (src->dpms_state != 0) { | 136 | if (src->power != -1) { |
124 | dst->dpms_state = src->dpms_state; | 137 | dst->power = src->power; |
125 | } | 138 | } |
126 | } | 139 | } |
127 | 140 | ||
@@ -180,11 +193,11 @@ static void merge_id_on_name(struct output_config *oc) { | |||
180 | list_add(config->output_configs, ion_oc); | 193 | list_add(config->output_configs, ion_oc); |
181 | sway_log(SWAY_DEBUG, "Generated id on name output config \"%s\"" | 194 | sway_log(SWAY_DEBUG, "Generated id on name output config \"%s\"" |
182 | " (enabled: %d) (%dx%d@%fHz position %d,%d scale %f " | 195 | " (enabled: %d) (%dx%d@%fHz position %d,%d scale %f " |
183 | "transform %d) (bg %s %s) (dpms %d) (max render time: %d)", | 196 | "transform %d) (bg %s %s) (power %d) (max render time: %d)", |
184 | ion_oc->name, ion_oc->enabled, ion_oc->width, ion_oc->height, | 197 | ion_oc->name, ion_oc->enabled, ion_oc->width, ion_oc->height, |
185 | ion_oc->refresh_rate, ion_oc->x, ion_oc->y, ion_oc->scale, | 198 | ion_oc->refresh_rate, ion_oc->x, ion_oc->y, ion_oc->scale, |
186 | ion_oc->transform, ion_oc->background, | 199 | ion_oc->transform, ion_oc->background, |
187 | ion_oc->background_option, ion_oc->dpms_state, | 200 | ion_oc->background_option, ion_oc->power, |
188 | ion_oc->max_render_time); | 201 | ion_oc->max_render_time); |
189 | } | 202 | } |
190 | } | 203 | } |
@@ -225,18 +238,18 @@ struct output_config *store_output_config(struct output_config *oc) { | |||
225 | } | 238 | } |
226 | 239 | ||
227 | sway_log(SWAY_DEBUG, "Config stored for output %s (enabled: %d) (%dx%d@%fHz " | 240 | sway_log(SWAY_DEBUG, "Config stored for output %s (enabled: %d) (%dx%d@%fHz " |
228 | "position %d,%d scale %f subpixel %s transform %d) (bg %s %s) (dpms %d) " | 241 | "position %d,%d scale %f subpixel %s transform %d) (bg %s %s) (power %d) " |
229 | "(max render time: %d)", | 242 | "(max render time: %d)", |
230 | oc->name, oc->enabled, oc->width, oc->height, oc->refresh_rate, | 243 | oc->name, oc->enabled, oc->width, oc->height, oc->refresh_rate, |
231 | oc->x, oc->y, oc->scale, sway_wl_output_subpixel_to_string(oc->subpixel), | 244 | oc->x, oc->y, oc->scale, sway_wl_output_subpixel_to_string(oc->subpixel), |
232 | oc->transform, oc->background, oc->background_option, oc->dpms_state, | 245 | oc->transform, oc->background, oc->background_option, oc->power, |
233 | oc->max_render_time); | 246 | oc->max_render_time); |
234 | 247 | ||
235 | return oc; | 248 | return oc; |
236 | } | 249 | } |
237 | 250 | ||
238 | static void set_mode(struct wlr_output *output, int width, int height, | 251 | static void set_mode(struct wlr_output *output, struct wlr_output_state *pending, |
239 | float refresh_rate, bool custom) { | 252 | int width, int height, float refresh_rate, bool custom) { |
240 | // Not all floating point integers can be represented exactly | 253 | // Not all floating point integers can be represented exactly |
241 | // as (int)(1000 * mHz / 1000.f) | 254 | // as (int)(1000 * mHz / 1000.f) |
242 | // round() the result to avoid any error | 255 | // round() the result to avoid any error |
@@ -244,7 +257,7 @@ static void set_mode(struct wlr_output *output, int width, int height, | |||
244 | 257 | ||
245 | if (wl_list_empty(&output->modes) || custom) { | 258 | if (wl_list_empty(&output->modes) || custom) { |
246 | sway_log(SWAY_DEBUG, "Assigning custom mode to %s", output->name); | 259 | sway_log(SWAY_DEBUG, "Assigning custom mode to %s", output->name); |
247 | wlr_output_set_custom_mode(output, width, height, | 260 | wlr_output_state_set_custom_mode(pending, width, height, |
248 | refresh_rate > 0 ? mhz : 0); | 261 | refresh_rate > 0 ? mhz : 0); |
249 | return; | 262 | return; |
250 | } | 263 | } |
@@ -268,7 +281,20 @@ static void set_mode(struct wlr_output *output, int width, int height, | |||
268 | } else { | 281 | } else { |
269 | sway_log(SWAY_DEBUG, "Assigning configured mode to %s", output->name); | 282 | sway_log(SWAY_DEBUG, "Assigning configured mode to %s", output->name); |
270 | } | 283 | } |
271 | wlr_output_set_mode(output, best); | 284 | wlr_output_state_set_mode(pending, best); |
285 | } | ||
286 | |||
287 | static void set_modeline(struct wlr_output *output, | ||
288 | struct wlr_output_state *pending, drmModeModeInfo *drm_mode) { | ||
289 | if (!wlr_output_is_drm(output)) { | ||
290 | sway_log(SWAY_ERROR, "Modeline can only be set to DRM output"); | ||
291 | return; | ||
292 | } | ||
293 | sway_log(SWAY_DEBUG, "Assigning custom modeline to %s", output->name); | ||
294 | struct wlr_output_mode *mode = wlr_drm_connector_add_mode(output, drm_mode); | ||
295 | if (mode) { | ||
296 | wlr_output_state_set_mode(pending, mode); | ||
297 | } | ||
272 | } | 298 | } |
273 | 299 | ||
274 | /* Some manufacturers hardcode the aspect-ratio of the output in the physical | 300 | /* Some manufacturers hardcode the aspect-ratio of the output in the physical |
@@ -289,23 +315,24 @@ static bool phys_size_is_aspect_ratio(struct wlr_output *output) { | |||
289 | // 1 inch = 25.4 mm | 315 | // 1 inch = 25.4 mm |
290 | #define MM_PER_INCH 25.4 | 316 | #define MM_PER_INCH 25.4 |
291 | 317 | ||
292 | static int compute_default_scale(struct wlr_output *output) { | 318 | static int compute_default_scale(struct wlr_output *output, |
319 | struct wlr_output_state *pending) { | ||
293 | struct wlr_box box = { .width = output->width, .height = output->height }; | 320 | struct wlr_box box = { .width = output->width, .height = output->height }; |
294 | if (output->pending.committed & WLR_OUTPUT_STATE_MODE) { | 321 | if (pending->committed & WLR_OUTPUT_STATE_MODE) { |
295 | switch (output->pending.mode_type) { | 322 | switch (pending->mode_type) { |
296 | case WLR_OUTPUT_STATE_MODE_FIXED: | 323 | case WLR_OUTPUT_STATE_MODE_FIXED: |
297 | box.width = output->pending.mode->width; | 324 | box.width = pending->mode->width; |
298 | box.height = output->pending.mode->height; | 325 | box.height = pending->mode->height; |
299 | break; | 326 | break; |
300 | case WLR_OUTPUT_STATE_MODE_CUSTOM: | 327 | case WLR_OUTPUT_STATE_MODE_CUSTOM: |
301 | box.width = output->pending.custom_mode.width; | 328 | box.width = pending->custom_mode.width; |
302 | box.height = output->pending.custom_mode.height; | 329 | box.height = pending->custom_mode.height; |
303 | break; | 330 | break; |
304 | } | 331 | } |
305 | } | 332 | } |
306 | enum wl_output_transform transform = output->transform; | 333 | enum wl_output_transform transform = output->transform; |
307 | if (output->pending.committed & WLR_OUTPUT_STATE_TRANSFORM) { | 334 | if (pending->committed & WLR_OUTPUT_STATE_TRANSFORM) { |
308 | transform = output->pending.transform; | 335 | transform = pending->transform; |
309 | } | 336 | } |
310 | wlr_box_transform(&box, &box, transform, box.width, box.height); | 337 | wlr_box_transform(&box, &box, transform, box.width, box.height); |
311 | 338 | ||
@@ -334,42 +361,88 @@ static int compute_default_scale(struct wlr_output *output) { | |||
334 | return 2; | 361 | return 2; |
335 | } | 362 | } |
336 | 363 | ||
364 | /* Lists of formats to try, in order, when a specific render bit depth has | ||
365 | * been asked for. The second to last format in each list should always | ||
366 | * be XRGB8888, as a reliable backup in case the others are not available; | ||
367 | * the last should be DRM_FORMAT_INVALID, to indicate the end of the list. */ | ||
368 | static const uint32_t *bit_depth_preferences[] = { | ||
369 | [RENDER_BIT_DEPTH_8] = (const uint32_t []){ | ||
370 | DRM_FORMAT_XRGB8888, | ||
371 | DRM_FORMAT_INVALID, | ||
372 | }, | ||
373 | [RENDER_BIT_DEPTH_10] = (const uint32_t []){ | ||
374 | DRM_FORMAT_XRGB2101010, | ||
375 | DRM_FORMAT_XBGR2101010, | ||
376 | DRM_FORMAT_XRGB8888, | ||
377 | DRM_FORMAT_INVALID, | ||
378 | }, | ||
379 | }; | ||
380 | |||
337 | static void queue_output_config(struct output_config *oc, | 381 | static void queue_output_config(struct output_config *oc, |
338 | struct sway_output *output) { | 382 | struct sway_output *output, struct wlr_output_state *pending) { |
339 | if (output == root->noop_output) { | 383 | if (output == root->fallback_output) { |
340 | return; | 384 | return; |
341 | } | 385 | } |
342 | 386 | ||
343 | struct wlr_output *wlr_output = output->wlr_output; | 387 | struct wlr_output *wlr_output = output->wlr_output; |
344 | 388 | ||
345 | if (oc && (!oc->enabled || oc->dpms_state == DPMS_OFF)) { | 389 | if (oc && (!oc->enabled || oc->power == 0)) { |
346 | sway_log(SWAY_DEBUG, "Turning off output %s", wlr_output->name); | 390 | sway_log(SWAY_DEBUG, "Turning off output %s", wlr_output->name); |
347 | wlr_output_enable(wlr_output, false); | 391 | wlr_output_state_set_enabled(pending, false); |
348 | return; | 392 | return; |
349 | } | 393 | } |
350 | 394 | ||
351 | sway_log(SWAY_DEBUG, "Turning on output %s", wlr_output->name); | 395 | sway_log(SWAY_DEBUG, "Turning on output %s", wlr_output->name); |
352 | wlr_output_enable(wlr_output, true); | 396 | wlr_output_state_set_enabled(pending, true); |
353 | 397 | ||
354 | if (oc && oc->width > 0 && oc->height > 0) { | 398 | if (oc && oc->drm_mode.type != 0 && oc->drm_mode.type != (uint32_t) -1) { |
399 | sway_log(SWAY_DEBUG, "Set %s modeline", | ||
400 | wlr_output->name); | ||
401 | set_modeline(wlr_output, pending, &oc->drm_mode); | ||
402 | } else if (oc && oc->width > 0 && oc->height > 0) { | ||
355 | sway_log(SWAY_DEBUG, "Set %s mode to %dx%d (%f Hz)", | 403 | sway_log(SWAY_DEBUG, "Set %s mode to %dx%d (%f Hz)", |
356 | wlr_output->name, oc->width, oc->height, oc->refresh_rate); | 404 | wlr_output->name, oc->width, oc->height, oc->refresh_rate); |
357 | set_mode(wlr_output, oc->width, oc->height, | 405 | set_mode(wlr_output, pending, oc->width, oc->height, |
358 | oc->refresh_rate, oc->custom_mode == 1); | 406 | oc->refresh_rate, oc->custom_mode == 1); |
359 | } else if (!wl_list_empty(&wlr_output->modes)) { | 407 | } else if (!wl_list_empty(&wlr_output->modes)) { |
360 | struct wlr_output_mode *mode = wlr_output_preferred_mode(wlr_output); | 408 | sway_log(SWAY_DEBUG, "Set preferred mode"); |
361 | wlr_output_set_mode(wlr_output, mode); | 409 | struct wlr_output_mode *preferred_mode = |
410 | wlr_output_preferred_mode(wlr_output); | ||
411 | wlr_output_state_set_mode(pending, preferred_mode); | ||
412 | |||
413 | if (!wlr_output_test_state(wlr_output, pending)) { | ||
414 | sway_log(SWAY_DEBUG, "Preferred mode rejected, " | ||
415 | "falling back to another mode"); | ||
416 | struct wlr_output_mode *mode; | ||
417 | wl_list_for_each(mode, &wlr_output->modes, link) { | ||
418 | if (mode == preferred_mode) { | ||
419 | continue; | ||
420 | } | ||
421 | |||
422 | wlr_output_state_set_mode(pending, mode); | ||
423 | if (wlr_output_test_state(wlr_output, pending)) { | ||
424 | break; | ||
425 | } | ||
426 | } | ||
427 | } | ||
362 | } | 428 | } |
363 | 429 | ||
364 | if (oc && (oc->subpixel != WL_OUTPUT_SUBPIXEL_UNKNOWN || config->reloading)) { | 430 | if (oc && (oc->subpixel != WL_OUTPUT_SUBPIXEL_UNKNOWN || config->reloading)) { |
365 | sway_log(SWAY_DEBUG, "Set %s subpixel to %s", oc->name, | 431 | sway_log(SWAY_DEBUG, "Set %s subpixel to %s", oc->name, |
366 | sway_wl_output_subpixel_to_string(oc->subpixel)); | 432 | sway_wl_output_subpixel_to_string(oc->subpixel)); |
367 | wlr_output_set_subpixel(wlr_output, oc->subpixel); | 433 | wlr_output_state_set_subpixel(pending, oc->subpixel); |
368 | } | 434 | } |
369 | 435 | ||
436 | enum wl_output_transform tr = WL_OUTPUT_TRANSFORM_NORMAL; | ||
370 | if (oc && oc->transform >= 0) { | 437 | if (oc && oc->transform >= 0) { |
371 | sway_log(SWAY_DEBUG, "Set %s transform to %d", oc->name, oc->transform); | 438 | tr = oc->transform; |
372 | wlr_output_set_transform(wlr_output, oc->transform); | 439 | } else if (wlr_output_is_drm(wlr_output)) { |
440 | tr = wlr_drm_connector_get_panel_orientation(wlr_output); | ||
441 | sway_log(SWAY_DEBUG, "Auto-detected output transform: %d", tr); | ||
442 | } | ||
443 | if (wlr_output->transform != tr) { | ||
444 | sway_log(SWAY_DEBUG, "Set %s transform to %d", oc->name, tr); | ||
445 | wlr_output_state_set_transform(pending, tr); | ||
373 | } | 446 | } |
374 | 447 | ||
375 | // Apply the scale last before the commit, because the scale auto-detection | 448 | // Apply the scale last before the commit, because the scale auto-detection |
@@ -378,23 +451,39 @@ static void queue_output_config(struct output_config *oc, | |||
378 | if (oc && oc->scale > 0) { | 451 | if (oc && oc->scale > 0) { |
379 | scale = oc->scale; | 452 | scale = oc->scale; |
380 | } else { | 453 | } else { |
381 | scale = compute_default_scale(wlr_output); | 454 | scale = compute_default_scale(wlr_output, pending); |
382 | sway_log(SWAY_DEBUG, "Auto-detected output scale: %f", scale); | 455 | sway_log(SWAY_DEBUG, "Auto-detected output scale: %f", scale); |
383 | } | 456 | } |
384 | if (scale != wlr_output->scale) { | 457 | if (scale != wlr_output->scale) { |
385 | sway_log(SWAY_DEBUG, "Set %s scale to %f", wlr_output->name, scale); | 458 | sway_log(SWAY_DEBUG, "Set %s scale to %f", wlr_output->name, scale); |
386 | wlr_output_set_scale(wlr_output, scale); | 459 | wlr_output_state_set_scale(pending, scale); |
387 | } | 460 | } |
388 | 461 | ||
389 | if (oc && oc->adaptive_sync != -1) { | 462 | if (oc && oc->adaptive_sync != -1) { |
390 | sway_log(SWAY_DEBUG, "Set %s adaptive sync to %d", wlr_output->name, | 463 | sway_log(SWAY_DEBUG, "Set %s adaptive sync to %d", wlr_output->name, |
391 | oc->adaptive_sync); | 464 | oc->adaptive_sync); |
392 | wlr_output_enable_adaptive_sync(wlr_output, oc->adaptive_sync == 1); | 465 | wlr_output_state_set_adaptive_sync_enabled(pending, oc->adaptive_sync == 1); |
466 | } | ||
467 | |||
468 | if (oc && oc->render_bit_depth != RENDER_BIT_DEPTH_DEFAULT) { | ||
469 | const uint32_t *fmts = bit_depth_preferences[oc->render_bit_depth]; | ||
470 | assert(fmts); | ||
471 | |||
472 | for (size_t i = 0; fmts[i] != DRM_FORMAT_INVALID; i++) { | ||
473 | wlr_output_state_set_render_format(pending, fmts[i]); | ||
474 | if (wlr_output_test_state(wlr_output, pending)) { | ||
475 | break; | ||
476 | } | ||
477 | |||
478 | sway_log(SWAY_DEBUG, "Preferred output format 0x%08x " | ||
479 | "failed to work, falling back to next in " | ||
480 | "list, 0x%08x", fmts[i], fmts[i + 1]); | ||
481 | } | ||
393 | } | 482 | } |
394 | } | 483 | } |
395 | 484 | ||
396 | bool apply_output_config(struct output_config *oc, struct sway_output *output) { | 485 | bool apply_output_config(struct output_config *oc, struct sway_output *output) { |
397 | if (output == root->noop_output) { | 486 | if (output == root->fallback_output) { |
398 | return false; | 487 | return false; |
399 | } | 488 | } |
400 | 489 | ||
@@ -403,14 +492,15 @@ bool apply_output_config(struct output_config *oc, struct sway_output *output) { | |||
403 | // Flag to prevent the output mode event handler from calling us | 492 | // Flag to prevent the output mode event handler from calling us |
404 | output->enabling = (!oc || oc->enabled); | 493 | output->enabling = (!oc || oc->enabled); |
405 | 494 | ||
406 | queue_output_config(oc, output); | 495 | struct wlr_output_state pending = {0}; |
496 | queue_output_config(oc, output, &pending); | ||
407 | 497 | ||
408 | if (!oc || oc->dpms_state != DPMS_OFF) { | 498 | if (!oc || oc->power != 0) { |
409 | output->current_mode = wlr_output->pending.mode; | 499 | output->current_mode = pending.mode; |
410 | } | 500 | } |
411 | 501 | ||
412 | sway_log(SWAY_DEBUG, "Committing output %s", wlr_output->name); | 502 | sway_log(SWAY_DEBUG, "Committing output %s", wlr_output->name); |
413 | if (!wlr_output_commit(wlr_output)) { | 503 | if (!wlr_output_commit_state(wlr_output, &pending)) { |
414 | // Failed to commit output changes, maybe the output is missing a CRTC. | 504 | // Failed to commit output changes, maybe the output is missing a CRTC. |
415 | // Leave the output disabled for now and try again when the output gets | 505 | // Leave the output disabled for now and try again when the output gets |
416 | // the mode we asked for. | 506 | // the mode we asked for. |
@@ -462,12 +552,12 @@ bool apply_output_config(struct output_config *oc, struct sway_output *output) { | |||
462 | } | 552 | } |
463 | 553 | ||
464 | // Update output->{lx, ly, width, height} | 554 | // Update output->{lx, ly, width, height} |
465 | struct wlr_box *output_box = | 555 | struct wlr_box output_box; |
466 | wlr_output_layout_get_box(root->output_layout, wlr_output); | 556 | wlr_output_layout_get_box(root->output_layout, wlr_output, &output_box); |
467 | output->lx = output_box->x; | 557 | output->lx = output_box.x; |
468 | output->ly = output_box->y; | 558 | output->ly = output_box.y; |
469 | output->width = output_box->width; | 559 | output->width = output_box.width; |
470 | output->height = output_box->height; | 560 | output->height = output_box.height; |
471 | 561 | ||
472 | if (!output->enabled) { | 562 | if (!output->enabled) { |
473 | output_enable(output); | 563 | output_enable(output); |
@@ -483,23 +573,25 @@ bool apply_output_config(struct output_config *oc, struct sway_output *output) { | |||
483 | // this output came online, and some config items (like map_to_output) are | 573 | // this output came online, and some config items (like map_to_output) are |
484 | // dependent on an output being present. | 574 | // dependent on an output being present. |
485 | input_manager_configure_all_inputs(); | 575 | input_manager_configure_all_inputs(); |
576 | // Reconfigure the cursor images, since the scale may have changed. | ||
577 | input_manager_configure_xcursor(); | ||
486 | return true; | 578 | return true; |
487 | } | 579 | } |
488 | 580 | ||
489 | bool test_output_config(struct output_config *oc, struct sway_output *output) { | 581 | bool test_output_config(struct output_config *oc, struct sway_output *output) { |
490 | if (output == root->noop_output) { | 582 | if (output == root->fallback_output) { |
491 | return false; | 583 | return false; |
492 | } | 584 | } |
493 | 585 | ||
494 | queue_output_config(oc, output); | 586 | struct wlr_output_state pending = {0}; |
495 | bool ok = wlr_output_test(output->wlr_output); | 587 | queue_output_config(oc, output, &pending); |
496 | wlr_output_rollback(output->wlr_output); | 588 | return wlr_output_test_state(output->wlr_output, &pending); |
497 | return ok; | ||
498 | } | 589 | } |
499 | 590 | ||
500 | static void default_output_config(struct output_config *oc, | 591 | static void default_output_config(struct output_config *oc, |
501 | struct wlr_output *wlr_output) { | 592 | struct wlr_output *wlr_output) { |
502 | oc->enabled = 1; | 593 | oc->enabled = 1; |
594 | oc->power = 1; | ||
503 | struct wlr_output_mode *mode = wlr_output_preferred_mode(wlr_output); | 595 | struct wlr_output_mode *mode = wlr_output_preferred_mode(wlr_output); |
504 | if (mode != NULL) { | 596 | if (mode != NULL) { |
505 | oc->width = mode->width; | 597 | oc->width = mode->width; |
@@ -512,7 +604,6 @@ static void default_output_config(struct output_config *oc, | |||
512 | struct sway_output *output = wlr_output->data; | 604 | struct sway_output *output = wlr_output->data; |
513 | oc->subpixel = output->detected_subpixel; | 605 | oc->subpixel = output->detected_subpixel; |
514 | oc->transform = WL_OUTPUT_TRANSFORM_NORMAL; | 606 | oc->transform = WL_OUTPUT_TRANSFORM_NORMAL; |
515 | oc->dpms_state = DPMS_ON; | ||
516 | oc->max_render_time = 0; | 607 | oc->max_render_time = 0; |
517 | } | 608 | } |
518 | 609 | ||
@@ -567,10 +658,10 @@ static struct output_config *get_output_config(char *identifier, | |||
567 | 658 | ||
568 | sway_log(SWAY_DEBUG, "Generated output config \"%s\" (enabled: %d)" | 659 | sway_log(SWAY_DEBUG, "Generated output config \"%s\" (enabled: %d)" |
569 | " (%dx%d@%fHz position %d,%d scale %f transform %d) (bg %s %s)" | 660 | " (%dx%d@%fHz position %d,%d scale %f transform %d) (bg %s %s)" |
570 | " (dpms %d) (max render time: %d)", result->name, result->enabled, | 661 | " (power %d) (max render time: %d)", result->name, result->enabled, |
571 | result->width, result->height, result->refresh_rate, | 662 | result->width, result->height, result->refresh_rate, |
572 | result->x, result->y, result->scale, result->transform, | 663 | result->x, result->y, result->scale, result->transform, |
573 | result->background, result->background_option, result->dpms_state, | 664 | result->background, result->background_option, result->power, |
574 | result->max_render_time); | 665 | result->max_render_time); |
575 | } else if (oc_name) { | 666 | } else if (oc_name) { |
576 | // No identifier config, just return a copy of the name config | 667 | // No identifier config, just return a copy of the name config |
@@ -702,6 +793,8 @@ static bool _spawn_swaybg(char **command) { | |||
702 | sway_log_errno(SWAY_ERROR, "fork failed"); | 793 | sway_log_errno(SWAY_ERROR, "fork failed"); |
703 | return false; | 794 | return false; |
704 | } else if (pid == 0) { | 795 | } else if (pid == 0) { |
796 | restore_nofile_limit(); | ||
797 | |||
705 | pid = fork(); | 798 | pid = fork(); |
706 | if (pid < 0) { | 799 | if (pid < 0) { |
707 | sway_log_errno(SWAY_ERROR, "fork failed"); | 800 | sway_log_errno(SWAY_ERROR, "fork failed"); |
diff --git a/sway/criteria.c b/sway/criteria.c index 409160c5..97cf667e 100644 --- a/sway/criteria.c +++ b/sway/criteria.c | |||
@@ -3,7 +3,8 @@ | |||
3 | #include <stdio.h> | 3 | #include <stdio.h> |
4 | #include <stdbool.h> | 4 | #include <stdbool.h> |
5 | #include <strings.h> | 5 | #include <strings.h> |
6 | #include <pcre.h> | 6 | #define PCRE2_CODE_UNIT_WIDTH 8 |
7 | #include <pcre2.h> | ||
7 | #include "sway/criteria.h" | 8 | #include "sway/criteria.h" |
8 | #include "sway/tree/container.h" | 9 | #include "sway/tree/container.h" |
9 | #include "sway/config.h" | 10 | #include "sway/config.h" |
@@ -40,17 +41,19 @@ bool criteria_is_empty(struct criteria *criteria) { | |||
40 | char *error = NULL; | 41 | char *error = NULL; |
41 | 42 | ||
42 | // Returns error string on failure or NULL otherwise. | 43 | // Returns error string on failure or NULL otherwise. |
43 | static bool generate_regex(pcre **regex, char *value) { | 44 | static bool generate_regex(pcre2_code **regex, char *value) { |
44 | const char *reg_err; | 45 | int errorcode; |
45 | int offset; | 46 | PCRE2_SIZE offset; |
46 | |||
47 | *regex = pcre_compile(value, PCRE_UTF8 | PCRE_UCP, ®_err, &offset, NULL); | ||
48 | 47 | ||
48 | *regex = pcre2_compile((PCRE2_SPTR)value, PCRE2_ZERO_TERMINATED, PCRE2_UTF | PCRE2_UCP, &errorcode, &offset, NULL); | ||
49 | if (!*regex) { | 49 | if (!*regex) { |
50 | PCRE2_UCHAR buffer[256]; | ||
51 | pcre2_get_error_message(errorcode, buffer, sizeof(buffer)); | ||
52 | |||
50 | const char *fmt = "Regex compilation for '%s' failed: %s"; | 53 | const char *fmt = "Regex compilation for '%s' failed: %s"; |
51 | int len = strlen(fmt) + strlen(value) + strlen(reg_err) - 3; | 54 | int len = strlen(fmt) + strlen(value) + strlen((char*) buffer) - 3; |
52 | error = malloc(len); | 55 | error = malloc(len); |
53 | snprintf(error, len, fmt, value, reg_err); | 56 | snprintf(error, len, fmt, value, buffer); |
54 | return false; | 57 | return false; |
55 | } | 58 | } |
56 | 59 | ||
@@ -66,7 +69,7 @@ static bool pattern_create(struct pattern **pattern, char *value) { | |||
66 | if (strcmp(value, "__focused__") == 0) { | 69 | if (strcmp(value, "__focused__") == 0) { |
67 | (*pattern)->match_type = PATTERN_FOCUSED; | 70 | (*pattern)->match_type = PATTERN_FOCUSED; |
68 | } else { | 71 | } else { |
69 | (*pattern)->match_type = PATTERN_PCRE; | 72 | (*pattern)->match_type = PATTERN_PCRE2; |
70 | if (!generate_regex(&(*pattern)->regex, value)) { | 73 | if (!generate_regex(&(*pattern)->regex, value)) { |
71 | return false; | 74 | return false; |
72 | }; | 75 | }; |
@@ -77,7 +80,7 @@ static bool pattern_create(struct pattern **pattern, char *value) { | |||
77 | static void pattern_destroy(struct pattern *pattern) { | 80 | static void pattern_destroy(struct pattern *pattern) { |
78 | if (pattern) { | 81 | if (pattern) { |
79 | if (pattern->regex) { | 82 | if (pattern->regex) { |
80 | pcre_free(pattern->regex); | 83 | pcre2_code_free(pattern->regex); |
81 | } | 84 | } |
82 | free(pattern); | 85 | free(pattern); |
83 | } | 86 | } |
@@ -99,8 +102,11 @@ void criteria_destroy(struct criteria *criteria) { | |||
99 | free(criteria); | 102 | free(criteria); |
100 | } | 103 | } |
101 | 104 | ||
102 | static int regex_cmp(const char *item, const pcre *regex) { | 105 | static int regex_cmp(const char *item, const pcre2_code *regex) { |
103 | return pcre_exec(regex, NULL, item, strlen(item), 0, 0, NULL, 0); | 106 | pcre2_match_data *match_data = pcre2_match_data_create_from_pattern(regex, NULL); |
107 | int result = pcre2_match(regex, (PCRE2_SPTR)item, strlen(item), 0, 0, match_data, NULL); | ||
108 | pcre2_match_data_free(match_data); | ||
109 | return result; | ||
104 | } | 110 | } |
105 | 111 | ||
106 | #if HAVE_XWAYLAND | 112 | #if HAVE_XWAYLAND |
@@ -155,7 +161,7 @@ static bool criteria_matches_container(struct criteria *criteria, | |||
155 | bool exists = false; | 161 | bool exists = false; |
156 | struct sway_container *con = container; | 162 | struct sway_container *con = container; |
157 | for (int i = 0; i < con->marks->length; ++i) { | 163 | for (int i = 0; i < con->marks->length; ++i) { |
158 | if (regex_cmp(con->marks->items[i], criteria->con_mark->regex) == 0) { | 164 | if (regex_cmp(con->marks->items[i], criteria->con_mark->regex) >= 0) { |
159 | exists = true; | 165 | exists = true; |
160 | break; | 166 | break; |
161 | } | 167 | } |
@@ -192,8 +198,8 @@ static bool criteria_matches_view(struct criteria *criteria, | |||
192 | return false; | 198 | return false; |
193 | } | 199 | } |
194 | break; | 200 | break; |
195 | case PATTERN_PCRE: | 201 | case PATTERN_PCRE2: |
196 | if (regex_cmp(title, criteria->title->regex) != 0) { | 202 | if (regex_cmp(title, criteria->title->regex) < 0) { |
197 | return false; | 203 | return false; |
198 | } | 204 | } |
199 | break; | 205 | break; |
@@ -212,8 +218,8 @@ static bool criteria_matches_view(struct criteria *criteria, | |||
212 | return false; | 218 | return false; |
213 | } | 219 | } |
214 | break; | 220 | break; |
215 | case PATTERN_PCRE: | 221 | case PATTERN_PCRE2: |
216 | if (regex_cmp(shell, criteria->shell->regex) != 0) { | 222 | if (regex_cmp(shell, criteria->shell->regex) < 0) { |
217 | return false; | 223 | return false; |
218 | } | 224 | } |
219 | break; | 225 | break; |
@@ -232,8 +238,8 @@ static bool criteria_matches_view(struct criteria *criteria, | |||
232 | return false; | 238 | return false; |
233 | } | 239 | } |
234 | break; | 240 | break; |
235 | case PATTERN_PCRE: | 241 | case PATTERN_PCRE2: |
236 | if (regex_cmp(app_id, criteria->app_id->regex) != 0) { | 242 | if (regex_cmp(app_id, criteria->app_id->regex) < 0) { |
237 | return false; | 243 | return false; |
238 | } | 244 | } |
239 | break; | 245 | break; |
@@ -264,8 +270,8 @@ static bool criteria_matches_view(struct criteria *criteria, | |||
264 | return false; | 270 | return false; |
265 | } | 271 | } |
266 | break; | 272 | break; |
267 | case PATTERN_PCRE: | 273 | case PATTERN_PCRE2: |
268 | if (regex_cmp(class, criteria->class->regex) != 0) { | 274 | if (regex_cmp(class, criteria->class->regex) < 0) { |
269 | return false; | 275 | return false; |
270 | } | 276 | } |
271 | break; | 277 | break; |
@@ -284,8 +290,8 @@ static bool criteria_matches_view(struct criteria *criteria, | |||
284 | return false; | 290 | return false; |
285 | } | 291 | } |
286 | break; | 292 | break; |
287 | case PATTERN_PCRE: | 293 | case PATTERN_PCRE2: |
288 | if (regex_cmp(instance, criteria->instance->regex) != 0) { | 294 | if (regex_cmp(instance, criteria->instance->regex) < 0) { |
289 | return false; | 295 | return false; |
290 | } | 296 | } |
291 | break; | 297 | break; |
@@ -304,8 +310,8 @@ static bool criteria_matches_view(struct criteria *criteria, | |||
304 | return false; | 310 | return false; |
305 | } | 311 | } |
306 | break; | 312 | break; |
307 | case PATTERN_PCRE: | 313 | case PATTERN_PCRE2: |
308 | if (regex_cmp(window_role, criteria->window_role->regex) != 0) { | 314 | if (regex_cmp(window_role, criteria->window_role->regex) < 0) { |
309 | return false; | 315 | return false; |
310 | } | 316 | } |
311 | break; | 317 | break; |
@@ -351,7 +357,7 @@ static bool criteria_matches_view(struct criteria *criteria, | |||
351 | } | 357 | } |
352 | 358 | ||
353 | if (criteria->workspace) { | 359 | if (criteria->workspace) { |
354 | struct sway_workspace *ws = view->container->workspace; | 360 | struct sway_workspace *ws = view->container->pending.workspace; |
355 | if (!ws) { | 361 | if (!ws) { |
356 | return false; | 362 | return false; |
357 | } | 363 | } |
@@ -359,12 +365,12 @@ static bool criteria_matches_view(struct criteria *criteria, | |||
359 | switch (criteria->workspace->match_type) { | 365 | switch (criteria->workspace->match_type) { |
360 | case PATTERN_FOCUSED: | 366 | case PATTERN_FOCUSED: |
361 | if (focused && | 367 | if (focused && |
362 | strcmp(ws->name, focused->container->workspace->name)) { | 368 | strcmp(ws->name, focused->container->pending.workspace->name)) { |
363 | return false; | 369 | return false; |
364 | } | 370 | } |
365 | break; | 371 | break; |
366 | case PATTERN_PCRE: | 372 | case PATTERN_PCRE2: |
367 | if (regex_cmp(ws->name, criteria->workspace->regex) != 0) { | 373 | if (regex_cmp(ws->name, criteria->workspace->regex) < 0) { |
368 | return false; | 374 | return false; |
369 | } | 375 | } |
370 | break; | 376 | break; |
@@ -676,7 +682,7 @@ struct criteria *criteria_parse(char *raw, char **error_arg) { | |||
676 | } | 682 | } |
677 | name = calloc(head - namestart + 1, 1); | 683 | name = calloc(head - namestart + 1, 1); |
678 | if (head != namestart) { | 684 | if (head != namestart) { |
679 | strncpy(name, namestart, head - namestart); | 685 | memcpy(name, namestart, head - namestart); |
680 | } | 686 | } |
681 | // Parse token value | 687 | // Parse token value |
682 | skip_spaces(&head); | 688 | skip_spaces(&head); |
@@ -703,7 +709,7 @@ struct criteria *criteria_parse(char *raw, char **error_arg) { | |||
703 | } | 709 | } |
704 | } | 710 | } |
705 | value = calloc(head - valuestart + 1, 1); | 711 | value = calloc(head - valuestart + 1, 1); |
706 | strncpy(value, valuestart, head - valuestart); | 712 | memcpy(value, valuestart, head - valuestart); |
707 | if (in_quotes) { | 713 | if (in_quotes) { |
708 | ++head; | 714 | ++head; |
709 | in_quotes = false; | 715 | in_quotes = false; |
@@ -734,7 +740,7 @@ struct criteria *criteria_parse(char *raw, char **error_arg) { | |||
734 | ++head; | 740 | ++head; |
735 | int len = head - raw; | 741 | int len = head - raw; |
736 | criteria->raw = calloc(len + 1, 1); | 742 | criteria->raw = calloc(len + 1, 1); |
737 | strncpy(criteria->raw, raw, len); | 743 | memcpy(criteria->raw, raw, len); |
738 | return criteria; | 744 | return criteria; |
739 | 745 | ||
740 | cleanup: | 746 | cleanup: |
diff --git a/sway/desktop/desktop.c b/sway/desktop/desktop.c index ec45d80a..c8d4502c 100644 --- a/sway/desktop/desktop.c +++ b/sway/desktop/desktop.c | |||
@@ -6,10 +6,11 @@ void desktop_damage_surface(struct wlr_surface *surface, double lx, double ly, | |||
6 | bool whole) { | 6 | bool whole) { |
7 | for (int i = 0; i < root->outputs->length; ++i) { | 7 | for (int i = 0; i < root->outputs->length; ++i) { |
8 | struct sway_output *output = root->outputs->items[i]; | 8 | struct sway_output *output = root->outputs->items[i]; |
9 | struct wlr_box *output_box = wlr_output_layout_get_box( | 9 | struct wlr_box output_box; |
10 | root->output_layout, output->wlr_output); | 10 | wlr_output_layout_get_box(root->output_layout, |
11 | output_damage_surface(output, lx - output_box->x, | 11 | output->wlr_output, &output_box); |
12 | ly - output_box->y, surface, whole); | 12 | output_damage_surface(output, lx - output_box.x, |
13 | ly - output_box.y, surface, whole); | ||
13 | } | 14 | } |
14 | } | 15 | } |
15 | 16 | ||
diff --git a/sway/desktop/idle_inhibit_v1.c b/sway/desktop/idle_inhibit_v1.c index a5cfd5b2..82353038 100644 --- a/sway/desktop/idle_inhibit_v1.c +++ b/sway/desktop/idle_inhibit_v1.c | |||
@@ -36,7 +36,7 @@ void handle_idle_inhibitor_v1(struct wl_listener *listener, void *data) { | |||
36 | 36 | ||
37 | inhibitor->manager = manager; | 37 | inhibitor->manager = manager; |
38 | inhibitor->mode = INHIBIT_IDLE_APPLICATION; | 38 | inhibitor->mode = INHIBIT_IDLE_APPLICATION; |
39 | inhibitor->view = view_from_wlr_surface(wlr_inhibitor->surface); | 39 | inhibitor->wlr_inhibitor = wlr_inhibitor; |
40 | wl_list_insert(&manager->inhibitors, &inhibitor->link); | 40 | wl_list_insert(&manager->inhibitors, &inhibitor->link); |
41 | 41 | ||
42 | inhibitor->destroy.notify = handle_destroy; | 42 | inhibitor->destroy.notify = handle_destroy; |
@@ -69,8 +69,8 @@ struct sway_idle_inhibitor_v1 *sway_idle_inhibit_v1_user_inhibitor_for_view( | |||
69 | struct sway_idle_inhibitor_v1 *inhibitor; | 69 | struct sway_idle_inhibitor_v1 *inhibitor; |
70 | wl_list_for_each(inhibitor, &server.idle_inhibit_manager_v1->inhibitors, | 70 | wl_list_for_each(inhibitor, &server.idle_inhibit_manager_v1->inhibitors, |
71 | link) { | 71 | link) { |
72 | if (inhibitor->view == view && | 72 | if (inhibitor->mode != INHIBIT_IDLE_APPLICATION && |
73 | inhibitor->mode != INHIBIT_IDLE_APPLICATION) { | 73 | inhibitor->view == view) { |
74 | return inhibitor; | 74 | return inhibitor; |
75 | } | 75 | } |
76 | } | 76 | } |
@@ -82,8 +82,8 @@ struct sway_idle_inhibitor_v1 *sway_idle_inhibit_v1_application_inhibitor_for_vi | |||
82 | struct sway_idle_inhibitor_v1 *inhibitor; | 82 | struct sway_idle_inhibitor_v1 *inhibitor; |
83 | wl_list_for_each(inhibitor, &server.idle_inhibit_manager_v1->inhibitors, | 83 | wl_list_for_each(inhibitor, &server.idle_inhibit_manager_v1->inhibitors, |
84 | link) { | 84 | link) { |
85 | if (inhibitor->view == view && | 85 | if (inhibitor->mode == INHIBIT_IDLE_APPLICATION && |
86 | inhibitor->mode == INHIBIT_IDLE_APPLICATION) { | 86 | view_from_wlr_surface(inhibitor->wlr_inhibitor->surface) == view) { |
87 | return inhibitor; | 87 | return inhibitor; |
88 | } | 88 | } |
89 | } | 89 | } |
@@ -104,10 +104,10 @@ void sway_idle_inhibit_v1_user_inhibitor_destroy( | |||
104 | 104 | ||
105 | bool sway_idle_inhibit_v1_is_active(struct sway_idle_inhibitor_v1 *inhibitor) { | 105 | bool sway_idle_inhibit_v1_is_active(struct sway_idle_inhibitor_v1 *inhibitor) { |
106 | switch (inhibitor->mode) { | 106 | switch (inhibitor->mode) { |
107 | case INHIBIT_IDLE_APPLICATION: | 107 | case INHIBIT_IDLE_APPLICATION:; |
108 | // If there is no view associated with the inhibitor, assume visible | 108 | // If there is no view associated with the inhibitor, assume visible |
109 | return !inhibitor->view || !inhibitor->view->container || | 109 | struct sway_view *view = view_from_wlr_surface(inhibitor->wlr_inhibitor->surface); |
110 | view_is_visible(inhibitor->view); | 110 | return !view || !view->container || view_is_visible(view); |
111 | case INHIBIT_IDLE_FOCUS:; | 111 | case INHIBIT_IDLE_FOCUS:; |
112 | struct sway_seat *seat = NULL; | 112 | struct sway_seat *seat = NULL; |
113 | wl_list_for_each(seat, &server.input->seats, link) { | 113 | wl_list_for_each(seat, &server.input->seats, link) { |
diff --git a/sway/desktop/layer_shell.c b/sway/desktop/layer_shell.c index d4ca4fb4..d44d6338 100644 --- a/sway/desktop/layer_shell.c +++ b/sway/desktop/layer_shell.c | |||
@@ -2,10 +2,10 @@ | |||
2 | #include <stdlib.h> | 2 | #include <stdlib.h> |
3 | #include <string.h> | 3 | #include <string.h> |
4 | #include <wayland-server-core.h> | 4 | #include <wayland-server-core.h> |
5 | #include <wlr/types/wlr_box.h> | ||
6 | #include <wlr/types/wlr_layer_shell_v1.h> | 5 | #include <wlr/types/wlr_layer_shell_v1.h> |
7 | #include <wlr/types/wlr_output_damage.h> | 6 | #include <wlr/types/wlr_output_damage.h> |
8 | #include <wlr/types/wlr_output.h> | 7 | #include <wlr/types/wlr_output.h> |
8 | #include <wlr/types/wlr_subcompositor.h> | ||
9 | #include "log.h" | 9 | #include "log.h" |
10 | #include "sway/desktop/transaction.h" | 10 | #include "sway/desktop/transaction.h" |
11 | #include "sway/input/cursor.h" | 11 | #include "sway/input/cursor.h" |
@@ -115,9 +115,10 @@ static void arrange_layer(struct sway_output *output, struct wl_list *list, | |||
115 | // Horizontal axis | 115 | // Horizontal axis |
116 | const uint32_t both_horiz = ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | 116 | const uint32_t both_horiz = ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT |
117 | | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT; | 117 | | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT; |
118 | if ((state->anchor & both_horiz) && box.width == 0) { | 118 | if (box.width == 0) { |
119 | box.x = bounds.x; | 119 | box.x = bounds.x; |
120 | box.width = bounds.width; | 120 | } else if ((state->anchor & both_horiz) == both_horiz) { |
121 | box.x = bounds.x + ((bounds.width / 2) - (box.width / 2)); | ||
121 | } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT)) { | 122 | } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT)) { |
122 | box.x = bounds.x; | 123 | box.x = bounds.x; |
123 | } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT)) { | 124 | } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT)) { |
@@ -128,9 +129,10 @@ static void arrange_layer(struct sway_output *output, struct wl_list *list, | |||
128 | // Vertical axis | 129 | // Vertical axis |
129 | const uint32_t both_vert = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | 130 | const uint32_t both_vert = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP |
130 | | ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM; | 131 | | ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM; |
131 | if ((state->anchor & both_vert) && box.height == 0) { | 132 | if (box.height == 0) { |
132 | box.y = bounds.y; | 133 | box.y = bounds.y; |
133 | box.height = bounds.height; | 134 | } else if ((state->anchor & both_vert) == both_vert) { |
135 | box.y = bounds.y + ((bounds.height / 2) - (box.height / 2)); | ||
134 | } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP)) { | 136 | } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP)) { |
135 | box.y = bounds.y; | 137 | box.y = bounds.y; |
136 | } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM)) { | 138 | } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM)) { |
@@ -139,25 +141,30 @@ static void arrange_layer(struct sway_output *output, struct wl_list *list, | |||
139 | box.y = bounds.y + ((bounds.height / 2) - (box.height / 2)); | 141 | box.y = bounds.y + ((bounds.height / 2) - (box.height / 2)); |
140 | } | 142 | } |
141 | // Margin | 143 | // Margin |
142 | if ((state->anchor & both_horiz) == both_horiz) { | 144 | if (box.width == 0) { |
143 | box.x += state->margin.left; | 145 | box.x += state->margin.left; |
144 | box.width -= state->margin.left + state->margin.right; | 146 | box.width = bounds.width - |
147 | (state->margin.left + state->margin.right); | ||
148 | } else if ((state->anchor & both_horiz) == both_horiz) { | ||
149 | // don't apply margins | ||
145 | } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT)) { | 150 | } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT)) { |
146 | box.x += state->margin.left; | 151 | box.x += state->margin.left; |
147 | } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT)) { | 152 | } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT)) { |
148 | box.x -= state->margin.right; | 153 | box.x -= state->margin.right; |
149 | } | 154 | } |
150 | if ((state->anchor & both_vert) == both_vert) { | 155 | if (box.height == 0) { |
151 | box.y += state->margin.top; | 156 | box.y += state->margin.top; |
152 | box.height -= state->margin.top + state->margin.bottom; | 157 | box.height = bounds.height - |
158 | (state->margin.top + state->margin.bottom); | ||
159 | } else if ((state->anchor & both_vert) == both_vert) { | ||
160 | // don't apply margins | ||
153 | } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP)) { | 161 | } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP)) { |
154 | box.y += state->margin.top; | 162 | box.y += state->margin.top; |
155 | } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM)) { | 163 | } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM)) { |
156 | box.y -= state->margin.bottom; | 164 | box.y -= state->margin.bottom; |
157 | } | 165 | } |
158 | if (box.width < 0 || box.height < 0) { | 166 | if (!sway_assert(box.width >= 0 && box.height >= 0, |
159 | // TODO: Bubble up a protocol error? | 167 | "Expected layer surface to have positive size")) { |
160 | wlr_layer_surface_v1_close(layer); | ||
161 | continue; | 168 | continue; |
162 | } | 169 | } |
163 | // Apply | 170 | // Apply |
@@ -191,7 +198,7 @@ void arrange_layers(struct sway_output *output) { | |||
191 | arrange_output(output); | 198 | arrange_output(output); |
192 | } | 199 | } |
193 | 200 | ||
194 | // Arrange non-exlusive surfaces from top->bottom | 201 | // Arrange non-exclusive surfaces from top->bottom |
195 | arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], | 202 | arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], |
196 | &usable_area, false); | 203 | &usable_area, false); |
197 | arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP], | 204 | arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP], |
@@ -264,10 +271,6 @@ static void handle_output_destroy(struct wl_listener *listener, void *data) { | |||
264 | wl_resource_get_client(sway_layer->layer_surface->resource); | 271 | wl_resource_get_client(sway_layer->layer_surface->resource); |
265 | bool set_focus = seat->exclusive_client == client; | 272 | bool set_focus = seat->exclusive_client == client; |
266 | 273 | ||
267 | wl_list_remove(&sway_layer->output_destroy.link); | ||
268 | wl_list_remove(&sway_layer->link); | ||
269 | wl_list_init(&sway_layer->link); | ||
270 | |||
271 | if (set_focus) { | 274 | if (set_focus) { |
272 | struct sway_layer_surface *layer = | 275 | struct sway_layer_surface *layer = |
273 | find_mapped_layer_by_client(client, sway_layer->layer_surface->output); | 276 | find_mapped_layer_by_client(client, sway_layer->layer_surface->output); |
@@ -276,8 +279,7 @@ static void handle_output_destroy(struct wl_listener *listener, void *data) { | |||
276 | } | 279 | } |
277 | } | 280 | } |
278 | 281 | ||
279 | sway_layer->layer_surface->output = NULL; | 282 | wlr_layer_surface_v1_destroy(sway_layer->layer_surface); |
280 | wlr_layer_surface_v1_close(sway_layer->layer_surface); | ||
281 | } | 283 | } |
282 | 284 | ||
283 | static void handle_surface_commit(struct wl_listener *listener, void *data) { | 285 | static void handle_surface_commit(struct wl_listener *listener, void *data) { |
@@ -285,26 +287,32 @@ static void handle_surface_commit(struct wl_listener *listener, void *data) { | |||
285 | wl_container_of(listener, layer, surface_commit); | 287 | wl_container_of(listener, layer, surface_commit); |
286 | struct wlr_layer_surface_v1 *layer_surface = layer->layer_surface; | 288 | struct wlr_layer_surface_v1 *layer_surface = layer->layer_surface; |
287 | struct wlr_output *wlr_output = layer_surface->output; | 289 | struct wlr_output *wlr_output = layer_surface->output; |
288 | if (wlr_output == NULL) { | 290 | sway_assert(wlr_output, "wlr_layer_surface_v1 has null output"); |
289 | return; | 291 | struct sway_output *output = wlr_output->data; |
292 | struct wlr_box old_extent = layer->extent; | ||
293 | |||
294 | bool layer_changed = false; | ||
295 | if (layer_surface->current.committed != 0 | ||
296 | || layer->mapped != layer_surface->mapped) { | ||
297 | layer->mapped = layer_surface->mapped; | ||
298 | layer_changed = layer->layer != layer_surface->current.layer; | ||
299 | if (layer_changed) { | ||
300 | wl_list_remove(&layer->link); | ||
301 | wl_list_insert(&output->layers[layer_surface->current.layer], | ||
302 | &layer->link); | ||
303 | layer->layer = layer_surface->current.layer; | ||
304 | } | ||
305 | arrange_layers(output); | ||
290 | } | 306 | } |
291 | 307 | ||
292 | struct sway_output *output = wlr_output->data; | 308 | wlr_surface_get_extends(layer_surface->surface, &layer->extent); |
293 | struct wlr_box old_geo = layer->geo; | 309 | layer->extent.x += layer->geo.x; |
294 | arrange_layers(output); | 310 | layer->extent.y += layer->geo.y; |
295 | 311 | ||
296 | bool geo_changed = | 312 | bool extent_changed = |
297 | memcmp(&old_geo, &layer->geo, sizeof(struct wlr_box)) != 0; | 313 | memcmp(&old_extent, &layer->extent, sizeof(struct wlr_box)) != 0; |
298 | bool layer_changed = layer->layer != layer_surface->current.layer; | 314 | if (extent_changed || layer_changed) { |
299 | if (layer_changed) { | 315 | output_damage_box(output, &old_extent); |
300 | wl_list_remove(&layer->link); | ||
301 | wl_list_insert(&output->layers[layer_surface->current.layer], | ||
302 | &layer->link); | ||
303 | layer->layer = layer_surface->current.layer; | ||
304 | } | ||
305 | if (geo_changed || layer_changed) { | ||
306 | output_damage_surface(output, old_geo.x, old_geo.y, | ||
307 | layer_surface->surface, true); | ||
308 | output_damage_surface(output, layer->geo.x, layer->geo.y, | 316 | output_damage_surface(output, layer->geo.x, layer->geo.y, |
309 | layer_surface->surface, true); | 317 | layer_surface->surface, true); |
310 | } else { | 318 | } else { |
@@ -326,17 +334,14 @@ static void unmap(struct sway_layer_surface *sway_layer) { | |||
326 | cursor_rebase_all(); | 334 | cursor_rebase_all(); |
327 | 335 | ||
328 | struct wlr_output *wlr_output = sway_layer->layer_surface->output; | 336 | struct wlr_output *wlr_output = sway_layer->layer_surface->output; |
329 | if (wlr_output == NULL) { | 337 | sway_assert(wlr_output, "wlr_layer_surface_v1 has null output"); |
330 | return; | ||
331 | } | ||
332 | struct sway_output *output = wlr_output->data; | 338 | struct sway_output *output = wlr_output->data; |
333 | if (output == NULL) { | ||
334 | return; | ||
335 | } | ||
336 | output_damage_surface(output, sway_layer->geo.x, sway_layer->geo.y, | 339 | output_damage_surface(output, sway_layer->geo.x, sway_layer->geo.y, |
337 | sway_layer->layer_surface->surface, true); | 340 | sway_layer->layer_surface->surface, true); |
338 | } | 341 | } |
339 | 342 | ||
343 | static void layer_subsurface_destroy(struct sway_layer_subsurface *subsurface); | ||
344 | |||
340 | static void handle_destroy(struct wl_listener *listener, void *data) { | 345 | static void handle_destroy(struct wl_listener *listener, void *data) { |
341 | struct sway_layer_surface *sway_layer = | 346 | struct sway_layer_surface *sway_layer = |
342 | wl_container_of(listener, sway_layer, destroy); | 347 | wl_container_of(listener, sway_layer, destroy); |
@@ -345,6 +350,12 @@ static void handle_destroy(struct wl_listener *listener, void *data) { | |||
345 | if (sway_layer->layer_surface->mapped) { | 350 | if (sway_layer->layer_surface->mapped) { |
346 | unmap(sway_layer); | 351 | unmap(sway_layer); |
347 | } | 352 | } |
353 | |||
354 | struct sway_layer_subsurface *subsurface, *subsurface_tmp; | ||
355 | wl_list_for_each_safe(subsurface, subsurface_tmp, &sway_layer->subsurfaces, link) { | ||
356 | layer_subsurface_destroy(subsurface); | ||
357 | } | ||
358 | |||
348 | wl_list_remove(&sway_layer->link); | 359 | wl_list_remove(&sway_layer->link); |
349 | wl_list_remove(&sway_layer->destroy.link); | 360 | wl_list_remove(&sway_layer->destroy.link); |
350 | wl_list_remove(&sway_layer->map.link); | 361 | wl_list_remove(&sway_layer->map.link); |
@@ -352,22 +363,24 @@ static void handle_destroy(struct wl_listener *listener, void *data) { | |||
352 | wl_list_remove(&sway_layer->surface_commit.link); | 363 | wl_list_remove(&sway_layer->surface_commit.link); |
353 | wl_list_remove(&sway_layer->new_popup.link); | 364 | wl_list_remove(&sway_layer->new_popup.link); |
354 | wl_list_remove(&sway_layer->new_subsurface.link); | 365 | wl_list_remove(&sway_layer->new_subsurface.link); |
355 | if (sway_layer->layer_surface->output != NULL) { | 366 | |
356 | struct sway_output *output = sway_layer->layer_surface->output->data; | 367 | struct wlr_output *wlr_output = sway_layer->layer_surface->output; |
357 | if (output != NULL) { | 368 | sway_assert(wlr_output, "wlr_layer_surface_v1 has null output"); |
358 | arrange_layers(output); | 369 | struct sway_output *output = wlr_output->data; |
359 | transaction_commit_dirty(); | 370 | arrange_layers(output); |
360 | } | 371 | transaction_commit_dirty(); |
361 | wl_list_remove(&sway_layer->output_destroy.link); | 372 | wl_list_remove(&sway_layer->output_destroy.link); |
362 | sway_layer->layer_surface->output = NULL; | 373 | sway_layer->layer_surface->output = NULL; |
363 | } | 374 | |
364 | free(sway_layer); | 375 | free(sway_layer); |
365 | } | 376 | } |
366 | 377 | ||
367 | static void handle_map(struct wl_listener *listener, void *data) { | 378 | static void handle_map(struct wl_listener *listener, void *data) { |
368 | struct sway_layer_surface *sway_layer = wl_container_of(listener, | 379 | struct sway_layer_surface *sway_layer = wl_container_of(listener, |
369 | sway_layer, map); | 380 | sway_layer, map); |
370 | struct sway_output *output = sway_layer->layer_surface->output->data; | 381 | struct wlr_output *wlr_output = sway_layer->layer_surface->output; |
382 | sway_assert(wlr_output, "wlr_layer_surface_v1 has null output"); | ||
383 | struct sway_output *output = wlr_output->data; | ||
371 | output_damage_surface(output, sway_layer->geo.x, sway_layer->geo.y, | 384 | output_damage_surface(output, sway_layer->geo.x, sway_layer->geo.y, |
372 | sway_layer->layer_surface->surface, true); | 385 | sway_layer->layer_surface->surface, true); |
373 | wlr_surface_send_enter(sway_layer->layer_surface->surface, | 386 | wlr_surface_send_enter(sway_layer->layer_surface->surface, |
@@ -385,9 +398,7 @@ static void subsurface_damage(struct sway_layer_subsurface *subsurface, | |||
385 | bool whole) { | 398 | bool whole) { |
386 | struct sway_layer_surface *layer = subsurface->layer_surface; | 399 | struct sway_layer_surface *layer = subsurface->layer_surface; |
387 | struct wlr_output *wlr_output = layer->layer_surface->output; | 400 | struct wlr_output *wlr_output = layer->layer_surface->output; |
388 | if (!wlr_output) { | 401 | sway_assert(wlr_output, "wlr_layer_surface_v1 has null output"); |
389 | return; | ||
390 | } | ||
391 | struct sway_output *output = wlr_output->data; | 402 | struct sway_output *output = wlr_output->data; |
392 | int ox = subsurface->wlr_subsurface->current.x + layer->geo.x; | 403 | int ox = subsurface->wlr_subsurface->current.x + layer->geo.x; |
393 | int oy = subsurface->wlr_subsurface->current.y + layer->geo.y; | 404 | int oy = subsurface->wlr_subsurface->current.y + layer->geo.y; |
@@ -413,11 +424,8 @@ static void subsurface_handle_commit(struct wl_listener *listener, void *data) { | |||
413 | subsurface_damage(subsurface, false); | 424 | subsurface_damage(subsurface, false); |
414 | } | 425 | } |
415 | 426 | ||
416 | static void subsurface_handle_destroy(struct wl_listener *listener, | 427 | static void layer_subsurface_destroy(struct sway_layer_subsurface *subsurface) { |
417 | void *data) { | 428 | wl_list_remove(&subsurface->link); |
418 | struct sway_layer_subsurface *subsurface = | ||
419 | wl_container_of(listener, subsurface, destroy); | ||
420 | |||
421 | wl_list_remove(&subsurface->map.link); | 429 | wl_list_remove(&subsurface->map.link); |
422 | wl_list_remove(&subsurface->unmap.link); | 430 | wl_list_remove(&subsurface->unmap.link); |
423 | wl_list_remove(&subsurface->destroy.link); | 431 | wl_list_remove(&subsurface->destroy.link); |
@@ -425,17 +433,25 @@ static void subsurface_handle_destroy(struct wl_listener *listener, | |||
425 | free(subsurface); | 433 | free(subsurface); |
426 | } | 434 | } |
427 | 435 | ||
436 | static void subsurface_handle_destroy(struct wl_listener *listener, | ||
437 | void *data) { | ||
438 | struct sway_layer_subsurface *subsurface = | ||
439 | wl_container_of(listener, subsurface, destroy); | ||
440 | layer_subsurface_destroy(subsurface); | ||
441 | } | ||
442 | |||
428 | static struct sway_layer_subsurface *create_subsurface( | 443 | static struct sway_layer_subsurface *create_subsurface( |
429 | struct wlr_subsurface *wlr_subsurface, | 444 | struct wlr_subsurface *wlr_subsurface, |
430 | struct sway_layer_surface *layer_surface) { | 445 | struct sway_layer_surface *layer_surface) { |
431 | struct sway_layer_subsurface *subsurface = | 446 | struct sway_layer_subsurface *subsurface = |
432 | calloc(1, sizeof(struct sway_layer_surface)); | 447 | calloc(1, sizeof(struct sway_layer_subsurface)); |
433 | if (subsurface == NULL) { | 448 | if (subsurface == NULL) { |
434 | return NULL; | 449 | return NULL; |
435 | } | 450 | } |
436 | 451 | ||
437 | subsurface->wlr_subsurface = wlr_subsurface; | 452 | subsurface->wlr_subsurface = wlr_subsurface; |
438 | subsurface->layer_surface = layer_surface; | 453 | subsurface->layer_surface = layer_surface; |
454 | wl_list_insert(&layer_surface->subsurfaces, &subsurface->link); | ||
439 | 455 | ||
440 | subsurface->map.notify = subsurface_handle_map; | 456 | subsurface->map.notify = subsurface_handle_map; |
441 | wl_signal_add(&wlr_subsurface->events.map, &subsurface->map); | 457 | wl_signal_add(&wlr_subsurface->events.map, &subsurface->map); |
@@ -468,15 +484,15 @@ static struct sway_layer_surface *popup_get_layer( | |||
468 | static void popup_damage(struct sway_layer_popup *layer_popup, bool whole) { | 484 | static void popup_damage(struct sway_layer_popup *layer_popup, bool whole) { |
469 | struct wlr_xdg_popup *popup = layer_popup->wlr_popup; | 485 | struct wlr_xdg_popup *popup = layer_popup->wlr_popup; |
470 | struct wlr_surface *surface = popup->base->surface; | 486 | struct wlr_surface *surface = popup->base->surface; |
471 | int popup_sx = popup->geometry.x - popup->base->geometry.x; | 487 | int popup_sx = popup->current.geometry.x - popup->base->current.geometry.x; |
472 | int popup_sy = popup->geometry.y - popup->base->geometry.y; | 488 | int popup_sy = popup->current.geometry.y - popup->base->current.geometry.y; |
473 | int ox = popup_sx, oy = popup_sy; | 489 | int ox = popup_sx, oy = popup_sy; |
474 | struct sway_layer_surface *layer; | 490 | struct sway_layer_surface *layer; |
475 | while (true) { | 491 | while (true) { |
476 | if (layer_popup->parent_type == LAYER_PARENT_POPUP) { | 492 | if (layer_popup->parent_type == LAYER_PARENT_POPUP) { |
477 | layer_popup = layer_popup->parent_popup; | 493 | layer_popup = layer_popup->parent_popup; |
478 | ox += layer_popup->wlr_popup->geometry.x; | 494 | ox += layer_popup->wlr_popup->current.geometry.x; |
479 | oy += layer_popup->wlr_popup->geometry.y; | 495 | oy += layer_popup->wlr_popup->current.geometry.y; |
480 | } else { | 496 | } else { |
481 | layer = layer_popup->parent_layer; | 497 | layer = layer_popup->parent_layer; |
482 | ox += layer->geo.x; | 498 | ox += layer->geo.x; |
@@ -485,6 +501,7 @@ static void popup_damage(struct sway_layer_popup *layer_popup, bool whole) { | |||
485 | } | 501 | } |
486 | } | 502 | } |
487 | struct wlr_output *wlr_output = layer->layer_surface->output; | 503 | struct wlr_output *wlr_output = layer->layer_surface->output; |
504 | sway_assert(wlr_output, "wlr_layer_surface_v1 has null output"); | ||
488 | struct sway_output *output = wlr_output->data; | 505 | struct sway_output *output = wlr_output->data; |
489 | output_damage_surface(output, ox, oy, surface, whole); | 506 | output_damage_surface(output, ox, oy, surface, whole); |
490 | } | 507 | } |
@@ -493,6 +510,7 @@ static void popup_handle_map(struct wl_listener *listener, void *data) { | |||
493 | struct sway_layer_popup *popup = wl_container_of(listener, popup, map); | 510 | struct sway_layer_popup *popup = wl_container_of(listener, popup, map); |
494 | struct sway_layer_surface *layer = popup_get_layer(popup); | 511 | struct sway_layer_surface *layer = popup_get_layer(popup); |
495 | struct wlr_output *wlr_output = layer->layer_surface->output; | 512 | struct wlr_output *wlr_output = layer->layer_surface->output; |
513 | sway_assert(wlr_output, "wlr_layer_surface_v1 has null output"); | ||
496 | wlr_surface_send_enter(popup->wlr_popup->base->surface, wlr_output); | 514 | wlr_surface_send_enter(popup->wlr_popup->base->surface, wlr_output); |
497 | popup_damage(popup, true); | 515 | popup_damage(popup, true); |
498 | } | 516 | } |
@@ -522,7 +540,9 @@ static void popup_unconstrain(struct sway_layer_popup *popup) { | |||
522 | struct sway_layer_surface *layer = popup_get_layer(popup); | 540 | struct sway_layer_surface *layer = popup_get_layer(popup); |
523 | struct wlr_xdg_popup *wlr_popup = popup->wlr_popup; | 541 | struct wlr_xdg_popup *wlr_popup = popup->wlr_popup; |
524 | 542 | ||
525 | struct sway_output *output = layer->layer_surface->output->data; | 543 | struct wlr_output *wlr_output = layer->layer_surface->output; |
544 | sway_assert(wlr_output, "wlr_layer_surface_v1 has null output"); | ||
545 | struct sway_output *output = wlr_output->data; | ||
526 | 546 | ||
527 | // the output box expressed in the coordinate system of the toplevel parent | 547 | // the output box expressed in the coordinate system of the toplevel parent |
528 | // of the popup | 548 | // of the popup |
@@ -590,14 +610,14 @@ void handle_layer_shell_surface(struct wl_listener *listener, void *data) { | |||
590 | sway_log(SWAY_DEBUG, "new layer surface: namespace %s layer %d anchor %" PRIu32 | 610 | sway_log(SWAY_DEBUG, "new layer surface: namespace %s layer %d anchor %" PRIu32 |
591 | " size %" PRIu32 "x%" PRIu32 " margin %" PRIu32 ",%" PRIu32 ",%" PRIu32 ",%" PRIu32 ",", | 611 | " size %" PRIu32 "x%" PRIu32 " margin %" PRIu32 ",%" PRIu32 ",%" PRIu32 ",%" PRIu32 ",", |
592 | layer_surface->namespace, | 612 | layer_surface->namespace, |
593 | layer_surface->client_pending.layer, | 613 | layer_surface->pending.layer, |
594 | layer_surface->client_pending.anchor, | 614 | layer_surface->pending.anchor, |
595 | layer_surface->client_pending.desired_width, | 615 | layer_surface->pending.desired_width, |
596 | layer_surface->client_pending.desired_height, | 616 | layer_surface->pending.desired_height, |
597 | layer_surface->client_pending.margin.top, | 617 | layer_surface->pending.margin.top, |
598 | layer_surface->client_pending.margin.right, | 618 | layer_surface->pending.margin.right, |
599 | layer_surface->client_pending.margin.bottom, | 619 | layer_surface->pending.margin.bottom, |
600 | layer_surface->client_pending.margin.left); | 620 | layer_surface->pending.margin.left); |
601 | 621 | ||
602 | if (!layer_surface->output) { | 622 | if (!layer_surface->output) { |
603 | // Assign last active output | 623 | // Assign last active output |
@@ -609,12 +629,16 @@ void handle_layer_shell_surface(struct wl_listener *listener, void *data) { | |||
609 | output = ws->output; | 629 | output = ws->output; |
610 | } | 630 | } |
611 | } | 631 | } |
612 | if (!output || output == root->noop_output) { | 632 | if (!output || output == root->fallback_output) { |
613 | if (!root->outputs->length) { | 633 | if (!root->outputs->length) { |
614 | sway_log(SWAY_ERROR, | 634 | sway_log(SWAY_ERROR, |
615 | "no output to auto-assign layer surface '%s' to", | 635 | "no output to auto-assign layer surface '%s' to", |
616 | layer_surface->namespace); | 636 | layer_surface->namespace); |
617 | wlr_layer_surface_v1_close(layer_surface); | 637 | // Note that layer_surface->output can be NULL |
638 | // here, but none of our destroy callbacks are | ||
639 | // registered yet so we don't have to make them | ||
640 | // handle that case. | ||
641 | wlr_layer_surface_v1_destroy(layer_surface); | ||
618 | return; | 642 | return; |
619 | } | 643 | } |
620 | output = root->outputs->items[0]; | 644 | output = root->outputs->items[0]; |
@@ -628,6 +652,8 @@ void handle_layer_shell_surface(struct wl_listener *listener, void *data) { | |||
628 | return; | 652 | return; |
629 | } | 653 | } |
630 | 654 | ||
655 | wl_list_init(&sway_layer->subsurfaces); | ||
656 | |||
631 | sway_layer->surface_commit.notify = handle_surface_commit; | 657 | sway_layer->surface_commit.notify = handle_surface_commit; |
632 | wl_signal_add(&layer_surface->surface->events.commit, | 658 | wl_signal_add(&layer_surface->surface->events.commit, |
633 | &sway_layer->surface_commit); | 659 | &sway_layer->surface_commit); |
@@ -649,15 +675,15 @@ void handle_layer_shell_surface(struct wl_listener *listener, void *data) { | |||
649 | 675 | ||
650 | struct sway_output *output = layer_surface->output->data; | 676 | struct sway_output *output = layer_surface->output->data; |
651 | sway_layer->output_destroy.notify = handle_output_destroy; | 677 | sway_layer->output_destroy.notify = handle_output_destroy; |
652 | wl_signal_add(&output->events.destroy, &sway_layer->output_destroy); | 678 | wl_signal_add(&output->events.disable, &sway_layer->output_destroy); |
653 | 679 | ||
654 | wl_list_insert(&output->layers[layer_surface->client_pending.layer], | 680 | wl_list_insert(&output->layers[layer_surface->pending.layer], |
655 | &sway_layer->link); | 681 | &sway_layer->link); |
656 | 682 | ||
657 | // Temporarily set the layer's current state to client_pending | 683 | // Temporarily set the layer's current state to pending |
658 | // So that we can easily arrange it | 684 | // So that we can easily arrange it |
659 | struct wlr_layer_surface_v1_state old_state = layer_surface->current; | 685 | struct wlr_layer_surface_v1_state old_state = layer_surface->current; |
660 | layer_surface->current = layer_surface->client_pending; | 686 | layer_surface->current = layer_surface->pending; |
661 | arrange_layers(output); | 687 | arrange_layers(output); |
662 | layer_surface->current = old_state; | 688 | layer_surface->current = old_state; |
663 | } | 689 | } |
diff --git a/sway/desktop/output.c b/sway/desktop/output.c index 5edc8f96..7bb9dab2 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c | |||
@@ -4,15 +4,17 @@ | |||
4 | #include <strings.h> | 4 | #include <strings.h> |
5 | #include <time.h> | 5 | #include <time.h> |
6 | #include <wayland-server-core.h> | 6 | #include <wayland-server-core.h> |
7 | #include <wlr/backend/drm.h> | ||
8 | #include <wlr/backend/headless.h> | ||
7 | #include <wlr/render/wlr_renderer.h> | 9 | #include <wlr/render/wlr_renderer.h> |
8 | #include <wlr/types/wlr_box.h> | ||
9 | #include <wlr/types/wlr_buffer.h> | 10 | #include <wlr/types/wlr_buffer.h> |
11 | #include <wlr/types/wlr_drm_lease_v1.h> | ||
10 | #include <wlr/types/wlr_matrix.h> | 12 | #include <wlr/types/wlr_matrix.h> |
11 | #include <wlr/types/wlr_output_damage.h> | 13 | #include <wlr/types/wlr_output_damage.h> |
12 | #include <wlr/types/wlr_output_layout.h> | 14 | #include <wlr/types/wlr_output_layout.h> |
13 | #include <wlr/types/wlr_output.h> | 15 | #include <wlr/types/wlr_output.h> |
14 | #include <wlr/types/wlr_presentation_time.h> | 16 | #include <wlr/types/wlr_presentation_time.h> |
15 | #include <wlr/types/wlr_surface.h> | 17 | #include <wlr/types/wlr_compositor.h> |
16 | #include <wlr/util/region.h> | 18 | #include <wlr/util/region.h> |
17 | #include "config.h" | 19 | #include "config.h" |
18 | #include "log.h" | 20 | #include "log.h" |
@@ -56,26 +58,6 @@ struct sway_output *all_output_by_name_or_id(const char *name_or_id) { | |||
56 | return NULL; | 58 | return NULL; |
57 | } | 59 | } |
58 | 60 | ||
59 | /** | ||
60 | * Rotate a child's position relative to a parent. The parent size is (pw, ph), | ||
61 | * the child position is (*sx, *sy) and its size is (sw, sh). | ||
62 | */ | ||
63 | static void rotate_child_position(double *sx, double *sy, double sw, double sh, | ||
64 | double pw, double ph, float rotation) { | ||
65 | if (rotation == 0.0f) { | ||
66 | return; | ||
67 | } | ||
68 | |||
69 | // Coordinates relative to the center of the subsurface | ||
70 | double ox = *sx - pw/2 + sw/2, | ||
71 | oy = *sy - ph/2 + sh/2; | ||
72 | // Rotated coordinates | ||
73 | double rx = cos(-rotation)*ox - sin(-rotation)*oy, | ||
74 | ry = cos(-rotation)*oy + sin(-rotation)*ox; | ||
75 | *sx = rx + pw/2 - sw/2; | ||
76 | *sy = ry + ph/2 - sh/2; | ||
77 | } | ||
78 | |||
79 | struct surface_iterator_data { | 61 | struct surface_iterator_data { |
80 | sway_surface_iterator_func_t user_iterator; | 62 | sway_surface_iterator_func_t user_iterator; |
81 | void *user_data; | 63 | void *user_data; |
@@ -84,7 +66,6 @@ struct surface_iterator_data { | |||
84 | struct sway_view *view; | 66 | struct sway_view *view; |
85 | double ox, oy; | 67 | double ox, oy; |
86 | int width, height; | 68 | int width, height; |
87 | float rotation; | ||
88 | }; | 69 | }; |
89 | 70 | ||
90 | static bool get_surface_box(struct surface_iterator_data *data, | 71 | static bool get_surface_box(struct surface_iterator_data *data, |
@@ -99,14 +80,9 @@ static bool get_surface_box(struct surface_iterator_data *data, | |||
99 | int sw = surface->current.width; | 80 | int sw = surface->current.width; |
100 | int sh = surface->current.height; | 81 | int sh = surface->current.height; |
101 | 82 | ||
102 | double _sx = sx + surface->sx; | ||
103 | double _sy = sy + surface->sy; | ||
104 | rotate_child_position(&_sx, &_sy, sw, sh, data->width, data->height, | ||
105 | data->rotation); | ||
106 | |||
107 | struct wlr_box box = { | 83 | struct wlr_box box = { |
108 | .x = data->ox + _sx, | 84 | .x = floor(data->ox + sx), |
109 | .y = data->oy + _sy, | 85 | .y = floor(data->oy + sy), |
110 | .width = sw, | 86 | .width = sw, |
111 | .height = sh, | 87 | .height = sh, |
112 | }; | 88 | }; |
@@ -114,16 +90,13 @@ static bool get_surface_box(struct surface_iterator_data *data, | |||
114 | memcpy(surface_box, &box, sizeof(struct wlr_box)); | 90 | memcpy(surface_box, &box, sizeof(struct wlr_box)); |
115 | } | 91 | } |
116 | 92 | ||
117 | struct wlr_box rotated_box; | ||
118 | wlr_box_rotated_bounds(&rotated_box, &box, data->rotation); | ||
119 | |||
120 | struct wlr_box output_box = { | 93 | struct wlr_box output_box = { |
121 | .width = output->width, | 94 | .width = output->width, |
122 | .height = output->height, | 95 | .height = output->height, |
123 | }; | 96 | }; |
124 | 97 | ||
125 | struct wlr_box intersection; | 98 | struct wlr_box intersection; |
126 | return wlr_box_intersection(&intersection, &output_box, &rotated_box); | 99 | return wlr_box_intersection(&intersection, &output_box, &box); |
127 | } | 100 | } |
128 | 101 | ||
129 | static void output_for_each_surface_iterator(struct wlr_surface *surface, | 102 | static void output_for_each_surface_iterator(struct wlr_surface *surface, |
@@ -136,7 +109,7 @@ static void output_for_each_surface_iterator(struct wlr_surface *surface, | |||
136 | return; | 109 | return; |
137 | } | 110 | } |
138 | 111 | ||
139 | data->user_iterator(data->output, data->view, surface, &box, data->rotation, | 112 | data->user_iterator(data->output, data->view, surface, &box, |
140 | data->user_data); | 113 | data->user_data); |
141 | } | 114 | } |
142 | 115 | ||
@@ -152,7 +125,6 @@ void output_surface_for_each_surface(struct sway_output *output, | |||
152 | .oy = oy, | 125 | .oy = oy, |
153 | .width = surface->current.width, | 126 | .width = surface->current.width, |
154 | .height = surface->current.height, | 127 | .height = surface->current.height, |
155 | .rotation = 0, | ||
156 | }; | 128 | }; |
157 | 129 | ||
158 | wlr_surface_for_each_surface(surface, | 130 | wlr_surface_for_each_surface(surface, |
@@ -173,7 +145,6 @@ void output_view_for_each_surface(struct sway_output *output, | |||
173 | - view->geometry.y, | 145 | - view->geometry.y, |
174 | .width = view->container->current.content_width, | 146 | .width = view->container->current.content_width, |
175 | .height = view->container->current.content_height, | 147 | .height = view->container->current.content_height, |
176 | .rotation = 0, // TODO | ||
177 | }; | 148 | }; |
178 | 149 | ||
179 | view_for_each_surface(view, output_for_each_surface_iterator, &data); | 150 | view_for_each_surface(view, output_for_each_surface_iterator, &data); |
@@ -193,7 +164,6 @@ void output_view_for_each_popup_surface(struct sway_output *output, | |||
193 | - view->geometry.y, | 164 | - view->geometry.y, |
194 | .width = view->container->current.content_width, | 165 | .width = view->container->current.content_width, |
195 | .height = view->container->current.content_height, | 166 | .height = view->container->current.content_height, |
196 | .rotation = 0, // TODO | ||
197 | }; | 167 | }; |
198 | 168 | ||
199 | view_for_each_popup_surface(view, output_for_each_surface_iterator, &data); | 169 | view_for_each_popup_surface(view, output_for_each_surface_iterator, &data); |
@@ -206,40 +176,19 @@ void output_layer_for_each_surface(struct sway_output *output, | |||
206 | wl_list_for_each(layer_surface, layer_surfaces, link) { | 176 | wl_list_for_each(layer_surface, layer_surfaces, link) { |
207 | struct wlr_layer_surface_v1 *wlr_layer_surface_v1 = | 177 | struct wlr_layer_surface_v1 *wlr_layer_surface_v1 = |
208 | layer_surface->layer_surface; | 178 | layer_surface->layer_surface; |
209 | output_surface_for_each_surface(output, wlr_layer_surface_v1->surface, | 179 | struct wlr_surface *surface = wlr_layer_surface_v1->surface; |
210 | layer_surface->geo.x, layer_surface->geo.y, iterator, | 180 | struct surface_iterator_data data = { |
211 | user_data); | 181 | .user_iterator = iterator, |
212 | 182 | .user_data = user_data, | |
213 | struct wlr_xdg_popup *state; | 183 | .output = output, |
214 | wl_list_for_each(state, &wlr_layer_surface_v1->popups, link) { | 184 | .view = NULL, |
215 | struct wlr_xdg_surface *popup = state->base; | 185 | .ox = layer_surface->geo.x, |
216 | if (!popup->configured) { | 186 | .oy = layer_surface->geo.y, |
217 | continue; | 187 | .width = surface->current.width, |
218 | } | 188 | .height = surface->current.height, |
219 | 189 | }; | |
220 | double popup_sx, popup_sy; | 190 | wlr_layer_surface_v1_for_each_surface(wlr_layer_surface_v1, |
221 | popup_sx = layer_surface->geo.x + | 191 | output_for_each_surface_iterator, &data); |
222 | popup->popup->geometry.x - popup->geometry.x; | ||
223 | popup_sy = layer_surface->geo.y + | ||
224 | popup->popup->geometry.y - popup->geometry.y; | ||
225 | |||
226 | struct wlr_surface *surface = popup->surface; | ||
227 | |||
228 | struct surface_iterator_data data = { | ||
229 | .user_iterator = iterator, | ||
230 | .user_data = user_data, | ||
231 | .output = output, | ||
232 | .view = NULL, | ||
233 | .ox = popup_sx, | ||
234 | .oy = popup_sy, | ||
235 | .width = surface->current.width, | ||
236 | .height = surface->current.height, | ||
237 | .rotation = 0, | ||
238 | }; | ||
239 | |||
240 | wlr_xdg_surface_for_each_surface( | ||
241 | popup, output_for_each_surface_iterator, &data); | ||
242 | } | ||
243 | } | 192 | } |
244 | } | 193 | } |
245 | 194 | ||
@@ -264,37 +213,19 @@ void output_layer_for_each_popup_surface(struct sway_output *output, | |||
264 | wl_list_for_each(layer_surface, layer_surfaces, link) { | 213 | wl_list_for_each(layer_surface, layer_surfaces, link) { |
265 | struct wlr_layer_surface_v1 *wlr_layer_surface_v1 = | 214 | struct wlr_layer_surface_v1 *wlr_layer_surface_v1 = |
266 | layer_surface->layer_surface; | 215 | layer_surface->layer_surface; |
267 | 216 | struct wlr_surface *surface = wlr_layer_surface_v1->surface; | |
268 | struct wlr_xdg_popup *state; | 217 | struct surface_iterator_data data = { |
269 | wl_list_for_each(state, &wlr_layer_surface_v1->popups, link) { | 218 | .user_iterator = iterator, |
270 | struct wlr_xdg_surface *popup = state->base; | 219 | .user_data = user_data, |
271 | if (!popup->configured) { | 220 | .output = output, |
272 | continue; | 221 | .view = NULL, |
273 | } | 222 | .ox = layer_surface->geo.x, |
274 | 223 | .oy = layer_surface->geo.y, | |
275 | double popup_sx, popup_sy; | 224 | .width = surface->current.width, |
276 | popup_sx = layer_surface->geo.x + | 225 | .height = surface->current.height, |
277 | popup->popup->geometry.x - popup->geometry.x; | 226 | }; |
278 | popup_sy = layer_surface->geo.y + | 227 | wlr_layer_surface_v1_for_each_popup_surface(wlr_layer_surface_v1, |
279 | popup->popup->geometry.y - popup->geometry.y; | 228 | output_for_each_surface_iterator, &data); |
280 | |||
281 | struct wlr_surface *surface = popup->surface; | ||
282 | |||
283 | struct surface_iterator_data data = { | ||
284 | .user_iterator = iterator, | ||
285 | .user_data = user_data, | ||
286 | .output = output, | ||
287 | .view = NULL, | ||
288 | .ox = popup_sx, | ||
289 | .oy = popup_sy, | ||
290 | .width = surface->current.width, | ||
291 | .height = surface->current.height, | ||
292 | .rotation = 0, | ||
293 | }; | ||
294 | |||
295 | wlr_xdg_surface_for_each_surface( | ||
296 | popup, output_for_each_surface_iterator, &data); | ||
297 | } | ||
298 | } | 229 | } |
299 | } | 230 | } |
300 | 231 | ||
@@ -344,6 +275,25 @@ static void for_each_surface_container_iterator(struct sway_container *con, | |||
344 | 275 | ||
345 | static void output_for_each_surface(struct sway_output *output, | 276 | static void output_for_each_surface(struct sway_output *output, |
346 | sway_surface_iterator_func_t iterator, void *user_data) { | 277 | sway_surface_iterator_func_t iterator, void *user_data) { |
278 | if (server.session_lock.locked) { | ||
279 | if (server.session_lock.lock == NULL) { | ||
280 | return; | ||
281 | } | ||
282 | struct wlr_session_lock_surface_v1 *lock_surface; | ||
283 | wl_list_for_each(lock_surface, &server.session_lock.lock->surfaces, link) { | ||
284 | if (lock_surface->output != output->wlr_output) { | ||
285 | continue; | ||
286 | } | ||
287 | if (!lock_surface->mapped) { | ||
288 | continue; | ||
289 | } | ||
290 | |||
291 | output_surface_for_each_surface(output, lock_surface->surface, | ||
292 | 0.0, 0.0, iterator, user_data); | ||
293 | } | ||
294 | return; | ||
295 | } | ||
296 | |||
347 | if (output_has_opaque_overlay_layer_surface(output)) { | 297 | if (output_has_opaque_overlay_layer_surface(output)) { |
348 | goto overlay; | 298 | goto overlay; |
349 | } | 299 | } |
@@ -463,9 +413,9 @@ struct send_frame_done_data { | |||
463 | int msec_until_refresh; | 413 | int msec_until_refresh; |
464 | }; | 414 | }; |
465 | 415 | ||
466 | static void send_frame_done_iterator(struct sway_output *output, struct sway_view *view, | 416 | static void send_frame_done_iterator(struct sway_output *output, |
467 | struct wlr_surface *surface, struct wlr_box *box, float rotation, | 417 | struct sway_view *view, struct wlr_surface *surface, |
468 | void *user_data) { | 418 | struct wlr_box *box, void *user_data) { |
469 | int view_max_render_time = 0; | 419 | int view_max_render_time = 0; |
470 | if (view != NULL) { | 420 | if (view != NULL) { |
471 | view_max_render_time = view->max_render_time; | 421 | view_max_render_time = view->max_render_time; |
@@ -488,9 +438,9 @@ static void send_frame_done(struct sway_output *output, struct send_frame_done_d | |||
488 | output_for_each_surface(output, send_frame_done_iterator, data); | 438 | output_for_each_surface(output, send_frame_done_iterator, data); |
489 | } | 439 | } |
490 | 440 | ||
491 | static void count_surface_iterator(struct sway_output *output, struct sway_view *view, | 441 | static void count_surface_iterator(struct sway_output *output, |
492 | struct wlr_surface *surface, struct wlr_box *_box, float rotation, | 442 | struct sway_view *view, struct wlr_surface *surface, |
493 | void *data) { | 443 | struct wlr_box *box, void *data) { |
494 | size_t *n = data; | 444 | size_t *n = data; |
495 | (*n)++; | 445 | (*n)++; |
496 | } | 446 | } |
@@ -503,6 +453,10 @@ static bool scan_out_fullscreen_view(struct sway_output *output, | |||
503 | return false; | 453 | return false; |
504 | } | 454 | } |
505 | 455 | ||
456 | if (server.session_lock.locked) { | ||
457 | return false; | ||
458 | } | ||
459 | |||
506 | if (!wl_list_empty(&view->saved_buffers)) { | 460 | if (!wl_list_empty(&view->saved_buffers)) { |
507 | return false; | 461 | return false; |
508 | } | 462 | } |
@@ -577,7 +531,7 @@ static int output_repaint_timer_handler(void *data) { | |||
577 | fullscreen_con = workspace->current.fullscreen; | 531 | fullscreen_con = workspace->current.fullscreen; |
578 | } | 532 | } |
579 | 533 | ||
580 | if (fullscreen_con && fullscreen_con->view) { | 534 | if (fullscreen_con && fullscreen_con->view && !debug.noscanout) { |
581 | // Try to scan-out the fullscreen view | 535 | // Try to scan-out the fullscreen view |
582 | static bool last_scanned_out = false; | 536 | static bool last_scanned_out = false; |
583 | bool scanned_out = | 537 | bool scanned_out = |
@@ -590,6 +544,7 @@ static int output_repaint_timer_handler(void *data) { | |||
590 | if (last_scanned_out && !scanned_out) { | 544 | if (last_scanned_out && !scanned_out) { |
591 | sway_log(SWAY_DEBUG, "Stopping fullscreen view scan out on %s", | 545 | sway_log(SWAY_DEBUG, "Stopping fullscreen view scan out on %s", |
592 | output->wlr_output->name); | 546 | output->wlr_output->name); |
547 | output_damage_whole(output); | ||
593 | } | 548 | } |
594 | last_scanned_out = scanned_out; | 549 | last_scanned_out = scanned_out; |
595 | 550 | ||
@@ -693,38 +648,30 @@ void output_damage_whole(struct sway_output *output) { | |||
693 | } | 648 | } |
694 | } | 649 | } |
695 | 650 | ||
696 | static void damage_surface_iterator(struct sway_output *output, struct sway_view *view, | 651 | static void damage_surface_iterator(struct sway_output *output, |
697 | struct wlr_surface *surface, struct wlr_box *_box, float rotation, | 652 | struct sway_view *view, struct wlr_surface *surface, |
698 | void *_data) { | 653 | struct wlr_box *_box, void *_data) { |
699 | bool *data = _data; | 654 | bool *data = _data; |
700 | bool whole = *data; | 655 | bool whole = *data; |
701 | 656 | ||
702 | struct wlr_box box = *_box; | 657 | struct wlr_box box = *_box; |
703 | scale_box(&box, output->wlr_output->scale); | 658 | scale_box(&box, output->wlr_output->scale); |
704 | 659 | ||
705 | int center_x = box.x + box.width/2; | 660 | pixman_region32_t damage; |
706 | int center_y = box.y + box.height/2; | 661 | pixman_region32_init(&damage); |
707 | 662 | wlr_surface_get_effective_damage(surface, &damage); | |
708 | if (pixman_region32_not_empty(&surface->buffer_damage)) { | 663 | wlr_region_scale(&damage, &damage, output->wlr_output->scale); |
709 | pixman_region32_t damage; | 664 | if (ceil(output->wlr_output->scale) > surface->current.scale) { |
710 | pixman_region32_init(&damage); | 665 | // When scaling up a surface, it'll become blurry so we need to |
711 | wlr_surface_get_effective_damage(surface, &damage); | 666 | // expand the damage region |
712 | wlr_region_scale(&damage, &damage, output->wlr_output->scale); | 667 | wlr_region_expand(&damage, &damage, |
713 | if (ceil(output->wlr_output->scale) > surface->current.scale) { | 668 | ceil(output->wlr_output->scale) - surface->current.scale); |
714 | // When scaling up a surface, it'll become blurry so we need to | ||
715 | // expand the damage region | ||
716 | wlr_region_expand(&damage, &damage, | ||
717 | ceil(output->wlr_output->scale) - surface->current.scale); | ||
718 | } | ||
719 | pixman_region32_translate(&damage, box.x, box.y); | ||
720 | wlr_region_rotated_bounds(&damage, &damage, rotation, | ||
721 | center_x, center_y); | ||
722 | wlr_output_damage_add(output->damage, &damage); | ||
723 | pixman_region32_fini(&damage); | ||
724 | } | 669 | } |
670 | pixman_region32_translate(&damage, box.x, box.y); | ||
671 | wlr_output_damage_add(output->damage, &damage); | ||
672 | pixman_region32_fini(&damage); | ||
725 | 673 | ||
726 | if (whole) { | 674 | if (whole) { |
727 | wlr_box_rotated_bounds(&box, &box, rotation); | ||
728 | wlr_output_damage_add_box(output->damage, &box); | 675 | wlr_output_damage_add_box(output->damage, &box); |
729 | } | 676 | } |
730 | 677 | ||
@@ -808,19 +755,20 @@ static void update_output_manager_config(struct sway_server *server) { | |||
808 | 755 | ||
809 | struct sway_output *output; | 756 | struct sway_output *output; |
810 | wl_list_for_each(output, &root->all_outputs, link) { | 757 | wl_list_for_each(output, &root->all_outputs, link) { |
811 | if (output == root->noop_output) { | 758 | if (output == root->fallback_output) { |
812 | continue; | 759 | continue; |
813 | } | 760 | } |
814 | struct wlr_output_configuration_head_v1 *config_head = | 761 | struct wlr_output_configuration_head_v1 *config_head = |
815 | wlr_output_configuration_head_v1_create(config, output->wlr_output); | 762 | wlr_output_configuration_head_v1_create(config, output->wlr_output); |
816 | struct wlr_box *output_box = wlr_output_layout_get_box( | 763 | struct wlr_box output_box; |
817 | root->output_layout, output->wlr_output); | 764 | wlr_output_layout_get_box(root->output_layout, |
818 | // We mark the output enabled even if it is switched off by DPMS | 765 | output->wlr_output, &output_box); |
819 | config_head->state.enabled = output->enabled; | 766 | // We mark the output enabled when it's switched off but not disabled |
767 | config_head->state.enabled = output->current_mode != NULL && output->enabled; | ||
820 | config_head->state.mode = output->current_mode; | 768 | config_head->state.mode = output->current_mode; |
821 | if (output_box) { | 769 | if (!wlr_box_empty(&output_box)) { |
822 | config_head->state.x = output_box->x; | 770 | config_head->state.x = output_box.x; |
823 | config_head->state.y = output_box->y; | 771 | config_head->state.y = output_box.y; |
824 | } | 772 | } |
825 | } | 773 | } |
826 | 774 | ||
@@ -830,18 +778,22 @@ static void update_output_manager_config(struct sway_server *server) { | |||
830 | static void handle_destroy(struct wl_listener *listener, void *data) { | 778 | static void handle_destroy(struct wl_listener *listener, void *data) { |
831 | struct sway_output *output = wl_container_of(listener, output, destroy); | 779 | struct sway_output *output = wl_container_of(listener, output, destroy); |
832 | struct sway_server *server = output->server; | 780 | struct sway_server *server = output->server; |
833 | wl_signal_emit(&output->events.destroy, output); | 781 | output_begin_destroy(output); |
834 | 782 | ||
835 | if (output->enabled) { | 783 | if (output->enabled) { |
836 | output_disable(output); | 784 | output_disable(output); |
837 | } | 785 | } |
838 | output_begin_destroy(output); | 786 | |
787 | wl_list_remove(&output->link); | ||
839 | 788 | ||
840 | wl_list_remove(&output->destroy.link); | 789 | wl_list_remove(&output->destroy.link); |
841 | wl_list_remove(&output->commit.link); | 790 | wl_list_remove(&output->commit.link); |
842 | wl_list_remove(&output->mode.link); | 791 | wl_list_remove(&output->mode.link); |
843 | wl_list_remove(&output->present.link); | 792 | wl_list_remove(&output->present.link); |
844 | 793 | ||
794 | output->wlr_output->data = NULL; | ||
795 | output->wlr_output = NULL; | ||
796 | |||
845 | transaction_commit_dirty(); | 797 | transaction_commit_dirty(); |
846 | 798 | ||
847 | update_output_manager_config(server); | 799 | update_output_manager_config(server); |
@@ -902,7 +854,7 @@ static void handle_present(struct wl_listener *listener, void *data) { | |||
902 | struct sway_output *output = wl_container_of(listener, output, present); | 854 | struct sway_output *output = wl_container_of(listener, output, present); |
903 | struct wlr_output_event_present *output_event = data; | 855 | struct wlr_output_event_present *output_event = data; |
904 | 856 | ||
905 | if (!output->enabled) { | 857 | if (!output->enabled || !output_event->presented) { |
906 | return; | 858 | return; |
907 | } | 859 | } |
908 | 860 | ||
@@ -910,10 +862,39 @@ static void handle_present(struct wl_listener *listener, void *data) { | |||
910 | output->refresh_nsec = output_event->refresh; | 862 | output->refresh_nsec = output_event->refresh; |
911 | } | 863 | } |
912 | 864 | ||
865 | static unsigned int last_headless_num = 0; | ||
866 | |||
913 | void handle_new_output(struct wl_listener *listener, void *data) { | 867 | void handle_new_output(struct wl_listener *listener, void *data) { |
914 | struct sway_server *server = wl_container_of(listener, server, new_output); | 868 | struct sway_server *server = wl_container_of(listener, server, new_output); |
915 | struct wlr_output *wlr_output = data; | 869 | struct wlr_output *wlr_output = data; |
916 | sway_log(SWAY_DEBUG, "New output %p: %s", wlr_output, wlr_output->name); | 870 | |
871 | if (wlr_output == root->fallback_output->wlr_output) { | ||
872 | return; | ||
873 | } | ||
874 | |||
875 | if (wlr_output_is_headless(wlr_output)) { | ||
876 | char name[64]; | ||
877 | snprintf(name, sizeof(name), "HEADLESS-%u", ++last_headless_num); | ||
878 | wlr_output_set_name(wlr_output, name); | ||
879 | } | ||
880 | |||
881 | sway_log(SWAY_DEBUG, "New output %p: %s (non-desktop: %d)", | ||
882 | wlr_output, wlr_output->name, wlr_output->non_desktop); | ||
883 | |||
884 | if (wlr_output->non_desktop) { | ||
885 | sway_log(SWAY_DEBUG, "Not configuring non-desktop output"); | ||
886 | if (server->drm_lease_manager) { | ||
887 | wlr_drm_lease_v1_manager_offer_output(server->drm_lease_manager, | ||
888 | wlr_output); | ||
889 | } | ||
890 | return; | ||
891 | } | ||
892 | |||
893 | if (!wlr_output_init_render(wlr_output, server->allocator, | ||
894 | server->renderer)) { | ||
895 | sway_log(SWAY_ERROR, "Failed to init output render"); | ||
896 | return; | ||
897 | } | ||
917 | 898 | ||
918 | struct sway_output *output = output_create(wlr_output); | 899 | struct sway_output *output = output_create(wlr_output); |
919 | if (!output) { | 900 | if (!output) { |
@@ -1047,10 +1028,10 @@ void handle_output_power_manager_set_mode(struct wl_listener *listener, | |||
1047 | struct output_config *oc = new_output_config(output->wlr_output->name); | 1028 | struct output_config *oc = new_output_config(output->wlr_output->name); |
1048 | switch (event->mode) { | 1029 | switch (event->mode) { |
1049 | case ZWLR_OUTPUT_POWER_V1_MODE_OFF: | 1030 | case ZWLR_OUTPUT_POWER_V1_MODE_OFF: |
1050 | oc->dpms_state = DPMS_OFF; | 1031 | oc->power = 0; |
1051 | break; | 1032 | break; |
1052 | case ZWLR_OUTPUT_POWER_V1_MODE_ON: | 1033 | case ZWLR_OUTPUT_POWER_V1_MODE_ON: |
1053 | oc->dpms_state = DPMS_ON; | 1034 | oc->power = 1; |
1054 | break; | 1035 | break; |
1055 | } | 1036 | } |
1056 | oc = store_output_config(oc); | 1037 | oc = store_output_config(oc); |
diff --git a/sway/desktop/render.c b/sway/desktop/render.c index bd85282c..ed9ad490 100644 --- a/sway/desktop/render.c +++ b/sway/desktop/render.c | |||
@@ -7,13 +7,12 @@ | |||
7 | #include <wayland-server-core.h> | 7 | #include <wayland-server-core.h> |
8 | #include <wlr/render/gles2.h> | 8 | #include <wlr/render/gles2.h> |
9 | #include <wlr/render/wlr_renderer.h> | 9 | #include <wlr/render/wlr_renderer.h> |
10 | #include <wlr/types/wlr_box.h> | ||
11 | #include <wlr/types/wlr_buffer.h> | 10 | #include <wlr/types/wlr_buffer.h> |
12 | #include <wlr/types/wlr_matrix.h> | 11 | #include <wlr/types/wlr_matrix.h> |
13 | #include <wlr/types/wlr_output_damage.h> | 12 | #include <wlr/types/wlr_output_damage.h> |
14 | #include <wlr/types/wlr_output_layout.h> | 13 | #include <wlr/types/wlr_output_layout.h> |
15 | #include <wlr/types/wlr_output.h> | 14 | #include <wlr/types/wlr_output.h> |
16 | #include <wlr/types/wlr_surface.h> | 15 | #include <wlr/types/wlr_compositor.h> |
17 | #include <wlr/util/region.h> | 16 | #include <wlr/util/region.h> |
18 | #include "log.h" | 17 | #include "log.h" |
19 | #include "config.h" | 18 | #include "config.h" |
@@ -32,6 +31,7 @@ | |||
32 | struct render_data { | 31 | struct render_data { |
33 | pixman_region32_t *damage; | 32 | pixman_region32_t *damage; |
34 | float alpha; | 33 | float alpha; |
34 | struct wlr_box *clip_box; | ||
35 | }; | 35 | }; |
36 | 36 | ||
37 | /** | 37 | /** |
@@ -52,7 +52,7 @@ static int scale_length(int length, int offset, float scale) { | |||
52 | 52 | ||
53 | static void scissor_output(struct wlr_output *wlr_output, | 53 | static void scissor_output(struct wlr_output *wlr_output, |
54 | pixman_box32_t *rect) { | 54 | pixman_box32_t *rect) { |
55 | struct wlr_renderer *renderer = wlr_backend_get_renderer(wlr_output->backend); | 55 | struct wlr_renderer *renderer = wlr_output->renderer; |
56 | assert(renderer); | 56 | assert(renderer); |
57 | 57 | ||
58 | struct wlr_box box = { | 58 | struct wlr_box box = { |
@@ -100,13 +100,9 @@ static void render_texture(struct wlr_output *wlr_output, | |||
100 | pixman_region32_t *output_damage, struct wlr_texture *texture, | 100 | pixman_region32_t *output_damage, struct wlr_texture *texture, |
101 | const struct wlr_fbox *src_box, const struct wlr_box *dst_box, | 101 | const struct wlr_fbox *src_box, const struct wlr_box *dst_box, |
102 | const float matrix[static 9], float alpha) { | 102 | const float matrix[static 9], float alpha) { |
103 | struct wlr_renderer *renderer = | 103 | struct wlr_renderer *renderer = wlr_output->renderer; |
104 | wlr_backend_get_renderer(wlr_output->backend); | ||
105 | struct sway_output *output = wlr_output->data; | 104 | struct sway_output *output = wlr_output->data; |
106 | 105 | ||
107 | struct wlr_gles2_texture_attribs attribs; | ||
108 | wlr_gles2_texture_get_attribs(texture, &attribs); | ||
109 | |||
110 | pixman_region32_t damage; | 106 | pixman_region32_t damage; |
111 | pixman_region32_init(&damage); | 107 | pixman_region32_init(&damage); |
112 | pixman_region32_union_rect(&damage, &damage, dst_box->x, dst_box->y, | 108 | pixman_region32_union_rect(&damage, &damage, dst_box->x, dst_box->y, |
@@ -133,9 +129,9 @@ damage_finish: | |||
133 | pixman_region32_fini(&damage); | 129 | pixman_region32_fini(&damage); |
134 | } | 130 | } |
135 | 131 | ||
136 | static void render_surface_iterator(struct sway_output *output, struct sway_view *view, | 132 | static void render_surface_iterator(struct sway_output *output, |
137 | struct wlr_surface *surface, struct wlr_box *_box, float rotation, | 133 | struct sway_view *view, struct wlr_surface *surface, |
138 | void *_data) { | 134 | struct wlr_box *_box, void *_data) { |
139 | struct render_data *data = _data; | 135 | struct render_data *data = _data; |
140 | struct wlr_output *wlr_output = output->wlr_output; | 136 | struct wlr_output *wlr_output = output->wlr_output; |
141 | pixman_region32_t *output_damage = data->damage; | 137 | pixman_region32_t *output_damage = data->damage; |
@@ -149,15 +145,23 @@ static void render_surface_iterator(struct sway_output *output, struct sway_view | |||
149 | struct wlr_fbox src_box; | 145 | struct wlr_fbox src_box; |
150 | wlr_surface_get_buffer_source_box(surface, &src_box); | 146 | wlr_surface_get_buffer_source_box(surface, &src_box); |
151 | 147 | ||
152 | struct wlr_box dst_box = *_box; | 148 | struct wlr_box proj_box = *_box; |
153 | scale_box(&dst_box, wlr_output->scale); | 149 | scale_box(&proj_box, wlr_output->scale); |
154 | 150 | ||
155 | float matrix[9]; | 151 | float matrix[9]; |
156 | enum wl_output_transform transform = | 152 | enum wl_output_transform transform = |
157 | wlr_output_transform_invert(surface->current.transform); | 153 | wlr_output_transform_invert(surface->current.transform); |
158 | wlr_matrix_project_box(matrix, &dst_box, transform, rotation, | 154 | wlr_matrix_project_box(matrix, &proj_box, transform, 0.0, |
159 | wlr_output->transform_matrix); | 155 | wlr_output->transform_matrix); |
160 | 156 | ||
157 | struct wlr_box dst_box = *_box; | ||
158 | struct wlr_box *clip_box = data->clip_box; | ||
159 | if (clip_box != NULL) { | ||
160 | dst_box.width = fmin(dst_box.width, clip_box->width); | ||
161 | dst_box.height = fmin(dst_box.height, clip_box->height); | ||
162 | } | ||
163 | scale_box(&dst_box, wlr_output->scale); | ||
164 | |||
161 | render_texture(wlr_output, output_damage, texture, | 165 | render_texture(wlr_output, output_damage, texture, |
162 | &src_box, &dst_box, matrix, alpha); | 166 | &src_box, &dst_box, matrix, alpha); |
163 | 167 | ||
@@ -213,8 +217,7 @@ void render_rect(struct sway_output *output, | |||
213 | pixman_region32_t *output_damage, const struct wlr_box *_box, | 217 | pixman_region32_t *output_damage, const struct wlr_box *_box, |
214 | float color[static 4]) { | 218 | float color[static 4]) { |
215 | struct wlr_output *wlr_output = output->wlr_output; | 219 | struct wlr_output *wlr_output = output->wlr_output; |
216 | struct wlr_renderer *renderer = | 220 | struct wlr_renderer *renderer = wlr_output->renderer; |
217 | wlr_backend_get_renderer(wlr_output->backend); | ||
218 | 221 | ||
219 | struct wlr_box box; | 222 | struct wlr_box box; |
220 | memcpy(&box, _box, sizeof(struct wlr_box)); | 223 | memcpy(&box, _box, sizeof(struct wlr_box)); |
@@ -256,6 +259,14 @@ static void render_view_toplevels(struct sway_view *view, | |||
256 | .damage = damage, | 259 | .damage = damage, |
257 | .alpha = alpha, | 260 | .alpha = alpha, |
258 | }; | 261 | }; |
262 | struct wlr_box clip_box; | ||
263 | if (!container_is_current_floating(view->container)) { | ||
264 | // As we pass the geometry offsets to the surface iterator, we will | ||
265 | // need to account for the offsets in the clip dimensions. | ||
266 | clip_box.width = view->container->current.content_width + view->geometry.x; | ||
267 | clip_box.height = view->container->current.content_height + view->geometry.y; | ||
268 | data.clip_box = &clip_box; | ||
269 | } | ||
259 | // Render all toplevels without descending into popups | 270 | // Render all toplevels without descending into popups |
260 | double ox = view->container->surface_x - | 271 | double ox = view->container->surface_x - |
261 | output->lx - view->geometry.x; | 272 | output->lx - view->geometry.x; |
@@ -282,17 +293,18 @@ static void render_saved_view(struct sway_view *view, | |||
282 | if (wl_list_empty(&view->saved_buffers)) { | 293 | if (wl_list_empty(&view->saved_buffers)) { |
283 | return; | 294 | return; |
284 | } | 295 | } |
296 | |||
297 | bool floating = container_is_current_floating(view->container); | ||
298 | |||
285 | struct sway_saved_buffer *saved_buf; | 299 | struct sway_saved_buffer *saved_buf; |
286 | wl_list_for_each(saved_buf, &view->saved_buffers, link) { | 300 | wl_list_for_each(saved_buf, &view->saved_buffers, link) { |
287 | if (!saved_buf->buffer->texture) { | 301 | if (!saved_buf->buffer->texture) { |
288 | continue; | 302 | continue; |
289 | } | 303 | } |
290 | 304 | ||
291 | struct wlr_box box = { | 305 | struct wlr_box proj_box = { |
292 | .x = view->container->surface_x - output->lx - | 306 | .x = saved_buf->x - view->saved_geometry.x - output->lx, |
293 | view->saved_geometry.x + saved_buf->x, | 307 | .y = saved_buf->y - view->saved_geometry.y - output->ly, |
294 | .y = view->container->surface_y - output->ly - | ||
295 | view->saved_geometry.y + saved_buf->y, | ||
296 | .width = saved_buf->width, | 308 | .width = saved_buf->width, |
297 | .height = saved_buf->height, | 309 | .height = saved_buf->height, |
298 | }; | 310 | }; |
@@ -303,20 +315,31 @@ static void render_saved_view(struct sway_view *view, | |||
303 | }; | 315 | }; |
304 | 316 | ||
305 | struct wlr_box intersection; | 317 | struct wlr_box intersection; |
306 | bool intersects = wlr_box_intersection(&intersection, &output_box, &box); | 318 | bool intersects = wlr_box_intersection(&intersection, &output_box, &proj_box); |
307 | if (!intersects) { | 319 | if (!intersects) { |
308 | continue; | 320 | continue; |
309 | } | 321 | } |
310 | 322 | ||
311 | scale_box(&box, wlr_output->scale); | 323 | struct wlr_box dst_box = proj_box; |
324 | scale_box(&proj_box, wlr_output->scale); | ||
312 | 325 | ||
313 | float matrix[9]; | 326 | float matrix[9]; |
314 | enum wl_output_transform transform = wlr_output_transform_invert(saved_buf->transform); | 327 | enum wl_output_transform transform = wlr_output_transform_invert(saved_buf->transform); |
315 | wlr_matrix_project_box(matrix, &box, transform, 0, | 328 | wlr_matrix_project_box(matrix, &proj_box, transform, 0, |
316 | wlr_output->transform_matrix); | 329 | wlr_output->transform_matrix); |
317 | 330 | ||
331 | if (!floating) { | ||
332 | dst_box.width = fmin(dst_box.width, | ||
333 | view->container->current.content_width - | ||
334 | (saved_buf->x - view->container->current.content_x) + view->saved_geometry.x); | ||
335 | dst_box.height = fmin(dst_box.height, | ||
336 | view->container->current.content_height - | ||
337 | (saved_buf->y - view->container->current.content_y) + view->saved_geometry.y); | ||
338 | } | ||
339 | scale_box(&dst_box, wlr_output->scale); | ||
340 | |||
318 | render_texture(wlr_output, damage, saved_buf->buffer->texture, | 341 | render_texture(wlr_output, damage, saved_buf->buffer->texture, |
319 | &saved_buf->source_box, &box, matrix, alpha); | 342 | &saved_buf->source_box, &dst_box, matrix, alpha); |
320 | } | 343 | } |
321 | 344 | ||
322 | // FIXME: we should set the surface that this saved buffer originates from | 345 | // FIXME: we should set the surface that this saved buffer originates from |
@@ -348,8 +371,8 @@ static void render_view(struct sway_output *output, pixman_region32_t *damage, | |||
348 | if (state->border_left) { | 371 | if (state->border_left) { |
349 | memcpy(&color, colors->child_border, sizeof(float) * 4); | 372 | memcpy(&color, colors->child_border, sizeof(float) * 4); |
350 | premultiply_alpha(color, con->alpha); | 373 | premultiply_alpha(color, con->alpha); |
351 | box.x = state->x; | 374 | box.x = floor(state->x); |
352 | box.y = state->content_y; | 375 | box.y = floor(state->content_y); |
353 | box.width = state->border_thickness; | 376 | box.width = state->border_thickness; |
354 | box.height = state->content_height; | 377 | box.height = state->content_height; |
355 | scale_box(&box, output_scale); | 378 | scale_box(&box, output_scale); |
@@ -361,14 +384,14 @@ static void render_view(struct sway_output *output, pixman_region32_t *damage, | |||
361 | container_current_parent_layout(con); | 384 | container_current_parent_layout(con); |
362 | 385 | ||
363 | if (state->border_right) { | 386 | if (state->border_right) { |
364 | if (!container_is_floating(con) && siblings->length == 1 && layout == L_HORIZ) { | 387 | if (!container_is_current_floating(con) && siblings->length == 1 && layout == L_HORIZ) { |
365 | memcpy(&color, colors->indicator, sizeof(float) * 4); | 388 | memcpy(&color, colors->indicator, sizeof(float) * 4); |
366 | } else { | 389 | } else { |
367 | memcpy(&color, colors->child_border, sizeof(float) * 4); | 390 | memcpy(&color, colors->child_border, sizeof(float) * 4); |
368 | } | 391 | } |
369 | premultiply_alpha(color, con->alpha); | 392 | premultiply_alpha(color, con->alpha); |
370 | box.x = state->content_x + state->content_width; | 393 | box.x = floor(state->content_x + state->content_width); |
371 | box.y = state->content_y; | 394 | box.y = floor(state->content_y); |
372 | box.width = state->border_thickness; | 395 | box.width = state->border_thickness; |
373 | box.height = state->content_height; | 396 | box.height = state->content_height; |
374 | scale_box(&box, output_scale); | 397 | scale_box(&box, output_scale); |
@@ -376,14 +399,14 @@ static void render_view(struct sway_output *output, pixman_region32_t *damage, | |||
376 | } | 399 | } |
377 | 400 | ||
378 | if (state->border_bottom) { | 401 | if (state->border_bottom) { |
379 | if (!container_is_floating(con) && siblings->length == 1 && layout == L_VERT) { | 402 | if (!container_is_current_floating(con) && siblings->length == 1 && layout == L_VERT) { |
380 | memcpy(&color, colors->indicator, sizeof(float) * 4); | 403 | memcpy(&color, colors->indicator, sizeof(float) * 4); |
381 | } else { | 404 | } else { |
382 | memcpy(&color, colors->child_border, sizeof(float) * 4); | 405 | memcpy(&color, colors->child_border, sizeof(float) * 4); |
383 | } | 406 | } |
384 | premultiply_alpha(color, con->alpha); | 407 | premultiply_alpha(color, con->alpha); |
385 | box.x = state->x; | 408 | box.x = floor(state->x); |
386 | box.y = state->content_y + state->content_height; | 409 | box.y = floor(state->content_y + state->content_height); |
387 | box.width = state->width; | 410 | box.width = state->width; |
388 | box.height = state->border_thickness; | 411 | box.height = state->border_thickness; |
389 | scale_box(&box, output_scale); | 412 | scale_box(&box, output_scale); |
@@ -464,9 +487,10 @@ static void render_titlebar(struct sway_output *output, | |||
464 | int ob_marks_x = 0; // output-buffer-local | 487 | int ob_marks_x = 0; // output-buffer-local |
465 | int ob_marks_width = 0; // output-buffer-local | 488 | int ob_marks_width = 0; // output-buffer-local |
466 | if (config->show_marks && marks_texture) { | 489 | if (config->show_marks && marks_texture) { |
467 | struct wlr_box texture_box; | 490 | struct wlr_box texture_box = { |
468 | wlr_texture_get_size(marks_texture, | 491 | .width = marks_texture->width, |
469 | &texture_box.width, &texture_box.height); | 492 | .height = marks_texture->height, |
493 | }; | ||
470 | ob_marks_width = texture_box.width; | 494 | ob_marks_width = texture_box.width; |
471 | 495 | ||
472 | // The marks texture might be shorter than the config->font_height, in | 496 | // The marks texture might be shorter than the config->font_height, in |
@@ -517,15 +541,23 @@ static void render_titlebar(struct sway_output *output, | |||
517 | int ob_title_x = 0; // output-buffer-local | 541 | int ob_title_x = 0; // output-buffer-local |
518 | int ob_title_width = 0; // output-buffer-local | 542 | int ob_title_width = 0; // output-buffer-local |
519 | if (title_texture) { | 543 | if (title_texture) { |
520 | struct wlr_box texture_box; | 544 | struct wlr_box texture_box = { |
521 | wlr_texture_get_size(title_texture, | 545 | .width = title_texture->width, |
522 | &texture_box.width, &texture_box.height); | 546 | .height = title_texture->height, |
547 | }; | ||
548 | |||
549 | // The effective output may be NULL when con is not on any output. | ||
550 | // This can happen because we render all children of containers, | ||
551 | // even those that are out of the bounds of any output. | ||
552 | struct sway_output *effective = container_get_effective_output(con); | ||
553 | float title_scale = effective ? effective->wlr_output->scale : output_scale; | ||
554 | texture_box.width = texture_box.width * output_scale / title_scale; | ||
555 | texture_box.height = texture_box.height * output_scale / title_scale; | ||
523 | ob_title_width = texture_box.width; | 556 | ob_title_width = texture_box.width; |
524 | 557 | ||
525 | // The title texture might be shorter than the config->font_height, | 558 | // The title texture might be shorter than the config->font_height, |
526 | // in which case we need to pad it above and below. | 559 | // in which case we need to pad it above and below. |
527 | int ob_padding_above = round((config->font_baseline - | 560 | int ob_padding_above = round((titlebar_v_padding - |
528 | con->title_baseline + titlebar_v_padding - | ||
529 | titlebar_border_thickness) * output_scale); | 561 | titlebar_border_thickness) * output_scale); |
530 | int ob_padding_below = ob_bg_height - ob_padding_above - | 562 | int ob_padding_below = ob_bg_height - ob_padding_above - |
531 | texture_box.height; | 563 | texture_box.height; |
@@ -660,8 +692,8 @@ static void render_top_border(struct sway_output *output, | |||
660 | // Child border - top edge | 692 | // Child border - top edge |
661 | memcpy(&color, colors->child_border, sizeof(float) * 4); | 693 | memcpy(&color, colors->child_border, sizeof(float) * 4); |
662 | premultiply_alpha(color, con->alpha); | 694 | premultiply_alpha(color, con->alpha); |
663 | box.x = state->x; | 695 | box.x = floor(state->x); |
664 | box.y = state->y; | 696 | box.y = floor(state->y); |
665 | box.width = state->width; | 697 | box.width = state->width; |
666 | box.height = state->border_thickness; | 698 | box.height = state->border_thickness; |
667 | scale_box(&box, output_scale); | 699 | scale_box(&box, output_scale); |
@@ -716,8 +748,8 @@ static void render_containers_linear(struct sway_output *output, | |||
716 | } | 748 | } |
717 | 749 | ||
718 | if (state->border == B_NORMAL) { | 750 | if (state->border == B_NORMAL) { |
719 | render_titlebar(output, damage, child, state->x, | 751 | render_titlebar(output, damage, child, floor(state->x), |
720 | state->y, state->width, colors, | 752 | floor(state->y), state->width, colors, |
721 | title_texture, marks_texture); | 753 | title_texture, marks_texture); |
722 | } else if (state->border == B_PIXEL) { | 754 | } else if (state->border == B_PIXEL) { |
723 | render_top_border(output, damage, child, colors); | 755 | render_top_border(output, damage, child, colors); |
@@ -730,6 +762,14 @@ static void render_containers_linear(struct sway_output *output, | |||
730 | } | 762 | } |
731 | } | 763 | } |
732 | 764 | ||
765 | static bool container_is_focused(struct sway_container *con, void *data) { | ||
766 | return con->current.focused; | ||
767 | } | ||
768 | |||
769 | static bool container_has_focused_child(struct sway_container *con) { | ||
770 | return container_find_child(con, container_is_focused, NULL); | ||
771 | } | ||
772 | |||
733 | /** | 773 | /** |
734 | * Render a container's children using the L_TABBED layout. | 774 | * Render a container's children using the L_TABBED layout. |
735 | */ | 775 | */ |
@@ -761,6 +801,10 @@ static void render_containers_tabbed(struct sway_output *output, | |||
761 | colors = &config->border_colors.focused; | 801 | colors = &config->border_colors.focused; |
762 | title_texture = child->title_focused; | 802 | title_texture = child->title_focused; |
763 | marks_texture = child->marks_focused; | 803 | marks_texture = child->marks_focused; |
804 | } else if (config->has_focused_tab_title && container_has_focused_child(child)) { | ||
805 | colors = &config->border_colors.focused_tab_title; | ||
806 | title_texture = child->title_focused_tab_title; | ||
807 | marks_texture = child->marks_focused_tab_title; | ||
764 | } else if (child == parent->active_child) { | 808 | } else if (child == parent->active_child) { |
765 | colors = &config->border_colors.focused_inactive; | 809 | colors = &config->border_colors.focused_inactive; |
766 | title_texture = child->title_focused_inactive; | 810 | title_texture = child->title_focused_inactive; |
@@ -771,7 +815,7 @@ static void render_containers_tabbed(struct sway_output *output, | |||
771 | marks_texture = child->marks_unfocused; | 815 | marks_texture = child->marks_unfocused; |
772 | } | 816 | } |
773 | 817 | ||
774 | int x = cstate->x + tab_width * i; | 818 | int x = floor(cstate->x + tab_width * i); |
775 | 819 | ||
776 | // Make last tab use the remaining width of the parent | 820 | // Make last tab use the remaining width of the parent |
777 | if (i == parent->children->length - 1) { | 821 | if (i == parent->children->length - 1) { |
@@ -826,7 +870,11 @@ static void render_containers_stacked(struct sway_output *output, | |||
826 | colors = &config->border_colors.focused; | 870 | colors = &config->border_colors.focused; |
827 | title_texture = child->title_focused; | 871 | title_texture = child->title_focused; |
828 | marks_texture = child->marks_focused; | 872 | marks_texture = child->marks_focused; |
829 | } else if (child == parent->active_child) { | 873 | } else if (config->has_focused_tab_title && container_has_focused_child(child)) { |
874 | colors = &config->border_colors.focused_tab_title; | ||
875 | title_texture = child->title_focused_tab_title; | ||
876 | marks_texture = child->marks_focused_tab_title; | ||
877 | } else if (child == parent->active_child) { | ||
830 | colors = &config->border_colors.focused_inactive; | 878 | colors = &config->border_colors.focused_inactive; |
831 | title_texture = child->title_focused_inactive; | 879 | title_texture = child->title_focused_inactive; |
832 | marks_texture = child->marks_focused_inactive; | 880 | marks_texture = child->marks_focused_inactive; |
@@ -884,8 +932,8 @@ static void render_container(struct sway_output *output, | |||
884 | struct parent_data data = { | 932 | struct parent_data data = { |
885 | .layout = con->current.layout, | 933 | .layout = con->current.layout, |
886 | .box = { | 934 | .box = { |
887 | .x = con->current.x, | 935 | .x = floor(con->current.x), |
888 | .y = con->current.y, | 936 | .y = floor(con->current.y), |
889 | .width = con->current.width, | 937 | .width = con->current.width, |
890 | .height = con->current.height, | 938 | .height = con->current.height, |
891 | }, | 939 | }, |
@@ -901,8 +949,8 @@ static void render_workspace(struct sway_output *output, | |||
901 | struct parent_data data = { | 949 | struct parent_data data = { |
902 | .layout = ws->current.layout, | 950 | .layout = ws->current.layout, |
903 | .box = { | 951 | .box = { |
904 | .x = ws->current.x, | 952 | .x = floor(ws->current.x), |
905 | .y = ws->current.y, | 953 | .y = floor(ws->current.y), |
906 | .width = ws->current.width, | 954 | .width = ws->current.width, |
907 | .height = ws->current.height, | 955 | .height = ws->current.height, |
908 | }, | 956 | }, |
@@ -936,8 +984,8 @@ static void render_floating_container(struct sway_output *soutput, | |||
936 | } | 984 | } |
937 | 985 | ||
938 | if (con->current.border == B_NORMAL) { | 986 | if (con->current.border == B_NORMAL) { |
939 | render_titlebar(soutput, damage, con, con->current.x, | 987 | render_titlebar(soutput, damage, con, floor(con->current.x), |
940 | con->current.y, con->current.width, colors, | 988 | floor(con->current.y), con->current.width, colors, |
941 | title_texture, marks_texture); | 989 | title_texture, marks_texture); |
942 | } else if (con->current.border == B_PIXEL) { | 990 | } else if (con->current.border == B_PIXEL) { |
943 | render_top_border(soutput, damage, con, colors); | 991 | render_top_border(soutput, damage, con, colors); |
@@ -959,7 +1007,7 @@ static void render_floating(struct sway_output *soutput, | |||
959 | } | 1007 | } |
960 | for (int k = 0; k < ws->current.floating->length; ++k) { | 1008 | for (int k = 0; k < ws->current.floating->length; ++k) { |
961 | struct sway_container *floater = ws->current.floating->items[k]; | 1009 | struct sway_container *floater = ws->current.floating->items[k]; |
962 | if (floater->fullscreen_mode != FULLSCREEN_NONE) { | 1010 | if (floater->current.fullscreen_mode != FULLSCREEN_NONE) { |
963 | continue; | 1011 | continue; |
964 | } | 1012 | } |
965 | render_floating_container(soutput, damage, floater); | 1013 | render_floating_container(soutput, damage, floater); |
@@ -979,13 +1027,7 @@ static void render_seatops(struct sway_output *output, | |||
979 | void output_render(struct sway_output *output, struct timespec *when, | 1027 | void output_render(struct sway_output *output, struct timespec *when, |
980 | pixman_region32_t *damage) { | 1028 | pixman_region32_t *damage) { |
981 | struct wlr_output *wlr_output = output->wlr_output; | 1029 | struct wlr_output *wlr_output = output->wlr_output; |
982 | 1030 | struct wlr_renderer *renderer = output->server->renderer; | |
983 | struct wlr_renderer *renderer = | ||
984 | wlr_backend_get_renderer(wlr_output->backend); | ||
985 | if (!sway_assert(renderer != NULL, | ||
986 | "expected the output backend to have a renderer")) { | ||
987 | return; | ||
988 | } | ||
989 | 1031 | ||
990 | struct sway_workspace *workspace = output->current.active_workspace; | 1032 | struct sway_workspace *workspace = output->current.active_workspace; |
991 | if (workspace == NULL) { | 1033 | if (workspace == NULL) { |
@@ -999,6 +1041,12 @@ void output_render(struct sway_output *output, struct timespec *when, | |||
999 | 1041 | ||
1000 | wlr_renderer_begin(renderer, wlr_output->width, wlr_output->height); | 1042 | wlr_renderer_begin(renderer, wlr_output->width, wlr_output->height); |
1001 | 1043 | ||
1044 | if (debug.damage == DAMAGE_RERENDER) { | ||
1045 | int width, height; | ||
1046 | wlr_output_transformed_resolution(wlr_output, &width, &height); | ||
1047 | pixman_region32_union_rect(damage, damage, 0, 0, width, height); | ||
1048 | } | ||
1049 | |||
1002 | if (!pixman_region32_not_empty(damage)) { | 1050 | if (!pixman_region32_not_empty(damage)) { |
1003 | // Output isn't damaged but needs buffer swap | 1051 | // Output isn't damaged but needs buffer swap |
1004 | goto renderer_end; | 1052 | goto renderer_end; |
@@ -1006,10 +1054,41 @@ void output_render(struct sway_output *output, struct timespec *when, | |||
1006 | 1054 | ||
1007 | if (debug.damage == DAMAGE_HIGHLIGHT) { | 1055 | if (debug.damage == DAMAGE_HIGHLIGHT) { |
1008 | wlr_renderer_clear(renderer, (float[]){1, 1, 0, 1}); | 1056 | wlr_renderer_clear(renderer, (float[]){1, 1, 0, 1}); |
1009 | } else if (debug.damage == DAMAGE_RERENDER) { | 1057 | } |
1010 | int width, height; | 1058 | |
1011 | wlr_output_transformed_resolution(wlr_output, &width, &height); | 1059 | if (server.session_lock.locked) { |
1012 | pixman_region32_union_rect(damage, damage, 0, 0, width, height); | 1060 | float clear_color[] = {0.0f, 0.0f, 0.0f, 1.0f}; |
1061 | if (server.session_lock.lock == NULL) { | ||
1062 | // abandoned lock -> red BG | ||
1063 | clear_color[0] = 1.f; | ||
1064 | } | ||
1065 | int nrects; | ||
1066 | pixman_box32_t *rects = pixman_region32_rectangles(damage, &nrects); | ||
1067 | for (int i = 0; i < nrects; ++i) { | ||
1068 | scissor_output(wlr_output, &rects[i]); | ||
1069 | wlr_renderer_clear(renderer, clear_color); | ||
1070 | } | ||
1071 | |||
1072 | if (server.session_lock.lock != NULL) { | ||
1073 | struct render_data data = { | ||
1074 | .damage = damage, | ||
1075 | .alpha = 1.0f, | ||
1076 | }; | ||
1077 | |||
1078 | struct wlr_session_lock_surface_v1 *lock_surface; | ||
1079 | wl_list_for_each(lock_surface, &server.session_lock.lock->surfaces, link) { | ||
1080 | if (lock_surface->output != wlr_output) { | ||
1081 | continue; | ||
1082 | } | ||
1083 | if (!lock_surface->mapped) { | ||
1084 | continue; | ||
1085 | } | ||
1086 | |||
1087 | output_surface_for_each_surface(output, lock_surface->surface, | ||
1088 | 0.0, 0.0, render_surface_iterator, &data); | ||
1089 | } | ||
1090 | } | ||
1091 | goto renderer_end; | ||
1013 | } | 1092 | } |
1014 | 1093 | ||
1015 | if (output_has_opaque_overlay_layer_surface(output)) { | 1094 | if (output_has_opaque_overlay_layer_surface(output)) { |
@@ -1110,7 +1189,7 @@ renderer_end: | |||
1110 | wlr_region_transform(&frame_damage, &output->damage->current, | 1189 | wlr_region_transform(&frame_damage, &output->damage->current, |
1111 | transform, width, height); | 1190 | transform, width, height); |
1112 | 1191 | ||
1113 | if (debug.damage == DAMAGE_HIGHLIGHT) { | 1192 | if (debug.damage != DAMAGE_DEFAULT) { |
1114 | pixman_region32_union_rect(&frame_damage, &frame_damage, | 1193 | pixman_region32_union_rect(&frame_damage, &frame_damage, |
1115 | 0, 0, wlr_output->width, wlr_output->height); | 1194 | 0, 0, wlr_output->width, wlr_output->height); |
1116 | } | 1195 | } |
diff --git a/sway/desktop/surface.c b/sway/desktop/surface.c index 767b2045..1d7b536d 100644 --- a/sway/desktop/surface.c +++ b/sway/desktop/surface.c | |||
@@ -1,7 +1,7 @@ | |||
1 | #define _POSIX_C_SOURCE 200112L | 1 | #define _POSIX_C_SOURCE 200112L |
2 | #include <stdlib.h> | 2 | #include <stdlib.h> |
3 | #include <time.h> | 3 | #include <time.h> |
4 | #include <wlr/types/wlr_surface.h> | 4 | #include <wlr/types/wlr_compositor.h> |
5 | #include "sway/server.h" | 5 | #include "sway/server.h" |
6 | #include "sway/surface.h" | 6 | #include "sway/surface.h" |
7 | 7 | ||
diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c index eac38991..f5a3a053 100644 --- a/sway/desktop/transaction.c +++ b/sway/desktop/transaction.c | |||
@@ -35,6 +35,8 @@ struct sway_transaction_instruction { | |||
35 | struct sway_container_state container_state; | 35 | struct sway_container_state container_state; |
36 | }; | 36 | }; |
37 | uint32_t serial; | 37 | uint32_t serial; |
38 | bool server_request; | ||
39 | bool waiting; | ||
38 | }; | 40 | }; |
39 | 41 | ||
40 | static struct sway_transaction *transaction_create(void) { | 42 | static struct sway_transaction *transaction_create(void) { |
@@ -86,7 +88,11 @@ static void transaction_destroy(struct sway_transaction *transaction) { | |||
86 | static void copy_output_state(struct sway_output *output, | 88 | static void copy_output_state(struct sway_output *output, |
87 | struct sway_transaction_instruction *instruction) { | 89 | struct sway_transaction_instruction *instruction) { |
88 | struct sway_output_state *state = &instruction->output_state; | 90 | struct sway_output_state *state = &instruction->output_state; |
89 | state->workspaces = create_list(); | 91 | if (state->workspaces) { |
92 | state->workspaces->length = 0; | ||
93 | } else { | ||
94 | state->workspaces = create_list(); | ||
95 | } | ||
90 | list_cat(state->workspaces, output->workspaces); | 96 | list_cat(state->workspaces, output->workspaces); |
91 | 97 | ||
92 | state->active_workspace = output_get_active_workspace(output); | 98 | state->active_workspace = output_get_active_workspace(output); |
@@ -104,8 +110,16 @@ static void copy_workspace_state(struct sway_workspace *ws, | |||
104 | state->layout = ws->layout; | 110 | state->layout = ws->layout; |
105 | 111 | ||
106 | state->output = ws->output; | 112 | state->output = ws->output; |
107 | state->floating = create_list(); | 113 | if (state->floating) { |
108 | state->tiling = create_list(); | 114 | state->floating->length = 0; |
115 | } else { | ||
116 | state->floating = create_list(); | ||
117 | } | ||
118 | if (state->tiling) { | ||
119 | state->tiling->length = 0; | ||
120 | } else { | ||
121 | state->tiling = create_list(); | ||
122 | } | ||
109 | list_cat(state->floating, ws->floating); | 123 | list_cat(state->floating, ws->floating); |
110 | list_cat(state->tiling, ws->tiling); | 124 | list_cat(state->tiling, ws->tiling); |
111 | 125 | ||
@@ -115,8 +129,8 @@ static void copy_workspace_state(struct sway_workspace *ws, | |||
115 | // Set focused_inactive_child to the direct tiling child | 129 | // Set focused_inactive_child to the direct tiling child |
116 | struct sway_container *focus = seat_get_focus_inactive_tiling(seat, ws); | 130 | struct sway_container *focus = seat_get_focus_inactive_tiling(seat, ws); |
117 | if (focus) { | 131 | if (focus) { |
118 | while (focus->parent) { | 132 | while (focus->pending.parent) { |
119 | focus = focus->parent; | 133 | focus = focus->pending.parent; |
120 | } | 134 | } |
121 | } | 135 | } |
122 | state->focused_inactive_child = focus; | 136 | state->focused_inactive_child = focus; |
@@ -126,28 +140,19 @@ static void copy_container_state(struct sway_container *container, | |||
126 | struct sway_transaction_instruction *instruction) { | 140 | struct sway_transaction_instruction *instruction) { |
127 | struct sway_container_state *state = &instruction->container_state; | 141 | struct sway_container_state *state = &instruction->container_state; |
128 | 142 | ||
129 | state->layout = container->layout; | 143 | if (state->children) { |
130 | state->x = container->x; | 144 | list_free(state->children); |
131 | state->y = container->y; | 145 | } |
132 | state->width = container->width; | 146 | |
133 | state->height = container->height; | 147 | memcpy(state, &container->pending, sizeof(struct sway_container_state)); |
134 | state->fullscreen_mode = container->fullscreen_mode; | ||
135 | state->parent = container->parent; | ||
136 | state->workspace = container->workspace; | ||
137 | state->border = container->border; | ||
138 | state->border_thickness = container->border_thickness; | ||
139 | state->border_top = container->border_top; | ||
140 | state->border_left = container->border_left; | ||
141 | state->border_right = container->border_right; | ||
142 | state->border_bottom = container->border_bottom; | ||
143 | state->content_x = container->content_x; | ||
144 | state->content_y = container->content_y; | ||
145 | state->content_width = container->content_width; | ||
146 | state->content_height = container->content_height; | ||
147 | 148 | ||
148 | if (!container->view) { | 149 | if (!container->view) { |
150 | // We store a copy of the child list to avoid having it mutated after | ||
151 | // we copy the state. | ||
149 | state->children = create_list(); | 152 | state->children = create_list(); |
150 | list_cat(state->children, container->children); | 153 | list_cat(state->children, container->pending.children); |
154 | } else { | ||
155 | state->children = NULL; | ||
151 | } | 156 | } |
152 | 157 | ||
153 | struct sway_seat *seat = input_manager_current_seat(); | 158 | struct sway_seat *seat = input_manager_current_seat(); |
@@ -161,14 +166,36 @@ static void copy_container_state(struct sway_container *container, | |||
161 | } | 166 | } |
162 | 167 | ||
163 | static void transaction_add_node(struct sway_transaction *transaction, | 168 | static void transaction_add_node(struct sway_transaction *transaction, |
164 | struct sway_node *node) { | 169 | struct sway_node *node, bool server_request) { |
165 | struct sway_transaction_instruction *instruction = | 170 | struct sway_transaction_instruction *instruction = NULL; |
166 | calloc(1, sizeof(struct sway_transaction_instruction)); | 171 | |
167 | if (!sway_assert(instruction, "Unable to allocate instruction")) { | 172 | // Check if we have an instruction for this node already, in which case we |
168 | return; | 173 | // update that instead of creating a new one. |
174 | if (node->ntxnrefs > 0) { | ||
175 | for (int idx = 0; idx < transaction->instructions->length; idx++) { | ||
176 | struct sway_transaction_instruction *other = | ||
177 | transaction->instructions->items[idx]; | ||
178 | if (other->node == node) { | ||
179 | instruction = other; | ||
180 | break; | ||
181 | } | ||
182 | } | ||
183 | } | ||
184 | |||
185 | if (!instruction) { | ||
186 | instruction = calloc(1, sizeof(struct sway_transaction_instruction)); | ||
187 | if (!sway_assert(instruction, "Unable to allocate instruction")) { | ||
188 | return; | ||
189 | } | ||
190 | instruction->transaction = transaction; | ||
191 | instruction->node = node; | ||
192 | instruction->server_request = server_request; | ||
193 | |||
194 | list_add(transaction->instructions, instruction); | ||
195 | node->ntxnrefs++; | ||
196 | } else if (server_request) { | ||
197 | instruction->server_request = true; | ||
169 | } | 198 | } |
170 | instruction->transaction = transaction; | ||
171 | instruction->node = node; | ||
172 | 199 | ||
173 | switch (node->type) { | 200 | switch (node->type) { |
174 | case N_ROOT: | 201 | case N_ROOT: |
@@ -183,9 +210,6 @@ static void transaction_add_node(struct sway_transaction *transaction, | |||
183 | copy_container_state(node->sway_container, instruction); | 210 | copy_container_state(node->sway_container, instruction); |
184 | break; | 211 | break; |
185 | } | 212 | } |
186 | |||
187 | list_add(transaction->instructions, instruction); | ||
188 | node->ntxnrefs++; | ||
189 | } | 213 | } |
190 | 214 | ||
191 | static void apply_output_state(struct sway_output *output, | 215 | static void apply_output_state(struct sway_output *output, |
@@ -214,8 +238,8 @@ static void apply_container_state(struct sway_container *container, | |||
214 | struct sway_saved_buffer *saved_buf; | 238 | struct sway_saved_buffer *saved_buf; |
215 | wl_list_for_each(saved_buf, &view->saved_buffers, link) { | 239 | wl_list_for_each(saved_buf, &view->saved_buffers, link) { |
216 | struct wlr_box box = { | 240 | struct wlr_box box = { |
217 | .x = container->current.content_x - view->saved_geometry.x + saved_buf->x, | 241 | .x = saved_buf->x - view->saved_geometry.x, |
218 | .y = container->current.content_y - view->saved_geometry.y + saved_buf->y, | 242 | .y = saved_buf->y - view->saved_geometry.y, |
219 | .width = saved_buf->width, | 243 | .width = saved_buf->width, |
220 | .height = saved_buf->height, | 244 | .height = saved_buf->height, |
221 | }; | 245 | }; |
@@ -238,6 +262,13 @@ static void apply_container_state(struct sway_container *container, | |||
238 | } | 262 | } |
239 | } | 263 | } |
240 | 264 | ||
265 | // If the view hasn't responded to the configure, center it within | ||
266 | // the container. This is important for fullscreen views which | ||
267 | // refuse to resize to the size of the output. | ||
268 | if (view && view->surface) { | ||
269 | view_center_surface(view); | ||
270 | } | ||
271 | |||
241 | // Damage the new location | 272 | // Damage the new location |
242 | desktop_damage_whole_container(container); | 273 | desktop_damage_whole_container(container); |
243 | if (view && view->surface) { | 274 | if (view && view->surface) { |
@@ -251,24 +282,6 @@ static void apply_container_state(struct sway_container *container, | |||
251 | desktop_damage_box(&box); | 282 | desktop_damage_box(&box); |
252 | } | 283 | } |
253 | 284 | ||
254 | // If the view hasn't responded to the configure, center it within | ||
255 | // the container. This is important for fullscreen views which | ||
256 | // refuse to resize to the size of the output. | ||
257 | if (view && view->surface) { | ||
258 | if (view->geometry.width < container->current.content_width) { | ||
259 | container->surface_x = container->current.content_x + | ||
260 | (container->current.content_width - view->geometry.width) / 2; | ||
261 | } else { | ||
262 | container->surface_x = container->current.content_x; | ||
263 | } | ||
264 | if (view->geometry.height < container->current.content_height) { | ||
265 | container->surface_y = container->current.content_y + | ||
266 | (container->current.content_height - view->geometry.height) / 2; | ||
267 | } else { | ||
268 | container->surface_y = container->current.content_y; | ||
269 | } | ||
270 | } | ||
271 | |||
272 | if (!container->node.destroying) { | 285 | if (!container->node.destroying) { |
273 | container_discover_outputs(container); | 286 | container_discover_outputs(container); |
274 | } | 287 | } |
@@ -317,70 +330,25 @@ static void transaction_apply(struct sway_transaction *transaction) { | |||
317 | cursor_rebase_all(); | 330 | cursor_rebase_all(); |
318 | } | 331 | } |
319 | 332 | ||
320 | static void transaction_commit(struct sway_transaction *transaction); | 333 | static void transaction_commit_pending(void); |
321 | 334 | ||
322 | // Return true if both transactions operate on the same nodes | 335 | static void transaction_progress(void) { |
323 | static bool transaction_same_nodes(struct sway_transaction *a, | 336 | if (!server.queued_transaction) { |
324 | struct sway_transaction *b) { | ||
325 | if (a->instructions->length != b->instructions->length) { | ||
326 | return false; | ||
327 | } | ||
328 | for (int i = 0; i < a->instructions->length; ++i) { | ||
329 | struct sway_transaction_instruction *a_inst = a->instructions->items[i]; | ||
330 | struct sway_transaction_instruction *b_inst = b->instructions->items[i]; | ||
331 | if (a_inst->node != b_inst->node) { | ||
332 | return false; | ||
333 | } | ||
334 | } | ||
335 | return true; | ||
336 | } | ||
337 | |||
338 | static void transaction_progress_queue(void) { | ||
339 | if (!server.transactions->length) { | ||
340 | return; | 337 | return; |
341 | } | 338 | } |
342 | // Only the first transaction in the queue is committed, so that's the one | 339 | if (server.queued_transaction->num_waiting > 0) { |
343 | // we try to process. | ||
344 | struct sway_transaction *transaction = server.transactions->items[0]; | ||
345 | if (transaction->num_waiting) { | ||
346 | return; | 340 | return; |
347 | } | 341 | } |
348 | transaction_apply(transaction); | 342 | transaction_apply(server.queued_transaction); |
349 | transaction_destroy(transaction); | 343 | transaction_destroy(server.queued_transaction); |
350 | list_del(server.transactions, 0); | 344 | server.queued_transaction = NULL; |
351 | 345 | ||
352 | if (server.transactions->length == 0) { | 346 | if (!server.pending_transaction) { |
353 | // The transaction queue is empty, so we're done. | ||
354 | sway_idle_inhibit_v1_check_active(server.idle_inhibit_manager_v1); | 347 | sway_idle_inhibit_v1_check_active(server.idle_inhibit_manager_v1); |
355 | return; | 348 | return; |
356 | } | 349 | } |
357 | 350 | ||
358 | // If there's a bunch of consecutive transactions which all apply to the | 351 | transaction_commit_pending(); |
359 | // same views, skip all except the last one. | ||
360 | while (server.transactions->length >= 2) { | ||
361 | struct sway_transaction *txn = server.transactions->items[0]; | ||
362 | struct sway_transaction *dup = NULL; | ||
363 | |||
364 | for (int i = 1; i < server.transactions->length; i++) { | ||
365 | struct sway_transaction *maybe_dup = server.transactions->items[i]; | ||
366 | if (transaction_same_nodes(txn, maybe_dup)) { | ||
367 | dup = maybe_dup; | ||
368 | break; | ||
369 | } | ||
370 | } | ||
371 | |||
372 | if (dup) { | ||
373 | list_del(server.transactions, 0); | ||
374 | transaction_destroy(txn); | ||
375 | } else { | ||
376 | break; | ||
377 | } | ||
378 | } | ||
379 | |||
380 | // We again commit the first transaction in the queue to process it. | ||
381 | transaction = server.transactions->items[0]; | ||
382 | transaction_commit(transaction); | ||
383 | transaction_progress_queue(); | ||
384 | } | 352 | } |
385 | 353 | ||
386 | static int handle_timeout(void *data) { | 354 | static int handle_timeout(void *data) { |
@@ -388,7 +356,7 @@ static int handle_timeout(void *data) { | |||
388 | sway_log(SWAY_DEBUG, "Transaction %p timed out (%zi waiting)", | 356 | sway_log(SWAY_DEBUG, "Transaction %p timed out (%zi waiting)", |
389 | transaction, transaction->num_waiting); | 357 | transaction, transaction->num_waiting); |
390 | transaction->num_waiting = 0; | 358 | transaction->num_waiting = 0; |
391 | transaction_progress_queue(); | 359 | transaction_progress(); |
392 | return 0; | 360 | return 0; |
393 | } | 361 | } |
394 | 362 | ||
@@ -400,6 +368,9 @@ static bool should_configure(struct sway_node *node, | |||
400 | if (node->destroying) { | 368 | if (node->destroying) { |
401 | return false; | 369 | return false; |
402 | } | 370 | } |
371 | if (!instruction->server_request) { | ||
372 | return false; | ||
373 | } | ||
403 | struct sway_container_state *cstate = &node->sway_container->current; | 374 | struct sway_container_state *cstate = &node->sway_container->current; |
404 | struct sway_container_state *istate = &instruction->container_state; | 375 | struct sway_container_state *istate = &instruction->container_state; |
405 | #if HAVE_XWAYLAND | 376 | #if HAVE_XWAYLAND |
@@ -431,13 +402,18 @@ static void transaction_commit(struct sway_transaction *transaction) { | |||
431 | struct sway_transaction_instruction *instruction = | 402 | struct sway_transaction_instruction *instruction = |
432 | transaction->instructions->items[i]; | 403 | transaction->instructions->items[i]; |
433 | struct sway_node *node = instruction->node; | 404 | struct sway_node *node = instruction->node; |
405 | bool hidden = node_is_view(node) && !node->destroying && | ||
406 | !view_is_visible(node->sway_container->view); | ||
434 | if (should_configure(node, instruction)) { | 407 | if (should_configure(node, instruction)) { |
435 | instruction->serial = view_configure(node->sway_container->view, | 408 | instruction->serial = view_configure(node->sway_container->view, |
436 | instruction->container_state.content_x, | 409 | instruction->container_state.content_x, |
437 | instruction->container_state.content_y, | 410 | instruction->container_state.content_y, |
438 | instruction->container_state.content_width, | 411 | instruction->container_state.content_width, |
439 | instruction->container_state.content_height); | 412 | instruction->container_state.content_height); |
440 | ++transaction->num_waiting; | 413 | if (!hidden) { |
414 | instruction->waiting = true; | ||
415 | ++transaction->num_waiting; | ||
416 | } | ||
441 | 417 | ||
442 | // From here on we are rendering a saved buffer of the view, which | 418 | // From here on we are rendering a saved buffer of the view, which |
443 | // means we can send a frame done event to make the client redraw it | 419 | // means we can send a frame done event to make the client redraw it |
@@ -448,7 +424,8 @@ static void transaction_commit(struct sway_transaction *transaction) { | |||
448 | wlr_surface_send_frame_done( | 424 | wlr_surface_send_frame_done( |
449 | node->sway_container->view->surface, &now); | 425 | node->sway_container->view->surface, &now); |
450 | } | 426 | } |
451 | if (node_is_view(node) && wl_list_empty(&node->sway_container->view->saved_buffers)) { | 427 | if (!hidden && node_is_view(node) && |
428 | wl_list_empty(&node->sway_container->view->saved_buffers)) { | ||
452 | view_save_buffer(node->sway_container->view); | 429 | view_save_buffer(node->sway_container->view); |
453 | memcpy(&node->sway_container->view->saved_geometry, | 430 | memcpy(&node->sway_container->view->saved_geometry, |
454 | &node->sway_container->view->geometry, | 431 | &node->sway_container->view->geometry, |
@@ -483,6 +460,17 @@ static void transaction_commit(struct sway_transaction *transaction) { | |||
483 | } | 460 | } |
484 | } | 461 | } |
485 | 462 | ||
463 | static void transaction_commit_pending(void) { | ||
464 | if (server.queued_transaction) { | ||
465 | return; | ||
466 | } | ||
467 | struct sway_transaction *transaction = server.pending_transaction; | ||
468 | server.pending_transaction = NULL; | ||
469 | server.queued_transaction = transaction; | ||
470 | transaction_commit(transaction); | ||
471 | transaction_progress(); | ||
472 | } | ||
473 | |||
486 | static void set_instruction_ready( | 474 | static void set_instruction_ready( |
487 | struct sway_transaction_instruction *instruction) { | 475 | struct sway_transaction_instruction *instruction) { |
488 | struct sway_transaction *transaction = instruction->transaction; | 476 | struct sway_transaction *transaction = instruction->transaction; |
@@ -501,13 +489,14 @@ static void set_instruction_ready( | |||
501 | } | 489 | } |
502 | 490 | ||
503 | // If the transaction has timed out then its num_waiting will be 0 already. | 491 | // If the transaction has timed out then its num_waiting will be 0 already. |
504 | if (transaction->num_waiting > 0 && --transaction->num_waiting == 0) { | 492 | if (instruction->waiting && transaction->num_waiting > 0 && |
493 | --transaction->num_waiting == 0) { | ||
505 | sway_log(SWAY_DEBUG, "Transaction %p is ready", transaction); | 494 | sway_log(SWAY_DEBUG, "Transaction %p is ready", transaction); |
506 | wl_event_source_timer_update(transaction->timer, 0); | 495 | wl_event_source_timer_update(transaction->timer, 0); |
507 | } | 496 | } |
508 | 497 | ||
509 | instruction->node->instruction = NULL; | 498 | instruction->node->instruction = NULL; |
510 | transaction_progress_queue(); | 499 | transaction_progress(); |
511 | } | 500 | } |
512 | 501 | ||
513 | void transaction_notify_view_ready_by_serial(struct sway_view *view, | 502 | void transaction_notify_view_ready_by_serial(struct sway_view *view, |
@@ -532,36 +521,32 @@ void transaction_notify_view_ready_by_geometry(struct sway_view *view, | |||
532 | } | 521 | } |
533 | } | 522 | } |
534 | 523 | ||
535 | void transaction_notify_view_ready_immediately(struct sway_view *view) { | 524 | static void _transaction_commit_dirty(bool server_request) { |
536 | struct sway_transaction_instruction *instruction = | ||
537 | view->container->node.instruction; | ||
538 | if (instruction != NULL) { | ||
539 | set_instruction_ready(instruction); | ||
540 | } | ||
541 | } | ||
542 | |||
543 | void transaction_commit_dirty(void) { | ||
544 | if (!server.dirty_nodes->length) { | 525 | if (!server.dirty_nodes->length) { |
545 | return; | 526 | return; |
546 | } | 527 | } |
547 | struct sway_transaction *transaction = transaction_create(); | 528 | |
548 | if (!transaction) { | 529 | if (!server.pending_transaction) { |
549 | return; | 530 | server.pending_transaction = transaction_create(); |
531 | if (!server.pending_transaction) { | ||
532 | return; | ||
533 | } | ||
550 | } | 534 | } |
535 | |||
551 | for (int i = 0; i < server.dirty_nodes->length; ++i) { | 536 | for (int i = 0; i < server.dirty_nodes->length; ++i) { |
552 | struct sway_node *node = server.dirty_nodes->items[i]; | 537 | struct sway_node *node = server.dirty_nodes->items[i]; |
553 | transaction_add_node(transaction, node); | 538 | transaction_add_node(server.pending_transaction, node, server_request); |
554 | node->dirty = false; | 539 | node->dirty = false; |
555 | } | 540 | } |
556 | server.dirty_nodes->length = 0; | 541 | server.dirty_nodes->length = 0; |
557 | 542 | ||
558 | list_add(server.transactions, transaction); | 543 | transaction_commit_pending(); |
544 | } | ||
559 | 545 | ||
560 | // We only commit the first transaction added to the queue. | 546 | void transaction_commit_dirty(void) { |
561 | if (server.transactions->length == 1) { | 547 | _transaction_commit_dirty(true); |
562 | transaction_commit(transaction); | 548 | } |
563 | // Attempting to progress the queue here is useful | 549 | |
564 | // if the transaction has nothing to wait for. | 550 | void transaction_commit_dirty_client(void) { |
565 | transaction_progress_queue(); | 551 | _transaction_commit_dirty(false); |
566 | } | ||
567 | } | 552 | } |
diff --git a/sway/desktop/xdg_shell.c b/sway/desktop/xdg_shell.c index 667fb9e5..8da922d5 100644 --- a/sway/desktop/xdg_shell.c +++ b/sway/desktop/xdg_shell.c | |||
@@ -21,18 +21,15 @@ | |||
21 | 21 | ||
22 | static const struct sway_view_child_impl popup_impl; | 22 | static const struct sway_view_child_impl popup_impl; |
23 | 23 | ||
24 | static void popup_get_root_coords(struct sway_view_child *child, | 24 | static void popup_get_view_coords(struct sway_view_child *child, |
25 | int *root_sx, int *root_sy) { | 25 | int *sx, int *sy) { |
26 | struct sway_xdg_popup *popup = (struct sway_xdg_popup *)child; | 26 | struct sway_xdg_popup *popup = (struct sway_xdg_popup *)child; |
27 | struct wlr_xdg_surface *surface = popup->wlr_xdg_surface; | 27 | struct wlr_xdg_popup *wlr_popup = popup->wlr_xdg_popup; |
28 | 28 | ||
29 | int x_offset = -child->view->geometry.x - surface->geometry.x; | 29 | wlr_xdg_popup_get_toplevel_coords(wlr_popup, |
30 | int y_offset = -child->view->geometry.y - surface->geometry.y; | 30 | wlr_popup->current.geometry.x - wlr_popup->base->current.geometry.x, |
31 | 31 | wlr_popup->current.geometry.y - wlr_popup->base->current.geometry.y, | |
32 | wlr_xdg_popup_get_toplevel_coords(surface->popup, | 32 | sx, sy); |
33 | x_offset + surface->popup->geometry.x, | ||
34 | y_offset + surface->popup->geometry.y, | ||
35 | root_sx, root_sy); | ||
36 | } | 33 | } |
37 | 34 | ||
38 | static void popup_destroy(struct sway_view_child *child) { | 35 | static void popup_destroy(struct sway_view_child *child) { |
@@ -47,7 +44,7 @@ static void popup_destroy(struct sway_view_child *child) { | |||
47 | } | 44 | } |
48 | 45 | ||
49 | static const struct sway_view_child_impl popup_impl = { | 46 | static const struct sway_view_child_impl popup_impl = { |
50 | .get_root_coords = popup_get_root_coords, | 47 | .get_view_coords = popup_get_view_coords, |
51 | .destroy = popup_destroy, | 48 | .destroy = popup_destroy, |
52 | }; | 49 | }; |
53 | 50 | ||
@@ -68,15 +65,15 @@ static void popup_handle_destroy(struct wl_listener *listener, void *data) { | |||
68 | 65 | ||
69 | static void popup_unconstrain(struct sway_xdg_popup *popup) { | 66 | static void popup_unconstrain(struct sway_xdg_popup *popup) { |
70 | struct sway_view *view = popup->child.view; | 67 | struct sway_view *view = popup->child.view; |
71 | struct wlr_xdg_popup *wlr_popup = popup->wlr_xdg_surface->popup; | 68 | struct wlr_xdg_popup *wlr_popup = popup->wlr_xdg_popup; |
72 | 69 | ||
73 | struct sway_output *output = view->container->workspace->output; | 70 | struct sway_output *output = view->container->pending.workspace->output; |
74 | 71 | ||
75 | // the output box expressed in the coordinate system of the toplevel parent | 72 | // the output box expressed in the coordinate system of the toplevel parent |
76 | // of the popup | 73 | // of the popup |
77 | struct wlr_box output_toplevel_sx_box = { | 74 | struct wlr_box output_toplevel_sx_box = { |
78 | .x = output->lx - view->container->content_x, | 75 | .x = output->lx - view->container->pending.content_x + view->geometry.x, |
79 | .y = output->ly - view->container->content_y, | 76 | .y = output->ly - view->container->pending.content_y + view->geometry.y, |
80 | .width = output->width, | 77 | .width = output->width, |
81 | .height = output->height, | 78 | .height = output->height, |
82 | }; | 79 | }; |
@@ -94,7 +91,7 @@ static struct sway_xdg_popup *popup_create( | |||
94 | return NULL; | 91 | return NULL; |
95 | } | 92 | } |
96 | view_child_init(&popup->child, &popup_impl, view, xdg_surface->surface); | 93 | view_child_init(&popup->child, &popup_impl, view, xdg_surface->surface); |
97 | popup->wlr_xdg_surface = xdg_surface; | 94 | popup->wlr_xdg_popup = xdg_surface->popup; |
98 | 95 | ||
99 | wl_signal_add(&xdg_surface->events.new_popup, &popup->new_popup); | 96 | wl_signal_add(&xdg_surface->events.new_popup, &popup->new_popup); |
100 | popup->new_popup.notify = popup_handle_new_popup; | 97 | popup->new_popup.notify = popup_handle_new_popup; |
@@ -122,7 +119,7 @@ static struct sway_xdg_shell_view *xdg_shell_view_from_view( | |||
122 | static void get_constraints(struct sway_view *view, double *min_width, | 119 | static void get_constraints(struct sway_view *view, double *min_width, |
123 | double *max_width, double *min_height, double *max_height) { | 120 | double *max_width, double *min_height, double *max_height) { |
124 | struct wlr_xdg_toplevel_state *state = | 121 | struct wlr_xdg_toplevel_state *state = |
125 | &view->wlr_xdg_surface->toplevel->current; | 122 | &view->wlr_xdg_toplevel->current; |
126 | *min_width = state->min_width > 0 ? state->min_width : DBL_MIN; | 123 | *min_width = state->min_width > 0 ? state->min_width : DBL_MIN; |
127 | *max_width = state->max_width > 0 ? state->max_width : DBL_MAX; | 124 | *max_width = state->max_width > 0 ? state->max_width : DBL_MAX; |
128 | *min_height = state->min_height > 0 ? state->min_height : DBL_MIN; | 125 | *min_height = state->min_height > 0 ? state->min_height : DBL_MIN; |
@@ -136,9 +133,9 @@ static const char *get_string_prop(struct sway_view *view, | |||
136 | } | 133 | } |
137 | switch (prop) { | 134 | switch (prop) { |
138 | case VIEW_PROP_TITLE: | 135 | case VIEW_PROP_TITLE: |
139 | return view->wlr_xdg_surface->toplevel->title; | 136 | return view->wlr_xdg_toplevel->title; |
140 | case VIEW_PROP_APP_ID: | 137 | case VIEW_PROP_APP_ID: |
141 | return view->wlr_xdg_surface->toplevel->app_id; | 138 | return view->wlr_xdg_toplevel->app_id; |
142 | default: | 139 | default: |
143 | return NULL; | 140 | return NULL; |
144 | } | 141 | } |
@@ -151,50 +148,45 @@ static uint32_t configure(struct sway_view *view, double lx, double ly, | |||
151 | if (xdg_shell_view == NULL) { | 148 | if (xdg_shell_view == NULL) { |
152 | return 0; | 149 | return 0; |
153 | } | 150 | } |
154 | return wlr_xdg_toplevel_set_size(view->wlr_xdg_surface, width, height); | 151 | return wlr_xdg_toplevel_set_size(view->wlr_xdg_toplevel, |
152 | width, height); | ||
155 | } | 153 | } |
156 | 154 | ||
157 | static void set_activated(struct sway_view *view, bool activated) { | 155 | static void set_activated(struct sway_view *view, bool activated) { |
158 | if (xdg_shell_view_from_view(view) == NULL) { | 156 | if (xdg_shell_view_from_view(view) == NULL) { |
159 | return; | 157 | return; |
160 | } | 158 | } |
161 | struct wlr_xdg_surface *surface = view->wlr_xdg_surface; | 159 | wlr_xdg_toplevel_set_activated(view->wlr_xdg_toplevel, activated); |
162 | if (surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL) { | ||
163 | wlr_xdg_toplevel_set_activated(surface, activated); | ||
164 | } | ||
165 | } | 160 | } |
166 | 161 | ||
167 | static void set_tiled(struct sway_view *view, bool tiled) { | 162 | static void set_tiled(struct sway_view *view, bool tiled) { |
168 | if (xdg_shell_view_from_view(view) == NULL) { | 163 | if (xdg_shell_view_from_view(view) == NULL) { |
169 | return; | 164 | return; |
170 | } | 165 | } |
171 | struct wlr_xdg_surface *surface = view->wlr_xdg_surface; | ||
172 | enum wlr_edges edges = WLR_EDGE_NONE; | 166 | enum wlr_edges edges = WLR_EDGE_NONE; |
173 | if (tiled) { | 167 | if (tiled) { |
174 | edges = WLR_EDGE_LEFT | WLR_EDGE_RIGHT | WLR_EDGE_TOP | | 168 | edges = WLR_EDGE_LEFT | WLR_EDGE_RIGHT | WLR_EDGE_TOP | |
175 | WLR_EDGE_BOTTOM; | 169 | WLR_EDGE_BOTTOM; |
176 | } | 170 | } |
177 | wlr_xdg_toplevel_set_tiled(surface, edges); | 171 | wlr_xdg_toplevel_set_tiled(view->wlr_xdg_toplevel, edges); |
178 | } | 172 | } |
179 | 173 | ||
180 | static void set_fullscreen(struct sway_view *view, bool fullscreen) { | 174 | static void set_fullscreen(struct sway_view *view, bool fullscreen) { |
181 | if (xdg_shell_view_from_view(view) == NULL) { | 175 | if (xdg_shell_view_from_view(view) == NULL) { |
182 | return; | 176 | return; |
183 | } | 177 | } |
184 | struct wlr_xdg_surface *surface = view->wlr_xdg_surface; | 178 | wlr_xdg_toplevel_set_fullscreen(view->wlr_xdg_toplevel, fullscreen); |
185 | wlr_xdg_toplevel_set_fullscreen(surface, fullscreen); | ||
186 | } | 179 | } |
187 | 180 | ||
188 | static void set_resizing(struct sway_view *view, bool resizing) { | 181 | static void set_resizing(struct sway_view *view, bool resizing) { |
189 | if (xdg_shell_view_from_view(view) == NULL) { | 182 | if (xdg_shell_view_from_view(view) == NULL) { |
190 | return; | 183 | return; |
191 | } | 184 | } |
192 | struct wlr_xdg_surface *surface = view->wlr_xdg_surface; | 185 | wlr_xdg_toplevel_set_resizing(view->wlr_xdg_toplevel, resizing); |
193 | wlr_xdg_toplevel_set_resizing(surface, resizing); | ||
194 | } | 186 | } |
195 | 187 | ||
196 | static bool wants_floating(struct sway_view *view) { | 188 | static bool wants_floating(struct sway_view *view) { |
197 | struct wlr_xdg_toplevel *toplevel = view->wlr_xdg_surface->toplevel; | 189 | struct wlr_xdg_toplevel *toplevel = view->wlr_xdg_toplevel; |
198 | struct wlr_xdg_toplevel_state *state = &toplevel->current; | 190 | struct wlr_xdg_toplevel_state *state = &toplevel->current; |
199 | return (state->min_width != 0 && state->min_height != 0 | 191 | return (state->min_width != 0 && state->min_height != 0 |
200 | && (state->min_width == state->max_width | 192 | && (state->min_width == state->max_width |
@@ -207,7 +199,7 @@ static void for_each_surface(struct sway_view *view, | |||
207 | if (xdg_shell_view_from_view(view) == NULL) { | 199 | if (xdg_shell_view_from_view(view) == NULL) { |
208 | return; | 200 | return; |
209 | } | 201 | } |
210 | wlr_xdg_surface_for_each_surface(view->wlr_xdg_surface, iterator, | 202 | wlr_xdg_surface_for_each_surface(view->wlr_xdg_toplevel->base, iterator, |
211 | user_data); | 203 | user_data); |
212 | } | 204 | } |
213 | 205 | ||
@@ -216,8 +208,8 @@ static void for_each_popup_surface(struct sway_view *view, | |||
216 | if (xdg_shell_view_from_view(view) == NULL) { | 208 | if (xdg_shell_view_from_view(view) == NULL) { |
217 | return; | 209 | return; |
218 | } | 210 | } |
219 | wlr_xdg_surface_for_each_popup_surface(view->wlr_xdg_surface, iterator, | 211 | wlr_xdg_surface_for_each_popup_surface(view->wlr_xdg_toplevel->base, |
220 | user_data); | 212 | iterator, user_data); |
221 | } | 213 | } |
222 | 214 | ||
223 | static bool is_transient_for(struct sway_view *child, | 215 | static bool is_transient_for(struct sway_view *child, |
@@ -225,12 +217,12 @@ static bool is_transient_for(struct sway_view *child, | |||
225 | if (xdg_shell_view_from_view(child) == NULL) { | 217 | if (xdg_shell_view_from_view(child) == NULL) { |
226 | return false; | 218 | return false; |
227 | } | 219 | } |
228 | struct wlr_xdg_surface *surface = child->wlr_xdg_surface; | 220 | struct wlr_xdg_toplevel *toplevel = child->wlr_xdg_toplevel; |
229 | while (surface && surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL) { | 221 | while (toplevel) { |
230 | if (surface->toplevel->parent == ancestor->wlr_xdg_surface) { | 222 | if (toplevel->parent == ancestor->wlr_xdg_toplevel) { |
231 | return true; | 223 | return true; |
232 | } | 224 | } |
233 | surface = surface->toplevel->parent; | 225 | toplevel = toplevel->parent; |
234 | } | 226 | } |
235 | return false; | 227 | return false; |
236 | } | 228 | } |
@@ -239,17 +231,13 @@ static void _close(struct sway_view *view) { | |||
239 | if (xdg_shell_view_from_view(view) == NULL) { | 231 | if (xdg_shell_view_from_view(view) == NULL) { |
240 | return; | 232 | return; |
241 | } | 233 | } |
242 | struct wlr_xdg_surface *surface = view->wlr_xdg_surface; | 234 | wlr_xdg_toplevel_send_close(view->wlr_xdg_toplevel); |
243 | if (surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL | ||
244 | && surface->toplevel) { | ||
245 | wlr_xdg_toplevel_send_close(surface); | ||
246 | } | ||
247 | } | 235 | } |
248 | 236 | ||
249 | static void close_popups(struct sway_view *view) { | 237 | static void close_popups(struct sway_view *view) { |
250 | struct wlr_xdg_popup *popup, *tmp; | 238 | struct wlr_xdg_popup *popup, *tmp; |
251 | wl_list_for_each_safe(popup, tmp, &view->wlr_xdg_surface->popups, link) { | 239 | wl_list_for_each_safe(popup, tmp, &view->wlr_xdg_toplevel->base->popups, link) { |
252 | wlr_xdg_popup_destroy(popup->base); | 240 | wlr_xdg_popup_destroy(popup); |
253 | } | 241 | } |
254 | } | 242 | } |
255 | 243 | ||
@@ -283,7 +271,7 @@ static void handle_commit(struct wl_listener *listener, void *data) { | |||
283 | struct sway_xdg_shell_view *xdg_shell_view = | 271 | struct sway_xdg_shell_view *xdg_shell_view = |
284 | wl_container_of(listener, xdg_shell_view, commit); | 272 | wl_container_of(listener, xdg_shell_view, commit); |
285 | struct sway_view *view = &xdg_shell_view->view; | 273 | struct sway_view *view = &xdg_shell_view->view; |
286 | struct wlr_xdg_surface *xdg_surface = view->wlr_xdg_surface; | 274 | struct wlr_xdg_surface *xdg_surface = view->wlr_xdg_toplevel->base; |
287 | 275 | ||
288 | struct wlr_box new_geo; | 276 | struct wlr_box new_geo; |
289 | wlr_xdg_surface_get_geometry(xdg_surface, &new_geo); | 277 | wlr_xdg_surface_get_geometry(xdg_surface, &new_geo); |
@@ -293,19 +281,23 @@ static void handle_commit(struct wl_listener *listener, void *data) { | |||
293 | new_geo.y != view->geometry.y; | 281 | new_geo.y != view->geometry.y; |
294 | 282 | ||
295 | if (new_size) { | 283 | if (new_size) { |
296 | // The view has unexpectedly sent a new size | 284 | // The client changed its surface size in this commit. For floating |
285 | // containers, we resize the container to match. For tiling containers, | ||
286 | // we only recenter the surface. | ||
297 | desktop_damage_view(view); | 287 | desktop_damage_view(view); |
298 | view_update_size(view, new_geo.width, new_geo.height); | ||
299 | memcpy(&view->geometry, &new_geo, sizeof(struct wlr_box)); | 288 | memcpy(&view->geometry, &new_geo, sizeof(struct wlr_box)); |
289 | if (container_is_floating(view->container)) { | ||
290 | view_update_size(view); | ||
291 | transaction_commit_dirty_client(); | ||
292 | } else { | ||
293 | view_center_surface(view); | ||
294 | } | ||
300 | desktop_damage_view(view); | 295 | desktop_damage_view(view); |
301 | transaction_commit_dirty(); | ||
302 | } | 296 | } |
303 | 297 | ||
304 | if (view->container->node.instruction) { | 298 | if (view->container->node.instruction) { |
305 | transaction_notify_view_ready_by_serial(view, | 299 | transaction_notify_view_ready_by_serial(view, |
306 | xdg_surface->configure_serial); | 300 | xdg_surface->current.configure_serial); |
307 | } else if (new_size) { | ||
308 | transaction_notify_view_ready_immediately(view); | ||
309 | } | 301 | } |
310 | 302 | ||
311 | view_damage_from(view); | 303 | view_damage_from(view); |
@@ -333,28 +325,30 @@ static void handle_new_popup(struct wl_listener *listener, void *data) { | |||
333 | popup_create(wlr_popup, &xdg_shell_view->view); | 325 | popup_create(wlr_popup, &xdg_shell_view->view); |
334 | } | 326 | } |
335 | 327 | ||
328 | static void handle_request_maximize(struct wl_listener *listener, void *data) { | ||
329 | struct sway_xdg_shell_view *xdg_shell_view = | ||
330 | wl_container_of(listener, xdg_shell_view, request_maximize); | ||
331 | struct wlr_xdg_toplevel *toplevel = xdg_shell_view->view.wlr_xdg_toplevel; | ||
332 | wlr_xdg_surface_schedule_configure(toplevel->base); | ||
333 | } | ||
334 | |||
336 | static void handle_request_fullscreen(struct wl_listener *listener, void *data) { | 335 | static void handle_request_fullscreen(struct wl_listener *listener, void *data) { |
337 | struct sway_xdg_shell_view *xdg_shell_view = | 336 | struct sway_xdg_shell_view *xdg_shell_view = |
338 | wl_container_of(listener, xdg_shell_view, request_fullscreen); | 337 | wl_container_of(listener, xdg_shell_view, request_fullscreen); |
339 | struct wlr_xdg_toplevel_set_fullscreen_event *e = data; | 338 | struct wlr_xdg_toplevel *toplevel = xdg_shell_view->view.wlr_xdg_toplevel; |
340 | struct wlr_xdg_surface *xdg_surface = | ||
341 | xdg_shell_view->view.wlr_xdg_surface; | ||
342 | struct sway_view *view = &xdg_shell_view->view; | 339 | struct sway_view *view = &xdg_shell_view->view; |
343 | 340 | ||
344 | if (!sway_assert(xdg_surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL, | 341 | if (!toplevel->base->mapped) { |
345 | "xdg_shell requested fullscreen of surface with role %i", | ||
346 | xdg_surface->role)) { | ||
347 | return; | ||
348 | } | ||
349 | if (!xdg_surface->mapped) { | ||
350 | return; | 342 | return; |
351 | } | 343 | } |
352 | 344 | ||
353 | struct sway_container *container = view->container; | 345 | struct sway_container *container = view->container; |
354 | if (e->fullscreen && e->output && e->output->data) { | 346 | struct wlr_xdg_toplevel_requested *req = &toplevel->requested; |
355 | struct sway_output *output = e->output->data; | 347 | if (req->fullscreen && req->fullscreen_output && req->fullscreen_output->data) { |
348 | struct sway_output *output = req->fullscreen_output->data; | ||
356 | struct sway_workspace *ws = output_get_active_workspace(output); | 349 | struct sway_workspace *ws = output_get_active_workspace(output); |
357 | if (ws && !container_is_scratchpad_hidden(container)) { | 350 | if (ws && !container_is_scratchpad_hidden(container) && |
351 | container->pending.workspace != ws) { | ||
358 | if (container_is_floating(container)) { | 352 | if (container_is_floating(container)) { |
359 | workspace_add_floating(ws, container); | 353 | workspace_add_floating(ws, container); |
360 | } else { | 354 | } else { |
@@ -363,22 +357,18 @@ static void handle_request_fullscreen(struct wl_listener *listener, void *data) | |||
363 | } | 357 | } |
364 | } | 358 | } |
365 | 359 | ||
366 | container_set_fullscreen(container, e->fullscreen); | 360 | container_set_fullscreen(container, req->fullscreen); |
367 | 361 | ||
368 | arrange_root(); | 362 | arrange_root(); |
369 | transaction_commit_dirty(); | 363 | transaction_commit_dirty(); |
370 | } | 364 | } |
371 | 365 | ||
372 | static void handle_request_maximize(struct wl_listener *listener, void *data) { | ||
373 | struct wlr_xdg_surface *surface = data; | ||
374 | wlr_xdg_surface_schedule_configure(surface); | ||
375 | } | ||
376 | |||
377 | static void handle_request_move(struct wl_listener *listener, void *data) { | 366 | static void handle_request_move(struct wl_listener *listener, void *data) { |
378 | struct sway_xdg_shell_view *xdg_shell_view = | 367 | struct sway_xdg_shell_view *xdg_shell_view = |
379 | wl_container_of(listener, xdg_shell_view, request_move); | 368 | wl_container_of(listener, xdg_shell_view, request_move); |
380 | struct sway_view *view = &xdg_shell_view->view; | 369 | struct sway_view *view = &xdg_shell_view->view; |
381 | if (!container_is_floating(view->container)) { | 370 | if (!container_is_floating(view->container) || |
371 | view->container->pending.fullscreen_mode) { | ||
382 | return; | 372 | return; |
383 | } | 373 | } |
384 | struct wlr_xdg_toplevel_move_event *e = data; | 374 | struct wlr_xdg_toplevel_move_event *e = data; |
@@ -415,8 +405,8 @@ static void handle_unmap(struct wl_listener *listener, void *data) { | |||
415 | 405 | ||
416 | wl_list_remove(&xdg_shell_view->commit.link); | 406 | wl_list_remove(&xdg_shell_view->commit.link); |
417 | wl_list_remove(&xdg_shell_view->new_popup.link); | 407 | wl_list_remove(&xdg_shell_view->new_popup.link); |
418 | wl_list_remove(&xdg_shell_view->request_fullscreen.link); | ||
419 | wl_list_remove(&xdg_shell_view->request_maximize.link); | 408 | wl_list_remove(&xdg_shell_view->request_maximize.link); |
409 | wl_list_remove(&xdg_shell_view->request_fullscreen.link); | ||
420 | wl_list_remove(&xdg_shell_view->request_move.link); | 410 | wl_list_remove(&xdg_shell_view->request_move.link); |
421 | wl_list_remove(&xdg_shell_view->request_resize.link); | 411 | wl_list_remove(&xdg_shell_view->request_resize.link); |
422 | wl_list_remove(&xdg_shell_view->set_title.link); | 412 | wl_list_remove(&xdg_shell_view->set_title.link); |
@@ -427,62 +417,65 @@ static void handle_map(struct wl_listener *listener, void *data) { | |||
427 | struct sway_xdg_shell_view *xdg_shell_view = | 417 | struct sway_xdg_shell_view *xdg_shell_view = |
428 | wl_container_of(listener, xdg_shell_view, map); | 418 | wl_container_of(listener, xdg_shell_view, map); |
429 | struct sway_view *view = &xdg_shell_view->view; | 419 | struct sway_view *view = &xdg_shell_view->view; |
430 | struct wlr_xdg_surface *xdg_surface = view->wlr_xdg_surface; | 420 | struct wlr_xdg_toplevel *toplevel = view->wlr_xdg_toplevel; |
431 | 421 | ||
432 | view->natural_width = view->wlr_xdg_surface->geometry.width; | 422 | view->natural_width = toplevel->base->current.geometry.width; |
433 | view->natural_height = view->wlr_xdg_surface->geometry.height; | 423 | view->natural_height = toplevel->base->current.geometry.height; |
434 | if (!view->natural_width && !view->natural_height) { | 424 | if (!view->natural_width && !view->natural_height) { |
435 | view->natural_width = view->wlr_xdg_surface->surface->current.width; | 425 | view->natural_width = toplevel->base->surface->current.width; |
436 | view->natural_height = view->wlr_xdg_surface->surface->current.height; | 426 | view->natural_height = toplevel->base->surface->current.height; |
437 | } | 427 | } |
438 | 428 | ||
439 | bool csd = false; | 429 | bool csd = false; |
440 | 430 | ||
441 | if (!view->xdg_decoration) { | 431 | if (view->xdg_decoration) { |
432 | enum wlr_xdg_toplevel_decoration_v1_mode mode = | ||
433 | view->xdg_decoration->wlr_xdg_decoration->requested_mode; | ||
434 | csd = mode == WLR_XDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE; | ||
435 | } else { | ||
442 | struct sway_server_decoration *deco = | 436 | struct sway_server_decoration *deco = |
443 | decoration_from_surface(xdg_surface->surface); | 437 | decoration_from_surface(toplevel->base->surface); |
444 | csd = !deco || deco->wlr_server_decoration->mode == | 438 | csd = !deco || deco->wlr_server_decoration->mode == |
445 | WLR_SERVER_DECORATION_MANAGER_MODE_CLIENT; | 439 | WLR_SERVER_DECORATION_MANAGER_MODE_CLIENT; |
446 | |||
447 | } | 440 | } |
448 | 441 | ||
449 | view_map(view, view->wlr_xdg_surface->surface, | 442 | view_map(view, toplevel->base->surface, |
450 | xdg_surface->toplevel->client_pending.fullscreen, | 443 | toplevel->requested.fullscreen, |
451 | xdg_surface->toplevel->client_pending.fullscreen_output, | 444 | toplevel->requested.fullscreen_output, |
452 | csd); | 445 | csd); |
453 | 446 | ||
454 | transaction_commit_dirty(); | 447 | transaction_commit_dirty(); |
455 | 448 | ||
456 | xdg_shell_view->commit.notify = handle_commit; | 449 | xdg_shell_view->commit.notify = handle_commit; |
457 | wl_signal_add(&xdg_surface->surface->events.commit, | 450 | wl_signal_add(&toplevel->base->surface->events.commit, |
458 | &xdg_shell_view->commit); | 451 | &xdg_shell_view->commit); |
459 | 452 | ||
460 | xdg_shell_view->new_popup.notify = handle_new_popup; | 453 | xdg_shell_view->new_popup.notify = handle_new_popup; |
461 | wl_signal_add(&xdg_surface->events.new_popup, | 454 | wl_signal_add(&toplevel->base->events.new_popup, |
462 | &xdg_shell_view->new_popup); | 455 | &xdg_shell_view->new_popup); |
463 | 456 | ||
464 | xdg_shell_view->request_fullscreen.notify = handle_request_fullscreen; | ||
465 | wl_signal_add(&xdg_surface->toplevel->events.request_fullscreen, | ||
466 | &xdg_shell_view->request_fullscreen); | ||
467 | |||
468 | xdg_shell_view->request_maximize.notify = handle_request_maximize; | 457 | xdg_shell_view->request_maximize.notify = handle_request_maximize; |
469 | wl_signal_add(&xdg_surface->toplevel->events.request_maximize, | 458 | wl_signal_add(&toplevel->events.request_maximize, |
470 | &xdg_shell_view->request_maximize); | 459 | &xdg_shell_view->request_maximize); |
471 | 460 | ||
461 | xdg_shell_view->request_fullscreen.notify = handle_request_fullscreen; | ||
462 | wl_signal_add(&toplevel->events.request_fullscreen, | ||
463 | &xdg_shell_view->request_fullscreen); | ||
464 | |||
472 | xdg_shell_view->request_move.notify = handle_request_move; | 465 | xdg_shell_view->request_move.notify = handle_request_move; |
473 | wl_signal_add(&xdg_surface->toplevel->events.request_move, | 466 | wl_signal_add(&toplevel->events.request_move, |
474 | &xdg_shell_view->request_move); | 467 | &xdg_shell_view->request_move); |
475 | 468 | ||
476 | xdg_shell_view->request_resize.notify = handle_request_resize; | 469 | xdg_shell_view->request_resize.notify = handle_request_resize; |
477 | wl_signal_add(&xdg_surface->toplevel->events.request_resize, | 470 | wl_signal_add(&toplevel->events.request_resize, |
478 | &xdg_shell_view->request_resize); | 471 | &xdg_shell_view->request_resize); |
479 | 472 | ||
480 | xdg_shell_view->set_title.notify = handle_set_title; | 473 | xdg_shell_view->set_title.notify = handle_set_title; |
481 | wl_signal_add(&xdg_surface->toplevel->events.set_title, | 474 | wl_signal_add(&toplevel->events.set_title, |
482 | &xdg_shell_view->set_title); | 475 | &xdg_shell_view->set_title); |
483 | 476 | ||
484 | xdg_shell_view->set_app_id.notify = handle_set_app_id; | 477 | xdg_shell_view->set_app_id.notify = handle_set_app_id; |
485 | wl_signal_add(&xdg_surface->toplevel->events.set_app_id, | 478 | wl_signal_add(&toplevel->events.set_app_id, |
486 | &xdg_shell_view->set_app_id); | 479 | &xdg_shell_view->set_app_id); |
487 | } | 480 | } |
488 | 481 | ||
@@ -496,7 +489,7 @@ static void handle_destroy(struct wl_listener *listener, void *data) { | |||
496 | wl_list_remove(&xdg_shell_view->destroy.link); | 489 | wl_list_remove(&xdg_shell_view->destroy.link); |
497 | wl_list_remove(&xdg_shell_view->map.link); | 490 | wl_list_remove(&xdg_shell_view->map.link); |
498 | wl_list_remove(&xdg_shell_view->unmap.link); | 491 | wl_list_remove(&xdg_shell_view->unmap.link); |
499 | view->wlr_xdg_surface = NULL; | 492 | view->wlr_xdg_toplevel = NULL; |
500 | if (view->xdg_decoration) { | 493 | if (view->xdg_decoration) { |
501 | view->xdg_decoration->view = NULL; | 494 | view->xdg_decoration->view = NULL; |
502 | } | 495 | } |
@@ -527,7 +520,7 @@ void handle_xdg_shell_surface(struct wl_listener *listener, void *data) { | |||
527 | } | 520 | } |
528 | 521 | ||
529 | view_init(&xdg_shell_view->view, SWAY_VIEW_XDG_SHELL, &view_impl); | 522 | view_init(&xdg_shell_view->view, SWAY_VIEW_XDG_SHELL, &view_impl); |
530 | xdg_shell_view->view.wlr_xdg_surface = xdg_surface; | 523 | xdg_shell_view->view.wlr_xdg_toplevel = xdg_surface->toplevel; |
531 | 524 | ||
532 | xdg_shell_view->map.notify = handle_map; | 525 | xdg_shell_view->map.notify = handle_map; |
533 | wl_signal_add(&xdg_surface->events.map, &xdg_shell_view->map); | 526 | wl_signal_add(&xdg_surface->events.map, &xdg_shell_view->map); |
diff --git a/sway/desktop/xwayland.c b/sway/desktop/xwayland.c index e1a2e463..7c5dde53 100644 --- a/sway/desktop/xwayland.c +++ b/sway/desktop/xwayland.c | |||
@@ -6,6 +6,7 @@ | |||
6 | #include <wlr/types/wlr_output_layout.h> | 6 | #include <wlr/types/wlr_output_layout.h> |
7 | #include <wlr/types/wlr_output.h> | 7 | #include <wlr/types/wlr_output.h> |
8 | #include <wlr/xwayland.h> | 8 | #include <wlr/xwayland.h> |
9 | #include <xcb/xcb_icccm.h> | ||
9 | #include "log.h" | 10 | #include "log.h" |
10 | #include "sway/desktop.h" | 11 | #include "sway/desktop.h" |
11 | #include "sway/desktop/transaction.h" | 12 | #include "sway/desktop/transaction.h" |
@@ -105,14 +106,10 @@ static void unmanaged_handle_unmap(struct wl_listener *listener, void *data) { | |||
105 | if (seat->wlr_seat->keyboard_state.focused_surface == xsurface->surface) { | 106 | if (seat->wlr_seat->keyboard_state.focused_surface == xsurface->surface) { |
106 | // This simply returns focus to the parent surface if there's one available. | 107 | // This simply returns focus to the parent surface if there's one available. |
107 | // This seems to handle JetBrains issues. | 108 | // This seems to handle JetBrains issues. |
108 | if (xsurface->parent && xsurface->parent->surface && | 109 | if (xsurface->parent && xsurface->parent->surface |
109 | wlr_surface_is_xwayland_surface(xsurface->parent->surface)) { | 110 | && wlr_xwayland_or_surface_wants_focus(xsurface->parent)) { |
110 | struct wlr_xwayland_surface *next_surface = | 111 | seat_set_focus_surface(seat, xsurface->parent->surface, false); |
111 | wlr_xwayland_surface_from_wlr_surface(xsurface->parent->surface); | 112 | return; |
112 | if (wlr_xwayland_or_surface_wants_focus(next_surface)) { | ||
113 | seat_set_focus_surface(seat, xsurface->parent->surface, false); | ||
114 | return; | ||
115 | } | ||
116 | } | 113 | } |
117 | 114 | ||
118 | // Restore focus | 115 | // Restore focus |
@@ -125,6 +122,20 @@ static void unmanaged_handle_unmap(struct wl_listener *listener, void *data) { | |||
125 | } | 122 | } |
126 | } | 123 | } |
127 | 124 | ||
125 | static void unmanaged_handle_request_activate(struct wl_listener *listener, void *data) { | ||
126 | struct wlr_xwayland_surface *xsurface = data; | ||
127 | if (!xsurface->mapped) { | ||
128 | return; | ||
129 | } | ||
130 | struct sway_seat *seat = input_manager_current_seat(); | ||
131 | struct sway_container *focus = seat_get_focused_container(seat); | ||
132 | if (focus && focus->view && focus->view->pid != xsurface->pid) { | ||
133 | return; | ||
134 | } | ||
135 | |||
136 | seat_set_focus_surface(seat, xsurface->surface, false); | ||
137 | } | ||
138 | |||
128 | static void unmanaged_handle_destroy(struct wl_listener *listener, void *data) { | 139 | static void unmanaged_handle_destroy(struct wl_listener *listener, void *data) { |
129 | struct sway_xwayland_unmanaged *surface = | 140 | struct sway_xwayland_unmanaged *surface = |
130 | wl_container_of(listener, surface, destroy); | 141 | wl_container_of(listener, surface, destroy); |
@@ -133,6 +144,7 @@ static void unmanaged_handle_destroy(struct wl_listener *listener, void *data) { | |||
133 | wl_list_remove(&surface->unmap.link); | 144 | wl_list_remove(&surface->unmap.link); |
134 | wl_list_remove(&surface->destroy.link); | 145 | wl_list_remove(&surface->destroy.link); |
135 | wl_list_remove(&surface->override_redirect.link); | 146 | wl_list_remove(&surface->override_redirect.link); |
147 | wl_list_remove(&surface->request_activate.link); | ||
136 | free(surface); | 148 | free(surface); |
137 | } | 149 | } |
138 | 150 | ||
@@ -180,6 +192,8 @@ static struct sway_xwayland_unmanaged *create_unmanaged( | |||
180 | surface->destroy.notify = unmanaged_handle_destroy; | 192 | surface->destroy.notify = unmanaged_handle_destroy; |
181 | wl_signal_add(&xsurface->events.set_override_redirect, &surface->override_redirect); | 193 | wl_signal_add(&xsurface->events.set_override_redirect, &surface->override_redirect); |
182 | surface->override_redirect.notify = unmanaged_handle_override_redirect; | 194 | surface->override_redirect.notify = unmanaged_handle_override_redirect; |
195 | wl_signal_add(&xsurface->events.request_activate, &surface->request_activate); | ||
196 | surface->request_activate.notify = unmanaged_handle_request_activate; | ||
183 | 197 | ||
184 | return surface; | 198 | return surface; |
185 | } | 199 | } |
@@ -258,6 +272,7 @@ static void set_activated(struct sway_view *view, bool activated) { | |||
258 | } | 272 | } |
259 | 273 | ||
260 | wlr_xwayland_surface_activate(surface, activated); | 274 | wlr_xwayland_surface_activate(surface, activated); |
275 | wlr_xwayland_surface_restack(surface, NULL, XCB_STACK_MODE_ABOVE); | ||
261 | } | 276 | } |
262 | 277 | ||
263 | static void set_tiled(struct sway_view *view, bool tiled) { | 278 | static void set_tiled(struct sway_view *view, bool tiled) { |
@@ -297,7 +312,7 @@ static bool wants_floating(struct sway_view *view) { | |||
297 | } | 312 | } |
298 | } | 313 | } |
299 | 314 | ||
300 | struct wlr_xwayland_surface_size_hints *size_hints = surface->size_hints; | 315 | xcb_size_hints_t *size_hints = surface->size_hints; |
301 | if (size_hints != NULL && | 316 | if (size_hints != NULL && |
302 | size_hints->min_width > 0 && size_hints->min_height > 0 && | 317 | size_hints->min_width > 0 && size_hints->min_height > 0 && |
303 | (size_hints->max_width == size_hints->min_width || | 318 | (size_hints->max_width == size_hints->min_width || |
@@ -351,7 +366,7 @@ static void destroy(struct sway_view *view) { | |||
351 | static void get_constraints(struct sway_view *view, double *min_width, | 366 | static void get_constraints(struct sway_view *view, double *min_width, |
352 | double *max_width, double *min_height, double *max_height) { | 367 | double *max_width, double *min_height, double *max_height) { |
353 | struct wlr_xwayland_surface *surface = view->wlr_xwayland_surface; | 368 | struct wlr_xwayland_surface *surface = view->wlr_xwayland_surface; |
354 | struct wlr_xwayland_surface_size_hints *size_hints = surface->size_hints; | 369 | xcb_size_hints_t *size_hints = surface->size_hints; |
355 | 370 | ||
356 | if (size_hints == NULL) { | 371 | if (size_hints == NULL) { |
357 | *min_width = DBL_MIN; | 372 | *min_width = DBL_MIN; |
@@ -399,30 +414,31 @@ static void handle_commit(struct wl_listener *listener, void *data) { | |||
399 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; | 414 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; |
400 | struct wlr_surface_state *state = &xsurface->surface->current; | 415 | struct wlr_surface_state *state = &xsurface->surface->current; |
401 | 416 | ||
417 | struct wlr_box new_geo; | ||
418 | get_geometry(view, &new_geo); | ||
419 | bool new_size = new_geo.width != view->geometry.width || | ||
420 | new_geo.height != view->geometry.height || | ||
421 | new_geo.x != view->geometry.x || | ||
422 | new_geo.y != view->geometry.y; | ||
423 | |||
424 | if (new_size) { | ||
425 | // The client changed its surface size in this commit. For floating | ||
426 | // containers, we resize the container to match. For tiling containers, | ||
427 | // we only recenter the surface. | ||
428 | desktop_damage_view(view); | ||
429 | memcpy(&view->geometry, &new_geo, sizeof(struct wlr_box)); | ||
430 | if (container_is_floating(view->container)) { | ||
431 | view_update_size(view); | ||
432 | transaction_commit_dirty_client(); | ||
433 | } else { | ||
434 | view_center_surface(view); | ||
435 | } | ||
436 | desktop_damage_view(view); | ||
437 | } | ||
438 | |||
402 | if (view->container->node.instruction) { | 439 | if (view->container->node.instruction) { |
403 | get_geometry(view, &view->geometry); | ||
404 | transaction_notify_view_ready_by_geometry(view, | 440 | transaction_notify_view_ready_by_geometry(view, |
405 | xsurface->x, xsurface->y, state->width, state->height); | 441 | xsurface->x, xsurface->y, state->width, state->height); |
406 | } else { | ||
407 | struct wlr_box new_geo; | ||
408 | get_geometry(view, &new_geo); | ||
409 | |||
410 | if ((new_geo.width != view->geometry.width || | ||
411 | new_geo.height != view->geometry.height || | ||
412 | new_geo.x != view->geometry.x || | ||
413 | new_geo.y != view->geometry.y)) { | ||
414 | // The view has unexpectedly sent a new size | ||
415 | // eg. The Firefox "Save As" dialog when downloading a file | ||
416 | desktop_damage_view(view); | ||
417 | view_update_size(view, new_geo.width, new_geo.height); | ||
418 | memcpy(&view->geometry, &new_geo, sizeof(struct wlr_box)); | ||
419 | desktop_damage_view(view); | ||
420 | transaction_commit_dirty(); | ||
421 | transaction_notify_view_ready_by_geometry(view, | ||
422 | xsurface->x, xsurface->y, new_geo.width, new_geo.height); | ||
423 | } else { | ||
424 | memcpy(&view->geometry, &new_geo, sizeof(struct wlr_box)); | ||
425 | } | ||
426 | } | 442 | } |
427 | 443 | ||
428 | view_damage_from(view); | 444 | view_damage_from(view); |
@@ -438,6 +454,8 @@ static void handle_destroy(struct wl_listener *listener, void *data) { | |||
438 | wl_list_remove(&xwayland_view->commit.link); | 454 | wl_list_remove(&xwayland_view->commit.link); |
439 | } | 455 | } |
440 | 456 | ||
457 | xwayland_view->view.wlr_xwayland_surface = NULL; | ||
458 | |||
441 | wl_list_remove(&xwayland_view->destroy.link); | 459 | wl_list_remove(&xwayland_view->destroy.link); |
442 | wl_list_remove(&xwayland_view->request_configure.link); | 460 | wl_list_remove(&xwayland_view->request_configure.link); |
443 | wl_list_remove(&xwayland_view->request_fullscreen.link); | 461 | wl_list_remove(&xwayland_view->request_fullscreen.link); |
@@ -527,10 +545,10 @@ static void handle_request_configure(struct wl_listener *listener, void *data) { | |||
527 | view->natural_height = ev->height; | 545 | view->natural_height = ev->height; |
528 | container_floating_resize_and_center(view->container); | 546 | container_floating_resize_and_center(view->container); |
529 | 547 | ||
530 | configure(view, view->container->content_x, | 548 | configure(view, view->container->pending.content_x, |
531 | view->container->content_y, | 549 | view->container->pending.content_y, |
532 | view->container->content_width, | 550 | view->container->pending.content_width, |
533 | view->container->content_height); | 551 | view->container->pending.content_height); |
534 | node_set_dirty(&view->container->node); | 552 | node_set_dirty(&view->container->node); |
535 | } else { | 553 | } else { |
536 | configure(view, view->container->current.content_x, | 554 | configure(view, view->container->current.content_x, |
@@ -577,7 +595,8 @@ static void handle_request_move(struct wl_listener *listener, void *data) { | |||
577 | if (!xsurface->mapped) { | 595 | if (!xsurface->mapped) { |
578 | return; | 596 | return; |
579 | } | 597 | } |
580 | if (!container_is_floating(view->container)) { | 598 | if (!container_is_floating(view->container) || |
599 | view->container->pending.fullscreen_mode) { | ||
581 | return; | 600 | return; |
582 | } | 601 | } |
583 | struct sway_seat *seat = input_manager_current_seat(); | 602 | struct sway_seat *seat = input_manager_current_seat(); |
@@ -666,14 +685,15 @@ static void handle_set_hints(struct wl_listener *listener, void *data) { | |||
666 | if (!xsurface->mapped) { | 685 | if (!xsurface->mapped) { |
667 | return; | 686 | return; |
668 | } | 687 | } |
669 | if (!xsurface->hints_urgency && view->urgent_timer) { | 688 | const bool hints_urgency = xcb_icccm_wm_hints_get_urgency(xsurface->hints); |
689 | if (!hints_urgency && view->urgent_timer) { | ||
670 | // The view is in the timeout period. We'll ignore the request to | 690 | // The view is in the timeout period. We'll ignore the request to |
671 | // unset urgency so that the view remains urgent until the timer clears | 691 | // unset urgency so that the view remains urgent until the timer clears |
672 | // it. | 692 | // it. |
673 | return; | 693 | return; |
674 | } | 694 | } |
675 | if (view->allow_request_urgent) { | 695 | if (view->allow_request_urgent) { |
676 | view_set_urgent(view, (bool)xsurface->hints_urgency); | 696 | view_set_urgent(view, hints_urgency); |
677 | } | 697 | } |
678 | } | 698 | } |
679 | 699 | ||
diff --git a/sway/input/cursor.c b/sway/input/cursor.c index fa604426..2ee63124 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c | |||
@@ -4,8 +4,8 @@ | |||
4 | #include <libevdev/libevdev.h> | 4 | #include <libevdev/libevdev.h> |
5 | #include <linux/input-event-codes.h> | 5 | #include <linux/input-event-codes.h> |
6 | #include <errno.h> | 6 | #include <errno.h> |
7 | #include <time.h> | ||
7 | #include <strings.h> | 8 | #include <strings.h> |
8 | #include <wlr/types/wlr_box.h> | ||
9 | #include <wlr/types/wlr_cursor.h> | 9 | #include <wlr/types/wlr_cursor.h> |
10 | #include <wlr/types/wlr_idle.h> | 10 | #include <wlr/types/wlr_idle.h> |
11 | #include <wlr/types/wlr_pointer.h> | 11 | #include <wlr/types/wlr_pointer.h> |
@@ -20,7 +20,6 @@ | |||
20 | #include "util.h" | 20 | #include "util.h" |
21 | #include "sway/commands.h" | 21 | #include "sway/commands.h" |
22 | #include "sway/desktop.h" | 22 | #include "sway/desktop.h" |
23 | #include "sway/desktop/transaction.h" | ||
24 | #include "sway/input/cursor.h" | 23 | #include "sway/input/cursor.h" |
25 | #include "sway/input/keyboard.h" | 24 | #include "sway/input/keyboard.h" |
26 | #include "sway/input/tablet.h" | 25 | #include "sway/input/tablet.h" |
@@ -32,6 +31,12 @@ | |||
32 | #include "sway/tree/workspace.h" | 31 | #include "sway/tree/workspace.h" |
33 | #include "wlr-layer-shell-unstable-v1-protocol.h" | 32 | #include "wlr-layer-shell-unstable-v1-protocol.h" |
34 | 33 | ||
34 | static uint32_t get_current_time_msec(void) { | ||
35 | struct timespec now; | ||
36 | clock_gettime(CLOCK_MONOTONIC, &now); | ||
37 | return now.tv_sec * 1000 + now.tv_nsec / 1000000; | ||
38 | } | ||
39 | |||
35 | static struct wlr_surface *layer_surface_at(struct sway_output *output, | 40 | static struct wlr_surface *layer_surface_at(struct sway_output *output, |
36 | struct wl_list *layer, double ox, double oy, double *sx, double *sy) { | 41 | struct wl_list *layer, double ox, double oy, double *sx, double *sy) { |
37 | struct sway_layer_surface *sway_layer; | 42 | struct sway_layer_surface *sway_layer; |
@@ -78,7 +83,28 @@ static struct wlr_surface *layer_surface_popup_at(struct sway_output *output, | |||
78 | struct sway_node *node_at_coords( | 83 | struct sway_node *node_at_coords( |
79 | struct sway_seat *seat, double lx, double ly, | 84 | struct sway_seat *seat, double lx, double ly, |
80 | struct wlr_surface **surface, double *sx, double *sy) { | 85 | struct wlr_surface **surface, double *sx, double *sy) { |
81 | // check for unmanaged views first | 86 | // find the output the cursor is on |
87 | struct wlr_output *wlr_output = wlr_output_layout_output_at( | ||
88 | root->output_layout, lx, ly); | ||
89 | if (wlr_output == NULL) { | ||
90 | return NULL; | ||
91 | } | ||
92 | struct sway_output *output = wlr_output->data; | ||
93 | if (!output || !output->enabled) { | ||
94 | // output is being destroyed or is being enabled | ||
95 | return NULL; | ||
96 | } | ||
97 | double ox = lx, oy = ly; | ||
98 | wlr_output_layout_output_coords(root->output_layout, wlr_output, &ox, &oy); | ||
99 | |||
100 | // layer surfaces on the overlay layer are rendered on top | ||
101 | if ((*surface = layer_surface_at(output, | ||
102 | &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], | ||
103 | ox, oy, sx, sy))) { | ||
104 | return NULL; | ||
105 | } | ||
106 | |||
107 | // check for unmanaged views | ||
82 | #if HAVE_XWAYLAND | 108 | #if HAVE_XWAYLAND |
83 | struct wl_list *unmanaged = &root->xwayland_unmanaged; | 109 | struct wl_list *unmanaged = &root->xwayland_unmanaged; |
84 | struct sway_xwayland_unmanaged *unmanaged_surface; | 110 | struct sway_xwayland_unmanaged *unmanaged_surface; |
@@ -96,19 +122,6 @@ struct sway_node *node_at_coords( | |||
96 | } | 122 | } |
97 | } | 123 | } |
98 | #endif | 124 | #endif |
99 | // find the output the cursor is on | ||
100 | struct wlr_output *wlr_output = wlr_output_layout_output_at( | ||
101 | root->output_layout, lx, ly); | ||
102 | if (wlr_output == NULL) { | ||
103 | return NULL; | ||
104 | } | ||
105 | struct sway_output *output = wlr_output->data; | ||
106 | if (!output || !output->enabled) { | ||
107 | // output is being destroyed or is being enabled | ||
108 | return NULL; | ||
109 | } | ||
110 | double ox = lx, oy = ly; | ||
111 | wlr_output_layout_output_coords(root->output_layout, wlr_output, &ox, &oy); | ||
112 | 125 | ||
113 | if (root->fullscreen_global) { | 126 | if (root->fullscreen_global) { |
114 | // Try fullscreen container | 127 | // Try fullscreen container |
@@ -126,11 +139,6 @@ struct sway_node *node_at_coords( | |||
126 | return NULL; | 139 | return NULL; |
127 | } | 140 | } |
128 | 141 | ||
129 | if ((*surface = layer_surface_at(output, | ||
130 | &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], | ||
131 | ox, oy, sx, sy))) { | ||
132 | return NULL; | ||
133 | } | ||
134 | if (ws->fullscreen) { | 142 | if (ws->fullscreen) { |
135 | // Try transient containers | 143 | // Try transient containers |
136 | for (int i = 0; i < ws->floating->length; ++i) { | 144 | for (int i = 0; i < ws->floating->length; ++i) { |
@@ -378,30 +386,29 @@ static void pointer_motion(struct sway_cursor *cursor, uint32_t time_msec, | |||
378 | static void handle_pointer_motion_relative( | 386 | static void handle_pointer_motion_relative( |
379 | struct wl_listener *listener, void *data) { | 387 | struct wl_listener *listener, void *data) { |
380 | struct sway_cursor *cursor = wl_container_of(listener, cursor, motion); | 388 | struct sway_cursor *cursor = wl_container_of(listener, cursor, motion); |
381 | struct wlr_event_pointer_motion *e = data; | 389 | struct wlr_pointer_motion_event *e = data; |
382 | cursor_handle_activity_from_device(cursor, e->device); | 390 | cursor_handle_activity_from_device(cursor, &e->pointer->base); |
383 | 391 | ||
384 | pointer_motion(cursor, e->time_msec, e->device, e->delta_x, e->delta_y, | 392 | pointer_motion(cursor, e->time_msec, &e->pointer->base, e->delta_x, |
385 | e->unaccel_dx, e->unaccel_dy); | 393 | e->delta_y, e->unaccel_dx, e->unaccel_dy); |
386 | transaction_commit_dirty(); | ||
387 | } | 394 | } |
388 | 395 | ||
389 | static void handle_pointer_motion_absolute( | 396 | static void handle_pointer_motion_absolute( |
390 | struct wl_listener *listener, void *data) { | 397 | struct wl_listener *listener, void *data) { |
391 | struct sway_cursor *cursor = | 398 | struct sway_cursor *cursor = |
392 | wl_container_of(listener, cursor, motion_absolute); | 399 | wl_container_of(listener, cursor, motion_absolute); |
393 | struct wlr_event_pointer_motion_absolute *event = data; | 400 | struct wlr_pointer_motion_absolute_event *event = data; |
394 | cursor_handle_activity_from_device(cursor, event->device); | 401 | cursor_handle_activity_from_device(cursor, &event->pointer->base); |
395 | 402 | ||
396 | double lx, ly; | 403 | double lx, ly; |
397 | wlr_cursor_absolute_to_layout_coords(cursor->cursor, event->device, | 404 | wlr_cursor_absolute_to_layout_coords(cursor->cursor, &event->pointer->base, |
398 | event->x, event->y, &lx, &ly); | 405 | event->x, event->y, &lx, &ly); |
399 | 406 | ||
400 | double dx = lx - cursor->cursor->x; | 407 | double dx = lx - cursor->cursor->x; |
401 | double dy = ly - cursor->cursor->y; | 408 | double dy = ly - cursor->cursor->y; |
402 | 409 | ||
403 | pointer_motion(cursor, event->time_msec, event->device, dx, dy, dx, dy); | 410 | pointer_motion(cursor, event->time_msec, &event->pointer->base, dx, dy, |
404 | transaction_commit_dirty(); | 411 | dx, dy); |
405 | } | 412 | } |
406 | 413 | ||
407 | void dispatch_cursor_button(struct sway_cursor *cursor, | 414 | void dispatch_cursor_button(struct sway_cursor *cursor, |
@@ -416,7 +423,7 @@ void dispatch_cursor_button(struct sway_cursor *cursor, | |||
416 | 423 | ||
417 | static void handle_pointer_button(struct wl_listener *listener, void *data) { | 424 | static void handle_pointer_button(struct wl_listener *listener, void *data) { |
418 | struct sway_cursor *cursor = wl_container_of(listener, cursor, button); | 425 | struct sway_cursor *cursor = wl_container_of(listener, cursor, button); |
419 | struct wlr_event_pointer_button *event = data; | 426 | struct wlr_pointer_button_event *event = data; |
420 | 427 | ||
421 | if (event->state == WLR_BUTTON_PRESSED) { | 428 | if (event->state == WLR_BUTTON_PRESSED) { |
422 | cursor->pressed_button_count++; | 429 | cursor->pressed_button_count++; |
@@ -428,23 +435,21 @@ static void handle_pointer_button(struct wl_listener *listener, void *data) { | |||
428 | } | 435 | } |
429 | } | 436 | } |
430 | 437 | ||
431 | cursor_handle_activity_from_device(cursor, event->device); | 438 | cursor_handle_activity_from_device(cursor, &event->pointer->base); |
432 | dispatch_cursor_button(cursor, event->device, | 439 | dispatch_cursor_button(cursor, &event->pointer->base, |
433 | event->time_msec, event->button, event->state); | 440 | event->time_msec, event->button, event->state); |
434 | transaction_commit_dirty(); | ||
435 | } | 441 | } |
436 | 442 | ||
437 | void dispatch_cursor_axis(struct sway_cursor *cursor, | 443 | void dispatch_cursor_axis(struct sway_cursor *cursor, |
438 | struct wlr_event_pointer_axis *event) { | 444 | struct wlr_pointer_axis_event *event) { |
439 | seatop_pointer_axis(cursor->seat, event); | 445 | seatop_pointer_axis(cursor->seat, event); |
440 | } | 446 | } |
441 | 447 | ||
442 | static void handle_pointer_axis(struct wl_listener *listener, void *data) { | 448 | static void handle_pointer_axis(struct wl_listener *listener, void *data) { |
443 | struct sway_cursor *cursor = wl_container_of(listener, cursor, axis); | 449 | struct sway_cursor *cursor = wl_container_of(listener, cursor, axis); |
444 | struct wlr_event_pointer_axis *event = data; | 450 | struct wlr_pointer_axis_event *event = data; |
445 | cursor_handle_activity_from_device(cursor, event->device); | 451 | cursor_handle_activity_from_device(cursor, &event->pointer->base); |
446 | dispatch_cursor_axis(cursor, event); | 452 | dispatch_cursor_axis(cursor, event); |
447 | transaction_commit_dirty(); | ||
448 | } | 453 | } |
449 | 454 | ||
450 | static void handle_pointer_frame(struct wl_listener *listener, void *data) { | 455 | static void handle_pointer_frame(struct wl_listener *listener, void *data) { |
@@ -454,8 +459,8 @@ static void handle_pointer_frame(struct wl_listener *listener, void *data) { | |||
454 | 459 | ||
455 | static void handle_touch_down(struct wl_listener *listener, void *data) { | 460 | static void handle_touch_down(struct wl_listener *listener, void *data) { |
456 | struct sway_cursor *cursor = wl_container_of(listener, cursor, touch_down); | 461 | struct sway_cursor *cursor = wl_container_of(listener, cursor, touch_down); |
457 | struct wlr_event_touch_down *event = data; | 462 | struct wlr_touch_down_event *event = data; |
458 | cursor_handle_activity_from_device(cursor, event->device); | 463 | cursor_handle_activity_from_device(cursor, &event->touch->base); |
459 | cursor_hide(cursor); | 464 | cursor_hide(cursor); |
460 | 465 | ||
461 | struct sway_seat *seat = cursor->seat; | 466 | struct sway_seat *seat = cursor->seat; |
@@ -463,7 +468,7 @@ static void handle_touch_down(struct wl_listener *listener, void *data) { | |||
463 | struct wlr_surface *surface = NULL; | 468 | struct wlr_surface *surface = NULL; |
464 | 469 | ||
465 | double lx, ly; | 470 | double lx, ly; |
466 | wlr_cursor_absolute_to_layout_coords(cursor->cursor, event->device, | 471 | wlr_cursor_absolute_to_layout_coords(cursor->cursor, &event->touch->base, |
467 | event->x, event->y, &lx, &ly); | 472 | event->x, event->y, &lx, &ly); |
468 | double sx, sy; | 473 | double sx, sy; |
469 | struct sway_node *focused_node = node_at_coords(seat, lx, ly, &surface, &sx, &sy); | 474 | struct sway_node *focused_node = node_at_coords(seat, lx, ly, &surface, &sx, &sy); |
@@ -491,28 +496,25 @@ static void handle_touch_down(struct wl_listener *listener, void *data) { | |||
491 | double dx, dy; | 496 | double dx, dy; |
492 | dx = lx - cursor->cursor->x; | 497 | dx = lx - cursor->cursor->x; |
493 | dy = ly - cursor->cursor->y; | 498 | dy = ly - cursor->cursor->y; |
494 | pointer_motion(cursor, event->time_msec, event->device, dx, dy, dx, dy); | 499 | pointer_motion(cursor, event->time_msec, &event->touch->base, dx, dy, |
495 | dispatch_cursor_button(cursor, event->device, event->time_msec, | 500 | dx, dy); |
501 | dispatch_cursor_button(cursor, &event->touch->base, event->time_msec, | ||
496 | BTN_LEFT, WLR_BUTTON_PRESSED); | 502 | BTN_LEFT, WLR_BUTTON_PRESSED); |
497 | wlr_seat_pointer_notify_frame(wlr_seat); | ||
498 | transaction_commit_dirty(); | ||
499 | } | 503 | } |
500 | } | 504 | } |
501 | 505 | ||
502 | static void handle_touch_up(struct wl_listener *listener, void *data) { | 506 | static void handle_touch_up(struct wl_listener *listener, void *data) { |
503 | struct sway_cursor *cursor = wl_container_of(listener, cursor, touch_up); | 507 | struct sway_cursor *cursor = wl_container_of(listener, cursor, touch_up); |
504 | struct wlr_event_touch_up *event = data; | 508 | struct wlr_touch_up_event *event = data; |
505 | cursor_handle_activity_from_device(cursor, event->device); | 509 | cursor_handle_activity_from_device(cursor, &event->touch->base); |
506 | 510 | ||
507 | struct wlr_seat *wlr_seat = cursor->seat->wlr_seat; | 511 | struct wlr_seat *wlr_seat = cursor->seat->wlr_seat; |
508 | 512 | ||
509 | if (cursor->simulating_pointer_from_touch) { | 513 | if (cursor->simulating_pointer_from_touch) { |
510 | if (cursor->pointer_touch_id == cursor->seat->touch_id) { | 514 | if (cursor->pointer_touch_id == cursor->seat->touch_id) { |
511 | cursor->simulating_pointer_from_touch = false; | 515 | cursor->pointer_touch_up = true; |
512 | dispatch_cursor_button(cursor, event->device, event->time_msec, | 516 | dispatch_cursor_button(cursor, &event->touch->base, |
513 | BTN_LEFT, WLR_BUTTON_RELEASED); | 517 | event->time_msec, BTN_LEFT, WLR_BUTTON_RELEASED); |
514 | wlr_seat_pointer_notify_frame(wlr_seat); | ||
515 | transaction_commit_dirty(); | ||
516 | } | 518 | } |
517 | } else { | 519 | } else { |
518 | wlr_seat_touch_notify_up(wlr_seat, event->time_msec, event->touch_id); | 520 | wlr_seat_touch_notify_up(wlr_seat, event->time_msec, event->touch_id); |
@@ -522,15 +524,15 @@ static void handle_touch_up(struct wl_listener *listener, void *data) { | |||
522 | static void handle_touch_motion(struct wl_listener *listener, void *data) { | 524 | static void handle_touch_motion(struct wl_listener *listener, void *data) { |
523 | struct sway_cursor *cursor = | 525 | struct sway_cursor *cursor = |
524 | wl_container_of(listener, cursor, touch_motion); | 526 | wl_container_of(listener, cursor, touch_motion); |
525 | struct wlr_event_touch_motion *event = data; | 527 | struct wlr_touch_motion_event *event = data; |
526 | cursor_handle_activity_from_device(cursor, event->device); | 528 | cursor_handle_activity_from_device(cursor, &event->touch->base); |
527 | 529 | ||
528 | struct sway_seat *seat = cursor->seat; | 530 | struct sway_seat *seat = cursor->seat; |
529 | struct wlr_seat *wlr_seat = seat->wlr_seat; | 531 | struct wlr_seat *wlr_seat = seat->wlr_seat; |
530 | struct wlr_surface *surface = NULL; | 532 | struct wlr_surface *surface = NULL; |
531 | 533 | ||
532 | double lx, ly; | 534 | double lx, ly; |
533 | wlr_cursor_absolute_to_layout_coords(cursor->cursor, event->device, | 535 | wlr_cursor_absolute_to_layout_coords(cursor->cursor, &event->touch->base, |
534 | event->x, event->y, &lx, &ly); | 536 | event->x, event->y, &lx, &ly); |
535 | double sx, sy; | 537 | double sx, sy; |
536 | node_at_coords(cursor->seat, lx, ly, &surface, &sx, &sy); | 538 | node_at_coords(cursor->seat, lx, ly, &surface, &sx, &sy); |
@@ -552,8 +554,8 @@ static void handle_touch_motion(struct wl_listener *listener, void *data) { | |||
552 | double dx, dy; | 554 | double dx, dy; |
553 | dx = lx - cursor->cursor->x; | 555 | dx = lx - cursor->cursor->x; |
554 | dy = ly - cursor->cursor->y; | 556 | dy = ly - cursor->cursor->y; |
555 | pointer_motion(cursor, event->time_msec, event->device, dx, dy, dx, dy); | 557 | pointer_motion(cursor, event->time_msec, &event->touch->base, |
556 | transaction_commit_dirty(); | 558 | dx, dy, dx, dy); |
557 | } | 559 | } |
558 | } else if (surface) { | 560 | } else if (surface) { |
559 | wlr_seat_touch_notify_motion(wlr_seat, event->time_msec, | 561 | wlr_seat_touch_notify_motion(wlr_seat, event->time_msec, |
@@ -561,6 +563,24 @@ static void handle_touch_motion(struct wl_listener *listener, void *data) { | |||
561 | } | 563 | } |
562 | } | 564 | } |
563 | 565 | ||
566 | static void handle_touch_frame(struct wl_listener *listener, void *data) { | ||
567 | struct sway_cursor *cursor = | ||
568 | wl_container_of(listener, cursor, touch_frame); | ||
569 | |||
570 | struct wlr_seat *wlr_seat = cursor->seat->wlr_seat; | ||
571 | |||
572 | if (cursor->simulating_pointer_from_touch) { | ||
573 | wlr_seat_pointer_notify_frame(wlr_seat); | ||
574 | |||
575 | if (cursor->pointer_touch_up) { | ||
576 | cursor->pointer_touch_up = false; | ||
577 | cursor->simulating_pointer_from_touch = false; | ||
578 | } | ||
579 | } else { | ||
580 | wlr_seat_touch_notify_frame(wlr_seat); | ||
581 | } | ||
582 | } | ||
583 | |||
564 | static double apply_mapping_from_coord(double low, double high, double value) { | 584 | static double apply_mapping_from_coord(double low, double high, double value) { |
565 | if (isnan(value)) { | 585 | if (isnan(value)) { |
566 | return value; | 586 | return value; |
@@ -574,14 +594,15 @@ static void apply_mapping_from_region(struct wlr_input_device *device, | |||
574 | double x1 = region->x1, x2 = region->x2; | 594 | double x1 = region->x1, x2 = region->x2; |
575 | double y1 = region->y1, y2 = region->y2; | 595 | double y1 = region->y1, y2 = region->y2; |
576 | 596 | ||
577 | if (region->mm) { | 597 | if (region->mm && device->type == WLR_INPUT_DEVICE_TABLET_TOOL) { |
578 | if (device->width_mm == 0 || device->height_mm == 0) { | 598 | struct wlr_tablet *tablet = wlr_tablet_from_input_device(device); |
599 | if (tablet->width_mm == 0 || tablet->height_mm == 0) { | ||
579 | return; | 600 | return; |
580 | } | 601 | } |
581 | x1 /= device->width_mm; | 602 | x1 /= tablet->width_mm; |
582 | x2 /= device->width_mm; | 603 | x2 /= tablet->width_mm; |
583 | y1 /= device->height_mm; | 604 | y1 /= tablet->height_mm; |
584 | y2 /= device->height_mm; | 605 | y2 /= tablet->height_mm; |
585 | } | 606 | } |
586 | 607 | ||
587 | *x = apply_mapping_from_coord(x1, x2, *x); | 608 | *x = apply_mapping_from_coord(x1, x2, *x); |
@@ -639,14 +660,12 @@ static void handle_tablet_tool_position(struct sway_cursor *cursor, | |||
639 | wlr_tablet_v2_tablet_tool_notify_proximity_out(tool->tablet_v2_tool); | 660 | wlr_tablet_v2_tablet_tool_notify_proximity_out(tool->tablet_v2_tool); |
640 | pointer_motion(cursor, time_msec, input_device->wlr_device, dx, dy, dx, dy); | 661 | pointer_motion(cursor, time_msec, input_device->wlr_device, dx, dy, dx, dy); |
641 | } | 662 | } |
642 | |||
643 | transaction_commit_dirty(); | ||
644 | } | 663 | } |
645 | 664 | ||
646 | static void handle_tool_axis(struct wl_listener *listener, void *data) { | 665 | static void handle_tool_axis(struct wl_listener *listener, void *data) { |
647 | struct sway_cursor *cursor = wl_container_of(listener, cursor, tool_axis); | 666 | struct sway_cursor *cursor = wl_container_of(listener, cursor, tool_axis); |
648 | struct wlr_event_tablet_tool_axis *event = data; | 667 | struct wlr_tablet_tool_axis_event *event = data; |
649 | cursor_handle_activity_from_device(cursor, event->device); | 668 | cursor_handle_activity_from_device(cursor, &event->tablet->base); |
650 | 669 | ||
651 | struct sway_tablet_tool *sway_tool = event->tool->data; | 670 | struct sway_tablet_tool *sway_tool = event->tool->data; |
652 | if (!sway_tool) { | 671 | if (!sway_tool) { |
@@ -701,8 +720,8 @@ static void handle_tool_axis(struct wl_listener *listener, void *data) { | |||
701 | 720 | ||
702 | static void handle_tool_tip(struct wl_listener *listener, void *data) { | 721 | static void handle_tool_tip(struct wl_listener *listener, void *data) { |
703 | struct sway_cursor *cursor = wl_container_of(listener, cursor, tool_tip); | 722 | struct sway_cursor *cursor = wl_container_of(listener, cursor, tool_tip); |
704 | struct wlr_event_tablet_tool_tip *event = data; | 723 | struct wlr_tablet_tool_tip_event *event = data; |
705 | cursor_handle_activity_from_device(cursor, event->device); | 724 | cursor_handle_activity_from_device(cursor, &event->tablet->base); |
706 | 725 | ||
707 | struct sway_tablet_tool *sway_tool = event->tool->data; | 726 | struct sway_tablet_tool *sway_tool = event->tool->data; |
708 | struct wlr_tablet_v2_tablet *tablet_v2 = sway_tool->tablet->tablet_v2; | 727 | struct wlr_tablet_v2_tablet *tablet_v2 = sway_tool->tablet->tablet_v2; |
@@ -717,10 +736,9 @@ static void handle_tool_tip(struct wl_listener *listener, void *data) { | |||
717 | if (cursor->simulating_pointer_from_tool_tip && | 736 | if (cursor->simulating_pointer_from_tool_tip && |
718 | event->state == WLR_TABLET_TOOL_TIP_UP) { | 737 | event->state == WLR_TABLET_TOOL_TIP_UP) { |
719 | cursor->simulating_pointer_from_tool_tip = false; | 738 | cursor->simulating_pointer_from_tool_tip = false; |
720 | dispatch_cursor_button(cursor, event->device, event->time_msec, | 739 | dispatch_cursor_button(cursor, &event->tablet->base, event->time_msec, |
721 | BTN_LEFT, WLR_BUTTON_RELEASED); | 740 | BTN_LEFT, WLR_BUTTON_RELEASED); |
722 | wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat); | 741 | wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat); |
723 | transaction_commit_dirty(); | ||
724 | } else if (!surface || !wlr_surface_accepts_tablet_v2(tablet_v2, surface)) { | 742 | } else if (!surface || !wlr_surface_accepts_tablet_v2(tablet_v2, surface)) { |
725 | // If we started holding the tool tip down on a surface that accepts | 743 | // If we started holding the tool tip down on a surface that accepts |
726 | // tablet v2, we should notify that surface if it gets released over a | 744 | // tablet v2, we should notify that surface if it gets released over a |
@@ -730,10 +748,9 @@ static void handle_tool_tip(struct wl_listener *listener, void *data) { | |||
730 | WLR_TABLET_TOOL_TIP_UP); | 748 | WLR_TABLET_TOOL_TIP_UP); |
731 | } else { | 749 | } else { |
732 | cursor->simulating_pointer_from_tool_tip = true; | 750 | cursor->simulating_pointer_from_tool_tip = true; |
733 | dispatch_cursor_button(cursor, event->device, event->time_msec, | 751 | dispatch_cursor_button(cursor, &event->tablet->base, |
734 | BTN_LEFT, WLR_BUTTON_PRESSED); | 752 | event->time_msec, BTN_LEFT, WLR_BUTTON_PRESSED); |
735 | wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat); | 753 | wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat); |
736 | transaction_commit_dirty(); | ||
737 | } | 754 | } |
738 | } else { | 755 | } else { |
739 | seatop_tablet_tool_tip(seat, sway_tool, event->time_msec, event->state); | 756 | seatop_tablet_tool_tip(seat, sway_tool, event->time_msec, event->state); |
@@ -754,12 +771,13 @@ static struct sway_tablet *get_tablet_for_device(struct sway_cursor *cursor, | |||
754 | static void handle_tool_proximity(struct wl_listener *listener, void *data) { | 771 | static void handle_tool_proximity(struct wl_listener *listener, void *data) { |
755 | struct sway_cursor *cursor = | 772 | struct sway_cursor *cursor = |
756 | wl_container_of(listener, cursor, tool_proximity); | 773 | wl_container_of(listener, cursor, tool_proximity); |
757 | struct wlr_event_tablet_tool_proximity *event = data; | 774 | struct wlr_tablet_tool_proximity_event *event = data; |
758 | cursor_handle_activity_from_device(cursor, event->device); | 775 | cursor_handle_activity_from_device(cursor, &event->tablet->base); |
759 | 776 | ||
760 | struct wlr_tablet_tool *tool = event->tool; | 777 | struct wlr_tablet_tool *tool = event->tool; |
761 | if (!tool->data) { | 778 | if (!tool->data) { |
762 | struct sway_tablet *tablet = get_tablet_for_device(cursor, event->device); | 779 | struct sway_tablet *tablet = get_tablet_for_device(cursor, |
780 | &event->tablet->base); | ||
763 | if (!tablet) { | 781 | if (!tablet) { |
764 | sway_log(SWAY_ERROR, "no tablet for tablet tool"); | 782 | sway_log(SWAY_ERROR, "no tablet for tablet tool"); |
765 | return; | 783 | return; |
@@ -784,8 +802,8 @@ static void handle_tool_proximity(struct wl_listener *listener, void *data) { | |||
784 | 802 | ||
785 | static void handle_tool_button(struct wl_listener *listener, void *data) { | 803 | static void handle_tool_button(struct wl_listener *listener, void *data) { |
786 | struct sway_cursor *cursor = wl_container_of(listener, cursor, tool_button); | 804 | struct sway_cursor *cursor = wl_container_of(listener, cursor, tool_button); |
787 | struct wlr_event_tablet_tool_button *event = data; | 805 | struct wlr_tablet_tool_button_event *event = data; |
788 | cursor_handle_activity_from_device(cursor, event->device); | 806 | cursor_handle_activity_from_device(cursor, &event->tablet->base); |
789 | 807 | ||
790 | struct sway_tablet_tool *sway_tool = event->tool->data; | 808 | struct sway_tablet_tool *sway_tool = event->tool->data; |
791 | if (!sway_tool) { | 809 | if (!sway_tool) { |
@@ -806,21 +824,20 @@ static void handle_tool_button(struct wl_listener *listener, void *data) { | |||
806 | switch (event->state) { | 824 | switch (event->state) { |
807 | case WLR_BUTTON_PRESSED: | 825 | case WLR_BUTTON_PRESSED: |
808 | if (cursor->tool_buttons == 0) { | 826 | if (cursor->tool_buttons == 0) { |
809 | dispatch_cursor_button(cursor, event->device, | 827 | dispatch_cursor_button(cursor, &event->tablet->base, |
810 | event->time_msec, BTN_RIGHT, event->state); | 828 | event->time_msec, BTN_RIGHT, event->state); |
811 | } | 829 | } |
812 | cursor->tool_buttons++; | 830 | cursor->tool_buttons++; |
813 | break; | 831 | break; |
814 | case WLR_BUTTON_RELEASED: | 832 | case WLR_BUTTON_RELEASED: |
815 | if (cursor->tool_buttons == 1) { | 833 | if (cursor->tool_buttons == 1) { |
816 | dispatch_cursor_button(cursor, event->device, | 834 | dispatch_cursor_button(cursor, &event->tablet->base, |
817 | event->time_msec, BTN_RIGHT, event->state); | 835 | event->time_msec, BTN_RIGHT, event->state); |
818 | } | 836 | } |
819 | cursor->tool_buttons--; | 837 | cursor->tool_buttons--; |
820 | break; | 838 | break; |
821 | } | 839 | } |
822 | wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat); | 840 | wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat); |
823 | transaction_commit_dirty(); | ||
824 | return; | 841 | return; |
825 | } | 842 | } |
826 | 843 | ||
@@ -837,8 +854,8 @@ static void check_constraint_region(struct sway_cursor *cursor) { | |||
837 | 854 | ||
838 | struct sway_container *con = view->container; | 855 | struct sway_container *con = view->container; |
839 | 856 | ||
840 | double sx = cursor->cursor->x - con->content_x + view->geometry.x; | 857 | double sx = cursor->cursor->x - con->pending.content_x + view->geometry.x; |
841 | double sy = cursor->cursor->y - con->content_y + view->geometry.y; | 858 | double sy = cursor->cursor->y - con->pending.content_y + view->geometry.y; |
842 | 859 | ||
843 | if (!pixman_region32_contains_point(region, | 860 | if (!pixman_region32_contains_point(region, |
844 | floor(sx), floor(sy), NULL)) { | 861 | floor(sx), floor(sy), NULL)) { |
@@ -849,8 +866,8 @@ static void check_constraint_region(struct sway_cursor *cursor) { | |||
849 | double sy = (boxes[0].y1 + boxes[0].y2) / 2.; | 866 | double sy = (boxes[0].y1 + boxes[0].y2) / 2.; |
850 | 867 | ||
851 | wlr_cursor_warp_closest(cursor->cursor, NULL, | 868 | wlr_cursor_warp_closest(cursor->cursor, NULL, |
852 | sx + con->content_x - view->geometry.x, | 869 | sx + con->pending.content_x - view->geometry.x, |
853 | sy + con->content_y - view->geometry.y); | 870 | sy + con->pending.content_y - view->geometry.y); |
854 | 871 | ||
855 | cursor_rebase(cursor); | 872 | cursor_rebase(cursor); |
856 | } | 873 | } |
@@ -911,59 +928,68 @@ static void handle_request_pointer_set_cursor(struct wl_listener *listener, | |||
911 | event->hotspot_y, focused_client); | 928 | event->hotspot_y, focused_client); |
912 | } | 929 | } |
913 | 930 | ||
931 | static void handle_pointer_hold_begin(struct wl_listener *listener, void *data) { | ||
932 | struct sway_cursor *cursor = wl_container_of( | ||
933 | listener, cursor, hold_begin); | ||
934 | struct wlr_pointer_hold_begin_event *event = data; | ||
935 | cursor_handle_activity_from_device(cursor, &event->pointer->base); | ||
936 | seatop_hold_begin(cursor->seat, event); | ||
937 | } | ||
938 | |||
939 | static void handle_pointer_hold_end(struct wl_listener *listener, void *data) { | ||
940 | struct sway_cursor *cursor = wl_container_of( | ||
941 | listener, cursor, hold_end); | ||
942 | struct wlr_pointer_hold_end_event *event = data; | ||
943 | cursor_handle_activity_from_device(cursor, &event->pointer->base); | ||
944 | seatop_hold_end(cursor->seat, event); | ||
945 | } | ||
946 | |||
914 | static void handle_pointer_pinch_begin(struct wl_listener *listener, void *data) { | 947 | static void handle_pointer_pinch_begin(struct wl_listener *listener, void *data) { |
915 | struct sway_cursor *cursor = wl_container_of( | 948 | struct sway_cursor *cursor = wl_container_of( |
916 | listener, cursor, pinch_begin); | 949 | listener, cursor, pinch_begin); |
917 | struct wlr_event_pointer_pinch_begin *event = data; | 950 | struct wlr_pointer_pinch_begin_event *event = data; |
918 | wlr_pointer_gestures_v1_send_pinch_begin( | 951 | cursor_handle_activity_from_device(cursor, &event->pointer->base); |
919 | cursor->pointer_gestures, cursor->seat->wlr_seat, | 952 | seatop_pinch_begin(cursor->seat, event); |
920 | event->time_msec, event->fingers); | ||
921 | } | 953 | } |
922 | 954 | ||
923 | static void handle_pointer_pinch_update(struct wl_listener *listener, void *data) { | 955 | static void handle_pointer_pinch_update(struct wl_listener *listener, void *data) { |
924 | struct sway_cursor *cursor = wl_container_of( | 956 | struct sway_cursor *cursor = wl_container_of( |
925 | listener, cursor, pinch_update); | 957 | listener, cursor, pinch_update); |
926 | struct wlr_event_pointer_pinch_update *event = data; | 958 | struct wlr_pointer_pinch_update_event *event = data; |
927 | wlr_pointer_gestures_v1_send_pinch_update( | 959 | cursor_handle_activity_from_device(cursor, &event->pointer->base); |
928 | cursor->pointer_gestures, cursor->seat->wlr_seat, | 960 | seatop_pinch_update(cursor->seat, event); |
929 | event->time_msec, event->dx, event->dy, | ||
930 | event->scale, event->rotation); | ||
931 | } | 961 | } |
932 | 962 | ||
933 | static void handle_pointer_pinch_end(struct wl_listener *listener, void *data) { | 963 | static void handle_pointer_pinch_end(struct wl_listener *listener, void *data) { |
934 | struct sway_cursor *cursor = wl_container_of( | 964 | struct sway_cursor *cursor = wl_container_of( |
935 | listener, cursor, pinch_end); | 965 | listener, cursor, pinch_end); |
936 | struct wlr_event_pointer_pinch_end *event = data; | 966 | struct wlr_pointer_pinch_end_event *event = data; |
937 | wlr_pointer_gestures_v1_send_pinch_end( | 967 | cursor_handle_activity_from_device(cursor, &event->pointer->base); |
938 | cursor->pointer_gestures, cursor->seat->wlr_seat, | 968 | seatop_pinch_end(cursor->seat, event); |
939 | event->time_msec, event->cancelled); | ||
940 | } | 969 | } |
941 | 970 | ||
942 | static void handle_pointer_swipe_begin(struct wl_listener *listener, void *data) { | 971 | static void handle_pointer_swipe_begin(struct wl_listener *listener, void *data) { |
943 | struct sway_cursor *cursor = wl_container_of( | 972 | struct sway_cursor *cursor = wl_container_of( |
944 | listener, cursor, swipe_begin); | 973 | listener, cursor, swipe_begin); |
945 | struct wlr_event_pointer_swipe_begin *event = data; | 974 | struct wlr_pointer_swipe_begin_event *event = data; |
946 | wlr_pointer_gestures_v1_send_swipe_begin( | 975 | cursor_handle_activity_from_device(cursor, &event->pointer->base); |
947 | cursor->pointer_gestures, cursor->seat->wlr_seat, | 976 | seatop_swipe_begin(cursor->seat, event); |
948 | event->time_msec, event->fingers); | ||
949 | } | 977 | } |
950 | 978 | ||
951 | static void handle_pointer_swipe_update(struct wl_listener *listener, void *data) { | 979 | static void handle_pointer_swipe_update(struct wl_listener *listener, void *data) { |
952 | struct sway_cursor *cursor = wl_container_of( | 980 | struct sway_cursor *cursor = wl_container_of( |
953 | listener, cursor, swipe_update); | 981 | listener, cursor, swipe_update); |
954 | struct wlr_event_pointer_swipe_update *event = data; | 982 | struct wlr_pointer_swipe_update_event *event = data; |
955 | wlr_pointer_gestures_v1_send_swipe_update( | 983 | cursor_handle_activity_from_device(cursor, &event->pointer->base); |
956 | cursor->pointer_gestures, cursor->seat->wlr_seat, | 984 | seatop_swipe_update(cursor->seat, event); |
957 | event->time_msec, event->dx, event->dy); | ||
958 | } | 985 | } |
959 | 986 | ||
960 | static void handle_pointer_swipe_end(struct wl_listener *listener, void *data) { | 987 | static void handle_pointer_swipe_end(struct wl_listener *listener, void *data) { |
961 | struct sway_cursor *cursor = wl_container_of( | 988 | struct sway_cursor *cursor = wl_container_of( |
962 | listener, cursor, swipe_end); | 989 | listener, cursor, swipe_end); |
963 | struct wlr_event_pointer_swipe_end *event = data; | 990 | struct wlr_pointer_swipe_end_event *event = data; |
964 | wlr_pointer_gestures_v1_send_swipe_end( | 991 | cursor_handle_activity_from_device(cursor, &event->pointer->base); |
965 | cursor->pointer_gestures, cursor->seat->wlr_seat, | 992 | seatop_swipe_end(cursor->seat, event); |
966 | event->time_msec, event->cancelled); | ||
967 | } | 993 | } |
968 | 994 | ||
969 | static void handle_image_surface_destroy(struct wl_listener *listener, | 995 | static void handle_image_surface_destroy(struct wl_listener *listener, |
@@ -1037,6 +1063,8 @@ void sway_cursor_destroy(struct sway_cursor *cursor) { | |||
1037 | wl_event_source_remove(cursor->hide_source); | 1063 | wl_event_source_remove(cursor->hide_source); |
1038 | 1064 | ||
1039 | wl_list_remove(&cursor->image_surface_destroy.link); | 1065 | wl_list_remove(&cursor->image_surface_destroy.link); |
1066 | wl_list_remove(&cursor->hold_begin.link); | ||
1067 | wl_list_remove(&cursor->hold_end.link); | ||
1040 | wl_list_remove(&cursor->pinch_begin.link); | 1068 | wl_list_remove(&cursor->pinch_begin.link); |
1041 | wl_list_remove(&cursor->pinch_update.link); | 1069 | wl_list_remove(&cursor->pinch_update.link); |
1042 | wl_list_remove(&cursor->pinch_end.link); | 1070 | wl_list_remove(&cursor->pinch_end.link); |
@@ -1051,6 +1079,7 @@ void sway_cursor_destroy(struct sway_cursor *cursor) { | |||
1051 | wl_list_remove(&cursor->touch_down.link); | 1079 | wl_list_remove(&cursor->touch_down.link); |
1052 | wl_list_remove(&cursor->touch_up.link); | 1080 | wl_list_remove(&cursor->touch_up.link); |
1053 | wl_list_remove(&cursor->touch_motion.link); | 1081 | wl_list_remove(&cursor->touch_motion.link); |
1082 | wl_list_remove(&cursor->touch_frame.link); | ||
1054 | wl_list_remove(&cursor->tool_axis.link); | 1083 | wl_list_remove(&cursor->tool_axis.link); |
1055 | wl_list_remove(&cursor->tool_tip.link); | 1084 | wl_list_remove(&cursor->tool_tip.link); |
1056 | wl_list_remove(&cursor->tool_button.link); | 1085 | wl_list_remove(&cursor->tool_button.link); |
@@ -1085,19 +1114,27 @@ struct sway_cursor *sway_cursor_create(struct sway_seat *seat) { | |||
1085 | wl_list_init(&cursor->image_surface_destroy.link); | 1114 | wl_list_init(&cursor->image_surface_destroy.link); |
1086 | cursor->image_surface_destroy.notify = handle_image_surface_destroy; | 1115 | cursor->image_surface_destroy.notify = handle_image_surface_destroy; |
1087 | 1116 | ||
1117 | // gesture events | ||
1088 | cursor->pointer_gestures = wlr_pointer_gestures_v1_create(server.wl_display); | 1118 | cursor->pointer_gestures = wlr_pointer_gestures_v1_create(server.wl_display); |
1089 | cursor->pinch_begin.notify = handle_pointer_pinch_begin; | 1119 | |
1120 | wl_signal_add(&wlr_cursor->events.hold_begin, &cursor->hold_begin); | ||
1121 | cursor->hold_begin.notify = handle_pointer_hold_begin; | ||
1122 | wl_signal_add(&wlr_cursor->events.hold_end, &cursor->hold_end); | ||
1123 | cursor->hold_end.notify = handle_pointer_hold_end; | ||
1124 | |||
1090 | wl_signal_add(&wlr_cursor->events.pinch_begin, &cursor->pinch_begin); | 1125 | wl_signal_add(&wlr_cursor->events.pinch_begin, &cursor->pinch_begin); |
1091 | cursor->pinch_update.notify = handle_pointer_pinch_update; | 1126 | cursor->pinch_begin.notify = handle_pointer_pinch_begin; |
1092 | wl_signal_add(&wlr_cursor->events.pinch_update, &cursor->pinch_update); | 1127 | wl_signal_add(&wlr_cursor->events.pinch_update, &cursor->pinch_update); |
1093 | cursor->pinch_end.notify = handle_pointer_pinch_end; | 1128 | cursor->pinch_update.notify = handle_pointer_pinch_update; |
1094 | wl_signal_add(&wlr_cursor->events.pinch_end, &cursor->pinch_end); | 1129 | wl_signal_add(&wlr_cursor->events.pinch_end, &cursor->pinch_end); |
1095 | cursor->swipe_begin.notify = handle_pointer_swipe_begin; | 1130 | cursor->pinch_end.notify = handle_pointer_pinch_end; |
1131 | |||
1096 | wl_signal_add(&wlr_cursor->events.swipe_begin, &cursor->swipe_begin); | 1132 | wl_signal_add(&wlr_cursor->events.swipe_begin, &cursor->swipe_begin); |
1097 | cursor->swipe_update.notify = handle_pointer_swipe_update; | 1133 | cursor->swipe_begin.notify = handle_pointer_swipe_begin; |
1098 | wl_signal_add(&wlr_cursor->events.swipe_update, &cursor->swipe_update); | 1134 | wl_signal_add(&wlr_cursor->events.swipe_update, &cursor->swipe_update); |
1099 | cursor->swipe_end.notify = handle_pointer_swipe_end; | 1135 | cursor->swipe_update.notify = handle_pointer_swipe_update; |
1100 | wl_signal_add(&wlr_cursor->events.swipe_end, &cursor->swipe_end); | 1136 | wl_signal_add(&wlr_cursor->events.swipe_end, &cursor->swipe_end); |
1137 | cursor->swipe_end.notify = handle_pointer_swipe_end; | ||
1101 | 1138 | ||
1102 | // input events | 1139 | // input events |
1103 | wl_signal_add(&wlr_cursor->events.motion, &cursor->motion); | 1140 | wl_signal_add(&wlr_cursor->events.motion, &cursor->motion); |
@@ -1126,6 +1163,9 @@ struct sway_cursor *sway_cursor_create(struct sway_seat *seat) { | |||
1126 | &cursor->touch_motion); | 1163 | &cursor->touch_motion); |
1127 | cursor->touch_motion.notify = handle_touch_motion; | 1164 | cursor->touch_motion.notify = handle_touch_motion; |
1128 | 1165 | ||
1166 | wl_signal_add(&wlr_cursor->events.touch_frame, &cursor->touch_frame); | ||
1167 | cursor->touch_frame.notify = handle_touch_frame; | ||
1168 | |||
1129 | wl_signal_add(&wlr_cursor->events.tablet_tool_axis, | 1169 | wl_signal_add(&wlr_cursor->events.tablet_tool_axis, |
1130 | &cursor->tool_axis); | 1170 | &cursor->tool_axis); |
1131 | cursor->tool_axis.notify = handle_tool_axis; | 1171 | cursor->tool_axis.notify = handle_tool_axis; |
@@ -1170,8 +1210,8 @@ void cursor_warp_to_container(struct sway_cursor *cursor, | |||
1170 | return; | 1210 | return; |
1171 | } | 1211 | } |
1172 | 1212 | ||
1173 | double x = container->x + container->width / 2.0; | 1213 | double x = container->pending.x + container->pending.width / 2.0; |
1174 | double y = container->y + container->height / 2.0; | 1214 | double y = container->pending.y + container->pending.height / 2.0; |
1175 | 1215 | ||
1176 | wlr_cursor_warp(cursor->cursor, NULL, x, y); | 1216 | wlr_cursor_warp(cursor->cursor, NULL, x, y); |
1177 | cursor_unhide(cursor); | 1217 | cursor_unhide(cursor); |
@@ -1284,8 +1324,8 @@ static void warp_to_constraint_cursor_hint(struct sway_cursor *cursor) { | |||
1284 | struct sway_view *view = view_from_wlr_surface(constraint->surface); | 1324 | struct sway_view *view = view_from_wlr_surface(constraint->surface); |
1285 | struct sway_container *con = view->container; | 1325 | struct sway_container *con = view->container; |
1286 | 1326 | ||
1287 | double lx = sx + con->content_x - view->geometry.x; | 1327 | double lx = sx + con->pending.content_x - view->geometry.x; |
1288 | double ly = sy + con->content_y - view->geometry.y; | 1328 | double ly = sy + con->pending.content_y - view->geometry.y; |
1289 | 1329 | ||
1290 | wlr_cursor_warp(cursor->cursor, NULL, lx, ly); | 1330 | wlr_cursor_warp(cursor->cursor, NULL, lx, ly); |
1291 | 1331 | ||
@@ -1333,7 +1373,7 @@ void handle_pointer_constraint(struct wl_listener *listener, void *data) { | |||
1333 | wl_signal_add(&constraint->events.destroy, &sway_constraint->destroy); | 1373 | wl_signal_add(&constraint->events.destroy, &sway_constraint->destroy); |
1334 | 1374 | ||
1335 | struct sway_node *focus = seat_get_focus(seat); | 1375 | struct sway_node *focus = seat_get_focus(seat); |
1336 | if (focus && focus->type == N_CONTAINER && focus->sway_container->view) { | 1376 | if (focus && node_is_view(focus)) { |
1337 | struct wlr_surface *surface = focus->sway_container->view->surface; | 1377 | struct wlr_surface *surface = focus->sway_container->view->surface; |
1338 | if (surface == constraint->surface) { | 1378 | if (surface == constraint->surface) { |
1339 | sway_cursor_constrain(seat->cursor, constraint); | 1379 | sway_cursor_constrain(seat->cursor, constraint); |
diff --git a/sway/input/input-manager.c b/sway/input/input-manager.c index f04a8ce0..4a0bce0e 100644 --- a/sway/input/input-manager.c +++ b/sway/input/input-manager.c | |||
@@ -289,6 +289,10 @@ static void handle_inhibit_deactivate(struct wl_listener *listener, void *data) | |||
289 | struct sway_input_manager *input_manager = wl_container_of( | 289 | struct sway_input_manager *input_manager = wl_container_of( |
290 | listener, input_manager, inhibit_deactivate); | 290 | listener, input_manager, inhibit_deactivate); |
291 | struct sway_seat *seat; | 291 | struct sway_seat *seat; |
292 | if (server.session_lock.locked) { | ||
293 | // Don't deactivate the grab of a screenlocker | ||
294 | return; | ||
295 | } | ||
292 | wl_list_for_each(seat, &input_manager->seats, link) { | 296 | wl_list_for_each(seat, &input_manager->seats, link) { |
293 | seat_set_exclusive_client(seat, NULL); | 297 | seat_set_exclusive_client(seat, NULL); |
294 | struct sway_node *previous = seat_get_focus(seat); | 298 | struct sway_node *previous = seat_get_focus(seat); |
@@ -377,7 +381,7 @@ void handle_virtual_keyboard(struct wl_listener *listener, void *data) { | |||
377 | struct sway_input_manager *input_manager = | 381 | struct sway_input_manager *input_manager = |
378 | wl_container_of(listener, input_manager, virtual_keyboard_new); | 382 | wl_container_of(listener, input_manager, virtual_keyboard_new); |
379 | struct wlr_virtual_keyboard_v1 *keyboard = data; | 383 | struct wlr_virtual_keyboard_v1 *keyboard = data; |
380 | struct wlr_input_device *device = &keyboard->input_device; | 384 | struct wlr_input_device *device = &keyboard->keyboard.base; |
381 | 385 | ||
382 | // TODO: Amend protocol to allow NULL seat | 386 | // TODO: Amend protocol to allow NULL seat |
383 | struct sway_seat *seat = keyboard->seat ? | 387 | struct sway_seat *seat = keyboard->seat ? |
@@ -410,7 +414,7 @@ void handle_virtual_pointer(struct wl_listener *listener, void *data) { | |||
410 | wl_container_of(listener, input_manager, virtual_pointer_new); | 414 | wl_container_of(listener, input_manager, virtual_pointer_new); |
411 | struct wlr_virtual_pointer_v1_new_pointer_event *event = data; | 415 | struct wlr_virtual_pointer_v1_new_pointer_event *event = data; |
412 | struct wlr_virtual_pointer_v1 *pointer = event->new_pointer; | 416 | struct wlr_virtual_pointer_v1 *pointer = event->new_pointer; |
413 | struct wlr_input_device *device = &pointer->input_device; | 417 | struct wlr_input_device *device = &pointer->pointer.base; |
414 | 418 | ||
415 | struct sway_seat *seat = event->suggested_seat ? | 419 | struct sway_seat *seat = event->suggested_seat ? |
416 | input_manager_sway_seat_from_wlr_seat(event->suggested_seat) : | 420 | input_manager_sway_seat_from_wlr_seat(event->suggested_seat) : |
diff --git a/sway/input/keyboard.c b/sway/input/keyboard.c index ce259eb2..5e5692f1 100644 --- a/sway/input/keyboard.c +++ b/sway/input/keyboard.c | |||
@@ -9,7 +9,6 @@ | |||
9 | #include <wlr/types/wlr_keyboard_group.h> | 9 | #include <wlr/types/wlr_keyboard_group.h> |
10 | #include <xkbcommon/xkbcommon-names.h> | 10 | #include <xkbcommon/xkbcommon-names.h> |
11 | #include "sway/commands.h" | 11 | #include "sway/commands.h" |
12 | #include "sway/desktop/transaction.h" | ||
13 | #include "sway/input/input-manager.h" | 12 | #include "sway/input/input-manager.h" |
14 | #include "sway/input/keyboard.h" | 13 | #include "sway/input/keyboard.h" |
15 | #include "sway/input/seat.h" | 14 | #include "sway/input/seat.h" |
@@ -292,14 +291,12 @@ static bool keyboard_execute_compositor_binding(struct sway_keyboard *keyboard, | |||
292 | static size_t keyboard_keysyms_translated(struct sway_keyboard *keyboard, | 291 | static size_t keyboard_keysyms_translated(struct sway_keyboard *keyboard, |
293 | xkb_keycode_t keycode, const xkb_keysym_t **keysyms, | 292 | xkb_keycode_t keycode, const xkb_keysym_t **keysyms, |
294 | uint32_t *modifiers) { | 293 | uint32_t *modifiers) { |
295 | struct wlr_input_device *device = | 294 | *modifiers = wlr_keyboard_get_modifiers(keyboard->wlr); |
296 | keyboard->seat_device->input_device->wlr_device; | ||
297 | *modifiers = wlr_keyboard_get_modifiers(device->keyboard); | ||
298 | xkb_mod_mask_t consumed = xkb_state_key_get_consumed_mods2( | 295 | xkb_mod_mask_t consumed = xkb_state_key_get_consumed_mods2( |
299 | device->keyboard->xkb_state, keycode, XKB_CONSUMED_MODE_XKB); | 296 | keyboard->wlr->xkb_state, keycode, XKB_CONSUMED_MODE_XKB); |
300 | *modifiers = *modifiers & ~consumed; | 297 | *modifiers = *modifiers & ~consumed; |
301 | 298 | ||
302 | return xkb_state_key_get_syms(device->keyboard->xkb_state, | 299 | return xkb_state_key_get_syms(keyboard->wlr->xkb_state, |
303 | keycode, keysyms); | 300 | keycode, keysyms); |
304 | } | 301 | } |
305 | 302 | ||
@@ -315,13 +312,11 @@ static size_t keyboard_keysyms_translated(struct sway_keyboard *keyboard, | |||
315 | static size_t keyboard_keysyms_raw(struct sway_keyboard *keyboard, | 312 | static size_t keyboard_keysyms_raw(struct sway_keyboard *keyboard, |
316 | xkb_keycode_t keycode, const xkb_keysym_t **keysyms, | 313 | xkb_keycode_t keycode, const xkb_keysym_t **keysyms, |
317 | uint32_t *modifiers) { | 314 | uint32_t *modifiers) { |
318 | struct wlr_input_device *device = | 315 | *modifiers = wlr_keyboard_get_modifiers(keyboard->wlr); |
319 | keyboard->seat_device->input_device->wlr_device; | ||
320 | *modifiers = wlr_keyboard_get_modifiers(device->keyboard); | ||
321 | 316 | ||
322 | xkb_layout_index_t layout_index = xkb_state_key_get_layout( | 317 | xkb_layout_index_t layout_index = xkb_state_key_get_layout( |
323 | device->keyboard->xkb_state, keycode); | 318 | keyboard->wlr->xkb_state, keycode); |
324 | return xkb_keymap_key_get_syms_by_level(device->keyboard->keymap, | 319 | return xkb_keymap_key_get_syms_by_level(keyboard->wlr->keymap, |
325 | keycode, layout_index, 0, keysyms); | 320 | keycode, layout_index, 0, keysyms); |
326 | } | 321 | } |
327 | 322 | ||
@@ -361,8 +356,7 @@ static void update_keyboard_state(struct sway_keyboard *keyboard, | |||
361 | keyinfo->keycode, &keyinfo->translated_keysyms, | 356 | keyinfo->keycode, &keyinfo->translated_keysyms, |
362 | &keyinfo->translated_modifiers); | 357 | &keyinfo->translated_modifiers); |
363 | 358 | ||
364 | keyinfo->code_modifiers = wlr_keyboard_get_modifiers( | 359 | keyinfo->code_modifiers = wlr_keyboard_get_modifiers(keyboard->wlr); |
365 | keyboard->seat_device->input_device->wlr_device->keyboard); | ||
366 | 360 | ||
367 | // Update shortcut model keyinfo | 361 | // Update shortcut model keyinfo |
368 | update_shortcut_state(&keyboard->state_keycodes, raw_keycode, keystate, | 362 | update_shortcut_state(&keyboard->state_keycodes, raw_keycode, keystate, |
@@ -379,16 +373,39 @@ static void update_keyboard_state(struct sway_keyboard *keyboard, | |||
379 | } | 373 | } |
380 | } | 374 | } |
381 | 375 | ||
376 | /** | ||
377 | * Get keyboard grab of the seat from sway_keyboard if we should forward events | ||
378 | * to it. | ||
379 | * | ||
380 | * Returns NULL if the keyboard is not grabbed by an input method, | ||
381 | * or if event is from virtual keyboard of the same client as grab. | ||
382 | * TODO: see swaywm/wlroots#2322 | ||
383 | */ | ||
384 | static struct wlr_input_method_keyboard_grab_v2 *keyboard_get_im_grab( | ||
385 | struct sway_keyboard *keyboard) { | ||
386 | struct wlr_input_method_v2 *input_method = keyboard->seat_device-> | ||
387 | sway_seat->im_relay.input_method; | ||
388 | struct wlr_virtual_keyboard_v1 *virtual_keyboard = | ||
389 | wlr_input_device_get_virtual_keyboard(keyboard->seat_device->input_device->wlr_device); | ||
390 | if (!input_method || !input_method->keyboard_grab || (virtual_keyboard && | ||
391 | wl_resource_get_client(virtual_keyboard->resource) == | ||
392 | wl_resource_get_client(input_method->keyboard_grab->resource))) { | ||
393 | return NULL; | ||
394 | } | ||
395 | return input_method->keyboard_grab; | ||
396 | } | ||
397 | |||
382 | static void handle_key_event(struct sway_keyboard *keyboard, | 398 | static void handle_key_event(struct sway_keyboard *keyboard, |
383 | struct wlr_event_keyboard_key *event) { | 399 | struct wlr_keyboard_key_event *event) { |
384 | struct sway_seat *seat = keyboard->seat_device->sway_seat; | 400 | struct sway_seat *seat = keyboard->seat_device->sway_seat; |
385 | struct wlr_seat *wlr_seat = seat->wlr_seat; | 401 | struct wlr_seat *wlr_seat = seat->wlr_seat; |
386 | struct wlr_input_device *wlr_device = | 402 | struct wlr_input_device *wlr_device = |
387 | keyboard->seat_device->input_device->wlr_device; | 403 | keyboard->seat_device->input_device->wlr_device; |
388 | char *device_identifier = input_device_get_identifier(wlr_device); | 404 | char *device_identifier = input_device_get_identifier(wlr_device); |
389 | bool exact_identifier = wlr_device->keyboard->group != NULL; | 405 | bool exact_identifier = keyboard->wlr->group != NULL; |
390 | seat_idle_notify_activity(seat, IDLE_SOURCE_KEYBOARD); | 406 | seat_idle_notify_activity(seat, IDLE_SOURCE_KEYBOARD); |
391 | bool input_inhibited = seat->exclusive_client != NULL; | 407 | bool input_inhibited = seat->exclusive_client != NULL || |
408 | server.session_lock.locked; | ||
392 | struct sway_keyboard_shortcuts_inhibitor *sway_inhibitor = | 409 | struct sway_keyboard_shortcuts_inhibitor *sway_inhibitor = |
393 | keyboard_shortcuts_inhibitor_get_for_focused_surface(seat); | 410 | keyboard_shortcuts_inhibitor_get_for_focused_surface(seat); |
394 | bool shortcuts_inhibited = sway_inhibitor && sway_inhibitor->inhibitor->active; | 411 | bool shortcuts_inhibited = sway_inhibitor && sway_inhibitor->inhibitor->active; |
@@ -456,10 +473,10 @@ static void handle_key_event(struct sway_keyboard *keyboard, | |||
456 | // Set up (or clear) keyboard repeat for a pressed binding. Since the | 473 | // Set up (or clear) keyboard repeat for a pressed binding. Since the |
457 | // binding may remove the keyboard, the timer needs to be updated first | 474 | // binding may remove the keyboard, the timer needs to be updated first |
458 | if (binding && !(binding->flags & BINDING_NOREPEAT) && | 475 | if (binding && !(binding->flags & BINDING_NOREPEAT) && |
459 | wlr_device->keyboard->repeat_info.delay > 0) { | 476 | keyboard->wlr->repeat_info.delay > 0) { |
460 | keyboard->repeat_binding = binding; | 477 | keyboard->repeat_binding = binding; |
461 | if (wl_event_source_timer_update(keyboard->key_repeat_source, | 478 | if (wl_event_source_timer_update(keyboard->key_repeat_source, |
462 | wlr_device->keyboard->repeat_info.delay) < 0) { | 479 | keyboard->wlr->repeat_info.delay) < 0) { |
463 | sway_log(SWAY_DEBUG, "failed to set key repeat timer"); | 480 | sway_log(SWAY_DEBUG, "failed to set key repeat timer"); |
464 | } | 481 | } |
465 | } else if (keyboard->repeat_binding) { | 482 | } else if (keyboard->repeat_binding) { |
@@ -471,7 +488,7 @@ static void handle_key_event(struct sway_keyboard *keyboard, | |||
471 | handled = true; | 488 | handled = true; |
472 | } | 489 | } |
473 | 490 | ||
474 | if (!handled && wlr_device->keyboard->group) { | 491 | if (!handled && keyboard->wlr->group) { |
475 | // Only handle device specific bindings for keyboards in a group | 492 | // Only handle device specific bindings for keyboards in a group |
476 | free(device_identifier); | 493 | free(device_identifier); |
477 | return; | 494 | return; |
@@ -489,18 +506,41 @@ static void handle_key_event(struct sway_keyboard *keyboard, | |||
489 | keyinfo.raw_keysyms_len); | 506 | keyinfo.raw_keysyms_len); |
490 | } | 507 | } |
491 | 508 | ||
492 | if (!handled || event->state == WL_KEYBOARD_KEY_STATE_RELEASED) { | 509 | if (event->state == WL_KEYBOARD_KEY_STATE_RELEASED) { |
510 | // If the pressed event was sent to a client, also send the released | ||
511 | // event. In particular, don't send the released event to the IM grab. | ||
493 | bool pressed_sent = update_shortcut_state( | 512 | bool pressed_sent = update_shortcut_state( |
494 | &keyboard->state_pressed_sent, event->keycode, event->state, | 513 | &keyboard->state_pressed_sent, event->keycode, |
495 | keyinfo.keycode, 0); | 514 | event->state, keyinfo.keycode, 0); |
496 | if (pressed_sent || event->state == WL_KEYBOARD_KEY_STATE_PRESSED) { | 515 | if (pressed_sent) { |
497 | wlr_seat_set_keyboard(wlr_seat, wlr_device); | 516 | wlr_seat_set_keyboard(wlr_seat, keyboard->wlr); |
498 | wlr_seat_keyboard_notify_key(wlr_seat, event->time_msec, | 517 | wlr_seat_keyboard_notify_key(wlr_seat, event->time_msec, |
499 | event->keycode, event->state); | 518 | event->keycode, event->state); |
519 | handled = true; | ||
500 | } | 520 | } |
501 | } | 521 | } |
502 | 522 | ||
503 | transaction_commit_dirty(); | 523 | if (!handled) { |
524 | struct wlr_input_method_keyboard_grab_v2 *kb_grab = keyboard_get_im_grab(keyboard); | ||
525 | |||
526 | if (kb_grab) { | ||
527 | wlr_input_method_keyboard_grab_v2_set_keyboard(kb_grab, keyboard->wlr); | ||
528 | wlr_input_method_keyboard_grab_v2_send_key(kb_grab, | ||
529 | event->time_msec, event->keycode, event->state); | ||
530 | handled = true; | ||
531 | } | ||
532 | } | ||
533 | |||
534 | if (!handled && event->state != WL_KEYBOARD_KEY_STATE_RELEASED) { | ||
535 | // If a released event failed pressed sent test, and not in sent to | ||
536 | // keyboard grab, it is still not handled. Don't handle released here. | ||
537 | update_shortcut_state( | ||
538 | &keyboard->state_pressed_sent, event->keycode, event->state, | ||
539 | keyinfo.keycode, 0); | ||
540 | wlr_seat_set_keyboard(wlr_seat, keyboard->wlr); | ||
541 | wlr_seat_keyboard_notify_key(wlr_seat, event->time_msec, | ||
542 | event->keycode, event->state); | ||
543 | } | ||
504 | 544 | ||
505 | free(device_identifier); | 545 | free(device_identifier); |
506 | } | 546 | } |
@@ -573,21 +613,18 @@ static void handle_keyboard_group_leave(struct wl_listener *listener, | |||
573 | } | 613 | } |
574 | 614 | ||
575 | static int handle_keyboard_repeat(void *data) { | 615 | static int handle_keyboard_repeat(void *data) { |
576 | struct sway_keyboard *keyboard = (struct sway_keyboard *)data; | 616 | struct sway_keyboard *keyboard = data; |
577 | struct wlr_keyboard *wlr_device = | ||
578 | keyboard->seat_device->input_device->wlr_device->keyboard; | ||
579 | if (keyboard->repeat_binding) { | 617 | if (keyboard->repeat_binding) { |
580 | if (wlr_device->repeat_info.rate > 0) { | 618 | if (keyboard->wlr->repeat_info.rate > 0) { |
581 | // We queue the next event first, as the command might cancel it | 619 | // We queue the next event first, as the command might cancel it |
582 | if (wl_event_source_timer_update(keyboard->key_repeat_source, | 620 | if (wl_event_source_timer_update(keyboard->key_repeat_source, |
583 | 1000 / wlr_device->repeat_info.rate) < 0) { | 621 | 1000 / keyboard->wlr->repeat_info.rate) < 0) { |
584 | sway_log(SWAY_DEBUG, "failed to update key repeat timer"); | 622 | sway_log(SWAY_DEBUG, "failed to update key repeat timer"); |
585 | } | 623 | } |
586 | } | 624 | } |
587 | 625 | ||
588 | seat_execute_command(keyboard->seat_device->sway_seat, | 626 | seat_execute_command(keyboard->seat_device->sway_seat, |
589 | keyboard->repeat_binding); | 627 | keyboard->repeat_binding); |
590 | transaction_commit_dirty(); | ||
591 | } | 628 | } |
592 | return 0; | 629 | return 0; |
593 | } | 630 | } |
@@ -614,22 +651,28 @@ static void determine_bar_visibility(uint32_t modifiers) { | |||
614 | } | 651 | } |
615 | 652 | ||
616 | static void handle_modifier_event(struct sway_keyboard *keyboard) { | 653 | static void handle_modifier_event(struct sway_keyboard *keyboard) { |
617 | struct wlr_input_device *wlr_device = | 654 | if (!keyboard->wlr->group) { |
618 | keyboard->seat_device->input_device->wlr_device; | 655 | struct wlr_input_method_keyboard_grab_v2 *kb_grab = keyboard_get_im_grab(keyboard); |
619 | if (!wlr_device->keyboard->group) { | ||
620 | struct wlr_seat *wlr_seat = keyboard->seat_device->sway_seat->wlr_seat; | ||
621 | wlr_seat_set_keyboard(wlr_seat, wlr_device); | ||
622 | wlr_seat_keyboard_notify_modifiers(wlr_seat, | ||
623 | &wlr_device->keyboard->modifiers); | ||
624 | 656 | ||
625 | uint32_t modifiers = wlr_keyboard_get_modifiers(wlr_device->keyboard); | 657 | if (kb_grab) { |
658 | wlr_input_method_keyboard_grab_v2_set_keyboard(kb_grab, keyboard->wlr); | ||
659 | wlr_input_method_keyboard_grab_v2_send_modifiers(kb_grab, | ||
660 | &keyboard->wlr->modifiers); | ||
661 | } else { | ||
662 | struct wlr_seat *wlr_seat = keyboard->seat_device->sway_seat->wlr_seat; | ||
663 | wlr_seat_set_keyboard(wlr_seat, keyboard->wlr); | ||
664 | wlr_seat_keyboard_notify_modifiers(wlr_seat, | ||
665 | &keyboard->wlr->modifiers); | ||
666 | } | ||
667 | |||
668 | uint32_t modifiers = wlr_keyboard_get_modifiers(keyboard->wlr); | ||
626 | determine_bar_visibility(modifiers); | 669 | determine_bar_visibility(modifiers); |
627 | } | 670 | } |
628 | 671 | ||
629 | if (wlr_device->keyboard->modifiers.group != keyboard->effective_layout) { | 672 | if (keyboard->wlr->modifiers.group != keyboard->effective_layout) { |
630 | keyboard->effective_layout = wlr_device->keyboard->modifiers.group; | 673 | keyboard->effective_layout = keyboard->wlr->modifiers.group; |
631 | 674 | ||
632 | if (!wlr_keyboard_group_from_wlr_keyboard(wlr_device->keyboard)) { | 675 | if (!wlr_keyboard_group_from_wlr_keyboard(keyboard->wlr)) { |
633 | ipc_event_input("xkb_layout", keyboard->seat_device->input_device); | 676 | ipc_event_input("xkb_layout", keyboard->seat_device->input_device); |
634 | } | 677 | } |
635 | } | 678 | } |
@@ -658,6 +701,7 @@ struct sway_keyboard *sway_keyboard_create(struct sway_seat *seat, | |||
658 | } | 701 | } |
659 | 702 | ||
660 | keyboard->seat_device = device; | 703 | keyboard->seat_device = device; |
704 | keyboard->wlr = wlr_keyboard_from_input_device(device->input_device->wlr_device); | ||
661 | device->keyboard = keyboard; | 705 | device->keyboard = keyboard; |
662 | 706 | ||
663 | wl_list_init(&keyboard->keyboard_key.link); | 707 | wl_list_init(&keyboard->keyboard_key.link); |
@@ -766,13 +810,12 @@ static void destroy_empty_wlr_keyboard_group(void *data) { | |||
766 | 810 | ||
767 | static void sway_keyboard_group_remove(struct sway_keyboard *keyboard) { | 811 | static void sway_keyboard_group_remove(struct sway_keyboard *keyboard) { |
768 | struct sway_input_device *device = keyboard->seat_device->input_device; | 812 | struct sway_input_device *device = keyboard->seat_device->input_device; |
769 | struct wlr_keyboard *wlr_keyboard = device->wlr_device->keyboard; | 813 | struct wlr_keyboard_group *wlr_group = keyboard->wlr->group; |
770 | struct wlr_keyboard_group *wlr_group = wlr_keyboard->group; | ||
771 | 814 | ||
772 | sway_log(SWAY_DEBUG, "Removing keyboard %s from group %p", | 815 | sway_log(SWAY_DEBUG, "Removing keyboard %s from group %p", |
773 | device->identifier, wlr_group); | 816 | device->identifier, wlr_group); |
774 | 817 | ||
775 | wlr_keyboard_group_remove_keyboard(wlr_keyboard->group, wlr_keyboard); | 818 | wlr_keyboard_group_remove_keyboard(keyboard->wlr->group, keyboard->wlr); |
776 | 819 | ||
777 | if (wl_list_empty(&wlr_group->devices)) { | 820 | if (wl_list_empty(&wlr_group->devices)) { |
778 | sway_log(SWAY_DEBUG, "Destroying empty keyboard group %p", | 821 | sway_log(SWAY_DEBUG, "Destroying empty keyboard group %p", |
@@ -797,9 +840,7 @@ static void sway_keyboard_group_remove(struct sway_keyboard *keyboard) { | |||
797 | } | 840 | } |
798 | 841 | ||
799 | static void sway_keyboard_group_remove_invalid(struct sway_keyboard *keyboard) { | 842 | static void sway_keyboard_group_remove_invalid(struct sway_keyboard *keyboard) { |
800 | struct sway_input_device *device = keyboard->seat_device->input_device; | 843 | if (!keyboard->wlr->group) { |
801 | struct wlr_keyboard *wlr_keyboard = device->wlr_device->keyboard; | ||
802 | if (!wlr_keyboard->group) { | ||
803 | return; | 844 | return; |
804 | } | 845 | } |
805 | 846 | ||
@@ -815,7 +856,7 @@ static void sway_keyboard_group_remove_invalid(struct sway_keyboard *keyboard) { | |||
815 | break; | 856 | break; |
816 | case KEYBOARD_GROUP_DEFAULT: /* fallthrough */ | 857 | case KEYBOARD_GROUP_DEFAULT: /* fallthrough */ |
817 | case KEYBOARD_GROUP_SMART:; | 858 | case KEYBOARD_GROUP_SMART:; |
818 | struct wlr_keyboard_group *group = wlr_keyboard->group; | 859 | struct wlr_keyboard_group *group = keyboard->wlr->group; |
819 | if (!wlr_keyboard_keymaps_match(keyboard->keymap, group->keyboard.keymap) || | 860 | if (!wlr_keyboard_keymaps_match(keyboard->keymap, group->keyboard.keymap) || |
820 | !repeat_info_match(keyboard, &group->keyboard)) { | 861 | !repeat_info_match(keyboard, &group->keyboard)) { |
821 | sway_keyboard_group_remove(keyboard); | 862 | sway_keyboard_group_remove(keyboard); |
@@ -826,7 +867,6 @@ static void sway_keyboard_group_remove_invalid(struct sway_keyboard *keyboard) { | |||
826 | 867 | ||
827 | static void sway_keyboard_group_add(struct sway_keyboard *keyboard) { | 868 | static void sway_keyboard_group_add(struct sway_keyboard *keyboard) { |
828 | struct sway_input_device *device = keyboard->seat_device->input_device; | 869 | struct sway_input_device *device = keyboard->seat_device->input_device; |
829 | struct wlr_keyboard *wlr_keyboard = device->wlr_device->keyboard; | ||
830 | struct sway_seat *seat = keyboard->seat_device->sway_seat; | 870 | struct sway_seat *seat = keyboard->seat_device->sway_seat; |
831 | struct seat_config *sc = seat_get_config(seat); | 871 | struct seat_config *sc = seat_get_config(seat); |
832 | 872 | ||
@@ -858,7 +898,7 @@ static void sway_keyboard_group_add(struct sway_keyboard *keyboard) { | |||
858 | repeat_info_match(keyboard, &wlr_group->keyboard)) { | 898 | repeat_info_match(keyboard, &wlr_group->keyboard)) { |
859 | sway_log(SWAY_DEBUG, "Adding keyboard %s to group %p", | 899 | sway_log(SWAY_DEBUG, "Adding keyboard %s to group %p", |
860 | device->identifier, wlr_group); | 900 | device->identifier, wlr_group); |
861 | wlr_keyboard_group_add_keyboard(wlr_group, wlr_keyboard); | 901 | wlr_keyboard_group_add_keyboard(wlr_group, keyboard->wlr); |
862 | return; | 902 | return; |
863 | } | 903 | } |
864 | break; | 904 | break; |
@@ -897,7 +937,7 @@ static void sway_keyboard_group_add(struct sway_keyboard *keyboard) { | |||
897 | goto cleanup; | 937 | goto cleanup; |
898 | } | 938 | } |
899 | sway_group->seat_device->input_device->wlr_device = | 939 | sway_group->seat_device->input_device->wlr_device = |
900 | sway_group->wlr_group->input_device; | 940 | &sway_group->wlr_group->keyboard.base; |
901 | 941 | ||
902 | if (!sway_keyboard_create(seat, sway_group->seat_device)) { | 942 | if (!sway_keyboard_create(seat, sway_group->seat_device)) { |
903 | sway_log(SWAY_ERROR, "Failed to allocate sway_keyboard for group"); | 943 | sway_log(SWAY_ERROR, "Failed to allocate sway_keyboard for group"); |
@@ -906,7 +946,7 @@ static void sway_keyboard_group_add(struct sway_keyboard *keyboard) { | |||
906 | 946 | ||
907 | sway_log(SWAY_DEBUG, "Adding keyboard %s to group %p", | 947 | sway_log(SWAY_DEBUG, "Adding keyboard %s to group %p", |
908 | device->identifier, sway_group->wlr_group); | 948 | device->identifier, sway_group->wlr_group); |
909 | wlr_keyboard_group_add_keyboard(sway_group->wlr_group, wlr_keyboard); | 949 | wlr_keyboard_group_add_keyboard(sway_group->wlr_group, keyboard->wlr); |
910 | 950 | ||
911 | wl_list_insert(&seat->keyboard_groups, &sway_group->link); | 951 | wl_list_insert(&seat->keyboard_groups, &sway_group->link); |
912 | 952 | ||
@@ -938,10 +978,8 @@ cleanup: | |||
938 | void sway_keyboard_configure(struct sway_keyboard *keyboard) { | 978 | void sway_keyboard_configure(struct sway_keyboard *keyboard) { |
939 | struct input_config *input_config = | 979 | struct input_config *input_config = |
940 | input_device_get_config(keyboard->seat_device->input_device); | 980 | input_device_get_config(keyboard->seat_device->input_device); |
941 | struct wlr_input_device *wlr_device = | ||
942 | keyboard->seat_device->input_device->wlr_device; | ||
943 | 981 | ||
944 | if (!sway_assert(!wlr_keyboard_group_from_wlr_keyboard(wlr_device->keyboard), | 982 | if (!sway_assert(!wlr_keyboard_group_from_wlr_keyboard(keyboard->wlr), |
945 | "sway_keyboard_configure should not be called with a " | 983 | "sway_keyboard_configure should not be called with a " |
946 | "keyboard group's keyboard")) { | 984 | "keyboard group's keyboard")) { |
947 | return; | 985 | return; |
@@ -983,11 +1021,11 @@ void sway_keyboard_configure(struct sway_keyboard *keyboard) { | |||
983 | 1021 | ||
984 | sway_keyboard_group_remove_invalid(keyboard); | 1022 | sway_keyboard_group_remove_invalid(keyboard); |
985 | 1023 | ||
986 | wlr_keyboard_set_keymap(wlr_device->keyboard, keyboard->keymap); | 1024 | wlr_keyboard_set_keymap(keyboard->wlr, keyboard->keymap); |
987 | wlr_keyboard_set_repeat_info(wlr_device->keyboard, | 1025 | wlr_keyboard_set_repeat_info(keyboard->wlr, |
988 | keyboard->repeat_rate, keyboard->repeat_delay); | 1026 | keyboard->repeat_rate, keyboard->repeat_delay); |
989 | 1027 | ||
990 | if (!wlr_device->keyboard->group) { | 1028 | if (!keyboard->wlr->group) { |
991 | sway_keyboard_group_add(keyboard); | 1029 | sway_keyboard_group_add(keyboard); |
992 | } | 1030 | } |
993 | 1031 | ||
@@ -1007,40 +1045,38 @@ void sway_keyboard_configure(struct sway_keyboard *keyboard) { | |||
1007 | } | 1045 | } |
1008 | } | 1046 | } |
1009 | if (locked_mods) { | 1047 | if (locked_mods) { |
1010 | wlr_keyboard_notify_modifiers(wlr_device->keyboard, 0, 0, | 1048 | wlr_keyboard_notify_modifiers(keyboard->wlr, 0, 0, |
1011 | locked_mods, 0); | 1049 | locked_mods, 0); |
1012 | uint32_t leds = 0; | 1050 | uint32_t leds = 0; |
1013 | for (uint32_t i = 0; i < WLR_LED_COUNT; ++i) { | 1051 | for (uint32_t i = 0; i < WLR_LED_COUNT; ++i) { |
1014 | if (xkb_state_led_index_is_active( | 1052 | if (xkb_state_led_index_is_active(keyboard->wlr->xkb_state, |
1015 | wlr_device->keyboard->xkb_state, | 1053 | keyboard->wlr->led_indexes[i])) { |
1016 | wlr_device->keyboard->led_indexes[i])) { | ||
1017 | leds |= (1 << i); | 1054 | leds |= (1 << i); |
1018 | } | 1055 | } |
1019 | } | 1056 | } |
1020 | if (wlr_device->keyboard->group) { | 1057 | if (keyboard->wlr->group) { |
1021 | wlr_keyboard_led_update( | 1058 | wlr_keyboard_led_update(&keyboard->wlr->group->keyboard, leds); |
1022 | &wlr_device->keyboard->group->keyboard, leds); | ||
1023 | } else { | 1059 | } else { |
1024 | wlr_keyboard_led_update(wlr_device->keyboard, leds); | 1060 | wlr_keyboard_led_update(keyboard->wlr, leds); |
1025 | } | 1061 | } |
1026 | } | 1062 | } |
1027 | } else { | 1063 | } else { |
1028 | xkb_keymap_unref(keymap); | 1064 | xkb_keymap_unref(keymap); |
1029 | sway_keyboard_group_remove_invalid(keyboard); | 1065 | sway_keyboard_group_remove_invalid(keyboard); |
1030 | if (!wlr_device->keyboard->group) { | 1066 | if (!keyboard->wlr->group) { |
1031 | sway_keyboard_group_add(keyboard); | 1067 | sway_keyboard_group_add(keyboard); |
1032 | } | 1068 | } |
1033 | } | 1069 | } |
1034 | 1070 | ||
1035 | struct wlr_seat *seat = keyboard->seat_device->sway_seat->wlr_seat; | 1071 | struct wlr_seat *seat = keyboard->seat_device->sway_seat->wlr_seat; |
1036 | wlr_seat_set_keyboard(seat, wlr_device); | 1072 | wlr_seat_set_keyboard(seat, keyboard->wlr); |
1037 | 1073 | ||
1038 | wl_list_remove(&keyboard->keyboard_key.link); | 1074 | wl_list_remove(&keyboard->keyboard_key.link); |
1039 | wl_signal_add(&wlr_device->keyboard->events.key, &keyboard->keyboard_key); | 1075 | wl_signal_add(&keyboard->wlr->events.key, &keyboard->keyboard_key); |
1040 | keyboard->keyboard_key.notify = handle_keyboard_key; | 1076 | keyboard->keyboard_key.notify = handle_keyboard_key; |
1041 | 1077 | ||
1042 | wl_list_remove(&keyboard->keyboard_modifiers.link); | 1078 | wl_list_remove(&keyboard->keyboard_modifiers.link); |
1043 | wl_signal_add(&wlr_device->keyboard->events.modifiers, | 1079 | wl_signal_add(&keyboard->wlr->events.modifiers, |
1044 | &keyboard->keyboard_modifiers); | 1080 | &keyboard->keyboard_modifiers); |
1045 | keyboard->keyboard_modifiers.notify = handle_keyboard_modifiers; | 1081 | keyboard->keyboard_modifiers.notify = handle_keyboard_modifiers; |
1046 | 1082 | ||
@@ -1057,12 +1093,11 @@ void sway_keyboard_destroy(struct sway_keyboard *keyboard) { | |||
1057 | if (!keyboard) { | 1093 | if (!keyboard) { |
1058 | return; | 1094 | return; |
1059 | } | 1095 | } |
1060 | if (keyboard->seat_device->input_device->wlr_device->keyboard->group) { | 1096 | if (keyboard->wlr->group) { |
1061 | sway_keyboard_group_remove(keyboard); | 1097 | sway_keyboard_group_remove(keyboard); |
1062 | } | 1098 | } |
1063 | struct wlr_seat *wlr_seat = keyboard->seat_device->sway_seat->wlr_seat; | 1099 | struct wlr_seat *wlr_seat = keyboard->seat_device->sway_seat->wlr_seat; |
1064 | struct sway_input_device *device = keyboard->seat_device->input_device; | 1100 | if (wlr_seat_get_keyboard(wlr_seat) == keyboard->wlr) { |
1065 | if (wlr_seat_get_keyboard(wlr_seat) == device->wlr_device->keyboard) { | ||
1066 | wlr_seat_set_keyboard(wlr_seat, NULL); | 1101 | wlr_seat_set_keyboard(wlr_seat, NULL); |
1067 | } | 1102 | } |
1068 | if (keyboard->keymap) { | 1103 | if (keyboard->keymap) { |
diff --git a/sway/input/libinput.c b/sway/input/libinput.c index 54520f9e..3c0f359d 100644 --- a/sway/input/libinput.c +++ b/sway/input/libinput.c | |||
@@ -1,5 +1,6 @@ | |||
1 | #include <float.h> | 1 | #include <float.h> |
2 | #include <libinput.h> | 2 | #include <libinput.h> |
3 | #include <libudev.h> | ||
3 | #include <limits.h> | 4 | #include <limits.h> |
4 | #include <wlr/backend/libinput.h> | 5 | #include <wlr/backend/libinput.h> |
5 | #include "log.h" | 6 | #include "log.h" |
@@ -312,3 +313,32 @@ void sway_input_reset_libinput_device(struct sway_input_device *input_device) { | |||
312 | ipc_event_input("libinput_config", input_device); | 313 | ipc_event_input("libinput_config", input_device); |
313 | } | 314 | } |
314 | } | 315 | } |
316 | |||
317 | bool sway_libinput_device_is_builtin(struct sway_input_device *sway_device) { | ||
318 | if (!wlr_input_device_is_libinput(sway_device->wlr_device)) { | ||
319 | return false; | ||
320 | } | ||
321 | |||
322 | struct libinput_device *device = | ||
323 | wlr_libinput_get_device_handle(sway_device->wlr_device); | ||
324 | struct udev_device *udev_device = | ||
325 | libinput_device_get_udev_device(device); | ||
326 | if (!udev_device) { | ||
327 | return false; | ||
328 | } | ||
329 | |||
330 | const char *id_path = udev_device_get_property_value(udev_device, "ID_PATH"); | ||
331 | if (!id_path) { | ||
332 | return false; | ||
333 | } | ||
334 | |||
335 | const char prefix_platform[] = "platform-"; | ||
336 | if (strncmp(id_path, prefix_platform, strlen(prefix_platform)) != 0) { | ||
337 | return false; | ||
338 | } | ||
339 | |||
340 | const char prefix_pci[] = "pci-"; | ||
341 | const char infix_platform[] = "-platform-"; | ||
342 | return (strncmp(id_path, prefix_pci, strlen(prefix_pci)) == 0) && | ||
343 | strstr(id_path, infix_platform); | ||
344 | } | ||
diff --git a/sway/input/seat.c b/sway/input/seat.c index 1f5865ee..b21e1b86 100644 --- a/sway/input/seat.c +++ b/sway/input/seat.c | |||
@@ -11,6 +11,7 @@ | |||
11 | #include <wlr/types/wlr_output_layout.h> | 11 | #include <wlr/types/wlr_output_layout.h> |
12 | #include <wlr/types/wlr_primary_selection.h> | 12 | #include <wlr/types/wlr_primary_selection.h> |
13 | #include <wlr/types/wlr_tablet_v2.h> | 13 | #include <wlr/types/wlr_tablet_v2.h> |
14 | #include <wlr/types/wlr_touch.h> | ||
14 | #include <wlr/types/wlr_xcursor_manager.h> | 15 | #include <wlr/types/wlr_xcursor_manager.h> |
15 | #include "config.h" | 16 | #include "config.h" |
16 | #include "list.h" | 17 | #include "list.h" |
@@ -20,6 +21,7 @@ | |||
20 | #include "sway/input/cursor.h" | 21 | #include "sway/input/cursor.h" |
21 | #include "sway/input/input-manager.h" | 22 | #include "sway/input/input-manager.h" |
22 | #include "sway/input/keyboard.h" | 23 | #include "sway/input/keyboard.h" |
24 | #include "sway/input/libinput.h" | ||
23 | #include "sway/input/seat.h" | 25 | #include "sway/input/seat.h" |
24 | #include "sway/input/switch.h" | 26 | #include "sway/input/switch.h" |
25 | #include "sway/input/tablet.h" | 27 | #include "sway/input/tablet.h" |
@@ -41,6 +43,7 @@ static void seat_device_destroy(struct sway_seat_device *seat_device) { | |||
41 | sway_keyboard_destroy(seat_device->keyboard); | 43 | sway_keyboard_destroy(seat_device->keyboard); |
42 | sway_tablet_destroy(seat_device->tablet); | 44 | sway_tablet_destroy(seat_device->tablet); |
43 | sway_tablet_pad_destroy(seat_device->tablet_pad); | 45 | sway_tablet_pad_destroy(seat_device->tablet_pad); |
46 | sway_switch_destroy(seat_device->switch_device); | ||
44 | wlr_cursor_detach_input_device(seat_device->sway_seat->cursor->cursor, | 47 | wlr_cursor_detach_input_device(seat_device->sway_seat->cursor->cursor, |
45 | seat_device->input_device->wlr_device); | 48 | seat_device->input_device->wlr_device); |
46 | wl_list_remove(&seat_device->link); | 49 | wl_list_remove(&seat_device->link); |
@@ -50,6 +53,16 @@ static void seat_device_destroy(struct sway_seat_device *seat_device) { | |||
50 | static void seat_node_destroy(struct sway_seat_node *seat_node) { | 53 | static void seat_node_destroy(struct sway_seat_node *seat_node) { |
51 | wl_list_remove(&seat_node->destroy.link); | 54 | wl_list_remove(&seat_node->destroy.link); |
52 | wl_list_remove(&seat_node->link); | 55 | wl_list_remove(&seat_node->link); |
56 | |||
57 | /* | ||
58 | * This is the only time we remove items from the focus stack without | ||
59 | * immediately re-adding them. If we just removed the last thing, | ||
60 | * mark that nothing has focus anymore. | ||
61 | */ | ||
62 | if (wl_list_empty(&seat_node->seat->focus_stack)) { | ||
63 | seat_node->seat->has_focus = false; | ||
64 | } | ||
65 | |||
53 | free(seat_node); | 66 | free(seat_node); |
54 | } | 67 | } |
55 | 68 | ||
@@ -129,7 +142,7 @@ static struct sway_keyboard *sway_keyboard_for_wlr_keyboard( | |||
129 | if (input_device->wlr_device->type != WLR_INPUT_DEVICE_KEYBOARD) { | 142 | if (input_device->wlr_device->type != WLR_INPUT_DEVICE_KEYBOARD) { |
130 | continue; | 143 | continue; |
131 | } | 144 | } |
132 | if (input_device->wlr_device->keyboard == wlr_keyboard) { | 145 | if (input_device->wlr_device == &wlr_keyboard->base) { |
133 | return seat_device->keyboard; | 146 | return seat_device->keyboard; |
134 | } | 147 | } |
135 | } | 148 | } |
@@ -137,7 +150,7 @@ static struct sway_keyboard *sway_keyboard_for_wlr_keyboard( | |||
137 | wl_list_for_each(group, &seat->keyboard_groups, link) { | 150 | wl_list_for_each(group, &seat->keyboard_groups, link) { |
138 | struct sway_input_device *input_device = | 151 | struct sway_input_device *input_device = |
139 | group->seat_device->input_device; | 152 | group->seat_device->input_device; |
140 | if (input_device->wlr_device->keyboard == wlr_keyboard) { | 153 | if (input_device->wlr_device == &wlr_keyboard->base) { |
141 | return group->seat_device->keyboard; | 154 | return group->seat_device->keyboard; |
142 | } | 155 | } |
143 | } | 156 | } |
@@ -199,6 +212,15 @@ static void seat_send_focus(struct sway_node *node, struct sway_seat *seat) { | |||
199 | } | 212 | } |
200 | } | 213 | } |
201 | 214 | ||
215 | void sway_force_focus(struct wlr_surface *surface) { | ||
216 | struct sway_seat *seat; | ||
217 | wl_list_for_each(seat, &server.input->seats, link) { | ||
218 | seat_keyboard_notify_enter(seat, surface); | ||
219 | seat_tablet_pads_notify_enter(seat, surface); | ||
220 | sway_input_method_relay_set_focus(&seat->im_relay, surface); | ||
221 | } | ||
222 | } | ||
223 | |||
202 | void seat_for_each_node(struct sway_seat *seat, | 224 | void seat_for_each_node(struct sway_seat *seat, |
203 | void (*f)(struct sway_node *node, void *data), void *data) { | 225 | void (*f)(struct sway_node *node, void *data), void *data) { |
204 | struct sway_seat_node *current = NULL; | 226 | struct sway_seat_node *current = NULL; |
@@ -209,14 +231,13 @@ void seat_for_each_node(struct sway_seat *seat, | |||
209 | 231 | ||
210 | struct sway_container *seat_get_focus_inactive_view(struct sway_seat *seat, | 232 | struct sway_container *seat_get_focus_inactive_view(struct sway_seat *seat, |
211 | struct sway_node *ancestor) { | 233 | struct sway_node *ancestor) { |
212 | if (ancestor->type == N_CONTAINER && ancestor->sway_container->view) { | 234 | if (node_is_view(ancestor)) { |
213 | return ancestor->sway_container; | 235 | return ancestor->sway_container; |
214 | } | 236 | } |
215 | struct sway_seat_node *current; | 237 | struct sway_seat_node *current; |
216 | wl_list_for_each(current, &seat->focus_stack, link) { | 238 | wl_list_for_each(current, &seat->focus_stack, link) { |
217 | struct sway_node *node = current->node; | 239 | struct sway_node *node = current->node; |
218 | if (node->type == N_CONTAINER && node->sway_container->view && | 240 | if (node_is_view(node) && node_has_ancestor(node, ancestor)) { |
219 | node_has_ancestor(node, ancestor)) { | ||
220 | return node->sway_container; | 241 | return node->sway_container; |
221 | } | 242 | } |
222 | } | 243 | } |
@@ -235,7 +256,7 @@ static void handle_seat_node_destroy(struct wl_listener *listener, void *data) { | |||
235 | seat_node_destroy(seat_node); | 256 | seat_node_destroy(seat_node); |
236 | // If an unmanaged or layer surface is focused when an output gets | 257 | // If an unmanaged or layer surface is focused when an output gets |
237 | // disabled and an empty workspace on the output was focused by the | 258 | // disabled and an empty workspace on the output was focused by the |
238 | // seat, the seat needs to refocus it's focus inactive to update the | 259 | // seat, the seat needs to refocus its focus inactive to update the |
239 | // value of seat->workspace. | 260 | // value of seat->workspace. |
240 | if (seat->workspace == node->sway_workspace) { | 261 | if (seat->workspace == node->sway_workspace) { |
241 | struct sway_node *node = seat_get_focus_inactive(seat, &root->node); | 262 | struct sway_node *node = seat_get_focus_inactive(seat, &root->node); |
@@ -309,8 +330,8 @@ static void handle_seat_node_destroy(struct wl_listener *listener, void *data) { | |||
309 | // Setting focus_inactive | 330 | // Setting focus_inactive |
310 | focus = seat_get_focus_inactive(seat, &root->node); | 331 | focus = seat_get_focus_inactive(seat, &root->node); |
311 | seat_set_raw_focus(seat, next_focus); | 332 | seat_set_raw_focus(seat, next_focus); |
312 | if (focus->type == N_CONTAINER && focus->sway_container->workspace) { | 333 | if (focus->type == N_CONTAINER && focus->sway_container->pending.workspace) { |
313 | seat_set_raw_focus(seat, &focus->sway_container->workspace->node); | 334 | seat_set_raw_focus(seat, &focus->sway_container->pending.workspace->node); |
314 | } | 335 | } |
315 | seat_set_raw_focus(seat, focus); | 336 | seat_set_raw_focus(seat, focus); |
316 | } | 337 | } |
@@ -368,8 +389,8 @@ void drag_icon_update_position(struct sway_drag_icon *icon) { | |||
368 | case WLR_DRAG_GRAB_KEYBOARD: | 389 | case WLR_DRAG_GRAB_KEYBOARD: |
369 | return; | 390 | return; |
370 | case WLR_DRAG_GRAB_KEYBOARD_POINTER: | 391 | case WLR_DRAG_GRAB_KEYBOARD_POINTER: |
371 | icon->x = cursor->x; | 392 | icon->x = cursor->x + wlr_icon->surface->sx; |
372 | icon->y = cursor->y; | 393 | icon->y = cursor->y + wlr_icon->surface->sy; |
373 | break; | 394 | break; |
374 | case WLR_DRAG_GRAB_KEYBOARD_TOUCH:; | 395 | case WLR_DRAG_GRAB_KEYBOARD_TOUCH:; |
375 | struct wlr_touch_point *point = | 396 | struct wlr_touch_point *point = |
@@ -377,8 +398,8 @@ void drag_icon_update_position(struct sway_drag_icon *icon) { | |||
377 | if (point == NULL) { | 398 | if (point == NULL) { |
378 | return; | 399 | return; |
379 | } | 400 | } |
380 | icon->x = seat->touch_x; | 401 | icon->x = seat->touch_x + wlr_icon->surface->sx; |
381 | icon->y = seat->touch_y; | 402 | icon->y = seat->touch_y + wlr_icon->surface->sy; |
382 | } | 403 | } |
383 | 404 | ||
384 | drag_icon_damage_whole(icon); | 405 | drag_icon_damage_whole(icon); |
@@ -666,6 +687,40 @@ static void seat_reset_input_config(struct sway_seat *seat, | |||
666 | sway_device->input_device->wlr_device, NULL); | 687 | sway_device->input_device->wlr_device, NULL); |
667 | } | 688 | } |
668 | 689 | ||
690 | static bool has_prefix(const char *str, const char *prefix) { | ||
691 | return strncmp(str, prefix, strlen(prefix)) == 0; | ||
692 | } | ||
693 | |||
694 | /** | ||
695 | * Get the name of the built-in output, if any. Returns NULL if there isn't | ||
696 | * exactly one built-in output. | ||
697 | */ | ||
698 | static const char *get_builtin_output_name(void) { | ||
699 | const char *match = NULL; | ||
700 | for (int i = 0; i < root->outputs->length; ++i) { | ||
701 | struct sway_output *output = root->outputs->items[i]; | ||
702 | const char *name = output->wlr_output->name; | ||
703 | if (has_prefix(name, "eDP-") || has_prefix(name, "LVDS-") || | ||
704 | has_prefix(name, "DSI-")) { | ||
705 | if (match != NULL) { | ||
706 | return NULL; | ||
707 | } | ||
708 | match = name; | ||
709 | } | ||
710 | } | ||
711 | return match; | ||
712 | } | ||
713 | |||
714 | static bool is_touch_or_tablet_tool(struct sway_seat_device *seat_device) { | ||
715 | switch (seat_device->input_device->wlr_device->type) { | ||
716 | case WLR_INPUT_DEVICE_TOUCH: | ||
717 | case WLR_INPUT_DEVICE_TABLET_TOOL: | ||
718 | return true; | ||
719 | default: | ||
720 | return false; | ||
721 | } | ||
722 | } | ||
723 | |||
669 | static void seat_apply_input_config(struct sway_seat *seat, | 724 | static void seat_apply_input_config(struct sway_seat *seat, |
670 | struct sway_seat_device *sway_device) { | 725 | struct sway_seat_device *sway_device) { |
671 | struct input_config *ic = | 726 | struct input_config *ic = |
@@ -680,8 +735,33 @@ static void seat_apply_input_config(struct sway_seat *seat, | |||
680 | ic == NULL ? MAPPED_TO_DEFAULT : ic->mapped_to; | 735 | ic == NULL ? MAPPED_TO_DEFAULT : ic->mapped_to; |
681 | 736 | ||
682 | switch (mapped_to) { | 737 | switch (mapped_to) { |
683 | case MAPPED_TO_DEFAULT: | 738 | case MAPPED_TO_DEFAULT:; |
684 | mapped_to_output = sway_device->input_device->wlr_device->output_name; | 739 | /* |
740 | * If the wlroots backend provides an output name, use that. | ||
741 | * | ||
742 | * Otherwise, try to map built-in touch and pointer devices to the | ||
743 | * built-in output. | ||
744 | */ | ||
745 | struct wlr_input_device *dev = sway_device->input_device->wlr_device; | ||
746 | switch (dev->type) { | ||
747 | case WLR_INPUT_DEVICE_POINTER: | ||
748 | mapped_to_output = wlr_pointer_from_input_device(dev)->output_name; | ||
749 | break; | ||
750 | case WLR_INPUT_DEVICE_TOUCH: | ||
751 | mapped_to_output = wlr_touch_from_input_device(dev)->output_name; | ||
752 | break; | ||
753 | default: | ||
754 | mapped_to_output = NULL; | ||
755 | break; | ||
756 | } | ||
757 | if (mapped_to_output == NULL && is_touch_or_tablet_tool(sway_device) && | ||
758 | sway_libinput_device_is_builtin(sway_device->input_device)) { | ||
759 | mapped_to_output = get_builtin_output_name(); | ||
760 | if (mapped_to_output) { | ||
761 | sway_log(SWAY_DEBUG, "Auto-detected output '%s' for device '%s'", | ||
762 | mapped_to_output, sway_device->input_device->identifier); | ||
763 | } | ||
764 | } | ||
685 | if (mapped_to_output == NULL) { | 765 | if (mapped_to_output == NULL) { |
686 | return; | 766 | return; |
687 | } | 767 | } |
@@ -742,12 +822,14 @@ static void seat_configure_keyboard(struct sway_seat *seat, | |||
742 | } | 822 | } |
743 | sway_keyboard_configure(seat_device->keyboard); | 823 | sway_keyboard_configure(seat_device->keyboard); |
744 | wlr_seat_set_keyboard(seat->wlr_seat, | 824 | wlr_seat_set_keyboard(seat->wlr_seat, |
745 | seat_device->input_device->wlr_device); | 825 | wlr_keyboard_from_input_device(seat_device->input_device->wlr_device)); |
746 | struct sway_node *focus = seat_get_focus(seat); | 826 | |
747 | if (focus && node_is_view(focus)) { | 827 | // force notify reenter to pick up the new configuration. This reuses |
748 | // force notify reenter to pick up the new configuration | 828 | // the current focused surface to avoid breaking input grabs. |
829 | struct wlr_surface *surface = seat->wlr_seat->keyboard_state.focused_surface; | ||
830 | if (surface) { | ||
749 | wlr_seat_keyboard_notify_clear_focus(seat->wlr_seat); | 831 | wlr_seat_keyboard_notify_clear_focus(seat->wlr_seat); |
750 | seat_keyboard_notify_enter(seat, focus->sway_container->view->surface); | 832 | seat_keyboard_notify_enter(seat, surface); |
751 | } | 833 | } |
752 | } | 834 | } |
753 | 835 | ||
@@ -999,7 +1081,8 @@ void seat_configure_xcursor(struct sway_seat *seat) { | |||
999 | bool seat_is_input_allowed(struct sway_seat *seat, | 1081 | bool seat_is_input_allowed(struct sway_seat *seat, |
1000 | struct wlr_surface *surface) { | 1082 | struct wlr_surface *surface) { |
1001 | struct wl_client *client = wl_resource_get_client(surface->resource); | 1083 | struct wl_client *client = wl_resource_get_client(surface->resource); |
1002 | return !seat->exclusive_client || seat->exclusive_client == client; | 1084 | return seat->exclusive_client == client || |
1085 | (seat->exclusive_client == NULL && !server.session_lock.locked); | ||
1003 | } | 1086 | } |
1004 | 1087 | ||
1005 | static void send_unfocus(struct sway_container *con, void *data) { | 1088 | static void send_unfocus(struct sway_container *con, void *data) { |
@@ -1086,29 +1169,23 @@ void seat_set_focus(struct sway_seat *seat, struct sway_node *node) { | |||
1086 | } | 1169 | } |
1087 | 1170 | ||
1088 | struct sway_workspace *new_workspace = node->type == N_WORKSPACE ? | 1171 | struct sway_workspace *new_workspace = node->type == N_WORKSPACE ? |
1089 | node->sway_workspace : node->sway_container->workspace; | 1172 | node->sway_workspace : node->sway_container->pending.workspace; |
1090 | struct sway_container *container = node->type == N_CONTAINER ? | 1173 | struct sway_container *container = node->type == N_CONTAINER ? |
1091 | node->sway_container : NULL; | 1174 | node->sway_container : NULL; |
1092 | 1175 | ||
1093 | // Deny setting focus to a view which is hidden by a fullscreen container | 1176 | // Deny setting focus to a view which is hidden by a fullscreen container or global |
1094 | if (new_workspace && new_workspace->fullscreen && container && | 1177 | if (container && container_obstructing_fullscreen_container(container)) { |
1095 | !container_is_fullscreen_or_child(container)) { | 1178 | return; |
1096 | // Unless it's a transient container | ||
1097 | if (!container_is_transient_for(container, new_workspace->fullscreen)) { | ||
1098 | return; | ||
1099 | } | ||
1100 | } | 1179 | } |
1180 | |||
1101 | // Deny setting focus to a workspace node when using fullscreen global | 1181 | // Deny setting focus to a workspace node when using fullscreen global |
1102 | if (root->fullscreen_global && !container && new_workspace) { | 1182 | if (root->fullscreen_global && !container && new_workspace) { |
1103 | return; | 1183 | return; |
1104 | } | 1184 | } |
1105 | // Deny setting focus to a view which is hidden by a fullscreen global | 1185 | |
1106 | if (root->fullscreen_global && container != root->fullscreen_global && | 1186 | // Deny setting focus when an input grab or lockscreen is active |
1107 | !container_has_ancestor(container, root->fullscreen_global)) { | 1187 | if (container && container->view && !seat_is_input_allowed(seat, container->view->surface)) { |
1108 | // Unless it's a transient container | 1188 | return; |
1109 | if (!container_is_transient_for(container, root->fullscreen_global)) { | ||
1110 | return; | ||
1111 | } | ||
1112 | } | 1189 | } |
1113 | 1190 | ||
1114 | struct sway_output *new_output = | 1191 | struct sway_output *new_output = |
@@ -1135,10 +1212,10 @@ void seat_set_focus(struct sway_seat *seat, struct sway_node *node) { | |||
1135 | // Put the container parents on the focus stack, then the workspace, then | 1212 | // Put the container parents on the focus stack, then the workspace, then |
1136 | // the focused container. | 1213 | // the focused container. |
1137 | if (container) { | 1214 | if (container) { |
1138 | struct sway_container *parent = container->parent; | 1215 | struct sway_container *parent = container->pending.parent; |
1139 | while (parent) { | 1216 | while (parent) { |
1140 | seat_set_raw_focus(seat, &parent->node); | 1217 | seat_set_raw_focus(seat, &parent->node); |
1141 | parent = parent->parent; | 1218 | parent = parent->pending.parent; |
1142 | } | 1219 | } |
1143 | } | 1220 | } |
1144 | if (new_workspace) { | 1221 | if (new_workspace) { |
@@ -1234,6 +1311,7 @@ void seat_set_focus_surface(struct sway_seat *seat, | |||
1234 | wlr_seat_keyboard_notify_clear_focus(seat->wlr_seat); | 1311 | wlr_seat_keyboard_notify_clear_focus(seat->wlr_seat); |
1235 | } | 1312 | } |
1236 | 1313 | ||
1314 | sway_input_method_relay_set_focus(&seat->im_relay, surface); | ||
1237 | seat_tablet_pads_notify_enter(seat, surface); | 1315 | seat_tablet_pads_notify_enter(seat, surface); |
1238 | } | 1316 | } |
1239 | 1317 | ||
@@ -1326,7 +1404,7 @@ struct sway_container *seat_get_focus_inactive_tiling(struct sway_seat *seat, | |||
1326 | struct sway_node *node = current->node; | 1404 | struct sway_node *node = current->node; |
1327 | if (node->type == N_CONTAINER && | 1405 | if (node->type == N_CONTAINER && |
1328 | !container_is_floating_or_child(node->sway_container) && | 1406 | !container_is_floating_or_child(node->sway_container) && |
1329 | node->sway_container->workspace == workspace) { | 1407 | node->sway_container->pending.workspace == workspace) { |
1330 | return node->sway_container; | 1408 | return node->sway_container; |
1331 | } | 1409 | } |
1332 | } | 1410 | } |
@@ -1343,7 +1421,7 @@ struct sway_container *seat_get_focus_inactive_floating(struct sway_seat *seat, | |||
1343 | struct sway_node *node = current->node; | 1421 | struct sway_node *node = current->node; |
1344 | if (node->type == N_CONTAINER && | 1422 | if (node->type == N_CONTAINER && |
1345 | container_is_floating_or_child(node->sway_container) && | 1423 | container_is_floating_or_child(node->sway_container) && |
1346 | node->sway_container->workspace == workspace) { | 1424 | node->sway_container->pending.workspace == workspace) { |
1347 | return node->sway_container; | 1425 | return node->sway_container; |
1348 | } | 1426 | } |
1349 | } | 1427 | } |
@@ -1377,9 +1455,8 @@ struct sway_node *seat_get_focus(struct sway_seat *seat) { | |||
1377 | if (!seat->has_focus) { | 1455 | if (!seat->has_focus) { |
1378 | return NULL; | 1456 | return NULL; |
1379 | } | 1457 | } |
1380 | if (wl_list_empty(&seat->focus_stack)) { | 1458 | sway_assert(!wl_list_empty(&seat->focus_stack), |
1381 | return NULL; | 1459 | "focus_stack is empty, but has_focus is true"); |
1382 | } | ||
1383 | struct sway_seat_node *current = | 1460 | struct sway_seat_node *current = |
1384 | wl_container_of(seat->focus_stack.next, current, link); | 1461 | wl_container_of(seat->focus_stack.next, current, link); |
1385 | return current->node; | 1462 | return current->node; |
@@ -1391,7 +1468,7 @@ struct sway_workspace *seat_get_focused_workspace(struct sway_seat *seat) { | |||
1391 | return NULL; | 1468 | return NULL; |
1392 | } | 1469 | } |
1393 | if (focus->type == N_CONTAINER) { | 1470 | if (focus->type == N_CONTAINER) { |
1394 | return focus->sway_container->workspace; | 1471 | return focus->sway_container->pending.workspace; |
1395 | } | 1472 | } |
1396 | if (focus->type == N_WORKSPACE) { | 1473 | if (focus->type == N_WORKSPACE) { |
1397 | return focus->sway_workspace; | 1474 | return focus->sway_workspace; |
@@ -1404,8 +1481,8 @@ struct sway_workspace *seat_get_last_known_workspace(struct sway_seat *seat) { | |||
1404 | wl_list_for_each(current, &seat->focus_stack, link) { | 1481 | wl_list_for_each(current, &seat->focus_stack, link) { |
1405 | struct sway_node *node = current->node; | 1482 | struct sway_node *node = current->node; |
1406 | if (node->type == N_CONTAINER && | 1483 | if (node->type == N_CONTAINER && |
1407 | node->sway_container->workspace) { | 1484 | node->sway_container->pending.workspace) { |
1408 | return node->sway_container->workspace; | 1485 | return node->sway_container->pending.workspace; |
1409 | } else if (node->type == N_WORKSPACE) { | 1486 | } else if (node->type == N_WORKSPACE) { |
1410 | return node->sway_workspace; | 1487 | return node->sway_workspace; |
1411 | } | 1488 | } |
@@ -1514,7 +1591,7 @@ void seatop_pointer_motion(struct sway_seat *seat, uint32_t time_msec) { | |||
1514 | } | 1591 | } |
1515 | 1592 | ||
1516 | void seatop_pointer_axis(struct sway_seat *seat, | 1593 | void seatop_pointer_axis(struct sway_seat *seat, |
1517 | struct wlr_event_pointer_axis *event) { | 1594 | struct wlr_pointer_axis_event *event) { |
1518 | if (seat->seatop_impl->pointer_axis) { | 1595 | if (seat->seatop_impl->pointer_axis) { |
1519 | seat->seatop_impl->pointer_axis(seat, event); | 1596 | seat->seatop_impl->pointer_axis(seat, event); |
1520 | } | 1597 | } |
@@ -1537,6 +1614,62 @@ void seatop_tablet_tool_motion(struct sway_seat *seat, | |||
1537 | } | 1614 | } |
1538 | } | 1615 | } |
1539 | 1616 | ||
1617 | void seatop_hold_begin(struct sway_seat *seat, | ||
1618 | struct wlr_pointer_hold_begin_event *event) { | ||
1619 | if (seat->seatop_impl->hold_begin) { | ||
1620 | seat->seatop_impl->hold_begin(seat, event); | ||
1621 | } | ||
1622 | } | ||
1623 | |||
1624 | void seatop_hold_end(struct sway_seat *seat, | ||
1625 | struct wlr_pointer_hold_end_event *event) { | ||
1626 | if (seat->seatop_impl->hold_end) { | ||
1627 | seat->seatop_impl->hold_end(seat, event); | ||
1628 | } | ||
1629 | } | ||
1630 | |||
1631 | void seatop_pinch_begin(struct sway_seat *seat, | ||
1632 | struct wlr_pointer_pinch_begin_event *event) { | ||
1633 | if (seat->seatop_impl->pinch_begin) { | ||
1634 | seat->seatop_impl->pinch_begin(seat, event); | ||
1635 | } | ||
1636 | } | ||
1637 | |||
1638 | void seatop_pinch_update(struct sway_seat *seat, | ||
1639 | struct wlr_pointer_pinch_update_event *event) { | ||
1640 | if (seat->seatop_impl->pinch_update) { | ||
1641 | seat->seatop_impl->pinch_update(seat, event); | ||
1642 | } | ||
1643 | } | ||
1644 | |||
1645 | void seatop_pinch_end(struct sway_seat *seat, | ||
1646 | struct wlr_pointer_pinch_end_event *event) { | ||
1647 | if (seat->seatop_impl->pinch_end) { | ||
1648 | seat->seatop_impl->pinch_end(seat, event); | ||
1649 | } | ||
1650 | } | ||
1651 | |||
1652 | void seatop_swipe_begin(struct sway_seat *seat, | ||
1653 | struct wlr_pointer_swipe_begin_event *event) { | ||
1654 | if (seat->seatop_impl->swipe_begin) { | ||
1655 | seat->seatop_impl->swipe_begin(seat, event); | ||
1656 | } | ||
1657 | } | ||
1658 | |||
1659 | void seatop_swipe_update(struct sway_seat *seat, | ||
1660 | struct wlr_pointer_swipe_update_event *event) { | ||
1661 | if (seat->seatop_impl->swipe_update) { | ||
1662 | seat->seatop_impl->swipe_update(seat, event); | ||
1663 | } | ||
1664 | } | ||
1665 | |||
1666 | void seatop_swipe_end(struct sway_seat *seat, | ||
1667 | struct wlr_pointer_swipe_end_event *event) { | ||
1668 | if (seat->seatop_impl->swipe_end) { | ||
1669 | seat->seatop_impl->swipe_end(seat, event); | ||
1670 | } | ||
1671 | } | ||
1672 | |||
1540 | void seatop_rebase(struct sway_seat *seat, uint32_t time_msec) { | 1673 | void seatop_rebase(struct sway_seat *seat, uint32_t time_msec) { |
1541 | if (seat->seatop_impl->rebase) { | 1674 | if (seat->seatop_impl->rebase) { |
1542 | seat->seatop_impl->rebase(seat, time_msec); | 1675 | seat->seatop_impl->rebase(seat, time_msec); |
diff --git a/sway/input/seatop_default.c b/sway/input/seatop_default.c index a583ed62..875426bf 100644 --- a/sway/input/seatop_default.c +++ b/sway/input/seatop_default.c | |||
@@ -4,6 +4,8 @@ | |||
4 | #include <wlr/types/wlr_cursor.h> | 4 | #include <wlr/types/wlr_cursor.h> |
5 | #include <wlr/types/wlr_tablet_v2.h> | 5 | #include <wlr/types/wlr_tablet_v2.h> |
6 | #include <wlr/types/wlr_xcursor_manager.h> | 6 | #include <wlr/types/wlr_xcursor_manager.h> |
7 | #include "gesture.h" | ||
8 | #include "sway/desktop/transaction.h" | ||
7 | #include "sway/input/cursor.h" | 9 | #include "sway/input/cursor.h" |
8 | #include "sway/input/seat.h" | 10 | #include "sway/input/seat.h" |
9 | #include "sway/input/tablet.h" | 11 | #include "sway/input/tablet.h" |
@@ -19,6 +21,7 @@ struct seatop_default_event { | |||
19 | struct sway_node *previous_node; | 21 | struct sway_node *previous_node; |
20 | uint32_t pressed_buttons[SWAY_CURSOR_PRESSED_BUTTONS_CAP]; | 22 | uint32_t pressed_buttons[SWAY_CURSOR_PRESSED_BUTTONS_CAP]; |
21 | size_t pressed_button_count; | 23 | size_t pressed_button_count; |
24 | struct gesture_tracker gestures; | ||
22 | }; | 25 | }; |
23 | 26 | ||
24 | /*-----------------------------------------\ | 27 | /*-----------------------------------------\ |
@@ -59,7 +62,7 @@ static bool edge_is_external(struct sway_container *cont, enum wlr_edges edge) { | |||
59 | return false; | 62 | return false; |
60 | } | 63 | } |
61 | } | 64 | } |
62 | cont = cont->parent; | 65 | cont = cont->pending.parent; |
63 | } | 66 | } |
64 | return true; | 67 | return true; |
65 | } | 68 | } |
@@ -69,25 +72,25 @@ static enum wlr_edges find_edge(struct sway_container *cont, | |||
69 | if (!cont->view || (surface && cont->view->surface != surface)) { | 72 | if (!cont->view || (surface && cont->view->surface != surface)) { |
70 | return WLR_EDGE_NONE; | 73 | return WLR_EDGE_NONE; |
71 | } | 74 | } |
72 | if (cont->border == B_NONE || !cont->border_thickness || | 75 | if (cont->pending.border == B_NONE || !cont->pending.border_thickness || |
73 | cont->border == B_CSD) { | 76 | cont->pending.border == B_CSD) { |
74 | return WLR_EDGE_NONE; | 77 | return WLR_EDGE_NONE; |
75 | } | 78 | } |
76 | if (cont->fullscreen_mode) { | 79 | if (cont->pending.fullscreen_mode) { |
77 | return WLR_EDGE_NONE; | 80 | return WLR_EDGE_NONE; |
78 | } | 81 | } |
79 | 82 | ||
80 | enum wlr_edges edge = 0; | 83 | enum wlr_edges edge = 0; |
81 | if (cursor->cursor->x < cont->x + cont->border_thickness) { | 84 | if (cursor->cursor->x < cont->pending.x + cont->pending.border_thickness) { |
82 | edge |= WLR_EDGE_LEFT; | 85 | edge |= WLR_EDGE_LEFT; |
83 | } | 86 | } |
84 | if (cursor->cursor->y < cont->y + cont->border_thickness) { | 87 | if (cursor->cursor->y < cont->pending.y + cont->pending.border_thickness) { |
85 | edge |= WLR_EDGE_TOP; | 88 | edge |= WLR_EDGE_TOP; |
86 | } | 89 | } |
87 | if (cursor->cursor->x >= cont->x + cont->width - cont->border_thickness) { | 90 | if (cursor->cursor->x >= cont->pending.x + cont->pending.width - cont->pending.border_thickness) { |
88 | edge |= WLR_EDGE_RIGHT; | 91 | edge |= WLR_EDGE_RIGHT; |
89 | } | 92 | } |
90 | if (cursor->cursor->y >= cont->y + cont->height - cont->border_thickness) { | 93 | if (cursor->cursor->y >= cont->pending.y + cont->pending.height - cont->pending.border_thickness) { |
91 | edge |= WLR_EDGE_BOTTOM; | 94 | edge |= WLR_EDGE_BOTTOM; |
92 | } | 95 | } |
93 | 96 | ||
@@ -231,6 +234,7 @@ static void handle_tablet_tool_tip(struct sway_seat *seat, | |||
231 | wlr_layer_surface_v1_from_wlr_surface(surface); | 234 | wlr_layer_surface_v1_from_wlr_surface(surface); |
232 | if (layer->current.keyboard_interactive) { | 235 | if (layer->current.keyboard_interactive) { |
233 | seat_set_focus_layer(seat, layer); | 236 | seat_set_focus_layer(seat, layer); |
237 | transaction_commit_dirty(); | ||
234 | } | 238 | } |
235 | } else if (cont) { | 239 | } else if (cont) { |
236 | bool is_floating_or_child = container_is_floating_or_child(cont); | 240 | bool is_floating_or_child = container_is_floating_or_child(cont); |
@@ -249,7 +253,7 @@ static void handle_tablet_tool_tip(struct sway_seat *seat, | |||
249 | 253 | ||
250 | // Handle moving a tiling container | 254 | // Handle moving a tiling container |
251 | if (config->tiling_drag && mod_pressed && !is_floating_or_child && | 255 | if (config->tiling_drag && mod_pressed && !is_floating_or_child && |
252 | cont->fullscreen_mode == FULLSCREEN_NONE) { | 256 | cont->pending.fullscreen_mode == FULLSCREEN_NONE) { |
253 | seatop_begin_move_tiling(seat, cont); | 257 | seatop_begin_move_tiling(seat, cont); |
254 | return; | 258 | return; |
255 | } | 259 | } |
@@ -268,6 +272,7 @@ static void handle_tablet_tool_tip(struct sway_seat *seat, | |||
268 | struct wlr_xwayland *xwayland = server.xwayland.wlr_xwayland; | 272 | struct wlr_xwayland *xwayland = server.xwayland.wlr_xwayland; |
269 | wlr_xwayland_set_seat(xwayland, seat->wlr_seat); | 273 | wlr_xwayland_set_seat(xwayland, seat->wlr_seat); |
270 | seat_set_focus_surface(seat, xsurface->surface, false); | 274 | seat_set_focus_surface(seat, xsurface->surface, false); |
275 | transaction_commit_dirty(); | ||
271 | } | 276 | } |
272 | } | 277 | } |
273 | #endif | 278 | #endif |
@@ -356,6 +361,7 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec, | |||
356 | if (node && node->type == N_WORKSPACE) { | 361 | if (node && node->type == N_WORKSPACE) { |
357 | if (state == WLR_BUTTON_PRESSED) { | 362 | if (state == WLR_BUTTON_PRESSED) { |
358 | seat_set_focus(seat, node); | 363 | seat_set_focus(seat, node); |
364 | transaction_commit_dirty(); | ||
359 | } | 365 | } |
360 | seat_pointer_notify_button(seat, time_msec, button, state); | 366 | seat_pointer_notify_button(seat, time_msec, button, state); |
361 | return; | 367 | return; |
@@ -367,6 +373,10 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec, | |||
367 | wlr_layer_surface_v1_from_wlr_surface(surface); | 373 | wlr_layer_surface_v1_from_wlr_surface(surface); |
368 | if (layer->current.keyboard_interactive) { | 374 | if (layer->current.keyboard_interactive) { |
369 | seat_set_focus_layer(seat, layer); | 375 | seat_set_focus_layer(seat, layer); |
376 | transaction_commit_dirty(); | ||
377 | } | ||
378 | if (state == WLR_BUTTON_PRESSED) { | ||
379 | seatop_begin_down_on_surface(seat, surface, time_msec, sx, sy); | ||
370 | } | 380 | } |
371 | seat_pointer_notify_button(seat, time_msec, button, state); | 381 | seat_pointer_notify_button(seat, time_msec, button, state); |
372 | return; | 382 | return; |
@@ -381,7 +391,7 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec, | |||
381 | struct sway_container *cont_to_focus = cont; | 391 | struct sway_container *cont_to_focus = cont; |
382 | enum sway_container_layout layout = container_parent_layout(cont); | 392 | enum sway_container_layout layout = container_parent_layout(cont); |
383 | if (layout == L_TABBED || layout == L_STACKED) { | 393 | if (layout == L_TABBED || layout == L_STACKED) { |
384 | cont_to_focus = seat_get_focus_inactive_view(seat, &cont->parent->node); | 394 | cont_to_focus = seat_get_focus_inactive_view(seat, &cont->pending.parent->node); |
385 | } | 395 | } |
386 | 396 | ||
387 | seat_set_focus_container(seat, cont_to_focus); | 397 | seat_set_focus_container(seat, cont_to_focus); |
@@ -397,9 +407,9 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec, | |||
397 | BTN_LEFT : BTN_RIGHT; | 407 | BTN_LEFT : BTN_RIGHT; |
398 | if (button == btn_resize) { | 408 | if (button == btn_resize) { |
399 | edge = 0; | 409 | edge = 0; |
400 | edge |= cursor->cursor->x > cont->x + cont->width / 2 ? | 410 | edge |= cursor->cursor->x > cont->pending.x + cont->pending.width / 2 ? |
401 | WLR_EDGE_RIGHT : WLR_EDGE_LEFT; | 411 | WLR_EDGE_RIGHT : WLR_EDGE_LEFT; |
402 | edge |= cursor->cursor->y > cont->y + cont->height / 2 ? | 412 | edge |= cursor->cursor->y > cont->pending.y + cont->pending.height / 2 ? |
403 | WLR_EDGE_BOTTOM : WLR_EDGE_TOP; | 413 | WLR_EDGE_BOTTOM : WLR_EDGE_TOP; |
404 | 414 | ||
405 | const char *image = NULL; | 415 | const char *image = NULL; |
@@ -446,9 +456,9 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec, | |||
446 | if (mod_pressed && button == btn_resize) { | 456 | if (mod_pressed && button == btn_resize) { |
447 | struct sway_container *floater = container_toplevel_ancestor(cont); | 457 | struct sway_container *floater = container_toplevel_ancestor(cont); |
448 | edge = 0; | 458 | edge = 0; |
449 | edge |= cursor->cursor->x > floater->x + floater->width / 2 ? | 459 | edge |= cursor->cursor->x > floater->pending.x + floater->pending.width / 2 ? |
450 | WLR_EDGE_RIGHT : WLR_EDGE_LEFT; | 460 | WLR_EDGE_RIGHT : WLR_EDGE_LEFT; |
451 | edge |= cursor->cursor->y > floater->y + floater->height / 2 ? | 461 | edge |= cursor->cursor->y > floater->pending.y + floater->pending.height / 2 ? |
452 | WLR_EDGE_BOTTOM : WLR_EDGE_TOP; | 462 | WLR_EDGE_BOTTOM : WLR_EDGE_TOP; |
453 | seatop_begin_resize_floating(seat, floater, edge); | 463 | seatop_begin_resize_floating(seat, floater, edge); |
454 | return; | 464 | return; |
@@ -458,7 +468,7 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec, | |||
458 | // Handle moving a tiling container | 468 | // Handle moving a tiling container |
459 | if (config->tiling_drag && (mod_pressed || on_titlebar) && | 469 | if (config->tiling_drag && (mod_pressed || on_titlebar) && |
460 | state == WLR_BUTTON_PRESSED && !is_floating_or_child && | 470 | state == WLR_BUTTON_PRESSED && !is_floating_or_child && |
461 | cont && cont->fullscreen_mode == FULLSCREEN_NONE) { | 471 | cont && cont->pending.fullscreen_mode == FULLSCREEN_NONE) { |
462 | struct sway_container *focus = seat_get_focused_container(seat); | 472 | struct sway_container *focus = seat_get_focused_container(seat); |
463 | bool focused = focus == cont || container_has_ancestor(focus, cont); | 473 | bool focused = focus == cont || container_has_ancestor(focus, cont); |
464 | if (on_titlebar && !focused) { | 474 | if (on_titlebar && !focused) { |
@@ -466,7 +476,7 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec, | |||
466 | seat_set_focus(seat, node); | 476 | seat_set_focus(seat, node); |
467 | } | 477 | } |
468 | 478 | ||
469 | // If moving a container by it's title bar, use a threshold for the drag | 479 | // If moving a container by its title bar, use a threshold for the drag |
470 | if (!mod_pressed && config->tiling_drag_threshold > 0) { | 480 | if (!mod_pressed && config->tiling_drag_threshold > 0) { |
471 | seatop_begin_move_tiling_threshold(seat, cont); | 481 | seatop_begin_move_tiling_threshold(seat, cont); |
472 | } else { | 482 | } else { |
@@ -487,6 +497,7 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec, | |||
487 | if (cont && state == WLR_BUTTON_PRESSED) { | 497 | if (cont && state == WLR_BUTTON_PRESSED) { |
488 | node = seat_get_focus_inactive(seat, &cont->node); | 498 | node = seat_get_focus_inactive(seat, &cont->node); |
489 | seat_set_focus(seat, node); | 499 | seat_set_focus(seat, node); |
500 | transaction_commit_dirty(); | ||
490 | seat_pointer_notify_button(seat, time_msec, button, state); | 501 | seat_pointer_notify_button(seat, time_msec, button, state); |
491 | return; | 502 | return; |
492 | } | 503 | } |
@@ -501,6 +512,7 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec, | |||
501 | struct wlr_xwayland *xwayland = server.xwayland.wlr_xwayland; | 512 | struct wlr_xwayland *xwayland = server.xwayland.wlr_xwayland; |
502 | wlr_xwayland_set_seat(xwayland, seat->wlr_seat); | 513 | wlr_xwayland_set_seat(xwayland, seat->wlr_seat); |
503 | seat_set_focus_surface(seat, xsurface->surface, false); | 514 | seat_set_focus_surface(seat, xsurface->surface, false); |
515 | transaction_commit_dirty(); | ||
504 | seat_pointer_notify_button(seat, time_msec, button, state); | 516 | seat_pointer_notify_button(seat, time_msec, button, state); |
505 | return; | 517 | return; |
506 | } | 518 | } |
@@ -530,6 +542,7 @@ static void check_focus_follows_mouse(struct sway_seat *seat, | |||
530 | if (focus && hovered_output != node_get_output(focus)) { | 542 | if (focus && hovered_output != node_get_output(focus)) { |
531 | struct sway_workspace *ws = output_get_active_workspace(hovered_output); | 543 | struct sway_workspace *ws = output_get_active_workspace(hovered_output); |
532 | seat_set_focus(seat, &ws->node); | 544 | seat_set_focus(seat, &ws->node); |
545 | transaction_commit_dirty(); | ||
533 | } | 546 | } |
534 | return; | 547 | return; |
535 | } | 548 | } |
@@ -541,6 +554,7 @@ static void check_focus_follows_mouse(struct sway_seat *seat, | |||
541 | struct sway_output *hovered_output = node_get_output(hovered_node); | 554 | struct sway_output *hovered_output = node_get_output(hovered_node); |
542 | if (hovered_output != focused_output) { | 555 | if (hovered_output != focused_output) { |
543 | seat_set_focus(seat, seat_get_focus_inactive(seat, hovered_node)); | 556 | seat_set_focus(seat, seat_get_focus_inactive(seat, hovered_node)); |
557 | transaction_commit_dirty(); | ||
544 | } | 558 | } |
545 | return; | 559 | return; |
546 | } | 560 | } |
@@ -556,6 +570,7 @@ static void check_focus_follows_mouse(struct sway_seat *seat, | |||
556 | if (hovered_node != e->previous_node || | 570 | if (hovered_node != e->previous_node || |
557 | config->focus_follows_mouse == FOLLOWS_ALWAYS) { | 571 | config->focus_follows_mouse == FOLLOWS_ALWAYS) { |
558 | seat_set_focus(seat, hovered_node); | 572 | seat_set_focus(seat, hovered_node); |
573 | transaction_commit_dirty(); | ||
559 | } | 574 | } |
560 | } | 575 | } |
561 | } | 576 | } |
@@ -632,7 +647,7 @@ static void handle_tablet_tool_motion(struct sway_seat *seat, | |||
632 | * Functions used by handle_pointer_axis / | 647 | * Functions used by handle_pointer_axis / |
633 | *--------------------------------------*/ | 648 | *--------------------------------------*/ |
634 | 649 | ||
635 | static uint32_t wl_axis_to_button(struct wlr_event_pointer_axis *event) { | 650 | static uint32_t wl_axis_to_button(struct wlr_pointer_axis_event *event) { |
636 | switch (event->orientation) { | 651 | switch (event->orientation) { |
637 | case WLR_AXIS_ORIENTATION_VERTICAL: | 652 | case WLR_AXIS_ORIENTATION_VERTICAL: |
638 | return event->delta < 0 ? SWAY_SCROLL_UP : SWAY_SCROLL_DOWN; | 653 | return event->delta < 0 ? SWAY_SCROLL_UP : SWAY_SCROLL_DOWN; |
@@ -645,9 +660,9 @@ static uint32_t wl_axis_to_button(struct wlr_event_pointer_axis *event) { | |||
645 | } | 660 | } |
646 | 661 | ||
647 | static void handle_pointer_axis(struct sway_seat *seat, | 662 | static void handle_pointer_axis(struct sway_seat *seat, |
648 | struct wlr_event_pointer_axis *event) { | 663 | struct wlr_pointer_axis_event *event) { |
649 | struct sway_input_device *input_device = | 664 | struct sway_input_device *input_device = |
650 | event->device ? event->device->data : NULL; | 665 | event->pointer ? event->pointer->base.data : NULL; |
651 | struct input_config *ic = | 666 | struct input_config *ic = |
652 | input_device ? input_device_get_config(input_device) : NULL; | 667 | input_device ? input_device_get_config(input_device) : NULL; |
653 | struct sway_cursor *cursor = seat->cursor; | 668 | struct sway_cursor *cursor = seat->cursor; |
@@ -664,7 +679,7 @@ static void handle_pointer_axis(struct sway_seat *seat, | |||
664 | bool on_border = edge != WLR_EDGE_NONE; | 679 | bool on_border = edge != WLR_EDGE_NONE; |
665 | bool on_titlebar = cont && !on_border && !surface; | 680 | bool on_titlebar = cont && !on_border && !surface; |
666 | bool on_titlebar_border = cont && on_border && | 681 | bool on_titlebar_border = cont && on_border && |
667 | cursor->cursor->y < cont->content_y; | 682 | cursor->cursor->y < cont->pending.content_y; |
668 | bool on_contents = cont && !on_border && surface; | 683 | bool on_contents = cont && !on_border && surface; |
669 | bool on_workspace = node && node->type == N_WORKSPACE; | 684 | bool on_workspace = node && node->type == N_WORKSPACE; |
670 | float scroll_factor = | 685 | float scroll_factor = |
@@ -700,7 +715,7 @@ static void handle_pointer_axis(struct sway_seat *seat, | |||
700 | seat_get_active_tiling_child(seat, tabcontainer); | 715 | seat_get_active_tiling_child(seat, tabcontainer); |
701 | list_t *siblings = container_get_siblings(cont); | 716 | list_t *siblings = container_get_siblings(cont); |
702 | int desired = list_find(siblings, active->sway_container) + | 717 | int desired = list_find(siblings, active->sway_container) + |
703 | round(scroll_factor * event->delta_discrete); | 718 | round(scroll_factor * event->delta_discrete / WLR_POINTER_AXIS_DISCRETE_STEP); |
704 | if (desired < 0) { | 719 | if (desired < 0) { |
705 | desired = 0; | 720 | desired = 0; |
706 | } else if (desired >= siblings->length) { | 721 | } else if (desired >= siblings->length) { |
@@ -714,6 +729,7 @@ static void handle_pointer_axis(struct sway_seat *seat, | |||
714 | // Use the focused child of the tabbed/stacked container, not the | 729 | // Use the focused child of the tabbed/stacked container, not the |
715 | // container the user scrolled on. | 730 | // container the user scrolled on. |
716 | seat_set_focus(seat, new_focus); | 731 | seat_set_focus(seat, new_focus); |
732 | transaction_commit_dirty(); | ||
717 | handled = true; | 733 | handled = true; |
718 | } | 734 | } |
719 | } | 735 | } |
@@ -736,6 +752,304 @@ static void handle_pointer_axis(struct sway_seat *seat, | |||
736 | } | 752 | } |
737 | } | 753 | } |
738 | 754 | ||
755 | /*------------------------------------\ | ||
756 | * Functions used by gesture support / | ||
757 | *----------------------------------*/ | ||
758 | |||
759 | /** | ||
760 | * Check gesture binding for a specific gesture type and finger count. | ||
761 | * Returns true if binding is present, false otherwise | ||
762 | */ | ||
763 | static bool gesture_binding_check(list_t *bindings, enum gesture_type type, | ||
764 | uint8_t fingers, struct sway_input_device *device) { | ||
765 | char *input = | ||
766 | device ? input_device_get_identifier(device->wlr_device) : strdup("*"); | ||
767 | |||
768 | for (int i = 0; i < bindings->length; ++i) { | ||
769 | struct sway_gesture_binding *binding = bindings->items[i]; | ||
770 | |||
771 | // Check type and finger count | ||
772 | if (!gesture_check(&binding->gesture, type, fingers)) { | ||
773 | continue; | ||
774 | } | ||
775 | |||
776 | // Check that input matches | ||
777 | if (strcmp(binding->input, "*") != 0 && | ||
778 | strcmp(binding->input, input) != 0) { | ||
779 | continue; | ||
780 | } | ||
781 | |||
782 | free(input); | ||
783 | |||
784 | return true; | ||
785 | } | ||
786 | |||
787 | free(input); | ||
788 | |||
789 | return false; | ||
790 | } | ||
791 | |||
792 | /** | ||
793 | * Return the gesture binding which matches gesture type, finger count | ||
794 | * and direction, otherwise return null. | ||
795 | */ | ||
796 | static struct sway_gesture_binding* gesture_binding_match( | ||
797 | list_t *bindings, struct gesture *gesture, const char *input) { | ||
798 | struct sway_gesture_binding *current = NULL; | ||
799 | |||
800 | // Find best matching binding | ||
801 | for (int i = 0; i < bindings->length; ++i) { | ||
802 | struct sway_gesture_binding *binding = bindings->items[i]; | ||
803 | bool exact = binding->flags & BINDING_EXACT; | ||
804 | |||
805 | // Check gesture matching | ||
806 | if (!gesture_match(&binding->gesture, gesture, exact)) { | ||
807 | continue; | ||
808 | } | ||
809 | |||
810 | // Check input matching | ||
811 | if (strcmp(binding->input, "*") != 0 && | ||
812 | strcmp(binding->input, input) != 0) { | ||
813 | continue; | ||
814 | } | ||
815 | |||
816 | // If we already have a match ... | ||
817 | if (current) { | ||
818 | // ... check if input matching is equivalent | ||
819 | if (strcmp(current->input, binding->input) == 0) { | ||
820 | |||
821 | // ... - do not override an exact binding | ||
822 | if (!exact && current->flags & BINDING_EXACT) { | ||
823 | continue; | ||
824 | } | ||
825 | |||
826 | // ... - and ensure direction matching is better or equal | ||
827 | if (gesture_compare(¤t->gesture, &binding->gesture) > 0) { | ||
828 | continue; | ||
829 | } | ||
830 | } else if (strcmp(binding->input, "*") == 0) { | ||
831 | // ... do not accept worse input match | ||
832 | continue; | ||
833 | } | ||
834 | } | ||
835 | |||
836 | // Accept newer or better match | ||
837 | current = binding; | ||
838 | |||
839 | // If exact binding and input is found, quit search | ||
840 | if (strcmp(current->input, input) == 0 && | ||
841 | gesture_compare(¤t->gesture, gesture) == 0) { | ||
842 | break; | ||
843 | } | ||
844 | } // for all gesture bindings | ||
845 | |||
846 | return current; | ||
847 | } | ||
848 | |||
849 | // Wrapper around gesture_tracker_end to use tracker with sway bindings | ||
850 | static struct sway_gesture_binding* gesture_tracker_end_and_match( | ||
851 | struct gesture_tracker *tracker, struct sway_input_device* device) { | ||
852 | // Determine name of input that received gesture | ||
853 | char *input = device | ||
854 | ? input_device_get_identifier(device->wlr_device) | ||
855 | : strdup("*"); | ||
856 | |||
857 | // Match tracking result to binding | ||
858 | struct gesture *gesture = gesture_tracker_end(tracker); | ||
859 | struct sway_gesture_binding *binding = gesture_binding_match( | ||
860 | config->current_mode->gesture_bindings, gesture, input); | ||
861 | free(gesture); | ||
862 | free(input); | ||
863 | |||
864 | return binding; | ||
865 | } | ||
866 | |||
867 | // Small wrapper around seat_execute_command to work on gesture bindings | ||
868 | static void gesture_binding_execute(struct sway_seat *seat, | ||
869 | struct sway_gesture_binding *binding) { | ||
870 | struct sway_binding *dummy_binding = | ||
871 | calloc(1, sizeof(struct sway_binding)); | ||
872 | dummy_binding->type = BINDING_GESTURE; | ||
873 | dummy_binding->command = binding->command; | ||
874 | |||
875 | char *description = gesture_to_string(&binding->gesture); | ||
876 | sway_log(SWAY_DEBUG, "executing gesture binding: %s", description); | ||
877 | free(description); | ||
878 | |||
879 | seat_execute_command(seat, dummy_binding); | ||
880 | |||
881 | free(dummy_binding); | ||
882 | } | ||
883 | |||
884 | static void handle_hold_begin(struct sway_seat *seat, | ||
885 | struct wlr_pointer_hold_begin_event *event) { | ||
886 | // Start tracking gesture if there is a matching binding ... | ||
887 | struct sway_input_device *device = | ||
888 | event->pointer ? event->pointer->base.data : NULL; | ||
889 | list_t *bindings = config->current_mode->gesture_bindings; | ||
890 | if (gesture_binding_check(bindings, GESTURE_TYPE_HOLD, event->fingers, device)) { | ||
891 | struct seatop_default_event *seatop = seat->seatop_data; | ||
892 | gesture_tracker_begin(&seatop->gestures, GESTURE_TYPE_HOLD, event->fingers); | ||
893 | } else { | ||
894 | // ... otherwise forward to client | ||
895 | struct sway_cursor *cursor = seat->cursor; | ||
896 | wlr_pointer_gestures_v1_send_hold_begin( | ||
897 | cursor->pointer_gestures, cursor->seat->wlr_seat, | ||
898 | event->time_msec, event->fingers); | ||
899 | } | ||
900 | } | ||
901 | |||
902 | static void handle_hold_end(struct sway_seat *seat, | ||
903 | struct wlr_pointer_hold_end_event *event) { | ||
904 | // Ensure that gesture is being tracked and was not cancelled | ||
905 | struct seatop_default_event *seatop = seat->seatop_data; | ||
906 | if (!gesture_tracker_check(&seatop->gestures, GESTURE_TYPE_HOLD)) { | ||
907 | struct sway_cursor *cursor = seat->cursor; | ||
908 | wlr_pointer_gestures_v1_send_hold_end( | ||
909 | cursor->pointer_gestures, cursor->seat->wlr_seat, | ||
910 | event->time_msec, event->cancelled); | ||
911 | return; | ||
912 | } | ||
913 | if (event->cancelled) { | ||
914 | gesture_tracker_cancel(&seatop->gestures); | ||
915 | return; | ||
916 | } | ||
917 | |||
918 | // End gesture tracking and execute matched binding | ||
919 | struct sway_input_device *device = | ||
920 | event->pointer ? event->pointer->base.data : NULL; | ||
921 | struct sway_gesture_binding *binding = gesture_tracker_end_and_match( | ||
922 | &seatop->gestures, device); | ||
923 | |||
924 | if (binding) { | ||
925 | gesture_binding_execute(seat, binding); | ||
926 | } | ||
927 | } | ||
928 | |||
929 | static void handle_pinch_begin(struct sway_seat *seat, | ||
930 | struct wlr_pointer_pinch_begin_event *event) { | ||
931 | // Start tracking gesture if there is a matching binding ... | ||
932 | struct sway_input_device *device = | ||
933 | event->pointer ? event->pointer->base.data : NULL; | ||
934 | list_t *bindings = config->current_mode->gesture_bindings; | ||
935 | if (gesture_binding_check(bindings, GESTURE_TYPE_PINCH, event->fingers, device)) { | ||
936 | struct seatop_default_event *seatop = seat->seatop_data; | ||
937 | gesture_tracker_begin(&seatop->gestures, GESTURE_TYPE_PINCH, event->fingers); | ||
938 | } else { | ||
939 | // ... otherwise forward to client | ||
940 | struct sway_cursor *cursor = seat->cursor; | ||
941 | wlr_pointer_gestures_v1_send_pinch_begin( | ||
942 | cursor->pointer_gestures, cursor->seat->wlr_seat, | ||
943 | event->time_msec, event->fingers); | ||
944 | } | ||
945 | } | ||
946 | |||
947 | static void handle_pinch_update(struct sway_seat *seat, | ||
948 | struct wlr_pointer_pinch_update_event *event) { | ||
949 | // Update any ongoing tracking ... | ||
950 | struct seatop_default_event *seatop = seat->seatop_data; | ||
951 | if (gesture_tracker_check(&seatop->gestures, GESTURE_TYPE_PINCH)) { | ||
952 | gesture_tracker_update(&seatop->gestures, event->dx, event->dy, | ||
953 | event->scale, event->rotation); | ||
954 | } else { | ||
955 | // ... otherwise forward to client | ||
956 | struct sway_cursor *cursor = seat->cursor; | ||
957 | wlr_pointer_gestures_v1_send_pinch_update( | ||
958 | cursor->pointer_gestures, | ||
959 | cursor->seat->wlr_seat, | ||
960 | event->time_msec, event->dx, event->dy, | ||
961 | event->scale, event->rotation); | ||
962 | } | ||
963 | } | ||
964 | |||
965 | static void handle_pinch_end(struct sway_seat *seat, | ||
966 | struct wlr_pointer_pinch_end_event *event) { | ||
967 | // Ensure that gesture is being tracked and was not cancelled | ||
968 | struct seatop_default_event *seatop = seat->seatop_data; | ||
969 | if (!gesture_tracker_check(&seatop->gestures, GESTURE_TYPE_PINCH)) { | ||
970 | struct sway_cursor *cursor = seat->cursor; | ||
971 | wlr_pointer_gestures_v1_send_pinch_end( | ||
972 | cursor->pointer_gestures, cursor->seat->wlr_seat, | ||
973 | event->time_msec, event->cancelled); | ||
974 | return; | ||
975 | } | ||
976 | if (event->cancelled) { | ||
977 | gesture_tracker_cancel(&seatop->gestures); | ||
978 | return; | ||
979 | } | ||
980 | |||
981 | // End gesture tracking and execute matched binding | ||
982 | struct sway_input_device *device = | ||
983 | event->pointer ? event->pointer->base.data : NULL; | ||
984 | struct sway_gesture_binding *binding = gesture_tracker_end_and_match( | ||
985 | &seatop->gestures, device); | ||
986 | |||
987 | if (binding) { | ||
988 | gesture_binding_execute(seat, binding); | ||
989 | } | ||
990 | } | ||
991 | |||
992 | static void handle_swipe_begin(struct sway_seat *seat, | ||
993 | struct wlr_pointer_swipe_begin_event *event) { | ||
994 | // Start tracking gesture if there is a matching binding ... | ||
995 | struct sway_input_device *device = | ||
996 | event->pointer ? event->pointer->base.data : NULL; | ||
997 | list_t *bindings = config->current_mode->gesture_bindings; | ||
998 | if (gesture_binding_check(bindings, GESTURE_TYPE_SWIPE, event->fingers, device)) { | ||
999 | struct seatop_default_event *seatop = seat->seatop_data; | ||
1000 | gesture_tracker_begin(&seatop->gestures, GESTURE_TYPE_SWIPE, event->fingers); | ||
1001 | } else { | ||
1002 | // ... otherwise forward to client | ||
1003 | struct sway_cursor *cursor = seat->cursor; | ||
1004 | wlr_pointer_gestures_v1_send_swipe_begin( | ||
1005 | cursor->pointer_gestures, cursor->seat->wlr_seat, | ||
1006 | event->time_msec, event->fingers); | ||
1007 | } | ||
1008 | } | ||
1009 | |||
1010 | static void handle_swipe_update(struct sway_seat *seat, | ||
1011 | struct wlr_pointer_swipe_update_event *event) { | ||
1012 | |||
1013 | // Update any ongoing tracking ... | ||
1014 | struct seatop_default_event *seatop = seat->seatop_data; | ||
1015 | if (gesture_tracker_check(&seatop->gestures, GESTURE_TYPE_SWIPE)) { | ||
1016 | gesture_tracker_update(&seatop->gestures, | ||
1017 | event->dx, event->dy, NAN, NAN); | ||
1018 | } else { | ||
1019 | // ... otherwise forward to client | ||
1020 | struct sway_cursor *cursor = seat->cursor; | ||
1021 | wlr_pointer_gestures_v1_send_swipe_update( | ||
1022 | cursor->pointer_gestures, cursor->seat->wlr_seat, | ||
1023 | event->time_msec, event->dx, event->dy); | ||
1024 | } | ||
1025 | } | ||
1026 | |||
1027 | static void handle_swipe_end(struct sway_seat *seat, | ||
1028 | struct wlr_pointer_swipe_end_event *event) { | ||
1029 | // Ensure gesture is being tracked and was not cancelled | ||
1030 | struct seatop_default_event *seatop = seat->seatop_data; | ||
1031 | if (!gesture_tracker_check(&seatop->gestures, GESTURE_TYPE_SWIPE)) { | ||
1032 | struct sway_cursor *cursor = seat->cursor; | ||
1033 | wlr_pointer_gestures_v1_send_swipe_end(cursor->pointer_gestures, | ||
1034 | cursor->seat->wlr_seat, event->time_msec, event->cancelled); | ||
1035 | return; | ||
1036 | } | ||
1037 | if (event->cancelled) { | ||
1038 | gesture_tracker_cancel(&seatop->gestures); | ||
1039 | return; | ||
1040 | } | ||
1041 | |||
1042 | // End gesture tracking and execute matched binding | ||
1043 | struct sway_input_device *device = | ||
1044 | event->pointer ? event->pointer->base.data : NULL; | ||
1045 | struct sway_gesture_binding *binding = gesture_tracker_end_and_match( | ||
1046 | &seatop->gestures, device); | ||
1047 | |||
1048 | if (binding) { | ||
1049 | gesture_binding_execute(seat, binding); | ||
1050 | } | ||
1051 | } | ||
1052 | |||
739 | /*----------------------------------\ | 1053 | /*----------------------------------\ |
740 | * Functions used by handle_rebase / | 1054 | * Functions used by handle_rebase / |
741 | *--------------------------------*/ | 1055 | *--------------------------------*/ |
@@ -765,6 +1079,14 @@ static const struct sway_seatop_impl seatop_impl = { | |||
765 | .pointer_axis = handle_pointer_axis, | 1079 | .pointer_axis = handle_pointer_axis, |
766 | .tablet_tool_tip = handle_tablet_tool_tip, | 1080 | .tablet_tool_tip = handle_tablet_tool_tip, |
767 | .tablet_tool_motion = handle_tablet_tool_motion, | 1081 | .tablet_tool_motion = handle_tablet_tool_motion, |
1082 | .hold_begin = handle_hold_begin, | ||
1083 | .hold_end = handle_hold_end, | ||
1084 | .pinch_begin = handle_pinch_begin, | ||
1085 | .pinch_update = handle_pinch_update, | ||
1086 | .pinch_end = handle_pinch_end, | ||
1087 | .swipe_begin = handle_swipe_begin, | ||
1088 | .swipe_update = handle_swipe_update, | ||
1089 | .swipe_end = handle_swipe_end, | ||
768 | .rebase = handle_rebase, | 1090 | .rebase = handle_rebase, |
769 | .allow_set_cursor = true, | 1091 | .allow_set_cursor = true, |
770 | }; | 1092 | }; |
@@ -775,8 +1097,8 @@ void seatop_begin_default(struct sway_seat *seat) { | |||
775 | struct seatop_default_event *e = | 1097 | struct seatop_default_event *e = |
776 | calloc(1, sizeof(struct seatop_default_event)); | 1098 | calloc(1, sizeof(struct seatop_default_event)); |
777 | sway_assert(e, "Unable to allocate seatop_default_event"); | 1099 | sway_assert(e, "Unable to allocate seatop_default_event"); |
1100 | |||
778 | seat->seatop_impl = &seatop_impl; | 1101 | seat->seatop_impl = &seatop_impl; |
779 | seat->seatop_data = e; | 1102 | seat->seatop_data = e; |
780 | |||
781 | seatop_rebase(seat, 0); | 1103 | seatop_rebase(seat, 0); |
782 | } | 1104 | } |
diff --git a/sway/input/seatop_down.c b/sway/input/seatop_down.c index 17f619e3..b40773d0 100644 --- a/sway/input/seatop_down.c +++ b/sway/input/seatop_down.c | |||
@@ -5,18 +5,22 @@ | |||
5 | #include "sway/input/cursor.h" | 5 | #include "sway/input/cursor.h" |
6 | #include "sway/input/seat.h" | 6 | #include "sway/input/seat.h" |
7 | #include "sway/tree/view.h" | 7 | #include "sway/tree/view.h" |
8 | #include "sway/desktop/transaction.h" | ||
8 | #include "log.h" | 9 | #include "log.h" |
9 | 10 | ||
10 | struct seatop_down_event { | 11 | struct seatop_down_event { |
11 | struct sway_container *con; | 12 | struct sway_container *con; |
13 | struct sway_seat *seat; | ||
14 | struct wl_listener surface_destroy; | ||
15 | struct wlr_surface *surface; | ||
12 | double ref_lx, ref_ly; // cursor's x/y at start of op | 16 | double ref_lx, ref_ly; // cursor's x/y at start of op |
13 | double ref_con_lx, ref_con_ly; // container's x/y at start of op | 17 | double ref_con_lx, ref_con_ly; // container's x/y at start of op |
14 | }; | 18 | }; |
15 | 19 | ||
16 | static void handle_pointer_axis(struct sway_seat *seat, | 20 | static void handle_pointer_axis(struct sway_seat *seat, |
17 | struct wlr_event_pointer_axis *event) { | 21 | struct wlr_pointer_axis_event *event) { |
18 | struct sway_input_device *input_device = | 22 | struct sway_input_device *input_device = |
19 | event->device ? event->device->data : NULL; | 23 | event->pointer ? event->pointer->base.data : NULL; |
20 | struct input_config *ic = | 24 | struct input_config *ic = |
21 | input_device ? input_device_get_config(input_device) : NULL; | 25 | input_device ? input_device_get_config(input_device) : NULL; |
22 | float scroll_factor = | 26 | float scroll_factor = |
@@ -39,8 +43,7 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec, | |||
39 | 43 | ||
40 | static void handle_pointer_motion(struct sway_seat *seat, uint32_t time_msec) { | 44 | static void handle_pointer_motion(struct sway_seat *seat, uint32_t time_msec) { |
41 | struct seatop_down_event *e = seat->seatop_data; | 45 | struct seatop_down_event *e = seat->seatop_data; |
42 | struct sway_container *con = e->con; | 46 | if (seat_is_input_allowed(seat, e->surface)) { |
43 | if (seat_is_input_allowed(seat, con->view->surface)) { | ||
44 | double moved_x = seat->cursor->cursor->x - e->ref_lx; | 47 | double moved_x = seat->cursor->cursor->x - e->ref_lx; |
45 | double moved_y = seat->cursor->cursor->y - e->ref_ly; | 48 | double moved_y = seat->cursor->cursor->y - e->ref_ly; |
46 | double sx = e->ref_con_lx + moved_x; | 49 | double sx = e->ref_con_lx + moved_x; |
@@ -61,8 +64,7 @@ static void handle_tablet_tool_tip(struct sway_seat *seat, | |||
61 | static void handle_tablet_tool_motion(struct sway_seat *seat, | 64 | static void handle_tablet_tool_motion(struct sway_seat *seat, |
62 | struct sway_tablet_tool *tool, uint32_t time_msec) { | 65 | struct sway_tablet_tool *tool, uint32_t time_msec) { |
63 | struct seatop_down_event *e = seat->seatop_data; | 66 | struct seatop_down_event *e = seat->seatop_data; |
64 | struct sway_container *con = e->con; | 67 | if (seat_is_input_allowed(seat, e->surface)) { |
65 | if (seat_is_input_allowed(seat, con->view->surface)) { | ||
66 | double moved_x = seat->cursor->cursor->x - e->ref_lx; | 68 | double moved_x = seat->cursor->cursor->x - e->ref_lx; |
67 | double moved_y = seat->cursor->cursor->y - e->ref_ly; | 69 | double moved_y = seat->cursor->cursor->y - e->ref_ly; |
68 | double sx = e->ref_con_lx + moved_x; | 70 | double sx = e->ref_con_lx + moved_x; |
@@ -71,6 +73,14 @@ static void handle_tablet_tool_motion(struct sway_seat *seat, | |||
71 | } | 73 | } |
72 | } | 74 | } |
73 | 75 | ||
76 | static void handle_destroy(struct wl_listener *listener, void *data) { | ||
77 | struct seatop_down_event *e = | ||
78 | wl_container_of(listener, e, surface_destroy); | ||
79 | if (e) { | ||
80 | seatop_begin_default(e->seat); | ||
81 | } | ||
82 | } | ||
83 | |||
74 | static void handle_unref(struct sway_seat *seat, struct sway_container *con) { | 84 | static void handle_unref(struct sway_seat *seat, struct sway_container *con) { |
75 | struct seatop_down_event *e = seat->seatop_data; | 85 | struct seatop_down_event *e = seat->seatop_data; |
76 | if (e->con == con) { | 86 | if (e->con == con) { |
@@ -78,6 +88,11 @@ static void handle_unref(struct sway_seat *seat, struct sway_container *con) { | |||
78 | } | 88 | } |
79 | } | 89 | } |
80 | 90 | ||
91 | static void handle_end(struct sway_seat *seat) { | ||
92 | struct seatop_down_event *e = seat->seatop_data; | ||
93 | wl_list_remove(&e->surface_destroy.link); | ||
94 | } | ||
95 | |||
81 | static const struct sway_seatop_impl seatop_impl = { | 96 | static const struct sway_seatop_impl seatop_impl = { |
82 | .button = handle_button, | 97 | .button = handle_button, |
83 | .pointer_motion = handle_pointer_motion, | 98 | .pointer_motion = handle_pointer_motion, |
@@ -85,11 +100,22 @@ static const struct sway_seatop_impl seatop_impl = { | |||
85 | .tablet_tool_tip = handle_tablet_tool_tip, | 100 | .tablet_tool_tip = handle_tablet_tool_tip, |
86 | .tablet_tool_motion = handle_tablet_tool_motion, | 101 | .tablet_tool_motion = handle_tablet_tool_motion, |
87 | .unref = handle_unref, | 102 | .unref = handle_unref, |
103 | .end = handle_end, | ||
88 | .allow_set_cursor = true, | 104 | .allow_set_cursor = true, |
89 | }; | 105 | }; |
90 | 106 | ||
91 | void seatop_begin_down(struct sway_seat *seat, struct sway_container *con, | 107 | void seatop_begin_down(struct sway_seat *seat, struct sway_container *con, |
92 | uint32_t time_msec, int sx, int sy) { | 108 | uint32_t time_msec, double sx, double sy) { |
109 | seatop_begin_down_on_surface(seat, con->view->surface, time_msec, sx, sy); | ||
110 | struct seatop_down_event *e = seat->seatop_data; | ||
111 | e->con = con; | ||
112 | |||
113 | container_raise_floating(con); | ||
114 | transaction_commit_dirty(); | ||
115 | } | ||
116 | |||
117 | void seatop_begin_down_on_surface(struct sway_seat *seat, | ||
118 | struct wlr_surface *surface, uint32_t time_msec, double sx, double sy) { | ||
93 | seatop_end(seat); | 119 | seatop_end(seat); |
94 | 120 | ||
95 | struct seatop_down_event *e = | 121 | struct seatop_down_event *e = |
@@ -97,7 +123,11 @@ void seatop_begin_down(struct sway_seat *seat, struct sway_container *con, | |||
97 | if (!e) { | 123 | if (!e) { |
98 | return; | 124 | return; |
99 | } | 125 | } |
100 | e->con = con; | 126 | e->con = NULL; |
127 | e->seat = seat; | ||
128 | e->surface = surface; | ||
129 | wl_signal_add(&e->surface->events.destroy, &e->surface_destroy); | ||
130 | e->surface_destroy.notify = handle_destroy; | ||
101 | e->ref_lx = seat->cursor->cursor->x; | 131 | e->ref_lx = seat->cursor->cursor->x; |
102 | e->ref_ly = seat->cursor->cursor->y; | 132 | e->ref_ly = seat->cursor->cursor->y; |
103 | e->ref_con_lx = sx; | 133 | e->ref_con_lx = sx; |
@@ -105,6 +135,4 @@ void seatop_begin_down(struct sway_seat *seat, struct sway_container *con, | |||
105 | 135 | ||
106 | seat->seatop_impl = &seatop_impl; | 136 | seat->seatop_impl = &seatop_impl; |
107 | seat->seatop_data = e; | 137 | seat->seatop_data = e; |
108 | |||
109 | container_raise_floating(con); | ||
110 | } | 138 | } |
diff --git a/sway/input/seatop_move_floating.c b/sway/input/seatop_move_floating.c index 7f501fc9..ddcd4c53 100644 --- a/sway/input/seatop_move_floating.c +++ b/sway/input/seatop_move_floating.c | |||
@@ -1,6 +1,7 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | 1 | #define _POSIX_C_SOURCE 200809L |
2 | #include <wlr/types/wlr_cursor.h> | 2 | #include <wlr/types/wlr_cursor.h> |
3 | #include "sway/desktop.h" | 3 | #include "sway/desktop.h" |
4 | #include "sway/desktop/transaction.h" | ||
4 | #include "sway/input/cursor.h" | 5 | #include "sway/input/cursor.h" |
5 | #include "sway/input/seat.h" | 6 | #include "sway/input/seat.h" |
6 | 7 | ||
@@ -14,7 +15,8 @@ static void finalize_move(struct sway_seat *seat) { | |||
14 | 15 | ||
15 | // We "move" the container to its own location | 16 | // We "move" the container to its own location |
16 | // so it discovers its output again. | 17 | // so it discovers its output again. |
17 | container_floating_move_to(e->con, e->con->x, e->con->y); | 18 | container_floating_move_to(e->con, e->con->pending.x, e->con->pending.y); |
19 | transaction_commit_dirty(); | ||
18 | 20 | ||
19 | seatop_begin_default(seat); | 21 | seatop_begin_default(seat); |
20 | } | 22 | } |
@@ -40,6 +42,7 @@ static void handle_pointer_motion(struct sway_seat *seat, uint32_t time_msec) { | |||
40 | desktop_damage_whole_container(e->con); | 42 | desktop_damage_whole_container(e->con); |
41 | container_floating_move_to(e->con, cursor->x - e->dx, cursor->y - e->dy); | 43 | container_floating_move_to(e->con, cursor->x - e->dx, cursor->y - e->dy); |
42 | desktop_damage_whole_container(e->con); | 44 | desktop_damage_whole_container(e->con); |
45 | transaction_commit_dirty(); | ||
43 | } | 46 | } |
44 | 47 | ||
45 | static void handle_unref(struct sway_seat *seat, struct sway_container *con) { | 48 | static void handle_unref(struct sway_seat *seat, struct sway_container *con) { |
@@ -67,13 +70,14 @@ void seatop_begin_move_floating(struct sway_seat *seat, | |||
67 | return; | 70 | return; |
68 | } | 71 | } |
69 | e->con = con; | 72 | e->con = con; |
70 | e->dx = cursor->cursor->x - con->x; | 73 | e->dx = cursor->cursor->x - con->pending.x; |
71 | e->dy = cursor->cursor->y - con->y; | 74 | e->dy = cursor->cursor->y - con->pending.y; |
72 | 75 | ||
73 | seat->seatop_impl = &seatop_impl; | 76 | seat->seatop_impl = &seatop_impl; |
74 | seat->seatop_data = e; | 77 | seat->seatop_data = e; |
75 | 78 | ||
76 | container_raise_floating(con); | 79 | container_raise_floating(con); |
80 | transaction_commit_dirty(); | ||
77 | 81 | ||
78 | cursor_set_image(cursor, "grab", NULL); | 82 | cursor_set_image(cursor, "grab", NULL); |
79 | wlr_seat_pointer_notify_clear_focus(seat->wlr_seat); | 83 | wlr_seat_pointer_notify_clear_focus(seat->wlr_seat); |
diff --git a/sway/input/seatop_move_tiling.c b/sway/input/seatop_move_tiling.c index 704e7270..223c6c08 100644 --- a/sway/input/seatop_move_tiling.c +++ b/sway/input/seatop_move_tiling.c | |||
@@ -3,6 +3,7 @@ | |||
3 | #include <wlr/types/wlr_cursor.h> | 3 | #include <wlr/types/wlr_cursor.h> |
4 | #include <wlr/util/edges.h> | 4 | #include <wlr/util/edges.h> |
5 | #include "sway/desktop.h" | 5 | #include "sway/desktop.h" |
6 | #include "sway/desktop/transaction.h" | ||
6 | #include "sway/input/cursor.h" | 7 | #include "sway/input/cursor.h" |
7 | #include "sway/input/seat.h" | 8 | #include "sway/input/seat.h" |
8 | #include "sway/ipc-server.h" | 9 | #include "sway/ipc-server.h" |
@@ -15,6 +16,10 @@ | |||
15 | // Thickness of the dropzone when dragging to the edge of a layout container | 16 | // Thickness of the dropzone when dragging to the edge of a layout container |
16 | #define DROP_LAYOUT_BORDER 30 | 17 | #define DROP_LAYOUT_BORDER 30 |
17 | 18 | ||
19 | // Thickness of indicator when dropping onto a titlebar. This should be a | ||
20 | // multiple of 2. | ||
21 | #define DROP_SPLIT_INDICATOR 10 | ||
22 | |||
18 | struct seatop_move_tiling_event { | 23 | struct seatop_move_tiling_event { |
19 | struct sway_container *con; | 24 | struct sway_container *con; |
20 | struct sway_node *target_node; | 25 | struct sway_node *target_node; |
@@ -22,6 +27,8 @@ struct seatop_move_tiling_event { | |||
22 | struct wlr_box drop_box; | 27 | struct wlr_box drop_box; |
23 | double ref_lx, ref_ly; // cursor's x/y at start of op | 28 | double ref_lx, ref_ly; // cursor's x/y at start of op |
24 | bool threshold_reached; | 29 | bool threshold_reached; |
30 | bool split_target; | ||
31 | bool insert_after_target; | ||
25 | }; | 32 | }; |
26 | 33 | ||
27 | static void handle_render(struct sway_seat *seat, | 34 | static void handle_render(struct sway_seat *seat, |
@@ -91,8 +98,76 @@ static void resize_box(struct wlr_box *box, enum wlr_edges edge, | |||
91 | } | 98 | } |
92 | } | 99 | } |
93 | 100 | ||
101 | static void split_border(double pos, int offset, int len, int n_children, | ||
102 | int avoid, int *out_pos, bool *out_after) { | ||
103 | int region = 2 * n_children * (pos - offset) / len; | ||
104 | // If the cursor is over the right side of a left-adjacent titlebar, or the | ||
105 | // left side of a right-adjacent titlebar, it's position when dropped will | ||
106 | // be the same. To avoid this, shift the region for adjacent containers. | ||
107 | if (avoid >= 0) { | ||
108 | if (region == 2 * avoid - 1 || region == 2 * avoid) { | ||
109 | region--; | ||
110 | } else if (region == 2 * avoid + 1 || region == 2 * avoid + 2) { | ||
111 | region++; | ||
112 | } | ||
113 | } | ||
114 | |||
115 | int child_index = (region + 1) / 2; | ||
116 | *out_after = region % 2; | ||
117 | // When dropping at the beginning or end of a container, show the drop | ||
118 | // region within the container boundary, otherwise show it on top of the | ||
119 | // border between two titlebars. | ||
120 | if (child_index == 0) { | ||
121 | *out_pos = offset; | ||
122 | } else if (child_index == n_children) { | ||
123 | *out_pos = offset + len - DROP_SPLIT_INDICATOR; | ||
124 | } else { | ||
125 | *out_pos = offset + child_index * len / n_children - | ||
126 | DROP_SPLIT_INDICATOR / 2; | ||
127 | } | ||
128 | } | ||
129 | |||
130 | static bool split_titlebar(struct sway_node *node, struct sway_container *avoid, | ||
131 | struct wlr_cursor *cursor, struct wlr_box *title_box, bool *after) { | ||
132 | struct sway_container *con = node->sway_container; | ||
133 | struct sway_node *parent = &con->pending.parent->node; | ||
134 | int title_height = container_titlebar_height(); | ||
135 | struct wlr_box box; | ||
136 | int n_children, avoid_index; | ||
137 | enum sway_container_layout layout = | ||
138 | parent ? node_get_layout(parent) : L_NONE; | ||
139 | if (layout == L_TABBED || layout == L_STACKED) { | ||
140 | node_get_box(parent, &box); | ||
141 | n_children = node_get_children(parent)->length; | ||
142 | avoid_index = list_find(node_get_children(parent), avoid); | ||
143 | } else { | ||
144 | node_get_box(node, &box); | ||
145 | n_children = 1; | ||
146 | avoid_index = -1; | ||
147 | } | ||
148 | if (layout == L_STACKED && cursor->y < box.y + title_height * n_children) { | ||
149 | // Drop into stacked titlebars. | ||
150 | title_box->width = box.width; | ||
151 | title_box->height = DROP_SPLIT_INDICATOR; | ||
152 | title_box->x = box.x; | ||
153 | split_border(cursor->y, box.y, title_height * n_children, | ||
154 | n_children, avoid_index, &title_box->y, after); | ||
155 | return true; | ||
156 | } else if (layout != L_STACKED && cursor->y < box.y + title_height) { | ||
157 | // Drop into side-by-side titlebars. | ||
158 | title_box->width = DROP_SPLIT_INDICATOR; | ||
159 | title_box->height = title_height; | ||
160 | title_box->y = box.y; | ||
161 | split_border(cursor->x, box.x, box.width, n_children, | ||
162 | avoid_index, &title_box->x, after); | ||
163 | return true; | ||
164 | } | ||
165 | return false; | ||
166 | } | ||
167 | |||
94 | static void handle_motion_postthreshold(struct sway_seat *seat) { | 168 | static void handle_motion_postthreshold(struct sway_seat *seat) { |
95 | struct seatop_move_tiling_event *e = seat->seatop_data; | 169 | struct seatop_move_tiling_event *e = seat->seatop_data; |
170 | e->split_target = false; | ||
96 | struct wlr_surface *surface = NULL; | 171 | struct wlr_surface *surface = NULL; |
97 | double sx, sy; | 172 | double sx, sy; |
98 | struct sway_cursor *cursor = seat->cursor; | 173 | struct sway_cursor *cursor = seat->cursor; |
@@ -119,34 +194,60 @@ static void handle_motion_postthreshold(struct sway_seat *seat) { | |||
119 | 194 | ||
120 | // Deny moving within own workspace if this is the only child | 195 | // Deny moving within own workspace if this is the only child |
121 | struct sway_container *con = node->sway_container; | 196 | struct sway_container *con = node->sway_container; |
122 | if (workspace_num_tiling_views(e->con->workspace) == 1 && | 197 | if (workspace_num_tiling_views(e->con->pending.workspace) == 1 && |
123 | con->workspace == e->con->workspace) { | 198 | con->pending.workspace == e->con->pending.workspace) { |
124 | e->target_node = NULL; | 199 | e->target_node = NULL; |
125 | e->target_edge = WLR_EDGE_NONE; | 200 | e->target_edge = WLR_EDGE_NONE; |
126 | return; | 201 | return; |
127 | } | 202 | } |
128 | 203 | ||
204 | // Check if the cursor is over a tilebar only if the destination | ||
205 | // container is not a descendant of the source container. | ||
206 | if (!surface && !container_has_ancestor(con, e->con) && | ||
207 | split_titlebar(node, e->con, cursor->cursor, | ||
208 | &e->drop_box, &e->insert_after_target)) { | ||
209 | // Don't allow dropping over the source container's titlebar | ||
210 | // to give users a chance to cancel a drag operation. | ||
211 | if (con == e->con) { | ||
212 | e->target_node = NULL; | ||
213 | } else { | ||
214 | e->target_node = node; | ||
215 | e->split_target = true; | ||
216 | } | ||
217 | e->target_edge = WLR_EDGE_NONE; | ||
218 | return; | ||
219 | } | ||
220 | |||
129 | // Traverse the ancestors, trying to find a layout container perpendicular | 221 | // Traverse the ancestors, trying to find a layout container perpendicular |
130 | // to the edge. Eg. close to the top or bottom of a horiz layout. | 222 | // to the edge. Eg. close to the top or bottom of a horiz layout. |
223 | int thresh_top = con->pending.content_y + DROP_LAYOUT_BORDER; | ||
224 | int thresh_bottom = con->pending.content_y + | ||
225 | con->pending.content_height - DROP_LAYOUT_BORDER; | ||
226 | int thresh_left = con->pending.content_x + DROP_LAYOUT_BORDER; | ||
227 | int thresh_right = con->pending.content_x + | ||
228 | con->pending.content_width - DROP_LAYOUT_BORDER; | ||
131 | while (con) { | 229 | while (con) { |
132 | enum wlr_edges edge = WLR_EDGE_NONE; | 230 | enum wlr_edges edge = WLR_EDGE_NONE; |
133 | enum sway_container_layout layout = container_parent_layout(con); | 231 | enum sway_container_layout layout = container_parent_layout(con); |
134 | struct wlr_box parent; | 232 | struct wlr_box box; |
135 | con->parent ? container_get_box(con->parent, &parent) : | 233 | node_get_box(node_get_parent(&con->node), &box); |
136 | workspace_get_box(con->workspace, &parent); | ||
137 | if (layout == L_HORIZ || layout == L_TABBED) { | 234 | if (layout == L_HORIZ || layout == L_TABBED) { |
138 | if (cursor->cursor->y < parent.y + DROP_LAYOUT_BORDER) { | 235 | if (cursor->cursor->y < thresh_top) { |
139 | edge = WLR_EDGE_TOP; | 236 | edge = WLR_EDGE_TOP; |
140 | } else if (cursor->cursor->y > parent.y + parent.height | 237 | box.height = thresh_top - box.y; |
141 | - DROP_LAYOUT_BORDER) { | 238 | } else if (cursor->cursor->y > thresh_bottom) { |
142 | edge = WLR_EDGE_BOTTOM; | 239 | edge = WLR_EDGE_BOTTOM; |
240 | box.height = box.y + box.height - thresh_bottom; | ||
241 | box.y = thresh_bottom; | ||
143 | } | 242 | } |
144 | } else if (layout == L_VERT || layout == L_STACKED) { | 243 | } else if (layout == L_VERT || layout == L_STACKED) { |
145 | if (cursor->cursor->x < parent.x + DROP_LAYOUT_BORDER) { | 244 | if (cursor->cursor->x < thresh_left) { |
146 | edge = WLR_EDGE_LEFT; | 245 | edge = WLR_EDGE_LEFT; |
147 | } else if (cursor->cursor->x > parent.x + parent.width | 246 | box.width = thresh_left - box.x; |
148 | - DROP_LAYOUT_BORDER) { | 247 | } else if (cursor->cursor->x > thresh_right) { |
149 | edge = WLR_EDGE_RIGHT; | 248 | edge = WLR_EDGE_RIGHT; |
249 | box.width = box.x + box.width - thresh_right; | ||
250 | box.x = thresh_right; | ||
150 | } | 251 | } |
151 | } | 252 | } |
152 | if (edge) { | 253 | if (edge) { |
@@ -155,12 +256,11 @@ static void handle_motion_postthreshold(struct sway_seat *seat) { | |||
155 | e->target_node = node_get_parent(e->target_node); | 256 | e->target_node = node_get_parent(e->target_node); |
156 | } | 257 | } |
157 | e->target_edge = edge; | 258 | e->target_edge = edge; |
158 | node_get_box(e->target_node, &e->drop_box); | 259 | e->drop_box = box; |
159 | resize_box(&e->drop_box, edge, DROP_LAYOUT_BORDER); | ||
160 | desktop_damage_box(&e->drop_box); | 260 | desktop_damage_box(&e->drop_box); |
161 | return; | 261 | return; |
162 | } | 262 | } |
163 | con = con->parent; | 263 | con = con->pending.parent; |
164 | } | 264 | } |
165 | 265 | ||
166 | // Use the hovered view - but we must be over the actual surface | 266 | // Use the hovered view - but we must be over the actual surface |
@@ -173,23 +273,23 @@ static void handle_motion_postthreshold(struct sway_seat *seat) { | |||
173 | } | 273 | } |
174 | 274 | ||
175 | // Find the closest edge | 275 | // Find the closest edge |
176 | size_t thickness = fmin(con->content_width, con->content_height) * 0.3; | 276 | size_t thickness = fmin(con->pending.content_width, con->pending.content_height) * 0.3; |
177 | size_t closest_dist = INT_MAX; | 277 | size_t closest_dist = INT_MAX; |
178 | size_t dist; | 278 | size_t dist; |
179 | e->target_edge = WLR_EDGE_NONE; | 279 | e->target_edge = WLR_EDGE_NONE; |
180 | if ((dist = cursor->cursor->y - con->y) < closest_dist) { | 280 | if ((dist = cursor->cursor->y - con->pending.y) < closest_dist) { |
181 | closest_dist = dist; | 281 | closest_dist = dist; |
182 | e->target_edge = WLR_EDGE_TOP; | 282 | e->target_edge = WLR_EDGE_TOP; |
183 | } | 283 | } |
184 | if ((dist = cursor->cursor->x - con->x) < closest_dist) { | 284 | if ((dist = cursor->cursor->x - con->pending.x) < closest_dist) { |
185 | closest_dist = dist; | 285 | closest_dist = dist; |
186 | e->target_edge = WLR_EDGE_LEFT; | 286 | e->target_edge = WLR_EDGE_LEFT; |
187 | } | 287 | } |
188 | if ((dist = con->x + con->width - cursor->cursor->x) < closest_dist) { | 288 | if ((dist = con->pending.x + con->pending.width - cursor->cursor->x) < closest_dist) { |
189 | closest_dist = dist; | 289 | closest_dist = dist; |
190 | e->target_edge = WLR_EDGE_RIGHT; | 290 | e->target_edge = WLR_EDGE_RIGHT; |
191 | } | 291 | } |
192 | if ((dist = con->y + con->height - cursor->cursor->y) < closest_dist) { | 292 | if ((dist = con->pending.y + con->pending.height - cursor->cursor->y) < closest_dist) { |
193 | closest_dist = dist; | 293 | closest_dist = dist; |
194 | e->target_edge = WLR_EDGE_BOTTOM; | 294 | e->target_edge = WLR_EDGE_BOTTOM; |
195 | } | 295 | } |
@@ -199,10 +299,10 @@ static void handle_motion_postthreshold(struct sway_seat *seat) { | |||
199 | } | 299 | } |
200 | 300 | ||
201 | e->target_node = node; | 301 | e->target_node = node; |
202 | e->drop_box.x = con->content_x; | 302 | e->drop_box.x = con->pending.content_x; |
203 | e->drop_box.y = con->content_y; | 303 | e->drop_box.y = con->pending.content_y; |
204 | e->drop_box.width = con->content_width; | 304 | e->drop_box.width = con->pending.content_width; |
205 | e->drop_box.height = con->content_height; | 305 | e->drop_box.height = con->pending.content_height; |
206 | resize_box(&e->drop_box, e->target_edge, thickness); | 306 | resize_box(&e->drop_box, e->target_edge, thickness); |
207 | desktop_damage_box(&e->drop_box); | 307 | desktop_damage_box(&e->drop_box); |
208 | } | 308 | } |
@@ -214,6 +314,7 @@ static void handle_pointer_motion(struct sway_seat *seat, uint32_t time_msec) { | |||
214 | } else { | 314 | } else { |
215 | handle_motion_prethreshold(seat); | 315 | handle_motion_prethreshold(seat); |
216 | } | 316 | } |
317 | transaction_commit_dirty(); | ||
217 | } | 318 | } |
218 | 319 | ||
219 | static bool is_parallel(enum sway_container_layout layout, | 320 | static bool is_parallel(enum sway_container_layout layout, |
@@ -232,14 +333,15 @@ static void finalize_move(struct sway_seat *seat) { | |||
232 | } | 333 | } |
233 | 334 | ||
234 | struct sway_container *con = e->con; | 335 | struct sway_container *con = e->con; |
235 | struct sway_container *old_parent = con->parent; | 336 | struct sway_container *old_parent = con->pending.parent; |
236 | struct sway_workspace *old_ws = con->workspace; | 337 | struct sway_workspace *old_ws = con->pending.workspace; |
237 | struct sway_node *target_node = e->target_node; | 338 | struct sway_node *target_node = e->target_node; |
238 | struct sway_workspace *new_ws = target_node->type == N_WORKSPACE ? | 339 | struct sway_workspace *new_ws = target_node->type == N_WORKSPACE ? |
239 | target_node->sway_workspace : target_node->sway_container->workspace; | 340 | target_node->sway_workspace : target_node->sway_container->pending.workspace; |
240 | enum wlr_edges edge = e->target_edge; | 341 | enum wlr_edges edge = e->target_edge; |
241 | int after = edge != WLR_EDGE_TOP && edge != WLR_EDGE_LEFT; | 342 | int after = edge != WLR_EDGE_TOP && edge != WLR_EDGE_LEFT; |
242 | bool swap = edge == WLR_EDGE_NONE && target_node->type == N_CONTAINER; | 343 | bool swap = edge == WLR_EDGE_NONE && target_node->type == N_CONTAINER && |
344 | !e->split_target; | ||
243 | 345 | ||
244 | if (!swap) { | 346 | if (!swap) { |
245 | container_detach(con); | 347 | container_detach(con); |
@@ -248,6 +350,14 @@ static void finalize_move(struct sway_seat *seat) { | |||
248 | // Moving container into empty workspace | 350 | // Moving container into empty workspace |
249 | if (target_node->type == N_WORKSPACE && edge == WLR_EDGE_NONE) { | 351 | if (target_node->type == N_WORKSPACE && edge == WLR_EDGE_NONE) { |
250 | con = workspace_add_tiling(new_ws, con); | 352 | con = workspace_add_tiling(new_ws, con); |
353 | } else if (e->split_target) { | ||
354 | struct sway_container *target = target_node->sway_container; | ||
355 | enum sway_container_layout layout = container_parent_layout(target); | ||
356 | if (layout != L_TABBED && layout != L_STACKED) { | ||
357 | container_split(target, L_TABBED); | ||
358 | } | ||
359 | container_add_sibling(target, con, e->insert_after_target); | ||
360 | ipc_event_window(con, "move"); | ||
251 | } else if (target_node->type == N_CONTAINER) { | 361 | } else if (target_node->type == N_CONTAINER) { |
252 | // Moving container before/after another | 362 | // Moving container before/after another |
253 | struct sway_container *target = target_node->sway_container; | 363 | struct sway_container *target = target_node->sway_container; |
@@ -283,8 +393,8 @@ static void finalize_move(struct sway_seat *seat) { | |||
283 | int index = list_find(siblings, con); | 393 | int index = list_find(siblings, con); |
284 | struct sway_container *sibling = index == 0 ? | 394 | struct sway_container *sibling = index == 0 ? |
285 | siblings->items[1] : siblings->items[index - 1]; | 395 | siblings->items[1] : siblings->items[index - 1]; |
286 | con->width = sibling->width; | 396 | con->pending.width = sibling->pending.width; |
287 | con->height = sibling->height; | 397 | con->pending.height = sibling->pending.height; |
288 | con->width_fraction = sibling->width_fraction; | 398 | con->width_fraction = sibling->width_fraction; |
289 | con->height_fraction = sibling->height_fraction; | 399 | con->height_fraction = sibling->height_fraction; |
290 | } | 400 | } |
@@ -294,6 +404,7 @@ static void finalize_move(struct sway_seat *seat) { | |||
294 | arrange_workspace(new_ws); | 404 | arrange_workspace(new_ws); |
295 | } | 405 | } |
296 | 406 | ||
407 | transaction_commit_dirty(); | ||
297 | seatop_begin_default(seat); | 408 | seatop_begin_default(seat); |
298 | } | 409 | } |
299 | 410 | ||
@@ -348,6 +459,7 @@ void seatop_begin_move_tiling_threshold(struct sway_seat *seat, | |||
348 | seat->seatop_data = e; | 459 | seat->seatop_data = e; |
349 | 460 | ||
350 | container_raise_floating(con); | 461 | container_raise_floating(con); |
462 | transaction_commit_dirty(); | ||
351 | wlr_seat_pointer_notify_clear_focus(seat->wlr_seat); | 463 | wlr_seat_pointer_notify_clear_focus(seat->wlr_seat); |
352 | } | 464 | } |
353 | 465 | ||
diff --git a/sway/input/seatop_resize_floating.c b/sway/input/seatop_resize_floating.c index 5da22e47..df683026 100644 --- a/sway/input/seatop_resize_floating.c +++ b/sway/input/seatop_resize_floating.c | |||
@@ -2,6 +2,7 @@ | |||
2 | #include <limits.h> | 2 | #include <limits.h> |
3 | #include <wlr/types/wlr_cursor.h> | 3 | #include <wlr/types/wlr_cursor.h> |
4 | #include <wlr/types/wlr_xcursor_manager.h> | 4 | #include <wlr/types/wlr_xcursor_manager.h> |
5 | #include "sway/desktop/transaction.h" | ||
5 | #include "sway/input/cursor.h" | 6 | #include "sway/input/cursor.h" |
6 | #include "sway/input/seat.h" | 7 | #include "sway/input/seat.h" |
7 | #include "sway/tree/arrange.h" | 8 | #include "sway/tree/arrange.h" |
@@ -27,6 +28,7 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec, | |||
27 | if (seat->cursor->pressed_button_count == 0) { | 28 | if (seat->cursor->pressed_button_count == 0) { |
28 | container_set_resizing(con, false); | 29 | container_set_resizing(con, false); |
29 | arrange_container(con); // Send configure w/o resizing hint | 30 | arrange_container(con); // Send configure w/o resizing hint |
31 | transaction_commit_dirty(); | ||
30 | seatop_begin_default(seat); | 32 | seatop_begin_default(seat); |
31 | } | 33 | } |
32 | } | 34 | } |
@@ -78,17 +80,25 @@ static void handle_pointer_motion(struct sway_seat *seat, uint32_t time_msec) { | |||
78 | double height = e->ref_height + grow_height; | 80 | double height = e->ref_height + grow_height; |
79 | int min_width, max_width, min_height, max_height; | 81 | int min_width, max_width, min_height, max_height; |
80 | floating_calculate_constraints(&min_width, &max_width, | 82 | floating_calculate_constraints(&min_width, &max_width, |
81 | &min_height, &max_height); | 83 | &min_height, &max_height); |
82 | width = fmax(min_width + border_width, fmin(width, max_width)); | 84 | width = fmin(width, max_width - border_width); |
83 | height = fmax(min_height + border_height, fmin(height, max_height)); | 85 | width = fmax(width, min_width + border_width); |
86 | width = fmax(width, 1); | ||
87 | height = fmin(height, max_height - border_height); | ||
88 | height = fmax(height, min_height + border_height); | ||
89 | height = fmax(height, 1); | ||
84 | 90 | ||
85 | // Apply the view's min/max size | 91 | // Apply the view's min/max size |
86 | if (con->view) { | 92 | if (con->view) { |
87 | double view_min_width, view_max_width, view_min_height, view_max_height; | 93 | double view_min_width, view_max_width, view_min_height, view_max_height; |
88 | view_get_constraints(con->view, &view_min_width, &view_max_width, | 94 | view_get_constraints(con->view, &view_min_width, &view_max_width, |
89 | &view_min_height, &view_max_height); | 95 | &view_min_height, &view_max_height); |
90 | width = fmax(view_min_width + border_width, fmin(width, view_max_width)); | 96 | width = fmin(width, view_max_width - border_width); |
91 | height = fmax(view_min_height + border_height, fmin(height, view_max_height)); | 97 | width = fmax(width, view_min_width + border_width); |
98 | width = fmax(width, 1); | ||
99 | height = fmin(height, view_max_height - border_height); | ||
100 | height = fmax(height, view_min_height + border_height); | ||
101 | height = fmax(height, 1); | ||
92 | 102 | ||
93 | } | 103 | } |
94 | 104 | ||
@@ -116,23 +126,24 @@ static void handle_pointer_motion(struct sway_seat *seat, uint32_t time_msec) { | |||
116 | 126 | ||
117 | // Determine the amounts we need to bump everything relative to the current | 127 | // Determine the amounts we need to bump everything relative to the current |
118 | // size. | 128 | // size. |
119 | int relative_grow_width = width - con->width; | 129 | int relative_grow_width = width - con->pending.width; |
120 | int relative_grow_height = height - con->height; | 130 | int relative_grow_height = height - con->pending.height; |
121 | int relative_grow_x = (e->ref_con_lx + grow_x) - con->x; | 131 | int relative_grow_x = (e->ref_con_lx + grow_x) - con->pending.x; |
122 | int relative_grow_y = (e->ref_con_ly + grow_y) - con->y; | 132 | int relative_grow_y = (e->ref_con_ly + grow_y) - con->pending.y; |
123 | 133 | ||
124 | // Actually resize stuff | 134 | // Actually resize stuff |
125 | con->x += relative_grow_x; | 135 | con->pending.x += relative_grow_x; |
126 | con->y += relative_grow_y; | 136 | con->pending.y += relative_grow_y; |
127 | con->width += relative_grow_width; | 137 | con->pending.width += relative_grow_width; |
128 | con->height += relative_grow_height; | 138 | con->pending.height += relative_grow_height; |
129 | 139 | ||
130 | con->content_x += relative_grow_x; | 140 | con->pending.content_x += relative_grow_x; |
131 | con->content_y += relative_grow_y; | 141 | con->pending.content_y += relative_grow_y; |
132 | con->content_width += relative_grow_width; | 142 | con->pending.content_width += relative_grow_width; |
133 | con->content_height += relative_grow_height; | 143 | con->pending.content_height += relative_grow_height; |
134 | 144 | ||
135 | arrange_container(con); | 145 | arrange_container(con); |
146 | transaction_commit_dirty(); | ||
136 | } | 147 | } |
137 | 148 | ||
138 | static void handle_unref(struct sway_seat *seat, struct sway_container *con) { | 149 | static void handle_unref(struct sway_seat *seat, struct sway_container *con) { |
@@ -166,16 +177,17 @@ void seatop_begin_resize_floating(struct sway_seat *seat, | |||
166 | e->edge = edge == WLR_EDGE_NONE ? WLR_EDGE_BOTTOM | WLR_EDGE_RIGHT : edge; | 177 | e->edge = edge == WLR_EDGE_NONE ? WLR_EDGE_BOTTOM | WLR_EDGE_RIGHT : edge; |
167 | e->ref_lx = seat->cursor->cursor->x; | 178 | e->ref_lx = seat->cursor->cursor->x; |
168 | e->ref_ly = seat->cursor->cursor->y; | 179 | e->ref_ly = seat->cursor->cursor->y; |
169 | e->ref_con_lx = con->x; | 180 | e->ref_con_lx = con->pending.x; |
170 | e->ref_con_ly = con->y; | 181 | e->ref_con_ly = con->pending.y; |
171 | e->ref_width = con->width; | 182 | e->ref_width = con->pending.width; |
172 | e->ref_height = con->height; | 183 | e->ref_height = con->pending.height; |
173 | 184 | ||
174 | seat->seatop_impl = &seatop_impl; | 185 | seat->seatop_impl = &seatop_impl; |
175 | seat->seatop_data = e; | 186 | seat->seatop_data = e; |
176 | 187 | ||
177 | container_set_resizing(con, true); | 188 | container_set_resizing(con, true); |
178 | container_raise_floating(con); | 189 | container_raise_floating(con); |
190 | transaction_commit_dirty(); | ||
179 | 191 | ||
180 | const char *image = edge == WLR_EDGE_NONE ? | 192 | const char *image = edge == WLR_EDGE_NONE ? |
181 | "se-resize" : wlr_xcursor_get_resize_name(edge); | 193 | "se-resize" : wlr_xcursor_get_resize_name(edge); |
diff --git a/sway/input/seatop_resize_tiling.c b/sway/input/seatop_resize_tiling.c index 2cca805d..869d11b5 100644 --- a/sway/input/seatop_resize_tiling.c +++ b/sway/input/seatop_resize_tiling.c | |||
@@ -2,6 +2,7 @@ | |||
2 | #include <wlr/types/wlr_cursor.h> | 2 | #include <wlr/types/wlr_cursor.h> |
3 | #include <wlr/util/edges.h> | 3 | #include <wlr/util/edges.h> |
4 | #include "sway/commands.h" | 4 | #include "sway/commands.h" |
5 | #include "sway/desktop/transaction.h" | ||
5 | #include "sway/input/cursor.h" | 6 | #include "sway/input/cursor.h" |
6 | #include "sway/input/seat.h" | 7 | #include "sway/input/seat.h" |
7 | #include "sway/tree/arrange.h" | 8 | #include "sway/tree/arrange.h" |
@@ -52,21 +53,22 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec, | |||
52 | if (e->h_con) { | 53 | if (e->h_con) { |
53 | container_set_resizing(e->h_con, false); | 54 | container_set_resizing(e->h_con, false); |
54 | container_set_resizing(e->h_sib, false); | 55 | container_set_resizing(e->h_sib, false); |
55 | if (e->h_con->parent) { | 56 | if (e->h_con->pending.parent) { |
56 | arrange_container(e->h_con->parent); | 57 | arrange_container(e->h_con->pending.parent); |
57 | } else { | 58 | } else { |
58 | arrange_workspace(e->h_con->workspace); | 59 | arrange_workspace(e->h_con->pending.workspace); |
59 | } | 60 | } |
60 | } | 61 | } |
61 | if (e->v_con) { | 62 | if (e->v_con) { |
62 | container_set_resizing(e->v_con, false); | 63 | container_set_resizing(e->v_con, false); |
63 | container_set_resizing(e->v_sib, false); | 64 | container_set_resizing(e->v_sib, false); |
64 | if (e->v_con->parent) { | 65 | if (e->v_con->pending.parent) { |
65 | arrange_container(e->v_con->parent); | 66 | arrange_container(e->v_con->pending.parent); |
66 | } else { | 67 | } else { |
67 | arrange_workspace(e->v_con->workspace); | 68 | arrange_workspace(e->v_con->pending.workspace); |
68 | } | 69 | } |
69 | } | 70 | } |
71 | transaction_commit_dirty(); | ||
70 | seatop_begin_default(seat); | 72 | seatop_begin_default(seat); |
71 | } | 73 | } |
72 | } | 74 | } |
@@ -80,16 +82,16 @@ static void handle_pointer_motion(struct sway_seat *seat, uint32_t time_msec) { | |||
80 | 82 | ||
81 | if (e->h_con) { | 83 | if (e->h_con) { |
82 | if (e->edge & WLR_EDGE_LEFT) { | 84 | if (e->edge & WLR_EDGE_LEFT) { |
83 | amount_x = (e->h_con_orig_width - moved_x) - e->h_con->width; | 85 | amount_x = (e->h_con_orig_width - moved_x) - e->h_con->pending.width; |
84 | } else if (e->edge & WLR_EDGE_RIGHT) { | 86 | } else if (e->edge & WLR_EDGE_RIGHT) { |
85 | amount_x = (e->h_con_orig_width + moved_x) - e->h_con->width; | 87 | amount_x = (e->h_con_orig_width + moved_x) - e->h_con->pending.width; |
86 | } | 88 | } |
87 | } | 89 | } |
88 | if (e->v_con) { | 90 | if (e->v_con) { |
89 | if (e->edge & WLR_EDGE_TOP) { | 91 | if (e->edge & WLR_EDGE_TOP) { |
90 | amount_y = (e->v_con_orig_height - moved_y) - e->v_con->height; | 92 | amount_y = (e->v_con_orig_height - moved_y) - e->v_con->pending.height; |
91 | } else if (e->edge & WLR_EDGE_BOTTOM) { | 93 | } else if (e->edge & WLR_EDGE_BOTTOM) { |
92 | amount_y = (e->v_con_orig_height + moved_y) - e->v_con->height; | 94 | amount_y = (e->v_con_orig_height + moved_y) - e->v_con->pending.height; |
93 | } | 95 | } |
94 | } | 96 | } |
95 | 97 | ||
@@ -99,6 +101,7 @@ static void handle_pointer_motion(struct sway_seat *seat, uint32_t time_msec) { | |||
99 | if (amount_y != 0) { | 101 | if (amount_y != 0) { |
100 | container_resize_tiled(e->v_con, e->edge_y, amount_y); | 102 | container_resize_tiled(e->v_con, e->edge_y, amount_y); |
101 | } | 103 | } |
104 | transaction_commit_dirty(); | ||
102 | } | 105 | } |
103 | 106 | ||
104 | static void handle_unref(struct sway_seat *seat, struct sway_container *con) { | 107 | static void handle_unref(struct sway_seat *seat, struct sway_container *con) { |
@@ -140,7 +143,7 @@ void seatop_begin_resize_tiling(struct sway_seat *seat, | |||
140 | if (e->h_con) { | 143 | if (e->h_con) { |
141 | container_set_resizing(e->h_con, true); | 144 | container_set_resizing(e->h_con, true); |
142 | container_set_resizing(e->h_sib, true); | 145 | container_set_resizing(e->h_sib, true); |
143 | e->h_con_orig_width = e->h_con->width; | 146 | e->h_con_orig_width = e->h_con->pending.width; |
144 | } | 147 | } |
145 | } | 148 | } |
146 | if (edge & (WLR_EDGE_TOP | WLR_EDGE_BOTTOM)) { | 149 | if (edge & (WLR_EDGE_TOP | WLR_EDGE_BOTTOM)) { |
@@ -151,12 +154,13 @@ void seatop_begin_resize_tiling(struct sway_seat *seat, | |||
151 | if (e->v_con) { | 154 | if (e->v_con) { |
152 | container_set_resizing(e->v_con, true); | 155 | container_set_resizing(e->v_con, true); |
153 | container_set_resizing(e->v_sib, true); | 156 | container_set_resizing(e->v_sib, true); |
154 | e->v_con_orig_height = e->v_con->height; | 157 | e->v_con_orig_height = e->v_con->pending.height; |
155 | } | 158 | } |
156 | } | 159 | } |
157 | 160 | ||
158 | seat->seatop_impl = &seatop_impl; | 161 | seat->seatop_impl = &seatop_impl; |
159 | seat->seatop_data = e; | 162 | seat->seatop_data = e; |
160 | 163 | ||
164 | transaction_commit_dirty(); | ||
161 | wlr_seat_pointer_notify_clear_focus(seat->wlr_seat); | 165 | wlr_seat_pointer_notify_clear_focus(seat->wlr_seat); |
162 | } | 166 | } |
diff --git a/sway/input/switch.c b/sway/input/switch.c index b7c28df1..fc7dfaff 100644 --- a/sway/input/switch.c +++ b/sway/input/switch.c | |||
@@ -1,5 +1,4 @@ | |||
1 | #include "sway/config.h" | 1 | #include "sway/config.h" |
2 | #include "sway/desktop/transaction.h" | ||
3 | #include "sway/input/switch.h" | 2 | #include "sway/input/switch.h" |
4 | #include <wlr/types/wlr_idle.h> | 3 | #include <wlr/types/wlr_idle.h> |
5 | #include "log.h" | 4 | #include "log.h" |
@@ -12,6 +11,7 @@ struct sway_switch *sway_switch_create(struct sway_seat *seat, | |||
12 | return NULL; | 11 | return NULL; |
13 | } | 12 | } |
14 | device->switch_device = switch_device; | 13 | device->switch_device = switch_device; |
14 | switch_device->wlr = wlr_switch_from_input_device(device->input_device->wlr_device); | ||
15 | switch_device->seat_device = device; | 15 | switch_device->seat_device = device; |
16 | switch_device->state = WLR_SWITCH_STATE_OFF; | 16 | switch_device->state = WLR_SWITCH_STATE_OFF; |
17 | wl_list_init(&switch_device->switch_toggle.link); | 17 | wl_list_init(&switch_device->switch_toggle.link); |
@@ -20,9 +20,23 @@ struct sway_switch *sway_switch_create(struct sway_seat *seat, | |||
20 | return switch_device; | 20 | return switch_device; |
21 | } | 21 | } |
22 | 22 | ||
23 | static bool sway_switch_trigger_test(enum sway_switch_trigger trigger, | ||
24 | enum wlr_switch_state state) { | ||
25 | switch (trigger) { | ||
26 | case SWAY_SWITCH_TRIGGER_ON: | ||
27 | return state == WLR_SWITCH_STATE_ON; | ||
28 | case SWAY_SWITCH_TRIGGER_OFF: | ||
29 | return state == WLR_SWITCH_STATE_OFF; | ||
30 | case SWAY_SWITCH_TRIGGER_TOGGLE: | ||
31 | return true; | ||
32 | } | ||
33 | abort(); // unreachable | ||
34 | } | ||
35 | |||
23 | static void execute_binding(struct sway_switch *sway_switch) { | 36 | static void execute_binding(struct sway_switch *sway_switch) { |
24 | struct sway_seat* seat = sway_switch->seat_device->sway_seat; | 37 | struct sway_seat* seat = sway_switch->seat_device->sway_seat; |
25 | bool input_inhibited = seat->exclusive_client != NULL; | 38 | bool input_inhibited = seat->exclusive_client != NULL || |
39 | server.session_lock.locked; | ||
26 | 40 | ||
27 | list_t *bindings = config->current_mode->switch_bindings; | 41 | list_t *bindings = config->current_mode->switch_bindings; |
28 | struct sway_switch_binding *matched_binding = NULL; | 42 | struct sway_switch_binding *matched_binding = NULL; |
@@ -31,11 +45,10 @@ static void execute_binding(struct sway_switch *sway_switch) { | |||
31 | if (binding->type != sway_switch->type) { | 45 | if (binding->type != sway_switch->type) { |
32 | continue; | 46 | continue; |
33 | } | 47 | } |
34 | if (binding->state != WLR_SWITCH_STATE_TOGGLE && | 48 | if (!sway_switch_trigger_test(binding->trigger, sway_switch->state)) { |
35 | binding->state != sway_switch->state) { | ||
36 | continue; | 49 | continue; |
37 | } | 50 | } |
38 | if (config->reloading && (binding->state == WLR_SWITCH_STATE_TOGGLE | 51 | if (config->reloading && (binding->trigger == SWAY_SWITCH_TRIGGER_TOGGLE |
39 | || (binding->flags & BINDING_RELOAD) == 0)) { | 52 | || (binding->flags & BINDING_RELOAD) == 0)) { |
40 | continue; | 53 | continue; |
41 | } | 54 | } |
@@ -61,15 +74,12 @@ static void execute_binding(struct sway_switch *sway_switch) { | |||
61 | seat_execute_command(seat, dummy_binding); | 74 | seat_execute_command(seat, dummy_binding); |
62 | free(dummy_binding); | 75 | free(dummy_binding); |
63 | } | 76 | } |
64 | |||
65 | transaction_commit_dirty(); | ||
66 | |||
67 | } | 77 | } |
68 | 78 | ||
69 | static void handle_switch_toggle(struct wl_listener *listener, void *data) { | 79 | static void handle_switch_toggle(struct wl_listener *listener, void *data) { |
70 | struct sway_switch *sway_switch = | 80 | struct sway_switch *sway_switch = |
71 | wl_container_of(listener, sway_switch, switch_toggle); | 81 | wl_container_of(listener, sway_switch, switch_toggle); |
72 | struct wlr_event_switch_toggle *event = data; | 82 | struct wlr_switch_toggle_event *event = data; |
73 | struct sway_seat *seat = sway_switch->seat_device->sway_seat; | 83 | struct sway_seat *seat = sway_switch->seat_device->sway_seat; |
74 | seat_idle_notify_activity(seat, IDLE_SOURCE_SWITCH); | 84 | seat_idle_notify_activity(seat, IDLE_SOURCE_SWITCH); |
75 | 85 | ||
@@ -86,10 +96,8 @@ static void handle_switch_toggle(struct wl_listener *listener, void *data) { | |||
86 | } | 96 | } |
87 | 97 | ||
88 | void sway_switch_configure(struct sway_switch *sway_switch) { | 98 | void sway_switch_configure(struct sway_switch *sway_switch) { |
89 | struct wlr_input_device *wlr_device = | ||
90 | sway_switch->seat_device->input_device->wlr_device; | ||
91 | wl_list_remove(&sway_switch->switch_toggle.link); | 99 | wl_list_remove(&sway_switch->switch_toggle.link); |
92 | wl_signal_add(&wlr_device->switch_device->events.toggle, | 100 | wl_signal_add(&sway_switch->wlr->events.toggle, |
93 | &sway_switch->switch_toggle); | 101 | &sway_switch->switch_toggle); |
94 | sway_switch->switch_toggle.notify = handle_switch_toggle; | 102 | sway_switch->switch_toggle.notify = handle_switch_toggle; |
95 | sway_log(SWAY_DEBUG, "Configured switch for device"); | 103 | sway_log(SWAY_DEBUG, "Configured switch for device"); |
diff --git a/sway/input/tablet.c b/sway/input/tablet.c index 26e86e36..92ede3fa 100644 --- a/sway/input/tablet.c +++ b/sway/input/tablet.c | |||
@@ -196,7 +196,7 @@ static void handle_tablet_pad_attach(struct wl_listener *listener, | |||
196 | 196 | ||
197 | static void handle_tablet_pad_ring(struct wl_listener *listener, void *data) { | 197 | static void handle_tablet_pad_ring(struct wl_listener *listener, void *data) { |
198 | struct sway_tablet_pad *pad = wl_container_of(listener, pad, ring); | 198 | struct sway_tablet_pad *pad = wl_container_of(listener, pad, ring); |
199 | struct wlr_event_tablet_pad_ring *event = data; | 199 | struct wlr_tablet_pad_ring_event *event = data; |
200 | 200 | ||
201 | if (!pad->current_surface) { | 201 | if (!pad->current_surface) { |
202 | return; | 202 | return; |
@@ -210,7 +210,7 @@ static void handle_tablet_pad_ring(struct wl_listener *listener, void *data) { | |||
210 | 210 | ||
211 | static void handle_tablet_pad_strip(struct wl_listener *listener, void *data) { | 211 | static void handle_tablet_pad_strip(struct wl_listener *listener, void *data) { |
212 | struct sway_tablet_pad *pad = wl_container_of(listener, pad, strip); | 212 | struct sway_tablet_pad *pad = wl_container_of(listener, pad, strip); |
213 | struct wlr_event_tablet_pad_strip *event = data; | 213 | struct wlr_tablet_pad_strip_event *event = data; |
214 | 214 | ||
215 | if (!pad->current_surface) { | 215 | if (!pad->current_surface) { |
216 | return; | 216 | return; |
@@ -224,7 +224,7 @@ static void handle_tablet_pad_strip(struct wl_listener *listener, void *data) { | |||
224 | 224 | ||
225 | static void handle_tablet_pad_button(struct wl_listener *listener, void *data) { | 225 | static void handle_tablet_pad_button(struct wl_listener *listener, void *data) { |
226 | struct sway_tablet_pad *pad = wl_container_of(listener, pad, button); | 226 | struct sway_tablet_pad *pad = wl_container_of(listener, pad, button); |
227 | struct wlr_event_tablet_pad_button *event = data; | 227 | struct wlr_tablet_pad_button_event *event = data; |
228 | 228 | ||
229 | if (!pad->current_surface) { | 229 | if (!pad->current_surface) { |
230 | return; | 230 | return; |
@@ -246,6 +246,7 @@ struct sway_tablet_pad *sway_tablet_pad_create(struct sway_seat *seat, | |||
246 | return NULL; | 246 | return NULL; |
247 | } | 247 | } |
248 | 248 | ||
249 | tablet_pad->wlr = wlr_tablet_pad_from_input_device(device->input_device->wlr_device); | ||
249 | tablet_pad->seat_device = device; | 250 | tablet_pad->seat_device = device; |
250 | wl_list_init(&tablet_pad->attach.link); | 251 | wl_list_init(&tablet_pad->attach.link); |
251 | wl_list_init(&tablet_pad->button.link); | 252 | wl_list_init(&tablet_pad->button.link); |
@@ -260,40 +261,40 @@ struct sway_tablet_pad *sway_tablet_pad_create(struct sway_seat *seat, | |||
260 | } | 261 | } |
261 | 262 | ||
262 | void sway_configure_tablet_pad(struct sway_tablet_pad *tablet_pad) { | 263 | void sway_configure_tablet_pad(struct sway_tablet_pad *tablet_pad) { |
263 | struct wlr_input_device *device = | 264 | struct wlr_input_device *wlr_device = |
264 | tablet_pad->seat_device->input_device->wlr_device; | 265 | tablet_pad->seat_device->input_device->wlr_device; |
265 | struct sway_seat *seat = tablet_pad->seat_device->sway_seat; | 266 | struct sway_seat *seat = tablet_pad->seat_device->sway_seat; |
266 | 267 | ||
267 | if (!tablet_pad->tablet_v2_pad) { | 268 | if (!tablet_pad->tablet_v2_pad) { |
268 | tablet_pad->tablet_v2_pad = | 269 | tablet_pad->tablet_v2_pad = |
269 | wlr_tablet_pad_create(server.tablet_v2, seat->wlr_seat, device); | 270 | wlr_tablet_pad_create(server.tablet_v2, seat->wlr_seat, wlr_device); |
270 | } | 271 | } |
271 | 272 | ||
272 | wl_list_remove(&tablet_pad->attach.link); | 273 | wl_list_remove(&tablet_pad->attach.link); |
273 | tablet_pad->attach.notify = handle_tablet_pad_attach; | 274 | tablet_pad->attach.notify = handle_tablet_pad_attach; |
274 | wl_signal_add(&device->tablet_pad->events.attach_tablet, | 275 | wl_signal_add(&tablet_pad->wlr->events.attach_tablet, |
275 | &tablet_pad->attach); | 276 | &tablet_pad->attach); |
276 | 277 | ||
277 | wl_list_remove(&tablet_pad->button.link); | 278 | wl_list_remove(&tablet_pad->button.link); |
278 | tablet_pad->button.notify = handle_tablet_pad_button; | 279 | tablet_pad->button.notify = handle_tablet_pad_button; |
279 | wl_signal_add(&device->tablet_pad->events.button, &tablet_pad->button); | 280 | wl_signal_add(&tablet_pad->wlr->events.button, &tablet_pad->button); |
280 | 281 | ||
281 | wl_list_remove(&tablet_pad->strip.link); | 282 | wl_list_remove(&tablet_pad->strip.link); |
282 | tablet_pad->strip.notify = handle_tablet_pad_strip; | 283 | tablet_pad->strip.notify = handle_tablet_pad_strip; |
283 | wl_signal_add(&device->tablet_pad->events.strip, &tablet_pad->strip); | 284 | wl_signal_add(&tablet_pad->wlr->events.strip, &tablet_pad->strip); |
284 | 285 | ||
285 | wl_list_remove(&tablet_pad->ring.link); | 286 | wl_list_remove(&tablet_pad->ring.link); |
286 | tablet_pad->ring.notify = handle_tablet_pad_ring; | 287 | tablet_pad->ring.notify = handle_tablet_pad_ring; |
287 | wl_signal_add(&device->tablet_pad->events.ring, &tablet_pad->ring); | 288 | wl_signal_add(&tablet_pad->wlr->events.ring, &tablet_pad->ring); |
288 | 289 | ||
289 | /* Search for a sibling tablet */ | 290 | /* Search for a sibling tablet */ |
290 | if (!wlr_input_device_is_libinput(device)) { | 291 | if (!wlr_input_device_is_libinput(wlr_device)) { |
291 | /* We can only do this on libinput devices */ | 292 | /* We can only do this on libinput devices */ |
292 | return; | 293 | return; |
293 | } | 294 | } |
294 | 295 | ||
295 | struct libinput_device_group *group = | 296 | struct libinput_device_group *group = |
296 | libinput_device_get_device_group(wlr_libinput_get_device_handle(device)); | 297 | libinput_device_get_device_group(wlr_libinput_get_device_handle(wlr_device)); |
297 | struct sway_tablet *tool; | 298 | struct sway_tablet *tool; |
298 | wl_list_for_each(tool, &seat->cursor->tablets, link) { | 299 | wl_list_for_each(tool, &seat->cursor->tablets, link) { |
299 | struct wlr_input_device *tablet = | 300 | struct wlr_input_device *tablet = |
diff --git a/sway/input/text_input.c b/sway/input/text_input.c index f83726ee..58911c2d 100644 --- a/sway/input/text_input.c +++ b/sway/input/text_input.c | |||
@@ -55,6 +55,35 @@ static void handle_im_commit(struct wl_listener *listener, void *data) { | |||
55 | wlr_text_input_v3_send_done(text_input->input); | 55 | wlr_text_input_v3_send_done(text_input->input); |
56 | } | 56 | } |
57 | 57 | ||
58 | static void handle_im_keyboard_grab_destroy(struct wl_listener *listener, void *data) { | ||
59 | struct sway_input_method_relay *relay = wl_container_of(listener, relay, | ||
60 | input_method_keyboard_grab_destroy); | ||
61 | struct wlr_input_method_keyboard_grab_v2 *keyboard_grab = data; | ||
62 | wl_list_remove(&relay->input_method_keyboard_grab_destroy.link); | ||
63 | |||
64 | if (keyboard_grab->keyboard) { | ||
65 | // send modifier state to original client | ||
66 | wlr_seat_keyboard_notify_modifiers(keyboard_grab->input_method->seat, | ||
67 | &keyboard_grab->keyboard->modifiers); | ||
68 | } | ||
69 | } | ||
70 | |||
71 | static void handle_im_grab_keyboard(struct wl_listener *listener, void *data) { | ||
72 | struct sway_input_method_relay *relay = wl_container_of(listener, relay, | ||
73 | input_method_grab_keyboard); | ||
74 | struct wlr_input_method_keyboard_grab_v2 *keyboard_grab = data; | ||
75 | |||
76 | // send modifier state to grab | ||
77 | struct wlr_keyboard *active_keyboard = wlr_seat_get_keyboard(relay->seat->wlr_seat); | ||
78 | wlr_input_method_keyboard_grab_v2_set_keyboard(keyboard_grab, | ||
79 | active_keyboard); | ||
80 | |||
81 | wl_signal_add(&keyboard_grab->events.destroy, | ||
82 | &relay->input_method_keyboard_grab_destroy); | ||
83 | relay->input_method_keyboard_grab_destroy.notify = | ||
84 | handle_im_keyboard_grab_destroy; | ||
85 | } | ||
86 | |||
58 | static void text_input_set_pending_focused_surface( | 87 | static void text_input_set_pending_focused_surface( |
59 | struct sway_text_input *text_input, struct wlr_surface *surface) { | 88 | struct sway_text_input *text_input, struct wlr_surface *surface) { |
60 | wl_list_remove(&text_input->pending_focused_surface_destroy.link); | 89 | wl_list_remove(&text_input->pending_focused_surface_destroy.link); |
@@ -92,13 +121,18 @@ static void relay_send_im_state(struct sway_input_method_relay *relay, | |||
92 | return; | 121 | return; |
93 | } | 122 | } |
94 | // TODO: only send each of those if they were modified | 123 | // TODO: only send each of those if they were modified |
95 | wlr_input_method_v2_send_surrounding_text(input_method, | 124 | if (input->active_features & WLR_TEXT_INPUT_V3_FEATURE_SURROUNDING_TEXT) { |
96 | input->current.surrounding.text, input->current.surrounding.cursor, | 125 | wlr_input_method_v2_send_surrounding_text(input_method, |
97 | input->current.surrounding.anchor); | 126 | input->current.surrounding.text, input->current.surrounding.cursor, |
127 | input->current.surrounding.anchor); | ||
128 | } | ||
98 | wlr_input_method_v2_send_text_change_cause(input_method, | 129 | wlr_input_method_v2_send_text_change_cause(input_method, |
99 | input->current.text_change_cause); | 130 | input->current.text_change_cause); |
100 | wlr_input_method_v2_send_content_type(input_method, | 131 | if (input->active_features & WLR_TEXT_INPUT_V3_FEATURE_CONTENT_TYPE) { |
101 | input->current.content_type.hint, input->current.content_type.purpose); | 132 | wlr_input_method_v2_send_content_type(input_method, |
133 | input->current.content_type.hint, | ||
134 | input->current.content_type.purpose); | ||
135 | } | ||
102 | wlr_input_method_v2_send_done(input_method); | 136 | wlr_input_method_v2_send_done(input_method); |
103 | // TODO: pass intent, display popup size | 137 | // TODO: pass intent, display popup size |
104 | } | 138 | } |
@@ -144,6 +178,10 @@ static void handle_text_input_disable(struct wl_listener *listener, | |||
144 | void *data) { | 178 | void *data) { |
145 | struct sway_text_input *text_input = wl_container_of(listener, text_input, | 179 | struct sway_text_input *text_input = wl_container_of(listener, text_input, |
146 | text_input_disable); | 180 | text_input_disable); |
181 | if (text_input->input->focused_surface == NULL) { | ||
182 | sway_log(SWAY_DEBUG, "Disabling text input, but no longer focused"); | ||
183 | return; | ||
184 | } | ||
147 | relay_disable_text_input(text_input->relay, text_input); | 185 | relay_disable_text_input(text_input->relay, text_input); |
148 | } | 186 | } |
149 | 187 | ||
@@ -236,6 +274,9 @@ static void relay_handle_input_method(struct wl_listener *listener, | |||
236 | wl_signal_add(&relay->input_method->events.commit, | 274 | wl_signal_add(&relay->input_method->events.commit, |
237 | &relay->input_method_commit); | 275 | &relay->input_method_commit); |
238 | relay->input_method_commit.notify = handle_im_commit; | 276 | relay->input_method_commit.notify = handle_im_commit; |
277 | wl_signal_add(&relay->input_method->events.grab_keyboard, | ||
278 | &relay->input_method_grab_keyboard); | ||
279 | relay->input_method_grab_keyboard.notify = handle_im_grab_keyboard; | ||
239 | wl_signal_add(&relay->input_method->events.destroy, | 280 | wl_signal_add(&relay->input_method->events.destroy, |
240 | &relay->input_method_destroy); | 281 | &relay->input_method_destroy); |
241 | relay->input_method_destroy.notify = handle_im_destroy; | 282 | relay->input_method_destroy.notify = handle_im_destroy; |
diff --git a/sway/ipc-json.c b/sway/ipc-json.c index fceee84d..e422b24d 100644 --- a/sway/ipc-json.c +++ b/sway/ipc-json.c | |||
@@ -1,7 +1,11 @@ | |||
1 | #include <ctype.h> | ||
2 | #include <float.h> | ||
1 | #include <json.h> | 3 | #include <json.h> |
2 | #include <libevdev/libevdev.h> | 4 | #include <libevdev/libevdev.h> |
3 | #include <stdio.h> | 5 | #include <stdio.h> |
4 | #include <ctype.h> | 6 | #include <wlr/backend/libinput.h> |
7 | #include <wlr/types/wlr_output.h> | ||
8 | #include <xkbcommon/xkbcommon.h> | ||
5 | #include "config.h" | 9 | #include "config.h" |
6 | #include "log.h" | 10 | #include "log.h" |
7 | #include "sway/config.h" | 11 | #include "sway/config.h" |
@@ -13,16 +17,26 @@ | |||
13 | #include "sway/input/input-manager.h" | 17 | #include "sway/input/input-manager.h" |
14 | #include "sway/input/cursor.h" | 18 | #include "sway/input/cursor.h" |
15 | #include "sway/input/seat.h" | 19 | #include "sway/input/seat.h" |
16 | #include <wlr/backend/libinput.h> | ||
17 | #include <wlr/types/wlr_box.h> | ||
18 | #include <wlr/types/wlr_output.h> | ||
19 | #include <xkbcommon/xkbcommon.h> | ||
20 | #include "wlr-layer-shell-unstable-v1-protocol.h" | 20 | #include "wlr-layer-shell-unstable-v1-protocol.h" |
21 | #include "sway/desktop/idle_inhibit_v1.h" | 21 | #include "sway/desktop/idle_inhibit_v1.h" |
22 | 22 | ||
23 | static const int i3_output_id = INT32_MAX; | 23 | static const int i3_output_id = INT32_MAX; |
24 | static const int i3_scratch_id = INT32_MAX - 1; | 24 | static const int i3_scratch_id = INT32_MAX - 1; |
25 | 25 | ||
26 | static const char *ipc_json_node_type_description(enum sway_node_type node_type) { | ||
27 | switch (node_type) { | ||
28 | case N_ROOT: | ||
29 | return "root"; | ||
30 | case N_OUTPUT: | ||
31 | return "output"; | ||
32 | case N_WORKSPACE: | ||
33 | return "workspace"; | ||
34 | case N_CONTAINER: | ||
35 | return "con"; | ||
36 | } | ||
37 | return "none"; | ||
38 | } | ||
39 | |||
26 | static const char *ipc_json_layout_description(enum sway_container_layout l) { | 40 | static const char *ipc_json_layout_description(enum sway_container_layout l) { |
27 | switch (l) { | 41 | switch (l) { |
28 | case L_VERT: | 42 | case L_VERT: |
@@ -189,16 +203,22 @@ static json_object *ipc_json_create_empty_rect(void) { | |||
189 | return ipc_json_create_rect(&empty); | 203 | return ipc_json_create_rect(&empty); |
190 | } | 204 | } |
191 | 205 | ||
192 | static json_object *ipc_json_create_node(int id, char *name, | 206 | static json_object *ipc_json_create_node(int id, const char* type, char *name, |
193 | bool focused, json_object *focus, struct wlr_box *box) { | 207 | bool focused, json_object *focus, struct wlr_box *box) { |
194 | json_object *object = json_object_new_object(); | 208 | json_object *object = json_object_new_object(); |
195 | 209 | ||
196 | json_object_object_add(object, "id", json_object_new_int(id)); | 210 | json_object_object_add(object, "id", json_object_new_int(id)); |
197 | json_object_object_add(object, "name", | 211 | json_object_object_add(object, "type", json_object_new_string(type)); |
198 | name ? json_object_new_string(name) : NULL); | 212 | json_object_object_add(object, "orientation", |
199 | json_object_object_add(object, "rect", ipc_json_create_rect(box)); | 213 | json_object_new_string( |
214 | ipc_json_orientation_description(L_HORIZ))); | ||
215 | json_object_object_add(object, "percent", NULL); | ||
216 | json_object_object_add(object, "urgent", json_object_new_boolean(false)); | ||
217 | json_object_object_add(object, "marks", json_object_new_array()); | ||
200 | json_object_object_add(object, "focused", json_object_new_boolean(focused)); | 218 | json_object_object_add(object, "focused", json_object_new_boolean(focused)); |
201 | json_object_object_add(object, "focus", focus); | 219 | json_object_object_add(object, "layout", |
220 | json_object_new_string( | ||
221 | ipc_json_layout_description(L_HORIZ))); | ||
202 | 222 | ||
203 | // set default values to be compatible with i3 | 223 | // set default values to be compatible with i3 |
204 | json_object_object_add(object, "border", | 224 | json_object_object_add(object, "border", |
@@ -206,49 +226,63 @@ static json_object *ipc_json_create_node(int id, char *name, | |||
206 | ipc_json_border_description(B_NONE))); | 226 | ipc_json_border_description(B_NONE))); |
207 | json_object_object_add(object, "current_border_width", | 227 | json_object_object_add(object, "current_border_width", |
208 | json_object_new_int(0)); | 228 | json_object_new_int(0)); |
209 | json_object_object_add(object, "layout", | 229 | json_object_object_add(object, "rect", ipc_json_create_rect(box)); |
210 | json_object_new_string( | ||
211 | ipc_json_layout_description(L_HORIZ))); | ||
212 | json_object_object_add(object, "orientation", | ||
213 | json_object_new_string( | ||
214 | ipc_json_orientation_description(L_HORIZ))); | ||
215 | json_object_object_add(object, "percent", NULL); | ||
216 | json_object_object_add(object, "window_rect", ipc_json_create_empty_rect()); | ||
217 | json_object_object_add(object, "deco_rect", ipc_json_create_empty_rect()); | 230 | json_object_object_add(object, "deco_rect", ipc_json_create_empty_rect()); |
231 | json_object_object_add(object, "window_rect", ipc_json_create_empty_rect()); | ||
218 | json_object_object_add(object, "geometry", ipc_json_create_empty_rect()); | 232 | json_object_object_add(object, "geometry", ipc_json_create_empty_rect()); |
233 | json_object_object_add(object, "name", | ||
234 | name ? json_object_new_string(name) : NULL); | ||
219 | json_object_object_add(object, "window", NULL); | 235 | json_object_object_add(object, "window", NULL); |
220 | json_object_object_add(object, "urgent", json_object_new_boolean(false)); | ||
221 | json_object_object_add(object, "marks", json_object_new_array()); | ||
222 | json_object_object_add(object, "fullscreen_mode", json_object_new_int(0)); | ||
223 | json_object_object_add(object, "nodes", json_object_new_array()); | 236 | json_object_object_add(object, "nodes", json_object_new_array()); |
224 | json_object_object_add(object, "floating_nodes", json_object_new_array()); | 237 | json_object_object_add(object, "floating_nodes", json_object_new_array()); |
238 | json_object_object_add(object, "focus", focus); | ||
239 | json_object_object_add(object, "fullscreen_mode", json_object_new_int(0)); | ||
225 | json_object_object_add(object, "sticky", json_object_new_boolean(false)); | 240 | json_object_object_add(object, "sticky", json_object_new_boolean(false)); |
226 | 241 | ||
227 | return object; | 242 | return object; |
228 | } | 243 | } |
229 | 244 | ||
230 | static void ipc_json_describe_root(struct sway_root *root, json_object *object) { | 245 | static void ipc_json_describe_output(struct sway_output *output, |
231 | json_object_object_add(object, "type", json_object_new_string("root")); | 246 | json_object *object) { |
247 | struct wlr_output *wlr_output = output->wlr_output; | ||
248 | |||
249 | json_object_object_add(object, "primary", json_object_new_boolean(false)); | ||
250 | json_object_object_add(object, "make", | ||
251 | json_object_new_string(wlr_output->make ? wlr_output->make : "Unknown")); | ||
252 | json_object_object_add(object, "model", | ||
253 | json_object_new_string(wlr_output->model ? wlr_output->model : "Unknown")); | ||
254 | json_object_object_add(object, "serial", | ||
255 | json_object_new_string(wlr_output->serial ? wlr_output->serial : "Unknown")); | ||
256 | |||
257 | json_object *modes_array = json_object_new_array(); | ||
258 | struct wlr_output_mode *mode; | ||
259 | wl_list_for_each(mode, &wlr_output->modes, link) { | ||
260 | json_object *mode_object = json_object_new_object(); | ||
261 | json_object_object_add(mode_object, "width", | ||
262 | json_object_new_int(mode->width)); | ||
263 | json_object_object_add(mode_object, "height", | ||
264 | json_object_new_int(mode->height)); | ||
265 | json_object_object_add(mode_object, "refresh", | ||
266 | json_object_new_int(mode->refresh)); | ||
267 | json_object_array_add(modes_array, mode_object); | ||
268 | } | ||
269 | json_object_object_add(object, "modes", modes_array); | ||
232 | } | 270 | } |
233 | 271 | ||
234 | static void ipc_json_describe_output(struct sway_output *output, | 272 | static void ipc_json_describe_enabled_output(struct sway_output *output, |
235 | json_object *object) { | 273 | json_object *object) { |
274 | ipc_json_describe_output(output, object); | ||
275 | |||
236 | struct wlr_output *wlr_output = output->wlr_output; | 276 | struct wlr_output *wlr_output = output->wlr_output; |
237 | json_object_object_add(object, "type", json_object_new_string("output")); | ||
238 | json_object_object_add(object, "active", json_object_new_boolean(true)); | 277 | json_object_object_add(object, "active", json_object_new_boolean(true)); |
239 | json_object_object_add(object, "dpms", | 278 | json_object_object_add(object, "dpms", |
240 | json_object_new_boolean(wlr_output->enabled)); | 279 | json_object_new_boolean(wlr_output->enabled)); |
241 | json_object_object_add(object, "primary", json_object_new_boolean(false)); | 280 | json_object_object_add(object, "power", |
281 | json_object_new_boolean(wlr_output->enabled)); | ||
242 | json_object_object_add(object, "layout", json_object_new_string("output")); | 282 | json_object_object_add(object, "layout", json_object_new_string("output")); |
243 | json_object_object_add(object, "orientation", | 283 | json_object_object_add(object, "orientation", |
244 | json_object_new_string( | 284 | json_object_new_string( |
245 | ipc_json_orientation_description(L_NONE))); | 285 | ipc_json_orientation_description(L_NONE))); |
246 | json_object_object_add(object, "make", | ||
247 | json_object_new_string(wlr_output->make)); | ||
248 | json_object_object_add(object, "model", | ||
249 | json_object_new_string(wlr_output->model)); | ||
250 | json_object_object_add(object, "serial", | ||
251 | json_object_new_string(wlr_output->serial)); | ||
252 | json_object_object_add(object, "scale", | 286 | json_object_object_add(object, "scale", |
253 | json_object_new_double(wlr_output->scale)); | 287 | json_object_new_double(wlr_output->scale)); |
254 | json_object_object_add(object, "scale_filter", | 288 | json_object_object_add(object, "scale_filter", |
@@ -315,33 +349,14 @@ json_object *ipc_json_describe_disabled_output(struct sway_output *output) { | |||
315 | 349 | ||
316 | json_object *object = json_object_new_object(); | 350 | json_object *object = json_object_new_object(); |
317 | 351 | ||
352 | ipc_json_describe_output(output, object); | ||
353 | |||
318 | json_object_object_add(object, "type", json_object_new_string("output")); | 354 | json_object_object_add(object, "type", json_object_new_string("output")); |
319 | json_object_object_add(object, "name", | 355 | json_object_object_add(object, "name", |
320 | json_object_new_string(wlr_output->name)); | 356 | json_object_new_string(wlr_output->name)); |
321 | json_object_object_add(object, "active", json_object_new_boolean(false)); | 357 | json_object_object_add(object, "active", json_object_new_boolean(false)); |
322 | json_object_object_add(object, "dpms", json_object_new_boolean(false)); | 358 | json_object_object_add(object, "dpms", json_object_new_boolean(false)); |
323 | json_object_object_add(object, "primary", json_object_new_boolean(false)); | 359 | json_object_object_add(object, "power", json_object_new_boolean(false)); |
324 | json_object_object_add(object, "make", | ||
325 | json_object_new_string(wlr_output->make)); | ||
326 | json_object_object_add(object, "model", | ||
327 | json_object_new_string(wlr_output->model)); | ||
328 | json_object_object_add(object, "serial", | ||
329 | json_object_new_string(wlr_output->serial)); | ||
330 | |||
331 | json_object *modes_array = json_object_new_array(); | ||
332 | struct wlr_output_mode *mode; | ||
333 | wl_list_for_each(mode, &wlr_output->modes, link) { | ||
334 | json_object *mode_object = json_object_new_object(); | ||
335 | json_object_object_add(mode_object, "width", | ||
336 | json_object_new_int(mode->width)); | ||
337 | json_object_object_add(mode_object, "height", | ||
338 | json_object_new_int(mode->height)); | ||
339 | json_object_object_add(mode_object, "refresh", | ||
340 | json_object_new_int(mode->refresh)); | ||
341 | json_object_array_add(modes_array, mode_object); | ||
342 | } | ||
343 | |||
344 | json_object_object_add(object, "modes", modes_array); | ||
345 | 360 | ||
346 | json_object_object_add(object, "current_workspace", NULL); | 361 | json_object_object_add(object, "current_workspace", NULL); |
347 | 362 | ||
@@ -369,11 +384,9 @@ static json_object *ipc_json_describe_scratchpad_output(void) { | |||
369 | json_object_new_int(container->node.id)); | 384 | json_object_new_int(container->node.id)); |
370 | } | 385 | } |
371 | 386 | ||
372 | json_object *workspace = ipc_json_create_node(i3_scratch_id, | 387 | json_object *workspace = ipc_json_create_node(i3_scratch_id, "workspace", |
373 | "__i3_scratch", false, workspace_focus, &box); | 388 | "__i3_scratch", false, workspace_focus, &box); |
374 | json_object_object_add(workspace, "fullscreen_mode", json_object_new_int(1)); | 389 | json_object_object_add(workspace, "fullscreen_mode", json_object_new_int(1)); |
375 | json_object_object_add(workspace, "type", | ||
376 | json_object_new_string("workspace")); | ||
377 | 390 | ||
378 | // List all hidden scratchpad containers as floating nodes | 391 | // List all hidden scratchpad containers as floating nodes |
379 | json_object *floating_array = json_object_new_array(); | 392 | json_object *floating_array = json_object_new_array(); |
@@ -390,10 +403,8 @@ static json_object *ipc_json_describe_scratchpad_output(void) { | |||
390 | json_object *output_focus = json_object_new_array(); | 403 | json_object *output_focus = json_object_new_array(); |
391 | json_object_array_add(output_focus, json_object_new_int(i3_scratch_id)); | 404 | json_object_array_add(output_focus, json_object_new_int(i3_scratch_id)); |
392 | 405 | ||
393 | json_object *output = ipc_json_create_node(i3_output_id, | 406 | json_object *output = ipc_json_create_node(i3_output_id, "output", |
394 | "__i3", false, output_focus, &box); | 407 | "__i3", false, output_focus, &box); |
395 | json_object_object_add(output, "type", | ||
396 | json_object_new_string("output")); | ||
397 | json_object_object_add(output, "layout", | 408 | json_object_object_add(output, "layout", |
398 | json_object_new_string("output")); | 409 | json_object_new_string("output")); |
399 | 410 | ||
@@ -423,7 +434,6 @@ static void ipc_json_describe_workspace(struct sway_workspace *workspace, | |||
423 | json_object_object_add(object, "fullscreen_mode", json_object_new_int(1)); | 434 | json_object_object_add(object, "fullscreen_mode", json_object_new_int(1)); |
424 | json_object_object_add(object, "output", workspace->output ? | 435 | json_object_object_add(object, "output", workspace->output ? |
425 | json_object_new_string(workspace->output->wlr_output->name) : NULL); | 436 | json_object_new_string(workspace->output->wlr_output->name) : NULL); |
426 | json_object_object_add(object, "type", json_object_new_string("workspace")); | ||
427 | json_object_object_add(object, "urgent", | 437 | json_object_object_add(object, "urgent", |
428 | json_object_new_boolean(workspace->urgent)); | 438 | json_object_new_boolean(workspace->urgent)); |
429 | json_object_object_add(object, "representation", workspace->representation ? | 439 | json_object_object_add(object, "representation", workspace->representation ? |
@@ -448,30 +458,32 @@ static void ipc_json_describe_workspace(struct sway_workspace *workspace, | |||
448 | 458 | ||
449 | static void get_deco_rect(struct sway_container *c, struct wlr_box *deco_rect) { | 459 | static void get_deco_rect(struct sway_container *c, struct wlr_box *deco_rect) { |
450 | enum sway_container_layout parent_layout = container_parent_layout(c); | 460 | enum sway_container_layout parent_layout = container_parent_layout(c); |
451 | bool tab_or_stack = parent_layout == L_TABBED || parent_layout == L_STACKED; | 461 | list_t *siblings = container_get_siblings(c); |
462 | bool tab_or_stack = (parent_layout == L_TABBED || parent_layout == L_STACKED) | ||
463 | && ((siblings && siblings->length > 1) || !config->hide_lone_tab); | ||
452 | if (((!tab_or_stack || container_is_floating(c)) && | 464 | if (((!tab_or_stack || container_is_floating(c)) && |
453 | c->current.border != B_NORMAL) || | 465 | c->current.border != B_NORMAL) || |
454 | c->fullscreen_mode != FULLSCREEN_NONE || | 466 | c->pending.fullscreen_mode != FULLSCREEN_NONE || |
455 | c->workspace == NULL) { | 467 | c->pending.workspace == NULL) { |
456 | deco_rect->x = deco_rect->y = deco_rect->width = deco_rect->height = 0; | 468 | deco_rect->x = deco_rect->y = deco_rect->width = deco_rect->height = 0; |
457 | return; | 469 | return; |
458 | } | 470 | } |
459 | 471 | ||
460 | if (c->parent) { | 472 | if (c->pending.parent) { |
461 | deco_rect->x = c->x - c->parent->x; | 473 | deco_rect->x = c->pending.x - c->pending.parent->pending.x; |
462 | deco_rect->y = c->y - c->parent->y; | 474 | deco_rect->y = c->pending.y - c->pending.parent->pending.y; |
463 | } else { | 475 | } else { |
464 | deco_rect->x = c->x - c->workspace->x; | 476 | deco_rect->x = c->pending.x - c->pending.workspace->x; |
465 | deco_rect->y = c->y - c->workspace->y; | 477 | deco_rect->y = c->pending.y - c->pending.workspace->y; |
466 | } | 478 | } |
467 | deco_rect->width = c->width; | 479 | deco_rect->width = c->pending.width; |
468 | deco_rect->height = container_titlebar_height(); | 480 | deco_rect->height = container_titlebar_height(); |
469 | 481 | ||
470 | if (!container_is_floating(c)) { | 482 | if (!container_is_floating(c)) { |
471 | if (parent_layout == L_TABBED) { | 483 | if (parent_layout == L_TABBED) { |
472 | deco_rect->width = c->parent | 484 | deco_rect->width = c->pending.parent |
473 | ? c->parent->width / c->parent->children->length | 485 | ? c->pending.parent->pending.width / c->pending.parent->pending.children->length |
474 | : c->workspace->width / c->workspace->tiling->length; | 486 | : c->pending.workspace->width / c->pending.workspace->tiling->length; |
475 | deco_rect->x += deco_rect->width * container_sibling_index(c); | 487 | deco_rect->x += deco_rect->width * container_sibling_index(c); |
476 | } else if (parent_layout == L_STACKED) { | 488 | } else if (parent_layout == L_STACKED) { |
477 | if (!c->view) { | 489 | if (!c->view) { |
@@ -494,10 +506,10 @@ static void ipc_json_describe_view(struct sway_container *c, json_object *object | |||
494 | json_object_object_add(object, "visible", json_object_new_boolean(visible)); | 506 | json_object_object_add(object, "visible", json_object_new_boolean(visible)); |
495 | 507 | ||
496 | struct wlr_box window_box = { | 508 | struct wlr_box window_box = { |
497 | c->content_x - c->x, | 509 | c->pending.content_x - c->pending.x, |
498 | (c->current.border == B_PIXEL) ? c->current.border_thickness : 0, | 510 | (c->current.border == B_PIXEL) ? c->current.border_thickness : 0, |
499 | c->content_width, | 511 | c->pending.content_width, |
500 | c->content_height | 512 | c->pending.content_height |
501 | }; | 513 | }; |
502 | 514 | ||
503 | json_object_object_add(object, "window_rect", ipc_json_create_rect(&window_box)); | 515 | json_object_object_add(object, "window_rect", ipc_json_create_rect(&window_box)); |
@@ -583,16 +595,18 @@ static void ipc_json_describe_view(struct sway_container *c, json_object *object | |||
583 | static void ipc_json_describe_container(struct sway_container *c, json_object *object) { | 595 | static void ipc_json_describe_container(struct sway_container *c, json_object *object) { |
584 | json_object_object_add(object, "name", | 596 | json_object_object_add(object, "name", |
585 | c->title ? json_object_new_string(c->title) : NULL); | 597 | c->title ? json_object_new_string(c->title) : NULL); |
586 | json_object_object_add(object, "type", | 598 | if (container_is_floating(c)) { |
587 | json_object_new_string(container_is_floating(c) ? "floating_con" : "con")); | 599 | json_object_object_add(object, "type", |
600 | json_object_new_string("floating_con")); | ||
601 | } | ||
588 | 602 | ||
589 | json_object_object_add(object, "layout", | 603 | json_object_object_add(object, "layout", |
590 | json_object_new_string( | 604 | json_object_new_string( |
591 | ipc_json_layout_description(c->layout))); | 605 | ipc_json_layout_description(c->pending.layout))); |
592 | 606 | ||
593 | json_object_object_add(object, "orientation", | 607 | json_object_object_add(object, "orientation", |
594 | json_object_new_string( | 608 | json_object_new_string( |
595 | ipc_json_orientation_description(c->layout))); | 609 | ipc_json_orientation_description(c->pending.layout))); |
596 | 610 | ||
597 | bool urgent = c->view ? | 611 | bool urgent = c->view ? |
598 | view_is_urgent(c->view) : container_has_urgent_child(c); | 612 | view_is_urgent(c->view) : container_has_urgent_child(c); |
@@ -600,7 +614,7 @@ static void ipc_json_describe_container(struct sway_container *c, json_object *o | |||
600 | json_object_object_add(object, "sticky", json_object_new_boolean(c->is_sticky)); | 614 | json_object_object_add(object, "sticky", json_object_new_boolean(c->is_sticky)); |
601 | 615 | ||
602 | json_object_object_add(object, "fullscreen_mode", | 616 | json_object_object_add(object, "fullscreen_mode", |
603 | json_object_new_int(c->fullscreen_mode)); | 617 | json_object_new_int(c->pending.fullscreen_mode)); |
604 | 618 | ||
605 | struct sway_node *parent = node_get_parent(&c->node); | 619 | struct sway_node *parent = node_get_parent(&c->node); |
606 | struct wlr_box parent_box = {0, 0, 0, 0}; | 620 | struct wlr_box parent_box = {0, 0, 0, 0}; |
@@ -610,8 +624,8 @@ static void ipc_json_describe_container(struct sway_container *c, json_object *o | |||
610 | } | 624 | } |
611 | 625 | ||
612 | if (parent_box.width != 0 && parent_box.height != 0) { | 626 | if (parent_box.width != 0 && parent_box.height != 0) { |
613 | double percent = ((double)c->width / parent_box.width) | 627 | double percent = ((double)c->pending.width / parent_box.width) |
614 | * ((double)c->height / parent_box.height); | 628 | * ((double)c->pending.height / parent_box.height); |
615 | json_object_object_add(object, "percent", json_object_new_double(percent)); | 629 | json_object_object_add(object, "percent", json_object_new_double(percent)); |
616 | } | 630 | } |
617 | 631 | ||
@@ -692,15 +706,14 @@ json_object *ipc_json_describe_node(struct sway_node *node) { | |||
692 | }; | 706 | }; |
693 | seat_for_each_node(seat, focus_inactive_children_iterator, &data); | 707 | seat_for_each_node(seat, focus_inactive_children_iterator, &data); |
694 | 708 | ||
695 | json_object *object = ipc_json_create_node( | 709 | json_object *object = ipc_json_create_node((int)node->id, |
696 | (int)node->id, name, focused, focus, &box); | 710 | ipc_json_node_type_description(node->type), name, focused, focus, &box); |
697 | 711 | ||
698 | switch (node->type) { | 712 | switch (node->type) { |
699 | case N_ROOT: | 713 | case N_ROOT: |
700 | ipc_json_describe_root(root, object); | ||
701 | break; | 714 | break; |
702 | case N_OUTPUT: | 715 | case N_OUTPUT: |
703 | ipc_json_describe_output(node->sway_output, object); | 716 | ipc_json_describe_enabled_output(node->sway_output, object); |
704 | break; | 717 | break; |
705 | case N_CONTAINER: | 718 | case N_CONTAINER: |
706 | ipc_json_describe_container(node->sway_container, object); | 719 | ipc_json_describe_container(node->sway_container, object); |
@@ -743,10 +756,10 @@ json_object *ipc_json_describe_node_recursive(struct sway_node *node) { | |||
743 | } | 756 | } |
744 | break; | 757 | break; |
745 | case N_CONTAINER: | 758 | case N_CONTAINER: |
746 | if (node->sway_container->children) { | 759 | if (node->sway_container->pending.children) { |
747 | for (i = 0; i < node->sway_container->children->length; ++i) { | 760 | for (i = 0; i < node->sway_container->pending.children->length; ++i) { |
748 | struct sway_container *child = | 761 | struct sway_container *child = |
749 | node->sway_container->children->items[i]; | 762 | node->sway_container->pending.children->items[i]; |
750 | json_object_array_add(children, | 763 | json_object_array_add(children, |
751 | ipc_json_describe_node_recursive(&child->node)); | 764 | ipc_json_describe_node_recursive(&child->node)); |
752 | } | 765 | } |
@@ -971,10 +984,16 @@ json_object *ipc_json_describe_input(struct sway_input_device *device) { | |||
971 | input_device_get_type(device))); | 984 | input_device_get_type(device))); |
972 | 985 | ||
973 | if (device->wlr_device->type == WLR_INPUT_DEVICE_KEYBOARD) { | 986 | if (device->wlr_device->type == WLR_INPUT_DEVICE_KEYBOARD) { |
974 | struct wlr_keyboard *keyboard = device->wlr_device->keyboard; | 987 | struct wlr_keyboard *keyboard = |
988 | wlr_keyboard_from_input_device(device->wlr_device); | ||
975 | struct xkb_keymap *keymap = keyboard->keymap; | 989 | struct xkb_keymap *keymap = keyboard->keymap; |
976 | struct xkb_state *state = keyboard->xkb_state; | 990 | struct xkb_state *state = keyboard->xkb_state; |
977 | 991 | ||
992 | json_object_object_add(object, "repeat_delay", | ||
993 | json_object_new_int(keyboard->repeat_info.delay)); | ||
994 | json_object_object_add(object, "repeat_rate", | ||
995 | json_object_new_int(keyboard->repeat_info.rate)); | ||
996 | |||
978 | json_object *layouts_arr = json_object_new_array(); | 997 | json_object *layouts_arr = json_object_new_array(); |
979 | json_object_object_add(object, "xkb_layout_names", layouts_arr); | 998 | json_object_object_add(object, "xkb_layout_names", layouts_arr); |
980 | 999 | ||
@@ -996,6 +1015,17 @@ json_object *ipc_json_describe_input(struct sway_input_device *device) { | |||
996 | } | 1015 | } |
997 | } | 1016 | } |
998 | 1017 | ||
1018 | if (device->wlr_device->type == WLR_INPUT_DEVICE_POINTER) { | ||
1019 | struct input_config *ic = input_device_get_config(device); | ||
1020 | float scroll_factor = 1.0f; | ||
1021 | if (ic != NULL && !isnan(ic->scroll_factor) && | ||
1022 | ic->scroll_factor != FLT_MIN) { | ||
1023 | scroll_factor = ic->scroll_factor; | ||
1024 | } | ||
1025 | json_object_object_add(object, "scroll_factor", | ||
1026 | json_object_new_double(scroll_factor)); | ||
1027 | } | ||
1028 | |||
999 | if (wlr_input_device_is_libinput(device->wlr_device)) { | 1029 | if (wlr_input_device_is_libinput(device->wlr_device)) { |
1000 | struct libinput_device *libinput_dev; | 1030 | struct libinput_device *libinput_dev; |
1001 | libinput_dev = wlr_libinput_get_device_handle(device->wlr_device); | 1031 | libinput_dev = wlr_libinput_get_device_handle(device->wlr_device); |
@@ -1109,7 +1139,9 @@ json_object *ipc_json_describe_bar_config(struct bar_config *bar) { | |||
1109 | json_object_object_add(json, "verbose", | 1139 | json_object_object_add(json, "verbose", |
1110 | json_object_new_boolean(bar->verbose)); | 1140 | json_object_new_boolean(bar->verbose)); |
1111 | json_object_object_add(json, "pango_markup", | 1141 | json_object_object_add(json, "pango_markup", |
1112 | json_object_new_boolean(bar->pango_markup)); | 1142 | json_object_new_boolean(bar->pango_markup == PANGO_MARKUP_DEFAULT |
1143 | ? config->pango_markup | ||
1144 | : bar->pango_markup)); | ||
1113 | 1145 | ||
1114 | json_object *colors = json_object_new_object(); | 1146 | json_object *colors = json_object_new_object(); |
1115 | json_object_object_add(colors, "background", | 1147 | json_object_object_add(colors, "background", |
diff --git a/sway/ipc-server.c b/sway/ipc-server.c index aad9a7b5..3cbf7889 100644 --- a/sway/ipc-server.c +++ b/sway/ipc-server.c | |||
@@ -150,7 +150,6 @@ struct sockaddr_un *ipc_user_sockaddr(void) { | |||
150 | int ipc_handle_connection(int fd, uint32_t mask, void *data) { | 150 | int ipc_handle_connection(int fd, uint32_t mask, void *data) { |
151 | (void) fd; | 151 | (void) fd; |
152 | struct sway_server *server = data; | 152 | struct sway_server *server = data; |
153 | sway_log(SWAY_DEBUG, "Event on IPC listening socket"); | ||
154 | assert(mask == WL_EVENT_READABLE); | 153 | assert(mask == WL_EVENT_READABLE); |
155 | 154 | ||
156 | int client_fd = accept(ipc_socket, NULL, NULL); | 155 | int client_fd = accept(ipc_socket, NULL, NULL); |
@@ -211,13 +210,10 @@ int ipc_client_handle_readable(int client_fd, uint32_t mask, void *data) { | |||
211 | } | 210 | } |
212 | 211 | ||
213 | if (mask & WL_EVENT_HANGUP) { | 212 | if (mask & WL_EVENT_HANGUP) { |
214 | sway_log(SWAY_DEBUG, "Client %d hung up", client->fd); | ||
215 | ipc_client_disconnect(client); | 213 | ipc_client_disconnect(client); |
216 | return 0; | 214 | return 0; |
217 | } | 215 | } |
218 | 216 | ||
219 | sway_log(SWAY_DEBUG, "Client %d readable", client->fd); | ||
220 | |||
221 | int read_available; | 217 | int read_available; |
222 | if (ioctl(client_fd, FIONREAD, &read_available) == -1) { | 218 | if (ioctl(client_fd, FIONREAD, &read_available) == -1) { |
223 | sway_log_errno(SWAY_INFO, "Unable to read IPC socket buffer size"); | 219 | sway_log_errno(SWAY_INFO, "Unable to read IPC socket buffer size"); |
@@ -523,7 +519,6 @@ int ipc_client_handle_writable(int client_fd, uint32_t mask, void *data) { | |||
523 | } | 519 | } |
524 | 520 | ||
525 | if (mask & WL_EVENT_HANGUP) { | 521 | if (mask & WL_EVENT_HANGUP) { |
526 | sway_log(SWAY_DEBUG, "Client %d hung up", client->fd); | ||
527 | ipc_client_disconnect(client); | 522 | ipc_client_disconnect(client); |
528 | return 0; | 523 | return 0; |
529 | } | 524 | } |
@@ -532,8 +527,6 @@ int ipc_client_handle_writable(int client_fd, uint32_t mask, void *data) { | |||
532 | return 0; | 527 | return 0; |
533 | } | 528 | } |
534 | 529 | ||
535 | sway_log(SWAY_DEBUG, "Client %d writable", client->fd); | ||
536 | |||
537 | ssize_t written = write(client->fd, client->write_buffer, client->write_buffer_len); | 530 | ssize_t written = write(client->fd, client->write_buffer, client->write_buffer_len); |
538 | 531 | ||
539 | if (written == -1 && errno == EAGAIN) { | 532 | if (written == -1 && errno == EAGAIN) { |
@@ -687,7 +680,7 @@ void ipc_client_handle_command(struct ipc_client *client, uint32_t payload_lengt | |||
687 | } | 680 | } |
688 | struct sway_output *output; | 681 | struct sway_output *output; |
689 | wl_list_for_each(output, &root->all_outputs, link) { | 682 | wl_list_for_each(output, &root->all_outputs, link) { |
690 | if (!output->enabled && output != root->noop_output) { | 683 | if (!output->enabled && output != root->fallback_output) { |
691 | json_object_array_add(outputs, | 684 | json_object_array_add(outputs, |
692 | ipc_json_describe_disabled_output(output)); | 685 | ipc_json_describe_disabled_output(output)); |
693 | } | 686 | } |
@@ -955,7 +948,5 @@ bool ipc_send_reply(struct ipc_client *client, enum ipc_command_type payload_typ | |||
955 | ipc_client_handle_writable, client); | 948 | ipc_client_handle_writable, client); |
956 | } | 949 | } |
957 | 950 | ||
958 | sway_log(SWAY_DEBUG, "Added IPC reply of type 0x%x to client %d queue: %s", | ||
959 | payload_type, client->fd, payload); | ||
960 | return true; | 951 | return true; |
961 | } | 952 | } |
diff --git a/sway/lock.c b/sway/lock.c new file mode 100644 index 00000000..04f80079 --- /dev/null +++ b/sway/lock.c | |||
@@ -0,0 +1,184 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <assert.h> | ||
3 | #include "log.h" | ||
4 | #include "sway/input/keyboard.h" | ||
5 | #include "sway/input/seat.h" | ||
6 | #include "sway/output.h" | ||
7 | #include "sway/server.h" | ||
8 | |||
9 | struct sway_session_lock_surface { | ||
10 | struct wlr_session_lock_surface_v1 *lock_surface; | ||
11 | struct sway_output *output; | ||
12 | struct wlr_surface *surface; | ||
13 | struct wl_listener map; | ||
14 | struct wl_listener destroy; | ||
15 | struct wl_listener surface_commit; | ||
16 | struct wl_listener output_mode; | ||
17 | struct wl_listener output_commit; | ||
18 | }; | ||
19 | |||
20 | static void handle_surface_map(struct wl_listener *listener, void *data) { | ||
21 | struct sway_session_lock_surface *surf = wl_container_of(listener, surf, map); | ||
22 | sway_force_focus(surf->surface); | ||
23 | output_damage_whole(surf->output); | ||
24 | } | ||
25 | |||
26 | static void handle_surface_commit(struct wl_listener *listener, void *data) { | ||
27 | struct sway_session_lock_surface *surf = wl_container_of(listener, surf, surface_commit); | ||
28 | output_damage_surface(surf->output, 0, 0, surf->surface, false); | ||
29 | } | ||
30 | |||
31 | static void handle_output_mode(struct wl_listener *listener, void *data) { | ||
32 | struct sway_session_lock_surface *surf = wl_container_of(listener, surf, output_mode); | ||
33 | wlr_session_lock_surface_v1_configure(surf->lock_surface, | ||
34 | surf->output->width, surf->output->height); | ||
35 | } | ||
36 | |||
37 | static void handle_output_commit(struct wl_listener *listener, void *data) { | ||
38 | struct wlr_output_event_commit *event = data; | ||
39 | struct sway_session_lock_surface *surf = wl_container_of(listener, surf, output_commit); | ||
40 | if (event->committed & ( | ||
41 | WLR_OUTPUT_STATE_MODE | | ||
42 | WLR_OUTPUT_STATE_SCALE | | ||
43 | WLR_OUTPUT_STATE_TRANSFORM)) { | ||
44 | wlr_session_lock_surface_v1_configure(surf->lock_surface, | ||
45 | surf->output->width, surf->output->height); | ||
46 | } | ||
47 | } | ||
48 | |||
49 | static void handle_surface_destroy(struct wl_listener *listener, void *data) { | ||
50 | struct sway_session_lock_surface *surf = wl_container_of(listener, surf, destroy); | ||
51 | wl_list_remove(&surf->map.link); | ||
52 | wl_list_remove(&surf->destroy.link); | ||
53 | wl_list_remove(&surf->surface_commit.link); | ||
54 | wl_list_remove(&surf->output_mode.link); | ||
55 | wl_list_remove(&surf->output_commit.link); | ||
56 | output_damage_whole(surf->output); | ||
57 | free(surf); | ||
58 | } | ||
59 | |||
60 | static void handle_new_surface(struct wl_listener *listener, void *data) { | ||
61 | struct wlr_session_lock_surface_v1 *lock_surface = data; | ||
62 | struct sway_session_lock_surface *surf = calloc(1, sizeof(*surf)); | ||
63 | if (surf == NULL) { | ||
64 | return; | ||
65 | } | ||
66 | |||
67 | sway_log(SWAY_DEBUG, "new lock layer surface"); | ||
68 | |||
69 | struct sway_output *output = lock_surface->output->data; | ||
70 | wlr_session_lock_surface_v1_configure(lock_surface, output->width, output->height); | ||
71 | |||
72 | surf->lock_surface = lock_surface; | ||
73 | surf->surface = lock_surface->surface; | ||
74 | surf->output = output; | ||
75 | surf->map.notify = handle_surface_map; | ||
76 | wl_signal_add(&lock_surface->events.map, &surf->map); | ||
77 | surf->destroy.notify = handle_surface_destroy; | ||
78 | wl_signal_add(&lock_surface->events.destroy, &surf->destroy); | ||
79 | surf->surface_commit.notify = handle_surface_commit; | ||
80 | wl_signal_add(&surf->surface->events.commit, &surf->surface_commit); | ||
81 | surf->output_mode.notify = handle_output_mode; | ||
82 | wl_signal_add(&output->wlr_output->events.mode, &surf->output_mode); | ||
83 | surf->output_commit.notify = handle_output_commit; | ||
84 | wl_signal_add(&output->wlr_output->events.commit, &surf->output_commit); | ||
85 | } | ||
86 | |||
87 | static void handle_unlock(struct wl_listener *listener, void *data) { | ||
88 | sway_log(SWAY_DEBUG, "session unlocked"); | ||
89 | server.session_lock.locked = false; | ||
90 | server.session_lock.lock = NULL; | ||
91 | |||
92 | wl_list_remove(&server.session_lock.lock_new_surface.link); | ||
93 | wl_list_remove(&server.session_lock.lock_unlock.link); | ||
94 | wl_list_remove(&server.session_lock.lock_destroy.link); | ||
95 | |||
96 | struct sway_seat *seat; | ||
97 | wl_list_for_each(seat, &server.input->seats, link) { | ||
98 | seat_set_exclusive_client(seat, NULL); | ||
99 | // copied from seat_set_focus_layer -- deduplicate? | ||
100 | struct sway_node *previous = seat_get_focus_inactive(seat, &root->node); | ||
101 | if (previous) { | ||
102 | // Hack to get seat to re-focus the return value of get_focus | ||
103 | seat_set_focus(seat, NULL); | ||
104 | seat_set_focus(seat, previous); | ||
105 | } | ||
106 | } | ||
107 | |||
108 | // redraw everything | ||
109 | for (int i = 0; i < root->outputs->length; ++i) { | ||
110 | struct sway_output *output = root->outputs->items[i]; | ||
111 | output_damage_whole(output); | ||
112 | } | ||
113 | } | ||
114 | |||
115 | static void handle_abandon(struct wl_listener *listener, void *data) { | ||
116 | sway_log(SWAY_INFO, "session lock abandoned"); | ||
117 | server.session_lock.lock = NULL; | ||
118 | |||
119 | wl_list_remove(&server.session_lock.lock_new_surface.link); | ||
120 | wl_list_remove(&server.session_lock.lock_unlock.link); | ||
121 | wl_list_remove(&server.session_lock.lock_destroy.link); | ||
122 | |||
123 | struct sway_seat *seat; | ||
124 | wl_list_for_each(seat, &server.input->seats, link) { | ||
125 | seat->exclusive_client = NULL; | ||
126 | } | ||
127 | |||
128 | // redraw everything | ||
129 | for (int i = 0; i < root->outputs->length; ++i) { | ||
130 | struct sway_output *output = root->outputs->items[i]; | ||
131 | output_damage_whole(output); | ||
132 | } | ||
133 | } | ||
134 | |||
135 | static void handle_session_lock(struct wl_listener *listener, void *data) { | ||
136 | struct wlr_session_lock_v1 *lock = data; | ||
137 | struct wl_client *client = wl_resource_get_client(lock->resource); | ||
138 | |||
139 | if (server.session_lock.lock) { | ||
140 | wlr_session_lock_v1_destroy(lock); | ||
141 | return; | ||
142 | } | ||
143 | |||
144 | sway_log(SWAY_DEBUG, "session locked"); | ||
145 | server.session_lock.locked = true; | ||
146 | server.session_lock.lock = lock; | ||
147 | |||
148 | struct sway_seat *seat; | ||
149 | wl_list_for_each(seat, &server.input->seats, link) { | ||
150 | seat_set_exclusive_client(seat, client); | ||
151 | } | ||
152 | |||
153 | wl_signal_add(&lock->events.new_surface, &server.session_lock.lock_new_surface); | ||
154 | wl_signal_add(&lock->events.unlock, &server.session_lock.lock_unlock); | ||
155 | wl_signal_add(&lock->events.destroy, &server.session_lock.lock_destroy); | ||
156 | |||
157 | wlr_session_lock_v1_send_locked(lock); | ||
158 | |||
159 | // redraw everything | ||
160 | for (int i = 0; i < root->outputs->length; ++i) { | ||
161 | struct sway_output *output = root->outputs->items[i]; | ||
162 | output_damage_whole(output); | ||
163 | } | ||
164 | } | ||
165 | |||
166 | static void handle_session_lock_destroy(struct wl_listener *listener, void *data) { | ||
167 | assert(server.session_lock.lock == NULL); | ||
168 | wl_list_remove(&server.session_lock.new_lock.link); | ||
169 | wl_list_remove(&server.session_lock.manager_destroy.link); | ||
170 | } | ||
171 | |||
172 | void sway_session_lock_init(void) { | ||
173 | server.session_lock.manager = wlr_session_lock_manager_v1_create(server.wl_display); | ||
174 | |||
175 | server.session_lock.lock_new_surface.notify = handle_new_surface; | ||
176 | server.session_lock.lock_unlock.notify = handle_unlock; | ||
177 | server.session_lock.lock_destroy.notify = handle_abandon; | ||
178 | server.session_lock.new_lock.notify = handle_session_lock; | ||
179 | server.session_lock.manager_destroy.notify = handle_session_lock_destroy; | ||
180 | wl_signal_add(&server.session_lock.manager->events.new_lock, | ||
181 | &server.session_lock.new_lock); | ||
182 | wl_signal_add(&server.session_lock.manager->events.destroy, | ||
183 | &server.session_lock.manager_destroy); | ||
184 | } | ||
diff --git a/sway/main.c b/sway/main.c index 0c219fb3..85bc2f1c 100644 --- a/sway/main.c +++ b/sway/main.c | |||
@@ -6,12 +6,14 @@ | |||
6 | #include <stdio.h> | 6 | #include <stdio.h> |
7 | #include <stdlib.h> | 7 | #include <stdlib.h> |
8 | #include <string.h> | 8 | #include <string.h> |
9 | #include <sys/resource.h> | ||
9 | #include <sys/stat.h> | 10 | #include <sys/stat.h> |
10 | #include <sys/types.h> | 11 | #include <sys/types.h> |
11 | #include <sys/wait.h> | 12 | #include <sys/wait.h> |
12 | #include <sys/un.h> | 13 | #include <sys/un.h> |
13 | #include <unistd.h> | 14 | #include <unistd.h> |
14 | #include <wlr/util/log.h> | 15 | #include <wlr/util/log.h> |
16 | #include <wlr/version.h> | ||
15 | #include "sway/commands.h" | 17 | #include "sway/commands.h" |
16 | #include "sway/config.h" | 18 | #include "sway/config.h" |
17 | #include "sway/server.h" | 19 | #include "sway/server.h" |
@@ -26,6 +28,7 @@ | |||
26 | 28 | ||
27 | static bool terminate_request = false; | 29 | static bool terminate_request = false; |
28 | static int exit_value = 0; | 30 | static int exit_value = 0; |
31 | static struct rlimit original_nofile_rlimit = {0}; | ||
29 | struct sway_server server = {0}; | 32 | struct sway_server server = {0}; |
30 | struct sway_debug debug = {0}; | 33 | struct sway_debug debug = {0}; |
31 | 34 | ||
@@ -46,43 +49,6 @@ void sig_handler(int signal) { | |||
46 | sway_terminate(EXIT_SUCCESS); | 49 | sway_terminate(EXIT_SUCCESS); |
47 | } | 50 | } |
48 | 51 | ||
49 | void detect_raspi(void) { | ||
50 | bool raspi = false; | ||
51 | FILE *f = fopen("/sys/firmware/devicetree/base/model", "r"); | ||
52 | if (!f) { | ||
53 | return; | ||
54 | } | ||
55 | char *line = NULL; | ||
56 | size_t line_size = 0; | ||
57 | while (getline(&line, &line_size, f) != -1) { | ||
58 | if (strstr(line, "Raspberry Pi")) { | ||
59 | raspi = true; | ||
60 | break; | ||
61 | } | ||
62 | } | ||
63 | fclose(f); | ||
64 | FILE *g = fopen("/proc/modules", "r"); | ||
65 | if (!g) { | ||
66 | free(line); | ||
67 | return; | ||
68 | } | ||
69 | bool vc4 = false; | ||
70 | while (getline(&line, &line_size, g) != -1) { | ||
71 | if (strstr(line, "vc4")) { | ||
72 | vc4 = true; | ||
73 | break; | ||
74 | } | ||
75 | } | ||
76 | free(line); | ||
77 | fclose(g); | ||
78 | if (!vc4 && raspi) { | ||
79 | fprintf(stderr, "\x1B[1;31mWarning: You have a " | ||
80 | "Raspberry Pi, but the vc4 Module is " | ||
81 | "not loaded! Set 'dtoverlay=vc4-kms-v3d'" | ||
82 | "in /boot/config.txt and reboot.\x1B[0m\n"); | ||
83 | } | ||
84 | } | ||
85 | |||
86 | void detect_proprietary(int allow_unsupported_gpu) { | 52 | void detect_proprietary(int allow_unsupported_gpu) { |
87 | FILE *f = fopen("/proc/modules", "r"); | 53 | FILE *f = fopen("/proc/modules", "r"); |
88 | if (!f) { | 54 | if (!f) { |
@@ -99,7 +65,7 @@ void detect_proprietary(int allow_unsupported_gpu) { | |||
99 | sway_log(SWAY_ERROR, | 65 | sway_log(SWAY_ERROR, |
100 | "Proprietary Nvidia drivers are NOT supported. " | 66 | "Proprietary Nvidia drivers are NOT supported. " |
101 | "Use Nouveau. To launch sway anyway, launch with " | 67 | "Use Nouveau. To launch sway anyway, launch with " |
102 | "--my-next-gpu-wont-be-nvidia and DO NOT report issues."); | 68 | "--unsupported-gpu and DO NOT report issues."); |
103 | exit(EXIT_FAILURE); | 69 | exit(EXIT_FAILURE); |
104 | } | 70 | } |
105 | break; | 71 | break; |
@@ -184,27 +150,47 @@ static void log_kernel(void) { | |||
184 | pclose(f); | 150 | pclose(f); |
185 | } | 151 | } |
186 | 152 | ||
187 | 153 | static bool detect_suid(void) { | |
188 | static bool drop_permissions(void) { | 154 | if (geteuid() != 0 && getegid() != 0) { |
189 | if (getuid() != geteuid() || getgid() != getegid()) { | 155 | return false; |
190 | // Set the gid and uid in the correct order. | ||
191 | if (setgid(getgid()) != 0) { | ||
192 | sway_log(SWAY_ERROR, "Unable to drop root group, refusing to start"); | ||
193 | return false; | ||
194 | } | ||
195 | if (setuid(getuid()) != 0) { | ||
196 | sway_log(SWAY_ERROR, "Unable to drop root user, refusing to start"); | ||
197 | return false; | ||
198 | } | ||
199 | } | 156 | } |
200 | if (setgid(0) != -1 || setuid(0) != -1) { | 157 | |
201 | sway_log(SWAY_ERROR, "Unable to drop root (we shouldn't be able to " | 158 | if (getuid() == geteuid() && getgid() == getegid()) { |
202 | "restore it after setuid), refusing to start"); | ||
203 | return false; | 159 | return false; |
204 | } | 160 | } |
161 | |||
162 | sway_log(SWAY_ERROR, "SUID operation is no longer supported, refusing to start. " | ||
163 | "This check will be removed in a future release."); | ||
205 | return true; | 164 | return true; |
206 | } | 165 | } |
207 | 166 | ||
167 | static void increase_nofile_limit(void) { | ||
168 | if (getrlimit(RLIMIT_NOFILE, &original_nofile_rlimit) != 0) { | ||
169 | sway_log_errno(SWAY_ERROR, "Failed to bump max open files limit: " | ||
170 | "getrlimit(NOFILE) failed"); | ||
171 | return; | ||
172 | } | ||
173 | |||
174 | struct rlimit new_rlimit = original_nofile_rlimit; | ||
175 | new_rlimit.rlim_cur = new_rlimit.rlim_max; | ||
176 | if (setrlimit(RLIMIT_NOFILE, &new_rlimit) != 0) { | ||
177 | sway_log_errno(SWAY_ERROR, "Failed to bump max open files limit: " | ||
178 | "setrlimit(NOFILE) failed"); | ||
179 | sway_log(SWAY_INFO, "Running with %d max open files", | ||
180 | (int)original_nofile_rlimit.rlim_cur); | ||
181 | } | ||
182 | } | ||
183 | |||
184 | void restore_nofile_limit(void) { | ||
185 | if (original_nofile_rlimit.rlim_cur == 0) { | ||
186 | return; | ||
187 | } | ||
188 | if (setrlimit(RLIMIT_NOFILE, &original_nofile_rlimit) != 0) { | ||
189 | sway_log_errno(SWAY_ERROR, "Failed to restore max open files limit: " | ||
190 | "setrlimit(NOFILE) failed"); | ||
191 | } | ||
192 | } | ||
193 | |||
208 | void enable_debug_flag(const char *flag) { | 194 | void enable_debug_flag(const char *flag) { |
209 | if (strcmp(flag, "damage=highlight") == 0) { | 195 | if (strcmp(flag, "damage=highlight") == 0) { |
210 | debug.damage = DAMAGE_HIGHLIGHT; | 196 | debug.damage = DAMAGE_HIGHLIGHT; |
@@ -218,6 +204,8 @@ void enable_debug_flag(const char *flag) { | |||
218 | debug.txn_timings = true; | 204 | debug.txn_timings = true; |
219 | } else if (strncmp(flag, "txn-timeout=", 12) == 0) { | 205 | } else if (strncmp(flag, "txn-timeout=", 12) == 0) { |
220 | server.txn_timeout_ms = atoi(&flag[12]); | 206 | server.txn_timeout_ms = atoi(&flag[12]); |
207 | } else if (strcmp(flag, "noscanout") == 0) { | ||
208 | debug.noscanout = true; | ||
221 | } else { | 209 | } else { |
222 | sway_log(SWAY_ERROR, "Unknown debug flag: %s", flag); | 210 | sway_log(SWAY_ERROR, "Unknown debug flag: %s", flag); |
223 | } | 211 | } |
@@ -242,36 +230,35 @@ static void handle_wlr_log(enum wlr_log_importance importance, | |||
242 | _sway_vlog(convert_wlr_log_importance(importance), sway_fmt, args); | 230 | _sway_vlog(convert_wlr_log_importance(importance), sway_fmt, args); |
243 | } | 231 | } |
244 | 232 | ||
233 | static const struct option long_options[] = { | ||
234 | {"help", no_argument, NULL, 'h'}, | ||
235 | {"config", required_argument, NULL, 'c'}, | ||
236 | {"validate", no_argument, NULL, 'C'}, | ||
237 | {"debug", no_argument, NULL, 'd'}, | ||
238 | {"version", no_argument, NULL, 'v'}, | ||
239 | {"verbose", no_argument, NULL, 'V'}, | ||
240 | {"get-socketpath", no_argument, NULL, 'p'}, | ||
241 | {"unsupported-gpu", no_argument, NULL, 'u'}, | ||
242 | {0, 0, 0, 0} | ||
243 | }; | ||
244 | |||
245 | static const char usage[] = | ||
246 | "Usage: sway [options] [command]\n" | ||
247 | "\n" | ||
248 | " -h, --help Show help message and quit.\n" | ||
249 | " -c, --config <config> Specify a config file.\n" | ||
250 | " -C, --validate Check the validity of the config file, then exit.\n" | ||
251 | " -d, --debug Enables full logging, including debug information.\n" | ||
252 | " -v, --version Show the version number and quit.\n" | ||
253 | " -V, --verbose Enables more verbose logging.\n" | ||
254 | " --get-socketpath Gets the IPC socket path and prints it, then exits.\n" | ||
255 | "\n"; | ||
256 | |||
245 | int main(int argc, char **argv) { | 257 | int main(int argc, char **argv) { |
246 | static int verbose = 0, debug = 0, validate = 0, allow_unsupported_gpu = 0; | 258 | static bool verbose = false, debug = false, validate = false, allow_unsupported_gpu = false; |
247 | |||
248 | static struct option long_options[] = { | ||
249 | {"help", no_argument, NULL, 'h'}, | ||
250 | {"config", required_argument, NULL, 'c'}, | ||
251 | {"validate", no_argument, NULL, 'C'}, | ||
252 | {"debug", no_argument, NULL, 'd'}, | ||
253 | {"version", no_argument, NULL, 'v'}, | ||
254 | {"verbose", no_argument, NULL, 'V'}, | ||
255 | {"get-socketpath", no_argument, NULL, 'p'}, | ||
256 | {"unsupported-gpu", no_argument, NULL, 'u'}, | ||
257 | {"my-next-gpu-wont-be-nvidia", no_argument, NULL, 'u'}, | ||
258 | {0, 0, 0, 0} | ||
259 | }; | ||
260 | 259 | ||
261 | char *config_path = NULL; | 260 | char *config_path = NULL; |
262 | 261 | ||
263 | const char* usage = | ||
264 | "Usage: sway [options] [command]\n" | ||
265 | "\n" | ||
266 | " -h, --help Show help message and quit.\n" | ||
267 | " -c, --config <config> Specify a config file.\n" | ||
268 | " -C, --validate Check the validity of the config file, then exit.\n" | ||
269 | " -d, --debug Enables full logging, including debug information.\n" | ||
270 | " -v, --version Show the version number and quit.\n" | ||
271 | " -V, --verbose Enables more verbose logging.\n" | ||
272 | " --get-socketpath Gets the IPC socket path and prints it, then exits.\n" | ||
273 | "\n"; | ||
274 | |||
275 | int c; | 262 | int c; |
276 | while (1) { | 263 | while (1) { |
277 | int option_index = 0; | 264 | int option_index = 0; |
@@ -289,25 +276,25 @@ int main(int argc, char **argv) { | |||
289 | config_path = strdup(optarg); | 276 | config_path = strdup(optarg); |
290 | break; | 277 | break; |
291 | case 'C': // validate | 278 | case 'C': // validate |
292 | validate = 1; | 279 | validate = true; |
293 | break; | 280 | break; |
294 | case 'd': // debug | 281 | case 'd': // debug |
295 | debug = 1; | 282 | debug = true; |
296 | break; | 283 | break; |
297 | case 'D': // extended debug options | 284 | case 'D': // extended debug options |
298 | enable_debug_flag(optarg); | 285 | enable_debug_flag(optarg); |
299 | break; | 286 | break; |
300 | case 'u': | 287 | case 'u': |
301 | allow_unsupported_gpu = 1; | 288 | allow_unsupported_gpu = true; |
302 | break; | 289 | break; |
303 | case 'v': // version | 290 | case 'v': // version |
304 | printf("sway version " SWAY_VERSION "\n"); | 291 | printf("sway version " SWAY_VERSION "\n"); |
305 | exit(EXIT_SUCCESS); | 292 | exit(EXIT_SUCCESS); |
306 | break; | 293 | break; |
307 | case 'V': // verbose | 294 | case 'V': // verbose |
308 | verbose = 1; | 295 | verbose = true; |
309 | break; | 296 | break; |
310 | case 'p': ; // --get-socketpath | 297 | case 'p': // --get-socketpath |
311 | if (getenv("SWAYSOCK")) { | 298 | if (getenv("SWAYSOCK")) { |
312 | printf("%s\n", getenv("SWAYSOCK")); | 299 | printf("%s\n", getenv("SWAYSOCK")); |
313 | exit(EXIT_SUCCESS); | 300 | exit(EXIT_SUCCESS); |
@@ -322,6 +309,11 @@ int main(int argc, char **argv) { | |||
322 | } | 309 | } |
323 | } | 310 | } |
324 | 311 | ||
312 | // SUID operation is deprecated, so block it for now. | ||
313 | if (detect_suid()) { | ||
314 | exit(EXIT_FAILURE); | ||
315 | } | ||
316 | |||
325 | // Since wayland requires XDG_RUNTIME_DIR to be set, abort with just the | 317 | // Since wayland requires XDG_RUNTIME_DIR to be set, abort with just the |
326 | // clear error message (when not running as an IPC client). | 318 | // clear error message (when not running as an IPC client). |
327 | if (!getenv("XDG_RUNTIME_DIR") && optind == argc) { | 319 | if (!getenv("XDG_RUNTIME_DIR") && optind == argc) { |
@@ -344,11 +336,10 @@ int main(int argc, char **argv) { | |||
344 | } | 336 | } |
345 | 337 | ||
346 | sway_log(SWAY_INFO, "Sway version " SWAY_VERSION); | 338 | sway_log(SWAY_INFO, "Sway version " SWAY_VERSION); |
339 | sway_log(SWAY_INFO, "wlroots version " WLR_VERSION_STR); | ||
347 | log_kernel(); | 340 | log_kernel(); |
348 | log_distro(); | 341 | log_distro(); |
349 | log_env(); | 342 | log_env(); |
350 | detect_proprietary(allow_unsupported_gpu); | ||
351 | detect_raspi(); | ||
352 | 343 | ||
353 | if (optind < argc) { // Behave as IPC client | 344 | if (optind < argc) { // Behave as IPC client |
354 | if (optind != 1) { | 345 | if (optind != 1) { |
@@ -361,9 +352,6 @@ int main(int argc, char **argv) { | |||
361 | "`sway -d 2>sway.log`."); | 352 | "`sway -d 2>sway.log`."); |
362 | exit(EXIT_FAILURE); | 353 | exit(EXIT_FAILURE); |
363 | } | 354 | } |
364 | if (!drop_permissions()) { | ||
365 | exit(EXIT_FAILURE); | ||
366 | } | ||
367 | char *socket_path = getenv("SWAYSOCK"); | 355 | char *socket_path = getenv("SWAYSOCK"); |
368 | if (!socket_path) { | 356 | if (!socket_path) { |
369 | sway_log(SWAY_ERROR, "Unable to retrieve socket path"); | 357 | sway_log(SWAY_ERROR, "Unable to retrieve socket path"); |
@@ -375,14 +363,8 @@ int main(int argc, char **argv) { | |||
375 | return 0; | 363 | return 0; |
376 | } | 364 | } |
377 | 365 | ||
378 | if (!server_privileged_prepare(&server)) { | 366 | detect_proprietary(allow_unsupported_gpu); |
379 | return 1; | 367 | increase_nofile_limit(); |
380 | } | ||
381 | |||
382 | if (!drop_permissions()) { | ||
383 | server_fini(&server); | ||
384 | exit(EXIT_FAILURE); | ||
385 | } | ||
386 | 368 | ||
387 | // handle SIGTERM signals | 369 | // handle SIGTERM signals |
388 | signal(SIGTERM, sig_handler); | 370 | signal(SIGTERM, sig_handler); |
@@ -413,6 +395,8 @@ int main(int argc, char **argv) { | |||
413 | goto shutdown; | 395 | goto shutdown; |
414 | } | 396 | } |
415 | 397 | ||
398 | set_rr_scheduling(); | ||
399 | |||
416 | if (!server_start(&server)) { | 400 | if (!server_start(&server)) { |
417 | sway_terminate(EXIT_FAILURE); | 401 | sway_terminate(EXIT_FAILURE); |
418 | goto shutdown; | 402 | goto shutdown; |
diff --git a/sway/meson.build b/sway/meson.build index 6e138101..ced7419c 100644 --- a/sway/meson.build +++ b/sway/meson.build | |||
@@ -5,9 +5,12 @@ sway_sources = files( | |||
5 | 'decoration.c', | 5 | 'decoration.c', |
6 | 'ipc-json.c', | 6 | 'ipc-json.c', |
7 | 'ipc-server.c', | 7 | 'ipc-server.c', |
8 | 'lock.c', | ||
8 | 'main.c', | 9 | 'main.c', |
10 | 'realtime.c', | ||
9 | 'server.c', | 11 | 'server.c', |
10 | 'swaynag.c', | 12 | 'swaynag.c', |
13 | 'xdg_activation_v1.c', | ||
11 | 'xdg_decoration.c', | 14 | 'xdg_decoration.c', |
12 | 15 | ||
13 | 'desktop/desktop.c', | 16 | 'desktop/desktop.c', |
@@ -64,6 +67,7 @@ sway_sources = files( | |||
64 | 'commands/force_focus_wrapping.c', | 67 | 'commands/force_focus_wrapping.c', |
65 | 'commands/fullscreen.c', | 68 | 'commands/fullscreen.c', |
66 | 'commands/gaps.c', | 69 | 'commands/gaps.c', |
70 | 'commands/gesture.c', | ||
67 | 'commands/hide_edge_borders.c', | 71 | 'commands/hide_edge_borders.c', |
68 | 'commands/inhibit_idle.c', | 72 | 'commands/inhibit_idle.c', |
69 | 'commands/kill.c', | 73 | 'commands/kill.c', |
@@ -187,6 +191,8 @@ sway_sources = files( | |||
187 | 'commands/output/max_render_time.c', | 191 | 'commands/output/max_render_time.c', |
188 | 'commands/output/mode.c', | 192 | 'commands/output/mode.c', |
189 | 'commands/output/position.c', | 193 | 'commands/output/position.c', |
194 | 'commands/output/power.c', | ||
195 | 'commands/output/render_bit_depth.c', | ||
190 | 'commands/output/scale.c', | 196 | 'commands/output/scale.c', |
191 | 'commands/output/scale_filter.c', | 197 | 'commands/output/scale_filter.c', |
192 | 'commands/output/subpixel.c', | 198 | 'commands/output/subpixel.c', |
@@ -204,18 +210,22 @@ sway_sources = files( | |||
204 | 210 | ||
205 | sway_deps = [ | 211 | sway_deps = [ |
206 | cairo, | 212 | cairo, |
213 | drm, | ||
207 | jsonc, | 214 | jsonc, |
208 | libevdev, | 215 | libevdev, |
209 | libinput, | 216 | libinput, |
217 | libudev, | ||
210 | math, | 218 | math, |
211 | pango, | 219 | pango, |
212 | pcre, | 220 | pcre2, |
213 | glesv2, | 221 | glesv2, |
214 | pixman, | 222 | pixman, |
215 | server_protos, | 223 | server_protos, |
224 | threads, | ||
216 | wayland_server, | 225 | wayland_server, |
217 | wlroots, | 226 | wlroots, |
218 | xkbcommon, | 227 | xkbcommon, |
228 | xcb_icccm, | ||
219 | ] | 229 | ] |
220 | 230 | ||
221 | if have_xwayland | 231 | if have_xwayland |
diff --git a/sway/realtime.c b/sway/realtime.c new file mode 100644 index 00000000..11154af0 --- /dev/null +++ b/sway/realtime.c | |||
@@ -0,0 +1,40 @@ | |||
1 | #include <sys/resource.h> | ||
2 | #include <sched.h> | ||
3 | #include <unistd.h> | ||
4 | #include <pthread.h> | ||
5 | #include "sway/server.h" | ||
6 | #include "log.h" | ||
7 | |||
8 | static void child_fork_callback(void) { | ||
9 | struct sched_param param; | ||
10 | |||
11 | param.sched_priority = 0; | ||
12 | |||
13 | int ret = pthread_setschedparam(pthread_self(), SCHED_OTHER, ¶m); | ||
14 | if (ret != 0) { | ||
15 | sway_log(SWAY_ERROR, "Failed to reset scheduler policy on fork"); | ||
16 | } | ||
17 | } | ||
18 | |||
19 | void set_rr_scheduling(void) { | ||
20 | int prio = sched_get_priority_min(SCHED_RR); | ||
21 | int old_policy; | ||
22 | int ret; | ||
23 | struct sched_param param; | ||
24 | |||
25 | ret = pthread_getschedparam(pthread_self(), &old_policy, ¶m); | ||
26 | if (ret != 0) { | ||
27 | sway_log(SWAY_DEBUG, "Failed to get old scheduling priority"); | ||
28 | return; | ||
29 | } | ||
30 | |||
31 | param.sched_priority = prio; | ||
32 | |||
33 | ret = pthread_setschedparam(pthread_self(), SCHED_RR, ¶m); | ||
34 | if (ret != 0) { | ||
35 | sway_log(SWAY_INFO, "Failed to set scheduling priority to %d", prio); | ||
36 | return; | ||
37 | } | ||
38 | |||
39 | pthread_atfork(NULL, NULL, child_fork_callback); | ||
40 | } | ||
diff --git a/sway/server.c b/sway/server.c index f51fcfe2..a87fc7cf 100644 --- a/sway/server.c +++ b/sway/server.c | |||
@@ -7,24 +7,33 @@ | |||
7 | #include <wlr/backend.h> | 7 | #include <wlr/backend.h> |
8 | #include <wlr/backend/headless.h> | 8 | #include <wlr/backend/headless.h> |
9 | #include <wlr/backend/multi.h> | 9 | #include <wlr/backend/multi.h> |
10 | #include <wlr/backend/noop.h> | ||
11 | #include <wlr/backend/session.h> | 10 | #include <wlr/backend/session.h> |
11 | #include <wlr/config.h> | ||
12 | #include <wlr/render/wlr_renderer.h> | 12 | #include <wlr/render/wlr_renderer.h> |
13 | #include <wlr/types/wlr_compositor.h> | 13 | #include <wlr/types/wlr_compositor.h> |
14 | #include <wlr/types/wlr_data_control_v1.h> | 14 | #include <wlr/types/wlr_data_control_v1.h> |
15 | #include <wlr/types/wlr_drm_lease_v1.h> | ||
16 | #include <wlr/types/wlr_drm.h> | ||
15 | #include <wlr/types/wlr_export_dmabuf_v1.h> | 17 | #include <wlr/types/wlr_export_dmabuf_v1.h> |
16 | #include <wlr/types/wlr_gamma_control_v1.h> | 18 | #include <wlr/types/wlr_gamma_control_v1.h> |
17 | #include <wlr/types/wlr_idle.h> | 19 | #include <wlr/types/wlr_idle.h> |
18 | #include <wlr/types/wlr_layer_shell_v1.h> | 20 | #include <wlr/types/wlr_layer_shell_v1.h> |
21 | #include <wlr/types/wlr_linux_dmabuf_v1.h> | ||
19 | #include <wlr/types/wlr_pointer_constraints_v1.h> | 22 | #include <wlr/types/wlr_pointer_constraints_v1.h> |
20 | #include <wlr/types/wlr_primary_selection_v1.h> | 23 | #include <wlr/types/wlr_primary_selection_v1.h> |
21 | #include <wlr/types/wlr_relative_pointer_v1.h> | 24 | #include <wlr/types/wlr_relative_pointer_v1.h> |
22 | #include <wlr/types/wlr_screencopy_v1.h> | 25 | #include <wlr/types/wlr_screencopy_v1.h> |
26 | #include <wlr/types/wlr_single_pixel_buffer_v1.h> | ||
23 | #include <wlr/types/wlr_server_decoration.h> | 27 | #include <wlr/types/wlr_server_decoration.h> |
28 | #include <wlr/types/wlr_subcompositor.h> | ||
24 | #include <wlr/types/wlr_tablet_v2.h> | 29 | #include <wlr/types/wlr_tablet_v2.h> |
25 | #include <wlr/types/wlr_viewporter.h> | 30 | #include <wlr/types/wlr_viewporter.h> |
26 | #include <wlr/types/wlr_xcursor_manager.h> | 31 | #include <wlr/types/wlr_xcursor_manager.h> |
32 | #include <wlr/types/wlr_xdg_activation_v1.h> | ||
27 | #include <wlr/types/wlr_xdg_decoration_v1.h> | 33 | #include <wlr/types/wlr_xdg_decoration_v1.h> |
34 | #include <wlr/types/wlr_xdg_foreign_registry.h> | ||
35 | #include <wlr/types/wlr_xdg_foreign_v1.h> | ||
36 | #include <wlr/types/wlr_xdg_foreign_v2.h> | ||
28 | #include <wlr/types/wlr_xdg_output_v1.h> | 37 | #include <wlr/types/wlr_xdg_output_v1.h> |
29 | #include "config.h" | 38 | #include "config.h" |
30 | #include "list.h" | 39 | #include "list.h" |
@@ -39,8 +48,22 @@ | |||
39 | #include "sway/xwayland.h" | 48 | #include "sway/xwayland.h" |
40 | #endif | 49 | #endif |
41 | 50 | ||
42 | bool server_privileged_prepare(struct sway_server *server) { | 51 | static void handle_drm_lease_request(struct wl_listener *listener, void *data) { |
43 | sway_log(SWAY_DEBUG, "Preparing Wayland server initialization"); | 52 | /* We only offer non-desktop outputs, but in the future we might want to do |
53 | * more logic here. */ | ||
54 | |||
55 | struct wlr_drm_lease_request_v1 *req = data; | ||
56 | struct wlr_drm_lease_v1 *lease = wlr_drm_lease_request_v1_grant(req); | ||
57 | if (!lease) { | ||
58 | sway_log(SWAY_ERROR, "Failed to grant lease request"); | ||
59 | wlr_drm_lease_request_v1_reject(req); | ||
60 | } | ||
61 | } | ||
62 | |||
63 | #define SWAY_XDG_SHELL_VERSION 2 | ||
64 | |||
65 | bool server_init(struct sway_server *server) { | ||
66 | sway_log(SWAY_DEBUG, "Initializing Wayland server"); | ||
44 | server->wl_display = wl_display_create(); | 67 | server->wl_display = wl_display_create(); |
45 | server->wl_event_loop = wl_display_get_event_loop(server->wl_display); | 68 | server->wl_event_loop = wl_display_get_event_loop(server->wl_display); |
46 | server->backend = wlr_backend_autocreate(server->wl_display); | 69 | server->backend = wlr_backend_autocreate(server->wl_display); |
@@ -49,22 +72,36 @@ bool server_privileged_prepare(struct sway_server *server) { | |||
49 | sway_log(SWAY_ERROR, "Unable to create backend"); | 72 | sway_log(SWAY_ERROR, "Unable to create backend"); |
50 | return false; | 73 | return false; |
51 | } | 74 | } |
52 | return true; | ||
53 | } | ||
54 | 75 | ||
55 | bool server_init(struct sway_server *server) { | 76 | server->renderer = wlr_renderer_autocreate(server->backend); |
56 | sway_log(SWAY_DEBUG, "Initializing Wayland server"); | 77 | if (!server->renderer) { |
78 | sway_log(SWAY_ERROR, "Failed to create renderer"); | ||
79 | return false; | ||
80 | } | ||
81 | |||
82 | wlr_renderer_init_wl_shm(server->renderer, server->wl_display); | ||
57 | 83 | ||
58 | struct wlr_renderer *renderer = wlr_backend_get_renderer(server->backend); | 84 | if (wlr_renderer_get_dmabuf_texture_formats(server->renderer) != NULL) { |
59 | assert(renderer); | 85 | wlr_drm_create(server->wl_display, server->renderer); |
86 | server->linux_dmabuf_v1 = | ||
87 | wlr_linux_dmabuf_v1_create(server->wl_display, server->renderer); | ||
88 | } | ||
60 | 89 | ||
61 | wlr_renderer_init_wl_display(renderer, server->wl_display); | 90 | server->allocator = wlr_allocator_autocreate(server->backend, |
91 | server->renderer); | ||
92 | if (!server->allocator) { | ||
93 | sway_log(SWAY_ERROR, "Failed to create allocator"); | ||
94 | return false; | ||
95 | } | ||
62 | 96 | ||
63 | server->compositor = wlr_compositor_create(server->wl_display, renderer); | 97 | server->compositor = wlr_compositor_create(server->wl_display, |
98 | server->renderer); | ||
64 | server->compositor_new_surface.notify = handle_compositor_new_surface; | 99 | server->compositor_new_surface.notify = handle_compositor_new_surface; |
65 | wl_signal_add(&server->compositor->events.new_surface, | 100 | wl_signal_add(&server->compositor->events.new_surface, |
66 | &server->compositor_new_surface); | 101 | &server->compositor_new_surface); |
67 | 102 | ||
103 | wlr_subcompositor_create(server->wl_display); | ||
104 | |||
68 | server->data_device_manager = | 105 | server->data_device_manager = |
69 | wlr_data_device_manager_create(server->wl_display); | 106 | wlr_data_device_manager_create(server->wl_display); |
70 | 107 | ||
@@ -87,7 +124,8 @@ bool server_init(struct sway_server *server) { | |||
87 | &server->layer_shell_surface); | 124 | &server->layer_shell_surface); |
88 | server->layer_shell_surface.notify = handle_layer_shell_surface; | 125 | server->layer_shell_surface.notify = handle_layer_shell_surface; |
89 | 126 | ||
90 | server->xdg_shell = wlr_xdg_shell_create(server->wl_display); | 127 | server->xdg_shell = wlr_xdg_shell_create(server->wl_display, |
128 | SWAY_XDG_SHELL_VERSION); | ||
91 | wl_signal_add(&server->xdg_shell->events.new_surface, | 129 | wl_signal_add(&server->xdg_shell->events.new_surface, |
92 | &server->xdg_shell_surface); | 130 | &server->xdg_shell_surface); |
93 | server->xdg_shell_surface.notify = handle_xdg_shell_surface; | 131 | server->xdg_shell_surface.notify = handle_xdg_shell_surface; |
@@ -144,16 +182,41 @@ bool server_init(struct sway_server *server) { | |||
144 | server->foreign_toplevel_manager = | 182 | server->foreign_toplevel_manager = |
145 | wlr_foreign_toplevel_manager_v1_create(server->wl_display); | 183 | wlr_foreign_toplevel_manager_v1_create(server->wl_display); |
146 | 184 | ||
185 | sway_session_lock_init(); | ||
186 | |||
187 | server->drm_lease_manager= | ||
188 | wlr_drm_lease_v1_manager_create(server->wl_display, server->backend); | ||
189 | if (server->drm_lease_manager) { | ||
190 | server->drm_lease_request.notify = handle_drm_lease_request; | ||
191 | wl_signal_add(&server->drm_lease_manager->events.request, | ||
192 | &server->drm_lease_request); | ||
193 | } else { | ||
194 | sway_log(SWAY_DEBUG, "Failed to create wlr_drm_lease_device_v1"); | ||
195 | sway_log(SWAY_INFO, "VR will not be available"); | ||
196 | } | ||
197 | |||
147 | wlr_export_dmabuf_manager_v1_create(server->wl_display); | 198 | wlr_export_dmabuf_manager_v1_create(server->wl_display); |
148 | wlr_screencopy_manager_v1_create(server->wl_display); | 199 | wlr_screencopy_manager_v1_create(server->wl_display); |
149 | wlr_data_control_manager_v1_create(server->wl_display); | 200 | wlr_data_control_manager_v1_create(server->wl_display); |
150 | wlr_primary_selection_v1_device_manager_create(server->wl_display); | 201 | wlr_primary_selection_v1_device_manager_create(server->wl_display); |
151 | wlr_viewporter_create(server->wl_display); | 202 | wlr_viewporter_create(server->wl_display); |
203 | wlr_single_pixel_buffer_manager_v1_create(server->wl_display); | ||
204 | |||
205 | struct wlr_xdg_foreign_registry *foreign_registry = | ||
206 | wlr_xdg_foreign_registry_create(server->wl_display); | ||
207 | wlr_xdg_foreign_v1_create(server->wl_display, foreign_registry); | ||
208 | wlr_xdg_foreign_v2_create(server->wl_display, foreign_registry); | ||
209 | |||
210 | server->xdg_activation_v1 = wlr_xdg_activation_v1_create(server->wl_display); | ||
211 | server->xdg_activation_v1_request_activate.notify = | ||
212 | xdg_activation_v1_handle_request_activate; | ||
213 | wl_signal_add(&server->xdg_activation_v1->events.request_activate, | ||
214 | &server->xdg_activation_v1_request_activate); | ||
152 | 215 | ||
153 | // Avoid using "wayland-0" as display socket | 216 | // Avoid using "wayland-0" as display socket |
154 | char name_candidate[16]; | 217 | char name_candidate[16]; |
155 | for (int i = 1; i <= 32; ++i) { | 218 | for (unsigned int i = 1; i <= 32; ++i) { |
156 | sprintf(name_candidate, "wayland-%d", i); | 219 | snprintf(name_candidate, sizeof(name_candidate), "wayland-%u", i); |
157 | if (wl_display_add_socket(server->wl_display, name_candidate) >= 0) { | 220 | if (wl_display_add_socket(server->wl_display, name_candidate) >= 0) { |
158 | server->socket = strdup(name_candidate); | 221 | server->socket = strdup(name_candidate); |
159 | break; | 222 | break; |
@@ -166,27 +229,26 @@ bool server_init(struct sway_server *server) { | |||
166 | return false; | 229 | return false; |
167 | } | 230 | } |
168 | 231 | ||
169 | server->noop_backend = wlr_noop_backend_create(server->wl_display); | 232 | server->headless_backend = wlr_headless_backend_create(server->wl_display); |
170 | |||
171 | struct wlr_output *wlr_output = wlr_noop_add_output(server->noop_backend); | ||
172 | root->noop_output = output_create(wlr_output); | ||
173 | |||
174 | server->headless_backend = | ||
175 | wlr_headless_backend_create_with_renderer(server->wl_display, renderer); | ||
176 | if (!server->headless_backend) { | 233 | if (!server->headless_backend) { |
177 | sway_log(SWAY_INFO, "Failed to create secondary headless backend, " | 234 | sway_log(SWAY_ERROR, "Failed to create secondary headless backend"); |
178 | "starting without it"); | 235 | wlr_backend_destroy(server->backend); |
236 | return false; | ||
179 | } else { | 237 | } else { |
180 | wlr_multi_backend_add(server->backend, server->headless_backend); | 238 | wlr_multi_backend_add(server->backend, server->headless_backend); |
181 | } | 239 | } |
182 | 240 | ||
241 | struct wlr_output *wlr_output = | ||
242 | wlr_headless_add_output(server->headless_backend, 800, 600); | ||
243 | wlr_output_set_name(wlr_output, "FALLBACK"); | ||
244 | root->fallback_output = output_create(wlr_output); | ||
245 | |||
183 | // This may have been set already via -Dtxn-timeout | 246 | // This may have been set already via -Dtxn-timeout |
184 | if (!server->txn_timeout_ms) { | 247 | if (!server->txn_timeout_ms) { |
185 | server->txn_timeout_ms = 200; | 248 | server->txn_timeout_ms = 200; |
186 | } | 249 | } |
187 | 250 | ||
188 | server->dirty_nodes = create_list(); | 251 | server->dirty_nodes = create_list(); |
189 | server->transactions = create_list(); | ||
190 | 252 | ||
191 | server->input = input_manager_create(server); | 253 | server->input = input_manager_create(server); |
192 | input_manager_get_default_seat(); // create seat0 | 254 | input_manager_get_default_seat(); // create seat0 |
@@ -202,7 +264,6 @@ void server_fini(struct sway_server *server) { | |||
202 | wl_display_destroy_clients(server->wl_display); | 264 | wl_display_destroy_clients(server->wl_display); |
203 | wl_display_destroy(server->wl_display); | 265 | wl_display_destroy(server->wl_display); |
204 | list_free(server->dirty_nodes); | 266 | list_free(server->dirty_nodes); |
205 | list_free(server->transactions); | ||
206 | } | 267 | } |
207 | 268 | ||
208 | bool server_start(struct sway_server *server) { | 269 | bool server_start(struct sway_server *server) { |
@@ -238,6 +299,7 @@ bool server_start(struct sway_server *server) { | |||
238 | wlr_backend_destroy(server->backend); | 299 | wlr_backend_destroy(server->backend); |
239 | return false; | 300 | return false; |
240 | } | 301 | } |
302 | |||
241 | return true; | 303 | return true; |
242 | } | 304 | } |
243 | 305 | ||
diff --git a/sway/sway-bar.5.scd b/sway/sway-bar.5.scd index 80d08449..42e59d57 100644 --- a/sway/sway-bar.5.scd +++ b/sway/sway-bar.5.scd | |||
@@ -40,7 +40,7 @@ runtime. | |||
40 | *font* <font> | 40 | *font* <font> |
41 | Specifies the font to be used in the bar. _font_ should be specified as a | 41 | Specifies the font to be used in the bar. _font_ should be specified as a |
42 | pango font description. For more information on pango font descriptions, | 42 | pango font description. For more information on pango font descriptions, |
43 | see https://developer.gnome.org/pango/stable/pango-Fonts.html#pango-font-description-from-string | 43 | see https://docs.gtk.org/Pango/type_func.FontDescription.from_string.html#description |
44 | 44 | ||
45 | *gaps* <all> | <horizontal> <vertical> | <top> <right> <bottom> <left> | 45 | *gaps* <all> | <horizontal> <vertical> | <top> <right> <bottom> <left> |
46 | Sets the gaps from the edge of the screen for the bar. Gaps can either be | 46 | Sets the gaps from the edge of the screen for the bar. Gaps can either be |
diff --git a/sway/sway-input.5.scd b/sway/sway-input.5.scd index dbf21d93..8b702b77 100644 --- a/sway/sway-input.5.scd +++ b/sway/sway-input.5.scd | |||
@@ -111,6 +111,9 @@ The following commands may only be used in the configuration file. | |||
111 | Maps inputs from this device to the specified output. Only meaningful if the | 111 | Maps inputs from this device to the specified output. Only meaningful if the |
112 | device is a pointer, touch, or drawing tablet device. | 112 | device is a pointer, touch, or drawing tablet device. |
113 | 113 | ||
114 | The wildcard _\*_ can be used to map the input device to the whole desktop | ||
115 | layout. | ||
116 | |||
114 | *input* <identifier> map_to_region <X> <Y> <width> <height> | 117 | *input* <identifier> map_to_region <X> <Y> <width> <height> |
115 | Maps inputs from this device to the specified region of the global output | 118 | Maps inputs from this device to the specified region of the global output |
116 | layout. Only meaningful if the device is a pointer, touch, or drawing tablet | 119 | layout. Only meaningful if the device is a pointer, touch, or drawing tablet |
@@ -230,7 +233,7 @@ correct seat. | |||
230 | not explicitly attached to another seat (similar to a "default" seat). | 233 | not explicitly attached to another seat (similar to a "default" seat). |
231 | 234 | ||
232 | *seat* <name> hide_cursor <timeout>|when-typing [enable|disable] | 235 | *seat* <name> hide_cursor <timeout>|when-typing [enable|disable] |
233 | Hides the cursor image after the specified event occured. | 236 | Hides the cursor image after the specified event occurred. |
234 | 237 | ||
235 | If _timeout_ is specified, then the cursor will be hidden after _timeout_ | 238 | If _timeout_ is specified, then the cursor will be hidden after _timeout_ |
236 | (in milliseconds) has elapsed with no activity on the cursor. A timeout of 0 | 239 | (in milliseconds) has elapsed with no activity on the cursor. A timeout of 0 |
@@ -274,7 +277,7 @@ correct seat. | |||
274 | whether future inhibitors are honoured by default, i.e. activated | 277 | whether future inhibitors are honoured by default, i.e. activated |
275 | automatically, the default being _enable_. When used at runtime, | 278 | automatically, the default being _enable_. When used at runtime, |
276 | _disable_ also disables any currently active inhibitors. _activate_, | 279 | _disable_ also disables any currently active inhibitors. _activate_, |
277 | _deactivate_ and _toggle_ are only useable at runtime and change the | 280 | _deactivate_ and _toggle_ are only usable at runtime and change the |
278 | state of a potentially existing inhibitor on the currently focused | 281 | state of a potentially existing inhibitor on the currently focused |
279 | window. This can be used with the current seat alias (_-_) to affect | 282 | window. This can be used with the current seat alias (_-_) to affect |
280 | only the currently focused window of the current seat. Subcommand | 283 | only the currently focused window of the current seat. Subcommand |
diff --git a/sway/sway-ipc.7.scd b/sway/sway-ipc.7.scd index 1b855959..c7a2c473 100644 --- a/sway/sway-ipc.7.scd +++ b/sway/sway-ipc.7.scd | |||
@@ -213,7 +213,10 @@ following properties: | |||
213 | : Whether this output is active/enabled | 213 | : Whether this output is active/enabled |
214 | |- dpms | 214 | |- dpms |
215 | : boolean | 215 | : boolean |
216 | : Whether this output is on/off (via DPMS) | 216 | : (Deprecated, use _power_ instead) Whether this output is on/off (via DPMS) |
217 | |- power | ||
218 | : boolean | ||
219 | : Whether this output is on/off | ||
217 | |- primary | 220 | |- primary |
218 | : boolean | 221 | : boolean |
219 | : For i3 compatibility, this will be false. It does not make sense in Wayland | 222 | : For i3 compatibility, this will be false. It does not make sense in Wayland |
@@ -294,7 +297,7 @@ following properties: | |||
294 | Retrieve a JSON representation of the tree | 297 | Retrieve a JSON representation of the tree |
295 | 298 | ||
296 | *REPLY*++ | 299 | *REPLY*++ |
297 | An array of object the represent the current tree. Each object represents one | 300 | An array of objects that represent the current tree. Each object represents one |
298 | node and will have the following properties: | 301 | node and will have the following properties: |
299 | 302 | ||
300 | [- *PROPERTY* | 303 | [- *PROPERTY* |
@@ -370,7 +373,7 @@ node and will have the following properties: | |||
370 | that can be used as an aid in submitting reproduction steps for bug reports | 373 | that can be used as an aid in submitting reproduction steps for bug reports |
371 | |- fullscreen_mode | 374 | |- fullscreen_mode |
372 | : integer | 375 | : integer |
373 | : (Only containers and views) The fullscreen mode of the node. 0 means none, 1 means | 376 | : (Only containers and views) The fullscreen mode of the node. 0 means none, 1 means |
374 | full workspace, and 2 means global fullscreen | 377 | full workspace, and 2 means global fullscreen |
375 | |- app_id | 378 | |- app_id |
376 | : string | 379 | : string |
@@ -1131,6 +1134,9 @@ following properties: | |||
1131 | |- xkb_active_layout_index | 1134 | |- xkb_active_layout_index |
1132 | : integer | 1135 | : integer |
1133 | : (Only keyboards) The index of the active keyboard layout in use | 1136 | : (Only keyboards) The index of the active keyboard layout in use |
1137 | |- scroll_factor | ||
1138 | : floating | ||
1139 | : (Only pointers) Multiplier applied on scroll event values. | ||
1134 | |- libinput | 1140 | |- libinput |
1135 | : object | 1141 | : object |
1136 | : (Only libinput devices) An object describing the current device settings. | 1142 | : (Only libinput devices) An object describing the current device settings. |
diff --git a/sway/sway-output.5.scd b/sway/sway-output.5.scd index 69f529fe..b7d5e577 100644 --- a/sway/sway-output.5.scd +++ b/sway/sway-output.5.scd | |||
@@ -24,7 +24,7 @@ must be separated by one space. For example: | |||
24 | 24 | ||
25 | # COMMANDS | 25 | # COMMANDS |
26 | 26 | ||
27 | *output* <name> mode|resolution|res [--custom] <WIDTHxHEIGHT>[@<RATE>Hz] | 27 | *output* <name> mode|resolution|res [--custom] <width>x<height>[@<rate>Hz] |
28 | Configures the specified output to use the given mode. Modes are a | 28 | Configures the specified output to use the given mode. Modes are a |
29 | combination of width and height (in pixels) and a refresh rate that your | 29 | combination of width and height (in pixels) and a refresh rate that your |
30 | display can be configured to use. For a list of available modes for each | 30 | display can be configured to use. For a list of available modes for each |
@@ -40,6 +40,16 @@ must be separated by one space. For example: | |||
40 | 40 | ||
41 | output HDMI-A-1 mode 1920x1080@60Hz | 41 | output HDMI-A-1 mode 1920x1080@60Hz |
42 | 42 | ||
43 | *output* <name> modeline <clock> <hdisplay> <hsync_start> <hsync_end> <htotal> <vdisplay> <vsync_start> <vsync_end> <vtotal> <hsync> <vsync> | ||
44 | Configures the specified output to use the given modeline. It can be | ||
45 | generated using *cvt*(1) and *gtf*(1) commands. See *xorg.conf*(5). | ||
46 | Only supported on DRM backend. | ||
47 | |||
48 | Example: | ||
49 | |||
50 | output HDMI-A-1 modeline 173.00 1920 2048 2248 2576 1080 1083 1088 1120 -hsync +vsync | ||
51 | |||
52 | |||
43 | *output* <name> position|pos <X> <Y> | 53 | *output* <name> position|pos <X> <Y> |
44 | Places the specified output at the specific position in the global | 54 | Places the specified output at the specific position in the global |
45 | coordinate space. The cursor may only be moved between immediately | 55 | coordinate space. The cursor may only be moved between immediately |
@@ -109,12 +119,20 @@ must be separated by one space. For example: | |||
109 | Enables or disables the specified output (all outputs are enabled by | 119 | Enables or disables the specified output (all outputs are enabled by |
110 | default). | 120 | default). |
111 | 121 | ||
122 | As opposed to the _power_ command, the output will loose its current | ||
123 | workspace and windows. | ||
124 | |||
112 | *output* <name> toggle | 125 | *output* <name> toggle |
113 | Toggle the specified output. | 126 | Toggle the specified output. |
114 | 127 | ||
115 | *output* <name> dpms on|off | 128 | *output* <name> power on|off|toggle |
116 | Enables or disables the specified output via DPMS. To turn an output off | 129 | Turns on or off the specified output. |
117 | (ie. blank the screen but keep workspaces as-is), one can set DPMS to off. | 130 | |
131 | As opposed to the _enable_ and _disable_ commands, the output keeps its | ||
132 | current workspaces and windows. | ||
133 | |||
134 | *output* <name> dpms on|off|toggle | ||
135 | Deprecated. Alias for _power_. | ||
118 | 136 | ||
119 | *output* <name> max_render_time off|<msec> | 137 | *output* <name> max_render_time off|<msec> |
120 | Controls when sway composites the output, as a positive number of | 138 | Controls when sway composites the output, as a positive number of |
@@ -142,11 +160,26 @@ must be separated by one space. For example: | |||
142 | Enables or disables adaptive synchronization (often referred to as Variable | 160 | Enables or disables adaptive synchronization (often referred to as Variable |
143 | Refresh Rate, or by the vendor-specific names FreeSync/G-Sync). | 161 | Refresh Rate, or by the vendor-specific names FreeSync/G-Sync). |
144 | 162 | ||
145 | Adaptive sync allows clients to submit frames a little to late without | 163 | Adaptive sync allows clients to submit frames a little too late without |
146 | having to wait a whole refresh period to display it on screen. Enabling | 164 | having to wait a whole refresh period to display it on screen. Enabling |
147 | adaptive sync can improve latency, but can cause flickering on some | 165 | adaptive sync can improve latency, but can cause flickering on some |
148 | hardware. | 166 | hardware. |
149 | 167 | ||
168 | *output* <name> render_bit_depth 8|10 | ||
169 | Controls the color channel bit depth at which frames are rendered; the | ||
170 | default is currently 8 bits per channel. | ||
171 | |||
172 | Setting higher values will not have an effect if hardware and software lack | ||
173 | support for such bit depths. Successfully increasing the render bit depth | ||
174 | will not necessarily increase the bit depth of the frames sent to a display. | ||
175 | An increased render bit depth may provide smoother rendering of gradients, | ||
176 | and screenshots which can more precisely store the colors of programs | ||
177 | which display high bit depth colors. | ||
178 | |||
179 | Warnings: this can break screenshot/screencast programs which have not been | ||
180 | updated to work with different bit depths. This command is experimental, | ||
181 | and may be removed or changed in the future. | ||
182 | |||
150 | # SEE ALSO | 183 | # SEE ALSO |
151 | 184 | ||
152 | *sway*(5) *sway-input*(5) | 185 | *sway*(5) *sway-input*(5) |
diff --git a/sway/sway.5.scd b/sway/sway.5.scd index 02592b5f..d8a462d3 100644 --- a/sway/sway.5.scd +++ b/sway/sway.5.scd | |||
@@ -155,7 +155,7 @@ They are expected to be used with *bindsym* or at runtime through *swaymsg*(1). | |||
155 | is specified, the view will be fullscreen across all outputs. | 155 | is specified, the view will be fullscreen across all outputs. |
156 | 156 | ||
157 | *gaps* inner|outer|horizontal|vertical|top|right|bottom|left all|current | 157 | *gaps* inner|outer|horizontal|vertical|top|right|bottom|left all|current |
158 | set|plus|minus <amount> | 158 | set|plus|minus|toggle <amount> |
159 | Changes the _inner_ or _outer_ gaps for either _all_ workspaces or the | 159 | Changes the _inner_ or _outer_ gaps for either _all_ workspaces or the |
160 | _current_ workspace. _outer_ gaps can be altered per side with _top_, | 160 | _current_ workspace. _outer_ gaps can be altered per side with _top_, |
161 | _right_, _bottom_, and _left_ or per direction with _horizontal_ and | 161 | _right_, _bottom_, and _left_ or per direction with _horizontal_ and |
@@ -176,6 +176,12 @@ set|plus|minus <amount> | |||
176 | *layout* default|splith|splitv|stacking|tabbed | 176 | *layout* default|splith|splitv|stacking|tabbed |
177 | Sets the layout mode of the focused container. | 177 | Sets the layout mode of the focused container. |
178 | 178 | ||
179 | When using the _stacking_ layout, only the focused window in the container is | ||
180 | displayed, with the opened windows' list on the top of the container. | ||
181 | |||
182 | The _tabbed_ layout is similar to _stacking_, but the windows’ list is vertically | ||
183 | split. | ||
184 | |||
179 | *layout* toggle [split|all] | 185 | *layout* toggle [split|all] |
180 | Cycles the layout mode of the focused container though a preset list of | 186 | Cycles the layout mode of the focused container though a preset list of |
181 | layouts. If no argument is given, then it cycles through stacking, tabbed | 187 | layouts. If no argument is given, then it cycles through stacking, tabbed |
@@ -210,15 +216,14 @@ set|plus|minus <amount> | |||
210 | further details. | 216 | further details. |
211 | 217 | ||
212 | *move* left|right|up|down [<px> px] | 218 | *move* left|right|up|down [<px> px] |
213 | Moves the focused container in the direction specified. If the container, | 219 | Moves the focused container in the direction specified. The optional _px_ |
214 | the optional _px_ argument specifies how many pixels to move the container. | 220 | argument specifies how many pixels to move the container. If unspecified, |
215 | If unspecified, the default is 10 pixels. Pixels are ignored when moving | 221 | the default is 10 pixels. Pixels are ignored when moving tiled containers. |
216 | tiled containers. | ||
217 | 222 | ||
218 | *move* [absolute] position <pos_x> [px|ppt] <pos_y> [px|ptt] | 223 | *move* [absolute] position <pos_x> [px|ppt] <pos_y> [px|ppt] |
219 | Moves the focused container to the specified position in the workspace. | 224 | Moves the focused container to the specified position in the workspace. |
220 | The position can be specified in pixels or percentage points, omitting | 225 | The position can be specified in pixels or percentage points, omitting |
221 | the unit defaults to pixels. If _absolute_ is used, the position is | 226 | the unit defaults to pixels. If _absolute_ is used, the position is |
222 | relative to all outputs. _absolute_ can not be used with percentage points. | 227 | relative to all outputs. _absolute_ can not be used with percentage points. |
223 | 228 | ||
224 | *move* [absolute] position center | 229 | *move* [absolute] position center |
@@ -319,8 +324,10 @@ set|plus|minus <amount> | |||
319 | established by the *seat* subcommand of the same name. See | 324 | established by the *seat* subcommand of the same name. See |
320 | *sway-input*(5) for more ways to affect inhibitors. | 325 | *sway-input*(5) for more ways to affect inhibitors. |
321 | 326 | ||
322 | *split* vertical|v|horizontal|h|toggle|t | 327 | *split* vertical|v|horizontal|h|none|n|toggle|t |
323 | Splits the current container, vertically or horizontally. When _toggle_ is | 328 | Splits the current container, vertically or horizontally. When _none_ is |
329 | specified, the effect of a previous split is undone if the current | ||
330 | container is the only child of a split parent. When _toggle_ is | ||
324 | specified, the current container is split opposite to the parent | 331 | specified, the current container is split opposite to the parent |
325 | container's layout. | 332 | container's layout. |
326 | 333 | ||
@@ -404,7 +411,7 @@ runtime. | |||
404 | a keyboard shortcuts inhibitor is active for the currently focused | 411 | a keyboard shortcuts inhibitor is active for the currently focused |
405 | window. Such inhibitors are usually requested by remote desktop and | 412 | window. Such inhibitors are usually requested by remote desktop and |
406 | virtualization software to enable the user to send keyboard shortcuts | 413 | virtualization software to enable the user to send keyboard shortcuts |
407 | to the remote or virtual session. The _--inhibited_ flag allows to | 414 | to the remote or virtual session. The _--inhibited_ flag allows one to |
408 | define bindings which will be exempt from pass-through to such | 415 | define bindings which will be exempt from pass-through to such |
409 | software. The same preference logic as for _--locked_ applies. | 416 | software. The same preference logic as for _--locked_ applies. |
410 | 417 | ||
@@ -480,6 +487,62 @@ runtime. | |||
480 | bindswitch lid:toggle exec echo "Lid moved" | 487 | bindswitch lid:toggle exec echo "Lid moved" |
481 | ``` | 488 | ``` |
482 | 489 | ||
490 | *bindgesture* [--exact] [--input-device=<device>] [--no-warn] \ | ||
491 | <gesture>[:<fingers>][:directions] <command> | ||
492 | Binds _gesture_ to execute the sway command _command_ when detected. | ||
493 | Currently supports the _hold_, _pinch_ or _swipe_ gesture. Optionally | ||
494 | can be limited to bind to a certain number of _fingers_ or, for a | ||
495 | _pinch_ or _swipe_ gesture, to certain _directions_. | ||
496 | |||
497 | [[ *type* | ||
498 | :[ *fingers* | ||
499 | :< *direction* | ||
500 | | hold | ||
501 | :- 1 - 5 | ||
502 | : none | ||
503 | | swipe | ||
504 | : 3 - 5 | ||
505 | : up, down, left, right | ||
506 | | pinch | ||
507 | : 2 - 5 | ||
508 | : all above + inward, outward, clockwise, counterclockwise | ||
509 | |||
510 | The _fingers_ can be limited to any sensible number or left empty to accept | ||
511 | any finger counts. | ||
512 | Valid directions are _up_, _down_, _left_ and _right_, as well as _inward_, | ||
513 | _outward_, _clockwise_, _counterclockwise_ for the _pinch_ gesture. | ||
514 | Multiple directions can be combined by a plus. | ||
515 | |||
516 | If a _input-device_ is given, the binding will only be executed for | ||
517 | that input device and will be executed instead of any binding that is | ||
518 | generic to all devices. By default, if you overwrite a binding, | ||
519 | swaynag will give you a warning. To silence this, use the _--no-warn_ flag. | ||
520 | |||
521 | The _--exact_ flag can be used to ensure a binding only matches when exactly | ||
522 | all specified directions are matched and nothing more. If there is matching | ||
523 | binding with _--exact_, it will be preferred. | ||
524 | |||
525 | The priority for matching bindings is as follows: input device, then | ||
526 | exact matches followed by matches with the highest number of matching | ||
527 | directions. | ||
528 | |||
529 | Gestures executed while the pointer is above a bar are not handled by sway. | ||
530 | See the respective documentation, e.g. *bindgesture* in *sway-bar*(5). | ||
531 | |||
532 | Example: | ||
533 | ``` | ||
534 | # Allow switching between workspaces with left and right swipes | ||
535 | bindgesture swipe:right workspace prev | ||
536 | bindgesture swipe:left workspace next | ||
537 | |||
538 | # Allow container movements by pinching them | ||
539 | bindgesture pinch:inward+up move up | ||
540 | bindgesture pinch:inward+down move down | ||
541 | bindgesture pinch:inward+left move left | ||
542 | bindgesture pinch:inward+right move right | ||
543 | |||
544 | ``` | ||
545 | |||
483 | *client.background* <color> | 546 | *client.background* <color> |
484 | This command is ignored and is only present for i3 compatibility. | 547 | This command is ignored and is only present for i3 compatibility. |
485 | 548 | ||
@@ -497,6 +560,12 @@ runtime. | |||
497 | *client.focused_inactive* | 560 | *client.focused_inactive* |
498 | The most recently focused view within a container which is not focused. | 561 | The most recently focused view within a container which is not focused. |
499 | 562 | ||
563 | *client.focused_tab_title* | ||
564 | A view that has focused descendant container. | ||
565 | Tab or stack container title that is the parent of the focused container | ||
566 | but is not directly focused. Defaults to focused_inactive if not | ||
567 | specified and does not use the indicator and child_border colors. | ||
568 | |||
500 | *client.placeholder* | 569 | *client.placeholder* |
501 | Ignored (present for i3 compatibility). | 570 | Ignored (present for i3 compatibility). |
502 | 571 | ||
@@ -552,6 +621,12 @@ The default colors are: | |||
552 | : #ffffff | 621 | : #ffffff |
553 | : #484e50 | 622 | : #484e50 |
554 | : #5f676a | 623 | : #5f676a |
624 | | *focused_tab_title* | ||
625 | : #333333 | ||
626 | : #5f676a | ||
627 | : #ffffff | ||
628 | : n/a | ||
629 | : n/a | ||
555 | | *unfocused* | 630 | | *unfocused* |
556 | : #333333 | 631 | : #333333 |
557 | : #222222 | 632 | : #222222 |
@@ -606,11 +681,11 @@ The default colors are: | |||
606 | after switching between workspaces. | 681 | after switching between workspaces. |
607 | 682 | ||
608 | *focus_on_window_activation* smart|urgent|focus|none | 683 | *focus_on_window_activation* smart|urgent|focus|none |
609 | This option determines what to do when an xwayland client requests | 684 | This option determines what to do when a client requests window activation. |
610 | window activation. If set to _urgent_, the urgent state will be set | 685 | If set to _urgent_, the urgent state will be set for that window. If set to |
611 | for that window. If set to _focus_, the window will become focused. | 686 | _focus_, the window will become focused. If set to _smart_, the window will |
612 | If set to _smart_, the window will become focused only if it is already | 687 | become focused only if it is already visible, otherwise the urgent state |
613 | visible, otherwise the urgent state will be set. Default is _urgent_. | 688 | will be set. Default is _urgent_. |
614 | 689 | ||
615 | *focus_wrapping* yes|no|force|workspace | 690 | *focus_wrapping* yes|no|force|workspace |
616 | This option determines what to do when attempting to focus over the edge | 691 | This option determines what to do when attempting to focus over the edge |
@@ -630,14 +705,14 @@ The default colors are: | |||
630 | should be used instead. Regardless of whether pango markup is enabled, | 705 | should be used instead. Regardless of whether pango markup is enabled, |
631 | _font_ should be specified as a pango font description. For more | 706 | _font_ should be specified as a pango font description. For more |
632 | information on pango font descriptions, see | 707 | information on pango font descriptions, see |
633 | https://developer.gnome.org/pango/stable/pango-Fonts.html#pango-font-description-from-string | 708 | https://docs.gtk.org/Pango/type_func.FontDescription.from_string.html#description |
634 | 709 | ||
635 | *force_display_urgency_hint* <timeout> [ms] | 710 | *force_display_urgency_hint* <timeout> [ms] |
636 | If an application on another workspace sets an urgency hint, switching to this | 711 | If an application on another workspace sets an urgency hint, switching to this |
637 | workspace may lead to immediate focus of the application, which also means the | 712 | workspace may lead to immediate focus of the application, which also means the |
638 | window decoration color would be immediately resetted to *client.focused*. This | 713 | window decoration color would be immediately reset to *client.focused*. This |
639 | may make it unnecessarily hard to tell which window originally raised the | 714 | may make it unnecessarily hard to tell which window originally raised the |
640 | event. This option allows to set a _timeout_ in ms to delay the urgency hint reset. | 715 | event. This option allows one to set a _timeout_ in ms to delay the urgency hint reset. |
641 | 716 | ||
642 | *titlebar_border_thickness* <thickness> | 717 | *titlebar_border_thickness* <thickness> |
643 | Thickness of the titlebar border in pixels | 718 | Thickness of the titlebar border in pixels |
@@ -690,9 +765,10 @@ The default colors are: | |||
690 | borders will only be enabled if the workspace has more than one visible | 765 | borders will only be enabled if the workspace has more than one visible |
691 | child and gaps equal to zero. | 766 | child and gaps equal to zero. |
692 | 767 | ||
693 | *smart_gaps* on|off | 768 | *smart_gaps* on|off|toggle|inverse_outer |
694 | If smart_gaps are _on_ gaps will only be enabled if a workspace has more | 769 | If smart_gaps are _on_ gaps will only be enabled if a workspace has more |
695 | than one child. | 770 | than one child. If smart_gaps are _inverse_outer_ outer gaps will only |
771 | be enabled if a workspace has exactly one child. | ||
696 | 772 | ||
697 | *mark* --add|--replace [--toggle] <identifier> | 773 | *mark* --add|--replace [--toggle] <identifier> |
698 | Marks are arbitrary labels that can be used to identify certain windows and | 774 | Marks are arbitrary labels that can be used to identify certain windows and |
@@ -771,6 +847,11 @@ The default colors are: | |||
771 | *unbindswitch* <switch>:<state> | 847 | *unbindswitch* <switch>:<state> |
772 | Removes a binding for when <switch> changes to <state>. | 848 | Removes a binding for when <switch> changes to <state>. |
773 | 849 | ||
850 | *unbindgesture* [--exact] [--input-device=<device>] \ | ||
851 | <gesture>[:<fingers>][:directions] | ||
852 | Removes a binding for the specified _gesture_, _fingers_ | ||
853 | and _directions_ combination. | ||
854 | |||
774 | *unbindsym* [--whole-window] [--border] [--exclude-titlebar] [--release] [--locked] \ | 855 | *unbindsym* [--whole-window] [--border] [--exclude-titlebar] [--release] [--locked] \ |
775 | [--to-code] [--input-device=<device>] <key combo> | 856 | [--to-code] [--input-device=<device>] <key combo> |
776 | Removes the binding for _key combo_ that was previously bound with the | 857 | Removes the binding for _key combo_ that was previously bound with the |
diff --git a/sway/swaynag.c b/sway/swaynag.c index db5a919a..4a0a6d30 100644 --- a/sway/swaynag.c +++ b/sway/swaynag.c | |||
@@ -64,6 +64,8 @@ bool swaynag_spawn(const char *swaynag_command, | |||
64 | sway_log(SWAY_ERROR, "Failed to create fork for swaynag"); | 64 | sway_log(SWAY_ERROR, "Failed to create fork for swaynag"); |
65 | goto failed; | 65 | goto failed; |
66 | } else if (pid == 0) { | 66 | } else if (pid == 0) { |
67 | restore_nofile_limit(); | ||
68 | |||
67 | pid = fork(); | 69 | pid = fork(); |
68 | if (pid < 0) { | 70 | if (pid < 0) { |
69 | sway_log_errno(SWAY_ERROR, "fork failed"); | 71 | sway_log_errno(SWAY_ERROR, "fork failed"); |
@@ -87,8 +89,8 @@ bool swaynag_spawn(const char *swaynag_command, | |||
87 | size_t length = strlen(swaynag_command) + strlen(swaynag->args) + 2; | 89 | size_t length = strlen(swaynag_command) + strlen(swaynag->args) + 2; |
88 | char *cmd = malloc(length); | 90 | char *cmd = malloc(length); |
89 | snprintf(cmd, length, "%s %s", swaynag_command, swaynag->args); | 91 | snprintf(cmd, length, "%s %s", swaynag_command, swaynag->args); |
90 | execl("/bin/sh", "/bin/sh", "-c", cmd, NULL); | 92 | execlp("sh", "sh", "-c", cmd, NULL); |
91 | sway_log_errno(SWAY_ERROR, "execl failed"); | 93 | sway_log_errno(SWAY_ERROR, "execlp failed"); |
92 | _exit(EXIT_FAILURE); | 94 | _exit(EXIT_FAILURE); |
93 | } | 95 | } |
94 | _exit(EXIT_SUCCESS); | 96 | _exit(EXIT_SUCCESS); |
diff --git a/sway/tree/arrange.c b/sway/tree/arrange.c index bac9f2fa..9c1a11e5 100644 --- a/sway/tree/arrange.c +++ b/sway/tree/arrange.c | |||
@@ -55,7 +55,7 @@ static void apply_horiz_layout(list_t *children, struct wlr_box *parent) { | |||
55 | // Calculate gap size | 55 | // Calculate gap size |
56 | double inner_gap = 0; | 56 | double inner_gap = 0; |
57 | struct sway_container *child = children->items[0]; | 57 | struct sway_container *child = children->items[0]; |
58 | struct sway_workspace *ws = child->workspace; | 58 | struct sway_workspace *ws = child->pending.workspace; |
59 | if (ws) { | 59 | if (ws) { |
60 | inner_gap = ws->gaps_inner; | 60 | inner_gap = ws->gaps_inner; |
61 | } | 61 | } |
@@ -66,7 +66,7 @@ static void apply_horiz_layout(list_t *children, struct wlr_box *parent) { | |||
66 | if (layout == L_TABBED || layout == L_STACKED) { | 66 | if (layout == L_TABBED || layout == L_STACKED) { |
67 | inner_gap = 0; | 67 | inner_gap = 0; |
68 | } | 68 | } |
69 | temp = temp->parent; | 69 | temp = temp->pending.parent; |
70 | } | 70 | } |
71 | double total_gap = fmin(inner_gap * (children->length - 1), | 71 | double total_gap = fmin(inner_gap * (children->length - 1), |
72 | fmax(0, parent->width - MIN_SANE_W * children->length)); | 72 | fmax(0, parent->width - MIN_SANE_W * children->length)); |
@@ -79,15 +79,15 @@ static void apply_horiz_layout(list_t *children, struct wlr_box *parent) { | |||
79 | for (int i = 0; i < children->length; ++i) { | 79 | for (int i = 0; i < children->length; ++i) { |
80 | struct sway_container *child = children->items[i]; | 80 | struct sway_container *child = children->items[i]; |
81 | child->child_total_width = child_total_width; | 81 | child->child_total_width = child_total_width; |
82 | child->x = child_x; | 82 | child->pending.x = child_x; |
83 | child->y = parent->y; | 83 | child->pending.y = parent->y; |
84 | child->width = round(child->width_fraction * child_total_width); | 84 | child->pending.width = round(child->width_fraction * child_total_width); |
85 | child->height = parent->height; | 85 | child->pending.height = parent->height; |
86 | child_x += child->width + inner_gap; | 86 | child_x += child->pending.width + inner_gap; |
87 | 87 | ||
88 | // Make last child use remaining width of parent | 88 | // Make last child use remaining width of parent |
89 | if (i == children->length - 1) { | 89 | if (i == children->length - 1) { |
90 | child->width = parent->x + parent->width - child->x; | 90 | child->pending.width = parent->x + parent->width - child->pending.x; |
91 | } | 91 | } |
92 | } | 92 | } |
93 | } | 93 | } |
@@ -134,7 +134,7 @@ static void apply_vert_layout(list_t *children, struct wlr_box *parent) { | |||
134 | // Calculate gap size | 134 | // Calculate gap size |
135 | double inner_gap = 0; | 135 | double inner_gap = 0; |
136 | struct sway_container *child = children->items[0]; | 136 | struct sway_container *child = children->items[0]; |
137 | struct sway_workspace *ws = child->workspace; | 137 | struct sway_workspace *ws = child->pending.workspace; |
138 | if (ws) { | 138 | if (ws) { |
139 | inner_gap = ws->gaps_inner; | 139 | inner_gap = ws->gaps_inner; |
140 | } | 140 | } |
@@ -145,7 +145,7 @@ static void apply_vert_layout(list_t *children, struct wlr_box *parent) { | |||
145 | if (layout == L_TABBED || layout == L_STACKED) { | 145 | if (layout == L_TABBED || layout == L_STACKED) { |
146 | inner_gap = 0; | 146 | inner_gap = 0; |
147 | } | 147 | } |
148 | temp = temp->parent; | 148 | temp = temp->pending.parent; |
149 | } | 149 | } |
150 | double total_gap = fmin(inner_gap * (children->length - 1), | 150 | double total_gap = fmin(inner_gap * (children->length - 1), |
151 | fmax(0, parent->height - MIN_SANE_H * children->length)); | 151 | fmax(0, parent->height - MIN_SANE_H * children->length)); |
@@ -158,15 +158,15 @@ static void apply_vert_layout(list_t *children, struct wlr_box *parent) { | |||
158 | for (int i = 0; i < children->length; ++i) { | 158 | for (int i = 0; i < children->length; ++i) { |
159 | struct sway_container *child = children->items[i]; | 159 | struct sway_container *child = children->items[i]; |
160 | child->child_total_height = child_total_height; | 160 | child->child_total_height = child_total_height; |
161 | child->x = parent->x; | 161 | child->pending.x = parent->x; |
162 | child->y = child_y; | 162 | child->pending.y = child_y; |
163 | child->width = parent->width; | 163 | child->pending.width = parent->width; |
164 | child->height = round(child->height_fraction * child_total_height); | 164 | child->pending.height = round(child->height_fraction * child_total_height); |
165 | child_y += child->height + inner_gap; | 165 | child_y += child->pending.height + inner_gap; |
166 | 166 | ||
167 | // Make last child use remaining height of parent | 167 | // Make last child use remaining height of parent |
168 | if (i == children->length - 1) { | 168 | if (i == children->length - 1) { |
169 | child->height = parent->y + parent->height - child->y; | 169 | child->pending.height = parent->y + parent->height - child->pending.y; |
170 | } | 170 | } |
171 | } | 171 | } |
172 | } | 172 | } |
@@ -178,10 +178,10 @@ static void apply_tabbed_layout(list_t *children, struct wlr_box *parent) { | |||
178 | for (int i = 0; i < children->length; ++i) { | 178 | for (int i = 0; i < children->length; ++i) { |
179 | struct sway_container *child = children->items[i]; | 179 | struct sway_container *child = children->items[i]; |
180 | int parent_offset = child->view ? 0 : container_titlebar_height(); | 180 | int parent_offset = child->view ? 0 : container_titlebar_height(); |
181 | child->x = parent->x; | 181 | child->pending.x = parent->x; |
182 | child->y = parent->y + parent_offset; | 182 | child->pending.y = parent->y + parent_offset; |
183 | child->width = parent->width; | 183 | child->pending.width = parent->width; |
184 | child->height = parent->height - parent_offset; | 184 | child->pending.height = parent->height - parent_offset; |
185 | } | 185 | } |
186 | } | 186 | } |
187 | 187 | ||
@@ -193,10 +193,10 @@ static void apply_stacked_layout(list_t *children, struct wlr_box *parent) { | |||
193 | struct sway_container *child = children->items[i]; | 193 | struct sway_container *child = children->items[i]; |
194 | int parent_offset = child->view ? 0 : | 194 | int parent_offset = child->view ? 0 : |
195 | container_titlebar_height() * children->length; | 195 | container_titlebar_height() * children->length; |
196 | child->x = parent->x; | 196 | child->pending.x = parent->x; |
197 | child->y = parent->y + parent_offset; | 197 | child->pending.y = parent->y + parent_offset; |
198 | child->width = parent->width; | 198 | child->pending.width = parent->width; |
199 | child->height = parent->height - parent_offset; | 199 | child->pending.height = parent->height - parent_offset; |
200 | } | 200 | } |
201 | } | 201 | } |
202 | 202 | ||
@@ -246,7 +246,7 @@ void arrange_container(struct sway_container *container) { | |||
246 | } | 246 | } |
247 | struct wlr_box box; | 247 | struct wlr_box box; |
248 | container_get_box(container, &box); | 248 | container_get_box(container, &box); |
249 | arrange_children(container->children, container->layout, &box); | 249 | arrange_children(container->pending.children, container->pending.layout, &box); |
250 | node_set_dirty(&container->node); | 250 | node_set_dirty(&container->node); |
251 | } | 251 | } |
252 | 252 | ||
@@ -278,8 +278,8 @@ void arrange_workspace(struct sway_workspace *workspace) { | |||
278 | for (int i = 0; i < workspace->floating->length; ++i) { | 278 | for (int i = 0; i < workspace->floating->length; ++i) { |
279 | struct sway_container *floater = workspace->floating->items[i]; | 279 | struct sway_container *floater = workspace->floating->items[i]; |
280 | container_floating_translate(floater, diff_x, diff_y); | 280 | container_floating_translate(floater, diff_x, diff_y); |
281 | double center_x = floater->x + floater->width / 2; | 281 | double center_x = floater->pending.x + floater->pending.width / 2; |
282 | double center_y = floater->y + floater->height / 2; | 282 | double center_y = floater->pending.y + floater->pending.height / 2; |
283 | struct wlr_box workspace_box; | 283 | struct wlr_box workspace_box; |
284 | workspace_get_box(workspace, &workspace_box); | 284 | workspace_get_box(workspace, &workspace_box); |
285 | if (!wlr_box_contains_point(&workspace_box, center_x, center_y)) { | 285 | if (!wlr_box_contains_point(&workspace_box, center_x, center_y)) { |
@@ -294,10 +294,10 @@ void arrange_workspace(struct sway_workspace *workspace) { | |||
294 | workspace->x, workspace->y); | 294 | workspace->x, workspace->y); |
295 | if (workspace->fullscreen) { | 295 | if (workspace->fullscreen) { |
296 | struct sway_container *fs = workspace->fullscreen; | 296 | struct sway_container *fs = workspace->fullscreen; |
297 | fs->x = output->lx; | 297 | fs->pending.x = output->lx; |
298 | fs->y = output->ly; | 298 | fs->pending.y = output->ly; |
299 | fs->width = output->width; | 299 | fs->pending.width = output->width; |
300 | fs->height = output->height; | 300 | fs->pending.height = output->height; |
301 | arrange_container(fs); | 301 | arrange_container(fs); |
302 | } else { | 302 | } else { |
303 | struct wlr_box box; | 303 | struct wlr_box box; |
@@ -311,12 +311,13 @@ void arrange_output(struct sway_output *output) { | |||
311 | if (config->reloading) { | 311 | if (config->reloading) { |
312 | return; | 312 | return; |
313 | } | 313 | } |
314 | const struct wlr_box *output_box = wlr_output_layout_get_box( | 314 | struct wlr_box output_box; |
315 | root->output_layout, output->wlr_output); | 315 | wlr_output_layout_get_box(root->output_layout, |
316 | output->lx = output_box->x; | 316 | output->wlr_output, &output_box); |
317 | output->ly = output_box->y; | 317 | output->lx = output_box.x; |
318 | output->width = output_box->width; | 318 | output->ly = output_box.y; |
319 | output->height = output_box->height; | 319 | output->width = output_box.width; |
320 | output->height = output_box.height; | ||
320 | 321 | ||
321 | for (int i = 0; i < output->workspaces->length; ++i) { | 322 | for (int i = 0; i < output->workspaces->length; ++i) { |
322 | struct sway_workspace *workspace = output->workspaces->items[i]; | 323 | struct sway_workspace *workspace = output->workspaces->items[i]; |
@@ -328,19 +329,19 @@ void arrange_root(void) { | |||
328 | if (config->reloading) { | 329 | if (config->reloading) { |
329 | return; | 330 | return; |
330 | } | 331 | } |
331 | const struct wlr_box *layout_box = | 332 | struct wlr_box layout_box; |
332 | wlr_output_layout_get_box(root->output_layout, NULL); | 333 | wlr_output_layout_get_box(root->output_layout, NULL, &layout_box); |
333 | root->x = layout_box->x; | 334 | root->x = layout_box.x; |
334 | root->y = layout_box->y; | 335 | root->y = layout_box.y; |
335 | root->width = layout_box->width; | 336 | root->width = layout_box.width; |
336 | root->height = layout_box->height; | 337 | root->height = layout_box.height; |
337 | 338 | ||
338 | if (root->fullscreen_global) { | 339 | if (root->fullscreen_global) { |
339 | struct sway_container *fs = root->fullscreen_global; | 340 | struct sway_container *fs = root->fullscreen_global; |
340 | fs->x = root->x; | 341 | fs->pending.x = root->x; |
341 | fs->y = root->y; | 342 | fs->pending.y = root->y; |
342 | fs->width = root->width; | 343 | fs->pending.width = root->width; |
343 | fs->height = root->height; | 344 | fs->pending.height = root->height; |
344 | arrange_container(fs); | 345 | arrange_container(fs); |
345 | } else { | 346 | } else { |
346 | for (int i = 0; i < root->outputs->length; ++i) { | 347 | for (int i = 0; i < root->outputs->length; ++i) { |
diff --git a/sway/tree/container.c b/sway/tree/container.c index 6a9ce1c4..04ef965f 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c | |||
@@ -1,12 +1,18 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | 1 | #define _POSIX_C_SOURCE 200809L |
2 | #include <assert.h> | 2 | #include <assert.h> |
3 | #include <drm_fourcc.h> | ||
3 | #include <stdint.h> | 4 | #include <stdint.h> |
4 | #include <stdlib.h> | 5 | #include <stdlib.h> |
5 | #include <string.h> | 6 | #include <string.h> |
6 | #include <strings.h> | 7 | #include <strings.h> |
8 | #include <sys/stat.h> | ||
7 | #include <wayland-server-core.h> | 9 | #include <wayland-server-core.h> |
10 | #include <wlr/types/wlr_linux_dmabuf_v1.h> | ||
8 | #include <wlr/types/wlr_output_layout.h> | 11 | #include <wlr/types/wlr_output_layout.h> |
9 | #include "cairo.h" | 12 | #include <wlr/types/wlr_subcompositor.h> |
13 | #include <wlr/render/drm_format_set.h> | ||
14 | #include "linux-dmabuf-unstable-v1-protocol.h" | ||
15 | #include "cairo_util.h" | ||
10 | #include "pango.h" | 16 | #include "pango.h" |
11 | #include "sway/config.h" | 17 | #include "sway/config.h" |
12 | #include "sway/desktop.h" | 18 | #include "sway/desktop.h" |
@@ -19,6 +25,7 @@ | |||
19 | #include "sway/tree/arrange.h" | 25 | #include "sway/tree/arrange.h" |
20 | #include "sway/tree/view.h" | 26 | #include "sway/tree/view.h" |
21 | #include "sway/tree/workspace.h" | 27 | #include "sway/tree/workspace.h" |
28 | #include "sway/xdg_decoration.h" | ||
22 | #include "list.h" | 29 | #include "list.h" |
23 | #include "log.h" | 30 | #include "log.h" |
24 | #include "stringop.h" | 31 | #include "stringop.h" |
@@ -30,12 +37,12 @@ struct sway_container *container_create(struct sway_view *view) { | |||
30 | return NULL; | 37 | return NULL; |
31 | } | 38 | } |
32 | node_init(&c->node, N_CONTAINER, c); | 39 | node_init(&c->node, N_CONTAINER, c); |
33 | c->layout = L_NONE; | 40 | c->pending.layout = L_NONE; |
34 | c->view = view; | 41 | c->view = view; |
35 | c->alpha = 1.0f; | 42 | c->alpha = 1.0f; |
36 | 43 | ||
37 | if (!view) { | 44 | if (!view) { |
38 | c->children = create_list(); | 45 | c->pending.children = create_list(); |
39 | c->current.children = create_list(); | 46 | c->current.children = create_list(); |
40 | } | 47 | } |
41 | c->marks = create_list(); | 48 | c->marks = create_list(); |
@@ -62,7 +69,8 @@ void container_destroy(struct sway_container *con) { | |||
62 | wlr_texture_destroy(con->title_focused_inactive); | 69 | wlr_texture_destroy(con->title_focused_inactive); |
63 | wlr_texture_destroy(con->title_unfocused); | 70 | wlr_texture_destroy(con->title_unfocused); |
64 | wlr_texture_destroy(con->title_urgent); | 71 | wlr_texture_destroy(con->title_urgent); |
65 | list_free(con->children); | 72 | wlr_texture_destroy(con->title_focused_tab_title); |
73 | list_free(con->pending.children); | ||
66 | list_free(con->current.children); | 74 | list_free(con->current.children); |
67 | list_free(con->outputs); | 75 | list_free(con->outputs); |
68 | 76 | ||
@@ -71,11 +79,10 @@ void container_destroy(struct sway_container *con) { | |||
71 | wlr_texture_destroy(con->marks_focused_inactive); | 79 | wlr_texture_destroy(con->marks_focused_inactive); |
72 | wlr_texture_destroy(con->marks_unfocused); | 80 | wlr_texture_destroy(con->marks_unfocused); |
73 | wlr_texture_destroy(con->marks_urgent); | 81 | wlr_texture_destroy(con->marks_urgent); |
82 | wlr_texture_destroy(con->marks_focused_tab_title); | ||
74 | 83 | ||
75 | if (con->view) { | 84 | if (con->view && con->view->container == con) { |
76 | if (con->view->container == con) { | 85 | con->view->container = NULL; |
77 | con->view->container = NULL; | ||
78 | } | ||
79 | if (con->view->destroying) { | 86 | if (con->view->destroying) { |
80 | view_destroy(con->view); | 87 | view_destroy(con->view); |
81 | } | 88 | } |
@@ -90,10 +97,10 @@ void container_begin_destroy(struct sway_container *con) { | |||
90 | } | 97 | } |
91 | // The workspace must have the fullscreen pointer cleared so that the | 98 | // The workspace must have the fullscreen pointer cleared so that the |
92 | // seat code can find an appropriate new focus. | 99 | // seat code can find an appropriate new focus. |
93 | if (con->fullscreen_mode == FULLSCREEN_WORKSPACE && con->workspace) { | 100 | if (con->pending.fullscreen_mode == FULLSCREEN_WORKSPACE && con->pending.workspace) { |
94 | con->workspace->fullscreen = NULL; | 101 | con->pending.workspace->fullscreen = NULL; |
95 | } | 102 | } |
96 | if (con->scratchpad && con->fullscreen_mode == FULLSCREEN_GLOBAL) { | 103 | if (con->scratchpad && con->pending.fullscreen_mode == FULLSCREEN_GLOBAL) { |
97 | container_fullscreen_disable(con); | 104 | container_fullscreen_disable(con); |
98 | } | 105 | } |
99 | 106 | ||
@@ -108,11 +115,11 @@ void container_begin_destroy(struct sway_container *con) { | |||
108 | root_scratchpad_remove_container(con); | 115 | root_scratchpad_remove_container(con); |
109 | } | 116 | } |
110 | 117 | ||
111 | if (con->fullscreen_mode == FULLSCREEN_GLOBAL) { | 118 | if (con->pending.fullscreen_mode == FULLSCREEN_GLOBAL) { |
112 | container_fullscreen_disable(con); | 119 | container_fullscreen_disable(con); |
113 | } | 120 | } |
114 | 121 | ||
115 | if (con->parent || con->workspace) { | 122 | if (con->pending.parent || con->pending.workspace) { |
116 | container_detach(con); | 123 | container_detach(con); |
117 | } | 124 | } |
118 | } | 125 | } |
@@ -121,12 +128,12 @@ void container_reap_empty(struct sway_container *con) { | |||
121 | if (con->view) { | 128 | if (con->view) { |
122 | return; | 129 | return; |
123 | } | 130 | } |
124 | struct sway_workspace *ws = con->workspace; | 131 | struct sway_workspace *ws = con->pending.workspace; |
125 | while (con) { | 132 | while (con) { |
126 | if (con->children->length) { | 133 | if (con->pending.children->length) { |
127 | return; | 134 | return; |
128 | } | 135 | } |
129 | struct sway_container *parent = con->parent; | 136 | struct sway_container *parent = con->pending.parent; |
130 | container_begin_destroy(con); | 137 | container_begin_destroy(con); |
131 | con = parent; | 138 | con = parent; |
132 | } | 139 | } |
@@ -139,9 +146,9 @@ struct sway_container *container_flatten(struct sway_container *container) { | |||
139 | if (container->view) { | 146 | if (container->view) { |
140 | return NULL; | 147 | return NULL; |
141 | } | 148 | } |
142 | while (container && container->children->length == 1) { | 149 | while (container && container->pending.children->length == 1) { |
143 | struct sway_container *child = container->children->items[0]; | 150 | struct sway_container *child = container->pending.children->items[0]; |
144 | struct sway_container *parent = container->parent; | 151 | struct sway_container *parent = container->pending.parent; |
145 | container_replace(container, child); | 152 | container_replace(container, child); |
146 | container_begin_destroy(container); | 153 | container_begin_destroy(container); |
147 | container = parent; | 154 | container = parent; |
@@ -151,11 +158,11 @@ struct sway_container *container_flatten(struct sway_container *container) { | |||
151 | 158 | ||
152 | struct sway_container *container_find_child(struct sway_container *container, | 159 | struct sway_container *container_find_child(struct sway_container *container, |
153 | bool (*test)(struct sway_container *con, void *data), void *data) { | 160 | bool (*test)(struct sway_container *con, void *data), void *data) { |
154 | if (!container->children) { | 161 | if (!container->pending.children) { |
155 | return NULL; | 162 | return NULL; |
156 | } | 163 | } |
157 | for (int i = 0; i < container->children->length; ++i) { | 164 | for (int i = 0; i < container->pending.children->length; ++i) { |
158 | struct sway_container *child = container->children->items[i]; | 165 | struct sway_container *child = container->pending.children->items[i]; |
159 | if (test(child, data)) { | 166 | if (test(child, data)) { |
160 | return child; | 167 | return child; |
161 | } | 168 | } |
@@ -187,7 +194,7 @@ static struct sway_container *surface_at_view(struct sway_container *con, double | |||
187 | #endif | 194 | #endif |
188 | case SWAY_VIEW_XDG_SHELL: | 195 | case SWAY_VIEW_XDG_SHELL: |
189 | _surface = wlr_xdg_surface_surface_at( | 196 | _surface = wlr_xdg_surface_surface_at( |
190 | view->wlr_xdg_surface, | 197 | view->wlr_xdg_toplevel->base, |
191 | view_sx, view_sy, &_sx, &_sy); | 198 | view_sx, view_sy, &_sx, &_sy); |
192 | break; | 199 | break; |
193 | } | 200 | } |
@@ -310,7 +317,7 @@ static struct sway_container *floating_container_at(double lx, double ly, | |||
310 | return NULL; | 317 | return NULL; |
311 | } | 318 | } |
312 | 319 | ||
313 | struct sway_container *view_container_at(struct sway_node *parent, | 320 | static struct sway_container *view_container_content_at(struct sway_node *parent, |
314 | double lx, double ly, | 321 | double lx, double ly, |
315 | struct wlr_surface **surface, double *sx, double *sy) { | 322 | struct wlr_surface **surface, double *sx, double *sy) { |
316 | if (!sway_assert(node_is_view(parent), "Expected a view")) { | 323 | if (!sway_assert(node_is_view(parent), "Expected a view")) { |
@@ -319,10 +326,33 @@ struct sway_container *view_container_at(struct sway_node *parent, | |||
319 | 326 | ||
320 | struct sway_container *container = parent->sway_container; | 327 | struct sway_container *container = parent->sway_container; |
321 | struct wlr_box box = { | 328 | struct wlr_box box = { |
322 | .x = container->x, | 329 | .x = container->pending.content_x, |
323 | .y = container->y, | 330 | .y = container->pending.content_y, |
324 | .width = container->width, | 331 | .width = container->pending.content_width, |
325 | .height = container->height, | 332 | .height = container->pending.content_height, |
333 | }; | ||
334 | |||
335 | if (wlr_box_contains_point(&box, lx, ly)) { | ||
336 | surface_at_view(parent->sway_container, lx, ly, surface, sx, sy); | ||
337 | return container; | ||
338 | } | ||
339 | |||
340 | return NULL; | ||
341 | } | ||
342 | |||
343 | static struct sway_container *view_container_at(struct sway_node *parent, | ||
344 | double lx, double ly, | ||
345 | struct wlr_surface **surface, double *sx, double *sy) { | ||
346 | if (!sway_assert(node_is_view(parent), "Expected a view")) { | ||
347 | return NULL; | ||
348 | } | ||
349 | |||
350 | struct sway_container *container = parent->sway_container; | ||
351 | struct wlr_box box = { | ||
352 | .x = container->pending.x, | ||
353 | .y = container->pending.y, | ||
354 | .width = container->pending.width, | ||
355 | .height = container->pending.height, | ||
326 | }; | 356 | }; |
327 | 357 | ||
328 | if (wlr_box_contains_point(&box, lx, ly)) { | 358 | if (wlr_box_contains_point(&box, lx, ly)) { |
@@ -357,19 +387,17 @@ struct sway_container *tiling_container_at(struct sway_node *parent, | |||
357 | } | 387 | } |
358 | 388 | ||
359 | static bool surface_is_popup(struct wlr_surface *surface) { | 389 | static bool surface_is_popup(struct wlr_surface *surface) { |
360 | if (wlr_surface_is_xdg_surface(surface)) { | 390 | while (!wlr_surface_is_xdg_surface(surface)) { |
361 | struct wlr_xdg_surface *xdg_surface = | 391 | if (!wlr_surface_is_subsurface(surface)) { |
362 | wlr_xdg_surface_from_wlr_surface(surface); | 392 | return false; |
363 | while (xdg_surface && xdg_surface->role != WLR_XDG_SURFACE_ROLE_NONE) { | ||
364 | if (xdg_surface->role == WLR_XDG_SURFACE_ROLE_POPUP) { | ||
365 | return true; | ||
366 | } | ||
367 | xdg_surface = xdg_surface->toplevel->parent; | ||
368 | } | 393 | } |
369 | return false; | 394 | struct wlr_subsurface *subsurface = |
395 | wlr_subsurface_from_wlr_surface(surface); | ||
396 | surface = subsurface->parent; | ||
370 | } | 397 | } |
371 | 398 | struct wlr_xdg_surface *xdg_surface = | |
372 | return false; | 399 | wlr_xdg_surface_from_wlr_surface(surface); |
400 | return xdg_surface->role == WLR_XDG_SURFACE_ROLE_POPUP; | ||
373 | } | 401 | } |
374 | 402 | ||
375 | struct sway_container *container_at(struct sway_workspace *workspace, | 403 | struct sway_container *container_at(struct sway_workspace *workspace, |
@@ -394,7 +422,7 @@ struct sway_container *container_at(struct sway_workspace *workspace, | |||
394 | } | 422 | } |
395 | // Tiling (focused) | 423 | // Tiling (focused) |
396 | if (focus && focus->view && !is_floating) { | 424 | if (focus && focus->view && !is_floating) { |
397 | if ((c = surface_at_view(focus, lx, ly, surface, sx, sy))) { | 425 | if ((c = view_container_content_at(&focus->node, lx, ly, surface, sx, sy))) { |
398 | return c; | 426 | return c; |
399 | } | 427 | } |
400 | } | 428 | } |
@@ -408,19 +436,41 @@ struct sway_container *container_at(struct sway_workspace *workspace, | |||
408 | void container_for_each_child(struct sway_container *container, | 436 | void container_for_each_child(struct sway_container *container, |
409 | void (*f)(struct sway_container *container, void *data), | 437 | void (*f)(struct sway_container *container, void *data), |
410 | void *data) { | 438 | void *data) { |
411 | if (container->children) { | 439 | if (container->pending.children) { |
412 | for (int i = 0; i < container->children->length; ++i) { | 440 | for (int i = 0; i < container->pending.children->length; ++i) { |
413 | struct sway_container *child = container->children->items[i]; | 441 | struct sway_container *child = container->pending.children->items[i]; |
414 | f(child, data); | 442 | f(child, data); |
415 | container_for_each_child(child, f, data); | 443 | container_for_each_child(child, f, data); |
416 | } | 444 | } |
417 | } | 445 | } |
418 | } | 446 | } |
419 | 447 | ||
448 | struct sway_container *container_obstructing_fullscreen_container(struct sway_container *container) | ||
449 | { | ||
450 | struct sway_workspace *workspace = container->pending.workspace; | ||
451 | |||
452 | if (workspace && workspace->fullscreen && !container_is_fullscreen_or_child(container)) { | ||
453 | if (container_is_transient_for(container, workspace->fullscreen)) { | ||
454 | return NULL; | ||
455 | } | ||
456 | return workspace->fullscreen; | ||
457 | } | ||
458 | |||
459 | struct sway_container *fullscreen_global = root->fullscreen_global; | ||
460 | if (fullscreen_global && container != fullscreen_global && !container_has_ancestor(container, fullscreen_global)) { | ||
461 | if (container_is_transient_for(container, fullscreen_global)) { | ||
462 | return NULL; | ||
463 | } | ||
464 | return fullscreen_global; | ||
465 | } | ||
466 | |||
467 | return NULL; | ||
468 | } | ||
469 | |||
420 | bool container_has_ancestor(struct sway_container *descendant, | 470 | bool container_has_ancestor(struct sway_container *descendant, |
421 | struct sway_container *ancestor) { | 471 | struct sway_container *ancestor) { |
422 | while (descendant) { | 472 | while (descendant) { |
423 | descendant = descendant->parent; | 473 | descendant = descendant->pending.parent; |
424 | if (descendant == ancestor) { | 474 | if (descendant == ancestor) { |
425 | return true; | 475 | return true; |
426 | } | 476 | } |
@@ -446,23 +496,13 @@ struct sway_output *container_get_effective_output(struct sway_container *con) { | |||
446 | return con->outputs->items[con->outputs->length - 1]; | 496 | return con->outputs->items[con->outputs->length - 1]; |
447 | } | 497 | } |
448 | 498 | ||
449 | static void update_title_texture(struct sway_container *con, | 499 | static void render_titlebar_text_texture(struct sway_output *output, |
450 | struct wlr_texture **texture, struct border_colors *class) { | 500 | struct sway_container *con, struct wlr_texture **texture, |
451 | struct sway_output *output = container_get_effective_output(con); | 501 | struct border_colors *class, bool pango_markup, char *text) { |
452 | if (!output) { | ||
453 | return; | ||
454 | } | ||
455 | if (*texture) { | ||
456 | wlr_texture_destroy(*texture); | ||
457 | *texture = NULL; | ||
458 | } | ||
459 | if (!con->formatted_title) { | ||
460 | return; | ||
461 | } | ||
462 | |||
463 | double scale = output->wlr_output->scale; | 502 | double scale = output->wlr_output->scale; |
464 | int width = 0; | 503 | int width = 0; |
465 | int height = con->title_height * scale; | 504 | int height = config->font_height * scale; |
505 | int baseline; | ||
466 | 506 | ||
467 | // We must use a non-nil cairo_t for cairo_set_font_options to work. | 507 | // We must use a non-nil cairo_t for cairo_set_font_options to work. |
468 | // Therefore, we cannot use cairo_create(NULL). | 508 | // Therefore, we cannot use cairo_create(NULL). |
@@ -480,13 +520,28 @@ static void update_title_texture(struct sway_container *con, | |||
480 | to_cairo_subpixel_order(output->wlr_output->subpixel)); | 520 | to_cairo_subpixel_order(output->wlr_output->subpixel)); |
481 | } | 521 | } |
482 | cairo_set_font_options(c, fo); | 522 | cairo_set_font_options(c, fo); |
483 | get_text_size(c, config->font, &width, NULL, NULL, scale, | 523 | get_text_size(c, config->font_description, &width, NULL, &baseline, scale, |
484 | config->pango_markup, "%s", con->formatted_title); | 524 | config->pango_markup, "%s", text); |
485 | cairo_surface_destroy(dummy_surface); | 525 | cairo_surface_destroy(dummy_surface); |
486 | cairo_destroy(c); | 526 | cairo_destroy(c); |
487 | 527 | ||
528 | if (width == 0 || height == 0) { | ||
529 | return; | ||
530 | } | ||
531 | |||
532 | if (height > config->font_height * scale) { | ||
533 | height = config->font_height * scale; | ||
534 | } | ||
535 | |||
488 | cairo_surface_t *surface = cairo_image_surface_create( | 536 | cairo_surface_t *surface = cairo_image_surface_create( |
489 | CAIRO_FORMAT_ARGB32, width, height); | 537 | CAIRO_FORMAT_ARGB32, width, height); |
538 | cairo_status_t status = cairo_surface_status(surface); | ||
539 | if (status != CAIRO_STATUS_SUCCESS) { | ||
540 | sway_log(SWAY_ERROR, "cairo_image_surface_create failed: %s", | ||
541 | cairo_status_to_string(status)); | ||
542 | return; | ||
543 | } | ||
544 | |||
490 | cairo_t *cairo = cairo_create(surface); | 545 | cairo_t *cairo = cairo_create(surface); |
491 | cairo_set_antialias(cairo, CAIRO_ANTIALIAS_BEST); | 546 | cairo_set_antialias(cairo, CAIRO_ANTIALIAS_BEST); |
492 | cairo_set_font_options(cairo, fo); | 547 | cairo_set_font_options(cairo, fo); |
@@ -497,23 +552,39 @@ static void update_title_texture(struct sway_container *con, | |||
497 | PangoContext *pango = pango_cairo_create_context(cairo); | 552 | PangoContext *pango = pango_cairo_create_context(cairo); |
498 | cairo_set_source_rgba(cairo, class->text[0], class->text[1], | 553 | cairo_set_source_rgba(cairo, class->text[0], class->text[1], |
499 | class->text[2], class->text[3]); | 554 | class->text[2], class->text[3]); |
500 | cairo_move_to(cairo, 0, 0); | 555 | cairo_move_to(cairo, 0, config->font_baseline * scale - baseline); |
501 | 556 | ||
502 | pango_printf(cairo, config->font, scale, config->pango_markup, | 557 | render_text(cairo, config->font_description, scale, pango_markup, "%s", text); |
503 | "%s", con->formatted_title); | ||
504 | 558 | ||
505 | cairo_surface_flush(surface); | 559 | cairo_surface_flush(surface); |
506 | unsigned char *data = cairo_image_surface_get_data(surface); | 560 | unsigned char *data = cairo_image_surface_get_data(surface); |
507 | int stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, width); | 561 | int stride = cairo_image_surface_get_stride(surface); |
508 | struct wlr_renderer *renderer = wlr_backend_get_renderer( | 562 | struct wlr_renderer *renderer = output->wlr_output->renderer; |
509 | output->wlr_output->backend); | ||
510 | *texture = wlr_texture_from_pixels( | 563 | *texture = wlr_texture_from_pixels( |
511 | renderer, WL_SHM_FORMAT_ARGB8888, stride, width, height, data); | 564 | renderer, DRM_FORMAT_ARGB8888, stride, width, height, data); |
512 | cairo_surface_destroy(surface); | 565 | cairo_surface_destroy(surface); |
513 | g_object_unref(pango); | 566 | g_object_unref(pango); |
514 | cairo_destroy(cairo); | 567 | cairo_destroy(cairo); |
515 | } | 568 | } |
516 | 569 | ||
570 | static void update_title_texture(struct sway_container *con, | ||
571 | struct wlr_texture **texture, struct border_colors *class) { | ||
572 | struct sway_output *output = container_get_effective_output(con); | ||
573 | if (!output) { | ||
574 | return; | ||
575 | } | ||
576 | if (*texture) { | ||
577 | wlr_texture_destroy(*texture); | ||
578 | *texture = NULL; | ||
579 | } | ||
580 | if (!con->formatted_title) { | ||
581 | return; | ||
582 | } | ||
583 | |||
584 | render_titlebar_text_texture(output, con, texture, class, | ||
585 | config->pango_markup, con->formatted_title); | ||
586 | } | ||
587 | |||
517 | void container_update_title_textures(struct sway_container *container) { | 588 | void container_update_title_textures(struct sway_container *container) { |
518 | update_title_texture(container, &container->title_focused, | 589 | update_title_texture(container, &container->title_focused, |
519 | &config->border_colors.focused); | 590 | &config->border_colors.focused); |
@@ -523,24 +594,11 @@ void container_update_title_textures(struct sway_container *container) { | |||
523 | &config->border_colors.unfocused); | 594 | &config->border_colors.unfocused); |
524 | update_title_texture(container, &container->title_urgent, | 595 | update_title_texture(container, &container->title_urgent, |
525 | &config->border_colors.urgent); | 596 | &config->border_colors.urgent); |
597 | update_title_texture(container, &container->title_focused_tab_title, | ||
598 | &config->border_colors.focused_tab_title); | ||
526 | container_damage_whole(container); | 599 | container_damage_whole(container); |
527 | } | 600 | } |
528 | 601 | ||
529 | void container_calculate_title_height(struct sway_container *container) { | ||
530 | if (!container->formatted_title) { | ||
531 | container->title_height = 0; | ||
532 | return; | ||
533 | } | ||
534 | cairo_t *cairo = cairo_create(NULL); | ||
535 | int height; | ||
536 | int baseline; | ||
537 | get_text_size(cairo, config->font, NULL, &height, &baseline, 1, | ||
538 | config->pango_markup, "%s", container->formatted_title); | ||
539 | cairo_destroy(cairo); | ||
540 | container->title_height = height; | ||
541 | container->title_baseline = baseline; | ||
542 | } | ||
543 | |||
544 | /** | 602 | /** |
545 | * Calculate and return the length of the tree representation. | 603 | * Calculate and return the length of the tree representation. |
546 | * An example tree representation is: V[Terminal, Firefox] | 604 | * An example tree representation is: V[Terminal, Firefox] |
@@ -596,23 +654,22 @@ size_t container_build_representation(enum sway_container_layout layout, | |||
596 | 654 | ||
597 | void container_update_representation(struct sway_container *con) { | 655 | void container_update_representation(struct sway_container *con) { |
598 | if (!con->view) { | 656 | if (!con->view) { |
599 | size_t len = container_build_representation(con->layout, | 657 | size_t len = container_build_representation(con->pending.layout, |
600 | con->children, NULL); | 658 | con->pending.children, NULL); |
601 | free(con->formatted_title); | 659 | free(con->formatted_title); |
602 | con->formatted_title = calloc(len + 1, sizeof(char)); | 660 | con->formatted_title = calloc(len + 1, sizeof(char)); |
603 | if (!sway_assert(con->formatted_title, | 661 | if (!sway_assert(con->formatted_title, |
604 | "Unable to allocate title string")) { | 662 | "Unable to allocate title string")) { |
605 | return; | 663 | return; |
606 | } | 664 | } |
607 | container_build_representation(con->layout, con->children, | 665 | container_build_representation(con->pending.layout, con->pending.children, |
608 | con->formatted_title); | 666 | con->formatted_title); |
609 | container_calculate_title_height(con); | ||
610 | container_update_title_textures(con); | 667 | container_update_title_textures(con); |
611 | } | 668 | } |
612 | if (con->parent) { | 669 | if (con->pending.parent) { |
613 | container_update_representation(con->parent); | 670 | container_update_representation(con->pending.parent); |
614 | } else if (con->workspace) { | 671 | } else if (con->pending.workspace) { |
615 | workspace_update_representation(con->workspace); | 672 | workspace_update_representation(con->pending.workspace); |
616 | } | 673 | } |
617 | } | 674 | } |
618 | 675 | ||
@@ -638,12 +695,13 @@ void floating_calculate_constraints(int *min_width, int *max_width, | |||
638 | *min_height = config->floating_minimum_height; | 695 | *min_height = config->floating_minimum_height; |
639 | } | 696 | } |
640 | 697 | ||
641 | struct wlr_box *box = wlr_output_layout_get_box(root->output_layout, NULL); | 698 | struct wlr_box box; |
699 | wlr_output_layout_get_box(root->output_layout, NULL, &box); | ||
642 | 700 | ||
643 | if (config->floating_maximum_width == -1) { // no maximum | 701 | if (config->floating_maximum_width == -1) { // no maximum |
644 | *max_width = INT_MAX; | 702 | *max_width = INT_MAX; |
645 | } else if (config->floating_maximum_width == 0) { // automatic | 703 | } else if (config->floating_maximum_width == 0) { // automatic |
646 | *max_width = box->width; | 704 | *max_width = box.width; |
647 | } else { | 705 | } else { |
648 | *max_width = config->floating_maximum_width; | 706 | *max_width = config->floating_maximum_width; |
649 | } | 707 | } |
@@ -651,7 +709,7 @@ void floating_calculate_constraints(int *min_width, int *max_width, | |||
651 | if (config->floating_maximum_height == -1) { // no maximum | 709 | if (config->floating_maximum_height == -1) { // no maximum |
652 | *max_height = INT_MAX; | 710 | *max_height = INT_MAX; |
653 | } else if (config->floating_maximum_height == 0) { // automatic | 711 | } else if (config->floating_maximum_height == 0) { // automatic |
654 | *max_height = box->height; | 712 | *max_height = box.height; |
655 | } else { | 713 | } else { |
656 | *max_height = config->floating_maximum_height; | 714 | *max_height = config->floating_maximum_height; |
657 | } | 715 | } |
@@ -663,66 +721,66 @@ static void floating_natural_resize(struct sway_container *con) { | |||
663 | floating_calculate_constraints(&min_width, &max_width, | 721 | floating_calculate_constraints(&min_width, &max_width, |
664 | &min_height, &max_height); | 722 | &min_height, &max_height); |
665 | if (!con->view) { | 723 | if (!con->view) { |
666 | con->width = fmax(min_width, fmin(con->width, max_width)); | 724 | con->pending.width = fmax(min_width, fmin(con->pending.width, max_width)); |
667 | con->height = fmax(min_height, fmin(con->height, max_height)); | 725 | con->pending.height = fmax(min_height, fmin(con->pending.height, max_height)); |
668 | } else { | 726 | } else { |
669 | struct sway_view *view = con->view; | 727 | struct sway_view *view = con->view; |
670 | con->content_width = | 728 | con->pending.content_width = |
671 | fmax(min_width, fmin(view->natural_width, max_width)); | 729 | fmax(min_width, fmin(view->natural_width, max_width)); |
672 | con->content_height = | 730 | con->pending.content_height = |
673 | fmax(min_height, fmin(view->natural_height, max_height)); | 731 | fmax(min_height, fmin(view->natural_height, max_height)); |
674 | container_set_geometry_from_content(con); | 732 | container_set_geometry_from_content(con); |
675 | } | 733 | } |
676 | } | 734 | } |
677 | 735 | ||
678 | void container_floating_resize_and_center(struct sway_container *con) { | 736 | void container_floating_resize_and_center(struct sway_container *con) { |
679 | struct sway_workspace *ws = con->workspace; | 737 | struct sway_workspace *ws = con->pending.workspace; |
680 | if (!ws) { | 738 | if (!ws) { |
681 | // On scratchpad, just resize | 739 | // On scratchpad, just resize |
682 | floating_natural_resize(con); | 740 | floating_natural_resize(con); |
683 | return; | 741 | return; |
684 | } | 742 | } |
685 | 743 | ||
686 | struct wlr_box *ob = wlr_output_layout_get_box(root->output_layout, | 744 | struct wlr_box ob; |
687 | ws->output->wlr_output); | 745 | wlr_output_layout_get_box(root->output_layout, ws->output->wlr_output, &ob); |
688 | if (!ob) { | 746 | if (wlr_box_empty(&ob)) { |
689 | // On NOOP output. Will be called again when moved to an output | 747 | // On NOOP output. Will be called again when moved to an output |
690 | con->x = 0; | 748 | con->pending.x = 0; |
691 | con->y = 0; | 749 | con->pending.y = 0; |
692 | con->width = 0; | 750 | con->pending.width = 0; |
693 | con->height = 0; | 751 | con->pending.height = 0; |
694 | return; | 752 | return; |
695 | } | 753 | } |
696 | 754 | ||
697 | floating_natural_resize(con); | 755 | floating_natural_resize(con); |
698 | if (!con->view) { | 756 | if (!con->view) { |
699 | if (con->width > ws->width || con->height > ws->height) { | 757 | if (con->pending.width > ws->width || con->pending.height > ws->height) { |
700 | con->x = ob->x + (ob->width - con->width) / 2; | 758 | con->pending.x = ob.x + (ob.width - con->pending.width) / 2; |
701 | con->y = ob->y + (ob->height - con->height) / 2; | 759 | con->pending.y = ob.y + (ob.height - con->pending.height) / 2; |
702 | } else { | 760 | } else { |
703 | con->x = ws->x + (ws->width - con->width) / 2; | 761 | con->pending.x = ws->x + (ws->width - con->pending.width) / 2; |
704 | con->y = ws->y + (ws->height - con->height) / 2; | 762 | con->pending.y = ws->y + (ws->height - con->pending.height) / 2; |
705 | } | 763 | } |
706 | } else { | 764 | } else { |
707 | if (con->content_width > ws->width | 765 | if (con->pending.content_width > ws->width |
708 | || con->content_height > ws->height) { | 766 | || con->pending.content_height > ws->height) { |
709 | con->content_x = ob->x + (ob->width - con->content_width) / 2; | 767 | con->pending.content_x = ob.x + (ob.width - con->pending.content_width) / 2; |
710 | con->content_y = ob->y + (ob->height - con->content_height) / 2; | 768 | con->pending.content_y = ob.y + (ob.height - con->pending.content_height) / 2; |
711 | } else { | 769 | } else { |
712 | con->content_x = ws->x + (ws->width - con->content_width) / 2; | 770 | con->pending.content_x = ws->x + (ws->width - con->pending.content_width) / 2; |
713 | con->content_y = ws->y + (ws->height - con->content_height) / 2; | 771 | con->pending.content_y = ws->y + (ws->height - con->pending.content_height) / 2; |
714 | } | 772 | } |
715 | 773 | ||
716 | // If the view's border is B_NONE then these properties are ignored. | 774 | // If the view's border is B_NONE then these properties are ignored. |
717 | con->border_top = con->border_bottom = true; | 775 | con->pending.border_top = con->pending.border_bottom = true; |
718 | con->border_left = con->border_right = true; | 776 | con->pending.border_left = con->pending.border_right = true; |
719 | 777 | ||
720 | container_set_geometry_from_content(con); | 778 | container_set_geometry_from_content(con); |
721 | } | 779 | } |
722 | } | 780 | } |
723 | 781 | ||
724 | void container_floating_set_default_size(struct sway_container *con) { | 782 | void container_floating_set_default_size(struct sway_container *con) { |
725 | if (!sway_assert(con->workspace, "Expected a container on a workspace")) { | 783 | if (!sway_assert(con->pending.workspace, "Expected a container on a workspace")) { |
726 | return; | 784 | return; |
727 | } | 785 | } |
728 | 786 | ||
@@ -730,16 +788,16 @@ void container_floating_set_default_size(struct sway_container *con) { | |||
730 | floating_calculate_constraints(&min_width, &max_width, | 788 | floating_calculate_constraints(&min_width, &max_width, |
731 | &min_height, &max_height); | 789 | &min_height, &max_height); |
732 | struct wlr_box *box = calloc(1, sizeof(struct wlr_box)); | 790 | struct wlr_box *box = calloc(1, sizeof(struct wlr_box)); |
733 | workspace_get_box(con->workspace, box); | 791 | workspace_get_box(con->pending.workspace, box); |
734 | 792 | ||
735 | double width = fmax(min_width, fmin(box->width * 0.5, max_width)); | 793 | double width = fmax(min_width, fmin(box->width * 0.5, max_width)); |
736 | double height = fmax(min_height, fmin(box->height * 0.75, max_height)); | 794 | double height = fmax(min_height, fmin(box->height * 0.75, max_height)); |
737 | if (!con->view) { | 795 | if (!con->view) { |
738 | con->width = width; | 796 | con->pending.width = width; |
739 | con->height = height; | 797 | con->pending.height = height; |
740 | } else { | 798 | } else { |
741 | con->content_width = width; | 799 | con->pending.content_width = width; |
742 | con->content_height = height; | 800 | con->pending.content_height = height; |
743 | container_set_geometry_from_content(con); | 801 | container_set_geometry_from_content(con); |
744 | } | 802 | } |
745 | 803 | ||
@@ -761,8 +819,8 @@ void container_set_resizing(struct sway_container *con, bool resizing) { | |||
761 | con->view->impl->set_resizing(con->view, resizing); | 819 | con->view->impl->set_resizing(con->view, resizing); |
762 | } | 820 | } |
763 | } else { | 821 | } else { |
764 | for (int i = 0; i < con->children->length; ++i ) { | 822 | for (int i = 0; i < con->pending.children->length; ++i ) { |
765 | struct sway_container *child = con->children->items[i]; | 823 | struct sway_container *child = con->pending.children->items[i]; |
766 | container_set_resizing(child, resizing); | 824 | container_set_resizing(child, resizing); |
767 | } | 825 | } |
768 | } | 826 | } |
@@ -774,21 +832,33 @@ void container_set_floating(struct sway_container *container, bool enable) { | |||
774 | } | 832 | } |
775 | 833 | ||
776 | struct sway_seat *seat = input_manager_current_seat(); | 834 | struct sway_seat *seat = input_manager_current_seat(); |
777 | struct sway_workspace *workspace = container->workspace; | 835 | struct sway_workspace *workspace = container->pending.workspace; |
836 | struct sway_container *focus = seat_get_focused_container(seat); | ||
837 | bool set_focus = focus == container; | ||
778 | 838 | ||
779 | if (enable) { | 839 | if (enable) { |
780 | struct sway_container *old_parent = container->parent; | 840 | struct sway_container *old_parent = container->pending.parent; |
781 | container_detach(container); | 841 | container_detach(container); |
782 | workspace_add_floating(workspace, container); | 842 | workspace_add_floating(workspace, container); |
783 | if (container->view) { | 843 | if (container->view) { |
784 | view_set_tiled(container->view, false); | 844 | view_set_tiled(container->view, false); |
785 | if (container->view->using_csd) { | 845 | if (container->view->using_csd) { |
786 | container->border = B_CSD; | 846 | container->saved_border = container->pending.border; |
847 | container->pending.border = B_CSD; | ||
848 | if (container->view->xdg_decoration) { | ||
849 | struct sway_xdg_decoration *deco = container->view->xdg_decoration; | ||
850 | wlr_xdg_toplevel_decoration_v1_set_mode(deco->wlr_xdg_decoration, | ||
851 | WLR_XDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE); | ||
852 | } | ||
787 | } | 853 | } |
788 | } | 854 | } |
789 | container_floating_set_default_size(container); | 855 | container_floating_set_default_size(container); |
790 | container_floating_resize_and_center(container); | 856 | container_floating_resize_and_center(container); |
791 | if (old_parent) { | 857 | if (old_parent) { |
858 | if (set_focus) { | ||
859 | seat_set_raw_focus(seat, &old_parent->node); | ||
860 | seat_set_raw_focus(seat, &container->node); | ||
861 | } | ||
792 | container_reap_empty(old_parent); | 862 | container_reap_empty(old_parent); |
793 | } | 863 | } |
794 | } else { | 864 | } else { |
@@ -800,19 +870,28 @@ void container_set_floating(struct sway_container *container, bool enable) { | |||
800 | struct sway_container *reference = | 870 | struct sway_container *reference = |
801 | seat_get_focus_inactive_tiling(seat, workspace); | 871 | seat_get_focus_inactive_tiling(seat, workspace); |
802 | if (reference) { | 872 | if (reference) { |
803 | container_add_sibling(reference, container, 1); | 873 | if (reference->view) { |
804 | container->width = reference->width; | 874 | container_add_sibling(reference, container, 1); |
805 | container->height = reference->height; | 875 | } else { |
876 | container_add_child(reference, container); | ||
877 | } | ||
878 | container->pending.width = reference->pending.width; | ||
879 | container->pending.height = reference->pending.height; | ||
806 | } else { | 880 | } else { |
807 | struct sway_container *other = | 881 | struct sway_container *other = |
808 | workspace_add_tiling(workspace, container); | 882 | workspace_add_tiling(workspace, container); |
809 | other->width = workspace->width; | 883 | other->pending.width = workspace->width; |
810 | other->height = workspace->height; | 884 | other->pending.height = workspace->height; |
811 | } | 885 | } |
812 | if (container->view) { | 886 | if (container->view) { |
813 | view_set_tiled(container->view, true); | 887 | view_set_tiled(container->view, true); |
814 | if (container->view->using_csd) { | 888 | if (container->view->using_csd) { |
815 | container->border = container->saved_border; | 889 | container->pending.border = container->saved_border; |
890 | if (container->view->xdg_decoration) { | ||
891 | struct sway_xdg_decoration *deco = container->view->xdg_decoration; | ||
892 | wlr_xdg_toplevel_decoration_v1_set_mode(deco->wlr_xdg_decoration, | ||
893 | WLR_XDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE); | ||
894 | } | ||
816 | } | 895 | } |
817 | } | 896 | } |
818 | container->width_fraction = 0; | 897 | container->width_fraction = 0; |
@@ -834,22 +913,33 @@ void container_set_geometry_from_content(struct sway_container *con) { | |||
834 | size_t border_width = 0; | 913 | size_t border_width = 0; |
835 | size_t top = 0; | 914 | size_t top = 0; |
836 | 915 | ||
837 | if (con->border != B_CSD) { | 916 | if (con->pending.border != B_CSD && !con->pending.fullscreen_mode) { |
838 | border_width = con->border_thickness * (con->border != B_NONE); | 917 | border_width = con->pending.border_thickness * (con->pending.border != B_NONE); |
839 | top = con->border == B_NORMAL ? | 918 | top = con->pending.border == B_NORMAL ? |
840 | container_titlebar_height() : border_width; | 919 | container_titlebar_height() : border_width; |
841 | } | 920 | } |
842 | 921 | ||
843 | con->x = con->content_x - border_width; | 922 | con->pending.x = con->pending.content_x - border_width; |
844 | con->y = con->content_y - top; | 923 | con->pending.y = con->pending.content_y - top; |
845 | con->width = con->content_width + border_width * 2; | 924 | con->pending.width = con->pending.content_width + border_width * 2; |
846 | con->height = top + con->content_height + border_width; | 925 | con->pending.height = top + con->pending.content_height + border_width; |
847 | node_set_dirty(&con->node); | 926 | node_set_dirty(&con->node); |
848 | } | 927 | } |
849 | 928 | ||
850 | bool container_is_floating(struct sway_container *container) { | 929 | bool container_is_floating(struct sway_container *container) { |
851 | if (!container->parent && container->workspace && | 930 | if (!container->pending.parent && container->pending.workspace && |
852 | list_find(container->workspace->floating, container) != -1) { | 931 | list_find(container->pending.workspace->floating, container) != -1) { |
932 | return true; | ||
933 | } | ||
934 | if (container->scratchpad) { | ||
935 | return true; | ||
936 | } | ||
937 | return false; | ||
938 | } | ||
939 | |||
940 | bool container_is_current_floating(struct sway_container *container) { | ||
941 | if (!container->current.parent && container->current.workspace && | ||
942 | list_find(container->current.workspace->floating, container) != -1) { | ||
853 | return true; | 943 | return true; |
854 | } | 944 | } |
855 | if (container->scratchpad) { | 945 | if (container->scratchpad) { |
@@ -859,10 +949,10 @@ bool container_is_floating(struct sway_container *container) { | |||
859 | } | 949 | } |
860 | 950 | ||
861 | void container_get_box(struct sway_container *container, struct wlr_box *box) { | 951 | void container_get_box(struct sway_container *container, struct wlr_box *box) { |
862 | box->x = container->x; | 952 | box->x = container->pending.x; |
863 | box->y = container->y; | 953 | box->y = container->pending.y; |
864 | box->width = container->width; | 954 | box->width = container->pending.width; |
865 | box->height = container->height; | 955 | box->height = container->pending.height; |
866 | } | 956 | } |
867 | 957 | ||
868 | /** | 958 | /** |
@@ -870,14 +960,14 @@ void container_get_box(struct sway_container *container, struct wlr_box *box) { | |||
870 | */ | 960 | */ |
871 | void container_floating_translate(struct sway_container *con, | 961 | void container_floating_translate(struct sway_container *con, |
872 | double x_amount, double y_amount) { | 962 | double x_amount, double y_amount) { |
873 | con->x += x_amount; | 963 | con->pending.x += x_amount; |
874 | con->y += y_amount; | 964 | con->pending.y += y_amount; |
875 | con->content_x += x_amount; | 965 | con->pending.content_x += x_amount; |
876 | con->content_y += y_amount; | 966 | con->pending.content_y += y_amount; |
877 | 967 | ||
878 | if (con->children) { | 968 | if (con->pending.children) { |
879 | for (int i = 0; i < con->children->length; ++i) { | 969 | for (int i = 0; i < con->pending.children->length; ++i) { |
880 | struct sway_container *child = con->children->items[i]; | 970 | struct sway_container *child = con->pending.children->items[i]; |
881 | container_floating_translate(child, x_amount, y_amount); | 971 | container_floating_translate(child, x_amount, y_amount); |
882 | } | 972 | } |
883 | } | 973 | } |
@@ -893,8 +983,8 @@ void container_floating_translate(struct sway_container *con, | |||
893 | * center. | 983 | * center. |
894 | */ | 984 | */ |
895 | struct sway_output *container_floating_find_output(struct sway_container *con) { | 985 | struct sway_output *container_floating_find_output(struct sway_container *con) { |
896 | double center_x = con->x + con->width / 2; | 986 | double center_x = con->pending.x + con->pending.width / 2; |
897 | double center_y = con->y + con->height / 2; | 987 | double center_y = con->pending.y + con->pending.height / 2; |
898 | struct sway_output *closest_output = NULL; | 988 | struct sway_output *closest_output = NULL; |
899 | double closest_distance = DBL_MAX; | 989 | double closest_distance = DBL_MAX; |
900 | for (int i = 0; i < root->outputs->length; ++i) { | 990 | for (int i = 0; i < root->outputs->length; ++i) { |
@@ -925,11 +1015,11 @@ void container_floating_move_to(struct sway_container *con, | |||
925 | "Expected a floating container")) { | 1015 | "Expected a floating container")) { |
926 | return; | 1016 | return; |
927 | } | 1017 | } |
928 | container_floating_translate(con, lx - con->x, ly - con->y); | 1018 | container_floating_translate(con, lx - con->pending.x, ly - con->pending.y); |
929 | if (container_is_scratchpad_hidden(con)) { | 1019 | if (container_is_scratchpad_hidden(con)) { |
930 | return; | 1020 | return; |
931 | } | 1021 | } |
932 | struct sway_workspace *old_workspace = con->workspace; | 1022 | struct sway_workspace *old_workspace = con->pending.workspace; |
933 | struct sway_output *new_output = container_floating_find_output(con); | 1023 | struct sway_output *new_output = container_floating_find_output(con); |
934 | if (!sway_assert(new_output, "Unable to find any output")) { | 1024 | if (!sway_assert(new_output, "Unable to find any output")) { |
935 | return; | 1025 | return; |
@@ -951,10 +1041,10 @@ void container_floating_move_to_center(struct sway_container *con) { | |||
951 | "Expected a floating container")) { | 1041 | "Expected a floating container")) { |
952 | return; | 1042 | return; |
953 | } | 1043 | } |
954 | struct sway_workspace *ws = con->workspace; | 1044 | struct sway_workspace *ws = con->pending.workspace; |
955 | double new_lx = ws->x + (ws->width - con->width) / 2; | 1045 | double new_lx = ws->x + (ws->width - con->pending.width) / 2; |
956 | double new_ly = ws->y + (ws->height - con->height) / 2; | 1046 | double new_ly = ws->y + (ws->height - con->pending.height) / 2; |
957 | container_floating_translate(con, new_lx - con->x, new_ly - con->y); | 1047 | container_floating_translate(con, new_lx - con->pending.x, new_ly - con->pending.y); |
958 | } | 1048 | } |
959 | 1049 | ||
960 | static bool find_urgent_iterator(struct sway_container *con, void *data) { | 1050 | static bool find_urgent_iterator(struct sway_container *con, void *data) { |
@@ -972,42 +1062,118 @@ void container_end_mouse_operation(struct sway_container *container) { | |||
972 | } | 1062 | } |
973 | } | 1063 | } |
974 | 1064 | ||
975 | static void set_fullscreen_iterator(struct sway_container *con, void *data) { | 1065 | static bool devid_from_fd(int fd, dev_t *devid) { |
1066 | struct stat stat; | ||
1067 | if (fstat(fd, &stat) != 0) { | ||
1068 | sway_log_errno(SWAY_ERROR, "fstat failed"); | ||
1069 | return false; | ||
1070 | } | ||
1071 | *devid = stat.st_rdev; | ||
1072 | return true; | ||
1073 | } | ||
1074 | |||
1075 | static void set_fullscreen(struct sway_container *con, bool enable) { | ||
976 | if (!con->view) { | 1076 | if (!con->view) { |
977 | return; | 1077 | return; |
978 | } | 1078 | } |
979 | if (con->view->impl->set_fullscreen) { | 1079 | if (con->view->impl->set_fullscreen) { |
980 | bool *enable = data; | 1080 | con->view->impl->set_fullscreen(con->view, enable); |
981 | con->view->impl->set_fullscreen(con->view, *enable); | ||
982 | if (con->view->foreign_toplevel) { | 1081 | if (con->view->foreign_toplevel) { |
983 | wlr_foreign_toplevel_handle_v1_set_fullscreen( | 1082 | wlr_foreign_toplevel_handle_v1_set_fullscreen( |
984 | con->view->foreign_toplevel, *enable); | 1083 | con->view->foreign_toplevel, enable); |
985 | } | 1084 | } |
986 | } | 1085 | } |
1086 | |||
1087 | if (!server.linux_dmabuf_v1 || !con->view->surface) { | ||
1088 | return; | ||
1089 | } | ||
1090 | if (!enable) { | ||
1091 | wlr_linux_dmabuf_v1_set_surface_feedback(server.linux_dmabuf_v1, | ||
1092 | con->view->surface, NULL); | ||
1093 | return; | ||
1094 | } | ||
1095 | |||
1096 | if (!con->pending.workspace || !con->pending.workspace->output) { | ||
1097 | return; | ||
1098 | } | ||
1099 | |||
1100 | struct sway_output *output = con->pending.workspace->output; | ||
1101 | struct wlr_output *wlr_output = output->wlr_output; | ||
1102 | |||
1103 | // TODO: add wlroots helpers for all of this stuff | ||
1104 | |||
1105 | const struct wlr_drm_format_set *renderer_formats = | ||
1106 | wlr_renderer_get_dmabuf_texture_formats(server.renderer); | ||
1107 | assert(renderer_formats); | ||
1108 | |||
1109 | int renderer_drm_fd = wlr_renderer_get_drm_fd(server.renderer); | ||
1110 | int backend_drm_fd = wlr_backend_get_drm_fd(wlr_output->backend); | ||
1111 | if (renderer_drm_fd < 0 || backend_drm_fd < 0) { | ||
1112 | return; | ||
1113 | } | ||
1114 | |||
1115 | dev_t render_dev, scanout_dev; | ||
1116 | if (!devid_from_fd(renderer_drm_fd, &render_dev) || | ||
1117 | !devid_from_fd(backend_drm_fd, &scanout_dev)) { | ||
1118 | return; | ||
1119 | } | ||
1120 | |||
1121 | const struct wlr_drm_format_set *output_formats = | ||
1122 | wlr_output_get_primary_formats(output->wlr_output, | ||
1123 | WLR_BUFFER_CAP_DMABUF); | ||
1124 | if (!output_formats) { | ||
1125 | return; | ||
1126 | } | ||
1127 | |||
1128 | struct wlr_drm_format_set scanout_formats = {0}; | ||
1129 | if (!wlr_drm_format_set_intersect(&scanout_formats, | ||
1130 | output_formats, renderer_formats)) { | ||
1131 | return; | ||
1132 | } | ||
1133 | |||
1134 | struct wlr_linux_dmabuf_feedback_v1_tranche tranches[] = { | ||
1135 | { | ||
1136 | .target_device = scanout_dev, | ||
1137 | .flags = ZWP_LINUX_DMABUF_FEEDBACK_V1_TRANCHE_FLAGS_SCANOUT, | ||
1138 | .formats = &scanout_formats, | ||
1139 | }, | ||
1140 | { | ||
1141 | .target_device = render_dev, | ||
1142 | .formats = renderer_formats, | ||
1143 | }, | ||
1144 | }; | ||
1145 | |||
1146 | const struct wlr_linux_dmabuf_feedback_v1 feedback = { | ||
1147 | .main_device = render_dev, | ||
1148 | .tranches = tranches, | ||
1149 | .tranches_len = sizeof(tranches) / sizeof(tranches[0]), | ||
1150 | }; | ||
1151 | wlr_linux_dmabuf_v1_set_surface_feedback(server.linux_dmabuf_v1, | ||
1152 | con->view->surface, &feedback); | ||
1153 | |||
1154 | wlr_drm_format_set_finish(&scanout_formats); | ||
987 | } | 1155 | } |
988 | 1156 | ||
989 | static void container_fullscreen_workspace(struct sway_container *con) { | 1157 | static void container_fullscreen_workspace(struct sway_container *con) { |
990 | if (!sway_assert(con->fullscreen_mode == FULLSCREEN_NONE, | 1158 | if (!sway_assert(con->pending.fullscreen_mode == FULLSCREEN_NONE, |
991 | "Expected a non-fullscreen container")) { | 1159 | "Expected a non-fullscreen container")) { |
992 | return; | 1160 | return; |
993 | } | 1161 | } |
994 | bool enable = true; | 1162 | set_fullscreen(con, true); |
995 | set_fullscreen_iterator(con, &enable); | 1163 | con->pending.fullscreen_mode = FULLSCREEN_WORKSPACE; |
996 | container_for_each_child(con, set_fullscreen_iterator, &enable); | ||
997 | con->fullscreen_mode = FULLSCREEN_WORKSPACE; | ||
998 | 1164 | ||
999 | con->saved_x = con->x; | 1165 | con->saved_x = con->pending.x; |
1000 | con->saved_y = con->y; | 1166 | con->saved_y = con->pending.y; |
1001 | con->saved_width = con->width; | 1167 | con->saved_width = con->pending.width; |
1002 | con->saved_height = con->height; | 1168 | con->saved_height = con->pending.height; |
1003 | 1169 | ||
1004 | if (con->workspace) { | 1170 | if (con->pending.workspace) { |
1005 | con->workspace->fullscreen = con; | 1171 | con->pending.workspace->fullscreen = con; |
1006 | struct sway_seat *seat; | 1172 | struct sway_seat *seat; |
1007 | struct sway_workspace *focus_ws; | 1173 | struct sway_workspace *focus_ws; |
1008 | wl_list_for_each(seat, &server.input->seats, link) { | 1174 | wl_list_for_each(seat, &server.input->seats, link) { |
1009 | focus_ws = seat_get_focused_workspace(seat); | 1175 | focus_ws = seat_get_focused_workspace(seat); |
1010 | if (focus_ws == con->workspace) { | 1176 | if (focus_ws == con->pending.workspace) { |
1011 | seat_set_focus_container(seat, con); | 1177 | seat_set_focus_container(seat, con); |
1012 | } else { | 1178 | } else { |
1013 | struct sway_node *focus = | 1179 | struct sway_node *focus = |
@@ -1023,19 +1189,17 @@ static void container_fullscreen_workspace(struct sway_container *con) { | |||
1023 | } | 1189 | } |
1024 | 1190 | ||
1025 | static void container_fullscreen_global(struct sway_container *con) { | 1191 | static void container_fullscreen_global(struct sway_container *con) { |
1026 | if (!sway_assert(con->fullscreen_mode == FULLSCREEN_NONE, | 1192 | if (!sway_assert(con->pending.fullscreen_mode == FULLSCREEN_NONE, |
1027 | "Expected a non-fullscreen container")) { | 1193 | "Expected a non-fullscreen container")) { |
1028 | return; | 1194 | return; |
1029 | } | 1195 | } |
1030 | bool enable = true; | 1196 | set_fullscreen(con, true); |
1031 | set_fullscreen_iterator(con, &enable); | ||
1032 | container_for_each_child(con, set_fullscreen_iterator, &enable); | ||
1033 | 1197 | ||
1034 | root->fullscreen_global = con; | 1198 | root->fullscreen_global = con; |
1035 | con->saved_x = con->x; | 1199 | con->saved_x = con->pending.x; |
1036 | con->saved_y = con->y; | 1200 | con->saved_y = con->pending.y; |
1037 | con->saved_width = con->width; | 1201 | con->saved_width = con->pending.width; |
1038 | con->saved_height = con->height; | 1202 | con->saved_height = con->pending.height; |
1039 | 1203 | ||
1040 | struct sway_seat *seat; | 1204 | struct sway_seat *seat; |
1041 | wl_list_for_each(seat, &server.input->seats, link) { | 1205 | wl_list_for_each(seat, &server.input->seats, link) { |
@@ -1045,34 +1209,32 @@ static void container_fullscreen_global(struct sway_container *con) { | |||
1045 | } | 1209 | } |
1046 | } | 1210 | } |
1047 | 1211 | ||
1048 | con->fullscreen_mode = FULLSCREEN_GLOBAL; | 1212 | con->pending.fullscreen_mode = FULLSCREEN_GLOBAL; |
1049 | container_end_mouse_operation(con); | 1213 | container_end_mouse_operation(con); |
1050 | ipc_event_window(con, "fullscreen_mode"); | 1214 | ipc_event_window(con, "fullscreen_mode"); |
1051 | } | 1215 | } |
1052 | 1216 | ||
1053 | void container_fullscreen_disable(struct sway_container *con) { | 1217 | void container_fullscreen_disable(struct sway_container *con) { |
1054 | if (!sway_assert(con->fullscreen_mode != FULLSCREEN_NONE, | 1218 | if (!sway_assert(con->pending.fullscreen_mode != FULLSCREEN_NONE, |
1055 | "Expected a fullscreen container")) { | 1219 | "Expected a fullscreen container")) { |
1056 | return; | 1220 | return; |
1057 | } | 1221 | } |
1058 | bool enable = false; | 1222 | set_fullscreen(con, false); |
1059 | set_fullscreen_iterator(con, &enable); | ||
1060 | container_for_each_child(con, set_fullscreen_iterator, &enable); | ||
1061 | 1223 | ||
1062 | if (container_is_floating(con)) { | 1224 | if (container_is_floating(con)) { |
1063 | con->x = con->saved_x; | 1225 | con->pending.x = con->saved_x; |
1064 | con->y = con->saved_y; | 1226 | con->pending.y = con->saved_y; |
1065 | con->width = con->saved_width; | 1227 | con->pending.width = con->saved_width; |
1066 | con->height = con->saved_height; | 1228 | con->pending.height = con->saved_height; |
1067 | } | 1229 | } |
1068 | 1230 | ||
1069 | if (con->fullscreen_mode == FULLSCREEN_WORKSPACE) { | 1231 | if (con->pending.fullscreen_mode == FULLSCREEN_WORKSPACE) { |
1070 | if (con->workspace) { | 1232 | if (con->pending.workspace) { |
1071 | con->workspace->fullscreen = NULL; | 1233 | con->pending.workspace->fullscreen = NULL; |
1072 | if (container_is_floating(con)) { | 1234 | if (container_is_floating(con)) { |
1073 | struct sway_output *output = | 1235 | struct sway_output *output = |
1074 | container_floating_find_output(con); | 1236 | container_floating_find_output(con); |
1075 | if (con->workspace->output != output) { | 1237 | if (con->pending.workspace->output != output) { |
1076 | container_floating_move_to_center(con); | 1238 | container_floating_move_to_center(con); |
1077 | } | 1239 | } |
1078 | } | 1240 | } |
@@ -1084,11 +1246,11 @@ void container_fullscreen_disable(struct sway_container *con) { | |||
1084 | // If the container was mapped as fullscreen and set as floating by | 1246 | // If the container was mapped as fullscreen and set as floating by |
1085 | // criteria, it needs to be reinitialized as floating to get the proper | 1247 | // criteria, it needs to be reinitialized as floating to get the proper |
1086 | // size and location | 1248 | // size and location |
1087 | if (container_is_floating(con) && (con->width == 0 || con->height == 0)) { | 1249 | if (container_is_floating(con) && (con->pending.width == 0 || con->pending.height == 0)) { |
1088 | container_floating_resize_and_center(con); | 1250 | container_floating_resize_and_center(con); |
1089 | } | 1251 | } |
1090 | 1252 | ||
1091 | con->fullscreen_mode = FULLSCREEN_NONE; | 1253 | con->pending.fullscreen_mode = FULLSCREEN_NONE; |
1092 | container_end_mouse_operation(con); | 1254 | container_end_mouse_operation(con); |
1093 | ipc_event_window(con, "fullscreen_mode"); | 1255 | ipc_event_window(con, "fullscreen_mode"); |
1094 | 1256 | ||
@@ -1106,7 +1268,7 @@ void container_fullscreen_disable(struct sway_container *con) { | |||
1106 | 1268 | ||
1107 | void container_set_fullscreen(struct sway_container *con, | 1269 | void container_set_fullscreen(struct sway_container *con, |
1108 | enum sway_fullscreen_mode mode) { | 1270 | enum sway_fullscreen_mode mode) { |
1109 | if (con->fullscreen_mode == mode) { | 1271 | if (con->pending.fullscreen_mode == mode) { |
1110 | return; | 1272 | return; |
1111 | } | 1273 | } |
1112 | 1274 | ||
@@ -1118,8 +1280,8 @@ void container_set_fullscreen(struct sway_container *con, | |||
1118 | if (root->fullscreen_global) { | 1280 | if (root->fullscreen_global) { |
1119 | container_fullscreen_disable(root->fullscreen_global); | 1281 | container_fullscreen_disable(root->fullscreen_global); |
1120 | } | 1282 | } |
1121 | if (con->workspace && con->workspace->fullscreen) { | 1283 | if (con->pending.workspace && con->pending.workspace->fullscreen) { |
1122 | container_fullscreen_disable(con->workspace->fullscreen); | 1284 | container_fullscreen_disable(con->pending.workspace->fullscreen); |
1123 | } | 1285 | } |
1124 | container_fullscreen_workspace(con); | 1286 | container_fullscreen_workspace(con); |
1125 | break; | 1287 | break; |
@@ -1127,7 +1289,7 @@ void container_set_fullscreen(struct sway_container *con, | |||
1127 | if (root->fullscreen_global) { | 1289 | if (root->fullscreen_global) { |
1128 | container_fullscreen_disable(root->fullscreen_global); | 1290 | container_fullscreen_disable(root->fullscreen_global); |
1129 | } | 1291 | } |
1130 | if (con->fullscreen_mode == FULLSCREEN_WORKSPACE) { | 1292 | if (con->pending.fullscreen_mode == FULLSCREEN_WORKSPACE) { |
1131 | container_fullscreen_disable(con); | 1293 | container_fullscreen_disable(con); |
1132 | } | 1294 | } |
1133 | container_fullscreen_global(con); | 1295 | container_fullscreen_global(con); |
@@ -1137,8 +1299,8 @@ void container_set_fullscreen(struct sway_container *con, | |||
1137 | 1299 | ||
1138 | struct sway_container *container_toplevel_ancestor( | 1300 | struct sway_container *container_toplevel_ancestor( |
1139 | struct sway_container *container) { | 1301 | struct sway_container *container) { |
1140 | while (container->parent) { | 1302 | while (container->pending.parent) { |
1141 | container = container->parent; | 1303 | container = container->pending.parent; |
1142 | } | 1304 | } |
1143 | 1305 | ||
1144 | return container; | 1306 | return container; |
@@ -1150,10 +1312,10 @@ bool container_is_floating_or_child(struct sway_container *container) { | |||
1150 | 1312 | ||
1151 | bool container_is_fullscreen_or_child(struct sway_container *container) { | 1313 | bool container_is_fullscreen_or_child(struct sway_container *container) { |
1152 | do { | 1314 | do { |
1153 | if (container->fullscreen_mode) { | 1315 | if (container->pending.fullscreen_mode) { |
1154 | return true; | 1316 | return true; |
1155 | } | 1317 | } |
1156 | container = container->parent; | 1318 | container = container->pending.parent; |
1157 | } while (container); | 1319 | } while (container); |
1158 | 1320 | ||
1159 | return false; | 1321 | return false; |
@@ -1226,11 +1388,11 @@ void container_discover_outputs(struct sway_container *con) { | |||
1226 | } | 1388 | } |
1227 | 1389 | ||
1228 | enum sway_container_layout container_parent_layout(struct sway_container *con) { | 1390 | enum sway_container_layout container_parent_layout(struct sway_container *con) { |
1229 | if (con->parent) { | 1391 | if (con->pending.parent) { |
1230 | return con->parent->layout; | 1392 | return con->pending.parent->pending.layout; |
1231 | } | 1393 | } |
1232 | if (con->workspace) { | 1394 | if (con->pending.workspace) { |
1233 | return con->workspace->layout; | 1395 | return con->pending.workspace->layout; |
1234 | } | 1396 | } |
1235 | return L_NONE; | 1397 | return L_NONE; |
1236 | } | 1398 | } |
@@ -1244,16 +1406,16 @@ enum sway_container_layout container_current_parent_layout( | |||
1244 | } | 1406 | } |
1245 | 1407 | ||
1246 | list_t *container_get_siblings(struct sway_container *container) { | 1408 | list_t *container_get_siblings(struct sway_container *container) { |
1247 | if (container->parent) { | 1409 | if (container->pending.parent) { |
1248 | return container->parent->children; | 1410 | return container->pending.parent->pending.children; |
1249 | } | 1411 | } |
1250 | if (container_is_scratchpad_hidden(container)) { | 1412 | if (container_is_scratchpad_hidden(container)) { |
1251 | return NULL; | 1413 | return NULL; |
1252 | } | 1414 | } |
1253 | if (list_find(container->workspace->tiling, container) != -1) { | 1415 | if (list_find(container->pending.workspace->tiling, container) != -1) { |
1254 | return container->workspace->tiling; | 1416 | return container->pending.workspace->tiling; |
1255 | } | 1417 | } |
1256 | return container->workspace->floating; | 1418 | return container->pending.workspace->floating; |
1257 | } | 1419 | } |
1258 | 1420 | ||
1259 | int container_sibling_index(struct sway_container *child) { | 1421 | int container_sibling_index(struct sway_container *child) { |
@@ -1268,30 +1430,30 @@ list_t *container_get_current_siblings(struct sway_container *container) { | |||
1268 | } | 1430 | } |
1269 | 1431 | ||
1270 | void container_handle_fullscreen_reparent(struct sway_container *con) { | 1432 | void container_handle_fullscreen_reparent(struct sway_container *con) { |
1271 | if (con->fullscreen_mode != FULLSCREEN_WORKSPACE || !con->workspace || | 1433 | if (con->pending.fullscreen_mode != FULLSCREEN_WORKSPACE || !con->pending.workspace || |
1272 | con->workspace->fullscreen == con) { | 1434 | con->pending.workspace->fullscreen == con) { |
1273 | return; | 1435 | return; |
1274 | } | 1436 | } |
1275 | if (con->workspace->fullscreen) { | 1437 | if (con->pending.workspace->fullscreen) { |
1276 | container_fullscreen_disable(con->workspace->fullscreen); | 1438 | container_fullscreen_disable(con->pending.workspace->fullscreen); |
1277 | } | 1439 | } |
1278 | con->workspace->fullscreen = con; | 1440 | con->pending.workspace->fullscreen = con; |
1279 | 1441 | ||
1280 | arrange_workspace(con->workspace); | 1442 | arrange_workspace(con->pending.workspace); |
1281 | } | 1443 | } |
1282 | 1444 | ||
1283 | static void set_workspace(struct sway_container *container, void *data) { | 1445 | static void set_workspace(struct sway_container *container, void *data) { |
1284 | container->workspace = container->parent->workspace; | 1446 | container->pending.workspace = container->pending.parent->pending.workspace; |
1285 | } | 1447 | } |
1286 | 1448 | ||
1287 | void container_insert_child(struct sway_container *parent, | 1449 | void container_insert_child(struct sway_container *parent, |
1288 | struct sway_container *child, int i) { | 1450 | struct sway_container *child, int i) { |
1289 | if (child->workspace) { | 1451 | if (child->pending.workspace) { |
1290 | container_detach(child); | 1452 | container_detach(child); |
1291 | } | 1453 | } |
1292 | list_insert(parent->children, i, child); | 1454 | list_insert(parent->pending.children, i, child); |
1293 | child->parent = parent; | 1455 | child->pending.parent = parent; |
1294 | child->workspace = parent->workspace; | 1456 | child->pending.workspace = parent->pending.workspace; |
1295 | container_for_each_child(child, set_workspace, NULL); | 1457 | container_for_each_child(child, set_workspace, NULL); |
1296 | container_handle_fullscreen_reparent(child); | 1458 | container_handle_fullscreen_reparent(child); |
1297 | container_update_representation(parent); | 1459 | container_update_representation(parent); |
@@ -1299,14 +1461,14 @@ void container_insert_child(struct sway_container *parent, | |||
1299 | 1461 | ||
1300 | void container_add_sibling(struct sway_container *fixed, | 1462 | void container_add_sibling(struct sway_container *fixed, |
1301 | struct sway_container *active, bool after) { | 1463 | struct sway_container *active, bool after) { |
1302 | if (active->workspace) { | 1464 | if (active->pending.workspace) { |
1303 | container_detach(active); | 1465 | container_detach(active); |
1304 | } | 1466 | } |
1305 | list_t *siblings = container_get_siblings(fixed); | 1467 | list_t *siblings = container_get_siblings(fixed); |
1306 | int index = list_find(siblings, fixed); | 1468 | int index = list_find(siblings, fixed); |
1307 | list_insert(siblings, index + after, active); | 1469 | list_insert(siblings, index + after, active); |
1308 | active->parent = fixed->parent; | 1470 | active->pending.parent = fixed->pending.parent; |
1309 | active->workspace = fixed->workspace; | 1471 | active->pending.workspace = fixed->pending.workspace; |
1310 | container_for_each_child(active, set_workspace, NULL); | 1472 | container_for_each_child(active, set_workspace, NULL); |
1311 | container_handle_fullscreen_reparent(active); | 1473 | container_handle_fullscreen_reparent(active); |
1312 | container_update_representation(active); | 1474 | container_update_representation(active); |
@@ -1314,17 +1476,13 @@ void container_add_sibling(struct sway_container *fixed, | |||
1314 | 1476 | ||
1315 | void container_add_child(struct sway_container *parent, | 1477 | void container_add_child(struct sway_container *parent, |
1316 | struct sway_container *child) { | 1478 | struct sway_container *child) { |
1317 | if (child->workspace) { | 1479 | if (child->pending.workspace) { |
1318 | container_detach(child); | 1480 | container_detach(child); |
1319 | } | 1481 | } |
1320 | list_add(parent->children, child); | 1482 | list_add(parent->pending.children, child); |
1321 | child->parent = parent; | 1483 | child->pending.parent = parent; |
1322 | child->workspace = parent->workspace; | 1484 | child->pending.workspace = parent->pending.workspace; |
1323 | container_for_each_child(child, set_workspace, NULL); | 1485 | container_for_each_child(child, set_workspace, NULL); |
1324 | bool fullscreen = child->fullscreen_mode != FULLSCREEN_NONE || | ||
1325 | parent->fullscreen_mode != FULLSCREEN_NONE; | ||
1326 | set_fullscreen_iterator(child, &fullscreen); | ||
1327 | container_for_each_child(child, set_fullscreen_iterator, &fullscreen); | ||
1328 | container_handle_fullscreen_reparent(child); | 1486 | container_handle_fullscreen_reparent(child); |
1329 | container_update_representation(parent); | 1487 | container_update_representation(parent); |
1330 | node_set_dirty(&child->node); | 1488 | node_set_dirty(&child->node); |
@@ -1332,15 +1490,15 @@ void container_add_child(struct sway_container *parent, | |||
1332 | } | 1490 | } |
1333 | 1491 | ||
1334 | void container_detach(struct sway_container *child) { | 1492 | void container_detach(struct sway_container *child) { |
1335 | if (child->fullscreen_mode == FULLSCREEN_WORKSPACE) { | 1493 | if (child->pending.fullscreen_mode == FULLSCREEN_WORKSPACE) { |
1336 | child->workspace->fullscreen = NULL; | 1494 | child->pending.workspace->fullscreen = NULL; |
1337 | } | 1495 | } |
1338 | if (child->fullscreen_mode == FULLSCREEN_GLOBAL) { | 1496 | if (child->pending.fullscreen_mode == FULLSCREEN_GLOBAL) { |
1339 | root->fullscreen_global = NULL; | 1497 | root->fullscreen_global = NULL; |
1340 | } | 1498 | } |
1341 | 1499 | ||
1342 | struct sway_container *old_parent = child->parent; | 1500 | struct sway_container *old_parent = child->pending.parent; |
1343 | struct sway_workspace *old_workspace = child->workspace; | 1501 | struct sway_workspace *old_workspace = child->pending.workspace; |
1344 | list_t *siblings = container_get_siblings(child); | 1502 | list_t *siblings = container_get_siblings(child); |
1345 | if (siblings) { | 1503 | if (siblings) { |
1346 | int index = list_find(siblings, child); | 1504 | int index = list_find(siblings, child); |
@@ -1348,8 +1506,8 @@ void container_detach(struct sway_container *child) { | |||
1348 | list_del(siblings, index); | 1506 | list_del(siblings, index); |
1349 | } | 1507 | } |
1350 | } | 1508 | } |
1351 | child->parent = NULL; | 1509 | child->pending.parent = NULL; |
1352 | child->workspace = NULL; | 1510 | child->pending.workspace = NULL; |
1353 | container_for_each_child(child, set_workspace, NULL); | 1511 | container_for_each_child(child, set_workspace, NULL); |
1354 | 1512 | ||
1355 | if (old_parent) { | 1513 | if (old_parent) { |
@@ -1364,18 +1522,18 @@ void container_detach(struct sway_container *child) { | |||
1364 | 1522 | ||
1365 | void container_replace(struct sway_container *container, | 1523 | void container_replace(struct sway_container *container, |
1366 | struct sway_container *replacement) { | 1524 | struct sway_container *replacement) { |
1367 | enum sway_fullscreen_mode fullscreen = container->fullscreen_mode; | 1525 | enum sway_fullscreen_mode fullscreen = container->pending.fullscreen_mode; |
1368 | bool scratchpad = container->scratchpad; | 1526 | bool scratchpad = container->scratchpad; |
1369 | struct sway_workspace *ws = NULL; | 1527 | struct sway_workspace *ws = NULL; |
1370 | if (fullscreen != FULLSCREEN_NONE) { | 1528 | if (fullscreen != FULLSCREEN_NONE) { |
1371 | container_fullscreen_disable(container); | 1529 | container_fullscreen_disable(container); |
1372 | } | 1530 | } |
1373 | if (scratchpad) { | 1531 | if (scratchpad) { |
1374 | ws = container->workspace; | 1532 | ws = container->pending.workspace; |
1375 | root_scratchpad_show(container); | 1533 | root_scratchpad_show(container); |
1376 | root_scratchpad_remove_container(container); | 1534 | root_scratchpad_remove_container(container); |
1377 | } | 1535 | } |
1378 | if (container->parent || container->workspace) { | 1536 | if (container->pending.parent || container->pending.workspace) { |
1379 | float width_fraction = container->width_fraction; | 1537 | float width_fraction = container->width_fraction; |
1380 | float height_fraction = container->height_fraction; | 1538 | float height_fraction = container->height_fraction; |
1381 | container_add_sibling(container, replacement, 1); | 1539 | container_add_sibling(container, replacement, 1); |
@@ -1403,7 +1561,7 @@ struct sway_container *container_split(struct sway_container *child, | |||
1403 | enum sway_container_layout layout) { | 1561 | enum sway_container_layout layout) { |
1404 | // i3 doesn't split singleton H/V containers | 1562 | // i3 doesn't split singleton H/V containers |
1405 | // https://github.com/i3/i3/blob/3cd1c45eba6de073bc4300eebb4e1cc1a0c4479a/src/tree.c#L354 | 1563 | // https://github.com/i3/i3/blob/3cd1c45eba6de073bc4300eebb4e1cc1a0c4479a/src/tree.c#L354 |
1406 | if (child->parent || child->workspace) { | 1564 | if (child->pending.parent || child->pending.workspace) { |
1407 | list_t *siblings = container_get_siblings(child); | 1565 | list_t *siblings = container_get_siblings(child); |
1408 | if (siblings->length == 1) { | 1566 | if (siblings->length == 1) { |
1409 | enum sway_container_layout current = container_parent_layout(child); | 1567 | enum sway_container_layout current = container_parent_layout(child); |
@@ -1411,12 +1569,12 @@ struct sway_container *container_split(struct sway_container *child, | |||
1411 | current = L_NONE; | 1569 | current = L_NONE; |
1412 | } | 1570 | } |
1413 | if (current == L_HORIZ || current == L_VERT) { | 1571 | if (current == L_HORIZ || current == L_VERT) { |
1414 | if (child->parent) { | 1572 | if (child->pending.parent) { |
1415 | child->parent->layout = layout; | 1573 | child->pending.parent->pending.layout = layout; |
1416 | container_update_representation(child->parent); | 1574 | container_update_representation(child->pending.parent); |
1417 | } else { | 1575 | } else { |
1418 | child->workspace->layout = layout; | 1576 | child->pending.workspace->layout = layout; |
1419 | workspace_update_representation(child->workspace); | 1577 | workspace_update_representation(child->pending.workspace); |
1420 | } | 1578 | } |
1421 | return child; | 1579 | return child; |
1422 | } | 1580 | } |
@@ -1429,25 +1587,25 @@ struct sway_container *container_split(struct sway_container *child, | |||
1429 | if (container_is_floating(child) && child->view) { | 1587 | if (container_is_floating(child) && child->view) { |
1430 | view_set_tiled(child->view, true); | 1588 | view_set_tiled(child->view, true); |
1431 | if (child->view->using_csd) { | 1589 | if (child->view->using_csd) { |
1432 | child->border = child->saved_border; | 1590 | child->pending.border = child->saved_border; |
1433 | } | 1591 | } |
1434 | } | 1592 | } |
1435 | 1593 | ||
1436 | struct sway_container *cont = container_create(NULL); | 1594 | struct sway_container *cont = container_create(NULL); |
1437 | cont->width = child->width; | 1595 | cont->pending.width = child->pending.width; |
1438 | cont->height = child->height; | 1596 | cont->pending.height = child->pending.height; |
1439 | cont->width_fraction = child->width_fraction; | 1597 | cont->width_fraction = child->width_fraction; |
1440 | cont->height_fraction = child->height_fraction; | 1598 | cont->height_fraction = child->height_fraction; |
1441 | cont->x = child->x; | 1599 | cont->pending.x = child->pending.x; |
1442 | cont->y = child->y; | 1600 | cont->pending.y = child->pending.y; |
1443 | cont->layout = layout; | 1601 | cont->pending.layout = layout; |
1444 | 1602 | ||
1445 | container_replace(child, cont); | 1603 | container_replace(child, cont); |
1446 | container_add_child(cont, child); | 1604 | container_add_child(cont, child); |
1447 | 1605 | ||
1448 | if (set_focus) { | 1606 | if (set_focus) { |
1449 | seat_set_raw_focus(seat, &cont->node); | 1607 | seat_set_raw_focus(seat, &cont->node); |
1450 | if (cont->fullscreen_mode == FULLSCREEN_GLOBAL) { | 1608 | if (cont->pending.fullscreen_mode == FULLSCREEN_GLOBAL) { |
1451 | seat_set_focus(seat, &child->node); | 1609 | seat_set_focus(seat, &child->node); |
1452 | } else { | 1610 | } else { |
1453 | seat_set_raw_focus(seat, &child->node); | 1611 | seat_set_raw_focus(seat, &child->node); |
@@ -1548,45 +1706,14 @@ static void update_marks_texture(struct sway_container *con, | |||
1548 | for (int i = 0; i < con->marks->length; ++i) { | 1706 | for (int i = 0; i < con->marks->length; ++i) { |
1549 | char *mark = con->marks->items[i]; | 1707 | char *mark = con->marks->items[i]; |
1550 | if (mark[0] != '_') { | 1708 | if (mark[0] != '_') { |
1551 | sprintf(part, "[%s]", mark); | 1709 | snprintf(part, len + 1, "[%s]", mark); |
1552 | strcat(buffer, part); | 1710 | strcat(buffer, part); |
1553 | } | 1711 | } |
1554 | } | 1712 | } |
1555 | free(part); | 1713 | free(part); |
1556 | 1714 | ||
1557 | double scale = output->wlr_output->scale; | 1715 | render_titlebar_text_texture(output, con, texture, class, false, buffer); |
1558 | int width = 0; | ||
1559 | int height = con->title_height * scale; | ||
1560 | |||
1561 | cairo_t *c = cairo_create(NULL); | ||
1562 | get_text_size(c, config->font, &width, NULL, NULL, scale, false, | ||
1563 | "%s", buffer); | ||
1564 | cairo_destroy(c); | ||
1565 | 1716 | ||
1566 | cairo_surface_t *surface = cairo_image_surface_create( | ||
1567 | CAIRO_FORMAT_ARGB32, width, height); | ||
1568 | cairo_t *cairo = cairo_create(surface); | ||
1569 | cairo_set_source_rgba(cairo, class->background[0], class->background[1], | ||
1570 | class->background[2], class->background[3]); | ||
1571 | cairo_paint(cairo); | ||
1572 | PangoContext *pango = pango_cairo_create_context(cairo); | ||
1573 | cairo_set_antialias(cairo, CAIRO_ANTIALIAS_BEST); | ||
1574 | cairo_set_source_rgba(cairo, class->text[0], class->text[1], | ||
1575 | class->text[2], class->text[3]); | ||
1576 | cairo_move_to(cairo, 0, 0); | ||
1577 | |||
1578 | pango_printf(cairo, config->font, scale, false, "%s", buffer); | ||
1579 | |||
1580 | cairo_surface_flush(surface); | ||
1581 | unsigned char *data = cairo_image_surface_get_data(surface); | ||
1582 | int stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, width); | ||
1583 | struct wlr_renderer *renderer = wlr_backend_get_renderer( | ||
1584 | output->wlr_output->backend); | ||
1585 | *texture = wlr_texture_from_pixels( | ||
1586 | renderer, WL_SHM_FORMAT_ARGB8888, stride, width, height, data); | ||
1587 | cairo_surface_destroy(surface); | ||
1588 | g_object_unref(pango); | ||
1589 | cairo_destroy(cairo); | ||
1590 | free(buffer); | 1717 | free(buffer); |
1591 | } | 1718 | } |
1592 | 1719 | ||
@@ -1602,25 +1729,27 @@ void container_update_marks_textures(struct sway_container *con) { | |||
1602 | &config->border_colors.unfocused); | 1729 | &config->border_colors.unfocused); |
1603 | update_marks_texture(con, &con->marks_urgent, | 1730 | update_marks_texture(con, &con->marks_urgent, |
1604 | &config->border_colors.urgent); | 1731 | &config->border_colors.urgent); |
1732 | update_marks_texture(con, &con->marks_focused_tab_title, | ||
1733 | &config->border_colors.focused_tab_title); | ||
1605 | container_damage_whole(con); | 1734 | container_damage_whole(con); |
1606 | } | 1735 | } |
1607 | 1736 | ||
1608 | void container_raise_floating(struct sway_container *con) { | 1737 | void container_raise_floating(struct sway_container *con) { |
1609 | // Bring container to front by putting it at the end of the floating list. | 1738 | // Bring container to front by putting it at the end of the floating list. |
1610 | struct sway_container *floater = container_toplevel_ancestor(con); | 1739 | struct sway_container *floater = container_toplevel_ancestor(con); |
1611 | if (container_is_floating(floater) && floater->workspace) { | 1740 | if (container_is_floating(floater) && floater->pending.workspace) { |
1612 | list_move_to_end(floater->workspace->floating, floater); | 1741 | list_move_to_end(floater->pending.workspace->floating, floater); |
1613 | node_set_dirty(&floater->workspace->node); | 1742 | node_set_dirty(&floater->pending.workspace->node); |
1614 | } | 1743 | } |
1615 | } | 1744 | } |
1616 | 1745 | ||
1617 | bool container_is_scratchpad_hidden(struct sway_container *con) { | 1746 | bool container_is_scratchpad_hidden(struct sway_container *con) { |
1618 | return con->scratchpad && !con->workspace; | 1747 | return con->scratchpad && !con->pending.workspace; |
1619 | } | 1748 | } |
1620 | 1749 | ||
1621 | bool container_is_scratchpad_hidden_or_child(struct sway_container *con) { | 1750 | bool container_is_scratchpad_hidden_or_child(struct sway_container *con) { |
1622 | con = container_toplevel_ancestor(con); | 1751 | con = container_toplevel_ancestor(con); |
1623 | return con->scratchpad && !con->workspace; | 1752 | return con->scratchpad && !con->pending.workspace; |
1624 | } | 1753 | } |
1625 | 1754 | ||
1626 | bool container_is_sticky(struct sway_container *con) { | 1755 | bool container_is_sticky(struct sway_container *con) { |
@@ -1648,39 +1777,39 @@ static bool is_parallel(enum sway_container_layout first, | |||
1648 | static bool container_is_squashable(struct sway_container *con, | 1777 | static bool container_is_squashable(struct sway_container *con, |
1649 | struct sway_container *child) { | 1778 | struct sway_container *child) { |
1650 | enum sway_container_layout gp_layout = container_parent_layout(con); | 1779 | enum sway_container_layout gp_layout = container_parent_layout(con); |
1651 | return (con->layout == L_HORIZ || con->layout == L_VERT) && | 1780 | return (con->pending.layout == L_HORIZ || con->pending.layout == L_VERT) && |
1652 | (child->layout == L_HORIZ || child->layout == L_VERT) && | 1781 | (child->pending.layout == L_HORIZ || child->pending.layout == L_VERT) && |
1653 | !is_parallel(con->layout, child->layout) && | 1782 | !is_parallel(con->pending.layout, child->pending.layout) && |
1654 | is_parallel(gp_layout, child->layout); | 1783 | is_parallel(gp_layout, child->pending.layout); |
1655 | } | 1784 | } |
1656 | 1785 | ||
1657 | static void container_squash_children(struct sway_container *con) { | 1786 | static void container_squash_children(struct sway_container *con) { |
1658 | for (int i = 0; i < con->children->length; i++) { | 1787 | for (int i = 0; i < con->pending.children->length; i++) { |
1659 | struct sway_container *child = con->children->items[i]; | 1788 | struct sway_container *child = con->pending.children->items[i]; |
1660 | i += container_squash(child); | 1789 | i += container_squash(child); |
1661 | } | 1790 | } |
1662 | } | 1791 | } |
1663 | 1792 | ||
1664 | int container_squash(struct sway_container *con) { | 1793 | int container_squash(struct sway_container *con) { |
1665 | if (!con->children) { | 1794 | if (!con->pending.children) { |
1666 | return 0; | 1795 | return 0; |
1667 | } | 1796 | } |
1668 | if (con->children->length != 1) { | 1797 | if (con->pending.children->length != 1) { |
1669 | container_squash_children(con); | 1798 | container_squash_children(con); |
1670 | return 0; | 1799 | return 0; |
1671 | } | 1800 | } |
1672 | struct sway_container *child = con->children->items[0]; | 1801 | struct sway_container *child = con->pending.children->items[0]; |
1673 | int idx = container_sibling_index(con); | 1802 | int idx = container_sibling_index(con); |
1674 | int change = 0; | 1803 | int change = 0; |
1675 | if (container_is_squashable(con, child)) { | 1804 | if (container_is_squashable(con, child)) { |
1676 | // con and child are a redundant H/V pair. Destroy them. | 1805 | // con and child are a redundant H/V pair. Destroy them. |
1677 | while (child->children->length) { | 1806 | while (child->pending.children->length) { |
1678 | struct sway_container *current = child->children->items[0]; | 1807 | struct sway_container *current = child->pending.children->items[0]; |
1679 | container_detach(current); | 1808 | container_detach(current); |
1680 | if (con->parent) { | 1809 | if (con->pending.parent) { |
1681 | container_insert_child(con->parent, current, idx); | 1810 | container_insert_child(con->pending.parent, current, idx); |
1682 | } else { | 1811 | } else { |
1683 | workspace_insert_tiling_direct(con->workspace, current, idx); | 1812 | workspace_insert_tiling_direct(con->pending.workspace, current, idx); |
1684 | } | 1813 | } |
1685 | change++; | 1814 | change++; |
1686 | } | 1815 | } |
diff --git a/sway/tree/node.c b/sway/tree/node.c index ffa7f2cc..bc7e2aa5 100644 --- a/sway/tree/node.c +++ b/sway/tree/node.c | |||
@@ -75,7 +75,7 @@ void node_get_box(struct sway_node *node, struct wlr_box *box) { | |||
75 | struct sway_output *node_get_output(struct sway_node *node) { | 75 | struct sway_output *node_get_output(struct sway_node *node) { |
76 | switch (node->type) { | 76 | switch (node->type) { |
77 | case N_CONTAINER: { | 77 | case N_CONTAINER: { |
78 | struct sway_workspace *ws = node->sway_container->workspace; | 78 | struct sway_workspace *ws = node->sway_container->pending.workspace; |
79 | return ws ? ws->output : NULL; | 79 | return ws ? ws->output : NULL; |
80 | } | 80 | } |
81 | case N_WORKSPACE: | 81 | case N_WORKSPACE: |
@@ -91,7 +91,7 @@ struct sway_output *node_get_output(struct sway_node *node) { | |||
91 | enum sway_container_layout node_get_layout(struct sway_node *node) { | 91 | enum sway_container_layout node_get_layout(struct sway_node *node) { |
92 | switch (node->type) { | 92 | switch (node->type) { |
93 | case N_CONTAINER: | 93 | case N_CONTAINER: |
94 | return node->sway_container->layout; | 94 | return node->sway_container->pending.layout; |
95 | case N_WORKSPACE: | 95 | case N_WORKSPACE: |
96 | return node->sway_workspace->layout; | 96 | return node->sway_workspace->layout; |
97 | case N_OUTPUT: | 97 | case N_OUTPUT: |
@@ -105,11 +105,11 @@ struct sway_node *node_get_parent(struct sway_node *node) { | |||
105 | switch (node->type) { | 105 | switch (node->type) { |
106 | case N_CONTAINER: { | 106 | case N_CONTAINER: { |
107 | struct sway_container *con = node->sway_container; | 107 | struct sway_container *con = node->sway_container; |
108 | if (con->parent) { | 108 | if (con->pending.parent) { |
109 | return &con->parent->node; | 109 | return &con->pending.parent->node; |
110 | } | 110 | } |
111 | if (con->workspace) { | 111 | if (con->pending.workspace) { |
112 | return &con->workspace->node; | 112 | return &con->pending.workspace->node; |
113 | } | 113 | } |
114 | } | 114 | } |
115 | return NULL; | 115 | return NULL; |
@@ -131,7 +131,7 @@ struct sway_node *node_get_parent(struct sway_node *node) { | |||
131 | list_t *node_get_children(struct sway_node *node) { | 131 | list_t *node_get_children(struct sway_node *node) { |
132 | switch (node->type) { | 132 | switch (node->type) { |
133 | case N_CONTAINER: | 133 | case N_CONTAINER: |
134 | return node->sway_container->children; | 134 | return node->sway_container->pending.children; |
135 | case N_WORKSPACE: | 135 | case N_WORKSPACE: |
136 | return node->sway_workspace->tiling; | 136 | return node->sway_workspace->tiling; |
137 | case N_OUTPUT: | 137 | case N_OUTPUT: |
@@ -143,7 +143,7 @@ list_t *node_get_children(struct sway_node *node) { | |||
143 | 143 | ||
144 | bool node_has_ancestor(struct sway_node *node, struct sway_node *ancestor) { | 144 | bool node_has_ancestor(struct sway_node *node, struct sway_node *ancestor) { |
145 | if (ancestor->type == N_ROOT && node->type == N_CONTAINER && | 145 | if (ancestor->type == N_ROOT && node->type == N_CONTAINER && |
146 | node->sway_container->fullscreen_mode == FULLSCREEN_GLOBAL) { | 146 | node->sway_container->pending.fullscreen_mode == FULLSCREEN_GLOBAL) { |
147 | return true; | 147 | return true; |
148 | } | 148 | } |
149 | struct sway_node *parent = node_get_parent(node); | 149 | struct sway_node *parent = node_get_parent(node); |
@@ -152,7 +152,7 @@ bool node_has_ancestor(struct sway_node *node, struct sway_node *ancestor) { | |||
152 | return true; | 152 | return true; |
153 | } | 153 | } |
154 | if (ancestor->type == N_ROOT && parent->type == N_CONTAINER && | 154 | if (ancestor->type == N_ROOT && parent->type == N_CONTAINER && |
155 | parent->sway_container->fullscreen_mode == FULLSCREEN_GLOBAL) { | 155 | parent->sway_container->pending.fullscreen_mode == FULLSCREEN_GLOBAL) { |
156 | return true; | 156 | return true; |
157 | } | 157 | } |
158 | parent = node_get_parent(parent); | 158 | parent = node_get_parent(parent); |
diff --git a/sway/tree/output.c b/sway/tree/output.c index a8ae30f7..52826c91 100644 --- a/sway/tree/output.c +++ b/sway/tree/output.c | |||
@@ -56,8 +56,8 @@ static void restore_workspaces(struct sway_output *output) { | |||
56 | } | 56 | } |
57 | 57 | ||
58 | // Saved workspaces | 58 | // Saved workspaces |
59 | while (root->noop_output->workspaces->length) { | 59 | while (root->fallback_output->workspaces->length) { |
60 | struct sway_workspace *ws = root->noop_output->workspaces->items[0]; | 60 | struct sway_workspace *ws = root->fallback_output->workspaces->items[0]; |
61 | workspace_detach(ws); | 61 | workspace_detach(ws); |
62 | output_add_workspace(output, ws); | 62 | output_add_workspace(output, ws); |
63 | 63 | ||
@@ -70,13 +70,13 @@ static void restore_workspaces(struct sway_output *output) { | |||
70 | // floater re-centered | 70 | // floater re-centered |
71 | for (int i = 0; i < ws->floating->length; i++) { | 71 | for (int i = 0; i < ws->floating->length; i++) { |
72 | struct sway_container *floater = ws->floating->items[i]; | 72 | struct sway_container *floater = ws->floating->items[i]; |
73 | if (floater->width == 0 || floater->height == 0 || | 73 | if (floater->pending.width == 0 || floater->pending.height == 0 || |
74 | floater->width > output->width || | 74 | floater->pending.width > output->width || |
75 | floater->height > output->height || | 75 | floater->pending.height > output->height || |
76 | floater->x > output->lx + output->width || | 76 | floater->pending.x > output->lx + output->width || |
77 | floater->y > output->ly + output->height || | 77 | floater->pending.y > output->ly + output->height || |
78 | floater->x + floater->width < output->lx || | 78 | floater->pending.x + floater->pending.width < output->lx || |
79 | floater->y + floater->height < output->ly) { | 79 | floater->pending.y + floater->pending.height < output->ly) { |
80 | container_floating_resize_and_center(floater); | 80 | container_floating_resize_and_center(floater); |
81 | } | 81 | } |
82 | } | 82 | } |
@@ -95,7 +95,7 @@ struct sway_output *output_create(struct wlr_output *wlr_output) { | |||
95 | output->detected_subpixel = wlr_output->subpixel; | 95 | output->detected_subpixel = wlr_output->subpixel; |
96 | output->scale_filter = SCALE_FILTER_NEAREST; | 96 | output->scale_filter = SCALE_FILTER_NEAREST; |
97 | 97 | ||
98 | wl_signal_init(&output->events.destroy); | 98 | wl_signal_init(&output->events.disable); |
99 | 99 | ||
100 | wl_list_insert(&root->all_outputs, &output->link); | 100 | wl_list_insert(&root->all_outputs, &output->link); |
101 | 101 | ||
@@ -192,7 +192,7 @@ static void output_evacuate(struct sway_output *output) { | |||
192 | new_output = fallback_output; | 192 | new_output = fallback_output; |
193 | } | 193 | } |
194 | if (!new_output) { | 194 | if (!new_output) { |
195 | new_output = root->noop_output; | 195 | new_output = root->fallback_output; |
196 | } | 196 | } |
197 | 197 | ||
198 | struct sway_workspace *new_output_ws = | 198 | struct sway_workspace *new_output_ws = |
@@ -262,7 +262,7 @@ void output_disable(struct sway_output *output) { | |||
262 | } | 262 | } |
263 | 263 | ||
264 | sway_log(SWAY_DEBUG, "Disabling output '%s'", output->wlr_output->name); | 264 | sway_log(SWAY_DEBUG, "Disabling output '%s'", output->wlr_output->name); |
265 | wl_signal_emit(&output->events.destroy, output); | 265 | wl_signal_emit(&output->events.disable, output); |
266 | 266 | ||
267 | output_evacuate(output); | 267 | output_evacuate(output); |
268 | 268 | ||
@@ -286,13 +286,10 @@ void output_begin_destroy(struct sway_output *output) { | |||
286 | return; | 286 | return; |
287 | } | 287 | } |
288 | sway_log(SWAY_DEBUG, "Destroying output '%s'", output->wlr_output->name); | 288 | sway_log(SWAY_DEBUG, "Destroying output '%s'", output->wlr_output->name); |
289 | wl_signal_emit(&output->node.events.destroy, &output->node); | ||
289 | 290 | ||
290 | output->node.destroying = true; | 291 | output->node.destroying = true; |
291 | node_set_dirty(&output->node); | 292 | node_set_dirty(&output->node); |
292 | |||
293 | wl_list_remove(&output->link); | ||
294 | output->wlr_output->data = NULL; | ||
295 | output->wlr_output = NULL; | ||
296 | } | 293 | } |
297 | 294 | ||
298 | struct sway_output *output_from_wlr_output(struct wlr_output *output) { | 295 | struct sway_output *output_from_wlr_output(struct wlr_output *output) { |
@@ -304,10 +301,10 @@ struct sway_output *output_get_in_direction(struct sway_output *reference, | |||
304 | if (!sway_assert(direction, "got invalid direction: %d", direction)) { | 301 | if (!sway_assert(direction, "got invalid direction: %d", direction)) { |
305 | return NULL; | 302 | return NULL; |
306 | } | 303 | } |
307 | struct wlr_box *output_box = | 304 | struct wlr_box output_box; |
308 | wlr_output_layout_get_box(root->output_layout, reference->wlr_output); | 305 | wlr_output_layout_get_box(root->output_layout, reference->wlr_output, &output_box); |
309 | int lx = output_box->x + output_box->width / 2; | 306 | int lx = output_box.x + output_box.width / 2; |
310 | int ly = output_box->y + output_box->height / 2; | 307 | int ly = output_box.y + output_box.height / 2; |
311 | struct wlr_output *wlr_adjacent = wlr_output_layout_adjacent_output( | 308 | struct wlr_output *wlr_adjacent = wlr_output_layout_adjacent_output( |
312 | root->output_layout, direction, reference->wlr_output, lx, ly); | 309 | root->output_layout, direction, reference->wlr_output, lx, ly); |
313 | if (!wlr_adjacent) { | 310 | if (!wlr_adjacent) { |
diff --git a/sway/tree/root.c b/sway/tree/root.c index ebd185ec..8508e9eb 100644 --- a/sway/tree/root.c +++ b/sway/tree/root.c | |||
@@ -59,11 +59,11 @@ void root_scratchpad_add_container(struct sway_container *con, struct sway_works | |||
59 | return; | 59 | return; |
60 | } | 60 | } |
61 | 61 | ||
62 | struct sway_container *parent = con->parent; | 62 | struct sway_container *parent = con->pending.parent; |
63 | struct sway_workspace *workspace = con->workspace; | 63 | struct sway_workspace *workspace = con->pending.workspace; |
64 | 64 | ||
65 | // Clear the fullscreen mode when sending to the scratchpad | 65 | // Clear the fullscreen mode when sending to the scratchpad |
66 | if (con->fullscreen_mode != FULLSCREEN_NONE) { | 66 | if (con->pending.fullscreen_mode != FULLSCREEN_NONE) { |
67 | container_fullscreen_disable(con); | 67 | container_fullscreen_disable(con); |
68 | } | 68 | } |
69 | 69 | ||
@@ -117,7 +117,7 @@ void root_scratchpad_show(struct sway_container *con) { | |||
117 | sway_log(SWAY_DEBUG, "No focused workspace to show scratchpad on"); | 117 | sway_log(SWAY_DEBUG, "No focused workspace to show scratchpad on"); |
118 | return; | 118 | return; |
119 | } | 119 | } |
120 | struct sway_workspace *old_ws = con->workspace; | 120 | struct sway_workspace *old_ws = con->pending.workspace; |
121 | 121 | ||
122 | // If the current con or any of its parents are in fullscreen mode, we | 122 | // If the current con or any of its parents are in fullscreen mode, we |
123 | // first need to disable it before showing the scratchpad con. | 123 | // first need to disable it before showing the scratchpad con. |
@@ -134,15 +134,15 @@ void root_scratchpad_show(struct sway_container *con) { | |||
134 | workspace_consider_destroy(old_ws); | 134 | workspace_consider_destroy(old_ws); |
135 | } else { | 135 | } else { |
136 | // Act on the ancestor of scratchpad hidden split containers | 136 | // Act on the ancestor of scratchpad hidden split containers |
137 | while (con->parent) { | 137 | while (con->pending.parent) { |
138 | con = con->parent; | 138 | con = con->pending.parent; |
139 | } | 139 | } |
140 | } | 140 | } |
141 | workspace_add_floating(new_ws, con); | 141 | workspace_add_floating(new_ws, con); |
142 | 142 | ||
143 | // Make sure the container's center point overlaps this workspace | 143 | // Make sure the container's center point overlaps this workspace |
144 | double center_lx = con->x + con->width / 2; | 144 | double center_lx = con->pending.x + con->pending.width / 2; |
145 | double center_ly = con->y + con->height / 2; | 145 | double center_ly = con->pending.y + con->pending.height / 2; |
146 | 146 | ||
147 | struct wlr_box workspace_box; | 147 | struct wlr_box workspace_box; |
148 | workspace_get_box(new_ws, &workspace_box); | 148 | workspace_get_box(new_ws, &workspace_box); |
@@ -155,7 +155,7 @@ void root_scratchpad_show(struct sway_container *con) { | |||
155 | } | 155 | } |
156 | 156 | ||
157 | static void disable_fullscreen(struct sway_container *con, void *data) { | 157 | static void disable_fullscreen(struct sway_container *con, void *data) { |
158 | if (con->fullscreen_mode != FULLSCREEN_NONE) { | 158 | if (con->pending.fullscreen_mode != FULLSCREEN_NONE) { |
159 | container_fullscreen_disable(con); | 159 | container_fullscreen_disable(con); |
160 | } | 160 | } |
161 | } | 161 | } |
@@ -163,9 +163,9 @@ static void disable_fullscreen(struct sway_container *con, void *data) { | |||
163 | void root_scratchpad_hide(struct sway_container *con) { | 163 | void root_scratchpad_hide(struct sway_container *con) { |
164 | struct sway_seat *seat = input_manager_current_seat(); | 164 | struct sway_seat *seat = input_manager_current_seat(); |
165 | struct sway_node *focus = seat_get_focus_inactive(seat, &root->node); | 165 | struct sway_node *focus = seat_get_focus_inactive(seat, &root->node); |
166 | struct sway_workspace *ws = con->workspace; | 166 | struct sway_workspace *ws = con->pending.workspace; |
167 | 167 | ||
168 | if (con->fullscreen_mode == FULLSCREEN_GLOBAL && !con->workspace) { | 168 | if (con->pending.fullscreen_mode == FULLSCREEN_GLOBAL && !con->pending.workspace) { |
169 | // If the container was made fullscreen global while in the scratchpad, | 169 | // If the container was made fullscreen global while in the scratchpad, |
170 | // it should be shown until fullscreen has been disabled | 170 | // it should be shown until fullscreen has been disabled |
171 | return; | 171 | return; |
@@ -209,7 +209,7 @@ static pid_t get_parent_pid(pid_t child) { | |||
209 | FILE *stat = NULL; | 209 | FILE *stat = NULL; |
210 | size_t buf_size = 0; | 210 | size_t buf_size = 0; |
211 | 211 | ||
212 | sprintf(file_name, "/proc/%d/stat", child); | 212 | snprintf(file_name, sizeof(file_name), "/proc/%d/stat", child); |
213 | 213 | ||
214 | if ((stat = fopen(file_name, "r"))) { | 214 | if ((stat = fopen(file_name, "r"))) { |
215 | if (getline(&buffer, &buf_size, stat) != -1) { | 215 | if (getline(&buffer, &buf_size, stat) != -1) { |
@@ -270,7 +270,16 @@ found: | |||
270 | sway_log(SWAY_DEBUG, | 270 | sway_log(SWAY_DEBUG, |
271 | "Creating workspace %s for pid %d because it disappeared", | 271 | "Creating workspace %s for pid %d because it disappeared", |
272 | pw->workspace, pid); | 272 | pw->workspace, pid); |
273 | ws = workspace_create(pw->output, pw->workspace); | 273 | |
274 | struct sway_output *output = pw->output; | ||
275 | if (pw->output && !pw->output->enabled) { | ||
276 | sway_log(SWAY_DEBUG, | ||
277 | "Workspace output %s is disabled, trying another one", | ||
278 | pw->output->wlr_output->name); | ||
279 | output = NULL; | ||
280 | } | ||
281 | |||
282 | ws = workspace_create(output, pw->workspace); | ||
274 | } | 283 | } |
275 | 284 | ||
276 | pid_workspace_destroy(pw); | 285 | pid_workspace_destroy(pw); |
@@ -365,8 +374,8 @@ void root_for_each_container(void (*f)(struct sway_container *con, void *data), | |||
365 | } | 374 | } |
366 | 375 | ||
367 | // Saved workspaces | 376 | // Saved workspaces |
368 | for (int i = 0; i < root->noop_output->workspaces->length; ++i) { | 377 | for (int i = 0; i < root->fallback_output->workspaces->length; ++i) { |
369 | struct sway_workspace *ws = root->noop_output->workspaces->items[i]; | 378 | struct sway_workspace *ws = root->fallback_output->workspaces->items[i]; |
370 | workspace_for_each_container(ws, f, data); | 379 | workspace_for_each_container(ws, f, data); |
371 | } | 380 | } |
372 | } | 381 | } |
@@ -418,8 +427,8 @@ struct sway_container *root_find_container( | |||
418 | } | 427 | } |
419 | 428 | ||
420 | // Saved workspaces | 429 | // Saved workspaces |
421 | for (int i = 0; i < root->noop_output->workspaces->length; ++i) { | 430 | for (int i = 0; i < root->fallback_output->workspaces->length; ++i) { |
422 | struct sway_workspace *ws = root->noop_output->workspaces->items[i]; | 431 | struct sway_workspace *ws = root->fallback_output->workspaces->items[i]; |
423 | if ((result = workspace_find_container(ws, test, data))) { | 432 | if ((result = workspace_find_container(ws, test, data))) { |
424 | return result; | 433 | return result; |
425 | } | 434 | } |
diff --git a/sway/tree/view.c b/sway/tree/view.c index 7afcdf31..7d9e038d 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c | |||
@@ -6,6 +6,7 @@ | |||
6 | #include <wlr/types/wlr_buffer.h> | 6 | #include <wlr/types/wlr_buffer.h> |
7 | #include <wlr/types/wlr_output_layout.h> | 7 | #include <wlr/types/wlr_output_layout.h> |
8 | #include <wlr/types/wlr_server_decoration.h> | 8 | #include <wlr/types/wlr_server_decoration.h> |
9 | #include <wlr/types/wlr_subcompositor.h> | ||
9 | #include <wlr/types/wlr_xdg_decoration_v1.h> | 10 | #include <wlr/types/wlr_xdg_decoration_v1.h> |
10 | #include "config.h" | 11 | #include "config.h" |
11 | #if HAVE_XWAYLAND | 12 | #if HAVE_XWAYLAND |
@@ -56,6 +57,7 @@ void view_destroy(struct sway_view *view) { | |||
56 | "(might have a pending transaction?)")) { | 57 | "(might have a pending transaction?)")) { |
57 | return; | 58 | return; |
58 | } | 59 | } |
60 | wl_list_remove(&view->events.unmap.listener_list); | ||
59 | if (!wl_list_empty(&view->saved_buffers)) { | 61 | if (!wl_list_empty(&view->saved_buffers)) { |
60 | view_remove_saved_buffer(view); | 62 | view_remove_saved_buffer(view); |
61 | } | 63 | } |
@@ -206,7 +208,7 @@ bool view_ancestor_is_only_visible(struct sway_view *view) { | |||
206 | } else { | 208 | } else { |
207 | only_visible = true; | 209 | only_visible = true; |
208 | } | 210 | } |
209 | con = con->parent; | 211 | con = con->pending.parent; |
210 | } | 212 | } |
211 | return only_visible; | 213 | return only_visible; |
212 | } | 214 | } |
@@ -222,72 +224,73 @@ static bool view_is_only_visible(struct sway_view *view) { | |||
222 | } | 224 | } |
223 | } | 225 | } |
224 | 226 | ||
225 | con = con->parent; | 227 | con = con->pending.parent; |
226 | } | 228 | } |
227 | 229 | ||
228 | return true; | 230 | return true; |
229 | } | 231 | } |
230 | 232 | ||
231 | static bool gaps_to_edge(struct sway_view *view) { | 233 | static bool gaps_to_edge(struct sway_view *view) { |
232 | struct side_gaps gaps = view->container->workspace->current_gaps; | 234 | struct side_gaps gaps = view->container->pending.workspace->current_gaps; |
233 | return gaps.top > 0 || gaps.right > 0 || gaps.bottom > 0 || gaps.left > 0; | 235 | return gaps.top > 0 || gaps.right > 0 || gaps.bottom > 0 || gaps.left > 0; |
234 | } | 236 | } |
235 | 237 | ||
236 | void view_autoconfigure(struct sway_view *view) { | 238 | void view_autoconfigure(struct sway_view *view) { |
237 | struct sway_container *con = view->container; | 239 | struct sway_container *con = view->container; |
238 | struct sway_workspace *ws = con->workspace; | 240 | struct sway_workspace *ws = con->pending.workspace; |
239 | 241 | ||
240 | if (container_is_scratchpad_hidden(con) && | 242 | if (container_is_scratchpad_hidden(con) && |
241 | con->fullscreen_mode != FULLSCREEN_GLOBAL) { | 243 | con->pending.fullscreen_mode != FULLSCREEN_GLOBAL) { |
242 | return; | 244 | return; |
243 | } | 245 | } |
244 | struct sway_output *output = ws ? ws->output : NULL; | 246 | struct sway_output *output = ws ? ws->output : NULL; |
245 | 247 | ||
246 | if (con->fullscreen_mode == FULLSCREEN_WORKSPACE) { | 248 | if (con->pending.fullscreen_mode == FULLSCREEN_WORKSPACE) { |
247 | con->content_x = output->lx; | 249 | con->pending.content_x = output->lx; |
248 | con->content_y = output->ly; | 250 | con->pending.content_y = output->ly; |
249 | con->content_width = output->width; | 251 | con->pending.content_width = output->width; |
250 | con->content_height = output->height; | 252 | con->pending.content_height = output->height; |
251 | return; | 253 | return; |
252 | } else if (con->fullscreen_mode == FULLSCREEN_GLOBAL) { | 254 | } else if (con->pending.fullscreen_mode == FULLSCREEN_GLOBAL) { |
253 | con->content_x = root->x; | 255 | con->pending.content_x = root->x; |
254 | con->content_y = root->y; | 256 | con->pending.content_y = root->y; |
255 | con->content_width = root->width; | 257 | con->pending.content_width = root->width; |
256 | con->content_height = root->height; | 258 | con->pending.content_height = root->height; |
257 | return; | 259 | return; |
258 | } | 260 | } |
259 | 261 | ||
260 | con->border_top = con->border_bottom = true; | 262 | con->pending.border_top = con->pending.border_bottom = true; |
261 | con->border_left = con->border_right = true; | 263 | con->pending.border_left = con->pending.border_right = true; |
262 | double y_offset = 0; | 264 | double y_offset = 0; |
263 | 265 | ||
264 | if (!container_is_floating(con) && ws) { | 266 | if (!container_is_floating_or_child(con) && ws) { |
265 | if (config->hide_edge_borders == E_BOTH | 267 | if (config->hide_edge_borders == E_BOTH |
266 | || config->hide_edge_borders == E_VERTICAL) { | 268 | || config->hide_edge_borders == E_VERTICAL) { |
267 | con->border_left = con->x != ws->x; | 269 | con->pending.border_left = con->pending.x != ws->x; |
268 | int right_x = con->x + con->width; | 270 | int right_x = con->pending.x + con->pending.width; |
269 | con->border_right = right_x != ws->x + ws->width; | 271 | con->pending.border_right = right_x != ws->x + ws->width; |
270 | } | 272 | } |
271 | 273 | ||
272 | if (config->hide_edge_borders == E_BOTH | 274 | if (config->hide_edge_borders == E_BOTH |
273 | || config->hide_edge_borders == E_HORIZONTAL) { | 275 | || config->hide_edge_borders == E_HORIZONTAL) { |
274 | con->border_top = con->y != ws->y; | 276 | con->pending.border_top = con->pending.y != ws->y; |
275 | int bottom_y = con->y + con->height; | 277 | int bottom_y = con->pending.y + con->pending.height; |
276 | con->border_bottom = bottom_y != ws->y + ws->height; | 278 | con->pending.border_bottom = bottom_y != ws->y + ws->height; |
277 | } | 279 | } |
278 | 280 | ||
279 | bool smart = config->hide_edge_borders_smart == ESMART_ON || | 281 | bool smart = config->hide_edge_borders_smart == ESMART_ON || |
280 | (config->hide_edge_borders_smart == ESMART_NO_GAPS && | 282 | (config->hide_edge_borders_smart == ESMART_NO_GAPS && |
281 | !gaps_to_edge(view)); | 283 | !gaps_to_edge(view)); |
282 | if (smart) { | 284 | if (smart) { |
283 | bool show_border = container_is_floating_or_child(con) || | 285 | bool show_border = !view_is_only_visible(view); |
284 | !view_is_only_visible(view); | 286 | con->pending.border_left &= show_border; |
285 | con->border_left &= show_border; | 287 | con->pending.border_right &= show_border; |
286 | con->border_right &= show_border; | 288 | con->pending.border_top &= show_border; |
287 | con->border_top &= show_border; | 289 | con->pending.border_bottom &= show_border; |
288 | con->border_bottom &= show_border; | ||
289 | } | 290 | } |
291 | } | ||
290 | 292 | ||
293 | if (!container_is_floating(con)) { | ||
291 | // In a tabbed or stacked container, the container's y is the top of the | 294 | // In a tabbed or stacked container, the container's y is the top of the |
292 | // title area. We have to offset the surface y by the height of the title, | 295 | // title area. We have to offset the surface y by the height of the title, |
293 | // bar, and disable any top border because we'll always have the title bar. | 296 | // bar, and disable any top border because we'll always have the title bar. |
@@ -298,56 +301,56 @@ void view_autoconfigure(struct sway_view *view) { | |||
298 | enum sway_container_layout layout = container_parent_layout(con); | 301 | enum sway_container_layout layout = container_parent_layout(con); |
299 | if (layout == L_TABBED) { | 302 | if (layout == L_TABBED) { |
300 | y_offset = container_titlebar_height(); | 303 | y_offset = container_titlebar_height(); |
301 | con->border_top = false; | 304 | con->pending.border_top = false; |
302 | } else if (layout == L_STACKED) { | 305 | } else if (layout == L_STACKED) { |
303 | y_offset = container_titlebar_height() * siblings->length; | 306 | y_offset = container_titlebar_height() * siblings->length; |
304 | con->border_top = false; | 307 | con->pending.border_top = false; |
305 | } | 308 | } |
306 | } | 309 | } |
307 | } | 310 | } |
308 | 311 | ||
309 | double x, y, width, height; | 312 | double x, y, width, height; |
310 | switch (con->border) { | 313 | switch (con->pending.border) { |
311 | default: | 314 | default: |
312 | case B_CSD: | 315 | case B_CSD: |
313 | case B_NONE: | 316 | case B_NONE: |
314 | x = con->x; | 317 | x = con->pending.x; |
315 | y = con->y + y_offset; | 318 | y = con->pending.y + y_offset; |
316 | width = con->width; | 319 | width = con->pending.width; |
317 | height = con->height - y_offset; | 320 | height = con->pending.height - y_offset; |
318 | break; | 321 | break; |
319 | case B_PIXEL: | 322 | case B_PIXEL: |
320 | x = con->x + con->border_thickness * con->border_left; | 323 | x = con->pending.x + con->pending.border_thickness * con->pending.border_left; |
321 | y = con->y + con->border_thickness * con->border_top + y_offset; | 324 | y = con->pending.y + con->pending.border_thickness * con->pending.border_top + y_offset; |
322 | width = con->width | 325 | width = con->pending.width |
323 | - con->border_thickness * con->border_left | 326 | - con->pending.border_thickness * con->pending.border_left |
324 | - con->border_thickness * con->border_right; | 327 | - con->pending.border_thickness * con->pending.border_right; |
325 | height = con->height - y_offset | 328 | height = con->pending.height - y_offset |
326 | - con->border_thickness * con->border_top | 329 | - con->pending.border_thickness * con->pending.border_top |
327 | - con->border_thickness * con->border_bottom; | 330 | - con->pending.border_thickness * con->pending.border_bottom; |
328 | break; | 331 | break; |
329 | case B_NORMAL: | 332 | case B_NORMAL: |
330 | // Height is: 1px border + 3px pad + title height + 3px pad + 1px border | 333 | // Height is: 1px border + 3px pad + title height + 3px pad + 1px border |
331 | x = con->x + con->border_thickness * con->border_left; | 334 | x = con->pending.x + con->pending.border_thickness * con->pending.border_left; |
332 | width = con->width | 335 | width = con->pending.width |
333 | - con->border_thickness * con->border_left | 336 | - con->pending.border_thickness * con->pending.border_left |
334 | - con->border_thickness * con->border_right; | 337 | - con->pending.border_thickness * con->pending.border_right; |
335 | if (y_offset) { | 338 | if (y_offset) { |
336 | y = con->y + y_offset; | 339 | y = con->pending.y + y_offset; |
337 | height = con->height - y_offset | 340 | height = con->pending.height - y_offset |
338 | - con->border_thickness * con->border_bottom; | 341 | - con->pending.border_thickness * con->pending.border_bottom; |
339 | } else { | 342 | } else { |
340 | y = con->y + container_titlebar_height(); | 343 | y = con->pending.y + container_titlebar_height(); |
341 | height = con->height - container_titlebar_height() | 344 | height = con->pending.height - container_titlebar_height() |
342 | - con->border_thickness * con->border_bottom; | 345 | - con->pending.border_thickness * con->pending.border_bottom; |
343 | } | 346 | } |
344 | break; | 347 | break; |
345 | } | 348 | } |
346 | 349 | ||
347 | con->content_x = x; | 350 | con->pending.content_x = x; |
348 | con->content_y = y; | 351 | con->pending.content_y = y; |
349 | con->content_width = width; | 352 | con->pending.content_width = width; |
350 | con->content_height = height; | 353 | con->pending.content_height = height; |
351 | } | 354 | } |
352 | 355 | ||
353 | void view_set_activated(struct sway_view *view, bool activated) { | 356 | void view_set_activated(struct sway_view *view, bool activated) { |
@@ -361,7 +364,7 @@ void view_set_activated(struct sway_view *view, bool activated) { | |||
361 | } | 364 | } |
362 | 365 | ||
363 | void view_request_activate(struct sway_view *view) { | 366 | void view_request_activate(struct sway_view *view) { |
364 | struct sway_workspace *ws = view->container->workspace; | 367 | struct sway_workspace *ws = view->container->pending.workspace; |
365 | if (!ws) { // hidden scratchpad container | 368 | if (!ws) { // hidden scratchpad container |
366 | return; | 369 | return; |
367 | } | 370 | } |
@@ -401,13 +404,13 @@ void view_set_csd_from_server(struct sway_view *view, bool enabled) { | |||
401 | void view_update_csd_from_client(struct sway_view *view, bool enabled) { | 404 | void view_update_csd_from_client(struct sway_view *view, bool enabled) { |
402 | sway_log(SWAY_DEBUG, "View %p updated CSD to %i", view, enabled); | 405 | sway_log(SWAY_DEBUG, "View %p updated CSD to %i", view, enabled); |
403 | struct sway_container *con = view->container; | 406 | struct sway_container *con = view->container; |
404 | if (enabled && con && con->border != B_CSD) { | 407 | if (enabled && con && con->pending.border != B_CSD) { |
405 | con->saved_border = con->border; | 408 | con->saved_border = con->pending.border; |
406 | if (container_is_floating(con)) { | 409 | if (container_is_floating(con)) { |
407 | con->border = B_CSD; | 410 | con->pending.border = B_CSD; |
408 | } | 411 | } |
409 | } else if (!enabled && con && con->border == B_CSD) { | 412 | } else if (!enabled && con && con->pending.border == B_CSD) { |
410 | con->border = con->saved_border; | 413 | con->pending.border = con->saved_border; |
411 | } | 414 | } |
412 | view->using_csd = enabled; | 415 | view->using_csd = enabled; |
413 | } | 416 | } |
@@ -465,6 +468,9 @@ static void view_subsurface_create(struct sway_view *view, | |||
465 | static void view_init_subsurfaces(struct sway_view *view, | 468 | static void view_init_subsurfaces(struct sway_view *view, |
466 | struct wlr_surface *surface); | 469 | struct wlr_surface *surface); |
467 | 470 | ||
471 | static void view_child_init_subsurfaces(struct sway_view_child *view_child, | ||
472 | struct wlr_surface *surface); | ||
473 | |||
468 | static void view_handle_surface_new_subsurface(struct wl_listener *listener, | 474 | static void view_handle_surface_new_subsurface(struct wl_listener *listener, |
469 | void *data) { | 475 | void *data) { |
470 | struct sway_view *view = | 476 | struct sway_view *view = |
@@ -577,7 +583,7 @@ static struct sway_workspace *select_workspace(struct sway_view *view) { | |||
577 | if (node && node->type == N_WORKSPACE) { | 583 | if (node && node->type == N_WORKSPACE) { |
578 | return node->sway_workspace; | 584 | return node->sway_workspace; |
579 | } else if (node && node->type == N_CONTAINER) { | 585 | } else if (node && node->type == N_CONTAINER) { |
580 | return node->sway_container->workspace; | 586 | return node->sway_container->pending.workspace; |
581 | } | 587 | } |
582 | 588 | ||
583 | // When there's no outputs connected, the above should match a workspace on | 589 | // When there's no outputs connected, the above should match a workspace on |
@@ -590,12 +596,17 @@ static bool should_focus(struct sway_view *view) { | |||
590 | struct sway_seat *seat = input_manager_current_seat(); | 596 | struct sway_seat *seat = input_manager_current_seat(); |
591 | struct sway_container *prev_con = seat_get_focused_container(seat); | 597 | struct sway_container *prev_con = seat_get_focused_container(seat); |
592 | struct sway_workspace *prev_ws = seat_get_focused_workspace(seat); | 598 | struct sway_workspace *prev_ws = seat_get_focused_workspace(seat); |
593 | struct sway_workspace *map_ws = view->container->workspace; | 599 | struct sway_workspace *map_ws = view->container->pending.workspace; |
594 | 600 | ||
595 | if (view->container->fullscreen_mode == FULLSCREEN_GLOBAL) { | 601 | if (view->container->pending.fullscreen_mode == FULLSCREEN_GLOBAL) { |
596 | return true; | 602 | return true; |
597 | } | 603 | } |
598 | 604 | ||
605 | // View opened "under" fullscreen view should not be given focus. | ||
606 | if (root->fullscreen_global || !map_ws || map_ws->fullscreen) { | ||
607 | return false; | ||
608 | } | ||
609 | |||
599 | // Views can only take focus if they are mapped into the active workspace | 610 | // Views can only take focus if they are mapped into the active workspace |
600 | if (prev_ws != map_ws) { | 611 | if (prev_ws != map_ws) { |
601 | return false; | 612 | return false; |
@@ -603,9 +614,9 @@ static bool should_focus(struct sway_view *view) { | |||
603 | 614 | ||
604 | // If the view is the only one in the focused workspace, it'll get focus | 615 | // If the view is the only one in the focused workspace, it'll get focus |
605 | // regardless of any no_focus criteria. | 616 | // regardless of any no_focus criteria. |
606 | if (!view->container->parent && !prev_con) { | 617 | if (!view->container->pending.parent && !prev_con) { |
607 | size_t num_children = view->container->workspace->tiling->length + | 618 | size_t num_children = view->container->pending.workspace->tiling->length + |
608 | view->container->workspace->floating->length; | 619 | view->container->pending.workspace->floating->length; |
609 | if (num_children == 1) { | 620 | if (num_children == 1) { |
610 | return true; | 621 | return true; |
611 | } | 622 | } |
@@ -635,6 +646,7 @@ static void handle_foreign_activate_request( | |||
635 | break; | 646 | break; |
636 | } | 647 | } |
637 | } | 648 | } |
649 | transaction_commit_dirty(); | ||
638 | } | 650 | } |
639 | 651 | ||
640 | static void handle_foreign_fullscreen_request( | 652 | static void handle_foreign_fullscreen_request( |
@@ -645,9 +657,21 @@ static void handle_foreign_fullscreen_request( | |||
645 | 657 | ||
646 | // Match fullscreen command behavior for scratchpad hidden views | 658 | // Match fullscreen command behavior for scratchpad hidden views |
647 | struct sway_container *container = view->container; | 659 | struct sway_container *container = view->container; |
648 | if (!container->workspace) { | 660 | if (!container->pending.workspace) { |
649 | while (container->parent) { | 661 | while (container->pending.parent) { |
650 | container = container->parent; | 662 | container = container->pending.parent; |
663 | } | ||
664 | } | ||
665 | |||
666 | if (event->fullscreen && event->output && event->output->data) { | ||
667 | struct sway_output *output = event->output->data; | ||
668 | struct sway_workspace *ws = output_get_active_workspace(output); | ||
669 | if (ws && !container_is_scratchpad_hidden(view->container)) { | ||
670 | if (container_is_floating(view->container)) { | ||
671 | workspace_add_floating(ws, view->container); | ||
672 | } else { | ||
673 | workspace_add_tiling(ws, view->container); | ||
674 | } | ||
651 | } | 675 | } |
652 | } | 676 | } |
653 | 677 | ||
@@ -656,12 +680,13 @@ static void handle_foreign_fullscreen_request( | |||
656 | if (event->fullscreen) { | 680 | if (event->fullscreen) { |
657 | arrange_root(); | 681 | arrange_root(); |
658 | } else { | 682 | } else { |
659 | if (container->parent) { | 683 | if (container->pending.parent) { |
660 | arrange_container(container->parent); | 684 | arrange_container(container->pending.parent); |
661 | } else if (container->workspace) { | 685 | } else if (container->pending.workspace) { |
662 | arrange_workspace(container->workspace); | 686 | arrange_workspace(container->pending.workspace); |
663 | } | 687 | } |
664 | } | 688 | } |
689 | transaction_commit_dirty(); | ||
665 | } | 690 | } |
666 | 691 | ||
667 | static void handle_foreign_close_request( | 692 | static void handle_foreign_close_request( |
@@ -705,10 +730,29 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface, | |||
705 | } | 730 | } |
706 | 731 | ||
707 | struct sway_seat *seat = input_manager_current_seat(); | 732 | struct sway_seat *seat = input_manager_current_seat(); |
708 | struct sway_node *node = ws ? seat_get_focus_inactive(seat, &ws->node) | 733 | struct sway_node *node = |
709 | : seat_get_focus_inactive(seat, &root->node); | 734 | seat_get_focus_inactive(seat, ws ? &ws->node : &root->node); |
710 | struct sway_container *target_sibling = node->type == N_CONTAINER ? | 735 | struct sway_container *target_sibling = NULL; |
711 | node->sway_container : NULL; | 736 | if (node && node->type == N_CONTAINER) { |
737 | if (container_is_floating(node->sway_container)) { | ||
738 | // If we're about to launch the view into the floating container, then | ||
739 | // launch it as a tiled view instead. | ||
740 | if (ws) { | ||
741 | target_sibling = seat_get_focus_inactive_tiling(seat, ws); | ||
742 | if (target_sibling) { | ||
743 | struct sway_container *con = | ||
744 | seat_get_focus_inactive_view(seat, &target_sibling->node); | ||
745 | if (con) { | ||
746 | target_sibling = con; | ||
747 | } | ||
748 | } | ||
749 | } else { | ||
750 | ws = seat_get_last_known_workspace(seat); | ||
751 | } | ||
752 | } else { | ||
753 | target_sibling = node->sway_container; | ||
754 | } | ||
755 | } | ||
712 | 756 | ||
713 | view->foreign_toplevel = | 757 | view->foreign_toplevel = |
714 | wlr_foreign_toplevel_handle_v1_create(server.foreign_toplevel_manager); | 758 | wlr_foreign_toplevel_handle_v1_create(server.foreign_toplevel_manager); |
@@ -725,13 +769,6 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface, | |||
725 | wl_signal_add(&view->foreign_toplevel->events.destroy, | 769 | wl_signal_add(&view->foreign_toplevel->events.destroy, |
726 | &view->foreign_destroy); | 770 | &view->foreign_destroy); |
727 | 771 | ||
728 | // If we're about to launch the view into the floating container, then | ||
729 | // launch it as a tiled view in the root of the workspace instead. | ||
730 | if (target_sibling && container_is_floating(target_sibling)) { | ||
731 | target_sibling = NULL; | ||
732 | ws = seat_get_last_known_workspace(seat); | ||
733 | } | ||
734 | |||
735 | struct sway_container *container = view->container; | 772 | struct sway_container *container = view->container; |
736 | if (target_sibling) { | 773 | if (target_sibling) { |
737 | container_add_sibling(target_sibling, container, 1); | 774 | container_add_sibling(target_sibling, container, 1); |
@@ -742,7 +779,7 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface, | |||
742 | 779 | ||
743 | view_init_subsurfaces(view, wlr_surface); | 780 | view_init_subsurfaces(view, wlr_surface); |
744 | wl_signal_add(&wlr_surface->events.new_subsurface, | 781 | wl_signal_add(&wlr_surface->events.new_subsurface, |
745 | &view->surface_new_subsurface); | 782 | &view->surface_new_subsurface); |
746 | view->surface_new_subsurface.notify = view_handle_surface_new_subsurface; | 783 | view->surface_new_subsurface.notify = view_handle_surface_new_subsurface; |
747 | 784 | ||
748 | if (decoration) { | 785 | if (decoration) { |
@@ -750,20 +787,20 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface, | |||
750 | } | 787 | } |
751 | 788 | ||
752 | if (view->impl->wants_floating && view->impl->wants_floating(view)) { | 789 | if (view->impl->wants_floating && view->impl->wants_floating(view)) { |
753 | view->container->border = config->floating_border; | 790 | view->container->pending.border = config->floating_border; |
754 | view->container->border_thickness = config->floating_border_thickness; | 791 | view->container->pending.border_thickness = config->floating_border_thickness; |
755 | container_set_floating(view->container, true); | 792 | container_set_floating(view->container, true); |
756 | } else { | 793 | } else { |
757 | view->container->border = config->border; | 794 | view->container->pending.border = config->border; |
758 | view->container->border_thickness = config->border_thickness; | 795 | view->container->pending.border_thickness = config->border_thickness; |
759 | view_set_tiled(view, true); | 796 | view_set_tiled(view, true); |
760 | } | 797 | } |
761 | 798 | ||
762 | if (config->popup_during_fullscreen == POPUP_LEAVE && | 799 | if (config->popup_during_fullscreen == POPUP_LEAVE && |
763 | container->workspace && | 800 | container->pending.workspace && |
764 | container->workspace->fullscreen && | 801 | container->pending.workspace->fullscreen && |
765 | container->workspace->fullscreen->view) { | 802 | container->pending.workspace->fullscreen->view) { |
766 | struct sway_container *fs = container->workspace->fullscreen; | 803 | struct sway_container *fs = container->pending.workspace->fullscreen; |
767 | if (view_is_transient_for(view, fs->view)) { | 804 | if (view_is_transient_for(view, fs->view)) { |
768 | container_set_fullscreen(fs, false); | 805 | container_set_fullscreen(fs, false); |
769 | } | 806 | } |
@@ -774,12 +811,12 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface, | |||
774 | 811 | ||
775 | if (fullscreen) { | 812 | if (fullscreen) { |
776 | container_set_fullscreen(view->container, true); | 813 | container_set_fullscreen(view->container, true); |
777 | arrange_workspace(view->container->workspace); | 814 | arrange_workspace(view->container->pending.workspace); |
778 | } else { | 815 | } else { |
779 | if (container->parent) { | 816 | if (container->pending.parent) { |
780 | arrange_container(container->parent); | 817 | arrange_container(container->pending.parent); |
781 | } else if (container->workspace) { | 818 | } else if (container->pending.workspace) { |
782 | arrange_workspace(container->workspace); | 819 | arrange_workspace(container->pending.workspace); |
783 | } | 820 | } |
784 | } | 821 | } |
785 | 822 | ||
@@ -790,9 +827,9 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface, | |||
790 | #if HAVE_XWAYLAND | 827 | #if HAVE_XWAYLAND |
791 | if (wlr_surface_is_xwayland_surface(wlr_surface)) { | 828 | if (wlr_surface_is_xwayland_surface(wlr_surface)) { |
792 | struct wlr_xwayland_surface *xsurface = | 829 | struct wlr_xwayland_surface *xsurface = |
793 | wlr_xwayland_surface_from_wlr_surface(wlr_surface); | 830 | wlr_xwayland_surface_from_wlr_surface(wlr_surface); |
794 | set_focus = (wlr_xwayland_icccm_input_model(xsurface) != | 831 | set_focus &= wlr_xwayland_icccm_input_model(xsurface) != |
795 | WLR_ICCCM_INPUT_MODEL_NONE) && set_focus; | 832 | WLR_ICCCM_INPUT_MODEL_NONE; |
796 | } | 833 | } |
797 | #endif | 834 | #endif |
798 | 835 | ||
@@ -803,11 +840,9 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface, | |||
803 | const char *app_id; | 840 | const char *app_id; |
804 | const char *class; | 841 | const char *class; |
805 | if ((app_id = view_get_app_id(view)) != NULL) { | 842 | if ((app_id = view_get_app_id(view)) != NULL) { |
806 | wlr_foreign_toplevel_handle_v1_set_app_id( | 843 | wlr_foreign_toplevel_handle_v1_set_app_id(view->foreign_toplevel, app_id); |
807 | view->foreign_toplevel, app_id); | ||
808 | } else if ((class = view_get_class(view)) != NULL) { | 844 | } else if ((class = view_get_class(view)) != NULL) { |
809 | wlr_foreign_toplevel_handle_v1_set_app_id( | 845 | wlr_foreign_toplevel_handle_v1_set_app_id(view->foreign_toplevel, class); |
810 | view->foreign_toplevel, class); | ||
811 | } | 846 | } |
812 | } | 847 | } |
813 | 848 | ||
@@ -826,8 +861,8 @@ void view_unmap(struct sway_view *view) { | |||
826 | view->foreign_toplevel = NULL; | 861 | view->foreign_toplevel = NULL; |
827 | } | 862 | } |
828 | 863 | ||
829 | struct sway_container *parent = view->container->parent; | 864 | struct sway_container *parent = view->container->pending.parent; |
830 | struct sway_workspace *ws = view->container->workspace; | 865 | struct sway_workspace *ws = view->container->pending.workspace; |
831 | container_begin_destroy(view->container); | 866 | container_begin_destroy(view->container); |
832 | if (parent) { | 867 | if (parent) { |
833 | container_reap_empty(parent); | 868 | container_reap_empty(parent); |
@@ -860,47 +895,38 @@ void view_unmap(struct sway_view *view) { | |||
860 | view->surface = NULL; | 895 | view->surface = NULL; |
861 | } | 896 | } |
862 | 897 | ||
863 | void view_update_size(struct sway_view *view, int width, int height) { | 898 | void view_update_size(struct sway_view *view) { |
864 | struct sway_container *con = view->container; | 899 | struct sway_container *con = view->container; |
900 | con->pending.content_width = view->geometry.width; | ||
901 | con->pending.content_height = view->geometry.height; | ||
902 | container_set_geometry_from_content(con); | ||
903 | } | ||
865 | 904 | ||
866 | if (container_is_floating(con)) { | 905 | void view_center_surface(struct sway_view *view) { |
867 | con->content_width = width; | 906 | struct sway_container *con = view->container; |
868 | con->content_height = height; | 907 | // We always center the current coordinates rather than the next, as the |
869 | container_set_geometry_from_content(con); | 908 | // geometry immediately affects the currently active rendering. |
870 | } else { | 909 | con->surface_x = fmax(con->current.content_x, con->current.content_x + |
871 | con->surface_x = con->content_x + (con->content_width - width) / 2; | 910 | (con->current.content_width - view->geometry.width) / 2); |
872 | con->surface_y = con->content_y + (con->content_height - height) / 2; | 911 | con->surface_y = fmax(con->current.content_y, con->current.content_y + |
873 | con->surface_x = fmax(con->surface_x, con->content_x); | 912 | (con->current.content_height - view->geometry.height) / 2); |
874 | con->surface_y = fmax(con->surface_y, con->content_y); | ||
875 | } | ||
876 | } | 913 | } |
877 | 914 | ||
878 | static const struct sway_view_child_impl subsurface_impl; | 915 | static const struct sway_view_child_impl subsurface_impl; |
879 | 916 | ||
880 | static void subsurface_get_root_coords(struct sway_view_child *child, | 917 | static void subsurface_get_view_coords(struct sway_view_child *child, |
881 | int *root_sx, int *root_sy) { | 918 | int *sx, int *sy) { |
882 | struct wlr_surface *surface = child->surface; | 919 | struct wlr_surface *surface = child->surface; |
883 | *root_sx = -child->view->geometry.x; | ||
884 | *root_sy = -child->view->geometry.y; | ||
885 | |||
886 | if (child->parent && child->parent->impl && | 920 | if (child->parent && child->parent->impl && |
887 | child->parent->impl->get_root_coords) { | 921 | child->parent->impl->get_view_coords) { |
888 | int sx, sy; | 922 | child->parent->impl->get_view_coords(child->parent, sx, sy); |
889 | child->parent->impl->get_root_coords(child->parent, &sx, &sy); | ||
890 | *root_sx += sx; | ||
891 | *root_sy += sy; | ||
892 | } else { | 923 | } else { |
893 | while (surface && wlr_surface_is_subsurface(surface)) { | 924 | *sx = *sy = 0; |
894 | struct wlr_subsurface *subsurface = | ||
895 | wlr_subsurface_from_wlr_surface(surface); | ||
896 | if (subsurface == NULL) { | ||
897 | break; | ||
898 | } | ||
899 | *root_sx += subsurface->current.x; | ||
900 | *root_sy += subsurface->current.y; | ||
901 | surface = subsurface->parent; | ||
902 | } | ||
903 | } | 925 | } |
926 | struct wlr_subsurface *subsurface = | ||
927 | wlr_subsurface_from_wlr_surface(surface); | ||
928 | *sx += subsurface->current.x; | ||
929 | *sy += subsurface->current.y; | ||
904 | } | 930 | } |
905 | 931 | ||
906 | static void subsurface_destroy(struct sway_view_child *child) { | 932 | static void subsurface_destroy(struct sway_view_child *child) { |
@@ -914,7 +940,7 @@ static void subsurface_destroy(struct sway_view_child *child) { | |||
914 | } | 940 | } |
915 | 941 | ||
916 | static const struct sway_view_child_impl subsurface_impl = { | 942 | static const struct sway_view_child_impl subsurface_impl = { |
917 | .get_root_coords = subsurface_get_root_coords, | 943 | .get_view_coords = subsurface_get_view_coords, |
918 | .destroy = subsurface_destroy, | 944 | .destroy = subsurface_destroy, |
919 | }; | 945 | }; |
920 | 946 | ||
@@ -968,15 +994,27 @@ static void view_child_subsurface_create(struct sway_view_child *child, | |||
968 | view_child_damage(&subsurface->child, true); | 994 | view_child_damage(&subsurface->child, true); |
969 | } | 995 | } |
970 | 996 | ||
997 | static bool view_child_is_mapped(struct sway_view_child *child) { | ||
998 | while (child) { | ||
999 | if (!child->mapped) { | ||
1000 | return false; | ||
1001 | } | ||
1002 | child = child->parent; | ||
1003 | } | ||
1004 | return true; | ||
1005 | } | ||
1006 | |||
971 | static void view_child_damage(struct sway_view_child *child, bool whole) { | 1007 | static void view_child_damage(struct sway_view_child *child, bool whole) { |
972 | if (!child || !child->mapped || !child->view || !child->view->container) { | 1008 | if (!child || !view_child_is_mapped(child) || !child->view || !child->view->container) { |
973 | return; | 1009 | return; |
974 | } | 1010 | } |
975 | int sx, sy; | 1011 | int sx, sy; |
976 | child->impl->get_root_coords(child, &sx, &sy); | 1012 | child->impl->get_view_coords(child, &sx, &sy); |
977 | desktop_damage_surface(child->surface, | 1013 | desktop_damage_surface(child->surface, |
978 | child->view->container->content_x + sx, | 1014 | child->view->container->pending.content_x - |
979 | child->view->container->content_y + sy, whole); | 1015 | child->view->geometry.x + sx, |
1016 | child->view->container->pending.content_y - | ||
1017 | child->view->geometry.y + sy, whole); | ||
980 | } | 1018 | } |
981 | 1019 | ||
982 | static void view_child_handle_surface_commit(struct wl_listener *listener, | 1020 | static void view_child_handle_surface_commit(struct wl_listener *listener, |
@@ -1004,11 +1042,29 @@ static void view_child_handle_surface_destroy(struct wl_listener *listener, | |||
1004 | static void view_init_subsurfaces(struct sway_view *view, | 1042 | static void view_init_subsurfaces(struct sway_view *view, |
1005 | struct wlr_surface *surface) { | 1043 | struct wlr_surface *surface) { |
1006 | struct wlr_subsurface *subsurface; | 1044 | struct wlr_subsurface *subsurface; |
1007 | wl_list_for_each(subsurface, &surface->subsurfaces, parent_link) { | 1045 | wl_list_for_each(subsurface, &surface->current.subsurfaces_below, |
1046 | current.link) { | ||
1047 | view_subsurface_create(view, subsurface); | ||
1048 | } | ||
1049 | wl_list_for_each(subsurface, &surface->current.subsurfaces_above, | ||
1050 | current.link) { | ||
1008 | view_subsurface_create(view, subsurface); | 1051 | view_subsurface_create(view, subsurface); |
1009 | } | 1052 | } |
1010 | } | 1053 | } |
1011 | 1054 | ||
1055 | static void view_child_init_subsurfaces(struct sway_view_child *view_child, | ||
1056 | struct wlr_surface *surface) { | ||
1057 | struct wlr_subsurface *subsurface; | ||
1058 | wl_list_for_each(subsurface, &surface->current.subsurfaces_below, | ||
1059 | current.link) { | ||
1060 | view_child_subsurface_create(view_child, subsurface); | ||
1061 | } | ||
1062 | wl_list_for_each(subsurface, &surface->current.subsurfaces_above, | ||
1063 | current.link) { | ||
1064 | view_child_subsurface_create(view_child, subsurface); | ||
1065 | } | ||
1066 | } | ||
1067 | |||
1012 | static void view_child_handle_surface_map(struct wl_listener *listener, | 1068 | static void view_child_handle_surface_map(struct wl_listener *listener, |
1013 | void *data) { | 1069 | void *data) { |
1014 | struct sway_view_child *child = | 1070 | struct sway_view_child *child = |
@@ -1059,16 +1115,19 @@ void view_child_init(struct sway_view_child *child, | |||
1059 | wl_signal_add(&view->events.unmap, &child->view_unmap); | 1115 | wl_signal_add(&view->events.unmap, &child->view_unmap); |
1060 | child->view_unmap.notify = view_child_handle_view_unmap; | 1116 | child->view_unmap.notify = view_child_handle_view_unmap; |
1061 | 1117 | ||
1062 | struct sway_workspace *workspace = child->view->container->workspace; | 1118 | struct sway_container *container = child->view->container; |
1063 | if (workspace) { | 1119 | if (container != NULL) { |
1064 | wlr_surface_send_enter(child->surface, workspace->output->wlr_output); | 1120 | struct sway_workspace *workspace = container->pending.workspace; |
1121 | if (workspace) { | ||
1122 | wlr_surface_send_enter(child->surface, workspace->output->wlr_output); | ||
1123 | } | ||
1065 | } | 1124 | } |
1066 | 1125 | ||
1067 | view_init_subsurfaces(child->view, surface); | 1126 | view_child_init_subsurfaces(child, surface); |
1068 | } | 1127 | } |
1069 | 1128 | ||
1070 | void view_child_destroy(struct sway_view_child *child) { | 1129 | void view_child_destroy(struct sway_view_child *child) { |
1071 | if (child->mapped && child->view->container != NULL) { | 1130 | if (view_child_is_mapped(child) && child->view->container != NULL) { |
1072 | view_child_damage(child, true); | 1131 | view_child_damage(child, true); |
1073 | } | 1132 | } |
1074 | 1133 | ||
@@ -1081,6 +1140,9 @@ void view_child_destroy(struct sway_view_child *child) { | |||
1081 | wl_list_for_each_safe(subchild, tmpchild, &child->children, link) { | 1140 | wl_list_for_each_safe(subchild, tmpchild, &child->children, link) { |
1082 | wl_list_remove(&subchild->link); | 1141 | wl_list_remove(&subchild->link); |
1083 | subchild->parent = NULL; | 1142 | subchild->parent = NULL; |
1143 | // The subchild lost its parent link, so it cannot see that the parent | ||
1144 | // is unmapped. Unmap it directly. | ||
1145 | subchild->mapped = false; | ||
1084 | } | 1146 | } |
1085 | 1147 | ||
1086 | wl_list_remove(&child->surface_commit.link); | 1148 | wl_list_remove(&child->surface_commit.link); |
@@ -1101,18 +1163,27 @@ struct sway_view *view_from_wlr_surface(struct wlr_surface *wlr_surface) { | |||
1101 | if (wlr_surface_is_xdg_surface(wlr_surface)) { | 1163 | if (wlr_surface_is_xdg_surface(wlr_surface)) { |
1102 | struct wlr_xdg_surface *xdg_surface = | 1164 | struct wlr_xdg_surface *xdg_surface = |
1103 | wlr_xdg_surface_from_wlr_surface(wlr_surface); | 1165 | wlr_xdg_surface_from_wlr_surface(wlr_surface); |
1166 | if (xdg_surface == NULL) { | ||
1167 | return NULL; | ||
1168 | } | ||
1104 | return view_from_wlr_xdg_surface(xdg_surface); | 1169 | return view_from_wlr_xdg_surface(xdg_surface); |
1105 | } | 1170 | } |
1106 | #if HAVE_XWAYLAND | 1171 | #if HAVE_XWAYLAND |
1107 | if (wlr_surface_is_xwayland_surface(wlr_surface)) { | 1172 | if (wlr_surface_is_xwayland_surface(wlr_surface)) { |
1108 | struct wlr_xwayland_surface *xsurface = | 1173 | struct wlr_xwayland_surface *xsurface = |
1109 | wlr_xwayland_surface_from_wlr_surface(wlr_surface); | 1174 | wlr_xwayland_surface_from_wlr_surface(wlr_surface); |
1175 | if (xsurface == NULL) { | ||
1176 | return NULL; | ||
1177 | } | ||
1110 | return view_from_wlr_xwayland_surface(xsurface); | 1178 | return view_from_wlr_xwayland_surface(xsurface); |
1111 | } | 1179 | } |
1112 | #endif | 1180 | #endif |
1113 | if (wlr_surface_is_subsurface(wlr_surface)) { | 1181 | if (wlr_surface_is_subsurface(wlr_surface)) { |
1114 | struct wlr_subsurface *subsurface = | 1182 | struct wlr_subsurface *subsurface = |
1115 | wlr_subsurface_from_wlr_surface(wlr_surface); | 1183 | wlr_subsurface_from_wlr_surface(wlr_surface); |
1184 | if (subsurface == NULL) { | ||
1185 | return NULL; | ||
1186 | } | ||
1116 | return view_from_wlr_surface(subsurface->parent); | 1187 | return view_from_wlr_surface(subsurface->parent); |
1117 | } | 1188 | } |
1118 | if (wlr_surface_is_layer_surface(wlr_surface)) { | 1189 | if (wlr_surface_is_layer_surface(wlr_surface)) { |
@@ -1225,8 +1296,6 @@ void view_update_title(struct sway_view *view, bool force) { | |||
1225 | view->container->title = NULL; | 1296 | view->container->title = NULL; |
1226 | view->container->formatted_title = NULL; | 1297 | view->container->formatted_title = NULL; |
1227 | } | 1298 | } |
1228 | container_calculate_title_height(view->container); | ||
1229 | config_update_font_height(false); | ||
1230 | 1299 | ||
1231 | // Update title after the global font height is updated | 1300 | // Update title after the global font height is updated |
1232 | container_update_title_textures(view->container); | 1301 | container_update_title_textures(view->container); |
@@ -1242,15 +1311,15 @@ bool view_is_visible(struct sway_view *view) { | |||
1242 | if (view->container->node.destroying) { | 1311 | if (view->container->node.destroying) { |
1243 | return false; | 1312 | return false; |
1244 | } | 1313 | } |
1245 | struct sway_workspace *workspace = view->container->workspace; | 1314 | struct sway_workspace *workspace = view->container->pending.workspace; |
1246 | if (!workspace && view->container->fullscreen_mode != FULLSCREEN_GLOBAL) { | 1315 | if (!workspace && view->container->pending.fullscreen_mode != FULLSCREEN_GLOBAL) { |
1247 | bool fs_global_descendant = false; | 1316 | bool fs_global_descendant = false; |
1248 | struct sway_container *parent = view->container->parent; | 1317 | struct sway_container *parent = view->container->pending.parent; |
1249 | while (parent) { | 1318 | while (parent) { |
1250 | if (parent->fullscreen_mode == FULLSCREEN_GLOBAL) { | 1319 | if (parent->pending.fullscreen_mode == FULLSCREEN_GLOBAL) { |
1251 | fs_global_descendant = true; | 1320 | fs_global_descendant = true; |
1252 | } | 1321 | } |
1253 | parent = parent->parent; | 1322 | parent = parent->pending.parent; |
1254 | } | 1323 | } |
1255 | if (!fs_global_descendant) { | 1324 | if (!fs_global_descendant) { |
1256 | return false; | 1325 | return false; |
@@ -1268,13 +1337,13 @@ bool view_is_visible(struct sway_view *view) { | |||
1268 | enum sway_container_layout layout = container_parent_layout(con); | 1337 | enum sway_container_layout layout = container_parent_layout(con); |
1269 | if ((layout == L_TABBED || layout == L_STACKED) | 1338 | if ((layout == L_TABBED || layout == L_STACKED) |
1270 | && !container_is_floating(con)) { | 1339 | && !container_is_floating(con)) { |
1271 | struct sway_node *parent = con->parent ? | 1340 | struct sway_node *parent = con->pending.parent ? |
1272 | &con->parent->node : &con->workspace->node; | 1341 | &con->pending.parent->node : &con->pending.workspace->node; |
1273 | if (seat_get_active_tiling_child(seat, parent) != &con->node) { | 1342 | if (seat_get_active_tiling_child(seat, parent) != &con->node) { |
1274 | return false; | 1343 | return false; |
1275 | } | 1344 | } |
1276 | } | 1345 | } |
1277 | con = con->parent; | 1346 | con = con->pending.parent; |
1278 | } | 1347 | } |
1279 | // Check view isn't hidden by another fullscreen view | 1348 | // Check view isn't hidden by another fullscreen view |
1280 | struct sway_container *fs = root->fullscreen_global ? | 1349 | struct sway_container *fs = root->fullscreen_global ? |
@@ -1308,7 +1377,7 @@ void view_set_urgent(struct sway_view *view, bool enable) { | |||
1308 | ipc_event_window(view->container, "urgent"); | 1377 | ipc_event_window(view->container, "urgent"); |
1309 | 1378 | ||
1310 | if (!container_is_scratchpad_hidden(view->container)) { | 1379 | if (!container_is_scratchpad_hidden(view->container)) { |
1311 | workspace_detect_urgent(view->container->workspace); | 1380 | workspace_detect_urgent(view->container->pending.workspace); |
1312 | } | 1381 | } |
1313 | } | 1382 | } |
1314 | 1383 | ||
@@ -1338,11 +1407,11 @@ static void view_save_buffer_iterator(struct wlr_surface *surface, | |||
1338 | saved_buffer->buffer = surface->buffer; | 1407 | saved_buffer->buffer = surface->buffer; |
1339 | saved_buffer->width = surface->current.width; | 1408 | saved_buffer->width = surface->current.width; |
1340 | saved_buffer->height = surface->current.height; | 1409 | saved_buffer->height = surface->current.height; |
1341 | saved_buffer->x = sx; | 1410 | saved_buffer->x = view->container->surface_x + sx; |
1342 | saved_buffer->y = sy; | 1411 | saved_buffer->y = view->container->surface_y + sy; |
1343 | saved_buffer->transform = surface->current.transform; | 1412 | saved_buffer->transform = surface->current.transform; |
1344 | wlr_surface_get_buffer_source_box(surface, &saved_buffer->source_box); | 1413 | wlr_surface_get_buffer_source_box(surface, &saved_buffer->source_box); |
1345 | wl_list_insert(&view->saved_buffers, &saved_buffer->link); | 1414 | wl_list_insert(view->saved_buffers.prev, &saved_buffer->link); |
1346 | } | 1415 | } |
1347 | } | 1416 | } |
1348 | 1417 | ||
diff --git a/sway/tree/workspace.c b/sway/tree/workspace.c index 921b7d19..c84320bd 100644 --- a/sway/tree/workspace.c +++ b/sway/tree/workspace.c | |||
@@ -48,10 +48,10 @@ struct sway_output *workspace_get_initial_output(const char *name) { | |||
48 | if (focus && focus->type == N_WORKSPACE) { | 48 | if (focus && focus->type == N_WORKSPACE) { |
49 | return focus->sway_workspace->output; | 49 | return focus->sway_workspace->output; |
50 | } else if (focus && focus->type == N_CONTAINER) { | 50 | } else if (focus && focus->type == N_CONTAINER) { |
51 | return focus->sway_container->workspace->output; | 51 | return focus->sway_container->pending.workspace->output; |
52 | } | 52 | } |
53 | // Fallback to the first output or noop output for headless | 53 | // Fallback to the first output or the headless output |
54 | return root->outputs->length ? root->outputs->items[0] : root->noop_output; | 54 | return root->outputs->length ? root->outputs->items[0] : root->fallback_output; |
55 | } | 55 | } |
56 | 56 | ||
57 | struct sway_workspace *workspace_create(struct sway_output *output, | 57 | struct sway_workspace *workspace_create(struct sway_output *output, |
@@ -222,10 +222,8 @@ static void workspace_name_from_binding(const struct sway_binding * binding, | |||
222 | // not a command about workspaces | 222 | // not a command about workspaces |
223 | if (strcmp(_target, "next") == 0 || | 223 | if (strcmp(_target, "next") == 0 || |
224 | strcmp(_target, "prev") == 0 || | 224 | strcmp(_target, "prev") == 0 || |
225 | strncmp(_target, "next_on_output", | 225 | strcmp(_target, "next_on_output") == 0 || |
226 | strlen("next_on_output")) == 0 || | 226 | strcmp(_target, "prev_on_output") == 0 || |
227 | strncmp(_target, "prev_on_output", | ||
228 | strlen("next_on_output")) == 0 || | ||
229 | strcmp(_target, "number") == 0 || | 227 | strcmp(_target, "number") == 0 || |
230 | strcmp(_target, "back_and_forth") == 0 || | 228 | strcmp(_target, "back_and_forth") == 0 || |
231 | strcmp(_target, "current") == 0) { | 229 | strcmp(_target, "current") == 0) { |
@@ -363,11 +361,11 @@ struct sway_workspace *workspace_by_name(const char *name) { | |||
363 | if (current && strcmp(name, "prev") == 0) { | 361 | if (current && strcmp(name, "prev") == 0) { |
364 | return workspace_prev(current); | 362 | return workspace_prev(current); |
365 | } else if (current && strcmp(name, "prev_on_output") == 0) { | 363 | } else if (current && strcmp(name, "prev_on_output") == 0) { |
366 | return workspace_output_prev(current, false); | 364 | return workspace_output_prev(current); |
367 | } else if (current && strcmp(name, "next") == 0) { | 365 | } else if (current && strcmp(name, "next") == 0) { |
368 | return workspace_next(current); | 366 | return workspace_next(current); |
369 | } else if (current && strcmp(name, "next_on_output") == 0) { | 367 | } else if (current && strcmp(name, "next_on_output") == 0) { |
370 | return workspace_output_next(current, false); | 368 | return workspace_output_next(current); |
371 | } else if (strcmp(name, "current") == 0) { | 369 | } else if (strcmp(name, "current") == 0) { |
372 | return current; | 370 | return current; |
373 | } else if (strcasecmp(name, "back_and_forth") == 0) { | 371 | } else if (strcasecmp(name, "back_and_forth") == 0) { |
@@ -530,7 +528,7 @@ struct sway_workspace *workspace_next(struct sway_workspace *workspace) { | |||
530 | * otherwise the next one is returned. | 528 | * otherwise the next one is returned. |
531 | */ | 529 | */ |
532 | static struct sway_workspace *workspace_output_prev_next_impl( | 530 | static struct sway_workspace *workspace_output_prev_next_impl( |
533 | struct sway_output *output, int dir, bool create) { | 531 | struct sway_output *output, int dir) { |
534 | struct sway_seat *seat = input_manager_current_seat(); | 532 | struct sway_seat *seat = input_manager_current_seat(); |
535 | struct sway_workspace *workspace = seat_get_focused_workspace(seat); | 533 | struct sway_workspace *workspace = seat_get_focused_workspace(seat); |
536 | if (!workspace) { | 534 | if (!workspace) { |
@@ -540,46 +538,43 @@ static struct sway_workspace *workspace_output_prev_next_impl( | |||
540 | } | 538 | } |
541 | 539 | ||
542 | int index = list_find(output->workspaces, workspace); | 540 | int index = list_find(output->workspaces, workspace); |
543 | if (!workspace_is_empty(workspace) && create && | ||
544 | (index + dir < 0 || index + dir == output->workspaces->length)) { | ||
545 | struct sway_output *output = workspace->output; | ||
546 | char *next = workspace_next_name(output->wlr_output->name); | ||
547 | workspace_create(output, next); | ||
548 | free(next); | ||
549 | } | ||
550 | size_t new_index = wrap(index + dir, output->workspaces->length); | 541 | size_t new_index = wrap(index + dir, output->workspaces->length); |
551 | return output->workspaces->items[new_index]; | 542 | return output->workspaces->items[new_index]; |
552 | } | 543 | } |
553 | 544 | ||
554 | struct sway_workspace *workspace_output_next( | 545 | |
555 | struct sway_workspace *current, bool create) { | 546 | struct sway_workspace *workspace_output_next(struct sway_workspace *current) { |
556 | return workspace_output_prev_next_impl(current->output, 1, create); | 547 | return workspace_output_prev_next_impl(current->output, 1); |
557 | } | 548 | } |
558 | 549 | ||
559 | struct sway_workspace *workspace_output_prev( | 550 | struct sway_workspace *workspace_output_prev(struct sway_workspace *current) { |
560 | struct sway_workspace *current, bool create) { | 551 | return workspace_output_prev_next_impl(current->output, -1); |
561 | return workspace_output_prev_next_impl(current->output, -1, create); | ||
562 | } | 552 | } |
563 | 553 | ||
564 | bool workspace_switch(struct sway_workspace *workspace, | 554 | struct sway_workspace *workspace_auto_back_and_forth( |
565 | bool no_auto_back_and_forth) { | 555 | struct sway_workspace *workspace) { |
566 | struct sway_seat *seat = input_manager_current_seat(); | 556 | struct sway_seat *seat = input_manager_current_seat(); |
567 | struct sway_workspace *active_ws = NULL; | 557 | struct sway_workspace *active_ws = NULL; |
568 | struct sway_node *focus = seat_get_focus_inactive(seat, &root->node); | 558 | struct sway_node *focus = seat_get_focus_inactive(seat, &root->node); |
569 | if (focus && focus->type == N_WORKSPACE) { | 559 | if (focus && focus->type == N_WORKSPACE) { |
570 | active_ws = focus->sway_workspace; | 560 | active_ws = focus->sway_workspace; |
571 | } else if (focus && focus->type == N_CONTAINER) { | 561 | } else if (focus && focus->type == N_CONTAINER) { |
572 | active_ws = focus->sway_container->workspace; | 562 | active_ws = focus->sway_container->pending.workspace; |
573 | } | 563 | } |
574 | 564 | ||
575 | if (!no_auto_back_and_forth && config->auto_back_and_forth && active_ws | 565 | if (config->auto_back_and_forth && active_ws && active_ws == workspace && |
576 | && active_ws == workspace && seat->prev_workspace_name) { | 566 | seat->prev_workspace_name) { |
577 | struct sway_workspace *new_ws = | 567 | struct sway_workspace *new_ws = |
578 | workspace_by_name(seat->prev_workspace_name); | 568 | workspace_by_name(seat->prev_workspace_name); |
579 | workspace = new_ws ? | 569 | workspace = new_ws ? |
580 | new_ws : | 570 | new_ws : |
581 | workspace_create(NULL, seat->prev_workspace_name); | 571 | workspace_create(NULL, seat->prev_workspace_name); |
582 | } | 572 | } |
573 | return workspace; | ||
574 | } | ||
575 | |||
576 | bool workspace_switch(struct sway_workspace *workspace) { | ||
577 | struct sway_seat *seat = input_manager_current_seat(); | ||
583 | 578 | ||
584 | sway_log(SWAY_DEBUG, "Switching to workspace %p:%s", | 579 | sway_log(SWAY_DEBUG, "Switching to workspace %p:%s", |
585 | workspace, workspace->name); | 580 | workspace, workspace->name); |
@@ -736,13 +731,13 @@ struct sway_container *workspace_find_container(struct sway_workspace *ws, | |||
736 | } | 731 | } |
737 | 732 | ||
738 | static void set_workspace(struct sway_container *container, void *data) { | 733 | static void set_workspace(struct sway_container *container, void *data) { |
739 | container->workspace = container->parent->workspace; | 734 | container->pending.workspace = container->pending.parent->pending.workspace; |
740 | } | 735 | } |
741 | 736 | ||
742 | static void workspace_attach_tiling(struct sway_workspace *ws, | 737 | static void workspace_attach_tiling(struct sway_workspace *ws, |
743 | struct sway_container *con) { | 738 | struct sway_container *con) { |
744 | list_add(ws->tiling, con); | 739 | list_add(ws->tiling, con); |
745 | con->workspace = ws; | 740 | con->pending.workspace = ws; |
746 | container_for_each_child(con, set_workspace, NULL); | 741 | container_for_each_child(con, set_workspace, NULL); |
747 | container_handle_fullscreen_reparent(con); | 742 | container_handle_fullscreen_reparent(con); |
748 | workspace_update_representation(ws); | 743 | workspace_update_representation(ws); |
@@ -753,7 +748,7 @@ static void workspace_attach_tiling(struct sway_workspace *ws, | |||
753 | struct sway_container *workspace_wrap_children(struct sway_workspace *ws) { | 748 | struct sway_container *workspace_wrap_children(struct sway_workspace *ws) { |
754 | struct sway_container *fs = ws->fullscreen; | 749 | struct sway_container *fs = ws->fullscreen; |
755 | struct sway_container *middle = container_create(NULL); | 750 | struct sway_container *middle = container_create(NULL); |
756 | middle->layout = ws->layout; | 751 | middle->pending.layout = ws->layout; |
757 | while (ws->tiling->length) { | 752 | while (ws->tiling->length) { |
758 | struct sway_container *child = ws->tiling->items[0]; | 753 | struct sway_container *child = ws->tiling->items[0]; |
759 | container_detach(child); | 754 | container_detach(child); |
@@ -771,9 +766,9 @@ void workspace_unwrap_children(struct sway_workspace *ws, | |||
771 | return; | 766 | return; |
772 | } | 767 | } |
773 | 768 | ||
774 | ws->layout = wrap->layout; | 769 | ws->layout = wrap->pending.layout; |
775 | while (wrap->children->length) { | 770 | while (wrap->pending.children->length) { |
776 | struct sway_container *child = wrap->children->items[0]; | 771 | struct sway_container *child = wrap->pending.children->items[0]; |
777 | container_detach(child); | 772 | container_detach(child); |
778 | workspace_add_tiling(ws, child); | 773 | workspace_add_tiling(ws, child); |
779 | } | 774 | } |
@@ -793,14 +788,18 @@ void workspace_detach(struct sway_workspace *workspace) { | |||
793 | 788 | ||
794 | struct sway_container *workspace_add_tiling(struct sway_workspace *workspace, | 789 | struct sway_container *workspace_add_tiling(struct sway_workspace *workspace, |
795 | struct sway_container *con) { | 790 | struct sway_container *con) { |
796 | if (con->workspace) { | 791 | if (con->pending.workspace) { |
792 | struct sway_container *old_parent = con->pending.parent; | ||
797 | container_detach(con); | 793 | container_detach(con); |
794 | if (old_parent) { | ||
795 | container_reap_empty(old_parent); | ||
796 | } | ||
798 | } | 797 | } |
799 | if (config->default_layout != L_NONE) { | 798 | if (config->default_layout != L_NONE) { |
800 | con = container_split(con, config->default_layout); | 799 | con = container_split(con, config->default_layout); |
801 | } | 800 | } |
802 | list_add(workspace->tiling, con); | 801 | list_add(workspace->tiling, con); |
803 | con->workspace = workspace; | 802 | con->pending.workspace = workspace; |
804 | container_for_each_child(con, set_workspace, NULL); | 803 | container_for_each_child(con, set_workspace, NULL); |
805 | container_handle_fullscreen_reparent(con); | 804 | container_handle_fullscreen_reparent(con); |
806 | workspace_update_representation(workspace); | 805 | workspace_update_representation(workspace); |
@@ -811,11 +810,11 @@ struct sway_container *workspace_add_tiling(struct sway_workspace *workspace, | |||
811 | 810 | ||
812 | void workspace_add_floating(struct sway_workspace *workspace, | 811 | void workspace_add_floating(struct sway_workspace *workspace, |
813 | struct sway_container *con) { | 812 | struct sway_container *con) { |
814 | if (con->workspace) { | 813 | if (con->pending.workspace) { |
815 | container_detach(con); | 814 | container_detach(con); |
816 | } | 815 | } |
817 | list_add(workspace->floating, con); | 816 | list_add(workspace->floating, con); |
818 | con->workspace = workspace; | 817 | con->pending.workspace = workspace; |
819 | container_for_each_child(con, set_workspace, NULL); | 818 | container_for_each_child(con, set_workspace, NULL); |
820 | container_handle_fullscreen_reparent(con); | 819 | container_handle_fullscreen_reparent(con); |
821 | node_set_dirty(&workspace->node); | 820 | node_set_dirty(&workspace->node); |
@@ -825,7 +824,7 @@ void workspace_add_floating(struct sway_workspace *workspace, | |||
825 | void workspace_insert_tiling_direct(struct sway_workspace *workspace, | 824 | void workspace_insert_tiling_direct(struct sway_workspace *workspace, |
826 | struct sway_container *con, int index) { | 825 | struct sway_container *con, int index) { |
827 | list_insert(workspace->tiling, index, con); | 826 | list_insert(workspace->tiling, index, con); |
828 | con->workspace = workspace; | 827 | con->pending.workspace = workspace; |
829 | container_for_each_child(con, set_workspace, NULL); | 828 | container_for_each_child(con, set_workspace, NULL); |
830 | container_handle_fullscreen_reparent(con); | 829 | container_handle_fullscreen_reparent(con); |
831 | workspace_update_representation(workspace); | 830 | workspace_update_representation(workspace); |
@@ -835,7 +834,7 @@ void workspace_insert_tiling_direct(struct sway_workspace *workspace, | |||
835 | 834 | ||
836 | struct sway_container *workspace_insert_tiling(struct sway_workspace *workspace, | 835 | struct sway_container *workspace_insert_tiling(struct sway_workspace *workspace, |
837 | struct sway_container *con, int index) { | 836 | struct sway_container *con, int index) { |
838 | if (con->workspace) { | 837 | if (con->pending.workspace) { |
839 | container_detach(con); | 838 | container_detach(con); |
840 | } | 839 | } |
841 | if (config->default_layout != L_NONE) { | 840 | if (config->default_layout != L_NONE) { |
@@ -845,24 +844,36 @@ struct sway_container *workspace_insert_tiling(struct sway_workspace *workspace, | |||
845 | return con; | 844 | return con; |
846 | } | 845 | } |
847 | 846 | ||
847 | bool workspace_has_single_visible_container(struct sway_workspace *ws) { | ||
848 | struct sway_seat *seat = input_manager_get_default_seat(); | ||
849 | struct sway_container *focus = | ||
850 | seat_get_focus_inactive_tiling(seat, ws); | ||
851 | if (focus && !focus->view) { | ||
852 | focus = seat_get_focus_inactive_view(seat, &focus->node); | ||
853 | } | ||
854 | return (focus && focus->view && view_ancestor_is_only_visible(focus->view)); | ||
855 | } | ||
856 | |||
848 | void workspace_add_gaps(struct sway_workspace *ws) { | 857 | void workspace_add_gaps(struct sway_workspace *ws) { |
849 | if (config->smart_gaps) { | 858 | if (config->smart_gaps == SMART_GAPS_ON |
850 | struct sway_seat *seat = input_manager_get_default_seat(); | 859 | && workspace_has_single_visible_container(ws)) { |
851 | struct sway_container *focus = | 860 | ws->current_gaps.top = 0; |
852 | seat_get_focus_inactive_tiling(seat, ws); | 861 | ws->current_gaps.right = 0; |
853 | if (focus && !focus->view) { | 862 | ws->current_gaps.bottom = 0; |
854 | focus = seat_get_focus_inactive_view(seat, &focus->node); | 863 | ws->current_gaps.left = 0; |
855 | } | 864 | return; |
856 | if (focus && focus->view && view_ancestor_is_only_visible(focus->view)) { | 865 | } |
857 | ws->current_gaps.top = 0; | 866 | |
858 | ws->current_gaps.right = 0; | 867 | if (config->smart_gaps == SMART_GAPS_INVERSE_OUTER |
859 | ws->current_gaps.bottom = 0; | 868 | && !workspace_has_single_visible_container(ws)) { |
860 | ws->current_gaps.left = 0; | 869 | ws->current_gaps.top = 0; |
861 | return; | 870 | ws->current_gaps.right = 0; |
862 | } | 871 | ws->current_gaps.bottom = 0; |
872 | ws->current_gaps.left = 0; | ||
873 | } else { | ||
874 | ws->current_gaps = ws->gaps_outer; | ||
863 | } | 875 | } |
864 | 876 | ||
865 | ws->current_gaps = ws->gaps_outer; | ||
866 | // Add inner gaps and make sure we don't turn out negative | 877 | // Add inner gaps and make sure we don't turn out negative |
867 | ws->current_gaps.top = fmax(0, ws->current_gaps.top + ws->gaps_inner); | 878 | ws->current_gaps.top = fmax(0, ws->current_gaps.top + ws->gaps_inner); |
868 | ws->current_gaps.right = fmax(0, ws->current_gaps.right + ws->gaps_inner); | 879 | ws->current_gaps.right = fmax(0, ws->current_gaps.right + ws->gaps_inner); |
@@ -905,7 +916,7 @@ struct sway_container *workspace_split(struct sway_workspace *workspace, | |||
905 | enum sway_container_layout old_layout = workspace->layout; | 916 | enum sway_container_layout old_layout = workspace->layout; |
906 | struct sway_container *middle = workspace_wrap_children(workspace); | 917 | struct sway_container *middle = workspace_wrap_children(workspace); |
907 | workspace->layout = layout; | 918 | workspace->layout = layout; |
908 | middle->layout = old_layout; | 919 | middle->pending.layout = old_layout; |
909 | 920 | ||
910 | struct sway_seat *seat; | 921 | struct sway_seat *seat; |
911 | wl_list_for_each(seat, &server.input->seats, link) { | 922 | wl_list_for_each(seat, &server.input->seats, link) { |
diff --git a/sway/xdg_activation_v1.c b/sway/xdg_activation_v1.c new file mode 100644 index 00000000..6c70c785 --- /dev/null +++ b/sway/xdg_activation_v1.c | |||
@@ -0,0 +1,20 @@ | |||
1 | #include <wlr/types/wlr_xdg_activation_v1.h> | ||
2 | #include "sway/tree/view.h" | ||
3 | |||
4 | void xdg_activation_v1_handle_request_activate(struct wl_listener *listener, | ||
5 | void *data) { | ||
6 | const struct wlr_xdg_activation_v1_request_activate_event *event = data; | ||
7 | |||
8 | if (!wlr_surface_is_xdg_surface(event->surface)) { | ||
9 | return; | ||
10 | } | ||
11 | |||
12 | struct wlr_xdg_surface *xdg_surface = | ||
13 | wlr_xdg_surface_from_wlr_surface(event->surface); | ||
14 | struct sway_view *view = xdg_surface->data; | ||
15 | if (!xdg_surface->mapped || view == NULL) { | ||
16 | return; | ||
17 | } | ||
18 | |||
19 | view_request_activate(view); | ||
20 | } | ||
diff --git a/sway/xdg_decoration.c b/sway/xdg_decoration.c index e7c3ea73..ec9e8d68 100644 --- a/sway/xdg_decoration.c +++ b/sway/xdg_decoration.c | |||
@@ -10,7 +10,7 @@ static void xdg_decoration_handle_destroy(struct wl_listener *listener, | |||
10 | void *data) { | 10 | void *data) { |
11 | struct sway_xdg_decoration *deco = | 11 | struct sway_xdg_decoration *deco = |
12 | wl_container_of(listener, deco, destroy); | 12 | wl_container_of(listener, deco, destroy); |
13 | if(deco->view) { | 13 | if (deco->view) { |
14 | deco->view->xdg_decoration = NULL; | 14 | deco->view->xdg_decoration = NULL; |
15 | } | 15 | } |
16 | wl_list_remove(&deco->destroy.link); | 16 | wl_list_remove(&deco->destroy.link); |
@@ -23,8 +23,32 @@ static void xdg_decoration_handle_request_mode(struct wl_listener *listener, | |||
23 | void *data) { | 23 | void *data) { |
24 | struct sway_xdg_decoration *deco = | 24 | struct sway_xdg_decoration *deco = |
25 | wl_container_of(listener, deco, request_mode); | 25 | wl_container_of(listener, deco, request_mode); |
26 | struct sway_view *view = deco->view; | ||
27 | enum wlr_xdg_toplevel_decoration_v1_mode mode = | ||
28 | WLR_XDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE; | ||
29 | enum wlr_xdg_toplevel_decoration_v1_mode client_mode = | ||
30 | deco->wlr_xdg_decoration->requested_mode; | ||
31 | |||
32 | bool floating; | ||
33 | if (view->container) { | ||
34 | floating = container_is_floating(view->container); | ||
35 | bool csd = false; | ||
36 | csd = client_mode == | ||
37 | WLR_XDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE; | ||
38 | view_update_csd_from_client(view, csd); | ||
39 | arrange_container(view->container); | ||
40 | transaction_commit_dirty(); | ||
41 | } else { | ||
42 | floating = view->impl->wants_floating && | ||
43 | view->impl->wants_floating(view); | ||
44 | } | ||
45 | |||
46 | if (floating && client_mode) { | ||
47 | mode = client_mode; | ||
48 | } | ||
49 | |||
26 | wlr_xdg_toplevel_decoration_v1_set_mode(deco->wlr_xdg_decoration, | 50 | wlr_xdg_toplevel_decoration_v1_set_mode(deco->wlr_xdg_decoration, |
27 | WLR_XDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE); | 51 | mode); |
28 | } | 52 | } |
29 | 53 | ||
30 | void handle_xdg_decoration(struct wl_listener *listener, void *data) { | 54 | void handle_xdg_decoration(struct wl_listener *listener, void *data) { |