diff options
author | Drew DeVault <sir@cmpwn.com> | 2018-07-23 20:27:56 -0400 |
---|---|---|
committer | Drew DeVault <sir@cmpwn.com> | 2018-07-23 20:31:11 -0400 |
commit | f4b882475eee7a81c206c7825616cc4656b2f60b (patch) | |
tree | 38e6ebf81b235424f105dcbcbb194e5e9eac70c0 /sway | |
parent | Implement pid->workspace tracking (diff) | |
parent | Merge pull request #2342 from RyanDwyer/update-cursor (diff) | |
download | sway-f4b882475eee7a81c206c7825616cc4656b2f60b.tar.gz sway-f4b882475eee7a81c206c7825616cc4656b2f60b.tar.zst sway-f4b882475eee7a81c206c7825616cc4656b2f60b.zip |
Merge branch 'master' into pid-workspaces
Diffstat (limited to 'sway')
106 files changed, 4208 insertions, 1752 deletions
diff --git a/sway/commands.c b/sway/commands.c index 5b20857a..fdae1961 100644 --- a/sway/commands.c +++ b/sway/commands.c | |||
@@ -98,12 +98,18 @@ static struct cmd_handler handlers[] = { | |||
98 | { "client.unfocused", cmd_client_unfocused }, | 98 | { "client.unfocused", cmd_client_unfocused }, |
99 | { "client.urgent", cmd_client_urgent }, | 99 | { "client.urgent", cmd_client_urgent }, |
100 | { "default_border", cmd_default_border }, | 100 | { "default_border", cmd_default_border }, |
101 | { "default_floating_border", cmd_default_floating_border }, | ||
101 | { "exec", cmd_exec }, | 102 | { "exec", cmd_exec }, |
102 | { "exec_always", cmd_exec_always }, | 103 | { "exec_always", cmd_exec_always }, |
104 | { "floating_maximum_size", cmd_floating_maximum_size }, | ||
105 | { "floating_minimum_size", cmd_floating_minimum_size }, | ||
106 | { "floating_modifier", cmd_floating_modifier }, | ||
107 | { "focus", cmd_focus }, | ||
103 | { "focus_follows_mouse", cmd_focus_follows_mouse }, | 108 | { "focus_follows_mouse", cmd_focus_follows_mouse }, |
104 | { "focus_wrapping", cmd_focus_wrapping }, | 109 | { "focus_wrapping", cmd_focus_wrapping }, |
105 | { "font", cmd_font }, | 110 | { "font", cmd_font }, |
106 | { "for_window", cmd_for_window }, | 111 | { "for_window", cmd_for_window }, |
112 | { "force_display_urgency_hint", cmd_force_display_urgency_hint }, | ||
107 | { "force_focus_wrapping", cmd_force_focus_wrapping }, | 113 | { "force_focus_wrapping", cmd_force_focus_wrapping }, |
108 | { "fullscreen", cmd_fullscreen }, | 114 | { "fullscreen", cmd_fullscreen }, |
109 | { "gaps", cmd_gaps }, | 115 | { "gaps", cmd_gaps }, |
@@ -112,6 +118,7 @@ static struct cmd_handler handlers[] = { | |||
112 | { "input", cmd_input }, | 118 | { "input", cmd_input }, |
113 | { "mode", cmd_mode }, | 119 | { "mode", cmd_mode }, |
114 | { "mouse_warping", cmd_mouse_warping }, | 120 | { "mouse_warping", cmd_mouse_warping }, |
121 | { "no_focus", cmd_no_focus }, | ||
115 | { "output", cmd_output }, | 122 | { "output", cmd_output }, |
116 | { "seat", cmd_seat }, | 123 | { "seat", cmd_seat }, |
117 | { "set", cmd_set }, | 124 | { "set", cmd_set }, |
@@ -133,7 +140,6 @@ static struct cmd_handler command_handlers[] = { | |||
133 | { "border", cmd_border }, | 140 | { "border", cmd_border }, |
134 | { "exit", cmd_exit }, | 141 | { "exit", cmd_exit }, |
135 | { "floating", cmd_floating }, | 142 | { "floating", cmd_floating }, |
136 | { "focus", cmd_focus }, | ||
137 | { "fullscreen", cmd_fullscreen }, | 143 | { "fullscreen", cmd_fullscreen }, |
138 | { "kill", cmd_kill }, | 144 | { "kill", cmd_kill }, |
139 | { "layout", cmd_layout }, | 145 | { "layout", cmd_layout }, |
@@ -143,6 +149,7 @@ static struct cmd_handler command_handlers[] = { | |||
143 | { "reload", cmd_reload }, | 149 | { "reload", cmd_reload }, |
144 | { "rename", cmd_rename }, | 150 | { "rename", cmd_rename }, |
145 | { "resize", cmd_resize }, | 151 | { "resize", cmd_resize }, |
152 | { "scratchpad", cmd_scratchpad }, | ||
146 | { "split", cmd_split }, | 153 | { "split", cmd_split }, |
147 | { "splith", cmd_splith }, | 154 | { "splith", cmd_splith }, |
148 | { "splitt", cmd_splitt }, | 155 | { "splitt", cmd_splitt }, |
@@ -151,6 +158,7 @@ static struct cmd_handler command_handlers[] = { | |||
151 | { "swap", cmd_swap }, | 158 | { "swap", cmd_swap }, |
152 | { "title_format", cmd_title_format }, | 159 | { "title_format", cmd_title_format }, |
153 | { "unmark", cmd_unmark }, | 160 | { "unmark", cmd_unmark }, |
161 | { "urgent", cmd_urgent }, | ||
154 | }; | 162 | }; |
155 | 163 | ||
156 | static int handler_compare(const void *_a, const void *_b) { | 164 | static int handler_compare(const void *_a, const void *_b) { |
@@ -163,7 +171,7 @@ struct cmd_handler *find_handler(char *line, struct cmd_handler *cmd_handlers, | |||
163 | int handlers_size) { | 171 | int handlers_size) { |
164 | struct cmd_handler d = { .command=line }; | 172 | struct cmd_handler d = { .command=line }; |
165 | struct cmd_handler *res = NULL; | 173 | struct cmd_handler *res = NULL; |
166 | wlr_log(L_DEBUG, "find_handler(%s)", line); | 174 | wlr_log(WLR_DEBUG, "find_handler(%s)", line); |
167 | 175 | ||
168 | bool config_loading = config->reading || !config->active; | 176 | bool config_loading = config->reading || !config->active; |
169 | 177 | ||
@@ -248,10 +256,10 @@ struct cmd_results *execute_command(char *_exec, struct sway_seat *seat) { | |||
248 | cmd = argsep(&cmdlist, ","); | 256 | cmd = argsep(&cmdlist, ","); |
249 | cmd += strspn(cmd, whitespace); | 257 | cmd += strspn(cmd, whitespace); |
250 | if (strcmp(cmd, "") == 0) { | 258 | if (strcmp(cmd, "") == 0) { |
251 | wlr_log(L_INFO, "Ignoring empty command."); | 259 | wlr_log(WLR_INFO, "Ignoring empty command."); |
252 | continue; | 260 | continue; |
253 | } | 261 | } |
254 | wlr_log(L_INFO, "Handling command '%s'", cmd); | 262 | wlr_log(WLR_INFO, "Handling command '%s'", cmd); |
255 | //TODO better handling of argv | 263 | //TODO better handling of argv |
256 | int argc; | 264 | int argc; |
257 | char **argv = split_args(cmd, &argc); | 265 | char **argv = split_args(cmd, &argc); |
@@ -319,7 +327,7 @@ struct cmd_results *execute_command(char *_exec, struct sway_seat *seat) { | |||
319 | } while(head); | 327 | } while(head); |
320 | cleanup: | 328 | cleanup: |
321 | free(exec); | 329 | free(exec); |
322 | free(views); | 330 | list_free(views); |
323 | if (!results) { | 331 | if (!results) { |
324 | results = cmd_results_new(CMD_SUCCESS, NULL, NULL); | 332 | results = cmd_results_new(CMD_SUCCESS, NULL, NULL); |
325 | } | 333 | } |
@@ -344,7 +352,7 @@ struct cmd_results *config_command(char *exec) { | |||
344 | 352 | ||
345 | // Start block | 353 | // Start block |
346 | if (argc > 1 && strcmp(argv[argc - 1], "{") == 0) { | 354 | if (argc > 1 && strcmp(argv[argc - 1], "{") == 0) { |
347 | char *block = join_args(argv, argc - 1); | 355 | char *block = join_args(argv, argc - 1); |
348 | results = cmd_results_new(CMD_BLOCK, block, NULL); | 356 | results = cmd_results_new(CMD_BLOCK, block, NULL); |
349 | free(block); | 357 | free(block); |
350 | goto cleanup; | 358 | goto cleanup; |
@@ -355,7 +363,7 @@ struct cmd_results *config_command(char *exec) { | |||
355 | results = cmd_results_new(CMD_BLOCK_END, NULL, NULL); | 363 | results = cmd_results_new(CMD_BLOCK_END, NULL, NULL); |
356 | goto cleanup; | 364 | goto cleanup; |
357 | } | 365 | } |
358 | wlr_log(L_INFO, "handling config command '%s'", exec); | 366 | wlr_log(WLR_INFO, "handling config command '%s'", exec); |
359 | struct cmd_handler *handler = find_handler(argv[0], NULL, 0); | 367 | struct cmd_handler *handler = find_handler(argv[0], NULL, 0); |
360 | if (!handler) { | 368 | if (!handler) { |
361 | char *input = argv[0] ? argv[0] : "(empty)"; | 369 | char *input = argv[0] ? argv[0] : "(empty)"; |
@@ -388,7 +396,7 @@ cleanup: | |||
388 | struct cmd_results *config_subcommand(char **argv, int argc, | 396 | struct cmd_results *config_subcommand(char **argv, int argc, |
389 | struct cmd_handler *handlers, size_t handlers_size) { | 397 | struct cmd_handler *handlers, size_t handlers_size) { |
390 | char *command = join_args(argv, argc); | 398 | char *command = join_args(argv, argc); |
391 | wlr_log(L_DEBUG, "Subcommand: %s", command); | 399 | wlr_log(WLR_DEBUG, "Subcommand: %s", command); |
392 | free(command); | 400 | free(command); |
393 | 401 | ||
394 | struct cmd_handler *handler = find_handler(argv[0], handlers, | 402 | struct cmd_handler *handler = find_handler(argv[0], handlers, |
@@ -428,8 +436,7 @@ struct cmd_results *config_commands_command(char *exec) { | |||
428 | 436 | ||
429 | struct cmd_handler *handler = find_handler(cmd, NULL, 0); | 437 | struct cmd_handler *handler = find_handler(cmd, NULL, 0); |
430 | if (!handler && strcmp(cmd, "*") != 0) { | 438 | if (!handler && strcmp(cmd, "*") != 0) { |
431 | char *input = cmd ? cmd : "(empty)"; | 439 | results = cmd_results_new(CMD_INVALID, cmd, "Unknown/invalid command"); |
432 | results = cmd_results_new(CMD_INVALID, input, "Unknown/invalid command"); | ||
433 | goto cleanup; | 440 | goto cleanup; |
434 | } | 441 | } |
435 | 442 | ||
@@ -471,14 +478,16 @@ struct cmd_results *config_commands_command(char *exec) { | |||
471 | } | 478 | } |
472 | if (!policy) { | 479 | if (!policy) { |
473 | policy = alloc_command_policy(cmd); | 480 | policy = alloc_command_policy(cmd); |
474 | sway_assert(policy, "Unable to allocate security policy"); | 481 | if (!sway_assert(policy, "Unable to allocate security policy")) { |
475 | if (policy) { | 482 | results = cmd_results_new(CMD_INVALID, cmd, |
476 | list_add(config->command_policies, policy); | 483 | "Unable to allocate memory"); |
484 | goto cleanup; | ||
477 | } | 485 | } |
486 | list_add(config->command_policies, policy); | ||
478 | } | 487 | } |
479 | policy->context = context; | 488 | policy->context = context; |
480 | 489 | ||
481 | wlr_log(L_INFO, "Set command policy for %s to %d", | 490 | wlr_log(WLR_INFO, "Set command policy for %s to %d", |
482 | policy->command, policy->context); | 491 | policy->command, policy->context); |
483 | 492 | ||
484 | results = cmd_results_new(CMD_SUCCESS, NULL, NULL); | 493 | results = cmd_results_new(CMD_SUCCESS, NULL, NULL); |
@@ -492,7 +501,7 @@ struct cmd_results *cmd_results_new(enum cmd_status status, | |||
492 | const char *input, const char *format, ...) { | 501 | const char *input, const char *format, ...) { |
493 | struct cmd_results *results = malloc(sizeof(struct cmd_results)); | 502 | struct cmd_results *results = malloc(sizeof(struct cmd_results)); |
494 | if (!results) { | 503 | if (!results) { |
495 | wlr_log(L_ERROR, "Unable to allocate command results"); | 504 | wlr_log(WLR_ERROR, "Unable to allocate command results"); |
496 | return NULL; | 505 | return NULL; |
497 | } | 506 | } |
498 | results->status = status; | 507 | results->status = status; |
@@ -526,7 +535,7 @@ void free_cmd_results(struct cmd_results *results) { | |||
526 | free(results); | 535 | free(results); |
527 | } | 536 | } |
528 | 537 | ||
529 | const char *cmd_results_to_json(struct cmd_results *results) { | 538 | char *cmd_results_to_json(struct cmd_results *results) { |
530 | json_object *result_array = json_object_new_array(); | 539 | json_object *result_array = json_object_new_array(); |
531 | json_object *root = json_object_new_object(); | 540 | json_object *root = json_object_new_object(); |
532 | json_object_object_add(root, "success", | 541 | json_object_object_add(root, "success", |
@@ -541,9 +550,9 @@ const char *cmd_results_to_json(struct cmd_results *results) { | |||
541 | } | 550 | } |
542 | json_object_array_add(result_array, root); | 551 | json_object_array_add(result_array, root); |
543 | const char *json = json_object_to_json_string(result_array); | 552 | const char *json = json_object_to_json_string(result_array); |
544 | free(result_array); | 553 | char *res = strdup(json); |
545 | free(root); | 554 | json_object_put(result_array); |
546 | return json; | 555 | return res; |
547 | } | 556 | } |
548 | 557 | ||
549 | /** | 558 | /** |
diff --git a/sway/commands/assign.c b/sway/commands/assign.c index 9d15e166..0bc0929a 100644 --- a/sway/commands/assign.c +++ b/sway/commands/assign.c | |||
@@ -27,6 +27,7 @@ struct cmd_results *cmd_assign(int argc, char **argv) { | |||
27 | 27 | ||
28 | if (strncmp(*argv, "→", strlen("→")) == 0) { | 28 | if (strncmp(*argv, "→", strlen("→")) == 0) { |
29 | if (argc < 3) { | 29 | if (argc < 3) { |
30 | free(criteria); | ||
30 | return cmd_results_new(CMD_INVALID, "assign", "Missing workspace"); | 31 | return cmd_results_new(CMD_INVALID, "assign", "Missing workspace"); |
31 | } | 32 | } |
32 | ++argv; | 33 | ++argv; |
@@ -44,7 +45,7 @@ struct cmd_results *cmd_assign(int argc, char **argv) { | |||
44 | criteria->target = join_args(argv, target_len); | 45 | criteria->target = join_args(argv, target_len); |
45 | 46 | ||
46 | list_add(config->criteria, criteria); | 47 | list_add(config->criteria, criteria); |
47 | wlr_log(L_DEBUG, "assign: '%s' -> '%s' added", criteria->raw, | 48 | wlr_log(WLR_DEBUG, "assign: '%s' -> '%s' added", criteria->raw, |
48 | criteria->target); | 49 | criteria->target); |
49 | 50 | ||
50 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | 51 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); |
diff --git a/sway/commands/bar.c b/sway/commands/bar.c index d84ce808..f6a70c17 100644 --- a/sway/commands/bar.c +++ b/sway/commands/bar.c | |||
@@ -63,13 +63,13 @@ struct cmd_results *cmd_bar(int argc, char **argv) { | |||
63 | for (int i = 0; i < config->bars->length; ++i) { | 63 | for (int i = 0; i < config->bars->length; ++i) { |
64 | struct bar_config *item = config->bars->items[i]; | 64 | struct bar_config *item = config->bars->items[i]; |
65 | if (strcmp(item->id, argv[0]) == 0) { | 65 | if (strcmp(item->id, argv[0]) == 0) { |
66 | wlr_log(L_DEBUG, "Selecting bar: %s", argv[0]); | 66 | wlr_log(WLR_DEBUG, "Selecting bar: %s", argv[0]); |
67 | bar = item; | 67 | bar = item; |
68 | break; | 68 | break; |
69 | } | 69 | } |
70 | } | 70 | } |
71 | if (!bar) { | 71 | if (!bar) { |
72 | wlr_log(L_DEBUG, "Creating bar: %s", argv[0]); | 72 | wlr_log(WLR_DEBUG, "Creating bar: %s", argv[0]); |
73 | bar = default_bar_config(); | 73 | bar = default_bar_config(); |
74 | if (!bar) { | 74 | if (!bar) { |
75 | return cmd_results_new(CMD_FAILURE, "bar", | 75 | return cmd_results_new(CMD_FAILURE, "bar", |
@@ -108,7 +108,7 @@ struct cmd_results *cmd_bar(int argc, char **argv) { | |||
108 | 108 | ||
109 | // Set current bar | 109 | // Set current bar |
110 | config->current_bar = bar; | 110 | config->current_bar = bar; |
111 | wlr_log(L_DEBUG, "Creating bar %s", bar->id); | 111 | wlr_log(WLR_DEBUG, "Creating bar %s", bar->id); |
112 | } | 112 | } |
113 | 113 | ||
114 | return config_subcommand(argv, argc, bar_handlers, sizeof(bar_handlers)); | 114 | return config_subcommand(argv, argc, bar_handlers, sizeof(bar_handlers)); |
diff --git a/sway/commands/bar/binding_mode_indicator.c b/sway/commands/bar/binding_mode_indicator.c index 3ba5f33f..0c48bee9 100644 --- a/sway/commands/bar/binding_mode_indicator.c +++ b/sway/commands/bar/binding_mode_indicator.c | |||
@@ -15,11 +15,11 @@ struct cmd_results *bar_cmd_binding_mode_indicator(int argc, char **argv) { | |||
15 | } | 15 | } |
16 | if (strcasecmp("yes", argv[0]) == 0) { | 16 | if (strcasecmp("yes", argv[0]) == 0) { |
17 | config->current_bar->binding_mode_indicator = true; | 17 | config->current_bar->binding_mode_indicator = true; |
18 | wlr_log(L_DEBUG, "Enabling binding mode indicator on bar: %s", | 18 | wlr_log(WLR_DEBUG, "Enabling binding mode indicator on bar: %s", |
19 | config->current_bar->id); | 19 | config->current_bar->id); |
20 | } else if (strcasecmp("no", argv[0]) == 0) { | 20 | } else if (strcasecmp("no", argv[0]) == 0) { |
21 | config->current_bar->binding_mode_indicator = false; | 21 | config->current_bar->binding_mode_indicator = false; |
22 | wlr_log(L_DEBUG, "Disabling binding mode indicator on bar: %s", | 22 | wlr_log(WLR_DEBUG, "Disabling binding mode indicator on bar: %s", |
23 | config->current_bar->id); | 23 | config->current_bar->id); |
24 | } | 24 | } |
25 | return cmd_results_new(CMD_INVALID, "binding_mode_indicator", | 25 | return cmd_results_new(CMD_INVALID, "binding_mode_indicator", |
diff --git a/sway/commands/bar/font.c b/sway/commands/bar/font.c index 80b7a593..2aa4e895 100644 --- a/sway/commands/bar/font.c +++ b/sway/commands/bar/font.c | |||
@@ -14,8 +14,8 @@ struct cmd_results *bar_cmd_font(int argc, char **argv) { | |||
14 | } | 14 | } |
15 | char *font = join_args(argv, argc); | 15 | char *font = join_args(argv, argc); |
16 | free(config->current_bar->font); | 16 | free(config->current_bar->font); |
17 | config->current_bar->font = strdup(font); | 17 | config->current_bar->font = font; |
18 | wlr_log(L_DEBUG, "Settings font '%s' for bar: %s", | 18 | wlr_log(WLR_DEBUG, "Settings font '%s' for bar: %s", |
19 | config->current_bar->font, config->current_bar->id); | 19 | config->current_bar->font, config->current_bar->id); |
20 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | 20 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); |
21 | } | 21 | } |
diff --git a/sway/commands/bar/height.c b/sway/commands/bar/height.c index 3160caed..18258526 100644 --- a/sway/commands/bar/height.c +++ b/sway/commands/bar/height.c | |||
@@ -14,7 +14,7 @@ struct cmd_results *bar_cmd_height(int argc, char **argv) { | |||
14 | "Invalid height value: %s", argv[0]); | 14 | "Invalid height value: %s", argv[0]); |
15 | } | 15 | } |
16 | config->current_bar->height = height; | 16 | config->current_bar->height = height; |
17 | wlr_log(L_DEBUG, "Setting bar height to %d on bar: %s", | 17 | wlr_log(WLR_DEBUG, "Setting bar height to %d on bar: %s", |
18 | height, config->current_bar->id); | 18 | height, config->current_bar->id); |
19 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | 19 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); |
20 | } | 20 | } |
diff --git a/sway/commands/bar/hidden_state.c b/sway/commands/bar/hidden_state.c index 6641f184..502ce2c4 100644 --- a/sway/commands/bar/hidden_state.c +++ b/sway/commands/bar/hidden_state.c | |||
@@ -27,7 +27,7 @@ static struct cmd_results *bar_set_hidden_state(struct bar_config *bar, | |||
27 | if (!config->reading) { | 27 | if (!config->reading) { |
28 | ipc_event_barconfig_update(bar); | 28 | ipc_event_barconfig_update(bar); |
29 | } | 29 | } |
30 | wlr_log(L_DEBUG, "Setting hidden_state: '%s' for bar: %s", | 30 | wlr_log(WLR_DEBUG, "Setting hidden_state: '%s' for bar: %s", |
31 | bar->hidden_state, bar->id); | 31 | bar->hidden_state, bar->id); |
32 | } | 32 | } |
33 | // free old mode | 33 | // free old mode |
diff --git a/sway/commands/bar/id.c b/sway/commands/bar/id.c index 6ce86fef..65fa69fd 100644 --- a/sway/commands/bar/id.c +++ b/sway/commands/bar/id.c | |||
@@ -24,7 +24,7 @@ struct cmd_results *bar_cmd_id(int argc, char **argv) { | |||
24 | } | 24 | } |
25 | } | 25 | } |
26 | 26 | ||
27 | wlr_log(L_DEBUG, "Renaming bar: '%s' to '%s'", oldname, name); | 27 | wlr_log(WLR_DEBUG, "Renaming bar: '%s' to '%s'", oldname, name); |
28 | 28 | ||
29 | // free old bar id | 29 | // free old bar id |
30 | free(config->current_bar->id); | 30 | free(config->current_bar->id); |
diff --git a/sway/commands/bar/mode.c b/sway/commands/bar/mode.c index 34bb0a4f..28e2d77b 100644 --- a/sway/commands/bar/mode.c +++ b/sway/commands/bar/mode.c | |||
@@ -28,7 +28,7 @@ static struct cmd_results *bar_set_mode(struct bar_config *bar, const char *mode | |||
28 | if (!config->reading) { | 28 | if (!config->reading) { |
29 | ipc_event_barconfig_update(bar); | 29 | ipc_event_barconfig_update(bar); |
30 | } | 30 | } |
31 | wlr_log(L_DEBUG, "Setting mode: '%s' for bar: %s", bar->mode, bar->id); | 31 | wlr_log(WLR_DEBUG, "Setting mode: '%s' for bar: %s", bar->mode, bar->id); |
32 | } | 32 | } |
33 | 33 | ||
34 | // free old mode | 34 | // free old mode |
diff --git a/sway/commands/bar/modifier.c b/sway/commands/bar/modifier.c index 7ba4b125..09025fff 100644 --- a/sway/commands/bar/modifier.c +++ b/sway/commands/bar/modifier.c | |||
@@ -22,14 +22,15 @@ struct cmd_results *bar_cmd_modifier(int argc, char **argv) { | |||
22 | mod |= tmp_mod; | 22 | mod |= tmp_mod; |
23 | continue; | 23 | continue; |
24 | } else { | 24 | } else { |
25 | error = cmd_results_new(CMD_INVALID, "modifier", | ||
26 | "Unknown modifier '%s'", split->items[i]); | ||
25 | free_flat_list(split); | 27 | free_flat_list(split); |
26 | return cmd_results_new(CMD_INVALID, "modifier", | 28 | return error; |
27 | "Unknown modifier '%s'", split->items[i]); | ||
28 | } | 29 | } |
29 | } | 30 | } |
30 | free_flat_list(split); | 31 | free_flat_list(split); |
31 | config->current_bar->modifier = mod; | 32 | config->current_bar->modifier = mod; |
32 | wlr_log(L_DEBUG, | 33 | wlr_log(WLR_DEBUG, |
33 | "Show/Hide the bar when pressing '%s' in hide mode.", argv[0]); | 34 | "Show/Hide the bar when pressing '%s' in hide mode.", argv[0]); |
34 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | 35 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); |
35 | } | 36 | } |
diff --git a/sway/commands/bar/output.c b/sway/commands/bar/output.c index f7ca0aa4..72754e05 100644 --- a/sway/commands/bar/output.c +++ b/sway/commands/bar/output.c | |||
@@ -42,7 +42,7 @@ struct cmd_results *bar_cmd_output(int argc, char **argv) { | |||
42 | 42 | ||
43 | if (add_output) { | 43 | if (add_output) { |
44 | list_add(outputs, strdup(output)); | 44 | list_add(outputs, strdup(output)); |
45 | wlr_log(L_DEBUG, "Adding bar: '%s' to output '%s'", | 45 | wlr_log(WLR_DEBUG, "Adding bar: '%s' to output '%s'", |
46 | config->current_bar->id, output); | 46 | config->current_bar->id, output); |
47 | } | 47 | } |
48 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | 48 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); |
diff --git a/sway/commands/bar/pango_markup.c b/sway/commands/bar/pango_markup.c index 480af724..857571fb 100644 --- a/sway/commands/bar/pango_markup.c +++ b/sway/commands/bar/pango_markup.c | |||
@@ -13,11 +13,11 @@ struct cmd_results *bar_cmd_pango_markup(int argc, char **argv) { | |||
13 | } | 13 | } |
14 | if (strcasecmp("enabled", argv[0]) == 0) { | 14 | if (strcasecmp("enabled", argv[0]) == 0) { |
15 | config->current_bar->pango_markup = true; | 15 | config->current_bar->pango_markup = true; |
16 | wlr_log(L_DEBUG, "Enabling pango markup for bar: %s", | 16 | wlr_log(WLR_DEBUG, "Enabling pango markup for bar: %s", |
17 | config->current_bar->id); | 17 | config->current_bar->id); |
18 | } else if (strcasecmp("disabled", argv[0]) == 0) { | 18 | } else if (strcasecmp("disabled", argv[0]) == 0) { |
19 | config->current_bar->pango_markup = false; | 19 | config->current_bar->pango_markup = false; |
20 | wlr_log(L_DEBUG, "Disabling pango markup for bar: %s", | 20 | wlr_log(WLR_DEBUG, "Disabling pango markup for bar: %s", |
21 | config->current_bar->id); | 21 | config->current_bar->id); |
22 | } else { | 22 | } else { |
23 | error = cmd_results_new(CMD_INVALID, "pango_markup", | 23 | error = cmd_results_new(CMD_INVALID, "pango_markup", |
diff --git a/sway/commands/bar/position.c b/sway/commands/bar/position.c index 9c580483..44bb4ae3 100644 --- a/sway/commands/bar/position.c +++ b/sway/commands/bar/position.c | |||
@@ -15,8 +15,9 @@ struct cmd_results *bar_cmd_position(int argc, char **argv) { | |||
15 | char *valid[] = { "top", "bottom", "left", "right" }; | 15 | char *valid[] = { "top", "bottom", "left", "right" }; |
16 | for (size_t i = 0; i < sizeof(valid) / sizeof(valid[0]); ++i) { | 16 | for (size_t i = 0; i < sizeof(valid) / sizeof(valid[0]); ++i) { |
17 | if (strcasecmp(valid[i], argv[0]) == 0) { | 17 | if (strcasecmp(valid[i], argv[0]) == 0) { |
18 | wlr_log(L_DEBUG, "Setting bar position '%s' for bar: %s", | 18 | wlr_log(WLR_DEBUG, "Setting bar position '%s' for bar: %s", |
19 | argv[0], config->current_bar->id); | 19 | argv[0], config->current_bar->id); |
20 | free(config->current_bar->position); | ||
20 | config->current_bar->position = strdup(argv[0]); | 21 | config->current_bar->position = strdup(argv[0]); |
21 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | 22 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); |
22 | } | 23 | } |
diff --git a/sway/commands/bar/separator_symbol.c b/sway/commands/bar/separator_symbol.c index 1e08df6d..392ab730 100644 --- a/sway/commands/bar/separator_symbol.c +++ b/sway/commands/bar/separator_symbol.c | |||
@@ -14,7 +14,7 @@ struct cmd_results *bar_cmd_separator_symbol(int argc, char **argv) { | |||
14 | } | 14 | } |
15 | free(config->current_bar->separator_symbol); | 15 | free(config->current_bar->separator_symbol); |
16 | config->current_bar->separator_symbol = strdup(argv[0]); | 16 | config->current_bar->separator_symbol = strdup(argv[0]); |
17 | wlr_log(L_DEBUG, "Settings separator_symbol '%s' for bar: %s", | 17 | wlr_log(WLR_DEBUG, "Settings separator_symbol '%s' for bar: %s", |
18 | config->current_bar->separator_symbol, config->current_bar->id); | 18 | config->current_bar->separator_symbol, config->current_bar->id); |
19 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | 19 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); |
20 | } | 20 | } |
diff --git a/sway/commands/bar/status_command.c b/sway/commands/bar/status_command.c index 5e199cde..6f6f81a3 100644 --- a/sway/commands/bar/status_command.c +++ b/sway/commands/bar/status_command.c | |||
@@ -14,7 +14,7 @@ struct cmd_results *bar_cmd_status_command(int argc, char **argv) { | |||
14 | } | 14 | } |
15 | free(config->current_bar->status_command); | 15 | free(config->current_bar->status_command); |
16 | config->current_bar->status_command = join_args(argv, argc); | 16 | config->current_bar->status_command = join_args(argv, argc); |
17 | wlr_log(L_DEBUG, "Feeding bar with status command: %s", | 17 | wlr_log(WLR_DEBUG, "Feeding bar with status command: %s", |
18 | config->current_bar->status_command); | 18 | config->current_bar->status_command); |
19 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | 19 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); |
20 | } | 20 | } |
diff --git a/sway/commands/bar/strip_workspace_numbers.c b/sway/commands/bar/strip_workspace_numbers.c index 4f24a356..4e47d047 100644 --- a/sway/commands/bar/strip_workspace_numbers.c +++ b/sway/commands/bar/strip_workspace_numbers.c | |||
@@ -15,11 +15,11 @@ struct cmd_results *bar_cmd_strip_workspace_numbers(int argc, char **argv) { | |||
15 | } | 15 | } |
16 | if (strcasecmp("yes", argv[0]) == 0) { | 16 | if (strcasecmp("yes", argv[0]) == 0) { |
17 | config->current_bar->strip_workspace_numbers = true; | 17 | config->current_bar->strip_workspace_numbers = true; |
18 | wlr_log(L_DEBUG, "Stripping workspace numbers on bar: %s", | 18 | wlr_log(WLR_DEBUG, "Stripping workspace numbers on bar: %s", |
19 | config->current_bar->id); | 19 | config->current_bar->id); |
20 | } else if (strcasecmp("no", argv[0]) == 0) { | 20 | } else if (strcasecmp("no", argv[0]) == 0) { |
21 | config->current_bar->strip_workspace_numbers = false; | 21 | config->current_bar->strip_workspace_numbers = false; |
22 | wlr_log(L_DEBUG, "Enabling workspace numbers on bar: %s", | 22 | wlr_log(WLR_DEBUG, "Enabling workspace numbers on bar: %s", |
23 | config->current_bar->id); | 23 | config->current_bar->id); |
24 | } else { | 24 | } else { |
25 | return cmd_results_new(CMD_INVALID, | 25 | return cmd_results_new(CMD_INVALID, |
diff --git a/sway/commands/bar/swaybar_command.c b/sway/commands/bar/swaybar_command.c index 520cdd11..04e78e77 100644 --- a/sway/commands/bar/swaybar_command.c +++ b/sway/commands/bar/swaybar_command.c | |||
@@ -14,7 +14,7 @@ struct cmd_results *bar_cmd_swaybar_command(int argc, char **argv) { | |||
14 | } | 14 | } |
15 | free(config->current_bar->swaybar_command); | 15 | free(config->current_bar->swaybar_command); |
16 | config->current_bar->swaybar_command = join_args(argv, argc); | 16 | config->current_bar->swaybar_command = join_args(argv, argc); |
17 | wlr_log(L_DEBUG, "Using custom swaybar command: %s", | 17 | wlr_log(WLR_DEBUG, "Using custom swaybar command: %s", |
18 | config->current_bar->swaybar_command); | 18 | config->current_bar->swaybar_command); |
19 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | 19 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); |
20 | } | 20 | } |
diff --git a/sway/commands/bar/workspace_buttons.c b/sway/commands/bar/workspace_buttons.c index 6edc3a0d..a4079b2a 100644 --- a/sway/commands/bar/workspace_buttons.c +++ b/sway/commands/bar/workspace_buttons.c | |||
@@ -14,11 +14,11 @@ struct cmd_results *bar_cmd_workspace_buttons(int argc, char **argv) { | |||
14 | } | 14 | } |
15 | if (strcasecmp("yes", argv[0]) == 0) { | 15 | if (strcasecmp("yes", argv[0]) == 0) { |
16 | config->current_bar->workspace_buttons = true; | 16 | config->current_bar->workspace_buttons = true; |
17 | wlr_log(L_DEBUG, "Enabling workspace buttons on bar: %s", | 17 | wlr_log(WLR_DEBUG, "Enabling workspace buttons on bar: %s", |
18 | config->current_bar->id); | 18 | config->current_bar->id); |
19 | } else if (strcasecmp("no", argv[0]) == 0) { | 19 | } else if (strcasecmp("no", argv[0]) == 0) { |
20 | config->current_bar->workspace_buttons = false; | 20 | config->current_bar->workspace_buttons = false; |
21 | wlr_log(L_DEBUG, "Disabling workspace buttons on bar: %s", | 21 | wlr_log(WLR_DEBUG, "Disabling workspace buttons on bar: %s", |
22 | config->current_bar->id); | 22 | config->current_bar->id); |
23 | } else { | 23 | } else { |
24 | return cmd_results_new(CMD_INVALID, "workspace_buttons", | 24 | return cmd_results_new(CMD_INVALID, "workspace_buttons", |
diff --git a/sway/commands/bar/wrap_scroll.c b/sway/commands/bar/wrap_scroll.c index 7386f82c..701de00a 100644 --- a/sway/commands/bar/wrap_scroll.c +++ b/sway/commands/bar/wrap_scroll.c | |||
@@ -13,11 +13,11 @@ struct cmd_results *bar_cmd_wrap_scroll(int argc, char **argv) { | |||
13 | } | 13 | } |
14 | if (strcasecmp("yes", argv[0]) == 0) { | 14 | if (strcasecmp("yes", argv[0]) == 0) { |
15 | config->current_bar->wrap_scroll = true; | 15 | config->current_bar->wrap_scroll = true; |
16 | wlr_log(L_DEBUG, "Enabling wrap scroll on bar: %s", | 16 | wlr_log(WLR_DEBUG, "Enabling wrap scroll on bar: %s", |
17 | config->current_bar->id); | 17 | config->current_bar->id); |
18 | } else if (strcasecmp("no", argv[0]) == 0) { | 18 | } else if (strcasecmp("no", argv[0]) == 0) { |
19 | config->current_bar->wrap_scroll = false; | 19 | config->current_bar->wrap_scroll = false; |
20 | wlr_log(L_DEBUG, "Disabling wrap scroll on bar: %s", | 20 | wlr_log(WLR_DEBUG, "Disabling wrap scroll on bar: %s", |
21 | config->current_bar->id); | 21 | config->current_bar->id); |
22 | } else { | 22 | } else { |
23 | return cmd_results_new(CMD_INVALID, | 23 | return cmd_results_new(CMD_INVALID, |
diff --git a/sway/commands/bind.c b/sway/commands/bind.c index 821f9cd1..83e9e432 100644 --- a/sway/commands/bind.c +++ b/sway/commands/bind.c | |||
@@ -184,7 +184,7 @@ static struct cmd_results *cmd_bindsym_or_bindcode(int argc, char **argv, | |||
184 | for (int i = 0; i < mode_bindings->length; ++i) { | 184 | for (int i = 0; i < mode_bindings->length; ++i) { |
185 | struct sway_binding *config_binding = mode_bindings->items[i]; | 185 | struct sway_binding *config_binding = mode_bindings->items[i]; |
186 | if (binding_key_compare(binding, config_binding)) { | 186 | if (binding_key_compare(binding, config_binding)) { |
187 | wlr_log(L_DEBUG, "overwriting old binding with command '%s'", | 187 | wlr_log(WLR_DEBUG, "overwriting old binding with command '%s'", |
188 | config_binding->command); | 188 | config_binding->command); |
189 | free_sway_binding(config_binding); | 189 | free_sway_binding(config_binding); |
190 | mode_bindings->items[i] = binding; | 190 | mode_bindings->items[i] = binding; |
@@ -196,7 +196,7 @@ static struct cmd_results *cmd_bindsym_or_bindcode(int argc, char **argv, | |||
196 | list_add(mode_bindings, binding); | 196 | list_add(mode_bindings, binding); |
197 | } | 197 | } |
198 | 198 | ||
199 | wlr_log(L_DEBUG, "%s - Bound %s to command %s", | 199 | wlr_log(WLR_DEBUG, "%s - Bound %s to command %s", |
200 | bindtype, argv[0], binding->command); | 200 | bindtype, argv[0], binding->command); |
201 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | 201 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); |
202 | 202 | ||
diff --git a/sway/commands/border.c b/sway/commands/border.c index 6db85395..9c19e20a 100644 --- a/sway/commands/border.c +++ b/sway/commands/border.c | |||
@@ -42,7 +42,7 @@ struct cmd_results *cmd_border(int argc, char **argv) { | |||
42 | container_set_geometry_from_floating_view(view->swayc); | 42 | container_set_geometry_from_floating_view(view->swayc); |
43 | } | 43 | } |
44 | 44 | ||
45 | arrange_and_commit(view->swayc); | 45 | arrange_windows(view->swayc); |
46 | 46 | ||
47 | struct sway_seat *seat = input_manager_current_seat(input_manager); | 47 | struct sway_seat *seat = input_manager_current_seat(input_manager); |
48 | if (seat->cursor) { | 48 | if (seat->cursor) { |
diff --git a/sway/commands/default_floating_border.c b/sway/commands/default_floating_border.c new file mode 100644 index 00000000..1bfc24af --- /dev/null +++ b/sway/commands/default_floating_border.c | |||
@@ -0,0 +1,29 @@ | |||
1 | #include "log.h" | ||
2 | #include "sway/commands.h" | ||
3 | #include "sway/config.h" | ||
4 | #include "sway/tree/container.h" | ||
5 | |||
6 | struct cmd_results *cmd_default_floating_border(int argc, char **argv) { | ||
7 | struct cmd_results *error = NULL; | ||
8 | if ((error = checkarg(argc, "default_floating_border", | ||
9 | EXPECTED_AT_LEAST, 1))) { | ||
10 | return error; | ||
11 | } | ||
12 | |||
13 | if (strcmp(argv[0], "none") == 0) { | ||
14 | config->floating_border = B_NONE; | ||
15 | } else if (strcmp(argv[0], "normal") == 0) { | ||
16 | config->floating_border = B_NORMAL; | ||
17 | } else if (strcmp(argv[0], "pixel") == 0) { | ||
18 | config->floating_border = B_PIXEL; | ||
19 | } else { | ||
20 | return cmd_results_new(CMD_INVALID, "default_floating_border", | ||
21 | "Expected 'default_floating_border <none|normal|pixel>' " | ||
22 | "or 'default_floating_border <normal|pixel> <px>'"); | ||
23 | } | ||
24 | if (argc == 2) { | ||
25 | config->floating_border_thickness = atoi(argv[1]); | ||
26 | } | ||
27 | |||
28 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | ||
29 | } | ||
diff --git a/sway/commands/exec.c b/sway/commands/exec.c index 363d5bef..7fc54123 100644 --- a/sway/commands/exec.c +++ b/sway/commands/exec.c | |||
@@ -8,7 +8,7 @@ struct cmd_results *cmd_exec(int argc, char **argv) { | |||
8 | if (!config->active) return cmd_results_new(CMD_DEFER, "exec", NULL); | 8 | if (!config->active) return cmd_results_new(CMD_DEFER, "exec", NULL); |
9 | if (config->reloading) { | 9 | if (config->reloading) { |
10 | char *args = join_args(argv, argc); | 10 | char *args = join_args(argv, argc); |
11 | wlr_log(L_DEBUG, "Ignoring 'exec %s' due to reload", args); | 11 | wlr_log(WLR_DEBUG, "Ignoring 'exec %s' due to reload", args); |
12 | free(args); | 12 | free(args); |
13 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | 13 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); |
14 | } | 14 | } |
diff --git a/sway/commands/exec_always.c b/sway/commands/exec_always.c index abd52e59..9bf2b320 100644 --- a/sway/commands/exec_always.c +++ b/sway/commands/exec_always.c | |||
@@ -20,7 +20,7 @@ struct cmd_results *cmd_exec_always(int argc, char **argv) { | |||
20 | 20 | ||
21 | char *tmp = NULL; | 21 | char *tmp = NULL; |
22 | if (strcmp((char*)*argv, "--no-startup-id") == 0) { | 22 | if (strcmp((char*)*argv, "--no-startup-id") == 0) { |
23 | wlr_log(L_INFO, "exec switch '--no-startup-id' not supported, ignored."); | 23 | wlr_log(WLR_INFO, "exec switch '--no-startup-id' not supported, ignored."); |
24 | if ((error = checkarg(argc - 1, "exec_always", EXPECTED_MORE_THAN, 0))) { | 24 | if ((error = checkarg(argc - 1, "exec_always", EXPECTED_MORE_THAN, 0))) { |
25 | return error; | 25 | return error; |
26 | } | 26 | } |
@@ -35,50 +35,49 @@ struct cmd_results *cmd_exec_always(int argc, char **argv) { | |||
35 | strncpy(cmd, tmp, sizeof(cmd) - 1); | 35 | strncpy(cmd, tmp, sizeof(cmd) - 1); |
36 | cmd[sizeof(cmd) - 1] = 0; | 36 | cmd[sizeof(cmd) - 1] = 0; |
37 | free(tmp); | 37 | free(tmp); |
38 | wlr_log(L_DEBUG, "Executing %s", cmd); | 38 | wlr_log(WLR_DEBUG, "Executing %s", cmd); |
39 | 39 | ||
40 | int fd[2]; | 40 | int fd[2]; |
41 | if (pipe(fd) != 0) { | 41 | if (pipe(fd) != 0) { |
42 | wlr_log(L_ERROR, "Unable to create pipe for fork"); | 42 | wlr_log(WLR_ERROR, "Unable to create pipe for fork"); |
43 | } | 43 | } |
44 | 44 | ||
45 | pid_t pid; | 45 | pid_t pid, child; |
46 | pid_t *child = malloc(sizeof(pid_t)); // malloc'd so that Linux can avoid copying the process space | ||
47 | if (!child) { | ||
48 | return cmd_results_new(CMD_FAILURE, "exec_always", "Unable to allocate child pid"); | ||
49 | } | ||
50 | // Fork process | 46 | // Fork process |
51 | if ((pid = fork()) == 0) { | 47 | if ((pid = fork()) == 0) { |
52 | // Fork child process again | 48 | // Fork child process again |
53 | setsid(); | 49 | setsid(); |
54 | if ((*child = fork()) == 0) { | 50 | close(fd[0]); |
51 | if ((child = fork()) == 0) { | ||
52 | close(fd[1]); | ||
55 | execl("/bin/sh", "/bin/sh", "-c", cmd, (void *)NULL); | 53 | execl("/bin/sh", "/bin/sh", "-c", cmd, (void *)NULL); |
56 | // Not reached | 54 | _exit(0); |
57 | } | 55 | } |
58 | close(fd[0]); | ||
59 | ssize_t s = 0; | 56 | ssize_t s = 0; |
60 | while ((size_t)s < sizeof(pid_t)) { | 57 | while ((size_t)s < sizeof(pid_t)) { |
61 | s += write(fd[1], ((uint8_t *)child) + s, sizeof(pid_t) - s); | 58 | s += write(fd[1], ((uint8_t *)&child) + s, sizeof(pid_t) - s); |
62 | } | 59 | } |
63 | close(fd[1]); | 60 | close(fd[1]); |
64 | _exit(0); // Close child process | 61 | _exit(0); // Close child process |
65 | } else if (pid < 0) { | 62 | } else if (pid < 0) { |
66 | free(child); | 63 | close(fd[0]); |
64 | close(fd[1]); | ||
67 | return cmd_results_new(CMD_FAILURE, "exec_always", "fork() failed"); | 65 | return cmd_results_new(CMD_FAILURE, "exec_always", "fork() failed"); |
68 | } | 66 | } |
69 | close(fd[1]); // close write | 67 | close(fd[1]); // close write |
70 | ssize_t s = 0; | 68 | ssize_t s = 0; |
71 | while ((size_t)s < sizeof(pid_t)) { | 69 | while ((size_t)s < sizeof(pid_t)) { |
72 | s += read(fd[0], ((uint8_t *)child) + s, sizeof(pid_t) - s); | 70 | s += read(fd[0], ((uint8_t *)&child) + s, sizeof(pid_t) - s); |
73 | } | 71 | } |
74 | close(fd[0]); | 72 | close(fd[0]); |
75 | // cleanup child process | 73 | // cleanup child process |
76 | waitpid(pid, NULL, 0); | 74 | waitpid(pid, NULL, 0); |
77 | if (*child > 0) { | 75 | if (child > 0) { |
78 | wlr_log(L_DEBUG, "Child process created with pid %d", *child); | 76 | wlr_log(WLR_DEBUG, "Child process created with pid %d", child); |
79 | workspace_record_pid(*child); | 77 | workspace_record_pid(child); |
80 | } else { | 78 | } else { |
81 | free(child); | 79 | return cmd_results_new(CMD_FAILURE, "exec_always", |
80 | "Second fork() failed"); | ||
82 | } | 81 | } |
83 | 82 | ||
84 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | 83 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); |
diff --git a/sway/commands/floating.c b/sway/commands/floating.c index e6003521..6ab56c3b 100644 --- a/sway/commands/floating.c +++ b/sway/commands/floating.c | |||
@@ -37,7 +37,7 @@ struct cmd_results *cmd_floating(int argc, char **argv) { | |||
37 | container_set_floating(container, wants_floating); | 37 | container_set_floating(container, wants_floating); |
38 | 38 | ||
39 | struct sway_container *workspace = container_parent(container, C_WORKSPACE); | 39 | struct sway_container *workspace = container_parent(container, C_WORKSPACE); |
40 | arrange_and_commit(workspace); | 40 | arrange_windows(workspace); |
41 | 41 | ||
42 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | 42 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); |
43 | } | 43 | } |
diff --git a/sway/commands/floating_minmax_size.c b/sway/commands/floating_minmax_size.c new file mode 100644 index 00000000..0af78908 --- /dev/null +++ b/sway/commands/floating_minmax_size.c | |||
@@ -0,0 +1,53 @@ | |||
1 | #include <errno.h> | ||
2 | #include <math.h> | ||
3 | #include <stdbool.h> | ||
4 | #include <stdlib.h> | ||
5 | #include <string.h> | ||
6 | #include <strings.h> | ||
7 | #include <wlr/util/log.h> | ||
8 | #include "sway/commands.h" | ||
9 | #include "log.h" | ||
10 | |||
11 | static const char* min_usage = | ||
12 | "Expected 'floating_minimum_size <width> x <height>'"; | ||
13 | |||
14 | static const char* max_usage = | ||
15 | "Expected 'floating_maximum_size <width> x <height>'"; | ||
16 | |||
17 | static struct cmd_results *handle_command(int argc, char **argv, char *cmd_name, | ||
18 | const char *usage, int *config_width, int *config_height) { | ||
19 | struct cmd_results *error; | ||
20 | if ((error = checkarg(argc, cmd_name, EXPECTED_EQUAL_TO, 3))) { | ||
21 | return error; | ||
22 | } | ||
23 | |||
24 | char *err; | ||
25 | int width = (int)strtol(argv[0], &err, 10); | ||
26 | if (*err) { | ||
27 | return cmd_results_new(CMD_INVALID, cmd_name, usage); | ||
28 | } | ||
29 | |||
30 | if (strcmp(argv[1], "x") != 0) { | ||
31 | return cmd_results_new(CMD_INVALID, cmd_name, usage); | ||
32 | } | ||
33 | |||
34 | int height = (int)strtol(argv[2], &err, 10); | ||
35 | if (*err) { | ||
36 | return cmd_results_new(CMD_INVALID, cmd_name, usage); | ||
37 | } | ||
38 | |||
39 | *config_width = width; | ||
40 | *config_height = height; | ||
41 | |||
42 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | ||
43 | } | ||
44 | |||
45 | struct cmd_results *cmd_floating_minimum_size(int argc, char **argv) { | ||
46 | return handle_command(argc, argv, "floating_minimum_size", min_usage, | ||
47 | &config->floating_minimum_width, &config->floating_minimum_height); | ||
48 | } | ||
49 | |||
50 | struct cmd_results *cmd_floating_maximum_size(int argc, char **argv) { | ||
51 | return handle_command(argc, argv, "floating_maximum_size", max_usage, | ||
52 | &config->floating_maximum_width, &config->floating_maximum_height); | ||
53 | } | ||
diff --git a/sway/commands/floating_modifier.c b/sway/commands/floating_modifier.c new file mode 100644 index 00000000..9432c9f1 --- /dev/null +++ b/sway/commands/floating_modifier.c | |||
@@ -0,0 +1,20 @@ | |||
1 | #include "sway/commands.h" | ||
2 | #include "sway/config.h" | ||
3 | #include "util.h" | ||
4 | |||
5 | struct cmd_results *cmd_floating_modifier(int argc, char **argv) { | ||
6 | struct cmd_results *error = NULL; | ||
7 | if ((error = checkarg(argc, "floating_modifier", EXPECTED_EQUAL_TO, 1))) { | ||
8 | return error; | ||
9 | } | ||
10 | |||
11 | uint32_t mod = get_modifier_mask_by_name(argv[0]); | ||
12 | if (!mod) { | ||
13 | return cmd_results_new(CMD_INVALID, "floating_modifier", | ||
14 | "Invalid modifier"); | ||
15 | } | ||
16 | |||
17 | config->floating_mod = mod; | ||
18 | |||
19 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | ||
20 | } | ||
diff --git a/sway/commands/focus.c b/sway/commands/focus.c index 74d9d535..9cd8bfae 100644 --- a/sway/commands/focus.c +++ b/sway/commands/focus.c | |||
@@ -1,10 +1,14 @@ | |||
1 | #include <strings.h> | 1 | #include <strings.h> |
2 | #include <wlr/util/log.h> | 2 | #include <wlr/util/log.h> |
3 | #include "log.h" | 3 | #include "log.h" |
4 | #include "sway/commands.h" | ||
4 | #include "sway/input/input-manager.h" | 5 | #include "sway/input/input-manager.h" |
5 | #include "sway/input/seat.h" | 6 | #include "sway/input/seat.h" |
7 | #include "sway/output.h" | ||
8 | #include "sway/tree/arrange.h" | ||
6 | #include "sway/tree/view.h" | 9 | #include "sway/tree/view.h" |
7 | #include "sway/commands.h" | 10 | #include "sway/tree/workspace.h" |
11 | #include "stringop.h" | ||
8 | 12 | ||
9 | static bool parse_movement_direction(const char *name, | 13 | static bool parse_movement_direction(const char *name, |
10 | enum movement_direction *out) { | 14 | enum movement_direction *out) { |
@@ -27,7 +31,55 @@ static bool parse_movement_direction(const char *name, | |||
27 | return true; | 31 | return true; |
28 | } | 32 | } |
29 | 33 | ||
34 | static struct cmd_results *focus_mode(struct sway_container *con, | ||
35 | struct sway_seat *seat, bool floating) { | ||
36 | struct sway_container *ws = con->type == C_WORKSPACE ? | ||
37 | con : container_parent(con, C_WORKSPACE); | ||
38 | struct sway_container *new_focus = ws; | ||
39 | if (floating) { | ||
40 | new_focus = ws->sway_workspace->floating; | ||
41 | if (new_focus->children->length == 0) { | ||
42 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | ||
43 | } | ||
44 | } | ||
45 | seat_set_focus(seat, seat_get_active_child(seat, new_focus)); | ||
46 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | ||
47 | } | ||
48 | |||
49 | static struct cmd_results *focus_output(struct sway_container *con, | ||
50 | struct sway_seat *seat, int argc, char **argv) { | ||
51 | if (!argc) { | ||
52 | return cmd_results_new(CMD_INVALID, "focus", | ||
53 | "Expected 'focus output <direction|name>'"); | ||
54 | } | ||
55 | char *identifier = join_args(argv, argc); | ||
56 | struct sway_container *output = output_by_name(identifier); | ||
57 | |||
58 | if (!output) { | ||
59 | enum movement_direction direction; | ||
60 | if (!parse_movement_direction(identifier, &direction) || | ||
61 | direction == MOVE_PARENT || direction == MOVE_CHILD) { | ||
62 | free(identifier); | ||
63 | return cmd_results_new(CMD_INVALID, "focus", | ||
64 | "There is no output with that name"); | ||
65 | } | ||
66 | struct sway_container *focus = seat_get_focus(seat); | ||
67 | focus = container_parent(focus, C_OUTPUT); | ||
68 | output = container_get_in_direction(focus, seat, direction); | ||
69 | } | ||
70 | |||
71 | free(identifier); | ||
72 | if (output) { | ||
73 | seat_set_focus(seat, seat_get_focus_inactive(seat, output)); | ||
74 | } | ||
75 | |||
76 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | ||
77 | } | ||
78 | |||
30 | struct cmd_results *cmd_focus(int argc, char **argv) { | 79 | struct cmd_results *cmd_focus(int argc, char **argv) { |
80 | if (config->reading || !config->active) { | ||
81 | return cmd_results_new(CMD_DEFER, NULL, NULL); | ||
82 | } | ||
31 | struct sway_container *con = config->handler_context.current_container; | 83 | struct sway_container *con = config->handler_context.current_container; |
32 | struct sway_seat *seat = config->handler_context.seat; | 84 | struct sway_seat *seat = config->handler_context.seat; |
33 | if (con->type < C_WORKSPACE) { | 85 | if (con->type < C_WORKSPACE) { |
@@ -40,11 +92,24 @@ struct cmd_results *cmd_focus(int argc, char **argv) { | |||
40 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | 92 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); |
41 | } | 93 | } |
42 | 94 | ||
43 | // TODO mode_toggle | 95 | if (strcmp(argv[0], "floating") == 0) { |
96 | return focus_mode(con, seat, true); | ||
97 | } else if (strcmp(argv[0], "tiling") == 0) { | ||
98 | return focus_mode(con, seat, false); | ||
99 | } else if (strcmp(argv[0], "mode_toggle") == 0) { | ||
100 | return focus_mode(con, seat, !container_is_floating(con)); | ||
101 | } | ||
102 | |||
103 | if (strcmp(argv[0], "output") == 0) { | ||
104 | argc--; argv++; | ||
105 | return focus_output(con, seat, argc, argv); | ||
106 | } | ||
107 | |||
44 | enum movement_direction direction = 0; | 108 | enum movement_direction direction = 0; |
45 | if (!parse_movement_direction(argv[0], &direction)) { | 109 | if (!parse_movement_direction(argv[0], &direction)) { |
46 | return cmd_results_new(CMD_INVALID, "focus", | 110 | return cmd_results_new(CMD_INVALID, "focus", |
47 | "Expected 'focus <direction|parent|child|mode_toggle>' or 'focus output <direction|name>'"); | 111 | "Expected 'focus <direction|parent|child|mode_toggle|floating|tiling>' " |
112 | "or 'focus output <direction|name>'"); | ||
48 | } | 113 | } |
49 | 114 | ||
50 | struct sway_container *next_focus = container_get_in_direction( | 115 | struct sway_container *next_focus = container_get_in_direction( |
diff --git a/sway/commands/for_window.c b/sway/commands/for_window.c index 8c425a1d..ac4d6563 100644 --- a/sway/commands/for_window.c +++ b/sway/commands/for_window.c | |||
@@ -24,7 +24,7 @@ struct cmd_results *cmd_for_window(int argc, char **argv) { | |||
24 | criteria->cmdlist = join_args(argv + 1, argc - 1); | 24 | criteria->cmdlist = join_args(argv + 1, argc - 1); |
25 | 25 | ||
26 | list_add(config->criteria, criteria); | 26 | list_add(config->criteria, criteria); |
27 | wlr_log(L_DEBUG, "for_window: '%s' -> '%s' added", criteria->raw, criteria->cmdlist); | 27 | wlr_log(WLR_DEBUG, "for_window: '%s' -> '%s' added", criteria->raw, criteria->cmdlist); |
28 | 28 | ||
29 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | 29 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); |
30 | } | 30 | } |
diff --git a/sway/commands/force_display_urgency_hint.c b/sway/commands/force_display_urgency_hint.c new file mode 100644 index 00000000..5e5e2d55 --- /dev/null +++ b/sway/commands/force_display_urgency_hint.c | |||
@@ -0,0 +1,23 @@ | |||
1 | #include "sway/commands.h" | ||
2 | #include "sway/config.h" | ||
3 | |||
4 | struct cmd_results *cmd_force_display_urgency_hint(int argc, char **argv) { | ||
5 | struct cmd_results *error = NULL; | ||
6 | if ((error = checkarg(argc, "force_display_urgency_hint", | ||
7 | EXPECTED_AT_LEAST, 1))) { | ||
8 | return error; | ||
9 | } | ||
10 | |||
11 | char *err; | ||
12 | int timeout = (int)strtol(argv[0], &err, 10); | ||
13 | if (*err) { | ||
14 | if (strcmp(err, "ms") != 0) { | ||
15 | return cmd_results_new(CMD_INVALID, "force_display_urgency_hint", | ||
16 | "Expected 'force_display_urgency_hint <timeout> ms'"); | ||
17 | } | ||
18 | } | ||
19 | |||
20 | config->urgent_timeout = timeout > 0 ? timeout : 0; | ||
21 | |||
22 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | ||
23 | } | ||
diff --git a/sway/commands/fullscreen.c b/sway/commands/fullscreen.c index 1a4d8b41..0b5beaa2 100644 --- a/sway/commands/fullscreen.c +++ b/sway/commands/fullscreen.c | |||
@@ -34,7 +34,7 @@ struct cmd_results *cmd_fullscreen(int argc, char **argv) { | |||
34 | view_set_fullscreen(view, wants_fullscreen); | 34 | view_set_fullscreen(view, wants_fullscreen); |
35 | 35 | ||
36 | struct sway_container *workspace = container_parent(container, C_WORKSPACE); | 36 | struct sway_container *workspace = container_parent(container, C_WORKSPACE); |
37 | arrange_and_commit(workspace->parent); | 37 | arrange_windows(workspace->parent); |
38 | 38 | ||
39 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | 39 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); |
40 | } | 40 | } |
diff --git a/sway/commands/gaps.c b/sway/commands/gaps.c index 801fb179..3906eb70 100644 --- a/sway/commands/gaps.c +++ b/sway/commands/gaps.c | |||
@@ -43,7 +43,7 @@ struct cmd_results *cmd_gaps(int argc, char **argv) { | |||
43 | return cmd_results_new(CMD_INVALID, "gaps", | 43 | return cmd_results_new(CMD_INVALID, "gaps", |
44 | "gaps edge_gaps on|off|toggle"); | 44 | "gaps edge_gaps on|off|toggle"); |
45 | } | 45 | } |
46 | arrange_and_commit(&root_container); | 46 | arrange_windows(&root_container); |
47 | } else { | 47 | } else { |
48 | int amount_idx = 0; // the current index in argv | 48 | int amount_idx = 0; // the current index in argv |
49 | enum gaps_op op = GAPS_OP_SET; | 49 | enum gaps_op op = GAPS_OP_SET; |
@@ -124,7 +124,7 @@ struct cmd_results *cmd_gaps(int argc, char **argv) { | |||
124 | if (amount_idx == 0) { // gaps <amount> | 124 | if (amount_idx == 0) { // gaps <amount> |
125 | config->gaps_inner = val; | 125 | config->gaps_inner = val; |
126 | config->gaps_outer = val; | 126 | config->gaps_outer = val; |
127 | arrange_and_commit(&root_container); | 127 | arrange_windows(&root_container); |
128 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | 128 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); |
129 | } | 129 | } |
130 | // Other variants. The middle-length variant (gaps inner|outer <amount>) | 130 | // Other variants. The middle-length variant (gaps inner|outer <amount>) |
@@ -155,7 +155,7 @@ struct cmd_results *cmd_gaps(int argc, char **argv) { | |||
155 | } else { | 155 | } else { |
156 | config->gaps_outer = total; | 156 | config->gaps_outer = total; |
157 | } | 157 | } |
158 | arrange_and_commit(&root_container); | 158 | arrange_windows(&root_container); |
159 | } else { | 159 | } else { |
160 | struct sway_container *c = | 160 | struct sway_container *c = |
161 | config->handler_context.current_container; | 161 | config->handler_context.current_container; |
@@ -169,7 +169,7 @@ struct cmd_results *cmd_gaps(int argc, char **argv) { | |||
169 | c->gaps_outer = total; | 169 | c->gaps_outer = total; |
170 | } | 170 | } |
171 | 171 | ||
172 | arrange_and_commit(c->parent ? c->parent : &root_container); | 172 | arrange_windows(c->parent ? c->parent : &root_container); |
173 | } | 173 | } |
174 | } | 174 | } |
175 | 175 | ||
diff --git a/sway/commands/input.c b/sway/commands/input.c index 678c57c4..5b203ea0 100644 --- a/sway/commands/input.c +++ b/sway/commands/input.c | |||
@@ -20,8 +20,10 @@ static struct cmd_handler input_handlers[] = { | |||
20 | { "pointer_accel", input_cmd_pointer_accel }, | 20 | { "pointer_accel", input_cmd_pointer_accel }, |
21 | { "repeat_delay", input_cmd_repeat_delay }, | 21 | { "repeat_delay", input_cmd_repeat_delay }, |
22 | { "repeat_rate", input_cmd_repeat_rate }, | 22 | { "repeat_rate", input_cmd_repeat_rate }, |
23 | { "scroll_button", input_cmd_scroll_button }, | ||
23 | { "scroll_method", input_cmd_scroll_method }, | 24 | { "scroll_method", input_cmd_scroll_method }, |
24 | { "tap", input_cmd_tap }, | 25 | { "tap", input_cmd_tap }, |
26 | { "tap_button_map", input_cmd_tap_button_map }, | ||
25 | { "xkb_layout", input_cmd_xkb_layout }, | 27 | { "xkb_layout", input_cmd_xkb_layout }, |
26 | { "xkb_model", input_cmd_xkb_model }, | 28 | { "xkb_model", input_cmd_xkb_model }, |
27 | { "xkb_options", input_cmd_xkb_options }, | 29 | { "xkb_options", input_cmd_xkb_options }, |
@@ -35,7 +37,7 @@ struct cmd_results *cmd_input(int argc, char **argv) { | |||
35 | return error; | 37 | return error; |
36 | } | 38 | } |
37 | 39 | ||
38 | wlr_log(L_DEBUG, "entering input block: %s", argv[0]); | 40 | wlr_log(WLR_DEBUG, "entering input block: %s", argv[0]); |
39 | 41 | ||
40 | config->handler_context.input_config = new_input_config(argv[0]); | 42 | config->handler_context.input_config = new_input_config(argv[0]); |
41 | if (!config->handler_context.input_config) { | 43 | if (!config->handler_context.input_config) { |
diff --git a/sway/commands/input/accel_profile.c b/sway/commands/input/accel_profile.c index 37d6e133..a4108ec3 100644 --- a/sway/commands/input/accel_profile.c +++ b/sway/commands/input/accel_profile.c | |||
@@ -23,6 +23,7 @@ struct cmd_results *input_cmd_accel_profile(int argc, char **argv) { | |||
23 | } else if (strcasecmp(argv[0], "flat") == 0) { | 23 | } else if (strcasecmp(argv[0], "flat") == 0) { |
24 | new_config->accel_profile = LIBINPUT_CONFIG_ACCEL_PROFILE_FLAT; | 24 | new_config->accel_profile = LIBINPUT_CONFIG_ACCEL_PROFILE_FLAT; |
25 | } else { | 25 | } else { |
26 | free_input_config(new_config); | ||
26 | return cmd_results_new(CMD_INVALID, "accel_profile", | 27 | return cmd_results_new(CMD_INVALID, "accel_profile", |
27 | "Expected 'accel_profile <adaptive|flat>'"); | 28 | "Expected 'accel_profile <adaptive|flat>'"); |
28 | } | 29 | } |
diff --git a/sway/commands/input/click_method.c b/sway/commands/input/click_method.c index 8f1f0aa7..5d0d8cc2 100644 --- a/sway/commands/input/click_method.c +++ b/sway/commands/input/click_method.c | |||
@@ -26,6 +26,7 @@ struct cmd_results *input_cmd_click_method(int argc, char **argv) { | |||
26 | } else if (strcasecmp(argv[0], "clickfinger") == 0) { | 26 | } else if (strcasecmp(argv[0], "clickfinger") == 0) { |
27 | new_config->click_method = LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER; | 27 | new_config->click_method = LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER; |
28 | } else { | 28 | } else { |
29 | free_input_config(new_config); | ||
29 | return cmd_results_new(CMD_INVALID, "click_method", | 30 | return cmd_results_new(CMD_INVALID, "click_method", |
30 | "Expected 'click_method <none|button_areas|clickfinger'"); | 31 | "Expected 'click_method <none|button_areas|clickfinger'"); |
31 | } | 32 | } |
diff --git a/sway/commands/input/drag_lock.c b/sway/commands/input/drag_lock.c index 8273a7d4..9e32816f 100644 --- a/sway/commands/input/drag_lock.c +++ b/sway/commands/input/drag_lock.c | |||
@@ -23,6 +23,7 @@ struct cmd_results *input_cmd_drag_lock(int argc, char **argv) { | |||
23 | } else if (strcasecmp(argv[0], "disabled") == 0) { | 23 | } else if (strcasecmp(argv[0], "disabled") == 0) { |
24 | new_config->drag_lock = LIBINPUT_CONFIG_DRAG_LOCK_DISABLED; | 24 | new_config->drag_lock = LIBINPUT_CONFIG_DRAG_LOCK_DISABLED; |
25 | } else { | 25 | } else { |
26 | free_input_config(new_config); | ||
26 | return cmd_results_new(CMD_INVALID, "drag_lock", | 27 | return cmd_results_new(CMD_INVALID, "drag_lock", |
27 | "Expected 'drag_lock <enabled|disabled>'"); | 28 | "Expected 'drag_lock <enabled|disabled>'"); |
28 | } | 29 | } |
diff --git a/sway/commands/input/dwt.c b/sway/commands/input/dwt.c index 995a2f47..73937507 100644 --- a/sway/commands/input/dwt.c +++ b/sway/commands/input/dwt.c | |||
@@ -22,6 +22,7 @@ struct cmd_results *input_cmd_dwt(int argc, char **argv) { | |||
22 | } else if (strcasecmp(argv[0], "disabled") == 0) { | 22 | } else if (strcasecmp(argv[0], "disabled") == 0) { |
23 | new_config->dwt = LIBINPUT_CONFIG_DWT_DISABLED; | 23 | new_config->dwt = LIBINPUT_CONFIG_DWT_DISABLED; |
24 | } else { | 24 | } else { |
25 | free_input_config(new_config); | ||
25 | return cmd_results_new(CMD_INVALID, "dwt", | 26 | return cmd_results_new(CMD_INVALID, "dwt", |
26 | "Expected 'dwt <enabled|disabled>'"); | 27 | "Expected 'dwt <enabled|disabled>'"); |
27 | } | 28 | } |
diff --git a/sway/commands/input/events.c b/sway/commands/input/events.c index 2217f5ce..abfe3b12 100644 --- a/sway/commands/input/events.c +++ b/sway/commands/input/events.c | |||
@@ -16,7 +16,7 @@ struct cmd_results *input_cmd_events(int argc, char **argv) { | |||
16 | return cmd_results_new(CMD_FAILURE, "events", | 16 | return cmd_results_new(CMD_FAILURE, "events", |
17 | "No input device defined."); | 17 | "No input device defined."); |
18 | } | 18 | } |
19 | wlr_log(L_DEBUG, "events for device: %s", | 19 | wlr_log(WLR_DEBUG, "events for device: %s", |
20 | current_input_config->identifier); | 20 | current_input_config->identifier); |
21 | struct input_config *new_config = | 21 | struct input_config *new_config = |
22 | new_input_config(current_input_config->identifier); | 22 | new_input_config(current_input_config->identifier); |
@@ -29,6 +29,7 @@ struct cmd_results *input_cmd_events(int argc, char **argv) { | |||
29 | new_config->send_events = | 29 | new_config->send_events = |
30 | LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE; | 30 | LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE; |
31 | } else { | 31 | } else { |
32 | free_input_config(new_config); | ||
32 | return cmd_results_new(CMD_INVALID, "events", | 33 | return cmd_results_new(CMD_INVALID, "events", |
33 | "Expected 'events <enabled|disabled|disabled_on_external_mouse>'"); | 34 | "Expected 'events <enabled|disabled|disabled_on_external_mouse>'"); |
34 | } | 35 | } |
diff --git a/sway/commands/input/left_handed.c b/sway/commands/input/left_handed.c index 94b8e03e..769ce98c 100644 --- a/sway/commands/input/left_handed.c +++ b/sway/commands/input/left_handed.c | |||
@@ -23,6 +23,7 @@ struct cmd_results *input_cmd_left_handed(int argc, char **argv) { | |||
23 | } else if (strcasecmp(argv[0], "disabled") == 0) { | 23 | } else if (strcasecmp(argv[0], "disabled") == 0) { |
24 | new_config->left_handed = 0; | 24 | new_config->left_handed = 0; |
25 | } else { | 25 | } else { |
26 | free_input_config(new_config); | ||
26 | return cmd_results_new(CMD_INVALID, "left_handed", | 27 | return cmd_results_new(CMD_INVALID, "left_handed", |
27 | "Expected 'left_handed <enabled|disabled>'"); | 28 | "Expected 'left_handed <enabled|disabled>'"); |
28 | } | 29 | } |
diff --git a/sway/commands/input/map_from_region.c b/sway/commands/input/map_from_region.c index 80bb856d..40f04214 100644 --- a/sway/commands/input/map_from_region.c +++ b/sway/commands/input/map_from_region.c | |||
@@ -54,20 +54,28 @@ struct cmd_results *input_cmd_map_from_region(int argc, char **argv) { | |||
54 | bool mm1, mm2; | 54 | bool mm1, mm2; |
55 | if (!parse_coords(argv[0], &new_config->mapped_from_region->x1, | 55 | if (!parse_coords(argv[0], &new_config->mapped_from_region->x1, |
56 | &new_config->mapped_from_region->y1, &mm1)) { | 56 | &new_config->mapped_from_region->y1, &mm1)) { |
57 | free(new_config->mapped_from_region); | ||
58 | free_input_config(new_config); | ||
57 | return cmd_results_new(CMD_FAILURE, "map_from_region", | 59 | return cmd_results_new(CMD_FAILURE, "map_from_region", |
58 | "Invalid top-left coordinates"); | 60 | "Invalid top-left coordinates"); |
59 | } | 61 | } |
60 | if (!parse_coords(argv[1], &new_config->mapped_from_region->x2, | 62 | if (!parse_coords(argv[1], &new_config->mapped_from_region->x2, |
61 | &new_config->mapped_from_region->y2, &mm2)) { | 63 | &new_config->mapped_from_region->y2, &mm2)) { |
64 | free(new_config->mapped_from_region); | ||
65 | free_input_config(new_config); | ||
62 | return cmd_results_new(CMD_FAILURE, "map_from_region", | 66 | return cmd_results_new(CMD_FAILURE, "map_from_region", |
63 | "Invalid bottom-right coordinates"); | 67 | "Invalid bottom-right coordinates"); |
64 | } | 68 | } |
65 | if (new_config->mapped_from_region->x1 > new_config->mapped_from_region->x2 || | 69 | if (new_config->mapped_from_region->x1 > new_config->mapped_from_region->x2 || |
66 | new_config->mapped_from_region->y1 > new_config->mapped_from_region->y2) { | 70 | new_config->mapped_from_region->y1 > new_config->mapped_from_region->y2) { |
71 | free(new_config->mapped_from_region); | ||
72 | free_input_config(new_config); | ||
67 | return cmd_results_new(CMD_FAILURE, "map_from_region", | 73 | return cmd_results_new(CMD_FAILURE, "map_from_region", |
68 | "Invalid rectangle"); | 74 | "Invalid rectangle"); |
69 | } | 75 | } |
70 | if (mm1 != mm2) { | 76 | if (mm1 != mm2) { |
77 | free(new_config->mapped_from_region); | ||
78 | free_input_config(new_config); | ||
71 | return cmd_results_new(CMD_FAILURE, "map_from_region", | 79 | return cmd_results_new(CMD_FAILURE, "map_from_region", |
72 | "Both coordinates must be in the same unit"); | 80 | "Both coordinates must be in the same unit"); |
73 | } | 81 | } |
diff --git a/sway/commands/input/middle_emulation.c b/sway/commands/input/middle_emulation.c index a551fd51..7ca01629 100644 --- a/sway/commands/input/middle_emulation.c +++ b/sway/commands/input/middle_emulation.c | |||
@@ -24,6 +24,7 @@ struct cmd_results *input_cmd_middle_emulation(int argc, char **argv) { | |||
24 | new_config->middle_emulation = | 24 | new_config->middle_emulation = |
25 | LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED; | 25 | LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED; |
26 | } else { | 26 | } else { |
27 | free_input_config(new_config); | ||
27 | return cmd_results_new(CMD_INVALID, "middle_emulation", | 28 | return cmd_results_new(CMD_INVALID, "middle_emulation", |
28 | "Expected 'middle_emulation <enabled|disabled>'"); | 29 | "Expected 'middle_emulation <enabled|disabled>'"); |
29 | } | 30 | } |
diff --git a/sway/commands/input/natural_scroll.c b/sway/commands/input/natural_scroll.c index c4e19b78..55236790 100644 --- a/sway/commands/input/natural_scroll.c +++ b/sway/commands/input/natural_scroll.c | |||
@@ -23,6 +23,7 @@ struct cmd_results *input_cmd_natural_scroll(int argc, char **argv) { | |||
23 | } else if (strcasecmp(argv[0], "disabled") == 0) { | 23 | } else if (strcasecmp(argv[0], "disabled") == 0) { |
24 | new_config->natural_scroll = 0; | 24 | new_config->natural_scroll = 0; |
25 | } else { | 25 | } else { |
26 | free_input_config(new_config); | ||
26 | return cmd_results_new(CMD_INVALID, "natural_scroll", | 27 | return cmd_results_new(CMD_INVALID, "natural_scroll", |
27 | "Expected 'natural_scroll <enabled|disabled>'"); | 28 | "Expected 'natural_scroll <enabled|disabled>'"); |
28 | } | 29 | } |
diff --git a/sway/commands/input/pointer_accel.c b/sway/commands/input/pointer_accel.c index 171063aa..8bbd0724 100644 --- a/sway/commands/input/pointer_accel.c +++ b/sway/commands/input/pointer_accel.c | |||
@@ -20,6 +20,7 @@ struct cmd_results *input_cmd_pointer_accel(int argc, char **argv) { | |||
20 | 20 | ||
21 | float pointer_accel = atof(argv[0]); | 21 | float pointer_accel = atof(argv[0]); |
22 | if (pointer_accel < -1 || pointer_accel > 1) { | 22 | if (pointer_accel < -1 || pointer_accel > 1) { |
23 | free_input_config(new_config); | ||
23 | return cmd_results_new(CMD_INVALID, "pointer_accel", | 24 | return cmd_results_new(CMD_INVALID, "pointer_accel", |
24 | "Input out of range [-1, 1]"); | 25 | "Input out of range [-1, 1]"); |
25 | } | 26 | } |
diff --git a/sway/commands/input/repeat_delay.c b/sway/commands/input/repeat_delay.c index ce265841..c9ddbf0e 100644 --- a/sway/commands/input/repeat_delay.c +++ b/sway/commands/input/repeat_delay.c | |||
@@ -20,6 +20,7 @@ struct cmd_results *input_cmd_repeat_delay(int argc, char **argv) { | |||
20 | 20 | ||
21 | int repeat_delay = atoi(argv[0]); | 21 | int repeat_delay = atoi(argv[0]); |
22 | if (repeat_delay < 0) { | 22 | if (repeat_delay < 0) { |
23 | free_input_config(new_config); | ||
23 | return cmd_results_new(CMD_INVALID, "repeat_delay", | 24 | return cmd_results_new(CMD_INVALID, "repeat_delay", |
24 | "Repeat delay cannot be negative"); | 25 | "Repeat delay cannot be negative"); |
25 | } | 26 | } |
diff --git a/sway/commands/input/repeat_rate.c b/sway/commands/input/repeat_rate.c index f2ea2e69..56878176 100644 --- a/sway/commands/input/repeat_rate.c +++ b/sway/commands/input/repeat_rate.c | |||
@@ -20,6 +20,7 @@ struct cmd_results *input_cmd_repeat_rate(int argc, char **argv) { | |||
20 | 20 | ||
21 | int repeat_rate = atoi(argv[0]); | 21 | int repeat_rate = atoi(argv[0]); |
22 | if (repeat_rate < 0) { | 22 | if (repeat_rate < 0) { |
23 | free_input_config(new_config); | ||
23 | return cmd_results_new(CMD_INVALID, "repeat_rate", | 24 | return cmd_results_new(CMD_INVALID, "repeat_rate", |
24 | "Repeat rate cannot be negative"); | 25 | "Repeat rate cannot be negative"); |
25 | } | 26 | } |
diff --git a/sway/commands/input/scroll_button.c b/sway/commands/input/scroll_button.c new file mode 100644 index 00000000..350fcca2 --- /dev/null +++ b/sway/commands/input/scroll_button.c | |||
@@ -0,0 +1,44 @@ | |||
1 | #include <string.h> | ||
2 | #include <strings.h> | ||
3 | #include <errno.h> | ||
4 | #include "sway/config.h" | ||
5 | #include "sway/commands.h" | ||
6 | #include "sway/input/input-manager.h" | ||
7 | |||
8 | struct cmd_results *input_cmd_scroll_button(int argc, char **argv) { | ||
9 | struct cmd_results *error = NULL; | ||
10 | if ((error = checkarg(argc, "scroll_button", EXPECTED_AT_LEAST, 1))) { | ||
11 | return error; | ||
12 | } | ||
13 | struct input_config *current_input_config = | ||
14 | config->handler_context.input_config; | ||
15 | if (!current_input_config) { | ||
16 | return cmd_results_new(CMD_FAILURE, "scroll_button", | ||
17 | "No input device defined."); | ||
18 | } | ||
19 | struct input_config *new_config = | ||
20 | new_input_config(current_input_config->identifier); | ||
21 | |||
22 | errno = 0; | ||
23 | char *endptr; | ||
24 | int scroll_button = strtol(*argv, &endptr, 10); | ||
25 | if (endptr == *argv && scroll_button == 0) { | ||
26 | free_input_config(new_config); | ||
27 | return cmd_results_new(CMD_INVALID, "scroll_button", | ||
28 | "Scroll button identifier must be an integer."); | ||
29 | } | ||
30 | if (errno == ERANGE) { | ||
31 | free_input_config(new_config); | ||
32 | return cmd_results_new(CMD_INVALID, "scroll_button", | ||
33 | "Scroll button identifier out of range."); | ||
34 | } | ||
35 | if (scroll_button < 0) { | ||
36 | free_input_config(new_config); | ||
37 | return cmd_results_new(CMD_INVALID, "scroll_button", | ||
38 | "Scroll button identifier cannot be negative."); | ||
39 | } | ||
40 | new_config->scroll_button = scroll_button; | ||
41 | |||
42 | apply_input_config(new_config); | ||
43 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | ||
44 | } | ||
diff --git a/sway/commands/input/scroll_method.c b/sway/commands/input/scroll_method.c index 0a1c57ac..4c6ac6b6 100644 --- a/sway/commands/input/scroll_method.c +++ b/sway/commands/input/scroll_method.c | |||
@@ -27,6 +27,7 @@ struct cmd_results *input_cmd_scroll_method(int argc, char **argv) { | |||
27 | } else if (strcasecmp(argv[0], "on_button_down") == 0) { | 27 | } else if (strcasecmp(argv[0], "on_button_down") == 0) { |
28 | new_config->scroll_method = LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN; | 28 | new_config->scroll_method = LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN; |
29 | } else { | 29 | } else { |
30 | free_input_config(new_config); | ||
30 | return cmd_results_new(CMD_INVALID, "scroll_method", | 31 | return cmd_results_new(CMD_INVALID, "scroll_method", |
31 | "Expected 'scroll_method <none|two_finger|edge|on_button_down>'"); | 32 | "Expected 'scroll_method <none|two_finger|edge|on_button_down>'"); |
32 | } | 33 | } |
diff --git a/sway/commands/input/tap.c b/sway/commands/input/tap.c index e7f03058..a8d1a10c 100644 --- a/sway/commands/input/tap.c +++ b/sway/commands/input/tap.c | |||
@@ -23,11 +23,12 @@ struct cmd_results *input_cmd_tap(int argc, char **argv) { | |||
23 | } else if (strcasecmp(argv[0], "disabled") == 0) { | 23 | } else if (strcasecmp(argv[0], "disabled") == 0) { |
24 | new_config->tap = LIBINPUT_CONFIG_TAP_DISABLED; | 24 | new_config->tap = LIBINPUT_CONFIG_TAP_DISABLED; |
25 | } else { | 25 | } else { |
26 | free_input_config(new_config); | ||
26 | return cmd_results_new(CMD_INVALID, "tap", | 27 | return cmd_results_new(CMD_INVALID, "tap", |
27 | "Expected 'tap <enabled|disabled>'"); | 28 | "Expected 'tap <enabled|disabled>'"); |
28 | } | 29 | } |
29 | 30 | ||
30 | wlr_log(L_DEBUG, "apply-tap for device: %s", | 31 | wlr_log(WLR_DEBUG, "apply-tap for device: %s", |
31 | current_input_config->identifier); | 32 | current_input_config->identifier); |
32 | apply_input_config(new_config); | 33 | apply_input_config(new_config); |
33 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | 34 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); |
diff --git a/sway/commands/input/tap_button_map.c b/sway/commands/input/tap_button_map.c new file mode 100644 index 00000000..bdbba472 --- /dev/null +++ b/sway/commands/input/tap_button_map.c | |||
@@ -0,0 +1,33 @@ | |||
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 | |||
7 | struct cmd_results *input_cmd_tap_button_map(int argc, char **argv) { | ||
8 | struct cmd_results *error = NULL; | ||
9 | if ((error = checkarg(argc, "tap_button_map", EXPECTED_AT_LEAST, 1))) { | ||
10 | return error; | ||
11 | } | ||
12 | struct input_config *current_input_config = | ||
13 | config->handler_context.input_config; | ||
14 | if (!current_input_config) { | ||
15 | return cmd_results_new(CMD_FAILURE, "tap_button_map", | ||
16 | "No input device defined."); | ||
17 | } | ||
18 | struct input_config *new_config = | ||
19 | new_input_config(current_input_config->identifier); | ||
20 | |||
21 | if (strcasecmp(argv[0], "lrm") == 0) { | ||
22 | new_config->tap_button_map = LIBINPUT_CONFIG_TAP_MAP_LRM; | ||
23 | } else if (strcasecmp(argv[0], "lmr") == 0) { | ||
24 | new_config->tap_button_map = LIBINPUT_CONFIG_TAP_MAP_LMR; | ||
25 | } else { | ||
26 | free_input_config(new_config); | ||
27 | return cmd_results_new(CMD_INVALID, "tap_button_map", | ||
28 | "Expected 'tap_button_map <lrm|lmr>'"); | ||
29 | } | ||
30 | |||
31 | apply_input_config(new_config); | ||
32 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | ||
33 | } | ||
diff --git a/sway/commands/input/xkb_layout.c b/sway/commands/input/xkb_layout.c index 867e65d3..9fa5a344 100644 --- a/sway/commands/input/xkb_layout.c +++ b/sway/commands/input/xkb_layout.c | |||
@@ -19,7 +19,7 @@ struct cmd_results *input_cmd_xkb_layout(int argc, char **argv) { | |||
19 | 19 | ||
20 | new_config->xkb_layout = strdup(argv[0]); | 20 | new_config->xkb_layout = strdup(argv[0]); |
21 | 21 | ||
22 | wlr_log(L_DEBUG, "apply-xkb_layout for device: %s layout: %s", | 22 | wlr_log(WLR_DEBUG, "apply-xkb_layout for device: %s layout: %s", |
23 | current_input_config->identifier, new_config->xkb_layout); | 23 | current_input_config->identifier, new_config->xkb_layout); |
24 | apply_input_config(new_config); | 24 | apply_input_config(new_config); |
25 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | 25 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); |
diff --git a/sway/commands/input/xkb_model.c b/sway/commands/input/xkb_model.c index e8c8e04e..0d082625 100644 --- a/sway/commands/input/xkb_model.c +++ b/sway/commands/input/xkb_model.c | |||
@@ -19,7 +19,7 @@ struct cmd_results *input_cmd_xkb_model(int argc, char **argv) { | |||
19 | 19 | ||
20 | new_config->xkb_model = strdup(argv[0]); | 20 | new_config->xkb_model = strdup(argv[0]); |
21 | 21 | ||
22 | wlr_log(L_DEBUG, "apply-xkb_model for device: %s model: %s", | 22 | wlr_log(WLR_DEBUG, "apply-xkb_model for device: %s model: %s", |
23 | current_input_config->identifier, new_config->xkb_model); | 23 | current_input_config->identifier, new_config->xkb_model); |
24 | apply_input_config(new_config); | 24 | apply_input_config(new_config); |
25 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | 25 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); |
diff --git a/sway/commands/input/xkb_options.c b/sway/commands/input/xkb_options.c index e9ddd6e3..3059d941 100644 --- a/sway/commands/input/xkb_options.c +++ b/sway/commands/input/xkb_options.c | |||
@@ -19,7 +19,7 @@ struct cmd_results *input_cmd_xkb_options(int argc, char **argv) { | |||
19 | 19 | ||
20 | new_config->xkb_options = strdup(argv[0]); | 20 | new_config->xkb_options = strdup(argv[0]); |
21 | 21 | ||
22 | wlr_log(L_DEBUG, "apply-xkb_options for device: %s options: %s", | 22 | wlr_log(WLR_DEBUG, "apply-xkb_options for device: %s options: %s", |
23 | current_input_config->identifier, new_config->xkb_options); | 23 | current_input_config->identifier, new_config->xkb_options); |
24 | apply_input_config(new_config); | 24 | apply_input_config(new_config); |
25 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | 25 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); |
diff --git a/sway/commands/input/xkb_rules.c b/sway/commands/input/xkb_rules.c index 926d0ac1..560f088e 100644 --- a/sway/commands/input/xkb_rules.c +++ b/sway/commands/input/xkb_rules.c | |||
@@ -19,7 +19,7 @@ struct cmd_results *input_cmd_xkb_rules(int argc, char **argv) { | |||
19 | 19 | ||
20 | new_config->xkb_rules = strdup(argv[0]); | 20 | new_config->xkb_rules = strdup(argv[0]); |
21 | 21 | ||
22 | wlr_log(L_DEBUG, "apply-xkb_rules for device: %s rules: %s", | 22 | wlr_log(WLR_DEBUG, "apply-xkb_rules for device: %s rules: %s", |
23 | current_input_config->identifier, new_config->xkb_rules); | 23 | current_input_config->identifier, new_config->xkb_rules); |
24 | apply_input_config(new_config); | 24 | apply_input_config(new_config); |
25 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | 25 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); |
diff --git a/sway/commands/input/xkb_variant.c b/sway/commands/input/xkb_variant.c index 0e3ffd41..0aa03440 100644 --- a/sway/commands/input/xkb_variant.c +++ b/sway/commands/input/xkb_variant.c | |||
@@ -19,7 +19,7 @@ struct cmd_results *input_cmd_xkb_variant(int argc, char **argv) { | |||
19 | 19 | ||
20 | new_config->xkb_variant = strdup(argv[0]); | 20 | new_config->xkb_variant = strdup(argv[0]); |
21 | 21 | ||
22 | wlr_log(L_DEBUG, "apply-xkb_variant for device: %s variant: %s", | 22 | wlr_log(WLR_DEBUG, "apply-xkb_variant for device: %s variant: %s", |
23 | current_input_config->identifier, new_config->xkb_variant); | 23 | current_input_config->identifier, new_config->xkb_variant); |
24 | apply_input_config(new_config); | 24 | apply_input_config(new_config); |
25 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | 25 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); |
diff --git a/sway/commands/layout.c b/sway/commands/layout.c index 9945fa5c..c446f1f9 100644 --- a/sway/commands/layout.c +++ b/sway/commands/layout.c | |||
@@ -49,7 +49,7 @@ struct cmd_results *cmd_layout(int argc, char **argv) { | |||
49 | } | 49 | } |
50 | 50 | ||
51 | container_notify_subtree_changed(parent); | 51 | container_notify_subtree_changed(parent); |
52 | arrange_and_commit(parent); | 52 | arrange_windows(parent); |
53 | 53 | ||
54 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | 54 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); |
55 | } | 55 | } |
diff --git a/sway/commands/mode.c b/sway/commands/mode.c index 00331ccc..b460fcb5 100644 --- a/sway/commands/mode.c +++ b/sway/commands/mode.c | |||
@@ -26,7 +26,17 @@ struct cmd_results *cmd_mode(int argc, char **argv) { | |||
26 | "mode", "Can only be used in config file."); | 26 | "mode", "Can only be used in config file."); |
27 | } | 27 | } |
28 | 28 | ||
29 | const char *mode_name = argv[0]; | 29 | bool pango = strcmp(*argv, "--pango_markup") == 0; |
30 | if (pango) { | ||
31 | argc--; argv++; | ||
32 | if (argc == 0) { | ||
33 | return cmd_results_new(CMD_FAILURE, "mode", | ||
34 | "Mode name is missing"); | ||
35 | } | ||
36 | } | ||
37 | |||
38 | char *mode_name = *argv; | ||
39 | strip_quotes(mode_name); | ||
30 | struct sway_mode *mode = NULL; | 40 | struct sway_mode *mode = NULL; |
31 | // Find mode | 41 | // Find mode |
32 | for (int i = 0; i < config->modes->length; ++i) { | 42 | for (int i = 0; i < config->modes->length; ++i) { |
@@ -46,6 +56,7 @@ struct cmd_results *cmd_mode(int argc, char **argv) { | |||
46 | mode->name = strdup(mode_name); | 56 | mode->name = strdup(mode_name); |
47 | mode->keysym_bindings = create_list(); | 57 | mode->keysym_bindings = create_list(); |
48 | mode->keycode_bindings = create_list(); | 58 | mode->keycode_bindings = create_list(); |
59 | mode->pango = pango; | ||
49 | list_add(config->modes, mode); | 60 | list_add(config->modes, mode); |
50 | } | 61 | } |
51 | if (!mode) { | 62 | if (!mode) { |
@@ -54,13 +65,15 @@ struct cmd_results *cmd_mode(int argc, char **argv) { | |||
54 | return error; | 65 | return error; |
55 | } | 66 | } |
56 | if ((config->reading && argc > 1) || (!config->reading && argc == 1)) { | 67 | if ((config->reading && argc > 1) || (!config->reading && argc == 1)) { |
57 | wlr_log(L_DEBUG, "Switching to mode `%s'",mode->name); | 68 | wlr_log(WLR_DEBUG, "Switching to mode `%s' (pango=%d)", |
69 | mode->name, mode->pango); | ||
58 | } | 70 | } |
59 | // Set current mode | 71 | // Set current mode |
60 | config->current_mode = mode; | 72 | config->current_mode = mode; |
61 | if (argc == 1) { | 73 | if (argc == 1) { |
62 | // trigger IPC mode event | 74 | // trigger IPC mode event |
63 | ipc_event_mode(config->current_mode->name); | 75 | ipc_event_mode(config->current_mode->name, |
76 | config->current_mode->pango); | ||
64 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | 77 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); |
65 | } | 78 | } |
66 | 79 | ||
diff --git a/sway/commands/move.c b/sway/commands/move.c index a4fae388..1940043d 100644 --- a/sway/commands/move.c +++ b/sway/commands/move.c | |||
@@ -1,13 +1,15 @@ | |||
1 | #define _XOPEN_SOURCE 500 | 1 | #define _XOPEN_SOURCE 500 |
2 | #include <string.h> | 2 | #include <string.h> |
3 | #include <strings.h> | 3 | #include <strings.h> |
4 | #include <wlr/types/wlr_cursor.h> | ||
4 | #include <wlr/types/wlr_output.h> | 5 | #include <wlr/types/wlr_output.h> |
5 | #include <wlr/types/wlr_output_layout.h> | 6 | #include <wlr/types/wlr_output_layout.h> |
6 | #include <wlr/util/log.h> | 7 | #include <wlr/util/log.h> |
7 | #include "sway/commands.h" | 8 | #include "sway/commands.h" |
8 | #include "sway/desktop/transaction.h" | 9 | #include "sway/input/cursor.h" |
9 | #include "sway/input/seat.h" | 10 | #include "sway/input/seat.h" |
10 | #include "sway/output.h" | 11 | #include "sway/output.h" |
12 | #include "sway/scratchpad.h" | ||
11 | #include "sway/tree/arrange.h" | 13 | #include "sway/tree/arrange.h" |
12 | #include "sway/tree/container.h" | 14 | #include "sway/tree/container.h" |
13 | #include "sway/tree/layout.h" | 15 | #include "sway/tree/layout.h" |
@@ -103,10 +105,8 @@ static struct cmd_results *cmd_move_container(struct sway_container *current, | |||
103 | // TODO: Ideally we would arrange the surviving parent after reaping, | 105 | // TODO: Ideally we would arrange the surviving parent after reaping, |
104 | // but container_reap_empty does not return it, so we arrange the | 106 | // but container_reap_empty does not return it, so we arrange the |
105 | // workspace instead. | 107 | // workspace instead. |
106 | struct sway_transaction *txn = transaction_create(); | 108 | arrange_windows(old_ws); |
107 | arrange_windows(old_ws, txn); | 109 | arrange_windows(destination->parent); |
108 | arrange_windows(destination->parent, txn); | ||
109 | transaction_commit(txn); | ||
110 | 110 | ||
111 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | 111 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); |
112 | } else if (strcasecmp(argv[1], "to") == 0 | 112 | } else if (strcasecmp(argv[1], "to") == 0 |
@@ -142,10 +142,8 @@ static struct cmd_results *cmd_move_container(struct sway_container *current, | |||
142 | // TODO: Ideally we would arrange the surviving parent after reaping, | 142 | // TODO: Ideally we would arrange the surviving parent after reaping, |
143 | // but container_reap_empty does not return it, so we arrange the | 143 | // but container_reap_empty does not return it, so we arrange the |
144 | // workspace instead. | 144 | // workspace instead. |
145 | struct sway_transaction *txn = transaction_create(); | 145 | arrange_windows(old_ws); |
146 | arrange_windows(old_ws, txn); | 146 | arrange_windows(focus->parent); |
147 | arrange_windows(focus->parent, txn); | ||
148 | transaction_commit(txn); | ||
149 | 147 | ||
150 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | 148 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); |
151 | } | 149 | } |
@@ -175,20 +173,56 @@ static struct cmd_results *cmd_move_workspace(struct sway_container *current, | |||
175 | } | 173 | } |
176 | container_move_to(current, destination); | 174 | container_move_to(current, destination); |
177 | 175 | ||
178 | struct sway_transaction *txn = transaction_create(); | 176 | arrange_windows(source); |
179 | arrange_windows(source, txn); | 177 | arrange_windows(destination); |
180 | arrange_windows(destination, txn); | ||
181 | transaction_commit(txn); | ||
182 | 178 | ||
183 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | 179 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); |
184 | } | 180 | } |
185 | 181 | ||
186 | static struct cmd_results *move_in_direction(struct sway_container *container, | 182 | static struct cmd_results *move_in_direction(struct sway_container *container, |
187 | enum movement_direction direction, int move_amt) { | 183 | enum movement_direction direction, int argc, char **argv) { |
184 | int move_amt = 10; | ||
185 | if (argc > 1) { | ||
186 | char *inv; | ||
187 | move_amt = (int)strtol(argv[1], &inv, 10); | ||
188 | if (*inv != '\0' && strcasecmp(inv, "px") != 0) { | ||
189 | return cmd_results_new(CMD_FAILURE, "move", | ||
190 | "Invalid distance specified"); | ||
191 | } | ||
192 | } | ||
193 | |||
188 | if (container->type == C_WORKSPACE) { | 194 | if (container->type == C_WORKSPACE) { |
189 | return cmd_results_new(CMD_FAILURE, "move", | 195 | return cmd_results_new(CMD_FAILURE, "move", |
190 | "Cannot move workspaces in a direction"); | 196 | "Cannot move workspaces in a direction"); |
191 | } | 197 | } |
198 | if (container_is_floating(container)) { | ||
199 | if (container->type == C_VIEW && container->sway_view->is_fullscreen) { | ||
200 | return cmd_results_new(CMD_FAILURE, "move", | ||
201 | "Cannot move fullscreen floating container"); | ||
202 | } | ||
203 | double lx = container->x; | ||
204 | double ly = container->y; | ||
205 | switch (direction) { | ||
206 | case MOVE_LEFT: | ||
207 | lx -= move_amt; | ||
208 | break; | ||
209 | case MOVE_RIGHT: | ||
210 | lx += move_amt; | ||
211 | break; | ||
212 | case MOVE_UP: | ||
213 | ly -= move_amt; | ||
214 | break; | ||
215 | case MOVE_DOWN: | ||
216 | ly += move_amt; | ||
217 | break; | ||
218 | case MOVE_PARENT: | ||
219 | case MOVE_CHILD: | ||
220 | return cmd_results_new(CMD_FAILURE, "move", | ||
221 | "Cannot move floating container to parent or child"); | ||
222 | } | ||
223 | container_floating_move_to(container, lx, ly); | ||
224 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | ||
225 | } | ||
192 | // For simplicity, we'll arrange the entire workspace. The reason for this | 226 | // For simplicity, we'll arrange the entire workspace. The reason for this |
193 | // is moving the container might reap the old parent, and container_move | 227 | // is moving the container might reap the old parent, and container_move |
194 | // does not return a surviving parent. | 228 | // does not return a surviving parent. |
@@ -198,54 +232,112 @@ static struct cmd_results *move_in_direction(struct sway_container *container, | |||
198 | container_move(container, direction, move_amt); | 232 | container_move(container, direction, move_amt); |
199 | struct sway_container *new_ws = container_parent(container, C_WORKSPACE); | 233 | struct sway_container *new_ws = container_parent(container, C_WORKSPACE); |
200 | 234 | ||
201 | struct sway_transaction *txn = transaction_create(); | 235 | arrange_windows(old_ws); |
202 | arrange_windows(old_ws, txn); | ||
203 | if (new_ws != old_ws) { | 236 | if (new_ws != old_ws) { |
204 | arrange_windows(new_ws, txn); | 237 | arrange_windows(new_ws); |
205 | } | 238 | } |
206 | transaction_commit(txn); | ||
207 | 239 | ||
208 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | 240 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); |
209 | } | 241 | } |
210 | 242 | ||
243 | static const char* expected_position_syntax = | ||
244 | "Expected 'move [absolute] position <x> <y>' or " | ||
245 | "'move [absolute] position mouse'"; | ||
246 | |||
247 | static struct cmd_results *move_to_position(struct sway_container *container, | ||
248 | int argc, char **argv) { | ||
249 | if (!container_is_floating(container)) { | ||
250 | return cmd_results_new(CMD_FAILURE, "move", | ||
251 | "Only floating containers " | ||
252 | "can be moved to an absolute position"); | ||
253 | } | ||
254 | if (!argc) { | ||
255 | return cmd_results_new(CMD_FAILURE, "move", expected_position_syntax); | ||
256 | } | ||
257 | if (strcmp(argv[0], "absolute") == 0) { | ||
258 | --argc; | ||
259 | ++argv; | ||
260 | } | ||
261 | if (!argc) { | ||
262 | return cmd_results_new(CMD_FAILURE, "move", expected_position_syntax); | ||
263 | } | ||
264 | if (strcmp(argv[0], "position") == 0) { | ||
265 | --argc; | ||
266 | ++argv; | ||
267 | } | ||
268 | if (!argc) { | ||
269 | return cmd_results_new(CMD_FAILURE, "move", expected_position_syntax); | ||
270 | } | ||
271 | if (strcmp(argv[0], "mouse") == 0) { | ||
272 | struct sway_seat *seat = config->handler_context.seat; | ||
273 | if (!seat->cursor) { | ||
274 | return cmd_results_new(CMD_FAILURE, "move", "No cursor device"); | ||
275 | } | ||
276 | double lx = seat->cursor->cursor->x - container->width / 2; | ||
277 | double ly = seat->cursor->cursor->y - container->height / 2; | ||
278 | container_floating_move_to(container, lx, ly); | ||
279 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | ||
280 | } | ||
281 | if (argc != 2) { | ||
282 | return cmd_results_new(CMD_FAILURE, "move", expected_position_syntax); | ||
283 | } | ||
284 | double lx, ly; | ||
285 | char *inv; | ||
286 | lx = (double)strtol(argv[0], &inv, 10); | ||
287 | if (*inv != '\0' && strcasecmp(inv, "px") != 0) { | ||
288 | return cmd_results_new(CMD_FAILURE, "move", | ||
289 | "Invalid position specified"); | ||
290 | } | ||
291 | ly = (double)strtol(argv[1], &inv, 10); | ||
292 | if (*inv != '\0' && strcasecmp(inv, "px") != 0) { | ||
293 | return cmd_results_new(CMD_FAILURE, "move", | ||
294 | "Invalid position specified"); | ||
295 | } | ||
296 | container_floating_move_to(container, lx, ly); | ||
297 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | ||
298 | } | ||
299 | |||
300 | static struct cmd_results *move_to_scratchpad(struct sway_container *con) { | ||
301 | if (con->type != C_CONTAINER && con->type != C_VIEW) { | ||
302 | return cmd_results_new(CMD_INVALID, "move", | ||
303 | "Only views and containers can be moved to the scratchpad"); | ||
304 | } | ||
305 | if (con->scratchpad) { | ||
306 | return cmd_results_new(CMD_INVALID, "move", | ||
307 | "Container is already in the scratchpad"); | ||
308 | } | ||
309 | scratchpad_add_container(con); | ||
310 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | ||
311 | } | ||
312 | |||
211 | struct cmd_results *cmd_move(int argc, char **argv) { | 313 | struct cmd_results *cmd_move(int argc, char **argv) { |
212 | struct cmd_results *error = NULL; | 314 | struct cmd_results *error = NULL; |
213 | int move_amt = 10; | ||
214 | if ((error = checkarg(argc, "move", EXPECTED_AT_LEAST, 1))) { | 315 | if ((error = checkarg(argc, "move", EXPECTED_AT_LEAST, 1))) { |
215 | return error; | 316 | return error; |
216 | } | 317 | } |
217 | struct sway_container *current = config->handler_context.current_container; | 318 | struct sway_container *current = config->handler_context.current_container; |
218 | 319 | ||
219 | if (argc == 2 || (argc == 3 && strcasecmp(argv[2], "px") == 0)) { | ||
220 | char *inv; | ||
221 | move_amt = (int)strtol(argv[1], &inv, 10); | ||
222 | if (*inv != '\0' && strcasecmp(inv, "px") != 0) { | ||
223 | return cmd_results_new(CMD_FAILURE, "move", | ||
224 | "Invalid distance specified"); | ||
225 | } | ||
226 | } | ||
227 | |||
228 | if (strcasecmp(argv[0], "left") == 0) { | 320 | if (strcasecmp(argv[0], "left") == 0) { |
229 | return move_in_direction(current, MOVE_LEFT, move_amt); | 321 | return move_in_direction(current, MOVE_LEFT, argc, argv); |
230 | } else if (strcasecmp(argv[0], "right") == 0) { | 322 | } else if (strcasecmp(argv[0], "right") == 0) { |
231 | return move_in_direction(current, MOVE_RIGHT, move_amt); | 323 | return move_in_direction(current, MOVE_RIGHT, argc, argv); |
232 | } else if (strcasecmp(argv[0], "up") == 0) { | 324 | } else if (strcasecmp(argv[0], "up") == 0) { |
233 | return move_in_direction(current, MOVE_UP, move_amt); | 325 | return move_in_direction(current, MOVE_UP, argc, argv); |
234 | } else if (strcasecmp(argv[0], "down") == 0) { | 326 | } else if (strcasecmp(argv[0], "down") == 0) { |
235 | return move_in_direction(current, MOVE_DOWN, move_amt); | 327 | return move_in_direction(current, MOVE_DOWN, argc, argv); |
236 | } else if (strcasecmp(argv[0], "container") == 0 | 328 | } else if (strcasecmp(argv[0], "container") == 0 |
237 | || strcasecmp(argv[0], "window") == 0) { | 329 | || strcasecmp(argv[0], "window") == 0) { |
238 | return cmd_move_container(current, argc, argv); | 330 | return cmd_move_container(current, argc, argv); |
239 | } else if (strcasecmp(argv[0], "workspace") == 0) { | 331 | } else if (strcasecmp(argv[0], "workspace") == 0) { |
240 | return cmd_move_workspace(current, argc, argv); | 332 | return cmd_move_workspace(current, argc, argv); |
241 | } else if (strcasecmp(argv[0], "scratchpad") == 0 | 333 | } else if (strcasecmp(argv[0], "scratchpad") == 0 |
242 | || (strcasecmp(argv[0], "to") == 0 | 334 | || (strcasecmp(argv[0], "to") == 0 && argc == 2 |
243 | && strcasecmp(argv[1], "scratchpad") == 0)) { | 335 | && strcasecmp(argv[1], "scratchpad") == 0)) { |
244 | // TODO: scratchpad | 336 | return move_to_scratchpad(current); |
245 | return cmd_results_new(CMD_FAILURE, "move", "Unimplemented"); | ||
246 | } else if (strcasecmp(argv[0], "position") == 0) { | 337 | } else if (strcasecmp(argv[0], "position") == 0) { |
247 | // TODO: floating | 338 | return move_to_position(current, argc, argv); |
248 | return cmd_results_new(CMD_FAILURE, "move", "Unimplemented"); | 339 | } else if (strcasecmp(argv[0], "absolute") == 0) { |
340 | return move_to_position(current, argc, argv); | ||
249 | } else { | 341 | } else { |
250 | return cmd_results_new(CMD_INVALID, "move", expected_syntax); | 342 | return cmd_results_new(CMD_INVALID, "move", expected_syntax); |
251 | } | 343 | } |
diff --git a/sway/commands/no_focus.c b/sway/commands/no_focus.c new file mode 100644 index 00000000..61a8de7e --- /dev/null +++ b/sway/commands/no_focus.c | |||
@@ -0,0 +1,26 @@ | |||
1 | #define _XOPEN_SOURCE 500 | ||
2 | #include <string.h> | ||
3 | #include "sway/commands.h" | ||
4 | #include "sway/criteria.h" | ||
5 | #include "list.h" | ||
6 | #include "log.h" | ||
7 | |||
8 | struct cmd_results *cmd_no_focus(int argc, char **argv) { | ||
9 | struct cmd_results *error = NULL; | ||
10 | if ((error = checkarg(argc, "no_focus", EXPECTED_AT_LEAST, 1))) { | ||
11 | return error; | ||
12 | } | ||
13 | |||
14 | char *err_str = NULL; | ||
15 | struct criteria *criteria = criteria_parse(argv[0], &err_str); | ||
16 | if (!criteria) { | ||
17 | error = cmd_results_new(CMD_INVALID, "no_focus", err_str); | ||
18 | free(err_str); | ||
19 | return error; | ||
20 | } | ||
21 | |||
22 | criteria->type = CT_NO_FOCUS; | ||
23 | list_add(config->criteria, criteria); | ||
24 | |||
25 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | ||
26 | } | ||
diff --git a/sway/commands/output.c b/sway/commands/output.c index f955bf90..ef1b7a69 100644 --- a/sway/commands/output.c +++ b/sway/commands/output.c | |||
@@ -29,7 +29,7 @@ struct cmd_results *cmd_output(int argc, char **argv) { | |||
29 | 29 | ||
30 | struct output_config *output = new_output_config(argv[0]); | 30 | struct output_config *output = new_output_config(argv[0]); |
31 | if (!output) { | 31 | if (!output) { |
32 | wlr_log(L_ERROR, "Failed to allocate output config"); | 32 | wlr_log(WLR_ERROR, "Failed to allocate output config"); |
33 | return NULL; | 33 | return NULL; |
34 | } | 34 | } |
35 | argc--; argv++; | 35 | argc--; argv++; |
@@ -60,53 +60,13 @@ struct cmd_results *cmd_output(int argc, char **argv) { | |||
60 | config->handler_context.leftovers.argc = 0; | 60 | config->handler_context.leftovers.argc = 0; |
61 | config->handler_context.leftovers.argv = NULL; | 61 | config->handler_context.leftovers.argv = NULL; |
62 | 62 | ||
63 | int i = list_seq_find(config->output_configs, output_name_cmp, output->name); | 63 | output = store_output_config(output); |
64 | if (i >= 0) { | ||
65 | // Merge existing config | ||
66 | struct output_config *current = config->output_configs->items[i]; | ||
67 | merge_output_config(current, output); | ||
68 | free_output_config(output); | ||
69 | output = current; | ||
70 | } else { | ||
71 | list_add(config->output_configs, output); | ||
72 | } | ||
73 | |||
74 | wlr_log(L_DEBUG, "Config stored for output %s (enabled: %d) (%dx%d@%fHz " | ||
75 | "position %d,%d scale %f transform %d) (bg %s %s) (dpms %d)", | ||
76 | output->name, output->enabled, output->width, output->height, | ||
77 | output->refresh_rate, output->x, output->y, output->scale, | ||
78 | output->transform, output->background, output->background_option, output->dpms_state); | ||
79 | |||
80 | // Try to find the output container and apply configuration now. If | ||
81 | // this is during startup then there will be no container and config | ||
82 | // will be applied during normal "new output" event from wlroots. | ||
83 | char identifier[128]; | ||
84 | bool all = strcmp(output->name, "*") == 0; | ||
85 | struct sway_output *sway_output; | ||
86 | wl_list_for_each(sway_output, &root_container.sway_root->outputs, link) { | ||
87 | output_get_identifier(identifier, sizeof(identifier), sway_output); | ||
88 | wlr_log(L_DEBUG, "Checking identifier %s", identifier); | ||
89 | if (all || strcmp(sway_output->wlr_output->name, output->name) == 0 | ||
90 | || strcmp(identifier, output->name) == 0) { | ||
91 | if (!sway_output->swayc) { | ||
92 | if (!output->enabled) { | ||
93 | if (!all) { | ||
94 | break; | ||
95 | } | ||
96 | continue; | ||
97 | } | ||
98 | 64 | ||
99 | output_enable(sway_output); | 65 | // If reloading, the output configs will be applied after reading the |
100 | } | 66 | // entire config and before the deferred commands so that an auto generated |
101 | 67 | // workspace name is not given to re-enabled outputs. | |
102 | apply_output_config(output, sway_output->swayc); | 68 | if (!config->reloading) { |
103 | 69 | apply_output_config_to_outputs(output); | |
104 | if (!all) { | ||
105 | // Stop looking if the output config isn't applicable to all | ||
106 | // outputs | ||
107 | break; | ||
108 | } | ||
109 | } | ||
110 | } | 70 | } |
111 | 71 | ||
112 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | 72 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); |
diff --git a/sway/commands/output/background.c b/sway/commands/output/background.c index 55cbdff0..4ed56c2a 100644 --- a/sway/commands/output/background.c +++ b/sway/commands/output/background.c | |||
@@ -72,7 +72,7 @@ struct cmd_results *output_cmd_background(int argc, char **argv) { | |||
72 | src = strdup(p.we_wordv[0]); | 72 | src = strdup(p.we_wordv[0]); |
73 | wordfree(&p); | 73 | wordfree(&p); |
74 | if (!src) { | 74 | if (!src) { |
75 | wlr_log(L_ERROR, "Failed to duplicate string"); | 75 | wlr_log(WLR_ERROR, "Failed to duplicate string"); |
76 | return cmd_results_new(CMD_FAILURE, "output", | 76 | return cmd_results_new(CMD_FAILURE, "output", |
77 | "Unable to allocate resource"); | 77 | "Unable to allocate resource"); |
78 | } | 78 | } |
@@ -80,9 +80,10 @@ struct cmd_results *output_cmd_background(int argc, char **argv) { | |||
80 | if (config->reading && *src != '/') { | 80 | if (config->reading && *src != '/') { |
81 | // src file is inside configuration dir | 81 | // src file is inside configuration dir |
82 | 82 | ||
83 | char *conf = strdup(config->current_config); | 83 | char *conf = strdup(config->current_config_path); |
84 | if(!conf) { | 84 | if (!conf) { |
85 | wlr_log(L_ERROR, "Failed to duplicate string"); | 85 | wlr_log(WLR_ERROR, "Failed to duplicate string"); |
86 | free(src); | ||
86 | return cmd_results_new(CMD_FAILURE, "output", | 87 | return cmd_results_new(CMD_FAILURE, "output", |
87 | "Unable to allocate resources"); | 88 | "Unable to allocate resources"); |
88 | } | 89 | } |
@@ -93,7 +94,7 @@ struct cmd_results *output_cmd_background(int argc, char **argv) { | |||
93 | if (!src) { | 94 | if (!src) { |
94 | free(rel_path); | 95 | free(rel_path); |
95 | free(conf); | 96 | free(conf); |
96 | wlr_log(L_ERROR, "Unable to allocate memory"); | 97 | wlr_log(WLR_ERROR, "Unable to allocate memory"); |
97 | return cmd_results_new(CMD_FAILURE, "output", | 98 | return cmd_results_new(CMD_FAILURE, "output", |
98 | "Unable to allocate resources"); | 99 | "Unable to allocate resources"); |
99 | } | 100 | } |
diff --git a/sway/commands/output/mode.c b/sway/commands/output/mode.c index daec6d44..ef56ae9e 100644 --- a/sway/commands/output/mode.c +++ b/sway/commands/output/mode.c | |||
@@ -36,11 +36,11 @@ struct cmd_results *output_cmd_mode(int argc, char **argv) { | |||
36 | } | 36 | } |
37 | } else { | 37 | } else { |
38 | // Format is 1234 4321 | 38 | // Format is 1234 4321 |
39 | argc--; argv++; | ||
39 | if (!argc) { | 40 | if (!argc) { |
40 | return cmd_results_new(CMD_INVALID, "output", | 41 | return cmd_results_new(CMD_INVALID, "output", |
41 | "Missing mode argument (height)."); | 42 | "Missing mode argument (height)."); |
42 | } | 43 | } |
43 | argc--; argv++; | ||
44 | output->height = strtol(*argv, &end, 10); | 44 | output->height = strtol(*argv, &end, 10); |
45 | if (*end) { | 45 | if (*end) { |
46 | return cmd_results_new(CMD_INVALID, "output", | 46 | return cmd_results_new(CMD_INVALID, "output", |
diff --git a/sway/commands/output/position.c b/sway/commands/output/position.c index c2aeb281..449767b1 100644 --- a/sway/commands/output/position.c +++ b/sway/commands/output/position.c | |||
@@ -27,11 +27,11 @@ struct cmd_results *output_cmd_position(int argc, char **argv) { | |||
27 | } | 27 | } |
28 | } else { | 28 | } else { |
29 | // Format is 1234 4321 (legacy) | 29 | // Format is 1234 4321 (legacy) |
30 | argc--; argv++; | ||
30 | if (!argc) { | 31 | if (!argc) { |
31 | return cmd_results_new(CMD_INVALID, "output", | 32 | return cmd_results_new(CMD_INVALID, "output", |
32 | "Missing position argument (y)."); | 33 | "Missing position argument (y)."); |
33 | } | 34 | } |
34 | argc--; argv++; | ||
35 | config->handler_context.output_config->y = strtol(*argv, &end, 10); | 35 | config->handler_context.output_config->y = strtol(*argv, &end, 10); |
36 | if (*end) { | 36 | if (*end) { |
37 | return cmd_results_new(CMD_INVALID, "output", | 37 | return cmd_results_new(CMD_INVALID, "output", |
diff --git a/sway/commands/reload.c b/sway/commands/reload.c index 9fc213c4..cea6a94b 100644 --- a/sway/commands/reload.c +++ b/sway/commands/reload.c | |||
@@ -7,11 +7,11 @@ struct cmd_results *cmd_reload(int argc, char **argv) { | |||
7 | if ((error = checkarg(argc, "reload", EXPECTED_EQUAL_TO, 0))) { | 7 | if ((error = checkarg(argc, "reload", EXPECTED_EQUAL_TO, 0))) { |
8 | return error; | 8 | return error; |
9 | } | 9 | } |
10 | if (!load_main_config(config->current_config, true)) { | 10 | if (!load_main_config(config->current_config_path, true)) { |
11 | return cmd_results_new(CMD_FAILURE, "reload", "Error(s) reloading config."); | 11 | return cmd_results_new(CMD_FAILURE, "reload", "Error(s) reloading config."); |
12 | } | 12 | } |
13 | 13 | ||
14 | load_swaybars(); | 14 | load_swaybars(); |
15 | arrange_and_commit(&root_container); | 15 | arrange_windows(&root_container); |
16 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | 16 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); |
17 | } | 17 | } |
diff --git a/sway/commands/rename.c b/sway/commands/rename.c index 104a3392..a380ff9c 100644 --- a/sway/commands/rename.c +++ b/sway/commands/rename.c | |||
@@ -68,7 +68,7 @@ struct cmd_results *cmd_rename(int argc, char **argv) { | |||
68 | "Workspace already exists"); | 68 | "Workspace already exists"); |
69 | } | 69 | } |
70 | 70 | ||
71 | wlr_log(L_DEBUG, "renaming workspace '%s' to '%s'", workspace->name, new_name); | 71 | wlr_log(WLR_DEBUG, "renaming workspace '%s' to '%s'", workspace->name, new_name); |
72 | free(workspace->name); | 72 | free(workspace->name); |
73 | workspace->name = new_name; | 73 | workspace->name = new_name; |
74 | 74 | ||
diff --git a/sway/commands/resize.c b/sway/commands/resize.c index 6357343e..e657864c 100644 --- a/sway/commands/resize.c +++ b/sway/commands/resize.c | |||
@@ -1,4 +1,5 @@ | |||
1 | #include <errno.h> | 1 | #include <errno.h> |
2 | #include <limits.h> | ||
2 | #include <math.h> | 3 | #include <math.h> |
3 | #include <stdbool.h> | 4 | #include <stdbool.h> |
4 | #include <stdlib.h> | 5 | #include <stdlib.h> |
@@ -7,6 +8,7 @@ | |||
7 | #include <wlr/util/log.h> | 8 | #include <wlr/util/log.h> |
8 | #include "sway/commands.h" | 9 | #include "sway/commands.h" |
9 | #include "sway/tree/arrange.h" | 10 | #include "sway/tree/arrange.h" |
11 | #include "sway/tree/view.h" | ||
10 | #include "log.h" | 12 | #include "log.h" |
11 | 13 | ||
12 | static const int MIN_SANE_W = 100, MIN_SANE_H = 60; | 14 | static const int MIN_SANE_W = 100, MIN_SANE_H = 60; |
@@ -21,9 +23,18 @@ enum resize_unit { | |||
21 | enum resize_axis { | 23 | enum resize_axis { |
22 | RESIZE_AXIS_HORIZONTAL, | 24 | RESIZE_AXIS_HORIZONTAL, |
23 | RESIZE_AXIS_VERTICAL, | 25 | RESIZE_AXIS_VERTICAL, |
26 | RESIZE_AXIS_UP, | ||
27 | RESIZE_AXIS_DOWN, | ||
28 | RESIZE_AXIS_LEFT, | ||
29 | RESIZE_AXIS_RIGHT, | ||
24 | RESIZE_AXIS_INVALID, | 30 | RESIZE_AXIS_INVALID, |
25 | }; | 31 | }; |
26 | 32 | ||
33 | struct resize_amount { | ||
34 | int amount; | ||
35 | enum resize_unit unit; | ||
36 | }; | ||
37 | |||
27 | static enum resize_unit parse_resize_unit(const char *unit) { | 38 | static enum resize_unit parse_resize_unit(const char *unit) { |
28 | if (strcasecmp(unit, "px") == 0) { | 39 | if (strcasecmp(unit, "px") == 0) { |
29 | return RESIZE_UNIT_PX; | 40 | return RESIZE_UNIT_PX; |
@@ -37,6 +48,69 @@ static enum resize_unit parse_resize_unit(const char *unit) { | |||
37 | return RESIZE_UNIT_INVALID; | 48 | return RESIZE_UNIT_INVALID; |
38 | } | 49 | } |
39 | 50 | ||
51 | // Parse arguments such as "10", "10px" or "10 px". | ||
52 | // Returns the number of arguments consumed. | ||
53 | static int parse_resize_amount(int argc, char **argv, | ||
54 | struct resize_amount *amount) { | ||
55 | char *err; | ||
56 | amount->amount = (int)strtol(argv[0], &err, 10); | ||
57 | if (*err) { | ||
58 | // e.g. 10px | ||
59 | amount->unit = parse_resize_unit(err); | ||
60 | return 1; | ||
61 | } | ||
62 | if (argc == 1) { | ||
63 | amount->unit = RESIZE_UNIT_DEFAULT; | ||
64 | return 1; | ||
65 | } | ||
66 | // Try the second argument | ||
67 | amount->unit = parse_resize_unit(argv[1]); | ||
68 | if (amount->unit == RESIZE_UNIT_INVALID) { | ||
69 | amount->unit = RESIZE_UNIT_DEFAULT; | ||
70 | return 1; | ||
71 | } | ||
72 | return 2; | ||
73 | } | ||
74 | |||
75 | static void calculate_constraints(int *min_width, int *max_width, | ||
76 | int *min_height, int *max_height) { | ||
77 | struct sway_container *con = config->handler_context.current_container; | ||
78 | |||
79 | if (config->floating_minimum_width == -1) { // no minimum | ||
80 | *min_width = 0; | ||
81 | } else if (config->floating_minimum_width == 0) { // automatic | ||
82 | *min_width = 75; | ||
83 | } else { | ||
84 | *min_width = config->floating_minimum_width; | ||
85 | } | ||
86 | |||
87 | if (config->floating_minimum_height == -1) { // no minimum | ||
88 | *min_height = 0; | ||
89 | } else if (config->floating_minimum_height == 0) { // automatic | ||
90 | *min_height = 50; | ||
91 | } else { | ||
92 | *min_height = config->floating_minimum_height; | ||
93 | } | ||
94 | |||
95 | if (config->floating_maximum_width == -1) { // no maximum | ||
96 | *max_width = INT_MAX; | ||
97 | } else if (config->floating_maximum_width == 0) { // automatic | ||
98 | struct sway_container *ws = container_parent(con, C_WORKSPACE); | ||
99 | *max_width = ws->width; | ||
100 | } else { | ||
101 | *max_width = config->floating_maximum_width; | ||
102 | } | ||
103 | |||
104 | if (config->floating_maximum_height == -1) { // no maximum | ||
105 | *max_height = INT_MAX; | ||
106 | } else if (config->floating_maximum_height == 0) { // automatic | ||
107 | struct sway_container *ws = container_parent(con, C_WORKSPACE); | ||
108 | *max_height = ws->height; | ||
109 | } else { | ||
110 | *max_height = config->floating_maximum_height; | ||
111 | } | ||
112 | } | ||
113 | |||
40 | static enum resize_axis parse_resize_axis(const char *axis) { | 114 | static enum resize_axis parse_resize_axis(const char *axis) { |
41 | if (strcasecmp(axis, "width") == 0 || strcasecmp(axis, "horizontal") == 0) { | 115 | if (strcasecmp(axis, "width") == 0 || strcasecmp(axis, "horizontal") == 0) { |
42 | return RESIZE_AXIS_HORIZONTAL; | 116 | return RESIZE_AXIS_HORIZONTAL; |
@@ -44,6 +118,18 @@ static enum resize_axis parse_resize_axis(const char *axis) { | |||
44 | if (strcasecmp(axis, "height") == 0 || strcasecmp(axis, "vertical") == 0) { | 118 | if (strcasecmp(axis, "height") == 0 || strcasecmp(axis, "vertical") == 0) { |
45 | return RESIZE_AXIS_VERTICAL; | 119 | return RESIZE_AXIS_VERTICAL; |
46 | } | 120 | } |
121 | if (strcasecmp(axis, "up") == 0) { | ||
122 | return RESIZE_AXIS_UP; | ||
123 | } | ||
124 | if (strcasecmp(axis, "down") == 0) { | ||
125 | return RESIZE_AXIS_DOWN; | ||
126 | } | ||
127 | if (strcasecmp(axis, "left") == 0) { | ||
128 | return RESIZE_AXIS_LEFT; | ||
129 | } | ||
130 | if (strcasecmp(axis, "right") == 0) { | ||
131 | return RESIZE_AXIS_RIGHT; | ||
132 | } | ||
47 | return RESIZE_AXIS_INVALID; | 133 | return RESIZE_AXIS_INVALID; |
48 | } | 134 | } |
49 | 135 | ||
@@ -95,7 +181,7 @@ static void resize_tiled(int amount, enum resize_axis axis) { | |||
95 | return; | 181 | return; |
96 | } | 182 | } |
97 | 183 | ||
98 | wlr_log(L_DEBUG, | 184 | wlr_log(WLR_DEBUG, |
99 | "Found the proper parent: %p. It has %d l conts, and %d r conts", | 185 | "Found the proper parent: %p. It has %d l conts, and %d r conts", |
100 | parent->parent, minor_weight, major_weight); | 186 | parent->parent, minor_weight, major_weight); |
101 | 187 | ||
@@ -182,105 +268,315 @@ static void resize_tiled(int amount, enum resize_axis axis) { | |||
182 | } | 268 | } |
183 | } | 269 | } |
184 | 270 | ||
185 | arrange_and_commit(parent->parent); | 271 | arrange_windows(parent->parent); |
186 | } | 272 | } |
187 | 273 | ||
188 | static void resize(int amount, enum resize_axis axis, enum resize_unit unit) { | 274 | /** |
189 | struct sway_container *current = config->handler_context.current_container; | 275 | * Implement `resize <grow|shrink>` for a floating container. |
190 | if (unit == RESIZE_UNIT_DEFAULT) { | 276 | */ |
191 | // Default for tiling; TODO floating should be px | 277 | static struct cmd_results *resize_adjust_floating(enum resize_axis axis, |
192 | unit = RESIZE_UNIT_PPT; | 278 | struct resize_amount *amount) { |
279 | struct sway_container *con = config->handler_context.current_container; | ||
280 | int grow_width = 0, grow_height = 0; | ||
281 | switch (axis) { | ||
282 | case RESIZE_AXIS_HORIZONTAL: | ||
283 | case RESIZE_AXIS_LEFT: | ||
284 | case RESIZE_AXIS_RIGHT: | ||
285 | grow_width = amount->amount; | ||
286 | break; | ||
287 | case RESIZE_AXIS_VERTICAL: | ||
288 | case RESIZE_AXIS_UP: | ||
289 | case RESIZE_AXIS_DOWN: | ||
290 | grow_height = amount->amount; | ||
291 | break; | ||
292 | case RESIZE_AXIS_INVALID: | ||
293 | return cmd_results_new(CMD_INVALID, "resize", "Invalid axis/direction"); | ||
294 | } | ||
295 | // Make sure we're not adjusting beyond floating min/max size | ||
296 | int min_width, max_width, min_height, max_height; | ||
297 | calculate_constraints(&min_width, &max_width, &min_height, &max_height); | ||
298 | if (con->width + grow_width < min_width) { | ||
299 | grow_width = min_width - con->width; | ||
300 | } else if (con->width + grow_width > max_width) { | ||
301 | grow_width = max_width - con->width; | ||
193 | } | 302 | } |
303 | if (con->height + grow_height < min_height) { | ||
304 | grow_height = min_height - con->height; | ||
305 | } else if (con->height + grow_height > max_height) { | ||
306 | grow_height = max_height - con->height; | ||
307 | } | ||
308 | int grow_x = 0, grow_y = 0; | ||
309 | switch (axis) { | ||
310 | case RESIZE_AXIS_HORIZONTAL: | ||
311 | grow_x = -grow_width / 2; | ||
312 | break; | ||
313 | case RESIZE_AXIS_VERTICAL: | ||
314 | grow_y = -grow_height / 2; | ||
315 | break; | ||
316 | case RESIZE_AXIS_UP: | ||
317 | grow_y = -grow_height; | ||
318 | break; | ||
319 | case RESIZE_AXIS_LEFT: | ||
320 | grow_x = -grow_width; | ||
321 | break; | ||
322 | case RESIZE_AXIS_DOWN: | ||
323 | case RESIZE_AXIS_RIGHT: | ||
324 | break; | ||
325 | case RESIZE_AXIS_INVALID: | ||
326 | return cmd_results_new(CMD_INVALID, "resize", "Invalid axis/direction"); | ||
327 | } | ||
328 | con->x += grow_x; | ||
329 | con->y += grow_y; | ||
330 | con->width += grow_width; | ||
331 | con->height += grow_height; | ||
332 | |||
333 | if (con->type == C_VIEW) { | ||
334 | struct sway_view *view = con->sway_view; | ||
335 | view->x += grow_x; | ||
336 | view->y += grow_y; | ||
337 | view->width += grow_width; | ||
338 | view->height += grow_height; | ||
339 | } | ||
340 | |||
341 | arrange_windows(con); | ||
342 | |||
343 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | ||
344 | } | ||
194 | 345 | ||
195 | if (unit == RESIZE_UNIT_PPT) { | 346 | /** |
196 | float pct = amount / 100.0f; | 347 | * Implement `resize <grow|shrink>` for a tiled container. |
348 | */ | ||
349 | static struct cmd_results *resize_adjust_tiled(enum resize_axis axis, | ||
350 | struct resize_amount *amount) { | ||
351 | struct sway_container *current = config->handler_context.current_container; | ||
352 | |||
353 | if (amount->unit == RESIZE_UNIT_DEFAULT) { | ||
354 | amount->unit = RESIZE_UNIT_PPT; | ||
355 | } | ||
356 | if (amount->unit == RESIZE_UNIT_PPT) { | ||
357 | float pct = amount->amount / 100.0f; | ||
358 | // TODO: Make left/right/up/down resize in that direction? | ||
197 | switch (axis) { | 359 | switch (axis) { |
360 | case RESIZE_AXIS_LEFT: | ||
361 | case RESIZE_AXIS_RIGHT: | ||
198 | case RESIZE_AXIS_HORIZONTAL: | 362 | case RESIZE_AXIS_HORIZONTAL: |
199 | amount = (float)current->width * pct; | 363 | amount->amount = (float)current->width * pct; |
200 | break; | 364 | break; |
365 | case RESIZE_AXIS_UP: | ||
366 | case RESIZE_AXIS_DOWN: | ||
201 | case RESIZE_AXIS_VERTICAL: | 367 | case RESIZE_AXIS_VERTICAL: |
202 | amount = (float)current->height * pct; | 368 | amount->amount = (float)current->height * pct; |
203 | break; | 369 | break; |
204 | default: | 370 | case RESIZE_AXIS_INVALID: |
205 | sway_assert(0, "invalid resize axis"); | 371 | return cmd_results_new(CMD_INVALID, "resize", |
206 | return; | 372 | "Invalid resize axis/direction"); |
207 | } | 373 | } |
208 | } | 374 | } |
209 | 375 | ||
210 | return resize_tiled(amount, axis); | 376 | resize_tiled(amount->amount, axis); |
377 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | ||
211 | } | 378 | } |
212 | 379 | ||
213 | struct cmd_results *cmd_resize(int argc, char **argv) { | 380 | /** |
214 | struct sway_container *current = config->handler_context.current_container; | 381 | * Implement `resize set` for a tiled container. |
215 | if (!current) { | 382 | */ |
216 | return cmd_results_new(CMD_INVALID, "resize", "Cannot resize nothing"); | 383 | static struct cmd_results *resize_set_tiled(struct sway_container *con, |
217 | } | 384 | struct resize_amount *width, struct resize_amount *height) { |
218 | if (current->type != C_VIEW && current->type != C_CONTAINER) { | 385 | return cmd_results_new(CMD_INVALID, "resize", |
219 | return cmd_results_new(CMD_INVALID, "resize", | 386 | "'resize set' is not implemented for tiled views"); |
220 | "Can only resize views/containers"); | 387 | } |
388 | |||
389 | /** | ||
390 | * Implement `resize set` for a floating container. | ||
391 | */ | ||
392 | static struct cmd_results *resize_set_floating(struct sway_container *con, | ||
393 | struct resize_amount *width, struct resize_amount *height) { | ||
394 | int min_width, max_width, min_height, max_height; | ||
395 | calculate_constraints(&min_width, &max_width, &min_height, &max_height); | ||
396 | width->amount = fmax(min_width, fmin(width->amount, max_width)); | ||
397 | height->amount = fmax(min_height, fmin(height->amount, max_height)); | ||
398 | int grow_width = width->amount - con->width; | ||
399 | int grow_height = height->amount - con->height; | ||
400 | con->x -= grow_width / 2; | ||
401 | con->y -= grow_height / 2; | ||
402 | con->width = width->amount; | ||
403 | con->height = height->amount; | ||
404 | |||
405 | if (con->type == C_VIEW) { | ||
406 | struct sway_view *view = con->sway_view; | ||
407 | view->x -= grow_width / 2; | ||
408 | view->y -= grow_height / 2; | ||
409 | view->width += grow_width; | ||
410 | view->height += grow_height; | ||
221 | } | 411 | } |
222 | 412 | ||
413 | arrange_windows(con); | ||
414 | |||
415 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | ||
416 | } | ||
417 | |||
418 | /** | ||
419 | * resize set <args> | ||
420 | * | ||
421 | * args: <width> [px|ppt] <height> [px|ppt] | ||
422 | */ | ||
423 | static struct cmd_results *cmd_resize_set(int argc, char **argv) { | ||
223 | struct cmd_results *error; | 424 | struct cmd_results *error; |
224 | if ((error = checkarg(argc, "resize", EXPECTED_AT_LEAST, 2))) { | 425 | if ((error = checkarg(argc, "resize", EXPECTED_AT_LEAST, 2))) { |
225 | return error; | 426 | return error; |
226 | } | 427 | } |
227 | 428 | const char *usage = "Expected 'resize set <width> <height>'"; | |
228 | if (strcasecmp(argv[0], "set") == 0) { | 429 | |
229 | // TODO | 430 | // Width |
230 | //return cmd_resize_set(argc - 1, &argv[1]); | 431 | struct resize_amount width; |
231 | return cmd_results_new(CMD_INVALID, "resize", "resize set unimplemented"); | 432 | int num_consumed_args = parse_resize_amount(argc, argv, &width); |
433 | argc -= num_consumed_args; | ||
434 | argv += num_consumed_args; | ||
435 | if (width.unit == RESIZE_UNIT_INVALID) { | ||
436 | return cmd_results_new(CMD_INVALID, "resize", usage); | ||
437 | } | ||
438 | if (!argc) { | ||
439 | return cmd_results_new(CMD_INVALID, "resize", usage); | ||
232 | } | 440 | } |
233 | 441 | ||
234 | // TODO: resize grow|shrink left|right|up|down | 442 | // Height |
443 | struct resize_amount height; | ||
444 | num_consumed_args = parse_resize_amount(argc, argv, &height); | ||
445 | argc -= num_consumed_args; | ||
446 | argv += num_consumed_args; | ||
447 | if (height.unit == RESIZE_UNIT_INVALID) { | ||
448 | return cmd_results_new(CMD_INVALID, "resize", usage); | ||
449 | } | ||
235 | 450 | ||
236 | const char *usage = "Expected 'resize <shrink|grow> " | 451 | // If 0, don't resize that dimension |
237 | "<width|height> [<amount>] [px|ppt]'"; | 452 | struct sway_container *con = config->handler_context.current_container; |
453 | if (width.amount <= 0) { | ||
454 | width.amount = con->width; | ||
455 | } | ||
456 | if (height.amount <= 0) { | ||
457 | height.amount = con->height; | ||
458 | } | ||
238 | 459 | ||
239 | int multiplier = 0; | 460 | if (container_is_floating(con)) { |
240 | if (strcasecmp(*argv, "grow") == 0) { | 461 | return resize_set_floating(con, &width, &height); |
241 | multiplier = 1; | ||
242 | } else if (strcasecmp(*argv, "shrink") == 0) { | ||
243 | multiplier = -1; | ||
244 | } else { | ||
245 | return cmd_results_new(CMD_INVALID, "resize", usage); | ||
246 | } | 462 | } |
247 | --argc; ++argv; | 463 | return resize_set_tiled(con, &width, &height); |
464 | } | ||
248 | 465 | ||
466 | /** | ||
467 | * resize <grow|shrink> <args> | ||
468 | * | ||
469 | * args: <direction> | ||
470 | * args: <direction> <amount> <unit> | ||
471 | * args: <direction> <amount> <unit> or <amount> <other_unit> | ||
472 | */ | ||
473 | static struct cmd_results *cmd_resize_adjust(int argc, char **argv, | ||
474 | int multiplier) { | ||
475 | const char *usage = "Expected 'resize grow|shrink <direction> " | ||
476 | "[<amount> px|ppt [or <amount> px|ppt]]'"; | ||
249 | enum resize_axis axis = parse_resize_axis(*argv); | 477 | enum resize_axis axis = parse_resize_axis(*argv); |
250 | if (axis == RESIZE_AXIS_INVALID) { | 478 | if (axis == RESIZE_AXIS_INVALID) { |
251 | return cmd_results_new(CMD_INVALID, "resize", usage); | 479 | return cmd_results_new(CMD_INVALID, "resize", usage); |
252 | } | 480 | } |
253 | --argc; ++argv; | 481 | --argc; ++argv; |
254 | 482 | ||
255 | int amount = 10; // Default amount | 483 | // First amount |
256 | enum resize_unit unit = RESIZE_UNIT_DEFAULT; | 484 | struct resize_amount first_amount; |
257 | |||
258 | if (argc) { | 485 | if (argc) { |
259 | char *err; | 486 | int num_consumed_args = parse_resize_amount(argc, argv, &first_amount); |
260 | amount = (int)strtol(*argv, &err, 10); | 487 | argc -= num_consumed_args; |
261 | if (*err) { | 488 | argv += num_consumed_args; |
262 | // e.g. `resize grow width 10px` | 489 | if (first_amount.unit == RESIZE_UNIT_INVALID) { |
263 | unit = parse_resize_unit(err); | 490 | return cmd_results_new(CMD_INVALID, "resize", usage); |
264 | if (unit == RESIZE_UNIT_INVALID) { | ||
265 | return cmd_results_new(CMD_INVALID, "resize", usage); | ||
266 | } | ||
267 | } | 491 | } |
268 | --argc; ++argv; | 492 | } else { |
493 | first_amount.amount = 10; | ||
494 | first_amount.unit = RESIZE_UNIT_DEFAULT; | ||
269 | } | 495 | } |
270 | 496 | ||
497 | // "or" | ||
271 | if (argc) { | 498 | if (argc) { |
272 | unit = parse_resize_unit(*argv); | 499 | if (strcmp(*argv, "or") != 0) { |
273 | if (unit == RESIZE_UNIT_INVALID) { | ||
274 | return cmd_results_new(CMD_INVALID, "resize", usage); | 500 | return cmd_results_new(CMD_INVALID, "resize", usage); |
275 | } | 501 | } |
276 | --argc; ++argv; | 502 | --argc; ++argv; |
277 | } | 503 | } |
278 | 504 | ||
505 | // Second amount | ||
506 | struct resize_amount second_amount; | ||
279 | if (argc) { | 507 | if (argc) { |
280 | // Provied too many args, the bastard | 508 | int num_consumed_args = parse_resize_amount(argc, argv, &second_amount); |
281 | return cmd_results_new(CMD_INVALID, "resize", usage); | 509 | argc -= num_consumed_args; |
510 | argv += num_consumed_args; | ||
511 | if (second_amount.unit == RESIZE_UNIT_INVALID) { | ||
512 | return cmd_results_new(CMD_INVALID, "resize", usage); | ||
513 | } | ||
514 | } else { | ||
515 | second_amount.unit = RESIZE_UNIT_INVALID; | ||
282 | } | 516 | } |
283 | 517 | ||
284 | resize(amount * multiplier, axis, unit); | 518 | first_amount.amount *= multiplier; |
285 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | 519 | second_amount.amount *= multiplier; |
520 | |||
521 | struct sway_container *con = config->handler_context.current_container; | ||
522 | if (container_is_floating(con)) { | ||
523 | // Floating containers can only resize in px. Choose an amount which | ||
524 | // uses px, with fallback to an amount that specified no unit. | ||
525 | if (first_amount.unit == RESIZE_UNIT_PX) { | ||
526 | return resize_adjust_floating(axis, &first_amount); | ||
527 | } else if (second_amount.unit == RESIZE_UNIT_PX) { | ||
528 | return resize_adjust_floating(axis, &second_amount); | ||
529 | } else if (first_amount.unit == RESIZE_UNIT_DEFAULT) { | ||
530 | return resize_adjust_floating(axis, &first_amount); | ||
531 | } else if (second_amount.unit == RESIZE_UNIT_DEFAULT) { | ||
532 | return resize_adjust_floating(axis, &second_amount); | ||
533 | } else { | ||
534 | return cmd_results_new(CMD_INVALID, "resize", | ||
535 | "Floating containers cannot use ppt measurements"); | ||
536 | } | ||
537 | } | ||
538 | |||
539 | // For tiling, prefer ppt -> default -> px | ||
540 | if (first_amount.unit == RESIZE_UNIT_PPT) { | ||
541 | return resize_adjust_tiled(axis, &first_amount); | ||
542 | } else if (second_amount.unit == RESIZE_UNIT_PPT) { | ||
543 | return resize_adjust_tiled(axis, &second_amount); | ||
544 | } else if (first_amount.unit == RESIZE_UNIT_DEFAULT) { | ||
545 | return resize_adjust_tiled(axis, &first_amount); | ||
546 | } else if (second_amount.unit == RESIZE_UNIT_DEFAULT) { | ||
547 | return resize_adjust_tiled(axis, &second_amount); | ||
548 | } else { | ||
549 | return resize_adjust_tiled(axis, &first_amount); | ||
550 | } | ||
551 | } | ||
552 | |||
553 | struct cmd_results *cmd_resize(int argc, char **argv) { | ||
554 | struct sway_container *current = config->handler_context.current_container; | ||
555 | if (!current) { | ||
556 | return cmd_results_new(CMD_INVALID, "resize", "Cannot resize nothing"); | ||
557 | } | ||
558 | if (current->type != C_VIEW && current->type != C_CONTAINER) { | ||
559 | return cmd_results_new(CMD_INVALID, "resize", | ||
560 | "Can only resize views/containers"); | ||
561 | } | ||
562 | |||
563 | struct cmd_results *error; | ||
564 | if ((error = checkarg(argc, "resize", EXPECTED_AT_LEAST, 2))) { | ||
565 | return error; | ||
566 | } | ||
567 | |||
568 | if (strcasecmp(argv[0], "set") == 0) { | ||
569 | return cmd_resize_set(argc - 1, &argv[1]); | ||
570 | } | ||
571 | if (strcasecmp(argv[0], "grow") == 0) { | ||
572 | return cmd_resize_adjust(argc - 1, &argv[1], 1); | ||
573 | } | ||
574 | if (strcasecmp(argv[0], "shrink") == 0) { | ||
575 | return cmd_resize_adjust(argc - 1, &argv[1], -1); | ||
576 | } | ||
577 | |||
578 | const char *usage = "Expected 'resize <shrink|grow> " | ||
579 | "<width|height|up|down|left|right> [<amount>] [px|ppt]'"; | ||
580 | |||
581 | return cmd_results_new(CMD_INVALID, "resize", usage); | ||
286 | } | 582 | } |
diff --git a/sway/commands/scratchpad.c b/sway/commands/scratchpad.c new file mode 100644 index 00000000..ccc07c87 --- /dev/null +++ b/sway/commands/scratchpad.c | |||
@@ -0,0 +1,36 @@ | |||
1 | #include "log.h" | ||
2 | #include "sway/commands.h" | ||
3 | #include "sway/config.h" | ||
4 | #include "sway/scratchpad.h" | ||
5 | #include "sway/tree/container.h" | ||
6 | |||
7 | struct cmd_results *cmd_scratchpad(int argc, char **argv) { | ||
8 | struct cmd_results *error = NULL; | ||
9 | if ((error = checkarg(argc, "scratchpad", EXPECTED_EQUAL_TO, 1))) { | ||
10 | return error; | ||
11 | } | ||
12 | if (strcmp(argv[0], "show") != 0) { | ||
13 | return cmd_results_new(CMD_INVALID, "scratchpad", | ||
14 | "Expected 'scratchpad show'"); | ||
15 | } | ||
16 | if (!root_container.sway_root->scratchpad->length) { | ||
17 | return cmd_results_new(CMD_INVALID, "scratchpad", | ||
18 | "Scratchpad is empty"); | ||
19 | } | ||
20 | |||
21 | if (config->handler_context.using_criteria) { | ||
22 | // If using criteria, this command is executed for every container which | ||
23 | // matches the criteria. If this container isn't in the scratchpad, | ||
24 | // we'll just silently return a success. | ||
25 | struct sway_container *con = config->handler_context.current_container; | ||
26 | wlr_log(WLR_INFO, "cmd_scratchpad(%s)", con->name); | ||
27 | if (!con->scratchpad) { | ||
28 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | ||
29 | } | ||
30 | scratchpad_toggle_container(con); | ||
31 | } else { | ||
32 | scratchpad_toggle_auto(); | ||
33 | } | ||
34 | |||
35 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | ||
36 | } | ||
diff --git a/sway/commands/set.c b/sway/commands/set.c index 84e9b792..ea388d3b 100644 --- a/sway/commands/set.c +++ b/sway/commands/set.c | |||
@@ -32,7 +32,7 @@ struct cmd_results *cmd_set(int argc, char **argv) { | |||
32 | } | 32 | } |
33 | 33 | ||
34 | if (argv[0][0] != '$') { | 34 | if (argv[0][0] != '$') { |
35 | wlr_log(L_INFO, "Warning: variable '%s' doesn't start with $", argv[0]); | 35 | wlr_log(WLR_INFO, "Warning: variable '%s' doesn't start with $", argv[0]); |
36 | 36 | ||
37 | size_t size = snprintf(NULL, 0, "$%s", argv[0]); | 37 | size_t size = snprintf(NULL, 0, "$%s", argv[0]); |
38 | tmp = malloc(size + 1); | 38 | tmp = malloc(size + 1); |
diff --git a/sway/commands/smart_gaps.c b/sway/commands/smart_gaps.c index f687e78e..7d27e571 100644 --- a/sway/commands/smart_gaps.c +++ b/sway/commands/smart_gaps.c | |||
@@ -23,7 +23,7 @@ struct cmd_results *cmd_smart_gaps(int argc, char **argv) { | |||
23 | "Expected 'smart_gaps <on|off>' "); | 23 | "Expected 'smart_gaps <on|off>' "); |
24 | } | 24 | } |
25 | 25 | ||
26 | arrange_and_commit(&root_container); | 26 | arrange_windows(&root_container); |
27 | 27 | ||
28 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | 28 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); |
29 | } | 29 | } |
diff --git a/sway/commands/split.c b/sway/commands/split.c index c40f4d9f..313799da 100644 --- a/sway/commands/split.c +++ b/sway/commands/split.c | |||
@@ -16,7 +16,7 @@ static struct cmd_results *do_split(int layout) { | |||
16 | } | 16 | } |
17 | struct sway_container *parent = container_split(con, layout); | 17 | struct sway_container *parent = container_split(con, layout); |
18 | container_create_notify(parent); | 18 | container_create_notify(parent); |
19 | arrange_and_commit(parent->parent); | 19 | arrange_windows(parent->parent); |
20 | 20 | ||
21 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | 21 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); |
22 | } | 22 | } |
diff --git a/sway/commands/swap.c b/sway/commands/swap.c index e052058f..2fc88308 100644 --- a/sway/commands/swap.c +++ b/sway/commands/swap.c | |||
@@ -1,7 +1,6 @@ | |||
1 | #include <strings.h> | 1 | #include <strings.h> |
2 | #include <wlr/util/log.h> | 2 | #include <wlr/util/log.h> |
3 | #include "sway/commands.h" | 3 | #include "sway/commands.h" |
4 | #include "sway/desktop/transaction.h" | ||
5 | #include "sway/tree/arrange.h" | 4 | #include "sway/tree/arrange.h" |
6 | #include "sway/tree/layout.h" | 5 | #include "sway/tree/layout.h" |
7 | #include "sway/tree/view.h" | 6 | #include "sway/tree/view.h" |
@@ -79,14 +78,10 @@ struct cmd_results *cmd_swap(int argc, char **argv) { | |||
79 | 78 | ||
80 | container_swap(current, other); | 79 | container_swap(current, other); |
81 | 80 | ||
82 | struct sway_transaction *txn = transaction_create(); | 81 | arrange_windows(current->parent); |
83 | arrange_windows(current->parent, txn); | ||
84 | |||
85 | if (other->parent != current->parent) { | 82 | if (other->parent != current->parent) { |
86 | arrange_windows(other->parent, txn); | 83 | arrange_windows(other->parent); |
87 | } | 84 | } |
88 | 85 | ||
89 | transaction_commit(txn); | ||
90 | |||
91 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | 86 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); |
92 | } | 87 | } |
diff --git a/sway/commands/swaybg_command.c b/sway/commands/swaybg_command.c index 770d4821..36f7fdcd 100644 --- a/sway/commands/swaybg_command.c +++ b/sway/commands/swaybg_command.c | |||
@@ -13,7 +13,7 @@ struct cmd_results *cmd_swaybg_command(int argc, char **argv) { | |||
13 | free(config->swaybg_command); | 13 | free(config->swaybg_command); |
14 | } | 14 | } |
15 | config->swaybg_command = join_args(argv, argc); | 15 | config->swaybg_command = join_args(argv, argc); |
16 | wlr_log(L_DEBUG, "Using custom swaybg command: %s", | 16 | wlr_log(WLR_DEBUG, "Using custom swaybg command: %s", |
17 | config->swaybg_command); | 17 | config->swaybg_command); |
18 | 18 | ||
19 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | 19 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); |
diff --git a/sway/commands/urgent.c b/sway/commands/urgent.c new file mode 100644 index 00000000..d199858a --- /dev/null +++ b/sway/commands/urgent.c | |||
@@ -0,0 +1,36 @@ | |||
1 | #include "log.h" | ||
2 | #include "sway/commands.h" | ||
3 | #include "sway/config.h" | ||
4 | #include "sway/tree/arrange.h" | ||
5 | #include "sway/tree/container.h" | ||
6 | #include "sway/tree/view.h" | ||
7 | #include "sway/tree/layout.h" | ||
8 | |||
9 | struct cmd_results *cmd_urgent(int argc, char **argv) { | ||
10 | struct cmd_results *error = NULL; | ||
11 | if ((error = checkarg(argc, "urgent", EXPECTED_EQUAL_TO, 1))) { | ||
12 | return error; | ||
13 | } | ||
14 | struct sway_container *container = | ||
15 | config->handler_context.current_container; | ||
16 | if (container->type != C_VIEW) { | ||
17 | return cmd_results_new(CMD_INVALID, "urgent", | ||
18 | "Only views can be urgent"); | ||
19 | } | ||
20 | struct sway_view *view = container->sway_view; | ||
21 | |||
22 | if (strcmp(argv[0], "enable") == 0) { | ||
23 | view_set_urgent(view, true); | ||
24 | } else if (strcmp(argv[0], "disable") == 0) { | ||
25 | view_set_urgent(view, false); | ||
26 | } else if (strcmp(argv[0], "allow") == 0) { | ||
27 | view->allow_request_urgent = true; | ||
28 | } else if (strcmp(argv[0], "deny") == 0) { | ||
29 | view->allow_request_urgent = false; | ||
30 | } else { | ||
31 | return cmd_results_new(CMD_INVALID, "urgent", | ||
32 | "Expected 'urgent <enable|disable|allow|deny>'"); | ||
33 | } | ||
34 | |||
35 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | ||
36 | } | ||
diff --git a/sway/commands/workspace.c b/sway/commands/workspace.c index d15be571..e8b37182 100644 --- a/sway/commands/workspace.c +++ b/sway/commands/workspace.c | |||
@@ -51,7 +51,7 @@ struct cmd_results *cmd_workspace(int argc, char **argv) { | |||
51 | free(old); // workspaces can only be assigned to a single output | 51 | free(old); // workspaces can only be assigned to a single output |
52 | list_del(config->workspace_outputs, i); | 52 | list_del(config->workspace_outputs, i); |
53 | } | 53 | } |
54 | wlr_log(L_DEBUG, "Assigning workspace %s to output %s", wso->workspace, wso->output); | 54 | wlr_log(WLR_DEBUG, "Assigning workspace %s to output %s", wso->workspace, wso->output); |
55 | list_add(config->workspace_outputs, wso); | 55 | list_add(config->workspace_outputs, wso); |
56 | } else { | 56 | } else { |
57 | if (config->reading || !config->active) { | 57 | if (config->reading || !config->active) { |
diff --git a/sway/config.c b/sway/config.c index 512cab31..5f6dd7ad 100644 --- a/sway/config.c +++ b/sway/config.c | |||
@@ -24,6 +24,7 @@ | |||
24 | #include "sway/input/seat.h" | 24 | #include "sway/input/seat.h" |
25 | #include "sway/commands.h" | 25 | #include "sway/commands.h" |
26 | #include "sway/config.h" | 26 | #include "sway/config.h" |
27 | #include "sway/criteria.h" | ||
27 | #include "sway/tree/arrange.h" | 28 | #include "sway/tree/arrange.h" |
28 | #include "sway/tree/layout.h" | 29 | #include "sway/tree/layout.h" |
29 | #include "sway/tree/workspace.h" | 30 | #include "sway/tree/workspace.h" |
@@ -86,7 +87,12 @@ void free_config(struct sway_config *config) { | |||
86 | } | 87 | } |
87 | list_free(config->cmd_queue); | 88 | list_free(config->cmd_queue); |
88 | list_free(config->workspace_outputs); | 89 | list_free(config->workspace_outputs); |
89 | list_free(config->output_configs); | 90 | if (config->output_configs) { |
91 | for (int i = 0; i < config->output_configs->length; i++) { | ||
92 | free_output_config(config->output_configs->items[i]); | ||
93 | } | ||
94 | list_free(config->output_configs); | ||
95 | } | ||
90 | if (config->input_configs) { | 96 | if (config->input_configs) { |
91 | for (int i = 0; i < config->input_configs->length; i++) { | 97 | for (int i = 0; i < config->input_configs->length; i++) { |
92 | free_input_config(config->input_configs->items[i]); | 98 | free_input_config(config->input_configs->items[i]); |
@@ -99,7 +105,12 @@ void free_config(struct sway_config *config) { | |||
99 | } | 105 | } |
100 | list_free(config->seat_configs); | 106 | list_free(config->seat_configs); |
101 | } | 107 | } |
102 | list_free(config->criteria); | 108 | if (config->criteria) { |
109 | for (int i = 0; i < config->criteria->length; ++i) { | ||
110 | criteria_destroy(config->criteria->items[i]); | ||
111 | } | ||
112 | list_free(config->criteria); | ||
113 | } | ||
103 | list_free(config->no_focus); | 114 | list_free(config->no_focus); |
104 | list_free(config->active_bar_modifiers); | 115 | list_free(config->active_bar_modifiers); |
105 | list_free(config->config_chain); | 116 | list_free(config->config_chain); |
@@ -111,6 +122,7 @@ void free_config(struct sway_config *config) { | |||
111 | free(config->floating_scroll_left_cmd); | 122 | free(config->floating_scroll_left_cmd); |
112 | free(config->floating_scroll_right_cmd); | 123 | free(config->floating_scroll_right_cmd); |
113 | free(config->font); | 124 | free(config->font); |
125 | free((char *)config->current_config_path); | ||
114 | free((char *)config->current_config); | 126 | free((char *)config->current_config); |
115 | free(config); | 127 | free(config); |
116 | } | 128 | } |
@@ -172,6 +184,7 @@ static void config_defaults(struct sway_config *config) { | |||
172 | config->default_orientation = L_NONE; | 184 | config->default_orientation = L_NONE; |
173 | if (!(config->font = strdup("monospace 10"))) goto cleanup; | 185 | if (!(config->font = strdup("monospace 10"))) goto cleanup; |
174 | config->font_height = 17; // height of monospace 10 | 186 | config->font_height = 17; // height of monospace 10 |
187 | config->urgent_timeout = 500; | ||
175 | 188 | ||
176 | // floating view | 189 | // floating view |
177 | config->floating_maximum_width = 0; | 190 | config->floating_maximum_width = 0; |
@@ -198,6 +211,7 @@ static void config_defaults(struct sway_config *config) { | |||
198 | if (!(config->active_bar_modifiers = create_list())) goto cleanup; | 211 | if (!(config->active_bar_modifiers = create_list())) goto cleanup; |
199 | 212 | ||
200 | if (!(config->config_chain = create_list())) goto cleanup; | 213 | if (!(config->config_chain = create_list())) goto cleanup; |
214 | config->current_config_path = NULL; | ||
201 | config->current_config = NULL; | 215 | config->current_config = NULL; |
202 | 216 | ||
203 | // borders | 217 | // borders |
@@ -269,12 +283,12 @@ static char *get_config_path(void) { | |||
269 | char *home = getenv("HOME"); | 283 | char *home = getenv("HOME"); |
270 | char *config_home = malloc(strlen(home) + strlen("/.config") + 1); | 284 | char *config_home = malloc(strlen(home) + strlen("/.config") + 1); |
271 | if (!config_home) { | 285 | if (!config_home) { |
272 | wlr_log(L_ERROR, "Unable to allocate $HOME/.config"); | 286 | wlr_log(WLR_ERROR, "Unable to allocate $HOME/.config"); |
273 | } else { | 287 | } else { |
274 | strcpy(config_home, home); | 288 | strcpy(config_home, home); |
275 | strcat(config_home, "/.config"); | 289 | strcat(config_home, "/.config"); |
276 | setenv("XDG_CONFIG_HOME", config_home, 1); | 290 | setenv("XDG_CONFIG_HOME", config_home, 1); |
277 | wlr_log(L_DEBUG, "Set XDG_CONFIG_HOME to %s", config_home); | 291 | wlr_log(WLR_DEBUG, "Set XDG_CONFIG_HOME to %s", config_home); |
278 | free(config_home); | 292 | free(config_home); |
279 | } | 293 | } |
280 | } | 294 | } |
@@ -297,25 +311,22 @@ static char *get_config_path(void) { | |||
297 | return NULL; // Not reached | 311 | return NULL; // Not reached |
298 | } | 312 | } |
299 | 313 | ||
300 | const char *current_config_path; | ||
301 | |||
302 | static bool load_config(const char *path, struct sway_config *config) { | 314 | static bool load_config(const char *path, struct sway_config *config) { |
303 | wlr_log(L_INFO, "Loading config from %s", path); | 315 | if (path == NULL) { |
304 | current_config_path = path; | 316 | wlr_log(WLR_ERROR, "Unable to find a config file!"); |
305 | |||
306 | struct stat sb; | ||
307 | if (stat(path, &sb) == 0 && S_ISDIR(sb.st_mode)) { | ||
308 | return false; | 317 | return false; |
309 | } | 318 | } |
310 | 319 | ||
311 | if (path == NULL) { | 320 | wlr_log(WLR_INFO, "Loading config from %s", path); |
312 | wlr_log(L_ERROR, "Unable to find a config file!"); | 321 | |
322 | struct stat sb; | ||
323 | if (stat(path, &sb) == 0 && S_ISDIR(sb.st_mode)) { | ||
313 | return false; | 324 | return false; |
314 | } | 325 | } |
315 | 326 | ||
316 | FILE *f = fopen(path, "r"); | 327 | FILE *f = fopen(path, "r"); |
317 | if (!f) { | 328 | if (!f) { |
318 | wlr_log(L_ERROR, "Unable to open %s for reading", path); | 329 | wlr_log(WLR_ERROR, "Unable to open %s for reading", path); |
319 | return false; | 330 | return false; |
320 | } | 331 | } |
321 | 332 | ||
@@ -323,10 +334,9 @@ static bool load_config(const char *path, struct sway_config *config) { | |||
323 | fclose(f); | 334 | fclose(f); |
324 | 335 | ||
325 | if (!config_load_success) { | 336 | if (!config_load_success) { |
326 | wlr_log(L_ERROR, "Error(s) loading config!"); | 337 | wlr_log(WLR_ERROR, "Error(s) loading config!"); |
327 | } | 338 | } |
328 | 339 | ||
329 | current_config_path = NULL; | ||
330 | return true; | 340 | return true; |
331 | } | 341 | } |
332 | 342 | ||
@@ -346,12 +356,13 @@ bool load_main_config(const char *file, bool is_active) { | |||
346 | 356 | ||
347 | config_defaults(config); | 357 | config_defaults(config); |
348 | if (is_active) { | 358 | if (is_active) { |
349 | wlr_log(L_DEBUG, "Performing configuration file reload"); | 359 | wlr_log(WLR_DEBUG, "Performing configuration file reload"); |
350 | config->reloading = true; | 360 | config->reloading = true; |
351 | config->active = true; | 361 | config->active = true; |
362 | create_default_output_configs(); | ||
352 | } | 363 | } |
353 | 364 | ||
354 | config->current_config = path; | 365 | config->current_config_path = path; |
355 | list_add(config->config_chain, path); | 366 | list_add(config->config_chain, path); |
356 | 367 | ||
357 | config->reading = true; | 368 | config->reading = true; |
@@ -362,7 +373,7 @@ bool load_main_config(const char *file, bool is_active) { | |||
362 | /* | 373 | /* |
363 | DIR *dir = opendir(SYSCONFDIR "/sway/security.d"); | 374 | DIR *dir = opendir(SYSCONFDIR "/sway/security.d"); |
364 | if (!dir) { | 375 | if (!dir) { |
365 | wlr_log(L_ERROR, | 376 | wlr_log(WLR_ERROR, |
366 | "%s does not exist, sway will have no security configuration" | 377 | "%s does not exist, sway will have no security configuration" |
367 | " and will probably be broken", SYSCONFDIR "/sway/security.d"); | 378 | " and will probably be broken", SYSCONFDIR "/sway/security.d"); |
368 | } else { | 379 | } else { |
@@ -391,7 +402,7 @@ bool load_main_config(const char *file, bool is_active) { | |||
391 | if (stat(_path, &s) || s.st_uid != 0 || s.st_gid != 0 || | 402 | if (stat(_path, &s) || s.st_uid != 0 || s.st_gid != 0 || |
392 | (((s.st_mode & 0777) != 0644) && | 403 | (((s.st_mode & 0777) != 0644) && |
393 | (s.st_mode & 0777) != 0444)) { | 404 | (s.st_mode & 0777) != 0444)) { |
394 | wlr_log(L_ERROR, | 405 | wlr_log(WLR_ERROR, |
395 | "Refusing to load %s - it must be owned by root " | 406 | "Refusing to load %s - it must be owned by root " |
396 | "and mode 644 or 444", _path); | 407 | "and mode 644 or 444", _path); |
397 | success = false; | 408 | success = false; |
@@ -407,6 +418,9 @@ bool load_main_config(const char *file, bool is_active) { | |||
407 | success = success && load_config(path, config); | 418 | success = success && load_config(path, config); |
408 | 419 | ||
409 | if (is_active) { | 420 | if (is_active) { |
421 | for (int i = 0; i < config->output_configs->length; i++) { | ||
422 | apply_output_config_to_outputs(config->output_configs->items[i]); | ||
423 | } | ||
410 | config->reloading = false; | 424 | config->reloading = false; |
411 | } | 425 | } |
412 | 426 | ||
@@ -421,26 +435,28 @@ bool load_main_config(const char *file, bool is_active) { | |||
421 | static bool load_include_config(const char *path, const char *parent_dir, | 435 | static bool load_include_config(const char *path, const char *parent_dir, |
422 | struct sway_config *config) { | 436 | struct sway_config *config) { |
423 | // save parent config | 437 | // save parent config |
424 | const char *parent_config = config->current_config; | 438 | const char *parent_config = config->current_config_path; |
425 | 439 | ||
426 | char *full_path = strdup(path); | 440 | char *full_path; |
427 | int len = strlen(path); | 441 | int len = strlen(path); |
428 | if (len >= 1 && path[0] != '/') { | 442 | if (len >= 1 && path[0] != '/') { |
429 | len = len + strlen(parent_dir) + 2; | 443 | len = len + strlen(parent_dir) + 2; |
430 | full_path = malloc(len * sizeof(char)); | 444 | full_path = malloc(len * sizeof(char)); |
431 | if (!full_path) { | 445 | if (!full_path) { |
432 | wlr_log(L_ERROR, | 446 | wlr_log(WLR_ERROR, |
433 | "Unable to allocate full path to included config"); | 447 | "Unable to allocate full path to included config"); |
434 | return false; | 448 | return false; |
435 | } | 449 | } |
436 | snprintf(full_path, len, "%s/%s", parent_dir, path); | 450 | snprintf(full_path, len, "%s/%s", parent_dir, path); |
451 | } else { | ||
452 | full_path = strdup(path); | ||
437 | } | 453 | } |
438 | 454 | ||
439 | char *real_path = realpath(full_path, NULL); | 455 | char *real_path = realpath(full_path, NULL); |
440 | free(full_path); | 456 | free(full_path); |
441 | 457 | ||
442 | if (real_path == NULL) { | 458 | if (real_path == NULL) { |
443 | wlr_log(L_DEBUG, "%s not found.", path); | 459 | wlr_log(WLR_DEBUG, "%s not found.", path); |
444 | return false; | 460 | return false; |
445 | } | 461 | } |
446 | 462 | ||
@@ -449,7 +465,7 @@ static bool load_include_config(const char *path, const char *parent_dir, | |||
449 | for (j = 0; j < config->config_chain->length; ++j) { | 465 | for (j = 0; j < config->config_chain->length; ++j) { |
450 | char *old_path = config->config_chain->items[j]; | 466 | char *old_path = config->config_chain->items[j]; |
451 | if (strcmp(real_path, old_path) == 0) { | 467 | if (strcmp(real_path, old_path) == 0) { |
452 | wlr_log(L_DEBUG, | 468 | wlr_log(WLR_DEBUG, |
453 | "%s already included once, won't be included again.", | 469 | "%s already included once, won't be included again.", |
454 | real_path); | 470 | real_path); |
455 | free(real_path); | 471 | free(real_path); |
@@ -457,25 +473,25 @@ static bool load_include_config(const char *path, const char *parent_dir, | |||
457 | } | 473 | } |
458 | } | 474 | } |
459 | 475 | ||
460 | config->current_config = real_path; | 476 | config->current_config_path = real_path; |
461 | list_add(config->config_chain, real_path); | 477 | list_add(config->config_chain, real_path); |
462 | int index = config->config_chain->length - 1; | 478 | int index = config->config_chain->length - 1; |
463 | 479 | ||
464 | if (!load_config(real_path, config)) { | 480 | if (!load_config(real_path, config)) { |
465 | free(real_path); | 481 | free(real_path); |
466 | config->current_config = parent_config; | 482 | config->current_config_path = parent_config; |
467 | list_del(config->config_chain, index); | 483 | list_del(config->config_chain, index); |
468 | return false; | 484 | return false; |
469 | } | 485 | } |
470 | 486 | ||
471 | // restore current_config | 487 | // restore current_config_path |
472 | config->current_config = parent_config; | 488 | config->current_config_path = parent_config; |
473 | return true; | 489 | return true; |
474 | } | 490 | } |
475 | 491 | ||
476 | bool load_include_configs(const char *path, struct sway_config *config) { | 492 | bool load_include_configs(const char *path, struct sway_config *config) { |
477 | char *wd = getcwd(NULL, 0); | 493 | char *wd = getcwd(NULL, 0); |
478 | char *parent_path = strdup(config->current_config); | 494 | char *parent_path = strdup(config->current_config_path); |
479 | const char *parent_dir = dirname(parent_path); | 495 | const char *parent_dir = dirname(parent_path); |
480 | 496 | ||
481 | if (chdir(parent_dir) < 0) { | 497 | if (chdir(parent_dir) < 0) { |
@@ -503,7 +519,7 @@ bool load_include_configs(const char *path, struct sway_config *config) { | |||
503 | // restore wd | 519 | // restore wd |
504 | if (chdir(wd) < 0) { | 520 | if (chdir(wd) < 0) { |
505 | free(wd); | 521 | free(wd); |
506 | wlr_log(L_ERROR, "failed to restore working directory"); | 522 | wlr_log(WLR_ERROR, "failed to restore working directory"); |
507 | return false; | 523 | return false; |
508 | } | 524 | } |
509 | 525 | ||
@@ -518,13 +534,13 @@ static int detect_brace_on_following_line(FILE *file, char *line, | |||
518 | char *peeked = NULL; | 534 | char *peeked = NULL; |
519 | long position = 0; | 535 | long position = 0; |
520 | do { | 536 | do { |
521 | wlr_log(L_DEBUG, "Peeking line %d", line_number + lines + 1); | 537 | wlr_log(WLR_DEBUG, "Peeking line %d", line_number + lines + 1); |
522 | free(peeked); | 538 | free(peeked); |
523 | peeked = peek_line(file, lines, &position); | 539 | peeked = peek_line(file, lines, &position); |
524 | if (peeked) { | 540 | if (peeked) { |
525 | peeked = strip_whitespace(peeked); | 541 | peeked = strip_whitespace(peeked); |
526 | } | 542 | } |
527 | wlr_log(L_DEBUG, "Peeked line: `%s`", peeked); | 543 | wlr_log(WLR_DEBUG, "Peeked line: `%s`", peeked); |
528 | lines++; | 544 | lines++; |
529 | } while (peeked && strlen(peeked) == 0); | 545 | } while (peeked && strlen(peeked) == 0); |
530 | 546 | ||
@@ -543,7 +559,7 @@ static char *expand_line(const char *block, const char *line, bool add_brace) { | |||
543 | + (add_brace ? 2 : 0) + 1; | 559 | + (add_brace ? 2 : 0) + 1; |
544 | char *expanded = calloc(1, size); | 560 | char *expanded = calloc(1, size); |
545 | if (!expanded) { | 561 | if (!expanded) { |
546 | wlr_log(L_ERROR, "Cannot allocate expanded line buffer"); | 562 | wlr_log(WLR_ERROR, "Cannot allocate expanded line buffer"); |
547 | return NULL; | 563 | return NULL; |
548 | } | 564 | } |
549 | snprintf(expanded, size, "%s%s%s%s", block ? block : "", | 565 | snprintf(expanded, size, "%s%s%s%s", block ? block : "", |
@@ -552,10 +568,33 @@ static char *expand_line(const char *block, const char *line, bool add_brace) { | |||
552 | } | 568 | } |
553 | 569 | ||
554 | bool read_config(FILE *file, struct sway_config *config) { | 570 | bool read_config(FILE *file, struct sway_config *config) { |
571 | bool reading_main_config = false; | ||
572 | char *this_config = NULL; | ||
573 | size_t config_size = 0; | ||
574 | if (config->current_config == NULL) { | ||
575 | reading_main_config = true; | ||
576 | |||
577 | int ret_seek = fseek(file, 0, SEEK_END); | ||
578 | long ret_tell = ftell(file); | ||
579 | if (ret_seek == -1 || ret_tell == -1) { | ||
580 | wlr_log(WLR_ERROR, "Unable to get size of config file"); | ||
581 | return false; | ||
582 | } | ||
583 | config_size = ret_tell; | ||
584 | rewind(file); | ||
585 | |||
586 | config->current_config = this_config = calloc(1, config_size + 1); | ||
587 | if (this_config == NULL) { | ||
588 | wlr_log(WLR_ERROR, "Unable to allocate buffer for config contents"); | ||
589 | return false; | ||
590 | } | ||
591 | } | ||
592 | |||
555 | bool success = true; | 593 | bool success = true; |
556 | int line_number = 0; | 594 | int line_number = 0; |
557 | char *line; | 595 | char *line; |
558 | list_t *stack = create_list(); | 596 | list_t *stack = create_list(); |
597 | size_t read = 0; | ||
559 | while (!feof(file)) { | 598 | while (!feof(file)) { |
560 | char *block = stack->length ? stack->items[0] : NULL; | 599 | char *block = stack->length ? stack->items[0] : NULL; |
561 | line = read_line(file); | 600 | line = read_line(file); |
@@ -563,7 +602,26 @@ bool read_config(FILE *file, struct sway_config *config) { | |||
563 | continue; | 602 | continue; |
564 | } | 603 | } |
565 | line_number++; | 604 | line_number++; |
566 | wlr_log(L_DEBUG, "Read line %d: %s", line_number, line); | 605 | wlr_log(WLR_DEBUG, "Read line %d: %s", line_number, line); |
606 | |||
607 | if (reading_main_config) { | ||
608 | size_t length = strlen(line); | ||
609 | |||
610 | if (read + length > config_size) { | ||
611 | wlr_log(WLR_ERROR, "Config file changed during reading"); | ||
612 | list_foreach(stack, free); | ||
613 | list_free(stack); | ||
614 | free(line); | ||
615 | return false; | ||
616 | } | ||
617 | |||
618 | strcpy(this_config + read, line); | ||
619 | if (line_number != 1) { | ||
620 | this_config[read - 1] = '\n'; | ||
621 | } | ||
622 | read += length + 1; | ||
623 | } | ||
624 | |||
567 | line = strip_whitespace(line); | 625 | line = strip_whitespace(line); |
568 | if (line[0] == '#') { | 626 | if (line[0] == '#') { |
569 | free(line); | 627 | free(line); |
@@ -577,13 +635,16 @@ bool read_config(FILE *file, struct sway_config *config) { | |||
577 | line_number); | 635 | line_number); |
578 | if (brace_detected > 0) { | 636 | if (brace_detected > 0) { |
579 | line_number += brace_detected; | 637 | line_number += brace_detected; |
580 | wlr_log(L_DEBUG, "Detected open brace on line %d", line_number); | 638 | wlr_log(WLR_DEBUG, "Detected open brace on line %d", line_number); |
581 | } | 639 | } |
582 | char *expanded = expand_line(block, line, brace_detected > 0); | 640 | char *expanded = expand_line(block, line, brace_detected > 0); |
583 | if (!expanded) { | 641 | if (!expanded) { |
642 | list_foreach(stack, free); | ||
643 | list_free(stack); | ||
644 | free(line); | ||
584 | return false; | 645 | return false; |
585 | } | 646 | } |
586 | wlr_log(L_DEBUG, "Expanded line: %s", expanded); | 647 | wlr_log(WLR_DEBUG, "Expanded line: %s", expanded); |
587 | struct cmd_results *res; | 648 | struct cmd_results *res; |
588 | if (block && strcmp(block, "<commands>") == 0) { | 649 | if (block && strcmp(block, "<commands>") == 0) { |
589 | // Special case | 650 | // Special case |
@@ -591,27 +652,26 @@ bool read_config(FILE *file, struct sway_config *config) { | |||
591 | } else { | 652 | } else { |
592 | res = config_command(expanded); | 653 | res = config_command(expanded); |
593 | } | 654 | } |
594 | free(expanded); | ||
595 | switch(res->status) { | 655 | switch(res->status) { |
596 | case CMD_FAILURE: | 656 | case CMD_FAILURE: |
597 | case CMD_INVALID: | 657 | case CMD_INVALID: |
598 | wlr_log(L_ERROR, "Error on line %i '%s': %s (%s)", line_number, | 658 | wlr_log(WLR_ERROR, "Error on line %i '%s': %s (%s)", line_number, |
599 | line, res->error, config->current_config); | 659 | line, res->error, config->current_config_path); |
600 | success = false; | 660 | success = false; |
601 | break; | 661 | break; |
602 | 662 | ||
603 | case CMD_DEFER: | 663 | case CMD_DEFER: |
604 | wlr_log(L_DEBUG, "Deferring command `%s'", line); | 664 | wlr_log(WLR_DEBUG, "Deferring command `%s'", line); |
605 | list_add(config->cmd_queue, strdup(line)); | 665 | list_add(config->cmd_queue, strdup(expanded)); |
606 | break; | 666 | break; |
607 | 667 | ||
608 | case CMD_BLOCK_COMMANDS: | 668 | case CMD_BLOCK_COMMANDS: |
609 | wlr_log(L_DEBUG, "Entering commands block"); | 669 | wlr_log(WLR_DEBUG, "Entering commands block"); |
610 | list_insert(stack, 0, "<commands>"); | 670 | list_insert(stack, 0, "<commands>"); |
611 | break; | 671 | break; |
612 | 672 | ||
613 | case CMD_BLOCK: | 673 | case CMD_BLOCK: |
614 | wlr_log(L_DEBUG, "Entering block '%s'", res->input); | 674 | wlr_log(WLR_DEBUG, "Entering block '%s'", res->input); |
615 | list_insert(stack, 0, strdup(res->input)); | 675 | list_insert(stack, 0, strdup(res->input)); |
616 | if (strcmp(res->input, "bar") == 0) { | 676 | if (strcmp(res->input, "bar") == 0) { |
617 | config->current_bar = NULL; | 677 | config->current_bar = NULL; |
@@ -620,7 +680,7 @@ bool read_config(FILE *file, struct sway_config *config) { | |||
620 | 680 | ||
621 | case CMD_BLOCK_END: | 681 | case CMD_BLOCK_END: |
622 | if (!block) { | 682 | if (!block) { |
623 | wlr_log(L_DEBUG, "Unmatched '}' on line %i", line_number); | 683 | wlr_log(WLR_DEBUG, "Unmatched '}' on line %i", line_number); |
624 | success = false; | 684 | success = false; |
625 | break; | 685 | break; |
626 | } | 686 | } |
@@ -628,13 +688,14 @@ bool read_config(FILE *file, struct sway_config *config) { | |||
628 | config->current_bar = NULL; | 688 | config->current_bar = NULL; |
629 | } | 689 | } |
630 | 690 | ||
631 | wlr_log(L_DEBUG, "Exiting block '%s'", block); | 691 | wlr_log(WLR_DEBUG, "Exiting block '%s'", block); |
632 | list_del(stack, 0); | 692 | list_del(stack, 0); |
633 | free(block); | 693 | free(block); |
634 | memset(&config->handler_context, 0, | 694 | memset(&config->handler_context, 0, |
635 | sizeof(config->handler_context)); | 695 | sizeof(config->handler_context)); |
636 | default:; | 696 | default:; |
637 | } | 697 | } |
698 | free(expanded); | ||
638 | free(line); | 699 | free(line); |
639 | free_cmd_results(res); | 700 | free_cmd_results(res); |
640 | } | 701 | } |
@@ -671,7 +732,7 @@ char *do_var_replacement(char *str) { | |||
671 | int vvlen = strlen(var->value); | 732 | int vvlen = strlen(var->value); |
672 | char *newstr = malloc(strlen(str) - vnlen + vvlen + 1); | 733 | char *newstr = malloc(strlen(str) - vnlen + vvlen + 1); |
673 | if (!newstr) { | 734 | if (!newstr) { |
674 | wlr_log(L_ERROR, | 735 | wlr_log(WLR_ERROR, |
675 | "Unable to allocate replacement " | 736 | "Unable to allocate replacement " |
676 | "during variable expansion"); | 737 | "during variable expansion"); |
677 | break; | 738 | break; |
@@ -733,6 +794,6 @@ void config_update_font_height(bool recalculate) { | |||
733 | } | 794 | } |
734 | 795 | ||
735 | if (config->font_height != prev_max_height) { | 796 | if (config->font_height != prev_max_height) { |
736 | arrange_and_commit(&root_container); | 797 | arrange_windows(&root_container); |
737 | } | 798 | } |
738 | } | 799 | } |
diff --git a/sway/config/bar.c b/sway/config/bar.c index 5a97c3cc..3a74331e 100644 --- a/sway/config/bar.c +++ b/sway/config/bar.c | |||
@@ -16,10 +16,10 @@ | |||
16 | #include "log.h" | 16 | #include "log.h" |
17 | 17 | ||
18 | static void terminate_swaybar(pid_t pid) { | 18 | static void terminate_swaybar(pid_t pid) { |
19 | wlr_log(L_DEBUG, "Terminating swaybar %d", pid); | 19 | wlr_log(WLR_DEBUG, "Terminating swaybar %d", pid); |
20 | int ret = kill(-pid, SIGTERM); | 20 | int ret = kill(-pid, SIGTERM); |
21 | if (ret != 0) { | 21 | if (ret != 0) { |
22 | wlr_log_errno(L_ERROR, "Unable to terminate swaybar %d", pid); | 22 | wlr_log_errno(WLR_ERROR, "Unable to terminate swaybar %d", pid); |
23 | } else { | 23 | } else { |
24 | int status; | 24 | int status; |
25 | waitpid(pid, &status, 0); | 25 | waitpid(pid, &status, 0); |
@@ -30,6 +30,7 @@ void free_bar_config(struct bar_config *bar) { | |||
30 | if (!bar) { | 30 | if (!bar) { |
31 | return; | 31 | return; |
32 | } | 32 | } |
33 | free(bar->id); | ||
33 | free(bar->mode); | 34 | free(bar->mode); |
34 | free(bar->position); | 35 | free(bar->position); |
35 | free(bar->hidden_state); | 36 | free(bar->hidden_state); |
@@ -70,16 +71,12 @@ void free_bar_config(struct bar_config *bar) { | |||
70 | 71 | ||
71 | struct bar_config *default_bar_config(void) { | 72 | struct bar_config *default_bar_config(void) { |
72 | struct bar_config *bar = NULL; | 73 | struct bar_config *bar = NULL; |
73 | bar = malloc(sizeof(struct bar_config)); | 74 | bar = calloc(1, sizeof(struct bar_config)); |
74 | if (!bar) { | 75 | if (!bar) { |
75 | return NULL; | 76 | return NULL; |
76 | } | 77 | } |
77 | if (!(bar->mode = strdup("dock"))) goto cleanup; | ||
78 | if (!(bar->hidden_state = strdup("hide"))) goto cleanup; | ||
79 | bar->outputs = NULL; | 78 | bar->outputs = NULL; |
80 | bar->position = strdup("bottom"); | 79 | bar->position = strdup("bottom"); |
81 | if (!(bar->bindings = create_list())) goto cleanup; | ||
82 | if (!(bar->status_command = strdup("while :; do date +'%Y-%m-%d %l:%M:%S %p'; sleep 1; done"))) goto cleanup; | ||
83 | bar->pango_markup = false; | 80 | bar->pango_markup = false; |
84 | bar->swaybar_command = NULL; | 81 | bar->swaybar_command = NULL; |
85 | bar->font = NULL; | 82 | bar->font = NULL; |
@@ -91,6 +88,19 @@ struct bar_config *default_bar_config(void) { | |||
91 | bar->binding_mode_indicator = true; | 88 | bar->binding_mode_indicator = true; |
92 | bar->verbose = false; | 89 | bar->verbose = false; |
93 | bar->pid = 0; | 90 | bar->pid = 0; |
91 | if (!(bar->mode = strdup("dock"))) { | ||
92 | goto cleanup; | ||
93 | } | ||
94 | if (!(bar->hidden_state = strdup("hide"))) { | ||
95 | goto cleanup; | ||
96 | } | ||
97 | if (!(bar->bindings = create_list())) { | ||
98 | goto cleanup; | ||
99 | } | ||
100 | if (!(bar->status_command = | ||
101 | strdup("while date +'%Y-%m-%d %l:%M:%S %p'; do sleep 1; done"))) { | ||
102 | goto cleanup; | ||
103 | } | ||
94 | // set default colors | 104 | // set default colors |
95 | if (!(bar->colors.background = strndup("#000000ff", 9))) { | 105 | if (!(bar->colors.background = strndup("#000000ff", 9))) { |
96 | goto cleanup; | 106 | goto cleanup; |
@@ -157,7 +167,7 @@ void invoke_swaybar(struct bar_config *bar) { | |||
157 | // Pipe to communicate errors | 167 | // Pipe to communicate errors |
158 | int filedes[2]; | 168 | int filedes[2]; |
159 | if (pipe(filedes) == -1) { | 169 | if (pipe(filedes) == -1) { |
160 | wlr_log(L_ERROR, "Pipe setup failed! Cannot fork into bar"); | 170 | wlr_log(WLR_ERROR, "Pipe setup failed! Cannot fork into bar"); |
161 | return; | 171 | return; |
162 | } | 172 | } |
163 | 173 | ||
@@ -174,7 +184,7 @@ void invoke_swaybar(struct bar_config *bar) { | |||
174 | if (!command) { | 184 | if (!command) { |
175 | const char msg[] = "Unable to allocate swaybar command string"; | 185 | const char msg[] = "Unable to allocate swaybar command string"; |
176 | size_t msg_len = sizeof(msg); | 186 | size_t msg_len = sizeof(msg); |
177 | if (write(filedes[1], &msg_len, sizeof(int))) {}; | 187 | if (write(filedes[1], &msg_len, sizeof(size_t))) {}; |
178 | if (write(filedes[1], msg, msg_len)) {}; | 188 | if (write(filedes[1], msg, msg_len)) {}; |
179 | close(filedes[1]); | 189 | close(filedes[1]); |
180 | exit(1); | 190 | exit(1); |
@@ -187,17 +197,17 @@ void invoke_swaybar(struct bar_config *bar) { | |||
187 | execvp(cmd[0], cmd); | 197 | execvp(cmd[0], cmd); |
188 | exit(1); | 198 | exit(1); |
189 | } | 199 | } |
190 | wlr_log(L_DEBUG, "Spawned swaybar %d", bar->pid); | 200 | wlr_log(WLR_DEBUG, "Spawned swaybar %d", bar->pid); |
191 | close(filedes[0]); | 201 | close(filedes[0]); |
192 | ssize_t len; | 202 | size_t len; |
193 | if (read(filedes[1], &len, sizeof(int)) == sizeof(int)) { | 203 | if (read(filedes[1], &len, sizeof(size_t)) == sizeof(size_t)) { |
194 | char *buf = malloc(len); | 204 | char *buf = malloc(len); |
195 | if(!buf) { | 205 | if(!buf) { |
196 | wlr_log(L_ERROR, "Cannot allocate error string"); | 206 | wlr_log(WLR_ERROR, "Cannot allocate error string"); |
197 | return; | 207 | return; |
198 | } | 208 | } |
199 | if (read(filedes[1], buf, len)) { | 209 | if (read(filedes[1], buf, len)) { |
200 | wlr_log(L_ERROR, "%s", buf); | 210 | wlr_log(WLR_ERROR, "%s", buf); |
201 | } | 211 | } |
202 | free(buf); | 212 | free(buf); |
203 | } | 213 | } |
@@ -234,7 +244,7 @@ void load_swaybars() { | |||
234 | if (bar->pid != 0) { | 244 | if (bar->pid != 0) { |
235 | terminate_swaybar(bar->pid); | 245 | terminate_swaybar(bar->pid); |
236 | } | 246 | } |
237 | wlr_log(L_DEBUG, "Invoking swaybar for bar id '%s'", bar->id); | 247 | wlr_log(WLR_DEBUG, "Invoking swaybar for bar id '%s'", bar->id); |
238 | invoke_swaybar(bar); | 248 | invoke_swaybar(bar); |
239 | } | 249 | } |
240 | } | 250 | } |
diff --git a/sway/config/input.c b/sway/config/input.c index 17303ccc..8d687a6d 100644 --- a/sway/config/input.c +++ b/sway/config/input.c | |||
@@ -8,17 +8,18 @@ | |||
8 | struct input_config *new_input_config(const char* identifier) { | 8 | struct input_config *new_input_config(const char* identifier) { |
9 | struct input_config *input = calloc(1, sizeof(struct input_config)); | 9 | struct input_config *input = calloc(1, sizeof(struct input_config)); |
10 | if (!input) { | 10 | if (!input) { |
11 | wlr_log(L_DEBUG, "Unable to allocate input config"); | 11 | wlr_log(WLR_DEBUG, "Unable to allocate input config"); |
12 | return NULL; | 12 | return NULL; |
13 | } | 13 | } |
14 | wlr_log(L_DEBUG, "new_input_config(%s)", identifier); | 14 | wlr_log(WLR_DEBUG, "new_input_config(%s)", identifier); |
15 | if (!(input->identifier = strdup(identifier))) { | 15 | if (!(input->identifier = strdup(identifier))) { |
16 | free(input); | 16 | free(input); |
17 | wlr_log(L_DEBUG, "Unable to allocate input config"); | 17 | wlr_log(WLR_DEBUG, "Unable to allocate input config"); |
18 | return NULL; | 18 | return NULL; |
19 | } | 19 | } |
20 | 20 | ||
21 | input->tap = INT_MIN; | 21 | input->tap = INT_MIN; |
22 | input->tap_button_map = INT_MIN; | ||
22 | input->drag_lock = INT_MIN; | 23 | input->drag_lock = INT_MIN; |
23 | input->dwt = INT_MIN; | 24 | input->dwt = INT_MIN; |
24 | input->send_events = INT_MIN; | 25 | input->send_events = INT_MIN; |
@@ -27,6 +28,7 @@ struct input_config *new_input_config(const char* identifier) { | |||
27 | input->natural_scroll = INT_MIN; | 28 | input->natural_scroll = INT_MIN; |
28 | input->accel_profile = INT_MIN; | 29 | input->accel_profile = INT_MIN; |
29 | input->pointer_accel = FLT_MIN; | 30 | input->pointer_accel = FLT_MIN; |
31 | input->scroll_button = INT_MIN; | ||
30 | input->scroll_method = INT_MIN; | 32 | input->scroll_method = INT_MIN; |
31 | input->left_handed = INT_MIN; | 33 | input->left_handed = INT_MIN; |
32 | input->repeat_delay = INT_MIN; | 34 | input->repeat_delay = INT_MIN; |
@@ -70,12 +72,18 @@ void merge_input_config(struct input_config *dst, struct input_config *src) { | |||
70 | if (src->scroll_method != INT_MIN) { | 72 | if (src->scroll_method != INT_MIN) { |
71 | dst->scroll_method = src->scroll_method; | 73 | dst->scroll_method = src->scroll_method; |
72 | } | 74 | } |
75 | if (src->scroll_button != INT_MIN) { | ||
76 | dst->scroll_button = src->scroll_button; | ||
77 | } | ||
73 | if (src->send_events != INT_MIN) { | 78 | if (src->send_events != INT_MIN) { |
74 | dst->send_events = src->send_events; | 79 | dst->send_events = src->send_events; |
75 | } | 80 | } |
76 | if (src->tap != INT_MIN) { | 81 | if (src->tap != INT_MIN) { |
77 | dst->tap = src->tap; | 82 | dst->tap = src->tap; |
78 | } | 83 | } |
84 | if (src->tap_button_map != INT_MIN) { | ||
85 | dst->tap_button_map = src->tap_button_map; | ||
86 | } | ||
79 | if (src->xkb_layout) { | 87 | if (src->xkb_layout) { |
80 | free(dst->xkb_layout); | 88 | free(dst->xkb_layout); |
81 | dst->xkb_layout = strdup(src->xkb_layout); | 89 | dst->xkb_layout = strdup(src->xkb_layout); |
@@ -112,7 +120,7 @@ void merge_input_config(struct input_config *dst, struct input_config *src) { | |||
112 | struct input_config *copy_input_config(struct input_config *ic) { | 120 | struct input_config *copy_input_config(struct input_config *ic) { |
113 | struct input_config *copy = calloc(1, sizeof(struct input_config)); | 121 | struct input_config *copy = calloc(1, sizeof(struct input_config)); |
114 | if (copy == NULL) { | 122 | if (copy == NULL) { |
115 | wlr_log(L_ERROR, "could not allocate input config"); | 123 | wlr_log(WLR_ERROR, "could not allocate input config"); |
116 | return NULL; | 124 | return NULL; |
117 | } | 125 | } |
118 | merge_input_config(copy, ic); | 126 | merge_input_config(copy, ic); |
diff --git a/sway/config/output.c b/sway/config/output.c index 648ded27..504c48c6 100644 --- a/sway/config/output.c +++ b/sway/config/output.c | |||
@@ -45,10 +45,6 @@ struct output_config *new_output_config(const char *name) { | |||
45 | } | 45 | } |
46 | 46 | ||
47 | void merge_output_config(struct output_config *dst, struct output_config *src) { | 47 | void merge_output_config(struct output_config *dst, struct output_config *src) { |
48 | if (src->name) { | ||
49 | free(dst->name); | ||
50 | dst->name = strdup(src->name); | ||
51 | } | ||
52 | if (src->enabled != -1) { | 48 | if (src->enabled != -1) { |
53 | dst->enabled = src->enabled; | 49 | dst->enabled = src->enabled; |
54 | } | 50 | } |
@@ -86,11 +82,61 @@ void merge_output_config(struct output_config *dst, struct output_config *src) { | |||
86 | } | 82 | } |
87 | } | 83 | } |
88 | 84 | ||
85 | static void merge_wildcard_on_all(struct output_config *wildcard) { | ||
86 | for (int i = 0; i < config->output_configs->length; i++) { | ||
87 | struct output_config *oc = config->output_configs->items[i]; | ||
88 | if (strcmp(wildcard->name, oc->name) != 0) { | ||
89 | wlr_log(WLR_DEBUG, "Merging output * config on %s", oc->name); | ||
90 | merge_output_config(oc, wildcard); | ||
91 | } | ||
92 | } | ||
93 | } | ||
94 | |||
95 | struct output_config *store_output_config(struct output_config *oc) { | ||
96 | bool wildcard = strcmp(oc->name, "*") == 0; | ||
97 | if (wildcard) { | ||
98 | merge_wildcard_on_all(oc); | ||
99 | } | ||
100 | |||
101 | int i = list_seq_find(config->output_configs, output_name_cmp, oc->name); | ||
102 | if (i >= 0) { | ||
103 | wlr_log(WLR_DEBUG, "Merging on top of existing output config"); | ||
104 | struct output_config *current = config->output_configs->items[i]; | ||
105 | merge_output_config(current, oc); | ||
106 | free_output_config(oc); | ||
107 | oc = current; | ||
108 | } else if (!wildcard) { | ||
109 | wlr_log(WLR_DEBUG, "Adding non-wildcard output config"); | ||
110 | i = list_seq_find(config->output_configs, output_name_cmp, "*"); | ||
111 | if (i >= 0) { | ||
112 | wlr_log(WLR_DEBUG, "Merging on top of output * config"); | ||
113 | struct output_config *current = new_output_config(oc->name); | ||
114 | merge_output_config(current, config->output_configs->items[i]); | ||
115 | merge_output_config(current, oc); | ||
116 | free_output_config(oc); | ||
117 | oc = current; | ||
118 | } | ||
119 | list_add(config->output_configs, oc); | ||
120 | } else { | ||
121 | // New wildcard config. Just add it | ||
122 | wlr_log(WLR_DEBUG, "Adding output * config"); | ||
123 | list_add(config->output_configs, oc); | ||
124 | } | ||
125 | |||
126 | wlr_log(WLR_DEBUG, "Config stored for output %s (enabled: %d) (%dx%d@%fHz " | ||
127 | "position %d,%d scale %f transform %d) (bg %s %s) (dpms %d)", | ||
128 | oc->name, oc->enabled, oc->width, oc->height, oc->refresh_rate, | ||
129 | oc->x, oc->y, oc->scale, oc->transform, oc->background, | ||
130 | oc->background_option, oc->dpms_state); | ||
131 | |||
132 | return oc; | ||
133 | } | ||
134 | |||
89 | static void set_mode(struct wlr_output *output, int width, int height, | 135 | static void set_mode(struct wlr_output *output, int width, int height, |
90 | float refresh_rate) { | 136 | float refresh_rate) { |
91 | int mhz = (int)(refresh_rate * 1000); | 137 | int mhz = (int)(refresh_rate * 1000); |
92 | if (wl_list_empty(&output->modes)) { | 138 | if (wl_list_empty(&output->modes)) { |
93 | wlr_log(L_DEBUG, "Assigning custom mode to %s", output->name); | 139 | wlr_log(WLR_DEBUG, "Assigning custom mode to %s", output->name); |
94 | wlr_output_set_custom_mode(output, width, height, mhz); | 140 | wlr_output_set_custom_mode(output, width, height, mhz); |
95 | return; | 141 | return; |
96 | } | 142 | } |
@@ -106,9 +152,9 @@ static void set_mode(struct wlr_output *output, int width, int height, | |||
106 | } | 152 | } |
107 | } | 153 | } |
108 | if (!best) { | 154 | if (!best) { |
109 | wlr_log(L_ERROR, "Configured mode for %s not available", output->name); | 155 | wlr_log(WLR_ERROR, "Configured mode for %s not available", output->name); |
110 | } else { | 156 | } else { |
111 | wlr_log(L_DEBUG, "Assigning configured mode to %s", output->name); | 157 | wlr_log(WLR_DEBUG, "Assigning configured mode to %s", output->name); |
112 | wlr_output_set_mode(output, best); | 158 | wlr_output_set_mode(output, best); |
113 | } | 159 | } |
114 | } | 160 | } |
@@ -116,7 +162,7 @@ static void set_mode(struct wlr_output *output, int width, int height, | |||
116 | void terminate_swaybg(pid_t pid) { | 162 | void terminate_swaybg(pid_t pid) { |
117 | int ret = kill(pid, SIGTERM); | 163 | int ret = kill(pid, SIGTERM); |
118 | if (ret != 0) { | 164 | if (ret != 0) { |
119 | wlr_log(L_ERROR, "Unable to terminate swaybg [pid: %d]", pid); | 165 | wlr_log(WLR_ERROR, "Unable to terminate swaybg [pid: %d]", pid); |
120 | } else { | 166 | } else { |
121 | int status; | 167 | int status; |
122 | waitpid(pid, &status, 0); | 168 | waitpid(pid, &status, 0); |
@@ -144,37 +190,27 @@ void apply_output_config(struct output_config *oc, struct sway_container *output | |||
144 | } | 190 | } |
145 | 191 | ||
146 | if (oc && oc->width > 0 && oc->height > 0) { | 192 | if (oc && oc->width > 0 && oc->height > 0) { |
147 | wlr_log(L_DEBUG, "Set %s mode to %dx%d (%f GHz)", oc->name, oc->width, | 193 | wlr_log(WLR_DEBUG, "Set %s mode to %dx%d (%f GHz)", oc->name, oc->width, |
148 | oc->height, oc->refresh_rate); | 194 | oc->height, oc->refresh_rate); |
149 | set_mode(wlr_output, oc->width, oc->height, oc->refresh_rate); | 195 | set_mode(wlr_output, oc->width, oc->height, oc->refresh_rate); |
150 | } | 196 | } |
151 | if (oc && oc->scale > 0) { | 197 | if (oc && oc->scale > 0) { |
152 | wlr_log(L_DEBUG, "Set %s scale to %f", oc->name, oc->scale); | 198 | wlr_log(WLR_DEBUG, "Set %s scale to %f", oc->name, oc->scale); |
153 | wlr_output_set_scale(wlr_output, oc->scale); | 199 | wlr_output_set_scale(wlr_output, oc->scale); |
154 | } | 200 | } |
155 | if (oc && oc->transform >= 0) { | 201 | if (oc && oc->transform >= 0) { |
156 | wlr_log(L_DEBUG, "Set %s transform to %d", oc->name, oc->transform); | 202 | wlr_log(WLR_DEBUG, "Set %s transform to %d", oc->name, oc->transform); |
157 | wlr_output_set_transform(wlr_output, oc->transform); | 203 | wlr_output_set_transform(wlr_output, oc->transform); |
158 | } | 204 | } |
159 | 205 | ||
160 | // Find position for it | 206 | // Find position for it |
161 | if (oc && (oc->x != -1 || oc->y != -1)) { | 207 | if (oc && (oc->x != -1 || oc->y != -1)) { |
162 | wlr_log(L_DEBUG, "Set %s position to %d, %d", oc->name, oc->x, oc->y); | 208 | wlr_log(WLR_DEBUG, "Set %s position to %d, %d", oc->name, oc->x, oc->y); |
163 | wlr_output_layout_add(output_layout, wlr_output, oc->x, oc->y); | 209 | wlr_output_layout_add(output_layout, wlr_output, oc->x, oc->y); |
164 | } else { | 210 | } else { |
165 | wlr_output_layout_add_auto(output_layout, wlr_output); | 211 | wlr_output_layout_add_auto(output_layout, wlr_output); |
166 | } | 212 | } |
167 | 213 | ||
168 | if (!oc || !oc->background) { | ||
169 | // Look for a * config for background | ||
170 | int i = list_seq_find(config->output_configs, output_name_cmp, "*"); | ||
171 | if (i >= 0) { | ||
172 | oc = config->output_configs->items[i]; | ||
173 | } else { | ||
174 | oc = NULL; | ||
175 | } | ||
176 | } | ||
177 | |||
178 | int output_i; | 214 | int output_i; |
179 | for (output_i = 0; output_i < root_container.children->length; ++output_i) { | 215 | for (output_i = 0; output_i < root_container.children->length; ++output_i) { |
180 | if (root_container.children->items[output_i] == output) { | 216 | if (root_container.children->items[output_i] == output) { |
@@ -187,7 +223,7 @@ void apply_output_config(struct output_config *oc, struct sway_container *output | |||
187 | terminate_swaybg(output->sway_output->bg_pid); | 223 | terminate_swaybg(output->sway_output->bg_pid); |
188 | } | 224 | } |
189 | 225 | ||
190 | wlr_log(L_DEBUG, "Setting background for output %d to %s", | 226 | wlr_log(WLR_DEBUG, "Setting background for output %d to %s", |
191 | output_i, oc->background); | 227 | output_i, oc->background); |
192 | 228 | ||
193 | size_t len = snprintf(NULL, 0, "%s %d %s %s", | 229 | size_t len = snprintf(NULL, 0, "%s %d %s %s", |
@@ -195,28 +231,30 @@ void apply_output_config(struct output_config *oc, struct sway_container *output | |||
195 | output_i, oc->background, oc->background_option); | 231 | output_i, oc->background, oc->background_option); |
196 | char *command = malloc(len + 1); | 232 | char *command = malloc(len + 1); |
197 | if (!command) { | 233 | if (!command) { |
198 | wlr_log(L_DEBUG, "Unable to allocate swaybg command"); | 234 | wlr_log(WLR_DEBUG, "Unable to allocate swaybg command"); |
199 | return; | 235 | return; |
200 | } | 236 | } |
201 | snprintf(command, len + 1, "%s %d %s %s", | 237 | snprintf(command, len + 1, "%s %d %s %s", |
202 | config->swaybg_command ? config->swaybg_command : "swaybg", | 238 | config->swaybg_command ? config->swaybg_command : "swaybg", |
203 | output_i, oc->background, oc->background_option); | 239 | output_i, oc->background, oc->background_option); |
204 | wlr_log(L_DEBUG, "-> %s", command); | 240 | wlr_log(WLR_DEBUG, "-> %s", command); |
205 | 241 | ||
206 | char *const cmd[] = { "sh", "-c", command, NULL }; | 242 | char *const cmd[] = { "sh", "-c", command, NULL }; |
207 | output->sway_output->bg_pid = fork(); | 243 | output->sway_output->bg_pid = fork(); |
208 | if (output->sway_output->bg_pid == 0) { | 244 | if (output->sway_output->bg_pid == 0) { |
209 | execvp(cmd[0], cmd); | 245 | execvp(cmd[0], cmd); |
246 | } else { | ||
247 | free(command); | ||
210 | } | 248 | } |
211 | } | 249 | } |
212 | if (oc && oc->dpms_state != DPMS_IGNORE) { | 250 | if (oc && oc->dpms_state != DPMS_IGNORE) { |
213 | switch (oc->dpms_state) { | 251 | switch (oc->dpms_state) { |
214 | case DPMS_ON: | 252 | case DPMS_ON: |
215 | wlr_log(L_DEBUG, "Turning on screen"); | 253 | wlr_log(WLR_DEBUG, "Turning on screen"); |
216 | wlr_output_enable(wlr_output, true); | 254 | wlr_output_enable(wlr_output, true); |
217 | break; | 255 | break; |
218 | case DPMS_OFF: | 256 | case DPMS_OFF: |
219 | wlr_log(L_DEBUG, "Turning off screen"); | 257 | wlr_log(WLR_DEBUG, "Turning off screen"); |
220 | wlr_output_enable(wlr_output, false); | 258 | wlr_output_enable(wlr_output, false); |
221 | break; | 259 | break; |
222 | case DPMS_IGNORE: | 260 | case DPMS_IGNORE: |
@@ -225,6 +263,60 @@ void apply_output_config(struct output_config *oc, struct sway_container *output | |||
225 | } | 263 | } |
226 | } | 264 | } |
227 | 265 | ||
266 | static struct output_config *get_output_config(char *name, char *identifier) { | ||
267 | int i = list_seq_find(config->output_configs, output_name_cmp, name); | ||
268 | if (i >= 0) { | ||
269 | return config->output_configs->items[i]; | ||
270 | } | ||
271 | |||
272 | i = list_seq_find(config->output_configs, output_name_cmp, identifier); | ||
273 | if (i >= 0) { | ||
274 | return config->output_configs->items[i]; | ||
275 | } | ||
276 | |||
277 | return NULL; | ||
278 | } | ||
279 | |||
280 | void apply_output_config_to_outputs(struct output_config *oc) { | ||
281 | // Try to find the output container and apply configuration now. If | ||
282 | // this is during startup then there will be no container and config | ||
283 | // will be applied during normal "new output" event from wlroots. | ||
284 | bool wildcard = strcmp(oc->name, "*") == 0; | ||
285 | char id[128]; | ||
286 | struct sway_output *sway_output; | ||
287 | wl_list_for_each(sway_output, &root_container.sway_root->outputs, link) { | ||
288 | char *name = sway_output->wlr_output->name; | ||
289 | output_get_identifier(id, sizeof(id), sway_output); | ||
290 | if (wildcard || !strcmp(name, oc->name) || !strcmp(id, oc->name)) { | ||
291 | if (!sway_output->swayc) { | ||
292 | if (!oc->enabled) { | ||
293 | if (!wildcard) { | ||
294 | break; | ||
295 | } | ||
296 | continue; | ||
297 | } | ||
298 | |||
299 | output_enable(sway_output); | ||
300 | } | ||
301 | |||
302 | struct output_config *current = oc; | ||
303 | if (wildcard) { | ||
304 | struct output_config *tmp = get_output_config(name, id); | ||
305 | if (tmp) { | ||
306 | current = tmp; | ||
307 | } | ||
308 | } | ||
309 | apply_output_config(current, sway_output->swayc); | ||
310 | |||
311 | if (!wildcard) { | ||
312 | // Stop looking if the output config isn't applicable to all | ||
313 | // outputs | ||
314 | break; | ||
315 | } | ||
316 | } | ||
317 | } | ||
318 | } | ||
319 | |||
228 | void free_output_config(struct output_config *oc) { | 320 | void free_output_config(struct output_config *oc) { |
229 | if (!oc) { | 321 | if (!oc) { |
230 | return; | 322 | return; |
@@ -234,3 +326,29 @@ void free_output_config(struct output_config *oc) { | |||
234 | free(oc->background_option); | 326 | free(oc->background_option); |
235 | free(oc); | 327 | free(oc); |
236 | } | 328 | } |
329 | |||
330 | static void default_output_config(struct output_config *oc, | ||
331 | struct wlr_output *wlr_output) { | ||
332 | oc->enabled = 1; | ||
333 | if (!wl_list_empty(&wlr_output->modes)) { | ||
334 | struct wlr_output_mode *mode = | ||
335 | wl_container_of(wlr_output->modes.prev, mode, link); | ||
336 | oc->width = mode->width; | ||
337 | oc->height = mode->height; | ||
338 | oc->refresh_rate = mode->refresh; | ||
339 | } | ||
340 | oc->x = oc->y = -1; | ||
341 | oc->scale = 1; | ||
342 | oc->transform = WL_OUTPUT_TRANSFORM_NORMAL; | ||
343 | } | ||
344 | |||
345 | void create_default_output_configs(void) { | ||
346 | struct sway_output *sway_output; | ||
347 | wl_list_for_each(sway_output, &root_container.sway_root->outputs, link) { | ||
348 | char *name = sway_output->wlr_output->name; | ||
349 | struct output_config *oc = new_output_config(name); | ||
350 | default_output_config(oc, sway_output->wlr_output); | ||
351 | list_add(config->output_configs, oc); | ||
352 | } | ||
353 | } | ||
354 | |||
diff --git a/sway/config/seat.c b/sway/config/seat.c index bd8b45c8..83dac4c0 100644 --- a/sway/config/seat.c +++ b/sway/config/seat.c | |||
@@ -7,11 +7,11 @@ | |||
7 | struct seat_config *new_seat_config(const char* name) { | 7 | struct seat_config *new_seat_config(const char* name) { |
8 | struct seat_config *seat = calloc(1, sizeof(struct seat_config)); | 8 | struct seat_config *seat = calloc(1, sizeof(struct seat_config)); |
9 | if (!seat) { | 9 | if (!seat) { |
10 | wlr_log(L_DEBUG, "Unable to allocate seat config"); | 10 | wlr_log(WLR_DEBUG, "Unable to allocate seat config"); |
11 | return NULL; | 11 | return NULL; |
12 | } | 12 | } |
13 | 13 | ||
14 | wlr_log(L_DEBUG, "new_seat_config(%s)", name); | 14 | wlr_log(WLR_DEBUG, "new_seat_config(%s)", name); |
15 | seat->name = strdup(name); | 15 | seat->name = strdup(name); |
16 | if (!sway_assert(seat->name, "could not allocate name for seat")) { | 16 | if (!sway_assert(seat->name, "could not allocate name for seat")) { |
17 | free(seat); | 17 | free(seat); |
@@ -34,7 +34,7 @@ struct seat_attachment_config *seat_attachment_config_new() { | |||
34 | struct seat_attachment_config *attachment = | 34 | struct seat_attachment_config *attachment = |
35 | calloc(1, sizeof(struct seat_attachment_config)); | 35 | calloc(1, sizeof(struct seat_attachment_config)); |
36 | if (!attachment) { | 36 | if (!attachment) { |
37 | wlr_log(L_DEBUG, "cannot allocate attachment config"); | 37 | wlr_log(WLR_DEBUG, "cannot allocate attachment config"); |
38 | return NULL; | 38 | return NULL; |
39 | } | 39 | } |
40 | return attachment; | 40 | return attachment; |
diff --git a/sway/criteria.c b/sway/criteria.c index d9f09ecc..c2e9c07e 100644 --- a/sway/criteria.c +++ b/sway/criteria.c | |||
@@ -37,7 +37,7 @@ void criteria_destroy(struct criteria *criteria) { | |||
37 | pcre_free(criteria->con_mark); | 37 | pcre_free(criteria->con_mark); |
38 | pcre_free(criteria->window_role); | 38 | pcre_free(criteria->window_role); |
39 | free(criteria->workspace); | 39 | free(criteria->workspace); |
40 | 40 | free(criteria->cmdlist); | |
41 | free(criteria->raw); | 41 | free(criteria->raw); |
42 | free(criteria); | 42 | free(criteria); |
43 | } | 43 | } |
@@ -46,6 +46,31 @@ static int regex_cmp(const char *item, const pcre *regex) { | |||
46 | return pcre_exec(regex, NULL, item, strlen(item), 0, 0, NULL, 0); | 46 | return pcre_exec(regex, NULL, item, strlen(item), 0, 0, NULL, 0); |
47 | } | 47 | } |
48 | 48 | ||
49 | static int cmp_urgent(const void *_a, const void *_b) { | ||
50 | struct sway_view *a = *(void **)_a; | ||
51 | struct sway_view *b = *(void **)_b; | ||
52 | |||
53 | if (a->urgent.tv_sec < b->urgent.tv_sec) { | ||
54 | return -1; | ||
55 | } else if (a->urgent.tv_sec > b->urgent.tv_sec) { | ||
56 | return 1; | ||
57 | } | ||
58 | if (a->urgent.tv_nsec < b->urgent.tv_nsec) { | ||
59 | return -1; | ||
60 | } else if (a->urgent.tv_nsec > b->urgent.tv_nsec) { | ||
61 | return 1; | ||
62 | } | ||
63 | return 0; | ||
64 | } | ||
65 | |||
66 | static void find_urgent_iterator(struct sway_container *swayc, void *data) { | ||
67 | if (swayc->type != C_VIEW || !view_is_urgent(swayc->sway_view)) { | ||
68 | return; | ||
69 | } | ||
70 | list_t *urgent_views = data; | ||
71 | list_add(urgent_views, swayc->sway_view); | ||
72 | } | ||
73 | |||
49 | static bool criteria_matches_view(struct criteria *criteria, | 74 | static bool criteria_matches_view(struct criteria *criteria, |
50 | struct sway_view *view) { | 75 | struct sway_view *view) { |
51 | if (criteria->title) { | 76 | if (criteria->title) { |
@@ -133,8 +158,23 @@ static bool criteria_matches_view(struct criteria *criteria, | |||
133 | } | 158 | } |
134 | 159 | ||
135 | if (criteria->urgent) { | 160 | if (criteria->urgent) { |
136 | // TODO | 161 | if (!view_is_urgent(view)) { |
137 | return false; | 162 | return false; |
163 | } | ||
164 | list_t *urgent_views = create_list(); | ||
165 | container_for_each_descendant_dfs(&root_container, | ||
166 | find_urgent_iterator, urgent_views); | ||
167 | list_stable_sort(urgent_views, cmp_urgent); | ||
168 | struct sway_view *target; | ||
169 | if (criteria->urgent == 'o') { // oldest | ||
170 | target = urgent_views->items[0]; | ||
171 | } else { // latest | ||
172 | target = urgent_views->items[urgent_views->length - 1]; | ||
173 | } | ||
174 | list_free(urgent_views); | ||
175 | if (view != target) { | ||
176 | return false; | ||
177 | } | ||
138 | } | 178 | } |
139 | 179 | ||
140 | if (criteria->workspace) { | 180 | if (criteria->workspace) { |
@@ -185,6 +225,15 @@ list_t *criteria_get_views(struct criteria *criteria) { | |||
185 | }; | 225 | }; |
186 | container_for_each_descendant_dfs(&root_container, | 226 | container_for_each_descendant_dfs(&root_container, |
187 | criteria_get_views_iterator, &data); | 227 | criteria_get_views_iterator, &data); |
228 | |||
229 | // Scratchpad items which are hidden are not in the tree. | ||
230 | for (int i = 0; i < root_container.sway_root->scratchpad->length; ++i) { | ||
231 | struct sway_container *con = | ||
232 | root_container.sway_root->scratchpad->items[i]; | ||
233 | if (!con->parent) { | ||
234 | criteria_get_views_iterator(con, &data); | ||
235 | } | ||
236 | } | ||
188 | return matches; | 237 | return matches; |
189 | } | 238 | } |
190 | 239 | ||
@@ -507,7 +556,7 @@ struct criteria *criteria_parse(char *raw, char **error_arg) { | |||
507 | } | 556 | } |
508 | unescape(value); | 557 | unescape(value); |
509 | } | 558 | } |
510 | wlr_log(L_DEBUG, "Found pair: %s=%s", name, value); | 559 | wlr_log(WLR_DEBUG, "Found pair: %s=%s", name, value); |
511 | if (!parse_token(criteria, name, value)) { | 560 | if (!parse_token(criteria, name, value)) { |
512 | *error_arg = error; | 561 | *error_arg = error; |
513 | goto cleanup; | 562 | goto cleanup; |
diff --git a/sway/desktop/desktop.c b/sway/desktop/desktop.c index e495790c..6575519d 100644 --- a/sway/desktop/desktop.c +++ b/sway/desktop/desktop.c | |||
@@ -13,3 +13,12 @@ void desktop_damage_surface(struct wlr_surface *surface, double lx, double ly, | |||
13 | } | 13 | } |
14 | } | 14 | } |
15 | } | 15 | } |
16 | |||
17 | void desktop_damage_whole_container(struct sway_container *con) { | ||
18 | for (int i = 0; i < root_container.children->length; ++i) { | ||
19 | struct sway_container *cont = root_container.children->items[i]; | ||
20 | if (cont->type == C_OUTPUT) { | ||
21 | output_damage_whole_container(cont->sway_output, con); | ||
22 | } | ||
23 | } | ||
24 | } | ||
diff --git a/sway/desktop/idle_inhibit_v1.c b/sway/desktop/idle_inhibit_v1.c new file mode 100644 index 00000000..da17d0f2 --- /dev/null +++ b/sway/desktop/idle_inhibit_v1.c | |||
@@ -0,0 +1,80 @@ | |||
1 | #include <stdlib.h> | ||
2 | #include <wlr/types/wlr_idle.h> | ||
3 | #include "log.h" | ||
4 | #include "sway/desktop/idle_inhibit_v1.h" | ||
5 | #include "sway/tree/view.h" | ||
6 | #include "sway/server.h" | ||
7 | |||
8 | |||
9 | static void handle_destroy(struct wl_listener *listener, void *data) { | ||
10 | struct sway_idle_inhibitor_v1 *inhibitor = | ||
11 | wl_container_of(listener, inhibitor, destroy); | ||
12 | wlr_log(WLR_DEBUG, "Sway idle inhibitor destroyed"); | ||
13 | wl_list_remove(&inhibitor->link); | ||
14 | wl_list_remove(&inhibitor->destroy.link); | ||
15 | idle_inhibit_v1_check_active(inhibitor->manager); | ||
16 | free(inhibitor); | ||
17 | } | ||
18 | |||
19 | void handle_idle_inhibitor_v1(struct wl_listener *listener, void *data) { | ||
20 | struct wlr_idle_inhibitor_v1 *wlr_inhibitor = data; | ||
21 | struct sway_idle_inhibit_manager_v1 *manager = | ||
22 | wl_container_of(listener, manager, new_idle_inhibitor_v1); | ||
23 | wlr_log(WLR_DEBUG, "New sway idle inhibitor"); | ||
24 | |||
25 | struct sway_idle_inhibitor_v1 *inhibitor = | ||
26 | calloc(1, sizeof(struct sway_idle_inhibitor_v1)); | ||
27 | if (!inhibitor) { | ||
28 | return; | ||
29 | } | ||
30 | |||
31 | inhibitor->manager = manager; | ||
32 | inhibitor->view = view_from_wlr_surface(wlr_inhibitor->surface); | ||
33 | wl_list_insert(&manager->inhibitors, &inhibitor->link); | ||
34 | |||
35 | |||
36 | inhibitor->destroy.notify = handle_destroy; | ||
37 | wl_signal_add(&wlr_inhibitor->events.destroy, &inhibitor->destroy); | ||
38 | |||
39 | idle_inhibit_v1_check_active(manager); | ||
40 | } | ||
41 | |||
42 | void idle_inhibit_v1_check_active( | ||
43 | struct sway_idle_inhibit_manager_v1 *manager) { | ||
44 | struct sway_idle_inhibitor_v1 *inhibitor; | ||
45 | bool inhibited = false; | ||
46 | wl_list_for_each(inhibitor, &manager->inhibitors, link) { | ||
47 | if (!inhibitor->view) { | ||
48 | /* Cannot guess if view is visible so assume it is */ | ||
49 | inhibited = true; | ||
50 | break; | ||
51 | } | ||
52 | if (view_is_visible(inhibitor->view)) { | ||
53 | inhibited = true; | ||
54 | break; | ||
55 | } | ||
56 | } | ||
57 | wlr_idle_set_enabled(manager->idle, NULL, !inhibited); | ||
58 | } | ||
59 | |||
60 | struct sway_idle_inhibit_manager_v1 *sway_idle_inhibit_manager_v1_create( | ||
61 | struct wl_display *wl_display, struct wlr_idle *idle) { | ||
62 | struct sway_idle_inhibit_manager_v1 *manager = | ||
63 | calloc(1, sizeof(struct sway_idle_inhibit_manager_v1)); | ||
64 | if (!manager) { | ||
65 | return NULL; | ||
66 | } | ||
67 | |||
68 | manager->wlr_manager = wlr_idle_inhibit_v1_create(wl_display); | ||
69 | if (!manager->wlr_manager) { | ||
70 | free(manager); | ||
71 | return NULL; | ||
72 | } | ||
73 | manager->idle = idle; | ||
74 | wl_signal_add(&manager->wlr_manager->events.new_inhibitor, | ||
75 | &manager->new_idle_inhibitor_v1); | ||
76 | manager->new_idle_inhibitor_v1.notify = handle_idle_inhibitor_v1; | ||
77 | wl_list_init(&manager->inhibitors); | ||
78 | |||
79 | return manager; | ||
80 | } | ||
diff --git a/sway/desktop/layer_shell.c b/sway/desktop/layer_shell.c index de1fe349..a7d96717 100644 --- a/sway/desktop/layer_shell.c +++ b/sway/desktop/layer_shell.c | |||
@@ -12,7 +12,6 @@ | |||
12 | #include "sway/layers.h" | 12 | #include "sway/layers.h" |
13 | #include "sway/output.h" | 13 | #include "sway/output.h" |
14 | #include "sway/server.h" | 14 | #include "sway/server.h" |
15 | #include "sway/tree/arrange.h" | ||
16 | #include "sway/tree/layout.h" | 15 | #include "sway/tree/layout.h" |
17 | #include "log.h" | 16 | #include "log.h" |
18 | 17 | ||
@@ -174,9 +173,9 @@ void arrange_layers(struct sway_output *output) { | |||
174 | 173 | ||
175 | if (memcmp(&usable_area, &output->usable_area, | 174 | if (memcmp(&usable_area, &output->usable_area, |
176 | sizeof(struct wlr_box)) != 0) { | 175 | sizeof(struct wlr_box)) != 0) { |
177 | wlr_log(L_DEBUG, "Usable area changed, rearranging output"); | 176 | wlr_log(WLR_DEBUG, "Usable area changed, rearranging output"); |
178 | memcpy(&output->usable_area, &usable_area, sizeof(struct wlr_box)); | 177 | memcpy(&output->usable_area, &usable_area, sizeof(struct wlr_box)); |
179 | arrange_and_commit(output->swayc); | 178 | container_set_dirty(output->swayc); |
180 | } | 179 | } |
181 | 180 | ||
182 | // Arrange non-exlusive surfaces from top->bottom | 181 | // Arrange non-exlusive surfaces from top->bottom |
@@ -269,7 +268,7 @@ static void unmap(struct sway_layer_surface *sway_layer) { | |||
269 | static void handle_destroy(struct wl_listener *listener, void *data) { | 268 | static void handle_destroy(struct wl_listener *listener, void *data) { |
270 | struct sway_layer_surface *sway_layer = | 269 | struct sway_layer_surface *sway_layer = |
271 | wl_container_of(listener, sway_layer, destroy); | 270 | wl_container_of(listener, sway_layer, destroy); |
272 | wlr_log(L_DEBUG, "Layer surface destroyed (%s)", | 271 | wlr_log(WLR_DEBUG, "Layer surface destroyed (%s)", |
273 | sway_layer->layer_surface->namespace); | 272 | sway_layer->layer_surface->namespace); |
274 | if (sway_layer->layer_surface->mapped) { | 273 | if (sway_layer->layer_surface->mapped) { |
275 | unmap(sway_layer); | 274 | unmap(sway_layer); |
@@ -316,7 +315,7 @@ void handle_layer_shell_surface(struct wl_listener *listener, void *data) { | |||
316 | struct wlr_layer_surface *layer_surface = data; | 315 | struct wlr_layer_surface *layer_surface = data; |
317 | struct sway_server *server = | 316 | struct sway_server *server = |
318 | wl_container_of(listener, server, layer_shell_surface); | 317 | wl_container_of(listener, server, layer_shell_surface); |
319 | wlr_log(L_DEBUG, "new layer surface: namespace %s layer %d anchor %d " | 318 | wlr_log(WLR_DEBUG, "new layer surface: namespace %s layer %d anchor %d " |
320 | "size %dx%d margin %d,%d,%d,%d", | 319 | "size %dx%d margin %d,%d,%d,%d", |
321 | layer_surface->namespace, layer_surface->layer, layer_surface->layer, | 320 | layer_surface->namespace, layer_surface->layer, layer_surface->layer, |
322 | layer_surface->client_pending.desired_width, | 321 | layer_surface->client_pending.desired_width, |
@@ -326,12 +325,6 @@ void handle_layer_shell_surface(struct wl_listener *listener, void *data) { | |||
326 | layer_surface->client_pending.margin.bottom, | 325 | layer_surface->client_pending.margin.bottom, |
327 | layer_surface->client_pending.margin.left); | 326 | layer_surface->client_pending.margin.left); |
328 | 327 | ||
329 | struct sway_layer_surface *sway_layer = | ||
330 | calloc(1, sizeof(struct sway_layer_surface)); | ||
331 | if (!sway_layer) { | ||
332 | return; | ||
333 | } | ||
334 | |||
335 | if (!layer_surface->output) { | 328 | if (!layer_surface->output) { |
336 | // Assign last active output | 329 | // Assign last active output |
337 | struct sway_container *output = NULL; | 330 | struct sway_container *output = NULL; |
@@ -353,6 +346,12 @@ void handle_layer_shell_surface(struct wl_listener *listener, void *data) { | |||
353 | layer_surface->output = output->sway_output->wlr_output; | 346 | layer_surface->output = output->sway_output->wlr_output; |
354 | } | 347 | } |
355 | 348 | ||
349 | struct sway_layer_surface *sway_layer = | ||
350 | calloc(1, sizeof(struct sway_layer_surface)); | ||
351 | if (!sway_layer) { | ||
352 | return; | ||
353 | } | ||
354 | |||
356 | sway_layer->surface_commit.notify = handle_surface_commit; | 355 | sway_layer->surface_commit.notify = handle_surface_commit; |
357 | wl_signal_add(&layer_surface->surface->events.commit, | 356 | wl_signal_add(&layer_surface->surface->events.commit, |
358 | &sway_layer->surface_commit); | 357 | &sway_layer->surface_commit); |
diff --git a/sway/desktop/output.c b/sway/desktop/output.c index 1e7494b3..a206ac6b 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c | |||
@@ -56,33 +56,15 @@ static void rotate_child_position(double *sx, double *sy, double sw, double sh, | |||
56 | *sy = ry + ph/2 - sh/2; | 56 | *sy = ry + ph/2 - sh/2; |
57 | } | 57 | } |
58 | 58 | ||
59 | /** | 59 | bool output_get_surface_box(struct root_geometry *geo, |
60 | * Contains a surface's root geometry information. For instance, when rendering | ||
61 | * a popup, this will contain the parent view's position and size. | ||
62 | */ | ||
63 | struct root_geometry { | ||
64 | double x, y; | ||
65 | int width, height; | ||
66 | float rotation; | ||
67 | }; | ||
68 | |||
69 | struct render_data { | ||
70 | struct root_geometry root_geo; | ||
71 | struct sway_output *output; | ||
72 | pixman_region32_t *damage; | ||
73 | struct sway_view *view; | ||
74 | float alpha; | ||
75 | }; | ||
76 | |||
77 | static bool get_surface_box(struct root_geometry *geo, | ||
78 | struct sway_output *output, struct wlr_surface *surface, int sx, int sy, | 60 | struct sway_output *output, struct wlr_surface *surface, int sx, int sy, |
79 | struct wlr_box *surface_box) { | 61 | struct wlr_box *surface_box) { |
80 | if (!wlr_surface_has_buffer(surface)) { | 62 | if (!wlr_surface_has_buffer(surface)) { |
81 | return false; | 63 | return false; |
82 | } | 64 | } |
83 | 65 | ||
84 | int sw = surface->current->width; | 66 | int sw = surface->current.width; |
85 | int sh = surface->current->height; | 67 | int sh = surface->current.height; |
86 | 68 | ||
87 | double _sx = sx, _sy = sy; | 69 | double _sx = sx, _sy = sy; |
88 | rotate_child_position(&_sx, &_sy, sw, sh, geo->width, geo->height, | 70 | rotate_child_position(&_sx, &_sy, sw, sh, geo->width, geo->height, |
@@ -110,24 +92,23 @@ static bool get_surface_box(struct root_geometry *geo, | |||
110 | return wlr_box_intersection(&output_box, &rotated_box, &intersection); | 92 | return wlr_box_intersection(&output_box, &rotated_box, &intersection); |
111 | } | 93 | } |
112 | 94 | ||
113 | static void surface_for_each_surface(struct wlr_surface *surface, | 95 | void output_surface_for_each_surface(struct wlr_surface *surface, |
114 | double ox, double oy, struct root_geometry *geo, | 96 | double ox, double oy, struct root_geometry *geo, |
115 | wlr_surface_iterator_func_t iterator, void *user_data) { | 97 | wlr_surface_iterator_func_t iterator, void *user_data) { |
116 | geo->x = ox; | 98 | geo->x = ox; |
117 | geo->y = oy; | 99 | geo->y = oy; |
118 | geo->width = surface->current->width; | 100 | geo->width = surface->current.width; |
119 | geo->height = surface->current->height; | 101 | geo->height = surface->current.height; |
120 | geo->rotation = 0; | 102 | geo->rotation = 0; |
121 | 103 | ||
122 | wlr_surface_for_each_surface(surface, iterator, user_data); | 104 | wlr_surface_for_each_surface(surface, iterator, user_data); |
123 | } | 105 | } |
124 | 106 | ||
125 | static void output_view_for_each_surface(struct sway_view *view, | 107 | void output_view_for_each_surface(struct sway_view *view, |
126 | struct root_geometry *geo, wlr_surface_iterator_func_t iterator, | 108 | struct sway_output *output, struct root_geometry *geo, |
127 | void *user_data) { | 109 | wlr_surface_iterator_func_t iterator, void *user_data) { |
128 | struct render_data *data = user_data; | 110 | geo->x = view->swayc->current.view_x - output->swayc->current.swayc_x; |
129 | geo->x = view->swayc->current.view_x - data->output->swayc->current.swayc_x; | 111 | geo->y = view->swayc->current.view_y - output->swayc->current.swayc_y; |
130 | geo->y = view->swayc->current.view_y - data->output->swayc->current.swayc_y; | ||
131 | geo->width = view->swayc->current.view_width; | 112 | geo->width = view->swayc->current.view_width; |
132 | geo->height = view->swayc->current.view_height; | 113 | geo->height = view->swayc->current.view_height; |
133 | geo->rotation = 0; // TODO | 114 | geo->rotation = 0; // TODO |
@@ -135,20 +116,20 @@ static void output_view_for_each_surface(struct sway_view *view, | |||
135 | view_for_each_surface(view, iterator, user_data); | 116 | view_for_each_surface(view, iterator, user_data); |
136 | } | 117 | } |
137 | 118 | ||
138 | static void layer_for_each_surface(struct wl_list *layer_surfaces, | 119 | void output_layer_for_each_surface(struct wl_list *layer_surfaces, |
139 | struct root_geometry *geo, wlr_surface_iterator_func_t iterator, | 120 | struct root_geometry *geo, wlr_surface_iterator_func_t iterator, |
140 | void *user_data) { | 121 | void *user_data) { |
141 | struct sway_layer_surface *layer_surface; | 122 | struct sway_layer_surface *layer_surface; |
142 | wl_list_for_each(layer_surface, layer_surfaces, link) { | 123 | wl_list_for_each(layer_surface, layer_surfaces, link) { |
143 | struct wlr_layer_surface *wlr_layer_surface = | 124 | struct wlr_layer_surface *wlr_layer_surface = |
144 | layer_surface->layer_surface; | 125 | layer_surface->layer_surface; |
145 | surface_for_each_surface(wlr_layer_surface->surface, | 126 | output_surface_for_each_surface(wlr_layer_surface->surface, |
146 | layer_surface->geo.x, layer_surface->geo.y, geo, iterator, | 127 | layer_surface->geo.x, layer_surface->geo.y, geo, iterator, |
147 | user_data); | 128 | user_data); |
148 | } | 129 | } |
149 | } | 130 | } |
150 | 131 | ||
151 | static void unmanaged_for_each_surface(struct wl_list *unmanaged, | 132 | void output_unmanaged_for_each_surface(struct wl_list *unmanaged, |
152 | struct sway_output *output, struct root_geometry *geo, | 133 | struct sway_output *output, struct root_geometry *geo, |
153 | wlr_surface_iterator_func_t iterator, void *user_data) { | 134 | wlr_surface_iterator_func_t iterator, void *user_data) { |
154 | struct sway_xwayland_unmanaged *unmanaged_surface; | 135 | struct sway_xwayland_unmanaged *unmanaged_surface; |
@@ -158,12 +139,12 @@ static void unmanaged_for_each_surface(struct wl_list *unmanaged, | |||
158 | double ox = unmanaged_surface->lx - output->swayc->current.swayc_x; | 139 | double ox = unmanaged_surface->lx - output->swayc->current.swayc_x; |
159 | double oy = unmanaged_surface->ly - output->swayc->current.swayc_y; | 140 | double oy = unmanaged_surface->ly - output->swayc->current.swayc_y; |
160 | 141 | ||
161 | surface_for_each_surface(xsurface->surface, ox, oy, geo, | 142 | output_surface_for_each_surface(xsurface->surface, ox, oy, geo, |
162 | iterator, user_data); | 143 | iterator, user_data); |
163 | } | 144 | } |
164 | } | 145 | } |
165 | 146 | ||
166 | static void drag_icons_for_each_surface(struct wl_list *drag_icons, | 147 | void output_drag_icons_for_each_surface(struct wl_list *drag_icons, |
167 | struct sway_output *output, struct root_geometry *geo, | 148 | struct sway_output *output, struct root_geometry *geo, |
168 | wlr_surface_iterator_func_t iterator, void *user_data) { | 149 | wlr_surface_iterator_func_t iterator, void *user_data) { |
169 | struct sway_drag_icon *drag_icon; | 150 | struct sway_drag_icon *drag_icon; |
@@ -172,7 +153,7 @@ static void drag_icons_for_each_surface(struct wl_list *drag_icons, | |||
172 | double oy = drag_icon->y - output->swayc->y; | 153 | double oy = drag_icon->y - output->swayc->y; |
173 | 154 | ||
174 | if (drag_icon->wlr_drag_icon->mapped) { | 155 | if (drag_icon->wlr_drag_icon->mapped) { |
175 | surface_for_each_surface(drag_icon->wlr_drag_icon->surface, | 156 | output_surface_for_each_surface(drag_icon->wlr_drag_icon->surface, |
176 | ox, oy, geo, iterator, user_data); | 157 | ox, oy, geo, iterator, user_data); |
177 | } | 158 | } |
178 | } | 159 | } |
@@ -185,722 +166,7 @@ static void scale_box(struct wlr_box *box, float scale) { | |||
185 | box->height *= scale; | 166 | box->height *= scale; |
186 | } | 167 | } |
187 | 168 | ||
188 | static void scissor_output(struct wlr_output *wlr_output, | 169 | struct sway_container *output_get_active_workspace(struct sway_output *output) { |
189 | pixman_box32_t *rect) { | ||
190 | struct wlr_renderer *renderer = wlr_backend_get_renderer(wlr_output->backend); | ||
191 | assert(renderer); | ||
192 | |||
193 | struct wlr_box box = { | ||
194 | .x = rect->x1, | ||
195 | .y = rect->y1, | ||
196 | .width = rect->x2 - rect->x1, | ||
197 | .height = rect->y2 - rect->y1, | ||
198 | }; | ||
199 | |||
200 | int ow, oh; | ||
201 | wlr_output_transformed_resolution(wlr_output, &ow, &oh); | ||
202 | |||
203 | enum wl_output_transform transform = | ||
204 | wlr_output_transform_invert(wlr_output->transform); | ||
205 | wlr_box_transform(&box, transform, ow, oh, &box); | ||
206 | |||
207 | wlr_renderer_scissor(renderer, &box); | ||
208 | } | ||
209 | |||
210 | static void render_texture(struct wlr_output *wlr_output, | ||
211 | pixman_region32_t *output_damage, struct wlr_texture *texture, | ||
212 | const struct wlr_box *box, const float matrix[static 9], float alpha) { | ||
213 | struct wlr_renderer *renderer = | ||
214 | wlr_backend_get_renderer(wlr_output->backend); | ||
215 | |||
216 | pixman_region32_t damage; | ||
217 | pixman_region32_init(&damage); | ||
218 | pixman_region32_union_rect(&damage, &damage, box->x, box->y, | ||
219 | box->width, box->height); | ||
220 | pixman_region32_intersect(&damage, &damage, output_damage); | ||
221 | bool damaged = pixman_region32_not_empty(&damage); | ||
222 | if (!damaged) { | ||
223 | goto damage_finish; | ||
224 | } | ||
225 | |||
226 | int nrects; | ||
227 | pixman_box32_t *rects = pixman_region32_rectangles(&damage, &nrects); | ||
228 | for (int i = 0; i < nrects; ++i) { | ||
229 | scissor_output(wlr_output, &rects[i]); | ||
230 | wlr_render_texture_with_matrix(renderer, texture, matrix, alpha); | ||
231 | } | ||
232 | |||
233 | damage_finish: | ||
234 | pixman_region32_fini(&damage); | ||
235 | } | ||
236 | |||
237 | static void render_surface_iterator(struct wlr_surface *surface, int sx, int sy, | ||
238 | void *_data) { | ||
239 | struct render_data *data = _data; | ||
240 | struct wlr_output *wlr_output = data->output->wlr_output; | ||
241 | float rotation = data->root_geo.rotation; | ||
242 | pixman_region32_t *output_damage = data->damage; | ||
243 | float alpha = data->alpha; | ||
244 | |||
245 | struct wlr_texture *texture = wlr_surface_get_texture(surface); | ||
246 | if (!texture) { | ||
247 | return; | ||
248 | } | ||
249 | |||
250 | struct wlr_box box; | ||
251 | bool intersects = get_surface_box(&data->root_geo, data->output, surface, | ||
252 | sx, sy, &box); | ||
253 | if (!intersects) { | ||
254 | return; | ||
255 | } | ||
256 | |||
257 | scale_box(&box, wlr_output->scale); | ||
258 | |||
259 | float matrix[9]; | ||
260 | enum wl_output_transform transform = | ||
261 | wlr_output_transform_invert(surface->current->transform); | ||
262 | wlr_matrix_project_box(matrix, &box, transform, rotation, | ||
263 | wlr_output->transform_matrix); | ||
264 | |||
265 | render_texture(wlr_output, output_damage, texture, &box, matrix, alpha); | ||
266 | } | ||
267 | |||
268 | static void render_layer(struct sway_output *output, | ||
269 | pixman_region32_t *damage, struct wl_list *layer_surfaces) { | ||
270 | struct render_data data = { | ||
271 | .output = output, | ||
272 | .damage = damage, | ||
273 | .alpha = 1.0f, | ||
274 | }; | ||
275 | layer_for_each_surface(layer_surfaces, &data.root_geo, | ||
276 | render_surface_iterator, &data); | ||
277 | } | ||
278 | |||
279 | static void render_unmanaged(struct sway_output *output, | ||
280 | pixman_region32_t *damage, struct wl_list *unmanaged) { | ||
281 | struct render_data data = { | ||
282 | .output = output, | ||
283 | .damage = damage, | ||
284 | .alpha = 1.0f, | ||
285 | }; | ||
286 | unmanaged_for_each_surface(unmanaged, output, &data.root_geo, | ||
287 | render_surface_iterator, &data); | ||
288 | } | ||
289 | |||
290 | static void render_drag_icons(struct sway_output *output, | ||
291 | pixman_region32_t *damage, struct wl_list *drag_icons) { | ||
292 | struct render_data data = { | ||
293 | .output = output, | ||
294 | .damage = damage, | ||
295 | .alpha = 1.0f, | ||
296 | }; | ||
297 | drag_icons_for_each_surface(drag_icons, output, &data.root_geo, | ||
298 | render_surface_iterator, &data); | ||
299 | } | ||
300 | |||
301 | static void render_rect(struct wlr_output *wlr_output, | ||
302 | pixman_region32_t *output_damage, const struct wlr_box *_box, | ||
303 | float color[static 4]) { | ||
304 | struct wlr_renderer *renderer = | ||
305 | wlr_backend_get_renderer(wlr_output->backend); | ||
306 | |||
307 | struct wlr_box box; | ||
308 | memcpy(&box, _box, sizeof(struct wlr_box)); | ||
309 | box.x -= wlr_output->lx * wlr_output->scale; | ||
310 | box.y -= wlr_output->ly * wlr_output->scale; | ||
311 | |||
312 | pixman_region32_t damage; | ||
313 | pixman_region32_init(&damage); | ||
314 | pixman_region32_union_rect(&damage, &damage, box.x, box.y, | ||
315 | box.width, box.height); | ||
316 | pixman_region32_intersect(&damage, &damage, output_damage); | ||
317 | bool damaged = pixman_region32_not_empty(&damage); | ||
318 | if (!damaged) { | ||
319 | goto damage_finish; | ||
320 | } | ||
321 | |||
322 | int nrects; | ||
323 | pixman_box32_t *rects = pixman_region32_rectangles(&damage, &nrects); | ||
324 | for (int i = 0; i < nrects; ++i) { | ||
325 | scissor_output(wlr_output, &rects[i]); | ||
326 | wlr_render_rect(renderer, &box, color, | ||
327 | wlr_output->transform_matrix); | ||
328 | } | ||
329 | |||
330 | damage_finish: | ||
331 | pixman_region32_fini(&damage); | ||
332 | } | ||
333 | |||
334 | static void premultiply_alpha(float color[4], float opacity) { | ||
335 | color[3] *= opacity; | ||
336 | color[0] *= color[3]; | ||
337 | color[1] *= color[3]; | ||
338 | color[2] *= color[3]; | ||
339 | } | ||
340 | |||
341 | static void render_view_surfaces(struct sway_view *view, | ||
342 | struct sway_output *output, pixman_region32_t *damage, float alpha) { | ||
343 | struct render_data data = { | ||
344 | .output = output, | ||
345 | .damage = damage, | ||
346 | .view = view, | ||
347 | .alpha = alpha, | ||
348 | }; | ||
349 | output_view_for_each_surface( | ||
350 | view, &data.root_geo, render_surface_iterator, &data); | ||
351 | } | ||
352 | |||
353 | static void render_saved_view(struct sway_view *view, | ||
354 | struct sway_output *output, pixman_region32_t *damage, float alpha) { | ||
355 | struct wlr_output *wlr_output = output->wlr_output; | ||
356 | |||
357 | int width, height; | ||
358 | struct wlr_texture *texture = | ||
359 | transaction_get_saved_texture(view, &width, &height); | ||
360 | if (!texture) { | ||
361 | return; | ||
362 | } | ||
363 | struct wlr_box box = { | ||
364 | .x = view->swayc->current.view_x - output->swayc->current.swayc_x, | ||
365 | .y = view->swayc->current.view_y - output->swayc->current.swayc_y, | ||
366 | .width = width, | ||
367 | .height = height, | ||
368 | }; | ||
369 | |||
370 | struct wlr_box output_box = { | ||
371 | .width = output->swayc->current.swayc_width, | ||
372 | .height = output->swayc->current.swayc_height, | ||
373 | }; | ||
374 | |||
375 | struct wlr_box intersection; | ||
376 | bool intersects = wlr_box_intersection(&output_box, &box, &intersection); | ||
377 | if (!intersects) { | ||
378 | return; | ||
379 | } | ||
380 | |||
381 | scale_box(&box, wlr_output->scale); | ||
382 | |||
383 | float matrix[9]; | ||
384 | wlr_matrix_project_box(matrix, &box, WL_OUTPUT_TRANSFORM_NORMAL, 0, | ||
385 | wlr_output->transform_matrix); | ||
386 | |||
387 | render_texture(wlr_output, damage, texture, &box, matrix, alpha); | ||
388 | } | ||
389 | |||
390 | /** | ||
391 | * Render a view's surface and left/bottom/right borders. | ||
392 | */ | ||
393 | static void render_view(struct sway_output *output, pixman_region32_t *damage, | ||
394 | struct sway_container *con, struct border_colors *colors) { | ||
395 | struct sway_view *view = con->sway_view; | ||
396 | if (view->swayc->instructions->length) { | ||
397 | render_saved_view(view, output, damage, view->swayc->alpha); | ||
398 | } else { | ||
399 | render_view_surfaces(view, output, damage, view->swayc->alpha); | ||
400 | } | ||
401 | |||
402 | struct wlr_box box; | ||
403 | float output_scale = output->wlr_output->scale; | ||
404 | float color[4]; | ||
405 | struct sway_container_state *state = &con->current; | ||
406 | |||
407 | if (state->border != B_NONE) { | ||
408 | if (state->border_left) { | ||
409 | memcpy(&color, colors->child_border, sizeof(float) * 4); | ||
410 | premultiply_alpha(color, con->alpha); | ||
411 | box.x = state->swayc_x; | ||
412 | box.y = state->view_y; | ||
413 | box.width = state->border_thickness; | ||
414 | box.height = state->view_height; | ||
415 | scale_box(&box, output_scale); | ||
416 | render_rect(output->wlr_output, damage, &box, color); | ||
417 | } | ||
418 | |||
419 | if (state->border_right) { | ||
420 | if (state->parent->current.children->length == 1 | ||
421 | && state->parent->current.layout == L_HORIZ) { | ||
422 | memcpy(&color, colors->indicator, sizeof(float) * 4); | ||
423 | } else { | ||
424 | memcpy(&color, colors->child_border, sizeof(float) * 4); | ||
425 | } | ||
426 | premultiply_alpha(color, con->alpha); | ||
427 | box.x = state->view_x + state->view_width; | ||
428 | box.y = state->view_y; | ||
429 | box.width = state->border_thickness; | ||
430 | box.height = state->view_height; | ||
431 | scale_box(&box, output_scale); | ||
432 | render_rect(output->wlr_output, damage, &box, color); | ||
433 | } | ||
434 | |||
435 | if (state->border_bottom) { | ||
436 | if (state->parent->current.children->length == 1 | ||
437 | && con->current.parent->current.layout == L_VERT) { | ||
438 | memcpy(&color, colors->indicator, sizeof(float) * 4); | ||
439 | } else { | ||
440 | memcpy(&color, colors->child_border, sizeof(float) * 4); | ||
441 | } | ||
442 | premultiply_alpha(color, con->alpha); | ||
443 | box.x = state->swayc_x; | ||
444 | box.y = state->view_y + state->view_height; | ||
445 | box.width = state->swayc_width; | ||
446 | box.height = state->border_thickness; | ||
447 | scale_box(&box, output_scale); | ||
448 | render_rect(output->wlr_output, damage, &box, color); | ||
449 | } | ||
450 | } | ||
451 | } | ||
452 | |||
453 | /** | ||
454 | * Render a titlebar. | ||
455 | * | ||
456 | * Care must be taken not to render over the same pixel multiple times, | ||
457 | * otherwise the colors will be incorrect when using opacity. | ||
458 | * | ||
459 | * The height is: 1px border, 3px padding, font height, 3px padding, 1px border | ||
460 | * The left side for L_TABBED is: 1px border, 2px padding, title | ||
461 | * The left side for other layouts is: 3px padding, title | ||
462 | */ | ||
463 | static void render_titlebar(struct sway_output *output, | ||
464 | pixman_region32_t *output_damage, struct sway_container *con, | ||
465 | int x, int y, int width, | ||
466 | struct border_colors *colors, struct wlr_texture *title_texture, | ||
467 | struct wlr_texture *marks_texture) { | ||
468 | struct wlr_box box; | ||
469 | float color[4]; | ||
470 | struct sway_container_state *state = &con->current; | ||
471 | float output_scale = output->wlr_output->scale; | ||
472 | enum sway_container_layout layout = state->parent->current.layout; | ||
473 | list_t *children = state->parent->current.children; | ||
474 | bool is_last_child = children->items[children->length - 1] == con; | ||
475 | double output_x = output->swayc->current.swayc_x; | ||
476 | double output_y = output->swayc->current.swayc_y; | ||
477 | |||
478 | // Single pixel bar above title | ||
479 | memcpy(&color, colors->border, sizeof(float) * 4); | ||
480 | premultiply_alpha(color, con->alpha); | ||
481 | box.x = x; | ||
482 | box.y = y; | ||
483 | box.width = width; | ||
484 | box.height = TITLEBAR_BORDER_THICKNESS; | ||
485 | scale_box(&box, output_scale); | ||
486 | render_rect(output->wlr_output, output_damage, &box, color); | ||
487 | |||
488 | // Single pixel bar below title | ||
489 | size_t left_offset = 0, right_offset = 0; | ||
490 | bool connects_sides = false; | ||
491 | if (layout == L_HORIZ || layout == L_VERT || | ||
492 | (layout == L_STACKED && is_last_child)) { | ||
493 | if (con->type == C_VIEW) { | ||
494 | left_offset = state->border_left * state->border_thickness; | ||
495 | right_offset = state->border_right * state->border_thickness; | ||
496 | connects_sides = true; | ||
497 | } | ||
498 | } | ||
499 | box.x = x + left_offset; | ||
500 | box.y = y + container_titlebar_height() - TITLEBAR_BORDER_THICKNESS; | ||
501 | box.width = width - left_offset - right_offset; | ||
502 | box.height = TITLEBAR_BORDER_THICKNESS; | ||
503 | scale_box(&box, output_scale); | ||
504 | render_rect(output->wlr_output, output_damage, &box, color); | ||
505 | |||
506 | if (layout == L_TABBED) { | ||
507 | // Single pixel left edge | ||
508 | box.x = x; | ||
509 | box.y = y + TITLEBAR_BORDER_THICKNESS; | ||
510 | box.width = TITLEBAR_BORDER_THICKNESS; | ||
511 | box.height = | ||
512 | container_titlebar_height() - TITLEBAR_BORDER_THICKNESS * 2; | ||
513 | scale_box(&box, output_scale); | ||
514 | render_rect(output->wlr_output, output_damage, &box, color); | ||
515 | |||
516 | // Single pixel right edge | ||
517 | box.x = (x + width - TITLEBAR_BORDER_THICKNESS) * output_scale; | ||
518 | render_rect(output->wlr_output, output_damage, &box, color); | ||
519 | } | ||
520 | |||
521 | size_t inner_width = width - TITLEBAR_H_PADDING * 2; | ||
522 | |||
523 | // Marks | ||
524 | size_t marks_width = 0; | ||
525 | if (config->show_marks && marks_texture) { | ||
526 | struct wlr_box texture_box; | ||
527 | wlr_texture_get_size(marks_texture, | ||
528 | &texture_box.width, &texture_box.height); | ||
529 | texture_box.x = (x - output_x + width - TITLEBAR_H_PADDING) | ||
530 | * output_scale - texture_box.width; | ||
531 | texture_box.y = (y - output_y + TITLEBAR_V_PADDING) * output_scale; | ||
532 | |||
533 | float matrix[9]; | ||
534 | wlr_matrix_project_box(matrix, &texture_box, | ||
535 | WL_OUTPUT_TRANSFORM_NORMAL, | ||
536 | 0.0, output->wlr_output->transform_matrix); | ||
537 | |||
538 | if (inner_width * output_scale < texture_box.width) { | ||
539 | texture_box.width = inner_width * output_scale; | ||
540 | } | ||
541 | render_texture(output->wlr_output, output_damage, marks_texture, | ||
542 | &texture_box, matrix, con->alpha); | ||
543 | marks_width = texture_box.width; | ||
544 | } | ||
545 | |||
546 | // Title text | ||
547 | size_t title_width = 0; | ||
548 | if (title_texture) { | ||
549 | struct wlr_box texture_box; | ||
550 | wlr_texture_get_size(title_texture, | ||
551 | &texture_box.width, &texture_box.height); | ||
552 | texture_box.x = (x - output_x + TITLEBAR_H_PADDING) * output_scale; | ||
553 | texture_box.y = (y - output_y + TITLEBAR_V_PADDING) * output_scale; | ||
554 | |||
555 | float matrix[9]; | ||
556 | wlr_matrix_project_box(matrix, &texture_box, | ||
557 | WL_OUTPUT_TRANSFORM_NORMAL, | ||
558 | 0.0, output->wlr_output->transform_matrix); | ||
559 | |||
560 | if (inner_width * output_scale - marks_width < texture_box.width) { | ||
561 | texture_box.width = inner_width * output_scale - marks_width; | ||
562 | } | ||
563 | render_texture(output->wlr_output, output_damage, title_texture, | ||
564 | &texture_box, matrix, con->alpha); | ||
565 | title_width = texture_box.width; | ||
566 | } | ||
567 | |||
568 | // Padding above title | ||
569 | memcpy(&color, colors->background, sizeof(float) * 4); | ||
570 | premultiply_alpha(color, con->alpha); | ||
571 | box.x = x + (layout == L_TABBED) * TITLEBAR_BORDER_THICKNESS; | ||
572 | box.y = y + TITLEBAR_BORDER_THICKNESS; | ||
573 | box.width = width - (layout == L_TABBED) * TITLEBAR_BORDER_THICKNESS * 2; | ||
574 | box.height = TITLEBAR_V_PADDING - TITLEBAR_BORDER_THICKNESS; | ||
575 | scale_box(&box, output_scale); | ||
576 | render_rect(output->wlr_output, output_damage, &box, color); | ||
577 | |||
578 | // Padding below title | ||
579 | box.y = (y + TITLEBAR_V_PADDING + config->font_height) * output_scale; | ||
580 | render_rect(output->wlr_output, output_damage, &box, color); | ||
581 | |||
582 | // Filler between title and marks | ||
583 | box.width = inner_width * output_scale - title_width - marks_width; | ||
584 | if (box.width > 0) { | ||
585 | box.x = (x + TITLEBAR_H_PADDING) * output_scale + title_width; | ||
586 | box.y = (y + TITLEBAR_V_PADDING) * output_scale; | ||
587 | box.height = config->font_height * output_scale; | ||
588 | render_rect(output->wlr_output, output_damage, &box, color); | ||
589 | } | ||
590 | |||
591 | // Padding left of title | ||
592 | left_offset = (layout == L_TABBED) * TITLEBAR_BORDER_THICKNESS; | ||
593 | box.x = x + left_offset; | ||
594 | box.y = y + TITLEBAR_V_PADDING; | ||
595 | box.width = TITLEBAR_H_PADDING - left_offset; | ||
596 | box.height = config->font_height; | ||
597 | scale_box(&box, output_scale); | ||
598 | render_rect(output->wlr_output, output_damage, &box, color); | ||
599 | |||
600 | // Padding right of marks | ||
601 | right_offset = (layout == L_TABBED) * TITLEBAR_BORDER_THICKNESS; | ||
602 | box.x = x + width - TITLEBAR_H_PADDING; | ||
603 | box.y = y + TITLEBAR_V_PADDING; | ||
604 | box.width = TITLEBAR_H_PADDING - right_offset; | ||
605 | box.height = config->font_height; | ||
606 | scale_box(&box, output_scale); | ||
607 | render_rect(output->wlr_output, output_damage, &box, color); | ||
608 | |||
609 | if (connects_sides) { | ||
610 | // Left pixel in line with bottom bar | ||
611 | box.x = x; | ||
612 | box.y = y + container_titlebar_height() - TITLEBAR_BORDER_THICKNESS; | ||
613 | box.width = state->border_thickness * state->border_left; | ||
614 | box.height = TITLEBAR_BORDER_THICKNESS; | ||
615 | scale_box(&box, output_scale); | ||
616 | render_rect(output->wlr_output, output_damage, &box, color); | ||
617 | |||
618 | // Right pixel in line with bottom bar | ||
619 | box.x = x + width - state->border_thickness * state->border_right; | ||
620 | box.y = y + container_titlebar_height() - TITLEBAR_BORDER_THICKNESS; | ||
621 | box.width = state->border_thickness * state->border_right; | ||
622 | box.height = TITLEBAR_BORDER_THICKNESS; | ||
623 | scale_box(&box, output_scale); | ||
624 | render_rect(output->wlr_output, output_damage, &box, color); | ||
625 | } | ||
626 | } | ||
627 | |||
628 | /** | ||
629 | * Render the top border line for a view using "border pixel". | ||
630 | */ | ||
631 | static void render_top_border(struct sway_output *output, | ||
632 | pixman_region32_t *output_damage, struct sway_container *con, | ||
633 | struct border_colors *colors) { | ||
634 | struct sway_container_state *state = &con->current; | ||
635 | if (!state->border_top) { | ||
636 | return; | ||
637 | } | ||
638 | struct wlr_box box; | ||
639 | float color[4]; | ||
640 | float output_scale = output->wlr_output->scale; | ||
641 | |||
642 | // Child border - top edge | ||
643 | memcpy(&color, colors->child_border, sizeof(float) * 4); | ||
644 | premultiply_alpha(color, con->alpha); | ||
645 | box.x = state->swayc_x; | ||
646 | box.y = state->swayc_y; | ||
647 | box.width = state->swayc_width; | ||
648 | box.height = state->border_thickness; | ||
649 | scale_box(&box, output_scale); | ||
650 | render_rect(output->wlr_output, output_damage, &box, color); | ||
651 | } | ||
652 | |||
653 | static void render_container(struct sway_output *output, | ||
654 | pixman_region32_t *damage, struct sway_container *con, bool parent_focused); | ||
655 | |||
656 | /** | ||
657 | * Render a container's children using a L_HORIZ or L_VERT layout. | ||
658 | * | ||
659 | * Wrap child views in borders and leave child containers borderless because | ||
660 | * they'll apply their own borders to their children. | ||
661 | */ | ||
662 | static void render_container_simple(struct sway_output *output, | ||
663 | pixman_region32_t *damage, struct sway_container *con, | ||
664 | bool parent_focused) { | ||
665 | struct sway_seat *seat = input_manager_current_seat(input_manager); | ||
666 | struct sway_container *focus = seat_get_focus(seat); | ||
667 | |||
668 | for (int i = 0; i < con->current.children->length; ++i) { | ||
669 | struct sway_container *child = con->current.children->items[i]; | ||
670 | |||
671 | if (child->type == C_VIEW) { | ||
672 | struct sway_view *view = child->sway_view; | ||
673 | struct border_colors *colors; | ||
674 | struct wlr_texture *title_texture; | ||
675 | struct wlr_texture *marks_texture; | ||
676 | struct sway_container_state *state = &child->current; | ||
677 | |||
678 | if (focus == child || parent_focused) { | ||
679 | colors = &config->border_colors.focused; | ||
680 | title_texture = child->title_focused; | ||
681 | marks_texture = view->marks_focused; | ||
682 | } else if (seat_get_focus_inactive(seat, con) == child) { | ||
683 | colors = &config->border_colors.focused_inactive; | ||
684 | title_texture = child->title_focused_inactive; | ||
685 | marks_texture = view->marks_focused_inactive; | ||
686 | } else { | ||
687 | colors = &config->border_colors.unfocused; | ||
688 | title_texture = child->title_unfocused; | ||
689 | marks_texture = view->marks_unfocused; | ||
690 | } | ||
691 | |||
692 | if (state->border == B_NORMAL) { | ||
693 | render_titlebar(output, damage, child, state->swayc_x, | ||
694 | state->swayc_y, state->swayc_width, colors, | ||
695 | title_texture, marks_texture); | ||
696 | } else { | ||
697 | render_top_border(output, damage, child, colors); | ||
698 | } | ||
699 | render_view(output, damage, child, colors); | ||
700 | } else { | ||
701 | render_container(output, damage, child, | ||
702 | parent_focused || focus == child); | ||
703 | } | ||
704 | } | ||
705 | } | ||
706 | |||
707 | /** | ||
708 | * Render a container's children using the L_TABBED layout. | ||
709 | */ | ||
710 | static void render_container_tabbed(struct sway_output *output, | ||
711 | pixman_region32_t *damage, struct sway_container *con, | ||
712 | bool parent_focused) { | ||
713 | if (!con->current.children->length) { | ||
714 | return; | ||
715 | } | ||
716 | struct sway_seat *seat = input_manager_current_seat(input_manager); | ||
717 | struct sway_container *focus = seat_get_focus(seat); | ||
718 | struct sway_container *current = seat_get_active_current_child(seat, con); | ||
719 | struct border_colors *current_colors = &config->border_colors.unfocused; | ||
720 | struct sway_container_state *pstate = &con->current; | ||
721 | |||
722 | // Render tabs | ||
723 | for (int i = 0; i < con->current.children->length; ++i) { | ||
724 | struct sway_container *child = con->current.children->items[i]; | ||
725 | struct sway_view *view = child->type == C_VIEW ? child->sway_view : NULL; | ||
726 | struct sway_container_state *cstate = &child->current; | ||
727 | struct border_colors *colors; | ||
728 | struct wlr_texture *title_texture; | ||
729 | struct wlr_texture *marks_texture; | ||
730 | |||
731 | if (focus == child || parent_focused) { | ||
732 | colors = &config->border_colors.focused; | ||
733 | title_texture = child->title_focused; | ||
734 | marks_texture = view ? view->marks_focused : NULL; | ||
735 | } else if (child == current) { | ||
736 | colors = &config->border_colors.focused_inactive; | ||
737 | title_texture = child->title_focused_inactive; | ||
738 | marks_texture = view ? view->marks_focused_inactive : NULL; | ||
739 | } else { | ||
740 | colors = &config->border_colors.unfocused; | ||
741 | title_texture = child->title_unfocused; | ||
742 | marks_texture = view ? view->marks_unfocused : NULL; | ||
743 | } | ||
744 | |||
745 | int tab_width = pstate->swayc_width / pstate->children->length; | ||
746 | int x = pstate->swayc_x + tab_width * i; | ||
747 | // Make last tab use the remaining width of the parent | ||
748 | if (i == pstate->children->length - 1) { | ||
749 | tab_width = pstate->swayc_width - tab_width * i; | ||
750 | } | ||
751 | |||
752 | render_titlebar(output, damage, child, x, cstate->swayc_y, tab_width, | ||
753 | colors, title_texture, marks_texture); | ||
754 | |||
755 | if (child == current) { | ||
756 | current_colors = colors; | ||
757 | } | ||
758 | } | ||
759 | |||
760 | // Render surface and left/right/bottom borders | ||
761 | if (current) { | ||
762 | if (current->type == C_VIEW) { | ||
763 | render_view(output, damage, current, current_colors); | ||
764 | } else { | ||
765 | render_container(output, damage, current, | ||
766 | parent_focused || current == focus); | ||
767 | } | ||
768 | } | ||
769 | } | ||
770 | |||
771 | /** | ||
772 | * Render a container's children using the L_STACKED layout. | ||
773 | */ | ||
774 | static void render_container_stacked(struct sway_output *output, | ||
775 | pixman_region32_t *damage, struct sway_container *con, | ||
776 | bool parent_focused) { | ||
777 | if (!con->current.children->length) { | ||
778 | return; | ||
779 | } | ||
780 | struct sway_seat *seat = input_manager_current_seat(input_manager); | ||
781 | struct sway_container *focus = seat_get_focus(seat); | ||
782 | struct sway_container *current = seat_get_active_current_child(seat, con); | ||
783 | struct border_colors *current_colors = &config->border_colors.unfocused; | ||
784 | struct sway_container_state *pstate = &con->current; | ||
785 | |||
786 | // Render titles | ||
787 | for (int i = 0; i < con->current.children->length; ++i) { | ||
788 | struct sway_container *child = con->current.children->items[i]; | ||
789 | struct sway_view *view = child->type == C_VIEW ? child->sway_view : NULL; | ||
790 | struct sway_container_state *cstate = &child->current; | ||
791 | struct border_colors *colors; | ||
792 | struct wlr_texture *title_texture; | ||
793 | struct wlr_texture *marks_texture; | ||
794 | |||
795 | if (focus == child || parent_focused) { | ||
796 | colors = &config->border_colors.focused; | ||
797 | title_texture = child->title_focused; | ||
798 | marks_texture = view ? view->marks_focused : NULL; | ||
799 | } else if (child == current) { | ||
800 | colors = &config->border_colors.focused_inactive; | ||
801 | title_texture = child->title_focused_inactive; | ||
802 | marks_texture = view ? view->marks_focused_inactive : NULL; | ||
803 | } else { | ||
804 | colors = &config->border_colors.unfocused; | ||
805 | title_texture = child->title_unfocused; | ||
806 | marks_texture = view ? view->marks_unfocused : NULL; | ||
807 | } | ||
808 | |||
809 | int y = pstate->swayc_y + container_titlebar_height() * i; | ||
810 | render_titlebar(output, damage, child, cstate->swayc_x, y, | ||
811 | cstate->swayc_width, colors, title_texture, marks_texture); | ||
812 | |||
813 | if (child == current) { | ||
814 | current_colors = colors; | ||
815 | } | ||
816 | } | ||
817 | |||
818 | // Render surface and left/right/bottom borders | ||
819 | if (current) { | ||
820 | if (current->type == C_VIEW) { | ||
821 | render_view(output, damage, current, current_colors); | ||
822 | } else { | ||
823 | render_container(output, damage, current, | ||
824 | parent_focused || current == focus); | ||
825 | } | ||
826 | } | ||
827 | } | ||
828 | |||
829 | static void render_container(struct sway_output *output, | ||
830 | pixman_region32_t *damage, struct sway_container *con, | ||
831 | bool parent_focused) { | ||
832 | switch (con->current.layout) { | ||
833 | case L_NONE: | ||
834 | case L_HORIZ: | ||
835 | case L_VERT: | ||
836 | render_container_simple(output, damage, con, parent_focused); | ||
837 | break; | ||
838 | case L_STACKED: | ||
839 | render_container_stacked(output, damage, con, parent_focused); | ||
840 | break; | ||
841 | case L_TABBED: | ||
842 | render_container_tabbed(output, damage, con, parent_focused); | ||
843 | break; | ||
844 | case L_FLOATING: | ||
845 | sway_assert(false, "Didn't expect to see floating here"); | ||
846 | } | ||
847 | } | ||
848 | |||
849 | static void render_floating_container(struct sway_output *soutput, | ||
850 | pixman_region32_t *damage, struct sway_container *con) { | ||
851 | if (con->type == C_VIEW) { | ||
852 | struct sway_view *view = con->sway_view; | ||
853 | struct sway_seat *seat = input_manager_current_seat(input_manager); | ||
854 | struct sway_container *focus = seat_get_focus(seat); | ||
855 | struct border_colors *colors; | ||
856 | struct wlr_texture *title_texture; | ||
857 | struct wlr_texture *marks_texture; | ||
858 | |||
859 | if (focus == con) { | ||
860 | colors = &config->border_colors.focused; | ||
861 | title_texture = con->title_focused; | ||
862 | marks_texture = view->marks_focused; | ||
863 | } else { | ||
864 | colors = &config->border_colors.unfocused; | ||
865 | title_texture = con->title_unfocused; | ||
866 | marks_texture = view->marks_unfocused; | ||
867 | } | ||
868 | |||
869 | if (con->current.border == B_NORMAL) { | ||
870 | render_titlebar(soutput, damage, con, con->current.swayc_x, | ||
871 | con->current.swayc_y, con->current.swayc_width, colors, | ||
872 | title_texture, marks_texture); | ||
873 | } else if (con->current.border != B_NONE) { | ||
874 | render_top_border(soutput, damage, con, colors); | ||
875 | } | ||
876 | render_view(soutput, damage, con, colors); | ||
877 | } else { | ||
878 | render_container(soutput, damage, con, false); | ||
879 | } | ||
880 | } | ||
881 | |||
882 | static void render_floating(struct sway_output *soutput, | ||
883 | pixman_region32_t *damage) { | ||
884 | for (int i = 0; i < root_container.current.children->length; ++i) { | ||
885 | struct sway_container *output = | ||
886 | root_container.current.children->items[i]; | ||
887 | for (int j = 0; j < output->current.children->length; ++j) { | ||
888 | struct sway_container *ws = output->current.children->items[j]; | ||
889 | if (!workspace_is_visible(ws)) { | ||
890 | continue; | ||
891 | } | ||
892 | list_t *floating = | ||
893 | ws->current.ws_floating->current.children; | ||
894 | for (int k = 0; k < floating->length; ++k) { | ||
895 | struct sway_container *floater = floating->items[k]; | ||
896 | render_floating_container(soutput, damage, floater); | ||
897 | } | ||
898 | } | ||
899 | } | ||
900 | } | ||
901 | |||
902 | static struct sway_container *output_get_active_workspace( | ||
903 | struct sway_output *output) { | ||
904 | struct sway_seat *seat = input_manager_current_seat(input_manager); | 170 | struct sway_seat *seat = input_manager_current_seat(input_manager); |
905 | struct sway_container *focus = | 171 | struct sway_container *focus = |
906 | seat_get_focus_inactive(seat, output->swayc); | 172 | seat_get_focus_inactive(seat, output->swayc); |
@@ -915,117 +181,58 @@ static struct sway_container *output_get_active_workspace( | |||
915 | return workspace; | 181 | return workspace; |
916 | } | 182 | } |
917 | 183 | ||
918 | static void render_output(struct sway_output *output, struct timespec *when, | 184 | bool output_has_opaque_lockscreen(struct sway_output *output, |
919 | pixman_region32_t *damage) { | 185 | struct sway_seat *seat) { |
920 | struct wlr_output *wlr_output = output->wlr_output; | 186 | if (!seat->exclusive_client) { |
921 | 187 | return false; | |
922 | struct wlr_renderer *renderer = | ||
923 | wlr_backend_get_renderer(wlr_output->backend); | ||
924 | if (!sway_assert(renderer != NULL, | ||
925 | "expected the output backend to have a renderer")) { | ||
926 | return; | ||
927 | } | ||
928 | |||
929 | wlr_renderer_begin(renderer, wlr_output->width, wlr_output->height); | ||
930 | |||
931 | bool damage_whole_before_swap = false; | ||
932 | if (!pixman_region32_not_empty(damage)) { | ||
933 | // Output isn't damaged but needs buffer swap | ||
934 | goto renderer_end; | ||
935 | } | ||
936 | |||
937 | const char *damage_debug = getenv("SWAY_DAMAGE_DEBUG"); | ||
938 | if (damage_debug != NULL) { | ||
939 | if (strcmp(damage_debug, "highlight") == 0) { | ||
940 | wlr_renderer_clear(renderer, (float[]){1, 1, 0, 1}); | ||
941 | damage_whole_before_swap = true; | ||
942 | } else if (strcmp(damage_debug, "rerender") == 0) { | ||
943 | int width, height; | ||
944 | wlr_output_transformed_resolution(wlr_output, &width, &height); | ||
945 | pixman_region32_union_rect(damage, damage, 0, 0, width, height); | ||
946 | } | ||
947 | } | 188 | } |
948 | 189 | ||
949 | struct sway_container *workspace = output_get_active_workspace(output); | 190 | struct wlr_layer_surface *wlr_layer_surface; |
950 | struct sway_view *fullscreen_view = workspace->current.ws_fullscreen; | 191 | wl_list_for_each(wlr_layer_surface, &server.layer_shell->surfaces, link) { |
951 | 192 | if (wlr_layer_surface->output != output->wlr_output) { | |
952 | if (fullscreen_view) { | 193 | continue; |
953 | float clear_color[] = {0.0f, 0.0f, 0.0f, 1.0f}; | ||
954 | |||
955 | int nrects; | ||
956 | pixman_box32_t *rects = pixman_region32_rectangles(damage, &nrects); | ||
957 | for (int i = 0; i < nrects; ++i) { | ||
958 | scissor_output(wlr_output, &rects[i]); | ||
959 | wlr_renderer_clear(renderer, clear_color); | ||
960 | } | 194 | } |
961 | 195 | struct wlr_surface *wlr_surface = wlr_layer_surface->surface; | |
962 | // TODO: handle views smaller than the output | 196 | if (wlr_surface->resource->client != seat->exclusive_client) { |
963 | render_view_surfaces(fullscreen_view, output, damage, 1.0f); | 197 | continue; |
964 | |||
965 | if (fullscreen_view->type == SWAY_VIEW_XWAYLAND) { | ||
966 | render_unmanaged(output, damage, | ||
967 | &root_container.sway_root->xwayland_unmanaged); | ||
968 | } | 198 | } |
969 | } else { | 199 | struct sway_layer_surface *sway_layer_surface = |
970 | float clear_color[] = {0.25f, 0.25f, 0.25f, 1.0f}; | 200 | layer_from_wlr_layer_surface(wlr_layer_surface); |
971 | 201 | pixman_box32_t output_box = { | |
972 | int nrects; | 202 | .x2 = output->swayc->current.swayc_width, |
973 | pixman_box32_t *rects = pixman_region32_rectangles(damage, &nrects); | 203 | .y2 = output->swayc->current.swayc_height, |
974 | for (int i = 0; i < nrects; ++i) { | 204 | }; |
975 | scissor_output(wlr_output, &rects[i]); | 205 | pixman_region32_t surface_opaque_box; |
976 | wlr_renderer_clear(renderer, clear_color); | 206 | pixman_region32_init(&surface_opaque_box); |
207 | pixman_region32_copy(&surface_opaque_box, &wlr_surface->opaque_region); | ||
208 | pixman_region32_translate(&surface_opaque_box, | ||
209 | sway_layer_surface->geo.x, sway_layer_surface->geo.y); | ||
210 | bool contains = pixman_region32_contains_rectangle(&surface_opaque_box, | ||
211 | &output_box); | ||
212 | pixman_region32_fini(&surface_opaque_box); | ||
213 | if (contains) { | ||
214 | return true; | ||
977 | } | 215 | } |
978 | |||
979 | render_layer(output, damage, | ||
980 | &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND]); | ||
981 | render_layer(output, damage, | ||
982 | &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM]); | ||
983 | |||
984 | struct sway_seat *seat = input_manager_current_seat(input_manager); | ||
985 | struct sway_container *focus = seat_get_focus(seat); | ||
986 | render_container(output, damage, workspace, focus == workspace); | ||
987 | render_floating(output, damage); | ||
988 | |||
989 | render_unmanaged(output, damage, | ||
990 | &root_container.sway_root->xwayland_unmanaged); | ||
991 | render_layer(output, damage, | ||
992 | &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]); | ||
993 | } | ||
994 | render_layer(output, damage, | ||
995 | &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY]); | ||
996 | render_drag_icons(output, damage, &root_container.sway_root->drag_icons); | ||
997 | |||
998 | renderer_end: | ||
999 | if (root_container.sway_root->debug_tree) { | ||
1000 | wlr_render_texture(renderer, root_container.sway_root->debug_tree, | ||
1001 | wlr_output->transform_matrix, 0, 0, 1); | ||
1002 | } | ||
1003 | |||
1004 | if (damage_whole_before_swap || root_container.sway_root->debug_tree) { | ||
1005 | int width, height; | ||
1006 | wlr_output_transformed_resolution(wlr_output, &width, &height); | ||
1007 | pixman_region32_union_rect(damage, damage, 0, 0, width, height); | ||
1008 | } | 216 | } |
1009 | 217 | return false; | |
1010 | wlr_renderer_scissor(renderer, NULL); | ||
1011 | wlr_renderer_end(renderer); | ||
1012 | if (!wlr_output_damage_swap_buffers(output->damage, when, damage)) { | ||
1013 | return; | ||
1014 | } | ||
1015 | output->last_frame = *when; | ||
1016 | } | 218 | } |
1017 | 219 | ||
1018 | struct send_frame_done_data { | 220 | struct send_frame_done_data { |
1019 | struct root_geometry root_geo; | 221 | struct root_geometry root_geo; |
1020 | struct sway_output *output; | 222 | struct sway_output *output; |
1021 | struct timespec *when; | 223 | struct timespec *when; |
224 | struct wl_client *exclusive_client; | ||
1022 | }; | 225 | }; |
1023 | 226 | ||
1024 | static void send_frame_done_iterator(struct wlr_surface *surface, | 227 | static void send_frame_done_iterator(struct wlr_surface *surface, |
1025 | int sx, int sy, void *_data) { | 228 | int sx, int sy, void *_data) { |
1026 | struct send_frame_done_data *data = _data; | 229 | struct send_frame_done_data *data = _data; |
230 | if (data->exclusive_client && | ||
231 | data->exclusive_client != surface->resource->client) { | ||
232 | return; | ||
233 | } | ||
1027 | 234 | ||
1028 | bool intersects = get_surface_box(&data->root_geo, data->output, surface, | 235 | bool intersects = output_get_surface_box(&data->root_geo, data->output, surface, |
1029 | sx, sy, NULL); | 236 | sx, sy, NULL); |
1030 | if (intersects) { | 237 | if (intersects) { |
1031 | wlr_surface_send_frame_done(surface, data->when); | 238 | wlr_surface_send_frame_done(surface, data->when); |
@@ -1034,19 +241,19 @@ static void send_frame_done_iterator(struct wlr_surface *surface, | |||
1034 | 241 | ||
1035 | static void send_frame_done_layer(struct send_frame_done_data *data, | 242 | static void send_frame_done_layer(struct send_frame_done_data *data, |
1036 | struct wl_list *layer_surfaces) { | 243 | struct wl_list *layer_surfaces) { |
1037 | layer_for_each_surface(layer_surfaces, &data->root_geo, | 244 | output_layer_for_each_surface(layer_surfaces, &data->root_geo, |
1038 | send_frame_done_iterator, data); | 245 | send_frame_done_iterator, data); |
1039 | } | 246 | } |
1040 | 247 | ||
1041 | static void send_frame_done_unmanaged(struct send_frame_done_data *data, | 248 | static void send_frame_done_unmanaged(struct send_frame_done_data *data, |
1042 | struct wl_list *unmanaged) { | 249 | struct wl_list *unmanaged) { |
1043 | unmanaged_for_each_surface(unmanaged, data->output, &data->root_geo, | 250 | output_unmanaged_for_each_surface(unmanaged, data->output, &data->root_geo, |
1044 | send_frame_done_iterator, data); | 251 | send_frame_done_iterator, data); |
1045 | } | 252 | } |
1046 | 253 | ||
1047 | static void send_frame_done_drag_icons(struct send_frame_done_data *data, | 254 | static void send_frame_done_drag_icons(struct send_frame_done_data *data, |
1048 | struct wl_list *drag_icons) { | 255 | struct wl_list *drag_icons) { |
1049 | drag_icons_for_each_surface(drag_icons, data->output, &data->root_geo, | 256 | output_drag_icons_for_each_surface(drag_icons, data->output, &data->root_geo, |
1050 | send_frame_done_iterator, data); | 257 | send_frame_done_iterator, data); |
1051 | } | 258 | } |
1052 | 259 | ||
@@ -1061,7 +268,7 @@ static void send_frame_done_container_iterator(struct sway_container *con, | |||
1061 | return; | 268 | return; |
1062 | } | 269 | } |
1063 | 270 | ||
1064 | output_view_for_each_surface(con->sway_view, &data->root_geo, | 271 | output_view_for_each_surface(con->sway_view, data->output, &data->root_geo, |
1065 | send_frame_done_iterator, data); | 272 | send_frame_done_iterator, data); |
1066 | } | 273 | } |
1067 | 274 | ||
@@ -1072,9 +279,12 @@ static void send_frame_done_container(struct send_frame_done_data *data, | |||
1072 | } | 279 | } |
1073 | 280 | ||
1074 | static void send_frame_done(struct sway_output *output, struct timespec *when) { | 281 | static void send_frame_done(struct sway_output *output, struct timespec *when) { |
282 | struct sway_seat *seat = input_manager_current_seat(input_manager); | ||
1075 | struct send_frame_done_data data = { | 283 | struct send_frame_done_data data = { |
1076 | .output = output, | 284 | .output = output, |
1077 | .when = when, | 285 | .when = when, |
286 | .exclusive_client = output_has_opaque_lockscreen(output, seat) ? | ||
287 | seat->exclusive_client : NULL, | ||
1078 | }; | 288 | }; |
1079 | 289 | ||
1080 | struct sway_container *workspace = output_get_active_workspace(output); | 290 | struct sway_container *workspace = output_get_active_workspace(output); |
@@ -1125,7 +335,7 @@ static void damage_handle_frame(struct wl_listener *listener, void *data) { | |||
1125 | } | 335 | } |
1126 | 336 | ||
1127 | if (needs_swap) { | 337 | if (needs_swap) { |
1128 | render_output(output, &now, &damage); | 338 | output_render(output, &now, &damage); |
1129 | } | 339 | } |
1130 | 340 | ||
1131 | pixman_region32_fini(&damage); | 341 | pixman_region32_fini(&damage); |
@@ -1152,7 +362,7 @@ static void damage_surface_iterator(struct wlr_surface *surface, int sx, int sy, | |||
1152 | bool whole = data->whole; | 362 | bool whole = data->whole; |
1153 | 363 | ||
1154 | struct wlr_box box; | 364 | struct wlr_box box; |
1155 | bool intersects = get_surface_box(&data->root_geo, data->output, surface, | 365 | bool intersects = output_get_surface_box(&data->root_geo, data->output, surface, |
1156 | sx, sy, &box); | 366 | sx, sy, &box); |
1157 | if (!intersects) { | 367 | if (!intersects) { |
1158 | return; | 368 | return; |
@@ -1163,16 +373,22 @@ static void damage_surface_iterator(struct wlr_surface *surface, int sx, int sy, | |||
1163 | int center_x = box.x + box.width/2; | 373 | int center_x = box.x + box.width/2; |
1164 | int center_y = box.y + box.height/2; | 374 | int center_y = box.y + box.height/2; |
1165 | 375 | ||
1166 | if (pixman_region32_not_empty(&surface->current->surface_damage)) { | 376 | if (pixman_region32_not_empty(&surface->buffer_damage)) { |
377 | enum wl_output_transform transform = | ||
378 | wlr_output_transform_invert(surface->current.transform); | ||
379 | |||
1167 | pixman_region32_t damage; | 380 | pixman_region32_t damage; |
1168 | pixman_region32_init(&damage); | 381 | pixman_region32_init(&damage); |
1169 | pixman_region32_copy(&damage, &surface->current->surface_damage); | 382 | pixman_region32_copy(&damage, &surface->buffer_damage); |
1170 | wlr_region_scale(&damage, &damage, output->wlr_output->scale); | 383 | wlr_region_transform(&damage, &damage, transform, |
1171 | if (ceil(output->wlr_output->scale) > surface->current->scale) { | 384 | surface->current.buffer_width, surface->current.buffer_height); |
385 | wlr_region_scale(&damage, &damage, | ||
386 | output->wlr_output->scale / (float)surface->current.scale); | ||
387 | if (ceil(output->wlr_output->scale) > surface->current.scale) { | ||
1172 | // When scaling up a surface, it'll become blurry so we need to | 388 | // When scaling up a surface, it'll become blurry so we need to |
1173 | // expand the damage region | 389 | // expand the damage region |
1174 | wlr_region_expand(&damage, &damage, | 390 | wlr_region_expand(&damage, &damage, |
1175 | ceil(output->wlr_output->scale) - surface->current->scale); | 391 | ceil(output->wlr_output->scale) - surface->current.scale); |
1176 | } | 392 | } |
1177 | pixman_region32_translate(&damage, box.x, box.y); | 393 | pixman_region32_translate(&damage, box.x, box.y); |
1178 | wlr_region_rotated_bounds(&damage, &damage, rotation, | 394 | wlr_region_rotated_bounds(&damage, &damage, rotation, |
@@ -1196,7 +412,7 @@ void output_damage_surface(struct sway_output *output, double ox, double oy, | |||
1196 | .whole = whole, | 412 | .whole = whole, |
1197 | }; | 413 | }; |
1198 | 414 | ||
1199 | surface_for_each_surface(surface, ox, oy, &data.root_geo, | 415 | output_surface_for_each_surface(surface, ox, oy, &data.root_geo, |
1200 | damage_surface_iterator, &data); | 416 | damage_surface_iterator, &data); |
1201 | } | 417 | } |
1202 | 418 | ||
@@ -1215,7 +431,7 @@ static void output_damage_view(struct sway_output *output, | |||
1215 | .whole = whole, | 431 | .whole = whole, |
1216 | }; | 432 | }; |
1217 | 433 | ||
1218 | output_view_for_each_surface(view, &data.root_geo, | 434 | output_view_for_each_surface(view, output, &data.root_geo, |
1219 | damage_surface_iterator, &data); | 435 | damage_surface_iterator, &data); |
1220 | } | 436 | } |
1221 | 437 | ||
@@ -1247,11 +463,12 @@ static void output_damage_whole_container_iterator(struct sway_container *con, | |||
1247 | 463 | ||
1248 | void output_damage_whole_container(struct sway_output *output, | 464 | void output_damage_whole_container(struct sway_output *output, |
1249 | struct sway_container *con) { | 465 | struct sway_container *con) { |
466 | // Pad the box by 1px, because the width is a double and might be a fraction | ||
1250 | struct wlr_box box = { | 467 | struct wlr_box box = { |
1251 | .x = con->current.swayc_x - output->wlr_output->lx, | 468 | .x = con->current.swayc_x - output->wlr_output->lx - 1, |
1252 | .y = con->current.swayc_y - output->wlr_output->ly, | 469 | .y = con->current.swayc_y - output->wlr_output->ly - 1, |
1253 | .width = con->current.swayc_width, | 470 | .width = con->current.swayc_width + 2, |
1254 | .height = con->current.swayc_height, | 471 | .height = con->current.swayc_height + 2, |
1255 | }; | 472 | }; |
1256 | scale_box(&box, output->wlr_output->scale); | 473 | scale_box(&box, output->wlr_output->scale); |
1257 | wlr_output_damage_add_box(output->damage, &box); | 474 | wlr_output_damage_add_box(output->damage, &box); |
@@ -1276,19 +493,21 @@ static void handle_destroy(struct wl_listener *listener, void *data) { | |||
1276 | output->wlr_output->data = NULL; | 493 | output->wlr_output->data = NULL; |
1277 | free(output); | 494 | free(output); |
1278 | 495 | ||
1279 | arrange_and_commit(&root_container); | 496 | arrange_windows(&root_container); |
1280 | } | 497 | } |
1281 | 498 | ||
1282 | static void handle_mode(struct wl_listener *listener, void *data) { | 499 | static void handle_mode(struct wl_listener *listener, void *data) { |
1283 | struct sway_output *output = wl_container_of(listener, output, mode); | 500 | struct sway_output *output = wl_container_of(listener, output, mode); |
1284 | arrange_layers(output); | 501 | arrange_layers(output); |
1285 | arrange_and_commit(output->swayc); | 502 | arrange_windows(output->swayc); |
503 | transaction_commit_dirty(); | ||
1286 | } | 504 | } |
1287 | 505 | ||
1288 | static void handle_transform(struct wl_listener *listener, void *data) { | 506 | static void handle_transform(struct wl_listener *listener, void *data) { |
1289 | struct sway_output *output = wl_container_of(listener, output, transform); | 507 | struct sway_output *output = wl_container_of(listener, output, transform); |
1290 | arrange_layers(output); | 508 | arrange_layers(output); |
1291 | arrange_and_commit(output->swayc); | 509 | arrange_windows(output->swayc); |
510 | transaction_commit_dirty(); | ||
1292 | } | 511 | } |
1293 | 512 | ||
1294 | static void handle_scale_iterator(struct sway_container *view, void *data) { | 513 | static void handle_scale_iterator(struct sway_container *view, void *data) { |
@@ -1299,7 +518,8 @@ static void handle_scale(struct wl_listener *listener, void *data) { | |||
1299 | struct sway_output *output = wl_container_of(listener, output, scale); | 518 | struct sway_output *output = wl_container_of(listener, output, scale); |
1300 | arrange_layers(output); | 519 | arrange_layers(output); |
1301 | container_descendants(output->swayc, C_VIEW, handle_scale_iterator, NULL); | 520 | container_descendants(output->swayc, C_VIEW, handle_scale_iterator, NULL); |
1302 | arrange_and_commit(output->swayc); | 521 | arrange_windows(output->swayc); |
522 | transaction_commit_dirty(); | ||
1303 | } | 523 | } |
1304 | 524 | ||
1305 | struct sway_output *output_from_wlr_output(struct wlr_output *wlr_output) { | 525 | struct sway_output *output_from_wlr_output(struct wlr_output *wlr_output) { |
@@ -1309,7 +529,7 @@ struct sway_output *output_from_wlr_output(struct wlr_output *wlr_output) { | |||
1309 | void handle_new_output(struct wl_listener *listener, void *data) { | 529 | void handle_new_output(struct wl_listener *listener, void *data) { |
1310 | struct sway_server *server = wl_container_of(listener, server, new_output); | 530 | struct sway_server *server = wl_container_of(listener, server, new_output); |
1311 | struct wlr_output *wlr_output = data; | 531 | struct wlr_output *wlr_output = data; |
1312 | wlr_log(L_DEBUG, "New output %p: %s", wlr_output, wlr_output->name); | 532 | wlr_log(WLR_DEBUG, "New output %p: %s", wlr_output, wlr_output->name); |
1313 | 533 | ||
1314 | struct sway_output *output = calloc(1, sizeof(struct sway_output)); | 534 | struct sway_output *output = calloc(1, sizeof(struct sway_output)); |
1315 | if (!output) { | 535 | if (!output) { |
@@ -1368,5 +588,6 @@ void output_enable(struct sway_output *output) { | |||
1368 | output->damage_destroy.notify = damage_handle_destroy; | 588 | output->damage_destroy.notify = damage_handle_destroy; |
1369 | 589 | ||
1370 | arrange_layers(output); | 590 | arrange_layers(output); |
1371 | arrange_and_commit(&root_container); | 591 | arrange_windows(&root_container); |
592 | transaction_commit_dirty(); | ||
1372 | } | 593 | } |
diff --git a/sway/desktop/render.c b/sway/desktop/render.c new file mode 100644 index 00000000..7da54594 --- /dev/null +++ b/sway/desktop/render.c | |||
@@ -0,0 +1,919 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <assert.h> | ||
3 | #include <stdlib.h> | ||
4 | #include <strings.h> | ||
5 | #include <time.h> | ||
6 | #include <wayland-server.h> | ||
7 | #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_matrix.h> | ||
11 | #include <wlr/types/wlr_output_damage.h> | ||
12 | #include <wlr/types/wlr_output_layout.h> | ||
13 | #include <wlr/types/wlr_output.h> | ||
14 | #include <wlr/types/wlr_surface.h> | ||
15 | #include <wlr/util/region.h> | ||
16 | #include "log.h" | ||
17 | #include "sway/config.h" | ||
18 | #include "sway/debug.h" | ||
19 | #include "sway/input/input-manager.h" | ||
20 | #include "sway/input/seat.h" | ||
21 | #include "sway/layers.h" | ||
22 | #include "sway/output.h" | ||
23 | #include "sway/server.h" | ||
24 | #include "sway/tree/arrange.h" | ||
25 | #include "sway/tree/container.h" | ||
26 | #include "sway/tree/layout.h" | ||
27 | #include "sway/tree/view.h" | ||
28 | #include "sway/tree/workspace.h" | ||
29 | |||
30 | struct render_data { | ||
31 | struct root_geometry root_geo; | ||
32 | struct sway_output *output; | ||
33 | pixman_region32_t *damage; | ||
34 | struct sway_view *view; | ||
35 | float alpha; | ||
36 | }; | ||
37 | |||
38 | static void scale_box(struct wlr_box *box, float scale) { | ||
39 | box->x *= scale; | ||
40 | box->y *= scale; | ||
41 | box->width *= scale; | ||
42 | box->height *= scale; | ||
43 | } | ||
44 | |||
45 | static void scissor_output(struct wlr_output *wlr_output, | ||
46 | pixman_box32_t *rect) { | ||
47 | struct wlr_renderer *renderer = wlr_backend_get_renderer(wlr_output->backend); | ||
48 | assert(renderer); | ||
49 | |||
50 | struct wlr_box box = { | ||
51 | .x = rect->x1, | ||
52 | .y = rect->y1, | ||
53 | .width = rect->x2 - rect->x1, | ||
54 | .height = rect->y2 - rect->y1, | ||
55 | }; | ||
56 | |||
57 | int ow, oh; | ||
58 | wlr_output_transformed_resolution(wlr_output, &ow, &oh); | ||
59 | |||
60 | enum wl_output_transform transform = | ||
61 | wlr_output_transform_invert(wlr_output->transform); | ||
62 | wlr_box_transform(&box, transform, ow, oh, &box); | ||
63 | |||
64 | wlr_renderer_scissor(renderer, &box); | ||
65 | } | ||
66 | |||
67 | static void render_texture(struct wlr_output *wlr_output, | ||
68 | pixman_region32_t *output_damage, struct wlr_texture *texture, | ||
69 | const struct wlr_box *box, const float matrix[static 9], float alpha) { | ||
70 | struct wlr_renderer *renderer = | ||
71 | wlr_backend_get_renderer(wlr_output->backend); | ||
72 | |||
73 | pixman_region32_t damage; | ||
74 | pixman_region32_init(&damage); | ||
75 | pixman_region32_union_rect(&damage, &damage, box->x, box->y, | ||
76 | box->width, box->height); | ||
77 | pixman_region32_intersect(&damage, &damage, output_damage); | ||
78 | bool damaged = pixman_region32_not_empty(&damage); | ||
79 | if (!damaged) { | ||
80 | goto damage_finish; | ||
81 | } | ||
82 | |||
83 | int nrects; | ||
84 | pixman_box32_t *rects = pixman_region32_rectangles(&damage, &nrects); | ||
85 | for (int i = 0; i < nrects; ++i) { | ||
86 | scissor_output(wlr_output, &rects[i]); | ||
87 | wlr_render_texture_with_matrix(renderer, texture, matrix, alpha); | ||
88 | } | ||
89 | |||
90 | damage_finish: | ||
91 | pixman_region32_fini(&damage); | ||
92 | } | ||
93 | |||
94 | static void render_surface_iterator(struct wlr_surface *surface, int sx, int sy, | ||
95 | void *_data) { | ||
96 | struct render_data *data = _data; | ||
97 | struct wlr_output *wlr_output = data->output->wlr_output; | ||
98 | float rotation = data->root_geo.rotation; | ||
99 | pixman_region32_t *output_damage = data->damage; | ||
100 | float alpha = data->alpha; | ||
101 | |||
102 | struct wlr_texture *texture = wlr_surface_get_texture(surface); | ||
103 | if (!texture) { | ||
104 | return; | ||
105 | } | ||
106 | |||
107 | struct wlr_box box; | ||
108 | bool intersects = output_get_surface_box(&data->root_geo, data->output, | ||
109 | surface, sx, sy, &box); | ||
110 | if (!intersects) { | ||
111 | return; | ||
112 | } | ||
113 | |||
114 | scale_box(&box, wlr_output->scale); | ||
115 | |||
116 | float matrix[9]; | ||
117 | enum wl_output_transform transform = | ||
118 | wlr_output_transform_invert(surface->current.transform); | ||
119 | wlr_matrix_project_box(matrix, &box, transform, rotation, | ||
120 | wlr_output->transform_matrix); | ||
121 | |||
122 | render_texture(wlr_output, output_damage, texture, &box, matrix, alpha); | ||
123 | } | ||
124 | |||
125 | static void render_layer(struct sway_output *output, | ||
126 | pixman_region32_t *damage, struct wl_list *layer_surfaces) { | ||
127 | struct render_data data = { | ||
128 | .output = output, | ||
129 | .damage = damage, | ||
130 | .alpha = 1.0f, | ||
131 | }; | ||
132 | output_layer_for_each_surface(layer_surfaces, &data.root_geo, | ||
133 | render_surface_iterator, &data); | ||
134 | } | ||
135 | |||
136 | static void render_unmanaged(struct sway_output *output, | ||
137 | pixman_region32_t *damage, struct wl_list *unmanaged) { | ||
138 | struct render_data data = { | ||
139 | .output = output, | ||
140 | .damage = damage, | ||
141 | .alpha = 1.0f, | ||
142 | }; | ||
143 | output_unmanaged_for_each_surface(unmanaged, output, &data.root_geo, | ||
144 | render_surface_iterator, &data); | ||
145 | } | ||
146 | |||
147 | static void render_drag_icons(struct sway_output *output, | ||
148 | pixman_region32_t *damage, struct wl_list *drag_icons) { | ||
149 | struct render_data data = { | ||
150 | .output = output, | ||
151 | .damage = damage, | ||
152 | .alpha = 1.0f, | ||
153 | }; | ||
154 | output_drag_icons_for_each_surface(drag_icons, output, &data.root_geo, | ||
155 | render_surface_iterator, &data); | ||
156 | } | ||
157 | |||
158 | static void render_rect(struct wlr_output *wlr_output, | ||
159 | pixman_region32_t *output_damage, const struct wlr_box *_box, | ||
160 | float color[static 4]) { | ||
161 | struct wlr_renderer *renderer = | ||
162 | wlr_backend_get_renderer(wlr_output->backend); | ||
163 | |||
164 | struct wlr_box box; | ||
165 | memcpy(&box, _box, sizeof(struct wlr_box)); | ||
166 | box.x -= wlr_output->lx * wlr_output->scale; | ||
167 | box.y -= wlr_output->ly * wlr_output->scale; | ||
168 | |||
169 | pixman_region32_t damage; | ||
170 | pixman_region32_init(&damage); | ||
171 | pixman_region32_union_rect(&damage, &damage, box.x, box.y, | ||
172 | box.width, box.height); | ||
173 | pixman_region32_intersect(&damage, &damage, output_damage); | ||
174 | bool damaged = pixman_region32_not_empty(&damage); | ||
175 | if (!damaged) { | ||
176 | goto damage_finish; | ||
177 | } | ||
178 | |||
179 | int nrects; | ||
180 | pixman_box32_t *rects = pixman_region32_rectangles(&damage, &nrects); | ||
181 | for (int i = 0; i < nrects; ++i) { | ||
182 | scissor_output(wlr_output, &rects[i]); | ||
183 | wlr_render_rect(renderer, &box, color, | ||
184 | wlr_output->transform_matrix); | ||
185 | } | ||
186 | |||
187 | damage_finish: | ||
188 | pixman_region32_fini(&damage); | ||
189 | } | ||
190 | |||
191 | static void premultiply_alpha(float color[4], float opacity) { | ||
192 | color[3] *= opacity; | ||
193 | color[0] *= color[3]; | ||
194 | color[1] *= color[3]; | ||
195 | color[2] *= color[3]; | ||
196 | } | ||
197 | |||
198 | static void render_view_surfaces(struct sway_view *view, | ||
199 | struct sway_output *output, pixman_region32_t *damage, float alpha) { | ||
200 | struct render_data data = { | ||
201 | .output = output, | ||
202 | .damage = damage, | ||
203 | .view = view, | ||
204 | .alpha = alpha, | ||
205 | }; | ||
206 | output_view_for_each_surface(view, output, &data.root_geo, | ||
207 | render_surface_iterator, &data); | ||
208 | } | ||
209 | |||
210 | static void render_saved_view(struct sway_view *view, | ||
211 | struct sway_output *output, pixman_region32_t *damage, float alpha) { | ||
212 | struct wlr_output *wlr_output = output->wlr_output; | ||
213 | |||
214 | int width, height; | ||
215 | struct wlr_texture *texture = | ||
216 | transaction_get_saved_texture(view, &width, &height); | ||
217 | if (!texture) { | ||
218 | return; | ||
219 | } | ||
220 | struct wlr_box box = { | ||
221 | .x = view->swayc->current.view_x - output->swayc->current.swayc_x, | ||
222 | .y = view->swayc->current.view_y - output->swayc->current.swayc_y, | ||
223 | .width = width, | ||
224 | .height = height, | ||
225 | }; | ||
226 | |||
227 | struct wlr_box output_box = { | ||
228 | .width = output->swayc->current.swayc_width, | ||
229 | .height = output->swayc->current.swayc_height, | ||
230 | }; | ||
231 | |||
232 | struct wlr_box intersection; | ||
233 | bool intersects = wlr_box_intersection(&output_box, &box, &intersection); | ||
234 | if (!intersects) { | ||
235 | return; | ||
236 | } | ||
237 | |||
238 | scale_box(&box, wlr_output->scale); | ||
239 | |||
240 | float matrix[9]; | ||
241 | wlr_matrix_project_box(matrix, &box, WL_OUTPUT_TRANSFORM_NORMAL, 0, | ||
242 | wlr_output->transform_matrix); | ||
243 | |||
244 | render_texture(wlr_output, damage, texture, &box, matrix, alpha); | ||
245 | } | ||
246 | |||
247 | /** | ||
248 | * Render a view's surface and left/bottom/right borders. | ||
249 | */ | ||
250 | static void render_view(struct sway_output *output, pixman_region32_t *damage, | ||
251 | struct sway_container *con, struct border_colors *colors) { | ||
252 | struct sway_view *view = con->sway_view; | ||
253 | if (view->swayc->instructions->length) { | ||
254 | render_saved_view(view, output, damage, view->swayc->alpha); | ||
255 | } else { | ||
256 | render_view_surfaces(view, output, damage, view->swayc->alpha); | ||
257 | } | ||
258 | |||
259 | if (view->using_csd) { | ||
260 | return; | ||
261 | } | ||
262 | |||
263 | struct wlr_box box; | ||
264 | float output_scale = output->wlr_output->scale; | ||
265 | float color[4]; | ||
266 | struct sway_container_state *state = &con->current; | ||
267 | |||
268 | if (state->border != B_NONE) { | ||
269 | if (state->border_left) { | ||
270 | memcpy(&color, colors->child_border, sizeof(float) * 4); | ||
271 | premultiply_alpha(color, con->alpha); | ||
272 | box.x = state->swayc_x; | ||
273 | box.y = state->view_y; | ||
274 | box.width = state->border_thickness; | ||
275 | box.height = state->view_height; | ||
276 | scale_box(&box, output_scale); | ||
277 | render_rect(output->wlr_output, damage, &box, color); | ||
278 | } | ||
279 | |||
280 | if (state->border_right) { | ||
281 | if (state->parent->current.children->length == 1 | ||
282 | && state->parent->current.layout == L_HORIZ) { | ||
283 | memcpy(&color, colors->indicator, sizeof(float) * 4); | ||
284 | } else { | ||
285 | memcpy(&color, colors->child_border, sizeof(float) * 4); | ||
286 | } | ||
287 | premultiply_alpha(color, con->alpha); | ||
288 | box.x = state->view_x + state->view_width; | ||
289 | box.y = state->view_y; | ||
290 | box.width = state->border_thickness; | ||
291 | box.height = state->view_height; | ||
292 | scale_box(&box, output_scale); | ||
293 | render_rect(output->wlr_output, damage, &box, color); | ||
294 | } | ||
295 | |||
296 | if (state->border_bottom) { | ||
297 | if (state->parent->current.children->length == 1 | ||
298 | && con->current.parent->current.layout == L_VERT) { | ||
299 | memcpy(&color, colors->indicator, sizeof(float) * 4); | ||
300 | } else { | ||
301 | memcpy(&color, colors->child_border, sizeof(float) * 4); | ||
302 | } | ||
303 | premultiply_alpha(color, con->alpha); | ||
304 | box.x = state->swayc_x; | ||
305 | box.y = state->view_y + state->view_height; | ||
306 | box.width = state->swayc_width; | ||
307 | box.height = state->border_thickness; | ||
308 | scale_box(&box, output_scale); | ||
309 | render_rect(output->wlr_output, damage, &box, color); | ||
310 | } | ||
311 | } | ||
312 | } | ||
313 | |||
314 | /** | ||
315 | * Render a titlebar. | ||
316 | * | ||
317 | * Care must be taken not to render over the same pixel multiple times, | ||
318 | * otherwise the colors will be incorrect when using opacity. | ||
319 | * | ||
320 | * The height is: 1px border, 3px padding, font height, 3px padding, 1px border | ||
321 | * The left side for L_TABBED is: 1px border, 2px padding, title | ||
322 | * The left side for other layouts is: 3px padding, title | ||
323 | */ | ||
324 | static void render_titlebar(struct sway_output *output, | ||
325 | pixman_region32_t *output_damage, struct sway_container *con, | ||
326 | int x, int y, int width, | ||
327 | struct border_colors *colors, struct wlr_texture *title_texture, | ||
328 | struct wlr_texture *marks_texture) { | ||
329 | struct wlr_box box; | ||
330 | float color[4]; | ||
331 | struct sway_container_state *state = &con->current; | ||
332 | float output_scale = output->wlr_output->scale; | ||
333 | enum sway_container_layout layout = state->parent->current.layout; | ||
334 | list_t *children = state->parent->current.children; | ||
335 | bool is_last_child = children->items[children->length - 1] == con; | ||
336 | double output_x = output->swayc->current.swayc_x; | ||
337 | double output_y = output->swayc->current.swayc_y; | ||
338 | |||
339 | // Single pixel bar above title | ||
340 | memcpy(&color, colors->border, sizeof(float) * 4); | ||
341 | premultiply_alpha(color, con->alpha); | ||
342 | box.x = x; | ||
343 | box.y = y; | ||
344 | box.width = width; | ||
345 | box.height = TITLEBAR_BORDER_THICKNESS; | ||
346 | scale_box(&box, output_scale); | ||
347 | render_rect(output->wlr_output, output_damage, &box, color); | ||
348 | |||
349 | // Single pixel bar below title | ||
350 | size_t left_offset = 0, right_offset = 0; | ||
351 | bool connects_sides = false; | ||
352 | if (layout == L_HORIZ || layout == L_VERT || | ||
353 | (layout == L_STACKED && is_last_child)) { | ||
354 | if (con->type == C_VIEW) { | ||
355 | left_offset = state->border_left * state->border_thickness; | ||
356 | right_offset = state->border_right * state->border_thickness; | ||
357 | connects_sides = true; | ||
358 | } | ||
359 | } | ||
360 | box.x = x + left_offset; | ||
361 | box.y = y + container_titlebar_height() - TITLEBAR_BORDER_THICKNESS; | ||
362 | box.width = width - left_offset - right_offset; | ||
363 | box.height = TITLEBAR_BORDER_THICKNESS; | ||
364 | scale_box(&box, output_scale); | ||
365 | render_rect(output->wlr_output, output_damage, &box, color); | ||
366 | |||
367 | if (layout == L_TABBED) { | ||
368 | // Single pixel left edge | ||
369 | box.x = x; | ||
370 | box.y = y + TITLEBAR_BORDER_THICKNESS; | ||
371 | box.width = TITLEBAR_BORDER_THICKNESS; | ||
372 | box.height = | ||
373 | container_titlebar_height() - TITLEBAR_BORDER_THICKNESS * 2; | ||
374 | scale_box(&box, output_scale); | ||
375 | render_rect(output->wlr_output, output_damage, &box, color); | ||
376 | |||
377 | // Single pixel right edge | ||
378 | box.x = (x + width - TITLEBAR_BORDER_THICKNESS) * output_scale; | ||
379 | render_rect(output->wlr_output, output_damage, &box, color); | ||
380 | } | ||
381 | |||
382 | size_t inner_width = width - TITLEBAR_H_PADDING * 2; | ||
383 | |||
384 | // Marks | ||
385 | size_t marks_ob_width = 0; // output-buffer-local | ||
386 | if (config->show_marks && marks_texture) { | ||
387 | struct wlr_box texture_box; | ||
388 | wlr_texture_get_size(marks_texture, | ||
389 | &texture_box.width, &texture_box.height); | ||
390 | texture_box.x = (x - output_x + width - TITLEBAR_H_PADDING) | ||
391 | * output_scale - texture_box.width; | ||
392 | texture_box.y = (y - output_y + TITLEBAR_V_PADDING) * output_scale; | ||
393 | |||
394 | float matrix[9]; | ||
395 | wlr_matrix_project_box(matrix, &texture_box, | ||
396 | WL_OUTPUT_TRANSFORM_NORMAL, | ||
397 | 0.0, output->wlr_output->transform_matrix); | ||
398 | |||
399 | if (inner_width * output_scale < texture_box.width) { | ||
400 | texture_box.width = inner_width * output_scale; | ||
401 | } | ||
402 | render_texture(output->wlr_output, output_damage, marks_texture, | ||
403 | &texture_box, matrix, con->alpha); | ||
404 | marks_ob_width = texture_box.width; | ||
405 | |||
406 | // Gap between the marks and bottom padding, for when the marks texture | ||
407 | // height is smaller than the config's font height | ||
408 | memcpy(&color, colors->background, sizeof(float) * 4); | ||
409 | premultiply_alpha(color, con->alpha); | ||
410 | box.x = texture_box.x; | ||
411 | box.y = texture_box.y + texture_box.height; | ||
412 | box.width = texture_box.width; | ||
413 | box.height = config->font_height * output_scale - texture_box.height; | ||
414 | if (box.height > 0) { | ||
415 | render_rect(output->wlr_output, output_damage, &box, color); | ||
416 | } | ||
417 | } | ||
418 | |||
419 | // Title text | ||
420 | size_t title_ob_width = 0; // output-buffer-local | ||
421 | if (title_texture) { | ||
422 | struct wlr_box texture_box; | ||
423 | wlr_texture_get_size(title_texture, | ||
424 | &texture_box.width, &texture_box.height); | ||
425 | texture_box.x = (x - output_x + TITLEBAR_H_PADDING) * output_scale; | ||
426 | texture_box.y = (y - output_y + TITLEBAR_V_PADDING) * output_scale; | ||
427 | |||
428 | float matrix[9]; | ||
429 | wlr_matrix_project_box(matrix, &texture_box, | ||
430 | WL_OUTPUT_TRANSFORM_NORMAL, | ||
431 | 0.0, output->wlr_output->transform_matrix); | ||
432 | |||
433 | if (inner_width * output_scale - marks_ob_width < texture_box.width) { | ||
434 | texture_box.width = inner_width * output_scale - marks_ob_width; | ||
435 | } | ||
436 | render_texture(output->wlr_output, output_damage, title_texture, | ||
437 | &texture_box, matrix, con->alpha); | ||
438 | title_ob_width = texture_box.width; | ||
439 | |||
440 | // Gap between the title and bottom padding, for when the title texture | ||
441 | // height is smaller than the config's font height | ||
442 | memcpy(&color, colors->background, sizeof(float) * 4); | ||
443 | premultiply_alpha(color, con->alpha); | ||
444 | box.x = texture_box.x; | ||
445 | box.y = texture_box.y + texture_box.height; | ||
446 | box.width = texture_box.width; | ||
447 | box.height = config->font_height * output_scale - texture_box.height; | ||
448 | if (box.height > 0) { | ||
449 | render_rect(output->wlr_output, output_damage, &box, color); | ||
450 | } | ||
451 | } | ||
452 | |||
453 | // Padding above title | ||
454 | memcpy(&color, colors->background, sizeof(float) * 4); | ||
455 | premultiply_alpha(color, con->alpha); | ||
456 | box.x = x + (layout == L_TABBED) * TITLEBAR_BORDER_THICKNESS; | ||
457 | box.y = y + TITLEBAR_BORDER_THICKNESS; | ||
458 | box.width = width - (layout == L_TABBED) * TITLEBAR_BORDER_THICKNESS * 2; | ||
459 | box.height = TITLEBAR_V_PADDING - TITLEBAR_BORDER_THICKNESS; | ||
460 | scale_box(&box, output_scale); | ||
461 | render_rect(output->wlr_output, output_damage, &box, color); | ||
462 | |||
463 | // Padding below title | ||
464 | box.y = (y + TITLEBAR_V_PADDING + config->font_height) * output_scale; | ||
465 | render_rect(output->wlr_output, output_damage, &box, color); | ||
466 | |||
467 | // Filler between title and marks | ||
468 | box.width = inner_width * output_scale - title_ob_width - marks_ob_width; | ||
469 | if (box.width > 0) { | ||
470 | box.x = (x + TITLEBAR_H_PADDING) * output_scale + title_ob_width; | ||
471 | box.y = (y + TITLEBAR_V_PADDING) * output_scale; | ||
472 | box.height = config->font_height * output_scale; | ||
473 | render_rect(output->wlr_output, output_damage, &box, color); | ||
474 | } | ||
475 | |||
476 | // Padding left of title | ||
477 | left_offset = (layout == L_TABBED) * TITLEBAR_BORDER_THICKNESS; | ||
478 | box.x = x + left_offset; | ||
479 | box.y = y + TITLEBAR_V_PADDING; | ||
480 | box.width = TITLEBAR_H_PADDING - left_offset; | ||
481 | box.height = config->font_height; | ||
482 | scale_box(&box, output_scale); | ||
483 | render_rect(output->wlr_output, output_damage, &box, color); | ||
484 | |||
485 | // Padding right of marks | ||
486 | right_offset = (layout == L_TABBED) * TITLEBAR_BORDER_THICKNESS; | ||
487 | box.x = x + width - TITLEBAR_H_PADDING; | ||
488 | box.y = y + TITLEBAR_V_PADDING; | ||
489 | box.width = TITLEBAR_H_PADDING - right_offset; | ||
490 | box.height = config->font_height; | ||
491 | scale_box(&box, output_scale); | ||
492 | render_rect(output->wlr_output, output_damage, &box, color); | ||
493 | |||
494 | if (connects_sides) { | ||
495 | // Left pixel in line with bottom bar | ||
496 | box.x = x; | ||
497 | box.y = y + container_titlebar_height() - TITLEBAR_BORDER_THICKNESS; | ||
498 | box.width = state->border_thickness * state->border_left; | ||
499 | box.height = TITLEBAR_BORDER_THICKNESS; | ||
500 | scale_box(&box, output_scale); | ||
501 | render_rect(output->wlr_output, output_damage, &box, color); | ||
502 | |||
503 | // Right pixel in line with bottom bar | ||
504 | box.x = x + width - state->border_thickness * state->border_right; | ||
505 | box.y = y + container_titlebar_height() - TITLEBAR_BORDER_THICKNESS; | ||
506 | box.width = state->border_thickness * state->border_right; | ||
507 | box.height = TITLEBAR_BORDER_THICKNESS; | ||
508 | scale_box(&box, output_scale); | ||
509 | render_rect(output->wlr_output, output_damage, &box, color); | ||
510 | } | ||
511 | } | ||
512 | |||
513 | /** | ||
514 | * Render the top border line for a view using "border pixel". | ||
515 | */ | ||
516 | static void render_top_border(struct sway_output *output, | ||
517 | pixman_region32_t *output_damage, struct sway_container *con, | ||
518 | struct border_colors *colors) { | ||
519 | struct sway_container_state *state = &con->current; | ||
520 | if (!state->border_top) { | ||
521 | return; | ||
522 | } | ||
523 | struct wlr_box box; | ||
524 | float color[4]; | ||
525 | float output_scale = output->wlr_output->scale; | ||
526 | |||
527 | // Child border - top edge | ||
528 | memcpy(&color, colors->child_border, sizeof(float) * 4); | ||
529 | premultiply_alpha(color, con->alpha); | ||
530 | box.x = state->swayc_x; | ||
531 | box.y = state->swayc_y; | ||
532 | box.width = state->swayc_width; | ||
533 | box.height = state->border_thickness; | ||
534 | scale_box(&box, output_scale); | ||
535 | render_rect(output->wlr_output, output_damage, &box, color); | ||
536 | } | ||
537 | |||
538 | static void render_container(struct sway_output *output, | ||
539 | pixman_region32_t *damage, struct sway_container *con, bool parent_focused); | ||
540 | |||
541 | /** | ||
542 | * Render a container's children using a L_HORIZ or L_VERT layout. | ||
543 | * | ||
544 | * Wrap child views in borders and leave child containers borderless because | ||
545 | * they'll apply their own borders to their children. | ||
546 | */ | ||
547 | static void render_container_simple(struct sway_output *output, | ||
548 | pixman_region32_t *damage, struct sway_container *con, | ||
549 | bool parent_focused) { | ||
550 | for (int i = 0; i < con->current.children->length; ++i) { | ||
551 | struct sway_container *child = con->current.children->items[i]; | ||
552 | |||
553 | if (child->type == C_VIEW) { | ||
554 | struct sway_view *view = child->sway_view; | ||
555 | struct border_colors *colors; | ||
556 | struct wlr_texture *title_texture; | ||
557 | struct wlr_texture *marks_texture; | ||
558 | struct sway_container_state *state = &child->current; | ||
559 | |||
560 | if (view_is_urgent(view)) { | ||
561 | colors = &config->border_colors.urgent; | ||
562 | title_texture = child->title_urgent; | ||
563 | marks_texture = view->marks_urgent; | ||
564 | } else if (state->focused || parent_focused) { | ||
565 | colors = &config->border_colors.focused; | ||
566 | title_texture = child->title_focused; | ||
567 | marks_texture = view->marks_focused; | ||
568 | } else if (con->current.focused_inactive_child == child) { | ||
569 | colors = &config->border_colors.focused_inactive; | ||
570 | title_texture = child->title_focused_inactive; | ||
571 | marks_texture = view->marks_focused_inactive; | ||
572 | } else { | ||
573 | colors = &config->border_colors.unfocused; | ||
574 | title_texture = child->title_unfocused; | ||
575 | marks_texture = view->marks_unfocused; | ||
576 | } | ||
577 | |||
578 | if (!view->using_csd) { | ||
579 | if (state->border == B_NORMAL) { | ||
580 | render_titlebar(output, damage, child, state->swayc_x, | ||
581 | state->swayc_y, state->swayc_width, colors, | ||
582 | title_texture, marks_texture); | ||
583 | } else { | ||
584 | render_top_border(output, damage, child, colors); | ||
585 | } | ||
586 | } | ||
587 | render_view(output, damage, child, colors); | ||
588 | } else { | ||
589 | render_container(output, damage, child, | ||
590 | parent_focused || child->current.focused); | ||
591 | } | ||
592 | } | ||
593 | } | ||
594 | |||
595 | /** | ||
596 | * Render a container's children using the L_TABBED layout. | ||
597 | */ | ||
598 | static void render_container_tabbed(struct sway_output *output, | ||
599 | pixman_region32_t *damage, struct sway_container *con, | ||
600 | bool parent_focused) { | ||
601 | if (!con->current.children->length) { | ||
602 | return; | ||
603 | } | ||
604 | struct sway_container_state *pstate = &con->current; | ||
605 | struct sway_container *current = pstate->focused_inactive_child; | ||
606 | struct border_colors *current_colors = &config->border_colors.unfocused; | ||
607 | |||
608 | double width_gap_adjustment = 2 * pstate->current_gaps; | ||
609 | int tab_width = | ||
610 | (pstate->swayc_width - width_gap_adjustment) / pstate->children->length; | ||
611 | |||
612 | // Render tabs | ||
613 | for (int i = 0; i < pstate->children->length; ++i) { | ||
614 | struct sway_container *child = pstate->children->items[i]; | ||
615 | struct sway_view *view = child->type == C_VIEW ? child->sway_view : NULL; | ||
616 | struct sway_container_state *cstate = &child->current; | ||
617 | struct border_colors *colors; | ||
618 | struct wlr_texture *title_texture; | ||
619 | struct wlr_texture *marks_texture; | ||
620 | bool urgent = view ? | ||
621 | view_is_urgent(view) : container_has_urgent_child(child); | ||
622 | |||
623 | if (urgent) { | ||
624 | colors = &config->border_colors.urgent; | ||
625 | title_texture = child->title_urgent; | ||
626 | marks_texture = view ? view->marks_urgent : NULL; | ||
627 | } else if (cstate->focused || parent_focused) { | ||
628 | colors = &config->border_colors.focused; | ||
629 | title_texture = child->title_focused; | ||
630 | marks_texture = view ? view->marks_focused : NULL; | ||
631 | } else if (child == pstate->focused_inactive_child) { | ||
632 | colors = &config->border_colors.focused_inactive; | ||
633 | title_texture = child->title_focused_inactive; | ||
634 | marks_texture = view ? view->marks_focused_inactive : NULL; | ||
635 | } else { | ||
636 | colors = &config->border_colors.unfocused; | ||
637 | title_texture = child->title_unfocused; | ||
638 | marks_texture = view ? view->marks_unfocused : NULL; | ||
639 | } | ||
640 | |||
641 | int x = cstate->swayc_x + tab_width * i; | ||
642 | |||
643 | // Make last tab use the remaining width of the parent | ||
644 | if (i == pstate->children->length - 1) { | ||
645 | tab_width = | ||
646 | pstate->swayc_width - width_gap_adjustment - tab_width * i; | ||
647 | } | ||
648 | |||
649 | render_titlebar(output, damage, child, x, cstate->swayc_y, tab_width, | ||
650 | colors, title_texture, marks_texture); | ||
651 | |||
652 | if (child == current) { | ||
653 | current_colors = colors; | ||
654 | } | ||
655 | } | ||
656 | |||
657 | // Render surface and left/right/bottom borders | ||
658 | if (current->type == C_VIEW) { | ||
659 | render_view(output, damage, current, current_colors); | ||
660 | } else { | ||
661 | render_container(output, damage, current, | ||
662 | parent_focused || current->current.focused); | ||
663 | } | ||
664 | } | ||
665 | |||
666 | /** | ||
667 | * Render a container's children using the L_STACKED layout. | ||
668 | */ | ||
669 | static void render_container_stacked(struct sway_output *output, | ||
670 | pixman_region32_t *damage, struct sway_container *con, | ||
671 | bool parent_focused) { | ||
672 | if (!con->current.children->length) { | ||
673 | return; | ||
674 | } | ||
675 | struct sway_container_state *pstate = &con->current; | ||
676 | struct sway_container *current = pstate->focused_inactive_child; | ||
677 | struct border_colors *current_colors = &config->border_colors.unfocused; | ||
678 | |||
679 | size_t titlebar_height = container_titlebar_height(); | ||
680 | |||
681 | // Render titles | ||
682 | for (int i = 0; i < pstate->children->length; ++i) { | ||
683 | struct sway_container *child = pstate->children->items[i]; | ||
684 | struct sway_view *view = child->type == C_VIEW ? child->sway_view : NULL; | ||
685 | struct sway_container_state *cstate = &child->current; | ||
686 | struct border_colors *colors; | ||
687 | struct wlr_texture *title_texture; | ||
688 | struct wlr_texture *marks_texture; | ||
689 | bool urgent = view ? | ||
690 | view_is_urgent(view) : container_has_urgent_child(child); | ||
691 | |||
692 | if (urgent) { | ||
693 | colors = &config->border_colors.urgent; | ||
694 | title_texture = child->title_urgent; | ||
695 | marks_texture = view ? view->marks_urgent : NULL; | ||
696 | } else if (cstate->focused || parent_focused) { | ||
697 | colors = &config->border_colors.focused; | ||
698 | title_texture = child->title_focused; | ||
699 | marks_texture = view ? view->marks_focused : NULL; | ||
700 | } else if (child == pstate->focused_inactive_child) { | ||
701 | colors = &config->border_colors.focused_inactive; | ||
702 | title_texture = child->title_focused_inactive; | ||
703 | marks_texture = view ? view->marks_focused_inactive : NULL; | ||
704 | } else { | ||
705 | colors = &config->border_colors.unfocused; | ||
706 | title_texture = child->title_unfocused; | ||
707 | marks_texture = view ? view->marks_unfocused : NULL; | ||
708 | } | ||
709 | |||
710 | int y = cstate->swayc_y + titlebar_height * i; | ||
711 | render_titlebar(output, damage, child, cstate->swayc_x, y, | ||
712 | cstate->swayc_width, colors, title_texture, marks_texture); | ||
713 | |||
714 | if (child == current) { | ||
715 | current_colors = colors; | ||
716 | } | ||
717 | } | ||
718 | |||
719 | // Render surface and left/right/bottom borders | ||
720 | if (current->type == C_VIEW) { | ||
721 | render_view(output, damage, current, current_colors); | ||
722 | } else { | ||
723 | render_container(output, damage, current, | ||
724 | parent_focused || current->current.focused); | ||
725 | } | ||
726 | } | ||
727 | |||
728 | static void render_container(struct sway_output *output, | ||
729 | pixman_region32_t *damage, struct sway_container *con, | ||
730 | bool parent_focused) { | ||
731 | switch (con->current.layout) { | ||
732 | case L_NONE: | ||
733 | case L_HORIZ: | ||
734 | case L_VERT: | ||
735 | render_container_simple(output, damage, con, parent_focused); | ||
736 | break; | ||
737 | case L_STACKED: | ||
738 | render_container_stacked(output, damage, con, parent_focused); | ||
739 | break; | ||
740 | case L_TABBED: | ||
741 | render_container_tabbed(output, damage, con, parent_focused); | ||
742 | break; | ||
743 | case L_FLOATING: | ||
744 | sway_assert(false, "Didn't expect to see floating here"); | ||
745 | } | ||
746 | } | ||
747 | |||
748 | static void render_floating_container(struct sway_output *soutput, | ||
749 | pixman_region32_t *damage, struct sway_container *con) { | ||
750 | if (con->type == C_VIEW) { | ||
751 | struct sway_view *view = con->sway_view; | ||
752 | struct border_colors *colors; | ||
753 | struct wlr_texture *title_texture; | ||
754 | struct wlr_texture *marks_texture; | ||
755 | |||
756 | if (view_is_urgent(view)) { | ||
757 | colors = &config->border_colors.urgent; | ||
758 | title_texture = con->title_urgent; | ||
759 | marks_texture = view->marks_urgent; | ||
760 | } else if (con->current.focused) { | ||
761 | colors = &config->border_colors.focused; | ||
762 | title_texture = con->title_focused; | ||
763 | marks_texture = view->marks_focused; | ||
764 | } else { | ||
765 | colors = &config->border_colors.unfocused; | ||
766 | title_texture = con->title_unfocused; | ||
767 | marks_texture = view->marks_unfocused; | ||
768 | } | ||
769 | |||
770 | if (!view->using_csd) { | ||
771 | if (con->current.border == B_NORMAL) { | ||
772 | render_titlebar(soutput, damage, con, con->current.swayc_x, | ||
773 | con->current.swayc_y, con->current.swayc_width, colors, | ||
774 | title_texture, marks_texture); | ||
775 | } else if (con->current.border != B_NONE) { | ||
776 | render_top_border(soutput, damage, con, colors); | ||
777 | } | ||
778 | } | ||
779 | render_view(soutput, damage, con, colors); | ||
780 | } else { | ||
781 | render_container(soutput, damage, con, false); | ||
782 | } | ||
783 | } | ||
784 | |||
785 | static void render_floating(struct sway_output *soutput, | ||
786 | pixman_region32_t *damage) { | ||
787 | for (int i = 0; i < root_container.current.children->length; ++i) { | ||
788 | struct sway_container *output = | ||
789 | root_container.current.children->items[i]; | ||
790 | for (int j = 0; j < output->current.children->length; ++j) { | ||
791 | struct sway_container *ws = output->current.children->items[j]; | ||
792 | if (!workspace_is_visible(ws)) { | ||
793 | continue; | ||
794 | } | ||
795 | list_t *floating = | ||
796 | ws->current.ws_floating->current.children; | ||
797 | for (int k = 0; k < floating->length; ++k) { | ||
798 | struct sway_container *floater = floating->items[k]; | ||
799 | render_floating_container(soutput, damage, floater); | ||
800 | } | ||
801 | } | ||
802 | } | ||
803 | } | ||
804 | |||
805 | const char *damage_debug = NULL; | ||
806 | |||
807 | void output_render(struct sway_output *output, struct timespec *when, | ||
808 | pixman_region32_t *damage) { | ||
809 | struct wlr_output *wlr_output = output->wlr_output; | ||
810 | |||
811 | struct wlr_renderer *renderer = | ||
812 | wlr_backend_get_renderer(wlr_output->backend); | ||
813 | if (!sway_assert(renderer != NULL, | ||
814 | "expected the output backend to have a renderer")) { | ||
815 | return; | ||
816 | } | ||
817 | |||
818 | wlr_renderer_begin(renderer, wlr_output->width, wlr_output->height); | ||
819 | |||
820 | bool damage_whole_before_swap = false; | ||
821 | if (!pixman_region32_not_empty(damage)) { | ||
822 | // Output isn't damaged but needs buffer swap | ||
823 | goto renderer_end; | ||
824 | } | ||
825 | |||
826 | if (damage_debug != NULL) { | ||
827 | if (strcmp(damage_debug, "highlight") == 0) { | ||
828 | wlr_renderer_clear(renderer, (float[]){1, 1, 0, 1}); | ||
829 | damage_whole_before_swap = true; | ||
830 | } else if (strcmp(damage_debug, "rerender") == 0) { | ||
831 | int width, height; | ||
832 | wlr_output_transformed_resolution(wlr_output, &width, &height); | ||
833 | pixman_region32_union_rect(damage, damage, 0, 0, width, height); | ||
834 | } | ||
835 | } | ||
836 | |||
837 | struct sway_container *workspace = output_get_active_workspace(output); | ||
838 | struct sway_view *fullscreen_view = workspace->current.ws_fullscreen; | ||
839 | struct sway_seat *seat = input_manager_current_seat(input_manager); | ||
840 | |||
841 | if (output_has_opaque_lockscreen(output, seat) && seat->focused_layer) { | ||
842 | struct wlr_layer_surface *wlr_layer_surface = seat->focused_layer; | ||
843 | struct sway_layer_surface *sway_layer_surface = | ||
844 | layer_from_wlr_layer_surface(seat->focused_layer); | ||
845 | struct render_data data = { | ||
846 | .output = output, | ||
847 | .damage = damage, | ||
848 | .alpha = 1.0f, | ||
849 | }; | ||
850 | output_surface_for_each_surface(wlr_layer_surface->surface, | ||
851 | sway_layer_surface->geo.x, sway_layer_surface->geo.y, | ||
852 | &data.root_geo, render_surface_iterator, &data); | ||
853 | } else if (fullscreen_view) { | ||
854 | float clear_color[] = {0.0f, 0.0f, 0.0f, 1.0f}; | ||
855 | |||
856 | int nrects; | ||
857 | pixman_box32_t *rects = pixman_region32_rectangles(damage, &nrects); | ||
858 | for (int i = 0; i < nrects; ++i) { | ||
859 | scissor_output(wlr_output, &rects[i]); | ||
860 | wlr_renderer_clear(renderer, clear_color); | ||
861 | } | ||
862 | |||
863 | // TODO: handle views smaller than the output | ||
864 | if (fullscreen_view->swayc->instructions->length) { | ||
865 | render_saved_view(fullscreen_view, output, damage, 1.0f); | ||
866 | } else { | ||
867 | render_view_surfaces(fullscreen_view, output, damage, 1.0f); | ||
868 | } | ||
869 | |||
870 | if (fullscreen_view->type == SWAY_VIEW_XWAYLAND) { | ||
871 | render_unmanaged(output, damage, | ||
872 | &root_container.sway_root->xwayland_unmanaged); | ||
873 | } | ||
874 | } else { | ||
875 | float clear_color[] = {0.25f, 0.25f, 0.25f, 1.0f}; | ||
876 | |||
877 | int nrects; | ||
878 | pixman_box32_t *rects = pixman_region32_rectangles(damage, &nrects); | ||
879 | for (int i = 0; i < nrects; ++i) { | ||
880 | scissor_output(wlr_output, &rects[i]); | ||
881 | wlr_renderer_clear(renderer, clear_color); | ||
882 | } | ||
883 | |||
884 | render_layer(output, damage, | ||
885 | &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND]); | ||
886 | render_layer(output, damage, | ||
887 | &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM]); | ||
888 | |||
889 | render_container(output, damage, workspace, workspace->current.focused); | ||
890 | render_floating(output, damage); | ||
891 | |||
892 | render_unmanaged(output, damage, | ||
893 | &root_container.sway_root->xwayland_unmanaged); | ||
894 | render_layer(output, damage, | ||
895 | &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]); | ||
896 | } | ||
897 | render_layer(output, damage, | ||
898 | &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY]); | ||
899 | render_drag_icons(output, damage, &root_container.sway_root->drag_icons); | ||
900 | |||
901 | renderer_end: | ||
902 | if (root_container.sway_root->debug_tree) { | ||
903 | wlr_render_texture(renderer, root_container.sway_root->debug_tree, | ||
904 | wlr_output->transform_matrix, 0, 0, 1); | ||
905 | } | ||
906 | |||
907 | if (damage_whole_before_swap || root_container.sway_root->debug_tree) { | ||
908 | int width, height; | ||
909 | wlr_output_transformed_resolution(wlr_output, &width, &height); | ||
910 | pixman_region32_union_rect(damage, damage, 0, 0, width, height); | ||
911 | } | ||
912 | |||
913 | wlr_renderer_scissor(renderer, NULL); | ||
914 | wlr_renderer_end(renderer); | ||
915 | if (!wlr_output_damage_swap_buffers(output->damage, when, damage)) { | ||
916 | return; | ||
917 | } | ||
918 | output->last_frame = *when; | ||
919 | } | ||
diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c index d2932c87..2a89880a 100644 --- a/sway/desktop/transaction.c +++ b/sway/desktop/transaction.c | |||
@@ -4,8 +4,8 @@ | |||
4 | #include <string.h> | 4 | #include <string.h> |
5 | #include <time.h> | 5 | #include <time.h> |
6 | #include <wlr/types/wlr_buffer.h> | 6 | #include <wlr/types/wlr_buffer.h> |
7 | #include <wlr/types/wlr_linux_dmabuf.h> | ||
8 | #include "sway/debug.h" | 7 | #include "sway/debug.h" |
8 | #include "sway/desktop/idle_inhibit_v1.h" | ||
9 | #include "sway/desktop/transaction.h" | 9 | #include "sway/desktop/transaction.h" |
10 | #include "sway/output.h" | 10 | #include "sway/output.h" |
11 | #include "sway/tree/container.h" | 11 | #include "sway/tree/container.h" |
@@ -18,14 +18,14 @@ | |||
18 | * How long we should wait for views to respond to the configure before giving | 18 | * How long we should wait for views to respond to the configure before giving |
19 | * up and applying the transaction anyway. | 19 | * up and applying the transaction anyway. |
20 | */ | 20 | */ |
21 | #define TIMEOUT_MS 200 | 21 | int txn_timeout_ms = 200; |
22 | 22 | ||
23 | /** | 23 | /** |
24 | * If enabled, sway will always wait for the transaction timeout before | 24 | * If enabled, sway will always wait for the transaction timeout before |
25 | * applying it, rather than applying it when the views are ready. This allows us | 25 | * applying it, rather than applying it when the views are ready. This allows us |
26 | * to observe the rendered state while a transaction is in progress. | 26 | * to observe the rendered state while a transaction is in progress. |
27 | */ | 27 | */ |
28 | #define TRANSACTION_DEBUG false | 28 | bool txn_debug = false; |
29 | 29 | ||
30 | struct sway_transaction { | 30 | struct sway_transaction { |
31 | struct wl_event_source *timer; | 31 | struct wl_event_source *timer; |
@@ -46,7 +46,7 @@ struct sway_transaction_instruction { | |||
46 | bool ready; | 46 | bool ready; |
47 | }; | 47 | }; |
48 | 48 | ||
49 | struct sway_transaction *transaction_create() { | 49 | static struct sway_transaction *transaction_create() { |
50 | struct sway_transaction *transaction = | 50 | struct sway_transaction *transaction = |
51 | calloc(1, sizeof(struct sway_transaction)); | 51 | calloc(1, sizeof(struct sway_transaction)); |
52 | transaction->instructions = create_list(); | 52 | transaction->instructions = create_list(); |
@@ -72,8 +72,8 @@ static void save_view_buffer(struct sway_view *view, | |||
72 | } | 72 | } |
73 | if (view->surface && wlr_surface_has_buffer(view->surface)) { | 73 | if (view->surface && wlr_surface_has_buffer(view->surface)) { |
74 | instruction->saved_buffer = wlr_buffer_ref(view->surface->buffer); | 74 | instruction->saved_buffer = wlr_buffer_ref(view->surface->buffer); |
75 | instruction->saved_buffer_width = view->surface->current->width; | 75 | instruction->saved_buffer_width = view->surface->current.width; |
76 | instruction->saved_buffer_height = view->surface->current->height; | 76 | instruction->saved_buffer_height = view->surface->current.height; |
77 | } | 77 | } |
78 | } | 78 | } |
79 | 79 | ||
@@ -138,25 +138,18 @@ static void copy_pending_state(struct sway_container *container, | |||
138 | state->children = create_list(); | 138 | state->children = create_list(); |
139 | list_cat(state->children, container->children); | 139 | list_cat(state->children, container->children); |
140 | } | 140 | } |
141 | } | ||
142 | 141 | ||
143 | static bool transaction_has_container(struct sway_transaction *transaction, | 142 | struct sway_seat *seat = input_manager_current_seat(input_manager); |
144 | struct sway_container *container) { | 143 | state->focused = seat_get_focus(seat) == container; |
145 | for (int i = 0; i < transaction->instructions->length; ++i) { | 144 | |
146 | struct sway_transaction_instruction *instruction = | 145 | if (container->type != C_VIEW) { |
147 | transaction->instructions->items[i]; | 146 | state->focused_inactive_child = |
148 | if (instruction->container == container) { | 147 | seat_get_active_child(seat, container); |
149 | return true; | ||
150 | } | ||
151 | } | 148 | } |
152 | return false; | ||
153 | } | 149 | } |
154 | 150 | ||
155 | void transaction_add_container(struct sway_transaction *transaction, | 151 | static void transaction_add_container(struct sway_transaction *transaction, |
156 | struct sway_container *container) { | 152 | struct sway_container *container) { |
157 | if (transaction_has_container(transaction, container)) { | ||
158 | return; | ||
159 | } | ||
160 | struct sway_transaction_instruction *instruction = | 153 | struct sway_transaction_instruction *instruction = |
161 | calloc(1, sizeof(struct sway_transaction_instruction)); | 154 | calloc(1, sizeof(struct sway_transaction_instruction)); |
162 | instruction->transaction = transaction; | 155 | instruction->transaction = transaction; |
@@ -174,7 +167,7 @@ void transaction_add_container(struct sway_transaction *transaction, | |||
174 | * Apply a transaction to the "current" state of the tree. | 167 | * Apply a transaction to the "current" state of the tree. |
175 | */ | 168 | */ |
176 | static void transaction_apply(struct sway_transaction *transaction) { | 169 | static void transaction_apply(struct sway_transaction *transaction) { |
177 | wlr_log(L_DEBUG, "Applying transaction %p", transaction); | 170 | wlr_log(WLR_DEBUG, "Applying transaction %p", transaction); |
178 | if (server.debug_txn_timings) { | 171 | if (server.debug_txn_timings) { |
179 | struct timespec now; | 172 | struct timespec now; |
180 | clock_gettime(CLOCK_MONOTONIC, &now); | 173 | clock_gettime(CLOCK_MONOTONIC, &now); |
@@ -185,9 +178,9 @@ static void transaction_apply(struct sway_transaction *transaction) { | |||
185 | float ms_waiting = (now.tv_sec - commit->tv_sec) * 1000 + | 178 | float ms_waiting = (now.tv_sec - commit->tv_sec) * 1000 + |
186 | (now.tv_nsec - commit->tv_nsec) / 1000000.0; | 179 | (now.tv_nsec - commit->tv_nsec) / 1000000.0; |
187 | float ms_total = ms_arranging + ms_waiting; | 180 | float ms_total = ms_arranging + ms_waiting; |
188 | wlr_log(L_DEBUG, "Transaction %p: %.1fms arranging, %.1fms waiting, " | 181 | wlr_log(WLR_DEBUG, "Transaction %p: %.1fms arranging, %.1fms waiting, " |
189 | "%.1fms total (%.1f frames if 60Hz)", transaction, | 182 | "%.1fms total (%.1f frames if 60Hz)", transaction, |
190 | ms_arranging, ms_waiting, ms_total, ms_total / (1000 / 60)); | 183 | ms_arranging, ms_waiting, ms_total, ms_total / (1000.0f / 60)); |
191 | } | 184 | } |
192 | 185 | ||
193 | // Apply the instruction state to the container's current state | 186 | // Apply the instruction state to the container's current state |
@@ -209,10 +202,12 @@ static void transaction_apply(struct sway_transaction *transaction) { | |||
209 | .width = instruction->state.swayc_width, | 202 | .width = instruction->state.swayc_width, |
210 | .height = instruction->state.swayc_height, | 203 | .height = instruction->state.swayc_height, |
211 | }; | 204 | }; |
212 | for (int j = 0; j < root_container.children->length; ++j) { | 205 | for (int j = 0; j < root_container.current.children->length; ++j) { |
213 | struct sway_container *output = root_container.children->items[j]; | 206 | struct sway_container *output = root_container.current.children->items[j]; |
214 | output_damage_box(output->sway_output, &old_box); | 207 | if (output->sway_output) { |
215 | output_damage_box(output->sway_output, &new_box); | 208 | output_damage_box(output->sway_output, &old_box); |
209 | output_damage_box(output->sway_output, &new_box); | ||
210 | } | ||
216 | } | 211 | } |
217 | 212 | ||
218 | // There are separate children lists for each instruction state, the | 213 | // There are separate children lists for each instruction state, the |
@@ -227,29 +222,22 @@ static void transaction_apply(struct sway_transaction *transaction) { | |||
227 | } | 222 | } |
228 | } | 223 | } |
229 | 224 | ||
230 | /** | ||
231 | * For simplicity, we only progress the queue if it can be completely flushed. | ||
232 | */ | ||
233 | static void transaction_progress_queue() { | 225 | static void transaction_progress_queue() { |
234 | // We iterate this list in reverse because we're more likely to find a | 226 | while (server.transactions->length) { |
235 | // waiting transactions at the end of the list. | 227 | struct sway_transaction *transaction = server.transactions->items[0]; |
236 | for (int i = server.transactions->length - 1; i >= 0; --i) { | ||
237 | struct sway_transaction *transaction = server.transactions->items[i]; | ||
238 | if (transaction->num_waiting) { | 228 | if (transaction->num_waiting) { |
239 | return; | 229 | return; |
240 | } | 230 | } |
241 | } | ||
242 | for (int i = 0; i < server.transactions->length; ++i) { | ||
243 | struct sway_transaction *transaction = server.transactions->items[i]; | ||
244 | transaction_apply(transaction); | 231 | transaction_apply(transaction); |
245 | transaction_destroy(transaction); | 232 | transaction_destroy(transaction); |
233 | list_del(server.transactions, 0); | ||
246 | } | 234 | } |
247 | server.transactions->length = 0; | 235 | idle_inhibit_v1_check_active(server.idle_inhibit_manager_v1); |
248 | } | 236 | } |
249 | 237 | ||
250 | static int handle_timeout(void *data) { | 238 | static int handle_timeout(void *data) { |
251 | struct sway_transaction *transaction = data; | 239 | struct sway_transaction *transaction = data; |
252 | wlr_log(L_DEBUG, "Transaction %p timed out (%li waiting)", | 240 | wlr_log(WLR_DEBUG, "Transaction %p timed out (%li waiting)", |
253 | transaction, transaction->num_waiting); | 241 | transaction, transaction->num_waiting); |
254 | transaction->num_waiting = 0; | 242 | transaction->num_waiting = 0; |
255 | transaction_progress_queue(); | 243 | transaction_progress_queue(); |
@@ -283,8 +271,8 @@ static bool should_configure(struct sway_container *con, | |||
283 | return true; | 271 | return true; |
284 | } | 272 | } |
285 | 273 | ||
286 | void transaction_commit(struct sway_transaction *transaction) { | 274 | static void transaction_commit(struct sway_transaction *transaction) { |
287 | wlr_log(L_DEBUG, "Transaction %p committing with %i instructions", | 275 | wlr_log(WLR_DEBUG, "Transaction %p committing with %i instructions", |
288 | transaction, transaction->instructions->length); | 276 | transaction, transaction->instructions->length); |
289 | transaction->num_waiting = 0; | 277 | transaction->num_waiting = 0; |
290 | for (int i = 0; i < transaction->instructions->length; ++i) { | 278 | for (int i = 0; i < transaction->instructions->length; ++i) { |
@@ -317,9 +305,10 @@ void transaction_commit(struct sway_transaction *transaction) { | |||
317 | } else { | 305 | } else { |
318 | // There are no other transactions in progress, and this one has nothing | 306 | // There are no other transactions in progress, and this one has nothing |
319 | // to wait for, so we can skip the queue. | 307 | // to wait for, so we can skip the queue. |
320 | wlr_log(L_DEBUG, "Transaction %p has nothing to wait for", transaction); | 308 | wlr_log(WLR_DEBUG, "Transaction %p has nothing to wait for", transaction); |
321 | transaction_apply(transaction); | 309 | transaction_apply(transaction); |
322 | transaction_destroy(transaction); | 310 | transaction_destroy(transaction); |
311 | idle_inhibit_v1_check_active(server.idle_inhibit_manager_v1); | ||
323 | return; | 312 | return; |
324 | } | 313 | } |
325 | 314 | ||
@@ -327,7 +316,7 @@ void transaction_commit(struct sway_transaction *transaction) { | |||
327 | // Set up a timer which the views must respond within | 316 | // Set up a timer which the views must respond within |
328 | transaction->timer = wl_event_loop_add_timer(server.wl_event_loop, | 317 | transaction->timer = wl_event_loop_add_timer(server.wl_event_loop, |
329 | handle_timeout, transaction); | 318 | handle_timeout, transaction); |
330 | wl_event_source_timer_update(transaction->timer, TIMEOUT_MS); | 319 | wl_event_source_timer_update(transaction->timer, txn_timeout_ms); |
331 | } | 320 | } |
332 | 321 | ||
333 | // The debug tree shows the pending/live tree. Here is a good place to | 322 | // The debug tree shows the pending/live tree. Here is a good place to |
@@ -347,7 +336,7 @@ static void set_instruction_ready( | |||
347 | struct timespec *start = &transaction->commit_time; | 336 | struct timespec *start = &transaction->commit_time; |
348 | float ms = (now.tv_sec - start->tv_sec) * 1000 + | 337 | float ms = (now.tv_sec - start->tv_sec) * 1000 + |
349 | (now.tv_nsec - start->tv_nsec) / 1000000.0; | 338 | (now.tv_nsec - start->tv_nsec) / 1000000.0; |
350 | wlr_log(L_DEBUG, "Transaction %p: %li/%li ready in %.1fms (%s)", | 339 | wlr_log(WLR_DEBUG, "Transaction %p: %li/%li ready in %.1fms (%s)", |
351 | transaction, | 340 | transaction, |
352 | transaction->num_configures - transaction->num_waiting + 1, | 341 | transaction->num_configures - transaction->num_waiting + 1, |
353 | transaction->num_configures, ms, | 342 | transaction->num_configures, ms, |
@@ -358,11 +347,11 @@ static void set_instruction_ready( | |||
358 | // If all views are ready, apply the transaction. | 347 | // If all views are ready, apply the transaction. |
359 | // If the transaction has timed out then its num_waiting will be 0 already. | 348 | // If the transaction has timed out then its num_waiting will be 0 already. |
360 | if (transaction->num_waiting > 0 && --transaction->num_waiting == 0) { | 349 | if (transaction->num_waiting > 0 && --transaction->num_waiting == 0) { |
361 | #if !TRANSACTION_DEBUG | 350 | if (!txn_debug) { |
362 | wlr_log(L_DEBUG, "Transaction %p is ready", transaction); | 351 | wlr_log(WLR_DEBUG, "Transaction %p is ready", transaction); |
363 | wl_event_source_timer_update(transaction->timer, 0); | 352 | wl_event_source_timer_update(transaction->timer, 0); |
364 | transaction_progress_queue(); | 353 | transaction_progress_queue(); |
365 | #endif | 354 | } |
366 | } | 355 | } |
367 | } | 356 | } |
368 | 357 | ||
@@ -374,7 +363,9 @@ static void set_instructions_ready(struct sway_view *view, int index) { | |||
374 | for (int i = 0; i <= index; ++i) { | 363 | for (int i = 0; i <= index; ++i) { |
375 | struct sway_transaction_instruction *instruction = | 364 | struct sway_transaction_instruction *instruction = |
376 | view->swayc->instructions->items[i]; | 365 | view->swayc->instructions->items[i]; |
377 | set_instruction_ready(instruction); | 366 | if (!instruction->ready) { |
367 | set_instruction_ready(instruction); | ||
368 | } | ||
378 | } | 369 | } |
379 | } | 370 | } |
380 | 371 | ||
@@ -413,3 +404,17 @@ struct wlr_texture *transaction_get_saved_texture(struct sway_view *view, | |||
413 | *height = instruction->saved_buffer_height; | 404 | *height = instruction->saved_buffer_height; |
414 | return instruction->saved_buffer->texture; | 405 | return instruction->saved_buffer->texture; |
415 | } | 406 | } |
407 | |||
408 | void transaction_commit_dirty(void) { | ||
409 | if (!server.dirty_containers->length) { | ||
410 | return; | ||
411 | } | ||
412 | struct sway_transaction *transaction = transaction_create(); | ||
413 | for (int i = 0; i < server.dirty_containers->length; ++i) { | ||
414 | struct sway_container *container = server.dirty_containers->items[i]; | ||
415 | transaction_add_container(transaction, container); | ||
416 | container->dirty = false; | ||
417 | } | ||
418 | server.dirty_containers->length = 0; | ||
419 | transaction_commit(transaction); | ||
420 | } | ||
diff --git a/sway/desktop/xdg_shell.c b/sway/desktop/xdg_shell.c index 47604c31..f3e4fef8 100644 --- a/sway/desktop/xdg_shell.c +++ b/sway/desktop/xdg_shell.c | |||
@@ -1,4 +1,5 @@ | |||
1 | #define _POSIX_C_SOURCE 199309L | 1 | #define _POSIX_C_SOURCE 199309L |
2 | #include <float.h> | ||
2 | #include <stdbool.h> | 3 | #include <stdbool.h> |
3 | #include <stdlib.h> | 4 | #include <stdlib.h> |
4 | #include <wayland-server.h> | 5 | #include <wayland-server.h> |
@@ -45,6 +46,24 @@ static void popup_handle_destroy(struct wl_listener *listener, void *data) { | |||
45 | view_child_destroy(&popup->child); | 46 | view_child_destroy(&popup->child); |
46 | } | 47 | } |
47 | 48 | ||
49 | static void popup_unconstrain(struct sway_xdg_popup *popup) { | ||
50 | struct sway_view *view = popup->child.view; | ||
51 | struct wlr_xdg_popup *wlr_popup = popup->wlr_xdg_surface->popup; | ||
52 | |||
53 | struct sway_container *output = container_parent(view->swayc, C_OUTPUT); | ||
54 | |||
55 | // the output box expressed in the coordinate system of the toplevel parent | ||
56 | // of the popup | ||
57 | struct wlr_box output_toplevel_sx_box = { | ||
58 | .x = output->x - view->x, | ||
59 | .y = output->y - view->y, | ||
60 | .width = output->width, | ||
61 | .height = output->height, | ||
62 | }; | ||
63 | |||
64 | wlr_xdg_popup_unconstrain_from_box(wlr_popup, &output_toplevel_sx_box); | ||
65 | } | ||
66 | |||
48 | static struct sway_xdg_popup *popup_create( | 67 | static struct sway_xdg_popup *popup_create( |
49 | struct wlr_xdg_popup *wlr_popup, struct sway_view *view) { | 68 | struct wlr_xdg_popup *wlr_popup, struct sway_view *view) { |
50 | struct wlr_xdg_surface *xdg_surface = wlr_popup->base; | 69 | struct wlr_xdg_surface *xdg_surface = wlr_popup->base; |
@@ -55,12 +74,15 @@ static struct sway_xdg_popup *popup_create( | |||
55 | return NULL; | 74 | return NULL; |
56 | } | 75 | } |
57 | view_child_init(&popup->child, &popup_impl, view, xdg_surface->surface); | 76 | view_child_init(&popup->child, &popup_impl, view, xdg_surface->surface); |
77 | popup->wlr_xdg_surface = xdg_surface; | ||
58 | 78 | ||
59 | wl_signal_add(&xdg_surface->events.new_popup, &popup->new_popup); | 79 | wl_signal_add(&xdg_surface->events.new_popup, &popup->new_popup); |
60 | popup->new_popup.notify = popup_handle_new_popup; | 80 | popup->new_popup.notify = popup_handle_new_popup; |
61 | wl_signal_add(&xdg_surface->events.destroy, &popup->destroy); | 81 | wl_signal_add(&xdg_surface->events.destroy, &popup->destroy); |
62 | popup->destroy.notify = popup_handle_destroy; | 82 | popup->destroy.notify = popup_handle_destroy; |
63 | 83 | ||
84 | popup_unconstrain(popup); | ||
85 | |||
64 | return popup; | 86 | return popup; |
65 | } | 87 | } |
66 | 88 | ||
@@ -74,6 +96,16 @@ static struct sway_xdg_shell_view *xdg_shell_view_from_view( | |||
74 | return (struct sway_xdg_shell_view *)view; | 96 | return (struct sway_xdg_shell_view *)view; |
75 | } | 97 | } |
76 | 98 | ||
99 | static void get_constraints(struct sway_view *view, double *min_width, | ||
100 | double *max_width, double *min_height, double *max_height) { | ||
101 | struct wlr_xdg_toplevel_state *state = | ||
102 | &view->wlr_xdg_surface->toplevel->current; | ||
103 | *min_width = state->min_width > 0 ? state->min_width : DBL_MIN; | ||
104 | *max_width = state->max_width > 0 ? state->max_width : DBL_MAX; | ||
105 | *min_height = state->min_height > 0 ? state->min_height : DBL_MIN; | ||
106 | *max_height = state->max_height > 0 ? state->max_height : DBL_MAX; | ||
107 | } | ||
108 | |||
77 | static const char *get_string_prop(struct sway_view *view, enum sway_view_prop prop) { | 109 | static const char *get_string_prop(struct sway_view *view, enum sway_view_prop prop) { |
78 | if (xdg_shell_view_from_view(view) == NULL) { | 110 | if (xdg_shell_view_from_view(view) == NULL) { |
79 | return NULL; | 111 | return NULL; |
@@ -167,6 +199,7 @@ static void destroy(struct sway_view *view) { | |||
167 | } | 199 | } |
168 | 200 | ||
169 | static const struct sway_view_impl view_impl = { | 201 | static const struct sway_view_impl view_impl = { |
202 | .get_constraints = get_constraints, | ||
170 | .get_string_prop = get_string_prop, | 203 | .get_string_prop = get_string_prop, |
171 | .configure = configure, | 204 | .configure = configure, |
172 | .set_activated = set_activated, | 205 | .set_activated = set_activated, |
@@ -192,10 +225,24 @@ static void handle_commit(struct wl_listener *listener, void *data) { | |||
192 | transaction_notify_view_ready(view, xdg_surface->configure_serial); | 225 | transaction_notify_view_ready(view, xdg_surface->configure_serial); |
193 | } | 226 | } |
194 | 227 | ||
195 | view_update_title(view, false); | ||
196 | view_damage_from(view); | 228 | view_damage_from(view); |
197 | } | 229 | } |
198 | 230 | ||
231 | static void handle_set_title(struct wl_listener *listener, void *data) { | ||
232 | struct sway_xdg_shell_view *xdg_shell_view = | ||
233 | wl_container_of(listener, xdg_shell_view, set_title); | ||
234 | struct sway_view *view = &xdg_shell_view->view; | ||
235 | view_update_title(view, false); | ||
236 | view_execute_criteria(view); | ||
237 | } | ||
238 | |||
239 | static void handle_set_app_id(struct wl_listener *listener, void *data) { | ||
240 | struct sway_xdg_shell_view *xdg_shell_view = | ||
241 | wl_container_of(listener, xdg_shell_view, set_app_id); | ||
242 | struct sway_view *view = &xdg_shell_view->view; | ||
243 | view_execute_criteria(view); | ||
244 | } | ||
245 | |||
199 | static void handle_new_popup(struct wl_listener *listener, void *data) { | 246 | static void handle_new_popup(struct wl_listener *listener, void *data) { |
200 | struct sway_xdg_shell_view *xdg_shell_view = | 247 | struct sway_xdg_shell_view *xdg_shell_view = |
201 | wl_container_of(listener, xdg_shell_view, new_popup); | 248 | wl_container_of(listener, xdg_shell_view, new_popup); |
@@ -222,8 +269,37 @@ static void handle_request_fullscreen(struct wl_listener *listener, void *data) | |||
222 | 269 | ||
223 | view_set_fullscreen(view, e->fullscreen); | 270 | view_set_fullscreen(view, e->fullscreen); |
224 | 271 | ||
225 | struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); | 272 | struct sway_container *output = container_parent(view->swayc, C_OUTPUT); |
226 | arrange_and_commit(ws); | 273 | arrange_windows(output); |
274 | transaction_commit_dirty(); | ||
275 | } | ||
276 | |||
277 | static void handle_request_move(struct wl_listener *listener, void *data) { | ||
278 | struct sway_xdg_shell_view *xdg_shell_view = | ||
279 | wl_container_of(listener, xdg_shell_view, request_move); | ||
280 | struct sway_view *view = &xdg_shell_view->view; | ||
281 | if (!container_is_floating(view->swayc)) { | ||
282 | return; | ||
283 | } | ||
284 | struct wlr_xdg_toplevel_move_event *e = data; | ||
285 | struct sway_seat *seat = e->seat->seat->data; | ||
286 | if (e->serial == seat->last_button_serial) { | ||
287 | seat_begin_move(seat, view->swayc, seat->last_button); | ||
288 | } | ||
289 | } | ||
290 | |||
291 | static void handle_request_resize(struct wl_listener *listener, void *data) { | ||
292 | struct sway_xdg_shell_view *xdg_shell_view = | ||
293 | wl_container_of(listener, xdg_shell_view, request_resize); | ||
294 | struct sway_view *view = &xdg_shell_view->view; | ||
295 | if (!container_is_floating(view->swayc)) { | ||
296 | return; | ||
297 | } | ||
298 | struct wlr_xdg_toplevel_resize_event *e = data; | ||
299 | struct sway_seat *seat = e->seat->seat->data; | ||
300 | if (e->serial == seat->last_button_serial) { | ||
301 | seat_begin_resize(seat, view->swayc, seat->last_button, e->edges); | ||
302 | } | ||
227 | } | 303 | } |
228 | 304 | ||
229 | static void handle_unmap(struct wl_listener *listener, void *data) { | 305 | static void handle_unmap(struct wl_listener *listener, void *data) { |
@@ -240,6 +316,10 @@ static void handle_unmap(struct wl_listener *listener, void *data) { | |||
240 | wl_list_remove(&xdg_shell_view->commit.link); | 316 | wl_list_remove(&xdg_shell_view->commit.link); |
241 | wl_list_remove(&xdg_shell_view->new_popup.link); | 317 | wl_list_remove(&xdg_shell_view->new_popup.link); |
242 | wl_list_remove(&xdg_shell_view->request_fullscreen.link); | 318 | wl_list_remove(&xdg_shell_view->request_fullscreen.link); |
319 | wl_list_remove(&xdg_shell_view->request_move.link); | ||
320 | wl_list_remove(&xdg_shell_view->request_resize.link); | ||
321 | wl_list_remove(&xdg_shell_view->set_title.link); | ||
322 | wl_list_remove(&xdg_shell_view->set_app_id.link); | ||
243 | } | 323 | } |
244 | 324 | ||
245 | static void handle_map(struct wl_listener *listener, void *data) { | 325 | static void handle_map(struct wl_listener *listener, void *data) { |
@@ -251,8 +331,8 @@ static void handle_map(struct wl_listener *listener, void *data) { | |||
251 | view->natural_width = view->wlr_xdg_surface->geometry.width; | 331 | view->natural_width = view->wlr_xdg_surface->geometry.width; |
252 | view->natural_height = view->wlr_xdg_surface->geometry.height; | 332 | view->natural_height = view->wlr_xdg_surface->geometry.height; |
253 | if (!view->natural_width && !view->natural_height) { | 333 | if (!view->natural_width && !view->natural_height) { |
254 | view->natural_width = view->wlr_xdg_surface->surface->current->width; | 334 | view->natural_width = view->wlr_xdg_surface->surface->current.width; |
255 | view->natural_height = view->wlr_xdg_surface->surface->current->height; | 335 | view->natural_height = view->wlr_xdg_surface->surface->current.height; |
256 | } | 336 | } |
257 | 337 | ||
258 | view_map(view, view->wlr_xdg_surface->surface); | 338 | view_map(view, view->wlr_xdg_surface->surface); |
@@ -260,10 +340,11 @@ static void handle_map(struct wl_listener *listener, void *data) { | |||
260 | if (xdg_surface->toplevel->client_pending.fullscreen) { | 340 | if (xdg_surface->toplevel->client_pending.fullscreen) { |
261 | view_set_fullscreen(view, true); | 341 | view_set_fullscreen(view, true); |
262 | struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); | 342 | struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); |
263 | arrange_and_commit(ws); | 343 | arrange_windows(ws); |
264 | } else { | 344 | } else { |
265 | arrange_and_commit(view->swayc->parent); | 345 | arrange_windows(view->swayc->parent); |
266 | } | 346 | } |
347 | transaction_commit_dirty(); | ||
267 | 348 | ||
268 | xdg_shell_view->commit.notify = handle_commit; | 349 | xdg_shell_view->commit.notify = handle_commit; |
269 | wl_signal_add(&xdg_surface->surface->events.commit, | 350 | wl_signal_add(&xdg_surface->surface->events.commit, |
@@ -276,6 +357,22 @@ static void handle_map(struct wl_listener *listener, void *data) { | |||
276 | xdg_shell_view->request_fullscreen.notify = handle_request_fullscreen; | 357 | xdg_shell_view->request_fullscreen.notify = handle_request_fullscreen; |
277 | wl_signal_add(&xdg_surface->toplevel->events.request_fullscreen, | 358 | wl_signal_add(&xdg_surface->toplevel->events.request_fullscreen, |
278 | &xdg_shell_view->request_fullscreen); | 359 | &xdg_shell_view->request_fullscreen); |
360 | |||
361 | xdg_shell_view->request_move.notify = handle_request_move; | ||
362 | wl_signal_add(&xdg_surface->toplevel->events.request_move, | ||
363 | &xdg_shell_view->request_move); | ||
364 | |||
365 | xdg_shell_view->request_resize.notify = handle_request_resize; | ||
366 | wl_signal_add(&xdg_surface->toplevel->events.request_resize, | ||
367 | &xdg_shell_view->request_resize); | ||
368 | |||
369 | xdg_shell_view->set_title.notify = handle_set_title; | ||
370 | wl_signal_add(&xdg_surface->toplevel->events.set_title, | ||
371 | &xdg_shell_view->set_title); | ||
372 | |||
373 | xdg_shell_view->set_app_id.notify = handle_set_app_id; | ||
374 | wl_signal_add(&xdg_surface->toplevel->events.set_app_id, | ||
375 | &xdg_shell_view->set_app_id); | ||
279 | } | 376 | } |
280 | 377 | ||
281 | static void handle_destroy(struct wl_listener *listener, void *data) { | 378 | static void handle_destroy(struct wl_listener *listener, void *data) { |
@@ -304,11 +401,11 @@ void handle_xdg_shell_surface(struct wl_listener *listener, void *data) { | |||
304 | struct wlr_xdg_surface *xdg_surface = data; | 401 | struct wlr_xdg_surface *xdg_surface = data; |
305 | 402 | ||
306 | if (xdg_surface->role == WLR_XDG_SURFACE_ROLE_POPUP) { | 403 | if (xdg_surface->role == WLR_XDG_SURFACE_ROLE_POPUP) { |
307 | wlr_log(L_DEBUG, "New xdg_shell popup"); | 404 | wlr_log(WLR_DEBUG, "New xdg_shell popup"); |
308 | return; | 405 | return; |
309 | } | 406 | } |
310 | 407 | ||
311 | wlr_log(L_DEBUG, "New xdg_shell toplevel title='%s' app_id='%s'", | 408 | wlr_log(WLR_DEBUG, "New xdg_shell toplevel title='%s' app_id='%s'", |
312 | xdg_surface->toplevel->title, xdg_surface->toplevel->app_id); | 409 | xdg_surface->toplevel->title, xdg_surface->toplevel->app_id); |
313 | wlr_xdg_surface_ping(xdg_surface); | 410 | wlr_xdg_surface_ping(xdg_surface); |
314 | 411 | ||
diff --git a/sway/desktop/xdg_shell_v6.c b/sway/desktop/xdg_shell_v6.c index b28c4b9c..46fd4769 100644 --- a/sway/desktop/xdg_shell_v6.c +++ b/sway/desktop/xdg_shell_v6.c | |||
@@ -1,4 +1,5 @@ | |||
1 | #define _POSIX_C_SOURCE 199309L | 1 | #define _POSIX_C_SOURCE 199309L |
2 | #include <float.h> | ||
2 | #include <stdbool.h> | 3 | #include <stdbool.h> |
3 | #include <stdlib.h> | 4 | #include <stdlib.h> |
4 | #include <wayland-server.h> | 5 | #include <wayland-server.h> |
@@ -44,6 +45,24 @@ static void popup_handle_destroy(struct wl_listener *listener, void *data) { | |||
44 | view_child_destroy(&popup->child); | 45 | view_child_destroy(&popup->child); |
45 | } | 46 | } |
46 | 47 | ||
48 | static void popup_unconstrain(struct sway_xdg_popup_v6 *popup) { | ||
49 | struct sway_view *view = popup->child.view; | ||
50 | struct wlr_xdg_popup_v6 *wlr_popup = popup->wlr_xdg_surface_v6->popup; | ||
51 | |||
52 | struct sway_container *output = container_parent(view->swayc, C_OUTPUT); | ||
53 | |||
54 | // the output box expressed in the coordinate system of the toplevel parent | ||
55 | // of the popup | ||
56 | struct wlr_box output_toplevel_sx_box = { | ||
57 | .x = output->x - view->x, | ||
58 | .y = output->y - view->y, | ||
59 | .width = output->width, | ||
60 | .height = output->height, | ||
61 | }; | ||
62 | |||
63 | wlr_xdg_popup_v6_unconstrain_from_box(wlr_popup, &output_toplevel_sx_box); | ||
64 | } | ||
65 | |||
47 | static struct sway_xdg_popup_v6 *popup_create( | 66 | static struct sway_xdg_popup_v6 *popup_create( |
48 | struct wlr_xdg_popup_v6 *wlr_popup, struct sway_view *view) { | 67 | struct wlr_xdg_popup_v6 *wlr_popup, struct sway_view *view) { |
49 | struct wlr_xdg_surface_v6 *xdg_surface = wlr_popup->base; | 68 | struct wlr_xdg_surface_v6 *xdg_surface = wlr_popup->base; |
@@ -54,12 +73,15 @@ static struct sway_xdg_popup_v6 *popup_create( | |||
54 | return NULL; | 73 | return NULL; |
55 | } | 74 | } |
56 | view_child_init(&popup->child, &popup_impl, view, xdg_surface->surface); | 75 | view_child_init(&popup->child, &popup_impl, view, xdg_surface->surface); |
76 | popup->wlr_xdg_surface_v6 = xdg_surface; | ||
57 | 77 | ||
58 | wl_signal_add(&xdg_surface->events.new_popup, &popup->new_popup); | 78 | wl_signal_add(&xdg_surface->events.new_popup, &popup->new_popup); |
59 | popup->new_popup.notify = popup_handle_new_popup; | 79 | popup->new_popup.notify = popup_handle_new_popup; |
60 | wl_signal_add(&xdg_surface->events.destroy, &popup->destroy); | 80 | wl_signal_add(&xdg_surface->events.destroy, &popup->destroy); |
61 | popup->destroy.notify = popup_handle_destroy; | 81 | popup->destroy.notify = popup_handle_destroy; |
62 | 82 | ||
83 | popup_unconstrain(popup); | ||
84 | |||
63 | return popup; | 85 | return popup; |
64 | } | 86 | } |
65 | 87 | ||
@@ -73,6 +95,16 @@ static struct sway_xdg_shell_v6_view *xdg_shell_v6_view_from_view( | |||
73 | return (struct sway_xdg_shell_v6_view *)view; | 95 | return (struct sway_xdg_shell_v6_view *)view; |
74 | } | 96 | } |
75 | 97 | ||
98 | static void get_constraints(struct sway_view *view, double *min_width, | ||
99 | double *max_width, double *min_height, double *max_height) { | ||
100 | struct wlr_xdg_toplevel_v6_state *state = | ||
101 | &view->wlr_xdg_surface_v6->toplevel->current; | ||
102 | *min_width = state->min_width > 0 ? state->min_width : DBL_MIN; | ||
103 | *max_width = state->max_width > 0 ? state->max_width : DBL_MAX; | ||
104 | *min_height = state->min_height > 0 ? state->min_height : DBL_MIN; | ||
105 | *max_height = state->max_height > 0 ? state->max_height : DBL_MAX; | ||
106 | } | ||
107 | |||
76 | static const char *get_string_prop(struct sway_view *view, enum sway_view_prop prop) { | 108 | static const char *get_string_prop(struct sway_view *view, enum sway_view_prop prop) { |
77 | if (xdg_shell_v6_view_from_view(view) == NULL) { | 109 | if (xdg_shell_v6_view_from_view(view) == NULL) { |
78 | return NULL; | 110 | return NULL; |
@@ -163,6 +195,7 @@ static void destroy(struct sway_view *view) { | |||
163 | } | 195 | } |
164 | 196 | ||
165 | static const struct sway_view_impl view_impl = { | 197 | static const struct sway_view_impl view_impl = { |
198 | .get_constraints = get_constraints, | ||
166 | .get_string_prop = get_string_prop, | 199 | .get_string_prop = get_string_prop, |
167 | .configure = configure, | 200 | .configure = configure, |
168 | .set_activated = set_activated, | 201 | .set_activated = set_activated, |
@@ -187,10 +220,24 @@ static void handle_commit(struct wl_listener *listener, void *data) { | |||
187 | transaction_notify_view_ready(view, xdg_surface_v6->configure_serial); | 220 | transaction_notify_view_ready(view, xdg_surface_v6->configure_serial); |
188 | } | 221 | } |
189 | 222 | ||
190 | view_update_title(view, false); | ||
191 | view_damage_from(view); | 223 | view_damage_from(view); |
192 | } | 224 | } |
193 | 225 | ||
226 | static void handle_set_title(struct wl_listener *listener, void *data) { | ||
227 | struct sway_xdg_shell_v6_view *xdg_shell_v6_view = | ||
228 | wl_container_of(listener, xdg_shell_v6_view, set_title); | ||
229 | struct sway_view *view = &xdg_shell_v6_view->view; | ||
230 | view_update_title(view, false); | ||
231 | view_execute_criteria(view); | ||
232 | } | ||
233 | |||
234 | static void handle_set_app_id(struct wl_listener *listener, void *data) { | ||
235 | struct sway_xdg_shell_v6_view *xdg_shell_v6_view = | ||
236 | wl_container_of(listener, xdg_shell_v6_view, set_app_id); | ||
237 | struct sway_view *view = &xdg_shell_v6_view->view; | ||
238 | view_execute_criteria(view); | ||
239 | } | ||
240 | |||
194 | static void handle_new_popup(struct wl_listener *listener, void *data) { | 241 | static void handle_new_popup(struct wl_listener *listener, void *data) { |
195 | struct sway_xdg_shell_v6_view *xdg_shell_v6_view = | 242 | struct sway_xdg_shell_v6_view *xdg_shell_v6_view = |
196 | wl_container_of(listener, xdg_shell_v6_view, new_popup); | 243 | wl_container_of(listener, xdg_shell_v6_view, new_popup); |
@@ -217,8 +264,37 @@ static void handle_request_fullscreen(struct wl_listener *listener, void *data) | |||
217 | 264 | ||
218 | view_set_fullscreen(view, e->fullscreen); | 265 | view_set_fullscreen(view, e->fullscreen); |
219 | 266 | ||
220 | struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); | 267 | struct sway_container *output = container_parent(view->swayc, C_OUTPUT); |
221 | arrange_and_commit(ws); | 268 | arrange_windows(output); |
269 | transaction_commit_dirty(); | ||
270 | } | ||
271 | |||
272 | static void handle_request_move(struct wl_listener *listener, void *data) { | ||
273 | struct sway_xdg_shell_v6_view *xdg_shell_v6_view = | ||
274 | wl_container_of(listener, xdg_shell_v6_view, request_move); | ||
275 | struct sway_view *view = &xdg_shell_v6_view->view; | ||
276 | if (!container_is_floating(view->swayc)) { | ||
277 | return; | ||
278 | } | ||
279 | struct wlr_xdg_toplevel_v6_move_event *e = data; | ||
280 | struct sway_seat *seat = e->seat->seat->data; | ||
281 | if (e->serial == seat->last_button_serial) { | ||
282 | seat_begin_move(seat, view->swayc, seat->last_button); | ||
283 | } | ||
284 | } | ||
285 | |||
286 | static void handle_request_resize(struct wl_listener *listener, void *data) { | ||
287 | struct sway_xdg_shell_v6_view *xdg_shell_v6_view = | ||
288 | wl_container_of(listener, xdg_shell_v6_view, request_resize); | ||
289 | struct sway_view *view = &xdg_shell_v6_view->view; | ||
290 | if (!container_is_floating(view->swayc)) { | ||
291 | return; | ||
292 | } | ||
293 | struct wlr_xdg_toplevel_v6_resize_event *e = data; | ||
294 | struct sway_seat *seat = e->seat->seat->data; | ||
295 | if (e->serial == seat->last_button_serial) { | ||
296 | seat_begin_resize(seat, view->swayc, seat->last_button, e->edges); | ||
297 | } | ||
222 | } | 298 | } |
223 | 299 | ||
224 | static void handle_unmap(struct wl_listener *listener, void *data) { | 300 | static void handle_unmap(struct wl_listener *listener, void *data) { |
@@ -235,6 +311,10 @@ static void handle_unmap(struct wl_listener *listener, void *data) { | |||
235 | wl_list_remove(&xdg_shell_v6_view->commit.link); | 311 | wl_list_remove(&xdg_shell_v6_view->commit.link); |
236 | wl_list_remove(&xdg_shell_v6_view->new_popup.link); | 312 | wl_list_remove(&xdg_shell_v6_view->new_popup.link); |
237 | wl_list_remove(&xdg_shell_v6_view->request_fullscreen.link); | 313 | wl_list_remove(&xdg_shell_v6_view->request_fullscreen.link); |
314 | wl_list_remove(&xdg_shell_v6_view->request_move.link); | ||
315 | wl_list_remove(&xdg_shell_v6_view->request_resize.link); | ||
316 | wl_list_remove(&xdg_shell_v6_view->set_title.link); | ||
317 | wl_list_remove(&xdg_shell_v6_view->set_app_id.link); | ||
238 | } | 318 | } |
239 | 319 | ||
240 | static void handle_map(struct wl_listener *listener, void *data) { | 320 | static void handle_map(struct wl_listener *listener, void *data) { |
@@ -246,8 +326,8 @@ static void handle_map(struct wl_listener *listener, void *data) { | |||
246 | view->natural_width = view->wlr_xdg_surface_v6->geometry.width; | 326 | view->natural_width = view->wlr_xdg_surface_v6->geometry.width; |
247 | view->natural_height = view->wlr_xdg_surface_v6->geometry.height; | 327 | view->natural_height = view->wlr_xdg_surface_v6->geometry.height; |
248 | if (!view->natural_width && !view->natural_height) { | 328 | if (!view->natural_width && !view->natural_height) { |
249 | view->natural_width = view->wlr_xdg_surface_v6->surface->current->width; | 329 | view->natural_width = view->wlr_xdg_surface_v6->surface->current.width; |
250 | view->natural_height = view->wlr_xdg_surface_v6->surface->current->height; | 330 | view->natural_height = view->wlr_xdg_surface_v6->surface->current.height; |
251 | } | 331 | } |
252 | 332 | ||
253 | view_map(view, view->wlr_xdg_surface_v6->surface); | 333 | view_map(view, view->wlr_xdg_surface_v6->surface); |
@@ -255,10 +335,11 @@ static void handle_map(struct wl_listener *listener, void *data) { | |||
255 | if (xdg_surface->toplevel->client_pending.fullscreen) { | 335 | if (xdg_surface->toplevel->client_pending.fullscreen) { |
256 | view_set_fullscreen(view, true); | 336 | view_set_fullscreen(view, true); |
257 | struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); | 337 | struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); |
258 | arrange_and_commit(ws); | 338 | arrange_windows(ws); |
259 | } else { | 339 | } else { |
260 | arrange_and_commit(view->swayc->parent); | 340 | arrange_windows(view->swayc->parent); |
261 | } | 341 | } |
342 | transaction_commit_dirty(); | ||
262 | 343 | ||
263 | xdg_shell_v6_view->commit.notify = handle_commit; | 344 | xdg_shell_v6_view->commit.notify = handle_commit; |
264 | wl_signal_add(&xdg_surface->surface->events.commit, | 345 | wl_signal_add(&xdg_surface->surface->events.commit, |
@@ -271,6 +352,22 @@ static void handle_map(struct wl_listener *listener, void *data) { | |||
271 | xdg_shell_v6_view->request_fullscreen.notify = handle_request_fullscreen; | 352 | xdg_shell_v6_view->request_fullscreen.notify = handle_request_fullscreen; |
272 | wl_signal_add(&xdg_surface->toplevel->events.request_fullscreen, | 353 | wl_signal_add(&xdg_surface->toplevel->events.request_fullscreen, |
273 | &xdg_shell_v6_view->request_fullscreen); | 354 | &xdg_shell_v6_view->request_fullscreen); |
355 | |||
356 | xdg_shell_v6_view->request_move.notify = handle_request_move; | ||
357 | wl_signal_add(&xdg_surface->toplevel->events.request_move, | ||
358 | &xdg_shell_v6_view->request_move); | ||
359 | |||
360 | xdg_shell_v6_view->request_resize.notify = handle_request_resize; | ||
361 | wl_signal_add(&xdg_surface->toplevel->events.request_resize, | ||
362 | &xdg_shell_v6_view->request_resize); | ||
363 | |||
364 | xdg_shell_v6_view->set_title.notify = handle_set_title; | ||
365 | wl_signal_add(&xdg_surface->toplevel->events.set_title, | ||
366 | &xdg_shell_v6_view->set_title); | ||
367 | |||
368 | xdg_shell_v6_view->set_app_id.notify = handle_set_app_id; | ||
369 | wl_signal_add(&xdg_surface->toplevel->events.set_app_id, | ||
370 | &xdg_shell_v6_view->set_app_id); | ||
274 | } | 371 | } |
275 | 372 | ||
276 | static void handle_destroy(struct wl_listener *listener, void *data) { | 373 | static void handle_destroy(struct wl_listener *listener, void *data) { |
@@ -295,11 +392,11 @@ void handle_xdg_shell_v6_surface(struct wl_listener *listener, void *data) { | |||
295 | struct wlr_xdg_surface_v6 *xdg_surface = data; | 392 | struct wlr_xdg_surface_v6 *xdg_surface = data; |
296 | 393 | ||
297 | if (xdg_surface->role == WLR_XDG_SURFACE_V6_ROLE_POPUP) { | 394 | if (xdg_surface->role == WLR_XDG_SURFACE_V6_ROLE_POPUP) { |
298 | wlr_log(L_DEBUG, "New xdg_shell_v6 popup"); | 395 | wlr_log(WLR_DEBUG, "New xdg_shell_v6 popup"); |
299 | return; | 396 | return; |
300 | } | 397 | } |
301 | 398 | ||
302 | wlr_log(L_DEBUG, "New xdg_shell_v6 toplevel title='%s' app_id='%s'", | 399 | wlr_log(WLR_DEBUG, "New xdg_shell_v6 toplevel title='%s' app_id='%s'", |
303 | xdg_surface->toplevel->title, xdg_surface->toplevel->app_id); | 400 | xdg_surface->toplevel->title, xdg_surface->toplevel->app_id); |
304 | wlr_xdg_surface_v6_ping(xdg_surface); | 401 | wlr_xdg_surface_v6_ping(xdg_surface); |
305 | 402 | ||
diff --git a/sway/desktop/xwayland.c b/sway/desktop/xwayland.c index b3b1473d..65d4fcd4 100644 --- a/sway/desktop/xwayland.c +++ b/sway/desktop/xwayland.c | |||
@@ -69,16 +69,11 @@ static void unmanaged_handle_map(struct wl_listener *listener, void *data) { | |||
69 | surface->ly = xsurface->y; | 69 | surface->ly = xsurface->y; |
70 | desktop_damage_surface(xsurface->surface, surface->lx, surface->ly, true); | 70 | desktop_damage_surface(xsurface->surface, surface->lx, surface->ly, true); |
71 | 71 | ||
72 | if (!wlr_xwayland_surface_is_unmanaged(xsurface)) { | 72 | struct sway_seat *seat = input_manager_current_seat(input_manager); |
73 | struct sway_seat *seat = input_manager_current_seat(input_manager); | 73 | struct wlr_xwayland *xwayland = |
74 | struct wlr_xwayland *xwayland = | 74 | seat->input->server->xwayland.wlr_xwayland; |
75 | seat->input->server->xwayland.wlr_xwayland; | 75 | wlr_xwayland_set_seat(xwayland, seat->wlr_seat); |
76 | wlr_xwayland_set_seat(xwayland, seat->wlr_seat); | 76 | seat_set_focus_surface(seat, xsurface->surface, false); |
77 | seat_set_focus_surface(seat, xsurface->surface); | ||
78 | } | ||
79 | |||
80 | // TODO: we don't send surface enter/leave events to xwayland unmanaged | ||
81 | // surfaces, but xwayland doesn't support HiDPI anyway | ||
82 | } | 77 | } |
83 | 78 | ||
84 | static void unmanaged_handle_unmap(struct wl_listener *listener, void *data) { | 79 | static void unmanaged_handle_unmap(struct wl_listener *listener, void *data) { |
@@ -89,18 +84,16 @@ static void unmanaged_handle_unmap(struct wl_listener *listener, void *data) { | |||
89 | wl_list_remove(&surface->link); | 84 | wl_list_remove(&surface->link); |
90 | wl_list_remove(&surface->commit.link); | 85 | wl_list_remove(&surface->commit.link); |
91 | 86 | ||
92 | if (!wlr_xwayland_surface_is_unmanaged(xsurface)) { | 87 | struct sway_seat *seat = input_manager_current_seat(input_manager); |
93 | struct sway_seat *seat = input_manager_current_seat(input_manager); | 88 | if (seat->wlr_seat->keyboard_state.focused_surface == |
94 | if (seat->wlr_seat->keyboard_state.focused_surface == | 89 | xsurface->surface) { |
95 | xsurface->surface) { | 90 | // Restore focus |
96 | // Restore focus | 91 | struct sway_container *previous = |
97 | struct sway_container *previous = | 92 | seat_get_focus_inactive(seat, &root_container); |
98 | seat_get_focus_inactive(seat, &root_container); | 93 | if (previous) { |
99 | if (previous) { | 94 | // Hack to get seat to re-focus the return value of get_focus |
100 | // Hack to get seat to re-focus the return value of get_focus | 95 | seat_set_focus(seat, previous->parent); |
101 | seat_set_focus(seat, previous->parent); | 96 | seat_set_focus(seat, previous); |
102 | seat_set_focus(seat, previous); | ||
103 | } | ||
104 | } | 97 | } |
105 | } | 98 | } |
106 | } | 99 | } |
@@ -119,7 +112,7 @@ static struct sway_xwayland_unmanaged *create_unmanaged( | |||
119 | struct sway_xwayland_unmanaged *surface = | 112 | struct sway_xwayland_unmanaged *surface = |
120 | calloc(1, sizeof(struct sway_xwayland_unmanaged)); | 113 | calloc(1, sizeof(struct sway_xwayland_unmanaged)); |
121 | if (surface == NULL) { | 114 | if (surface == NULL) { |
122 | wlr_log(L_ERROR, "Allocation failed"); | 115 | wlr_log(WLR_ERROR, "Allocation failed"); |
123 | return NULL; | 116 | return NULL; |
124 | } | 117 | } |
125 | 118 | ||
@@ -246,6 +239,14 @@ static bool wants_floating(struct sway_view *view) { | |||
246 | return false; | 239 | return false; |
247 | } | 240 | } |
248 | 241 | ||
242 | static bool has_client_side_decorations(struct sway_view *view) { | ||
243 | if (xwayland_view_from_view(view) == NULL) { | ||
244 | return false; | ||
245 | } | ||
246 | struct wlr_xwayland_surface *surface = view->wlr_xwayland_surface; | ||
247 | return surface->decorations != WLR_XWAYLAND_SURFACE_DECORATIONS_ALL; | ||
248 | } | ||
249 | |||
249 | static void _close(struct sway_view *view) { | 250 | static void _close(struct sway_view *view) { |
250 | if (xwayland_view_from_view(view) == NULL) { | 251 | if (xwayland_view_from_view(view) == NULL) { |
251 | return; | 252 | return; |
@@ -269,6 +270,7 @@ static const struct sway_view_impl view_impl = { | |||
269 | .set_tiled = set_tiled, | 270 | .set_tiled = set_tiled, |
270 | .set_fullscreen = set_fullscreen, | 271 | .set_fullscreen = set_fullscreen, |
271 | .wants_floating = wants_floating, | 272 | .wants_floating = wants_floating, |
273 | .has_client_side_decorations = has_client_side_decorations, | ||
272 | .close = _close, | 274 | .close = _close, |
273 | .destroy = destroy, | 275 | .destroy = destroy, |
274 | }; | 276 | }; |
@@ -278,15 +280,42 @@ static void handle_commit(struct wl_listener *listener, void *data) { | |||
278 | wl_container_of(listener, xwayland_view, commit); | 280 | wl_container_of(listener, xwayland_view, commit); |
279 | struct sway_view *view = &xwayland_view->view; | 281 | struct sway_view *view = &xwayland_view->view; |
280 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; | 282 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; |
281 | struct wlr_surface_state *surface_state = xsurface->surface->current; | 283 | struct wlr_surface_state *surface_state = &xsurface->surface->current; |
282 | 284 | ||
283 | if (view->swayc->instructions->length) { | 285 | if (view->swayc->instructions->length) { |
284 | transaction_notify_view_ready_by_size(view, | 286 | transaction_notify_view_ready_by_size(view, |
285 | surface_state->width, surface_state->height); | 287 | surface_state->width, surface_state->height); |
288 | } else if (container_is_floating(view->swayc)) { | ||
289 | view_update_size(view, surface_state->width, surface_state->height); | ||
286 | } | 290 | } |
291 | |||
287 | view_damage_from(view); | 292 | view_damage_from(view); |
288 | } | 293 | } |
289 | 294 | ||
295 | static void handle_destroy(struct wl_listener *listener, void *data) { | ||
296 | struct sway_xwayland_view *xwayland_view = | ||
297 | wl_container_of(listener, xwayland_view, destroy); | ||
298 | struct sway_view *view = &xwayland_view->view; | ||
299 | |||
300 | if (view->surface) { | ||
301 | view_unmap(view); | ||
302 | wl_list_remove(&xwayland_view->commit.link); | ||
303 | } | ||
304 | |||
305 | wl_list_remove(&xwayland_view->destroy.link); | ||
306 | wl_list_remove(&xwayland_view->request_configure.link); | ||
307 | wl_list_remove(&xwayland_view->request_fullscreen.link); | ||
308 | wl_list_remove(&xwayland_view->request_move.link); | ||
309 | wl_list_remove(&xwayland_view->request_resize.link); | ||
310 | wl_list_remove(&xwayland_view->set_title.link); | ||
311 | wl_list_remove(&xwayland_view->set_class.link); | ||
312 | wl_list_remove(&xwayland_view->set_window_type.link); | ||
313 | wl_list_remove(&xwayland_view->set_hints.link); | ||
314 | wl_list_remove(&xwayland_view->map.link); | ||
315 | wl_list_remove(&xwayland_view->unmap.link); | ||
316 | view_destroy(&xwayland_view->view); | ||
317 | } | ||
318 | |||
290 | static void handle_unmap(struct wl_listener *listener, void *data) { | 319 | static void handle_unmap(struct wl_listener *listener, void *data) { |
291 | struct sway_xwayland_view *xwayland_view = | 320 | struct sway_xwayland_view *xwayland_view = |
292 | wl_container_of(listener, xwayland_view, unmap); | 321 | wl_container_of(listener, xwayland_view, unmap); |
@@ -307,6 +336,15 @@ static void handle_map(struct wl_listener *listener, void *data) { | |||
307 | struct wlr_xwayland_surface *xsurface = data; | 336 | struct wlr_xwayland_surface *xsurface = data; |
308 | struct sway_view *view = &xwayland_view->view; | 337 | struct sway_view *view = &xwayland_view->view; |
309 | 338 | ||
339 | if (xsurface->override_redirect) { | ||
340 | // This window used not to have the override redirect flag and has it | ||
341 | // now. Switch to unmanaged. | ||
342 | handle_destroy(&xwayland_view->destroy, view); | ||
343 | struct sway_xwayland_unmanaged *unmanaged = create_unmanaged(xsurface); | ||
344 | unmanaged_handle_map(&unmanaged->map, xsurface); | ||
345 | return; | ||
346 | } | ||
347 | |||
310 | view->natural_width = xsurface->width; | 348 | view->natural_width = xsurface->width; |
311 | view->natural_height = xsurface->height; | 349 | view->natural_height = xsurface->height; |
312 | 350 | ||
@@ -321,31 +359,11 @@ static void handle_map(struct wl_listener *listener, void *data) { | |||
321 | if (xsurface->fullscreen) { | 359 | if (xsurface->fullscreen) { |
322 | view_set_fullscreen(view, true); | 360 | view_set_fullscreen(view, true); |
323 | struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); | 361 | struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); |
324 | arrange_and_commit(ws); | 362 | arrange_windows(ws); |
325 | } else { | 363 | } else { |
326 | arrange_and_commit(view->swayc->parent); | 364 | arrange_windows(view->swayc->parent); |
327 | } | ||
328 | } | ||
329 | |||
330 | static void handle_destroy(struct wl_listener *listener, void *data) { | ||
331 | struct sway_xwayland_view *xwayland_view = | ||
332 | wl_container_of(listener, xwayland_view, destroy); | ||
333 | struct sway_view *view = &xwayland_view->view; | ||
334 | |||
335 | if (view->surface) { | ||
336 | view_unmap(view); | ||
337 | wl_list_remove(&xwayland_view->commit.link); | ||
338 | } | 365 | } |
339 | 366 | transaction_commit_dirty(); | |
340 | wl_list_remove(&xwayland_view->destroy.link); | ||
341 | wl_list_remove(&xwayland_view->request_configure.link); | ||
342 | wl_list_remove(&xwayland_view->request_fullscreen.link); | ||
343 | wl_list_remove(&xwayland_view->set_title.link); | ||
344 | wl_list_remove(&xwayland_view->set_class.link); | ||
345 | wl_list_remove(&xwayland_view->set_window_type.link); | ||
346 | wl_list_remove(&xwayland_view->map.link); | ||
347 | wl_list_remove(&xwayland_view->unmap.link); | ||
348 | view_destroy(&xwayland_view->view); | ||
349 | } | 367 | } |
350 | 368 | ||
351 | static void handle_request_configure(struct wl_listener *listener, void *data) { | 369 | static void handle_request_configure(struct wl_listener *listener, void *data) { |
@@ -379,8 +397,40 @@ static void handle_request_fullscreen(struct wl_listener *listener, void *data) | |||
379 | } | 397 | } |
380 | view_set_fullscreen(view, xsurface->fullscreen); | 398 | view_set_fullscreen(view, xsurface->fullscreen); |
381 | 399 | ||
382 | struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); | 400 | struct sway_container *output = container_parent(view->swayc, C_OUTPUT); |
383 | arrange_and_commit(ws); | 401 | arrange_windows(output); |
402 | transaction_commit_dirty(); | ||
403 | } | ||
404 | |||
405 | static void handle_request_move(struct wl_listener *listener, void *data) { | ||
406 | struct sway_xwayland_view *xwayland_view = | ||
407 | wl_container_of(listener, xwayland_view, request_move); | ||
408 | struct sway_view *view = &xwayland_view->view; | ||
409 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; | ||
410 | if (!xsurface->mapped) { | ||
411 | return; | ||
412 | } | ||
413 | if (!container_is_floating(view->swayc)) { | ||
414 | return; | ||
415 | } | ||
416 | struct sway_seat *seat = input_manager_current_seat(input_manager); | ||
417 | seat_begin_move(seat, view->swayc, seat->last_button); | ||
418 | } | ||
419 | |||
420 | static void handle_request_resize(struct wl_listener *listener, void *data) { | ||
421 | struct sway_xwayland_view *xwayland_view = | ||
422 | wl_container_of(listener, xwayland_view, request_resize); | ||
423 | struct sway_view *view = &xwayland_view->view; | ||
424 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; | ||
425 | if (!xsurface->mapped) { | ||
426 | return; | ||
427 | } | ||
428 | if (!container_is_floating(view->swayc)) { | ||
429 | return; | ||
430 | } | ||
431 | struct wlr_xwayland_resize_event *e = data; | ||
432 | struct sway_seat *seat = input_manager_current_seat(input_manager); | ||
433 | seat_begin_resize(seat, view->swayc, seat->last_button, e->edges); | ||
384 | } | 434 | } |
385 | 435 | ||
386 | static void handle_set_title(struct wl_listener *listener, void *data) { | 436 | static void handle_set_title(struct wl_listener *listener, void *data) { |
@@ -417,6 +467,25 @@ static void handle_set_window_type(struct wl_listener *listener, void *data) { | |||
417 | view_execute_criteria(view); | 467 | view_execute_criteria(view); |
418 | } | 468 | } |
419 | 469 | ||
470 | static void handle_set_hints(struct wl_listener *listener, void *data) { | ||
471 | struct sway_xwayland_view *xwayland_view = | ||
472 | wl_container_of(listener, xwayland_view, set_hints); | ||
473 | struct sway_view *view = &xwayland_view->view; | ||
474 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; | ||
475 | if (!xsurface->mapped) { | ||
476 | return; | ||
477 | } | ||
478 | if (!xsurface->hints_urgency && view->urgent_timer) { | ||
479 | // The view is is in the timeout period. We'll ignore the request to | ||
480 | // unset urgency so that the view remains urgent until the timer clears | ||
481 | // it. | ||
482 | return; | ||
483 | } | ||
484 | if (view->allow_request_urgent) { | ||
485 | view_set_urgent(view, (bool)xsurface->hints_urgency); | ||
486 | } | ||
487 | } | ||
488 | |||
420 | struct sway_view *view_from_wlr_xwayland_surface( | 489 | struct sway_view *view_from_wlr_xwayland_surface( |
421 | struct wlr_xwayland_surface *xsurface) { | 490 | struct wlr_xwayland_surface *xsurface) { |
422 | return xsurface->data; | 491 | return xsurface->data; |
@@ -427,14 +496,13 @@ void handle_xwayland_surface(struct wl_listener *listener, void *data) { | |||
427 | xwayland_surface); | 496 | xwayland_surface); |
428 | struct wlr_xwayland_surface *xsurface = data; | 497 | struct wlr_xwayland_surface *xsurface = data; |
429 | 498 | ||
430 | if (wlr_xwayland_surface_is_unmanaged(xsurface) || | 499 | if (xsurface->override_redirect) { |
431 | xsurface->override_redirect) { | 500 | wlr_log(WLR_DEBUG, "New xwayland unmanaged surface"); |
432 | wlr_log(L_DEBUG, "New xwayland unmanaged surface"); | ||
433 | create_unmanaged(xsurface); | 501 | create_unmanaged(xsurface); |
434 | return; | 502 | return; |
435 | } | 503 | } |
436 | 504 | ||
437 | wlr_log(L_DEBUG, "New xwayland surface title='%s' class='%s'", | 505 | wlr_log(WLR_DEBUG, "New xwayland surface title='%s' class='%s'", |
438 | xsurface->title, xsurface->class); | 506 | xsurface->title, xsurface->class); |
439 | 507 | ||
440 | struct sway_xwayland_view *xwayland_view = | 508 | struct sway_xwayland_view *xwayland_view = |
@@ -457,6 +525,14 @@ void handle_xwayland_surface(struct wl_listener *listener, void *data) { | |||
457 | &xwayland_view->request_fullscreen); | 525 | &xwayland_view->request_fullscreen); |
458 | xwayland_view->request_fullscreen.notify = handle_request_fullscreen; | 526 | xwayland_view->request_fullscreen.notify = handle_request_fullscreen; |
459 | 527 | ||
528 | wl_signal_add(&xsurface->events.request_move, | ||
529 | &xwayland_view->request_move); | ||
530 | xwayland_view->request_move.notify = handle_request_move; | ||
531 | |||
532 | wl_signal_add(&xsurface->events.request_resize, | ||
533 | &xwayland_view->request_resize); | ||
534 | xwayland_view->request_resize.notify = handle_request_resize; | ||
535 | |||
460 | wl_signal_add(&xsurface->events.set_title, &xwayland_view->set_title); | 536 | wl_signal_add(&xsurface->events.set_title, &xwayland_view->set_title); |
461 | xwayland_view->set_title.notify = handle_set_title; | 537 | xwayland_view->set_title.notify = handle_set_title; |
462 | 538 | ||
@@ -467,6 +543,9 @@ void handle_xwayland_surface(struct wl_listener *listener, void *data) { | |||
467 | &xwayland_view->set_window_type); | 543 | &xwayland_view->set_window_type); |
468 | xwayland_view->set_window_type.notify = handle_set_window_type; | 544 | xwayland_view->set_window_type.notify = handle_set_window_type; |
469 | 545 | ||
546 | wl_signal_add(&xsurface->events.set_hints, &xwayland_view->set_hints); | ||
547 | xwayland_view->set_hints.notify = handle_set_hints; | ||
548 | |||
470 | wl_signal_add(&xsurface->events.unmap, &xwayland_view->unmap); | 549 | wl_signal_add(&xsurface->events.unmap, &xwayland_view->unmap); |
471 | xwayland_view->unmap.notify = handle_unmap; | 550 | xwayland_view->unmap.notify = handle_unmap; |
472 | 551 | ||
@@ -484,7 +563,7 @@ void handle_xwayland_ready(struct wl_listener *listener, void *data) { | |||
484 | xcb_connection_t *xcb_conn = xcb_connect(NULL, NULL); | 563 | xcb_connection_t *xcb_conn = xcb_connect(NULL, NULL); |
485 | int err = xcb_connection_has_error(xcb_conn); | 564 | int err = xcb_connection_has_error(xcb_conn); |
486 | if (err) { | 565 | if (err) { |
487 | wlr_log(L_ERROR, "XCB connect failed: %d", err); | 566 | wlr_log(WLR_ERROR, "XCB connect failed: %d", err); |
488 | return; | 567 | return; |
489 | } | 568 | } |
490 | 569 | ||
@@ -503,7 +582,7 @@ void handle_xwayland_ready(struct wl_listener *listener, void *data) { | |||
503 | free(reply); | 582 | free(reply); |
504 | 583 | ||
505 | if (error != NULL) { | 584 | if (error != NULL) { |
506 | wlr_log(L_ERROR, "could not resolve atom %s, X11 error code %d", | 585 | wlr_log(WLR_ERROR, "could not resolve atom %s, X11 error code %d", |
507 | atom_map[i], error->error_code); | 586 | atom_map[i], error->error_code); |
508 | free(error); | 587 | free(error); |
509 | break; | 588 | break; |
diff --git a/sway/input/cursor.c b/sway/input/cursor.c index 944e35aa..65d04cac 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c | |||
@@ -5,14 +5,19 @@ | |||
5 | #elif __FreeBSD__ | 5 | #elif __FreeBSD__ |
6 | #include <dev/evdev/input-event-codes.h> | 6 | #include <dev/evdev/input-event-codes.h> |
7 | #endif | 7 | #endif |
8 | #include <limits.h> | ||
8 | #include <wlr/types/wlr_cursor.h> | 9 | #include <wlr/types/wlr_cursor.h> |
9 | #include <wlr/types/wlr_xcursor_manager.h> | 10 | #include <wlr/types/wlr_xcursor_manager.h> |
10 | #include <wlr/types/wlr_idle.h> | 11 | #include <wlr/types/wlr_idle.h> |
11 | #include "list.h" | 12 | #include "list.h" |
12 | #include "log.h" | 13 | #include "log.h" |
14 | #include "sway/desktop.h" | ||
15 | #include "sway/desktop/transaction.h" | ||
13 | #include "sway/input/cursor.h" | 16 | #include "sway/input/cursor.h" |
17 | #include "sway/input/keyboard.h" | ||
14 | #include "sway/layers.h" | 18 | #include "sway/layers.h" |
15 | #include "sway/output.h" | 19 | #include "sway/output.h" |
20 | #include "sway/tree/arrange.h" | ||
16 | #include "sway/tree/view.h" | 21 | #include "sway/tree/view.h" |
17 | #include "sway/tree/workspace.h" | 22 | #include "sway/tree/workspace.h" |
18 | #include "wlr-layer-shell-unstable-v1-protocol.h" | 23 | #include "wlr-layer-shell-unstable-v1-protocol.h" |
@@ -126,7 +131,7 @@ static struct sway_container *container_at_coords( | |||
126 | return ws; | 131 | return ws; |
127 | } | 132 | } |
128 | 133 | ||
129 | c = seat_get_focus_inactive(seat, output->swayc); | 134 | c = seat_get_active_child(seat, output->swayc); |
130 | if (c) { | 135 | if (c) { |
131 | return c; | 136 | return c; |
132 | } | 137 | } |
@@ -138,6 +143,171 @@ static struct sway_container *container_at_coords( | |||
138 | return output->swayc; | 143 | return output->swayc; |
139 | } | 144 | } |
140 | 145 | ||
146 | static enum wlr_edges find_resize_edge(struct sway_container *cont, | ||
147 | struct sway_cursor *cursor) { | ||
148 | if (cont->type != C_VIEW) { | ||
149 | return WLR_EDGE_NONE; | ||
150 | } | ||
151 | struct sway_view *view = cont->sway_view; | ||
152 | if (view->border == B_NONE || !view->border_thickness || view->using_csd) { | ||
153 | return WLR_EDGE_NONE; | ||
154 | } | ||
155 | |||
156 | enum wlr_edges edge = 0; | ||
157 | if (cursor->cursor->x < cont->x + view->border_thickness) { | ||
158 | edge |= WLR_EDGE_LEFT; | ||
159 | } | ||
160 | if (cursor->cursor->y < cont->y + view->border_thickness) { | ||
161 | edge |= WLR_EDGE_TOP; | ||
162 | } | ||
163 | if (cursor->cursor->x >= cont->x + cont->width - view->border_thickness) { | ||
164 | edge |= WLR_EDGE_RIGHT; | ||
165 | } | ||
166 | if (cursor->cursor->y >= cont->y + cont->height - view->border_thickness) { | ||
167 | edge |= WLR_EDGE_BOTTOM; | ||
168 | } | ||
169 | return edge; | ||
170 | } | ||
171 | |||
172 | static void handle_move_motion(struct sway_seat *seat, | ||
173 | struct sway_cursor *cursor) { | ||
174 | struct sway_container *con = seat->op_container; | ||
175 | desktop_damage_whole_container(con); | ||
176 | container_floating_translate(con, | ||
177 | cursor->cursor->x - cursor->previous.x, | ||
178 | cursor->cursor->y - cursor->previous.y); | ||
179 | desktop_damage_whole_container(con); | ||
180 | } | ||
181 | |||
182 | static void calculate_floating_constraints(struct sway_container *con, | ||
183 | int *min_width, int *max_width, int *min_height, int *max_height) { | ||
184 | if (config->floating_minimum_width == -1) { // no minimum | ||
185 | *min_width = 0; | ||
186 | } else if (config->floating_minimum_width == 0) { // automatic | ||
187 | *min_width = 75; | ||
188 | } else { | ||
189 | *min_width = config->floating_minimum_width; | ||
190 | } | ||
191 | |||
192 | if (config->floating_minimum_height == -1) { // no minimum | ||
193 | *min_height = 0; | ||
194 | } else if (config->floating_minimum_height == 0) { // automatic | ||
195 | *min_height = 50; | ||
196 | } else { | ||
197 | *min_height = config->floating_minimum_height; | ||
198 | } | ||
199 | |||
200 | if (config->floating_maximum_width == -1) { // no maximum | ||
201 | *max_width = INT_MAX; | ||
202 | } else if (config->floating_maximum_width == 0) { // automatic | ||
203 | struct sway_container *ws = container_parent(con, C_WORKSPACE); | ||
204 | *max_width = ws->width; | ||
205 | } else { | ||
206 | *max_width = config->floating_maximum_width; | ||
207 | } | ||
208 | |||
209 | if (config->floating_maximum_height == -1) { // no maximum | ||
210 | *max_height = INT_MAX; | ||
211 | } else if (config->floating_maximum_height == 0) { // automatic | ||
212 | struct sway_container *ws = container_parent(con, C_WORKSPACE); | ||
213 | *max_height = ws->height; | ||
214 | } else { | ||
215 | *max_height = config->floating_maximum_height; | ||
216 | } | ||
217 | } | ||
218 | |||
219 | static void handle_resize_motion(struct sway_seat *seat, | ||
220 | struct sway_cursor *cursor) { | ||
221 | struct sway_container *con = seat->op_container; | ||
222 | enum wlr_edges edge = seat->op_resize_edge; | ||
223 | |||
224 | // The amount the mouse has moved since the start of the resize operation | ||
225 | // Positive is down/right | ||
226 | double mouse_move_x = cursor->cursor->x - seat->op_ref_lx; | ||
227 | double mouse_move_y = cursor->cursor->y - seat->op_ref_ly; | ||
228 | |||
229 | if (edge == WLR_EDGE_TOP || edge == WLR_EDGE_BOTTOM) { | ||
230 | mouse_move_x = 0; | ||
231 | } | ||
232 | if (edge == WLR_EDGE_LEFT || edge == WLR_EDGE_RIGHT) { | ||
233 | mouse_move_y = 0; | ||
234 | } | ||
235 | |||
236 | double grow_width = edge & WLR_EDGE_LEFT ? -mouse_move_x : mouse_move_x; | ||
237 | double grow_height = edge & WLR_EDGE_TOP ? -mouse_move_y : mouse_move_y; | ||
238 | |||
239 | if (seat->op_resize_preserve_ratio) { | ||
240 | double x_multiplier = grow_width / seat->op_ref_width; | ||
241 | double y_multiplier = grow_height / seat->op_ref_height; | ||
242 | double max_multiplier = fmax(x_multiplier, y_multiplier); | ||
243 | grow_width = seat->op_ref_width * max_multiplier; | ||
244 | grow_height = seat->op_ref_height * max_multiplier; | ||
245 | } | ||
246 | |||
247 | // Determine new width/height, and accommodate for floating min/max values | ||
248 | double width = seat->op_ref_width + grow_width; | ||
249 | double height = seat->op_ref_height + grow_height; | ||
250 | int min_width, max_width, min_height, max_height; | ||
251 | calculate_floating_constraints(con, &min_width, &max_width, | ||
252 | &min_height, &max_height); | ||
253 | width = fmax(min_width, fmin(width, max_width)); | ||
254 | height = fmax(min_height, fmin(height, max_height)); | ||
255 | |||
256 | // Apply the view's min/max size | ||
257 | if (con->type == C_VIEW) { | ||
258 | double view_min_width, view_max_width, view_min_height, view_max_height; | ||
259 | view_get_constraints(con->sway_view, &view_min_width, &view_max_width, | ||
260 | &view_min_height, &view_max_height); | ||
261 | width = fmax(view_min_width, fmin(width, view_max_width)); | ||
262 | height = fmax(view_min_height, fmin(height, view_max_height)); | ||
263 | } | ||
264 | |||
265 | // Recalculate these, in case we hit a min/max limit | ||
266 | grow_width = width - seat->op_ref_width; | ||
267 | grow_height = height - seat->op_ref_height; | ||
268 | |||
269 | // Determine grow x/y values - these are relative to the container's x/y at | ||
270 | // the start of the resize operation. | ||
271 | double grow_x = 0, grow_y = 0; | ||
272 | if (edge & WLR_EDGE_LEFT) { | ||
273 | grow_x = -grow_width; | ||
274 | } else if (edge & WLR_EDGE_RIGHT) { | ||
275 | grow_x = 0; | ||
276 | } else { | ||
277 | grow_x = -grow_width / 2; | ||
278 | } | ||
279 | if (edge & WLR_EDGE_TOP) { | ||
280 | grow_y = -grow_height; | ||
281 | } else if (edge & WLR_EDGE_BOTTOM) { | ||
282 | grow_y = 0; | ||
283 | } else { | ||
284 | grow_y = -grow_height / 2; | ||
285 | } | ||
286 | |||
287 | // Determine the amounts we need to bump everything relative to the current | ||
288 | // size. | ||
289 | int relative_grow_width = width - con->width; | ||
290 | int relative_grow_height = height - con->height; | ||
291 | int relative_grow_x = (seat->op_ref_con_lx + grow_x) - con->x; | ||
292 | int relative_grow_y = (seat->op_ref_con_ly + grow_y) - con->y; | ||
293 | |||
294 | // Actually resize stuff | ||
295 | con->x += relative_grow_x; | ||
296 | con->y += relative_grow_y; | ||
297 | con->width += relative_grow_width; | ||
298 | con->height += relative_grow_height; | ||
299 | |||
300 | if (con->type == C_VIEW) { | ||
301 | struct sway_view *view = con->sway_view; | ||
302 | view->x += relative_grow_x; | ||
303 | view->y += relative_grow_y; | ||
304 | view->width += relative_grow_width; | ||
305 | view->height += relative_grow_height; | ||
306 | } | ||
307 | |||
308 | arrange_windows(con); | ||
309 | } | ||
310 | |||
141 | void cursor_send_pointer_motion(struct sway_cursor *cursor, uint32_t time_msec, | 311 | void cursor_send_pointer_motion(struct sway_cursor *cursor, uint32_t time_msec, |
142 | bool allow_refocusing) { | 312 | bool allow_refocusing) { |
143 | if (time_msec == 0) { | 313 | if (time_msec == 0) { |
@@ -145,6 +315,18 @@ void cursor_send_pointer_motion(struct sway_cursor *cursor, uint32_t time_msec, | |||
145 | } | 315 | } |
146 | 316 | ||
147 | struct sway_seat *seat = cursor->seat; | 317 | struct sway_seat *seat = cursor->seat; |
318 | |||
319 | if (seat->operation != OP_NONE) { | ||
320 | if (seat->operation == OP_MOVE) { | ||
321 | handle_move_motion(seat, cursor); | ||
322 | } else { | ||
323 | handle_resize_motion(seat, cursor); | ||
324 | } | ||
325 | cursor->previous.x = cursor->cursor->x; | ||
326 | cursor->previous.y = cursor->cursor->y; | ||
327 | return; | ||
328 | } | ||
329 | |||
148 | struct wlr_seat *wlr_seat = seat->wlr_seat; | 330 | struct wlr_seat *wlr_seat = seat->wlr_seat; |
149 | struct wlr_surface *surface = NULL; | 331 | struct wlr_surface *surface = NULL; |
150 | double sx, sy; | 332 | double sx, sy; |
@@ -193,15 +375,21 @@ void cursor_send_pointer_motion(struct sway_cursor *cursor, uint32_t time_msec, | |||
193 | } | 375 | } |
194 | } | 376 | } |
195 | 377 | ||
196 | // reset cursor if switching between clients | 378 | // Handle cursor image |
197 | struct wl_client *client = NULL; | 379 | if (surface) { |
198 | if (surface != NULL) { | 380 | // Reset cursor if switching between clients |
199 | client = wl_resource_get_client(surface->resource); | 381 | struct wl_client *client = wl_resource_get_client(surface->resource); |
200 | } | 382 | if (client != cursor->image_client) { |
201 | if (client != cursor->image_client) { | 383 | cursor_set_image(cursor, "left_ptr", client); |
202 | wlr_xcursor_manager_set_cursor_image(cursor->xcursor_manager, | 384 | } |
203 | "left_ptr", cursor->cursor); | 385 | } else if (c && container_is_floating(c)) { |
204 | cursor->image_client = client; | 386 | // Try a floating container's resize edge |
387 | enum wlr_edges edge = find_resize_edge(c, cursor); | ||
388 | const char *image = edge == WLR_EDGE_NONE ? | ||
389 | "left_ptr" : wlr_xcursor_get_resize_name(edge); | ||
390 | cursor_set_image(cursor, image, NULL); | ||
391 | } else { | ||
392 | cursor_set_image(cursor, "left_ptr", NULL); | ||
205 | } | 393 | } |
206 | 394 | ||
207 | // send pointer enter/leave | 395 | // send pointer enter/leave |
@@ -228,6 +416,7 @@ static void handle_cursor_motion(struct wl_listener *listener, void *data) { | |||
228 | wlr_cursor_move(cursor->cursor, event->device, | 416 | wlr_cursor_move(cursor->cursor, event->device, |
229 | event->delta_x, event->delta_y); | 417 | event->delta_x, event->delta_y); |
230 | cursor_send_pointer_motion(cursor, event->time_msec, true); | 418 | cursor_send_pointer_motion(cursor, event->time_msec, true); |
419 | transaction_commit_dirty(); | ||
231 | } | 420 | } |
232 | 421 | ||
233 | static void handle_cursor_motion_absolute( | 422 | static void handle_cursor_motion_absolute( |
@@ -238,10 +427,56 @@ static void handle_cursor_motion_absolute( | |||
238 | struct wlr_event_pointer_motion_absolute *event = data; | 427 | struct wlr_event_pointer_motion_absolute *event = data; |
239 | wlr_cursor_warp_absolute(cursor->cursor, event->device, event->x, event->y); | 428 | wlr_cursor_warp_absolute(cursor->cursor, event->device, event->x, event->y); |
240 | cursor_send_pointer_motion(cursor, event->time_msec, true); | 429 | cursor_send_pointer_motion(cursor, event->time_msec, true); |
430 | transaction_commit_dirty(); | ||
431 | } | ||
432 | |||
433 | static void dispatch_cursor_button_floating(struct sway_cursor *cursor, | ||
434 | uint32_t time_msec, uint32_t button, enum wlr_button_state state, | ||
435 | struct wlr_surface *surface, double sx, double sy, | ||
436 | struct sway_container *cont) { | ||
437 | struct sway_seat *seat = cursor->seat; | ||
438 | |||
439 | // Deny moving or resizing a fullscreen view | ||
440 | if (cont->type == C_VIEW && cont->sway_view->is_fullscreen) { | ||
441 | seat_pointer_notify_button(seat, time_msec, button, state); | ||
442 | return; | ||
443 | } | ||
444 | |||
445 | struct wlr_keyboard *keyboard = wlr_seat_get_keyboard(seat->wlr_seat); | ||
446 | bool mod_pressed = keyboard && | ||
447 | (wlr_keyboard_get_modifiers(keyboard) & config->floating_mod); | ||
448 | enum wlr_edges edge = find_resize_edge(cont, cursor); | ||
449 | bool over_title = edge == WLR_EDGE_NONE && !surface; | ||
450 | |||
451 | // Check for beginning move | ||
452 | if (button == BTN_LEFT && state == WLR_BUTTON_PRESSED && | ||
453 | (mod_pressed || over_title)) { | ||
454 | seat_begin_move(seat, cont, BTN_LEFT); | ||
455 | return; | ||
456 | } | ||
457 | |||
458 | // Check for beginning resize | ||
459 | bool resizing_via_border = button == BTN_LEFT && edge != WLR_EDGE_NONE; | ||
460 | bool resizing_via_mod = button == BTN_RIGHT && mod_pressed; | ||
461 | if ((resizing_via_border || resizing_via_mod) && | ||
462 | state == WLR_BUTTON_PRESSED) { | ||
463 | seat_begin_resize(seat, cont, button, edge); | ||
464 | return; | ||
465 | } | ||
466 | |||
467 | // Send event to surface | ||
468 | seat_set_focus(seat, cont); | ||
469 | seat_pointer_notify_button(seat, time_msec, button, state); | ||
241 | } | 470 | } |
242 | 471 | ||
243 | void dispatch_cursor_button(struct sway_cursor *cursor, | 472 | void dispatch_cursor_button(struct sway_cursor *cursor, |
244 | uint32_t time_msec, uint32_t button, enum wlr_button_state state) { | 473 | uint32_t time_msec, uint32_t button, enum wlr_button_state state) { |
474 | if (cursor->seat->operation != OP_NONE && | ||
475 | button == cursor->seat->op_button && state == WLR_BUTTON_RELEASED) { | ||
476 | seat_end_mouse_operation(cursor->seat); | ||
477 | seat_pointer_notify_button(cursor->seat, time_msec, button, state); | ||
478 | return; | ||
479 | } | ||
245 | if (time_msec == 0) { | 480 | if (time_msec == 0) { |
246 | time_msec = get_current_time_msec(); | 481 | time_msec = get_current_time_msec(); |
247 | } | 482 | } |
@@ -255,14 +490,16 @@ void dispatch_cursor_button(struct sway_cursor *cursor, | |||
255 | wlr_layer_surface_from_wlr_surface(surface); | 490 | wlr_layer_surface_from_wlr_surface(surface); |
256 | if (layer->current.keyboard_interactive) { | 491 | if (layer->current.keyboard_interactive) { |
257 | seat_set_focus_layer(cursor->seat, layer); | 492 | seat_set_focus_layer(cursor->seat, layer); |
258 | return; | ||
259 | } | 493 | } |
260 | } | 494 | seat_pointer_notify_button(cursor->seat, time_msec, button, state); |
261 | // Avoid moving keyboard focus from a surface that accepts it to one | 495 | } else if (cont && container_is_floating(cont)) { |
262 | // that does not unless the change would move us to a new workspace. | 496 | dispatch_cursor_button_floating(cursor, time_msec, button, state, |
263 | // | 497 | surface, sx, sy, cont); |
264 | // This prevents, for example, losing focus when clicking on swaybar. | 498 | } else if (surface && cont && cont->type != C_VIEW) { |
265 | if (surface && cont && cont->type != C_VIEW) { | 499 | // Avoid moving keyboard focus from a surface that accepts it to one |
500 | // that does not unless the change would move us to a new workspace. | ||
501 | // | ||
502 | // This prevents, for example, losing focus when clicking on swaybar. | ||
266 | struct sway_container *new_ws = cont; | 503 | struct sway_container *new_ws = cont; |
267 | if (new_ws && new_ws->type != C_WORKSPACE) { | 504 | if (new_ws && new_ws->type != C_WORKSPACE) { |
268 | new_ws = container_parent(new_ws, C_WORKSPACE); | 505 | new_ws = container_parent(new_ws, C_WORKSPACE); |
@@ -274,12 +511,15 @@ void dispatch_cursor_button(struct sway_cursor *cursor, | |||
274 | if (new_ws != old_ws) { | 511 | if (new_ws != old_ws) { |
275 | seat_set_focus(cursor->seat, cont); | 512 | seat_set_focus(cursor->seat, cont); |
276 | } | 513 | } |
514 | seat_pointer_notify_button(cursor->seat, time_msec, button, state); | ||
277 | } else if (cont) { | 515 | } else if (cont) { |
278 | seat_set_focus(cursor->seat, cont); | 516 | seat_set_focus(cursor->seat, cont); |
517 | seat_pointer_notify_button(cursor->seat, time_msec, button, state); | ||
518 | } else { | ||
519 | seat_pointer_notify_button(cursor->seat, time_msec, button, state); | ||
279 | } | 520 | } |
280 | 521 | ||
281 | wlr_seat_pointer_notify_button(cursor->seat->wlr_seat, | 522 | transaction_commit_dirty(); |
282 | time_msec, button, state); | ||
283 | } | 523 | } |
284 | 524 | ||
285 | static void handle_cursor_button(struct wl_listener *listener, void *data) { | 525 | static void handle_cursor_button(struct wl_listener *listener, void *data) { |
@@ -425,6 +665,7 @@ static void handle_tool_axis(struct wl_listener *listener, void *data) { | |||
425 | 665 | ||
426 | wlr_cursor_warp_absolute(cursor->cursor, event->device, x, y); | 666 | wlr_cursor_warp_absolute(cursor->cursor, event->device, x, y); |
427 | cursor_send_pointer_motion(cursor, event->time_msec, true); | 667 | cursor_send_pointer_motion(cursor, event->time_msec, true); |
668 | transaction_commit_dirty(); | ||
428 | } | 669 | } |
429 | 670 | ||
430 | static void handle_tool_tip(struct wl_listener *listener, void *data) { | 671 | static void handle_tool_tip(struct wl_listener *listener, void *data) { |
@@ -464,6 +705,9 @@ static void handle_request_set_cursor(struct wl_listener *listener, | |||
464 | void *data) { | 705 | void *data) { |
465 | struct sway_cursor *cursor = | 706 | struct sway_cursor *cursor = |
466 | wl_container_of(listener, cursor, request_set_cursor); | 707 | wl_container_of(listener, cursor, request_set_cursor); |
708 | if (cursor->seat->operation != OP_NONE) { | ||
709 | return; | ||
710 | } | ||
467 | struct wlr_seat_pointer_request_set_cursor_event *event = data; | 711 | struct wlr_seat_pointer_request_set_cursor_event *event = data; |
468 | 712 | ||
469 | struct wl_client *focused_client = NULL; | 713 | struct wl_client *focused_client = NULL; |
@@ -476,15 +720,26 @@ static void handle_request_set_cursor(struct wl_listener *listener, | |||
476 | // TODO: check cursor mode | 720 | // TODO: check cursor mode |
477 | if (focused_client == NULL || | 721 | if (focused_client == NULL || |
478 | event->seat_client->client != focused_client) { | 722 | event->seat_client->client != focused_client) { |
479 | wlr_log(L_DEBUG, "denying request to set cursor from unfocused client"); | 723 | wlr_log(WLR_DEBUG, "denying request to set cursor from unfocused client"); |
480 | return; | 724 | return; |
481 | } | 725 | } |
482 | 726 | ||
483 | wlr_cursor_set_surface(cursor->cursor, event->surface, event->hotspot_x, | 727 | wlr_cursor_set_surface(cursor->cursor, event->surface, event->hotspot_x, |
484 | event->hotspot_y); | 728 | event->hotspot_y); |
729 | cursor->image = NULL; | ||
485 | cursor->image_client = focused_client; | 730 | cursor->image_client = focused_client; |
486 | } | 731 | } |
487 | 732 | ||
733 | void cursor_set_image(struct sway_cursor *cursor, const char *image, | ||
734 | struct wl_client *client) { | ||
735 | if (!cursor->image || strcmp(cursor->image, image) != 0) { | ||
736 | wlr_xcursor_manager_set_cursor_image(cursor->xcursor_manager, image, | ||
737 | cursor->cursor); | ||
738 | cursor->image = image; | ||
739 | } | ||
740 | cursor->image_client = client; | ||
741 | } | ||
742 | |||
488 | void sway_cursor_destroy(struct sway_cursor *cursor) { | 743 | void sway_cursor_destroy(struct sway_cursor *cursor) { |
489 | if (!cursor) { | 744 | if (!cursor) { |
490 | return; | 745 | return; |
diff --git a/sway/input/input-manager.c b/sway/input/input-manager.c index 29b47a7b..0b7cb766 100644 --- a/sway/input/input-manager.c +++ b/sway/input/input-manager.c | |||
@@ -61,7 +61,7 @@ static char *get_device_identifier(struct wlr_input_device *device) { | |||
61 | int len = snprintf(NULL, 0, fmt, vendor, product, name) + 1; | 61 | int len = snprintf(NULL, 0, fmt, vendor, product, name) + 1; |
62 | char *identifier = malloc(len); | 62 | char *identifier = malloc(len); |
63 | if (!identifier) { | 63 | if (!identifier) { |
64 | wlr_log(L_ERROR, "Unable to allocate unique input device name"); | 64 | wlr_log(WLR_ERROR, "Unable to allocate unique input device name"); |
65 | return NULL; | 65 | return NULL; |
66 | } | 66 | } |
67 | 67 | ||
@@ -104,77 +104,89 @@ static void input_manager_libinput_config_pointer( | |||
104 | } | 104 | } |
105 | 105 | ||
106 | libinput_device = wlr_libinput_get_device_handle(wlr_device); | 106 | libinput_device = wlr_libinput_get_device_handle(wlr_device); |
107 | wlr_log(L_DEBUG, "input_manager_libinput_config_pointer(%s)", | 107 | wlr_log(WLR_DEBUG, "input_manager_libinput_config_pointer(%s)", |
108 | ic->identifier); | 108 | ic->identifier); |
109 | 109 | ||
110 | if (ic->accel_profile != INT_MIN) { | 110 | if (ic->accel_profile != INT_MIN) { |
111 | wlr_log(L_DEBUG, "libinput_config_pointer(%s) accel_set_profile(%d)", | 111 | wlr_log(WLR_DEBUG, "libinput_config_pointer(%s) accel_set_profile(%d)", |
112 | ic->identifier, ic->accel_profile); | 112 | ic->identifier, ic->accel_profile); |
113 | libinput_device_config_accel_set_profile(libinput_device, | 113 | libinput_device_config_accel_set_profile(libinput_device, |
114 | ic->accel_profile); | 114 | ic->accel_profile); |
115 | } | 115 | } |
116 | if (ic->click_method != INT_MIN) { | 116 | if (ic->click_method != INT_MIN) { |
117 | wlr_log(L_DEBUG, "libinput_config_pointer(%s) click_set_method(%d)", | 117 | wlr_log(WLR_DEBUG, "libinput_config_pointer(%s) click_set_method(%d)", |
118 | ic->identifier, ic->click_method); | 118 | ic->identifier, ic->click_method); |
119 | libinput_device_config_click_set_method(libinput_device, | 119 | libinput_device_config_click_set_method(libinput_device, |
120 | ic->click_method); | 120 | ic->click_method); |
121 | } | 121 | } |
122 | if (ic->drag_lock != INT_MIN) { | 122 | if (ic->drag_lock != INT_MIN) { |
123 | wlr_log(L_DEBUG, | 123 | wlr_log(WLR_DEBUG, |
124 | "libinput_config_pointer(%s) tap_set_drag_lock_enabled(%d)", | 124 | "libinput_config_pointer(%s) tap_set_drag_lock_enabled(%d)", |
125 | ic->identifier, ic->click_method); | 125 | ic->identifier, ic->click_method); |
126 | libinput_device_config_tap_set_drag_lock_enabled(libinput_device, | 126 | libinput_device_config_tap_set_drag_lock_enabled(libinput_device, |
127 | ic->drag_lock); | 127 | ic->drag_lock); |
128 | } | 128 | } |
129 | if (ic->dwt != INT_MIN) { | 129 | if (ic->dwt != INT_MIN) { |
130 | wlr_log(L_DEBUG, "libinput_config_pointer(%s) dwt_set_enabled(%d)", | 130 | wlr_log(WLR_DEBUG, "libinput_config_pointer(%s) dwt_set_enabled(%d)", |
131 | ic->identifier, ic->dwt); | 131 | ic->identifier, ic->dwt); |
132 | libinput_device_config_dwt_set_enabled(libinput_device, ic->dwt); | 132 | libinput_device_config_dwt_set_enabled(libinput_device, ic->dwt); |
133 | } | 133 | } |
134 | if (ic->left_handed != INT_MIN) { | 134 | if (ic->left_handed != INT_MIN) { |
135 | wlr_log(L_DEBUG, | 135 | wlr_log(WLR_DEBUG, |
136 | "libinput_config_pointer(%s) left_handed_set_enabled(%d)", | 136 | "libinput_config_pointer(%s) left_handed_set_enabled(%d)", |
137 | ic->identifier, ic->left_handed); | 137 | ic->identifier, ic->left_handed); |
138 | libinput_device_config_left_handed_set(libinput_device, | 138 | libinput_device_config_left_handed_set(libinput_device, |
139 | ic->left_handed); | 139 | ic->left_handed); |
140 | } | 140 | } |
141 | if (ic->middle_emulation != INT_MIN) { | 141 | if (ic->middle_emulation != INT_MIN) { |
142 | wlr_log(L_DEBUG, | 142 | wlr_log(WLR_DEBUG, |
143 | "libinput_config_pointer(%s) middle_emulation_set_enabled(%d)", | 143 | "libinput_config_pointer(%s) middle_emulation_set_enabled(%d)", |
144 | ic->identifier, ic->middle_emulation); | 144 | ic->identifier, ic->middle_emulation); |
145 | libinput_device_config_middle_emulation_set_enabled(libinput_device, | 145 | libinput_device_config_middle_emulation_set_enabled(libinput_device, |
146 | ic->middle_emulation); | 146 | ic->middle_emulation); |
147 | } | 147 | } |
148 | if (ic->natural_scroll != INT_MIN) { | 148 | if (ic->natural_scroll != INT_MIN) { |
149 | wlr_log(L_DEBUG, | 149 | wlr_log(WLR_DEBUG, |
150 | "libinput_config_pointer(%s) natural_scroll_set_enabled(%d)", | 150 | "libinput_config_pointer(%s) natural_scroll_set_enabled(%d)", |
151 | ic->identifier, ic->natural_scroll); | 151 | ic->identifier, ic->natural_scroll); |
152 | libinput_device_config_scroll_set_natural_scroll_enabled( | 152 | libinput_device_config_scroll_set_natural_scroll_enabled( |
153 | libinput_device, ic->natural_scroll); | 153 | libinput_device, ic->natural_scroll); |
154 | } | 154 | } |
155 | if (ic->pointer_accel != FLT_MIN) { | 155 | if (ic->pointer_accel != FLT_MIN) { |
156 | wlr_log(L_DEBUG, "libinput_config_pointer(%s) accel_set_speed(%f)", | 156 | wlr_log(WLR_DEBUG, "libinput_config_pointer(%s) accel_set_speed(%f)", |
157 | ic->identifier, ic->pointer_accel); | 157 | ic->identifier, ic->pointer_accel); |
158 | libinput_device_config_accel_set_speed(libinput_device, | 158 | libinput_device_config_accel_set_speed(libinput_device, |
159 | ic->pointer_accel); | 159 | ic->pointer_accel); |
160 | } | 160 | } |
161 | if (ic->scroll_button != INT_MIN) { | ||
162 | wlr_log(WLR_DEBUG, "libinput_config_pointer(%s) scroll_set_button(%d)", | ||
163 | ic->identifier, ic->scroll_button); | ||
164 | libinput_device_config_scroll_set_button(libinput_device, | ||
165 | ic->scroll_button); | ||
166 | } | ||
161 | if (ic->scroll_method != INT_MIN) { | 167 | if (ic->scroll_method != INT_MIN) { |
162 | wlr_log(L_DEBUG, "libinput_config_pointer(%s) scroll_set_method(%d)", | 168 | wlr_log(WLR_DEBUG, "libinput_config_pointer(%s) scroll_set_method(%d)", |
163 | ic->identifier, ic->scroll_method); | 169 | ic->identifier, ic->scroll_method); |
164 | libinput_device_config_scroll_set_method(libinput_device, | 170 | libinput_device_config_scroll_set_method(libinput_device, |
165 | ic->scroll_method); | 171 | ic->scroll_method); |
166 | } | 172 | } |
167 | if (ic->send_events != INT_MIN) { | 173 | if (ic->send_events != INT_MIN) { |
168 | wlr_log(L_DEBUG, "libinput_config_pointer(%s) send_events_set_mode(%d)", | 174 | wlr_log(WLR_DEBUG, "libinput_config_pointer(%s) send_events_set_mode(%d)", |
169 | ic->identifier, ic->send_events); | 175 | ic->identifier, ic->send_events); |
170 | libinput_device_config_send_events_set_mode(libinput_device, | 176 | libinput_device_config_send_events_set_mode(libinput_device, |
171 | ic->send_events); | 177 | ic->send_events); |
172 | } | 178 | } |
173 | if (ic->tap != INT_MIN) { | 179 | if (ic->tap != INT_MIN) { |
174 | wlr_log(L_DEBUG, "libinput_config_pointer(%s) tap_set_enabled(%d)", | 180 | wlr_log(WLR_DEBUG, "libinput_config_pointer(%s) tap_set_enabled(%d)", |
175 | ic->identifier, ic->tap); | 181 | ic->identifier, ic->tap); |
176 | libinput_device_config_tap_set_enabled(libinput_device, ic->tap); | 182 | libinput_device_config_tap_set_enabled(libinput_device, ic->tap); |
177 | } | 183 | } |
184 | if (ic->tap_button_map != INT_MIN) { | ||
185 | wlr_log(WLR_DEBUG, "libinput_config_pointer(%s) tap_set_button_map(%d)", | ||
186 | ic->identifier, ic->tap); | ||
187 | libinput_device_config_tap_set_button_map(libinput_device, | ||
188 | ic->tap_button_map); | ||
189 | } | ||
178 | } | 190 | } |
179 | 191 | ||
180 | static void handle_device_destroy(struct wl_listener *listener, void *data) { | 192 | static void handle_device_destroy(struct wl_listener *listener, void *data) { |
@@ -187,7 +199,7 @@ static void handle_device_destroy(struct wl_listener *listener, void *data) { | |||
187 | return; | 199 | return; |
188 | } | 200 | } |
189 | 201 | ||
190 | wlr_log(L_DEBUG, "removing device: '%s'", | 202 | wlr_log(WLR_DEBUG, "removing device: '%s'", |
191 | input_device->identifier); | 203 | input_device->identifier); |
192 | 204 | ||
193 | struct sway_seat *seat = NULL; | 205 | struct sway_seat *seat = NULL; |
@@ -217,16 +229,19 @@ static void handle_new_input(struct wl_listener *listener, void *data) { | |||
217 | input_device->identifier = get_device_identifier(device); | 229 | input_device->identifier = get_device_identifier(device); |
218 | wl_list_insert(&input->devices, &input_device->link); | 230 | wl_list_insert(&input->devices, &input_device->link); |
219 | 231 | ||
220 | wlr_log(L_DEBUG, "adding device: '%s'", | 232 | wlr_log(WLR_DEBUG, "adding device: '%s'", |
221 | input_device->identifier); | 233 | input_device->identifier); |
222 | 234 | ||
223 | if (input_device->wlr_device->type == WLR_INPUT_DEVICE_POINTER) { | 235 | if (input_device->wlr_device->type == WLR_INPUT_DEVICE_POINTER) { |
224 | input_manager_libinput_config_pointer(input_device); | 236 | input_manager_libinput_config_pointer(input_device); |
225 | } | 237 | } |
226 | 238 | ||
239 | wl_signal_add(&device->events.destroy, &input_device->device_destroy); | ||
240 | input_device->device_destroy.notify = handle_device_destroy; | ||
241 | |||
227 | struct sway_seat *seat = NULL; | 242 | struct sway_seat *seat = NULL; |
228 | if (!input_has_seat_configuration(input)) { | 243 | if (!input_has_seat_configuration(input)) { |
229 | wlr_log(L_DEBUG, "no seat configuration, using default seat"); | 244 | wlr_log(WLR_DEBUG, "no seat configuration, using default seat"); |
230 | seat = input_manager_get_seat(input, default_seat); | 245 | seat = input_manager_get_seat(input, default_seat); |
231 | seat_add_device(seat, input_device); | 246 | seat_add_device(seat, input_device); |
232 | return; | 247 | return; |
@@ -256,13 +271,10 @@ static void handle_new_input(struct wl_listener *listener, void *data) { | |||
256 | } | 271 | } |
257 | 272 | ||
258 | if (!added) { | 273 | if (!added) { |
259 | wlr_log(L_DEBUG, | 274 | wlr_log(WLR_DEBUG, |
260 | "device '%s' is not configured on any seats", | 275 | "device '%s' is not configured on any seats", |
261 | input_device->identifier); | 276 | input_device->identifier); |
262 | } | 277 | } |
263 | |||
264 | wl_signal_add(&device->events.destroy, &input_device->device_destroy); | ||
265 | input_device->device_destroy.notify = handle_device_destroy; | ||
266 | } | 278 | } |
267 | 279 | ||
268 | static void handle_inhibit_activate(struct wl_listener *listener, void *data) { | 280 | static void handle_inhibit_activate(struct wl_listener *listener, void *data) { |
@@ -282,7 +294,7 @@ static void handle_inhibit_deactivate(struct wl_listener *listener, void *data) | |||
282 | seat_set_exclusive_client(seat, NULL); | 294 | seat_set_exclusive_client(seat, NULL); |
283 | struct sway_container *previous = seat_get_focus(seat); | 295 | struct sway_container *previous = seat_get_focus(seat); |
284 | if (previous) { | 296 | if (previous) { |
285 | wlr_log(L_DEBUG, "Returning focus to %p %s '%s'", previous, | 297 | wlr_log(WLR_DEBUG, "Returning focus to %p %s '%s'", previous, |
286 | container_type_to_str(previous->type), previous->name); | 298 | container_type_to_str(previous->type), previous->name); |
287 | // Hack to get seat to re-focus the return value of get_focus | 299 | // Hack to get seat to re-focus the return value of get_focus |
288 | seat_set_focus(seat, previous->parent); | 300 | seat_set_focus(seat, previous->parent); |
@@ -359,7 +371,7 @@ void input_manager_apply_input_config(struct sway_input_manager *input, | |||
359 | 371 | ||
360 | void input_manager_apply_seat_config(struct sway_input_manager *input, | 372 | void input_manager_apply_seat_config(struct sway_input_manager *input, |
361 | struct seat_config *seat_config) { | 373 | struct seat_config *seat_config) { |
362 | wlr_log(L_DEBUG, "applying new seat config for seat %s", | 374 | wlr_log(WLR_DEBUG, "applying new seat config for seat %s", |
363 | seat_config->name); | 375 | seat_config->name); |
364 | struct sway_seat *seat = input_manager_get_seat(input, seat_config->name); | 376 | struct sway_seat *seat = input_manager_get_seat(input, seat_config->name); |
365 | if (!seat) { | 377 | if (!seat) { |
diff --git a/sway/input/keyboard.c b/sway/input/keyboard.c index ec149d06..ede38519 100644 --- a/sway/input/keyboard.c +++ b/sway/input/keyboard.c | |||
@@ -3,6 +3,7 @@ | |||
3 | #include <wlr/backend/multi.h> | 3 | #include <wlr/backend/multi.h> |
4 | #include <wlr/backend/session.h> | 4 | #include <wlr/backend/session.h> |
5 | #include <wlr/types/wlr_idle.h> | 5 | #include <wlr/types/wlr_idle.h> |
6 | #include "sway/desktop/transaction.h" | ||
6 | #include "sway/input/seat.h" | 7 | #include "sway/input/seat.h" |
7 | #include "sway/input/keyboard.h" | 8 | #include "sway/input/keyboard.h" |
8 | #include "sway/input/input-manager.h" | 9 | #include "sway/input/input-manager.h" |
@@ -108,7 +109,7 @@ static void get_active_binding(const struct sway_shortcut_state *state, | |||
108 | } | 109 | } |
109 | 110 | ||
110 | if (*current_binding && *current_binding != binding) { | 111 | if (*current_binding && *current_binding != binding) { |
111 | wlr_log(L_DEBUG, "encountered duplicate bindings %d and %d", | 112 | wlr_log(WLR_DEBUG, "encountered duplicate bindings %d and %d", |
112 | (*current_binding)->order, binding->order); | 113 | (*current_binding)->order, binding->order); |
113 | } else { | 114 | } else { |
114 | *current_binding = binding; | 115 | *current_binding = binding; |
@@ -122,12 +123,13 @@ static void get_active_binding(const struct sway_shortcut_state *state, | |||
122 | */ | 123 | */ |
123 | static void keyboard_execute_command(struct sway_keyboard *keyboard, | 124 | static void keyboard_execute_command(struct sway_keyboard *keyboard, |
124 | struct sway_binding *binding) { | 125 | struct sway_binding *binding) { |
125 | wlr_log(L_DEBUG, "running command for binding: %s", | 126 | wlr_log(WLR_DEBUG, "running command for binding: %s", |
126 | binding->command); | 127 | binding->command); |
127 | config->handler_context.seat = keyboard->seat_device->sway_seat; | 128 | config->handler_context.seat = keyboard->seat_device->sway_seat; |
128 | struct cmd_results *results = execute_command(binding->command, NULL); | 129 | struct cmd_results *results = execute_command(binding->command, NULL); |
130 | transaction_commit_dirty(); | ||
129 | if (results->status != CMD_SUCCESS) { | 131 | if (results->status != CMD_SUCCESS) { |
130 | wlr_log(L_DEBUG, "could not run command for binding: %s (%s)", | 132 | wlr_log(WLR_DEBUG, "could not run command for binding: %s (%s)", |
131 | binding->command, results->error); | 133 | binding->command, results->error); |
132 | } | 134 | } |
133 | free_cmd_results(results); | 135 | free_cmd_results(results); |
@@ -386,7 +388,7 @@ void sway_keyboard_configure(struct sway_keyboard *keyboard) { | |||
386 | xkb_keymap_new_from_names(context, &rules, XKB_KEYMAP_COMPILE_NO_FLAGS); | 388 | xkb_keymap_new_from_names(context, &rules, XKB_KEYMAP_COMPILE_NO_FLAGS); |
387 | 389 | ||
388 | if (!keymap) { | 390 | if (!keymap) { |
389 | wlr_log(L_DEBUG, "cannot configure keyboard: keymap does not exist"); | 391 | wlr_log(WLR_DEBUG, "cannot configure keyboard: keymap does not exist"); |
390 | xkb_context_unref(context); | 392 | xkb_context_unref(context); |
391 | return; | 393 | return; |
392 | } | 394 | } |
@@ -420,6 +422,9 @@ void sway_keyboard_destroy(struct sway_keyboard *keyboard) { | |||
420 | if (!keyboard) { | 422 | if (!keyboard) { |
421 | return; | 423 | return; |
422 | } | 424 | } |
425 | if (keyboard->keymap) { | ||
426 | xkb_keymap_unref(keyboard->keymap); | ||
427 | } | ||
423 | wl_list_remove(&keyboard->keyboard_key.link); | 428 | wl_list_remove(&keyboard->keyboard_key.link); |
424 | wl_list_remove(&keyboard->keyboard_modifiers.link); | 429 | wl_list_remove(&keyboard->keyboard_modifiers.link); |
425 | free(keyboard); | 430 | free(keyboard); |
diff --git a/sway/input/seat.c b/sway/input/seat.c index 2c2087da..fc9e54b6 100644 --- a/sway/input/seat.c +++ b/sway/input/seat.c | |||
@@ -1,6 +1,11 @@ | |||
1 | #define _XOPEN_SOURCE 700 | 1 | #define _XOPEN_SOURCE 700 |
2 | #define _POSIX_C_SOURCE 199309L | 2 | #define _POSIX_C_SOURCE 199309L |
3 | #include <assert.h> | 3 | #include <assert.h> |
4 | #ifdef __linux__ | ||
5 | #include <linux/input-event-codes.h> | ||
6 | #elif __FreeBSD__ | ||
7 | #include <dev/evdev/input-event-codes.h> | ||
8 | #endif | ||
4 | #include <strings.h> | 9 | #include <strings.h> |
5 | #include <time.h> | 10 | #include <time.h> |
6 | #include <wlr/types/wlr_cursor.h> | 11 | #include <wlr/types/wlr_cursor.h> |
@@ -75,7 +80,7 @@ static void seat_send_activate(struct sway_container *con, | |||
75 | struct sway_seat *seat) { | 80 | struct sway_seat *seat) { |
76 | if (con->type == C_VIEW) { | 81 | if (con->type == C_VIEW) { |
77 | if (!seat_is_input_allowed(seat, con->sway_view->surface)) { | 82 | if (!seat_is_input_allowed(seat, con->sway_view->surface)) { |
78 | wlr_log(L_DEBUG, "Refusing to set focus, input is inhibited"); | 83 | wlr_log(WLR_DEBUG, "Refusing to set focus, input is inhibited"); |
79 | return; | 84 | return; |
80 | } | 85 | } |
81 | view_set_activated(con->sway_view, true); | 86 | view_set_activated(con->sway_view, true); |
@@ -219,7 +224,7 @@ static struct sway_seat_container *seat_container_from_container( | |||
219 | 224 | ||
220 | seat_con = calloc(1, sizeof(struct sway_seat_container)); | 225 | seat_con = calloc(1, sizeof(struct sway_seat_container)); |
221 | if (seat_con == NULL) { | 226 | if (seat_con == NULL) { |
222 | wlr_log(L_ERROR, "could not allocate seat container"); | 227 | wlr_log(WLR_ERROR, "could not allocate seat container"); |
223 | return NULL; | 228 | return NULL; |
224 | } | 229 | } |
225 | 230 | ||
@@ -301,7 +306,7 @@ static void handle_new_drag_icon(struct wl_listener *listener, void *data) { | |||
301 | 306 | ||
302 | struct sway_drag_icon *icon = calloc(1, sizeof(struct sway_drag_icon)); | 307 | struct sway_drag_icon *icon = calloc(1, sizeof(struct sway_drag_icon)); |
303 | if (icon == NULL) { | 308 | if (icon == NULL) { |
304 | wlr_log(L_ERROR, "Allocation failed"); | 309 | wlr_log(WLR_ERROR, "Allocation failed"); |
305 | return; | 310 | return; |
306 | } | 311 | } |
307 | icon->seat = seat; | 312 | icon->seat = seat; |
@@ -348,6 +353,7 @@ struct sway_seat *seat_create(struct sway_input_manager *input, | |||
348 | free(seat); | 353 | free(seat); |
349 | return NULL; | 354 | return NULL; |
350 | } | 355 | } |
356 | seat->wlr_seat->data = seat; | ||
351 | 357 | ||
352 | seat->cursor = sway_cursor_create(seat); | 358 | seat->cursor = sway_cursor_create(seat); |
353 | if (!seat->cursor) { | 359 | if (!seat->cursor) { |
@@ -391,7 +397,7 @@ static void seat_apply_input_config(struct sway_seat *seat, | |||
391 | struct input_config *ic = input_device_get_config( | 397 | struct input_config *ic = input_device_get_config( |
392 | sway_device->input_device); | 398 | sway_device->input_device); |
393 | if (ic != NULL) { | 399 | if (ic != NULL) { |
394 | wlr_log(L_DEBUG, "Applying input config to %s", | 400 | wlr_log(WLR_DEBUG, "Applying input config to %s", |
395 | sway_device->input_device->identifier); | 401 | sway_device->input_device->identifier); |
396 | 402 | ||
397 | mapped_to_output = ic->mapped_to_output; | 403 | mapped_to_output = ic->mapped_to_output; |
@@ -401,7 +407,7 @@ static void seat_apply_input_config(struct sway_seat *seat, | |||
401 | mapped_to_output = sway_device->input_device->wlr_device->output_name; | 407 | mapped_to_output = sway_device->input_device->wlr_device->output_name; |
402 | } | 408 | } |
403 | if (mapped_to_output != NULL) { | 409 | if (mapped_to_output != NULL) { |
404 | wlr_log(L_DEBUG, "Mapping input device %s to output %s", | 410 | wlr_log(WLR_DEBUG, "Mapping input device %s to output %s", |
405 | sway_device->input_device->identifier, mapped_to_output); | 411 | sway_device->input_device->identifier, mapped_to_output); |
406 | struct sway_container *output = NULL; | 412 | struct sway_container *output = NULL; |
407 | for (int i = 0; i < root_container.children->length; ++i) { | 413 | for (int i = 0; i < root_container.children->length; ++i) { |
@@ -415,7 +421,7 @@ static void seat_apply_input_config(struct sway_seat *seat, | |||
415 | wlr_cursor_map_input_to_output(seat->cursor->cursor, | 421 | wlr_cursor_map_input_to_output(seat->cursor->cursor, |
416 | sway_device->input_device->wlr_device, | 422 | sway_device->input_device->wlr_device, |
417 | output->sway_output->wlr_output); | 423 | output->sway_output->wlr_output); |
418 | wlr_log(L_DEBUG, "Mapped to output %s", output->name); | 424 | wlr_log(WLR_DEBUG, "Mapped to output %s", output->name); |
419 | } | 425 | } |
420 | } | 426 | } |
421 | } | 427 | } |
@@ -495,7 +501,7 @@ void seat_configure_device(struct sway_seat *seat, | |||
495 | seat_configure_tablet_tool(seat, seat_device); | 501 | seat_configure_tablet_tool(seat, seat_device); |
496 | break; | 502 | break; |
497 | case WLR_INPUT_DEVICE_TABLET_PAD: | 503 | case WLR_INPUT_DEVICE_TABLET_PAD: |
498 | wlr_log(L_DEBUG, "TODO: configure tablet pad"); | 504 | wlr_log(WLR_DEBUG, "TODO: configure tablet pad"); |
499 | break; | 505 | break; |
500 | } | 506 | } |
501 | } | 507 | } |
@@ -510,11 +516,11 @@ void seat_add_device(struct sway_seat *seat, | |||
510 | struct sway_seat_device *seat_device = | 516 | struct sway_seat_device *seat_device = |
511 | calloc(1, sizeof(struct sway_seat_device)); | 517 | calloc(1, sizeof(struct sway_seat_device)); |
512 | if (!seat_device) { | 518 | if (!seat_device) { |
513 | wlr_log(L_DEBUG, "could not allocate seat device"); | 519 | wlr_log(WLR_DEBUG, "could not allocate seat device"); |
514 | return; | 520 | return; |
515 | } | 521 | } |
516 | 522 | ||
517 | wlr_log(L_DEBUG, "adding device %s to seat %s", | 523 | wlr_log(WLR_DEBUG, "adding device %s to seat %s", |
518 | input_device->identifier, seat->wlr_seat->name); | 524 | input_device->identifier, seat->wlr_seat->name); |
519 | 525 | ||
520 | seat_device->sway_seat = seat; | 526 | seat_device->sway_seat = seat; |
@@ -533,7 +539,7 @@ void seat_remove_device(struct sway_seat *seat, | |||
533 | return; | 539 | return; |
534 | } | 540 | } |
535 | 541 | ||
536 | wlr_log(L_DEBUG, "removing device %s from seat %s", | 542 | wlr_log(WLR_DEBUG, "removing device %s from seat %s", |
537 | input_device->identifier, seat->wlr_seat->name); | 543 | input_device->identifier, seat->wlr_seat->name); |
538 | 544 | ||
539 | seat_device_destroy(seat_device); | 545 | seat_device_destroy(seat_device); |
@@ -594,6 +600,12 @@ static void seat_send_unfocus(struct sway_container *container, | |||
594 | } | 600 | } |
595 | } | 601 | } |
596 | 602 | ||
603 | static int handle_urgent_timeout(void *data) { | ||
604 | struct sway_view *view = data; | ||
605 | view_set_urgent(view, false); | ||
606 | return 0; | ||
607 | } | ||
608 | |||
597 | void seat_set_focus_warp(struct sway_seat *seat, | 609 | void seat_set_focus_warp(struct sway_seat *seat, |
598 | struct sway_container *container, bool warp) { | 610 | struct sway_container *container, bool warp) { |
599 | if (seat->focused_layer) { | 611 | if (seat->focused_layer) { |
@@ -616,6 +628,7 @@ void seat_set_focus_warp(struct sway_seat *seat, | |||
616 | 628 | ||
617 | if (last_workspace && last_workspace == new_workspace | 629 | if (last_workspace && last_workspace == new_workspace |
618 | && last_workspace->sway_workspace->fullscreen | 630 | && last_workspace->sway_workspace->fullscreen |
631 | && container && container->type == C_VIEW | ||
619 | && !container->sway_view->is_fullscreen) { | 632 | && !container->sway_view->is_fullscreen) { |
620 | return; | 633 | return; |
621 | } | 634 | } |
@@ -649,6 +662,7 @@ void seat_set_focus_warp(struct sway_seat *seat, | |||
649 | while (parent) { | 662 | while (parent) { |
650 | wl_list_remove(&parent->link); | 663 | wl_list_remove(&parent->link); |
651 | wl_list_insert(&seat->focus_stack, &parent->link); | 664 | wl_list_insert(&seat->focus_stack, &parent->link); |
665 | container_set_dirty(parent->container); | ||
652 | 666 | ||
653 | parent = | 667 | parent = |
654 | seat_container_from_container(seat, | 668 | seat_container_from_container(seat, |
@@ -661,9 +675,39 @@ void seat_set_focus_warp(struct sway_seat *seat, | |||
661 | if (last_focus) { | 675 | if (last_focus) { |
662 | seat_send_unfocus(last_focus, seat); | 676 | seat_send_unfocus(last_focus, seat); |
663 | } | 677 | } |
664 | |||
665 | seat_send_focus(container, seat); | 678 | seat_send_focus(container, seat); |
666 | container_damage_whole(container); | 679 | |
680 | container_set_dirty(container); | ||
681 | container_set_dirty(container->parent); // for focused_inactive_child | ||
682 | if (last_focus) { | ||
683 | container_set_dirty(last_focus); | ||
684 | } | ||
685 | } | ||
686 | |||
687 | // If urgent, either unset the urgency or start a timer to unset it | ||
688 | if (container && container->type == C_VIEW && | ||
689 | view_is_urgent(container->sway_view) && | ||
690 | !container->sway_view->urgent_timer) { | ||
691 | struct sway_view *view = container->sway_view; | ||
692 | if (last_workspace && last_workspace != new_workspace && | ||
693 | config->urgent_timeout > 0) { | ||
694 | view->urgent_timer = wl_event_loop_add_timer(server.wl_event_loop, | ||
695 | handle_urgent_timeout, view); | ||
696 | wl_event_source_timer_update(view->urgent_timer, | ||
697 | config->urgent_timeout); | ||
698 | } else { | ||
699 | view_set_urgent(view, false); | ||
700 | } | ||
701 | } | ||
702 | |||
703 | // If we've focused a floating container, bring it to the front. | ||
704 | // We do this by putting it at the end of the floating list. | ||
705 | // This must happen for both the pending and current children lists. | ||
706 | if (container && container_is_floating(container)) { | ||
707 | list_move_to_end(container->parent->children, container); | ||
708 | if (container_has_ancestor(container, container->current.parent)) { | ||
709 | list_move_to_end(container->parent->current.children, container); | ||
710 | } | ||
667 | } | 711 | } |
668 | 712 | ||
669 | // clean up unfocused empty workspace on new output | 713 | // clean up unfocused empty workspace on new output |
@@ -707,11 +751,7 @@ void seat_set_focus_warp(struct sway_seat *seat, | |||
707 | } | 751 | } |
708 | } | 752 | } |
709 | 753 | ||
710 | if (last_focus) { | 754 | if (last_focus != NULL) { |
711 | container_damage_whole(last_focus); | ||
712 | } | ||
713 | |||
714 | if (last_workspace && last_workspace != new_workspace) { | ||
715 | cursor_send_pointer_motion(seat->cursor, 0, true); | 755 | cursor_send_pointer_motion(seat->cursor, 0, true); |
716 | } | 756 | } |
717 | 757 | ||
@@ -726,11 +766,11 @@ void seat_set_focus(struct sway_seat *seat, | |||
726 | } | 766 | } |
727 | 767 | ||
728 | void seat_set_focus_surface(struct sway_seat *seat, | 768 | void seat_set_focus_surface(struct sway_seat *seat, |
729 | struct wlr_surface *surface) { | 769 | struct wlr_surface *surface, bool unfocus) { |
730 | if (seat->focused_layer != NULL) { | 770 | if (seat->focused_layer != NULL) { |
731 | return; | 771 | return; |
732 | } | 772 | } |
733 | if (seat->has_focus) { | 773 | if (seat->has_focus && unfocus) { |
734 | struct sway_container *focus = seat_get_focus(seat); | 774 | struct sway_container *focus = seat_get_focus(seat); |
735 | seat_send_unfocus(focus, seat); | 775 | seat_send_unfocus(focus, seat); |
736 | seat->has_focus = false; | 776 | seat->has_focus = false; |
@@ -752,7 +792,7 @@ void seat_set_focus_layer(struct sway_seat *seat, | |||
752 | struct sway_container *previous = | 792 | struct sway_container *previous = |
753 | seat_get_focus_inactive(seat, &root_container); | 793 | seat_get_focus_inactive(seat, &root_container); |
754 | if (previous) { | 794 | if (previous) { |
755 | wlr_log(L_DEBUG, "Returning focus to %p %s '%s'", previous, | 795 | wlr_log(WLR_DEBUG, "Returning focus to %p %s '%s'", previous, |
756 | container_type_to_str(previous->type), previous->name); | 796 | container_type_to_str(previous->type), previous->name); |
757 | // Hack to get seat to re-focus the return value of get_focus | 797 | // Hack to get seat to re-focus the return value of get_focus |
758 | seat_set_focus(seat, previous->parent); | 798 | seat_set_focus(seat, previous->parent); |
@@ -762,7 +802,7 @@ void seat_set_focus_layer(struct sway_seat *seat, | |||
762 | } else if (!layer || seat->focused_layer == layer) { | 802 | } else if (!layer || seat->focused_layer == layer) { |
763 | return; | 803 | return; |
764 | } | 804 | } |
765 | seat_set_focus_surface(seat, layer->surface); | 805 | seat_set_focus_surface(seat, layer->surface, true); |
766 | if (layer->layer >= ZWLR_LAYER_SHELL_V1_LAYER_TOP) { | 806 | if (layer->layer >= ZWLR_LAYER_SHELL_V1_LAYER_TOP) { |
767 | seat->focused_layer = layer; | 807 | seat->focused_layer = layer; |
768 | } | 808 | } |
@@ -830,18 +870,6 @@ struct sway_container *seat_get_active_child(struct sway_seat *seat, | |||
830 | return NULL; | 870 | return NULL; |
831 | } | 871 | } |
832 | 872 | ||
833 | struct sway_container *seat_get_active_current_child(struct sway_seat *seat, | ||
834 | struct sway_container *container) { | ||
835 | struct sway_container *child = seat_get_active_child(seat, container); | ||
836 | if (child) { | ||
837 | return child; | ||
838 | } | ||
839 | if (container->current.children->length == 1) { | ||
840 | return container->current.children->items[0]; | ||
841 | } | ||
842 | return NULL; | ||
843 | } | ||
844 | |||
845 | struct sway_container *seat_get_focus(struct sway_seat *seat) { | 873 | struct sway_container *seat_get_focus(struct sway_seat *seat) { |
846 | if (!seat->has_focus) { | 874 | if (!seat->has_focus) { |
847 | return NULL; | 875 | return NULL; |
@@ -873,3 +901,68 @@ struct seat_config *seat_get_config(struct sway_seat *seat) { | |||
873 | 901 | ||
874 | return NULL; | 902 | return NULL; |
875 | } | 903 | } |
904 | |||
905 | void seat_begin_move(struct sway_seat *seat, struct sway_container *con, | ||
906 | uint32_t button) { | ||
907 | if (!seat->cursor) { | ||
908 | wlr_log(WLR_DEBUG, "Ignoring move request due to no cursor device"); | ||
909 | return; | ||
910 | } | ||
911 | seat->operation = OP_MOVE; | ||
912 | seat->op_container = con; | ||
913 | seat->op_button = button; | ||
914 | cursor_set_image(seat->cursor, "grab", NULL); | ||
915 | } | ||
916 | |||
917 | void seat_begin_resize(struct sway_seat *seat, struct sway_container *con, | ||
918 | uint32_t button, enum wlr_edges edge) { | ||
919 | if (!seat->cursor) { | ||
920 | wlr_log(WLR_DEBUG, "Ignoring resize request due to no cursor device"); | ||
921 | return; | ||
922 | } | ||
923 | struct wlr_keyboard *keyboard = wlr_seat_get_keyboard(seat->wlr_seat); | ||
924 | seat->operation = OP_RESIZE; | ||
925 | seat->op_container = con; | ||
926 | seat->op_resize_preserve_ratio = keyboard && | ||
927 | (wlr_keyboard_get_modifiers(keyboard) & WLR_MODIFIER_SHIFT); | ||
928 | seat->op_resize_edge = edge == WLR_EDGE_NONE ? | ||
929 | RESIZE_EDGE_BOTTOM | RESIZE_EDGE_RIGHT : edge; | ||
930 | seat->op_button = button; | ||
931 | seat->op_ref_lx = seat->cursor->cursor->x; | ||
932 | seat->op_ref_ly = seat->cursor->cursor->y; | ||
933 | seat->op_ref_con_lx = con->x; | ||
934 | seat->op_ref_con_ly = con->y; | ||
935 | seat->op_ref_width = con->width; | ||
936 | seat->op_ref_height = con->height; | ||
937 | |||
938 | const char *image = edge == WLR_EDGE_NONE ? | ||
939 | "se-resize" : wlr_xcursor_get_resize_name(edge); | ||
940 | cursor_set_image(seat->cursor, image, NULL); | ||
941 | } | ||
942 | |||
943 | void seat_end_mouse_operation(struct sway_seat *seat) { | ||
944 | switch (seat->operation) { | ||
945 | case OP_MOVE: | ||
946 | { | ||
947 | // We "move" the container to its own location so it discovers its | ||
948 | // output again. | ||
949 | struct sway_container *con = seat->op_container; | ||
950 | container_floating_move_to(con, con->x, con->y); | ||
951 | } | ||
952 | case OP_RESIZE: | ||
953 | // Don't need to do anything here. | ||
954 | break; | ||
955 | case OP_NONE: | ||
956 | break; | ||
957 | } | ||
958 | seat->operation = OP_NONE; | ||
959 | seat->op_container = NULL; | ||
960 | cursor_set_image(seat->cursor, "left_ptr", NULL); | ||
961 | } | ||
962 | |||
963 | void seat_pointer_notify_button(struct sway_seat *seat, uint32_t time_msec, | ||
964 | uint32_t button, enum wlr_button_state state) { | ||
965 | seat->last_button = button; | ||
966 | seat->last_button_serial = wlr_seat_pointer_notify_button(seat->wlr_seat, | ||
967 | time_msec, button, state); | ||
968 | } | ||
diff --git a/sway/ipc-json.c b/sway/ipc-json.c index b9289e25..c49ea47e 100644 --- a/sway/ipc-json.c +++ b/sway/ipc-json.c | |||
@@ -2,6 +2,7 @@ | |||
2 | #include <stdio.h> | 2 | #include <stdio.h> |
3 | #include <ctype.h> | 3 | #include <ctype.h> |
4 | #include "log.h" | 4 | #include "log.h" |
5 | #include "sway/config.h" | ||
5 | #include "sway/ipc-json.h" | 6 | #include "sway/ipc-json.h" |
6 | #include "sway/tree/container.h" | 7 | #include "sway/tree/container.h" |
7 | #include "sway/tree/workspace.h" | 8 | #include "sway/tree/workspace.h" |
@@ -41,6 +42,7 @@ json_object *ipc_json_get_version() { | |||
41 | json_object_object_add(version, "major", json_object_new_int(major)); | 42 | json_object_object_add(version, "major", json_object_new_int(major)); |
42 | json_object_object_add(version, "minor", json_object_new_int(minor)); | 43 | json_object_object_add(version, "minor", json_object_new_int(minor)); |
43 | json_object_object_add(version, "patch", json_object_new_int(patch)); | 44 | json_object_object_add(version, "patch", json_object_new_int(patch)); |
45 | json_object_object_add(version, "loaded_config_file_name", json_object_new_string(config->current_config_path)); | ||
44 | 46 | ||
45 | return version; | 47 | return version; |
46 | } | 48 | } |
@@ -168,7 +170,8 @@ static void ipc_json_describe_workspace(struct sway_container *workspace, | |||
168 | json_object_object_add(object, "output", workspace->parent ? | 170 | json_object_object_add(object, "output", workspace->parent ? |
169 | json_object_new_string(workspace->parent->name) : NULL); | 171 | json_object_new_string(workspace->parent->name) : NULL); |
170 | json_object_object_add(object, "type", json_object_new_string("workspace")); | 172 | json_object_object_add(object, "type", json_object_new_string("workspace")); |
171 | json_object_object_add(object, "urgent", json_object_new_boolean(false)); | 173 | json_object_object_add(object, "urgent", |
174 | json_object_new_boolean(workspace->sway_workspace->urgent)); | ||
172 | json_object_object_add(object, "representation", workspace->formatted_title ? | 175 | json_object_object_add(object, "representation", workspace->formatted_title ? |
173 | json_object_new_string(workspace->formatted_title) : NULL); | 176 | json_object_new_string(workspace->formatted_title) : NULL); |
174 | 177 | ||
@@ -194,6 +197,10 @@ static void ipc_json_describe_view(struct sway_container *c, json_object *object | |||
194 | json_object_object_add(object, "layout", | 197 | json_object_object_add(object, "layout", |
195 | json_object_new_string(ipc_json_layout_description(c->layout))); | 198 | json_object_new_string(ipc_json_layout_description(c->layout))); |
196 | } | 199 | } |
200 | |||
201 | bool urgent = c->type == C_VIEW ? | ||
202 | view_is_urgent(c->sway_view) : container_has_urgent_child(c); | ||
203 | json_object_object_add(object, "urgent", json_object_new_boolean(urgent)); | ||
197 | } | 204 | } |
198 | 205 | ||
199 | static void focus_inactive_children_iterator(struct sway_container *c, void *data) { | 206 | static void focus_inactive_children_iterator(struct sway_container *c, void *data) { |
diff --git a/sway/ipc-server.c b/sway/ipc-server.c index 241fe742..be703915 100644 --- a/sway/ipc-server.c +++ b/sway/ipc-server.c | |||
@@ -17,6 +17,8 @@ | |||
17 | #include <unistd.h> | 17 | #include <unistd.h> |
18 | #include <wayland-server.h> | 18 | #include <wayland-server.h> |
19 | #include "sway/commands.h" | 19 | #include "sway/commands.h" |
20 | #include "sway/config.h" | ||
21 | #include "sway/desktop/transaction.h" | ||
20 | #include "sway/ipc-json.h" | 22 | #include "sway/ipc-json.h" |
21 | #include "sway/ipc-server.h" | 23 | #include "sway/ipc-server.h" |
22 | #include "sway/output.h" | 24 | #include "sway/output.h" |
@@ -31,6 +33,7 @@ static int ipc_socket = -1; | |||
31 | static struct wl_event_source *ipc_event_source = NULL; | 33 | static struct wl_event_source *ipc_event_source = NULL; |
32 | static struct sockaddr_un *ipc_sockaddr = NULL; | 34 | static struct sockaddr_un *ipc_sockaddr = NULL; |
33 | static list_t *ipc_client_list = NULL; | 35 | static list_t *ipc_client_list = NULL; |
36 | static struct wl_listener ipc_display_destroy; | ||
34 | 37 | ||
35 | static const char ipc_magic[] = {'i', '3', '-', 'i', 'p', 'c'}; | 38 | static const char ipc_magic[] = {'i', '3', '-', 'i', 'p', 'c'}; |
36 | 39 | ||
@@ -56,6 +59,26 @@ void ipc_client_disconnect(struct ipc_client *client); | |||
56 | void ipc_client_handle_command(struct ipc_client *client); | 59 | void ipc_client_handle_command(struct ipc_client *client); |
57 | bool ipc_send_reply(struct ipc_client *client, const char *payload, uint32_t payload_length); | 60 | bool ipc_send_reply(struct ipc_client *client, const char *payload, uint32_t payload_length); |
58 | 61 | ||
62 | static void handle_display_destroy(struct wl_listener *listener, void *data) { | ||
63 | if (ipc_event_source) { | ||
64 | wl_event_source_remove(ipc_event_source); | ||
65 | } | ||
66 | close(ipc_socket); | ||
67 | unlink(ipc_sockaddr->sun_path); | ||
68 | |||
69 | while (ipc_client_list->length) { | ||
70 | struct ipc_client *client = ipc_client_list->items[0]; | ||
71 | ipc_client_disconnect(client); | ||
72 | } | ||
73 | list_free(ipc_client_list); | ||
74 | |||
75 | if (ipc_sockaddr) { | ||
76 | free(ipc_sockaddr); | ||
77 | } | ||
78 | |||
79 | wl_list_remove(&ipc_display_destroy.link); | ||
80 | } | ||
81 | |||
59 | void ipc_init(struct sway_server *server) { | 82 | void ipc_init(struct sway_server *server) { |
60 | ipc_socket = socket(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0); | 83 | ipc_socket = socket(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0); |
61 | if (ipc_socket == -1) { | 84 | if (ipc_socket == -1) { |
@@ -85,24 +108,13 @@ void ipc_init(struct sway_server *server) { | |||
85 | 108 | ||
86 | ipc_client_list = create_list(); | 109 | ipc_client_list = create_list(); |
87 | 110 | ||
111 | ipc_display_destroy.notify = handle_display_destroy; | ||
112 | wl_display_add_destroy_listener(server->wl_display, &ipc_display_destroy); | ||
113 | |||
88 | ipc_event_source = wl_event_loop_add_fd(server->wl_event_loop, ipc_socket, | 114 | ipc_event_source = wl_event_loop_add_fd(server->wl_event_loop, ipc_socket, |
89 | WL_EVENT_READABLE, ipc_handle_connection, server); | 115 | WL_EVENT_READABLE, ipc_handle_connection, server); |
90 | } | 116 | } |
91 | 117 | ||
92 | void ipc_terminate(void) { | ||
93 | if (ipc_event_source) { | ||
94 | wl_event_source_remove(ipc_event_source); | ||
95 | } | ||
96 | close(ipc_socket); | ||
97 | unlink(ipc_sockaddr->sun_path); | ||
98 | |||
99 | list_free(ipc_client_list); | ||
100 | |||
101 | if (ipc_sockaddr) { | ||
102 | free(ipc_sockaddr); | ||
103 | } | ||
104 | } | ||
105 | |||
106 | struct sockaddr_un *ipc_user_sockaddr(void) { | 118 | struct sockaddr_un *ipc_user_sockaddr(void) { |
107 | struct sockaddr_un *ipc_sockaddr = malloc(sizeof(struct sockaddr_un)); | 119 | struct sockaddr_un *ipc_sockaddr = malloc(sizeof(struct sockaddr_un)); |
108 | if (ipc_sockaddr == NULL) { | 120 | if (ipc_sockaddr == NULL) { |
@@ -128,32 +140,32 @@ struct sockaddr_un *ipc_user_sockaddr(void) { | |||
128 | int ipc_handle_connection(int fd, uint32_t mask, void *data) { | 140 | int ipc_handle_connection(int fd, uint32_t mask, void *data) { |
129 | (void) fd; | 141 | (void) fd; |
130 | struct sway_server *server = data; | 142 | struct sway_server *server = data; |
131 | wlr_log(L_DEBUG, "Event on IPC listening socket"); | 143 | wlr_log(WLR_DEBUG, "Event on IPC listening socket"); |
132 | assert(mask == WL_EVENT_READABLE); | 144 | assert(mask == WL_EVENT_READABLE); |
133 | 145 | ||
134 | int client_fd = accept(ipc_socket, NULL, NULL); | 146 | int client_fd = accept(ipc_socket, NULL, NULL); |
135 | if (client_fd == -1) { | 147 | if (client_fd == -1) { |
136 | wlr_log_errno(L_ERROR, "Unable to accept IPC client connection"); | 148 | wlr_log_errno(WLR_ERROR, "Unable to accept IPC client connection"); |
137 | return 0; | 149 | return 0; |
138 | } | 150 | } |
139 | 151 | ||
140 | int flags; | 152 | int flags; |
141 | if ((flags = fcntl(client_fd, F_GETFD)) == -1 | 153 | if ((flags = fcntl(client_fd, F_GETFD)) == -1 |
142 | || fcntl(client_fd, F_SETFD, flags|FD_CLOEXEC) == -1) { | 154 | || fcntl(client_fd, F_SETFD, flags|FD_CLOEXEC) == -1) { |
143 | wlr_log_errno(L_ERROR, "Unable to set CLOEXEC on IPC client socket"); | 155 | wlr_log_errno(WLR_ERROR, "Unable to set CLOEXEC on IPC client socket"); |
144 | close(client_fd); | 156 | close(client_fd); |
145 | return 0; | 157 | return 0; |
146 | } | 158 | } |
147 | if ((flags = fcntl(client_fd, F_GETFL)) == -1 | 159 | if ((flags = fcntl(client_fd, F_GETFL)) == -1 |
148 | || fcntl(client_fd, F_SETFL, flags|O_NONBLOCK) == -1) { | 160 | || fcntl(client_fd, F_SETFL, flags|O_NONBLOCK) == -1) { |
149 | wlr_log_errno(L_ERROR, "Unable to set NONBLOCK on IPC client socket"); | 161 | wlr_log_errno(WLR_ERROR, "Unable to set NONBLOCK on IPC client socket"); |
150 | close(client_fd); | 162 | close(client_fd); |
151 | return 0; | 163 | return 0; |
152 | } | 164 | } |
153 | 165 | ||
154 | struct ipc_client *client = malloc(sizeof(struct ipc_client)); | 166 | struct ipc_client *client = malloc(sizeof(struct ipc_client)); |
155 | if (!client) { | 167 | if (!client) { |
156 | wlr_log(L_ERROR, "Unable to allocate ipc client"); | 168 | wlr_log(WLR_ERROR, "Unable to allocate ipc client"); |
157 | close(client_fd); | 169 | close(client_fd); |
158 | return 0; | 170 | return 0; |
159 | } | 171 | } |
@@ -169,12 +181,12 @@ int ipc_handle_connection(int fd, uint32_t mask, void *data) { | |||
169 | client->write_buffer_len = 0; | 181 | client->write_buffer_len = 0; |
170 | client->write_buffer = malloc(client->write_buffer_size); | 182 | client->write_buffer = malloc(client->write_buffer_size); |
171 | if (!client->write_buffer) { | 183 | if (!client->write_buffer) { |
172 | wlr_log(L_ERROR, "Unable to allocate ipc client write buffer"); | 184 | wlr_log(WLR_ERROR, "Unable to allocate ipc client write buffer"); |
173 | close(client_fd); | 185 | close(client_fd); |
174 | return 0; | 186 | return 0; |
175 | } | 187 | } |
176 | 188 | ||
177 | wlr_log(L_DEBUG, "New client: fd %d", client_fd); | 189 | wlr_log(WLR_DEBUG, "New client: fd %d", client_fd); |
178 | list_add(ipc_client_list, client); | 190 | list_add(ipc_client_list, client); |
179 | return 0; | 191 | return 0; |
180 | } | 192 | } |
@@ -185,22 +197,22 @@ int ipc_client_handle_readable(int client_fd, uint32_t mask, void *data) { | |||
185 | struct ipc_client *client = data; | 197 | struct ipc_client *client = data; |
186 | 198 | ||
187 | if (mask & WL_EVENT_ERROR) { | 199 | if (mask & WL_EVENT_ERROR) { |
188 | wlr_log(L_ERROR, "IPC Client socket error, removing client"); | 200 | wlr_log(WLR_ERROR, "IPC Client socket error, removing client"); |
189 | ipc_client_disconnect(client); | 201 | ipc_client_disconnect(client); |
190 | return 0; | 202 | return 0; |
191 | } | 203 | } |
192 | 204 | ||
193 | if (mask & WL_EVENT_HANGUP) { | 205 | if (mask & WL_EVENT_HANGUP) { |
194 | wlr_log(L_DEBUG, "Client %d hung up", client->fd); | 206 | wlr_log(WLR_DEBUG, "Client %d hung up", client->fd); |
195 | ipc_client_disconnect(client); | 207 | ipc_client_disconnect(client); |
196 | return 0; | 208 | return 0; |
197 | } | 209 | } |
198 | 210 | ||
199 | wlr_log(L_DEBUG, "Client %d readable", client->fd); | 211 | wlr_log(WLR_DEBUG, "Client %d readable", client->fd); |
200 | 212 | ||
201 | int read_available; | 213 | int read_available; |
202 | if (ioctl(client_fd, FIONREAD, &read_available) == -1) { | 214 | if (ioctl(client_fd, FIONREAD, &read_available) == -1) { |
203 | wlr_log_errno(L_INFO, "Unable to read IPC socket buffer size"); | 215 | wlr_log_errno(WLR_INFO, "Unable to read IPC socket buffer size"); |
204 | ipc_client_disconnect(client); | 216 | ipc_client_disconnect(client); |
205 | return 0; | 217 | return 0; |
206 | } | 218 | } |
@@ -222,13 +234,13 @@ int ipc_client_handle_readable(int client_fd, uint32_t mask, void *data) { | |||
222 | // Should be fully available, because read_available >= ipc_header_size | 234 | // Should be fully available, because read_available >= ipc_header_size |
223 | ssize_t received = recv(client_fd, buf, ipc_header_size, 0); | 235 | ssize_t received = recv(client_fd, buf, ipc_header_size, 0); |
224 | if (received == -1) { | 236 | if (received == -1) { |
225 | wlr_log_errno(L_INFO, "Unable to receive header from IPC client"); | 237 | wlr_log_errno(WLR_INFO, "Unable to receive header from IPC client"); |
226 | ipc_client_disconnect(client); | 238 | ipc_client_disconnect(client); |
227 | return 0; | 239 | return 0; |
228 | } | 240 | } |
229 | 241 | ||
230 | if (memcmp(buf, ipc_magic, sizeof(ipc_magic)) != 0) { | 242 | if (memcmp(buf, ipc_magic, sizeof(ipc_magic)) != 0) { |
231 | wlr_log(L_DEBUG, "IPC header check failed"); | 243 | wlr_log(WLR_DEBUG, "IPC header check failed"); |
232 | ipc_client_disconnect(client); | 244 | ipc_client_disconnect(client); |
233 | return 0; | 245 | return 0; |
234 | } | 246 | } |
@@ -262,8 +274,11 @@ static void ipc_send_event(const char *json_string, enum ipc_command_type event) | |||
262 | } | 274 | } |
263 | client->current_command = event; | 275 | client->current_command = event; |
264 | if (!ipc_send_reply(client, json_string, (uint32_t) strlen(json_string))) { | 276 | if (!ipc_send_reply(client, json_string, (uint32_t) strlen(json_string))) { |
265 | wlr_log_errno(L_INFO, "Unable to send reply to IPC client"); | 277 | wlr_log_errno(WLR_INFO, "Unable to send reply to IPC client"); |
266 | ipc_client_disconnect(client); | 278 | /* ipc_send_reply destroys client on error, which also |
279 | * removes it from the list, so we need to process | ||
280 | * current index again */ | ||
281 | i--; | ||
267 | } | 282 | } |
268 | } | 283 | } |
269 | } | 284 | } |
@@ -273,7 +288,7 @@ void ipc_event_workspace(struct sway_container *old, | |||
273 | if (!ipc_has_event_listeners(IPC_EVENT_WORKSPACE)) { | 288 | if (!ipc_has_event_listeners(IPC_EVENT_WORKSPACE)) { |
274 | return; | 289 | return; |
275 | } | 290 | } |
276 | wlr_log(L_DEBUG, "Sending workspace::%s event", change); | 291 | wlr_log(WLR_DEBUG, "Sending workspace::%s event", change); |
277 | json_object *obj = json_object_new_object(); | 292 | json_object *obj = json_object_new_object(); |
278 | json_object_object_add(obj, "change", json_object_new_string(change)); | 293 | json_object_object_add(obj, "change", json_object_new_string(change)); |
279 | if (strcmp("focus", change) == 0) { | 294 | if (strcmp("focus", change) == 0) { |
@@ -301,7 +316,7 @@ void ipc_event_window(struct sway_container *window, const char *change) { | |||
301 | if (!ipc_has_event_listeners(IPC_EVENT_WINDOW)) { | 316 | if (!ipc_has_event_listeners(IPC_EVENT_WINDOW)) { |
302 | return; | 317 | return; |
303 | } | 318 | } |
304 | wlr_log(L_DEBUG, "Sending window::%s event", change); | 319 | wlr_log(WLR_DEBUG, "Sending window::%s event", change); |
305 | json_object *obj = json_object_new_object(); | 320 | json_object *obj = json_object_new_object(); |
306 | json_object_object_add(obj, "change", json_object_new_string(change)); | 321 | json_object_object_add(obj, "change", json_object_new_string(change)); |
307 | json_object_object_add(obj, "container", ipc_json_describe_container_recursive(window)); | 322 | json_object_object_add(obj, "container", ipc_json_describe_container_recursive(window)); |
@@ -315,7 +330,7 @@ void ipc_event_barconfig_update(struct bar_config *bar) { | |||
315 | if (!ipc_has_event_listeners(IPC_EVENT_BARCONFIG_UPDATE)) { | 330 | if (!ipc_has_event_listeners(IPC_EVENT_BARCONFIG_UPDATE)) { |
316 | return; | 331 | return; |
317 | } | 332 | } |
318 | wlr_log(L_DEBUG, "Sending barconfig_update event"); | 333 | wlr_log(WLR_DEBUG, "Sending barconfig_update event"); |
319 | json_object *json = ipc_json_describe_bar_config(bar); | 334 | json_object *json = ipc_json_describe_bar_config(bar); |
320 | 335 | ||
321 | const char *json_string = json_object_to_json_string(json); | 336 | const char *json_string = json_object_to_json_string(json); |
@@ -323,13 +338,15 @@ void ipc_event_barconfig_update(struct bar_config *bar) { | |||
323 | json_object_put(json); | 338 | json_object_put(json); |
324 | } | 339 | } |
325 | 340 | ||
326 | void ipc_event_mode(const char *mode) { | 341 | void ipc_event_mode(const char *mode, bool pango) { |
327 | if (!ipc_has_event_listeners(IPC_EVENT_MODE)) { | 342 | if (!ipc_has_event_listeners(IPC_EVENT_MODE)) { |
328 | return; | 343 | return; |
329 | } | 344 | } |
330 | wlr_log(L_DEBUG, "Sending mode::%s event", mode); | 345 | wlr_log(WLR_DEBUG, "Sending mode::%s event", mode); |
331 | json_object *obj = json_object_new_object(); | 346 | json_object *obj = json_object_new_object(); |
332 | json_object_object_add(obj, "change", json_object_new_string(mode)); | 347 | json_object_object_add(obj, "change", json_object_new_string(mode)); |
348 | json_object_object_add(obj, "pango_markup", | ||
349 | json_object_new_boolean(pango)); | ||
333 | 350 | ||
334 | const char *json_string = json_object_to_json_string(obj); | 351 | const char *json_string = json_object_to_json_string(obj); |
335 | ipc_send_event(json_string, IPC_EVENT_MODE); | 352 | ipc_send_event(json_string, IPC_EVENT_MODE); |
@@ -340,13 +357,13 @@ int ipc_client_handle_writable(int client_fd, uint32_t mask, void *data) { | |||
340 | struct ipc_client *client = data; | 357 | struct ipc_client *client = data; |
341 | 358 | ||
342 | if (mask & WL_EVENT_ERROR) { | 359 | if (mask & WL_EVENT_ERROR) { |
343 | wlr_log(L_ERROR, "IPC Client socket error, removing client"); | 360 | wlr_log(WLR_ERROR, "IPC Client socket error, removing client"); |
344 | ipc_client_disconnect(client); | 361 | ipc_client_disconnect(client); |
345 | return 0; | 362 | return 0; |
346 | } | 363 | } |
347 | 364 | ||
348 | if (mask & WL_EVENT_HANGUP) { | 365 | if (mask & WL_EVENT_HANGUP) { |
349 | wlr_log(L_DEBUG, "Client %d hung up", client->fd); | 366 | wlr_log(WLR_DEBUG, "Client %d hung up", client->fd); |
350 | ipc_client_disconnect(client); | 367 | ipc_client_disconnect(client); |
351 | return 0; | 368 | return 0; |
352 | } | 369 | } |
@@ -355,14 +372,14 @@ int ipc_client_handle_writable(int client_fd, uint32_t mask, void *data) { | |||
355 | return 0; | 372 | return 0; |
356 | } | 373 | } |
357 | 374 | ||
358 | wlr_log(L_DEBUG, "Client %d writable", client->fd); | 375 | wlr_log(WLR_DEBUG, "Client %d writable", client->fd); |
359 | 376 | ||
360 | ssize_t written = write(client->fd, client->write_buffer, client->write_buffer_len); | 377 | ssize_t written = write(client->fd, client->write_buffer, client->write_buffer_len); |
361 | 378 | ||
362 | if (written == -1 && errno == EAGAIN) { | 379 | if (written == -1 && errno == EAGAIN) { |
363 | return 0; | 380 | return 0; |
364 | } else if (written == -1) { | 381 | } else if (written == -1) { |
365 | wlr_log_errno(L_INFO, "Unable to send data from queue to IPC client"); | 382 | wlr_log_errno(WLR_INFO, "Unable to send data from queue to IPC client"); |
366 | ipc_client_disconnect(client); | 383 | ipc_client_disconnect(client); |
367 | return 0; | 384 | return 0; |
368 | } | 385 | } |
@@ -383,11 +400,9 @@ void ipc_client_disconnect(struct ipc_client *client) { | |||
383 | return; | 400 | return; |
384 | } | 401 | } |
385 | 402 | ||
386 | if (client->fd != -1) { | 403 | shutdown(client->fd, SHUT_RDWR); |
387 | shutdown(client->fd, SHUT_RDWR); | ||
388 | } | ||
389 | 404 | ||
390 | wlr_log(L_INFO, "IPC Client %d disconnected", client->fd); | 405 | wlr_log(WLR_INFO, "IPC Client %d disconnected", client->fd); |
391 | wl_event_source_remove(client->event_source); | 406 | wl_event_source_remove(client->event_source); |
392 | if (client->writable_event_source) { | 407 | if (client->writable_event_source) { |
393 | wl_event_source_remove(client->writable_event_source); | 408 | wl_event_source_remove(client->writable_event_source); |
@@ -448,7 +463,7 @@ void ipc_client_handle_command(struct ipc_client *client) { | |||
448 | 463 | ||
449 | char *buf = malloc(client->payload_length + 1); | 464 | char *buf = malloc(client->payload_length + 1); |
450 | if (!buf) { | 465 | if (!buf) { |
451 | wlr_log_errno(L_INFO, "Unable to allocate IPC payload"); | 466 | wlr_log_errno(WLR_INFO, "Unable to allocate IPC payload"); |
452 | ipc_client_disconnect(client); | 467 | ipc_client_disconnect(client); |
453 | return; | 468 | return; |
454 | } | 469 | } |
@@ -457,7 +472,7 @@ void ipc_client_handle_command(struct ipc_client *client) { | |||
457 | ssize_t received = recv(client->fd, buf, client->payload_length, 0); | 472 | ssize_t received = recv(client->fd, buf, client->payload_length, 0); |
458 | if (received == -1) | 473 | if (received == -1) |
459 | { | 474 | { |
460 | wlr_log_errno(L_INFO, "Unable to receive payload from IPC client"); | 475 | wlr_log_errno(WLR_INFO, "Unable to receive payload from IPC client"); |
461 | ipc_client_disconnect(client); | 476 | ipc_client_disconnect(client); |
462 | free(buf); | 477 | free(buf); |
463 | return; | 478 | return; |
@@ -465,16 +480,16 @@ void ipc_client_handle_command(struct ipc_client *client) { | |||
465 | } | 480 | } |
466 | buf[client->payload_length] = '\0'; | 481 | buf[client->payload_length] = '\0'; |
467 | 482 | ||
468 | const char *error_denied = "{ \"success\": false, \"error\": \"Permission denied\" }"; | 483 | bool client_valid = true; |
469 | |||
470 | switch (client->current_command) { | 484 | switch (client->current_command) { |
471 | case IPC_COMMAND: | 485 | case IPC_COMMAND: |
472 | { | 486 | { |
473 | struct cmd_results *results = execute_command(buf, NULL); | 487 | struct cmd_results *results = execute_command(buf, NULL); |
474 | const char *json = cmd_results_to_json(results); | 488 | transaction_commit_dirty(); |
475 | char reply[256]; | 489 | char *json = cmd_results_to_json(results); |
476 | int length = snprintf(reply, sizeof(reply), "%s", json); | 490 | int length = strlen(json); |
477 | ipc_send_reply(client, reply, (uint32_t) length); | 491 | client_valid = ipc_send_reply(client, json, (uint32_t)length); |
492 | free(json); | ||
478 | free_cmd_results(results); | 493 | free_cmd_results(results); |
479 | goto exit_cleanup; | 494 | goto exit_cleanup; |
480 | } | 495 | } |
@@ -497,7 +512,8 @@ void ipc_client_handle_command(struct ipc_client *client) { | |||
497 | } | 512 | } |
498 | } | 513 | } |
499 | const char *json_string = json_object_to_json_string(outputs); | 514 | const char *json_string = json_object_to_json_string(outputs); |
500 | ipc_send_reply(client, json_string, (uint32_t) strlen(json_string)); | 515 | client_valid = |
516 | ipc_send_reply(client, json_string, (uint32_t)strlen(json_string)); | ||
501 | json_object_put(outputs); // free | 517 | json_object_put(outputs); // free |
502 | goto exit_cleanup; | 518 | goto exit_cleanup; |
503 | } | 519 | } |
@@ -508,7 +524,8 @@ void ipc_client_handle_command(struct ipc_client *client) { | |||
508 | container_for_each_descendant_dfs(&root_container, | 524 | container_for_each_descendant_dfs(&root_container, |
509 | ipc_get_workspaces_callback, workspaces); | 525 | ipc_get_workspaces_callback, workspaces); |
510 | const char *json_string = json_object_to_json_string(workspaces); | 526 | const char *json_string = json_object_to_json_string(workspaces); |
511 | ipc_send_reply(client, json_string, (uint32_t) strlen(json_string)); | 527 | client_valid = |
528 | ipc_send_reply(client, json_string, (uint32_t)strlen(json_string)); | ||
512 | json_object_put(workspaces); // free | 529 | json_object_put(workspaces); // free |
513 | goto exit_cleanup; | 530 | goto exit_cleanup; |
514 | } | 531 | } |
@@ -518,8 +535,8 @@ void ipc_client_handle_command(struct ipc_client *client) { | |||
518 | // TODO: Check if they're permitted to use these events | 535 | // TODO: Check if they're permitted to use these events |
519 | struct json_object *request = json_tokener_parse(buf); | 536 | struct json_object *request = json_tokener_parse(buf); |
520 | if (request == NULL) { | 537 | if (request == NULL) { |
521 | ipc_send_reply(client, "{\"success\": false}", 18); | 538 | client_valid = ipc_send_reply(client, "{\"success\": false}", 18); |
522 | wlr_log_errno(L_INFO, "Failed to read request"); | 539 | wlr_log_errno(WLR_INFO, "Failed to read request"); |
523 | goto exit_cleanup; | 540 | goto exit_cleanup; |
524 | } | 541 | } |
525 | 542 | ||
@@ -539,15 +556,16 @@ void ipc_client_handle_command(struct ipc_client *client) { | |||
539 | } else if (strcmp(event_type, "binding") == 0) { | 556 | } else if (strcmp(event_type, "binding") == 0) { |
540 | client->subscribed_events |= event_mask(IPC_EVENT_BINDING); | 557 | client->subscribed_events |= event_mask(IPC_EVENT_BINDING); |
541 | } else { | 558 | } else { |
542 | ipc_send_reply(client, "{\"success\": false}", 18); | 559 | client_valid = |
560 | ipc_send_reply(client, "{\"success\": false}", 18); | ||
543 | json_object_put(request); | 561 | json_object_put(request); |
544 | wlr_log_errno(L_INFO, "Failed to parse request"); | 562 | wlr_log_errno(WLR_INFO, "Failed to parse request"); |
545 | goto exit_cleanup; | 563 | goto exit_cleanup; |
546 | } | 564 | } |
547 | } | 565 | } |
548 | 566 | ||
549 | json_object_put(request); | 567 | json_object_put(request); |
550 | ipc_send_reply(client, "{\"success\": true}", 17); | 568 | client_valid = ipc_send_reply(client, "{\"success\": true}", 17); |
551 | goto exit_cleanup; | 569 | goto exit_cleanup; |
552 | } | 570 | } |
553 | 571 | ||
@@ -559,7 +577,8 @@ void ipc_client_handle_command(struct ipc_client *client) { | |||
559 | json_object_array_add(inputs, ipc_json_describe_input(device)); | 577 | json_object_array_add(inputs, ipc_json_describe_input(device)); |
560 | } | 578 | } |
561 | const char *json_string = json_object_to_json_string(inputs); | 579 | const char *json_string = json_object_to_json_string(inputs); |
562 | ipc_send_reply(client, json_string, (uint32_t)strlen(json_string)); | 580 | client_valid = |
581 | ipc_send_reply(client, json_string, (uint32_t)strlen(json_string)); | ||
563 | json_object_put(inputs); // free | 582 | json_object_put(inputs); // free |
564 | goto exit_cleanup; | 583 | goto exit_cleanup; |
565 | } | 584 | } |
@@ -572,7 +591,8 @@ void ipc_client_handle_command(struct ipc_client *client) { | |||
572 | json_object_array_add(seats, ipc_json_describe_seat(seat)); | 591 | json_object_array_add(seats, ipc_json_describe_seat(seat)); |
573 | } | 592 | } |
574 | const char *json_string = json_object_to_json_string(seats); | 593 | const char *json_string = json_object_to_json_string(seats); |
575 | ipc_send_reply(client, json_string, (uint32_t)strlen(json_string)); | 594 | client_valid = |
595 | ipc_send_reply(client, json_string, (uint32_t)strlen(json_string)); | ||
576 | json_object_put(seats); // free | 596 | json_object_put(seats); // free |
577 | goto exit_cleanup; | 597 | goto exit_cleanup; |
578 | } | 598 | } |
@@ -582,7 +602,8 @@ void ipc_client_handle_command(struct ipc_client *client) { | |||
582 | json_object *tree = | 602 | json_object *tree = |
583 | ipc_json_describe_container_recursive(&root_container); | 603 | ipc_json_describe_container_recursive(&root_container); |
584 | const char *json_string = json_object_to_json_string(tree); | 604 | const char *json_string = json_object_to_json_string(tree); |
585 | ipc_send_reply(client, json_string, (uint32_t) strlen(json_string)); | 605 | client_valid = |
606 | ipc_send_reply(client, json_string, (uint32_t) strlen(json_string)); | ||
586 | json_object_put(tree); | 607 | json_object_put(tree); |
587 | goto exit_cleanup; | 608 | goto exit_cleanup; |
588 | } | 609 | } |
@@ -593,7 +614,8 @@ void ipc_client_handle_command(struct ipc_client *client) { | |||
593 | container_descendants(&root_container, C_VIEW, ipc_get_marks_callback, | 614 | container_descendants(&root_container, C_VIEW, ipc_get_marks_callback, |
594 | marks); | 615 | marks); |
595 | const char *json_string = json_object_to_json_string(marks); | 616 | const char *json_string = json_object_to_json_string(marks); |
596 | ipc_send_reply(client, json_string, (uint32_t)strlen(json_string)); | 617 | client_valid = |
618 | ipc_send_reply(client, json_string, (uint32_t)strlen(json_string)); | ||
597 | json_object_put(marks); | 619 | json_object_put(marks); |
598 | goto exit_cleanup; | 620 | goto exit_cleanup; |
599 | } | 621 | } |
@@ -602,7 +624,8 @@ void ipc_client_handle_command(struct ipc_client *client) { | |||
602 | { | 624 | { |
603 | json_object *version = ipc_json_get_version(); | 625 | json_object *version = ipc_json_get_version(); |
604 | const char *json_string = json_object_to_json_string(version); | 626 | const char *json_string = json_object_to_json_string(version); |
605 | ipc_send_reply(client, json_string, (uint32_t)strlen(json_string)); | 627 | client_valid = |
628 | ipc_send_reply(client, json_string, (uint32_t)strlen(json_string)); | ||
606 | json_object_put(version); // free | 629 | json_object_put(version); // free |
607 | goto exit_cleanup; | 630 | goto exit_cleanup; |
608 | } | 631 | } |
@@ -617,7 +640,9 @@ void ipc_client_handle_command(struct ipc_client *client) { | |||
617 | json_object_array_add(bars, json_object_new_string(bar->id)); | 640 | json_object_array_add(bars, json_object_new_string(bar->id)); |
618 | } | 641 | } |
619 | const char *json_string = json_object_to_json_string(bars); | 642 | const char *json_string = json_object_to_json_string(bars); |
620 | ipc_send_reply(client, json_string, (uint32_t)strlen(json_string)); | 643 | client_valid = |
644 | ipc_send_reply(client, json_string, | ||
645 | (uint32_t)strlen(json_string)); | ||
621 | json_object_put(bars); // free | 646 | json_object_put(bars); // free |
622 | } else { | 647 | } else { |
623 | // Send particular bar's details | 648 | // Send particular bar's details |
@@ -631,27 +656,54 @@ void ipc_client_handle_command(struct ipc_client *client) { | |||
631 | } | 656 | } |
632 | if (!bar) { | 657 | if (!bar) { |
633 | const char *error = "{ \"success\": false, \"error\": \"No bar with that ID\" }"; | 658 | const char *error = "{ \"success\": false, \"error\": \"No bar with that ID\" }"; |
634 | ipc_send_reply(client, error, (uint32_t)strlen(error)); | 659 | client_valid = |
660 | ipc_send_reply(client, error, (uint32_t)strlen(error)); | ||
635 | goto exit_cleanup; | 661 | goto exit_cleanup; |
636 | } | 662 | } |
637 | json_object *json = ipc_json_describe_bar_config(bar); | 663 | json_object *json = ipc_json_describe_bar_config(bar); |
638 | const char *json_string = json_object_to_json_string(json); | 664 | const char *json_string = json_object_to_json_string(json); |
639 | ipc_send_reply(client, json_string, (uint32_t)strlen(json_string)); | 665 | client_valid = |
666 | ipc_send_reply(client, json_string, | ||
667 | (uint32_t)strlen(json_string)); | ||
640 | json_object_put(json); // free | 668 | json_object_put(json); // free |
641 | } | 669 | } |
642 | goto exit_cleanup; | 670 | goto exit_cleanup; |
643 | } | 671 | } |
644 | 672 | ||
645 | default: | 673 | case IPC_GET_BINDING_MODES: |
646 | wlr_log(L_INFO, "Unknown IPC command type %i", client->current_command); | 674 | { |
675 | json_object *modes = json_object_new_array(); | ||
676 | for (int i = 0; i < config->modes->length; i++) { | ||
677 | struct sway_mode *mode = config->modes->items[i]; | ||
678 | json_object_array_add(modes, json_object_new_string(mode->name)); | ||
679 | } | ||
680 | const char *json_string = json_object_to_json_string(modes); | ||
681 | client_valid = | ||
682 | ipc_send_reply(client, json_string, (uint32_t)strlen(json_string)); | ||
683 | json_object_put(modes); // free | ||
647 | goto exit_cleanup; | 684 | goto exit_cleanup; |
648 | } | 685 | } |
649 | 686 | ||
650 | ipc_send_reply(client, error_denied, (uint32_t)strlen(error_denied)); | 687 | case IPC_GET_CONFIG: |
651 | wlr_log(L_DEBUG, "Denied IPC client access to %i", client->current_command); | 688 | { |
689 | json_object *json = json_object_new_object(); | ||
690 | json_object_object_add(json, "config", json_object_new_string(config->current_config)); | ||
691 | const char *json_string = json_object_to_json_string(json); | ||
692 | client_valid = | ||
693 | ipc_send_reply(client, json_string, (uint32_t)strlen(json_string)); | ||
694 | json_object_put(json); // free | ||
695 | goto exit_cleanup; | ||
696 | } | ||
697 | |||
698 | default: | ||
699 | wlr_log(WLR_INFO, "Unknown IPC command type %i", client->current_command); | ||
700 | goto exit_cleanup; | ||
701 | } | ||
652 | 702 | ||
653 | exit_cleanup: | 703 | exit_cleanup: |
654 | client->payload_length = 0; | 704 | if (client_valid) { |
705 | client->payload_length = 0; | ||
706 | } | ||
655 | free(buf); | 707 | free(buf); |
656 | return; | 708 | return; |
657 | } | 709 | } |
@@ -672,14 +724,14 @@ bool ipc_send_reply(struct ipc_client *client, const char *payload, uint32_t pay | |||
672 | } | 724 | } |
673 | 725 | ||
674 | if (client->write_buffer_size > 4e6) { // 4 MB | 726 | if (client->write_buffer_size > 4e6) { // 4 MB |
675 | wlr_log(L_ERROR, "Client write buffer too big, disconnecting client"); | 727 | wlr_log(WLR_ERROR, "Client write buffer too big, disconnecting client"); |
676 | ipc_client_disconnect(client); | 728 | ipc_client_disconnect(client); |
677 | return false; | 729 | return false; |
678 | } | 730 | } |
679 | 731 | ||
680 | char *new_buffer = realloc(client->write_buffer, client->write_buffer_size); | 732 | char *new_buffer = realloc(client->write_buffer, client->write_buffer_size); |
681 | if (!new_buffer) { | 733 | if (!new_buffer) { |
682 | wlr_log(L_ERROR, "Unable to reallocate ipc client write buffer"); | 734 | wlr_log(WLR_ERROR, "Unable to reallocate ipc client write buffer"); |
683 | ipc_client_disconnect(client); | 735 | ipc_client_disconnect(client); |
684 | return false; | 736 | return false; |
685 | } | 737 | } |
@@ -696,6 +748,6 @@ bool ipc_send_reply(struct ipc_client *client, const char *payload, uint32_t pay | |||
696 | ipc_client_handle_writable, client); | 748 | ipc_client_handle_writable, client); |
697 | } | 749 | } |
698 | 750 | ||
699 | wlr_log(L_DEBUG, "Added IPC reply to client %d queue: %s", client->fd, payload); | 751 | wlr_log(WLR_DEBUG, "Added IPC reply to client %d queue: %s", client->fd, payload); |
700 | return true; | 752 | return true; |
701 | } | 753 | } |
diff --git a/sway/main.c b/sway/main.c index a325dc3a..a20f1dac 100644 --- a/sway/main.c +++ b/sway/main.c | |||
@@ -1,6 +1,7 @@ | |||
1 | #define _XOPEN_SOURCE 700 | 1 | #define _XOPEN_SOURCE 700 |
2 | #define _POSIX_C_SOURCE 200112L | 2 | #define _POSIX_C_SOURCE 200112L |
3 | #include <getopt.h> | 3 | #include <getopt.h> |
4 | #include <pango/pangocairo.h> | ||
4 | #include <signal.h> | 5 | #include <signal.h> |
5 | #include <stdbool.h> | 6 | #include <stdbool.h> |
6 | #include <stdlib.h> | 7 | #include <stdlib.h> |
@@ -19,6 +20,7 @@ | |||
19 | #include "sway/commands.h" | 20 | #include "sway/commands.h" |
20 | #include "sway/config.h" | 21 | #include "sway/config.h" |
21 | #include "sway/debug.h" | 22 | #include "sway/debug.h" |
23 | #include "sway/desktop/transaction.h" | ||
22 | #include "sway/server.h" | 24 | #include "sway/server.h" |
23 | #include "sway/tree/layout.h" | 25 | #include "sway/tree/layout.h" |
24 | #include "sway/ipc-server.h" | 26 | #include "sway/ipc-server.h" |
@@ -128,7 +130,7 @@ static void log_env() { | |||
128 | "SWAYSOCK" | 130 | "SWAYSOCK" |
129 | }; | 131 | }; |
130 | for (size_t i = 0; i < sizeof(log_vars) / sizeof(char *); ++i) { | 132 | for (size_t i = 0; i < sizeof(log_vars) / sizeof(char *); ++i) { |
131 | wlr_log(L_INFO, "%s=%s", log_vars[i], getenv(log_vars[i])); | 133 | wlr_log(WLR_INFO, "%s=%s", log_vars[i], getenv(log_vars[i])); |
132 | } | 134 | } |
133 | } | 135 | } |
134 | 136 | ||
@@ -143,14 +145,14 @@ static void log_distro() { | |||
143 | for (size_t i = 0; i < sizeof(paths) / sizeof(char *); ++i) { | 145 | for (size_t i = 0; i < sizeof(paths) / sizeof(char *); ++i) { |
144 | FILE *f = fopen(paths[i], "r"); | 146 | FILE *f = fopen(paths[i], "r"); |
145 | if (f) { | 147 | if (f) { |
146 | wlr_log(L_INFO, "Contents of %s:", paths[i]); | 148 | wlr_log(WLR_INFO, "Contents of %s:", paths[i]); |
147 | while (!feof(f)) { | 149 | while (!feof(f)) { |
148 | char *line; | 150 | char *line; |
149 | if (!(line = read_line(f))) { | 151 | if (!(line = read_line(f))) { |
150 | break; | 152 | break; |
151 | } | 153 | } |
152 | if (*line) { | 154 | if (*line) { |
153 | wlr_log(L_INFO, "%s", line); | 155 | wlr_log(WLR_INFO, "%s", line); |
154 | } | 156 | } |
155 | free(line); | 157 | free(line); |
156 | } | 158 | } |
@@ -162,7 +164,7 @@ static void log_distro() { | |||
162 | static void log_kernel() { | 164 | static void log_kernel() { |
163 | FILE *f = popen("uname -a", "r"); | 165 | FILE *f = popen("uname -a", "r"); |
164 | if (!f) { | 166 | if (!f) { |
165 | wlr_log(L_INFO, "Unable to determine kernel version"); | 167 | wlr_log(WLR_INFO, "Unable to determine kernel version"); |
166 | return; | 168 | return; |
167 | } | 169 | } |
168 | while (!feof(f)) { | 170 | while (!feof(f)) { |
@@ -171,25 +173,25 @@ static void log_kernel() { | |||
171 | break; | 173 | break; |
172 | } | 174 | } |
173 | if (*line) { | 175 | if (*line) { |
174 | wlr_log(L_INFO, "%s", line); | 176 | wlr_log(WLR_INFO, "%s", line); |
175 | } | 177 | } |
176 | free(line); | 178 | free(line); |
177 | } | 179 | } |
178 | fclose(f); | 180 | pclose(f); |
179 | } | 181 | } |
180 | 182 | ||
181 | static void security_sanity_check() { | 183 | static void security_sanity_check() { |
182 | // TODO: Notify users visually if this has issues | 184 | // TODO: Notify users visually if this has issues |
183 | struct stat s; | 185 | struct stat s; |
184 | if (stat("/proc", &s)) { | 186 | if (stat("/proc", &s)) { |
185 | wlr_log(L_ERROR, | 187 | wlr_log(WLR_ERROR, |
186 | "!! DANGER !! /proc is not available - sway CANNOT enforce security rules!"); | 188 | "!! DANGER !! /proc is not available - sway CANNOT enforce security rules!"); |
187 | } | 189 | } |
188 | #ifdef __linux__ | 190 | #ifdef __linux__ |
189 | cap_flag_value_t v; | 191 | cap_flag_value_t v; |
190 | cap_t cap = cap_get_proc(); | 192 | cap_t cap = cap_get_proc(); |
191 | if (!cap || cap_get_flag(cap, CAP_SYS_PTRACE, CAP_PERMITTED, &v) != 0 || v != CAP_SET) { | 193 | if (!cap || cap_get_flag(cap, CAP_SYS_PTRACE, CAP_PERMITTED, &v) != 0 || v != CAP_SET) { |
192 | wlr_log(L_ERROR, | 194 | wlr_log(WLR_ERROR, |
193 | "!! DANGER !! Sway does not have CAP_SYS_PTRACE and cannot enforce security rules for processes running as other users."); | 195 | "!! DANGER !! Sway does not have CAP_SYS_PTRACE and cannot enforce security rules for processes running as other users."); |
194 | } | 196 | } |
195 | if (cap) { | 197 | if (cap) { |
@@ -205,13 +207,13 @@ static void executable_sanity_check() { | |||
205 | stat(exe, &sb); | 207 | stat(exe, &sb); |
206 | // We assume that cap_get_file returning NULL implies ENODATA | 208 | // We assume that cap_get_file returning NULL implies ENODATA |
207 | if (sb.st_mode & (S_ISUID|S_ISGID) && cap_get_file(exe)) { | 209 | if (sb.st_mode & (S_ISUID|S_ISGID) && cap_get_file(exe)) { |
208 | wlr_log(L_ERROR, | 210 | wlr_log(WLR_ERROR, |
209 | "sway executable has both the s(g)uid bit AND file caps set."); | 211 | "sway executable has both the s(g)uid bit AND file caps set."); |
210 | wlr_log(L_ERROR, | 212 | wlr_log(WLR_ERROR, |
211 | "This is strongly discouraged (and completely broken)."); | 213 | "This is strongly discouraged (and completely broken)."); |
212 | wlr_log(L_ERROR, | 214 | wlr_log(WLR_ERROR, |
213 | "Please clear one of them (either the suid bit, or the file caps)."); | 215 | "Please clear one of them (either the suid bit, or the file caps)."); |
214 | wlr_log(L_ERROR, | 216 | wlr_log(WLR_ERROR, |
215 | "If unsure, strip the file caps."); | 217 | "If unsure, strip the file caps."); |
216 | exit(EXIT_FAILURE); | 218 | exit(EXIT_FAILURE); |
217 | } | 219 | } |
@@ -222,16 +224,16 @@ static void executable_sanity_check() { | |||
222 | static void drop_permissions(bool keep_caps) { | 224 | static void drop_permissions(bool keep_caps) { |
223 | if (getuid() != geteuid() || getgid() != getegid()) { | 225 | if (getuid() != geteuid() || getgid() != getegid()) { |
224 | if (setgid(getgid()) != 0) { | 226 | if (setgid(getgid()) != 0) { |
225 | wlr_log(L_ERROR, "Unable to drop root"); | 227 | wlr_log(WLR_ERROR, "Unable to drop root"); |
226 | exit(EXIT_FAILURE); | 228 | exit(EXIT_FAILURE); |
227 | } | 229 | } |
228 | if (setuid(getuid()) != 0) { | 230 | if (setuid(getuid()) != 0) { |
229 | wlr_log(L_ERROR, "Unable to drop root"); | 231 | wlr_log(WLR_ERROR, "Unable to drop root"); |
230 | exit(EXIT_FAILURE); | 232 | exit(EXIT_FAILURE); |
231 | } | 233 | } |
232 | } | 234 | } |
233 | if (setuid(0) != -1) { | 235 | if (setuid(0) != -1) { |
234 | wlr_log(L_ERROR, "Root privileges can be restored."); | 236 | wlr_log(WLR_ERROR, "Root privileges can be restored."); |
235 | exit(EXIT_FAILURE); | 237 | exit(EXIT_FAILURE); |
236 | } | 238 | } |
237 | #ifdef __linux__ | 239 | #ifdef __linux__ |
@@ -239,17 +241,29 @@ static void drop_permissions(bool keep_caps) { | |||
239 | // Drop every cap except CAP_SYS_PTRACE | 241 | // Drop every cap except CAP_SYS_PTRACE |
240 | cap_t caps = cap_init(); | 242 | cap_t caps = cap_init(); |
241 | cap_value_t keep = CAP_SYS_PTRACE; | 243 | cap_value_t keep = CAP_SYS_PTRACE; |
242 | wlr_log(L_INFO, "Dropping extra capabilities"); | 244 | wlr_log(WLR_INFO, "Dropping extra capabilities"); |
243 | if (cap_set_flag(caps, CAP_PERMITTED, 1, &keep, CAP_SET) || | 245 | if (cap_set_flag(caps, CAP_PERMITTED, 1, &keep, CAP_SET) || |
244 | cap_set_flag(caps, CAP_EFFECTIVE, 1, &keep, CAP_SET) || | 246 | cap_set_flag(caps, CAP_EFFECTIVE, 1, &keep, CAP_SET) || |
245 | cap_set_proc(caps)) { | 247 | cap_set_proc(caps)) { |
246 | wlr_log(L_ERROR, "Failed to drop extra capabilities"); | 248 | wlr_log(WLR_ERROR, "Failed to drop extra capabilities"); |
247 | exit(EXIT_FAILURE); | 249 | exit(EXIT_FAILURE); |
248 | } | 250 | } |
249 | } | 251 | } |
250 | #endif | 252 | #endif |
251 | } | 253 | } |
252 | 254 | ||
255 | void enable_debug_flag(const char *flag) { | ||
256 | if (strcmp(flag, "render-tree") == 0) { | ||
257 | enable_debug_tree = true; | ||
258 | } else if (strncmp(flag, "damage=", 7) == 0) { | ||
259 | damage_debug = &flag[7]; | ||
260 | } else if (strcmp(flag, "txn-debug") == 0) { | ||
261 | txn_debug = true; | ||
262 | } else if (strncmp(flag, "txn-timeout=", 12) == 0) { | ||
263 | txn_timeout_ms = atoi(&flag[12]); | ||
264 | } | ||
265 | } | ||
266 | |||
253 | int main(int argc, char **argv) { | 267 | int main(int argc, char **argv) { |
254 | static int verbose = 0, debug = 0, validate = 0; | 268 | static int verbose = 0, debug = 0, validate = 0; |
255 | 269 | ||
@@ -289,7 +303,7 @@ int main(int argc, char **argv) { | |||
289 | int c; | 303 | int c; |
290 | while (1) { | 304 | while (1) { |
291 | int option_index = 0; | 305 | int option_index = 0; |
292 | c = getopt_long(argc, argv, "hCdDvVc:", long_options, &option_index); | 306 | c = getopt_long(argc, argv, "hCdD:vVc:", long_options, &option_index); |
293 | if (c == -1) { | 307 | if (c == -1) { |
294 | break; | 308 | break; |
295 | } | 309 | } |
@@ -308,7 +322,7 @@ int main(int argc, char **argv) { | |||
308 | debug = 1; | 322 | debug = 1; |
309 | break; | 323 | break; |
310 | case 'D': // extended debug options | 324 | case 'D': // extended debug options |
311 | enable_debug_tree = true; | 325 | enable_debug_flag(optarg); |
312 | break; | 326 | break; |
313 | case 'v': // version | 327 | case 'v': // version |
314 | fprintf(stdout, "sway version " SWAY_VERSION "\n"); | 328 | fprintf(stdout, "sway version " SWAY_VERSION "\n"); |
@@ -334,22 +348,22 @@ int main(int argc, char **argv) { | |||
334 | 348 | ||
335 | // TODO: switch logging over to wlroots? | 349 | // TODO: switch logging over to wlroots? |
336 | if (debug) { | 350 | if (debug) { |
337 | wlr_log_init(L_DEBUG, NULL); | 351 | wlr_log_init(WLR_DEBUG, NULL); |
338 | } else if (verbose || validate) { | 352 | } else if (verbose || validate) { |
339 | wlr_log_init(L_INFO, NULL); | 353 | wlr_log_init(WLR_INFO, NULL); |
340 | } else { | 354 | } else { |
341 | wlr_log_init(L_ERROR, NULL); | 355 | wlr_log_init(WLR_ERROR, NULL); |
342 | } | 356 | } |
343 | 357 | ||
344 | if (optind < argc) { // Behave as IPC client | 358 | if (optind < argc) { // Behave as IPC client |
345 | if(optind != 1) { | 359 | if(optind != 1) { |
346 | wlr_log(L_ERROR, "Don't use options with the IPC client"); | 360 | wlr_log(WLR_ERROR, "Don't use options with the IPC client"); |
347 | exit(EXIT_FAILURE); | 361 | exit(EXIT_FAILURE); |
348 | } | 362 | } |
349 | drop_permissions(false); | 363 | drop_permissions(false); |
350 | char *socket_path = getenv("SWAYSOCK"); | 364 | char *socket_path = getenv("SWAYSOCK"); |
351 | if (!socket_path) { | 365 | if (!socket_path) { |
352 | wlr_log(L_ERROR, "Unable to retrieve socket path"); | 366 | wlr_log(WLR_ERROR, "Unable to retrieve socket path"); |
353 | exit(EXIT_FAILURE); | 367 | exit(EXIT_FAILURE); |
354 | } | 368 | } |
355 | char *command = join_args(argv + optind, argc - optind); | 369 | char *command = join_args(argv + optind, argc - optind); |
@@ -368,7 +382,7 @@ int main(int argc, char **argv) { | |||
368 | if (getuid() != geteuid() || getgid() != getegid()) { | 382 | if (getuid() != geteuid() || getgid() != getegid()) { |
369 | // Retain capabilities after setuid() | 383 | // Retain capabilities after setuid() |
370 | if (prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0)) { | 384 | if (prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0)) { |
371 | wlr_log(L_ERROR, "Cannot keep caps after setuid()"); | 385 | wlr_log(WLR_ERROR, "Cannot keep caps after setuid()"); |
372 | exit(EXIT_FAILURE); | 386 | exit(EXIT_FAILURE); |
373 | } | 387 | } |
374 | suid = true; | 388 | suid = true; |
@@ -389,7 +403,7 @@ int main(int argc, char **argv) { | |||
389 | // prevent ipc from crashing sway | 403 | // prevent ipc from crashing sway |
390 | signal(SIGPIPE, SIG_IGN); | 404 | signal(SIGPIPE, SIG_IGN); |
391 | 405 | ||
392 | wlr_log(L_INFO, "Starting sway version " SWAY_VERSION); | 406 | wlr_log(WLR_INFO, "Starting sway version " SWAY_VERSION); |
393 | 407 | ||
394 | layout_init(); | 408 | layout_init(); |
395 | 409 | ||
@@ -415,32 +429,41 @@ int main(int argc, char **argv) { | |||
415 | 429 | ||
416 | security_sanity_check(); | 430 | security_sanity_check(); |
417 | 431 | ||
432 | setenv("WAYLAND_DISPLAY", server.socket, true); | ||
433 | if (!terminate_request) { | ||
434 | if (!server_start_backend(&server)) { | ||
435 | sway_terminate(EXIT_FAILURE); | ||
436 | } | ||
437 | } | ||
438 | |||
418 | config->active = true; | 439 | config->active = true; |
419 | // Execute commands until there are none left | 440 | // Execute commands until there are none left |
441 | wlr_log(WLR_DEBUG, "Running deferred commands"); | ||
420 | while (config->cmd_queue->length) { | 442 | while (config->cmd_queue->length) { |
421 | char *line = config->cmd_queue->items[0]; | 443 | char *line = config->cmd_queue->items[0]; |
422 | struct cmd_results *res = execute_command(line, NULL); | 444 | struct cmd_results *res = execute_command(line, NULL); |
423 | if (res->status != CMD_SUCCESS) { | 445 | if (res->status != CMD_SUCCESS) { |
424 | wlr_log(L_ERROR, "Error on line '%s': %s", line, res->error); | 446 | wlr_log(WLR_ERROR, "Error on line '%s': %s", line, res->error); |
425 | } | 447 | } |
426 | free_cmd_results(res); | 448 | free_cmd_results(res); |
427 | free(line); | 449 | free(line); |
428 | list_del(config->cmd_queue, 0); | 450 | list_del(config->cmd_queue, 0); |
429 | } | 451 | } |
452 | transaction_commit_dirty(); | ||
430 | 453 | ||
431 | if (!terminate_request) { | 454 | if (!terminate_request) { |
432 | server_run(&server); | 455 | server_run(&server); |
433 | } | 456 | } |
434 | 457 | ||
435 | wlr_log(L_INFO, "Shutting down sway"); | 458 | wlr_log(WLR_INFO, "Shutting down sway"); |
436 | 459 | ||
437 | server_fini(&server); | 460 | server_fini(&server); |
438 | 461 | ||
439 | ipc_terminate(); | ||
440 | |||
441 | if (config) { | 462 | if (config) { |
442 | free_config(config); | 463 | free_config(config); |
443 | } | 464 | } |
444 | 465 | ||
466 | pango_cairo_font_map_set_default(NULL); | ||
467 | |||
445 | return exit_value; | 468 | return exit_value; |
446 | } | 469 | } |
diff --git a/sway/meson.build b/sway/meson.build index 9ff3f05f..30c848e2 100644 --- a/sway/meson.build +++ b/sway/meson.build | |||
@@ -7,11 +7,14 @@ sway_sources = files( | |||
7 | 'debug-tree.c', | 7 | 'debug-tree.c', |
8 | 'ipc-json.c', | 8 | 'ipc-json.c', |
9 | 'ipc-server.c', | 9 | 'ipc-server.c', |
10 | 'scratchpad.c', | ||
10 | 'security.c', | 11 | 'security.c', |
11 | 12 | ||
12 | 'desktop/desktop.c', | 13 | 'desktop/desktop.c', |
14 | 'desktop/idle_inhibit_v1.c', | ||
13 | 'desktop/layer_shell.c', | 15 | 'desktop/layer_shell.c', |
14 | 'desktop/output.c', | 16 | 'desktop/output.c', |
17 | 'desktop/render.c', | ||
15 | 'desktop/transaction.c', | 18 | 'desktop/transaction.c', |
16 | 'desktop/xdg_shell_v6.c', | 19 | 'desktop/xdg_shell_v6.c', |
17 | 'desktop/xdg_shell.c', | 20 | 'desktop/xdg_shell.c', |
@@ -33,16 +36,20 @@ sway_sources = files( | |||
33 | 'commands/border.c', | 36 | 'commands/border.c', |
34 | 'commands/client.c', | 37 | 'commands/client.c', |
35 | 'commands/default_border.c', | 38 | 'commands/default_border.c', |
39 | 'commands/default_floating_border.c', | ||
36 | 'commands/default_orientation.c', | 40 | 'commands/default_orientation.c', |
37 | 'commands/exit.c', | 41 | 'commands/exit.c', |
38 | 'commands/exec.c', | 42 | 'commands/exec.c', |
39 | 'commands/exec_always.c', | 43 | 'commands/exec_always.c', |
40 | 'commands/floating.c', | 44 | 'commands/floating.c', |
45 | 'commands/floating_minmax_size.c', | ||
46 | 'commands/floating_modifier.c', | ||
41 | 'commands/focus.c', | 47 | 'commands/focus.c', |
42 | 'commands/focus_follows_mouse.c', | 48 | 'commands/focus_follows_mouse.c', |
43 | 'commands/focus_wrapping.c', | 49 | 'commands/focus_wrapping.c', |
44 | 'commands/font.c', | 50 | 'commands/font.c', |
45 | 'commands/for_window.c', | 51 | 'commands/for_window.c', |
52 | 'commands/force_display_urgency_hint.c', | ||
46 | 'commands/force_focus_wrapping.c', | 53 | 'commands/force_focus_wrapping.c', |
47 | 'commands/fullscreen.c', | 54 | 'commands/fullscreen.c', |
48 | 'commands/gaps.c', | 55 | 'commands/gaps.c', |
@@ -56,10 +63,12 @@ sway_sources = files( | |||
56 | 'commands/mode.c', | 63 | 'commands/mode.c', |
57 | 'commands/mouse_warping.c', | 64 | 'commands/mouse_warping.c', |
58 | 'commands/move.c', | 65 | 'commands/move.c', |
66 | 'commands/no_focus.c', | ||
59 | 'commands/output.c', | 67 | 'commands/output.c', |
60 | 'commands/reload.c', | 68 | 'commands/reload.c', |
61 | 'commands/rename.c', | 69 | 'commands/rename.c', |
62 | 'commands/resize.c', | 70 | 'commands/resize.c', |
71 | 'commands/scratchpad.c', | ||
63 | 'commands/seat.c', | 72 | 'commands/seat.c', |
64 | 'commands/seat/attach.c', | 73 | 'commands/seat/attach.c', |
65 | 'commands/seat/cursor.c', | 74 | 'commands/seat/cursor.c', |
@@ -73,6 +82,7 @@ sway_sources = files( | |||
73 | 'commands/swap.c', | 82 | 'commands/swap.c', |
74 | 'commands/title_format.c', | 83 | 'commands/title_format.c', |
75 | 'commands/unmark.c', | 84 | 'commands/unmark.c', |
85 | 'commands/urgent.c', | ||
76 | 'commands/workspace.c', | 86 | 'commands/workspace.c', |
77 | 'commands/workspace_layout.c', | 87 | 'commands/workspace_layout.c', |
78 | 'commands/ws_auto_back_and_forth.c', | 88 | 'commands/ws_auto_back_and_forth.c', |
@@ -115,8 +125,10 @@ sway_sources = files( | |||
115 | 'commands/input/pointer_accel.c', | 125 | 'commands/input/pointer_accel.c', |
116 | 'commands/input/repeat_delay.c', | 126 | 'commands/input/repeat_delay.c', |
117 | 'commands/input/repeat_rate.c', | 127 | 'commands/input/repeat_rate.c', |
128 | 'commands/input/scroll_button.c', | ||
118 | 'commands/input/scroll_method.c', | 129 | 'commands/input/scroll_method.c', |
119 | 'commands/input/tap.c', | 130 | 'commands/input/tap.c', |
131 | 'commands/input/tap_button_map.c', | ||
120 | 'commands/input/xkb_layout.c', | 132 | 'commands/input/xkb_layout.c', |
121 | 'commands/input/xkb_model.c', | 133 | 'commands/input/xkb_model.c', |
122 | 'commands/input/xkb_options.c', | 134 | 'commands/input/xkb_options.c', |
diff --git a/sway/scratchpad.c b/sway/scratchpad.c new file mode 100644 index 00000000..1e836e7d --- /dev/null +++ b/sway/scratchpad.c | |||
@@ -0,0 +1,175 @@ | |||
1 | #define _XOPEN_SOURCE 700 | ||
2 | #include <stdlib.h> | ||
3 | #include <stdio.h> | ||
4 | #include <stdbool.h> | ||
5 | #include "sway/scratchpad.h" | ||
6 | #include "sway/input/seat.h" | ||
7 | #include "sway/tree/arrange.h" | ||
8 | #include "sway/tree/container.h" | ||
9 | #include "sway/tree/view.h" | ||
10 | #include "sway/tree/workspace.h" | ||
11 | #include "list.h" | ||
12 | #include "log.h" | ||
13 | |||
14 | void scratchpad_add_container(struct sway_container *con) { | ||
15 | if (!sway_assert(!con->scratchpad, "Container is already in scratchpad")) { | ||
16 | return; | ||
17 | } | ||
18 | con->scratchpad = true; | ||
19 | list_add(root_container.sway_root->scratchpad, con); | ||
20 | |||
21 | struct sway_container *parent = con->parent; | ||
22 | container_set_floating(con, true); | ||
23 | container_remove_child(con); | ||
24 | arrange_windows(parent); | ||
25 | |||
26 | struct sway_seat *seat = input_manager_current_seat(input_manager); | ||
27 | seat_set_focus(seat, seat_get_focus_inactive(seat, parent)); | ||
28 | } | ||
29 | |||
30 | void scratchpad_remove_container(struct sway_container *con) { | ||
31 | if (!sway_assert(con->scratchpad, "Container is not in scratchpad")) { | ||
32 | return; | ||
33 | } | ||
34 | con->scratchpad = false; | ||
35 | for (int i = 0; i < root_container.sway_root->scratchpad->length; ++i) { | ||
36 | if (root_container.sway_root->scratchpad->items[i] == con) { | ||
37 | list_del(root_container.sway_root->scratchpad, i); | ||
38 | break; | ||
39 | } | ||
40 | } | ||
41 | } | ||
42 | |||
43 | /** | ||
44 | * Show a single scratchpad container. | ||
45 | * The container might be visible on another workspace already. | ||
46 | */ | ||
47 | static void scratchpad_show(struct sway_container *con) { | ||
48 | struct sway_seat *seat = input_manager_current_seat(input_manager); | ||
49 | struct sway_container *ws = seat_get_focus(seat); | ||
50 | if (ws->type != C_WORKSPACE) { | ||
51 | ws = container_parent(ws, C_WORKSPACE); | ||
52 | } | ||
53 | |||
54 | // If the current con or any of its parents are in fullscreen mode, we | ||
55 | // first need to disable it before showing the scratchpad con. | ||
56 | if (ws->sway_workspace->fullscreen) { | ||
57 | view_set_fullscreen(ws->sway_workspace->fullscreen, false); | ||
58 | } | ||
59 | |||
60 | // Show the container | ||
61 | if (con->parent) { | ||
62 | container_remove_child(con); | ||
63 | } | ||
64 | container_add_child(ws->sway_workspace->floating, con); | ||
65 | |||
66 | // Make sure the container's center point overlaps this workspace | ||
67 | double center_lx = con->x + con->width / 2; | ||
68 | double center_ly = con->y + con->height / 2; | ||
69 | |||
70 | struct wlr_box workspace_box; | ||
71 | container_get_box(ws, &workspace_box); | ||
72 | if (!wlr_box_contains_point(&workspace_box, center_lx, center_ly)) { | ||
73 | // Maybe resize it | ||
74 | if (con->width > ws->width || con->height > ws->height) { | ||
75 | // TODO: Do this properly once we can float C_CONTAINERs | ||
76 | if (con->type == C_VIEW) { | ||
77 | view_init_floating(con->sway_view); | ||
78 | arrange_windows(con); | ||
79 | } | ||
80 | } | ||
81 | |||
82 | // Center it | ||
83 | double new_lx = ws->x + (ws->width - con->width) / 2; | ||
84 | double new_ly = ws->y + (ws->height - con->height) / 2; | ||
85 | container_floating_move_to(con, new_lx, new_ly); | ||
86 | } | ||
87 | |||
88 | seat_set_focus(seat, con); | ||
89 | |||
90 | container_set_dirty(con->parent); | ||
91 | } | ||
92 | |||
93 | /** | ||
94 | * Hide a single scratchpad container. | ||
95 | * The container might not be the focused container (eg. when using criteria). | ||
96 | */ | ||
97 | static void scratchpad_hide(struct sway_container *con) { | ||
98 | struct sway_seat *seat = input_manager_current_seat(input_manager); | ||
99 | struct sway_container *focus = seat_get_focus(seat); | ||
100 | struct sway_container *ws = container_parent(con, C_WORKSPACE); | ||
101 | |||
102 | container_remove_child(con); | ||
103 | arrange_windows(ws); | ||
104 | if (con == focus) { | ||
105 | seat_set_focus(seat, seat_get_focus_inactive(seat, ws)); | ||
106 | } | ||
107 | list_move_to_end(root_container.sway_root->scratchpad, con); | ||
108 | } | ||
109 | |||
110 | void scratchpad_toggle_auto(void) { | ||
111 | struct sway_seat *seat = input_manager_current_seat(input_manager); | ||
112 | struct sway_container *focus = seat_get_focus(seat); | ||
113 | struct sway_container *ws = focus->type == C_WORKSPACE ? | ||
114 | focus : container_parent(focus, C_WORKSPACE); | ||
115 | |||
116 | // Check if the currently focused window is a scratchpad window and should | ||
117 | // be hidden again. | ||
118 | if (focus->scratchpad) { | ||
119 | wlr_log(WLR_DEBUG, "Focus is a scratchpad window - hiding %s", | ||
120 | focus->name); | ||
121 | scratchpad_hide(focus); | ||
122 | return; | ||
123 | } | ||
124 | |||
125 | // Check if there is an unfocused scratchpad window on the current workspace | ||
126 | // and focus it. | ||
127 | for (int i = 0; i < ws->sway_workspace->floating->children->length; ++i) { | ||
128 | struct sway_container *floater = | ||
129 | ws->sway_workspace->floating->children->items[i]; | ||
130 | if (floater->scratchpad && focus != floater) { | ||
131 | wlr_log(WLR_DEBUG, | ||
132 | "Focusing other scratchpad window (%s) in this workspace", | ||
133 | floater->name); | ||
134 | scratchpad_show(floater); | ||
135 | return; | ||
136 | } | ||
137 | } | ||
138 | |||
139 | // Check if there is a visible scratchpad window on another workspace. | ||
140 | // In this case we move it to the current workspace. | ||
141 | for (int i = 0; i < root_container.sway_root->scratchpad->length; ++i) { | ||
142 | struct sway_container *con = | ||
143 | root_container.sway_root->scratchpad->items[i]; | ||
144 | if (con->parent) { | ||
145 | wlr_log(WLR_DEBUG, | ||
146 | "Moving a visible scratchpad window (%s) to this workspace", | ||
147 | con->name); | ||
148 | scratchpad_show(con); | ||
149 | return; | ||
150 | } | ||
151 | } | ||
152 | |||
153 | // Take the container at the bottom of the scratchpad list | ||
154 | if (!sway_assert(root_container.sway_root->scratchpad->length, | ||
155 | "Scratchpad is empty")) { | ||
156 | return; | ||
157 | } | ||
158 | struct sway_container *con = root_container.sway_root->scratchpad->items[0]; | ||
159 | wlr_log(WLR_DEBUG, "Showing %s from list", con->name); | ||
160 | scratchpad_show(con); | ||
161 | } | ||
162 | |||
163 | void scratchpad_toggle_container(struct sway_container *con) { | ||
164 | if (!sway_assert(con->scratchpad, "Container isn't in the scratchpad")) { | ||
165 | return; | ||
166 | } | ||
167 | |||
168 | // Check if it matches a currently visible scratchpad window and hide it. | ||
169 | if (con->parent) { | ||
170 | scratchpad_hide(con); | ||
171 | return; | ||
172 | } | ||
173 | |||
174 | scratchpad_show(con); | ||
175 | } | ||
diff --git a/sway/server.c b/sway/server.c index f5700c09..89dfbf8c 100644 --- a/sway/server.c +++ b/sway/server.c | |||
@@ -7,13 +7,13 @@ | |||
7 | #include <wlr/backend/session.h> | 7 | #include <wlr/backend/session.h> |
8 | #include <wlr/render/wlr_renderer.h> | 8 | #include <wlr/render/wlr_renderer.h> |
9 | #include <wlr/types/wlr_compositor.h> | 9 | #include <wlr/types/wlr_compositor.h> |
10 | #include <wlr/types/wlr_export_dmabuf_v1.h> | ||
10 | #include <wlr/types/wlr_gamma_control.h> | 11 | #include <wlr/types/wlr_gamma_control.h> |
11 | #include <wlr/types/wlr_idle.h> | 12 | #include <wlr/types/wlr_idle.h> |
12 | #include <wlr/types/wlr_layer_shell.h> | 13 | #include <wlr/types/wlr_layer_shell.h> |
13 | #include <wlr/types/wlr_linux_dmabuf.h> | 14 | #include <wlr/types/wlr_linux_dmabuf_v1.h> |
14 | #include <wlr/types/wlr_export_dmabuf_v1.h> | ||
15 | #include <wlr/types/wlr_primary_selection.h> | 15 | #include <wlr/types/wlr_primary_selection.h> |
16 | #include <wlr/types/wlr_screenshooter.h> | 16 | #include <wlr/types/wlr_screencopy_v1.h> |
17 | #include <wlr/types/wlr_server_decoration.h> | 17 | #include <wlr/types/wlr_server_decoration.h> |
18 | #include <wlr/types/wlr_xcursor_manager.h> | 18 | #include <wlr/types/wlr_xcursor_manager.h> |
19 | #include <wlr/types/wlr_xdg_output.h> | 19 | #include <wlr/types/wlr_xdg_output.h> |
@@ -21,26 +21,27 @@ | |||
21 | // TODO WLR: make Xwayland optional | 21 | // TODO WLR: make Xwayland optional |
22 | #include "list.h" | 22 | #include "list.h" |
23 | #include "sway/config.h" | 23 | #include "sway/config.h" |
24 | #include "sway/desktop/idle_inhibit_v1.h" | ||
24 | #include "sway/input/input-manager.h" | 25 | #include "sway/input/input-manager.h" |
25 | #include "sway/server.h" | 26 | #include "sway/server.h" |
26 | #include "sway/tree/layout.h" | 27 | #include "sway/tree/layout.h" |
27 | #include "sway/xwayland.h" | 28 | #include "sway/xwayland.h" |
28 | 29 | ||
29 | bool server_privileged_prepare(struct sway_server *server) { | 30 | bool server_privileged_prepare(struct sway_server *server) { |
30 | wlr_log(L_DEBUG, "Preparing Wayland server initialization"); | 31 | wlr_log(WLR_DEBUG, "Preparing Wayland server initialization"); |
31 | server->wl_display = wl_display_create(); | 32 | server->wl_display = wl_display_create(); |
32 | server->wl_event_loop = wl_display_get_event_loop(server->wl_display); | 33 | server->wl_event_loop = wl_display_get_event_loop(server->wl_display); |
33 | server->backend = wlr_backend_autocreate(server->wl_display, NULL); | 34 | server->backend = wlr_backend_autocreate(server->wl_display, NULL); |
34 | 35 | ||
35 | if (!server->backend) { | 36 | if (!server->backend) { |
36 | wlr_log(L_ERROR, "Unable to create backend"); | 37 | wlr_log(WLR_ERROR, "Unable to create backend"); |
37 | return false; | 38 | return false; |
38 | } | 39 | } |
39 | return true; | 40 | return true; |
40 | } | 41 | } |
41 | 42 | ||
42 | bool server_init(struct sway_server *server) { | 43 | bool server_init(struct sway_server *server) { |
43 | wlr_log(L_DEBUG, "Initializing Wayland server"); | 44 | wlr_log(WLR_DEBUG, "Initializing Wayland server"); |
44 | 45 | ||
45 | struct wlr_renderer *renderer = wlr_backend_get_renderer(server->backend); | 46 | struct wlr_renderer *renderer = wlr_backend_get_renderer(server->backend); |
46 | assert(renderer); | 47 | assert(renderer); |
@@ -51,8 +52,6 @@ bool server_init(struct sway_server *server) { | |||
51 | server->data_device_manager = | 52 | server->data_device_manager = |
52 | wlr_data_device_manager_create(server->wl_display); | 53 | wlr_data_device_manager_create(server->wl_display); |
53 | 54 | ||
54 | server->idle = wlr_idle_create(server->wl_display); | ||
55 | wlr_screenshooter_create(server->wl_display); | ||
56 | wlr_gamma_control_manager_create(server->wl_display); | 55 | wlr_gamma_control_manager_create(server->wl_display); |
57 | wlr_primary_selection_device_manager_create(server->wl_display); | 56 | wlr_primary_selection_device_manager_create(server->wl_display); |
58 | 57 | ||
@@ -62,6 +61,10 @@ bool server_init(struct sway_server *server) { | |||
62 | wlr_xdg_output_manager_create(server->wl_display, | 61 | wlr_xdg_output_manager_create(server->wl_display, |
63 | root_container.sway_root->output_layout); | 62 | root_container.sway_root->output_layout); |
64 | 63 | ||
64 | server->idle = wlr_idle_create(server->wl_display); | ||
65 | server->idle_inhibit_manager_v1 = | ||
66 | sway_idle_inhibit_manager_v1_create(server->wl_display, server->idle); | ||
67 | |||
65 | server->layer_shell = wlr_layer_shell_create(server->wl_display); | 68 | server->layer_shell = wlr_layer_shell_create(server->wl_display); |
66 | wl_signal_add(&server->layer_shell->events.new_surface, | 69 | wl_signal_add(&server->layer_shell->events.new_surface, |
67 | &server->layer_shell_surface); | 70 | &server->layer_shell_surface); |
@@ -105,12 +108,13 @@ bool server_init(struct sway_server *server) { | |||
105 | wlr_server_decoration_manager_set_default_mode( | 108 | wlr_server_decoration_manager_set_default_mode( |
106 | deco_manager, WLR_SERVER_DECORATION_MANAGER_MODE_SERVER); | 109 | deco_manager, WLR_SERVER_DECORATION_MANAGER_MODE_SERVER); |
107 | 110 | ||
108 | wlr_linux_dmabuf_create(server->wl_display, renderer); | 111 | wlr_linux_dmabuf_v1_create(server->wl_display, renderer); |
109 | wlr_export_dmabuf_manager_v1_create(server->wl_display); | 112 | wlr_export_dmabuf_manager_v1_create(server->wl_display); |
113 | wlr_screencopy_manager_v1_create(server->wl_display); | ||
110 | 114 | ||
111 | server->socket = wl_display_add_socket_auto(server->wl_display); | 115 | server->socket = wl_display_add_socket_auto(server->wl_display); |
112 | if (!server->socket) { | 116 | if (!server->socket) { |
113 | wlr_log(L_ERROR, "Unable to open wayland socket"); | 117 | wlr_log(WLR_ERROR, "Unable to open wayland socket"); |
114 | wlr_backend_destroy(server->backend); | 118 | wlr_backend_destroy(server->backend); |
115 | return false; | 119 | return false; |
116 | } | 120 | } |
@@ -119,8 +123,7 @@ bool server_init(struct sway_server *server) { | |||
119 | if (debug != NULL && strcmp(debug, "txn_timings") == 0) { | 123 | if (debug != NULL && strcmp(debug, "txn_timings") == 0) { |
120 | server->debug_txn_timings = true; | 124 | server->debug_txn_timings = true; |
121 | } | 125 | } |
122 | server->destroying_containers = create_list(); | 126 | server->dirty_containers = create_list(); |
123 | |||
124 | server->transactions = create_list(); | 127 | server->transactions = create_list(); |
125 | 128 | ||
126 | input_manager = input_manager_create(server); | 129 | input_manager = input_manager_create(server); |
@@ -130,18 +133,23 @@ bool server_init(struct sway_server *server) { | |||
130 | void server_fini(struct sway_server *server) { | 133 | void server_fini(struct sway_server *server) { |
131 | // TODO: free sway-specific resources | 134 | // TODO: free sway-specific resources |
132 | wl_display_destroy(server->wl_display); | 135 | wl_display_destroy(server->wl_display); |
133 | list_free(server->destroying_containers); | 136 | list_free(server->dirty_containers); |
134 | list_free(server->transactions); | 137 | list_free(server->transactions); |
135 | } | 138 | } |
136 | 139 | ||
137 | void server_run(struct sway_server *server) { | 140 | bool server_start_backend(struct sway_server *server) { |
138 | wlr_log(L_INFO, "Running compositor on wayland display '%s'", | 141 | wlr_log(WLR_INFO, "Starting backend on wayland display '%s'", |
139 | server->socket); | 142 | server->socket); |
140 | setenv("WAYLAND_DISPLAY", server->socket, true); | ||
141 | if (!wlr_backend_start(server->backend)) { | 143 | if (!wlr_backend_start(server->backend)) { |
142 | wlr_log(L_ERROR, "Failed to start backend"); | 144 | wlr_log(WLR_ERROR, "Failed to start backend"); |
143 | wlr_backend_destroy(server->backend); | 145 | wlr_backend_destroy(server->backend); |
144 | return; | 146 | return false; |
145 | } | 147 | } |
148 | return true; | ||
149 | } | ||
150 | |||
151 | void server_run(struct sway_server *server) { | ||
152 | wlr_log(WLR_INFO, "Running compositor on wayland display '%s'", | ||
153 | server->socket); | ||
146 | wl_display_run(server->wl_display); | 154 | wl_display_run(server->wl_display); |
147 | } | 155 | } |
diff --git a/sway/sway-input.5.scd b/sway/sway-input.5.scd index c07460b1..b6391431 100644 --- a/sway/sway-input.5.scd +++ b/sway/sway-input.5.scd | |||
@@ -67,8 +67,8 @@ For more information on these xkb configuration options, see | |||
67 | Enables or disables disable-while-typing for the specified input device. | 67 | Enables or disables disable-while-typing for the specified input device. |
68 | 68 | ||
69 | *input* <identifier> events enabled|disabled|disabled\_on\_external\_mouse | 69 | *input* <identifier> events enabled|disabled|disabled\_on\_external\_mouse |
70 | Enables or disables send_events for specified input device. (Disabling | 70 | Enables or disables send\_events for specified input device. (Disabling |
71 | send_events disables the input device) | 71 | send\_events disables the input device) |
72 | 72 | ||
73 | *input* <identifier> left\_handed enabled|disabled | 73 | *input* <identifier> left\_handed enabled|disabled |
74 | Enables or disables left handed mode for specified input device. | 74 | Enables or disables left handed mode for specified input device. |
@@ -92,9 +92,20 @@ For more information on these xkb configuration options, see | |||
92 | *input* <identifier> scroll\_method none|two\_finger|edge|on\_button\_down | 92 | *input* <identifier> scroll\_method none|two\_finger|edge|on\_button\_down |
93 | Changes the scroll method for the specified input device. | 93 | Changes the scroll method for the specified input device. |
94 | 94 | ||
95 | *input* <identifier> scroll\_button <button\_identifier> | ||
96 | Sets button used for scroll\_method on\_button\_down. The button identifier | ||
97 | can be obtained from `libinput debug-events`. | ||
98 | If set to 0, it disables the scroll\_button on\_button\_down. | ||
99 | |||
95 | *input* <identifier> tap enabled|disabled | 100 | *input* <identifier> tap enabled|disabled |
96 | Enables or disables tap for specified input device. | 101 | Enables or disables tap for specified input device. |
97 | 102 | ||
103 | *input* <identifier> tap_button_map lrm|lmr | ||
104 | Specifies which button mapping to use for tapping. _lrm_ treats 1 finger as | ||
105 | left click, 2 fingers as right click, and 3 fingers as middle click. _lmr_ | ||
106 | treats 1 finger as left click, 2 fingers as middle click, and 3 fingers as | ||
107 | right click. | ||
108 | |||
98 | ## SEAT CONFIGURATION | 109 | ## SEAT CONFIGURATION |
99 | 110 | ||
100 | Configure options for multiseat mode. sway-seat commands must be used inside a | 111 | Configure options for multiseat mode. sway-seat commands must be used inside a |
diff --git a/sway/sway.5.scd b/sway/sway.5.scd index 5ce6bf06..d369d7b6 100644 --- a/sway/sway.5.scd +++ b/sway/sway.5.scd | |||
@@ -92,6 +92,12 @@ They are expected to be used with *bindsym* or at runtime through *swaymsg*(1). | |||
92 | *focus* output <name> | 92 | *focus* output <name> |
93 | Moves focus to the named output. | 93 | Moves focus to the named output. |
94 | 94 | ||
95 | *focus tiling* | ||
96 | Sets focus to the last focused tiling container. | ||
97 | |||
98 | *focus floating* | ||
99 | Sets focus to the last focused floating container. | ||
100 | |||
95 | *focus* mode\_toggle | 101 | *focus* mode\_toggle |
96 | Moves focus between the floating and tiled layers. | 102 | Moves focus between the floating and tiled layers. |
97 | 103 | ||
@@ -413,10 +419,12 @@ The default colors are: | |||
413 | *mode* <mode> | 419 | *mode* <mode> |
414 | Switches to the specified mode. The default mode _default_. | 420 | Switches to the specified mode. The default mode _default_. |
415 | 421 | ||
416 | *mode* <mode> *{* <commands...> *}* | 422 | *mode* [--pango\_markup] <mode> *{* <commands...> *}* |
417 | _commands..._ after *{* will be added to the specified mode. A newline is | 423 | _commands..._ after *{* will be added to the specified mode. A newline is |
418 | required between *{* and the first command, and *}* must be alone on a | 424 | required between *{* and the first command, and *}* must be alone on a |
419 | line. Only *bindsym* and *bindcode* commands are permitted in mode blocks. | 425 | line. Only *bindsym* and *bindcode* commands are permitted in mode blocks. |
426 | If _--pango\_markup_ is given, then _mode_ will be interpreted as pango | ||
427 | markup. | ||
420 | 428 | ||
421 | *mouse\_warping* output|none | 429 | *mouse\_warping* output|none |
422 | If _output_ is specified, the mouse will be moved to new outputs as you | 430 | If _output_ is specified, the mouse will be moved to new outputs as you |
@@ -491,6 +499,11 @@ config after the others, or it will be matched instead of the others. | |||
491 | *unmark* will remove _identifier_ from the list of current marks on a | 499 | *unmark* will remove _identifier_ from the list of current marks on a |
492 | window. If _identifier_ is omitted, all marks are removed. | 500 | window. If _identifier_ is omitted, all marks are removed. |
493 | 501 | ||
502 | *urgent* enable|disable|allow|deny | ||
503 | Using _enable_ or _disable_ manually sets or unsets the window's urgent | ||
504 | state. Using _allow_ or _deny_ controls the window's ability to set itself | ||
505 | as urgent. By default, windows are allowed to set their own urgency. | ||
506 | |||
494 | *workspace* [number] <name> | 507 | *workspace* [number] <name> |
495 | Switches to the specified workspace. The string "number" is optional and is | 508 | Switches to the specified workspace. The string "number" is optional and is |
496 | used to sort workspaces. | 509 | used to sort workspaces. |
diff --git a/sway/tree/arrange.c b/sway/tree/arrange.c index 582b2891..533cf71c 100644 --- a/sway/tree/arrange.c +++ b/sway/tree/arrange.c | |||
@@ -47,11 +47,11 @@ static void apply_horiz_layout(struct sway_container *parent) { | |||
47 | double scale = parent->width / total_width; | 47 | double scale = parent->width / total_width; |
48 | 48 | ||
49 | // Resize windows | 49 | // Resize windows |
50 | wlr_log(L_DEBUG, "Arranging %p horizontally", parent); | 50 | wlr_log(WLR_DEBUG, "Arranging %p horizontally", parent); |
51 | double child_x = parent->x; | 51 | double child_x = parent->x; |
52 | for (size_t i = 0; i < num_children; ++i) { | 52 | for (size_t i = 0; i < num_children; ++i) { |
53 | struct sway_container *child = parent->children->items[i]; | 53 | struct sway_container *child = parent->children->items[i]; |
54 | wlr_log(L_DEBUG, | 54 | wlr_log(WLR_DEBUG, |
55 | "Calculating arrangement for %p:%d (will scale %f by %f)", | 55 | "Calculating arrangement for %p:%d (will scale %f by %f)", |
56 | child, child->type, child->width, scale); | 56 | child, child->type, child->width, scale); |
57 | child->x = child_x; | 57 | child->x = child_x; |
@@ -99,11 +99,11 @@ static void apply_vert_layout(struct sway_container *parent) { | |||
99 | double scale = parent_height / total_height; | 99 | double scale = parent_height / total_height; |
100 | 100 | ||
101 | // Resize | 101 | // Resize |
102 | wlr_log(L_DEBUG, "Arranging %p vertically", parent); | 102 | wlr_log(WLR_DEBUG, "Arranging %p vertically", parent); |
103 | double child_y = parent->y + parent_offset; | 103 | double child_y = parent->y + parent_offset; |
104 | for (size_t i = 0; i < num_children; ++i) { | 104 | for (size_t i = 0; i < num_children; ++i) { |
105 | struct sway_container *child = parent->children->items[i]; | 105 | struct sway_container *child = parent->children->items[i]; |
106 | wlr_log(L_DEBUG, | 106 | wlr_log(WLR_DEBUG, |
107 | "Calculating arrangement for %p:%d (will scale %f by %f)", | 107 | "Calculating arrangement for %p:%d (will scale %f by %f)", |
108 | child, child->type, child->height, scale); | 108 | child, child->type, child->height, scale); |
109 | child->x = parent->x; | 109 | child->x = parent->x; |
@@ -144,42 +144,26 @@ static void apply_tabbed_or_stacked_layout(struct sway_container *parent) { | |||
144 | } | 144 | } |
145 | } | 145 | } |
146 | 146 | ||
147 | /** | 147 | static void arrange_children_of(struct sway_container *parent); |
148 | * If a container has been deleted from the pending tree state, we must add it | ||
149 | * to the transaction so it can be freed afterwards. To do this, we iterate the | ||
150 | * server's destroying_containers list and add all of them. We may add more than | ||
151 | * what we need to, but this is easy and has no negative consequences. | ||
152 | */ | ||
153 | static void add_deleted_containers(struct sway_transaction *transaction) { | ||
154 | for (int i = 0; i < server.destroying_containers->length; ++i) { | ||
155 | struct sway_container *child = server.destroying_containers->items[i]; | ||
156 | transaction_add_container(transaction, child); | ||
157 | } | ||
158 | } | ||
159 | |||
160 | static void arrange_children_of(struct sway_container *parent, | ||
161 | struct sway_transaction *transaction); | ||
162 | 148 | ||
163 | static void arrange_floating(struct sway_container *floating, | 149 | static void arrange_floating(struct sway_container *floating) { |
164 | struct sway_transaction *transaction) { | ||
165 | for (int i = 0; i < floating->children->length; ++i) { | 150 | for (int i = 0; i < floating->children->length; ++i) { |
166 | struct sway_container *floater = floating->children->items[i]; | 151 | struct sway_container *floater = floating->children->items[i]; |
167 | if (floater->type == C_VIEW) { | 152 | if (floater->type == C_VIEW) { |
168 | view_autoconfigure(floater->sway_view); | 153 | view_autoconfigure(floater->sway_view); |
169 | } else { | 154 | } else { |
170 | arrange_children_of(floater, transaction); | 155 | arrange_children_of(floater); |
171 | } | 156 | } |
172 | transaction_add_container(transaction, floater); | 157 | container_set_dirty(floater); |
173 | } | 158 | } |
174 | transaction_add_container(transaction, floating); | 159 | container_set_dirty(floating); |
175 | } | 160 | } |
176 | 161 | ||
177 | static void arrange_children_of(struct sway_container *parent, | 162 | static void arrange_children_of(struct sway_container *parent) { |
178 | struct sway_transaction *transaction) { | ||
179 | if (config->reloading) { | 163 | if (config->reloading) { |
180 | return; | 164 | return; |
181 | } | 165 | } |
182 | wlr_log(L_DEBUG, "Arranging layout for %p %s %fx%f+%f,%f", parent, | 166 | wlr_log(WLR_DEBUG, "Arranging layout for %p %s %fx%f+%f,%f", parent, |
183 | parent->name, parent->width, parent->height, parent->x, parent->y); | 167 | parent->name, parent->width, parent->height, parent->x, parent->y); |
184 | 168 | ||
185 | // Calculate x, y, width and height of children | 169 | // Calculate x, y, width and height of children |
@@ -198,7 +182,7 @@ static void arrange_children_of(struct sway_container *parent, | |||
198 | apply_horiz_layout(parent); | 182 | apply_horiz_layout(parent); |
199 | break; | 183 | break; |
200 | case L_FLOATING: | 184 | case L_FLOATING: |
201 | arrange_floating(parent, transaction); | 185 | arrange_floating(parent); |
202 | break; | 186 | break; |
203 | } | 187 | } |
204 | 188 | ||
@@ -213,20 +197,19 @@ static void arrange_children_of(struct sway_container *parent, | |||
213 | if (child->type == C_VIEW) { | 197 | if (child->type == C_VIEW) { |
214 | view_autoconfigure(child->sway_view); | 198 | view_autoconfigure(child->sway_view); |
215 | } else { | 199 | } else { |
216 | arrange_children_of(child, transaction); | 200 | arrange_children_of(child); |
217 | } | 201 | } |
218 | transaction_add_container(transaction, child); | 202 | container_set_dirty(child); |
219 | } | 203 | } |
220 | } | 204 | } |
221 | 205 | ||
222 | static void arrange_workspace(struct sway_container *workspace, | 206 | static void arrange_workspace(struct sway_container *workspace) { |
223 | struct sway_transaction *transaction) { | ||
224 | if (config->reloading) { | 207 | if (config->reloading) { |
225 | return; | 208 | return; |
226 | } | 209 | } |
227 | struct sway_container *output = workspace->parent; | 210 | struct sway_container *output = workspace->parent; |
228 | struct wlr_box *area = &output->sway_output->usable_area; | 211 | struct wlr_box *area = &output->sway_output->usable_area; |
229 | wlr_log(L_DEBUG, "Usable area for ws: %dx%d@%d,%d", | 212 | wlr_log(WLR_DEBUG, "Usable area for ws: %dx%d@%d,%d", |
230 | area->width, area->height, area->x, area->y); | 213 | area->width, area->height, area->x, area->y); |
231 | remove_gaps(workspace); | 214 | remove_gaps(workspace); |
232 | workspace->width = area->width; | 215 | workspace->width = area->width; |
@@ -234,15 +217,14 @@ static void arrange_workspace(struct sway_container *workspace, | |||
234 | workspace->x = output->x + area->x; | 217 | workspace->x = output->x + area->x; |
235 | workspace->y = output->y + area->y; | 218 | workspace->y = output->y + area->y; |
236 | add_gaps(workspace); | 219 | add_gaps(workspace); |
237 | transaction_add_container(transaction, workspace); | 220 | container_set_dirty(workspace); |
238 | wlr_log(L_DEBUG, "Arranging workspace '%s' at %f, %f", workspace->name, | 221 | wlr_log(WLR_DEBUG, "Arranging workspace '%s' at %f, %f", workspace->name, |
239 | workspace->x, workspace->y); | 222 | workspace->x, workspace->y); |
240 | arrange_floating(workspace->sway_workspace->floating, transaction); | 223 | arrange_floating(workspace->sway_workspace->floating); |
241 | arrange_children_of(workspace, transaction); | 224 | arrange_children_of(workspace); |
242 | } | 225 | } |
243 | 226 | ||
244 | static void arrange_output(struct sway_container *output, | 227 | static void arrange_output(struct sway_container *output) { |
245 | struct sway_transaction *transaction) { | ||
246 | if (config->reloading) { | 228 | if (config->reloading) { |
247 | return; | 229 | return; |
248 | } | 230 | } |
@@ -253,16 +235,16 @@ static void arrange_output(struct sway_container *output, | |||
253 | output->y = output_box->y; | 235 | output->y = output_box->y; |
254 | output->width = output_box->width; | 236 | output->width = output_box->width; |
255 | output->height = output_box->height; | 237 | output->height = output_box->height; |
256 | transaction_add_container(transaction, output); | 238 | container_set_dirty(output); |
257 | wlr_log(L_DEBUG, "Arranging output '%s' at %f,%f", | 239 | wlr_log(WLR_DEBUG, "Arranging output '%s' at %f,%f", |
258 | output->name, output->x, output->y); | 240 | output->name, output->x, output->y); |
259 | for (int i = 0; i < output->children->length; ++i) { | 241 | for (int i = 0; i < output->children->length; ++i) { |
260 | struct sway_container *workspace = output->children->items[i]; | 242 | struct sway_container *workspace = output->children->items[i]; |
261 | arrange_workspace(workspace, transaction); | 243 | arrange_workspace(workspace); |
262 | } | 244 | } |
263 | } | 245 | } |
264 | 246 | ||
265 | static void arrange_root(struct sway_transaction *transaction) { | 247 | static void arrange_root() { |
266 | if (config->reloading) { | 248 | if (config->reloading) { |
267 | return; | 249 | return; |
268 | } | 250 | } |
@@ -274,48 +256,40 @@ static void arrange_root(struct sway_transaction *transaction) { | |||
274 | root_container.y = layout_box->y; | 256 | root_container.y = layout_box->y; |
275 | root_container.width = layout_box->width; | 257 | root_container.width = layout_box->width; |
276 | root_container.height = layout_box->height; | 258 | root_container.height = layout_box->height; |
277 | transaction_add_container(transaction, &root_container); | 259 | container_set_dirty(&root_container); |
278 | for (int i = 0; i < root_container.children->length; ++i) { | 260 | for (int i = 0; i < root_container.children->length; ++i) { |
279 | struct sway_container *output = root_container.children->items[i]; | 261 | struct sway_container *output = root_container.children->items[i]; |
280 | arrange_output(output, transaction); | 262 | arrange_output(output); |
281 | } | 263 | } |
282 | } | 264 | } |
283 | 265 | ||
284 | void arrange_windows(struct sway_container *container, | 266 | void arrange_windows(struct sway_container *container) { |
285 | struct sway_transaction *transaction) { | ||
286 | switch (container->type) { | 267 | switch (container->type) { |
287 | case C_ROOT: | 268 | case C_ROOT: |
288 | arrange_root(transaction); | 269 | arrange_root(); |
289 | break; | 270 | break; |
290 | case C_OUTPUT: | 271 | case C_OUTPUT: |
291 | arrange_output(container, transaction); | 272 | arrange_output(container); |
292 | break; | 273 | break; |
293 | case C_WORKSPACE: | 274 | case C_WORKSPACE: |
294 | arrange_workspace(container, transaction); | 275 | arrange_workspace(container); |
295 | break; | 276 | break; |
296 | case C_CONTAINER: | 277 | case C_CONTAINER: |
297 | arrange_children_of(container, transaction); | 278 | arrange_children_of(container); |
298 | transaction_add_container(transaction, container); | 279 | container_set_dirty(container); |
299 | break; | 280 | break; |
300 | case C_VIEW: | 281 | case C_VIEW: |
301 | view_autoconfigure(container->sway_view); | 282 | view_autoconfigure(container->sway_view); |
302 | transaction_add_container(transaction, container); | 283 | container_set_dirty(container); |
303 | break; | 284 | break; |
304 | case C_TYPES: | 285 | case C_TYPES: |
305 | break; | 286 | break; |
306 | } | 287 | } |
307 | add_deleted_containers(transaction); | ||
308 | } | ||
309 | |||
310 | void arrange_and_commit(struct sway_container *container) { | ||
311 | struct sway_transaction *transaction = transaction_create(); | ||
312 | arrange_windows(container, transaction); | ||
313 | transaction_commit(transaction); | ||
314 | } | 288 | } |
315 | 289 | ||
316 | void remove_gaps(struct sway_container *c) { | 290 | void remove_gaps(struct sway_container *c) { |
317 | if (c->current_gaps == 0) { | 291 | if (c->current_gaps == 0) { |
318 | wlr_log(L_DEBUG, "Removing gaps: not gapped: %p", c); | 292 | wlr_log(WLR_DEBUG, "Removing gaps: not gapped: %p", c); |
319 | return; | 293 | return; |
320 | } | 294 | } |
321 | 295 | ||
@@ -326,12 +300,12 @@ void remove_gaps(struct sway_container *c) { | |||
326 | 300 | ||
327 | c->current_gaps = 0; | 301 | c->current_gaps = 0; |
328 | 302 | ||
329 | wlr_log(L_DEBUG, "Removing gaps %p", c); | 303 | wlr_log(WLR_DEBUG, "Removing gaps %p", c); |
330 | } | 304 | } |
331 | 305 | ||
332 | void add_gaps(struct sway_container *c) { | 306 | void add_gaps(struct sway_container *c) { |
333 | if (c->current_gaps > 0 || c->type == C_CONTAINER) { | 307 | if (c->current_gaps > 0 || c->type == C_CONTAINER) { |
334 | wlr_log(L_DEBUG, "Not adding gaps: %p", c); | 308 | wlr_log(WLR_DEBUG, "Not adding gaps: %p", c); |
335 | return; | 309 | return; |
336 | } | 310 | } |
337 | 311 | ||
@@ -348,5 +322,5 @@ void add_gaps(struct sway_container *c) { | |||
348 | c->height -= 2 * gaps; | 322 | c->height -= 2 * gaps; |
349 | c->current_gaps = gaps; | 323 | c->current_gaps = gaps; |
350 | 324 | ||
351 | wlr_log(L_DEBUG, "Adding gaps: %p", c); | 325 | wlr_log(WLR_DEBUG, "Adding gaps: %p", c); |
352 | } | 326 | } |
diff --git a/sway/tree/container.c b/sway/tree/container.c index 6f6137c4..4f743c40 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c | |||
@@ -11,11 +11,15 @@ | |||
11 | #include "cairo.h" | 11 | #include "cairo.h" |
12 | #include "pango.h" | 12 | #include "pango.h" |
13 | #include "sway/config.h" | 13 | #include "sway/config.h" |
14 | #include "sway/desktop.h" | ||
15 | #include "sway/desktop/transaction.h" | ||
14 | #include "sway/input/input-manager.h" | 16 | #include "sway/input/input-manager.h" |
15 | #include "sway/input/seat.h" | 17 | #include "sway/input/seat.h" |
16 | #include "sway/ipc-server.h" | 18 | #include "sway/ipc-server.h" |
17 | #include "sway/output.h" | 19 | #include "sway/output.h" |
20 | #include "sway/scratchpad.h" | ||
18 | #include "sway/server.h" | 21 | #include "sway/server.h" |
22 | #include "sway/tree/arrange.h" | ||
19 | #include "sway/tree/layout.h" | 23 | #include "sway/tree/layout.h" |
20 | #include "sway/tree/view.h" | 24 | #include "sway/tree/view.h" |
21 | #include "sway/tree/workspace.h" | 25 | #include "sway/tree/workspace.h" |
@@ -28,7 +32,7 @@ static list_t *get_bfs_queue() { | |||
28 | if (!bfs_queue) { | 32 | if (!bfs_queue) { |
29 | bfs_queue = create_list(); | 33 | bfs_queue = create_list(); |
30 | if (!bfs_queue) { | 34 | if (!bfs_queue) { |
31 | wlr_log(L_ERROR, "could not allocate list for bfs queue"); | 35 | wlr_log(WLR_ERROR, "could not allocate list for bfs queue"); |
32 | return NULL; | 36 | return NULL; |
33 | } | 37 | } |
34 | } | 38 | } |
@@ -151,18 +155,11 @@ void container_free(struct sway_container *cont) { | |||
151 | return; | 155 | return; |
152 | } | 156 | } |
153 | free(cont->name); | 157 | free(cont->name); |
158 | free(cont->formatted_title); | ||
154 | wlr_texture_destroy(cont->title_focused); | 159 | wlr_texture_destroy(cont->title_focused); |
155 | wlr_texture_destroy(cont->title_focused_inactive); | 160 | wlr_texture_destroy(cont->title_focused_inactive); |
156 | wlr_texture_destroy(cont->title_unfocused); | 161 | wlr_texture_destroy(cont->title_unfocused); |
157 | wlr_texture_destroy(cont->title_urgent); | 162 | wlr_texture_destroy(cont->title_urgent); |
158 | |||
159 | for (int i = 0; i < server.destroying_containers->length; ++i) { | ||
160 | if (server.destroying_containers->items[i] == cont) { | ||
161 | list_del(server.destroying_containers, i); | ||
162 | break; | ||
163 | } | ||
164 | } | ||
165 | |||
166 | list_free(cont->instructions); | 163 | list_free(cont->instructions); |
167 | list_free(cont->children); | 164 | list_free(cont->children); |
168 | list_free(cont->current.children); | 165 | list_free(cont->current.children); |
@@ -203,16 +200,23 @@ static struct sway_container *container_workspace_destroy( | |||
203 | return NULL; | 200 | return NULL; |
204 | } | 201 | } |
205 | 202 | ||
206 | // Do not destroy this if it's the last workspace on this output | ||
207 | struct sway_container *output = container_parent(workspace, C_OUTPUT); | 203 | struct sway_container *output = container_parent(workspace, C_OUTPUT); |
208 | if (output && output->children->length == 1) { | 204 | |
205 | // If we're destroying the output, it will be NULL here. Return the root so | ||
206 | // that it doesn't appear that the workspace has refused to be destoyed, | ||
207 | // which would leave it in a broken state with no parent. | ||
208 | if (output == NULL) { | ||
209 | return &root_container; | ||
210 | } | ||
211 | |||
212 | // Do not destroy this if it's the last workspace on this output | ||
213 | if (output->children->length == 1) { | ||
209 | return NULL; | 214 | return NULL; |
210 | } | 215 | } |
211 | 216 | ||
212 | wlr_log(L_DEBUG, "destroying workspace '%s'", workspace->name); | 217 | wlr_log(WLR_DEBUG, "destroying workspace '%s'", workspace->name); |
213 | 218 | ||
214 | struct sway_container *parent = workspace->parent; | 219 | if (!workspace_is_empty(workspace)) { |
215 | if (!workspace_is_empty(workspace) && output) { | ||
216 | // Move children to a different workspace on this output | 220 | // Move children to a different workspace on this output |
217 | struct sway_container *new_workspace = NULL; | 221 | struct sway_container *new_workspace = NULL; |
218 | for (int i = 0; i < output->children->length; i++) { | 222 | for (int i = 0; i < output->children->length; i++) { |
@@ -222,7 +226,7 @@ static struct sway_container *container_workspace_destroy( | |||
222 | } | 226 | } |
223 | } | 227 | } |
224 | 228 | ||
225 | wlr_log(L_DEBUG, "moving children to different workspace '%s' -> '%s'", | 229 | wlr_log(WLR_DEBUG, "moving children to different workspace '%s' -> '%s'", |
226 | workspace->name, new_workspace->name); | 230 | workspace->name, new_workspace->name); |
227 | for (int i = 0; i < workspace->children->length; i++) { | 231 | for (int i = 0; i < workspace->children->length; i++) { |
228 | container_move_to(workspace->children->items[i], new_workspace); | 232 | container_move_to(workspace->children->items[i], new_workspace); |
@@ -234,7 +238,7 @@ static struct sway_container *container_workspace_destroy( | |||
234 | } | 238 | } |
235 | } | 239 | } |
236 | 240 | ||
237 | return parent; | 241 | return output; |
238 | } | 242 | } |
239 | 243 | ||
240 | static struct sway_container *container_output_destroy( | 244 | static struct sway_container *container_output_destroy( |
@@ -288,7 +292,7 @@ static struct sway_container *container_output_destroy( | |||
288 | output->sway_output->swayc = NULL; | 292 | output->sway_output->swayc = NULL; |
289 | output->sway_output = NULL; | 293 | output->sway_output = NULL; |
290 | 294 | ||
291 | wlr_log(L_DEBUG, "OUTPUT: Destroying output '%s'", output->name); | 295 | wlr_log(WLR_DEBUG, "OUTPUT: Destroying output '%s'", output->name); |
292 | 296 | ||
293 | return &root_container; | 297 | return &root_container; |
294 | } | 298 | } |
@@ -315,13 +319,19 @@ static struct sway_container *container_destroy_noreaping( | |||
315 | // Workspaces will refuse to be destroyed if they're the last workspace | 319 | // Workspaces will refuse to be destroyed if they're the last workspace |
316 | // on their output. | 320 | // on their output. |
317 | if (!container_workspace_destroy(con)) { | 321 | if (!container_workspace_destroy(con)) { |
318 | wlr_log(L_ERROR, "workspace doesn't want to destroy"); | 322 | wlr_log(WLR_ERROR, "workspace doesn't want to destroy"); |
319 | return NULL; | 323 | return NULL; |
320 | } | 324 | } |
321 | } | 325 | } |
322 | 326 | ||
327 | container_end_mouse_operation(con); | ||
328 | |||
323 | con->destroying = true; | 329 | con->destroying = true; |
324 | list_add(server.destroying_containers, con); | 330 | container_set_dirty(con); |
331 | |||
332 | if (con->scratchpad) { | ||
333 | scratchpad_remove_container(con); | ||
334 | } | ||
325 | 335 | ||
326 | if (!con->parent) { | 336 | if (!con->parent) { |
327 | return NULL; | 337 | return NULL; |
@@ -342,7 +352,7 @@ bool container_reap_empty(struct sway_container *con) { | |||
342 | break; | 352 | break; |
343 | case C_WORKSPACE: | 353 | case C_WORKSPACE: |
344 | if (!workspace_is_visible(con) && workspace_is_empty(con)) { | 354 | if (!workspace_is_visible(con) && workspace_is_empty(con)) { |
345 | wlr_log(L_DEBUG, "Destroying workspace via reaper"); | 355 | wlr_log(WLR_DEBUG, "Destroying workspace via reaper"); |
346 | container_destroy_noreaping(con); | 356 | container_destroy_noreaping(con); |
347 | return true; | 357 | return true; |
348 | } | 358 | } |
@@ -435,7 +445,7 @@ struct sway_container *container_view_create(struct sway_container *sibling, | |||
435 | } | 445 | } |
436 | const char *title = view_get_title(sway_view); | 446 | const char *title = view_get_title(sway_view); |
437 | struct sway_container *swayc = container_create(C_VIEW); | 447 | struct sway_container *swayc = container_create(C_VIEW); |
438 | wlr_log(L_DEBUG, "Adding new view %p:%s to container %p %d %s", | 448 | wlr_log(WLR_DEBUG, "Adding new view %p:%s to container %p %d %s", |
439 | swayc, title, sibling, sibling ? sibling->type : 0, sibling->name); | 449 | swayc, title, sibling, sibling ? sibling->type : 0, sibling->name); |
440 | // Setup values | 450 | // Setup values |
441 | swayc->sway_view = sway_view; | 451 | swayc->sway_view = sway_view; |
@@ -657,7 +667,9 @@ struct sway_container *floating_container_at(double lx, double ly, | |||
657 | if (!workspace_is_visible(workspace)) { | 667 | if (!workspace_is_visible(workspace)) { |
658 | continue; | 668 | continue; |
659 | } | 669 | } |
660 | for (int k = 0; k < ws->floating->children->length; ++k) { | 670 | // Items at the end of the list are on top, so iterate the list in |
671 | // reverse. | ||
672 | for (int k = ws->floating->children->length - 1; k >= 0; --k) { | ||
661 | struct sway_container *floater = | 673 | struct sway_container *floater = |
662 | ws->floating->children->items[k]; | 674 | ws->floating->children->items[k]; |
663 | struct wlr_box box = { | 675 | struct wlr_box box = { |
@@ -678,16 +690,23 @@ struct sway_container *floating_container_at(double lx, double ly, | |||
678 | void container_for_each_descendant_dfs(struct sway_container *container, | 690 | void container_for_each_descendant_dfs(struct sway_container *container, |
679 | void (*f)(struct sway_container *container, void *data), | 691 | void (*f)(struct sway_container *container, void *data), |
680 | void *data) { | 692 | void *data) { |
681 | if (container) { | 693 | if (!container) { |
682 | if (container->children) { | 694 | return; |
683 | for (int i = 0; i < container->children->length; ++i) { | 695 | } |
684 | struct sway_container *child = | 696 | if (container->children) { |
685 | container->children->items[i]; | 697 | for (int i = 0; i < container->children->length; ++i) { |
686 | container_for_each_descendant_dfs(child, f, data); | 698 | struct sway_container *child = container->children->items[i]; |
687 | } | 699 | container_for_each_descendant_dfs(child, f, data); |
688 | } | 700 | } |
689 | f(container, data); | ||
690 | } | 701 | } |
702 | if (container->type == C_WORKSPACE) { | ||
703 | struct sway_container *floating = container->sway_workspace->floating; | ||
704 | for (int i = 0; i < floating->children->length; ++i) { | ||
705 | struct sway_container *child = floating->children->items[i]; | ||
706 | container_for_each_descendant_dfs(child, f, data); | ||
707 | } | ||
708 | } | ||
709 | f(container, data); | ||
691 | } | 710 | } |
692 | 711 | ||
693 | void container_for_each_descendant_bfs(struct sway_container *con, | 712 | void container_for_each_descendant_bfs(struct sway_container *con, |
@@ -698,7 +717,7 @@ void container_for_each_descendant_bfs(struct sway_container *con, | |||
698 | } | 717 | } |
699 | 718 | ||
700 | if (queue == NULL) { | 719 | if (queue == NULL) { |
701 | wlr_log(L_ERROR, "could not allocate list"); | 720 | wlr_log(WLR_ERROR, "could not allocate list"); |
702 | return; | 721 | return; |
703 | } | 722 | } |
704 | 723 | ||
@@ -782,7 +801,7 @@ static void update_title_texture(struct sway_container *con, | |||
782 | 801 | ||
783 | double scale = output->sway_output->wlr_output->scale; | 802 | double scale = output->sway_output->wlr_output->scale; |
784 | int width = 0; | 803 | int width = 0; |
785 | int height = config->font_height * scale; | 804 | int height = con->title_height * scale; |
786 | 805 | ||
787 | cairo_t *c = cairo_create(NULL); | 806 | cairo_t *c = cairo_create(NULL); |
788 | get_text_size(c, config->font, &width, NULL, scale, config->pango_markup, | 807 | get_text_size(c, config->font, &width, NULL, scale, config->pango_markup, |
@@ -935,11 +954,15 @@ void container_set_floating(struct sway_container *container, bool enable) { | |||
935 | container_add_child(workspace->sway_workspace->floating, container); | 954 | container_add_child(workspace->sway_workspace->floating, container); |
936 | if (container->type == C_VIEW) { | 955 | if (container->type == C_VIEW) { |
937 | view_init_floating(container->sway_view); | 956 | view_init_floating(container->sway_view); |
957 | view_set_tiled(container->sway_view, false); | ||
938 | } | 958 | } |
939 | seat_set_focus(seat, seat_get_focus_inactive(seat, container)); | 959 | seat_set_focus(seat, seat_get_focus_inactive(seat, container)); |
940 | container_reap_empty_recursive(workspace); | 960 | container_reap_empty_recursive(workspace); |
941 | } else { | 961 | } else { |
942 | // Returning to tiled | 962 | // Returning to tiled |
963 | if (container->scratchpad) { | ||
964 | scratchpad_remove_container(container); | ||
965 | } | ||
943 | container_remove_child(container); | 966 | container_remove_child(container); |
944 | container_add_child(workspace, container); | 967 | container_add_child(workspace, container); |
945 | container->width = container->parent->width; | 968 | container->width = container->parent->width; |
@@ -951,6 +974,8 @@ void container_set_floating(struct sway_container *container, bool enable) { | |||
951 | container_reap_empty_recursive(workspace->sway_workspace->floating); | 974 | container_reap_empty_recursive(workspace->sway_workspace->floating); |
952 | } | 975 | } |
953 | 976 | ||
977 | container_end_mouse_operation(container); | ||
978 | |||
954 | ipc_event_window(container, "floating"); | 979 | ipc_event_window(container, "floating"); |
955 | } | 980 | } |
956 | 981 | ||
@@ -963,9 +988,14 @@ void container_set_geometry_from_floating_view(struct sway_container *con) { | |||
963 | return; | 988 | return; |
964 | } | 989 | } |
965 | struct sway_view *view = con->sway_view; | 990 | struct sway_view *view = con->sway_view; |
966 | size_t border_width = view->border_thickness * (view->border != B_NONE); | 991 | size_t border_width = 0; |
967 | size_t top = | 992 | size_t top = 0; |
968 | view->border == B_NORMAL ? container_titlebar_height() : border_width; | 993 | |
994 | if (!view->using_csd) { | ||
995 | border_width = view->border_thickness * (view->border != B_NONE); | ||
996 | top = view->border == B_NORMAL ? | ||
997 | container_titlebar_height() : border_width; | ||
998 | } | ||
969 | 999 | ||
970 | con->x = view->x - border_width; | 1000 | con->x = view->x - border_width; |
971 | con->y = view->y - top; | 1001 | con->y = view->y - top; |
@@ -987,3 +1017,112 @@ void container_get_box(struct sway_container *container, struct wlr_box *box) { | |||
987 | box->width = container->width; | 1017 | box->width = container->width; |
988 | box->height = container->height; | 1018 | box->height = container->height; |
989 | } | 1019 | } |
1020 | |||
1021 | /** | ||
1022 | * Translate the container's position as well as all children. | ||
1023 | */ | ||
1024 | void container_floating_translate(struct sway_container *con, | ||
1025 | double x_amount, double y_amount) { | ||
1026 | con->x += x_amount; | ||
1027 | con->y += y_amount; | ||
1028 | con->current.swayc_x += x_amount; | ||
1029 | con->current.swayc_y += y_amount; | ||
1030 | if (con->type == C_VIEW) { | ||
1031 | con->sway_view->x += x_amount; | ||
1032 | con->sway_view->y += y_amount; | ||
1033 | con->current.view_x += x_amount; | ||
1034 | con->current.view_y += y_amount; | ||
1035 | } else { | ||
1036 | for (int i = 0; i < con->children->length; ++i) { | ||
1037 | struct sway_container *child = con->children->items[i]; | ||
1038 | container_floating_translate(child, x_amount, y_amount); | ||
1039 | } | ||
1040 | } | ||
1041 | } | ||
1042 | |||
1043 | /** | ||
1044 | * Choose an output for the floating container's new position. | ||
1045 | * | ||
1046 | * If the center of the container intersects an output then we'll choose that | ||
1047 | * one, otherwise we'll choose whichever output is closest to the container's | ||
1048 | * center. | ||
1049 | */ | ||
1050 | static struct sway_container *container_floating_find_output( | ||
1051 | struct sway_container *con) { | ||
1052 | double center_x = con->x + con->width / 2; | ||
1053 | double center_y = con->y + con->height / 2; | ||
1054 | struct sway_container *closest_output = NULL; | ||
1055 | double closest_distance = DBL_MAX; | ||
1056 | for (int i = 0; i < root_container.children->length; ++i) { | ||
1057 | struct sway_container *output = root_container.children->items[i]; | ||
1058 | struct wlr_box output_box; | ||
1059 | double closest_x, closest_y; | ||
1060 | container_get_box(output, &output_box); | ||
1061 | wlr_box_closest_point(&output_box, center_x, center_y, | ||
1062 | &closest_x, &closest_y); | ||
1063 | if (center_x == closest_x && center_y == closest_y) { | ||
1064 | // The center of the floating container is on this output | ||
1065 | return output; | ||
1066 | } | ||
1067 | double x_dist = closest_x - center_x; | ||
1068 | double y_dist = closest_y - center_y; | ||
1069 | double distance = x_dist * x_dist + y_dist * y_dist; | ||
1070 | if (distance < closest_distance) { | ||
1071 | closest_output = output; | ||
1072 | closest_distance = distance; | ||
1073 | } | ||
1074 | } | ||
1075 | return closest_output; | ||
1076 | } | ||
1077 | |||
1078 | void container_floating_move_to(struct sway_container *con, | ||
1079 | double lx, double ly) { | ||
1080 | if (!sway_assert(container_is_floating(con), | ||
1081 | "Expected a floating container")) { | ||
1082 | return; | ||
1083 | } | ||
1084 | desktop_damage_whole_container(con); | ||
1085 | container_floating_translate(con, lx - con->x, ly - con->y); | ||
1086 | desktop_damage_whole_container(con); | ||
1087 | struct sway_container *old_workspace = container_parent(con, C_WORKSPACE); | ||
1088 | struct sway_container *new_output = container_floating_find_output(con); | ||
1089 | if (!sway_assert(new_output, "Unable to find any output")) { | ||
1090 | return; | ||
1091 | } | ||
1092 | struct sway_container *new_workspace = | ||
1093 | output_get_active_workspace(new_output->sway_output); | ||
1094 | if (old_workspace != new_workspace) { | ||
1095 | container_remove_child(con); | ||
1096 | container_add_child(new_workspace->sway_workspace->floating, con); | ||
1097 | arrange_windows(old_workspace); | ||
1098 | arrange_windows(new_workspace); | ||
1099 | workspace_detect_urgent(old_workspace); | ||
1100 | workspace_detect_urgent(new_workspace); | ||
1101 | } | ||
1102 | } | ||
1103 | |||
1104 | void container_set_dirty(struct sway_container *container) { | ||
1105 | if (container->dirty) { | ||
1106 | return; | ||
1107 | } | ||
1108 | container->dirty = true; | ||
1109 | list_add(server.dirty_containers, container); | ||
1110 | } | ||
1111 | |||
1112 | static bool find_urgent_iterator(struct sway_container *con, | ||
1113 | void *data) { | ||
1114 | return con->type == C_VIEW && view_is_urgent(con->sway_view); | ||
1115 | } | ||
1116 | |||
1117 | bool container_has_urgent_child(struct sway_container *container) { | ||
1118 | return container_find(container, find_urgent_iterator, NULL); | ||
1119 | } | ||
1120 | |||
1121 | void container_end_mouse_operation(struct sway_container *container) { | ||
1122 | struct sway_seat *seat; | ||
1123 | wl_list_for_each(seat, &input_manager->seats, link) { | ||
1124 | if (seat->op_container == container) { | ||
1125 | seat_end_mouse_operation(seat); | ||
1126 | } | ||
1127 | } | ||
1128 | } | ||
diff --git a/sway/tree/layout.c b/sway/tree/layout.c index 14631ad4..a2be0ef3 100644 --- a/sway/tree/layout.c +++ b/sway/tree/layout.c | |||
@@ -22,7 +22,8 @@ struct sway_container root_container; | |||
22 | 22 | ||
23 | static void output_layout_handle_change(struct wl_listener *listener, | 23 | static void output_layout_handle_change(struct wl_listener *listener, |
24 | void *data) { | 24 | void *data) { |
25 | arrange_and_commit(&root_container); | 25 | arrange_windows(&root_container); |
26 | transaction_commit_dirty(); | ||
26 | } | 27 | } |
27 | 28 | ||
28 | void layout_init(void) { | 29 | void layout_init(void) { |
@@ -41,6 +42,7 @@ void layout_init(void) { | |||
41 | wl_list_init(&root_container.sway_root->xwayland_unmanaged); | 42 | wl_list_init(&root_container.sway_root->xwayland_unmanaged); |
42 | wl_list_init(&root_container.sway_root->drag_icons); | 43 | wl_list_init(&root_container.sway_root->drag_icons); |
43 | wl_signal_init(&root_container.sway_root->events.new_container); | 44 | wl_signal_init(&root_container.sway_root->events.new_container); |
45 | root_container.sway_root->scratchpad = create_list(); | ||
44 | 46 | ||
45 | root_container.sway_root->output_layout_change.notify = | 47 | root_container.sway_root->output_layout_change.notify = |
46 | output_layout_handle_change; | 48 | output_layout_handle_change; |
@@ -101,7 +103,7 @@ void container_insert_child(struct sway_container *parent, | |||
101 | if (old_parent) { | 103 | if (old_parent) { |
102 | container_remove_child(child); | 104 | container_remove_child(child); |
103 | } | 105 | } |
104 | wlr_log(L_DEBUG, "Inserting id:%zd at index %d", child->id, i); | 106 | wlr_log(WLR_DEBUG, "Inserting id:%zd at index %d", child->id, i); |
105 | list_insert(parent->children, i, child); | 107 | list_insert(parent->children, i, child); |
106 | child->parent = parent; | 108 | child->parent = parent; |
107 | container_handle_fullscreen_reparent(child, old_parent); | 109 | container_handle_fullscreen_reparent(child, old_parent); |
@@ -127,13 +129,17 @@ struct sway_container *container_add_sibling(struct sway_container *fixed, | |||
127 | 129 | ||
128 | void container_add_child(struct sway_container *parent, | 130 | void container_add_child(struct sway_container *parent, |
129 | struct sway_container *child) { | 131 | struct sway_container *child) { |
130 | wlr_log(L_DEBUG, "Adding %p (%d, %fx%f) to %p (%d, %fx%f)", | 132 | wlr_log(WLR_DEBUG, "Adding %p (%d, %fx%f) to %p (%d, %fx%f)", |
131 | child, child->type, child->width, child->height, | 133 | child, child->type, child->width, child->height, |
132 | parent, parent->type, parent->width, parent->height); | 134 | parent, parent->type, parent->width, parent->height); |
133 | struct sway_container *old_parent = child->parent; | 135 | struct sway_container *old_parent = child->parent; |
134 | list_add(parent->children, child); | 136 | list_add(parent->children, child); |
135 | child->parent = parent; | 137 | child->parent = parent; |
136 | container_handle_fullscreen_reparent(child, old_parent); | 138 | container_handle_fullscreen_reparent(child, old_parent); |
139 | if (old_parent) { | ||
140 | container_set_dirty(old_parent); | ||
141 | } | ||
142 | container_set_dirty(child); | ||
137 | } | 143 | } |
138 | 144 | ||
139 | struct sway_container *container_remove_child(struct sway_container *child) { | 145 | struct sway_container *container_remove_child(struct sway_container *child) { |
@@ -152,6 +158,9 @@ struct sway_container *container_remove_child(struct sway_container *child) { | |||
152 | child->parent = NULL; | 158 | child->parent = NULL; |
153 | container_notify_subtree_changed(parent); | 159 | container_notify_subtree_changed(parent); |
154 | 160 | ||
161 | container_set_dirty(parent); | ||
162 | container_set_dirty(child); | ||
163 | |||
155 | return parent; | 164 | return parent; |
156 | } | 165 | } |
157 | 166 | ||
@@ -168,7 +177,12 @@ void container_move_to(struct sway_container *container, | |||
168 | struct sway_container *old_parent = container_remove_child(container); | 177 | struct sway_container *old_parent = container_remove_child(container); |
169 | container->width = container->height = 0; | 178 | container->width = container->height = 0; |
170 | container->saved_width = container->saved_height = 0; | 179 | container->saved_width = container->saved_height = 0; |
171 | struct sway_container *new_parent; | 180 | |
181 | struct sway_container *new_parent, *new_parent_focus; | ||
182 | struct sway_seat *seat = input_manager_get_default_seat(input_manager); | ||
183 | |||
184 | // Get the focus of the destination before we change it. | ||
185 | new_parent_focus = seat_get_focus_inactive(seat, destination); | ||
172 | if (destination->type == C_VIEW) { | 186 | if (destination->type == C_VIEW) { |
173 | new_parent = container_add_sibling(destination, container); | 187 | new_parent = container_add_sibling(destination, container); |
174 | } else { | 188 | } else { |
@@ -176,17 +190,20 @@ void container_move_to(struct sway_container *container, | |||
176 | container_add_child(destination, container); | 190 | container_add_child(destination, container); |
177 | } | 191 | } |
178 | wl_signal_emit(&container->events.reparent, old_parent); | 192 | wl_signal_emit(&container->events.reparent, old_parent); |
193 | |||
179 | if (container->type == C_WORKSPACE) { | 194 | if (container->type == C_WORKSPACE) { |
180 | // If moving a workspace to a new output, maybe create a new workspace | 195 | // If moving a workspace to a new output, maybe create a new workspace |
181 | // on the previous output | 196 | // on the previous output |
182 | struct sway_seat *seat = input_manager_get_default_seat(input_manager); | ||
183 | if (old_parent->children->length == 0) { | 197 | if (old_parent->children->length == 0) { |
184 | char *ws_name = workspace_next_name(old_parent->name); | 198 | char *ws_name = workspace_next_name(old_parent->name); |
185 | struct sway_container *ws = | 199 | struct sway_container *ws = workspace_create(old_parent, ws_name); |
186 | workspace_create(old_parent, ws_name); | ||
187 | free(ws_name); | 200 | free(ws_name); |
188 | seat_set_focus(seat, ws); | 201 | seat_set_focus(seat, ws); |
189 | } | 202 | } |
203 | |||
204 | // Try to remove an empty workspace from the destination output. | ||
205 | container_reap_empty_recursive(new_parent_focus); | ||
206 | |||
190 | container_sort_workspaces(new_parent); | 207 | container_sort_workspaces(new_parent); |
191 | seat_set_focus(seat, new_parent); | 208 | seat_set_focus(seat, new_parent); |
192 | workspace_output_raise_priority(container, old_parent, new_parent); | 209 | workspace_output_raise_priority(container, old_parent, new_parent); |
@@ -216,6 +233,17 @@ void container_move_to(struct sway_container *container, | |||
216 | } | 233 | } |
217 | } | 234 | } |
218 | } | 235 | } |
236 | // Update workspace urgent state | ||
237 | struct sway_container *old_workspace = old_parent; | ||
238 | if (old_workspace->type != C_WORKSPACE) { | ||
239 | old_workspace = container_parent(old_workspace, C_WORKSPACE); | ||
240 | } | ||
241 | if (new_workspace != old_workspace) { | ||
242 | workspace_detect_urgent(new_workspace); | ||
243 | if (old_workspace) { | ||
244 | workspace_detect_urgent(old_workspace); | ||
245 | } | ||
246 | } | ||
219 | } | 247 | } |
220 | 248 | ||
221 | static bool sway_dir_to_wlr(enum movement_direction dir, | 249 | static bool sway_dir_to_wlr(enum movement_direction dir, |
@@ -311,13 +339,13 @@ static void move_out_of_tabs_stacks(struct sway_container *container, | |||
311 | int offs) { | 339 | int offs) { |
312 | if (container->parent == current->parent | 340 | if (container->parent == current->parent |
313 | && current->parent->children->length == 1) { | 341 | && current->parent->children->length == 1) { |
314 | wlr_log(L_DEBUG, "Changing layout of %zd", current->parent->id); | 342 | wlr_log(WLR_DEBUG, "Changing layout of %zd", current->parent->id); |
315 | current->parent->layout = move_dir == | 343 | current->parent->layout = move_dir == |
316 | MOVE_LEFT || move_dir == MOVE_RIGHT ? L_HORIZ : L_VERT; | 344 | MOVE_LEFT || move_dir == MOVE_RIGHT ? L_HORIZ : L_VERT; |
317 | return; | 345 | return; |
318 | } | 346 | } |
319 | 347 | ||
320 | wlr_log(L_DEBUG, "Moving out of tab/stack into a split"); | 348 | wlr_log(WLR_DEBUG, "Moving out of tab/stack into a split"); |
321 | bool is_workspace = current->parent->type == C_WORKSPACE; | 349 | bool is_workspace = current->parent->type == C_WORKSPACE; |
322 | struct sway_container *new_parent = container_split(current->parent, | 350 | struct sway_container *new_parent = container_split(current->parent, |
323 | move_dir == MOVE_LEFT || move_dir == MOVE_RIGHT ? L_HORIZ : L_VERT); | 351 | move_dir == MOVE_LEFT || move_dir == MOVE_RIGHT ? L_HORIZ : L_VERT); |
@@ -362,7 +390,7 @@ void container_move(struct sway_container *container, | |||
362 | } | 390 | } |
363 | 391 | ||
364 | parent = current->parent; | 392 | parent = current->parent; |
365 | wlr_log(L_DEBUG, "Visiting %p %s '%s'", current, | 393 | wlr_log(WLR_DEBUG, "Visiting %p %s '%s'", current, |
366 | container_type_to_str(current->type), current->name); | 394 | container_type_to_str(current->type), current->name); |
367 | 395 | ||
368 | int index = index_child(current); | 396 | int index = index_child(current); |
@@ -380,12 +408,12 @@ void container_move(struct sway_container *container, | |||
380 | root_container.sway_root->output_layout, wlr_dir, | 408 | root_container.sway_root->output_layout, wlr_dir, |
381 | current->sway_output->wlr_output, ref_lx, ref_ly); | 409 | current->sway_output->wlr_output, ref_lx, ref_ly); |
382 | if (!next) { | 410 | if (!next) { |
383 | wlr_log(L_DEBUG, "Hit edge of output, nowhere else to go"); | 411 | wlr_log(WLR_DEBUG, "Hit edge of output, nowhere else to go"); |
384 | return; | 412 | return; |
385 | } | 413 | } |
386 | struct sway_output *next_output = next->data; | 414 | struct sway_output *next_output = next->data; |
387 | current = next_output->swayc; | 415 | current = next_output->swayc; |
388 | wlr_log(L_DEBUG, "Selected next output (%s)", current->name); | 416 | wlr_log(WLR_DEBUG, "Selected next output (%s)", current->name); |
389 | // Select workspace and get outta here | 417 | // Select workspace and get outta here |
390 | current = seat_get_focus_inactive( | 418 | current = seat_get_focus_inactive( |
391 | config->handler_context.seat, current); | 419 | config->handler_context.seat, current); |
@@ -398,20 +426,20 @@ void container_move(struct sway_container *container, | |||
398 | case C_WORKSPACE: | 426 | case C_WORKSPACE: |
399 | if (!is_parallel(current->layout, move_dir)) { | 427 | if (!is_parallel(current->layout, move_dir)) { |
400 | if (current->children->length >= 2) { | 428 | if (current->children->length >= 2) { |
401 | wlr_log(L_DEBUG, "Rejiggering the workspace (%d kiddos)", | 429 | wlr_log(WLR_DEBUG, "Rejiggering the workspace (%d kiddos)", |
402 | current->children->length); | 430 | current->children->length); |
403 | workspace_rejigger(current, container, move_dir); | 431 | workspace_rejigger(current, container, move_dir); |
404 | return; | 432 | return; |
405 | } else { | 433 | } else { |
406 | wlr_log(L_DEBUG, "Selecting output"); | 434 | wlr_log(WLR_DEBUG, "Selecting output"); |
407 | current = current->parent; | 435 | current = current->parent; |
408 | } | 436 | } |
409 | } else if (current->layout == L_TABBED | 437 | } else if (current->layout == L_TABBED |
410 | || current->layout == L_STACKED) { | 438 | || current->layout == L_STACKED) { |
411 | wlr_log(L_DEBUG, "Rejiggering out of tabs/stacks"); | 439 | wlr_log(WLR_DEBUG, "Rejiggering out of tabs/stacks"); |
412 | workspace_rejigger(current, container, move_dir); | 440 | workspace_rejigger(current, container, move_dir); |
413 | } else { | 441 | } else { |
414 | wlr_log(L_DEBUG, "Selecting output"); | 442 | wlr_log(WLR_DEBUG, "Selecting output"); |
415 | current = current->parent; | 443 | current = current->parent; |
416 | } | 444 | } |
417 | break; | 445 | break; |
@@ -427,11 +455,11 @@ void container_move(struct sway_container *container, | |||
427 | move_dir, offs); | 455 | move_dir, offs); |
428 | return; | 456 | return; |
429 | } else { | 457 | } else { |
430 | wlr_log(L_DEBUG, "Hit limit, selecting parent"); | 458 | wlr_log(WLR_DEBUG, "Hit limit, selecting parent"); |
431 | current = current->parent; | 459 | current = current->parent; |
432 | } | 460 | } |
433 | } else { | 461 | } else { |
434 | wlr_log(L_DEBUG, "Hit limit, " | 462 | wlr_log(WLR_DEBUG, "Hit limit, " |
435 | "promoting descendant to sibling"); | 463 | "promoting descendant to sibling"); |
436 | // Special case | 464 | // Special case |
437 | container_insert_child(current->parent, container, | 465 | container_insert_child(current->parent, container, |
@@ -441,14 +469,14 @@ void container_move(struct sway_container *container, | |||
441 | } | 469 | } |
442 | } else { | 470 | } else { |
443 | sibling = parent->children->items[index + offs]; | 471 | sibling = parent->children->items[index + offs]; |
444 | wlr_log(L_DEBUG, "Selecting sibling id:%zd", sibling->id); | 472 | wlr_log(WLR_DEBUG, "Selecting sibling id:%zd", sibling->id); |
445 | } | 473 | } |
446 | } else if (parent->layout == L_TABBED | 474 | } else if (parent->layout == L_TABBED |
447 | || parent->layout == L_STACKED) { | 475 | || parent->layout == L_STACKED) { |
448 | move_out_of_tabs_stacks(container, current, move_dir, offs); | 476 | move_out_of_tabs_stacks(container, current, move_dir, offs); |
449 | return; | 477 | return; |
450 | } else { | 478 | } else { |
451 | wlr_log(L_DEBUG, "Moving up to find a parallel container"); | 479 | wlr_log(WLR_DEBUG, "Moving up to find a parallel container"); |
452 | current = current->parent; | 480 | current = current->parent; |
453 | } | 481 | } |
454 | break; | 482 | break; |
@@ -467,11 +495,11 @@ void container_move(struct sway_container *container, | |||
467 | switch (sibling->type) { | 495 | switch (sibling->type) { |
468 | case C_VIEW: | 496 | case C_VIEW: |
469 | if (sibling->parent == container->parent) { | 497 | if (sibling->parent == container->parent) { |
470 | wlr_log(L_DEBUG, "Swapping siblings"); | 498 | wlr_log(WLR_DEBUG, "Swapping siblings"); |
471 | sibling->parent->children->items[index + offs] = container; | 499 | sibling->parent->children->items[index + offs] = container; |
472 | sibling->parent->children->items[index] = sibling; | 500 | sibling->parent->children->items[index] = sibling; |
473 | } else { | 501 | } else { |
474 | wlr_log(L_DEBUG, "Promoting to sibling of cousin"); | 502 | wlr_log(WLR_DEBUG, "Promoting to sibling of cousin"); |
475 | container_insert_child(sibling->parent, container, | 503 | container_insert_child(sibling->parent, container, |
476 | index_child(sibling) + (offs > 0 ? 0 : 1)); | 504 | index_child(sibling) + (offs > 0 ? 0 : 1)); |
477 | container->width = container->height = 0; | 505 | container->width = container->height = 0; |
@@ -482,31 +510,31 @@ void container_move(struct sway_container *container, | |||
482 | case C_CONTAINER: | 510 | case C_CONTAINER: |
483 | if (is_parallel(sibling->layout, move_dir)) { | 511 | if (is_parallel(sibling->layout, move_dir)) { |
484 | int limit = container_limit(sibling, invert_movement(move_dir)); | 512 | int limit = container_limit(sibling, invert_movement(move_dir)); |
485 | wlr_log(L_DEBUG, "limit: %d", limit); | 513 | wlr_log(WLR_DEBUG, "limit: %d", limit); |
486 | wlr_log(L_DEBUG, | 514 | wlr_log(WLR_DEBUG, |
487 | "Reparenting container (parallel) to index %d " | 515 | "Reparenting container (parallel) to index %d " |
488 | "(move dir: %d)", limit, move_dir); | 516 | "(move dir: %d)", limit, move_dir); |
489 | container_insert_child(sibling, container, limit); | 517 | container_insert_child(sibling, container, limit); |
490 | container->width = container->height = 0; | 518 | container->width = container->height = 0; |
491 | sibling = NULL; | 519 | sibling = NULL; |
492 | } else { | 520 | } else { |
493 | wlr_log(L_DEBUG, "Reparenting container (perpendicular)"); | 521 | wlr_log(WLR_DEBUG, "Reparenting container (perpendicular)"); |
494 | struct sway_container *focus_inactive = seat_get_focus_inactive( | 522 | struct sway_container *focus_inactive = seat_get_focus_inactive( |
495 | config->handler_context.seat, sibling); | 523 | config->handler_context.seat, sibling); |
496 | if (focus_inactive && focus_inactive != sibling) { | 524 | if (focus_inactive && focus_inactive != sibling) { |
497 | while (focus_inactive->parent != sibling) { | 525 | while (focus_inactive->parent != sibling) { |
498 | focus_inactive = focus_inactive->parent; | 526 | focus_inactive = focus_inactive->parent; |
499 | } | 527 | } |
500 | wlr_log(L_DEBUG, "Focus inactive: id:%zd", | 528 | wlr_log(WLR_DEBUG, "Focus inactive: id:%zd", |
501 | focus_inactive->id); | 529 | focus_inactive->id); |
502 | sibling = focus_inactive; | 530 | sibling = focus_inactive; |
503 | continue; | 531 | continue; |
504 | } else if (sibling->children->length) { | 532 | } else if (sibling->children->length) { |
505 | wlr_log(L_DEBUG, "No focus-inactive, adding arbitrarily"); | 533 | wlr_log(WLR_DEBUG, "No focus-inactive, adding arbitrarily"); |
506 | container_remove_child(container); | 534 | container_remove_child(container); |
507 | container_add_sibling(sibling->children->items[0], container); | 535 | container_add_sibling(sibling->children->items[0], container); |
508 | } else { | 536 | } else { |
509 | wlr_log(L_DEBUG, "No kiddos, adding container alone"); | 537 | wlr_log(WLR_DEBUG, "No kiddos, adding container alone"); |
510 | container_remove_child(container); | 538 | container_remove_child(container); |
511 | container_add_child(sibling, container); | 539 | container_add_child(sibling, container); |
512 | } | 540 | } |
@@ -539,7 +567,10 @@ void container_move(struct sway_container *container, | |||
539 | } | 567 | } |
540 | if (last_ws && next_ws && last_ws != next_ws) { | 568 | if (last_ws && next_ws && last_ws != next_ws) { |
541 | ipc_event_workspace(last_ws, container, "focus"); | 569 | ipc_event_workspace(last_ws, container, "focus"); |
570 | workspace_detect_urgent(last_ws); | ||
571 | workspace_detect_urgent(next_ws); | ||
542 | } | 572 | } |
573 | container_end_mouse_operation(container); | ||
543 | } | 574 | } |
544 | 575 | ||
545 | enum sway_container_layout container_get_default_layout( | 576 | enum sway_container_layout container_get_default_layout( |
@@ -603,7 +634,7 @@ static struct sway_container *get_swayc_in_output_direction( | |||
603 | } | 634 | } |
604 | 635 | ||
605 | if (ws == NULL) { | 636 | if (ws == NULL) { |
606 | wlr_log(L_ERROR, "got an output without a workspace"); | 637 | wlr_log(WLR_ERROR, "got an output without a workspace"); |
607 | return NULL; | 638 | return NULL; |
608 | } | 639 | } |
609 | 640 | ||
@@ -775,7 +806,7 @@ struct sway_container *container_get_in_direction( | |||
775 | } else { | 806 | } else { |
776 | struct sway_container *desired_con = | 807 | struct sway_container *desired_con = |
777 | parent->children->items[desired]; | 808 | parent->children->items[desired]; |
778 | wlr_log(L_DEBUG, | 809 | wlr_log(WLR_DEBUG, |
779 | "cont %d-%p dir %i sibling %d: %p", idx, | 810 | "cont %d-%p dir %i sibling %d: %p", idx, |
780 | container, dir, desired, desired_con); | 811 | container, dir, desired, desired_con); |
781 | return seat_get_focus_inactive_view(seat, desired_con); | 812 | return seat_get_focus_inactive_view(seat, desired_con); |
@@ -840,7 +871,7 @@ struct sway_container *container_split(struct sway_container *child, | |||
840 | 871 | ||
841 | struct sway_container *cont = container_create(C_CONTAINER); | 872 | struct sway_container *cont = container_create(C_CONTAINER); |
842 | 873 | ||
843 | wlr_log(L_DEBUG, "creating container %p around %p", cont, child); | 874 | wlr_log(WLR_DEBUG, "creating container %p around %p", cont, child); |
844 | 875 | ||
845 | remove_gaps(child); | 876 | remove_gaps(child); |
846 | 877 | ||
@@ -888,7 +919,7 @@ struct sway_container *container_split(struct sway_container *child, | |||
888 | void container_recursive_resize(struct sway_container *container, | 919 | void container_recursive_resize(struct sway_container *container, |
889 | double amount, enum resize_edge edge) { | 920 | double amount, enum resize_edge edge) { |
890 | bool layout_match = true; | 921 | bool layout_match = true; |
891 | wlr_log(L_DEBUG, "Resizing %p with amount: %f", container, amount); | 922 | wlr_log(WLR_DEBUG, "Resizing %p with amount: %f", container, amount); |
892 | if (edge == RESIZE_EDGE_LEFT || edge == RESIZE_EDGE_RIGHT) { | 923 | if (edge == RESIZE_EDGE_LEFT || edge == RESIZE_EDGE_RIGHT) { |
893 | container->width += amount; | 924 | container->width += amount; |
894 | layout_match = container->layout == L_HORIZ; | 925 | layout_match = container->layout == L_HORIZ; |
@@ -978,7 +1009,7 @@ void container_swap(struct sway_container *con1, struct sway_container *con2) { | |||
978 | return; | 1009 | return; |
979 | } | 1010 | } |
980 | 1011 | ||
981 | wlr_log(L_DEBUG, "Swapping containers %zu and %zu", con1->id, con2->id); | 1012 | wlr_log(WLR_DEBUG, "Swapping containers %zu and %zu", con1->id, con2->id); |
982 | 1013 | ||
983 | int fs1 = con1->type == C_VIEW && con1->sway_view->is_fullscreen; | 1014 | int fs1 = con1->type == C_VIEW && con1->sway_view->is_fullscreen; |
984 | int fs2 = con2->type == C_VIEW && con2->sway_view->is_fullscreen; | 1015 | int fs2 = con2->type == C_VIEW && con2->sway_view->is_fullscreen; |
diff --git a/sway/tree/output.c b/sway/tree/output.c index e2927cdb..da535c18 100644 --- a/sway/tree/output.c +++ b/sway/tree/output.c | |||
@@ -43,11 +43,11 @@ struct sway_container *output_create( | |||
43 | 43 | ||
44 | if (strcasecmp(name, cur->name) == 0 || | 44 | if (strcasecmp(name, cur->name) == 0 || |
45 | strcasecmp(identifier, cur->name) == 0) { | 45 | strcasecmp(identifier, cur->name) == 0) { |
46 | wlr_log(L_DEBUG, "Matched output config for %s", name); | 46 | wlr_log(WLR_DEBUG, "Matched output config for %s", name); |
47 | oc = cur; | 47 | oc = cur; |
48 | } | 48 | } |
49 | if (strcasecmp("*", cur->name) == 0) { | 49 | if (strcasecmp("*", cur->name) == 0) { |
50 | wlr_log(L_DEBUG, "Matched wildcard output config for %s", name); | 50 | wlr_log(WLR_DEBUG, "Matched wildcard output config for %s", name); |
51 | all = cur; | 51 | all = cur; |
52 | } | 52 | } |
53 | 53 | ||
@@ -86,7 +86,7 @@ struct sway_container *output_create( | |||
86 | if (!output->children->length) { | 86 | if (!output->children->length) { |
87 | // Create workspace | 87 | // Create workspace |
88 | char *ws_name = workspace_next_name(output->name); | 88 | char *ws_name = workspace_next_name(output->name); |
89 | wlr_log(L_DEBUG, "Creating default workspace %s", ws_name); | 89 | wlr_log(WLR_DEBUG, "Creating default workspace %s", ws_name); |
90 | struct sway_container *ws = workspace_create(output, ws_name); | 90 | struct sway_container *ws = workspace_create(output, ws_name); |
91 | // Set each seat's focus if not already set | 91 | // Set each seat's focus if not already set |
92 | struct sway_seat *seat = NULL; | 92 | struct sway_seat *seat = NULL; |
diff --git a/sway/tree/view.c b/sway/tree/view.c index 24fb6864..a55c8a29 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c | |||
@@ -26,6 +26,7 @@ void view_init(struct sway_view *view, enum sway_view_type type, | |||
26 | view->impl = impl; | 26 | view->impl = impl; |
27 | view->executed_criteria = create_list(); | 27 | view->executed_criteria = create_list(); |
28 | view->marks = create_list(); | 28 | view->marks = create_list(); |
29 | view->allow_request_urgent = true; | ||
29 | wl_signal_init(&view->events.unmap); | 30 | wl_signal_init(&view->events.unmap); |
30 | } | 31 | } |
31 | 32 | ||
@@ -141,6 +142,19 @@ const char *view_get_shell(struct sway_view *view) { | |||
141 | return "unknown"; | 142 | return "unknown"; |
142 | } | 143 | } |
143 | 144 | ||
145 | void view_get_constraints(struct sway_view *view, double *min_width, | ||
146 | double *max_width, double *min_height, double *max_height) { | ||
147 | if (view->impl->get_constraints) { | ||
148 | view->impl->get_constraints(view, | ||
149 | min_width, max_width, min_height, max_height); | ||
150 | } else { | ||
151 | *min_width = DBL_MIN; | ||
152 | *max_width = DBL_MAX; | ||
153 | *min_height = DBL_MIN; | ||
154 | *max_height = DBL_MAX; | ||
155 | } | ||
156 | } | ||
157 | |||
144 | uint32_t view_configure(struct sway_view *view, double lx, double ly, int width, | 158 | uint32_t view_configure(struct sway_view *view, double lx, double ly, int width, |
145 | int height) { | 159 | int height) { |
146 | if (view->impl->configure) { | 160 | if (view->impl->configure) { |
@@ -151,12 +165,43 @@ uint32_t view_configure(struct sway_view *view, double lx, double ly, int width, | |||
151 | 165 | ||
152 | void view_init_floating(struct sway_view *view) { | 166 | void view_init_floating(struct sway_view *view) { |
153 | struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); | 167 | struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); |
154 | int max_width = ws->width * 0.6666; | 168 | int min_width, min_height; |
155 | int max_height = ws->height * 0.6666; | 169 | int max_width, max_height; |
156 | view->width = | 170 | |
157 | view->natural_width > max_width ? max_width : view->natural_width; | 171 | if (config->floating_minimum_width == -1) { // no minimum |
158 | view->height = | 172 | min_width = 0; |
159 | view->natural_height > max_height ? max_height : view->natural_height; | 173 | } else if (config->floating_minimum_width == 0) { // automatic |
174 | min_width = 75; | ||
175 | } else { | ||
176 | min_width = config->floating_minimum_width; | ||
177 | } | ||
178 | |||
179 | if (config->floating_minimum_height == -1) { // no minimum | ||
180 | min_height = 0; | ||
181 | } else if (config->floating_minimum_height == 0) { // automatic | ||
182 | min_height = 50; | ||
183 | } else { | ||
184 | min_height = config->floating_minimum_height; | ||
185 | } | ||
186 | |||
187 | if (config->floating_maximum_width == -1) { // no maximum | ||
188 | max_width = INT_MAX; | ||
189 | } else if (config->floating_maximum_width == 0) { // automatic | ||
190 | max_width = ws->width * 0.6666; | ||
191 | } else { | ||
192 | max_width = config->floating_maximum_width; | ||
193 | } | ||
194 | |||
195 | if (config->floating_maximum_height == -1) { // no maximum | ||
196 | max_height = INT_MAX; | ||
197 | } else if (config->floating_maximum_height == 0) { // automatic | ||
198 | max_height = ws->height * 0.6666; | ||
199 | } else { | ||
200 | max_height = config->floating_maximum_height; | ||
201 | } | ||
202 | |||
203 | view->width = fmax(min_width, fmin(view->natural_width, max_width)); | ||
204 | view->height = fmax(min_height, fmin(view->natural_height, max_height)); | ||
160 | view->x = ws->x + (ws->width - view->width) / 2; | 205 | view->x = ws->x + (ws->width - view->width) / 2; |
161 | view->y = ws->y + (ws->height - view->height) / 2; | 206 | view->y = ws->y + (ws->height - view->height) / 2; |
162 | 207 | ||
@@ -165,9 +210,6 @@ void view_init_floating(struct sway_view *view) { | |||
165 | view->border_left = view->border_right = true; | 210 | view->border_left = view->border_right = true; |
166 | 211 | ||
167 | container_set_geometry_from_floating_view(view->swayc); | 212 | container_set_geometry_from_floating_view(view->swayc); |
168 | |||
169 | // Don't maximize floating windows | ||
170 | view_set_tiled(view, false); | ||
171 | } | 213 | } |
172 | 214 | ||
173 | void view_autoconfigure(struct sway_view *view) { | 215 | void view_autoconfigure(struct sway_view *view) { |
@@ -279,7 +321,6 @@ void view_autoconfigure(struct sway_view *view) { | |||
279 | view->y = y; | 321 | view->y = y; |
280 | view->width = width; | 322 | view->width = width; |
281 | view->height = height; | 323 | view->height = height; |
282 | view_set_tiled(view, true); | ||
283 | } | 324 | } |
284 | 325 | ||
285 | void view_set_activated(struct sway_view *view, bool activated) { | 326 | void view_set_activated(struct sway_view *view, bool activated) { |
@@ -289,7 +330,15 @@ void view_set_activated(struct sway_view *view, bool activated) { | |||
289 | } | 330 | } |
290 | 331 | ||
291 | void view_set_tiled(struct sway_view *view, bool tiled) { | 332 | void view_set_tiled(struct sway_view *view, bool tiled) { |
292 | view->border = tiled ? config->border : B_NONE; | 333 | if (!tiled) { |
334 | view->using_csd = true; | ||
335 | if (view->impl->has_client_side_decorations) { | ||
336 | view->using_csd = view->impl->has_client_side_decorations(view); | ||
337 | } | ||
338 | } else { | ||
339 | view->using_csd = false; | ||
340 | } | ||
341 | |||
293 | if (view->impl->set_tiled) { | 342 | if (view->impl->set_tiled) { |
294 | view->impl->set_tiled(view, tiled); | 343 | view->impl->set_tiled(view, tiled); |
295 | } | 344 | } |
@@ -352,6 +401,8 @@ void view_set_fullscreen(struct sway_view *view, bool fullscreen) { | |||
352 | } | 401 | } |
353 | } | 402 | } |
354 | 403 | ||
404 | container_end_mouse_operation(view->swayc); | ||
405 | |||
355 | ipc_event_window(view->swayc, "fullscreen_mode"); | 406 | ipc_event_window(view->swayc, "fullscreen_mode"); |
356 | } | 407 | } |
357 | 408 | ||
@@ -467,27 +518,45 @@ void view_execute_criteria(struct sway_view *view) { | |||
467 | list_t *criterias = criteria_for_view(view, CT_COMMAND); | 518 | list_t *criterias = criteria_for_view(view, CT_COMMAND); |
468 | for (int i = 0; i < criterias->length; i++) { | 519 | for (int i = 0; i < criterias->length; i++) { |
469 | struct criteria *criteria = criterias->items[i]; | 520 | struct criteria *criteria = criterias->items[i]; |
470 | wlr_log(L_DEBUG, "Checking criteria %s", criteria->raw); | 521 | wlr_log(WLR_DEBUG, "Checking criteria %s", criteria->raw); |
471 | if (view_has_executed_criteria(view, criteria)) { | 522 | if (view_has_executed_criteria(view, criteria)) { |
472 | wlr_log(L_DEBUG, "Criteria already executed"); | 523 | wlr_log(WLR_DEBUG, "Criteria already executed"); |
473 | continue; | 524 | continue; |
474 | } | 525 | } |
475 | wlr_log(L_DEBUG, "for_window '%s' matches view %p, cmd: '%s'", | 526 | wlr_log(WLR_DEBUG, "for_window '%s' matches view %p, cmd: '%s'", |
476 | criteria->raw, view, criteria->cmdlist); | 527 | criteria->raw, view, criteria->cmdlist); |
528 | seat_set_focus(seat, view->swayc); | ||
477 | list_add(view->executed_criteria, criteria); | 529 | list_add(view->executed_criteria, criteria); |
478 | struct cmd_results *res = execute_command(criteria->cmdlist, NULL); | 530 | struct cmd_results *res = execute_command(criteria->cmdlist, NULL); |
479 | if (res->status != CMD_SUCCESS) { | 531 | if (res->status != CMD_SUCCESS) { |
480 | wlr_log(L_ERROR, "Command '%s' failed: %s", res->input, res->error); | 532 | wlr_log(WLR_ERROR, "Command '%s' failed: %s", res->input, res->error); |
481 | } | 533 | } |
482 | free_cmd_results(res); | 534 | free_cmd_results(res); |
483 | // view must be focused for commands to affect it, | ||
484 | // so always refocus in-between command lists | ||
485 | seat_set_focus(seat, view->swayc); | ||
486 | } | 535 | } |
487 | list_free(criterias); | 536 | list_free(criterias); |
488 | seat_set_focus(seat, prior_focus); | 537 | seat_set_focus(seat, prior_focus); |
489 | } | 538 | } |
490 | 539 | ||
540 | static bool should_focus(struct sway_view *view) { | ||
541 | // If the view is the only one in the focused workspace, it'll get focus | ||
542 | // regardless of any no_focus criteria. | ||
543 | struct sway_container *parent = view->swayc->parent; | ||
544 | struct sway_seat *seat = input_manager_current_seat(input_manager); | ||
545 | if (parent->type == C_WORKSPACE && seat_get_focus(seat) == parent) { | ||
546 | size_t num_children = parent->children->length + | ||
547 | parent->sway_workspace->floating->children->length; | ||
548 | if (num_children == 1) { | ||
549 | return true; | ||
550 | } | ||
551 | } | ||
552 | |||
553 | // Check no_focus criteria | ||
554 | list_t *criterias = criteria_for_view(view, CT_NO_FOCUS); | ||
555 | size_t len = criterias->length; | ||
556 | list_free(criterias); | ||
557 | return len == 0; | ||
558 | } | ||
559 | |||
491 | void view_map(struct sway_view *view, struct wlr_surface *wlr_surface) { | 560 | void view_map(struct sway_view *view, struct wlr_surface *wlr_surface) { |
492 | if (!sway_assert(view->surface == NULL, "cannot map mapped view")) { | 561 | if (!sway_assert(view->surface == NULL, "cannot map mapped view")) { |
493 | return; | 562 | return; |
@@ -524,7 +593,11 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface) { | |||
524 | prev_focus = target_sibling; | 593 | prev_focus = target_sibling; |
525 | target_sibling = seat_get_focus_inactive(seat, workspace); | 594 | target_sibling = seat_get_focus_inactive(seat, workspace); |
526 | } else { | 595 | } else { |
527 | // TODO: CT_ASSIGN_OUTPUT | 596 | // CT_ASSIGN_OUTPUT |
597 | struct sway_container *output = output_by_name(criteria->target); | ||
598 | if (output) { | ||
599 | prev_focus = seat_get_focus_inactive(seat, output); | ||
600 | } | ||
528 | } | 601 | } |
529 | } | 602 | } |
530 | list_free(criterias); | 603 | list_free(criterias); |
@@ -549,8 +622,6 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface) { | |||
549 | 622 | ||
550 | view->surface = wlr_surface; | 623 | view->surface = wlr_surface; |
551 | view->swayc = cont; | 624 | view->swayc = cont; |
552 | view->border = config->border; | ||
553 | view->border_thickness = config->border_thickness; | ||
554 | 625 | ||
555 | view_init_subsurfaces(view, wlr_surface); | 626 | view_init_subsurfaces(view, wlr_surface); |
556 | wl_signal_add(&wlr_surface->events.new_subsurface, | 627 | wl_signal_add(&wlr_surface->events.new_subsurface, |
@@ -561,11 +632,20 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface) { | |||
561 | view->container_reparent.notify = view_handle_container_reparent; | 632 | view->container_reparent.notify = view_handle_container_reparent; |
562 | 633 | ||
563 | if (view->impl->wants_floating && view->impl->wants_floating(view)) { | 634 | if (view->impl->wants_floating && view->impl->wants_floating(view)) { |
635 | view->border = config->floating_border; | ||
636 | view->border_thickness = config->floating_border_thickness; | ||
564 | container_set_floating(view->swayc, true); | 637 | container_set_floating(view->swayc, true); |
638 | } else { | ||
639 | view->border = config->border; | ||
640 | view->border_thickness = config->border_thickness; | ||
641 | view_set_tiled(view, true); | ||
565 | } | 642 | } |
566 | 643 | ||
567 | if (prev_focus == target_sibling) { | 644 | if (should_focus(view) && prev_focus == target_sibling) { |
568 | input_manager_set_focus(input_manager, cont); | 645 | input_manager_set_focus(input_manager, cont); |
646 | if (workspace) { | ||
647 | workspace_switch(workspace); | ||
648 | } | ||
569 | } | 649 | } |
570 | 650 | ||
571 | view_update_title(view, false); | 651 | view_update_title(view, false); |
@@ -581,16 +661,27 @@ void view_unmap(struct sway_view *view) { | |||
581 | wl_list_remove(&view->surface_new_subsurface.link); | 661 | wl_list_remove(&view->surface_new_subsurface.link); |
582 | wl_list_remove(&view->container_reparent.link); | 662 | wl_list_remove(&view->container_reparent.link); |
583 | 663 | ||
664 | if (view->urgent_timer) { | ||
665 | wl_event_source_remove(view->urgent_timer); | ||
666 | view->urgent_timer = NULL; | ||
667 | } | ||
668 | |||
669 | struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); | ||
670 | |||
671 | struct sway_container *parent; | ||
584 | if (view->is_fullscreen) { | 672 | if (view->is_fullscreen) { |
585 | struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); | ||
586 | ws->sway_workspace->fullscreen = NULL; | 673 | ws->sway_workspace->fullscreen = NULL; |
587 | container_destroy(view->swayc); | 674 | parent = container_destroy(view->swayc); |
588 | 675 | ||
589 | arrange_and_commit(ws->parent); | 676 | arrange_windows(ws->parent); |
590 | } else { | 677 | } else { |
591 | struct sway_container *parent = container_destroy(view->swayc); | 678 | parent = container_destroy(view->swayc); |
592 | arrange_and_commit(parent); | 679 | arrange_windows(parent); |
680 | } | ||
681 | if (parent->type >= C_WORKSPACE) { // if the workspace still exists | ||
682 | workspace_detect_urgent(ws); | ||
593 | } | 683 | } |
684 | transaction_commit_dirty(); | ||
594 | view->surface = NULL; | 685 | view->surface = NULL; |
595 | } | 686 | } |
596 | 687 | ||
@@ -601,6 +692,8 @@ void view_update_position(struct sway_view *view, double lx, double ly) { | |||
601 | container_damage_whole(view->swayc); | 692 | container_damage_whole(view->swayc); |
602 | view->x = lx; | 693 | view->x = lx; |
603 | view->y = ly; | 694 | view->y = ly; |
695 | view->swayc->current.view_x = lx; | ||
696 | view->swayc->current.view_y = ly; | ||
604 | if (container_is_floating(view->swayc)) { | 697 | if (container_is_floating(view->swayc)) { |
605 | container_set_geometry_from_floating_view(view->swayc); | 698 | container_set_geometry_from_floating_view(view->swayc); |
606 | } | 699 | } |
@@ -614,6 +707,8 @@ void view_update_size(struct sway_view *view, int width, int height) { | |||
614 | container_damage_whole(view->swayc); | 707 | container_damage_whole(view->swayc); |
615 | view->width = width; | 708 | view->width = width; |
616 | view->height = height; | 709 | view->height = height; |
710 | view->swayc->current.view_width = width; | ||
711 | view->swayc->current.view_height = height; | ||
617 | if (container_is_floating(view->swayc)) { | 712 | if (container_is_floating(view->swayc)) { |
618 | container_set_geometry_from_floating_view(view->swayc); | 713 | container_set_geometry_from_floating_view(view->swayc); |
619 | } | 714 | } |
@@ -624,7 +719,7 @@ static void view_subsurface_create(struct sway_view *view, | |||
624 | struct wlr_subsurface *subsurface) { | 719 | struct wlr_subsurface *subsurface) { |
625 | struct sway_view_child *child = calloc(1, sizeof(struct sway_view_child)); | 720 | struct sway_view_child *child = calloc(1, sizeof(struct sway_view_child)); |
626 | if (child == NULL) { | 721 | if (child == NULL) { |
627 | wlr_log(L_ERROR, "Allocation failed"); | 722 | wlr_log(WLR_ERROR, "Allocation failed"); |
628 | return; | 723 | return; |
629 | } | 724 | } |
630 | view_child_init(child, NULL, view, subsurface->surface); | 725 | view_child_init(child, NULL, view, subsurface->surface); |
@@ -744,8 +839,9 @@ struct sway_view *view_from_wlr_surface(struct wlr_surface *wlr_surface) { | |||
744 | return NULL; | 839 | return NULL; |
745 | } | 840 | } |
746 | 841 | ||
747 | wlr_log(L_DEBUG, "Surface of unknown type (role %s): %p", | 842 | const char *role = wlr_surface->role ? wlr_surface->role->name : NULL; |
748 | wlr_surface->role, wlr_surface); | 843 | wlr_log(WLR_DEBUG, "Surface of unknown type (role %s): %p", |
844 | role, wlr_surface); | ||
749 | return NULL; | 845 | return NULL; |
750 | } | 846 | } |
751 | 847 | ||
@@ -812,7 +908,7 @@ static char *escape_title(char *buffer) { | |||
812 | char *escaped_title = calloc(length + 1, sizeof(char)); | 908 | char *escaped_title = calloc(length + 1, sizeof(char)); |
813 | int result = escape_markup_text(buffer, escaped_title, length); | 909 | int result = escape_markup_text(buffer, escaped_title, length); |
814 | if (result != length) { | 910 | if (result != length) { |
815 | wlr_log(L_ERROR, "Could not escape title: %s", buffer); | 911 | wlr_log(WLR_ERROR, "Could not escape title: %s", buffer); |
816 | free(escaped_title); | 912 | free(escaped_title); |
817 | return buffer; | 913 | return buffer; |
818 | } | 914 | } |
@@ -946,7 +1042,7 @@ static void update_marks_texture(struct sway_view *view, | |||
946 | 1042 | ||
947 | double scale = output->sway_output->wlr_output->scale; | 1043 | double scale = output->sway_output->wlr_output->scale; |
948 | int width = 0; | 1044 | int width = 0; |
949 | int height = config->font_height * scale; | 1045 | int height = view->swayc->title_height * scale; |
950 | 1046 | ||
951 | cairo_t *c = cairo_create(NULL); | 1047 | cairo_t *c = cairo_create(NULL); |
952 | get_text_size(c, config->font, &width, NULL, scale, false, "%s", buffer); | 1048 | get_text_size(c, config->font, &width, NULL, scale, false, "%s", buffer); |
@@ -995,7 +1091,7 @@ void view_update_marks_textures(struct sway_view *view) { | |||
995 | } | 1091 | } |
996 | 1092 | ||
997 | bool view_is_visible(struct sway_view *view) { | 1093 | bool view_is_visible(struct sway_view *view) { |
998 | if (!view->swayc || view->swayc->destroying) { | 1094 | if (!view->swayc || view->swayc->destroying || !view->swayc->parent) { |
999 | return false; | 1095 | return false; |
1000 | } | 1096 | } |
1001 | struct sway_container *workspace = | 1097 | struct sway_container *workspace = |
@@ -1033,3 +1129,32 @@ bool view_is_visible(struct sway_view *view) { | |||
1033 | } | 1129 | } |
1034 | return true; | 1130 | return true; |
1035 | } | 1131 | } |
1132 | |||
1133 | void view_set_urgent(struct sway_view *view, bool enable) { | ||
1134 | if (view_is_urgent(view) == enable) { | ||
1135 | return; | ||
1136 | } | ||
1137 | if (enable) { | ||
1138 | struct sway_seat *seat = input_manager_current_seat(input_manager); | ||
1139 | if (seat_get_focus(seat) == view->swayc) { | ||
1140 | return; | ||
1141 | } | ||
1142 | clock_gettime(CLOCK_MONOTONIC, &view->urgent); | ||
1143 | } else { | ||
1144 | view->urgent = (struct timespec){ 0 }; | ||
1145 | if (view->urgent_timer) { | ||
1146 | wl_event_source_remove(view->urgent_timer); | ||
1147 | view->urgent_timer = NULL; | ||
1148 | } | ||
1149 | } | ||
1150 | container_damage_whole(view->swayc); | ||
1151 | |||
1152 | ipc_event_window(view->swayc, "urgent"); | ||
1153 | |||
1154 | struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); | ||
1155 | workspace_detect_urgent(ws); | ||
1156 | } | ||
1157 | |||
1158 | bool view_is_urgent(struct sway_view *view) { | ||
1159 | return view->urgent.tv_sec || view->urgent.tv_nsec; | ||
1160 | } | ||
diff --git a/sway/tree/workspace.c b/sway/tree/workspace.c index 651cc011..a93d9f44 100644 --- a/sway/tree/workspace.c +++ b/sway/tree/workspace.c | |||
@@ -12,6 +12,7 @@ | |||
12 | #include "sway/output.h" | 12 | #include "sway/output.h" |
13 | #include "sway/tree/arrange.h" | 13 | #include "sway/tree/arrange.h" |
14 | #include "sway/tree/container.h" | 14 | #include "sway/tree/container.h" |
15 | #include "sway/tree/view.h" | ||
15 | #include "sway/tree/workspace.h" | 16 | #include "sway/tree/workspace.h" |
16 | #include "list.h" | 17 | #include "list.h" |
17 | #include "log.h" | 18 | #include "log.h" |
@@ -50,7 +51,7 @@ struct sway_container *workspace_create(struct sway_container *output, | |||
50 | output = get_workspace_initial_output(name); | 51 | output = get_workspace_initial_output(name); |
51 | } | 52 | } |
52 | 53 | ||
53 | wlr_log(L_DEBUG, "Added workspace %s for output %s", name, output->name); | 54 | wlr_log(WLR_DEBUG, "Added workspace %s for output %s", name, output->name); |
54 | struct sway_container *workspace = container_create(C_WORKSPACE); | 55 | struct sway_container *workspace = container_create(C_WORKSPACE); |
55 | 56 | ||
56 | workspace->x = output->x; | 57 | workspace->x = output->x; |
@@ -108,9 +109,8 @@ static bool workspace_valid_on_output(const char *output_name, | |||
108 | } | 109 | } |
109 | 110 | ||
110 | char *workspace_next_name(const char *output_name) { | 111 | char *workspace_next_name(const char *output_name) { |
111 | wlr_log(L_DEBUG, "Workspace: Generating new workspace name for output %s", | 112 | wlr_log(WLR_DEBUG, "Workspace: Generating new workspace name for output %s", |
112 | output_name); | 113 | output_name); |
113 | int l = 1; | ||
114 | // Scan all workspace bindings to find the next available workspace name, | 114 | // Scan all workspace bindings to find the next available workspace name, |
115 | // if none are found/available then default to a number | 115 | // if none are found/available then default to a number |
116 | struct sway_mode *mode = config->current_mode; | 116 | struct sway_mode *mode = config->current_mode; |
@@ -137,7 +137,7 @@ char *workspace_next_name(const char *output_name) { | |||
137 | while (isspace(*_target)) { | 137 | while (isspace(*_target)) { |
138 | memmove(_target, _target+1, strlen(_target+1)); | 138 | memmove(_target, _target+1, strlen(_target+1)); |
139 | } | 139 | } |
140 | wlr_log(L_DEBUG, "Got valid workspace command for target: '%s'", | 140 | wlr_log(WLR_DEBUG, "Got valid workspace command for target: '%s'", |
141 | _target); | 141 | _target); |
142 | 142 | ||
143 | // Make sure that the command references an actual workspace | 143 | // Make sure that the command references an actual workspace |
@@ -163,7 +163,7 @@ char *workspace_next_name(const char *output_name) { | |||
163 | temp[length - 1] = '\0'; | 163 | temp[length - 1] = '\0'; |
164 | free(_target); | 164 | free(_target); |
165 | _target = temp; | 165 | _target = temp; |
166 | wlr_log(L_DEBUG, "Isolated name from workspace number: '%s'", _target); | 166 | wlr_log(WLR_DEBUG, "Isolated name from workspace number: '%s'", _target); |
167 | 167 | ||
168 | // Make sure the workspace number doesn't already exist | 168 | // Make sure the workspace number doesn't already exist |
169 | if (workspace_by_number(_target)) { | 169 | if (workspace_by_number(_target)) { |
@@ -192,7 +192,9 @@ char *workspace_next_name(const char *output_name) { | |||
192 | order = binding->order; | 192 | order = binding->order; |
193 | free(target); | 193 | free(target); |
194 | target = _target; | 194 | target = _target; |
195 | wlr_log(L_DEBUG, "Workspace: Found free name %s", _target); | 195 | wlr_log(WLR_DEBUG, "Workspace: Found free name %s", _target); |
196 | } else { | ||
197 | free(_target); | ||
196 | } | 198 | } |
197 | } | 199 | } |
198 | free(dup); | 200 | free(dup); |
@@ -203,14 +205,9 @@ char *workspace_next_name(const char *output_name) { | |||
203 | // As a fall back, get the current number of active workspaces | 205 | // As a fall back, get the current number of active workspaces |
204 | // and return that + 1 for the next workspace's name | 206 | // and return that + 1 for the next workspace's name |
205 | int ws_num = root_container.children->length; | 207 | int ws_num = root_container.children->length; |
206 | if (ws_num >= 10) { | 208 | int l = snprintf(NULL, 0, "%d", ws_num); |
207 | l = 2; | ||
208 | } else if (ws_num >= 100) { | ||
209 | l = 3; | ||
210 | } | ||
211 | char *name = malloc(l + 1); | 209 | char *name = malloc(l + 1); |
212 | if (!name) { | 210 | if (!sway_assert(name, "Cloud not allocate workspace name")) { |
213 | wlr_log(L_ERROR, "Could not allocate workspace name"); | ||
214 | return NULL; | 211 | return NULL; |
215 | } | 212 | } |
216 | sprintf(name, "%d", ws_num++); | 213 | sprintf(name, "%d", ws_num++); |
@@ -272,6 +269,9 @@ struct sway_container *workspace_by_name(const char *name) { | |||
272 | */ | 269 | */ |
273 | struct sway_container *workspace_output_prev_next_impl( | 270 | struct sway_container *workspace_output_prev_next_impl( |
274 | struct sway_container *output, bool next) { | 271 | struct sway_container *output, bool next) { |
272 | if (!output) { | ||
273 | return NULL; | ||
274 | } | ||
275 | if (!sway_assert(output->type == C_OUTPUT, | 275 | if (!sway_assert(output->type == C_OUTPUT, |
276 | "Argument must be an output, is %d", output->type)) { | 276 | "Argument must be an output, is %d", output->type)) { |
277 | return NULL; | 277 | return NULL; |
@@ -304,6 +304,9 @@ struct sway_container *workspace_output_prev_next_impl( | |||
304 | */ | 304 | */ |
305 | struct sway_container *workspace_prev_next_impl( | 305 | struct sway_container *workspace_prev_next_impl( |
306 | struct sway_container *workspace, bool next) { | 306 | struct sway_container *workspace, bool next) { |
307 | if (!workspace) { | ||
308 | return NULL; | ||
309 | } | ||
307 | if (!sway_assert(workspace->type == C_WORKSPACE, | 310 | if (!sway_assert(workspace->type == C_WORKSPACE, |
308 | "Argument must be a workspace, is %d", workspace->type)) { | 311 | "Argument must be a workspace, is %d", workspace->type)) { |
309 | return NULL; | 312 | return NULL; |
@@ -386,7 +389,7 @@ bool workspace_switch(struct sway_container *workspace) { | |||
386 | free(prev_workspace_name); | 389 | free(prev_workspace_name); |
387 | prev_workspace_name = malloc(strlen(active_ws->name) + 1); | 390 | prev_workspace_name = malloc(strlen(active_ws->name) + 1); |
388 | if (!prev_workspace_name) { | 391 | if (!prev_workspace_name) { |
389 | wlr_log(L_ERROR, "Unable to allocate previous workspace name"); | 392 | wlr_log(WLR_ERROR, "Unable to allocate previous workspace name"); |
390 | return false; | 393 | return false; |
391 | } | 394 | } |
392 | strcpy(prev_workspace_name, active_ws->name); | 395 | strcpy(prev_workspace_name, active_ws->name); |
@@ -408,7 +411,7 @@ bool workspace_switch(struct sway_container *workspace) { | |||
408 | } | 411 | } |
409 | } | 412 | } |
410 | 413 | ||
411 | wlr_log(L_DEBUG, "Switching to workspace %p:%s", | 414 | wlr_log(WLR_DEBUG, "Switching to workspace %p:%s", |
412 | workspace, workspace->name); | 415 | workspace, workspace->name); |
413 | struct sway_container *next = seat_get_focus_inactive(seat, workspace); | 416 | struct sway_container *next = seat_get_focus_inactive(seat, workspace); |
414 | if (next == NULL) { | 417 | if (next == NULL) { |
@@ -426,7 +429,7 @@ bool workspace_switch(struct sway_container *workspace) { | |||
426 | } | 429 | } |
427 | seat_set_focus(seat, next); | 430 | seat_set_focus(seat, next); |
428 | struct sway_container *output = container_parent(workspace, C_OUTPUT); | 431 | struct sway_container *output = container_parent(workspace, C_OUTPUT); |
429 | arrange_and_commit(output); | 432 | arrange_windows(output); |
430 | return true; | 433 | return true; |
431 | } | 434 | } |
432 | 435 | ||
@@ -518,6 +521,16 @@ struct sway_container *workspace_output_get_highest_available( | |||
518 | return NULL; | 521 | return NULL; |
519 | } | 522 | } |
520 | 523 | ||
524 | void workspace_detect_urgent(struct sway_container *workspace) { | ||
525 | bool new_urgent = container_has_urgent_child(workspace); | ||
526 | |||
527 | if (workspace->sway_workspace->urgent != new_urgent) { | ||
528 | workspace->sway_workspace->urgent = new_urgent; | ||
529 | ipc_event_workspace(NULL, workspace, "urgent"); | ||
530 | container_damage_whole(workspace); | ||
531 | } | ||
532 | } | ||
533 | |||
521 | struct pid_workspace { | 534 | struct pid_workspace { |
522 | pid_t pid; | 535 | pid_t pid; |
523 | char *workspace; | 536 | char *workspace; |
@@ -540,14 +553,14 @@ struct sway_container *workspace_for_pid(pid_t pid) { | |||
540 | struct sway_container *ws = NULL; | 553 | struct sway_container *ws = NULL; |
541 | struct pid_workspace *pw = NULL; | 554 | struct pid_workspace *pw = NULL; |
542 | 555 | ||
543 | wlr_log(L_DEBUG, "Looking up workspace for pid %d", pid); | 556 | wlr_log(WLR_DEBUG, "Looking up workspace for pid %d", pid); |
544 | 557 | ||
545 | do { | 558 | do { |
546 | struct pid_workspace *_pw = NULL; | 559 | struct pid_workspace *_pw = NULL; |
547 | wl_list_for_each(_pw, &pid_workspaces, link) { | 560 | wl_list_for_each(_pw, &pid_workspaces, link) { |
548 | if (pid == _pw->pid) { | 561 | if (pid == _pw->pid) { |
549 | pw = _pw; | 562 | pw = _pw; |
550 | wlr_log(L_DEBUG, | 563 | wlr_log(WLR_DEBUG, |
551 | "found pid_workspace for pid %d, workspace %s", | 564 | "found pid_workspace for pid %d, workspace %s", |
552 | pid, pw->workspace); | 565 | pid, pw->workspace); |
553 | goto found; | 566 | goto found; |
@@ -561,7 +574,7 @@ found: | |||
561 | ws = workspace_by_name(pw->workspace); | 574 | ws = workspace_by_name(pw->workspace); |
562 | 575 | ||
563 | if (!ws) { | 576 | if (!ws) { |
564 | wlr_log(L_DEBUG, | 577 | wlr_log(WLR_DEBUG, |
565 | "Creating workspace %s for pid %d because it disappeared", | 578 | "Creating workspace %s for pid %d because it disappeared", |
566 | pw->workspace, pid); | 579 | pw->workspace, pid); |
567 | ws = workspace_create(pw->output, pw->workspace); | 580 | ws = workspace_create(pw->output, pw->workspace); |
@@ -582,7 +595,7 @@ static void pw_handle_output_destroy(struct wl_listener *listener, void *data) { | |||
582 | } | 595 | } |
583 | 596 | ||
584 | void workspace_record_pid(pid_t pid) { | 597 | void workspace_record_pid(pid_t pid) { |
585 | wlr_log(L_DEBUG, "Recording workspace for process %d", pid); | 598 | wlr_log(WLR_DEBUG, "Recording workspace for process %d", pid); |
586 | if (!pid_workspaces.prev && !pid_workspaces.next) { | 599 | if (!pid_workspaces.prev && !pid_workspaces.next) { |
587 | wl_list_init(&pid_workspaces); | 600 | wl_list_init(&pid_workspaces); |
588 | } | 601 | } |
@@ -594,12 +607,12 @@ void workspace_record_pid(pid_t pid) { | |||
594 | ws = container_parent(ws, C_WORKSPACE); | 607 | ws = container_parent(ws, C_WORKSPACE); |
595 | } | 608 | } |
596 | if (!ws) { | 609 | if (!ws) { |
597 | wlr_log(L_DEBUG, "Bailing out, no workspace"); | 610 | wlr_log(WLR_DEBUG, "Bailing out, no workspace"); |
598 | return; | 611 | return; |
599 | } | 612 | } |
600 | struct sway_container *output = ws->parent; | 613 | struct sway_container *output = ws->parent; |
601 | if (!output) { | 614 | if (!output) { |
602 | wlr_log(L_DEBUG, "Bailing out, no output"); | 615 | wlr_log(WLR_DEBUG, "Bailing out, no output"); |
603 | return; | 616 | return; |
604 | } | 617 | } |
605 | 618 | ||