diff options
Diffstat (limited to 'sway')
140 files changed, 9558 insertions, 7109 deletions
diff --git a/sway/commands.c b/sway/commands.c index fe1e98b5..8d003dfa 100644 --- a/sway/commands.c +++ b/sway/commands.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200809 | ||
2 | #include <ctype.h> | 1 | #include <ctype.h> |
3 | #include <stdarg.h> | 2 | #include <stdarg.h> |
4 | #include <stdlib.h> | 3 | #include <stdlib.h> |
@@ -42,15 +41,17 @@ struct cmd_results *checkarg(int argc, const char *name, enum expected_args type | |||
42 | } | 41 | } |
43 | 42 | ||
44 | /* Keep alphabetized */ | 43 | /* Keep alphabetized */ |
45 | static struct cmd_handler handlers[] = { | 44 | static const struct cmd_handler handlers[] = { |
46 | { "assign", cmd_assign }, | 45 | { "assign", cmd_assign }, |
47 | { "bar", cmd_bar }, | 46 | { "bar", cmd_bar }, |
48 | { "bindcode", cmd_bindcode }, | 47 | { "bindcode", cmd_bindcode }, |
48 | { "bindgesture", cmd_bindgesture }, | ||
49 | { "bindswitch", cmd_bindswitch }, | 49 | { "bindswitch", cmd_bindswitch }, |
50 | { "bindsym", cmd_bindsym }, | 50 | { "bindsym", cmd_bindsym }, |
51 | { "client.background", cmd_client_noop }, | 51 | { "client.background", cmd_client_noop }, |
52 | { "client.focused", cmd_client_focused }, | 52 | { "client.focused", cmd_client_focused }, |
53 | { "client.focused_inactive", cmd_client_focused_inactive }, | 53 | { "client.focused_inactive", cmd_client_focused_inactive }, |
54 | { "client.focused_tab_title", cmd_client_focused_tab_title }, | ||
54 | { "client.placeholder", cmd_client_noop }, | 55 | { "client.placeholder", cmd_client_noop }, |
55 | { "client.unfocused", cmd_client_unfocused }, | 56 | { "client.unfocused", cmd_client_unfocused }, |
56 | { "client.urgent", cmd_client_urgent }, | 57 | { "client.urgent", cmd_client_urgent }, |
@@ -91,6 +92,7 @@ static struct cmd_handler handlers[] = { | |||
91 | { "titlebar_border_thickness", cmd_titlebar_border_thickness }, | 92 | { "titlebar_border_thickness", cmd_titlebar_border_thickness }, |
92 | { "titlebar_padding", cmd_titlebar_padding }, | 93 | { "titlebar_padding", cmd_titlebar_padding }, |
93 | { "unbindcode", cmd_unbindcode }, | 94 | { "unbindcode", cmd_unbindcode }, |
95 | { "unbindgesture", cmd_unbindgesture }, | ||
94 | { "unbindswitch", cmd_unbindswitch }, | 96 | { "unbindswitch", cmd_unbindswitch }, |
95 | { "unbindsym", cmd_unbindsym }, | 97 | { "unbindsym", cmd_unbindsym }, |
96 | { "workspace", cmd_workspace }, | 98 | { "workspace", cmd_workspace }, |
@@ -98,9 +100,10 @@ static struct cmd_handler handlers[] = { | |||
98 | }; | 100 | }; |
99 | 101 | ||
100 | /* Config-time only commands. Keep alphabetized */ | 102 | /* Config-time only commands. Keep alphabetized */ |
101 | static struct cmd_handler config_handlers[] = { | 103 | static const struct cmd_handler config_handlers[] = { |
102 | { "default_orientation", cmd_default_orientation }, | 104 | { "default_orientation", cmd_default_orientation }, |
103 | { "include", cmd_include }, | 105 | { "include", cmd_include }, |
106 | { "primary_selection", cmd_primary_selection }, | ||
104 | { "swaybg_command", cmd_swaybg_command }, | 107 | { "swaybg_command", cmd_swaybg_command }, |
105 | { "swaynag_command", cmd_swaynag_command }, | 108 | { "swaynag_command", cmd_swaynag_command }, |
106 | { "workspace_layout", cmd_workspace_layout }, | 109 | { "workspace_layout", cmd_workspace_layout }, |
@@ -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(const 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,12 +378,15 @@ 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 | if (handler) { |
376 | ? "Command '%s' is shimmed, but unimplemented" | 384 | results = cmd_results_new(CMD_INVALID, |
377 | : "Unknown/invalid command '%s'"; | 385 | "Command '%s' is shimmed, but unimplemented", argv[0]); |
378 | results = cmd_results_new(CMD_INVALID, error, argv[0]); | 386 | } else { |
387 | results = cmd_results_new(CMD_INVALID, | ||
388 | "Unknown/invalid command '%s'", argv[0]); | ||
389 | } | ||
379 | goto cleanup; | 390 | goto cleanup; |
380 | } | 391 | } |
381 | 392 | ||
@@ -401,6 +412,7 @@ struct cmd_results *config_command(char *exec, char **new_block) { | |||
401 | && handler->handle != cmd_bindsym | 412 | && handler->handle != cmd_bindsym |
402 | && handler->handle != cmd_bindcode | 413 | && handler->handle != cmd_bindcode |
403 | && handler->handle != cmd_bindswitch | 414 | && handler->handle != cmd_bindswitch |
415 | && handler->handle != cmd_bindgesture | ||
404 | && handler->handle != cmd_set | 416 | && handler->handle != cmd_set |
405 | && handler->handle != cmd_for_window | 417 | && handler->handle != cmd_for_window |
406 | && (*argv[i] == '\"' || *argv[i] == '\'')) { | 418 | && (*argv[i] == '\"' || *argv[i] == '\'')) { |
@@ -418,12 +430,12 @@ cleanup: | |||
418 | } | 430 | } |
419 | 431 | ||
420 | struct cmd_results *config_subcommand(char **argv, int argc, | 432 | struct cmd_results *config_subcommand(char **argv, int argc, |
421 | struct cmd_handler *handlers, size_t handlers_size) { | 433 | const struct cmd_handler *handlers, size_t handlers_size) { |
422 | char *command = join_args(argv, argc); | 434 | char *command = join_args(argv, argc); |
423 | sway_log(SWAY_DEBUG, "Subcommand: %s", command); | 435 | sway_log(SWAY_DEBUG, "Subcommand: %s", command); |
424 | free(command); | 436 | free(command); |
425 | 437 | ||
426 | struct cmd_handler *handler = find_handler(argv[0], handlers, | 438 | const struct cmd_handler *handler = find_handler(argv[0], handlers, |
427 | handlers_size); | 439 | handlers_size); |
428 | if (!handler) { | 440 | if (!handler) { |
429 | return cmd_results_new(CMD_INVALID, | 441 | return cmd_results_new(CMD_INVALID, |
@@ -453,41 +465,13 @@ struct cmd_results *config_commands_command(char *exec) { | |||
453 | goto cleanup; | 465 | goto cleanup; |
454 | } | 466 | } |
455 | 467 | ||
456 | struct cmd_handler *handler = find_handler(cmd, NULL, 0); | 468 | const struct cmd_handler *handler = find_handler(cmd, NULL, 0); |
457 | if (!handler && strcmp(cmd, "*") != 0) { | 469 | if (!handler && strcmp(cmd, "*") != 0) { |
458 | results = cmd_results_new(CMD_INVALID, | 470 | results = cmd_results_new(CMD_INVALID, |
459 | "Unknown/invalid command '%s'", cmd); | 471 | "Unknown/invalid command '%s'", cmd); |
460 | goto cleanup; | 472 | goto cleanup; |
461 | } | 473 | } |
462 | 474 | ||
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); | 475 | results = cmd_results_new(CMD_SUCCESS, NULL); |
492 | 476 | ||
493 | cleanup: | 477 | cleanup: |
@@ -504,14 +488,10 @@ struct cmd_results *cmd_results_new(enum cmd_status status, | |||
504 | } | 488 | } |
505 | results->status = status; | 489 | results->status = status; |
506 | if (format) { | 490 | if (format) { |
507 | char *error = malloc(256); | ||
508 | va_list args; | 491 | va_list args; |
509 | va_start(args, format); | 492 | va_start(args, format); |
510 | if (error) { | 493 | results->error = vformat_str(format, args); |
511 | vsnprintf(error, 256, format, args); | ||
512 | } | ||
513 | va_end(args); | 494 | va_end(args); |
514 | results->error = error; | ||
515 | } else { | 495 | } else { |
516 | results->error = NULL; | 496 | results->error = NULL; |
517 | } | 497 | } |
diff --git a/sway/commands/assign.c b/sway/commands/assign.c index 976bc3cc..bf95cf00 100644 --- a/sway/commands/assign.c +++ b/sway/commands/assign.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <stdio.h> | 1 | #include <stdio.h> |
3 | #include <string.h> | 2 | #include <string.h> |
4 | #include "sway/commands.h" | 3 | #include "sway/commands.h" |
@@ -17,7 +16,7 @@ struct cmd_results *cmd_assign(int argc, char **argv) { | |||
17 | char *err_str = NULL; | 16 | char *err_str = NULL; |
18 | struct criteria *criteria = criteria_parse(argv[0], &err_str); | 17 | struct criteria *criteria = criteria_parse(argv[0], &err_str); |
19 | if (!criteria) { | 18 | if (!criteria) { |
20 | error = cmd_results_new(CMD_INVALID, err_str); | 19 | error = cmd_results_new(CMD_INVALID, "%s", err_str); |
21 | free(err_str); | 20 | free(err_str); |
22 | return error; | 21 | return error; |
23 | } | 22 | } |
diff --git a/sway/commands/bar.c b/sway/commands/bar.c index d42b7fc2..635e895b 100644 --- a/sway/commands/bar.c +++ b/sway/commands/bar.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200809 | ||
2 | #include <stdio.h> | 1 | #include <stdio.h> |
3 | #include <string.h> | 2 | #include <string.h> |
4 | #include <strings.h> | 3 | #include <strings.h> |
@@ -8,7 +7,7 @@ | |||
8 | #include "log.h" | 7 | #include "log.h" |
9 | 8 | ||
10 | // Must be in alphabetical order for bsearch | 9 | // Must be in alphabetical order for bsearch |
11 | static struct cmd_handler bar_handlers[] = { | 10 | static const struct cmd_handler bar_handlers[] = { |
12 | { "bindcode", bar_cmd_bindcode }, | 11 | { "bindcode", bar_cmd_bindcode }, |
13 | { "binding_mode_indicator", bar_cmd_binding_mode_indicator }, | 12 | { "binding_mode_indicator", bar_cmd_binding_mode_indicator }, |
14 | { "bindsym", bar_cmd_bindsym }, | 13 | { "bindsym", bar_cmd_bindsym }, |
@@ -41,7 +40,7 @@ static struct cmd_handler bar_handlers[] = { | |||
41 | }; | 40 | }; |
42 | 41 | ||
43 | // Must be in alphabetical order for bsearch | 42 | // Must be in alphabetical order for bsearch |
44 | static struct cmd_handler bar_config_handlers[] = { | 43 | static const struct cmd_handler bar_config_handlers[] = { |
45 | { "id", bar_cmd_id }, | 44 | { "id", bar_cmd_id }, |
46 | { "swaybar_command", bar_cmd_swaybar_command }, | 45 | { "swaybar_command", bar_cmd_swaybar_command }, |
47 | }; | 46 | }; |
@@ -73,12 +72,10 @@ struct cmd_results *cmd_bar(int argc, char **argv) { | |||
73 | } | 72 | } |
74 | ++argv; --argc; | 73 | ++argv; --argc; |
75 | } else if (config->reading && !config->current_bar) { | 74 | } else if (config->reading && !config->current_bar) { |
76 | int len = snprintf(NULL, 0, "bar-%d", config->bars->length) + 1; | 75 | id = format_str("bar-%d", config->bars->length); |
77 | id = malloc(len * sizeof(char)); | ||
78 | if (!id) { | 76 | if (!id) { |
79 | return cmd_results_new(CMD_FAILURE, "Unable to allocate bar id"); | 77 | return cmd_results_new(CMD_FAILURE, "Unable to allocate bar id"); |
80 | } | 78 | } |
81 | snprintf(id, len, "bar-%d", config->bars->length); | ||
82 | } else if (!config->reading && strcmp(argv[0], "mode") != 0 && | 79 | } else if (!config->reading && strcmp(argv[0], "mode") != 0 && |
83 | strcmp(argv[0], "hidden_state") != 0) { | 80 | strcmp(argv[0], "hidden_state") != 0) { |
84 | if (is_subcommand(argv[0])) { | 81 | if (is_subcommand(argv[0])) { |
@@ -116,6 +113,7 @@ struct cmd_results *cmd_bar(int argc, char **argv) { | |||
116 | if (res && res->status != CMD_SUCCESS) { | 113 | if (res && res->status != CMD_SUCCESS) { |
117 | if (id) { | 114 | if (id) { |
118 | free_bar_config(config->current_bar); | 115 | free_bar_config(config->current_bar); |
116 | config->current_bar = NULL; | ||
119 | id = NULL; | 117 | id = NULL; |
120 | } | 118 | } |
121 | return res; | 119 | return res; |
diff --git a/sway/commands/bar/bind.c b/sway/commands/bar/bind.c index b4b5bc45..8a837e3f 100644 --- a/sway/commands/bar/bind.c +++ b/sway/commands/bar/bind.c | |||
@@ -96,7 +96,7 @@ static struct cmd_results *bar_cmd_bind(int argc, char **argv, bool code, | |||
96 | } | 96 | } |
97 | if (message) { | 97 | if (message) { |
98 | free_bar_binding(binding); | 98 | free_bar_binding(binding); |
99 | error = cmd_results_new(CMD_INVALID, message); | 99 | error = cmd_results_new(CMD_INVALID, "%s", message); |
100 | free(message); | 100 | free(message); |
101 | return error; | 101 | return error; |
102 | } else if (!binding->button) { | 102 | } else if (!binding->button) { |
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..0c074679 100644 --- a/sway/commands/bar/font.c +++ b/sway/commands/bar/font.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <string.h> | 1 | #include <string.h> |
3 | #include "sway/commands.h" | 2 | #include "sway/commands.h" |
4 | #include "log.h" | 3 | #include "log.h" |
@@ -11,7 +10,20 @@ struct cmd_results *bar_cmd_font(int argc, char **argv) { | |||
11 | } | 10 | } |
12 | char *font = join_args(argv, argc); | 11 | char *font = join_args(argv, argc); |
13 | free(config->current_bar->font); | 12 | free(config->current_bar->font); |
14 | config->current_bar->font = font; | 13 | |
14 | if (strncmp(font, "pango:", 6) == 0) { | ||
15 | if (config->current_bar->pango_markup == PANGO_MARKUP_DEFAULT) { | ||
16 | config->current_bar->pango_markup = true; | ||
17 | } | ||
18 | config->current_bar->font = strdup(font + 6); | ||
19 | } else { | ||
20 | if (config->current_bar->pango_markup == PANGO_MARKUP_DEFAULT) { | ||
21 | config->current_bar->pango_markup = false; | ||
22 | } | ||
23 | config->current_bar->font = strdup(font); | ||
24 | } | ||
25 | |||
26 | free(font); | ||
15 | sway_log(SWAY_DEBUG, "Settings font '%s' for bar: %s", | 27 | sway_log(SWAY_DEBUG, "Settings font '%s' for bar: %s", |
16 | config->current_bar->font, config->current_bar->id); | 28 | config->current_bar->font, config->current_bar->id); |
17 | return cmd_results_new(CMD_SUCCESS, NULL); | 29 | 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..7b38831e 100644 --- a/sway/commands/bar/hidden_state.c +++ b/sway/commands/bar/hidden_state.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <string.h> | 1 | #include <string.h> |
3 | #include <strings.h> | 2 | #include <strings.h> |
4 | #include "sway/commands.h" | 3 | #include "sway/commands.h" |
@@ -54,7 +53,7 @@ struct cmd_results *bar_cmd_hidden_state(int argc, char **argv) { | |||
54 | } | 53 | } |
55 | 54 | ||
56 | const char *state = argv[0]; | 55 | const char *state = argv[0]; |
57 | if (config->reading) { | 56 | if (config->current_bar) { |
58 | error = bar_set_hidden_state(config->current_bar, state); | 57 | error = bar_set_hidden_state(config->current_bar, state); |
59 | } else { | 58 | } else { |
60 | const char *id = argc == 2 ? argv[1] : NULL; | 59 | const char *id = argc == 2 ? argv[1] : NULL; |
diff --git a/sway/commands/bar/icon_theme.c b/sway/commands/bar/icon_theme.c index 6ac07843..fee21709 100644 --- a/sway/commands/bar/icon_theme.c +++ b/sway/commands/bar/icon_theme.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <string.h> | 1 | #include <string.h> |
3 | #include "config.h" | 2 | #include "config.h" |
4 | #include "sway/commands.h" | 3 | #include "sway/commands.h" |
diff --git a/sway/commands/bar/id.c b/sway/commands/bar/id.c index a9a61743..46cf4ca9 100644 --- a/sway/commands/bar/id.c +++ b/sway/commands/bar/id.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <string.h> | 1 | #include <string.h> |
3 | #include "sway/commands.h" | 2 | #include "sway/commands.h" |
4 | #include "log.h" | 3 | #include "log.h" |
diff --git a/sway/commands/bar/mode.c b/sway/commands/bar/mode.c index 8b3fb275..d69e910b 100644 --- a/sway/commands/bar/mode.c +++ b/sway/commands/bar/mode.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <string.h> | 1 | #include <string.h> |
3 | #include <strings.h> | 2 | #include <strings.h> |
4 | #include "sway/commands.h" | 3 | #include "sway/commands.h" |
@@ -58,7 +57,7 @@ struct cmd_results *bar_cmd_mode(int argc, char **argv) { | |||
58 | } | 57 | } |
59 | 58 | ||
60 | const char *mode = argv[0]; | 59 | const char *mode = argv[0]; |
61 | if (config->reading) { | 60 | if (config->current_bar) { |
62 | error = bar_set_mode(config->current_bar, mode); | 61 | error = bar_set_mode(config->current_bar, mode); |
63 | } else { | 62 | } else { |
64 | const char *id = argc == 2 ? argv[1] : NULL; | 63 | const char *id = argc == 2 ? argv[1] : NULL; |
diff --git a/sway/commands/bar/output.c b/sway/commands/bar/output.c index cac1d056..51730176 100644 --- a/sway/commands/bar/output.c +++ b/sway/commands/bar/output.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <stdbool.h> | 1 | #include <stdbool.h> |
3 | #include <string.h> | 2 | #include <string.h> |
4 | #include "sway/commands.h" | 3 | #include "sway/commands.h" |
diff --git a/sway/commands/bar/position.c b/sway/commands/bar/position.c index b207de0b..94f530ec 100644 --- a/sway/commands/bar/position.c +++ b/sway/commands/bar/position.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <string.h> | 1 | #include <string.h> |
3 | #include <strings.h> | 2 | #include <strings.h> |
4 | #include "sway/commands.h" | 3 | #include "sway/commands.h" |
diff --git a/sway/commands/bar/separator_symbol.c b/sway/commands/bar/separator_symbol.c index 6737d4d2..50e9a873 100644 --- a/sway/commands/bar/separator_symbol.c +++ b/sway/commands/bar/separator_symbol.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <string.h> | 1 | #include <string.h> |
3 | #include "sway/commands.h" | 2 | #include "sway/commands.h" |
4 | #include "log.h" | 3 | #include "log.h" |
diff --git a/sway/commands/bar/tray_bind.c b/sway/commands/bar/tray_bind.c index 243834ba..3dc9bc4c 100644 --- a/sway/commands/bar/tray_bind.c +++ b/sway/commands/bar/tray_bind.c | |||
@@ -26,7 +26,7 @@ static struct cmd_results *tray_bind(int argc, char **argv, bool code) { | |||
26 | } | 26 | } |
27 | if (message) { | 27 | if (message) { |
28 | free(binding); | 28 | free(binding); |
29 | error = cmd_results_new(CMD_INVALID, message); | 29 | error = cmd_results_new(CMD_INVALID, "%s", message); |
30 | free(message); | 30 | free(message); |
31 | return error; | 31 | return error; |
32 | } else if (!binding->button) { | 32 | } else if (!binding->button) { |
diff --git a/sway/commands/bar/tray_output.c b/sway/commands/bar/tray_output.c index eb3b486e..679facf7 100644 --- a/sway/commands/bar/tray_output.c +++ b/sway/commands/bar/tray_output.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <string.h> | 1 | #include <string.h> |
3 | #include "config.h" | 2 | #include "config.h" |
4 | #include "sway/commands.h" | 3 | #include "sway/commands.h" |
diff --git a/sway/commands/bind.c b/sway/commands/bind.c index f6e58d99..268f2855 100644 --- a/sway/commands/bind.c +++ b/sway/commands/bind.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <libevdev/libevdev.h> | 1 | #include <libevdev/libevdev.h> |
3 | #include <linux/input-event-codes.h> | 2 | #include <linux/input-event-codes.h> |
4 | #include <string.h> | 3 | #include <string.h> |
@@ -8,6 +7,7 @@ | |||
8 | #include <wlr/types/wlr_cursor.h> | 7 | #include <wlr/types/wlr_cursor.h> |
9 | #include "sway/commands.h" | 8 | #include "sway/commands.h" |
10 | #include "sway/config.h" | 9 | #include "sway/config.h" |
10 | #include "sway/desktop/transaction.h" | ||
11 | #include "sway/input/cursor.h" | 11 | #include "sway/input/cursor.h" |
12 | #include "sway/input/keyboard.h" | 12 | #include "sway/input/keyboard.h" |
13 | #include "sway/ipc-server.h" | 13 | #include "sway/ipc-server.h" |
@@ -46,7 +46,7 @@ static bool binding_switch_compare(struct sway_switch_binding *binding_a, | |||
46 | if (binding_a->type != binding_b->type) { | 46 | if (binding_a->type != binding_b->type) { |
47 | return false; | 47 | return false; |
48 | } | 48 | } |
49 | if (binding_a->state != binding_b->state) { | 49 | if (binding_a->trigger != binding_b->trigger) { |
50 | return false; | 50 | return false; |
51 | } | 51 | } |
52 | if ((binding_a->flags & BINDING_LOCKED) != | 52 | if ((binding_a->flags & BINDING_LOCKED) != |
@@ -126,7 +126,7 @@ static struct cmd_results *identify_key(const char* name, bool first_key, | |||
126 | if (!button) { | 126 | if (!button) { |
127 | if (message) { | 127 | if (message) { |
128 | struct cmd_results *error = | 128 | struct cmd_results *error = |
129 | cmd_results_new(CMD_INVALID, message); | 129 | cmd_results_new(CMD_INVALID, "%s", message); |
130 | free(message); | 130 | free(message); |
131 | return error; | 131 | return error; |
132 | } else { | 132 | } else { |
@@ -142,7 +142,7 @@ static struct cmd_results *identify_key(const char* name, bool first_key, | |||
142 | if (!button) { | 142 | if (!button) { |
143 | if (message) { | 143 | if (message) { |
144 | struct cmd_results *error = | 144 | struct cmd_results *error = |
145 | cmd_results_new(CMD_INVALID, message); | 145 | cmd_results_new(CMD_INVALID, "%s", message); |
146 | free(message); | 146 | free(message); |
147 | return error; | 147 | return error; |
148 | } else { | 148 | } else { |
@@ -181,7 +181,7 @@ static struct cmd_results *identify_key(const char* name, bool first_key, | |||
181 | uint32_t button = get_mouse_bindsym(name, &message); | 181 | uint32_t button = get_mouse_bindsym(name, &message); |
182 | if (message) { | 182 | if (message) { |
183 | struct cmd_results *error = | 183 | struct cmd_results *error = |
184 | cmd_results_new(CMD_INVALID, message); | 184 | cmd_results_new(CMD_INVALID, "%s", message); |
185 | free(message); | 185 | free(message); |
186 | return error; | 186 | return error; |
187 | } else if (button) { | 187 | } else if (button) { |
@@ -371,6 +371,7 @@ static struct cmd_results *cmd_bindsym_or_bindcode(int argc, char **argv, | |||
371 | strlen("--input-device=")) == 0) { | 371 | strlen("--input-device=")) == 0) { |
372 | free(binding->input); | 372 | free(binding->input); |
373 | binding->input = strdup(argv[0] + strlen("--input-device=")); | 373 | binding->input = strdup(argv[0] + strlen("--input-device=")); |
374 | strip_quotes(binding->input); | ||
374 | } else if (strcmp("--no-warn", argv[0]) == 0) { | 375 | } else if (strcmp("--no-warn", argv[0]) == 0) { |
375 | warn = false; | 376 | warn = false; |
376 | } else if (strcmp("--no-repeat", argv[0]) == 0) { | 377 | } else if (strcmp("--no-repeat", argv[0]) == 0) { |
@@ -537,7 +538,7 @@ struct cmd_results *cmd_bind_or_unbind_switch(int argc, char **argv, | |||
537 | free_switch_binding(binding); | 538 | free_switch_binding(binding); |
538 | return cmd_results_new(CMD_FAILURE, | 539 | return cmd_results_new(CMD_FAILURE, |
539 | "Invalid %s command (expected binding with the form " | 540 | "Invalid %s command (expected binding with the form " |
540 | "<switch>:<state>)", bindtype, argc); | 541 | "<switch>:<state>)", bindtype); |
541 | } | 542 | } |
542 | if (strcmp(split->items[0], "tablet") == 0) { | 543 | if (strcmp(split->items[0], "tablet") == 0) { |
543 | binding->type = WLR_SWITCH_TYPE_TABLET_MODE; | 544 | binding->type = WLR_SWITCH_TYPE_TABLET_MODE; |
@@ -547,20 +548,21 @@ struct cmd_results *cmd_bind_or_unbind_switch(int argc, char **argv, | |||
547 | free_switch_binding(binding); | 548 | free_switch_binding(binding); |
548 | return cmd_results_new(CMD_FAILURE, | 549 | return cmd_results_new(CMD_FAILURE, |
549 | "Invalid %s command (expected switch binding: " | 550 | "Invalid %s command (expected switch binding: " |
550 | "unknown switch %s)", bindtype, split->items[0]); | 551 | "unknown switch %s)", bindtype, |
552 | (const char *)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, (const char *)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..fd2ac7a8 100644 --- a/sway/commands/client.c +++ b/sway/commands/client.c | |||
@@ -5,9 +5,8 @@ | |||
5 | #include "sway/tree/container.h" | 5 | #include "sway/tree/container.h" |
6 | #include "util.h" | 6 | #include "util.h" |
7 | 7 | ||
8 | static void rebuild_textures_iterator(struct sway_container *con, void *data) { | 8 | static void container_update_iterator(struct sway_container *con, void *data) { |
9 | container_update_marks_textures(con); | 9 | container_update(con); |
10 | container_update_title_textures(con); | ||
11 | } | 10 | } |
12 | 11 | ||
13 | static struct cmd_results *handle_command(int argc, char **argv, char *cmd_name, | 12 | static struct cmd_results *handle_command(int argc, char **argv, char *cmd_name, |
@@ -18,6 +17,12 @@ static struct cmd_results *handle_command(int argc, char **argv, char *cmd_name, | |||
18 | return error; | 17 | return error; |
19 | } | 18 | } |
20 | 19 | ||
20 | if (argc > 3 && strcmp(cmd_name, "client.focused_tab_title") == 0) { | ||
21 | sway_log(SWAY_ERROR, | ||
22 | "Warning: indicator and child_border colors have no effect for %s", | ||
23 | cmd_name); | ||
24 | } | ||
25 | |||
21 | struct border_colors colors = {0}; | 26 | struct border_colors colors = {0}; |
22 | const char *ind_hex = argc > 3 ? argv[3] : default_indicator; | 27 | const char *ind_hex = argc > 3 ? argv[3] : default_indicator; |
23 | const char *child_hex = argc > 4 ? argv[4] : argv[1]; // def to background | 28 | const char *child_hex = argc > 4 ? argv[4] : argv[1]; // def to background |
@@ -45,12 +50,7 @@ static struct cmd_results *handle_command(int argc, char **argv, char *cmd_name, | |||
45 | memcpy(class, &colors, sizeof(struct border_colors)); | 50 | memcpy(class, &colors, sizeof(struct border_colors)); |
46 | 51 | ||
47 | if (config->active) { | 52 | if (config->active) { |
48 | root_for_each_container(rebuild_textures_iterator, NULL); | 53 | root_for_each_container(container_update_iterator, NULL); |
49 | |||
50 | for (int i = 0; i < root->outputs->length; ++i) { | ||
51 | struct sway_output *output = root->outputs->items[i]; | ||
52 | output_damage_whole(output); | ||
53 | } | ||
54 | } | 54 | } |
55 | 55 | ||
56 | return cmd_results_new(CMD_SUCCESS, NULL); | 56 | return cmd_results_new(CMD_SUCCESS, NULL); |
@@ -80,3 +80,13 @@ struct cmd_results *cmd_client_noop(int argc, char **argv) { | |||
80 | sway_log(SWAY_INFO, "Warning: %s is ignored by sway", argv[-1]); | 80 | sway_log(SWAY_INFO, "Warning: %s is ignored by sway", argv[-1]); |
81 | return cmd_results_new(CMD_SUCCESS, NULL); | 81 | return cmd_results_new(CMD_SUCCESS, NULL); |
82 | } | 82 | } |
83 | |||
84 | struct cmd_results *cmd_client_focused_tab_title(int argc, char **argv) { | ||
85 | struct cmd_results *result = handle_command(argc, argv, | ||
86 | "client.focused_tab_title", | ||
87 | &config->border_colors.focused_tab_title, "#2e9ef4ff"); | ||
88 | if (result && result->status == CMD_SUCCESS) { | ||
89 | config->has_focused_tab_title = true; | ||
90 | } | ||
91 | return result; | ||
92 | } | ||
diff --git a/sway/commands/exec_always.c b/sway/commands/exec_always.c index 39e48a44..8bc1048c 100644 --- a/sway/commands/exec_always.c +++ b/sway/commands/exec_always.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <stdlib.h> | 1 | #include <stdlib.h> |
3 | #include <stdint.h> | 2 | #include <stdint.h> |
4 | #include <string.h> | 3 | #include <string.h> |
@@ -7,6 +6,8 @@ | |||
7 | #include <signal.h> | 6 | #include <signal.h> |
8 | #include "sway/commands.h" | 7 | #include "sway/commands.h" |
9 | #include "sway/config.h" | 8 | #include "sway/config.h" |
9 | #include "sway/server.h" | ||
10 | #include "sway/desktop/launcher.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" |
@@ -24,11 +25,22 @@ struct cmd_results *cmd_exec_validate(int argc, char **argv) { | |||
24 | return error; | 25 | return error; |
25 | } | 26 | } |
26 | 27 | ||
28 | static void export_xdga_token(struct launcher_ctx *ctx) { | ||
29 | const char *token = launcher_ctx_get_token_name(ctx); | ||
30 | setenv("XDG_ACTIVATION_TOKEN", token, 1); | ||
31 | } | ||
32 | |||
33 | static void export_startup_id(struct launcher_ctx *ctx) { | ||
34 | const char *token = launcher_ctx_get_token_name(ctx); | ||
35 | setenv("DESKTOP_STARTUP_ID", token, 1); | ||
36 | } | ||
37 | |||
27 | struct cmd_results *cmd_exec_process(int argc, char **argv) { | 38 | struct cmd_results *cmd_exec_process(int argc, char **argv) { |
28 | struct cmd_results *error = NULL; | 39 | struct cmd_results *error = NULL; |
29 | char *tmp = NULL; | 40 | char *cmd = NULL; |
41 | bool no_startup_id = false; | ||
30 | if (strcmp(argv[0], "--no-startup-id") == 0) { | 42 | if (strcmp(argv[0], "--no-startup-id") == 0) { |
31 | sway_log(SWAY_INFO, "exec switch '--no-startup-id' not supported, ignored."); | 43 | no_startup_id = true; |
32 | --argc; ++argv; | 44 | --argc; ++argv; |
33 | if ((error = checkarg(argc, argv[-1], EXPECTED_AT_LEAST, 1))) { | 45 | if ((error = checkarg(argc, argv[-1], EXPECTED_AT_LEAST, 1))) { |
34 | return error; | 46 | return error; |
@@ -36,17 +48,12 @@ struct cmd_results *cmd_exec_process(int argc, char **argv) { | |||
36 | } | 48 | } |
37 | 49 | ||
38 | if (argc == 1 && (argv[0][0] == '\'' || argv[0][0] == '"')) { | 50 | if (argc == 1 && (argv[0][0] == '\'' || argv[0][0] == '"')) { |
39 | tmp = strdup(argv[0]); | 51 | cmd = strdup(argv[0]); |
40 | strip_quotes(tmp); | 52 | strip_quotes(cmd); |
41 | } else { | 53 | } else { |
42 | tmp = join_args(argv, argc); | 54 | cmd = join_args(argv, argc); |
43 | } | 55 | } |
44 | 56 | ||
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); | 57 | sway_log(SWAY_DEBUG, "Executing %s", cmd); |
51 | 58 | ||
52 | int fd[2]; | 59 | int fd[2]; |
@@ -55,18 +62,28 @@ struct cmd_results *cmd_exec_process(int argc, char **argv) { | |||
55 | } | 62 | } |
56 | 63 | ||
57 | pid_t pid, child; | 64 | pid_t pid, child; |
65 | struct launcher_ctx *ctx = launcher_ctx_create_internal(); | ||
58 | // Fork process | 66 | // Fork process |
59 | if ((pid = fork()) == 0) { | 67 | if ((pid = fork()) == 0) { |
60 | // Fork child process again | 68 | // Fork child process again |
69 | restore_nofile_limit(); | ||
61 | setsid(); | 70 | setsid(); |
62 | sigset_t set; | 71 | sigset_t set; |
63 | sigemptyset(&set); | 72 | sigemptyset(&set); |
64 | sigprocmask(SIG_SETMASK, &set, NULL); | 73 | sigprocmask(SIG_SETMASK, &set, NULL); |
74 | signal(SIGPIPE, SIG_DFL); | ||
65 | close(fd[0]); | 75 | close(fd[0]); |
66 | if ((child = fork()) == 0) { | 76 | if ((child = fork()) == 0) { |
67 | close(fd[1]); | 77 | close(fd[1]); |
68 | execl("/bin/sh", "/bin/sh", "-c", cmd, (void *)NULL); | 78 | if (ctx) { |
69 | _exit(0); | 79 | export_xdga_token(ctx); |
80 | } | ||
81 | if (ctx && !no_startup_id) { | ||
82 | export_startup_id(ctx); | ||
83 | } | ||
84 | execlp("sh", "sh", "-c", cmd, (void *)NULL); | ||
85 | sway_log_errno(SWAY_ERROR, "execlp failed"); | ||
86 | _exit(1); | ||
70 | } | 87 | } |
71 | ssize_t s = 0; | 88 | ssize_t s = 0; |
72 | while ((size_t)s < sizeof(pid_t)) { | 89 | while ((size_t)s < sizeof(pid_t)) { |
@@ -75,10 +92,12 @@ struct cmd_results *cmd_exec_process(int argc, char **argv) { | |||
75 | close(fd[1]); | 92 | close(fd[1]); |
76 | _exit(0); // Close child process | 93 | _exit(0); // Close child process |
77 | } else if (pid < 0) { | 94 | } else if (pid < 0) { |
95 | free(cmd); | ||
78 | close(fd[0]); | 96 | close(fd[0]); |
79 | close(fd[1]); | 97 | close(fd[1]); |
80 | return cmd_results_new(CMD_FAILURE, "fork() failed"); | 98 | return cmd_results_new(CMD_FAILURE, "fork() failed"); |
81 | } | 99 | } |
100 | free(cmd); | ||
82 | close(fd[1]); // close write | 101 | close(fd[1]); // close write |
83 | ssize_t s = 0; | 102 | ssize_t s = 0; |
84 | while ((size_t)s < sizeof(pid_t)) { | 103 | while ((size_t)s < sizeof(pid_t)) { |
@@ -89,8 +108,12 @@ struct cmd_results *cmd_exec_process(int argc, char **argv) { | |||
89 | waitpid(pid, NULL, 0); | 108 | waitpid(pid, NULL, 0); |
90 | if (child > 0) { | 109 | if (child > 0) { |
91 | sway_log(SWAY_DEBUG, "Child process created with pid %d", child); | 110 | sway_log(SWAY_DEBUG, "Child process created with pid %d", child); |
92 | root_record_workspace_pid(child); | 111 | if (ctx != NULL) { |
112 | sway_log(SWAY_DEBUG, "Recording workspace for process %d", child); | ||
113 | ctx->pid = child; | ||
114 | } | ||
93 | } else { | 115 | } else { |
116 | launcher_ctx_destroy(ctx); | ||
94 | return cmd_results_new(CMD_FAILURE, "Second fork() failed"); | 117 | return cmd_results_new(CMD_FAILURE, "Second fork() failed"); |
95 | } | 118 | } |
96 | 119 | ||
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/floating_minmax_size.c b/sway/commands/floating_minmax_size.c index 3a1d606a..e8c24ace 100644 --- a/sway/commands/floating_minmax_size.c +++ b/sway/commands/floating_minmax_size.c | |||
@@ -23,16 +23,16 @@ static struct cmd_results *handle_command(int argc, char **argv, char *cmd_name, | |||
23 | char *err; | 23 | char *err; |
24 | int width = (int)strtol(argv[0], &err, 10); | 24 | int width = (int)strtol(argv[0], &err, 10); |
25 | if (*err) { | 25 | if (*err) { |
26 | return cmd_results_new(CMD_INVALID, cmd_name, usage); | 26 | return cmd_results_new(CMD_INVALID, "%s", usage); |
27 | } | 27 | } |
28 | 28 | ||
29 | if (strcmp(argv[1], "x") != 0) { | 29 | if (strcmp(argv[1], "x") != 0) { |
30 | return cmd_results_new(CMD_INVALID, cmd_name, usage); | 30 | return cmd_results_new(CMD_INVALID, "%s", usage); |
31 | } | 31 | } |
32 | 32 | ||
33 | int height = (int)strtol(argv[2], &err, 10); | 33 | int height = (int)strtol(argv[2], &err, 10); |
34 | if (*err) { | 34 | if (*err) { |
35 | return cmd_results_new(CMD_INVALID, cmd_name, usage); | 35 | return cmd_results_new(CMD_INVALID, "%s", usage); |
36 | } | 36 | } |
37 | 37 | ||
38 | *config_width = width; | 38 | *config_width = width; |
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..9920d03e 100644 --- a/sway/commands/font.c +++ b/sway/commands/font.c | |||
@@ -1,9 +1,9 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <string.h> | 1 | #include <string.h> |
3 | #include "sway/commands.h" | 2 | #include "sway/commands.h" |
4 | #include "sway/config.h" | 3 | #include "sway/config.h" |
5 | #include "log.h" | 4 | #include "log.h" |
6 | #include "stringop.h" | 5 | #include "stringop.h" |
6 | #include <pango/pangocairo.h> | ||
7 | 7 | ||
8 | struct cmd_results *cmd_font(int argc, char **argv) { | 8 | struct cmd_results *cmd_font(int argc, char **argv) { |
9 | struct cmd_results *error = NULL; | 9 | struct cmd_results *error = NULL; |
@@ -16,12 +16,34 @@ struct cmd_results *cmd_font(int argc, char **argv) { | |||
16 | if (strncmp(font, "pango:", 6) == 0) { | 16 | if (strncmp(font, "pango:", 6) == 0) { |
17 | config->pango_markup = true; | 17 | config->pango_markup = true; |
18 | config->font = strdup(font + 6); | 18 | config->font = strdup(font + 6); |
19 | free(font); | ||
19 | } else { | 20 | } else { |
20 | config->pango_markup = false; | 21 | config->pango_markup = false; |
21 | config->font = strdup(font); | 22 | config->font = font; |
22 | } | 23 | } |
23 | 24 | ||
24 | free(font); | 25 | // Parse the font early so we can reject it if it's not valid for pango. |
25 | config_update_font_height(true); | 26 | // Also avoids re-parsing each time we render text. |
27 | PangoFontDescription *font_description = pango_font_description_from_string(config->font); | ||
28 | |||
29 | const char *family = pango_font_description_get_family(font_description); | ||
30 | if (family == NULL) { | ||
31 | pango_font_description_free(font_description); | ||
32 | return cmd_results_new(CMD_FAILURE, "Invalid font family."); | ||
33 | } | ||
34 | |||
35 | const gint size = pango_font_description_get_size(font_description); | ||
36 | if (size == 0) { | ||
37 | pango_font_description_free(font_description); | ||
38 | return cmd_results_new(CMD_FAILURE, "Invalid font size."); | ||
39 | } | ||
40 | |||
41 | if (config->font_description != NULL) { | ||
42 | pango_font_description_free(config->font_description); | ||
43 | } | ||
44 | |||
45 | config->font_description = font_description; | ||
46 | config_update_font_height(); | ||
47 | |||
26 | return cmd_results_new(CMD_SUCCESS, NULL); | 48 | return cmd_results_new(CMD_SUCCESS, NULL); |
27 | } | 49 | } |
diff --git a/sway/commands/for_window.c b/sway/commands/for_window.c index ee9f4647..905e6776 100644 --- a/sway/commands/for_window.c +++ b/sway/commands/for_window.c | |||
@@ -14,7 +14,7 @@ struct cmd_results *cmd_for_window(int argc, char **argv) { | |||
14 | char *err_str = NULL; | 14 | char *err_str = NULL; |
15 | struct criteria *criteria = criteria_parse(argv[0], &err_str); | 15 | struct criteria *criteria = criteria_parse(argv[0], &err_str); |
16 | if (!criteria) { | 16 | if (!criteria) { |
17 | error = cmd_results_new(CMD_INVALID, err_str); | 17 | error = cmd_results_new(CMD_INVALID, "%s", err_str); |
18 | free(err_str); | 18 | free(err_str); |
19 | return error; | 19 | return error; |
20 | } | 20 | } |
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..90a20716 --- /dev/null +++ b/sway/commands/gesture.c | |||
@@ -0,0 +1,165 @@ | |||
1 | #include "sway/config.h" | ||
2 | |||
3 | #include "gesture.h" | ||
4 | #include "log.h" | ||
5 | #include "stringop.h" | ||
6 | #include "sway/commands.h" | ||
7 | |||
8 | void free_gesture_binding(struct sway_gesture_binding *binding) { | ||
9 | if (!binding) { | ||
10 | return; | ||
11 | } | ||
12 | free(binding->input); | ||
13 | free(binding->command); | ||
14 | free(binding); | ||
15 | } | ||
16 | |||
17 | /** | ||
18 | * Returns true if the bindings have the same gesture type, direction, etc | ||
19 | */ | ||
20 | static bool binding_gesture_equal(struct sway_gesture_binding *binding_a, | ||
21 | struct sway_gesture_binding *binding_b) { | ||
22 | if (strcmp(binding_a->input, binding_b->input) != 0) { | ||
23 | return false; | ||
24 | } | ||
25 | |||
26 | if (!gesture_equal(&binding_a->gesture, &binding_b->gesture)) { | ||
27 | return false; | ||
28 | } | ||
29 | |||
30 | if ((binding_a->flags & BINDING_EXACT) != | ||
31 | (binding_b->flags & BINDING_EXACT)) { | ||
32 | return false; | ||
33 | } | ||
34 | return true; | ||
35 | } | ||
36 | |||
37 | /** | ||
38 | * Add gesture binding to config | ||
39 | */ | ||
40 | static struct cmd_results *gesture_binding_add( | ||
41 | struct sway_gesture_binding *binding, | ||
42 | const char *gesturecombo, bool warn) { | ||
43 | list_t *mode_bindings = config->current_mode->gesture_bindings; | ||
44 | // overwrite the binding if it already exists | ||
45 | bool overwritten = false; | ||
46 | for (int i = 0; i < mode_bindings->length; ++i) { | ||
47 | struct sway_gesture_binding *config_binding = mode_bindings->items[i]; | ||
48 | if (binding_gesture_equal(binding, config_binding)) { | ||
49 | sway_log(SWAY_INFO, "Overwriting binding '%s' to `%s` from `%s`", | ||
50 | gesturecombo, binding->command, config_binding->command); | ||
51 | if (warn) { | ||
52 | config_add_swaynag_warning("Overwriting binding" | ||
53 | "'%s' to `%s` from `%s`", | ||
54 | gesturecombo, binding->command, | ||
55 | config_binding->command); | ||
56 | } | ||
57 | free_gesture_binding(config_binding); | ||
58 | mode_bindings->items[i] = binding; | ||
59 | overwritten = true; | ||
60 | } | ||
61 | } | ||
62 | |||
63 | if (!overwritten) { | ||
64 | list_add(mode_bindings, binding); | ||
65 | sway_log(SWAY_DEBUG, "bindgesture - Bound %s to command `%s`", | ||
66 | gesturecombo, binding->command); | ||
67 | } | ||
68 | |||
69 | return cmd_results_new(CMD_SUCCESS, NULL); | ||
70 | } | ||
71 | |||
72 | /** | ||
73 | * Remove gesture binding from config | ||
74 | */ | ||
75 | static struct cmd_results *gesture_binding_remove( | ||
76 | struct sway_gesture_binding *binding, const char *gesturecombo) { | ||
77 | list_t *mode_bindings = config->current_mode->gesture_bindings; | ||
78 | for (int i = 0; i < mode_bindings->length; ++i) { | ||
79 | struct sway_gesture_binding *config_binding = mode_bindings->items[i]; | ||
80 | if (binding_gesture_equal(binding, config_binding)) { | ||
81 | free_gesture_binding(config_binding); | ||
82 | free_gesture_binding(binding); | ||
83 | list_del(mode_bindings, i); | ||
84 | sway_log(SWAY_DEBUG, "unbindgesture - Unbound %s gesture", | ||
85 | gesturecombo); | ||
86 | return cmd_results_new(CMD_SUCCESS, NULL); | ||
87 | } | ||
88 | } | ||
89 | |||
90 | free_gesture_binding(binding); | ||
91 | return cmd_results_new(CMD_FAILURE, "Could not find gesture binding `%s`", | ||
92 | gesturecombo); | ||
93 | } | ||
94 | |||
95 | /** | ||
96 | * Parse and execute bindgesture or unbindgesture command. | ||
97 | */ | ||
98 | static struct cmd_results *cmd_bind_or_unbind_gesture(int argc, char **argv, bool unbind) { | ||
99 | int minargs = 2; | ||
100 | char *bindtype = "bindgesture"; | ||
101 | if (unbind) { | ||
102 | minargs--; | ||
103 | bindtype = "unbindgesture"; | ||
104 | } | ||
105 | |||
106 | struct cmd_results *error = NULL; | ||
107 | if ((error = checkarg(argc, bindtype, EXPECTED_AT_LEAST, minargs))) { | ||
108 | return error; | ||
109 | } | ||
110 | struct sway_gesture_binding *binding = calloc(1, sizeof(struct sway_gesture_binding)); | ||
111 | if (!binding) { | ||
112 | return cmd_results_new(CMD_FAILURE, "Unable to allocate binding"); | ||
113 | } | ||
114 | binding->input = strdup("*"); | ||
115 | |||
116 | bool warn = true; | ||
117 | |||
118 | // Handle flags | ||
119 | while (argc > 0) { | ||
120 | if (strcmp("--exact", argv[0]) == 0) { | ||
121 | binding->flags |= BINDING_EXACT; | ||
122 | } else if (strcmp("--no-warn", argv[0]) == 0) { | ||
123 | warn = false; | ||
124 | } else if (strncmp("--input-device=", argv[0], | ||
125 | strlen("--input-device=")) == 0) { | ||
126 | free(binding->input); | ||
127 | binding->input = strdup(argv[0] + strlen("--input-device=")); | ||
128 | } else { | ||
129 | break; | ||
130 | } | ||
131 | argv++; | ||
132 | argc--; | ||
133 | } | ||
134 | |||
135 | if (argc < minargs) { | ||
136 | free(binding); | ||
137 | return cmd_results_new(CMD_FAILURE, | ||
138 | "Invalid %s command (expected at least %d " | ||
139 | "non-option arguments, got %d)", bindtype, minargs, argc); | ||
140 | } | ||
141 | |||
142 | char* errmsg = NULL; | ||
143 | if ((errmsg = gesture_parse(argv[0], &binding->gesture))) { | ||
144 | free(binding); | ||
145 | struct cmd_results *final = cmd_results_new(CMD_FAILURE, | ||
146 | "Invalid %s command (%s)", | ||
147 | bindtype, errmsg); | ||
148 | free(errmsg); | ||
149 | return final; | ||
150 | } | ||
151 | |||
152 | if (unbind) { | ||
153 | return gesture_binding_remove(binding, argv[0]); | ||
154 | } | ||
155 | binding->command = join_args(argv + 1, argc - 1); | ||
156 | return gesture_binding_add(binding, argv[0], warn); | ||
157 | } | ||
158 | |||
159 | struct cmd_results *cmd_bindgesture(int argc, char **argv) { | ||
160 | return cmd_bind_or_unbind_gesture(argc, argv, false); | ||
161 | } | ||
162 | |||
163 | struct cmd_results *cmd_unbindgesture(int argc, char **argv) { | ||
164 | return cmd_bind_or_unbind_gesture(argc, argv, true); | ||
165 | } | ||
diff --git a/sway/commands/hide_edge_borders.c b/sway/commands/hide_edge_borders.c index 9a1d8445..43bd6dc8 100644 --- a/sway/commands/hide_edge_borders.c +++ b/sway/commands/hide_edge_borders.c | |||
@@ -20,7 +20,7 @@ struct cmd_results *cmd_hide_edge_borders(int argc, char **argv) { | |||
20 | } | 20 | } |
21 | 21 | ||
22 | if (!argc) { | 22 | if (!argc) { |
23 | return cmd_results_new(CMD_INVALID, expected_syntax); | 23 | return cmd_results_new(CMD_INVALID, "%s", expected_syntax); |
24 | } | 24 | } |
25 | 25 | ||
26 | if (strcmp(argv[0], "none") == 0) { | 26 | if (strcmp(argv[0], "none") == 0) { |
@@ -38,7 +38,7 @@ struct cmd_results *cmd_hide_edge_borders(int argc, char **argv) { | |||
38 | config->hide_edge_borders = E_NONE; | 38 | config->hide_edge_borders = E_NONE; |
39 | config->hide_edge_borders_smart = ESMART_NO_GAPS; | 39 | config->hide_edge_borders_smart = ESMART_NO_GAPS; |
40 | } else { | 40 | } else { |
41 | return cmd_results_new(CMD_INVALID, expected_syntax); | 41 | return cmd_results_new(CMD_INVALID, "%s", expected_syntax); |
42 | } | 42 | } |
43 | config->hide_lone_tab = hide_lone_tab; | 43 | config->hide_lone_tab = hide_lone_tab; |
44 | 44 | ||
diff --git a/sway/commands/inhibit_idle.c b/sway/commands/inhibit_idle.c index aebc2bf9..6125736a 100644 --- a/sway/commands/inhibit_idle.c +++ b/sway/commands/inhibit_idle.c | |||
@@ -41,7 +41,7 @@ struct cmd_results *cmd_inhibit_idle(int argc, char **argv) { | |||
41 | sway_idle_inhibit_v1_user_inhibitor_destroy(inhibitor); | 41 | sway_idle_inhibit_v1_user_inhibitor_destroy(inhibitor); |
42 | } else { | 42 | } else { |
43 | inhibitor->mode = mode; | 43 | inhibitor->mode = mode; |
44 | sway_idle_inhibit_v1_check_active(server.idle_inhibit_manager_v1); | 44 | sway_idle_inhibit_v1_check_active(); |
45 | } | 45 | } |
46 | } else if (!clear) { | 46 | } else if (!clear) { |
47 | sway_idle_inhibit_v1_user_inhibitor_register(con->view, mode); | 47 | sway_idle_inhibit_v1_user_inhibitor_register(con->view, mode); |
diff --git a/sway/commands/input.c b/sway/commands/input.c index c9bb8e06..306c40f7 100644 --- a/sway/commands/input.c +++ b/sway/commands/input.c | |||
@@ -7,13 +7,14 @@ | |||
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 }, |
14 | { "drag", input_cmd_drag }, | 14 | { "drag", input_cmd_drag }, |
15 | { "drag_lock", input_cmd_drag_lock }, | 15 | { "drag_lock", input_cmd_drag_lock }, |
16 | { "dwt", input_cmd_dwt }, | 16 | { "dwt", input_cmd_dwt }, |
17 | { "dwtp", input_cmd_dwtp }, | ||
17 | { "events", input_cmd_events }, | 18 | { "events", input_cmd_events }, |
18 | { "left_handed", input_cmd_left_handed }, | 19 | { "left_handed", input_cmd_left_handed }, |
19 | { "map_from_region", input_cmd_map_from_region }, | 20 | { "map_from_region", input_cmd_map_from_region }, |
@@ -24,7 +25,9 @@ static struct cmd_handler input_handlers[] = { | |||
24 | { "pointer_accel", input_cmd_pointer_accel }, | 25 | { "pointer_accel", input_cmd_pointer_accel }, |
25 | { "repeat_delay", input_cmd_repeat_delay }, | 26 | { "repeat_delay", input_cmd_repeat_delay }, |
26 | { "repeat_rate", input_cmd_repeat_rate }, | 27 | { "repeat_rate", input_cmd_repeat_rate }, |
28 | { "rotation_angle", input_cmd_rotation_angle }, | ||
27 | { "scroll_button", input_cmd_scroll_button }, | 29 | { "scroll_button", input_cmd_scroll_button }, |
30 | { "scroll_button_lock", input_cmd_scroll_button_lock }, | ||
28 | { "scroll_factor", input_cmd_scroll_factor }, | 31 | { "scroll_factor", input_cmd_scroll_factor }, |
29 | { "scroll_method", input_cmd_scroll_method }, | 32 | { "scroll_method", input_cmd_scroll_method }, |
30 | { "tap", input_cmd_tap }, | 33 | { "tap", input_cmd_tap }, |
@@ -40,7 +43,7 @@ static struct cmd_handler input_handlers[] = { | |||
40 | }; | 43 | }; |
41 | 44 | ||
42 | // must be in order for the bsearch | 45 | // must be in order for the bsearch |
43 | static struct cmd_handler input_config_handlers[] = { | 46 | static const struct cmd_handler input_config_handlers[] = { |
44 | { "xkb_capslock", input_cmd_xkb_capslock }, | 47 | { "xkb_capslock", input_cmd_xkb_capslock }, |
45 | { "xkb_numlock", input_cmd_xkb_numlock }, | 48 | { "xkb_numlock", input_cmd_xkb_numlock }, |
46 | }; | 49 | }; |
diff --git a/sway/commands/input/calibration_matrix.c b/sway/commands/input/calibration_matrix.c index 38749fbb..53fe2c35 100644 --- a/sway/commands/input/calibration_matrix.c +++ b/sway/commands/input/calibration_matrix.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <string.h> | 1 | #include <string.h> |
3 | #include <strings.h> | 2 | #include <strings.h> |
4 | #include "sway/config.h" | 3 | #include "sway/config.h" |
diff --git a/sway/commands/input/dwtp.c b/sway/commands/input/dwtp.c new file mode 100644 index 00000000..232e2b26 --- /dev/null +++ b/sway/commands/input/dwtp.c | |||
@@ -0,0 +1,25 @@ | |||
1 | #include <string.h> | ||
2 | #include <strings.h> | ||
3 | #include "sway/config.h" | ||
4 | #include "sway/commands.h" | ||
5 | #include "sway/input/input-manager.h" | ||
6 | #include "util.h" | ||
7 | |||
8 | struct cmd_results *input_cmd_dwtp(int argc, char **argv) { | ||
9 | struct cmd_results *error = NULL; | ||
10 | if ((error = checkarg(argc, "dwtp", EXPECTED_AT_LEAST, 1))) { | ||
11 | return error; | ||
12 | } | ||
13 | struct input_config *ic = config->handler_context.input_config; | ||
14 | if (!ic) { | ||
15 | return cmd_results_new(CMD_FAILURE, "No input device defined."); | ||
16 | } | ||
17 | |||
18 | if (parse_boolean(argv[0], true)) { | ||
19 | ic->dwtp = LIBINPUT_CONFIG_DWTP_ENABLED; | ||
20 | } else { | ||
21 | ic->dwtp = LIBINPUT_CONFIG_DWTP_DISABLED; | ||
22 | } | ||
23 | |||
24 | return cmd_results_new(CMD_SUCCESS, NULL); | ||
25 | } | ||
diff --git a/sway/commands/input/events.c b/sway/commands/input/events.c index 9405181a..08d99bf0 100644 --- a/sway/commands/input/events.c +++ b/sway/commands/input/events.c | |||
@@ -1,14 +1,19 @@ | |||
1 | #include <limits.h> | 1 | #include <limits.h> |
2 | #include <string.h> | 2 | #include <string.h> |
3 | #include <strings.h> | 3 | #include <strings.h> |
4 | #include <wlr/backend/libinput.h> | 4 | #include <wlr/config.h> |
5 | #include "sway/config.h" | 5 | #include "sway/config.h" |
6 | #include "sway/commands.h" | 6 | #include "sway/commands.h" |
7 | #include "sway/input/input-manager.h" | 7 | #include "sway/input/input-manager.h" |
8 | #include "log.h" | 8 | #include "log.h" |
9 | 9 | ||
10 | #if WLR_HAS_LIBINPUT_BACKEND | ||
11 | #include <wlr/backend/libinput.h> | ||
12 | #endif | ||
13 | |||
10 | static void toggle_supported_send_events_for_device(struct input_config *ic, | 14 | static void toggle_supported_send_events_for_device(struct input_config *ic, |
11 | struct sway_input_device *input_device) { | 15 | struct sway_input_device *input_device) { |
16 | #if WLR_HAS_LIBINPUT_BACKEND | ||
12 | struct wlr_input_device *wlr_device = input_device->wlr_device; | 17 | struct wlr_input_device *wlr_device = input_device->wlr_device; |
13 | if (!wlr_input_device_is_libinput(wlr_device)) { | 18 | if (!wlr_input_device_is_libinput(wlr_device)) { |
14 | return; | 19 | return; |
@@ -41,6 +46,7 @@ static void toggle_supported_send_events_for_device(struct input_config *ic, | |||
41 | } | 46 | } |
42 | 47 | ||
43 | ic->send_events = mode; | 48 | ic->send_events = mode; |
49 | #endif | ||
44 | } | 50 | } |
45 | 51 | ||
46 | static int mode_for_name(const char *name) { | 52 | static int mode_for_name(const char *name) { |
@@ -56,6 +62,7 @@ static int mode_for_name(const char *name) { | |||
56 | 62 | ||
57 | static void toggle_select_send_events_for_device(struct input_config *ic, | 63 | static void toggle_select_send_events_for_device(struct input_config *ic, |
58 | struct sway_input_device *input_device, int argc, char **argv) { | 64 | struct sway_input_device *input_device, int argc, char **argv) { |
65 | #if WLR_HAS_LIBINPUT_BACKEND | ||
59 | if (!wlr_input_device_is_libinput(input_device->wlr_device)) { | 66 | if (!wlr_input_device_is_libinput(input_device->wlr_device)) { |
60 | return; | 67 | return; |
61 | } | 68 | } |
@@ -72,6 +79,7 @@ static void toggle_select_send_events_for_device(struct input_config *ic, | |||
72 | } | 79 | } |
73 | } | 80 | } |
74 | ic->send_events = mode_for_name(argv[index % argc]); | 81 | ic->send_events = mode_for_name(argv[index % argc]); |
82 | #endif | ||
75 | } | 83 | } |
76 | 84 | ||
77 | static void toggle_send_events(int argc, char **argv) { | 85 | static void toggle_send_events(int argc, char **argv) { |
diff --git a/sway/commands/input/map_from_region.c b/sway/commands/input/map_from_region.c index de00b714..2f8f753d 100644 --- a/sway/commands/input/map_from_region.c +++ b/sway/commands/input/map_from_region.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <stdbool.h> | 1 | #include <stdbool.h> |
3 | #include <string.h> | 2 | #include <string.h> |
4 | #include <strings.h> | 3 | #include <strings.h> |
@@ -11,11 +10,21 @@ static bool parse_coords(const char *str, double *x, double *y, bool *mm) { | |||
11 | *mm = false; | 10 | *mm = false; |
12 | 11 | ||
13 | char *end; | 12 | char *end; |
14 | *x = strtod(str, &end); | 13 | |
15 | if (end[0] != 'x') { | 14 | // Check for "0x" prefix to avoid strtod treating the string as hex |
16 | return false; | 15 | if (str[0] == '0' && str[1] == 'x') { |
16 | if (strlen(str) < 3) { | ||
17 | return false; | ||
18 | } | ||
19 | *x = 0; | ||
20 | end = (char *)str + 2; | ||
21 | } else { | ||
22 | *x = strtod(str, &end); | ||
23 | if (end[0] != 'x') { | ||
24 | return false; | ||
25 | } | ||
26 | ++end; | ||
17 | } | 27 | } |
18 | ++end; | ||
19 | 28 | ||
20 | *y = strtod(end, &end); | 29 | *y = strtod(end, &end); |
21 | if (end[0] == 'm') { | 30 | if (end[0] == 'm') { |
diff --git a/sway/commands/input/map_to_output.c b/sway/commands/input/map_to_output.c index f60fb7d5..a7266baa 100644 --- a/sway/commands/input/map_to_output.c +++ b/sway/commands/input/map_to_output.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <string.h> | 1 | #include <string.h> |
3 | #include <strings.h> | 2 | #include <strings.h> |
4 | #include "sway/config.h" | 3 | #include "sway/config.h" |
diff --git a/sway/commands/input/map_to_region.c b/sway/commands/input/map_to_region.c index e85495e5..9087c589 100644 --- a/sway/commands/input/map_to_region.c +++ b/sway/commands/input/map_to_region.c | |||
@@ -1,7 +1,5 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <stdlib.h> | 1 | #include <stdlib.h> |
3 | #include <string.h> | 2 | #include <string.h> |
4 | #include <wlr/types/wlr_box.h> | ||
5 | #include "sway/commands.h" | 3 | #include "sway/commands.h" |
6 | #include "sway/config.h" | 4 | #include "sway/config.h" |
7 | 5 | ||
@@ -50,5 +48,5 @@ struct cmd_results *input_cmd_map_to_region(int argc, char **argv) { | |||
50 | error: | 48 | error: |
51 | free(ic->mapped_to_region); | 49 | free(ic->mapped_to_region); |
52 | ic->mapped_to_region = NULL; | 50 | ic->mapped_to_region = NULL; |
53 | return cmd_results_new(CMD_FAILURE, errstr); | 51 | return cmd_results_new(CMD_FAILURE, "%s", errstr); |
54 | } | 52 | } |
diff --git a/sway/commands/input/rotation_angle.c b/sway/commands/input/rotation_angle.c new file mode 100644 index 00000000..5e278fff --- /dev/null +++ b/sway/commands/input/rotation_angle.c | |||
@@ -0,0 +1,29 @@ | |||
1 | #include <math.h> | ||
2 | #include <stdlib.h> | ||
3 | #include <string.h> | ||
4 | #include "sway/config.h" | ||
5 | #include "sway/commands.h" | ||
6 | #include "sway/input/input-manager.h" | ||
7 | #include "util.h" | ||
8 | |||
9 | struct cmd_results *input_cmd_rotation_angle(int argc, char **argv) { | ||
10 | struct cmd_results *error = NULL; | ||
11 | if ((error = checkarg(argc, "rotation_angle", EXPECTED_AT_LEAST, 1))) { | ||
12 | return error; | ||
13 | } | ||
14 | struct input_config *ic = config->handler_context.input_config; | ||
15 | if (!ic) { | ||
16 | return cmd_results_new(CMD_FAILURE, "No input device defined."); | ||
17 | } | ||
18 | |||
19 | float rotation_angle = parse_float(argv[0]); | ||
20 | if (isnan(rotation_angle)) { | ||
21 | return cmd_results_new(CMD_INVALID, | ||
22 | "Invalid rotation_angle; expected float."); | ||
23 | } if (rotation_angle < 0 || rotation_angle > 360) { | ||
24 | return cmd_results_new(CMD_INVALID, "Input out of range [0, 360)"); | ||
25 | } | ||
26 | ic->rotation_angle = rotation_angle; | ||
27 | |||
28 | return cmd_results_new(CMD_SUCCESS, NULL); | ||
29 | } | ||
diff --git a/sway/commands/input/scroll_button.c b/sway/commands/input/scroll_button.c index 6b331419..81f69a6d 100644 --- a/sway/commands/input/scroll_button.c +++ b/sway/commands/input/scroll_button.c | |||
@@ -21,7 +21,7 @@ struct cmd_results *input_cmd_scroll_button(int argc, char **argv) { | |||
21 | char *message = NULL; | 21 | char *message = NULL; |
22 | uint32_t button = get_mouse_button(*argv, &message); | 22 | uint32_t button = get_mouse_button(*argv, &message); |
23 | if (message) { | 23 | if (message) { |
24 | error = cmd_results_new(CMD_INVALID, message); | 24 | error = cmd_results_new(CMD_INVALID, "%s", message); |
25 | free(message); | 25 | free(message); |
26 | return error; | 26 | return error; |
27 | } else if (button == SWAY_SCROLL_UP || button == SWAY_SCROLL_DOWN | 27 | } else if (button == SWAY_SCROLL_UP || button == SWAY_SCROLL_DOWN |
diff --git a/sway/commands/input/scroll_button_lock.c b/sway/commands/input/scroll_button_lock.c new file mode 100644 index 00000000..f96b6514 --- /dev/null +++ b/sway/commands/input/scroll_button_lock.c | |||
@@ -0,0 +1,26 @@ | |||
1 | #include <libinput.h> | ||
2 | #include <string.h> | ||
3 | #include <strings.h> | ||
4 | #include "sway/config.h" | ||
5 | #include "sway/commands.h" | ||
6 | #include "sway/input/input-manager.h" | ||
7 | #include "util.h" | ||
8 | |||
9 | struct cmd_results *input_cmd_scroll_button_lock(int argc, char **argv) { | ||
10 | struct cmd_results *error = NULL; | ||
11 | if ((error = checkarg(argc, "scroll_button_lock", EXPECTED_AT_LEAST, 1))) { | ||
12 | return error; | ||
13 | } | ||
14 | struct input_config *ic = config->handler_context.input_config; | ||
15 | if (!ic) { | ||
16 | return cmd_results_new(CMD_FAILURE, "No input device defined."); | ||
17 | } | ||
18 | |||
19 | if (parse_boolean(argv[0], true)) { | ||
20 | ic->scroll_button_lock = LIBINPUT_CONFIG_SCROLL_BUTTON_LOCK_ENABLED; | ||
21 | } else { | ||
22 | ic->scroll_button_lock = LIBINPUT_CONFIG_SCROLL_BUTTON_LOCK_DISABLED; | ||
23 | } | ||
24 | |||
25 | return cmd_results_new(CMD_SUCCESS, NULL); | ||
26 | } | ||
diff --git a/sway/commands/input/xkb_file.c b/sway/commands/input/xkb_file.c index 493f94fb..056f00e5 100644 --- a/sway/commands/input/xkb_file.c +++ b/sway/commands/input/xkb_file.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <unistd.h> | 1 | #include <unistd.h> |
3 | #include <errno.h> | 2 | #include <errno.h> |
4 | #include "sway/config.h" | 3 | #include "sway/config.h" |
diff --git a/sway/commands/input/xkb_layout.c b/sway/commands/input/xkb_layout.c index 22626517..1d01886c 100644 --- a/sway/commands/input/xkb_layout.c +++ b/sway/commands/input/xkb_layout.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include "sway/config.h" | 1 | #include "sway/config.h" |
3 | #include "sway/commands.h" | 2 | #include "sway/commands.h" |
4 | #include "log.h" | 3 | #include "log.h" |
diff --git a/sway/commands/input/xkb_model.c b/sway/commands/input/xkb_model.c index f4a33de3..a9144a8a 100644 --- a/sway/commands/input/xkb_model.c +++ b/sway/commands/input/xkb_model.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include "sway/config.h" | 1 | #include "sway/config.h" |
3 | #include "sway/commands.h" | 2 | #include "sway/commands.h" |
4 | #include "log.h" | 3 | #include "log.h" |
diff --git a/sway/commands/input/xkb_numlock.c b/sway/commands/input/xkb_numlock.c index 87d3e60c..bbe848fe 100644 --- a/sway/commands/input/xkb_numlock.c +++ b/sway/commands/input/xkb_numlock.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include "sway/config.h" | 1 | #include "sway/config.h" |
3 | #include "sway/commands.h" | 2 | #include "sway/commands.h" |
4 | #include "util.h" | 3 | #include "util.h" |
diff --git a/sway/commands/input/xkb_options.c b/sway/commands/input/xkb_options.c index d609293f..7ca20777 100644 --- a/sway/commands/input/xkb_options.c +++ b/sway/commands/input/xkb_options.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include "sway/config.h" | 1 | #include "sway/config.h" |
3 | #include "sway/commands.h" | 2 | #include "sway/commands.h" |
4 | #include "log.h" | 3 | #include "log.h" |
diff --git a/sway/commands/input/xkb_rules.c b/sway/commands/input/xkb_rules.c index 3b59622c..8fbd26fb 100644 --- a/sway/commands/input/xkb_rules.c +++ b/sway/commands/input/xkb_rules.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include "sway/config.h" | 1 | #include "sway/config.h" |
3 | #include "sway/commands.h" | 2 | #include "sway/commands.h" |
4 | #include "log.h" | 3 | #include "log.h" |
diff --git a/sway/commands/input/xkb_switch_layout.c b/sway/commands/input/xkb_switch_layout.c index d6548a68..ecac8e6c 100644 --- a/sway/commands/input/xkb_switch_layout.c +++ b/sway/commands/input/xkb_switch_layout.c | |||
@@ -1,10 +1,15 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <assert.h> | 1 | #include <assert.h> |
2 | #include <wlr/interfaces/wlr_keyboard.h> | ||
3 | #include "sway/config.h" | 3 | #include "sway/config.h" |
4 | #include "sway/commands.h" | 4 | #include "sway/commands.h" |
5 | #include "sway/input/input-manager.h" | 5 | #include "sway/input/input-manager.h" |
6 | #include "log.h" | 6 | #include "log.h" |
7 | 7 | ||
8 | struct xkb_switch_layout_action { | ||
9 | struct wlr_keyboard *keyboard; | ||
10 | xkb_layout_index_t layout; | ||
11 | }; | ||
12 | |||
8 | static void switch_layout(struct wlr_keyboard *kbd, xkb_layout_index_t idx) { | 13 | 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); | 14 | xkb_layout_index_t num_layouts = xkb_keymap_num_layouts(kbd->keymap); |
10 | if (idx >= num_layouts) { | 15 | if (idx >= num_layouts) { |
@@ -28,10 +33,10 @@ static xkb_layout_index_t get_current_layout_index(struct wlr_keyboard *kbd) { | |||
28 | return layout_idx; | 33 | return layout_idx; |
29 | } | 34 | } |
30 | 35 | ||
31 | static void switch_layout_relative(struct wlr_keyboard *kbd, int dir) { | 36 | 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); | 37 | xkb_layout_index_t num_layouts = xkb_keymap_num_layouts(kbd->keymap); |
33 | xkb_layout_index_t idx = get_current_layout_index(kbd); | 38 | xkb_layout_index_t idx = get_current_layout_index(kbd); |
34 | switch_layout(kbd, (idx + num_layouts + dir) % num_layouts); | 39 | return (idx + num_layouts + dir) % num_layouts; |
35 | } | 40 | } |
36 | 41 | ||
37 | struct cmd_results *input_cmd_xkb_switch_layout(int argc, char **argv) { | 42 | struct cmd_results *input_cmd_xkb_switch_layout(int argc, char **argv) { |
@@ -66,6 +71,18 @@ struct cmd_results *input_cmd_xkb_switch_layout(int argc, char **argv) { | |||
66 | relative = 0; | 71 | relative = 0; |
67 | } | 72 | } |
68 | 73 | ||
74 | struct xkb_switch_layout_action *actions = calloc( | ||
75 | wl_list_length(&server.input->devices), | ||
76 | sizeof(struct xkb_switch_layout_action)); | ||
77 | size_t actions_len = 0; | ||
78 | |||
79 | if (!actions) { | ||
80 | return cmd_results_new(CMD_FAILURE, "Unable to allocate actions"); | ||
81 | } | ||
82 | |||
83 | /* Calculate new indexes first because switching a layout in one | ||
84 | keyboard may result in a change on other keyboards as well because | ||
85 | of keyboard groups. */ | ||
69 | struct sway_input_device *dev; | 86 | struct sway_input_device *dev; |
70 | wl_list_for_each(dev, &server.input->devices, link) { | 87 | wl_list_for_each(dev, &server.input->devices, link) { |
71 | if (strcmp(ic->identifier, "*") != 0 && | 88 | if (strcmp(ic->identifier, "*") != 0 && |
@@ -76,12 +93,22 @@ struct cmd_results *input_cmd_xkb_switch_layout(int argc, char **argv) { | |||
76 | if (dev->wlr_device->type != WLR_INPUT_DEVICE_KEYBOARD) { | 93 | if (dev->wlr_device->type != WLR_INPUT_DEVICE_KEYBOARD) { |
77 | continue; | 94 | continue; |
78 | } | 95 | } |
96 | |||
97 | struct xkb_switch_layout_action *action = | ||
98 | &actions[actions_len++]; | ||
99 | |||
100 | action->keyboard = wlr_keyboard_from_input_device(dev->wlr_device); | ||
79 | if (relative) { | 101 | if (relative) { |
80 | switch_layout_relative(dev->wlr_device->keyboard, relative); | 102 | action->layout = get_layout_relative(action->keyboard, relative); |
81 | } else { | 103 | } else { |
82 | switch_layout(dev->wlr_device->keyboard, layout); | 104 | action->layout = layout; |
83 | } | 105 | } |
84 | } | 106 | } |
85 | 107 | ||
108 | for (size_t i = 0; i < actions_len; i++) { | ||
109 | switch_layout(actions[i].keyboard, actions[i].layout); | ||
110 | } | ||
111 | free(actions); | ||
112 | |||
86 | return cmd_results_new(CMD_SUCCESS, NULL); | 113 | return cmd_results_new(CMD_SUCCESS, NULL); |
87 | } | 114 | } |
diff --git a/sway/commands/input/xkb_variant.c b/sway/commands/input/xkb_variant.c index d0e21d77..2d14ea9c 100644 --- a/sway/commands/input/xkb_variant.c +++ b/sway/commands/input/xkb_variant.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include "sway/config.h" | 1 | #include "sway/config.h" |
3 | #include "sway/commands.h" | 2 | #include "sway/commands.h" |
4 | #include "log.h" | 3 | #include "log.h" |
diff --git a/sway/commands/layout.c b/sway/commands/layout.c index f2af183b..12ce4839 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, |
@@ -153,20 +153,20 @@ struct cmd_results *cmd_layout(int argc, char **argv) { | |||
153 | workspace->output); | 153 | workspace->output); |
154 | } | 154 | } |
155 | if (new_layout == L_NONE) { | 155 | if (new_layout == L_NONE) { |
156 | return cmd_results_new(CMD_INVALID, expected_syntax); | 156 | return cmd_results_new(CMD_INVALID, "%s", expected_syntax); |
157 | } | 157 | } |
158 | if (new_layout != old_layout) { | 158 | if (new_layout != old_layout) { |
159 | if (container) { | 159 | if (container) { |
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/mark.c b/sway/commands/mark.c index aa5f185c..2bfc86b3 100644 --- a/sway/commands/mark.c +++ b/sway/commands/mark.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <string.h> | 1 | #include <string.h> |
3 | #include "sway/commands.h" | 2 | #include "sway/commands.h" |
4 | #include "sway/config.h" | 3 | #include "sway/config.h" |
@@ -59,7 +58,7 @@ struct cmd_results *cmd_mark(int argc, char **argv) { | |||
59 | } | 58 | } |
60 | 59 | ||
61 | free(mark); | 60 | free(mark); |
62 | container_update_marks_textures(container); | 61 | container_update_marks(container); |
63 | if (container->view) { | 62 | if (container->view) { |
64 | view_execute_criteria(container->view); | 63 | view_execute_criteria(container->view); |
65 | } | 64 | } |
diff --git a/sway/commands/mode.c b/sway/commands/mode.c index a5871dab..b3216967 100644 --- a/sway/commands/mode.c +++ b/sway/commands/mode.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <stdbool.h> | 1 | #include <stdbool.h> |
3 | #include <string.h> | 2 | #include <string.h> |
4 | #include "sway/commands.h" | 3 | #include "sway/commands.h" |
@@ -9,12 +8,14 @@ | |||
9 | #include "stringop.h" | 8 | #include "stringop.h" |
10 | 9 | ||
11 | // Must be in order for the bsearch | 10 | // Must be in order for the bsearch |
12 | static struct cmd_handler mode_handlers[] = { | 11 | static const struct cmd_handler mode_handlers[] = { |
13 | { "bindcode", cmd_bindcode }, | 12 | { "bindcode", cmd_bindcode }, |
13 | { "bindgesture", cmd_bindgesture }, | ||
14 | { "bindswitch", cmd_bindswitch }, | 14 | { "bindswitch", cmd_bindswitch }, |
15 | { "bindsym", cmd_bindsym }, | 15 | { "bindsym", cmd_bindsym }, |
16 | { "set", cmd_set }, | 16 | { "set", cmd_set }, |
17 | { "unbindcode", cmd_unbindcode }, | 17 | { "unbindcode", cmd_unbindcode }, |
18 | { "unbindgesture", cmd_unbindgesture }, | ||
18 | { "unbindswitch", cmd_unbindswitch }, | 19 | { "unbindswitch", cmd_unbindswitch }, |
19 | { "unbindsym", cmd_unbindsym }, | 20 | { "unbindsym", cmd_unbindsym }, |
20 | }; | 21 | }; |
@@ -59,6 +60,7 @@ struct cmd_results *cmd_mode(int argc, char **argv) { | |||
59 | mode->keycode_bindings = create_list(); | 60 | mode->keycode_bindings = create_list(); |
60 | mode->mouse_bindings = create_list(); | 61 | mode->mouse_bindings = create_list(); |
61 | mode->switch_bindings = create_list(); | 62 | mode->switch_bindings = create_list(); |
63 | mode->gesture_bindings = create_list(); | ||
62 | mode->pango = pango; | 64 | mode->pango = pango; |
63 | list_add(config->modes, mode); | 65 | list_add(config->modes, mode); |
64 | } | 66 | } |
diff --git a/sway/commands/move.c b/sway/commands/move.c index f8f89f18..8addf26e 100644 --- a/sway/commands/move.c +++ b/sway/commands/move.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <ctype.h> | 1 | #include <ctype.h> |
3 | #include <math.h> | 2 | #include <math.h> |
4 | #include <stdbool.h> | 3 | #include <stdbool.h> |
@@ -113,8 +112,8 @@ static void container_move_to_container_from_direction( | |||
113 | struct sway_container *container, struct sway_container *destination, | 112 | struct sway_container *container, struct sway_container *destination, |
114 | enum wlr_direction move_dir) { | 113 | enum wlr_direction move_dir) { |
115 | if (destination->view) { | 114 | if (destination->view) { |
116 | if (destination->parent == container->parent && | 115 | if (destination->pending.parent == container->pending.parent && |
117 | destination->workspace == container->workspace) { | 116 | destination->pending.workspace == container->pending.workspace) { |
118 | sway_log(SWAY_DEBUG, "Swapping siblings"); | 117 | sway_log(SWAY_DEBUG, "Swapping siblings"); |
119 | list_t *siblings = container_get_siblings(container); | 118 | list_t *siblings = container_get_siblings(container); |
120 | int container_index = list_find(siblings, container); | 119 | int container_index = list_find(siblings, container); |
@@ -126,28 +125,28 @@ static void container_move_to_container_from_direction( | |||
126 | int offset = | 125 | int offset = |
127 | move_dir == WLR_DIRECTION_LEFT || move_dir == WLR_DIRECTION_UP; | 126 | move_dir == WLR_DIRECTION_LEFT || move_dir == WLR_DIRECTION_UP; |
128 | int index = container_sibling_index(destination) + offset; | 127 | int index = container_sibling_index(destination) + offset; |
129 | if (destination->parent) { | 128 | if (destination->pending.parent) { |
130 | container_insert_child(destination->parent, container, index); | 129 | container_insert_child(destination->pending.parent, container, index); |
131 | } else { | 130 | } else { |
132 | workspace_insert_tiling(destination->workspace, | 131 | workspace_insert_tiling(destination->pending.workspace, |
133 | container, index); | 132 | container, index); |
134 | } | 133 | } |
135 | container->width = container->height = 0; | 134 | container->pending.width = container->pending.height = 0; |
136 | container->width_fraction = container->height_fraction = 0; | 135 | container->width_fraction = container->height_fraction = 0; |
137 | workspace_squash(destination->workspace); | 136 | workspace_squash(destination->pending.workspace); |
138 | } | 137 | } |
139 | return; | 138 | return; |
140 | } | 139 | } |
141 | 140 | ||
142 | if (is_parallel(destination->layout, move_dir)) { | 141 | if (is_parallel(destination->pending.layout, move_dir)) { |
143 | sway_log(SWAY_DEBUG, "Reparenting container (parallel)"); | 142 | sway_log(SWAY_DEBUG, "Reparenting container (parallel)"); |
144 | int index = | 143 | int index = |
145 | move_dir == WLR_DIRECTION_RIGHT || move_dir == WLR_DIRECTION_DOWN ? | 144 | move_dir == WLR_DIRECTION_RIGHT || move_dir == WLR_DIRECTION_DOWN ? |
146 | 0 : destination->children->length; | 145 | 0 : destination->pending.children->length; |
147 | container_insert_child(destination, container, index); | 146 | container_insert_child(destination, container, index); |
148 | container->width = container->height = 0; | 147 | container->pending.width = container->pending.height = 0; |
149 | container->width_fraction = container->height_fraction = 0; | 148 | container->width_fraction = container->height_fraction = 0; |
150 | workspace_squash(destination->workspace); | 149 | workspace_squash(destination->pending.workspace); |
151 | return; | 150 | return; |
152 | } | 151 | } |
153 | 152 | ||
@@ -168,7 +167,7 @@ static void container_move_to_container_from_direction( | |||
168 | static void container_move_to_workspace_from_direction( | 167 | static void container_move_to_workspace_from_direction( |
169 | struct sway_container *container, struct sway_workspace *workspace, | 168 | struct sway_container *container, struct sway_workspace *workspace, |
170 | enum wlr_direction move_dir) { | 169 | enum wlr_direction move_dir) { |
171 | container->width = container->height = 0; | 170 | container->pending.width = container->pending.height = 0; |
172 | container->width_fraction = container->height_fraction = 0; | 171 | container->width_fraction = container->height_fraction = 0; |
173 | 172 | ||
174 | if (is_parallel(workspace->layout, move_dir)) { | 173 | if (is_parallel(workspace->layout, move_dir)) { |
@@ -188,8 +187,8 @@ static void container_move_to_workspace_from_direction( | |||
188 | workspace_add_tiling(workspace, container); | 187 | workspace_add_tiling(workspace, container); |
189 | return; | 188 | return; |
190 | } | 189 | } |
191 | while (focus_inactive->parent) { | 190 | while (focus_inactive->pending.parent) { |
192 | focus_inactive = focus_inactive->parent; | 191 | focus_inactive = focus_inactive->pending.parent; |
193 | } | 192 | } |
194 | container_move_to_container_from_direction(container, focus_inactive, | 193 | container_move_to_container_from_direction(container, focus_inactive, |
195 | move_dir); | 194 | move_dir); |
@@ -197,25 +196,33 @@ static void container_move_to_workspace_from_direction( | |||
197 | 196 | ||
198 | static void container_move_to_workspace(struct sway_container *container, | 197 | static void container_move_to_workspace(struct sway_container *container, |
199 | struct sway_workspace *workspace) { | 198 | struct sway_workspace *workspace) { |
200 | if (container->workspace == workspace) { | 199 | if (container->pending.workspace == workspace) { |
201 | return; | 200 | return; |
202 | } | 201 | } |
203 | struct sway_workspace *old_workspace = container->workspace; | 202 | struct sway_workspace *old_workspace = container->pending.workspace; |
204 | if (container_is_floating(container)) { | 203 | if (container_is_floating(container)) { |
205 | struct sway_output *old_output = container->workspace->output; | 204 | struct sway_output *old_output = container->pending.workspace->output; |
206 | container_detach(container); | 205 | container_detach(container); |
207 | workspace_add_floating(workspace, container); | 206 | workspace_add_floating(workspace, container); |
208 | container_handle_fullscreen_reparent(container); | 207 | container_handle_fullscreen_reparent(container); |
209 | // If changing output, center it within the workspace | 208 | // If changing output, adjust the coordinates of the window. |
210 | if (old_output != workspace->output && !container->fullscreen_mode) { | 209 | if (old_output != workspace->output && !container->pending.fullscreen_mode) { |
211 | container_floating_move_to_center(container); | 210 | struct wlr_box workspace_box, old_workspace_box; |
211 | workspace_get_box(workspace, &workspace_box); | ||
212 | workspace_get_box(old_workspace, &old_workspace_box); | ||
213 | floating_fix_coordinates(container, &old_workspace_box, &workspace_box); | ||
214 | if (container->scratchpad && workspace->output) { | ||
215 | struct wlr_box output_box; | ||
216 | output_get_box(workspace->output, &output_box); | ||
217 | container->transform = workspace_box; | ||
218 | } | ||
212 | } | 219 | } |
213 | } else { | 220 | } else { |
214 | container_detach(container); | 221 | container_detach(container); |
215 | if (workspace_is_empty(workspace) && container->children) { | 222 | if (workspace_is_empty(workspace) && container->pending.children) { |
216 | workspace_unwrap_children(workspace, container); | 223 | workspace_unwrap_children(workspace, container); |
217 | } else { | 224 | } else { |
218 | container->width = container->height = 0; | 225 | container->pending.width = container->pending.height = 0; |
219 | container->width_fraction = container->height_fraction = 0; | 226 | container->width_fraction = container->height_fraction = 0; |
220 | workspace_add_tiling(workspace, container); | 227 | workspace_add_tiling(workspace, container); |
221 | } | 228 | } |
@@ -237,13 +244,13 @@ static void container_move_to_container(struct sway_container *container, | |||
237 | return; | 244 | return; |
238 | } | 245 | } |
239 | if (container_is_floating(container)) { | 246 | if (container_is_floating(container)) { |
240 | container_move_to_workspace(container, destination->workspace); | 247 | container_move_to_workspace(container, destination->pending.workspace); |
241 | return; | 248 | return; |
242 | } | 249 | } |
243 | struct sway_workspace *old_workspace = container->workspace; | 250 | struct sway_workspace *old_workspace = container->pending.workspace; |
244 | 251 | ||
245 | container_detach(container); | 252 | container_detach(container); |
246 | container->width = container->height = 0; | 253 | container->pending.width = container->pending.height = 0; |
247 | container->width_fraction = container->height_fraction = 0; | 254 | container->width_fraction = container->height_fraction = 0; |
248 | 255 | ||
249 | if (destination->view) { | 256 | if (destination->view) { |
@@ -256,12 +263,12 @@ static void container_move_to_container(struct sway_container *container, | |||
256 | ipc_event_window(container, "move"); | 263 | ipc_event_window(container, "move"); |
257 | } | 264 | } |
258 | 265 | ||
259 | if (destination->workspace) { | 266 | if (destination->pending.workspace) { |
260 | workspace_focus_fullscreen(destination->workspace); | 267 | workspace_focus_fullscreen(destination->pending.workspace); |
261 | workspace_detect_urgent(destination->workspace); | 268 | workspace_detect_urgent(destination->pending.workspace); |
262 | } | 269 | } |
263 | 270 | ||
264 | if (old_workspace && old_workspace != destination->workspace) { | 271 | if (old_workspace && old_workspace != destination->pending.workspace) { |
265 | workspace_detect_urgent(old_workspace); | 272 | workspace_detect_urgent(old_workspace); |
266 | } | 273 | } |
267 | } | 274 | } |
@@ -275,7 +282,7 @@ static bool container_move_to_next_output(struct sway_container *container, | |||
275 | if (!sway_assert(ws, "Expected output to have a workspace")) { | 282 | if (!sway_assert(ws, "Expected output to have a workspace")) { |
276 | return false; | 283 | return false; |
277 | } | 284 | } |
278 | switch (container->fullscreen_mode) { | 285 | switch (container->pending.fullscreen_mode) { |
279 | case FULLSCREEN_NONE: | 286 | case FULLSCREEN_NONE: |
280 | container_move_to_workspace_from_direction(container, ws, move_dir); | 287 | container_move_to_workspace_from_direction(container, ws, move_dir); |
281 | return true; | 288 | return true; |
@@ -293,12 +300,12 @@ static bool container_move_to_next_output(struct sway_container *container, | |||
293 | static bool container_move_in_direction(struct sway_container *container, | 300 | static bool container_move_in_direction(struct sway_container *container, |
294 | enum wlr_direction move_dir) { | 301 | enum wlr_direction move_dir) { |
295 | // If moving a fullscreen view, only consider outputs | 302 | // If moving a fullscreen view, only consider outputs |
296 | switch (container->fullscreen_mode) { | 303 | switch (container->pending.fullscreen_mode) { |
297 | case FULLSCREEN_NONE: | 304 | case FULLSCREEN_NONE: |
298 | break; | 305 | break; |
299 | case FULLSCREEN_WORKSPACE: | 306 | case FULLSCREEN_WORKSPACE: |
300 | return container_move_to_next_output(container, | 307 | return container_move_to_next_output(container, |
301 | container->workspace->output, move_dir); | 308 | container->pending.workspace->output, move_dir); |
302 | case FULLSCREEN_GLOBAL: | 309 | case FULLSCREEN_GLOBAL: |
303 | return false; | 310 | return false; |
304 | } | 311 | } |
@@ -317,26 +324,26 @@ static bool container_move_in_direction(struct sway_container *container, | |||
317 | while (!ancestor) { | 324 | while (!ancestor) { |
318 | // Don't allow containers to move out of their | 325 | // Don't allow containers to move out of their |
319 | // fullscreen or floating parent | 326 | // fullscreen or floating parent |
320 | if (current->fullscreen_mode || container_is_floating(current)) { | 327 | if (current->pending.fullscreen_mode || container_is_floating(current)) { |
321 | return false; | 328 | return false; |
322 | } | 329 | } |
323 | 330 | ||
324 | enum sway_container_layout parent_layout = container_parent_layout(current); | 331 | enum sway_container_layout parent_layout = container_parent_layout(current); |
325 | if (!is_parallel(parent_layout, move_dir)) { | 332 | if (!is_parallel(parent_layout, move_dir)) { |
326 | if (!current->parent) { | 333 | if (!current->pending.parent) { |
327 | // No parallel parent, so we reorient the workspace | 334 | // No parallel parent, so we reorient the workspace |
328 | current = workspace_wrap_children(current->workspace); | 335 | current = workspace_wrap_children(current->pending.workspace); |
329 | current->workspace->layout = | 336 | current->pending.workspace->layout = |
330 | move_dir == WLR_DIRECTION_LEFT || | 337 | move_dir == WLR_DIRECTION_LEFT || |
331 | move_dir == WLR_DIRECTION_RIGHT ? | 338 | move_dir == WLR_DIRECTION_RIGHT ? |
332 | L_HORIZ : L_VERT; | 339 | L_HORIZ : L_VERT; |
333 | container->height = container->width = 0; | 340 | container->pending.height = container->pending.width = 0; |
334 | container->height_fraction = container->width_fraction = 0; | 341 | container->height_fraction = container->width_fraction = 0; |
335 | workspace_update_representation(current->workspace); | 342 | workspace_update_representation(current->pending.workspace); |
336 | wrapped = true; | 343 | wrapped = true; |
337 | } else { | 344 | } else { |
338 | // Keep looking for a parallel parent | 345 | // Keep looking for a parallel parent |
339 | current = current->parent; | 346 | current = current->pending.parent; |
340 | } | 347 | } |
341 | continue; | 348 | continue; |
342 | } | 349 | } |
@@ -356,14 +363,14 @@ static bool container_move_in_direction(struct sway_container *container, | |||
356 | container_move_to_container_from_direction(container, | 363 | container_move_to_container_from_direction(container, |
357 | target, move_dir); | 364 | target, move_dir); |
358 | return true; | 365 | return true; |
359 | } else if (!container->parent) { | 366 | } else if (!container->pending.parent) { |
360 | // Container is at workspace level so we move it to the | 367 | // Container is at workspace level so we move it to the |
361 | // next workspace if possible | 368 | // next workspace if possible |
362 | return container_move_to_next_output(container, | 369 | return container_move_to_next_output(container, |
363 | current->workspace->output, move_dir); | 370 | current->pending.workspace->output, move_dir); |
364 | } else { | 371 | } else { |
365 | // Container has escaped its immediate parallel parent | 372 | // Container has escaped its immediate parallel parent |
366 | current = current->parent; | 373 | current = current->pending.parent; |
367 | continue; | 374 | continue; |
368 | } | 375 | } |
369 | } | 376 | } |
@@ -377,31 +384,31 @@ static bool container_move_in_direction(struct sway_container *container, | |||
377 | container_move_to_container_from_direction(container, | 384 | container_move_to_container_from_direction(container, |
378 | target, move_dir); | 385 | target, move_dir); |
379 | return true; | 386 | return true; |
380 | } else if (!wrapped && !container->parent->parent && | 387 | } else if (!wrapped && !container->pending.parent->pending.parent && |
381 | container->parent->children->length == 1) { | 388 | container->pending.parent->pending.children->length == 1) { |
382 | // Treat singleton children as if they are at workspace level like i3 | 389 | // Treat singleton children as if they are at workspace level like i3 |
383 | // https://github.com/i3/i3/blob/1d9160f2d247dbaa83fb62f02fd7041dec767fc2/src/move.c#L367 | 390 | // https://github.com/i3/i3/blob/1d9160f2d247dbaa83fb62f02fd7041dec767fc2/src/move.c#L367 |
384 | return container_move_to_next_output(container, | 391 | return container_move_to_next_output(container, |
385 | ancestor->workspace->output, move_dir); | 392 | ancestor->pending.workspace->output, move_dir); |
386 | } else { | 393 | } else { |
387 | // Container will be promoted | 394 | // Container will be promoted |
388 | struct sway_container *old_parent = container->parent; | 395 | struct sway_container *old_parent = container->pending.parent; |
389 | if (ancestor->parent) { | 396 | if (ancestor->pending.parent) { |
390 | // Container will move in with its parent | 397 | // Container will move in with its parent |
391 | container_insert_child(ancestor->parent, container, | 398 | container_insert_child(ancestor->pending.parent, container, |
392 | index + (offs < 0 ? 0 : 1)); | 399 | index + (offs < 0 ? 0 : 1)); |
393 | } else { | 400 | } else { |
394 | // Container will move to workspace level, | 401 | // Container will move to workspace level, |
395 | // may be re-split by workspace_layout | 402 | // may be re-split by workspace_layout |
396 | workspace_insert_tiling(ancestor->workspace, container, | 403 | workspace_insert_tiling(ancestor->pending.workspace, container, |
397 | index + (offs < 0 ? 0 : 1)); | 404 | index + (offs < 0 ? 0 : 1)); |
398 | } | 405 | } |
399 | ancestor->height = ancestor->width = 0; | 406 | ancestor->pending.height = ancestor->pending.width = 0; |
400 | ancestor->height_fraction = ancestor->width_fraction = 0; | 407 | ancestor->height_fraction = ancestor->width_fraction = 0; |
401 | if (old_parent) { | 408 | if (old_parent) { |
402 | container_reap_empty(old_parent); | 409 | container_reap_empty(old_parent); |
403 | } | 410 | } |
404 | workspace_squash(container->workspace); | 411 | workspace_squash(container->pending.workspace); |
405 | return true; | 412 | return true; |
406 | } | 413 | } |
407 | } | 414 | } |
@@ -427,14 +434,14 @@ static struct cmd_results *cmd_move_container(bool no_auto_back_and_forth, | |||
427 | container = workspace_wrap_children(workspace); | 434 | container = workspace_wrap_children(workspace); |
428 | } | 435 | } |
429 | 436 | ||
430 | if (container->fullscreen_mode == FULLSCREEN_GLOBAL) { | 437 | if (container->pending.fullscreen_mode == FULLSCREEN_GLOBAL) { |
431 | return cmd_results_new(CMD_FAILURE, | 438 | return cmd_results_new(CMD_FAILURE, |
432 | "Can't move fullscreen global container"); | 439 | "Can't move fullscreen global container"); |
433 | } | 440 | } |
434 | 441 | ||
435 | struct sway_seat *seat = config->handler_context.seat; | 442 | struct sway_seat *seat = config->handler_context.seat; |
436 | struct sway_container *old_parent = container->parent; | 443 | struct sway_container *old_parent = container->pending.parent; |
437 | struct sway_workspace *old_ws = container->workspace; | 444 | struct sway_workspace *old_ws = container->pending.workspace; |
438 | struct sway_output *old_output = old_ws ? old_ws->output : NULL; | 445 | struct sway_output *old_output = old_ws ? old_ws->output : NULL; |
439 | struct sway_node *destination = NULL; | 446 | struct sway_node *destination = NULL; |
440 | 447 | ||
@@ -462,7 +469,7 @@ static struct cmd_results *cmd_move_container(bool no_auto_back_and_forth, | |||
462 | if (strcasecmp(argv[1], "number") == 0) { | 469 | if (strcasecmp(argv[1], "number") == 0) { |
463 | // move [window|container] [to] "workspace number x" | 470 | // move [window|container] [to] "workspace number x" |
464 | if (argc < 3) { | 471 | if (argc < 3) { |
465 | return cmd_results_new(CMD_INVALID, expected_syntax); | 472 | return cmd_results_new(CMD_INVALID, "%s", expected_syntax); |
466 | } | 473 | } |
467 | if (!isdigit(argv[2][0])) { | 474 | if (!isdigit(argv[2][0])) { |
468 | return cmd_results_new(CMD_INVALID, | 475 | return cmd_results_new(CMD_INVALID, |
@@ -508,7 +515,7 @@ static struct cmd_results *cmd_move_container(bool no_auto_back_and_forth, | |||
508 | destination = dst ? &dst->node : &ws->node; | 515 | destination = dst ? &dst->node : &ws->node; |
509 | } else if (strcasecmp(argv[0], "output") == 0) { | 516 | } else if (strcasecmp(argv[0], "output") == 0) { |
510 | struct sway_output *new_output = output_in_direction(argv[1], | 517 | struct sway_output *new_output = output_in_direction(argv[1], |
511 | old_output, container->x, container->y); | 518 | old_output, container->pending.x, container->pending.y); |
512 | if (!new_output) { | 519 | if (!new_output) { |
513 | return cmd_results_new(CMD_FAILURE, | 520 | return cmd_results_new(CMD_FAILURE, |
514 | "Can't find output with name/direction '%s'", argv[1]); | 521 | "Can't find output with name/direction '%s'", argv[1]); |
@@ -522,7 +529,7 @@ static struct cmd_results *cmd_move_container(bool no_auto_back_and_forth, | |||
522 | } | 529 | } |
523 | destination = &dest_con->node; | 530 | destination = &dest_con->node; |
524 | } else { | 531 | } else { |
525 | return cmd_results_new(CMD_INVALID, expected_syntax); | 532 | return cmd_results_new(CMD_INVALID, "%s", expected_syntax); |
526 | } | 533 | } |
527 | 534 | ||
528 | if (destination->type == N_CONTAINER && | 535 | if (destination->type == N_CONTAINER && |
@@ -686,6 +693,9 @@ static struct cmd_results *cmd_move_workspace(int argc, char **argv) { | |||
686 | arrange_output(old_output); | 693 | arrange_output(old_output); |
687 | arrange_output(new_output); | 694 | arrange_output(new_output); |
688 | 695 | ||
696 | struct sway_seat *seat = config->handler_context.seat; | ||
697 | seat_consider_warp_to_focus(seat); | ||
698 | |||
689 | return cmd_results_new(CMD_SUCCESS, NULL); | 699 | return cmd_results_new(CMD_SUCCESS, NULL); |
690 | } | 700 | } |
691 | 701 | ||
@@ -706,12 +716,12 @@ static struct cmd_results *cmd_move_in_direction( | |||
706 | "Cannot move workspaces in a direction"); | 716 | "Cannot move workspaces in a direction"); |
707 | } | 717 | } |
708 | if (container_is_floating(container)) { | 718 | if (container_is_floating(container)) { |
709 | if (container->fullscreen_mode) { | 719 | if (container->pending.fullscreen_mode) { |
710 | return cmd_results_new(CMD_FAILURE, | 720 | return cmd_results_new(CMD_FAILURE, |
711 | "Cannot move fullscreen floating container"); | 721 | "Cannot move fullscreen floating container"); |
712 | } | 722 | } |
713 | double lx = container->x; | 723 | double lx = container->pending.x; |
714 | double ly = container->y; | 724 | double ly = container->pending.y; |
715 | switch (direction) { | 725 | switch (direction) { |
716 | case WLR_DIRECTION_LEFT: | 726 | case WLR_DIRECTION_LEFT: |
717 | lx -= move_amt; | 727 | lx -= move_amt; |
@@ -729,8 +739,8 @@ static struct cmd_results *cmd_move_in_direction( | |||
729 | container_floating_move_to(container, lx, ly); | 739 | container_floating_move_to(container, lx, ly); |
730 | return cmd_results_new(CMD_SUCCESS, NULL); | 740 | return cmd_results_new(CMD_SUCCESS, NULL); |
731 | } | 741 | } |
732 | struct sway_workspace *old_ws = container->workspace; | 742 | struct sway_workspace *old_ws = container->pending.workspace; |
733 | struct sway_container *old_parent = container->parent; | 743 | struct sway_container *old_parent = container->pending.parent; |
734 | 744 | ||
735 | if (!container_move_in_direction(container, direction)) { | 745 | if (!container_move_in_direction(container, direction)) { |
736 | // Container didn't move | 746 | // Container didn't move |
@@ -744,7 +754,7 @@ static struct cmd_results *cmd_move_in_direction( | |||
744 | workspace_consider_destroy(old_ws); | 754 | workspace_consider_destroy(old_ws); |
745 | } | 755 | } |
746 | 756 | ||
747 | struct sway_workspace *new_ws = container->workspace; | 757 | struct sway_workspace *new_ws = container->pending.workspace; |
748 | 758 | ||
749 | if (root->fullscreen_global) { | 759 | if (root->fullscreen_global) { |
750 | arrange_root(); | 760 | arrange_root(); |
@@ -759,15 +769,6 @@ static struct cmd_results *cmd_move_in_direction( | |||
759 | ipc_event_window(container, "move"); | 769 | ipc_event_window(container, "move"); |
760 | } | 770 | } |
761 | 771 | ||
762 | // Hack to re-focus container | ||
763 | seat_set_raw_focus(config->handler_context.seat, &new_ws->node); | ||
764 | seat_set_focus_container(config->handler_context.seat, container); | ||
765 | |||
766 | if (old_ws != new_ws) { | ||
767 | ipc_event_workspace(old_ws, new_ws, "focus"); | ||
768 | workspace_detect_urgent(old_ws); | ||
769 | workspace_detect_urgent(new_ws); | ||
770 | } | ||
771 | container_end_mouse_operation(container); | 772 | container_end_mouse_operation(container); |
772 | 773 | ||
773 | return cmd_results_new(CMD_SUCCESS, NULL); | 774 | return cmd_results_new(CMD_SUCCESS, NULL); |
@@ -781,22 +782,22 @@ static struct cmd_results *cmd_move_to_position_pointer( | |||
781 | } | 782 | } |
782 | struct wlr_cursor *cursor = seat->cursor->cursor; | 783 | struct wlr_cursor *cursor = seat->cursor->cursor; |
783 | /* Determine where to put the window. */ | 784 | /* Determine where to put the window. */ |
784 | double lx = cursor->x - container->width / 2; | 785 | double lx = cursor->x - container->pending.width / 2; |
785 | double ly = cursor->y - container->height / 2; | 786 | double ly = cursor->y - container->pending.height / 2; |
786 | 787 | ||
787 | /* Correct target coordinates to be in bounds (on screen). */ | 788 | /* Correct target coordinates to be in bounds (on screen). */ |
788 | struct wlr_output *output = wlr_output_layout_output_at( | 789 | struct wlr_output *output = wlr_output_layout_output_at( |
789 | root->output_layout, cursor->x, cursor->y); | 790 | root->output_layout, cursor->x, cursor->y); |
790 | if (output) { | 791 | if (output) { |
791 | struct wlr_box *box = | 792 | struct wlr_box box; |
792 | wlr_output_layout_get_box(root->output_layout, output); | 793 | wlr_output_layout_get_box(root->output_layout, output, &box); |
793 | lx = fmax(lx, box->x); | 794 | lx = fmax(lx, box.x); |
794 | ly = fmax(ly, box->y); | 795 | ly = fmax(ly, box.y); |
795 | if (lx + container->width > box->x + box->width) { | 796 | if (lx + container->pending.width > box.x + box.width) { |
796 | lx = box->x + box->width - container->width; | 797 | lx = box.x + box.width - container->pending.width; |
797 | } | 798 | } |
798 | if (ly + container->height > box->y + box->height) { | 799 | if (ly + container->pending.height > box.y + box.height) { |
799 | ly = box->y + box->height - container->height; | 800 | ly = box.y + box.height - container->pending.height; |
800 | } | 801 | } |
801 | } | 802 | } |
802 | 803 | ||
@@ -818,7 +819,7 @@ static struct cmd_results *cmd_move_to_position(int argc, char **argv) { | |||
818 | } | 819 | } |
819 | 820 | ||
820 | if (!argc) { | 821 | if (!argc) { |
821 | return cmd_results_new(CMD_INVALID, expected_position_syntax); | 822 | return cmd_results_new(CMD_INVALID, "%s", expected_position_syntax); |
822 | } | 823 | } |
823 | 824 | ||
824 | bool absolute = false; | 825 | bool absolute = false; |
@@ -828,41 +829,41 @@ static struct cmd_results *cmd_move_to_position(int argc, char **argv) { | |||
828 | ++argv; | 829 | ++argv; |
829 | } | 830 | } |
830 | if (!argc) { | 831 | if (!argc) { |
831 | return cmd_results_new(CMD_INVALID, expected_position_syntax); | 832 | return cmd_results_new(CMD_INVALID, "%s", expected_position_syntax); |
832 | } | 833 | } |
833 | if (strcmp(argv[0], "position") == 0) { | 834 | if (strcmp(argv[0], "position") == 0) { |
834 | --argc; | 835 | --argc; |
835 | ++argv; | 836 | ++argv; |
836 | } | 837 | } |
837 | if (!argc) { | 838 | if (!argc) { |
838 | return cmd_results_new(CMD_INVALID, expected_position_syntax); | 839 | return cmd_results_new(CMD_INVALID, "%s", expected_position_syntax); |
839 | } | 840 | } |
840 | if (strcmp(argv[0], "cursor") == 0 || strcmp(argv[0], "mouse") == 0 || | 841 | if (strcmp(argv[0], "cursor") == 0 || strcmp(argv[0], "mouse") == 0 || |
841 | strcmp(argv[0], "pointer") == 0) { | 842 | strcmp(argv[0], "pointer") == 0) { |
842 | if (absolute) { | 843 | if (absolute) { |
843 | return cmd_results_new(CMD_INVALID, expected_position_syntax); | 844 | return cmd_results_new(CMD_INVALID, "%s", expected_position_syntax); |
844 | } | 845 | } |
845 | return cmd_move_to_position_pointer(container); | 846 | return cmd_move_to_position_pointer(container); |
846 | } else if (strcmp(argv[0], "center") == 0) { | 847 | } else if (strcmp(argv[0], "center") == 0) { |
847 | double lx, ly; | 848 | double lx, ly; |
848 | if (absolute) { | 849 | if (absolute) { |
849 | lx = root->x + (root->width - container->width) / 2; | 850 | lx = root->x + (root->width - container->pending.width) / 2; |
850 | ly = root->y + (root->height - container->height) / 2; | 851 | ly = root->y + (root->height - container->pending.height) / 2; |
851 | } else { | 852 | } else { |
852 | struct sway_workspace *ws = container->workspace; | 853 | struct sway_workspace *ws = container->pending.workspace; |
853 | if (!ws) { | 854 | if (!ws) { |
854 | struct sway_seat *seat = config->handler_context.seat; | 855 | struct sway_seat *seat = config->handler_context.seat; |
855 | ws = seat_get_focused_workspace(seat); | 856 | ws = seat_get_focused_workspace(seat); |
856 | } | 857 | } |
857 | lx = ws->x + (ws->width - container->width) / 2; | 858 | lx = ws->x + (ws->width - container->pending.width) / 2; |
858 | ly = ws->y + (ws->height - container->height) / 2; | 859 | ly = ws->y + (ws->height - container->pending.height) / 2; |
859 | } | 860 | } |
860 | container_floating_move_to(container, lx, ly); | 861 | container_floating_move_to(container, lx, ly); |
861 | return cmd_results_new(CMD_SUCCESS, NULL); | 862 | return cmd_results_new(CMD_SUCCESS, NULL); |
862 | } | 863 | } |
863 | 864 | ||
864 | if (argc < 2) { | 865 | if (argc < 2) { |
865 | return cmd_results_new(CMD_FAILURE, expected_position_syntax); | 866 | return cmd_results_new(CMD_FAILURE, "%s", expected_position_syntax); |
866 | } | 867 | } |
867 | 868 | ||
868 | struct movement_amount lx = { .amount = 0, .unit = MOVEMENT_UNIT_INVALID }; | 869 | struct movement_amount lx = { .amount = 0, .unit = MOVEMENT_UNIT_INVALID }; |
@@ -874,19 +875,23 @@ static struct cmd_results *cmd_move_to_position(int argc, char **argv) { | |||
874 | return cmd_results_new(CMD_INVALID, "Invalid x position specified"); | 875 | return cmd_results_new(CMD_INVALID, "Invalid x position specified"); |
875 | } | 876 | } |
876 | 877 | ||
878 | if (argc < 1) { | ||
879 | return cmd_results_new(CMD_FAILURE, "%s", expected_position_syntax); | ||
880 | } | ||
881 | |||
877 | struct movement_amount ly = { .amount = 0, .unit = MOVEMENT_UNIT_INVALID }; | 882 | struct movement_amount ly = { .amount = 0, .unit = MOVEMENT_UNIT_INVALID }; |
878 | // Y direction | 883 | // Y direction |
879 | num_consumed_args = parse_movement_amount(argc, argv, &ly); | 884 | num_consumed_args = parse_movement_amount(argc, argv, &ly); |
880 | argc -= num_consumed_args; | 885 | argc -= num_consumed_args; |
881 | argv += num_consumed_args; | 886 | argv += num_consumed_args; |
882 | if (argc > 0) { | 887 | if (argc > 0) { |
883 | return cmd_results_new(CMD_INVALID, expected_position_syntax); | 888 | return cmd_results_new(CMD_INVALID, "%s", expected_position_syntax); |
884 | } | 889 | } |
885 | if (ly.unit == MOVEMENT_UNIT_INVALID) { | 890 | if (ly.unit == MOVEMENT_UNIT_INVALID) { |
886 | return cmd_results_new(CMD_INVALID, "Invalid y position specified"); | 891 | return cmd_results_new(CMD_INVALID, "Invalid y position specified"); |
887 | } | 892 | } |
888 | 893 | ||
889 | struct sway_workspace *ws = container->workspace; | 894 | struct sway_workspace *ws = container->pending.workspace; |
890 | if (!ws) { | 895 | if (!ws) { |
891 | struct sway_seat *seat = config->handler_context.seat; | 896 | struct sway_seat *seat = config->handler_context.seat; |
892 | ws = seat_get_focused_workspace(seat); | 897 | ws = seat_get_focused_workspace(seat); |
@@ -960,14 +965,14 @@ static struct cmd_results *cmd_move_to_scratchpad(void) { | |||
960 | // If the container is in a floating split container, | 965 | // If the container is in a floating split container, |
961 | // operate on the split container instead of the child. | 966 | // operate on the split container instead of the child. |
962 | if (container_is_floating_or_child(con)) { | 967 | if (container_is_floating_or_child(con)) { |
963 | while (con->parent) { | 968 | while (con->pending.parent) { |
964 | con = con->parent; | 969 | con = con->pending.parent; |
965 | } | 970 | } |
966 | } | 971 | } |
967 | 972 | ||
968 | if (!con->scratchpad) { | 973 | if (!con->scratchpad) { |
969 | root_scratchpad_add_container(con, NULL); | 974 | root_scratchpad_add_container(con, NULL); |
970 | } else if (con->workspace) { | 975 | } else if (con->pending.workspace) { |
971 | root_scratchpad_hide(con); | 976 | root_scratchpad_hide(con); |
972 | } | 977 | } |
973 | return cmd_results_new(CMD_SUCCESS, NULL); | 978 | return cmd_results_new(CMD_SUCCESS, NULL); |
@@ -1026,13 +1031,13 @@ struct cmd_results *cmd_move(int argc, char **argv) { | |||
1026 | } | 1031 | } |
1027 | 1032 | ||
1028 | if (!argc) { | 1033 | if (!argc) { |
1029 | return cmd_results_new(CMD_INVALID, expected_full_syntax); | 1034 | return cmd_results_new(CMD_INVALID, "%s", expected_full_syntax); |
1030 | } | 1035 | } |
1031 | 1036 | ||
1032 | // Only `move [window|container] [to] workspace` supports | 1037 | // Only `move [window|container] [to] workspace` supports |
1033 | // `--no-auto-back-and-forth` so treat others as invalid syntax | 1038 | // `--no-auto-back-and-forth` so treat others as invalid syntax |
1034 | if (no_auto_back_and_forth && strcasecmp(argv[0], "workspace") != 0) { | 1039 | if (no_auto_back_and_forth && strcasecmp(argv[0], "workspace") != 0) { |
1035 | return cmd_results_new(CMD_INVALID, expected_full_syntax); | 1040 | return cmd_results_new(CMD_INVALID, "%s", expected_full_syntax); |
1036 | } | 1041 | } |
1037 | 1042 | ||
1038 | if (strcasecmp(argv[0], "workspace") == 0 || | 1043 | if (strcasecmp(argv[0], "workspace") == 0 || |
@@ -1046,5 +1051,5 @@ struct cmd_results *cmd_move(int argc, char **argv) { | |||
1046 | strcasecmp(argv[1], "position") == 0)) { | 1051 | strcasecmp(argv[1], "position") == 0)) { |
1047 | return cmd_move_to_position(argc, argv); | 1052 | return cmd_move_to_position(argc, argv); |
1048 | } | 1053 | } |
1049 | return cmd_results_new(CMD_INVALID, expected_full_syntax); | 1054 | return cmd_results_new(CMD_INVALID, "%s", expected_full_syntax); |
1050 | } | 1055 | } |
diff --git a/sway/commands/no_focus.c b/sway/commands/no_focus.c index 2001e04f..ccfdec82 100644 --- a/sway/commands/no_focus.c +++ b/sway/commands/no_focus.c | |||
@@ -13,7 +13,7 @@ struct cmd_results *cmd_no_focus(int argc, char **argv) { | |||
13 | char *err_str = NULL; | 13 | char *err_str = NULL; |
14 | struct criteria *criteria = criteria_parse(argv[0], &err_str); | 14 | struct criteria *criteria = criteria_parse(argv[0], &err_str); |
15 | if (!criteria) { | 15 | if (!criteria) { |
16 | error = cmd_results_new(CMD_INVALID, err_str); | 16 | error = cmd_results_new(CMD_INVALID, "%s", err_str); |
17 | free(err_str); | 17 | free(err_str); |
18 | return error; | 18 | return error; |
19 | } | 19 | } |
diff --git a/sway/commands/opacity.c b/sway/commands/opacity.c index 96e6228e..610cecc6 100644 --- a/sway/commands/opacity.c +++ b/sway/commands/opacity.c | |||
@@ -37,6 +37,7 @@ struct cmd_results *cmd_opacity(int argc, char **argv) { | |||
37 | } | 37 | } |
38 | 38 | ||
39 | con->alpha = val; | 39 | con->alpha = val; |
40 | container_damage_whole(con); | 40 | container_update(con); |
41 | |||
41 | return cmd_results_new(CMD_SUCCESS, NULL); | 42 | return cmd_results_new(CMD_SUCCESS, NULL); |
42 | } | 43 | } |
diff --git a/sway/commands/output.c b/sway/commands/output.c index 5186a2ba..5e5d31b3 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 }, |
@@ -24,6 +27,7 @@ static struct cmd_handler output_handlers[] = { | |||
24 | { "subpixel", output_cmd_subpixel }, | 27 | { "subpixel", output_cmd_subpixel }, |
25 | { "toggle", output_cmd_toggle }, | 28 | { "toggle", output_cmd_toggle }, |
26 | { "transform", output_cmd_transform }, | 29 | { "transform", output_cmd_transform }, |
30 | { "unplug", output_cmd_unplug }, | ||
27 | }; | 31 | }; |
28 | 32 | ||
29 | struct cmd_results *cmd_output(int argc, char **argv) { | 33 | struct cmd_results *cmd_output(int argc, char **argv) { |
@@ -32,9 +36,9 @@ struct cmd_results *cmd_output(int argc, char **argv) { | |||
32 | return error; | 36 | return error; |
33 | } | 37 | } |
34 | 38 | ||
35 | // The NOOP-1 output is a dummy output used when there's no outputs | 39 | // The HEADLESS-1 output is a dummy output used when there's no outputs |
36 | // connected. It should never be configured. | 40 | // connected. It should never be configured. |
37 | if (strcasecmp(argv[0], root->noop_output->wlr_output->name) == 0) { | 41 | if (strcasecmp(argv[0], root->fallback_output->wlr_output->name) == 0) { |
38 | return cmd_results_new(CMD_FAILURE, | 42 | return cmd_results_new(CMD_FAILURE, |
39 | "Refusing to configure the no op output"); | 43 | "Refusing to configure the no op output"); |
40 | } | 44 | } |
@@ -51,7 +55,7 @@ struct cmd_results *cmd_output(int argc, char **argv) { | |||
51 | if (!sway_output) { | 55 | if (!sway_output) { |
52 | return cmd_results_new(CMD_FAILURE, "Unknown output"); | 56 | return cmd_results_new(CMD_FAILURE, "Unknown output"); |
53 | } | 57 | } |
54 | if (sway_output == root->noop_output) { | 58 | if (sway_output == root->fallback_output) { |
55 | return cmd_results_new(CMD_FAILURE, | 59 | return cmd_results_new(CMD_FAILURE, |
56 | "Refusing to configure the no op output"); | 60 | "Refusing to configure the no op output"); |
57 | } | 61 | } |
@@ -99,15 +103,18 @@ struct cmd_results *cmd_output(int argc, char **argv) { | |||
99 | 103 | ||
100 | bool background = output->background; | 104 | bool background = output->background; |
101 | 105 | ||
102 | output = store_output_config(output); | 106 | store_output_config(output); |
103 | 107 | ||
104 | // If reloading, the output configs will be applied after reading the | 108 | // If reloading, the output configs will be applied after reading the |
105 | // entire config and before the deferred commands so that an auto generated | 109 | // entire config and before the deferred commands so that an auto generated |
106 | // workspace name is not given to re-enabled outputs. | 110 | // workspace name is not given to re-enabled outputs. |
107 | if (!config->reloading && !config->validating) { | 111 | if (!config->reloading && !config->validating) { |
108 | apply_output_config_to_outputs(output); | 112 | apply_all_output_configs(); |
109 | if (background) { | 113 | if (background) { |
110 | spawn_swaybg(); | 114 | if (!spawn_swaybg()) { |
115 | return cmd_results_new(CMD_FAILURE, | ||
116 | "Failed to apply background configuration"); | ||
117 | } | ||
111 | } | 118 | } |
112 | } | 119 | } |
113 | 120 | ||
diff --git a/sway/commands/output/background.c b/sway/commands/output/background.c index 68ee9fe1..55bd7671 100644 --- a/sway/commands/output/background.c +++ b/sway/commands/output/background.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <libgen.h> | 1 | #include <libgen.h> |
3 | #include <stdio.h> | 2 | #include <stdio.h> |
4 | #include <string.h> | 3 | #include <string.h> |
@@ -102,19 +101,19 @@ struct cmd_results *output_cmd_background(int argc, char **argv) { | |||
102 | } | 101 | } |
103 | 102 | ||
104 | char *conf_path = dirname(conf); | 103 | char *conf_path = dirname(conf); |
105 | char *rel_path = src; | 104 | char *real_src = malloc(strlen(conf_path) + strlen(src) + 2); |
106 | src = malloc(strlen(conf_path) + strlen(src) + 2); | 105 | if (!real_src) { |
107 | if (!src) { | 106 | free(src); |
108 | free(rel_path); | ||
109 | free(conf); | 107 | free(conf); |
110 | sway_log(SWAY_ERROR, "Unable to allocate memory"); | 108 | sway_log(SWAY_ERROR, "Unable to allocate memory"); |
111 | return cmd_results_new(CMD_FAILURE, | 109 | return cmd_results_new(CMD_FAILURE, |
112 | "Unable to allocate resources"); | 110 | "Unable to allocate resources"); |
113 | } | 111 | } |
114 | 112 | ||
115 | sprintf(src, "%s/%s", conf_path, rel_path); | 113 | snprintf(real_src, strlen(conf_path) + strlen(src) + 2, "%s/%s", conf_path, src); |
116 | free(rel_path); | 114 | free(src); |
117 | free(conf); | 115 | free(conf); |
116 | src = real_src; | ||
118 | } | 117 | } |
119 | 118 | ||
120 | bool can_access = access(src, F_OK) != -1; | 119 | bool can_access = access(src, F_OK) != -1; |
@@ -123,7 +122,10 @@ struct cmd_results *output_cmd_background(int argc, char **argv) { | |||
123 | src); | 122 | src); |
124 | config_add_swaynag_warning("Unable to access background file '%s'", | 123 | config_add_swaynag_warning("Unable to access background file '%s'", |
125 | src); | 124 | src); |
125 | struct cmd_results *result = cmd_results_new(CMD_FAILURE, | ||
126 | "unable to access background file '%s'", src); | ||
126 | free(src); | 127 | free(src); |
128 | return result; | ||
127 | } else { | 129 | } else { |
128 | output->background = src; | 130 | output->background = src; |
129 | output->background_option = strdup(mode); | 131 | output->background_option = strdup(mode); |
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/output/toggle.c b/sway/commands/output/toggle.c index 6342d526..c6b72845 100644 --- a/sway/commands/output/toggle.c +++ b/sway/commands/output/toggle.c | |||
@@ -29,7 +29,7 @@ struct cmd_results *output_cmd_toggle(int argc, char **argv) { | |||
29 | config->handler_context.output_config->enabled = 1; | 29 | config->handler_context.output_config->enabled = 1; |
30 | } | 30 | } |
31 | 31 | ||
32 | free(oc); | 32 | free_output_config(oc); |
33 | config->handler_context.leftovers.argc = argc; | 33 | config->handler_context.leftovers.argc = argc; |
34 | config->handler_context.leftovers.argv = argv; | 34 | config->handler_context.leftovers.argv = argv; |
35 | return NULL; | 35 | return NULL; |
diff --git a/sway/commands/output/transform.c b/sway/commands/output/transform.c index f4fcc8c9..8db71bb3 100644 --- a/sway/commands/output/transform.c +++ b/sway/commands/output/transform.c | |||
@@ -1,4 +1,5 @@ | |||
1 | #include <string.h> | 1 | #include <string.h> |
2 | #include <wlr/util/transform.h> | ||
2 | #include "sway/commands.h" | 3 | #include "sway/commands.h" |
3 | #include "sway/config.h" | 4 | #include "sway/config.h" |
4 | #include "log.h" | 5 | #include "log.h" |
diff --git a/sway/commands/output/unplug.c b/sway/commands/output/unplug.c new file mode 100644 index 00000000..dfef626f --- /dev/null +++ b/sway/commands/output/unplug.c | |||
@@ -0,0 +1,54 @@ | |||
1 | #include <strings.h> | ||
2 | #include <wlr/config.h> | ||
3 | #include <wlr/backend/headless.h> | ||
4 | #include <wlr/backend/wayland.h> | ||
5 | #if WLR_HAS_X11_BACKEND | ||
6 | #include <wlr/backend/x11.h> | ||
7 | #endif | ||
8 | #include "sway/commands.h" | ||
9 | #include "sway/config.h" | ||
10 | #include "sway/output.h" | ||
11 | |||
12 | static bool is_backend_allowed(struct wlr_backend *backend) { | ||
13 | if (wlr_backend_is_headless(backend)) { | ||
14 | return true; | ||
15 | } | ||
16 | if (wlr_backend_is_wl(backend)) { | ||
17 | return true; | ||
18 | } | ||
19 | #if WLR_HAS_X11_BACKEND | ||
20 | if (wlr_backend_is_x11(backend)) { | ||
21 | return true; | ||
22 | } | ||
23 | #endif | ||
24 | return false; | ||
25 | } | ||
26 | |||
27 | /** | ||
28 | * This command is intended for developer use only. | ||
29 | */ | ||
30 | struct cmd_results *output_cmd_unplug(int argc, char **argv) { | ||
31 | if (!config->handler_context.output_config) { | ||
32 | return cmd_results_new(CMD_FAILURE, "Missing output config"); | ||
33 | } | ||
34 | |||
35 | const char *oc_name = config->handler_context.output_config->name; | ||
36 | if (strcmp(oc_name, "*") == 0) { | ||
37 | return cmd_results_new(CMD_INVALID, "Won't unplug all outputs"); | ||
38 | } | ||
39 | |||
40 | struct sway_output *sway_output = all_output_by_name_or_id(oc_name); | ||
41 | if (!sway_output) { | ||
42 | return cmd_results_new(CMD_INVALID, | ||
43 | "Cannot unplug unknown output %s", oc_name); | ||
44 | } | ||
45 | |||
46 | if (!is_backend_allowed(sway_output->wlr_output->backend)) { | ||
47 | return cmd_results_new(CMD_INVALID, | ||
48 | "Can only unplug outputs with headless, wayland or x11 backend"); | ||
49 | } | ||
50 | |||
51 | wlr_output_destroy(sway_output->wlr_output); | ||
52 | |||
53 | return cmd_results_new(CMD_SUCCESS, NULL); | ||
54 | } | ||
diff --git a/sway/commands/primary_selection.c b/sway/commands/primary_selection.c new file mode 100644 index 00000000..9e2689c2 --- /dev/null +++ b/sway/commands/primary_selection.c | |||
@@ -0,0 +1,25 @@ | |||
1 | #include <string.h> | ||
2 | #include <strings.h> | ||
3 | #include "sway/config.h" | ||
4 | #include "sway/commands.h" | ||
5 | #include "util.h" | ||
6 | |||
7 | struct cmd_results *cmd_primary_selection(int argc, char **argv) { | ||
8 | struct cmd_results *error = NULL; | ||
9 | if ((error = checkarg(argc, "primary_selection", EXPECTED_EQUAL_TO, 1))) { | ||
10 | return error; | ||
11 | } | ||
12 | |||
13 | bool primary_selection = parse_boolean(argv[0], true); | ||
14 | |||
15 | // config->primary_selection is reset to the previous value on reload in | ||
16 | // load_main_config() | ||
17 | if (config->reloading && config->primary_selection != primary_selection) { | ||
18 | return cmd_results_new(CMD_FAILURE, | ||
19 | "primary_selection can only be enabled/disabled at launch"); | ||
20 | } | ||
21 | |||
22 | config->primary_selection = primary_selection; | ||
23 | |||
24 | return cmd_results_new(CMD_SUCCESS, NULL); | ||
25 | } | ||
diff --git a/sway/commands/reload.c b/sway/commands/reload.c index 3c994d54..6c0aac26 100644 --- a/sway/commands/reload.c +++ b/sway/commands/reload.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <string.h> | 1 | #include <string.h> |
3 | #include "sway/commands.h" | 2 | #include "sway/commands.h" |
4 | #include "sway/config.h" | 3 | #include "sway/config.h" |
@@ -9,9 +8,8 @@ | |||
9 | #include "list.h" | 8 | #include "list.h" |
10 | #include "log.h" | 9 | #include "log.h" |
11 | 10 | ||
12 | static void rebuild_textures_iterator(struct sway_container *con, void *data) { | 11 | static void title_bar_update_iterator(struct sway_container *con, void *data) { |
13 | container_update_marks_textures(con); | 12 | container_update_title_bar(con); |
14 | container_update_title_textures(con); | ||
15 | } | 13 | } |
16 | 14 | ||
17 | static void do_reload(void *data) { | 15 | static void do_reload(void *data) { |
@@ -48,8 +46,7 @@ static void do_reload(void *data) { | |||
48 | } | 46 | } |
49 | list_free_items_and_destroy(bar_ids); | 47 | list_free_items_and_destroy(bar_ids); |
50 | 48 | ||
51 | config_update_font_height(true); | 49 | root_for_each_container(title_bar_update_iterator, NULL); |
52 | root_for_each_container(rebuild_textures_iterator, NULL); | ||
53 | 50 | ||
54 | arrange_root(); | 51 | arrange_root(); |
55 | } | 52 | } |
diff --git a/sway/commands/rename.c b/sway/commands/rename.c index 3b855fdf..0d36cc21 100644 --- a/sway/commands/rename.c +++ b/sway/commands/rename.c | |||
@@ -7,6 +7,7 @@ | |||
7 | #include "sway/config.h" | 7 | #include "sway/config.h" |
8 | #include "sway/ipc-server.h" | 8 | #include "sway/ipc-server.h" |
9 | #include "sway/output.h" | 9 | #include "sway/output.h" |
10 | #include "sway/desktop/launcher.h" | ||
10 | #include "sway/tree/container.h" | 11 | #include "sway/tree/container.h" |
11 | #include "sway/tree/workspace.h" | 12 | #include "sway/tree/workspace.h" |
12 | #include "sway/tree/root.h" | 13 | #include "sway/tree/root.h" |
@@ -25,7 +26,7 @@ struct cmd_results *cmd_rename(int argc, char **argv) { | |||
25 | "Can't run this command while there's no outputs connected."); | 26 | "Can't run this command while there's no outputs connected."); |
26 | } | 27 | } |
27 | if (strcasecmp(argv[0], "workspace") != 0) { | 28 | if (strcasecmp(argv[0], "workspace") != 0) { |
28 | return cmd_results_new(CMD_INVALID, expected_syntax); | 29 | return cmd_results_new(CMD_INVALID, "%s", expected_syntax); |
29 | } | 30 | } |
30 | 31 | ||
31 | int argn = 1; | 32 | int argn = 1; |
@@ -64,7 +65,7 @@ struct cmd_results *cmd_rename(int argc, char **argv) { | |||
64 | ++argn; // move past "to" | 65 | ++argn; // move past "to" |
65 | 66 | ||
66 | if (argn >= argc) { | 67 | if (argn >= argc) { |
67 | return cmd_results_new(CMD_INVALID, expected_syntax); | 68 | return cmd_results_new(CMD_INVALID, "%s", expected_syntax); |
68 | } | 69 | } |
69 | 70 | ||
70 | char *new_name = join_args(argv + argn, argc - argn); | 71 | char *new_name = join_args(argv + argn, argc - argn); |
@@ -91,8 +92,6 @@ struct cmd_results *cmd_rename(int argc, char **argv) { | |||
91 | 92 | ||
92 | sway_log(SWAY_DEBUG, "renaming workspace '%s' to '%s'", workspace->name, new_name); | 93 | sway_log(SWAY_DEBUG, "renaming workspace '%s' to '%s'", workspace->name, new_name); |
93 | 94 | ||
94 | root_rename_pid_workspaces(workspace->name, new_name); | ||
95 | |||
96 | free(workspace->name); | 95 | free(workspace->name); |
97 | workspace->name = new_name; | 96 | workspace->name = new_name; |
98 | 97 | ||
diff --git a/sway/commands/resize.c b/sway/commands/resize.c index ca36e858..32b746ea 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; |
@@ -75,6 +75,10 @@ void container_resize_tiled(struct sway_container *con, | |||
75 | return; | 75 | return; |
76 | } | 76 | } |
77 | 77 | ||
78 | if (container_is_scratchpad_hidden_or_child(con)) { | ||
79 | return; | ||
80 | } | ||
81 | |||
78 | // For HORIZONTAL or VERTICAL, we are growing in two directions so select | 82 | // For HORIZONTAL or VERTICAL, we are growing in two directions so select |
79 | // both adjacent siblings. For RIGHT or DOWN, just select the next sibling. | 83 | // both adjacent siblings. For RIGHT or DOWN, just select the next sibling. |
80 | // For LEFT or UP, convert it to a RIGHT or DOWN resize and reassign con to | 84 | // For LEFT or UP, convert it to a RIGHT or DOWN resize and reassign con to |
@@ -115,13 +119,13 @@ void container_resize_tiled(struct sway_container *con, | |||
115 | int sibling_amount = prev ? ceil((double)amount / 2.0) : amount; | 119 | int sibling_amount = prev ? ceil((double)amount / 2.0) : amount; |
116 | 120 | ||
117 | if (is_horizontal(axis)) { | 121 | if (is_horizontal(axis)) { |
118 | if (con->width + amount < MIN_SANE_W) { | 122 | if (con->pending.width + amount < MIN_SANE_W) { |
119 | return; | 123 | return; |
120 | } | 124 | } |
121 | if (next->width - sibling_amount < MIN_SANE_W) { | 125 | if (next->pending.width - sibling_amount < MIN_SANE_W) { |
122 | return; | 126 | return; |
123 | } | 127 | } |
124 | if (prev && prev->width - sibling_amount < MIN_SANE_W) { | 128 | if (prev && prev->pending.width - sibling_amount < MIN_SANE_W) { |
125 | return; | 129 | return; |
126 | } | 130 | } |
127 | if (con->child_total_width <= 0) { | 131 | if (con->child_total_width <= 0) { |
@@ -133,7 +137,7 @@ void container_resize_tiled(struct sway_container *con, | |||
133 | list_t *siblings = container_get_siblings(con); | 137 | list_t *siblings = container_get_siblings(con); |
134 | for (int i = 0; i < siblings->length; ++i) { | 138 | for (int i = 0; i < siblings->length; ++i) { |
135 | struct sway_container *con = siblings->items[i]; | 139 | struct sway_container *con = siblings->items[i]; |
136 | con->width_fraction = con->width / con->child_total_width; | 140 | con->width_fraction = con->pending.width / con->child_total_width; |
137 | } | 141 | } |
138 | 142 | ||
139 | double amount_fraction = (double)amount / con->child_total_width; | 143 | double amount_fraction = (double)amount / con->child_total_width; |
@@ -146,13 +150,13 @@ void container_resize_tiled(struct sway_container *con, | |||
146 | prev->width_fraction -= sibling_amount_fraction; | 150 | prev->width_fraction -= sibling_amount_fraction; |
147 | } | 151 | } |
148 | } else { | 152 | } else { |
149 | if (con->height + amount < MIN_SANE_H) { | 153 | if (con->pending.height + amount < MIN_SANE_H) { |
150 | return; | 154 | return; |
151 | } | 155 | } |
152 | if (next->height - sibling_amount < MIN_SANE_H) { | 156 | if (next->pending.height - sibling_amount < MIN_SANE_H) { |
153 | return; | 157 | return; |
154 | } | 158 | } |
155 | if (prev && prev->height - sibling_amount < MIN_SANE_H) { | 159 | if (prev && prev->pending.height - sibling_amount < MIN_SANE_H) { |
156 | return; | 160 | return; |
157 | } | 161 | } |
158 | if (con->child_total_height <= 0) { | 162 | if (con->child_total_height <= 0) { |
@@ -164,7 +168,7 @@ void container_resize_tiled(struct sway_container *con, | |||
164 | list_t *siblings = container_get_siblings(con); | 168 | list_t *siblings = container_get_siblings(con); |
165 | for (int i = 0; i < siblings->length; ++i) { | 169 | for (int i = 0; i < siblings->length; ++i) { |
166 | struct sway_container *con = siblings->items[i]; | 170 | struct sway_container *con = siblings->items[i]; |
167 | con->height_fraction = con->height / con->child_total_height; | 171 | con->height_fraction = con->pending.height / con->child_total_height; |
168 | } | 172 | } |
169 | 173 | ||
170 | double amount_fraction = (double)amount / con->child_total_height; | 174 | double amount_fraction = (double)amount / con->child_total_height; |
@@ -178,10 +182,10 @@ void container_resize_tiled(struct sway_container *con, | |||
178 | } | 182 | } |
179 | } | 183 | } |
180 | 184 | ||
181 | if (con->parent) { | 185 | if (con->pending.parent) { |
182 | arrange_container(con->parent); | 186 | arrange_container(con->pending.parent); |
183 | } else { | 187 | } else { |
184 | arrange_workspace(con->workspace); | 188 | arrange_workspace(con->pending.workspace); |
185 | } | 189 | } |
186 | } | 190 | } |
187 | 191 | ||
@@ -203,15 +207,15 @@ static struct cmd_results *resize_adjust_floating(uint32_t axis, | |||
203 | int min_width, max_width, min_height, max_height; | 207 | int min_width, max_width, min_height, max_height; |
204 | floating_calculate_constraints(&min_width, &max_width, | 208 | floating_calculate_constraints(&min_width, &max_width, |
205 | &min_height, &max_height); | 209 | &min_height, &max_height); |
206 | if (con->width + grow_width < min_width) { | 210 | if (con->pending.width + grow_width < min_width) { |
207 | grow_width = min_width - con->width; | 211 | grow_width = min_width - con->pending.width; |
208 | } else if (con->width + grow_width > max_width) { | 212 | } else if (con->pending.width + grow_width > max_width) { |
209 | grow_width = max_width - con->width; | 213 | grow_width = max_width - con->pending.width; |
210 | } | 214 | } |
211 | if (con->height + grow_height < min_height) { | 215 | if (con->pending.height + grow_height < min_height) { |
212 | grow_height = min_height - con->height; | 216 | grow_height = min_height - con->pending.height; |
213 | } else if (con->height + grow_height > max_height) { | 217 | } else if (con->pending.height + grow_height > max_height) { |
214 | grow_height = max_height - con->height; | 218 | grow_height = max_height - con->pending.height; |
215 | } | 219 | } |
216 | int grow_x = 0, grow_y = 0; | 220 | int grow_x = 0, grow_y = 0; |
217 | 221 | ||
@@ -227,15 +231,15 @@ static struct cmd_results *resize_adjust_floating(uint32_t axis, | |||
227 | if (grow_width == 0 && grow_height == 0) { | 231 | if (grow_width == 0 && grow_height == 0) { |
228 | return cmd_results_new(CMD_INVALID, "Cannot resize any further"); | 232 | return cmd_results_new(CMD_INVALID, "Cannot resize any further"); |
229 | } | 233 | } |
230 | con->x += grow_x; | 234 | con->pending.x += grow_x; |
231 | con->y += grow_y; | 235 | con->pending.y += grow_y; |
232 | con->width += grow_width; | 236 | con->pending.width += grow_width; |
233 | con->height += grow_height; | 237 | con->pending.height += grow_height; |
234 | 238 | ||
235 | con->content_x += grow_x; | 239 | con->pending.content_x += grow_x; |
236 | con->content_y += grow_y; | 240 | con->pending.content_y += grow_y; |
237 | con->content_width += grow_width; | 241 | con->pending.content_width += grow_width; |
238 | con->content_height += grow_height; | 242 | con->pending.content_height += grow_height; |
239 | 243 | ||
240 | arrange_container(con); | 244 | arrange_container(con); |
241 | 245 | ||
@@ -249,16 +253,35 @@ static struct cmd_results *resize_adjust_tiled(uint32_t axis, | |||
249 | struct movement_amount *amount) { | 253 | struct movement_amount *amount) { |
250 | struct sway_container *current = config->handler_context.container; | 254 | struct sway_container *current = config->handler_context.container; |
251 | 255 | ||
256 | if (container_is_scratchpad_hidden_or_child(current)) { | ||
257 | return cmd_results_new(CMD_FAILURE, "Cannot resize a hidden scratchpad container"); | ||
258 | } | ||
259 | |||
252 | if (amount->unit == MOVEMENT_UNIT_DEFAULT) { | 260 | if (amount->unit == MOVEMENT_UNIT_DEFAULT) { |
253 | amount->unit = MOVEMENT_UNIT_PPT; | 261 | amount->unit = MOVEMENT_UNIT_PPT; |
254 | } | 262 | } |
255 | if (amount->unit == MOVEMENT_UNIT_PPT) { | 263 | if (amount->unit == MOVEMENT_UNIT_PPT) { |
264 | struct sway_container *parent = current->pending.parent; | ||
256 | float pct = amount->amount / 100.0f; | 265 | float pct = amount->amount / 100.0f; |
257 | 266 | ||
258 | if (is_horizontal(axis)) { | 267 | if (is_horizontal(axis)) { |
259 | amount->amount = (float)current->width * pct; | 268 | while (parent && parent->pending.layout != L_HORIZ) { |
269 | parent = parent->pending.parent; | ||
270 | } | ||
271 | if (parent) { | ||
272 | amount->amount = (float)parent->pending.width * pct; | ||
273 | } else { | ||
274 | amount->amount = (float)current->pending.workspace->width * pct; | ||
275 | } | ||
260 | } else { | 276 | } else { |
261 | amount->amount = (float)current->height * pct; | 277 | while (parent && parent->pending.layout != L_VERT) { |
278 | parent = parent->pending.parent; | ||
279 | } | ||
280 | if (parent) { | ||
281 | amount->amount = (float)parent->pending.height * pct; | ||
282 | } else { | ||
283 | amount->amount = (float)current->pending.workspace->height * pct; | ||
284 | } | ||
262 | } | 285 | } |
263 | } | 286 | } |
264 | 287 | ||
@@ -277,24 +300,29 @@ static struct cmd_results *resize_adjust_tiled(uint32_t axis, | |||
277 | */ | 300 | */ |
278 | static struct cmd_results *resize_set_tiled(struct sway_container *con, | 301 | static struct cmd_results *resize_set_tiled(struct sway_container *con, |
279 | struct movement_amount *width, struct movement_amount *height) { | 302 | struct movement_amount *width, struct movement_amount *height) { |
303 | |||
304 | if (container_is_scratchpad_hidden_or_child(con)) { | ||
305 | return cmd_results_new(CMD_FAILURE, "Cannot resize a hidden scratchpad container"); | ||
306 | } | ||
307 | |||
280 | if (width->amount) { | 308 | if (width->amount) { |
281 | if (width->unit == MOVEMENT_UNIT_PPT || | 309 | if (width->unit == MOVEMENT_UNIT_PPT || |
282 | width->unit == MOVEMENT_UNIT_DEFAULT) { | 310 | width->unit == MOVEMENT_UNIT_DEFAULT) { |
283 | // Convert to px | 311 | // Convert to px |
284 | struct sway_container *parent = con->parent; | 312 | struct sway_container *parent = con->pending.parent; |
285 | while (parent && parent->layout != L_HORIZ) { | 313 | while (parent && parent->pending.layout != L_HORIZ) { |
286 | parent = parent->parent; | 314 | parent = parent->pending.parent; |
287 | } | 315 | } |
288 | if (parent) { | 316 | if (parent) { |
289 | width->amount = parent->width * width->amount / 100; | 317 | width->amount = parent->pending.width * width->amount / 100; |
290 | } else { | 318 | } else { |
291 | width->amount = con->workspace->width * width->amount / 100; | 319 | width->amount = con->pending.workspace->width * width->amount / 100; |
292 | } | 320 | } |
293 | width->unit = MOVEMENT_UNIT_PX; | 321 | width->unit = MOVEMENT_UNIT_PX; |
294 | } | 322 | } |
295 | if (width->unit == MOVEMENT_UNIT_PX) { | 323 | if (width->unit == MOVEMENT_UNIT_PX) { |
296 | container_resize_tiled(con, AXIS_HORIZONTAL, | 324 | container_resize_tiled(con, AXIS_HORIZONTAL, |
297 | width->amount - con->width); | 325 | width->amount - con->pending.width); |
298 | } | 326 | } |
299 | } | 327 | } |
300 | 328 | ||
@@ -302,20 +330,20 @@ static struct cmd_results *resize_set_tiled(struct sway_container *con, | |||
302 | if (height->unit == MOVEMENT_UNIT_PPT || | 330 | if (height->unit == MOVEMENT_UNIT_PPT || |
303 | height->unit == MOVEMENT_UNIT_DEFAULT) { | 331 | height->unit == MOVEMENT_UNIT_DEFAULT) { |
304 | // Convert to px | 332 | // Convert to px |
305 | struct sway_container *parent = con->parent; | 333 | struct sway_container *parent = con->pending.parent; |
306 | while (parent && parent->layout != L_VERT) { | 334 | while (parent && parent->pending.layout != L_VERT) { |
307 | parent = parent->parent; | 335 | parent = parent->pending.parent; |
308 | } | 336 | } |
309 | if (parent) { | 337 | if (parent) { |
310 | height->amount = parent->height * height->amount / 100; | 338 | height->amount = parent->pending.height * height->amount / 100; |
311 | } else { | 339 | } else { |
312 | height->amount = con->workspace->height * height->amount / 100; | 340 | height->amount = con->pending.workspace->height * height->amount / 100; |
313 | } | 341 | } |
314 | height->unit = MOVEMENT_UNIT_PX; | 342 | height->unit = MOVEMENT_UNIT_PX; |
315 | } | 343 | } |
316 | if (height->unit == MOVEMENT_UNIT_PX) { | 344 | if (height->unit == MOVEMENT_UNIT_PX) { |
317 | container_resize_tiled(con, AXIS_VERTICAL, | 345 | container_resize_tiled(con, AXIS_VERTICAL, |
318 | height->amount - con->height); | 346 | height->amount - con->pending.height); |
319 | } | 347 | } |
320 | } | 348 | } |
321 | 349 | ||
@@ -339,15 +367,15 @@ static struct cmd_results *resize_set_floating(struct sway_container *con, | |||
339 | "Cannot resize a hidden scratchpad container by ppt"); | 367 | "Cannot resize a hidden scratchpad container by ppt"); |
340 | } | 368 | } |
341 | // Convert to px | 369 | // Convert to px |
342 | width->amount = con->workspace->width * width->amount / 100; | 370 | width->amount = con->pending.workspace->width * width->amount / 100; |
343 | width->unit = MOVEMENT_UNIT_PX; | 371 | width->unit = MOVEMENT_UNIT_PX; |
344 | // Falls through | 372 | // Falls through |
345 | case MOVEMENT_UNIT_PX: | 373 | case MOVEMENT_UNIT_PX: |
346 | case MOVEMENT_UNIT_DEFAULT: | 374 | case MOVEMENT_UNIT_DEFAULT: |
347 | width->amount = fmax(min_width, fmin(width->amount, max_width)); | 375 | width->amount = fmax(min_width, fmin(width->amount, max_width)); |
348 | grow_width = width->amount - con->width; | 376 | grow_width = width->amount - con->pending.width; |
349 | con->x -= grow_width / 2; | 377 | con->pending.x -= grow_width / 2; |
350 | con->width = width->amount; | 378 | con->pending.width = width->amount; |
351 | break; | 379 | break; |
352 | case MOVEMENT_UNIT_INVALID: | 380 | case MOVEMENT_UNIT_INVALID: |
353 | sway_assert(false, "invalid width unit"); | 381 | sway_assert(false, "invalid width unit"); |
@@ -363,15 +391,15 @@ static struct cmd_results *resize_set_floating(struct sway_container *con, | |||
363 | "Cannot resize a hidden scratchpad container by ppt"); | 391 | "Cannot resize a hidden scratchpad container by ppt"); |
364 | } | 392 | } |
365 | // Convert to px | 393 | // Convert to px |
366 | height->amount = con->workspace->height * height->amount / 100; | 394 | height->amount = con->pending.workspace->height * height->amount / 100; |
367 | height->unit = MOVEMENT_UNIT_PX; | 395 | height->unit = MOVEMENT_UNIT_PX; |
368 | // Falls through | 396 | // Falls through |
369 | case MOVEMENT_UNIT_PX: | 397 | case MOVEMENT_UNIT_PX: |
370 | case MOVEMENT_UNIT_DEFAULT: | 398 | case MOVEMENT_UNIT_DEFAULT: |
371 | height->amount = fmax(min_height, fmin(height->amount, max_height)); | 399 | height->amount = fmax(min_height, fmin(height->amount, max_height)); |
372 | grow_height = height->amount - con->height; | 400 | grow_height = height->amount - con->pending.height; |
373 | con->y -= grow_height / 2; | 401 | con->pending.y -= grow_height / 2; |
374 | con->height = height->amount; | 402 | con->pending.height = height->amount; |
375 | break; | 403 | break; |
376 | case MOVEMENT_UNIT_INVALID: | 404 | case MOVEMENT_UNIT_INVALID: |
377 | sway_assert(false, "invalid height unit"); | 405 | sway_assert(false, "invalid height unit"); |
@@ -379,10 +407,10 @@ static struct cmd_results *resize_set_floating(struct sway_container *con, | |||
379 | } | 407 | } |
380 | } | 408 | } |
381 | 409 | ||
382 | con->content_x -= grow_width / 2; | 410 | con->pending.content_x -= grow_width / 2; |
383 | con->content_y -= grow_height / 2; | 411 | con->pending.content_y -= grow_height / 2; |
384 | con->content_width += grow_width; | 412 | con->pending.content_width += grow_width; |
385 | con->content_height += grow_height; | 413 | con->pending.content_height += grow_height; |
386 | 414 | ||
387 | arrange_container(con); | 415 | arrange_container(con); |
388 | 416 | ||
@@ -415,7 +443,7 @@ static struct cmd_results *cmd_resize_set(int argc, char **argv) { | |||
415 | argc -= num_consumed_args; | 443 | argc -= num_consumed_args; |
416 | argv += num_consumed_args; | 444 | argv += num_consumed_args; |
417 | if (width.unit == MOVEMENT_UNIT_INVALID) { | 445 | if (width.unit == MOVEMENT_UNIT_INVALID) { |
418 | return cmd_results_new(CMD_INVALID, usage); | 446 | return cmd_results_new(CMD_INVALID, "%s", usage); |
419 | } | 447 | } |
420 | } | 448 | } |
421 | 449 | ||
@@ -427,20 +455,20 @@ static struct cmd_results *cmd_resize_set(int argc, char **argv) { | |||
427 | } | 455 | } |
428 | int num_consumed_args = parse_movement_amount(argc, argv, &height); | 456 | int num_consumed_args = parse_movement_amount(argc, argv, &height); |
429 | if (argc > num_consumed_args) { | 457 | if (argc > num_consumed_args) { |
430 | return cmd_results_new(CMD_INVALID, usage); | 458 | return cmd_results_new(CMD_INVALID, "%s", usage); |
431 | } | 459 | } |
432 | if (width.unit == MOVEMENT_UNIT_INVALID) { | 460 | if (width.unit == MOVEMENT_UNIT_INVALID) { |
433 | return cmd_results_new(CMD_INVALID, usage); | 461 | return cmd_results_new(CMD_INVALID, "%s", usage); |
434 | } | 462 | } |
435 | } | 463 | } |
436 | 464 | ||
437 | // If 0, don't resize that dimension | 465 | // If 0, don't resize that dimension |
438 | struct sway_container *con = config->handler_context.container; | 466 | struct sway_container *con = config->handler_context.container; |
439 | if (width.amount <= 0) { | 467 | if (width.amount <= 0) { |
440 | width.amount = con->width; | 468 | width.amount = con->pending.width; |
441 | } | 469 | } |
442 | if (height.amount <= 0) { | 470 | if (height.amount <= 0) { |
443 | height.amount = con->height; | 471 | height.amount = con->pending.height; |
444 | } | 472 | } |
445 | 473 | ||
446 | if (container_is_floating(con)) { | 474 | if (container_is_floating(con)) { |
@@ -462,7 +490,7 @@ static struct cmd_results *cmd_resize_adjust(int argc, char **argv, | |||
462 | "[<amount> px|ppt [or <amount> px|ppt]]'"; | 490 | "[<amount> px|ppt [or <amount> px|ppt]]'"; |
463 | uint32_t axis = parse_resize_axis(*argv); | 491 | uint32_t axis = parse_resize_axis(*argv); |
464 | if (axis == WLR_EDGE_NONE) { | 492 | if (axis == WLR_EDGE_NONE) { |
465 | return cmd_results_new(CMD_INVALID, usage); | 493 | return cmd_results_new(CMD_INVALID, "%s", usage); |
466 | } | 494 | } |
467 | --argc; ++argv; | 495 | --argc; ++argv; |
468 | 496 | ||
@@ -473,7 +501,7 @@ static struct cmd_results *cmd_resize_adjust(int argc, char **argv, | |||
473 | argc -= num_consumed_args; | 501 | argc -= num_consumed_args; |
474 | argv += num_consumed_args; | 502 | argv += num_consumed_args; |
475 | if (first_amount.unit == MOVEMENT_UNIT_INVALID) { | 503 | if (first_amount.unit == MOVEMENT_UNIT_INVALID) { |
476 | return cmd_results_new(CMD_INVALID, usage); | 504 | return cmd_results_new(CMD_INVALID, "%s", usage); |
477 | } | 505 | } |
478 | } else { | 506 | } else { |
479 | first_amount.amount = 10; | 507 | first_amount.amount = 10; |
@@ -483,7 +511,7 @@ static struct cmd_results *cmd_resize_adjust(int argc, char **argv, | |||
483 | // "or" | 511 | // "or" |
484 | if (argc) { | 512 | if (argc) { |
485 | if (strcmp(*argv, "or") != 0) { | 513 | if (strcmp(*argv, "or") != 0) { |
486 | return cmd_results_new(CMD_INVALID, usage); | 514 | return cmd_results_new(CMD_INVALID, "%s", usage); |
487 | } | 515 | } |
488 | --argc; ++argv; | 516 | --argc; ++argv; |
489 | } | 517 | } |
@@ -493,10 +521,10 @@ static struct cmd_results *cmd_resize_adjust(int argc, char **argv, | |||
493 | if (argc) { | 521 | if (argc) { |
494 | int num_consumed_args = parse_movement_amount(argc, argv, &second_amount); | 522 | int num_consumed_args = parse_movement_amount(argc, argv, &second_amount); |
495 | if (argc > num_consumed_args) { | 523 | if (argc > num_consumed_args) { |
496 | return cmd_results_new(CMD_INVALID, usage); | 524 | return cmd_results_new(CMD_INVALID, "%s", usage); |
497 | } | 525 | } |
498 | if (second_amount.unit == MOVEMENT_UNIT_INVALID) { | 526 | if (second_amount.unit == MOVEMENT_UNIT_INVALID) { |
499 | return cmd_results_new(CMD_INVALID, usage); | 527 | return cmd_results_new(CMD_INVALID, "%s", usage); |
500 | } | 528 | } |
501 | } else { | 529 | } else { |
502 | second_amount.amount = 0; | 530 | second_amount.amount = 0; |
@@ -566,5 +594,5 @@ struct cmd_results *cmd_resize(int argc, char **argv) { | |||
566 | const char usage[] = "Expected 'resize <shrink|grow> " | 594 | const char usage[] = "Expected 'resize <shrink|grow> " |
567 | "<width|height|up|down|left|right> [<amount>] [px|ppt]'"; | 595 | "<width|height|up|down|left|right> [<amount>] [px|ppt]'"; |
568 | 596 | ||
569 | return cmd_results_new(CMD_INVALID, usage); | 597 | return cmd_results_new(CMD_INVALID, "%s", usage); |
570 | } | 598 | } |
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..47d18546 100644 --- a/sway/commands/seat/attach.c +++ b/sway/commands/seat/attach.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <string.h> | 1 | #include <string.h> |
3 | #include "sway/commands.h" | 2 | #include "sway/commands.h" |
4 | #include "sway/config.h" | 3 | #include "sway/config.h" |
@@ -12,7 +11,7 @@ struct cmd_results *seat_cmd_attach(int argc, char **argv) { | |||
12 | if (!config->handler_context.seat_config) { | 11 | if (!config->handler_context.seat_config) { |
13 | return cmd_results_new(CMD_FAILURE, "No seat defined"); | 12 | return cmd_results_new(CMD_FAILURE, "No seat defined"); |
14 | } | 13 | } |
15 | if (config->reading) { | 14 | if (!config->active) { |
16 | return cmd_results_new(CMD_DEFER, NULL); | 15 | return cmd_results_new(CMD_DEFER, NULL); |
17 | } | 16 | } |
18 | 17 | ||
diff --git a/sway/commands/seat/cursor.c b/sway/commands/seat/cursor.c index 749235eb..df7c379d 100644 --- a/sway/commands/seat/cursor.c +++ b/sway/commands/seat/cursor.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <linux/input-event-codes.h> | 1 | #include <linux/input-event-codes.h> |
3 | 2 | ||
4 | #include <strings.h> | 3 | #include <strings.h> |
@@ -18,7 +17,7 @@ static struct cmd_results *handle_command(struct sway_cursor *cursor, | |||
18 | int argc, char **argv) { | 17 | int argc, char **argv) { |
19 | if (strcasecmp(argv[0], "move") == 0) { | 18 | if (strcasecmp(argv[0], "move") == 0) { |
20 | if (argc < 3) { | 19 | if (argc < 3) { |
21 | return cmd_results_new(CMD_INVALID, expected_syntax); | 20 | return cmd_results_new(CMD_INVALID, "%s", expected_syntax); |
22 | } | 21 | } |
23 | int delta_x = strtol(argv[1], NULL, 10); | 22 | int delta_x = strtol(argv[1], NULL, 10); |
24 | int delta_y = strtol(argv[2], NULL, 10); | 23 | int delta_y = strtol(argv[2], NULL, 10); |
@@ -27,7 +26,7 @@ static struct cmd_results *handle_command(struct sway_cursor *cursor, | |||
27 | wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat); | 26 | wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat); |
28 | } else if (strcasecmp(argv[0], "set") == 0) { | 27 | } else if (strcasecmp(argv[0], "set") == 0) { |
29 | if (argc < 3) { | 28 | if (argc < 3) { |
30 | return cmd_results_new(CMD_INVALID, expected_syntax); | 29 | return cmd_results_new(CMD_INVALID, "%s", expected_syntax); |
31 | } | 30 | } |
32 | // map absolute coords (0..1,0..1) to root container coords | 31 | // map absolute coords (0..1,0..1) to root container coords |
33 | float x = strtof(argv[1], NULL) / root->width; | 32 | float x = strtof(argv[1], NULL) / root->width; |
@@ -37,7 +36,7 @@ static struct cmd_results *handle_command(struct sway_cursor *cursor, | |||
37 | wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat); | 36 | wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat); |
38 | } else { | 37 | } else { |
39 | if (argc < 2) { | 38 | if (argc < 2) { |
40 | return cmd_results_new(CMD_INVALID, expected_syntax); | 39 | return cmd_results_new(CMD_INVALID, "%s", expected_syntax); |
41 | } | 40 | } |
42 | struct cmd_results *error = NULL; | 41 | struct cmd_results *error = NULL; |
43 | if ((error = press_or_release(cursor, argv[0], argv[1]))) { | 42 | if ((error = press_or_release(cursor, argv[0], argv[1]))) { |
@@ -85,36 +84,36 @@ struct cmd_results *seat_cmd_cursor(int argc, char **argv) { | |||
85 | 84 | ||
86 | static struct cmd_results *press_or_release(struct sway_cursor *cursor, | 85 | static struct cmd_results *press_or_release(struct sway_cursor *cursor, |
87 | char *action, char *button_str) { | 86 | char *action, char *button_str) { |
88 | enum wlr_button_state state; | 87 | enum wl_pointer_button_state state; |
89 | uint32_t button; | 88 | uint32_t button; |
90 | if (strcasecmp(action, "press") == 0) { | 89 | if (strcasecmp(action, "press") == 0) { |
91 | state = WLR_BUTTON_PRESSED; | 90 | state = WL_POINTER_BUTTON_STATE_PRESSED; |
92 | } else if (strcasecmp(action, "release") == 0) { | 91 | } else if (strcasecmp(action, "release") == 0) { |
93 | state = WLR_BUTTON_RELEASED; | 92 | state = WL_POINTER_BUTTON_STATE_RELEASED; |
94 | } else { | 93 | } else { |
95 | return cmd_results_new(CMD_INVALID, expected_syntax); | 94 | return cmd_results_new(CMD_INVALID, "%s", expected_syntax); |
96 | } | 95 | } |
97 | 96 | ||
98 | char *message = NULL; | 97 | char *message = NULL; |
99 | button = get_mouse_button(button_str, &message); | 98 | button = get_mouse_button(button_str, &message); |
100 | if (message) { | 99 | if (message) { |
101 | struct cmd_results *error = | 100 | struct cmd_results *error = |
102 | cmd_results_new(CMD_INVALID, message); | 101 | cmd_results_new(CMD_INVALID, "%s", message); |
103 | free(message); | 102 | free(message); |
104 | return error; | 103 | return error; |
105 | } else if (button == SWAY_SCROLL_UP || button == SWAY_SCROLL_DOWN | 104 | } else if (button == SWAY_SCROLL_UP || button == SWAY_SCROLL_DOWN |
106 | || button == SWAY_SCROLL_LEFT || button == SWAY_SCROLL_RIGHT) { | 105 | || button == SWAY_SCROLL_LEFT || button == SWAY_SCROLL_RIGHT) { |
107 | // Dispatch axis event | 106 | // Dispatch axis event |
108 | enum wlr_axis_orientation orientation = | 107 | enum wl_pointer_axis orientation = |
109 | (button == SWAY_SCROLL_UP || button == SWAY_SCROLL_DOWN) | 108 | (button == SWAY_SCROLL_UP || button == SWAY_SCROLL_DOWN) |
110 | ? WLR_AXIS_ORIENTATION_VERTICAL | 109 | ? WL_POINTER_AXIS_VERTICAL_SCROLL |
111 | : WLR_AXIS_ORIENTATION_HORIZONTAL; | 110 | : WL_POINTER_AXIS_HORIZONTAL_SCROLL; |
112 | double delta = (button == SWAY_SCROLL_UP || button == SWAY_SCROLL_LEFT) | 111 | double delta = (button == SWAY_SCROLL_UP || button == SWAY_SCROLL_LEFT) |
113 | ? -1 : 1; | 112 | ? -1 : 1; |
114 | struct wlr_event_pointer_axis event = { | 113 | struct wlr_pointer_axis_event event = { |
115 | .device = NULL, | 114 | .pointer = NULL, |
116 | .time_msec = 0, | 115 | .time_msec = 0, |
117 | .source = WLR_AXIS_SOURCE_WHEEL, | 116 | .source = WL_POINTER_AXIS_SOURCE_WHEEL, |
118 | .orientation = orientation, | 117 | .orientation = orientation, |
119 | .delta = delta * 15, | 118 | .delta = delta * 15, |
120 | .delta_discrete = delta | 119 | .delta_discrete = delta |
diff --git a/sway/commands/seat/hide_cursor.c b/sway/commands/seat/hide_cursor.c index e09b82d9..f5177a47 100644 --- a/sway/commands/seat/hide_cursor.c +++ b/sway/commands/seat/hide_cursor.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <string.h> | 1 | #include <string.h> |
3 | #include "sway/commands.h" | 2 | #include "sway/commands.h" |
4 | #include "sway/config.h" | 3 | #include "sway/config.h" |
diff --git a/sway/commands/seat/idle.c b/sway/commands/seat/idle.c index 82428f2c..2974453e 100644 --- a/sway/commands/seat/idle.c +++ b/sway/commands/seat/idle.c | |||
@@ -1,8 +1,8 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <limits.h> | 1 | #include <limits.h> |
3 | #include <string.h> | 2 | #include <string.h> |
4 | #include <strings.h> | 3 | #include <strings.h> |
5 | #include <stdint.h> | 4 | #include <stdint.h> |
5 | #include "log.h" | ||
6 | #include "sway/commands.h" | 6 | #include "sway/commands.h" |
7 | #include "sway/config.h" | 7 | #include "sway/config.h" |
8 | #include "sway/input/seat.h" | 8 | #include "sway/input/seat.h" |
@@ -69,5 +69,10 @@ struct cmd_results *seat_cmd_idle_wake(int argc, char **argv) { | |||
69 | return cmd_results_new(CMD_FAILURE, "Invalid idle source"); | 69 | return cmd_results_new(CMD_FAILURE, "Invalid idle source"); |
70 | } | 70 | } |
71 | config->handler_context.seat_config->idle_wake_sources = sources; | 71 | config->handler_context.seat_config->idle_wake_sources = sources; |
72 | sway_log(SWAY_INFO, "Warning: seat idle_wake is deprecated"); | ||
73 | if (config->reading) { | ||
74 | config_add_swaynag_warning("seat idle_wake is deprecated. " | ||
75 | "Only seat idle_inhibit is supported."); | ||
76 | } | ||
72 | return cmd_results_new(CMD_SUCCESS, NULL); | 77 | return cmd_results_new(CMD_SUCCESS, NULL); |
73 | } | 78 | } |
diff --git a/sway/commands/seat/xcursor_theme.c b/sway/commands/seat/xcursor_theme.c index 202f35b9..61322a57 100644 --- a/sway/commands/seat/xcursor_theme.c +++ b/sway/commands/seat/xcursor_theme.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <string.h> | 1 | #include <string.h> |
3 | #include "sway/commands.h" | 2 | #include "sway/commands.h" |
4 | #include "sway/config.h" | 3 | #include "sway/config.h" |
diff --git a/sway/commands/set.c b/sway/commands/set.c index c539e9fc..ba384c7c 100644 --- a/sway/commands/set.c +++ b/sway/commands/set.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <stdio.h> | 1 | #include <stdio.h> |
3 | #include <string.h> | 2 | #include <string.h> |
4 | #include <strings.h> | 3 | #include <strings.h> |
diff --git a/sway/commands/show_marks.c b/sway/commands/show_marks.c index 0d373b80..60cef9fa 100644 --- a/sway/commands/show_marks.c +++ b/sway/commands/show_marks.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <string.h> | 1 | #include <string.h> |
3 | #include "sway/commands.h" | 2 | #include "sway/commands.h" |
4 | #include "sway/config.h" | 3 | #include "sway/config.h" |
@@ -10,8 +9,8 @@ | |||
10 | #include "stringop.h" | 9 | #include "stringop.h" |
11 | #include "util.h" | 10 | #include "util.h" |
12 | 11 | ||
13 | static void rebuild_marks_iterator(struct sway_container *con, void *data) { | 12 | static void title_bar_update_iterator(struct sway_container *con, void *data) { |
14 | container_update_marks_textures(con); | 13 | container_update_marks(con); |
15 | } | 14 | } |
16 | 15 | ||
17 | struct cmd_results *cmd_show_marks(int argc, char **argv) { | 16 | struct cmd_results *cmd_show_marks(int argc, char **argv) { |
@@ -23,12 +22,7 @@ struct cmd_results *cmd_show_marks(int argc, char **argv) { | |||
23 | config->show_marks = parse_boolean(argv[0], config->show_marks); | 22 | config->show_marks = parse_boolean(argv[0], config->show_marks); |
24 | 23 | ||
25 | if (config->show_marks) { | 24 | if (config->show_marks) { |
26 | root_for_each_container(rebuild_marks_iterator, NULL); | 25 | root_for_each_container(title_bar_update_iterator, NULL); |
27 | } | ||
28 | |||
29 | for (int i = 0; i < root->outputs->length; ++i) { | ||
30 | struct sway_output *output = root->outputs->items[i]; | ||
31 | output_damage_whole(output); | ||
32 | } | 26 | } |
33 | 27 | ||
34 | return cmd_results_new(CMD_SUCCESS, NULL); | 28 | return cmd_results_new(CMD_SUCCESS, NULL); |
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..500a497d 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(void) { | ||
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..c0b0d0b9 100644 --- a/sway/commands/swap.c +++ b/sway/commands/swap.c | |||
@@ -1,10 +1,10 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <strings.h> | 1 | #include <strings.h> |
3 | #include "config.h" | 2 | #include "config.h" |
4 | #include "log.h" | 3 | #include "log.h" |
5 | #include "sway/commands.h" | 4 | #include "sway/commands.h" |
6 | #include "sway/output.h" | 5 | #include "sway/output.h" |
7 | #include "sway/tree/arrange.h" | 6 | #include "sway/tree/arrange.h" |
7 | #include "sway/tree/container.h" | ||
8 | #include "sway/tree/root.h" | 8 | #include "sway/tree/root.h" |
9 | #include "sway/tree/view.h" | 9 | #include "sway/tree/view.h" |
10 | #include "sway/tree/workspace.h" | 10 | #include "sway/tree/workspace.h" |
@@ -13,186 +13,12 @@ | |||
13 | static const char expected_syntax[] = | 13 | static const char expected_syntax[] = |
14 | "Expected 'swap container with id|con_id|mark <arg>'"; | 14 | "Expected 'swap container with id|con_id|mark <arg>'"; |
15 | 15 | ||
16 | static void swap_places(struct sway_container *con1, | ||
17 | struct sway_container *con2) { | ||
18 | struct sway_container *temp = malloc(sizeof(struct sway_container)); | ||
19 | temp->x = con1->x; | ||
20 | temp->y = con1->y; | ||
21 | temp->width = con1->width; | ||
22 | temp->height = con1->height; | ||
23 | temp->width_fraction = con1->width_fraction; | ||
24 | temp->height_fraction = con1->height_fraction; | ||
25 | temp->parent = con1->parent; | ||
26 | temp->workspace = con1->workspace; | ||
27 | bool temp_floating = container_is_floating(con1); | ||
28 | |||
29 | con1->x = con2->x; | ||
30 | con1->y = con2->y; | ||
31 | con1->width = con2->width; | ||
32 | con1->height = con2->height; | ||
33 | con1->width_fraction = con2->width_fraction; | ||
34 | con1->height_fraction = con2->height_fraction; | ||
35 | |||
36 | con2->x = temp->x; | ||
37 | con2->y = temp->y; | ||
38 | con2->width = temp->width; | ||
39 | con2->height = temp->height; | ||
40 | con2->width_fraction = temp->width_fraction; | ||
41 | con2->height_fraction = temp->height_fraction; | ||
42 | |||
43 | int temp_index = container_sibling_index(con1); | ||
44 | if (con2->parent) { | ||
45 | container_insert_child(con2->parent, con1, | ||
46 | container_sibling_index(con2)); | ||
47 | } else if (container_is_floating(con2)) { | ||
48 | workspace_add_floating(con2->workspace, con1); | ||
49 | } else { | ||
50 | workspace_insert_tiling(con2->workspace, con1, | ||
51 | container_sibling_index(con2)); | ||
52 | } | ||
53 | if (temp->parent) { | ||
54 | container_insert_child(temp->parent, con2, temp_index); | ||
55 | } else if (temp_floating) { | ||
56 | workspace_add_floating(temp->workspace, con2); | ||
57 | } else { | ||
58 | workspace_insert_tiling(temp->workspace, con2, temp_index); | ||
59 | } | ||
60 | |||
61 | free(temp); | ||
62 | } | ||
63 | |||
64 | static void swap_focus(struct sway_container *con1, | ||
65 | struct sway_container *con2, struct sway_seat *seat, | ||
66 | struct sway_container *focus) { | ||
67 | if (focus == con1 || focus == con2) { | ||
68 | struct sway_workspace *ws1 = con1->workspace; | ||
69 | struct sway_workspace *ws2 = con2->workspace; | ||
70 | enum sway_container_layout layout1 = container_parent_layout(con1); | ||
71 | enum sway_container_layout layout2 = container_parent_layout(con2); | ||
72 | if (focus == con1 && (layout2 == L_TABBED || layout2 == L_STACKED)) { | ||
73 | if (workspace_is_visible(ws2)) { | ||
74 | seat_set_focus(seat, &con2->node); | ||
75 | } | ||
76 | seat_set_focus_container(seat, ws1 != ws2 ? con2 : con1); | ||
77 | } else if (focus == con2 && (layout1 == L_TABBED | ||
78 | || layout1 == L_STACKED)) { | ||
79 | if (workspace_is_visible(ws1)) { | ||
80 | seat_set_focus(seat, &con1->node); | ||
81 | } | ||
82 | seat_set_focus_container(seat, ws1 != ws2 ? con1 : con2); | ||
83 | } else if (ws1 != ws2) { | ||
84 | seat_set_focus_container(seat, focus == con1 ? con2 : con1); | ||
85 | } else { | ||
86 | seat_set_focus_container(seat, focus); | ||
87 | } | ||
88 | } else { | ||
89 | seat_set_focus_container(seat, focus); | ||
90 | } | ||
91 | |||
92 | if (root->fullscreen_global) { | ||
93 | seat_set_focus(seat, | ||
94 | seat_get_focus_inactive(seat, &root->fullscreen_global->node)); | ||
95 | } | ||
96 | } | ||
97 | |||
98 | void container_swap(struct sway_container *con1, struct sway_container *con2) { | ||
99 | if (!sway_assert(con1 && con2, "Cannot swap with nothing")) { | ||
100 | return; | ||
101 | } | ||
102 | if (!sway_assert(!container_has_ancestor(con1, con2) | ||
103 | && !container_has_ancestor(con2, con1), | ||
104 | "Cannot swap ancestor and descendant")) { | ||
105 | return; | ||
106 | } | ||
107 | |||
108 | sway_log(SWAY_DEBUG, "Swapping containers %zu and %zu", | ||
109 | con1->node.id, con2->node.id); | ||
110 | |||
111 | bool scratch1 = con1->scratchpad; | ||
112 | bool hidden1 = container_is_scratchpad_hidden(con1); | ||
113 | bool scratch2 = con2->scratchpad; | ||
114 | bool hidden2 = container_is_scratchpad_hidden(con2); | ||
115 | if (scratch1) { | ||
116 | if (hidden1) { | ||
117 | root_scratchpad_show(con1); | ||
118 | } | ||
119 | root_scratchpad_remove_container(con1); | ||
120 | } | ||
121 | if (scratch2) { | ||
122 | if (hidden2) { | ||
123 | root_scratchpad_show(con2); | ||
124 | } | ||
125 | root_scratchpad_remove_container(con2); | ||
126 | } | ||
127 | |||
128 | enum sway_fullscreen_mode fs1 = con1->fullscreen_mode; | ||
129 | enum sway_fullscreen_mode fs2 = con2->fullscreen_mode; | ||
130 | if (fs1) { | ||
131 | container_fullscreen_disable(con1); | ||
132 | } | ||
133 | if (fs2) { | ||
134 | container_fullscreen_disable(con2); | ||
135 | } | ||
136 | |||
137 | struct sway_seat *seat = config->handler_context.seat; | ||
138 | struct sway_container *focus = seat_get_focused_container(seat); | ||
139 | struct sway_workspace *vis1 = | ||
140 | output_get_active_workspace(con1->workspace->output); | ||
141 | struct sway_workspace *vis2 = | ||
142 | output_get_active_workspace(con2->workspace->output); | ||
143 | if (!sway_assert(vis1 && vis2, "con1 or con2 are on an output without a" | ||
144 | "workspace. This should not happen")) { | ||
145 | return; | ||
146 | } | ||
147 | |||
148 | char *stored_prev_name = NULL; | ||
149 | if (seat->prev_workspace_name) { | ||
150 | stored_prev_name = strdup(seat->prev_workspace_name); | ||
151 | } | ||
152 | |||
153 | swap_places(con1, con2); | ||
154 | |||
155 | if (!workspace_is_visible(vis1)) { | ||
156 | seat_set_focus(seat, seat_get_focus_inactive(seat, &vis1->node)); | ||
157 | } | ||
158 | if (!workspace_is_visible(vis2)) { | ||
159 | seat_set_focus(seat, seat_get_focus_inactive(seat, &vis2->node)); | ||
160 | } | ||
161 | |||
162 | swap_focus(con1, con2, seat, focus); | ||
163 | |||
164 | if (stored_prev_name) { | ||
165 | free(seat->prev_workspace_name); | ||
166 | seat->prev_workspace_name = stored_prev_name; | ||
167 | } | ||
168 | |||
169 | if (scratch1) { | ||
170 | root_scratchpad_add_container(con2, NULL); | ||
171 | if (!hidden1) { | ||
172 | root_scratchpad_show(con2); | ||
173 | } | ||
174 | } | ||
175 | if (scratch2) { | ||
176 | root_scratchpad_add_container(con1, NULL); | ||
177 | if (!hidden2) { | ||
178 | root_scratchpad_show(con1); | ||
179 | } | ||
180 | } | ||
181 | |||
182 | if (fs1) { | ||
183 | container_set_fullscreen(con2, fs1); | ||
184 | } | ||
185 | if (fs2) { | ||
186 | container_set_fullscreen(con1, fs2); | ||
187 | } | ||
188 | } | ||
189 | |||
190 | static bool test_con_id(struct sway_container *container, void *data) { | 16 | static bool test_con_id(struct sway_container *container, void *data) { |
191 | size_t *con_id = data; | 17 | size_t *con_id = data; |
192 | return container->node.id == *con_id; | 18 | return container->node.id == *con_id; |
193 | } | 19 | } |
194 | 20 | ||
195 | #if HAVE_XWAYLAND | 21 | #if WLR_HAS_XWAYLAND |
196 | static bool test_id(struct sway_container *container, void *data) { | 22 | static bool test_id(struct sway_container *container, void *data) { |
197 | xcb_window_t *wid = data; | 23 | xcb_window_t *wid = data; |
198 | return (container->view && container->view->type == SWAY_VIEW_XWAYLAND | 24 | return (container->view && container->view->type == SWAY_VIEW_XWAYLAND |
@@ -219,7 +45,7 @@ struct cmd_results *cmd_swap(int argc, char **argv) { | |||
219 | } | 45 | } |
220 | 46 | ||
221 | if (strcasecmp(argv[0], "container") || strcasecmp(argv[1], "with")) { | 47 | if (strcasecmp(argv[0], "container") || strcasecmp(argv[1], "with")) { |
222 | return cmd_results_new(CMD_INVALID, expected_syntax); | 48 | return cmd_results_new(CMD_INVALID, "%s", expected_syntax); |
223 | } | 49 | } |
224 | 50 | ||
225 | struct sway_container *current = config->handler_context.container; | 51 | struct sway_container *current = config->handler_context.container; |
@@ -227,7 +53,7 @@ struct cmd_results *cmd_swap(int argc, char **argv) { | |||
227 | 53 | ||
228 | char *value = join_args(argv + 3, argc - 3); | 54 | char *value = join_args(argv + 3, argc - 3); |
229 | if (strcasecmp(argv[2], "id") == 0) { | 55 | if (strcasecmp(argv[2], "id") == 0) { |
230 | #if HAVE_XWAYLAND | 56 | #if WLR_HAS_XWAYLAND |
231 | xcb_window_t id = strtol(value, NULL, 0); | 57 | xcb_window_t id = strtol(value, NULL, 0); |
232 | other = root_find_container(test_id, &id); | 58 | other = root_find_container(test_id, &id); |
233 | #endif | 59 | #endif |
@@ -238,7 +64,7 @@ struct cmd_results *cmd_swap(int argc, char **argv) { | |||
238 | other = root_find_container(test_mark, value); | 64 | other = root_find_container(test_mark, value); |
239 | } else { | 65 | } else { |
240 | free(value); | 66 | free(value); |
241 | return cmd_results_new(CMD_INVALID, expected_syntax); | 67 | return cmd_results_new(CMD_INVALID, "%s", expected_syntax); |
242 | } | 68 | } |
243 | 69 | ||
244 | if (!other) { | 70 | if (!other) { |
@@ -247,6 +73,9 @@ struct cmd_results *cmd_swap(int argc, char **argv) { | |||
247 | } else if (!current) { | 73 | } else if (!current) { |
248 | error = cmd_results_new(CMD_FAILURE, | 74 | error = cmd_results_new(CMD_FAILURE, |
249 | "Can only swap with containers and views"); | 75 | "Can only swap with containers and views"); |
76 | } else if (current == other) { | ||
77 | error = cmd_results_new(CMD_FAILURE, | ||
78 | "Cannot swap a container with itself"); | ||
250 | } else if (container_has_ancestor(current, other) | 79 | } else if (container_has_ancestor(current, other) |
251 | || container_has_ancestor(other, current)) { | 80 | || container_has_ancestor(other, current)) { |
252 | error = cmd_results_new(CMD_FAILURE, | 81 | error = cmd_results_new(CMD_FAILURE, |
diff --git a/sway/commands/title_align.c b/sway/commands/title_align.c index c30355de..be298a29 100644 --- a/sway/commands/title_align.c +++ b/sway/commands/title_align.c | |||
@@ -4,6 +4,10 @@ | |||
4 | #include "sway/tree/container.h" | 4 | #include "sway/tree/container.h" |
5 | #include "sway/tree/root.h" | 5 | #include "sway/tree/root.h" |
6 | 6 | ||
7 | static void arrange_title_bar_iterator(struct sway_container *con, void *data) { | ||
8 | container_arrange_title_bar(con); | ||
9 | } | ||
10 | |||
7 | struct cmd_results *cmd_title_align(int argc, char **argv) { | 11 | struct cmd_results *cmd_title_align(int argc, char **argv) { |
8 | struct cmd_results *error = NULL; | 12 | struct cmd_results *error = NULL; |
9 | if ((error = checkarg(argc, "title_align", EXPECTED_AT_LEAST, 1))) { | 13 | if ((error = checkarg(argc, "title_align", EXPECTED_AT_LEAST, 1))) { |
@@ -21,10 +25,7 @@ struct cmd_results *cmd_title_align(int argc, char **argv) { | |||
21 | "Expected 'title_align left|center|right'"); | 25 | "Expected 'title_align left|center|right'"); |
22 | } | 26 | } |
23 | 27 | ||
24 | for (int i = 0; i < root->outputs->length; ++i) { | 28 | root_for_each_container(arrange_title_bar_iterator, NULL); |
25 | struct sway_output *output = root->outputs->items[i]; | ||
26 | output_damage_whole(output); | ||
27 | } | ||
28 | 29 | ||
29 | return cmd_results_new(CMD_SUCCESS, NULL); | 30 | return cmd_results_new(CMD_SUCCESS, NULL); |
30 | } | 31 | } |
diff --git a/sway/commands/title_format.c b/sway/commands/title_format.c index 9d312470..0b2ea265 100644 --- a/sway/commands/title_format.c +++ b/sway/commands/title_format.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <string.h> | 1 | #include <string.h> |
3 | #include "sway/commands.h" | 2 | #include "sway/commands.h" |
4 | #include "sway/config.h" | 3 | #include "sway/config.h" |
@@ -23,6 +22,5 @@ struct cmd_results *cmd_title_format(int argc, char **argv) { | |||
23 | } | 22 | } |
24 | view->title_format = format; | 23 | view->title_format = format; |
25 | view_update_title(view, true); | 24 | view_update_title(view, true); |
26 | config_update_font_height(true); | ||
27 | return cmd_results_new(CMD_SUCCESS, NULL); | 25 | return cmd_results_new(CMD_SUCCESS, NULL); |
28 | } | 26 | } |
diff --git a/sway/commands/titlebar_border_thickness.c b/sway/commands/titlebar_border_thickness.c index 7c27c163..fa3db3c5 100644 --- a/sway/commands/titlebar_border_thickness.c +++ b/sway/commands/titlebar_border_thickness.c | |||
@@ -27,7 +27,6 @@ struct cmd_results *cmd_titlebar_border_thickness(int argc, char **argv) { | |||
27 | "Expected output to have a workspace"); | 27 | "Expected output to have a workspace"); |
28 | } | 28 | } |
29 | arrange_workspace(ws); | 29 | arrange_workspace(ws); |
30 | output_damage_whole(output); | ||
31 | } | 30 | } |
32 | 31 | ||
33 | return cmd_results_new(CMD_SUCCESS, NULL); | 32 | return cmd_results_new(CMD_SUCCESS, NULL); |
diff --git a/sway/commands/titlebar_padding.c b/sway/commands/titlebar_padding.c index 29ce59ff..6999f7a2 100644 --- a/sway/commands/titlebar_padding.c +++ b/sway/commands/titlebar_padding.c | |||
@@ -33,7 +33,6 @@ struct cmd_results *cmd_titlebar_padding(int argc, char **argv) { | |||
33 | for (int i = 0; i < root->outputs->length; ++i) { | 33 | for (int i = 0; i < root->outputs->length; ++i) { |
34 | struct sway_output *output = root->outputs->items[i]; | 34 | struct sway_output *output = root->outputs->items[i]; |
35 | arrange_workspace(output_get_active_workspace(output)); | 35 | arrange_workspace(output_get_active_workspace(output)); |
36 | output_damage_whole(output); | ||
37 | } | 36 | } |
38 | 37 | ||
39 | return cmd_results_new(CMD_SUCCESS, NULL); | 38 | return cmd_results_new(CMD_SUCCESS, NULL); |
diff --git a/sway/commands/unmark.c b/sway/commands/unmark.c index cedfcfb2..4aba5bae 100644 --- a/sway/commands/unmark.c +++ b/sway/commands/unmark.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <string.h> | 1 | #include <string.h> |
3 | #include "sway/commands.h" | 2 | #include "sway/commands.h" |
4 | #include "sway/config.h" | 3 | #include "sway/config.h" |
@@ -8,9 +7,13 @@ | |||
8 | #include "log.h" | 7 | #include "log.h" |
9 | #include "stringop.h" | 8 | #include "stringop.h" |
10 | 9 | ||
11 | static void remove_all_marks_iterator(struct sway_container *con, void *data) { | 10 | static void remove_mark(struct sway_container *con) { |
12 | container_clear_marks(con); | 11 | container_clear_marks(con); |
13 | container_update_marks_textures(con); | 12 | container_update_marks(con); |
13 | } | ||
14 | |||
15 | static void remove_all_marks_iterator(struct sway_container *con, void *data) { | ||
16 | remove_mark(con); | ||
14 | } | 17 | } |
15 | 18 | ||
16 | // unmark Remove all marks from all views | 19 | // unmark Remove all marks from all views |
@@ -21,7 +24,7 @@ static void remove_all_marks_iterator(struct sway_container *con, void *data) { | |||
21 | struct cmd_results *cmd_unmark(int argc, char **argv) { | 24 | struct cmd_results *cmd_unmark(int argc, char **argv) { |
22 | // Determine the container | 25 | // Determine the container |
23 | struct sway_container *con = NULL; | 26 | struct sway_container *con = NULL; |
24 | if (config->handler_context.using_criteria) { | 27 | if (config->handler_context.node_overridden) { |
25 | con = config->handler_context.container; | 28 | con = config->handler_context.container; |
26 | } | 29 | } |
27 | 30 | ||
@@ -38,8 +41,7 @@ struct cmd_results *cmd_unmark(int argc, char **argv) { | |||
38 | } | 41 | } |
39 | } else if (con && !mark) { | 42 | } else if (con && !mark) { |
40 | // Clear all marks from the given container | 43 | // Clear all marks from the given container |
41 | container_clear_marks(con); | 44 | remove_mark(con); |
42 | container_update_marks_textures(con); | ||
43 | } else if (!con && mark) { | 45 | } else if (!con && mark) { |
44 | // Remove mark from whichever container has it | 46 | // Remove mark from whichever container has it |
45 | container_find_and_unmark(mark); | 47 | container_find_and_unmark(mark); |
diff --git a/sway/commands/workspace.c b/sway/commands/workspace.c index 9ff1c97d..37a201b4 100644 --- a/sway/commands/workspace.c +++ b/sway/commands/workspace.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <ctype.h> | 1 | #include <ctype.h> |
3 | #include <limits.h> | 2 | #include <limits.h> |
4 | #include <string.h> | 3 | #include <string.h> |
@@ -61,7 +60,7 @@ static struct cmd_results *cmd_workspace_gaps(int argc, char **argv, | |||
61 | const char expected[] = "Expected 'workspace <name> gaps " | 60 | const char expected[] = "Expected 'workspace <name> gaps " |
62 | "inner|outer|horizontal|vertical|top|right|bottom|left <px>'"; | 61 | "inner|outer|horizontal|vertical|top|right|bottom|left <px>'"; |
63 | if (gaps_location == 0) { | 62 | if (gaps_location == 0) { |
64 | return cmd_results_new(CMD_INVALID, expected); | 63 | return cmd_results_new(CMD_INVALID, "%s", expected); |
65 | } | 64 | } |
66 | struct cmd_results *error = NULL; | 65 | struct cmd_results *error = NULL; |
67 | if ((error = checkarg(argc, "workspace", EXPECTED_EQUAL_TO, | 66 | if ((error = checkarg(argc, "workspace", EXPECTED_EQUAL_TO, |
@@ -79,7 +78,7 @@ static struct cmd_results *cmd_workspace_gaps(int argc, char **argv, | |||
79 | char *end; | 78 | char *end; |
80 | int amount = strtol(argv[gaps_location + 2], &end, 10); | 79 | int amount = strtol(argv[gaps_location + 2], &end, 10); |
81 | if (strlen(end)) { | 80 | if (strlen(end)) { |
82 | return cmd_results_new(CMD_FAILURE, expected); | 81 | return cmd_results_new(CMD_FAILURE, "%s", expected); |
83 | } | 82 | } |
84 | 83 | ||
85 | bool valid = false; | 84 | bool valid = false; |
@@ -110,7 +109,7 @@ static struct cmd_results *cmd_workspace_gaps(int argc, char **argv, | |||
110 | } | 109 | } |
111 | } | 110 | } |
112 | if (!valid) { | 111 | if (!valid) { |
113 | return cmd_results_new(CMD_INVALID, expected); | 112 | return cmd_results_new(CMD_INVALID, "%s", expected); |
114 | } | 113 | } |
115 | 114 | ||
116 | // Prevent invalid gaps configurations. | 115 | // Prevent invalid gaps configurations. |
@@ -178,25 +177,20 @@ struct cmd_results *cmd_workspace(int argc, char **argv) { | |||
178 | } | 177 | } |
179 | 178 | ||
180 | if (root->fullscreen_global) { | 179 | if (root->fullscreen_global) { |
181 | return cmd_results_new(CMD_FAILURE, "workspace", | 180 | return cmd_results_new(CMD_FAILURE, |
182 | "Can't switch workspaces while fullscreen global"); | 181 | "Can't switch workspaces while fullscreen global"); |
183 | } | 182 | } |
184 | 183 | ||
185 | bool no_auto_back_and_forth = false; | 184 | bool auto_back_and_forth = true; |
186 | while (strcasecmp(argv[0], "--no-auto-back-and-forth") == 0) { | 185 | while (strcasecmp(argv[0], "--no-auto-back-and-forth") == 0) { |
187 | no_auto_back_and_forth = true; | 186 | auto_back_and_forth = false; |
188 | if ((error = checkarg(--argc, "workspace", EXPECTED_AT_LEAST, 1))) { | 187 | if ((error = checkarg(--argc, "workspace", EXPECTED_AT_LEAST, 1))) { |
189 | return error; | 188 | return error; |
190 | } | 189 | } |
191 | ++argv; | 190 | ++argv; |
192 | } | 191 | } |
193 | 192 | ||
194 | bool create = argc > 1 && strcasecmp(argv[1], "--create") == 0; | ||
195 | struct sway_seat *seat = config->handler_context.seat; | 193 | 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 | 194 | ||
201 | struct sway_workspace *ws = NULL; | 195 | struct sway_workspace *ws = NULL; |
202 | if (strcasecmp(argv[0], "number") == 0) { | 196 | if (strcasecmp(argv[0], "number") == 0) { |
@@ -213,14 +207,15 @@ struct cmd_results *cmd_workspace(int argc, char **argv) { | |||
213 | ws = workspace_create(NULL, name); | 207 | ws = workspace_create(NULL, name); |
214 | free(name); | 208 | free(name); |
215 | } | 209 | } |
210 | if (ws && auto_back_and_forth) { | ||
211 | ws = workspace_auto_back_and_forth(ws); | ||
212 | } | ||
216 | } else if (strcasecmp(argv[0], "next") == 0 || | 213 | } else if (strcasecmp(argv[0], "next") == 0 || |
217 | strcasecmp(argv[0], "prev") == 0 || | 214 | strcasecmp(argv[0], "prev") == 0 || |
215 | strcasecmp(argv[0], "next_on_output") == 0 || | ||
216 | strcasecmp(argv[0], "prev_on_output") == 0 || | ||
218 | strcasecmp(argv[0], "current") == 0) { | 217 | strcasecmp(argv[0], "current") == 0) { |
219 | ws = workspace_by_name(argv[0]); | 218 | 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) { | 219 | } else if (strcasecmp(argv[0], "back_and_forth") == 0) { |
225 | if (!seat->prev_workspace_name) { | 220 | if (!seat->prev_workspace_name) { |
226 | return cmd_results_new(CMD_INVALID, | 221 | return cmd_results_new(CMD_INVALID, |
@@ -235,11 +230,14 @@ struct cmd_results *cmd_workspace(int argc, char **argv) { | |||
235 | ws = workspace_create(NULL, name); | 230 | ws = workspace_create(NULL, name); |
236 | } | 231 | } |
237 | free(name); | 232 | free(name); |
233 | if (ws && auto_back_and_forth) { | ||
234 | ws = workspace_auto_back_and_forth(ws); | ||
235 | } | ||
238 | } | 236 | } |
239 | if (!ws) { | 237 | if (!ws) { |
240 | return cmd_results_new(CMD_FAILURE, "No workspace to switch to"); | 238 | return cmd_results_new(CMD_FAILURE, "No workspace to switch to"); |
241 | } | 239 | } |
242 | workspace_switch(ws, no_auto_back_and_forth); | 240 | workspace_switch(ws); |
243 | seat_consider_warp_to_focus(seat); | 241 | seat_consider_warp_to_focus(seat); |
244 | } | 242 | } |
245 | return cmd_results_new(CMD_SUCCESS, NULL); | 243 | return cmd_results_new(CMD_SUCCESS, NULL); |
diff --git a/sway/commands/xwayland.c b/sway/commands/xwayland.c index 6ca26923..c0b175fc 100644 --- a/sway/commands/xwayland.c +++ b/sway/commands/xwayland.c | |||
@@ -10,7 +10,7 @@ struct cmd_results *cmd_xwayland(int argc, char **argv) { | |||
10 | return error; | 10 | return error; |
11 | } | 11 | } |
12 | 12 | ||
13 | #ifdef HAVE_XWAYLAND | 13 | #ifdef WLR_HAS_XWAYLAND |
14 | enum xwayland_mode xwayland; | 14 | enum xwayland_mode xwayland; |
15 | if (strcmp(argv[0], "force") == 0) { | 15 | if (strcmp(argv[0], "force") == 0) { |
16 | xwayland = XWAYLAND_MODE_IMMEDIATE; | 16 | xwayland = XWAYLAND_MODE_IMMEDIATE; |
@@ -20,6 +20,8 @@ struct cmd_results *cmd_xwayland(int argc, char **argv) { | |||
20 | xwayland = XWAYLAND_MODE_DISABLED; | 20 | xwayland = XWAYLAND_MODE_DISABLED; |
21 | } | 21 | } |
22 | 22 | ||
23 | // config->xwayland is reset to the previous value on reload in | ||
24 | // load_main_config() | ||
23 | if (config->reloading && config->xwayland != xwayland) { | 25 | if (config->reloading && config->xwayland != xwayland) { |
24 | return cmd_results_new(CMD_FAILURE, | 26 | return cmd_results_new(CMD_FAILURE, |
25 | "xwayland can only be enabled/disabled at launch"); | 27 | "xwayland can only be enabled/disabled at launch"); |
diff --git a/sway/config.c b/sway/config.c index 6e665434..f9131e0f 100644 --- a/sway/config.c +++ b/sway/config.c | |||
@@ -1,3 +1,4 @@ | |||
1 | #undef _POSIX_C_SOURCE | ||
1 | #define _XOPEN_SOURCE 700 // for realpath | 2 | #define _XOPEN_SOURCE 700 // for realpath |
2 | #include <stdio.h> | 3 | #include <stdio.h> |
3 | #include <stdbool.h> | 4 | #include <stdbool.h> |
@@ -26,7 +27,7 @@ | |||
26 | #include "sway/tree/arrange.h" | 27 | #include "sway/tree/arrange.h" |
27 | #include "sway/tree/root.h" | 28 | #include "sway/tree/root.h" |
28 | #include "sway/tree/workspace.h" | 29 | #include "sway/tree/workspace.h" |
29 | #include "cairo.h" | 30 | #include "cairo_util.h" |
30 | #include "pango.h" | 31 | #include "pango.h" |
31 | #include "stringop.h" | 32 | #include "stringop.h" |
32 | #include "list.h" | 33 | #include "list.h" |
@@ -36,19 +37,26 @@ | |||
36 | struct sway_config *config = NULL; | 37 | struct sway_config *config = NULL; |
37 | 38 | ||
38 | static struct xkb_state *keysym_translation_state_create( | 39 | static struct xkb_state *keysym_translation_state_create( |
39 | struct xkb_rule_names rules) { | 40 | struct xkb_rule_names rules, uint32_t context_flags) { |
40 | struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); | 41 | struct xkb_context *context = xkb_context_new(context_flags | XKB_CONTEXT_NO_SECURE_GETENV); |
41 | struct xkb_keymap *xkb_keymap = xkb_keymap_new_from_names( | 42 | struct xkb_keymap *xkb_keymap = xkb_keymap_new_from_names( |
42 | context, | 43 | context, |
43 | &rules, | 44 | &rules, |
44 | XKB_KEYMAP_COMPILE_NO_FLAGS); | 45 | XKB_KEYMAP_COMPILE_NO_FLAGS); |
45 | |||
46 | xkb_context_unref(context); | 46 | xkb_context_unref(context); |
47 | if (xkb_keymap == NULL) { | ||
48 | sway_log(SWAY_ERROR, "Failed to compile keysym translation XKB keymap"); | ||
49 | return NULL; | ||
50 | } | ||
51 | |||
47 | return xkb_state_new(xkb_keymap); | 52 | return xkb_state_new(xkb_keymap); |
48 | } | 53 | } |
49 | 54 | ||
50 | static void keysym_translation_state_destroy( | 55 | static void keysym_translation_state_destroy( |
51 | struct xkb_state *state) { | 56 | struct xkb_state *state) { |
57 | if (state == NULL) { | ||
58 | return; | ||
59 | } | ||
52 | xkb_keymap_unref(xkb_state_get_keymap(state)); | 60 | xkb_keymap_unref(xkb_state_get_keymap(state)); |
53 | xkb_state_unref(state); | 61 | xkb_state_unref(state); |
54 | } | 62 | } |
@@ -82,6 +90,12 @@ static void free_mode(struct sway_mode *mode) { | |||
82 | } | 90 | } |
83 | list_free(mode->switch_bindings); | 91 | list_free(mode->switch_bindings); |
84 | } | 92 | } |
93 | if (mode->gesture_bindings) { | ||
94 | for (int i = 0; i < mode->gesture_bindings->length; i++) { | ||
95 | free_gesture_binding(mode->gesture_bindings->items[i]); | ||
96 | } | ||
97 | list_free(mode->gesture_bindings); | ||
98 | } | ||
85 | free(mode); | 99 | free(mode); |
86 | } | 100 | } |
87 | 101 | ||
@@ -222,6 +236,7 @@ static void config_defaults(struct sway_config *config) { | |||
222 | if (!(config->current_mode->keycode_bindings = create_list())) goto cleanup; | 236 | if (!(config->current_mode->keycode_bindings = create_list())) goto cleanup; |
223 | if (!(config->current_mode->mouse_bindings = create_list())) goto cleanup; | 237 | if (!(config->current_mode->mouse_bindings = create_list())) goto cleanup; |
224 | if (!(config->current_mode->switch_bindings = create_list())) goto cleanup; | 238 | if (!(config->current_mode->switch_bindings = create_list())) goto cleanup; |
239 | if (!(config->current_mode->gesture_bindings = create_list())) goto cleanup; | ||
225 | list_add(config->modes, config->current_mode); | 240 | list_add(config->modes, config->current_mode); |
226 | 241 | ||
227 | config->floating_mod = 0; | 242 | config->floating_mod = 0; |
@@ -236,7 +251,7 @@ static void config_defaults(struct sway_config *config) { | |||
236 | config->default_layout = L_NONE; | 251 | config->default_layout = L_NONE; |
237 | config->default_orientation = L_NONE; | 252 | config->default_orientation = L_NONE; |
238 | if (!(config->font = strdup("monospace 10"))) goto cleanup; | 253 | if (!(config->font = strdup("monospace 10"))) goto cleanup; |
239 | config->font_height = 17; // height of monospace 10 | 254 | config->font_description = pango_font_description_from_string(config->font); |
240 | config->urgent_timeout = 500; | 255 | config->urgent_timeout = 500; |
241 | config->focus_on_window_activation = FOWA_URGENT; | 256 | config->focus_on_window_activation = FOWA_URGENT; |
242 | config->popup_during_fullscreen = POPUP_SMART; | 257 | config->popup_during_fullscreen = POPUP_SMART; |
@@ -266,8 +281,9 @@ static void config_defaults(struct sway_config *config) { | |||
266 | config->title_align = ALIGN_LEFT; | 281 | config->title_align = ALIGN_LEFT; |
267 | config->tiling_drag = true; | 282 | config->tiling_drag = true; |
268 | config->tiling_drag_threshold = 9; | 283 | config->tiling_drag_threshold = 9; |
284 | config->primary_selection = true; | ||
269 | 285 | ||
270 | config->smart_gaps = false; | 286 | config->smart_gaps = SMART_GAPS_OFF; |
271 | config->gaps_inner = 0; | 287 | config->gaps_inner = 0; |
272 | config->gaps_outer.top = 0; | 288 | config->gaps_outer.top = 0; |
273 | config->gaps_outer.right = 0; | 289 | config->gaps_outer.right = 0; |
@@ -291,6 +307,8 @@ static void config_defaults(struct sway_config *config) { | |||
291 | config->hide_edge_borders_smart = ESMART_OFF; | 307 | config->hide_edge_borders_smart = ESMART_OFF; |
292 | config->hide_lone_tab = false; | 308 | config->hide_lone_tab = false; |
293 | 309 | ||
310 | config->has_focused_tab_title = false; | ||
311 | |||
294 | // border colors | 312 | // border colors |
295 | color_to_rgba(config->border_colors.focused.border, 0x4C7899FF); | 313 | color_to_rgba(config->border_colors.focused.border, 0x4C7899FF); |
296 | color_to_rgba(config->border_colors.focused.background, 0x285577FF); | 314 | color_to_rgba(config->border_colors.focused.background, 0x285577FF); |
@@ -326,8 +344,14 @@ static void config_defaults(struct sway_config *config) { | |||
326 | 344 | ||
327 | // The keysym to keycode translation | 345 | // The keysym to keycode translation |
328 | struct xkb_rule_names rules = {0}; | 346 | struct xkb_rule_names rules = {0}; |
329 | config->keysym_translation_state = | 347 | config->keysym_translation_state = keysym_translation_state_create(rules, 0); |
330 | keysym_translation_state_create(rules); | 348 | if (config->keysym_translation_state == NULL) { |
349 | config->keysym_translation_state = keysym_translation_state_create(rules, | ||
350 | XKB_CONTEXT_NO_ENVIRONMENT_NAMES); | ||
351 | } | ||
352 | if (config->keysym_translation_state == NULL) { | ||
353 | goto cleanup; | ||
354 | } | ||
331 | 355 | ||
332 | return; | 356 | return; |
333 | cleanup: | 357 | cleanup: |
@@ -338,35 +362,53 @@ static bool file_exists(const char *path) { | |||
338 | return path && access(path, R_OK) != -1; | 362 | return path && access(path, R_OK) != -1; |
339 | } | 363 | } |
340 | 364 | ||
365 | static char *config_path(const char *prefix, const char *config_folder) { | ||
366 | if (!prefix || !prefix[0] || !config_folder || !config_folder[0]) { | ||
367 | return NULL; | ||
368 | } | ||
369 | return format_str("%s/%s/config", prefix, config_folder); | ||
370 | } | ||
371 | |||
341 | static char *get_config_path(void) { | 372 | static char *get_config_path(void) { |
342 | static const char *config_paths[] = { | 373 | char *path = NULL; |
343 | "$HOME/.sway/config", | 374 | const char *home = getenv("HOME"); |
344 | "$XDG_CONFIG_HOME/sway/config", | 375 | char *config_home_fallback = NULL; |
345 | "$HOME/.i3/config", | 376 | |
346 | "$XDG_CONFIG_HOME/i3/config", | 377 | const char *config_home = getenv("XDG_CONFIG_HOME"); |
347 | SYSCONFDIR "/sway/config", | 378 | if ((config_home == NULL || config_home[0] == '\0') && home != NULL) { |
348 | SYSCONFDIR "/i3/config", | 379 | config_home_fallback = format_str("%s/.config", home); |
380 | config_home = config_home_fallback; | ||
381 | } | ||
382 | |||
383 | struct config_path { | ||
384 | const char *prefix; | ||
385 | const char *config_folder; | ||
349 | }; | 386 | }; |
350 | 387 | ||
351 | char *config_home = getenv("XDG_CONFIG_HOME"); | 388 | struct config_path config_paths[] = { |
352 | if (!config_home || !*config_home) { | 389 | { .prefix = home, .config_folder = ".sway"}, |
353 | config_paths[1] = "$HOME/.config/sway/config"; | 390 | { .prefix = config_home, .config_folder = "sway"}, |
354 | config_paths[3] = "$HOME/.config/i3/config"; | 391 | { .prefix = home, .config_folder = ".i3"}, |
355 | } | 392 | { .prefix = config_home, .config_folder = "i3"}, |
393 | { .prefix = SYSCONFDIR, .config_folder = "sway"}, | ||
394 | { .prefix = SYSCONFDIR, .config_folder = "i3"} | ||
395 | }; | ||
356 | 396 | ||
357 | for (size_t i = 0; i < sizeof(config_paths) / sizeof(char *); ++i) { | 397 | size_t num_config_paths = sizeof(config_paths)/sizeof(config_paths[0]); |
358 | wordexp_t p; | 398 | for (size_t i = 0; i < num_config_paths; i++) { |
359 | if (wordexp(config_paths[i], &p, WRDE_UNDEF) == 0) { | 399 | path = config_path(config_paths[i].prefix, config_paths[i].config_folder); |
360 | char *path = strdup(p.we_wordv[0]); | 400 | if (!path) { |
361 | wordfree(&p); | 401 | continue; |
362 | if (file_exists(path)) { | 402 | } |
363 | return path; | 403 | if (file_exists(path)) { |
364 | } | 404 | break; |
365 | free(path); | ||
366 | } | 405 | } |
406 | free(path); | ||
407 | path = NULL; | ||
367 | } | 408 | } |
368 | 409 | ||
369 | return NULL; | 410 | free(config_home_fallback); |
411 | return path; | ||
370 | } | 412 | } |
371 | 413 | ||
372 | static bool load_config(const char *path, struct sway_config *config, | 414 | static bool load_config(const char *path, struct sway_config *config, |
@@ -438,6 +480,11 @@ bool load_main_config(const char *file, bool is_active, bool validating) { | |||
438 | old_config->xwayland ? "enabled" : "disabled"); | 480 | old_config->xwayland ? "enabled" : "disabled"); |
439 | config->xwayland = old_config->xwayland; | 481 | config->xwayland = old_config->xwayland; |
440 | 482 | ||
483 | // primary_selection can only be enabled/disabled at launch | ||
484 | sway_log(SWAY_DEBUG, "primary_selection will remain %s", | ||
485 | old_config->primary_selection ? "enabled" : "disabled"); | ||
486 | config->primary_selection = old_config->primary_selection; | ||
487 | |||
441 | if (!config->validating) { | 488 | if (!config->validating) { |
442 | if (old_config->swaybg_client != NULL) { | 489 | if (old_config->swaybg_client != NULL) { |
443 | wl_client_destroy(old_config->swaybg_client); | 490 | wl_client_destroy(old_config->swaybg_client); |
@@ -457,56 +504,7 @@ bool load_main_config(const char *file, bool is_active, bool validating) { | |||
457 | 504 | ||
458 | config->reading = true; | 505 | config->reading = true; |
459 | 506 | ||
460 | // Read security configs | 507 | bool success = load_config(path, config, &config->swaynag_config_errors); |
461 | // TODO: Security | ||
462 | bool success = true; | ||
463 | /* | ||
464 | DIR *dir = opendir(SYSCONFDIR "/sway/security.d"); | ||
465 | if (!dir) { | ||
466 | sway_log(SWAY_ERROR, | ||
467 | "%s does not exist, sway will have no security configuration" | ||
468 | " and will probably be broken", SYSCONFDIR "/sway/security.d"); | ||
469 | } else { | ||
470 | list_t *secconfigs = create_list(); | ||
471 | char *base = SYSCONFDIR "/sway/security.d/"; | ||
472 | struct dirent *ent = readdir(dir); | ||
473 | struct stat s; | ||
474 | while (ent != NULL) { | ||
475 | char *_path = malloc(strlen(ent->d_name) + strlen(base) + 1); | ||
476 | strcpy(_path, base); | ||
477 | strcat(_path, ent->d_name); | ||
478 | lstat(_path, &s); | ||
479 | if (S_ISREG(s.st_mode) && ent->d_name[0] != '.') { | ||
480 | list_add(secconfigs, _path); | ||
481 | } | ||
482 | else { | ||
483 | free(_path); | ||
484 | } | ||
485 | ent = readdir(dir); | ||
486 | } | ||
487 | closedir(dir); | ||
488 | |||
489 | list_qsort(secconfigs, qstrcmp); | ||
490 | for (int i = 0; i < secconfigs->length; ++i) { | ||
491 | char *_path = secconfigs->items[i]; | ||
492 | if (stat(_path, &s) || s.st_uid != 0 || s.st_gid != 0 || | ||
493 | (((s.st_mode & 0777) != 0644) && | ||
494 | (s.st_mode & 0777) != 0444)) { | ||
495 | sway_log(SWAY_ERROR, | ||
496 | "Refusing to load %s - it must be owned by root " | ||
497 | "and mode 644 or 444", _path); | ||
498 | success = false; | ||
499 | } else { | ||
500 | success = success && load_config(_path, config); | ||
501 | } | ||
502 | } | ||
503 | |||
504 | list_free_items_and_destroy(secconfigs); | ||
505 | } | ||
506 | */ | ||
507 | |||
508 | success = success && load_config(path, config, | ||
509 | &config->swaynag_config_errors); | ||
510 | 508 | ||
511 | if (validating) { | 509 | if (validating) { |
512 | free_config(config); | 510 | free_config(config); |
@@ -514,6 +512,9 @@ bool load_main_config(const char *file, bool is_active, bool validating) { | |||
514 | return success; | 512 | return success; |
515 | } | 513 | } |
516 | 514 | ||
515 | // Only really necessary if not explicitly `font` is set in the config. | ||
516 | config_update_font_height(); | ||
517 | |||
517 | if (is_active && !validating) { | 518 | if (is_active && !validating) { |
518 | input_manager_verify_fallback_seat(); | 519 | input_manager_verify_fallback_seat(); |
519 | 520 | ||
@@ -531,7 +532,7 @@ bool load_main_config(const char *file, bool is_active, bool validating) { | |||
531 | } | 532 | } |
532 | sway_switch_retrigger_bindings_for_all(); | 533 | sway_switch_retrigger_bindings_for_all(); |
533 | 534 | ||
534 | reset_outputs(); | 535 | apply_all_output_configs(); |
535 | spawn_swaybg(); | 536 | spawn_swaybg(); |
536 | 537 | ||
537 | config->reloading = false; | 538 | config->reloading = false; |
@@ -884,23 +885,18 @@ void config_add_swaynag_warning(char *fmt, ...) { | |||
884 | if (config->reading && !config->validating) { | 885 | if (config->reading && !config->validating) { |
885 | va_list args; | 886 | va_list args; |
886 | va_start(args, fmt); | 887 | va_start(args, fmt); |
887 | size_t length = vsnprintf(NULL, 0, fmt, args) + 1; | 888 | char *str = vformat_str(fmt, args); |
888 | va_end(args); | 889 | va_end(args); |
889 | 890 | if (str == NULL) { | |
890 | char *temp = malloc(length + 1); | ||
891 | if (!temp) { | ||
892 | sway_log(SWAY_ERROR, "Failed to allocate buffer for warning."); | ||
893 | return; | 891 | return; |
894 | } | 892 | } |
895 | 893 | ||
896 | va_start(args, fmt); | ||
897 | vsnprintf(temp, length, fmt, args); | ||
898 | va_end(args); | ||
899 | |||
900 | swaynag_log(config->swaynag_command, &config->swaynag_config_errors, | 894 | swaynag_log(config->swaynag_command, &config->swaynag_config_errors, |
901 | "Warning on line %i (%s) '%s': %s", | 895 | "Warning on line %i (%s) '%s': %s", |
902 | config->current_config_line_number, config->current_config_path, | 896 | config->current_config_line_number, config->current_config_path, |
903 | config->current_config_line, temp); | 897 | config->current_config_line, str); |
898 | |||
899 | free(str); | ||
904 | } | 900 | } |
905 | } | 901 | } |
906 | 902 | ||
@@ -940,7 +936,7 @@ char *do_var_replacement(char *str) { | |||
940 | int offset = find - str; | 936 | int offset = find - str; |
941 | strncpy(newptr, str, offset); | 937 | strncpy(newptr, str, offset); |
942 | newptr += offset; | 938 | newptr += offset; |
943 | strncpy(newptr, var->value, vvlen); | 939 | memcpy(newptr, var->value, vvlen); |
944 | newptr += vvlen; | 940 | newptr += vvlen; |
945 | strcpy(newptr, find + vnlen); | 941 | strcpy(newptr, find + vnlen); |
946 | free(str); | 942 | free(str); |
@@ -964,31 +960,11 @@ int workspace_output_cmp_workspace(const void *a, const void *b) { | |||
964 | return lenient_strcmp(wsa->workspace, wsb->workspace); | 960 | return lenient_strcmp(wsa->workspace, wsb->workspace); |
965 | } | 961 | } |
966 | 962 | ||
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 | 963 | ||
985 | void config_update_font_height(bool recalculate) { | 964 | void config_update_font_height(void) { |
986 | size_t prev_max_height = config->font_height; | 965 | int prev_max_height = config->font_height; |
987 | config->font_height = 0; | ||
988 | config->font_baseline = 0; | ||
989 | 966 | ||
990 | root_for_each_container(find_baseline_iterator, &recalculate); | 967 | get_text_metrics(config->font_description, &config->font_height, &config->font_baseline); |
991 | root_for_each_container(find_font_height_iterator, NULL); | ||
992 | 968 | ||
993 | if (config->font_height != prev_max_height) { | 969 | if (config->font_height != prev_max_height) { |
994 | arrange_root(); | 970 | arrange_root(); |
@@ -1022,8 +998,12 @@ void translate_keysyms(struct input_config *input_config) { | |||
1022 | 998 | ||
1023 | struct xkb_rule_names rules = {0}; | 999 | struct xkb_rule_names rules = {0}; |
1024 | input_config_fill_rule_names(input_config, &rules); | 1000 | input_config_fill_rule_names(input_config, &rules); |
1025 | config->keysym_translation_state = | 1001 | config->keysym_translation_state = keysym_translation_state_create(rules, 0); |
1026 | keysym_translation_state_create(rules); | 1002 | if (config->keysym_translation_state == NULL) { |
1003 | sway_log(SWAY_ERROR, "Failed to create keysym translation XKB state " | ||
1004 | "for device '%s'", input_config->identifier); | ||
1005 | return; | ||
1006 | } | ||
1027 | 1007 | ||
1028 | for (int i = 0; i < config->modes->length; ++i) { | 1008 | for (int i = 0; i < config->modes->length; ++i) { |
1029 | struct sway_mode *mode = config->modes->items[i]; | 1009 | struct sway_mode *mode = config->modes->items[i]; |
diff --git a/sway/config/bar.c b/sway/config/bar.c index 767534a6..908b2865 100644 --- a/sway/config/bar.c +++ b/sway/config/bar.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <signal.h> | 1 | #include <signal.h> |
3 | #include <stdbool.h> | 2 | #include <stdbool.h> |
4 | #include <stdio.h> | 3 | #include <stdio.h> |
@@ -91,7 +90,7 @@ struct bar_config *default_bar_config(void) { | |||
91 | } | 90 | } |
92 | bar->outputs = NULL; | 91 | bar->outputs = NULL; |
93 | bar->position = strdup("bottom"); | 92 | bar->position = strdup("bottom"); |
94 | bar->pango_markup = false; | 93 | bar->pango_markup = PANGO_MARKUP_DEFAULT; |
95 | bar->swaybar_command = NULL; | 94 | bar->swaybar_command = NULL; |
96 | bar->font = NULL; | 95 | bar->font = NULL; |
97 | bar->height = 0; | 96 | bar->height = 0; |
@@ -217,6 +216,9 @@ static void invoke_swaybar(struct bar_config *bar) { | |||
217 | sigset_t set; | 216 | sigset_t set; |
218 | sigemptyset(&set); | 217 | sigemptyset(&set); |
219 | sigprocmask(SIG_SETMASK, &set, NULL); | 218 | sigprocmask(SIG_SETMASK, &set, NULL); |
219 | signal(SIGPIPE, SIG_DFL); | ||
220 | |||
221 | restore_nofile_limit(); | ||
220 | 222 | ||
221 | pid = fork(); | 223 | pid = fork(); |
222 | if (pid < 0) { | 224 | if (pid < 0) { |
@@ -253,7 +255,6 @@ static void invoke_swaybar(struct bar_config *bar) { | |||
253 | } | 255 | } |
254 | 256 | ||
255 | sway_log(SWAY_DEBUG, "Spawned swaybar %s", bar->id); | 257 | sway_log(SWAY_DEBUG, "Spawned swaybar %s", bar->id); |
256 | return; | ||
257 | } | 258 | } |
258 | 259 | ||
259 | void load_swaybar(struct bar_config *bar) { | 260 | void load_swaybar(struct bar_config *bar) { |
diff --git a/sway/config/input.c b/sway/config/input.c index a998e170..de3b21ed 100644 --- a/sway/config/input.c +++ b/sway/config/input.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <stdlib.h> | 1 | #include <stdlib.h> |
3 | #include <limits.h> | 2 | #include <limits.h> |
4 | #include <float.h> | 3 | #include <float.h> |
@@ -25,14 +24,17 @@ struct input_config *new_input_config(const char* identifier) { | |||
25 | input->drag = INT_MIN; | 24 | input->drag = INT_MIN; |
26 | input->drag_lock = INT_MIN; | 25 | input->drag_lock = INT_MIN; |
27 | input->dwt = INT_MIN; | 26 | input->dwt = INT_MIN; |
27 | input->dwtp = INT_MIN; | ||
28 | input->send_events = INT_MIN; | 28 | input->send_events = INT_MIN; |
29 | input->click_method = INT_MIN; | 29 | input->click_method = INT_MIN; |
30 | input->middle_emulation = INT_MIN; | 30 | input->middle_emulation = INT_MIN; |
31 | input->natural_scroll = INT_MIN; | 31 | input->natural_scroll = INT_MIN; |
32 | input->accel_profile = INT_MIN; | 32 | input->accel_profile = INT_MIN; |
33 | input->rotation_angle = FLT_MIN; | ||
33 | input->pointer_accel = FLT_MIN; | 34 | input->pointer_accel = FLT_MIN; |
34 | input->scroll_factor = FLT_MIN; | 35 | input->scroll_factor = FLT_MIN; |
35 | input->scroll_button = INT_MIN; | 36 | input->scroll_button = INT_MIN; |
37 | input->scroll_button_lock = INT_MIN; | ||
36 | input->scroll_method = INT_MIN; | 38 | input->scroll_method = INT_MIN; |
37 | input->left_handed = INT_MIN; | 39 | input->left_handed = INT_MIN; |
38 | input->repeat_delay = INT_MIN; | 40 | input->repeat_delay = INT_MIN; |
@@ -61,6 +63,9 @@ void merge_input_config(struct input_config *dst, struct input_config *src) { | |||
61 | if (src->dwt != INT_MIN) { | 63 | if (src->dwt != INT_MIN) { |
62 | dst->dwt = src->dwt; | 64 | dst->dwt = src->dwt; |
63 | } | 65 | } |
66 | if (src->dwtp != INT_MIN) { | ||
67 | dst->dwtp = src->dwtp; | ||
68 | } | ||
64 | if (src->left_handed != INT_MIN) { | 69 | if (src->left_handed != INT_MIN) { |
65 | dst->left_handed = src->left_handed; | 70 | dst->left_handed = src->left_handed; |
66 | } | 71 | } |
@@ -70,6 +75,9 @@ void merge_input_config(struct input_config *dst, struct input_config *src) { | |||
70 | if (src->natural_scroll != INT_MIN) { | 75 | if (src->natural_scroll != INT_MIN) { |
71 | dst->natural_scroll = src->natural_scroll; | 76 | dst->natural_scroll = src->natural_scroll; |
72 | } | 77 | } |
78 | if (src->rotation_angle != FLT_MIN) { | ||
79 | dst->rotation_angle = src->rotation_angle; | ||
80 | } | ||
73 | if (src->pointer_accel != FLT_MIN) { | 81 | if (src->pointer_accel != FLT_MIN) { |
74 | dst->pointer_accel = src->pointer_accel; | 82 | dst->pointer_accel = src->pointer_accel; |
75 | } | 83 | } |
@@ -88,6 +96,9 @@ void merge_input_config(struct input_config *dst, struct input_config *src) { | |||
88 | if (src->scroll_button != INT_MIN) { | 96 | if (src->scroll_button != INT_MIN) { |
89 | dst->scroll_button = src->scroll_button; | 97 | dst->scroll_button = src->scroll_button; |
90 | } | 98 | } |
99 | if (src->scroll_button_lock != INT_MIN) { | ||
100 | dst->scroll_button_lock = src->scroll_button_lock; | ||
101 | } | ||
91 | if (src->send_events != INT_MIN) { | 102 | if (src->send_events != INT_MIN) { |
92 | dst->send_events = src->send_events; | 103 | dst->send_events = src->send_events; |
93 | } | 104 | } |
diff --git a/sway/config/output.c b/sway/config/output.c index c9ec6745..e7299459 100644 --- a/sway/config/output.c +++ b/sway/config/output.c | |||
@@ -1,13 +1,15 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <assert.h> | 1 | #include <assert.h> |
2 | #include <drm_fourcc.h> | ||
3 | #include <stdbool.h> | 3 | #include <stdbool.h> |
4 | #include <string.h> | 4 | #include <string.h> |
5 | #include <sys/socket.h> | 5 | #include <sys/socket.h> |
6 | #include <sys/wait.h> | 6 | #include <sys/wait.h> |
7 | #include <unistd.h> | 7 | #include <unistd.h> |
8 | #include <wlr/config.h> | ||
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/types/wlr_output_swapchain_manager.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" |
@@ -15,6 +17,10 @@ | |||
15 | #include "log.h" | 17 | #include "log.h" |
16 | #include "util.h" | 18 | #include "util.h" |
17 | 19 | ||
20 | #if WLR_HAS_DRM_BACKEND | ||
21 | #include <wlr/backend/drm.h> | ||
22 | #endif | ||
23 | |||
18 | int output_name_cmp(const void *item, const void *data) { | 24 | int output_name_cmp(const void *item, const void *data) { |
19 | const struct output_config *output = item; | 25 | const struct output_config *output = item; |
20 | const char *name = data; | 26 | const char *name = data; |
@@ -25,8 +31,10 @@ int output_name_cmp(const void *item, const void *data) { | |||
25 | void output_get_identifier(char *identifier, size_t len, | 31 | void output_get_identifier(char *identifier, size_t len, |
26 | struct sway_output *output) { | 32 | struct sway_output *output) { |
27 | struct wlr_output *wlr_output = output->wlr_output; | 33 | struct wlr_output *wlr_output = output->wlr_output; |
28 | snprintf(identifier, len, "%s %s %s", wlr_output->make, wlr_output->model, | 34 | snprintf(identifier, len, "%s %s %s", |
29 | wlr_output->serial); | 35 | wlr_output->make ? wlr_output->make : "Unknown", |
36 | wlr_output->model ? wlr_output->model : "Unknown", | ||
37 | wlr_output->serial ? wlr_output->serial : "Unknown"); | ||
30 | } | 38 | } |
31 | 39 | ||
32 | const char *sway_output_scale_filter_to_string(enum scale_filter_mode scale_filter) { | 40 | const char *sway_output_scale_filter_to_string(enum scale_filter_mode scale_filter) { |
@@ -58,6 +66,7 @@ struct output_config *new_output_config(const char *name) { | |||
58 | oc->width = oc->height = -1; | 66 | oc->width = oc->height = -1; |
59 | oc->refresh_rate = -1; | 67 | oc->refresh_rate = -1; |
60 | oc->custom_mode = -1; | 68 | oc->custom_mode = -1; |
69 | oc->drm_mode.type = -1; | ||
61 | oc->x = oc->y = -1; | 70 | oc->x = oc->y = -1; |
62 | oc->scale = -1; | 71 | oc->scale = -1; |
63 | oc->scale_filter = SCALE_FILTER_DEFAULT; | 72 | oc->scale_filter = SCALE_FILTER_DEFAULT; |
@@ -65,10 +74,77 @@ struct output_config *new_output_config(const char *name) { | |||
65 | oc->subpixel = WL_OUTPUT_SUBPIXEL_UNKNOWN; | 74 | oc->subpixel = WL_OUTPUT_SUBPIXEL_UNKNOWN; |
66 | oc->max_render_time = -1; | 75 | oc->max_render_time = -1; |
67 | oc->adaptive_sync = -1; | 76 | oc->adaptive_sync = -1; |
77 | oc->render_bit_depth = RENDER_BIT_DEPTH_DEFAULT; | ||
78 | oc->power = -1; | ||
68 | return oc; | 79 | return oc; |
69 | } | 80 | } |
70 | 81 | ||
71 | void merge_output_config(struct output_config *dst, struct output_config *src) { | 82 | // supersede_output_config clears all fields in dst that were set in src |
83 | static void supersede_output_config(struct output_config *dst, struct output_config *src) { | ||
84 | if (src->enabled != -1) { | ||
85 | dst->enabled = -1; | ||
86 | } | ||
87 | if (src->width != -1) { | ||
88 | dst->width = -1; | ||
89 | } | ||
90 | if (src->height != -1) { | ||
91 | dst->height = -1; | ||
92 | } | ||
93 | if (src->x != -1) { | ||
94 | dst->x = -1; | ||
95 | } | ||
96 | if (src->y != -1) { | ||
97 | dst->y = -1; | ||
98 | } | ||
99 | if (src->scale != -1) { | ||
100 | dst->scale = -1; | ||
101 | } | ||
102 | if (src->scale_filter != SCALE_FILTER_DEFAULT) { | ||
103 | dst->scale_filter = SCALE_FILTER_DEFAULT; | ||
104 | } | ||
105 | if (src->subpixel != WL_OUTPUT_SUBPIXEL_UNKNOWN) { | ||
106 | dst->subpixel = WL_OUTPUT_SUBPIXEL_UNKNOWN; | ||
107 | } | ||
108 | if (src->refresh_rate != -1) { | ||
109 | dst->refresh_rate = -1; | ||
110 | } | ||
111 | if (src->custom_mode != -1) { | ||
112 | dst->custom_mode = -1; | ||
113 | } | ||
114 | if (src->drm_mode.type != (uint32_t) -1) { | ||
115 | dst->drm_mode.type = -1; | ||
116 | } | ||
117 | if (src->transform != -1) { | ||
118 | dst->transform = -1; | ||
119 | } | ||
120 | if (src->max_render_time != -1) { | ||
121 | dst->max_render_time = -1; | ||
122 | } | ||
123 | if (src->adaptive_sync != -1) { | ||
124 | dst->adaptive_sync = -1; | ||
125 | } | ||
126 | if (src->render_bit_depth != RENDER_BIT_DEPTH_DEFAULT) { | ||
127 | dst->render_bit_depth = RENDER_BIT_DEPTH_DEFAULT; | ||
128 | } | ||
129 | if (src->background) { | ||
130 | free(dst->background); | ||
131 | dst->background = NULL; | ||
132 | } | ||
133 | if (src->background_option) { | ||
134 | free(dst->background_option); | ||
135 | dst->background_option = NULL; | ||
136 | } | ||
137 | if (src->background_fallback) { | ||
138 | free(dst->background_fallback); | ||
139 | dst->background_fallback = NULL; | ||
140 | } | ||
141 | if (src->power != -1) { | ||
142 | dst->power = -1; | ||
143 | } | ||
144 | } | ||
145 | |||
146 | // merge_output_config sets all fields in dst that were set in src | ||
147 | static void merge_output_config(struct output_config *dst, struct output_config *src) { | ||
72 | if (src->enabled != -1) { | 148 | if (src->enabled != -1) { |
73 | dst->enabled = src->enabled; | 149 | dst->enabled = src->enabled; |
74 | } | 150 | } |
@@ -99,6 +175,9 @@ void merge_output_config(struct output_config *dst, struct output_config *src) { | |||
99 | if (src->custom_mode != -1) { | 175 | if (src->custom_mode != -1) { |
100 | dst->custom_mode = src->custom_mode; | 176 | dst->custom_mode = src->custom_mode; |
101 | } | 177 | } |
178 | if (src->drm_mode.type != (uint32_t) -1) { | ||
179 | memcpy(&dst->drm_mode, &src->drm_mode, sizeof(src->drm_mode)); | ||
180 | } | ||
102 | if (src->transform != -1) { | 181 | if (src->transform != -1) { |
103 | dst->transform = src->transform; | 182 | dst->transform = src->transform; |
104 | } | 183 | } |
@@ -108,6 +187,9 @@ void merge_output_config(struct output_config *dst, struct output_config *src) { | |||
108 | if (src->adaptive_sync != -1) { | 187 | if (src->adaptive_sync != -1) { |
109 | dst->adaptive_sync = src->adaptive_sync; | 188 | dst->adaptive_sync = src->adaptive_sync; |
110 | } | 189 | } |
190 | if (src->render_bit_depth != RENDER_BIT_DEPTH_DEFAULT) { | ||
191 | dst->render_bit_depth = src->render_bit_depth; | ||
192 | } | ||
111 | if (src->background) { | 193 | if (src->background) { |
112 | free(dst->background); | 194 | free(dst->background); |
113 | dst->background = strdup(src->background); | 195 | dst->background = strdup(src->background); |
@@ -120,155 +202,124 @@ void merge_output_config(struct output_config *dst, struct output_config *src) { | |||
120 | free(dst->background_fallback); | 202 | free(dst->background_fallback); |
121 | dst->background_fallback = strdup(src->background_fallback); | 203 | dst->background_fallback = strdup(src->background_fallback); |
122 | } | 204 | } |
123 | if (src->dpms_state != 0) { | 205 | if (src->power != -1) { |
124 | dst->dpms_state = src->dpms_state; | 206 | dst->power = src->power; |
125 | } | 207 | } |
126 | } | 208 | } |
127 | 209 | ||
128 | static void merge_wildcard_on_all(struct output_config *wildcard) { | 210 | void store_output_config(struct output_config *oc) { |
129 | for (int i = 0; i < config->output_configs->length; i++) { | 211 | bool merged = false; |
130 | struct output_config *oc = config->output_configs->items[i]; | 212 | bool wildcard = strcmp(oc->name, "*") == 0; |
131 | if (strcmp(wildcard->name, oc->name) != 0) { | 213 | struct sway_output *output = wildcard ? NULL : all_output_by_name_or_id(oc->name); |
132 | sway_log(SWAY_DEBUG, "Merging output * config on %s", oc->name); | ||
133 | merge_output_config(oc, wildcard); | ||
134 | } | ||
135 | } | ||
136 | } | ||
137 | 214 | ||
138 | static void merge_id_on_name(struct output_config *oc) { | ||
139 | char *id_on_name = NULL; | ||
140 | char id[128]; | 215 | char id[128]; |
141 | char *name = NULL; | 216 | if (output) { |
142 | struct sway_output *output; | ||
143 | wl_list_for_each(output, &root->all_outputs, link) { | ||
144 | name = output->wlr_output->name; | ||
145 | output_get_identifier(id, sizeof(id), output); | 217 | output_get_identifier(id, sizeof(id), output); |
146 | if (strcmp(name, oc->name) == 0 || strcmp(id, oc->name) == 0) { | ||
147 | size_t length = snprintf(NULL, 0, "%s on %s", id, name) + 1; | ||
148 | id_on_name = malloc(length); | ||
149 | if (!id_on_name) { | ||
150 | sway_log(SWAY_ERROR, "Failed to allocate id on name string"); | ||
151 | return; | ||
152 | } | ||
153 | snprintf(id_on_name, length, "%s on %s", id, name); | ||
154 | break; | ||
155 | } | ||
156 | } | ||
157 | |||
158 | if (!id_on_name) { | ||
159 | return; | ||
160 | } | 218 | } |
161 | 219 | ||
162 | int i = list_seq_find(config->output_configs, output_name_cmp, id_on_name); | 220 | for (int i = 0; i < config->output_configs->length; i++) { |
163 | if (i >= 0) { | 221 | struct output_config *old = config->output_configs->items[i]; |
164 | sway_log(SWAY_DEBUG, "Merging on top of existing id on name config"); | 222 | |
165 | merge_output_config(config->output_configs->items[i], oc); | 223 | // If the old config matches the new config's name, regardless of |
166 | } else { | 224 | // whether it was name or identifier, merge on top of the existing |
167 | // If both a name and identifier config, exist generate an id on name | 225 | // config. If the new config is a wildcard, this also merges on top of |
168 | int ni = list_seq_find(config->output_configs, output_name_cmp, name); | 226 | // old wildcard configs. |
169 | int ii = list_seq_find(config->output_configs, output_name_cmp, id); | 227 | if (strcmp(old->name, oc->name) == 0) { |
170 | if ((ni >= 0 && ii >= 0) || (ni >= 0 && strcmp(oc->name, id) == 0) | 228 | merge_output_config(old, oc); |
171 | || (ii >= 0 && strcmp(oc->name, name) == 0)) { | 229 | merged = true; |
172 | struct output_config *ion_oc = new_output_config(id_on_name); | 230 | continue; |
173 | if (ni >= 0) { | ||
174 | merge_output_config(ion_oc, config->output_configs->items[ni]); | ||
175 | } | ||
176 | if (ii >= 0) { | ||
177 | merge_output_config(ion_oc, config->output_configs->items[ii]); | ||
178 | } | ||
179 | merge_output_config(ion_oc, oc); | ||
180 | list_add(config->output_configs, ion_oc); | ||
181 | sway_log(SWAY_DEBUG, "Generated id on name output config \"%s\"" | ||
182 | " (enabled: %d) (%dx%d@%fHz position %d,%d scale %f " | ||
183 | "transform %d) (bg %s %s) (dpms %d) (max render time: %d)", | ||
184 | 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, | ||
186 | ion_oc->transform, ion_oc->background, | ||
187 | ion_oc->background_option, ion_oc->dpms_state, | ||
188 | ion_oc->max_render_time); | ||
189 | } | 231 | } |
190 | } | ||
191 | free(id_on_name); | ||
192 | } | ||
193 | 232 | ||
194 | struct output_config *store_output_config(struct output_config *oc) { | 233 | // If the new config is a wildcard config we supersede all non-wildcard |
195 | bool wildcard = strcmp(oc->name, "*") == 0; | 234 | // configs. Old wildcard configs have already been handled above. |
196 | if (wildcard) { | 235 | if (wildcard) { |
197 | merge_wildcard_on_all(oc); | 236 | supersede_output_config(old, oc); |
198 | } else { | 237 | continue; |
199 | merge_id_on_name(oc); | 238 | } |
200 | } | ||
201 | 239 | ||
202 | int i = list_seq_find(config->output_configs, output_name_cmp, oc->name); | 240 | // If the new config matches an output's name, and the old config |
203 | if (i >= 0) { | 241 | // matches on that output's identifier, supersede it. |
204 | sway_log(SWAY_DEBUG, "Merging on top of existing output config"); | 242 | if (output && strcmp(old->name, id) == 0 && |
205 | struct output_config *current = config->output_configs->items[i]; | 243 | strcmp(oc->name, output->wlr_output->name) == 0) { |
206 | merge_output_config(current, oc); | 244 | supersede_output_config(old, oc); |
207 | free_output_config(oc); | ||
208 | oc = current; | ||
209 | } else if (!wildcard) { | ||
210 | sway_log(SWAY_DEBUG, "Adding non-wildcard output config"); | ||
211 | i = list_seq_find(config->output_configs, output_name_cmp, "*"); | ||
212 | if (i >= 0) { | ||
213 | sway_log(SWAY_DEBUG, "Merging on top of output * config"); | ||
214 | struct output_config *current = new_output_config(oc->name); | ||
215 | merge_output_config(current, config->output_configs->items[i]); | ||
216 | merge_output_config(current, oc); | ||
217 | free_output_config(oc); | ||
218 | oc = current; | ||
219 | } | 245 | } |
220 | list_add(config->output_configs, oc); | ||
221 | } else { | ||
222 | // New wildcard config. Just add it | ||
223 | sway_log(SWAY_DEBUG, "Adding output * config"); | ||
224 | list_add(config->output_configs, oc); | ||
225 | } | 246 | } |
226 | 247 | ||
227 | sway_log(SWAY_DEBUG, "Config stored for output %s (enabled: %d) (%dx%d@%fHz " | 248 | 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) " | 249 | "position %d,%d scale %f subpixel %s transform %d) (bg %s %s) (power %d) " |
229 | "(max render time: %d)", | 250 | "(max render time: %d)", |
230 | oc->name, oc->enabled, oc->width, oc->height, oc->refresh_rate, | 251 | 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), | 252 | 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, | 253 | oc->transform, oc->background, oc->background_option, oc->power, |
233 | oc->max_render_time); | 254 | oc->max_render_time); |
234 | 255 | ||
235 | return oc; | 256 | // If the configuration was not merged into an existing configuration, add |
257 | // it to the list. Otherwise we're done with it and can free it. | ||
258 | if (!merged) { | ||
259 | list_add(config->output_configs, oc); | ||
260 | } else { | ||
261 | free_output_config(oc); | ||
262 | } | ||
236 | } | 263 | } |
237 | 264 | ||
238 | static void set_mode(struct wlr_output *output, int width, int height, | 265 | static void set_mode(struct wlr_output *output, struct wlr_output_state *pending, |
239 | float refresh_rate, bool custom) { | 266 | int width, int height, float refresh_rate, bool custom) { |
240 | // Not all floating point integers can be represented exactly | 267 | // Not all floating point integers can be represented exactly |
241 | // as (int)(1000 * mHz / 1000.f) | 268 | // as (int)(1000 * mHz / 1000.f) |
242 | // round() the result to avoid any error | 269 | // round() the result to avoid any error |
243 | int mhz = (int)round(refresh_rate * 1000); | 270 | int mhz = (int)roundf(refresh_rate * 1000); |
271 | // If no target refresh rate is given, match highest available | ||
272 | mhz = mhz <= 0 ? INT_MAX : mhz; | ||
244 | 273 | ||
245 | if (wl_list_empty(&output->modes) || custom) { | 274 | if (wl_list_empty(&output->modes) || custom) { |
246 | sway_log(SWAY_DEBUG, "Assigning custom mode to %s", output->name); | 275 | sway_log(SWAY_DEBUG, "Assigning custom mode to %s", output->name); |
247 | wlr_output_set_custom_mode(output, width, height, | 276 | wlr_output_state_set_custom_mode(pending, width, height, |
248 | refresh_rate > 0 ? mhz : 0); | 277 | refresh_rate > 0 ? mhz : 0); |
249 | return; | 278 | return; |
250 | } | 279 | } |
251 | 280 | ||
252 | struct wlr_output_mode *mode, *best = NULL; | 281 | struct wlr_output_mode *mode, *best = NULL; |
282 | int best_diff_mhz = INT_MAX; | ||
253 | wl_list_for_each(mode, &output->modes, link) { | 283 | wl_list_for_each(mode, &output->modes, link) { |
254 | if (mode->width == width && mode->height == height) { | 284 | if (mode->width == width && mode->height == height) { |
255 | if (mode->refresh == mhz) { | 285 | int diff_mhz = abs(mode->refresh - mhz); |
256 | best = mode; | 286 | if (diff_mhz < best_diff_mhz) { |
257 | break; | 287 | best_diff_mhz = diff_mhz; |
258 | } | ||
259 | if (best == NULL || mode->refresh > best->refresh) { | ||
260 | best = mode; | 288 | best = mode; |
289 | if (best_diff_mhz == 0) { | ||
290 | break; | ||
291 | } | ||
261 | } | 292 | } |
262 | } | 293 | } |
263 | } | 294 | } |
264 | if (!best) { | 295 | if (best) { |
265 | sway_log(SWAY_ERROR, "Configured mode for %s not available", output->name); | 296 | sway_log(SWAY_INFO, "Assigning configured mode (%dx%d@%.3fHz) to %s", |
266 | sway_log(SWAY_INFO, "Picking preferred mode instead"); | 297 | best->width, best->height, best->refresh / 1000.f, output->name); |
267 | best = wlr_output_preferred_mode(output); | ||
268 | } else { | 298 | } else { |
269 | sway_log(SWAY_DEBUG, "Assigning configured mode to %s", output->name); | 299 | best = wlr_output_preferred_mode(output); |
300 | sway_log(SWAY_INFO, "Configured mode (%dx%d@%.3fHz) not available, " | ||
301 | "applying preferred mode (%dx%d@%.3fHz)", | ||
302 | width, height, refresh_rate, | ||
303 | best->width, best->height, best->refresh / 1000.f); | ||
304 | } | ||
305 | wlr_output_state_set_mode(pending, best); | ||
306 | } | ||
307 | |||
308 | static void set_modeline(struct wlr_output *output, | ||
309 | struct wlr_output_state *pending, drmModeModeInfo *drm_mode) { | ||
310 | #if WLR_HAS_DRM_BACKEND | ||
311 | if (!wlr_output_is_drm(output)) { | ||
312 | sway_log(SWAY_ERROR, "Modeline can only be set to DRM output"); | ||
313 | return; | ||
314 | } | ||
315 | sway_log(SWAY_DEBUG, "Assigning custom modeline to %s", output->name); | ||
316 | struct wlr_output_mode *mode = wlr_drm_connector_add_mode(output, drm_mode); | ||
317 | if (mode) { | ||
318 | wlr_output_state_set_mode(pending, mode); | ||
270 | } | 319 | } |
271 | wlr_output_set_mode(output, best); | 320 | #else |
321 | sway_log(SWAY_ERROR, "Modeline can only be set to DRM output"); | ||
322 | #endif | ||
272 | } | 323 | } |
273 | 324 | ||
274 | /* Some manufacturers hardcode the aspect-ratio of the output in the physical | 325 | /* Some manufacturers hardcode the aspect-ratio of the output in the physical |
@@ -289,23 +340,24 @@ static bool phys_size_is_aspect_ratio(struct wlr_output *output) { | |||
289 | // 1 inch = 25.4 mm | 340 | // 1 inch = 25.4 mm |
290 | #define MM_PER_INCH 25.4 | 341 | #define MM_PER_INCH 25.4 |
291 | 342 | ||
292 | static int compute_default_scale(struct wlr_output *output) { | 343 | static int compute_default_scale(struct wlr_output *output, |
344 | struct wlr_output_state *pending) { | ||
293 | struct wlr_box box = { .width = output->width, .height = output->height }; | 345 | struct wlr_box box = { .width = output->width, .height = output->height }; |
294 | if (output->pending.committed & WLR_OUTPUT_STATE_MODE) { | 346 | if (pending->committed & WLR_OUTPUT_STATE_MODE) { |
295 | switch (output->pending.mode_type) { | 347 | switch (pending->mode_type) { |
296 | case WLR_OUTPUT_STATE_MODE_FIXED: | 348 | case WLR_OUTPUT_STATE_MODE_FIXED: |
297 | box.width = output->pending.mode->width; | 349 | box.width = pending->mode->width; |
298 | box.height = output->pending.mode->height; | 350 | box.height = pending->mode->height; |
299 | break; | 351 | break; |
300 | case WLR_OUTPUT_STATE_MODE_CUSTOM: | 352 | case WLR_OUTPUT_STATE_MODE_CUSTOM: |
301 | box.width = output->pending.custom_mode.width; | 353 | box.width = pending->custom_mode.width; |
302 | box.height = output->pending.custom_mode.height; | 354 | box.height = pending->custom_mode.height; |
303 | break; | 355 | break; |
304 | } | 356 | } |
305 | } | 357 | } |
306 | enum wl_output_transform transform = output->transform; | 358 | enum wl_output_transform transform = output->transform; |
307 | if (output->pending.committed & WLR_OUTPUT_STATE_TRANSFORM) { | 359 | if (pending->committed & WLR_OUTPUT_STATE_TRANSFORM) { |
308 | transform = output->pending.transform; | 360 | transform = pending->transform; |
309 | } | 361 | } |
310 | wlr_box_transform(&box, &box, transform, box.width, box.height); | 362 | wlr_box_transform(&box, &box, transform, box.width, box.height); |
311 | 363 | ||
@@ -334,42 +386,70 @@ static int compute_default_scale(struct wlr_output *output) { | |||
334 | return 2; | 386 | return 2; |
335 | } | 387 | } |
336 | 388 | ||
389 | static bool render_format_is_10bit(uint32_t render_format) { | ||
390 | return render_format == DRM_FORMAT_XRGB2101010 || | ||
391 | render_format == DRM_FORMAT_XBGR2101010; | ||
392 | } | ||
393 | |||
394 | static bool render_format_is_bgr(uint32_t fmt) { | ||
395 | return fmt == DRM_FORMAT_XBGR2101010 || fmt == DRM_FORMAT_XBGR8888; | ||
396 | } | ||
397 | |||
398 | static bool output_config_is_disabling(struct output_config *oc) { | ||
399 | return oc && (!oc->enabled || oc->power == 0); | ||
400 | } | ||
401 | |||
337 | static void queue_output_config(struct output_config *oc, | 402 | static void queue_output_config(struct output_config *oc, |
338 | struct sway_output *output) { | 403 | struct sway_output *output, struct wlr_output_state *pending) { |
339 | if (output == root->noop_output) { | 404 | if (output == root->fallback_output) { |
340 | return; | 405 | return; |
341 | } | 406 | } |
342 | 407 | ||
343 | struct wlr_output *wlr_output = output->wlr_output; | 408 | struct wlr_output *wlr_output = output->wlr_output; |
344 | 409 | ||
345 | if (oc && (!oc->enabled || oc->dpms_state == DPMS_OFF)) { | 410 | if (output_config_is_disabling(oc)) { |
346 | sway_log(SWAY_DEBUG, "Turning off output %s", wlr_output->name); | 411 | sway_log(SWAY_DEBUG, "Turning off output %s", wlr_output->name); |
347 | wlr_output_enable(wlr_output, false); | 412 | wlr_output_state_set_enabled(pending, false); |
348 | return; | 413 | return; |
349 | } | 414 | } |
350 | 415 | ||
351 | sway_log(SWAY_DEBUG, "Turning on output %s", wlr_output->name); | 416 | sway_log(SWAY_DEBUG, "Turning on output %s", wlr_output->name); |
352 | wlr_output_enable(wlr_output, true); | 417 | wlr_output_state_set_enabled(pending, true); |
353 | 418 | ||
354 | if (oc && oc->width > 0 && oc->height > 0) { | 419 | if (oc && oc->drm_mode.type != 0 && oc->drm_mode.type != (uint32_t) -1) { |
420 | sway_log(SWAY_DEBUG, "Set %s modeline", | ||
421 | wlr_output->name); | ||
422 | set_modeline(wlr_output, pending, &oc->drm_mode); | ||
423 | } else if (oc && oc->width > 0 && oc->height > 0) { | ||
355 | sway_log(SWAY_DEBUG, "Set %s mode to %dx%d (%f Hz)", | 424 | sway_log(SWAY_DEBUG, "Set %s mode to %dx%d (%f Hz)", |
356 | wlr_output->name, oc->width, oc->height, oc->refresh_rate); | 425 | wlr_output->name, oc->width, oc->height, oc->refresh_rate); |
357 | set_mode(wlr_output, oc->width, oc->height, | 426 | set_mode(wlr_output, pending, oc->width, oc->height, |
358 | oc->refresh_rate, oc->custom_mode == 1); | 427 | oc->refresh_rate, oc->custom_mode == 1); |
359 | } else if (!wl_list_empty(&wlr_output->modes)) { | 428 | } else if (!wl_list_empty(&wlr_output->modes)) { |
360 | struct wlr_output_mode *mode = wlr_output_preferred_mode(wlr_output); | 429 | sway_log(SWAY_DEBUG, "Set preferred mode"); |
361 | wlr_output_set_mode(wlr_output, mode); | 430 | struct wlr_output_mode *preferred_mode = |
431 | wlr_output_preferred_mode(wlr_output); | ||
432 | wlr_output_state_set_mode(pending, preferred_mode); | ||
362 | } | 433 | } |
363 | 434 | ||
364 | if (oc && (oc->subpixel != WL_OUTPUT_SUBPIXEL_UNKNOWN || config->reloading)) { | 435 | if (oc && (oc->subpixel != WL_OUTPUT_SUBPIXEL_UNKNOWN || config->reloading)) { |
365 | sway_log(SWAY_DEBUG, "Set %s subpixel to %s", oc->name, | 436 | sway_log(SWAY_DEBUG, "Set %s subpixel to %s", oc->name, |
366 | sway_wl_output_subpixel_to_string(oc->subpixel)); | 437 | sway_wl_output_subpixel_to_string(oc->subpixel)); |
367 | wlr_output_set_subpixel(wlr_output, oc->subpixel); | 438 | wlr_output_state_set_subpixel(pending, oc->subpixel); |
368 | } | 439 | } |
369 | 440 | ||
441 | enum wl_output_transform tr = WL_OUTPUT_TRANSFORM_NORMAL; | ||
370 | if (oc && oc->transform >= 0) { | 442 | if (oc && oc->transform >= 0) { |
371 | sway_log(SWAY_DEBUG, "Set %s transform to %d", oc->name, oc->transform); | 443 | tr = oc->transform; |
372 | wlr_output_set_transform(wlr_output, oc->transform); | 444 | #if WLR_HAS_DRM_BACKEND |
445 | } else if (wlr_output_is_drm(wlr_output)) { | ||
446 | tr = wlr_drm_connector_get_panel_orientation(wlr_output); | ||
447 | sway_log(SWAY_DEBUG, "Auto-detected output transform: %d", tr); | ||
448 | #endif | ||
449 | } | ||
450 | if (wlr_output->transform != tr) { | ||
451 | sway_log(SWAY_DEBUG, "Set %s transform to %d", wlr_output->name, tr); | ||
452 | wlr_output_state_set_transform(pending, tr); | ||
373 | } | 453 | } |
374 | 454 | ||
375 | // Apply the scale last before the commit, because the scale auto-detection | 455 | // Apply the scale last before the commit, because the scale auto-detection |
@@ -377,50 +457,50 @@ static void queue_output_config(struct output_config *oc, | |||
377 | float scale; | 457 | float scale; |
378 | if (oc && oc->scale > 0) { | 458 | if (oc && oc->scale > 0) { |
379 | scale = oc->scale; | 459 | scale = oc->scale; |
460 | |||
461 | // The factional-scale-v1 protocol uses increments of 120ths to send | ||
462 | // the scale factor to the client. Adjust the scale so that we use the | ||
463 | // same value as the clients'. | ||
464 | float adjusted_scale = round(scale * 120) / 120; | ||
465 | if (scale != adjusted_scale) { | ||
466 | sway_log(SWAY_INFO, "Adjusting output scale from %f to %f", | ||
467 | scale, adjusted_scale); | ||
468 | scale = adjusted_scale; | ||
469 | } | ||
380 | } else { | 470 | } else { |
381 | scale = compute_default_scale(wlr_output); | 471 | scale = compute_default_scale(wlr_output, pending); |
382 | sway_log(SWAY_DEBUG, "Auto-detected output scale: %f", scale); | 472 | sway_log(SWAY_DEBUG, "Auto-detected output scale: %f", scale); |
383 | } | 473 | } |
384 | if (scale != wlr_output->scale) { | 474 | if (scale != wlr_output->scale) { |
385 | sway_log(SWAY_DEBUG, "Set %s scale to %f", wlr_output->name, scale); | 475 | sway_log(SWAY_DEBUG, "Set %s scale to %f", wlr_output->name, scale); |
386 | wlr_output_set_scale(wlr_output, scale); | 476 | wlr_output_state_set_scale(pending, scale); |
387 | } | 477 | } |
388 | 478 | ||
389 | if (oc && oc->adaptive_sync != -1) { | 479 | if (oc && oc->adaptive_sync != -1) { |
390 | sway_log(SWAY_DEBUG, "Set %s adaptive sync to %d", wlr_output->name, | 480 | sway_log(SWAY_DEBUG, "Set %s adaptive sync to %d", wlr_output->name, |
391 | oc->adaptive_sync); | 481 | oc->adaptive_sync); |
392 | wlr_output_enable_adaptive_sync(wlr_output, oc->adaptive_sync == 1); | 482 | wlr_output_state_set_adaptive_sync_enabled(pending, oc->adaptive_sync == 1); |
393 | } | ||
394 | } | ||
395 | |||
396 | bool apply_output_config(struct output_config *oc, struct sway_output *output) { | ||
397 | if (output == root->noop_output) { | ||
398 | return false; | ||
399 | } | 483 | } |
400 | 484 | ||
401 | struct wlr_output *wlr_output = output->wlr_output; | 485 | if (oc && oc->render_bit_depth != RENDER_BIT_DEPTH_DEFAULT) { |
402 | 486 | if (oc->render_bit_depth == RENDER_BIT_DEPTH_10 && | |
403 | // Flag to prevent the output mode event handler from calling us | 487 | render_format_is_10bit(output->wlr_output->render_format)) { |
404 | output->enabling = (!oc || oc->enabled); | 488 | // 10-bit was set successfully before, try to save some tests by reusing the format |
405 | 489 | wlr_output_state_set_render_format(pending, output->wlr_output->render_format); | |
406 | queue_output_config(oc, output); | 490 | } else if (oc->render_bit_depth == RENDER_BIT_DEPTH_10) { |
407 | 491 | wlr_output_state_set_render_format(pending, DRM_FORMAT_XRGB2101010); | |
408 | if (!oc || oc->dpms_state != DPMS_OFF) { | 492 | } else { |
409 | output->current_mode = wlr_output->pending.mode; | 493 | wlr_output_state_set_render_format(pending, DRM_FORMAT_XRGB8888); |
494 | } | ||
410 | } | 495 | } |
496 | } | ||
411 | 497 | ||
412 | sway_log(SWAY_DEBUG, "Committing output %s", wlr_output->name); | 498 | static bool finalize_output_config(struct output_config *oc, struct sway_output *output) { |
413 | if (!wlr_output_commit(wlr_output)) { | 499 | if (output == root->fallback_output) { |
414 | // 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 | ||
416 | // the mode we asked for. | ||
417 | sway_log(SWAY_ERROR, "Failed to commit output %s", wlr_output->name); | ||
418 | output->enabling = false; | ||
419 | return false; | 500 | return false; |
420 | } | 501 | } |
421 | 502 | ||
422 | output->enabling = false; | 503 | struct wlr_output *wlr_output = output->wlr_output; |
423 | |||
424 | if (oc && !oc->enabled) { | 504 | if (oc && !oc->enabled) { |
425 | sway_log(SWAY_DEBUG, "Disabling output %s", oc->name); | 505 | sway_log(SWAY_DEBUG, "Disabling output %s", oc->name); |
426 | if (output->enabled) { | 506 | if (output->enabled) { |
@@ -430,10 +510,6 @@ bool apply_output_config(struct output_config *oc, struct sway_output *output) { | |||
430 | return true; | 510 | return true; |
431 | } | 511 | } |
432 | 512 | ||
433 | if (config->reloading) { | ||
434 | output_damage_whole(output); | ||
435 | } | ||
436 | |||
437 | if (oc) { | 513 | if (oc) { |
438 | enum scale_filter_mode scale_filter_old = output->scale_filter; | 514 | enum scale_filter_mode scale_filter_old = output->scale_filter; |
439 | switch (oc->scale_filter) { | 515 | switch (oc->scale_filter) { |
@@ -450,6 +526,7 @@ bool apply_output_config(struct output_config *oc, struct sway_output *output) { | |||
450 | if (scale_filter_old != output->scale_filter) { | 526 | if (scale_filter_old != output->scale_filter) { |
451 | sway_log(SWAY_DEBUG, "Set %s scale_filter to %s", oc->name, | 527 | sway_log(SWAY_DEBUG, "Set %s scale_filter to %s", oc->name, |
452 | sway_output_scale_filter_to_string(output->scale_filter)); | 528 | sway_output_scale_filter_to_string(output->scale_filter)); |
529 | wlr_damage_ring_add_whole(&output->scene_output->damage_ring); | ||
453 | } | 530 | } |
454 | } | 531 | } |
455 | 532 | ||
@@ -462,12 +539,12 @@ bool apply_output_config(struct output_config *oc, struct sway_output *output) { | |||
462 | } | 539 | } |
463 | 540 | ||
464 | // Update output->{lx, ly, width, height} | 541 | // Update output->{lx, ly, width, height} |
465 | struct wlr_box *output_box = | 542 | struct wlr_box output_box; |
466 | wlr_output_layout_get_box(root->output_layout, wlr_output); | 543 | wlr_output_layout_get_box(root->output_layout, wlr_output, &output_box); |
467 | output->lx = output_box->x; | 544 | output->lx = output_box.x; |
468 | output->ly = output_box->y; | 545 | output->ly = output_box.y; |
469 | output->width = output_box->width; | 546 | output->width = output_box.width; |
470 | output->height = output_box->height; | 547 | output->height = output_box.height; |
471 | 548 | ||
472 | if (!output->enabled) { | 549 | if (!output->enabled) { |
473 | output_enable(output); | 550 | output_enable(output); |
@@ -479,27 +556,13 @@ bool apply_output_config(struct output_config *oc, struct sway_output *output) { | |||
479 | output->max_render_time = oc->max_render_time; | 556 | output->max_render_time = oc->max_render_time; |
480 | } | 557 | } |
481 | 558 | ||
482 | // Reconfigure all devices, since input config may have been applied before | ||
483 | // this output came online, and some config items (like map_to_output) are | ||
484 | // dependent on an output being present. | ||
485 | input_manager_configure_all_inputs(); | ||
486 | return true; | 559 | return true; |
487 | } | 560 | } |
488 | 561 | ||
489 | bool test_output_config(struct output_config *oc, struct sway_output *output) { | ||
490 | if (output == root->noop_output) { | ||
491 | return false; | ||
492 | } | ||
493 | |||
494 | queue_output_config(oc, output); | ||
495 | bool ok = wlr_output_test(output->wlr_output); | ||
496 | wlr_output_rollback(output->wlr_output); | ||
497 | return ok; | ||
498 | } | ||
499 | |||
500 | static void default_output_config(struct output_config *oc, | 562 | static void default_output_config(struct output_config *oc, |
501 | struct wlr_output *wlr_output) { | 563 | struct wlr_output *wlr_output) { |
502 | oc->enabled = 1; | 564 | oc->enabled = 1; |
565 | oc->power = 1; | ||
503 | struct wlr_output_mode *mode = wlr_output_preferred_mode(wlr_output); | 566 | struct wlr_output_mode *mode = wlr_output_preferred_mode(wlr_output); |
504 | if (mode != NULL) { | 567 | if (mode != NULL) { |
505 | oc->width = mode->width; | 568 | oc->width = mode->width; |
@@ -512,147 +575,418 @@ static void default_output_config(struct output_config *oc, | |||
512 | struct sway_output *output = wlr_output->data; | 575 | struct sway_output *output = wlr_output->data; |
513 | oc->subpixel = output->detected_subpixel; | 576 | oc->subpixel = output->detected_subpixel; |
514 | oc->transform = WL_OUTPUT_TRANSFORM_NORMAL; | 577 | oc->transform = WL_OUTPUT_TRANSFORM_NORMAL; |
515 | oc->dpms_state = DPMS_ON; | ||
516 | oc->max_render_time = 0; | 578 | oc->max_render_time = 0; |
517 | } | 579 | } |
518 | 580 | ||
519 | static struct output_config *get_output_config(char *identifier, | 581 | // find_output_config returns a merged output_config containing all stored |
520 | struct sway_output *sway_output) { | 582 | // configuration that applies to the specified output. |
583 | struct output_config *find_output_config(struct sway_output *sway_output) { | ||
521 | const char *name = sway_output->wlr_output->name; | 584 | const char *name = sway_output->wlr_output->name; |
585 | struct output_config *oc = NULL; | ||
586 | |||
587 | struct output_config *result = new_output_config(name); | ||
588 | if (config->reloading) { | ||
589 | default_output_config(result, sway_output->wlr_output); | ||
590 | } | ||
522 | 591 | ||
523 | struct output_config *oc_id_on_name = NULL; | 592 | char id[128]; |
524 | struct output_config *oc_name = NULL; | 593 | output_get_identifier(id, sizeof(id), sway_output); |
525 | struct output_config *oc_id = NULL; | ||
526 | 594 | ||
527 | size_t length = snprintf(NULL, 0, "%s on %s", identifier, name) + 1; | 595 | int i; |
528 | char *id_on_name = malloc(length); | 596 | bool match = false; |
529 | snprintf(id_on_name, length, "%s on %s", identifier, name); | 597 | if ((i = list_seq_find(config->output_configs, output_name_cmp, "*")) >= 0) { |
530 | int i = list_seq_find(config->output_configs, output_name_cmp, id_on_name); | 598 | match = true; |
531 | if (i >= 0) { | 599 | oc = config->output_configs->items[i]; |
532 | oc_id_on_name = config->output_configs->items[i]; | 600 | merge_output_config(result, oc); |
533 | } else { | 601 | } |
534 | i = list_seq_find(config->output_configs, output_name_cmp, name); | 602 | if ((i = list_seq_find(config->output_configs, output_name_cmp, name)) >= 0) { |
535 | if (i >= 0) { | 603 | match = true; |
536 | oc_name = config->output_configs->items[i]; | 604 | oc = config->output_configs->items[i]; |
605 | merge_output_config(result, oc); | ||
606 | } | ||
607 | if ((i = list_seq_find(config->output_configs, output_name_cmp, id)) >= 0) { | ||
608 | match = true; | ||
609 | oc = config->output_configs->items[i]; | ||
610 | merge_output_config(result, oc); | ||
611 | } | ||
612 | |||
613 | if (!match && !config->reloading) { | ||
614 | // No name, identifier, or wildcard config. Since we are not | ||
615 | // reloading with defaults, the output config will be empty, so | ||
616 | // just return NULL | ||
617 | free_output_config(result); | ||
618 | return NULL; | ||
619 | } | ||
620 | |||
621 | return result; | ||
622 | } | ||
623 | |||
624 | static bool config_has_auto_mode(struct output_config *oc) { | ||
625 | if (!oc) { | ||
626 | return true; | ||
627 | } | ||
628 | if (oc->drm_mode.type != 0 && oc->drm_mode.type != (uint32_t)-1) { | ||
629 | return true; | ||
630 | } else if (oc->width > 0 && oc->height > 0) { | ||
631 | return true; | ||
632 | } | ||
633 | return false; | ||
634 | } | ||
635 | |||
636 | struct search_context { | ||
637 | struct wlr_output_swapchain_manager *swapchain_mgr; | ||
638 | struct wlr_backend_output_state *states; | ||
639 | struct matched_output_config *configs; | ||
640 | size_t configs_len; | ||
641 | bool degrade_to_off; | ||
642 | }; | ||
643 | |||
644 | static void dump_output_state(struct wlr_output *wlr_output, struct wlr_output_state *state) { | ||
645 | sway_log(SWAY_DEBUG, "Output state for %s", wlr_output->name); | ||
646 | if (state->committed & WLR_OUTPUT_STATE_ENABLED) { | ||
647 | sway_log(SWAY_DEBUG, " enabled: %s", state->enabled ? "yes" : "no"); | ||
648 | } | ||
649 | if (state->committed & WLR_OUTPUT_STATE_RENDER_FORMAT) { | ||
650 | sway_log(SWAY_DEBUG, " render_format: %d", state->render_format); | ||
651 | } | ||
652 | if (state->committed & WLR_OUTPUT_STATE_MODE) { | ||
653 | if (state->mode_type == WLR_OUTPUT_STATE_MODE_CUSTOM) { | ||
654 | sway_log(SWAY_DEBUG, " custom mode: %dx%d@%dmHz", | ||
655 | state->custom_mode.width, state->custom_mode.height, state->custom_mode.refresh); | ||
656 | } else { | ||
657 | sway_log(SWAY_DEBUG, " mode: %dx%d@%dmHz%s", | ||
658 | state->mode->width, state->mode->height, state->mode->refresh, | ||
659 | state->mode->preferred ? " (preferred)" : ""); | ||
660 | } | ||
661 | } | ||
662 | if (state->committed & WLR_OUTPUT_STATE_ADAPTIVE_SYNC_ENABLED) { | ||
663 | sway_log(SWAY_DEBUG, " adaptive_sync: %s", | ||
664 | state->adaptive_sync_enabled ? "enabled": "disabled"); | ||
665 | } | ||
666 | } | ||
667 | |||
668 | static bool search_valid_config(struct search_context *ctx, size_t output_idx); | ||
669 | |||
670 | static void reset_output_state(struct wlr_output_state *state) { | ||
671 | wlr_output_state_finish(state); | ||
672 | wlr_output_state_init(state); | ||
673 | state->committed = 0; | ||
674 | } | ||
675 | |||
676 | static void clear_later_output_states(struct wlr_backend_output_state *states, | ||
677 | size_t configs_len, size_t output_idx) { | ||
678 | |||
679 | // Clear and disable all output states after this one to avoid conflict | ||
680 | // with previous tests. | ||
681 | for (size_t idx = output_idx+1; idx < configs_len; idx++) { | ||
682 | struct wlr_backend_output_state *backend_state = &states[idx]; | ||
683 | struct wlr_output_state *state = &backend_state->base; | ||
684 | |||
685 | reset_output_state(state); | ||
686 | wlr_output_state_set_enabled(state, false); | ||
687 | } | ||
688 | } | ||
689 | |||
690 | static bool search_finish(struct search_context *ctx, size_t output_idx) { | ||
691 | struct wlr_backend_output_state *backend_state = &ctx->states[output_idx]; | ||
692 | struct wlr_output_state *state = &backend_state->base; | ||
693 | struct wlr_output *wlr_output = backend_state->output; | ||
694 | |||
695 | clear_later_output_states(ctx->states, ctx->configs_len, output_idx); | ||
696 | dump_output_state(wlr_output, state); | ||
697 | return wlr_output_swapchain_manager_prepare(ctx->swapchain_mgr, ctx->states, ctx->configs_len) && | ||
698 | search_valid_config(ctx, output_idx+1); | ||
699 | } | ||
700 | |||
701 | static bool search_adaptive_sync(struct search_context *ctx, size_t output_idx) { | ||
702 | struct matched_output_config *cfg = &ctx->configs[output_idx]; | ||
703 | struct wlr_backend_output_state *backend_state = &ctx->states[output_idx]; | ||
704 | struct wlr_output_state *state = &backend_state->base; | ||
705 | |||
706 | if (cfg->config && cfg->config->adaptive_sync == 1) { | ||
707 | wlr_output_state_set_adaptive_sync_enabled(state, true); | ||
708 | if (search_finish(ctx, output_idx)) { | ||
709 | return true; | ||
537 | } | 710 | } |
711 | } | ||
712 | if (!cfg->config || cfg->config->adaptive_sync != -1) { | ||
713 | wlr_output_state_set_adaptive_sync_enabled(state, false); | ||
714 | if (search_finish(ctx, output_idx)) { | ||
715 | return true; | ||
716 | } | ||
717 | } | ||
718 | // If adaptive sync has not been set, or fallback in case we are on a | ||
719 | // backend that cannot disable adaptive sync such as the wayland backend. | ||
720 | state->committed &= ~WLR_OUTPUT_STATE_ADAPTIVE_SYNC_ENABLED; | ||
721 | return search_finish(ctx, output_idx); | ||
722 | } | ||
538 | 723 | ||
539 | i = list_seq_find(config->output_configs, output_name_cmp, identifier); | 724 | static bool search_mode(struct search_context *ctx, size_t output_idx) { |
540 | if (i >= 0) { | 725 | struct matched_output_config *cfg = &ctx->configs[output_idx]; |
541 | oc_id = config->output_configs->items[i]; | 726 | struct wlr_backend_output_state *backend_state = &ctx->states[output_idx]; |
727 | struct wlr_output_state *state = &backend_state->base; | ||
728 | struct wlr_output *wlr_output = backend_state->output; | ||
729 | |||
730 | if (!config_has_auto_mode(cfg->config)) { | ||
731 | return search_adaptive_sync(ctx, output_idx); | ||
732 | } | ||
733 | |||
734 | struct wlr_output_mode *preferred_mode = wlr_output_preferred_mode(wlr_output); | ||
735 | if (preferred_mode) { | ||
736 | wlr_output_state_set_mode(state, preferred_mode); | ||
737 | if (search_adaptive_sync(ctx, output_idx)) { | ||
738 | return true; | ||
542 | } | 739 | } |
543 | } | 740 | } |
544 | 741 | ||
545 | struct output_config *result = new_output_config("temp"); | 742 | if (wl_list_empty(&wlr_output->modes)) { |
546 | if (config->reloading) { | 743 | state->committed &= ~WLR_OUTPUT_STATE_MODE; |
547 | default_output_config(result, sway_output->wlr_output); | 744 | return search_adaptive_sync(ctx, output_idx); |
548 | } | 745 | } |
549 | if (oc_id_on_name) { | 746 | |
550 | // Already have an identifier on name config, use that | 747 | struct wlr_output_mode *mode; |
551 | free(result->name); | 748 | wl_list_for_each(mode, &backend_state->output->modes, link) { |
552 | result->name = strdup(id_on_name); | 749 | if (mode == preferred_mode) { |
553 | merge_output_config(result, oc_id_on_name); | 750 | continue; |
554 | } else if (oc_name && oc_id) { | 751 | } |
555 | // Generate a config named `<identifier> on <name>` which contains a | 752 | wlr_output_state_set_mode(state, mode); |
556 | // merged copy of the identifier on name. This will make sure that both | 753 | if (search_adaptive_sync(ctx, output_idx)) { |
557 | // identifier and name configs are respected, with identifier getting | 754 | return true; |
558 | // priority | ||
559 | struct output_config *temp = new_output_config(id_on_name); | ||
560 | merge_output_config(temp, oc_name); | ||
561 | merge_output_config(temp, oc_id); | ||
562 | list_add(config->output_configs, temp); | ||
563 | |||
564 | free(result->name); | ||
565 | result->name = strdup(id_on_name); | ||
566 | merge_output_config(result, temp); | ||
567 | |||
568 | sway_log(SWAY_DEBUG, "Generated output config \"%s\" (enabled: %d)" | ||
569 | " (%dx%d@%fHz position %d,%d scale %f transform %d) (bg %s %s)" | ||
570 | " (dpms %d) (max render time: %d)", result->name, result->enabled, | ||
571 | result->width, result->height, result->refresh_rate, | ||
572 | result->x, result->y, result->scale, result->transform, | ||
573 | result->background, result->background_option, result->dpms_state, | ||
574 | result->max_render_time); | ||
575 | } else if (oc_name) { | ||
576 | // No identifier config, just return a copy of the name config | ||
577 | free(result->name); | ||
578 | result->name = strdup(name); | ||
579 | merge_output_config(result, oc_name); | ||
580 | } else if (oc_id) { | ||
581 | // No name config, just return a copy of the identifier config | ||
582 | free(result->name); | ||
583 | result->name = strdup(identifier); | ||
584 | merge_output_config(result, oc_id); | ||
585 | } else { | ||
586 | i = list_seq_find(config->output_configs, output_name_cmp, "*"); | ||
587 | if (i >= 0) { | ||
588 | // No name or identifier config, but there is a wildcard config | ||
589 | free(result->name); | ||
590 | result->name = strdup("*"); | ||
591 | merge_output_config(result, config->output_configs->items[i]); | ||
592 | } else if (!config->reloading) { | ||
593 | // No name, identifier, or wildcard config. Since we are not | ||
594 | // reloading with defaults, the output config will be empty, so | ||
595 | // just return NULL | ||
596 | free_output_config(result); | ||
597 | result = NULL; | ||
598 | } | 755 | } |
599 | } | 756 | } |
600 | 757 | ||
601 | free(id_on_name); | 758 | return false; |
602 | return result; | ||
603 | } | 759 | } |
604 | 760 | ||
605 | struct output_config *find_output_config(struct sway_output *output) { | 761 | static bool search_render_format(struct search_context *ctx, size_t output_idx) { |
606 | char id[128]; | 762 | struct matched_output_config *cfg = &ctx->configs[output_idx]; |
607 | output_get_identifier(id, sizeof(id), output); | 763 | struct wlr_backend_output_state *backend_state = &ctx->states[output_idx]; |
608 | return get_output_config(id, output); | 764 | struct wlr_output_state *state = &backend_state->base; |
765 | struct wlr_output *wlr_output = backend_state->output; | ||
766 | |||
767 | uint32_t fmts[] = { | ||
768 | DRM_FORMAT_XRGB2101010, | ||
769 | DRM_FORMAT_XBGR2101010, | ||
770 | DRM_FORMAT_XRGB8888, | ||
771 | DRM_FORMAT_INVALID, | ||
772 | }; | ||
773 | if (render_format_is_bgr(wlr_output->render_format)) { | ||
774 | // Start with BGR in the unlikely event that we previously required it. | ||
775 | fmts[0] = DRM_FORMAT_XBGR2101010; | ||
776 | fmts[1] = DRM_FORMAT_XRGB2101010; | ||
777 | } | ||
778 | |||
779 | const struct wlr_drm_format_set *primary_formats = | ||
780 | wlr_output_get_primary_formats(wlr_output, WLR_BUFFER_CAP_DMABUF); | ||
781 | bool need_10bit = cfg->config && cfg->config->render_bit_depth == RENDER_BIT_DEPTH_10; | ||
782 | for (size_t idx = 0; fmts[idx] != DRM_FORMAT_INVALID; idx++) { | ||
783 | if (!need_10bit && render_format_is_10bit(fmts[idx])) { | ||
784 | continue; | ||
785 | } | ||
786 | if (!wlr_drm_format_set_get(primary_formats, fmts[idx])) { | ||
787 | // This is not a supported format for this output | ||
788 | continue; | ||
789 | } | ||
790 | wlr_output_state_set_render_format(state, fmts[idx]); | ||
791 | if (search_mode(ctx, output_idx)) { | ||
792 | return true; | ||
793 | } | ||
794 | } | ||
795 | return false; | ||
609 | } | 796 | } |
610 | 797 | ||
611 | void apply_output_config_to_outputs(struct output_config *oc) { | 798 | static bool search_valid_config(struct search_context *ctx, size_t output_idx) { |
612 | // Try to find the output container and apply configuration now. If | 799 | if (output_idx >= ctx->configs_len) { |
613 | // this is during startup then there will be no container and config | 800 | // We reached the end of the search, all good! |
614 | // will be applied during normal "new output" event from wlroots. | 801 | return true; |
615 | bool wildcard = strcmp(oc->name, "*") == 0; | 802 | } |
616 | char id[128]; | ||
617 | struct sway_output *sway_output, *tmp; | ||
618 | wl_list_for_each_safe(sway_output, tmp, &root->all_outputs, link) { | ||
619 | char *name = sway_output->wlr_output->name; | ||
620 | output_get_identifier(id, sizeof(id), sway_output); | ||
621 | if (wildcard || !strcmp(name, oc->name) || !strcmp(id, oc->name)) { | ||
622 | struct output_config *current = get_output_config(id, sway_output); | ||
623 | if (!current) { | ||
624 | // No stored output config matched, apply oc directly | ||
625 | sway_log(SWAY_DEBUG, "Applying oc directly"); | ||
626 | current = new_output_config(oc->name); | ||
627 | merge_output_config(current, oc); | ||
628 | } | ||
629 | apply_output_config(current, sway_output); | ||
630 | free_output_config(current); | ||
631 | 803 | ||
632 | if (!wildcard) { | 804 | struct matched_output_config *cfg = &ctx->configs[output_idx]; |
633 | // Stop looking if the output config isn't applicable to all | 805 | struct wlr_backend_output_state *backend_state = &ctx->states[output_idx]; |
634 | // outputs | 806 | struct wlr_output_state *state = &backend_state->base; |
635 | break; | 807 | struct wlr_output *wlr_output = backend_state->output; |
636 | } | 808 | |
809 | if (!output_config_is_disabling(cfg->config)) { | ||
810 | // Search through our possible configurations, doing a depth-first | ||
811 | // through render_format, modes, adaptive_sync and the next output's | ||
812 | // config. | ||
813 | queue_output_config(cfg->config, cfg->output, &backend_state->base); | ||
814 | if (search_render_format(ctx, output_idx)) { | ||
815 | return true; | ||
816 | } else if (!ctx->degrade_to_off) { | ||
817 | return false; | ||
818 | } | ||
819 | // We could not get anything to work, try to disable this output to see | ||
820 | // if we can at least make the outputs before us work. | ||
821 | sway_log(SWAY_DEBUG, "Unable to find valid config with output %s, disabling", | ||
822 | wlr_output->name); | ||
823 | reset_output_state(state); | ||
824 | } | ||
825 | |||
826 | wlr_output_state_set_enabled(state, false); | ||
827 | return search_finish(ctx, output_idx); | ||
828 | } | ||
829 | |||
830 | static int compare_matched_output_config_priority(const void *a, const void *b) { | ||
831 | |||
832 | const struct matched_output_config *amc = a; | ||
833 | const struct matched_output_config *bmc = b; | ||
834 | bool a_disabling = output_config_is_disabling(amc->config); | ||
835 | bool b_disabling = output_config_is_disabling(bmc->config); | ||
836 | bool a_enabled = amc->output->enabled; | ||
837 | bool b_enabled = bmc->output->enabled; | ||
838 | |||
839 | // We want to give priority to existing enabled outputs. To do so, we want | ||
840 | // the configuration order to be: | ||
841 | // 1. Existing, enabled outputs | ||
842 | // 2. Outputs that need to be enabled | ||
843 | // 3. Disabled or disabling outputs | ||
844 | if (a_enabled && !a_disabling) { | ||
845 | return -1; | ||
846 | } else if (b_enabled && !b_disabling) { | ||
847 | return 1; | ||
848 | } else if (b_disabling && !a_disabling) { | ||
849 | return -1; | ||
850 | } else if (a_disabling && !b_disabling) { | ||
851 | return 1; | ||
852 | } | ||
853 | return 0; | ||
854 | } | ||
855 | |||
856 | void sort_output_configs_by_priority(struct matched_output_config *configs, | ||
857 | size_t configs_len) { | ||
858 | qsort(configs, configs_len, sizeof(*configs), compare_matched_output_config_priority); | ||
859 | } | ||
860 | |||
861 | bool apply_output_configs(struct matched_output_config *configs, | ||
862 | size_t configs_len, bool test_only, bool degrade_to_off) { | ||
863 | struct wlr_backend_output_state *states = calloc(configs_len, sizeof(*states)); | ||
864 | if (!states) { | ||
865 | return false; | ||
866 | } | ||
867 | |||
868 | sway_log(SWAY_DEBUG, "Committing %zd outputs", configs_len); | ||
869 | for (size_t idx = 0; idx < configs_len; idx++) { | ||
870 | struct matched_output_config *cfg = &configs[idx]; | ||
871 | struct wlr_backend_output_state *backend_state = &states[idx]; | ||
872 | |||
873 | backend_state->output = cfg->output->wlr_output; | ||
874 | wlr_output_state_init(&backend_state->base); | ||
875 | |||
876 | sway_log(SWAY_DEBUG, "Preparing config for %s", | ||
877 | cfg->output->wlr_output->name); | ||
878 | queue_output_config(cfg->config, cfg->output, &backend_state->base); | ||
879 | } | ||
880 | |||
881 | struct wlr_output_swapchain_manager swapchain_mgr; | ||
882 | wlr_output_swapchain_manager_init(&swapchain_mgr, server.backend); | ||
883 | |||
884 | bool ok = wlr_output_swapchain_manager_prepare(&swapchain_mgr, states, configs_len); | ||
885 | if (!ok) { | ||
886 | sway_log(SWAY_ERROR, "Requested backend configuration failed, searching for valid fallbacks"); | ||
887 | struct search_context ctx = { | ||
888 | .swapchain_mgr = &swapchain_mgr, | ||
889 | .states = states, | ||
890 | .configs = configs, | ||
891 | .configs_len = configs_len, | ||
892 | .degrade_to_off = degrade_to_off, | ||
893 | }; | ||
894 | if (!search_valid_config(&ctx, 0)) { | ||
895 | sway_log(SWAY_ERROR, "Search for valid config failed"); | ||
896 | goto out; | ||
897 | } | ||
898 | } | ||
899 | |||
900 | if (test_only) { | ||
901 | // The swapchain manager already did a test for us | ||
902 | goto out; | ||
903 | } | ||
904 | |||
905 | for (size_t idx = 0; idx < configs_len; idx++) { | ||
906 | struct matched_output_config *cfg = &configs[idx]; | ||
907 | struct wlr_backend_output_state *backend_state = &states[idx]; | ||
908 | |||
909 | struct wlr_scene_output_state_options opts = { | ||
910 | .swapchain = wlr_output_swapchain_manager_get_swapchain( | ||
911 | &swapchain_mgr, backend_state->output), | ||
912 | }; | ||
913 | struct wlr_scene_output *scene_output = cfg->output->scene_output; | ||
914 | struct wlr_output_state *state = &backend_state->base; | ||
915 | if (!wlr_scene_output_build_state(scene_output, state, &opts)) { | ||
916 | sway_log(SWAY_ERROR, "Building output state for '%s' failed", | ||
917 | backend_state->output->name); | ||
918 | goto out; | ||
637 | } | 919 | } |
638 | } | 920 | } |
639 | 921 | ||
922 | ok = wlr_backend_commit(server.backend, states, configs_len); | ||
923 | if (!ok) { | ||
924 | sway_log(SWAY_ERROR, "Backend commit failed"); | ||
925 | goto out; | ||
926 | } | ||
927 | |||
928 | sway_log(SWAY_DEBUG, "Commit of %zd outputs succeeded", configs_len); | ||
929 | |||
930 | wlr_output_swapchain_manager_apply(&swapchain_mgr); | ||
931 | |||
932 | for (size_t idx = 0; idx < configs_len; idx++) { | ||
933 | struct matched_output_config *cfg = &configs[idx]; | ||
934 | sway_log(SWAY_DEBUG, "Finalizing config for %s", | ||
935 | cfg->output->wlr_output->name); | ||
936 | finalize_output_config(cfg->config, cfg->output); | ||
937 | } | ||
938 | |||
939 | out: | ||
940 | wlr_output_swapchain_manager_finish(&swapchain_mgr); | ||
941 | for (size_t idx = 0; idx < configs_len; idx++) { | ||
942 | struct wlr_backend_output_state *backend_state = &states[idx]; | ||
943 | wlr_output_state_finish(&backend_state->base); | ||
944 | } | ||
945 | free(states); | ||
946 | |||
947 | // Reconfigure all devices, since input config may have been applied before | ||
948 | // this output came online, and some config items (like map_to_output) are | ||
949 | // dependent on an output being present. | ||
950 | input_manager_configure_all_input_mappings(); | ||
951 | // Reconfigure the cursor images, since the scale may have changed. | ||
952 | input_manager_configure_xcursor(); | ||
953 | |||
640 | struct sway_seat *seat; | 954 | struct sway_seat *seat; |
641 | wl_list_for_each(seat, &server.input->seats, link) { | 955 | wl_list_for_each(seat, &server.input->seats, link) { |
642 | wlr_seat_pointer_notify_clear_focus(seat->wlr_seat); | 956 | wlr_seat_pointer_notify_clear_focus(seat->wlr_seat); |
643 | cursor_rebase(seat->cursor); | 957 | cursor_rebase(seat->cursor); |
644 | } | 958 | } |
959 | |||
960 | return ok; | ||
645 | } | 961 | } |
646 | 962 | ||
647 | void reset_outputs(void) { | 963 | void apply_all_output_configs(void) { |
648 | struct output_config *oc = NULL; | 964 | size_t configs_len = wl_list_length(&root->all_outputs); |
649 | int i = list_seq_find(config->output_configs, output_name_cmp, "*"); | 965 | struct matched_output_config *configs = calloc(configs_len, sizeof(*configs)); |
650 | if (i >= 0) { | 966 | if (!configs) { |
651 | oc = config->output_configs->items[i]; | 967 | return; |
652 | } else { | ||
653 | oc = store_output_config(new_output_config("*")); | ||
654 | } | 968 | } |
655 | apply_output_config_to_outputs(oc); | 969 | |
970 | int config_idx = 0; | ||
971 | struct sway_output *sway_output; | ||
972 | wl_list_for_each(sway_output, &root->all_outputs, link) { | ||
973 | if (sway_output == root->fallback_output) { | ||
974 | configs_len--; | ||
975 | continue; | ||
976 | } | ||
977 | |||
978 | struct matched_output_config *config = &configs[config_idx++]; | ||
979 | config->output = sway_output; | ||
980 | config->config = find_output_config(sway_output); | ||
981 | } | ||
982 | |||
983 | sort_output_configs_by_priority(configs, configs_len); | ||
984 | apply_output_configs(configs, configs_len, false, true); | ||
985 | for (size_t idx = 0; idx < configs_len; idx++) { | ||
986 | struct matched_output_config *cfg = &configs[idx]; | ||
987 | free_output_config(cfg->config); | ||
988 | } | ||
989 | free(configs); | ||
656 | } | 990 | } |
657 | 991 | ||
658 | void free_output_config(struct output_config *oc) { | 992 | void free_output_config(struct output_config *oc) { |
@@ -702,6 +1036,8 @@ static bool _spawn_swaybg(char **command) { | |||
702 | sway_log_errno(SWAY_ERROR, "fork failed"); | 1036 | sway_log_errno(SWAY_ERROR, "fork failed"); |
703 | return false; | 1037 | return false; |
704 | } else if (pid == 0) { | 1038 | } else if (pid == 0) { |
1039 | restore_nofile_limit(); | ||
1040 | |||
705 | pid = fork(); | 1041 | pid = fork(); |
706 | if (pid < 0) { | 1042 | if (pid < 0) { |
707 | sway_log_errno(SWAY_ERROR, "fork failed"); | 1043 | sway_log_errno(SWAY_ERROR, "fork failed"); |
@@ -717,7 +1053,9 @@ static bool _spawn_swaybg(char **command) { | |||
717 | setenv("WAYLAND_SOCKET", wayland_socket_str, true); | 1053 | setenv("WAYLAND_SOCKET", wayland_socket_str, true); |
718 | 1054 | ||
719 | execvp(command[0], command); | 1055 | execvp(command[0], command); |
720 | sway_log_errno(SWAY_ERROR, "execvp failed"); | 1056 | sway_log_errno(SWAY_ERROR, "failed to execute '%s' " |
1057 | "(background configuration probably not applied)", | ||
1058 | command[0]); | ||
721 | _exit(EXIT_FAILURE); | 1059 | _exit(EXIT_FAILURE); |
722 | } | 1060 | } |
723 | _exit(EXIT_SUCCESS); | 1061 | _exit(EXIT_SUCCESS); |
@@ -727,12 +1065,13 @@ static bool _spawn_swaybg(char **command) { | |||
727 | sway_log_errno(SWAY_ERROR, "close failed"); | 1065 | sway_log_errno(SWAY_ERROR, "close failed"); |
728 | return false; | 1066 | return false; |
729 | } | 1067 | } |
730 | if (waitpid(pid, NULL, 0) < 0) { | 1068 | int fork_status = 0; |
1069 | if (waitpid(pid, &fork_status, 0) < 0) { | ||
731 | sway_log_errno(SWAY_ERROR, "waitpid failed"); | 1070 | sway_log_errno(SWAY_ERROR, "waitpid failed"); |
732 | return false; | 1071 | return false; |
733 | } | 1072 | } |
734 | 1073 | ||
735 | return true; | 1074 | return WIFEXITED(fork_status) && WEXITSTATUS(fork_status) == EXIT_SUCCESS; |
736 | } | 1075 | } |
737 | 1076 | ||
738 | bool spawn_swaybg(void) { | 1077 | bool spawn_swaybg(void) { |
diff --git a/sway/config/seat.c b/sway/config/seat.c index 84260aa3..f2326189 100644 --- a/sway/config/seat.c +++ b/sway/config/seat.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <limits.h> | 1 | #include <limits.h> |
3 | #include <stdlib.h> | 2 | #include <stdlib.h> |
4 | #include <string.h> | 3 | #include <string.h> |
@@ -99,7 +98,6 @@ static void seat_attachment_config_free( | |||
99 | struct seat_attachment_config *attachment) { | 98 | struct seat_attachment_config *attachment) { |
100 | free(attachment->identifier); | 99 | free(attachment->identifier); |
101 | free(attachment); | 100 | free(attachment); |
102 | return; | ||
103 | } | 101 | } |
104 | 102 | ||
105 | static struct seat_attachment_config *seat_attachment_config_copy( | 103 | static struct seat_attachment_config *seat_attachment_config_copy( |
diff --git a/sway/criteria.c b/sway/criteria.c index 409160c5..13f0530e 100644 --- a/sway/criteria.c +++ b/sway/criteria.c | |||
@@ -1,9 +1,9 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <stdlib.h> | 1 | #include <stdlib.h> |
3 | #include <stdio.h> | 2 | #include <stdio.h> |
4 | #include <stdbool.h> | 3 | #include <stdbool.h> |
5 | #include <strings.h> | 4 | #include <strings.h> |
6 | #include <pcre.h> | 5 | #define PCRE2_CODE_UNIT_WIDTH 8 |
6 | #include <pcre2.h> | ||
7 | #include "sway/criteria.h" | 7 | #include "sway/criteria.h" |
8 | #include "sway/tree/container.h" | 8 | #include "sway/tree/container.h" |
9 | #include "sway/config.h" | 9 | #include "sway/config.h" |
@@ -18,10 +18,11 @@ | |||
18 | bool criteria_is_empty(struct criteria *criteria) { | 18 | bool criteria_is_empty(struct criteria *criteria) { |
19 | return !criteria->title | 19 | return !criteria->title |
20 | && !criteria->shell | 20 | && !criteria->shell |
21 | && !criteria->all | ||
21 | && !criteria->app_id | 22 | && !criteria->app_id |
22 | && !criteria->con_mark | 23 | && !criteria->con_mark |
23 | && !criteria->con_id | 24 | && !criteria->con_id |
24 | #if HAVE_XWAYLAND | 25 | #if WLR_HAS_XWAYLAND |
25 | && !criteria->class | 26 | && !criteria->class |
26 | && !criteria->id | 27 | && !criteria->id |
27 | && !criteria->instance | 28 | && !criteria->instance |
@@ -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 | } |
@@ -87,23 +90,27 @@ void criteria_destroy(struct criteria *criteria) { | |||
87 | pattern_destroy(criteria->title); | 90 | pattern_destroy(criteria->title); |
88 | pattern_destroy(criteria->shell); | 91 | pattern_destroy(criteria->shell); |
89 | pattern_destroy(criteria->app_id); | 92 | pattern_destroy(criteria->app_id); |
90 | #if HAVE_XWAYLAND | 93 | #if WLR_HAS_XWAYLAND |
91 | pattern_destroy(criteria->class); | 94 | pattern_destroy(criteria->class); |
92 | pattern_destroy(criteria->instance); | 95 | pattern_destroy(criteria->instance); |
93 | pattern_destroy(criteria->window_role); | 96 | pattern_destroy(criteria->window_role); |
94 | #endif | 97 | #endif |
95 | pattern_destroy(criteria->con_mark); | 98 | pattern_destroy(criteria->con_mark); |
96 | free(criteria->workspace); | 99 | pattern_destroy(criteria->workspace); |
100 | free(criteria->target); | ||
97 | free(criteria->cmdlist); | 101 | free(criteria->cmdlist); |
98 | free(criteria->raw); | 102 | free(criteria->raw); |
99 | free(criteria); | 103 | free(criteria); |
100 | } | 104 | } |
101 | 105 | ||
102 | static int regex_cmp(const char *item, const pcre *regex) { | 106 | static int regex_cmp(const char *item, const pcre2_code *regex) { |
103 | return pcre_exec(regex, NULL, item, strlen(item), 0, 0, NULL, 0); | 107 | pcre2_match_data *match_data = pcre2_match_data_create_from_pattern(regex, NULL); |
108 | int result = pcre2_match(regex, (PCRE2_SPTR)item, strlen(item), 0, 0, match_data, NULL); | ||
109 | pcre2_match_data_free(match_data); | ||
110 | return result; | ||
104 | } | 111 | } |
105 | 112 | ||
106 | #if HAVE_XWAYLAND | 113 | #if WLR_HAS_XWAYLAND |
107 | static bool view_has_window_type(struct sway_view *view, enum atom_name name) { | 114 | static bool view_has_window_type(struct sway_view *view, enum atom_name name) { |
108 | if (view->type != SWAY_VIEW_XWAYLAND) { | 115 | if (view->type != SWAY_VIEW_XWAYLAND) { |
109 | return false; | 116 | return false; |
@@ -155,7 +162,7 @@ static bool criteria_matches_container(struct criteria *criteria, | |||
155 | bool exists = false; | 162 | bool exists = false; |
156 | struct sway_container *con = container; | 163 | struct sway_container *con = container; |
157 | for (int i = 0; i < con->marks->length; ++i) { | 164 | for (int i = 0; i < con->marks->length; ++i) { |
158 | if (regex_cmp(con->marks->items[i], criteria->con_mark->regex) == 0) { | 165 | if (regex_cmp(con->marks->items[i], criteria->con_mark->regex) >= 0) { |
159 | exists = true; | 166 | exists = true; |
160 | break; | 167 | break; |
161 | } | 168 | } |
@@ -183,7 +190,7 @@ static bool criteria_matches_view(struct criteria *criteria, | |||
183 | if (criteria->title) { | 190 | if (criteria->title) { |
184 | const char *title = view_get_title(view); | 191 | const char *title = view_get_title(view); |
185 | if (!title) { | 192 | if (!title) { |
186 | return false; | 193 | title = ""; |
187 | } | 194 | } |
188 | 195 | ||
189 | switch (criteria->title->match_type) { | 196 | switch (criteria->title->match_type) { |
@@ -192,8 +199,8 @@ static bool criteria_matches_view(struct criteria *criteria, | |||
192 | return false; | 199 | return false; |
193 | } | 200 | } |
194 | break; | 201 | break; |
195 | case PATTERN_PCRE: | 202 | case PATTERN_PCRE2: |
196 | if (regex_cmp(title, criteria->title->regex) != 0) { | 203 | if (regex_cmp(title, criteria->title->regex) < 0) { |
197 | return false; | 204 | return false; |
198 | } | 205 | } |
199 | break; | 206 | break; |
@@ -203,7 +210,7 @@ static bool criteria_matches_view(struct criteria *criteria, | |||
203 | if (criteria->shell) { | 210 | if (criteria->shell) { |
204 | const char *shell = view_get_shell(view); | 211 | const char *shell = view_get_shell(view); |
205 | if (!shell) { | 212 | if (!shell) { |
206 | return false; | 213 | shell = ""; |
207 | } | 214 | } |
208 | 215 | ||
209 | switch (criteria->shell->match_type) { | 216 | switch (criteria->shell->match_type) { |
@@ -212,8 +219,8 @@ static bool criteria_matches_view(struct criteria *criteria, | |||
212 | return false; | 219 | return false; |
213 | } | 220 | } |
214 | break; | 221 | break; |
215 | case PATTERN_PCRE: | 222 | case PATTERN_PCRE2: |
216 | if (regex_cmp(shell, criteria->shell->regex) != 0) { | 223 | if (regex_cmp(shell, criteria->shell->regex) < 0) { |
217 | return false; | 224 | return false; |
218 | } | 225 | } |
219 | break; | 226 | break; |
@@ -223,7 +230,7 @@ static bool criteria_matches_view(struct criteria *criteria, | |||
223 | if (criteria->app_id) { | 230 | if (criteria->app_id) { |
224 | const char *app_id = view_get_app_id(view); | 231 | const char *app_id = view_get_app_id(view); |
225 | if (!app_id) { | 232 | if (!app_id) { |
226 | return false; | 233 | app_id = ""; |
227 | } | 234 | } |
228 | 235 | ||
229 | switch (criteria->app_id->match_type) { | 236 | switch (criteria->app_id->match_type) { |
@@ -232,8 +239,8 @@ static bool criteria_matches_view(struct criteria *criteria, | |||
232 | return false; | 239 | return false; |
233 | } | 240 | } |
234 | break; | 241 | break; |
235 | case PATTERN_PCRE: | 242 | case PATTERN_PCRE2: |
236 | if (regex_cmp(app_id, criteria->app_id->regex) != 0) { | 243 | if (regex_cmp(app_id, criteria->app_id->regex) < 0) { |
237 | return false; | 244 | return false; |
238 | } | 245 | } |
239 | break; | 246 | break; |
@@ -244,7 +251,7 @@ static bool criteria_matches_view(struct criteria *criteria, | |||
244 | return false; | 251 | return false; |
245 | } | 252 | } |
246 | 253 | ||
247 | #if HAVE_XWAYLAND | 254 | #if WLR_HAS_XWAYLAND |
248 | if (criteria->id) { // X11 window ID | 255 | if (criteria->id) { // X11 window ID |
249 | uint32_t x11_window_id = view_get_x11_window_id(view); | 256 | uint32_t x11_window_id = view_get_x11_window_id(view); |
250 | if (!x11_window_id || x11_window_id != criteria->id) { | 257 | if (!x11_window_id || x11_window_id != criteria->id) { |
@@ -255,7 +262,7 @@ static bool criteria_matches_view(struct criteria *criteria, | |||
255 | if (criteria->class) { | 262 | if (criteria->class) { |
256 | const char *class = view_get_class(view); | 263 | const char *class = view_get_class(view); |
257 | if (!class) { | 264 | if (!class) { |
258 | return false; | 265 | class = ""; |
259 | } | 266 | } |
260 | 267 | ||
261 | switch (criteria->class->match_type) { | 268 | switch (criteria->class->match_type) { |
@@ -264,8 +271,8 @@ static bool criteria_matches_view(struct criteria *criteria, | |||
264 | return false; | 271 | return false; |
265 | } | 272 | } |
266 | break; | 273 | break; |
267 | case PATTERN_PCRE: | 274 | case PATTERN_PCRE2: |
268 | if (regex_cmp(class, criteria->class->regex) != 0) { | 275 | if (regex_cmp(class, criteria->class->regex) < 0) { |
269 | return false; | 276 | return false; |
270 | } | 277 | } |
271 | break; | 278 | break; |
@@ -275,17 +282,17 @@ static bool criteria_matches_view(struct criteria *criteria, | |||
275 | if (criteria->instance) { | 282 | if (criteria->instance) { |
276 | const char *instance = view_get_instance(view); | 283 | const char *instance = view_get_instance(view); |
277 | if (!instance) { | 284 | if (!instance) { |
278 | return false; | 285 | instance = ""; |
279 | } | 286 | } |
280 | 287 | ||
281 | switch (criteria->instance->match_type) { | 288 | switch (criteria->instance->match_type) { |
282 | case PATTERN_FOCUSED: | 289 | case PATTERN_FOCUSED: |
283 | if (focused && strcmp(instance, view_get_instance(focused))) { | 290 | if (focused && lenient_strcmp(instance, view_get_instance(focused))) { |
284 | return false; | 291 | return false; |
285 | } | 292 | } |
286 | break; | 293 | break; |
287 | case PATTERN_PCRE: | 294 | case PATTERN_PCRE2: |
288 | if (regex_cmp(instance, criteria->instance->regex) != 0) { | 295 | if (regex_cmp(instance, criteria->instance->regex) < 0) { |
289 | return false; | 296 | return false; |
290 | } | 297 | } |
291 | break; | 298 | break; |
@@ -295,17 +302,17 @@ static bool criteria_matches_view(struct criteria *criteria, | |||
295 | if (criteria->window_role) { | 302 | if (criteria->window_role) { |
296 | const char *window_role = view_get_window_role(view); | 303 | const char *window_role = view_get_window_role(view); |
297 | if (!window_role) { | 304 | if (!window_role) { |
298 | return false; | 305 | window_role = ""; |
299 | } | 306 | } |
300 | 307 | ||
301 | switch (criteria->window_role->match_type) { | 308 | switch (criteria->window_role->match_type) { |
302 | case PATTERN_FOCUSED: | 309 | case PATTERN_FOCUSED: |
303 | if (focused && strcmp(window_role, view_get_window_role(focused))) { | 310 | if (focused && lenient_strcmp(window_role, view_get_window_role(focused))) { |
304 | return false; | 311 | return false; |
305 | } | 312 | } |
306 | break; | 313 | break; |
307 | case PATTERN_PCRE: | 314 | case PATTERN_PCRE2: |
308 | if (regex_cmp(window_role, criteria->window_role->regex) != 0) { | 315 | if (regex_cmp(window_role, criteria->window_role->regex) < 0) { |
309 | return false; | 316 | return false; |
310 | } | 317 | } |
311 | break; | 318 | break; |
@@ -351,7 +358,7 @@ static bool criteria_matches_view(struct criteria *criteria, | |||
351 | } | 358 | } |
352 | 359 | ||
353 | if (criteria->workspace) { | 360 | if (criteria->workspace) { |
354 | struct sway_workspace *ws = view->container->workspace; | 361 | struct sway_workspace *ws = view->container->pending.workspace; |
355 | if (!ws) { | 362 | if (!ws) { |
356 | return false; | 363 | return false; |
357 | } | 364 | } |
@@ -359,12 +366,12 @@ static bool criteria_matches_view(struct criteria *criteria, | |||
359 | switch (criteria->workspace->match_type) { | 366 | switch (criteria->workspace->match_type) { |
360 | case PATTERN_FOCUSED: | 367 | case PATTERN_FOCUSED: |
361 | if (focused && | 368 | if (focused && |
362 | strcmp(ws->name, focused->container->workspace->name)) { | 369 | strcmp(ws->name, focused->container->pending.workspace->name)) { |
363 | return false; | 370 | return false; |
364 | } | 371 | } |
365 | break; | 372 | break; |
366 | case PATTERN_PCRE: | 373 | case PATTERN_PCRE2: |
367 | if (regex_cmp(ws->name, criteria->workspace->regex) != 0) { | 374 | if (regex_cmp(ws->name, criteria->workspace->regex) < 0) { |
368 | return false; | 375 | return false; |
369 | } | 376 | } |
370 | break; | 377 | break; |
@@ -421,7 +428,7 @@ list_t *criteria_get_containers(struct criteria *criteria) { | |||
421 | return matches; | 428 | return matches; |
422 | } | 429 | } |
423 | 430 | ||
424 | #if HAVE_XWAYLAND | 431 | #if WLR_HAS_XWAYLAND |
425 | static enum atom_name parse_window_type(const char *type) { | 432 | static enum atom_name parse_window_type(const char *type) { |
426 | if (strcasecmp(type, "normal") == 0) { | 433 | if (strcasecmp(type, "normal") == 0) { |
427 | return NET_WM_WINDOW_TYPE_NORMAL; | 434 | return NET_WM_WINDOW_TYPE_NORMAL; |
@@ -449,11 +456,12 @@ static enum atom_name parse_window_type(const char *type) { | |||
449 | #endif | 456 | #endif |
450 | 457 | ||
451 | enum criteria_token { | 458 | enum criteria_token { |
459 | T_ALL, | ||
452 | T_APP_ID, | 460 | T_APP_ID, |
453 | T_CON_ID, | 461 | T_CON_ID, |
454 | T_CON_MARK, | 462 | T_CON_MARK, |
455 | T_FLOATING, | 463 | T_FLOATING, |
456 | #if HAVE_XWAYLAND | 464 | #if WLR_HAS_XWAYLAND |
457 | T_CLASS, | 465 | T_CLASS, |
458 | T_ID, | 466 | T_ID, |
459 | T_INSTANCE, | 467 | T_INSTANCE, |
@@ -471,13 +479,15 @@ enum criteria_token { | |||
471 | }; | 479 | }; |
472 | 480 | ||
473 | static enum criteria_token token_from_name(char *name) { | 481 | static enum criteria_token token_from_name(char *name) { |
474 | if (strcmp(name, "app_id") == 0) { | 482 | if (strcmp(name, "all") == 0) { |
483 | return T_ALL; | ||
484 | } else if (strcmp(name, "app_id") == 0) { | ||
475 | return T_APP_ID; | 485 | return T_APP_ID; |
476 | } else if (strcmp(name, "con_id") == 0) { | 486 | } else if (strcmp(name, "con_id") == 0) { |
477 | return T_CON_ID; | 487 | return T_CON_ID; |
478 | } else if (strcmp(name, "con_mark") == 0) { | 488 | } else if (strcmp(name, "con_mark") == 0) { |
479 | return T_CON_MARK; | 489 | return T_CON_MARK; |
480 | #if HAVE_XWAYLAND | 490 | #if WLR_HAS_XWAYLAND |
481 | } else if (strcmp(name, "class") == 0) { | 491 | } else if (strcmp(name, "class") == 0) { |
482 | return T_CLASS; | 492 | return T_CLASS; |
483 | } else if (strcmp(name, "id") == 0) { | 493 | } else if (strcmp(name, "id") == 0) { |
@@ -517,8 +527,8 @@ static bool parse_token(struct criteria *criteria, char *name, char *value) { | |||
517 | return false; | 527 | return false; |
518 | } | 528 | } |
519 | 529 | ||
520 | // Require value, unless token is floating or tiled | 530 | // Require value, unless token is all, floating or tiled |
521 | if (!value && token != T_FLOATING && token != T_TILING) { | 531 | if (!value && token != T_ALL && token != T_FLOATING && token != T_TILING) { |
522 | const char *fmt = "Token '%s' requires a value"; | 532 | const char *fmt = "Token '%s' requires a value"; |
523 | int len = strlen(fmt) + strlen(name) - 1; | 533 | int len = strlen(fmt) + strlen(name) - 1; |
524 | error = malloc(len); | 534 | error = malloc(len); |
@@ -528,6 +538,9 @@ static bool parse_token(struct criteria *criteria, char *name, char *value) { | |||
528 | 538 | ||
529 | char *endptr = NULL; | 539 | char *endptr = NULL; |
530 | switch (token) { | 540 | switch (token) { |
541 | case T_ALL: | ||
542 | criteria->all = true; | ||
543 | break; | ||
531 | case T_TITLE: | 544 | case T_TITLE: |
532 | pattern_create(&criteria->title, value); | 545 | pattern_create(&criteria->title, value); |
533 | break; | 546 | break; |
@@ -553,7 +566,7 @@ static bool parse_token(struct criteria *criteria, char *name, char *value) { | |||
553 | case T_CON_MARK: | 566 | case T_CON_MARK: |
554 | pattern_create(&criteria->con_mark, value); | 567 | pattern_create(&criteria->con_mark, value); |
555 | break; | 568 | break; |
556 | #if HAVE_XWAYLAND | 569 | #if WLR_HAS_XWAYLAND |
557 | case T_CLASS: | 570 | case T_CLASS: |
558 | pattern_create(&criteria->class, value); | 571 | pattern_create(&criteria->class, value); |
559 | break; | 572 | break; |
@@ -661,7 +674,7 @@ struct criteria *criteria_parse(char *raw, char **error_arg) { | |||
661 | ++head; | 674 | ++head; |
662 | 675 | ||
663 | struct criteria *criteria = calloc(1, sizeof(struct criteria)); | 676 | struct criteria *criteria = calloc(1, sizeof(struct criteria)); |
664 | #if HAVE_XWAYLAND | 677 | #if WLR_HAS_XWAYLAND |
665 | criteria->window_type = ATOM_LAST; // default value | 678 | criteria->window_type = ATOM_LAST; // default value |
666 | #endif | 679 | #endif |
667 | char *name = NULL, *value = NULL; | 680 | char *name = NULL, *value = NULL; |
@@ -676,7 +689,7 @@ struct criteria *criteria_parse(char *raw, char **error_arg) { | |||
676 | } | 689 | } |
677 | name = calloc(head - namestart + 1, 1); | 690 | name = calloc(head - namestart + 1, 1); |
678 | if (head != namestart) { | 691 | if (head != namestart) { |
679 | strncpy(name, namestart, head - namestart); | 692 | memcpy(name, namestart, head - namestart); |
680 | } | 693 | } |
681 | // Parse token value | 694 | // Parse token value |
682 | skip_spaces(&head); | 695 | skip_spaces(&head); |
@@ -703,7 +716,7 @@ struct criteria *criteria_parse(char *raw, char **error_arg) { | |||
703 | } | 716 | } |
704 | } | 717 | } |
705 | value = calloc(head - valuestart + 1, 1); | 718 | value = calloc(head - valuestart + 1, 1); |
706 | strncpy(value, valuestart, head - valuestart); | 719 | memcpy(value, valuestart, head - valuestart); |
707 | if (in_quotes) { | 720 | if (in_quotes) { |
708 | ++head; | 721 | ++head; |
709 | in_quotes = false; | 722 | in_quotes = false; |
@@ -734,7 +747,7 @@ struct criteria *criteria_parse(char *raw, char **error_arg) { | |||
734 | ++head; | 747 | ++head; |
735 | int len = head - raw; | 748 | int len = head - raw; |
736 | criteria->raw = calloc(len + 1, 1); | 749 | criteria->raw = calloc(len + 1, 1); |
737 | strncpy(criteria->raw, raw, len); | 750 | memcpy(criteria->raw, raw, len); |
738 | return criteria; | 751 | return criteria; |
739 | 752 | ||
740 | cleanup: | 753 | cleanup: |
diff --git a/sway/desktop/desktop.c b/sway/desktop/desktop.c deleted file mode 100644 index ec45d80a..00000000 --- a/sway/desktop/desktop.c +++ /dev/null | |||
@@ -1,39 +0,0 @@ | |||
1 | #include "sway/tree/container.h" | ||
2 | #include "sway/desktop.h" | ||
3 | #include "sway/output.h" | ||
4 | |||
5 | void desktop_damage_surface(struct wlr_surface *surface, double lx, double ly, | ||
6 | bool whole) { | ||
7 | for (int i = 0; i < root->outputs->length; ++i) { | ||
8 | struct sway_output *output = root->outputs->items[i]; | ||
9 | struct wlr_box *output_box = wlr_output_layout_get_box( | ||
10 | root->output_layout, output->wlr_output); | ||
11 | output_damage_surface(output, lx - output_box->x, | ||
12 | ly - output_box->y, surface, whole); | ||
13 | } | ||
14 | } | ||
15 | |||
16 | void desktop_damage_whole_container(struct sway_container *con) { | ||
17 | for (int i = 0; i < root->outputs->length; ++i) { | ||
18 | struct sway_output *output = root->outputs->items[i]; | ||
19 | output_damage_whole_container(output, con); | ||
20 | } | ||
21 | } | ||
22 | |||
23 | void desktop_damage_box(struct wlr_box *box) { | ||
24 | for (int i = 0; i < root->outputs->length; ++i) { | ||
25 | struct sway_output *output = root->outputs->items[i]; | ||
26 | output_damage_box(output, box); | ||
27 | } | ||
28 | } | ||
29 | |||
30 | void desktop_damage_view(struct sway_view *view) { | ||
31 | desktop_damage_whole_container(view->container); | ||
32 | struct wlr_box box = { | ||
33 | .x = view->container->current.content_x - view->geometry.x, | ||
34 | .y = view->container->current.content_y - view->geometry.y, | ||
35 | .width = view->surface->current.width, | ||
36 | .height = view->surface->current.height, | ||
37 | }; | ||
38 | desktop_damage_box(&box); | ||
39 | } | ||
diff --git a/sway/desktop/idle_inhibit_v1.c b/sway/desktop/idle_inhibit_v1.c index a5cfd5b2..f3af7aa1 100644 --- a/sway/desktop/idle_inhibit_v1.c +++ b/sway/desktop/idle_inhibit_v1.c | |||
@@ -1,5 +1,5 @@ | |||
1 | #include <stdlib.h> | 1 | #include <stdlib.h> |
2 | #include <wlr/types/wlr_idle.h> | 2 | #include <wlr/types/wlr_idle_notify_v1.h> |
3 | #include "log.h" | 3 | #include "log.h" |
4 | #include "sway/desktop/idle_inhibit_v1.h" | 4 | #include "sway/desktop/idle_inhibit_v1.h" |
5 | #include "sway/input/seat.h" | 5 | #include "sway/input/seat.h" |
@@ -11,7 +11,7 @@ | |||
11 | static void destroy_inhibitor(struct sway_idle_inhibitor_v1 *inhibitor) { | 11 | static void destroy_inhibitor(struct sway_idle_inhibitor_v1 *inhibitor) { |
12 | wl_list_remove(&inhibitor->link); | 12 | wl_list_remove(&inhibitor->link); |
13 | wl_list_remove(&inhibitor->destroy.link); | 13 | wl_list_remove(&inhibitor->destroy.link); |
14 | sway_idle_inhibit_v1_check_active(inhibitor->manager); | 14 | sway_idle_inhibit_v1_check_active(); |
15 | free(inhibitor); | 15 | free(inhibitor); |
16 | } | 16 | } |
17 | 17 | ||
@@ -34,43 +34,43 @@ void handle_idle_inhibitor_v1(struct wl_listener *listener, void *data) { | |||
34 | return; | 34 | return; |
35 | } | 35 | } |
36 | 36 | ||
37 | inhibitor->manager = manager; | ||
38 | inhibitor->mode = INHIBIT_IDLE_APPLICATION; | 37 | inhibitor->mode = INHIBIT_IDLE_APPLICATION; |
39 | inhibitor->view = view_from_wlr_surface(wlr_inhibitor->surface); | 38 | inhibitor->wlr_inhibitor = wlr_inhibitor; |
40 | wl_list_insert(&manager->inhibitors, &inhibitor->link); | 39 | wl_list_insert(&manager->inhibitors, &inhibitor->link); |
41 | 40 | ||
42 | inhibitor->destroy.notify = handle_destroy; | 41 | inhibitor->destroy.notify = handle_destroy; |
43 | wl_signal_add(&wlr_inhibitor->events.destroy, &inhibitor->destroy); | 42 | wl_signal_add(&wlr_inhibitor->events.destroy, &inhibitor->destroy); |
44 | 43 | ||
45 | sway_idle_inhibit_v1_check_active(manager); | 44 | sway_idle_inhibit_v1_check_active(); |
46 | } | 45 | } |
47 | 46 | ||
48 | void sway_idle_inhibit_v1_user_inhibitor_register(struct sway_view *view, | 47 | void sway_idle_inhibit_v1_user_inhibitor_register(struct sway_view *view, |
49 | enum sway_idle_inhibit_mode mode) { | 48 | enum sway_idle_inhibit_mode mode) { |
49 | struct sway_idle_inhibit_manager_v1 *manager = &server.idle_inhibit_manager_v1; | ||
50 | |||
50 | struct sway_idle_inhibitor_v1 *inhibitor = | 51 | struct sway_idle_inhibitor_v1 *inhibitor = |
51 | calloc(1, sizeof(struct sway_idle_inhibitor_v1)); | 52 | calloc(1, sizeof(struct sway_idle_inhibitor_v1)); |
52 | if (!inhibitor) { | 53 | if (!inhibitor) { |
53 | return; | 54 | return; |
54 | } | 55 | } |
55 | 56 | ||
56 | inhibitor->manager = server.idle_inhibit_manager_v1; | ||
57 | inhibitor->mode = mode; | 57 | inhibitor->mode = mode; |
58 | inhibitor->view = view; | 58 | inhibitor->view = view; |
59 | wl_list_insert(&inhibitor->manager->inhibitors, &inhibitor->link); | 59 | wl_list_insert(&manager->inhibitors, &inhibitor->link); |
60 | 60 | ||
61 | inhibitor->destroy.notify = handle_destroy; | 61 | inhibitor->destroy.notify = handle_destroy; |
62 | wl_signal_add(&view->events.unmap, &inhibitor->destroy); | 62 | wl_signal_add(&view->events.unmap, &inhibitor->destroy); |
63 | 63 | ||
64 | sway_idle_inhibit_v1_check_active(inhibitor->manager); | 64 | sway_idle_inhibit_v1_check_active(); |
65 | } | 65 | } |
66 | 66 | ||
67 | struct sway_idle_inhibitor_v1 *sway_idle_inhibit_v1_user_inhibitor_for_view( | 67 | struct sway_idle_inhibitor_v1 *sway_idle_inhibit_v1_user_inhibitor_for_view( |
68 | struct sway_view *view) { | 68 | struct sway_view *view) { |
69 | struct sway_idle_inhibit_manager_v1 *manager = &server.idle_inhibit_manager_v1; | ||
69 | struct sway_idle_inhibitor_v1 *inhibitor; | 70 | struct sway_idle_inhibitor_v1 *inhibitor; |
70 | wl_list_for_each(inhibitor, &server.idle_inhibit_manager_v1->inhibitors, | 71 | wl_list_for_each(inhibitor, &manager->inhibitors, link) { |
71 | link) { | 72 | if (inhibitor->mode != INHIBIT_IDLE_APPLICATION && |
72 | if (inhibitor->view == view && | 73 | inhibitor->view == view) { |
73 | inhibitor->mode != INHIBIT_IDLE_APPLICATION) { | ||
74 | return inhibitor; | 74 | return inhibitor; |
75 | } | 75 | } |
76 | } | 76 | } |
@@ -79,11 +79,11 @@ struct sway_idle_inhibitor_v1 *sway_idle_inhibit_v1_user_inhibitor_for_view( | |||
79 | 79 | ||
80 | struct sway_idle_inhibitor_v1 *sway_idle_inhibit_v1_application_inhibitor_for_view( | 80 | struct sway_idle_inhibitor_v1 *sway_idle_inhibit_v1_application_inhibitor_for_view( |
81 | struct sway_view *view) { | 81 | struct sway_view *view) { |
82 | struct sway_idle_inhibit_manager_v1 *manager = &server.idle_inhibit_manager_v1; | ||
82 | struct sway_idle_inhibitor_v1 *inhibitor; | 83 | struct sway_idle_inhibitor_v1 *inhibitor; |
83 | wl_list_for_each(inhibitor, &server.idle_inhibit_manager_v1->inhibitors, | 84 | wl_list_for_each(inhibitor, &manager->inhibitors, link) { |
84 | link) { | 85 | if (inhibitor->mode == INHIBIT_IDLE_APPLICATION && |
85 | if (inhibitor->view == view && | 86 | view_from_wlr_surface(inhibitor->wlr_inhibitor->surface) == view) { |
86 | inhibitor->mode == INHIBIT_IDLE_APPLICATION) { | ||
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) { |
@@ -130,8 +130,8 @@ bool sway_idle_inhibit_v1_is_active(struct sway_idle_inhibitor_v1 *inhibitor) { | |||
130 | return false; | 130 | return false; |
131 | } | 131 | } |
132 | 132 | ||
133 | void sway_idle_inhibit_v1_check_active( | 133 | void sway_idle_inhibit_v1_check_active(void) { |
134 | struct sway_idle_inhibit_manager_v1 *manager) { | 134 | struct sway_idle_inhibit_manager_v1 *manager = &server.idle_inhibit_manager_v1; |
135 | struct sway_idle_inhibitor_v1 *inhibitor; | 135 | struct sway_idle_inhibitor_v1 *inhibitor; |
136 | bool inhibited = false; | 136 | bool inhibited = false; |
137 | wl_list_for_each(inhibitor, &manager->inhibitors, link) { | 137 | wl_list_for_each(inhibitor, &manager->inhibitors, link) { |
@@ -139,27 +139,21 @@ void sway_idle_inhibit_v1_check_active( | |||
139 | break; | 139 | break; |
140 | } | 140 | } |
141 | } | 141 | } |
142 | wlr_idle_set_enabled(manager->idle, NULL, !inhibited); | 142 | wlr_idle_notifier_v1_set_inhibited(server.idle_notifier_v1, inhibited); |
143 | } | 143 | } |
144 | 144 | ||
145 | struct sway_idle_inhibit_manager_v1 *sway_idle_inhibit_manager_v1_create( | 145 | bool sway_idle_inhibit_manager_v1_init(void) { |
146 | struct wl_display *wl_display, struct wlr_idle *idle) { | 146 | struct sway_idle_inhibit_manager_v1 *manager = &server.idle_inhibit_manager_v1; |
147 | struct sway_idle_inhibit_manager_v1 *manager = | ||
148 | calloc(1, sizeof(struct sway_idle_inhibit_manager_v1)); | ||
149 | if (!manager) { | ||
150 | return NULL; | ||
151 | } | ||
152 | 147 | ||
153 | manager->wlr_manager = wlr_idle_inhibit_v1_create(wl_display); | 148 | manager->wlr_manager = wlr_idle_inhibit_v1_create(server.wl_display); |
154 | if (!manager->wlr_manager) { | 149 | if (!manager->wlr_manager) { |
155 | free(manager); | 150 | return false; |
156 | return NULL; | ||
157 | } | 151 | } |
158 | manager->idle = idle; | 152 | |
159 | wl_signal_add(&manager->wlr_manager->events.new_inhibitor, | 153 | wl_signal_add(&manager->wlr_manager->events.new_inhibitor, |
160 | &manager->new_idle_inhibitor_v1); | 154 | &manager->new_idle_inhibitor_v1); |
161 | manager->new_idle_inhibitor_v1.notify = handle_idle_inhibitor_v1; | 155 | manager->new_idle_inhibitor_v1.notify = handle_idle_inhibitor_v1; |
162 | wl_list_init(&manager->inhibitors); | 156 | wl_list_init(&manager->inhibitors); |
163 | 157 | ||
164 | return manager; | 158 | return true; |
165 | } | 159 | } |
diff --git a/sway/desktop/launcher.c b/sway/desktop/launcher.c new file mode 100644 index 00000000..28043d19 --- /dev/null +++ b/sway/desktop/launcher.c | |||
@@ -0,0 +1,267 @@ | |||
1 | #include <stdlib.h> | ||
2 | #include <string.h> | ||
3 | #include <wlr/types/wlr_xdg_activation_v1.h> | ||
4 | #include "sway/input/seat.h" | ||
5 | #include "sway/output.h" | ||
6 | #include "sway/desktop/launcher.h" | ||
7 | #include "sway/tree/node.h" | ||
8 | #include "sway/tree/container.h" | ||
9 | #include "sway/tree/workspace.h" | ||
10 | #include "sway/tree/root.h" | ||
11 | #include "log.h" | ||
12 | |||
13 | /** | ||
14 | * Get the pid of a parent process given the pid of a child process. | ||
15 | * | ||
16 | * Returns the parent pid or NULL if the parent pid cannot be determined. | ||
17 | */ | ||
18 | static pid_t get_parent_pid(pid_t child) { | ||
19 | pid_t parent = -1; | ||
20 | char file_name[100]; | ||
21 | char *buffer = NULL; | ||
22 | const char *sep = " "; | ||
23 | FILE *stat = NULL; | ||
24 | size_t buf_size = 0; | ||
25 | |||
26 | snprintf(file_name, sizeof(file_name), "/proc/%d/stat", child); | ||
27 | |||
28 | if ((stat = fopen(file_name, "r"))) { | ||
29 | if (getline(&buffer, &buf_size, stat) != -1) { | ||
30 | strtok(buffer, sep); // pid | ||
31 | strtok(NULL, sep); // executable name | ||
32 | strtok(NULL, sep); // state | ||
33 | char *token = strtok(NULL, sep); // parent pid | ||
34 | parent = strtol(token, NULL, 10); | ||
35 | } | ||
36 | free(buffer); | ||
37 | fclose(stat); | ||
38 | } | ||
39 | |||
40 | if (parent) { | ||
41 | return (parent == child) ? -1 : parent; | ||
42 | } | ||
43 | |||
44 | return -1; | ||
45 | } | ||
46 | |||
47 | void launcher_ctx_consume(struct launcher_ctx *ctx) { | ||
48 | // The view is now responsible for destroying this ctx | ||
49 | wl_list_remove(&ctx->token_destroy.link); | ||
50 | wl_list_init(&ctx->token_destroy.link); | ||
51 | |||
52 | if (!ctx->activated) { | ||
53 | // An unactivated token hasn't been destroyed yet | ||
54 | wlr_xdg_activation_token_v1_destroy(ctx->token); | ||
55 | } | ||
56 | ctx->token = NULL; | ||
57 | |||
58 | // Prevent additional matches | ||
59 | wl_list_remove(&ctx->link); | ||
60 | wl_list_init(&ctx->link); | ||
61 | } | ||
62 | |||
63 | void launcher_ctx_destroy(struct launcher_ctx *ctx) { | ||
64 | if (ctx == NULL) { | ||
65 | return; | ||
66 | } | ||
67 | wl_list_remove(&ctx->node_destroy.link); | ||
68 | wl_list_remove(&ctx->token_destroy.link); | ||
69 | if (ctx->seat) { | ||
70 | wl_list_remove(&ctx->seat_destroy.link); | ||
71 | } | ||
72 | wl_list_remove(&ctx->link); | ||
73 | wlr_xdg_activation_token_v1_destroy(ctx->token); | ||
74 | free(ctx->fallback_name); | ||
75 | free(ctx); | ||
76 | } | ||
77 | |||
78 | struct launcher_ctx *launcher_ctx_find_pid(pid_t pid) { | ||
79 | if (wl_list_empty(&server.pending_launcher_ctxs)) { | ||
80 | return NULL; | ||
81 | } | ||
82 | |||
83 | struct launcher_ctx *ctx = NULL; | ||
84 | sway_log(SWAY_DEBUG, "Looking up workspace for pid %d", pid); | ||
85 | |||
86 | do { | ||
87 | struct launcher_ctx *_ctx = NULL; | ||
88 | wl_list_for_each(_ctx, &server.pending_launcher_ctxs, link) { | ||
89 | if (pid == _ctx->pid) { | ||
90 | ctx = _ctx; | ||
91 | sway_log(SWAY_DEBUG, | ||
92 | "found %s match for pid %d: %s", | ||
93 | node_type_to_str(ctx->node->type), pid, node_get_name(ctx->node)); | ||
94 | break; | ||
95 | } | ||
96 | } | ||
97 | pid = get_parent_pid(pid); | ||
98 | } while (pid > 1); | ||
99 | |||
100 | return ctx; | ||
101 | } | ||
102 | |||
103 | struct sway_workspace *launcher_ctx_get_workspace( | ||
104 | struct launcher_ctx *ctx) { | ||
105 | struct sway_workspace *ws = NULL; | ||
106 | struct sway_output *output = NULL; | ||
107 | |||
108 | switch (ctx->node->type) { | ||
109 | case N_CONTAINER: | ||
110 | // Unimplemented | ||
111 | // TODO: add container matching? | ||
112 | ws = ctx->node->sway_container->pending.workspace; | ||
113 | break; | ||
114 | case N_WORKSPACE: | ||
115 | ws = ctx->node->sway_workspace; | ||
116 | break; | ||
117 | case N_OUTPUT: | ||
118 | output = ctx->node->sway_output; | ||
119 | ws = workspace_by_name(ctx->fallback_name); | ||
120 | if (!ws) { | ||
121 | sway_log(SWAY_DEBUG, | ||
122 | "Creating workspace %s for pid %d because it disappeared", | ||
123 | ctx->fallback_name, ctx->pid); | ||
124 | if (!output->enabled) { | ||
125 | sway_log(SWAY_DEBUG, | ||
126 | "Workspace output %s is disabled, trying another one", | ||
127 | output->wlr_output->name); | ||
128 | output = NULL; | ||
129 | } | ||
130 | ws = workspace_create(output, ctx->fallback_name); | ||
131 | } | ||
132 | break; | ||
133 | case N_ROOT: | ||
134 | ws = workspace_create(NULL, ctx->fallback_name); | ||
135 | break; | ||
136 | } | ||
137 | |||
138 | return ws; | ||
139 | } | ||
140 | |||
141 | static void ctx_handle_node_destroy(struct wl_listener *listener, void *data) { | ||
142 | struct launcher_ctx *ctx = wl_container_of(listener, ctx, node_destroy); | ||
143 | switch (ctx->node->type) { | ||
144 | case N_CONTAINER: | ||
145 | // Unimplemented | ||
146 | break; | ||
147 | case N_WORKSPACE:; | ||
148 | struct sway_workspace *ws = ctx->node->sway_workspace; | ||
149 | wl_list_remove(&ctx->node_destroy.link); | ||
150 | wl_list_init(&ctx->node_destroy.link); | ||
151 | // We want to save this ws name to recreate later, hopefully on the | ||
152 | // same output | ||
153 | free(ctx->fallback_name); | ||
154 | ctx->fallback_name = strdup(ws->name); | ||
155 | if (!ws->output || ws->output->node.destroying) { | ||
156 | // If the output is being destroyed it would be pointless to track | ||
157 | // If the output is being disabled, we'll find out if it's still | ||
158 | // disabled when we try to match it. | ||
159 | ctx->node = &root->node; | ||
160 | break; | ||
161 | } | ||
162 | ctx->node = &ws->output->node; | ||
163 | wl_signal_add(&ctx->node->events.destroy, &ctx->node_destroy); | ||
164 | break; | ||
165 | case N_OUTPUT: | ||
166 | wl_list_remove(&ctx->node_destroy.link); | ||
167 | wl_list_init(&ctx->node_destroy.link); | ||
168 | // We'll make the ws ctx->name somewhere else | ||
169 | ctx->node = &root->node; | ||
170 | break; | ||
171 | case N_ROOT: | ||
172 | // Unreachable | ||
173 | break; | ||
174 | } | ||
175 | } | ||
176 | |||
177 | static void token_handle_destroy(struct wl_listener *listener, void *data) { | ||
178 | struct launcher_ctx *ctx = wl_container_of(listener, ctx, token_destroy); | ||
179 | ctx->token = NULL; | ||
180 | launcher_ctx_destroy(ctx); | ||
181 | } | ||
182 | |||
183 | struct launcher_ctx *launcher_ctx_create(struct wlr_xdg_activation_token_v1 *token, | ||
184 | struct sway_node *node) { | ||
185 | struct launcher_ctx *ctx = calloc(1, sizeof(struct launcher_ctx)); | ||
186 | |||
187 | const char *fallback_name = NULL; | ||
188 | struct sway_workspace *ws = NULL; | ||
189 | switch (node->type) { | ||
190 | case N_CONTAINER: | ||
191 | // Unimplemented | ||
192 | free(ctx); | ||
193 | return NULL; | ||
194 | case N_WORKSPACE: | ||
195 | ws = node->sway_workspace; | ||
196 | fallback_name = ws->name; | ||
197 | break; | ||
198 | case N_OUTPUT:; | ||
199 | struct sway_output *output = node->sway_output; | ||
200 | ws = output_get_active_workspace(output); | ||
201 | fallback_name = ws ? ws->name : NULL; | ||
202 | break; | ||
203 | case N_ROOT: | ||
204 | // Unimplemented | ||
205 | free(ctx); | ||
206 | return NULL; | ||
207 | } | ||
208 | |||
209 | if (!fallback_name) { | ||
210 | // TODO: implement a better fallback. | ||
211 | free(ctx); | ||
212 | return NULL; | ||
213 | } | ||
214 | |||
215 | ctx->fallback_name = strdup(fallback_name); | ||
216 | ctx->token = token; | ||
217 | ctx->node = node; | ||
218 | // Having surface set means that the focus check in wlroots has passed | ||
219 | ctx->had_focused_surface = token->surface != NULL; | ||
220 | |||
221 | ctx->node_destroy.notify = ctx_handle_node_destroy; | ||
222 | wl_signal_add(&ctx->node->events.destroy, &ctx->node_destroy); | ||
223 | |||
224 | ctx->token_destroy.notify = token_handle_destroy; | ||
225 | wl_signal_add(&token->events.destroy, &ctx->token_destroy); | ||
226 | |||
227 | wl_list_init(&ctx->link); | ||
228 | wl_list_insert(&server.pending_launcher_ctxs, &ctx->link); | ||
229 | |||
230 | token->data = ctx; | ||
231 | return ctx; | ||
232 | } | ||
233 | |||
234 | static void launch_ctx_handle_seat_destroy(struct wl_listener *listener, void *data) { | ||
235 | struct launcher_ctx *ctx = wl_container_of(listener, ctx, seat_destroy); | ||
236 | ctx->seat = NULL; | ||
237 | wl_list_remove(&ctx->seat_destroy.link); | ||
238 | } | ||
239 | |||
240 | // Creates a context with a new token for the internal launcher | ||
241 | struct launcher_ctx *launcher_ctx_create_internal(void) { | ||
242 | struct sway_seat *seat = input_manager_current_seat(); | ||
243 | struct sway_workspace *ws = seat_get_focused_workspace(seat); | ||
244 | if (!ws) { | ||
245 | sway_log(SWAY_DEBUG, "Failed to create launch context. No workspace."); | ||
246 | return NULL; | ||
247 | } | ||
248 | |||
249 | struct wlr_xdg_activation_token_v1 *token = | ||
250 | wlr_xdg_activation_token_v1_create(server.xdg_activation_v1); | ||
251 | |||
252 | struct launcher_ctx *ctx = launcher_ctx_create(token, &ws->node); | ||
253 | if (!ctx) { | ||
254 | wlr_xdg_activation_token_v1_destroy(token); | ||
255 | return NULL; | ||
256 | } | ||
257 | ctx->seat = seat; | ||
258 | ctx->seat_destroy.notify = launch_ctx_handle_seat_destroy; | ||
259 | wl_signal_add(&seat->wlr_seat->events.destroy, &ctx->seat_destroy); | ||
260 | |||
261 | return ctx; | ||
262 | } | ||
263 | |||
264 | const char *launcher_ctx_get_token_name(struct launcher_ctx *ctx) { | ||
265 | const char *token = wlr_xdg_activation_token_v1_get_name(ctx->token); | ||
266 | return token; | ||
267 | } | ||
diff --git a/sway/desktop/layer_shell.c b/sway/desktop/layer_shell.c index d4ca4fb4..6221b7b9 100644 --- a/sway/desktop/layer_shell.c +++ b/sway/desktop/layer_shell.c | |||
@@ -2,11 +2,14 @@ | |||
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> | 5 | #include <wlr/types/wlr_fractional_scale_v1.h> |
6 | #include <wlr/types/wlr_layer_shell_v1.h> | 6 | #include <wlr/types/wlr_layer_shell_v1.h> |
7 | #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_scene.h> | ||
9 | #include <wlr/types/wlr_subcompositor.h> | ||
10 | #include <wlr/types/wlr_xdg_shell.h> | ||
9 | #include "log.h" | 11 | #include "log.h" |
12 | #include "sway/scene_descriptor.h" | ||
10 | #include "sway/desktop/transaction.h" | 13 | #include "sway/desktop/transaction.h" |
11 | #include "sway/input/cursor.h" | 14 | #include "sway/input/cursor.h" |
12 | #include "sway/input/input-manager.h" | 15 | #include "sway/input/input-manager.h" |
@@ -17,155 +20,55 @@ | |||
17 | #include "sway/tree/arrange.h" | 20 | #include "sway/tree/arrange.h" |
18 | #include "sway/tree/workspace.h" | 21 | #include "sway/tree/workspace.h" |
19 | 22 | ||
20 | static void apply_exclusive(struct wlr_box *usable_area, | 23 | struct wlr_layer_surface_v1 *toplevel_layer_surface_from_surface( |
21 | uint32_t anchor, int32_t exclusive, | 24 | struct wlr_surface *surface) { |
22 | int32_t margin_top, int32_t margin_right, | 25 | struct wlr_layer_surface_v1 *layer; |
23 | int32_t margin_bottom, int32_t margin_left) { | 26 | do { |
24 | if (exclusive <= 0) { | 27 | if (!surface) { |
25 | return; | 28 | return NULL; |
26 | } | 29 | } |
27 | struct { | 30 | // Topmost layer surface |
28 | uint32_t singular_anchor; | 31 | if ((layer = wlr_layer_surface_v1_try_from_wlr_surface(surface))) { |
29 | uint32_t anchor_triplet; | 32 | return layer; |
30 | int *positive_axis; | 33 | } |
31 | int *negative_axis; | 34 | // Layer subsurface |
32 | int margin; | 35 | if (wlr_subsurface_try_from_wlr_surface(surface)) { |
33 | } edges[] = { | 36 | surface = wlr_surface_get_root_surface(surface); |
34 | // Top | 37 | continue; |
35 | { | 38 | } |
36 | .singular_anchor = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP, | 39 | |
37 | .anchor_triplet = | 40 | // Layer surface popup |
38 | ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | | 41 | struct wlr_xdg_surface *xdg_surface = NULL; |
39 | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT | | 42 | if ((xdg_surface = wlr_xdg_surface_try_from_wlr_surface(surface)) && |
40 | ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP, | 43 | xdg_surface->role == WLR_XDG_SURFACE_ROLE_POPUP && xdg_surface->popup != NULL) { |
41 | .positive_axis = &usable_area->y, | 44 | if (!xdg_surface->popup->parent) { |
42 | .negative_axis = &usable_area->height, | 45 | return NULL; |
43 | .margin = margin_top, | ||
44 | }, | ||
45 | // Bottom | ||
46 | { | ||
47 | .singular_anchor = ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM, | ||
48 | .anchor_triplet = | ||
49 | ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | | ||
50 | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT | | ||
51 | ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM, | ||
52 | .positive_axis = NULL, | ||
53 | .negative_axis = &usable_area->height, | ||
54 | .margin = margin_bottom, | ||
55 | }, | ||
56 | // Left | ||
57 | { | ||
58 | .singular_anchor = ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT, | ||
59 | .anchor_triplet = | ||
60 | ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | | ||
61 | ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | | ||
62 | ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM, | ||
63 | .positive_axis = &usable_area->x, | ||
64 | .negative_axis = &usable_area->width, | ||
65 | .margin = margin_left, | ||
66 | }, | ||
67 | // Right | ||
68 | { | ||
69 | .singular_anchor = ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT, | ||
70 | .anchor_triplet = | ||
71 | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT | | ||
72 | ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | | ||
73 | ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM, | ||
74 | .positive_axis = NULL, | ||
75 | .negative_axis = &usable_area->width, | ||
76 | .margin = margin_right, | ||
77 | }, | ||
78 | }; | ||
79 | for (size_t i = 0; i < sizeof(edges) / sizeof(edges[0]); ++i) { | ||
80 | if ((anchor == edges[i].singular_anchor || anchor == edges[i].anchor_triplet) | ||
81 | && exclusive + edges[i].margin > 0) { | ||
82 | if (edges[i].positive_axis) { | ||
83 | *edges[i].positive_axis += exclusive + edges[i].margin; | ||
84 | } | ||
85 | if (edges[i].negative_axis) { | ||
86 | *edges[i].negative_axis -= exclusive + edges[i].margin; | ||
87 | } | 46 | } |
88 | break; | 47 | surface = wlr_surface_get_root_surface(xdg_surface->popup->parent); |
48 | continue; | ||
89 | } | 49 | } |
90 | } | 50 | |
51 | // Return early if the surface is not a layer/xdg_popup/sub surface | ||
52 | return NULL; | ||
53 | } while (true); | ||
91 | } | 54 | } |
92 | 55 | ||
93 | static void arrange_layer(struct sway_output *output, struct wl_list *list, | 56 | static void arrange_surface(struct sway_output *output, const struct wlr_box *full_area, |
94 | struct wlr_box *usable_area, bool exclusive) { | 57 | struct wlr_box *usable_area, struct wlr_scene_tree *tree) { |
95 | struct sway_layer_surface *sway_layer; | 58 | struct wlr_scene_node *node; |
96 | struct wlr_box full_area = { 0 }; | 59 | wl_list_for_each(node, &tree->children, link) { |
97 | wlr_output_effective_resolution(output->wlr_output, | 60 | struct sway_layer_surface *surface = scene_descriptor_try_get(node, |
98 | &full_area.width, &full_area.height); | 61 | SWAY_SCENE_DESC_LAYER_SHELL); |
99 | wl_list_for_each(sway_layer, list, link) { | 62 | // surface could be null during destruction |
100 | struct wlr_layer_surface_v1 *layer = sway_layer->layer_surface; | 63 | if (!surface) { |
101 | struct wlr_layer_surface_v1_state *state = &layer->current; | ||
102 | if (exclusive != (state->exclusive_zone > 0)) { | ||
103 | continue; | 64 | continue; |
104 | } | 65 | } |
105 | struct wlr_box bounds; | 66 | |
106 | if (state->exclusive_zone == -1) { | 67 | if (!surface->scene->layer_surface->initialized) { |
107 | bounds = full_area; | ||
108 | } else { | ||
109 | bounds = *usable_area; | ||
110 | } | ||
111 | struct wlr_box box = { | ||
112 | .width = state->desired_width, | ||
113 | .height = state->desired_height | ||
114 | }; | ||
115 | // Horizontal axis | ||
116 | const uint32_t both_horiz = ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | ||
117 | | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT; | ||
118 | if ((state->anchor & both_horiz) && box.width == 0) { | ||
119 | box.x = bounds.x; | ||
120 | box.width = bounds.width; | ||
121 | } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT)) { | ||
122 | box.x = bounds.x; | ||
123 | } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT)) { | ||
124 | box.x = bounds.x + (bounds.width - box.width); | ||
125 | } else { | ||
126 | box.x = bounds.x + ((bounds.width / 2) - (box.width / 2)); | ||
127 | } | ||
128 | // Vertical axis | ||
129 | const uint32_t both_vert = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | ||
130 | | ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM; | ||
131 | if ((state->anchor & both_vert) && box.height == 0) { | ||
132 | box.y = bounds.y; | ||
133 | box.height = bounds.height; | ||
134 | } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP)) { | ||
135 | box.y = bounds.y; | ||
136 | } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM)) { | ||
137 | box.y = bounds.y + (bounds.height - box.height); | ||
138 | } else { | ||
139 | box.y = bounds.y + ((bounds.height / 2) - (box.height / 2)); | ||
140 | } | ||
141 | // Margin | ||
142 | if ((state->anchor & both_horiz) == both_horiz) { | ||
143 | box.x += state->margin.left; | ||
144 | box.width -= state->margin.left + state->margin.right; | ||
145 | } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT)) { | ||
146 | box.x += state->margin.left; | ||
147 | } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT)) { | ||
148 | box.x -= state->margin.right; | ||
149 | } | ||
150 | if ((state->anchor & both_vert) == both_vert) { | ||
151 | box.y += state->margin.top; | ||
152 | box.height -= state->margin.top + state->margin.bottom; | ||
153 | } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP)) { | ||
154 | box.y += state->margin.top; | ||
155 | } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM)) { | ||
156 | box.y -= state->margin.bottom; | ||
157 | } | ||
158 | if (box.width < 0 || box.height < 0) { | ||
159 | // TODO: Bubble up a protocol error? | ||
160 | wlr_layer_surface_v1_close(layer); | ||
161 | continue; | 68 | continue; |
162 | } | 69 | } |
163 | // Apply | 70 | |
164 | sway_layer->geo = box; | 71 | wlr_scene_layer_surface_v1_configure(surface->scene, full_area, usable_area); |
165 | apply_exclusive(usable_area, state->anchor, state->exclusive_zone, | ||
166 | state->margin.top, state->margin.right, | ||
167 | state->margin.bottom, state->margin.left); | ||
168 | wlr_layer_surface_v1_configure(layer, box.width, box.height); | ||
169 | } | 72 | } |
170 | } | 73 | } |
171 | 74 | ||
@@ -173,81 +76,94 @@ void arrange_layers(struct sway_output *output) { | |||
173 | struct wlr_box usable_area = { 0 }; | 76 | struct wlr_box usable_area = { 0 }; |
174 | wlr_output_effective_resolution(output->wlr_output, | 77 | wlr_output_effective_resolution(output->wlr_output, |
175 | &usable_area.width, &usable_area.height); | 78 | &usable_area.width, &usable_area.height); |
79 | const struct wlr_box full_area = usable_area; | ||
80 | |||
81 | arrange_surface(output, &full_area, &usable_area, output->layers.shell_background); | ||
82 | arrange_surface(output, &full_area, &usable_area, output->layers.shell_bottom); | ||
83 | arrange_surface(output, &full_area, &usable_area, output->layers.shell_top); | ||
84 | arrange_surface(output, &full_area, &usable_area, output->layers.shell_overlay); | ||
176 | 85 | ||
177 | // Arrange exclusive surfaces from top->bottom | 86 | if (!wlr_box_equal(&usable_area, &output->usable_area)) { |
178 | arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], | ||
179 | &usable_area, true); | ||
180 | arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP], | ||
181 | &usable_area, true); | ||
182 | arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM], | ||
183 | &usable_area, true); | ||
184 | arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND], | ||
185 | &usable_area, true); | ||
186 | |||
187 | if (memcmp(&usable_area, &output->usable_area, | ||
188 | sizeof(struct wlr_box)) != 0) { | ||
189 | sway_log(SWAY_DEBUG, "Usable area changed, rearranging output"); | 87 | sway_log(SWAY_DEBUG, "Usable area changed, rearranging output"); |
190 | memcpy(&output->usable_area, &usable_area, sizeof(struct wlr_box)); | 88 | output->usable_area = usable_area; |
191 | arrange_output(output); | 89 | arrange_output(output); |
90 | } else { | ||
91 | arrange_popups(root->layers.popup); | ||
192 | } | 92 | } |
93 | } | ||
193 | 94 | ||
194 | // Arrange non-exlusive surfaces from top->bottom | 95 | static struct wlr_scene_tree *sway_layer_get_scene(struct sway_output *output, |
195 | arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], | 96 | enum zwlr_layer_shell_v1_layer type) { |
196 | &usable_area, false); | 97 | switch (type) { |
197 | arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP], | 98 | case ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND: |
198 | &usable_area, false); | 99 | return output->layers.shell_background; |
199 | arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM], | 100 | case ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM: |
200 | &usable_area, false); | 101 | return output->layers.shell_bottom; |
201 | arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND], | 102 | case ZWLR_LAYER_SHELL_V1_LAYER_TOP: |
202 | &usable_area, false); | 103 | return output->layers.shell_top; |
203 | 104 | case ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY: | |
204 | // Find topmost keyboard interactive layer, if such a layer exists | 105 | return output->layers.shell_overlay; |
205 | uint32_t layers_above_shell[] = { | ||
206 | ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY, | ||
207 | ZWLR_LAYER_SHELL_V1_LAYER_TOP, | ||
208 | }; | ||
209 | size_t nlayers = sizeof(layers_above_shell) / sizeof(layers_above_shell[0]); | ||
210 | struct sway_layer_surface *layer, *topmost = NULL; | ||
211 | for (size_t i = 0; i < nlayers; ++i) { | ||
212 | wl_list_for_each_reverse(layer, | ||
213 | &output->layers[layers_above_shell[i]], link) { | ||
214 | if (layer->layer_surface->current.keyboard_interactive && | ||
215 | layer->layer_surface->mapped) { | ||
216 | topmost = layer; | ||
217 | break; | ||
218 | } | ||
219 | } | ||
220 | if (topmost != NULL) { | ||
221 | break; | ||
222 | } | ||
223 | } | 106 | } |
224 | 107 | ||
225 | struct sway_seat *seat; | 108 | sway_assert(false, "unreachable"); |
226 | wl_list_for_each(seat, &server.input->seats, link) { | 109 | return NULL; |
227 | if (topmost != NULL) { | 110 | } |
228 | seat_set_focus_layer(seat, topmost->layer_surface); | 111 | |
229 | } else if (seat->focused_layer && | 112 | static struct sway_layer_surface *sway_layer_surface_create( |
230 | !seat->focused_layer->current.keyboard_interactive) { | 113 | struct wlr_scene_layer_surface_v1 *scene) { |
231 | seat_set_focus_layer(seat, NULL); | 114 | struct sway_layer_surface *surface = calloc(1, sizeof(*surface)); |
232 | } | 115 | if (!surface) { |
116 | sway_log(SWAY_ERROR, "Could not allocate a scene_layer surface"); | ||
117 | return NULL; | ||
118 | } | ||
119 | |||
120 | struct wlr_scene_tree *popups = wlr_scene_tree_create(root->layers.popup); | ||
121 | if (!popups) { | ||
122 | sway_log(SWAY_ERROR, "Could not allocate a scene_layer popup node"); | ||
123 | free(surface); | ||
124 | return NULL; | ||
125 | } | ||
126 | |||
127 | surface->desc.relative = &scene->tree->node; | ||
128 | |||
129 | if (!scene_descriptor_assign(&popups->node, | ||
130 | SWAY_SCENE_DESC_POPUP, &surface->desc)) { | ||
131 | sway_log(SWAY_ERROR, "Failed to allocate a popup scene descriptor"); | ||
132 | wlr_scene_node_destroy(&popups->node); | ||
133 | free(surface); | ||
134 | return NULL; | ||
233 | } | 135 | } |
136 | |||
137 | surface->tree = scene->tree; | ||
138 | surface->scene = scene; | ||
139 | surface->layer_surface = scene->layer_surface; | ||
140 | surface->popups = popups; | ||
141 | surface->layer_surface->data = surface; | ||
142 | |||
143 | return surface; | ||
234 | } | 144 | } |
235 | 145 | ||
236 | static struct sway_layer_surface *find_mapped_layer_by_client( | 146 | static struct sway_layer_surface *find_mapped_layer_by_client( |
237 | struct wl_client *client, struct wlr_output *ignore_output) { | 147 | struct wl_client *client, struct sway_output *ignore_output) { |
238 | for (int i = 0; i < root->outputs->length; ++i) { | 148 | for (int i = 0; i < root->outputs->length; ++i) { |
239 | struct sway_output *output = root->outputs->items[i]; | 149 | struct sway_output *output = root->outputs->items[i]; |
240 | if (output->wlr_output == ignore_output) { | 150 | if (output == ignore_output) { |
241 | continue; | 151 | continue; |
242 | } | 152 | } |
243 | // For now we'll only check the overlay layer | 153 | // For now we'll only check the overlay layer |
244 | struct sway_layer_surface *lsurface; | 154 | struct wlr_scene_node *node; |
245 | wl_list_for_each(lsurface, | 155 | wl_list_for_each (node, &output->layers.shell_overlay->children, link) { |
246 | &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], link) { | 156 | struct sway_layer_surface *surface = scene_descriptor_try_get(node, |
247 | struct wl_resource *resource = lsurface->layer_surface->resource; | 157 | SWAY_SCENE_DESC_LAYER_SHELL); |
158 | if (!surface) { | ||
159 | continue; | ||
160 | } | ||
161 | |||
162 | struct wlr_layer_surface_v1 *layer_surface = surface->layer_surface; | ||
163 | struct wl_resource *resource = layer_surface->resource; | ||
248 | if (wl_resource_get_client(resource) == client | 164 | if (wl_resource_get_client(resource) == client |
249 | && lsurface->layer_surface->mapped) { | 165 | && layer_surface->surface->mapped) { |
250 | return lsurface; | 166 | return surface; |
251 | } | 167 | } |
252 | } | 168 | } |
253 | } | 169 | } |
@@ -255,280 +171,142 @@ static struct sway_layer_surface *find_mapped_layer_by_client( | |||
255 | } | 171 | } |
256 | 172 | ||
257 | static void handle_output_destroy(struct wl_listener *listener, void *data) { | 173 | static void handle_output_destroy(struct wl_listener *listener, void *data) { |
258 | struct sway_layer_surface *sway_layer = | 174 | struct sway_layer_surface *layer = |
259 | wl_container_of(listener, sway_layer, output_destroy); | 175 | wl_container_of(listener, layer, output_destroy); |
260 | // Determine if this layer is being used by an exclusive client. If it is, | ||
261 | // try and find another layer owned by this client to pass focus to. | ||
262 | struct sway_seat *seat = input_manager_get_default_seat(); | ||
263 | struct wl_client *client = | ||
264 | wl_resource_get_client(sway_layer->layer_surface->resource); | ||
265 | bool set_focus = seat->exclusive_client == client; | ||
266 | |||
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) { | ||
272 | struct sway_layer_surface *layer = | ||
273 | find_mapped_layer_by_client(client, sway_layer->layer_surface->output); | ||
274 | if (layer) { | ||
275 | seat_set_focus_layer(seat, layer->layer_surface); | ||
276 | } | ||
277 | } | ||
278 | 176 | ||
279 | sway_layer->layer_surface->output = NULL; | 177 | layer->output = NULL; |
280 | wlr_layer_surface_v1_close(sway_layer->layer_surface); | 178 | wlr_scene_node_destroy(&layer->scene->tree->node); |
281 | } | 179 | } |
282 | 180 | ||
283 | static void handle_surface_commit(struct wl_listener *listener, void *data) { | 181 | static void handle_node_destroy(struct wl_listener *listener, void *data) { |
284 | struct sway_layer_surface *layer = | 182 | struct sway_layer_surface *layer = |
285 | wl_container_of(listener, layer, surface_commit); | 183 | wl_container_of(listener, layer, node_destroy); |
286 | struct wlr_layer_surface_v1 *layer_surface = layer->layer_surface; | ||
287 | struct wlr_output *wlr_output = layer_surface->output; | ||
288 | if (wlr_output == NULL) { | ||
289 | return; | ||
290 | } | ||
291 | 184 | ||
292 | struct sway_output *output = wlr_output->data; | 185 | // destroy the scene descriptor straight away if it exists, otherwise |
293 | struct wlr_box old_geo = layer->geo; | 186 | // we will try to reflow still considering the destroyed node. |
294 | arrange_layers(output); | 187 | scene_descriptor_destroy(&layer->tree->node, SWAY_SCENE_DESC_LAYER_SHELL); |
295 | |||
296 | bool geo_changed = | ||
297 | memcmp(&old_geo, &layer->geo, sizeof(struct wlr_box)) != 0; | ||
298 | bool 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 | 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, | ||
309 | layer_surface->surface, true); | ||
310 | } else { | ||
311 | output_damage_surface(output, layer->geo.x, layer->geo.y, | ||
312 | layer_surface->surface, false); | ||
313 | } | ||
314 | |||
315 | transaction_commit_dirty(); | ||
316 | } | ||
317 | 188 | ||
318 | static void unmap(struct sway_layer_surface *sway_layer) { | 189 | // Determine if this layer is being used by an exclusive client. If it is, |
319 | struct sway_seat *seat; | 190 | // try and find another layer owned by this client to pass focus to. |
320 | wl_list_for_each(seat, &server.input->seats, link) { | 191 | struct sway_seat *seat = input_manager_get_default_seat(); |
321 | if (seat->focused_layer == sway_layer->layer_surface) { | 192 | struct wl_client *client = |
322 | seat_set_focus_layer(seat, NULL); | 193 | wl_resource_get_client(layer->layer_surface->resource); |
194 | if (!server.session_lock.lock) { | ||
195 | struct sway_layer_surface *consider_layer = | ||
196 | find_mapped_layer_by_client(client, layer->output); | ||
197 | if (consider_layer) { | ||
198 | seat_set_focus_layer(seat, consider_layer->layer_surface); | ||
323 | } | 199 | } |
324 | } | 200 | } |
325 | 201 | ||
326 | cursor_rebase_all(); | 202 | if (layer->output) { |
327 | 203 | arrange_layers(layer->output); | |
328 | struct wlr_output *wlr_output = sway_layer->layer_surface->output; | 204 | transaction_commit_dirty(); |
329 | if (wlr_output == NULL) { | ||
330 | return; | ||
331 | } | 205 | } |
332 | 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, | ||
337 | sway_layer->layer_surface->surface, true); | ||
338 | } | ||
339 | 206 | ||
340 | static void handle_destroy(struct wl_listener *listener, void *data) { | 207 | wlr_scene_node_destroy(&layer->popups->node); |
341 | struct sway_layer_surface *sway_layer = | ||
342 | wl_container_of(listener, sway_layer, destroy); | ||
343 | sway_log(SWAY_DEBUG, "Layer surface destroyed (%s)", | ||
344 | sway_layer->layer_surface->namespace); | ||
345 | if (sway_layer->layer_surface->mapped) { | ||
346 | unmap(sway_layer); | ||
347 | } | ||
348 | wl_list_remove(&sway_layer->link); | ||
349 | wl_list_remove(&sway_layer->destroy.link); | ||
350 | wl_list_remove(&sway_layer->map.link); | ||
351 | wl_list_remove(&sway_layer->unmap.link); | ||
352 | wl_list_remove(&sway_layer->surface_commit.link); | ||
353 | wl_list_remove(&sway_layer->new_popup.link); | ||
354 | wl_list_remove(&sway_layer->new_subsurface.link); | ||
355 | if (sway_layer->layer_surface->output != NULL) { | ||
356 | struct sway_output *output = sway_layer->layer_surface->output->data; | ||
357 | if (output != NULL) { | ||
358 | arrange_layers(output); | ||
359 | transaction_commit_dirty(); | ||
360 | } | ||
361 | wl_list_remove(&sway_layer->output_destroy.link); | ||
362 | sway_layer->layer_surface->output = NULL; | ||
363 | } | ||
364 | free(sway_layer); | ||
365 | } | ||
366 | 208 | ||
367 | static void handle_map(struct wl_listener *listener, void *data) { | 209 | wl_list_remove(&layer->map.link); |
368 | struct sway_layer_surface *sway_layer = wl_container_of(listener, | 210 | wl_list_remove(&layer->unmap.link); |
369 | sway_layer, map); | 211 | wl_list_remove(&layer->surface_commit.link); |
370 | struct sway_output *output = sway_layer->layer_surface->output->data; | 212 | wl_list_remove(&layer->node_destroy.link); |
371 | output_damage_surface(output, sway_layer->geo.x, sway_layer->geo.y, | 213 | wl_list_remove(&layer->output_destroy.link); |
372 | sway_layer->layer_surface->surface, true); | ||
373 | wlr_surface_send_enter(sway_layer->layer_surface->surface, | ||
374 | sway_layer->layer_surface->output); | ||
375 | cursor_rebase_all(); | ||
376 | } | ||
377 | 214 | ||
378 | static void handle_unmap(struct wl_listener *listener, void *data) { | 215 | layer->layer_surface->data = NULL; |
379 | struct sway_layer_surface *sway_layer = wl_container_of( | ||
380 | listener, sway_layer, unmap); | ||
381 | unmap(sway_layer); | ||
382 | } | ||
383 | 216 | ||
384 | static void subsurface_damage(struct sway_layer_subsurface *subsurface, | 217 | free(layer); |
385 | bool whole) { | ||
386 | struct sway_layer_surface *layer = subsurface->layer_surface; | ||
387 | struct wlr_output *wlr_output = layer->layer_surface->output; | ||
388 | if (!wlr_output) { | ||
389 | return; | ||
390 | } | ||
391 | struct sway_output *output = wlr_output->data; | ||
392 | int ox = subsurface->wlr_subsurface->current.x + layer->geo.x; | ||
393 | int oy = subsurface->wlr_subsurface->current.y + layer->geo.y; | ||
394 | output_damage_surface( | ||
395 | output, ox, oy, subsurface->wlr_subsurface->surface, whole); | ||
396 | } | 218 | } |
397 | 219 | ||
398 | static void subsurface_handle_unmap(struct wl_listener *listener, void *data) { | 220 | static void handle_surface_commit(struct wl_listener *listener, void *data) { |
399 | struct sway_layer_subsurface *subsurface = | 221 | struct sway_layer_surface *surface = |
400 | wl_container_of(listener, subsurface, unmap); | 222 | wl_container_of(listener, surface, surface_commit); |
401 | subsurface_damage(subsurface, true); | ||
402 | } | ||
403 | |||
404 | static void subsurface_handle_map(struct wl_listener *listener, void *data) { | ||
405 | struct sway_layer_subsurface *subsurface = | ||
406 | wl_container_of(listener, subsurface, map); | ||
407 | subsurface_damage(subsurface, true); | ||
408 | } | ||
409 | |||
410 | static void subsurface_handle_commit(struct wl_listener *listener, void *data) { | ||
411 | struct sway_layer_subsurface *subsurface = | ||
412 | wl_container_of(listener, subsurface, commit); | ||
413 | subsurface_damage(subsurface, false); | ||
414 | } | ||
415 | |||
416 | static void subsurface_handle_destroy(struct wl_listener *listener, | ||
417 | void *data) { | ||
418 | struct sway_layer_subsurface *subsurface = | ||
419 | wl_container_of(listener, subsurface, destroy); | ||
420 | |||
421 | wl_list_remove(&subsurface->map.link); | ||
422 | wl_list_remove(&subsurface->unmap.link); | ||
423 | wl_list_remove(&subsurface->destroy.link); | ||
424 | wl_list_remove(&subsurface->commit.link); | ||
425 | free(subsurface); | ||
426 | } | ||
427 | 223 | ||
428 | static struct sway_layer_subsurface *create_subsurface( | 224 | struct wlr_layer_surface_v1 *layer_surface = surface->layer_surface; |
429 | struct wlr_subsurface *wlr_subsurface, | 225 | if (!layer_surface->initialized) { |
430 | struct sway_layer_surface *layer_surface) { | 226 | return; |
431 | struct sway_layer_subsurface *subsurface = | ||
432 | calloc(1, sizeof(struct sway_layer_surface)); | ||
433 | if (subsurface == NULL) { | ||
434 | return NULL; | ||
435 | } | 227 | } |
436 | 228 | ||
437 | subsurface->wlr_subsurface = wlr_subsurface; | 229 | uint32_t committed = layer_surface->current.committed; |
438 | subsurface->layer_surface = layer_surface; | 230 | if (committed & WLR_LAYER_SURFACE_V1_STATE_LAYER) { |
439 | 231 | enum zwlr_layer_shell_v1_layer layer_type = layer_surface->current.layer; | |
440 | subsurface->map.notify = subsurface_handle_map; | 232 | struct wlr_scene_tree *output_layer = sway_layer_get_scene( |
441 | wl_signal_add(&wlr_subsurface->events.map, &subsurface->map); | 233 | surface->output, layer_type); |
442 | subsurface->unmap.notify = subsurface_handle_unmap; | 234 | wlr_scene_node_reparent(&surface->scene->tree->node, output_layer); |
443 | wl_signal_add(&wlr_subsurface->events.unmap, &subsurface->unmap); | 235 | } |
444 | subsurface->destroy.notify = subsurface_handle_destroy; | ||
445 | wl_signal_add(&wlr_subsurface->events.destroy, &subsurface->destroy); | ||
446 | subsurface->commit.notify = subsurface_handle_commit; | ||
447 | wl_signal_add(&wlr_subsurface->surface->events.commit, &subsurface->commit); | ||
448 | |||
449 | return subsurface; | ||
450 | } | ||
451 | |||
452 | static void handle_new_subsurface(struct wl_listener *listener, void *data) { | ||
453 | struct sway_layer_surface *sway_layer_surface = | ||
454 | wl_container_of(listener, sway_layer_surface, new_subsurface); | ||
455 | struct wlr_subsurface *wlr_subsurface = data; | ||
456 | create_subsurface(wlr_subsurface, sway_layer_surface); | ||
457 | } | ||
458 | |||
459 | 236 | ||
460 | static struct sway_layer_surface *popup_get_layer( | 237 | if (layer_surface->initial_commit || committed || layer_surface->surface->mapped != surface->mapped) { |
461 | struct sway_layer_popup *popup) { | 238 | surface->mapped = layer_surface->surface->mapped; |
462 | while (popup->parent_type == LAYER_PARENT_POPUP) { | 239 | arrange_layers(surface->output); |
463 | popup = popup->parent_popup; | 240 | transaction_commit_dirty(); |
464 | } | 241 | } |
465 | return popup->parent_layer; | ||
466 | } | 242 | } |
467 | 243 | ||
468 | static void popup_damage(struct sway_layer_popup *layer_popup, bool whole) { | 244 | static void handle_map(struct wl_listener *listener, void *data) { |
469 | struct wlr_xdg_popup *popup = layer_popup->wlr_popup; | 245 | struct sway_layer_surface *surface = wl_container_of(listener, |
470 | struct wlr_surface *surface = popup->base->surface; | 246 | surface, map); |
471 | int popup_sx = popup->geometry.x - popup->base->geometry.x; | 247 | |
472 | int popup_sy = popup->geometry.y - popup->base->geometry.y; | 248 | struct wlr_layer_surface_v1 *layer_surface = |
473 | int ox = popup_sx, oy = popup_sy; | 249 | surface->scene->layer_surface; |
474 | struct sway_layer_surface *layer; | 250 | |
475 | while (true) { | 251 | // focus on new surface |
476 | if (layer_popup->parent_type == LAYER_PARENT_POPUP) { | 252 | if (layer_surface->current.keyboard_interactive && |
477 | layer_popup = layer_popup->parent_popup; | 253 | (layer_surface->current.layer == ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY || |
478 | ox += layer_popup->wlr_popup->geometry.x; | 254 | layer_surface->current.layer == ZWLR_LAYER_SHELL_V1_LAYER_TOP)) { |
479 | oy += layer_popup->wlr_popup->geometry.y; | 255 | struct sway_seat *seat; |
480 | } else { | 256 | wl_list_for_each(seat, &server.input->seats, link) { |
481 | layer = layer_popup->parent_layer; | 257 | // but only if the currently focused layer has a lower precedence |
482 | ox += layer->geo.x; | 258 | if (!seat->focused_layer || |
483 | oy += layer->geo.y; | 259 | seat->focused_layer->current.layer >= layer_surface->current.layer) { |
484 | break; | 260 | seat_set_focus_layer(seat, layer_surface); |
261 | } | ||
485 | } | 262 | } |
263 | arrange_layers(surface->output); | ||
486 | } | 264 | } |
487 | struct wlr_output *wlr_output = layer->layer_surface->output; | ||
488 | struct sway_output *output = wlr_output->data; | ||
489 | output_damage_surface(output, ox, oy, surface, whole); | ||
490 | } | ||
491 | 265 | ||
492 | static void popup_handle_map(struct wl_listener *listener, void *data) { | 266 | cursor_rebase_all(); |
493 | struct sway_layer_popup *popup = wl_container_of(listener, popup, map); | ||
494 | struct sway_layer_surface *layer = popup_get_layer(popup); | ||
495 | struct wlr_output *wlr_output = layer->layer_surface->output; | ||
496 | wlr_surface_send_enter(popup->wlr_popup->base->surface, wlr_output); | ||
497 | popup_damage(popup, true); | ||
498 | } | 267 | } |
499 | 268 | ||
500 | static void popup_handle_unmap(struct wl_listener *listener, void *data) { | 269 | static void handle_unmap(struct wl_listener *listener, void *data) { |
501 | struct sway_layer_popup *popup = wl_container_of(listener, popup, unmap); | 270 | struct sway_layer_surface *surface = wl_container_of( |
502 | popup_damage(popup, true); | 271 | listener, surface, unmap); |
503 | } | 272 | struct sway_seat *seat; |
273 | wl_list_for_each(seat, &server.input->seats, link) { | ||
274 | if (seat->focused_layer == surface->layer_surface) { | ||
275 | seat_set_focus_layer(seat, NULL); | ||
276 | } | ||
277 | } | ||
504 | 278 | ||
505 | static void popup_handle_commit(struct wl_listener *listener, void *data) { | 279 | cursor_rebase_all(); |
506 | struct sway_layer_popup *popup = wl_container_of(listener, popup, commit); | ||
507 | popup_damage(popup, false); | ||
508 | } | 280 | } |
509 | 281 | ||
510 | static void popup_handle_destroy(struct wl_listener *listener, void *data) { | 282 | static void popup_handle_destroy(struct wl_listener *listener, void *data) { |
511 | struct sway_layer_popup *popup = | 283 | struct sway_layer_popup *popup = |
512 | wl_container_of(listener, popup, destroy); | 284 | wl_container_of(listener, popup, destroy); |
513 | 285 | ||
514 | wl_list_remove(&popup->map.link); | ||
515 | wl_list_remove(&popup->unmap.link); | ||
516 | wl_list_remove(&popup->destroy.link); | 286 | wl_list_remove(&popup->destroy.link); |
287 | wl_list_remove(&popup->new_popup.link); | ||
517 | wl_list_remove(&popup->commit.link); | 288 | wl_list_remove(&popup->commit.link); |
518 | free(popup); | 289 | free(popup); |
519 | } | 290 | } |
520 | 291 | ||
521 | static void popup_unconstrain(struct sway_layer_popup *popup) { | 292 | static void popup_unconstrain(struct sway_layer_popup *popup) { |
522 | struct sway_layer_surface *layer = popup_get_layer(popup); | ||
523 | struct wlr_xdg_popup *wlr_popup = popup->wlr_popup; | 293 | struct wlr_xdg_popup *wlr_popup = popup->wlr_popup; |
294 | struct sway_output *output = popup->toplevel->output; | ||
524 | 295 | ||
525 | struct sway_output *output = layer->layer_surface->output->data; | 296 | // if a client tries to create a popup while we are in the process of destroying |
297 | // its output, don't crash. | ||
298 | if (!output) { | ||
299 | return; | ||
300 | } | ||
301 | |||
302 | int lx, ly; | ||
303 | wlr_scene_node_coords(&popup->toplevel->scene->tree->node, &lx, &ly); | ||
526 | 304 | ||
527 | // the output box expressed in the coordinate system of the toplevel parent | 305 | // the output box expressed in the coordinate system of the toplevel parent |
528 | // of the popup | 306 | // of the popup |
529 | struct wlr_box output_toplevel_sx_box = { | 307 | struct wlr_box output_toplevel_sx_box = { |
530 | .x = -layer->geo.x, | 308 | .x = output->lx - lx, |
531 | .y = -layer->geo.y, | 309 | .y = output->ly - ly, |
532 | .width = output->width, | 310 | .width = output->width, |
533 | .height = output->height, | 311 | .height = output->height, |
534 | }; | 312 | }; |
@@ -536,32 +314,38 @@ static void popup_unconstrain(struct sway_layer_popup *popup) { | |||
536 | wlr_xdg_popup_unconstrain_from_box(wlr_popup, &output_toplevel_sx_box); | 314 | wlr_xdg_popup_unconstrain_from_box(wlr_popup, &output_toplevel_sx_box); |
537 | } | 315 | } |
538 | 316 | ||
317 | static void popup_handle_commit(struct wl_listener *listener, void *data) { | ||
318 | struct sway_layer_popup *popup = wl_container_of(listener, popup, commit); | ||
319 | if (popup->wlr_popup->base->initial_commit) { | ||
320 | popup_unconstrain(popup); | ||
321 | } | ||
322 | } | ||
323 | |||
539 | static void popup_handle_new_popup(struct wl_listener *listener, void *data); | 324 | static void popup_handle_new_popup(struct wl_listener *listener, void *data); |
540 | 325 | ||
541 | static struct sway_layer_popup *create_popup(struct wlr_xdg_popup *wlr_popup, | 326 | static struct sway_layer_popup *create_popup(struct wlr_xdg_popup *wlr_popup, |
542 | enum layer_parent parent_type, void *parent) { | 327 | struct sway_layer_surface *toplevel, struct wlr_scene_tree *parent) { |
543 | struct sway_layer_popup *popup = | 328 | struct sway_layer_popup *popup = calloc(1, sizeof(*popup)); |
544 | calloc(1, sizeof(struct sway_layer_popup)); | ||
545 | if (popup == NULL) { | 329 | if (popup == NULL) { |
546 | return NULL; | 330 | return NULL; |
547 | } | 331 | } |
548 | 332 | ||
333 | popup->toplevel = toplevel; | ||
549 | popup->wlr_popup = wlr_popup; | 334 | popup->wlr_popup = wlr_popup; |
550 | popup->parent_type = parent_type; | 335 | popup->scene = wlr_scene_xdg_surface_create(parent, |
551 | popup->parent_layer = parent; | 336 | wlr_popup->base); |
337 | |||
338 | if (!popup->scene) { | ||
339 | free(popup); | ||
340 | return NULL; | ||
341 | } | ||
552 | 342 | ||
553 | popup->map.notify = popup_handle_map; | ||
554 | wl_signal_add(&wlr_popup->base->events.map, &popup->map); | ||
555 | popup->unmap.notify = popup_handle_unmap; | ||
556 | wl_signal_add(&wlr_popup->base->events.unmap, &popup->unmap); | ||
557 | popup->destroy.notify = popup_handle_destroy; | 343 | popup->destroy.notify = popup_handle_destroy; |
558 | wl_signal_add(&wlr_popup->base->events.destroy, &popup->destroy); | 344 | wl_signal_add(&wlr_popup->base->events.destroy, &popup->destroy); |
559 | popup->commit.notify = popup_handle_commit; | ||
560 | wl_signal_add(&wlr_popup->base->surface->events.commit, &popup->commit); | ||
561 | popup->new_popup.notify = popup_handle_new_popup; | 345 | popup->new_popup.notify = popup_handle_new_popup; |
562 | wl_signal_add(&wlr_popup->base->events.new_popup, &popup->new_popup); | 346 | wl_signal_add(&wlr_popup->base->events.new_popup, &popup->new_popup); |
563 | 347 | popup->commit.notify = popup_handle_commit; | |
564 | popup_unconstrain(popup); | 348 | wl_signal_add(&wlr_popup->base->surface->events.commit, &popup->commit); |
565 | 349 | ||
566 | return popup; | 350 | return popup; |
567 | } | 351 | } |
@@ -570,19 +354,14 @@ static void popup_handle_new_popup(struct wl_listener *listener, void *data) { | |||
570 | struct sway_layer_popup *sway_layer_popup = | 354 | struct sway_layer_popup *sway_layer_popup = |
571 | wl_container_of(listener, sway_layer_popup, new_popup); | 355 | wl_container_of(listener, sway_layer_popup, new_popup); |
572 | struct wlr_xdg_popup *wlr_popup = data; | 356 | struct wlr_xdg_popup *wlr_popup = data; |
573 | create_popup(wlr_popup, LAYER_PARENT_POPUP, sway_layer_popup); | 357 | create_popup(wlr_popup, sway_layer_popup->toplevel, sway_layer_popup->scene); |
574 | } | 358 | } |
575 | 359 | ||
576 | static void handle_new_popup(struct wl_listener *listener, void *data) { | 360 | static void handle_new_popup(struct wl_listener *listener, void *data) { |
577 | struct sway_layer_surface *sway_layer_surface = | 361 | struct sway_layer_surface *sway_layer_surface = |
578 | wl_container_of(listener, sway_layer_surface, new_popup); | 362 | wl_container_of(listener, sway_layer_surface, new_popup); |
579 | struct wlr_xdg_popup *wlr_popup = data; | 363 | struct wlr_xdg_popup *wlr_popup = data; |
580 | create_popup(wlr_popup, LAYER_PARENT_LAYER, sway_layer_surface); | 364 | create_popup(wlr_popup, sway_layer_surface, sway_layer_surface->popups); |
581 | } | ||
582 | |||
583 | struct sway_layer_surface *layer_from_wlr_layer_surface_v1( | ||
584 | struct wlr_layer_surface_v1 *layer_surface) { | ||
585 | return layer_surface->data; | ||
586 | } | 365 | } |
587 | 366 | ||
588 | void handle_layer_shell_surface(struct wl_listener *listener, void *data) { | 367 | void handle_layer_shell_surface(struct wl_listener *listener, void *data) { |
@@ -590,14 +369,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 | 369 | sway_log(SWAY_DEBUG, "new layer surface: namespace %s layer %d anchor %" PRIu32 |
591 | " size %" PRIu32 "x%" PRIu32 " margin %" PRIu32 ",%" PRIu32 ",%" PRIu32 ",%" PRIu32 ",", | 370 | " size %" PRIu32 "x%" PRIu32 " margin %" PRIu32 ",%" PRIu32 ",%" PRIu32 ",%" PRIu32 ",", |
592 | layer_surface->namespace, | 371 | layer_surface->namespace, |
593 | layer_surface->client_pending.layer, | 372 | layer_surface->pending.layer, |
594 | layer_surface->client_pending.anchor, | 373 | layer_surface->pending.anchor, |
595 | layer_surface->client_pending.desired_width, | 374 | layer_surface->pending.desired_width, |
596 | layer_surface->client_pending.desired_height, | 375 | layer_surface->pending.desired_height, |
597 | layer_surface->client_pending.margin.top, | 376 | layer_surface->pending.margin.top, |
598 | layer_surface->client_pending.margin.right, | 377 | layer_surface->pending.margin.right, |
599 | layer_surface->client_pending.margin.bottom, | 378 | layer_surface->pending.margin.bottom, |
600 | layer_surface->client_pending.margin.left); | 379 | layer_surface->pending.margin.left); |
601 | 380 | ||
602 | if (!layer_surface->output) { | 381 | if (!layer_surface->output) { |
603 | // Assign last active output | 382 | // Assign last active output |
@@ -609,12 +388,12 @@ void handle_layer_shell_surface(struct wl_listener *listener, void *data) { | |||
609 | output = ws->output; | 388 | output = ws->output; |
610 | } | 389 | } |
611 | } | 390 | } |
612 | if (!output || output == root->noop_output) { | 391 | if (!output || output == root->fallback_output) { |
613 | if (!root->outputs->length) { | 392 | if (!root->outputs->length) { |
614 | sway_log(SWAY_ERROR, | 393 | sway_log(SWAY_ERROR, |
615 | "no output to auto-assign layer surface '%s' to", | 394 | "no output to auto-assign layer surface '%s' to", |
616 | layer_surface->namespace); | 395 | layer_surface->namespace); |
617 | wlr_layer_surface_v1_close(layer_surface); | 396 | wlr_layer_surface_v1_destroy(layer_surface); |
618 | return; | 397 | return; |
619 | } | 398 | } |
620 | output = root->outputs->items[0]; | 399 | output = root->outputs->items[0]; |
@@ -622,42 +401,57 @@ void handle_layer_shell_surface(struct wl_listener *listener, void *data) { | |||
622 | layer_surface->output = output->wlr_output; | 401 | layer_surface->output = output->wlr_output; |
623 | } | 402 | } |
624 | 403 | ||
625 | struct sway_layer_surface *sway_layer = | 404 | struct sway_output *output = layer_surface->output->data; |
626 | calloc(1, sizeof(struct sway_layer_surface)); | 405 | |
627 | if (!sway_layer) { | 406 | enum zwlr_layer_shell_v1_layer layer_type = layer_surface->pending.layer; |
407 | struct wlr_scene_tree *output_layer = sway_layer_get_scene( | ||
408 | output, layer_type); | ||
409 | struct wlr_scene_layer_surface_v1 *scene_surface = | ||
410 | wlr_scene_layer_surface_v1_create(output_layer, layer_surface); | ||
411 | if (!scene_surface) { | ||
412 | sway_log(SWAY_ERROR, "Could not allocate a layer_surface_v1"); | ||
628 | return; | 413 | return; |
629 | } | 414 | } |
630 | 415 | ||
631 | sway_layer->surface_commit.notify = handle_surface_commit; | 416 | struct sway_layer_surface *surface = |
632 | wl_signal_add(&layer_surface->surface->events.commit, | 417 | sway_layer_surface_create(scene_surface); |
633 | &sway_layer->surface_commit); | 418 | if (!surface) { |
634 | 419 | wlr_layer_surface_v1_destroy(layer_surface); | |
635 | sway_layer->destroy.notify = handle_destroy; | ||
636 | wl_signal_add(&layer_surface->events.destroy, &sway_layer->destroy); | ||
637 | sway_layer->map.notify = handle_map; | ||
638 | wl_signal_add(&layer_surface->events.map, &sway_layer->map); | ||
639 | sway_layer->unmap.notify = handle_unmap; | ||
640 | wl_signal_add(&layer_surface->events.unmap, &sway_layer->unmap); | ||
641 | sway_layer->new_popup.notify = handle_new_popup; | ||
642 | wl_signal_add(&layer_surface->events.new_popup, &sway_layer->new_popup); | ||
643 | sway_layer->new_subsurface.notify = handle_new_subsurface; | ||
644 | wl_signal_add(&layer_surface->surface->events.new_subsurface, | ||
645 | &sway_layer->new_subsurface); | ||
646 | |||
647 | sway_layer->layer_surface = layer_surface; | ||
648 | layer_surface->data = sway_layer; | ||
649 | 420 | ||
650 | struct sway_output *output = layer_surface->output->data; | 421 | sway_log(SWAY_ERROR, "Could not allocate a sway_layer_surface"); |
651 | sway_layer->output_destroy.notify = handle_output_destroy; | 422 | return; |
652 | wl_signal_add(&output->events.destroy, &sway_layer->output_destroy); | 423 | } |
653 | 424 | ||
654 | wl_list_insert(&output->layers[layer_surface->client_pending.layer], | 425 | if (!scene_descriptor_assign(&scene_surface->tree->node, |
655 | &sway_layer->link); | 426 | SWAY_SCENE_DESC_LAYER_SHELL, surface)) { |
656 | 427 | sway_log(SWAY_ERROR, "Failed to allocate a layer surface descriptor"); | |
657 | // Temporarily set the layer's current state to client_pending | 428 | // destroying the layer_surface will also destroy its corresponding |
658 | // So that we can easily arrange it | 429 | // scene node |
659 | struct wlr_layer_surface_v1_state old_state = layer_surface->current; | 430 | wlr_layer_surface_v1_destroy(layer_surface); |
660 | layer_surface->current = layer_surface->client_pending; | 431 | return; |
661 | arrange_layers(output); | 432 | } |
662 | layer_surface->current = old_state; | 433 | |
434 | surface->output = output; | ||
435 | |||
436 | // now that the surface's output is known, we can advertise its scale | ||
437 | wlr_fractional_scale_v1_notify_scale(surface->layer_surface->surface, | ||
438 | layer_surface->output->scale); | ||
439 | wlr_surface_set_preferred_buffer_scale(surface->layer_surface->surface, | ||
440 | ceil(layer_surface->output->scale)); | ||
441 | |||
442 | surface->surface_commit.notify = handle_surface_commit; | ||
443 | wl_signal_add(&layer_surface->surface->events.commit, | ||
444 | &surface->surface_commit); | ||
445 | surface->map.notify = handle_map; | ||
446 | wl_signal_add(&layer_surface->surface->events.map, &surface->map); | ||
447 | surface->unmap.notify = handle_unmap; | ||
448 | wl_signal_add(&layer_surface->surface->events.unmap, &surface->unmap); | ||
449 | surface->new_popup.notify = handle_new_popup; | ||
450 | wl_signal_add(&layer_surface->events.new_popup, &surface->new_popup); | ||
451 | |||
452 | surface->output_destroy.notify = handle_output_destroy; | ||
453 | wl_signal_add(&output->events.disable, &surface->output_destroy); | ||
454 | |||
455 | surface->node_destroy.notify = handle_node_destroy; | ||
456 | wl_signal_add(&scene_surface->tree->node.events.destroy, &surface->node_destroy); | ||
663 | } | 457 | } |
diff --git a/sway/desktop/output.c b/sway/desktop/output.c index 5edc8f96..2722e556 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c | |||
@@ -1,42 +1,61 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <assert.h> | 1 | #include <assert.h> |
3 | #include <stdlib.h> | 2 | #include <stdlib.h> |
4 | #include <strings.h> | 3 | #include <strings.h> |
5 | #include <time.h> | 4 | #include <time.h> |
6 | #include <wayland-server-core.h> | 5 | #include <wayland-server-core.h> |
6 | #include <wlr/config.h> | ||
7 | #include <wlr/backend/headless.h> | ||
8 | #include <wlr/render/swapchain.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_gamma_control_v1.h> | ||
10 | #include <wlr/types/wlr_matrix.h> | 12 | #include <wlr/types/wlr_matrix.h> |
11 | #include <wlr/types/wlr_output_damage.h> | ||
12 | #include <wlr/types/wlr_output_layout.h> | 13 | #include <wlr/types/wlr_output_layout.h> |
14 | #include <wlr/types/wlr_output_management_v1.h> | ||
15 | #include <wlr/types/wlr_output_power_management_v1.h> | ||
13 | #include <wlr/types/wlr_output.h> | 16 | #include <wlr/types/wlr_output.h> |
14 | #include <wlr/types/wlr_presentation_time.h> | 17 | #include <wlr/types/wlr_presentation_time.h> |
15 | #include <wlr/types/wlr_surface.h> | 18 | #include <wlr/types/wlr_compositor.h> |
16 | #include <wlr/util/region.h> | 19 | #include <wlr/util/region.h> |
20 | #include <wlr/util/transform.h> | ||
17 | #include "config.h" | 21 | #include "config.h" |
18 | #include "log.h" | 22 | #include "log.h" |
19 | #include "sway/config.h" | 23 | #include "sway/config.h" |
20 | #include "sway/desktop/transaction.h" | 24 | #include "sway/desktop/transaction.h" |
21 | #include "sway/input/input-manager.h" | 25 | #include "sway/input/input-manager.h" |
22 | #include "sway/input/seat.h" | 26 | #include "sway/input/seat.h" |
27 | #include "sway/ipc-server.h" | ||
23 | #include "sway/layers.h" | 28 | #include "sway/layers.h" |
24 | #include "sway/output.h" | 29 | #include "sway/output.h" |
30 | #include "sway/scene_descriptor.h" | ||
25 | #include "sway/server.h" | 31 | #include "sway/server.h" |
26 | #include "sway/surface.h" | ||
27 | #include "sway/tree/arrange.h" | 32 | #include "sway/tree/arrange.h" |
28 | #include "sway/tree/container.h" | 33 | #include "sway/tree/container.h" |
29 | #include "sway/tree/root.h" | 34 | #include "sway/tree/root.h" |
30 | #include "sway/tree/view.h" | 35 | #include "sway/tree/view.h" |
31 | #include "sway/tree/workspace.h" | 36 | #include "sway/tree/workspace.h" |
32 | 37 | ||
38 | #if WLR_HAS_DRM_BACKEND | ||
39 | #include <wlr/backend/drm.h> | ||
40 | #include <wlr/types/wlr_drm_lease_v1.h> | ||
41 | #endif | ||
42 | |||
43 | bool output_match_name_or_id(struct sway_output *output, | ||
44 | const char *name_or_id) { | ||
45 | if (strcmp(name_or_id, "*") == 0) { | ||
46 | return true; | ||
47 | } | ||
48 | |||
49 | char identifier[128]; | ||
50 | output_get_identifier(identifier, sizeof(identifier), output); | ||
51 | return strcasecmp(identifier, name_or_id) == 0 | ||
52 | || strcasecmp(output->wlr_output->name, name_or_id) == 0; | ||
53 | } | ||
54 | |||
33 | struct sway_output *output_by_name_or_id(const char *name_or_id) { | 55 | struct sway_output *output_by_name_or_id(const char *name_or_id) { |
34 | for (int i = 0; i < root->outputs->length; ++i) { | 56 | for (int i = 0; i < root->outputs->length; ++i) { |
35 | struct sway_output *output = root->outputs->items[i]; | 57 | struct sway_output *output = root->outputs->items[i]; |
36 | char identifier[128]; | 58 | if (output_match_name_or_id(output, name_or_id)) { |
37 | output_get_identifier(identifier, sizeof(identifier), output); | ||
38 | if (strcasecmp(identifier, name_or_id) == 0 | ||
39 | || strcasecmp(output->wlr_output->name, name_or_id) == 0) { | ||
40 | return output; | 59 | return output; |
41 | } | 60 | } |
42 | } | 61 | } |
@@ -46,583 +65,217 @@ struct sway_output *output_by_name_or_id(const char *name_or_id) { | |||
46 | struct sway_output *all_output_by_name_or_id(const char *name_or_id) { | 65 | struct sway_output *all_output_by_name_or_id(const char *name_or_id) { |
47 | struct sway_output *output; | 66 | struct sway_output *output; |
48 | wl_list_for_each(output, &root->all_outputs, link) { | 67 | wl_list_for_each(output, &root->all_outputs, link) { |
49 | char identifier[128]; | 68 | if (output_match_name_or_id(output, name_or_id)) { |
50 | output_get_identifier(identifier, sizeof(identifier), output); | ||
51 | if (strcasecmp(identifier, name_or_id) == 0 | ||
52 | || strcasecmp(output->wlr_output->name, name_or_id) == 0) { | ||
53 | return output; | 69 | return output; |
54 | } | 70 | } |
55 | } | 71 | } |
56 | return NULL; | 72 | return NULL; |
57 | } | 73 | } |
58 | 74 | ||
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 | 75 | ||
69 | // Coordinates relative to the center of the subsurface | 76 | struct sway_workspace *output_get_active_workspace(struct sway_output *output) { |
70 | double ox = *sx - pw/2 + sw/2, | 77 | struct sway_seat *seat = input_manager_current_seat(); |
71 | oy = *sy - ph/2 + sh/2; | 78 | struct sway_node *focus = seat_get_active_tiling_child(seat, &output->node); |
72 | // Rotated coordinates | 79 | if (!focus) { |
73 | double rx = cos(-rotation)*ox - sin(-rotation)*oy, | 80 | if (!output->workspaces->length) { |
74 | ry = cos(-rotation)*oy + sin(-rotation)*ox; | 81 | return NULL; |
75 | *sx = rx + pw/2 - sw/2; | 82 | } |
76 | *sy = ry + ph/2 - sh/2; | 83 | return output->workspaces->items[0]; |
84 | } | ||
85 | return focus->sway_workspace; | ||
77 | } | 86 | } |
78 | 87 | ||
79 | struct surface_iterator_data { | 88 | struct send_frame_done_data { |
80 | sway_surface_iterator_func_t user_iterator; | 89 | struct timespec when; |
81 | void *user_data; | 90 | int msec_until_refresh; |
82 | |||
83 | struct sway_output *output; | 91 | struct sway_output *output; |
84 | struct sway_view *view; | ||
85 | double ox, oy; | ||
86 | int width, height; | ||
87 | float rotation; | ||
88 | }; | 92 | }; |
89 | 93 | ||
90 | static bool get_surface_box(struct surface_iterator_data *data, | 94 | struct buffer_timer { |
91 | struct wlr_surface *surface, int sx, int sy, | 95 | struct wl_listener destroy; |
92 | struct wlr_box *surface_box) { | 96 | struct wl_event_source *frame_done_timer; |
93 | struct sway_output *output = data->output; | 97 | }; |
94 | |||
95 | if (!wlr_surface_has_buffer(surface)) { | ||
96 | return false; | ||
97 | } | ||
98 | |||
99 | int sw = surface->current.width; | ||
100 | int sh = surface->current.height; | ||
101 | |||
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 = { | ||
108 | .x = data->ox + _sx, | ||
109 | .y = data->oy + _sy, | ||
110 | .width = sw, | ||
111 | .height = sh, | ||
112 | }; | ||
113 | if (surface_box != NULL) { | ||
114 | memcpy(surface_box, &box, sizeof(struct wlr_box)); | ||
115 | } | ||
116 | |||
117 | struct wlr_box rotated_box; | ||
118 | wlr_box_rotated_bounds(&rotated_box, &box, data->rotation); | ||
119 | |||
120 | struct wlr_box output_box = { | ||
121 | .width = output->width, | ||
122 | .height = output->height, | ||
123 | }; | ||
124 | |||
125 | struct wlr_box intersection; | ||
126 | return wlr_box_intersection(&intersection, &output_box, &rotated_box); | ||
127 | } | ||
128 | |||
129 | static void output_for_each_surface_iterator(struct wlr_surface *surface, | ||
130 | int sx, int sy, void *_data) { | ||
131 | struct surface_iterator_data *data = _data; | ||
132 | |||
133 | struct wlr_box box; | ||
134 | bool intersects = get_surface_box(data, surface, sx, sy, &box); | ||
135 | if (!intersects) { | ||
136 | return; | ||
137 | } | ||
138 | 98 | ||
139 | data->user_iterator(data->output, data->view, surface, &box, data->rotation, | 99 | static int handle_buffer_timer(void *data) { |
140 | data->user_data); | 100 | struct wlr_scene_buffer *buffer = data; |
141 | } | ||
142 | 101 | ||
143 | void output_surface_for_each_surface(struct sway_output *output, | 102 | struct timespec now; |
144 | struct wlr_surface *surface, double ox, double oy, | 103 | clock_gettime(CLOCK_MONOTONIC, &now); |
145 | sway_surface_iterator_func_t iterator, void *user_data) { | 104 | wlr_scene_buffer_send_frame_done(buffer, &now); |
146 | struct surface_iterator_data data = { | 105 | return 0; |
147 | .user_iterator = iterator, | ||
148 | .user_data = user_data, | ||
149 | .output = output, | ||
150 | .view = NULL, | ||
151 | .ox = ox, | ||
152 | .oy = oy, | ||
153 | .width = surface->current.width, | ||
154 | .height = surface->current.height, | ||
155 | .rotation = 0, | ||
156 | }; | ||
157 | |||
158 | wlr_surface_for_each_surface(surface, | ||
159 | output_for_each_surface_iterator, &data); | ||
160 | } | 106 | } |
161 | 107 | ||
162 | void output_view_for_each_surface(struct sway_output *output, | 108 | static void handle_buffer_timer_destroy(struct wl_listener *listener, |
163 | struct sway_view *view, sway_surface_iterator_func_t iterator, | 109 | void *data) { |
164 | void *user_data) { | 110 | struct buffer_timer *timer = wl_container_of(listener, timer, destroy); |
165 | struct surface_iterator_data data = { | ||
166 | .user_iterator = iterator, | ||
167 | .user_data = user_data, | ||
168 | .output = output, | ||
169 | .view = view, | ||
170 | .ox = view->container->surface_x - output->lx | ||
171 | - view->geometry.x, | ||
172 | .oy = view->container->surface_y - output->ly | ||
173 | - view->geometry.y, | ||
174 | .width = view->container->current.content_width, | ||
175 | .height = view->container->current.content_height, | ||
176 | .rotation = 0, // TODO | ||
177 | }; | ||
178 | |||
179 | view_for_each_surface(view, output_for_each_surface_iterator, &data); | ||
180 | } | ||
181 | 111 | ||
182 | void output_view_for_each_popup_surface(struct sway_output *output, | 112 | wl_list_remove(&timer->destroy.link); |
183 | struct sway_view *view, sway_surface_iterator_func_t iterator, | 113 | wl_event_source_remove(timer->frame_done_timer); |
184 | void *user_data) { | 114 | free(timer); |
185 | struct surface_iterator_data data = { | ||
186 | .user_iterator = iterator, | ||
187 | .user_data = user_data, | ||
188 | .output = output, | ||
189 | .view = view, | ||
190 | .ox = view->container->surface_x - output->lx | ||
191 | - view->geometry.x, | ||
192 | .oy = view->container->surface_y - output->ly | ||
193 | - view->geometry.y, | ||
194 | .width = view->container->current.content_width, | ||
195 | .height = view->container->current.content_height, | ||
196 | .rotation = 0, // TODO | ||
197 | }; | ||
198 | |||
199 | view_for_each_popup_surface(view, output_for_each_surface_iterator, &data); | ||
200 | } | 115 | } |
201 | 116 | ||
202 | void output_layer_for_each_surface(struct sway_output *output, | 117 | static struct buffer_timer *buffer_timer_get_or_create(struct wlr_scene_buffer *buffer) { |
203 | struct wl_list *layer_surfaces, sway_surface_iterator_func_t iterator, | 118 | struct buffer_timer *timer = |
204 | void *user_data) { | 119 | scene_descriptor_try_get(&buffer->node, SWAY_SCENE_DESC_BUFFER_TIMER); |
205 | struct sway_layer_surface *layer_surface; | 120 | if (timer) { |
206 | wl_list_for_each(layer_surface, layer_surfaces, link) { | 121 | return timer; |
207 | struct wlr_layer_surface_v1 *wlr_layer_surface_v1 = | ||
208 | layer_surface->layer_surface; | ||
209 | output_surface_for_each_surface(output, wlr_layer_surface_v1->surface, | ||
210 | layer_surface->geo.x, layer_surface->geo.y, iterator, | ||
211 | user_data); | ||
212 | |||
213 | struct wlr_xdg_popup *state; | ||
214 | wl_list_for_each(state, &wlr_layer_surface_v1->popups, link) { | ||
215 | struct wlr_xdg_surface *popup = state->base; | ||
216 | if (!popup->configured) { | ||
217 | continue; | ||
218 | } | ||
219 | |||
220 | double popup_sx, popup_sy; | ||
221 | popup_sx = layer_surface->geo.x + | ||
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 | } | 122 | } |
244 | } | ||
245 | 123 | ||
246 | void output_layer_for_each_toplevel_surface(struct sway_output *output, | 124 | timer = calloc(1, sizeof(struct buffer_timer)); |
247 | struct wl_list *layer_surfaces, sway_surface_iterator_func_t iterator, | 125 | if (!timer) { |
248 | void *user_data) { | 126 | return NULL; |
249 | struct sway_layer_surface *layer_surface; | ||
250 | wl_list_for_each(layer_surface, layer_surfaces, link) { | ||
251 | struct wlr_layer_surface_v1 *wlr_layer_surface_v1 = | ||
252 | layer_surface->layer_surface; | ||
253 | output_surface_for_each_surface(output, wlr_layer_surface_v1->surface, | ||
254 | layer_surface->geo.x, layer_surface->geo.y, iterator, | ||
255 | user_data); | ||
256 | } | 127 | } |
257 | } | ||
258 | |||
259 | 128 | ||
260 | void output_layer_for_each_popup_surface(struct sway_output *output, | 129 | timer->frame_done_timer = wl_event_loop_add_timer(server.wl_event_loop, |
261 | struct wl_list *layer_surfaces, sway_surface_iterator_func_t iterator, | 130 | handle_buffer_timer, buffer); |
262 | void *user_data) { | 131 | if (!timer->frame_done_timer) { |
263 | struct sway_layer_surface *layer_surface; | 132 | free(timer); |
264 | wl_list_for_each(layer_surface, layer_surfaces, link) { | 133 | return NULL; |
265 | struct wlr_layer_surface_v1 *wlr_layer_surface_v1 = | ||
266 | layer_surface->layer_surface; | ||
267 | |||
268 | struct wlr_xdg_popup *state; | ||
269 | wl_list_for_each(state, &wlr_layer_surface_v1->popups, link) { | ||
270 | struct wlr_xdg_surface *popup = state->base; | ||
271 | if (!popup->configured) { | ||
272 | continue; | ||
273 | } | ||
274 | |||
275 | double popup_sx, popup_sy; | ||
276 | popup_sx = layer_surface->geo.x + | ||
277 | popup->popup->geometry.x - popup->geometry.x; | ||
278 | popup_sy = layer_surface->geo.y + | ||
279 | popup->popup->geometry.y - popup->geometry.y; | ||
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 | } | 134 | } |
299 | } | ||
300 | 135 | ||
301 | #if HAVE_XWAYLAND | 136 | scene_descriptor_assign(&buffer->node, SWAY_SCENE_DESC_BUFFER_TIMER, timer); |
302 | void output_unmanaged_for_each_surface(struct sway_output *output, | ||
303 | struct wl_list *unmanaged, sway_surface_iterator_func_t iterator, | ||
304 | void *user_data) { | ||
305 | struct sway_xwayland_unmanaged *unmanaged_surface; | ||
306 | wl_list_for_each(unmanaged_surface, unmanaged, link) { | ||
307 | struct wlr_xwayland_surface *xsurface = | ||
308 | unmanaged_surface->wlr_xwayland_surface; | ||
309 | double ox = unmanaged_surface->lx - output->lx; | ||
310 | double oy = unmanaged_surface->ly - output->ly; | ||
311 | |||
312 | output_surface_for_each_surface(output, xsurface->surface, ox, oy, | ||
313 | iterator, user_data); | ||
314 | } | ||
315 | } | ||
316 | #endif | ||
317 | 137 | ||
318 | void output_drag_icons_for_each_surface(struct sway_output *output, | 138 | timer->destroy.notify = handle_buffer_timer_destroy; |
319 | struct wl_list *drag_icons, sway_surface_iterator_func_t iterator, | 139 | wl_signal_add(&buffer->node.events.destroy, &timer->destroy); |
320 | void *user_data) { | ||
321 | struct sway_drag_icon *drag_icon; | ||
322 | wl_list_for_each(drag_icon, drag_icons, link) { | ||
323 | double ox = drag_icon->x - output->lx; | ||
324 | double oy = drag_icon->y - output->ly; | ||
325 | |||
326 | if (drag_icon->wlr_drag_icon->mapped) { | ||
327 | output_surface_for_each_surface(output, | ||
328 | drag_icon->wlr_drag_icon->surface, ox, oy, | ||
329 | iterator, user_data); | ||
330 | } | ||
331 | } | ||
332 | } | ||
333 | |||
334 | static void for_each_surface_container_iterator(struct sway_container *con, | ||
335 | void *_data) { | ||
336 | if (!con->view || !view_is_visible(con->view)) { | ||
337 | return; | ||
338 | } | ||
339 | 140 | ||
340 | struct surface_iterator_data *data = _data; | 141 | return timer; |
341 | output_view_for_each_surface(data->output, con->view, | ||
342 | data->user_iterator, data->user_data); | ||
343 | } | 142 | } |
344 | 143 | ||
345 | static void output_for_each_surface(struct sway_output *output, | 144 | static void send_frame_done_iterator(struct wlr_scene_buffer *buffer, |
346 | sway_surface_iterator_func_t iterator, void *user_data) { | 145 | int x, int y, void *user_data) { |
347 | if (output_has_opaque_overlay_layer_surface(output)) { | 146 | struct send_frame_done_data *data = user_data; |
348 | goto overlay; | 147 | struct sway_output *output = data->output; |
349 | } | 148 | int view_max_render_time = 0; |
350 | 149 | ||
351 | struct surface_iterator_data data = { | 150 | if (buffer->primary_output != data->output->scene_output) { |
352 | .user_iterator = iterator, | 151 | return; |
353 | .user_data = user_data, | ||
354 | .output = output, | ||
355 | .view = NULL, | ||
356 | }; | ||
357 | |||
358 | struct sway_workspace *workspace = output_get_active_workspace(output); | ||
359 | struct sway_container *fullscreen_con = root->fullscreen_global; | ||
360 | if (!fullscreen_con) { | ||
361 | if (!workspace) { | ||
362 | return; | ||
363 | } | ||
364 | fullscreen_con = workspace->current.fullscreen; | ||
365 | } | ||
366 | if (fullscreen_con) { | ||
367 | for_each_surface_container_iterator(fullscreen_con, &data); | ||
368 | container_for_each_child(fullscreen_con, | ||
369 | for_each_surface_container_iterator, &data); | ||
370 | |||
371 | // TODO: Show transient containers for fullscreen global | ||
372 | if (fullscreen_con == workspace->current.fullscreen) { | ||
373 | for (int i = 0; i < workspace->current.floating->length; ++i) { | ||
374 | struct sway_container *floater = | ||
375 | workspace->current.floating->items[i]; | ||
376 | if (container_is_transient_for(floater, fullscreen_con)) { | ||
377 | for_each_surface_container_iterator(floater, &data); | ||
378 | } | ||
379 | } | ||
380 | } | ||
381 | #if HAVE_XWAYLAND | ||
382 | output_unmanaged_for_each_surface(output, &root->xwayland_unmanaged, | ||
383 | iterator, user_data); | ||
384 | #endif | ||
385 | } else { | ||
386 | output_layer_for_each_surface(output, | ||
387 | &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND], | ||
388 | iterator, user_data); | ||
389 | output_layer_for_each_surface(output, | ||
390 | &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM], | ||
391 | iterator, user_data); | ||
392 | |||
393 | workspace_for_each_container(workspace, | ||
394 | for_each_surface_container_iterator, &data); | ||
395 | |||
396 | #if HAVE_XWAYLAND | ||
397 | output_unmanaged_for_each_surface(output, &root->xwayland_unmanaged, | ||
398 | iterator, user_data); | ||
399 | #endif | ||
400 | output_layer_for_each_surface(output, | ||
401 | &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP], | ||
402 | iterator, user_data); | ||
403 | } | 152 | } |
404 | 153 | ||
405 | overlay: | 154 | struct wlr_scene_node *current = &buffer->node; |
406 | output_layer_for_each_surface(output, | 155 | while (true) { |
407 | &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], | 156 | struct sway_view *view = scene_descriptor_try_get(current, |
408 | iterator, user_data); | 157 | SWAY_SCENE_DESC_VIEW); |
409 | output_drag_icons_for_each_surface(output, &root->drag_icons, | 158 | if (view) { |
410 | iterator, user_data); | 159 | view_max_render_time = view->max_render_time; |
411 | } | 160 | break; |
412 | |||
413 | static int scale_length(int length, int offset, float scale) { | ||
414 | return round((offset + length) * scale) - round(offset * scale); | ||
415 | } | ||
416 | |||
417 | void scale_box(struct wlr_box *box, float scale) { | ||
418 | box->width = scale_length(box->width, box->x, scale); | ||
419 | box->height = scale_length(box->height, box->y, scale); | ||
420 | box->x = round(box->x * scale); | ||
421 | box->y = round(box->y * scale); | ||
422 | } | ||
423 | |||
424 | struct sway_workspace *output_get_active_workspace(struct sway_output *output) { | ||
425 | struct sway_seat *seat = input_manager_current_seat(); | ||
426 | struct sway_node *focus = seat_get_active_tiling_child(seat, &output->node); | ||
427 | if (!focus) { | ||
428 | if (!output->workspaces->length) { | ||
429 | return NULL; | ||
430 | } | 161 | } |
431 | return output->workspaces->items[0]; | ||
432 | } | ||
433 | return focus->sway_workspace; | ||
434 | } | ||
435 | 162 | ||
436 | bool output_has_opaque_overlay_layer_surface(struct sway_output *output) { | 163 | if (!current->parent) { |
437 | struct sway_layer_surface *sway_layer_surface; | 164 | break; |
438 | wl_list_for_each(sway_layer_surface, | ||
439 | &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], link) { | ||
440 | struct wlr_surface *wlr_surface = sway_layer_surface->layer_surface->surface; | ||
441 | pixman_box32_t output_box = { | ||
442 | .x2 = output->width, | ||
443 | .y2 = output->height, | ||
444 | }; | ||
445 | pixman_region32_t surface_opaque_box; | ||
446 | pixman_region32_init(&surface_opaque_box); | ||
447 | pixman_region32_copy(&surface_opaque_box, &wlr_surface->opaque_region); | ||
448 | pixman_region32_translate(&surface_opaque_box, | ||
449 | sway_layer_surface->geo.x, sway_layer_surface->geo.y); | ||
450 | pixman_region_overlap_t contains = | ||
451 | pixman_region32_contains_rectangle(&surface_opaque_box, &output_box); | ||
452 | pixman_region32_fini(&surface_opaque_box); | ||
453 | |||
454 | if (contains == PIXMAN_REGION_IN) { | ||
455 | return true; | ||
456 | } | 165 | } |
457 | } | ||
458 | return false; | ||
459 | } | ||
460 | 166 | ||
461 | struct send_frame_done_data { | 167 | current = ¤t->parent->node; |
462 | struct timespec when; | ||
463 | int msec_until_refresh; | ||
464 | }; | ||
465 | |||
466 | static void send_frame_done_iterator(struct sway_output *output, struct sway_view *view, | ||
467 | struct wlr_surface *surface, struct wlr_box *box, float rotation, | ||
468 | void *user_data) { | ||
469 | int view_max_render_time = 0; | ||
470 | if (view != NULL) { | ||
471 | view_max_render_time = view->max_render_time; | ||
472 | } | 168 | } |
473 | 169 | ||
474 | struct send_frame_done_data *data = user_data; | ||
475 | |||
476 | int delay = data->msec_until_refresh - output->max_render_time | 170 | int delay = data->msec_until_refresh - output->max_render_time |
477 | - view_max_render_time; | 171 | - view_max_render_time; |
478 | 172 | ||
479 | if (output->max_render_time == 0 || view_max_render_time == 0 || delay < 1) { | 173 | struct buffer_timer *timer = NULL; |
480 | wlr_surface_send_frame_done(surface, &data->when); | ||
481 | } else { | ||
482 | struct sway_surface *sway_surface = surface->data; | ||
483 | wl_event_source_timer_update(sway_surface->frame_done_timer, delay); | ||
484 | } | ||
485 | } | ||
486 | |||
487 | static void send_frame_done(struct sway_output *output, struct send_frame_done_data *data) { | ||
488 | output_for_each_surface(output, send_frame_done_iterator, data); | ||
489 | } | ||
490 | |||
491 | static void count_surface_iterator(struct sway_output *output, struct sway_view *view, | ||
492 | struct wlr_surface *surface, struct wlr_box *_box, float rotation, | ||
493 | void *data) { | ||
494 | size_t *n = data; | ||
495 | (*n)++; | ||
496 | } | ||
497 | 174 | ||
498 | static bool scan_out_fullscreen_view(struct sway_output *output, | 175 | if (output->max_render_time != 0 && view_max_render_time != 0 && delay > 0) { |
499 | struct sway_view *view) { | 176 | timer = buffer_timer_get_or_create(buffer); |
500 | struct wlr_output *wlr_output = output->wlr_output; | ||
501 | struct sway_workspace *workspace = output->current.active_workspace; | ||
502 | if (!sway_assert(workspace, "Expected an active workspace")) { | ||
503 | return false; | ||
504 | } | 177 | } |
505 | 178 | ||
506 | if (!wl_list_empty(&view->saved_buffers)) { | 179 | if (timer) { |
507 | return false; | 180 | wl_event_source_timer_update(timer->frame_done_timer, delay); |
181 | } else { | ||
182 | wlr_scene_buffer_send_frame_done(buffer, &data->when); | ||
508 | } | 183 | } |
184 | } | ||
509 | 185 | ||
510 | for (int i = 0; i < workspace->current.floating->length; ++i) { | 186 | static enum wlr_scale_filter_mode get_scale_filter(struct sway_output *output, |
511 | struct sway_container *floater = | 187 | struct wlr_scene_buffer *buffer) { |
512 | workspace->current.floating->items[i]; | 188 | // if we are scaling down, we should always choose linear |
513 | if (container_is_transient_for(floater, view->container)) { | 189 | if (buffer->dst_width > 0 && buffer->dst_height > 0 && ( |
514 | return false; | 190 | buffer->dst_width < buffer->buffer_width || |
515 | } | 191 | buffer->dst_height < buffer->buffer_height)) { |
192 | return WLR_SCALE_FILTER_BILINEAR; | ||
516 | } | 193 | } |
517 | 194 | ||
518 | #if HAVE_XWAYLAND | 195 | switch (output->scale_filter) { |
519 | if (!wl_list_empty(&root->xwayland_unmanaged)) { | 196 | case SCALE_FILTER_LINEAR: |
520 | return false; | 197 | return WLR_SCALE_FILTER_BILINEAR; |
198 | case SCALE_FILTER_NEAREST: | ||
199 | return WLR_SCALE_FILTER_NEAREST; | ||
200 | default: | ||
201 | abort(); // unreachable | ||
521 | } | 202 | } |
522 | #endif | 203 | } |
523 | 204 | ||
524 | if (!wl_list_empty(&output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY])) { | 205 | static void output_configure_scene(struct sway_output *output, |
525 | return false; | 206 | struct wlr_scene_node *node, float opacity) { |
526 | } | 207 | if (!node->enabled) { |
527 | if (!wl_list_empty(&root->drag_icons)) { | 208 | return; |
528 | return false; | ||
529 | } | 209 | } |
530 | 210 | ||
531 | struct wlr_surface *surface = view->surface; | 211 | struct sway_container *con = |
532 | if (surface == NULL) { | 212 | scene_descriptor_try_get(node, SWAY_SCENE_DESC_CONTAINER); |
533 | return false; | 213 | if (con) { |
534 | } | 214 | opacity = con->alpha; |
535 | size_t n_surfaces = 0; | ||
536 | output_view_for_each_surface(output, view, | ||
537 | count_surface_iterator, &n_surfaces); | ||
538 | if (n_surfaces != 1) { | ||
539 | return false; | ||
540 | } | 215 | } |
541 | 216 | ||
542 | if (surface->buffer == NULL) { | 217 | if (node->type == WLR_SCENE_NODE_BUFFER) { |
543 | return false; | 218 | struct wlr_scene_buffer *buffer = wlr_scene_buffer_from_node(node); |
544 | } | ||
545 | 219 | ||
546 | if ((float)surface->current.scale != wlr_output->scale || | 220 | // hack: don't call the scene setter because that will damage all outputs |
547 | surface->current.transform != wlr_output->transform) { | 221 | // We don't want to damage outputs that aren't our current output that |
548 | return false; | 222 | // we're configuring |
549 | } | 223 | buffer->filter_mode = get_scale_filter(output, buffer); |
550 | 224 | ||
551 | wlr_output_attach_buffer(wlr_output, &surface->buffer->base); | 225 | wlr_scene_buffer_set_opacity(buffer, opacity); |
552 | if (!wlr_output_test(wlr_output)) { | 226 | } else if (node->type == WLR_SCENE_NODE_TREE) { |
553 | return false; | 227 | struct wlr_scene_tree *tree = wlr_scene_tree_from_node(node); |
228 | struct wlr_scene_node *node; | ||
229 | wl_list_for_each(node, &tree->children, link) { | ||
230 | output_configure_scene(output, node, opacity); | ||
231 | } | ||
554 | } | 232 | } |
555 | |||
556 | wlr_presentation_surface_sampled_on_output(server.presentation, surface, | ||
557 | wlr_output); | ||
558 | |||
559 | return wlr_output_commit(wlr_output); | ||
560 | } | 233 | } |
561 | 234 | ||
562 | static int output_repaint_timer_handler(void *data) { | 235 | static int output_repaint_timer_handler(void *data) { |
563 | struct sway_output *output = data; | 236 | struct sway_output *output = data; |
564 | if (output->wlr_output == NULL) { | ||
565 | return 0; | ||
566 | } | ||
567 | 237 | ||
568 | output->wlr_output->frame_pending = false; | 238 | if (!output->enabled) { |
569 | |||
570 | struct sway_workspace *workspace = output->current.active_workspace; | ||
571 | if (workspace == NULL) { | ||
572 | return 0; | 239 | return 0; |
573 | } | 240 | } |
574 | 241 | ||
575 | struct sway_container *fullscreen_con = root->fullscreen_global; | 242 | output->wlr_output->frame_pending = false; |
576 | if (!fullscreen_con) { | ||
577 | fullscreen_con = workspace->current.fullscreen; | ||
578 | } | ||
579 | 243 | ||
580 | if (fullscreen_con && fullscreen_con->view) { | 244 | output_configure_scene(output, &root->root_scene->tree.node, 1.0f); |
581 | // Try to scan-out the fullscreen view | ||
582 | static bool last_scanned_out = false; | ||
583 | bool scanned_out = | ||
584 | scan_out_fullscreen_view(output, fullscreen_con->view); | ||
585 | 245 | ||
586 | if (scanned_out && !last_scanned_out) { | 246 | if (output->gamma_lut_changed) { |
587 | sway_log(SWAY_DEBUG, "Scanning out fullscreen view on %s", | 247 | struct wlr_output_state pending; |
588 | output->wlr_output->name); | 248 | wlr_output_state_init(&pending); |
249 | if (!wlr_scene_output_build_state(output->scene_output, &pending, NULL)) { | ||
250 | return 0; | ||
589 | } | 251 | } |
590 | if (last_scanned_out && !scanned_out) { | 252 | |
591 | sway_log(SWAY_DEBUG, "Stopping fullscreen view scan out on %s", | 253 | output->gamma_lut_changed = false; |
592 | output->wlr_output->name); | 254 | struct wlr_gamma_control_v1 *gamma_control = |
255 | wlr_gamma_control_manager_v1_get_control( | ||
256 | server.gamma_control_manager_v1, output->wlr_output); | ||
257 | if (!wlr_gamma_control_v1_apply(gamma_control, &pending)) { | ||
258 | wlr_output_state_finish(&pending); | ||
259 | return 0; | ||
593 | } | 260 | } |
594 | last_scanned_out = scanned_out; | ||
595 | 261 | ||
596 | if (scanned_out) { | 262 | if (!wlr_output_commit_state(output->wlr_output, &pending)) { |
263 | wlr_gamma_control_v1_send_failed_and_destroy(gamma_control); | ||
264 | wlr_output_state_finish(&pending); | ||
597 | return 0; | 265 | return 0; |
598 | } | 266 | } |
599 | } | ||
600 | 267 | ||
601 | bool needs_frame; | 268 | wlr_output_state_finish(&pending); |
602 | pixman_region32_t damage; | ||
603 | pixman_region32_init(&damage); | ||
604 | if (!wlr_output_damage_attach_render(output->damage, | ||
605 | &needs_frame, &damage)) { | ||
606 | return 0; | 269 | return 0; |
607 | } | 270 | } |
608 | 271 | ||
609 | if (needs_frame) { | 272 | wlr_scene_output_commit(output->scene_output, NULL); |
610 | struct timespec now; | ||
611 | clock_gettime(CLOCK_MONOTONIC, &now); | ||
612 | |||
613 | output_render(output, &now, &damage); | ||
614 | } else { | ||
615 | wlr_output_rollback(output->wlr_output); | ||
616 | } | ||
617 | |||
618 | pixman_region32_fini(&damage); | ||
619 | |||
620 | return 0; | 273 | return 0; |
621 | } | 274 | } |
622 | 275 | ||
623 | static void damage_handle_frame(struct wl_listener *listener, void *user_data) { | 276 | static void handle_frame(struct wl_listener *listener, void *user_data) { |
624 | struct sway_output *output = | 277 | struct sway_output *output = |
625 | wl_container_of(listener, output, damage_frame); | 278 | wl_container_of(listener, output, frame); |
626 | if (!output->enabled || !output->wlr_output->enabled) { | 279 | if (!output->enabled || !output->wlr_output->enabled) { |
627 | return; | 280 | return; |
628 | } | 281 | } |
@@ -633,9 +286,7 @@ static void damage_handle_frame(struct wl_listener *listener, void *user_data) { | |||
633 | 286 | ||
634 | if (output->max_render_time != 0) { | 287 | if (output->max_render_time != 0) { |
635 | struct timespec now; | 288 | struct timespec now; |
636 | clockid_t presentation_clock | 289 | clock_gettime(CLOCK_MONOTONIC, &now); |
637 | = wlr_backend_get_presentation_clock(server.backend); | ||
638 | clock_gettime(presentation_clock, &now); | ||
639 | 290 | ||
640 | const long NSEC_IN_SECONDS = 1000000000; | 291 | const long NSEC_IN_SECONDS = 1000000000; |
641 | struct timespec predicted_refresh = output->last_presentation; | 292 | struct timespec predicted_refresh = output->last_presentation; |
@@ -682,124 +333,8 @@ static void damage_handle_frame(struct wl_listener *listener, void *user_data) { | |||
682 | struct send_frame_done_data data = {0}; | 333 | struct send_frame_done_data data = {0}; |
683 | clock_gettime(CLOCK_MONOTONIC, &data.when); | 334 | clock_gettime(CLOCK_MONOTONIC, &data.when); |
684 | data.msec_until_refresh = msec_until_refresh; | 335 | data.msec_until_refresh = msec_until_refresh; |
685 | send_frame_done(output, &data); | 336 | data.output = output; |
686 | } | 337 | wlr_scene_output_for_each_buffer(output->scene_output, send_frame_done_iterator, &data); |
687 | |||
688 | void output_damage_whole(struct sway_output *output) { | ||
689 | // The output can exist with no wlr_output if it's just been disconnected | ||
690 | // and the transaction to evacuate it has't completed yet. | ||
691 | if (output && output->wlr_output && output->damage) { | ||
692 | wlr_output_damage_add_whole(output->damage); | ||
693 | } | ||
694 | } | ||
695 | |||
696 | static void damage_surface_iterator(struct sway_output *output, struct sway_view *view, | ||
697 | struct wlr_surface *surface, struct wlr_box *_box, float rotation, | ||
698 | void *_data) { | ||
699 | bool *data = _data; | ||
700 | bool whole = *data; | ||
701 | |||
702 | struct wlr_box box = *_box; | ||
703 | scale_box(&box, output->wlr_output->scale); | ||
704 | |||
705 | int center_x = box.x + box.width/2; | ||
706 | int center_y = box.y + box.height/2; | ||
707 | |||
708 | if (pixman_region32_not_empty(&surface->buffer_damage)) { | ||
709 | pixman_region32_t damage; | ||
710 | pixman_region32_init(&damage); | ||
711 | wlr_surface_get_effective_damage(surface, &damage); | ||
712 | wlr_region_scale(&damage, &damage, output->wlr_output->scale); | ||
713 | if (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 | } | ||
725 | |||
726 | if (whole) { | ||
727 | wlr_box_rotated_bounds(&box, &box, rotation); | ||
728 | wlr_output_damage_add_box(output->damage, &box); | ||
729 | } | ||
730 | |||
731 | if (!wl_list_empty(&surface->current.frame_callback_list)) { | ||
732 | wlr_output_schedule_frame(output->wlr_output); | ||
733 | } | ||
734 | } | ||
735 | |||
736 | void output_damage_surface(struct sway_output *output, double ox, double oy, | ||
737 | struct wlr_surface *surface, bool whole) { | ||
738 | output_surface_for_each_surface(output, surface, ox, oy, | ||
739 | damage_surface_iterator, &whole); | ||
740 | } | ||
741 | |||
742 | void output_damage_from_view(struct sway_output *output, | ||
743 | struct sway_view *view) { | ||
744 | if (!view_is_visible(view)) { | ||
745 | return; | ||
746 | } | ||
747 | bool whole = false; | ||
748 | output_view_for_each_surface(output, view, damage_surface_iterator, &whole); | ||
749 | } | ||
750 | |||
751 | // Expecting an unscaled box in layout coordinates | ||
752 | void output_damage_box(struct sway_output *output, struct wlr_box *_box) { | ||
753 | struct wlr_box box; | ||
754 | memcpy(&box, _box, sizeof(struct wlr_box)); | ||
755 | box.x -= output->lx; | ||
756 | box.y -= output->ly; | ||
757 | scale_box(&box, output->wlr_output->scale); | ||
758 | wlr_output_damage_add_box(output->damage, &box); | ||
759 | } | ||
760 | |||
761 | static void damage_child_views_iterator(struct sway_container *con, | ||
762 | void *data) { | ||
763 | if (!con->view || !view_is_visible(con->view)) { | ||
764 | return; | ||
765 | } | ||
766 | struct sway_output *output = data; | ||
767 | bool whole = true; | ||
768 | output_view_for_each_surface(output, con->view, damage_surface_iterator, | ||
769 | &whole); | ||
770 | } | ||
771 | |||
772 | void output_damage_whole_container(struct sway_output *output, | ||
773 | struct sway_container *con) { | ||
774 | // Pad the box by 1px, because the width is a double and might be a fraction | ||
775 | struct wlr_box box = { | ||
776 | .x = con->current.x - output->lx - 1, | ||
777 | .y = con->current.y - output->ly - 1, | ||
778 | .width = con->current.width + 2, | ||
779 | .height = con->current.height + 2, | ||
780 | }; | ||
781 | scale_box(&box, output->wlr_output->scale); | ||
782 | wlr_output_damage_add_box(output->damage, &box); | ||
783 | // Damage subsurfaces as well, which may extend outside the box | ||
784 | if (con->view) { | ||
785 | damage_child_views_iterator(con, output); | ||
786 | } else { | ||
787 | container_for_each_child(con, damage_child_views_iterator, output); | ||
788 | } | ||
789 | } | ||
790 | |||
791 | static void damage_handle_destroy(struct wl_listener *listener, void *data) { | ||
792 | struct sway_output *output = | ||
793 | wl_container_of(listener, output, damage_destroy); | ||
794 | if (!output->enabled) { | ||
795 | return; | ||
796 | } | ||
797 | output_disable(output); | ||
798 | |||
799 | wl_list_remove(&output->damage_destroy.link); | ||
800 | wl_list_remove(&output->damage_frame.link); | ||
801 | |||
802 | transaction_commit_dirty(); | ||
803 | } | 338 | } |
804 | 339 | ||
805 | static void update_output_manager_config(struct sway_server *server) { | 340 | static void update_output_manager_config(struct sway_server *server) { |
@@ -808,73 +343,61 @@ static void update_output_manager_config(struct sway_server *server) { | |||
808 | 343 | ||
809 | struct sway_output *output; | 344 | struct sway_output *output; |
810 | wl_list_for_each(output, &root->all_outputs, link) { | 345 | wl_list_for_each(output, &root->all_outputs, link) { |
811 | if (output == root->noop_output) { | 346 | if (output == root->fallback_output) { |
812 | continue; | 347 | continue; |
813 | } | 348 | } |
814 | struct wlr_output_configuration_head_v1 *config_head = | 349 | struct wlr_output_configuration_head_v1 *config_head = |
815 | wlr_output_configuration_head_v1_create(config, output->wlr_output); | 350 | wlr_output_configuration_head_v1_create(config, output->wlr_output); |
816 | struct wlr_box *output_box = wlr_output_layout_get_box( | 351 | struct wlr_box output_box; |
817 | root->output_layout, output->wlr_output); | 352 | wlr_output_layout_get_box(root->output_layout, |
818 | // We mark the output enabled even if it is switched off by DPMS | 353 | output->wlr_output, &output_box); |
819 | config_head->state.enabled = output->enabled; | 354 | // We mark the output enabled when it's switched off but not disabled |
820 | config_head->state.mode = output->current_mode; | 355 | config_head->state.enabled = !wlr_box_empty(&output_box); |
821 | if (output_box) { | 356 | config_head->state.x = output_box.x; |
822 | config_head->state.x = output_box->x; | 357 | config_head->state.y = output_box.y; |
823 | config_head->state.y = output_box->y; | ||
824 | } | ||
825 | } | 358 | } |
826 | 359 | ||
827 | wlr_output_manager_v1_set_configuration(server->output_manager_v1, config); | 360 | wlr_output_manager_v1_set_configuration(server->output_manager_v1, config); |
361 | |||
362 | ipc_event_output(); | ||
828 | } | 363 | } |
829 | 364 | ||
830 | static void handle_destroy(struct wl_listener *listener, void *data) { | 365 | static void begin_destroy(struct sway_output *output) { |
831 | struct sway_output *output = wl_container_of(listener, output, destroy); | ||
832 | struct sway_server *server = output->server; | 366 | struct sway_server *server = output->server; |
833 | wl_signal_emit(&output->events.destroy, output); | ||
834 | 367 | ||
835 | if (output->enabled) { | 368 | if (output->enabled) { |
836 | output_disable(output); | 369 | output_disable(output); |
837 | } | 370 | } |
371 | |||
838 | output_begin_destroy(output); | 372 | output_begin_destroy(output); |
839 | 373 | ||
374 | wl_list_remove(&output->link); | ||
375 | |||
376 | wl_list_remove(&output->layout_destroy.link); | ||
840 | wl_list_remove(&output->destroy.link); | 377 | wl_list_remove(&output->destroy.link); |
841 | wl_list_remove(&output->commit.link); | 378 | wl_list_remove(&output->commit.link); |
842 | wl_list_remove(&output->mode.link); | ||
843 | wl_list_remove(&output->present.link); | 379 | wl_list_remove(&output->present.link); |
380 | wl_list_remove(&output->frame.link); | ||
381 | wl_list_remove(&output->request_state.link); | ||
382 | |||
383 | wlr_scene_output_destroy(output->scene_output); | ||
384 | output->scene_output = NULL; | ||
385 | output->wlr_output->data = NULL; | ||
386 | output->wlr_output = NULL; | ||
844 | 387 | ||
845 | transaction_commit_dirty(); | 388 | transaction_commit_dirty(); |
846 | 389 | ||
847 | update_output_manager_config(server); | 390 | update_output_manager_config(server); |
848 | } | 391 | } |
849 | 392 | ||
850 | static void handle_mode(struct wl_listener *listener, void *data) { | 393 | static void handle_destroy(struct wl_listener *listener, void *data) { |
851 | struct sway_output *output = wl_container_of(listener, output, mode); | 394 | struct sway_output *output = wl_container_of(listener, output, destroy); |
852 | if (!output->enabled && !output->enabling) { | 395 | begin_destroy(output); |
853 | struct output_config *oc = find_output_config(output); | ||
854 | if (output->wlr_output->current_mode != NULL && | ||
855 | (!oc || oc->enabled)) { | ||
856 | // We want to enable this output, but it didn't work last time, | ||
857 | // possibly because we hadn't enough CRTCs. Try again now that the | ||
858 | // output has a mode. | ||
859 | sway_log(SWAY_DEBUG, "Output %s has gained a CRTC, " | ||
860 | "trying to enable it", output->wlr_output->name); | ||
861 | apply_output_config(oc, output); | ||
862 | } | ||
863 | return; | ||
864 | } | ||
865 | if (!output->enabled) { | ||
866 | return; | ||
867 | } | ||
868 | arrange_layers(output); | ||
869 | arrange_output(output); | ||
870 | transaction_commit_dirty(); | ||
871 | |||
872 | update_output_manager_config(output->server); | ||
873 | } | 396 | } |
874 | 397 | ||
875 | static void update_textures(struct sway_container *con, void *data) { | 398 | static void handle_layout_destroy(struct wl_listener *listener, void *data) { |
876 | container_update_title_textures(con); | 399 | struct sway_output *output = wl_container_of(listener, output, layout_destroy); |
877 | container_update_marks_textures(con); | 400 | begin_destroy(output); |
878 | } | 401 | } |
879 | 402 | ||
880 | static void handle_commit(struct wl_listener *listener, void *data) { | 403 | static void handle_commit(struct wl_listener *listener, void *data) { |
@@ -885,24 +408,28 @@ static void handle_commit(struct wl_listener *listener, void *data) { | |||
885 | return; | 408 | return; |
886 | } | 409 | } |
887 | 410 | ||
888 | if (event->committed & WLR_OUTPUT_STATE_SCALE) { | 411 | if (event->state->committed & ( |
889 | output_for_each_container(output, update_textures, NULL); | 412 | WLR_OUTPUT_STATE_MODE | |
890 | } | 413 | WLR_OUTPUT_STATE_TRANSFORM | |
891 | 414 | WLR_OUTPUT_STATE_SCALE)) { | |
892 | if (event->committed & (WLR_OUTPUT_STATE_TRANSFORM | WLR_OUTPUT_STATE_SCALE)) { | ||
893 | arrange_layers(output); | 415 | arrange_layers(output); |
894 | arrange_output(output); | 416 | arrange_output(output); |
895 | transaction_commit_dirty(); | 417 | transaction_commit_dirty(); |
896 | 418 | ||
897 | update_output_manager_config(output->server); | 419 | update_output_manager_config(output->server); |
898 | } | 420 | } |
421 | |||
422 | // Next time the output is enabled, try to re-apply the gamma LUT | ||
423 | if ((event->state->committed & WLR_OUTPUT_STATE_ENABLED) && !output->wlr_output->enabled) { | ||
424 | output->gamma_lut_changed = true; | ||
425 | } | ||
899 | } | 426 | } |
900 | 427 | ||
901 | static void handle_present(struct wl_listener *listener, void *data) { | 428 | static void handle_present(struct wl_listener *listener, void *data) { |
902 | struct sway_output *output = wl_container_of(listener, output, present); | 429 | struct sway_output *output = wl_container_of(listener, output, present); |
903 | struct wlr_output_event_present *output_event = data; | 430 | struct wlr_output_event_present *output_event = data; |
904 | 431 | ||
905 | if (!output->enabled) { | 432 | if (!output->enabled || !output_event->presented) { |
906 | return; | 433 | return; |
907 | } | 434 | } |
908 | 435 | ||
@@ -910,37 +437,91 @@ static void handle_present(struct wl_listener *listener, void *data) { | |||
910 | output->refresh_nsec = output_event->refresh; | 437 | output->refresh_nsec = output_event->refresh; |
911 | } | 438 | } |
912 | 439 | ||
440 | static void handle_request_state(struct wl_listener *listener, void *data) { | ||
441 | struct sway_output *output = | ||
442 | wl_container_of(listener, output, request_state); | ||
443 | const struct wlr_output_event_request_state *event = data; | ||
444 | wlr_output_commit_state(output->wlr_output, event->state); | ||
445 | } | ||
446 | |||
447 | static unsigned int last_headless_num = 0; | ||
448 | |||
913 | void handle_new_output(struct wl_listener *listener, void *data) { | 449 | void handle_new_output(struct wl_listener *listener, void *data) { |
914 | struct sway_server *server = wl_container_of(listener, server, new_output); | 450 | struct sway_server *server = wl_container_of(listener, server, new_output); |
915 | struct wlr_output *wlr_output = data; | 451 | struct wlr_output *wlr_output = data; |
916 | sway_log(SWAY_DEBUG, "New output %p: %s", wlr_output, wlr_output->name); | 452 | |
453 | if (wlr_output == root->fallback_output->wlr_output) { | ||
454 | return; | ||
455 | } | ||
456 | |||
457 | if (wlr_output_is_headless(wlr_output)) { | ||
458 | char name[64]; | ||
459 | snprintf(name, sizeof(name), "HEADLESS-%u", ++last_headless_num); | ||
460 | wlr_output_set_name(wlr_output, name); | ||
461 | } | ||
462 | |||
463 | sway_log(SWAY_DEBUG, "New output %p: %s (non-desktop: %d)", | ||
464 | wlr_output, wlr_output->name, wlr_output->non_desktop); | ||
465 | |||
466 | if (wlr_output->non_desktop) { | ||
467 | sway_log(SWAY_DEBUG, "Not configuring non-desktop output"); | ||
468 | struct sway_output_non_desktop *non_desktop = output_non_desktop_create(wlr_output); | ||
469 | #if WLR_HAS_DRM_BACKEND | ||
470 | if (server->drm_lease_manager) { | ||
471 | wlr_drm_lease_v1_manager_offer_output(server->drm_lease_manager, | ||
472 | wlr_output); | ||
473 | } | ||
474 | #endif | ||
475 | list_add(root->non_desktop_outputs, non_desktop); | ||
476 | return; | ||
477 | } | ||
478 | |||
479 | if (!wlr_output_init_render(wlr_output, server->allocator, | ||
480 | server->renderer)) { | ||
481 | sway_log(SWAY_ERROR, "Failed to init output render"); | ||
482 | return; | ||
483 | } | ||
484 | |||
485 | // Create the scene output here so we're not accidentally creating one for | ||
486 | // the fallback output | ||
487 | struct wlr_scene_output *scene_output = | ||
488 | wlr_scene_output_create(root->root_scene, wlr_output); | ||
489 | if (!scene_output) { | ||
490 | sway_log(SWAY_ERROR, "Failed to create a scene output"); | ||
491 | return; | ||
492 | } | ||
917 | 493 | ||
918 | struct sway_output *output = output_create(wlr_output); | 494 | struct sway_output *output = output_create(wlr_output); |
919 | if (!output) { | 495 | if (!output) { |
496 | sway_log(SWAY_ERROR, "Failed to create a sway output"); | ||
497 | wlr_scene_output_destroy(scene_output); | ||
920 | return; | 498 | return; |
921 | } | 499 | } |
500 | |||
922 | output->server = server; | 501 | output->server = server; |
923 | output->damage = wlr_output_damage_create(wlr_output); | 502 | output->scene_output = scene_output; |
924 | 503 | ||
504 | wl_signal_add(&root->output_layout->events.destroy, &output->layout_destroy); | ||
505 | output->layout_destroy.notify = handle_layout_destroy; | ||
925 | wl_signal_add(&wlr_output->events.destroy, &output->destroy); | 506 | wl_signal_add(&wlr_output->events.destroy, &output->destroy); |
926 | output->destroy.notify = handle_destroy; | 507 | output->destroy.notify = handle_destroy; |
927 | wl_signal_add(&wlr_output->events.commit, &output->commit); | 508 | wl_signal_add(&wlr_output->events.commit, &output->commit); |
928 | output->commit.notify = handle_commit; | 509 | output->commit.notify = handle_commit; |
929 | wl_signal_add(&wlr_output->events.mode, &output->mode); | ||
930 | output->mode.notify = handle_mode; | ||
931 | wl_signal_add(&wlr_output->events.present, &output->present); | 510 | wl_signal_add(&wlr_output->events.present, &output->present); |
932 | output->present.notify = handle_present; | 511 | output->present.notify = handle_present; |
933 | wl_signal_add(&output->damage->events.frame, &output->damage_frame); | 512 | wl_signal_add(&wlr_output->events.frame, &output->frame); |
934 | output->damage_frame.notify = damage_handle_frame; | 513 | output->frame.notify = handle_frame; |
935 | wl_signal_add(&output->damage->events.destroy, &output->damage_destroy); | 514 | wl_signal_add(&wlr_output->events.request_state, &output->request_state); |
936 | output->damage_destroy.notify = damage_handle_destroy; | 515 | output->request_state.notify = handle_request_state; |
937 | 516 | ||
938 | output->repaint_timer = wl_event_loop_add_timer(server->wl_event_loop, | 517 | output->repaint_timer = wl_event_loop_add_timer(server->wl_event_loop, |
939 | output_repaint_timer_handler, output); | 518 | output_repaint_timer_handler, output); |
940 | 519 | ||
941 | struct output_config *oc = find_output_config(output); | 520 | if (server->session_lock.lock) { |
942 | apply_output_config(oc, output); | 521 | sway_session_lock_add_output(server->session_lock.lock, output); |
943 | free_output_config(oc); | 522 | } |
523 | |||
524 | apply_all_output_configs(); | ||
944 | 525 | ||
945 | transaction_commit_dirty(); | 526 | transaction_commit_dirty(); |
946 | 527 | ||
@@ -954,62 +535,104 @@ void handle_output_layout_change(struct wl_listener *listener, | |||
954 | update_output_manager_config(server); | 535 | update_output_manager_config(server); |
955 | } | 536 | } |
956 | 537 | ||
538 | void handle_gamma_control_set_gamma(struct wl_listener *listener, void *data) { | ||
539 | struct sway_server *server = | ||
540 | wl_container_of(listener, server, gamma_control_set_gamma); | ||
541 | const struct wlr_gamma_control_manager_v1_set_gamma_event *event = data; | ||
542 | |||
543 | struct sway_output *output = event->output->data; | ||
544 | |||
545 | if(!output) { | ||
546 | return; | ||
547 | } | ||
548 | |||
549 | output->gamma_lut_changed = true; | ||
550 | wlr_output_schedule_frame(output->wlr_output); | ||
551 | } | ||
552 | |||
553 | static struct output_config *output_config_for_config_head( | ||
554 | struct wlr_output_configuration_head_v1 *config_head, | ||
555 | struct sway_output *output) { | ||
556 | struct output_config *oc = new_output_config(output->wlr_output->name); | ||
557 | oc->enabled = config_head->state.enabled; | ||
558 | if (!oc->enabled) { | ||
559 | return oc; | ||
560 | } | ||
561 | |||
562 | if (config_head->state.mode != NULL) { | ||
563 | struct wlr_output_mode *mode = config_head->state.mode; | ||
564 | oc->width = mode->width; | ||
565 | oc->height = mode->height; | ||
566 | oc->refresh_rate = mode->refresh / 1000.f; | ||
567 | } else { | ||
568 | oc->width = config_head->state.custom_mode.width; | ||
569 | oc->height = config_head->state.custom_mode.height; | ||
570 | oc->refresh_rate = | ||
571 | config_head->state.custom_mode.refresh / 1000.f; | ||
572 | } | ||
573 | oc->x = config_head->state.x; | ||
574 | oc->y = config_head->state.y; | ||
575 | oc->transform = config_head->state.transform; | ||
576 | oc->scale = config_head->state.scale; | ||
577 | oc->adaptive_sync = config_head->state.adaptive_sync_enabled; | ||
578 | return oc; | ||
579 | } | ||
580 | |||
957 | static void output_manager_apply(struct sway_server *server, | 581 | static void output_manager_apply(struct sway_server *server, |
958 | struct wlr_output_configuration_v1 *config, bool test_only) { | 582 | struct wlr_output_configuration_v1 *config, bool test_only) { |
959 | // TODO: perform atomic tests on the whole backend atomically | 583 | size_t configs_len = wl_list_length(&root->all_outputs); |
960 | 584 | struct matched_output_config *configs = calloc(configs_len, sizeof(*configs)); | |
961 | struct wlr_output_configuration_head_v1 *config_head; | 585 | if (!configs) { |
962 | // First disable outputs we need to disable | 586 | return; |
963 | bool ok = true; | 587 | } |
964 | wl_list_for_each(config_head, &config->heads, link) { | 588 | |
965 | struct wlr_output *wlr_output = config_head->state.output; | 589 | int config_idx = 0; |
966 | struct sway_output *output = wlr_output->data; | 590 | struct sway_output *sway_output; |
967 | if (!output->enabled || config_head->state.enabled) { | 591 | wl_list_for_each(sway_output, &root->all_outputs, link) { |
592 | if (sway_output == root->fallback_output) { | ||
593 | configs_len--; | ||
968 | continue; | 594 | continue; |
969 | } | 595 | } |
970 | struct output_config *oc = new_output_config(output->wlr_output->name); | ||
971 | oc->enabled = false; | ||
972 | 596 | ||
973 | if (test_only) { | 597 | struct matched_output_config *cfg = &configs[config_idx++]; |
974 | ok &= test_output_config(oc, output); | 598 | cfg->output = sway_output; |
975 | } else { | ||
976 | oc = store_output_config(oc); | ||
977 | ok &= apply_output_config(oc, output); | ||
978 | } | ||
979 | } | ||
980 | 599 | ||
981 | // Then enable outputs that need to | 600 | struct wlr_output_configuration_head_v1 *config_head; |
982 | wl_list_for_each(config_head, &config->heads, link) { | 601 | wl_list_for_each(config_head, &config->heads, link) { |
983 | struct wlr_output *wlr_output = config_head->state.output; | 602 | if (config_head->state.output == sway_output->wlr_output) { |
984 | struct sway_output *output = wlr_output->data; | 603 | cfg->config = output_config_for_config_head(config_head, sway_output); |
985 | if (!config_head->state.enabled) { | 604 | break; |
986 | continue; | 605 | } |
987 | } | 606 | } |
988 | struct output_config *oc = new_output_config(output->wlr_output->name); | 607 | if (!cfg->config) { |
989 | oc->enabled = true; | 608 | cfg->config = find_output_config(sway_output); |
990 | if (config_head->state.mode != NULL) { | ||
991 | struct wlr_output_mode *mode = config_head->state.mode; | ||
992 | oc->width = mode->width; | ||
993 | oc->height = mode->height; | ||
994 | oc->refresh_rate = mode->refresh / 1000.f; | ||
995 | } else { | ||
996 | oc->width = config_head->state.custom_mode.width; | ||
997 | oc->height = config_head->state.custom_mode.height; | ||
998 | oc->refresh_rate = | ||
999 | config_head->state.custom_mode.refresh / 1000.f; | ||
1000 | } | 609 | } |
1001 | oc->x = config_head->state.x; | 610 | } |
1002 | oc->y = config_head->state.y; | ||
1003 | oc->transform = config_head->state.transform; | ||
1004 | oc->scale = config_head->state.scale; | ||
1005 | 611 | ||
1006 | if (test_only) { | 612 | sort_output_configs_by_priority(configs, configs_len); |
1007 | ok &= test_output_config(oc, output); | 613 | bool ok = apply_output_configs(configs, configs_len, test_only, false); |
614 | for (size_t idx = 0; idx < configs_len; idx++) { | ||
615 | struct matched_output_config *cfg = &configs[idx]; | ||
616 | |||
617 | // Only store new configs for successful non-test commits. Old configs, | ||
618 | // test-only and failed commits just get freed. | ||
619 | bool store_config = false; | ||
620 | if (!test_only && ok) { | ||
621 | struct wlr_output_configuration_head_v1 *config_head; | ||
622 | wl_list_for_each(config_head, &config->heads, link) { | ||
623 | if (config_head->state.output == cfg->output->wlr_output) { | ||
624 | store_config = true; | ||
625 | break; | ||
626 | } | ||
627 | } | ||
628 | } | ||
629 | if (store_config) { | ||
630 | store_output_config(cfg->config); | ||
1008 | } else { | 631 | } else { |
1009 | oc = store_output_config(oc); | 632 | free_output_config(cfg->config); |
1010 | ok &= apply_output_config(oc, output); | ||
1011 | } | 633 | } |
1012 | } | 634 | } |
635 | free(configs); | ||
1013 | 636 | ||
1014 | if (ok) { | 637 | if (ok) { |
1015 | wlr_output_configuration_v1_send_succeeded(config); | 638 | wlr_output_configuration_v1_send_succeeded(config); |
@@ -1047,12 +670,12 @@ void handle_output_power_manager_set_mode(struct wl_listener *listener, | |||
1047 | struct output_config *oc = new_output_config(output->wlr_output->name); | 670 | struct output_config *oc = new_output_config(output->wlr_output->name); |
1048 | switch (event->mode) { | 671 | switch (event->mode) { |
1049 | case ZWLR_OUTPUT_POWER_V1_MODE_OFF: | 672 | case ZWLR_OUTPUT_POWER_V1_MODE_OFF: |
1050 | oc->dpms_state = DPMS_OFF; | 673 | oc->power = 0; |
1051 | break; | 674 | break; |
1052 | case ZWLR_OUTPUT_POWER_V1_MODE_ON: | 675 | case ZWLR_OUTPUT_POWER_V1_MODE_ON: |
1053 | oc->dpms_state = DPMS_ON; | 676 | oc->power = 1; |
1054 | break; | 677 | break; |
1055 | } | 678 | } |
1056 | oc = store_output_config(oc); | 679 | store_output_config(oc); |
1057 | apply_output_config(oc, output); | 680 | apply_all_output_configs(); |
1058 | } | 681 | } |
diff --git a/sway/desktop/render.c b/sway/desktop/render.c deleted file mode 100644 index bd85282c..00000000 --- a/sway/desktop/render.c +++ /dev/null | |||
@@ -1,1125 +0,0 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <assert.h> | ||
3 | #include <GLES2/gl2.h> | ||
4 | #include <stdlib.h> | ||
5 | #include <strings.h> | ||
6 | #include <time.h> | ||
7 | #include <wayland-server-core.h> | ||
8 | #include <wlr/render/gles2.h> | ||
9 | #include <wlr/render/wlr_renderer.h> | ||
10 | #include <wlr/types/wlr_box.h> | ||
11 | #include <wlr/types/wlr_buffer.h> | ||
12 | #include <wlr/types/wlr_matrix.h> | ||
13 | #include <wlr/types/wlr_output_damage.h> | ||
14 | #include <wlr/types/wlr_output_layout.h> | ||
15 | #include <wlr/types/wlr_output.h> | ||
16 | #include <wlr/types/wlr_surface.h> | ||
17 | #include <wlr/util/region.h> | ||
18 | #include "log.h" | ||
19 | #include "config.h" | ||
20 | #include "sway/config.h" | ||
21 | #include "sway/input/input-manager.h" | ||
22 | #include "sway/input/seat.h" | ||
23 | #include "sway/layers.h" | ||
24 | #include "sway/output.h" | ||
25 | #include "sway/server.h" | ||
26 | #include "sway/tree/arrange.h" | ||
27 | #include "sway/tree/container.h" | ||
28 | #include "sway/tree/root.h" | ||
29 | #include "sway/tree/view.h" | ||
30 | #include "sway/tree/workspace.h" | ||
31 | |||
32 | struct render_data { | ||
33 | pixman_region32_t *damage; | ||
34 | float alpha; | ||
35 | }; | ||
36 | |||
37 | /** | ||
38 | * Apply scale to a width or height. | ||
39 | * | ||
40 | * One does not simply multiply the width by the scale. We allow fractional | ||
41 | * scaling, which means the resulting scaled width might be a decimal. | ||
42 | * So we round it. | ||
43 | * | ||
44 | * But even this can produce undesirable results depending on the X or Y offset | ||
45 | * of the box. For example, with a scale of 1.5, a box with width=1 should not | ||
46 | * scale to 2px if its X coordinate is 1, because the X coordinate would have | ||
47 | * scaled to 2px. | ||
48 | */ | ||
49 | static int scale_length(int length, int offset, float scale) { | ||
50 | return round((offset + length) * scale) - round(offset * scale); | ||
51 | } | ||
52 | |||
53 | static void scissor_output(struct wlr_output *wlr_output, | ||
54 | pixman_box32_t *rect) { | ||
55 | struct wlr_renderer *renderer = wlr_backend_get_renderer(wlr_output->backend); | ||
56 | assert(renderer); | ||
57 | |||
58 | struct wlr_box box = { | ||
59 | .x = rect->x1, | ||
60 | .y = rect->y1, | ||
61 | .width = rect->x2 - rect->x1, | ||
62 | .height = rect->y2 - rect->y1, | ||
63 | }; | ||
64 | |||
65 | int ow, oh; | ||
66 | wlr_output_transformed_resolution(wlr_output, &ow, &oh); | ||
67 | |||
68 | enum wl_output_transform transform = | ||
69 | wlr_output_transform_invert(wlr_output->transform); | ||
70 | wlr_box_transform(&box, &box, transform, ow, oh); | ||
71 | |||
72 | wlr_renderer_scissor(renderer, &box); | ||
73 | } | ||
74 | |||
75 | static void set_scale_filter(struct wlr_output *wlr_output, | ||
76 | struct wlr_texture *texture, enum scale_filter_mode scale_filter) { | ||
77 | if (!wlr_texture_is_gles2(texture)) { | ||
78 | return; | ||
79 | } | ||
80 | |||
81 | struct wlr_gles2_texture_attribs attribs; | ||
82 | wlr_gles2_texture_get_attribs(texture, &attribs); | ||
83 | |||
84 | glBindTexture(attribs.target, attribs.tex); | ||
85 | |||
86 | switch (scale_filter) { | ||
87 | case SCALE_FILTER_LINEAR: | ||
88 | glTexParameteri(attribs.target, GL_TEXTURE_MAG_FILTER, GL_LINEAR); | ||
89 | break; | ||
90 | case SCALE_FILTER_NEAREST: | ||
91 | glTexParameteri(attribs.target, GL_TEXTURE_MAG_FILTER, GL_NEAREST); | ||
92 | break; | ||
93 | case SCALE_FILTER_DEFAULT: | ||
94 | case SCALE_FILTER_SMART: | ||
95 | assert(false); // unreachable | ||
96 | } | ||
97 | } | ||
98 | |||
99 | static void render_texture(struct wlr_output *wlr_output, | ||
100 | pixman_region32_t *output_damage, struct wlr_texture *texture, | ||
101 | const struct wlr_fbox *src_box, const struct wlr_box *dst_box, | ||
102 | const float matrix[static 9], float alpha) { | ||
103 | struct wlr_renderer *renderer = | ||
104 | wlr_backend_get_renderer(wlr_output->backend); | ||
105 | struct sway_output *output = wlr_output->data; | ||
106 | |||
107 | struct wlr_gles2_texture_attribs attribs; | ||
108 | wlr_gles2_texture_get_attribs(texture, &attribs); | ||
109 | |||
110 | pixman_region32_t damage; | ||
111 | pixman_region32_init(&damage); | ||
112 | pixman_region32_union_rect(&damage, &damage, dst_box->x, dst_box->y, | ||
113 | dst_box->width, dst_box->height); | ||
114 | pixman_region32_intersect(&damage, &damage, output_damage); | ||
115 | bool damaged = pixman_region32_not_empty(&damage); | ||
116 | if (!damaged) { | ||
117 | goto damage_finish; | ||
118 | } | ||
119 | |||
120 | int nrects; | ||
121 | pixman_box32_t *rects = pixman_region32_rectangles(&damage, &nrects); | ||
122 | for (int i = 0; i < nrects; ++i) { | ||
123 | scissor_output(wlr_output, &rects[i]); | ||
124 | set_scale_filter(wlr_output, texture, output->scale_filter); | ||
125 | if (src_box != NULL) { | ||
126 | wlr_render_subtexture_with_matrix(renderer, texture, src_box, matrix, alpha); | ||
127 | } else { | ||
128 | wlr_render_texture_with_matrix(renderer, texture, matrix, alpha); | ||
129 | } | ||
130 | } | ||
131 | |||
132 | damage_finish: | ||
133 | pixman_region32_fini(&damage); | ||
134 | } | ||
135 | |||
136 | static void render_surface_iterator(struct sway_output *output, struct sway_view *view, | ||
137 | struct wlr_surface *surface, struct wlr_box *_box, float rotation, | ||
138 | void *_data) { | ||
139 | struct render_data *data = _data; | ||
140 | struct wlr_output *wlr_output = output->wlr_output; | ||
141 | pixman_region32_t *output_damage = data->damage; | ||
142 | float alpha = data->alpha; | ||
143 | |||
144 | struct wlr_texture *texture = wlr_surface_get_texture(surface); | ||
145 | if (!texture) { | ||
146 | return; | ||
147 | } | ||
148 | |||
149 | struct wlr_fbox src_box; | ||
150 | wlr_surface_get_buffer_source_box(surface, &src_box); | ||
151 | |||
152 | struct wlr_box dst_box = *_box; | ||
153 | scale_box(&dst_box, wlr_output->scale); | ||
154 | |||
155 | float matrix[9]; | ||
156 | enum wl_output_transform transform = | ||
157 | wlr_output_transform_invert(surface->current.transform); | ||
158 | wlr_matrix_project_box(matrix, &dst_box, transform, rotation, | ||
159 | wlr_output->transform_matrix); | ||
160 | |||
161 | render_texture(wlr_output, output_damage, texture, | ||
162 | &src_box, &dst_box, matrix, alpha); | ||
163 | |||
164 | wlr_presentation_surface_sampled_on_output(server.presentation, surface, | ||
165 | wlr_output); | ||
166 | } | ||
167 | |||
168 | static void render_layer_toplevel(struct sway_output *output, | ||
169 | pixman_region32_t *damage, struct wl_list *layer_surfaces) { | ||
170 | struct render_data data = { | ||
171 | .damage = damage, | ||
172 | .alpha = 1.0f, | ||
173 | }; | ||
174 | output_layer_for_each_toplevel_surface(output, layer_surfaces, | ||
175 | render_surface_iterator, &data); | ||
176 | } | ||
177 | |||
178 | static void render_layer_popups(struct sway_output *output, | ||
179 | pixman_region32_t *damage, struct wl_list *layer_surfaces) { | ||
180 | struct render_data data = { | ||
181 | .damage = damage, | ||
182 | .alpha = 1.0f, | ||
183 | }; | ||
184 | output_layer_for_each_popup_surface(output, layer_surfaces, | ||
185 | render_surface_iterator, &data); | ||
186 | } | ||
187 | |||
188 | #if HAVE_XWAYLAND | ||
189 | static void render_unmanaged(struct sway_output *output, | ||
190 | pixman_region32_t *damage, struct wl_list *unmanaged) { | ||
191 | struct render_data data = { | ||
192 | .damage = damage, | ||
193 | .alpha = 1.0f, | ||
194 | }; | ||
195 | output_unmanaged_for_each_surface(output, unmanaged, | ||
196 | render_surface_iterator, &data); | ||
197 | } | ||
198 | #endif | ||
199 | |||
200 | static void render_drag_icons(struct sway_output *output, | ||
201 | pixman_region32_t *damage, struct wl_list *drag_icons) { | ||
202 | struct render_data data = { | ||
203 | .damage = damage, | ||
204 | .alpha = 1.0f, | ||
205 | }; | ||
206 | output_drag_icons_for_each_surface(output, drag_icons, | ||
207 | render_surface_iterator, &data); | ||
208 | } | ||
209 | |||
210 | // _box.x and .y are expected to be layout-local | ||
211 | // _box.width and .height are expected to be output-buffer-local | ||
212 | void render_rect(struct sway_output *output, | ||
213 | pixman_region32_t *output_damage, const struct wlr_box *_box, | ||
214 | float color[static 4]) { | ||
215 | struct wlr_output *wlr_output = output->wlr_output; | ||
216 | struct wlr_renderer *renderer = | ||
217 | wlr_backend_get_renderer(wlr_output->backend); | ||
218 | |||
219 | struct wlr_box box; | ||
220 | memcpy(&box, _box, sizeof(struct wlr_box)); | ||
221 | box.x -= output->lx * wlr_output->scale; | ||
222 | box.y -= output->ly * wlr_output->scale; | ||
223 | |||
224 | pixman_region32_t damage; | ||
225 | pixman_region32_init(&damage); | ||
226 | pixman_region32_union_rect(&damage, &damage, box.x, box.y, | ||
227 | box.width, box.height); | ||
228 | pixman_region32_intersect(&damage, &damage, output_damage); | ||
229 | bool damaged = pixman_region32_not_empty(&damage); | ||
230 | if (!damaged) { | ||
231 | goto damage_finish; | ||
232 | } | ||
233 | |||
234 | int nrects; | ||
235 | pixman_box32_t *rects = pixman_region32_rectangles(&damage, &nrects); | ||
236 | for (int i = 0; i < nrects; ++i) { | ||
237 | scissor_output(wlr_output, &rects[i]); | ||
238 | wlr_render_rect(renderer, &box, color, | ||
239 | wlr_output->transform_matrix); | ||
240 | } | ||
241 | |||
242 | damage_finish: | ||
243 | pixman_region32_fini(&damage); | ||
244 | } | ||
245 | |||
246 | void premultiply_alpha(float color[4], float opacity) { | ||
247 | color[3] *= opacity; | ||
248 | color[0] *= color[3]; | ||
249 | color[1] *= color[3]; | ||
250 | color[2] *= color[3]; | ||
251 | } | ||
252 | |||
253 | static void render_view_toplevels(struct sway_view *view, | ||
254 | struct sway_output *output, pixman_region32_t *damage, float alpha) { | ||
255 | struct render_data data = { | ||
256 | .damage = damage, | ||
257 | .alpha = alpha, | ||
258 | }; | ||
259 | // Render all toplevels without descending into popups | ||
260 | double ox = view->container->surface_x - | ||
261 | output->lx - view->geometry.x; | ||
262 | double oy = view->container->surface_y - | ||
263 | output->ly - view->geometry.y; | ||
264 | output_surface_for_each_surface(output, view->surface, ox, oy, | ||
265 | render_surface_iterator, &data); | ||
266 | } | ||
267 | |||
268 | static void render_view_popups(struct sway_view *view, | ||
269 | struct sway_output *output, pixman_region32_t *damage, float alpha) { | ||
270 | struct render_data data = { | ||
271 | .damage = damage, | ||
272 | .alpha = alpha, | ||
273 | }; | ||
274 | output_view_for_each_popup_surface(output, view, | ||
275 | render_surface_iterator, &data); | ||
276 | } | ||
277 | |||
278 | static void render_saved_view(struct sway_view *view, | ||
279 | struct sway_output *output, pixman_region32_t *damage, float alpha) { | ||
280 | struct wlr_output *wlr_output = output->wlr_output; | ||
281 | |||
282 | if (wl_list_empty(&view->saved_buffers)) { | ||
283 | return; | ||
284 | } | ||
285 | struct sway_saved_buffer *saved_buf; | ||
286 | wl_list_for_each(saved_buf, &view->saved_buffers, link) { | ||
287 | if (!saved_buf->buffer->texture) { | ||
288 | continue; | ||
289 | } | ||
290 | |||
291 | struct wlr_box box = { | ||
292 | .x = view->container->surface_x - output->lx - | ||
293 | view->saved_geometry.x + saved_buf->x, | ||
294 | .y = view->container->surface_y - output->ly - | ||
295 | view->saved_geometry.y + saved_buf->y, | ||
296 | .width = saved_buf->width, | ||
297 | .height = saved_buf->height, | ||
298 | }; | ||
299 | |||
300 | struct wlr_box output_box = { | ||
301 | .width = output->width, | ||
302 | .height = output->height, | ||
303 | }; | ||
304 | |||
305 | struct wlr_box intersection; | ||
306 | bool intersects = wlr_box_intersection(&intersection, &output_box, &box); | ||
307 | if (!intersects) { | ||
308 | continue; | ||
309 | } | ||
310 | |||
311 | scale_box(&box, wlr_output->scale); | ||
312 | |||
313 | float matrix[9]; | ||
314 | enum wl_output_transform transform = wlr_output_transform_invert(saved_buf->transform); | ||
315 | wlr_matrix_project_box(matrix, &box, transform, 0, | ||
316 | wlr_output->transform_matrix); | ||
317 | |||
318 | render_texture(wlr_output, damage, saved_buf->buffer->texture, | ||
319 | &saved_buf->source_box, &box, matrix, alpha); | ||
320 | } | ||
321 | |||
322 | // FIXME: we should set the surface that this saved buffer originates from | ||
323 | // as sampled here. | ||
324 | // https://github.com/swaywm/sway/pull/4465#discussion_r321082059 | ||
325 | } | ||
326 | |||
327 | /** | ||
328 | * Render a view's surface and left/bottom/right borders. | ||
329 | */ | ||
330 | static void render_view(struct sway_output *output, pixman_region32_t *damage, | ||
331 | struct sway_container *con, struct border_colors *colors) { | ||
332 | struct sway_view *view = con->view; | ||
333 | if (!wl_list_empty(&view->saved_buffers)) { | ||
334 | render_saved_view(view, output, damage, view->container->alpha); | ||
335 | } else if (view->surface) { | ||
336 | render_view_toplevels(view, output, damage, view->container->alpha); | ||
337 | } | ||
338 | |||
339 | if (con->current.border == B_NONE || con->current.border == B_CSD) { | ||
340 | return; | ||
341 | } | ||
342 | |||
343 | struct wlr_box box; | ||
344 | float output_scale = output->wlr_output->scale; | ||
345 | float color[4]; | ||
346 | struct sway_container_state *state = &con->current; | ||
347 | |||
348 | if (state->border_left) { | ||
349 | memcpy(&color, colors->child_border, sizeof(float) * 4); | ||
350 | premultiply_alpha(color, con->alpha); | ||
351 | box.x = state->x; | ||
352 | box.y = state->content_y; | ||
353 | box.width = state->border_thickness; | ||
354 | box.height = state->content_height; | ||
355 | scale_box(&box, output_scale); | ||
356 | render_rect(output, damage, &box, color); | ||
357 | } | ||
358 | |||
359 | list_t *siblings = container_get_current_siblings(con); | ||
360 | enum sway_container_layout layout = | ||
361 | container_current_parent_layout(con); | ||
362 | |||
363 | if (state->border_right) { | ||
364 | if (!container_is_floating(con) && siblings->length == 1 && layout == L_HORIZ) { | ||
365 | memcpy(&color, colors->indicator, sizeof(float) * 4); | ||
366 | } else { | ||
367 | memcpy(&color, colors->child_border, sizeof(float) * 4); | ||
368 | } | ||
369 | premultiply_alpha(color, con->alpha); | ||
370 | box.x = state->content_x + state->content_width; | ||
371 | box.y = state->content_y; | ||
372 | box.width = state->border_thickness; | ||
373 | box.height = state->content_height; | ||
374 | scale_box(&box, output_scale); | ||
375 | render_rect(output, damage, &box, color); | ||
376 | } | ||
377 | |||
378 | if (state->border_bottom) { | ||
379 | if (!container_is_floating(con) && siblings->length == 1 && layout == L_VERT) { | ||
380 | memcpy(&color, colors->indicator, sizeof(float) * 4); | ||
381 | } else { | ||
382 | memcpy(&color, colors->child_border, sizeof(float) * 4); | ||
383 | } | ||
384 | premultiply_alpha(color, con->alpha); | ||
385 | box.x = state->x; | ||
386 | box.y = state->content_y + state->content_height; | ||
387 | box.width = state->width; | ||
388 | box.height = state->border_thickness; | ||
389 | scale_box(&box, output_scale); | ||
390 | render_rect(output, damage, &box, color); | ||
391 | } | ||
392 | } | ||
393 | |||
394 | /** | ||
395 | * Render a titlebar. | ||
396 | * | ||
397 | * Care must be taken not to render over the same pixel multiple times, | ||
398 | * otherwise the colors will be incorrect when using opacity. | ||
399 | * | ||
400 | * The height is: 1px border, 3px padding, font height, 3px padding, 1px border | ||
401 | * The left side is: 1px border, 2px padding, title | ||
402 | */ | ||
403 | static void render_titlebar(struct sway_output *output, | ||
404 | pixman_region32_t *output_damage, struct sway_container *con, | ||
405 | int x, int y, int width, | ||
406 | struct border_colors *colors, struct wlr_texture *title_texture, | ||
407 | struct wlr_texture *marks_texture) { | ||
408 | struct wlr_box box; | ||
409 | float color[4]; | ||
410 | float output_scale = output->wlr_output->scale; | ||
411 | double output_x = output->lx; | ||
412 | double output_y = output->ly; | ||
413 | int titlebar_border_thickness = config->titlebar_border_thickness; | ||
414 | int titlebar_h_padding = config->titlebar_h_padding; | ||
415 | int titlebar_v_padding = config->titlebar_v_padding; | ||
416 | enum alignment title_align = config->title_align; | ||
417 | |||
418 | // Single pixel bar above title | ||
419 | memcpy(&color, colors->border, sizeof(float) * 4); | ||
420 | premultiply_alpha(color, con->alpha); | ||
421 | box.x = x; | ||
422 | box.y = y; | ||
423 | box.width = width; | ||
424 | box.height = titlebar_border_thickness; | ||
425 | scale_box(&box, output_scale); | ||
426 | render_rect(output, output_damage, &box, color); | ||
427 | |||
428 | // Single pixel bar below title | ||
429 | box.x = x; | ||
430 | box.y = y + container_titlebar_height() - titlebar_border_thickness; | ||
431 | box.width = width; | ||
432 | box.height = titlebar_border_thickness; | ||
433 | scale_box(&box, output_scale); | ||
434 | render_rect(output, output_damage, &box, color); | ||
435 | |||
436 | // Single pixel left edge | ||
437 | box.x = x; | ||
438 | box.y = y + titlebar_border_thickness; | ||
439 | box.width = titlebar_border_thickness; | ||
440 | box.height = container_titlebar_height() - titlebar_border_thickness * 2; | ||
441 | scale_box(&box, output_scale); | ||
442 | render_rect(output, output_damage, &box, color); | ||
443 | |||
444 | // Single pixel right edge | ||
445 | box.x = x + width - titlebar_border_thickness; | ||
446 | box.y = y + titlebar_border_thickness; | ||
447 | box.width = titlebar_border_thickness; | ||
448 | box.height = container_titlebar_height() - titlebar_border_thickness * 2; | ||
449 | scale_box(&box, output_scale); | ||
450 | render_rect(output, output_damage, &box, color); | ||
451 | |||
452 | int inner_x = x - output_x + titlebar_h_padding; | ||
453 | int bg_y = y + titlebar_border_thickness; | ||
454 | size_t inner_width = width - titlebar_h_padding * 2; | ||
455 | |||
456 | // output-buffer local | ||
457 | int ob_inner_x = round(inner_x * output_scale); | ||
458 | int ob_inner_width = scale_length(inner_width, inner_x, output_scale); | ||
459 | int ob_bg_height = scale_length( | ||
460 | (titlebar_v_padding - titlebar_border_thickness) * 2 + | ||
461 | config->font_height, bg_y, output_scale); | ||
462 | |||
463 | // Marks | ||
464 | int ob_marks_x = 0; // output-buffer-local | ||
465 | int ob_marks_width = 0; // output-buffer-local | ||
466 | if (config->show_marks && marks_texture) { | ||
467 | struct wlr_box texture_box; | ||
468 | wlr_texture_get_size(marks_texture, | ||
469 | &texture_box.width, &texture_box.height); | ||
470 | ob_marks_width = texture_box.width; | ||
471 | |||
472 | // The marks texture might be shorter than the config->font_height, in | ||
473 | // which case we need to pad it as evenly as possible above and below. | ||
474 | int ob_padding_total = ob_bg_height - texture_box.height; | ||
475 | int ob_padding_above = floor(ob_padding_total / 2.0); | ||
476 | int ob_padding_below = ceil(ob_padding_total / 2.0); | ||
477 | |||
478 | // Render texture. If the title is on the right, the marks will be on | ||
479 | // the left. Otherwise, they will be on the right. | ||
480 | if (title_align == ALIGN_RIGHT || texture_box.width > ob_inner_width) { | ||
481 | texture_box.x = ob_inner_x; | ||
482 | } else { | ||
483 | texture_box.x = ob_inner_x + ob_inner_width - texture_box.width; | ||
484 | } | ||
485 | ob_marks_x = texture_box.x; | ||
486 | |||
487 | texture_box.y = round((bg_y - output_y) * output_scale) + | ||
488 | ob_padding_above; | ||
489 | |||
490 | float matrix[9]; | ||
491 | wlr_matrix_project_box(matrix, &texture_box, | ||
492 | WL_OUTPUT_TRANSFORM_NORMAL, | ||
493 | 0.0, output->wlr_output->transform_matrix); | ||
494 | |||
495 | if (ob_inner_width < texture_box.width) { | ||
496 | texture_box.width = ob_inner_width; | ||
497 | } | ||
498 | render_texture(output->wlr_output, output_damage, marks_texture, | ||
499 | NULL, &texture_box, matrix, con->alpha); | ||
500 | |||
501 | // Padding above | ||
502 | memcpy(&color, colors->background, sizeof(float) * 4); | ||
503 | premultiply_alpha(color, con->alpha); | ||
504 | box.x = texture_box.x + round(output_x * output_scale); | ||
505 | box.y = round((y + titlebar_border_thickness) * output_scale); | ||
506 | box.width = texture_box.width; | ||
507 | box.height = ob_padding_above; | ||
508 | render_rect(output, output_damage, &box, color); | ||
509 | |||
510 | // Padding below | ||
511 | box.y += ob_padding_above + texture_box.height; | ||
512 | box.height = ob_padding_below; | ||
513 | render_rect(output, output_damage, &box, color); | ||
514 | } | ||
515 | |||
516 | // Title text | ||
517 | int ob_title_x = 0; // output-buffer-local | ||
518 | int ob_title_width = 0; // output-buffer-local | ||
519 | if (title_texture) { | ||
520 | struct wlr_box texture_box; | ||
521 | wlr_texture_get_size(title_texture, | ||
522 | &texture_box.width, &texture_box.height); | ||
523 | ob_title_width = texture_box.width; | ||
524 | |||
525 | // The title texture might be shorter than the config->font_height, | ||
526 | // in which case we need to pad it above and below. | ||
527 | int ob_padding_above = round((config->font_baseline - | ||
528 | con->title_baseline + titlebar_v_padding - | ||
529 | titlebar_border_thickness) * output_scale); | ||
530 | int ob_padding_below = ob_bg_height - ob_padding_above - | ||
531 | texture_box.height; | ||
532 | |||
533 | // Render texture | ||
534 | if (texture_box.width > ob_inner_width - ob_marks_width) { | ||
535 | texture_box.x = (title_align == ALIGN_RIGHT && ob_marks_width) | ||
536 | ? ob_marks_x + ob_marks_width : ob_inner_x; | ||
537 | } else if (title_align == ALIGN_LEFT) { | ||
538 | texture_box.x = ob_inner_x; | ||
539 | } else if (title_align == ALIGN_CENTER) { | ||
540 | // If there are marks visible, center between the edge and marks. | ||
541 | // Otherwise, center in the inner area. | ||
542 | if (ob_marks_width) { | ||
543 | texture_box.x = (ob_inner_x + ob_marks_x) / 2 | ||
544 | - texture_box.width / 2; | ||
545 | } else { | ||
546 | texture_box.x = ob_inner_x + ob_inner_width / 2 | ||
547 | - texture_box.width / 2; | ||
548 | } | ||
549 | } else { | ||
550 | texture_box.x = ob_inner_x + ob_inner_width - texture_box.width; | ||
551 | } | ||
552 | ob_title_x = texture_box.x; | ||
553 | |||
554 | texture_box.y = | ||
555 | round((bg_y - output_y) * output_scale) + ob_padding_above; | ||
556 | |||
557 | float matrix[9]; | ||
558 | wlr_matrix_project_box(matrix, &texture_box, | ||
559 | WL_OUTPUT_TRANSFORM_NORMAL, | ||
560 | 0.0, output->wlr_output->transform_matrix); | ||
561 | |||
562 | if (ob_inner_width - ob_marks_width < texture_box.width) { | ||
563 | texture_box.width = ob_inner_width - ob_marks_width; | ||
564 | } | ||
565 | |||
566 | render_texture(output->wlr_output, output_damage, title_texture, | ||
567 | NULL, &texture_box, matrix, con->alpha); | ||
568 | |||
569 | // Padding above | ||
570 | memcpy(&color, colors->background, sizeof(float) * 4); | ||
571 | premultiply_alpha(color, con->alpha); | ||
572 | box.x = texture_box.x + round(output_x * output_scale); | ||
573 | box.y = round((y + titlebar_border_thickness) * output_scale); | ||
574 | box.width = texture_box.width; | ||
575 | box.height = ob_padding_above; | ||
576 | render_rect(output, output_damage, &box, color); | ||
577 | |||
578 | // Padding below | ||
579 | box.y += ob_padding_above + texture_box.height; | ||
580 | box.height = ob_padding_below; | ||
581 | render_rect(output, output_damage, &box, color); | ||
582 | } | ||
583 | |||
584 | // Determine the left + right extends of the textures (output-buffer local) | ||
585 | int ob_left_x, ob_left_width, ob_right_x, ob_right_width; | ||
586 | if (ob_title_width == 0 && ob_marks_width == 0) { | ||
587 | ob_left_x = ob_inner_x; | ||
588 | ob_left_width = 0; | ||
589 | ob_right_x = ob_inner_x; | ||
590 | ob_right_width = 0; | ||
591 | } else if (ob_title_x < ob_marks_x) { | ||
592 | ob_left_x = ob_title_x; | ||
593 | ob_left_width = ob_title_width; | ||
594 | ob_right_x = ob_marks_x; | ||
595 | ob_right_width = ob_marks_width; | ||
596 | } else { | ||
597 | ob_left_x = ob_marks_x; | ||
598 | ob_left_width = ob_marks_width; | ||
599 | ob_right_x = ob_title_x; | ||
600 | ob_right_width = ob_title_width; | ||
601 | } | ||
602 | if (ob_left_x < ob_inner_x) { | ||
603 | ob_left_x = ob_inner_x; | ||
604 | } else if (ob_left_x + ob_left_width > ob_right_x + ob_right_width) { | ||
605 | ob_right_x = ob_left_x; | ||
606 | ob_right_width = ob_left_width; | ||
607 | } | ||
608 | |||
609 | // Filler between title and marks | ||
610 | box.width = ob_right_x - ob_left_x - ob_left_width; | ||
611 | if (box.width > 0) { | ||
612 | box.x = ob_left_x + ob_left_width + round(output_x * output_scale); | ||
613 | box.y = round(bg_y * output_scale); | ||
614 | box.height = ob_bg_height; | ||
615 | render_rect(output, output_damage, &box, color); | ||
616 | } | ||
617 | |||
618 | // Padding on left side | ||
619 | box.x = x + titlebar_border_thickness; | ||
620 | box.y = y + titlebar_border_thickness; | ||
621 | box.width = titlebar_h_padding - titlebar_border_thickness; | ||
622 | box.height = (titlebar_v_padding - titlebar_border_thickness) * 2 + | ||
623 | config->font_height; | ||
624 | scale_box(&box, output_scale); | ||
625 | int left_x = ob_left_x + round(output_x * output_scale); | ||
626 | if (box.x + box.width < left_x) { | ||
627 | box.width += left_x - box.x - box.width; | ||
628 | } | ||
629 | render_rect(output, output_damage, &box, color); | ||
630 | |||
631 | // Padding on right side | ||
632 | box.x = x + width - titlebar_h_padding; | ||
633 | box.y = y + titlebar_border_thickness; | ||
634 | box.width = titlebar_h_padding - titlebar_border_thickness; | ||
635 | box.height = (titlebar_v_padding - titlebar_border_thickness) * 2 + | ||
636 | config->font_height; | ||
637 | scale_box(&box, output_scale); | ||
638 | int right_rx = ob_right_x + ob_right_width + round(output_x * output_scale); | ||
639 | if (right_rx < box.x) { | ||
640 | box.width += box.x - right_rx; | ||
641 | box.x = right_rx; | ||
642 | } | ||
643 | render_rect(output, output_damage, &box, color); | ||
644 | } | ||
645 | |||
646 | /** | ||
647 | * Render the top border line for a view using "border pixel". | ||
648 | */ | ||
649 | static void render_top_border(struct sway_output *output, | ||
650 | pixman_region32_t *output_damage, struct sway_container *con, | ||
651 | struct border_colors *colors) { | ||
652 | struct sway_container_state *state = &con->current; | ||
653 | if (!state->border_top) { | ||
654 | return; | ||
655 | } | ||
656 | struct wlr_box box; | ||
657 | float color[4]; | ||
658 | float output_scale = output->wlr_output->scale; | ||
659 | |||
660 | // Child border - top edge | ||
661 | memcpy(&color, colors->child_border, sizeof(float) * 4); | ||
662 | premultiply_alpha(color, con->alpha); | ||
663 | box.x = state->x; | ||
664 | box.y = state->y; | ||
665 | box.width = state->width; | ||
666 | box.height = state->border_thickness; | ||
667 | scale_box(&box, output_scale); | ||
668 | render_rect(output, output_damage, &box, color); | ||
669 | } | ||
670 | |||
671 | struct parent_data { | ||
672 | enum sway_container_layout layout; | ||
673 | struct wlr_box box; | ||
674 | list_t *children; | ||
675 | bool focused; | ||
676 | struct sway_container *active_child; | ||
677 | }; | ||
678 | |||
679 | static void render_container(struct sway_output *output, | ||
680 | pixman_region32_t *damage, struct sway_container *con, bool parent_focused); | ||
681 | |||
682 | /** | ||
683 | * Render a container's children using a L_HORIZ or L_VERT layout. | ||
684 | * | ||
685 | * Wrap child views in borders and leave child containers borderless because | ||
686 | * they'll apply their own borders to their children. | ||
687 | */ | ||
688 | static void render_containers_linear(struct sway_output *output, | ||
689 | pixman_region32_t *damage, struct parent_data *parent) { | ||
690 | for (int i = 0; i < parent->children->length; ++i) { | ||
691 | struct sway_container *child = parent->children->items[i]; | ||
692 | |||
693 | if (child->view) { | ||
694 | struct sway_view *view = child->view; | ||
695 | struct border_colors *colors; | ||
696 | struct wlr_texture *title_texture; | ||
697 | struct wlr_texture *marks_texture; | ||
698 | struct sway_container_state *state = &child->current; | ||
699 | |||
700 | if (view_is_urgent(view)) { | ||
701 | colors = &config->border_colors.urgent; | ||
702 | title_texture = child->title_urgent; | ||
703 | marks_texture = child->marks_urgent; | ||
704 | } else if (state->focused || parent->focused) { | ||
705 | colors = &config->border_colors.focused; | ||
706 | title_texture = child->title_focused; | ||
707 | marks_texture = child->marks_focused; | ||
708 | } else if (child == parent->active_child) { | ||
709 | colors = &config->border_colors.focused_inactive; | ||
710 | title_texture = child->title_focused_inactive; | ||
711 | marks_texture = child->marks_focused_inactive; | ||
712 | } else { | ||
713 | colors = &config->border_colors.unfocused; | ||
714 | title_texture = child->title_unfocused; | ||
715 | marks_texture = child->marks_unfocused; | ||
716 | } | ||
717 | |||
718 | if (state->border == B_NORMAL) { | ||
719 | render_titlebar(output, damage, child, state->x, | ||
720 | state->y, state->width, colors, | ||
721 | title_texture, marks_texture); | ||
722 | } else if (state->border == B_PIXEL) { | ||
723 | render_top_border(output, damage, child, colors); | ||
724 | } | ||
725 | render_view(output, damage, child, colors); | ||
726 | } else { | ||
727 | render_container(output, damage, child, | ||
728 | parent->focused || child->current.focused); | ||
729 | } | ||
730 | } | ||
731 | } | ||
732 | |||
733 | /** | ||
734 | * Render a container's children using the L_TABBED layout. | ||
735 | */ | ||
736 | static void render_containers_tabbed(struct sway_output *output, | ||
737 | pixman_region32_t *damage, struct parent_data *parent) { | ||
738 | if (!parent->children->length) { | ||
739 | return; | ||
740 | } | ||
741 | struct sway_container *current = parent->active_child; | ||
742 | struct border_colors *current_colors = &config->border_colors.unfocused; | ||
743 | int tab_width = parent->box.width / parent->children->length; | ||
744 | |||
745 | // Render tabs | ||
746 | for (int i = 0; i < parent->children->length; ++i) { | ||
747 | struct sway_container *child = parent->children->items[i]; | ||
748 | struct sway_view *view = child->view; | ||
749 | struct sway_container_state *cstate = &child->current; | ||
750 | struct border_colors *colors; | ||
751 | struct wlr_texture *title_texture; | ||
752 | struct wlr_texture *marks_texture; | ||
753 | bool urgent = view ? | ||
754 | view_is_urgent(view) : container_has_urgent_child(child); | ||
755 | |||
756 | if (urgent) { | ||
757 | colors = &config->border_colors.urgent; | ||
758 | title_texture = child->title_urgent; | ||
759 | marks_texture = child->marks_urgent; | ||
760 | } else if (cstate->focused || parent->focused) { | ||
761 | colors = &config->border_colors.focused; | ||
762 | title_texture = child->title_focused; | ||
763 | marks_texture = child->marks_focused; | ||
764 | } else if (child == parent->active_child) { | ||
765 | colors = &config->border_colors.focused_inactive; | ||
766 | title_texture = child->title_focused_inactive; | ||
767 | marks_texture = child->marks_focused_inactive; | ||
768 | } else { | ||
769 | colors = &config->border_colors.unfocused; | ||
770 | title_texture = child->title_unfocused; | ||
771 | marks_texture = child->marks_unfocused; | ||
772 | } | ||
773 | |||
774 | int x = cstate->x + tab_width * i; | ||
775 | |||
776 | // Make last tab use the remaining width of the parent | ||
777 | if (i == parent->children->length - 1) { | ||
778 | tab_width = parent->box.width - tab_width * i; | ||
779 | } | ||
780 | |||
781 | render_titlebar(output, damage, child, x, parent->box.y, tab_width, | ||
782 | colors, title_texture, marks_texture); | ||
783 | |||
784 | if (child == current) { | ||
785 | current_colors = colors; | ||
786 | } | ||
787 | } | ||
788 | |||
789 | // Render surface and left/right/bottom borders | ||
790 | if (current->view) { | ||
791 | render_view(output, damage, current, current_colors); | ||
792 | } else { | ||
793 | render_container(output, damage, current, | ||
794 | parent->focused || current->current.focused); | ||
795 | } | ||
796 | } | ||
797 | |||
798 | /** | ||
799 | * Render a container's children using the L_STACKED layout. | ||
800 | */ | ||
801 | static void render_containers_stacked(struct sway_output *output, | ||
802 | pixman_region32_t *damage, struct parent_data *parent) { | ||
803 | if (!parent->children->length) { | ||
804 | return; | ||
805 | } | ||
806 | struct sway_container *current = parent->active_child; | ||
807 | struct border_colors *current_colors = &config->border_colors.unfocused; | ||
808 | size_t titlebar_height = container_titlebar_height(); | ||
809 | |||
810 | // Render titles | ||
811 | for (int i = 0; i < parent->children->length; ++i) { | ||
812 | struct sway_container *child = parent->children->items[i]; | ||
813 | struct sway_view *view = child->view; | ||
814 | struct sway_container_state *cstate = &child->current; | ||
815 | struct border_colors *colors; | ||
816 | struct wlr_texture *title_texture; | ||
817 | struct wlr_texture *marks_texture; | ||
818 | bool urgent = view ? | ||
819 | view_is_urgent(view) : container_has_urgent_child(child); | ||
820 | |||
821 | if (urgent) { | ||
822 | colors = &config->border_colors.urgent; | ||
823 | title_texture = child->title_urgent; | ||
824 | marks_texture = child->marks_urgent; | ||
825 | } else if (cstate->focused || parent->focused) { | ||
826 | colors = &config->border_colors.focused; | ||
827 | title_texture = child->title_focused; | ||
828 | marks_texture = child->marks_focused; | ||
829 | } else if (child == parent->active_child) { | ||
830 | colors = &config->border_colors.focused_inactive; | ||
831 | title_texture = child->title_focused_inactive; | ||
832 | marks_texture = child->marks_focused_inactive; | ||
833 | } else { | ||
834 | colors = &config->border_colors.unfocused; | ||
835 | title_texture = child->title_unfocused; | ||
836 | marks_texture = child->marks_unfocused; | ||
837 | } | ||
838 | |||
839 | int y = parent->box.y + titlebar_height * i; | ||
840 | render_titlebar(output, damage, child, parent->box.x, y, | ||
841 | parent->box.width, colors, title_texture, marks_texture); | ||
842 | |||
843 | if (child == current) { | ||
844 | current_colors = colors; | ||
845 | } | ||
846 | } | ||
847 | |||
848 | // Render surface and left/right/bottom borders | ||
849 | if (current->view) { | ||
850 | render_view(output, damage, current, current_colors); | ||
851 | } else { | ||
852 | render_container(output, damage, current, | ||
853 | parent->focused || current->current.focused); | ||
854 | } | ||
855 | } | ||
856 | |||
857 | static void render_containers(struct sway_output *output, | ||
858 | pixman_region32_t *damage, struct parent_data *parent) { | ||
859 | if (config->hide_lone_tab && parent->children->length == 1) { | ||
860 | struct sway_container *child = parent->children->items[0]; | ||
861 | if (child->view) { | ||
862 | render_containers_linear(output,damage, parent); | ||
863 | return; | ||
864 | } | ||
865 | } | ||
866 | |||
867 | switch (parent->layout) { | ||
868 | case L_NONE: | ||
869 | case L_HORIZ: | ||
870 | case L_VERT: | ||
871 | render_containers_linear(output, damage, parent); | ||
872 | break; | ||
873 | case L_STACKED: | ||
874 | render_containers_stacked(output, damage, parent); | ||
875 | break; | ||
876 | case L_TABBED: | ||
877 | render_containers_tabbed(output, damage, parent); | ||
878 | break; | ||
879 | } | ||
880 | } | ||
881 | |||
882 | static void render_container(struct sway_output *output, | ||
883 | pixman_region32_t *damage, struct sway_container *con, bool focused) { | ||
884 | struct parent_data data = { | ||
885 | .layout = con->current.layout, | ||
886 | .box = { | ||
887 | .x = con->current.x, | ||
888 | .y = con->current.y, | ||
889 | .width = con->current.width, | ||
890 | .height = con->current.height, | ||
891 | }, | ||
892 | .children = con->current.children, | ||
893 | .focused = focused, | ||
894 | .active_child = con->current.focused_inactive_child, | ||
895 | }; | ||
896 | render_containers(output, damage, &data); | ||
897 | } | ||
898 | |||
899 | static void render_workspace(struct sway_output *output, | ||
900 | pixman_region32_t *damage, struct sway_workspace *ws, bool focused) { | ||
901 | struct parent_data data = { | ||
902 | .layout = ws->current.layout, | ||
903 | .box = { | ||
904 | .x = ws->current.x, | ||
905 | .y = ws->current.y, | ||
906 | .width = ws->current.width, | ||
907 | .height = ws->current.height, | ||
908 | }, | ||
909 | .children = ws->current.tiling, | ||
910 | .focused = focused, | ||
911 | .active_child = ws->current.focused_inactive_child, | ||
912 | }; | ||
913 | render_containers(output, damage, &data); | ||
914 | } | ||
915 | |||
916 | static void render_floating_container(struct sway_output *soutput, | ||
917 | pixman_region32_t *damage, struct sway_container *con) { | ||
918 | if (con->view) { | ||
919 | struct sway_view *view = con->view; | ||
920 | struct border_colors *colors; | ||
921 | struct wlr_texture *title_texture; | ||
922 | struct wlr_texture *marks_texture; | ||
923 | |||
924 | if (view_is_urgent(view)) { | ||
925 | colors = &config->border_colors.urgent; | ||
926 | title_texture = con->title_urgent; | ||
927 | marks_texture = con->marks_urgent; | ||
928 | } else if (con->current.focused) { | ||
929 | colors = &config->border_colors.focused; | ||
930 | title_texture = con->title_focused; | ||
931 | marks_texture = con->marks_focused; | ||
932 | } else { | ||
933 | colors = &config->border_colors.unfocused; | ||
934 | title_texture = con->title_unfocused; | ||
935 | marks_texture = con->marks_unfocused; | ||
936 | } | ||
937 | |||
938 | if (con->current.border == B_NORMAL) { | ||
939 | render_titlebar(soutput, damage, con, con->current.x, | ||
940 | con->current.y, con->current.width, colors, | ||
941 | title_texture, marks_texture); | ||
942 | } else if (con->current.border == B_PIXEL) { | ||
943 | render_top_border(soutput, damage, con, colors); | ||
944 | } | ||
945 | render_view(soutput, damage, con, colors); | ||
946 | } else { | ||
947 | render_container(soutput, damage, con, con->current.focused); | ||
948 | } | ||
949 | } | ||
950 | |||
951 | static void render_floating(struct sway_output *soutput, | ||
952 | pixman_region32_t *damage) { | ||
953 | for (int i = 0; i < root->outputs->length; ++i) { | ||
954 | struct sway_output *output = root->outputs->items[i]; | ||
955 | for (int j = 0; j < output->current.workspaces->length; ++j) { | ||
956 | struct sway_workspace *ws = output->current.workspaces->items[j]; | ||
957 | if (!workspace_is_visible(ws)) { | ||
958 | continue; | ||
959 | } | ||
960 | for (int k = 0; k < ws->current.floating->length; ++k) { | ||
961 | struct sway_container *floater = ws->current.floating->items[k]; | ||
962 | if (floater->fullscreen_mode != FULLSCREEN_NONE) { | ||
963 | continue; | ||
964 | } | ||
965 | render_floating_container(soutput, damage, floater); | ||
966 | } | ||
967 | } | ||
968 | } | ||
969 | } | ||
970 | |||
971 | static void render_seatops(struct sway_output *output, | ||
972 | pixman_region32_t *damage) { | ||
973 | struct sway_seat *seat; | ||
974 | wl_list_for_each(seat, &server.input->seats, link) { | ||
975 | seatop_render(seat, output, damage); | ||
976 | } | ||
977 | } | ||
978 | |||
979 | void output_render(struct sway_output *output, struct timespec *when, | ||
980 | pixman_region32_t *damage) { | ||
981 | struct wlr_output *wlr_output = output->wlr_output; | ||
982 | |||
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 | |||
990 | struct sway_workspace *workspace = output->current.active_workspace; | ||
991 | if (workspace == NULL) { | ||
992 | return; | ||
993 | } | ||
994 | |||
995 | struct sway_container *fullscreen_con = root->fullscreen_global; | ||
996 | if (!fullscreen_con) { | ||
997 | fullscreen_con = workspace->current.fullscreen; | ||
998 | } | ||
999 | |||
1000 | wlr_renderer_begin(renderer, wlr_output->width, wlr_output->height); | ||
1001 | |||
1002 | if (!pixman_region32_not_empty(damage)) { | ||
1003 | // Output isn't damaged but needs buffer swap | ||
1004 | goto renderer_end; | ||
1005 | } | ||
1006 | |||
1007 | if (debug.damage == DAMAGE_HIGHLIGHT) { | ||
1008 | wlr_renderer_clear(renderer, (float[]){1, 1, 0, 1}); | ||
1009 | } else if (debug.damage == DAMAGE_RERENDER) { | ||
1010 | int width, height; | ||
1011 | wlr_output_transformed_resolution(wlr_output, &width, &height); | ||
1012 | pixman_region32_union_rect(damage, damage, 0, 0, width, height); | ||
1013 | } | ||
1014 | |||
1015 | if (output_has_opaque_overlay_layer_surface(output)) { | ||
1016 | goto render_overlay; | ||
1017 | } | ||
1018 | |||
1019 | if (fullscreen_con) { | ||
1020 | float clear_color[] = {0.0f, 0.0f, 0.0f, 1.0f}; | ||
1021 | |||
1022 | int nrects; | ||
1023 | pixman_box32_t *rects = pixman_region32_rectangles(damage, &nrects); | ||
1024 | for (int i = 0; i < nrects; ++i) { | ||
1025 | scissor_output(wlr_output, &rects[i]); | ||
1026 | wlr_renderer_clear(renderer, clear_color); | ||
1027 | } | ||
1028 | |||
1029 | if (fullscreen_con->view) { | ||
1030 | if (!wl_list_empty(&fullscreen_con->view->saved_buffers)) { | ||
1031 | render_saved_view(fullscreen_con->view, output, damage, 1.0f); | ||
1032 | } else if (fullscreen_con->view->surface) { | ||
1033 | render_view_toplevels(fullscreen_con->view, | ||
1034 | output, damage, 1.0f); | ||
1035 | } | ||
1036 | } else { | ||
1037 | render_container(output, damage, fullscreen_con, | ||
1038 | fullscreen_con->current.focused); | ||
1039 | } | ||
1040 | |||
1041 | for (int i = 0; i < workspace->current.floating->length; ++i) { | ||
1042 | struct sway_container *floater = | ||
1043 | workspace->current.floating->items[i]; | ||
1044 | if (container_is_transient_for(floater, fullscreen_con)) { | ||
1045 | render_floating_container(output, damage, floater); | ||
1046 | } | ||
1047 | } | ||
1048 | #if HAVE_XWAYLAND | ||
1049 | render_unmanaged(output, damage, &root->xwayland_unmanaged); | ||
1050 | #endif | ||
1051 | } else { | ||
1052 | float clear_color[] = {0.25f, 0.25f, 0.25f, 1.0f}; | ||
1053 | |||
1054 | int nrects; | ||
1055 | pixman_box32_t *rects = pixman_region32_rectangles(damage, &nrects); | ||
1056 | for (int i = 0; i < nrects; ++i) { | ||
1057 | scissor_output(wlr_output, &rects[i]); | ||
1058 | wlr_renderer_clear(renderer, clear_color); | ||
1059 | } | ||
1060 | |||
1061 | render_layer_toplevel(output, damage, | ||
1062 | &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND]); | ||
1063 | render_layer_toplevel(output, damage, | ||
1064 | &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM]); | ||
1065 | |||
1066 | render_workspace(output, damage, workspace, workspace->current.focused); | ||
1067 | render_floating(output, damage); | ||
1068 | #if HAVE_XWAYLAND | ||
1069 | render_unmanaged(output, damage, &root->xwayland_unmanaged); | ||
1070 | #endif | ||
1071 | render_layer_toplevel(output, damage, | ||
1072 | &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]); | ||
1073 | |||
1074 | render_layer_popups(output, damage, | ||
1075 | &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND]); | ||
1076 | render_layer_popups(output, damage, | ||
1077 | &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM]); | ||
1078 | render_layer_popups(output, damage, | ||
1079 | &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]); | ||
1080 | } | ||
1081 | |||
1082 | render_seatops(output, damage); | ||
1083 | |||
1084 | struct sway_seat *seat = input_manager_current_seat(); | ||
1085 | struct sway_container *focus = seat_get_focused_container(seat); | ||
1086 | if (focus && focus->view) { | ||
1087 | render_view_popups(focus->view, output, damage, focus->alpha); | ||
1088 | } | ||
1089 | |||
1090 | render_overlay: | ||
1091 | render_layer_toplevel(output, damage, | ||
1092 | &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY]); | ||
1093 | render_layer_popups(output, damage, | ||
1094 | &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY]); | ||
1095 | render_drag_icons(output, damage, &root->drag_icons); | ||
1096 | |||
1097 | renderer_end: | ||
1098 | wlr_renderer_scissor(renderer, NULL); | ||
1099 | wlr_output_render_software_cursors(wlr_output, damage); | ||
1100 | wlr_renderer_end(renderer); | ||
1101 | |||
1102 | int width, height; | ||
1103 | wlr_output_transformed_resolution(wlr_output, &width, &height); | ||
1104 | |||
1105 | pixman_region32_t frame_damage; | ||
1106 | pixman_region32_init(&frame_damage); | ||
1107 | |||
1108 | enum wl_output_transform transform = | ||
1109 | wlr_output_transform_invert(wlr_output->transform); | ||
1110 | wlr_region_transform(&frame_damage, &output->damage->current, | ||
1111 | transform, width, height); | ||
1112 | |||
1113 | if (debug.damage == DAMAGE_HIGHLIGHT) { | ||
1114 | pixman_region32_union_rect(&frame_damage, &frame_damage, | ||
1115 | 0, 0, wlr_output->width, wlr_output->height); | ||
1116 | } | ||
1117 | |||
1118 | wlr_output_set_damage(wlr_output, &frame_damage); | ||
1119 | pixman_region32_fini(&frame_damage); | ||
1120 | |||
1121 | if (!wlr_output_commit(wlr_output)) { | ||
1122 | return; | ||
1123 | } | ||
1124 | output->last_frame = *when; | ||
1125 | } | ||
diff --git a/sway/desktop/surface.c b/sway/desktop/surface.c deleted file mode 100644 index 767b2045..00000000 --- a/sway/desktop/surface.c +++ /dev/null | |||
@@ -1,46 +0,0 @@ | |||
1 | #define _POSIX_C_SOURCE 200112L | ||
2 | #include <stdlib.h> | ||
3 | #include <time.h> | ||
4 | #include <wlr/types/wlr_surface.h> | ||
5 | #include "sway/server.h" | ||
6 | #include "sway/surface.h" | ||
7 | |||
8 | static void handle_destroy(struct wl_listener *listener, void *data) { | ||
9 | struct sway_surface *surface = wl_container_of(listener, surface, destroy); | ||
10 | |||
11 | surface->wlr_surface->data = NULL; | ||
12 | wl_list_remove(&surface->destroy.link); | ||
13 | |||
14 | if (surface->frame_done_timer) { | ||
15 | wl_event_source_remove(surface->frame_done_timer); | ||
16 | } | ||
17 | |||
18 | free(surface); | ||
19 | } | ||
20 | |||
21 | static int surface_frame_done_timer_handler(void *data) { | ||
22 | struct sway_surface *surface = data; | ||
23 | |||
24 | struct timespec now; | ||
25 | clock_gettime(CLOCK_MONOTONIC, &now); | ||
26 | wlr_surface_send_frame_done(surface->wlr_surface, &now); | ||
27 | |||
28 | return 0; | ||
29 | } | ||
30 | |||
31 | void handle_compositor_new_surface(struct wl_listener *listener, void *data) { | ||
32 | struct wlr_surface *wlr_surface = data; | ||
33 | |||
34 | struct sway_surface *surface = calloc(1, sizeof(struct sway_surface)); | ||
35 | surface->wlr_surface = wlr_surface; | ||
36 | wlr_surface->data = surface; | ||
37 | |||
38 | surface->destroy.notify = handle_destroy; | ||
39 | wl_signal_add(&wlr_surface->events.destroy, &surface->destroy); | ||
40 | |||
41 | surface->frame_done_timer = wl_event_loop_add_timer(server.wl_event_loop, | ||
42 | surface_frame_done_timer_handler, surface); | ||
43 | if (!surface->frame_done_timer) { | ||
44 | wl_resource_post_no_memory(wlr_surface->resource); | ||
45 | } | ||
46 | } | ||
diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c index eac38991..e464ff1a 100644 --- a/sway/desktop/transaction.c +++ b/sway/desktop/transaction.c | |||
@@ -1,11 +1,10 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <stdbool.h> | 1 | #include <stdbool.h> |
3 | #include <stdlib.h> | 2 | #include <stdlib.h> |
4 | #include <string.h> | 3 | #include <string.h> |
5 | #include <time.h> | 4 | #include <time.h> |
6 | #include <wlr/types/wlr_buffer.h> | 5 | #include <wlr/types/wlr_buffer.h> |
7 | #include "sway/config.h" | 6 | #include "sway/config.h" |
8 | #include "sway/desktop.h" | 7 | #include "sway/scene_descriptor.h" |
9 | #include "sway/desktop/idle_inhibit_v1.h" | 8 | #include "sway/desktop/idle_inhibit_v1.h" |
10 | #include "sway/desktop/transaction.h" | 9 | #include "sway/desktop/transaction.h" |
11 | #include "sway/input/cursor.h" | 10 | #include "sway/input/cursor.h" |
@@ -35,6 +34,8 @@ struct sway_transaction_instruction { | |||
35 | struct sway_container_state container_state; | 34 | struct sway_container_state container_state; |
36 | }; | 35 | }; |
37 | uint32_t serial; | 36 | uint32_t serial; |
37 | bool server_request; | ||
38 | bool waiting; | ||
38 | }; | 39 | }; |
39 | 40 | ||
40 | static struct sway_transaction *transaction_create(void) { | 41 | static struct sway_transaction *transaction_create(void) { |
@@ -86,7 +87,11 @@ static void transaction_destroy(struct sway_transaction *transaction) { | |||
86 | static void copy_output_state(struct sway_output *output, | 87 | static void copy_output_state(struct sway_output *output, |
87 | struct sway_transaction_instruction *instruction) { | 88 | struct sway_transaction_instruction *instruction) { |
88 | struct sway_output_state *state = &instruction->output_state; | 89 | struct sway_output_state *state = &instruction->output_state; |
89 | state->workspaces = create_list(); | 90 | if (state->workspaces) { |
91 | state->workspaces->length = 0; | ||
92 | } else { | ||
93 | state->workspaces = create_list(); | ||
94 | } | ||
90 | list_cat(state->workspaces, output->workspaces); | 95 | list_cat(state->workspaces, output->workspaces); |
91 | 96 | ||
92 | state->active_workspace = output_get_active_workspace(output); | 97 | state->active_workspace = output_get_active_workspace(output); |
@@ -104,8 +109,16 @@ static void copy_workspace_state(struct sway_workspace *ws, | |||
104 | state->layout = ws->layout; | 109 | state->layout = ws->layout; |
105 | 110 | ||
106 | state->output = ws->output; | 111 | state->output = ws->output; |
107 | state->floating = create_list(); | 112 | if (state->floating) { |
108 | state->tiling = create_list(); | 113 | state->floating->length = 0; |
114 | } else { | ||
115 | state->floating = create_list(); | ||
116 | } | ||
117 | if (state->tiling) { | ||
118 | state->tiling->length = 0; | ||
119 | } else { | ||
120 | state->tiling = create_list(); | ||
121 | } | ||
109 | list_cat(state->floating, ws->floating); | 122 | list_cat(state->floating, ws->floating); |
110 | list_cat(state->tiling, ws->tiling); | 123 | list_cat(state->tiling, ws->tiling); |
111 | 124 | ||
@@ -115,8 +128,8 @@ static void copy_workspace_state(struct sway_workspace *ws, | |||
115 | // Set focused_inactive_child to the direct tiling child | 128 | // Set focused_inactive_child to the direct tiling child |
116 | struct sway_container *focus = seat_get_focus_inactive_tiling(seat, ws); | 129 | struct sway_container *focus = seat_get_focus_inactive_tiling(seat, ws); |
117 | if (focus) { | 130 | if (focus) { |
118 | while (focus->parent) { | 131 | while (focus->pending.parent) { |
119 | focus = focus->parent; | 132 | focus = focus->pending.parent; |
120 | } | 133 | } |
121 | } | 134 | } |
122 | state->focused_inactive_child = focus; | 135 | state->focused_inactive_child = focus; |
@@ -126,28 +139,19 @@ static void copy_container_state(struct sway_container *container, | |||
126 | struct sway_transaction_instruction *instruction) { | 139 | struct sway_transaction_instruction *instruction) { |
127 | struct sway_container_state *state = &instruction->container_state; | 140 | struct sway_container_state *state = &instruction->container_state; |
128 | 141 | ||
129 | state->layout = container->layout; | 142 | if (state->children) { |
130 | state->x = container->x; | 143 | list_free(state->children); |
131 | state->y = container->y; | 144 | } |
132 | state->width = container->width; | 145 | |
133 | state->height = container->height; | 146 | 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 | 147 | ||
148 | if (!container->view) { | 148 | if (!container->view) { |
149 | // We store a copy of the child list to avoid having it mutated after | ||
150 | // we copy the state. | ||
149 | state->children = create_list(); | 151 | state->children = create_list(); |
150 | list_cat(state->children, container->children); | 152 | list_cat(state->children, container->pending.children); |
153 | } else { | ||
154 | state->children = NULL; | ||
151 | } | 155 | } |
152 | 156 | ||
153 | struct sway_seat *seat = input_manager_current_seat(); | 157 | struct sway_seat *seat = input_manager_current_seat(); |
@@ -161,14 +165,36 @@ static void copy_container_state(struct sway_container *container, | |||
161 | } | 165 | } |
162 | 166 | ||
163 | static void transaction_add_node(struct sway_transaction *transaction, | 167 | static void transaction_add_node(struct sway_transaction *transaction, |
164 | struct sway_node *node) { | 168 | struct sway_node *node, bool server_request) { |
165 | struct sway_transaction_instruction *instruction = | 169 | struct sway_transaction_instruction *instruction = NULL; |
166 | calloc(1, sizeof(struct sway_transaction_instruction)); | 170 | |
167 | if (!sway_assert(instruction, "Unable to allocate instruction")) { | 171 | // Check if we have an instruction for this node already, in which case we |
168 | return; | 172 | // update that instead of creating a new one. |
173 | if (node->ntxnrefs > 0) { | ||
174 | for (int idx = 0; idx < transaction->instructions->length; idx++) { | ||
175 | struct sway_transaction_instruction *other = | ||
176 | transaction->instructions->items[idx]; | ||
177 | if (other->node == node) { | ||
178 | instruction = other; | ||
179 | break; | ||
180 | } | ||
181 | } | ||
182 | } | ||
183 | |||
184 | if (!instruction) { | ||
185 | instruction = calloc(1, sizeof(struct sway_transaction_instruction)); | ||
186 | if (!sway_assert(instruction, "Unable to allocate instruction")) { | ||
187 | return; | ||
188 | } | ||
189 | instruction->transaction = transaction; | ||
190 | instruction->node = node; | ||
191 | instruction->server_request = server_request; | ||
192 | |||
193 | list_add(transaction->instructions, instruction); | ||
194 | node->ntxnrefs++; | ||
195 | } else if (server_request) { | ||
196 | instruction->server_request = true; | ||
169 | } | 197 | } |
170 | instruction->transaction = transaction; | ||
171 | instruction->node = node; | ||
172 | 198 | ||
173 | switch (node->type) { | 199 | switch (node->type) { |
174 | case N_ROOT: | 200 | case N_ROOT: |
@@ -183,46 +209,24 @@ static void transaction_add_node(struct sway_transaction *transaction, | |||
183 | copy_container_state(node->sway_container, instruction); | 209 | copy_container_state(node->sway_container, instruction); |
184 | break; | 210 | break; |
185 | } | 211 | } |
186 | |||
187 | list_add(transaction->instructions, instruction); | ||
188 | node->ntxnrefs++; | ||
189 | } | 212 | } |
190 | 213 | ||
191 | static void apply_output_state(struct sway_output *output, | 214 | static void apply_output_state(struct sway_output *output, |
192 | struct sway_output_state *state) { | 215 | struct sway_output_state *state) { |
193 | output_damage_whole(output); | ||
194 | list_free(output->current.workspaces); | 216 | list_free(output->current.workspaces); |
195 | memcpy(&output->current, state, sizeof(struct sway_output_state)); | 217 | memcpy(&output->current, state, sizeof(struct sway_output_state)); |
196 | output_damage_whole(output); | ||
197 | } | 218 | } |
198 | 219 | ||
199 | static void apply_workspace_state(struct sway_workspace *ws, | 220 | static void apply_workspace_state(struct sway_workspace *ws, |
200 | struct sway_workspace_state *state) { | 221 | struct sway_workspace_state *state) { |
201 | output_damage_whole(ws->current.output); | ||
202 | list_free(ws->current.floating); | 222 | list_free(ws->current.floating); |
203 | list_free(ws->current.tiling); | 223 | list_free(ws->current.tiling); |
204 | memcpy(&ws->current, state, sizeof(struct sway_workspace_state)); | 224 | memcpy(&ws->current, state, sizeof(struct sway_workspace_state)); |
205 | output_damage_whole(ws->current.output); | ||
206 | } | 225 | } |
207 | 226 | ||
208 | static void apply_container_state(struct sway_container *container, | 227 | static void apply_container_state(struct sway_container *container, |
209 | struct sway_container_state *state) { | 228 | struct sway_container_state *state) { |
210 | struct sway_view *view = container->view; | 229 | struct sway_view *view = container->view; |
211 | // Damage the old location | ||
212 | desktop_damage_whole_container(container); | ||
213 | if (view && !wl_list_empty(&view->saved_buffers)) { | ||
214 | struct sway_saved_buffer *saved_buf; | ||
215 | wl_list_for_each(saved_buf, &view->saved_buffers, link) { | ||
216 | struct wlr_box box = { | ||
217 | .x = container->current.content_x - view->saved_geometry.x + saved_buf->x, | ||
218 | .y = container->current.content_y - view->saved_geometry.y + saved_buf->y, | ||
219 | .width = saved_buf->width, | ||
220 | .height = saved_buf->height, | ||
221 | }; | ||
222 | desktop_damage_box(&box); | ||
223 | } | ||
224 | } | ||
225 | |||
226 | // There are separate children lists for each instruction state, the | 230 | // There are separate children lists for each instruction state, the |
227 | // container's current state and the container's pending state | 231 | // container's current state and the container's pending state |
228 | // (ie. con->children). The list itself needs to be freed here. | 232 | // (ie. con->children). The list itself needs to be freed here. |
@@ -232,48 +236,445 @@ static void apply_container_state(struct sway_container *container, | |||
232 | 236 | ||
233 | memcpy(&container->current, state, sizeof(struct sway_container_state)); | 237 | memcpy(&container->current, state, sizeof(struct sway_container_state)); |
234 | 238 | ||
235 | if (view && !wl_list_empty(&view->saved_buffers)) { | 239 | if (view) { |
236 | if (!container->node.destroying || container->node.ntxnrefs == 1) { | 240 | if (view->saved_surface_tree) { |
237 | view_remove_saved_buffer(view); | 241 | if (!container->node.destroying || container->node.ntxnrefs == 1) { |
242 | view_remove_saved_buffer(view); | ||
243 | } | ||
244 | } | ||
245 | |||
246 | // If the view hasn't responded to the configure, center it within | ||
247 | // the container. This is important for fullscreen views which | ||
248 | // refuse to resize to the size of the output. | ||
249 | if (view->surface) { | ||
250 | view_center_and_clip_surface(view); | ||
251 | } | ||
252 | } | ||
253 | } | ||
254 | |||
255 | static void arrange_title_bar(struct sway_container *con, | ||
256 | int x, int y, int width, int height) { | ||
257 | container_update(con); | ||
258 | |||
259 | bool has_title_bar = height > 0; | ||
260 | wlr_scene_node_set_enabled(&con->title_bar.tree->node, has_title_bar); | ||
261 | if (!has_title_bar) { | ||
262 | return; | ||
263 | } | ||
264 | |||
265 | wlr_scene_node_set_position(&con->title_bar.tree->node, x, y); | ||
266 | |||
267 | con->title_width = width; | ||
268 | container_arrange_title_bar(con); | ||
269 | } | ||
270 | |||
271 | static void disable_container(struct sway_container *con) { | ||
272 | if (con->view) { | ||
273 | wlr_scene_node_reparent(&con->view->scene_tree->node, con->content_tree); | ||
274 | } else { | ||
275 | for (int i = 0; i < con->current.children->length; i++) { | ||
276 | struct sway_container *child = con->current.children->items[i]; | ||
277 | |||
278 | wlr_scene_node_reparent(&child->scene_tree->node, con->content_tree); | ||
279 | |||
280 | disable_container(child); | ||
281 | } | ||
282 | } | ||
283 | } | ||
284 | |||
285 | static void arrange_container(struct sway_container *con, | ||
286 | int width, int height, bool title_bar, int gaps); | ||
287 | |||
288 | static void arrange_children(enum sway_container_layout layout, list_t *children, | ||
289 | struct sway_container *active, struct wlr_scene_tree *content, | ||
290 | int width, int height, int gaps) { | ||
291 | int title_bar_height = container_titlebar_height(); | ||
292 | |||
293 | if (layout == L_TABBED) { | ||
294 | struct sway_container *first = children->length == 1 ? | ||
295 | ((struct sway_container *)children->items[0]) : NULL; | ||
296 | if (config->hide_lone_tab && first && first->view && | ||
297 | first->current.border != B_NORMAL) { | ||
298 | title_bar_height = 0; | ||
299 | } | ||
300 | |||
301 | double w = (double) width / children->length; | ||
302 | int title_offset = 0; | ||
303 | for (int i = 0; i < children->length; i++) { | ||
304 | struct sway_container *child = children->items[i]; | ||
305 | bool activated = child == active; | ||
306 | int next_title_offset = round(w * i + w); | ||
307 | |||
308 | arrange_title_bar(child, title_offset, -title_bar_height, | ||
309 | next_title_offset - title_offset, title_bar_height); | ||
310 | wlr_scene_node_set_enabled(&child->border.tree->node, activated); | ||
311 | wlr_scene_node_set_position(&child->scene_tree->node, 0, title_bar_height); | ||
312 | wlr_scene_node_reparent(&child->scene_tree->node, content); | ||
313 | |||
314 | if (activated) { | ||
315 | arrange_container(child, width, height - title_bar_height, | ||
316 | false, 0); | ||
317 | } else { | ||
318 | disable_container(child); | ||
319 | } | ||
320 | |||
321 | title_offset = next_title_offset; | ||
238 | } | 322 | } |
323 | } else if (layout == L_STACKED) { | ||
324 | struct sway_container *first = children->length == 1 ? | ||
325 | ((struct sway_container *)children->items[0]) : NULL; | ||
326 | if (config->hide_lone_tab && first && first->view && | ||
327 | first->current.border != B_NORMAL) { | ||
328 | title_bar_height = 0; | ||
329 | } | ||
330 | |||
331 | int title_height = title_bar_height * children->length; | ||
332 | |||
333 | int y = 0; | ||
334 | for (int i = 0; i < children->length; i++) { | ||
335 | struct sway_container *child = children->items[i]; | ||
336 | bool activated = child == active; | ||
337 | |||
338 | arrange_title_bar(child, 0, y - title_height, width, title_bar_height); | ||
339 | wlr_scene_node_set_enabled(&child->border.tree->node, activated); | ||
340 | wlr_scene_node_set_position(&child->scene_tree->node, 0, title_height); | ||
341 | wlr_scene_node_reparent(&child->scene_tree->node, content); | ||
342 | |||
343 | if (activated) { | ||
344 | arrange_container(child, width, height - title_height, | ||
345 | false, 0); | ||
346 | } else { | ||
347 | disable_container(child); | ||
348 | } | ||
349 | |||
350 | y += title_bar_height; | ||
351 | } | ||
352 | } else if (layout == L_VERT) { | ||
353 | int off = 0; | ||
354 | for (int i = 0; i < children->length; i++) { | ||
355 | struct sway_container *child = children->items[i]; | ||
356 | int cheight = child->current.height; | ||
357 | |||
358 | wlr_scene_node_set_enabled(&child->border.tree->node, true); | ||
359 | wlr_scene_node_set_position(&child->scene_tree->node, 0, off); | ||
360 | wlr_scene_node_reparent(&child->scene_tree->node, content); | ||
361 | arrange_container(child, width, cheight, true, gaps); | ||
362 | off += cheight + gaps; | ||
363 | } | ||
364 | } else if (layout == L_HORIZ) { | ||
365 | int off = 0; | ||
366 | for (int i = 0; i < children->length; i++) { | ||
367 | struct sway_container *child = children->items[i]; | ||
368 | int cwidth = child->current.width; | ||
369 | |||
370 | wlr_scene_node_set_enabled(&child->border.tree->node, true); | ||
371 | wlr_scene_node_set_position(&child->scene_tree->node, off, 0); | ||
372 | wlr_scene_node_reparent(&child->scene_tree->node, content); | ||
373 | arrange_container(child, cwidth, height, true, gaps); | ||
374 | off += cwidth + gaps; | ||
375 | } | ||
376 | } else { | ||
377 | sway_assert(false, "unreachable"); | ||
378 | } | ||
379 | } | ||
380 | |||
381 | static void arrange_container(struct sway_container *con, | ||
382 | int width, int height, bool title_bar, int gaps) { | ||
383 | // this container might have previously been in the scratchpad, | ||
384 | // make sure it's enabled for viewing | ||
385 | wlr_scene_node_set_enabled(&con->scene_tree->node, true); | ||
386 | |||
387 | if (con->output_handler) { | ||
388 | wlr_scene_buffer_set_dest_size(con->output_handler, width, height); | ||
239 | } | 389 | } |
240 | 390 | ||
241 | // Damage the new location | 391 | if (con->view) { |
242 | desktop_damage_whole_container(container); | 392 | int border_top = container_titlebar_height(); |
243 | if (view && view->surface) { | 393 | int border_width = con->current.border_thickness; |
244 | struct wlr_surface *surface = view->surface; | 394 | |
245 | struct wlr_box box = { | 395 | if (title_bar && con->current.border != B_NORMAL) { |
246 | .x = container->current.content_x - view->geometry.x, | 396 | wlr_scene_node_set_enabled(&con->title_bar.tree->node, false); |
247 | .y = container->current.content_y - view->geometry.y, | 397 | wlr_scene_node_set_enabled(&con->border.top->node, true); |
248 | .width = surface->current.width, | ||
249 | .height = surface->current.height, | ||
250 | }; | ||
251 | desktop_damage_box(&box); | ||
252 | } | ||
253 | |||
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 { | 398 | } else { |
262 | container->surface_x = container->current.content_x; | 399 | wlr_scene_node_set_enabled(&con->border.top->node, false); |
263 | } | 400 | } |
264 | if (view->geometry.height < container->current.content_height) { | 401 | |
265 | container->surface_y = container->current.content_y + | 402 | if (con->current.border == B_NORMAL) { |
266 | (container->current.content_height - view->geometry.height) / 2; | 403 | if (title_bar) { |
404 | arrange_title_bar(con, 0, 0, width, border_top); | ||
405 | } else { | ||
406 | border_top = 0; | ||
407 | // should be handled by the parent container | ||
408 | } | ||
409 | } else if (con->current.border == B_PIXEL) { | ||
410 | container_update(con); | ||
411 | border_top = title_bar && con->current.border_top ? border_width : 0; | ||
412 | } else if (con->current.border == B_NONE) { | ||
413 | container_update(con); | ||
414 | border_top = 0; | ||
415 | border_width = 0; | ||
416 | } else if (con->current.border == B_CSD) { | ||
417 | border_top = 0; | ||
418 | border_width = 0; | ||
419 | } else { | ||
420 | sway_assert(false, "unreachable"); | ||
421 | } | ||
422 | |||
423 | int border_bottom = con->current.border_bottom ? border_width : 0; | ||
424 | int border_left = con->current.border_left ? border_width : 0; | ||
425 | int border_right = con->current.border_right ? border_width : 0; | ||
426 | |||
427 | wlr_scene_rect_set_size(con->border.top, width, border_top); | ||
428 | wlr_scene_rect_set_size(con->border.bottom, width, border_bottom); | ||
429 | wlr_scene_rect_set_size(con->border.left, | ||
430 | border_left, height - border_top - border_bottom); | ||
431 | wlr_scene_rect_set_size(con->border.right, | ||
432 | border_right, height - border_top - border_bottom); | ||
433 | |||
434 | wlr_scene_node_set_position(&con->border.top->node, 0, 0); | ||
435 | wlr_scene_node_set_position(&con->border.bottom->node, | ||
436 | 0, height - border_bottom); | ||
437 | wlr_scene_node_set_position(&con->border.left->node, | ||
438 | 0, border_top); | ||
439 | wlr_scene_node_set_position(&con->border.right->node, | ||
440 | width - border_right, border_top); | ||
441 | |||
442 | // make sure to reparent, it's possible that the client just came out of | ||
443 | // fullscreen mode where the parent of the surface is not the container | ||
444 | wlr_scene_node_reparent(&con->view->scene_tree->node, con->content_tree); | ||
445 | wlr_scene_node_set_position(&con->view->scene_tree->node, | ||
446 | border_left, border_top); | ||
447 | } else { | ||
448 | // make sure to disable the title bar if the parent is not managing it | ||
449 | if (title_bar) { | ||
450 | wlr_scene_node_set_enabled(&con->title_bar.tree->node, false); | ||
451 | } | ||
452 | |||
453 | arrange_children(con->current.layout, con->current.children, | ||
454 | con->current.focused_inactive_child, con->content_tree, | ||
455 | width, height, gaps); | ||
456 | } | ||
457 | } | ||
458 | |||
459 | static int container_get_gaps(struct sway_container *con) { | ||
460 | struct sway_workspace *ws = con->current.workspace; | ||
461 | struct sway_container *temp = con; | ||
462 | while (temp) { | ||
463 | enum sway_container_layout layout; | ||
464 | if (temp->current.parent) { | ||
465 | layout = temp->current.parent->current.layout; | ||
267 | } else { | 466 | } else { |
268 | container->surface_y = container->current.content_y; | 467 | layout = ws->current.layout; |
269 | } | 468 | } |
469 | if (layout == L_TABBED || layout == L_STACKED) { | ||
470 | return 0; | ||
471 | } | ||
472 | temp = temp->pending.parent; | ||
270 | } | 473 | } |
474 | return ws->gaps_inner; | ||
475 | } | ||
476 | |||
477 | static void arrange_fullscreen(struct wlr_scene_tree *tree, | ||
478 | struct sway_container *fs, struct sway_workspace *ws, | ||
479 | int width, int height) { | ||
480 | struct wlr_scene_node *fs_node; | ||
481 | if (fs->view) { | ||
482 | fs_node = &fs->view->scene_tree->node; | ||
483 | |||
484 | // if we only care about the view, disable any decorations | ||
485 | wlr_scene_node_set_enabled(&fs->scene_tree->node, false); | ||
486 | } else { | ||
487 | fs_node = &fs->scene_tree->node; | ||
488 | arrange_container(fs, width, height, true, container_get_gaps(fs)); | ||
489 | } | ||
490 | |||
491 | wlr_scene_node_reparent(fs_node, tree); | ||
492 | wlr_scene_node_lower_to_bottom(fs_node); | ||
493 | wlr_scene_node_set_position(fs_node, 0, 0); | ||
494 | } | ||
495 | |||
496 | static void arrange_workspace_floating(struct sway_workspace *ws) { | ||
497 | for (int i = 0; i < ws->current.floating->length; i++) { | ||
498 | struct sway_container *floater = ws->current.floating->items[i]; | ||
499 | struct wlr_scene_tree *layer = root->layers.floating; | ||
271 | 500 | ||
272 | if (!container->node.destroying) { | 501 | if (floater->current.fullscreen_mode != FULLSCREEN_NONE) { |
273 | container_discover_outputs(container); | 502 | continue; |
503 | } | ||
504 | |||
505 | if (root->fullscreen_global) { | ||
506 | if (container_is_transient_for(floater, root->fullscreen_global)) { | ||
507 | layer = root->layers.fullscreen_global; | ||
508 | } | ||
509 | } else { | ||
510 | for (int i = 0; i < root->outputs->length; i++) { | ||
511 | struct sway_output *output = root->outputs->items[i]; | ||
512 | struct sway_workspace *active = output->current.active_workspace; | ||
513 | |||
514 | if (active && active->fullscreen && | ||
515 | container_is_transient_for(floater, active->fullscreen)) { | ||
516 | layer = root->layers.fullscreen; | ||
517 | } | ||
518 | } | ||
519 | } | ||
520 | |||
521 | wlr_scene_node_reparent(&floater->scene_tree->node, layer); | ||
522 | wlr_scene_node_set_position(&floater->scene_tree->node, | ||
523 | floater->current.x, floater->current.y); | ||
524 | wlr_scene_node_set_enabled(&floater->scene_tree->node, true); | ||
525 | |||
526 | arrange_container(floater, floater->current.width, floater->current.height, | ||
527 | true, ws->gaps_inner); | ||
274 | } | 528 | } |
275 | } | 529 | } |
276 | 530 | ||
531 | static void arrange_workspace_tiling(struct sway_workspace *ws, | ||
532 | int width, int height) { | ||
533 | arrange_children(ws->current.layout, ws->current.tiling, | ||
534 | ws->current.focused_inactive_child, ws->layers.tiling, | ||
535 | width, height, ws->gaps_inner); | ||
536 | } | ||
537 | |||
538 | static void disable_workspace(struct sway_workspace *ws) { | ||
539 | // if any containers were just moved to a disabled workspace it will | ||
540 | // have the parent of the old workspace. Move the workspace so that it won't | ||
541 | // be shown. | ||
542 | for (int i = 0; i < ws->current.tiling->length; i++) { | ||
543 | struct sway_container *child = ws->current.tiling->items[i]; | ||
544 | |||
545 | wlr_scene_node_reparent(&child->scene_tree->node, ws->layers.tiling); | ||
546 | disable_container(child); | ||
547 | } | ||
548 | |||
549 | for (int i = 0; i < ws->current.floating->length; i++) { | ||
550 | struct sway_container *floater = ws->current.floating->items[i]; | ||
551 | wlr_scene_node_reparent(&floater->scene_tree->node, root->layers.floating); | ||
552 | disable_container(floater); | ||
553 | wlr_scene_node_set_enabled(&floater->scene_tree->node, false); | ||
554 | } | ||
555 | } | ||
556 | |||
557 | static void arrange_output(struct sway_output *output, int width, int height) { | ||
558 | for (int i = 0; i < output->current.workspaces->length; i++) { | ||
559 | struct sway_workspace *child = output->current.workspaces->items[i]; | ||
560 | |||
561 | bool activated = output->current.active_workspace == child; | ||
562 | |||
563 | wlr_scene_node_reparent(&child->layers.tiling->node, output->layers.tiling); | ||
564 | wlr_scene_node_reparent(&child->layers.fullscreen->node, output->layers.fullscreen); | ||
565 | |||
566 | for (int i = 0; i < child->current.floating->length; i++) { | ||
567 | struct sway_container *floater = child->current.floating->items[i]; | ||
568 | wlr_scene_node_reparent(&floater->scene_tree->node, root->layers.floating); | ||
569 | wlr_scene_node_set_enabled(&floater->scene_tree->node, activated); | ||
570 | } | ||
571 | |||
572 | if (activated) { | ||
573 | struct sway_container *fs = child->current.fullscreen; | ||
574 | wlr_scene_node_set_enabled(&child->layers.tiling->node, !fs); | ||
575 | wlr_scene_node_set_enabled(&child->layers.fullscreen->node, fs); | ||
576 | |||
577 | arrange_workspace_floating(child); | ||
578 | |||
579 | wlr_scene_node_set_enabled(&output->layers.shell_background->node, !fs); | ||
580 | wlr_scene_node_set_enabled(&output->layers.shell_bottom->node, !fs); | ||
581 | wlr_scene_node_set_enabled(&output->layers.fullscreen->node, fs); | ||
582 | |||
583 | if (fs) { | ||
584 | wlr_scene_rect_set_size(output->fullscreen_background, width, height); | ||
585 | |||
586 | arrange_fullscreen(child->layers.fullscreen, fs, child, | ||
587 | width, height); | ||
588 | } else { | ||
589 | struct wlr_box *area = &output->usable_area; | ||
590 | struct side_gaps *gaps = &child->current_gaps; | ||
591 | |||
592 | wlr_scene_node_set_position(&child->layers.tiling->node, | ||
593 | gaps->left + area->x, gaps->top + area->y); | ||
594 | |||
595 | arrange_workspace_tiling(child, | ||
596 | area->width - gaps->left - gaps->right, | ||
597 | area->height - gaps->top - gaps->bottom); | ||
598 | } | ||
599 | } else { | ||
600 | wlr_scene_node_set_enabled(&child->layers.tiling->node, false); | ||
601 | wlr_scene_node_set_enabled(&child->layers.fullscreen->node, false); | ||
602 | |||
603 | disable_workspace(child); | ||
604 | } | ||
605 | } | ||
606 | } | ||
607 | |||
608 | void arrange_popups(struct wlr_scene_tree *popups) { | ||
609 | struct wlr_scene_node *node; | ||
610 | wl_list_for_each(node, &popups->children, link) { | ||
611 | struct sway_popup_desc *popup = scene_descriptor_try_get(node, | ||
612 | SWAY_SCENE_DESC_POPUP); | ||
613 | |||
614 | int lx, ly; | ||
615 | wlr_scene_node_coords(popup->relative, &lx, &ly); | ||
616 | wlr_scene_node_set_position(node, lx, ly); | ||
617 | } | ||
618 | } | ||
619 | |||
620 | static void arrange_root(struct sway_root *root) { | ||
621 | struct sway_container *fs = root->fullscreen_global; | ||
622 | |||
623 | wlr_scene_node_set_enabled(&root->layers.shell_background->node, !fs); | ||
624 | wlr_scene_node_set_enabled(&root->layers.shell_bottom->node, !fs); | ||
625 | wlr_scene_node_set_enabled(&root->layers.tiling->node, !fs); | ||
626 | wlr_scene_node_set_enabled(&root->layers.floating->node, !fs); | ||
627 | wlr_scene_node_set_enabled(&root->layers.shell_top->node, !fs); | ||
628 | wlr_scene_node_set_enabled(&root->layers.fullscreen->node, !fs); | ||
629 | |||
630 | // hide all contents in the scratchpad | ||
631 | for (int i = 0; i < root->scratchpad->length; i++) { | ||
632 | struct sway_container *con = root->scratchpad->items[i]; | ||
633 | |||
634 | wlr_scene_node_set_enabled(&con->scene_tree->node, false); | ||
635 | } | ||
636 | |||
637 | if (fs) { | ||
638 | for (int i = 0; i < root->outputs->length; i++) { | ||
639 | struct sway_output *output = root->outputs->items[i]; | ||
640 | struct sway_workspace *ws = output->current.active_workspace; | ||
641 | |||
642 | if (ws) { | ||
643 | arrange_workspace_floating(ws); | ||
644 | } | ||
645 | } | ||
646 | |||
647 | arrange_fullscreen(root->layers.fullscreen_global, fs, NULL, | ||
648 | root->width, root->height); | ||
649 | } else { | ||
650 | for (int i = 0; i < root->outputs->length; i++) { | ||
651 | struct sway_output *output = root->outputs->items[i]; | ||
652 | |||
653 | wlr_scene_output_set_position(output->scene_output, output->lx, output->ly); | ||
654 | |||
655 | wlr_scene_node_reparent(&output->layers.shell_background->node, root->layers.shell_background); | ||
656 | wlr_scene_node_reparent(&output->layers.shell_bottom->node, root->layers.shell_bottom); | ||
657 | wlr_scene_node_reparent(&output->layers.tiling->node, root->layers.tiling); | ||
658 | wlr_scene_node_reparent(&output->layers.shell_top->node, root->layers.shell_top); | ||
659 | wlr_scene_node_reparent(&output->layers.shell_overlay->node, root->layers.shell_overlay); | ||
660 | wlr_scene_node_reparent(&output->layers.fullscreen->node, root->layers.fullscreen); | ||
661 | wlr_scene_node_reparent(&output->layers.session_lock->node, root->layers.session_lock); | ||
662 | |||
663 | wlr_scene_node_set_position(&output->layers.shell_background->node, output->lx, output->ly); | ||
664 | wlr_scene_node_set_position(&output->layers.shell_bottom->node, output->lx, output->ly); | ||
665 | wlr_scene_node_set_position(&output->layers.tiling->node, output->lx, output->ly); | ||
666 | wlr_scene_node_set_position(&output->layers.fullscreen->node, output->lx, output->ly); | ||
667 | wlr_scene_node_set_position(&output->layers.shell_top->node, output->lx, output->ly); | ||
668 | wlr_scene_node_set_position(&output->layers.shell_overlay->node, output->lx, output->ly); | ||
669 | wlr_scene_node_set_position(&output->layers.session_lock->node, output->lx, output->ly); | ||
670 | |||
671 | arrange_output(output, output->width, output->height); | ||
672 | } | ||
673 | } | ||
674 | |||
675 | arrange_popups(root->layers.popup); | ||
676 | } | ||
677 | |||
277 | /** | 678 | /** |
278 | * Apply a transaction to the "current" state of the tree. | 679 | * Apply a transaction to the "current" state of the tree. |
279 | */ | 680 | */ |
@@ -313,74 +714,29 @@ static void transaction_apply(struct sway_transaction *transaction) { | |||
313 | 714 | ||
314 | node->instruction = NULL; | 715 | node->instruction = NULL; |
315 | } | 716 | } |
316 | |||
317 | cursor_rebase_all(); | ||
318 | } | 717 | } |
319 | 718 | ||
320 | static void transaction_commit(struct sway_transaction *transaction); | 719 | static void transaction_commit_pending(void); |
321 | 720 | ||
322 | // Return true if both transactions operate on the same nodes | 721 | static void transaction_progress(void) { |
323 | static bool transaction_same_nodes(struct sway_transaction *a, | 722 | 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; | 723 | return; |
341 | } | 724 | } |
342 | // Only the first transaction in the queue is committed, so that's the one | 725 | 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; | 726 | return; |
347 | } | 727 | } |
348 | transaction_apply(transaction); | 728 | transaction_apply(server.queued_transaction); |
349 | transaction_destroy(transaction); | 729 | arrange_root(root); |
350 | list_del(server.transactions, 0); | 730 | cursor_rebase_all(); |
731 | transaction_destroy(server.queued_transaction); | ||
732 | server.queued_transaction = NULL; | ||
351 | 733 | ||
352 | if (server.transactions->length == 0) { | 734 | if (!server.pending_transaction) { |
353 | // The transaction queue is empty, so we're done. | 735 | sway_idle_inhibit_v1_check_active(); |
354 | sway_idle_inhibit_v1_check_active(server.idle_inhibit_manager_v1); | ||
355 | return; | 736 | return; |
356 | } | 737 | } |
357 | 738 | ||
358 | // If there's a bunch of consecutive transactions which all apply to the | 739 | 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 | } | 740 | } |
385 | 741 | ||
386 | static int handle_timeout(void *data) { | 742 | static int handle_timeout(void *data) { |
@@ -388,7 +744,7 @@ static int handle_timeout(void *data) { | |||
388 | sway_log(SWAY_DEBUG, "Transaction %p timed out (%zi waiting)", | 744 | sway_log(SWAY_DEBUG, "Transaction %p timed out (%zi waiting)", |
389 | transaction, transaction->num_waiting); | 745 | transaction, transaction->num_waiting); |
390 | transaction->num_waiting = 0; | 746 | transaction->num_waiting = 0; |
391 | transaction_progress_queue(); | 747 | transaction_progress(); |
392 | return 0; | 748 | return 0; |
393 | } | 749 | } |
394 | 750 | ||
@@ -400,9 +756,12 @@ static bool should_configure(struct sway_node *node, | |||
400 | if (node->destroying) { | 756 | if (node->destroying) { |
401 | return false; | 757 | return false; |
402 | } | 758 | } |
759 | if (!instruction->server_request) { | ||
760 | return false; | ||
761 | } | ||
403 | struct sway_container_state *cstate = &node->sway_container->current; | 762 | struct sway_container_state *cstate = &node->sway_container->current; |
404 | struct sway_container_state *istate = &instruction->container_state; | 763 | struct sway_container_state *istate = &instruction->container_state; |
405 | #if HAVE_XWAYLAND | 764 | #if WLR_HAS_XWAYLAND |
406 | // Xwayland views are position-aware and need to be reconfigured | 765 | // Xwayland views are position-aware and need to be reconfigured |
407 | // when their position changes. | 766 | // when their position changes. |
408 | if (node->sway_container->view->type == SWAY_VIEW_XWAYLAND) { | 767 | if (node->sway_container->view->type == SWAY_VIEW_XWAYLAND) { |
@@ -431,28 +790,24 @@ static void transaction_commit(struct sway_transaction *transaction) { | |||
431 | struct sway_transaction_instruction *instruction = | 790 | struct sway_transaction_instruction *instruction = |
432 | transaction->instructions->items[i]; | 791 | transaction->instructions->items[i]; |
433 | struct sway_node *node = instruction->node; | 792 | struct sway_node *node = instruction->node; |
793 | bool hidden = node_is_view(node) && !node->destroying && | ||
794 | !view_is_visible(node->sway_container->view); | ||
434 | if (should_configure(node, instruction)) { | 795 | if (should_configure(node, instruction)) { |
435 | instruction->serial = view_configure(node->sway_container->view, | 796 | instruction->serial = view_configure(node->sway_container->view, |
436 | instruction->container_state.content_x, | 797 | instruction->container_state.content_x, |
437 | instruction->container_state.content_y, | 798 | instruction->container_state.content_y, |
438 | instruction->container_state.content_width, | 799 | instruction->container_state.content_width, |
439 | instruction->container_state.content_height); | 800 | instruction->container_state.content_height); |
440 | ++transaction->num_waiting; | 801 | if (!hidden) { |
441 | 802 | instruction->waiting = true; | |
442 | // From here on we are rendering a saved buffer of the view, which | 803 | ++transaction->num_waiting; |
443 | // means we can send a frame done event to make the client redraw it | 804 | } |
444 | // as soon as possible. Additionally, this is required if a view is | 805 | |
445 | // mapping and its default geometry doesn't intersect an output. | 806 | view_send_frame_done(node->sway_container->view); |
446 | struct timespec now; | ||
447 | clock_gettime(CLOCK_MONOTONIC, &now); | ||
448 | wlr_surface_send_frame_done( | ||
449 | node->sway_container->view->surface, &now); | ||
450 | } | 807 | } |
451 | if (node_is_view(node) && wl_list_empty(&node->sway_container->view->saved_buffers)) { | 808 | if (!hidden && node_is_view(node) && |
809 | !node->sway_container->view->saved_surface_tree) { | ||
452 | view_save_buffer(node->sway_container->view); | 810 | view_save_buffer(node->sway_container->view); |
453 | memcpy(&node->sway_container->view->saved_geometry, | ||
454 | &node->sway_container->view->geometry, | ||
455 | sizeof(struct wlr_box)); | ||
456 | } | 811 | } |
457 | node->instruction = instruction; | 812 | node->instruction = instruction; |
458 | } | 813 | } |
@@ -483,6 +838,17 @@ static void transaction_commit(struct sway_transaction *transaction) { | |||
483 | } | 838 | } |
484 | } | 839 | } |
485 | 840 | ||
841 | static void transaction_commit_pending(void) { | ||
842 | if (server.queued_transaction) { | ||
843 | return; | ||
844 | } | ||
845 | struct sway_transaction *transaction = server.pending_transaction; | ||
846 | server.pending_transaction = NULL; | ||
847 | server.queued_transaction = transaction; | ||
848 | transaction_commit(transaction); | ||
849 | transaction_progress(); | ||
850 | } | ||
851 | |||
486 | static void set_instruction_ready( | 852 | static void set_instruction_ready( |
487 | struct sway_transaction_instruction *instruction) { | 853 | struct sway_transaction_instruction *instruction) { |
488 | struct sway_transaction *transaction = instruction->transaction; | 854 | struct sway_transaction *transaction = instruction->transaction; |
@@ -501,25 +867,28 @@ static void set_instruction_ready( | |||
501 | } | 867 | } |
502 | 868 | ||
503 | // If the transaction has timed out then its num_waiting will be 0 already. | 869 | // If the transaction has timed out then its num_waiting will be 0 already. |
504 | if (transaction->num_waiting > 0 && --transaction->num_waiting == 0) { | 870 | if (instruction->waiting && transaction->num_waiting > 0 && |
871 | --transaction->num_waiting == 0) { | ||
505 | sway_log(SWAY_DEBUG, "Transaction %p is ready", transaction); | 872 | sway_log(SWAY_DEBUG, "Transaction %p is ready", transaction); |
506 | wl_event_source_timer_update(transaction->timer, 0); | 873 | wl_event_source_timer_update(transaction->timer, 0); |
507 | } | 874 | } |
508 | 875 | ||
509 | instruction->node->instruction = NULL; | 876 | instruction->node->instruction = NULL; |
510 | transaction_progress_queue(); | 877 | transaction_progress(); |
511 | } | 878 | } |
512 | 879 | ||
513 | void transaction_notify_view_ready_by_serial(struct sway_view *view, | 880 | bool transaction_notify_view_ready_by_serial(struct sway_view *view, |
514 | uint32_t serial) { | 881 | uint32_t serial) { |
515 | struct sway_transaction_instruction *instruction = | 882 | struct sway_transaction_instruction *instruction = |
516 | view->container->node.instruction; | 883 | view->container->node.instruction; |
517 | if (instruction != NULL && instruction->serial == serial) { | 884 | if (instruction != NULL && instruction->serial == serial) { |
518 | set_instruction_ready(instruction); | 885 | set_instruction_ready(instruction); |
886 | return true; | ||
519 | } | 887 | } |
888 | return false; | ||
520 | } | 889 | } |
521 | 890 | ||
522 | void transaction_notify_view_ready_by_geometry(struct sway_view *view, | 891 | bool transaction_notify_view_ready_by_geometry(struct sway_view *view, |
523 | double x, double y, int width, int height) { | 892 | double x, double y, int width, int height) { |
524 | struct sway_transaction_instruction *instruction = | 893 | struct sway_transaction_instruction *instruction = |
525 | view->container->node.instruction; | 894 | view->container->node.instruction; |
@@ -529,39 +898,37 @@ void transaction_notify_view_ready_by_geometry(struct sway_view *view, | |||
529 | instruction->container_state.content_width == width && | 898 | instruction->container_state.content_width == width && |
530 | instruction->container_state.content_height == height) { | 899 | instruction->container_state.content_height == height) { |
531 | set_instruction_ready(instruction); | 900 | set_instruction_ready(instruction); |
901 | return true; | ||
532 | } | 902 | } |
903 | return false; | ||
533 | } | 904 | } |
534 | 905 | ||
535 | void transaction_notify_view_ready_immediately(struct sway_view *view) { | 906 | 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) { | 907 | if (!server.dirty_nodes->length) { |
545 | return; | 908 | return; |
546 | } | 909 | } |
547 | struct sway_transaction *transaction = transaction_create(); | 910 | |
548 | if (!transaction) { | 911 | if (!server.pending_transaction) { |
549 | return; | 912 | server.pending_transaction = transaction_create(); |
913 | if (!server.pending_transaction) { | ||
914 | return; | ||
915 | } | ||
550 | } | 916 | } |
917 | |||
551 | for (int i = 0; i < server.dirty_nodes->length; ++i) { | 918 | for (int i = 0; i < server.dirty_nodes->length; ++i) { |
552 | struct sway_node *node = server.dirty_nodes->items[i]; | 919 | struct sway_node *node = server.dirty_nodes->items[i]; |
553 | transaction_add_node(transaction, node); | 920 | transaction_add_node(server.pending_transaction, node, server_request); |
554 | node->dirty = false; | 921 | node->dirty = false; |
555 | } | 922 | } |
556 | server.dirty_nodes->length = 0; | 923 | server.dirty_nodes->length = 0; |
557 | 924 | ||
558 | list_add(server.transactions, transaction); | 925 | transaction_commit_pending(); |
926 | } | ||
559 | 927 | ||
560 | // We only commit the first transaction added to the queue. | 928 | void transaction_commit_dirty(void) { |
561 | if (server.transactions->length == 1) { | 929 | _transaction_commit_dirty(true); |
562 | transaction_commit(transaction); | 930 | } |
563 | // Attempting to progress the queue here is useful | 931 | |
564 | // if the transaction has nothing to wait for. | 932 | void transaction_commit_dirty_client(void) { |
565 | transaction_progress_queue(); | 933 | _transaction_commit_dirty(false); |
566 | } | ||
567 | } | 934 | } |
diff --git a/sway/desktop/xdg_shell.c b/sway/desktop/xdg_shell.c index 667fb9e5..7c417891 100644 --- a/sway/desktop/xdg_shell.c +++ b/sway/desktop/xdg_shell.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 199309L | ||
2 | #include <float.h> | 1 | #include <float.h> |
3 | #include <stdbool.h> | 2 | #include <stdbool.h> |
4 | #include <stdlib.h> | 3 | #include <stdlib.h> |
@@ -7,7 +6,7 @@ | |||
7 | #include <wlr/util/edges.h> | 6 | #include <wlr/util/edges.h> |
8 | #include "log.h" | 7 | #include "log.h" |
9 | #include "sway/decoration.h" | 8 | #include "sway/decoration.h" |
10 | #include "sway/desktop.h" | 9 | #include "sway/scene_descriptor.h" |
11 | #include "sway/desktop/transaction.h" | 10 | #include "sway/desktop/transaction.h" |
12 | #include "sway/input/cursor.h" | 11 | #include "sway/input/cursor.h" |
13 | #include "sway/input/input-manager.h" | 12 | #include "sway/input/input-manager.h" |
@@ -19,64 +18,45 @@ | |||
19 | #include "sway/tree/workspace.h" | 18 | #include "sway/tree/workspace.h" |
20 | #include "sway/xdg_decoration.h" | 19 | #include "sway/xdg_decoration.h" |
21 | 20 | ||
22 | static const struct sway_view_child_impl popup_impl; | ||
23 | |||
24 | static void popup_get_root_coords(struct sway_view_child *child, | ||
25 | int *root_sx, int *root_sy) { | ||
26 | struct sway_xdg_popup *popup = (struct sway_xdg_popup *)child; | ||
27 | struct wlr_xdg_surface *surface = popup->wlr_xdg_surface; | ||
28 | |||
29 | int x_offset = -child->view->geometry.x - surface->geometry.x; | ||
30 | int y_offset = -child->view->geometry.y - surface->geometry.y; | ||
31 | |||
32 | wlr_xdg_popup_get_toplevel_coords(surface->popup, | ||
33 | x_offset + surface->popup->geometry.x, | ||
34 | y_offset + surface->popup->geometry.y, | ||
35 | root_sx, root_sy); | ||
36 | } | ||
37 | |||
38 | static void popup_destroy(struct sway_view_child *child) { | ||
39 | if (!sway_assert(child->impl == &popup_impl, | ||
40 | "Expected an xdg_shell popup")) { | ||
41 | return; | ||
42 | } | ||
43 | struct sway_xdg_popup *popup = (struct sway_xdg_popup *)child; | ||
44 | wl_list_remove(&popup->new_popup.link); | ||
45 | wl_list_remove(&popup->destroy.link); | ||
46 | free(popup); | ||
47 | } | ||
48 | |||
49 | static const struct sway_view_child_impl popup_impl = { | ||
50 | .get_root_coords = popup_get_root_coords, | ||
51 | .destroy = popup_destroy, | ||
52 | }; | ||
53 | |||
54 | static struct sway_xdg_popup *popup_create( | 21 | static struct sway_xdg_popup *popup_create( |
55 | struct wlr_xdg_popup *wlr_popup, struct sway_view *view); | 22 | struct wlr_xdg_popup *wlr_popup, struct sway_view *view, |
23 | struct wlr_scene_tree *parent); | ||
56 | 24 | ||
57 | static void popup_handle_new_popup(struct wl_listener *listener, void *data) { | 25 | static void popup_handle_new_popup(struct wl_listener *listener, void *data) { |
58 | struct sway_xdg_popup *popup = | 26 | struct sway_xdg_popup *popup = |
59 | wl_container_of(listener, popup, new_popup); | 27 | wl_container_of(listener, popup, new_popup); |
60 | struct wlr_xdg_popup *wlr_popup = data; | 28 | struct wlr_xdg_popup *wlr_popup = data; |
61 | popup_create(wlr_popup, popup->child.view); | 29 | popup_create(wlr_popup, popup->view, popup->xdg_surface_tree); |
62 | } | 30 | } |
63 | 31 | ||
64 | static void popup_handle_destroy(struct wl_listener *listener, void *data) { | 32 | static void popup_handle_destroy(struct wl_listener *listener, void *data) { |
65 | struct sway_xdg_popup *popup = wl_container_of(listener, popup, destroy); | 33 | struct sway_xdg_popup *popup = wl_container_of(listener, popup, destroy); |
66 | view_child_destroy(&popup->child); | 34 | |
35 | wl_list_remove(&popup->new_popup.link); | ||
36 | wl_list_remove(&popup->destroy.link); | ||
37 | wl_list_remove(&popup->surface_commit.link); | ||
38 | wl_list_remove(&popup->reposition.link); | ||
39 | wlr_scene_node_destroy(&popup->scene_tree->node); | ||
40 | free(popup); | ||
67 | } | 41 | } |
68 | 42 | ||
69 | static void popup_unconstrain(struct sway_xdg_popup *popup) { | 43 | static void popup_unconstrain(struct sway_xdg_popup *popup) { |
70 | struct sway_view *view = popup->child.view; | 44 | struct sway_view *view = popup->view; |
71 | struct wlr_xdg_popup *wlr_popup = popup->wlr_xdg_surface->popup; | 45 | struct wlr_xdg_popup *wlr_popup = popup->wlr_xdg_popup; |
46 | |||
47 | struct sway_workspace *workspace = view->container->pending.workspace; | ||
48 | if (!workspace) { | ||
49 | // is null if in the scratchpad | ||
50 | return; | ||
51 | } | ||
72 | 52 | ||
73 | struct sway_output *output = view->container->workspace->output; | 53 | struct sway_output *output = workspace->output; |
74 | 54 | ||
75 | // the output box expressed in the coordinate system of the toplevel parent | 55 | // the output box expressed in the coordinate system of the toplevel parent |
76 | // of the popup | 56 | // of the popup |
77 | struct wlr_box output_toplevel_sx_box = { | 57 | struct wlr_box output_toplevel_sx_box = { |
78 | .x = output->lx - view->container->content_x, | 58 | .x = output->lx - view->container->pending.content_x + view->geometry.x, |
79 | .y = output->ly - view->container->content_y, | 59 | .y = output->ly - view->container->pending.content_y + view->geometry.y, |
80 | .width = output->width, | 60 | .width = output->width, |
81 | .height = output->height, | 61 | .height = output->height, |
82 | }; | 62 | }; |
@@ -84,32 +64,72 @@ static void popup_unconstrain(struct sway_xdg_popup *popup) { | |||
84 | wlr_xdg_popup_unconstrain_from_box(wlr_popup, &output_toplevel_sx_box); | 64 | wlr_xdg_popup_unconstrain_from_box(wlr_popup, &output_toplevel_sx_box); |
85 | } | 65 | } |
86 | 66 | ||
87 | static struct sway_xdg_popup *popup_create( | 67 | static void popup_handle_surface_commit(struct wl_listener *listener, void *data) { |
88 | struct wlr_xdg_popup *wlr_popup, struct sway_view *view) { | 68 | struct sway_xdg_popup *popup = wl_container_of(listener, popup, surface_commit); |
69 | if (popup->wlr_xdg_popup->base->initial_commit) { | ||
70 | popup_unconstrain(popup); | ||
71 | } | ||
72 | } | ||
73 | |||
74 | static void popup_handle_reposition(struct wl_listener *listener, void *data) { | ||
75 | struct sway_xdg_popup *popup = wl_container_of(listener, popup, reposition); | ||
76 | popup_unconstrain(popup); | ||
77 | } | ||
78 | |||
79 | static struct sway_xdg_popup *popup_create(struct wlr_xdg_popup *wlr_popup, | ||
80 | struct sway_view *view, struct wlr_scene_tree *parent) { | ||
89 | struct wlr_xdg_surface *xdg_surface = wlr_popup->base; | 81 | struct wlr_xdg_surface *xdg_surface = wlr_popup->base; |
90 | 82 | ||
91 | struct sway_xdg_popup *popup = | 83 | struct sway_xdg_popup *popup = calloc(1, sizeof(struct sway_xdg_popup)); |
92 | calloc(1, sizeof(struct sway_xdg_popup)); | 84 | if (!popup) { |
93 | if (popup == NULL) { | 85 | return NULL; |
86 | } | ||
87 | |||
88 | popup->wlr_xdg_popup = wlr_popup; | ||
89 | popup->view = view; | ||
90 | |||
91 | popup->scene_tree = wlr_scene_tree_create(parent); | ||
92 | if (!popup->scene_tree) { | ||
93 | free(popup); | ||
94 | return NULL; | ||
95 | } | ||
96 | |||
97 | popup->xdg_surface_tree = wlr_scene_xdg_surface_create( | ||
98 | popup->scene_tree, xdg_surface); | ||
99 | if (!popup->xdg_surface_tree) { | ||
100 | wlr_scene_node_destroy(&popup->scene_tree->node); | ||
101 | free(popup); | ||
94 | return NULL; | 102 | return NULL; |
95 | } | 103 | } |
96 | view_child_init(&popup->child, &popup_impl, view, xdg_surface->surface); | ||
97 | popup->wlr_xdg_surface = xdg_surface; | ||
98 | 104 | ||
105 | popup->desc.relative = &view->content_tree->node; | ||
106 | popup->desc.view = view; | ||
107 | |||
108 | if (!scene_descriptor_assign(&popup->scene_tree->node, | ||
109 | SWAY_SCENE_DESC_POPUP, &popup->desc)) { | ||
110 | sway_log(SWAY_ERROR, "Failed to allocate a popup scene descriptor"); | ||
111 | wlr_scene_node_destroy(&popup->scene_tree->node); | ||
112 | free(popup); | ||
113 | return NULL; | ||
114 | } | ||
115 | |||
116 | popup->wlr_xdg_popup = xdg_surface->popup; | ||
117 | struct sway_xdg_shell_view *shell_view = | ||
118 | wl_container_of(view, shell_view, view); | ||
119 | xdg_surface->data = shell_view; | ||
120 | |||
121 | wl_signal_add(&xdg_surface->surface->events.commit, &popup->surface_commit); | ||
122 | popup->surface_commit.notify = popup_handle_surface_commit; | ||
99 | wl_signal_add(&xdg_surface->events.new_popup, &popup->new_popup); | 123 | wl_signal_add(&xdg_surface->events.new_popup, &popup->new_popup); |
100 | popup->new_popup.notify = popup_handle_new_popup; | 124 | popup->new_popup.notify = popup_handle_new_popup; |
101 | wl_signal_add(&xdg_surface->events.destroy, &popup->destroy); | 125 | wl_signal_add(&wlr_popup->events.reposition, &popup->reposition); |
126 | popup->reposition.notify = popup_handle_reposition; | ||
127 | wl_signal_add(&wlr_popup->events.destroy, &popup->destroy); | ||
102 | popup->destroy.notify = popup_handle_destroy; | 128 | popup->destroy.notify = popup_handle_destroy; |
103 | 129 | ||
104 | wl_signal_add(&xdg_surface->events.map, &popup->child.surface_map); | ||
105 | wl_signal_add(&xdg_surface->events.unmap, &popup->child.surface_unmap); | ||
106 | |||
107 | popup_unconstrain(popup); | ||
108 | |||
109 | return popup; | 130 | return popup; |
110 | } | 131 | } |
111 | 132 | ||
112 | |||
113 | static struct sway_xdg_shell_view *xdg_shell_view_from_view( | 133 | static struct sway_xdg_shell_view *xdg_shell_view_from_view( |
114 | struct sway_view *view) { | 134 | struct sway_view *view) { |
115 | if (!sway_assert(view->type == SWAY_VIEW_XDG_SHELL, | 135 | if (!sway_assert(view->type == SWAY_VIEW_XDG_SHELL, |
@@ -122,7 +142,7 @@ static struct sway_xdg_shell_view *xdg_shell_view_from_view( | |||
122 | static void get_constraints(struct sway_view *view, double *min_width, | 142 | static void get_constraints(struct sway_view *view, double *min_width, |
123 | double *max_width, double *min_height, double *max_height) { | 143 | double *max_width, double *min_height, double *max_height) { |
124 | struct wlr_xdg_toplevel_state *state = | 144 | struct wlr_xdg_toplevel_state *state = |
125 | &view->wlr_xdg_surface->toplevel->current; | 145 | &view->wlr_xdg_toplevel->current; |
126 | *min_width = state->min_width > 0 ? state->min_width : DBL_MIN; | 146 | *min_width = state->min_width > 0 ? state->min_width : DBL_MIN; |
127 | *max_width = state->max_width > 0 ? state->max_width : DBL_MAX; | 147 | *max_width = state->max_width > 0 ? state->max_width : DBL_MAX; |
128 | *min_height = state->min_height > 0 ? state->min_height : DBL_MIN; | 148 | *min_height = state->min_height > 0 ? state->min_height : DBL_MIN; |
@@ -136,9 +156,9 @@ static const char *get_string_prop(struct sway_view *view, | |||
136 | } | 156 | } |
137 | switch (prop) { | 157 | switch (prop) { |
138 | case VIEW_PROP_TITLE: | 158 | case VIEW_PROP_TITLE: |
139 | return view->wlr_xdg_surface->toplevel->title; | 159 | return view->wlr_xdg_toplevel->title; |
140 | case VIEW_PROP_APP_ID: | 160 | case VIEW_PROP_APP_ID: |
141 | return view->wlr_xdg_surface->toplevel->app_id; | 161 | return view->wlr_xdg_toplevel->app_id; |
142 | default: | 162 | default: |
143 | return NULL; | 163 | return NULL; |
144 | } | 164 | } |
@@ -151,50 +171,52 @@ static uint32_t configure(struct sway_view *view, double lx, double ly, | |||
151 | if (xdg_shell_view == NULL) { | 171 | if (xdg_shell_view == NULL) { |
152 | return 0; | 172 | return 0; |
153 | } | 173 | } |
154 | return wlr_xdg_toplevel_set_size(view->wlr_xdg_surface, width, height); | 174 | return wlr_xdg_toplevel_set_size(view->wlr_xdg_toplevel, |
175 | width, height); | ||
155 | } | 176 | } |
156 | 177 | ||
157 | static void set_activated(struct sway_view *view, bool activated) { | 178 | static void set_activated(struct sway_view *view, bool activated) { |
158 | if (xdg_shell_view_from_view(view) == NULL) { | 179 | if (xdg_shell_view_from_view(view) == NULL) { |
159 | return; | 180 | return; |
160 | } | 181 | } |
161 | struct wlr_xdg_surface *surface = view->wlr_xdg_surface; | 182 | 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 | } | 183 | } |
166 | 184 | ||
167 | static void set_tiled(struct sway_view *view, bool tiled) { | 185 | static void set_tiled(struct sway_view *view, bool tiled) { |
168 | if (xdg_shell_view_from_view(view) == NULL) { | 186 | if (xdg_shell_view_from_view(view) == NULL) { |
169 | return; | 187 | return; |
170 | } | 188 | } |
171 | struct wlr_xdg_surface *surface = view->wlr_xdg_surface; | 189 | if (wl_resource_get_version(view->wlr_xdg_toplevel->resource) >= |
172 | enum wlr_edges edges = WLR_EDGE_NONE; | 190 | XDG_TOPLEVEL_STATE_TILED_LEFT_SINCE_VERSION) { |
173 | if (tiled) { | 191 | enum wlr_edges edges = WLR_EDGE_NONE; |
174 | edges = WLR_EDGE_LEFT | WLR_EDGE_RIGHT | WLR_EDGE_TOP | | 192 | if (tiled) { |
175 | WLR_EDGE_BOTTOM; | 193 | edges = WLR_EDGE_LEFT | WLR_EDGE_RIGHT | WLR_EDGE_TOP | |
194 | WLR_EDGE_BOTTOM; | ||
195 | } | ||
196 | wlr_xdg_toplevel_set_tiled(view->wlr_xdg_toplevel, edges); | ||
197 | } else { | ||
198 | // The version is too low for the tiled state; configure as maximized instead | ||
199 | // to stop the client from drawing decorations outside of the toplevel geometry. | ||
200 | wlr_xdg_toplevel_set_maximized(view->wlr_xdg_toplevel, tiled); | ||
176 | } | 201 | } |
177 | wlr_xdg_toplevel_set_tiled(surface, edges); | ||
178 | } | 202 | } |
179 | 203 | ||
180 | static void set_fullscreen(struct sway_view *view, bool fullscreen) { | 204 | static void set_fullscreen(struct sway_view *view, bool fullscreen) { |
181 | if (xdg_shell_view_from_view(view) == NULL) { | 205 | if (xdg_shell_view_from_view(view) == NULL) { |
182 | return; | 206 | return; |
183 | } | 207 | } |
184 | struct wlr_xdg_surface *surface = view->wlr_xdg_surface; | 208 | wlr_xdg_toplevel_set_fullscreen(view->wlr_xdg_toplevel, fullscreen); |
185 | wlr_xdg_toplevel_set_fullscreen(surface, fullscreen); | ||
186 | } | 209 | } |
187 | 210 | ||
188 | static void set_resizing(struct sway_view *view, bool resizing) { | 211 | static void set_resizing(struct sway_view *view, bool resizing) { |
189 | if (xdg_shell_view_from_view(view) == NULL) { | 212 | if (xdg_shell_view_from_view(view) == NULL) { |
190 | return; | 213 | return; |
191 | } | 214 | } |
192 | struct wlr_xdg_surface *surface = view->wlr_xdg_surface; | 215 | wlr_xdg_toplevel_set_resizing(view->wlr_xdg_toplevel, resizing); |
193 | wlr_xdg_toplevel_set_resizing(surface, resizing); | ||
194 | } | 216 | } |
195 | 217 | ||
196 | static bool wants_floating(struct sway_view *view) { | 218 | static bool wants_floating(struct sway_view *view) { |
197 | struct wlr_xdg_toplevel *toplevel = view->wlr_xdg_surface->toplevel; | 219 | struct wlr_xdg_toplevel *toplevel = view->wlr_xdg_toplevel; |
198 | struct wlr_xdg_toplevel_state *state = &toplevel->current; | 220 | struct wlr_xdg_toplevel_state *state = &toplevel->current; |
199 | return (state->min_width != 0 && state->min_height != 0 | 221 | return (state->min_width != 0 && state->min_height != 0 |
200 | && (state->min_width == state->max_width | 222 | && (state->min_width == state->max_width |
@@ -202,35 +224,17 @@ static bool wants_floating(struct sway_view *view) { | |||
202 | || toplevel->parent; | 224 | || toplevel->parent; |
203 | } | 225 | } |
204 | 226 | ||
205 | static void for_each_surface(struct sway_view *view, | ||
206 | wlr_surface_iterator_func_t iterator, void *user_data) { | ||
207 | if (xdg_shell_view_from_view(view) == NULL) { | ||
208 | return; | ||
209 | } | ||
210 | wlr_xdg_surface_for_each_surface(view->wlr_xdg_surface, iterator, | ||
211 | user_data); | ||
212 | } | ||
213 | |||
214 | static void for_each_popup_surface(struct sway_view *view, | ||
215 | wlr_surface_iterator_func_t iterator, void *user_data) { | ||
216 | if (xdg_shell_view_from_view(view) == NULL) { | ||
217 | return; | ||
218 | } | ||
219 | wlr_xdg_surface_for_each_popup_surface(view->wlr_xdg_surface, iterator, | ||
220 | user_data); | ||
221 | } | ||
222 | |||
223 | static bool is_transient_for(struct sway_view *child, | 227 | static bool is_transient_for(struct sway_view *child, |
224 | struct sway_view *ancestor) { | 228 | struct sway_view *ancestor) { |
225 | if (xdg_shell_view_from_view(child) == NULL) { | 229 | if (xdg_shell_view_from_view(child) == NULL) { |
226 | return false; | 230 | return false; |
227 | } | 231 | } |
228 | struct wlr_xdg_surface *surface = child->wlr_xdg_surface; | 232 | struct wlr_xdg_toplevel *toplevel = child->wlr_xdg_toplevel; |
229 | while (surface && surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL) { | 233 | while (toplevel) { |
230 | if (surface->toplevel->parent == ancestor->wlr_xdg_surface) { | 234 | if (toplevel->parent == ancestor->wlr_xdg_toplevel) { |
231 | return true; | 235 | return true; |
232 | } | 236 | } |
233 | surface = surface->toplevel->parent; | 237 | toplevel = toplevel->parent; |
234 | } | 238 | } |
235 | return false; | 239 | return false; |
236 | } | 240 | } |
@@ -239,17 +243,13 @@ static void _close(struct sway_view *view) { | |||
239 | if (xdg_shell_view_from_view(view) == NULL) { | 243 | if (xdg_shell_view_from_view(view) == NULL) { |
240 | return; | 244 | return; |
241 | } | 245 | } |
242 | struct wlr_xdg_surface *surface = view->wlr_xdg_surface; | 246 | 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 | } | 247 | } |
248 | 248 | ||
249 | static void close_popups(struct sway_view *view) { | 249 | static void close_popups(struct sway_view *view) { |
250 | struct wlr_xdg_popup *popup, *tmp; | 250 | struct wlr_xdg_popup *popup, *tmp; |
251 | wl_list_for_each_safe(popup, tmp, &view->wlr_xdg_surface->popups, link) { | 251 | wl_list_for_each_safe(popup, tmp, &view->wlr_xdg_toplevel->base->popups, link) { |
252 | wlr_xdg_popup_destroy(popup->base); | 252 | wlr_xdg_popup_destroy(popup); |
253 | } | 253 | } |
254 | } | 254 | } |
255 | 255 | ||
@@ -271,8 +271,6 @@ static const struct sway_view_impl view_impl = { | |||
271 | .set_fullscreen = set_fullscreen, | 271 | .set_fullscreen = set_fullscreen, |
272 | .set_resizing = set_resizing, | 272 | .set_resizing = set_resizing, |
273 | .wants_floating = wants_floating, | 273 | .wants_floating = wants_floating, |
274 | .for_each_surface = for_each_surface, | ||
275 | .for_each_popup_surface = for_each_popup_surface, | ||
276 | .is_transient_for = is_transient_for, | 274 | .is_transient_for = is_transient_for, |
277 | .close = _close, | 275 | .close = _close, |
278 | .close_popups = close_popups, | 276 | .close_popups = close_popups, |
@@ -283,7 +281,21 @@ static void handle_commit(struct wl_listener *listener, void *data) { | |||
283 | struct sway_xdg_shell_view *xdg_shell_view = | 281 | struct sway_xdg_shell_view *xdg_shell_view = |
284 | wl_container_of(listener, xdg_shell_view, commit); | 282 | wl_container_of(listener, xdg_shell_view, commit); |
285 | struct sway_view *view = &xdg_shell_view->view; | 283 | struct sway_view *view = &xdg_shell_view->view; |
286 | struct wlr_xdg_surface *xdg_surface = view->wlr_xdg_surface; | 284 | struct wlr_xdg_surface *xdg_surface = view->wlr_xdg_toplevel->base; |
285 | |||
286 | if (xdg_surface->initial_commit) { | ||
287 | if (view->xdg_decoration != NULL) { | ||
288 | set_xdg_decoration_mode(view->xdg_decoration); | ||
289 | } | ||
290 | // XXX: https://github.com/swaywm/sway/issues/2176 | ||
291 | wlr_xdg_surface_schedule_configure(xdg_surface); | ||
292 | // TODO: wlr_xdg_toplevel_set_bounds() | ||
293 | return; | ||
294 | } | ||
295 | |||
296 | if (!xdg_surface->surface->mapped) { | ||
297 | return; | ||
298 | } | ||
287 | 299 | ||
288 | struct wlr_box new_geo; | 300 | struct wlr_box new_geo; |
289 | wlr_xdg_surface_get_geometry(xdg_surface, &new_geo); | 301 | wlr_xdg_surface_get_geometry(xdg_surface, &new_geo); |
@@ -293,22 +305,35 @@ static void handle_commit(struct wl_listener *listener, void *data) { | |||
293 | new_geo.y != view->geometry.y; | 305 | new_geo.y != view->geometry.y; |
294 | 306 | ||
295 | if (new_size) { | 307 | if (new_size) { |
296 | // The view has unexpectedly sent a new size | 308 | // The client changed its surface size in this commit. For floating |
297 | desktop_damage_view(view); | 309 | // containers, we resize the container to match. For tiling containers, |
298 | view_update_size(view, new_geo.width, new_geo.height); | 310 | // we only recenter the surface. |
299 | memcpy(&view->geometry, &new_geo, sizeof(struct wlr_box)); | 311 | memcpy(&view->geometry, &new_geo, sizeof(struct wlr_box)); |
300 | desktop_damage_view(view); | 312 | if (container_is_floating(view->container)) { |
301 | transaction_commit_dirty(); | 313 | view_update_size(view); |
314 | // Only set the toplevel size the current container actually has a size. | ||
315 | if (view->container->current.width) { | ||
316 | wlr_xdg_toplevel_set_size(view->wlr_xdg_toplevel, view->geometry.width, | ||
317 | view->geometry.height); | ||
318 | } | ||
319 | transaction_commit_dirty_client(); | ||
320 | } | ||
321 | |||
322 | view_center_and_clip_surface(view); | ||
302 | } | 323 | } |
303 | 324 | ||
304 | if (view->container->node.instruction) { | 325 | if (view->container->node.instruction) { |
305 | transaction_notify_view_ready_by_serial(view, | 326 | bool successful = transaction_notify_view_ready_by_serial(view, |
306 | xdg_surface->configure_serial); | 327 | xdg_surface->current.configure_serial); |
307 | } else if (new_size) { | 328 | |
308 | transaction_notify_view_ready_immediately(view); | 329 | // If we saved the view and this commit isn't what we're looking for |
330 | // that means the user will never actually see the buffers submitted to | ||
331 | // us here. Just send frame done events to these surfaces so they can | ||
332 | // commit another time for us. | ||
333 | if (view->saved_surface_tree && !successful) { | ||
334 | view_send_frame_done(view); | ||
335 | } | ||
309 | } | 336 | } |
310 | |||
311 | view_damage_from(view); | ||
312 | } | 337 | } |
313 | 338 | ||
314 | static void handle_set_title(struct wl_listener *listener, void *data) { | 339 | static void handle_set_title(struct wl_listener *listener, void *data) { |
@@ -323,6 +348,7 @@ static void handle_set_app_id(struct wl_listener *listener, void *data) { | |||
323 | struct sway_xdg_shell_view *xdg_shell_view = | 348 | struct sway_xdg_shell_view *xdg_shell_view = |
324 | wl_container_of(listener, xdg_shell_view, set_app_id); | 349 | wl_container_of(listener, xdg_shell_view, set_app_id); |
325 | struct sway_view *view = &xdg_shell_view->view; | 350 | struct sway_view *view = &xdg_shell_view->view; |
351 | view_update_app_id(view); | ||
326 | view_execute_criteria(view); | 352 | view_execute_criteria(view); |
327 | } | 353 | } |
328 | 354 | ||
@@ -330,31 +356,42 @@ static void handle_new_popup(struct wl_listener *listener, void *data) { | |||
330 | struct sway_xdg_shell_view *xdg_shell_view = | 356 | struct sway_xdg_shell_view *xdg_shell_view = |
331 | wl_container_of(listener, xdg_shell_view, new_popup); | 357 | wl_container_of(listener, xdg_shell_view, new_popup); |
332 | struct wlr_xdg_popup *wlr_popup = data; | 358 | struct wlr_xdg_popup *wlr_popup = data; |
333 | popup_create(wlr_popup, &xdg_shell_view->view); | 359 | |
360 | struct sway_xdg_popup *popup = popup_create(wlr_popup, | ||
361 | &xdg_shell_view->view, root->layers.popup); | ||
362 | if (!popup) { | ||
363 | return; | ||
364 | } | ||
365 | |||
366 | int lx, ly; | ||
367 | wlr_scene_node_coords(&popup->view->content_tree->node, &lx, &ly); | ||
368 | wlr_scene_node_set_position(&popup->scene_tree->node, lx, ly); | ||
369 | } | ||
370 | |||
371 | static void handle_request_maximize(struct wl_listener *listener, void *data) { | ||
372 | struct sway_xdg_shell_view *xdg_shell_view = | ||
373 | wl_container_of(listener, xdg_shell_view, request_maximize); | ||
374 | struct wlr_xdg_toplevel *toplevel = xdg_shell_view->view.wlr_xdg_toplevel; | ||
375 | wlr_xdg_surface_schedule_configure(toplevel->base); | ||
334 | } | 376 | } |
335 | 377 | ||
336 | static void handle_request_fullscreen(struct wl_listener *listener, void *data) { | 378 | static void handle_request_fullscreen(struct wl_listener *listener, void *data) { |
337 | struct sway_xdg_shell_view *xdg_shell_view = | 379 | struct sway_xdg_shell_view *xdg_shell_view = |
338 | wl_container_of(listener, xdg_shell_view, request_fullscreen); | 380 | wl_container_of(listener, xdg_shell_view, request_fullscreen); |
339 | struct wlr_xdg_toplevel_set_fullscreen_event *e = data; | 381 | 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; | 382 | struct sway_view *view = &xdg_shell_view->view; |
343 | 383 | ||
344 | if (!sway_assert(xdg_surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL, | 384 | if (!toplevel->base->surface->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; | 385 | return; |
351 | } | 386 | } |
352 | 387 | ||
353 | struct sway_container *container = view->container; | 388 | struct sway_container *container = view->container; |
354 | if (e->fullscreen && e->output && e->output->data) { | 389 | struct wlr_xdg_toplevel_requested *req = &toplevel->requested; |
355 | struct sway_output *output = e->output->data; | 390 | if (req->fullscreen && req->fullscreen_output && req->fullscreen_output->data) { |
391 | struct sway_output *output = req->fullscreen_output->data; | ||
356 | struct sway_workspace *ws = output_get_active_workspace(output); | 392 | struct sway_workspace *ws = output_get_active_workspace(output); |
357 | if (ws && !container_is_scratchpad_hidden(container)) { | 393 | if (ws && !container_is_scratchpad_hidden(container) && |
394 | container->pending.workspace != ws) { | ||
358 | if (container_is_floating(container)) { | 395 | if (container_is_floating(container)) { |
359 | workspace_add_floating(ws, container); | 396 | workspace_add_floating(ws, container); |
360 | } else { | 397 | } else { |
@@ -363,22 +400,18 @@ static void handle_request_fullscreen(struct wl_listener *listener, void *data) | |||
363 | } | 400 | } |
364 | } | 401 | } |
365 | 402 | ||
366 | container_set_fullscreen(container, e->fullscreen); | 403 | container_set_fullscreen(container, req->fullscreen); |
367 | 404 | ||
368 | arrange_root(); | 405 | arrange_root(); |
369 | transaction_commit_dirty(); | 406 | transaction_commit_dirty(); |
370 | } | 407 | } |
371 | 408 | ||
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) { | 409 | static void handle_request_move(struct wl_listener *listener, void *data) { |
378 | struct sway_xdg_shell_view *xdg_shell_view = | 410 | struct sway_xdg_shell_view *xdg_shell_view = |
379 | wl_container_of(listener, xdg_shell_view, request_move); | 411 | wl_container_of(listener, xdg_shell_view, request_move); |
380 | struct sway_view *view = &xdg_shell_view->view; | 412 | struct sway_view *view = &xdg_shell_view->view; |
381 | if (!container_is_floating(view->container)) { | 413 | if (!container_is_floating(view->container) || |
414 | view->container->pending.fullscreen_mode) { | ||
382 | return; | 415 | return; |
383 | } | 416 | } |
384 | struct wlr_xdg_toplevel_move_event *e = data; | 417 | struct wlr_xdg_toplevel_move_event *e = data; |
@@ -413,10 +446,9 @@ static void handle_unmap(struct wl_listener *listener, void *data) { | |||
413 | 446 | ||
414 | view_unmap(view); | 447 | view_unmap(view); |
415 | 448 | ||
416 | wl_list_remove(&xdg_shell_view->commit.link); | ||
417 | wl_list_remove(&xdg_shell_view->new_popup.link); | 449 | 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); | 450 | wl_list_remove(&xdg_shell_view->request_maximize.link); |
451 | wl_list_remove(&xdg_shell_view->request_fullscreen.link); | ||
420 | wl_list_remove(&xdg_shell_view->request_move.link); | 452 | wl_list_remove(&xdg_shell_view->request_move.link); |
421 | wl_list_remove(&xdg_shell_view->request_resize.link); | 453 | wl_list_remove(&xdg_shell_view->request_resize.link); |
422 | wl_list_remove(&xdg_shell_view->set_title.link); | 454 | wl_list_remove(&xdg_shell_view->set_title.link); |
@@ -427,62 +459,61 @@ static void handle_map(struct wl_listener *listener, void *data) { | |||
427 | struct sway_xdg_shell_view *xdg_shell_view = | 459 | struct sway_xdg_shell_view *xdg_shell_view = |
428 | wl_container_of(listener, xdg_shell_view, map); | 460 | wl_container_of(listener, xdg_shell_view, map); |
429 | struct sway_view *view = &xdg_shell_view->view; | 461 | struct sway_view *view = &xdg_shell_view->view; |
430 | struct wlr_xdg_surface *xdg_surface = view->wlr_xdg_surface; | 462 | struct wlr_xdg_toplevel *toplevel = view->wlr_xdg_toplevel; |
431 | 463 | ||
432 | view->natural_width = view->wlr_xdg_surface->geometry.width; | 464 | view->natural_width = toplevel->base->current.geometry.width; |
433 | view->natural_height = view->wlr_xdg_surface->geometry.height; | 465 | view->natural_height = toplevel->base->current.geometry.height; |
434 | if (!view->natural_width && !view->natural_height) { | 466 | if (!view->natural_width && !view->natural_height) { |
435 | view->natural_width = view->wlr_xdg_surface->surface->current.width; | 467 | view->natural_width = toplevel->base->surface->current.width; |
436 | view->natural_height = view->wlr_xdg_surface->surface->current.height; | 468 | view->natural_height = toplevel->base->surface->current.height; |
437 | } | 469 | } |
438 | 470 | ||
439 | bool csd = false; | 471 | bool csd = false; |
440 | 472 | ||
441 | if (!view->xdg_decoration) { | 473 | if (view->xdg_decoration) { |
474 | enum wlr_xdg_toplevel_decoration_v1_mode mode = | ||
475 | view->xdg_decoration->wlr_xdg_decoration->requested_mode; | ||
476 | csd = mode == WLR_XDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE; | ||
477 | } else { | ||
442 | struct sway_server_decoration *deco = | 478 | struct sway_server_decoration *deco = |
443 | decoration_from_surface(xdg_surface->surface); | 479 | decoration_from_surface(toplevel->base->surface); |
444 | csd = !deco || deco->wlr_server_decoration->mode == | 480 | csd = !deco || deco->wlr_server_decoration->mode == |
445 | WLR_SERVER_DECORATION_MANAGER_MODE_CLIENT; | 481 | WLR_SERVER_DECORATION_MANAGER_MODE_CLIENT; |
446 | |||
447 | } | 482 | } |
448 | 483 | ||
449 | view_map(view, view->wlr_xdg_surface->surface, | 484 | view_map(view, toplevel->base->surface, |
450 | xdg_surface->toplevel->client_pending.fullscreen, | 485 | toplevel->requested.fullscreen, |
451 | xdg_surface->toplevel->client_pending.fullscreen_output, | 486 | toplevel->requested.fullscreen_output, |
452 | csd); | 487 | csd); |
453 | 488 | ||
454 | transaction_commit_dirty(); | 489 | transaction_commit_dirty(); |
455 | 490 | ||
456 | xdg_shell_view->commit.notify = handle_commit; | ||
457 | wl_signal_add(&xdg_surface->surface->events.commit, | ||
458 | &xdg_shell_view->commit); | ||
459 | |||
460 | xdg_shell_view->new_popup.notify = handle_new_popup; | 491 | xdg_shell_view->new_popup.notify = handle_new_popup; |
461 | wl_signal_add(&xdg_surface->events.new_popup, | 492 | wl_signal_add(&toplevel->base->events.new_popup, |
462 | &xdg_shell_view->new_popup); | 493 | &xdg_shell_view->new_popup); |
463 | 494 | ||
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; | 495 | xdg_shell_view->request_maximize.notify = handle_request_maximize; |
469 | wl_signal_add(&xdg_surface->toplevel->events.request_maximize, | 496 | wl_signal_add(&toplevel->events.request_maximize, |
470 | &xdg_shell_view->request_maximize); | 497 | &xdg_shell_view->request_maximize); |
471 | 498 | ||
499 | xdg_shell_view->request_fullscreen.notify = handle_request_fullscreen; | ||
500 | wl_signal_add(&toplevel->events.request_fullscreen, | ||
501 | &xdg_shell_view->request_fullscreen); | ||
502 | |||
472 | xdg_shell_view->request_move.notify = handle_request_move; | 503 | xdg_shell_view->request_move.notify = handle_request_move; |
473 | wl_signal_add(&xdg_surface->toplevel->events.request_move, | 504 | wl_signal_add(&toplevel->events.request_move, |
474 | &xdg_shell_view->request_move); | 505 | &xdg_shell_view->request_move); |
475 | 506 | ||
476 | xdg_shell_view->request_resize.notify = handle_request_resize; | 507 | xdg_shell_view->request_resize.notify = handle_request_resize; |
477 | wl_signal_add(&xdg_surface->toplevel->events.request_resize, | 508 | wl_signal_add(&toplevel->events.request_resize, |
478 | &xdg_shell_view->request_resize); | 509 | &xdg_shell_view->request_resize); |
479 | 510 | ||
480 | xdg_shell_view->set_title.notify = handle_set_title; | 511 | xdg_shell_view->set_title.notify = handle_set_title; |
481 | wl_signal_add(&xdg_surface->toplevel->events.set_title, | 512 | wl_signal_add(&toplevel->events.set_title, |
482 | &xdg_shell_view->set_title); | 513 | &xdg_shell_view->set_title); |
483 | 514 | ||
484 | xdg_shell_view->set_app_id.notify = handle_set_app_id; | 515 | xdg_shell_view->set_app_id.notify = handle_set_app_id; |
485 | wl_signal_add(&xdg_surface->toplevel->events.set_app_id, | 516 | wl_signal_add(&toplevel->events.set_app_id, |
486 | &xdg_shell_view->set_app_id); | 517 | &xdg_shell_view->set_app_id); |
487 | } | 518 | } |
488 | 519 | ||
@@ -496,7 +527,8 @@ static void handle_destroy(struct wl_listener *listener, void *data) { | |||
496 | wl_list_remove(&xdg_shell_view->destroy.link); | 527 | wl_list_remove(&xdg_shell_view->destroy.link); |
497 | wl_list_remove(&xdg_shell_view->map.link); | 528 | wl_list_remove(&xdg_shell_view->map.link); |
498 | wl_list_remove(&xdg_shell_view->unmap.link); | 529 | wl_list_remove(&xdg_shell_view->unmap.link); |
499 | view->wlr_xdg_surface = NULL; | 530 | wl_list_remove(&xdg_shell_view->commit.link); |
531 | view->wlr_xdg_toplevel = NULL; | ||
500 | if (view->xdg_decoration) { | 532 | if (view->xdg_decoration) { |
501 | view->xdg_decoration->view = NULL; | 533 | view->xdg_decoration->view = NULL; |
502 | } | 534 | } |
@@ -508,17 +540,12 @@ struct sway_view *view_from_wlr_xdg_surface( | |||
508 | return xdg_surface->data; | 540 | return xdg_surface->data; |
509 | } | 541 | } |
510 | 542 | ||
511 | void handle_xdg_shell_surface(struct wl_listener *listener, void *data) { | 543 | void handle_xdg_shell_toplevel(struct wl_listener *listener, void *data) { |
512 | struct wlr_xdg_surface *xdg_surface = data; | 544 | struct wlr_xdg_toplevel *xdg_toplevel = data; |
513 | |||
514 | if (xdg_surface->role == WLR_XDG_SURFACE_ROLE_POPUP) { | ||
515 | sway_log(SWAY_DEBUG, "New xdg_shell popup"); | ||
516 | return; | ||
517 | } | ||
518 | 545 | ||
519 | sway_log(SWAY_DEBUG, "New xdg_shell toplevel title='%s' app_id='%s'", | 546 | sway_log(SWAY_DEBUG, "New xdg_shell toplevel title='%s' app_id='%s'", |
520 | xdg_surface->toplevel->title, xdg_surface->toplevel->app_id); | 547 | xdg_toplevel->title, xdg_toplevel->app_id); |
521 | wlr_xdg_surface_ping(xdg_surface); | 548 | wlr_xdg_surface_ping(xdg_toplevel->base); |
522 | 549 | ||
523 | struct sway_xdg_shell_view *xdg_shell_view = | 550 | struct sway_xdg_shell_view *xdg_shell_view = |
524 | calloc(1, sizeof(struct sway_xdg_shell_view)); | 551 | calloc(1, sizeof(struct sway_xdg_shell_view)); |
@@ -526,17 +553,29 @@ void handle_xdg_shell_surface(struct wl_listener *listener, void *data) { | |||
526 | return; | 553 | return; |
527 | } | 554 | } |
528 | 555 | ||
529 | view_init(&xdg_shell_view->view, SWAY_VIEW_XDG_SHELL, &view_impl); | 556 | if (!view_init(&xdg_shell_view->view, SWAY_VIEW_XDG_SHELL, &view_impl)) { |
530 | xdg_shell_view->view.wlr_xdg_surface = xdg_surface; | 557 | free(xdg_shell_view); |
558 | return; | ||
559 | } | ||
560 | xdg_shell_view->view.wlr_xdg_toplevel = xdg_toplevel; | ||
531 | 561 | ||
532 | xdg_shell_view->map.notify = handle_map; | 562 | xdg_shell_view->map.notify = handle_map; |
533 | wl_signal_add(&xdg_surface->events.map, &xdg_shell_view->map); | 563 | wl_signal_add(&xdg_toplevel->base->surface->events.map, &xdg_shell_view->map); |
534 | 564 | ||
535 | xdg_shell_view->unmap.notify = handle_unmap; | 565 | xdg_shell_view->unmap.notify = handle_unmap; |
536 | wl_signal_add(&xdg_surface->events.unmap, &xdg_shell_view->unmap); | 566 | wl_signal_add(&xdg_toplevel->base->surface->events.unmap, &xdg_shell_view->unmap); |
567 | |||
568 | xdg_shell_view->commit.notify = handle_commit; | ||
569 | wl_signal_add(&xdg_toplevel->base->surface->events.commit, | ||
570 | &xdg_shell_view->commit); | ||
537 | 571 | ||
538 | xdg_shell_view->destroy.notify = handle_destroy; | 572 | xdg_shell_view->destroy.notify = handle_destroy; |
539 | wl_signal_add(&xdg_surface->events.destroy, &xdg_shell_view->destroy); | 573 | wl_signal_add(&xdg_toplevel->events.destroy, &xdg_shell_view->destroy); |
574 | |||
575 | wlr_scene_xdg_surface_create(xdg_shell_view->view.content_tree, xdg_toplevel->base); | ||
576 | |||
577 | xdg_toplevel->base->data = xdg_shell_view; | ||
540 | 578 | ||
541 | xdg_surface->data = xdg_shell_view; | 579 | wlr_xdg_toplevel_set_wm_capabilities(xdg_toplevel, |
580 | XDG_TOPLEVEL_WM_CAPABILITIES_FULLSCREEN); | ||
542 | } | 581 | } |
diff --git a/sway/desktop/xwayland.c b/sway/desktop/xwayland.c index e1a2e463..270cf08f 100644 --- a/sway/desktop/xwayland.c +++ b/sway/desktop/xwayland.c | |||
@@ -1,20 +1,23 @@ | |||
1 | #define _POSIX_C_SOURCE 199309L | ||
2 | #include <float.h> | 1 | #include <float.h> |
3 | #include <stdbool.h> | 2 | #include <stdbool.h> |
4 | #include <stdlib.h> | 3 | #include <stdlib.h> |
5 | #include <wayland-server-core.h> | 4 | #include <wayland-server-core.h> |
6 | #include <wlr/types/wlr_output_layout.h> | 5 | #include <wlr/types/wlr_output_layout.h> |
7 | #include <wlr/types/wlr_output.h> | 6 | #include <wlr/types/wlr_output.h> |
7 | #include <wlr/types/wlr_xdg_activation_v1.h> | ||
8 | #include <wlr/types/wlr_scene.h> | ||
8 | #include <wlr/xwayland.h> | 9 | #include <wlr/xwayland.h> |
10 | #include <xcb/xcb_icccm.h> | ||
9 | #include "log.h" | 11 | #include "log.h" |
10 | #include "sway/desktop.h" | ||
11 | #include "sway/desktop/transaction.h" | 12 | #include "sway/desktop/transaction.h" |
12 | #include "sway/input/cursor.h" | 13 | #include "sway/input/cursor.h" |
13 | #include "sway/input/input-manager.h" | 14 | #include "sway/input/input-manager.h" |
14 | #include "sway/input/seat.h" | 15 | #include "sway/input/seat.h" |
15 | #include "sway/output.h" | 16 | #include "sway/output.h" |
17 | #include "sway/scene_descriptor.h" | ||
16 | #include "sway/tree/arrange.h" | 18 | #include "sway/tree/arrange.h" |
17 | #include "sway/tree/container.h" | 19 | #include "sway/tree/container.h" |
20 | #include "sway/server.h" | ||
18 | #include "sway/tree/view.h" | 21 | #include "sway/tree/view.h" |
19 | #include "sway/tree/workspace.h" | 22 | #include "sway/tree/workspace.h" |
20 | 23 | ||
@@ -42,29 +45,12 @@ static void unmanaged_handle_request_configure(struct wl_listener *listener, | |||
42 | ev->width, ev->height); | 45 | ev->width, ev->height); |
43 | } | 46 | } |
44 | 47 | ||
45 | static void unmanaged_handle_commit(struct wl_listener *listener, void *data) { | ||
46 | struct sway_xwayland_unmanaged *surface = | ||
47 | wl_container_of(listener, surface, commit); | ||
48 | struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface; | ||
49 | |||
50 | desktop_damage_surface(xsurface->surface, surface->lx, surface->ly, | ||
51 | false); | ||
52 | } | ||
53 | |||
54 | static void unmanaged_handle_set_geometry(struct wl_listener *listener, void *data) { | 48 | static void unmanaged_handle_set_geometry(struct wl_listener *listener, void *data) { |
55 | struct sway_xwayland_unmanaged *surface = | 49 | struct sway_xwayland_unmanaged *surface = |
56 | wl_container_of(listener, surface, set_geometry); | 50 | wl_container_of(listener, surface, set_geometry); |
57 | struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface; | 51 | struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface; |
58 | 52 | ||
59 | if (xsurface->x != surface->lx || xsurface->y != surface->ly) { | 53 | wlr_scene_node_set_position(&surface->surface_scene->buffer->node, xsurface->x, xsurface->y); |
60 | // Surface has moved | ||
61 | desktop_damage_surface(xsurface->surface, surface->lx, surface->ly, | ||
62 | true); | ||
63 | surface->lx = xsurface->x; | ||
64 | surface->ly = xsurface->y; | ||
65 | desktop_damage_surface(xsurface->surface, surface->lx, surface->ly, | ||
66 | true); | ||
67 | } | ||
68 | } | 54 | } |
69 | 55 | ||
70 | static void unmanaged_handle_map(struct wl_listener *listener, void *data) { | 56 | static void unmanaged_handle_map(struct wl_listener *listener, void *data) { |
@@ -72,17 +58,18 @@ static void unmanaged_handle_map(struct wl_listener *listener, void *data) { | |||
72 | wl_container_of(listener, surface, map); | 58 | wl_container_of(listener, surface, map); |
73 | struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface; | 59 | struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface; |
74 | 60 | ||
75 | wl_list_insert(root->xwayland_unmanaged.prev, &surface->link); | 61 | surface->surface_scene = wlr_scene_surface_create(root->layers.unmanaged, |
76 | 62 | xsurface->surface); | |
77 | wl_signal_add(&xsurface->events.set_geometry, &surface->set_geometry); | ||
78 | surface->set_geometry.notify = unmanaged_handle_set_geometry; | ||
79 | 63 | ||
80 | wl_signal_add(&xsurface->surface->events.commit, &surface->commit); | 64 | if (surface->surface_scene) { |
81 | surface->commit.notify = unmanaged_handle_commit; | 65 | scene_descriptor_assign(&surface->surface_scene->buffer->node, |
66 | SWAY_SCENE_DESC_XWAYLAND_UNMANAGED, surface); | ||
67 | wlr_scene_node_set_position(&surface->surface_scene->buffer->node, | ||
68 | xsurface->x, xsurface->y); | ||
82 | 69 | ||
83 | surface->lx = xsurface->x; | 70 | wl_signal_add(&xsurface->events.set_geometry, &surface->set_geometry); |
84 | surface->ly = xsurface->y; | 71 | surface->set_geometry.notify = unmanaged_handle_set_geometry; |
85 | desktop_damage_surface(xsurface->surface, surface->lx, surface->ly, true); | 72 | } |
86 | 73 | ||
87 | if (wlr_xwayland_or_surface_wants_focus(xsurface)) { | 74 | if (wlr_xwayland_or_surface_wants_focus(xsurface)) { |
88 | struct sway_seat *seat = input_manager_current_seat(); | 75 | struct sway_seat *seat = input_manager_current_seat(); |
@@ -96,23 +83,22 @@ static void unmanaged_handle_unmap(struct wl_listener *listener, void *data) { | |||
96 | struct sway_xwayland_unmanaged *surface = | 83 | struct sway_xwayland_unmanaged *surface = |
97 | wl_container_of(listener, surface, unmap); | 84 | wl_container_of(listener, surface, unmap); |
98 | struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface; | 85 | struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface; |
99 | desktop_damage_surface(xsurface->surface, xsurface->x, xsurface->y, true); | 86 | |
100 | wl_list_remove(&surface->link); | 87 | if (surface->surface_scene) { |
101 | wl_list_remove(&surface->set_geometry.link); | 88 | wl_list_remove(&surface->set_geometry.link); |
102 | wl_list_remove(&surface->commit.link); | 89 | |
90 | wlr_scene_node_destroy(&surface->surface_scene->buffer->node); | ||
91 | surface->surface_scene = NULL; | ||
92 | } | ||
103 | 93 | ||
104 | struct sway_seat *seat = input_manager_current_seat(); | 94 | struct sway_seat *seat = input_manager_current_seat(); |
105 | if (seat->wlr_seat->keyboard_state.focused_surface == xsurface->surface) { | 95 | if (seat->wlr_seat->keyboard_state.focused_surface == xsurface->surface) { |
106 | // This simply returns focus to the parent surface if there's one available. | 96 | // This simply returns focus to the parent surface if there's one available. |
107 | // This seems to handle JetBrains issues. | 97 | // This seems to handle JetBrains issues. |
108 | if (xsurface->parent && xsurface->parent->surface && | 98 | if (xsurface->parent && xsurface->parent->surface |
109 | wlr_surface_is_xwayland_surface(xsurface->parent->surface)) { | 99 | && wlr_xwayland_or_surface_wants_focus(xsurface->parent)) { |
110 | struct wlr_xwayland_surface *next_surface = | 100 | seat_set_focus_surface(seat, xsurface->parent->surface, false); |
111 | wlr_xwayland_surface_from_wlr_surface(xsurface->parent->surface); | 101 | 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 | } | 102 | } |
117 | 103 | ||
118 | // Restore focus | 104 | // Restore focus |
@@ -125,18 +111,53 @@ static void unmanaged_handle_unmap(struct wl_listener *listener, void *data) { | |||
125 | } | 111 | } |
126 | } | 112 | } |
127 | 113 | ||
114 | static void unmanaged_handle_request_activate(struct wl_listener *listener, void *data) { | ||
115 | struct sway_xwayland_unmanaged *surface = | ||
116 | wl_container_of(listener, surface, request_activate); | ||
117 | struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface; | ||
118 | if (xsurface->surface == NULL || !xsurface->surface->mapped) { | ||
119 | return; | ||
120 | } | ||
121 | struct sway_seat *seat = input_manager_current_seat(); | ||
122 | struct sway_container *focus = seat_get_focused_container(seat); | ||
123 | if (focus && focus->view && focus->view->pid != xsurface->pid) { | ||
124 | return; | ||
125 | } | ||
126 | |||
127 | seat_set_focus_surface(seat, xsurface->surface, false); | ||
128 | } | ||
129 | |||
130 | static void unmanaged_handle_associate(struct wl_listener *listener, void *data) { | ||
131 | struct sway_xwayland_unmanaged *surface = | ||
132 | wl_container_of(listener, surface, associate); | ||
133 | struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface; | ||
134 | wl_signal_add(&xsurface->surface->events.map, &surface->map); | ||
135 | surface->map.notify = unmanaged_handle_map; | ||
136 | wl_signal_add(&xsurface->surface->events.unmap, &surface->unmap); | ||
137 | surface->unmap.notify = unmanaged_handle_unmap; | ||
138 | } | ||
139 | |||
140 | static void unmanaged_handle_dissociate(struct wl_listener *listener, void *data) { | ||
141 | struct sway_xwayland_unmanaged *surface = | ||
142 | wl_container_of(listener, surface, dissociate); | ||
143 | wl_list_remove(&surface->map.link); | ||
144 | wl_list_remove(&surface->unmap.link); | ||
145 | } | ||
146 | |||
128 | static void unmanaged_handle_destroy(struct wl_listener *listener, void *data) { | 147 | static void unmanaged_handle_destroy(struct wl_listener *listener, void *data) { |
129 | struct sway_xwayland_unmanaged *surface = | 148 | struct sway_xwayland_unmanaged *surface = |
130 | wl_container_of(listener, surface, destroy); | 149 | wl_container_of(listener, surface, destroy); |
131 | wl_list_remove(&surface->request_configure.link); | 150 | wl_list_remove(&surface->request_configure.link); |
132 | wl_list_remove(&surface->map.link); | 151 | wl_list_remove(&surface->associate.link); |
133 | wl_list_remove(&surface->unmap.link); | 152 | wl_list_remove(&surface->dissociate.link); |
134 | wl_list_remove(&surface->destroy.link); | 153 | wl_list_remove(&surface->destroy.link); |
135 | wl_list_remove(&surface->override_redirect.link); | 154 | wl_list_remove(&surface->override_redirect.link); |
155 | wl_list_remove(&surface->request_activate.link); | ||
136 | free(surface); | 156 | free(surface); |
137 | } | 157 | } |
138 | 158 | ||
139 | static void handle_map(struct wl_listener *listener, void *data); | 159 | static void handle_map(struct wl_listener *listener, void *data); |
160 | static void handle_associate(struct wl_listener *listener, void *data); | ||
140 | 161 | ||
141 | struct sway_xwayland_view *create_xwayland_view(struct wlr_xwayland_surface *xsurface); | 162 | struct sway_xwayland_view *create_xwayland_view(struct wlr_xwayland_surface *xsurface); |
142 | 163 | ||
@@ -145,14 +166,22 @@ static void unmanaged_handle_override_redirect(struct wl_listener *listener, voi | |||
145 | wl_container_of(listener, surface, override_redirect); | 166 | wl_container_of(listener, surface, override_redirect); |
146 | struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface; | 167 | struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface; |
147 | 168 | ||
148 | bool mapped = xsurface->mapped; | 169 | bool associated = xsurface->surface != NULL; |
170 | bool mapped = associated && xsurface->surface->mapped; | ||
149 | if (mapped) { | 171 | if (mapped) { |
150 | unmanaged_handle_unmap(&surface->unmap, NULL); | 172 | unmanaged_handle_unmap(&surface->unmap, NULL); |
151 | } | 173 | } |
174 | if (associated) { | ||
175 | unmanaged_handle_dissociate(&surface->dissociate, NULL); | ||
176 | } | ||
152 | 177 | ||
153 | unmanaged_handle_destroy(&surface->destroy, NULL); | 178 | unmanaged_handle_destroy(&surface->destroy, NULL); |
154 | xsurface->data = NULL; | 179 | xsurface->data = NULL; |
180 | |||
155 | struct sway_xwayland_view *xwayland_view = create_xwayland_view(xsurface); | 181 | struct sway_xwayland_view *xwayland_view = create_xwayland_view(xsurface); |
182 | if (associated) { | ||
183 | handle_associate(&xwayland_view->associate, NULL); | ||
184 | } | ||
156 | if (mapped) { | 185 | if (mapped) { |
157 | handle_map(&xwayland_view->map, xsurface); | 186 | handle_map(&xwayland_view->map, xsurface); |
158 | } | 187 | } |
@@ -172,14 +201,16 @@ static struct sway_xwayland_unmanaged *create_unmanaged( | |||
172 | wl_signal_add(&xsurface->events.request_configure, | 201 | wl_signal_add(&xsurface->events.request_configure, |
173 | &surface->request_configure); | 202 | &surface->request_configure); |
174 | surface->request_configure.notify = unmanaged_handle_request_configure; | 203 | surface->request_configure.notify = unmanaged_handle_request_configure; |
175 | wl_signal_add(&xsurface->events.map, &surface->map); | 204 | wl_signal_add(&xsurface->events.associate, &surface->associate); |
176 | surface->map.notify = unmanaged_handle_map; | 205 | surface->associate.notify = unmanaged_handle_associate; |
177 | wl_signal_add(&xsurface->events.unmap, &surface->unmap); | 206 | wl_signal_add(&xsurface->events.dissociate, &surface->dissociate); |
178 | surface->unmap.notify = unmanaged_handle_unmap; | 207 | surface->dissociate.notify = unmanaged_handle_dissociate; |
179 | wl_signal_add(&xsurface->events.destroy, &surface->destroy); | 208 | wl_signal_add(&xsurface->events.destroy, &surface->destroy); |
180 | surface->destroy.notify = unmanaged_handle_destroy; | 209 | surface->destroy.notify = unmanaged_handle_destroy; |
181 | wl_signal_add(&xsurface->events.set_override_redirect, &surface->override_redirect); | 210 | wl_signal_add(&xsurface->events.set_override_redirect, &surface->override_redirect); |
182 | surface->override_redirect.notify = unmanaged_handle_override_redirect; | 211 | surface->override_redirect.notify = unmanaged_handle_override_redirect; |
212 | wl_signal_add(&xsurface->events.request_activate, &surface->request_activate); | ||
213 | surface->request_activate.notify = unmanaged_handle_request_activate; | ||
183 | 214 | ||
184 | return surface; | 215 | return surface; |
185 | } | 216 | } |
@@ -258,6 +289,7 @@ static void set_activated(struct sway_view *view, bool activated) { | |||
258 | } | 289 | } |
259 | 290 | ||
260 | wlr_xwayland_surface_activate(surface, activated); | 291 | wlr_xwayland_surface_activate(surface, activated); |
292 | wlr_xwayland_surface_restack(surface, NULL, XCB_STACK_MODE_ABOVE); | ||
261 | } | 293 | } |
262 | 294 | ||
263 | static void set_tiled(struct sway_view *view, bool tiled) { | 295 | static void set_tiled(struct sway_view *view, bool tiled) { |
@@ -297,7 +329,7 @@ static bool wants_floating(struct sway_view *view) { | |||
297 | } | 329 | } |
298 | } | 330 | } |
299 | 331 | ||
300 | struct wlr_xwayland_surface_size_hints *size_hints = surface->size_hints; | 332 | xcb_size_hints_t *size_hints = surface->size_hints; |
301 | if (size_hints != NULL && | 333 | if (size_hints != NULL && |
302 | size_hints->min_width > 0 && size_hints->min_height > 0 && | 334 | size_hints->min_width > 0 && size_hints->min_height > 0 && |
303 | (size_hints->max_width == size_hints->min_width || | 335 | (size_hints->max_width == size_hints->min_width || |
@@ -351,7 +383,7 @@ static void destroy(struct sway_view *view) { | |||
351 | static void get_constraints(struct sway_view *view, double *min_width, | 383 | static void get_constraints(struct sway_view *view, double *min_width, |
352 | double *max_width, double *min_height, double *max_height) { | 384 | double *max_width, double *min_height, double *max_height) { |
353 | struct wlr_xwayland_surface *surface = view->wlr_xwayland_surface; | 385 | struct wlr_xwayland_surface *surface = view->wlr_xwayland_surface; |
354 | struct wlr_xwayland_surface_size_hints *size_hints = surface->size_hints; | 386 | xcb_size_hints_t *size_hints = surface->size_hints; |
355 | 387 | ||
356 | if (size_hints == NULL) { | 388 | if (size_hints == NULL) { |
357 | *min_width = DBL_MIN; | 389 | *min_width = DBL_MIN; |
@@ -381,17 +413,6 @@ static const struct sway_view_impl view_impl = { | |||
381 | .destroy = destroy, | 413 | .destroy = destroy, |
382 | }; | 414 | }; |
383 | 415 | ||
384 | static void get_geometry(struct sway_view *view, struct wlr_box *box) { | ||
385 | box->x = box->y = 0; | ||
386 | if (view->surface) { | ||
387 | box->width = view->surface->current.width; | ||
388 | box->height = view->surface->current.height; | ||
389 | } else { | ||
390 | box->width = 0; | ||
391 | box->height = 0; | ||
392 | } | ||
393 | } | ||
394 | |||
395 | static void handle_commit(struct wl_listener *listener, void *data) { | 416 | static void handle_commit(struct wl_listener *listener, void *data) { |
396 | struct sway_xwayland_view *xwayland_view = | 417 | struct sway_xwayland_view *xwayland_view = |
397 | wl_container_of(listener, xwayland_view, commit); | 418 | wl_container_of(listener, xwayland_view, commit); |
@@ -399,33 +420,38 @@ static void handle_commit(struct wl_listener *listener, void *data) { | |||
399 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; | 420 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; |
400 | struct wlr_surface_state *state = &xsurface->surface->current; | 421 | struct wlr_surface_state *state = &xsurface->surface->current; |
401 | 422 | ||
423 | struct wlr_box new_geo = {0}; | ||
424 | new_geo.width = state->width; | ||
425 | new_geo.height = state->height; | ||
426 | |||
427 | bool new_size = new_geo.width != view->geometry.width || | ||
428 | new_geo.height != view->geometry.height; | ||
429 | |||
430 | if (new_size) { | ||
431 | // The client changed its surface size in this commit. For floating | ||
432 | // containers, we resize the container to match. For tiling containers, | ||
433 | // we only recenter the surface. | ||
434 | memcpy(&view->geometry, &new_geo, sizeof(struct wlr_box)); | ||
435 | if (container_is_floating(view->container)) { | ||
436 | view_update_size(view); | ||
437 | transaction_commit_dirty_client(); | ||
438 | } | ||
439 | |||
440 | view_center_and_clip_surface(view); | ||
441 | } | ||
442 | |||
402 | if (view->container->node.instruction) { | 443 | if (view->container->node.instruction) { |
403 | get_geometry(view, &view->geometry); | 444 | bool successful = transaction_notify_view_ready_by_geometry(view, |
404 | transaction_notify_view_ready_by_geometry(view, | ||
405 | xsurface->x, xsurface->y, state->width, state->height); | 445 | xsurface->x, xsurface->y, state->width, state->height); |
406 | } else { | 446 | |
407 | struct wlr_box new_geo; | 447 | // If we saved the view and this commit isn't what we're looking for |
408 | get_geometry(view, &new_geo); | 448 | // that means the user will never actually see the buffers submitted to |
409 | 449 | // us here. Just send frame done events to these surfaces so they can | |
410 | if ((new_geo.width != view->geometry.width || | 450 | // commit another time for us. |
411 | new_geo.height != view->geometry.height || | 451 | if (view->saved_surface_tree && !successful) { |
412 | new_geo.x != view->geometry.x || | 452 | view_send_frame_done(view); |
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 | } | 453 | } |
426 | } | 454 | } |
427 | |||
428 | view_damage_from(view); | ||
429 | } | 455 | } |
430 | 456 | ||
431 | static void handle_destroy(struct wl_listener *listener, void *data) { | 457 | static void handle_destroy(struct wl_listener *listener, void *data) { |
@@ -438,6 +464,8 @@ static void handle_destroy(struct wl_listener *listener, void *data) { | |||
438 | wl_list_remove(&xwayland_view->commit.link); | 464 | wl_list_remove(&xwayland_view->commit.link); |
439 | } | 465 | } |
440 | 466 | ||
467 | xwayland_view->view.wlr_xwayland_surface = NULL; | ||
468 | |||
441 | wl_list_remove(&xwayland_view->destroy.link); | 469 | wl_list_remove(&xwayland_view->destroy.link); |
442 | wl_list_remove(&xwayland_view->request_configure.link); | 470 | wl_list_remove(&xwayland_view->request_configure.link); |
443 | wl_list_remove(&xwayland_view->request_fullscreen.link); | 471 | wl_list_remove(&xwayland_view->request_fullscreen.link); |
@@ -448,11 +476,12 @@ static void handle_destroy(struct wl_listener *listener, void *data) { | |||
448 | wl_list_remove(&xwayland_view->set_title.link); | 476 | wl_list_remove(&xwayland_view->set_title.link); |
449 | wl_list_remove(&xwayland_view->set_class.link); | 477 | wl_list_remove(&xwayland_view->set_class.link); |
450 | wl_list_remove(&xwayland_view->set_role.link); | 478 | wl_list_remove(&xwayland_view->set_role.link); |
479 | wl_list_remove(&xwayland_view->set_startup_id.link); | ||
451 | wl_list_remove(&xwayland_view->set_window_type.link); | 480 | wl_list_remove(&xwayland_view->set_window_type.link); |
452 | wl_list_remove(&xwayland_view->set_hints.link); | 481 | wl_list_remove(&xwayland_view->set_hints.link); |
453 | wl_list_remove(&xwayland_view->set_decorations.link); | 482 | wl_list_remove(&xwayland_view->set_decorations.link); |
454 | wl_list_remove(&xwayland_view->map.link); | 483 | wl_list_remove(&xwayland_view->associate.link); |
455 | wl_list_remove(&xwayland_view->unmap.link); | 484 | wl_list_remove(&xwayland_view->dissociate.link); |
456 | wl_list_remove(&xwayland_view->override_redirect.link); | 485 | wl_list_remove(&xwayland_view->override_redirect.link); |
457 | view_begin_destroy(&xwayland_view->view); | 486 | view_begin_destroy(&xwayland_view->view); |
458 | } | 487 | } |
@@ -466,16 +495,28 @@ static void handle_unmap(struct wl_listener *listener, void *data) { | |||
466 | return; | 495 | return; |
467 | } | 496 | } |
468 | 497 | ||
498 | wl_list_remove(&xwayland_view->commit.link); | ||
499 | wl_list_remove(&xwayland_view->surface_tree_destroy.link); | ||
500 | |||
501 | if (xwayland_view->surface_tree) { | ||
502 | wlr_scene_node_destroy(&xwayland_view->surface_tree->node); | ||
503 | xwayland_view->surface_tree = NULL; | ||
504 | } | ||
505 | |||
469 | view_unmap(view); | 506 | view_unmap(view); |
507 | } | ||
470 | 508 | ||
471 | wl_list_remove(&xwayland_view->commit.link); | 509 | static void handle_surface_tree_destroy(struct wl_listener *listener, void *data) { |
510 | struct sway_xwayland_view *xwayland_view = wl_container_of(listener, xwayland_view, | ||
511 | surface_tree_destroy); | ||
512 | xwayland_view->surface_tree = NULL; | ||
472 | } | 513 | } |
473 | 514 | ||
474 | static void handle_map(struct wl_listener *listener, void *data) { | 515 | static void handle_map(struct wl_listener *listener, void *data) { |
475 | struct sway_xwayland_view *xwayland_view = | 516 | struct sway_xwayland_view *xwayland_view = |
476 | wl_container_of(listener, xwayland_view, map); | 517 | wl_container_of(listener, xwayland_view, map); |
477 | struct wlr_xwayland_surface *xsurface = data; | ||
478 | struct sway_view *view = &xwayland_view->view; | 518 | struct sway_view *view = &xwayland_view->view; |
519 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; | ||
479 | 520 | ||
480 | view->natural_width = xsurface->width; | 521 | view->natural_width = xsurface->width; |
481 | view->natural_height = xsurface->height; | 522 | view->natural_height = xsurface->height; |
@@ -488,23 +529,42 @@ static void handle_map(struct wl_listener *listener, void *data) { | |||
488 | // Put it back into the tree | 529 | // Put it back into the tree |
489 | view_map(view, xsurface->surface, xsurface->fullscreen, NULL, false); | 530 | view_map(view, xsurface->surface, xsurface->fullscreen, NULL, false); |
490 | 531 | ||
532 | xwayland_view->surface_tree = wlr_scene_subsurface_tree_create( | ||
533 | xwayland_view->view.content_tree, xsurface->surface); | ||
534 | |||
535 | if (xwayland_view->surface_tree) { | ||
536 | xwayland_view->surface_tree_destroy.notify = handle_surface_tree_destroy; | ||
537 | wl_signal_add(&xwayland_view->surface_tree->node.events.destroy, | ||
538 | &xwayland_view->surface_tree_destroy); | ||
539 | } | ||
540 | |||
491 | transaction_commit_dirty(); | 541 | transaction_commit_dirty(); |
492 | } | 542 | } |
493 | 543 | ||
544 | static void handle_dissociate(struct wl_listener *listener, void *data); | ||
545 | |||
494 | static void handle_override_redirect(struct wl_listener *listener, void *data) { | 546 | static void handle_override_redirect(struct wl_listener *listener, void *data) { |
495 | struct sway_xwayland_view *xwayland_view = | 547 | struct sway_xwayland_view *xwayland_view = |
496 | wl_container_of(listener, xwayland_view, override_redirect); | 548 | wl_container_of(listener, xwayland_view, override_redirect); |
497 | struct wlr_xwayland_surface *xsurface = data; | ||
498 | struct sway_view *view = &xwayland_view->view; | 549 | struct sway_view *view = &xwayland_view->view; |
550 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; | ||
499 | 551 | ||
500 | bool mapped = xsurface->mapped; | 552 | bool associated = xsurface->surface != NULL; |
553 | bool mapped = associated && xsurface->surface->mapped; | ||
501 | if (mapped) { | 554 | if (mapped) { |
502 | handle_unmap(&xwayland_view->unmap, NULL); | 555 | handle_unmap(&xwayland_view->unmap, NULL); |
503 | } | 556 | } |
557 | if (associated) { | ||
558 | handle_dissociate(&xwayland_view->dissociate, NULL); | ||
559 | } | ||
504 | 560 | ||
505 | handle_destroy(&xwayland_view->destroy, view); | 561 | handle_destroy(&xwayland_view->destroy, view); |
506 | xsurface->data = NULL; | 562 | xsurface->data = NULL; |
563 | |||
507 | struct sway_xwayland_unmanaged *unmanaged = create_unmanaged(xsurface); | 564 | struct sway_xwayland_unmanaged *unmanaged = create_unmanaged(xsurface); |
565 | if (associated) { | ||
566 | unmanaged_handle_associate(&unmanaged->associate, NULL); | ||
567 | } | ||
508 | if (mapped) { | 568 | if (mapped) { |
509 | unmanaged_handle_map(&unmanaged->map, xsurface); | 569 | unmanaged_handle_map(&unmanaged->map, xsurface); |
510 | } | 570 | } |
@@ -516,7 +576,7 @@ static void handle_request_configure(struct wl_listener *listener, void *data) { | |||
516 | struct wlr_xwayland_surface_configure_event *ev = data; | 576 | struct wlr_xwayland_surface_configure_event *ev = data; |
517 | struct sway_view *view = &xwayland_view->view; | 577 | struct sway_view *view = &xwayland_view->view; |
518 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; | 578 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; |
519 | if (!xsurface->mapped) { | 579 | if (xsurface->surface == NULL || !xsurface->surface->mapped) { |
520 | wlr_xwayland_surface_configure(xsurface, ev->x, ev->y, | 580 | wlr_xwayland_surface_configure(xsurface, ev->x, ev->y, |
521 | ev->width, ev->height); | 581 | ev->width, ev->height); |
522 | return; | 582 | return; |
@@ -527,10 +587,10 @@ static void handle_request_configure(struct wl_listener *listener, void *data) { | |||
527 | view->natural_height = ev->height; | 587 | view->natural_height = ev->height; |
528 | container_floating_resize_and_center(view->container); | 588 | container_floating_resize_and_center(view->container); |
529 | 589 | ||
530 | configure(view, view->container->content_x, | 590 | configure(view, view->container->pending.content_x, |
531 | view->container->content_y, | 591 | view->container->pending.content_y, |
532 | view->container->content_width, | 592 | view->container->pending.content_width, |
533 | view->container->content_height); | 593 | view->container->pending.content_height); |
534 | node_set_dirty(&view->container->node); | 594 | node_set_dirty(&view->container->node); |
535 | } else { | 595 | } else { |
536 | configure(view, view->container->current.content_x, | 596 | configure(view, view->container->current.content_x, |
@@ -545,7 +605,7 @@ static void handle_request_fullscreen(struct wl_listener *listener, void *data) | |||
545 | wl_container_of(listener, xwayland_view, request_fullscreen); | 605 | wl_container_of(listener, xwayland_view, request_fullscreen); |
546 | struct sway_view *view = &xwayland_view->view; | 606 | struct sway_view *view = &xwayland_view->view; |
547 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; | 607 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; |
548 | if (!xsurface->mapped) { | 608 | if (xsurface->surface == NULL || !xsurface->surface->mapped) { |
549 | return; | 609 | return; |
550 | } | 610 | } |
551 | container_set_fullscreen(view->container, xsurface->fullscreen); | 611 | container_set_fullscreen(view->container, xsurface->fullscreen); |
@@ -559,7 +619,7 @@ static void handle_request_minimize(struct wl_listener *listener, void *data) { | |||
559 | wl_container_of(listener, xwayland_view, request_minimize); | 619 | wl_container_of(listener, xwayland_view, request_minimize); |
560 | struct sway_view *view = &xwayland_view->view; | 620 | struct sway_view *view = &xwayland_view->view; |
561 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; | 621 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; |
562 | if (!xsurface->mapped) { | 622 | if (xsurface->surface == NULL || !xsurface->surface->mapped) { |
563 | return; | 623 | return; |
564 | } | 624 | } |
565 | 625 | ||
@@ -574,10 +634,11 @@ static void handle_request_move(struct wl_listener *listener, void *data) { | |||
574 | wl_container_of(listener, xwayland_view, request_move); | 634 | wl_container_of(listener, xwayland_view, request_move); |
575 | struct sway_view *view = &xwayland_view->view; | 635 | struct sway_view *view = &xwayland_view->view; |
576 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; | 636 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; |
577 | if (!xsurface->mapped) { | 637 | if (xsurface->surface == NULL || !xsurface->surface->mapped) { |
578 | return; | 638 | return; |
579 | } | 639 | } |
580 | if (!container_is_floating(view->container)) { | 640 | if (!container_is_floating(view->container) || |
641 | view->container->pending.fullscreen_mode) { | ||
581 | return; | 642 | return; |
582 | } | 643 | } |
583 | struct sway_seat *seat = input_manager_current_seat(); | 644 | struct sway_seat *seat = input_manager_current_seat(); |
@@ -589,7 +650,7 @@ static void handle_request_resize(struct wl_listener *listener, void *data) { | |||
589 | wl_container_of(listener, xwayland_view, request_resize); | 650 | wl_container_of(listener, xwayland_view, request_resize); |
590 | struct sway_view *view = &xwayland_view->view; | 651 | struct sway_view *view = &xwayland_view->view; |
591 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; | 652 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; |
592 | if (!xsurface->mapped) { | 653 | if (xsurface->surface == NULL || !xsurface->surface->mapped) { |
593 | return; | 654 | return; |
594 | } | 655 | } |
595 | if (!container_is_floating(view->container)) { | 656 | if (!container_is_floating(view->container)) { |
@@ -605,10 +666,10 @@ static void handle_request_activate(struct wl_listener *listener, void *data) { | |||
605 | wl_container_of(listener, xwayland_view, request_activate); | 666 | wl_container_of(listener, xwayland_view, request_activate); |
606 | struct sway_view *view = &xwayland_view->view; | 667 | struct sway_view *view = &xwayland_view->view; |
607 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; | 668 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; |
608 | if (!xsurface->mapped) { | 669 | if (xsurface->surface == NULL || !xsurface->surface->mapped) { |
609 | return; | 670 | return; |
610 | } | 671 | } |
611 | view_request_activate(view); | 672 | view_request_activate(view, NULL); |
612 | 673 | ||
613 | transaction_commit_dirty(); | 674 | transaction_commit_dirty(); |
614 | } | 675 | } |
@@ -618,7 +679,7 @@ static void handle_set_title(struct wl_listener *listener, void *data) { | |||
618 | wl_container_of(listener, xwayland_view, set_title); | 679 | wl_container_of(listener, xwayland_view, set_title); |
619 | struct sway_view *view = &xwayland_view->view; | 680 | struct sway_view *view = &xwayland_view->view; |
620 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; | 681 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; |
621 | if (!xsurface->mapped) { | 682 | if (xsurface->surface == NULL || !xsurface->surface->mapped) { |
622 | return; | 683 | return; |
623 | } | 684 | } |
624 | view_update_title(view, false); | 685 | view_update_title(view, false); |
@@ -630,7 +691,7 @@ static void handle_set_class(struct wl_listener *listener, void *data) { | |||
630 | wl_container_of(listener, xwayland_view, set_class); | 691 | wl_container_of(listener, xwayland_view, set_class); |
631 | struct sway_view *view = &xwayland_view->view; | 692 | struct sway_view *view = &xwayland_view->view; |
632 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; | 693 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; |
633 | if (!xsurface->mapped) { | 694 | if (xsurface->surface == NULL || !xsurface->surface->mapped) { |
634 | return; | 695 | return; |
635 | } | 696 | } |
636 | view_execute_criteria(view); | 697 | view_execute_criteria(view); |
@@ -641,18 +702,43 @@ static void handle_set_role(struct wl_listener *listener, void *data) { | |||
641 | wl_container_of(listener, xwayland_view, set_role); | 702 | wl_container_of(listener, xwayland_view, set_role); |
642 | struct sway_view *view = &xwayland_view->view; | 703 | struct sway_view *view = &xwayland_view->view; |
643 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; | 704 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; |
644 | if (!xsurface->mapped) { | 705 | if (xsurface->surface == NULL || !xsurface->surface->mapped) { |
645 | return; | 706 | return; |
646 | } | 707 | } |
647 | view_execute_criteria(view); | 708 | view_execute_criteria(view); |
648 | } | 709 | } |
649 | 710 | ||
711 | static void handle_set_startup_id(struct wl_listener *listener, void *data) { | ||
712 | struct sway_xwayland_view *xwayland_view = | ||
713 | wl_container_of(listener, xwayland_view, set_startup_id); | ||
714 | struct sway_view *view = &xwayland_view->view; | ||
715 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; | ||
716 | if (xsurface->startup_id == NULL) { | ||
717 | return; | ||
718 | } | ||
719 | |||
720 | struct wlr_xdg_activation_token_v1 *token = | ||
721 | wlr_xdg_activation_v1_find_token( | ||
722 | server.xdg_activation_v1, xsurface->startup_id); | ||
723 | if (token == NULL) { | ||
724 | // Tried to activate with an unknown or expired token | ||
725 | return; | ||
726 | } | ||
727 | |||
728 | struct launcher_ctx *ctx = token->data; | ||
729 | if (token->data == NULL) { | ||
730 | // TODO: support external launchers in X | ||
731 | return; | ||
732 | } | ||
733 | view_assign_ctx(view, ctx); | ||
734 | } | ||
735 | |||
650 | static void handle_set_window_type(struct wl_listener *listener, void *data) { | 736 | static void handle_set_window_type(struct wl_listener *listener, void *data) { |
651 | struct sway_xwayland_view *xwayland_view = | 737 | struct sway_xwayland_view *xwayland_view = |
652 | wl_container_of(listener, xwayland_view, set_window_type); | 738 | wl_container_of(listener, xwayland_view, set_window_type); |
653 | struct sway_view *view = &xwayland_view->view; | 739 | struct sway_view *view = &xwayland_view->view; |
654 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; | 740 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; |
655 | if (!xsurface->mapped) { | 741 | if (xsurface->surface == NULL || !xsurface->surface->mapped) { |
656 | return; | 742 | return; |
657 | } | 743 | } |
658 | view_execute_criteria(view); | 744 | view_execute_criteria(view); |
@@ -663,20 +749,39 @@ static void handle_set_hints(struct wl_listener *listener, void *data) { | |||
663 | wl_container_of(listener, xwayland_view, set_hints); | 749 | wl_container_of(listener, xwayland_view, set_hints); |
664 | struct sway_view *view = &xwayland_view->view; | 750 | struct sway_view *view = &xwayland_view->view; |
665 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; | 751 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; |
666 | if (!xsurface->mapped) { | 752 | if (xsurface->surface == NULL || !xsurface->surface->mapped) { |
667 | return; | 753 | return; |
668 | } | 754 | } |
669 | if (!xsurface->hints_urgency && view->urgent_timer) { | 755 | const bool hints_urgency = xcb_icccm_wm_hints_get_urgency(xsurface->hints); |
756 | if (!hints_urgency && view->urgent_timer) { | ||
670 | // The view is in the timeout period. We'll ignore the request to | 757 | // 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 | 758 | // unset urgency so that the view remains urgent until the timer clears |
672 | // it. | 759 | // it. |
673 | return; | 760 | return; |
674 | } | 761 | } |
675 | if (view->allow_request_urgent) { | 762 | if (view->allow_request_urgent) { |
676 | view_set_urgent(view, (bool)xsurface->hints_urgency); | 763 | view_set_urgent(view, hints_urgency); |
677 | } | 764 | } |
678 | } | 765 | } |
679 | 766 | ||
767 | static void handle_associate(struct wl_listener *listener, void *data) { | ||
768 | struct sway_xwayland_view *xwayland_view = | ||
769 | wl_container_of(listener, xwayland_view, associate); | ||
770 | struct wlr_xwayland_surface *xsurface = | ||
771 | xwayland_view->view.wlr_xwayland_surface; | ||
772 | wl_signal_add(&xsurface->surface->events.unmap, &xwayland_view->unmap); | ||
773 | xwayland_view->unmap.notify = handle_unmap; | ||
774 | wl_signal_add(&xsurface->surface->events.map, &xwayland_view->map); | ||
775 | xwayland_view->map.notify = handle_map; | ||
776 | } | ||
777 | |||
778 | static void handle_dissociate(struct wl_listener *listener, void *data) { | ||
779 | struct sway_xwayland_view *xwayland_view = | ||
780 | wl_container_of(listener, xwayland_view, dissociate); | ||
781 | wl_list_remove(&xwayland_view->map.link); | ||
782 | wl_list_remove(&xwayland_view->unmap.link); | ||
783 | } | ||
784 | |||
680 | struct sway_view *view_from_wlr_xwayland_surface( | 785 | struct sway_view *view_from_wlr_xwayland_surface( |
681 | struct wlr_xwayland_surface *xsurface) { | 786 | struct wlr_xwayland_surface *xsurface) { |
682 | return xsurface->data; | 787 | return xsurface->data; |
@@ -692,7 +797,10 @@ struct sway_xwayland_view *create_xwayland_view(struct wlr_xwayland_surface *xsu | |||
692 | return NULL; | 797 | return NULL; |
693 | } | 798 | } |
694 | 799 | ||
695 | view_init(&xwayland_view->view, SWAY_VIEW_XWAYLAND, &view_impl); | 800 | if (!view_init(&xwayland_view->view, SWAY_VIEW_XWAYLAND, &view_impl)) { |
801 | free(xwayland_view); | ||
802 | return NULL; | ||
803 | } | ||
696 | xwayland_view->view.wlr_xwayland_surface = xsurface; | 804 | xwayland_view->view.wlr_xwayland_surface = xsurface; |
697 | 805 | ||
698 | wl_signal_add(&xsurface->events.destroy, &xwayland_view->destroy); | 806 | wl_signal_add(&xsurface->events.destroy, &xwayland_view->destroy); |
@@ -731,6 +839,10 @@ struct sway_xwayland_view *create_xwayland_view(struct wlr_xwayland_surface *xsu | |||
731 | wl_signal_add(&xsurface->events.set_role, &xwayland_view->set_role); | 839 | wl_signal_add(&xsurface->events.set_role, &xwayland_view->set_role); |
732 | xwayland_view->set_role.notify = handle_set_role; | 840 | xwayland_view->set_role.notify = handle_set_role; |
733 | 841 | ||
842 | wl_signal_add(&xsurface->events.set_startup_id, | ||
843 | &xwayland_view->set_startup_id); | ||
844 | xwayland_view->set_startup_id.notify = handle_set_startup_id; | ||
845 | |||
734 | wl_signal_add(&xsurface->events.set_window_type, | 846 | wl_signal_add(&xsurface->events.set_window_type, |
735 | &xwayland_view->set_window_type); | 847 | &xwayland_view->set_window_type); |
736 | xwayland_view->set_window_type.notify = handle_set_window_type; | 848 | xwayland_view->set_window_type.notify = handle_set_window_type; |
@@ -742,11 +854,11 @@ struct sway_xwayland_view *create_xwayland_view(struct wlr_xwayland_surface *xsu | |||
742 | &xwayland_view->set_decorations); | 854 | &xwayland_view->set_decorations); |
743 | xwayland_view->set_decorations.notify = handle_set_decorations; | 855 | xwayland_view->set_decorations.notify = handle_set_decorations; |
744 | 856 | ||
745 | wl_signal_add(&xsurface->events.unmap, &xwayland_view->unmap); | 857 | wl_signal_add(&xsurface->events.associate, &xwayland_view->associate); |
746 | xwayland_view->unmap.notify = handle_unmap; | 858 | xwayland_view->associate.notify = handle_associate; |
747 | 859 | ||
748 | wl_signal_add(&xsurface->events.map, &xwayland_view->map); | 860 | wl_signal_add(&xsurface->events.dissociate, &xwayland_view->dissociate); |
749 | xwayland_view->map.notify = handle_map; | 861 | xwayland_view->dissociate.notify = handle_dissociate; |
750 | 862 | ||
751 | wl_signal_add(&xsurface->events.set_override_redirect, | 863 | wl_signal_add(&xsurface->events.set_override_redirect, |
752 | &xwayland_view->override_redirect); | 864 | &xwayland_view->override_redirect); |
diff --git a/sway/input/cursor.c b/sway/input/cursor.c index fa604426..0b4a36d9 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c | |||
@@ -1,14 +1,14 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <assert.h> | 1 | #include <assert.h> |
3 | #include <math.h> | 2 | #include <math.h> |
4 | #include <libevdev/libevdev.h> | 3 | #include <libevdev/libevdev.h> |
5 | #include <linux/input-event-codes.h> | 4 | #include <linux/input-event-codes.h> |
6 | #include <errno.h> | 5 | #include <errno.h> |
6 | #include <time.h> | ||
7 | #include <strings.h> | 7 | #include <strings.h> |
8 | #include <wlr/types/wlr_box.h> | ||
9 | #include <wlr/types/wlr_cursor.h> | 8 | #include <wlr/types/wlr_cursor.h> |
10 | #include <wlr/types/wlr_idle.h> | 9 | #include <wlr/types/wlr_cursor_shape_v1.h> |
11 | #include <wlr/types/wlr_pointer.h> | 10 | #include <wlr/types/wlr_pointer.h> |
11 | #include <wlr/types/wlr_relative_pointer_v1.h> | ||
12 | #include <wlr/types/wlr_touch.h> | 12 | #include <wlr/types/wlr_touch.h> |
13 | #include <wlr/types/wlr_tablet_v2.h> | 13 | #include <wlr/types/wlr_tablet_v2.h> |
14 | #include <wlr/types/wlr_tablet_pad.h> | 14 | #include <wlr/types/wlr_tablet_pad.h> |
@@ -19,56 +19,22 @@ | |||
19 | #include "log.h" | 19 | #include "log.h" |
20 | #include "util.h" | 20 | #include "util.h" |
21 | #include "sway/commands.h" | 21 | #include "sway/commands.h" |
22 | #include "sway/desktop.h" | ||
23 | #include "sway/desktop/transaction.h" | ||
24 | #include "sway/input/cursor.h" | 22 | #include "sway/input/cursor.h" |
25 | #include "sway/input/keyboard.h" | 23 | #include "sway/input/keyboard.h" |
26 | #include "sway/input/tablet.h" | 24 | #include "sway/input/tablet.h" |
27 | #include "sway/layers.h" | 25 | #include "sway/layers.h" |
28 | #include "sway/output.h" | 26 | #include "sway/output.h" |
27 | #include "sway/scene_descriptor.h" | ||
29 | #include "sway/tree/container.h" | 28 | #include "sway/tree/container.h" |
30 | #include "sway/tree/root.h" | 29 | #include "sway/tree/root.h" |
31 | #include "sway/tree/view.h" | 30 | #include "sway/tree/view.h" |
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 | ||
35 | static struct wlr_surface *layer_surface_at(struct sway_output *output, | 34 | static uint32_t get_current_time_msec(void) { |
36 | struct wl_list *layer, double ox, double oy, double *sx, double *sy) { | 35 | struct timespec now; |
37 | struct sway_layer_surface *sway_layer; | 36 | clock_gettime(CLOCK_MONOTONIC, &now); |
38 | wl_list_for_each_reverse(sway_layer, layer, link) { | 37 | return now.tv_sec * 1000 + now.tv_nsec / 1000000; |
39 | double _sx = ox - sway_layer->geo.x; | ||
40 | double _sy = oy - sway_layer->geo.y; | ||
41 | struct wlr_surface *sub = wlr_layer_surface_v1_surface_at( | ||
42 | sway_layer->layer_surface, _sx, _sy, sx, sy); | ||
43 | if (sub) { | ||
44 | return sub; | ||
45 | } | ||
46 | } | ||
47 | return NULL; | ||
48 | } | ||
49 | |||
50 | static bool surface_is_xdg_popup(struct wlr_surface *surface) { | ||
51 | if (wlr_surface_is_xdg_surface(surface)) { | ||
52 | struct wlr_xdg_surface *xdg_surface = | ||
53 | wlr_xdg_surface_from_wlr_surface(surface); | ||
54 | return xdg_surface->role == WLR_XDG_SURFACE_ROLE_POPUP; | ||
55 | } | ||
56 | return false; | ||
57 | } | ||
58 | |||
59 | static struct wlr_surface *layer_surface_popup_at(struct sway_output *output, | ||
60 | struct wl_list *layer, double ox, double oy, double *sx, double *sy) { | ||
61 | struct sway_layer_surface *sway_layer; | ||
62 | wl_list_for_each_reverse(sway_layer, layer, link) { | ||
63 | double _sx = ox - sway_layer->geo.x; | ||
64 | double _sy = oy - sway_layer->geo.y; | ||
65 | struct wlr_surface *sub = wlr_layer_surface_v1_surface_at( | ||
66 | sway_layer->layer_surface, _sx, _sy, sx, sy); | ||
67 | if (sub && surface_is_xdg_popup(sub)) { | ||
68 | return sub; | ||
69 | } | ||
70 | } | ||
71 | return NULL; | ||
72 | } | 38 | } |
73 | 39 | ||
74 | /** | 40 | /** |
@@ -78,116 +44,101 @@ static struct wlr_surface *layer_surface_popup_at(struct sway_output *output, | |||
78 | struct sway_node *node_at_coords( | 44 | struct sway_node *node_at_coords( |
79 | struct sway_seat *seat, double lx, double ly, | 45 | struct sway_seat *seat, double lx, double ly, |
80 | struct wlr_surface **surface, double *sx, double *sy) { | 46 | struct wlr_surface **surface, double *sx, double *sy) { |
81 | // check for unmanaged views first | 47 | struct wlr_scene_node *scene_node = NULL; |
82 | #if HAVE_XWAYLAND | 48 | |
83 | struct wl_list *unmanaged = &root->xwayland_unmanaged; | 49 | struct wlr_scene_node *node; |
84 | struct sway_xwayland_unmanaged *unmanaged_surface; | 50 | wl_list_for_each_reverse(node, &root->layer_tree->children, link) { |
85 | wl_list_for_each_reverse(unmanaged_surface, unmanaged, link) { | 51 | struct wlr_scene_tree *layer = wlr_scene_tree_from_node(node); |
86 | struct wlr_xwayland_surface *xsurface = | 52 | |
87 | unmanaged_surface->wlr_xwayland_surface; | 53 | bool non_interactive = scene_descriptor_try_get(&layer->node, |
88 | 54 | SWAY_SCENE_DESC_NON_INTERACTIVE); | |
89 | double _sx = lx - unmanaged_surface->lx; | 55 | if (non_interactive) { |
90 | double _sy = ly - unmanaged_surface->ly; | 56 | continue; |
91 | if (wlr_surface_point_accepts_input(xsurface->surface, _sx, _sy)) { | 57 | } |
92 | *surface = xsurface->surface; | 58 | |
93 | *sx = _sx; | 59 | scene_node = wlr_scene_node_at(&layer->node, lx, ly, sx, sy); |
94 | *sy = _sy; | 60 | if (scene_node) { |
95 | return NULL; | 61 | break; |
96 | } | 62 | } |
97 | } | 63 | } |
64 | |||
65 | if (scene_node) { | ||
66 | // determine what wlr_surface we clicked on | ||
67 | if (scene_node->type == WLR_SCENE_NODE_BUFFER) { | ||
68 | struct wlr_scene_buffer *scene_buffer = | ||
69 | wlr_scene_buffer_from_node(scene_node); | ||
70 | struct wlr_scene_surface *scene_surface = | ||
71 | wlr_scene_surface_try_from_buffer(scene_buffer); | ||
72 | |||
73 | if (scene_surface) { | ||
74 | *surface = scene_surface->surface; | ||
75 | } | ||
76 | } | ||
77 | |||
78 | // determine what container we clicked on | ||
79 | struct wlr_scene_node *current = scene_node; | ||
80 | while (true) { | ||
81 | struct sway_container *con = scene_descriptor_try_get(current, | ||
82 | SWAY_SCENE_DESC_CONTAINER); | ||
83 | |||
84 | if (!con) { | ||
85 | struct sway_view *view = scene_descriptor_try_get(current, | ||
86 | SWAY_SCENE_DESC_VIEW); | ||
87 | if (view) { | ||
88 | con = view->container; | ||
89 | } | ||
90 | } | ||
91 | |||
92 | if (!con) { | ||
93 | struct sway_popup_desc *popup = | ||
94 | scene_descriptor_try_get(current, SWAY_SCENE_DESC_POPUP); | ||
95 | if (popup && popup->view) { | ||
96 | con = popup->view->container; | ||
97 | } | ||
98 | } | ||
99 | |||
100 | if (con && (!con->view || con->view->surface)) { | ||
101 | return &con->node; | ||
102 | } | ||
103 | |||
104 | if (scene_descriptor_try_get(current, SWAY_SCENE_DESC_LAYER_SHELL)) { | ||
105 | // We don't want to feed through the current workspace on | ||
106 | // layer shells | ||
107 | return NULL; | ||
108 | } | ||
109 | |||
110 | #if WLR_HAS_XWAYLAND | ||
111 | if (scene_descriptor_try_get(current, SWAY_SCENE_DESC_XWAYLAND_UNMANAGED)) { | ||
112 | return NULL; | ||
113 | } | ||
98 | #endif | 114 | #endif |
99 | // find the output the cursor is on | 115 | |
116 | if (!current->parent) { | ||
117 | break; | ||
118 | } | ||
119 | |||
120 | current = ¤t->parent->node; | ||
121 | } | ||
122 | } | ||
123 | |||
124 | // if we aren't on a container, determine what workspace we are on | ||
100 | struct wlr_output *wlr_output = wlr_output_layout_output_at( | 125 | struct wlr_output *wlr_output = wlr_output_layout_output_at( |
101 | root->output_layout, lx, ly); | 126 | root->output_layout, lx, ly); |
102 | if (wlr_output == NULL) { | 127 | if (wlr_output == NULL) { |
103 | return NULL; | 128 | return NULL; |
104 | } | 129 | } |
130 | |||
105 | struct sway_output *output = wlr_output->data; | 131 | struct sway_output *output = wlr_output->data; |
106 | if (!output || !output->enabled) { | 132 | if (!output || !output->enabled) { |
107 | // output is being destroyed or is being enabled | 133 | // output is being destroyed or is being enabled |
108 | return NULL; | 134 | return NULL; |
109 | } | 135 | } |
110 | double ox = lx, oy = ly; | ||
111 | wlr_output_layout_output_coords(root->output_layout, wlr_output, &ox, &oy); | ||
112 | 136 | ||
113 | if (root->fullscreen_global) { | ||
114 | // Try fullscreen container | ||
115 | struct sway_container *con = tiling_container_at( | ||
116 | &root->fullscreen_global->node, lx, ly, surface, sx, sy); | ||
117 | if (con) { | ||
118 | return &con->node; | ||
119 | } | ||
120 | return NULL; | ||
121 | } | ||
122 | |||
123 | // find the focused workspace on the output for this seat | ||
124 | struct sway_workspace *ws = output_get_active_workspace(output); | 137 | struct sway_workspace *ws = output_get_active_workspace(output); |
125 | if (!ws) { | 138 | if (!ws) { |
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) { | ||
135 | // Try transient containers | ||
136 | for (int i = 0; i < ws->floating->length; ++i) { | ||
137 | struct sway_container *floater = ws->floating->items[i]; | ||
138 | if (container_is_transient_for(floater, ws->fullscreen)) { | ||
139 | struct sway_container *con = tiling_container_at( | ||
140 | &floater->node, lx, ly, surface, sx, sy); | ||
141 | if (con) { | ||
142 | return &con->node; | ||
143 | } | ||
144 | } | ||
145 | } | ||
146 | // Try fullscreen container | ||
147 | struct sway_container *con = | ||
148 | tiling_container_at(&ws->fullscreen->node, lx, ly, surface, sx, sy); | ||
149 | if (con) { | ||
150 | return &con->node; | ||
151 | } | ||
152 | return NULL; | ||
153 | } | ||
154 | if ((*surface = layer_surface_popup_at(output, | ||
155 | &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP], | ||
156 | ox, oy, sx, sy))) { | ||
157 | return NULL; | ||
158 | } | ||
159 | if ((*surface = layer_surface_popup_at(output, | ||
160 | &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM], | ||
161 | ox, oy, sx, sy))) { | ||
162 | return NULL; | ||
163 | } | ||
164 | if ((*surface = layer_surface_popup_at(output, | ||
165 | &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND], | ||
166 | ox, oy, sx, sy))) { | ||
167 | return NULL; | ||
168 | } | ||
169 | if ((*surface = layer_surface_at(output, | ||
170 | &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP], | ||
171 | ox, oy, sx, sy))) { | ||
172 | return NULL; | ||
173 | } | ||
174 | |||
175 | struct sway_container *c; | ||
176 | if ((c = container_at(ws, lx, ly, surface, sx, sy))) { | ||
177 | return &c->node; | ||
178 | } | ||
179 | |||
180 | if ((*surface = layer_surface_at(output, | ||
181 | &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM], | ||
182 | ox, oy, sx, sy))) { | ||
183 | return NULL; | ||
184 | } | ||
185 | if ((*surface = layer_surface_at(output, | ||
186 | &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND], | ||
187 | ox, oy, sx, sy))) { | ||
188 | return NULL; | ||
189 | } | ||
190 | |||
191 | return &ws->node; | 142 | return &ws->node; |
192 | } | 143 | } |
193 | 144 | ||
@@ -213,7 +164,7 @@ void cursor_update_image(struct sway_cursor *cursor, | |||
213 | // Try a node's resize edge | 164 | // Try a node's resize edge |
214 | enum wlr_edges edge = find_resize_edge(node->sway_container, NULL, cursor); | 165 | enum wlr_edges edge = find_resize_edge(node->sway_container, NULL, cursor); |
215 | if (edge == WLR_EDGE_NONE) { | 166 | if (edge == WLR_EDGE_NONE) { |
216 | cursor_set_image(cursor, "left_ptr", NULL); | 167 | cursor_set_image(cursor, "default", NULL); |
217 | } else if (container_is_floating(node->sway_container)) { | 168 | } else if (container_is_floating(node->sway_container)) { |
218 | cursor_set_image(cursor, wlr_xcursor_get_resize_name(edge), NULL); | 169 | cursor_set_image(cursor, wlr_xcursor_get_resize_name(edge), NULL); |
219 | } else { | 170 | } else { |
@@ -224,12 +175,12 @@ void cursor_update_image(struct sway_cursor *cursor, | |||
224 | } | 175 | } |
225 | } | 176 | } |
226 | } else { | 177 | } else { |
227 | cursor_set_image(cursor, "left_ptr", NULL); | 178 | cursor_set_image(cursor, "default", NULL); |
228 | } | 179 | } |
229 | } | 180 | } |
230 | 181 | ||
231 | static void cursor_hide(struct sway_cursor *cursor) { | 182 | static void cursor_hide(struct sway_cursor *cursor) { |
232 | wlr_cursor_set_image(cursor->cursor, NULL, 0, 0, 0, 0, 0, 0); | 183 | wlr_cursor_unset_image(cursor->cursor); |
233 | cursor->hidden = true; | 184 | cursor->hidden = true; |
234 | wlr_seat_pointer_notify_clear_focus(cursor->seat->wlr_seat); | 185 | wlr_seat_pointer_notify_clear_focus(cursor->seat->wlr_seat); |
235 | } | 186 | } |
@@ -292,7 +243,7 @@ static enum sway_input_idle_source idle_source_from_device( | |||
292 | return IDLE_SOURCE_POINTER; | 243 | return IDLE_SOURCE_POINTER; |
293 | case WLR_INPUT_DEVICE_TOUCH: | 244 | case WLR_INPUT_DEVICE_TOUCH: |
294 | return IDLE_SOURCE_TOUCH; | 245 | return IDLE_SOURCE_TOUCH; |
295 | case WLR_INPUT_DEVICE_TABLET_TOOL: | 246 | case WLR_INPUT_DEVICE_TABLET: |
296 | return IDLE_SOURCE_TABLET_TOOL; | 247 | return IDLE_SOURCE_TABLET_TOOL; |
297 | case WLR_INPUT_DEVICE_TABLET_PAD: | 248 | case WLR_INPUT_DEVICE_TABLET_PAD: |
298 | return IDLE_SOURCE_TABLET_PAD; | 249 | return IDLE_SOURCE_TABLET_PAD; |
@@ -341,7 +292,7 @@ void cursor_unhide(struct sway_cursor *cursor) { | |||
341 | wl_event_source_timer_update(cursor->hide_source, cursor_get_timeout(cursor)); | 292 | wl_event_source_timer_update(cursor->hide_source, cursor_get_timeout(cursor)); |
342 | } | 293 | } |
343 | 294 | ||
344 | static void pointer_motion(struct sway_cursor *cursor, uint32_t time_msec, | 295 | void pointer_motion(struct sway_cursor *cursor, uint32_t time_msec, |
345 | struct wlr_input_device *device, double dx, double dy, | 296 | struct wlr_input_device *device, double dx, double dy, |
346 | double dx_unaccel, double dy_unaccel) { | 297 | double dx_unaccel, double dy_unaccel) { |
347 | wlr_relative_pointer_manager_v1_send_relative_motion( | 298 | wlr_relative_pointer_manager_v1_send_relative_motion( |
@@ -378,35 +329,34 @@ static void pointer_motion(struct sway_cursor *cursor, uint32_t time_msec, | |||
378 | static void handle_pointer_motion_relative( | 329 | static void handle_pointer_motion_relative( |
379 | struct wl_listener *listener, void *data) { | 330 | struct wl_listener *listener, void *data) { |
380 | struct sway_cursor *cursor = wl_container_of(listener, cursor, motion); | 331 | struct sway_cursor *cursor = wl_container_of(listener, cursor, motion); |
381 | struct wlr_event_pointer_motion *e = data; | 332 | struct wlr_pointer_motion_event *e = data; |
382 | cursor_handle_activity_from_device(cursor, e->device); | 333 | cursor_handle_activity_from_device(cursor, &e->pointer->base); |
383 | 334 | ||
384 | pointer_motion(cursor, e->time_msec, e->device, e->delta_x, e->delta_y, | 335 | pointer_motion(cursor, e->time_msec, &e->pointer->base, e->delta_x, |
385 | e->unaccel_dx, e->unaccel_dy); | 336 | e->delta_y, e->unaccel_dx, e->unaccel_dy); |
386 | transaction_commit_dirty(); | ||
387 | } | 337 | } |
388 | 338 | ||
389 | static void handle_pointer_motion_absolute( | 339 | static void handle_pointer_motion_absolute( |
390 | struct wl_listener *listener, void *data) { | 340 | struct wl_listener *listener, void *data) { |
391 | struct sway_cursor *cursor = | 341 | struct sway_cursor *cursor = |
392 | wl_container_of(listener, cursor, motion_absolute); | 342 | wl_container_of(listener, cursor, motion_absolute); |
393 | struct wlr_event_pointer_motion_absolute *event = data; | 343 | struct wlr_pointer_motion_absolute_event *event = data; |
394 | cursor_handle_activity_from_device(cursor, event->device); | 344 | cursor_handle_activity_from_device(cursor, &event->pointer->base); |
395 | 345 | ||
396 | double lx, ly; | 346 | double lx, ly; |
397 | wlr_cursor_absolute_to_layout_coords(cursor->cursor, event->device, | 347 | wlr_cursor_absolute_to_layout_coords(cursor->cursor, &event->pointer->base, |
398 | event->x, event->y, &lx, &ly); | 348 | event->x, event->y, &lx, &ly); |
399 | 349 | ||
400 | double dx = lx - cursor->cursor->x; | 350 | double dx = lx - cursor->cursor->x; |
401 | double dy = ly - cursor->cursor->y; | 351 | double dy = ly - cursor->cursor->y; |
402 | 352 | ||
403 | pointer_motion(cursor, event->time_msec, event->device, dx, dy, dx, dy); | 353 | pointer_motion(cursor, event->time_msec, &event->pointer->base, dx, dy, |
404 | transaction_commit_dirty(); | 354 | dx, dy); |
405 | } | 355 | } |
406 | 356 | ||
407 | void dispatch_cursor_button(struct sway_cursor *cursor, | 357 | void dispatch_cursor_button(struct sway_cursor *cursor, |
408 | struct wlr_input_device *device, uint32_t time_msec, uint32_t button, | 358 | struct wlr_input_device *device, uint32_t time_msec, uint32_t button, |
409 | enum wlr_button_state state) { | 359 | enum wl_pointer_button_state state) { |
410 | if (time_msec == 0) { | 360 | if (time_msec == 0) { |
411 | time_msec = get_current_time_msec(); | 361 | time_msec = get_current_time_msec(); |
412 | } | 362 | } |
@@ -416,9 +366,9 @@ void dispatch_cursor_button(struct sway_cursor *cursor, | |||
416 | 366 | ||
417 | static void handle_pointer_button(struct wl_listener *listener, void *data) { | 367 | static void handle_pointer_button(struct wl_listener *listener, void *data) { |
418 | struct sway_cursor *cursor = wl_container_of(listener, cursor, button); | 368 | struct sway_cursor *cursor = wl_container_of(listener, cursor, button); |
419 | struct wlr_event_pointer_button *event = data; | 369 | struct wlr_pointer_button_event *event = data; |
420 | 370 | ||
421 | if (event->state == WLR_BUTTON_PRESSED) { | 371 | if (event->state == WL_POINTER_BUTTON_STATE_PRESSED) { |
422 | cursor->pressed_button_count++; | 372 | cursor->pressed_button_count++; |
423 | } else { | 373 | } else { |
424 | if (cursor->pressed_button_count > 0) { | 374 | if (cursor->pressed_button_count > 0) { |
@@ -428,23 +378,21 @@ static void handle_pointer_button(struct wl_listener *listener, void *data) { | |||
428 | } | 378 | } |
429 | } | 379 | } |
430 | 380 | ||
431 | cursor_handle_activity_from_device(cursor, event->device); | 381 | cursor_handle_activity_from_device(cursor, &event->pointer->base); |
432 | dispatch_cursor_button(cursor, event->device, | 382 | dispatch_cursor_button(cursor, &event->pointer->base, |
433 | event->time_msec, event->button, event->state); | 383 | event->time_msec, event->button, event->state); |
434 | transaction_commit_dirty(); | ||
435 | } | 384 | } |
436 | 385 | ||
437 | void dispatch_cursor_axis(struct sway_cursor *cursor, | 386 | void dispatch_cursor_axis(struct sway_cursor *cursor, |
438 | struct wlr_event_pointer_axis *event) { | 387 | struct wlr_pointer_axis_event *event) { |
439 | seatop_pointer_axis(cursor->seat, event); | 388 | seatop_pointer_axis(cursor->seat, event); |
440 | } | 389 | } |
441 | 390 | ||
442 | static void handle_pointer_axis(struct wl_listener *listener, void *data) { | 391 | static void handle_pointer_axis(struct wl_listener *listener, void *data) { |
443 | struct sway_cursor *cursor = wl_container_of(listener, cursor, axis); | 392 | struct sway_cursor *cursor = wl_container_of(listener, cursor, axis); |
444 | struct wlr_event_pointer_axis *event = data; | 393 | struct wlr_pointer_axis_event *event = data; |
445 | cursor_handle_activity_from_device(cursor, event->device); | 394 | cursor_handle_activity_from_device(cursor, &event->pointer->base); |
446 | dispatch_cursor_axis(cursor, event); | 395 | dispatch_cursor_axis(cursor, event); |
447 | transaction_commit_dirty(); | ||
448 | } | 396 | } |
449 | 397 | ||
450 | static void handle_pointer_frame(struct wl_listener *listener, void *data) { | 398 | static void handle_pointer_frame(struct wl_listener *listener, void *data) { |
@@ -454,97 +402,76 @@ static void handle_pointer_frame(struct wl_listener *listener, void *data) { | |||
454 | 402 | ||
455 | static void handle_touch_down(struct wl_listener *listener, void *data) { | 403 | static void handle_touch_down(struct wl_listener *listener, void *data) { |
456 | struct sway_cursor *cursor = wl_container_of(listener, cursor, touch_down); | 404 | struct sway_cursor *cursor = wl_container_of(listener, cursor, touch_down); |
457 | struct wlr_event_touch_down *event = data; | 405 | struct wlr_touch_down_event *event = data; |
458 | cursor_handle_activity_from_device(cursor, event->device); | 406 | cursor_handle_activity_from_device(cursor, &event->touch->base); |
459 | cursor_hide(cursor); | 407 | cursor_hide(cursor); |
460 | 408 | ||
461 | struct sway_seat *seat = cursor->seat; | 409 | struct sway_seat *seat = cursor->seat; |
462 | struct wlr_seat *wlr_seat = seat->wlr_seat; | ||
463 | struct wlr_surface *surface = NULL; | ||
464 | 410 | ||
465 | double lx, ly; | 411 | double lx, ly; |
466 | wlr_cursor_absolute_to_layout_coords(cursor->cursor, event->device, | 412 | wlr_cursor_absolute_to_layout_coords(cursor->cursor, &event->touch->base, |
467 | event->x, event->y, &lx, &ly); | 413 | event->x, event->y, &lx, &ly); |
468 | double sx, sy; | ||
469 | struct sway_node *focused_node = node_at_coords(seat, lx, ly, &surface, &sx, &sy); | ||
470 | 414 | ||
471 | seat->touch_id = event->touch_id; | 415 | seat->touch_id = event->touch_id; |
472 | seat->touch_x = lx; | 416 | seat->touch_x = lx; |
473 | seat->touch_y = ly; | 417 | seat->touch_y = ly; |
474 | 418 | ||
475 | if (surface && wlr_surface_accepts_touch(wlr_seat, surface)) { | 419 | seatop_touch_down(seat, event, lx, ly); |
476 | if (seat_is_input_allowed(seat, surface)) { | 420 | } |
477 | wlr_seat_touch_notify_down(wlr_seat, surface, event->time_msec, | ||
478 | event->touch_id, sx, sy); | ||
479 | 421 | ||
480 | if (focused_node) { | 422 | static void handle_touch_up(struct wl_listener *listener, void *data) { |
481 | seat_set_focus(seat, focused_node); | 423 | struct sway_cursor *cursor = wl_container_of(listener, cursor, touch_up); |
482 | } | 424 | struct wlr_touch_up_event *event = data; |
425 | cursor_handle_activity_from_device(cursor, &event->touch->base); | ||
426 | |||
427 | struct sway_seat *seat = cursor->seat; | ||
428 | |||
429 | if (cursor->simulating_pointer_from_touch) { | ||
430 | if (cursor->pointer_touch_id == cursor->seat->touch_id) { | ||
431 | cursor->pointer_touch_up = true; | ||
432 | dispatch_cursor_button(cursor, &event->touch->base, | ||
433 | event->time_msec, BTN_LEFT, WL_POINTER_BUTTON_STATE_RELEASED); | ||
483 | } | 434 | } |
484 | } else if (!cursor->simulating_pointer_from_touch && | 435 | } else { |
485 | (!surface || seat_is_input_allowed(seat, surface))) { | 436 | seatop_touch_up(seat, event); |
486 | // Fallback to cursor simulation. | ||
487 | // The pointer_touch_id state is needed, so drags are not aborted when over | ||
488 | // a surface supporting touch and multi touch events don't interfere. | ||
489 | cursor->simulating_pointer_from_touch = true; | ||
490 | cursor->pointer_touch_id = seat->touch_id; | ||
491 | double dx, dy; | ||
492 | dx = lx - cursor->cursor->x; | ||
493 | dy = ly - cursor->cursor->y; | ||
494 | pointer_motion(cursor, event->time_msec, event->device, dx, dy, dx, dy); | ||
495 | dispatch_cursor_button(cursor, event->device, event->time_msec, | ||
496 | BTN_LEFT, WLR_BUTTON_PRESSED); | ||
497 | wlr_seat_pointer_notify_frame(wlr_seat); | ||
498 | transaction_commit_dirty(); | ||
499 | } | 437 | } |
500 | } | 438 | } |
501 | 439 | ||
502 | static void handle_touch_up(struct wl_listener *listener, void *data) { | 440 | static void handle_touch_cancel(struct wl_listener *listener, void *data) { |
503 | struct sway_cursor *cursor = wl_container_of(listener, cursor, touch_up); | 441 | struct sway_cursor *cursor = wl_container_of(listener, cursor, touch_cancel); |
504 | struct wlr_event_touch_up *event = data; | 442 | struct wlr_touch_cancel_event *event = data; |
505 | cursor_handle_activity_from_device(cursor, event->device); | 443 | cursor_handle_activity_from_device(cursor, &event->touch->base); |
506 | 444 | ||
507 | struct wlr_seat *wlr_seat = cursor->seat->wlr_seat; | 445 | struct sway_seat *seat = cursor->seat; |
508 | 446 | ||
509 | if (cursor->simulating_pointer_from_touch) { | 447 | if (cursor->simulating_pointer_from_touch) { |
510 | if (cursor->pointer_touch_id == cursor->seat->touch_id) { | 448 | if (cursor->pointer_touch_id == cursor->seat->touch_id) { |
511 | cursor->simulating_pointer_from_touch = false; | 449 | cursor->pointer_touch_up = true; |
512 | dispatch_cursor_button(cursor, event->device, event->time_msec, | 450 | dispatch_cursor_button(cursor, &event->touch->base, |
513 | BTN_LEFT, WLR_BUTTON_RELEASED); | 451 | event->time_msec, BTN_LEFT, WL_POINTER_BUTTON_STATE_RELEASED); |
514 | wlr_seat_pointer_notify_frame(wlr_seat); | ||
515 | transaction_commit_dirty(); | ||
516 | } | 452 | } |
517 | } else { | 453 | } else { |
518 | wlr_seat_touch_notify_up(wlr_seat, event->time_msec, event->touch_id); | 454 | seatop_touch_cancel(seat, event); |
519 | } | 455 | } |
520 | } | 456 | } |
521 | 457 | ||
522 | static void handle_touch_motion(struct wl_listener *listener, void *data) { | 458 | static void handle_touch_motion(struct wl_listener *listener, void *data) { |
523 | struct sway_cursor *cursor = | 459 | struct sway_cursor *cursor = |
524 | wl_container_of(listener, cursor, touch_motion); | 460 | wl_container_of(listener, cursor, touch_motion); |
525 | struct wlr_event_touch_motion *event = data; | 461 | struct wlr_touch_motion_event *event = data; |
526 | cursor_handle_activity_from_device(cursor, event->device); | 462 | cursor_handle_activity_from_device(cursor, &event->touch->base); |
527 | 463 | ||
528 | struct sway_seat *seat = cursor->seat; | 464 | struct sway_seat *seat = cursor->seat; |
529 | struct wlr_seat *wlr_seat = seat->wlr_seat; | ||
530 | struct wlr_surface *surface = NULL; | ||
531 | 465 | ||
532 | double lx, ly; | 466 | double lx, ly; |
533 | wlr_cursor_absolute_to_layout_coords(cursor->cursor, event->device, | 467 | wlr_cursor_absolute_to_layout_coords(cursor->cursor, &event->touch->base, |
534 | event->x, event->y, &lx, &ly); | 468 | event->x, event->y, &lx, &ly); |
535 | double sx, sy; | ||
536 | node_at_coords(cursor->seat, lx, ly, &surface, &sx, &sy); | ||
537 | 469 | ||
538 | if (seat->touch_id == event->touch_id) { | 470 | if (seat->touch_id == event->touch_id) { |
539 | seat->touch_x = lx; | 471 | seat->touch_x = lx; |
540 | seat->touch_y = ly; | 472 | seat->touch_y = ly; |
541 | 473 | ||
542 | struct sway_drag_icon *drag_icon; | 474 | drag_icons_update_position(seat); |
543 | wl_list_for_each(drag_icon, &root->drag_icons, link) { | ||
544 | if (drag_icon->seat == seat) { | ||
545 | drag_icon_update_position(drag_icon); | ||
546 | } | ||
547 | } | ||
548 | } | 475 | } |
549 | 476 | ||
550 | if (cursor->simulating_pointer_from_touch) { | 477 | if (cursor->simulating_pointer_from_touch) { |
@@ -552,12 +479,29 @@ static void handle_touch_motion(struct wl_listener *listener, void *data) { | |||
552 | double dx, dy; | 479 | double dx, dy; |
553 | dx = lx - cursor->cursor->x; | 480 | dx = lx - cursor->cursor->x; |
554 | dy = ly - cursor->cursor->y; | 481 | dy = ly - cursor->cursor->y; |
555 | pointer_motion(cursor, event->time_msec, event->device, dx, dy, dx, dy); | 482 | pointer_motion(cursor, event->time_msec, &event->touch->base, |
556 | transaction_commit_dirty(); | 483 | dx, dy, dx, dy); |
484 | } | ||
485 | } else { | ||
486 | seatop_touch_motion(seat, event, lx, ly); | ||
487 | } | ||
488 | } | ||
489 | |||
490 | static void handle_touch_frame(struct wl_listener *listener, void *data) { | ||
491 | struct sway_cursor *cursor = | ||
492 | wl_container_of(listener, cursor, touch_frame); | ||
493 | |||
494 | struct wlr_seat *wlr_seat = cursor->seat->wlr_seat; | ||
495 | |||
496 | if (cursor->simulating_pointer_from_touch) { | ||
497 | wlr_seat_pointer_notify_frame(wlr_seat); | ||
498 | |||
499 | if (cursor->pointer_touch_up) { | ||
500 | cursor->pointer_touch_up = false; | ||
501 | cursor->simulating_pointer_from_touch = false; | ||
557 | } | 502 | } |
558 | } else if (surface) { | 503 | } else { |
559 | wlr_seat_touch_notify_motion(wlr_seat, event->time_msec, | 504 | wlr_seat_touch_notify_frame(wlr_seat); |
560 | event->touch_id, sx, sy); | ||
561 | } | 505 | } |
562 | } | 506 | } |
563 | 507 | ||
@@ -574,14 +518,15 @@ static void apply_mapping_from_region(struct wlr_input_device *device, | |||
574 | double x1 = region->x1, x2 = region->x2; | 518 | double x1 = region->x1, x2 = region->x2; |
575 | double y1 = region->y1, y2 = region->y2; | 519 | double y1 = region->y1, y2 = region->y2; |
576 | 520 | ||
577 | if (region->mm) { | 521 | if (region->mm && device->type == WLR_INPUT_DEVICE_TABLET) { |
578 | if (device->width_mm == 0 || device->height_mm == 0) { | 522 | struct wlr_tablet *tablet = wlr_tablet_from_input_device(device); |
523 | if (tablet->width_mm == 0 || tablet->height_mm == 0) { | ||
579 | return; | 524 | return; |
580 | } | 525 | } |
581 | x1 /= device->width_mm; | 526 | x1 /= tablet->width_mm; |
582 | x2 /= device->width_mm; | 527 | x2 /= tablet->width_mm; |
583 | y1 /= device->height_mm; | 528 | y1 /= tablet->height_mm; |
584 | y2 /= device->height_mm; | 529 | y2 /= tablet->height_mm; |
585 | } | 530 | } |
586 | 531 | ||
587 | *x = apply_mapping_from_coord(x1, x2, *x); | 532 | *x = apply_mapping_from_coord(x1, x2, *x); |
@@ -639,14 +584,12 @@ static void handle_tablet_tool_position(struct sway_cursor *cursor, | |||
639 | wlr_tablet_v2_tablet_tool_notify_proximity_out(tool->tablet_v2_tool); | 584 | 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); | 585 | pointer_motion(cursor, time_msec, input_device->wlr_device, dx, dy, dx, dy); |
641 | } | 586 | } |
642 | |||
643 | transaction_commit_dirty(); | ||
644 | } | 587 | } |
645 | 588 | ||
646 | static void handle_tool_axis(struct wl_listener *listener, void *data) { | 589 | static void handle_tool_axis(struct wl_listener *listener, void *data) { |
647 | struct sway_cursor *cursor = wl_container_of(listener, cursor, tool_axis); | 590 | struct sway_cursor *cursor = wl_container_of(listener, cursor, tool_axis); |
648 | struct wlr_event_tablet_tool_axis *event = data; | 591 | struct wlr_tablet_tool_axis_event *event = data; |
649 | cursor_handle_activity_from_device(cursor, event->device); | 592 | cursor_handle_activity_from_device(cursor, &event->tablet->base); |
650 | 593 | ||
651 | struct sway_tablet_tool *sway_tool = event->tool->data; | 594 | struct sway_tablet_tool *sway_tool = event->tool->data; |
652 | if (!sway_tool) { | 595 | if (!sway_tool) { |
@@ -701,8 +644,8 @@ static void handle_tool_axis(struct wl_listener *listener, void *data) { | |||
701 | 644 | ||
702 | static void handle_tool_tip(struct wl_listener *listener, void *data) { | 645 | static void handle_tool_tip(struct wl_listener *listener, void *data) { |
703 | struct sway_cursor *cursor = wl_container_of(listener, cursor, tool_tip); | 646 | struct sway_cursor *cursor = wl_container_of(listener, cursor, tool_tip); |
704 | struct wlr_event_tablet_tool_tip *event = data; | 647 | struct wlr_tablet_tool_tip_event *event = data; |
705 | cursor_handle_activity_from_device(cursor, event->device); | 648 | cursor_handle_activity_from_device(cursor, &event->tablet->base); |
706 | 649 | ||
707 | struct sway_tablet_tool *sway_tool = event->tool->data; | 650 | struct sway_tablet_tool *sway_tool = event->tool->data; |
708 | struct wlr_tablet_v2_tablet *tablet_v2 = sway_tool->tablet->tablet_v2; | 651 | struct wlr_tablet_v2_tablet *tablet_v2 = sway_tool->tablet->tablet_v2; |
@@ -717,10 +660,9 @@ static void handle_tool_tip(struct wl_listener *listener, void *data) { | |||
717 | if (cursor->simulating_pointer_from_tool_tip && | 660 | if (cursor->simulating_pointer_from_tool_tip && |
718 | event->state == WLR_TABLET_TOOL_TIP_UP) { | 661 | event->state == WLR_TABLET_TOOL_TIP_UP) { |
719 | cursor->simulating_pointer_from_tool_tip = false; | 662 | cursor->simulating_pointer_from_tool_tip = false; |
720 | dispatch_cursor_button(cursor, event->device, event->time_msec, | 663 | dispatch_cursor_button(cursor, &event->tablet->base, event->time_msec, |
721 | BTN_LEFT, WLR_BUTTON_RELEASED); | 664 | BTN_LEFT, WL_POINTER_BUTTON_STATE_RELEASED); |
722 | wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat); | 665 | 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)) { | 666 | } 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 | 667 | // 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 | 668 | // tablet v2, we should notify that surface if it gets released over a |
@@ -730,10 +672,9 @@ static void handle_tool_tip(struct wl_listener *listener, void *data) { | |||
730 | WLR_TABLET_TOOL_TIP_UP); | 672 | WLR_TABLET_TOOL_TIP_UP); |
731 | } else { | 673 | } else { |
732 | cursor->simulating_pointer_from_tool_tip = true; | 674 | cursor->simulating_pointer_from_tool_tip = true; |
733 | dispatch_cursor_button(cursor, event->device, event->time_msec, | 675 | dispatch_cursor_button(cursor, &event->tablet->base, |
734 | BTN_LEFT, WLR_BUTTON_PRESSED); | 676 | event->time_msec, BTN_LEFT, WL_POINTER_BUTTON_STATE_PRESSED); |
735 | wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat); | 677 | wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat); |
736 | transaction_commit_dirty(); | ||
737 | } | 678 | } |
738 | } else { | 679 | } else { |
739 | seatop_tablet_tool_tip(seat, sway_tool, event->time_msec, event->state); | 680 | seatop_tablet_tool_tip(seat, sway_tool, event->time_msec, event->state); |
@@ -754,12 +695,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) { | 695 | static void handle_tool_proximity(struct wl_listener *listener, void *data) { |
755 | struct sway_cursor *cursor = | 696 | struct sway_cursor *cursor = |
756 | wl_container_of(listener, cursor, tool_proximity); | 697 | wl_container_of(listener, cursor, tool_proximity); |
757 | struct wlr_event_tablet_tool_proximity *event = data; | 698 | struct wlr_tablet_tool_proximity_event *event = data; |
758 | cursor_handle_activity_from_device(cursor, event->device); | 699 | cursor_handle_activity_from_device(cursor, &event->tablet->base); |
759 | 700 | ||
760 | struct wlr_tablet_tool *tool = event->tool; | 701 | struct wlr_tablet_tool *tool = event->tool; |
761 | if (!tool->data) { | 702 | if (!tool->data) { |
762 | struct sway_tablet *tablet = get_tablet_for_device(cursor, event->device); | 703 | struct sway_tablet *tablet = get_tablet_for_device(cursor, |
704 | &event->tablet->base); | ||
763 | if (!tablet) { | 705 | if (!tablet) { |
764 | sway_log(SWAY_ERROR, "no tablet for tablet tool"); | 706 | sway_log(SWAY_ERROR, "no tablet for tablet tool"); |
765 | return; | 707 | return; |
@@ -784,8 +726,8 @@ static void handle_tool_proximity(struct wl_listener *listener, void *data) { | |||
784 | 726 | ||
785 | static void handle_tool_button(struct wl_listener *listener, void *data) { | 727 | static void handle_tool_button(struct wl_listener *listener, void *data) { |
786 | struct sway_cursor *cursor = wl_container_of(listener, cursor, tool_button); | 728 | struct sway_cursor *cursor = wl_container_of(listener, cursor, tool_button); |
787 | struct wlr_event_tablet_tool_button *event = data; | 729 | struct wlr_tablet_tool_button_event *event = data; |
788 | cursor_handle_activity_from_device(cursor, event->device); | 730 | cursor_handle_activity_from_device(cursor, &event->tablet->base); |
789 | 731 | ||
790 | struct sway_tablet_tool *sway_tool = event->tool->data; | 732 | struct sway_tablet_tool *sway_tool = event->tool->data; |
791 | if (!sway_tool) { | 733 | if (!sway_tool) { |
@@ -800,32 +742,71 @@ static void handle_tool_button(struct wl_listener *listener, void *data) { | |||
800 | node_at_coords(cursor->seat, cursor->cursor->x, cursor->cursor->y, | 742 | node_at_coords(cursor->seat, cursor->cursor->x, cursor->cursor->y, |
801 | &surface, &sx, &sy); | 743 | &surface, &sx, &sy); |
802 | 744 | ||
803 | if (!surface || !wlr_surface_accepts_tablet_v2(tablet_v2, surface)) { | 745 | // TODO: floating resize should support graphics tablet events |
746 | struct wlr_keyboard *keyboard = wlr_seat_get_keyboard(cursor->seat->wlr_seat); | ||
747 | uint32_t modifiers = keyboard ? wlr_keyboard_get_modifiers(keyboard) : 0; | ||
748 | bool mod_pressed = modifiers & config->floating_mod; | ||
749 | |||
750 | bool surface_supports_tablet_events = | ||
751 | surface && wlr_surface_accepts_tablet_v2(tablet_v2, surface); | ||
752 | |||
753 | // Simulate pointer when: | ||
754 | // 1. The modifier key is pressed, OR | ||
755 | // 2. The surface under the cursor does not support tablet events. | ||
756 | bool should_simulate_pointer = mod_pressed || !surface_supports_tablet_events; | ||
757 | |||
758 | // Similar to tool tip, we need to selectively simulate mouse events, but we | ||
759 | // want to make sure that it is always consistent. Because all tool buttons | ||
760 | // currently map to BTN_RIGHT, we need to keep count of how many tool | ||
761 | // buttons are currently pressed down so we can send consistent events. | ||
762 | // | ||
763 | // The logic follows: | ||
764 | // - If we are already simulating the pointer, we should continue to do so | ||
765 | // until at least no tool button is held down. | ||
766 | // - If we should simulate the pointer and no tool button is currently held | ||
767 | // down, begin simulating the pointer. | ||
768 | // - If neither of the above are true, send the tablet events. | ||
769 | if ((cursor->tool_buttons > 0 && cursor->simulating_pointer_from_tool_button) | ||
770 | || (cursor->tool_buttons == 0 && should_simulate_pointer)) { | ||
771 | cursor->simulating_pointer_from_tool_button = true; | ||
772 | |||
804 | // TODO: the user may want to configure which tool buttons are mapped to | 773 | // TODO: the user may want to configure which tool buttons are mapped to |
805 | // which simulated pointer buttons | 774 | // which simulated pointer buttons |
806 | switch (event->state) { | 775 | switch (event->state) { |
807 | case WLR_BUTTON_PRESSED: | 776 | case WLR_BUTTON_PRESSED: |
808 | if (cursor->tool_buttons == 0) { | 777 | if (cursor->tool_buttons == 0) { |
809 | dispatch_cursor_button(cursor, event->device, | 778 | dispatch_cursor_button(cursor, &event->tablet->base, |
810 | event->time_msec, BTN_RIGHT, event->state); | 779 | event->time_msec, BTN_RIGHT, WL_POINTER_BUTTON_STATE_PRESSED); |
811 | } | 780 | } |
812 | cursor->tool_buttons++; | ||
813 | break; | 781 | break; |
814 | case WLR_BUTTON_RELEASED: | 782 | case WLR_BUTTON_RELEASED: |
815 | if (cursor->tool_buttons == 1) { | 783 | if (cursor->tool_buttons <= 1) { |
816 | dispatch_cursor_button(cursor, event->device, | 784 | dispatch_cursor_button(cursor, &event->tablet->base, |
817 | event->time_msec, BTN_RIGHT, event->state); | 785 | event->time_msec, BTN_RIGHT, WL_POINTER_BUTTON_STATE_RELEASED); |
818 | } | 786 | } |
819 | cursor->tool_buttons--; | ||
820 | break; | 787 | break; |
821 | } | 788 | } |
822 | wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat); | 789 | wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat); |
823 | transaction_commit_dirty(); | 790 | } else { |
824 | return; | 791 | cursor->simulating_pointer_from_tool_button = false; |
792 | |||
793 | wlr_tablet_v2_tablet_tool_notify_button(sway_tool->tablet_v2_tool, | ||
794 | event->button, (enum zwp_tablet_pad_v2_button_state)event->state); | ||
825 | } | 795 | } |
826 | 796 | ||
827 | wlr_tablet_v2_tablet_tool_notify_button(sway_tool->tablet_v2_tool, | 797 | // Update tool button count. |
828 | event->button, (enum zwp_tablet_pad_v2_button_state)event->state); | 798 | switch (event->state) { |
799 | case WLR_BUTTON_PRESSED: | ||
800 | cursor->tool_buttons++; | ||
801 | break; | ||
802 | case WLR_BUTTON_RELEASED: | ||
803 | if (cursor->tool_buttons == 0) { | ||
804 | sway_log(SWAY_ERROR, "inconsistent tablet tool button events"); | ||
805 | } else { | ||
806 | cursor->tool_buttons--; | ||
807 | } | ||
808 | break; | ||
809 | } | ||
829 | } | 810 | } |
830 | 811 | ||
831 | static void check_constraint_region(struct sway_cursor *cursor) { | 812 | static void check_constraint_region(struct sway_cursor *cursor) { |
@@ -837,8 +818,8 @@ static void check_constraint_region(struct sway_cursor *cursor) { | |||
837 | 818 | ||
838 | struct sway_container *con = view->container; | 819 | struct sway_container *con = view->container; |
839 | 820 | ||
840 | double sx = cursor->cursor->x - con->content_x + view->geometry.x; | 821 | double sx = cursor->cursor->x - con->pending.content_x + view->geometry.x; |
841 | double sy = cursor->cursor->y - con->content_y + view->geometry.y; | 822 | double sy = cursor->cursor->y - con->pending.content_y + view->geometry.y; |
842 | 823 | ||
843 | if (!pixman_region32_contains_point(region, | 824 | if (!pixman_region32_contains_point(region, |
844 | floor(sx), floor(sy), NULL)) { | 825 | floor(sx), floor(sy), NULL)) { |
@@ -849,8 +830,8 @@ static void check_constraint_region(struct sway_cursor *cursor) { | |||
849 | double sy = (boxes[0].y1 + boxes[0].y2) / 2.; | 830 | double sy = (boxes[0].y1 + boxes[0].y2) / 2.; |
850 | 831 | ||
851 | wlr_cursor_warp_closest(cursor->cursor, NULL, | 832 | wlr_cursor_warp_closest(cursor->cursor, NULL, |
852 | sx + con->content_x - view->geometry.x, | 833 | sx + con->pending.content_x - view->geometry.x, |
853 | sy + con->content_y - view->geometry.y); | 834 | sy + con->pending.content_y - view->geometry.y); |
854 | 835 | ||
855 | cursor_rebase(cursor); | 836 | cursor_rebase(cursor); |
856 | } | 837 | } |
@@ -911,59 +892,68 @@ static void handle_request_pointer_set_cursor(struct wl_listener *listener, | |||
911 | event->hotspot_y, focused_client); | 892 | event->hotspot_y, focused_client); |
912 | } | 893 | } |
913 | 894 | ||
895 | static void handle_pointer_hold_begin(struct wl_listener *listener, void *data) { | ||
896 | struct sway_cursor *cursor = wl_container_of( | ||
897 | listener, cursor, hold_begin); | ||
898 | struct wlr_pointer_hold_begin_event *event = data; | ||
899 | cursor_handle_activity_from_device(cursor, &event->pointer->base); | ||
900 | seatop_hold_begin(cursor->seat, event); | ||
901 | } | ||
902 | |||
903 | static void handle_pointer_hold_end(struct wl_listener *listener, void *data) { | ||
904 | struct sway_cursor *cursor = wl_container_of( | ||
905 | listener, cursor, hold_end); | ||
906 | struct wlr_pointer_hold_end_event *event = data; | ||
907 | cursor_handle_activity_from_device(cursor, &event->pointer->base); | ||
908 | seatop_hold_end(cursor->seat, event); | ||
909 | } | ||
910 | |||
914 | static void handle_pointer_pinch_begin(struct wl_listener *listener, void *data) { | 911 | static void handle_pointer_pinch_begin(struct wl_listener *listener, void *data) { |
915 | struct sway_cursor *cursor = wl_container_of( | 912 | struct sway_cursor *cursor = wl_container_of( |
916 | listener, cursor, pinch_begin); | 913 | listener, cursor, pinch_begin); |
917 | struct wlr_event_pointer_pinch_begin *event = data; | 914 | struct wlr_pointer_pinch_begin_event *event = data; |
918 | wlr_pointer_gestures_v1_send_pinch_begin( | 915 | cursor_handle_activity_from_device(cursor, &event->pointer->base); |
919 | cursor->pointer_gestures, cursor->seat->wlr_seat, | 916 | seatop_pinch_begin(cursor->seat, event); |
920 | event->time_msec, event->fingers); | ||
921 | } | 917 | } |
922 | 918 | ||
923 | static void handle_pointer_pinch_update(struct wl_listener *listener, void *data) { | 919 | static void handle_pointer_pinch_update(struct wl_listener *listener, void *data) { |
924 | struct sway_cursor *cursor = wl_container_of( | 920 | struct sway_cursor *cursor = wl_container_of( |
925 | listener, cursor, pinch_update); | 921 | listener, cursor, pinch_update); |
926 | struct wlr_event_pointer_pinch_update *event = data; | 922 | struct wlr_pointer_pinch_update_event *event = data; |
927 | wlr_pointer_gestures_v1_send_pinch_update( | 923 | cursor_handle_activity_from_device(cursor, &event->pointer->base); |
928 | cursor->pointer_gestures, cursor->seat->wlr_seat, | 924 | seatop_pinch_update(cursor->seat, event); |
929 | event->time_msec, event->dx, event->dy, | ||
930 | event->scale, event->rotation); | ||
931 | } | 925 | } |
932 | 926 | ||
933 | static void handle_pointer_pinch_end(struct wl_listener *listener, void *data) { | 927 | static void handle_pointer_pinch_end(struct wl_listener *listener, void *data) { |
934 | struct sway_cursor *cursor = wl_container_of( | 928 | struct sway_cursor *cursor = wl_container_of( |
935 | listener, cursor, pinch_end); | 929 | listener, cursor, pinch_end); |
936 | struct wlr_event_pointer_pinch_end *event = data; | 930 | struct wlr_pointer_pinch_end_event *event = data; |
937 | wlr_pointer_gestures_v1_send_pinch_end( | 931 | cursor_handle_activity_from_device(cursor, &event->pointer->base); |
938 | cursor->pointer_gestures, cursor->seat->wlr_seat, | 932 | seatop_pinch_end(cursor->seat, event); |
939 | event->time_msec, event->cancelled); | ||
940 | } | 933 | } |
941 | 934 | ||
942 | static void handle_pointer_swipe_begin(struct wl_listener *listener, void *data) { | 935 | static void handle_pointer_swipe_begin(struct wl_listener *listener, void *data) { |
943 | struct sway_cursor *cursor = wl_container_of( | 936 | struct sway_cursor *cursor = wl_container_of( |
944 | listener, cursor, swipe_begin); | 937 | listener, cursor, swipe_begin); |
945 | struct wlr_event_pointer_swipe_begin *event = data; | 938 | struct wlr_pointer_swipe_begin_event *event = data; |
946 | wlr_pointer_gestures_v1_send_swipe_begin( | 939 | cursor_handle_activity_from_device(cursor, &event->pointer->base); |
947 | cursor->pointer_gestures, cursor->seat->wlr_seat, | 940 | seatop_swipe_begin(cursor->seat, event); |
948 | event->time_msec, event->fingers); | ||
949 | } | 941 | } |
950 | 942 | ||
951 | static void handle_pointer_swipe_update(struct wl_listener *listener, void *data) { | 943 | static void handle_pointer_swipe_update(struct wl_listener *listener, void *data) { |
952 | struct sway_cursor *cursor = wl_container_of( | 944 | struct sway_cursor *cursor = wl_container_of( |
953 | listener, cursor, swipe_update); | 945 | listener, cursor, swipe_update); |
954 | struct wlr_event_pointer_swipe_update *event = data; | 946 | struct wlr_pointer_swipe_update_event *event = data; |
955 | wlr_pointer_gestures_v1_send_swipe_update( | 947 | cursor_handle_activity_from_device(cursor, &event->pointer->base); |
956 | cursor->pointer_gestures, cursor->seat->wlr_seat, | 948 | seatop_swipe_update(cursor->seat, event); |
957 | event->time_msec, event->dx, event->dy); | ||
958 | } | 949 | } |
959 | 950 | ||
960 | static void handle_pointer_swipe_end(struct wl_listener *listener, void *data) { | 951 | static void handle_pointer_swipe_end(struct wl_listener *listener, void *data) { |
961 | struct sway_cursor *cursor = wl_container_of( | 952 | struct sway_cursor *cursor = wl_container_of( |
962 | listener, cursor, swipe_end); | 953 | listener, cursor, swipe_end); |
963 | struct wlr_event_pointer_swipe_end *event = data; | 954 | struct wlr_pointer_swipe_end_event *event = data; |
964 | wlr_pointer_gestures_v1_send_swipe_end( | 955 | cursor_handle_activity_from_device(cursor, &event->pointer->base); |
965 | cursor->pointer_gestures, cursor->seat->wlr_seat, | 956 | seatop_swipe_end(cursor->seat, event); |
966 | event->time_msec, event->cancelled); | ||
967 | } | 957 | } |
968 | 958 | ||
969 | static void handle_image_surface_destroy(struct wl_listener *listener, | 959 | static void handle_image_surface_destroy(struct wl_listener *listener, |
@@ -1002,10 +992,9 @@ void cursor_set_image(struct sway_cursor *cursor, const char *image, | |||
1002 | } | 992 | } |
1003 | 993 | ||
1004 | if (!image) { | 994 | if (!image) { |
1005 | wlr_cursor_set_image(cursor->cursor, NULL, 0, 0, 0, 0, 0, 0); | 995 | wlr_cursor_unset_image(cursor->cursor); |
1006 | } else if (!current_image || strcmp(current_image, image) != 0) { | 996 | } else if (!current_image || strcmp(current_image, image) != 0) { |
1007 | wlr_xcursor_manager_set_cursor_image(cursor->xcursor_manager, image, | 997 | wlr_cursor_set_xcursor(cursor->cursor, cursor->xcursor_manager, image); |
1008 | cursor->cursor); | ||
1009 | } | 998 | } |
1010 | } | 999 | } |
1011 | 1000 | ||
@@ -1037,6 +1026,8 @@ void sway_cursor_destroy(struct sway_cursor *cursor) { | |||
1037 | wl_event_source_remove(cursor->hide_source); | 1026 | wl_event_source_remove(cursor->hide_source); |
1038 | 1027 | ||
1039 | wl_list_remove(&cursor->image_surface_destroy.link); | 1028 | wl_list_remove(&cursor->image_surface_destroy.link); |
1029 | wl_list_remove(&cursor->hold_begin.link); | ||
1030 | wl_list_remove(&cursor->hold_end.link); | ||
1040 | wl_list_remove(&cursor->pinch_begin.link); | 1031 | wl_list_remove(&cursor->pinch_begin.link); |
1041 | wl_list_remove(&cursor->pinch_update.link); | 1032 | wl_list_remove(&cursor->pinch_update.link); |
1042 | wl_list_remove(&cursor->pinch_end.link); | 1033 | wl_list_remove(&cursor->pinch_end.link); |
@@ -1050,7 +1041,9 @@ void sway_cursor_destroy(struct sway_cursor *cursor) { | |||
1050 | wl_list_remove(&cursor->frame.link); | 1041 | wl_list_remove(&cursor->frame.link); |
1051 | wl_list_remove(&cursor->touch_down.link); | 1042 | wl_list_remove(&cursor->touch_down.link); |
1052 | wl_list_remove(&cursor->touch_up.link); | 1043 | wl_list_remove(&cursor->touch_up.link); |
1044 | wl_list_remove(&cursor->touch_cancel.link); | ||
1053 | wl_list_remove(&cursor->touch_motion.link); | 1045 | wl_list_remove(&cursor->touch_motion.link); |
1046 | wl_list_remove(&cursor->touch_frame.link); | ||
1054 | wl_list_remove(&cursor->tool_axis.link); | 1047 | wl_list_remove(&cursor->tool_axis.link); |
1055 | wl_list_remove(&cursor->tool_tip.link); | 1048 | wl_list_remove(&cursor->tool_tip.link); |
1056 | wl_list_remove(&cursor->tool_button.link); | 1049 | wl_list_remove(&cursor->tool_button.link); |
@@ -1085,19 +1078,24 @@ struct sway_cursor *sway_cursor_create(struct sway_seat *seat) { | |||
1085 | wl_list_init(&cursor->image_surface_destroy.link); | 1078 | wl_list_init(&cursor->image_surface_destroy.link); |
1086 | cursor->image_surface_destroy.notify = handle_image_surface_destroy; | 1079 | cursor->image_surface_destroy.notify = handle_image_surface_destroy; |
1087 | 1080 | ||
1088 | cursor->pointer_gestures = wlr_pointer_gestures_v1_create(server.wl_display); | 1081 | wl_signal_add(&wlr_cursor->events.hold_begin, &cursor->hold_begin); |
1089 | cursor->pinch_begin.notify = handle_pointer_pinch_begin; | 1082 | cursor->hold_begin.notify = handle_pointer_hold_begin; |
1083 | wl_signal_add(&wlr_cursor->events.hold_end, &cursor->hold_end); | ||
1084 | cursor->hold_end.notify = handle_pointer_hold_end; | ||
1085 | |||
1090 | wl_signal_add(&wlr_cursor->events.pinch_begin, &cursor->pinch_begin); | 1086 | wl_signal_add(&wlr_cursor->events.pinch_begin, &cursor->pinch_begin); |
1091 | cursor->pinch_update.notify = handle_pointer_pinch_update; | 1087 | cursor->pinch_begin.notify = handle_pointer_pinch_begin; |
1092 | wl_signal_add(&wlr_cursor->events.pinch_update, &cursor->pinch_update); | 1088 | wl_signal_add(&wlr_cursor->events.pinch_update, &cursor->pinch_update); |
1093 | cursor->pinch_end.notify = handle_pointer_pinch_end; | 1089 | cursor->pinch_update.notify = handle_pointer_pinch_update; |
1094 | wl_signal_add(&wlr_cursor->events.pinch_end, &cursor->pinch_end); | 1090 | wl_signal_add(&wlr_cursor->events.pinch_end, &cursor->pinch_end); |
1095 | cursor->swipe_begin.notify = handle_pointer_swipe_begin; | 1091 | cursor->pinch_end.notify = handle_pointer_pinch_end; |
1092 | |||
1096 | wl_signal_add(&wlr_cursor->events.swipe_begin, &cursor->swipe_begin); | 1093 | wl_signal_add(&wlr_cursor->events.swipe_begin, &cursor->swipe_begin); |
1097 | cursor->swipe_update.notify = handle_pointer_swipe_update; | 1094 | cursor->swipe_begin.notify = handle_pointer_swipe_begin; |
1098 | wl_signal_add(&wlr_cursor->events.swipe_update, &cursor->swipe_update); | 1095 | wl_signal_add(&wlr_cursor->events.swipe_update, &cursor->swipe_update); |
1099 | cursor->swipe_end.notify = handle_pointer_swipe_end; | 1096 | cursor->swipe_update.notify = handle_pointer_swipe_update; |
1100 | wl_signal_add(&wlr_cursor->events.swipe_end, &cursor->swipe_end); | 1097 | wl_signal_add(&wlr_cursor->events.swipe_end, &cursor->swipe_end); |
1098 | cursor->swipe_end.notify = handle_pointer_swipe_end; | ||
1101 | 1099 | ||
1102 | // input events | 1100 | // input events |
1103 | wl_signal_add(&wlr_cursor->events.motion, &cursor->motion); | 1101 | wl_signal_add(&wlr_cursor->events.motion, &cursor->motion); |
@@ -1122,10 +1120,16 @@ struct sway_cursor *sway_cursor_create(struct sway_seat *seat) { | |||
1122 | wl_signal_add(&wlr_cursor->events.touch_up, &cursor->touch_up); | 1120 | wl_signal_add(&wlr_cursor->events.touch_up, &cursor->touch_up); |
1123 | cursor->touch_up.notify = handle_touch_up; | 1121 | cursor->touch_up.notify = handle_touch_up; |
1124 | 1122 | ||
1123 | wl_signal_add(&wlr_cursor->events.touch_cancel, &cursor->touch_cancel); | ||
1124 | cursor->touch_cancel.notify = handle_touch_cancel; | ||
1125 | |||
1125 | wl_signal_add(&wlr_cursor->events.touch_motion, | 1126 | wl_signal_add(&wlr_cursor->events.touch_motion, |
1126 | &cursor->touch_motion); | 1127 | &cursor->touch_motion); |
1127 | cursor->touch_motion.notify = handle_touch_motion; | 1128 | cursor->touch_motion.notify = handle_touch_motion; |
1128 | 1129 | ||
1130 | wl_signal_add(&wlr_cursor->events.touch_frame, &cursor->touch_frame); | ||
1131 | cursor->touch_frame.notify = handle_touch_frame; | ||
1132 | |||
1129 | wl_signal_add(&wlr_cursor->events.tablet_tool_axis, | 1133 | wl_signal_add(&wlr_cursor->events.tablet_tool_axis, |
1130 | &cursor->tool_axis); | 1134 | &cursor->tool_axis); |
1131 | cursor->tool_axis.notify = handle_tool_axis; | 1135 | cursor->tool_axis.notify = handle_tool_axis; |
@@ -1170,8 +1174,8 @@ void cursor_warp_to_container(struct sway_cursor *cursor, | |||
1170 | return; | 1174 | return; |
1171 | } | 1175 | } |
1172 | 1176 | ||
1173 | double x = container->x + container->width / 2.0; | 1177 | double x = container->pending.x + container->pending.width / 2.0; |
1174 | double y = container->y + container->height / 2.0; | 1178 | double y = container->pending.y + container->pending.height / 2.0; |
1175 | 1179 | ||
1176 | wlr_cursor_warp(cursor->cursor, NULL, x, y); | 1180 | wlr_cursor_warp(cursor->cursor, NULL, x, y); |
1177 | cursor_unhide(cursor); | 1181 | cursor_unhide(cursor); |
@@ -1211,11 +1215,7 @@ uint32_t get_mouse_bindsym(const char *name, char **error) { | |||
1211 | // Get event code from name | 1215 | // Get event code from name |
1212 | int code = libevdev_event_code_from_name(EV_KEY, name); | 1216 | int code = libevdev_event_code_from_name(EV_KEY, name); |
1213 | if (code == -1) { | 1217 | if (code == -1) { |
1214 | size_t len = snprintf(NULL, 0, "Unknown event %s", name) + 1; | 1218 | *error = format_str("Unknown event %s", name); |
1215 | *error = malloc(len); | ||
1216 | if (*error) { | ||
1217 | snprintf(*error, len, "Unknown event %s", name); | ||
1218 | } | ||
1219 | return 0; | 1219 | return 0; |
1220 | } | 1220 | } |
1221 | return code; | 1221 | return code; |
@@ -1237,13 +1237,8 @@ uint32_t get_mouse_bindcode(const char *name, char **error) { | |||
1237 | } | 1237 | } |
1238 | const char *event = libevdev_event_code_get_name(EV_KEY, code); | 1238 | const char *event = libevdev_event_code_get_name(EV_KEY, code); |
1239 | if (!event || strncmp(event, "BTN_", strlen("BTN_")) != 0) { | 1239 | if (!event || strncmp(event, "BTN_", strlen("BTN_")) != 0) { |
1240 | size_t len = snprintf(NULL, 0, "Event code %d (%s) is not a button", | 1240 | *error = format_str("Event code %d (%s) is not a button", |
1241 | code, event ? event : "(null)") + 1; | 1241 | code, event ? event : "(null)"); |
1242 | *error = malloc(len); | ||
1243 | if (*error) { | ||
1244 | snprintf(*error, len, "Event code %d (%s) is not a button", | ||
1245 | code, event ? event : "(null)"); | ||
1246 | } | ||
1247 | return 0; | 1242 | return 0; |
1248 | } | 1243 | } |
1249 | return code; | 1244 | return code; |
@@ -1276,16 +1271,19 @@ const char *get_mouse_button_name(uint32_t button) { | |||
1276 | static void warp_to_constraint_cursor_hint(struct sway_cursor *cursor) { | 1271 | static void warp_to_constraint_cursor_hint(struct sway_cursor *cursor) { |
1277 | struct wlr_pointer_constraint_v1 *constraint = cursor->active_constraint; | 1272 | struct wlr_pointer_constraint_v1 *constraint = cursor->active_constraint; |
1278 | 1273 | ||
1279 | if (constraint->current.committed & | 1274 | if (constraint->current.cursor_hint.enabled) { |
1280 | WLR_POINTER_CONSTRAINT_V1_STATE_CURSOR_HINT) { | ||
1281 | double sx = constraint->current.cursor_hint.x; | 1275 | double sx = constraint->current.cursor_hint.x; |
1282 | double sy = constraint->current.cursor_hint.y; | 1276 | double sy = constraint->current.cursor_hint.y; |
1283 | 1277 | ||
1284 | struct sway_view *view = view_from_wlr_surface(constraint->surface); | 1278 | struct sway_view *view = view_from_wlr_surface(constraint->surface); |
1279 | if (!view) { | ||
1280 | return; | ||
1281 | } | ||
1282 | |||
1285 | struct sway_container *con = view->container; | 1283 | struct sway_container *con = view->container; |
1286 | 1284 | ||
1287 | double lx = sx + con->content_x - view->geometry.x; | 1285 | double lx = sx + con->pending.content_x - view->geometry.x; |
1288 | double ly = sy + con->content_y - view->geometry.y; | 1286 | double ly = sy + con->pending.content_y - view->geometry.y; |
1289 | 1287 | ||
1290 | wlr_cursor_warp(cursor->cursor, NULL, lx, ly); | 1288 | wlr_cursor_warp(cursor->cursor, NULL, lx, ly); |
1291 | 1289 | ||
@@ -1332,12 +1330,9 @@ void handle_pointer_constraint(struct wl_listener *listener, void *data) { | |||
1332 | sway_constraint->destroy.notify = handle_constraint_destroy; | 1330 | sway_constraint->destroy.notify = handle_constraint_destroy; |
1333 | wl_signal_add(&constraint->events.destroy, &sway_constraint->destroy); | 1331 | wl_signal_add(&constraint->events.destroy, &sway_constraint->destroy); |
1334 | 1332 | ||
1335 | struct sway_node *focus = seat_get_focus(seat); | 1333 | struct wlr_surface *surface = seat->wlr_seat->keyboard_state.focused_surface; |
1336 | if (focus && focus->type == N_CONTAINER && focus->sway_container->view) { | 1334 | if (surface && surface == constraint->surface) { |
1337 | struct wlr_surface *surface = focus->sway_container->view->surface; | 1335 | sway_cursor_constrain(seat->cursor, constraint); |
1338 | if (surface == constraint->surface) { | ||
1339 | sway_cursor_constrain(seat->cursor, constraint); | ||
1340 | } | ||
1341 | } | 1336 | } |
1342 | } | 1337 | } |
1343 | 1338 | ||
@@ -1395,3 +1390,26 @@ void sway_cursor_constrain(struct sway_cursor *cursor, | |||
1395 | wl_signal_add(&constraint->surface->events.commit, | 1390 | wl_signal_add(&constraint->surface->events.commit, |
1396 | &cursor->constraint_commit); | 1391 | &cursor->constraint_commit); |
1397 | } | 1392 | } |
1393 | |||
1394 | void handle_request_set_cursor_shape(struct wl_listener *listener, void *data) { | ||
1395 | const struct wlr_cursor_shape_manager_v1_request_set_shape_event *event = data; | ||
1396 | struct sway_seat *seat = event->seat_client->seat->data; | ||
1397 | |||
1398 | if (!seatop_allows_set_cursor(seat)) { | ||
1399 | return; | ||
1400 | } | ||
1401 | |||
1402 | struct wl_client *focused_client = NULL; | ||
1403 | struct wlr_surface *focused_surface = seat->wlr_seat->pointer_state.focused_surface; | ||
1404 | if (focused_surface != NULL) { | ||
1405 | focused_client = wl_resource_get_client(focused_surface->resource); | ||
1406 | } | ||
1407 | |||
1408 | // TODO: check cursor mode | ||
1409 | if (focused_client == NULL || event->seat_client->client != focused_client) { | ||
1410 | sway_log(SWAY_DEBUG, "denying request to set cursor from unfocused client"); | ||
1411 | return; | ||
1412 | } | ||
1413 | |||
1414 | cursor_set_image(seat->cursor, wlr_cursor_shape_v1_name(event->shape), focused_client); | ||
1415 | } | ||
diff --git a/sway/input/input-manager.c b/sway/input/input-manager.c index f04a8ce0..248ca34e 100644 --- a/sway/input/input-manager.c +++ b/sway/input/input-manager.c | |||
@@ -1,12 +1,12 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <ctype.h> | 1 | #include <ctype.h> |
3 | #include <stdio.h> | 2 | #include <stdio.h> |
4 | #include <string.h> | 3 | #include <string.h> |
5 | #include <math.h> | 4 | #include <math.h> |
5 | #include <assert.h> | ||
6 | #include <wlr/config.h> | ||
6 | #include <wlr/backend/libinput.h> | 7 | #include <wlr/backend/libinput.h> |
7 | #include <wlr/types/wlr_cursor.h> | 8 | #include <wlr/types/wlr_cursor.h> |
8 | #include <wlr/types/wlr_keyboard_group.h> | 9 | #include <wlr/types/wlr_keyboard_group.h> |
9 | #include <wlr/types/wlr_input_inhibitor.h> | ||
10 | #include <wlr/types/wlr_virtual_keyboard_v1.h> | 10 | #include <wlr/types/wlr_virtual_keyboard_v1.h> |
11 | #include <wlr/types/wlr_virtual_pointer_v1.h> | 11 | #include <wlr/types/wlr_virtual_pointer_v1.h> |
12 | #include "sway/config.h" | 12 | #include "sway/config.h" |
@@ -22,6 +22,10 @@ | |||
22 | #include "list.h" | 22 | #include "list.h" |
23 | #include "log.h" | 23 | #include "log.h" |
24 | 24 | ||
25 | #if WLR_HAS_LIBINPUT_BACKEND | ||
26 | #include <wlr/backend/libinput.h> | ||
27 | #endif | ||
28 | |||
25 | #define DEFAULT_SEAT "seat0" | 29 | #define DEFAULT_SEAT "seat0" |
26 | 30 | ||
27 | struct input_config *current_input_config = NULL; | 31 | struct input_config *current_input_config = NULL; |
@@ -63,8 +67,15 @@ struct sway_seat *input_manager_sway_seat_from_wlr_seat(struct wlr_seat *wlr_sea | |||
63 | } | 67 | } |
64 | 68 | ||
65 | char *input_device_get_identifier(struct wlr_input_device *device) { | 69 | char *input_device_get_identifier(struct wlr_input_device *device) { |
66 | int vendor = device->vendor; | 70 | int vendor = 0, product = 0; |
67 | int product = device->product; | 71 | #if WLR_HAS_LIBINPUT_BACKEND |
72 | if (wlr_input_device_is_libinput(device)) { | ||
73 | struct libinput_device *libinput_dev = wlr_libinput_get_device_handle(device); | ||
74 | vendor = libinput_device_get_id_vendor(libinput_dev); | ||
75 | product = libinput_device_get_id_product(libinput_dev); | ||
76 | } | ||
77 | #endif | ||
78 | |||
68 | char *name = strdup(device->name ? device->name : ""); | 79 | char *name = strdup(device->name ? device->name : ""); |
69 | strip_whitespace(name); | 80 | strip_whitespace(name); |
70 | 81 | ||
@@ -76,20 +87,13 @@ char *input_device_get_identifier(struct wlr_input_device *device) { | |||
76 | } | 87 | } |
77 | } | 88 | } |
78 | 89 | ||
79 | const char *fmt = "%d:%d:%s"; | 90 | char *identifier = format_str("%d:%d:%s", vendor, product, name); |
80 | int len = snprintf(NULL, 0, fmt, vendor, product, name) + 1; | ||
81 | char *identifier = malloc(len); | ||
82 | if (!identifier) { | ||
83 | sway_log(SWAY_ERROR, "Unable to allocate unique input device name"); | ||
84 | return NULL; | ||
85 | } | ||
86 | |||
87 | snprintf(identifier, len, fmt, vendor, product, name); | ||
88 | free(name); | 91 | free(name); |
89 | return identifier; | 92 | return identifier; |
90 | } | 93 | } |
91 | 94 | ||
92 | static bool device_is_touchpad(struct sway_input_device *device) { | 95 | static bool device_is_touchpad(struct sway_input_device *device) { |
96 | #if WLR_HAS_LIBINPUT_BACKEND | ||
93 | if (device->wlr_device->type != WLR_INPUT_DEVICE_POINTER || | 97 | if (device->wlr_device->type != WLR_INPUT_DEVICE_POINTER || |
94 | !wlr_input_device_is_libinput(device->wlr_device)) { | 98 | !wlr_input_device_is_libinput(device->wlr_device)) { |
95 | return false; | 99 | return false; |
@@ -99,6 +103,9 @@ static bool device_is_touchpad(struct sway_input_device *device) { | |||
99 | wlr_libinput_get_device_handle(device->wlr_device); | 103 | wlr_libinput_get_device_handle(device->wlr_device); |
100 | 104 | ||
101 | return libinput_device_config_tap_get_finger_count(libinput_device) > 0; | 105 | return libinput_device_config_tap_get_finger_count(libinput_device) > 0; |
106 | #else | ||
107 | return false; | ||
108 | #endif | ||
102 | } | 109 | } |
103 | 110 | ||
104 | const char *input_device_get_type(struct sway_input_device *device) { | 111 | const char *input_device_get_type(struct sway_input_device *device) { |
@@ -113,7 +120,7 @@ const char *input_device_get_type(struct sway_input_device *device) { | |||
113 | return "keyboard"; | 120 | return "keyboard"; |
114 | case WLR_INPUT_DEVICE_TOUCH: | 121 | case WLR_INPUT_DEVICE_TOUCH: |
115 | return "touch"; | 122 | return "touch"; |
116 | case WLR_INPUT_DEVICE_TABLET_TOOL: | 123 | case WLR_INPUT_DEVICE_TABLET: |
117 | return "tablet_tool"; | 124 | return "tablet_tool"; |
118 | case WLR_INPUT_DEVICE_TABLET_PAD: | 125 | case WLR_INPUT_DEVICE_TABLET_PAD: |
119 | return "tablet_pad"; | 126 | return "tablet_pad"; |
@@ -236,7 +243,11 @@ static void handle_new_input(struct wl_listener *listener, void *data) { | |||
236 | 243 | ||
237 | apply_input_type_config(input_device); | 244 | apply_input_type_config(input_device); |
238 | 245 | ||
239 | sway_input_configure_libinput_device(input_device); | 246 | #if WLR_HAS_LIBINPUT_BACKEND |
247 | bool config_changed = sway_input_configure_libinput_device(input_device); | ||
248 | #else | ||
249 | bool config_changed = false; | ||
250 | #endif | ||
240 | 251 | ||
241 | wl_signal_add(&device->events.destroy, &input_device->device_destroy); | 252 | wl_signal_add(&device->events.destroy, &input_device->device_destroy); |
242 | input_device->device_destroy.notify = handle_device_destroy; | 253 | input_device->device_destroy.notify = handle_device_destroy; |
@@ -274,29 +285,9 @@ static void handle_new_input(struct wl_listener *listener, void *data) { | |||
274 | } | 285 | } |
275 | 286 | ||
276 | ipc_event_input("added", input_device); | 287 | ipc_event_input("added", input_device); |
277 | } | ||
278 | 288 | ||
279 | static void handle_inhibit_activate(struct wl_listener *listener, void *data) { | 289 | if (config_changed) { |
280 | struct sway_input_manager *input_manager = wl_container_of( | 290 | ipc_event_input("libinput_config", input_device); |
281 | listener, input_manager, inhibit_activate); | ||
282 | struct sway_seat *seat; | ||
283 | wl_list_for_each(seat, &input_manager->seats, link) { | ||
284 | seat_set_exclusive_client(seat, input_manager->inhibit->active_client); | ||
285 | } | ||
286 | } | ||
287 | |||
288 | static void handle_inhibit_deactivate(struct wl_listener *listener, void *data) { | ||
289 | struct sway_input_manager *input_manager = wl_container_of( | ||
290 | listener, input_manager, inhibit_deactivate); | ||
291 | struct sway_seat *seat; | ||
292 | wl_list_for_each(seat, &input_manager->seats, link) { | ||
293 | seat_set_exclusive_client(seat, NULL); | ||
294 | struct sway_node *previous = seat_get_focus(seat); | ||
295 | if (previous) { | ||
296 | // Hack to get seat to re-focus the return value of get_focus | ||
297 | seat_set_focus(seat, NULL); | ||
298 | seat_set_focus(seat, previous); | ||
299 | } | ||
300 | } | 291 | } |
301 | } | 292 | } |
302 | 293 | ||
@@ -377,7 +368,7 @@ void handle_virtual_keyboard(struct wl_listener *listener, void *data) { | |||
377 | struct sway_input_manager *input_manager = | 368 | struct sway_input_manager *input_manager = |
378 | wl_container_of(listener, input_manager, virtual_keyboard_new); | 369 | wl_container_of(listener, input_manager, virtual_keyboard_new); |
379 | struct wlr_virtual_keyboard_v1 *keyboard = data; | 370 | struct wlr_virtual_keyboard_v1 *keyboard = data; |
380 | struct wlr_input_device *device = &keyboard->input_device; | 371 | struct wlr_input_device *device = &keyboard->keyboard.base; |
381 | 372 | ||
382 | // TODO: Amend protocol to allow NULL seat | 373 | // TODO: Amend protocol to allow NULL seat |
383 | struct sway_seat *seat = keyboard->seat ? | 374 | struct sway_seat *seat = keyboard->seat ? |
@@ -410,7 +401,7 @@ void handle_virtual_pointer(struct wl_listener *listener, void *data) { | |||
410 | wl_container_of(listener, input_manager, virtual_pointer_new); | 401 | wl_container_of(listener, input_manager, virtual_pointer_new); |
411 | struct wlr_virtual_pointer_v1_new_pointer_event *event = data; | 402 | struct wlr_virtual_pointer_v1_new_pointer_event *event = data; |
412 | struct wlr_virtual_pointer_v1 *pointer = event->new_pointer; | 403 | struct wlr_virtual_pointer_v1 *pointer = event->new_pointer; |
413 | struct wlr_input_device *device = &pointer->input_device; | 404 | struct wlr_input_device *device = &pointer->pointer.base; |
414 | 405 | ||
415 | struct sway_seat *seat = event->suggested_seat ? | 406 | struct sway_seat *seat = event->suggested_seat ? |
416 | input_manager_sway_seat_from_wlr_seat(event->suggested_seat) : | 407 | input_manager_sway_seat_from_wlr_seat(event->suggested_seat) : |
@@ -442,6 +433,20 @@ void handle_virtual_pointer(struct wl_listener *listener, void *data) { | |||
442 | } | 433 | } |
443 | } | 434 | } |
444 | 435 | ||
436 | static void handle_transient_seat_manager_create_seat( | ||
437 | struct wl_listener *listener, void *data) { | ||
438 | struct wlr_transient_seat_v1 *transient_seat = data; | ||
439 | static uint64_t i; | ||
440 | char name[256]; | ||
441 | snprintf(name, sizeof(name), "transient-%"PRIx64, i++); | ||
442 | struct sway_seat *seat = seat_create(name); | ||
443 | if (seat && seat->wlr_seat) { | ||
444 | wlr_transient_seat_v1_ready(transient_seat, seat->wlr_seat); | ||
445 | } else { | ||
446 | wlr_transient_seat_v1_deny(transient_seat); | ||
447 | } | ||
448 | } | ||
449 | |||
445 | struct sway_input_manager *input_manager_create(struct sway_server *server) { | 450 | struct sway_input_manager *input_manager_create(struct sway_server *server) { |
446 | struct sway_input_manager *input = | 451 | struct sway_input_manager *input = |
447 | calloc(1, sizeof(struct sway_input_manager)); | 452 | calloc(1, sizeof(struct sway_input_manager)); |
@@ -468,14 +473,6 @@ struct sway_input_manager *input_manager_create(struct sway_server *server) { | |||
468 | &input->virtual_pointer_new); | 473 | &input->virtual_pointer_new); |
469 | input->virtual_pointer_new.notify = handle_virtual_pointer; | 474 | input->virtual_pointer_new.notify = handle_virtual_pointer; |
470 | 475 | ||
471 | input->inhibit = wlr_input_inhibit_manager_create(server->wl_display); | ||
472 | input->inhibit_activate.notify = handle_inhibit_activate; | ||
473 | wl_signal_add(&input->inhibit->events.activate, | ||
474 | &input->inhibit_activate); | ||
475 | input->inhibit_deactivate.notify = handle_inhibit_deactivate; | ||
476 | wl_signal_add(&input->inhibit->events.deactivate, | ||
477 | &input->inhibit_deactivate); | ||
478 | |||
479 | input->keyboard_shortcuts_inhibit = | 476 | input->keyboard_shortcuts_inhibit = |
480 | wlr_keyboard_shortcuts_inhibit_v1_create(server->wl_display); | 477 | wlr_keyboard_shortcuts_inhibit_v1_create(server->wl_display); |
481 | input->keyboard_shortcuts_inhibit_new_inhibitor.notify = | 478 | input->keyboard_shortcuts_inhibit_new_inhibitor.notify = |
@@ -483,6 +480,17 @@ struct sway_input_manager *input_manager_create(struct sway_server *server) { | |||
483 | wl_signal_add(&input->keyboard_shortcuts_inhibit->events.new_inhibitor, | 480 | wl_signal_add(&input->keyboard_shortcuts_inhibit->events.new_inhibitor, |
484 | &input->keyboard_shortcuts_inhibit_new_inhibitor); | 481 | &input->keyboard_shortcuts_inhibit_new_inhibitor); |
485 | 482 | ||
483 | input->pointer_gestures = wlr_pointer_gestures_v1_create(server->wl_display); | ||
484 | |||
485 | input->transient_seat_manager = | ||
486 | wlr_transient_seat_manager_v1_create(server->wl_display); | ||
487 | assert(input->transient_seat_manager); | ||
488 | |||
489 | input->transient_seat_create.notify = | ||
490 | handle_transient_seat_manager_create_seat; | ||
491 | wl_signal_add(&input->transient_seat_manager->events.create_seat, | ||
492 | &input->transient_seat_create); | ||
493 | |||
486 | return input; | 494 | return input; |
487 | } | 495 | } |
488 | 496 | ||
@@ -520,21 +528,50 @@ static void retranslate_keysyms(struct input_config *input_config) { | |||
520 | return; | 528 | return; |
521 | } | 529 | } |
522 | } | 530 | } |
531 | |||
532 | for (int i = 0; i < config->input_type_configs->length; ++i) { | ||
533 | struct input_config *ic = config->input_type_configs->items[i]; | ||
534 | if (ic->xkb_layout || ic->xkb_file) { | ||
535 | // this is the first config with xkb_layout or xkb_file | ||
536 | if (ic->identifier == input_config->identifier) { | ||
537 | translate_keysyms(ic); | ||
538 | } | ||
539 | |||
540 | return; | ||
541 | } | ||
542 | } | ||
523 | } | 543 | } |
524 | 544 | ||
525 | static void input_manager_configure_input( | 545 | static void input_manager_configure_input( |
526 | struct sway_input_device *input_device) { | 546 | struct sway_input_device *input_device) { |
527 | sway_input_configure_libinput_device(input_device); | 547 | #if WLR_HAS_LIBINPUT_BACKEND |
548 | bool config_changed = sway_input_configure_libinput_device(input_device); | ||
549 | #else | ||
550 | bool config_changed = false; | ||
551 | #endif | ||
528 | struct sway_seat *seat = NULL; | 552 | struct sway_seat *seat = NULL; |
529 | wl_list_for_each(seat, &server.input->seats, link) { | 553 | wl_list_for_each(seat, &server.input->seats, link) { |
530 | seat_configure_device(seat, input_device); | 554 | seat_configure_device(seat, input_device); |
531 | } | 555 | } |
556 | if (config_changed) { | ||
557 | ipc_event_input("libinput_config", input_device); | ||
558 | } | ||
532 | } | 559 | } |
533 | 560 | ||
534 | void input_manager_configure_all_inputs(void) { | 561 | void input_manager_configure_all_input_mappings(void) { |
535 | struct sway_input_device *input_device = NULL; | 562 | struct sway_input_device *input_device; |
536 | wl_list_for_each(input_device, &server.input->devices, link) { | 563 | wl_list_for_each(input_device, &server.input->devices, link) { |
537 | input_manager_configure_input(input_device); | 564 | struct sway_seat *seat; |
565 | wl_list_for_each(seat, &server.input->seats, link) { | ||
566 | seat_configure_device_mapping(seat, input_device); | ||
567 | } | ||
568 | |||
569 | #if WLR_HAS_LIBINPUT_BACKEND | ||
570 | // Input devices mapped to unavailable outputs get their libinput | ||
571 | // send_events setting switched to false. We need to re-enable this | ||
572 | // when the output appears. | ||
573 | sway_input_configure_libinput_device_send_events(input_device); | ||
574 | #endif | ||
538 | } | 575 | } |
539 | } | 576 | } |
540 | 577 | ||
@@ -556,7 +593,9 @@ void input_manager_apply_input_config(struct input_config *input_config) { | |||
556 | } | 593 | } |
557 | 594 | ||
558 | void input_manager_reset_input(struct sway_input_device *input_device) { | 595 | void input_manager_reset_input(struct sway_input_device *input_device) { |
596 | #if WLR_HAS_LIBINPUT_BACKEND | ||
559 | sway_input_reset_libinput_device(input_device); | 597 | sway_input_reset_libinput_device(input_device); |
598 | #endif | ||
560 | struct sway_seat *seat = NULL; | 599 | struct sway_seat *seat = NULL; |
561 | wl_list_for_each(seat, &server.input->seats, link) { | 600 | wl_list_for_each(seat, &server.input->seats, link) { |
562 | seat_reset_device(seat, input_device); | 601 | seat_reset_device(seat, input_device); |
@@ -564,6 +603,13 @@ void input_manager_reset_input(struct sway_input_device *input_device) { | |||
564 | } | 603 | } |
565 | 604 | ||
566 | void input_manager_reset_all_inputs(void) { | 605 | void input_manager_reset_all_inputs(void) { |
606 | // Set the active keyboard to NULL to avoid spamming configuration updates | ||
607 | // for all keyboard devices. | ||
608 | struct sway_seat *seat; | ||
609 | wl_list_for_each(seat, &server.input->seats, link) { | ||
610 | wlr_seat_set_keyboard(seat->wlr_seat, NULL); | ||
611 | } | ||
612 | |||
567 | struct sway_input_device *input_device = NULL; | 613 | struct sway_input_device *input_device = NULL; |
568 | wl_list_for_each(input_device, &server.input->devices, link) { | 614 | wl_list_for_each(input_device, &server.input->devices, link) { |
569 | input_manager_reset_input(input_device); | 615 | input_manager_reset_input(input_device); |
@@ -572,7 +618,6 @@ void input_manager_reset_all_inputs(void) { | |||
572 | // If there is at least one keyboard using the default keymap, repeat delay, | 618 | // If there is at least one keyboard using the default keymap, repeat delay, |
573 | // and repeat rate, then it is possible that there is a keyboard group that | 619 | // and repeat rate, then it is possible that there is a keyboard group that |
574 | // need their keyboard disarmed. | 620 | // need their keyboard disarmed. |
575 | struct sway_seat *seat; | ||
576 | wl_list_for_each(seat, &server.input->seats, link) { | 621 | wl_list_for_each(seat, &server.input->seats, link) { |
577 | struct sway_keyboard_group *group; | 622 | struct sway_keyboard_group *group; |
578 | wl_list_for_each(group, &seat->keyboard_groups, link) { | 623 | wl_list_for_each(group, &seat->keyboard_groups, link) { |
diff --git a/sway/input/keyboard.c b/sway/input/keyboard.c index ce259eb2..f74d0658 100644 --- a/sway/input/keyboard.c +++ b/sway/input/keyboard.c | |||
@@ -1,15 +1,13 @@ | |||
1 | #include <assert.h> | 1 | #include <assert.h> |
2 | #include <limits.h> | 2 | #include <limits.h> |
3 | #include <strings.h> | 3 | #include <strings.h> |
4 | #include <wlr/config.h> | ||
4 | #include <wlr/backend/multi.h> | 5 | #include <wlr/backend/multi.h> |
5 | #include <wlr/backend/session.h> | ||
6 | #include <wlr/interfaces/wlr_keyboard.h> | 6 | #include <wlr/interfaces/wlr_keyboard.h> |
7 | #include <wlr/types/wlr_idle.h> | ||
8 | #include <wlr/types/wlr_keyboard.h> | 7 | #include <wlr/types/wlr_keyboard.h> |
9 | #include <wlr/types/wlr_keyboard_group.h> | 8 | #include <wlr/types/wlr_keyboard_group.h> |
10 | #include <xkbcommon/xkbcommon-names.h> | 9 | #include <xkbcommon/xkbcommon-names.h> |
11 | #include "sway/commands.h" | 10 | #include "sway/commands.h" |
12 | #include "sway/desktop/transaction.h" | ||
13 | #include "sway/input/input-manager.h" | 11 | #include "sway/input/input-manager.h" |
14 | #include "sway/input/keyboard.h" | 12 | #include "sway/input/keyboard.h" |
15 | #include "sway/input/seat.h" | 13 | #include "sway/input/seat.h" |
@@ -17,6 +15,10 @@ | |||
17 | #include "sway/ipc-server.h" | 15 | #include "sway/ipc-server.h" |
18 | #include "log.h" | 16 | #include "log.h" |
19 | 17 | ||
18 | #if WLR_HAS_SESSION | ||
19 | #include <wlr/backend/session.h> | ||
20 | #endif | ||
21 | |||
20 | static struct modifier_key { | 22 | static struct modifier_key { |
21 | char *name; | 23 | char *name; |
22 | uint32_t mod; | 24 | uint32_t mod; |
@@ -30,6 +32,7 @@ static struct modifier_key { | |||
30 | { XKB_MOD_NAME_NUM, WLR_MODIFIER_MOD2 }, | 32 | { XKB_MOD_NAME_NUM, WLR_MODIFIER_MOD2 }, |
31 | { "Mod3", WLR_MODIFIER_MOD3 }, | 33 | { "Mod3", WLR_MODIFIER_MOD3 }, |
32 | { XKB_MOD_NAME_LOGO, WLR_MODIFIER_LOGO }, | 34 | { XKB_MOD_NAME_LOGO, WLR_MODIFIER_LOGO }, |
35 | { "Super", WLR_MODIFIER_LOGO }, | ||
33 | { "Mod5", WLR_MODIFIER_MOD5 }, | 36 | { "Mod5", WLR_MODIFIER_MOD5 }, |
34 | }; | 37 | }; |
35 | 38 | ||
@@ -265,14 +268,12 @@ static bool keyboard_execute_compositor_binding(struct sway_keyboard *keyboard, | |||
265 | xkb_keysym_t keysym = pressed_keysyms[i]; | 268 | xkb_keysym_t keysym = pressed_keysyms[i]; |
266 | if (keysym >= XKB_KEY_XF86Switch_VT_1 && | 269 | if (keysym >= XKB_KEY_XF86Switch_VT_1 && |
267 | keysym <= XKB_KEY_XF86Switch_VT_12) { | 270 | keysym <= XKB_KEY_XF86Switch_VT_12) { |
268 | if (wlr_backend_is_multi(server.backend)) { | 271 | #if WLR_HAS_SESSION |
269 | struct wlr_session *session = | 272 | if (server.session) { |
270 | wlr_backend_get_session(server.backend); | 273 | unsigned vt = keysym - XKB_KEY_XF86Switch_VT_1 + 1; |
271 | if (session) { | 274 | wlr_session_change_vt(server.session, vt); |
272 | unsigned vt = keysym - XKB_KEY_XF86Switch_VT_1 + 1; | ||
273 | wlr_session_change_vt(session, vt); | ||
274 | } | ||
275 | } | 275 | } |
276 | #endif | ||
276 | return true; | 277 | return true; |
277 | } | 278 | } |
278 | } | 279 | } |
@@ -292,14 +293,12 @@ static bool keyboard_execute_compositor_binding(struct sway_keyboard *keyboard, | |||
292 | static size_t keyboard_keysyms_translated(struct sway_keyboard *keyboard, | 293 | static size_t keyboard_keysyms_translated(struct sway_keyboard *keyboard, |
293 | xkb_keycode_t keycode, const xkb_keysym_t **keysyms, | 294 | xkb_keycode_t keycode, const xkb_keysym_t **keysyms, |
294 | uint32_t *modifiers) { | 295 | uint32_t *modifiers) { |
295 | struct wlr_input_device *device = | 296 | *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( | 297 | xkb_mod_mask_t consumed = xkb_state_key_get_consumed_mods2( |
299 | device->keyboard->xkb_state, keycode, XKB_CONSUMED_MODE_XKB); | 298 | keyboard->wlr->xkb_state, keycode, XKB_CONSUMED_MODE_XKB); |
300 | *modifiers = *modifiers & ~consumed; | 299 | *modifiers = *modifiers & ~consumed; |
301 | 300 | ||
302 | return xkb_state_key_get_syms(device->keyboard->xkb_state, | 301 | return xkb_state_key_get_syms(keyboard->wlr->xkb_state, |
303 | keycode, keysyms); | 302 | keycode, keysyms); |
304 | } | 303 | } |
305 | 304 | ||
@@ -315,13 +314,11 @@ static size_t keyboard_keysyms_translated(struct sway_keyboard *keyboard, | |||
315 | static size_t keyboard_keysyms_raw(struct sway_keyboard *keyboard, | 314 | static size_t keyboard_keysyms_raw(struct sway_keyboard *keyboard, |
316 | xkb_keycode_t keycode, const xkb_keysym_t **keysyms, | 315 | xkb_keycode_t keycode, const xkb_keysym_t **keysyms, |
317 | uint32_t *modifiers) { | 316 | uint32_t *modifiers) { |
318 | struct wlr_input_device *device = | 317 | *modifiers = wlr_keyboard_get_modifiers(keyboard->wlr); |
319 | keyboard->seat_device->input_device->wlr_device; | ||
320 | *modifiers = wlr_keyboard_get_modifiers(device->keyboard); | ||
321 | 318 | ||
322 | xkb_layout_index_t layout_index = xkb_state_key_get_layout( | 319 | xkb_layout_index_t layout_index = xkb_state_key_get_layout( |
323 | device->keyboard->xkb_state, keycode); | 320 | keyboard->wlr->xkb_state, keycode); |
324 | return xkb_keymap_key_get_syms_by_level(device->keyboard->keymap, | 321 | return xkb_keymap_key_get_syms_by_level(keyboard->wlr->keymap, |
325 | keycode, layout_index, 0, keysyms); | 322 | keycode, layout_index, 0, keysyms); |
326 | } | 323 | } |
327 | 324 | ||
@@ -361,8 +358,7 @@ static void update_keyboard_state(struct sway_keyboard *keyboard, | |||
361 | keyinfo->keycode, &keyinfo->translated_keysyms, | 358 | keyinfo->keycode, &keyinfo->translated_keysyms, |
362 | &keyinfo->translated_modifiers); | 359 | &keyinfo->translated_modifiers); |
363 | 360 | ||
364 | keyinfo->code_modifiers = wlr_keyboard_get_modifiers( | 361 | keyinfo->code_modifiers = wlr_keyboard_get_modifiers(keyboard->wlr); |
365 | keyboard->seat_device->input_device->wlr_device->keyboard); | ||
366 | 362 | ||
367 | // Update shortcut model keyinfo | 363 | // Update shortcut model keyinfo |
368 | update_shortcut_state(&keyboard->state_keycodes, raw_keycode, keystate, | 364 | update_shortcut_state(&keyboard->state_keycodes, raw_keycode, keystate, |
@@ -379,16 +375,38 @@ static void update_keyboard_state(struct sway_keyboard *keyboard, | |||
379 | } | 375 | } |
380 | } | 376 | } |
381 | 377 | ||
378 | /** | ||
379 | * Get keyboard grab of the seat from sway_keyboard if we should forward events | ||
380 | * to it. | ||
381 | * | ||
382 | * Returns NULL if the keyboard is not grabbed by an input method, | ||
383 | * or if event is from virtual keyboard of the same client as grab. | ||
384 | * TODO: see swaywm/wlroots#2322 | ||
385 | */ | ||
386 | static struct wlr_input_method_keyboard_grab_v2 *keyboard_get_im_grab( | ||
387 | struct sway_keyboard *keyboard) { | ||
388 | struct wlr_input_method_v2 *input_method = keyboard->seat_device-> | ||
389 | sway_seat->im_relay.input_method; | ||
390 | struct wlr_virtual_keyboard_v1 *virtual_keyboard = | ||
391 | wlr_input_device_get_virtual_keyboard(keyboard->seat_device->input_device->wlr_device); | ||
392 | if (!input_method || !input_method->keyboard_grab || (virtual_keyboard && | ||
393 | wl_resource_get_client(virtual_keyboard->resource) == | ||
394 | wl_resource_get_client(input_method->keyboard_grab->resource))) { | ||
395 | return NULL; | ||
396 | } | ||
397 | return input_method->keyboard_grab; | ||
398 | } | ||
399 | |||
382 | static void handle_key_event(struct sway_keyboard *keyboard, | 400 | static void handle_key_event(struct sway_keyboard *keyboard, |
383 | struct wlr_event_keyboard_key *event) { | 401 | struct wlr_keyboard_key_event *event) { |
384 | struct sway_seat *seat = keyboard->seat_device->sway_seat; | 402 | struct sway_seat *seat = keyboard->seat_device->sway_seat; |
385 | struct wlr_seat *wlr_seat = seat->wlr_seat; | 403 | struct wlr_seat *wlr_seat = seat->wlr_seat; |
386 | struct wlr_input_device *wlr_device = | 404 | struct wlr_input_device *wlr_device = |
387 | keyboard->seat_device->input_device->wlr_device; | 405 | keyboard->seat_device->input_device->wlr_device; |
388 | char *device_identifier = input_device_get_identifier(wlr_device); | 406 | char *device_identifier = input_device_get_identifier(wlr_device); |
389 | bool exact_identifier = wlr_device->keyboard->group != NULL; | 407 | bool exact_identifier = keyboard->wlr->group != NULL; |
390 | seat_idle_notify_activity(seat, IDLE_SOURCE_KEYBOARD); | 408 | seat_idle_notify_activity(seat, IDLE_SOURCE_KEYBOARD); |
391 | bool input_inhibited = seat->exclusive_client != NULL; | 409 | bool locked = server.session_lock.lock; |
392 | struct sway_keyboard_shortcuts_inhibitor *sway_inhibitor = | 410 | struct sway_keyboard_shortcuts_inhibitor *sway_inhibitor = |
393 | keyboard_shortcuts_inhibitor_get_for_focused_surface(seat); | 411 | keyboard_shortcuts_inhibitor_get_for_focused_surface(seat); |
394 | bool shortcuts_inhibited = sway_inhibitor && sway_inhibitor->inhibitor->active; | 412 | bool shortcuts_inhibited = sway_inhibitor && sway_inhibitor->inhibitor->active; |
@@ -406,17 +424,17 @@ static void handle_key_event(struct sway_keyboard *keyboard, | |||
406 | struct sway_binding *binding_released = NULL; | 424 | struct sway_binding *binding_released = NULL; |
407 | get_active_binding(&keyboard->state_keycodes, | 425 | get_active_binding(&keyboard->state_keycodes, |
408 | config->current_mode->keycode_bindings, &binding_released, | 426 | config->current_mode->keycode_bindings, &binding_released, |
409 | keyinfo.code_modifiers, true, input_inhibited, | 427 | keyinfo.code_modifiers, true, locked, |
410 | shortcuts_inhibited, device_identifier, | 428 | shortcuts_inhibited, device_identifier, |
411 | exact_identifier, keyboard->effective_layout); | 429 | exact_identifier, keyboard->effective_layout); |
412 | get_active_binding(&keyboard->state_keysyms_raw, | 430 | get_active_binding(&keyboard->state_keysyms_raw, |
413 | config->current_mode->keysym_bindings, &binding_released, | 431 | config->current_mode->keysym_bindings, &binding_released, |
414 | keyinfo.raw_modifiers, true, input_inhibited, | 432 | keyinfo.raw_modifiers, true, locked, |
415 | shortcuts_inhibited, device_identifier, | 433 | shortcuts_inhibited, device_identifier, |
416 | exact_identifier, keyboard->effective_layout); | 434 | exact_identifier, keyboard->effective_layout); |
417 | get_active_binding(&keyboard->state_keysyms_translated, | 435 | get_active_binding(&keyboard->state_keysyms_translated, |
418 | config->current_mode->keysym_bindings, &binding_released, | 436 | config->current_mode->keysym_bindings, &binding_released, |
419 | keyinfo.translated_modifiers, true, input_inhibited, | 437 | keyinfo.translated_modifiers, true, locked, |
420 | shortcuts_inhibited, device_identifier, | 438 | shortcuts_inhibited, device_identifier, |
421 | exact_identifier, keyboard->effective_layout); | 439 | exact_identifier, keyboard->effective_layout); |
422 | 440 | ||
@@ -438,17 +456,17 @@ static void handle_key_event(struct sway_keyboard *keyboard, | |||
438 | if (event->state == WL_KEYBOARD_KEY_STATE_PRESSED) { | 456 | if (event->state == WL_KEYBOARD_KEY_STATE_PRESSED) { |
439 | get_active_binding(&keyboard->state_keycodes, | 457 | get_active_binding(&keyboard->state_keycodes, |
440 | config->current_mode->keycode_bindings, &binding, | 458 | config->current_mode->keycode_bindings, &binding, |
441 | keyinfo.code_modifiers, false, input_inhibited, | 459 | keyinfo.code_modifiers, false, locked, |
442 | shortcuts_inhibited, device_identifier, | 460 | shortcuts_inhibited, device_identifier, |
443 | exact_identifier, keyboard->effective_layout); | 461 | exact_identifier, keyboard->effective_layout); |
444 | get_active_binding(&keyboard->state_keysyms_raw, | 462 | get_active_binding(&keyboard->state_keysyms_raw, |
445 | config->current_mode->keysym_bindings, &binding, | 463 | config->current_mode->keysym_bindings, &binding, |
446 | keyinfo.raw_modifiers, false, input_inhibited, | 464 | keyinfo.raw_modifiers, false, locked, |
447 | shortcuts_inhibited, device_identifier, | 465 | shortcuts_inhibited, device_identifier, |
448 | exact_identifier, keyboard->effective_layout); | 466 | exact_identifier, keyboard->effective_layout); |
449 | get_active_binding(&keyboard->state_keysyms_translated, | 467 | get_active_binding(&keyboard->state_keysyms_translated, |
450 | config->current_mode->keysym_bindings, &binding, | 468 | config->current_mode->keysym_bindings, &binding, |
451 | keyinfo.translated_modifiers, false, input_inhibited, | 469 | keyinfo.translated_modifiers, false, locked, |
452 | shortcuts_inhibited, device_identifier, | 470 | shortcuts_inhibited, device_identifier, |
453 | exact_identifier, keyboard->effective_layout); | 471 | exact_identifier, keyboard->effective_layout); |
454 | } | 472 | } |
@@ -456,10 +474,10 @@ static void handle_key_event(struct sway_keyboard *keyboard, | |||
456 | // Set up (or clear) keyboard repeat for a pressed binding. Since the | 474 | // 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 | 475 | // binding may remove the keyboard, the timer needs to be updated first |
458 | if (binding && !(binding->flags & BINDING_NOREPEAT) && | 476 | if (binding && !(binding->flags & BINDING_NOREPEAT) && |
459 | wlr_device->keyboard->repeat_info.delay > 0) { | 477 | keyboard->wlr->repeat_info.delay > 0) { |
460 | keyboard->repeat_binding = binding; | 478 | keyboard->repeat_binding = binding; |
461 | if (wl_event_source_timer_update(keyboard->key_repeat_source, | 479 | if (wl_event_source_timer_update(keyboard->key_repeat_source, |
462 | wlr_device->keyboard->repeat_info.delay) < 0) { | 480 | keyboard->wlr->repeat_info.delay) < 0) { |
463 | sway_log(SWAY_DEBUG, "failed to set key repeat timer"); | 481 | sway_log(SWAY_DEBUG, "failed to set key repeat timer"); |
464 | } | 482 | } |
465 | } else if (keyboard->repeat_binding) { | 483 | } else if (keyboard->repeat_binding) { |
@@ -471,7 +489,7 @@ static void handle_key_event(struct sway_keyboard *keyboard, | |||
471 | handled = true; | 489 | handled = true; |
472 | } | 490 | } |
473 | 491 | ||
474 | if (!handled && wlr_device->keyboard->group) { | 492 | if (!handled && keyboard->wlr->group) { |
475 | // Only handle device specific bindings for keyboards in a group | 493 | // Only handle device specific bindings for keyboards in a group |
476 | free(device_identifier); | 494 | free(device_identifier); |
477 | return; | 495 | return; |
@@ -489,18 +507,41 @@ static void handle_key_event(struct sway_keyboard *keyboard, | |||
489 | keyinfo.raw_keysyms_len); | 507 | keyinfo.raw_keysyms_len); |
490 | } | 508 | } |
491 | 509 | ||
492 | if (!handled || event->state == WL_KEYBOARD_KEY_STATE_RELEASED) { | 510 | if (event->state == WL_KEYBOARD_KEY_STATE_RELEASED) { |
511 | // If the pressed event was sent to a client, also send the released | ||
512 | // event. In particular, don't send the released event to the IM grab. | ||
493 | bool pressed_sent = update_shortcut_state( | 513 | bool pressed_sent = update_shortcut_state( |
494 | &keyboard->state_pressed_sent, event->keycode, event->state, | 514 | &keyboard->state_pressed_sent, event->keycode, |
495 | keyinfo.keycode, 0); | 515 | event->state, keyinfo.keycode, 0); |
496 | if (pressed_sent || event->state == WL_KEYBOARD_KEY_STATE_PRESSED) { | 516 | if (pressed_sent) { |
497 | wlr_seat_set_keyboard(wlr_seat, wlr_device); | 517 | wlr_seat_set_keyboard(wlr_seat, keyboard->wlr); |
498 | wlr_seat_keyboard_notify_key(wlr_seat, event->time_msec, | 518 | wlr_seat_keyboard_notify_key(wlr_seat, event->time_msec, |
499 | event->keycode, event->state); | 519 | event->keycode, event->state); |
520 | handled = true; | ||
521 | } | ||
522 | } | ||
523 | |||
524 | if (!handled) { | ||
525 | struct wlr_input_method_keyboard_grab_v2 *kb_grab = keyboard_get_im_grab(keyboard); | ||
526 | |||
527 | if (kb_grab) { | ||
528 | wlr_input_method_keyboard_grab_v2_set_keyboard(kb_grab, keyboard->wlr); | ||
529 | wlr_input_method_keyboard_grab_v2_send_key(kb_grab, | ||
530 | event->time_msec, event->keycode, event->state); | ||
531 | handled = true; | ||
500 | } | 532 | } |
501 | } | 533 | } |
502 | 534 | ||
503 | transaction_commit_dirty(); | 535 | if (!handled && event->state != WL_KEYBOARD_KEY_STATE_RELEASED) { |
536 | // If a released event failed pressed sent test, and not in sent to | ||
537 | // keyboard grab, it is still not handled. Don't handle released here. | ||
538 | update_shortcut_state( | ||
539 | &keyboard->state_pressed_sent, event->keycode, event->state, | ||
540 | keyinfo.keycode, 0); | ||
541 | wlr_seat_set_keyboard(wlr_seat, keyboard->wlr); | ||
542 | wlr_seat_keyboard_notify_key(wlr_seat, event->time_msec, | ||
543 | event->keycode, event->state); | ||
544 | } | ||
504 | 545 | ||
505 | free(device_identifier); | 546 | free(device_identifier); |
506 | } | 547 | } |
@@ -573,21 +614,18 @@ static void handle_keyboard_group_leave(struct wl_listener *listener, | |||
573 | } | 614 | } |
574 | 615 | ||
575 | static int handle_keyboard_repeat(void *data) { | 616 | static int handle_keyboard_repeat(void *data) { |
576 | struct sway_keyboard *keyboard = (struct sway_keyboard *)data; | 617 | 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) { | 618 | if (keyboard->repeat_binding) { |
580 | if (wlr_device->repeat_info.rate > 0) { | 619 | if (keyboard->wlr->repeat_info.rate > 0) { |
581 | // We queue the next event first, as the command might cancel it | 620 | // We queue the next event first, as the command might cancel it |
582 | if (wl_event_source_timer_update(keyboard->key_repeat_source, | 621 | if (wl_event_source_timer_update(keyboard->key_repeat_source, |
583 | 1000 / wlr_device->repeat_info.rate) < 0) { | 622 | 1000 / keyboard->wlr->repeat_info.rate) < 0) { |
584 | sway_log(SWAY_DEBUG, "failed to update key repeat timer"); | 623 | sway_log(SWAY_DEBUG, "failed to update key repeat timer"); |
585 | } | 624 | } |
586 | } | 625 | } |
587 | 626 | ||
588 | seat_execute_command(keyboard->seat_device->sway_seat, | 627 | seat_execute_command(keyboard->seat_device->sway_seat, |
589 | keyboard->repeat_binding); | 628 | keyboard->repeat_binding); |
590 | transaction_commit_dirty(); | ||
591 | } | 629 | } |
592 | return 0; | 630 | return 0; |
593 | } | 631 | } |
@@ -614,22 +652,28 @@ static void determine_bar_visibility(uint32_t modifiers) { | |||
614 | } | 652 | } |
615 | 653 | ||
616 | static void handle_modifier_event(struct sway_keyboard *keyboard) { | 654 | static void handle_modifier_event(struct sway_keyboard *keyboard) { |
617 | struct wlr_input_device *wlr_device = | 655 | if (!keyboard->wlr->group) { |
618 | keyboard->seat_device->input_device->wlr_device; | 656 | 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 | 657 | ||
625 | uint32_t modifiers = wlr_keyboard_get_modifiers(wlr_device->keyboard); | 658 | if (kb_grab) { |
659 | wlr_input_method_keyboard_grab_v2_set_keyboard(kb_grab, keyboard->wlr); | ||
660 | wlr_input_method_keyboard_grab_v2_send_modifiers(kb_grab, | ||
661 | &keyboard->wlr->modifiers); | ||
662 | } else { | ||
663 | struct wlr_seat *wlr_seat = keyboard->seat_device->sway_seat->wlr_seat; | ||
664 | wlr_seat_set_keyboard(wlr_seat, keyboard->wlr); | ||
665 | wlr_seat_keyboard_notify_modifiers(wlr_seat, | ||
666 | &keyboard->wlr->modifiers); | ||
667 | } | ||
668 | |||
669 | uint32_t modifiers = wlr_keyboard_get_modifiers(keyboard->wlr); | ||
626 | determine_bar_visibility(modifiers); | 670 | determine_bar_visibility(modifiers); |
627 | } | 671 | } |
628 | 672 | ||
629 | if (wlr_device->keyboard->modifiers.group != keyboard->effective_layout) { | 673 | if (keyboard->wlr->modifiers.group != keyboard->effective_layout) { |
630 | keyboard->effective_layout = wlr_device->keyboard->modifiers.group; | 674 | keyboard->effective_layout = keyboard->wlr->modifiers.group; |
631 | 675 | ||
632 | if (!wlr_keyboard_group_from_wlr_keyboard(wlr_device->keyboard)) { | 676 | if (!wlr_keyboard_group_from_wlr_keyboard(keyboard->wlr)) { |
633 | ipc_event_input("xkb_layout", keyboard->seat_device->input_device); | 677 | ipc_event_input("xkb_layout", keyboard->seat_device->input_device); |
634 | } | 678 | } |
635 | } | 679 | } |
@@ -658,6 +702,7 @@ struct sway_keyboard *sway_keyboard_create(struct sway_seat *seat, | |||
658 | } | 702 | } |
659 | 703 | ||
660 | keyboard->seat_device = device; | 704 | keyboard->seat_device = device; |
705 | keyboard->wlr = wlr_keyboard_from_input_device(device->input_device->wlr_device); | ||
661 | device->keyboard = keyboard; | 706 | device->keyboard = keyboard; |
662 | 707 | ||
663 | wl_list_init(&keyboard->keyboard_key.link); | 708 | wl_list_init(&keyboard->keyboard_key.link); |
@@ -671,23 +716,11 @@ struct sway_keyboard *sway_keyboard_create(struct sway_seat *seat, | |||
671 | 716 | ||
672 | static void handle_xkb_context_log(struct xkb_context *context, | 717 | static void handle_xkb_context_log(struct xkb_context *context, |
673 | enum xkb_log_level level, const char *format, va_list args) { | 718 | enum xkb_log_level level, const char *format, va_list args) { |
674 | va_list args_copy; | 719 | char *error = vformat_str(format, args); |
675 | va_copy(args_copy, args); | ||
676 | size_t length = vsnprintf(NULL, 0, format, args_copy) + 1; | ||
677 | va_end(args_copy); | ||
678 | |||
679 | char *error = malloc(length); | ||
680 | if (!error) { | ||
681 | sway_log(SWAY_ERROR, "Failed to allocate libxkbcommon log message"); | ||
682 | return; | ||
683 | } | ||
684 | |||
685 | va_copy(args_copy, args); | ||
686 | vsnprintf(error, length, format, args_copy); | ||
687 | va_end(args_copy); | ||
688 | 720 | ||
689 | if (error[length - 2] == '\n') { | 721 | size_t len = strlen(error); |
690 | error[length - 2] = '\0'; | 722 | if (error[len - 1] == '\n') { |
723 | error[len - 1] = '\0'; | ||
691 | } | 724 | } |
692 | 725 | ||
693 | sway_log_importance_t importance = SWAY_DEBUG; | 726 | sway_log_importance_t importance = SWAY_DEBUG; |
@@ -708,7 +741,7 @@ static void handle_xkb_context_log(struct xkb_context *context, | |||
708 | 741 | ||
709 | struct xkb_keymap *sway_keyboard_compile_keymap(struct input_config *ic, | 742 | struct xkb_keymap *sway_keyboard_compile_keymap(struct input_config *ic, |
710 | char **error) { | 743 | char **error) { |
711 | struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); | 744 | struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_SECURE_GETENV); |
712 | if (!sway_assert(context, "cannot create XKB context")) { | 745 | if (!sway_assert(context, "cannot create XKB context")) { |
713 | return NULL; | 746 | return NULL; |
714 | } | 747 | } |
@@ -722,13 +755,8 @@ struct xkb_keymap *sway_keyboard_compile_keymap(struct input_config *ic, | |||
722 | if (!keymap_file) { | 755 | if (!keymap_file) { |
723 | sway_log_errno(SWAY_ERROR, "cannot read xkb file %s", ic->xkb_file); | 756 | sway_log_errno(SWAY_ERROR, "cannot read xkb file %s", ic->xkb_file); |
724 | if (error) { | 757 | if (error) { |
725 | size_t len = snprintf(NULL, 0, "cannot read xkb file %s: %s", | 758 | *error = format_str("cannot read xkb file %s: %s", |
726 | ic->xkb_file, strerror(errno)) + 1; | 759 | ic->xkb_file, strerror(errno)); |
727 | *error = malloc(len); | ||
728 | if (*error) { | ||
729 | snprintf(*error, len, "cannot read xkb_file %s: %s", | ||
730 | ic->xkb_file, strerror(errno)); | ||
731 | } | ||
732 | } | 760 | } |
733 | goto cleanup; | 761 | goto cleanup; |
734 | } | 762 | } |
@@ -766,13 +794,12 @@ static void destroy_empty_wlr_keyboard_group(void *data) { | |||
766 | 794 | ||
767 | static void sway_keyboard_group_remove(struct sway_keyboard *keyboard) { | 795 | static void sway_keyboard_group_remove(struct sway_keyboard *keyboard) { |
768 | struct sway_input_device *device = keyboard->seat_device->input_device; | 796 | struct sway_input_device *device = keyboard->seat_device->input_device; |
769 | struct wlr_keyboard *wlr_keyboard = device->wlr_device->keyboard; | 797 | struct wlr_keyboard_group *wlr_group = keyboard->wlr->group; |
770 | struct wlr_keyboard_group *wlr_group = wlr_keyboard->group; | ||
771 | 798 | ||
772 | sway_log(SWAY_DEBUG, "Removing keyboard %s from group %p", | 799 | sway_log(SWAY_DEBUG, "Removing keyboard %s from group %p", |
773 | device->identifier, wlr_group); | 800 | device->identifier, wlr_group); |
774 | 801 | ||
775 | wlr_keyboard_group_remove_keyboard(wlr_keyboard->group, wlr_keyboard); | 802 | wlr_keyboard_group_remove_keyboard(keyboard->wlr->group, keyboard->wlr); |
776 | 803 | ||
777 | if (wl_list_empty(&wlr_group->devices)) { | 804 | if (wl_list_empty(&wlr_group->devices)) { |
778 | sway_log(SWAY_DEBUG, "Destroying empty keyboard group %p", | 805 | sway_log(SWAY_DEBUG, "Destroying empty keyboard group %p", |
@@ -797,9 +824,7 @@ static void sway_keyboard_group_remove(struct sway_keyboard *keyboard) { | |||
797 | } | 824 | } |
798 | 825 | ||
799 | static void sway_keyboard_group_remove_invalid(struct sway_keyboard *keyboard) { | 826 | static void sway_keyboard_group_remove_invalid(struct sway_keyboard *keyboard) { |
800 | struct sway_input_device *device = keyboard->seat_device->input_device; | 827 | if (!keyboard->wlr->group) { |
801 | struct wlr_keyboard *wlr_keyboard = device->wlr_device->keyboard; | ||
802 | if (!wlr_keyboard->group) { | ||
803 | return; | 828 | return; |
804 | } | 829 | } |
805 | 830 | ||
@@ -815,7 +840,7 @@ static void sway_keyboard_group_remove_invalid(struct sway_keyboard *keyboard) { | |||
815 | break; | 840 | break; |
816 | case KEYBOARD_GROUP_DEFAULT: /* fallthrough */ | 841 | case KEYBOARD_GROUP_DEFAULT: /* fallthrough */ |
817 | case KEYBOARD_GROUP_SMART:; | 842 | case KEYBOARD_GROUP_SMART:; |
818 | struct wlr_keyboard_group *group = wlr_keyboard->group; | 843 | struct wlr_keyboard_group *group = keyboard->wlr->group; |
819 | if (!wlr_keyboard_keymaps_match(keyboard->keymap, group->keyboard.keymap) || | 844 | if (!wlr_keyboard_keymaps_match(keyboard->keymap, group->keyboard.keymap) || |
820 | !repeat_info_match(keyboard, &group->keyboard)) { | 845 | !repeat_info_match(keyboard, &group->keyboard)) { |
821 | sway_keyboard_group_remove(keyboard); | 846 | sway_keyboard_group_remove(keyboard); |
@@ -826,7 +851,6 @@ static void sway_keyboard_group_remove_invalid(struct sway_keyboard *keyboard) { | |||
826 | 851 | ||
827 | static void sway_keyboard_group_add(struct sway_keyboard *keyboard) { | 852 | static void sway_keyboard_group_add(struct sway_keyboard *keyboard) { |
828 | struct sway_input_device *device = keyboard->seat_device->input_device; | 853 | 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; | 854 | struct sway_seat *seat = keyboard->seat_device->sway_seat; |
831 | struct seat_config *sc = seat_get_config(seat); | 855 | struct seat_config *sc = seat_get_config(seat); |
832 | 856 | ||
@@ -858,7 +882,7 @@ static void sway_keyboard_group_add(struct sway_keyboard *keyboard) { | |||
858 | repeat_info_match(keyboard, &wlr_group->keyboard)) { | 882 | repeat_info_match(keyboard, &wlr_group->keyboard)) { |
859 | sway_log(SWAY_DEBUG, "Adding keyboard %s to group %p", | 883 | sway_log(SWAY_DEBUG, "Adding keyboard %s to group %p", |
860 | device->identifier, wlr_group); | 884 | device->identifier, wlr_group); |
861 | wlr_keyboard_group_add_keyboard(wlr_group, wlr_keyboard); | 885 | wlr_keyboard_group_add_keyboard(wlr_group, keyboard->wlr); |
862 | return; | 886 | return; |
863 | } | 887 | } |
864 | break; | 888 | break; |
@@ -897,7 +921,7 @@ static void sway_keyboard_group_add(struct sway_keyboard *keyboard) { | |||
897 | goto cleanup; | 921 | goto cleanup; |
898 | } | 922 | } |
899 | sway_group->seat_device->input_device->wlr_device = | 923 | sway_group->seat_device->input_device->wlr_device = |
900 | sway_group->wlr_group->input_device; | 924 | &sway_group->wlr_group->keyboard.base; |
901 | 925 | ||
902 | if (!sway_keyboard_create(seat, sway_group->seat_device)) { | 926 | if (!sway_keyboard_create(seat, sway_group->seat_device)) { |
903 | sway_log(SWAY_ERROR, "Failed to allocate sway_keyboard for group"); | 927 | sway_log(SWAY_ERROR, "Failed to allocate sway_keyboard for group"); |
@@ -906,7 +930,7 @@ static void sway_keyboard_group_add(struct sway_keyboard *keyboard) { | |||
906 | 930 | ||
907 | sway_log(SWAY_DEBUG, "Adding keyboard %s to group %p", | 931 | sway_log(SWAY_DEBUG, "Adding keyboard %s to group %p", |
908 | device->identifier, sway_group->wlr_group); | 932 | device->identifier, sway_group->wlr_group); |
909 | wlr_keyboard_group_add_keyboard(sway_group->wlr_group, wlr_keyboard); | 933 | wlr_keyboard_group_add_keyboard(sway_group->wlr_group, keyboard->wlr); |
910 | 934 | ||
911 | wl_list_insert(&seat->keyboard_groups, &sway_group->link); | 935 | wl_list_insert(&seat->keyboard_groups, &sway_group->link); |
912 | 936 | ||
@@ -938,10 +962,8 @@ cleanup: | |||
938 | void sway_keyboard_configure(struct sway_keyboard *keyboard) { | 962 | void sway_keyboard_configure(struct sway_keyboard *keyboard) { |
939 | struct input_config *input_config = | 963 | struct input_config *input_config = |
940 | input_device_get_config(keyboard->seat_device->input_device); | 964 | 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 | 965 | ||
944 | if (!sway_assert(!wlr_keyboard_group_from_wlr_keyboard(wlr_device->keyboard), | 966 | if (!sway_assert(!wlr_keyboard_group_from_wlr_keyboard(keyboard->wlr), |
945 | "sway_keyboard_configure should not be called with a " | 967 | "sway_keyboard_configure should not be called with a " |
946 | "keyboard group's keyboard")) { | 968 | "keyboard group's keyboard")) { |
947 | return; | 969 | return; |
@@ -983,11 +1005,11 @@ void sway_keyboard_configure(struct sway_keyboard *keyboard) { | |||
983 | 1005 | ||
984 | sway_keyboard_group_remove_invalid(keyboard); | 1006 | sway_keyboard_group_remove_invalid(keyboard); |
985 | 1007 | ||
986 | wlr_keyboard_set_keymap(wlr_device->keyboard, keyboard->keymap); | 1008 | wlr_keyboard_set_keymap(keyboard->wlr, keyboard->keymap); |
987 | wlr_keyboard_set_repeat_info(wlr_device->keyboard, | 1009 | wlr_keyboard_set_repeat_info(keyboard->wlr, |
988 | keyboard->repeat_rate, keyboard->repeat_delay); | 1010 | keyboard->repeat_rate, keyboard->repeat_delay); |
989 | 1011 | ||
990 | if (!wlr_device->keyboard->group) { | 1012 | if (!keyboard->wlr->group) { |
991 | sway_keyboard_group_add(keyboard); | 1013 | sway_keyboard_group_add(keyboard); |
992 | } | 1014 | } |
993 | 1015 | ||
@@ -1007,40 +1029,42 @@ void sway_keyboard_configure(struct sway_keyboard *keyboard) { | |||
1007 | } | 1029 | } |
1008 | } | 1030 | } |
1009 | if (locked_mods) { | 1031 | if (locked_mods) { |
1010 | wlr_keyboard_notify_modifiers(wlr_device->keyboard, 0, 0, | 1032 | wlr_keyboard_notify_modifiers(keyboard->wlr, 0, 0, |
1011 | locked_mods, 0); | 1033 | locked_mods, 0); |
1012 | uint32_t leds = 0; | 1034 | uint32_t leds = 0; |
1013 | for (uint32_t i = 0; i < WLR_LED_COUNT; ++i) { | 1035 | for (uint32_t i = 0; i < WLR_LED_COUNT; ++i) { |
1014 | if (xkb_state_led_index_is_active( | 1036 | if (xkb_state_led_index_is_active(keyboard->wlr->xkb_state, |
1015 | wlr_device->keyboard->xkb_state, | 1037 | keyboard->wlr->led_indexes[i])) { |
1016 | wlr_device->keyboard->led_indexes[i])) { | ||
1017 | leds |= (1 << i); | 1038 | leds |= (1 << i); |
1018 | } | 1039 | } |
1019 | } | 1040 | } |
1020 | if (wlr_device->keyboard->group) { | 1041 | if (keyboard->wlr->group) { |
1021 | wlr_keyboard_led_update( | 1042 | wlr_keyboard_led_update(&keyboard->wlr->group->keyboard, leds); |
1022 | &wlr_device->keyboard->group->keyboard, leds); | ||
1023 | } else { | 1043 | } else { |
1024 | wlr_keyboard_led_update(wlr_device->keyboard, leds); | 1044 | wlr_keyboard_led_update(keyboard->wlr, leds); |
1025 | } | 1045 | } |
1026 | } | 1046 | } |
1027 | } else { | 1047 | } else { |
1028 | xkb_keymap_unref(keymap); | 1048 | xkb_keymap_unref(keymap); |
1029 | sway_keyboard_group_remove_invalid(keyboard); | 1049 | sway_keyboard_group_remove_invalid(keyboard); |
1030 | if (!wlr_device->keyboard->group) { | 1050 | if (!keyboard->wlr->group) { |
1031 | sway_keyboard_group_add(keyboard); | 1051 | sway_keyboard_group_add(keyboard); |
1032 | } | 1052 | } |
1033 | } | 1053 | } |
1034 | 1054 | ||
1055 | // If the seat has no active keyboard, set this one | ||
1035 | struct wlr_seat *seat = keyboard->seat_device->sway_seat->wlr_seat; | 1056 | struct wlr_seat *seat = keyboard->seat_device->sway_seat->wlr_seat; |
1036 | wlr_seat_set_keyboard(seat, wlr_device); | 1057 | struct wlr_keyboard *current_keyboard = seat->keyboard_state.keyboard; |
1058 | if (current_keyboard == NULL) { | ||
1059 | wlr_seat_set_keyboard(seat, keyboard->wlr); | ||
1060 | } | ||
1037 | 1061 | ||
1038 | wl_list_remove(&keyboard->keyboard_key.link); | 1062 | wl_list_remove(&keyboard->keyboard_key.link); |
1039 | wl_signal_add(&wlr_device->keyboard->events.key, &keyboard->keyboard_key); | 1063 | wl_signal_add(&keyboard->wlr->events.key, &keyboard->keyboard_key); |
1040 | keyboard->keyboard_key.notify = handle_keyboard_key; | 1064 | keyboard->keyboard_key.notify = handle_keyboard_key; |
1041 | 1065 | ||
1042 | wl_list_remove(&keyboard->keyboard_modifiers.link); | 1066 | wl_list_remove(&keyboard->keyboard_modifiers.link); |
1043 | wl_signal_add(&wlr_device->keyboard->events.modifiers, | 1067 | wl_signal_add(&keyboard->wlr->events.modifiers, |
1044 | &keyboard->keyboard_modifiers); | 1068 | &keyboard->keyboard_modifiers); |
1045 | keyboard->keyboard_modifiers.notify = handle_keyboard_modifiers; | 1069 | keyboard->keyboard_modifiers.notify = handle_keyboard_modifiers; |
1046 | 1070 | ||
@@ -1057,12 +1081,11 @@ void sway_keyboard_destroy(struct sway_keyboard *keyboard) { | |||
1057 | if (!keyboard) { | 1081 | if (!keyboard) { |
1058 | return; | 1082 | return; |
1059 | } | 1083 | } |
1060 | if (keyboard->seat_device->input_device->wlr_device->keyboard->group) { | 1084 | if (keyboard->wlr->group) { |
1061 | sway_keyboard_group_remove(keyboard); | 1085 | sway_keyboard_group_remove(keyboard); |
1062 | } | 1086 | } |
1063 | struct wlr_seat *wlr_seat = keyboard->seat_device->sway_seat->wlr_seat; | 1087 | struct wlr_seat *wlr_seat = keyboard->seat_device->sway_seat->wlr_seat; |
1064 | struct sway_input_device *device = keyboard->seat_device->input_device; | 1088 | 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); | 1089 | wlr_seat_set_keyboard(wlr_seat, NULL); |
1067 | } | 1090 | } |
1068 | if (keyboard->keymap) { | 1091 | if (keyboard->keymap) { |
diff --git a/sway/input/libinput.c b/sway/input/libinput.c index 54520f9e..0266c7a9 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" |
@@ -78,6 +79,16 @@ static bool set_accel_speed(struct libinput_device *device, double speed) { | |||
78 | return true; | 79 | return true; |
79 | } | 80 | } |
80 | 81 | ||
82 | static bool set_rotation_angle(struct libinput_device *device, double angle) { | ||
83 | if (!libinput_device_config_rotation_is_available(device) || | ||
84 | libinput_device_config_rotation_get_angle(device) == angle) { | ||
85 | return false; | ||
86 | } | ||
87 | sway_log(SWAY_DEBUG, "rotation_set_angle(%f)", angle); | ||
88 | log_status(libinput_device_config_rotation_set_angle(device, angle)); | ||
89 | return true; | ||
90 | } | ||
91 | |||
81 | static bool set_accel_profile(struct libinput_device *device, | 92 | static bool set_accel_profile(struct libinput_device *device, |
82 | enum libinput_config_accel_profile profile) { | 93 | enum libinput_config_accel_profile profile) { |
83 | if (!libinput_device_config_accel_is_available(device) || | 94 | if (!libinput_device_config_accel_is_available(device) || |
@@ -155,6 +166,18 @@ static bool set_scroll_button(struct libinput_device *dev, uint32_t button) { | |||
155 | return true; | 166 | return true; |
156 | } | 167 | } |
157 | 168 | ||
169 | static bool set_scroll_button_lock(struct libinput_device *dev, | ||
170 | enum libinput_config_scroll_button_lock_state lock) { | ||
171 | uint32_t scroll = libinput_device_config_scroll_get_methods(dev); | ||
172 | if ((scroll & ~LIBINPUT_CONFIG_SCROLL_NO_SCROLL) == 0 || | ||
173 | libinput_device_config_scroll_get_button_lock(dev) == lock) { | ||
174 | return false; | ||
175 | } | ||
176 | sway_log(SWAY_DEBUG, "scroll_set_button_lock(%" PRIu32 ")", lock); | ||
177 | log_status(libinput_device_config_scroll_set_button_lock(dev, lock)); | ||
178 | return true; | ||
179 | } | ||
180 | |||
158 | static bool set_dwt(struct libinput_device *device, bool dwt) { | 181 | static bool set_dwt(struct libinput_device *device, bool dwt) { |
159 | if (!libinput_device_config_dwt_is_available(device) || | 182 | if (!libinput_device_config_dwt_is_available(device) || |
160 | libinput_device_config_dwt_get_enabled(device) == dwt) { | 183 | libinput_device_config_dwt_get_enabled(device) == dwt) { |
@@ -165,6 +188,16 @@ static bool set_dwt(struct libinput_device *device, bool dwt) { | |||
165 | return true; | 188 | return true; |
166 | } | 189 | } |
167 | 190 | ||
191 | static bool set_dwtp(struct libinput_device *device, bool dwtp) { | ||
192 | if (!libinput_device_config_dwtp_is_available(device) || | ||
193 | libinput_device_config_dwtp_get_enabled(device) == dwtp) { | ||
194 | return false; | ||
195 | } | ||
196 | sway_log(SWAY_DEBUG, "dwtp_set_enabled(%d)", dwtp); | ||
197 | log_status(libinput_device_config_dwtp_set_enabled(device, dwtp)); | ||
198 | return true; | ||
199 | } | ||
200 | |||
168 | static bool set_calibration_matrix(struct libinput_device *dev, float mat[6]) { | 201 | static bool set_calibration_matrix(struct libinput_device *dev, float mat[6]) { |
169 | if (!libinput_device_config_calibration_has_matrix(dev)) { | 202 | if (!libinput_device_config_calibration_has_matrix(dev)) { |
170 | return false; | 203 | return false; |
@@ -186,35 +219,38 @@ static bool set_calibration_matrix(struct libinput_device *dev, float mat[6]) { | |||
186 | return changed; | 219 | return changed; |
187 | } | 220 | } |
188 | 221 | ||
189 | void sway_input_configure_libinput_device(struct sway_input_device *input_device) { | 222 | static bool configure_send_events(struct libinput_device *device, |
190 | struct input_config *ic = input_device_get_config(input_device); | 223 | struct input_config *ic) { |
191 | if (!ic || !wlr_input_device_is_libinput(input_device->wlr_device)) { | ||
192 | return; | ||
193 | } | ||
194 | |||
195 | struct libinput_device *device = | ||
196 | wlr_libinput_get_device_handle(input_device->wlr_device); | ||
197 | sway_log(SWAY_DEBUG, "sway_input_configure_libinput_device('%s' on '%s')", | ||
198 | ic->identifier, input_device->identifier); | ||
199 | |||
200 | bool changed = false; | ||
201 | if (ic->mapped_to_output && | 224 | if (ic->mapped_to_output && |
225 | strcmp("*", ic->mapped_to_output) != 0 && | ||
202 | !output_by_name_or_id(ic->mapped_to_output)) { | 226 | !output_by_name_or_id(ic->mapped_to_output)) { |
203 | sway_log(SWAY_DEBUG, | 227 | sway_log(SWAY_DEBUG, |
204 | "%s '%s' is mapped to offline output '%s'; disabling input", | 228 | "%s '%s' is mapped to offline output '%s'; disabling input", |
205 | ic->input_type, ic->identifier, ic->mapped_to_output); | 229 | ic->input_type, ic->identifier, ic->mapped_to_output); |
206 | changed |= set_send_events(device, | 230 | return set_send_events(device, LIBINPUT_CONFIG_SEND_EVENTS_DISABLED); |
207 | LIBINPUT_CONFIG_SEND_EVENTS_DISABLED); | ||
208 | } else if (ic->send_events != INT_MIN) { | 231 | } else if (ic->send_events != INT_MIN) { |
209 | changed |= set_send_events(device, ic->send_events); | 232 | return set_send_events(device, ic->send_events); |
210 | } else { | 233 | } else { |
211 | // Have to reset to the default mode here, otherwise if ic->send_events | 234 | // Have to reset to the default mode here, otherwise if ic->send_events |
212 | // is unset and a mapped output just came online after being disabled, | 235 | // is unset and a mapped output just came online after being disabled, |
213 | // we'd remain stuck sending no events. | 236 | // we'd remain stuck sending no events. |
214 | changed |= set_send_events(device, | 237 | return set_send_events(device, |
215 | libinput_device_config_send_events_get_default_mode(device)); | 238 | libinput_device_config_send_events_get_default_mode(device)); |
216 | } | 239 | } |
240 | } | ||
241 | |||
242 | bool sway_input_configure_libinput_device(struct sway_input_device *input_device) { | ||
243 | struct input_config *ic = input_device_get_config(input_device); | ||
244 | if (!ic || !wlr_input_device_is_libinput(input_device->wlr_device)) { | ||
245 | return false; | ||
246 | } | ||
217 | 247 | ||
248 | struct libinput_device *device = | ||
249 | wlr_libinput_get_device_handle(input_device->wlr_device); | ||
250 | sway_log(SWAY_DEBUG, "sway_input_configure_libinput_device('%s' on '%s')", | ||
251 | ic->identifier, input_device->identifier); | ||
252 | |||
253 | bool changed = configure_send_events(device, ic); | ||
218 | if (ic->tap != INT_MIN) { | 254 | if (ic->tap != INT_MIN) { |
219 | changed |= set_tap(device, ic->tap); | 255 | changed |= set_tap(device, ic->tap); |
220 | } | 256 | } |
@@ -230,6 +266,9 @@ void sway_input_configure_libinput_device(struct sway_input_device *input_device | |||
230 | if (ic->pointer_accel != FLT_MIN) { | 266 | if (ic->pointer_accel != FLT_MIN) { |
231 | changed |= set_accel_speed(device, ic->pointer_accel); | 267 | changed |= set_accel_speed(device, ic->pointer_accel); |
232 | } | 268 | } |
269 | if (ic->rotation_angle != FLT_MIN) { | ||
270 | changed |= set_rotation_angle(device, ic->rotation_angle); | ||
271 | } | ||
233 | if (ic->accel_profile != INT_MIN) { | 272 | if (ic->accel_profile != INT_MIN) { |
234 | changed |= set_accel_profile(device, ic->accel_profile); | 273 | changed |= set_accel_profile(device, ic->accel_profile); |
235 | } | 274 | } |
@@ -251,13 +290,33 @@ void sway_input_configure_libinput_device(struct sway_input_device *input_device | |||
251 | if (ic->scroll_button != INT_MIN) { | 290 | if (ic->scroll_button != INT_MIN) { |
252 | changed |= set_scroll_button(device, ic->scroll_button); | 291 | changed |= set_scroll_button(device, ic->scroll_button); |
253 | } | 292 | } |
293 | if (ic->scroll_button_lock != INT_MIN) { | ||
294 | changed |= set_scroll_button_lock(device, ic->scroll_button_lock); | ||
295 | } | ||
254 | if (ic->dwt != INT_MIN) { | 296 | if (ic->dwt != INT_MIN) { |
255 | changed |= set_dwt(device, ic->dwt); | 297 | changed |= set_dwt(device, ic->dwt); |
256 | } | 298 | } |
299 | if (ic->dwtp != INT_MIN) { | ||
300 | changed |= set_dwtp(device, ic->dwtp); | ||
301 | } | ||
257 | if (ic->calibration_matrix.configured) { | 302 | if (ic->calibration_matrix.configured) { |
258 | changed |= set_calibration_matrix(device, ic->calibration_matrix.matrix); | 303 | changed |= set_calibration_matrix(device, ic->calibration_matrix.matrix); |
259 | } | 304 | } |
260 | 305 | ||
306 | return changed; | ||
307 | } | ||
308 | |||
309 | void sway_input_configure_libinput_device_send_events( | ||
310 | struct sway_input_device *input_device) { | ||
311 | struct input_config *ic = input_device_get_config(input_device); | ||
312 | if (!ic || !wlr_input_device_is_libinput(input_device->wlr_device)) { | ||
313 | return; | ||
314 | } | ||
315 | |||
316 | struct libinput_device *device = | ||
317 | wlr_libinput_get_device_handle(input_device->wlr_device); | ||
318 | bool changed = configure_send_events(device, ic); | ||
319 | |||
261 | if (changed) { | 320 | if (changed) { |
262 | ipc_event_input("libinput_config", input_device); | 321 | ipc_event_input("libinput_config", input_device); |
263 | } | 322 | } |
@@ -286,6 +345,8 @@ void sway_input_reset_libinput_device(struct sway_input_device *input_device) { | |||
286 | libinput_device_config_tap_get_default_drag_lock_enabled(device)); | 345 | libinput_device_config_tap_get_default_drag_lock_enabled(device)); |
287 | changed |= set_accel_speed(device, | 346 | changed |= set_accel_speed(device, |
288 | libinput_device_config_accel_get_default_speed(device)); | 347 | libinput_device_config_accel_get_default_speed(device)); |
348 | changed |= set_rotation_angle(device, | ||
349 | libinput_device_config_rotation_get_default_angle(device)); | ||
289 | changed |= set_accel_profile(device, | 350 | changed |= set_accel_profile(device, |
290 | libinput_device_config_accel_get_default_profile(device)); | 351 | libinput_device_config_accel_get_default_profile(device)); |
291 | changed |= set_natural_scroll(device, | 352 | changed |= set_natural_scroll(device, |
@@ -303,6 +364,8 @@ void sway_input_reset_libinput_device(struct sway_input_device *input_device) { | |||
303 | libinput_device_config_scroll_get_default_button(device)); | 364 | libinput_device_config_scroll_get_default_button(device)); |
304 | changed |= set_dwt(device, | 365 | changed |= set_dwt(device, |
305 | libinput_device_config_dwt_get_default_enabled(device)); | 366 | libinput_device_config_dwt_get_default_enabled(device)); |
367 | changed |= set_dwtp(device, | ||
368 | libinput_device_config_dwtp_get_default_enabled(device)); | ||
306 | 369 | ||
307 | float matrix[6]; | 370 | float matrix[6]; |
308 | libinput_device_config_calibration_get_default_matrix(device, matrix); | 371 | libinput_device_config_calibration_get_default_matrix(device, matrix); |
@@ -312,3 +375,32 @@ void sway_input_reset_libinput_device(struct sway_input_device *input_device) { | |||
312 | ipc_event_input("libinput_config", input_device); | 375 | ipc_event_input("libinput_config", input_device); |
313 | } | 376 | } |
314 | } | 377 | } |
378 | |||
379 | bool sway_libinput_device_is_builtin(struct sway_input_device *sway_device) { | ||
380 | if (!wlr_input_device_is_libinput(sway_device->wlr_device)) { | ||
381 | return false; | ||
382 | } | ||
383 | |||
384 | struct libinput_device *device = | ||
385 | wlr_libinput_get_device_handle(sway_device->wlr_device); | ||
386 | struct udev_device *udev_device = | ||
387 | libinput_device_get_udev_device(device); | ||
388 | if (!udev_device) { | ||
389 | return false; | ||
390 | } | ||
391 | |||
392 | const char *id_path = udev_device_get_property_value(udev_device, "ID_PATH"); | ||
393 | if (!id_path) { | ||
394 | return false; | ||
395 | } | ||
396 | |||
397 | const char prefix_platform[] = "platform-"; | ||
398 | if (strncmp(id_path, prefix_platform, strlen(prefix_platform)) != 0) { | ||
399 | return false; | ||
400 | } | ||
401 | |||
402 | const char prefix_pci[] = "pci-"; | ||
403 | const char infix_platform[] = "-platform-"; | ||
404 | return (strncmp(id_path, prefix_pci, strlen(prefix_pci)) == 0) && | ||
405 | strstr(id_path, infix_platform); | ||
406 | } | ||
diff --git a/sway/input/seat.c b/sway/input/seat.c index 1f5865ee..da4bb12a 100644 --- a/sway/input/seat.c +++ b/sway/input/seat.c | |||
@@ -1,25 +1,27 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <assert.h> | 1 | #include <assert.h> |
3 | #include <linux/input-event-codes.h> | 2 | #include <linux/input-event-codes.h> |
4 | #include <string.h> | 3 | #include <string.h> |
5 | #include <strings.h> | 4 | #include <strings.h> |
6 | #include <time.h> | 5 | #include <time.h> |
6 | #include <wlr/config.h> | ||
7 | #include <wlr/types/wlr_cursor.h> | 7 | #include <wlr/types/wlr_cursor.h> |
8 | #include <wlr/types/wlr_data_device.h> | 8 | #include <wlr/types/wlr_data_device.h> |
9 | #include <wlr/types/wlr_idle.h> | 9 | #include <wlr/types/wlr_idle_notify_v1.h> |
10 | #include <wlr/types/wlr_keyboard_group.h> | 10 | #include <wlr/types/wlr_keyboard_group.h> |
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" |
17 | #include "log.h" | 18 | #include "log.h" |
18 | #include "sway/config.h" | 19 | #include "sway/config.h" |
19 | #include "sway/desktop.h" | 20 | #include "sway/scene_descriptor.h" |
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,10 +53,26 @@ 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 | ||
56 | void seat_destroy(struct sway_seat *seat) { | 69 | void seat_destroy(struct sway_seat *seat) { |
70 | wlr_seat_destroy(seat->wlr_seat); | ||
71 | } | ||
72 | |||
73 | static void handle_seat_destroy(struct wl_listener *listener, void *data) { | ||
74 | struct sway_seat *seat = wl_container_of(listener, seat, destroy); | ||
75 | |||
57 | if (seat == config->handler_context.seat) { | 76 | if (seat == config->handler_context.seat) { |
58 | config->handler_context.seat = input_manager_get_default_seat(); | 77 | config->handler_context.seat = input_manager_get_default_seat(); |
59 | } | 78 | } |
@@ -74,10 +93,11 @@ void seat_destroy(struct sway_seat *seat) { | |||
74 | wl_list_remove(&seat->request_set_selection.link); | 93 | wl_list_remove(&seat->request_set_selection.link); |
75 | wl_list_remove(&seat->request_set_primary_selection.link); | 94 | wl_list_remove(&seat->request_set_primary_selection.link); |
76 | wl_list_remove(&seat->link); | 95 | wl_list_remove(&seat->link); |
77 | wlr_seat_destroy(seat->wlr_seat); | 96 | wl_list_remove(&seat->destroy.link); |
78 | for (int i = 0; i < seat->deferred_bindings->length; i++) { | 97 | for (int i = 0; i < seat->deferred_bindings->length; i++) { |
79 | free_sway_binding(seat->deferred_bindings->items[i]); | 98 | free_sway_binding(seat->deferred_bindings->items[i]); |
80 | } | 99 | } |
100 | wlr_scene_node_destroy(&seat->scene_tree->node); | ||
81 | list_free(seat->deferred_bindings); | 101 | list_free(seat->deferred_bindings); |
82 | free(seat->prev_workspace_name); | 102 | free(seat->prev_workspace_name); |
83 | free(seat); | 103 | free(seat); |
@@ -85,21 +105,10 @@ void seat_destroy(struct sway_seat *seat) { | |||
85 | 105 | ||
86 | void seat_idle_notify_activity(struct sway_seat *seat, | 106 | void seat_idle_notify_activity(struct sway_seat *seat, |
87 | enum sway_input_idle_source source) { | 107 | enum sway_input_idle_source source) { |
88 | uint32_t mask = seat->idle_inhibit_sources; | 108 | if ((source & seat->idle_inhibit_sources) == 0) { |
89 | struct wlr_idle_timeout *timeout; | 109 | return; |
90 | int ntimers = 0, nidle = 0; | ||
91 | wl_list_for_each(timeout, &server.idle->idle_timers, link) { | ||
92 | ++ntimers; | ||
93 | if (timeout->idle_state) { | ||
94 | ++nidle; | ||
95 | } | ||
96 | } | ||
97 | if (nidle == ntimers) { | ||
98 | mask = seat->idle_wake_sources; | ||
99 | } | ||
100 | if ((source & mask) > 0) { | ||
101 | wlr_idle_notify_activity(server.idle, seat->wlr_seat); | ||
102 | } | 110 | } |
111 | wlr_idle_notifier_v1_notify_activity(server.idle_notifier_v1, seat->wlr_seat); | ||
103 | } | 112 | } |
104 | 113 | ||
105 | /** | 114 | /** |
@@ -129,7 +138,7 @@ static struct sway_keyboard *sway_keyboard_for_wlr_keyboard( | |||
129 | if (input_device->wlr_device->type != WLR_INPUT_DEVICE_KEYBOARD) { | 138 | if (input_device->wlr_device->type != WLR_INPUT_DEVICE_KEYBOARD) { |
130 | continue; | 139 | continue; |
131 | } | 140 | } |
132 | if (input_device->wlr_device->keyboard == wlr_keyboard) { | 141 | if (input_device->wlr_device == &wlr_keyboard->base) { |
133 | return seat_device->keyboard; | 142 | return seat_device->keyboard; |
134 | } | 143 | } |
135 | } | 144 | } |
@@ -137,7 +146,7 @@ static struct sway_keyboard *sway_keyboard_for_wlr_keyboard( | |||
137 | wl_list_for_each(group, &seat->keyboard_groups, link) { | 146 | wl_list_for_each(group, &seat->keyboard_groups, link) { |
138 | struct sway_input_device *input_device = | 147 | struct sway_input_device *input_device = |
139 | group->seat_device->input_device; | 148 | group->seat_device->input_device; |
140 | if (input_device->wlr_device->keyboard == wlr_keyboard) { | 149 | if (input_device->wlr_device == &wlr_keyboard->base) { |
141 | return group->seat_device->keyboard; | 150 | return group->seat_device->keyboard; |
142 | } | 151 | } |
143 | } | 152 | } |
@@ -161,11 +170,11 @@ static void seat_keyboard_notify_enter(struct sway_seat *seat, | |||
161 | state->pressed_keycodes, state->npressed, &keyboard->modifiers); | 170 | state->pressed_keycodes, state->npressed, &keyboard->modifiers); |
162 | } | 171 | } |
163 | 172 | ||
164 | static void seat_tablet_pads_notify_enter(struct sway_seat *seat, | 173 | static void seat_tablet_pads_set_focus(struct sway_seat *seat, |
165 | struct wlr_surface *surface) { | 174 | struct wlr_surface *surface) { |
166 | struct sway_seat_device *seat_device; | 175 | struct sway_seat_device *seat_device; |
167 | wl_list_for_each(seat_device, &seat->devices, link) { | 176 | wl_list_for_each(seat_device, &seat->devices, link) { |
168 | sway_tablet_pad_notify_enter(seat_device->tablet_pad, surface); | 177 | sway_tablet_pad_set_focus(seat_device->tablet_pad, surface); |
169 | } | 178 | } |
170 | } | 179 | } |
171 | 180 | ||
@@ -181,7 +190,7 @@ static void seat_send_focus(struct sway_node *node, struct sway_seat *seat) { | |||
181 | node->sway_container->view : NULL; | 190 | node->sway_container->view : NULL; |
182 | 191 | ||
183 | if (view && seat_is_input_allowed(seat, view->surface)) { | 192 | if (view && seat_is_input_allowed(seat, view->surface)) { |
184 | #if HAVE_XWAYLAND | 193 | #if WLR_HAS_XWAYLAND |
185 | if (view->type == SWAY_VIEW_XWAYLAND) { | 194 | if (view->type == SWAY_VIEW_XWAYLAND) { |
186 | struct wlr_xwayland *xwayland = server.xwayland.wlr_xwayland; | 195 | struct wlr_xwayland *xwayland = server.xwayland.wlr_xwayland; |
187 | wlr_xwayland_set_seat(xwayland, seat->wlr_seat); | 196 | wlr_xwayland_set_seat(xwayland, seat->wlr_seat); |
@@ -189,7 +198,7 @@ static void seat_send_focus(struct sway_node *node, struct sway_seat *seat) { | |||
189 | #endif | 198 | #endif |
190 | 199 | ||
191 | seat_keyboard_notify_enter(seat, view->surface); | 200 | seat_keyboard_notify_enter(seat, view->surface); |
192 | seat_tablet_pads_notify_enter(seat, view->surface); | 201 | seat_tablet_pads_set_focus(seat, view->surface); |
193 | sway_input_method_relay_set_focus(&seat->im_relay, view->surface); | 202 | sway_input_method_relay_set_focus(&seat->im_relay, view->surface); |
194 | 203 | ||
195 | struct wlr_pointer_constraint_v1 *constraint = | 204 | struct wlr_pointer_constraint_v1 *constraint = |
@@ -209,14 +218,13 @@ void seat_for_each_node(struct sway_seat *seat, | |||
209 | 218 | ||
210 | struct sway_container *seat_get_focus_inactive_view(struct sway_seat *seat, | 219 | struct sway_container *seat_get_focus_inactive_view(struct sway_seat *seat, |
211 | struct sway_node *ancestor) { | 220 | struct sway_node *ancestor) { |
212 | if (ancestor->type == N_CONTAINER && ancestor->sway_container->view) { | 221 | if (node_is_view(ancestor)) { |
213 | return ancestor->sway_container; | 222 | return ancestor->sway_container; |
214 | } | 223 | } |
215 | struct sway_seat_node *current; | 224 | struct sway_seat_node *current; |
216 | wl_list_for_each(current, &seat->focus_stack, link) { | 225 | wl_list_for_each(current, &seat->focus_stack, link) { |
217 | struct sway_node *node = current->node; | 226 | struct sway_node *node = current->node; |
218 | if (node->type == N_CONTAINER && node->sway_container->view && | 227 | if (node_is_view(node) && node_has_ancestor(node, ancestor)) { |
219 | node_has_ancestor(node, ancestor)) { | ||
220 | return node->sway_container; | 228 | return node->sway_container; |
221 | } | 229 | } |
222 | } | 230 | } |
@@ -235,7 +243,7 @@ static void handle_seat_node_destroy(struct wl_listener *listener, void *data) { | |||
235 | seat_node_destroy(seat_node); | 243 | seat_node_destroy(seat_node); |
236 | // If an unmanaged or layer surface is focused when an output gets | 244 | // 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 | 245 | // 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 | 246 | // seat, the seat needs to refocus its focus inactive to update the |
239 | // value of seat->workspace. | 247 | // value of seat->workspace. |
240 | if (seat->workspace == node->sway_workspace) { | 248 | if (seat->workspace == node->sway_workspace) { |
241 | struct sway_node *node = seat_get_focus_inactive(seat, &root->node); | 249 | struct sway_node *node = seat_get_focus_inactive(seat, &root->node); |
@@ -309,8 +317,8 @@ static void handle_seat_node_destroy(struct wl_listener *listener, void *data) { | |||
309 | // Setting focus_inactive | 317 | // Setting focus_inactive |
310 | focus = seat_get_focus_inactive(seat, &root->node); | 318 | focus = seat_get_focus_inactive(seat, &root->node); |
311 | seat_set_raw_focus(seat, next_focus); | 319 | seat_set_raw_focus(seat, next_focus); |
312 | if (focus->type == N_CONTAINER && focus->sway_container->workspace) { | 320 | if (focus->type == N_CONTAINER && focus->sway_container->pending.workspace) { |
313 | seat_set_raw_focus(seat, &focus->sway_container->workspace->node); | 321 | seat_set_raw_focus(seat, &focus->sway_container->pending.workspace->node); |
314 | } | 322 | } |
315 | seat_set_raw_focus(seat, focus); | 323 | seat_set_raw_focus(seat, focus); |
316 | } | 324 | } |
@@ -351,25 +359,15 @@ static void handle_new_node(struct wl_listener *listener, void *data) { | |||
351 | seat_node_from_node(seat, node); | 359 | seat_node_from_node(seat, node); |
352 | } | 360 | } |
353 | 361 | ||
354 | static void drag_icon_damage_whole(struct sway_drag_icon *icon) { | 362 | static void drag_icon_update_position(struct sway_seat *seat, struct wlr_scene_node *node) { |
355 | if (!icon->wlr_drag_icon->mapped) { | 363 | struct wlr_drag_icon *wlr_icon = scene_descriptor_try_get(node, SWAY_SCENE_DESC_DRAG_ICON); |
356 | return; | ||
357 | } | ||
358 | desktop_damage_surface(icon->wlr_drag_icon->surface, icon->x, icon->y, true); | ||
359 | } | ||
360 | |||
361 | void drag_icon_update_position(struct sway_drag_icon *icon) { | ||
362 | drag_icon_damage_whole(icon); | ||
363 | |||
364 | struct wlr_drag_icon *wlr_icon = icon->wlr_drag_icon; | ||
365 | struct sway_seat *seat = icon->seat; | ||
366 | struct wlr_cursor *cursor = seat->cursor->cursor; | 364 | struct wlr_cursor *cursor = seat->cursor->cursor; |
365 | |||
367 | switch (wlr_icon->drag->grab_type) { | 366 | switch (wlr_icon->drag->grab_type) { |
368 | case WLR_DRAG_GRAB_KEYBOARD: | 367 | case WLR_DRAG_GRAB_KEYBOARD: |
369 | return; | 368 | return; |
370 | case WLR_DRAG_GRAB_KEYBOARD_POINTER: | 369 | case WLR_DRAG_GRAB_KEYBOARD_POINTER: |
371 | icon->x = cursor->x; | 370 | wlr_scene_node_set_position(node, cursor->x, cursor->y); |
372 | icon->y = cursor->y; | ||
373 | break; | 371 | break; |
374 | case WLR_DRAG_GRAB_KEYBOARD_TOUCH:; | 372 | case WLR_DRAG_GRAB_KEYBOARD_TOUCH:; |
375 | struct wlr_touch_point *point = | 373 | struct wlr_touch_point *point = |
@@ -377,39 +375,15 @@ void drag_icon_update_position(struct sway_drag_icon *icon) { | |||
377 | if (point == NULL) { | 375 | if (point == NULL) { |
378 | return; | 376 | return; |
379 | } | 377 | } |
380 | icon->x = seat->touch_x; | 378 | wlr_scene_node_set_position(node, seat->touch_x, seat->touch_y); |
381 | icon->y = seat->touch_y; | ||
382 | } | 379 | } |
383 | |||
384 | drag_icon_damage_whole(icon); | ||
385 | } | 380 | } |
386 | 381 | ||
387 | static void drag_icon_handle_surface_commit(struct wl_listener *listener, | 382 | void drag_icons_update_position(struct sway_seat *seat) { |
388 | void *data) { | 383 | struct wlr_scene_node *node; |
389 | struct sway_drag_icon *icon = | 384 | wl_list_for_each(node, &seat->drag_icons->children, link) { |
390 | wl_container_of(listener, icon, surface_commit); | 385 | drag_icon_update_position(seat, node); |
391 | drag_icon_update_position(icon); | 386 | } |
392 | } | ||
393 | |||
394 | static void drag_icon_handle_map(struct wl_listener *listener, void *data) { | ||
395 | struct sway_drag_icon *icon = wl_container_of(listener, icon, map); | ||
396 | drag_icon_damage_whole(icon); | ||
397 | } | ||
398 | |||
399 | static void drag_icon_handle_unmap(struct wl_listener *listener, void *data) { | ||
400 | struct sway_drag_icon *icon = wl_container_of(listener, icon, unmap); | ||
401 | drag_icon_damage_whole(icon); | ||
402 | } | ||
403 | |||
404 | static void drag_icon_handle_destroy(struct wl_listener *listener, void *data) { | ||
405 | struct sway_drag_icon *icon = wl_container_of(listener, icon, destroy); | ||
406 | icon->wlr_drag_icon->data = NULL; | ||
407 | wl_list_remove(&icon->link); | ||
408 | wl_list_remove(&icon->surface_commit.link); | ||
409 | wl_list_remove(&icon->unmap.link); | ||
410 | wl_list_remove(&icon->map.link); | ||
411 | wl_list_remove(&icon->destroy.link); | ||
412 | free(icon); | ||
413 | } | 387 | } |
414 | 388 | ||
415 | static void drag_handle_destroy(struct wl_listener *listener, void *data) { | 389 | static void drag_handle_destroy(struct wl_listener *listener, void *data) { |
@@ -481,27 +455,20 @@ static void handle_start_drag(struct wl_listener *listener, void *data) { | |||
481 | 455 | ||
482 | struct wlr_drag_icon *wlr_drag_icon = wlr_drag->icon; | 456 | struct wlr_drag_icon *wlr_drag_icon = wlr_drag->icon; |
483 | if (wlr_drag_icon != NULL) { | 457 | if (wlr_drag_icon != NULL) { |
484 | struct sway_drag_icon *icon = calloc(1, sizeof(struct sway_drag_icon)); | 458 | struct wlr_scene_tree *tree = wlr_scene_drag_icon_create(seat->drag_icons, wlr_drag_icon); |
485 | if (icon == NULL) { | 459 | if (!tree) { |
486 | sway_log(SWAY_ERROR, "Allocation failed"); | 460 | sway_log(SWAY_ERROR, "Failed to allocate a drag icon scene tree"); |
487 | return; | 461 | return; |
488 | } | 462 | } |
489 | icon->seat = seat; | ||
490 | icon->wlr_drag_icon = wlr_drag_icon; | ||
491 | wlr_drag_icon->data = icon; | ||
492 | |||
493 | icon->surface_commit.notify = drag_icon_handle_surface_commit; | ||
494 | wl_signal_add(&wlr_drag_icon->surface->events.commit, &icon->surface_commit); | ||
495 | icon->unmap.notify = drag_icon_handle_unmap; | ||
496 | wl_signal_add(&wlr_drag_icon->events.unmap, &icon->unmap); | ||
497 | icon->map.notify = drag_icon_handle_map; | ||
498 | wl_signal_add(&wlr_drag_icon->events.map, &icon->map); | ||
499 | icon->destroy.notify = drag_icon_handle_destroy; | ||
500 | wl_signal_add(&wlr_drag_icon->events.destroy, &icon->destroy); | ||
501 | 463 | ||
502 | wl_list_insert(&root->drag_icons, &icon->link); | 464 | if (!scene_descriptor_assign(&tree->node, SWAY_SCENE_DESC_DRAG_ICON, |
465 | wlr_drag_icon)) { | ||
466 | sway_log(SWAY_ERROR, "Failed to allocate a drag icon scene descriptor"); | ||
467 | wlr_scene_node_destroy(&tree->node); | ||
468 | return; | ||
469 | } | ||
503 | 470 | ||
504 | drag_icon_update_position(icon); | 471 | drag_icon_update_position(seat, &tree->node); |
505 | } | 472 | } |
506 | seatop_begin_default(seat); | 473 | seatop_begin_default(seat); |
507 | } | 474 | } |
@@ -548,8 +515,18 @@ struct sway_seat *seat_create(const char *seat_name) { | |||
548 | return NULL; | 515 | return NULL; |
549 | } | 516 | } |
550 | 517 | ||
518 | bool failed = false; | ||
519 | seat->scene_tree = alloc_scene_tree(root->layers.seat, &failed); | ||
520 | seat->drag_icons = alloc_scene_tree(seat->scene_tree, &failed); | ||
521 | if (failed) { | ||
522 | wlr_scene_node_destroy(&seat->scene_tree->node); | ||
523 | free(seat); | ||
524 | return NULL; | ||
525 | } | ||
526 | |||
551 | seat->wlr_seat = wlr_seat_create(server.wl_display, seat_name); | 527 | seat->wlr_seat = wlr_seat_create(server.wl_display, seat_name); |
552 | if (!sway_assert(seat->wlr_seat, "could not allocate seat")) { | 528 | if (!sway_assert(seat->wlr_seat, "could not allocate seat")) { |
529 | wlr_scene_node_destroy(&seat->scene_tree->node); | ||
553 | free(seat); | 530 | free(seat); |
554 | return NULL; | 531 | return NULL; |
555 | } | 532 | } |
@@ -557,11 +534,15 @@ struct sway_seat *seat_create(const char *seat_name) { | |||
557 | 534 | ||
558 | seat->cursor = sway_cursor_create(seat); | 535 | seat->cursor = sway_cursor_create(seat); |
559 | if (!seat->cursor) { | 536 | if (!seat->cursor) { |
537 | wlr_scene_node_destroy(&seat->scene_tree->node); | ||
560 | wlr_seat_destroy(seat->wlr_seat); | 538 | wlr_seat_destroy(seat->wlr_seat); |
561 | free(seat); | 539 | free(seat); |
562 | return NULL; | 540 | return NULL; |
563 | } | 541 | } |
564 | 542 | ||
543 | seat->destroy.notify = handle_seat_destroy; | ||
544 | wl_signal_add(&seat->wlr_seat->events.destroy, &seat->destroy); | ||
545 | |||
565 | seat->idle_inhibit_sources = seat->idle_wake_sources = | 546 | seat->idle_inhibit_sources = seat->idle_wake_sources = |
566 | IDLE_SOURCE_KEYBOARD | | 547 | IDLE_SOURCE_KEYBOARD | |
567 | IDLE_SOURCE_POINTER | | 548 | IDLE_SOURCE_POINTER | |
@@ -635,7 +616,7 @@ static void seat_update_capabilities(struct sway_seat *seat) { | |||
635 | case WLR_INPUT_DEVICE_TOUCH: | 616 | case WLR_INPUT_DEVICE_TOUCH: |
636 | caps |= WL_SEAT_CAPABILITY_TOUCH; | 617 | caps |= WL_SEAT_CAPABILITY_TOUCH; |
637 | break; | 618 | break; |
638 | case WLR_INPUT_DEVICE_TABLET_TOOL: | 619 | case WLR_INPUT_DEVICE_TABLET: |
639 | caps |= WL_SEAT_CAPABILITY_POINTER; | 620 | caps |= WL_SEAT_CAPABILITY_POINTER; |
640 | break; | 621 | break; |
641 | case WLR_INPUT_DEVICE_SWITCH: | 622 | case WLR_INPUT_DEVICE_SWITCH: |
@@ -653,7 +634,7 @@ static void seat_update_capabilities(struct sway_seat *seat) { | |||
653 | } else { | 634 | } else { |
654 | wlr_seat_set_capabilities(seat->wlr_seat, caps); | 635 | wlr_seat_set_capabilities(seat->wlr_seat, caps); |
655 | if ((previous_caps & WL_SEAT_CAPABILITY_POINTER) == 0) { | 636 | if ((previous_caps & WL_SEAT_CAPABILITY_POINTER) == 0) { |
656 | cursor_set_image(seat->cursor, "left_ptr", NULL); | 637 | cursor_set_image(seat->cursor, "default", NULL); |
657 | } | 638 | } |
658 | } | 639 | } |
659 | } | 640 | } |
@@ -666,12 +647,55 @@ static void seat_reset_input_config(struct sway_seat *seat, | |||
666 | sway_device->input_device->wlr_device, NULL); | 647 | sway_device->input_device->wlr_device, NULL); |
667 | } | 648 | } |
668 | 649 | ||
669 | static void seat_apply_input_config(struct sway_seat *seat, | 650 | static bool has_prefix(const char *str, const char *prefix) { |
651 | return strncmp(str, prefix, strlen(prefix)) == 0; | ||
652 | } | ||
653 | |||
654 | /** | ||
655 | * Get the name of the built-in output, if any. Returns NULL if there isn't | ||
656 | * exactly one built-in output. | ||
657 | */ | ||
658 | static const char *get_builtin_output_name(void) { | ||
659 | const char *match = NULL; | ||
660 | for (int i = 0; i < root->outputs->length; ++i) { | ||
661 | struct sway_output *output = root->outputs->items[i]; | ||
662 | const char *name = output->wlr_output->name; | ||
663 | if (has_prefix(name, "eDP-") || has_prefix(name, "LVDS-") || | ||
664 | has_prefix(name, "DSI-")) { | ||
665 | if (match != NULL) { | ||
666 | return NULL; | ||
667 | } | ||
668 | match = name; | ||
669 | } | ||
670 | } | ||
671 | return match; | ||
672 | } | ||
673 | |||
674 | static bool is_touch_or_tablet_tool(struct sway_seat_device *seat_device) { | ||
675 | switch (seat_device->input_device->wlr_device->type) { | ||
676 | case WLR_INPUT_DEVICE_TOUCH: | ||
677 | case WLR_INPUT_DEVICE_TABLET: | ||
678 | return true; | ||
679 | default: | ||
680 | return false; | ||
681 | } | ||
682 | } | ||
683 | |||
684 | static void seat_apply_input_mapping(struct sway_seat *seat, | ||
670 | struct sway_seat_device *sway_device) { | 685 | struct sway_seat_device *sway_device) { |
671 | struct input_config *ic = | 686 | struct input_config *ic = |
672 | input_device_get_config(sway_device->input_device); | 687 | input_device_get_config(sway_device->input_device); |
673 | 688 | ||
674 | sway_log(SWAY_DEBUG, "Applying input config to %s", | 689 | switch (sway_device->input_device->wlr_device->type) { |
690 | case WLR_INPUT_DEVICE_POINTER: | ||
691 | case WLR_INPUT_DEVICE_TOUCH: | ||
692 | case WLR_INPUT_DEVICE_TABLET: | ||
693 | break; | ||
694 | default: | ||
695 | return; // these devices don't support mappings | ||
696 | } | ||
697 | |||
698 | sway_log(SWAY_DEBUG, "Applying input mapping to %s", | ||
675 | sway_device->input_device->identifier); | 699 | sway_device->input_device->identifier); |
676 | 700 | ||
677 | const char *mapped_to_output = ic == NULL ? NULL : ic->mapped_to_output; | 701 | const char *mapped_to_output = ic == NULL ? NULL : ic->mapped_to_output; |
@@ -680,8 +704,38 @@ static void seat_apply_input_config(struct sway_seat *seat, | |||
680 | ic == NULL ? MAPPED_TO_DEFAULT : ic->mapped_to; | 704 | ic == NULL ? MAPPED_TO_DEFAULT : ic->mapped_to; |
681 | 705 | ||
682 | switch (mapped_to) { | 706 | switch (mapped_to) { |
683 | case MAPPED_TO_DEFAULT: | 707 | case MAPPED_TO_DEFAULT:; |
684 | mapped_to_output = sway_device->input_device->wlr_device->output_name; | 708 | /* |
709 | * If the wlroots backend provides an output name, use that. | ||
710 | * | ||
711 | * Otherwise, try to map built-in touch and pointer devices to the | ||
712 | * built-in output. | ||
713 | */ | ||
714 | struct wlr_input_device *dev = sway_device->input_device->wlr_device; | ||
715 | switch (dev->type) { | ||
716 | case WLR_INPUT_DEVICE_POINTER: | ||
717 | mapped_to_output = wlr_pointer_from_input_device(dev)->output_name; | ||
718 | break; | ||
719 | case WLR_INPUT_DEVICE_TOUCH: | ||
720 | mapped_to_output = wlr_touch_from_input_device(dev)->output_name; | ||
721 | break; | ||
722 | default: | ||
723 | mapped_to_output = NULL; | ||
724 | break; | ||
725 | } | ||
726 | #if WLR_HAS_LIBINPUT_BACKEND | ||
727 | if (mapped_to_output == NULL && is_touch_or_tablet_tool(sway_device) && | ||
728 | sway_libinput_device_is_builtin(sway_device->input_device)) { | ||
729 | mapped_to_output = get_builtin_output_name(); | ||
730 | if (mapped_to_output) { | ||
731 | sway_log(SWAY_DEBUG, "Auto-detected output '%s' for device '%s'", | ||
732 | mapped_to_output, sway_device->input_device->identifier); | ||
733 | } | ||
734 | } | ||
735 | #else | ||
736 | (void)is_touch_or_tablet_tool; | ||
737 | (void)get_builtin_output_name; | ||
738 | #endif | ||
685 | if (mapped_to_output == NULL) { | 739 | if (mapped_to_output == NULL) { |
686 | return; | 740 | return; |
687 | } | 741 | } |
@@ -725,12 +779,9 @@ static void seat_apply_input_config(struct sway_seat *seat, | |||
725 | 779 | ||
726 | static void seat_configure_pointer(struct sway_seat *seat, | 780 | static void seat_configure_pointer(struct sway_seat *seat, |
727 | struct sway_seat_device *sway_device) { | 781 | struct sway_seat_device *sway_device) { |
728 | if ((seat->wlr_seat->capabilities & WL_SEAT_CAPABILITY_POINTER) == 0) { | 782 | seat_configure_xcursor(seat); |
729 | seat_configure_xcursor(seat); | ||
730 | } | ||
731 | wlr_cursor_attach_input_device(seat->cursor->cursor, | 783 | wlr_cursor_attach_input_device(seat->cursor->cursor, |
732 | sway_device->input_device->wlr_device); | 784 | sway_device->input_device->wlr_device); |
733 | seat_apply_input_config(seat, sway_device); | ||
734 | wl_event_source_timer_update( | 785 | wl_event_source_timer_update( |
735 | seat->cursor->hide_source, cursor_get_timeout(seat->cursor)); | 786 | seat->cursor->hide_source, cursor_get_timeout(seat->cursor)); |
736 | } | 787 | } |
@@ -741,13 +792,22 @@ static void seat_configure_keyboard(struct sway_seat *seat, | |||
741 | sway_keyboard_create(seat, seat_device); | 792 | sway_keyboard_create(seat, seat_device); |
742 | } | 793 | } |
743 | sway_keyboard_configure(seat_device->keyboard); | 794 | sway_keyboard_configure(seat_device->keyboard); |
744 | wlr_seat_set_keyboard(seat->wlr_seat, | 795 | |
745 | seat_device->input_device->wlr_device); | 796 | // We only need to update the current keyboard, as the rest will be updated |
746 | struct sway_node *focus = seat_get_focus(seat); | 797 | // as they are activated. |
747 | if (focus && node_is_view(focus)) { | 798 | struct wlr_keyboard *wlr_keyboard = |
748 | // force notify reenter to pick up the new configuration | 799 | wlr_keyboard_from_input_device(seat_device->input_device->wlr_device); |
800 | struct wlr_keyboard *current_keyboard = seat->wlr_seat->keyboard_state.keyboard; | ||
801 | if (wlr_keyboard != current_keyboard) { | ||
802 | return; | ||
803 | } | ||
804 | |||
805 | // force notify reenter to pick up the new configuration. This reuses | ||
806 | // the current focused surface to avoid breaking input grabs. | ||
807 | struct wlr_surface *surface = seat->wlr_seat->keyboard_state.focused_surface; | ||
808 | if (surface) { | ||
749 | wlr_seat_keyboard_notify_clear_focus(seat->wlr_seat); | 809 | wlr_seat_keyboard_notify_clear_focus(seat->wlr_seat); |
750 | seat_keyboard_notify_enter(seat, focus->sway_container->view->surface); | 810 | seat_keyboard_notify_enter(seat, surface); |
751 | } | 811 | } |
752 | } | 812 | } |
753 | 813 | ||
@@ -756,7 +816,6 @@ static void seat_configure_switch(struct sway_seat *seat, | |||
756 | if (!seat_device->switch_device) { | 816 | if (!seat_device->switch_device) { |
757 | sway_switch_create(seat, seat_device); | 817 | sway_switch_create(seat, seat_device); |
758 | } | 818 | } |
759 | seat_apply_input_config(seat, seat_device); | ||
760 | sway_switch_configure(seat_device->switch_device); | 819 | sway_switch_configure(seat_device->switch_device); |
761 | } | 820 | } |
762 | 821 | ||
@@ -764,7 +823,6 @@ static void seat_configure_touch(struct sway_seat *seat, | |||
764 | struct sway_seat_device *sway_device) { | 823 | struct sway_seat_device *sway_device) { |
765 | wlr_cursor_attach_input_device(seat->cursor->cursor, | 824 | wlr_cursor_attach_input_device(seat->cursor->cursor, |
766 | sway_device->input_device->wlr_device); | 825 | sway_device->input_device->wlr_device); |
767 | seat_apply_input_config(seat, sway_device); | ||
768 | } | 826 | } |
769 | 827 | ||
770 | static void seat_configure_tablet_tool(struct sway_seat *seat, | 828 | static void seat_configure_tablet_tool(struct sway_seat *seat, |
@@ -775,7 +833,6 @@ static void seat_configure_tablet_tool(struct sway_seat *seat, | |||
775 | sway_configure_tablet(sway_device->tablet); | 833 | sway_configure_tablet(sway_device->tablet); |
776 | wlr_cursor_attach_input_device(seat->cursor->cursor, | 834 | wlr_cursor_attach_input_device(seat->cursor->cursor, |
777 | sway_device->input_device->wlr_device); | 835 | sway_device->input_device->wlr_device); |
778 | seat_apply_input_config(seat, sway_device); | ||
779 | } | 836 | } |
780 | 837 | ||
781 | static void seat_configure_tablet_pad(struct sway_seat *seat, | 838 | static void seat_configure_tablet_pad(struct sway_seat *seat, |
@@ -825,13 +882,25 @@ void seat_configure_device(struct sway_seat *seat, | |||
825 | case WLR_INPUT_DEVICE_TOUCH: | 882 | case WLR_INPUT_DEVICE_TOUCH: |
826 | seat_configure_touch(seat, seat_device); | 883 | seat_configure_touch(seat, seat_device); |
827 | break; | 884 | break; |
828 | case WLR_INPUT_DEVICE_TABLET_TOOL: | 885 | case WLR_INPUT_DEVICE_TABLET: |
829 | seat_configure_tablet_tool(seat, seat_device); | 886 | seat_configure_tablet_tool(seat, seat_device); |
830 | break; | 887 | break; |
831 | case WLR_INPUT_DEVICE_TABLET_PAD: | 888 | case WLR_INPUT_DEVICE_TABLET_PAD: |
832 | seat_configure_tablet_pad(seat, seat_device); | 889 | seat_configure_tablet_pad(seat, seat_device); |
833 | break; | 890 | break; |
834 | } | 891 | } |
892 | |||
893 | seat_apply_input_mapping(seat, seat_device); | ||
894 | } | ||
895 | |||
896 | void seat_configure_device_mapping(struct sway_seat *seat, | ||
897 | struct sway_input_device *input_device) { | ||
898 | struct sway_seat_device *seat_device = seat_get_device(seat, input_device); | ||
899 | if (!seat_device) { | ||
900 | return; | ||
901 | } | ||
902 | |||
903 | seat_apply_input_mapping(seat, seat_device); | ||
835 | } | 904 | } |
836 | 905 | ||
837 | void seat_reset_device(struct sway_seat *seat, | 906 | void seat_reset_device(struct sway_seat *seat, |
@@ -852,7 +921,7 @@ void seat_reset_device(struct sway_seat *seat, | |||
852 | case WLR_INPUT_DEVICE_TOUCH: | 921 | case WLR_INPUT_DEVICE_TOUCH: |
853 | seat_reset_input_config(seat, seat_device); | 922 | seat_reset_input_config(seat, seat_device); |
854 | break; | 923 | break; |
855 | case WLR_INPUT_DEVICE_TABLET_TOOL: | 924 | case WLR_INPUT_DEVICE_TABLET: |
856 | seat_reset_input_config(seat, seat_device); | 925 | seat_reset_input_config(seat, seat_device); |
857 | break; | 926 | break; |
858 | case WLR_INPUT_DEVICE_TABLET_PAD: | 927 | case WLR_INPUT_DEVICE_TABLET_PAD: |
@@ -933,7 +1002,7 @@ void seat_configure_xcursor(struct sway_seat *seat) { | |||
933 | setenv("XCURSOR_THEME", cursor_theme, 1); | 1002 | setenv("XCURSOR_THEME", cursor_theme, 1); |
934 | } | 1003 | } |
935 | 1004 | ||
936 | #if HAVE_XWAYLAND | 1005 | #if WLR_HAS_XWAYLAND |
937 | if (server.xwayland.wlr_xwayland && (!server.xwayland.xcursor_manager || | 1006 | if (server.xwayland.wlr_xwayland && (!server.xwayland.xcursor_manager || |
938 | !xcursor_manager_is_named(server.xwayland.xcursor_manager, | 1007 | !xcursor_manager_is_named(server.xwayland.xcursor_manager, |
939 | cursor_theme) || | 1008 | cursor_theme) || |
@@ -948,7 +1017,7 @@ void seat_configure_xcursor(struct sway_seat *seat) { | |||
948 | 1017 | ||
949 | wlr_xcursor_manager_load(server.xwayland.xcursor_manager, 1); | 1018 | wlr_xcursor_manager_load(server.xwayland.xcursor_manager, 1); |
950 | struct wlr_xcursor *xcursor = wlr_xcursor_manager_get_xcursor( | 1019 | struct wlr_xcursor *xcursor = wlr_xcursor_manager_get_xcursor( |
951 | server.xwayland.xcursor_manager, "left_ptr", 1); | 1020 | server.xwayland.xcursor_manager, "default", 1); |
952 | if (xcursor != NULL) { | 1021 | if (xcursor != NULL) { |
953 | struct wlr_xcursor_image *image = xcursor->images[0]; | 1022 | struct wlr_xcursor_image *image = xcursor->images[0]; |
954 | wlr_xwayland_set_cursor( | 1023 | wlr_xwayland_set_cursor( |
@@ -974,32 +1043,35 @@ void seat_configure_xcursor(struct sway_seat *seat) { | |||
974 | sway_log(SWAY_ERROR, | 1043 | sway_log(SWAY_ERROR, |
975 | "Cannot create XCursor manager for theme '%s'", cursor_theme); | 1044 | "Cannot create XCursor manager for theme '%s'", cursor_theme); |
976 | } | 1045 | } |
977 | } | ||
978 | 1046 | ||
979 | for (int i = 0; i < root->outputs->length; ++i) { | 1047 | |
980 | struct sway_output *sway_output = root->outputs->items[i]; | 1048 | for (int i = 0; i < root->outputs->length; ++i) { |
981 | struct wlr_output *output = sway_output->wlr_output; | 1049 | struct sway_output *sway_output = root->outputs->items[i]; |
982 | bool result = | 1050 | struct wlr_output *output = sway_output->wlr_output; |
983 | wlr_xcursor_manager_load(seat->cursor->xcursor_manager, | 1051 | bool result = |
984 | output->scale); | 1052 | wlr_xcursor_manager_load(seat->cursor->xcursor_manager, |
985 | if (!result) { | 1053 | output->scale); |
986 | sway_log(SWAY_ERROR, | 1054 | if (!result) { |
987 | "Cannot load xcursor theme for output '%s' with scale %f", | 1055 | sway_log(SWAY_ERROR, |
988 | output->name, output->scale); | 1056 | "Cannot load xcursor theme for output '%s' with scale %f", |
1057 | output->name, output->scale); | ||
1058 | } | ||
989 | } | 1059 | } |
990 | } | ||
991 | 1060 | ||
992 | // Reset the cursor so that we apply it to outputs that just appeared | 1061 | // Reset the cursor so that we apply it to outputs that just appeared |
993 | cursor_set_image(seat->cursor, NULL, NULL); | 1062 | cursor_set_image(seat->cursor, NULL, NULL); |
994 | cursor_set_image(seat->cursor, "left_ptr", NULL); | 1063 | cursor_set_image(seat->cursor, "default", NULL); |
995 | wlr_cursor_warp(seat->cursor->cursor, NULL, seat->cursor->cursor->x, | 1064 | wlr_cursor_warp(seat->cursor->cursor, NULL, seat->cursor->cursor->x, |
996 | seat->cursor->cursor->y); | 1065 | seat->cursor->cursor->y); |
1066 | } | ||
997 | } | 1067 | } |
998 | 1068 | ||
999 | bool seat_is_input_allowed(struct sway_seat *seat, | 1069 | bool seat_is_input_allowed(struct sway_seat *seat, |
1000 | struct wlr_surface *surface) { | 1070 | struct wlr_surface *surface) { |
1001 | struct wl_client *client = wl_resource_get_client(surface->resource); | 1071 | if (server.session_lock.lock) { |
1002 | return !seat->exclusive_client || seat->exclusive_client == client; | 1072 | return sway_session_lock_has_surface(server.session_lock.lock, surface); |
1073 | } | ||
1074 | return true; | ||
1003 | } | 1075 | } |
1004 | 1076 | ||
1005 | static void send_unfocus(struct sway_container *con, void *data) { | 1077 | static void send_unfocus(struct sway_container *con, void *data) { |
@@ -1058,15 +1130,7 @@ void seat_set_raw_focus(struct sway_seat *seat, struct sway_node *node) { | |||
1058 | } | 1130 | } |
1059 | } | 1131 | } |
1060 | 1132 | ||
1061 | void seat_set_focus(struct sway_seat *seat, struct sway_node *node) { | 1133 | static void seat_set_workspace_focus(struct sway_seat *seat, struct sway_node *node) { |
1062 | if (seat->focused_layer) { | ||
1063 | struct wlr_layer_surface_v1 *layer = seat->focused_layer; | ||
1064 | seat_set_focus_layer(seat, NULL); | ||
1065 | seat_set_focus(seat, node); | ||
1066 | seat_set_focus_layer(seat, layer); | ||
1067 | return; | ||
1068 | } | ||
1069 | |||
1070 | struct sway_node *last_focus = seat_get_focus(seat); | 1134 | struct sway_node *last_focus = seat_get_focus(seat); |
1071 | if (last_focus == node) { | 1135 | if (last_focus == node) { |
1072 | return; | 1136 | return; |
@@ -1086,30 +1150,19 @@ void seat_set_focus(struct sway_seat *seat, struct sway_node *node) { | |||
1086 | } | 1150 | } |
1087 | 1151 | ||
1088 | struct sway_workspace *new_workspace = node->type == N_WORKSPACE ? | 1152 | struct sway_workspace *new_workspace = node->type == N_WORKSPACE ? |
1089 | node->sway_workspace : node->sway_container->workspace; | 1153 | node->sway_workspace : node->sway_container->pending.workspace; |
1090 | struct sway_container *container = node->type == N_CONTAINER ? | 1154 | struct sway_container *container = node->type == N_CONTAINER ? |
1091 | node->sway_container : NULL; | 1155 | node->sway_container : NULL; |
1092 | 1156 | ||
1093 | // Deny setting focus to a view which is hidden by a fullscreen container | 1157 | // Deny setting focus to a view which is hidden by a fullscreen container or global |
1094 | if (new_workspace && new_workspace->fullscreen && container && | 1158 | if (container && container_obstructing_fullscreen_container(container)) { |
1095 | !container_is_fullscreen_or_child(container)) { | 1159 | return; |
1096 | // Unless it's a transient container | ||
1097 | if (!container_is_transient_for(container, new_workspace->fullscreen)) { | ||
1098 | return; | ||
1099 | } | ||
1100 | } | 1160 | } |
1161 | |||
1101 | // Deny setting focus to a workspace node when using fullscreen global | 1162 | // Deny setting focus to a workspace node when using fullscreen global |
1102 | if (root->fullscreen_global && !container && new_workspace) { | 1163 | if (root->fullscreen_global && !container && new_workspace) { |
1103 | return; | 1164 | return; |
1104 | } | 1165 | } |
1105 | // Deny setting focus to a view which is hidden by a fullscreen global | ||
1106 | if (root->fullscreen_global && container != root->fullscreen_global && | ||
1107 | !container_has_ancestor(container, root->fullscreen_global)) { | ||
1108 | // Unless it's a transient container | ||
1109 | if (!container_is_transient_for(container, root->fullscreen_global)) { | ||
1110 | return; | ||
1111 | } | ||
1112 | } | ||
1113 | 1166 | ||
1114 | struct sway_output *new_output = | 1167 | struct sway_output *new_output = |
1115 | new_workspace ? new_workspace->output : NULL; | 1168 | new_workspace ? new_workspace->output : NULL; |
@@ -1135,10 +1188,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 | 1188 | // Put the container parents on the focus stack, then the workspace, then |
1136 | // the focused container. | 1189 | // the focused container. |
1137 | if (container) { | 1190 | if (container) { |
1138 | struct sway_container *parent = container->parent; | 1191 | struct sway_container *parent = container->pending.parent; |
1139 | while (parent) { | 1192 | while (parent) { |
1140 | seat_set_raw_focus(seat, &parent->node); | 1193 | seat_set_raw_focus(seat, &parent->node); |
1141 | parent = parent->parent; | 1194 | parent = parent->pending.parent; |
1142 | } | 1195 | } |
1143 | } | 1196 | } |
1144 | if (new_workspace) { | 1197 | if (new_workspace) { |
@@ -1210,6 +1263,24 @@ void seat_set_focus(struct sway_seat *seat, struct sway_node *node) { | |||
1210 | } | 1263 | } |
1211 | } | 1264 | } |
1212 | 1265 | ||
1266 | void seat_set_focus(struct sway_seat *seat, struct sway_node *node) { | ||
1267 | // Prevents the layer from losing focus if it has keyboard exclusivity | ||
1268 | if (seat->has_exclusive_layer) { | ||
1269 | struct wlr_layer_surface_v1 *layer = seat->focused_layer; | ||
1270 | seat_set_focus_layer(seat, NULL); | ||
1271 | seat_set_workspace_focus(seat, node); | ||
1272 | seat_set_focus_layer(seat, layer); | ||
1273 | } else if (seat->focused_layer) { | ||
1274 | seat_set_focus_layer(seat, NULL); | ||
1275 | seat_set_workspace_focus(seat, node); | ||
1276 | } else { | ||
1277 | seat_set_workspace_focus(seat, node); | ||
1278 | } | ||
1279 | if (server.session_lock.lock) { | ||
1280 | seat_set_focus_surface(seat, server.session_lock.lock->focused, false); | ||
1281 | } | ||
1282 | } | ||
1283 | |||
1213 | void seat_set_focus_container(struct sway_seat *seat, | 1284 | void seat_set_focus_container(struct sway_seat *seat, |
1214 | struct sway_container *con) { | 1285 | struct sway_container *con) { |
1215 | seat_set_focus(seat, con ? &con->node : NULL); | 1286 | seat_set_focus(seat, con ? &con->node : NULL); |
@@ -1234,7 +1305,8 @@ void seat_set_focus_surface(struct sway_seat *seat, | |||
1234 | wlr_seat_keyboard_notify_clear_focus(seat->wlr_seat); | 1305 | wlr_seat_keyboard_notify_clear_focus(seat->wlr_seat); |
1235 | } | 1306 | } |
1236 | 1307 | ||
1237 | seat_tablet_pads_notify_enter(seat, surface); | 1308 | sway_input_method_relay_set_focus(&seat->im_relay, surface); |
1309 | seat_tablet_pads_set_focus(seat, surface); | ||
1238 | } | 1310 | } |
1239 | 1311 | ||
1240 | void seat_set_focus_layer(struct sway_seat *seat, | 1312 | void seat_set_focus_layer(struct sway_seat *seat, |
@@ -1248,28 +1320,23 @@ void seat_set_focus_layer(struct sway_seat *seat, | |||
1248 | seat_set_focus(seat, previous); | 1320 | seat_set_focus(seat, previous); |
1249 | } | 1321 | } |
1250 | return; | 1322 | return; |
1251 | } else if (!layer || seat->focused_layer == layer) { | 1323 | } else if (!layer) { |
1252 | return; | 1324 | return; |
1253 | } | 1325 | } |
1254 | assert(layer->mapped); | 1326 | assert(layer->surface->mapped); |
1255 | seat_set_focus_surface(seat, layer->surface, true); | 1327 | if (layer->current.layer >= ZWLR_LAYER_SHELL_V1_LAYER_TOP && |
1256 | if (layer->current.layer >= ZWLR_LAYER_SHELL_V1_LAYER_TOP) { | 1328 | layer->current.keyboard_interactive |
1257 | seat->focused_layer = layer; | 1329 | == ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_EXCLUSIVE) { |
1330 | seat->has_exclusive_layer = true; | ||
1258 | } | 1331 | } |
1259 | } | 1332 | if (seat->focused_layer == layer) { |
1260 | |||
1261 | void seat_set_exclusive_client(struct sway_seat *seat, | ||
1262 | struct wl_client *client) { | ||
1263 | if (!client) { | ||
1264 | seat->exclusive_client = client; | ||
1265 | // Triggers a refocus of the topmost surface layer if necessary | ||
1266 | // TODO: Make layer surface focus per-output based on cursor position | ||
1267 | for (int i = 0; i < root->outputs->length; ++i) { | ||
1268 | struct sway_output *output = root->outputs->items[i]; | ||
1269 | arrange_layers(output); | ||
1270 | } | ||
1271 | return; | 1333 | return; |
1272 | } | 1334 | } |
1335 | seat_set_focus_surface(seat, layer->surface, true); | ||
1336 | seat->focused_layer = layer; | ||
1337 | } | ||
1338 | |||
1339 | void seat_unfocus_unless_client(struct sway_seat *seat, struct wl_client *client) { | ||
1273 | if (seat->focused_layer) { | 1340 | if (seat->focused_layer) { |
1274 | if (wl_resource_get_client(seat->focused_layer->resource) != client) { | 1341 | if (wl_resource_get_client(seat->focused_layer->resource) != client) { |
1275 | seat_set_focus_layer(seat, NULL); | 1342 | seat_set_focus_layer(seat, NULL); |
@@ -1296,7 +1363,6 @@ void seat_set_exclusive_client(struct sway_seat *seat, | |||
1296 | now.tv_nsec / 1000, point->touch_id); | 1363 | now.tv_nsec / 1000, point->touch_id); |
1297 | } | 1364 | } |
1298 | } | 1365 | } |
1299 | seat->exclusive_client = client; | ||
1300 | } | 1366 | } |
1301 | 1367 | ||
1302 | struct sway_node *seat_get_focus_inactive(struct sway_seat *seat, | 1368 | struct sway_node *seat_get_focus_inactive(struct sway_seat *seat, |
@@ -1326,7 +1392,7 @@ struct sway_container *seat_get_focus_inactive_tiling(struct sway_seat *seat, | |||
1326 | struct sway_node *node = current->node; | 1392 | struct sway_node *node = current->node; |
1327 | if (node->type == N_CONTAINER && | 1393 | if (node->type == N_CONTAINER && |
1328 | !container_is_floating_or_child(node->sway_container) && | 1394 | !container_is_floating_or_child(node->sway_container) && |
1329 | node->sway_container->workspace == workspace) { | 1395 | node->sway_container->pending.workspace == workspace) { |
1330 | return node->sway_container; | 1396 | return node->sway_container; |
1331 | } | 1397 | } |
1332 | } | 1398 | } |
@@ -1343,7 +1409,7 @@ struct sway_container *seat_get_focus_inactive_floating(struct sway_seat *seat, | |||
1343 | struct sway_node *node = current->node; | 1409 | struct sway_node *node = current->node; |
1344 | if (node->type == N_CONTAINER && | 1410 | if (node->type == N_CONTAINER && |
1345 | container_is_floating_or_child(node->sway_container) && | 1411 | container_is_floating_or_child(node->sway_container) && |
1346 | node->sway_container->workspace == workspace) { | 1412 | node->sway_container->pending.workspace == workspace) { |
1347 | return node->sway_container; | 1413 | return node->sway_container; |
1348 | } | 1414 | } |
1349 | } | 1415 | } |
@@ -1377,9 +1443,8 @@ struct sway_node *seat_get_focus(struct sway_seat *seat) { | |||
1377 | if (!seat->has_focus) { | 1443 | if (!seat->has_focus) { |
1378 | return NULL; | 1444 | return NULL; |
1379 | } | 1445 | } |
1380 | if (wl_list_empty(&seat->focus_stack)) { | 1446 | sway_assert(!wl_list_empty(&seat->focus_stack), |
1381 | return NULL; | 1447 | "focus_stack is empty, but has_focus is true"); |
1382 | } | ||
1383 | struct sway_seat_node *current = | 1448 | struct sway_seat_node *current = |
1384 | wl_container_of(seat->focus_stack.next, current, link); | 1449 | wl_container_of(seat->focus_stack.next, current, link); |
1385 | return current->node; | 1450 | return current->node; |
@@ -1391,7 +1456,7 @@ struct sway_workspace *seat_get_focused_workspace(struct sway_seat *seat) { | |||
1391 | return NULL; | 1456 | return NULL; |
1392 | } | 1457 | } |
1393 | if (focus->type == N_CONTAINER) { | 1458 | if (focus->type == N_CONTAINER) { |
1394 | return focus->sway_container->workspace; | 1459 | return focus->sway_container->pending.workspace; |
1395 | } | 1460 | } |
1396 | if (focus->type == N_WORKSPACE) { | 1461 | if (focus->type == N_WORKSPACE) { |
1397 | return focus->sway_workspace; | 1462 | return focus->sway_workspace; |
@@ -1404,8 +1469,8 @@ struct sway_workspace *seat_get_last_known_workspace(struct sway_seat *seat) { | |||
1404 | wl_list_for_each(current, &seat->focus_stack, link) { | 1469 | wl_list_for_each(current, &seat->focus_stack, link) { |
1405 | struct sway_node *node = current->node; | 1470 | struct sway_node *node = current->node; |
1406 | if (node->type == N_CONTAINER && | 1471 | if (node->type == N_CONTAINER && |
1407 | node->sway_container->workspace) { | 1472 | node->sway_container->pending.workspace) { |
1408 | return node->sway_container->workspace; | 1473 | return node->sway_container->pending.workspace; |
1409 | } else if (node->type == N_WORKSPACE) { | 1474 | } else if (node->type == N_WORKSPACE) { |
1410 | return node->sway_workspace; | 1475 | return node->sway_workspace; |
1411 | } | 1476 | } |
@@ -1464,7 +1529,7 @@ struct seat_config *seat_get_config_by_name(const char *name) { | |||
1464 | } | 1529 | } |
1465 | 1530 | ||
1466 | void seat_pointer_notify_button(struct sway_seat *seat, uint32_t time_msec, | 1531 | void seat_pointer_notify_button(struct sway_seat *seat, uint32_t time_msec, |
1467 | uint32_t button, enum wlr_button_state state) { | 1532 | uint32_t button, enum wl_pointer_button_state state) { |
1468 | seat->last_button_serial = wlr_seat_pointer_notify_button(seat->wlr_seat, | 1533 | seat->last_button_serial = wlr_seat_pointer_notify_button(seat->wlr_seat, |
1469 | time_msec, button, state); | 1534 | time_msec, button, state); |
1470 | } | 1535 | } |
@@ -1501,7 +1566,7 @@ void seatop_unref(struct sway_seat *seat, struct sway_container *con) { | |||
1501 | 1566 | ||
1502 | void seatop_button(struct sway_seat *seat, uint32_t time_msec, | 1567 | void seatop_button(struct sway_seat *seat, uint32_t time_msec, |
1503 | struct wlr_input_device *device, uint32_t button, | 1568 | struct wlr_input_device *device, uint32_t button, |
1504 | enum wlr_button_state state) { | 1569 | enum wl_pointer_button_state state) { |
1505 | if (seat->seatop_impl->button) { | 1570 | if (seat->seatop_impl->button) { |
1506 | seat->seatop_impl->button(seat, time_msec, device, button, state); | 1571 | seat->seatop_impl->button(seat, time_msec, device, button, state); |
1507 | } | 1572 | } |
@@ -1514,12 +1579,38 @@ void seatop_pointer_motion(struct sway_seat *seat, uint32_t time_msec) { | |||
1514 | } | 1579 | } |
1515 | 1580 | ||
1516 | void seatop_pointer_axis(struct sway_seat *seat, | 1581 | void seatop_pointer_axis(struct sway_seat *seat, |
1517 | struct wlr_event_pointer_axis *event) { | 1582 | struct wlr_pointer_axis_event *event) { |
1518 | if (seat->seatop_impl->pointer_axis) { | 1583 | if (seat->seatop_impl->pointer_axis) { |
1519 | seat->seatop_impl->pointer_axis(seat, event); | 1584 | seat->seatop_impl->pointer_axis(seat, event); |
1520 | } | 1585 | } |
1521 | } | 1586 | } |
1522 | 1587 | ||
1588 | void seatop_touch_motion(struct sway_seat *seat, struct wlr_touch_motion_event *event, | ||
1589 | double lx, double ly) { | ||
1590 | if (seat->seatop_impl->touch_motion) { | ||
1591 | seat->seatop_impl->touch_motion(seat, event, lx, ly); | ||
1592 | } | ||
1593 | } | ||
1594 | |||
1595 | void seatop_touch_up(struct sway_seat *seat, struct wlr_touch_up_event *event) { | ||
1596 | if (seat->seatop_impl->touch_up) { | ||
1597 | seat->seatop_impl->touch_up(seat, event); | ||
1598 | } | ||
1599 | } | ||
1600 | |||
1601 | void seatop_touch_down(struct sway_seat *seat, struct wlr_touch_down_event *event, | ||
1602 | double lx, double ly) { | ||
1603 | if (seat->seatop_impl->touch_down) { | ||
1604 | seat->seatop_impl->touch_down(seat, event, lx, ly); | ||
1605 | } | ||
1606 | } | ||
1607 | |||
1608 | void seatop_touch_cancel(struct sway_seat *seat, struct wlr_touch_cancel_event *event) { | ||
1609 | if (seat->seatop_impl->touch_cancel) { | ||
1610 | seat->seatop_impl->touch_cancel(seat, event); | ||
1611 | } | ||
1612 | } | ||
1613 | |||
1523 | void seatop_tablet_tool_tip(struct sway_seat *seat, | 1614 | void seatop_tablet_tool_tip(struct sway_seat *seat, |
1524 | struct sway_tablet_tool *tool, uint32_t time_msec, | 1615 | struct sway_tablet_tool *tool, uint32_t time_msec, |
1525 | enum wlr_tablet_tool_tip_state state) { | 1616 | enum wlr_tablet_tool_tip_state state) { |
@@ -1537,6 +1628,62 @@ void seatop_tablet_tool_motion(struct sway_seat *seat, | |||
1537 | } | 1628 | } |
1538 | } | 1629 | } |
1539 | 1630 | ||
1631 | void seatop_hold_begin(struct sway_seat *seat, | ||
1632 | struct wlr_pointer_hold_begin_event *event) { | ||
1633 | if (seat->seatop_impl->hold_begin) { | ||
1634 | seat->seatop_impl->hold_begin(seat, event); | ||
1635 | } | ||
1636 | } | ||
1637 | |||
1638 | void seatop_hold_end(struct sway_seat *seat, | ||
1639 | struct wlr_pointer_hold_end_event *event) { | ||
1640 | if (seat->seatop_impl->hold_end) { | ||
1641 | seat->seatop_impl->hold_end(seat, event); | ||
1642 | } | ||
1643 | } | ||
1644 | |||
1645 | void seatop_pinch_begin(struct sway_seat *seat, | ||
1646 | struct wlr_pointer_pinch_begin_event *event) { | ||
1647 | if (seat->seatop_impl->pinch_begin) { | ||
1648 | seat->seatop_impl->pinch_begin(seat, event); | ||
1649 | } | ||
1650 | } | ||
1651 | |||
1652 | void seatop_pinch_update(struct sway_seat *seat, | ||
1653 | struct wlr_pointer_pinch_update_event *event) { | ||
1654 | if (seat->seatop_impl->pinch_update) { | ||
1655 | seat->seatop_impl->pinch_update(seat, event); | ||
1656 | } | ||
1657 | } | ||
1658 | |||
1659 | void seatop_pinch_end(struct sway_seat *seat, | ||
1660 | struct wlr_pointer_pinch_end_event *event) { | ||
1661 | if (seat->seatop_impl->pinch_end) { | ||
1662 | seat->seatop_impl->pinch_end(seat, event); | ||
1663 | } | ||
1664 | } | ||
1665 | |||
1666 | void seatop_swipe_begin(struct sway_seat *seat, | ||
1667 | struct wlr_pointer_swipe_begin_event *event) { | ||
1668 | if (seat->seatop_impl->swipe_begin) { | ||
1669 | seat->seatop_impl->swipe_begin(seat, event); | ||
1670 | } | ||
1671 | } | ||
1672 | |||
1673 | void seatop_swipe_update(struct sway_seat *seat, | ||
1674 | struct wlr_pointer_swipe_update_event *event) { | ||
1675 | if (seat->seatop_impl->swipe_update) { | ||
1676 | seat->seatop_impl->swipe_update(seat, event); | ||
1677 | } | ||
1678 | } | ||
1679 | |||
1680 | void seatop_swipe_end(struct sway_seat *seat, | ||
1681 | struct wlr_pointer_swipe_end_event *event) { | ||
1682 | if (seat->seatop_impl->swipe_end) { | ||
1683 | seat->seatop_impl->swipe_end(seat, event); | ||
1684 | } | ||
1685 | } | ||
1686 | |||
1540 | void seatop_rebase(struct sway_seat *seat, uint32_t time_msec) { | 1687 | void seatop_rebase(struct sway_seat *seat, uint32_t time_msec) { |
1541 | if (seat->seatop_impl->rebase) { | 1688 | if (seat->seatop_impl->rebase) { |
1542 | seat->seatop_impl->rebase(seat, time_msec); | 1689 | seat->seatop_impl->rebase(seat, time_msec); |
@@ -1552,13 +1699,6 @@ void seatop_end(struct sway_seat *seat) { | |||
1552 | seat->seatop_impl = NULL; | 1699 | seat->seatop_impl = NULL; |
1553 | } | 1700 | } |
1554 | 1701 | ||
1555 | void seatop_render(struct sway_seat *seat, struct sway_output *output, | ||
1556 | pixman_region32_t *damage) { | ||
1557 | if (seat->seatop_impl->render) { | ||
1558 | seat->seatop_impl->render(seat, output, damage); | ||
1559 | } | ||
1560 | } | ||
1561 | |||
1562 | bool seatop_allows_set_cursor(struct sway_seat *seat) { | 1702 | bool seatop_allows_set_cursor(struct sway_seat *seat) { |
1563 | return seat->seatop_impl->allow_set_cursor; | 1703 | return seat->seatop_impl->allow_set_cursor; |
1564 | } | 1704 | } |
diff --git a/sway/input/seatop_default.c b/sway/input/seatop_default.c index a583ed62..e01fa933 100644 --- a/sway/input/seatop_default.c +++ b/sway/input/seatop_default.c | |||
@@ -1,17 +1,21 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <float.h> | 1 | #include <float.h> |
3 | #include <libevdev/libevdev.h> | 2 | #include <libevdev/libevdev.h> |
4 | #include <wlr/types/wlr_cursor.h> | 3 | #include <wlr/types/wlr_cursor.h> |
4 | #include <wlr/types/wlr_subcompositor.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" |
12 | #include "sway/layers.h" | ||
10 | #include "sway/output.h" | 13 | #include "sway/output.h" |
14 | #include "sway/scene_descriptor.h" | ||
11 | #include "sway/tree/view.h" | 15 | #include "sway/tree/view.h" |
12 | #include "sway/tree/workspace.h" | 16 | #include "sway/tree/workspace.h" |
13 | #include "log.h" | 17 | #include "log.h" |
14 | #if HAVE_XWAYLAND | 18 | #if WLR_HAS_XWAYLAND |
15 | #include "sway/xwayland.h" | 19 | #include "sway/xwayland.h" |
16 | #endif | 20 | #endif |
17 | 21 | ||
@@ -19,6 +23,7 @@ struct seatop_default_event { | |||
19 | struct sway_node *previous_node; | 23 | struct sway_node *previous_node; |
20 | uint32_t pressed_buttons[SWAY_CURSOR_PRESSED_BUTTONS_CAP]; | 24 | uint32_t pressed_buttons[SWAY_CURSOR_PRESSED_BUTTONS_CAP]; |
21 | size_t pressed_button_count; | 25 | size_t pressed_button_count; |
26 | struct gesture_tracker gestures; | ||
22 | }; | 27 | }; |
23 | 28 | ||
24 | /*-----------------------------------------\ | 29 | /*-----------------------------------------\ |
@@ -50,6 +55,9 @@ static bool edge_is_external(struct sway_container *cont, enum wlr_edges edge) { | |||
50 | while (cont) { | 55 | while (cont) { |
51 | if (container_parent_layout(cont) == layout) { | 56 | if (container_parent_layout(cont) == layout) { |
52 | list_t *siblings = container_get_siblings(cont); | 57 | list_t *siblings = container_get_siblings(cont); |
58 | if (!siblings) { | ||
59 | return false; | ||
60 | } | ||
53 | int index = list_find(siblings, cont); | 61 | int index = list_find(siblings, cont); |
54 | if (index > 0 && (edge == WLR_EDGE_LEFT || edge == WLR_EDGE_TOP)) { | 62 | if (index > 0 && (edge == WLR_EDGE_LEFT || edge == WLR_EDGE_TOP)) { |
55 | return false; | 63 | return false; |
@@ -59,7 +67,7 @@ static bool edge_is_external(struct sway_container *cont, enum wlr_edges edge) { | |||
59 | return false; | 67 | return false; |
60 | } | 68 | } |
61 | } | 69 | } |
62 | cont = cont->parent; | 70 | cont = cont->pending.parent; |
63 | } | 71 | } |
64 | return true; | 72 | return true; |
65 | } | 73 | } |
@@ -69,25 +77,25 @@ static enum wlr_edges find_edge(struct sway_container *cont, | |||
69 | if (!cont->view || (surface && cont->view->surface != surface)) { | 77 | if (!cont->view || (surface && cont->view->surface != surface)) { |
70 | return WLR_EDGE_NONE; | 78 | return WLR_EDGE_NONE; |
71 | } | 79 | } |
72 | if (cont->border == B_NONE || !cont->border_thickness || | 80 | if (cont->pending.border == B_NONE || !cont->pending.border_thickness || |
73 | cont->border == B_CSD) { | 81 | cont->pending.border == B_CSD) { |
74 | return WLR_EDGE_NONE; | 82 | return WLR_EDGE_NONE; |
75 | } | 83 | } |
76 | if (cont->fullscreen_mode) { | 84 | if (cont->pending.fullscreen_mode) { |
77 | return WLR_EDGE_NONE; | 85 | return WLR_EDGE_NONE; |
78 | } | 86 | } |
79 | 87 | ||
80 | enum wlr_edges edge = 0; | 88 | enum wlr_edges edge = 0; |
81 | if (cursor->cursor->x < cont->x + cont->border_thickness) { | 89 | if (cursor->cursor->x < cont->pending.x + cont->pending.border_thickness) { |
82 | edge |= WLR_EDGE_LEFT; | 90 | edge |= WLR_EDGE_LEFT; |
83 | } | 91 | } |
84 | if (cursor->cursor->y < cont->y + cont->border_thickness) { | 92 | if (cursor->cursor->y < cont->pending.y + cont->pending.border_thickness) { |
85 | edge |= WLR_EDGE_TOP; | 93 | edge |= WLR_EDGE_TOP; |
86 | } | 94 | } |
87 | if (cursor->cursor->x >= cont->x + cont->width - cont->border_thickness) { | 95 | if (cursor->cursor->x >= cont->pending.x + cont->pending.width - cont->pending.border_thickness) { |
88 | edge |= WLR_EDGE_RIGHT; | 96 | edge |= WLR_EDGE_RIGHT; |
89 | } | 97 | } |
90 | if (cursor->cursor->y >= cont->y + cont->height - cont->border_thickness) { | 98 | if (cursor->cursor->y >= cont->pending.y + cont->pending.height - cont->pending.border_thickness) { |
91 | edge |= WLR_EDGE_BOTTOM; | 99 | edge |= WLR_EDGE_BOTTOM; |
92 | } | 100 | } |
93 | 101 | ||
@@ -225,13 +233,15 @@ static void handle_tablet_tool_tip(struct sway_seat *seat, | |||
225 | struct sway_container *cont = node && node->type == N_CONTAINER ? | 233 | struct sway_container *cont = node && node->type == N_CONTAINER ? |
226 | node->sway_container : NULL; | 234 | node->sway_container : NULL; |
227 | 235 | ||
228 | if (wlr_surface_is_layer_surface(surface)) { | 236 | struct wlr_layer_surface_v1 *layer; |
237 | #if WLR_HAS_XWAYLAND | ||
238 | struct wlr_xwayland_surface *xsurface; | ||
239 | #endif | ||
240 | if ((layer = wlr_layer_surface_v1_try_from_wlr_surface(surface)) && | ||
241 | layer->current.keyboard_interactive) { | ||
229 | // Handle tapping a layer surface | 242 | // Handle tapping a layer surface |
230 | struct wlr_layer_surface_v1 *layer = | 243 | seat_set_focus_layer(seat, layer); |
231 | wlr_layer_surface_v1_from_wlr_surface(surface); | 244 | transaction_commit_dirty(); |
232 | if (layer->current.keyboard_interactive) { | ||
233 | seat_set_focus_layer(seat, layer); | ||
234 | } | ||
235 | } else if (cont) { | 245 | } else if (cont) { |
236 | bool is_floating_or_child = container_is_floating_or_child(cont); | 246 | bool is_floating_or_child = container_is_floating_or_child(cont); |
237 | bool is_fullscreen_or_child = container_is_fullscreen_or_child(cont); | 247 | bool is_fullscreen_or_child = container_is_fullscreen_or_child(cont); |
@@ -249,26 +259,24 @@ static void handle_tablet_tool_tip(struct sway_seat *seat, | |||
249 | 259 | ||
250 | // Handle moving a tiling container | 260 | // Handle moving a tiling container |
251 | if (config->tiling_drag && mod_pressed && !is_floating_or_child && | 261 | if (config->tiling_drag && mod_pressed && !is_floating_or_child && |
252 | cont->fullscreen_mode == FULLSCREEN_NONE) { | 262 | cont->pending.fullscreen_mode == FULLSCREEN_NONE) { |
253 | seatop_begin_move_tiling(seat, cont); | 263 | seatop_begin_move_tiling(seat, cont); |
254 | return; | 264 | return; |
255 | } | 265 | } |
256 | 266 | ||
257 | // Handle tapping on a container surface | 267 | // Handle tapping on a container surface |
258 | seat_set_focus_container(seat, cont); | 268 | seat_set_focus_container(seat, cont); |
259 | seatop_begin_down(seat, node->sway_container, time_msec, sx, sy); | 269 | seatop_begin_down(seat, node->sway_container, sx, sy); |
260 | } | 270 | } |
261 | #if HAVE_XWAYLAND | 271 | #if WLR_HAS_XWAYLAND |
262 | // Handle tapping on an xwayland unmanaged view | 272 | // Handle tapping on an xwayland unmanaged view |
263 | else if (wlr_surface_is_xwayland_surface(surface)) { | 273 | else if ((xsurface = wlr_xwayland_surface_try_from_wlr_surface(surface)) && |
264 | struct wlr_xwayland_surface *xsurface = | 274 | xsurface->override_redirect && |
265 | wlr_xwayland_surface_from_wlr_surface(surface); | 275 | wlr_xwayland_or_surface_wants_focus(xsurface)) { |
266 | if (xsurface->override_redirect && | 276 | struct wlr_xwayland *xwayland = server.xwayland.wlr_xwayland; |
267 | wlr_xwayland_or_surface_wants_focus(xsurface)) { | 277 | wlr_xwayland_set_seat(xwayland, seat->wlr_seat); |
268 | struct wlr_xwayland *xwayland = server.xwayland.wlr_xwayland; | 278 | seat_set_focus_surface(seat, xsurface->surface, false); |
269 | wlr_xwayland_set_seat(xwayland, seat->wlr_seat); | 279 | transaction_commit_dirty(); |
270 | seat_set_focus_surface(seat, xsurface->surface, false); | ||
271 | } | ||
272 | } | 280 | } |
273 | #endif | 281 | #endif |
274 | 282 | ||
@@ -282,7 +290,7 @@ static void handle_tablet_tool_tip(struct sway_seat *seat, | |||
282 | 290 | ||
283 | static bool trigger_pointer_button_binding(struct sway_seat *seat, | 291 | static bool trigger_pointer_button_binding(struct sway_seat *seat, |
284 | struct wlr_input_device *device, uint32_t button, | 292 | struct wlr_input_device *device, uint32_t button, |
285 | enum wlr_button_state state, uint32_t modifiers, | 293 | enum wl_pointer_button_state state, uint32_t modifiers, |
286 | bool on_titlebar, bool on_border, bool on_contents, bool on_workspace) { | 294 | bool on_titlebar, bool on_border, bool on_contents, bool on_workspace) { |
287 | // We can reach this for non-pointer devices if we're currently emulating | 295 | // We can reach this for non-pointer devices if we're currently emulating |
288 | // pointer input for one. Emulated input should not trigger bindings. The | 296 | // pointer input for one. Emulated input should not trigger bindings. The |
@@ -296,7 +304,7 @@ static bool trigger_pointer_button_binding(struct sway_seat *seat, | |||
296 | char *device_identifier = device ? input_device_get_identifier(device) | 304 | char *device_identifier = device ? input_device_get_identifier(device) |
297 | : strdup("*"); | 305 | : strdup("*"); |
298 | struct sway_binding *binding = NULL; | 306 | struct sway_binding *binding = NULL; |
299 | if (state == WLR_BUTTON_PRESSED) { | 307 | if (state == WL_POINTER_BUTTON_STATE_PRESSED) { |
300 | state_add_button(e, button); | 308 | state_add_button(e, button); |
301 | binding = get_active_mouse_binding(e, | 309 | binding = get_active_mouse_binding(e, |
302 | config->current_mode->mouse_bindings, modifiers, false, | 310 | config->current_mode->mouse_bindings, modifiers, false, |
@@ -321,7 +329,7 @@ static bool trigger_pointer_button_binding(struct sway_seat *seat, | |||
321 | 329 | ||
322 | static void handle_button(struct sway_seat *seat, uint32_t time_msec, | 330 | static void handle_button(struct sway_seat *seat, uint32_t time_msec, |
323 | struct wlr_input_device *device, uint32_t button, | 331 | struct wlr_input_device *device, uint32_t button, |
324 | enum wlr_button_state state) { | 332 | enum wl_pointer_button_state state) { |
325 | struct sway_cursor *cursor = seat->cursor; | 333 | struct sway_cursor *cursor = seat->cursor; |
326 | 334 | ||
327 | // Determine what's under the cursor | 335 | // Determine what's under the cursor |
@@ -354,19 +362,23 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec, | |||
354 | 362 | ||
355 | // Handle clicking an empty workspace | 363 | // Handle clicking an empty workspace |
356 | if (node && node->type == N_WORKSPACE) { | 364 | if (node && node->type == N_WORKSPACE) { |
357 | if (state == WLR_BUTTON_PRESSED) { | 365 | if (state == WL_POINTER_BUTTON_STATE_PRESSED) { |
358 | seat_set_focus(seat, node); | 366 | seat_set_focus(seat, node); |
367 | transaction_commit_dirty(); | ||
359 | } | 368 | } |
360 | seat_pointer_notify_button(seat, time_msec, button, state); | 369 | seat_pointer_notify_button(seat, time_msec, button, state); |
361 | return; | 370 | return; |
362 | } | 371 | } |
363 | 372 | ||
364 | // Handle clicking a layer surface | 373 | // Handle clicking a layer surface and its popups/subsurfaces |
365 | if (surface && wlr_surface_is_layer_surface(surface)) { | 374 | struct wlr_layer_surface_v1 *layer = NULL; |
366 | struct wlr_layer_surface_v1 *layer = | 375 | if ((layer = toplevel_layer_surface_from_surface(surface))) { |
367 | wlr_layer_surface_v1_from_wlr_surface(surface); | ||
368 | if (layer->current.keyboard_interactive) { | 376 | if (layer->current.keyboard_interactive) { |
369 | seat_set_focus_layer(seat, layer); | 377 | seat_set_focus_layer(seat, layer); |
378 | transaction_commit_dirty(); | ||
379 | } | ||
380 | if (state == WL_POINTER_BUTTON_STATE_PRESSED) { | ||
381 | seatop_begin_down_on_surface(seat, surface, sx, sy); | ||
370 | } | 382 | } |
371 | seat_pointer_notify_button(seat, time_msec, button, state); | 383 | seat_pointer_notify_button(seat, time_msec, button, state); |
372 | return; | 384 | return; |
@@ -374,14 +386,14 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec, | |||
374 | 386 | ||
375 | // Handle tiling resize via border | 387 | // Handle tiling resize via border |
376 | if (cont && resize_edge && button == BTN_LEFT && | 388 | if (cont && resize_edge && button == BTN_LEFT && |
377 | state == WLR_BUTTON_PRESSED && !is_floating) { | 389 | state == WL_POINTER_BUTTON_STATE_PRESSED && !is_floating) { |
378 | // If a resize is triggered on a tabbed or stacked container, change | 390 | // If a resize is triggered on a tabbed or stacked container, change |
379 | // focus to the tab which already had inactive focus -- otherwise, we'd | 391 | // focus to the tab which already had inactive focus -- otherwise, we'd |
380 | // change the active tab when the user probably just wanted to resize. | 392 | // change the active tab when the user probably just wanted to resize. |
381 | struct sway_container *cont_to_focus = cont; | 393 | struct sway_container *cont_to_focus = cont; |
382 | enum sway_container_layout layout = container_parent_layout(cont); | 394 | enum sway_container_layout layout = container_parent_layout(cont); |
383 | if (layout == L_TABBED || layout == L_STACKED) { | 395 | if (layout == L_TABBED || layout == L_STACKED) { |
384 | cont_to_focus = seat_get_focus_inactive_view(seat, &cont->parent->node); | 396 | cont_to_focus = seat_get_focus_inactive_view(seat, &cont->pending.parent->node); |
385 | } | 397 | } |
386 | 398 | ||
387 | seat_set_focus_container(seat, cont_to_focus); | 399 | seat_set_focus_container(seat, cont_to_focus); |
@@ -392,14 +404,14 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec, | |||
392 | // Handle tiling resize via mod | 404 | // Handle tiling resize via mod |
393 | bool mod_pressed = modifiers & config->floating_mod; | 405 | bool mod_pressed = modifiers & config->floating_mod; |
394 | if (cont && !is_floating_or_child && mod_pressed && | 406 | if (cont && !is_floating_or_child && mod_pressed && |
395 | state == WLR_BUTTON_PRESSED) { | 407 | state == WL_POINTER_BUTTON_STATE_PRESSED) { |
396 | uint32_t btn_resize = config->floating_mod_inverse ? | 408 | uint32_t btn_resize = config->floating_mod_inverse ? |
397 | BTN_LEFT : BTN_RIGHT; | 409 | BTN_LEFT : BTN_RIGHT; |
398 | if (button == btn_resize) { | 410 | if (button == btn_resize) { |
399 | edge = 0; | 411 | edge = 0; |
400 | edge |= cursor->cursor->x > cont->x + cont->width / 2 ? | 412 | edge |= cursor->cursor->x > cont->pending.x + cont->pending.width / 2 ? |
401 | WLR_EDGE_RIGHT : WLR_EDGE_LEFT; | 413 | WLR_EDGE_RIGHT : WLR_EDGE_LEFT; |
402 | edge |= cursor->cursor->y > cont->y + cont->height / 2 ? | 414 | edge |= cursor->cursor->y > cont->pending.y + cont->pending.height / 2 ? |
403 | WLR_EDGE_BOTTOM : WLR_EDGE_TOP; | 415 | WLR_EDGE_BOTTOM : WLR_EDGE_TOP; |
404 | 416 | ||
405 | const char *image = NULL; | 417 | const char *image = NULL; |
@@ -419,13 +431,31 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec, | |||
419 | } | 431 | } |
420 | } | 432 | } |
421 | 433 | ||
434 | // Handle changing focus when clicking on a container | ||
435 | if (cont && state == WL_POINTER_BUTTON_STATE_PRESSED) { | ||
436 | // Default case: focus the container that was just clicked. | ||
437 | node = &cont->node; | ||
438 | |||
439 | // If the container is a tab/stacked container and the click happened | ||
440 | // on a tab, switch to the tab. If the tab contents were already | ||
441 | // focused, focus the tab container itself. If the tab container was | ||
442 | // already focused, cycle back to focusing the tab contents. | ||
443 | if (on_titlebar) { | ||
444 | struct sway_container *focus = seat_get_focused_container(seat); | ||
445 | if (focus == cont || !container_has_ancestor(focus, cont)) { | ||
446 | node = seat_get_focus_inactive(seat, &cont->node); | ||
447 | } | ||
448 | } | ||
449 | |||
450 | seat_set_focus(seat, node); | ||
451 | transaction_commit_dirty(); | ||
452 | } | ||
453 | |||
422 | // Handle beginning floating move | 454 | // Handle beginning floating move |
423 | if (cont && is_floating_or_child && !is_fullscreen_or_child && | 455 | if (cont && is_floating_or_child && !is_fullscreen_or_child && |
424 | state == WLR_BUTTON_PRESSED) { | 456 | state == WL_POINTER_BUTTON_STATE_PRESSED) { |
425 | uint32_t btn_move = config->floating_mod_inverse ? BTN_RIGHT : BTN_LEFT; | 457 | uint32_t btn_move = config->floating_mod_inverse ? BTN_RIGHT : BTN_LEFT; |
426 | if (button == btn_move && (mod_pressed || on_titlebar)) { | 458 | if (button == btn_move && (mod_pressed || on_titlebar)) { |
427 | seat_set_focus_container(seat, | ||
428 | seat_get_focus_inactive_view(seat, &cont->node)); | ||
429 | seatop_begin_move_floating(seat, container_toplevel_ancestor(cont)); | 459 | seatop_begin_move_floating(seat, container_toplevel_ancestor(cont)); |
430 | return; | 460 | return; |
431 | } | 461 | } |
@@ -433,9 +463,10 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec, | |||
433 | 463 | ||
434 | // Handle beginning floating resize | 464 | // Handle beginning floating resize |
435 | if (cont && is_floating_or_child && !is_fullscreen_or_child && | 465 | if (cont && is_floating_or_child && !is_fullscreen_or_child && |
436 | state == WLR_BUTTON_PRESSED) { | 466 | state == WL_POINTER_BUTTON_STATE_PRESSED) { |
437 | // Via border | 467 | // Via border |
438 | if (button == BTN_LEFT && resize_edge != WLR_EDGE_NONE) { | 468 | if (button == BTN_LEFT && resize_edge != WLR_EDGE_NONE) { |
469 | seat_set_focus_container(seat, cont); | ||
439 | seatop_begin_resize_floating(seat, cont, resize_edge); | 470 | seatop_begin_resize_floating(seat, cont, resize_edge); |
440 | return; | 471 | return; |
441 | } | 472 | } |
@@ -446,10 +477,11 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec, | |||
446 | if (mod_pressed && button == btn_resize) { | 477 | if (mod_pressed && button == btn_resize) { |
447 | struct sway_container *floater = container_toplevel_ancestor(cont); | 478 | struct sway_container *floater = container_toplevel_ancestor(cont); |
448 | edge = 0; | 479 | edge = 0; |
449 | edge |= cursor->cursor->x > floater->x + floater->width / 2 ? | 480 | edge |= cursor->cursor->x > floater->pending.x + floater->pending.width / 2 ? |
450 | WLR_EDGE_RIGHT : WLR_EDGE_LEFT; | 481 | WLR_EDGE_RIGHT : WLR_EDGE_LEFT; |
451 | edge |= cursor->cursor->y > floater->y + floater->height / 2 ? | 482 | edge |= cursor->cursor->y > floater->pending.y + floater->pending.height / 2 ? |
452 | WLR_EDGE_BOTTOM : WLR_EDGE_TOP; | 483 | WLR_EDGE_BOTTOM : WLR_EDGE_TOP; |
484 | seat_set_focus_container(seat, floater); | ||
453 | seatop_begin_resize_floating(seat, floater, edge); | 485 | seatop_begin_resize_floating(seat, floater, edge); |
454 | return; | 486 | return; |
455 | } | 487 | } |
@@ -457,53 +489,43 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec, | |||
457 | 489 | ||
458 | // Handle moving a tiling container | 490 | // Handle moving a tiling container |
459 | if (config->tiling_drag && (mod_pressed || on_titlebar) && | 491 | if (config->tiling_drag && (mod_pressed || on_titlebar) && |
460 | state == WLR_BUTTON_PRESSED && !is_floating_or_child && | 492 | state == WL_POINTER_BUTTON_STATE_PRESSED && !is_floating_or_child && |
461 | cont && cont->fullscreen_mode == FULLSCREEN_NONE) { | 493 | cont && cont->pending.fullscreen_mode == FULLSCREEN_NONE) { |
462 | struct sway_container *focus = seat_get_focused_container(seat); | 494 | // If moving a container by its title bar, use a threshold for the drag |
463 | bool focused = focus == cont || container_has_ancestor(focus, cont); | ||
464 | if (on_titlebar && !focused) { | ||
465 | node = seat_get_focus_inactive(seat, &cont->node); | ||
466 | seat_set_focus(seat, node); | ||
467 | } | ||
468 | |||
469 | // If moving a container by it's title bar, use a threshold for the drag | ||
470 | if (!mod_pressed && config->tiling_drag_threshold > 0) { | 495 | if (!mod_pressed && config->tiling_drag_threshold > 0) { |
471 | seatop_begin_move_tiling_threshold(seat, cont); | 496 | seatop_begin_move_tiling_threshold(seat, cont); |
472 | } else { | 497 | } else { |
473 | seatop_begin_move_tiling(seat, cont); | 498 | seatop_begin_move_tiling(seat, cont); |
474 | } | 499 | } |
500 | |||
475 | return; | 501 | return; |
476 | } | 502 | } |
477 | 503 | ||
478 | // Handle mousedown on a container surface | 504 | // Handle mousedown on a container surface |
479 | if (surface && cont && state == WLR_BUTTON_PRESSED) { | 505 | if (surface && cont && state == WL_POINTER_BUTTON_STATE_PRESSED) { |
480 | seat_set_focus_container(seat, cont); | 506 | seatop_begin_down(seat, cont, sx, sy); |
481 | seatop_begin_down(seat, cont, time_msec, sx, sy); | 507 | seat_pointer_notify_button(seat, time_msec, button, WL_POINTER_BUTTON_STATE_PRESSED); |
482 | seat_pointer_notify_button(seat, time_msec, button, WLR_BUTTON_PRESSED); | ||
483 | return; | 508 | return; |
484 | } | 509 | } |
485 | 510 | ||
486 | // Handle clicking a container surface or decorations | 511 | // Handle clicking a container surface or decorations |
487 | if (cont && state == WLR_BUTTON_PRESSED) { | 512 | if (cont && state == WL_POINTER_BUTTON_STATE_PRESSED) { |
488 | node = seat_get_focus_inactive(seat, &cont->node); | ||
489 | seat_set_focus(seat, node); | ||
490 | seat_pointer_notify_button(seat, time_msec, button, state); | 513 | seat_pointer_notify_button(seat, time_msec, button, state); |
491 | return; | 514 | return; |
492 | } | 515 | } |
493 | 516 | ||
494 | #if HAVE_XWAYLAND | 517 | #if WLR_HAS_XWAYLAND |
495 | // Handle clicking on xwayland unmanaged view | 518 | // Handle clicking on xwayland unmanaged view |
496 | if (surface && wlr_surface_is_xwayland_surface(surface)) { | 519 | struct wlr_xwayland_surface *xsurface; |
497 | struct wlr_xwayland_surface *xsurface = | 520 | if (surface && |
498 | wlr_xwayland_surface_from_wlr_surface(surface); | 521 | (xsurface = wlr_xwayland_surface_try_from_wlr_surface(surface)) && |
499 | if (xsurface->override_redirect && | 522 | xsurface->override_redirect && |
500 | wlr_xwayland_or_surface_wants_focus(xsurface)) { | 523 | wlr_xwayland_or_surface_wants_focus(xsurface)) { |
501 | struct wlr_xwayland *xwayland = server.xwayland.wlr_xwayland; | 524 | struct wlr_xwayland *xwayland = server.xwayland.wlr_xwayland; |
502 | wlr_xwayland_set_seat(xwayland, seat->wlr_seat); | 525 | wlr_xwayland_set_seat(xwayland, seat->wlr_seat); |
503 | seat_set_focus_surface(seat, xsurface->surface, false); | 526 | seat_set_focus_surface(seat, xsurface->surface, false); |
504 | seat_pointer_notify_button(seat, time_msec, button, state); | 527 | transaction_commit_dirty(); |
505 | return; | 528 | seat_pointer_notify_button(seat, time_msec, button, state); |
506 | } | ||
507 | } | 529 | } |
508 | #endif | 530 | #endif |
509 | 531 | ||
@@ -526,10 +548,26 @@ static void check_focus_follows_mouse(struct sway_seat *seat, | |||
526 | if (wlr_output == NULL) { | 548 | if (wlr_output == NULL) { |
527 | return; | 549 | return; |
528 | } | 550 | } |
551 | |||
552 | struct wlr_surface *surface = NULL; | ||
553 | double sx, sy; | ||
554 | node_at_coords(seat, seat->cursor->cursor->x, seat->cursor->cursor->y, | ||
555 | &surface, &sx, &sy); | ||
556 | |||
557 | // Focus topmost layer surface | ||
558 | struct wlr_layer_surface_v1 *layer = NULL; | ||
559 | if ((layer = toplevel_layer_surface_from_surface(surface)) && | ||
560 | layer->current.keyboard_interactive) { | ||
561 | seat_set_focus_layer(seat, layer); | ||
562 | transaction_commit_dirty(); | ||
563 | return; | ||
564 | } | ||
565 | |||
529 | struct sway_output *hovered_output = wlr_output->data; | 566 | struct sway_output *hovered_output = wlr_output->data; |
530 | if (focus && hovered_output != node_get_output(focus)) { | 567 | if (focus && hovered_output != node_get_output(focus)) { |
531 | struct sway_workspace *ws = output_get_active_workspace(hovered_output); | 568 | struct sway_workspace *ws = output_get_active_workspace(hovered_output); |
532 | seat_set_focus(seat, &ws->node); | 569 | seat_set_focus(seat, &ws->node); |
570 | transaction_commit_dirty(); | ||
533 | } | 571 | } |
534 | return; | 572 | return; |
535 | } | 573 | } |
@@ -541,6 +579,7 @@ static void check_focus_follows_mouse(struct sway_seat *seat, | |||
541 | struct sway_output *hovered_output = node_get_output(hovered_node); | 579 | struct sway_output *hovered_output = node_get_output(hovered_node); |
542 | if (hovered_output != focused_output) { | 580 | if (hovered_output != focused_output) { |
543 | seat_set_focus(seat, seat_get_focus_inactive(seat, hovered_node)); | 581 | seat_set_focus(seat, seat_get_focus_inactive(seat, hovered_node)); |
582 | transaction_commit_dirty(); | ||
544 | } | 583 | } |
545 | return; | 584 | return; |
546 | } | 585 | } |
@@ -556,6 +595,7 @@ static void check_focus_follows_mouse(struct sway_seat *seat, | |||
556 | if (hovered_node != e->previous_node || | 595 | if (hovered_node != e->previous_node || |
557 | config->focus_follows_mouse == FOLLOWS_ALWAYS) { | 596 | config->focus_follows_mouse == FOLLOWS_ALWAYS) { |
558 | seat_set_focus(seat, hovered_node); | 597 | seat_set_focus(seat, hovered_node); |
598 | transaction_commit_dirty(); | ||
559 | } | 599 | } |
560 | } | 600 | } |
561 | } | 601 | } |
@@ -583,12 +623,7 @@ static void handle_pointer_motion(struct sway_seat *seat, uint32_t time_msec) { | |||
583 | wlr_seat_pointer_notify_clear_focus(seat->wlr_seat); | 623 | wlr_seat_pointer_notify_clear_focus(seat->wlr_seat); |
584 | } | 624 | } |
585 | 625 | ||
586 | struct sway_drag_icon *drag_icon; | 626 | drag_icons_update_position(seat); |
587 | wl_list_for_each(drag_icon, &root->drag_icons, link) { | ||
588 | if (drag_icon->seat == seat) { | ||
589 | drag_icon_update_position(drag_icon); | ||
590 | } | ||
591 | } | ||
592 | 627 | ||
593 | e->previous_node = node; | 628 | e->previous_node = node; |
594 | } | 629 | } |
@@ -618,25 +653,50 @@ static void handle_tablet_tool_motion(struct sway_seat *seat, | |||
618 | wlr_tablet_v2_tablet_tool_notify_proximity_out(tool->tablet_v2_tool); | 653 | wlr_tablet_v2_tablet_tool_notify_proximity_out(tool->tablet_v2_tool); |
619 | } | 654 | } |
620 | 655 | ||
621 | struct sway_drag_icon *drag_icon; | 656 | drag_icons_update_position(seat); |
622 | wl_list_for_each(drag_icon, &root->drag_icons, link) { | ||
623 | if (drag_icon->seat == seat) { | ||
624 | drag_icon_update_position(drag_icon); | ||
625 | } | ||
626 | } | ||
627 | 657 | ||
628 | e->previous_node = node; | 658 | e->previous_node = node; |
629 | } | 659 | } |
630 | 660 | ||
661 | static void handle_touch_down(struct sway_seat *seat, | ||
662 | struct wlr_touch_down_event *event, double lx, double ly) { | ||
663 | struct wlr_surface *surface = NULL; | ||
664 | struct wlr_seat *wlr_seat = seat->wlr_seat; | ||
665 | struct sway_cursor *cursor = seat->cursor; | ||
666 | double sx, sy; | ||
667 | node_at_coords(seat, seat->touch_x, seat->touch_y, &surface, &sx, &sy); | ||
668 | |||
669 | if (surface && wlr_surface_accepts_touch(wlr_seat, surface)) { | ||
670 | if (seat_is_input_allowed(seat, surface)) { | ||
671 | cursor->simulating_pointer_from_touch = false; | ||
672 | seatop_begin_touch_down(seat, surface, event, sx, sy, lx, ly); | ||
673 | } | ||
674 | } else if (!cursor->simulating_pointer_from_touch && | ||
675 | (!surface || seat_is_input_allowed(seat, surface))) { | ||
676 | // Fallback to cursor simulation. | ||
677 | // The pointer_touch_id state is needed, so drags are not aborted when over | ||
678 | // a surface supporting touch and multi touch events don't interfere. | ||
679 | cursor->simulating_pointer_from_touch = true; | ||
680 | cursor->pointer_touch_id = seat->touch_id; | ||
681 | double dx, dy; | ||
682 | dx = seat->touch_x - cursor->cursor->x; | ||
683 | dy = seat->touch_y - cursor->cursor->y; | ||
684 | pointer_motion(cursor, event->time_msec, &event->touch->base, dx, dy, | ||
685 | dx, dy); | ||
686 | dispatch_cursor_button(cursor, &event->touch->base, event->time_msec, | ||
687 | BTN_LEFT, WL_POINTER_BUTTON_STATE_PRESSED); | ||
688 | } | ||
689 | } | ||
690 | |||
631 | /*----------------------------------------\ | 691 | /*----------------------------------------\ |
632 | * Functions used by handle_pointer_axis / | 692 | * Functions used by handle_pointer_axis / |
633 | *--------------------------------------*/ | 693 | *--------------------------------------*/ |
634 | 694 | ||
635 | static uint32_t wl_axis_to_button(struct wlr_event_pointer_axis *event) { | 695 | static uint32_t wl_axis_to_button(struct wlr_pointer_axis_event *event) { |
636 | switch (event->orientation) { | 696 | switch (event->orientation) { |
637 | case WLR_AXIS_ORIENTATION_VERTICAL: | 697 | case WL_POINTER_AXIS_VERTICAL_SCROLL: |
638 | return event->delta < 0 ? SWAY_SCROLL_UP : SWAY_SCROLL_DOWN; | 698 | return event->delta < 0 ? SWAY_SCROLL_UP : SWAY_SCROLL_DOWN; |
639 | case WLR_AXIS_ORIENTATION_HORIZONTAL: | 699 | case WL_POINTER_AXIS_HORIZONTAL_SCROLL: |
640 | return event->delta < 0 ? SWAY_SCROLL_LEFT : SWAY_SCROLL_RIGHT; | 700 | return event->delta < 0 ? SWAY_SCROLL_LEFT : SWAY_SCROLL_RIGHT; |
641 | default: | 701 | default: |
642 | sway_log(SWAY_DEBUG, "Unknown axis orientation"); | 702 | sway_log(SWAY_DEBUG, "Unknown axis orientation"); |
@@ -645,9 +705,9 @@ static uint32_t wl_axis_to_button(struct wlr_event_pointer_axis *event) { | |||
645 | } | 705 | } |
646 | 706 | ||
647 | static void handle_pointer_axis(struct sway_seat *seat, | 707 | static void handle_pointer_axis(struct sway_seat *seat, |
648 | struct wlr_event_pointer_axis *event) { | 708 | struct wlr_pointer_axis_event *event) { |
649 | struct sway_input_device *input_device = | 709 | struct sway_input_device *input_device = |
650 | event->device ? event->device->data : NULL; | 710 | event->pointer ? event->pointer->base.data : NULL; |
651 | struct input_config *ic = | 711 | struct input_config *ic = |
652 | input_device ? input_device_get_config(input_device) : NULL; | 712 | input_device ? input_device_get_config(input_device) : NULL; |
653 | struct sway_cursor *cursor = seat->cursor; | 713 | struct sway_cursor *cursor = seat->cursor; |
@@ -664,7 +724,7 @@ static void handle_pointer_axis(struct sway_seat *seat, | |||
664 | bool on_border = edge != WLR_EDGE_NONE; | 724 | bool on_border = edge != WLR_EDGE_NONE; |
665 | bool on_titlebar = cont && !on_border && !surface; | 725 | bool on_titlebar = cont && !on_border && !surface; |
666 | bool on_titlebar_border = cont && on_border && | 726 | bool on_titlebar_border = cont && on_border && |
667 | cursor->cursor->y < cont->content_y; | 727 | cursor->cursor->y < cont->pending.content_y; |
668 | bool on_contents = cont && !on_border && surface; | 728 | bool on_contents = cont && !on_border && surface; |
669 | bool on_workspace = node && node->type == N_WORKSPACE; | 729 | bool on_workspace = node && node->type == N_WORKSPACE; |
670 | float scroll_factor = | 730 | float scroll_factor = |
@@ -693,6 +753,7 @@ static void handle_pointer_axis(struct sway_seat *seat, | |||
693 | 753 | ||
694 | // Scrolling on a tabbed or stacked title bar (handled as press event) | 754 | // Scrolling on a tabbed or stacked title bar (handled as press event) |
695 | if (!handled && (on_titlebar || on_titlebar_border)) { | 755 | if (!handled && (on_titlebar || on_titlebar_border)) { |
756 | struct sway_node *new_focus; | ||
696 | enum sway_container_layout layout = container_parent_layout(cont); | 757 | enum sway_container_layout layout = container_parent_layout(cont); |
697 | if (layout == L_TABBED || layout == L_STACKED) { | 758 | if (layout == L_TABBED || layout == L_STACKED) { |
698 | struct sway_node *tabcontainer = node_get_parent(node); | 759 | struct sway_node *tabcontainer = node_get_parent(node); |
@@ -700,7 +761,7 @@ static void handle_pointer_axis(struct sway_seat *seat, | |||
700 | seat_get_active_tiling_child(seat, tabcontainer); | 761 | seat_get_active_tiling_child(seat, tabcontainer); |
701 | list_t *siblings = container_get_siblings(cont); | 762 | list_t *siblings = container_get_siblings(cont); |
702 | int desired = list_find(siblings, active->sway_container) + | 763 | int desired = list_find(siblings, active->sway_container) + |
703 | round(scroll_factor * event->delta_discrete); | 764 | roundf(scroll_factor * event->delta_discrete / WLR_POINTER_AXIS_DISCRETE_STEP); |
704 | if (desired < 0) { | 765 | if (desired < 0) { |
705 | desired = 0; | 766 | desired = 0; |
706 | } else if (desired >= siblings->length) { | 767 | } else if (desired >= siblings->length) { |
@@ -709,13 +770,16 @@ static void handle_pointer_axis(struct sway_seat *seat, | |||
709 | 770 | ||
710 | struct sway_container *new_sibling_con = siblings->items[desired]; | 771 | struct sway_container *new_sibling_con = siblings->items[desired]; |
711 | struct sway_node *new_sibling = &new_sibling_con->node; | 772 | struct sway_node *new_sibling = &new_sibling_con->node; |
712 | struct sway_node *new_focus = | ||
713 | seat_get_focus_inactive(seat, new_sibling); | ||
714 | // Use the focused child of the tabbed/stacked container, not the | 773 | // Use the focused child of the tabbed/stacked container, not the |
715 | // container the user scrolled on. | 774 | // container the user scrolled on. |
716 | seat_set_focus(seat, new_focus); | 775 | new_focus = seat_get_focus_inactive(seat, new_sibling); |
717 | handled = true; | 776 | } else { |
777 | new_focus = seat_get_focus_inactive(seat, &cont->node); | ||
718 | } | 778 | } |
779 | |||
780 | seat_set_focus(seat, new_focus); | ||
781 | transaction_commit_dirty(); | ||
782 | handled = true; | ||
719 | } | 783 | } |
720 | 784 | ||
721 | // Handle mouse bindings - x11 mouse buttons 4-7 - release event | 785 | // Handle mouse bindings - x11 mouse buttons 4-7 - release event |
@@ -731,8 +795,307 @@ static void handle_pointer_axis(struct sway_seat *seat, | |||
731 | 795 | ||
732 | if (!handled) { | 796 | if (!handled) { |
733 | wlr_seat_pointer_notify_axis(cursor->seat->wlr_seat, event->time_msec, | 797 | wlr_seat_pointer_notify_axis(cursor->seat->wlr_seat, event->time_msec, |
734 | event->orientation, scroll_factor * event->delta, | 798 | event->orientation, scroll_factor * event->delta, |
735 | round(scroll_factor * event->delta_discrete), event->source); | 799 | roundf(scroll_factor * event->delta_discrete), event->source, |
800 | event->relative_direction); | ||
801 | } | ||
802 | } | ||
803 | |||
804 | /*------------------------------------\ | ||
805 | * Functions used by gesture support / | ||
806 | *----------------------------------*/ | ||
807 | |||
808 | /** | ||
809 | * Check gesture binding for a specific gesture type and finger count. | ||
810 | * Returns true if binding is present, false otherwise | ||
811 | */ | ||
812 | static bool gesture_binding_check(list_t *bindings, enum gesture_type type, | ||
813 | uint8_t fingers, struct sway_input_device *device) { | ||
814 | char *input = | ||
815 | device ? input_device_get_identifier(device->wlr_device) : strdup("*"); | ||
816 | |||
817 | for (int i = 0; i < bindings->length; ++i) { | ||
818 | struct sway_gesture_binding *binding = bindings->items[i]; | ||
819 | |||
820 | // Check type and finger count | ||
821 | if (!gesture_check(&binding->gesture, type, fingers)) { | ||
822 | continue; | ||
823 | } | ||
824 | |||
825 | // Check that input matches | ||
826 | if (strcmp(binding->input, "*") != 0 && | ||
827 | strcmp(binding->input, input) != 0) { | ||
828 | continue; | ||
829 | } | ||
830 | |||
831 | free(input); | ||
832 | |||
833 | return true; | ||
834 | } | ||
835 | |||
836 | free(input); | ||
837 | |||
838 | return false; | ||
839 | } | ||
840 | |||
841 | /** | ||
842 | * Return the gesture binding which matches gesture type, finger count | ||
843 | * and direction, otherwise return null. | ||
844 | */ | ||
845 | static struct sway_gesture_binding* gesture_binding_match( | ||
846 | list_t *bindings, struct gesture *gesture, const char *input) { | ||
847 | struct sway_gesture_binding *current = NULL; | ||
848 | |||
849 | // Find best matching binding | ||
850 | for (int i = 0; i < bindings->length; ++i) { | ||
851 | struct sway_gesture_binding *binding = bindings->items[i]; | ||
852 | bool exact = binding->flags & BINDING_EXACT; | ||
853 | |||
854 | // Check gesture matching | ||
855 | if (!gesture_match(&binding->gesture, gesture, exact)) { | ||
856 | continue; | ||
857 | } | ||
858 | |||
859 | // Check input matching | ||
860 | if (strcmp(binding->input, "*") != 0 && | ||
861 | strcmp(binding->input, input) != 0) { | ||
862 | continue; | ||
863 | } | ||
864 | |||
865 | // If we already have a match ... | ||
866 | if (current) { | ||
867 | // ... check if input matching is equivalent | ||
868 | if (strcmp(current->input, binding->input) == 0) { | ||
869 | |||
870 | // ... - do not override an exact binding | ||
871 | if (!exact && current->flags & BINDING_EXACT) { | ||
872 | continue; | ||
873 | } | ||
874 | |||
875 | // ... - and ensure direction matching is better or equal | ||
876 | if (gesture_compare(¤t->gesture, &binding->gesture) > 0) { | ||
877 | continue; | ||
878 | } | ||
879 | } else if (strcmp(binding->input, "*") == 0) { | ||
880 | // ... do not accept worse input match | ||
881 | continue; | ||
882 | } | ||
883 | } | ||
884 | |||
885 | // Accept newer or better match | ||
886 | current = binding; | ||
887 | |||
888 | // If exact binding and input is found, quit search | ||
889 | if (strcmp(current->input, input) == 0 && | ||
890 | gesture_compare(¤t->gesture, gesture) == 0) { | ||
891 | break; | ||
892 | } | ||
893 | } // for all gesture bindings | ||
894 | |||
895 | return current; | ||
896 | } | ||
897 | |||
898 | // Wrapper around gesture_tracker_end to use tracker with sway bindings | ||
899 | static struct sway_gesture_binding* gesture_tracker_end_and_match( | ||
900 | struct gesture_tracker *tracker, struct sway_input_device* device) { | ||
901 | // Determine name of input that received gesture | ||
902 | char *input = device | ||
903 | ? input_device_get_identifier(device->wlr_device) | ||
904 | : strdup("*"); | ||
905 | |||
906 | // Match tracking result to binding | ||
907 | struct gesture *gesture = gesture_tracker_end(tracker); | ||
908 | struct sway_gesture_binding *binding = gesture_binding_match( | ||
909 | config->current_mode->gesture_bindings, gesture, input); | ||
910 | free(gesture); | ||
911 | free(input); | ||
912 | |||
913 | return binding; | ||
914 | } | ||
915 | |||
916 | // Small wrapper around seat_execute_command to work on gesture bindings | ||
917 | static void gesture_binding_execute(struct sway_seat *seat, | ||
918 | struct sway_gesture_binding *binding) { | ||
919 | struct sway_binding *dummy_binding = | ||
920 | calloc(1, sizeof(struct sway_binding)); | ||
921 | dummy_binding->type = BINDING_GESTURE; | ||
922 | dummy_binding->command = binding->command; | ||
923 | |||
924 | char *description = gesture_to_string(&binding->gesture); | ||
925 | sway_log(SWAY_DEBUG, "executing gesture binding: %s", description); | ||
926 | free(description); | ||
927 | |||
928 | seat_execute_command(seat, dummy_binding); | ||
929 | |||
930 | free(dummy_binding); | ||
931 | } | ||
932 | |||
933 | static void handle_hold_begin(struct sway_seat *seat, | ||
934 | struct wlr_pointer_hold_begin_event *event) { | ||
935 | // Start tracking gesture if there is a matching binding ... | ||
936 | struct sway_input_device *device = | ||
937 | event->pointer ? event->pointer->base.data : NULL; | ||
938 | list_t *bindings = config->current_mode->gesture_bindings; | ||
939 | if (gesture_binding_check(bindings, GESTURE_TYPE_HOLD, event->fingers, device)) { | ||
940 | struct seatop_default_event *seatop = seat->seatop_data; | ||
941 | gesture_tracker_begin(&seatop->gestures, GESTURE_TYPE_HOLD, event->fingers); | ||
942 | } else { | ||
943 | // ... otherwise forward to client | ||
944 | struct sway_cursor *cursor = seat->cursor; | ||
945 | wlr_pointer_gestures_v1_send_hold_begin( | ||
946 | server.input->pointer_gestures, cursor->seat->wlr_seat, | ||
947 | event->time_msec, event->fingers); | ||
948 | } | ||
949 | } | ||
950 | |||
951 | static void handle_hold_end(struct sway_seat *seat, | ||
952 | struct wlr_pointer_hold_end_event *event) { | ||
953 | // Ensure that gesture is being tracked and was not cancelled | ||
954 | struct seatop_default_event *seatop = seat->seatop_data; | ||
955 | if (!gesture_tracker_check(&seatop->gestures, GESTURE_TYPE_HOLD)) { | ||
956 | struct sway_cursor *cursor = seat->cursor; | ||
957 | wlr_pointer_gestures_v1_send_hold_end( | ||
958 | server.input->pointer_gestures, cursor->seat->wlr_seat, | ||
959 | event->time_msec, event->cancelled); | ||
960 | return; | ||
961 | } | ||
962 | if (event->cancelled) { | ||
963 | gesture_tracker_cancel(&seatop->gestures); | ||
964 | return; | ||
965 | } | ||
966 | |||
967 | // End gesture tracking and execute matched binding | ||
968 | struct sway_input_device *device = | ||
969 | event->pointer ? event->pointer->base.data : NULL; | ||
970 | struct sway_gesture_binding *binding = gesture_tracker_end_and_match( | ||
971 | &seatop->gestures, device); | ||
972 | |||
973 | if (binding) { | ||
974 | gesture_binding_execute(seat, binding); | ||
975 | } | ||
976 | } | ||
977 | |||
978 | static void handle_pinch_begin(struct sway_seat *seat, | ||
979 | struct wlr_pointer_pinch_begin_event *event) { | ||
980 | // Start tracking gesture if there is a matching binding ... | ||
981 | struct sway_input_device *device = | ||
982 | event->pointer ? event->pointer->base.data : NULL; | ||
983 | list_t *bindings = config->current_mode->gesture_bindings; | ||
984 | if (gesture_binding_check(bindings, GESTURE_TYPE_PINCH, event->fingers, device)) { | ||
985 | struct seatop_default_event *seatop = seat->seatop_data; | ||
986 | gesture_tracker_begin(&seatop->gestures, GESTURE_TYPE_PINCH, event->fingers); | ||
987 | } else { | ||
988 | // ... otherwise forward to client | ||
989 | struct sway_cursor *cursor = seat->cursor; | ||
990 | wlr_pointer_gestures_v1_send_pinch_begin( | ||
991 | server.input->pointer_gestures, cursor->seat->wlr_seat, | ||
992 | event->time_msec, event->fingers); | ||
993 | } | ||
994 | } | ||
995 | |||
996 | static void handle_pinch_update(struct sway_seat *seat, | ||
997 | struct wlr_pointer_pinch_update_event *event) { | ||
998 | // Update any ongoing tracking ... | ||
999 | struct seatop_default_event *seatop = seat->seatop_data; | ||
1000 | if (gesture_tracker_check(&seatop->gestures, GESTURE_TYPE_PINCH)) { | ||
1001 | gesture_tracker_update(&seatop->gestures, event->dx, event->dy, | ||
1002 | event->scale, event->rotation); | ||
1003 | } else { | ||
1004 | // ... otherwise forward to client | ||
1005 | struct sway_cursor *cursor = seat->cursor; | ||
1006 | wlr_pointer_gestures_v1_send_pinch_update( | ||
1007 | server.input->pointer_gestures, | ||
1008 | cursor->seat->wlr_seat, | ||
1009 | event->time_msec, event->dx, event->dy, | ||
1010 | event->scale, event->rotation); | ||
1011 | } | ||
1012 | } | ||
1013 | |||
1014 | static void handle_pinch_end(struct sway_seat *seat, | ||
1015 | struct wlr_pointer_pinch_end_event *event) { | ||
1016 | // Ensure that gesture is being tracked and was not cancelled | ||
1017 | struct seatop_default_event *seatop = seat->seatop_data; | ||
1018 | if (!gesture_tracker_check(&seatop->gestures, GESTURE_TYPE_PINCH)) { | ||
1019 | struct sway_cursor *cursor = seat->cursor; | ||
1020 | wlr_pointer_gestures_v1_send_pinch_end( | ||
1021 | server.input->pointer_gestures, cursor->seat->wlr_seat, | ||
1022 | event->time_msec, event->cancelled); | ||
1023 | return; | ||
1024 | } | ||
1025 | if (event->cancelled) { | ||
1026 | gesture_tracker_cancel(&seatop->gestures); | ||
1027 | return; | ||
1028 | } | ||
1029 | |||
1030 | // End gesture tracking and execute matched binding | ||
1031 | struct sway_input_device *device = | ||
1032 | event->pointer ? event->pointer->base.data : NULL; | ||
1033 | struct sway_gesture_binding *binding = gesture_tracker_end_and_match( | ||
1034 | &seatop->gestures, device); | ||
1035 | |||
1036 | if (binding) { | ||
1037 | gesture_binding_execute(seat, binding); | ||
1038 | } | ||
1039 | } | ||
1040 | |||
1041 | static void handle_swipe_begin(struct sway_seat *seat, | ||
1042 | struct wlr_pointer_swipe_begin_event *event) { | ||
1043 | // Start tracking gesture if there is a matching binding ... | ||
1044 | struct sway_input_device *device = | ||
1045 | event->pointer ? event->pointer->base.data : NULL; | ||
1046 | list_t *bindings = config->current_mode->gesture_bindings; | ||
1047 | if (gesture_binding_check(bindings, GESTURE_TYPE_SWIPE, event->fingers, device)) { | ||
1048 | struct seatop_default_event *seatop = seat->seatop_data; | ||
1049 | gesture_tracker_begin(&seatop->gestures, GESTURE_TYPE_SWIPE, event->fingers); | ||
1050 | } else { | ||
1051 | // ... otherwise forward to client | ||
1052 | struct sway_cursor *cursor = seat->cursor; | ||
1053 | wlr_pointer_gestures_v1_send_swipe_begin( | ||
1054 | server.input->pointer_gestures, cursor->seat->wlr_seat, | ||
1055 | event->time_msec, event->fingers); | ||
1056 | } | ||
1057 | } | ||
1058 | |||
1059 | static void handle_swipe_update(struct sway_seat *seat, | ||
1060 | struct wlr_pointer_swipe_update_event *event) { | ||
1061 | |||
1062 | // Update any ongoing tracking ... | ||
1063 | struct seatop_default_event *seatop = seat->seatop_data; | ||
1064 | if (gesture_tracker_check(&seatop->gestures, GESTURE_TYPE_SWIPE)) { | ||
1065 | gesture_tracker_update(&seatop->gestures, | ||
1066 | event->dx, event->dy, NAN, NAN); | ||
1067 | } else { | ||
1068 | // ... otherwise forward to client | ||
1069 | struct sway_cursor *cursor = seat->cursor; | ||
1070 | wlr_pointer_gestures_v1_send_swipe_update( | ||
1071 | server.input->pointer_gestures, cursor->seat->wlr_seat, | ||
1072 | event->time_msec, event->dx, event->dy); | ||
1073 | } | ||
1074 | } | ||
1075 | |||
1076 | static void handle_swipe_end(struct sway_seat *seat, | ||
1077 | struct wlr_pointer_swipe_end_event *event) { | ||
1078 | // Ensure gesture is being tracked and was not cancelled | ||
1079 | struct seatop_default_event *seatop = seat->seatop_data; | ||
1080 | if (!gesture_tracker_check(&seatop->gestures, GESTURE_TYPE_SWIPE)) { | ||
1081 | struct sway_cursor *cursor = seat->cursor; | ||
1082 | wlr_pointer_gestures_v1_send_swipe_end(server.input->pointer_gestures, | ||
1083 | cursor->seat->wlr_seat, event->time_msec, event->cancelled); | ||
1084 | return; | ||
1085 | } | ||
1086 | if (event->cancelled) { | ||
1087 | gesture_tracker_cancel(&seatop->gestures); | ||
1088 | return; | ||
1089 | } | ||
1090 | |||
1091 | // End gesture tracking and execute matched binding | ||
1092 | struct sway_input_device *device = | ||
1093 | event->pointer ? event->pointer->base.data : NULL; | ||
1094 | struct sway_gesture_binding *binding = gesture_tracker_end_and_match( | ||
1095 | &seatop->gestures, device); | ||
1096 | |||
1097 | if (binding) { | ||
1098 | gesture_binding_execute(seat, binding); | ||
736 | } | 1099 | } |
737 | } | 1100 | } |
738 | 1101 | ||
@@ -765,6 +1128,15 @@ static const struct sway_seatop_impl seatop_impl = { | |||
765 | .pointer_axis = handle_pointer_axis, | 1128 | .pointer_axis = handle_pointer_axis, |
766 | .tablet_tool_tip = handle_tablet_tool_tip, | 1129 | .tablet_tool_tip = handle_tablet_tool_tip, |
767 | .tablet_tool_motion = handle_tablet_tool_motion, | 1130 | .tablet_tool_motion = handle_tablet_tool_motion, |
1131 | .hold_begin = handle_hold_begin, | ||
1132 | .hold_end = handle_hold_end, | ||
1133 | .pinch_begin = handle_pinch_begin, | ||
1134 | .pinch_update = handle_pinch_update, | ||
1135 | .pinch_end = handle_pinch_end, | ||
1136 | .swipe_begin = handle_swipe_begin, | ||
1137 | .swipe_update = handle_swipe_update, | ||
1138 | .swipe_end = handle_swipe_end, | ||
1139 | .touch_down = handle_touch_down, | ||
768 | .rebase = handle_rebase, | 1140 | .rebase = handle_rebase, |
769 | .allow_set_cursor = true, | 1141 | .allow_set_cursor = true, |
770 | }; | 1142 | }; |
@@ -775,8 +1147,8 @@ void seatop_begin_default(struct sway_seat *seat) { | |||
775 | struct seatop_default_event *e = | 1147 | struct seatop_default_event *e = |
776 | calloc(1, sizeof(struct seatop_default_event)); | 1148 | calloc(1, sizeof(struct seatop_default_event)); |
777 | sway_assert(e, "Unable to allocate seatop_default_event"); | 1149 | sway_assert(e, "Unable to allocate seatop_default_event"); |
1150 | |||
778 | seat->seatop_impl = &seatop_impl; | 1151 | seat->seatop_impl = &seatop_impl; |
779 | seat->seatop_data = e; | 1152 | seat->seatop_data = e; |
780 | |||
781 | seatop_rebase(seat, 0); | 1153 | seatop_rebase(seat, 0); |
782 | } | 1154 | } |
diff --git a/sway/input/seatop_down.c b/sway/input/seatop_down.c index 17f619e3..340e334b 100644 --- a/sway/input/seatop_down.c +++ b/sway/input/seatop_down.c | |||
@@ -1,22 +1,138 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <float.h> | 1 | #include <float.h> |
3 | #include <wlr/types/wlr_cursor.h> | 2 | #include <wlr/types/wlr_cursor.h> |
4 | #include <wlr/types/wlr_tablet_v2.h> | 3 | #include <wlr/types/wlr_tablet_v2.h> |
4 | #include <wlr/types/wlr_touch.h> | ||
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 | ||
11 | struct seatop_touch_point_event { | ||
12 | double ref_lx, ref_ly; // touch's x/y at start of op | ||
13 | double ref_con_lx, ref_con_ly; // container's x/y at start of op | ||
14 | int32_t touch_id; | ||
15 | struct wl_list link; | ||
16 | }; | ||
17 | |||
10 | struct seatop_down_event { | 18 | struct seatop_down_event { |
11 | struct sway_container *con; | 19 | struct sway_container *con; |
20 | struct sway_seat *seat; | ||
21 | struct wl_listener surface_destroy; | ||
22 | struct wlr_surface *surface; | ||
12 | double ref_lx, ref_ly; // cursor's x/y at start of op | 23 | 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 | 24 | double ref_con_lx, ref_con_ly; // container's x/y at start of op |
25 | struct wl_list point_events; // seatop_touch_point_event::link | ||
14 | }; | 26 | }; |
15 | 27 | ||
28 | static void handle_touch_motion(struct sway_seat *seat, | ||
29 | struct wlr_touch_motion_event *event, double lx, double ly) { | ||
30 | struct seatop_down_event *e = seat->seatop_data; | ||
31 | |||
32 | struct seatop_touch_point_event *point_event; | ||
33 | bool found = false; | ||
34 | wl_list_for_each(point_event, &e->point_events, link) { | ||
35 | if (point_event->touch_id == event->touch_id) { | ||
36 | found = true; | ||
37 | break; | ||
38 | } | ||
39 | } | ||
40 | if (!found) { | ||
41 | return; // Probably not a point_event from this seatop_down | ||
42 | } | ||
43 | |||
44 | double moved_x = lx - point_event->ref_lx; | ||
45 | double moved_y = ly - point_event->ref_ly; | ||
46 | double sx = point_event->ref_con_lx + moved_x; | ||
47 | double sy = point_event->ref_con_ly + moved_y; | ||
48 | |||
49 | wlr_seat_touch_notify_motion(seat->wlr_seat, event->time_msec, | ||
50 | event->touch_id, sx, sy); | ||
51 | } | ||
52 | |||
53 | static void handle_touch_up(struct sway_seat *seat, | ||
54 | struct wlr_touch_up_event *event) { | ||
55 | struct seatop_down_event *e = seat->seatop_data; | ||
56 | struct seatop_touch_point_event *point_event, *tmp; | ||
57 | |||
58 | wl_list_for_each_safe(point_event, tmp, &e->point_events, link) { | ||
59 | if (point_event->touch_id == event->touch_id) { | ||
60 | wl_list_remove(&point_event->link); | ||
61 | free(point_event); | ||
62 | break; | ||
63 | } | ||
64 | } | ||
65 | |||
66 | wlr_seat_touch_notify_up(seat->wlr_seat, event->time_msec, event->touch_id); | ||
67 | |||
68 | if (wl_list_empty(&e->point_events)) { | ||
69 | seatop_begin_default(seat); | ||
70 | } | ||
71 | } | ||
72 | |||
73 | static void handle_touch_down(struct sway_seat *seat, | ||
74 | struct wlr_touch_down_event *event, double lx, double ly) { | ||
75 | struct seatop_down_event *e = seat->seatop_data; | ||
76 | double sx, sy; | ||
77 | struct wlr_surface *surface = NULL; | ||
78 | struct sway_node *focused_node = node_at_coords(seat, seat->touch_x, | ||
79 | seat->touch_y, &surface, &sx, &sy); | ||
80 | |||
81 | if (!surface || surface != e->surface) { // Must start from the initial surface | ||
82 | return; | ||
83 | } | ||
84 | |||
85 | struct seatop_touch_point_event *point_event = | ||
86 | calloc(1, sizeof(struct seatop_touch_point_event)); | ||
87 | if (!sway_assert(point_event, "Unable to allocate point_event")) { | ||
88 | return; | ||
89 | } | ||
90 | point_event->touch_id = event->touch_id; | ||
91 | point_event->ref_lx = lx; | ||
92 | point_event->ref_ly = ly; | ||
93 | point_event->ref_con_lx = sx; | ||
94 | point_event->ref_con_ly = sy; | ||
95 | |||
96 | wl_list_insert(&e->point_events, &point_event->link); | ||
97 | |||
98 | wlr_seat_touch_notify_down(seat->wlr_seat, surface, event->time_msec, | ||
99 | event->touch_id, sx, sy); | ||
100 | |||
101 | if (focused_node) { | ||
102 | seat_set_focus(seat, focused_node); | ||
103 | } | ||
104 | } | ||
105 | |||
106 | static void handle_touch_cancel(struct sway_seat *seat, | ||
107 | struct wlr_touch_cancel_event *event) { | ||
108 | struct seatop_down_event *e = seat->seatop_data; | ||
109 | struct seatop_touch_point_event *point_event, *tmp; | ||
110 | |||
111 | wl_list_for_each_safe(point_event, tmp, &e->point_events, link) { | ||
112 | if (point_event->touch_id == event->touch_id) { | ||
113 | wl_list_remove(&point_event->link); | ||
114 | free(point_event); | ||
115 | break; | ||
116 | } | ||
117 | } | ||
118 | |||
119 | if (e->surface) { | ||
120 | struct wl_client *client = wl_resource_get_client(e->surface->resource); | ||
121 | struct wlr_seat_client *seat_client = wlr_seat_client_for_wl_client(seat->wlr_seat, client); | ||
122 | if (seat_client != NULL) { | ||
123 | wlr_seat_touch_notify_cancel(seat->wlr_seat, seat_client); | ||
124 | } | ||
125 | } | ||
126 | |||
127 | if (wl_list_empty(&e->point_events)) { | ||
128 | seatop_begin_default(seat); | ||
129 | } | ||
130 | } | ||
131 | |||
16 | static void handle_pointer_axis(struct sway_seat *seat, | 132 | static void handle_pointer_axis(struct sway_seat *seat, |
17 | struct wlr_event_pointer_axis *event) { | 133 | struct wlr_pointer_axis_event *event) { |
18 | struct sway_input_device *input_device = | 134 | struct sway_input_device *input_device = |
19 | event->device ? event->device->data : NULL; | 135 | event->pointer ? event->pointer->base.data : NULL; |
20 | struct input_config *ic = | 136 | struct input_config *ic = |
21 | input_device ? input_device_get_config(input_device) : NULL; | 137 | input_device ? input_device_get_config(input_device) : NULL; |
22 | float scroll_factor = | 138 | float scroll_factor = |
@@ -24,12 +140,13 @@ static void handle_pointer_axis(struct sway_seat *seat, | |||
24 | 140 | ||
25 | wlr_seat_pointer_notify_axis(seat->wlr_seat, event->time_msec, | 141 | wlr_seat_pointer_notify_axis(seat->wlr_seat, event->time_msec, |
26 | event->orientation, scroll_factor * event->delta, | 142 | event->orientation, scroll_factor * event->delta, |
27 | round(scroll_factor * event->delta_discrete), event->source); | 143 | roundf(scroll_factor * event->delta_discrete), event->source, |
144 | event->relative_direction); | ||
28 | } | 145 | } |
29 | 146 | ||
30 | static void handle_button(struct sway_seat *seat, uint32_t time_msec, | 147 | static void handle_button(struct sway_seat *seat, uint32_t time_msec, |
31 | struct wlr_input_device *device, uint32_t button, | 148 | struct wlr_input_device *device, uint32_t button, |
32 | enum wlr_button_state state) { | 149 | enum wl_pointer_button_state state) { |
33 | seat_pointer_notify_button(seat, time_msec, button, state); | 150 | seat_pointer_notify_button(seat, time_msec, button, state); |
34 | 151 | ||
35 | if (seat->cursor->pressed_button_count == 0) { | 152 | if (seat->cursor->pressed_button_count == 0) { |
@@ -39,8 +156,7 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec, | |||
39 | 156 | ||
40 | static void handle_pointer_motion(struct sway_seat *seat, uint32_t time_msec) { | 157 | static void handle_pointer_motion(struct sway_seat *seat, uint32_t time_msec) { |
41 | struct seatop_down_event *e = seat->seatop_data; | 158 | struct seatop_down_event *e = seat->seatop_data; |
42 | struct sway_container *con = e->con; | 159 | 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; | 160 | double moved_x = seat->cursor->cursor->x - e->ref_lx; |
45 | double moved_y = seat->cursor->cursor->y - e->ref_ly; | 161 | double moved_y = seat->cursor->cursor->y - e->ref_ly; |
46 | double sx = e->ref_con_lx + moved_x; | 162 | double sx = e->ref_con_lx + moved_x; |
@@ -61,8 +177,7 @@ static void handle_tablet_tool_tip(struct sway_seat *seat, | |||
61 | static void handle_tablet_tool_motion(struct sway_seat *seat, | 177 | static void handle_tablet_tool_motion(struct sway_seat *seat, |
62 | struct sway_tablet_tool *tool, uint32_t time_msec) { | 178 | struct sway_tablet_tool *tool, uint32_t time_msec) { |
63 | struct seatop_down_event *e = seat->seatop_data; | 179 | struct seatop_down_event *e = seat->seatop_data; |
64 | struct sway_container *con = e->con; | 180 | 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; | 181 | double moved_x = seat->cursor->cursor->x - e->ref_lx; |
67 | double moved_y = seat->cursor->cursor->y - e->ref_ly; | 182 | double moved_y = seat->cursor->cursor->y - e->ref_ly; |
68 | double sx = e->ref_con_lx + moved_x; | 183 | double sx = e->ref_con_lx + moved_x; |
@@ -71,6 +186,14 @@ static void handle_tablet_tool_motion(struct sway_seat *seat, | |||
71 | } | 186 | } |
72 | } | 187 | } |
73 | 188 | ||
189 | static void handle_destroy(struct wl_listener *listener, void *data) { | ||
190 | struct seatop_down_event *e = | ||
191 | wl_container_of(listener, e, surface_destroy); | ||
192 | if (e) { | ||
193 | seatop_begin_default(e->seat); | ||
194 | } | ||
195 | } | ||
196 | |||
74 | static void handle_unref(struct sway_seat *seat, struct sway_container *con) { | 197 | static void handle_unref(struct sway_seat *seat, struct sway_container *con) { |
75 | struct seatop_down_event *e = seat->seatop_data; | 198 | struct seatop_down_event *e = seat->seatop_data; |
76 | if (e->con == con) { | 199 | if (e->con == con) { |
@@ -78,33 +201,63 @@ static void handle_unref(struct sway_seat *seat, struct sway_container *con) { | |||
78 | } | 201 | } |
79 | } | 202 | } |
80 | 203 | ||
204 | static void handle_end(struct sway_seat *seat) { | ||
205 | struct seatop_down_event *e = seat->seatop_data; | ||
206 | wl_list_remove(&e->surface_destroy.link); | ||
207 | } | ||
208 | |||
81 | static const struct sway_seatop_impl seatop_impl = { | 209 | static const struct sway_seatop_impl seatop_impl = { |
82 | .button = handle_button, | 210 | .button = handle_button, |
83 | .pointer_motion = handle_pointer_motion, | 211 | .pointer_motion = handle_pointer_motion, |
84 | .pointer_axis = handle_pointer_axis, | 212 | .pointer_axis = handle_pointer_axis, |
85 | .tablet_tool_tip = handle_tablet_tool_tip, | 213 | .tablet_tool_tip = handle_tablet_tool_tip, |
86 | .tablet_tool_motion = handle_tablet_tool_motion, | 214 | .tablet_tool_motion = handle_tablet_tool_motion, |
215 | .touch_motion = handle_touch_motion, | ||
216 | .touch_up = handle_touch_up, | ||
217 | .touch_down = handle_touch_down, | ||
218 | .touch_cancel = handle_touch_cancel, | ||
87 | .unref = handle_unref, | 219 | .unref = handle_unref, |
220 | .end = handle_end, | ||
88 | .allow_set_cursor = true, | 221 | .allow_set_cursor = true, |
89 | }; | 222 | }; |
90 | 223 | ||
91 | void seatop_begin_down(struct sway_seat *seat, struct sway_container *con, | 224 | void seatop_begin_down(struct sway_seat *seat, struct sway_container *con, |
92 | uint32_t time_msec, int sx, int sy) { | 225 | double sx, double sy) { |
226 | seatop_begin_down_on_surface(seat, con->view->surface, sx, sy); | ||
227 | struct seatop_down_event *e = seat->seatop_data; | ||
228 | e->con = con; | ||
229 | |||
230 | container_raise_floating(con); | ||
231 | transaction_commit_dirty(); | ||
232 | } | ||
233 | |||
234 | void seatop_begin_touch_down(struct sway_seat *seat, | ||
235 | struct wlr_surface *surface, struct wlr_touch_down_event *event, | ||
236 | double sx, double sy, double lx, double ly) { | ||
237 | seatop_begin_down_on_surface(seat, surface, sx, sy); | ||
238 | handle_touch_down(seat, event, lx, ly); | ||
239 | } | ||
240 | |||
241 | void seatop_begin_down_on_surface(struct sway_seat *seat, | ||
242 | struct wlr_surface *surface, double sx, double sy) { | ||
93 | seatop_end(seat); | 243 | seatop_end(seat); |
94 | 244 | ||
95 | struct seatop_down_event *e = | 245 | struct seatop_down_event *e = |
96 | calloc(1, sizeof(struct seatop_down_event)); | 246 | calloc(1, sizeof(struct seatop_down_event)); |
97 | if (!e) { | 247 | if (!sway_assert(e, "Unable to allocate e")) { |
98 | return; | 248 | return; |
99 | } | 249 | } |
100 | e->con = con; | 250 | e->con = NULL; |
251 | e->seat = seat; | ||
252 | e->surface = surface; | ||
253 | wl_signal_add(&e->surface->events.destroy, &e->surface_destroy); | ||
254 | e->surface_destroy.notify = handle_destroy; | ||
101 | e->ref_lx = seat->cursor->cursor->x; | 255 | e->ref_lx = seat->cursor->cursor->x; |
102 | e->ref_ly = seat->cursor->cursor->y; | 256 | e->ref_ly = seat->cursor->cursor->y; |
103 | e->ref_con_lx = sx; | 257 | e->ref_con_lx = sx; |
104 | e->ref_con_ly = sy; | 258 | e->ref_con_ly = sy; |
259 | wl_list_init(&e->point_events); | ||
105 | 260 | ||
106 | seat->seatop_impl = &seatop_impl; | 261 | seat->seatop_impl = &seatop_impl; |
107 | seat->seatop_data = e; | 262 | seat->seatop_data = e; |
108 | |||
109 | container_raise_floating(con); | ||
110 | } | 263 | } |
diff --git a/sway/input/seatop_move_floating.c b/sway/input/seatop_move_floating.c index 7f501fc9..83668d88 100644 --- a/sway/input/seatop_move_floating.c +++ b/sway/input/seatop_move_floating.c | |||
@@ -1,6 +1,5 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <wlr/types/wlr_cursor.h> | 1 | #include <wlr/types/wlr_cursor.h> |
3 | #include "sway/desktop.h" | 2 | #include "sway/desktop/transaction.h" |
4 | #include "sway/input/cursor.h" | 3 | #include "sway/input/cursor.h" |
5 | #include "sway/input/seat.h" | 4 | #include "sway/input/seat.h" |
6 | 5 | ||
@@ -14,14 +13,15 @@ static void finalize_move(struct sway_seat *seat) { | |||
14 | 13 | ||
15 | // We "move" the container to its own location | 14 | // We "move" the container to its own location |
16 | // so it discovers its output again. | 15 | // so it discovers its output again. |
17 | container_floating_move_to(e->con, e->con->x, e->con->y); | 16 | container_floating_move_to(e->con, e->con->pending.x, e->con->pending.y); |
17 | transaction_commit_dirty(); | ||
18 | 18 | ||
19 | seatop_begin_default(seat); | 19 | seatop_begin_default(seat); |
20 | } | 20 | } |
21 | 21 | ||
22 | static void handle_button(struct sway_seat *seat, uint32_t time_msec, | 22 | static void handle_button(struct sway_seat *seat, uint32_t time_msec, |
23 | struct wlr_input_device *device, uint32_t button, | 23 | struct wlr_input_device *device, uint32_t button, |
24 | enum wlr_button_state state) { | 24 | enum wl_pointer_button_state state) { |
25 | if (seat->cursor->pressed_button_count == 0) { | 25 | if (seat->cursor->pressed_button_count == 0) { |
26 | finalize_move(seat); | 26 | finalize_move(seat); |
27 | } | 27 | } |
@@ -37,9 +37,8 @@ static void handle_tablet_tool_tip(struct sway_seat *seat, | |||
37 | static void handle_pointer_motion(struct sway_seat *seat, uint32_t time_msec) { | 37 | static void handle_pointer_motion(struct sway_seat *seat, uint32_t time_msec) { |
38 | struct seatop_move_floating_event *e = seat->seatop_data; | 38 | struct seatop_move_floating_event *e = seat->seatop_data; |
39 | struct wlr_cursor *cursor = seat->cursor->cursor; | 39 | struct wlr_cursor *cursor = seat->cursor->cursor; |
40 | desktop_damage_whole_container(e->con); | ||
41 | container_floating_move_to(e->con, cursor->x - e->dx, cursor->y - e->dy); | 40 | container_floating_move_to(e->con, cursor->x - e->dx, cursor->y - e->dy); |
42 | desktop_damage_whole_container(e->con); | 41 | transaction_commit_dirty(); |
43 | } | 42 | } |
44 | 43 | ||
45 | static void handle_unref(struct sway_seat *seat, struct sway_container *con) { | 44 | static void handle_unref(struct sway_seat *seat, struct sway_container *con) { |
@@ -67,13 +66,14 @@ void seatop_begin_move_floating(struct sway_seat *seat, | |||
67 | return; | 66 | return; |
68 | } | 67 | } |
69 | e->con = con; | 68 | e->con = con; |
70 | e->dx = cursor->cursor->x - con->x; | 69 | e->dx = cursor->cursor->x - con->pending.x; |
71 | e->dy = cursor->cursor->y - con->y; | 70 | e->dy = cursor->cursor->y - con->pending.y; |
72 | 71 | ||
73 | seat->seatop_impl = &seatop_impl; | 72 | seat->seatop_impl = &seatop_impl; |
74 | seat->seatop_data = e; | 73 | seat->seatop_data = e; |
75 | 74 | ||
76 | container_raise_floating(con); | 75 | container_raise_floating(con); |
76 | transaction_commit_dirty(); | ||
77 | 77 | ||
78 | cursor_set_image(cursor, "grab", NULL); | 78 | cursor_set_image(cursor, "grab", NULL); |
79 | wlr_seat_pointer_notify_clear_focus(seat->wlr_seat); | 79 | 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..c525b77a 100644 --- a/sway/input/seatop_move_tiling.c +++ b/sway/input/seatop_move_tiling.c | |||
@@ -1,8 +1,7 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <limits.h> | 1 | #include <limits.h> |
3 | #include <wlr/types/wlr_cursor.h> | 2 | #include <wlr/types/wlr_cursor.h> |
4 | #include <wlr/util/edges.h> | 3 | #include <wlr/util/edges.h> |
5 | #include "sway/desktop.h" | 4 | #include "sway/desktop/transaction.h" |
6 | #include "sway/input/cursor.h" | 5 | #include "sway/input/cursor.h" |
7 | #include "sway/input/seat.h" | 6 | #include "sway/input/seat.h" |
8 | #include "sway/ipc-server.h" | 7 | #include "sway/ipc-server.h" |
@@ -15,31 +14,25 @@ | |||
15 | // Thickness of the dropzone when dragging to the edge of a layout container | 14 | // Thickness of the dropzone when dragging to the edge of a layout container |
16 | #define DROP_LAYOUT_BORDER 30 | 15 | #define DROP_LAYOUT_BORDER 30 |
17 | 16 | ||
17 | // Thickness of indicator when dropping onto a titlebar. This should be a | ||
18 | // multiple of 2. | ||
19 | #define DROP_SPLIT_INDICATOR 10 | ||
20 | |||
18 | struct seatop_move_tiling_event { | 21 | struct seatop_move_tiling_event { |
19 | struct sway_container *con; | 22 | struct sway_container *con; |
20 | struct sway_node *target_node; | 23 | struct sway_node *target_node; |
21 | enum wlr_edges target_edge; | 24 | enum wlr_edges target_edge; |
22 | struct wlr_box drop_box; | ||
23 | double ref_lx, ref_ly; // cursor's x/y at start of op | 25 | double ref_lx, ref_ly; // cursor's x/y at start of op |
24 | bool threshold_reached; | 26 | bool threshold_reached; |
27 | bool split_target; | ||
28 | bool insert_after_target; | ||
29 | struct wlr_scene_rect *indicator_rect; | ||
25 | }; | 30 | }; |
26 | 31 | ||
27 | static void handle_render(struct sway_seat *seat, | 32 | static void handle_end(struct sway_seat *seat) { |
28 | struct sway_output *output, pixman_region32_t *damage) { | ||
29 | struct seatop_move_tiling_event *e = seat->seatop_data; | 33 | struct seatop_move_tiling_event *e = seat->seatop_data; |
30 | if (!e->threshold_reached) { | 34 | wlr_scene_node_destroy(&e->indicator_rect->node); |
31 | return; | 35 | e->indicator_rect = NULL; |
32 | } | ||
33 | if (e->target_node && node_get_output(e->target_node) == output) { | ||
34 | float color[4]; | ||
35 | memcpy(&color, config->border_colors.focused.indicator, | ||
36 | sizeof(float) * 4); | ||
37 | premultiply_alpha(color, 0.5); | ||
38 | struct wlr_box box; | ||
39 | memcpy(&box, &e->drop_box, sizeof(struct wlr_box)); | ||
40 | scale_box(&box, output->wlr_output->scale); | ||
41 | render_rect(output, damage, &box, color); | ||
42 | } | ||
43 | } | 36 | } |
44 | 37 | ||
45 | static void handle_motion_prethreshold(struct sway_seat *seat) { | 38 | static void handle_motion_prethreshold(struct sway_seat *seat) { |
@@ -60,6 +53,7 @@ static void handle_motion_prethreshold(struct sway_seat *seat) { | |||
60 | 53 | ||
61 | // If the threshold has been exceeded, start the actual drag | 54 | // If the threshold has been exceeded, start the actual drag |
62 | if ((cx - sx) * (cx - sx) + (cy - sy) * (cy - sy) > threshold) { | 55 | if ((cx - sx) * (cx - sx) + (cy - sy) * (cy - sy) > threshold) { |
56 | wlr_scene_node_set_enabled(&e->indicator_rect->node, true); | ||
63 | e->threshold_reached = true; | 57 | e->threshold_reached = true; |
64 | cursor_set_image(seat->cursor, "grab", NULL); | 58 | cursor_set_image(seat->cursor, "grab", NULL); |
65 | } | 59 | } |
@@ -91,15 +85,86 @@ static void resize_box(struct wlr_box *box, enum wlr_edges edge, | |||
91 | } | 85 | } |
92 | } | 86 | } |
93 | 87 | ||
88 | static void split_border(double pos, int offset, int len, int n_children, | ||
89 | int avoid, int *out_pos, bool *out_after) { | ||
90 | int region = 2 * n_children * (pos - offset) / len; | ||
91 | // If the cursor is over the right side of a left-adjacent titlebar, or the | ||
92 | // left side of a right-adjacent titlebar, it's position when dropped will | ||
93 | // be the same. To avoid this, shift the region for adjacent containers. | ||
94 | if (avoid >= 0) { | ||
95 | if (region == 2 * avoid - 1 || region == 2 * avoid) { | ||
96 | region--; | ||
97 | } else if (region == 2 * avoid + 1 || region == 2 * avoid + 2) { | ||
98 | region++; | ||
99 | } | ||
100 | } | ||
101 | |||
102 | int child_index = (region + 1) / 2; | ||
103 | *out_after = region % 2; | ||
104 | // When dropping at the beginning or end of a container, show the drop | ||
105 | // region within the container boundary, otherwise show it on top of the | ||
106 | // border between two titlebars. | ||
107 | if (child_index == 0) { | ||
108 | *out_pos = offset; | ||
109 | } else if (child_index == n_children) { | ||
110 | *out_pos = offset + len - DROP_SPLIT_INDICATOR; | ||
111 | } else { | ||
112 | *out_pos = offset + child_index * len / n_children - | ||
113 | DROP_SPLIT_INDICATOR / 2; | ||
114 | } | ||
115 | } | ||
116 | |||
117 | static bool split_titlebar(struct sway_node *node, struct sway_container *avoid, | ||
118 | struct wlr_cursor *cursor, struct wlr_box *title_box, bool *after) { | ||
119 | struct sway_container *con = node->sway_container; | ||
120 | struct sway_node *parent = &con->pending.parent->node; | ||
121 | int title_height = container_titlebar_height(); | ||
122 | struct wlr_box box; | ||
123 | int n_children, avoid_index; | ||
124 | enum sway_container_layout layout = | ||
125 | parent ? node_get_layout(parent) : L_NONE; | ||
126 | if (layout == L_TABBED || layout == L_STACKED) { | ||
127 | node_get_box(parent, &box); | ||
128 | n_children = node_get_children(parent)->length; | ||
129 | avoid_index = list_find(node_get_children(parent), avoid); | ||
130 | } else { | ||
131 | node_get_box(node, &box); | ||
132 | n_children = 1; | ||
133 | avoid_index = -1; | ||
134 | } | ||
135 | if (layout == L_STACKED && cursor->y < box.y + title_height * n_children) { | ||
136 | // Drop into stacked titlebars. | ||
137 | title_box->width = box.width; | ||
138 | title_box->height = DROP_SPLIT_INDICATOR; | ||
139 | title_box->x = box.x; | ||
140 | split_border(cursor->y, box.y, title_height * n_children, | ||
141 | n_children, avoid_index, &title_box->y, after); | ||
142 | return true; | ||
143 | } else if (layout != L_STACKED && cursor->y < box.y + title_height) { | ||
144 | // Drop into side-by-side titlebars. | ||
145 | title_box->width = DROP_SPLIT_INDICATOR; | ||
146 | title_box->height = title_height; | ||
147 | title_box->y = box.y; | ||
148 | split_border(cursor->x, box.x, box.width, n_children, | ||
149 | avoid_index, &title_box->x, after); | ||
150 | return true; | ||
151 | } | ||
152 | return false; | ||
153 | } | ||
154 | |||
155 | static void update_indicator(struct seatop_move_tiling_event *e, struct wlr_box *box) { | ||
156 | wlr_scene_node_set_position(&e->indicator_rect->node, box->x, box->y); | ||
157 | wlr_scene_rect_set_size(e->indicator_rect, box->width, box->height); | ||
158 | } | ||
159 | |||
94 | static void handle_motion_postthreshold(struct sway_seat *seat) { | 160 | static void handle_motion_postthreshold(struct sway_seat *seat) { |
95 | struct seatop_move_tiling_event *e = seat->seatop_data; | 161 | struct seatop_move_tiling_event *e = seat->seatop_data; |
162 | e->split_target = false; | ||
96 | struct wlr_surface *surface = NULL; | 163 | struct wlr_surface *surface = NULL; |
97 | double sx, sy; | 164 | double sx, sy; |
98 | struct sway_cursor *cursor = seat->cursor; | 165 | struct sway_cursor *cursor = seat->cursor; |
99 | struct sway_node *node = node_at_coords(seat, | 166 | struct sway_node *node = node_at_coords(seat, |
100 | cursor->cursor->x, cursor->cursor->y, &surface, &sx, &sy); | 167 | cursor->cursor->x, cursor->cursor->y, &surface, &sx, &sy); |
101 | // Damage the old location | ||
102 | desktop_damage_box(&e->drop_box); | ||
103 | 168 | ||
104 | if (!node) { | 169 | if (!node) { |
105 | // Eg. hovered over a layer surface such as swaybar | 170 | // Eg. hovered over a layer surface such as swaybar |
@@ -112,41 +177,77 @@ static void handle_motion_postthreshold(struct sway_seat *seat) { | |||
112 | // Empty workspace | 177 | // Empty workspace |
113 | e->target_node = node; | 178 | e->target_node = node; |
114 | e->target_edge = WLR_EDGE_NONE; | 179 | e->target_edge = WLR_EDGE_NONE; |
115 | workspace_get_box(node->sway_workspace, &e->drop_box); | 180 | |
116 | desktop_damage_box(&e->drop_box); | 181 | struct wlr_box drop_box; |
182 | workspace_get_box(node->sway_workspace, &drop_box); | ||
183 | update_indicator(e, &drop_box); | ||
117 | return; | 184 | return; |
118 | } | 185 | } |
119 | 186 | ||
120 | // Deny moving within own workspace if this is the only child | 187 | // Deny moving within own workspace if this is the only child |
121 | struct sway_container *con = node->sway_container; | 188 | struct sway_container *con = node->sway_container; |
122 | if (workspace_num_tiling_views(e->con->workspace) == 1 && | 189 | if (workspace_num_tiling_views(e->con->pending.workspace) == 1 && |
123 | con->workspace == e->con->workspace) { | 190 | con->pending.workspace == e->con->pending.workspace) { |
124 | e->target_node = NULL; | 191 | e->target_node = NULL; |
125 | e->target_edge = WLR_EDGE_NONE; | 192 | e->target_edge = WLR_EDGE_NONE; |
126 | return; | 193 | return; |
127 | } | 194 | } |
128 | 195 | ||
196 | struct wlr_box drop_box = { | ||
197 | .x = con->pending.content_x, | ||
198 | .y = con->pending.content_y, | ||
199 | .width = con->pending.content_width, | ||
200 | .height = con->pending.content_height, | ||
201 | }; | ||
202 | |||
203 | // Check if the cursor is over a tilebar only if the destination | ||
204 | // container is not a descendant of the source container. | ||
205 | if (!surface && !container_has_ancestor(con, e->con) && | ||
206 | split_titlebar(node, e->con, cursor->cursor, | ||
207 | &drop_box, &e->insert_after_target)) { | ||
208 | // Don't allow dropping over the source container's titlebar | ||
209 | // to give users a chance to cancel a drag operation. | ||
210 | if (con == e->con) { | ||
211 | e->target_node = NULL; | ||
212 | } else { | ||
213 | e->target_node = node; | ||
214 | e->split_target = true; | ||
215 | } | ||
216 | e->target_edge = WLR_EDGE_NONE; | ||
217 | update_indicator(e, &drop_box); | ||
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,10 @@ 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 | update_indicator(e, &box); |
159 | resize_box(&e->drop_box, edge, DROP_LAYOUT_BORDER); | ||
160 | desktop_damage_box(&e->drop_box); | ||
161 | return; | 260 | return; |
162 | } | 261 | } |
163 | con = con->parent; | 262 | con = con->pending.parent; |
164 | } | 263 | } |
165 | 264 | ||
166 | // Use the hovered view - but we must be over the actual surface | 265 | // Use the hovered view - but we must be over the actual surface |
@@ -173,23 +272,23 @@ static void handle_motion_postthreshold(struct sway_seat *seat) { | |||
173 | } | 272 | } |
174 | 273 | ||
175 | // Find the closest edge | 274 | // Find the closest edge |
176 | size_t thickness = fmin(con->content_width, con->content_height) * 0.3; | 275 | size_t thickness = fmin(con->pending.content_width, con->pending.content_height) * 0.3; |
177 | size_t closest_dist = INT_MAX; | 276 | size_t closest_dist = INT_MAX; |
178 | size_t dist; | 277 | size_t dist; |
179 | e->target_edge = WLR_EDGE_NONE; | 278 | e->target_edge = WLR_EDGE_NONE; |
180 | if ((dist = cursor->cursor->y - con->y) < closest_dist) { | 279 | if ((dist = cursor->cursor->y - con->pending.y) < closest_dist) { |
181 | closest_dist = dist; | 280 | closest_dist = dist; |
182 | e->target_edge = WLR_EDGE_TOP; | 281 | e->target_edge = WLR_EDGE_TOP; |
183 | } | 282 | } |
184 | if ((dist = cursor->cursor->x - con->x) < closest_dist) { | 283 | if ((dist = cursor->cursor->x - con->pending.x) < closest_dist) { |
185 | closest_dist = dist; | 284 | closest_dist = dist; |
186 | e->target_edge = WLR_EDGE_LEFT; | 285 | e->target_edge = WLR_EDGE_LEFT; |
187 | } | 286 | } |
188 | if ((dist = con->x + con->width - cursor->cursor->x) < closest_dist) { | 287 | if ((dist = con->pending.x + con->pending.width - cursor->cursor->x) < closest_dist) { |
189 | closest_dist = dist; | 288 | closest_dist = dist; |
190 | e->target_edge = WLR_EDGE_RIGHT; | 289 | e->target_edge = WLR_EDGE_RIGHT; |
191 | } | 290 | } |
192 | if ((dist = con->y + con->height - cursor->cursor->y) < closest_dist) { | 291 | if ((dist = con->pending.y + con->pending.height - cursor->cursor->y) < closest_dist) { |
193 | closest_dist = dist; | 292 | closest_dist = dist; |
194 | e->target_edge = WLR_EDGE_BOTTOM; | 293 | e->target_edge = WLR_EDGE_BOTTOM; |
195 | } | 294 | } |
@@ -199,12 +298,8 @@ static void handle_motion_postthreshold(struct sway_seat *seat) { | |||
199 | } | 298 | } |
200 | 299 | ||
201 | e->target_node = node; | 300 | e->target_node = node; |
202 | e->drop_box.x = con->content_x; | 301 | resize_box(&drop_box, e->target_edge, thickness); |
203 | e->drop_box.y = con->content_y; | 302 | update_indicator(e, &drop_box); |
204 | e->drop_box.width = con->content_width; | ||
205 | e->drop_box.height = con->content_height; | ||
206 | resize_box(&e->drop_box, e->target_edge, thickness); | ||
207 | desktop_damage_box(&e->drop_box); | ||
208 | } | 303 | } |
209 | 304 | ||
210 | static void handle_pointer_motion(struct sway_seat *seat, uint32_t time_msec) { | 305 | static void handle_pointer_motion(struct sway_seat *seat, uint32_t time_msec) { |
@@ -214,6 +309,7 @@ static void handle_pointer_motion(struct sway_seat *seat, uint32_t time_msec) { | |||
214 | } else { | 309 | } else { |
215 | handle_motion_prethreshold(seat); | 310 | handle_motion_prethreshold(seat); |
216 | } | 311 | } |
312 | transaction_commit_dirty(); | ||
217 | } | 313 | } |
218 | 314 | ||
219 | static bool is_parallel(enum sway_container_layout layout, | 315 | static bool is_parallel(enum sway_container_layout layout, |
@@ -232,14 +328,15 @@ static void finalize_move(struct sway_seat *seat) { | |||
232 | } | 328 | } |
233 | 329 | ||
234 | struct sway_container *con = e->con; | 330 | struct sway_container *con = e->con; |
235 | struct sway_container *old_parent = con->parent; | 331 | struct sway_container *old_parent = con->pending.parent; |
236 | struct sway_workspace *old_ws = con->workspace; | 332 | struct sway_workspace *old_ws = con->pending.workspace; |
237 | struct sway_node *target_node = e->target_node; | 333 | struct sway_node *target_node = e->target_node; |
238 | struct sway_workspace *new_ws = target_node->type == N_WORKSPACE ? | 334 | struct sway_workspace *new_ws = target_node->type == N_WORKSPACE ? |
239 | target_node->sway_workspace : target_node->sway_container->workspace; | 335 | target_node->sway_workspace : target_node->sway_container->pending.workspace; |
240 | enum wlr_edges edge = e->target_edge; | 336 | enum wlr_edges edge = e->target_edge; |
241 | int after = edge != WLR_EDGE_TOP && edge != WLR_EDGE_LEFT; | 337 | int after = edge != WLR_EDGE_TOP && edge != WLR_EDGE_LEFT; |
242 | bool swap = edge == WLR_EDGE_NONE && target_node->type == N_CONTAINER; | 338 | bool swap = edge == WLR_EDGE_NONE && target_node->type == N_CONTAINER && |
339 | !e->split_target; | ||
243 | 340 | ||
244 | if (!swap) { | 341 | if (!swap) { |
245 | container_detach(con); | 342 | container_detach(con); |
@@ -248,6 +345,14 @@ static void finalize_move(struct sway_seat *seat) { | |||
248 | // Moving container into empty workspace | 345 | // Moving container into empty workspace |
249 | if (target_node->type == N_WORKSPACE && edge == WLR_EDGE_NONE) { | 346 | if (target_node->type == N_WORKSPACE && edge == WLR_EDGE_NONE) { |
250 | con = workspace_add_tiling(new_ws, con); | 347 | con = workspace_add_tiling(new_ws, con); |
348 | } else if (e->split_target) { | ||
349 | struct sway_container *target = target_node->sway_container; | ||
350 | enum sway_container_layout layout = container_parent_layout(target); | ||
351 | if (layout != L_TABBED && layout != L_STACKED) { | ||
352 | container_split(target, L_TABBED); | ||
353 | } | ||
354 | container_add_sibling(target, con, e->insert_after_target); | ||
355 | ipc_event_window(con, "move"); | ||
251 | } else if (target_node->type == N_CONTAINER) { | 356 | } else if (target_node->type == N_CONTAINER) { |
252 | // Moving container before/after another | 357 | // Moving container before/after another |
253 | struct sway_container *target = target_node->sway_container; | 358 | struct sway_container *target = target_node->sway_container; |
@@ -283,8 +388,8 @@ static void finalize_move(struct sway_seat *seat) { | |||
283 | int index = list_find(siblings, con); | 388 | int index = list_find(siblings, con); |
284 | struct sway_container *sibling = index == 0 ? | 389 | struct sway_container *sibling = index == 0 ? |
285 | siblings->items[1] : siblings->items[index - 1]; | 390 | siblings->items[1] : siblings->items[index - 1]; |
286 | con->width = sibling->width; | 391 | con->pending.width = sibling->pending.width; |
287 | con->height = sibling->height; | 392 | con->pending.height = sibling->pending.height; |
288 | con->width_fraction = sibling->width_fraction; | 393 | con->width_fraction = sibling->width_fraction; |
289 | con->height_fraction = sibling->height_fraction; | 394 | con->height_fraction = sibling->height_fraction; |
290 | } | 395 | } |
@@ -294,12 +399,13 @@ static void finalize_move(struct sway_seat *seat) { | |||
294 | arrange_workspace(new_ws); | 399 | arrange_workspace(new_ws); |
295 | } | 400 | } |
296 | 401 | ||
402 | transaction_commit_dirty(); | ||
297 | seatop_begin_default(seat); | 403 | seatop_begin_default(seat); |
298 | } | 404 | } |
299 | 405 | ||
300 | static void handle_button(struct sway_seat *seat, uint32_t time_msec, | 406 | static void handle_button(struct sway_seat *seat, uint32_t time_msec, |
301 | struct wlr_input_device *device, uint32_t button, | 407 | struct wlr_input_device *device, uint32_t button, |
302 | enum wlr_button_state state) { | 408 | enum wl_pointer_button_state state) { |
303 | if (seat->cursor->pressed_button_count == 0) { | 409 | if (seat->cursor->pressed_button_count == 0) { |
304 | finalize_move(seat); | 410 | finalize_move(seat); |
305 | } | 411 | } |
@@ -328,7 +434,7 @@ static const struct sway_seatop_impl seatop_impl = { | |||
328 | .pointer_motion = handle_pointer_motion, | 434 | .pointer_motion = handle_pointer_motion, |
329 | .tablet_tool_tip = handle_tablet_tool_tip, | 435 | .tablet_tool_tip = handle_tablet_tool_tip, |
330 | .unref = handle_unref, | 436 | .unref = handle_unref, |
331 | .render = handle_render, | 437 | .end = handle_end, |
332 | }; | 438 | }; |
333 | 439 | ||
334 | void seatop_begin_move_tiling_threshold(struct sway_seat *seat, | 440 | void seatop_begin_move_tiling_threshold(struct sway_seat *seat, |
@@ -340,6 +446,20 @@ void seatop_begin_move_tiling_threshold(struct sway_seat *seat, | |||
340 | if (!e) { | 446 | if (!e) { |
341 | return; | 447 | return; |
342 | } | 448 | } |
449 | |||
450 | const float *indicator = config->border_colors.focused.indicator; | ||
451 | float color[4] = { | ||
452 | indicator[0] * .5, | ||
453 | indicator[1] * .5, | ||
454 | indicator[2] * .5, | ||
455 | indicator[3] * .5, | ||
456 | }; | ||
457 | e->indicator_rect = wlr_scene_rect_create(seat->scene_tree, 0, 0, color); | ||
458 | if (!e->indicator_rect) { | ||
459 | free(e); | ||
460 | return; | ||
461 | } | ||
462 | |||
343 | e->con = con; | 463 | e->con = con; |
344 | e->ref_lx = seat->cursor->cursor->x; | 464 | e->ref_lx = seat->cursor->cursor->x; |
345 | e->ref_ly = seat->cursor->cursor->y; | 465 | e->ref_ly = seat->cursor->cursor->y; |
@@ -348,6 +468,7 @@ void seatop_begin_move_tiling_threshold(struct sway_seat *seat, | |||
348 | seat->seatop_data = e; | 468 | seat->seatop_data = e; |
349 | 469 | ||
350 | container_raise_floating(con); | 470 | container_raise_floating(con); |
471 | transaction_commit_dirty(); | ||
351 | wlr_seat_pointer_notify_clear_focus(seat->wlr_seat); | 472 | wlr_seat_pointer_notify_clear_focus(seat->wlr_seat); |
352 | } | 473 | } |
353 | 474 | ||
diff --git a/sway/input/seatop_resize_floating.c b/sway/input/seatop_resize_floating.c index 5da22e47..bec86e33 100644 --- a/sway/input/seatop_resize_floating.c +++ b/sway/input/seatop_resize_floating.c | |||
@@ -1,7 +1,7 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <limits.h> | 1 | #include <limits.h> |
3 | #include <wlr/types/wlr_cursor.h> | 2 | #include <wlr/types/wlr_cursor.h> |
4 | #include <wlr/types/wlr_xcursor_manager.h> | 3 | #include <wlr/types/wlr_xcursor_manager.h> |
4 | #include "sway/desktop/transaction.h" | ||
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/arrange.h" | 7 | #include "sway/tree/arrange.h" |
@@ -20,13 +20,14 @@ struct seatop_resize_floating_event { | |||
20 | 20 | ||
21 | static void handle_button(struct sway_seat *seat, uint32_t time_msec, | 21 | static void handle_button(struct sway_seat *seat, uint32_t time_msec, |
22 | struct wlr_input_device *device, uint32_t button, | 22 | struct wlr_input_device *device, uint32_t button, |
23 | enum wlr_button_state state) { | 23 | enum wl_pointer_button_state state) { |
24 | struct seatop_resize_floating_event *e = seat->seatop_data; | 24 | struct seatop_resize_floating_event *e = seat->seatop_data; |
25 | struct sway_container *con = e->con; | 25 | struct sway_container *con = e->con; |
26 | 26 | ||
27 | if (seat->cursor->pressed_button_count == 0) { | 27 | if (seat->cursor->pressed_button_count == 0) { |
28 | container_set_resizing(con, false); | 28 | container_set_resizing(con, false); |
29 | arrange_container(con); // Send configure w/o resizing hint | 29 | arrange_container(con); // Send configure w/o resizing hint |
30 | transaction_commit_dirty(); | ||
30 | seatop_begin_default(seat); | 31 | seatop_begin_default(seat); |
31 | } | 32 | } |
32 | } | 33 | } |
@@ -78,17 +79,25 @@ static void handle_pointer_motion(struct sway_seat *seat, uint32_t time_msec) { | |||
78 | double height = e->ref_height + grow_height; | 79 | double height = e->ref_height + grow_height; |
79 | int min_width, max_width, min_height, max_height; | 80 | int min_width, max_width, min_height, max_height; |
80 | floating_calculate_constraints(&min_width, &max_width, | 81 | floating_calculate_constraints(&min_width, &max_width, |
81 | &min_height, &max_height); | 82 | &min_height, &max_height); |
82 | width = fmax(min_width + border_width, fmin(width, max_width)); | 83 | width = fmin(width, max_width - border_width); |
83 | height = fmax(min_height + border_height, fmin(height, max_height)); | 84 | width = fmax(width, min_width + border_width); |
85 | width = fmax(width, 1); | ||
86 | height = fmin(height, max_height - border_height); | ||
87 | height = fmax(height, min_height + border_height); | ||
88 | height = fmax(height, 1); | ||
84 | 89 | ||
85 | // Apply the view's min/max size | 90 | // Apply the view's min/max size |
86 | if (con->view) { | 91 | if (con->view) { |
87 | double view_min_width, view_max_width, view_min_height, view_max_height; | 92 | 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, | 93 | view_get_constraints(con->view, &view_min_width, &view_max_width, |
89 | &view_min_height, &view_max_height); | 94 | &view_min_height, &view_max_height); |
90 | width = fmax(view_min_width + border_width, fmin(width, view_max_width)); | 95 | width = fmin(width, view_max_width - border_width); |
91 | height = fmax(view_min_height + border_height, fmin(height, view_max_height)); | 96 | width = fmax(width, view_min_width + border_width); |
97 | width = fmax(width, 1); | ||
98 | height = fmin(height, view_max_height - border_height); | ||
99 | height = fmax(height, view_min_height + border_height); | ||
100 | height = fmax(height, 1); | ||
92 | 101 | ||
93 | } | 102 | } |
94 | 103 | ||
@@ -116,23 +125,24 @@ static void handle_pointer_motion(struct sway_seat *seat, uint32_t time_msec) { | |||
116 | 125 | ||
117 | // Determine the amounts we need to bump everything relative to the current | 126 | // Determine the amounts we need to bump everything relative to the current |
118 | // size. | 127 | // size. |
119 | int relative_grow_width = width - con->width; | 128 | int relative_grow_width = width - con->pending.width; |
120 | int relative_grow_height = height - con->height; | 129 | int relative_grow_height = height - con->pending.height; |
121 | int relative_grow_x = (e->ref_con_lx + grow_x) - con->x; | 130 | 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; | 131 | int relative_grow_y = (e->ref_con_ly + grow_y) - con->pending.y; |
123 | 132 | ||
124 | // Actually resize stuff | 133 | // Actually resize stuff |
125 | con->x += relative_grow_x; | 134 | con->pending.x += relative_grow_x; |
126 | con->y += relative_grow_y; | 135 | con->pending.y += relative_grow_y; |
127 | con->width += relative_grow_width; | 136 | con->pending.width += relative_grow_width; |
128 | con->height += relative_grow_height; | 137 | con->pending.height += relative_grow_height; |
129 | 138 | ||
130 | con->content_x += relative_grow_x; | 139 | con->pending.content_x += relative_grow_x; |
131 | con->content_y += relative_grow_y; | 140 | con->pending.content_y += relative_grow_y; |
132 | con->content_width += relative_grow_width; | 141 | con->pending.content_width += relative_grow_width; |
133 | con->content_height += relative_grow_height; | 142 | con->pending.content_height += relative_grow_height; |
134 | 143 | ||
135 | arrange_container(con); | 144 | arrange_container(con); |
145 | transaction_commit_dirty(); | ||
136 | } | 146 | } |
137 | 147 | ||
138 | static void handle_unref(struct sway_seat *seat, struct sway_container *con) { | 148 | static void handle_unref(struct sway_seat *seat, struct sway_container *con) { |
@@ -166,16 +176,17 @@ void seatop_begin_resize_floating(struct sway_seat *seat, | |||
166 | e->edge = edge == WLR_EDGE_NONE ? WLR_EDGE_BOTTOM | WLR_EDGE_RIGHT : edge; | 176 | e->edge = edge == WLR_EDGE_NONE ? WLR_EDGE_BOTTOM | WLR_EDGE_RIGHT : edge; |
167 | e->ref_lx = seat->cursor->cursor->x; | 177 | e->ref_lx = seat->cursor->cursor->x; |
168 | e->ref_ly = seat->cursor->cursor->y; | 178 | e->ref_ly = seat->cursor->cursor->y; |
169 | e->ref_con_lx = con->x; | 179 | e->ref_con_lx = con->pending.x; |
170 | e->ref_con_ly = con->y; | 180 | e->ref_con_ly = con->pending.y; |
171 | e->ref_width = con->width; | 181 | e->ref_width = con->pending.width; |
172 | e->ref_height = con->height; | 182 | e->ref_height = con->pending.height; |
173 | 183 | ||
174 | seat->seatop_impl = &seatop_impl; | 184 | seat->seatop_impl = &seatop_impl; |
175 | seat->seatop_data = e; | 185 | seat->seatop_data = e; |
176 | 186 | ||
177 | container_set_resizing(con, true); | 187 | container_set_resizing(con, true); |
178 | container_raise_floating(con); | 188 | container_raise_floating(con); |
189 | transaction_commit_dirty(); | ||
179 | 190 | ||
180 | const char *image = edge == WLR_EDGE_NONE ? | 191 | const char *image = edge == WLR_EDGE_NONE ? |
181 | "se-resize" : wlr_xcursor_get_resize_name(edge); | 192 | "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..15fd333b 100644 --- a/sway/input/seatop_resize_tiling.c +++ b/sway/input/seatop_resize_tiling.c | |||
@@ -1,7 +1,7 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <wlr/types/wlr_cursor.h> | 1 | #include <wlr/types/wlr_cursor.h> |
3 | #include <wlr/util/edges.h> | 2 | #include <wlr/util/edges.h> |
4 | #include "sway/commands.h" | 3 | #include "sway/commands.h" |
4 | #include "sway/desktop/transaction.h" | ||
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/arrange.h" | 7 | #include "sway/tree/arrange.h" |
@@ -45,28 +45,29 @@ static struct sway_container *container_get_resize_sibling( | |||
45 | 45 | ||
46 | static void handle_button(struct sway_seat *seat, uint32_t time_msec, | 46 | static void handle_button(struct sway_seat *seat, uint32_t time_msec, |
47 | struct wlr_input_device *device, uint32_t button, | 47 | struct wlr_input_device *device, uint32_t button, |
48 | enum wlr_button_state state) { | 48 | enum wl_pointer_button_state state) { |
49 | struct seatop_resize_tiling_event *e = seat->seatop_data; | 49 | struct seatop_resize_tiling_event *e = seat->seatop_data; |
50 | 50 | ||
51 | if (seat->cursor->pressed_button_count == 0) { | 51 | if (seat->cursor->pressed_button_count == 0) { |
52 | if (e->h_con) { | 52 | if (e->h_con) { |
53 | container_set_resizing(e->h_con, false); | 53 | container_set_resizing(e->h_con, false); |
54 | container_set_resizing(e->h_sib, false); | 54 | container_set_resizing(e->h_sib, false); |
55 | if (e->h_con->parent) { | 55 | if (e->h_con->pending.parent) { |
56 | arrange_container(e->h_con->parent); | 56 | arrange_container(e->h_con->pending.parent); |
57 | } else { | 57 | } else { |
58 | arrange_workspace(e->h_con->workspace); | 58 | arrange_workspace(e->h_con->pending.workspace); |
59 | } | 59 | } |
60 | } | 60 | } |
61 | if (e->v_con) { | 61 | if (e->v_con) { |
62 | container_set_resizing(e->v_con, false); | 62 | container_set_resizing(e->v_con, false); |
63 | container_set_resizing(e->v_sib, false); | 63 | container_set_resizing(e->v_sib, false); |
64 | if (e->v_con->parent) { | 64 | if (e->v_con->pending.parent) { |
65 | arrange_container(e->v_con->parent); | 65 | arrange_container(e->v_con->pending.parent); |
66 | } else { | 66 | } else { |
67 | arrange_workspace(e->v_con->workspace); | 67 | arrange_workspace(e->v_con->pending.workspace); |
68 | } | 68 | } |
69 | } | 69 | } |
70 | transaction_commit_dirty(); | ||
70 | seatop_begin_default(seat); | 71 | seatop_begin_default(seat); |
71 | } | 72 | } |
72 | } | 73 | } |
@@ -80,16 +81,16 @@ static void handle_pointer_motion(struct sway_seat *seat, uint32_t time_msec) { | |||
80 | 81 | ||
81 | if (e->h_con) { | 82 | if (e->h_con) { |
82 | if (e->edge & WLR_EDGE_LEFT) { | 83 | if (e->edge & WLR_EDGE_LEFT) { |
83 | amount_x = (e->h_con_orig_width - moved_x) - e->h_con->width; | 84 | amount_x = (e->h_con_orig_width - moved_x) - e->h_con->pending.width; |
84 | } else if (e->edge & WLR_EDGE_RIGHT) { | 85 | } else if (e->edge & WLR_EDGE_RIGHT) { |
85 | amount_x = (e->h_con_orig_width + moved_x) - e->h_con->width; | 86 | amount_x = (e->h_con_orig_width + moved_x) - e->h_con->pending.width; |
86 | } | 87 | } |
87 | } | 88 | } |
88 | if (e->v_con) { | 89 | if (e->v_con) { |
89 | if (e->edge & WLR_EDGE_TOP) { | 90 | if (e->edge & WLR_EDGE_TOP) { |
90 | amount_y = (e->v_con_orig_height - moved_y) - e->v_con->height; | 91 | amount_y = (e->v_con_orig_height - moved_y) - e->v_con->pending.height; |
91 | } else if (e->edge & WLR_EDGE_BOTTOM) { | 92 | } else if (e->edge & WLR_EDGE_BOTTOM) { |
92 | amount_y = (e->v_con_orig_height + moved_y) - e->v_con->height; | 93 | amount_y = (e->v_con_orig_height + moved_y) - e->v_con->pending.height; |
93 | } | 94 | } |
94 | } | 95 | } |
95 | 96 | ||
@@ -99,6 +100,7 @@ static void handle_pointer_motion(struct sway_seat *seat, uint32_t time_msec) { | |||
99 | if (amount_y != 0) { | 100 | if (amount_y != 0) { |
100 | container_resize_tiled(e->v_con, e->edge_y, amount_y); | 101 | container_resize_tiled(e->v_con, e->edge_y, amount_y); |
101 | } | 102 | } |
103 | transaction_commit_dirty(); | ||
102 | } | 104 | } |
103 | 105 | ||
104 | static void handle_unref(struct sway_seat *seat, struct sway_container *con) { | 106 | static void handle_unref(struct sway_seat *seat, struct sway_container *con) { |
@@ -140,7 +142,7 @@ void seatop_begin_resize_tiling(struct sway_seat *seat, | |||
140 | if (e->h_con) { | 142 | if (e->h_con) { |
141 | container_set_resizing(e->h_con, true); | 143 | container_set_resizing(e->h_con, true); |
142 | container_set_resizing(e->h_sib, true); | 144 | container_set_resizing(e->h_sib, true); |
143 | e->h_con_orig_width = e->h_con->width; | 145 | e->h_con_orig_width = e->h_con->pending.width; |
144 | } | 146 | } |
145 | } | 147 | } |
146 | if (edge & (WLR_EDGE_TOP | WLR_EDGE_BOTTOM)) { | 148 | if (edge & (WLR_EDGE_TOP | WLR_EDGE_BOTTOM)) { |
@@ -151,12 +153,13 @@ void seatop_begin_resize_tiling(struct sway_seat *seat, | |||
151 | if (e->v_con) { | 153 | if (e->v_con) { |
152 | container_set_resizing(e->v_con, true); | 154 | container_set_resizing(e->v_con, true); |
153 | container_set_resizing(e->v_sib, true); | 155 | container_set_resizing(e->v_sib, true); |
154 | e->v_con_orig_height = e->v_con->height; | 156 | e->v_con_orig_height = e->v_con->pending.height; |
155 | } | 157 | } |
156 | } | 158 | } |
157 | 159 | ||
158 | seat->seatop_impl = &seatop_impl; | 160 | seat->seatop_impl = &seatop_impl; |
159 | seat->seatop_data = e; | 161 | seat->seatop_data = e; |
160 | 162 | ||
163 | transaction_commit_dirty(); | ||
161 | wlr_seat_pointer_notify_clear_focus(seat->wlr_seat); | 164 | wlr_seat_pointer_notify_clear_focus(seat->wlr_seat); |
162 | } | 165 | } |
diff --git a/sway/input/switch.c b/sway/input/switch.c index b7c28df1..831f4dbf 100644 --- a/sway/input/switch.c +++ b/sway/input/switch.c | |||
@@ -1,7 +1,5 @@ | |||
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> | ||
5 | #include "log.h" | 3 | #include "log.h" |
6 | 4 | ||
7 | struct sway_switch *sway_switch_create(struct sway_seat *seat, | 5 | struct sway_switch *sway_switch_create(struct sway_seat *seat, |
@@ -12,6 +10,7 @@ struct sway_switch *sway_switch_create(struct sway_seat *seat, | |||
12 | return NULL; | 10 | return NULL; |
13 | } | 11 | } |
14 | device->switch_device = switch_device; | 12 | device->switch_device = switch_device; |
13 | switch_device->wlr = wlr_switch_from_input_device(device->input_device->wlr_device); | ||
15 | switch_device->seat_device = device; | 14 | switch_device->seat_device = device; |
16 | switch_device->state = WLR_SWITCH_STATE_OFF; | 15 | switch_device->state = WLR_SWITCH_STATE_OFF; |
17 | wl_list_init(&switch_device->switch_toggle.link); | 16 | wl_list_init(&switch_device->switch_toggle.link); |
@@ -20,9 +19,22 @@ struct sway_switch *sway_switch_create(struct sway_seat *seat, | |||
20 | return switch_device; | 19 | return switch_device; |
21 | } | 20 | } |
22 | 21 | ||
22 | static bool sway_switch_trigger_test(enum sway_switch_trigger trigger, | ||
23 | enum wlr_switch_state state) { | ||
24 | switch (trigger) { | ||
25 | case SWAY_SWITCH_TRIGGER_ON: | ||
26 | return state == WLR_SWITCH_STATE_ON; | ||
27 | case SWAY_SWITCH_TRIGGER_OFF: | ||
28 | return state == WLR_SWITCH_STATE_OFF; | ||
29 | case SWAY_SWITCH_TRIGGER_TOGGLE: | ||
30 | return true; | ||
31 | } | ||
32 | abort(); // unreachable | ||
33 | } | ||
34 | |||
23 | static void execute_binding(struct sway_switch *sway_switch) { | 35 | static void execute_binding(struct sway_switch *sway_switch) { |
24 | struct sway_seat* seat = sway_switch->seat_device->sway_seat; | 36 | struct sway_seat *seat = sway_switch->seat_device->sway_seat; |
25 | bool input_inhibited = seat->exclusive_client != NULL; | 37 | bool locked = server.session_lock.lock; |
26 | 38 | ||
27 | list_t *bindings = config->current_mode->switch_bindings; | 39 | list_t *bindings = config->current_mode->switch_bindings; |
28 | struct sway_switch_binding *matched_binding = NULL; | 40 | struct sway_switch_binding *matched_binding = NULL; |
@@ -31,22 +43,21 @@ static void execute_binding(struct sway_switch *sway_switch) { | |||
31 | if (binding->type != sway_switch->type) { | 43 | if (binding->type != sway_switch->type) { |
32 | continue; | 44 | continue; |
33 | } | 45 | } |
34 | if (binding->state != WLR_SWITCH_STATE_TOGGLE && | 46 | if (!sway_switch_trigger_test(binding->trigger, sway_switch->state)) { |
35 | binding->state != sway_switch->state) { | ||
36 | continue; | 47 | continue; |
37 | } | 48 | } |
38 | if (config->reloading && (binding->state == WLR_SWITCH_STATE_TOGGLE | 49 | if (config->reloading && (binding->trigger == SWAY_SWITCH_TRIGGER_TOGGLE |
39 | || (binding->flags & BINDING_RELOAD) == 0)) { | 50 | || (binding->flags & BINDING_RELOAD) == 0)) { |
40 | continue; | 51 | continue; |
41 | } | 52 | } |
42 | bool binding_locked = binding->flags & BINDING_LOCKED; | 53 | bool binding_locked = binding->flags & BINDING_LOCKED; |
43 | if (!binding_locked && input_inhibited) { | 54 | if (!binding_locked && locked) { |
44 | continue; | 55 | continue; |
45 | } | 56 | } |
46 | 57 | ||
47 | matched_binding = binding; | 58 | matched_binding = binding; |
48 | 59 | ||
49 | if (binding_locked == input_inhibited) { | 60 | if (binding_locked == locked) { |
50 | break; | 61 | break; |
51 | } | 62 | } |
52 | } | 63 | } |
@@ -61,15 +72,12 @@ static void execute_binding(struct sway_switch *sway_switch) { | |||
61 | seat_execute_command(seat, dummy_binding); | 72 | seat_execute_command(seat, dummy_binding); |
62 | free(dummy_binding); | 73 | free(dummy_binding); |
63 | } | 74 | } |
64 | |||
65 | transaction_commit_dirty(); | ||
66 | |||
67 | } | 75 | } |
68 | 76 | ||
69 | static void handle_switch_toggle(struct wl_listener *listener, void *data) { | 77 | static void handle_switch_toggle(struct wl_listener *listener, void *data) { |
70 | struct sway_switch *sway_switch = | 78 | struct sway_switch *sway_switch = |
71 | wl_container_of(listener, sway_switch, switch_toggle); | 79 | wl_container_of(listener, sway_switch, switch_toggle); |
72 | struct wlr_event_switch_toggle *event = data; | 80 | struct wlr_switch_toggle_event *event = data; |
73 | struct sway_seat *seat = sway_switch->seat_device->sway_seat; | 81 | struct sway_seat *seat = sway_switch->seat_device->sway_seat; |
74 | seat_idle_notify_activity(seat, IDLE_SOURCE_SWITCH); | 82 | seat_idle_notify_activity(seat, IDLE_SOURCE_SWITCH); |
75 | 83 | ||
@@ -86,10 +94,8 @@ static void handle_switch_toggle(struct wl_listener *listener, void *data) { | |||
86 | } | 94 | } |
87 | 95 | ||
88 | void sway_switch_configure(struct sway_switch *sway_switch) { | 96 | 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); | 97 | wl_list_remove(&sway_switch->switch_toggle.link); |
92 | wl_signal_add(&wlr_device->switch_device->events.toggle, | 98 | wl_signal_add(&sway_switch->wlr->events.toggle, |
93 | &sway_switch->switch_toggle); | 99 | &sway_switch->switch_toggle); |
94 | sway_switch->switch_toggle.notify = handle_switch_toggle; | 100 | sway_switch->switch_toggle.notify = handle_switch_toggle; |
95 | sway_log(SWAY_DEBUG, "Configured switch for device"); | 101 | sway_log(SWAY_DEBUG, "Configured switch for device"); |
diff --git a/sway/input/tablet.c b/sway/input/tablet.c index 26e86e36..2863642a 100644 --- a/sway/input/tablet.c +++ b/sway/input/tablet.c | |||
@@ -1,6 +1,5 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <stdlib.h> | 1 | #include <stdlib.h> |
3 | #include <wlr/backend/libinput.h> | 2 | #include <wlr/config.h> |
4 | #include <wlr/types/wlr_tablet_v2.h> | 3 | #include <wlr/types/wlr_tablet_v2.h> |
5 | #include <wlr/types/wlr_tablet_tool.h> | 4 | #include <wlr/types/wlr_tablet_tool.h> |
6 | #include <wlr/types/wlr_tablet_pad.h> | 5 | #include <wlr/types/wlr_tablet_pad.h> |
@@ -9,6 +8,10 @@ | |||
9 | #include "sway/input/seat.h" | 8 | #include "sway/input/seat.h" |
10 | #include "sway/input/tablet.h" | 9 | #include "sway/input/tablet.h" |
11 | 10 | ||
11 | #if WLR_HAS_LIBINPUT_BACKEND | ||
12 | #include <wlr/backend/libinput.h> | ||
13 | #endif | ||
14 | |||
12 | static void handle_pad_tablet_destroy(struct wl_listener *listener, void *data) { | 15 | static void handle_pad_tablet_destroy(struct wl_listener *listener, void *data) { |
13 | struct sway_tablet_pad *pad = | 16 | struct sway_tablet_pad *pad = |
14 | wl_container_of(listener, pad, tablet_destroy); | 17 | wl_container_of(listener, pad, tablet_destroy); |
@@ -54,15 +57,14 @@ void sway_configure_tablet(struct sway_tablet *tablet) { | |||
54 | tablet->seat_device->input_device->wlr_device; | 57 | tablet->seat_device->input_device->wlr_device; |
55 | struct sway_seat *seat = tablet->seat_device->sway_seat; | 58 | struct sway_seat *seat = tablet->seat_device->sway_seat; |
56 | 59 | ||
57 | if ((seat->wlr_seat->capabilities & WL_SEAT_CAPABILITY_POINTER) == 0) { | 60 | seat_configure_xcursor(seat); |
58 | seat_configure_xcursor(seat); | ||
59 | } | ||
60 | 61 | ||
61 | if (!tablet->tablet_v2) { | 62 | if (!tablet->tablet_v2) { |
62 | tablet->tablet_v2 = | 63 | tablet->tablet_v2 = |
63 | wlr_tablet_create(server.tablet_v2, seat->wlr_seat, device); | 64 | wlr_tablet_create(server.tablet_v2, seat->wlr_seat, device); |
64 | } | 65 | } |
65 | 66 | ||
67 | #if WLR_HAS_LIBINPUT_BACKEND | ||
66 | /* Search for a sibling tablet pad */ | 68 | /* Search for a sibling tablet pad */ |
67 | if (!wlr_input_device_is_libinput(device)) { | 69 | if (!wlr_input_device_is_libinput(device)) { |
68 | /* We can only do this on libinput devices */ | 70 | /* We can only do this on libinput devices */ |
@@ -87,6 +89,7 @@ void sway_configure_tablet(struct sway_tablet *tablet) { | |||
87 | break; | 89 | break; |
88 | } | 90 | } |
89 | } | 91 | } |
92 | #endif | ||
90 | } | 93 | } |
91 | 94 | ||
92 | void sway_tablet_destroy(struct sway_tablet *tablet) { | 95 | void sway_tablet_destroy(struct sway_tablet *tablet) { |
@@ -196,7 +199,7 @@ static void handle_tablet_pad_attach(struct wl_listener *listener, | |||
196 | 199 | ||
197 | static void handle_tablet_pad_ring(struct wl_listener *listener, void *data) { | 200 | static void handle_tablet_pad_ring(struct wl_listener *listener, void *data) { |
198 | struct sway_tablet_pad *pad = wl_container_of(listener, pad, ring); | 201 | struct sway_tablet_pad *pad = wl_container_of(listener, pad, ring); |
199 | struct wlr_event_tablet_pad_ring *event = data; | 202 | struct wlr_tablet_pad_ring_event *event = data; |
200 | 203 | ||
201 | if (!pad->current_surface) { | 204 | if (!pad->current_surface) { |
202 | return; | 205 | return; |
@@ -210,7 +213,7 @@ static void handle_tablet_pad_ring(struct wl_listener *listener, void *data) { | |||
210 | 213 | ||
211 | static void handle_tablet_pad_strip(struct wl_listener *listener, void *data) { | 214 | static void handle_tablet_pad_strip(struct wl_listener *listener, void *data) { |
212 | struct sway_tablet_pad *pad = wl_container_of(listener, pad, strip); | 215 | struct sway_tablet_pad *pad = wl_container_of(listener, pad, strip); |
213 | struct wlr_event_tablet_pad_strip *event = data; | 216 | struct wlr_tablet_pad_strip_event *event = data; |
214 | 217 | ||
215 | if (!pad->current_surface) { | 218 | if (!pad->current_surface) { |
216 | return; | 219 | return; |
@@ -224,7 +227,7 @@ static void handle_tablet_pad_strip(struct wl_listener *listener, void *data) { | |||
224 | 227 | ||
225 | static void handle_tablet_pad_button(struct wl_listener *listener, void *data) { | 228 | static void handle_tablet_pad_button(struct wl_listener *listener, void *data) { |
226 | struct sway_tablet_pad *pad = wl_container_of(listener, pad, button); | 229 | struct sway_tablet_pad *pad = wl_container_of(listener, pad, button); |
227 | struct wlr_event_tablet_pad_button *event = data; | 230 | struct wlr_tablet_pad_button_event *event = data; |
228 | 231 | ||
229 | if (!pad->current_surface) { | 232 | if (!pad->current_surface) { |
230 | return; | 233 | return; |
@@ -246,6 +249,7 @@ struct sway_tablet_pad *sway_tablet_pad_create(struct sway_seat *seat, | |||
246 | return NULL; | 249 | return NULL; |
247 | } | 250 | } |
248 | 251 | ||
252 | tablet_pad->wlr = wlr_tablet_pad_from_input_device(device->input_device->wlr_device); | ||
249 | tablet_pad->seat_device = device; | 253 | tablet_pad->seat_device = device; |
250 | wl_list_init(&tablet_pad->attach.link); | 254 | wl_list_init(&tablet_pad->attach.link); |
251 | wl_list_init(&tablet_pad->button.link); | 255 | wl_list_init(&tablet_pad->button.link); |
@@ -260,40 +264,41 @@ struct sway_tablet_pad *sway_tablet_pad_create(struct sway_seat *seat, | |||
260 | } | 264 | } |
261 | 265 | ||
262 | void sway_configure_tablet_pad(struct sway_tablet_pad *tablet_pad) { | 266 | void sway_configure_tablet_pad(struct sway_tablet_pad *tablet_pad) { |
263 | struct wlr_input_device *device = | 267 | struct wlr_input_device *wlr_device = |
264 | tablet_pad->seat_device->input_device->wlr_device; | 268 | tablet_pad->seat_device->input_device->wlr_device; |
265 | struct sway_seat *seat = tablet_pad->seat_device->sway_seat; | 269 | struct sway_seat *seat = tablet_pad->seat_device->sway_seat; |
266 | 270 | ||
267 | if (!tablet_pad->tablet_v2_pad) { | 271 | if (!tablet_pad->tablet_v2_pad) { |
268 | tablet_pad->tablet_v2_pad = | 272 | tablet_pad->tablet_v2_pad = |
269 | wlr_tablet_pad_create(server.tablet_v2, seat->wlr_seat, device); | 273 | wlr_tablet_pad_create(server.tablet_v2, seat->wlr_seat, wlr_device); |
270 | } | 274 | } |
271 | 275 | ||
272 | wl_list_remove(&tablet_pad->attach.link); | 276 | wl_list_remove(&tablet_pad->attach.link); |
273 | tablet_pad->attach.notify = handle_tablet_pad_attach; | 277 | tablet_pad->attach.notify = handle_tablet_pad_attach; |
274 | wl_signal_add(&device->tablet_pad->events.attach_tablet, | 278 | wl_signal_add(&tablet_pad->wlr->events.attach_tablet, |
275 | &tablet_pad->attach); | 279 | &tablet_pad->attach); |
276 | 280 | ||
277 | wl_list_remove(&tablet_pad->button.link); | 281 | wl_list_remove(&tablet_pad->button.link); |
278 | tablet_pad->button.notify = handle_tablet_pad_button; | 282 | tablet_pad->button.notify = handle_tablet_pad_button; |
279 | wl_signal_add(&device->tablet_pad->events.button, &tablet_pad->button); | 283 | wl_signal_add(&tablet_pad->wlr->events.button, &tablet_pad->button); |
280 | 284 | ||
281 | wl_list_remove(&tablet_pad->strip.link); | 285 | wl_list_remove(&tablet_pad->strip.link); |
282 | tablet_pad->strip.notify = handle_tablet_pad_strip; | 286 | tablet_pad->strip.notify = handle_tablet_pad_strip; |
283 | wl_signal_add(&device->tablet_pad->events.strip, &tablet_pad->strip); | 287 | wl_signal_add(&tablet_pad->wlr->events.strip, &tablet_pad->strip); |
284 | 288 | ||
285 | wl_list_remove(&tablet_pad->ring.link); | 289 | wl_list_remove(&tablet_pad->ring.link); |
286 | tablet_pad->ring.notify = handle_tablet_pad_ring; | 290 | tablet_pad->ring.notify = handle_tablet_pad_ring; |
287 | wl_signal_add(&device->tablet_pad->events.ring, &tablet_pad->ring); | 291 | wl_signal_add(&tablet_pad->wlr->events.ring, &tablet_pad->ring); |
288 | 292 | ||
293 | #if WLR_HAS_LIBINPUT_BACKEND | ||
289 | /* Search for a sibling tablet */ | 294 | /* Search for a sibling tablet */ |
290 | if (!wlr_input_device_is_libinput(device)) { | 295 | if (!wlr_input_device_is_libinput(wlr_device)) { |
291 | /* We can only do this on libinput devices */ | 296 | /* We can only do this on libinput devices */ |
292 | return; | 297 | return; |
293 | } | 298 | } |
294 | 299 | ||
295 | struct libinput_device_group *group = | 300 | struct libinput_device_group *group = |
296 | libinput_device_get_device_group(wlr_libinput_get_device_handle(device)); | 301 | libinput_device_get_device_group(wlr_libinput_get_device_handle(wlr_device)); |
297 | struct sway_tablet *tool; | 302 | struct sway_tablet *tool; |
298 | wl_list_for_each(tool, &seat->cursor->tablets, link) { | 303 | wl_list_for_each(tool, &seat->cursor->tablets, link) { |
299 | struct wlr_input_device *tablet = | 304 | struct wlr_input_device *tablet = |
@@ -310,6 +315,7 @@ void sway_configure_tablet_pad(struct sway_tablet_pad *tablet_pad) { | |||
310 | break; | 315 | break; |
311 | } | 316 | } |
312 | } | 317 | } |
318 | #endif | ||
313 | } | 319 | } |
314 | 320 | ||
315 | void sway_tablet_pad_destroy(struct sway_tablet_pad *tablet_pad) { | 321 | void sway_tablet_pad_destroy(struct sway_tablet_pad *tablet_pad) { |
@@ -333,14 +339,10 @@ static void handle_pad_tablet_surface_destroy(struct wl_listener *listener, | |||
333 | struct sway_tablet_pad *tablet_pad = | 339 | struct sway_tablet_pad *tablet_pad = |
334 | wl_container_of(listener, tablet_pad, surface_destroy); | 340 | wl_container_of(listener, tablet_pad, surface_destroy); |
335 | 341 | ||
336 | wlr_tablet_v2_tablet_pad_notify_leave(tablet_pad->tablet_v2_pad, | 342 | sway_tablet_pad_set_focus(tablet_pad, NULL); |
337 | tablet_pad->current_surface); | ||
338 | wl_list_remove(&tablet_pad->surface_destroy.link); | ||
339 | wl_list_init(&tablet_pad->surface_destroy.link); | ||
340 | tablet_pad->current_surface = NULL; | ||
341 | } | 343 | } |
342 | 344 | ||
343 | void sway_tablet_pad_notify_enter(struct sway_tablet_pad *tablet_pad, | 345 | void sway_tablet_pad_set_focus(struct sway_tablet_pad *tablet_pad, |
344 | struct wlr_surface *surface) { | 346 | struct wlr_surface *surface) { |
345 | if (!tablet_pad || !tablet_pad->tablet) { | 347 | if (!tablet_pad || !tablet_pad->tablet) { |
346 | return; | 348 | return; |
@@ -359,7 +361,8 @@ void sway_tablet_pad_notify_enter(struct sway_tablet_pad *tablet_pad, | |||
359 | tablet_pad->current_surface = NULL; | 361 | tablet_pad->current_surface = NULL; |
360 | } | 362 | } |
361 | 363 | ||
362 | if (!wlr_surface_accepts_tablet_v2(tablet_pad->tablet->tablet_v2, surface)) { | 364 | if (surface == NULL || |
365 | !wlr_surface_accepts_tablet_v2(tablet_pad->tablet->tablet_v2, surface)) { | ||
363 | return; | 366 | return; |
364 | } | 367 | } |
365 | 368 | ||
@@ -367,7 +370,6 @@ void sway_tablet_pad_notify_enter(struct sway_tablet_pad *tablet_pad, | |||
367 | tablet_pad->tablet->tablet_v2, surface); | 370 | tablet_pad->tablet->tablet_v2, surface); |
368 | 371 | ||
369 | tablet_pad->current_surface = surface; | 372 | tablet_pad->current_surface = surface; |
370 | wl_list_remove(&tablet_pad->surface_destroy.link); | ||
371 | tablet_pad->surface_destroy.notify = handle_pad_tablet_surface_destroy; | 373 | tablet_pad->surface_destroy.notify = handle_pad_tablet_surface_destroy; |
372 | wl_signal_add(&surface->events.destroy, &tablet_pad->surface_destroy); | 374 | wl_signal_add(&surface->events.destroy, &tablet_pad->surface_destroy); |
373 | } | 375 | } |
diff --git a/sway/input/text_input.c b/sway/input/text_input.c index f83726ee..c38a3bb2 100644 --- a/sway/input/text_input.c +++ b/sway/input/text_input.c | |||
@@ -2,7 +2,14 @@ | |||
2 | #include <stdlib.h> | 2 | #include <stdlib.h> |
3 | #include "log.h" | 3 | #include "log.h" |
4 | #include "sway/input/seat.h" | 4 | #include "sway/input/seat.h" |
5 | #include "sway/scene_descriptor.h" | ||
6 | #include "sway/tree/root.h" | ||
7 | #include "sway/tree/view.h" | ||
8 | #include "sway/output.h" | ||
5 | #include "sway/input/text_input.h" | 9 | #include "sway/input/text_input.h" |
10 | #include "sway/input/text_input_popup.h" | ||
11 | #include "sway/layers.h" | ||
12 | static void input_popup_update(struct sway_input_popup *popup); | ||
6 | 13 | ||
7 | static struct sway_text_input *relay_get_focusable_text_input( | 14 | static struct sway_text_input *relay_get_focusable_text_input( |
8 | struct sway_input_method_relay *relay) { | 15 | struct sway_input_method_relay *relay) { |
@@ -55,6 +62,35 @@ static void handle_im_commit(struct wl_listener *listener, void *data) { | |||
55 | wlr_text_input_v3_send_done(text_input->input); | 62 | wlr_text_input_v3_send_done(text_input->input); |
56 | } | 63 | } |
57 | 64 | ||
65 | static void handle_im_keyboard_grab_destroy(struct wl_listener *listener, void *data) { | ||
66 | struct sway_input_method_relay *relay = wl_container_of(listener, relay, | ||
67 | input_method_keyboard_grab_destroy); | ||
68 | struct wlr_input_method_keyboard_grab_v2 *keyboard_grab = data; | ||
69 | wl_list_remove(&relay->input_method_keyboard_grab_destroy.link); | ||
70 | |||
71 | if (keyboard_grab->keyboard) { | ||
72 | // send modifier state to original client | ||
73 | wlr_seat_keyboard_notify_modifiers(keyboard_grab->input_method->seat, | ||
74 | &keyboard_grab->keyboard->modifiers); | ||
75 | } | ||
76 | } | ||
77 | |||
78 | static void handle_im_grab_keyboard(struct wl_listener *listener, void *data) { | ||
79 | struct sway_input_method_relay *relay = wl_container_of(listener, relay, | ||
80 | input_method_grab_keyboard); | ||
81 | struct wlr_input_method_keyboard_grab_v2 *keyboard_grab = data; | ||
82 | |||
83 | // send modifier state to grab | ||
84 | struct wlr_keyboard *active_keyboard = wlr_seat_get_keyboard(relay->seat->wlr_seat); | ||
85 | wlr_input_method_keyboard_grab_v2_set_keyboard(keyboard_grab, | ||
86 | active_keyboard); | ||
87 | |||
88 | wl_signal_add(&keyboard_grab->events.destroy, | ||
89 | &relay->input_method_keyboard_grab_destroy); | ||
90 | relay->input_method_keyboard_grab_destroy.notify = | ||
91 | handle_im_keyboard_grab_destroy; | ||
92 | } | ||
93 | |||
58 | static void text_input_set_pending_focused_surface( | 94 | static void text_input_set_pending_focused_surface( |
59 | struct sway_text_input *text_input, struct wlr_surface *surface) { | 95 | struct sway_text_input *text_input, struct wlr_surface *surface) { |
60 | wl_list_remove(&text_input->pending_focused_surface_destroy.link); | 96 | wl_list_remove(&text_input->pending_focused_surface_destroy.link); |
@@ -73,6 +109,10 @@ static void handle_im_destroy(struct wl_listener *listener, void *data) { | |||
73 | input_method_destroy); | 109 | input_method_destroy); |
74 | struct wlr_input_method_v2 *context = data; | 110 | struct wlr_input_method_v2 *context = data; |
75 | assert(context == relay->input_method); | 111 | assert(context == relay->input_method); |
112 | wl_list_remove(&relay->input_method_commit.link); | ||
113 | wl_list_remove(&relay->input_method_grab_keyboard.link); | ||
114 | wl_list_remove(&relay->input_method_destroy.link); | ||
115 | wl_list_remove(&relay->input_method_new_popup_surface.link); | ||
76 | relay->input_method = NULL; | 116 | relay->input_method = NULL; |
77 | struct sway_text_input *text_input = relay_get_focused_text_input(relay); | 117 | struct sway_text_input *text_input = relay_get_focused_text_input(relay); |
78 | if (text_input) { | 118 | if (text_input) { |
@@ -92,13 +132,23 @@ static void relay_send_im_state(struct sway_input_method_relay *relay, | |||
92 | return; | 132 | return; |
93 | } | 133 | } |
94 | // TODO: only send each of those if they were modified | 134 | // TODO: only send each of those if they were modified |
95 | wlr_input_method_v2_send_surrounding_text(input_method, | 135 | if (input->active_features & WLR_TEXT_INPUT_V3_FEATURE_SURROUNDING_TEXT) { |
96 | input->current.surrounding.text, input->current.surrounding.cursor, | 136 | wlr_input_method_v2_send_surrounding_text(input_method, |
97 | input->current.surrounding.anchor); | 137 | input->current.surrounding.text, input->current.surrounding.cursor, |
138 | input->current.surrounding.anchor); | ||
139 | } | ||
98 | wlr_input_method_v2_send_text_change_cause(input_method, | 140 | wlr_input_method_v2_send_text_change_cause(input_method, |
99 | input->current.text_change_cause); | 141 | input->current.text_change_cause); |
100 | wlr_input_method_v2_send_content_type(input_method, | 142 | if (input->active_features & WLR_TEXT_INPUT_V3_FEATURE_CONTENT_TYPE) { |
101 | input->current.content_type.hint, input->current.content_type.purpose); | 143 | wlr_input_method_v2_send_content_type(input_method, |
144 | input->current.content_type.hint, | ||
145 | input->current.content_type.purpose); | ||
146 | } | ||
147 | struct sway_input_popup *popup; | ||
148 | wl_list_for_each(popup, &relay->input_popups, link) { | ||
149 | // send_text_input_rectangle is called in this function | ||
150 | input_popup_update(popup); | ||
151 | } | ||
102 | wlr_input_method_v2_send_done(input_method); | 152 | wlr_input_method_v2_send_done(input_method); |
103 | // TODO: pass intent, display popup size | 153 | // TODO: pass intent, display popup size |
104 | } | 154 | } |
@@ -144,6 +194,10 @@ static void handle_text_input_disable(struct wl_listener *listener, | |||
144 | void *data) { | 194 | void *data) { |
145 | struct sway_text_input *text_input = wl_container_of(listener, text_input, | 195 | struct sway_text_input *text_input = wl_container_of(listener, text_input, |
146 | text_input_disable); | 196 | text_input_disable); |
197 | if (text_input->input->focused_surface == NULL) { | ||
198 | sway_log(SWAY_DEBUG, "Disabling text input, but no longer focused"); | ||
199 | return; | ||
200 | } | ||
147 | relay_disable_text_input(text_input->relay, text_input); | 201 | relay_disable_text_input(text_input->relay, text_input); |
148 | } | 202 | } |
149 | 203 | ||
@@ -217,6 +271,211 @@ static void relay_handle_text_input(struct wl_listener *listener, | |||
217 | sway_text_input_create(relay, wlr_text_input); | 271 | sway_text_input_create(relay, wlr_text_input); |
218 | } | 272 | } |
219 | 273 | ||
274 | static void input_popup_update(struct sway_input_popup *popup) { | ||
275 | struct sway_text_input *text_input = | ||
276 | relay_get_focused_text_input(popup->relay); | ||
277 | |||
278 | if (text_input == NULL || text_input->input->focused_surface == NULL) { | ||
279 | return; | ||
280 | } | ||
281 | |||
282 | if (popup->scene_tree != NULL) { | ||
283 | wlr_scene_node_destroy(&popup->scene_tree->node); | ||
284 | popup->scene_tree = NULL; | ||
285 | } | ||
286 | if (popup->desc.relative != NULL) { | ||
287 | wlr_scene_node_destroy(popup->desc.relative); | ||
288 | popup->desc.relative = NULL; | ||
289 | } | ||
290 | popup->desc.view = NULL; | ||
291 | |||
292 | if (!popup->popup_surface->surface->mapped) { | ||
293 | return; | ||
294 | } | ||
295 | |||
296 | bool cursor_rect = text_input->input->current.features | ||
297 | & WLR_TEXT_INPUT_V3_FEATURE_CURSOR_RECTANGLE; | ||
298 | struct wlr_surface *focused_surface = text_input->input->focused_surface; | ||
299 | struct wlr_box cursor_area = text_input->input->current.cursor_rectangle; | ||
300 | |||
301 | struct wlr_box output_box; | ||
302 | struct wlr_box parent = {0}; | ||
303 | struct wlr_layer_surface_v1 *layer_surface = | ||
304 | wlr_layer_surface_v1_try_from_wlr_surface(focused_surface); | ||
305 | struct wlr_scene_tree *relative_parent; | ||
306 | |||
307 | struct wlr_box geo = {0}; | ||
308 | |||
309 | popup->scene_tree = wlr_scene_subsurface_tree_create(root->layers.popup, popup->popup_surface->surface); | ||
310 | if (layer_surface != NULL) { | ||
311 | struct sway_layer_surface *layer = | ||
312 | layer_surface->data; | ||
313 | if (layer == NULL) { | ||
314 | return; | ||
315 | } | ||
316 | |||
317 | relative_parent = layer->scene->tree; | ||
318 | struct wlr_output *output = layer->layer_surface->output; | ||
319 | wlr_output_layout_get_box(root->output_layout, output, &output_box); | ||
320 | int lx, ly; | ||
321 | wlr_scene_node_coords(&layer->tree->node, &lx, &ly); | ||
322 | parent.x = lx; | ||
323 | parent.y = ly; | ||
324 | popup->desc.view = NULL; | ||
325 | } else { | ||
326 | struct sway_view *view = view_from_wlr_surface(focused_surface); | ||
327 | relative_parent = view->scene_tree; | ||
328 | geo = view->geometry; | ||
329 | int lx, ly; | ||
330 | wlr_scene_node_coords(&view->scene_tree->node, &lx, &ly); | ||
331 | struct wlr_output *output = wlr_output_layout_output_at(root->output_layout, | ||
332 | view->container->pending.content_x + view->geometry.x, | ||
333 | view->container->pending.content_y + view->geometry.y); | ||
334 | wlr_output_layout_get_box(root->output_layout, output, &output_box); | ||
335 | parent.x = lx; | ||
336 | parent.y = ly; | ||
337 | |||
338 | parent.width = view->geometry.width; | ||
339 | parent.height = view->geometry.height; | ||
340 | popup->desc.view = view; | ||
341 | } | ||
342 | |||
343 | struct wlr_scene_tree *relative = wlr_scene_tree_create(relative_parent); | ||
344 | |||
345 | popup->desc.relative = &relative->node; | ||
346 | if (!scene_descriptor_assign(&popup->scene_tree->node, | ||
347 | SWAY_SCENE_DESC_POPUP, &popup->desc)) { | ||
348 | wlr_scene_node_destroy(&popup->scene_tree->node); | ||
349 | popup->scene_tree = NULL; | ||
350 | return; | ||
351 | } | ||
352 | |||
353 | if (!cursor_rect) { | ||
354 | cursor_area.x = 0; | ||
355 | cursor_area.y = 0; | ||
356 | cursor_area.width = parent.width; | ||
357 | cursor_area.height = parent.height; | ||
358 | } | ||
359 | |||
360 | int popup_width = popup->popup_surface->surface->current.width; | ||
361 | int popup_height = popup->popup_surface->surface->current.height; | ||
362 | int x1 = parent.x + cursor_area.x; | ||
363 | int x2 = parent.x + cursor_area.x + cursor_area.width; | ||
364 | int y1 = parent.y + cursor_area.y; | ||
365 | int y2 = parent.y + cursor_area.y + cursor_area.height; | ||
366 | int x = x1; | ||
367 | int y = y2; | ||
368 | |||
369 | int available_right = output_box.x + output_box.width - x1; | ||
370 | int available_left = x2 - output_box.x; | ||
371 | if (available_right < popup_width && available_left > available_right) { | ||
372 | x = x2 - popup_width; | ||
373 | } | ||
374 | |||
375 | int available_down = output_box.y + output_box.height - y2; | ||
376 | int available_up = y1 - output_box.y; | ||
377 | if (available_down < popup_height && available_up > available_down) { | ||
378 | y = y1 - popup_height; | ||
379 | } | ||
380 | |||
381 | wlr_scene_node_set_position(&relative->node, x - parent.x - geo.x, y - parent.y - geo.y); | ||
382 | if (cursor_rect) { | ||
383 | struct wlr_box box = { | ||
384 | .x = x1 - x, | ||
385 | .y = y1 - y, | ||
386 | .width = cursor_area.width, | ||
387 | .height = cursor_area.height, | ||
388 | }; | ||
389 | wlr_input_popup_surface_v2_send_text_input_rectangle( | ||
390 | popup->popup_surface, &box); | ||
391 | } | ||
392 | wlr_scene_node_set_position(&popup->scene_tree->node, x - geo.x, y - geo.y); | ||
393 | } | ||
394 | |||
395 | static void input_popup_set_focus(struct sway_input_popup *popup, | ||
396 | struct wlr_surface *surface) { | ||
397 | wl_list_remove(&popup->focused_surface_unmap.link); | ||
398 | |||
399 | if (surface == NULL) { | ||
400 | wl_list_init(&popup->focused_surface_unmap.link); | ||
401 | input_popup_update(popup); | ||
402 | return; | ||
403 | } | ||
404 | struct wlr_layer_surface_v1 *layer_surface = | ||
405 | wlr_layer_surface_v1_try_from_wlr_surface(surface); | ||
406 | if (layer_surface != NULL) { | ||
407 | wl_signal_add( | ||
408 | &layer_surface->surface->events.unmap, &popup->focused_surface_unmap); | ||
409 | input_popup_update(popup); | ||
410 | return; | ||
411 | } | ||
412 | |||
413 | struct sway_view *view = view_from_wlr_surface(surface); | ||
414 | wl_signal_add(&view->events.unmap, &popup->focused_surface_unmap); | ||
415 | } | ||
416 | |||
417 | static void handle_im_popup_destroy(struct wl_listener *listener, void *data) { | ||
418 | struct sway_input_popup *popup = | ||
419 | wl_container_of(listener, popup, popup_destroy); | ||
420 | wl_list_remove(&popup->focused_surface_unmap.link); | ||
421 | wl_list_remove(&popup->popup_surface_commit.link); | ||
422 | wl_list_remove(&popup->popup_destroy.link); | ||
423 | wl_list_remove(&popup->link); | ||
424 | |||
425 | free(popup); | ||
426 | } | ||
427 | |||
428 | static void handle_im_popup_surface_commit(struct wl_listener *listener, | ||
429 | void *data) { | ||
430 | struct sway_input_popup *popup = | ||
431 | wl_container_of(listener, popup, popup_surface_commit); | ||
432 | input_popup_update(popup); | ||
433 | } | ||
434 | |||
435 | static void handle_im_focused_surface_unmap( | ||
436 | struct wl_listener *listener, void *data) { | ||
437 | struct sway_input_popup *popup = | ||
438 | wl_container_of(listener, popup, focused_surface_unmap); | ||
439 | input_popup_update(popup); | ||
440 | } | ||
441 | |||
442 | static void handle_im_new_popup_surface(struct wl_listener *listener, | ||
443 | void *data) { | ||
444 | struct sway_input_method_relay *relay = wl_container_of(listener, relay, | ||
445 | input_method_new_popup_surface); | ||
446 | struct sway_input_popup *popup = calloc(1, sizeof(*popup)); | ||
447 | popup->relay = relay; | ||
448 | popup->popup_surface = data; | ||
449 | popup->popup_surface->data = popup; | ||
450 | |||
451 | wl_signal_add( | ||
452 | &popup->popup_surface->events.destroy, &popup->popup_destroy); | ||
453 | popup->popup_destroy.notify = handle_im_popup_destroy; | ||
454 | wl_signal_add(&popup->popup_surface->surface->events.commit, | ||
455 | &popup->popup_surface_commit); | ||
456 | popup->popup_surface_commit.notify = handle_im_popup_surface_commit; | ||
457 | wl_list_init(&popup->focused_surface_unmap.link); | ||
458 | popup->focused_surface_unmap.notify = handle_im_focused_surface_unmap; | ||
459 | |||
460 | struct sway_text_input *text_input = relay_get_focused_text_input(relay); | ||
461 | if (text_input != NULL) { | ||
462 | input_popup_set_focus(popup, text_input->input->focused_surface); | ||
463 | } else { | ||
464 | input_popup_set_focus(popup, NULL); | ||
465 | } | ||
466 | |||
467 | wl_list_insert(&relay->input_popups, &popup->link); | ||
468 | } | ||
469 | |||
470 | static void text_input_send_enter(struct sway_text_input *text_input, | ||
471 | struct wlr_surface *surface) { | ||
472 | wlr_text_input_v3_send_enter(text_input->input, surface); | ||
473 | struct sway_input_popup *popup; | ||
474 | wl_list_for_each(popup, &text_input->relay->input_popups, link) { | ||
475 | input_popup_set_focus(popup, surface); | ||
476 | } | ||
477 | } | ||
478 | |||
220 | static void relay_handle_input_method(struct wl_listener *listener, | 479 | static void relay_handle_input_method(struct wl_listener *listener, |
221 | void *data) { | 480 | void *data) { |
222 | struct sway_input_method_relay *relay = wl_container_of(listener, relay, | 481 | struct sway_input_method_relay *relay = wl_container_of(listener, relay, |
@@ -236,13 +495,19 @@ static void relay_handle_input_method(struct wl_listener *listener, | |||
236 | wl_signal_add(&relay->input_method->events.commit, | 495 | wl_signal_add(&relay->input_method->events.commit, |
237 | &relay->input_method_commit); | 496 | &relay->input_method_commit); |
238 | relay->input_method_commit.notify = handle_im_commit; | 497 | relay->input_method_commit.notify = handle_im_commit; |
498 | wl_signal_add(&relay->input_method->events.grab_keyboard, | ||
499 | &relay->input_method_grab_keyboard); | ||
500 | relay->input_method_grab_keyboard.notify = handle_im_grab_keyboard; | ||
239 | wl_signal_add(&relay->input_method->events.destroy, | 501 | wl_signal_add(&relay->input_method->events.destroy, |
240 | &relay->input_method_destroy); | 502 | &relay->input_method_destroy); |
241 | relay->input_method_destroy.notify = handle_im_destroy; | 503 | relay->input_method_destroy.notify = handle_im_destroy; |
504 | wl_signal_add(&relay->input_method->events.new_popup_surface, | ||
505 | &relay->input_method_new_popup_surface); | ||
506 | relay->input_method_new_popup_surface.notify = handle_im_new_popup_surface; | ||
242 | 507 | ||
243 | struct sway_text_input *text_input = relay_get_focusable_text_input(relay); | 508 | struct sway_text_input *text_input = relay_get_focusable_text_input(relay); |
244 | if (text_input) { | 509 | if (text_input) { |
245 | wlr_text_input_v3_send_enter(text_input->input, | 510 | text_input_send_enter(text_input, |
246 | text_input->pending_focused_surface); | 511 | text_input->pending_focused_surface); |
247 | text_input_set_pending_focused_surface(text_input, NULL); | 512 | text_input_set_pending_focused_surface(text_input, NULL); |
248 | } | 513 | } |
@@ -252,6 +517,7 @@ void sway_input_method_relay_init(struct sway_seat *seat, | |||
252 | struct sway_input_method_relay *relay) { | 517 | struct sway_input_method_relay *relay) { |
253 | relay->seat = seat; | 518 | relay->seat = seat; |
254 | wl_list_init(&relay->text_inputs); | 519 | wl_list_init(&relay->text_inputs); |
520 | wl_list_init(&relay->input_popups); | ||
255 | 521 | ||
256 | relay->text_input_new.notify = relay_handle_text_input; | 522 | relay->text_input_new.notify = relay_handle_text_input; |
257 | wl_signal_add(&server.text_input->events.text_input, | 523 | wl_signal_add(&server.text_input->events.text_input, |
diff --git a/sway/ipc-json.c b/sway/ipc-json.c index fceee84d..b7370aa6 100644 --- a/sway/ipc-json.c +++ b/sway/ipc-json.c | |||
@@ -1,7 +1,12 @@ | |||
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/config.h> |
7 | #include <wlr/types/wlr_content_type_v1.h> | ||
8 | #include <wlr/types/wlr_output.h> | ||
9 | #include <xkbcommon/xkbcommon.h> | ||
5 | #include "config.h" | 10 | #include "config.h" |
6 | #include "log.h" | 11 | #include "log.h" |
7 | #include "sway/config.h" | 12 | #include "sway/config.h" |
@@ -13,16 +18,30 @@ | |||
13 | #include "sway/input/input-manager.h" | 18 | #include "sway/input/input-manager.h" |
14 | #include "sway/input/cursor.h" | 19 | #include "sway/input/cursor.h" |
15 | #include "sway/input/seat.h" | 20 | #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" | 21 | #include "wlr-layer-shell-unstable-v1-protocol.h" |
21 | #include "sway/desktop/idle_inhibit_v1.h" | 22 | #include "sway/desktop/idle_inhibit_v1.h" |
22 | 23 | ||
24 | #if WLR_HAS_LIBINPUT_BACKEND | ||
25 | #include <wlr/backend/libinput.h> | ||
26 | #endif | ||
27 | |||
23 | static const int i3_output_id = INT32_MAX; | 28 | static const int i3_output_id = INT32_MAX; |
24 | static const int i3_scratch_id = INT32_MAX - 1; | 29 | static const int i3_scratch_id = INT32_MAX - 1; |
25 | 30 | ||
31 | static const char *ipc_json_node_type_description(enum sway_node_type node_type) { | ||
32 | switch (node_type) { | ||
33 | case N_ROOT: | ||
34 | return "root"; | ||
35 | case N_OUTPUT: | ||
36 | return "output"; | ||
37 | case N_WORKSPACE: | ||
38 | return "workspace"; | ||
39 | case N_CONTAINER: | ||
40 | return "con"; | ||
41 | } | ||
42 | return "none"; | ||
43 | } | ||
44 | |||
26 | static const char *ipc_json_layout_description(enum sway_container_layout l) { | 45 | static const char *ipc_json_layout_description(enum sway_container_layout l) { |
27 | switch (l) { | 46 | switch (l) { |
28 | case L_VERT: | 47 | case L_VERT: |
@@ -98,13 +117,44 @@ static const char *ipc_json_output_adaptive_sync_status_description( | |||
98 | return "disabled"; | 117 | return "disabled"; |
99 | case WLR_OUTPUT_ADAPTIVE_SYNC_ENABLED: | 118 | case WLR_OUTPUT_ADAPTIVE_SYNC_ENABLED: |
100 | return "enabled"; | 119 | return "enabled"; |
101 | case WLR_OUTPUT_ADAPTIVE_SYNC_UNKNOWN: | ||
102 | return "unknown"; | ||
103 | } | 120 | } |
104 | return NULL; | 121 | return NULL; |
105 | } | 122 | } |
106 | 123 | ||
107 | #if HAVE_XWAYLAND | 124 | static const char *ipc_json_output_mode_aspect_ratio_description( |
125 | enum wlr_output_mode_aspect_ratio aspect_ratio) { | ||
126 | switch (aspect_ratio) { | ||
127 | case WLR_OUTPUT_MODE_ASPECT_RATIO_NONE: | ||
128 | return "none"; | ||
129 | case WLR_OUTPUT_MODE_ASPECT_RATIO_4_3: | ||
130 | return "4:3"; | ||
131 | case WLR_OUTPUT_MODE_ASPECT_RATIO_16_9: | ||
132 | return "16:9"; | ||
133 | case WLR_OUTPUT_MODE_ASPECT_RATIO_64_27: | ||
134 | return "64:27"; | ||
135 | case WLR_OUTPUT_MODE_ASPECT_RATIO_256_135: | ||
136 | return "256:135"; | ||
137 | } | ||
138 | return NULL; | ||
139 | } | ||
140 | |||
141 | static json_object *ipc_json_output_mode_description( | ||
142 | const struct wlr_output_mode *mode) { | ||
143 | const char *pic_ar = | ||
144 | ipc_json_output_mode_aspect_ratio_description(mode->picture_aspect_ratio); | ||
145 | json_object *mode_object = json_object_new_object(); | ||
146 | json_object_object_add(mode_object, "width", | ||
147 | json_object_new_int(mode->width)); | ||
148 | json_object_object_add(mode_object, "height", | ||
149 | json_object_new_int(mode->height)); | ||
150 | json_object_object_add(mode_object, "refresh", | ||
151 | json_object_new_int(mode->refresh)); | ||
152 | json_object_object_add(mode_object, "picture_aspect_ratio", | ||
153 | json_object_new_string(pic_ar)); | ||
154 | return mode_object; | ||
155 | } | ||
156 | |||
157 | #if WLR_HAS_XWAYLAND | ||
108 | static const char *ipc_json_xwindow_type_description(struct sway_view *view) { | 158 | static const char *ipc_json_xwindow_type_description(struct sway_view *view) { |
109 | struct wlr_xwayland_surface *surface = view->wlr_xwayland_surface; | 159 | struct wlr_xwayland_surface *surface = view->wlr_xwayland_surface; |
110 | struct sway_xwayland *xwayland = &server.xwayland; | 160 | struct sway_xwayland *xwayland = &server.xwayland; |
@@ -156,6 +206,20 @@ static const char *ipc_json_user_idle_inhibitor_description(enum sway_idle_inhib | |||
156 | return NULL; | 206 | return NULL; |
157 | } | 207 | } |
158 | 208 | ||
209 | static const char *ipc_json_content_type_description(enum wp_content_type_v1_type type) { | ||
210 | switch (type) { | ||
211 | case WP_CONTENT_TYPE_V1_TYPE_NONE: | ||
212 | return "none"; | ||
213 | case WP_CONTENT_TYPE_V1_TYPE_PHOTO: | ||
214 | return "photo"; | ||
215 | case WP_CONTENT_TYPE_V1_TYPE_VIDEO: | ||
216 | return "video"; | ||
217 | case WP_CONTENT_TYPE_V1_TYPE_GAME: | ||
218 | return "game"; | ||
219 | } | ||
220 | return NULL; | ||
221 | } | ||
222 | |||
159 | json_object *ipc_json_get_version(void) { | 223 | json_object *ipc_json_get_version(void) { |
160 | int major = 0, minor = 0, patch = 0; | 224 | int major = 0, minor = 0, patch = 0; |
161 | json_object *version = json_object_new_object(); | 225 | json_object *version = json_object_new_object(); |
@@ -189,16 +253,22 @@ static json_object *ipc_json_create_empty_rect(void) { | |||
189 | return ipc_json_create_rect(&empty); | 253 | return ipc_json_create_rect(&empty); |
190 | } | 254 | } |
191 | 255 | ||
192 | static json_object *ipc_json_create_node(int id, char *name, | 256 | static json_object *ipc_json_create_node(int id, const char* type, char *name, |
193 | bool focused, json_object *focus, struct wlr_box *box) { | 257 | bool focused, json_object *focus, struct wlr_box *box) { |
194 | json_object *object = json_object_new_object(); | 258 | json_object *object = json_object_new_object(); |
195 | 259 | ||
196 | json_object_object_add(object, "id", json_object_new_int(id)); | 260 | json_object_object_add(object, "id", json_object_new_int(id)); |
197 | json_object_object_add(object, "name", | 261 | json_object_object_add(object, "type", json_object_new_string(type)); |
198 | name ? json_object_new_string(name) : NULL); | 262 | json_object_object_add(object, "orientation", |
199 | json_object_object_add(object, "rect", ipc_json_create_rect(box)); | 263 | json_object_new_string( |
264 | ipc_json_orientation_description(L_HORIZ))); | ||
265 | json_object_object_add(object, "percent", NULL); | ||
266 | json_object_object_add(object, "urgent", json_object_new_boolean(false)); | ||
267 | json_object_object_add(object, "marks", json_object_new_array()); | ||
200 | json_object_object_add(object, "focused", json_object_new_boolean(focused)); | 268 | json_object_object_add(object, "focused", json_object_new_boolean(focused)); |
201 | json_object_object_add(object, "focus", focus); | 269 | json_object_object_add(object, "layout", |
270 | json_object_new_string( | ||
271 | ipc_json_layout_description(L_HORIZ))); | ||
202 | 272 | ||
203 | // set default values to be compatible with i3 | 273 | // set default values to be compatible with i3 |
204 | json_object_object_add(object, "border", | 274 | json_object_object_add(object, "border", |
@@ -206,49 +276,68 @@ static json_object *ipc_json_create_node(int id, char *name, | |||
206 | ipc_json_border_description(B_NONE))); | 276 | ipc_json_border_description(B_NONE))); |
207 | json_object_object_add(object, "current_border_width", | 277 | json_object_object_add(object, "current_border_width", |
208 | json_object_new_int(0)); | 278 | json_object_new_int(0)); |
209 | json_object_object_add(object, "layout", | 279 | 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()); | 280 | json_object_object_add(object, "deco_rect", ipc_json_create_empty_rect()); |
281 | json_object_object_add(object, "window_rect", ipc_json_create_empty_rect()); | ||
218 | json_object_object_add(object, "geometry", ipc_json_create_empty_rect()); | 282 | json_object_object_add(object, "geometry", ipc_json_create_empty_rect()); |
283 | json_object_object_add(object, "name", | ||
284 | name ? json_object_new_string(name) : NULL); | ||
219 | json_object_object_add(object, "window", NULL); | 285 | 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()); | 286 | json_object_object_add(object, "nodes", json_object_new_array()); |
224 | json_object_object_add(object, "floating_nodes", json_object_new_array()); | 287 | json_object_object_add(object, "floating_nodes", json_object_new_array()); |
288 | json_object_object_add(object, "focus", focus); | ||
289 | json_object_object_add(object, "fullscreen_mode", json_object_new_int(0)); | ||
225 | json_object_object_add(object, "sticky", json_object_new_boolean(false)); | 290 | json_object_object_add(object, "sticky", json_object_new_boolean(false)); |
291 | json_object_object_add(object, "floating", NULL); | ||
292 | json_object_object_add(object, "scratchpad_state", NULL); | ||
226 | 293 | ||
227 | return object; | 294 | return object; |
228 | } | 295 | } |
229 | 296 | ||
230 | static void ipc_json_describe_root(struct sway_root *root, json_object *object) { | 297 | static void ipc_json_describe_wlr_output(struct wlr_output *wlr_output, json_object *object) { |
231 | json_object_object_add(object, "type", json_object_new_string("root")); | 298 | json_object_object_add(object, "primary", json_object_new_boolean(false)); |
299 | json_object_object_add(object, "make", | ||
300 | json_object_new_string(wlr_output->make ? wlr_output->make : "Unknown")); | ||
301 | json_object_object_add(object, "model", | ||
302 | json_object_new_string(wlr_output->model ? wlr_output->model : "Unknown")); | ||
303 | json_object_object_add(object, "serial", | ||
304 | json_object_new_string(wlr_output->serial ? wlr_output->serial : "Unknown")); | ||
305 | |||
306 | json_object *modes_array = json_object_new_array(); | ||
307 | struct wlr_output_mode *mode; | ||
308 | wl_list_for_each(mode, &wlr_output->modes, link) { | ||
309 | json_object *mode_object = json_object_new_object(); | ||
310 | json_object_object_add(mode_object, "width", | ||
311 | json_object_new_int(mode->width)); | ||
312 | json_object_object_add(mode_object, "height", | ||
313 | json_object_new_int(mode->height)); | ||
314 | json_object_object_add(mode_object, "refresh", | ||
315 | json_object_new_int(mode->refresh)); | ||
316 | json_object_array_add(modes_array, mode_object); | ||
317 | } | ||
318 | json_object_object_add(object, "modes", modes_array); | ||
232 | } | 319 | } |
233 | 320 | ||
234 | static void ipc_json_describe_output(struct sway_output *output, | 321 | static void ipc_json_describe_output(struct sway_output *output, |
235 | json_object *object) { | 322 | json_object *object) { |
323 | ipc_json_describe_wlr_output(output->wlr_output, object); | ||
324 | } | ||
325 | |||
326 | static void ipc_json_describe_enabled_output(struct sway_output *output, | ||
327 | json_object *object) { | ||
328 | ipc_json_describe_output(output, object); | ||
329 | |||
236 | struct wlr_output *wlr_output = output->wlr_output; | 330 | struct wlr_output *wlr_output = output->wlr_output; |
237 | json_object_object_add(object, "type", json_object_new_string("output")); | 331 | json_object_object_add(object, "non_desktop", json_object_new_boolean(false)); |
238 | json_object_object_add(object, "active", json_object_new_boolean(true)); | 332 | json_object_object_add(object, "active", json_object_new_boolean(true)); |
239 | json_object_object_add(object, "dpms", | 333 | json_object_object_add(object, "dpms", |
240 | json_object_new_boolean(wlr_output->enabled)); | 334 | json_object_new_boolean(wlr_output->enabled)); |
241 | json_object_object_add(object, "primary", json_object_new_boolean(false)); | 335 | json_object_object_add(object, "power", |
336 | json_object_new_boolean(wlr_output->enabled)); | ||
242 | json_object_object_add(object, "layout", json_object_new_string("output")); | 337 | json_object_object_add(object, "layout", json_object_new_string("output")); |
243 | json_object_object_add(object, "orientation", | 338 | json_object_object_add(object, "orientation", |
244 | json_object_new_string( | 339 | json_object_new_string( |
245 | ipc_json_orientation_description(L_NONE))); | 340 | 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", | 341 | json_object_object_add(object, "scale", |
253 | json_object_new_double(wlr_output->scale)); | 342 | json_object_new_double(wlr_output->scale)); |
254 | json_object_object_add(object, "scale_filter", | 343 | json_object_object_add(object, "scale_filter", |
@@ -273,25 +362,26 @@ static void ipc_json_describe_output(struct sway_output *output, | |||
273 | json_object *modes_array = json_object_new_array(); | 362 | json_object *modes_array = json_object_new_array(); |
274 | struct wlr_output_mode *mode; | 363 | struct wlr_output_mode *mode; |
275 | wl_list_for_each(mode, &wlr_output->modes, link) { | 364 | wl_list_for_each(mode, &wlr_output->modes, link) { |
276 | json_object *mode_object = json_object_new_object(); | 365 | json_object *mode_object = |
277 | json_object_object_add(mode_object, "width", | 366 | ipc_json_output_mode_description(mode); |
278 | json_object_new_int(mode->width)); | ||
279 | json_object_object_add(mode_object, "height", | ||
280 | json_object_new_int(mode->height)); | ||
281 | json_object_object_add(mode_object, "refresh", | ||
282 | json_object_new_int(mode->refresh)); | ||
283 | json_object_array_add(modes_array, mode_object); | 367 | json_object_array_add(modes_array, mode_object); |
284 | } | 368 | } |
285 | 369 | ||
286 | json_object_object_add(object, "modes", modes_array); | 370 | json_object_object_add(object, "modes", modes_array); |
287 | 371 | ||
288 | json_object *current_mode_object = json_object_new_object(); | 372 | json_object *current_mode_object; |
289 | json_object_object_add(current_mode_object, "width", | 373 | if (wlr_output->current_mode != NULL) { |
290 | json_object_new_int(wlr_output->width)); | 374 | current_mode_object = |
291 | json_object_object_add(current_mode_object, "height", | 375 | ipc_json_output_mode_description(wlr_output->current_mode); |
292 | json_object_new_int(wlr_output->height)); | 376 | } else { |
293 | json_object_object_add(current_mode_object, "refresh", | 377 | current_mode_object = json_object_new_object(); |
294 | json_object_new_int(wlr_output->refresh)); | 378 | json_object_object_add(current_mode_object, "width", |
379 | json_object_new_int(wlr_output->width)); | ||
380 | json_object_object_add(current_mode_object, "height", | ||
381 | json_object_new_int(wlr_output->height)); | ||
382 | json_object_object_add(current_mode_object, "refresh", | ||
383 | json_object_new_int(wlr_output->refresh)); | ||
384 | } | ||
295 | json_object_object_add(object, "current_mode", current_mode_object); | 385 | json_object_object_add(object, "current_mode", current_mode_object); |
296 | 386 | ||
297 | struct sway_node *parent = node_get_parent(&output->node); | 387 | struct sway_node *parent = node_get_parent(&output->node); |
@@ -315,33 +405,15 @@ json_object *ipc_json_describe_disabled_output(struct sway_output *output) { | |||
315 | 405 | ||
316 | json_object *object = json_object_new_object(); | 406 | json_object *object = json_object_new_object(); |
317 | 407 | ||
408 | ipc_json_describe_output(output, object); | ||
409 | |||
410 | json_object_object_add(object, "non_desktop", json_object_new_boolean(false)); | ||
318 | json_object_object_add(object, "type", json_object_new_string("output")); | 411 | json_object_object_add(object, "type", json_object_new_string("output")); |
319 | json_object_object_add(object, "name", | 412 | json_object_object_add(object, "name", |
320 | json_object_new_string(wlr_output->name)); | 413 | json_object_new_string(wlr_output->name)); |
321 | json_object_object_add(object, "active", json_object_new_boolean(false)); | 414 | json_object_object_add(object, "active", json_object_new_boolean(false)); |
322 | json_object_object_add(object, "dpms", json_object_new_boolean(false)); | 415 | json_object_object_add(object, "dpms", json_object_new_boolean(false)); |
323 | json_object_object_add(object, "primary", json_object_new_boolean(false)); | 416 | 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 | 417 | ||
346 | json_object_object_add(object, "current_workspace", NULL); | 418 | json_object_object_add(object, "current_workspace", NULL); |
347 | 419 | ||
@@ -357,6 +429,21 @@ json_object *ipc_json_describe_disabled_output(struct sway_output *output) { | |||
357 | return object; | 429 | return object; |
358 | } | 430 | } |
359 | 431 | ||
432 | json_object *ipc_json_describe_non_desktop_output(struct sway_output_non_desktop *output) { | ||
433 | struct wlr_output *wlr_output = output->wlr_output; | ||
434 | |||
435 | json_object *object = json_object_new_object(); | ||
436 | |||
437 | ipc_json_describe_wlr_output(wlr_output, object); | ||
438 | |||
439 | json_object_object_add(object, "non_desktop", json_object_new_boolean(true)); | ||
440 | json_object_object_add(object, "type", json_object_new_string("output")); | ||
441 | json_object_object_add(object, "name", | ||
442 | json_object_new_string(wlr_output->name)); | ||
443 | |||
444 | return object; | ||
445 | } | ||
446 | |||
360 | static json_object *ipc_json_describe_scratchpad_output(void) { | 447 | static json_object *ipc_json_describe_scratchpad_output(void) { |
361 | struct wlr_box box; | 448 | struct wlr_box box; |
362 | root_get_box(root, &box); | 449 | root_get_box(root, &box); |
@@ -369,11 +456,9 @@ static json_object *ipc_json_describe_scratchpad_output(void) { | |||
369 | json_object_new_int(container->node.id)); | 456 | json_object_new_int(container->node.id)); |
370 | } | 457 | } |
371 | 458 | ||
372 | json_object *workspace = ipc_json_create_node(i3_scratch_id, | 459 | json_object *workspace = ipc_json_create_node(i3_scratch_id, "workspace", |
373 | "__i3_scratch", false, workspace_focus, &box); | 460 | "__i3_scratch", false, workspace_focus, &box); |
374 | json_object_object_add(workspace, "fullscreen_mode", json_object_new_int(1)); | 461 | 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 | 462 | ||
378 | // List all hidden scratchpad containers as floating nodes | 463 | // List all hidden scratchpad containers as floating nodes |
379 | json_object *floating_array = json_object_new_array(); | 464 | json_object *floating_array = json_object_new_array(); |
@@ -390,10 +475,8 @@ static json_object *ipc_json_describe_scratchpad_output(void) { | |||
390 | json_object *output_focus = json_object_new_array(); | 475 | json_object *output_focus = json_object_new_array(); |
391 | json_object_array_add(output_focus, json_object_new_int(i3_scratch_id)); | 476 | json_object_array_add(output_focus, json_object_new_int(i3_scratch_id)); |
392 | 477 | ||
393 | json_object *output = ipc_json_create_node(i3_output_id, | 478 | json_object *output = ipc_json_create_node(i3_output_id, "output", |
394 | "__i3", false, output_focus, &box); | 479 | "__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", | 480 | json_object_object_add(output, "layout", |
398 | json_object_new_string("output")); | 481 | json_object_new_string("output")); |
399 | 482 | ||
@@ -423,7 +506,6 @@ static void ipc_json_describe_workspace(struct sway_workspace *workspace, | |||
423 | json_object_object_add(object, "fullscreen_mode", json_object_new_int(1)); | 506 | json_object_object_add(object, "fullscreen_mode", json_object_new_int(1)); |
424 | json_object_object_add(object, "output", workspace->output ? | 507 | json_object_object_add(object, "output", workspace->output ? |
425 | json_object_new_string(workspace->output->wlr_output->name) : NULL); | 508 | 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", | 509 | json_object_object_add(object, "urgent", |
428 | json_object_new_boolean(workspace->urgent)); | 510 | json_object_new_boolean(workspace->urgent)); |
429 | json_object_object_add(object, "representation", workspace->representation ? | 511 | json_object_object_add(object, "representation", workspace->representation ? |
@@ -448,30 +530,32 @@ static void ipc_json_describe_workspace(struct sway_workspace *workspace, | |||
448 | 530 | ||
449 | static void get_deco_rect(struct sway_container *c, struct wlr_box *deco_rect) { | 531 | 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); | 532 | enum sway_container_layout parent_layout = container_parent_layout(c); |
451 | bool tab_or_stack = parent_layout == L_TABBED || parent_layout == L_STACKED; | 533 | list_t *siblings = container_get_siblings(c); |
534 | bool tab_or_stack = (parent_layout == L_TABBED || parent_layout == L_STACKED) | ||
535 | && ((siblings && siblings->length > 1) || !config->hide_lone_tab); | ||
452 | if (((!tab_or_stack || container_is_floating(c)) && | 536 | if (((!tab_or_stack || container_is_floating(c)) && |
453 | c->current.border != B_NORMAL) || | 537 | c->current.border != B_NORMAL) || |
454 | c->fullscreen_mode != FULLSCREEN_NONE || | 538 | c->pending.fullscreen_mode != FULLSCREEN_NONE || |
455 | c->workspace == NULL) { | 539 | c->pending.workspace == NULL) { |
456 | deco_rect->x = deco_rect->y = deco_rect->width = deco_rect->height = 0; | 540 | deco_rect->x = deco_rect->y = deco_rect->width = deco_rect->height = 0; |
457 | return; | 541 | return; |
458 | } | 542 | } |
459 | 543 | ||
460 | if (c->parent) { | 544 | if (c->pending.parent) { |
461 | deco_rect->x = c->x - c->parent->x; | 545 | deco_rect->x = c->pending.x - c->pending.parent->pending.x; |
462 | deco_rect->y = c->y - c->parent->y; | 546 | deco_rect->y = c->pending.y - c->pending.parent->pending.y; |
463 | } else { | 547 | } else { |
464 | deco_rect->x = c->x - c->workspace->x; | 548 | deco_rect->x = c->pending.x - c->pending.workspace->x; |
465 | deco_rect->y = c->y - c->workspace->y; | 549 | deco_rect->y = c->pending.y - c->pending.workspace->y; |
466 | } | 550 | } |
467 | deco_rect->width = c->width; | 551 | deco_rect->width = c->pending.width; |
468 | deco_rect->height = container_titlebar_height(); | 552 | deco_rect->height = container_titlebar_height(); |
469 | 553 | ||
470 | if (!container_is_floating(c)) { | 554 | if (!container_is_floating(c)) { |
471 | if (parent_layout == L_TABBED) { | 555 | if (parent_layout == L_TABBED) { |
472 | deco_rect->width = c->parent | 556 | deco_rect->width = c->pending.parent |
473 | ? c->parent->width / c->parent->children->length | 557 | ? c->pending.parent->pending.width / c->pending.parent->pending.children->length |
474 | : c->workspace->width / c->workspace->tiling->length; | 558 | : c->pending.workspace->width / c->pending.workspace->tiling->length; |
475 | deco_rect->x += deco_rect->width * container_sibling_index(c); | 559 | deco_rect->x += deco_rect->width * container_sibling_index(c); |
476 | } else if (parent_layout == L_STACKED) { | 560 | } else if (parent_layout == L_STACKED) { |
477 | if (!c->view) { | 561 | if (!c->view) { |
@@ -494,10 +578,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)); | 578 | json_object_object_add(object, "visible", json_object_new_boolean(visible)); |
495 | 579 | ||
496 | struct wlr_box window_box = { | 580 | struct wlr_box window_box = { |
497 | c->content_x - c->x, | 581 | c->pending.content_x - c->pending.x, |
498 | (c->current.border == B_PIXEL) ? c->current.border_thickness : 0, | 582 | (c->current.border == B_PIXEL) ? c->pending.content_y - c->pending.y : 0, |
499 | c->content_width, | 583 | c->pending.content_width, |
500 | c->content_height | 584 | c->pending.content_height |
501 | }; | 585 | }; |
502 | 586 | ||
503 | json_object_object_add(object, "window_rect", ipc_json_create_rect(&window_box)); | 587 | json_object_object_add(object, "window_rect", ipc_json_create_rect(&window_box)); |
@@ -539,7 +623,17 @@ static void ipc_json_describe_view(struct sway_container *c, json_object *object | |||
539 | 623 | ||
540 | json_object_object_add(object, "idle_inhibitors", idle_inhibitors); | 624 | json_object_object_add(object, "idle_inhibitors", idle_inhibitors); |
541 | 625 | ||
542 | #if HAVE_XWAYLAND | 626 | enum wp_content_type_v1_type content_type = WP_CONTENT_TYPE_V1_TYPE_NONE; |
627 | if (c->view->surface != NULL) { | ||
628 | content_type = wlr_surface_get_content_type_v1(server.content_type_manager_v1, | ||
629 | c->view->surface); | ||
630 | } | ||
631 | if (content_type != WP_CONTENT_TYPE_V1_TYPE_NONE) { | ||
632 | json_object_object_add(object, "content_type", | ||
633 | json_object_new_string(ipc_json_content_type_description(content_type))); | ||
634 | } | ||
635 | |||
636 | #if WLR_HAS_XWAYLAND | ||
543 | if (c->view->type == SWAY_VIEW_XWAYLAND) { | 637 | if (c->view->type == SWAY_VIEW_XWAYLAND) { |
544 | json_object_object_add(object, "window", | 638 | json_object_object_add(object, "window", |
545 | json_object_new_int(view_get_x11_window_id(c->view))); | 639 | json_object_new_int(view_get_x11_window_id(c->view))); |
@@ -583,24 +677,35 @@ 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) { | 677 | static void ipc_json_describe_container(struct sway_container *c, json_object *object) { |
584 | json_object_object_add(object, "name", | 678 | json_object_object_add(object, "name", |
585 | c->title ? json_object_new_string(c->title) : NULL); | 679 | c->title ? json_object_new_string(c->title) : NULL); |
586 | json_object_object_add(object, "type", | 680 | bool floating = container_is_floating(c); |
587 | json_object_new_string(container_is_floating(c) ? "floating_con" : "con")); | 681 | if (floating) { |
682 | json_object_object_add(object, "type", | ||
683 | json_object_new_string("floating_con")); | ||
684 | } | ||
588 | 685 | ||
589 | json_object_object_add(object, "layout", | 686 | json_object_object_add(object, "layout", |
590 | json_object_new_string( | 687 | json_object_new_string( |
591 | ipc_json_layout_description(c->layout))); | 688 | ipc_json_layout_description(c->pending.layout))); |
592 | 689 | ||
593 | json_object_object_add(object, "orientation", | 690 | json_object_object_add(object, "orientation", |
594 | json_object_new_string( | 691 | json_object_new_string( |
595 | ipc_json_orientation_description(c->layout))); | 692 | ipc_json_orientation_description(c->pending.layout))); |
596 | 693 | ||
597 | bool urgent = c->view ? | 694 | bool urgent = c->view ? |
598 | view_is_urgent(c->view) : container_has_urgent_child(c); | 695 | view_is_urgent(c->view) : container_has_urgent_child(c); |
599 | json_object_object_add(object, "urgent", json_object_new_boolean(urgent)); | 696 | json_object_object_add(object, "urgent", json_object_new_boolean(urgent)); |
600 | json_object_object_add(object, "sticky", json_object_new_boolean(c->is_sticky)); | 697 | json_object_object_add(object, "sticky", json_object_new_boolean(c->is_sticky)); |
601 | 698 | ||
699 | // sway doesn't track the floating reason, so we can't use "auto_on" or "user_off" | ||
700 | json_object_object_add(object, "floating", | ||
701 | json_object_new_string(floating ? "user_on" : "auto_off")); | ||
702 | |||
602 | json_object_object_add(object, "fullscreen_mode", | 703 | json_object_object_add(object, "fullscreen_mode", |
603 | json_object_new_int(c->fullscreen_mode)); | 704 | json_object_new_int(c->pending.fullscreen_mode)); |
705 | |||
706 | // sway doesn't track if window was resized in scratchpad, so we can't use "changed" | ||
707 | json_object_object_add(object, "scratchpad_state", | ||
708 | json_object_new_string(!c->scratchpad ? "none" : "fresh")); | ||
604 | 709 | ||
605 | struct sway_node *parent = node_get_parent(&c->node); | 710 | struct sway_node *parent = node_get_parent(&c->node); |
606 | struct wlr_box parent_box = {0, 0, 0, 0}; | 711 | struct wlr_box parent_box = {0, 0, 0, 0}; |
@@ -610,8 +715,8 @@ static void ipc_json_describe_container(struct sway_container *c, json_object *o | |||
610 | } | 715 | } |
611 | 716 | ||
612 | if (parent_box.width != 0 && parent_box.height != 0) { | 717 | if (parent_box.width != 0 && parent_box.height != 0) { |
613 | double percent = ((double)c->width / parent_box.width) | 718 | double percent = ((double)c->pending.width / parent_box.width) |
614 | * ((double)c->height / parent_box.height); | 719 | * ((double)c->pending.height / parent_box.height); |
615 | json_object_object_add(object, "percent", json_object_new_double(percent)); | 720 | json_object_object_add(object, "percent", json_object_new_double(percent)); |
616 | } | 721 | } |
617 | 722 | ||
@@ -692,15 +797,14 @@ json_object *ipc_json_describe_node(struct sway_node *node) { | |||
692 | }; | 797 | }; |
693 | seat_for_each_node(seat, focus_inactive_children_iterator, &data); | 798 | seat_for_each_node(seat, focus_inactive_children_iterator, &data); |
694 | 799 | ||
695 | json_object *object = ipc_json_create_node( | 800 | json_object *object = ipc_json_create_node((int)node->id, |
696 | (int)node->id, name, focused, focus, &box); | 801 | ipc_json_node_type_description(node->type), name, focused, focus, &box); |
697 | 802 | ||
698 | switch (node->type) { | 803 | switch (node->type) { |
699 | case N_ROOT: | 804 | case N_ROOT: |
700 | ipc_json_describe_root(root, object); | ||
701 | break; | 805 | break; |
702 | case N_OUTPUT: | 806 | case N_OUTPUT: |
703 | ipc_json_describe_output(node->sway_output, object); | 807 | ipc_json_describe_enabled_output(node->sway_output, object); |
704 | break; | 808 | break; |
705 | case N_CONTAINER: | 809 | case N_CONTAINER: |
706 | ipc_json_describe_container(node->sway_container, object); | 810 | ipc_json_describe_container(node->sway_container, object); |
@@ -743,10 +847,10 @@ json_object *ipc_json_describe_node_recursive(struct sway_node *node) { | |||
743 | } | 847 | } |
744 | break; | 848 | break; |
745 | case N_CONTAINER: | 849 | case N_CONTAINER: |
746 | if (node->sway_container->children) { | 850 | if (node->sway_container->pending.children) { |
747 | for (i = 0; i < node->sway_container->children->length; ++i) { | 851 | for (i = 0; i < node->sway_container->pending.children->length; ++i) { |
748 | struct sway_container *child = | 852 | struct sway_container *child = |
749 | node->sway_container->children->items[i]; | 853 | node->sway_container->pending.children->items[i]; |
750 | json_object_array_add(children, | 854 | json_object_array_add(children, |
751 | ipc_json_describe_node_recursive(&child->node)); | 855 | ipc_json_describe_node_recursive(&child->node)); |
752 | } | 856 | } |
@@ -758,6 +862,7 @@ json_object *ipc_json_describe_node_recursive(struct sway_node *node) { | |||
758 | return object; | 862 | return object; |
759 | } | 863 | } |
760 | 864 | ||
865 | #if WLR_HAS_LIBINPUT_BACKEND | ||
761 | static json_object *describe_libinput_device(struct libinput_device *device) { | 866 | static json_object *describe_libinput_device(struct libinput_device *device) { |
762 | json_object *object = json_object_new_object(); | 867 | json_object *object = json_object_new_object(); |
763 | 868 | ||
@@ -841,6 +946,11 @@ static json_object *describe_libinput_device(struct libinput_device *device) { | |||
841 | case LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE: | 946 | case LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE: |
842 | accel_profile = "adaptive"; | 947 | accel_profile = "adaptive"; |
843 | break; | 948 | break; |
949 | #if HAVE_LIBINPUT_CONFIG_ACCEL_PROFILE_CUSTOM | ||
950 | case LIBINPUT_CONFIG_ACCEL_PROFILE_CUSTOM: | ||
951 | accel_profile = "custom"; | ||
952 | break; | ||
953 | #endif | ||
844 | } | 954 | } |
845 | json_object_object_add(object, "accel_profile", | 955 | json_object_object_add(object, "accel_profile", |
846 | json_object_new_string(accel_profile)); | 956 | json_object_new_string(accel_profile)); |
@@ -920,6 +1030,17 @@ static json_object *describe_libinput_device(struct libinput_device *device) { | |||
920 | uint32_t button = libinput_device_config_scroll_get_button(device); | 1030 | uint32_t button = libinput_device_config_scroll_get_button(device); |
921 | json_object_object_add(object, "scroll_button", | 1031 | json_object_object_add(object, "scroll_button", |
922 | json_object_new_int(button)); | 1032 | json_object_new_int(button)); |
1033 | const char *lock = "unknown"; | ||
1034 | switch (libinput_device_config_scroll_get_button_lock(device)) { | ||
1035 | case LIBINPUT_CONFIG_SCROLL_BUTTON_LOCK_ENABLED: | ||
1036 | lock = "enabled"; | ||
1037 | break; | ||
1038 | case LIBINPUT_CONFIG_SCROLL_BUTTON_LOCK_DISABLED: | ||
1039 | lock = "disabled"; | ||
1040 | break; | ||
1041 | } | ||
1042 | json_object_object_add(object, "scroll_button_lock", | ||
1043 | json_object_new_string(lock)); | ||
923 | } | 1044 | } |
924 | } | 1045 | } |
925 | 1046 | ||
@@ -936,6 +1057,19 @@ static json_object *describe_libinput_device(struct libinput_device *device) { | |||
936 | json_object_object_add(object, "dwt", json_object_new_string(dwt)); | 1057 | json_object_object_add(object, "dwt", json_object_new_string(dwt)); |
937 | } | 1058 | } |
938 | 1059 | ||
1060 | if (libinput_device_config_dwtp_is_available(device)) { | ||
1061 | const char *dwtp = "unknown"; | ||
1062 | switch (libinput_device_config_dwtp_get_enabled(device)) { | ||
1063 | case LIBINPUT_CONFIG_DWTP_ENABLED: | ||
1064 | dwtp = "enabled"; | ||
1065 | break; | ||
1066 | case LIBINPUT_CONFIG_DWTP_DISABLED: | ||
1067 | dwtp = "disabled"; | ||
1068 | break; | ||
1069 | } | ||
1070 | json_object_object_add(object, "dwtp", json_object_new_string(dwtp)); | ||
1071 | } | ||
1072 | |||
939 | if (libinput_device_config_calibration_has_matrix(device)) { | 1073 | if (libinput_device_config_calibration_has_matrix(device)) { |
940 | float matrix[6]; | 1074 | float matrix[6]; |
941 | libinput_device_config_calibration_get_matrix(device, matrix); | 1075 | libinput_device_config_calibration_get_matrix(device, matrix); |
@@ -950,6 +1084,7 @@ static json_object *describe_libinput_device(struct libinput_device *device) { | |||
950 | 1084 | ||
951 | return object; | 1085 | return object; |
952 | } | 1086 | } |
1087 | #endif | ||
953 | 1088 | ||
954 | json_object *ipc_json_describe_input(struct sway_input_device *device) { | 1089 | json_object *ipc_json_describe_input(struct sway_input_device *device) { |
955 | if (!(sway_assert(device, "Device must not be null"))) { | 1090 | if (!(sway_assert(device, "Device must not be null"))) { |
@@ -962,19 +1097,21 @@ json_object *ipc_json_describe_input(struct sway_input_device *device) { | |||
962 | json_object_new_string(device->identifier)); | 1097 | json_object_new_string(device->identifier)); |
963 | json_object_object_add(object, "name", | 1098 | json_object_object_add(object, "name", |
964 | json_object_new_string(device->wlr_device->name)); | 1099 | json_object_new_string(device->wlr_device->name)); |
965 | json_object_object_add(object, "vendor", | ||
966 | json_object_new_int(device->wlr_device->vendor)); | ||
967 | json_object_object_add(object, "product", | ||
968 | json_object_new_int(device->wlr_device->product)); | ||
969 | json_object_object_add(object, "type", | 1100 | json_object_object_add(object, "type", |
970 | json_object_new_string( | 1101 | json_object_new_string( |
971 | input_device_get_type(device))); | 1102 | input_device_get_type(device))); |
972 | 1103 | ||
973 | if (device->wlr_device->type == WLR_INPUT_DEVICE_KEYBOARD) { | 1104 | if (device->wlr_device->type == WLR_INPUT_DEVICE_KEYBOARD) { |
974 | struct wlr_keyboard *keyboard = device->wlr_device->keyboard; | 1105 | struct wlr_keyboard *keyboard = |
1106 | wlr_keyboard_from_input_device(device->wlr_device); | ||
975 | struct xkb_keymap *keymap = keyboard->keymap; | 1107 | struct xkb_keymap *keymap = keyboard->keymap; |
976 | struct xkb_state *state = keyboard->xkb_state; | 1108 | struct xkb_state *state = keyboard->xkb_state; |
977 | 1109 | ||
1110 | json_object_object_add(object, "repeat_delay", | ||
1111 | json_object_new_int(keyboard->repeat_info.delay)); | ||
1112 | json_object_object_add(object, "repeat_rate", | ||
1113 | json_object_new_int(keyboard->repeat_info.rate)); | ||
1114 | |||
978 | json_object *layouts_arr = json_object_new_array(); | 1115 | json_object *layouts_arr = json_object_new_array(); |
979 | json_object_object_add(object, "xkb_layout_names", layouts_arr); | 1116 | json_object_object_add(object, "xkb_layout_names", layouts_arr); |
980 | 1117 | ||
@@ -996,12 +1133,29 @@ json_object *ipc_json_describe_input(struct sway_input_device *device) { | |||
996 | } | 1133 | } |
997 | } | 1134 | } |
998 | 1135 | ||
1136 | if (device->wlr_device->type == WLR_INPUT_DEVICE_POINTER) { | ||
1137 | struct input_config *ic = input_device_get_config(device); | ||
1138 | float scroll_factor = 1.0f; | ||
1139 | if (ic != NULL && !isnan(ic->scroll_factor) && | ||
1140 | ic->scroll_factor != FLT_MIN) { | ||
1141 | scroll_factor = ic->scroll_factor; | ||
1142 | } | ||
1143 | json_object_object_add(object, "scroll_factor", | ||
1144 | json_object_new_double(scroll_factor)); | ||
1145 | } | ||
1146 | |||
1147 | #if WLR_HAS_LIBINPUT_BACKEND | ||
999 | if (wlr_input_device_is_libinput(device->wlr_device)) { | 1148 | if (wlr_input_device_is_libinput(device->wlr_device)) { |
1000 | struct libinput_device *libinput_dev; | 1149 | struct libinput_device *libinput_dev; |
1001 | libinput_dev = wlr_libinput_get_device_handle(device->wlr_device); | 1150 | libinput_dev = wlr_libinput_get_device_handle(device->wlr_device); |
1002 | json_object_object_add(object, "libinput", | 1151 | json_object_object_add(object, "libinput", |
1003 | describe_libinput_device(libinput_dev)); | 1152 | describe_libinput_device(libinput_dev)); |
1153 | json_object_object_add(object, "vendor", | ||
1154 | json_object_new_int(libinput_device_get_id_vendor(libinput_dev))); | ||
1155 | json_object_object_add(object, "product", | ||
1156 | json_object_new_int(libinput_device_get_id_product(libinput_dev))); | ||
1004 | } | 1157 | } |
1158 | #endif | ||
1005 | 1159 | ||
1006 | return object; | 1160 | return object; |
1007 | } | 1161 | } |
@@ -1109,7 +1263,9 @@ json_object *ipc_json_describe_bar_config(struct bar_config *bar) { | |||
1109 | json_object_object_add(json, "verbose", | 1263 | json_object_object_add(json, "verbose", |
1110 | json_object_new_boolean(bar->verbose)); | 1264 | json_object_new_boolean(bar->verbose)); |
1111 | json_object_object_add(json, "pango_markup", | 1265 | json_object_object_add(json, "pango_markup", |
1112 | json_object_new_boolean(bar->pango_markup)); | 1266 | json_object_new_boolean(bar->pango_markup == PANGO_MARKUP_DEFAULT |
1267 | ? config->pango_markup | ||
1268 | : bar->pango_markup)); | ||
1113 | 1269 | ||
1114 | json_object *colors = json_object_new_object(); | 1270 | json_object *colors = json_object_new_object(); |
1115 | json_object_object_add(colors, "background", | 1271 | json_object_object_add(colors, "background", |
diff --git a/sway/ipc-server.c b/sway/ipc-server.c index aad9a7b5..7f353c0e 100644 --- a/sway/ipc-server.c +++ b/sway/ipc-server.c | |||
@@ -1,5 +1,4 @@ | |||
1 | // See https://i3wm.org/docs/ipc.html for protocol information | 1 | // See https://i3wm.org/docs/ipc.html for protocol information |
2 | #define _POSIX_C_SOURCE 200112L | ||
3 | #include <linux/input-event-codes.h> | 2 | #include <linux/input-event-codes.h> |
4 | #include <assert.h> | 3 | #include <assert.h> |
5 | #include <errno.h> | 4 | #include <errno.h> |
@@ -56,7 +55,6 @@ struct ipc_client { | |||
56 | enum ipc_command_type pending_type; | 55 | enum ipc_command_type pending_type; |
57 | }; | 56 | }; |
58 | 57 | ||
59 | struct sockaddr_un *ipc_user_sockaddr(void); | ||
60 | int ipc_handle_connection(int fd, uint32_t mask, void *data); | 58 | int ipc_handle_connection(int fd, uint32_t mask, void *data); |
61 | int ipc_client_handle_readable(int client_fd, uint32_t mask, void *data); | 59 | int ipc_client_handle_readable(int client_fd, uint32_t mask, void *data); |
62 | int ipc_client_handle_writable(int client_fd, uint32_t mask, void *data); | 60 | int ipc_client_handle_writable(int client_fd, uint32_t mask, void *data); |
@@ -150,7 +148,6 @@ struct sockaddr_un *ipc_user_sockaddr(void) { | |||
150 | int ipc_handle_connection(int fd, uint32_t mask, void *data) { | 148 | int ipc_handle_connection(int fd, uint32_t mask, void *data) { |
151 | (void) fd; | 149 | (void) fd; |
152 | struct sway_server *server = data; | 150 | struct sway_server *server = data; |
153 | sway_log(SWAY_DEBUG, "Event on IPC listening socket"); | ||
154 | assert(mask == WL_EVENT_READABLE); | 151 | assert(mask == WL_EVENT_READABLE); |
155 | 152 | ||
156 | int client_fd = accept(ipc_socket, NULL, NULL); | 153 | int client_fd = accept(ipc_socket, NULL, NULL); |
@@ -211,13 +208,10 @@ int ipc_client_handle_readable(int client_fd, uint32_t mask, void *data) { | |||
211 | } | 208 | } |
212 | 209 | ||
213 | if (mask & WL_EVENT_HANGUP) { | 210 | if (mask & WL_EVENT_HANGUP) { |
214 | sway_log(SWAY_DEBUG, "Client %d hung up", client->fd); | ||
215 | ipc_client_disconnect(client); | 211 | ipc_client_disconnect(client); |
216 | return 0; | 212 | return 0; |
217 | } | 213 | } |
218 | 214 | ||
219 | sway_log(SWAY_DEBUG, "Client %d readable", client->fd); | ||
220 | |||
221 | int read_available; | 215 | int read_available; |
222 | if (ioctl(client_fd, FIONREAD, &read_available) == -1) { | 216 | if (ioctl(client_fd, FIONREAD, &read_available) == -1) { |
223 | sway_log_errno(SWAY_INFO, "Unable to read IPC socket buffer size"); | 217 | sway_log_errno(SWAY_INFO, "Unable to read IPC socket buffer size"); |
@@ -513,6 +507,20 @@ void ipc_event_input(const char *change, struct sway_input_device *device) { | |||
513 | json_object_put(json); | 507 | json_object_put(json); |
514 | } | 508 | } |
515 | 509 | ||
510 | void ipc_event_output(void) { | ||
511 | if (!ipc_has_event_listeners(IPC_EVENT_OUTPUT)) { | ||
512 | return; | ||
513 | } | ||
514 | sway_log(SWAY_DEBUG, "Sending output event"); | ||
515 | |||
516 | json_object *json = json_object_new_object(); | ||
517 | json_object_object_add(json, "change", json_object_new_string("unspecified")); | ||
518 | |||
519 | const char *json_string = json_object_to_json_string(json); | ||
520 | ipc_send_event(json_string, IPC_EVENT_OUTPUT); | ||
521 | json_object_put(json); | ||
522 | } | ||
523 | |||
516 | int ipc_client_handle_writable(int client_fd, uint32_t mask, void *data) { | 524 | int ipc_client_handle_writable(int client_fd, uint32_t mask, void *data) { |
517 | struct ipc_client *client = data; | 525 | struct ipc_client *client = data; |
518 | 526 | ||
@@ -523,7 +531,6 @@ int ipc_client_handle_writable(int client_fd, uint32_t mask, void *data) { | |||
523 | } | 531 | } |
524 | 532 | ||
525 | if (mask & WL_EVENT_HANGUP) { | 533 | if (mask & WL_EVENT_HANGUP) { |
526 | sway_log(SWAY_DEBUG, "Client %d hung up", client->fd); | ||
527 | ipc_client_disconnect(client); | 534 | ipc_client_disconnect(client); |
528 | return 0; | 535 | return 0; |
529 | } | 536 | } |
@@ -532,8 +539,6 @@ int ipc_client_handle_writable(int client_fd, uint32_t mask, void *data) { | |||
532 | return 0; | 539 | return 0; |
533 | } | 540 | } |
534 | 541 | ||
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); | 542 | ssize_t written = write(client->fd, client->write_buffer, client->write_buffer_len); |
538 | 543 | ||
539 | if (written == -1 && errno == EAGAIN) { | 544 | if (written == -1 && errno == EAGAIN) { |
@@ -687,11 +692,17 @@ void ipc_client_handle_command(struct ipc_client *client, uint32_t payload_lengt | |||
687 | } | 692 | } |
688 | struct sway_output *output; | 693 | struct sway_output *output; |
689 | wl_list_for_each(output, &root->all_outputs, link) { | 694 | wl_list_for_each(output, &root->all_outputs, link) { |
690 | if (!output->enabled && output != root->noop_output) { | 695 | if (!output->enabled && output != root->fallback_output) { |
691 | json_object_array_add(outputs, | 696 | json_object_array_add(outputs, |
692 | ipc_json_describe_disabled_output(output)); | 697 | ipc_json_describe_disabled_output(output)); |
693 | } | 698 | } |
694 | } | 699 | } |
700 | |||
701 | for (int i = 0; i < root->non_desktop_outputs->length; i++) { | ||
702 | struct sway_output_non_desktop *non_desktop_output = root->non_desktop_outputs->items[i]; | ||
703 | json_object_array_add(outputs, ipc_json_describe_non_desktop_output(non_desktop_output)); | ||
704 | } | ||
705 | |||
695 | const char *json_string = json_object_to_json_string(outputs); | 706 | const char *json_string = json_object_to_json_string(outputs); |
696 | ipc_send_reply(client, payload_type, json_string, | 707 | ipc_send_reply(client, payload_type, json_string, |
697 | (uint32_t)strlen(json_string)); | 708 | (uint32_t)strlen(json_string)); |
@@ -727,6 +738,8 @@ void ipc_client_handle_command(struct ipc_client *client, uint32_t payload_lengt | |||
727 | const char *event_type = json_object_get_string(json_object_array_get_idx(request, i)); | 738 | const char *event_type = json_object_get_string(json_object_array_get_idx(request, i)); |
728 | if (strcmp(event_type, "workspace") == 0) { | 739 | if (strcmp(event_type, "workspace") == 0) { |
729 | client->subscribed_events |= event_mask(IPC_EVENT_WORKSPACE); | 740 | client->subscribed_events |= event_mask(IPC_EVENT_WORKSPACE); |
741 | } else if (strcmp(event_type, "output") == 0) { | ||
742 | client->subscribed_events |= event_mask(IPC_EVENT_OUTPUT); | ||
730 | } else if (strcmp(event_type, "barconfig_update") == 0) { | 743 | } else if (strcmp(event_type, "barconfig_update") == 0) { |
731 | client->subscribed_events |= event_mask(IPC_EVENT_BARCONFIG_UPDATE); | 744 | client->subscribed_events |= event_mask(IPC_EVENT_BARCONFIG_UPDATE); |
732 | } else if (strcmp(event_type, "bar_state_update") == 0) { | 745 | } else if (strcmp(event_type, "bar_state_update") == 0) { |
@@ -911,7 +924,6 @@ void ipc_client_handle_command(struct ipc_client *client, uint32_t payload_lengt | |||
911 | 924 | ||
912 | exit_cleanup: | 925 | exit_cleanup: |
913 | free(buf); | 926 | free(buf); |
914 | return; | ||
915 | } | 927 | } |
916 | 928 | ||
917 | bool ipc_send_reply(struct ipc_client *client, enum ipc_command_type payload_type, | 929 | bool ipc_send_reply(struct ipc_client *client, enum ipc_command_type payload_type, |
@@ -955,7 +967,5 @@ bool ipc_send_reply(struct ipc_client *client, enum ipc_command_type payload_typ | |||
955 | ipc_client_handle_writable, client); | 967 | ipc_client_handle_writable, client); |
956 | } | 968 | } |
957 | 969 | ||
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; | 970 | return true; |
961 | } | 971 | } |
diff --git a/sway/lock.c b/sway/lock.c new file mode 100644 index 00000000..289e8ca4 --- /dev/null +++ b/sway/lock.c | |||
@@ -0,0 +1,354 @@ | |||
1 | #include <assert.h> | ||
2 | #include <wlr/types/wlr_scene.h> | ||
3 | #include <wlr/types/wlr_session_lock_v1.h> | ||
4 | #include "log.h" | ||
5 | #include "sway/input/cursor.h" | ||
6 | #include "sway/input/keyboard.h" | ||
7 | #include "sway/input/seat.h" | ||
8 | #include "sway/layers.h" | ||
9 | #include "sway/output.h" | ||
10 | #include "sway/server.h" | ||
11 | |||
12 | struct sway_session_lock_output { | ||
13 | struct wlr_scene_tree *tree; | ||
14 | struct wlr_scene_rect *background; | ||
15 | struct sway_session_lock *lock; | ||
16 | |||
17 | struct sway_output *output; | ||
18 | |||
19 | struct wl_list link; // sway_session_lock::outputs | ||
20 | |||
21 | struct wl_listener destroy; | ||
22 | struct wl_listener commit; | ||
23 | |||
24 | struct wlr_session_lock_surface_v1 *surface; | ||
25 | |||
26 | // invalid if surface is NULL | ||
27 | struct wl_listener surface_destroy; | ||
28 | struct wl_listener surface_map; | ||
29 | }; | ||
30 | |||
31 | static void focus_surface(struct sway_session_lock *lock, | ||
32 | struct wlr_surface *focused) { | ||
33 | lock->focused = focused; | ||
34 | |||
35 | struct sway_seat *seat; | ||
36 | wl_list_for_each(seat, &server.input->seats, link) { | ||
37 | seat_set_focus_surface(seat, focused, false); | ||
38 | } | ||
39 | } | ||
40 | |||
41 | static void refocus_output(struct sway_session_lock_output *output) { | ||
42 | // Move the seat focus to another surface if one is available | ||
43 | if (output->lock->focused == output->surface->surface) { | ||
44 | struct wlr_surface *next_focus = NULL; | ||
45 | |||
46 | struct sway_session_lock_output *candidate; | ||
47 | wl_list_for_each(candidate, &output->lock->outputs, link) { | ||
48 | if (candidate == output || !candidate->surface) { | ||
49 | continue; | ||
50 | } | ||
51 | |||
52 | if (candidate->surface->surface->mapped) { | ||
53 | next_focus = candidate->surface->surface; | ||
54 | break; | ||
55 | } | ||
56 | } | ||
57 | |||
58 | focus_surface(output->lock, next_focus); | ||
59 | } | ||
60 | } | ||
61 | |||
62 | static void handle_surface_map(struct wl_listener *listener, void *data) { | ||
63 | struct sway_session_lock_output *surf = wl_container_of(listener, surf, surface_map); | ||
64 | if (surf->lock->focused == NULL) { | ||
65 | focus_surface(surf->lock, surf->surface->surface); | ||
66 | } | ||
67 | cursor_rebase_all(); | ||
68 | } | ||
69 | |||
70 | static void handle_surface_destroy(struct wl_listener *listener, void *data) { | ||
71 | struct sway_session_lock_output *output = | ||
72 | wl_container_of(listener, output, surface_destroy); | ||
73 | refocus_output(output); | ||
74 | |||
75 | sway_assert(output->surface, "Trying to destroy a surface that the lock doesn't think exists"); | ||
76 | output->surface = NULL; | ||
77 | wl_list_remove(&output->surface_destroy.link); | ||
78 | wl_list_remove(&output->surface_map.link); | ||
79 | } | ||
80 | |||
81 | static void lock_output_reconfigure(struct sway_session_lock_output *output) { | ||
82 | int width = output->output->width; | ||
83 | int height = output->output->height; | ||
84 | |||
85 | wlr_scene_rect_set_size(output->background, width, height); | ||
86 | |||
87 | if (output->surface) { | ||
88 | wlr_session_lock_surface_v1_configure(output->surface, width, height); | ||
89 | } | ||
90 | } | ||
91 | |||
92 | static void handle_new_surface(struct wl_listener *listener, void *data) { | ||
93 | struct sway_session_lock *lock = wl_container_of(listener, lock, new_surface); | ||
94 | struct wlr_session_lock_surface_v1 *lock_surface = data; | ||
95 | struct sway_output *output = lock_surface->output->data; | ||
96 | |||
97 | sway_log(SWAY_DEBUG, "new lock layer surface"); | ||
98 | |||
99 | struct sway_session_lock_output *current_lock_output, *lock_output = NULL; | ||
100 | wl_list_for_each(current_lock_output, &lock->outputs, link) { | ||
101 | if (current_lock_output->output == output) { | ||
102 | lock_output = current_lock_output; | ||
103 | break; | ||
104 | } | ||
105 | } | ||
106 | sway_assert(lock_output, "Couldn't find output to lock"); | ||
107 | sway_assert(!lock_output->surface, "Tried to reassign a surface to an existing output"); | ||
108 | |||
109 | lock_output->surface = lock_surface; | ||
110 | |||
111 | wlr_scene_subsurface_tree_create(lock_output->tree, lock_surface->surface); | ||
112 | |||
113 | lock_output->surface_destroy.notify = handle_surface_destroy; | ||
114 | wl_signal_add(&lock_surface->events.destroy, &lock_output->surface_destroy); | ||
115 | lock_output->surface_map.notify = handle_surface_map; | ||
116 | wl_signal_add(&lock_surface->surface->events.map, &lock_output->surface_map); | ||
117 | |||
118 | lock_output_reconfigure(lock_output); | ||
119 | } | ||
120 | |||
121 | static void sway_session_lock_output_destroy(struct sway_session_lock_output *output) { | ||
122 | if (output->surface) { | ||
123 | refocus_output(output); | ||
124 | wl_list_remove(&output->surface_destroy.link); | ||
125 | wl_list_remove(&output->surface_map.link); | ||
126 | } | ||
127 | |||
128 | wl_list_remove(&output->commit.link); | ||
129 | wl_list_remove(&output->destroy.link); | ||
130 | wl_list_remove(&output->link); | ||
131 | |||
132 | free(output); | ||
133 | } | ||
134 | |||
135 | static void lock_node_handle_destroy(struct wl_listener *listener, void *data) { | ||
136 | struct sway_session_lock_output *output = | ||
137 | wl_container_of(listener, output, destroy); | ||
138 | sway_session_lock_output_destroy(output); | ||
139 | } | ||
140 | |||
141 | static void lock_output_handle_commit(struct wl_listener *listener, void *data) { | ||
142 | struct wlr_output_event_commit *event = data; | ||
143 | struct sway_session_lock_output *output = | ||
144 | wl_container_of(listener, output, commit); | ||
145 | if (event->state->committed & ( | ||
146 | WLR_OUTPUT_STATE_MODE | | ||
147 | WLR_OUTPUT_STATE_SCALE | | ||
148 | WLR_OUTPUT_STATE_TRANSFORM)) { | ||
149 | lock_output_reconfigure(output); | ||
150 | } | ||
151 | } | ||
152 | |||
153 | static struct sway_session_lock_output *session_lock_output_create( | ||
154 | struct sway_session_lock *lock, struct sway_output *output) { | ||
155 | struct sway_session_lock_output *lock_output = calloc(1, sizeof(*lock_output)); | ||
156 | if (!lock_output) { | ||
157 | sway_log(SWAY_ERROR, "failed to allocate a session lock output"); | ||
158 | return NULL; | ||
159 | } | ||
160 | |||
161 | struct wlr_scene_tree *tree = wlr_scene_tree_create(output->layers.session_lock); | ||
162 | if (!tree) { | ||
163 | sway_log(SWAY_ERROR, "failed to allocate a session lock output scene tree"); | ||
164 | free(lock_output); | ||
165 | return NULL; | ||
166 | } | ||
167 | |||
168 | struct wlr_scene_rect *background = wlr_scene_rect_create(tree, 0, 0, (float[4]){ | ||
169 | lock->abandoned ? 1.f : 0.f, | ||
170 | 0.f, | ||
171 | 0.f, | ||
172 | 1.f, | ||
173 | }); | ||
174 | if (!background) { | ||
175 | sway_log(SWAY_ERROR, "failed to allocate a session lock output scene background"); | ||
176 | wlr_scene_node_destroy(&tree->node); | ||
177 | free(lock_output); | ||
178 | return NULL; | ||
179 | } | ||
180 | |||
181 | lock_output->output = output; | ||
182 | lock_output->tree = tree; | ||
183 | lock_output->background = background; | ||
184 | lock_output->lock = lock; | ||
185 | |||
186 | lock_output->destroy.notify = lock_node_handle_destroy; | ||
187 | wl_signal_add(&tree->node.events.destroy, &lock_output->destroy); | ||
188 | |||
189 | lock_output->commit.notify = lock_output_handle_commit; | ||
190 | wl_signal_add(&output->wlr_output->events.commit, &lock_output->commit); | ||
191 | |||
192 | lock_output_reconfigure(lock_output); | ||
193 | |||
194 | wl_list_insert(&lock->outputs, &lock_output->link); | ||
195 | |||
196 | return lock_output; | ||
197 | } | ||
198 | |||
199 | static void sway_session_lock_destroy(struct sway_session_lock* lock) { | ||
200 | struct sway_session_lock_output *lock_output, *tmp_lock_output; | ||
201 | wl_list_for_each_safe(lock_output, tmp_lock_output, &lock->outputs, link) { | ||
202 | // destroying the node will also destroy the whole lock output | ||
203 | wlr_scene_node_destroy(&lock_output->tree->node); | ||
204 | } | ||
205 | |||
206 | if (server.session_lock.lock == lock) { | ||
207 | server.session_lock.lock = NULL; | ||
208 | } | ||
209 | |||
210 | if (!lock->abandoned) { | ||
211 | wl_list_remove(&lock->destroy.link); | ||
212 | wl_list_remove(&lock->unlock.link); | ||
213 | wl_list_remove(&lock->new_surface.link); | ||
214 | } | ||
215 | |||
216 | free(lock); | ||
217 | } | ||
218 | |||
219 | static void handle_unlock(struct wl_listener *listener, void *data) { | ||
220 | struct sway_session_lock *lock = wl_container_of(listener, lock, unlock); | ||
221 | sway_log(SWAY_DEBUG, "session unlocked"); | ||
222 | |||
223 | sway_session_lock_destroy(lock); | ||
224 | |||
225 | struct sway_seat *seat; | ||
226 | wl_list_for_each(seat, &server.input->seats, link) { | ||
227 | // copied from seat_set_focus_layer -- deduplicate? | ||
228 | struct sway_node *previous = seat_get_focus_inactive(seat, &root->node); | ||
229 | if (previous) { | ||
230 | // Hack to get seat to re-focus the return value of get_focus | ||
231 | seat_set_focus(seat, NULL); | ||
232 | seat_set_focus(seat, previous); | ||
233 | } | ||
234 | } | ||
235 | |||
236 | // Triggers a refocus of the topmost surface layer if necessary | ||
237 | // TODO: Make layer surface focus per-output based on cursor position | ||
238 | for (int i = 0; i < root->outputs->length; ++i) { | ||
239 | struct sway_output *output = root->outputs->items[i]; | ||
240 | arrange_layers(output); | ||
241 | } | ||
242 | } | ||
243 | |||
244 | static void handle_abandon(struct wl_listener *listener, void *data) { | ||
245 | struct sway_session_lock *lock = wl_container_of(listener, lock, destroy); | ||
246 | sway_log(SWAY_INFO, "session lock abandoned"); | ||
247 | |||
248 | struct sway_session_lock_output *lock_output; | ||
249 | wl_list_for_each(lock_output, &lock->outputs, link) { | ||
250 | wlr_scene_rect_set_color(lock_output->background, | ||
251 | (float[4]){ 1.f, 0.f, 0.f, 1.f }); | ||
252 | } | ||
253 | |||
254 | lock->abandoned = true; | ||
255 | wl_list_remove(&lock->destroy.link); | ||
256 | wl_list_remove(&lock->unlock.link); | ||
257 | wl_list_remove(&lock->new_surface.link); | ||
258 | } | ||
259 | |||
260 | static void handle_session_lock(struct wl_listener *listener, void *data) { | ||
261 | struct wlr_session_lock_v1 *lock = data; | ||
262 | struct wl_client *client = wl_resource_get_client(lock->resource); | ||
263 | |||
264 | if (server.session_lock.lock) { | ||
265 | if (server.session_lock.lock->abandoned) { | ||
266 | sway_log(SWAY_INFO, "Replacing abandoned lock"); | ||
267 | sway_session_lock_destroy(server.session_lock.lock); | ||
268 | } else { | ||
269 | sway_log(SWAY_ERROR, "Cannot lock an already locked session"); | ||
270 | wlr_session_lock_v1_destroy(lock); | ||
271 | return; | ||
272 | } | ||
273 | } | ||
274 | |||
275 | struct sway_session_lock *sway_lock = calloc(1, sizeof(*sway_lock)); | ||
276 | if (!sway_lock) { | ||
277 | sway_log(SWAY_ERROR, "failed to allocate a session lock object"); | ||
278 | wlr_session_lock_v1_destroy(lock); | ||
279 | return; | ||
280 | } | ||
281 | |||
282 | wl_list_init(&sway_lock->outputs); | ||
283 | |||
284 | sway_log(SWAY_DEBUG, "session locked"); | ||
285 | |||
286 | struct sway_seat *seat; | ||
287 | wl_list_for_each(seat, &server.input->seats, link) { | ||
288 | seat_unfocus_unless_client(seat, client); | ||
289 | } | ||
290 | |||
291 | struct sway_output *output; | ||
292 | wl_list_for_each(output, &root->all_outputs, link) { | ||
293 | sway_session_lock_add_output(sway_lock, output); | ||
294 | } | ||
295 | |||
296 | sway_lock->new_surface.notify = handle_new_surface; | ||
297 | wl_signal_add(&lock->events.new_surface, &sway_lock->new_surface); | ||
298 | sway_lock->unlock.notify = handle_unlock; | ||
299 | wl_signal_add(&lock->events.unlock, &sway_lock->unlock); | ||
300 | sway_lock->destroy.notify = handle_abandon; | ||
301 | wl_signal_add(&lock->events.destroy, &sway_lock->destroy); | ||
302 | |||
303 | wlr_session_lock_v1_send_locked(lock); | ||
304 | server.session_lock.lock = sway_lock; | ||
305 | } | ||
306 | |||
307 | static void handle_session_lock_destroy(struct wl_listener *listener, void *data) { | ||
308 | // if the server shuts down while a lock is active, destroy the lock | ||
309 | if (server.session_lock.lock) { | ||
310 | sway_session_lock_destroy(server.session_lock.lock); | ||
311 | } | ||
312 | |||
313 | wl_list_remove(&server.session_lock.new_lock.link); | ||
314 | wl_list_remove(&server.session_lock.manager_destroy.link); | ||
315 | |||
316 | server.session_lock.manager = NULL; | ||
317 | } | ||
318 | |||
319 | void sway_session_lock_add_output(struct sway_session_lock *lock, | ||
320 | struct sway_output *output) { | ||
321 | struct sway_session_lock_output *lock_output = | ||
322 | session_lock_output_create(lock, output); | ||
323 | |||
324 | // if we run out of memory while trying to lock the screen, the best we | ||
325 | // can do is kill the sway process. Security conscious users will have | ||
326 | // the sway session fall back to a login shell. | ||
327 | if (!lock_output) { | ||
328 | sway_log(SWAY_ERROR, "aborting: failed to allocate a lock output"); | ||
329 | abort(); | ||
330 | } | ||
331 | } | ||
332 | |||
333 | bool sway_session_lock_has_surface(struct sway_session_lock *lock, | ||
334 | struct wlr_surface *surface) { | ||
335 | struct sway_session_lock_output *lock_output; | ||
336 | wl_list_for_each(lock_output, &lock->outputs, link) { | ||
337 | if (lock_output->surface && lock_output->surface->surface == surface) { | ||
338 | return true; | ||
339 | } | ||
340 | } | ||
341 | |||
342 | return false; | ||
343 | } | ||
344 | |||
345 | void sway_session_lock_init(void) { | ||
346 | server.session_lock.manager = wlr_session_lock_manager_v1_create(server.wl_display); | ||
347 | |||
348 | server.session_lock.new_lock.notify = handle_session_lock; | ||
349 | server.session_lock.manager_destroy.notify = handle_session_lock_destroy; | ||
350 | wl_signal_add(&server.session_lock.manager->events.new_lock, | ||
351 | &server.session_lock.new_lock); | ||
352 | wl_signal_add(&server.session_lock.manager->events.destroy, | ||
353 | &server.session_lock.manager_destroy); | ||
354 | } | ||
diff --git a/sway/main.c b/sway/main.c index 0c219fb3..1c4939aa 100644 --- a/sway/main.c +++ b/sway/main.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <getopt.h> | 1 | #include <getopt.h> |
3 | #include <pango/pangocairo.h> | 2 | #include <pango/pangocairo.h> |
4 | #include <signal.h> | 3 | #include <signal.h> |
@@ -6,12 +5,14 @@ | |||
6 | #include <stdio.h> | 5 | #include <stdio.h> |
7 | #include <stdlib.h> | 6 | #include <stdlib.h> |
8 | #include <string.h> | 7 | #include <string.h> |
8 | #include <sys/resource.h> | ||
9 | #include <sys/stat.h> | 9 | #include <sys/stat.h> |
10 | #include <sys/types.h> | 10 | #include <sys/types.h> |
11 | #include <sys/wait.h> | 11 | #include <sys/wait.h> |
12 | #include <sys/un.h> | 12 | #include <sys/un.h> |
13 | #include <unistd.h> | 13 | #include <unistd.h> |
14 | #include <wlr/util/log.h> | 14 | #include <wlr/util/log.h> |
15 | #include <wlr/version.h> | ||
15 | #include "sway/commands.h" | 16 | #include "sway/commands.h" |
16 | #include "sway/config.h" | 17 | #include "sway/config.h" |
17 | #include "sway/server.h" | 18 | #include "sway/server.h" |
@@ -26,6 +27,7 @@ | |||
26 | 27 | ||
27 | static bool terminate_request = false; | 28 | static bool terminate_request = false; |
28 | static int exit_value = 0; | 29 | static int exit_value = 0; |
30 | static struct rlimit original_nofile_rlimit = {0}; | ||
29 | struct sway_server server = {0}; | 31 | struct sway_server server = {0}; |
30 | struct sway_debug debug = {0}; | 32 | struct sway_debug debug = {0}; |
31 | 33 | ||
@@ -46,81 +48,6 @@ void sig_handler(int signal) { | |||
46 | sway_terminate(EXIT_SUCCESS); | 48 | sway_terminate(EXIT_SUCCESS); |
47 | } | 49 | } |
48 | 50 | ||
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) { | ||
87 | FILE *f = fopen("/proc/modules", "r"); | ||
88 | if (!f) { | ||
89 | return; | ||
90 | } | ||
91 | char *line = NULL; | ||
92 | size_t line_size = 0; | ||
93 | while (getline(&line, &line_size, f) != -1) { | ||
94 | if (strncmp(line, "nvidia ", 7) == 0) { | ||
95 | if (allow_unsupported_gpu) { | ||
96 | sway_log(SWAY_ERROR, | ||
97 | "!!! Proprietary Nvidia drivers are in use !!!"); | ||
98 | } else { | ||
99 | sway_log(SWAY_ERROR, | ||
100 | "Proprietary Nvidia drivers are NOT supported. " | ||
101 | "Use Nouveau. To launch sway anyway, launch with " | ||
102 | "--my-next-gpu-wont-be-nvidia and DO NOT report issues."); | ||
103 | exit(EXIT_FAILURE); | ||
104 | } | ||
105 | break; | ||
106 | } | ||
107 | if (strstr(line, "fglrx")) { | ||
108 | if (allow_unsupported_gpu) { | ||
109 | sway_log(SWAY_ERROR, | ||
110 | "!!! Proprietary AMD drivers are in use !!!"); | ||
111 | } else { | ||
112 | sway_log(SWAY_ERROR, "Proprietary AMD drivers do NOT support " | ||
113 | "Wayland. Use radeon. To try anyway, launch sway with " | ||
114 | "--unsupported-gpu and DO NOT report issues."); | ||
115 | exit(EXIT_FAILURE); | ||
116 | } | ||
117 | break; | ||
118 | } | ||
119 | } | ||
120 | free(line); | ||
121 | fclose(f); | ||
122 | } | ||
123 | |||
124 | void run_as_ipc_client(char *command, char *socket_path) { | 51 | void run_as_ipc_client(char *command, char *socket_path) { |
125 | int socketfd = ipc_open_socket(socket_path); | 52 | int socketfd = ipc_open_socket(socket_path); |
126 | uint32_t len = strlen(command); | 53 | uint32_t len = strlen(command); |
@@ -184,33 +111,49 @@ static void log_kernel(void) { | |||
184 | pclose(f); | 111 | pclose(f); |
185 | } | 112 | } |
186 | 113 | ||
187 | 114 | static bool detect_suid(void) { | |
188 | static bool drop_permissions(void) { | 115 | if (geteuid() != 0 && getegid() != 0) { |
189 | if (getuid() != geteuid() || getgid() != getegid()) { | 116 | 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 | } | 117 | } |
200 | if (setgid(0) != -1 || setuid(0) != -1) { | 118 | |
201 | sway_log(SWAY_ERROR, "Unable to drop root (we shouldn't be able to " | 119 | if (getuid() == geteuid() && getgid() == getegid()) { |
202 | "restore it after setuid), refusing to start"); | ||
203 | return false; | 120 | return false; |
204 | } | 121 | } |
122 | |||
123 | sway_log(SWAY_ERROR, "SUID operation is no longer supported, refusing to start. " | ||
124 | "This check will be removed in a future release."); | ||
205 | return true; | 125 | return true; |
206 | } | 126 | } |
207 | 127 | ||
128 | static void increase_nofile_limit(void) { | ||
129 | if (getrlimit(RLIMIT_NOFILE, &original_nofile_rlimit) != 0) { | ||
130 | sway_log_errno(SWAY_ERROR, "Failed to bump max open files limit: " | ||
131 | "getrlimit(NOFILE) failed"); | ||
132 | return; | ||
133 | } | ||
134 | |||
135 | struct rlimit new_rlimit = original_nofile_rlimit; | ||
136 | new_rlimit.rlim_cur = new_rlimit.rlim_max; | ||
137 | if (setrlimit(RLIMIT_NOFILE, &new_rlimit) != 0) { | ||
138 | sway_log_errno(SWAY_ERROR, "Failed to bump max open files limit: " | ||
139 | "setrlimit(NOFILE) failed"); | ||
140 | sway_log(SWAY_INFO, "Running with %d max open files", | ||
141 | (int)original_nofile_rlimit.rlim_cur); | ||
142 | } | ||
143 | } | ||
144 | |||
145 | void restore_nofile_limit(void) { | ||
146 | if (original_nofile_rlimit.rlim_cur == 0) { | ||
147 | return; | ||
148 | } | ||
149 | if (setrlimit(RLIMIT_NOFILE, &original_nofile_rlimit) != 0) { | ||
150 | sway_log_errno(SWAY_ERROR, "Failed to restore max open files limit: " | ||
151 | "setrlimit(NOFILE) failed"); | ||
152 | } | ||
153 | } | ||
154 | |||
208 | void enable_debug_flag(const char *flag) { | 155 | void enable_debug_flag(const char *flag) { |
209 | if (strcmp(flag, "damage=highlight") == 0) { | 156 | if (strcmp(flag, "noatomic") == 0) { |
210 | debug.damage = DAMAGE_HIGHLIGHT; | ||
211 | } else if (strcmp(flag, "damage=rerender") == 0) { | ||
212 | debug.damage = DAMAGE_RERENDER; | ||
213 | } else if (strcmp(flag, "noatomic") == 0) { | ||
214 | debug.noatomic = true; | 157 | debug.noatomic = true; |
215 | } else if (strcmp(flag, "txn-wait") == 0) { | 158 | } else if (strcmp(flag, "txn-wait") == 0) { |
216 | debug.txn_wait = true; | 159 | debug.txn_wait = true; |
@@ -218,6 +161,8 @@ void enable_debug_flag(const char *flag) { | |||
218 | debug.txn_timings = true; | 161 | debug.txn_timings = true; |
219 | } else if (strncmp(flag, "txn-timeout=", 12) == 0) { | 162 | } else if (strncmp(flag, "txn-timeout=", 12) == 0) { |
220 | server.txn_timeout_ms = atoi(&flag[12]); | 163 | server.txn_timeout_ms = atoi(&flag[12]); |
164 | } else if (strcmp(flag, "legacy-wl-drm") == 0) { | ||
165 | debug.legacy_wl_drm = true; | ||
221 | } else { | 166 | } else { |
222 | sway_log(SWAY_ERROR, "Unknown debug flag: %s", flag); | 167 | sway_log(SWAY_ERROR, "Unknown debug flag: %s", flag); |
223 | } | 168 | } |
@@ -242,36 +187,35 @@ static void handle_wlr_log(enum wlr_log_importance importance, | |||
242 | _sway_vlog(convert_wlr_log_importance(importance), sway_fmt, args); | 187 | _sway_vlog(convert_wlr_log_importance(importance), sway_fmt, args); |
243 | } | 188 | } |
244 | 189 | ||
190 | static const struct option long_options[] = { | ||
191 | {"help", no_argument, NULL, 'h'}, | ||
192 | {"config", required_argument, NULL, 'c'}, | ||
193 | {"validate", no_argument, NULL, 'C'}, | ||
194 | {"debug", no_argument, NULL, 'd'}, | ||
195 | {"version", no_argument, NULL, 'v'}, | ||
196 | {"verbose", no_argument, NULL, 'V'}, | ||
197 | {"get-socketpath", no_argument, NULL, 'p'}, | ||
198 | {"unsupported-gpu", no_argument, NULL, 'u'}, | ||
199 | {0, 0, 0, 0} | ||
200 | }; | ||
201 | |||
202 | static const char usage[] = | ||
203 | "Usage: sway [options] [command]\n" | ||
204 | "\n" | ||
205 | " -h, --help Show help message and quit.\n" | ||
206 | " -c, --config <config> Specify a config file.\n" | ||
207 | " -C, --validate Check the validity of the config file, then exit.\n" | ||
208 | " -d, --debug Enables full logging, including debug information.\n" | ||
209 | " -v, --version Show the version number and quit.\n" | ||
210 | " -V, --verbose Enables more verbose logging.\n" | ||
211 | " --get-socketpath Gets the IPC socket path and prints it, then exits.\n" | ||
212 | "\n"; | ||
213 | |||
245 | int main(int argc, char **argv) { | 214 | int main(int argc, char **argv) { |
246 | static int verbose = 0, debug = 0, validate = 0, allow_unsupported_gpu = 0; | 215 | static bool verbose = false, debug = false, validate = 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 | 216 | ||
261 | char *config_path = NULL; | 217 | char *config_path = NULL; |
262 | 218 | ||
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; | 219 | int c; |
276 | while (1) { | 220 | while (1) { |
277 | int option_index = 0; | 221 | int option_index = 0; |
@@ -289,25 +233,25 @@ int main(int argc, char **argv) { | |||
289 | config_path = strdup(optarg); | 233 | config_path = strdup(optarg); |
290 | break; | 234 | break; |
291 | case 'C': // validate | 235 | case 'C': // validate |
292 | validate = 1; | 236 | validate = true; |
293 | break; | 237 | break; |
294 | case 'd': // debug | 238 | case 'd': // debug |
295 | debug = 1; | 239 | debug = true; |
296 | break; | 240 | break; |
297 | case 'D': // extended debug options | 241 | case 'D': // extended debug options |
298 | enable_debug_flag(optarg); | 242 | enable_debug_flag(optarg); |
299 | break; | 243 | break; |
300 | case 'u': | 244 | case 'u': |
301 | allow_unsupported_gpu = 1; | 245 | allow_unsupported_gpu = true; |
302 | break; | 246 | break; |
303 | case 'v': // version | 247 | case 'v': // version |
304 | printf("sway version " SWAY_VERSION "\n"); | 248 | printf("sway version " SWAY_VERSION "\n"); |
305 | exit(EXIT_SUCCESS); | 249 | exit(EXIT_SUCCESS); |
306 | break; | 250 | break; |
307 | case 'V': // verbose | 251 | case 'V': // verbose |
308 | verbose = 1; | 252 | verbose = true; |
309 | break; | 253 | break; |
310 | case 'p': ; // --get-socketpath | 254 | case 'p': // --get-socketpath |
311 | if (getenv("SWAYSOCK")) { | 255 | if (getenv("SWAYSOCK")) { |
312 | printf("%s\n", getenv("SWAYSOCK")); | 256 | printf("%s\n", getenv("SWAYSOCK")); |
313 | exit(EXIT_SUCCESS); | 257 | exit(EXIT_SUCCESS); |
@@ -322,6 +266,11 @@ int main(int argc, char **argv) { | |||
322 | } | 266 | } |
323 | } | 267 | } |
324 | 268 | ||
269 | // SUID operation is deprecated, so block it for now. | ||
270 | if (detect_suid()) { | ||
271 | exit(EXIT_FAILURE); | ||
272 | } | ||
273 | |||
325 | // Since wayland requires XDG_RUNTIME_DIR to be set, abort with just the | 274 | // Since wayland requires XDG_RUNTIME_DIR to be set, abort with just the |
326 | // clear error message (when not running as an IPC client). | 275 | // clear error message (when not running as an IPC client). |
327 | if (!getenv("XDG_RUNTIME_DIR") && optind == argc) { | 276 | if (!getenv("XDG_RUNTIME_DIR") && optind == argc) { |
@@ -344,11 +293,10 @@ int main(int argc, char **argv) { | |||
344 | } | 293 | } |
345 | 294 | ||
346 | sway_log(SWAY_INFO, "Sway version " SWAY_VERSION); | 295 | sway_log(SWAY_INFO, "Sway version " SWAY_VERSION); |
296 | sway_log(SWAY_INFO, "wlroots version " WLR_VERSION_STR); | ||
347 | log_kernel(); | 297 | log_kernel(); |
348 | log_distro(); | 298 | log_distro(); |
349 | log_env(); | 299 | log_env(); |
350 | detect_proprietary(allow_unsupported_gpu); | ||
351 | detect_raspi(); | ||
352 | 300 | ||
353 | if (optind < argc) { // Behave as IPC client | 301 | if (optind < argc) { // Behave as IPC client |
354 | if (optind != 1) { | 302 | if (optind != 1) { |
@@ -361,9 +309,6 @@ int main(int argc, char **argv) { | |||
361 | "`sway -d 2>sway.log`."); | 309 | "`sway -d 2>sway.log`."); |
362 | exit(EXIT_FAILURE); | 310 | exit(EXIT_FAILURE); |
363 | } | 311 | } |
364 | if (!drop_permissions()) { | ||
365 | exit(EXIT_FAILURE); | ||
366 | } | ||
367 | char *socket_path = getenv("SWAYSOCK"); | 312 | char *socket_path = getenv("SWAYSOCK"); |
368 | if (!socket_path) { | 313 | if (!socket_path) { |
369 | sway_log(SWAY_ERROR, "Unable to retrieve socket path"); | 314 | sway_log(SWAY_ERROR, "Unable to retrieve socket path"); |
@@ -375,14 +320,7 @@ int main(int argc, char **argv) { | |||
375 | return 0; | 320 | return 0; |
376 | } | 321 | } |
377 | 322 | ||
378 | if (!server_privileged_prepare(&server)) { | 323 | increase_nofile_limit(); |
379 | return 1; | ||
380 | } | ||
381 | |||
382 | if (!drop_permissions()) { | ||
383 | server_fini(&server); | ||
384 | exit(EXIT_FAILURE); | ||
385 | } | ||
386 | 324 | ||
387 | // handle SIGTERM signals | 325 | // handle SIGTERM signals |
388 | signal(SIGTERM, sig_handler); | 326 | signal(SIGTERM, sig_handler); |
@@ -393,12 +331,14 @@ int main(int argc, char **argv) { | |||
393 | 331 | ||
394 | sway_log(SWAY_INFO, "Starting sway version " SWAY_VERSION); | 332 | sway_log(SWAY_INFO, "Starting sway version " SWAY_VERSION); |
395 | 333 | ||
396 | root = root_create(); | ||
397 | |||
398 | if (!server_init(&server)) { | 334 | if (!server_init(&server)) { |
399 | return 1; | 335 | return 1; |
400 | } | 336 | } |
401 | 337 | ||
338 | if (server.linux_dmabuf_v1) { | ||
339 | wlr_scene_set_linux_dmabuf_v1(root->root_scene, server.linux_dmabuf_v1); | ||
340 | } | ||
341 | |||
402 | if (validate) { | 342 | if (validate) { |
403 | bool valid = load_main_config(config_path, false, true); | 343 | bool valid = load_main_config(config_path, false, true); |
404 | free(config_path); | 344 | free(config_path); |
@@ -413,6 +353,8 @@ int main(int argc, char **argv) { | |||
413 | goto shutdown; | 353 | goto shutdown; |
414 | } | 354 | } |
415 | 355 | ||
356 | set_rr_scheduling(); | ||
357 | |||
416 | if (!server_start(&server)) { | 358 | if (!server_start(&server)) { |
417 | sway_terminate(EXIT_FAILURE); | 359 | sway_terminate(EXIT_FAILURE); |
418 | goto shutdown; | 360 | goto shutdown; |
diff --git a/sway/meson.build b/sway/meson.build index 6e138101..47b51d0c 100644 --- a/sway/meson.build +++ b/sway/meson.build | |||
@@ -5,24 +5,26 @@ 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', | ||
11 | 'scene_descriptor.c', | ||
9 | 'server.c', | 12 | 'server.c', |
13 | 'sway_text_node.c', | ||
10 | 'swaynag.c', | 14 | 'swaynag.c', |
15 | 'xdg_activation_v1.c', | ||
11 | 'xdg_decoration.c', | 16 | 'xdg_decoration.c', |
12 | 17 | ||
13 | 'desktop/desktop.c', | ||
14 | 'desktop/idle_inhibit_v1.c', | 18 | 'desktop/idle_inhibit_v1.c', |
15 | 'desktop/layer_shell.c', | 19 | 'desktop/layer_shell.c', |
16 | 'desktop/output.c', | 20 | 'desktop/output.c', |
17 | 'desktop/render.c', | ||
18 | 'desktop/surface.c', | ||
19 | 'desktop/transaction.c', | 21 | 'desktop/transaction.c', |
20 | 'desktop/xdg_shell.c', | 22 | 'desktop/xdg_shell.c', |
23 | 'desktop/launcher.c', | ||
21 | 24 | ||
22 | 'input/input-manager.c', | 25 | 'input/input-manager.c', |
23 | 'input/cursor.c', | 26 | 'input/cursor.c', |
24 | 'input/keyboard.c', | 27 | 'input/keyboard.c', |
25 | 'input/libinput.c', | ||
26 | 'input/seat.c', | 28 | 'input/seat.c', |
27 | 'input/seatop_default.c', | 29 | 'input/seatop_default.c', |
28 | 'input/seatop_down.c', | 30 | 'input/seatop_down.c', |
@@ -64,6 +66,7 @@ sway_sources = files( | |||
64 | 'commands/force_focus_wrapping.c', | 66 | 'commands/force_focus_wrapping.c', |
65 | 'commands/fullscreen.c', | 67 | 'commands/fullscreen.c', |
66 | 'commands/gaps.c', | 68 | 'commands/gaps.c', |
69 | 'commands/gesture.c', | ||
67 | 'commands/hide_edge_borders.c', | 70 | 'commands/hide_edge_borders.c', |
68 | 'commands/inhibit_idle.c', | 71 | 'commands/inhibit_idle.c', |
69 | 'commands/kill.c', | 72 | 'commands/kill.c', |
@@ -82,6 +85,7 @@ sway_sources = files( | |||
82 | 'commands/nop.c', | 85 | 'commands/nop.c', |
83 | 'commands/output.c', | 86 | 'commands/output.c', |
84 | 'commands/popup_during_fullscreen.c', | 87 | 'commands/popup_during_fullscreen.c', |
88 | 'commands/primary_selection.c', | ||
85 | 'commands/reload.c', | 89 | 'commands/reload.c', |
86 | 'commands/rename.c', | 90 | 'commands/rename.c', |
87 | 'commands/resize.c', | 91 | 'commands/resize.c', |
@@ -153,6 +157,7 @@ sway_sources = files( | |||
153 | 'commands/input/drag.c', | 157 | 'commands/input/drag.c', |
154 | 'commands/input/drag_lock.c', | 158 | 'commands/input/drag_lock.c', |
155 | 'commands/input/dwt.c', | 159 | 'commands/input/dwt.c', |
160 | 'commands/input/dwtp.c', | ||
156 | 'commands/input/events.c', | 161 | 'commands/input/events.c', |
157 | 'commands/input/left_handed.c', | 162 | 'commands/input/left_handed.c', |
158 | 'commands/input/map_from_region.c', | 163 | 'commands/input/map_from_region.c', |
@@ -161,9 +166,11 @@ sway_sources = files( | |||
161 | 'commands/input/middle_emulation.c', | 166 | 'commands/input/middle_emulation.c', |
162 | 'commands/input/natural_scroll.c', | 167 | 'commands/input/natural_scroll.c', |
163 | 'commands/input/pointer_accel.c', | 168 | 'commands/input/pointer_accel.c', |
169 | 'commands/input/rotation_angle.c', | ||
164 | 'commands/input/repeat_delay.c', | 170 | 'commands/input/repeat_delay.c', |
165 | 'commands/input/repeat_rate.c', | 171 | 'commands/input/repeat_rate.c', |
166 | 'commands/input/scroll_button.c', | 172 | 'commands/input/scroll_button.c', |
173 | 'commands/input/scroll_button_lock.c', | ||
167 | 'commands/input/scroll_factor.c', | 174 | 'commands/input/scroll_factor.c', |
168 | 'commands/input/scroll_method.c', | 175 | 'commands/input/scroll_method.c', |
169 | 'commands/input/tap.c', | 176 | 'commands/input/tap.c', |
@@ -187,11 +194,14 @@ sway_sources = files( | |||
187 | 'commands/output/max_render_time.c', | 194 | 'commands/output/max_render_time.c', |
188 | 'commands/output/mode.c', | 195 | 'commands/output/mode.c', |
189 | 'commands/output/position.c', | 196 | 'commands/output/position.c', |
197 | 'commands/output/power.c', | ||
198 | 'commands/output/render_bit_depth.c', | ||
190 | 'commands/output/scale.c', | 199 | 'commands/output/scale.c', |
191 | 'commands/output/scale_filter.c', | 200 | 'commands/output/scale_filter.c', |
192 | 'commands/output/subpixel.c', | 201 | 'commands/output/subpixel.c', |
193 | 'commands/output/toggle.c', | 202 | 'commands/output/toggle.c', |
194 | 'commands/output/transform.c', | 203 | 'commands/output/transform.c', |
204 | 'commands/output/unplug.c', | ||
195 | 205 | ||
196 | 'tree/arrange.c', | 206 | 'tree/arrange.c', |
197 | 'tree/container.c', | 207 | 'tree/container.c', |
@@ -204,28 +214,34 @@ sway_sources = files( | |||
204 | 214 | ||
205 | sway_deps = [ | 215 | sway_deps = [ |
206 | cairo, | 216 | cairo, |
217 | drm, | ||
207 | jsonc, | 218 | jsonc, |
208 | libevdev, | 219 | libevdev, |
209 | libinput, | 220 | libinput, |
221 | libudev, | ||
210 | math, | 222 | math, |
211 | pango, | 223 | pango, |
212 | pcre, | 224 | pcre2, |
213 | glesv2, | ||
214 | pixman, | 225 | pixman, |
215 | server_protos, | 226 | threads, |
216 | wayland_server, | 227 | wayland_server, |
217 | wlroots, | 228 | wlroots, |
218 | xkbcommon, | 229 | xkbcommon, |
230 | xcb, | ||
231 | xcb_icccm, | ||
219 | ] | 232 | ] |
220 | 233 | ||
221 | if have_xwayland | 234 | if wlroots_features['xwayland'] |
222 | sway_sources += 'desktop/xwayland.c' | 235 | sway_sources += 'desktop/xwayland.c' |
223 | sway_deps += xcb | 236 | endif |
237 | |||
238 | if wlroots_features['libinput_backend'] | ||
239 | sway_sources += 'input/libinput.c' | ||
224 | endif | 240 | endif |
225 | 241 | ||
226 | executable( | 242 | executable( |
227 | 'sway', | 243 | 'sway', |
228 | sway_sources, | 244 | sway_sources + wl_protos_src, |
229 | include_directories: [sway_inc], | 245 | include_directories: [sway_inc], |
230 | dependencies: sway_deps, | 246 | dependencies: sway_deps, |
231 | link_with: [lib_sway_common], | 247 | link_with: [lib_sway_common], |
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/scene_descriptor.c b/sway/scene_descriptor.c new file mode 100644 index 00000000..a30d4664 --- /dev/null +++ b/sway/scene_descriptor.c | |||
@@ -0,0 +1,66 @@ | |||
1 | #include <stdlib.h> | ||
2 | #include <wlr/util/addon.h> | ||
3 | #include "log.h" | ||
4 | #include "sway/scene_descriptor.h" | ||
5 | |||
6 | struct scene_descriptor { | ||
7 | void *data; | ||
8 | struct wlr_addon addon; | ||
9 | }; | ||
10 | |||
11 | static const struct wlr_addon_interface addon_interface; | ||
12 | |||
13 | static struct scene_descriptor *scene_node_get_descriptor( | ||
14 | struct wlr_scene_node *node, enum sway_scene_descriptor_type type) { | ||
15 | struct wlr_addon *addon = wlr_addon_find(&node->addons, (void *)type, &addon_interface); | ||
16 | if (!addon) { | ||
17 | return NULL; | ||
18 | } | ||
19 | |||
20 | struct scene_descriptor *desc = wl_container_of(addon, desc, addon); | ||
21 | return desc; | ||
22 | } | ||
23 | |||
24 | static void descriptor_destroy(struct scene_descriptor *desc) { | ||
25 | wlr_addon_finish(&desc->addon); | ||
26 | free(desc); | ||
27 | } | ||
28 | |||
29 | void *scene_descriptor_try_get(struct wlr_scene_node *node, | ||
30 | enum sway_scene_descriptor_type type) { | ||
31 | struct scene_descriptor *desc = scene_node_get_descriptor(node, type); | ||
32 | if (!desc) { | ||
33 | return NULL; | ||
34 | } | ||
35 | |||
36 | return desc->data; | ||
37 | } | ||
38 | |||
39 | void scene_descriptor_destroy(struct wlr_scene_node *node, | ||
40 | enum sway_scene_descriptor_type type) { | ||
41 | struct scene_descriptor *desc = scene_node_get_descriptor(node, type); | ||
42 | descriptor_destroy(desc); | ||
43 | } | ||
44 | |||
45 | static void addon_handle_destroy(struct wlr_addon *addon) { | ||
46 | struct scene_descriptor *desc = wl_container_of(addon, desc, addon); | ||
47 | descriptor_destroy(desc); | ||
48 | } | ||
49 | |||
50 | static const struct wlr_addon_interface addon_interface = { | ||
51 | .name = "sway_scene_descriptor", | ||
52 | .destroy = addon_handle_destroy, | ||
53 | }; | ||
54 | |||
55 | bool scene_descriptor_assign(struct wlr_scene_node *node, | ||
56 | enum sway_scene_descriptor_type type, void *data) { | ||
57 | struct scene_descriptor *desc = calloc(1, sizeof(*desc)); | ||
58 | if (!desc) { | ||
59 | sway_log(SWAY_ERROR, "Could not allocate a scene descriptor"); | ||
60 | return false; | ||
61 | } | ||
62 | |||
63 | wlr_addon_init(&desc->addon, &node->addons, (void *)type, &addon_interface); | ||
64 | desc->data = data; | ||
65 | return true; | ||
66 | } | ||
diff --git a/sway/server.c b/sway/server.c index f51fcfe2..4b48e8e5 100644 --- a/sway/server.c +++ b/sway/server.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <assert.h> | 1 | #include <assert.h> |
3 | #include <stdbool.h> | 2 | #include <stdbool.h> |
4 | #include <stdlib.h> | 3 | #include <stdlib.h> |
@@ -7,25 +6,45 @@ | |||
7 | #include <wlr/backend.h> | 6 | #include <wlr/backend.h> |
8 | #include <wlr/backend/headless.h> | 7 | #include <wlr/backend/headless.h> |
9 | #include <wlr/backend/multi.h> | 8 | #include <wlr/backend/multi.h> |
10 | #include <wlr/backend/noop.h> | 9 | #include <wlr/config.h> |
11 | #include <wlr/backend/session.h> | 10 | #include <wlr/render/allocator.h> |
12 | #include <wlr/render/wlr_renderer.h> | 11 | #include <wlr/render/wlr_renderer.h> |
13 | #include <wlr/types/wlr_compositor.h> | 12 | #include <wlr/types/wlr_compositor.h> |
13 | #include <wlr/types/wlr_content_type_v1.h> | ||
14 | #include <wlr/types/wlr_cursor_shape_v1.h> | ||
14 | #include <wlr/types/wlr_data_control_v1.h> | 15 | #include <wlr/types/wlr_data_control_v1.h> |
16 | #include <wlr/types/wlr_data_device.h> | ||
17 | #include <wlr/types/wlr_drm.h> | ||
15 | #include <wlr/types/wlr_export_dmabuf_v1.h> | 18 | #include <wlr/types/wlr_export_dmabuf_v1.h> |
19 | #include <wlr/types/wlr_ext_foreign_toplevel_list_v1.h> | ||
20 | #include <wlr/types/wlr_foreign_toplevel_management_v1.h> | ||
21 | #include <wlr/types/wlr_fractional_scale_v1.h> | ||
16 | #include <wlr/types/wlr_gamma_control_v1.h> | 22 | #include <wlr/types/wlr_gamma_control_v1.h> |
17 | #include <wlr/types/wlr_idle.h> | 23 | #include <wlr/types/wlr_idle_notify_v1.h> |
18 | #include <wlr/types/wlr_layer_shell_v1.h> | 24 | #include <wlr/types/wlr_layer_shell_v1.h> |
25 | #include <wlr/types/wlr_linux_dmabuf_v1.h> | ||
26 | #include <wlr/types/wlr_output_management_v1.h> | ||
27 | #include <wlr/types/wlr_output_power_management_v1.h> | ||
19 | #include <wlr/types/wlr_pointer_constraints_v1.h> | 28 | #include <wlr/types/wlr_pointer_constraints_v1.h> |
29 | #include <wlr/types/wlr_presentation_time.h> | ||
20 | #include <wlr/types/wlr_primary_selection_v1.h> | 30 | #include <wlr/types/wlr_primary_selection_v1.h> |
21 | #include <wlr/types/wlr_relative_pointer_v1.h> | 31 | #include <wlr/types/wlr_relative_pointer_v1.h> |
22 | #include <wlr/types/wlr_screencopy_v1.h> | 32 | #include <wlr/types/wlr_screencopy_v1.h> |
33 | #include <wlr/types/wlr_security_context_v1.h> | ||
23 | #include <wlr/types/wlr_server_decoration.h> | 34 | #include <wlr/types/wlr_server_decoration.h> |
35 | #include <wlr/types/wlr_session_lock_v1.h> | ||
36 | #include <wlr/types/wlr_single_pixel_buffer_v1.h> | ||
37 | #include <wlr/types/wlr_subcompositor.h> | ||
24 | #include <wlr/types/wlr_tablet_v2.h> | 38 | #include <wlr/types/wlr_tablet_v2.h> |
25 | #include <wlr/types/wlr_viewporter.h> | 39 | #include <wlr/types/wlr_viewporter.h> |
26 | #include <wlr/types/wlr_xcursor_manager.h> | 40 | #include <wlr/types/wlr_xcursor_manager.h> |
41 | #include <wlr/types/wlr_xdg_activation_v1.h> | ||
27 | #include <wlr/types/wlr_xdg_decoration_v1.h> | 42 | #include <wlr/types/wlr_xdg_decoration_v1.h> |
43 | #include <wlr/types/wlr_xdg_foreign_registry.h> | ||
44 | #include <wlr/types/wlr_xdg_foreign_v1.h> | ||
45 | #include <wlr/types/wlr_xdg_foreign_v2.h> | ||
28 | #include <wlr/types/wlr_xdg_output_v1.h> | 46 | #include <wlr/types/wlr_xdg_output_v1.h> |
47 | #include <xf86drm.h> | ||
29 | #include "config.h" | 48 | #include "config.h" |
30 | #include "list.h" | 49 | #include "list.h" |
31 | #include "log.h" | 50 | #include "log.h" |
@@ -34,41 +53,221 @@ | |||
34 | #include "sway/input/input-manager.h" | 53 | #include "sway/input/input-manager.h" |
35 | #include "sway/output.h" | 54 | #include "sway/output.h" |
36 | #include "sway/server.h" | 55 | #include "sway/server.h" |
56 | #include "sway/input/cursor.h" | ||
37 | #include "sway/tree/root.h" | 57 | #include "sway/tree/root.h" |
38 | #if HAVE_XWAYLAND | 58 | |
59 | #if WLR_HAS_XWAYLAND | ||
60 | #include <wlr/xwayland/shell.h> | ||
39 | #include "sway/xwayland.h" | 61 | #include "sway/xwayland.h" |
40 | #endif | 62 | #endif |
41 | 63 | ||
42 | bool server_privileged_prepare(struct sway_server *server) { | 64 | #if WLR_HAS_DRM_BACKEND |
43 | sway_log(SWAY_DEBUG, "Preparing Wayland server initialization"); | 65 | #include <wlr/types/wlr_drm_lease_v1.h> |
66 | #endif | ||
67 | |||
68 | #define SWAY_XDG_SHELL_VERSION 5 | ||
69 | #define SWAY_LAYER_SHELL_VERSION 4 | ||
70 | #define SWAY_FOREIGN_TOPLEVEL_LIST_VERSION 1 | ||
71 | |||
72 | bool allow_unsupported_gpu = false; | ||
73 | |||
74 | #if WLR_HAS_DRM_BACKEND | ||
75 | static void handle_drm_lease_request(struct wl_listener *listener, void *data) { | ||
76 | /* We only offer non-desktop outputs, but in the future we might want to do | ||
77 | * more logic here. */ | ||
78 | |||
79 | struct wlr_drm_lease_request_v1 *req = data; | ||
80 | struct wlr_drm_lease_v1 *lease = wlr_drm_lease_request_v1_grant(req); | ||
81 | if (!lease) { | ||
82 | sway_log(SWAY_ERROR, "Failed to grant lease request"); | ||
83 | wlr_drm_lease_request_v1_reject(req); | ||
84 | } | ||
85 | } | ||
86 | #endif | ||
87 | |||
88 | static bool is_privileged(const struct wl_global *global) { | ||
89 | #if WLR_HAS_DRM_BACKEND | ||
90 | if (server.drm_lease_manager != NULL) { | ||
91 | struct wlr_drm_lease_device_v1 *drm_lease_dev; | ||
92 | wl_list_for_each(drm_lease_dev, &server.drm_lease_manager->devices, link) { | ||
93 | if (drm_lease_dev->global == global) { | ||
94 | return true; | ||
95 | } | ||
96 | } | ||
97 | } | ||
98 | #endif | ||
99 | |||
100 | return | ||
101 | global == server.output_manager_v1->global || | ||
102 | global == server.output_power_manager_v1->global || | ||
103 | global == server.input_method->global || | ||
104 | global == server.foreign_toplevel_list->global || | ||
105 | global == server.foreign_toplevel_manager->global || | ||
106 | global == server.data_control_manager_v1->global || | ||
107 | global == server.screencopy_manager_v1->global || | ||
108 | global == server.export_dmabuf_manager_v1->global || | ||
109 | global == server.security_context_manager_v1->global || | ||
110 | global == server.gamma_control_manager_v1->global || | ||
111 | global == server.layer_shell->global || | ||
112 | global == server.session_lock.manager->global || | ||
113 | global == server.input->keyboard_shortcuts_inhibit->global || | ||
114 | global == server.input->virtual_keyboard->global || | ||
115 | global == server.input->virtual_pointer->global || | ||
116 | global == server.input->transient_seat_manager->global; | ||
117 | } | ||
118 | |||
119 | static bool filter_global(const struct wl_client *client, | ||
120 | const struct wl_global *global, void *data) { | ||
121 | #if WLR_HAS_XWAYLAND | ||
122 | struct wlr_xwayland *xwayland = server.xwayland.wlr_xwayland; | ||
123 | if (xwayland && global == xwayland->shell_v1->global) { | ||
124 | return xwayland->server != NULL && client == xwayland->server->client; | ||
125 | } | ||
126 | #endif | ||
127 | |||
128 | // Restrict usage of privileged protocols to unsandboxed clients | ||
129 | // TODO: add a way for users to configure an allow-list | ||
130 | const struct wlr_security_context_v1_state *security_context = | ||
131 | wlr_security_context_manager_v1_lookup_client( | ||
132 | server.security_context_manager_v1, (struct wl_client *)client); | ||
133 | if (is_privileged(global)) { | ||
134 | return security_context == NULL; | ||
135 | } | ||
136 | |||
137 | return true; | ||
138 | } | ||
139 | |||
140 | static void detect_proprietary(struct wlr_backend *backend, void *data) { | ||
141 | int drm_fd = wlr_backend_get_drm_fd(backend); | ||
142 | if (drm_fd < 0) { | ||
143 | return; | ||
144 | } | ||
145 | |||
146 | drmVersion *version = drmGetVersion(drm_fd); | ||
147 | if (version == NULL) { | ||
148 | sway_log(SWAY_ERROR, "drmGetVersion() failed"); | ||
149 | return; | ||
150 | } | ||
151 | |||
152 | bool is_unsupported = false; | ||
153 | if (strcmp(version->name, "nvidia-drm") == 0) { | ||
154 | is_unsupported = true; | ||
155 | sway_log(SWAY_ERROR, "!!! Proprietary Nvidia drivers are in use !!!"); | ||
156 | if (!allow_unsupported_gpu) { | ||
157 | sway_log(SWAY_ERROR, "Use Nouveau instead"); | ||
158 | } | ||
159 | } | ||
160 | |||
161 | if (strcmp(version->name, "evdi") == 0) { | ||
162 | is_unsupported = true; | ||
163 | sway_log(SWAY_ERROR, "!!! Proprietary DisplayLink drivers are in use !!!"); | ||
164 | } | ||
165 | |||
166 | if (!allow_unsupported_gpu && is_unsupported) { | ||
167 | sway_log(SWAY_ERROR, | ||
168 | "Proprietary drivers are NOT supported. To launch sway anyway, " | ||
169 | "launch with --unsupported-gpu and DO NOT report issues."); | ||
170 | exit(EXIT_FAILURE); | ||
171 | } | ||
172 | |||
173 | drmFreeVersion(version); | ||
174 | } | ||
175 | |||
176 | static void handle_renderer_lost(struct wl_listener *listener, void *data) { | ||
177 | struct sway_server *server = wl_container_of(listener, server, renderer_lost); | ||
178 | |||
179 | sway_log(SWAY_INFO, "Re-creating renderer after GPU reset"); | ||
180 | |||
181 | struct wlr_renderer *renderer = wlr_renderer_autocreate(server->backend); | ||
182 | if (renderer == NULL) { | ||
183 | sway_log(SWAY_ERROR, "Unable to create renderer"); | ||
184 | return; | ||
185 | } | ||
186 | |||
187 | struct wlr_allocator *allocator = | ||
188 | wlr_allocator_autocreate(server->backend, renderer); | ||
189 | if (allocator == NULL) { | ||
190 | sway_log(SWAY_ERROR, "Unable to create allocator"); | ||
191 | wlr_renderer_destroy(renderer); | ||
192 | return; | ||
193 | } | ||
194 | |||
195 | struct wlr_renderer *old_renderer = server->renderer; | ||
196 | struct wlr_allocator *old_allocator = server->allocator; | ||
197 | server->renderer = renderer; | ||
198 | server->allocator = allocator; | ||
199 | |||
200 | wl_list_remove(&server->renderer_lost.link); | ||
201 | wl_signal_add(&server->renderer->events.lost, &server->renderer_lost); | ||
202 | |||
203 | wlr_compositor_set_renderer(server->compositor, renderer); | ||
204 | |||
205 | for (int i = 0; i < root->outputs->length; ++i) { | ||
206 | struct sway_output *output = root->outputs->items[i]; | ||
207 | wlr_output_init_render(output->wlr_output, | ||
208 | server->allocator, server->renderer); | ||
209 | } | ||
210 | |||
211 | wlr_allocator_destroy(old_allocator); | ||
212 | wlr_renderer_destroy(old_renderer); | ||
213 | } | ||
214 | |||
215 | bool server_init(struct sway_server *server) { | ||
216 | sway_log(SWAY_DEBUG, "Initializing Wayland server"); | ||
44 | server->wl_display = wl_display_create(); | 217 | server->wl_display = wl_display_create(); |
45 | server->wl_event_loop = wl_display_get_event_loop(server->wl_display); | 218 | server->wl_event_loop = wl_display_get_event_loop(server->wl_display); |
46 | server->backend = wlr_backend_autocreate(server->wl_display); | ||
47 | 219 | ||
220 | wl_display_set_global_filter(server->wl_display, filter_global, NULL); | ||
221 | |||
222 | root = root_create(server->wl_display); | ||
223 | |||
224 | server->backend = wlr_backend_autocreate(server->wl_event_loop, &server->session); | ||
48 | if (!server->backend) { | 225 | if (!server->backend) { |
49 | sway_log(SWAY_ERROR, "Unable to create backend"); | 226 | sway_log(SWAY_ERROR, "Unable to create backend"); |
50 | return false; | 227 | return false; |
51 | } | 228 | } |
52 | return true; | ||
53 | } | ||
54 | 229 | ||
55 | bool server_init(struct sway_server *server) { | 230 | wlr_multi_for_each_backend(server->backend, detect_proprietary, NULL); |
56 | sway_log(SWAY_DEBUG, "Initializing Wayland server"); | 231 | |
232 | server->renderer = wlr_renderer_autocreate(server->backend); | ||
233 | if (!server->renderer) { | ||
234 | sway_log(SWAY_ERROR, "Failed to create renderer"); | ||
235 | return false; | ||
236 | } | ||
57 | 237 | ||
58 | struct wlr_renderer *renderer = wlr_backend_get_renderer(server->backend); | 238 | server->renderer_lost.notify = handle_renderer_lost; |
59 | assert(renderer); | 239 | wl_signal_add(&server->renderer->events.lost, &server->renderer_lost); |
60 | 240 | ||
61 | wlr_renderer_init_wl_display(renderer, server->wl_display); | 241 | wlr_renderer_init_wl_shm(server->renderer, server->wl_display); |
62 | 242 | ||
63 | server->compositor = wlr_compositor_create(server->wl_display, renderer); | 243 | if (wlr_renderer_get_texture_formats(server->renderer, WLR_BUFFER_CAP_DMABUF) != NULL) { |
64 | server->compositor_new_surface.notify = handle_compositor_new_surface; | 244 | server->linux_dmabuf_v1 = wlr_linux_dmabuf_v1_create_with_renderer( |
65 | wl_signal_add(&server->compositor->events.new_surface, | 245 | server->wl_display, 4, server->renderer); |
66 | &server->compositor_new_surface); | 246 | if (debug.legacy_wl_drm) { |
247 | wlr_drm_create(server->wl_display, server->renderer); | ||
248 | } | ||
249 | } | ||
250 | |||
251 | server->allocator = wlr_allocator_autocreate(server->backend, | ||
252 | server->renderer); | ||
253 | if (!server->allocator) { | ||
254 | sway_log(SWAY_ERROR, "Failed to create allocator"); | ||
255 | return false; | ||
256 | } | ||
257 | |||
258 | server->compositor = wlr_compositor_create(server->wl_display, 6, | ||
259 | server->renderer); | ||
260 | |||
261 | wlr_subcompositor_create(server->wl_display); | ||
67 | 262 | ||
68 | server->data_device_manager = | 263 | server->data_device_manager = |
69 | wlr_data_device_manager_create(server->wl_display); | 264 | wlr_data_device_manager_create(server->wl_display); |
70 | 265 | ||
71 | wlr_gamma_control_manager_v1_create(server->wl_display); | 266 | server->gamma_control_manager_v1 = |
267 | wlr_gamma_control_manager_v1_create(server->wl_display); | ||
268 | server->gamma_control_set_gamma.notify = handle_gamma_control_set_gamma; | ||
269 | wl_signal_add(&server->gamma_control_manager_v1->events.set_gamma, | ||
270 | &server->gamma_control_set_gamma); | ||
72 | 271 | ||
73 | server->new_output.notify = handle_new_output; | 272 | server->new_output.notify = handle_new_output; |
74 | wl_signal_add(&server->backend->events.new_output, &server->new_output); | 273 | wl_signal_add(&server->backend->events.new_output, &server->new_output); |
@@ -78,19 +277,20 @@ bool server_init(struct sway_server *server) { | |||
78 | 277 | ||
79 | wlr_xdg_output_manager_v1_create(server->wl_display, root->output_layout); | 278 | wlr_xdg_output_manager_v1_create(server->wl_display, root->output_layout); |
80 | 279 | ||
81 | server->idle = wlr_idle_create(server->wl_display); | 280 | server->idle_notifier_v1 = wlr_idle_notifier_v1_create(server->wl_display); |
82 | server->idle_inhibit_manager_v1 = | 281 | sway_idle_inhibit_manager_v1_init(); |
83 | sway_idle_inhibit_manager_v1_create(server->wl_display, server->idle); | ||
84 | 282 | ||
85 | server->layer_shell = wlr_layer_shell_v1_create(server->wl_display); | 283 | server->layer_shell = wlr_layer_shell_v1_create(server->wl_display, |
284 | SWAY_LAYER_SHELL_VERSION); | ||
86 | wl_signal_add(&server->layer_shell->events.new_surface, | 285 | wl_signal_add(&server->layer_shell->events.new_surface, |
87 | &server->layer_shell_surface); | 286 | &server->layer_shell_surface); |
88 | server->layer_shell_surface.notify = handle_layer_shell_surface; | 287 | server->layer_shell_surface.notify = handle_layer_shell_surface; |
89 | 288 | ||
90 | server->xdg_shell = wlr_xdg_shell_create(server->wl_display); | 289 | server->xdg_shell = wlr_xdg_shell_create(server->wl_display, |
91 | wl_signal_add(&server->xdg_shell->events.new_surface, | 290 | SWAY_XDG_SHELL_VERSION); |
92 | &server->xdg_shell_surface); | 291 | wl_signal_add(&server->xdg_shell->events.new_toplevel, |
93 | server->xdg_shell_surface.notify = handle_xdg_shell_surface; | 292 | &server->xdg_shell_toplevel); |
293 | server->xdg_shell_toplevel.notify = handle_xdg_shell_toplevel; | ||
94 | 294 | ||
95 | server->tablet_v2 = wlr_tablet_v2_create(server->wl_display); | 295 | server->tablet_v2 = wlr_tablet_v2_create(server->wl_display); |
96 | 296 | ||
@@ -121,8 +321,7 @@ bool server_init(struct sway_server *server) { | |||
121 | wl_signal_add(&server->pointer_constraints->events.new_constraint, | 321 | wl_signal_add(&server->pointer_constraints->events.new_constraint, |
122 | &server->pointer_constraint); | 322 | &server->pointer_constraint); |
123 | 323 | ||
124 | server->presentation = | 324 | wlr_presentation_create(server->wl_display, server->backend); |
125 | wlr_presentation_create(server->wl_display, server->backend); | ||
126 | 325 | ||
127 | server->output_manager_v1 = | 326 | server->output_manager_v1 = |
128 | wlr_output_manager_v1_create(server->wl_display); | 327 | wlr_output_manager_v1_create(server->wl_display); |
@@ -141,19 +340,62 @@ bool server_init(struct sway_server *server) { | |||
141 | &server->output_power_manager_set_mode); | 340 | &server->output_power_manager_set_mode); |
142 | server->input_method = wlr_input_method_manager_v2_create(server->wl_display); | 341 | server->input_method = wlr_input_method_manager_v2_create(server->wl_display); |
143 | server->text_input = wlr_text_input_manager_v3_create(server->wl_display); | 342 | server->text_input = wlr_text_input_manager_v3_create(server->wl_display); |
343 | server->foreign_toplevel_list = | ||
344 | wlr_ext_foreign_toplevel_list_v1_create(server->wl_display, SWAY_FOREIGN_TOPLEVEL_LIST_VERSION); | ||
144 | server->foreign_toplevel_manager = | 345 | server->foreign_toplevel_manager = |
145 | wlr_foreign_toplevel_manager_v1_create(server->wl_display); | 346 | wlr_foreign_toplevel_manager_v1_create(server->wl_display); |
146 | 347 | ||
147 | wlr_export_dmabuf_manager_v1_create(server->wl_display); | 348 | sway_session_lock_init(); |
148 | wlr_screencopy_manager_v1_create(server->wl_display); | 349 | |
149 | wlr_data_control_manager_v1_create(server->wl_display); | 350 | #if WLR_HAS_DRM_BACKEND |
150 | wlr_primary_selection_v1_device_manager_create(server->wl_display); | 351 | server->drm_lease_manager= |
352 | wlr_drm_lease_v1_manager_create(server->wl_display, server->backend); | ||
353 | if (server->drm_lease_manager) { | ||
354 | server->drm_lease_request.notify = handle_drm_lease_request; | ||
355 | wl_signal_add(&server->drm_lease_manager->events.request, | ||
356 | &server->drm_lease_request); | ||
357 | } else { | ||
358 | sway_log(SWAY_DEBUG, "Failed to create wlr_drm_lease_device_v1"); | ||
359 | sway_log(SWAY_INFO, "VR will not be available"); | ||
360 | } | ||
361 | #endif | ||
362 | |||
363 | server->export_dmabuf_manager_v1 = wlr_export_dmabuf_manager_v1_create(server->wl_display); | ||
364 | server->screencopy_manager_v1 = wlr_screencopy_manager_v1_create(server->wl_display); | ||
365 | server->data_control_manager_v1 = wlr_data_control_manager_v1_create(server->wl_display); | ||
366 | server->security_context_manager_v1 = wlr_security_context_manager_v1_create(server->wl_display); | ||
151 | wlr_viewporter_create(server->wl_display); | 367 | wlr_viewporter_create(server->wl_display); |
368 | wlr_single_pixel_buffer_manager_v1_create(server->wl_display); | ||
369 | server->content_type_manager_v1 = | ||
370 | wlr_content_type_manager_v1_create(server->wl_display, 1); | ||
371 | wlr_fractional_scale_manager_v1_create(server->wl_display, 1); | ||
372 | |||
373 | struct wlr_xdg_foreign_registry *foreign_registry = | ||
374 | wlr_xdg_foreign_registry_create(server->wl_display); | ||
375 | wlr_xdg_foreign_v1_create(server->wl_display, foreign_registry); | ||
376 | wlr_xdg_foreign_v2_create(server->wl_display, foreign_registry); | ||
377 | |||
378 | server->xdg_activation_v1 = wlr_xdg_activation_v1_create(server->wl_display); | ||
379 | server->xdg_activation_v1_request_activate.notify = | ||
380 | xdg_activation_v1_handle_request_activate; | ||
381 | wl_signal_add(&server->xdg_activation_v1->events.request_activate, | ||
382 | &server->xdg_activation_v1_request_activate); | ||
383 | server->xdg_activation_v1_new_token.notify = | ||
384 | xdg_activation_v1_handle_new_token; | ||
385 | wl_signal_add(&server->xdg_activation_v1->events.new_token, | ||
386 | &server->xdg_activation_v1_new_token); | ||
387 | |||
388 | struct wlr_cursor_shape_manager_v1 *cursor_shape_manager = | ||
389 | wlr_cursor_shape_manager_v1_create(server->wl_display, 1); | ||
390 | server->request_set_cursor_shape.notify = handle_request_set_cursor_shape; | ||
391 | wl_signal_add(&cursor_shape_manager->events.request_set_shape, &server->request_set_cursor_shape); | ||
392 | |||
393 | wl_list_init(&server->pending_launcher_ctxs); | ||
152 | 394 | ||
153 | // Avoid using "wayland-0" as display socket | 395 | // Avoid using "wayland-0" as display socket |
154 | char name_candidate[16]; | 396 | char name_candidate[16]; |
155 | for (int i = 1; i <= 32; ++i) { | 397 | for (unsigned int i = 1; i <= 32; ++i) { |
156 | sprintf(name_candidate, "wayland-%d", i); | 398 | snprintf(name_candidate, sizeof(name_candidate), "wayland-%u", i); |
157 | if (wl_display_add_socket(server->wl_display, name_candidate) >= 0) { | 399 | if (wl_display_add_socket(server->wl_display, name_candidate) >= 0) { |
158 | server->socket = strdup(name_candidate); | 400 | server->socket = strdup(name_candidate); |
159 | break; | 401 | break; |
@@ -166,27 +408,26 @@ bool server_init(struct sway_server *server) { | |||
166 | return false; | 408 | return false; |
167 | } | 409 | } |
168 | 410 | ||
169 | server->noop_backend = wlr_noop_backend_create(server->wl_display); | 411 | server->headless_backend = wlr_headless_backend_create(server->wl_event_loop); |
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) { | 412 | if (!server->headless_backend) { |
177 | sway_log(SWAY_INFO, "Failed to create secondary headless backend, " | 413 | sway_log(SWAY_ERROR, "Failed to create secondary headless backend"); |
178 | "starting without it"); | 414 | wlr_backend_destroy(server->backend); |
415 | return false; | ||
179 | } else { | 416 | } else { |
180 | wlr_multi_backend_add(server->backend, server->headless_backend); | 417 | wlr_multi_backend_add(server->backend, server->headless_backend); |
181 | } | 418 | } |
182 | 419 | ||
420 | struct wlr_output *wlr_output = | ||
421 | wlr_headless_add_output(server->headless_backend, 800, 600); | ||
422 | wlr_output_set_name(wlr_output, "FALLBACK"); | ||
423 | root->fallback_output = output_create(wlr_output); | ||
424 | |||
183 | // This may have been set already via -Dtxn-timeout | 425 | // This may have been set already via -Dtxn-timeout |
184 | if (!server->txn_timeout_ms) { | 426 | if (!server->txn_timeout_ms) { |
185 | server->txn_timeout_ms = 200; | 427 | server->txn_timeout_ms = 200; |
186 | } | 428 | } |
187 | 429 | ||
188 | server->dirty_nodes = create_list(); | 430 | server->dirty_nodes = create_list(); |
189 | server->transactions = create_list(); | ||
190 | 431 | ||
191 | server->input = input_manager_create(server); | 432 | server->input = input_manager_create(server); |
192 | input_manager_get_default_seat(); // create seat0 | 433 | input_manager_get_default_seat(); // create seat0 |
@@ -196,17 +437,17 @@ bool server_init(struct sway_server *server) { | |||
196 | 437 | ||
197 | void server_fini(struct sway_server *server) { | 438 | void server_fini(struct sway_server *server) { |
198 | // TODO: free sway-specific resources | 439 | // TODO: free sway-specific resources |
199 | #if HAVE_XWAYLAND | 440 | #if WLR_HAS_XWAYLAND |
200 | wlr_xwayland_destroy(server->xwayland.wlr_xwayland); | 441 | wlr_xwayland_destroy(server->xwayland.wlr_xwayland); |
201 | #endif | 442 | #endif |
202 | wl_display_destroy_clients(server->wl_display); | 443 | wl_display_destroy_clients(server->wl_display); |
444 | wlr_backend_destroy(server->backend); | ||
203 | wl_display_destroy(server->wl_display); | 445 | wl_display_destroy(server->wl_display); |
204 | list_free(server->dirty_nodes); | 446 | list_free(server->dirty_nodes); |
205 | list_free(server->transactions); | ||
206 | } | 447 | } |
207 | 448 | ||
208 | bool server_start(struct sway_server *server) { | 449 | bool server_start(struct sway_server *server) { |
209 | #if HAVE_XWAYLAND | 450 | #if WLR_HAS_XWAYLAND |
210 | if (config->xwayland != XWAYLAND_MODE_DISABLED) { | 451 | if (config->xwayland != XWAYLAND_MODE_DISABLED) { |
211 | sway_log(SWAY_DEBUG, "Initializing Xwayland (lazy=%d)", | 452 | sway_log(SWAY_DEBUG, "Initializing Xwayland (lazy=%d)", |
212 | config->xwayland == XWAYLAND_MODE_LAZY); | 453 | config->xwayland == XWAYLAND_MODE_LAZY); |
@@ -231,6 +472,10 @@ bool server_start(struct sway_server *server) { | |||
231 | } | 472 | } |
232 | #endif | 473 | #endif |
233 | 474 | ||
475 | if (config->primary_selection) { | ||
476 | wlr_primary_selection_v1_device_manager_create(server->wl_display); | ||
477 | } | ||
478 | |||
234 | sway_log(SWAY_INFO, "Starting backend on wayland display '%s'", | 479 | sway_log(SWAY_INFO, "Starting backend on wayland display '%s'", |
235 | server->socket); | 480 | server->socket); |
236 | if (!wlr_backend_start(server->backend)) { | 481 | if (!wlr_backend_start(server->backend)) { |
@@ -238,6 +483,7 @@ bool server_start(struct sway_server *server) { | |||
238 | wlr_backend_destroy(server->backend); | 483 | wlr_backend_destroy(server->backend); |
239 | return false; | 484 | return false; |
240 | } | 485 | } |
486 | |||
241 | return true; | 487 | return true; |
242 | } | 488 | } |
243 | 489 | ||
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..442311bb 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 |
@@ -118,11 +121,16 @@ The following commands may only be used in the configuration file. | |||
118 | 121 | ||
119 | *input* <identifier> map_from_region <X1xY1> <X2xY2> | 122 | *input* <identifier> map_from_region <X1xY1> <X2xY2> |
120 | Ignores inputs from this device that do not occur within the specified | 123 | Ignores inputs from this device that do not occur within the specified |
121 | region. Can be in millimeters (e.g. 10x20mm 20x40mm) or in terms of 0..1 | 124 | region. Can be in millimeters (e.g. 10x20mm 20x40mm) or the fraction of the |
122 | (e.g. 0.5x0.5 0.7x0.7). Not all devices support millimeters. Only meaningful | 125 | full available space in terms of 0..1 (e.g. 0.5x0.5 0.7x0.7). Not all |
123 | if the device is not a keyboard and provides events in absolute terms (such | 126 | devices support millimeters. Only meaningful if the device is not a |
124 | as a drawing tablet or touch screen - most pointers provide events relative | 127 | keyboard and provides events in absolute terms (such as a drawing tablet |
125 | to the previous frame). | 128 | or touch screen - most pointers provide events relative to the previous |
129 | frame). | ||
130 | |||
131 | Commonly used to maintain the aspect ratio of the input device and screen. | ||
132 | Cropping a 16:10 input region to match a 16:9 display can use 0x0 1x0.9 as | ||
133 | the argument. | ||
126 | 134 | ||
127 | ## LIBINPUT CONFIGURATION | 135 | ## LIBINPUT CONFIGURATION |
128 | 136 | ||
@@ -144,6 +152,10 @@ The following commands may only be used in the configuration file. | |||
144 | *input* <identifier> dwt enabled|disabled | 152 | *input* <identifier> dwt enabled|disabled |
145 | Enables or disables disable-while-typing for the specified input device. | 153 | Enables or disables disable-while-typing for the specified input device. |
146 | 154 | ||
155 | *input* <identifier> dwtp enabled|disabled | ||
156 | Enables or disables disable-while-trackpointing for the specified input | ||
157 | device. | ||
158 | |||
147 | *input* <identifier> events enabled|disabled|disabled_on_external_mouse|toggle [<toggle-modes>] | 159 | *input* <identifier> events enabled|disabled|disabled_on_external_mouse|toggle [<toggle-modes>] |
148 | Enables or disables send_events for specified input device. Disabling | 160 | Enables or disables send_events for specified input device. Disabling |
149 | send_events disables the input device. | 161 | send_events disables the input device. |
@@ -168,12 +180,19 @@ The following commands may only be used in the configuration file. | |||
168 | *input* <identifier> pointer_accel [<-1|1>] | 180 | *input* <identifier> pointer_accel [<-1|1>] |
169 | Changes the pointer acceleration for the specified input device. | 181 | Changes the pointer acceleration for the specified input device. |
170 | 182 | ||
183 | *input* <identifier> rotation_angle <angle> | ||
184 | Sets the rotation angle of the device to the given clockwise angle in | ||
185 | degrees. The angle must be between 0.0 (inclusive) and 360.0 (exclusive). | ||
186 | |||
171 | *input* <identifier> scroll_button disable|button[1-3,8,9]|<event-code-or-name> | 187 | *input* <identifier> scroll_button disable|button[1-3,8,9]|<event-code-or-name> |
172 | Sets the button used for scroll_method on_button_down. The button can | 188 | Sets the button used for scroll_method on_button_down. The button can |
173 | be given as an event name or code, which can be obtained from *libinput | 189 | be given as an event name or code, which can be obtained from *libinput |
174 | debug-events*, or as a x11 mouse button (button[1-3,8,9]). If set to | 190 | debug-events*, or as a x11 mouse button (button[1-3,8,9]). If set to |
175 | _disable_, it disables the scroll_method on_button_down. | 191 | _disable_, it disables the scroll_method on_button_down. |
176 | 192 | ||
193 | *input* <identifier> scroll_button_lock enabled|disabled | ||
194 | Enables or disables scroll button lock for specified input device. | ||
195 | |||
177 | *input* <identifier> scroll_factor <floating point value> | 196 | *input* <identifier> scroll_factor <floating point value> |
178 | Changes the scroll factor for the specified input device. Scroll speed will | 197 | Changes the scroll factor for the specified input device. Scroll speed will |
179 | be scaled by the given value, which must be non-negative. | 198 | be scaled by the given value, which must be non-negative. |
@@ -217,6 +236,8 @@ correct seat. | |||
217 | absolute coordinates (with respect to the global coordinate space). | 236 | absolute coordinates (with respect to the global coordinate space). |
218 | Specifying either value as 0 will not update that coordinate. | 237 | Specifying either value as 0 will not update that coordinate. |
219 | 238 | ||
239 | Deprecated: use the virtual-pointer Wayland protocol instead. | ||
240 | |||
220 | *seat* <seat> cursor press|release button[1-9]|<event-name-or-code> | 241 | *seat* <seat> cursor press|release button[1-9]|<event-name-or-code> |
221 | Simulate pressing (or releasing) the specified mouse button on the | 242 | Simulate pressing (or releasing) the specified mouse button on the |
222 | specified seat. The button can either be provided as a button event name or | 243 | specified seat. The button can either be provided as a button event name or |
@@ -225,12 +246,14 @@ correct seat. | |||
225 | event will be simulated, however _press_ and _release_ will be ignored and | 246 | event will be simulated, however _press_ and _release_ will be ignored and |
226 | both will occur. | 247 | both will occur. |
227 | 248 | ||
249 | Deprecated: use the virtual-pointer Wayland protocol instead. | ||
250 | |||
228 | *seat* <name> fallback true|false | 251 | *seat* <name> fallback true|false |
229 | Set this seat as the fallback seat. A fallback seat will attach any device | 252 | Set this seat as the fallback seat. A fallback seat will attach any device |
230 | not explicitly attached to another seat (similar to a "default" seat). | 253 | not explicitly attached to another seat (similar to a "default" seat). |
231 | 254 | ||
232 | *seat* <name> hide_cursor <timeout>|when-typing [enable|disable] | 255 | *seat* <name> hide_cursor <timeout>|when-typing [enable|disable] |
233 | Hides the cursor image after the specified event occured. | 256 | Hides the cursor image after the specified event occurred. |
234 | 257 | ||
235 | If _timeout_ is specified, then the cursor will be hidden after _timeout_ | 258 | 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 | 259 | (in milliseconds) has elapsed with no activity on the cursor. A timeout of 0 |
@@ -240,18 +263,16 @@ correct seat. | |||
240 | If _when-typing_ is enabled, then the cursor will be hidden whenever a key | 263 | If _when-typing_ is enabled, then the cursor will be hidden whenever a key |
241 | is pressed. | 264 | is pressed. |
242 | 265 | ||
266 | Be aware that this setting can interfere with input handling in games and | ||
267 | certain types of software (Gimp, Blender etc) that rely on simultaneous | ||
268 | input from mouse and keyboard. | ||
269 | |||
243 | *seat* <name> idle_inhibit <sources...> | 270 | *seat* <name> idle_inhibit <sources...> |
244 | Sets the set of input event sources which can prevent the seat from | 271 | Sets the set of input event sources which can prevent the seat from |
245 | becoming idle, as a space separated list of source names. Valid names are | 272 | becoming idle, as a space separated list of source names. Valid names are |
246 | "keyboard", "pointer", "touchpad", "touch", "tablet_pad", "tablet_tool", | 273 | "keyboard", "pointer", "touchpad", "touch", "tablet_pad", "tablet_tool", |
247 | and "switch". The default behavior is to prevent idle on any event. | 274 | and "switch". The default behavior is to prevent idle on any event. |
248 | 275 | ||
249 | *seat* <name> idle_wake <sources...> | ||
250 | Sets the set of input event sources which can wake the seat from | ||
251 | its idle state, as a space separated list of source names. Valid names are | ||
252 | "keyboard", "pointer", "touchpad", "touch", "tablet_pad", "tablet_tool", | ||
253 | and "switch". The default behavior is to wake from idle on any event. | ||
254 | |||
255 | *seat* <name> keyboard_grouping none|smart | 276 | *seat* <name> keyboard_grouping none|smart |
256 | Set how the keyboards in the seat are grouped together. Currently, there | 277 | Set how the keyboards in the seat are grouped together. Currently, there |
257 | are two options. _none_ will disable all keyboard grouping. This will make | 278 | are two options. _none_ will disable all keyboard grouping. This will make |
@@ -274,7 +295,7 @@ correct seat. | |||
274 | whether future inhibitors are honoured by default, i.e. activated | 295 | whether future inhibitors are honoured by default, i.e. activated |
275 | automatically, the default being _enable_. When used at runtime, | 296 | automatically, the default being _enable_. When used at runtime, |
276 | _disable_ also disables any currently active inhibitors. _activate_, | 297 | _disable_ also disables any currently active inhibitors. _activate_, |
277 | _deactivate_ and _toggle_ are only useable at runtime and change the | 298 | _deactivate_ and _toggle_ are only usable at runtime and change the |
278 | state of a potentially existing inhibitor on the currently focused | 299 | state of a potentially existing inhibitor on the currently focused |
279 | window. This can be used with the current seat alias (_-_) to affect | 300 | window. This can be used with the current seat alias (_-_) to affect |
280 | only the currently focused window of the current seat. Subcommand | 301 | 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..2f697248 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* |
@@ -334,8 +337,9 @@ node and will have the following properties: | |||
334 | this, but borders are included. | 337 | this, but borders are included. |
335 | |- window_rect | 338 | |- window_rect |
336 | : object | 339 | : object |
337 | : The geometry of the contents inside the node. The window decorations are | 340 | : The geometry of the content inside the node. These coordinates are relative |
338 | excluded from this calculation, but borders are included. | 341 | to the node itself. Window decorations and borders are outside the |
342 | _window_rect_ | ||
339 | |- deco_rect | 343 | |- deco_rect |
340 | : object | 344 | : object |
341 | : The geometry of the decorations for the node relative to the parent node | 345 | : The geometry of the decorations for the node relative to the parent node |
@@ -370,8 +374,14 @@ node and will have the following properties: | |||
370 | that can be used as an aid in submitting reproduction steps for bug reports | 374 | that can be used as an aid in submitting reproduction steps for bug reports |
371 | |- fullscreen_mode | 375 | |- fullscreen_mode |
372 | : integer | 376 | : integer |
373 | : (Only containers and views) The fullscreen mode of the node. 0 means none, 1 means | 377 | : (Only containers and views) The fullscreen mode of the node. 0 means none, 1 means |
374 | full workspace, and 2 means global fullscreen | 378 | full workspace, and 2 means global fullscreen |
379 | |- floating | ||
380 | : string | ||
381 | : Floating state of container. Can be either "auto_off" or "user_on" | ||
382 | |- scratchpad_state | ||
383 | : string | ||
384 | : Whether the window is in the scratchpad. Can be either "none" or "fresh" | ||
375 | |- app_id | 385 | |- app_id |
376 | : string | 386 | : string |
377 | : (Only views) For an xdg-shell view, the name of the application, if set. | 387 | : (Only views) For an xdg-shell view, the name of the application, if set. |
@@ -1036,7 +1046,7 @@ An object with a single string property containing the contents of the config | |||
1036 | *Example Reply:* | 1046 | *Example Reply:* |
1037 | ``` | 1047 | ``` |
1038 | { | 1048 | { |
1039 | "config": "set $mod Mod4\nbindsym $mod+q exit\n" | 1049 | "config": "set $mod Mod4\\nbindsym $mod+q exit\\n" |
1040 | } | 1050 | } |
1041 | ``` | 1051 | ``` |
1042 | 1052 | ||
@@ -1131,6 +1141,9 @@ following properties: | |||
1131 | |- xkb_active_layout_index | 1141 | |- xkb_active_layout_index |
1132 | : integer | 1142 | : integer |
1133 | : (Only keyboards) The index of the active keyboard layout in use | 1143 | : (Only keyboards) The index of the active keyboard layout in use |
1144 | |- scroll_factor | ||
1145 | : floating | ||
1146 | : (Only pointers) Multiplier applied on scroll event values. | ||
1134 | |- libinput | 1147 | |- libinput |
1135 | : object | 1148 | : object |
1136 | : (Only libinput devices) An object describing the current device settings. | 1149 | : (Only libinput devices) An object describing the current device settings. |
@@ -1188,9 +1201,16 @@ following properties will be included for devices that support them: | |||
1188 | : int | 1201 | : int |
1189 | : The scroll button to use when _scroll_method_ is _on_button_down_. This | 1202 | : The scroll button to use when _scroll_method_ is _on_button_down_. This |
1190 | will be given as an input event code | 1203 | will be given as an input event code |
1204 | |- scroll_button_lock | ||
1205 | : string | ||
1206 | : Whether scroll button lock is enabled. It can be _enabled_ or _disabled_ | ||
1191 | |- dwt | 1207 | |- dwt |
1192 | : string | 1208 | : string |
1193 | : Whether disable-while-typing is enabled. It can be _enabled_ or _disabled_ | 1209 | : Whether disable-while-typing is enabled. It can be _enabled_ or _disabled_ |
1210 | |- dwtp | ||
1211 | : string | ||
1212 | : Whether disable-while-trackpointing is enabled. It can be _enabled_ or | ||
1213 | _disabled_ | ||
1194 | |- calibration_matrix | 1214 | |- calibration_matrix |
1195 | : array | 1215 | : array |
1196 | : An array of 6 floats representing the calibration matrix for absolute | 1216 | : An array of 6 floats representing the calibration matrix for absolute |
@@ -1230,7 +1250,8 @@ following properties will be included for devices that support them: | |||
1230 | "click_method": "button_areas", | 1250 | "click_method": "button_areas", |
1231 | "middle_emulation": "disabled", | 1251 | "middle_emulation": "disabled", |
1232 | "scroll_method": "edge", | 1252 | "scroll_method": "edge", |
1233 | "dwt": "enabled" | 1253 | "dwt": "enabled", |
1254 | "dwtp": "enabled" | ||
1234 | } | 1255 | } |
1235 | }, | 1256 | }, |
1236 | { | 1257 | { |
@@ -1357,7 +1378,8 @@ one seat. Each object has the following properties: | |||
1357 | "click_method": "button_areas", | 1378 | "click_method": "button_areas", |
1358 | "middle_emulation": "disabled", | 1379 | "middle_emulation": "disabled", |
1359 | "scroll_method": "edge", | 1380 | "scroll_method": "edge", |
1360 | "dwt": "enabled" | 1381 | "dwt": "enabled", |
1382 | "dwtp": "enabled" | ||
1361 | } | 1383 | } |
1362 | }, | 1384 | }, |
1363 | { | 1385 | { |
@@ -1433,6 +1455,9 @@ available: | |||
1433 | : workspace | 1455 | : workspace |
1434 | :[ Sent whenever an event involving a workspace occurs such as initialization | 1456 | :[ Sent whenever an event involving a workspace occurs such as initialization |
1435 | of a new workspace or a different workspace gains focus | 1457 | of a new workspace or a different workspace gains focus |
1458 | |- 0x80000001 | ||
1459 | : output | ||
1460 | : Sent when outputs are updated | ||
1436 | |- 0x80000002 | 1461 | |- 0x80000002 |
1437 | : mode | 1462 | : mode |
1438 | : Sent whenever the binding mode changes | 1463 | : Sent whenever the binding mode changes |
@@ -1553,6 +1578,20 @@ The following change types are currently available: | |||
1553 | } | 1578 | } |
1554 | ``` | 1579 | ``` |
1555 | 1580 | ||
1581 | ## 0x80000001. OUTPUT | ||
1582 | |||
1583 | Sent whenever an output is added, removed, or its configuration is changed. | ||
1584 | The event is a single object with the property _change_, which is a string | ||
1585 | containing the reason for the change. Currently, the only value for _change_ is | ||
1586 | _unspecified_. | ||
1587 | |||
1588 | *Example Event:* | ||
1589 | ``` | ||
1590 | { | ||
1591 | "change": "unspecified" | ||
1592 | } | ||
1593 | ``` | ||
1594 | |||
1556 | ## 0x80000002. MODE | 1595 | ## 0x80000002. MODE |
1557 | 1596 | ||
1558 | Sent whenever the binding mode changes. The event consists of a single object | 1597 | Sent whenever the binding mode changes. The event consists of a single object |
diff --git a/sway/sway-output.5.scd b/sway/sway-output.5.scd index 69f529fe..7d088d5d 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 |
@@ -62,13 +72,11 @@ must be separated by one space. For example: | |||
62 | 72 | ||
63 | *output* <name> scale <factor> | 73 | *output* <name> scale <factor> |
64 | Scales the specified output by the specified scale _factor_. An integer is | 74 | Scales the specified output by the specified scale _factor_. An integer is |
65 | recommended, but fractional values are also supported. If a fractional | 75 | recommended, but fractional values are also supported. You may be better |
66 | value are specified, be warned that it is not possible to faithfully | 76 | served by setting an integer scale factor and adjusting the font size of |
67 | represent the contents of your windows - they will be rendered at the next | 77 | your applications to taste. HiDPI isn't supported with Xwayland clients |
68 | highest integer scale factor and downscaled. You may be better served by | 78 | (windows will blur). A fractional scale may be slightly adjusted to match |
69 | setting an integer scale factor and adjusting the font size of your | 79 | requirements of the protocol. |
70 | applications to taste. HiDPI isn't supported with Xwayland clients (windows | ||
71 | will blur). | ||
72 | 80 | ||
73 | *output* <name> scale_filter linear|nearest|smart | 81 | *output* <name> scale_filter linear|nearest|smart |
74 | Indicates how to scale application buffers that are rendered at a scale | 82 | Indicates how to scale application buffers that are rendered at a scale |
@@ -109,12 +117,20 @@ must be separated by one space. For example: | |||
109 | Enables or disables the specified output (all outputs are enabled by | 117 | Enables or disables the specified output (all outputs are enabled by |
110 | default). | 118 | default). |
111 | 119 | ||
120 | As opposed to the _power_ command, the output will lose its current | ||
121 | workspace and windows. | ||
122 | |||
112 | *output* <name> toggle | 123 | *output* <name> toggle |
113 | Toggle the specified output. | 124 | Toggle the specified output. |
114 | 125 | ||
115 | *output* <name> dpms on|off | 126 | *output* <name> power on|off|toggle |
116 | Enables or disables the specified output via DPMS. To turn an output off | 127 | Turns on or off the specified output. |
117 | (ie. blank the screen but keep workspaces as-is), one can set DPMS to off. | 128 | |
129 | As opposed to the _enable_ and _disable_ commands, the output keeps its | ||
130 | current workspaces and windows. | ||
131 | |||
132 | *output* <name> dpms on|off|toggle | ||
133 | Deprecated. Alias for _power_. | ||
118 | 134 | ||
119 | *output* <name> max_render_time off|<msec> | 135 | *output* <name> max_render_time off|<msec> |
120 | Controls when sway composites the output, as a positive number of | 136 | Controls when sway composites the output, as a positive number of |
@@ -142,11 +158,26 @@ must be separated by one space. For example: | |||
142 | Enables or disables adaptive synchronization (often referred to as Variable | 158 | Enables or disables adaptive synchronization (often referred to as Variable |
143 | Refresh Rate, or by the vendor-specific names FreeSync/G-Sync). | 159 | Refresh Rate, or by the vendor-specific names FreeSync/G-Sync). |
144 | 160 | ||
145 | Adaptive sync allows clients to submit frames a little to late without | 161 | 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 | 162 | 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 | 163 | adaptive sync can improve latency, but can cause flickering on some |
148 | hardware. | 164 | hardware. |
149 | 165 | ||
166 | *output* <name> render_bit_depth 8|10 | ||
167 | Controls the color channel bit depth at which frames are rendered; the | ||
168 | default is currently 8 bits per channel. | ||
169 | |||
170 | Setting higher values will not have an effect if hardware and software lack | ||
171 | support for such bit depths. Successfully increasing the render bit depth | ||
172 | will not necessarily increase the bit depth of the frames sent to a display. | ||
173 | An increased render bit depth may provide smoother rendering of gradients, | ||
174 | and screenshots which can more precisely store the colors of programs | ||
175 | which display high bit depth colors. | ||
176 | |||
177 | Warnings: this can break screenshot/screencast programs which have not been | ||
178 | updated to work with different bit depths. This command is experimental, | ||
179 | and may be removed or changed in the future. | ||
180 | |||
150 | # SEE ALSO | 181 | # SEE ALSO |
151 | 182 | ||
152 | *sway*(5) *sway-input*(5) | 183 | *sway*(5) *sway-input*(5) |
diff --git a/sway/sway.5.scd b/sway/sway.5.scd index 02592b5f..9f823947 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 | ||
@@ -382,8 +389,8 @@ runtime. | |||
382 | for_window <criteria> move container to output <output> | 389 | for_window <criteria> move container to output <output> |
383 | 390 | ||
384 | *bindsym* [--whole-window] [--border] [--exclude-titlebar] [--release] [--locked] \ | 391 | *bindsym* [--whole-window] [--border] [--exclude-titlebar] [--release] [--locked] \ |
385 | [--to-code] [--input-device=<device>] [--no-warn] [--no-repeat] [Group<1-4>+]<key combo> \ | 392 | [--to-code] [--input-device=<device>] [--no-warn] [--no-repeat] [--inhibited] \ |
386 | <command> | 393 | [Group<1-4>+]<key combo> <command> |
387 | Binds _key combo_ to execute the sway command _command_ when pressed. You | 394 | Binds _key combo_ to execute the sway command _command_ when pressed. You |
388 | may use XKB key names here (*wev*(1) is a good tool for discovering these). | 395 | may use XKB key names here (*wev*(1) is a good tool for discovering these). |
389 | With the flag _--release_, the command is executed when the key combo is | 396 | With the flag _--release_, the command is executed when the key combo is |
@@ -393,6 +400,12 @@ runtime. | |||
393 | only be available for that group. By default, if you overwrite a binding, | 400 | only be available for that group. By default, if you overwrite a binding, |
394 | swaynag will give you a warning. To silence this, use the _--no-warn_ flag. | 401 | swaynag will give you a warning. To silence this, use the _--no-warn_ flag. |
395 | 402 | ||
403 | For specifying modifier keys, you can use the XKB modifier names _Shift_, | ||
404 | _Lock_ (for Caps Lock), _Control_, _Mod1_ (for Alt), _Mod2_ (for Num Lock), | ||
405 | _Mod3_ (for XKB modifier Mod3), _Mod4_ (for the Logo key), and _Mod5_ (for | ||
406 | AltGr). In addition, you can use the aliases _Ctrl_ (for Control), _Alt_ | ||
407 | (for Alt), and _Super_ (for the Logo key). | ||
408 | |||
396 | Unless the flag _--locked_ is set, the command will not be run when a | 409 | Unless the flag _--locked_ is set, the command will not be run when a |
397 | screen locking program is active. If there is a matching binding with | 410 | screen locking program is active. If there is a matching binding with |
398 | and without _--locked_, the one with will be preferred when locked and the | 411 | and without _--locked_, the one with will be preferred when locked and the |
@@ -404,7 +417,7 @@ runtime. | |||
404 | a keyboard shortcuts inhibitor is active for the currently focused | 417 | a keyboard shortcuts inhibitor is active for the currently focused |
405 | window. Such inhibitors are usually requested by remote desktop and | 418 | window. Such inhibitors are usually requested by remote desktop and |
406 | virtualization software to enable the user to send keyboard shortcuts | 419 | virtualization software to enable the user to send keyboard shortcuts |
407 | to the remote or virtual session. The _--inhibited_ flag allows to | 420 | to the remote or virtual session. The _--inhibited_ flag allows one to |
408 | define bindings which will be exempt from pass-through to such | 421 | define bindings which will be exempt from pass-through to such |
409 | software. The same preference logic as for _--locked_ applies. | 422 | software. The same preference logic as for _--locked_ applies. |
410 | 423 | ||
@@ -447,7 +460,8 @@ runtime. | |||
447 | ``` | 460 | ``` |
448 | 461 | ||
449 | *bindcode* [--whole-window] [--border] [--exclude-titlebar] [--release] \ | 462 | *bindcode* [--whole-window] [--border] [--exclude-titlebar] [--release] \ |
450 | [--locked] [--input-device=<device>] [--no-warn] [Group<1-4>+]<code> <command> | 463 | [--locked] [--input-device=<device>] [--no-warn] [--no-repeat] [--inhibited] \ |
464 | [Group<1-4>+]<code> <command> | ||
451 | is also available for binding with key/button codes instead of key/button names. | 465 | is also available for binding with key/button codes instead of key/button names. |
452 | 466 | ||
453 | *bindswitch* [--locked] [--no-warn] [--reload] <switch>:<state> <command> | 467 | *bindswitch* [--locked] [--no-warn] [--reload] <switch>:<state> <command> |
@@ -480,6 +494,62 @@ runtime. | |||
480 | bindswitch lid:toggle exec echo "Lid moved" | 494 | bindswitch lid:toggle exec echo "Lid moved" |
481 | ``` | 495 | ``` |
482 | 496 | ||
497 | *bindgesture* [--exact] [--input-device=<device>] [--no-warn] \ | ||
498 | <gesture>[:<fingers>][:directions] <command> | ||
499 | Binds _gesture_ to execute the sway command _command_ when detected. | ||
500 | Currently supports the _hold_, _pinch_ or _swipe_ gesture. Optionally | ||
501 | can be limited to bind to a certain number of _fingers_ or, for a | ||
502 | _pinch_ or _swipe_ gesture, to certain _directions_. | ||
503 | |||
504 | [[ *type* | ||
505 | :[ *fingers* | ||
506 | :< *direction* | ||
507 | | hold | ||
508 | :- 1 - 5 | ||
509 | : none | ||
510 | | swipe | ||
511 | : 3 - 5 | ||
512 | : up, down, left, right | ||
513 | | pinch | ||
514 | : 2 - 5 | ||
515 | : all above + inward, outward, clockwise, counterclockwise | ||
516 | |||
517 | The _fingers_ can be limited to any sensible number or left empty to accept | ||
518 | any finger counts. | ||
519 | Valid directions are _up_, _down_, _left_ and _right_, as well as _inward_, | ||
520 | _outward_, _clockwise_, _counterclockwise_ for the _pinch_ gesture. | ||
521 | Multiple directions can be combined by a plus. | ||
522 | |||
523 | If a _input-device_ is given, the binding will only be executed for | ||
524 | that input device and will be executed instead of any binding that is | ||
525 | generic to all devices. By default, if you overwrite a binding, | ||
526 | swaynag will give you a warning. To silence this, use the _--no-warn_ flag. | ||
527 | |||
528 | The _--exact_ flag can be used to ensure a binding only matches when exactly | ||
529 | all specified directions are matched and nothing more. If there is matching | ||
530 | binding with _--exact_, it will be preferred. | ||
531 | |||
532 | The priority for matching bindings is as follows: input device, then | ||
533 | exact matches followed by matches with the highest number of matching | ||
534 | directions. | ||
535 | |||
536 | Gestures executed while the pointer is above a bar are not handled by sway. | ||
537 | See the respective documentation, e.g. *bindgesture* in *sway-bar*(5). | ||
538 | |||
539 | Example: | ||
540 | ``` | ||
541 | # Allow switching between workspaces with left and right swipes | ||
542 | bindgesture swipe:right workspace prev | ||
543 | bindgesture swipe:left workspace next | ||
544 | |||
545 | # Allow container movements by pinching them | ||
546 | bindgesture pinch:inward+up move up | ||
547 | bindgesture pinch:inward+down move down | ||
548 | bindgesture pinch:inward+left move left | ||
549 | bindgesture pinch:inward+right move right | ||
550 | |||
551 | ``` | ||
552 | |||
483 | *client.background* <color> | 553 | *client.background* <color> |
484 | This command is ignored and is only present for i3 compatibility. | 554 | This command is ignored and is only present for i3 compatibility. |
485 | 555 | ||
@@ -497,6 +567,12 @@ runtime. | |||
497 | *client.focused_inactive* | 567 | *client.focused_inactive* |
498 | The most recently focused view within a container which is not focused. | 568 | The most recently focused view within a container which is not focused. |
499 | 569 | ||
570 | *client.focused_tab_title* | ||
571 | A view that has focused descendant container. | ||
572 | Tab or stack container title that is the parent of the focused container | ||
573 | but is not directly focused. Defaults to focused_inactive if not | ||
574 | specified and does not use the indicator and child_border colors. | ||
575 | |||
500 | *client.placeholder* | 576 | *client.placeholder* |
501 | Ignored (present for i3 compatibility). | 577 | Ignored (present for i3 compatibility). |
502 | 578 | ||
@@ -552,6 +628,12 @@ The default colors are: | |||
552 | : #ffffff | 628 | : #ffffff |
553 | : #484e50 | 629 | : #484e50 |
554 | : #5f676a | 630 | : #5f676a |
631 | | *focused_tab_title* | ||
632 | : #333333 | ||
633 | : #5f676a | ||
634 | : #ffffff | ||
635 | : n/a | ||
636 | : n/a | ||
555 | | *unfocused* | 637 | | *unfocused* |
556 | : #333333 | 638 | : #333333 |
557 | : #222222 | 639 | : #222222 |
@@ -573,7 +655,8 @@ The default colors are: | |||
573 | 655 | ||
574 | 656 | ||
575 | *default_border* normal|none|pixel [<n>] | 657 | *default_border* normal|none|pixel [<n>] |
576 | Set default border style for new tiled windows. | 658 | Set default border style for new tiled windows. Config reload won't affect |
659 | existing windows, only newly created ones after the reload. | ||
577 | 660 | ||
578 | *default_floating_border* normal|none|pixel [<n>] | 661 | *default_floating_border* normal|none|pixel [<n>] |
579 | Set default border style for new floating windows. This only applies to | 662 | Set default border style for new floating windows. This only applies to |
@@ -606,11 +689,11 @@ The default colors are: | |||
606 | after switching between workspaces. | 689 | after switching between workspaces. |
607 | 690 | ||
608 | *focus_on_window_activation* smart|urgent|focus|none | 691 | *focus_on_window_activation* smart|urgent|focus|none |
609 | This option determines what to do when an xwayland client requests | 692 | 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 | 693 | 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. | 694 | _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 | 695 | become focused only if it is already visible, otherwise the urgent state |
613 | visible, otherwise the urgent state will be set. Default is _urgent_. | 696 | will be set. Default is _urgent_. |
614 | 697 | ||
615 | *focus_wrapping* yes|no|force|workspace | 698 | *focus_wrapping* yes|no|force|workspace |
616 | This option determines what to do when attempting to focus over the edge | 699 | This option determines what to do when attempting to focus over the edge |
@@ -630,14 +713,14 @@ The default colors are: | |||
630 | should be used instead. Regardless of whether pango markup is enabled, | 713 | should be used instead. Regardless of whether pango markup is enabled, |
631 | _font_ should be specified as a pango font description. For more | 714 | _font_ should be specified as a pango font description. For more |
632 | information on pango font descriptions, see | 715 | information on pango font descriptions, see |
633 | https://developer.gnome.org/pango/stable/pango-Fonts.html#pango-font-description-from-string | 716 | https://docs.gtk.org/Pango/type_func.FontDescription.from_string.html#description |
634 | 717 | ||
635 | *force_display_urgency_hint* <timeout> [ms] | 718 | *force_display_urgency_hint* <timeout> [ms] |
636 | If an application on another workspace sets an urgency hint, switching to this | 719 | 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 | 720 | 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 | 721 | window decoration color would be immediately reset to *client.focused*. This |
639 | may make it unnecessarily hard to tell which window originally raised the | 722 | 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. | 723 | event. This option allows one to set a _timeout_ in ms to delay the urgency hint reset. |
641 | 724 | ||
642 | *titlebar_border_thickness* <thickness> | 725 | *titlebar_border_thickness* <thickness> |
643 | Thickness of the titlebar border in pixels | 726 | Thickness of the titlebar border in pixels |
@@ -690,9 +773,10 @@ The default colors are: | |||
690 | borders will only be enabled if the workspace has more than one visible | 773 | borders will only be enabled if the workspace has more than one visible |
691 | child and gaps equal to zero. | 774 | child and gaps equal to zero. |
692 | 775 | ||
693 | *smart_gaps* on|off | 776 | *smart_gaps* on|off|toggle|inverse_outer |
694 | If smart_gaps are _on_ gaps will only be enabled if a workspace has more | 777 | If smart_gaps are _on_ gaps will only be enabled if a workspace has more |
695 | than one child. | 778 | than one child. If smart_gaps are _inverse_outer_ outer gaps will only |
779 | be enabled if a workspace has exactly one child. | ||
696 | 780 | ||
697 | *mark* --add|--replace [--toggle] <identifier> | 781 | *mark* --add|--replace [--toggle] <identifier> |
698 | Marks are arbitrary labels that can be used to identify certain windows and | 782 | Marks are arbitrary labels that can be used to identify certain windows and |
@@ -731,6 +815,10 @@ The default colors are: | |||
731 | dialog will not be rendered. If _leave_fullscreen_, the view will exit | 815 | dialog will not be rendered. If _leave_fullscreen_, the view will exit |
732 | fullscreen mode and the dialog will be rendered. | 816 | fullscreen mode and the dialog will be rendered. |
733 | 817 | ||
818 | *primary_selection* enabled|disabled | ||
819 | Enable or disable the primary selection clipboard. May only be configured | ||
820 | at launch. Default is _enabled_. | ||
821 | |||
734 | *set* $<name> <value> | 822 | *set* $<name> <value> |
735 | Sets variable $_name_ to _value_. You can use the new variable in the | 823 | Sets variable $_name_ to _value_. You can use the new variable in the |
736 | arguments of future commands. When the variable is used, it can be escaped | 824 | arguments of future commands. When the variable is used, it can be escaped |
@@ -771,6 +859,11 @@ The default colors are: | |||
771 | *unbindswitch* <switch>:<state> | 859 | *unbindswitch* <switch>:<state> |
772 | Removes a binding for when <switch> changes to <state>. | 860 | Removes a binding for when <switch> changes to <state>. |
773 | 861 | ||
862 | *unbindgesture* [--exact] [--input-device=<device>] \ | ||
863 | <gesture>[:<fingers>][:directions] | ||
864 | Removes a binding for the specified _gesture_, _fingers_ | ||
865 | and _directions_ combination. | ||
866 | |||
774 | *unbindsym* [--whole-window] [--border] [--exclude-titlebar] [--release] [--locked] \ | 867 | *unbindsym* [--whole-window] [--border] [--exclude-titlebar] [--release] [--locked] \ |
775 | [--to-code] [--input-device=<device>] <key combo> | 868 | [--to-code] [--input-device=<device>] <key combo> |
776 | Removes the binding for _key combo_ that was previously bound with the | 869 | Removes the binding for _key combo_ that was previously bound with the |
@@ -876,6 +969,9 @@ properties in practice for your applications. | |||
876 | 969 | ||
877 | The following attributes may be matched with: | 970 | The following attributes may be matched with: |
878 | 971 | ||
972 | *all* | ||
973 | Matches all windows. | ||
974 | |||
879 | *app_id* | 975 | *app_id* |
880 | Compare value against the app id. Can be a regular expression. If value is | 976 | Compare value against the app id. Can be a regular expression. If value is |
881 | \_\_focused\_\_, then the app id must be the same as that of the currently | 977 | \_\_focused\_\_, then the app id must be the same as that of the currently |
@@ -884,7 +980,8 @@ The following attributes may be matched with: | |||
884 | *class* | 980 | *class* |
885 | Compare value against the window class. Can be a regular expression. If | 981 | Compare value against the window class. Can be a regular expression. If |
886 | value is \_\_focused\_\_, then the window class must be the same as that of | 982 | value is \_\_focused\_\_, then the window class must be the same as that of |
887 | the currently focused window. _class_ are specific to X11 applications. | 983 | the currently focused window. _class_ are specific to X11 applications and |
984 | require XWayland. | ||
888 | 985 | ||
889 | *con_id* | 986 | *con_id* |
890 | Compare against the internal container ID, which you can find via IPC. If | 987 | Compare against the internal container ID, which you can find via IPC. If |
@@ -898,12 +995,14 @@ The following attributes may be matched with: | |||
898 | Matches floating windows. | 995 | Matches floating windows. |
899 | 996 | ||
900 | *id* | 997 | *id* |
901 | Compare value against the X11 window ID. Must be numeric. | 998 | Compare value against the X11 window ID. Must be numeric. id is specific to |
999 | X11 applications and requires XWayland. | ||
902 | 1000 | ||
903 | *instance* | 1001 | *instance* |
904 | Compare value against the window instance. Can be a regular expression. If | 1002 | Compare value against the window instance. Can be a regular expression. If |
905 | value is \_\_focused\_\_, then the window instance must be the same as that | 1003 | value is \_\_focused\_\_, then the window instance must be the same as that |
906 | of the currently focused window. | 1004 | of the currently focused window. instance is specific to X11 applications and |
1005 | requires XWayland. | ||
907 | 1006 | ||
908 | *pid* | 1007 | *pid* |
909 | Compare value against the window's process ID. Must be numeric. | 1008 | Compare value against the window's process ID. Must be numeric. |
@@ -928,12 +1027,14 @@ The following attributes may be matched with: | |||
928 | *window_role* | 1027 | *window_role* |
929 | Compare against the window role (WM_WINDOW_ROLE). Can be a regular | 1028 | Compare against the window role (WM_WINDOW_ROLE). Can be a regular |
930 | expression. If value is \_\_focused\_\_, then the window role must be the | 1029 | expression. If value is \_\_focused\_\_, then the window role must be the |
931 | same as that of the currently focused window. | 1030 | same as that of the currently focused window. window_role is specific to X11 |
1031 | applications and requires XWayland. | ||
932 | 1032 | ||
933 | *window_type* | 1033 | *window_type* |
934 | Compare against the window type (\_NET_WM_WINDOW_TYPE). Possible values | 1034 | Compare against the window type (\_NET_WM_WINDOW_TYPE). Possible values |
935 | are normal, dialog, utility, toolbar, splash, menu, dropdown_menu, | 1035 | are normal, dialog, utility, toolbar, splash, menu, dropdown_menu, |
936 | popup_menu, tooltip and notification. | 1036 | popup_menu, tooltip and notification. window_type is specific to X11 |
1037 | applications and requires XWayland. | ||
937 | 1038 | ||
938 | *workspace* | 1039 | *workspace* |
939 | Compare against the workspace name for this view. Can be a regular | 1040 | Compare against the workspace name for this view. Can be a regular |
diff --git a/sway/sway_text_node.c b/sway/sway_text_node.c new file mode 100644 index 00000000..4b7ee999 --- /dev/null +++ b/sway/sway_text_node.c | |||
@@ -0,0 +1,308 @@ | |||
1 | #include <drm_fourcc.h> | ||
2 | #include <stdio.h> | ||
3 | #include <stdlib.h> | ||
4 | #include <string.h> | ||
5 | #include <wlr/types/wlr_buffer.h> | ||
6 | #include <wlr/interfaces/wlr_buffer.h> | ||
7 | #include "cairo_util.h" | ||
8 | #include "log.h" | ||
9 | #include "pango.h" | ||
10 | #include "sway/config.h" | ||
11 | #include "sway/sway_text_node.h" | ||
12 | |||
13 | struct cairo_buffer { | ||
14 | struct wlr_buffer base; | ||
15 | cairo_surface_t *surface; | ||
16 | cairo_t *cairo; | ||
17 | }; | ||
18 | |||
19 | static void cairo_buffer_handle_destroy(struct wlr_buffer *wlr_buffer) { | ||
20 | struct cairo_buffer *buffer = wl_container_of(wlr_buffer, buffer, base); | ||
21 | |||
22 | cairo_surface_destroy(buffer->surface); | ||
23 | cairo_destroy(buffer->cairo); | ||
24 | free(buffer); | ||
25 | } | ||
26 | |||
27 | static bool cairo_buffer_handle_begin_data_ptr_access(struct wlr_buffer *wlr_buffer, | ||
28 | uint32_t flags, void **data, uint32_t *format, size_t *stride) { | ||
29 | struct cairo_buffer *buffer = wl_container_of(wlr_buffer, buffer, base); | ||
30 | *data = cairo_image_surface_get_data(buffer->surface); | ||
31 | *stride = cairo_image_surface_get_stride(buffer->surface); | ||
32 | *format = DRM_FORMAT_ARGB8888; | ||
33 | return true; | ||
34 | } | ||
35 | |||
36 | static void cairo_buffer_handle_end_data_ptr_access(struct wlr_buffer *wlr_buffer) { | ||
37 | // This space is intentionally left blank | ||
38 | } | ||
39 | |||
40 | static const struct wlr_buffer_impl cairo_buffer_impl = { | ||
41 | .destroy = cairo_buffer_handle_destroy, | ||
42 | .begin_data_ptr_access = cairo_buffer_handle_begin_data_ptr_access, | ||
43 | .end_data_ptr_access = cairo_buffer_handle_end_data_ptr_access, | ||
44 | }; | ||
45 | |||
46 | struct text_buffer { | ||
47 | struct wlr_scene_buffer *buffer_node; | ||
48 | char *text; | ||
49 | struct sway_text_node props; | ||
50 | |||
51 | bool visible; | ||
52 | float scale; | ||
53 | enum wl_output_subpixel subpixel; | ||
54 | |||
55 | struct wl_listener outputs_update; | ||
56 | struct wl_listener destroy; | ||
57 | }; | ||
58 | |||
59 | static int get_text_width(struct sway_text_node *props) { | ||
60 | int width = props->width; | ||
61 | if (props->max_width >= 0) { | ||
62 | width = MIN(width, props->max_width); | ||
63 | } | ||
64 | return MAX(width, 0); | ||
65 | } | ||
66 | |||
67 | static void update_source_box(struct text_buffer *buffer) { | ||
68 | struct sway_text_node *props = &buffer->props; | ||
69 | struct wlr_fbox source_box = { | ||
70 | .x = 0, | ||
71 | .y = 0, | ||
72 | .width = ceil(get_text_width(props) * buffer->scale), | ||
73 | .height = ceil(props->height * buffer->scale), | ||
74 | }; | ||
75 | |||
76 | wlr_scene_buffer_set_source_box(buffer->buffer_node, &source_box); | ||
77 | } | ||
78 | |||
79 | static void render_backing_buffer(struct text_buffer *buffer) { | ||
80 | if (!buffer->visible) { | ||
81 | return; | ||
82 | } | ||
83 | |||
84 | if (buffer->props.max_width == 0) { | ||
85 | wlr_scene_buffer_set_buffer(buffer->buffer_node, NULL); | ||
86 | return; | ||
87 | } | ||
88 | |||
89 | float scale = buffer->scale; | ||
90 | int width = ceil(buffer->props.width * scale); | ||
91 | int height = ceil(buffer->props.height * scale); | ||
92 | float *color = (float *)&buffer->props.color; | ||
93 | float *background = (float *)&buffer->props.background; | ||
94 | PangoContext *pango = NULL; | ||
95 | |||
96 | cairo_font_options_t *fo = cairo_font_options_create(); | ||
97 | cairo_font_options_set_hint_style(fo, CAIRO_HINT_STYLE_FULL); | ||
98 | enum wl_output_subpixel subpixel = buffer->subpixel; | ||
99 | if (subpixel == WL_OUTPUT_SUBPIXEL_NONE || subpixel == WL_OUTPUT_SUBPIXEL_UNKNOWN) { | ||
100 | cairo_font_options_set_antialias(fo, CAIRO_ANTIALIAS_GRAY); | ||
101 | } else { | ||
102 | cairo_font_options_set_antialias(fo, CAIRO_ANTIALIAS_SUBPIXEL); | ||
103 | cairo_font_options_set_subpixel_order(fo, to_cairo_subpixel_order(subpixel)); | ||
104 | } | ||
105 | |||
106 | cairo_surface_t *surface = cairo_image_surface_create( | ||
107 | CAIRO_FORMAT_ARGB32, width, height); | ||
108 | cairo_status_t status = cairo_surface_status(surface); | ||
109 | if (status != CAIRO_STATUS_SUCCESS) { | ||
110 | sway_log(SWAY_ERROR, "cairo_image_surface_create failed: %s", | ||
111 | cairo_status_to_string(status)); | ||
112 | goto err; | ||
113 | } | ||
114 | |||
115 | struct cairo_buffer *cairo_buffer = calloc(1, sizeof(*cairo_buffer)); | ||
116 | if (!cairo_buffer) { | ||
117 | sway_log(SWAY_ERROR, "cairo_buffer allocation failed"); | ||
118 | goto err; | ||
119 | } | ||
120 | |||
121 | cairo_t *cairo = cairo_create(surface); | ||
122 | if (!cairo) { | ||
123 | sway_log(SWAY_ERROR, "cairo_create failed"); | ||
124 | free(cairo_buffer); | ||
125 | goto err; | ||
126 | } | ||
127 | |||
128 | cairo_set_antialias(cairo, CAIRO_ANTIALIAS_BEST); | ||
129 | cairo_set_font_options(cairo, fo); | ||
130 | pango = pango_cairo_create_context(cairo); | ||
131 | |||
132 | cairo_set_source_rgba(cairo, background[0], background[1], background[2], background[3]); | ||
133 | cairo_rectangle(cairo, 0, 0, width, height); | ||
134 | cairo_fill(cairo); | ||
135 | |||
136 | cairo_set_source_rgba(cairo, color[0], color[1], color[2], color[3]); | ||
137 | cairo_move_to(cairo, 0, (config->font_baseline - buffer->props.baseline) * scale); | ||
138 | |||
139 | render_text(cairo, config->font_description, scale, buffer->props.pango_markup, | ||
140 | "%s", buffer->text); | ||
141 | |||
142 | cairo_surface_flush(surface); | ||
143 | |||
144 | wlr_buffer_init(&cairo_buffer->base, &cairo_buffer_impl, width, height); | ||
145 | cairo_buffer->surface = surface; | ||
146 | cairo_buffer->cairo = cairo; | ||
147 | |||
148 | wlr_scene_buffer_set_buffer(buffer->buffer_node, &cairo_buffer->base); | ||
149 | wlr_buffer_drop(&cairo_buffer->base); | ||
150 | update_source_box(buffer); | ||
151 | |||
152 | pixman_region32_t opaque; | ||
153 | pixman_region32_init(&opaque); | ||
154 | if (background[3] == 1) { | ||
155 | pixman_region32_union_rect(&opaque, &opaque, 0, 0, | ||
156 | buffer->props.width, buffer->props.height); | ||
157 | } | ||
158 | wlr_scene_buffer_set_opaque_region(buffer->buffer_node, &opaque); | ||
159 | pixman_region32_fini(&opaque); | ||
160 | |||
161 | err: | ||
162 | if (pango) g_object_unref(pango); | ||
163 | cairo_font_options_destroy(fo); | ||
164 | } | ||
165 | |||
166 | static void handle_outputs_update(struct wl_listener *listener, void *data) { | ||
167 | struct text_buffer *buffer = wl_container_of(listener, buffer, outputs_update); | ||
168 | struct wlr_scene_outputs_update_event *event = data; | ||
169 | |||
170 | float scale = 0; | ||
171 | enum wl_output_subpixel subpixel = WL_OUTPUT_SUBPIXEL_UNKNOWN; | ||
172 | |||
173 | for (size_t i = 0; i < event->size; i++) { | ||
174 | struct wlr_scene_output *output = event->active[i]; | ||
175 | if (subpixel == WL_OUTPUT_SUBPIXEL_UNKNOWN) { | ||
176 | subpixel = output->output->subpixel; | ||
177 | } else if (subpixel != output->output->subpixel) { | ||
178 | subpixel = WL_OUTPUT_SUBPIXEL_NONE; | ||
179 | } | ||
180 | |||
181 | if (scale != 0 && scale != output->output->scale) { | ||
182 | // drop down to gray scale if we encounter outputs with different | ||
183 | // scales or else we will have chromatic aberations | ||
184 | subpixel = WL_OUTPUT_SUBPIXEL_NONE; | ||
185 | } | ||
186 | |||
187 | if (scale < output->output->scale) { | ||
188 | scale = output->output->scale; | ||
189 | } | ||
190 | } | ||
191 | |||
192 | buffer->visible = event->size > 0; | ||
193 | |||
194 | if (scale != buffer->scale || subpixel != buffer->subpixel) { | ||
195 | buffer->scale = scale; | ||
196 | buffer->subpixel = subpixel; | ||
197 | render_backing_buffer(buffer); | ||
198 | } | ||
199 | } | ||
200 | |||
201 | static void handle_destroy(struct wl_listener *listener, void *data) { | ||
202 | struct text_buffer *buffer = wl_container_of(listener, buffer, destroy); | ||
203 | |||
204 | wl_list_remove(&buffer->outputs_update.link); | ||
205 | wl_list_remove(&buffer->destroy.link); | ||
206 | |||
207 | free(buffer->text); | ||
208 | free(buffer); | ||
209 | } | ||
210 | |||
211 | static void text_calc_size(struct text_buffer *buffer) { | ||
212 | struct sway_text_node *props = &buffer->props; | ||
213 | |||
214 | cairo_t *c = cairo_create(NULL); | ||
215 | if (!c) { | ||
216 | sway_log(SWAY_ERROR, "cairo_t allocation failed"); | ||
217 | return; | ||
218 | } | ||
219 | |||
220 | cairo_set_antialias(c, CAIRO_ANTIALIAS_BEST); | ||
221 | get_text_size(c, config->font_description, &props->width, NULL, | ||
222 | &props->baseline, 1, props->pango_markup, "%s", buffer->text); | ||
223 | cairo_destroy(c); | ||
224 | |||
225 | wlr_scene_buffer_set_dest_size(buffer->buffer_node, | ||
226 | get_text_width(props), props->height); | ||
227 | } | ||
228 | |||
229 | struct sway_text_node *sway_text_node_create(struct wlr_scene_tree *parent, | ||
230 | char *text, float color[4], bool pango_markup) { | ||
231 | struct text_buffer *buffer = calloc(1, sizeof(*buffer)); | ||
232 | if (!buffer) { | ||
233 | return NULL; | ||
234 | } | ||
235 | |||
236 | struct wlr_scene_buffer *node = wlr_scene_buffer_create(parent, NULL); | ||
237 | if (!node) { | ||
238 | free(buffer); | ||
239 | return NULL; | ||
240 | } | ||
241 | |||
242 | buffer->buffer_node = node; | ||
243 | buffer->props.node = &node->node; | ||
244 | buffer->props.max_width = -1; | ||
245 | buffer->text = strdup(text); | ||
246 | if (!buffer->text) { | ||
247 | free(buffer); | ||
248 | wlr_scene_node_destroy(&node->node); | ||
249 | return NULL; | ||
250 | } | ||
251 | |||
252 | buffer->props.height = config->font_height; | ||
253 | buffer->props.pango_markup = pango_markup; | ||
254 | memcpy(&buffer->props.color, color, sizeof(*color) * 4); | ||
255 | |||
256 | buffer->destroy.notify = handle_destroy; | ||
257 | wl_signal_add(&node->node.events.destroy, &buffer->destroy); | ||
258 | buffer->outputs_update.notify = handle_outputs_update; | ||
259 | wl_signal_add(&node->events.outputs_update, &buffer->outputs_update); | ||
260 | |||
261 | text_calc_size(buffer); | ||
262 | |||
263 | return &buffer->props; | ||
264 | } | ||
265 | |||
266 | void sway_text_node_set_color(struct sway_text_node *node, float color[4]) { | ||
267 | if (memcmp(&node->color, color, sizeof(*color) * 4) == 0) { | ||
268 | return; | ||
269 | } | ||
270 | |||
271 | memcpy(&node->color, color, sizeof(*color) * 4); | ||
272 | struct text_buffer *buffer = wl_container_of(node, buffer, props); | ||
273 | |||
274 | render_backing_buffer(buffer); | ||
275 | } | ||
276 | |||
277 | void sway_text_node_set_text(struct sway_text_node *node, char *text) { | ||
278 | struct text_buffer *buffer = wl_container_of(node, buffer, props); | ||
279 | if (strcmp(buffer->text, text) == 0) { | ||
280 | return; | ||
281 | } | ||
282 | |||
283 | char *new_text = strdup(text); | ||
284 | if (!new_text) { | ||
285 | return; | ||
286 | } | ||
287 | |||
288 | free(buffer->text); | ||
289 | buffer->text = new_text; | ||
290 | |||
291 | text_calc_size(buffer); | ||
292 | render_backing_buffer(buffer); | ||
293 | } | ||
294 | |||
295 | void sway_text_node_set_max_width(struct sway_text_node *node, int max_width) { | ||
296 | struct text_buffer *buffer = wl_container_of(node, buffer, props); | ||
297 | buffer->props.max_width = max_width; | ||
298 | wlr_scene_buffer_set_dest_size(buffer->buffer_node, | ||
299 | get_text_width(&buffer->props), buffer->props.height); | ||
300 | update_source_box(buffer); | ||
301 | render_backing_buffer(buffer); | ||
302 | } | ||
303 | |||
304 | void sway_text_node_set_background(struct sway_text_node *node, float background[4]) { | ||
305 | struct text_buffer *buffer = wl_container_of(node, buffer, props); | ||
306 | memcpy(&node->background, background, sizeof(*background) * 4); | ||
307 | render_backing_buffer(buffer); | ||
308 | } | ||
diff --git a/sway/swaynag.c b/sway/swaynag.c index db5a919a..bc5e23ea 100644 --- a/sway/swaynag.c +++ b/sway/swaynag.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <signal.h> | 1 | #include <signal.h> |
3 | #include <stdbool.h> | 2 | #include <stdbool.h> |
4 | #include <stdlib.h> | 3 | #include <stdlib.h> |
@@ -64,6 +63,8 @@ bool swaynag_spawn(const char *swaynag_command, | |||
64 | sway_log(SWAY_ERROR, "Failed to create fork for swaynag"); | 63 | sway_log(SWAY_ERROR, "Failed to create fork for swaynag"); |
65 | goto failed; | 64 | goto failed; |
66 | } else if (pid == 0) { | 65 | } else if (pid == 0) { |
66 | restore_nofile_limit(); | ||
67 | |||
67 | pid = fork(); | 68 | pid = fork(); |
68 | if (pid < 0) { | 69 | if (pid < 0) { |
69 | sway_log_errno(SWAY_ERROR, "fork failed"); | 70 | sway_log_errno(SWAY_ERROR, "fork failed"); |
@@ -87,8 +88,8 @@ bool swaynag_spawn(const char *swaynag_command, | |||
87 | size_t length = strlen(swaynag_command) + strlen(swaynag->args) + 2; | 88 | size_t length = strlen(swaynag_command) + strlen(swaynag->args) + 2; |
88 | char *cmd = malloc(length); | 89 | char *cmd = malloc(length); |
89 | snprintf(cmd, length, "%s %s", swaynag_command, swaynag->args); | 90 | snprintf(cmd, length, "%s %s", swaynag_command, swaynag->args); |
90 | execl("/bin/sh", "/bin/sh", "-c", cmd, NULL); | 91 | execlp("sh", "sh", "-c", cmd, NULL); |
91 | sway_log_errno(SWAY_ERROR, "execl failed"); | 92 | sway_log_errno(SWAY_ERROR, "execlp failed"); |
92 | _exit(EXIT_FAILURE); | 93 | _exit(EXIT_FAILURE); |
93 | } | 94 | } |
94 | _exit(EXIT_SUCCESS); | 95 | _exit(EXIT_SUCCESS); |
@@ -143,22 +144,16 @@ void swaynag_log(const char *swaynag_command, struct swaynag_instance *swaynag, | |||
143 | 144 | ||
144 | va_list args; | 145 | va_list args; |
145 | va_start(args, fmt); | 146 | va_start(args, fmt); |
146 | size_t length = vsnprintf(NULL, 0, fmt, args) + 1; | 147 | char *str = vformat_str(fmt, args); |
147 | va_end(args); | 148 | va_end(args); |
148 | 149 | if (!str) { | |
149 | char *temp = malloc(length + 1); | ||
150 | if (!temp) { | ||
151 | sway_log(SWAY_ERROR, "Failed to allocate buffer for swaynag log entry."); | 150 | sway_log(SWAY_ERROR, "Failed to allocate buffer for swaynag log entry."); |
152 | return; | 151 | return; |
153 | } | 152 | } |
154 | 153 | ||
155 | va_start(args, fmt); | 154 | write(swaynag->fd[1], str, strlen(str)); |
156 | vsnprintf(temp, length, fmt, args); | ||
157 | va_end(args); | ||
158 | |||
159 | write(swaynag->fd[1], temp, length); | ||
160 | 155 | ||
161 | free(temp); | 156 | free(str); |
162 | } | 157 | } |
163 | 158 | ||
164 | void swaynag_show(struct swaynag_instance *swaynag) { | 159 | void swaynag_show(struct swaynag_instance *swaynag) { |
diff --git a/sway/tree/arrange.c b/sway/tree/arrange.c index bac9f2fa..d4003fe6 100644 --- a/sway/tree/arrange.c +++ b/sway/tree/arrange.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <ctype.h> | 1 | #include <ctype.h> |
3 | #include <stdbool.h> | 2 | #include <stdbool.h> |
4 | #include <stdlib.h> | 3 | #include <stdlib.h> |
@@ -55,7 +54,7 @@ static void apply_horiz_layout(list_t *children, struct wlr_box *parent) { | |||
55 | // Calculate gap size | 54 | // Calculate gap size |
56 | double inner_gap = 0; | 55 | double inner_gap = 0; |
57 | struct sway_container *child = children->items[0]; | 56 | struct sway_container *child = children->items[0]; |
58 | struct sway_workspace *ws = child->workspace; | 57 | struct sway_workspace *ws = child->pending.workspace; |
59 | if (ws) { | 58 | if (ws) { |
60 | inner_gap = ws->gaps_inner; | 59 | inner_gap = ws->gaps_inner; |
61 | } | 60 | } |
@@ -66,7 +65,7 @@ static void apply_horiz_layout(list_t *children, struct wlr_box *parent) { | |||
66 | if (layout == L_TABBED || layout == L_STACKED) { | 65 | if (layout == L_TABBED || layout == L_STACKED) { |
67 | inner_gap = 0; | 66 | inner_gap = 0; |
68 | } | 67 | } |
69 | temp = temp->parent; | 68 | temp = temp->pending.parent; |
70 | } | 69 | } |
71 | double total_gap = fmin(inner_gap * (children->length - 1), | 70 | double total_gap = fmin(inner_gap * (children->length - 1), |
72 | fmax(0, parent->width - MIN_SANE_W * children->length)); | 71 | fmax(0, parent->width - MIN_SANE_W * children->length)); |
@@ -79,15 +78,15 @@ static void apply_horiz_layout(list_t *children, struct wlr_box *parent) { | |||
79 | for (int i = 0; i < children->length; ++i) { | 78 | for (int i = 0; i < children->length; ++i) { |
80 | struct sway_container *child = children->items[i]; | 79 | struct sway_container *child = children->items[i]; |
81 | child->child_total_width = child_total_width; | 80 | child->child_total_width = child_total_width; |
82 | child->x = child_x; | 81 | child->pending.x = child_x; |
83 | child->y = parent->y; | 82 | child->pending.y = parent->y; |
84 | child->width = round(child->width_fraction * child_total_width); | 83 | child->pending.width = round(child->width_fraction * child_total_width); |
85 | child->height = parent->height; | 84 | child->pending.height = parent->height; |
86 | child_x += child->width + inner_gap; | 85 | child_x += child->pending.width + inner_gap; |
87 | 86 | ||
88 | // Make last child use remaining width of parent | 87 | // Make last child use remaining width of parent |
89 | if (i == children->length - 1) { | 88 | if (i == children->length - 1) { |
90 | child->width = parent->x + parent->width - child->x; | 89 | child->pending.width = parent->x + parent->width - child->pending.x; |
91 | } | 90 | } |
92 | } | 91 | } |
93 | } | 92 | } |
@@ -134,7 +133,7 @@ static void apply_vert_layout(list_t *children, struct wlr_box *parent) { | |||
134 | // Calculate gap size | 133 | // Calculate gap size |
135 | double inner_gap = 0; | 134 | double inner_gap = 0; |
136 | struct sway_container *child = children->items[0]; | 135 | struct sway_container *child = children->items[0]; |
137 | struct sway_workspace *ws = child->workspace; | 136 | struct sway_workspace *ws = child->pending.workspace; |
138 | if (ws) { | 137 | if (ws) { |
139 | inner_gap = ws->gaps_inner; | 138 | inner_gap = ws->gaps_inner; |
140 | } | 139 | } |
@@ -145,7 +144,7 @@ static void apply_vert_layout(list_t *children, struct wlr_box *parent) { | |||
145 | if (layout == L_TABBED || layout == L_STACKED) { | 144 | if (layout == L_TABBED || layout == L_STACKED) { |
146 | inner_gap = 0; | 145 | inner_gap = 0; |
147 | } | 146 | } |
148 | temp = temp->parent; | 147 | temp = temp->pending.parent; |
149 | } | 148 | } |
150 | double total_gap = fmin(inner_gap * (children->length - 1), | 149 | double total_gap = fmin(inner_gap * (children->length - 1), |
151 | fmax(0, parent->height - MIN_SANE_H * children->length)); | 150 | fmax(0, parent->height - MIN_SANE_H * children->length)); |
@@ -158,15 +157,15 @@ static void apply_vert_layout(list_t *children, struct wlr_box *parent) { | |||
158 | for (int i = 0; i < children->length; ++i) { | 157 | for (int i = 0; i < children->length; ++i) { |
159 | struct sway_container *child = children->items[i]; | 158 | struct sway_container *child = children->items[i]; |
160 | child->child_total_height = child_total_height; | 159 | child->child_total_height = child_total_height; |
161 | child->x = parent->x; | 160 | child->pending.x = parent->x; |
162 | child->y = child_y; | 161 | child->pending.y = child_y; |
163 | child->width = parent->width; | 162 | child->pending.width = parent->width; |
164 | child->height = round(child->height_fraction * child_total_height); | 163 | child->pending.height = round(child->height_fraction * child_total_height); |
165 | child_y += child->height + inner_gap; | 164 | child_y += child->pending.height + inner_gap; |
166 | 165 | ||
167 | // Make last child use remaining height of parent | 166 | // Make last child use remaining height of parent |
168 | if (i == children->length - 1) { | 167 | if (i == children->length - 1) { |
169 | child->height = parent->y + parent->height - child->y; | 168 | child->pending.height = parent->y + parent->height - child->pending.y; |
170 | } | 169 | } |
171 | } | 170 | } |
172 | } | 171 | } |
@@ -178,10 +177,10 @@ static void apply_tabbed_layout(list_t *children, struct wlr_box *parent) { | |||
178 | for (int i = 0; i < children->length; ++i) { | 177 | for (int i = 0; i < children->length; ++i) { |
179 | struct sway_container *child = children->items[i]; | 178 | struct sway_container *child = children->items[i]; |
180 | int parent_offset = child->view ? 0 : container_titlebar_height(); | 179 | int parent_offset = child->view ? 0 : container_titlebar_height(); |
181 | child->x = parent->x; | 180 | child->pending.x = parent->x; |
182 | child->y = parent->y + parent_offset; | 181 | child->pending.y = parent->y + parent_offset; |
183 | child->width = parent->width; | 182 | child->pending.width = parent->width; |
184 | child->height = parent->height - parent_offset; | 183 | child->pending.height = parent->height - parent_offset; |
185 | } | 184 | } |
186 | } | 185 | } |
187 | 186 | ||
@@ -193,10 +192,10 @@ static void apply_stacked_layout(list_t *children, struct wlr_box *parent) { | |||
193 | struct sway_container *child = children->items[i]; | 192 | struct sway_container *child = children->items[i]; |
194 | int parent_offset = child->view ? 0 : | 193 | int parent_offset = child->view ? 0 : |
195 | container_titlebar_height() * children->length; | 194 | container_titlebar_height() * children->length; |
196 | child->x = parent->x; | 195 | child->pending.x = parent->x; |
197 | child->y = parent->y + parent_offset; | 196 | child->pending.y = parent->y + parent_offset; |
198 | child->width = parent->width; | 197 | child->pending.width = parent->width; |
199 | child->height = parent->height - parent_offset; | 198 | child->pending.height = parent->height - parent_offset; |
200 | } | 199 | } |
201 | } | 200 | } |
202 | 201 | ||
@@ -246,7 +245,7 @@ void arrange_container(struct sway_container *container) { | |||
246 | } | 245 | } |
247 | struct wlr_box box; | 246 | struct wlr_box box; |
248 | container_get_box(container, &box); | 247 | container_get_box(container, &box); |
249 | arrange_children(container->children, container->layout, &box); | 248 | arrange_children(container->pending.children, container->pending.layout, &box); |
250 | node_set_dirty(&container->node); | 249 | node_set_dirty(&container->node); |
251 | } | 250 | } |
252 | 251 | ||
@@ -264,6 +263,9 @@ void arrange_workspace(struct sway_workspace *workspace) { | |||
264 | area->width, area->height, area->x, area->y); | 263 | area->width, area->height, area->x, area->y); |
265 | 264 | ||
266 | bool first_arrange = workspace->width == 0 && workspace->height == 0; | 265 | bool first_arrange = workspace->width == 0 && workspace->height == 0; |
266 | struct wlr_box prev_box; | ||
267 | workspace_get_box(workspace, &prev_box); | ||
268 | |||
267 | double prev_x = workspace->x - workspace->current_gaps.left; | 269 | double prev_x = workspace->x - workspace->current_gaps.left; |
268 | double prev_y = workspace->y - workspace->current_gaps.top; | 270 | double prev_y = workspace->y - workspace->current_gaps.top; |
269 | workspace->width = area->width; | 271 | workspace->width = area->width; |
@@ -277,13 +279,14 @@ void arrange_workspace(struct sway_workspace *workspace) { | |||
277 | if (!first_arrange && (diff_x != 0 || diff_y != 0)) { | 279 | if (!first_arrange && (diff_x != 0 || diff_y != 0)) { |
278 | for (int i = 0; i < workspace->floating->length; ++i) { | 280 | for (int i = 0; i < workspace->floating->length; ++i) { |
279 | struct sway_container *floater = workspace->floating->items[i]; | 281 | struct sway_container *floater = workspace->floating->items[i]; |
280 | container_floating_translate(floater, diff_x, diff_y); | ||
281 | double center_x = floater->x + floater->width / 2; | ||
282 | double center_y = floater->y + floater->height / 2; | ||
283 | struct wlr_box workspace_box; | 282 | struct wlr_box workspace_box; |
284 | workspace_get_box(workspace, &workspace_box); | 283 | workspace_get_box(workspace, &workspace_box); |
285 | if (!wlr_box_contains_point(&workspace_box, center_x, center_y)) { | 284 | floating_fix_coordinates(floater, &prev_box, &workspace_box); |
286 | container_floating_move_to_center(floater); | 285 | // Set transformation for scratchpad windows. |
286 | if (floater->scratchpad) { | ||
287 | struct wlr_box output_box; | ||
288 | output_get_box(output, &output_box); | ||
289 | floater->transform = output_box; | ||
287 | } | 290 | } |
288 | } | 291 | } |
289 | } | 292 | } |
@@ -294,10 +297,10 @@ void arrange_workspace(struct sway_workspace *workspace) { | |||
294 | workspace->x, workspace->y); | 297 | workspace->x, workspace->y); |
295 | if (workspace->fullscreen) { | 298 | if (workspace->fullscreen) { |
296 | struct sway_container *fs = workspace->fullscreen; | 299 | struct sway_container *fs = workspace->fullscreen; |
297 | fs->x = output->lx; | 300 | fs->pending.x = output->lx; |
298 | fs->y = output->ly; | 301 | fs->pending.y = output->ly; |
299 | fs->width = output->width; | 302 | fs->pending.width = output->width; |
300 | fs->height = output->height; | 303 | fs->pending.height = output->height; |
301 | arrange_container(fs); | 304 | arrange_container(fs); |
302 | } else { | 305 | } else { |
303 | struct wlr_box box; | 306 | struct wlr_box box; |
@@ -311,12 +314,13 @@ void arrange_output(struct sway_output *output) { | |||
311 | if (config->reloading) { | 314 | if (config->reloading) { |
312 | return; | 315 | return; |
313 | } | 316 | } |
314 | const struct wlr_box *output_box = wlr_output_layout_get_box( | 317 | struct wlr_box output_box; |
315 | root->output_layout, output->wlr_output); | 318 | wlr_output_layout_get_box(root->output_layout, |
316 | output->lx = output_box->x; | 319 | output->wlr_output, &output_box); |
317 | output->ly = output_box->y; | 320 | output->lx = output_box.x; |
318 | output->width = output_box->width; | 321 | output->ly = output_box.y; |
319 | output->height = output_box->height; | 322 | output->width = output_box.width; |
323 | output->height = output_box.height; | ||
320 | 324 | ||
321 | for (int i = 0; i < output->workspaces->length; ++i) { | 325 | for (int i = 0; i < output->workspaces->length; ++i) { |
322 | struct sway_workspace *workspace = output->workspaces->items[i]; | 326 | struct sway_workspace *workspace = output->workspaces->items[i]; |
@@ -328,19 +332,19 @@ void arrange_root(void) { | |||
328 | if (config->reloading) { | 332 | if (config->reloading) { |
329 | return; | 333 | return; |
330 | } | 334 | } |
331 | const struct wlr_box *layout_box = | 335 | struct wlr_box layout_box; |
332 | wlr_output_layout_get_box(root->output_layout, NULL); | 336 | wlr_output_layout_get_box(root->output_layout, NULL, &layout_box); |
333 | root->x = layout_box->x; | 337 | root->x = layout_box.x; |
334 | root->y = layout_box->y; | 338 | root->y = layout_box.y; |
335 | root->width = layout_box->width; | 339 | root->width = layout_box.width; |
336 | root->height = layout_box->height; | 340 | root->height = layout_box.height; |
337 | 341 | ||
338 | if (root->fullscreen_global) { | 342 | if (root->fullscreen_global) { |
339 | struct sway_container *fs = root->fullscreen_global; | 343 | struct sway_container *fs = root->fullscreen_global; |
340 | fs->x = root->x; | 344 | fs->pending.x = root->x; |
341 | fs->y = root->y; | 345 | fs->pending.y = root->y; |
342 | fs->width = root->width; | 346 | fs->pending.width = root->width; |
343 | fs->height = root->height; | 347 | fs->pending.height = root->height; |
344 | arrange_container(fs); | 348 | arrange_container(fs); |
345 | } else { | 349 | } else { |
346 | for (int i = 0; i < root->outputs->length; ++i) { | 350 | for (int i = 0; i < root->outputs->length; ++i) { |
diff --git a/sway/tree/container.c b/sway/tree/container.c index 6a9ce1c4..80ef34fe 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c | |||
@@ -1,28 +1,77 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <assert.h> | 1 | #include <assert.h> |
2 | #include <drm_fourcc.h> | ||
3 | #include <stdint.h> | 3 | #include <stdint.h> |
4 | #include <stdlib.h> | 4 | #include <stdlib.h> |
5 | #include <string.h> | ||
6 | #include <strings.h> | ||
7 | #include <wayland-server-core.h> | 5 | #include <wayland-server-core.h> |
6 | #include <wlr/types/wlr_foreign_toplevel_management_v1.h> | ||
7 | #include <wlr/types/wlr_linux_dmabuf_v1.h> | ||
8 | #include <wlr/types/wlr_output_layout.h> | 8 | #include <wlr/types/wlr_output_layout.h> |
9 | #include "cairo.h" | 9 | #include <wlr/types/wlr_subcompositor.h> |
10 | #include "pango.h" | 10 | #include "linux-dmabuf-unstable-v1-protocol.h" |
11 | #include "sway/config.h" | 11 | #include "sway/config.h" |
12 | #include "sway/desktop.h" | ||
13 | #include "sway/desktop/transaction.h" | 12 | #include "sway/desktop/transaction.h" |
14 | #include "sway/input/input-manager.h" | 13 | #include "sway/input/input-manager.h" |
15 | #include "sway/input/seat.h" | 14 | #include "sway/input/seat.h" |
16 | #include "sway/ipc-server.h" | 15 | #include "sway/ipc-server.h" |
16 | #include "sway/scene_descriptor.h" | ||
17 | #include "sway/sway_text_node.h" | ||
17 | #include "sway/output.h" | 18 | #include "sway/output.h" |
18 | #include "sway/server.h" | 19 | #include "sway/server.h" |
19 | #include "sway/tree/arrange.h" | 20 | #include "sway/tree/arrange.h" |
20 | #include "sway/tree/view.h" | 21 | #include "sway/tree/view.h" |
21 | #include "sway/tree/workspace.h" | 22 | #include "sway/tree/workspace.h" |
23 | #include "sway/xdg_decoration.h" | ||
22 | #include "list.h" | 24 | #include "list.h" |
23 | #include "log.h" | 25 | #include "log.h" |
24 | #include "stringop.h" | 26 | #include "stringop.h" |
25 | 27 | ||
28 | static void handle_output_enter( | ||
29 | struct wl_listener *listener, void *data) { | ||
30 | struct sway_container *con = wl_container_of( | ||
31 | listener, con, output_enter); | ||
32 | struct wlr_scene_output *output = data; | ||
33 | |||
34 | if (con->view->foreign_toplevel) { | ||
35 | wlr_foreign_toplevel_handle_v1_output_enter( | ||
36 | con->view->foreign_toplevel, output->output); | ||
37 | } | ||
38 | } | ||
39 | |||
40 | static void handle_output_leave( | ||
41 | struct wl_listener *listener, void *data) { | ||
42 | struct sway_container *con = wl_container_of( | ||
43 | listener, con, output_leave); | ||
44 | struct wlr_scene_output *output = data; | ||
45 | |||
46 | if (con->view->foreign_toplevel) { | ||
47 | wlr_foreign_toplevel_handle_v1_output_leave( | ||
48 | con->view->foreign_toplevel, output->output); | ||
49 | } | ||
50 | } | ||
51 | |||
52 | static bool handle_point_accepts_input( | ||
53 | struct wlr_scene_buffer *buffer, double *x, double *y) { | ||
54 | return false; | ||
55 | } | ||
56 | |||
57 | static struct wlr_scene_rect *alloc_rect_node(struct wlr_scene_tree *parent, | ||
58 | bool *failed) { | ||
59 | if (*failed) { | ||
60 | return NULL; | ||
61 | } | ||
62 | |||
63 | // just pass in random values. These will be overwritten when | ||
64 | // they need to be used. | ||
65 | struct wlr_scene_rect *rect = wlr_scene_rect_create( | ||
66 | parent, 0, 0, (float[4]){0.f, 0.f, 0.f, 1.f}); | ||
67 | if (!rect) { | ||
68 | sway_log(SWAY_ERROR, "Failed to allocate a wlr_scene_rect"); | ||
69 | *failed = true; | ||
70 | } | ||
71 | |||
72 | return rect; | ||
73 | } | ||
74 | |||
26 | struct sway_container *container_create(struct sway_view *view) { | 75 | struct sway_container *container_create(struct sway_view *view) { |
27 | struct sway_container *c = calloc(1, sizeof(struct sway_container)); | 76 | struct sway_container *c = calloc(1, sizeof(struct sway_container)); |
28 | if (!c) { | 77 | if (!c) { |
@@ -30,23 +79,415 @@ struct sway_container *container_create(struct sway_view *view) { | |||
30 | return NULL; | 79 | return NULL; |
31 | } | 80 | } |
32 | node_init(&c->node, N_CONTAINER, c); | 81 | node_init(&c->node, N_CONTAINER, c); |
33 | c->layout = L_NONE; | 82 | |
34 | c->view = view; | 83 | // Container tree structure |
35 | c->alpha = 1.0f; | 84 | // - scene tree |
85 | // - title bar | ||
86 | // - border | ||
87 | // - background | ||
88 | // - title text | ||
89 | // - marks text | ||
90 | // - border | ||
91 | // - border top/bottom/left/right | ||
92 | // - content_tree (we put the content node here so when we disable the | ||
93 | // border everything gets disabled. We only render the content iff there | ||
94 | // is a border as well) | ||
95 | // - buffer used for output enter/leave events for foreign_toplevel | ||
96 | bool failed = false; | ||
97 | c->scene_tree = alloc_scene_tree(root->staging, &failed); | ||
98 | |||
99 | c->title_bar.tree = alloc_scene_tree(c->scene_tree, &failed); | ||
100 | c->title_bar.border = alloc_scene_tree(c->title_bar.tree, &failed); | ||
101 | c->title_bar.background = alloc_scene_tree(c->title_bar.tree, &failed); | ||
102 | |||
103 | // for opacity purposes we need to carfully create the scene such that | ||
104 | // none of our rect nodes as well as text buffers don't overlap. To do | ||
105 | // this we have to create rects such that they go around text buffers | ||
106 | for (int i = 0; i < 4; i++) { | ||
107 | alloc_rect_node(c->title_bar.border, &failed); | ||
108 | } | ||
109 | |||
110 | for (int i = 0; i < 5; i++) { | ||
111 | alloc_rect_node(c->title_bar.background, &failed); | ||
112 | } | ||
113 | |||
114 | c->border.tree = alloc_scene_tree(c->scene_tree, &failed); | ||
115 | c->content_tree = alloc_scene_tree(c->border.tree, &failed); | ||
116 | |||
117 | if (view) { | ||
118 | // only containers with views can have borders | ||
119 | c->border.top = alloc_rect_node(c->border.tree, &failed); | ||
120 | c->border.bottom = alloc_rect_node(c->border.tree, &failed); | ||
121 | c->border.left = alloc_rect_node(c->border.tree, &failed); | ||
122 | c->border.right = alloc_rect_node(c->border.tree, &failed); | ||
123 | |||
124 | c->output_handler = wlr_scene_buffer_create(c->border.tree, NULL); | ||
125 | if (!c->output_handler) { | ||
126 | sway_log(SWAY_ERROR, "Failed to allocate a scene node"); | ||
127 | failed = true; | ||
128 | } | ||
129 | |||
130 | if (!failed) { | ||
131 | c->output_enter.notify = handle_output_enter; | ||
132 | wl_signal_add(&c->output_handler->events.output_enter, | ||
133 | &c->output_enter); | ||
134 | c->output_leave.notify = handle_output_leave; | ||
135 | wl_signal_add(&c->output_handler->events.output_leave, | ||
136 | &c->output_leave); | ||
137 | c->output_handler->point_accepts_input = handle_point_accepts_input; | ||
138 | } | ||
139 | } | ||
140 | |||
141 | if (!failed && !scene_descriptor_assign(&c->scene_tree->node, | ||
142 | SWAY_SCENE_DESC_CONTAINER, c)) { | ||
143 | failed = true; | ||
144 | } | ||
145 | |||
146 | if (failed) { | ||
147 | wlr_scene_node_destroy(&c->scene_tree->node); | ||
148 | free(c); | ||
149 | return NULL; | ||
150 | } | ||
36 | 151 | ||
37 | if (!view) { | 152 | if (!view) { |
38 | c->children = create_list(); | 153 | c->pending.children = create_list(); |
39 | c->current.children = create_list(); | 154 | c->current.children = create_list(); |
40 | } | 155 | } |
156 | |||
157 | c->pending.layout = L_NONE; | ||
158 | c->view = view; | ||
159 | c->alpha = 1.0f; | ||
41 | c->marks = create_list(); | 160 | c->marks = create_list(); |
42 | c->outputs = create_list(); | ||
43 | 161 | ||
44 | wl_signal_init(&c->events.destroy); | 162 | wl_signal_init(&c->events.destroy); |
45 | wl_signal_emit(&root->events.new_node, &c->node); | 163 | wl_signal_emit_mutable(&root->events.new_node, &c->node); |
164 | |||
165 | container_update(c); | ||
46 | 166 | ||
47 | return c; | 167 | return c; |
48 | } | 168 | } |
49 | 169 | ||
170 | static bool container_is_focused(struct sway_container *con, void *data) { | ||
171 | return con->current.focused; | ||
172 | } | ||
173 | |||
174 | static bool container_has_focused_child(struct sway_container *con) { | ||
175 | return container_find_child(con, container_is_focused, NULL); | ||
176 | } | ||
177 | |||
178 | static bool container_is_current_parent_focused(struct sway_container *con) { | ||
179 | if (con->current.parent) { | ||
180 | struct sway_container *parent = con->current.parent; | ||
181 | return parent->current.focused || container_is_current_parent_focused(parent); | ||
182 | } else if (con->current.workspace) { | ||
183 | struct sway_workspace *ws = con->current.workspace; | ||
184 | return ws->current.focused; | ||
185 | } | ||
186 | |||
187 | return false; | ||
188 | } | ||
189 | |||
190 | static struct border_colors *container_get_current_colors( | ||
191 | struct sway_container *con) { | ||
192 | struct border_colors *colors; | ||
193 | |||
194 | bool urgent = con->view ? | ||
195 | view_is_urgent(con->view) : container_has_urgent_child(con); | ||
196 | struct sway_container *active_child; | ||
197 | |||
198 | if (con->current.parent) { | ||
199 | active_child = con->current.parent->current.focused_inactive_child; | ||
200 | } else if (con->current.workspace) { | ||
201 | active_child = con->current.workspace->current.focused_inactive_child; | ||
202 | } else { | ||
203 | active_child = NULL; | ||
204 | } | ||
205 | |||
206 | if (urgent) { | ||
207 | colors = &config->border_colors.urgent; | ||
208 | } else if (con->current.focused || container_is_current_parent_focused(con)) { | ||
209 | colors = &config->border_colors.focused; | ||
210 | } else if (config->has_focused_tab_title && container_has_focused_child(con)) { | ||
211 | colors = &config->border_colors.focused_tab_title; | ||
212 | } else if (con == active_child) { | ||
213 | colors = &config->border_colors.focused_inactive; | ||
214 | } else { | ||
215 | colors = &config->border_colors.unfocused; | ||
216 | } | ||
217 | |||
218 | return colors; | ||
219 | } | ||
220 | |||
221 | static bool container_is_current_floating(struct sway_container *container) { | ||
222 | if (!container->current.parent && container->current.workspace && | ||
223 | list_find(container->current.workspace->floating, container) != -1) { | ||
224 | return true; | ||
225 | } | ||
226 | if (container->scratchpad) { | ||
227 | return true; | ||
228 | } | ||
229 | return false; | ||
230 | } | ||
231 | |||
232 | // scene rect wants premultiplied colors | ||
233 | static void scene_rect_set_color(struct wlr_scene_rect *rect, | ||
234 | const float color[4], float opacity) { | ||
235 | const float premultiplied[] = { | ||
236 | color[0] * color[3] * opacity, | ||
237 | color[1] * color[3] * opacity, | ||
238 | color[2] * color[3] * opacity, | ||
239 | color[3] * opacity, | ||
240 | }; | ||
241 | |||
242 | wlr_scene_rect_set_color(rect, premultiplied); | ||
243 | } | ||
244 | |||
245 | void container_update(struct sway_container *con) { | ||
246 | struct border_colors *colors = container_get_current_colors(con); | ||
247 | list_t *siblings = NULL; | ||
248 | enum sway_container_layout layout = L_NONE; | ||
249 | float alpha = con->alpha; | ||
250 | |||
251 | if (con->current.parent) { | ||
252 | siblings = con->current.parent->current.children; | ||
253 | layout = con->current.parent->current.layout; | ||
254 | } else if (con->current.workspace) { | ||
255 | siblings = con->current.workspace->current.tiling; | ||
256 | layout = con->current.workspace->current.layout; | ||
257 | } | ||
258 | |||
259 | float bottom[4], right[4]; | ||
260 | memcpy(bottom, colors->child_border, sizeof(bottom)); | ||
261 | memcpy(right, colors->child_border, sizeof(right)); | ||
262 | |||
263 | if (!container_is_current_floating(con) && siblings && siblings->length == 1) { | ||
264 | if (layout == L_HORIZ) { | ||
265 | memcpy(right, colors->indicator, sizeof(right)); | ||
266 | } else if (layout == L_VERT) { | ||
267 | memcpy(bottom, colors->indicator, sizeof(bottom)); | ||
268 | } | ||
269 | } | ||
270 | |||
271 | struct wlr_scene_node *node; | ||
272 | wl_list_for_each(node, &con->title_bar.border->children, link) { | ||
273 | struct wlr_scene_rect *rect = wlr_scene_rect_from_node(node); | ||
274 | scene_rect_set_color(rect, colors->border, alpha); | ||
275 | } | ||
276 | |||
277 | wl_list_for_each(node, &con->title_bar.background->children, link) { | ||
278 | struct wlr_scene_rect *rect = wlr_scene_rect_from_node(node); | ||
279 | scene_rect_set_color(rect, colors->background, alpha); | ||
280 | } | ||
281 | |||
282 | if (con->view) { | ||
283 | scene_rect_set_color(con->border.top, colors->child_border, alpha); | ||
284 | scene_rect_set_color(con->border.bottom, bottom, alpha); | ||
285 | scene_rect_set_color(con->border.left, colors->child_border, alpha); | ||
286 | scene_rect_set_color(con->border.right, right, alpha); | ||
287 | } | ||
288 | |||
289 | if (con->title_bar.title_text) { | ||
290 | sway_text_node_set_color(con->title_bar.title_text, colors->text); | ||
291 | sway_text_node_set_background(con->title_bar.title_text, colors->background); | ||
292 | } | ||
293 | |||
294 | if (con->title_bar.marks_text) { | ||
295 | sway_text_node_set_color(con->title_bar.marks_text, colors->text); | ||
296 | sway_text_node_set_background(con->title_bar.marks_text, colors->background); | ||
297 | } | ||
298 | } | ||
299 | |||
300 | void container_update_itself_and_parents(struct sway_container *con) { | ||
301 | container_update(con); | ||
302 | |||
303 | if (con->current.parent) { | ||
304 | container_update_itself_and_parents(con->current.parent); | ||
305 | } | ||
306 | } | ||
307 | |||
308 | static void update_rect_list(struct wlr_scene_tree *tree, pixman_region32_t *region) { | ||
309 | int len; | ||
310 | const pixman_box32_t *rects = pixman_region32_rectangles(region, &len); | ||
311 | |||
312 | wlr_scene_node_set_enabled(&tree->node, len > 0); | ||
313 | if (len == 0) { | ||
314 | return; | ||
315 | } | ||
316 | |||
317 | int i = 0; | ||
318 | struct wlr_scene_node *node; | ||
319 | wl_list_for_each(node, &tree->children, link) { | ||
320 | struct wlr_scene_rect *rect = wlr_scene_rect_from_node(node); | ||
321 | wlr_scene_node_set_enabled(&rect->node, i < len); | ||
322 | |||
323 | if (i < len) { | ||
324 | const pixman_box32_t *box = &rects[i++]; | ||
325 | wlr_scene_node_set_position(&rect->node, box->x1, box->y1); | ||
326 | wlr_scene_rect_set_size(rect, box->x2 - box->x1, box->y2 - box->y1); | ||
327 | } | ||
328 | } | ||
329 | } | ||
330 | |||
331 | void container_arrange_title_bar(struct sway_container *con) { | ||
332 | enum alignment title_align = config->title_align; | ||
333 | int marks_buffer_width = 0; | ||
334 | int width = con->title_width; | ||
335 | int height = container_titlebar_height(); | ||
336 | |||
337 | pixman_region32_t text_area; | ||
338 | pixman_region32_init(&text_area); | ||
339 | |||
340 | if (con->title_bar.marks_text) { | ||
341 | struct sway_text_node *node = con->title_bar.marks_text; | ||
342 | marks_buffer_width = node->width; | ||
343 | |||
344 | int h_padding; | ||
345 | if (title_align == ALIGN_RIGHT) { | ||
346 | h_padding = config->titlebar_h_padding; | ||
347 | } else { | ||
348 | h_padding = width - config->titlebar_h_padding - marks_buffer_width; | ||
349 | } | ||
350 | |||
351 | h_padding = MAX(h_padding, 0); | ||
352 | |||
353 | int alloc_width = MIN((int)node->width, | ||
354 | width - h_padding - config->titlebar_h_padding); | ||
355 | alloc_width = MAX(alloc_width, 0); | ||
356 | |||
357 | sway_text_node_set_max_width(node, alloc_width); | ||
358 | wlr_scene_node_set_position(node->node, | ||
359 | h_padding, (height - node->height) >> 1); | ||
360 | |||
361 | pixman_region32_union_rect(&text_area, &text_area, | ||
362 | node->node->x, node->node->y, alloc_width, node->height); | ||
363 | } | ||
364 | |||
365 | if (con->title_bar.title_text) { | ||
366 | struct sway_text_node *node = con->title_bar.title_text; | ||
367 | |||
368 | int h_padding; | ||
369 | if (title_align == ALIGN_RIGHT) { | ||
370 | h_padding = width - config->titlebar_h_padding - node->width; | ||
371 | } else if (title_align == ALIGN_CENTER) { | ||
372 | h_padding = ((int)width - marks_buffer_width - node->width) >> 1; | ||
373 | } else { | ||
374 | h_padding = config->titlebar_h_padding; | ||
375 | } | ||
376 | |||
377 | h_padding = MAX(h_padding, 0); | ||
378 | |||
379 | int alloc_width = MIN((int) node->width, | ||
380 | width - h_padding - config->titlebar_h_padding); | ||
381 | alloc_width = MAX(alloc_width, 0); | ||
382 | |||
383 | sway_text_node_set_max_width(node, alloc_width); | ||
384 | wlr_scene_node_set_position(node->node, | ||
385 | h_padding, (height - node->height) >> 1); | ||
386 | |||
387 | pixman_region32_union_rect(&text_area, &text_area, | ||
388 | node->node->x, node->node->y, alloc_width, node->height); | ||
389 | } | ||
390 | |||
391 | // silence pixman errors | ||
392 | if (width <= 0 || height <= 0) { | ||
393 | pixman_region32_fini(&text_area); | ||
394 | return; | ||
395 | } | ||
396 | |||
397 | pixman_region32_t background, border; | ||
398 | |||
399 | int thickness = config->titlebar_border_thickness; | ||
400 | pixman_region32_init_rect(&background, | ||
401 | thickness, thickness, | ||
402 | width - thickness * 2, height - thickness * 2); | ||
403 | pixman_region32_init_rect(&border, 0, 0, width, height); | ||
404 | pixman_region32_subtract(&border, &border, &background); | ||
405 | |||
406 | pixman_region32_subtract(&background, &background, &text_area); | ||
407 | pixman_region32_fini(&text_area); | ||
408 | |||
409 | update_rect_list(con->title_bar.background, &background); | ||
410 | pixman_region32_fini(&background); | ||
411 | |||
412 | update_rect_list(con->title_bar.border, &border); | ||
413 | pixman_region32_fini(&border); | ||
414 | |||
415 | container_update(con); | ||
416 | } | ||
417 | |||
418 | void container_update_marks(struct sway_container *con) { | ||
419 | char *buffer = NULL; | ||
420 | |||
421 | if (config->show_marks && con->marks->length) { | ||
422 | size_t len = 0; | ||
423 | for (int i = 0; i < con->marks->length; ++i) { | ||
424 | char *mark = con->marks->items[i]; | ||
425 | if (mark[0] != '_') { | ||
426 | len += strlen(mark) + 2; | ||
427 | } | ||
428 | } | ||
429 | buffer = calloc(len + 1, 1); | ||
430 | char *part = malloc(len + 1); | ||
431 | |||
432 | if (!sway_assert(buffer && part, "Unable to allocate memory")) { | ||
433 | free(buffer); | ||
434 | return; | ||
435 | } | ||
436 | |||
437 | for (int i = 0; i < con->marks->length; ++i) { | ||
438 | char *mark = con->marks->items[i]; | ||
439 | if (mark[0] != '_') { | ||
440 | snprintf(part, len + 1, "[%s]", mark); | ||
441 | strcat(buffer, part); | ||
442 | } | ||
443 | } | ||
444 | free(part); | ||
445 | } | ||
446 | |||
447 | if (!buffer) { | ||
448 | if (con->title_bar.marks_text) { | ||
449 | wlr_scene_node_destroy(con->title_bar.marks_text->node); | ||
450 | con->title_bar.marks_text = NULL; | ||
451 | } | ||
452 | } else if (!con->title_bar.marks_text) { | ||
453 | struct border_colors *colors = container_get_current_colors(con); | ||
454 | |||
455 | con->title_bar.marks_text = sway_text_node_create(con->title_bar.tree, | ||
456 | buffer, colors->text, false); | ||
457 | } else { | ||
458 | sway_text_node_set_text(con->title_bar.marks_text, buffer); | ||
459 | } | ||
460 | |||
461 | container_arrange_title_bar(con); | ||
462 | free(buffer); | ||
463 | } | ||
464 | |||
465 | void container_update_title_bar(struct sway_container *con) { | ||
466 | if (!con->formatted_title) { | ||
467 | return; | ||
468 | } | ||
469 | |||
470 | struct border_colors *colors = container_get_current_colors(con); | ||
471 | |||
472 | if (con->title_bar.title_text) { | ||
473 | wlr_scene_node_destroy(con->title_bar.title_text->node); | ||
474 | con->title_bar.title_text = NULL; | ||
475 | } | ||
476 | |||
477 | con->title_bar.title_text = sway_text_node_create(con->title_bar.tree, | ||
478 | con->formatted_title, colors->text, config->pango_markup); | ||
479 | |||
480 | // we always have to remake these text buffers completely for text font | ||
481 | // changes etc... | ||
482 | if (con->title_bar.marks_text) { | ||
483 | wlr_scene_node_destroy(con->title_bar.marks_text->node); | ||
484 | con->title_bar.marks_text = NULL; | ||
485 | } | ||
486 | |||
487 | container_update_marks(con); | ||
488 | container_arrange_title_bar(con); | ||
489 | } | ||
490 | |||
50 | void container_destroy(struct sway_container *con) { | 491 | void container_destroy(struct sway_container *con) { |
51 | if (!sway_assert(con->node.destroying, | 492 | if (!sway_assert(con->node.destroying, |
52 | "Tried to free container which wasn't marked as destroying")) { | 493 | "Tried to free container which wasn't marked as destroying")) { |
@@ -58,29 +499,21 @@ void container_destroy(struct sway_container *con) { | |||
58 | } | 499 | } |
59 | free(con->title); | 500 | free(con->title); |
60 | free(con->formatted_title); | 501 | free(con->formatted_title); |
61 | wlr_texture_destroy(con->title_focused); | 502 | list_free(con->pending.children); |
62 | wlr_texture_destroy(con->title_focused_inactive); | ||
63 | wlr_texture_destroy(con->title_unfocused); | ||
64 | wlr_texture_destroy(con->title_urgent); | ||
65 | list_free(con->children); | ||
66 | list_free(con->current.children); | 503 | list_free(con->current.children); |
67 | list_free(con->outputs); | ||
68 | 504 | ||
69 | list_free_items_and_destroy(con->marks); | 505 | list_free_items_and_destroy(con->marks); |
70 | wlr_texture_destroy(con->marks_focused); | ||
71 | wlr_texture_destroy(con->marks_focused_inactive); | ||
72 | wlr_texture_destroy(con->marks_unfocused); | ||
73 | wlr_texture_destroy(con->marks_urgent); | ||
74 | 506 | ||
75 | if (con->view) { | 507 | if (con->view && con->view->container == con) { |
76 | if (con->view->container == con) { | 508 | con->view->container = NULL; |
77 | con->view->container = NULL; | 509 | wlr_scene_node_destroy(&con->output_handler->node); |
78 | } | ||
79 | if (con->view->destroying) { | 510 | if (con->view->destroying) { |
80 | view_destroy(con->view); | 511 | view_destroy(con->view); |
81 | } | 512 | } |
82 | } | 513 | } |
83 | 514 | ||
515 | scene_node_disown_children(con->content_tree); | ||
516 | wlr_scene_node_destroy(&con->scene_tree->node); | ||
84 | free(con); | 517 | free(con); |
85 | } | 518 | } |
86 | 519 | ||
@@ -90,14 +523,14 @@ void container_begin_destroy(struct sway_container *con) { | |||
90 | } | 523 | } |
91 | // The workspace must have the fullscreen pointer cleared so that the | 524 | // The workspace must have the fullscreen pointer cleared so that the |
92 | // seat code can find an appropriate new focus. | 525 | // seat code can find an appropriate new focus. |
93 | if (con->fullscreen_mode == FULLSCREEN_WORKSPACE && con->workspace) { | 526 | if (con->pending.fullscreen_mode == FULLSCREEN_WORKSPACE && con->pending.workspace) { |
94 | con->workspace->fullscreen = NULL; | 527 | con->pending.workspace->fullscreen = NULL; |
95 | } | 528 | } |
96 | if (con->scratchpad && con->fullscreen_mode == FULLSCREEN_GLOBAL) { | 529 | if (con->scratchpad && con->pending.fullscreen_mode == FULLSCREEN_GLOBAL) { |
97 | container_fullscreen_disable(con); | 530 | container_fullscreen_disable(con); |
98 | } | 531 | } |
99 | 532 | ||
100 | wl_signal_emit(&con->node.events.destroy, &con->node); | 533 | wl_signal_emit_mutable(&con->node.events.destroy, &con->node); |
101 | 534 | ||
102 | container_end_mouse_operation(con); | 535 | container_end_mouse_operation(con); |
103 | 536 | ||
@@ -108,11 +541,11 @@ void container_begin_destroy(struct sway_container *con) { | |||
108 | root_scratchpad_remove_container(con); | 541 | root_scratchpad_remove_container(con); |
109 | } | 542 | } |
110 | 543 | ||
111 | if (con->fullscreen_mode == FULLSCREEN_GLOBAL) { | 544 | if (con->pending.fullscreen_mode == FULLSCREEN_GLOBAL) { |
112 | container_fullscreen_disable(con); | 545 | container_fullscreen_disable(con); |
113 | } | 546 | } |
114 | 547 | ||
115 | if (con->parent || con->workspace) { | 548 | if (con->pending.parent || con->pending.workspace) { |
116 | container_detach(con); | 549 | container_detach(con); |
117 | } | 550 | } |
118 | } | 551 | } |
@@ -121,12 +554,12 @@ void container_reap_empty(struct sway_container *con) { | |||
121 | if (con->view) { | 554 | if (con->view) { |
122 | return; | 555 | return; |
123 | } | 556 | } |
124 | struct sway_workspace *ws = con->workspace; | 557 | struct sway_workspace *ws = con->pending.workspace; |
125 | while (con) { | 558 | while (con) { |
126 | if (con->children->length) { | 559 | if (con->pending.children->length) { |
127 | return; | 560 | return; |
128 | } | 561 | } |
129 | struct sway_container *parent = con->parent; | 562 | struct sway_container *parent = con->pending.parent; |
130 | container_begin_destroy(con); | 563 | container_begin_destroy(con); |
131 | con = parent; | 564 | con = parent; |
132 | } | 565 | } |
@@ -139,9 +572,9 @@ struct sway_container *container_flatten(struct sway_container *container) { | |||
139 | if (container->view) { | 572 | if (container->view) { |
140 | return NULL; | 573 | return NULL; |
141 | } | 574 | } |
142 | while (container && container->children->length == 1) { | 575 | while (container && container->pending.children->length == 1) { |
143 | struct sway_container *child = container->children->items[0]; | 576 | struct sway_container *child = container->pending.children->items[0]; |
144 | struct sway_container *parent = container->parent; | 577 | struct sway_container *parent = container->pending.parent; |
145 | container_replace(container, child); | 578 | container_replace(container, child); |
146 | container_begin_destroy(container); | 579 | container_begin_destroy(container); |
147 | container = parent; | 580 | container = parent; |
@@ -151,11 +584,11 @@ struct sway_container *container_flatten(struct sway_container *container) { | |||
151 | 584 | ||
152 | struct sway_container *container_find_child(struct sway_container *container, | 585 | struct sway_container *container_find_child(struct sway_container *container, |
153 | bool (*test)(struct sway_container *con, void *data), void *data) { | 586 | bool (*test)(struct sway_container *con, void *data), void *data) { |
154 | if (!container->children) { | 587 | if (!container->pending.children) { |
155 | return NULL; | 588 | return NULL; |
156 | } | 589 | } |
157 | for (int i = 0; i < container->children->length; ++i) { | 590 | for (int i = 0; i < container->pending.children->length; ++i) { |
158 | struct sway_container *child = container->children->items[i]; | 591 | struct sway_container *child = container->pending.children->items[i]; |
159 | if (test(child, data)) { | 592 | if (test(child, data)) { |
160 | return child; | 593 | return child; |
161 | } | 594 | } |
@@ -167,260 +600,44 @@ struct sway_container *container_find_child(struct sway_container *container, | |||
167 | return NULL; | 600 | return NULL; |
168 | } | 601 | } |
169 | 602 | ||
170 | static struct sway_container *surface_at_view(struct sway_container *con, double lx, double ly, | 603 | void container_for_each_child(struct sway_container *container, |
171 | struct wlr_surface **surface, double *sx, double *sy) { | 604 | void (*f)(struct sway_container *container, void *data), |
172 | if (!sway_assert(con->view, "Expected a view")) { | 605 | void *data) { |
173 | return NULL; | 606 | if (container->pending.children) { |
174 | } | 607 | for (int i = 0; i < container->pending.children->length; ++i) { |
175 | struct sway_view *view = con->view; | 608 | struct sway_container *child = container->pending.children->items[i]; |
176 | double view_sx = lx - con->surface_x + view->geometry.x; | 609 | f(child, data); |
177 | double view_sy = ly - con->surface_y + view->geometry.y; | 610 | container_for_each_child(child, f, data); |
178 | |||
179 | double _sx, _sy; | ||
180 | struct wlr_surface *_surface = NULL; | ||
181 | switch (view->type) { | ||
182 | #if HAVE_XWAYLAND | ||
183 | case SWAY_VIEW_XWAYLAND: | ||
184 | _surface = wlr_surface_surface_at(view->surface, | ||
185 | view_sx, view_sy, &_sx, &_sy); | ||
186 | break; | ||
187 | #endif | ||
188 | case SWAY_VIEW_XDG_SHELL: | ||
189 | _surface = wlr_xdg_surface_surface_at( | ||
190 | view->wlr_xdg_surface, | ||
191 | view_sx, view_sy, &_sx, &_sy); | ||
192 | break; | ||
193 | } | ||
194 | if (_surface) { | ||
195 | *sx = _sx; | ||
196 | *sy = _sy; | ||
197 | *surface = _surface; | ||
198 | return con; | ||
199 | } | ||
200 | return NULL; | ||
201 | } | ||
202 | |||
203 | /** | ||
204 | * container_at for a container with layout L_TABBED. | ||
205 | */ | ||
206 | static struct sway_container *container_at_tabbed(struct sway_node *parent, | ||
207 | double lx, double ly, | ||
208 | struct wlr_surface **surface, double *sx, double *sy) { | ||
209 | struct wlr_box box; | ||
210 | node_get_box(parent, &box); | ||
211 | if (lx < box.x || lx > box.x + box.width || | ||
212 | ly < box.y || ly > box.y + box.height) { | ||
213 | return NULL; | ||
214 | } | ||
215 | struct sway_seat *seat = input_manager_current_seat(); | ||
216 | list_t *children = node_get_children(parent); | ||
217 | if (!children->length) { | ||
218 | return NULL; | ||
219 | } | ||
220 | |||
221 | // Tab titles | ||
222 | int title_height = container_titlebar_height(); | ||
223 | if (ly < box.y + title_height) { | ||
224 | int tab_width = box.width / children->length; | ||
225 | int child_index = (lx - box.x) / tab_width; | ||
226 | if (child_index >= children->length) { | ||
227 | child_index = children->length - 1; | ||
228 | } | ||
229 | struct sway_container *child = children->items[child_index]; | ||
230 | return child; | ||
231 | } | ||
232 | |||
233 | // Surfaces | ||
234 | struct sway_node *current = seat_get_active_tiling_child(seat, parent); | ||
235 | return current ? tiling_container_at(current, lx, ly, surface, sx, sy) : NULL; | ||
236 | } | ||
237 | |||
238 | /** | ||
239 | * container_at for a container with layout L_STACKED. | ||
240 | */ | ||
241 | static struct sway_container *container_at_stacked(struct sway_node *parent, | ||
242 | double lx, double ly, | ||
243 | struct wlr_surface **surface, double *sx, double *sy) { | ||
244 | struct wlr_box box; | ||
245 | node_get_box(parent, &box); | ||
246 | if (lx < box.x || lx > box.x + box.width || | ||
247 | ly < box.y || ly > box.y + box.height) { | ||
248 | return NULL; | ||
249 | } | ||
250 | struct sway_seat *seat = input_manager_current_seat(); | ||
251 | list_t *children = node_get_children(parent); | ||
252 | |||
253 | // Title bars | ||
254 | int title_height = container_titlebar_height(); | ||
255 | if (title_height > 0) { | ||
256 | int child_index = (ly - box.y) / title_height; | ||
257 | if (child_index < children->length) { | ||
258 | struct sway_container *child = children->items[child_index]; | ||
259 | return child; | ||
260 | } | 611 | } |
261 | } | 612 | } |
262 | |||
263 | // Surfaces | ||
264 | struct sway_node *current = seat_get_active_tiling_child(seat, parent); | ||
265 | return current ? tiling_container_at(current, lx, ly, surface, sx, sy) : NULL; | ||
266 | } | 613 | } |
267 | 614 | ||
268 | /** | 615 | struct sway_container *container_obstructing_fullscreen_container(struct sway_container *container) |
269 | * container_at for a container with layout L_HORIZ or L_VERT. | 616 | { |
270 | */ | 617 | struct sway_workspace *workspace = container->pending.workspace; |
271 | static struct sway_container *container_at_linear(struct sway_node *parent, | ||
272 | double lx, double ly, | ||
273 | struct wlr_surface **surface, double *sx, double *sy) { | ||
274 | list_t *children = node_get_children(parent); | ||
275 | for (int i = 0; i < children->length; ++i) { | ||
276 | struct sway_container *child = children->items[i]; | ||
277 | struct sway_container *container = | ||
278 | tiling_container_at(&child->node, lx, ly, surface, sx, sy); | ||
279 | if (container) { | ||
280 | return container; | ||
281 | } | ||
282 | } | ||
283 | return NULL; | ||
284 | } | ||
285 | 618 | ||
286 | static struct sway_container *floating_container_at(double lx, double ly, | 619 | if (workspace && workspace->fullscreen && !container_is_fullscreen_or_child(container)) { |
287 | struct wlr_surface **surface, double *sx, double *sy) { | 620 | if (container_is_transient_for(container, workspace->fullscreen)) { |
288 | // For outputs with floating containers that overhang the output bounds, | 621 | return NULL; |
289 | // those at the end of the output list appear on top of floating | ||
290 | // containers from other outputs, so iterate the list in reverse. | ||
291 | for (int i = root->outputs->length - 1; i >= 0; --i) { | ||
292 | struct sway_output *output = root->outputs->items[i]; | ||
293 | for (int j = 0; j < output->workspaces->length; ++j) { | ||
294 | struct sway_workspace *ws = output->workspaces->items[j]; | ||
295 | if (!workspace_is_visible(ws)) { | ||
296 | continue; | ||
297 | } | ||
298 | // Items at the end of the list are on top, so iterate the list in | ||
299 | // reverse. | ||
300 | for (int k = ws->floating->length - 1; k >= 0; --k) { | ||
301 | struct sway_container *floater = ws->floating->items[k]; | ||
302 | struct sway_container *container = | ||
303 | tiling_container_at(&floater->node, lx, ly, surface, sx, sy); | ||
304 | if (container) { | ||
305 | return container; | ||
306 | } | ||
307 | } | ||
308 | } | 622 | } |
309 | } | 623 | return workspace->fullscreen; |
310 | return NULL; | ||
311 | } | ||
312 | |||
313 | struct sway_container *view_container_at(struct sway_node *parent, | ||
314 | double lx, double ly, | ||
315 | struct wlr_surface **surface, double *sx, double *sy) { | ||
316 | if (!sway_assert(node_is_view(parent), "Expected a view")) { | ||
317 | return NULL; | ||
318 | } | 624 | } |
319 | 625 | ||
320 | struct sway_container *container = parent->sway_container; | 626 | struct sway_container *fullscreen_global = root->fullscreen_global; |
321 | struct wlr_box box = { | 627 | if (fullscreen_global && container != fullscreen_global && !container_has_ancestor(container, fullscreen_global)) { |
322 | .x = container->x, | 628 | if (container_is_transient_for(container, fullscreen_global)) { |
323 | .y = container->y, | 629 | return NULL; |
324 | .width = container->width, | ||
325 | .height = container->height, | ||
326 | }; | ||
327 | |||
328 | if (wlr_box_contains_point(&box, lx, ly)) { | ||
329 | surface_at_view(parent->sway_container, lx, ly, surface, sx, sy); | ||
330 | return container; | ||
331 | } | ||
332 | |||
333 | return NULL; | ||
334 | } | ||
335 | |||
336 | struct sway_container *tiling_container_at(struct sway_node *parent, | ||
337 | double lx, double ly, | ||
338 | struct wlr_surface **surface, double *sx, double *sy) { | ||
339 | if (node_is_view(parent)) { | ||
340 | return view_container_at(parent, lx, ly, surface, sx, sy); | ||
341 | } | ||
342 | if (!node_get_children(parent)) { | ||
343 | return NULL; | ||
344 | } | ||
345 | switch (node_get_layout(parent)) { | ||
346 | case L_HORIZ: | ||
347 | case L_VERT: | ||
348 | return container_at_linear(parent, lx, ly, surface, sx, sy); | ||
349 | case L_TABBED: | ||
350 | return container_at_tabbed(parent, lx, ly, surface, sx, sy); | ||
351 | case L_STACKED: | ||
352 | return container_at_stacked(parent, lx, ly, surface, sx, sy); | ||
353 | case L_NONE: | ||
354 | return NULL; | ||
355 | } | ||
356 | return NULL; | ||
357 | } | ||
358 | |||
359 | static bool surface_is_popup(struct wlr_surface *surface) { | ||
360 | if (wlr_surface_is_xdg_surface(surface)) { | ||
361 | struct wlr_xdg_surface *xdg_surface = | ||
362 | wlr_xdg_surface_from_wlr_surface(surface); | ||
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 | } | 630 | } |
369 | return false; | 631 | return fullscreen_global; |
370 | } | 632 | } |
371 | 633 | ||
372 | return false; | ||
373 | } | ||
374 | |||
375 | struct sway_container *container_at(struct sway_workspace *workspace, | ||
376 | double lx, double ly, | ||
377 | struct wlr_surface **surface, double *sx, double *sy) { | ||
378 | struct sway_container *c; | ||
379 | |||
380 | struct sway_seat *seat = input_manager_current_seat(); | ||
381 | struct sway_container *focus = seat_get_focused_container(seat); | ||
382 | bool is_floating = focus && container_is_floating_or_child(focus); | ||
383 | // Focused view's popups | ||
384 | if (focus && focus->view) { | ||
385 | c = surface_at_view(focus, lx, ly, surface, sx, sy); | ||
386 | if (c && surface_is_popup(*surface)) { | ||
387 | return c; | ||
388 | } | ||
389 | *surface = NULL; | ||
390 | } | ||
391 | // Floating | ||
392 | if ((c = floating_container_at(lx, ly, surface ,sx ,sy))) { | ||
393 | return c; | ||
394 | } | ||
395 | // Tiling (focused) | ||
396 | if (focus && focus->view && !is_floating) { | ||
397 | if ((c = surface_at_view(focus, lx, ly, surface, sx, sy))) { | ||
398 | return c; | ||
399 | } | ||
400 | } | ||
401 | // Tiling (non-focused) | ||
402 | if ((c = tiling_container_at(&workspace->node, lx, ly, surface, sx, sy))) { | ||
403 | return c; | ||
404 | } | ||
405 | return NULL; | 634 | return NULL; |
406 | } | 635 | } |
407 | 636 | ||
408 | void container_for_each_child(struct sway_container *container, | ||
409 | void (*f)(struct sway_container *container, void *data), | ||
410 | void *data) { | ||
411 | if (container->children) { | ||
412 | for (int i = 0; i < container->children->length; ++i) { | ||
413 | struct sway_container *child = container->children->items[i]; | ||
414 | f(child, data); | ||
415 | container_for_each_child(child, f, data); | ||
416 | } | ||
417 | } | ||
418 | } | ||
419 | |||
420 | bool container_has_ancestor(struct sway_container *descendant, | 637 | bool container_has_ancestor(struct sway_container *descendant, |
421 | struct sway_container *ancestor) { | 638 | struct sway_container *ancestor) { |
422 | while (descendant) { | 639 | while (descendant) { |
423 | descendant = descendant->parent; | 640 | descendant = descendant->pending.parent; |
424 | if (descendant == ancestor) { | 641 | if (descendant == ancestor) { |
425 | return true; | 642 | return true; |
426 | } | 643 | } |
@@ -428,119 +645,6 @@ bool container_has_ancestor(struct sway_container *descendant, | |||
428 | return false; | 645 | return false; |
429 | } | 646 | } |
430 | 647 | ||
431 | void container_damage_whole(struct sway_container *container) { | ||
432 | for (int i = 0; i < root->outputs->length; ++i) { | ||
433 | struct sway_output *output = root->outputs->items[i]; | ||
434 | output_damage_whole_container(output, container); | ||
435 | } | ||
436 | } | ||
437 | |||
438 | /** | ||
439 | * Return the output which will be used for scale purposes. | ||
440 | * This is the most recently entered output. | ||
441 | */ | ||
442 | struct sway_output *container_get_effective_output(struct sway_container *con) { | ||
443 | if (con->outputs->length == 0) { | ||
444 | return NULL; | ||
445 | } | ||
446 | return con->outputs->items[con->outputs->length - 1]; | ||
447 | } | ||
448 | |||
449 | static void update_title_texture(struct sway_container *con, | ||
450 | struct wlr_texture **texture, struct border_colors *class) { | ||
451 | struct sway_output *output = container_get_effective_output(con); | ||
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; | ||
464 | int width = 0; | ||
465 | int height = con->title_height * scale; | ||
466 | |||
467 | // We must use a non-nil cairo_t for cairo_set_font_options to work. | ||
468 | // Therefore, we cannot use cairo_create(NULL). | ||
469 | cairo_surface_t *dummy_surface = cairo_image_surface_create( | ||
470 | CAIRO_FORMAT_ARGB32, 0, 0); | ||
471 | cairo_t *c = cairo_create(dummy_surface); | ||
472 | cairo_set_antialias(c, CAIRO_ANTIALIAS_BEST); | ||
473 | cairo_font_options_t *fo = cairo_font_options_create(); | ||
474 | cairo_font_options_set_hint_style(fo, CAIRO_HINT_STYLE_FULL); | ||
475 | if (output->wlr_output->subpixel == WL_OUTPUT_SUBPIXEL_NONE) { | ||
476 | cairo_font_options_set_antialias(fo, CAIRO_ANTIALIAS_GRAY); | ||
477 | } else { | ||
478 | cairo_font_options_set_antialias(fo, CAIRO_ANTIALIAS_SUBPIXEL); | ||
479 | cairo_font_options_set_subpixel_order(fo, | ||
480 | to_cairo_subpixel_order(output->wlr_output->subpixel)); | ||
481 | } | ||
482 | cairo_set_font_options(c, fo); | ||
483 | get_text_size(c, config->font, &width, NULL, NULL, scale, | ||
484 | config->pango_markup, "%s", con->formatted_title); | ||
485 | cairo_surface_destroy(dummy_surface); | ||
486 | cairo_destroy(c); | ||
487 | |||
488 | cairo_surface_t *surface = cairo_image_surface_create( | ||
489 | CAIRO_FORMAT_ARGB32, width, height); | ||
490 | cairo_t *cairo = cairo_create(surface); | ||
491 | cairo_set_antialias(cairo, CAIRO_ANTIALIAS_BEST); | ||
492 | cairo_set_font_options(cairo, fo); | ||
493 | cairo_font_options_destroy(fo); | ||
494 | cairo_set_source_rgba(cairo, class->background[0], class->background[1], | ||
495 | class->background[2], class->background[3]); | ||
496 | cairo_paint(cairo); | ||
497 | PangoContext *pango = pango_cairo_create_context(cairo); | ||
498 | cairo_set_source_rgba(cairo, class->text[0], class->text[1], | ||
499 | class->text[2], class->text[3]); | ||
500 | cairo_move_to(cairo, 0, 0); | ||
501 | |||
502 | pango_printf(cairo, config->font, scale, config->pango_markup, | ||
503 | "%s", con->formatted_title); | ||
504 | |||
505 | cairo_surface_flush(surface); | ||
506 | unsigned char *data = cairo_image_surface_get_data(surface); | ||
507 | int stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, width); | ||
508 | struct wlr_renderer *renderer = wlr_backend_get_renderer( | ||
509 | output->wlr_output->backend); | ||
510 | *texture = wlr_texture_from_pixels( | ||
511 | renderer, WL_SHM_FORMAT_ARGB8888, stride, width, height, data); | ||
512 | cairo_surface_destroy(surface); | ||
513 | g_object_unref(pango); | ||
514 | cairo_destroy(cairo); | ||
515 | } | ||
516 | |||
517 | void container_update_title_textures(struct sway_container *container) { | ||
518 | update_title_texture(container, &container->title_focused, | ||
519 | &config->border_colors.focused); | ||
520 | update_title_texture(container, &container->title_focused_inactive, | ||
521 | &config->border_colors.focused_inactive); | ||
522 | update_title_texture(container, &container->title_unfocused, | ||
523 | &config->border_colors.unfocused); | ||
524 | update_title_texture(container, &container->title_urgent, | ||
525 | &config->border_colors.urgent); | ||
526 | container_damage_whole(container); | ||
527 | } | ||
528 | |||
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 | /** | 648 | /** |
545 | * Calculate and return the length of the tree representation. | 649 | * Calculate and return the length of the tree representation. |
546 | * An example tree representation is: V[Terminal, Firefox] | 650 | * An example tree representation is: V[Terminal, Firefox] |
@@ -596,23 +700,28 @@ size_t container_build_representation(enum sway_container_layout layout, | |||
596 | 700 | ||
597 | void container_update_representation(struct sway_container *con) { | 701 | void container_update_representation(struct sway_container *con) { |
598 | if (!con->view) { | 702 | if (!con->view) { |
599 | size_t len = container_build_representation(con->layout, | 703 | size_t len = container_build_representation(con->pending.layout, |
600 | con->children, NULL); | 704 | con->pending.children, NULL); |
601 | free(con->formatted_title); | 705 | free(con->formatted_title); |
602 | con->formatted_title = calloc(len + 1, sizeof(char)); | 706 | con->formatted_title = calloc(len + 1, sizeof(char)); |
603 | if (!sway_assert(con->formatted_title, | 707 | if (!sway_assert(con->formatted_title, |
604 | "Unable to allocate title string")) { | 708 | "Unable to allocate title string")) { |
605 | return; | 709 | return; |
606 | } | 710 | } |
607 | container_build_representation(con->layout, con->children, | 711 | container_build_representation(con->pending.layout, con->pending.children, |
608 | con->formatted_title); | 712 | con->formatted_title); |
609 | container_calculate_title_height(con); | 713 | |
610 | container_update_title_textures(con); | 714 | if (con->title_bar.title_text) { |
715 | sway_text_node_set_text(con->title_bar.title_text, con->formatted_title); | ||
716 | container_arrange_title_bar(con); | ||
717 | } else { | ||
718 | container_update_title_bar(con); | ||
719 | } | ||
611 | } | 720 | } |
612 | if (con->parent) { | 721 | if (con->pending.parent) { |
613 | container_update_representation(con->parent); | 722 | container_update_representation(con->pending.parent); |
614 | } else if (con->workspace) { | 723 | } else if (con->pending.workspace) { |
615 | workspace_update_representation(con->workspace); | 724 | workspace_update_representation(con->pending.workspace); |
616 | } | 725 | } |
617 | } | 726 | } |
618 | 727 | ||
@@ -638,12 +747,13 @@ void floating_calculate_constraints(int *min_width, int *max_width, | |||
638 | *min_height = config->floating_minimum_height; | 747 | *min_height = config->floating_minimum_height; |
639 | } | 748 | } |
640 | 749 | ||
641 | struct wlr_box *box = wlr_output_layout_get_box(root->output_layout, NULL); | 750 | struct wlr_box box; |
751 | wlr_output_layout_get_box(root->output_layout, NULL, &box); | ||
642 | 752 | ||
643 | if (config->floating_maximum_width == -1) { // no maximum | 753 | if (config->floating_maximum_width == -1) { // no maximum |
644 | *max_width = INT_MAX; | 754 | *max_width = INT_MAX; |
645 | } else if (config->floating_maximum_width == 0) { // automatic | 755 | } else if (config->floating_maximum_width == 0) { // automatic |
646 | *max_width = box->width; | 756 | *max_width = box.width; |
647 | } else { | 757 | } else { |
648 | *max_width = config->floating_maximum_width; | 758 | *max_width = config->floating_maximum_width; |
649 | } | 759 | } |
@@ -651,99 +761,112 @@ void floating_calculate_constraints(int *min_width, int *max_width, | |||
651 | if (config->floating_maximum_height == -1) { // no maximum | 761 | if (config->floating_maximum_height == -1) { // no maximum |
652 | *max_height = INT_MAX; | 762 | *max_height = INT_MAX; |
653 | } else if (config->floating_maximum_height == 0) { // automatic | 763 | } else if (config->floating_maximum_height == 0) { // automatic |
654 | *max_height = box->height; | 764 | *max_height = box.height; |
655 | } else { | 765 | } else { |
656 | *max_height = config->floating_maximum_height; | 766 | *max_height = config->floating_maximum_height; |
657 | } | 767 | } |
658 | 768 | ||
659 | } | 769 | } |
660 | 770 | ||
771 | void floating_fix_coordinates(struct sway_container *con, struct wlr_box *old, struct wlr_box *new) { | ||
772 | if (!old->width || !old->height) { | ||
773 | // Fall back to centering on the workspace. | ||
774 | container_floating_move_to_center(con); | ||
775 | } else { | ||
776 | int rel_x = con->pending.x - old->x + (con->pending.width / 2); | ||
777 | int rel_y = con->pending.y - old->y + (con->pending.height / 2); | ||
778 | |||
779 | con->pending.x = new->x + (double)(rel_x * new->width) / old->width - (con->pending.width / 2); | ||
780 | con->pending.y = new->y + (double)(rel_y * new->height) / old->height - (con->pending.height / 2); | ||
781 | |||
782 | sway_log(SWAY_DEBUG, "Transformed container %p to coords (%f, %f)", con, con->pending.x, con->pending.y); | ||
783 | } | ||
784 | } | ||
785 | |||
661 | static void floating_natural_resize(struct sway_container *con) { | 786 | static void floating_natural_resize(struct sway_container *con) { |
662 | int min_width, max_width, min_height, max_height; | 787 | int min_width, max_width, min_height, max_height; |
663 | floating_calculate_constraints(&min_width, &max_width, | 788 | floating_calculate_constraints(&min_width, &max_width, |
664 | &min_height, &max_height); | 789 | &min_height, &max_height); |
665 | if (!con->view) { | 790 | if (!con->view) { |
666 | con->width = fmax(min_width, fmin(con->width, max_width)); | 791 | con->pending.width = fmax(min_width, fmin(con->pending.width, max_width)); |
667 | con->height = fmax(min_height, fmin(con->height, max_height)); | 792 | con->pending.height = fmax(min_height, fmin(con->pending.height, max_height)); |
668 | } else { | 793 | } else { |
669 | struct sway_view *view = con->view; | 794 | struct sway_view *view = con->view; |
670 | con->content_width = | 795 | con->pending.content_width = |
671 | fmax(min_width, fmin(view->natural_width, max_width)); | 796 | fmax(min_width, fmin(view->natural_width, max_width)); |
672 | con->content_height = | 797 | con->pending.content_height = |
673 | fmax(min_height, fmin(view->natural_height, max_height)); | 798 | fmax(min_height, fmin(view->natural_height, max_height)); |
674 | container_set_geometry_from_content(con); | 799 | container_set_geometry_from_content(con); |
675 | } | 800 | } |
676 | } | 801 | } |
677 | 802 | ||
678 | void container_floating_resize_and_center(struct sway_container *con) { | 803 | void container_floating_resize_and_center(struct sway_container *con) { |
679 | struct sway_workspace *ws = con->workspace; | 804 | struct sway_workspace *ws = con->pending.workspace; |
680 | if (!ws) { | 805 | if (!ws) { |
681 | // On scratchpad, just resize | 806 | // On scratchpad, just resize |
682 | floating_natural_resize(con); | 807 | floating_natural_resize(con); |
683 | return; | 808 | return; |
684 | } | 809 | } |
685 | 810 | ||
686 | struct wlr_box *ob = wlr_output_layout_get_box(root->output_layout, | 811 | struct wlr_box ob; |
687 | ws->output->wlr_output); | 812 | wlr_output_layout_get_box(root->output_layout, ws->output->wlr_output, &ob); |
688 | if (!ob) { | 813 | if (wlr_box_empty(&ob)) { |
689 | // On NOOP output. Will be called again when moved to an output | 814 | // On NOOP output. Will be called again when moved to an output |
690 | con->x = 0; | 815 | con->pending.x = 0; |
691 | con->y = 0; | 816 | con->pending.y = 0; |
692 | con->width = 0; | 817 | con->pending.width = 0; |
693 | con->height = 0; | 818 | con->pending.height = 0; |
694 | return; | 819 | return; |
695 | } | 820 | } |
696 | 821 | ||
697 | floating_natural_resize(con); | 822 | floating_natural_resize(con); |
698 | if (!con->view) { | 823 | if (!con->view) { |
699 | if (con->width > ws->width || con->height > ws->height) { | 824 | if (con->pending.width > ws->width || con->pending.height > ws->height) { |
700 | con->x = ob->x + (ob->width - con->width) / 2; | 825 | con->pending.x = ob.x + (ob.width - con->pending.width) / 2; |
701 | con->y = ob->y + (ob->height - con->height) / 2; | 826 | con->pending.y = ob.y + (ob.height - con->pending.height) / 2; |
702 | } else { | 827 | } else { |
703 | con->x = ws->x + (ws->width - con->width) / 2; | 828 | con->pending.x = ws->x + (ws->width - con->pending.width) / 2; |
704 | con->y = ws->y + (ws->height - con->height) / 2; | 829 | con->pending.y = ws->y + (ws->height - con->pending.height) / 2; |
705 | } | 830 | } |
706 | } else { | 831 | } else { |
707 | if (con->content_width > ws->width | 832 | if (con->pending.content_width > ws->width |
708 | || con->content_height > ws->height) { | 833 | || con->pending.content_height > ws->height) { |
709 | con->content_x = ob->x + (ob->width - con->content_width) / 2; | 834 | 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; | 835 | con->pending.content_y = ob.y + (ob.height - con->pending.content_height) / 2; |
711 | } else { | 836 | } else { |
712 | con->content_x = ws->x + (ws->width - con->content_width) / 2; | 837 | 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; | 838 | con->pending.content_y = ws->y + (ws->height - con->pending.content_height) / 2; |
714 | } | 839 | } |
715 | 840 | ||
716 | // If the view's border is B_NONE then these properties are ignored. | 841 | // If the view's border is B_NONE then these properties are ignored. |
717 | con->border_top = con->border_bottom = true; | 842 | con->pending.border_top = con->pending.border_bottom = true; |
718 | con->border_left = con->border_right = true; | 843 | con->pending.border_left = con->pending.border_right = true; |
719 | 844 | ||
720 | container_set_geometry_from_content(con); | 845 | container_set_geometry_from_content(con); |
721 | } | 846 | } |
722 | } | 847 | } |
723 | 848 | ||
724 | void container_floating_set_default_size(struct sway_container *con) { | 849 | void container_floating_set_default_size(struct sway_container *con) { |
725 | if (!sway_assert(con->workspace, "Expected a container on a workspace")) { | 850 | if (!sway_assert(con->pending.workspace, "Expected a container on a workspace")) { |
726 | return; | 851 | return; |
727 | } | 852 | } |
728 | 853 | ||
729 | int min_width, max_width, min_height, max_height; | 854 | int min_width, max_width, min_height, max_height; |
730 | floating_calculate_constraints(&min_width, &max_width, | 855 | floating_calculate_constraints(&min_width, &max_width, |
731 | &min_height, &max_height); | 856 | &min_height, &max_height); |
732 | struct wlr_box *box = calloc(1, sizeof(struct wlr_box)); | 857 | struct wlr_box box; |
733 | workspace_get_box(con->workspace, box); | 858 | workspace_get_box(con->pending.workspace, &box); |
734 | 859 | ||
735 | double width = fmax(min_width, fmin(box->width * 0.5, max_width)); | 860 | 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)); | 861 | double height = fmax(min_height, fmin(box.height * 0.75, max_height)); |
737 | if (!con->view) { | 862 | if (!con->view) { |
738 | con->width = width; | 863 | con->pending.width = width; |
739 | con->height = height; | 864 | con->pending.height = height; |
740 | } else { | 865 | } else { |
741 | con->content_width = width; | 866 | con->pending.content_width = width; |
742 | con->content_height = height; | 867 | con->pending.content_height = height; |
743 | container_set_geometry_from_content(con); | 868 | container_set_geometry_from_content(con); |
744 | } | 869 | } |
745 | |||
746 | free(box); | ||
747 | } | 870 | } |
748 | 871 | ||
749 | 872 | ||
@@ -761,8 +884,8 @@ void container_set_resizing(struct sway_container *con, bool resizing) { | |||
761 | con->view->impl->set_resizing(con->view, resizing); | 884 | con->view->impl->set_resizing(con->view, resizing); |
762 | } | 885 | } |
763 | } else { | 886 | } else { |
764 | for (int i = 0; i < con->children->length; ++i ) { | 887 | for (int i = 0; i < con->pending.children->length; ++i ) { |
765 | struct sway_container *child = con->children->items[i]; | 888 | struct sway_container *child = con->pending.children->items[i]; |
766 | container_set_resizing(child, resizing); | 889 | container_set_resizing(child, resizing); |
767 | } | 890 | } |
768 | } | 891 | } |
@@ -774,21 +897,33 @@ void container_set_floating(struct sway_container *container, bool enable) { | |||
774 | } | 897 | } |
775 | 898 | ||
776 | struct sway_seat *seat = input_manager_current_seat(); | 899 | struct sway_seat *seat = input_manager_current_seat(); |
777 | struct sway_workspace *workspace = container->workspace; | 900 | struct sway_workspace *workspace = container->pending.workspace; |
901 | struct sway_container *focus = seat_get_focused_container(seat); | ||
902 | bool set_focus = focus == container; | ||
778 | 903 | ||
779 | if (enable) { | 904 | if (enable) { |
780 | struct sway_container *old_parent = container->parent; | 905 | struct sway_container *old_parent = container->pending.parent; |
781 | container_detach(container); | 906 | container_detach(container); |
782 | workspace_add_floating(workspace, container); | 907 | workspace_add_floating(workspace, container); |
783 | if (container->view) { | 908 | if (container->view) { |
784 | view_set_tiled(container->view, false); | 909 | view_set_tiled(container->view, false); |
785 | if (container->view->using_csd) { | 910 | if (container->view->using_csd) { |
786 | container->border = B_CSD; | 911 | container->saved_border = container->pending.border; |
912 | container->pending.border = B_CSD; | ||
913 | if (container->view->xdg_decoration) { | ||
914 | struct sway_xdg_decoration *deco = container->view->xdg_decoration; | ||
915 | wlr_xdg_toplevel_decoration_v1_set_mode(deco->wlr_xdg_decoration, | ||
916 | WLR_XDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE); | ||
917 | } | ||
787 | } | 918 | } |
788 | } | 919 | } |
789 | container_floating_set_default_size(container); | 920 | container_floating_set_default_size(container); |
790 | container_floating_resize_and_center(container); | 921 | container_floating_resize_and_center(container); |
791 | if (old_parent) { | 922 | if (old_parent) { |
923 | if (set_focus) { | ||
924 | seat_set_raw_focus(seat, &old_parent->node); | ||
925 | seat_set_raw_focus(seat, &container->node); | ||
926 | } | ||
792 | container_reap_empty(old_parent); | 927 | container_reap_empty(old_parent); |
793 | } | 928 | } |
794 | } else { | 929 | } else { |
@@ -800,19 +935,28 @@ void container_set_floating(struct sway_container *container, bool enable) { | |||
800 | struct sway_container *reference = | 935 | struct sway_container *reference = |
801 | seat_get_focus_inactive_tiling(seat, workspace); | 936 | seat_get_focus_inactive_tiling(seat, workspace); |
802 | if (reference) { | 937 | if (reference) { |
803 | container_add_sibling(reference, container, 1); | 938 | if (reference->view) { |
804 | container->width = reference->width; | 939 | container_add_sibling(reference, container, 1); |
805 | container->height = reference->height; | 940 | } else { |
941 | container_add_child(reference, container); | ||
942 | } | ||
943 | container->pending.width = reference->pending.width; | ||
944 | container->pending.height = reference->pending.height; | ||
806 | } else { | 945 | } else { |
807 | struct sway_container *other = | 946 | struct sway_container *other = |
808 | workspace_add_tiling(workspace, container); | 947 | workspace_add_tiling(workspace, container); |
809 | other->width = workspace->width; | 948 | other->pending.width = workspace->width; |
810 | other->height = workspace->height; | 949 | other->pending.height = workspace->height; |
811 | } | 950 | } |
812 | if (container->view) { | 951 | if (container->view) { |
813 | view_set_tiled(container->view, true); | 952 | view_set_tiled(container->view, true); |
814 | if (container->view->using_csd) { | 953 | if (container->view->using_csd) { |
815 | container->border = container->saved_border; | 954 | container->pending.border = container->saved_border; |
955 | if (container->view->xdg_decoration) { | ||
956 | struct sway_xdg_decoration *deco = container->view->xdg_decoration; | ||
957 | wlr_xdg_toplevel_decoration_v1_set_mode(deco->wlr_xdg_decoration, | ||
958 | WLR_XDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE); | ||
959 | } | ||
816 | } | 960 | } |
817 | } | 961 | } |
818 | container->width_fraction = 0; | 962 | container->width_fraction = 0; |
@@ -834,22 +978,22 @@ void container_set_geometry_from_content(struct sway_container *con) { | |||
834 | size_t border_width = 0; | 978 | size_t border_width = 0; |
835 | size_t top = 0; | 979 | size_t top = 0; |
836 | 980 | ||
837 | if (con->border != B_CSD) { | 981 | if (con->pending.border != B_CSD && !con->pending.fullscreen_mode) { |
838 | border_width = con->border_thickness * (con->border != B_NONE); | 982 | border_width = con->pending.border_thickness * (con->pending.border != B_NONE); |
839 | top = con->border == B_NORMAL ? | 983 | top = con->pending.border == B_NORMAL ? |
840 | container_titlebar_height() : border_width; | 984 | container_titlebar_height() : border_width; |
841 | } | 985 | } |
842 | 986 | ||
843 | con->x = con->content_x - border_width; | 987 | con->pending.x = con->pending.content_x - border_width; |
844 | con->y = con->content_y - top; | 988 | con->pending.y = con->pending.content_y - top; |
845 | con->width = con->content_width + border_width * 2; | 989 | con->pending.width = con->pending.content_width + border_width * 2; |
846 | con->height = top + con->content_height + border_width; | 990 | con->pending.height = top + con->pending.content_height + border_width; |
847 | node_set_dirty(&con->node); | 991 | node_set_dirty(&con->node); |
848 | } | 992 | } |
849 | 993 | ||
850 | bool container_is_floating(struct sway_container *container) { | 994 | bool container_is_floating(struct sway_container *container) { |
851 | if (!container->parent && container->workspace && | 995 | if (!container->pending.parent && container->pending.workspace && |
852 | list_find(container->workspace->floating, container) != -1) { | 996 | list_find(container->pending.workspace->floating, container) != -1) { |
853 | return true; | 997 | return true; |
854 | } | 998 | } |
855 | if (container->scratchpad) { | 999 | if (container->scratchpad) { |
@@ -859,10 +1003,10 @@ bool container_is_floating(struct sway_container *container) { | |||
859 | } | 1003 | } |
860 | 1004 | ||
861 | void container_get_box(struct sway_container *container, struct wlr_box *box) { | 1005 | void container_get_box(struct sway_container *container, struct wlr_box *box) { |
862 | box->x = container->x; | 1006 | box->x = container->pending.x; |
863 | box->y = container->y; | 1007 | box->y = container->pending.y; |
864 | box->width = container->width; | 1008 | box->width = container->pending.width; |
865 | box->height = container->height; | 1009 | box->height = container->pending.height; |
866 | } | 1010 | } |
867 | 1011 | ||
868 | /** | 1012 | /** |
@@ -870,14 +1014,14 @@ void container_get_box(struct sway_container *container, struct wlr_box *box) { | |||
870 | */ | 1014 | */ |
871 | void container_floating_translate(struct sway_container *con, | 1015 | void container_floating_translate(struct sway_container *con, |
872 | double x_amount, double y_amount) { | 1016 | double x_amount, double y_amount) { |
873 | con->x += x_amount; | 1017 | con->pending.x += x_amount; |
874 | con->y += y_amount; | 1018 | con->pending.y += y_amount; |
875 | con->content_x += x_amount; | 1019 | con->pending.content_x += x_amount; |
876 | con->content_y += y_amount; | 1020 | con->pending.content_y += y_amount; |
877 | 1021 | ||
878 | if (con->children) { | 1022 | if (con->pending.children) { |
879 | for (int i = 0; i < con->children->length; ++i) { | 1023 | for (int i = 0; i < con->pending.children->length; ++i) { |
880 | struct sway_container *child = con->children->items[i]; | 1024 | struct sway_container *child = con->pending.children->items[i]; |
881 | container_floating_translate(child, x_amount, y_amount); | 1025 | container_floating_translate(child, x_amount, y_amount); |
882 | } | 1026 | } |
883 | } | 1027 | } |
@@ -893,8 +1037,8 @@ void container_floating_translate(struct sway_container *con, | |||
893 | * center. | 1037 | * center. |
894 | */ | 1038 | */ |
895 | struct sway_output *container_floating_find_output(struct sway_container *con) { | 1039 | struct sway_output *container_floating_find_output(struct sway_container *con) { |
896 | double center_x = con->x + con->width / 2; | 1040 | double center_x = con->pending.x + con->pending.width / 2; |
897 | double center_y = con->y + con->height / 2; | 1041 | double center_y = con->pending.y + con->pending.height / 2; |
898 | struct sway_output *closest_output = NULL; | 1042 | struct sway_output *closest_output = NULL; |
899 | double closest_distance = DBL_MAX; | 1043 | double closest_distance = DBL_MAX; |
900 | for (int i = 0; i < root->outputs->length; ++i) { | 1044 | for (int i = 0; i < root->outputs->length; ++i) { |
@@ -925,11 +1069,11 @@ void container_floating_move_to(struct sway_container *con, | |||
925 | "Expected a floating container")) { | 1069 | "Expected a floating container")) { |
926 | return; | 1070 | return; |
927 | } | 1071 | } |
928 | container_floating_translate(con, lx - con->x, ly - con->y); | 1072 | container_floating_translate(con, lx - con->pending.x, ly - con->pending.y); |
929 | if (container_is_scratchpad_hidden(con)) { | 1073 | if (container_is_scratchpad_hidden(con)) { |
930 | return; | 1074 | return; |
931 | } | 1075 | } |
932 | struct sway_workspace *old_workspace = con->workspace; | 1076 | struct sway_workspace *old_workspace = con->pending.workspace; |
933 | struct sway_output *new_output = container_floating_find_output(con); | 1077 | struct sway_output *new_output = container_floating_find_output(con); |
934 | if (!sway_assert(new_output, "Unable to find any output")) { | 1078 | if (!sway_assert(new_output, "Unable to find any output")) { |
935 | return; | 1079 | return; |
@@ -941,6 +1085,13 @@ void container_floating_move_to(struct sway_container *con, | |||
941 | workspace_add_floating(new_workspace, con); | 1085 | workspace_add_floating(new_workspace, con); |
942 | arrange_workspace(old_workspace); | 1086 | arrange_workspace(old_workspace); |
943 | arrange_workspace(new_workspace); | 1087 | arrange_workspace(new_workspace); |
1088 | // If the moved container was a visible scratchpad container, then | ||
1089 | // update its transform. | ||
1090 | if (con->scratchpad) { | ||
1091 | struct wlr_box output_box; | ||
1092 | output_get_box(new_output, &output_box); | ||
1093 | con->transform = output_box; | ||
1094 | } | ||
944 | workspace_detect_urgent(old_workspace); | 1095 | workspace_detect_urgent(old_workspace); |
945 | workspace_detect_urgent(new_workspace); | 1096 | workspace_detect_urgent(new_workspace); |
946 | } | 1097 | } |
@@ -951,10 +1102,10 @@ void container_floating_move_to_center(struct sway_container *con) { | |||
951 | "Expected a floating container")) { | 1102 | "Expected a floating container")) { |
952 | return; | 1103 | return; |
953 | } | 1104 | } |
954 | struct sway_workspace *ws = con->workspace; | 1105 | struct sway_workspace *ws = con->pending.workspace; |
955 | double new_lx = ws->x + (ws->width - con->width) / 2; | 1106 | double new_lx = ws->x + (ws->width - con->pending.width) / 2; |
956 | double new_ly = ws->y + (ws->height - con->height) / 2; | 1107 | double new_ly = ws->y + (ws->height - con->pending.height) / 2; |
957 | container_floating_translate(con, new_lx - con->x, new_ly - con->y); | 1108 | container_floating_translate(con, new_lx - con->pending.x, new_ly - con->pending.y); |
958 | } | 1109 | } |
959 | 1110 | ||
960 | static bool find_urgent_iterator(struct sway_container *con, void *data) { | 1111 | static bool find_urgent_iterator(struct sway_container *con, void *data) { |
@@ -972,42 +1123,39 @@ void container_end_mouse_operation(struct sway_container *container) { | |||
972 | } | 1123 | } |
973 | } | 1124 | } |
974 | 1125 | ||
975 | static void set_fullscreen_iterator(struct sway_container *con, void *data) { | 1126 | static void set_fullscreen(struct sway_container *con, bool enable) { |
976 | if (!con->view) { | 1127 | if (!con->view) { |
977 | return; | 1128 | return; |
978 | } | 1129 | } |
979 | if (con->view->impl->set_fullscreen) { | 1130 | if (con->view->impl->set_fullscreen) { |
980 | bool *enable = data; | 1131 | con->view->impl->set_fullscreen(con->view, enable); |
981 | con->view->impl->set_fullscreen(con->view, *enable); | ||
982 | if (con->view->foreign_toplevel) { | 1132 | if (con->view->foreign_toplevel) { |
983 | wlr_foreign_toplevel_handle_v1_set_fullscreen( | 1133 | wlr_foreign_toplevel_handle_v1_set_fullscreen( |
984 | con->view->foreign_toplevel, *enable); | 1134 | con->view->foreign_toplevel, enable); |
985 | } | 1135 | } |
986 | } | 1136 | } |
987 | } | 1137 | } |
988 | 1138 | ||
989 | static void container_fullscreen_workspace(struct sway_container *con) { | 1139 | static void container_fullscreen_workspace(struct sway_container *con) { |
990 | if (!sway_assert(con->fullscreen_mode == FULLSCREEN_NONE, | 1140 | if (!sway_assert(con->pending.fullscreen_mode == FULLSCREEN_NONE, |
991 | "Expected a non-fullscreen container")) { | 1141 | "Expected a non-fullscreen container")) { |
992 | return; | 1142 | return; |
993 | } | 1143 | } |
994 | bool enable = true; | 1144 | set_fullscreen(con, true); |
995 | set_fullscreen_iterator(con, &enable); | 1145 | con->pending.fullscreen_mode = FULLSCREEN_WORKSPACE; |
996 | container_for_each_child(con, set_fullscreen_iterator, &enable); | ||
997 | con->fullscreen_mode = FULLSCREEN_WORKSPACE; | ||
998 | 1146 | ||
999 | con->saved_x = con->x; | 1147 | con->saved_x = con->pending.x; |
1000 | con->saved_y = con->y; | 1148 | con->saved_y = con->pending.y; |
1001 | con->saved_width = con->width; | 1149 | con->saved_width = con->pending.width; |
1002 | con->saved_height = con->height; | 1150 | con->saved_height = con->pending.height; |
1003 | 1151 | ||
1004 | if (con->workspace) { | 1152 | if (con->pending.workspace) { |
1005 | con->workspace->fullscreen = con; | 1153 | con->pending.workspace->fullscreen = con; |
1006 | struct sway_seat *seat; | 1154 | struct sway_seat *seat; |
1007 | struct sway_workspace *focus_ws; | 1155 | struct sway_workspace *focus_ws; |
1008 | wl_list_for_each(seat, &server.input->seats, link) { | 1156 | wl_list_for_each(seat, &server.input->seats, link) { |
1009 | focus_ws = seat_get_focused_workspace(seat); | 1157 | focus_ws = seat_get_focused_workspace(seat); |
1010 | if (focus_ws == con->workspace) { | 1158 | if (focus_ws == con->pending.workspace) { |
1011 | seat_set_focus_container(seat, con); | 1159 | seat_set_focus_container(seat, con); |
1012 | } else { | 1160 | } else { |
1013 | struct sway_node *focus = | 1161 | struct sway_node *focus = |
@@ -1023,19 +1171,17 @@ static void container_fullscreen_workspace(struct sway_container *con) { | |||
1023 | } | 1171 | } |
1024 | 1172 | ||
1025 | static void container_fullscreen_global(struct sway_container *con) { | 1173 | static void container_fullscreen_global(struct sway_container *con) { |
1026 | if (!sway_assert(con->fullscreen_mode == FULLSCREEN_NONE, | 1174 | if (!sway_assert(con->pending.fullscreen_mode == FULLSCREEN_NONE, |
1027 | "Expected a non-fullscreen container")) { | 1175 | "Expected a non-fullscreen container")) { |
1028 | return; | 1176 | return; |
1029 | } | 1177 | } |
1030 | bool enable = true; | 1178 | set_fullscreen(con, true); |
1031 | set_fullscreen_iterator(con, &enable); | ||
1032 | container_for_each_child(con, set_fullscreen_iterator, &enable); | ||
1033 | 1179 | ||
1034 | root->fullscreen_global = con; | 1180 | root->fullscreen_global = con; |
1035 | con->saved_x = con->x; | 1181 | con->saved_x = con->pending.x; |
1036 | con->saved_y = con->y; | 1182 | con->saved_y = con->pending.y; |
1037 | con->saved_width = con->width; | 1183 | con->saved_width = con->pending.width; |
1038 | con->saved_height = con->height; | 1184 | con->saved_height = con->pending.height; |
1039 | 1185 | ||
1040 | struct sway_seat *seat; | 1186 | struct sway_seat *seat; |
1041 | wl_list_for_each(seat, &server.input->seats, link) { | 1187 | wl_list_for_each(seat, &server.input->seats, link) { |
@@ -1045,34 +1191,32 @@ static void container_fullscreen_global(struct sway_container *con) { | |||
1045 | } | 1191 | } |
1046 | } | 1192 | } |
1047 | 1193 | ||
1048 | con->fullscreen_mode = FULLSCREEN_GLOBAL; | 1194 | con->pending.fullscreen_mode = FULLSCREEN_GLOBAL; |
1049 | container_end_mouse_operation(con); | 1195 | container_end_mouse_operation(con); |
1050 | ipc_event_window(con, "fullscreen_mode"); | 1196 | ipc_event_window(con, "fullscreen_mode"); |
1051 | } | 1197 | } |
1052 | 1198 | ||
1053 | void container_fullscreen_disable(struct sway_container *con) { | 1199 | void container_fullscreen_disable(struct sway_container *con) { |
1054 | if (!sway_assert(con->fullscreen_mode != FULLSCREEN_NONE, | 1200 | if (!sway_assert(con->pending.fullscreen_mode != FULLSCREEN_NONE, |
1055 | "Expected a fullscreen container")) { | 1201 | "Expected a fullscreen container")) { |
1056 | return; | 1202 | return; |
1057 | } | 1203 | } |
1058 | bool enable = false; | 1204 | set_fullscreen(con, false); |
1059 | set_fullscreen_iterator(con, &enable); | ||
1060 | container_for_each_child(con, set_fullscreen_iterator, &enable); | ||
1061 | 1205 | ||
1062 | if (container_is_floating(con)) { | 1206 | if (container_is_floating(con)) { |
1063 | con->x = con->saved_x; | 1207 | con->pending.x = con->saved_x; |
1064 | con->y = con->saved_y; | 1208 | con->pending.y = con->saved_y; |
1065 | con->width = con->saved_width; | 1209 | con->pending.width = con->saved_width; |
1066 | con->height = con->saved_height; | 1210 | con->pending.height = con->saved_height; |
1067 | } | 1211 | } |
1068 | 1212 | ||
1069 | if (con->fullscreen_mode == FULLSCREEN_WORKSPACE) { | 1213 | if (con->pending.fullscreen_mode == FULLSCREEN_WORKSPACE) { |
1070 | if (con->workspace) { | 1214 | if (con->pending.workspace) { |
1071 | con->workspace->fullscreen = NULL; | 1215 | con->pending.workspace->fullscreen = NULL; |
1072 | if (container_is_floating(con)) { | 1216 | if (container_is_floating(con)) { |
1073 | struct sway_output *output = | 1217 | struct sway_output *output = |
1074 | container_floating_find_output(con); | 1218 | container_floating_find_output(con); |
1075 | if (con->workspace->output != output) { | 1219 | if (con->pending.workspace->output != output) { |
1076 | container_floating_move_to_center(con); | 1220 | container_floating_move_to_center(con); |
1077 | } | 1221 | } |
1078 | } | 1222 | } |
@@ -1084,11 +1228,11 @@ void container_fullscreen_disable(struct sway_container *con) { | |||
1084 | // If the container was mapped as fullscreen and set as floating by | 1228 | // 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 | 1229 | // criteria, it needs to be reinitialized as floating to get the proper |
1086 | // size and location | 1230 | // size and location |
1087 | if (container_is_floating(con) && (con->width == 0 || con->height == 0)) { | 1231 | if (container_is_floating(con) && (con->pending.width == 0 || con->pending.height == 0)) { |
1088 | container_floating_resize_and_center(con); | 1232 | container_floating_resize_and_center(con); |
1089 | } | 1233 | } |
1090 | 1234 | ||
1091 | con->fullscreen_mode = FULLSCREEN_NONE; | 1235 | con->pending.fullscreen_mode = FULLSCREEN_NONE; |
1092 | container_end_mouse_operation(con); | 1236 | container_end_mouse_operation(con); |
1093 | ipc_event_window(con, "fullscreen_mode"); | 1237 | ipc_event_window(con, "fullscreen_mode"); |
1094 | 1238 | ||
@@ -1106,7 +1250,7 @@ void container_fullscreen_disable(struct sway_container *con) { | |||
1106 | 1250 | ||
1107 | void container_set_fullscreen(struct sway_container *con, | 1251 | void container_set_fullscreen(struct sway_container *con, |
1108 | enum sway_fullscreen_mode mode) { | 1252 | enum sway_fullscreen_mode mode) { |
1109 | if (con->fullscreen_mode == mode) { | 1253 | if (con->pending.fullscreen_mode == mode) { |
1110 | return; | 1254 | return; |
1111 | } | 1255 | } |
1112 | 1256 | ||
@@ -1118,8 +1262,8 @@ void container_set_fullscreen(struct sway_container *con, | |||
1118 | if (root->fullscreen_global) { | 1262 | if (root->fullscreen_global) { |
1119 | container_fullscreen_disable(root->fullscreen_global); | 1263 | container_fullscreen_disable(root->fullscreen_global); |
1120 | } | 1264 | } |
1121 | if (con->workspace && con->workspace->fullscreen) { | 1265 | if (con->pending.workspace && con->pending.workspace->fullscreen) { |
1122 | container_fullscreen_disable(con->workspace->fullscreen); | 1266 | container_fullscreen_disable(con->pending.workspace->fullscreen); |
1123 | } | 1267 | } |
1124 | container_fullscreen_workspace(con); | 1268 | container_fullscreen_workspace(con); |
1125 | break; | 1269 | break; |
@@ -1127,7 +1271,7 @@ void container_set_fullscreen(struct sway_container *con, | |||
1127 | if (root->fullscreen_global) { | 1271 | if (root->fullscreen_global) { |
1128 | container_fullscreen_disable(root->fullscreen_global); | 1272 | container_fullscreen_disable(root->fullscreen_global); |
1129 | } | 1273 | } |
1130 | if (con->fullscreen_mode == FULLSCREEN_WORKSPACE) { | 1274 | if (con->pending.fullscreen_mode == FULLSCREEN_WORKSPACE) { |
1131 | container_fullscreen_disable(con); | 1275 | container_fullscreen_disable(con); |
1132 | } | 1276 | } |
1133 | container_fullscreen_global(con); | 1277 | container_fullscreen_global(con); |
@@ -1137,8 +1281,8 @@ void container_set_fullscreen(struct sway_container *con, | |||
1137 | 1281 | ||
1138 | struct sway_container *container_toplevel_ancestor( | 1282 | struct sway_container *container_toplevel_ancestor( |
1139 | struct sway_container *container) { | 1283 | struct sway_container *container) { |
1140 | while (container->parent) { | 1284 | while (container->pending.parent) { |
1141 | container = container->parent; | 1285 | container = container->pending.parent; |
1142 | } | 1286 | } |
1143 | 1287 | ||
1144 | return container; | 1288 | return container; |
@@ -1150,148 +1294,67 @@ bool container_is_floating_or_child(struct sway_container *container) { | |||
1150 | 1294 | ||
1151 | bool container_is_fullscreen_or_child(struct sway_container *container) { | 1295 | bool container_is_fullscreen_or_child(struct sway_container *container) { |
1152 | do { | 1296 | do { |
1153 | if (container->fullscreen_mode) { | 1297 | if (container->pending.fullscreen_mode) { |
1154 | return true; | 1298 | return true; |
1155 | } | 1299 | } |
1156 | container = container->parent; | 1300 | container = container->pending.parent; |
1157 | } while (container); | 1301 | } while (container); |
1158 | 1302 | ||
1159 | return false; | 1303 | return false; |
1160 | } | 1304 | } |
1161 | 1305 | ||
1162 | static void surface_send_enter_iterator(struct wlr_surface *surface, | ||
1163 | int x, int y, void *data) { | ||
1164 | struct wlr_output *wlr_output = data; | ||
1165 | wlr_surface_send_enter(surface, wlr_output); | ||
1166 | } | ||
1167 | |||
1168 | static void surface_send_leave_iterator(struct wlr_surface *surface, | ||
1169 | int x, int y, void *data) { | ||
1170 | struct wlr_output *wlr_output = data; | ||
1171 | wlr_surface_send_leave(surface, wlr_output); | ||
1172 | } | ||
1173 | |||
1174 | void container_discover_outputs(struct sway_container *con) { | ||
1175 | struct wlr_box con_box = { | ||
1176 | .x = con->current.x, | ||
1177 | .y = con->current.y, | ||
1178 | .width = con->current.width, | ||
1179 | .height = con->current.height, | ||
1180 | }; | ||
1181 | struct sway_output *old_output = container_get_effective_output(con); | ||
1182 | |||
1183 | for (int i = 0; i < root->outputs->length; ++i) { | ||
1184 | struct sway_output *output = root->outputs->items[i]; | ||
1185 | struct wlr_box output_box; | ||
1186 | output_get_box(output, &output_box); | ||
1187 | struct wlr_box intersection; | ||
1188 | bool intersects = | ||
1189 | wlr_box_intersection(&intersection, &con_box, &output_box); | ||
1190 | int index = list_find(con->outputs, output); | ||
1191 | |||
1192 | if (intersects && index == -1) { | ||
1193 | // Send enter | ||
1194 | sway_log(SWAY_DEBUG, "Container %p entered output %p", con, output); | ||
1195 | if (con->view) { | ||
1196 | view_for_each_surface(con->view, | ||
1197 | surface_send_enter_iterator, output->wlr_output); | ||
1198 | if (con->view->foreign_toplevel) { | ||
1199 | wlr_foreign_toplevel_handle_v1_output_enter( | ||
1200 | con->view->foreign_toplevel, output->wlr_output); | ||
1201 | } | ||
1202 | } | ||
1203 | list_add(con->outputs, output); | ||
1204 | } else if (!intersects && index != -1) { | ||
1205 | // Send leave | ||
1206 | sway_log(SWAY_DEBUG, "Container %p left output %p", con, output); | ||
1207 | if (con->view) { | ||
1208 | view_for_each_surface(con->view, | ||
1209 | surface_send_leave_iterator, output->wlr_output); | ||
1210 | if (con->view->foreign_toplevel) { | ||
1211 | wlr_foreign_toplevel_handle_v1_output_leave( | ||
1212 | con->view->foreign_toplevel, output->wlr_output); | ||
1213 | } | ||
1214 | } | ||
1215 | list_del(con->outputs, index); | ||
1216 | } | ||
1217 | } | ||
1218 | struct sway_output *new_output = container_get_effective_output(con); | ||
1219 | double old_scale = old_output && old_output->enabled ? | ||
1220 | old_output->wlr_output->scale : -1; | ||
1221 | double new_scale = new_output ? new_output->wlr_output->scale : -1; | ||
1222 | if (old_scale != new_scale) { | ||
1223 | container_update_title_textures(con); | ||
1224 | container_update_marks_textures(con); | ||
1225 | } | ||
1226 | } | ||
1227 | |||
1228 | enum sway_container_layout container_parent_layout(struct sway_container *con) { | 1306 | enum sway_container_layout container_parent_layout(struct sway_container *con) { |
1229 | if (con->parent) { | 1307 | if (con->pending.parent) { |
1230 | return con->parent->layout; | 1308 | return con->pending.parent->pending.layout; |
1231 | } | 1309 | } |
1232 | if (con->workspace) { | 1310 | if (con->pending.workspace) { |
1233 | return con->workspace->layout; | 1311 | return con->pending.workspace->layout; |
1234 | } | 1312 | } |
1235 | return L_NONE; | 1313 | return L_NONE; |
1236 | } | 1314 | } |
1237 | 1315 | ||
1238 | enum sway_container_layout container_current_parent_layout( | ||
1239 | struct sway_container *con) { | ||
1240 | if (con->current.parent) { | ||
1241 | return con->current.parent->current.layout; | ||
1242 | } | ||
1243 | return con->current.workspace->current.layout; | ||
1244 | } | ||
1245 | |||
1246 | list_t *container_get_siblings(struct sway_container *container) { | 1316 | list_t *container_get_siblings(struct sway_container *container) { |
1247 | if (container->parent) { | 1317 | if (container->pending.parent) { |
1248 | return container->parent->children; | 1318 | return container->pending.parent->pending.children; |
1249 | } | 1319 | } |
1250 | if (container_is_scratchpad_hidden(container)) { | 1320 | if (!container->pending.workspace) { |
1251 | return NULL; | 1321 | return NULL; |
1252 | } | 1322 | } |
1253 | if (list_find(container->workspace->tiling, container) != -1) { | 1323 | if (list_find(container->pending.workspace->tiling, container) != -1) { |
1254 | return container->workspace->tiling; | 1324 | return container->pending.workspace->tiling; |
1255 | } | 1325 | } |
1256 | return container->workspace->floating; | 1326 | return container->pending.workspace->floating; |
1257 | } | 1327 | } |
1258 | 1328 | ||
1259 | int container_sibling_index(struct sway_container *child) { | 1329 | int container_sibling_index(struct sway_container *child) { |
1260 | return list_find(container_get_siblings(child), child); | 1330 | return list_find(container_get_siblings(child), child); |
1261 | } | 1331 | } |
1262 | 1332 | ||
1263 | list_t *container_get_current_siblings(struct sway_container *container) { | ||
1264 | if (container->current.parent) { | ||
1265 | return container->current.parent->current.children; | ||
1266 | } | ||
1267 | return container->current.workspace->current.tiling; | ||
1268 | } | ||
1269 | |||
1270 | void container_handle_fullscreen_reparent(struct sway_container *con) { | 1333 | void container_handle_fullscreen_reparent(struct sway_container *con) { |
1271 | if (con->fullscreen_mode != FULLSCREEN_WORKSPACE || !con->workspace || | 1334 | if (con->pending.fullscreen_mode != FULLSCREEN_WORKSPACE || !con->pending.workspace || |
1272 | con->workspace->fullscreen == con) { | 1335 | con->pending.workspace->fullscreen == con) { |
1273 | return; | 1336 | return; |
1274 | } | 1337 | } |
1275 | if (con->workspace->fullscreen) { | 1338 | if (con->pending.workspace->fullscreen) { |
1276 | container_fullscreen_disable(con->workspace->fullscreen); | 1339 | container_fullscreen_disable(con->pending.workspace->fullscreen); |
1277 | } | 1340 | } |
1278 | con->workspace->fullscreen = con; | 1341 | con->pending.workspace->fullscreen = con; |
1279 | 1342 | ||
1280 | arrange_workspace(con->workspace); | 1343 | arrange_workspace(con->pending.workspace); |
1281 | } | 1344 | } |
1282 | 1345 | ||
1283 | static void set_workspace(struct sway_container *container, void *data) { | 1346 | static void set_workspace(struct sway_container *container, void *data) { |
1284 | container->workspace = container->parent->workspace; | 1347 | container->pending.workspace = container->pending.parent->pending.workspace; |
1285 | } | 1348 | } |
1286 | 1349 | ||
1287 | void container_insert_child(struct sway_container *parent, | 1350 | void container_insert_child(struct sway_container *parent, |
1288 | struct sway_container *child, int i) { | 1351 | struct sway_container *child, int i) { |
1289 | if (child->workspace) { | 1352 | if (child->pending.workspace) { |
1290 | container_detach(child); | 1353 | container_detach(child); |
1291 | } | 1354 | } |
1292 | list_insert(parent->children, i, child); | 1355 | list_insert(parent->pending.children, i, child); |
1293 | child->parent = parent; | 1356 | child->pending.parent = parent; |
1294 | child->workspace = parent->workspace; | 1357 | child->pending.workspace = parent->pending.workspace; |
1295 | container_for_each_child(child, set_workspace, NULL); | 1358 | container_for_each_child(child, set_workspace, NULL); |
1296 | container_handle_fullscreen_reparent(child); | 1359 | container_handle_fullscreen_reparent(child); |
1297 | container_update_representation(parent); | 1360 | container_update_representation(parent); |
@@ -1299,14 +1362,14 @@ void container_insert_child(struct sway_container *parent, | |||
1299 | 1362 | ||
1300 | void container_add_sibling(struct sway_container *fixed, | 1363 | void container_add_sibling(struct sway_container *fixed, |
1301 | struct sway_container *active, bool after) { | 1364 | struct sway_container *active, bool after) { |
1302 | if (active->workspace) { | 1365 | if (active->pending.workspace) { |
1303 | container_detach(active); | 1366 | container_detach(active); |
1304 | } | 1367 | } |
1305 | list_t *siblings = container_get_siblings(fixed); | 1368 | list_t *siblings = container_get_siblings(fixed); |
1306 | int index = list_find(siblings, fixed); | 1369 | int index = list_find(siblings, fixed); |
1307 | list_insert(siblings, index + after, active); | 1370 | list_insert(siblings, index + after, active); |
1308 | active->parent = fixed->parent; | 1371 | active->pending.parent = fixed->pending.parent; |
1309 | active->workspace = fixed->workspace; | 1372 | active->pending.workspace = fixed->pending.workspace; |
1310 | container_for_each_child(active, set_workspace, NULL); | 1373 | container_for_each_child(active, set_workspace, NULL); |
1311 | container_handle_fullscreen_reparent(active); | 1374 | container_handle_fullscreen_reparent(active); |
1312 | container_update_representation(active); | 1375 | container_update_representation(active); |
@@ -1314,17 +1377,13 @@ void container_add_sibling(struct sway_container *fixed, | |||
1314 | 1377 | ||
1315 | void container_add_child(struct sway_container *parent, | 1378 | void container_add_child(struct sway_container *parent, |
1316 | struct sway_container *child) { | 1379 | struct sway_container *child) { |
1317 | if (child->workspace) { | 1380 | if (child->pending.workspace) { |
1318 | container_detach(child); | 1381 | container_detach(child); |
1319 | } | 1382 | } |
1320 | list_add(parent->children, child); | 1383 | list_add(parent->pending.children, child); |
1321 | child->parent = parent; | 1384 | child->pending.parent = parent; |
1322 | child->workspace = parent->workspace; | 1385 | child->pending.workspace = parent->pending.workspace; |
1323 | container_for_each_child(child, set_workspace, NULL); | 1386 | 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); | 1387 | container_handle_fullscreen_reparent(child); |
1329 | container_update_representation(parent); | 1388 | container_update_representation(parent); |
1330 | node_set_dirty(&child->node); | 1389 | node_set_dirty(&child->node); |
@@ -1332,15 +1391,15 @@ void container_add_child(struct sway_container *parent, | |||
1332 | } | 1391 | } |
1333 | 1392 | ||
1334 | void container_detach(struct sway_container *child) { | 1393 | void container_detach(struct sway_container *child) { |
1335 | if (child->fullscreen_mode == FULLSCREEN_WORKSPACE) { | 1394 | if (child->pending.fullscreen_mode == FULLSCREEN_WORKSPACE) { |
1336 | child->workspace->fullscreen = NULL; | 1395 | child->pending.workspace->fullscreen = NULL; |
1337 | } | 1396 | } |
1338 | if (child->fullscreen_mode == FULLSCREEN_GLOBAL) { | 1397 | if (child->pending.fullscreen_mode == FULLSCREEN_GLOBAL) { |
1339 | root->fullscreen_global = NULL; | 1398 | root->fullscreen_global = NULL; |
1340 | } | 1399 | } |
1341 | 1400 | ||
1342 | struct sway_container *old_parent = child->parent; | 1401 | struct sway_container *old_parent = child->pending.parent; |
1343 | struct sway_workspace *old_workspace = child->workspace; | 1402 | struct sway_workspace *old_workspace = child->pending.workspace; |
1344 | list_t *siblings = container_get_siblings(child); | 1403 | list_t *siblings = container_get_siblings(child); |
1345 | if (siblings) { | 1404 | if (siblings) { |
1346 | int index = list_find(siblings, child); | 1405 | int index = list_find(siblings, child); |
@@ -1348,8 +1407,8 @@ void container_detach(struct sway_container *child) { | |||
1348 | list_del(siblings, index); | 1407 | list_del(siblings, index); |
1349 | } | 1408 | } |
1350 | } | 1409 | } |
1351 | child->parent = NULL; | 1410 | child->pending.parent = NULL; |
1352 | child->workspace = NULL; | 1411 | child->pending.workspace = NULL; |
1353 | container_for_each_child(child, set_workspace, NULL); | 1412 | container_for_each_child(child, set_workspace, NULL); |
1354 | 1413 | ||
1355 | if (old_parent) { | 1414 | if (old_parent) { |
@@ -1364,18 +1423,18 @@ void container_detach(struct sway_container *child) { | |||
1364 | 1423 | ||
1365 | void container_replace(struct sway_container *container, | 1424 | void container_replace(struct sway_container *container, |
1366 | struct sway_container *replacement) { | 1425 | struct sway_container *replacement) { |
1367 | enum sway_fullscreen_mode fullscreen = container->fullscreen_mode; | 1426 | enum sway_fullscreen_mode fullscreen = container->pending.fullscreen_mode; |
1368 | bool scratchpad = container->scratchpad; | 1427 | bool scratchpad = container->scratchpad; |
1369 | struct sway_workspace *ws = NULL; | 1428 | struct sway_workspace *ws = NULL; |
1370 | if (fullscreen != FULLSCREEN_NONE) { | 1429 | if (fullscreen != FULLSCREEN_NONE) { |
1371 | container_fullscreen_disable(container); | 1430 | container_fullscreen_disable(container); |
1372 | } | 1431 | } |
1373 | if (scratchpad) { | 1432 | if (scratchpad) { |
1374 | ws = container->workspace; | 1433 | ws = container->pending.workspace; |
1375 | root_scratchpad_show(container); | 1434 | root_scratchpad_show(container); |
1376 | root_scratchpad_remove_container(container); | 1435 | root_scratchpad_remove_container(container); |
1377 | } | 1436 | } |
1378 | if (container->parent || container->workspace) { | 1437 | if (container->pending.parent || container->pending.workspace) { |
1379 | float width_fraction = container->width_fraction; | 1438 | float width_fraction = container->width_fraction; |
1380 | float height_fraction = container->height_fraction; | 1439 | float height_fraction = container->height_fraction; |
1381 | container_add_sibling(container, replacement, 1); | 1440 | container_add_sibling(container, replacement, 1); |
@@ -1403,7 +1462,7 @@ struct sway_container *container_split(struct sway_container *child, | |||
1403 | enum sway_container_layout layout) { | 1462 | enum sway_container_layout layout) { |
1404 | // i3 doesn't split singleton H/V containers | 1463 | // i3 doesn't split singleton H/V containers |
1405 | // https://github.com/i3/i3/blob/3cd1c45eba6de073bc4300eebb4e1cc1a0c4479a/src/tree.c#L354 | 1464 | // https://github.com/i3/i3/blob/3cd1c45eba6de073bc4300eebb4e1cc1a0c4479a/src/tree.c#L354 |
1406 | if (child->parent || child->workspace) { | 1465 | if (child->pending.parent || child->pending.workspace) { |
1407 | list_t *siblings = container_get_siblings(child); | 1466 | list_t *siblings = container_get_siblings(child); |
1408 | if (siblings->length == 1) { | 1467 | if (siblings->length == 1) { |
1409 | enum sway_container_layout current = container_parent_layout(child); | 1468 | enum sway_container_layout current = container_parent_layout(child); |
@@ -1411,12 +1470,12 @@ struct sway_container *container_split(struct sway_container *child, | |||
1411 | current = L_NONE; | 1470 | current = L_NONE; |
1412 | } | 1471 | } |
1413 | if (current == L_HORIZ || current == L_VERT) { | 1472 | if (current == L_HORIZ || current == L_VERT) { |
1414 | if (child->parent) { | 1473 | if (child->pending.parent) { |
1415 | child->parent->layout = layout; | 1474 | child->pending.parent->pending.layout = layout; |
1416 | container_update_representation(child->parent); | 1475 | container_update_representation(child->pending.parent); |
1417 | } else { | 1476 | } else { |
1418 | child->workspace->layout = layout; | 1477 | child->pending.workspace->layout = layout; |
1419 | workspace_update_representation(child->workspace); | 1478 | workspace_update_representation(child->pending.workspace); |
1420 | } | 1479 | } |
1421 | return child; | 1480 | return child; |
1422 | } | 1481 | } |
@@ -1429,25 +1488,25 @@ struct sway_container *container_split(struct sway_container *child, | |||
1429 | if (container_is_floating(child) && child->view) { | 1488 | if (container_is_floating(child) && child->view) { |
1430 | view_set_tiled(child->view, true); | 1489 | view_set_tiled(child->view, true); |
1431 | if (child->view->using_csd) { | 1490 | if (child->view->using_csd) { |
1432 | child->border = child->saved_border; | 1491 | child->pending.border = child->saved_border; |
1433 | } | 1492 | } |
1434 | } | 1493 | } |
1435 | 1494 | ||
1436 | struct sway_container *cont = container_create(NULL); | 1495 | struct sway_container *cont = container_create(NULL); |
1437 | cont->width = child->width; | 1496 | cont->pending.width = child->pending.width; |
1438 | cont->height = child->height; | 1497 | cont->pending.height = child->pending.height; |
1439 | cont->width_fraction = child->width_fraction; | 1498 | cont->width_fraction = child->width_fraction; |
1440 | cont->height_fraction = child->height_fraction; | 1499 | cont->height_fraction = child->height_fraction; |
1441 | cont->x = child->x; | 1500 | cont->pending.x = child->pending.x; |
1442 | cont->y = child->y; | 1501 | cont->pending.y = child->pending.y; |
1443 | cont->layout = layout; | 1502 | cont->pending.layout = layout; |
1444 | 1503 | ||
1445 | container_replace(child, cont); | 1504 | container_replace(child, cont); |
1446 | container_add_child(cont, child); | 1505 | container_add_child(cont, child); |
1447 | 1506 | ||
1448 | if (set_focus) { | 1507 | if (set_focus) { |
1449 | seat_set_raw_focus(seat, &cont->node); | 1508 | seat_set_raw_focus(seat, &cont->node); |
1450 | if (cont->fullscreen_mode == FULLSCREEN_GLOBAL) { | 1509 | if (cont->pending.fullscreen_mode == FULLSCREEN_GLOBAL) { |
1451 | seat_set_focus(seat, &child->node); | 1510 | seat_set_focus(seat, &child->node); |
1452 | } else { | 1511 | } else { |
1453 | seat_set_raw_focus(seat, &child->node); | 1512 | seat_set_raw_focus(seat, &child->node); |
@@ -1485,7 +1544,7 @@ bool container_find_and_unmark(char *mark) { | |||
1485 | if (strcmp(con_mark, mark) == 0) { | 1544 | if (strcmp(con_mark, mark) == 0) { |
1486 | free(con_mark); | 1545 | free(con_mark); |
1487 | list_del(con->marks, i); | 1546 | list_del(con->marks, i); |
1488 | container_update_marks_textures(con); | 1547 | container_update_marks(con); |
1489 | ipc_event_window(con, "mark"); | 1548 | ipc_event_window(con, "mark"); |
1490 | return true; | 1549 | return true; |
1491 | } | 1550 | } |
@@ -1516,111 +1575,27 @@ void container_add_mark(struct sway_container *con, char *mark) { | |||
1516 | ipc_event_window(con, "mark"); | 1575 | ipc_event_window(con, "mark"); |
1517 | } | 1576 | } |
1518 | 1577 | ||
1519 | static void update_marks_texture(struct sway_container *con, | ||
1520 | struct wlr_texture **texture, struct border_colors *class) { | ||
1521 | struct sway_output *output = container_get_effective_output(con); | ||
1522 | if (!output) { | ||
1523 | return; | ||
1524 | } | ||
1525 | if (*texture) { | ||
1526 | wlr_texture_destroy(*texture); | ||
1527 | *texture = NULL; | ||
1528 | } | ||
1529 | if (!con->marks->length) { | ||
1530 | return; | ||
1531 | } | ||
1532 | |||
1533 | size_t len = 0; | ||
1534 | for (int i = 0; i < con->marks->length; ++i) { | ||
1535 | char *mark = con->marks->items[i]; | ||
1536 | if (mark[0] != '_') { | ||
1537 | len += strlen(mark) + 2; | ||
1538 | } | ||
1539 | } | ||
1540 | char *buffer = calloc(len + 1, 1); | ||
1541 | char *part = malloc(len + 1); | ||
1542 | |||
1543 | if (!sway_assert(buffer && part, "Unable to allocate memory")) { | ||
1544 | free(buffer); | ||
1545 | return; | ||
1546 | } | ||
1547 | |||
1548 | for (int i = 0; i < con->marks->length; ++i) { | ||
1549 | char *mark = con->marks->items[i]; | ||
1550 | if (mark[0] != '_') { | ||
1551 | sprintf(part, "[%s]", mark); | ||
1552 | strcat(buffer, part); | ||
1553 | } | ||
1554 | } | ||
1555 | free(part); | ||
1556 | |||
1557 | double scale = output->wlr_output->scale; | ||
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 | |||
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); | ||
1591 | } | ||
1592 | |||
1593 | void container_update_marks_textures(struct sway_container *con) { | ||
1594 | if (!config->show_marks) { | ||
1595 | return; | ||
1596 | } | ||
1597 | update_marks_texture(con, &con->marks_focused, | ||
1598 | &config->border_colors.focused); | ||
1599 | update_marks_texture(con, &con->marks_focused_inactive, | ||
1600 | &config->border_colors.focused_inactive); | ||
1601 | update_marks_texture(con, &con->marks_unfocused, | ||
1602 | &config->border_colors.unfocused); | ||
1603 | update_marks_texture(con, &con->marks_urgent, | ||
1604 | &config->border_colors.urgent); | ||
1605 | container_damage_whole(con); | ||
1606 | } | ||
1607 | |||
1608 | void container_raise_floating(struct sway_container *con) { | 1578 | void container_raise_floating(struct sway_container *con) { |
1609 | // Bring container to front by putting it at the end of the floating list. | 1579 | // Bring container to front by putting it at the end of the floating list. |
1610 | struct sway_container *floater = container_toplevel_ancestor(con); | 1580 | struct sway_container *floater = container_toplevel_ancestor(con); |
1611 | if (container_is_floating(floater) && floater->workspace) { | 1581 | if (container_is_floating(floater) && floater->pending.workspace) { |
1612 | list_move_to_end(floater->workspace->floating, floater); | 1582 | // it's okay to just raise the scene directly instead of waiting |
1613 | node_set_dirty(&floater->workspace->node); | 1583 | // for the transaction to go through. We won't be reconfiguring |
1584 | // surfaces | ||
1585 | wlr_scene_node_raise_to_top(&floater->scene_tree->node); | ||
1586 | |||
1587 | list_move_to_end(floater->pending.workspace->floating, floater); | ||
1588 | node_set_dirty(&floater->pending.workspace->node); | ||
1614 | } | 1589 | } |
1615 | } | 1590 | } |
1616 | 1591 | ||
1617 | bool container_is_scratchpad_hidden(struct sway_container *con) { | 1592 | bool container_is_scratchpad_hidden(struct sway_container *con) { |
1618 | return con->scratchpad && !con->workspace; | 1593 | return con->scratchpad && !con->pending.workspace; |
1619 | } | 1594 | } |
1620 | 1595 | ||
1621 | bool container_is_scratchpad_hidden_or_child(struct sway_container *con) { | 1596 | bool container_is_scratchpad_hidden_or_child(struct sway_container *con) { |
1622 | con = container_toplevel_ancestor(con); | 1597 | con = container_toplevel_ancestor(con); |
1623 | return con->scratchpad && !con->workspace; | 1598 | return con->scratchpad && !con->pending.workspace; |
1624 | } | 1599 | } |
1625 | 1600 | ||
1626 | bool container_is_sticky(struct sway_container *con) { | 1601 | bool container_is_sticky(struct sway_container *con) { |
@@ -1648,39 +1623,39 @@ static bool is_parallel(enum sway_container_layout first, | |||
1648 | static bool container_is_squashable(struct sway_container *con, | 1623 | static bool container_is_squashable(struct sway_container *con, |
1649 | struct sway_container *child) { | 1624 | struct sway_container *child) { |
1650 | enum sway_container_layout gp_layout = container_parent_layout(con); | 1625 | enum sway_container_layout gp_layout = container_parent_layout(con); |
1651 | return (con->layout == L_HORIZ || con->layout == L_VERT) && | 1626 | return (con->pending.layout == L_HORIZ || con->pending.layout == L_VERT) && |
1652 | (child->layout == L_HORIZ || child->layout == L_VERT) && | 1627 | (child->pending.layout == L_HORIZ || child->pending.layout == L_VERT) && |
1653 | !is_parallel(con->layout, child->layout) && | 1628 | !is_parallel(con->pending.layout, child->pending.layout) && |
1654 | is_parallel(gp_layout, child->layout); | 1629 | is_parallel(gp_layout, child->pending.layout); |
1655 | } | 1630 | } |
1656 | 1631 | ||
1657 | static void container_squash_children(struct sway_container *con) { | 1632 | static void container_squash_children(struct sway_container *con) { |
1658 | for (int i = 0; i < con->children->length; i++) { | 1633 | for (int i = 0; i < con->pending.children->length; i++) { |
1659 | struct sway_container *child = con->children->items[i]; | 1634 | struct sway_container *child = con->pending.children->items[i]; |
1660 | i += container_squash(child); | 1635 | i += container_squash(child); |
1661 | } | 1636 | } |
1662 | } | 1637 | } |
1663 | 1638 | ||
1664 | int container_squash(struct sway_container *con) { | 1639 | int container_squash(struct sway_container *con) { |
1665 | if (!con->children) { | 1640 | if (!con->pending.children) { |
1666 | return 0; | 1641 | return 0; |
1667 | } | 1642 | } |
1668 | if (con->children->length != 1) { | 1643 | if (con->pending.children->length != 1) { |
1669 | container_squash_children(con); | 1644 | container_squash_children(con); |
1670 | return 0; | 1645 | return 0; |
1671 | } | 1646 | } |
1672 | struct sway_container *child = con->children->items[0]; | 1647 | struct sway_container *child = con->pending.children->items[0]; |
1673 | int idx = container_sibling_index(con); | 1648 | int idx = container_sibling_index(con); |
1674 | int change = 0; | 1649 | int change = 0; |
1675 | if (container_is_squashable(con, child)) { | 1650 | if (container_is_squashable(con, child)) { |
1676 | // con and child are a redundant H/V pair. Destroy them. | 1651 | // con and child are a redundant H/V pair. Destroy them. |
1677 | while (child->children->length) { | 1652 | while (child->pending.children->length) { |
1678 | struct sway_container *current = child->children->items[0]; | 1653 | struct sway_container *current = child->pending.children->items[0]; |
1679 | container_detach(current); | 1654 | container_detach(current); |
1680 | if (con->parent) { | 1655 | if (con->pending.parent) { |
1681 | container_insert_child(con->parent, current, idx); | 1656 | container_insert_child(con->pending.parent, current, idx); |
1682 | } else { | 1657 | } else { |
1683 | workspace_insert_tiling_direct(con->workspace, current, idx); | 1658 | workspace_insert_tiling_direct(con->pending.workspace, current, idx); |
1684 | } | 1659 | } |
1685 | change++; | 1660 | change++; |
1686 | } | 1661 | } |
@@ -1692,3 +1667,177 @@ int container_squash(struct sway_container *con) { | |||
1692 | } | 1667 | } |
1693 | return change; | 1668 | return change; |
1694 | } | 1669 | } |
1670 | |||
1671 | static void swap_places(struct sway_container *con1, | ||
1672 | struct sway_container *con2) { | ||
1673 | struct sway_container *temp = malloc(sizeof(struct sway_container)); | ||
1674 | temp->pending.x = con1->pending.x; | ||
1675 | temp->pending.y = con1->pending.y; | ||
1676 | temp->pending.width = con1->pending.width; | ||
1677 | temp->pending.height = con1->pending.height; | ||
1678 | temp->width_fraction = con1->width_fraction; | ||
1679 | temp->height_fraction = con1->height_fraction; | ||
1680 | temp->pending.parent = con1->pending.parent; | ||
1681 | temp->pending.workspace = con1->pending.workspace; | ||
1682 | bool temp_floating = container_is_floating(con1); | ||
1683 | |||
1684 | con1->pending.x = con2->pending.x; | ||
1685 | con1->pending.y = con2->pending.y; | ||
1686 | con1->pending.width = con2->pending.width; | ||
1687 | con1->pending.height = con2->pending.height; | ||
1688 | con1->width_fraction = con2->width_fraction; | ||
1689 | con1->height_fraction = con2->height_fraction; | ||
1690 | |||
1691 | con2->pending.x = temp->pending.x; | ||
1692 | con2->pending.y = temp->pending.y; | ||
1693 | con2->pending.width = temp->pending.width; | ||
1694 | con2->pending.height = temp->pending.height; | ||
1695 | con2->width_fraction = temp->width_fraction; | ||
1696 | con2->height_fraction = temp->height_fraction; | ||
1697 | |||
1698 | int temp_index = container_sibling_index(con1); | ||
1699 | if (con2->pending.parent) { | ||
1700 | container_insert_child(con2->pending.parent, con1, | ||
1701 | container_sibling_index(con2)); | ||
1702 | } else if (container_is_floating(con2)) { | ||
1703 | workspace_add_floating(con2->pending.workspace, con1); | ||
1704 | } else { | ||
1705 | workspace_insert_tiling(con2->pending.workspace, con1, | ||
1706 | container_sibling_index(con2)); | ||
1707 | } | ||
1708 | if (temp->pending.parent) { | ||
1709 | container_insert_child(temp->pending.parent, con2, temp_index); | ||
1710 | } else if (temp_floating) { | ||
1711 | workspace_add_floating(temp->pending.workspace, con2); | ||
1712 | } else { | ||
1713 | workspace_insert_tiling(temp->pending.workspace, con2, temp_index); | ||
1714 | } | ||
1715 | |||
1716 | free(temp); | ||
1717 | } | ||
1718 | |||
1719 | static void swap_focus(struct sway_container *con1, | ||
1720 | struct sway_container *con2, struct sway_seat *seat, | ||
1721 | struct sway_container *focus) { | ||
1722 | if (focus == con1 || focus == con2) { | ||
1723 | struct sway_workspace *ws1 = con1->pending.workspace; | ||
1724 | struct sway_workspace *ws2 = con2->pending.workspace; | ||
1725 | enum sway_container_layout layout1 = container_parent_layout(con1); | ||
1726 | enum sway_container_layout layout2 = container_parent_layout(con2); | ||
1727 | if (focus == con1 && (layout2 == L_TABBED || layout2 == L_STACKED)) { | ||
1728 | if (workspace_is_visible(ws2)) { | ||
1729 | seat_set_focus(seat, &con2->node); | ||
1730 | } | ||
1731 | seat_set_focus_container(seat, ws1 != ws2 ? con2 : con1); | ||
1732 | } else if (focus == con2 && (layout1 == L_TABBED | ||
1733 | || layout1 == L_STACKED)) { | ||
1734 | if (workspace_is_visible(ws1)) { | ||
1735 | seat_set_focus(seat, &con1->node); | ||
1736 | } | ||
1737 | seat_set_focus_container(seat, ws1 != ws2 ? con1 : con2); | ||
1738 | } else if (ws1 != ws2) { | ||
1739 | seat_set_focus_container(seat, focus == con1 ? con2 : con1); | ||
1740 | } else { | ||
1741 | seat_set_focus_container(seat, focus); | ||
1742 | } | ||
1743 | } else { | ||
1744 | seat_set_focus_container(seat, focus); | ||
1745 | } | ||
1746 | |||
1747 | if (root->fullscreen_global) { | ||
1748 | seat_set_focus(seat, | ||
1749 | seat_get_focus_inactive(seat, &root->fullscreen_global->node)); | ||
1750 | } | ||
1751 | } | ||
1752 | |||
1753 | void container_swap(struct sway_container *con1, struct sway_container *con2) { | ||
1754 | if (!sway_assert(con1 && con2, "Cannot swap with nothing")) { | ||
1755 | return; | ||
1756 | } | ||
1757 | if (!sway_assert(!container_has_ancestor(con1, con2) | ||
1758 | && !container_has_ancestor(con2, con1), | ||
1759 | "Cannot swap ancestor and descendant")) { | ||
1760 | return; | ||
1761 | } | ||
1762 | |||
1763 | sway_log(SWAY_DEBUG, "Swapping containers %zu and %zu", | ||
1764 | con1->node.id, con2->node.id); | ||
1765 | |||
1766 | bool scratch1 = con1->scratchpad; | ||
1767 | bool hidden1 = container_is_scratchpad_hidden(con1); | ||
1768 | bool scratch2 = con2->scratchpad; | ||
1769 | bool hidden2 = container_is_scratchpad_hidden(con2); | ||
1770 | if (scratch1) { | ||
1771 | if (hidden1) { | ||
1772 | root_scratchpad_show(con1); | ||
1773 | } | ||
1774 | root_scratchpad_remove_container(con1); | ||
1775 | } | ||
1776 | if (scratch2) { | ||
1777 | if (hidden2) { | ||
1778 | root_scratchpad_show(con2); | ||
1779 | } | ||
1780 | root_scratchpad_remove_container(con2); | ||
1781 | } | ||
1782 | |||
1783 | enum sway_fullscreen_mode fs1 = con1->pending.fullscreen_mode; | ||
1784 | if (fs1) { | ||
1785 | container_fullscreen_disable(con1); | ||
1786 | } | ||
1787 | enum sway_fullscreen_mode fs2 = con2->pending.fullscreen_mode; | ||
1788 | if (fs2) { | ||
1789 | container_fullscreen_disable(con2); | ||
1790 | } | ||
1791 | |||
1792 | struct sway_seat *seat = input_manager_current_seat(); | ||
1793 | struct sway_container *focus = seat_get_focused_container(seat); | ||
1794 | struct sway_workspace *vis1 = | ||
1795 | output_get_active_workspace(con1->pending.workspace->output); | ||
1796 | struct sway_workspace *vis2 = | ||
1797 | output_get_active_workspace(con2->pending.workspace->output); | ||
1798 | if (!sway_assert(vis1 && vis2, "con1 or con2 are on an output without a" | ||
1799 | "workspace. This should not happen")) { | ||
1800 | return; | ||
1801 | } | ||
1802 | |||
1803 | char *stored_prev_name = NULL; | ||
1804 | if (seat->prev_workspace_name) { | ||
1805 | stored_prev_name = strdup(seat->prev_workspace_name); | ||
1806 | } | ||
1807 | |||
1808 | swap_places(con1, con2); | ||
1809 | |||
1810 | if (!workspace_is_visible(vis1)) { | ||
1811 | seat_set_focus(seat, seat_get_focus_inactive(seat, &vis1->node)); | ||
1812 | } | ||
1813 | if (!workspace_is_visible(vis2)) { | ||
1814 | seat_set_focus(seat, seat_get_focus_inactive(seat, &vis2->node)); | ||
1815 | } | ||
1816 | |||
1817 | swap_focus(con1, con2, seat, focus); | ||
1818 | |||
1819 | if (stored_prev_name) { | ||
1820 | free(seat->prev_workspace_name); | ||
1821 | seat->prev_workspace_name = stored_prev_name; | ||
1822 | } | ||
1823 | |||
1824 | if (scratch1) { | ||
1825 | root_scratchpad_add_container(con2, NULL); | ||
1826 | if (!hidden1) { | ||
1827 | root_scratchpad_show(con2); | ||
1828 | } | ||
1829 | } | ||
1830 | if (scratch2) { | ||
1831 | root_scratchpad_add_container(con1, NULL); | ||
1832 | if (!hidden2) { | ||
1833 | root_scratchpad_show(con1); | ||
1834 | } | ||
1835 | } | ||
1836 | |||
1837 | if (fs1) { | ||
1838 | container_set_fullscreen(con2, fs1); | ||
1839 | } | ||
1840 | if (fs2) { | ||
1841 | container_set_fullscreen(con1, fs2); | ||
1842 | } | ||
1843 | } | ||
diff --git a/sway/tree/node.c b/sway/tree/node.c index ffa7f2cc..7aaf9762 100644 --- a/sway/tree/node.c +++ b/sway/tree/node.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include "sway/output.h" | 1 | #include "sway/output.h" |
3 | #include "sway/server.h" | 2 | #include "sway/server.h" |
4 | #include "sway/tree/container.h" | 3 | #include "sway/tree/container.h" |
@@ -18,13 +17,13 @@ void node_init(struct sway_node *node, enum sway_node_type type, void *thing) { | |||
18 | const char *node_type_to_str(enum sway_node_type type) { | 17 | const char *node_type_to_str(enum sway_node_type type) { |
19 | switch (type) { | 18 | switch (type) { |
20 | case N_ROOT: | 19 | case N_ROOT: |
21 | return "N_ROOT"; | 20 | return "root"; |
22 | case N_OUTPUT: | 21 | case N_OUTPUT: |
23 | return "N_OUTPUT"; | 22 | return "output"; |
24 | case N_WORKSPACE: | 23 | case N_WORKSPACE: |
25 | return "N_WORKSPACE"; | 24 | return "workspace"; |
26 | case N_CONTAINER: | 25 | case N_CONTAINER: |
27 | return "N_CONTAINER"; | 26 | return "container"; |
28 | } | 27 | } |
29 | return ""; | 28 | return ""; |
30 | } | 29 | } |
@@ -75,7 +74,7 @@ void node_get_box(struct sway_node *node, struct wlr_box *box) { | |||
75 | struct sway_output *node_get_output(struct sway_node *node) { | 74 | struct sway_output *node_get_output(struct sway_node *node) { |
76 | switch (node->type) { | 75 | switch (node->type) { |
77 | case N_CONTAINER: { | 76 | case N_CONTAINER: { |
78 | struct sway_workspace *ws = node->sway_container->workspace; | 77 | struct sway_workspace *ws = node->sway_container->pending.workspace; |
79 | return ws ? ws->output : NULL; | 78 | return ws ? ws->output : NULL; |
80 | } | 79 | } |
81 | case N_WORKSPACE: | 80 | case N_WORKSPACE: |
@@ -91,7 +90,7 @@ struct sway_output *node_get_output(struct sway_node *node) { | |||
91 | enum sway_container_layout node_get_layout(struct sway_node *node) { | 90 | enum sway_container_layout node_get_layout(struct sway_node *node) { |
92 | switch (node->type) { | 91 | switch (node->type) { |
93 | case N_CONTAINER: | 92 | case N_CONTAINER: |
94 | return node->sway_container->layout; | 93 | return node->sway_container->pending.layout; |
95 | case N_WORKSPACE: | 94 | case N_WORKSPACE: |
96 | return node->sway_workspace->layout; | 95 | return node->sway_workspace->layout; |
97 | case N_OUTPUT: | 96 | case N_OUTPUT: |
@@ -105,11 +104,11 @@ struct sway_node *node_get_parent(struct sway_node *node) { | |||
105 | switch (node->type) { | 104 | switch (node->type) { |
106 | case N_CONTAINER: { | 105 | case N_CONTAINER: { |
107 | struct sway_container *con = node->sway_container; | 106 | struct sway_container *con = node->sway_container; |
108 | if (con->parent) { | 107 | if (con->pending.parent) { |
109 | return &con->parent->node; | 108 | return &con->pending.parent->node; |
110 | } | 109 | } |
111 | if (con->workspace) { | 110 | if (con->pending.workspace) { |
112 | return &con->workspace->node; | 111 | return &con->pending.workspace->node; |
113 | } | 112 | } |
114 | } | 113 | } |
115 | return NULL; | 114 | return NULL; |
@@ -131,7 +130,7 @@ struct sway_node *node_get_parent(struct sway_node *node) { | |||
131 | list_t *node_get_children(struct sway_node *node) { | 130 | list_t *node_get_children(struct sway_node *node) { |
132 | switch (node->type) { | 131 | switch (node->type) { |
133 | case N_CONTAINER: | 132 | case N_CONTAINER: |
134 | return node->sway_container->children; | 133 | return node->sway_container->pending.children; |
135 | case N_WORKSPACE: | 134 | case N_WORKSPACE: |
136 | return node->sway_workspace->tiling; | 135 | return node->sway_workspace->tiling; |
137 | case N_OUTPUT: | 136 | case N_OUTPUT: |
@@ -143,7 +142,7 @@ list_t *node_get_children(struct sway_node *node) { | |||
143 | 142 | ||
144 | bool node_has_ancestor(struct sway_node *node, struct sway_node *ancestor) { | 143 | bool node_has_ancestor(struct sway_node *node, struct sway_node *ancestor) { |
145 | if (ancestor->type == N_ROOT && node->type == N_CONTAINER && | 144 | if (ancestor->type == N_ROOT && node->type == N_CONTAINER && |
146 | node->sway_container->fullscreen_mode == FULLSCREEN_GLOBAL) { | 145 | node->sway_container->pending.fullscreen_mode == FULLSCREEN_GLOBAL) { |
147 | return true; | 146 | return true; |
148 | } | 147 | } |
149 | struct sway_node *parent = node_get_parent(node); | 148 | struct sway_node *parent = node_get_parent(node); |
@@ -152,10 +151,39 @@ bool node_has_ancestor(struct sway_node *node, struct sway_node *ancestor) { | |||
152 | return true; | 151 | return true; |
153 | } | 152 | } |
154 | if (ancestor->type == N_ROOT && parent->type == N_CONTAINER && | 153 | if (ancestor->type == N_ROOT && parent->type == N_CONTAINER && |
155 | parent->sway_container->fullscreen_mode == FULLSCREEN_GLOBAL) { | 154 | parent->sway_container->pending.fullscreen_mode == FULLSCREEN_GLOBAL) { |
156 | return true; | 155 | return true; |
157 | } | 156 | } |
158 | parent = node_get_parent(parent); | 157 | parent = node_get_parent(parent); |
159 | } | 158 | } |
160 | return false; | 159 | return false; |
161 | } | 160 | } |
161 | |||
162 | void scene_node_disown_children(struct wlr_scene_tree *tree) { | ||
163 | // this function can be called as part of destruction code that will be invoked | ||
164 | // upon an allocation failure. Let's not crash on NULL due to an allocation error. | ||
165 | if (!tree) { | ||
166 | return; | ||
167 | } | ||
168 | |||
169 | struct wlr_scene_node *child, *tmp_child; | ||
170 | wl_list_for_each_safe(child, tmp_child, &tree->children, link) { | ||
171 | wlr_scene_node_reparent(child, root->staging); | ||
172 | } | ||
173 | } | ||
174 | |||
175 | struct wlr_scene_tree *alloc_scene_tree(struct wlr_scene_tree *parent, | ||
176 | bool *failed) { | ||
177 | // fallthrough | ||
178 | if (*failed) { | ||
179 | return NULL; | ||
180 | } | ||
181 | |||
182 | struct wlr_scene_tree *tree = wlr_scene_tree_create(parent); | ||
183 | if (!tree) { | ||
184 | sway_log(SWAY_ERROR, "Failed to allocate a scene node"); | ||
185 | *failed = true; | ||
186 | } | ||
187 | |||
188 | return tree; | ||
189 | } | ||
diff --git a/sway/tree/output.c b/sway/tree/output.c index a8ae30f7..2d11195e 100644 --- a/sway/tree/output.c +++ b/sway/tree/output.c | |||
@@ -1,14 +1,13 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <assert.h> | 1 | #include <assert.h> |
3 | #include <ctype.h> | 2 | #include <ctype.h> |
4 | #include <string.h> | 3 | #include <string.h> |
5 | #include <strings.h> | 4 | #include <strings.h> |
6 | #include <wlr/types/wlr_output_damage.h> | ||
7 | #include "sway/ipc-server.h" | 5 | #include "sway/ipc-server.h" |
8 | #include "sway/layers.h" | 6 | #include "sway/layers.h" |
9 | #include "sway/output.h" | 7 | #include "sway/output.h" |
10 | #include "sway/tree/arrange.h" | 8 | #include "sway/tree/arrange.h" |
11 | #include "sway/tree/workspace.h" | 9 | #include "sway/tree/workspace.h" |
10 | #include "sway/server.h" | ||
12 | #include "log.h" | 11 | #include "log.h" |
13 | #include "util.h" | 12 | #include "util.h" |
14 | 13 | ||
@@ -56,8 +55,8 @@ static void restore_workspaces(struct sway_output *output) { | |||
56 | } | 55 | } |
57 | 56 | ||
58 | // Saved workspaces | 57 | // Saved workspaces |
59 | while (root->noop_output->workspaces->length) { | 58 | while (root->fallback_output->workspaces->length) { |
60 | struct sway_workspace *ws = root->noop_output->workspaces->items[0]; | 59 | struct sway_workspace *ws = root->fallback_output->workspaces->items[0]; |
61 | workspace_detach(ws); | 60 | workspace_detach(ws); |
62 | output_add_workspace(output, ws); | 61 | output_add_workspace(output, ws); |
63 | 62 | ||
@@ -70,13 +69,13 @@ static void restore_workspaces(struct sway_output *output) { | |||
70 | // floater re-centered | 69 | // floater re-centered |
71 | for (int i = 0; i < ws->floating->length; i++) { | 70 | for (int i = 0; i < ws->floating->length; i++) { |
72 | struct sway_container *floater = ws->floating->items[i]; | 71 | struct sway_container *floater = ws->floating->items[i]; |
73 | if (floater->width == 0 || floater->height == 0 || | 72 | if (floater->pending.width == 0 || floater->pending.height == 0 || |
74 | floater->width > output->width || | 73 | floater->pending.width > output->width || |
75 | floater->height > output->height || | 74 | floater->pending.height > output->height || |
76 | floater->x > output->lx + output->width || | 75 | floater->pending.x > output->lx + output->width || |
77 | floater->y > output->ly + output->height || | 76 | floater->pending.y > output->ly + output->height || |
78 | floater->x + floater->width < output->lx || | 77 | floater->pending.x + floater->pending.width < output->lx || |
79 | floater->y + floater->height < output->ly) { | 78 | floater->pending.y + floater->pending.height < output->ly) { |
80 | container_floating_resize_and_center(floater); | 79 | container_floating_resize_and_center(floater); |
81 | } | 80 | } |
82 | } | 81 | } |
@@ -87,26 +86,63 @@ static void restore_workspaces(struct sway_output *output) { | |||
87 | output_sort_workspaces(output); | 86 | output_sort_workspaces(output); |
88 | } | 87 | } |
89 | 88 | ||
89 | static void destroy_scene_layers(struct sway_output *output) { | ||
90 | wlr_scene_node_destroy(&output->fullscreen_background->node); | ||
91 | |||
92 | scene_node_disown_children(output->layers.tiling); | ||
93 | scene_node_disown_children(output->layers.fullscreen); | ||
94 | |||
95 | wlr_scene_node_destroy(&output->layers.shell_background->node); | ||
96 | wlr_scene_node_destroy(&output->layers.shell_bottom->node); | ||
97 | wlr_scene_node_destroy(&output->layers.tiling->node); | ||
98 | wlr_scene_node_destroy(&output->layers.fullscreen->node); | ||
99 | wlr_scene_node_destroy(&output->layers.shell_top->node); | ||
100 | wlr_scene_node_destroy(&output->layers.shell_overlay->node); | ||
101 | wlr_scene_node_destroy(&output->layers.session_lock->node); | ||
102 | } | ||
103 | |||
90 | struct sway_output *output_create(struct wlr_output *wlr_output) { | 104 | struct sway_output *output_create(struct wlr_output *wlr_output) { |
91 | struct sway_output *output = calloc(1, sizeof(struct sway_output)); | 105 | struct sway_output *output = calloc(1, sizeof(struct sway_output)); |
92 | node_init(&output->node, N_OUTPUT, output); | 106 | node_init(&output->node, N_OUTPUT, output); |
107 | |||
108 | bool failed = false; | ||
109 | output->layers.shell_background = alloc_scene_tree(root->staging, &failed); | ||
110 | output->layers.shell_bottom = alloc_scene_tree(root->staging, &failed); | ||
111 | output->layers.tiling = alloc_scene_tree(root->staging, &failed); | ||
112 | output->layers.fullscreen = alloc_scene_tree(root->staging, &failed); | ||
113 | output->layers.shell_top = alloc_scene_tree(root->staging, &failed); | ||
114 | output->layers.shell_overlay = alloc_scene_tree(root->staging, &failed); | ||
115 | output->layers.session_lock = alloc_scene_tree(root->staging, &failed); | ||
116 | |||
117 | if (!failed) { | ||
118 | output->fullscreen_background = wlr_scene_rect_create( | ||
119 | output->layers.fullscreen, 0, 0, (float[4]){0.f, 0.f, 0.f, 1.f}); | ||
120 | |||
121 | if (!output->fullscreen_background) { | ||
122 | sway_log(SWAY_ERROR, "Unable to allocate a background rect"); | ||
123 | failed = true; | ||
124 | } | ||
125 | } | ||
126 | |||
127 | if (failed) { | ||
128 | destroy_scene_layers(output); | ||
129 | wlr_scene_output_destroy(output->scene_output); | ||
130 | free(output); | ||
131 | return NULL; | ||
132 | } | ||
133 | |||
93 | output->wlr_output = wlr_output; | 134 | output->wlr_output = wlr_output; |
94 | wlr_output->data = output; | 135 | wlr_output->data = output; |
95 | output->detected_subpixel = wlr_output->subpixel; | 136 | output->detected_subpixel = wlr_output->subpixel; |
96 | output->scale_filter = SCALE_FILTER_NEAREST; | 137 | output->scale_filter = SCALE_FILTER_NEAREST; |
97 | 138 | ||
98 | wl_signal_init(&output->events.destroy); | 139 | wl_signal_init(&output->events.disable); |
99 | 140 | ||
100 | wl_list_insert(&root->all_outputs, &output->link); | 141 | wl_list_insert(&root->all_outputs, &output->link); |
101 | 142 | ||
102 | output->workspaces = create_list(); | 143 | output->workspaces = create_list(); |
103 | output->current.workspaces = create_list(); | 144 | output->current.workspaces = create_list(); |
104 | 145 | ||
105 | size_t len = sizeof(output->layers) / sizeof(output->layers[0]); | ||
106 | for (size_t i = 0; i < len; ++i) { | ||
107 | wl_list_init(&output->layers[i]); | ||
108 | } | ||
109 | |||
110 | return output; | 146 | return output; |
111 | } | 147 | } |
112 | 148 | ||
@@ -146,7 +182,7 @@ void output_enable(struct sway_output *output) { | |||
146 | 182 | ||
147 | input_manager_configure_xcursor(); | 183 | input_manager_configure_xcursor(); |
148 | 184 | ||
149 | wl_signal_emit(&root->events.new_node, &output->node); | 185 | wl_signal_emit_mutable(&root->events.new_node, &output->node); |
150 | 186 | ||
151 | arrange_layers(output); | 187 | arrange_layers(output); |
152 | arrange_root(); | 188 | arrange_root(); |
@@ -192,7 +228,7 @@ static void output_evacuate(struct sway_output *output) { | |||
192 | new_output = fallback_output; | 228 | new_output = fallback_output; |
193 | } | 229 | } |
194 | if (!new_output) { | 230 | if (!new_output) { |
195 | new_output = root->noop_output; | 231 | new_output = root->fallback_output; |
196 | } | 232 | } |
197 | 233 | ||
198 | struct sway_workspace *new_output_ws = | 234 | struct sway_workspace *new_output_ws = |
@@ -238,20 +274,14 @@ void output_destroy(struct sway_output *output) { | |||
238 | "which is still referenced by transactions")) { | 274 | "which is still referenced by transactions")) { |
239 | return; | 275 | return; |
240 | } | 276 | } |
277 | |||
278 | destroy_scene_layers(output); | ||
241 | list_free(output->workspaces); | 279 | list_free(output->workspaces); |
242 | list_free(output->current.workspaces); | 280 | list_free(output->current.workspaces); |
243 | wl_event_source_remove(output->repaint_timer); | 281 | wl_event_source_remove(output->repaint_timer); |
244 | free(output); | 282 | free(output); |
245 | } | 283 | } |
246 | 284 | ||
247 | static void untrack_output(struct sway_container *con, void *data) { | ||
248 | struct sway_output *output = data; | ||
249 | int index = list_find(con->outputs, output); | ||
250 | if (index != -1) { | ||
251 | list_del(con->outputs, index); | ||
252 | } | ||
253 | } | ||
254 | |||
255 | void output_disable(struct sway_output *output) { | 285 | void output_disable(struct sway_output *output) { |
256 | if (!sway_assert(output->enabled, "Expected an enabled output")) { | 286 | if (!sway_assert(output->enabled, "Expected an enabled output")) { |
257 | return; | 287 | return; |
@@ -262,23 +292,20 @@ void output_disable(struct sway_output *output) { | |||
262 | } | 292 | } |
263 | 293 | ||
264 | sway_log(SWAY_DEBUG, "Disabling output '%s'", output->wlr_output->name); | 294 | sway_log(SWAY_DEBUG, "Disabling output '%s'", output->wlr_output->name); |
265 | wl_signal_emit(&output->events.destroy, output); | 295 | wl_signal_emit_mutable(&output->events.disable, output); |
266 | 296 | ||
267 | output_evacuate(output); | 297 | output_evacuate(output); |
268 | 298 | ||
269 | root_for_each_container(untrack_output, output); | ||
270 | |||
271 | list_del(root->outputs, index); | 299 | list_del(root->outputs, index); |
272 | 300 | ||
273 | output->enabled = false; | 301 | output->enabled = false; |
274 | output->current_mode = NULL; | ||
275 | 302 | ||
276 | arrange_root(); | 303 | arrange_root(); |
277 | 304 | ||
278 | // Reconfigure all devices, since devices with map_to_output directives for | 305 | // Reconfigure all devices, since devices with map_to_output directives for |
279 | // an output that goes offline should stop sending events as long as the | 306 | // an output that goes offline should stop sending events as long as the |
280 | // output remains offline. | 307 | // output remains offline. |
281 | input_manager_configure_all_inputs(); | 308 | input_manager_configure_all_input_mappings(); |
282 | } | 309 | } |
283 | 310 | ||
284 | void output_begin_destroy(struct sway_output *output) { | 311 | void output_begin_destroy(struct sway_output *output) { |
@@ -286,13 +313,10 @@ void output_begin_destroy(struct sway_output *output) { | |||
286 | return; | 313 | return; |
287 | } | 314 | } |
288 | sway_log(SWAY_DEBUG, "Destroying output '%s'", output->wlr_output->name); | 315 | sway_log(SWAY_DEBUG, "Destroying output '%s'", output->wlr_output->name); |
316 | wl_signal_emit_mutable(&output->node.events.destroy, &output->node); | ||
289 | 317 | ||
290 | output->node.destroying = true; | 318 | output->node.destroying = true; |
291 | node_set_dirty(&output->node); | 319 | node_set_dirty(&output->node); |
292 | |||
293 | wl_list_remove(&output->link); | ||
294 | output->wlr_output->data = NULL; | ||
295 | output->wlr_output = NULL; | ||
296 | } | 320 | } |
297 | 321 | ||
298 | struct sway_output *output_from_wlr_output(struct wlr_output *output) { | 322 | struct sway_output *output_from_wlr_output(struct wlr_output *output) { |
@@ -304,10 +328,10 @@ struct sway_output *output_get_in_direction(struct sway_output *reference, | |||
304 | if (!sway_assert(direction, "got invalid direction: %d", direction)) { | 328 | if (!sway_assert(direction, "got invalid direction: %d", direction)) { |
305 | return NULL; | 329 | return NULL; |
306 | } | 330 | } |
307 | struct wlr_box *output_box = | 331 | struct wlr_box output_box; |
308 | wlr_output_layout_get_box(root->output_layout, reference->wlr_output); | 332 | wlr_output_layout_get_box(root->output_layout, reference->wlr_output, &output_box); |
309 | int lx = output_box->x + output_box->width / 2; | 333 | int lx = output_box.x + output_box.width / 2; |
310 | int ly = output_box->y + output_box->height / 2; | 334 | int ly = output_box.y + output_box.height / 2; |
311 | struct wlr_output *wlr_adjacent = wlr_output_layout_adjacent_output( | 335 | struct wlr_output *wlr_adjacent = wlr_output_layout_adjacent_output( |
312 | root->output_layout, direction, reference->wlr_output, lx, ly); | 336 | root->output_layout, direction, reference->wlr_output, lx, ly); |
313 | if (!wlr_adjacent) { | 337 | if (!wlr_adjacent) { |
@@ -393,6 +417,33 @@ void output_get_box(struct sway_output *output, struct wlr_box *box) { | |||
393 | box->height = output->height; | 417 | box->height = output->height; |
394 | } | 418 | } |
395 | 419 | ||
420 | static void handle_destroy_non_desktop(struct wl_listener *listener, void *data) { | ||
421 | struct sway_output_non_desktop *output = | ||
422 | wl_container_of(listener, output, destroy); | ||
423 | |||
424 | sway_log(SWAY_DEBUG, "Destroying non-desktop output '%s'", output->wlr_output->name); | ||
425 | |||
426 | int index = list_find(root->non_desktop_outputs, output); | ||
427 | list_del(root->non_desktop_outputs, index); | ||
428 | |||
429 | wl_list_remove(&output->destroy.link); | ||
430 | |||
431 | free(output); | ||
432 | } | ||
433 | |||
434 | struct sway_output_non_desktop *output_non_desktop_create( | ||
435 | struct wlr_output *wlr_output) { | ||
436 | struct sway_output_non_desktop *output = | ||
437 | calloc(1, sizeof(struct sway_output_non_desktop)); | ||
438 | |||
439 | output->wlr_output = wlr_output; | ||
440 | |||
441 | wl_signal_add(&wlr_output->events.destroy, &output->destroy); | ||
442 | output->destroy.notify = handle_destroy_non_desktop; | ||
443 | |||
444 | return output; | ||
445 | } | ||
446 | |||
396 | enum sway_container_layout output_get_default_layout( | 447 | enum sway_container_layout output_get_default_layout( |
397 | struct sway_output *output) { | 448 | struct sway_output *output) { |
398 | if (config->default_orientation != L_NONE) { | 449 | if (config->default_orientation != L_NONE) { |
diff --git a/sway/tree/root.c b/sway/tree/root.c index ebd185ec..20fcfa59 100644 --- a/sway/tree/root.c +++ b/sway/tree/root.c | |||
@@ -1,12 +1,14 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <stdbool.h> | 1 | #include <stdbool.h> |
3 | #include <stdlib.h> | 2 | #include <stdlib.h> |
4 | #include <string.h> | 3 | #include <string.h> |
5 | #include <wlr/types/wlr_output_layout.h> | 4 | #include <wlr/types/wlr_output_layout.h> |
5 | #include <wlr/types/wlr_scene.h> | ||
6 | #include <wlr/util/transform.h> | ||
6 | #include "sway/desktop/transaction.h" | 7 | #include "sway/desktop/transaction.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" |
9 | #include "sway/output.h" | 10 | #include "sway/output.h" |
11 | #include "sway/scene_descriptor.h" | ||
10 | #include "sway/tree/arrange.h" | 12 | #include "sway/tree/arrange.h" |
11 | #include "sway/tree/container.h" | 13 | #include "sway/tree/container.h" |
12 | #include "sway/tree/root.h" | 14 | #include "sway/tree/root.h" |
@@ -23,21 +25,60 @@ static void output_layout_handle_change(struct wl_listener *listener, | |||
23 | transaction_commit_dirty(); | 25 | transaction_commit_dirty(); |
24 | } | 26 | } |
25 | 27 | ||
26 | struct sway_root *root_create(void) { | 28 | struct sway_root *root_create(struct wl_display *wl_display) { |
27 | struct sway_root *root = calloc(1, sizeof(struct sway_root)); | 29 | struct sway_root *root = calloc(1, sizeof(struct sway_root)); |
28 | if (!root) { | 30 | if (!root) { |
29 | sway_log(SWAY_ERROR, "Unable to allocate sway_root"); | 31 | sway_log(SWAY_ERROR, "Unable to allocate sway_root"); |
30 | return NULL; | 32 | return NULL; |
31 | } | 33 | } |
34 | |||
35 | struct wlr_scene *root_scene = wlr_scene_create(); | ||
36 | if (!root_scene) { | ||
37 | sway_log(SWAY_ERROR, "Unable to allocate root scene node"); | ||
38 | free(root); | ||
39 | return NULL; | ||
40 | } | ||
41 | |||
32 | node_init(&root->node, N_ROOT, root); | 42 | node_init(&root->node, N_ROOT, root); |
33 | root->output_layout = wlr_output_layout_create(); | 43 | root->root_scene = root_scene; |
34 | wl_list_init(&root->all_outputs); | 44 | |
35 | #if HAVE_XWAYLAND | 45 | bool failed = false; |
36 | wl_list_init(&root->xwayland_unmanaged); | 46 | root->staging = alloc_scene_tree(&root_scene->tree, &failed); |
47 | root->layer_tree = alloc_scene_tree(&root_scene->tree, &failed); | ||
48 | |||
49 | root->layers.shell_background = alloc_scene_tree(root->layer_tree, &failed); | ||
50 | root->layers.shell_bottom = alloc_scene_tree(root->layer_tree, &failed); | ||
51 | root->layers.tiling = alloc_scene_tree(root->layer_tree, &failed); | ||
52 | root->layers.floating = alloc_scene_tree(root->layer_tree, &failed); | ||
53 | root->layers.shell_top = alloc_scene_tree(root->layer_tree, &failed); | ||
54 | root->layers.fullscreen = alloc_scene_tree(root->layer_tree, &failed); | ||
55 | root->layers.fullscreen_global = alloc_scene_tree(root->layer_tree, &failed); | ||
56 | #if WLR_HAS_XWAYLAND | ||
57 | root->layers.unmanaged = alloc_scene_tree(root->layer_tree, &failed); | ||
37 | #endif | 58 | #endif |
38 | wl_list_init(&root->drag_icons); | 59 | root->layers.shell_overlay = alloc_scene_tree(root->layer_tree, &failed); |
60 | root->layers.popup = alloc_scene_tree(root->layer_tree, &failed); | ||
61 | root->layers.seat = alloc_scene_tree(root->layer_tree, &failed); | ||
62 | root->layers.session_lock = alloc_scene_tree(root->layer_tree, &failed); | ||
63 | |||
64 | if (!failed && !scene_descriptor_assign(&root->layers.seat->node, | ||
65 | SWAY_SCENE_DESC_NON_INTERACTIVE, (void *)1)) { | ||
66 | failed = true; | ||
67 | } | ||
68 | |||
69 | if (failed) { | ||
70 | wlr_scene_node_destroy(&root_scene->tree.node); | ||
71 | free(root); | ||
72 | return NULL; | ||
73 | } | ||
74 | |||
75 | wlr_scene_node_set_enabled(&root->staging->node, false); | ||
76 | |||
77 | root->output_layout = wlr_output_layout_create(wl_display); | ||
78 | wl_list_init(&root->all_outputs); | ||
39 | wl_signal_init(&root->events.new_node); | 79 | wl_signal_init(&root->events.new_node); |
40 | root->outputs = create_list(); | 80 | root->outputs = create_list(); |
81 | root->non_desktop_outputs = create_list(); | ||
41 | root->scratchpad = create_list(); | 82 | root->scratchpad = create_list(); |
42 | 83 | ||
43 | root->output_layout_change.notify = output_layout_handle_change; | 84 | root->output_layout_change.notify = output_layout_handle_change; |
@@ -49,21 +90,34 @@ struct sway_root *root_create(void) { | |||
49 | void root_destroy(struct sway_root *root) { | 90 | void root_destroy(struct sway_root *root) { |
50 | wl_list_remove(&root->output_layout_change.link); | 91 | wl_list_remove(&root->output_layout_change.link); |
51 | list_free(root->scratchpad); | 92 | list_free(root->scratchpad); |
93 | list_free(root->non_desktop_outputs); | ||
52 | list_free(root->outputs); | 94 | list_free(root->outputs); |
53 | wlr_output_layout_destroy(root->output_layout); | 95 | wlr_scene_node_destroy(&root->root_scene->tree.node); |
54 | free(root); | 96 | free(root); |
55 | } | 97 | } |
56 | 98 | ||
99 | static void set_container_transform(struct sway_workspace *ws, | ||
100 | struct sway_container *con) { | ||
101 | struct sway_output *output = ws->output; | ||
102 | struct wlr_box box = {0}; | ||
103 | if (output) { | ||
104 | output_get_box(output, &box); | ||
105 | } | ||
106 | con->transform = box; | ||
107 | } | ||
108 | |||
57 | void root_scratchpad_add_container(struct sway_container *con, struct sway_workspace *ws) { | 109 | void root_scratchpad_add_container(struct sway_container *con, struct sway_workspace *ws) { |
58 | if (!sway_assert(!con->scratchpad, "Container is already in scratchpad")) { | 110 | if (!sway_assert(!con->scratchpad, "Container is already in scratchpad")) { |
59 | return; | 111 | return; |
60 | } | 112 | } |
61 | 113 | ||
62 | struct sway_container *parent = con->parent; | 114 | struct sway_container *parent = con->pending.parent; |
63 | struct sway_workspace *workspace = con->workspace; | 115 | struct sway_workspace *workspace = con->pending.workspace; |
116 | |||
117 | set_container_transform(workspace, con); | ||
64 | 118 | ||
65 | // Clear the fullscreen mode when sending to the scratchpad | 119 | // Clear the fullscreen mode when sending to the scratchpad |
66 | if (con->fullscreen_mode != FULLSCREEN_NONE) { | 120 | if (con->pending.fullscreen_mode != FULLSCREEN_NONE) { |
67 | container_fullscreen_disable(con); | 121 | container_fullscreen_disable(con); |
68 | } | 122 | } |
69 | 123 | ||
@@ -117,7 +171,7 @@ void root_scratchpad_show(struct sway_container *con) { | |||
117 | sway_log(SWAY_DEBUG, "No focused workspace to show scratchpad on"); | 171 | sway_log(SWAY_DEBUG, "No focused workspace to show scratchpad on"); |
118 | return; | 172 | return; |
119 | } | 173 | } |
120 | struct sway_workspace *old_ws = con->workspace; | 174 | struct sway_workspace *old_ws = con->pending.workspace; |
121 | 175 | ||
122 | // If the current con or any of its parents are in fullscreen mode, we | 176 | // 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. | 177 | // first need to disable it before showing the scratchpad con. |
@@ -131,31 +185,34 @@ void root_scratchpad_show(struct sway_container *con) { | |||
131 | // Show the container | 185 | // Show the container |
132 | if (old_ws) { | 186 | if (old_ws) { |
133 | container_detach(con); | 187 | container_detach(con); |
134 | workspace_consider_destroy(old_ws); | 188 | // Make sure the last inactive container on the old workspace is above |
189 | // the workspace itself in the focus stack. | ||
190 | struct sway_node *node = seat_get_focus_inactive(seat, &old_ws->node); | ||
191 | seat_set_raw_focus(seat, node); | ||
135 | } else { | 192 | } else { |
136 | // Act on the ancestor of scratchpad hidden split containers | 193 | // Act on the ancestor of scratchpad hidden split containers |
137 | while (con->parent) { | 194 | while (con->pending.parent) { |
138 | con = con->parent; | 195 | con = con->pending.parent; |
139 | } | 196 | } |
140 | } | 197 | } |
141 | workspace_add_floating(new_ws, con); | 198 | workspace_add_floating(new_ws, con); |
142 | 199 | ||
143 | // Make sure the container's center point overlaps this workspace | 200 | if (new_ws->output) { |
144 | double center_lx = con->x + con->width / 2; | 201 | struct wlr_box output_box; |
145 | double center_ly = con->y + con->height / 2; | 202 | output_get_box(new_ws->output, &output_box); |
146 | 203 | floating_fix_coordinates(con, &con->transform, &output_box); | |
147 | struct wlr_box workspace_box; | ||
148 | workspace_get_box(new_ws, &workspace_box); | ||
149 | if (!wlr_box_contains_point(&workspace_box, center_lx, center_ly)) { | ||
150 | container_floating_resize_and_center(con); | ||
151 | } | 204 | } |
205 | set_container_transform(new_ws, con); | ||
152 | 206 | ||
153 | arrange_workspace(new_ws); | 207 | arrange_workspace(new_ws); |
154 | seat_set_focus(seat, seat_get_focus_inactive(seat, &con->node)); | 208 | seat_set_focus(seat, seat_get_focus_inactive(seat, &con->node)); |
209 | if (old_ws) { | ||
210 | workspace_consider_destroy(old_ws); | ||
211 | } | ||
155 | } | 212 | } |
156 | 213 | ||
157 | static void disable_fullscreen(struct sway_container *con, void *data) { | 214 | static void disable_fullscreen(struct sway_container *con, void *data) { |
158 | if (con->fullscreen_mode != FULLSCREEN_NONE) { | 215 | if (con->pending.fullscreen_mode != FULLSCREEN_NONE) { |
159 | container_fullscreen_disable(con); | 216 | container_fullscreen_disable(con); |
160 | } | 217 | } |
161 | } | 218 | } |
@@ -163,14 +220,16 @@ static void disable_fullscreen(struct sway_container *con, void *data) { | |||
163 | void root_scratchpad_hide(struct sway_container *con) { | 220 | void root_scratchpad_hide(struct sway_container *con) { |
164 | struct sway_seat *seat = input_manager_current_seat(); | 221 | struct sway_seat *seat = input_manager_current_seat(); |
165 | struct sway_node *focus = seat_get_focus_inactive(seat, &root->node); | 222 | struct sway_node *focus = seat_get_focus_inactive(seat, &root->node); |
166 | struct sway_workspace *ws = con->workspace; | 223 | struct sway_workspace *ws = con->pending.workspace; |
167 | 224 | ||
168 | if (con->fullscreen_mode == FULLSCREEN_GLOBAL && !con->workspace) { | 225 | if (con->pending.fullscreen_mode == FULLSCREEN_GLOBAL && !con->pending.workspace) { |
169 | // If the container was made fullscreen global while in the scratchpad, | 226 | // If the container was made fullscreen global while in the scratchpad, |
170 | // it should be shown until fullscreen has been disabled | 227 | // it should be shown until fullscreen has been disabled |
171 | return; | 228 | return; |
172 | } | 229 | } |
173 | 230 | ||
231 | set_container_transform(con->pending.workspace, con); | ||
232 | |||
174 | disable_fullscreen(con, NULL); | 233 | disable_fullscreen(con, NULL); |
175 | container_for_each_child(con, disable_fullscreen, NULL); | 234 | container_for_each_child(con, disable_fullscreen, NULL); |
176 | container_detach(con); | 235 | container_detach(con); |
@@ -183,163 +242,6 @@ void root_scratchpad_hide(struct sway_container *con) { | |||
183 | ipc_event_window(con, "move"); | 242 | ipc_event_window(con, "move"); |
184 | } | 243 | } |
185 | 244 | ||
186 | struct pid_workspace { | ||
187 | pid_t pid; | ||
188 | char *workspace; | ||
189 | struct timespec time_added; | ||
190 | |||
191 | struct sway_output *output; | ||
192 | struct wl_listener output_destroy; | ||
193 | |||
194 | struct wl_list link; | ||
195 | }; | ||
196 | |||
197 | static struct wl_list pid_workspaces; | ||
198 | |||
199 | /** | ||
200 | * Get the pid of a parent process given the pid of a child process. | ||
201 | * | ||
202 | * Returns the parent pid or NULL if the parent pid cannot be determined. | ||
203 | */ | ||
204 | static pid_t get_parent_pid(pid_t child) { | ||
205 | pid_t parent = -1; | ||
206 | char file_name[100]; | ||
207 | char *buffer = NULL; | ||
208 | const char *sep = " "; | ||
209 | FILE *stat = NULL; | ||
210 | size_t buf_size = 0; | ||
211 | |||
212 | sprintf(file_name, "/proc/%d/stat", child); | ||
213 | |||
214 | if ((stat = fopen(file_name, "r"))) { | ||
215 | if (getline(&buffer, &buf_size, stat) != -1) { | ||
216 | strtok(buffer, sep); // pid | ||
217 | strtok(NULL, sep); // executable name | ||
218 | strtok(NULL, sep); // state | ||
219 | char *token = strtok(NULL, sep); // parent pid | ||
220 | parent = strtol(token, NULL, 10); | ||
221 | } | ||
222 | free(buffer); | ||
223 | fclose(stat); | ||
224 | } | ||
225 | |||
226 | if (parent) { | ||
227 | return (parent == child) ? -1 : parent; | ||
228 | } | ||
229 | |||
230 | return -1; | ||
231 | } | ||
232 | |||
233 | static void pid_workspace_destroy(struct pid_workspace *pw) { | ||
234 | wl_list_remove(&pw->output_destroy.link); | ||
235 | wl_list_remove(&pw->link); | ||
236 | free(pw->workspace); | ||
237 | free(pw); | ||
238 | } | ||
239 | |||
240 | struct sway_workspace *root_workspace_for_pid(pid_t pid) { | ||
241 | if (!pid_workspaces.prev && !pid_workspaces.next) { | ||
242 | wl_list_init(&pid_workspaces); | ||
243 | return NULL; | ||
244 | } | ||
245 | |||
246 | struct sway_workspace *ws = NULL; | ||
247 | struct pid_workspace *pw = NULL; | ||
248 | |||
249 | sway_log(SWAY_DEBUG, "Looking up workspace for pid %d", pid); | ||
250 | |||
251 | do { | ||
252 | struct pid_workspace *_pw = NULL; | ||
253 | wl_list_for_each(_pw, &pid_workspaces, link) { | ||
254 | if (pid == _pw->pid) { | ||
255 | pw = _pw; | ||
256 | sway_log(SWAY_DEBUG, | ||
257 | "found pid_workspace for pid %d, workspace %s", | ||
258 | pid, pw->workspace); | ||
259 | goto found; | ||
260 | } | ||
261 | } | ||
262 | pid = get_parent_pid(pid); | ||
263 | } while (pid > 1); | ||
264 | found: | ||
265 | |||
266 | if (pw && pw->workspace) { | ||
267 | ws = workspace_by_name(pw->workspace); | ||
268 | |||
269 | if (!ws) { | ||
270 | sway_log(SWAY_DEBUG, | ||
271 | "Creating workspace %s for pid %d because it disappeared", | ||
272 | pw->workspace, pid); | ||
273 | ws = workspace_create(pw->output, pw->workspace); | ||
274 | } | ||
275 | |||
276 | pid_workspace_destroy(pw); | ||
277 | } | ||
278 | |||
279 | return ws; | ||
280 | } | ||
281 | |||
282 | static void pw_handle_output_destroy(struct wl_listener *listener, void *data) { | ||
283 | struct pid_workspace *pw = wl_container_of(listener, pw, output_destroy); | ||
284 | pw->output = NULL; | ||
285 | wl_list_remove(&pw->output_destroy.link); | ||
286 | wl_list_init(&pw->output_destroy.link); | ||
287 | } | ||
288 | |||
289 | void root_record_workspace_pid(pid_t pid) { | ||
290 | sway_log(SWAY_DEBUG, "Recording workspace for process %d", pid); | ||
291 | if (!pid_workspaces.prev && !pid_workspaces.next) { | ||
292 | wl_list_init(&pid_workspaces); | ||
293 | } | ||
294 | |||
295 | struct sway_seat *seat = input_manager_current_seat(); | ||
296 | struct sway_workspace *ws = seat_get_focused_workspace(seat); | ||
297 | if (!ws) { | ||
298 | sway_log(SWAY_DEBUG, "Bailing out, no workspace"); | ||
299 | return; | ||
300 | } | ||
301 | struct sway_output *output = ws->output; | ||
302 | if (!output) { | ||
303 | sway_log(SWAY_DEBUG, "Bailing out, no output"); | ||
304 | return; | ||
305 | } | ||
306 | |||
307 | struct timespec now; | ||
308 | clock_gettime(CLOCK_MONOTONIC, &now); | ||
309 | |||
310 | // Remove expired entries | ||
311 | static const int timeout = 60; | ||
312 | struct pid_workspace *old, *_old; | ||
313 | wl_list_for_each_safe(old, _old, &pid_workspaces, link) { | ||
314 | if (now.tv_sec - old->time_added.tv_sec >= timeout) { | ||
315 | pid_workspace_destroy(old); | ||
316 | } | ||
317 | } | ||
318 | |||
319 | struct pid_workspace *pw = calloc(1, sizeof(struct pid_workspace)); | ||
320 | pw->workspace = strdup(ws->name); | ||
321 | pw->output = output; | ||
322 | pw->pid = pid; | ||
323 | memcpy(&pw->time_added, &now, sizeof(struct timespec)); | ||
324 | pw->output_destroy.notify = pw_handle_output_destroy; | ||
325 | wl_signal_add(&output->wlr_output->events.destroy, &pw->output_destroy); | ||
326 | wl_list_insert(&pid_workspaces, &pw->link); | ||
327 | } | ||
328 | |||
329 | void root_remove_workspace_pid(pid_t pid) { | ||
330 | if (!pid_workspaces.prev || !pid_workspaces.next) { | ||
331 | return; | ||
332 | } | ||
333 | |||
334 | struct pid_workspace *pw, *tmp; | ||
335 | wl_list_for_each_safe(pw, tmp, &pid_workspaces, link) { | ||
336 | if (pid == pw->pid) { | ||
337 | pid_workspace_destroy(pw); | ||
338 | return; | ||
339 | } | ||
340 | } | ||
341 | } | ||
342 | |||
343 | void root_for_each_workspace(void (*f)(struct sway_workspace *ws, void *data), | 245 | void root_for_each_workspace(void (*f)(struct sway_workspace *ws, void *data), |
344 | void *data) { | 246 | void *data) { |
345 | for (int i = 0; i < root->outputs->length; ++i) { | 247 | for (int i = 0; i < root->outputs->length; ++i) { |
@@ -365,8 +267,8 @@ void root_for_each_container(void (*f)(struct sway_container *con, void *data), | |||
365 | } | 267 | } |
366 | 268 | ||
367 | // Saved workspaces | 269 | // Saved workspaces |
368 | for (int i = 0; i < root->noop_output->workspaces->length; ++i) { | 270 | for (int i = 0; i < root->fallback_output->workspaces->length; ++i) { |
369 | struct sway_workspace *ws = root->noop_output->workspaces->items[i]; | 271 | struct sway_workspace *ws = root->fallback_output->workspaces->items[i]; |
370 | workspace_for_each_container(ws, f, data); | 272 | workspace_for_each_container(ws, f, data); |
371 | } | 273 | } |
372 | } | 274 | } |
@@ -418,8 +320,8 @@ struct sway_container *root_find_container( | |||
418 | } | 320 | } |
419 | 321 | ||
420 | // Saved workspaces | 322 | // Saved workspaces |
421 | for (int i = 0; i < root->noop_output->workspaces->length; ++i) { | 323 | for (int i = 0; i < root->fallback_output->workspaces->length; ++i) { |
422 | struct sway_workspace *ws = root->noop_output->workspaces->items[i]; | 324 | struct sway_workspace *ws = root->fallback_output->workspaces->items[i]; |
423 | if ((result = workspace_find_container(ws, test, data))) { | 325 | if ((result = workspace_find_container(ws, test, data))) { |
424 | return result; | 326 | return result; |
425 | } | 327 | } |
@@ -434,17 +336,3 @@ void root_get_box(struct sway_root *root, struct wlr_box *box) { | |||
434 | box->width = root->width; | 336 | box->width = root->width; |
435 | box->height = root->height; | 337 | box->height = root->height; |
436 | } | 338 | } |
437 | |||
438 | void root_rename_pid_workspaces(const char *old_name, const char *new_name) { | ||
439 | if (!pid_workspaces.prev && !pid_workspaces.next) { | ||
440 | wl_list_init(&pid_workspaces); | ||
441 | } | ||
442 | |||
443 | struct pid_workspace *pw = NULL; | ||
444 | wl_list_for_each(pw, &pid_workspaces, link) { | ||
445 | if (strcmp(pw->workspace, old_name) == 0) { | ||
446 | free(pw->workspace); | ||
447 | pw->workspace = strdup(new_name); | ||
448 | } | ||
449 | } | ||
450 | } | ||
diff --git a/sway/tree/view.c b/sway/tree/view.c index 7afcdf31..1c1c8ee8 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c | |||
@@ -1,28 +1,32 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <stdlib.h> | 1 | #include <stdlib.h> |
3 | #include <strings.h> | 2 | #include <strings.h> |
4 | #include <wayland-server-core.h> | 3 | #include <wayland-server-core.h> |
4 | #include <wlr/config.h> | ||
5 | #include <wlr/render/wlr_renderer.h> | 5 | #include <wlr/render/wlr_renderer.h> |
6 | #include <wlr/types/wlr_buffer.h> | 6 | #include <wlr/types/wlr_buffer.h> |
7 | #include <wlr/types/wlr_ext_foreign_toplevel_list_v1.h> | ||
8 | #include <wlr/types/wlr_foreign_toplevel_management_v1.h> | ||
7 | #include <wlr/types/wlr_output_layout.h> | 9 | #include <wlr/types/wlr_output_layout.h> |
8 | #include <wlr/types/wlr_server_decoration.h> | 10 | #include <wlr/types/wlr_server_decoration.h> |
11 | #include <wlr/types/wlr_subcompositor.h> | ||
9 | #include <wlr/types/wlr_xdg_decoration_v1.h> | 12 | #include <wlr/types/wlr_xdg_decoration_v1.h> |
10 | #include "config.h" | 13 | #if WLR_HAS_XWAYLAND |
11 | #if HAVE_XWAYLAND | ||
12 | #include <wlr/xwayland.h> | 14 | #include <wlr/xwayland.h> |
13 | #endif | 15 | #endif |
14 | #include "list.h" | 16 | #include "list.h" |
15 | #include "log.h" | 17 | #include "log.h" |
16 | #include "sway/criteria.h" | 18 | #include "sway/criteria.h" |
17 | #include "sway/commands.h" | 19 | #include "sway/commands.h" |
18 | #include "sway/desktop.h" | ||
19 | #include "sway/desktop/transaction.h" | 20 | #include "sway/desktop/transaction.h" |
20 | #include "sway/desktop/idle_inhibit_v1.h" | 21 | #include "sway/desktop/idle_inhibit_v1.h" |
22 | #include "sway/desktop/launcher.h" | ||
21 | #include "sway/input/cursor.h" | 23 | #include "sway/input/cursor.h" |
22 | #include "sway/ipc-server.h" | 24 | #include "sway/ipc-server.h" |
23 | #include "sway/output.h" | 25 | #include "sway/output.h" |
24 | #include "sway/input/seat.h" | 26 | #include "sway/input/seat.h" |
27 | #include "sway/scene_descriptor.h" | ||
25 | #include "sway/server.h" | 28 | #include "sway/server.h" |
29 | #include "sway/sway_text_node.h" | ||
26 | #include "sway/tree/arrange.h" | 30 | #include "sway/tree/arrange.h" |
27 | #include "sway/tree/container.h" | 31 | #include "sway/tree/container.h" |
28 | #include "sway/tree/view.h" | 32 | #include "sway/tree/view.h" |
@@ -32,15 +36,29 @@ | |||
32 | #include "pango.h" | 36 | #include "pango.h" |
33 | #include "stringop.h" | 37 | #include "stringop.h" |
34 | 38 | ||
35 | void view_init(struct sway_view *view, enum sway_view_type type, | 39 | bool view_init(struct sway_view *view, enum sway_view_type type, |
36 | const struct sway_view_impl *impl) { | 40 | const struct sway_view_impl *impl) { |
41 | bool failed = false; | ||
42 | view->scene_tree = alloc_scene_tree(root->staging, &failed); | ||
43 | view->content_tree = alloc_scene_tree(view->scene_tree, &failed); | ||
44 | |||
45 | if (!failed && !scene_descriptor_assign(&view->scene_tree->node, | ||
46 | SWAY_SCENE_DESC_VIEW, view)) { | ||
47 | failed = true; | ||
48 | } | ||
49 | |||
50 | if (failed) { | ||
51 | wlr_scene_node_destroy(&view->scene_tree->node); | ||
52 | return false; | ||
53 | } | ||
54 | |||
37 | view->type = type; | 55 | view->type = type; |
38 | view->impl = impl; | 56 | view->impl = impl; |
39 | view->executed_criteria = create_list(); | 57 | view->executed_criteria = create_list(); |
40 | wl_list_init(&view->saved_buffers); | ||
41 | view->allow_request_urgent = true; | 58 | view->allow_request_urgent = true; |
42 | view->shortcuts_inhibit = SHORTCUTS_INHIBIT_DEFAULT; | 59 | view->shortcuts_inhibit = SHORTCUTS_INHIBIT_DEFAULT; |
43 | wl_signal_init(&view->events.unmap); | 60 | wl_signal_init(&view->events.unmap); |
61 | return true; | ||
44 | } | 62 | } |
45 | 63 | ||
46 | void view_destroy(struct sway_view *view) { | 64 | void view_destroy(struct sway_view *view) { |
@@ -56,11 +74,11 @@ void view_destroy(struct sway_view *view) { | |||
56 | "(might have a pending transaction?)")) { | 74 | "(might have a pending transaction?)")) { |
57 | return; | 75 | return; |
58 | } | 76 | } |
59 | if (!wl_list_empty(&view->saved_buffers)) { | 77 | wl_list_remove(&view->events.unmap.listener_list); |
60 | view_remove_saved_buffer(view); | ||
61 | } | ||
62 | list_free(view->executed_criteria); | 78 | list_free(view->executed_criteria); |
63 | 79 | ||
80 | view_assign_ctx(view, NULL); | ||
81 | wlr_scene_node_destroy(&view->scene_tree->node); | ||
64 | free(view->title_format); | 82 | free(view->title_format); |
65 | 83 | ||
66 | if (view->impl->destroy) { | 84 | if (view->impl->destroy) { |
@@ -108,7 +126,7 @@ const char *view_get_instance(struct sway_view *view) { | |||
108 | } | 126 | } |
109 | return NULL; | 127 | return NULL; |
110 | } | 128 | } |
111 | #if HAVE_XWAYLAND | 129 | #if WLR_HAS_XWAYLAND |
112 | uint32_t view_get_x11_window_id(struct sway_view *view) { | 130 | uint32_t view_get_x11_window_id(struct sway_view *view) { |
113 | if (view->impl->get_int_prop) { | 131 | if (view->impl->get_int_prop) { |
114 | return view->impl->get_int_prop(view, VIEW_PROP_X11_WINDOW_ID); | 132 | return view->impl->get_int_prop(view, VIEW_PROP_X11_WINDOW_ID); |
@@ -141,7 +159,7 @@ const char *view_get_shell(struct sway_view *view) { | |||
141 | switch(view->type) { | 159 | switch(view->type) { |
142 | case SWAY_VIEW_XDG_SHELL: | 160 | case SWAY_VIEW_XDG_SHELL: |
143 | return "xdg_shell"; | 161 | return "xdg_shell"; |
144 | #if HAVE_XWAYLAND | 162 | #if WLR_HAS_XWAYLAND |
145 | case SWAY_VIEW_XWAYLAND: | 163 | case SWAY_VIEW_XWAYLAND: |
146 | return "xwayland"; | 164 | return "xwayland"; |
147 | #endif | 165 | #endif |
@@ -206,7 +224,7 @@ bool view_ancestor_is_only_visible(struct sway_view *view) { | |||
206 | } else { | 224 | } else { |
207 | only_visible = true; | 225 | only_visible = true; |
208 | } | 226 | } |
209 | con = con->parent; | 227 | con = con->pending.parent; |
210 | } | 228 | } |
211 | return only_visible; | 229 | return only_visible; |
212 | } | 230 | } |
@@ -222,72 +240,73 @@ static bool view_is_only_visible(struct sway_view *view) { | |||
222 | } | 240 | } |
223 | } | 241 | } |
224 | 242 | ||
225 | con = con->parent; | 243 | con = con->pending.parent; |
226 | } | 244 | } |
227 | 245 | ||
228 | return true; | 246 | return true; |
229 | } | 247 | } |
230 | 248 | ||
231 | static bool gaps_to_edge(struct sway_view *view) { | 249 | static bool gaps_to_edge(struct sway_view *view) { |
232 | struct side_gaps gaps = view->container->workspace->current_gaps; | 250 | struct side_gaps gaps = view->container->pending.workspace->current_gaps; |
233 | return gaps.top > 0 || gaps.right > 0 || gaps.bottom > 0 || gaps.left > 0; | 251 | return gaps.top > 0 || gaps.right > 0 || gaps.bottom > 0 || gaps.left > 0; |
234 | } | 252 | } |
235 | 253 | ||
236 | void view_autoconfigure(struct sway_view *view) { | 254 | void view_autoconfigure(struct sway_view *view) { |
237 | struct sway_container *con = view->container; | 255 | struct sway_container *con = view->container; |
238 | struct sway_workspace *ws = con->workspace; | 256 | struct sway_workspace *ws = con->pending.workspace; |
239 | 257 | ||
240 | if (container_is_scratchpad_hidden(con) && | 258 | if (container_is_scratchpad_hidden(con) && |
241 | con->fullscreen_mode != FULLSCREEN_GLOBAL) { | 259 | con->pending.fullscreen_mode != FULLSCREEN_GLOBAL) { |
242 | return; | 260 | return; |
243 | } | 261 | } |
244 | struct sway_output *output = ws ? ws->output : NULL; | 262 | struct sway_output *output = ws ? ws->output : NULL; |
245 | 263 | ||
246 | if (con->fullscreen_mode == FULLSCREEN_WORKSPACE) { | 264 | if (con->pending.fullscreen_mode == FULLSCREEN_WORKSPACE) { |
247 | con->content_x = output->lx; | 265 | con->pending.content_x = output->lx; |
248 | con->content_y = output->ly; | 266 | con->pending.content_y = output->ly; |
249 | con->content_width = output->width; | 267 | con->pending.content_width = output->width; |
250 | con->content_height = output->height; | 268 | con->pending.content_height = output->height; |
251 | return; | 269 | return; |
252 | } else if (con->fullscreen_mode == FULLSCREEN_GLOBAL) { | 270 | } else if (con->pending.fullscreen_mode == FULLSCREEN_GLOBAL) { |
253 | con->content_x = root->x; | 271 | con->pending.content_x = root->x; |
254 | con->content_y = root->y; | 272 | con->pending.content_y = root->y; |
255 | con->content_width = root->width; | 273 | con->pending.content_width = root->width; |
256 | con->content_height = root->height; | 274 | con->pending.content_height = root->height; |
257 | return; | 275 | return; |
258 | } | 276 | } |
259 | 277 | ||
260 | con->border_top = con->border_bottom = true; | 278 | con->pending.border_top = con->pending.border_bottom = true; |
261 | con->border_left = con->border_right = true; | 279 | con->pending.border_left = con->pending.border_right = true; |
262 | double y_offset = 0; | 280 | double y_offset = 0; |
263 | 281 | ||
264 | if (!container_is_floating(con) && ws) { | 282 | if (!container_is_floating_or_child(con) && ws) { |
265 | if (config->hide_edge_borders == E_BOTH | 283 | if (config->hide_edge_borders == E_BOTH |
266 | || config->hide_edge_borders == E_VERTICAL) { | 284 | || config->hide_edge_borders == E_VERTICAL) { |
267 | con->border_left = con->x != ws->x; | 285 | con->pending.border_left = con->pending.x != ws->x; |
268 | int right_x = con->x + con->width; | 286 | int right_x = con->pending.x + con->pending.width; |
269 | con->border_right = right_x != ws->x + ws->width; | 287 | con->pending.border_right = right_x != ws->x + ws->width; |
270 | } | 288 | } |
271 | 289 | ||
272 | if (config->hide_edge_borders == E_BOTH | 290 | if (config->hide_edge_borders == E_BOTH |
273 | || config->hide_edge_borders == E_HORIZONTAL) { | 291 | || config->hide_edge_borders == E_HORIZONTAL) { |
274 | con->border_top = con->y != ws->y; | 292 | con->pending.border_top = con->pending.y != ws->y; |
275 | int bottom_y = con->y + con->height; | 293 | int bottom_y = con->pending.y + con->pending.height; |
276 | con->border_bottom = bottom_y != ws->y + ws->height; | 294 | con->pending.border_bottom = bottom_y != ws->y + ws->height; |
277 | } | 295 | } |
278 | 296 | ||
279 | bool smart = config->hide_edge_borders_smart == ESMART_ON || | 297 | bool smart = config->hide_edge_borders_smart == ESMART_ON || |
280 | (config->hide_edge_borders_smart == ESMART_NO_GAPS && | 298 | (config->hide_edge_borders_smart == ESMART_NO_GAPS && |
281 | !gaps_to_edge(view)); | 299 | !gaps_to_edge(view)); |
282 | if (smart) { | 300 | if (smart) { |
283 | bool show_border = container_is_floating_or_child(con) || | 301 | bool show_border = !view_is_only_visible(view); |
284 | !view_is_only_visible(view); | 302 | con->pending.border_left &= show_border; |
285 | con->border_left &= show_border; | 303 | con->pending.border_right &= show_border; |
286 | con->border_right &= show_border; | 304 | con->pending.border_top &= show_border; |
287 | con->border_top &= show_border; | 305 | con->pending.border_bottom &= show_border; |
288 | con->border_bottom &= show_border; | ||
289 | } | 306 | } |
307 | } | ||
290 | 308 | ||
309 | if (!container_is_floating(con)) { | ||
291 | // In a tabbed or stacked container, the container's y is the top of the | 310 | // 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, | 311 | // 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. | 312 | // bar, and disable any top border because we'll always have the title bar. |
@@ -298,56 +317,56 @@ void view_autoconfigure(struct sway_view *view) { | |||
298 | enum sway_container_layout layout = container_parent_layout(con); | 317 | enum sway_container_layout layout = container_parent_layout(con); |
299 | if (layout == L_TABBED) { | 318 | if (layout == L_TABBED) { |
300 | y_offset = container_titlebar_height(); | 319 | y_offset = container_titlebar_height(); |
301 | con->border_top = false; | 320 | con->pending.border_top = false; |
302 | } else if (layout == L_STACKED) { | 321 | } else if (layout == L_STACKED) { |
303 | y_offset = container_titlebar_height() * siblings->length; | 322 | y_offset = container_titlebar_height() * siblings->length; |
304 | con->border_top = false; | 323 | con->pending.border_top = false; |
305 | } | 324 | } |
306 | } | 325 | } |
307 | } | 326 | } |
308 | 327 | ||
309 | double x, y, width, height; | 328 | double x, y, width, height; |
310 | switch (con->border) { | 329 | switch (con->pending.border) { |
311 | default: | 330 | default: |
312 | case B_CSD: | 331 | case B_CSD: |
313 | case B_NONE: | 332 | case B_NONE: |
314 | x = con->x; | 333 | x = con->pending.x; |
315 | y = con->y + y_offset; | 334 | y = con->pending.y + y_offset; |
316 | width = con->width; | 335 | width = con->pending.width; |
317 | height = con->height - y_offset; | 336 | height = con->pending.height - y_offset; |
318 | break; | 337 | break; |
319 | case B_PIXEL: | 338 | case B_PIXEL: |
320 | x = con->x + con->border_thickness * con->border_left; | 339 | x = con->pending.x + con->pending.border_thickness * con->pending.border_left; |
321 | y = con->y + con->border_thickness * con->border_top + y_offset; | 340 | y = con->pending.y + con->pending.border_thickness * con->pending.border_top + y_offset; |
322 | width = con->width | 341 | width = con->pending.width |
323 | - con->border_thickness * con->border_left | 342 | - con->pending.border_thickness * con->pending.border_left |
324 | - con->border_thickness * con->border_right; | 343 | - con->pending.border_thickness * con->pending.border_right; |
325 | height = con->height - y_offset | 344 | height = con->pending.height - y_offset |
326 | - con->border_thickness * con->border_top | 345 | - con->pending.border_thickness * con->pending.border_top |
327 | - con->border_thickness * con->border_bottom; | 346 | - con->pending.border_thickness * con->pending.border_bottom; |
328 | break; | 347 | break; |
329 | case B_NORMAL: | 348 | case B_NORMAL: |
330 | // Height is: 1px border + 3px pad + title height + 3px pad + 1px border | 349 | // Height is: 1px border + 3px pad + title height + 3px pad + 1px border |
331 | x = con->x + con->border_thickness * con->border_left; | 350 | x = con->pending.x + con->pending.border_thickness * con->pending.border_left; |
332 | width = con->width | 351 | width = con->pending.width |
333 | - con->border_thickness * con->border_left | 352 | - con->pending.border_thickness * con->pending.border_left |
334 | - con->border_thickness * con->border_right; | 353 | - con->pending.border_thickness * con->pending.border_right; |
335 | if (y_offset) { | 354 | if (y_offset) { |
336 | y = con->y + y_offset; | 355 | y = con->pending.y + y_offset; |
337 | height = con->height - y_offset | 356 | height = con->pending.height - y_offset |
338 | - con->border_thickness * con->border_bottom; | 357 | - con->pending.border_thickness * con->pending.border_bottom; |
339 | } else { | 358 | } else { |
340 | y = con->y + container_titlebar_height(); | 359 | y = con->pending.y + container_titlebar_height(); |
341 | height = con->height - container_titlebar_height() | 360 | height = con->pending.height - container_titlebar_height() |
342 | - con->border_thickness * con->border_bottom; | 361 | - con->pending.border_thickness * con->pending.border_bottom; |
343 | } | 362 | } |
344 | break; | 363 | break; |
345 | } | 364 | } |
346 | 365 | ||
347 | con->content_x = x; | 366 | con->pending.content_x = x; |
348 | con->content_y = y; | 367 | con->pending.content_y = y; |
349 | con->content_width = width; | 368 | con->pending.content_width = width; |
350 | con->content_height = height; | 369 | con->pending.content_height = height; |
351 | } | 370 | } |
352 | 371 | ||
353 | void view_set_activated(struct sway_view *view, bool activated) { | 372 | void view_set_activated(struct sway_view *view, bool activated) { |
@@ -360,17 +379,17 @@ void view_set_activated(struct sway_view *view, bool activated) { | |||
360 | } | 379 | } |
361 | } | 380 | } |
362 | 381 | ||
363 | void view_request_activate(struct sway_view *view) { | 382 | void view_request_activate(struct sway_view *view, struct sway_seat *seat) { |
364 | struct sway_workspace *ws = view->container->workspace; | 383 | struct sway_workspace *ws = view->container->pending.workspace; |
365 | if (!ws) { // hidden scratchpad container | 384 | if (!seat) { |
366 | return; | 385 | seat = input_manager_current_seat(); |
367 | } | 386 | } |
368 | struct sway_seat *seat = input_manager_current_seat(); | ||
369 | 387 | ||
370 | switch (config->focus_on_window_activation) { | 388 | switch (config->focus_on_window_activation) { |
371 | case FOWA_SMART: | 389 | case FOWA_SMART: |
372 | if (workspace_is_visible(ws)) { | 390 | if (ws && workspace_is_visible(ws)) { |
373 | seat_set_focus_container(seat, view->container); | 391 | seat_set_focus_container(seat, view->container); |
392 | container_raise_floating(view->container); | ||
374 | } else { | 393 | } else { |
375 | view_set_urgent(view, true); | 394 | view_set_urgent(view, true); |
376 | } | 395 | } |
@@ -379,11 +398,23 @@ void view_request_activate(struct sway_view *view) { | |||
379 | view_set_urgent(view, true); | 398 | view_set_urgent(view, true); |
380 | break; | 399 | break; |
381 | case FOWA_FOCUS: | 400 | case FOWA_FOCUS: |
382 | seat_set_focus_container(seat, view->container); | 401 | if (container_is_scratchpad_hidden_or_child(view->container)) { |
402 | root_scratchpad_show(view->container); | ||
403 | } else { | ||
404 | seat_set_focus_container(seat, view->container); | ||
405 | container_raise_floating(view->container); | ||
406 | } | ||
383 | break; | 407 | break; |
384 | case FOWA_NONE: | 408 | case FOWA_NONE: |
385 | break; | 409 | break; |
386 | } | 410 | } |
411 | transaction_commit_dirty(); | ||
412 | } | ||
413 | |||
414 | void view_request_urgent(struct sway_view *view) { | ||
415 | if (config->focus_on_window_activation != FOWA_NONE) { | ||
416 | view_set_urgent(view, true); | ||
417 | } | ||
387 | } | 418 | } |
388 | 419 | ||
389 | void view_set_csd_from_server(struct sway_view *view, bool enabled) { | 420 | void view_set_csd_from_server(struct sway_view *view, bool enabled) { |
@@ -401,13 +432,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) { | 432 | 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); | 433 | sway_log(SWAY_DEBUG, "View %p updated CSD to %i", view, enabled); |
403 | struct sway_container *con = view->container; | 434 | struct sway_container *con = view->container; |
404 | if (enabled && con && con->border != B_CSD) { | 435 | if (enabled && con && con->pending.border != B_CSD) { |
405 | con->saved_border = con->border; | 436 | con->saved_border = con->pending.border; |
406 | if (container_is_floating(con)) { | 437 | if (container_is_floating(con)) { |
407 | con->border = B_CSD; | 438 | con->pending.border = B_CSD; |
408 | } | 439 | } |
409 | } else if (!enabled && con && con->border == B_CSD) { | 440 | } else if (!enabled && con && con->pending.border == B_CSD) { |
410 | con->border = con->saved_border; | 441 | con->pending.border = con->saved_border; |
411 | } | 442 | } |
412 | view->using_csd = enabled; | 443 | view->using_csd = enabled; |
413 | } | 444 | } |
@@ -430,49 +461,6 @@ void view_close_popups(struct sway_view *view) { | |||
430 | } | 461 | } |
431 | } | 462 | } |
432 | 463 | ||
433 | void view_damage_from(struct sway_view *view) { | ||
434 | for (int i = 0; i < root->outputs->length; ++i) { | ||
435 | struct sway_output *output = root->outputs->items[i]; | ||
436 | output_damage_from_view(output, view); | ||
437 | } | ||
438 | } | ||
439 | |||
440 | void view_for_each_surface(struct sway_view *view, | ||
441 | wlr_surface_iterator_func_t iterator, void *user_data) { | ||
442 | if (!view->surface) { | ||
443 | return; | ||
444 | } | ||
445 | if (view->impl->for_each_surface) { | ||
446 | view->impl->for_each_surface(view, iterator, user_data); | ||
447 | } else { | ||
448 | wlr_surface_for_each_surface(view->surface, iterator, user_data); | ||
449 | } | ||
450 | } | ||
451 | |||
452 | void view_for_each_popup_surface(struct sway_view *view, | ||
453 | wlr_surface_iterator_func_t iterator, void *user_data) { | ||
454 | if (!view->surface) { | ||
455 | return; | ||
456 | } | ||
457 | if (view->impl->for_each_popup_surface) { | ||
458 | view->impl->for_each_popup_surface(view, iterator, user_data); | ||
459 | } | ||
460 | } | ||
461 | |||
462 | static void view_subsurface_create(struct sway_view *view, | ||
463 | struct wlr_subsurface *subsurface); | ||
464 | |||
465 | static void view_init_subsurfaces(struct sway_view *view, | ||
466 | struct wlr_surface *surface); | ||
467 | |||
468 | static void view_handle_surface_new_subsurface(struct wl_listener *listener, | ||
469 | void *data) { | ||
470 | struct sway_view *view = | ||
471 | wl_container_of(listener, view, surface_new_subsurface); | ||
472 | struct wlr_subsurface *subsurface = data; | ||
473 | view_subsurface_create(view, subsurface); | ||
474 | } | ||
475 | |||
476 | static bool view_has_executed_criteria(struct sway_view *view, | 464 | static bool view_has_executed_criteria(struct sway_view *view, |
477 | struct criteria *criteria) { | 465 | struct criteria *criteria) { |
478 | for (int i = 0; i < view->executed_criteria->length; ++i) { | 466 | for (int i = 0; i < view->executed_criteria->length; ++i) { |
@@ -511,10 +499,10 @@ void view_execute_criteria(struct sway_view *view) { | |||
511 | static void view_populate_pid(struct sway_view *view) { | 499 | static void view_populate_pid(struct sway_view *view) { |
512 | pid_t pid; | 500 | pid_t pid; |
513 | switch (view->type) { | 501 | switch (view->type) { |
514 | #if HAVE_XWAYLAND | 502 | #if WLR_HAS_XWAYLAND |
515 | case SWAY_VIEW_XWAYLAND:; | 503 | case SWAY_VIEW_XWAYLAND:; |
516 | struct wlr_xwayland_surface *surf = | 504 | struct wlr_xwayland_surface *surf = |
517 | wlr_xwayland_surface_from_wlr_surface(view->surface); | 505 | wlr_xwayland_surface_try_from_wlr_surface(view->surface); |
518 | pid = surf->pid; | 506 | pid = surf->pid; |
519 | break; | 507 | break; |
520 | #endif | 508 | #endif |
@@ -527,6 +515,20 @@ static void view_populate_pid(struct sway_view *view) { | |||
527 | view->pid = pid; | 515 | view->pid = pid; |
528 | } | 516 | } |
529 | 517 | ||
518 | void view_assign_ctx(struct sway_view *view, struct launcher_ctx *ctx) { | ||
519 | if (view->ctx) { | ||
520 | // This ctx has been replaced | ||
521 | launcher_ctx_destroy(view->ctx); | ||
522 | view->ctx = NULL; | ||
523 | } | ||
524 | if (ctx == NULL) { | ||
525 | return; | ||
526 | } | ||
527 | launcher_ctx_consume(ctx); | ||
528 | |||
529 | view->ctx = ctx; | ||
530 | } | ||
531 | |||
530 | static struct sway_workspace *select_workspace(struct sway_view *view) { | 532 | static struct sway_workspace *select_workspace(struct sway_view *view) { |
531 | struct sway_seat *seat = input_manager_current_seat(); | 533 | struct sway_seat *seat = input_manager_current_seat(); |
532 | 534 | ||
@@ -562,13 +564,14 @@ static struct sway_workspace *select_workspace(struct sway_view *view) { | |||
562 | } | 564 | } |
563 | list_free(criterias); | 565 | list_free(criterias); |
564 | if (ws) { | 566 | if (ws) { |
565 | root_remove_workspace_pid(view->pid); | 567 | view_assign_ctx(view, NULL); |
566 | return ws; | 568 | return ws; |
567 | } | 569 | } |
568 | 570 | ||
569 | // Check if there's a PID mapping | 571 | // Check if there's a PID mapping |
570 | ws = root_workspace_for_pid(view->pid); | 572 | ws = view->ctx ? launcher_ctx_get_workspace(view->ctx) : NULL; |
571 | if (ws) { | 573 | if (ws) { |
574 | view_assign_ctx(view, NULL); | ||
572 | return ws; | 575 | return ws; |
573 | } | 576 | } |
574 | 577 | ||
@@ -577,7 +580,7 @@ static struct sway_workspace *select_workspace(struct sway_view *view) { | |||
577 | if (node && node->type == N_WORKSPACE) { | 580 | if (node && node->type == N_WORKSPACE) { |
578 | return node->sway_workspace; | 581 | return node->sway_workspace; |
579 | } else if (node && node->type == N_CONTAINER) { | 582 | } else if (node && node->type == N_CONTAINER) { |
580 | return node->sway_container->workspace; | 583 | return node->sway_container->pending.workspace; |
581 | } | 584 | } |
582 | 585 | ||
583 | // When there's no outputs connected, the above should match a workspace on | 586 | // When there's no outputs connected, the above should match a workspace on |
@@ -586,16 +589,29 @@ static struct sway_workspace *select_workspace(struct sway_view *view) { | |||
586 | return NULL; | 589 | return NULL; |
587 | } | 590 | } |
588 | 591 | ||
592 | static void update_ext_foreign_toplevel(struct sway_view *view) { | ||
593 | struct wlr_ext_foreign_toplevel_handle_v1_state toplevel_state = { | ||
594 | .app_id = view_get_app_id(view), | ||
595 | .title = view_get_title(view), | ||
596 | }; | ||
597 | wlr_ext_foreign_toplevel_handle_v1_update_state(view->ext_foreign_toplevel, &toplevel_state); | ||
598 | } | ||
599 | |||
589 | static bool should_focus(struct sway_view *view) { | 600 | static bool should_focus(struct sway_view *view) { |
590 | struct sway_seat *seat = input_manager_current_seat(); | 601 | struct sway_seat *seat = input_manager_current_seat(); |
591 | struct sway_container *prev_con = seat_get_focused_container(seat); | 602 | struct sway_container *prev_con = seat_get_focused_container(seat); |
592 | struct sway_workspace *prev_ws = seat_get_focused_workspace(seat); | 603 | struct sway_workspace *prev_ws = seat_get_focused_workspace(seat); |
593 | struct sway_workspace *map_ws = view->container->workspace; | 604 | struct sway_workspace *map_ws = view->container->pending.workspace; |
594 | 605 | ||
595 | if (view->container->fullscreen_mode == FULLSCREEN_GLOBAL) { | 606 | if (view->container->pending.fullscreen_mode == FULLSCREEN_GLOBAL) { |
596 | return true; | 607 | return true; |
597 | } | 608 | } |
598 | 609 | ||
610 | // View opened "under" fullscreen view should not be given focus. | ||
611 | if (root->fullscreen_global || !map_ws || map_ws->fullscreen) { | ||
612 | return false; | ||
613 | } | ||
614 | |||
599 | // Views can only take focus if they are mapped into the active workspace | 615 | // Views can only take focus if they are mapped into the active workspace |
600 | if (prev_ws != map_ws) { | 616 | if (prev_ws != map_ws) { |
601 | return false; | 617 | return false; |
@@ -603,9 +619,9 @@ static bool should_focus(struct sway_view *view) { | |||
603 | 619 | ||
604 | // If the view is the only one in the focused workspace, it'll get focus | 620 | // If the view is the only one in the focused workspace, it'll get focus |
605 | // regardless of any no_focus criteria. | 621 | // regardless of any no_focus criteria. |
606 | if (!view->container->parent && !prev_con) { | 622 | if (!view->container->pending.parent && !prev_con) { |
607 | size_t num_children = view->container->workspace->tiling->length + | 623 | size_t num_children = view->container->pending.workspace->tiling->length + |
608 | view->container->workspace->floating->length; | 624 | view->container->pending.workspace->floating->length; |
609 | if (num_children == 1) { | 625 | if (num_children == 1) { |
610 | return true; | 626 | return true; |
611 | } | 627 | } |
@@ -635,6 +651,7 @@ static void handle_foreign_activate_request( | |||
635 | break; | 651 | break; |
636 | } | 652 | } |
637 | } | 653 | } |
654 | transaction_commit_dirty(); | ||
638 | } | 655 | } |
639 | 656 | ||
640 | static void handle_foreign_fullscreen_request( | 657 | static void handle_foreign_fullscreen_request( |
@@ -645,9 +662,21 @@ static void handle_foreign_fullscreen_request( | |||
645 | 662 | ||
646 | // Match fullscreen command behavior for scratchpad hidden views | 663 | // Match fullscreen command behavior for scratchpad hidden views |
647 | struct sway_container *container = view->container; | 664 | struct sway_container *container = view->container; |
648 | if (!container->workspace) { | 665 | if (!container->pending.workspace) { |
649 | while (container->parent) { | 666 | while (container->pending.parent) { |
650 | container = container->parent; | 667 | container = container->pending.parent; |
668 | } | ||
669 | } | ||
670 | |||
671 | if (event->fullscreen && event->output && event->output->data) { | ||
672 | struct sway_output *output = event->output->data; | ||
673 | struct sway_workspace *ws = output_get_active_workspace(output); | ||
674 | if (ws && !container_is_scratchpad_hidden(view->container)) { | ||
675 | if (container_is_floating(view->container)) { | ||
676 | workspace_add_floating(ws, view->container); | ||
677 | } else { | ||
678 | workspace_add_tiling(ws, view->container); | ||
679 | } | ||
651 | } | 680 | } |
652 | } | 681 | } |
653 | 682 | ||
@@ -656,12 +685,13 @@ static void handle_foreign_fullscreen_request( | |||
656 | if (event->fullscreen) { | 685 | if (event->fullscreen) { |
657 | arrange_root(); | 686 | arrange_root(); |
658 | } else { | 687 | } else { |
659 | if (container->parent) { | 688 | if (container->pending.parent) { |
660 | arrange_container(container->parent); | 689 | arrange_container(container->pending.parent); |
661 | } else if (container->workspace) { | 690 | } else if (container->pending.workspace) { |
662 | arrange_workspace(container->workspace); | 691 | arrange_workspace(container->pending.workspace); |
663 | } | 692 | } |
664 | } | 693 | } |
694 | transaction_commit_dirty(); | ||
665 | } | 695 | } |
666 | 696 | ||
667 | static void handle_foreign_close_request( | 697 | static void handle_foreign_close_request( |
@@ -692,6 +722,13 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface, | |||
692 | view_populate_pid(view); | 722 | view_populate_pid(view); |
693 | view->container = container_create(view); | 723 | view->container = container_create(view); |
694 | 724 | ||
725 | if (view->ctx == NULL) { | ||
726 | struct launcher_ctx *ctx = launcher_ctx_find_pid(view->pid); | ||
727 | if (ctx != NULL) { | ||
728 | view_assign_ctx(view, ctx); | ||
729 | } | ||
730 | } | ||
731 | |||
695 | // If there is a request to be opened fullscreen on a specific output, try | 732 | // If there is a request to be opened fullscreen on a specific output, try |
696 | // to honor that request. Otherwise, fallback to assigns, pid mappings, | 733 | // to honor that request. Otherwise, fallback to assigns, pid mappings, |
697 | // focused workspace, etc | 734 | // focused workspace, etc |
@@ -705,10 +742,36 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface, | |||
705 | } | 742 | } |
706 | 743 | ||
707 | struct sway_seat *seat = input_manager_current_seat(); | 744 | struct sway_seat *seat = input_manager_current_seat(); |
708 | struct sway_node *node = ws ? seat_get_focus_inactive(seat, &ws->node) | 745 | struct sway_node *node = |
709 | : seat_get_focus_inactive(seat, &root->node); | 746 | seat_get_focus_inactive(seat, ws ? &ws->node : &root->node); |
710 | struct sway_container *target_sibling = node->type == N_CONTAINER ? | 747 | struct sway_container *target_sibling = NULL; |
711 | node->sway_container : NULL; | 748 | if (node && node->type == N_CONTAINER) { |
749 | if (container_is_floating(node->sway_container)) { | ||
750 | // If we're about to launch the view into the floating container, then | ||
751 | // launch it as a tiled view instead. | ||
752 | if (ws) { | ||
753 | target_sibling = seat_get_focus_inactive_tiling(seat, ws); | ||
754 | if (target_sibling) { | ||
755 | struct sway_container *con = | ||
756 | seat_get_focus_inactive_view(seat, &target_sibling->node); | ||
757 | if (con) { | ||
758 | target_sibling = con; | ||
759 | } | ||
760 | } | ||
761 | } else { | ||
762 | ws = seat_get_last_known_workspace(seat); | ||
763 | } | ||
764 | } else { | ||
765 | target_sibling = node->sway_container; | ||
766 | } | ||
767 | } | ||
768 | |||
769 | struct wlr_ext_foreign_toplevel_handle_v1_state foreign_toplevel_state = { | ||
770 | .app_id = view_get_app_id(view), | ||
771 | .title = view_get_title(view), | ||
772 | }; | ||
773 | view->ext_foreign_toplevel = | ||
774 | wlr_ext_foreign_toplevel_handle_v1_create(server.foreign_toplevel_list, &foreign_toplevel_state); | ||
712 | 775 | ||
713 | view->foreign_toplevel = | 776 | view->foreign_toplevel = |
714 | wlr_foreign_toplevel_handle_v1_create(server.foreign_toplevel_manager); | 777 | wlr_foreign_toplevel_handle_v1_create(server.foreign_toplevel_manager); |
@@ -725,13 +788,6 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface, | |||
725 | wl_signal_add(&view->foreign_toplevel->events.destroy, | 788 | wl_signal_add(&view->foreign_toplevel->events.destroy, |
726 | &view->foreign_destroy); | 789 | &view->foreign_destroy); |
727 | 790 | ||
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; | 791 | struct sway_container *container = view->container; |
736 | if (target_sibling) { | 792 | if (target_sibling) { |
737 | container_add_sibling(target_sibling, container, 1); | 793 | container_add_sibling(target_sibling, container, 1); |
@@ -740,30 +796,25 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface, | |||
740 | } | 796 | } |
741 | ipc_event_window(view->container, "new"); | 797 | ipc_event_window(view->container, "new"); |
742 | 798 | ||
743 | view_init_subsurfaces(view, wlr_surface); | ||
744 | wl_signal_add(&wlr_surface->events.new_subsurface, | ||
745 | &view->surface_new_subsurface); | ||
746 | view->surface_new_subsurface.notify = view_handle_surface_new_subsurface; | ||
747 | |||
748 | if (decoration) { | 799 | if (decoration) { |
749 | view_update_csd_from_client(view, decoration); | 800 | view_update_csd_from_client(view, decoration); |
750 | } | 801 | } |
751 | 802 | ||
752 | if (view->impl->wants_floating && view->impl->wants_floating(view)) { | 803 | if (view->impl->wants_floating && view->impl->wants_floating(view)) { |
753 | view->container->border = config->floating_border; | 804 | view->container->pending.border = config->floating_border; |
754 | view->container->border_thickness = config->floating_border_thickness; | 805 | view->container->pending.border_thickness = config->floating_border_thickness; |
755 | container_set_floating(view->container, true); | 806 | container_set_floating(view->container, true); |
756 | } else { | 807 | } else { |
757 | view->container->border = config->border; | 808 | view->container->pending.border = config->border; |
758 | view->container->border_thickness = config->border_thickness; | 809 | view->container->pending.border_thickness = config->border_thickness; |
759 | view_set_tiled(view, true); | 810 | view_set_tiled(view, true); |
760 | } | 811 | } |
761 | 812 | ||
762 | if (config->popup_during_fullscreen == POPUP_LEAVE && | 813 | if (config->popup_during_fullscreen == POPUP_LEAVE && |
763 | container->workspace && | 814 | container->pending.workspace && |
764 | container->workspace->fullscreen && | 815 | container->pending.workspace->fullscreen && |
765 | container->workspace->fullscreen->view) { | 816 | container->pending.workspace->fullscreen->view) { |
766 | struct sway_container *fs = container->workspace->fullscreen; | 817 | struct sway_container *fs = container->pending.workspace->fullscreen; |
767 | if (view_is_transient_for(view, fs->view)) { | 818 | if (view_is_transient_for(view, fs->view)) { |
768 | container_set_fullscreen(fs, false); | 819 | container_set_fullscreen(fs, false); |
769 | } | 820 | } |
@@ -774,12 +825,12 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface, | |||
774 | 825 | ||
775 | if (fullscreen) { | 826 | if (fullscreen) { |
776 | container_set_fullscreen(view->container, true); | 827 | container_set_fullscreen(view->container, true); |
777 | arrange_workspace(view->container->workspace); | 828 | arrange_workspace(view->container->pending.workspace); |
778 | } else { | 829 | } else { |
779 | if (container->parent) { | 830 | if (container->pending.parent) { |
780 | arrange_container(container->parent); | 831 | arrange_container(container->pending.parent); |
781 | } else if (container->workspace) { | 832 | } else if (container->pending.workspace) { |
782 | arrange_workspace(container->workspace); | 833 | arrange_workspace(container->pending.workspace); |
783 | } | 834 | } |
784 | } | 835 | } |
785 | 836 | ||
@@ -787,12 +838,11 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface, | |||
787 | 838 | ||
788 | bool set_focus = should_focus(view); | 839 | bool set_focus = should_focus(view); |
789 | 840 | ||
790 | #if HAVE_XWAYLAND | 841 | #if WLR_HAS_XWAYLAND |
791 | if (wlr_surface_is_xwayland_surface(wlr_surface)) { | 842 | struct wlr_xwayland_surface *xsurface; |
792 | struct wlr_xwayland_surface *xsurface = | 843 | if ((xsurface = wlr_xwayland_surface_try_from_wlr_surface(wlr_surface))) { |
793 | wlr_xwayland_surface_from_wlr_surface(wlr_surface); | 844 | set_focus &= wlr_xwayland_icccm_input_model(xsurface) != |
794 | set_focus = (wlr_xwayland_icccm_input_model(xsurface) != | 845 | WLR_ICCCM_INPUT_MODEL_NONE; |
795 | WLR_ICCCM_INPUT_MODEL_NONE) && set_focus; | ||
796 | } | 846 | } |
797 | #endif | 847 | #endif |
798 | 848 | ||
@@ -800,34 +850,41 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface, | |||
800 | input_manager_set_focus(&view->container->node); | 850 | input_manager_set_focus(&view->container->node); |
801 | } | 851 | } |
802 | 852 | ||
853 | if (view->ext_foreign_toplevel) { | ||
854 | update_ext_foreign_toplevel(view); | ||
855 | } | ||
856 | |||
803 | const char *app_id; | 857 | const char *app_id; |
804 | const char *class; | 858 | const char *class; |
805 | if ((app_id = view_get_app_id(view)) != NULL) { | 859 | if ((app_id = view_get_app_id(view)) != NULL) { |
806 | wlr_foreign_toplevel_handle_v1_set_app_id( | 860 | 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) { | 861 | } else if ((class = view_get_class(view)) != NULL) { |
809 | wlr_foreign_toplevel_handle_v1_set_app_id( | 862 | wlr_foreign_toplevel_handle_v1_set_app_id(view->foreign_toplevel, class); |
810 | view->foreign_toplevel, class); | ||
811 | } | 863 | } |
812 | } | 864 | } |
813 | 865 | ||
814 | void view_unmap(struct sway_view *view) { | 866 | void view_unmap(struct sway_view *view) { |
815 | wl_signal_emit(&view->events.unmap, view); | 867 | wl_signal_emit_mutable(&view->events.unmap, view); |
816 | 868 | ||
817 | wl_list_remove(&view->surface_new_subsurface.link); | 869 | view->executed_criteria->length = 0; |
818 | 870 | ||
819 | if (view->urgent_timer) { | 871 | if (view->urgent_timer) { |
820 | wl_event_source_remove(view->urgent_timer); | 872 | wl_event_source_remove(view->urgent_timer); |
821 | view->urgent_timer = NULL; | 873 | view->urgent_timer = NULL; |
822 | } | 874 | } |
823 | 875 | ||
876 | if (view->ext_foreign_toplevel) { | ||
877 | wlr_ext_foreign_toplevel_handle_v1_destroy(view->ext_foreign_toplevel); | ||
878 | view->ext_foreign_toplevel = NULL; | ||
879 | } | ||
880 | |||
824 | if (view->foreign_toplevel) { | 881 | if (view->foreign_toplevel) { |
825 | wlr_foreign_toplevel_handle_v1_destroy(view->foreign_toplevel); | 882 | wlr_foreign_toplevel_handle_v1_destroy(view->foreign_toplevel); |
826 | view->foreign_toplevel = NULL; | 883 | view->foreign_toplevel = NULL; |
827 | } | 884 | } |
828 | 885 | ||
829 | struct sway_container *parent = view->container->parent; | 886 | struct sway_container *parent = view->container->pending.parent; |
830 | struct sway_workspace *ws = view->container->workspace; | 887 | struct sway_workspace *ws = view->container->pending.workspace; |
831 | container_begin_destroy(view->container); | 888 | container_begin_destroy(view->container); |
832 | if (parent) { | 889 | if (parent) { |
833 | container_reap_empty(parent); | 890 | container_reap_empty(parent); |
@@ -860,262 +917,61 @@ void view_unmap(struct sway_view *view) { | |||
860 | view->surface = NULL; | 917 | view->surface = NULL; |
861 | } | 918 | } |
862 | 919 | ||
863 | void view_update_size(struct sway_view *view, int width, int height) { | 920 | void view_update_size(struct sway_view *view) { |
864 | struct sway_container *con = view->container; | 921 | struct sway_container *con = view->container; |
865 | 922 | con->pending.content_width = view->geometry.width; | |
866 | if (container_is_floating(con)) { | 923 | con->pending.content_height = view->geometry.height; |
867 | con->content_width = width; | 924 | container_set_geometry_from_content(con); |
868 | con->content_height = height; | ||
869 | container_set_geometry_from_content(con); | ||
870 | } else { | ||
871 | con->surface_x = con->content_x + (con->content_width - width) / 2; | ||
872 | con->surface_y = con->content_y + (con->content_height - height) / 2; | ||
873 | con->surface_x = fmax(con->surface_x, con->content_x); | ||
874 | con->surface_y = fmax(con->surface_y, con->content_y); | ||
875 | } | ||
876 | } | 925 | } |
877 | 926 | ||
878 | static const struct sway_view_child_impl subsurface_impl; | 927 | void view_center_and_clip_surface(struct sway_view *view) { |
879 | 928 | struct sway_container *con = view->container; | |
880 | static void subsurface_get_root_coords(struct sway_view_child *child, | ||
881 | int *root_sx, int *root_sy) { | ||
882 | 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 && | ||
887 | child->parent->impl->get_root_coords) { | ||
888 | int sx, sy; | ||
889 | child->parent->impl->get_root_coords(child->parent, &sx, &sy); | ||
890 | *root_sx += sx; | ||
891 | *root_sy += sy; | ||
892 | } else { | ||
893 | while (surface && wlr_surface_is_subsurface(surface)) { | ||
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 | } | ||
904 | } | ||
905 | |||
906 | static void subsurface_destroy(struct sway_view_child *child) { | ||
907 | if (!sway_assert(child->impl == &subsurface_impl, | ||
908 | "Expected a subsurface")) { | ||
909 | return; | ||
910 | } | ||
911 | struct sway_subsurface *subsurface = (struct sway_subsurface *)child; | ||
912 | wl_list_remove(&subsurface->destroy.link); | ||
913 | free(subsurface); | ||
914 | } | ||
915 | |||
916 | static const struct sway_view_child_impl subsurface_impl = { | ||
917 | .get_root_coords = subsurface_get_root_coords, | ||
918 | .destroy = subsurface_destroy, | ||
919 | }; | ||
920 | |||
921 | static void subsurface_handle_destroy(struct wl_listener *listener, | ||
922 | void *data) { | ||
923 | struct sway_subsurface *subsurface = | ||
924 | wl_container_of(listener, subsurface, destroy); | ||
925 | struct sway_view_child *child = &subsurface->child; | ||
926 | view_child_destroy(child); | ||
927 | } | ||
928 | |||
929 | static void view_child_damage(struct sway_view_child *child, bool whole); | ||
930 | |||
931 | static void view_subsurface_create(struct sway_view *view, | ||
932 | struct wlr_subsurface *wlr_subsurface) { | ||
933 | struct sway_subsurface *subsurface = | ||
934 | calloc(1, sizeof(struct sway_subsurface)); | ||
935 | if (subsurface == NULL) { | ||
936 | sway_log(SWAY_ERROR, "Allocation failed"); | ||
937 | return; | ||
938 | } | ||
939 | view_child_init(&subsurface->child, &subsurface_impl, view, | ||
940 | wlr_subsurface->surface); | ||
941 | |||
942 | wl_signal_add(&wlr_subsurface->events.destroy, &subsurface->destroy); | ||
943 | subsurface->destroy.notify = subsurface_handle_destroy; | ||
944 | |||
945 | subsurface->child.mapped = true; | ||
946 | |||
947 | view_child_damage(&subsurface->child, true); | ||
948 | } | ||
949 | |||
950 | static void view_child_subsurface_create(struct sway_view_child *child, | ||
951 | struct wlr_subsurface *wlr_subsurface) { | ||
952 | struct sway_subsurface *subsurface = | ||
953 | calloc(1, sizeof(struct sway_subsurface)); | ||
954 | if (subsurface == NULL) { | ||
955 | sway_log(SWAY_ERROR, "Allocation failed"); | ||
956 | return; | ||
957 | } | ||
958 | subsurface->child.parent = child; | ||
959 | wl_list_insert(&child->children, &subsurface->child.link); | ||
960 | view_child_init(&subsurface->child, &subsurface_impl, child->view, | ||
961 | wlr_subsurface->surface); | ||
962 | |||
963 | wl_signal_add(&wlr_subsurface->events.destroy, &subsurface->destroy); | ||
964 | subsurface->destroy.notify = subsurface_handle_destroy; | ||
965 | |||
966 | subsurface->child.mapped = true; | ||
967 | |||
968 | view_child_damage(&subsurface->child, true); | ||
969 | } | ||
970 | |||
971 | static void view_child_damage(struct sway_view_child *child, bool whole) { | ||
972 | if (!child || !child->mapped || !child->view || !child->view->container) { | ||
973 | return; | ||
974 | } | ||
975 | int sx, sy; | ||
976 | child->impl->get_root_coords(child, &sx, &sy); | ||
977 | desktop_damage_surface(child->surface, | ||
978 | child->view->container->content_x + sx, | ||
979 | child->view->container->content_y + sy, whole); | ||
980 | } | ||
981 | |||
982 | static void view_child_handle_surface_commit(struct wl_listener *listener, | ||
983 | void *data) { | ||
984 | struct sway_view_child *child = | ||
985 | wl_container_of(listener, child, surface_commit); | ||
986 | view_child_damage(child, false); | ||
987 | } | ||
988 | |||
989 | static void view_child_handle_surface_new_subsurface( | ||
990 | struct wl_listener *listener, void *data) { | ||
991 | struct sway_view_child *child = | ||
992 | wl_container_of(listener, child, surface_new_subsurface); | ||
993 | struct wlr_subsurface *subsurface = data; | ||
994 | view_child_subsurface_create(child, subsurface); | ||
995 | } | ||
996 | |||
997 | static void view_child_handle_surface_destroy(struct wl_listener *listener, | ||
998 | void *data) { | ||
999 | struct sway_view_child *child = | ||
1000 | wl_container_of(listener, child, surface_destroy); | ||
1001 | view_child_destroy(child); | ||
1002 | } | ||
1003 | |||
1004 | static void view_init_subsurfaces(struct sway_view *view, | ||
1005 | struct wlr_surface *surface) { | ||
1006 | struct wlr_subsurface *subsurface; | ||
1007 | wl_list_for_each(subsurface, &surface->subsurfaces, parent_link) { | ||
1008 | view_subsurface_create(view, subsurface); | ||
1009 | } | ||
1010 | } | ||
1011 | |||
1012 | static void view_child_handle_surface_map(struct wl_listener *listener, | ||
1013 | void *data) { | ||
1014 | struct sway_view_child *child = | ||
1015 | wl_container_of(listener, child, surface_map); | ||
1016 | child->mapped = true; | ||
1017 | view_child_damage(child, true); | ||
1018 | } | ||
1019 | |||
1020 | static void view_child_handle_surface_unmap(struct wl_listener *listener, | ||
1021 | void *data) { | ||
1022 | struct sway_view_child *child = | ||
1023 | wl_container_of(listener, child, surface_unmap); | ||
1024 | view_child_damage(child, true); | ||
1025 | child->mapped = false; | ||
1026 | } | ||
1027 | |||
1028 | static void view_child_handle_view_unmap(struct wl_listener *listener, | ||
1029 | void *data) { | ||
1030 | struct sway_view_child *child = | ||
1031 | wl_container_of(listener, child, view_unmap); | ||
1032 | view_child_damage(child, true); | ||
1033 | child->mapped = false; | ||
1034 | } | ||
1035 | |||
1036 | void view_child_init(struct sway_view_child *child, | ||
1037 | const struct sway_view_child_impl *impl, struct sway_view *view, | ||
1038 | struct wlr_surface *surface) { | ||
1039 | child->impl = impl; | ||
1040 | child->view = view; | ||
1041 | child->surface = surface; | ||
1042 | wl_list_init(&child->children); | ||
1043 | |||
1044 | wl_signal_add(&surface->events.commit, &child->surface_commit); | ||
1045 | child->surface_commit.notify = view_child_handle_surface_commit; | ||
1046 | wl_signal_add(&surface->events.new_subsurface, | ||
1047 | &child->surface_new_subsurface); | ||
1048 | child->surface_new_subsurface.notify = | ||
1049 | view_child_handle_surface_new_subsurface; | ||
1050 | wl_signal_add(&surface->events.destroy, &child->surface_destroy); | ||
1051 | child->surface_destroy.notify = view_child_handle_surface_destroy; | ||
1052 | |||
1053 | // Not all child views have a map/unmap event | ||
1054 | child->surface_map.notify = view_child_handle_surface_map; | ||
1055 | wl_list_init(&child->surface_map.link); | ||
1056 | child->surface_unmap.notify = view_child_handle_surface_unmap; | ||
1057 | wl_list_init(&child->surface_unmap.link); | ||
1058 | |||
1059 | wl_signal_add(&view->events.unmap, &child->view_unmap); | ||
1060 | child->view_unmap.notify = view_child_handle_view_unmap; | ||
1061 | |||
1062 | struct sway_workspace *workspace = child->view->container->workspace; | ||
1063 | if (workspace) { | ||
1064 | wlr_surface_send_enter(child->surface, workspace->output->wlr_output); | ||
1065 | } | ||
1066 | |||
1067 | view_init_subsurfaces(child->view, surface); | ||
1068 | } | ||
1069 | |||
1070 | void view_child_destroy(struct sway_view_child *child) { | ||
1071 | if (child->mapped && child->view->container != NULL) { | ||
1072 | view_child_damage(child, true); | ||
1073 | } | ||
1074 | 929 | ||
1075 | if (child->parent != NULL) { | 930 | bool clip_to_geometry = true; |
1076 | wl_list_remove(&child->link); | ||
1077 | child->parent = NULL; | ||
1078 | } | ||
1079 | 931 | ||
1080 | struct sway_view_child *subchild, *tmpchild; | 932 | if (container_is_floating(con)) { |
1081 | wl_list_for_each_safe(subchild, tmpchild, &child->children, link) { | 933 | // We always center the current coordinates rather than the next, as the |
1082 | wl_list_remove(&subchild->link); | 934 | // geometry immediately affects the currently active rendering. |
1083 | subchild->parent = NULL; | 935 | int x = (int) fmax(0, (con->current.content_width - view->geometry.width) / 2); |
1084 | } | 936 | int y = (int) fmax(0, (con->current.content_height - view->geometry.height) / 2); |
1085 | 937 | clip_to_geometry = !view->using_csd; | |
1086 | wl_list_remove(&child->surface_commit.link); | ||
1087 | wl_list_remove(&child->surface_destroy.link); | ||
1088 | wl_list_remove(&child->surface_map.link); | ||
1089 | wl_list_remove(&child->surface_unmap.link); | ||
1090 | wl_list_remove(&child->view_unmap.link); | ||
1091 | wl_list_remove(&child->surface_new_subsurface.link); | ||
1092 | 938 | ||
1093 | if (child->impl && child->impl->destroy) { | 939 | wlr_scene_node_set_position(&view->content_tree->node, x, y); |
1094 | child->impl->destroy(child); | ||
1095 | } else { | 940 | } else { |
1096 | free(child); | 941 | wlr_scene_node_set_position(&view->content_tree->node, 0, 0); |
942 | } | ||
943 | |||
944 | // only make sure to clip the content if there is content to clip | ||
945 | if (!wl_list_empty(&con->view->content_tree->children)) { | ||
946 | struct wlr_box clip = {0}; | ||
947 | if (clip_to_geometry) { | ||
948 | clip = (struct wlr_box){ | ||
949 | .x = con->view->geometry.x, | ||
950 | .y = con->view->geometry.y, | ||
951 | .width = con->current.content_width, | ||
952 | .height = con->current.content_height, | ||
953 | }; | ||
954 | } | ||
955 | wlr_scene_subsurface_tree_set_clip(&con->view->content_tree->node, &clip); | ||
1097 | } | 956 | } |
1098 | } | 957 | } |
1099 | 958 | ||
1100 | struct sway_view *view_from_wlr_surface(struct wlr_surface *wlr_surface) { | 959 | struct sway_view *view_from_wlr_surface(struct wlr_surface *wlr_surface) { |
1101 | if (wlr_surface_is_xdg_surface(wlr_surface)) { | 960 | struct wlr_xdg_surface *xdg_surface; |
1102 | struct wlr_xdg_surface *xdg_surface = | 961 | if ((xdg_surface = wlr_xdg_surface_try_from_wlr_surface(wlr_surface))) { |
1103 | wlr_xdg_surface_from_wlr_surface(wlr_surface); | ||
1104 | return view_from_wlr_xdg_surface(xdg_surface); | 962 | return view_from_wlr_xdg_surface(xdg_surface); |
1105 | } | 963 | } |
1106 | #if HAVE_XWAYLAND | 964 | #if WLR_HAS_XWAYLAND |
1107 | if (wlr_surface_is_xwayland_surface(wlr_surface)) { | 965 | struct wlr_xwayland_surface *xsurface; |
1108 | struct wlr_xwayland_surface *xsurface = | 966 | if ((xsurface = wlr_xwayland_surface_try_from_wlr_surface(wlr_surface))) { |
1109 | wlr_xwayland_surface_from_wlr_surface(wlr_surface); | ||
1110 | return view_from_wlr_xwayland_surface(xsurface); | 967 | return view_from_wlr_xwayland_surface(xsurface); |
1111 | } | 968 | } |
1112 | #endif | 969 | #endif |
1113 | if (wlr_surface_is_subsurface(wlr_surface)) { | 970 | struct wlr_subsurface *subsurface; |
1114 | struct wlr_subsurface *subsurface = | 971 | if ((subsurface = wlr_subsurface_try_from_wlr_surface(wlr_surface))) { |
1115 | wlr_subsurface_from_wlr_surface(wlr_surface); | ||
1116 | return view_from_wlr_surface(subsurface->parent); | 972 | return view_from_wlr_surface(subsurface->parent); |
1117 | } | 973 | } |
1118 | if (wlr_surface_is_layer_surface(wlr_surface)) { | 974 | if (wlr_layer_surface_v1_try_from_wlr_surface(wlr_surface) != NULL) { |
1119 | return NULL; | 975 | return NULL; |
1120 | } | 976 | } |
1121 | 977 | ||
@@ -1196,6 +1052,18 @@ static size_t parse_title_format(struct sway_view *view, char *buffer) { | |||
1196 | return len; | 1052 | return len; |
1197 | } | 1053 | } |
1198 | 1054 | ||
1055 | void view_update_app_id(struct sway_view *view) { | ||
1056 | const char *app_id = view_get_app_id(view); | ||
1057 | |||
1058 | if (view->foreign_toplevel && app_id) { | ||
1059 | wlr_foreign_toplevel_handle_v1_set_app_id(view->foreign_toplevel, app_id); | ||
1060 | } | ||
1061 | |||
1062 | if (view->ext_foreign_toplevel) { | ||
1063 | update_ext_foreign_toplevel(view); | ||
1064 | } | ||
1065 | } | ||
1066 | |||
1199 | void view_update_title(struct sway_view *view, bool force) { | 1067 | void view_update_title(struct sway_view *view, bool force) { |
1200 | const char *title = view_get_title(view); | 1068 | const char *title = view_get_title(view); |
1201 | 1069 | ||
@@ -1211,46 +1079,56 @@ void view_update_title(struct sway_view *view, bool force) { | |||
1211 | 1079 | ||
1212 | free(view->container->title); | 1080 | free(view->container->title); |
1213 | free(view->container->formatted_title); | 1081 | free(view->container->formatted_title); |
1214 | if (title) { | 1082 | |
1215 | size_t len = parse_title_format(view, NULL); | 1083 | size_t len = parse_title_format(view, NULL); |
1084 | |||
1085 | if (len) { | ||
1216 | char *buffer = calloc(len + 1, sizeof(char)); | 1086 | char *buffer = calloc(len + 1, sizeof(char)); |
1217 | if (!sway_assert(buffer, "Unable to allocate title string")) { | 1087 | if (!sway_assert(buffer, "Unable to allocate title string")) { |
1218 | return; | 1088 | return; |
1219 | } | 1089 | } |
1220 | parse_title_format(view, buffer); | ||
1221 | 1090 | ||
1222 | view->container->title = strdup(title); | 1091 | parse_title_format(view, buffer); |
1223 | view->container->formatted_title = buffer; | 1092 | view->container->formatted_title = buffer; |
1224 | } else { | 1093 | } else { |
1225 | view->container->title = NULL; | ||
1226 | view->container->formatted_title = NULL; | 1094 | view->container->formatted_title = NULL; |
1227 | } | 1095 | } |
1228 | container_calculate_title_height(view->container); | 1096 | |
1229 | config_update_font_height(false); | 1097 | view->container->title = title ? strdup(title) : NULL; |
1230 | 1098 | ||
1231 | // Update title after the global font height is updated | 1099 | // Update title after the global font height is updated |
1232 | container_update_title_textures(view->container); | 1100 | if (view->container->title_bar.title_text && len) { |
1101 | sway_text_node_set_text(view->container->title_bar.title_text, | ||
1102 | view->container->formatted_title); | ||
1103 | container_arrange_title_bar(view->container); | ||
1104 | } else { | ||
1105 | container_update_title_bar(view->container); | ||
1106 | } | ||
1233 | 1107 | ||
1234 | ipc_event_window(view->container, "title"); | 1108 | ipc_event_window(view->container, "title"); |
1235 | 1109 | ||
1236 | if (view->foreign_toplevel && title) { | 1110 | if (view->foreign_toplevel && title) { |
1237 | wlr_foreign_toplevel_handle_v1_set_title(view->foreign_toplevel, title); | 1111 | wlr_foreign_toplevel_handle_v1_set_title(view->foreign_toplevel, title); |
1238 | } | 1112 | } |
1113 | |||
1114 | if (view->ext_foreign_toplevel) { | ||
1115 | update_ext_foreign_toplevel(view); | ||
1116 | } | ||
1239 | } | 1117 | } |
1240 | 1118 | ||
1241 | bool view_is_visible(struct sway_view *view) { | 1119 | bool view_is_visible(struct sway_view *view) { |
1242 | if (view->container->node.destroying) { | 1120 | if (view->container->node.destroying) { |
1243 | return false; | 1121 | return false; |
1244 | } | 1122 | } |
1245 | struct sway_workspace *workspace = view->container->workspace; | 1123 | struct sway_workspace *workspace = view->container->pending.workspace; |
1246 | if (!workspace && view->container->fullscreen_mode != FULLSCREEN_GLOBAL) { | 1124 | if (!workspace && view->container->pending.fullscreen_mode != FULLSCREEN_GLOBAL) { |
1247 | bool fs_global_descendant = false; | 1125 | bool fs_global_descendant = false; |
1248 | struct sway_container *parent = view->container->parent; | 1126 | struct sway_container *parent = view->container->pending.parent; |
1249 | while (parent) { | 1127 | while (parent) { |
1250 | if (parent->fullscreen_mode == FULLSCREEN_GLOBAL) { | 1128 | if (parent->pending.fullscreen_mode == FULLSCREEN_GLOBAL) { |
1251 | fs_global_descendant = true; | 1129 | fs_global_descendant = true; |
1252 | } | 1130 | } |
1253 | parent = parent->parent; | 1131 | parent = parent->pending.parent; |
1254 | } | 1132 | } |
1255 | if (!fs_global_descendant) { | 1133 | if (!fs_global_descendant) { |
1256 | return false; | 1134 | return false; |
@@ -1268,13 +1146,13 @@ bool view_is_visible(struct sway_view *view) { | |||
1268 | enum sway_container_layout layout = container_parent_layout(con); | 1146 | enum sway_container_layout layout = container_parent_layout(con); |
1269 | if ((layout == L_TABBED || layout == L_STACKED) | 1147 | if ((layout == L_TABBED || layout == L_STACKED) |
1270 | && !container_is_floating(con)) { | 1148 | && !container_is_floating(con)) { |
1271 | struct sway_node *parent = con->parent ? | 1149 | struct sway_node *parent = con->pending.parent ? |
1272 | &con->parent->node : &con->workspace->node; | 1150 | &con->pending.parent->node : &con->pending.workspace->node; |
1273 | if (seat_get_active_tiling_child(seat, parent) != &con->node) { | 1151 | if (seat_get_active_tiling_child(seat, parent) != &con->node) { |
1274 | return false; | 1152 | return false; |
1275 | } | 1153 | } |
1276 | } | 1154 | } |
1277 | con = con->parent; | 1155 | con = con->pending.parent; |
1278 | } | 1156 | } |
1279 | // Check view isn't hidden by another fullscreen view | 1157 | // Check view isn't hidden by another fullscreen view |
1280 | struct sway_container *fs = root->fullscreen_global ? | 1158 | struct sway_container *fs = root->fullscreen_global ? |
@@ -1296,6 +1174,7 @@ void view_set_urgent(struct sway_view *view, bool enable) { | |||
1296 | return; | 1174 | return; |
1297 | } | 1175 | } |
1298 | clock_gettime(CLOCK_MONOTONIC, &view->urgent); | 1176 | clock_gettime(CLOCK_MONOTONIC, &view->urgent); |
1177 | container_update_itself_and_parents(view->container); | ||
1299 | } else { | 1178 | } else { |
1300 | view->urgent = (struct timespec){ 0 }; | 1179 | view->urgent = (struct timespec){ 0 }; |
1301 | if (view->urgent_timer) { | 1180 | if (view->urgent_timer) { |
@@ -1303,12 +1182,11 @@ void view_set_urgent(struct sway_view *view, bool enable) { | |||
1303 | view->urgent_timer = NULL; | 1182 | view->urgent_timer = NULL; |
1304 | } | 1183 | } |
1305 | } | 1184 | } |
1306 | container_damage_whole(view->container); | ||
1307 | 1185 | ||
1308 | ipc_event_window(view->container, "urgent"); | 1186 | ipc_event_window(view->container, "urgent"); |
1309 | 1187 | ||
1310 | if (!container_is_scratchpad_hidden(view->container)) { | 1188 | if (!container_is_scratchpad_hidden(view->container)) { |
1311 | workspace_detect_urgent(view->container->workspace); | 1189 | workspace_detect_urgent(view->container->pending.workspace); |
1312 | } | 1190 | } |
1313 | } | 1191 | } |
1314 | 1192 | ||
@@ -1317,40 +1195,54 @@ bool view_is_urgent(struct sway_view *view) { | |||
1317 | } | 1195 | } |
1318 | 1196 | ||
1319 | void view_remove_saved_buffer(struct sway_view *view) { | 1197 | void view_remove_saved_buffer(struct sway_view *view) { |
1320 | if (!sway_assert(!wl_list_empty(&view->saved_buffers), "Expected a saved buffer")) { | 1198 | if (!sway_assert(view->saved_surface_tree, "Expected a saved buffer")) { |
1321 | return; | 1199 | return; |
1322 | } | 1200 | } |
1323 | struct sway_saved_buffer *saved_buf, *tmp; | 1201 | |
1324 | wl_list_for_each_safe(saved_buf, tmp, &view->saved_buffers, link) { | 1202 | wlr_scene_node_destroy(&view->saved_surface_tree->node); |
1325 | wlr_buffer_unlock(&saved_buf->buffer->base); | 1203 | view->saved_surface_tree = NULL; |
1326 | wl_list_remove(&saved_buf->link); | 1204 | wlr_scene_node_set_enabled(&view->content_tree->node, true); |
1327 | free(saved_buf); | ||
1328 | } | ||
1329 | } | 1205 | } |
1330 | 1206 | ||
1331 | static void view_save_buffer_iterator(struct wlr_surface *surface, | 1207 | static void view_save_buffer_iterator(struct wlr_scene_buffer *buffer, |
1332 | int sx, int sy, void *data) { | 1208 | int sx, int sy, void *data) { |
1333 | struct sway_view *view = data; | 1209 | struct wlr_scene_tree *tree = data; |
1334 | 1210 | ||
1335 | if (surface && wlr_surface_has_buffer(surface)) { | 1211 | struct wlr_scene_buffer *sbuf = wlr_scene_buffer_create(tree, NULL); |
1336 | wlr_buffer_lock(&surface->buffer->base); | 1212 | if (!sbuf) { |
1337 | struct sway_saved_buffer *saved_buffer = calloc(1, sizeof(struct sway_saved_buffer)); | 1213 | sway_log(SWAY_ERROR, "Could not allocate a scene buffer when saving a surface"); |
1338 | saved_buffer->buffer = surface->buffer; | 1214 | return; |
1339 | saved_buffer->width = surface->current.width; | ||
1340 | saved_buffer->height = surface->current.height; | ||
1341 | saved_buffer->x = sx; | ||
1342 | saved_buffer->y = sy; | ||
1343 | saved_buffer->transform = surface->current.transform; | ||
1344 | wlr_surface_get_buffer_source_box(surface, &saved_buffer->source_box); | ||
1345 | wl_list_insert(&view->saved_buffers, &saved_buffer->link); | ||
1346 | } | 1215 | } |
1216 | |||
1217 | wlr_scene_buffer_set_dest_size(sbuf, | ||
1218 | buffer->dst_width, buffer->dst_height); | ||
1219 | wlr_scene_buffer_set_opaque_region(sbuf, &buffer->opaque_region); | ||
1220 | wlr_scene_buffer_set_source_box(sbuf, &buffer->src_box); | ||
1221 | wlr_scene_node_set_position(&sbuf->node, sx, sy); | ||
1222 | wlr_scene_buffer_set_transform(sbuf, buffer->transform); | ||
1223 | wlr_scene_buffer_set_buffer(sbuf, buffer->buffer); | ||
1347 | } | 1224 | } |
1348 | 1225 | ||
1349 | void view_save_buffer(struct sway_view *view) { | 1226 | void view_save_buffer(struct sway_view *view) { |
1350 | if (!sway_assert(wl_list_empty(&view->saved_buffers), "Didn't expect saved buffer")) { | 1227 | if (!sway_assert(!view->saved_surface_tree, "Didn't expect saved buffer")) { |
1351 | view_remove_saved_buffer(view); | 1228 | view_remove_saved_buffer(view); |
1352 | } | 1229 | } |
1353 | view_for_each_surface(view, view_save_buffer_iterator, view); | 1230 | |
1231 | view->saved_surface_tree = wlr_scene_tree_create(view->scene_tree); | ||
1232 | if (!view->saved_surface_tree) { | ||
1233 | sway_log(SWAY_ERROR, "Could not allocate a scene tree node when saving a surface"); | ||
1234 | return; | ||
1235 | } | ||
1236 | |||
1237 | // Enable and disable the saved surface tree like so to atomitaclly update | ||
1238 | // the tree. This will prevent over damaging or other weirdness. | ||
1239 | wlr_scene_node_set_enabled(&view->saved_surface_tree->node, false); | ||
1240 | |||
1241 | wlr_scene_node_for_each_buffer(&view->content_tree->node, | ||
1242 | view_save_buffer_iterator, view->saved_surface_tree); | ||
1243 | |||
1244 | wlr_scene_node_set_enabled(&view->content_tree->node, false); | ||
1245 | wlr_scene_node_set_enabled(&view->saved_surface_tree->node, true); | ||
1354 | } | 1246 | } |
1355 | 1247 | ||
1356 | bool view_is_transient_for(struct sway_view *child, | 1248 | bool view_is_transient_for(struct sway_view *child, |
@@ -1358,3 +1250,19 @@ bool view_is_transient_for(struct sway_view *child, | |||
1358 | return child->impl->is_transient_for && | 1250 | return child->impl->is_transient_for && |
1359 | child->impl->is_transient_for(child, ancestor); | 1251 | child->impl->is_transient_for(child, ancestor); |
1360 | } | 1252 | } |
1253 | |||
1254 | static void send_frame_done_iterator(struct wlr_scene_buffer *scene_buffer, | ||
1255 | int x, int y, void *data) { | ||
1256 | struct timespec *when = data; | ||
1257 | wl_signal_emit_mutable(&scene_buffer->events.frame_done, when); | ||
1258 | } | ||
1259 | |||
1260 | void view_send_frame_done(struct sway_view *view) { | ||
1261 | struct timespec when; | ||
1262 | clock_gettime(CLOCK_MONOTONIC, &when); | ||
1263 | |||
1264 | struct wlr_scene_node *node; | ||
1265 | wl_list_for_each(node, &view->content_tree->children, link) { | ||
1266 | wlr_scene_node_for_each_buffer(node, send_frame_done_iterator, &when); | ||
1267 | } | ||
1268 | } | ||
diff --git a/sway/tree/workspace.c b/sway/tree/workspace.c index 921b7d19..a68dc927 100644 --- a/sway/tree/workspace.c +++ b/sway/tree/workspace.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _POSIX_C_SOURCE 200809 | ||
2 | #include <ctype.h> | 1 | #include <ctype.h> |
3 | #include <limits.h> | 2 | #include <limits.h> |
4 | #include <stdbool.h> | 3 | #include <stdbool.h> |
@@ -48,14 +47,16 @@ struct sway_output *workspace_get_initial_output(const char *name) { | |||
48 | if (focus && focus->type == N_WORKSPACE) { | 47 | if (focus && focus->type == N_WORKSPACE) { |
49 | return focus->sway_workspace->output; | 48 | return focus->sway_workspace->output; |
50 | } else if (focus && focus->type == N_CONTAINER) { | 49 | } else if (focus && focus->type == N_CONTAINER) { |
51 | return focus->sway_container->workspace->output; | 50 | return focus->sway_container->pending.workspace->output; |
52 | } | 51 | } |
53 | // Fallback to the first output or noop output for headless | 52 | // Fallback to the first output or the headless output |
54 | return root->outputs->length ? root->outputs->items[0] : root->noop_output; | 53 | return root->outputs->length ? root->outputs->items[0] : root->fallback_output; |
55 | } | 54 | } |
56 | 55 | ||
57 | struct sway_workspace *workspace_create(struct sway_output *output, | 56 | struct sway_workspace *workspace_create(struct sway_output *output, |
58 | const char *name) { | 57 | const char *name) { |
58 | sway_assert(name, "NULL name given to workspace_create"); | ||
59 | |||
59 | if (output == NULL) { | 60 | if (output == NULL) { |
60 | output = workspace_get_initial_output(name); | 61 | output = workspace_get_initial_output(name); |
61 | } | 62 | } |
@@ -69,7 +70,19 @@ struct sway_workspace *workspace_create(struct sway_output *output, | |||
69 | return NULL; | 70 | return NULL; |
70 | } | 71 | } |
71 | node_init(&ws->node, N_WORKSPACE, ws); | 72 | node_init(&ws->node, N_WORKSPACE, ws); |
72 | ws->name = name ? strdup(name) : NULL; | 73 | |
74 | bool failed = false; | ||
75 | ws->layers.tiling = alloc_scene_tree(root->staging, &failed); | ||
76 | ws->layers.fullscreen = alloc_scene_tree(root->staging, &failed); | ||
77 | |||
78 | if (failed) { | ||
79 | wlr_scene_node_destroy(&ws->layers.tiling->node); | ||
80 | wlr_scene_node_destroy(&ws->layers.fullscreen->node); | ||
81 | free(ws); | ||
82 | return NULL; | ||
83 | } | ||
84 | |||
85 | ws->name = strdup(name); | ||
73 | ws->prev_split_layout = L_NONE; | 86 | ws->prev_split_layout = L_NONE; |
74 | ws->layout = output_get_default_layout(output); | 87 | ws->layout = output_get_default_layout(output); |
75 | ws->floating = create_list(); | 88 | ws->floating = create_list(); |
@@ -114,7 +127,7 @@ struct sway_workspace *workspace_create(struct sway_output *output, | |||
114 | output_sort_workspaces(output); | 127 | output_sort_workspaces(output); |
115 | 128 | ||
116 | ipc_event_workspace(NULL, ws, "init"); | 129 | ipc_event_workspace(NULL, ws, "init"); |
117 | wl_signal_emit(&root->events.new_node, &ws->node); | 130 | wl_signal_emit_mutable(&root->events.new_node, &ws->node); |
118 | 131 | ||
119 | return ws; | 132 | return ws; |
120 | } | 133 | } |
@@ -129,6 +142,11 @@ void workspace_destroy(struct sway_workspace *workspace) { | |||
129 | return; | 142 | return; |
130 | } | 143 | } |
131 | 144 | ||
145 | scene_node_disown_children(workspace->layers.tiling); | ||
146 | scene_node_disown_children(workspace->layers.fullscreen); | ||
147 | wlr_scene_node_destroy(&workspace->layers.tiling->node); | ||
148 | wlr_scene_node_destroy(&workspace->layers.fullscreen->node); | ||
149 | |||
132 | free(workspace->name); | 150 | free(workspace->name); |
133 | free(workspace->representation); | 151 | free(workspace->representation); |
134 | list_free_items_and_destroy(workspace->output_priority); | 152 | list_free_items_and_destroy(workspace->output_priority); |
@@ -142,7 +160,7 @@ void workspace_destroy(struct sway_workspace *workspace) { | |||
142 | void workspace_begin_destroy(struct sway_workspace *workspace) { | 160 | void workspace_begin_destroy(struct sway_workspace *workspace) { |
143 | sway_log(SWAY_DEBUG, "Destroying workspace '%s'", workspace->name); | 161 | sway_log(SWAY_DEBUG, "Destroying workspace '%s'", workspace->name); |
144 | ipc_event_workspace(NULL, workspace, "empty"); // intentional | 162 | ipc_event_workspace(NULL, workspace, "empty"); // intentional |
145 | wl_signal_emit(&workspace->node.events.destroy, &workspace->node); | 163 | wl_signal_emit_mutable(&workspace->node.events.destroy, &workspace->node); |
146 | 164 | ||
147 | if (workspace->output) { | 165 | if (workspace->output) { |
148 | workspace_detach(workspace); | 166 | workspace_detach(workspace); |
@@ -174,22 +192,16 @@ void workspace_consider_destroy(struct sway_workspace *ws) { | |||
174 | static bool workspace_valid_on_output(const char *output_name, | 192 | static bool workspace_valid_on_output(const char *output_name, |
175 | const char *ws_name) { | 193 | const char *ws_name) { |
176 | struct workspace_config *wsc = workspace_find_config(ws_name); | 194 | struct workspace_config *wsc = workspace_find_config(ws_name); |
177 | char identifier[128]; | ||
178 | struct sway_output *output = output_by_name_or_id(output_name); | 195 | struct sway_output *output = output_by_name_or_id(output_name); |
179 | if (!output) { | 196 | if (!output) { |
180 | return false; | 197 | return false; |
181 | } | 198 | } |
182 | output_name = output->wlr_output->name; | ||
183 | output_get_identifier(identifier, sizeof(identifier), output); | ||
184 | |||
185 | if (!wsc) { | 199 | if (!wsc) { |
186 | return true; | 200 | return true; |
187 | } | 201 | } |
188 | 202 | ||
189 | for (int i = 0; i < wsc->outputs->length; i++) { | 203 | for (int i = 0; i < wsc->outputs->length; i++) { |
190 | if (strcmp(wsc->outputs->items[i], "*") == 0 || | 204 | if (output_match_name_or_id(output, wsc->outputs->items[i])) { |
191 | strcmp(wsc->outputs->items[i], output_name) == 0 || | ||
192 | strcmp(wsc->outputs->items[i], identifier) == 0) { | ||
193 | return true; | 205 | return true; |
194 | } | 206 | } |
195 | } | 207 | } |
@@ -222,10 +234,8 @@ static void workspace_name_from_binding(const struct sway_binding * binding, | |||
222 | // not a command about workspaces | 234 | // not a command about workspaces |
223 | if (strcmp(_target, "next") == 0 || | 235 | if (strcmp(_target, "next") == 0 || |
224 | strcmp(_target, "prev") == 0 || | 236 | strcmp(_target, "prev") == 0 || |
225 | strncmp(_target, "next_on_output", | 237 | strcmp(_target, "next_on_output") == 0 || |
226 | strlen("next_on_output")) == 0 || | 238 | strcmp(_target, "prev_on_output") == 0 || |
227 | strncmp(_target, "prev_on_output", | ||
228 | strlen("next_on_output")) == 0 || | ||
229 | strcmp(_target, "number") == 0 || | 239 | strcmp(_target, "number") == 0 || |
230 | strcmp(_target, "back_and_forth") == 0 || | 240 | strcmp(_target, "back_and_forth") == 0 || |
231 | strcmp(_target, "current") == 0) { | 241 | strcmp(_target, "current") == 0) { |
@@ -286,13 +296,10 @@ char *workspace_next_name(const char *output_name) { | |||
286 | // assignments primarily, falling back to bindings and numbers. | 296 | // assignments primarily, falling back to bindings and numbers. |
287 | struct sway_mode *mode = config->current_mode; | 297 | struct sway_mode *mode = config->current_mode; |
288 | 298 | ||
289 | char identifier[128]; | ||
290 | struct sway_output *output = output_by_name_or_id(output_name); | 299 | struct sway_output *output = output_by_name_or_id(output_name); |
291 | if (!output) { | 300 | if (!output) { |
292 | return NULL; | 301 | return NULL; |
293 | } | 302 | } |
294 | output_name = output->wlr_output->name; | ||
295 | output_get_identifier(identifier, sizeof(identifier), output); | ||
296 | 303 | ||
297 | int order = INT_MAX; | 304 | int order = INT_MAX; |
298 | char *target = NULL; | 305 | char *target = NULL; |
@@ -312,9 +319,7 @@ char *workspace_next_name(const char *output_name) { | |||
312 | } | 319 | } |
313 | bool found = false; | 320 | bool found = false; |
314 | for (int j = 0; j < wsc->outputs->length; ++j) { | 321 | for (int j = 0; j < wsc->outputs->length; ++j) { |
315 | if (strcmp(wsc->outputs->items[j], "*") == 0 || | 322 | if (output_match_name_or_id(output, wsc->outputs->items[j])) { |
316 | strcmp(wsc->outputs->items[j], output_name) == 0 || | ||
317 | strcmp(wsc->outputs->items[j], identifier) == 0) { | ||
318 | found = true; | 323 | found = true; |
319 | free(target); | 324 | free(target); |
320 | target = strdup(wsc->workspace); | 325 | target = strdup(wsc->workspace); |
@@ -363,11 +368,11 @@ struct sway_workspace *workspace_by_name(const char *name) { | |||
363 | if (current && strcmp(name, "prev") == 0) { | 368 | if (current && strcmp(name, "prev") == 0) { |
364 | return workspace_prev(current); | 369 | return workspace_prev(current); |
365 | } else if (current && strcmp(name, "prev_on_output") == 0) { | 370 | } else if (current && strcmp(name, "prev_on_output") == 0) { |
366 | return workspace_output_prev(current, false); | 371 | return workspace_output_prev(current); |
367 | } else if (current && strcmp(name, "next") == 0) { | 372 | } else if (current && strcmp(name, "next") == 0) { |
368 | return workspace_next(current); | 373 | return workspace_next(current); |
369 | } else if (current && strcmp(name, "next_on_output") == 0) { | 374 | } else if (current && strcmp(name, "next_on_output") == 0) { |
370 | return workspace_output_next(current, false); | 375 | return workspace_output_next(current); |
371 | } else if (strcmp(name, "current") == 0) { | 376 | } else if (strcmp(name, "current") == 0) { |
372 | return current; | 377 | return current; |
373 | } else if (strcasecmp(name, "back_and_forth") == 0) { | 378 | } else if (strcasecmp(name, "back_and_forth") == 0) { |
@@ -530,7 +535,7 @@ struct sway_workspace *workspace_next(struct sway_workspace *workspace) { | |||
530 | * otherwise the next one is returned. | 535 | * otherwise the next one is returned. |
531 | */ | 536 | */ |
532 | static struct sway_workspace *workspace_output_prev_next_impl( | 537 | static struct sway_workspace *workspace_output_prev_next_impl( |
533 | struct sway_output *output, int dir, bool create) { | 538 | struct sway_output *output, int dir) { |
534 | struct sway_seat *seat = input_manager_current_seat(); | 539 | struct sway_seat *seat = input_manager_current_seat(); |
535 | struct sway_workspace *workspace = seat_get_focused_workspace(seat); | 540 | struct sway_workspace *workspace = seat_get_focused_workspace(seat); |
536 | if (!workspace) { | 541 | if (!workspace) { |
@@ -540,46 +545,43 @@ static struct sway_workspace *workspace_output_prev_next_impl( | |||
540 | } | 545 | } |
541 | 546 | ||
542 | int index = list_find(output->workspaces, workspace); | 547 | 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); | 548 | size_t new_index = wrap(index + dir, output->workspaces->length); |
551 | return output->workspaces->items[new_index]; | 549 | return output->workspaces->items[new_index]; |
552 | } | 550 | } |
553 | 551 | ||
554 | struct sway_workspace *workspace_output_next( | 552 | |
555 | struct sway_workspace *current, bool create) { | 553 | struct sway_workspace *workspace_output_next(struct sway_workspace *current) { |
556 | return workspace_output_prev_next_impl(current->output, 1, create); | 554 | return workspace_output_prev_next_impl(current->output, 1); |
557 | } | 555 | } |
558 | 556 | ||
559 | struct sway_workspace *workspace_output_prev( | 557 | struct sway_workspace *workspace_output_prev(struct sway_workspace *current) { |
560 | struct sway_workspace *current, bool create) { | 558 | return workspace_output_prev_next_impl(current->output, -1); |
561 | return workspace_output_prev_next_impl(current->output, -1, create); | ||
562 | } | 559 | } |
563 | 560 | ||
564 | bool workspace_switch(struct sway_workspace *workspace, | 561 | struct sway_workspace *workspace_auto_back_and_forth( |
565 | bool no_auto_back_and_forth) { | 562 | struct sway_workspace *workspace) { |
566 | struct sway_seat *seat = input_manager_current_seat(); | 563 | struct sway_seat *seat = input_manager_current_seat(); |
567 | struct sway_workspace *active_ws = NULL; | 564 | struct sway_workspace *active_ws = NULL; |
568 | struct sway_node *focus = seat_get_focus_inactive(seat, &root->node); | 565 | struct sway_node *focus = seat_get_focus_inactive(seat, &root->node); |
569 | if (focus && focus->type == N_WORKSPACE) { | 566 | if (focus && focus->type == N_WORKSPACE) { |
570 | active_ws = focus->sway_workspace; | 567 | active_ws = focus->sway_workspace; |
571 | } else if (focus && focus->type == N_CONTAINER) { | 568 | } else if (focus && focus->type == N_CONTAINER) { |
572 | active_ws = focus->sway_container->workspace; | 569 | active_ws = focus->sway_container->pending.workspace; |
573 | } | 570 | } |
574 | 571 | ||
575 | if (!no_auto_back_and_forth && config->auto_back_and_forth && active_ws | 572 | if (config->auto_back_and_forth && active_ws && active_ws == workspace && |
576 | && active_ws == workspace && seat->prev_workspace_name) { | 573 | seat->prev_workspace_name) { |
577 | struct sway_workspace *new_ws = | 574 | struct sway_workspace *new_ws = |
578 | workspace_by_name(seat->prev_workspace_name); | 575 | workspace_by_name(seat->prev_workspace_name); |
579 | workspace = new_ws ? | 576 | workspace = new_ws ? |
580 | new_ws : | 577 | new_ws : |
581 | workspace_create(NULL, seat->prev_workspace_name); | 578 | workspace_create(NULL, seat->prev_workspace_name); |
582 | } | 579 | } |
580 | return workspace; | ||
581 | } | ||
582 | |||
583 | bool workspace_switch(struct sway_workspace *workspace) { | ||
584 | struct sway_seat *seat = input_manager_current_seat(); | ||
583 | 585 | ||
584 | sway_log(SWAY_DEBUG, "Switching to workspace %p:%s", | 586 | sway_log(SWAY_DEBUG, "Switching to workspace %p:%s", |
585 | workspace, workspace->name); | 587 | workspace, workspace->name); |
@@ -657,15 +659,9 @@ void workspace_output_add_priority(struct sway_workspace *workspace, | |||
657 | 659 | ||
658 | struct sway_output *workspace_output_get_highest_available( | 660 | struct sway_output *workspace_output_get_highest_available( |
659 | struct sway_workspace *ws, struct sway_output *exclude) { | 661 | struct sway_workspace *ws, struct sway_output *exclude) { |
660 | char exclude_id[128] = {'\0'}; | ||
661 | if (exclude) { | ||
662 | output_get_identifier(exclude_id, sizeof(exclude_id), exclude); | ||
663 | } | ||
664 | |||
665 | for (int i = 0; i < ws->output_priority->length; i++) { | 662 | for (int i = 0; i < ws->output_priority->length; i++) { |
666 | char *name = ws->output_priority->items[i]; | 663 | const char *name = ws->output_priority->items[i]; |
667 | if (exclude && (strcmp(name, exclude->wlr_output->name) == 0 | 664 | if (exclude && output_match_name_or_id(exclude, name)) { |
668 | || strcmp(name, exclude_id) == 0)) { | ||
669 | continue; | 665 | continue; |
670 | } | 666 | } |
671 | 667 | ||
@@ -689,7 +685,6 @@ void workspace_detect_urgent(struct sway_workspace *workspace) { | |||
689 | if (workspace->urgent != new_urgent) { | 685 | if (workspace->urgent != new_urgent) { |
690 | workspace->urgent = new_urgent; | 686 | workspace->urgent = new_urgent; |
691 | ipc_event_workspace(NULL, workspace, "urgent"); | 687 | ipc_event_workspace(NULL, workspace, "urgent"); |
692 | output_damage_whole(workspace->output); | ||
693 | } | 688 | } |
694 | } | 689 | } |
695 | 690 | ||
@@ -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..b7c80dd4 --- /dev/null +++ b/sway/xdg_activation_v1.c | |||
@@ -0,0 +1,65 @@ | |||
1 | #include <wlr/types/wlr_xdg_activation_v1.h> | ||
2 | #include <wlr/types/wlr_xdg_shell.h> | ||
3 | #include "sway/desktop/launcher.h" | ||
4 | #include "sway/tree/view.h" | ||
5 | #include "sway/tree/workspace.h" | ||
6 | |||
7 | void xdg_activation_v1_handle_request_activate(struct wl_listener *listener, | ||
8 | void *data) { | ||
9 | const struct wlr_xdg_activation_v1_request_activate_event *event = data; | ||
10 | |||
11 | struct wlr_xdg_surface *xdg_surface = | ||
12 | wlr_xdg_surface_try_from_wlr_surface(event->surface); | ||
13 | if (xdg_surface == NULL) { | ||
14 | return; | ||
15 | } | ||
16 | struct sway_view *view = xdg_surface->data; | ||
17 | if (view == NULL) { | ||
18 | return; | ||
19 | } | ||
20 | |||
21 | struct launcher_ctx *ctx = event->token->data; | ||
22 | if (ctx == NULL) { | ||
23 | return; | ||
24 | } | ||
25 | |||
26 | if (!xdg_surface->surface->mapped) { | ||
27 | // This is a startup notification. If we are tracking it, the data | ||
28 | // field is a launcher_ctx. | ||
29 | if (ctx->activated) { | ||
30 | // This ctx has already been activated and cannot be used again | ||
31 | // for a startup notification. It will be destroyed | ||
32 | return; | ||
33 | } else { | ||
34 | ctx->activated = true; | ||
35 | view_assign_ctx(view, ctx); | ||
36 | } | ||
37 | return; | ||
38 | } | ||
39 | |||
40 | // This is an activation request. If this context is internal we have ctx->seat. | ||
41 | struct sway_seat *seat = ctx->seat; | ||
42 | if (!seat) { | ||
43 | // Otherwise, use the seat indicated by the launcher client in set_serial | ||
44 | seat = ctx->token->seat ? ctx->token->seat->data : NULL; | ||
45 | } | ||
46 | |||
47 | if (seat && ctx->had_focused_surface) { | ||
48 | view_request_activate(view, seat); | ||
49 | } else { | ||
50 | // The token is valid, but cannot be used to activate a window | ||
51 | view_request_urgent(view); | ||
52 | } | ||
53 | } | ||
54 | |||
55 | void xdg_activation_v1_handle_new_token(struct wl_listener *listener, void *data) { | ||
56 | struct wlr_xdg_activation_token_v1 *token = data; | ||
57 | struct sway_seat *seat = token->seat ? token->seat->data : | ||
58 | input_manager_current_seat(); | ||
59 | |||
60 | struct sway_workspace *ws = seat_get_focused_workspace(seat); | ||
61 | if (ws) { | ||
62 | launcher_ctx_create(token, &ws->node); | ||
63 | return; | ||
64 | } | ||
65 | } | ||
diff --git a/sway/xdg_decoration.c b/sway/xdg_decoration.c index e7c3ea73..fa8c6279 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,13 +23,12 @@ 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 | wlr_xdg_toplevel_decoration_v1_set_mode(deco->wlr_xdg_decoration, | 26 | set_xdg_decoration_mode(deco); |
27 | WLR_XDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE); | ||
28 | } | 27 | } |
29 | 28 | ||
30 | void handle_xdg_decoration(struct wl_listener *listener, void *data) { | 29 | void handle_xdg_decoration(struct wl_listener *listener, void *data) { |
31 | struct wlr_xdg_toplevel_decoration_v1 *wlr_deco = data; | 30 | struct wlr_xdg_toplevel_decoration_v1 *wlr_deco = data; |
32 | struct sway_xdg_shell_view *xdg_shell_view = wlr_deco->surface->data; | 31 | struct sway_xdg_shell_view *xdg_shell_view = wlr_deco->toplevel->base->data; |
33 | 32 | ||
34 | struct sway_xdg_decoration *deco = calloc(1, sizeof(*deco)); | 33 | struct sway_xdg_decoration *deco = calloc(1, sizeof(*deco)); |
35 | if (deco == NULL) { | 34 | if (deco == NULL) { |
@@ -48,16 +47,46 @@ void handle_xdg_decoration(struct wl_listener *listener, void *data) { | |||
48 | 47 | ||
49 | wl_list_insert(&server.xdg_decorations, &deco->link); | 48 | wl_list_insert(&server.xdg_decorations, &deco->link); |
50 | 49 | ||
51 | xdg_decoration_handle_request_mode(&deco->request_mode, wlr_deco); | 50 | set_xdg_decoration_mode(deco); |
52 | } | 51 | } |
53 | 52 | ||
54 | struct sway_xdg_decoration *xdg_decoration_from_surface( | 53 | struct sway_xdg_decoration *xdg_decoration_from_surface( |
55 | struct wlr_surface *surface) { | 54 | struct wlr_surface *surface) { |
56 | struct sway_xdg_decoration *deco; | 55 | struct sway_xdg_decoration *deco; |
57 | wl_list_for_each(deco, &server.xdg_decorations, link) { | 56 | wl_list_for_each(deco, &server.xdg_decorations, link) { |
58 | if (deco->wlr_xdg_decoration->surface->surface == surface) { | 57 | if (deco->wlr_xdg_decoration->toplevel->base->surface == surface) { |
59 | return deco; | 58 | return deco; |
60 | } | 59 | } |
61 | } | 60 | } |
62 | return NULL; | 61 | return NULL; |
63 | } | 62 | } |
63 | |||
64 | void set_xdg_decoration_mode(struct sway_xdg_decoration *deco) { | ||
65 | struct sway_view *view = deco->view; | ||
66 | enum wlr_xdg_toplevel_decoration_v1_mode mode = | ||
67 | WLR_XDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE; | ||
68 | enum wlr_xdg_toplevel_decoration_v1_mode client_mode = | ||
69 | deco->wlr_xdg_decoration->requested_mode; | ||
70 | |||
71 | bool floating; | ||
72 | if (view->container) { | ||
73 | floating = container_is_floating(view->container); | ||
74 | bool csd = false; | ||
75 | csd = client_mode == | ||
76 | WLR_XDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE; | ||
77 | view_update_csd_from_client(view, csd); | ||
78 | arrange_container(view->container); | ||
79 | transaction_commit_dirty(); | ||
80 | } else { | ||
81 | floating = view->impl->wants_floating && | ||
82 | view->impl->wants_floating(view); | ||
83 | } | ||
84 | |||
85 | if (floating && client_mode) { | ||
86 | mode = client_mode; | ||
87 | } | ||
88 | |||
89 | if (view->wlr_xdg_toplevel->base->initialized) { | ||
90 | wlr_xdg_toplevel_decoration_v1_set_mode(deco->wlr_xdg_decoration, mode); | ||
91 | } | ||
92 | } | ||