aboutsummaryrefslogtreecommitdiffstats
path: root/sway
diff options
context:
space:
mode:
Diffstat (limited to 'sway')
-rw-r--r--sway/commands.c53
-rw-r--r--sway/commands/assign.c3
-rw-r--r--sway/commands/bar.c5
-rw-r--r--sway/commands/bar/bind.c2
-rw-r--r--sway/commands/bar/font.c1
-rw-r--r--sway/commands/bar/hidden_state.c3
-rw-r--r--sway/commands/bar/icon_theme.c1
-rw-r--r--sway/commands/bar/id.c1
-rw-r--r--sway/commands/bar/mode.c3
-rw-r--r--sway/commands/bar/output.c1
-rw-r--r--sway/commands/bar/position.c1
-rw-r--r--sway/commands/bar/separator_symbol.c1
-rw-r--r--sway/commands/bar/tray_bind.c2
-rw-r--r--sway/commands/bar/tray_output.c1
-rw-r--r--sway/commands/bind.c25
-rw-r--r--sway/commands/client.c28
-rw-r--r--sway/commands/exec_always.c30
-rw-r--r--sway/commands/floating_minmax_size.c6
-rw-r--r--sway/commands/focus.c26
-rw-r--r--sway/commands/font.c30
-rw-r--r--sway/commands/for_window.c2
-rw-r--r--sway/commands/gesture.c165
-rw-r--r--sway/commands/hide_edge_borders.c4
-rw-r--r--sway/commands/inhibit_idle.c2
-rw-r--r--sway/commands/input.c3
-rw-r--r--sway/commands/input/calibration_matrix.c1
-rw-r--r--sway/commands/input/dwtp.c25
-rw-r--r--sway/commands/input/events.c10
-rw-r--r--sway/commands/input/map_from_region.c19
-rw-r--r--sway/commands/input/map_to_output.c1
-rw-r--r--sway/commands/input/map_to_region.c3
-rw-r--r--sway/commands/input/rotation_angle.c29
-rw-r--r--sway/commands/input/scroll_button.c2
-rw-r--r--sway/commands/input/scroll_button_lock.c26
-rw-r--r--sway/commands/input/xkb_file.c1
-rw-r--r--sway/commands/input/xkb_layout.c1
-rw-r--r--sway/commands/input/xkb_model.c1
-rw-r--r--sway/commands/input/xkb_numlock.c1
-rw-r--r--sway/commands/input/xkb_options.c1
-rw-r--r--sway/commands/input/xkb_rules.c1
-rw-r--r--sway/commands/input/xkb_switch_layout.c37
-rw-r--r--sway/commands/input/xkb_variant.c1
-rw-r--r--sway/commands/layout.c2
-rw-r--r--sway/commands/mark.c3
-rw-r--r--sway/commands/mode.c4
-rw-r--r--sway/commands/move.c67
-rw-r--r--sway/commands/no_focus.c2
-rw-r--r--sway/commands/opacity.c3
-rw-r--r--sway/commands/output.c19
-rw-r--r--sway/commands/output/background.c16
-rw-r--r--sway/commands/output/dpms.c45
-rw-r--r--sway/commands/output/mode.c58
-rw-r--r--sway/commands/output/power.c43
-rw-r--r--sway/commands/output/render_bit_depth.c29
-rw-r--r--sway/commands/output/toggle.c2
-rw-r--r--sway/commands/output/transform.c1
-rw-r--r--sway/commands/output/unplug.c54
-rw-r--r--sway/commands/primary_selection.c25
-rw-r--r--sway/commands/reload.c9
-rw-r--r--sway/commands/rename.c7
-rw-r--r--sway/commands/resize.c50
-rw-r--r--sway/commands/seat/attach.c1
-rw-r--r--sway/commands/seat/cursor.c29
-rw-r--r--sway/commands/seat/hide_cursor.c1
-rw-r--r--sway/commands/seat/idle.c7
-rw-r--r--sway/commands/seat/xcursor_theme.c1
-rw-r--r--sway/commands/set.c1
-rw-r--r--sway/commands/show_marks.c12
-rw-r--r--sway/commands/smart_gaps.c7
-rw-r--r--sway/commands/split.c21
-rw-r--r--sway/commands/swap.c187
-rw-r--r--sway/commands/title_align.c9
-rw-r--r--sway/commands/title_format.c2
-rw-r--r--sway/commands/titlebar_border_thickness.c1
-rw-r--r--sway/commands/titlebar_padding.c1
-rw-r--r--sway/commands/unmark.c12
-rw-r--r--sway/commands/workspace.c13
-rw-r--r--sway/commands/xwayland.c4
-rw-r--r--sway/config.c163
-rw-r--r--sway/config/bar.c4
-rw-r--r--sway/config/input.c13
-rw-r--r--sway/config/output.c937
-rw-r--r--sway/config/seat.c2
-rw-r--r--sway/criteria.c117
-rw-r--r--sway/desktop/desktop.c39
-rw-r--r--sway/desktop/idle_inhibit_v1.c54
-rw-r--r--sway/desktop/launcher.c267
-rw-r--r--sway/desktop/layer_shell.c785
-rw-r--r--sway/desktop/output.c1012
-rw-r--r--sway/desktop/render.c1160
-rw-r--r--sway/desktop/surface.c46
-rw-r--r--sway/desktop/transaction.c504
-rw-r--r--sway/desktop/xdg_shell.c388
-rw-r--r--sway/desktop/xwayland.c297
-rw-r--r--sway/input/cursor.c587
-rw-r--r--sway/input/input-manager.c149
-rw-r--r--sway/input/keyboard.c190
-rw-r--r--sway/input/libinput.c105
-rw-r--r--sway/input/seat.c449
-rw-r--r--sway/input/seatop_default.c543
-rw-r--r--sway/input/seatop_down.c181
-rw-r--r--sway/input/seatop_move_floating.c6
-rw-r--r--sway/input/seatop_move_tiling.c75
-rw-r--r--sway/input/seatop_resize_floating.c21
-rw-r--r--sway/input/seatop_resize_tiling.c3
-rw-r--r--sway/input/switch.c34
-rw-r--r--sway/input/tablet.c50
-rw-r--r--sway/input/text_input.c229
-rw-r--r--sway/ipc-json.c255
-rw-r--r--sway/ipc-server.c36
-rw-r--r--sway/lock.c354
-rw-r--r--sway/main.c186
-rw-r--r--sway/meson.build33
-rw-r--r--sway/realtime.c40
-rw-r--r--sway/scene_descriptor.c66
-rw-r--r--sway/server.c326
-rw-r--r--sway/sway-bar.5.scd2
-rw-r--r--sway/sway-input.5.scd44
-rw-r--r--sway/sway-ipc.7.scd52
-rw-r--r--sway/sway-output.5.scd51
-rw-r--r--sway/sway.5.scd157
-rw-r--r--sway/sway_text_node.c308
-rw-r--r--sway/swaynag.c17
-rw-r--r--sway/tree/arrange.c40
-rw-r--r--sway/tree/container.c1301
-rw-r--r--sway/tree/node.c38
-rw-r--r--sway/tree/output.c117
-rw-r--r--sway/tree/root.c281
-rw-r--r--sway/tree/view.c630
-rw-r--r--sway/tree/workspace.c98
-rw-r--r--sway/xdg_activation_v1.c57
-rw-r--r--sway/xdg_decoration.c41
132 files changed, 8109 insertions, 6138 deletions
diff --git a/sway/commands.c b/sway/commands.c
index b09a04c7..8d003dfa 100644
--- a/sway/commands.c
+++ b/sway/commands.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809
2#include <ctype.h> 1#include <ctype.h>
3#include <stdarg.h> 2#include <stdarg.h>
4#include <stdlib.h> 3#include <stdlib.h>
@@ -46,11 +45,13 @@ static const struct cmd_handler handlers[] = {
46 { "assign", cmd_assign }, 45 { "assign", cmd_assign },
47 { "bar", cmd_bar }, 46 { "bar", cmd_bar },
48 { "bindcode", cmd_bindcode }, 47 { "bindcode", cmd_bindcode },
48 { "bindgesture", cmd_bindgesture },
49 { "bindswitch", cmd_bindswitch }, 49 { "bindswitch", cmd_bindswitch },
50 { "bindsym", cmd_bindsym }, 50 { "bindsym", cmd_bindsym },
51 { "client.background", cmd_client_noop }, 51 { "client.background", cmd_client_noop },
52 { "client.focused", cmd_client_focused }, 52 { "client.focused", cmd_client_focused },
53 { "client.focused_inactive", cmd_client_focused_inactive }, 53 { "client.focused_inactive", cmd_client_focused_inactive },
54 { "client.focused_tab_title", cmd_client_focused_tab_title },
54 { "client.placeholder", cmd_client_noop }, 55 { "client.placeholder", cmd_client_noop },
55 { "client.unfocused", cmd_client_unfocused }, 56 { "client.unfocused", cmd_client_unfocused },
56 { "client.urgent", cmd_client_urgent }, 57 { "client.urgent", cmd_client_urgent },
@@ -91,6 +92,7 @@ static const struct cmd_handler handlers[] = {
91 { "titlebar_border_thickness", cmd_titlebar_border_thickness }, 92 { "titlebar_border_thickness", cmd_titlebar_border_thickness },
92 { "titlebar_padding", cmd_titlebar_padding }, 93 { "titlebar_padding", cmd_titlebar_padding },
93 { "unbindcode", cmd_unbindcode }, 94 { "unbindcode", cmd_unbindcode },
95 { "unbindgesture", cmd_unbindgesture },
94 { "unbindswitch", cmd_unbindswitch }, 96 { "unbindswitch", cmd_unbindswitch },
95 { "unbindsym", cmd_unbindsym }, 97 { "unbindsym", cmd_unbindsym },
96 { "workspace", cmd_workspace }, 98 { "workspace", cmd_workspace },
@@ -101,6 +103,7 @@ static const struct cmd_handler handlers[] = {
101static const struct cmd_handler config_handlers[] = { 103static const struct cmd_handler config_handlers[] = {
102 { "default_orientation", cmd_default_orientation }, 104 { "default_orientation", cmd_default_orientation },
103 { "include", cmd_include }, 105 { "include", cmd_include },
106 { "primary_selection", cmd_primary_selection },
104 { "swaybg_command", cmd_swaybg_command }, 107 { "swaybg_command", cmd_swaybg_command },
105 { "swaynag_command", cmd_swaynag_command }, 108 { "swaynag_command", cmd_swaynag_command },
106 { "workspace_layout", cmd_workspace_layout }, 109 { "workspace_layout", cmd_workspace_layout },
@@ -144,7 +147,7 @@ static int handler_compare(const void *_a, const void *_b) {
144 return strcasecmp(a->command, b->command); 147 return strcasecmp(a->command, b->command);
145} 148}
146 149
147const struct cmd_handler *find_handler(char *line, 150const struct cmd_handler *find_handler(const char *line,
148 const struct cmd_handler *handlers, size_t handlers_size) { 151 const struct cmd_handler *handlers, size_t handlers_size) {
149 if (!handlers || !handlers_size) { 152 if (!handlers || !handlers_size) {
150 return NULL; 153 return NULL;
@@ -377,10 +380,13 @@ struct cmd_results *config_command(char *exec, char **new_block) {
377 sway_log(SWAY_INFO, "Config command: %s", exec); 380 sway_log(SWAY_INFO, "Config command: %s", exec);
378 const struct cmd_handler *handler = find_core_handler(argv[0]); 381 const struct cmd_handler *handler = find_core_handler(argv[0]);
379 if (!handler || !handler->handle) { 382 if (!handler || !handler->handle) {
380 const char *error = handler 383 if (handler) {
381 ? "Command '%s' is shimmed, but unimplemented" 384 results = cmd_results_new(CMD_INVALID,
382 : "Unknown/invalid command '%s'"; 385 "Command '%s' is shimmed, but unimplemented", argv[0]);
383 results = cmd_results_new(CMD_INVALID, error, argv[0]); 386 } else {
387 results = cmd_results_new(CMD_INVALID,
388 "Unknown/invalid command '%s'", argv[0]);
389 }
384 goto cleanup; 390 goto cleanup;
385 } 391 }
386 392
@@ -406,6 +412,7 @@ struct cmd_results *config_command(char *exec, char **new_block) {
406 && handler->handle != cmd_bindsym 412 && handler->handle != cmd_bindsym
407 && handler->handle != cmd_bindcode 413 && handler->handle != cmd_bindcode
408 && handler->handle != cmd_bindswitch 414 && handler->handle != cmd_bindswitch
415 && handler->handle != cmd_bindgesture
409 && handler->handle != cmd_set 416 && handler->handle != cmd_set
410 && handler->handle != cmd_for_window 417 && handler->handle != cmd_for_window
411 && (*argv[i] == '\"' || *argv[i] == '\'')) { 418 && (*argv[i] == '\"' || *argv[i] == '\'')) {
@@ -465,34 +472,6 @@ struct cmd_results *config_commands_command(char *exec) {
465 goto cleanup; 472 goto cleanup;
466 } 473 }
467 474
468 enum command_context context = 0;
469
470 struct {
471 char *name;
472 enum command_context context;
473 } context_names[] = {
474 { "config", CONTEXT_CONFIG },
475 { "binding", CONTEXT_BINDING },
476 { "ipc", CONTEXT_IPC },
477 { "criteria", CONTEXT_CRITERIA },
478 { "all", CONTEXT_ALL },
479 };
480
481 for (int i = 1; i < argc; ++i) {
482 size_t j;
483 for (j = 0; j < sizeof(context_names) / sizeof(context_names[0]); ++j) {
484 if (strcmp(context_names[j].name, argv[i]) == 0) {
485 break;
486 }
487 }
488 if (j == sizeof(context_names) / sizeof(context_names[0])) {
489 results = cmd_results_new(CMD_INVALID,
490 "Invalid command context %s", argv[i]);
491 goto cleanup;
492 }
493 context |= context_names[j].context;
494 }
495
496 results = cmd_results_new(CMD_SUCCESS, NULL); 475 results = cmd_results_new(CMD_SUCCESS, NULL);
497 476
498cleanup: 477cleanup:
@@ -509,14 +488,10 @@ struct cmd_results *cmd_results_new(enum cmd_status status,
509 } 488 }
510 results->status = status; 489 results->status = status;
511 if (format) { 490 if (format) {
512 char *error = malloc(256);
513 va_list args; 491 va_list args;
514 va_start(args, format); 492 va_start(args, format);
515 if (error) { 493 results->error = vformat_str(format, args);
516 vsnprintf(error, 256, format, args);
517 }
518 va_end(args); 494 va_end(args);
519 results->error = error;
520 } else { 495 } else {
521 results->error = NULL; 496 results->error = NULL;
522 } 497 }
diff --git a/sway/commands/assign.c b/sway/commands/assign.c
index 976bc3cc..bf95cf00 100644
--- a/sway/commands/assign.c
+++ b/sway/commands/assign.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <stdio.h> 1#include <stdio.h>
3#include <string.h> 2#include <string.h>
4#include "sway/commands.h" 3#include "sway/commands.h"
@@ -17,7 +16,7 @@ struct cmd_results *cmd_assign(int argc, char **argv) {
17 char *err_str = NULL; 16 char *err_str = NULL;
18 struct criteria *criteria = criteria_parse(argv[0], &err_str); 17 struct criteria *criteria = criteria_parse(argv[0], &err_str);
19 if (!criteria) { 18 if (!criteria) {
20 error = cmd_results_new(CMD_INVALID, err_str); 19 error = cmd_results_new(CMD_INVALID, "%s", err_str);
21 free(err_str); 20 free(err_str);
22 return error; 21 return error;
23 } 22 }
diff --git a/sway/commands/bar.c b/sway/commands/bar.c
index 8571d282..635e895b 100644
--- a/sway/commands/bar.c
+++ b/sway/commands/bar.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809
2#include <stdio.h> 1#include <stdio.h>
3#include <string.h> 2#include <string.h>
4#include <strings.h> 3#include <strings.h>
@@ -73,12 +72,10 @@ struct cmd_results *cmd_bar(int argc, char **argv) {
73 } 72 }
74 ++argv; --argc; 73 ++argv; --argc;
75 } else if (config->reading && !config->current_bar) { 74 } else if (config->reading && !config->current_bar) {
76 int len = snprintf(NULL, 0, "bar-%d", config->bars->length) + 1; 75 id = format_str("bar-%d", config->bars->length);
77 id = malloc(len * sizeof(char));
78 if (!id) { 76 if (!id) {
79 return cmd_results_new(CMD_FAILURE, "Unable to allocate bar id"); 77 return cmd_results_new(CMD_FAILURE, "Unable to allocate bar id");
80 } 78 }
81 snprintf(id, len, "bar-%d", config->bars->length);
82 } else if (!config->reading && strcmp(argv[0], "mode") != 0 && 79 } else if (!config->reading && strcmp(argv[0], "mode") != 0 &&
83 strcmp(argv[0], "hidden_state") != 0) { 80 strcmp(argv[0], "hidden_state") != 0) {
84 if (is_subcommand(argv[0])) { 81 if (is_subcommand(argv[0])) {
diff --git a/sway/commands/bar/bind.c b/sway/commands/bar/bind.c
index b4b5bc45..8a837e3f 100644
--- a/sway/commands/bar/bind.c
+++ b/sway/commands/bar/bind.c
@@ -96,7 +96,7 @@ static struct cmd_results *bar_cmd_bind(int argc, char **argv, bool code,
96 } 96 }
97 if (message) { 97 if (message) {
98 free_bar_binding(binding); 98 free_bar_binding(binding);
99 error = cmd_results_new(CMD_INVALID, message); 99 error = cmd_results_new(CMD_INVALID, "%s", message);
100 free(message); 100 free(message);
101 return error; 101 return error;
102 } else if (!binding->button) { 102 } else if (!binding->button) {
diff --git a/sway/commands/bar/font.c b/sway/commands/bar/font.c
index 891c87af..0c074679 100644
--- a/sway/commands/bar/font.c
+++ b/sway/commands/bar/font.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <string.h> 1#include <string.h>
3#include "sway/commands.h" 2#include "sway/commands.h"
4#include "log.h" 3#include "log.h"
diff --git a/sway/commands/bar/hidden_state.c b/sway/commands/bar/hidden_state.c
index 1f08a5d2..7b38831e 100644
--- a/sway/commands/bar/hidden_state.c
+++ b/sway/commands/bar/hidden_state.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <string.h> 1#include <string.h>
3#include <strings.h> 2#include <strings.h>
4#include "sway/commands.h" 3#include "sway/commands.h"
@@ -54,7 +53,7 @@ struct cmd_results *bar_cmd_hidden_state(int argc, char **argv) {
54 } 53 }
55 54
56 const char *state = argv[0]; 55 const char *state = argv[0];
57 if (config->reading) { 56 if (config->current_bar) {
58 error = bar_set_hidden_state(config->current_bar, state); 57 error = bar_set_hidden_state(config->current_bar, state);
59 } else { 58 } else {
60 const char *id = argc == 2 ? argv[1] : NULL; 59 const char *id = argc == 2 ? argv[1] : NULL;
diff --git a/sway/commands/bar/icon_theme.c b/sway/commands/bar/icon_theme.c
index 6ac07843..fee21709 100644
--- a/sway/commands/bar/icon_theme.c
+++ b/sway/commands/bar/icon_theme.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <string.h> 1#include <string.h>
3#include "config.h" 2#include "config.h"
4#include "sway/commands.h" 3#include "sway/commands.h"
diff --git a/sway/commands/bar/id.c b/sway/commands/bar/id.c
index a9a61743..46cf4ca9 100644
--- a/sway/commands/bar/id.c
+++ b/sway/commands/bar/id.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <string.h> 1#include <string.h>
3#include "sway/commands.h" 2#include "sway/commands.h"
4#include "log.h" 3#include "log.h"
diff --git a/sway/commands/bar/mode.c b/sway/commands/bar/mode.c
index 8b3fb275..d69e910b 100644
--- a/sway/commands/bar/mode.c
+++ b/sway/commands/bar/mode.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <string.h> 1#include <string.h>
3#include <strings.h> 2#include <strings.h>
4#include "sway/commands.h" 3#include "sway/commands.h"
@@ -58,7 +57,7 @@ struct cmd_results *bar_cmd_mode(int argc, char **argv) {
58 } 57 }
59 58
60 const char *mode = argv[0]; 59 const char *mode = argv[0];
61 if (config->reading) { 60 if (config->current_bar) {
62 error = bar_set_mode(config->current_bar, mode); 61 error = bar_set_mode(config->current_bar, mode);
63 } else { 62 } else {
64 const char *id = argc == 2 ? argv[1] : NULL; 63 const char *id = argc == 2 ? argv[1] : NULL;
diff --git a/sway/commands/bar/output.c b/sway/commands/bar/output.c
index cac1d056..51730176 100644
--- a/sway/commands/bar/output.c
+++ b/sway/commands/bar/output.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <stdbool.h> 1#include <stdbool.h>
3#include <string.h> 2#include <string.h>
4#include "sway/commands.h" 3#include "sway/commands.h"
diff --git a/sway/commands/bar/position.c b/sway/commands/bar/position.c
index b207de0b..94f530ec 100644
--- a/sway/commands/bar/position.c
+++ b/sway/commands/bar/position.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <string.h> 1#include <string.h>
3#include <strings.h> 2#include <strings.h>
4#include "sway/commands.h" 3#include "sway/commands.h"
diff --git a/sway/commands/bar/separator_symbol.c b/sway/commands/bar/separator_symbol.c
index 6737d4d2..50e9a873 100644
--- a/sway/commands/bar/separator_symbol.c
+++ b/sway/commands/bar/separator_symbol.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <string.h> 1#include <string.h>
3#include "sway/commands.h" 2#include "sway/commands.h"
4#include "log.h" 3#include "log.h"
diff --git a/sway/commands/bar/tray_bind.c b/sway/commands/bar/tray_bind.c
index 243834ba..3dc9bc4c 100644
--- a/sway/commands/bar/tray_bind.c
+++ b/sway/commands/bar/tray_bind.c
@@ -26,7 +26,7 @@ static struct cmd_results *tray_bind(int argc, char **argv, bool code) {
26 } 26 }
27 if (message) { 27 if (message) {
28 free(binding); 28 free(binding);
29 error = cmd_results_new(CMD_INVALID, message); 29 error = cmd_results_new(CMD_INVALID, "%s", message);
30 free(message); 30 free(message);
31 return error; 31 return error;
32 } else if (!binding->button) { 32 } else if (!binding->button) {
diff --git a/sway/commands/bar/tray_output.c b/sway/commands/bar/tray_output.c
index eb3b486e..679facf7 100644
--- a/sway/commands/bar/tray_output.c
+++ b/sway/commands/bar/tray_output.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <string.h> 1#include <string.h>
3#include "config.h" 2#include "config.h"
4#include "sway/commands.h" 3#include "sway/commands.h"
diff --git a/sway/commands/bind.c b/sway/commands/bind.c
index 4c67b3ce..268f2855 100644
--- a/sway/commands/bind.c
+++ b/sway/commands/bind.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <libevdev/libevdev.h> 1#include <libevdev/libevdev.h>
3#include <linux/input-event-codes.h> 2#include <linux/input-event-codes.h>
4#include <string.h> 3#include <string.h>
@@ -47,7 +46,7 @@ static bool binding_switch_compare(struct sway_switch_binding *binding_a,
47 if (binding_a->type != binding_b->type) { 46 if (binding_a->type != binding_b->type) {
48 return false; 47 return false;
49 } 48 }
50 if (binding_a->state != binding_b->state) { 49 if (binding_a->trigger != binding_b->trigger) {
51 return false; 50 return false;
52 } 51 }
53 if ((binding_a->flags & BINDING_LOCKED) != 52 if ((binding_a->flags & BINDING_LOCKED) !=
@@ -127,7 +126,7 @@ static struct cmd_results *identify_key(const char* name, bool first_key,
127 if (!button) { 126 if (!button) {
128 if (message) { 127 if (message) {
129 struct cmd_results *error = 128 struct cmd_results *error =
130 cmd_results_new(CMD_INVALID, message); 129 cmd_results_new(CMD_INVALID, "%s", message);
131 free(message); 130 free(message);
132 return error; 131 return error;
133 } else { 132 } else {
@@ -143,7 +142,7 @@ static struct cmd_results *identify_key(const char* name, bool first_key,
143 if (!button) { 142 if (!button) {
144 if (message) { 143 if (message) {
145 struct cmd_results *error = 144 struct cmd_results *error =
146 cmd_results_new(CMD_INVALID, message); 145 cmd_results_new(CMD_INVALID, "%s", message);
147 free(message); 146 free(message);
148 return error; 147 return error;
149 } else { 148 } else {
@@ -182,7 +181,7 @@ static struct cmd_results *identify_key(const char* name, bool first_key,
182 uint32_t button = get_mouse_bindsym(name, &message); 181 uint32_t button = get_mouse_bindsym(name, &message);
183 if (message) { 182 if (message) {
184 struct cmd_results *error = 183 struct cmd_results *error =
185 cmd_results_new(CMD_INVALID, message); 184 cmd_results_new(CMD_INVALID, "%s", message);
186 free(message); 185 free(message);
187 return error; 186 return error;
188 } else if (button) { 187 } else if (button) {
@@ -372,6 +371,7 @@ static struct cmd_results *cmd_bindsym_or_bindcode(int argc, char **argv,
372 strlen("--input-device=")) == 0) { 371 strlen("--input-device=")) == 0) {
373 free(binding->input); 372 free(binding->input);
374 binding->input = strdup(argv[0] + strlen("--input-device=")); 373 binding->input = strdup(argv[0] + strlen("--input-device="));
374 strip_quotes(binding->input);
375 } else if (strcmp("--no-warn", argv[0]) == 0) { 375 } else if (strcmp("--no-warn", argv[0]) == 0) {
376 warn = false; 376 warn = false;
377 } else if (strcmp("--no-repeat", argv[0]) == 0) { 377 } else if (strcmp("--no-repeat", argv[0]) == 0) {
@@ -538,7 +538,7 @@ struct cmd_results *cmd_bind_or_unbind_switch(int argc, char **argv,
538 free_switch_binding(binding); 538 free_switch_binding(binding);
539 return cmd_results_new(CMD_FAILURE, 539 return cmd_results_new(CMD_FAILURE,
540 "Invalid %s command (expected binding with the form " 540 "Invalid %s command (expected binding with the form "
541 "<switch>:<state>)", bindtype, argc); 541 "<switch>:<state>)", bindtype);
542 } 542 }
543 if (strcmp(split->items[0], "tablet") == 0) { 543 if (strcmp(split->items[0], "tablet") == 0) {
544 binding->type = WLR_SWITCH_TYPE_TABLET_MODE; 544 binding->type = WLR_SWITCH_TYPE_TABLET_MODE;
@@ -548,20 +548,21 @@ struct cmd_results *cmd_bind_or_unbind_switch(int argc, char **argv,
548 free_switch_binding(binding); 548 free_switch_binding(binding);
549 return cmd_results_new(CMD_FAILURE, 549 return cmd_results_new(CMD_FAILURE,
550 "Invalid %s command (expected switch binding: " 550 "Invalid %s command (expected switch binding: "
551 "unknown switch %s)", bindtype, split->items[0]); 551 "unknown switch %s)", bindtype,
552 (const char *)split->items[0]);
552 } 553 }
553 if (strcmp(split->items[1], "on") == 0) { 554 if (strcmp(split->items[1], "on") == 0) {
554 binding->state = WLR_SWITCH_STATE_ON; 555 binding->trigger = SWAY_SWITCH_TRIGGER_ON;
555 } else if (strcmp(split->items[1], "off") == 0) { 556 } else if (strcmp(split->items[1], "off") == 0) {
556 binding->state = WLR_SWITCH_STATE_OFF; 557 binding->trigger = SWAY_SWITCH_TRIGGER_OFF;
557 } else if (strcmp(split->items[1], "toggle") == 0) { 558 } else if (strcmp(split->items[1], "toggle") == 0) {
558 binding->state = WLR_SWITCH_STATE_TOGGLE; 559 binding->trigger = SWAY_SWITCH_TRIGGER_TOGGLE;
559 } else { 560 } else {
560 free_switch_binding(binding); 561 free_switch_binding(binding);
561 return cmd_results_new(CMD_FAILURE, 562 return cmd_results_new(CMD_FAILURE,
562 "Invalid %s command " 563 "Invalid %s command "
563 "(expected switch state: unknown state %d)", 564 "(expected switch state: unknown state %s)",
564 bindtype, split->items[0]); 565 bindtype, (const char *)split->items[1]);
565 } 566 }
566 list_free_items_and_destroy(split); 567 list_free_items_and_destroy(split);
567 568
diff --git a/sway/commands/client.c b/sway/commands/client.c
index dd0694df..fd2ac7a8 100644
--- a/sway/commands/client.c
+++ b/sway/commands/client.c
@@ -5,9 +5,8 @@
5#include "sway/tree/container.h" 5#include "sway/tree/container.h"
6#include "util.h" 6#include "util.h"
7 7
8static void rebuild_textures_iterator(struct sway_container *con, void *data) { 8static void container_update_iterator(struct sway_container *con, void *data) {
9 container_update_marks_textures(con); 9 container_update(con);
10 container_update_title_textures(con);
11} 10}
12 11
13static struct cmd_results *handle_command(int argc, char **argv, char *cmd_name, 12static struct cmd_results *handle_command(int argc, char **argv, char *cmd_name,
@@ -18,6 +17,12 @@ static struct cmd_results *handle_command(int argc, char **argv, char *cmd_name,
18 return error; 17 return error;
19 } 18 }
20 19
20 if (argc > 3 && strcmp(cmd_name, "client.focused_tab_title") == 0) {
21 sway_log(SWAY_ERROR,
22 "Warning: indicator and child_border colors have no effect for %s",
23 cmd_name);
24 }
25
21 struct border_colors colors = {0}; 26 struct border_colors colors = {0};
22 const char *ind_hex = argc > 3 ? argv[3] : default_indicator; 27 const char *ind_hex = argc > 3 ? argv[3] : default_indicator;
23 const char *child_hex = argc > 4 ? argv[4] : argv[1]; // def to background 28 const char *child_hex = argc > 4 ? argv[4] : argv[1]; // def to background
@@ -45,12 +50,7 @@ static struct cmd_results *handle_command(int argc, char **argv, char *cmd_name,
45 memcpy(class, &colors, sizeof(struct border_colors)); 50 memcpy(class, &colors, sizeof(struct border_colors));
46 51
47 if (config->active) { 52 if (config->active) {
48 root_for_each_container(rebuild_textures_iterator, NULL); 53 root_for_each_container(container_update_iterator, NULL);
49
50 for (int i = 0; i < root->outputs->length; ++i) {
51 struct sway_output *output = root->outputs->items[i];
52 output_damage_whole(output);
53 }
54 } 54 }
55 55
56 return cmd_results_new(CMD_SUCCESS, NULL); 56 return cmd_results_new(CMD_SUCCESS, NULL);
@@ -80,3 +80,13 @@ struct cmd_results *cmd_client_noop(int argc, char **argv) {
80 sway_log(SWAY_INFO, "Warning: %s is ignored by sway", argv[-1]); 80 sway_log(SWAY_INFO, "Warning: %s is ignored by sway", argv[-1]);
81 return cmd_results_new(CMD_SUCCESS, NULL); 81 return cmd_results_new(CMD_SUCCESS, NULL);
82} 82}
83
84struct cmd_results *cmd_client_focused_tab_title(int argc, char **argv) {
85 struct cmd_results *result = handle_command(argc, argv,
86 "client.focused_tab_title",
87 &config->border_colors.focused_tab_title, "#2e9ef4ff");
88 if (result && result->status == CMD_SUCCESS) {
89 config->has_focused_tab_title = true;
90 }
91 return result;
92}
diff --git a/sway/commands/exec_always.c b/sway/commands/exec_always.c
index fce337d5..8bc1048c 100644
--- a/sway/commands/exec_always.c
+++ b/sway/commands/exec_always.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <stdlib.h> 1#include <stdlib.h>
3#include <stdint.h> 2#include <stdint.h>
4#include <string.h> 3#include <string.h>
@@ -7,6 +6,8 @@
7#include <signal.h> 6#include <signal.h>
8#include "sway/commands.h" 7#include "sway/commands.h"
9#include "sway/config.h" 8#include "sway/config.h"
9#include "sway/server.h"
10#include "sway/desktop/launcher.h"
10#include "sway/tree/container.h" 11#include "sway/tree/container.h"
11#include "sway/tree/root.h" 12#include "sway/tree/root.h"
12#include "sway/tree/workspace.h" 13#include "sway/tree/workspace.h"
@@ -24,11 +25,22 @@ struct cmd_results *cmd_exec_validate(int argc, char **argv) {
24 return error; 25 return error;
25} 26}
26 27
28static void export_xdga_token(struct launcher_ctx *ctx) {
29 const char *token = launcher_ctx_get_token_name(ctx);
30 setenv("XDG_ACTIVATION_TOKEN", token, 1);
31}
32
33static void export_startup_id(struct launcher_ctx *ctx) {
34 const char *token = launcher_ctx_get_token_name(ctx);
35 setenv("DESKTOP_STARTUP_ID", token, 1);
36}
37
27struct cmd_results *cmd_exec_process(int argc, char **argv) { 38struct cmd_results *cmd_exec_process(int argc, char **argv) {
28 struct cmd_results *error = NULL; 39 struct cmd_results *error = NULL;
29 char *cmd = NULL; 40 char *cmd = NULL;
41 bool no_startup_id = false;
30 if (strcmp(argv[0], "--no-startup-id") == 0) { 42 if (strcmp(argv[0], "--no-startup-id") == 0) {
31 sway_log(SWAY_INFO, "exec switch '--no-startup-id' not supported, ignored."); 43 no_startup_id = true;
32 --argc; ++argv; 44 --argc; ++argv;
33 if ((error = checkarg(argc, argv[-1], EXPECTED_AT_LEAST, 1))) { 45 if ((error = checkarg(argc, argv[-1], EXPECTED_AT_LEAST, 1))) {
34 return error; 46 return error;
@@ -50,9 +62,11 @@ struct cmd_results *cmd_exec_process(int argc, char **argv) {
50 } 62 }
51 63
52 pid_t pid, child; 64 pid_t pid, child;
65 struct launcher_ctx *ctx = launcher_ctx_create_internal();
53 // Fork process 66 // Fork process
54 if ((pid = fork()) == 0) { 67 if ((pid = fork()) == 0) {
55 // Fork child process again 68 // Fork child process again
69 restore_nofile_limit();
56 setsid(); 70 setsid();
57 sigset_t set; 71 sigset_t set;
58 sigemptyset(&set); 72 sigemptyset(&set);
@@ -61,6 +75,12 @@ struct cmd_results *cmd_exec_process(int argc, char **argv) {
61 close(fd[0]); 75 close(fd[0]);
62 if ((child = fork()) == 0) { 76 if ((child = fork()) == 0) {
63 close(fd[1]); 77 close(fd[1]);
78 if (ctx) {
79 export_xdga_token(ctx);
80 }
81 if (ctx && !no_startup_id) {
82 export_startup_id(ctx);
83 }
64 execlp("sh", "sh", "-c", cmd, (void *)NULL); 84 execlp("sh", "sh", "-c", cmd, (void *)NULL);
65 sway_log_errno(SWAY_ERROR, "execlp failed"); 85 sway_log_errno(SWAY_ERROR, "execlp failed");
66 _exit(1); 86 _exit(1);
@@ -88,8 +108,12 @@ struct cmd_results *cmd_exec_process(int argc, char **argv) {
88 waitpid(pid, NULL, 0); 108 waitpid(pid, NULL, 0);
89 if (child > 0) { 109 if (child > 0) {
90 sway_log(SWAY_DEBUG, "Child process created with pid %d", child); 110 sway_log(SWAY_DEBUG, "Child process created with pid %d", child);
91 root_record_workspace_pid(child); 111 if (ctx != NULL) {
112 sway_log(SWAY_DEBUG, "Recording workspace for process %d", child);
113 ctx->pid = child;
114 }
92 } else { 115 } else {
116 launcher_ctx_destroy(ctx);
93 return cmd_results_new(CMD_FAILURE, "Second fork() failed"); 117 return cmd_results_new(CMD_FAILURE, "Second fork() failed");
94 } 118 }
95 119
diff --git a/sway/commands/floating_minmax_size.c b/sway/commands/floating_minmax_size.c
index 3a1d606a..e8c24ace 100644
--- a/sway/commands/floating_minmax_size.c
+++ b/sway/commands/floating_minmax_size.c
@@ -23,16 +23,16 @@ static struct cmd_results *handle_command(int argc, char **argv, char *cmd_name,
23 char *err; 23 char *err;
24 int width = (int)strtol(argv[0], &err, 10); 24 int width = (int)strtol(argv[0], &err, 10);
25 if (*err) { 25 if (*err) {
26 return cmd_results_new(CMD_INVALID, cmd_name, usage); 26 return cmd_results_new(CMD_INVALID, "%s", usage);
27 } 27 }
28 28
29 if (strcmp(argv[1], "x") != 0) { 29 if (strcmp(argv[1], "x") != 0) {
30 return cmd_results_new(CMD_INVALID, cmd_name, usage); 30 return cmd_results_new(CMD_INVALID, "%s", usage);
31 } 31 }
32 32
33 int height = (int)strtol(argv[2], &err, 10); 33 int height = (int)strtol(argv[2], &err, 10);
34 if (*err) { 34 if (*err) {
35 return cmd_results_new(CMD_INVALID, cmd_name, usage); 35 return cmd_results_new(CMD_INVALID, "%s", usage);
36 } 36 }
37 37
38 *config_width = width; 38 *config_width = width;
diff --git a/sway/commands/focus.c b/sway/commands/focus.c
index 6771ca2f..facd82de 100644
--- a/sway/commands/focus.c
+++ b/sway/commands/focus.c
@@ -54,7 +54,7 @@ static bool get_direction_from_next_prev(struct sway_container *container,
54 } else { 54 } else {
55 return false; 55 return false;
56 } 56 }
57 57
58 return true; 58 return true;
59} 59}
60 60
@@ -267,6 +267,11 @@ static struct cmd_results *focus_mode(struct sway_workspace *ws,
267 new_focus = seat_get_focus_inactive_tiling(seat, ws); 267 new_focus = seat_get_focus_inactive_tiling(seat, ws);
268 } 268 }
269 if (new_focus) { 269 if (new_focus) {
270 struct sway_container *new_focus_view =
271 seat_get_focus_inactive_view(seat, &new_focus->node);
272 if (new_focus_view) {
273 new_focus = new_focus_view;
274 }
270 seat_set_focus_container(seat, new_focus); 275 seat_set_focus_container(seat, new_focus);
271 276
272 // If we're on the floating layer and the floating container area 277 // If we're on the floating layer and the floating container area
@@ -280,7 +285,7 @@ static struct cmd_results *focus_mode(struct sway_workspace *ws,
280 } 285 }
281 } else { 286 } else {
282 return cmd_results_new(CMD_FAILURE, 287 return cmd_results_new(CMD_FAILURE,
283 "Failed to find a %s container in workspace", 288 "Failed to find a %s container in workspace.",
284 floating ? "floating" : "tiling"); 289 floating ? "floating" : "tiling");
285 } 290 }
286 return cmd_results_new(CMD_SUCCESS, NULL); 291 return cmd_results_new(CMD_SUCCESS, NULL);
@@ -290,7 +295,7 @@ static struct cmd_results *focus_output(struct sway_seat *seat,
290 int argc, char **argv) { 295 int argc, char **argv) {
291 if (!argc) { 296 if (!argc) {
292 return cmd_results_new(CMD_INVALID, 297 return cmd_results_new(CMD_INVALID,
293 "Expected 'focus output <direction|name>'"); 298 "Expected 'focus output <direction|name>'.");
294 } 299 }
295 char *identifier = join_args(argv, argc); 300 char *identifier = join_args(argv, argc);
296 struct sway_output *output = output_by_name_or_id(identifier); 301 struct sway_output *output = output_by_name_or_id(identifier);
@@ -300,13 +305,13 @@ static struct cmd_results *focus_output(struct sway_seat *seat,
300 if (!parse_direction(identifier, &direction)) { 305 if (!parse_direction(identifier, &direction)) {
301 free(identifier); 306 free(identifier);
302 return cmd_results_new(CMD_INVALID, 307 return cmd_results_new(CMD_INVALID,
303 "There is no output with that name"); 308 "There is no output with that name.");
304 } 309 }
305 struct sway_workspace *ws = seat_get_focused_workspace(seat); 310 struct sway_workspace *ws = seat_get_focused_workspace(seat);
306 if (!ws) { 311 if (!ws) {
307 free(identifier); 312 free(identifier);
308 return cmd_results_new(CMD_FAILURE, 313 return cmd_results_new(CMD_FAILURE,
309 "No focused workspace to base directions off of"); 314 "No focused workspace to base directions off of.");
310 } 315 }
311 output = output_get_in_direction(ws->output, direction); 316 output = output_get_in_direction(ws->output, direction);
312 317
@@ -370,10 +375,14 @@ struct cmd_results *cmd_focus(int argc, char **argv) {
370 struct sway_seat *seat = config->handler_context.seat; 375 struct sway_seat *seat = config->handler_context.seat;
371 if (node->type < N_WORKSPACE) { 376 if (node->type < N_WORKSPACE) {
372 return cmd_results_new(CMD_FAILURE, 377 return cmd_results_new(CMD_FAILURE,
373 "Command 'focus' cannot be used above the workspace level"); 378 "Command 'focus' cannot be used above the workspace level.");
374 } 379 }
375 380
376 if (argc == 0 && container) { 381 if (argc == 0) {
382 if (!container) {
383 return cmd_results_new(CMD_FAILURE, "No container to focus was specified.");
384 }
385
377 if (container_is_scratchpad_hidden_or_child(container)) { 386 if (container_is_scratchpad_hidden_or_child(container)) {
378 root_scratchpad_show(container); 387 root_scratchpad_show(container);
379 } 388 }
@@ -446,7 +455,8 @@ struct cmd_results *cmd_focus(int argc, char **argv) {
446 return cmd_results_new(CMD_FAILURE, ""); 455 return cmd_results_new(CMD_FAILURE, "");
447 } 456 }
448 struct sway_node *next_focus = NULL; 457 struct sway_node *next_focus = NULL;
449 if (container_is_floating(container)) { 458 if (container_is_floating(container) &&
459 container->pending.fullscreen_mode == FULLSCREEN_NONE) {
450 next_focus = node_get_in_direction_floating(container, seat, direction); 460 next_focus = node_get_in_direction_floating(container, seat, direction);
451 } else { 461 } else {
452 next_focus = node_get_in_direction_tiling(container, seat, direction, descend); 462 next_focus = node_get_in_direction_tiling(container, seat, direction, descend);
diff --git a/sway/commands/font.c b/sway/commands/font.c
index c54365b5..9920d03e 100644
--- a/sway/commands/font.c
+++ b/sway/commands/font.c
@@ -1,9 +1,9 @@
1#define _POSIX_C_SOURCE 200809L
2#include <string.h> 1#include <string.h>
3#include "sway/commands.h" 2#include "sway/commands.h"
4#include "sway/config.h" 3#include "sway/config.h"
5#include "log.h" 4#include "log.h"
6#include "stringop.h" 5#include "stringop.h"
6#include <pango/pangocairo.h>
7 7
8struct cmd_results *cmd_font(int argc, char **argv) { 8struct cmd_results *cmd_font(int argc, char **argv) {
9 struct cmd_results *error = NULL; 9 struct cmd_results *error = NULL;
@@ -16,12 +16,34 @@ struct cmd_results *cmd_font(int argc, char **argv) {
16 if (strncmp(font, "pango:", 6) == 0) { 16 if (strncmp(font, "pango:", 6) == 0) {
17 config->pango_markup = true; 17 config->pango_markup = true;
18 config->font = strdup(font + 6); 18 config->font = strdup(font + 6);
19 free(font);
19 } else { 20 } else {
20 config->pango_markup = false; 21 config->pango_markup = false;
21 config->font = strdup(font); 22 config->font = font;
22 } 23 }
23 24
24 free(font); 25 // Parse the font early so we can reject it if it's not valid for pango.
25 config_update_font_height(true); 26 // Also avoids re-parsing each time we render text.
27 PangoFontDescription *font_description = pango_font_description_from_string(config->font);
28
29 const char *family = pango_font_description_get_family(font_description);
30 if (family == NULL) {
31 pango_font_description_free(font_description);
32 return cmd_results_new(CMD_FAILURE, "Invalid font family.");
33 }
34
35 const gint size = pango_font_description_get_size(font_description);
36 if (size == 0) {
37 pango_font_description_free(font_description);
38 return cmd_results_new(CMD_FAILURE, "Invalid font size.");
39 }
40
41 if (config->font_description != NULL) {
42 pango_font_description_free(config->font_description);
43 }
44
45 config->font_description = font_description;
46 config_update_font_height();
47
26 return cmd_results_new(CMD_SUCCESS, NULL); 48 return cmd_results_new(CMD_SUCCESS, NULL);
27} 49}
diff --git a/sway/commands/for_window.c b/sway/commands/for_window.c
index ee9f4647..905e6776 100644
--- a/sway/commands/for_window.c
+++ b/sway/commands/for_window.c
@@ -14,7 +14,7 @@ struct cmd_results *cmd_for_window(int argc, char **argv) {
14 char *err_str = NULL; 14 char *err_str = NULL;
15 struct criteria *criteria = criteria_parse(argv[0], &err_str); 15 struct criteria *criteria = criteria_parse(argv[0], &err_str);
16 if (!criteria) { 16 if (!criteria) {
17 error = cmd_results_new(CMD_INVALID, err_str); 17 error = cmd_results_new(CMD_INVALID, "%s", err_str);
18 free(err_str); 18 free(err_str);
19 return error; 19 return error;
20 } 20 }
diff --git a/sway/commands/gesture.c b/sway/commands/gesture.c
new file mode 100644
index 00000000..90a20716
--- /dev/null
+++ b/sway/commands/gesture.c
@@ -0,0 +1,165 @@
1#include "sway/config.h"
2
3#include "gesture.h"
4#include "log.h"
5#include "stringop.h"
6#include "sway/commands.h"
7
8void free_gesture_binding(struct sway_gesture_binding *binding) {
9 if (!binding) {
10 return;
11 }
12 free(binding->input);
13 free(binding->command);
14 free(binding);
15}
16
17/**
18 * Returns true if the bindings have the same gesture type, direction, etc
19 */
20static bool binding_gesture_equal(struct sway_gesture_binding *binding_a,
21 struct sway_gesture_binding *binding_b) {
22 if (strcmp(binding_a->input, binding_b->input) != 0) {
23 return false;
24 }
25
26 if (!gesture_equal(&binding_a->gesture, &binding_b->gesture)) {
27 return false;
28 }
29
30 if ((binding_a->flags & BINDING_EXACT) !=
31 (binding_b->flags & BINDING_EXACT)) {
32 return false;
33 }
34 return true;
35}
36
37/**
38 * Add gesture binding to config
39 */
40static struct cmd_results *gesture_binding_add(
41 struct sway_gesture_binding *binding,
42 const char *gesturecombo, bool warn) {
43 list_t *mode_bindings = config->current_mode->gesture_bindings;
44 // overwrite the binding if it already exists
45 bool overwritten = false;
46 for (int i = 0; i < mode_bindings->length; ++i) {
47 struct sway_gesture_binding *config_binding = mode_bindings->items[i];
48 if (binding_gesture_equal(binding, config_binding)) {
49 sway_log(SWAY_INFO, "Overwriting binding '%s' to `%s` from `%s`",
50 gesturecombo, binding->command, config_binding->command);
51 if (warn) {
52 config_add_swaynag_warning("Overwriting binding"
53 "'%s' to `%s` from `%s`",
54 gesturecombo, binding->command,
55 config_binding->command);
56 }
57 free_gesture_binding(config_binding);
58 mode_bindings->items[i] = binding;
59 overwritten = true;
60 }
61 }
62
63 if (!overwritten) {
64 list_add(mode_bindings, binding);
65 sway_log(SWAY_DEBUG, "bindgesture - Bound %s to command `%s`",
66 gesturecombo, binding->command);
67 }
68
69 return cmd_results_new(CMD_SUCCESS, NULL);
70}
71
72/**
73 * Remove gesture binding from config
74 */
75static struct cmd_results *gesture_binding_remove(
76 struct sway_gesture_binding *binding, const char *gesturecombo) {
77 list_t *mode_bindings = config->current_mode->gesture_bindings;
78 for (int i = 0; i < mode_bindings->length; ++i) {
79 struct sway_gesture_binding *config_binding = mode_bindings->items[i];
80 if (binding_gesture_equal(binding, config_binding)) {
81 free_gesture_binding(config_binding);
82 free_gesture_binding(binding);
83 list_del(mode_bindings, i);
84 sway_log(SWAY_DEBUG, "unbindgesture - Unbound %s gesture",
85 gesturecombo);
86 return cmd_results_new(CMD_SUCCESS, NULL);
87 }
88 }
89
90 free_gesture_binding(binding);
91 return cmd_results_new(CMD_FAILURE, "Could not find gesture binding `%s`",
92 gesturecombo);
93}
94
95/**
96 * Parse and execute bindgesture or unbindgesture command.
97 */
98static struct cmd_results *cmd_bind_or_unbind_gesture(int argc, char **argv, bool unbind) {
99 int minargs = 2;
100 char *bindtype = "bindgesture";
101 if (unbind) {
102 minargs--;
103 bindtype = "unbindgesture";
104 }
105
106 struct cmd_results *error = NULL;
107 if ((error = checkarg(argc, bindtype, EXPECTED_AT_LEAST, minargs))) {
108 return error;
109 }
110 struct sway_gesture_binding *binding = calloc(1, sizeof(struct sway_gesture_binding));
111 if (!binding) {
112 return cmd_results_new(CMD_FAILURE, "Unable to allocate binding");
113 }
114 binding->input = strdup("*");
115
116 bool warn = true;
117
118 // Handle flags
119 while (argc > 0) {
120 if (strcmp("--exact", argv[0]) == 0) {
121 binding->flags |= BINDING_EXACT;
122 } else if (strcmp("--no-warn", argv[0]) == 0) {
123 warn = false;
124 } else if (strncmp("--input-device=", argv[0],
125 strlen("--input-device=")) == 0) {
126 free(binding->input);
127 binding->input = strdup(argv[0] + strlen("--input-device="));
128 } else {
129 break;
130 }
131 argv++;
132 argc--;
133 }
134
135 if (argc < minargs) {
136 free(binding);
137 return cmd_results_new(CMD_FAILURE,
138 "Invalid %s command (expected at least %d "
139 "non-option arguments, got %d)", bindtype, minargs, argc);
140 }
141
142 char* errmsg = NULL;
143 if ((errmsg = gesture_parse(argv[0], &binding->gesture))) {
144 free(binding);
145 struct cmd_results *final = cmd_results_new(CMD_FAILURE,
146 "Invalid %s command (%s)",
147 bindtype, errmsg);
148 free(errmsg);
149 return final;
150 }
151
152 if (unbind) {
153 return gesture_binding_remove(binding, argv[0]);
154 }
155 binding->command = join_args(argv + 1, argc - 1);
156 return gesture_binding_add(binding, argv[0], warn);
157}
158
159struct cmd_results *cmd_bindgesture(int argc, char **argv) {
160 return cmd_bind_or_unbind_gesture(argc, argv, false);
161}
162
163struct cmd_results *cmd_unbindgesture(int argc, char **argv) {
164 return cmd_bind_or_unbind_gesture(argc, argv, true);
165}
diff --git a/sway/commands/hide_edge_borders.c b/sway/commands/hide_edge_borders.c
index 9a1d8445..43bd6dc8 100644
--- a/sway/commands/hide_edge_borders.c
+++ b/sway/commands/hide_edge_borders.c
@@ -20,7 +20,7 @@ struct cmd_results *cmd_hide_edge_borders(int argc, char **argv) {
20 } 20 }
21 21
22 if (!argc) { 22 if (!argc) {
23 return cmd_results_new(CMD_INVALID, expected_syntax); 23 return cmd_results_new(CMD_INVALID, "%s", expected_syntax);
24 } 24 }
25 25
26 if (strcmp(argv[0], "none") == 0) { 26 if (strcmp(argv[0], "none") == 0) {
@@ -38,7 +38,7 @@ struct cmd_results *cmd_hide_edge_borders(int argc, char **argv) {
38 config->hide_edge_borders = E_NONE; 38 config->hide_edge_borders = E_NONE;
39 config->hide_edge_borders_smart = ESMART_NO_GAPS; 39 config->hide_edge_borders_smart = ESMART_NO_GAPS;
40 } else { 40 } else {
41 return cmd_results_new(CMD_INVALID, expected_syntax); 41 return cmd_results_new(CMD_INVALID, "%s", expected_syntax);
42 } 42 }
43 config->hide_lone_tab = hide_lone_tab; 43 config->hide_lone_tab = hide_lone_tab;
44 44
diff --git a/sway/commands/inhibit_idle.c b/sway/commands/inhibit_idle.c
index aebc2bf9..6125736a 100644
--- a/sway/commands/inhibit_idle.c
+++ b/sway/commands/inhibit_idle.c
@@ -41,7 +41,7 @@ struct cmd_results *cmd_inhibit_idle(int argc, char **argv) {
41 sway_idle_inhibit_v1_user_inhibitor_destroy(inhibitor); 41 sway_idle_inhibit_v1_user_inhibitor_destroy(inhibitor);
42 } else { 42 } else {
43 inhibitor->mode = mode; 43 inhibitor->mode = mode;
44 sway_idle_inhibit_v1_check_active(server.idle_inhibit_manager_v1); 44 sway_idle_inhibit_v1_check_active();
45 } 45 }
46 } else if (!clear) { 46 } else if (!clear) {
47 sway_idle_inhibit_v1_user_inhibitor_register(con->view, mode); 47 sway_idle_inhibit_v1_user_inhibitor_register(con->view, mode);
diff --git a/sway/commands/input.c b/sway/commands/input.c
index 77acb671..306c40f7 100644
--- a/sway/commands/input.c
+++ b/sway/commands/input.c
@@ -14,6 +14,7 @@ static const struct cmd_handler input_handlers[] = {
14 { "drag", input_cmd_drag }, 14 { "drag", input_cmd_drag },
15 { "drag_lock", input_cmd_drag_lock }, 15 { "drag_lock", input_cmd_drag_lock },
16 { "dwt", input_cmd_dwt }, 16 { "dwt", input_cmd_dwt },
17 { "dwtp", input_cmd_dwtp },
17 { "events", input_cmd_events }, 18 { "events", input_cmd_events },
18 { "left_handed", input_cmd_left_handed }, 19 { "left_handed", input_cmd_left_handed },
19 { "map_from_region", input_cmd_map_from_region }, 20 { "map_from_region", input_cmd_map_from_region },
@@ -24,7 +25,9 @@ static const struct cmd_handler input_handlers[] = {
24 { "pointer_accel", input_cmd_pointer_accel }, 25 { "pointer_accel", input_cmd_pointer_accel },
25 { "repeat_delay", input_cmd_repeat_delay }, 26 { "repeat_delay", input_cmd_repeat_delay },
26 { "repeat_rate", input_cmd_repeat_rate }, 27 { "repeat_rate", input_cmd_repeat_rate },
28 { "rotation_angle", input_cmd_rotation_angle },
27 { "scroll_button", input_cmd_scroll_button }, 29 { "scroll_button", input_cmd_scroll_button },
30 { "scroll_button_lock", input_cmd_scroll_button_lock },
28 { "scroll_factor", input_cmd_scroll_factor }, 31 { "scroll_factor", input_cmd_scroll_factor },
29 { "scroll_method", input_cmd_scroll_method }, 32 { "scroll_method", input_cmd_scroll_method },
30 { "tap", input_cmd_tap }, 33 { "tap", input_cmd_tap },
diff --git a/sway/commands/input/calibration_matrix.c b/sway/commands/input/calibration_matrix.c
index 38749fbb..53fe2c35 100644
--- a/sway/commands/input/calibration_matrix.c
+++ b/sway/commands/input/calibration_matrix.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <string.h> 1#include <string.h>
3#include <strings.h> 2#include <strings.h>
4#include "sway/config.h" 3#include "sway/config.h"
diff --git a/sway/commands/input/dwtp.c b/sway/commands/input/dwtp.c
new file mode 100644
index 00000000..232e2b26
--- /dev/null
+++ b/sway/commands/input/dwtp.c
@@ -0,0 +1,25 @@
1#include <string.h>
2#include <strings.h>
3#include "sway/config.h"
4#include "sway/commands.h"
5#include "sway/input/input-manager.h"
6#include "util.h"
7
8struct cmd_results *input_cmd_dwtp(int argc, char **argv) {
9 struct cmd_results *error = NULL;
10 if ((error = checkarg(argc, "dwtp", EXPECTED_AT_LEAST, 1))) {
11 return error;
12 }
13 struct input_config *ic = config->handler_context.input_config;
14 if (!ic) {
15 return cmd_results_new(CMD_FAILURE, "No input device defined.");
16 }
17
18 if (parse_boolean(argv[0], true)) {
19 ic->dwtp = LIBINPUT_CONFIG_DWTP_ENABLED;
20 } else {
21 ic->dwtp = LIBINPUT_CONFIG_DWTP_DISABLED;
22 }
23
24 return cmd_results_new(CMD_SUCCESS, NULL);
25}
diff --git a/sway/commands/input/events.c b/sway/commands/input/events.c
index 9405181a..08d99bf0 100644
--- a/sway/commands/input/events.c
+++ b/sway/commands/input/events.c
@@ -1,14 +1,19 @@
1#include <limits.h> 1#include <limits.h>
2#include <string.h> 2#include <string.h>
3#include <strings.h> 3#include <strings.h>
4#include <wlr/backend/libinput.h> 4#include <wlr/config.h>
5#include "sway/config.h" 5#include "sway/config.h"
6#include "sway/commands.h" 6#include "sway/commands.h"
7#include "sway/input/input-manager.h" 7#include "sway/input/input-manager.h"
8#include "log.h" 8#include "log.h"
9 9
10#if WLR_HAS_LIBINPUT_BACKEND
11#include <wlr/backend/libinput.h>
12#endif
13
10static void toggle_supported_send_events_for_device(struct input_config *ic, 14static void toggle_supported_send_events_for_device(struct input_config *ic,
11 struct sway_input_device *input_device) { 15 struct sway_input_device *input_device) {
16#if WLR_HAS_LIBINPUT_BACKEND
12 struct wlr_input_device *wlr_device = input_device->wlr_device; 17 struct wlr_input_device *wlr_device = input_device->wlr_device;
13 if (!wlr_input_device_is_libinput(wlr_device)) { 18 if (!wlr_input_device_is_libinput(wlr_device)) {
14 return; 19 return;
@@ -41,6 +46,7 @@ static void toggle_supported_send_events_for_device(struct input_config *ic,
41 } 46 }
42 47
43 ic->send_events = mode; 48 ic->send_events = mode;
49#endif
44} 50}
45 51
46static int mode_for_name(const char *name) { 52static int mode_for_name(const char *name) {
@@ -56,6 +62,7 @@ static int mode_for_name(const char *name) {
56 62
57static void toggle_select_send_events_for_device(struct input_config *ic, 63static void toggle_select_send_events_for_device(struct input_config *ic,
58 struct sway_input_device *input_device, int argc, char **argv) { 64 struct sway_input_device *input_device, int argc, char **argv) {
65#if WLR_HAS_LIBINPUT_BACKEND
59 if (!wlr_input_device_is_libinput(input_device->wlr_device)) { 66 if (!wlr_input_device_is_libinput(input_device->wlr_device)) {
60 return; 67 return;
61 } 68 }
@@ -72,6 +79,7 @@ static void toggle_select_send_events_for_device(struct input_config *ic,
72 } 79 }
73 } 80 }
74 ic->send_events = mode_for_name(argv[index % argc]); 81 ic->send_events = mode_for_name(argv[index % argc]);
82#endif
75} 83}
76 84
77static void toggle_send_events(int argc, char **argv) { 85static void toggle_send_events(int argc, char **argv) {
diff --git a/sway/commands/input/map_from_region.c b/sway/commands/input/map_from_region.c
index de00b714..2f8f753d 100644
--- a/sway/commands/input/map_from_region.c
+++ b/sway/commands/input/map_from_region.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <stdbool.h> 1#include <stdbool.h>
3#include <string.h> 2#include <string.h>
4#include <strings.h> 3#include <strings.h>
@@ -11,11 +10,21 @@ static bool parse_coords(const char *str, double *x, double *y, bool *mm) {
11 *mm = false; 10 *mm = false;
12 11
13 char *end; 12 char *end;
14 *x = strtod(str, &end); 13
15 if (end[0] != 'x') { 14 // Check for "0x" prefix to avoid strtod treating the string as hex
16 return false; 15 if (str[0] == '0' && str[1] == 'x') {
16 if (strlen(str) < 3) {
17 return false;
18 }
19 *x = 0;
20 end = (char *)str + 2;
21 } else {
22 *x = strtod(str, &end);
23 if (end[0] != 'x') {
24 return false;
25 }
26 ++end;
17 } 27 }
18 ++end;
19 28
20 *y = strtod(end, &end); 29 *y = strtod(end, &end);
21 if (end[0] == 'm') { 30 if (end[0] == 'm') {
diff --git a/sway/commands/input/map_to_output.c b/sway/commands/input/map_to_output.c
index f60fb7d5..a7266baa 100644
--- a/sway/commands/input/map_to_output.c
+++ b/sway/commands/input/map_to_output.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <string.h> 1#include <string.h>
3#include <strings.h> 2#include <strings.h>
4#include "sway/config.h" 3#include "sway/config.h"
diff --git a/sway/commands/input/map_to_region.c b/sway/commands/input/map_to_region.c
index 284b57d0..9087c589 100644
--- a/sway/commands/input/map_to_region.c
+++ b/sway/commands/input/map_to_region.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <stdlib.h> 1#include <stdlib.h>
3#include <string.h> 2#include <string.h>
4#include "sway/commands.h" 3#include "sway/commands.h"
@@ -49,5 +48,5 @@ struct cmd_results *input_cmd_map_to_region(int argc, char **argv) {
49error: 48error:
50 free(ic->mapped_to_region); 49 free(ic->mapped_to_region);
51 ic->mapped_to_region = NULL; 50 ic->mapped_to_region = NULL;
52 return cmd_results_new(CMD_FAILURE, errstr); 51 return cmd_results_new(CMD_FAILURE, "%s", errstr);
53} 52}
diff --git a/sway/commands/input/rotation_angle.c b/sway/commands/input/rotation_angle.c
new file mode 100644
index 00000000..5e278fff
--- /dev/null
+++ b/sway/commands/input/rotation_angle.c
@@ -0,0 +1,29 @@
1#include <math.h>
2#include <stdlib.h>
3#include <string.h>
4#include "sway/config.h"
5#include "sway/commands.h"
6#include "sway/input/input-manager.h"
7#include "util.h"
8
9struct cmd_results *input_cmd_rotation_angle(int argc, char **argv) {
10 struct cmd_results *error = NULL;
11 if ((error = checkarg(argc, "rotation_angle", EXPECTED_AT_LEAST, 1))) {
12 return error;
13 }
14 struct input_config *ic = config->handler_context.input_config;
15 if (!ic) {
16 return cmd_results_new(CMD_FAILURE, "No input device defined.");
17 }
18
19 float rotation_angle = parse_float(argv[0]);
20 if (isnan(rotation_angle)) {
21 return cmd_results_new(CMD_INVALID,
22 "Invalid rotation_angle; expected float.");
23 } if (rotation_angle < 0 || rotation_angle > 360) {
24 return cmd_results_new(CMD_INVALID, "Input out of range [0, 360)");
25 }
26 ic->rotation_angle = rotation_angle;
27
28 return cmd_results_new(CMD_SUCCESS, NULL);
29}
diff --git a/sway/commands/input/scroll_button.c b/sway/commands/input/scroll_button.c
index 6b331419..81f69a6d 100644
--- a/sway/commands/input/scroll_button.c
+++ b/sway/commands/input/scroll_button.c
@@ -21,7 +21,7 @@ struct cmd_results *input_cmd_scroll_button(int argc, char **argv) {
21 char *message = NULL; 21 char *message = NULL;
22 uint32_t button = get_mouse_button(*argv, &message); 22 uint32_t button = get_mouse_button(*argv, &message);
23 if (message) { 23 if (message) {
24 error = cmd_results_new(CMD_INVALID, message); 24 error = cmd_results_new(CMD_INVALID, "%s", message);
25 free(message); 25 free(message);
26 return error; 26 return error;
27 } else if (button == SWAY_SCROLL_UP || button == SWAY_SCROLL_DOWN 27 } else if (button == SWAY_SCROLL_UP || button == SWAY_SCROLL_DOWN
diff --git a/sway/commands/input/scroll_button_lock.c b/sway/commands/input/scroll_button_lock.c
new file mode 100644
index 00000000..f96b6514
--- /dev/null
+++ b/sway/commands/input/scroll_button_lock.c
@@ -0,0 +1,26 @@
1#include <libinput.h>
2#include <string.h>
3#include <strings.h>
4#include "sway/config.h"
5#include "sway/commands.h"
6#include "sway/input/input-manager.h"
7#include "util.h"
8
9struct cmd_results *input_cmd_scroll_button_lock(int argc, char **argv) {
10 struct cmd_results *error = NULL;
11 if ((error = checkarg(argc, "scroll_button_lock", EXPECTED_AT_LEAST, 1))) {
12 return error;
13 }
14 struct input_config *ic = config->handler_context.input_config;
15 if (!ic) {
16 return cmd_results_new(CMD_FAILURE, "No input device defined.");
17 }
18
19 if (parse_boolean(argv[0], true)) {
20 ic->scroll_button_lock = LIBINPUT_CONFIG_SCROLL_BUTTON_LOCK_ENABLED;
21 } else {
22 ic->scroll_button_lock = LIBINPUT_CONFIG_SCROLL_BUTTON_LOCK_DISABLED;
23 }
24
25 return cmd_results_new(CMD_SUCCESS, NULL);
26}
diff --git a/sway/commands/input/xkb_file.c b/sway/commands/input/xkb_file.c
index 493f94fb..056f00e5 100644
--- a/sway/commands/input/xkb_file.c
+++ b/sway/commands/input/xkb_file.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <unistd.h> 1#include <unistd.h>
3#include <errno.h> 2#include <errno.h>
4#include "sway/config.h" 3#include "sway/config.h"
diff --git a/sway/commands/input/xkb_layout.c b/sway/commands/input/xkb_layout.c
index 22626517..1d01886c 100644
--- a/sway/commands/input/xkb_layout.c
+++ b/sway/commands/input/xkb_layout.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include "sway/config.h" 1#include "sway/config.h"
3#include "sway/commands.h" 2#include "sway/commands.h"
4#include "log.h" 3#include "log.h"
diff --git a/sway/commands/input/xkb_model.c b/sway/commands/input/xkb_model.c
index f4a33de3..a9144a8a 100644
--- a/sway/commands/input/xkb_model.c
+++ b/sway/commands/input/xkb_model.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include "sway/config.h" 1#include "sway/config.h"
3#include "sway/commands.h" 2#include "sway/commands.h"
4#include "log.h" 3#include "log.h"
diff --git a/sway/commands/input/xkb_numlock.c b/sway/commands/input/xkb_numlock.c
index 87d3e60c..bbe848fe 100644
--- a/sway/commands/input/xkb_numlock.c
+++ b/sway/commands/input/xkb_numlock.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include "sway/config.h" 1#include "sway/config.h"
3#include "sway/commands.h" 2#include "sway/commands.h"
4#include "util.h" 3#include "util.h"
diff --git a/sway/commands/input/xkb_options.c b/sway/commands/input/xkb_options.c
index d609293f..7ca20777 100644
--- a/sway/commands/input/xkb_options.c
+++ b/sway/commands/input/xkb_options.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include "sway/config.h" 1#include "sway/config.h"
3#include "sway/commands.h" 2#include "sway/commands.h"
4#include "log.h" 3#include "log.h"
diff --git a/sway/commands/input/xkb_rules.c b/sway/commands/input/xkb_rules.c
index 3b59622c..8fbd26fb 100644
--- a/sway/commands/input/xkb_rules.c
+++ b/sway/commands/input/xkb_rules.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include "sway/config.h" 1#include "sway/config.h"
3#include "sway/commands.h" 2#include "sway/commands.h"
4#include "log.h" 3#include "log.h"
diff --git a/sway/commands/input/xkb_switch_layout.c b/sway/commands/input/xkb_switch_layout.c
index d6548a68..ecac8e6c 100644
--- a/sway/commands/input/xkb_switch_layout.c
+++ b/sway/commands/input/xkb_switch_layout.c
@@ -1,10 +1,15 @@
1#define _POSIX_C_SOURCE 200809L
2#include <assert.h> 1#include <assert.h>
2#include <wlr/interfaces/wlr_keyboard.h>
3#include "sway/config.h" 3#include "sway/config.h"
4#include "sway/commands.h" 4#include "sway/commands.h"
5#include "sway/input/input-manager.h" 5#include "sway/input/input-manager.h"
6#include "log.h" 6#include "log.h"
7 7
8struct xkb_switch_layout_action {
9 struct wlr_keyboard *keyboard;
10 xkb_layout_index_t layout;
11};
12
8static void switch_layout(struct wlr_keyboard *kbd, xkb_layout_index_t idx) { 13static void switch_layout(struct wlr_keyboard *kbd, xkb_layout_index_t idx) {
9 xkb_layout_index_t num_layouts = xkb_keymap_num_layouts(kbd->keymap); 14 xkb_layout_index_t num_layouts = xkb_keymap_num_layouts(kbd->keymap);
10 if (idx >= num_layouts) { 15 if (idx >= num_layouts) {
@@ -28,10 +33,10 @@ static xkb_layout_index_t get_current_layout_index(struct wlr_keyboard *kbd) {
28 return layout_idx; 33 return layout_idx;
29} 34}
30 35
31static void switch_layout_relative(struct wlr_keyboard *kbd, int dir) { 36static xkb_layout_index_t get_layout_relative(struct wlr_keyboard *kbd, int dir) {
32 xkb_layout_index_t num_layouts = xkb_keymap_num_layouts(kbd->keymap); 37 xkb_layout_index_t num_layouts = xkb_keymap_num_layouts(kbd->keymap);
33 xkb_layout_index_t idx = get_current_layout_index(kbd); 38 xkb_layout_index_t idx = get_current_layout_index(kbd);
34 switch_layout(kbd, (idx + num_layouts + dir) % num_layouts); 39 return (idx + num_layouts + dir) % num_layouts;
35} 40}
36 41
37struct cmd_results *input_cmd_xkb_switch_layout(int argc, char **argv) { 42struct cmd_results *input_cmd_xkb_switch_layout(int argc, char **argv) {
@@ -66,6 +71,18 @@ struct cmd_results *input_cmd_xkb_switch_layout(int argc, char **argv) {
66 relative = 0; 71 relative = 0;
67 } 72 }
68 73
74 struct xkb_switch_layout_action *actions = calloc(
75 wl_list_length(&server.input->devices),
76 sizeof(struct xkb_switch_layout_action));
77 size_t actions_len = 0;
78
79 if (!actions) {
80 return cmd_results_new(CMD_FAILURE, "Unable to allocate actions");
81 }
82
83 /* Calculate new indexes first because switching a layout in one
84 keyboard may result in a change on other keyboards as well because
85 of keyboard groups. */
69 struct sway_input_device *dev; 86 struct sway_input_device *dev;
70 wl_list_for_each(dev, &server.input->devices, link) { 87 wl_list_for_each(dev, &server.input->devices, link) {
71 if (strcmp(ic->identifier, "*") != 0 && 88 if (strcmp(ic->identifier, "*") != 0 &&
@@ -76,12 +93,22 @@ struct cmd_results *input_cmd_xkb_switch_layout(int argc, char **argv) {
76 if (dev->wlr_device->type != WLR_INPUT_DEVICE_KEYBOARD) { 93 if (dev->wlr_device->type != WLR_INPUT_DEVICE_KEYBOARD) {
77 continue; 94 continue;
78 } 95 }
96
97 struct xkb_switch_layout_action *action =
98 &actions[actions_len++];
99
100 action->keyboard = wlr_keyboard_from_input_device(dev->wlr_device);
79 if (relative) { 101 if (relative) {
80 switch_layout_relative(dev->wlr_device->keyboard, relative); 102 action->layout = get_layout_relative(action->keyboard, relative);
81 } else { 103 } else {
82 switch_layout(dev->wlr_device->keyboard, layout); 104 action->layout = layout;
83 } 105 }
84 } 106 }
85 107
108 for (size_t i = 0; i < actions_len; i++) {
109 switch_layout(actions[i].keyboard, actions[i].layout);
110 }
111 free(actions);
112
86 return cmd_results_new(CMD_SUCCESS, NULL); 113 return cmd_results_new(CMD_SUCCESS, NULL);
87} 114}
diff --git a/sway/commands/input/xkb_variant.c b/sway/commands/input/xkb_variant.c
index d0e21d77..2d14ea9c 100644
--- a/sway/commands/input/xkb_variant.c
+++ b/sway/commands/input/xkb_variant.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include "sway/config.h" 1#include "sway/config.h"
3#include "sway/commands.h" 2#include "sway/commands.h"
4#include "log.h" 3#include "log.h"
diff --git a/sway/commands/layout.c b/sway/commands/layout.c
index 2ba61b38..12ce4839 100644
--- a/sway/commands/layout.c
+++ b/sway/commands/layout.c
@@ -153,7 +153,7 @@ struct cmd_results *cmd_layout(int argc, char **argv) {
153 workspace->output); 153 workspace->output);
154 } 154 }
155 if (new_layout == L_NONE) { 155 if (new_layout == L_NONE) {
156 return cmd_results_new(CMD_INVALID, expected_syntax); 156 return cmd_results_new(CMD_INVALID, "%s", expected_syntax);
157 } 157 }
158 if (new_layout != old_layout) { 158 if (new_layout != old_layout) {
159 if (container) { 159 if (container) {
diff --git a/sway/commands/mark.c b/sway/commands/mark.c
index aa5f185c..2bfc86b3 100644
--- a/sway/commands/mark.c
+++ b/sway/commands/mark.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <string.h> 1#include <string.h>
3#include "sway/commands.h" 2#include "sway/commands.h"
4#include "sway/config.h" 3#include "sway/config.h"
@@ -59,7 +58,7 @@ struct cmd_results *cmd_mark(int argc, char **argv) {
59 } 58 }
60 59
61 free(mark); 60 free(mark);
62 container_update_marks_textures(container); 61 container_update_marks(container);
63 if (container->view) { 62 if (container->view) {
64 view_execute_criteria(container->view); 63 view_execute_criteria(container->view);
65 } 64 }
diff --git a/sway/commands/mode.c b/sway/commands/mode.c
index e23e4ee4..b3216967 100644
--- a/sway/commands/mode.c
+++ b/sway/commands/mode.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <stdbool.h> 1#include <stdbool.h>
3#include <string.h> 2#include <string.h>
4#include "sway/commands.h" 3#include "sway/commands.h"
@@ -11,10 +10,12 @@
11// Must be in order for the bsearch 10// Must be in order for the bsearch
12static const struct cmd_handler mode_handlers[] = { 11static const struct cmd_handler mode_handlers[] = {
13 { "bindcode", cmd_bindcode }, 12 { "bindcode", cmd_bindcode },
13 { "bindgesture", cmd_bindgesture },
14 { "bindswitch", cmd_bindswitch }, 14 { "bindswitch", cmd_bindswitch },
15 { "bindsym", cmd_bindsym }, 15 { "bindsym", cmd_bindsym },
16 { "set", cmd_set }, 16 { "set", cmd_set },
17 { "unbindcode", cmd_unbindcode }, 17 { "unbindcode", cmd_unbindcode },
18 { "unbindgesture", cmd_unbindgesture },
18 { "unbindswitch", cmd_unbindswitch }, 19 { "unbindswitch", cmd_unbindswitch },
19 { "unbindsym", cmd_unbindsym }, 20 { "unbindsym", cmd_unbindsym },
20}; 21};
@@ -59,6 +60,7 @@ struct cmd_results *cmd_mode(int argc, char **argv) {
59 mode->keycode_bindings = create_list(); 60 mode->keycode_bindings = create_list();
60 mode->mouse_bindings = create_list(); 61 mode->mouse_bindings = create_list();
61 mode->switch_bindings = create_list(); 62 mode->switch_bindings = create_list();
63 mode->gesture_bindings = create_list();
62 mode->pango = pango; 64 mode->pango = pango;
63 list_add(config->modes, mode); 65 list_add(config->modes, mode);
64 } 66 }
diff --git a/sway/commands/move.c b/sway/commands/move.c
index f2702fa1..8addf26e 100644
--- a/sway/commands/move.c
+++ b/sway/commands/move.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <ctype.h> 1#include <ctype.h>
3#include <math.h> 2#include <math.h>
4#include <stdbool.h> 3#include <stdbool.h>
@@ -206,9 +205,17 @@ static void container_move_to_workspace(struct sway_container *container,
206 container_detach(container); 205 container_detach(container);
207 workspace_add_floating(workspace, container); 206 workspace_add_floating(workspace, container);
208 container_handle_fullscreen_reparent(container); 207 container_handle_fullscreen_reparent(container);
209 // If changing output, center it within the workspace 208 // If changing output, adjust the coordinates of the window.
210 if (old_output != workspace->output && !container->pending.fullscreen_mode) { 209 if (old_output != workspace->output && !container->pending.fullscreen_mode) {
211 container_floating_move_to_center(container); 210 struct wlr_box workspace_box, old_workspace_box;
211 workspace_get_box(workspace, &workspace_box);
212 workspace_get_box(old_workspace, &old_workspace_box);
213 floating_fix_coordinates(container, &old_workspace_box, &workspace_box);
214 if (container->scratchpad && workspace->output) {
215 struct wlr_box output_box;
216 output_get_box(workspace->output, &output_box);
217 container->transform = workspace_box;
218 }
212 } 219 }
213 } else { 220 } else {
214 container_detach(container); 221 container_detach(container);
@@ -462,7 +469,7 @@ static struct cmd_results *cmd_move_container(bool no_auto_back_and_forth,
462 if (strcasecmp(argv[1], "number") == 0) { 469 if (strcasecmp(argv[1], "number") == 0) {
463 // move [window|container] [to] "workspace number x" 470 // move [window|container] [to] "workspace number x"
464 if (argc < 3) { 471 if (argc < 3) {
465 return cmd_results_new(CMD_INVALID, expected_syntax); 472 return cmd_results_new(CMD_INVALID, "%s", expected_syntax);
466 } 473 }
467 if (!isdigit(argv[2][0])) { 474 if (!isdigit(argv[2][0])) {
468 return cmd_results_new(CMD_INVALID, 475 return cmd_results_new(CMD_INVALID,
@@ -522,7 +529,7 @@ static struct cmd_results *cmd_move_container(bool no_auto_back_and_forth,
522 } 529 }
523 destination = &dest_con->node; 530 destination = &dest_con->node;
524 } else { 531 } else {
525 return cmd_results_new(CMD_INVALID, expected_syntax); 532 return cmd_results_new(CMD_INVALID, "%s", expected_syntax);
526 } 533 }
527 534
528 if (destination->type == N_CONTAINER && 535 if (destination->type == N_CONTAINER &&
@@ -686,6 +693,9 @@ static struct cmd_results *cmd_move_workspace(int argc, char **argv) {
686 arrange_output(old_output); 693 arrange_output(old_output);
687 arrange_output(new_output); 694 arrange_output(new_output);
688 695
696 struct sway_seat *seat = config->handler_context.seat;
697 seat_consider_warp_to_focus(seat);
698
689 return cmd_results_new(CMD_SUCCESS, NULL); 699 return cmd_results_new(CMD_SUCCESS, NULL);
690} 700}
691 701
@@ -759,15 +769,6 @@ static struct cmd_results *cmd_move_in_direction(
759 ipc_event_window(container, "move"); 769 ipc_event_window(container, "move");
760 } 770 }
761 771
762 // Hack to re-focus container
763 seat_set_raw_focus(config->handler_context.seat, &new_ws->node);
764 seat_set_focus_container(config->handler_context.seat, container);
765
766 if (old_ws != new_ws) {
767 ipc_event_workspace(old_ws, new_ws, "focus");
768 workspace_detect_urgent(old_ws);
769 workspace_detect_urgent(new_ws);
770 }
771 container_end_mouse_operation(container); 772 container_end_mouse_operation(container);
772 773
773 return cmd_results_new(CMD_SUCCESS, NULL); 774 return cmd_results_new(CMD_SUCCESS, NULL);
@@ -788,15 +789,15 @@ static struct cmd_results *cmd_move_to_position_pointer(
788 struct wlr_output *output = wlr_output_layout_output_at( 789 struct wlr_output *output = wlr_output_layout_output_at(
789 root->output_layout, cursor->x, cursor->y); 790 root->output_layout, cursor->x, cursor->y);
790 if (output) { 791 if (output) {
791 struct wlr_box *box = 792 struct wlr_box box;
792 wlr_output_layout_get_box(root->output_layout, output); 793 wlr_output_layout_get_box(root->output_layout, output, &box);
793 lx = fmax(lx, box->x); 794 lx = fmax(lx, box.x);
794 ly = fmax(ly, box->y); 795 ly = fmax(ly, box.y);
795 if (lx + container->pending.width > box->x + box->width) { 796 if (lx + container->pending.width > box.x + box.width) {
796 lx = box->x + box->width - container->pending.width; 797 lx = box.x + box.width - container->pending.width;
797 } 798 }
798 if (ly + container->pending.height > box->y + box->height) { 799 if (ly + container->pending.height > box.y + box.height) {
799 ly = box->y + box->height - container->pending.height; 800 ly = box.y + box.height - container->pending.height;
800 } 801 }
801 } 802 }
802 803
@@ -818,7 +819,7 @@ static struct cmd_results *cmd_move_to_position(int argc, char **argv) {
818 } 819 }
819 820
820 if (!argc) { 821 if (!argc) {
821 return cmd_results_new(CMD_INVALID, expected_position_syntax); 822 return cmd_results_new(CMD_INVALID, "%s", expected_position_syntax);
822 } 823 }
823 824
824 bool absolute = false; 825 bool absolute = false;
@@ -828,19 +829,19 @@ static struct cmd_results *cmd_move_to_position(int argc, char **argv) {
828 ++argv; 829 ++argv;
829 } 830 }
830 if (!argc) { 831 if (!argc) {
831 return cmd_results_new(CMD_INVALID, expected_position_syntax); 832 return cmd_results_new(CMD_INVALID, "%s", expected_position_syntax);
832 } 833 }
833 if (strcmp(argv[0], "position") == 0) { 834 if (strcmp(argv[0], "position") == 0) {
834 --argc; 835 --argc;
835 ++argv; 836 ++argv;
836 } 837 }
837 if (!argc) { 838 if (!argc) {
838 return cmd_results_new(CMD_INVALID, expected_position_syntax); 839 return cmd_results_new(CMD_INVALID, "%s", expected_position_syntax);
839 } 840 }
840 if (strcmp(argv[0], "cursor") == 0 || strcmp(argv[0], "mouse") == 0 || 841 if (strcmp(argv[0], "cursor") == 0 || strcmp(argv[0], "mouse") == 0 ||
841 strcmp(argv[0], "pointer") == 0) { 842 strcmp(argv[0], "pointer") == 0) {
842 if (absolute) { 843 if (absolute) {
843 return cmd_results_new(CMD_INVALID, expected_position_syntax); 844 return cmd_results_new(CMD_INVALID, "%s", expected_position_syntax);
844 } 845 }
845 return cmd_move_to_position_pointer(container); 846 return cmd_move_to_position_pointer(container);
846 } else if (strcmp(argv[0], "center") == 0) { 847 } else if (strcmp(argv[0], "center") == 0) {
@@ -862,7 +863,7 @@ static struct cmd_results *cmd_move_to_position(int argc, char **argv) {
862 } 863 }
863 864
864 if (argc < 2) { 865 if (argc < 2) {
865 return cmd_results_new(CMD_FAILURE, expected_position_syntax); 866 return cmd_results_new(CMD_FAILURE, "%s", expected_position_syntax);
866 } 867 }
867 868
868 struct movement_amount lx = { .amount = 0, .unit = MOVEMENT_UNIT_INVALID }; 869 struct movement_amount lx = { .amount = 0, .unit = MOVEMENT_UNIT_INVALID };
@@ -874,13 +875,17 @@ static struct cmd_results *cmd_move_to_position(int argc, char **argv) {
874 return cmd_results_new(CMD_INVALID, "Invalid x position specified"); 875 return cmd_results_new(CMD_INVALID, "Invalid x position specified");
875 } 876 }
876 877
878 if (argc < 1) {
879 return cmd_results_new(CMD_FAILURE, "%s", expected_position_syntax);
880 }
881
877 struct movement_amount ly = { .amount = 0, .unit = MOVEMENT_UNIT_INVALID }; 882 struct movement_amount ly = { .amount = 0, .unit = MOVEMENT_UNIT_INVALID };
878 // Y direction 883 // Y direction
879 num_consumed_args = parse_movement_amount(argc, argv, &ly); 884 num_consumed_args = parse_movement_amount(argc, argv, &ly);
880 argc -= num_consumed_args; 885 argc -= num_consumed_args;
881 argv += num_consumed_args; 886 argv += num_consumed_args;
882 if (argc > 0) { 887 if (argc > 0) {
883 return cmd_results_new(CMD_INVALID, expected_position_syntax); 888 return cmd_results_new(CMD_INVALID, "%s", expected_position_syntax);
884 } 889 }
885 if (ly.unit == MOVEMENT_UNIT_INVALID) { 890 if (ly.unit == MOVEMENT_UNIT_INVALID) {
886 return cmd_results_new(CMD_INVALID, "Invalid y position specified"); 891 return cmd_results_new(CMD_INVALID, "Invalid y position specified");
@@ -1026,13 +1031,13 @@ struct cmd_results *cmd_move(int argc, char **argv) {
1026 } 1031 }
1027 1032
1028 if (!argc) { 1033 if (!argc) {
1029 return cmd_results_new(CMD_INVALID, expected_full_syntax); 1034 return cmd_results_new(CMD_INVALID, "%s", expected_full_syntax);
1030 } 1035 }
1031 1036
1032 // Only `move [window|container] [to] workspace` supports 1037 // Only `move [window|container] [to] workspace` supports
1033 // `--no-auto-back-and-forth` so treat others as invalid syntax 1038 // `--no-auto-back-and-forth` so treat others as invalid syntax
1034 if (no_auto_back_and_forth && strcasecmp(argv[0], "workspace") != 0) { 1039 if (no_auto_back_and_forth && strcasecmp(argv[0], "workspace") != 0) {
1035 return cmd_results_new(CMD_INVALID, expected_full_syntax); 1040 return cmd_results_new(CMD_INVALID, "%s", expected_full_syntax);
1036 } 1041 }
1037 1042
1038 if (strcasecmp(argv[0], "workspace") == 0 || 1043 if (strcasecmp(argv[0], "workspace") == 0 ||
@@ -1046,5 +1051,5 @@ struct cmd_results *cmd_move(int argc, char **argv) {
1046 strcasecmp(argv[1], "position") == 0)) { 1051 strcasecmp(argv[1], "position") == 0)) {
1047 return cmd_move_to_position(argc, argv); 1052 return cmd_move_to_position(argc, argv);
1048 } 1053 }
1049 return cmd_results_new(CMD_INVALID, expected_full_syntax); 1054 return cmd_results_new(CMD_INVALID, "%s", expected_full_syntax);
1050} 1055}
diff --git a/sway/commands/no_focus.c b/sway/commands/no_focus.c
index 2001e04f..ccfdec82 100644
--- a/sway/commands/no_focus.c
+++ b/sway/commands/no_focus.c
@@ -13,7 +13,7 @@ struct cmd_results *cmd_no_focus(int argc, char **argv) {
13 char *err_str = NULL; 13 char *err_str = NULL;
14 struct criteria *criteria = criteria_parse(argv[0], &err_str); 14 struct criteria *criteria = criteria_parse(argv[0], &err_str);
15 if (!criteria) { 15 if (!criteria) {
16 error = cmd_results_new(CMD_INVALID, err_str); 16 error = cmd_results_new(CMD_INVALID, "%s", err_str);
17 free(err_str); 17 free(err_str);
18 return error; 18 return error;
19 } 19 }
diff --git a/sway/commands/opacity.c b/sway/commands/opacity.c
index 96e6228e..610cecc6 100644
--- a/sway/commands/opacity.c
+++ b/sway/commands/opacity.c
@@ -37,6 +37,7 @@ struct cmd_results *cmd_opacity(int argc, char **argv) {
37 } 37 }
38 38
39 con->alpha = val; 39 con->alpha = val;
40 container_damage_whole(con); 40 container_update(con);
41
41 return cmd_results_new(CMD_SUCCESS, NULL); 42 return cmd_results_new(CMD_SUCCESS, NULL);
42} 43}
diff --git a/sway/commands/output.c b/sway/commands/output.c
index 4418f23f..5e5d31b3 100644
--- a/sway/commands/output.c
+++ b/sway/commands/output.c
@@ -15,8 +15,11 @@ static const struct cmd_handler output_handlers[] = {
15 { "enable", output_cmd_enable }, 15 { "enable", output_cmd_enable },
16 { "max_render_time", output_cmd_max_render_time }, 16 { "max_render_time", output_cmd_max_render_time },
17 { "mode", output_cmd_mode }, 17 { "mode", output_cmd_mode },
18 { "modeline", output_cmd_modeline },
18 { "pos", output_cmd_position }, 19 { "pos", output_cmd_position },
19 { "position", output_cmd_position }, 20 { "position", output_cmd_position },
21 { "power", output_cmd_power },
22 { "render_bit_depth", output_cmd_render_bit_depth },
20 { "res", output_cmd_mode }, 23 { "res", output_cmd_mode },
21 { "resolution", output_cmd_mode }, 24 { "resolution", output_cmd_mode },
22 { "scale", output_cmd_scale }, 25 { "scale", output_cmd_scale },
@@ -24,6 +27,7 @@ static const struct cmd_handler output_handlers[] = {
24 { "subpixel", output_cmd_subpixel }, 27 { "subpixel", output_cmd_subpixel },
25 { "toggle", output_cmd_toggle }, 28 { "toggle", output_cmd_toggle },
26 { "transform", output_cmd_transform }, 29 { "transform", output_cmd_transform },
30 { "unplug", output_cmd_unplug },
27}; 31};
28 32
29struct cmd_results *cmd_output(int argc, char **argv) { 33struct cmd_results *cmd_output(int argc, char **argv) {
@@ -32,9 +36,9 @@ struct cmd_results *cmd_output(int argc, char **argv) {
32 return error; 36 return error;
33 } 37 }
34 38
35 // The NOOP-1 output is a dummy output used when there's no outputs 39 // The HEADLESS-1 output is a dummy output used when there's no outputs
36 // connected. It should never be configured. 40 // connected. It should never be configured.
37 if (strcasecmp(argv[0], root->noop_output->wlr_output->name) == 0) { 41 if (strcasecmp(argv[0], root->fallback_output->wlr_output->name) == 0) {
38 return cmd_results_new(CMD_FAILURE, 42 return cmd_results_new(CMD_FAILURE,
39 "Refusing to configure the no op output"); 43 "Refusing to configure the no op output");
40 } 44 }
@@ -51,7 +55,7 @@ struct cmd_results *cmd_output(int argc, char **argv) {
51 if (!sway_output) { 55 if (!sway_output) {
52 return cmd_results_new(CMD_FAILURE, "Unknown output"); 56 return cmd_results_new(CMD_FAILURE, "Unknown output");
53 } 57 }
54 if (sway_output == root->noop_output) { 58 if (sway_output == root->fallback_output) {
55 return cmd_results_new(CMD_FAILURE, 59 return cmd_results_new(CMD_FAILURE,
56 "Refusing to configure the no op output"); 60 "Refusing to configure the no op output");
57 } 61 }
@@ -99,15 +103,18 @@ struct cmd_results *cmd_output(int argc, char **argv) {
99 103
100 bool background = output->background; 104 bool background = output->background;
101 105
102 output = store_output_config(output); 106 store_output_config(output);
103 107
104 // If reloading, the output configs will be applied after reading the 108 // If reloading, the output configs will be applied after reading the
105 // entire config and before the deferred commands so that an auto generated 109 // entire config and before the deferred commands so that an auto generated
106 // workspace name is not given to re-enabled outputs. 110 // workspace name is not given to re-enabled outputs.
107 if (!config->reloading && !config->validating) { 111 if (!config->reloading && !config->validating) {
108 apply_output_config_to_outputs(output); 112 apply_all_output_configs();
109 if (background) { 113 if (background) {
110 spawn_swaybg(); 114 if (!spawn_swaybg()) {
115 return cmd_results_new(CMD_FAILURE,
116 "Failed to apply background configuration");
117 }
111 } 118 }
112 } 119 }
113 120
diff --git a/sway/commands/output/background.c b/sway/commands/output/background.c
index 68ee9fe1..55bd7671 100644
--- a/sway/commands/output/background.c
+++ b/sway/commands/output/background.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <libgen.h> 1#include <libgen.h>
3#include <stdio.h> 2#include <stdio.h>
4#include <string.h> 3#include <string.h>
@@ -102,19 +101,19 @@ struct cmd_results *output_cmd_background(int argc, char **argv) {
102 } 101 }
103 102
104 char *conf_path = dirname(conf); 103 char *conf_path = dirname(conf);
105 char *rel_path = src; 104 char *real_src = malloc(strlen(conf_path) + strlen(src) + 2);
106 src = malloc(strlen(conf_path) + strlen(src) + 2); 105 if (!real_src) {
107 if (!src) { 106 free(src);
108 free(rel_path);
109 free(conf); 107 free(conf);
110 sway_log(SWAY_ERROR, "Unable to allocate memory"); 108 sway_log(SWAY_ERROR, "Unable to allocate memory");
111 return cmd_results_new(CMD_FAILURE, 109 return cmd_results_new(CMD_FAILURE,
112 "Unable to allocate resources"); 110 "Unable to allocate resources");
113 } 111 }
114 112
115 sprintf(src, "%s/%s", conf_path, rel_path); 113 snprintf(real_src, strlen(conf_path) + strlen(src) + 2, "%s/%s", conf_path, src);
116 free(rel_path); 114 free(src);
117 free(conf); 115 free(conf);
116 src = real_src;
118 } 117 }
119 118
120 bool can_access = access(src, F_OK) != -1; 119 bool can_access = access(src, F_OK) != -1;
@@ -123,7 +122,10 @@ struct cmd_results *output_cmd_background(int argc, char **argv) {
123 src); 122 src);
124 config_add_swaynag_warning("Unable to access background file '%s'", 123 config_add_swaynag_warning("Unable to access background file '%s'",
125 src); 124 src);
125 struct cmd_results *result = cmd_results_new(CMD_FAILURE,
126 "unable to access background file '%s'", src);
126 free(src); 127 free(src);
128 return result;
127 } else { 129 } else {
128 output->background = src; 130 output->background = src;
129 output->background_option = strdup(mode); 131 output->background_option = strdup(mode);
diff --git a/sway/commands/output/dpms.c b/sway/commands/output/dpms.c
index 638c0ade..c7adbd58 100644
--- a/sway/commands/output/dpms.c
+++ b/sway/commands/output/dpms.c
@@ -1,45 +1,8 @@
1#include "log.h"
1#include "sway/commands.h" 2#include "sway/commands.h"
2#include "sway/config.h"
3#include "sway/output.h"
4#include "util.h"
5#include <strings.h>
6 3
7struct cmd_results *output_cmd_dpms(int argc, char **argv) { 4struct cmd_results *output_cmd_dpms(int argc, char **argv) {
8 if (!config->handler_context.output_config) { 5 sway_log(SWAY_INFO, "The \"output dpms\" command is deprecated, "
9 return cmd_results_new(CMD_FAILURE, "Missing output config"); 6 "use \"output power\" instead");
10 } 7 return output_cmd_power(argc, argv);
11 if (!argc) {
12 return cmd_results_new(CMD_INVALID, "Missing dpms argument.");
13 }
14
15 enum config_dpms current_dpms = DPMS_ON;
16
17 if (strcasecmp(argv[0], "toggle") == 0) {
18
19 const char *oc_name = config->handler_context.output_config->name;
20 if (strcmp(oc_name, "*") == 0) {
21 return cmd_results_new(CMD_INVALID,
22 "Cannot apply toggle to all outputs.");
23 }
24
25 struct sway_output *sway_output = all_output_by_name_or_id(oc_name);
26 if (!sway_output || !sway_output->wlr_output) {
27 return cmd_results_new(CMD_FAILURE,
28 "Cannot apply toggle to unknown output %s", oc_name);
29 }
30
31 if (sway_output->enabled && !sway_output->wlr_output->enabled) {
32 current_dpms = DPMS_OFF;
33 }
34 }
35
36 if (parse_boolean(argv[0], current_dpms == DPMS_ON)) {
37 config->handler_context.output_config->dpms_state = DPMS_ON;
38 } else {
39 config->handler_context.output_config->dpms_state = DPMS_OFF;
40 }
41
42 config->handler_context.leftovers.argc = argc - 1;
43 config->handler_context.leftovers.argv = argv + 1;
44 return NULL;
45} 8}
diff --git a/sway/commands/output/mode.c b/sway/commands/output/mode.c
index 5b710713..019d625a 100644
--- a/sway/commands/output/mode.c
+++ b/sway/commands/output/mode.c
@@ -20,6 +20,9 @@ struct cmd_results *output_cmd_mode(int argc, char **argv) {
20 output->custom_mode = 0; 20 output->custom_mode = 0;
21 } 21 }
22 22
23 // Reset custom modeline, if any
24 output->drm_mode.type = 0;
25
23 char *end; 26 char *end;
24 output->width = strtol(*argv, &end, 10); 27 output->width = strtol(*argv, &end, 10);
25 if (*end) { 28 if (*end) {
@@ -58,3 +61,58 @@ struct cmd_results *output_cmd_mode(int argc, char **argv) {
58 return NULL; 61 return NULL;
59} 62}
60 63
64static bool parse_modeline(char **argv, drmModeModeInfo *mode) {
65 mode->type = DRM_MODE_TYPE_USERDEF;
66 mode->clock = strtof(argv[0], NULL) * 1000;
67 mode->hdisplay = strtol(argv[1], NULL, 10);
68 mode->hsync_start = strtol(argv[2], NULL, 10);
69 mode->hsync_end = strtol(argv[3], NULL, 10);
70 mode->htotal = strtol(argv[4], NULL, 10);
71 mode->vdisplay = strtol(argv[5], NULL, 10);
72 mode->vsync_start = strtol(argv[6], NULL, 10);
73 mode->vsync_end = strtol(argv[7], NULL, 10);
74 mode->vtotal = strtol(argv[8], NULL, 10);
75
76 mode->vrefresh = mode->clock * 1000.0 * 1000.0
77 / mode->htotal / mode->vtotal;
78 if (strcasecmp(argv[9], "+hsync") == 0) {
79 mode->flags |= DRM_MODE_FLAG_PHSYNC;
80 } else if (strcasecmp(argv[9], "-hsync") == 0) {
81 mode->flags |= DRM_MODE_FLAG_NHSYNC;
82 } else {
83 return false;
84 }
85
86 if (strcasecmp(argv[10], "+vsync") == 0) {
87 mode->flags |= DRM_MODE_FLAG_PVSYNC;
88 } else if (strcasecmp(argv[10], "-vsync") == 0) {
89 mode->flags |= DRM_MODE_FLAG_NVSYNC;
90 } else {
91 return false;
92 }
93
94 snprintf(mode->name, sizeof(mode->name), "%dx%d@%d",
95 mode->hdisplay, mode->vdisplay, mode->vrefresh / 1000);
96
97 return true;
98}
99
100struct cmd_results *output_cmd_modeline(int argc, char **argv) {
101 if (!config->handler_context.output_config) {
102 return cmd_results_new(CMD_FAILURE, "Missing output config");
103 }
104 if (!argc) {
105 return cmd_results_new(CMD_INVALID, "Missing modeline argument.");
106 }
107
108 struct output_config *output = config->handler_context.output_config;
109
110 if (argc != 11 || !parse_modeline(argv, &output->drm_mode)) {
111 return cmd_results_new(CMD_INVALID, "Invalid modeline");
112 }
113
114 config->handler_context.leftovers.argc = argc - 12;
115 config->handler_context.leftovers.argv = argv + 12;
116 return NULL;
117}
118
diff --git a/sway/commands/output/power.c b/sway/commands/output/power.c
new file mode 100644
index 00000000..e6ae2852
--- /dev/null
+++ b/sway/commands/output/power.c
@@ -0,0 +1,43 @@
1#include <strings.h>
2#include "sway/commands.h"
3#include "sway/config.h"
4#include "sway/output.h"
5#include "util.h"
6
7struct cmd_results *output_cmd_power(int argc, char **argv) {
8 if (!config->handler_context.output_config) {
9 return cmd_results_new(CMD_FAILURE, "Missing output config");
10 }
11 if (argc == 0) {
12 return cmd_results_new(CMD_INVALID, "Missing power argument");
13 }
14
15 bool current = true;
16 if (strcasecmp(argv[0], "toggle") == 0) {
17 const char *oc_name = config->handler_context.output_config->name;
18 if (strcmp(oc_name, "*") == 0) {
19 return cmd_results_new(CMD_INVALID,
20 "Cannot apply toggle to all outputs");
21 }
22
23 struct sway_output *sway_output = all_output_by_name_or_id(oc_name);
24 if (!sway_output || !sway_output->wlr_output) {
25 return cmd_results_new(CMD_FAILURE,
26 "Cannot apply toggle to unknown output %s", oc_name);
27 }
28
29 if (sway_output->enabled && !sway_output->wlr_output->enabled) {
30 current = false;
31 }
32 }
33
34 if (parse_boolean(argv[0], current)) {
35 config->handler_context.output_config->power = 1;
36 } else {
37 config->handler_context.output_config->power = 0;
38 }
39
40 config->handler_context.leftovers.argc = argc - 1;
41 config->handler_context.leftovers.argv = argv + 1;
42 return NULL;
43}
diff --git a/sway/commands/output/render_bit_depth.c b/sway/commands/output/render_bit_depth.c
new file mode 100644
index 00000000..c419321e
--- /dev/null
+++ b/sway/commands/output/render_bit_depth.c
@@ -0,0 +1,29 @@
1#include <drm_fourcc.h>
2#include <strings.h>
3#include "sway/commands.h"
4#include "sway/config.h"
5
6struct cmd_results *output_cmd_render_bit_depth(int argc, char **argv) {
7 if (!config->handler_context.output_config) {
8 return cmd_results_new(CMD_FAILURE, "Missing output config");
9 }
10 if (!argc) {
11 return cmd_results_new(CMD_INVALID, "Missing bit depth argument.");
12 }
13
14 if (strcmp(*argv, "8") == 0) {
15 config->handler_context.output_config->render_bit_depth =
16 RENDER_BIT_DEPTH_8;
17 } else if (strcmp(*argv, "10") == 0) {
18 config->handler_context.output_config->render_bit_depth =
19 RENDER_BIT_DEPTH_10;
20 } else {
21 return cmd_results_new(CMD_INVALID,
22 "Invalid bit depth. Must be a value in (8|10).");
23 }
24
25 config->handler_context.leftovers.argc = argc - 1;
26 config->handler_context.leftovers.argv = argv + 1;
27 return NULL;
28}
29
diff --git a/sway/commands/output/toggle.c b/sway/commands/output/toggle.c
index 6342d526..c6b72845 100644
--- a/sway/commands/output/toggle.c
+++ b/sway/commands/output/toggle.c
@@ -29,7 +29,7 @@ struct cmd_results *output_cmd_toggle(int argc, char **argv) {
29 config->handler_context.output_config->enabled = 1; 29 config->handler_context.output_config->enabled = 1;
30 } 30 }
31 31
32 free(oc); 32 free_output_config(oc);
33 config->handler_context.leftovers.argc = argc; 33 config->handler_context.leftovers.argc = argc;
34 config->handler_context.leftovers.argv = argv; 34 config->handler_context.leftovers.argv = argv;
35 return NULL; 35 return NULL;
diff --git a/sway/commands/output/transform.c b/sway/commands/output/transform.c
index f4fcc8c9..8db71bb3 100644
--- a/sway/commands/output/transform.c
+++ b/sway/commands/output/transform.c
@@ -1,4 +1,5 @@
1#include <string.h> 1#include <string.h>
2#include <wlr/util/transform.h>
2#include "sway/commands.h" 3#include "sway/commands.h"
3#include "sway/config.h" 4#include "sway/config.h"
4#include "log.h" 5#include "log.h"
diff --git a/sway/commands/output/unplug.c b/sway/commands/output/unplug.c
new file mode 100644
index 00000000..dfef626f
--- /dev/null
+++ b/sway/commands/output/unplug.c
@@ -0,0 +1,54 @@
1#include <strings.h>
2#include <wlr/config.h>
3#include <wlr/backend/headless.h>
4#include <wlr/backend/wayland.h>
5#if WLR_HAS_X11_BACKEND
6#include <wlr/backend/x11.h>
7#endif
8#include "sway/commands.h"
9#include "sway/config.h"
10#include "sway/output.h"
11
12static bool is_backend_allowed(struct wlr_backend *backend) {
13 if (wlr_backend_is_headless(backend)) {
14 return true;
15 }
16 if (wlr_backend_is_wl(backend)) {
17 return true;
18 }
19#if WLR_HAS_X11_BACKEND
20 if (wlr_backend_is_x11(backend)) {
21 return true;
22 }
23#endif
24 return false;
25}
26
27/**
28 * This command is intended for developer use only.
29 */
30struct cmd_results *output_cmd_unplug(int argc, char **argv) {
31 if (!config->handler_context.output_config) {
32 return cmd_results_new(CMD_FAILURE, "Missing output config");
33 }
34
35 const char *oc_name = config->handler_context.output_config->name;
36 if (strcmp(oc_name, "*") == 0) {
37 return cmd_results_new(CMD_INVALID, "Won't unplug all outputs");
38 }
39
40 struct sway_output *sway_output = all_output_by_name_or_id(oc_name);
41 if (!sway_output) {
42 return cmd_results_new(CMD_INVALID,
43 "Cannot unplug unknown output %s", oc_name);
44 }
45
46 if (!is_backend_allowed(sway_output->wlr_output->backend)) {
47 return cmd_results_new(CMD_INVALID,
48 "Can only unplug outputs with headless, wayland or x11 backend");
49 }
50
51 wlr_output_destroy(sway_output->wlr_output);
52
53 return cmd_results_new(CMD_SUCCESS, NULL);
54}
diff --git a/sway/commands/primary_selection.c b/sway/commands/primary_selection.c
new file mode 100644
index 00000000..9e2689c2
--- /dev/null
+++ b/sway/commands/primary_selection.c
@@ -0,0 +1,25 @@
1#include <string.h>
2#include <strings.h>
3#include "sway/config.h"
4#include "sway/commands.h"
5#include "util.h"
6
7struct cmd_results *cmd_primary_selection(int argc, char **argv) {
8 struct cmd_results *error = NULL;
9 if ((error = checkarg(argc, "primary_selection", EXPECTED_EQUAL_TO, 1))) {
10 return error;
11 }
12
13 bool primary_selection = parse_boolean(argv[0], true);
14
15 // config->primary_selection is reset to the previous value on reload in
16 // load_main_config()
17 if (config->reloading && config->primary_selection != primary_selection) {
18 return cmd_results_new(CMD_FAILURE,
19 "primary_selection can only be enabled/disabled at launch");
20 }
21
22 config->primary_selection = primary_selection;
23
24 return cmd_results_new(CMD_SUCCESS, NULL);
25}
diff --git a/sway/commands/reload.c b/sway/commands/reload.c
index 3c994d54..6c0aac26 100644
--- a/sway/commands/reload.c
+++ b/sway/commands/reload.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <string.h> 1#include <string.h>
3#include "sway/commands.h" 2#include "sway/commands.h"
4#include "sway/config.h" 3#include "sway/config.h"
@@ -9,9 +8,8 @@
9#include "list.h" 8#include "list.h"
10#include "log.h" 9#include "log.h"
11 10
12static void rebuild_textures_iterator(struct sway_container *con, void *data) { 11static void title_bar_update_iterator(struct sway_container *con, void *data) {
13 container_update_marks_textures(con); 12 container_update_title_bar(con);
14 container_update_title_textures(con);
15} 13}
16 14
17static void do_reload(void *data) { 15static void do_reload(void *data) {
@@ -48,8 +46,7 @@ static void do_reload(void *data) {
48 } 46 }
49 list_free_items_and_destroy(bar_ids); 47 list_free_items_and_destroy(bar_ids);
50 48
51 config_update_font_height(true); 49 root_for_each_container(title_bar_update_iterator, NULL);
52 root_for_each_container(rebuild_textures_iterator, NULL);
53 50
54 arrange_root(); 51 arrange_root();
55} 52}
diff --git a/sway/commands/rename.c b/sway/commands/rename.c
index 3b855fdf..0d36cc21 100644
--- a/sway/commands/rename.c
+++ b/sway/commands/rename.c
@@ -7,6 +7,7 @@
7#include "sway/config.h" 7#include "sway/config.h"
8#include "sway/ipc-server.h" 8#include "sway/ipc-server.h"
9#include "sway/output.h" 9#include "sway/output.h"
10#include "sway/desktop/launcher.h"
10#include "sway/tree/container.h" 11#include "sway/tree/container.h"
11#include "sway/tree/workspace.h" 12#include "sway/tree/workspace.h"
12#include "sway/tree/root.h" 13#include "sway/tree/root.h"
@@ -25,7 +26,7 @@ struct cmd_results *cmd_rename(int argc, char **argv) {
25 "Can't run this command while there's no outputs connected."); 26 "Can't run this command while there's no outputs connected.");
26 } 27 }
27 if (strcasecmp(argv[0], "workspace") != 0) { 28 if (strcasecmp(argv[0], "workspace") != 0) {
28 return cmd_results_new(CMD_INVALID, expected_syntax); 29 return cmd_results_new(CMD_INVALID, "%s", expected_syntax);
29 } 30 }
30 31
31 int argn = 1; 32 int argn = 1;
@@ -64,7 +65,7 @@ struct cmd_results *cmd_rename(int argc, char **argv) {
64 ++argn; // move past "to" 65 ++argn; // move past "to"
65 66
66 if (argn >= argc) { 67 if (argn >= argc) {
67 return cmd_results_new(CMD_INVALID, expected_syntax); 68 return cmd_results_new(CMD_INVALID, "%s", expected_syntax);
68 } 69 }
69 70
70 char *new_name = join_args(argv + argn, argc - argn); 71 char *new_name = join_args(argv + argn, argc - argn);
@@ -91,8 +92,6 @@ struct cmd_results *cmd_rename(int argc, char **argv) {
91 92
92 sway_log(SWAY_DEBUG, "renaming workspace '%s' to '%s'", workspace->name, new_name); 93 sway_log(SWAY_DEBUG, "renaming workspace '%s' to '%s'", workspace->name, new_name);
93 94
94 root_rename_pid_workspaces(workspace->name, new_name);
95
96 free(workspace->name); 95 free(workspace->name);
97 workspace->name = new_name; 96 workspace->name = new_name;
98 97
diff --git a/sway/commands/resize.c b/sway/commands/resize.c
index 425069de..32b746ea 100644
--- a/sway/commands/resize.c
+++ b/sway/commands/resize.c
@@ -75,6 +75,10 @@ void container_resize_tiled(struct sway_container *con,
75 return; 75 return;
76 } 76 }
77 77
78 if (container_is_scratchpad_hidden_or_child(con)) {
79 return;
80 }
81
78 // For HORIZONTAL or VERTICAL, we are growing in two directions so select 82 // For HORIZONTAL or VERTICAL, we are growing in two directions so select
79 // both adjacent siblings. For RIGHT or DOWN, just select the next sibling. 83 // both adjacent siblings. For RIGHT or DOWN, just select the next sibling.
80 // For LEFT or UP, convert it to a RIGHT or DOWN resize and reassign con to 84 // For LEFT or UP, convert it to a RIGHT or DOWN resize and reassign con to
@@ -249,16 +253,35 @@ static struct cmd_results *resize_adjust_tiled(uint32_t axis,
249 struct movement_amount *amount) { 253 struct movement_amount *amount) {
250 struct sway_container *current = config->handler_context.container; 254 struct sway_container *current = config->handler_context.container;
251 255
256 if (container_is_scratchpad_hidden_or_child(current)) {
257 return cmd_results_new(CMD_FAILURE, "Cannot resize a hidden scratchpad container");
258 }
259
252 if (amount->unit == MOVEMENT_UNIT_DEFAULT) { 260 if (amount->unit == MOVEMENT_UNIT_DEFAULT) {
253 amount->unit = MOVEMENT_UNIT_PPT; 261 amount->unit = MOVEMENT_UNIT_PPT;
254 } 262 }
255 if (amount->unit == MOVEMENT_UNIT_PPT) { 263 if (amount->unit == MOVEMENT_UNIT_PPT) {
264 struct sway_container *parent = current->pending.parent;
256 float pct = amount->amount / 100.0f; 265 float pct = amount->amount / 100.0f;
257 266
258 if (is_horizontal(axis)) { 267 if (is_horizontal(axis)) {
259 amount->amount = (float)current->pending.width * pct; 268 while (parent && parent->pending.layout != L_HORIZ) {
269 parent = parent->pending.parent;
270 }
271 if (parent) {
272 amount->amount = (float)parent->pending.width * pct;
273 } else {
274 amount->amount = (float)current->pending.workspace->width * pct;
275 }
260 } else { 276 } else {
261 amount->amount = (float)current->pending.height * pct; 277 while (parent && parent->pending.layout != L_VERT) {
278 parent = parent->pending.parent;
279 }
280 if (parent) {
281 amount->amount = (float)parent->pending.height * pct;
282 } else {
283 amount->amount = (float)current->pending.workspace->height * pct;
284 }
262 } 285 }
263 } 286 }
264 287
@@ -277,6 +300,11 @@ static struct cmd_results *resize_adjust_tiled(uint32_t axis,
277 */ 300 */
278static struct cmd_results *resize_set_tiled(struct sway_container *con, 301static struct cmd_results *resize_set_tiled(struct sway_container *con,
279 struct movement_amount *width, struct movement_amount *height) { 302 struct movement_amount *width, struct movement_amount *height) {
303
304 if (container_is_scratchpad_hidden_or_child(con)) {
305 return cmd_results_new(CMD_FAILURE, "Cannot resize a hidden scratchpad container");
306 }
307
280 if (width->amount) { 308 if (width->amount) {
281 if (width->unit == MOVEMENT_UNIT_PPT || 309 if (width->unit == MOVEMENT_UNIT_PPT ||
282 width->unit == MOVEMENT_UNIT_DEFAULT) { 310 width->unit == MOVEMENT_UNIT_DEFAULT) {
@@ -415,7 +443,7 @@ static struct cmd_results *cmd_resize_set(int argc, char **argv) {
415 argc -= num_consumed_args; 443 argc -= num_consumed_args;
416 argv += num_consumed_args; 444 argv += num_consumed_args;
417 if (width.unit == MOVEMENT_UNIT_INVALID) { 445 if (width.unit == MOVEMENT_UNIT_INVALID) {
418 return cmd_results_new(CMD_INVALID, usage); 446 return cmd_results_new(CMD_INVALID, "%s", usage);
419 } 447 }
420 } 448 }
421 449
@@ -427,10 +455,10 @@ static struct cmd_results *cmd_resize_set(int argc, char **argv) {
427 } 455 }
428 int num_consumed_args = parse_movement_amount(argc, argv, &height); 456 int num_consumed_args = parse_movement_amount(argc, argv, &height);
429 if (argc > num_consumed_args) { 457 if (argc > num_consumed_args) {
430 return cmd_results_new(CMD_INVALID, usage); 458 return cmd_results_new(CMD_INVALID, "%s", usage);
431 } 459 }
432 if (width.unit == MOVEMENT_UNIT_INVALID) { 460 if (width.unit == MOVEMENT_UNIT_INVALID) {
433 return cmd_results_new(CMD_INVALID, usage); 461 return cmd_results_new(CMD_INVALID, "%s", usage);
434 } 462 }
435 } 463 }
436 464
@@ -462,7 +490,7 @@ static struct cmd_results *cmd_resize_adjust(int argc, char **argv,
462 "[<amount> px|ppt [or <amount> px|ppt]]'"; 490 "[<amount> px|ppt [or <amount> px|ppt]]'";
463 uint32_t axis = parse_resize_axis(*argv); 491 uint32_t axis = parse_resize_axis(*argv);
464 if (axis == WLR_EDGE_NONE) { 492 if (axis == WLR_EDGE_NONE) {
465 return cmd_results_new(CMD_INVALID, usage); 493 return cmd_results_new(CMD_INVALID, "%s", usage);
466 } 494 }
467 --argc; ++argv; 495 --argc; ++argv;
468 496
@@ -473,7 +501,7 @@ static struct cmd_results *cmd_resize_adjust(int argc, char **argv,
473 argc -= num_consumed_args; 501 argc -= num_consumed_args;
474 argv += num_consumed_args; 502 argv += num_consumed_args;
475 if (first_amount.unit == MOVEMENT_UNIT_INVALID) { 503 if (first_amount.unit == MOVEMENT_UNIT_INVALID) {
476 return cmd_results_new(CMD_INVALID, usage); 504 return cmd_results_new(CMD_INVALID, "%s", usage);
477 } 505 }
478 } else { 506 } else {
479 first_amount.amount = 10; 507 first_amount.amount = 10;
@@ -483,7 +511,7 @@ static struct cmd_results *cmd_resize_adjust(int argc, char **argv,
483 // "or" 511 // "or"
484 if (argc) { 512 if (argc) {
485 if (strcmp(*argv, "or") != 0) { 513 if (strcmp(*argv, "or") != 0) {
486 return cmd_results_new(CMD_INVALID, usage); 514 return cmd_results_new(CMD_INVALID, "%s", usage);
487 } 515 }
488 --argc; ++argv; 516 --argc; ++argv;
489 } 517 }
@@ -493,10 +521,10 @@ static struct cmd_results *cmd_resize_adjust(int argc, char **argv,
493 if (argc) { 521 if (argc) {
494 int num_consumed_args = parse_movement_amount(argc, argv, &second_amount); 522 int num_consumed_args = parse_movement_amount(argc, argv, &second_amount);
495 if (argc > num_consumed_args) { 523 if (argc > num_consumed_args) {
496 return cmd_results_new(CMD_INVALID, usage); 524 return cmd_results_new(CMD_INVALID, "%s", usage);
497 } 525 }
498 if (second_amount.unit == MOVEMENT_UNIT_INVALID) { 526 if (second_amount.unit == MOVEMENT_UNIT_INVALID) {
499 return cmd_results_new(CMD_INVALID, usage); 527 return cmd_results_new(CMD_INVALID, "%s", usage);
500 } 528 }
501 } else { 529 } else {
502 second_amount.amount = 0; 530 second_amount.amount = 0;
@@ -566,5 +594,5 @@ struct cmd_results *cmd_resize(int argc, char **argv) {
566 const char usage[] = "Expected 'resize <shrink|grow> " 594 const char usage[] = "Expected 'resize <shrink|grow> "
567 "<width|height|up|down|left|right> [<amount>] [px|ppt]'"; 595 "<width|height|up|down|left|right> [<amount>] [px|ppt]'";
568 596
569 return cmd_results_new(CMD_INVALID, usage); 597 return cmd_results_new(CMD_INVALID, "%s", usage);
570} 598}
diff --git a/sway/commands/seat/attach.c b/sway/commands/seat/attach.c
index 00bfdab6..47d18546 100644
--- a/sway/commands/seat/attach.c
+++ b/sway/commands/seat/attach.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <string.h> 1#include <string.h>
3#include "sway/commands.h" 2#include "sway/commands.h"
4#include "sway/config.h" 3#include "sway/config.h"
diff --git a/sway/commands/seat/cursor.c b/sway/commands/seat/cursor.c
index 749235eb..df7c379d 100644
--- a/sway/commands/seat/cursor.c
+++ b/sway/commands/seat/cursor.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <linux/input-event-codes.h> 1#include <linux/input-event-codes.h>
3 2
4#include <strings.h> 3#include <strings.h>
@@ -18,7 +17,7 @@ static struct cmd_results *handle_command(struct sway_cursor *cursor,
18 int argc, char **argv) { 17 int argc, char **argv) {
19 if (strcasecmp(argv[0], "move") == 0) { 18 if (strcasecmp(argv[0], "move") == 0) {
20 if (argc < 3) { 19 if (argc < 3) {
21 return cmd_results_new(CMD_INVALID, expected_syntax); 20 return cmd_results_new(CMD_INVALID, "%s", expected_syntax);
22 } 21 }
23 int delta_x = strtol(argv[1], NULL, 10); 22 int delta_x = strtol(argv[1], NULL, 10);
24 int delta_y = strtol(argv[2], NULL, 10); 23 int delta_y = strtol(argv[2], NULL, 10);
@@ -27,7 +26,7 @@ static struct cmd_results *handle_command(struct sway_cursor *cursor,
27 wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat); 26 wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat);
28 } else if (strcasecmp(argv[0], "set") == 0) { 27 } else if (strcasecmp(argv[0], "set") == 0) {
29 if (argc < 3) { 28 if (argc < 3) {
30 return cmd_results_new(CMD_INVALID, expected_syntax); 29 return cmd_results_new(CMD_INVALID, "%s", expected_syntax);
31 } 30 }
32 // map absolute coords (0..1,0..1) to root container coords 31 // map absolute coords (0..1,0..1) to root container coords
33 float x = strtof(argv[1], NULL) / root->width; 32 float x = strtof(argv[1], NULL) / root->width;
@@ -37,7 +36,7 @@ static struct cmd_results *handle_command(struct sway_cursor *cursor,
37 wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat); 36 wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat);
38 } else { 37 } else {
39 if (argc < 2) { 38 if (argc < 2) {
40 return cmd_results_new(CMD_INVALID, expected_syntax); 39 return cmd_results_new(CMD_INVALID, "%s", expected_syntax);
41 } 40 }
42 struct cmd_results *error = NULL; 41 struct cmd_results *error = NULL;
43 if ((error = press_or_release(cursor, argv[0], argv[1]))) { 42 if ((error = press_or_release(cursor, argv[0], argv[1]))) {
@@ -85,36 +84,36 @@ struct cmd_results *seat_cmd_cursor(int argc, char **argv) {
85 84
86static struct cmd_results *press_or_release(struct sway_cursor *cursor, 85static struct cmd_results *press_or_release(struct sway_cursor *cursor,
87 char *action, char *button_str) { 86 char *action, char *button_str) {
88 enum wlr_button_state state; 87 enum wl_pointer_button_state state;
89 uint32_t button; 88 uint32_t button;
90 if (strcasecmp(action, "press") == 0) { 89 if (strcasecmp(action, "press") == 0) {
91 state = WLR_BUTTON_PRESSED; 90 state = WL_POINTER_BUTTON_STATE_PRESSED;
92 } else if (strcasecmp(action, "release") == 0) { 91 } else if (strcasecmp(action, "release") == 0) {
93 state = WLR_BUTTON_RELEASED; 92 state = WL_POINTER_BUTTON_STATE_RELEASED;
94 } else { 93 } else {
95 return cmd_results_new(CMD_INVALID, expected_syntax); 94 return cmd_results_new(CMD_INVALID, "%s", expected_syntax);
96 } 95 }
97 96
98 char *message = NULL; 97 char *message = NULL;
99 button = get_mouse_button(button_str, &message); 98 button = get_mouse_button(button_str, &message);
100 if (message) { 99 if (message) {
101 struct cmd_results *error = 100 struct cmd_results *error =
102 cmd_results_new(CMD_INVALID, message); 101 cmd_results_new(CMD_INVALID, "%s", message);
103 free(message); 102 free(message);
104 return error; 103 return error;
105 } else if (button == SWAY_SCROLL_UP || button == SWAY_SCROLL_DOWN 104 } else if (button == SWAY_SCROLL_UP || button == SWAY_SCROLL_DOWN
106 || button == SWAY_SCROLL_LEFT || button == SWAY_SCROLL_RIGHT) { 105 || button == SWAY_SCROLL_LEFT || button == SWAY_SCROLL_RIGHT) {
107 // Dispatch axis event 106 // Dispatch axis event
108 enum wlr_axis_orientation orientation = 107 enum wl_pointer_axis orientation =
109 (button == SWAY_SCROLL_UP || button == SWAY_SCROLL_DOWN) 108 (button == SWAY_SCROLL_UP || button == SWAY_SCROLL_DOWN)
110 ? WLR_AXIS_ORIENTATION_VERTICAL 109 ? WL_POINTER_AXIS_VERTICAL_SCROLL
111 : WLR_AXIS_ORIENTATION_HORIZONTAL; 110 : WL_POINTER_AXIS_HORIZONTAL_SCROLL;
112 double delta = (button == SWAY_SCROLL_UP || button == SWAY_SCROLL_LEFT) 111 double delta = (button == SWAY_SCROLL_UP || button == SWAY_SCROLL_LEFT)
113 ? -1 : 1; 112 ? -1 : 1;
114 struct wlr_event_pointer_axis event = { 113 struct wlr_pointer_axis_event event = {
115 .device = NULL, 114 .pointer = NULL,
116 .time_msec = 0, 115 .time_msec = 0,
117 .source = WLR_AXIS_SOURCE_WHEEL, 116 .source = WL_POINTER_AXIS_SOURCE_WHEEL,
118 .orientation = orientation, 117 .orientation = orientation,
119 .delta = delta * 15, 118 .delta = delta * 15,
120 .delta_discrete = delta 119 .delta_discrete = delta
diff --git a/sway/commands/seat/hide_cursor.c b/sway/commands/seat/hide_cursor.c
index e09b82d9..f5177a47 100644
--- a/sway/commands/seat/hide_cursor.c
+++ b/sway/commands/seat/hide_cursor.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <string.h> 1#include <string.h>
3#include "sway/commands.h" 2#include "sway/commands.h"
4#include "sway/config.h" 3#include "sway/config.h"
diff --git a/sway/commands/seat/idle.c b/sway/commands/seat/idle.c
index 82428f2c..2974453e 100644
--- a/sway/commands/seat/idle.c
+++ b/sway/commands/seat/idle.c
@@ -1,8 +1,8 @@
1#define _POSIX_C_SOURCE 200809L
2#include <limits.h> 1#include <limits.h>
3#include <string.h> 2#include <string.h>
4#include <strings.h> 3#include <strings.h>
5#include <stdint.h> 4#include <stdint.h>
5#include "log.h"
6#include "sway/commands.h" 6#include "sway/commands.h"
7#include "sway/config.h" 7#include "sway/config.h"
8#include "sway/input/seat.h" 8#include "sway/input/seat.h"
@@ -69,5 +69,10 @@ struct cmd_results *seat_cmd_idle_wake(int argc, char **argv) {
69 return cmd_results_new(CMD_FAILURE, "Invalid idle source"); 69 return cmd_results_new(CMD_FAILURE, "Invalid idle source");
70 } 70 }
71 config->handler_context.seat_config->idle_wake_sources = sources; 71 config->handler_context.seat_config->idle_wake_sources = sources;
72 sway_log(SWAY_INFO, "Warning: seat idle_wake is deprecated");
73 if (config->reading) {
74 config_add_swaynag_warning("seat idle_wake is deprecated. "
75 "Only seat idle_inhibit is supported.");
76 }
72 return cmd_results_new(CMD_SUCCESS, NULL); 77 return cmd_results_new(CMD_SUCCESS, NULL);
73} 78}
diff --git a/sway/commands/seat/xcursor_theme.c b/sway/commands/seat/xcursor_theme.c
index 202f35b9..61322a57 100644
--- a/sway/commands/seat/xcursor_theme.c
+++ b/sway/commands/seat/xcursor_theme.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <string.h> 1#include <string.h>
3#include "sway/commands.h" 2#include "sway/commands.h"
4#include "sway/config.h" 3#include "sway/config.h"
diff --git a/sway/commands/set.c b/sway/commands/set.c
index c539e9fc..ba384c7c 100644
--- a/sway/commands/set.c
+++ b/sway/commands/set.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <stdio.h> 1#include <stdio.h>
3#include <string.h> 2#include <string.h>
4#include <strings.h> 3#include <strings.h>
diff --git a/sway/commands/show_marks.c b/sway/commands/show_marks.c
index 0d373b80..60cef9fa 100644
--- a/sway/commands/show_marks.c
+++ b/sway/commands/show_marks.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <string.h> 1#include <string.h>
3#include "sway/commands.h" 2#include "sway/commands.h"
4#include "sway/config.h" 3#include "sway/config.h"
@@ -10,8 +9,8 @@
10#include "stringop.h" 9#include "stringop.h"
11#include "util.h" 10#include "util.h"
12 11
13static void rebuild_marks_iterator(struct sway_container *con, void *data) { 12static void title_bar_update_iterator(struct sway_container *con, void *data) {
14 container_update_marks_textures(con); 13 container_update_marks(con);
15} 14}
16 15
17struct cmd_results *cmd_show_marks(int argc, char **argv) { 16struct cmd_results *cmd_show_marks(int argc, char **argv) {
@@ -23,12 +22,7 @@ struct cmd_results *cmd_show_marks(int argc, char **argv) {
23 config->show_marks = parse_boolean(argv[0], config->show_marks); 22 config->show_marks = parse_boolean(argv[0], config->show_marks);
24 23
25 if (config->show_marks) { 24 if (config->show_marks) {
26 root_for_each_container(rebuild_marks_iterator, NULL); 25 root_for_each_container(title_bar_update_iterator, NULL);
27 }
28
29 for (int i = 0; i < root->outputs->length; ++i) {
30 struct sway_output *output = root->outputs->items[i];
31 output_damage_whole(output);
32 } 26 }
33 27
34 return cmd_results_new(CMD_SUCCESS, NULL); 28 return cmd_results_new(CMD_SUCCESS, NULL);
diff --git a/sway/commands/smart_gaps.c b/sway/commands/smart_gaps.c
index b27f9ccd..a6d165dc 100644
--- a/sway/commands/smart_gaps.c
+++ b/sway/commands/smart_gaps.c
@@ -15,7 +15,12 @@ struct cmd_results *cmd_smart_gaps(int argc, char **argv) {
15 return error; 15 return error;
16 } 16 }
17 17
18 config->smart_gaps = parse_boolean(argv[0], config->smart_gaps); 18 if (strcmp(argv[0], "inverse_outer") == 0) {
19 config->smart_gaps = SMART_GAPS_INVERSE_OUTER;
20 } else {
21 config->smart_gaps = parse_boolean(argv[0], config->smart_gaps)
22 ? SMART_GAPS_ON : SMART_GAPS_OFF;
23 }
19 24
20 arrange_root(); 25 arrange_root();
21 26
diff --git a/sway/commands/split.c b/sway/commands/split.c
index 3e25c6f7..500a497d 100644
--- a/sway/commands/split.c
+++ b/sway/commands/split.c
@@ -32,6 +32,24 @@ static struct cmd_results *do_split(int layout) {
32 return cmd_results_new(CMD_SUCCESS, NULL); 32 return cmd_results_new(CMD_SUCCESS, NULL);
33} 33}
34 34
35static struct cmd_results *do_unsplit(void) {
36 struct sway_container *con = config->handler_context.container;
37 struct sway_workspace *ws = config->handler_context.workspace;
38
39 if (con && con->pending.parent && con->pending.parent->pending.children->length == 1) {
40 container_flatten(con->pending.parent);
41 } else {
42 return cmd_results_new(CMD_FAILURE, "Can only flatten a child container with no siblings");
43 }
44
45 if (root->fullscreen_global) {
46 arrange_root();
47 } else {
48 arrange_workspace(ws);
49 }
50 return cmd_results_new(CMD_SUCCESS, NULL);
51}
52
35struct cmd_results *cmd_split(int argc, char **argv) { 53struct cmd_results *cmd_split(int argc, char **argv) {
36 struct cmd_results *error = NULL; 54 struct cmd_results *error = NULL;
37 if ((error = checkarg(argc, "split", EXPECTED_EQUAL_TO, 1))) { 55 if ((error = checkarg(argc, "split", EXPECTED_EQUAL_TO, 1))) {
@@ -55,6 +73,9 @@ struct cmd_results *cmd_split(int argc, char **argv) {
55 } else { 73 } else {
56 return do_split(L_VERT); 74 return do_split(L_VERT);
57 } 75 }
76 } else if (strcasecmp(argv[0], "n") == 0 ||
77 strcasecmp(argv[0], "none") == 0) {
78 return do_unsplit();
58 } else { 79 } else {
59 return cmd_results_new(CMD_FAILURE, 80 return cmd_results_new(CMD_FAILURE,
60 "Invalid split command (expected either horizontal or vertical)."); 81 "Invalid split command (expected either horizontal or vertical).");
diff --git a/sway/commands/swap.c b/sway/commands/swap.c
index ce5e5128..c0b0d0b9 100644
--- a/sway/commands/swap.c
+++ b/sway/commands/swap.c
@@ -1,10 +1,10 @@
1#define _POSIX_C_SOURCE 200809L
2#include <strings.h> 1#include <strings.h>
3#include "config.h" 2#include "config.h"
4#include "log.h" 3#include "log.h"
5#include "sway/commands.h" 4#include "sway/commands.h"
6#include "sway/output.h" 5#include "sway/output.h"
7#include "sway/tree/arrange.h" 6#include "sway/tree/arrange.h"
7#include "sway/tree/container.h"
8#include "sway/tree/root.h" 8#include "sway/tree/root.h"
9#include "sway/tree/view.h" 9#include "sway/tree/view.h"
10#include "sway/tree/workspace.h" 10#include "sway/tree/workspace.h"
@@ -13,186 +13,12 @@
13static const char expected_syntax[] = 13static const char expected_syntax[] =
14 "Expected 'swap container with id|con_id|mark <arg>'"; 14 "Expected 'swap container with id|con_id|mark <arg>'";
15 15
16static void swap_places(struct sway_container *con1,
17 struct sway_container *con2) {
18 struct sway_container *temp = malloc(sizeof(struct sway_container));
19 temp->pending.x = con1->pending.x;
20 temp->pending.y = con1->pending.y;
21 temp->pending.width = con1->pending.width;
22 temp->pending.height = con1->pending.height;
23 temp->width_fraction = con1->width_fraction;
24 temp->height_fraction = con1->height_fraction;
25 temp->pending.parent = con1->pending.parent;
26 temp->pending.workspace = con1->pending.workspace;
27 bool temp_floating = container_is_floating(con1);
28
29 con1->pending.x = con2->pending.x;
30 con1->pending.y = con2->pending.y;
31 con1->pending.width = con2->pending.width;
32 con1->pending.height = con2->pending.height;
33 con1->width_fraction = con2->width_fraction;
34 con1->height_fraction = con2->height_fraction;
35
36 con2->pending.x = temp->pending.x;
37 con2->pending.y = temp->pending.y;
38 con2->pending.width = temp->pending.width;
39 con2->pending.height = temp->pending.height;
40 con2->width_fraction = temp->width_fraction;
41 con2->height_fraction = temp->height_fraction;
42
43 int temp_index = container_sibling_index(con1);
44 if (con2->pending.parent) {
45 container_insert_child(con2->pending.parent, con1,
46 container_sibling_index(con2));
47 } else if (container_is_floating(con2)) {
48 workspace_add_floating(con2->pending.workspace, con1);
49 } else {
50 workspace_insert_tiling(con2->pending.workspace, con1,
51 container_sibling_index(con2));
52 }
53 if (temp->pending.parent) {
54 container_insert_child(temp->pending.parent, con2, temp_index);
55 } else if (temp_floating) {
56 workspace_add_floating(temp->pending.workspace, con2);
57 } else {
58 workspace_insert_tiling(temp->pending.workspace, con2, temp_index);
59 }
60
61 free(temp);
62}
63
64static void swap_focus(struct sway_container *con1,
65 struct sway_container *con2, struct sway_seat *seat,
66 struct sway_container *focus) {
67 if (focus == con1 || focus == con2) {
68 struct sway_workspace *ws1 = con1->pending.workspace;
69 struct sway_workspace *ws2 = con2->pending.workspace;
70 enum sway_container_layout layout1 = container_parent_layout(con1);
71 enum sway_container_layout layout2 = container_parent_layout(con2);
72 if (focus == con1 && (layout2 == L_TABBED || layout2 == L_STACKED)) {
73 if (workspace_is_visible(ws2)) {
74 seat_set_focus(seat, &con2->node);
75 }
76 seat_set_focus_container(seat, ws1 != ws2 ? con2 : con1);
77 } else if (focus == con2 && (layout1 == L_TABBED
78 || layout1 == L_STACKED)) {
79 if (workspace_is_visible(ws1)) {
80 seat_set_focus(seat, &con1->node);
81 }
82 seat_set_focus_container(seat, ws1 != ws2 ? con1 : con2);
83 } else if (ws1 != ws2) {
84 seat_set_focus_container(seat, focus == con1 ? con2 : con1);
85 } else {
86 seat_set_focus_container(seat, focus);
87 }
88 } else {
89 seat_set_focus_container(seat, focus);
90 }
91
92 if (root->fullscreen_global) {
93 seat_set_focus(seat,
94 seat_get_focus_inactive(seat, &root->fullscreen_global->node));
95 }
96}
97
98void container_swap(struct sway_container *con1, struct sway_container *con2) {
99 if (!sway_assert(con1 && con2, "Cannot swap with nothing")) {
100 return;
101 }
102 if (!sway_assert(!container_has_ancestor(con1, con2)
103 && !container_has_ancestor(con2, con1),
104 "Cannot swap ancestor and descendant")) {
105 return;
106 }
107
108 sway_log(SWAY_DEBUG, "Swapping containers %zu and %zu",
109 con1->node.id, con2->node.id);
110
111 bool scratch1 = con1->scratchpad;
112 bool hidden1 = container_is_scratchpad_hidden(con1);
113 bool scratch2 = con2->scratchpad;
114 bool hidden2 = container_is_scratchpad_hidden(con2);
115 if (scratch1) {
116 if (hidden1) {
117 root_scratchpad_show(con1);
118 }
119 root_scratchpad_remove_container(con1);
120 }
121 if (scratch2) {
122 if (hidden2) {
123 root_scratchpad_show(con2);
124 }
125 root_scratchpad_remove_container(con2);
126 }
127
128 enum sway_fullscreen_mode fs1 = con1->pending.fullscreen_mode;
129 enum sway_fullscreen_mode fs2 = con2->pending.fullscreen_mode;
130 if (fs1) {
131 container_fullscreen_disable(con1);
132 }
133 if (fs2) {
134 container_fullscreen_disable(con2);
135 }
136
137 struct sway_seat *seat = config->handler_context.seat;
138 struct sway_container *focus = seat_get_focused_container(seat);
139 struct sway_workspace *vis1 =
140 output_get_active_workspace(con1->pending.workspace->output);
141 struct sway_workspace *vis2 =
142 output_get_active_workspace(con2->pending.workspace->output);
143 if (!sway_assert(vis1 && vis2, "con1 or con2 are on an output without a"
144 "workspace. This should not happen")) {
145 return;
146 }
147
148 char *stored_prev_name = NULL;
149 if (seat->prev_workspace_name) {
150 stored_prev_name = strdup(seat->prev_workspace_name);
151 }
152
153 swap_places(con1, con2);
154
155 if (!workspace_is_visible(vis1)) {
156 seat_set_focus(seat, seat_get_focus_inactive(seat, &vis1->node));
157 }
158 if (!workspace_is_visible(vis2)) {
159 seat_set_focus(seat, seat_get_focus_inactive(seat, &vis2->node));
160 }
161
162 swap_focus(con1, con2, seat, focus);
163
164 if (stored_prev_name) {
165 free(seat->prev_workspace_name);
166 seat->prev_workspace_name = stored_prev_name;
167 }
168
169 if (scratch1) {
170 root_scratchpad_add_container(con2, NULL);
171 if (!hidden1) {
172 root_scratchpad_show(con2);
173 }
174 }
175 if (scratch2) {
176 root_scratchpad_add_container(con1, NULL);
177 if (!hidden2) {
178 root_scratchpad_show(con1);
179 }
180 }
181
182 if (fs1) {
183 container_set_fullscreen(con2, fs1);
184 }
185 if (fs2) {
186 container_set_fullscreen(con1, fs2);
187 }
188}
189
190static bool test_con_id(struct sway_container *container, void *data) { 16static bool test_con_id(struct sway_container *container, void *data) {
191 size_t *con_id = data; 17 size_t *con_id = data;
192 return container->node.id == *con_id; 18 return container->node.id == *con_id;
193} 19}
194 20
195#if HAVE_XWAYLAND 21#if WLR_HAS_XWAYLAND
196static bool test_id(struct sway_container *container, void *data) { 22static bool test_id(struct sway_container *container, void *data) {
197 xcb_window_t *wid = data; 23 xcb_window_t *wid = data;
198 return (container->view && container->view->type == SWAY_VIEW_XWAYLAND 24 return (container->view && container->view->type == SWAY_VIEW_XWAYLAND
@@ -219,7 +45,7 @@ struct cmd_results *cmd_swap(int argc, char **argv) {
219 } 45 }
220 46
221 if (strcasecmp(argv[0], "container") || strcasecmp(argv[1], "with")) { 47 if (strcasecmp(argv[0], "container") || strcasecmp(argv[1], "with")) {
222 return cmd_results_new(CMD_INVALID, expected_syntax); 48 return cmd_results_new(CMD_INVALID, "%s", expected_syntax);
223 } 49 }
224 50
225 struct sway_container *current = config->handler_context.container; 51 struct sway_container *current = config->handler_context.container;
@@ -227,7 +53,7 @@ struct cmd_results *cmd_swap(int argc, char **argv) {
227 53
228 char *value = join_args(argv + 3, argc - 3); 54 char *value = join_args(argv + 3, argc - 3);
229 if (strcasecmp(argv[2], "id") == 0) { 55 if (strcasecmp(argv[2], "id") == 0) {
230#if HAVE_XWAYLAND 56#if WLR_HAS_XWAYLAND
231 xcb_window_t id = strtol(value, NULL, 0); 57 xcb_window_t id = strtol(value, NULL, 0);
232 other = root_find_container(test_id, &id); 58 other = root_find_container(test_id, &id);
233#endif 59#endif
@@ -238,7 +64,7 @@ struct cmd_results *cmd_swap(int argc, char **argv) {
238 other = root_find_container(test_mark, value); 64 other = root_find_container(test_mark, value);
239 } else { 65 } else {
240 free(value); 66 free(value);
241 return cmd_results_new(CMD_INVALID, expected_syntax); 67 return cmd_results_new(CMD_INVALID, "%s", expected_syntax);
242 } 68 }
243 69
244 if (!other) { 70 if (!other) {
@@ -247,6 +73,9 @@ struct cmd_results *cmd_swap(int argc, char **argv) {
247 } else if (!current) { 73 } else if (!current) {
248 error = cmd_results_new(CMD_FAILURE, 74 error = cmd_results_new(CMD_FAILURE,
249 "Can only swap with containers and views"); 75 "Can only swap with containers and views");
76 } else if (current == other) {
77 error = cmd_results_new(CMD_FAILURE,
78 "Cannot swap a container with itself");
250 } else if (container_has_ancestor(current, other) 79 } else if (container_has_ancestor(current, other)
251 || container_has_ancestor(other, current)) { 80 || container_has_ancestor(other, current)) {
252 error = cmd_results_new(CMD_FAILURE, 81 error = cmd_results_new(CMD_FAILURE,
diff --git a/sway/commands/title_align.c b/sway/commands/title_align.c
index c30355de..be298a29 100644
--- a/sway/commands/title_align.c
+++ b/sway/commands/title_align.c
@@ -4,6 +4,10 @@
4#include "sway/tree/container.h" 4#include "sway/tree/container.h"
5#include "sway/tree/root.h" 5#include "sway/tree/root.h"
6 6
7static void arrange_title_bar_iterator(struct sway_container *con, void *data) {
8 container_arrange_title_bar(con);
9}
10
7struct cmd_results *cmd_title_align(int argc, char **argv) { 11struct cmd_results *cmd_title_align(int argc, char **argv) {
8 struct cmd_results *error = NULL; 12 struct cmd_results *error = NULL;
9 if ((error = checkarg(argc, "title_align", EXPECTED_AT_LEAST, 1))) { 13 if ((error = checkarg(argc, "title_align", EXPECTED_AT_LEAST, 1))) {
@@ -21,10 +25,7 @@ struct cmd_results *cmd_title_align(int argc, char **argv) {
21 "Expected 'title_align left|center|right'"); 25 "Expected 'title_align left|center|right'");
22 } 26 }
23 27
24 for (int i = 0; i < root->outputs->length; ++i) { 28 root_for_each_container(arrange_title_bar_iterator, NULL);
25 struct sway_output *output = root->outputs->items[i];
26 output_damage_whole(output);
27 }
28 29
29 return cmd_results_new(CMD_SUCCESS, NULL); 30 return cmd_results_new(CMD_SUCCESS, NULL);
30} 31}
diff --git a/sway/commands/title_format.c b/sway/commands/title_format.c
index 9d312470..0b2ea265 100644
--- a/sway/commands/title_format.c
+++ b/sway/commands/title_format.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <string.h> 1#include <string.h>
3#include "sway/commands.h" 2#include "sway/commands.h"
4#include "sway/config.h" 3#include "sway/config.h"
@@ -23,6 +22,5 @@ struct cmd_results *cmd_title_format(int argc, char **argv) {
23 } 22 }
24 view->title_format = format; 23 view->title_format = format;
25 view_update_title(view, true); 24 view_update_title(view, true);
26 config_update_font_height(true);
27 return cmd_results_new(CMD_SUCCESS, NULL); 25 return cmd_results_new(CMD_SUCCESS, NULL);
28} 26}
diff --git a/sway/commands/titlebar_border_thickness.c b/sway/commands/titlebar_border_thickness.c
index 7c27c163..fa3db3c5 100644
--- a/sway/commands/titlebar_border_thickness.c
+++ b/sway/commands/titlebar_border_thickness.c
@@ -27,7 +27,6 @@ struct cmd_results *cmd_titlebar_border_thickness(int argc, char **argv) {
27 "Expected output to have a workspace"); 27 "Expected output to have a workspace");
28 } 28 }
29 arrange_workspace(ws); 29 arrange_workspace(ws);
30 output_damage_whole(output);
31 } 30 }
32 31
33 return cmd_results_new(CMD_SUCCESS, NULL); 32 return cmd_results_new(CMD_SUCCESS, NULL);
diff --git a/sway/commands/titlebar_padding.c b/sway/commands/titlebar_padding.c
index 29ce59ff..6999f7a2 100644
--- a/sway/commands/titlebar_padding.c
+++ b/sway/commands/titlebar_padding.c
@@ -33,7 +33,6 @@ struct cmd_results *cmd_titlebar_padding(int argc, char **argv) {
33 for (int i = 0; i < root->outputs->length; ++i) { 33 for (int i = 0; i < root->outputs->length; ++i) {
34 struct sway_output *output = root->outputs->items[i]; 34 struct sway_output *output = root->outputs->items[i];
35 arrange_workspace(output_get_active_workspace(output)); 35 arrange_workspace(output_get_active_workspace(output));
36 output_damage_whole(output);
37 } 36 }
38 37
39 return cmd_results_new(CMD_SUCCESS, NULL); 38 return cmd_results_new(CMD_SUCCESS, NULL);
diff --git a/sway/commands/unmark.c b/sway/commands/unmark.c
index 19274dfb..4aba5bae 100644
--- a/sway/commands/unmark.c
+++ b/sway/commands/unmark.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <string.h> 1#include <string.h>
3#include "sway/commands.h" 2#include "sway/commands.h"
4#include "sway/config.h" 3#include "sway/config.h"
@@ -8,9 +7,13 @@
8#include "log.h" 7#include "log.h"
9#include "stringop.h" 8#include "stringop.h"
10 9
11static void remove_all_marks_iterator(struct sway_container *con, void *data) { 10static void remove_mark(struct sway_container *con) {
12 container_clear_marks(con); 11 container_clear_marks(con);
13 container_update_marks_textures(con); 12 container_update_marks(con);
13}
14
15static void remove_all_marks_iterator(struct sway_container *con, void *data) {
16 remove_mark(con);
14} 17}
15 18
16// unmark Remove all marks from all views 19// unmark Remove all marks from all views
@@ -38,8 +41,7 @@ struct cmd_results *cmd_unmark(int argc, char **argv) {
38 } 41 }
39 } else if (con && !mark) { 42 } else if (con && !mark) {
40 // Clear all marks from the given container 43 // Clear all marks from the given container
41 container_clear_marks(con); 44 remove_mark(con);
42 container_update_marks_textures(con);
43 } else if (!con && mark) { 45 } else if (!con && mark) {
44 // Remove mark from whichever container has it 46 // Remove mark from whichever container has it
45 container_find_and_unmark(mark); 47 container_find_and_unmark(mark);
diff --git a/sway/commands/workspace.c b/sway/commands/workspace.c
index a6a0beda..37a201b4 100644
--- a/sway/commands/workspace.c
+++ b/sway/commands/workspace.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <ctype.h> 1#include <ctype.h>
3#include <limits.h> 2#include <limits.h>
4#include <string.h> 3#include <string.h>
@@ -61,7 +60,7 @@ static struct cmd_results *cmd_workspace_gaps(int argc, char **argv,
61 const char expected[] = "Expected 'workspace <name> gaps " 60 const char expected[] = "Expected 'workspace <name> gaps "
62 "inner|outer|horizontal|vertical|top|right|bottom|left <px>'"; 61 "inner|outer|horizontal|vertical|top|right|bottom|left <px>'";
63 if (gaps_location == 0) { 62 if (gaps_location == 0) {
64 return cmd_results_new(CMD_INVALID, expected); 63 return cmd_results_new(CMD_INVALID, "%s", expected);
65 } 64 }
66 struct cmd_results *error = NULL; 65 struct cmd_results *error = NULL;
67 if ((error = checkarg(argc, "workspace", EXPECTED_EQUAL_TO, 66 if ((error = checkarg(argc, "workspace", EXPECTED_EQUAL_TO,
@@ -79,7 +78,7 @@ static struct cmd_results *cmd_workspace_gaps(int argc, char **argv,
79 char *end; 78 char *end;
80 int amount = strtol(argv[gaps_location + 2], &end, 10); 79 int amount = strtol(argv[gaps_location + 2], &end, 10);
81 if (strlen(end)) { 80 if (strlen(end)) {
82 return cmd_results_new(CMD_FAILURE, expected); 81 return cmd_results_new(CMD_FAILURE, "%s", expected);
83 } 82 }
84 83
85 bool valid = false; 84 bool valid = false;
@@ -110,7 +109,7 @@ static struct cmd_results *cmd_workspace_gaps(int argc, char **argv,
110 } 109 }
111 } 110 }
112 if (!valid) { 111 if (!valid) {
113 return cmd_results_new(CMD_INVALID, expected); 112 return cmd_results_new(CMD_INVALID, "%s", expected);
114 } 113 }
115 114
116 // Prevent invalid gaps configurations. 115 // Prevent invalid gaps configurations.
@@ -158,6 +157,10 @@ struct cmd_results *cmd_workspace(int argc, char **argv) {
158 return cmd_results_new(CMD_FAILURE, 157 return cmd_results_new(CMD_FAILURE,
159 "Unable to allocate workspace output"); 158 "Unable to allocate workspace output");
160 } 159 }
160 if (output_location + 1 < argc) {
161 list_free_items_and_destroy(wsc->outputs);
162 wsc->outputs = create_list();
163 }
161 for (int i = output_location + 1; i < argc; ++i) { 164 for (int i = output_location + 1; i < argc; ++i) {
162 list_add(wsc->outputs, strdup(argv[i])); 165 list_add(wsc->outputs, strdup(argv[i]));
163 } 166 }
@@ -174,7 +177,7 @@ struct cmd_results *cmd_workspace(int argc, char **argv) {
174 } 177 }
175 178
176 if (root->fullscreen_global) { 179 if (root->fullscreen_global) {
177 return cmd_results_new(CMD_FAILURE, "workspace", 180 return cmd_results_new(CMD_FAILURE,
178 "Can't switch workspaces while fullscreen global"); 181 "Can't switch workspaces while fullscreen global");
179 } 182 }
180 183
diff --git a/sway/commands/xwayland.c b/sway/commands/xwayland.c
index 6ca26923..c0b175fc 100644
--- a/sway/commands/xwayland.c
+++ b/sway/commands/xwayland.c
@@ -10,7 +10,7 @@ struct cmd_results *cmd_xwayland(int argc, char **argv) {
10 return error; 10 return error;
11 } 11 }
12 12
13#ifdef HAVE_XWAYLAND 13#ifdef WLR_HAS_XWAYLAND
14 enum xwayland_mode xwayland; 14 enum xwayland_mode xwayland;
15 if (strcmp(argv[0], "force") == 0) { 15 if (strcmp(argv[0], "force") == 0) {
16 xwayland = XWAYLAND_MODE_IMMEDIATE; 16 xwayland = XWAYLAND_MODE_IMMEDIATE;
@@ -20,6 +20,8 @@ struct cmd_results *cmd_xwayland(int argc, char **argv) {
20 xwayland = XWAYLAND_MODE_DISABLED; 20 xwayland = XWAYLAND_MODE_DISABLED;
21 } 21 }
22 22
23 // config->xwayland is reset to the previous value on reload in
24 // load_main_config()
23 if (config->reloading && config->xwayland != xwayland) { 25 if (config->reloading && config->xwayland != xwayland) {
24 return cmd_results_new(CMD_FAILURE, 26 return cmd_results_new(CMD_FAILURE,
25 "xwayland can only be enabled/disabled at launch"); 27 "xwayland can only be enabled/disabled at launch");
diff --git a/sway/config.c b/sway/config.c
index fde386c7..f9131e0f 100644
--- a/sway/config.c
+++ b/sway/config.c
@@ -1,3 +1,4 @@
1#undef _POSIX_C_SOURCE
1#define _XOPEN_SOURCE 700 // for realpath 2#define _XOPEN_SOURCE 700 // for realpath
2#include <stdio.h> 3#include <stdio.h>
3#include <stdbool.h> 4#include <stdbool.h>
@@ -36,19 +37,26 @@
36struct sway_config *config = NULL; 37struct sway_config *config = NULL;
37 38
38static struct xkb_state *keysym_translation_state_create( 39static struct xkb_state *keysym_translation_state_create(
39 struct xkb_rule_names rules) { 40 struct xkb_rule_names rules, uint32_t context_flags) {
40 struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); 41 struct xkb_context *context = xkb_context_new(context_flags | XKB_CONTEXT_NO_SECURE_GETENV);
41 struct xkb_keymap *xkb_keymap = xkb_keymap_new_from_names( 42 struct xkb_keymap *xkb_keymap = xkb_keymap_new_from_names(
42 context, 43 context,
43 &rules, 44 &rules,
44 XKB_KEYMAP_COMPILE_NO_FLAGS); 45 XKB_KEYMAP_COMPILE_NO_FLAGS);
45
46 xkb_context_unref(context); 46 xkb_context_unref(context);
47 if (xkb_keymap == NULL) {
48 sway_log(SWAY_ERROR, "Failed to compile keysym translation XKB keymap");
49 return NULL;
50 }
51
47 return xkb_state_new(xkb_keymap); 52 return xkb_state_new(xkb_keymap);
48} 53}
49 54
50static void keysym_translation_state_destroy( 55static void keysym_translation_state_destroy(
51 struct xkb_state *state) { 56 struct xkb_state *state) {
57 if (state == NULL) {
58 return;
59 }
52 xkb_keymap_unref(xkb_state_get_keymap(state)); 60 xkb_keymap_unref(xkb_state_get_keymap(state));
53 xkb_state_unref(state); 61 xkb_state_unref(state);
54} 62}
@@ -82,6 +90,12 @@ static void free_mode(struct sway_mode *mode) {
82 } 90 }
83 list_free(mode->switch_bindings); 91 list_free(mode->switch_bindings);
84 } 92 }
93 if (mode->gesture_bindings) {
94 for (int i = 0; i < mode->gesture_bindings->length; i++) {
95 free_gesture_binding(mode->gesture_bindings->items[i]);
96 }
97 list_free(mode->gesture_bindings);
98 }
85 free(mode); 99 free(mode);
86} 100}
87 101
@@ -222,6 +236,7 @@ static void config_defaults(struct sway_config *config) {
222 if (!(config->current_mode->keycode_bindings = create_list())) goto cleanup; 236 if (!(config->current_mode->keycode_bindings = create_list())) goto cleanup;
223 if (!(config->current_mode->mouse_bindings = create_list())) goto cleanup; 237 if (!(config->current_mode->mouse_bindings = create_list())) goto cleanup;
224 if (!(config->current_mode->switch_bindings = create_list())) goto cleanup; 238 if (!(config->current_mode->switch_bindings = create_list())) goto cleanup;
239 if (!(config->current_mode->gesture_bindings = create_list())) goto cleanup;
225 list_add(config->modes, config->current_mode); 240 list_add(config->modes, config->current_mode);
226 241
227 config->floating_mod = 0; 242 config->floating_mod = 0;
@@ -236,7 +251,7 @@ static void config_defaults(struct sway_config *config) {
236 config->default_layout = L_NONE; 251 config->default_layout = L_NONE;
237 config->default_orientation = L_NONE; 252 config->default_orientation = L_NONE;
238 if (!(config->font = strdup("monospace 10"))) goto cleanup; 253 if (!(config->font = strdup("monospace 10"))) goto cleanup;
239 config->font_height = 17; // height of monospace 10 254 config->font_description = pango_font_description_from_string(config->font);
240 config->urgent_timeout = 500; 255 config->urgent_timeout = 500;
241 config->focus_on_window_activation = FOWA_URGENT; 256 config->focus_on_window_activation = FOWA_URGENT;
242 config->popup_during_fullscreen = POPUP_SMART; 257 config->popup_during_fullscreen = POPUP_SMART;
@@ -266,8 +281,9 @@ static void config_defaults(struct sway_config *config) {
266 config->title_align = ALIGN_LEFT; 281 config->title_align = ALIGN_LEFT;
267 config->tiling_drag = true; 282 config->tiling_drag = true;
268 config->tiling_drag_threshold = 9; 283 config->tiling_drag_threshold = 9;
284 config->primary_selection = true;
269 285
270 config->smart_gaps = false; 286 config->smart_gaps = SMART_GAPS_OFF;
271 config->gaps_inner = 0; 287 config->gaps_inner = 0;
272 config->gaps_outer.top = 0; 288 config->gaps_outer.top = 0;
273 config->gaps_outer.right = 0; 289 config->gaps_outer.right = 0;
@@ -291,6 +307,8 @@ static void config_defaults(struct sway_config *config) {
291 config->hide_edge_borders_smart = ESMART_OFF; 307 config->hide_edge_borders_smart = ESMART_OFF;
292 config->hide_lone_tab = false; 308 config->hide_lone_tab = false;
293 309
310 config->has_focused_tab_title = false;
311
294 // border colors 312 // border colors
295 color_to_rgba(config->border_colors.focused.border, 0x4C7899FF); 313 color_to_rgba(config->border_colors.focused.border, 0x4C7899FF);
296 color_to_rgba(config->border_colors.focused.background, 0x285577FF); 314 color_to_rgba(config->border_colors.focused.background, 0x285577FF);
@@ -326,8 +344,14 @@ static void config_defaults(struct sway_config *config) {
326 344
327 // The keysym to keycode translation 345 // The keysym to keycode translation
328 struct xkb_rule_names rules = {0}; 346 struct xkb_rule_names rules = {0};
329 config->keysym_translation_state = 347 config->keysym_translation_state = keysym_translation_state_create(rules, 0);
330 keysym_translation_state_create(rules); 348 if (config->keysym_translation_state == NULL) {
349 config->keysym_translation_state = keysym_translation_state_create(rules,
350 XKB_CONTEXT_NO_ENVIRONMENT_NAMES);
351 }
352 if (config->keysym_translation_state == NULL) {
353 goto cleanup;
354 }
331 355
332 return; 356 return;
333cleanup: 357cleanup:
@@ -342,13 +366,7 @@ static char *config_path(const char *prefix, const char *config_folder) {
342 if (!prefix || !prefix[0] || !config_folder || !config_folder[0]) { 366 if (!prefix || !prefix[0] || !config_folder || !config_folder[0]) {
343 return NULL; 367 return NULL;
344 } 368 }
345 369 return format_str("%s/%s/config", prefix, config_folder);
346 const char *filename = "config";
347
348 size_t size = 3 + strlen(prefix) + strlen(config_folder) + strlen(filename);
349 char *path = calloc(size, sizeof(char));
350 snprintf(path, size, "%s/%s/%s", prefix, config_folder, filename);
351 return path;
352} 370}
353 371
354static char *get_config_path(void) { 372static char *get_config_path(void) {
@@ -358,10 +376,7 @@ static char *get_config_path(void) {
358 376
359 const char *config_home = getenv("XDG_CONFIG_HOME"); 377 const char *config_home = getenv("XDG_CONFIG_HOME");
360 if ((config_home == NULL || config_home[0] == '\0') && home != NULL) { 378 if ((config_home == NULL || config_home[0] == '\0') && home != NULL) {
361 size_t size_fallback = 1 + strlen(home) + strlen("/.config"); 379 config_home_fallback = format_str("%s/.config", home);
362 config_home_fallback = calloc(size_fallback, sizeof(char));
363 if (config_home_fallback != NULL)
364 snprintf(config_home_fallback, size_fallback, "%s/.config", home);
365 config_home = config_home_fallback; 380 config_home = config_home_fallback;
366 } 381 }
367 382
@@ -465,6 +480,11 @@ bool load_main_config(const char *file, bool is_active, bool validating) {
465 old_config->xwayland ? "enabled" : "disabled"); 480 old_config->xwayland ? "enabled" : "disabled");
466 config->xwayland = old_config->xwayland; 481 config->xwayland = old_config->xwayland;
467 482
483 // primary_selection can only be enabled/disabled at launch
484 sway_log(SWAY_DEBUG, "primary_selection will remain %s",
485 old_config->primary_selection ? "enabled" : "disabled");
486 config->primary_selection = old_config->primary_selection;
487
468 if (!config->validating) { 488 if (!config->validating) {
469 if (old_config->swaybg_client != NULL) { 489 if (old_config->swaybg_client != NULL) {
470 wl_client_destroy(old_config->swaybg_client); 490 wl_client_destroy(old_config->swaybg_client);
@@ -484,56 +504,7 @@ bool load_main_config(const char *file, bool is_active, bool validating) {
484 504
485 config->reading = true; 505 config->reading = true;
486 506
487 // Read security configs 507 bool success = load_config(path, config, &config->swaynag_config_errors);
488 // TODO: Security
489 bool success = true;
490 /*
491 DIR *dir = opendir(SYSCONFDIR "/sway/security.d");
492 if (!dir) {
493 sway_log(SWAY_ERROR,
494 "%s does not exist, sway will have no security configuration"
495 " and will probably be broken", SYSCONFDIR "/sway/security.d");
496 } else {
497 list_t *secconfigs = create_list();
498 char *base = SYSCONFDIR "/sway/security.d/";
499 struct dirent *ent = readdir(dir);
500 struct stat s;
501 while (ent != NULL) {
502 char *_path = malloc(strlen(ent->d_name) + strlen(base) + 1);
503 strcpy(_path, base);
504 strcat(_path, ent->d_name);
505 lstat(_path, &s);
506 if (S_ISREG(s.st_mode) && ent->d_name[0] != '.') {
507 list_add(secconfigs, _path);
508 }
509 else {
510 free(_path);
511 }
512 ent = readdir(dir);
513 }
514 closedir(dir);
515
516 list_qsort(secconfigs, qstrcmp);
517 for (int i = 0; i < secconfigs->length; ++i) {
518 char *_path = secconfigs->items[i];
519 if (stat(_path, &s) || s.st_uid != 0 || s.st_gid != 0 ||
520 (((s.st_mode & 0777) != 0644) &&
521 (s.st_mode & 0777) != 0444)) {
522 sway_log(SWAY_ERROR,
523 "Refusing to load %s - it must be owned by root "
524 "and mode 644 or 444", _path);
525 success = false;
526 } else {
527 success = success && load_config(_path, config);
528 }
529 }
530
531 list_free_items_and_destroy(secconfigs);
532 }
533 */
534
535 success = success && load_config(path, config,
536 &config->swaynag_config_errors);
537 508
538 if (validating) { 509 if (validating) {
539 free_config(config); 510 free_config(config);
@@ -541,6 +512,9 @@ bool load_main_config(const char *file, bool is_active, bool validating) {
541 return success; 512 return success;
542 } 513 }
543 514
515 // Only really necessary if not explicitly `font` is set in the config.
516 config_update_font_height();
517
544 if (is_active && !validating) { 518 if (is_active && !validating) {
545 input_manager_verify_fallback_seat(); 519 input_manager_verify_fallback_seat();
546 520
@@ -558,7 +532,7 @@ bool load_main_config(const char *file, bool is_active, bool validating) {
558 } 532 }
559 sway_switch_retrigger_bindings_for_all(); 533 sway_switch_retrigger_bindings_for_all();
560 534
561 reset_outputs(); 535 apply_all_output_configs();
562 spawn_swaybg(); 536 spawn_swaybg();
563 537
564 config->reloading = false; 538 config->reloading = false;
@@ -911,23 +885,18 @@ void config_add_swaynag_warning(char *fmt, ...) {
911 if (config->reading && !config->validating) { 885 if (config->reading && !config->validating) {
912 va_list args; 886 va_list args;
913 va_start(args, fmt); 887 va_start(args, fmt);
914 size_t length = vsnprintf(NULL, 0, fmt, args) + 1; 888 char *str = vformat_str(fmt, args);
915 va_end(args); 889 va_end(args);
916 890 if (str == NULL) {
917 char *temp = malloc(length + 1);
918 if (!temp) {
919 sway_log(SWAY_ERROR, "Failed to allocate buffer for warning.");
920 return; 891 return;
921 } 892 }
922 893
923 va_start(args, fmt);
924 vsnprintf(temp, length, fmt, args);
925 va_end(args);
926
927 swaynag_log(config->swaynag_command, &config->swaynag_config_errors, 894 swaynag_log(config->swaynag_command, &config->swaynag_config_errors,
928 "Warning on line %i (%s) '%s': %s", 895 "Warning on line %i (%s) '%s': %s",
929 config->current_config_line_number, config->current_config_path, 896 config->current_config_line_number, config->current_config_path,
930 config->current_config_line, temp); 897 config->current_config_line, str);
898
899 free(str);
931 } 900 }
932} 901}
933 902
@@ -967,7 +936,7 @@ char *do_var_replacement(char *str) {
967 int offset = find - str; 936 int offset = find - str;
968 strncpy(newptr, str, offset); 937 strncpy(newptr, str, offset);
969 newptr += offset; 938 newptr += offset;
970 strncpy(newptr, var->value, vvlen); 939 memcpy(newptr, var->value, vvlen);
971 newptr += vvlen; 940 newptr += vvlen;
972 strcpy(newptr, find + vnlen); 941 strcpy(newptr, find + vnlen);
973 free(str); 942 free(str);
@@ -991,31 +960,11 @@ int workspace_output_cmp_workspace(const void *a, const void *b) {
991 return lenient_strcmp(wsa->workspace, wsb->workspace); 960 return lenient_strcmp(wsa->workspace, wsb->workspace);
992} 961}
993 962
994static void find_font_height_iterator(struct sway_container *con, void *data) {
995 size_t amount_below_baseline = con->title_height - con->title_baseline;
996 size_t extended_height = config->font_baseline + amount_below_baseline;
997 if (extended_height > config->font_height) {
998 config->font_height = extended_height;
999 }
1000}
1001
1002static void find_baseline_iterator(struct sway_container *con, void *data) {
1003 bool *recalculate = data;
1004 if (*recalculate) {
1005 container_calculate_title_height(con);
1006 }
1007 if (con->title_baseline > config->font_baseline) {
1008 config->font_baseline = con->title_baseline;
1009 }
1010}
1011 963
1012void config_update_font_height(bool recalculate) { 964void config_update_font_height(void) {
1013 size_t prev_max_height = config->font_height; 965 int prev_max_height = config->font_height;
1014 config->font_height = 0;
1015 config->font_baseline = 0;
1016 966
1017 root_for_each_container(find_baseline_iterator, &recalculate); 967 get_text_metrics(config->font_description, &config->font_height, &config->font_baseline);
1018 root_for_each_container(find_font_height_iterator, NULL);
1019 968
1020 if (config->font_height != prev_max_height) { 969 if (config->font_height != prev_max_height) {
1021 arrange_root(); 970 arrange_root();
@@ -1049,8 +998,12 @@ void translate_keysyms(struct input_config *input_config) {
1049 998
1050 struct xkb_rule_names rules = {0}; 999 struct xkb_rule_names rules = {0};
1051 input_config_fill_rule_names(input_config, &rules); 1000 input_config_fill_rule_names(input_config, &rules);
1052 config->keysym_translation_state = 1001 config->keysym_translation_state = keysym_translation_state_create(rules, 0);
1053 keysym_translation_state_create(rules); 1002 if (config->keysym_translation_state == NULL) {
1003 sway_log(SWAY_ERROR, "Failed to create keysym translation XKB state "
1004 "for device '%s'", input_config->identifier);
1005 return;
1006 }
1054 1007
1055 for (int i = 0; i < config->modes->length; ++i) { 1008 for (int i = 0; i < config->modes->length; ++i) {
1056 struct sway_mode *mode = config->modes->items[i]; 1009 struct sway_mode *mode = config->modes->items[i];
diff --git a/sway/config/bar.c b/sway/config/bar.c
index e09add44..908b2865 100644
--- a/sway/config/bar.c
+++ b/sway/config/bar.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <signal.h> 1#include <signal.h>
3#include <stdbool.h> 2#include <stdbool.h>
4#include <stdio.h> 3#include <stdio.h>
@@ -219,6 +218,8 @@ static void invoke_swaybar(struct bar_config *bar) {
219 sigprocmask(SIG_SETMASK, &set, NULL); 218 sigprocmask(SIG_SETMASK, &set, NULL);
220 signal(SIGPIPE, SIG_DFL); 219 signal(SIGPIPE, SIG_DFL);
221 220
221 restore_nofile_limit();
222
222 pid = fork(); 223 pid = fork();
223 if (pid < 0) { 224 if (pid < 0) {
224 sway_log_errno(SWAY_ERROR, "fork failed"); 225 sway_log_errno(SWAY_ERROR, "fork failed");
@@ -254,7 +255,6 @@ static void invoke_swaybar(struct bar_config *bar) {
254 } 255 }
255 256
256 sway_log(SWAY_DEBUG, "Spawned swaybar %s", bar->id); 257 sway_log(SWAY_DEBUG, "Spawned swaybar %s", bar->id);
257 return;
258} 258}
259 259
260void load_swaybar(struct bar_config *bar) { 260void load_swaybar(struct bar_config *bar) {
diff --git a/sway/config/input.c b/sway/config/input.c
index a998e170..de3b21ed 100644
--- a/sway/config/input.c
+++ b/sway/config/input.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <stdlib.h> 1#include <stdlib.h>
3#include <limits.h> 2#include <limits.h>
4#include <float.h> 3#include <float.h>
@@ -25,14 +24,17 @@ struct input_config *new_input_config(const char* identifier) {
25 input->drag = INT_MIN; 24 input->drag = INT_MIN;
26 input->drag_lock = INT_MIN; 25 input->drag_lock = INT_MIN;
27 input->dwt = INT_MIN; 26 input->dwt = INT_MIN;
27 input->dwtp = INT_MIN;
28 input->send_events = INT_MIN; 28 input->send_events = INT_MIN;
29 input->click_method = INT_MIN; 29 input->click_method = INT_MIN;
30 input->middle_emulation = INT_MIN; 30 input->middle_emulation = INT_MIN;
31 input->natural_scroll = INT_MIN; 31 input->natural_scroll = INT_MIN;
32 input->accel_profile = INT_MIN; 32 input->accel_profile = INT_MIN;
33 input->rotation_angle = FLT_MIN;
33 input->pointer_accel = FLT_MIN; 34 input->pointer_accel = FLT_MIN;
34 input->scroll_factor = FLT_MIN; 35 input->scroll_factor = FLT_MIN;
35 input->scroll_button = INT_MIN; 36 input->scroll_button = INT_MIN;
37 input->scroll_button_lock = INT_MIN;
36 input->scroll_method = INT_MIN; 38 input->scroll_method = INT_MIN;
37 input->left_handed = INT_MIN; 39 input->left_handed = INT_MIN;
38 input->repeat_delay = INT_MIN; 40 input->repeat_delay = INT_MIN;
@@ -61,6 +63,9 @@ void merge_input_config(struct input_config *dst, struct input_config *src) {
61 if (src->dwt != INT_MIN) { 63 if (src->dwt != INT_MIN) {
62 dst->dwt = src->dwt; 64 dst->dwt = src->dwt;
63 } 65 }
66 if (src->dwtp != INT_MIN) {
67 dst->dwtp = src->dwtp;
68 }
64 if (src->left_handed != INT_MIN) { 69 if (src->left_handed != INT_MIN) {
65 dst->left_handed = src->left_handed; 70 dst->left_handed = src->left_handed;
66 } 71 }
@@ -70,6 +75,9 @@ void merge_input_config(struct input_config *dst, struct input_config *src) {
70 if (src->natural_scroll != INT_MIN) { 75 if (src->natural_scroll != INT_MIN) {
71 dst->natural_scroll = src->natural_scroll; 76 dst->natural_scroll = src->natural_scroll;
72 } 77 }
78 if (src->rotation_angle != FLT_MIN) {
79 dst->rotation_angle = src->rotation_angle;
80 }
73 if (src->pointer_accel != FLT_MIN) { 81 if (src->pointer_accel != FLT_MIN) {
74 dst->pointer_accel = src->pointer_accel; 82 dst->pointer_accel = src->pointer_accel;
75 } 83 }
@@ -88,6 +96,9 @@ void merge_input_config(struct input_config *dst, struct input_config *src) {
88 if (src->scroll_button != INT_MIN) { 96 if (src->scroll_button != INT_MIN) {
89 dst->scroll_button = src->scroll_button; 97 dst->scroll_button = src->scroll_button;
90 } 98 }
99 if (src->scroll_button_lock != INT_MIN) {
100 dst->scroll_button_lock = src->scroll_button_lock;
101 }
91 if (src->send_events != INT_MIN) { 102 if (src->send_events != INT_MIN) {
92 dst->send_events = src->send_events; 103 dst->send_events = src->send_events;
93 } 104 }
diff --git a/sway/config/output.c b/sway/config/output.c
index 7d0ed395..e7299459 100644
--- a/sway/config/output.c
+++ b/sway/config/output.c
@@ -1,13 +1,15 @@
1#define _POSIX_C_SOURCE 200809L
2#include <assert.h> 1#include <assert.h>
2#include <drm_fourcc.h>
3#include <stdbool.h> 3#include <stdbool.h>
4#include <string.h> 4#include <string.h>
5#include <sys/socket.h> 5#include <sys/socket.h>
6#include <sys/wait.h> 6#include <sys/wait.h>
7#include <unistd.h> 7#include <unistd.h>
8#include <wlr/config.h>
8#include <wlr/types/wlr_cursor.h> 9#include <wlr/types/wlr_cursor.h>
9#include <wlr/types/wlr_output_layout.h> 10#include <wlr/types/wlr_output_layout.h>
10#include <wlr/types/wlr_output.h> 11#include <wlr/types/wlr_output.h>
12#include <wlr/types/wlr_output_swapchain_manager.h>
11#include "sway/config.h" 13#include "sway/config.h"
12#include "sway/input/cursor.h" 14#include "sway/input/cursor.h"
13#include "sway/output.h" 15#include "sway/output.h"
@@ -15,6 +17,10 @@
15#include "log.h" 17#include "log.h"
16#include "util.h" 18#include "util.h"
17 19
20#if WLR_HAS_DRM_BACKEND
21#include <wlr/backend/drm.h>
22#endif
23
18int output_name_cmp(const void *item, const void *data) { 24int output_name_cmp(const void *item, const void *data) {
19 const struct output_config *output = item; 25 const struct output_config *output = item;
20 const char *name = data; 26 const char *name = data;
@@ -25,8 +31,10 @@ int output_name_cmp(const void *item, const void *data) {
25void output_get_identifier(char *identifier, size_t len, 31void output_get_identifier(char *identifier, size_t len,
26 struct sway_output *output) { 32 struct sway_output *output) {
27 struct wlr_output *wlr_output = output->wlr_output; 33 struct wlr_output *wlr_output = output->wlr_output;
28 snprintf(identifier, len, "%s %s %s", wlr_output->make, wlr_output->model, 34 snprintf(identifier, len, "%s %s %s",
29 wlr_output->serial); 35 wlr_output->make ? wlr_output->make : "Unknown",
36 wlr_output->model ? wlr_output->model : "Unknown",
37 wlr_output->serial ? wlr_output->serial : "Unknown");
30} 38}
31 39
32const char *sway_output_scale_filter_to_string(enum scale_filter_mode scale_filter) { 40const char *sway_output_scale_filter_to_string(enum scale_filter_mode scale_filter) {
@@ -58,6 +66,7 @@ struct output_config *new_output_config(const char *name) {
58 oc->width = oc->height = -1; 66 oc->width = oc->height = -1;
59 oc->refresh_rate = -1; 67 oc->refresh_rate = -1;
60 oc->custom_mode = -1; 68 oc->custom_mode = -1;
69 oc->drm_mode.type = -1;
61 oc->x = oc->y = -1; 70 oc->x = oc->y = -1;
62 oc->scale = -1; 71 oc->scale = -1;
63 oc->scale_filter = SCALE_FILTER_DEFAULT; 72 oc->scale_filter = SCALE_FILTER_DEFAULT;
@@ -65,10 +74,77 @@ struct output_config *new_output_config(const char *name) {
65 oc->subpixel = WL_OUTPUT_SUBPIXEL_UNKNOWN; 74 oc->subpixel = WL_OUTPUT_SUBPIXEL_UNKNOWN;
66 oc->max_render_time = -1; 75 oc->max_render_time = -1;
67 oc->adaptive_sync = -1; 76 oc->adaptive_sync = -1;
77 oc->render_bit_depth = RENDER_BIT_DEPTH_DEFAULT;
78 oc->power = -1;
68 return oc; 79 return oc;
69} 80}
70 81
71void merge_output_config(struct output_config *dst, struct output_config *src) { 82// supersede_output_config clears all fields in dst that were set in src
83static void supersede_output_config(struct output_config *dst, struct output_config *src) {
84 if (src->enabled != -1) {
85 dst->enabled = -1;
86 }
87 if (src->width != -1) {
88 dst->width = -1;
89 }
90 if (src->height != -1) {
91 dst->height = -1;
92 }
93 if (src->x != -1) {
94 dst->x = -1;
95 }
96 if (src->y != -1) {
97 dst->y = -1;
98 }
99 if (src->scale != -1) {
100 dst->scale = -1;
101 }
102 if (src->scale_filter != SCALE_FILTER_DEFAULT) {
103 dst->scale_filter = SCALE_FILTER_DEFAULT;
104 }
105 if (src->subpixel != WL_OUTPUT_SUBPIXEL_UNKNOWN) {
106 dst->subpixel = WL_OUTPUT_SUBPIXEL_UNKNOWN;
107 }
108 if (src->refresh_rate != -1) {
109 dst->refresh_rate = -1;
110 }
111 if (src->custom_mode != -1) {
112 dst->custom_mode = -1;
113 }
114 if (src->drm_mode.type != (uint32_t) -1) {
115 dst->drm_mode.type = -1;
116 }
117 if (src->transform != -1) {
118 dst->transform = -1;
119 }
120 if (src->max_render_time != -1) {
121 dst->max_render_time = -1;
122 }
123 if (src->adaptive_sync != -1) {
124 dst->adaptive_sync = -1;
125 }
126 if (src->render_bit_depth != RENDER_BIT_DEPTH_DEFAULT) {
127 dst->render_bit_depth = RENDER_BIT_DEPTH_DEFAULT;
128 }
129 if (src->background) {
130 free(dst->background);
131 dst->background = NULL;
132 }
133 if (src->background_option) {
134 free(dst->background_option);
135 dst->background_option = NULL;
136 }
137 if (src->background_fallback) {
138 free(dst->background_fallback);
139 dst->background_fallback = NULL;
140 }
141 if (src->power != -1) {
142 dst->power = -1;
143 }
144}
145
146// merge_output_config sets all fields in dst that were set in src
147static void merge_output_config(struct output_config *dst, struct output_config *src) {
72 if (src->enabled != -1) { 148 if (src->enabled != -1) {
73 dst->enabled = src->enabled; 149 dst->enabled = src->enabled;
74 } 150 }
@@ -99,6 +175,9 @@ void merge_output_config(struct output_config *dst, struct output_config *src) {
99 if (src->custom_mode != -1) { 175 if (src->custom_mode != -1) {
100 dst->custom_mode = src->custom_mode; 176 dst->custom_mode = src->custom_mode;
101 } 177 }
178 if (src->drm_mode.type != (uint32_t) -1) {
179 memcpy(&dst->drm_mode, &src->drm_mode, sizeof(src->drm_mode));
180 }
102 if (src->transform != -1) { 181 if (src->transform != -1) {
103 dst->transform = src->transform; 182 dst->transform = src->transform;
104 } 183 }
@@ -108,6 +187,9 @@ void merge_output_config(struct output_config *dst, struct output_config *src) {
108 if (src->adaptive_sync != -1) { 187 if (src->adaptive_sync != -1) {
109 dst->adaptive_sync = src->adaptive_sync; 188 dst->adaptive_sync = src->adaptive_sync;
110 } 189 }
190 if (src->render_bit_depth != RENDER_BIT_DEPTH_DEFAULT) {
191 dst->render_bit_depth = src->render_bit_depth;
192 }
111 if (src->background) { 193 if (src->background) {
112 free(dst->background); 194 free(dst->background);
113 dst->background = strdup(src->background); 195 dst->background = strdup(src->background);
@@ -120,155 +202,124 @@ void merge_output_config(struct output_config *dst, struct output_config *src) {
120 free(dst->background_fallback); 202 free(dst->background_fallback);
121 dst->background_fallback = strdup(src->background_fallback); 203 dst->background_fallback = strdup(src->background_fallback);
122 } 204 }
123 if (src->dpms_state != 0) { 205 if (src->power != -1) {
124 dst->dpms_state = src->dpms_state; 206 dst->power = src->power;
125 } 207 }
126} 208}
127 209
128static void merge_wildcard_on_all(struct output_config *wildcard) { 210void store_output_config(struct output_config *oc) {
129 for (int i = 0; i < config->output_configs->length; i++) { 211 bool merged = false;
130 struct output_config *oc = config->output_configs->items[i]; 212 bool wildcard = strcmp(oc->name, "*") == 0;
131 if (strcmp(wildcard->name, oc->name) != 0) { 213 struct sway_output *output = wildcard ? NULL : all_output_by_name_or_id(oc->name);
132 sway_log(SWAY_DEBUG, "Merging output * config on %s", oc->name);
133 merge_output_config(oc, wildcard);
134 }
135 }
136}
137 214
138static void merge_id_on_name(struct output_config *oc) {
139 char *id_on_name = NULL;
140 char id[128]; 215 char id[128];
141 char *name = NULL; 216 if (output) {
142 struct sway_output *output;
143 wl_list_for_each(output, &root->all_outputs, link) {
144 name = output->wlr_output->name;
145 output_get_identifier(id, sizeof(id), output); 217 output_get_identifier(id, sizeof(id), output);
146 if (strcmp(name, oc->name) == 0 || strcmp(id, oc->name) == 0) {
147 size_t length = snprintf(NULL, 0, "%s on %s", id, name) + 1;
148 id_on_name = malloc(length);
149 if (!id_on_name) {
150 sway_log(SWAY_ERROR, "Failed to allocate id on name string");
151 return;
152 }
153 snprintf(id_on_name, length, "%s on %s", id, name);
154 break;
155 }
156 } 218 }
157 219
158 if (!id_on_name) { 220 for (int i = 0; i < config->output_configs->length; i++) {
159 return; 221 struct output_config *old = config->output_configs->items[i];
160 } 222
161 223 // If the old config matches the new config's name, regardless of
162 int i = list_seq_find(config->output_configs, output_name_cmp, id_on_name); 224 // whether it was name or identifier, merge on top of the existing
163 if (i >= 0) { 225 // config. If the new config is a wildcard, this also merges on top of
164 sway_log(SWAY_DEBUG, "Merging on top of existing id on name config"); 226 // old wildcard configs.
165 merge_output_config(config->output_configs->items[i], oc); 227 if (strcmp(old->name, oc->name) == 0) {
166 } else { 228 merge_output_config(old, oc);
167 // If both a name and identifier config, exist generate an id on name 229 merged = true;
168 int ni = list_seq_find(config->output_configs, output_name_cmp, name); 230 continue;
169 int ii = list_seq_find(config->output_configs, output_name_cmp, id);
170 if ((ni >= 0 && ii >= 0) || (ni >= 0 && strcmp(oc->name, id) == 0)
171 || (ii >= 0 && strcmp(oc->name, name) == 0)) {
172 struct output_config *ion_oc = new_output_config(id_on_name);
173 if (ni >= 0) {
174 merge_output_config(ion_oc, config->output_configs->items[ni]);
175 }
176 if (ii >= 0) {
177 merge_output_config(ion_oc, config->output_configs->items[ii]);
178 }
179 merge_output_config(ion_oc, oc);
180 list_add(config->output_configs, ion_oc);
181 sway_log(SWAY_DEBUG, "Generated id on name output config \"%s\""
182 " (enabled: %d) (%dx%d@%fHz position %d,%d scale %f "
183 "transform %d) (bg %s %s) (dpms %d) (max render time: %d)",
184 ion_oc->name, ion_oc->enabled, ion_oc->width, ion_oc->height,
185 ion_oc->refresh_rate, ion_oc->x, ion_oc->y, ion_oc->scale,
186 ion_oc->transform, ion_oc->background,
187 ion_oc->background_option, ion_oc->dpms_state,
188 ion_oc->max_render_time);
189 } 231 }
190 }
191 free(id_on_name);
192}
193 232
194struct output_config *store_output_config(struct output_config *oc) { 233 // If the new config is a wildcard config we supersede all non-wildcard
195 bool wildcard = strcmp(oc->name, "*") == 0; 234 // configs. Old wildcard configs have already been handled above.
196 if (wildcard) { 235 if (wildcard) {
197 merge_wildcard_on_all(oc); 236 supersede_output_config(old, oc);
198 } else { 237 continue;
199 merge_id_on_name(oc); 238 }
200 }
201 239
202 int i = list_seq_find(config->output_configs, output_name_cmp, oc->name); 240 // If the new config matches an output's name, and the old config
203 if (i >= 0) { 241 // matches on that output's identifier, supersede it.
204 sway_log(SWAY_DEBUG, "Merging on top of existing output config"); 242 if (output && strcmp(old->name, id) == 0 &&
205 struct output_config *current = config->output_configs->items[i]; 243 strcmp(oc->name, output->wlr_output->name) == 0) {
206 merge_output_config(current, oc); 244 supersede_output_config(old, oc);
207 free_output_config(oc);
208 oc = current;
209 } else if (!wildcard) {
210 sway_log(SWAY_DEBUG, "Adding non-wildcard output config");
211 i = list_seq_find(config->output_configs, output_name_cmp, "*");
212 if (i >= 0) {
213 sway_log(SWAY_DEBUG, "Merging on top of output * config");
214 struct output_config *current = new_output_config(oc->name);
215 merge_output_config(current, config->output_configs->items[i]);
216 merge_output_config(current, oc);
217 free_output_config(oc);
218 oc = current;
219 } 245 }
220 list_add(config->output_configs, oc);
221 } else {
222 // New wildcard config. Just add it
223 sway_log(SWAY_DEBUG, "Adding output * config");
224 list_add(config->output_configs, oc);
225 } 246 }
226 247
227 sway_log(SWAY_DEBUG, "Config stored for output %s (enabled: %d) (%dx%d@%fHz " 248 sway_log(SWAY_DEBUG, "Config stored for output %s (enabled: %d) (%dx%d@%fHz "
228 "position %d,%d scale %f subpixel %s transform %d) (bg %s %s) (dpms %d) " 249 "position %d,%d scale %f subpixel %s transform %d) (bg %s %s) (power %d) "
229 "(max render time: %d)", 250 "(max render time: %d)",
230 oc->name, oc->enabled, oc->width, oc->height, oc->refresh_rate, 251 oc->name, oc->enabled, oc->width, oc->height, oc->refresh_rate,
231 oc->x, oc->y, oc->scale, sway_wl_output_subpixel_to_string(oc->subpixel), 252 oc->x, oc->y, oc->scale, sway_wl_output_subpixel_to_string(oc->subpixel),
232 oc->transform, oc->background, oc->background_option, oc->dpms_state, 253 oc->transform, oc->background, oc->background_option, oc->power,
233 oc->max_render_time); 254 oc->max_render_time);
234 255
235 return oc; 256 // If the configuration was not merged into an existing configuration, add
257 // it to the list. Otherwise we're done with it and can free it.
258 if (!merged) {
259 list_add(config->output_configs, oc);
260 } else {
261 free_output_config(oc);
262 }
236} 263}
237 264
238static void set_mode(struct wlr_output *output, int width, int height, 265static void set_mode(struct wlr_output *output, struct wlr_output_state *pending,
239 float refresh_rate, bool custom) { 266 int width, int height, float refresh_rate, bool custom) {
240 // Not all floating point integers can be represented exactly 267 // Not all floating point integers can be represented exactly
241 // as (int)(1000 * mHz / 1000.f) 268 // as (int)(1000 * mHz / 1000.f)
242 // round() the result to avoid any error 269 // round() the result to avoid any error
243 int mhz = (int)round(refresh_rate * 1000); 270 int mhz = (int)roundf(refresh_rate * 1000);
271 // If no target refresh rate is given, match highest available
272 mhz = mhz <= 0 ? INT_MAX : mhz;
244 273
245 if (wl_list_empty(&output->modes) || custom) { 274 if (wl_list_empty(&output->modes) || custom) {
246 sway_log(SWAY_DEBUG, "Assigning custom mode to %s", output->name); 275 sway_log(SWAY_DEBUG, "Assigning custom mode to %s", output->name);
247 wlr_output_set_custom_mode(output, width, height, 276 wlr_output_state_set_custom_mode(pending, width, height,
248 refresh_rate > 0 ? mhz : 0); 277 refresh_rate > 0 ? mhz : 0);
249 return; 278 return;
250 } 279 }
251 280
252 struct wlr_output_mode *mode, *best = NULL; 281 struct wlr_output_mode *mode, *best = NULL;
282 int best_diff_mhz = INT_MAX;
253 wl_list_for_each(mode, &output->modes, link) { 283 wl_list_for_each(mode, &output->modes, link) {
254 if (mode->width == width && mode->height == height) { 284 if (mode->width == width && mode->height == height) {
255 if (mode->refresh == mhz) { 285 int diff_mhz = abs(mode->refresh - mhz);
256 best = mode; 286 if (diff_mhz < best_diff_mhz) {
257 break; 287 best_diff_mhz = diff_mhz;
258 }
259 if (best == NULL || mode->refresh > best->refresh) {
260 best = mode; 288 best = mode;
289 if (best_diff_mhz == 0) {
290 break;
291 }
261 } 292 }
262 } 293 }
263 } 294 }
264 if (!best) { 295 if (best) {
265 sway_log(SWAY_ERROR, "Configured mode for %s not available", output->name); 296 sway_log(SWAY_INFO, "Assigning configured mode (%dx%d@%.3fHz) to %s",
266 sway_log(SWAY_INFO, "Picking preferred mode instead"); 297 best->width, best->height, best->refresh / 1000.f, output->name);
267 best = wlr_output_preferred_mode(output);
268 } else { 298 } else {
269 sway_log(SWAY_DEBUG, "Assigning configured mode to %s", output->name); 299 best = wlr_output_preferred_mode(output);
300 sway_log(SWAY_INFO, "Configured mode (%dx%d@%.3fHz) not available, "
301 "applying preferred mode (%dx%d@%.3fHz)",
302 width, height, refresh_rate,
303 best->width, best->height, best->refresh / 1000.f);
304 }
305 wlr_output_state_set_mode(pending, best);
306}
307
308static void set_modeline(struct wlr_output *output,
309 struct wlr_output_state *pending, drmModeModeInfo *drm_mode) {
310#if WLR_HAS_DRM_BACKEND
311 if (!wlr_output_is_drm(output)) {
312 sway_log(SWAY_ERROR, "Modeline can only be set to DRM output");
313 return;
270 } 314 }
271 wlr_output_set_mode(output, best); 315 sway_log(SWAY_DEBUG, "Assigning custom modeline to %s", output->name);
316 struct wlr_output_mode *mode = wlr_drm_connector_add_mode(output, drm_mode);
317 if (mode) {
318 wlr_output_state_set_mode(pending, mode);
319 }
320#else
321 sway_log(SWAY_ERROR, "Modeline can only be set to DRM output");
322#endif
272} 323}
273 324
274/* Some manufacturers hardcode the aspect-ratio of the output in the physical 325/* Some manufacturers hardcode the aspect-ratio of the output in the physical
@@ -289,23 +340,24 @@ static bool phys_size_is_aspect_ratio(struct wlr_output *output) {
289// 1 inch = 25.4 mm 340// 1 inch = 25.4 mm
290#define MM_PER_INCH 25.4 341#define MM_PER_INCH 25.4
291 342
292static int compute_default_scale(struct wlr_output *output) { 343static int compute_default_scale(struct wlr_output *output,
344 struct wlr_output_state *pending) {
293 struct wlr_box box = { .width = output->width, .height = output->height }; 345 struct wlr_box box = { .width = output->width, .height = output->height };
294 if (output->pending.committed & WLR_OUTPUT_STATE_MODE) { 346 if (pending->committed & WLR_OUTPUT_STATE_MODE) {
295 switch (output->pending.mode_type) { 347 switch (pending->mode_type) {
296 case WLR_OUTPUT_STATE_MODE_FIXED: 348 case WLR_OUTPUT_STATE_MODE_FIXED:
297 box.width = output->pending.mode->width; 349 box.width = pending->mode->width;
298 box.height = output->pending.mode->height; 350 box.height = pending->mode->height;
299 break; 351 break;
300 case WLR_OUTPUT_STATE_MODE_CUSTOM: 352 case WLR_OUTPUT_STATE_MODE_CUSTOM:
301 box.width = output->pending.custom_mode.width; 353 box.width = pending->custom_mode.width;
302 box.height = output->pending.custom_mode.height; 354 box.height = pending->custom_mode.height;
303 break; 355 break;
304 } 356 }
305 } 357 }
306 enum wl_output_transform transform = output->transform; 358 enum wl_output_transform transform = output->transform;
307 if (output->pending.committed & WLR_OUTPUT_STATE_TRANSFORM) { 359 if (pending->committed & WLR_OUTPUT_STATE_TRANSFORM) {
308 transform = output->pending.transform; 360 transform = pending->transform;
309 } 361 }
310 wlr_box_transform(&box, &box, transform, box.width, box.height); 362 wlr_box_transform(&box, &box, transform, box.width, box.height);
311 363
@@ -334,42 +386,70 @@ static int compute_default_scale(struct wlr_output *output) {
334 return 2; 386 return 2;
335} 387}
336 388
389static bool render_format_is_10bit(uint32_t render_format) {
390 return render_format == DRM_FORMAT_XRGB2101010 ||
391 render_format == DRM_FORMAT_XBGR2101010;
392}
393
394static bool render_format_is_bgr(uint32_t fmt) {
395 return fmt == DRM_FORMAT_XBGR2101010 || fmt == DRM_FORMAT_XBGR8888;
396}
397
398static bool output_config_is_disabling(struct output_config *oc) {
399 return oc && (!oc->enabled || oc->power == 0);
400}
401
337static void queue_output_config(struct output_config *oc, 402static void queue_output_config(struct output_config *oc,
338 struct sway_output *output) { 403 struct sway_output *output, struct wlr_output_state *pending) {
339 if (output == root->noop_output) { 404 if (output == root->fallback_output) {
340 return; 405 return;
341 } 406 }
342 407
343 struct wlr_output *wlr_output = output->wlr_output; 408 struct wlr_output *wlr_output = output->wlr_output;
344 409
345 if (oc && (!oc->enabled || oc->dpms_state == DPMS_OFF)) { 410 if (output_config_is_disabling(oc)) {
346 sway_log(SWAY_DEBUG, "Turning off output %s", wlr_output->name); 411 sway_log(SWAY_DEBUG, "Turning off output %s", wlr_output->name);
347 wlr_output_enable(wlr_output, false); 412 wlr_output_state_set_enabled(pending, false);
348 return; 413 return;
349 } 414 }
350 415
351 sway_log(SWAY_DEBUG, "Turning on output %s", wlr_output->name); 416 sway_log(SWAY_DEBUG, "Turning on output %s", wlr_output->name);
352 wlr_output_enable(wlr_output, true); 417 wlr_output_state_set_enabled(pending, true);
353 418
354 if (oc && oc->width > 0 && oc->height > 0) { 419 if (oc && oc->drm_mode.type != 0 && oc->drm_mode.type != (uint32_t) -1) {
420 sway_log(SWAY_DEBUG, "Set %s modeline",
421 wlr_output->name);
422 set_modeline(wlr_output, pending, &oc->drm_mode);
423 } else if (oc && oc->width > 0 && oc->height > 0) {
355 sway_log(SWAY_DEBUG, "Set %s mode to %dx%d (%f Hz)", 424 sway_log(SWAY_DEBUG, "Set %s mode to %dx%d (%f Hz)",
356 wlr_output->name, oc->width, oc->height, oc->refresh_rate); 425 wlr_output->name, oc->width, oc->height, oc->refresh_rate);
357 set_mode(wlr_output, oc->width, oc->height, 426 set_mode(wlr_output, pending, oc->width, oc->height,
358 oc->refresh_rate, oc->custom_mode == 1); 427 oc->refresh_rate, oc->custom_mode == 1);
359 } else if (!wl_list_empty(&wlr_output->modes)) { 428 } else if (!wl_list_empty(&wlr_output->modes)) {
360 struct wlr_output_mode *mode = wlr_output_preferred_mode(wlr_output); 429 sway_log(SWAY_DEBUG, "Set preferred mode");
361 wlr_output_set_mode(wlr_output, mode); 430 struct wlr_output_mode *preferred_mode =
431 wlr_output_preferred_mode(wlr_output);
432 wlr_output_state_set_mode(pending, preferred_mode);
362 } 433 }
363 434
364 if (oc && (oc->subpixel != WL_OUTPUT_SUBPIXEL_UNKNOWN || config->reloading)) { 435 if (oc && (oc->subpixel != WL_OUTPUT_SUBPIXEL_UNKNOWN || config->reloading)) {
365 sway_log(SWAY_DEBUG, "Set %s subpixel to %s", oc->name, 436 sway_log(SWAY_DEBUG, "Set %s subpixel to %s", oc->name,
366 sway_wl_output_subpixel_to_string(oc->subpixel)); 437 sway_wl_output_subpixel_to_string(oc->subpixel));
367 wlr_output_set_subpixel(wlr_output, oc->subpixel); 438 wlr_output_state_set_subpixel(pending, oc->subpixel);
368 } 439 }
369 440
441 enum wl_output_transform tr = WL_OUTPUT_TRANSFORM_NORMAL;
370 if (oc && oc->transform >= 0) { 442 if (oc && oc->transform >= 0) {
371 sway_log(SWAY_DEBUG, "Set %s transform to %d", oc->name, oc->transform); 443 tr = oc->transform;
372 wlr_output_set_transform(wlr_output, oc->transform); 444#if WLR_HAS_DRM_BACKEND
445 } else if (wlr_output_is_drm(wlr_output)) {
446 tr = wlr_drm_connector_get_panel_orientation(wlr_output);
447 sway_log(SWAY_DEBUG, "Auto-detected output transform: %d", tr);
448#endif
449 }
450 if (wlr_output->transform != tr) {
451 sway_log(SWAY_DEBUG, "Set %s transform to %d", wlr_output->name, tr);
452 wlr_output_state_set_transform(pending, tr);
373 } 453 }
374 454
375 // Apply the scale last before the commit, because the scale auto-detection 455 // Apply the scale last before the commit, because the scale auto-detection
@@ -377,50 +457,50 @@ static void queue_output_config(struct output_config *oc,
377 float scale; 457 float scale;
378 if (oc && oc->scale > 0) { 458 if (oc && oc->scale > 0) {
379 scale = oc->scale; 459 scale = oc->scale;
460
461 // The factional-scale-v1 protocol uses increments of 120ths to send
462 // the scale factor to the client. Adjust the scale so that we use the
463 // same value as the clients'.
464 float adjusted_scale = round(scale * 120) / 120;
465 if (scale != adjusted_scale) {
466 sway_log(SWAY_INFO, "Adjusting output scale from %f to %f",
467 scale, adjusted_scale);
468 scale = adjusted_scale;
469 }
380 } else { 470 } else {
381 scale = compute_default_scale(wlr_output); 471 scale = compute_default_scale(wlr_output, pending);
382 sway_log(SWAY_DEBUG, "Auto-detected output scale: %f", scale); 472 sway_log(SWAY_DEBUG, "Auto-detected output scale: %f", scale);
383 } 473 }
384 if (scale != wlr_output->scale) { 474 if (scale != wlr_output->scale) {
385 sway_log(SWAY_DEBUG, "Set %s scale to %f", wlr_output->name, scale); 475 sway_log(SWAY_DEBUG, "Set %s scale to %f", wlr_output->name, scale);
386 wlr_output_set_scale(wlr_output, scale); 476 wlr_output_state_set_scale(pending, scale);
387 } 477 }
388 478
389 if (oc && oc->adaptive_sync != -1) { 479 if (oc && oc->adaptive_sync != -1) {
390 sway_log(SWAY_DEBUG, "Set %s adaptive sync to %d", wlr_output->name, 480 sway_log(SWAY_DEBUG, "Set %s adaptive sync to %d", wlr_output->name,
391 oc->adaptive_sync); 481 oc->adaptive_sync);
392 wlr_output_enable_adaptive_sync(wlr_output, oc->adaptive_sync == 1); 482 wlr_output_state_set_adaptive_sync_enabled(pending, oc->adaptive_sync == 1);
393 }
394}
395
396bool apply_output_config(struct output_config *oc, struct sway_output *output) {
397 if (output == root->noop_output) {
398 return false;
399 } 483 }
400 484
401 struct wlr_output *wlr_output = output->wlr_output; 485 if (oc && oc->render_bit_depth != RENDER_BIT_DEPTH_DEFAULT) {
402 486 if (oc->render_bit_depth == RENDER_BIT_DEPTH_10 &&
403 // Flag to prevent the output mode event handler from calling us 487 render_format_is_10bit(output->wlr_output->render_format)) {
404 output->enabling = (!oc || oc->enabled); 488 // 10-bit was set successfully before, try to save some tests by reusing the format
405 489 wlr_output_state_set_render_format(pending, output->wlr_output->render_format);
406 queue_output_config(oc, output); 490 } else if (oc->render_bit_depth == RENDER_BIT_DEPTH_10) {
407 491 wlr_output_state_set_render_format(pending, DRM_FORMAT_XRGB2101010);
408 if (!oc || oc->dpms_state != DPMS_OFF) { 492 } else {
409 output->current_mode = wlr_output->pending.mode; 493 wlr_output_state_set_render_format(pending, DRM_FORMAT_XRGB8888);
494 }
410 } 495 }
496}
411 497
412 sway_log(SWAY_DEBUG, "Committing output %s", wlr_output->name); 498static bool finalize_output_config(struct output_config *oc, struct sway_output *output) {
413 if (!wlr_output_commit(wlr_output)) { 499 if (output == root->fallback_output) {
414 // Failed to commit output changes, maybe the output is missing a CRTC.
415 // Leave the output disabled for now and try again when the output gets
416 // the mode we asked for.
417 sway_log(SWAY_ERROR, "Failed to commit output %s", wlr_output->name);
418 output->enabling = false;
419 return false; 500 return false;
420 } 501 }
421 502
422 output->enabling = false; 503 struct wlr_output *wlr_output = output->wlr_output;
423
424 if (oc && !oc->enabled) { 504 if (oc && !oc->enabled) {
425 sway_log(SWAY_DEBUG, "Disabling output %s", oc->name); 505 sway_log(SWAY_DEBUG, "Disabling output %s", oc->name);
426 if (output->enabled) { 506 if (output->enabled) {
@@ -430,10 +510,6 @@ bool apply_output_config(struct output_config *oc, struct sway_output *output) {
430 return true; 510 return true;
431 } 511 }
432 512
433 if (config->reloading) {
434 output_damage_whole(output);
435 }
436
437 if (oc) { 513 if (oc) {
438 enum scale_filter_mode scale_filter_old = output->scale_filter; 514 enum scale_filter_mode scale_filter_old = output->scale_filter;
439 switch (oc->scale_filter) { 515 switch (oc->scale_filter) {
@@ -450,6 +526,7 @@ bool apply_output_config(struct output_config *oc, struct sway_output *output) {
450 if (scale_filter_old != output->scale_filter) { 526 if (scale_filter_old != output->scale_filter) {
451 sway_log(SWAY_DEBUG, "Set %s scale_filter to %s", oc->name, 527 sway_log(SWAY_DEBUG, "Set %s scale_filter to %s", oc->name,
452 sway_output_scale_filter_to_string(output->scale_filter)); 528 sway_output_scale_filter_to_string(output->scale_filter));
529 wlr_damage_ring_add_whole(&output->scene_output->damage_ring);
453 } 530 }
454 } 531 }
455 532
@@ -462,12 +539,12 @@ bool apply_output_config(struct output_config *oc, struct sway_output *output) {
462 } 539 }
463 540
464 // Update output->{lx, ly, width, height} 541 // Update output->{lx, ly, width, height}
465 struct wlr_box *output_box = 542 struct wlr_box output_box;
466 wlr_output_layout_get_box(root->output_layout, wlr_output); 543 wlr_output_layout_get_box(root->output_layout, wlr_output, &output_box);
467 output->lx = output_box->x; 544 output->lx = output_box.x;
468 output->ly = output_box->y; 545 output->ly = output_box.y;
469 output->width = output_box->width; 546 output->width = output_box.width;
470 output->height = output_box->height; 547 output->height = output_box.height;
471 548
472 if (!output->enabled) { 549 if (!output->enabled) {
473 output_enable(output); 550 output_enable(output);
@@ -479,29 +556,13 @@ bool apply_output_config(struct output_config *oc, struct sway_output *output) {
479 output->max_render_time = oc->max_render_time; 556 output->max_render_time = oc->max_render_time;
480 } 557 }
481 558
482 // Reconfigure all devices, since input config may have been applied before
483 // this output came online, and some config items (like map_to_output) are
484 // dependent on an output being present.
485 input_manager_configure_all_inputs();
486 // Reconfigure the cursor images, since the scale may have changed.
487 input_manager_configure_xcursor();
488 return true; 559 return true;
489} 560}
490 561
491bool test_output_config(struct output_config *oc, struct sway_output *output) {
492 if (output == root->noop_output) {
493 return false;
494 }
495
496 queue_output_config(oc, output);
497 bool ok = wlr_output_test(output->wlr_output);
498 wlr_output_rollback(output->wlr_output);
499 return ok;
500}
501
502static void default_output_config(struct output_config *oc, 562static void default_output_config(struct output_config *oc,
503 struct wlr_output *wlr_output) { 563 struct wlr_output *wlr_output) {
504 oc->enabled = 1; 564 oc->enabled = 1;
565 oc->power = 1;
505 struct wlr_output_mode *mode = wlr_output_preferred_mode(wlr_output); 566 struct wlr_output_mode *mode = wlr_output_preferred_mode(wlr_output);
506 if (mode != NULL) { 567 if (mode != NULL) {
507 oc->width = mode->width; 568 oc->width = mode->width;
@@ -514,147 +575,418 @@ static void default_output_config(struct output_config *oc,
514 struct sway_output *output = wlr_output->data; 575 struct sway_output *output = wlr_output->data;
515 oc->subpixel = output->detected_subpixel; 576 oc->subpixel = output->detected_subpixel;
516 oc->transform = WL_OUTPUT_TRANSFORM_NORMAL; 577 oc->transform = WL_OUTPUT_TRANSFORM_NORMAL;
517 oc->dpms_state = DPMS_ON;
518 oc->max_render_time = 0; 578 oc->max_render_time = 0;
519} 579}
520 580
521static struct output_config *get_output_config(char *identifier, 581// find_output_config returns a merged output_config containing all stored
522 struct sway_output *sway_output) { 582// configuration that applies to the specified output.
583struct output_config *find_output_config(struct sway_output *sway_output) {
523 const char *name = sway_output->wlr_output->name; 584 const char *name = sway_output->wlr_output->name;
585 struct output_config *oc = NULL;
586
587 struct output_config *result = new_output_config(name);
588 if (config->reloading) {
589 default_output_config(result, sway_output->wlr_output);
590 }
524 591
525 struct output_config *oc_id_on_name = NULL; 592 char id[128];
526 struct output_config *oc_name = NULL; 593 output_get_identifier(id, sizeof(id), sway_output);
527 struct output_config *oc_id = NULL;
528 594
529 size_t length = snprintf(NULL, 0, "%s on %s", identifier, name) + 1; 595 int i;
530 char *id_on_name = malloc(length); 596 bool match = false;
531 snprintf(id_on_name, length, "%s on %s", identifier, name); 597 if ((i = list_seq_find(config->output_configs, output_name_cmp, "*")) >= 0) {
532 int i = list_seq_find(config->output_configs, output_name_cmp, id_on_name); 598 match = true;
533 if (i >= 0) { 599 oc = config->output_configs->items[i];
534 oc_id_on_name = config->output_configs->items[i]; 600 merge_output_config(result, oc);
535 } else { 601 }
536 i = list_seq_find(config->output_configs, output_name_cmp, name); 602 if ((i = list_seq_find(config->output_configs, output_name_cmp, name)) >= 0) {
537 if (i >= 0) { 603 match = true;
538 oc_name = config->output_configs->items[i]; 604 oc = config->output_configs->items[i];
605 merge_output_config(result, oc);
606 }
607 if ((i = list_seq_find(config->output_configs, output_name_cmp, id)) >= 0) {
608 match = true;
609 oc = config->output_configs->items[i];
610 merge_output_config(result, oc);
611 }
612
613 if (!match && !config->reloading) {
614 // No name, identifier, or wildcard config. Since we are not
615 // reloading with defaults, the output config will be empty, so
616 // just return NULL
617 free_output_config(result);
618 return NULL;
619 }
620
621 return result;
622}
623
624static bool config_has_auto_mode(struct output_config *oc) {
625 if (!oc) {
626 return true;
627 }
628 if (oc->drm_mode.type != 0 && oc->drm_mode.type != (uint32_t)-1) {
629 return true;
630 } else if (oc->width > 0 && oc->height > 0) {
631 return true;
632 }
633 return false;
634}
635
636struct search_context {
637 struct wlr_output_swapchain_manager *swapchain_mgr;
638 struct wlr_backend_output_state *states;
639 struct matched_output_config *configs;
640 size_t configs_len;
641 bool degrade_to_off;
642};
643
644static void dump_output_state(struct wlr_output *wlr_output, struct wlr_output_state *state) {
645 sway_log(SWAY_DEBUG, "Output state for %s", wlr_output->name);
646 if (state->committed & WLR_OUTPUT_STATE_ENABLED) {
647 sway_log(SWAY_DEBUG, " enabled: %s", state->enabled ? "yes" : "no");
648 }
649 if (state->committed & WLR_OUTPUT_STATE_RENDER_FORMAT) {
650 sway_log(SWAY_DEBUG, " render_format: %d", state->render_format);
651 }
652 if (state->committed & WLR_OUTPUT_STATE_MODE) {
653 if (state->mode_type == WLR_OUTPUT_STATE_MODE_CUSTOM) {
654 sway_log(SWAY_DEBUG, " custom mode: %dx%d@%dmHz",
655 state->custom_mode.width, state->custom_mode.height, state->custom_mode.refresh);
656 } else {
657 sway_log(SWAY_DEBUG, " mode: %dx%d@%dmHz%s",
658 state->mode->width, state->mode->height, state->mode->refresh,
659 state->mode->preferred ? " (preferred)" : "");
539 } 660 }
661 }
662 if (state->committed & WLR_OUTPUT_STATE_ADAPTIVE_SYNC_ENABLED) {
663 sway_log(SWAY_DEBUG, " adaptive_sync: %s",
664 state->adaptive_sync_enabled ? "enabled": "disabled");
665 }
666}
667
668static bool search_valid_config(struct search_context *ctx, size_t output_idx);
669
670static void reset_output_state(struct wlr_output_state *state) {
671 wlr_output_state_finish(state);
672 wlr_output_state_init(state);
673 state->committed = 0;
674}
540 675
541 i = list_seq_find(config->output_configs, output_name_cmp, identifier); 676static void clear_later_output_states(struct wlr_backend_output_state *states,
542 if (i >= 0) { 677 size_t configs_len, size_t output_idx) {
543 oc_id = config->output_configs->items[i]; 678
679 // Clear and disable all output states after this one to avoid conflict
680 // with previous tests.
681 for (size_t idx = output_idx+1; idx < configs_len; idx++) {
682 struct wlr_backend_output_state *backend_state = &states[idx];
683 struct wlr_output_state *state = &backend_state->base;
684
685 reset_output_state(state);
686 wlr_output_state_set_enabled(state, false);
687 }
688}
689
690static bool search_finish(struct search_context *ctx, size_t output_idx) {
691 struct wlr_backend_output_state *backend_state = &ctx->states[output_idx];
692 struct wlr_output_state *state = &backend_state->base;
693 struct wlr_output *wlr_output = backend_state->output;
694
695 clear_later_output_states(ctx->states, ctx->configs_len, output_idx);
696 dump_output_state(wlr_output, state);
697 return wlr_output_swapchain_manager_prepare(ctx->swapchain_mgr, ctx->states, ctx->configs_len) &&
698 search_valid_config(ctx, output_idx+1);
699}
700
701static bool search_adaptive_sync(struct search_context *ctx, size_t output_idx) {
702 struct matched_output_config *cfg = &ctx->configs[output_idx];
703 struct wlr_backend_output_state *backend_state = &ctx->states[output_idx];
704 struct wlr_output_state *state = &backend_state->base;
705
706 if (cfg->config && cfg->config->adaptive_sync == 1) {
707 wlr_output_state_set_adaptive_sync_enabled(state, true);
708 if (search_finish(ctx, output_idx)) {
709 return true;
710 }
711 }
712 if (!cfg->config || cfg->config->adaptive_sync != -1) {
713 wlr_output_state_set_adaptive_sync_enabled(state, false);
714 if (search_finish(ctx, output_idx)) {
715 return true;
544 } 716 }
545 } 717 }
718 // If adaptive sync has not been set, or fallback in case we are on a
719 // backend that cannot disable adaptive sync such as the wayland backend.
720 state->committed &= ~WLR_OUTPUT_STATE_ADAPTIVE_SYNC_ENABLED;
721 return search_finish(ctx, output_idx);
722}
546 723
547 struct output_config *result = new_output_config("temp"); 724static bool search_mode(struct search_context *ctx, size_t output_idx) {
548 if (config->reloading) { 725 struct matched_output_config *cfg = &ctx->configs[output_idx];
549 default_output_config(result, sway_output->wlr_output); 726 struct wlr_backend_output_state *backend_state = &ctx->states[output_idx];
727 struct wlr_output_state *state = &backend_state->base;
728 struct wlr_output *wlr_output = backend_state->output;
729
730 if (!config_has_auto_mode(cfg->config)) {
731 return search_adaptive_sync(ctx, output_idx);
550 } 732 }
551 if (oc_id_on_name) { 733
552 // Already have an identifier on name config, use that 734 struct wlr_output_mode *preferred_mode = wlr_output_preferred_mode(wlr_output);
553 free(result->name); 735 if (preferred_mode) {
554 result->name = strdup(id_on_name); 736 wlr_output_state_set_mode(state, preferred_mode);
555 merge_output_config(result, oc_id_on_name); 737 if (search_adaptive_sync(ctx, output_idx)) {
556 } else if (oc_name && oc_id) { 738 return true;
557 // Generate a config named `<identifier> on <name>` which contains a
558 // merged copy of the identifier on name. This will make sure that both
559 // identifier and name configs are respected, with identifier getting
560 // priority
561 struct output_config *temp = new_output_config(id_on_name);
562 merge_output_config(temp, oc_name);
563 merge_output_config(temp, oc_id);
564 list_add(config->output_configs, temp);
565
566 free(result->name);
567 result->name = strdup(id_on_name);
568 merge_output_config(result, temp);
569
570 sway_log(SWAY_DEBUG, "Generated output config \"%s\" (enabled: %d)"
571 " (%dx%d@%fHz position %d,%d scale %f transform %d) (bg %s %s)"
572 " (dpms %d) (max render time: %d)", result->name, result->enabled,
573 result->width, result->height, result->refresh_rate,
574 result->x, result->y, result->scale, result->transform,
575 result->background, result->background_option, result->dpms_state,
576 result->max_render_time);
577 } else if (oc_name) {
578 // No identifier config, just return a copy of the name config
579 free(result->name);
580 result->name = strdup(name);
581 merge_output_config(result, oc_name);
582 } else if (oc_id) {
583 // No name config, just return a copy of the identifier config
584 free(result->name);
585 result->name = strdup(identifier);
586 merge_output_config(result, oc_id);
587 } else {
588 i = list_seq_find(config->output_configs, output_name_cmp, "*");
589 if (i >= 0) {
590 // No name or identifier config, but there is a wildcard config
591 free(result->name);
592 result->name = strdup("*");
593 merge_output_config(result, config->output_configs->items[i]);
594 } else if (!config->reloading) {
595 // No name, identifier, or wildcard config. Since we are not
596 // reloading with defaults, the output config will be empty, so
597 // just return NULL
598 free_output_config(result);
599 result = NULL;
600 } 739 }
601 } 740 }
602 741
603 free(id_on_name); 742 if (wl_list_empty(&wlr_output->modes)) {
604 return result; 743 state->committed &= ~WLR_OUTPUT_STATE_MODE;
744 return search_adaptive_sync(ctx, output_idx);
745 }
746
747 struct wlr_output_mode *mode;
748 wl_list_for_each(mode, &backend_state->output->modes, link) {
749 if (mode == preferred_mode) {
750 continue;
751 }
752 wlr_output_state_set_mode(state, mode);
753 if (search_adaptive_sync(ctx, output_idx)) {
754 return true;
755 }
756 }
757
758 return false;
605} 759}
606 760
607struct output_config *find_output_config(struct sway_output *output) { 761static bool search_render_format(struct search_context *ctx, size_t output_idx) {
608 char id[128]; 762 struct matched_output_config *cfg = &ctx->configs[output_idx];
609 output_get_identifier(id, sizeof(id), output); 763 struct wlr_backend_output_state *backend_state = &ctx->states[output_idx];
610 return get_output_config(id, output); 764 struct wlr_output_state *state = &backend_state->base;
765 struct wlr_output *wlr_output = backend_state->output;
766
767 uint32_t fmts[] = {
768 DRM_FORMAT_XRGB2101010,
769 DRM_FORMAT_XBGR2101010,
770 DRM_FORMAT_XRGB8888,
771 DRM_FORMAT_INVALID,
772 };
773 if (render_format_is_bgr(wlr_output->render_format)) {
774 // Start with BGR in the unlikely event that we previously required it.
775 fmts[0] = DRM_FORMAT_XBGR2101010;
776 fmts[1] = DRM_FORMAT_XRGB2101010;
777 }
778
779 const struct wlr_drm_format_set *primary_formats =
780 wlr_output_get_primary_formats(wlr_output, WLR_BUFFER_CAP_DMABUF);
781 bool need_10bit = cfg->config && cfg->config->render_bit_depth == RENDER_BIT_DEPTH_10;
782 for (size_t idx = 0; fmts[idx] != DRM_FORMAT_INVALID; idx++) {
783 if (!need_10bit && render_format_is_10bit(fmts[idx])) {
784 continue;
785 }
786 if (!wlr_drm_format_set_get(primary_formats, fmts[idx])) {
787 // This is not a supported format for this output
788 continue;
789 }
790 wlr_output_state_set_render_format(state, fmts[idx]);
791 if (search_mode(ctx, output_idx)) {
792 return true;
793 }
794 }
795 return false;
611} 796}
612 797
613void apply_output_config_to_outputs(struct output_config *oc) { 798static bool search_valid_config(struct search_context *ctx, size_t output_idx) {
614 // Try to find the output container and apply configuration now. If 799 if (output_idx >= ctx->configs_len) {
615 // this is during startup then there will be no container and config 800 // We reached the end of the search, all good!
616 // will be applied during normal "new output" event from wlroots. 801 return true;
617 bool wildcard = strcmp(oc->name, "*") == 0; 802 }
618 char id[128];
619 struct sway_output *sway_output, *tmp;
620 wl_list_for_each_safe(sway_output, tmp, &root->all_outputs, link) {
621 char *name = sway_output->wlr_output->name;
622 output_get_identifier(id, sizeof(id), sway_output);
623 if (wildcard || !strcmp(name, oc->name) || !strcmp(id, oc->name)) {
624 struct output_config *current = get_output_config(id, sway_output);
625 if (!current) {
626 // No stored output config matched, apply oc directly
627 sway_log(SWAY_DEBUG, "Applying oc directly");
628 current = new_output_config(oc->name);
629 merge_output_config(current, oc);
630 }
631 apply_output_config(current, sway_output);
632 free_output_config(current);
633 803
634 if (!wildcard) { 804 struct matched_output_config *cfg = &ctx->configs[output_idx];
635 // Stop looking if the output config isn't applicable to all 805 struct wlr_backend_output_state *backend_state = &ctx->states[output_idx];
636 // outputs 806 struct wlr_output_state *state = &backend_state->base;
637 break; 807 struct wlr_output *wlr_output = backend_state->output;
638 } 808
809 if (!output_config_is_disabling(cfg->config)) {
810 // Search through our possible configurations, doing a depth-first
811 // through render_format, modes, adaptive_sync and the next output's
812 // config.
813 queue_output_config(cfg->config, cfg->output, &backend_state->base);
814 if (search_render_format(ctx, output_idx)) {
815 return true;
816 } else if (!ctx->degrade_to_off) {
817 return false;
818 }
819 // We could not get anything to work, try to disable this output to see
820 // if we can at least make the outputs before us work.
821 sway_log(SWAY_DEBUG, "Unable to find valid config with output %s, disabling",
822 wlr_output->name);
823 reset_output_state(state);
824 }
825
826 wlr_output_state_set_enabled(state, false);
827 return search_finish(ctx, output_idx);
828}
829
830static int compare_matched_output_config_priority(const void *a, const void *b) {
831
832 const struct matched_output_config *amc = a;
833 const struct matched_output_config *bmc = b;
834 bool a_disabling = output_config_is_disabling(amc->config);
835 bool b_disabling = output_config_is_disabling(bmc->config);
836 bool a_enabled = amc->output->enabled;
837 bool b_enabled = bmc->output->enabled;
838
839 // We want to give priority to existing enabled outputs. To do so, we want
840 // the configuration order to be:
841 // 1. Existing, enabled outputs
842 // 2. Outputs that need to be enabled
843 // 3. Disabled or disabling outputs
844 if (a_enabled && !a_disabling) {
845 return -1;
846 } else if (b_enabled && !b_disabling) {
847 return 1;
848 } else if (b_disabling && !a_disabling) {
849 return -1;
850 } else if (a_disabling && !b_disabling) {
851 return 1;
852 }
853 return 0;
854}
855
856void sort_output_configs_by_priority(struct matched_output_config *configs,
857 size_t configs_len) {
858 qsort(configs, configs_len, sizeof(*configs), compare_matched_output_config_priority);
859}
860
861bool apply_output_configs(struct matched_output_config *configs,
862 size_t configs_len, bool test_only, bool degrade_to_off) {
863 struct wlr_backend_output_state *states = calloc(configs_len, sizeof(*states));
864 if (!states) {
865 return false;
866 }
867
868 sway_log(SWAY_DEBUG, "Committing %zd outputs", configs_len);
869 for (size_t idx = 0; idx < configs_len; idx++) {
870 struct matched_output_config *cfg = &configs[idx];
871 struct wlr_backend_output_state *backend_state = &states[idx];
872
873 backend_state->output = cfg->output->wlr_output;
874 wlr_output_state_init(&backend_state->base);
875
876 sway_log(SWAY_DEBUG, "Preparing config for %s",
877 cfg->output->wlr_output->name);
878 queue_output_config(cfg->config, cfg->output, &backend_state->base);
879 }
880
881 struct wlr_output_swapchain_manager swapchain_mgr;
882 wlr_output_swapchain_manager_init(&swapchain_mgr, server.backend);
883
884 bool ok = wlr_output_swapchain_manager_prepare(&swapchain_mgr, states, configs_len);
885 if (!ok) {
886 sway_log(SWAY_ERROR, "Requested backend configuration failed, searching for valid fallbacks");
887 struct search_context ctx = {
888 .swapchain_mgr = &swapchain_mgr,
889 .states = states,
890 .configs = configs,
891 .configs_len = configs_len,
892 .degrade_to_off = degrade_to_off,
893 };
894 if (!search_valid_config(&ctx, 0)) {
895 sway_log(SWAY_ERROR, "Search for valid config failed");
896 goto out;
639 } 897 }
640 } 898 }
641 899
900 if (test_only) {
901 // The swapchain manager already did a test for us
902 goto out;
903 }
904
905 for (size_t idx = 0; idx < configs_len; idx++) {
906 struct matched_output_config *cfg = &configs[idx];
907 struct wlr_backend_output_state *backend_state = &states[idx];
908
909 struct wlr_scene_output_state_options opts = {
910 .swapchain = wlr_output_swapchain_manager_get_swapchain(
911 &swapchain_mgr, backend_state->output),
912 };
913 struct wlr_scene_output *scene_output = cfg->output->scene_output;
914 struct wlr_output_state *state = &backend_state->base;
915 if (!wlr_scene_output_build_state(scene_output, state, &opts)) {
916 sway_log(SWAY_ERROR, "Building output state for '%s' failed",
917 backend_state->output->name);
918 goto out;
919 }
920 }
921
922 ok = wlr_backend_commit(server.backend, states, configs_len);
923 if (!ok) {
924 sway_log(SWAY_ERROR, "Backend commit failed");
925 goto out;
926 }
927
928 sway_log(SWAY_DEBUG, "Commit of %zd outputs succeeded", configs_len);
929
930 wlr_output_swapchain_manager_apply(&swapchain_mgr);
931
932 for (size_t idx = 0; idx < configs_len; idx++) {
933 struct matched_output_config *cfg = &configs[idx];
934 sway_log(SWAY_DEBUG, "Finalizing config for %s",
935 cfg->output->wlr_output->name);
936 finalize_output_config(cfg->config, cfg->output);
937 }
938
939out:
940 wlr_output_swapchain_manager_finish(&swapchain_mgr);
941 for (size_t idx = 0; idx < configs_len; idx++) {
942 struct wlr_backend_output_state *backend_state = &states[idx];
943 wlr_output_state_finish(&backend_state->base);
944 }
945 free(states);
946
947 // Reconfigure all devices, since input config may have been applied before
948 // this output came online, and some config items (like map_to_output) are
949 // dependent on an output being present.
950 input_manager_configure_all_input_mappings();
951 // Reconfigure the cursor images, since the scale may have changed.
952 input_manager_configure_xcursor();
953
642 struct sway_seat *seat; 954 struct sway_seat *seat;
643 wl_list_for_each(seat, &server.input->seats, link) { 955 wl_list_for_each(seat, &server.input->seats, link) {
644 wlr_seat_pointer_notify_clear_focus(seat->wlr_seat); 956 wlr_seat_pointer_notify_clear_focus(seat->wlr_seat);
645 cursor_rebase(seat->cursor); 957 cursor_rebase(seat->cursor);
646 } 958 }
959
960 return ok;
647} 961}
648 962
649void reset_outputs(void) { 963void apply_all_output_configs(void) {
650 struct output_config *oc = NULL; 964 size_t configs_len = wl_list_length(&root->all_outputs);
651 int i = list_seq_find(config->output_configs, output_name_cmp, "*"); 965 struct matched_output_config *configs = calloc(configs_len, sizeof(*configs));
652 if (i >= 0) { 966 if (!configs) {
653 oc = config->output_configs->items[i]; 967 return;
654 } else {
655 oc = store_output_config(new_output_config("*"));
656 } 968 }
657 apply_output_config_to_outputs(oc); 969
970 int config_idx = 0;
971 struct sway_output *sway_output;
972 wl_list_for_each(sway_output, &root->all_outputs, link) {
973 if (sway_output == root->fallback_output) {
974 configs_len--;
975 continue;
976 }
977
978 struct matched_output_config *config = &configs[config_idx++];
979 config->output = sway_output;
980 config->config = find_output_config(sway_output);
981 }
982
983 sort_output_configs_by_priority(configs, configs_len);
984 apply_output_configs(configs, configs_len, false, true);
985 for (size_t idx = 0; idx < configs_len; idx++) {
986 struct matched_output_config *cfg = &configs[idx];
987 free_output_config(cfg->config);
988 }
989 free(configs);
658} 990}
659 991
660void free_output_config(struct output_config *oc) { 992void free_output_config(struct output_config *oc) {
@@ -704,6 +1036,8 @@ static bool _spawn_swaybg(char **command) {
704 sway_log_errno(SWAY_ERROR, "fork failed"); 1036 sway_log_errno(SWAY_ERROR, "fork failed");
705 return false; 1037 return false;
706 } else if (pid == 0) { 1038 } else if (pid == 0) {
1039 restore_nofile_limit();
1040
707 pid = fork(); 1041 pid = fork();
708 if (pid < 0) { 1042 if (pid < 0) {
709 sway_log_errno(SWAY_ERROR, "fork failed"); 1043 sway_log_errno(SWAY_ERROR, "fork failed");
@@ -719,7 +1053,9 @@ static bool _spawn_swaybg(char **command) {
719 setenv("WAYLAND_SOCKET", wayland_socket_str, true); 1053 setenv("WAYLAND_SOCKET", wayland_socket_str, true);
720 1054
721 execvp(command[0], command); 1055 execvp(command[0], command);
722 sway_log_errno(SWAY_ERROR, "execvp failed"); 1056 sway_log_errno(SWAY_ERROR, "failed to execute '%s' "
1057 "(background configuration probably not applied)",
1058 command[0]);
723 _exit(EXIT_FAILURE); 1059 _exit(EXIT_FAILURE);
724 } 1060 }
725 _exit(EXIT_SUCCESS); 1061 _exit(EXIT_SUCCESS);
@@ -729,12 +1065,13 @@ static bool _spawn_swaybg(char **command) {
729 sway_log_errno(SWAY_ERROR, "close failed"); 1065 sway_log_errno(SWAY_ERROR, "close failed");
730 return false; 1066 return false;
731 } 1067 }
732 if (waitpid(pid, NULL, 0) < 0) { 1068 int fork_status = 0;
1069 if (waitpid(pid, &fork_status, 0) < 0) {
733 sway_log_errno(SWAY_ERROR, "waitpid failed"); 1070 sway_log_errno(SWAY_ERROR, "waitpid failed");
734 return false; 1071 return false;
735 } 1072 }
736 1073
737 return true; 1074 return WIFEXITED(fork_status) && WEXITSTATUS(fork_status) == EXIT_SUCCESS;
738} 1075}
739 1076
740bool spawn_swaybg(void) { 1077bool spawn_swaybg(void) {
diff --git a/sway/config/seat.c b/sway/config/seat.c
index 84260aa3..f2326189 100644
--- a/sway/config/seat.c
+++ b/sway/config/seat.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <limits.h> 1#include <limits.h>
3#include <stdlib.h> 2#include <stdlib.h>
4#include <string.h> 3#include <string.h>
@@ -99,7 +98,6 @@ static void seat_attachment_config_free(
99 struct seat_attachment_config *attachment) { 98 struct seat_attachment_config *attachment) {
100 free(attachment->identifier); 99 free(attachment->identifier);
101 free(attachment); 100 free(attachment);
102 return;
103} 101}
104 102
105static struct seat_attachment_config *seat_attachment_config_copy( 103static struct seat_attachment_config *seat_attachment_config_copy(
diff --git a/sway/criteria.c b/sway/criteria.c
index d2a5566f..13f0530e 100644
--- a/sway/criteria.c
+++ b/sway/criteria.c
@@ -1,9 +1,9 @@
1#define _POSIX_C_SOURCE 200809L
2#include <stdlib.h> 1#include <stdlib.h>
3#include <stdio.h> 2#include <stdio.h>
4#include <stdbool.h> 3#include <stdbool.h>
5#include <strings.h> 4#include <strings.h>
6#include <pcre.h> 5#define PCRE2_CODE_UNIT_WIDTH 8
6#include <pcre2.h>
7#include "sway/criteria.h" 7#include "sway/criteria.h"
8#include "sway/tree/container.h" 8#include "sway/tree/container.h"
9#include "sway/config.h" 9#include "sway/config.h"
@@ -18,10 +18,11 @@
18bool criteria_is_empty(struct criteria *criteria) { 18bool criteria_is_empty(struct criteria *criteria) {
19 return !criteria->title 19 return !criteria->title
20 && !criteria->shell 20 && !criteria->shell
21 && !criteria->all
21 && !criteria->app_id 22 && !criteria->app_id
22 && !criteria->con_mark 23 && !criteria->con_mark
23 && !criteria->con_id 24 && !criteria->con_id
24#if HAVE_XWAYLAND 25#if WLR_HAS_XWAYLAND
25 && !criteria->class 26 && !criteria->class
26 && !criteria->id 27 && !criteria->id
27 && !criteria->instance 28 && !criteria->instance
@@ -40,17 +41,19 @@ bool criteria_is_empty(struct criteria *criteria) {
40char *error = NULL; 41char *error = NULL;
41 42
42// Returns error string on failure or NULL otherwise. 43// Returns error string on failure or NULL otherwise.
43static bool generate_regex(pcre **regex, char *value) { 44static bool generate_regex(pcre2_code **regex, char *value) {
44 const char *reg_err; 45 int errorcode;
45 int offset; 46 PCRE2_SIZE offset;
46
47 *regex = pcre_compile(value, PCRE_UTF8 | PCRE_UCP, &reg_err, &offset, NULL);
48 47
48 *regex = pcre2_compile((PCRE2_SPTR)value, PCRE2_ZERO_TERMINATED, PCRE2_UTF | PCRE2_UCP, &errorcode, &offset, NULL);
49 if (!*regex) { 49 if (!*regex) {
50 PCRE2_UCHAR buffer[256];
51 pcre2_get_error_message(errorcode, buffer, sizeof(buffer));
52
50 const char *fmt = "Regex compilation for '%s' failed: %s"; 53 const char *fmt = "Regex compilation for '%s' failed: %s";
51 int len = strlen(fmt) + strlen(value) + strlen(reg_err) - 3; 54 int len = strlen(fmt) + strlen(value) + strlen((char*) buffer) - 3;
52 error = malloc(len); 55 error = malloc(len);
53 snprintf(error, len, fmt, value, reg_err); 56 snprintf(error, len, fmt, value, buffer);
54 return false; 57 return false;
55 } 58 }
56 59
@@ -66,7 +69,7 @@ static bool pattern_create(struct pattern **pattern, char *value) {
66 if (strcmp(value, "__focused__") == 0) { 69 if (strcmp(value, "__focused__") == 0) {
67 (*pattern)->match_type = PATTERN_FOCUSED; 70 (*pattern)->match_type = PATTERN_FOCUSED;
68 } else { 71 } else {
69 (*pattern)->match_type = PATTERN_PCRE; 72 (*pattern)->match_type = PATTERN_PCRE2;
70 if (!generate_regex(&(*pattern)->regex, value)) { 73 if (!generate_regex(&(*pattern)->regex, value)) {
71 return false; 74 return false;
72 }; 75 };
@@ -77,7 +80,7 @@ static bool pattern_create(struct pattern **pattern, char *value) {
77static void pattern_destroy(struct pattern *pattern) { 80static void pattern_destroy(struct pattern *pattern) {
78 if (pattern) { 81 if (pattern) {
79 if (pattern->regex) { 82 if (pattern->regex) {
80 pcre_free(pattern->regex); 83 pcre2_code_free(pattern->regex);
81 } 84 }
82 free(pattern); 85 free(pattern);
83 } 86 }
@@ -87,23 +90,27 @@ void criteria_destroy(struct criteria *criteria) {
87 pattern_destroy(criteria->title); 90 pattern_destroy(criteria->title);
88 pattern_destroy(criteria->shell); 91 pattern_destroy(criteria->shell);
89 pattern_destroy(criteria->app_id); 92 pattern_destroy(criteria->app_id);
90#if HAVE_XWAYLAND 93#if WLR_HAS_XWAYLAND
91 pattern_destroy(criteria->class); 94 pattern_destroy(criteria->class);
92 pattern_destroy(criteria->instance); 95 pattern_destroy(criteria->instance);
93 pattern_destroy(criteria->window_role); 96 pattern_destroy(criteria->window_role);
94#endif 97#endif
95 pattern_destroy(criteria->con_mark); 98 pattern_destroy(criteria->con_mark);
96 free(criteria->workspace); 99 pattern_destroy(criteria->workspace);
100 free(criteria->target);
97 free(criteria->cmdlist); 101 free(criteria->cmdlist);
98 free(criteria->raw); 102 free(criteria->raw);
99 free(criteria); 103 free(criteria);
100} 104}
101 105
102static int regex_cmp(const char *item, const pcre *regex) { 106static int regex_cmp(const char *item, const pcre2_code *regex) {
103 return pcre_exec(regex, NULL, item, strlen(item), 0, 0, NULL, 0); 107 pcre2_match_data *match_data = pcre2_match_data_create_from_pattern(regex, NULL);
108 int result = pcre2_match(regex, (PCRE2_SPTR)item, strlen(item), 0, 0, match_data, NULL);
109 pcre2_match_data_free(match_data);
110 return result;
104} 111}
105 112
106#if HAVE_XWAYLAND 113#if WLR_HAS_XWAYLAND
107static bool view_has_window_type(struct sway_view *view, enum atom_name name) { 114static bool view_has_window_type(struct sway_view *view, enum atom_name name) {
108 if (view->type != SWAY_VIEW_XWAYLAND) { 115 if (view->type != SWAY_VIEW_XWAYLAND) {
109 return false; 116 return false;
@@ -155,7 +162,7 @@ static bool criteria_matches_container(struct criteria *criteria,
155 bool exists = false; 162 bool exists = false;
156 struct sway_container *con = container; 163 struct sway_container *con = container;
157 for (int i = 0; i < con->marks->length; ++i) { 164 for (int i = 0; i < con->marks->length; ++i) {
158 if (regex_cmp(con->marks->items[i], criteria->con_mark->regex) == 0) { 165 if (regex_cmp(con->marks->items[i], criteria->con_mark->regex) >= 0) {
159 exists = true; 166 exists = true;
160 break; 167 break;
161 } 168 }
@@ -183,7 +190,7 @@ static bool criteria_matches_view(struct criteria *criteria,
183 if (criteria->title) { 190 if (criteria->title) {
184 const char *title = view_get_title(view); 191 const char *title = view_get_title(view);
185 if (!title) { 192 if (!title) {
186 return false; 193 title = "";
187 } 194 }
188 195
189 switch (criteria->title->match_type) { 196 switch (criteria->title->match_type) {
@@ -192,8 +199,8 @@ static bool criteria_matches_view(struct criteria *criteria,
192 return false; 199 return false;
193 } 200 }
194 break; 201 break;
195 case PATTERN_PCRE: 202 case PATTERN_PCRE2:
196 if (regex_cmp(title, criteria->title->regex) != 0) { 203 if (regex_cmp(title, criteria->title->regex) < 0) {
197 return false; 204 return false;
198 } 205 }
199 break; 206 break;
@@ -203,7 +210,7 @@ static bool criteria_matches_view(struct criteria *criteria,
203 if (criteria->shell) { 210 if (criteria->shell) {
204 const char *shell = view_get_shell(view); 211 const char *shell = view_get_shell(view);
205 if (!shell) { 212 if (!shell) {
206 return false; 213 shell = "";
207 } 214 }
208 215
209 switch (criteria->shell->match_type) { 216 switch (criteria->shell->match_type) {
@@ -212,8 +219,8 @@ static bool criteria_matches_view(struct criteria *criteria,
212 return false; 219 return false;
213 } 220 }
214 break; 221 break;
215 case PATTERN_PCRE: 222 case PATTERN_PCRE2:
216 if (regex_cmp(shell, criteria->shell->regex) != 0) { 223 if (regex_cmp(shell, criteria->shell->regex) < 0) {
217 return false; 224 return false;
218 } 225 }
219 break; 226 break;
@@ -223,7 +230,7 @@ static bool criteria_matches_view(struct criteria *criteria,
223 if (criteria->app_id) { 230 if (criteria->app_id) {
224 const char *app_id = view_get_app_id(view); 231 const char *app_id = view_get_app_id(view);
225 if (!app_id) { 232 if (!app_id) {
226 return false; 233 app_id = "";
227 } 234 }
228 235
229 switch (criteria->app_id->match_type) { 236 switch (criteria->app_id->match_type) {
@@ -232,8 +239,8 @@ static bool criteria_matches_view(struct criteria *criteria,
232 return false; 239 return false;
233 } 240 }
234 break; 241 break;
235 case PATTERN_PCRE: 242 case PATTERN_PCRE2:
236 if (regex_cmp(app_id, criteria->app_id->regex) != 0) { 243 if (regex_cmp(app_id, criteria->app_id->regex) < 0) {
237 return false; 244 return false;
238 } 245 }
239 break; 246 break;
@@ -244,7 +251,7 @@ static bool criteria_matches_view(struct criteria *criteria,
244 return false; 251 return false;
245 } 252 }
246 253
247#if HAVE_XWAYLAND 254#if WLR_HAS_XWAYLAND
248 if (criteria->id) { // X11 window ID 255 if (criteria->id) { // X11 window ID
249 uint32_t x11_window_id = view_get_x11_window_id(view); 256 uint32_t x11_window_id = view_get_x11_window_id(view);
250 if (!x11_window_id || x11_window_id != criteria->id) { 257 if (!x11_window_id || x11_window_id != criteria->id) {
@@ -255,7 +262,7 @@ static bool criteria_matches_view(struct criteria *criteria,
255 if (criteria->class) { 262 if (criteria->class) {
256 const char *class = view_get_class(view); 263 const char *class = view_get_class(view);
257 if (!class) { 264 if (!class) {
258 return false; 265 class = "";
259 } 266 }
260 267
261 switch (criteria->class->match_type) { 268 switch (criteria->class->match_type) {
@@ -264,8 +271,8 @@ static bool criteria_matches_view(struct criteria *criteria,
264 return false; 271 return false;
265 } 272 }
266 break; 273 break;
267 case PATTERN_PCRE: 274 case PATTERN_PCRE2:
268 if (regex_cmp(class, criteria->class->regex) != 0) { 275 if (regex_cmp(class, criteria->class->regex) < 0) {
269 return false; 276 return false;
270 } 277 }
271 break; 278 break;
@@ -275,17 +282,17 @@ static bool criteria_matches_view(struct criteria *criteria,
275 if (criteria->instance) { 282 if (criteria->instance) {
276 const char *instance = view_get_instance(view); 283 const char *instance = view_get_instance(view);
277 if (!instance) { 284 if (!instance) {
278 return false; 285 instance = "";
279 } 286 }
280 287
281 switch (criteria->instance->match_type) { 288 switch (criteria->instance->match_type) {
282 case PATTERN_FOCUSED: 289 case PATTERN_FOCUSED:
283 if (focused && strcmp(instance, view_get_instance(focused))) { 290 if (focused && lenient_strcmp(instance, view_get_instance(focused))) {
284 return false; 291 return false;
285 } 292 }
286 break; 293 break;
287 case PATTERN_PCRE: 294 case PATTERN_PCRE2:
288 if (regex_cmp(instance, criteria->instance->regex) != 0) { 295 if (regex_cmp(instance, criteria->instance->regex) < 0) {
289 return false; 296 return false;
290 } 297 }
291 break; 298 break;
@@ -295,17 +302,17 @@ static bool criteria_matches_view(struct criteria *criteria,
295 if (criteria->window_role) { 302 if (criteria->window_role) {
296 const char *window_role = view_get_window_role(view); 303 const char *window_role = view_get_window_role(view);
297 if (!window_role) { 304 if (!window_role) {
298 return false; 305 window_role = "";
299 } 306 }
300 307
301 switch (criteria->window_role->match_type) { 308 switch (criteria->window_role->match_type) {
302 case PATTERN_FOCUSED: 309 case PATTERN_FOCUSED:
303 if (focused && strcmp(window_role, view_get_window_role(focused))) { 310 if (focused && lenient_strcmp(window_role, view_get_window_role(focused))) {
304 return false; 311 return false;
305 } 312 }
306 break; 313 break;
307 case PATTERN_PCRE: 314 case PATTERN_PCRE2:
308 if (regex_cmp(window_role, criteria->window_role->regex) != 0) { 315 if (regex_cmp(window_role, criteria->window_role->regex) < 0) {
309 return false; 316 return false;
310 } 317 }
311 break; 318 break;
@@ -363,8 +370,8 @@ static bool criteria_matches_view(struct criteria *criteria,
363 return false; 370 return false;
364 } 371 }
365 break; 372 break;
366 case PATTERN_PCRE: 373 case PATTERN_PCRE2:
367 if (regex_cmp(ws->name, criteria->workspace->regex) != 0) { 374 if (regex_cmp(ws->name, criteria->workspace->regex) < 0) {
368 return false; 375 return false;
369 } 376 }
370 break; 377 break;
@@ -421,7 +428,7 @@ list_t *criteria_get_containers(struct criteria *criteria) {
421 return matches; 428 return matches;
422} 429}
423 430
424#if HAVE_XWAYLAND 431#if WLR_HAS_XWAYLAND
425static enum atom_name parse_window_type(const char *type) { 432static enum atom_name parse_window_type(const char *type) {
426 if (strcasecmp(type, "normal") == 0) { 433 if (strcasecmp(type, "normal") == 0) {
427 return NET_WM_WINDOW_TYPE_NORMAL; 434 return NET_WM_WINDOW_TYPE_NORMAL;
@@ -449,11 +456,12 @@ static enum atom_name parse_window_type(const char *type) {
449#endif 456#endif
450 457
451enum criteria_token { 458enum criteria_token {
459 T_ALL,
452 T_APP_ID, 460 T_APP_ID,
453 T_CON_ID, 461 T_CON_ID,
454 T_CON_MARK, 462 T_CON_MARK,
455 T_FLOATING, 463 T_FLOATING,
456#if HAVE_XWAYLAND 464#if WLR_HAS_XWAYLAND
457 T_CLASS, 465 T_CLASS,
458 T_ID, 466 T_ID,
459 T_INSTANCE, 467 T_INSTANCE,
@@ -471,13 +479,15 @@ enum criteria_token {
471}; 479};
472 480
473static enum criteria_token token_from_name(char *name) { 481static enum criteria_token token_from_name(char *name) {
474 if (strcmp(name, "app_id") == 0) { 482 if (strcmp(name, "all") == 0) {
483 return T_ALL;
484 } else if (strcmp(name, "app_id") == 0) {
475 return T_APP_ID; 485 return T_APP_ID;
476 } else if (strcmp(name, "con_id") == 0) { 486 } else if (strcmp(name, "con_id") == 0) {
477 return T_CON_ID; 487 return T_CON_ID;
478 } else if (strcmp(name, "con_mark") == 0) { 488 } else if (strcmp(name, "con_mark") == 0) {
479 return T_CON_MARK; 489 return T_CON_MARK;
480#if HAVE_XWAYLAND 490#if WLR_HAS_XWAYLAND
481 } else if (strcmp(name, "class") == 0) { 491 } else if (strcmp(name, "class") == 0) {
482 return T_CLASS; 492 return T_CLASS;
483 } else if (strcmp(name, "id") == 0) { 493 } else if (strcmp(name, "id") == 0) {
@@ -517,8 +527,8 @@ static bool parse_token(struct criteria *criteria, char *name, char *value) {
517 return false; 527 return false;
518 } 528 }
519 529
520 // Require value, unless token is floating or tiled 530 // Require value, unless token is all, floating or tiled
521 if (!value && token != T_FLOATING && token != T_TILING) { 531 if (!value && token != T_ALL && token != T_FLOATING && token != T_TILING) {
522 const char *fmt = "Token '%s' requires a value"; 532 const char *fmt = "Token '%s' requires a value";
523 int len = strlen(fmt) + strlen(name) - 1; 533 int len = strlen(fmt) + strlen(name) - 1;
524 error = malloc(len); 534 error = malloc(len);
@@ -528,6 +538,9 @@ static bool parse_token(struct criteria *criteria, char *name, char *value) {
528 538
529 char *endptr = NULL; 539 char *endptr = NULL;
530 switch (token) { 540 switch (token) {
541 case T_ALL:
542 criteria->all = true;
543 break;
531 case T_TITLE: 544 case T_TITLE:
532 pattern_create(&criteria->title, value); 545 pattern_create(&criteria->title, value);
533 break; 546 break;
@@ -553,7 +566,7 @@ static bool parse_token(struct criteria *criteria, char *name, char *value) {
553 case T_CON_MARK: 566 case T_CON_MARK:
554 pattern_create(&criteria->con_mark, value); 567 pattern_create(&criteria->con_mark, value);
555 break; 568 break;
556#if HAVE_XWAYLAND 569#if WLR_HAS_XWAYLAND
557 case T_CLASS: 570 case T_CLASS:
558 pattern_create(&criteria->class, value); 571 pattern_create(&criteria->class, value);
559 break; 572 break;
@@ -661,7 +674,7 @@ struct criteria *criteria_parse(char *raw, char **error_arg) {
661 ++head; 674 ++head;
662 675
663 struct criteria *criteria = calloc(1, sizeof(struct criteria)); 676 struct criteria *criteria = calloc(1, sizeof(struct criteria));
664#if HAVE_XWAYLAND 677#if WLR_HAS_XWAYLAND
665 criteria->window_type = ATOM_LAST; // default value 678 criteria->window_type = ATOM_LAST; // default value
666#endif 679#endif
667 char *name = NULL, *value = NULL; 680 char *name = NULL, *value = NULL;
@@ -676,7 +689,7 @@ struct criteria *criteria_parse(char *raw, char **error_arg) {
676 } 689 }
677 name = calloc(head - namestart + 1, 1); 690 name = calloc(head - namestart + 1, 1);
678 if (head != namestart) { 691 if (head != namestart) {
679 strncpy(name, namestart, head - namestart); 692 memcpy(name, namestart, head - namestart);
680 } 693 }
681 // Parse token value 694 // Parse token value
682 skip_spaces(&head); 695 skip_spaces(&head);
@@ -703,7 +716,7 @@ struct criteria *criteria_parse(char *raw, char **error_arg) {
703 } 716 }
704 } 717 }
705 value = calloc(head - valuestart + 1, 1); 718 value = calloc(head - valuestart + 1, 1);
706 strncpy(value, valuestart, head - valuestart); 719 memcpy(value, valuestart, head - valuestart);
707 if (in_quotes) { 720 if (in_quotes) {
708 ++head; 721 ++head;
709 in_quotes = false; 722 in_quotes = false;
@@ -734,7 +747,7 @@ struct criteria *criteria_parse(char *raw, char **error_arg) {
734 ++head; 747 ++head;
735 int len = head - raw; 748 int len = head - raw;
736 criteria->raw = calloc(len + 1, 1); 749 criteria->raw = calloc(len + 1, 1);
737 strncpy(criteria->raw, raw, len); 750 memcpy(criteria->raw, raw, len);
738 return criteria; 751 return criteria;
739 752
740cleanup: 753cleanup:
diff --git a/sway/desktop/desktop.c b/sway/desktop/desktop.c
deleted file mode 100644
index ec45d80a..00000000
--- a/sway/desktop/desktop.c
+++ /dev/null
@@ -1,39 +0,0 @@
1#include "sway/tree/container.h"
2#include "sway/desktop.h"
3#include "sway/output.h"
4
5void desktop_damage_surface(struct wlr_surface *surface, double lx, double ly,
6 bool whole) {
7 for (int i = 0; i < root->outputs->length; ++i) {
8 struct sway_output *output = root->outputs->items[i];
9 struct wlr_box *output_box = wlr_output_layout_get_box(
10 root->output_layout, output->wlr_output);
11 output_damage_surface(output, lx - output_box->x,
12 ly - output_box->y, surface, whole);
13 }
14}
15
16void desktop_damage_whole_container(struct sway_container *con) {
17 for (int i = 0; i < root->outputs->length; ++i) {
18 struct sway_output *output = root->outputs->items[i];
19 output_damage_whole_container(output, con);
20 }
21}
22
23void desktop_damage_box(struct wlr_box *box) {
24 for (int i = 0; i < root->outputs->length; ++i) {
25 struct sway_output *output = root->outputs->items[i];
26 output_damage_box(output, box);
27 }
28}
29
30void desktop_damage_view(struct sway_view *view) {
31 desktop_damage_whole_container(view->container);
32 struct wlr_box box = {
33 .x = view->container->current.content_x - view->geometry.x,
34 .y = view->container->current.content_y - view->geometry.y,
35 .width = view->surface->current.width,
36 .height = view->surface->current.height,
37 };
38 desktop_damage_box(&box);
39}
diff --git a/sway/desktop/idle_inhibit_v1.c b/sway/desktop/idle_inhibit_v1.c
index a6ad7166..f3af7aa1 100644
--- a/sway/desktop/idle_inhibit_v1.c
+++ b/sway/desktop/idle_inhibit_v1.c
@@ -1,5 +1,5 @@
1#include <stdlib.h> 1#include <stdlib.h>
2#include <wlr/types/wlr_idle.h> 2#include <wlr/types/wlr_idle_notify_v1.h>
3#include "log.h" 3#include "log.h"
4#include "sway/desktop/idle_inhibit_v1.h" 4#include "sway/desktop/idle_inhibit_v1.h"
5#include "sway/input/seat.h" 5#include "sway/input/seat.h"
@@ -11,7 +11,7 @@
11static void destroy_inhibitor(struct sway_idle_inhibitor_v1 *inhibitor) { 11static void destroy_inhibitor(struct sway_idle_inhibitor_v1 *inhibitor) {
12 wl_list_remove(&inhibitor->link); 12 wl_list_remove(&inhibitor->link);
13 wl_list_remove(&inhibitor->destroy.link); 13 wl_list_remove(&inhibitor->destroy.link);
14 sway_idle_inhibit_v1_check_active(inhibitor->manager); 14 sway_idle_inhibit_v1_check_active();
15 free(inhibitor); 15 free(inhibitor);
16} 16}
17 17
@@ -34,7 +34,6 @@ void handle_idle_inhibitor_v1(struct wl_listener *listener, void *data) {
34 return; 34 return;
35 } 35 }
36 36
37 inhibitor->manager = manager;
38 inhibitor->mode = INHIBIT_IDLE_APPLICATION; 37 inhibitor->mode = INHIBIT_IDLE_APPLICATION;
39 inhibitor->wlr_inhibitor = wlr_inhibitor; 38 inhibitor->wlr_inhibitor = wlr_inhibitor;
40 wl_list_insert(&manager->inhibitors, &inhibitor->link); 39 wl_list_insert(&manager->inhibitors, &inhibitor->link);
@@ -42,35 +41,36 @@ void handle_idle_inhibitor_v1(struct wl_listener *listener, void *data) {
42 inhibitor->destroy.notify = handle_destroy; 41 inhibitor->destroy.notify = handle_destroy;
43 wl_signal_add(&wlr_inhibitor->events.destroy, &inhibitor->destroy); 42 wl_signal_add(&wlr_inhibitor->events.destroy, &inhibitor->destroy);
44 43
45 sway_idle_inhibit_v1_check_active(manager); 44 sway_idle_inhibit_v1_check_active();
46} 45}
47 46
48void sway_idle_inhibit_v1_user_inhibitor_register(struct sway_view *view, 47void sway_idle_inhibit_v1_user_inhibitor_register(struct sway_view *view,
49 enum sway_idle_inhibit_mode mode) { 48 enum sway_idle_inhibit_mode mode) {
49 struct sway_idle_inhibit_manager_v1 *manager = &server.idle_inhibit_manager_v1;
50
50 struct sway_idle_inhibitor_v1 *inhibitor = 51 struct sway_idle_inhibitor_v1 *inhibitor =
51 calloc(1, sizeof(struct sway_idle_inhibitor_v1)); 52 calloc(1, sizeof(struct sway_idle_inhibitor_v1));
52 if (!inhibitor) { 53 if (!inhibitor) {
53 return; 54 return;
54 } 55 }
55 56
56 inhibitor->manager = server.idle_inhibit_manager_v1;
57 inhibitor->mode = mode; 57 inhibitor->mode = mode;
58 inhibitor->view = view; 58 inhibitor->view = view;
59 wl_list_insert(&inhibitor->manager->inhibitors, &inhibitor->link); 59 wl_list_insert(&manager->inhibitors, &inhibitor->link);
60 60
61 inhibitor->destroy.notify = handle_destroy; 61 inhibitor->destroy.notify = handle_destroy;
62 wl_signal_add(&view->events.unmap, &inhibitor->destroy); 62 wl_signal_add(&view->events.unmap, &inhibitor->destroy);
63 63
64 sway_idle_inhibit_v1_check_active(inhibitor->manager); 64 sway_idle_inhibit_v1_check_active();
65} 65}
66 66
67struct sway_idle_inhibitor_v1 *sway_idle_inhibit_v1_user_inhibitor_for_view( 67struct sway_idle_inhibitor_v1 *sway_idle_inhibit_v1_user_inhibitor_for_view(
68 struct sway_view *view) { 68 struct sway_view *view) {
69 struct sway_idle_inhibit_manager_v1 *manager = &server.idle_inhibit_manager_v1;
69 struct sway_idle_inhibitor_v1 *inhibitor; 70 struct sway_idle_inhibitor_v1 *inhibitor;
70 wl_list_for_each(inhibitor, &server.idle_inhibit_manager_v1->inhibitors, 71 wl_list_for_each(inhibitor, &manager->inhibitors, link) {
71 link) { 72 if (inhibitor->mode != INHIBIT_IDLE_APPLICATION &&
72 if (inhibitor->view == view && 73 inhibitor->view == view) {
73 inhibitor->mode != INHIBIT_IDLE_APPLICATION) {
74 return inhibitor; 74 return inhibitor;
75 } 75 }
76 } 76 }
@@ -79,11 +79,11 @@ struct sway_idle_inhibitor_v1 *sway_idle_inhibit_v1_user_inhibitor_for_view(
79 79
80struct sway_idle_inhibitor_v1 *sway_idle_inhibit_v1_application_inhibitor_for_view( 80struct sway_idle_inhibitor_v1 *sway_idle_inhibit_v1_application_inhibitor_for_view(
81 struct sway_view *view) { 81 struct sway_view *view) {
82 struct sway_idle_inhibit_manager_v1 *manager = &server.idle_inhibit_manager_v1;
82 struct sway_idle_inhibitor_v1 *inhibitor; 83 struct sway_idle_inhibitor_v1 *inhibitor;
83 wl_list_for_each(inhibitor, &server.idle_inhibit_manager_v1->inhibitors, 84 wl_list_for_each(inhibitor, &manager->inhibitors, link) {
84 link) { 85 if (inhibitor->mode == INHIBIT_IDLE_APPLICATION &&
85 if (inhibitor->view == view && 86 view_from_wlr_surface(inhibitor->wlr_inhibitor->surface) == view) {
86 inhibitor->mode == INHIBIT_IDLE_APPLICATION) {
87 return inhibitor; 87 return inhibitor;
88 } 88 }
89 } 89 }
@@ -130,8 +130,8 @@ bool sway_idle_inhibit_v1_is_active(struct sway_idle_inhibitor_v1 *inhibitor) {
130 return false; 130 return false;
131} 131}
132 132
133void sway_idle_inhibit_v1_check_active( 133void sway_idle_inhibit_v1_check_active(void) {
134 struct sway_idle_inhibit_manager_v1 *manager) { 134 struct sway_idle_inhibit_manager_v1 *manager = &server.idle_inhibit_manager_v1;
135 struct sway_idle_inhibitor_v1 *inhibitor; 135 struct sway_idle_inhibitor_v1 *inhibitor;
136 bool inhibited = false; 136 bool inhibited = false;
137 wl_list_for_each(inhibitor, &manager->inhibitors, link) { 137 wl_list_for_each(inhibitor, &manager->inhibitors, link) {
@@ -139,27 +139,21 @@ void sway_idle_inhibit_v1_check_active(
139 break; 139 break;
140 } 140 }
141 } 141 }
142 wlr_idle_set_enabled(manager->idle, NULL, !inhibited); 142 wlr_idle_notifier_v1_set_inhibited(server.idle_notifier_v1, inhibited);
143} 143}
144 144
145struct sway_idle_inhibit_manager_v1 *sway_idle_inhibit_manager_v1_create( 145bool sway_idle_inhibit_manager_v1_init(void) {
146 struct wl_display *wl_display, struct wlr_idle *idle) { 146 struct sway_idle_inhibit_manager_v1 *manager = &server.idle_inhibit_manager_v1;
147 struct sway_idle_inhibit_manager_v1 *manager =
148 calloc(1, sizeof(struct sway_idle_inhibit_manager_v1));
149 if (!manager) {
150 return NULL;
151 }
152 147
153 manager->wlr_manager = wlr_idle_inhibit_v1_create(wl_display); 148 manager->wlr_manager = wlr_idle_inhibit_v1_create(server.wl_display);
154 if (!manager->wlr_manager) { 149 if (!manager->wlr_manager) {
155 free(manager); 150 return false;
156 return NULL;
157 } 151 }
158 manager->idle = idle; 152
159 wl_signal_add(&manager->wlr_manager->events.new_inhibitor, 153 wl_signal_add(&manager->wlr_manager->events.new_inhibitor,
160 &manager->new_idle_inhibitor_v1); 154 &manager->new_idle_inhibitor_v1);
161 manager->new_idle_inhibitor_v1.notify = handle_idle_inhibitor_v1; 155 manager->new_idle_inhibitor_v1.notify = handle_idle_inhibitor_v1;
162 wl_list_init(&manager->inhibitors); 156 wl_list_init(&manager->inhibitors);
163 157
164 return manager; 158 return true;
165} 159}
diff --git a/sway/desktop/launcher.c b/sway/desktop/launcher.c
new file mode 100644
index 00000000..28043d19
--- /dev/null
+++ b/sway/desktop/launcher.c
@@ -0,0 +1,267 @@
1#include <stdlib.h>
2#include <string.h>
3#include <wlr/types/wlr_xdg_activation_v1.h>
4#include "sway/input/seat.h"
5#include "sway/output.h"
6#include "sway/desktop/launcher.h"
7#include "sway/tree/node.h"
8#include "sway/tree/container.h"
9#include "sway/tree/workspace.h"
10#include "sway/tree/root.h"
11#include "log.h"
12
13/**
14 * Get the pid of a parent process given the pid of a child process.
15 *
16 * Returns the parent pid or NULL if the parent pid cannot be determined.
17 */
18static pid_t get_parent_pid(pid_t child) {
19 pid_t parent = -1;
20 char file_name[100];
21 char *buffer = NULL;
22 const char *sep = " ";
23 FILE *stat = NULL;
24 size_t buf_size = 0;
25
26 snprintf(file_name, sizeof(file_name), "/proc/%d/stat", child);
27
28 if ((stat = fopen(file_name, "r"))) {
29 if (getline(&buffer, &buf_size, stat) != -1) {
30 strtok(buffer, sep); // pid
31 strtok(NULL, sep); // executable name
32 strtok(NULL, sep); // state
33 char *token = strtok(NULL, sep); // parent pid
34 parent = strtol(token, NULL, 10);
35 }
36 free(buffer);
37 fclose(stat);
38 }
39
40 if (parent) {
41 return (parent == child) ? -1 : parent;
42 }
43
44 return -1;
45}
46
47void launcher_ctx_consume(struct launcher_ctx *ctx) {
48 // The view is now responsible for destroying this ctx
49 wl_list_remove(&ctx->token_destroy.link);
50 wl_list_init(&ctx->token_destroy.link);
51
52 if (!ctx->activated) {
53 // An unactivated token hasn't been destroyed yet
54 wlr_xdg_activation_token_v1_destroy(ctx->token);
55 }
56 ctx->token = NULL;
57
58 // Prevent additional matches
59 wl_list_remove(&ctx->link);
60 wl_list_init(&ctx->link);
61}
62
63void launcher_ctx_destroy(struct launcher_ctx *ctx) {
64 if (ctx == NULL) {
65 return;
66 }
67 wl_list_remove(&ctx->node_destroy.link);
68 wl_list_remove(&ctx->token_destroy.link);
69 if (ctx->seat) {
70 wl_list_remove(&ctx->seat_destroy.link);
71 }
72 wl_list_remove(&ctx->link);
73 wlr_xdg_activation_token_v1_destroy(ctx->token);
74 free(ctx->fallback_name);
75 free(ctx);
76}
77
78struct launcher_ctx *launcher_ctx_find_pid(pid_t pid) {
79 if (wl_list_empty(&server.pending_launcher_ctxs)) {
80 return NULL;
81 }
82
83 struct launcher_ctx *ctx = NULL;
84 sway_log(SWAY_DEBUG, "Looking up workspace for pid %d", pid);
85
86 do {
87 struct launcher_ctx *_ctx = NULL;
88 wl_list_for_each(_ctx, &server.pending_launcher_ctxs, link) {
89 if (pid == _ctx->pid) {
90 ctx = _ctx;
91 sway_log(SWAY_DEBUG,
92 "found %s match for pid %d: %s",
93 node_type_to_str(ctx->node->type), pid, node_get_name(ctx->node));
94 break;
95 }
96 }
97 pid = get_parent_pid(pid);
98 } while (pid > 1);
99
100 return ctx;
101}
102
103struct sway_workspace *launcher_ctx_get_workspace(
104 struct launcher_ctx *ctx) {
105 struct sway_workspace *ws = NULL;
106 struct sway_output *output = NULL;
107
108 switch (ctx->node->type) {
109 case N_CONTAINER:
110 // Unimplemented
111 // TODO: add container matching?
112 ws = ctx->node->sway_container->pending.workspace;
113 break;
114 case N_WORKSPACE:
115 ws = ctx->node->sway_workspace;
116 break;
117 case N_OUTPUT:
118 output = ctx->node->sway_output;
119 ws = workspace_by_name(ctx->fallback_name);
120 if (!ws) {
121 sway_log(SWAY_DEBUG,
122 "Creating workspace %s for pid %d because it disappeared",
123 ctx->fallback_name, ctx->pid);
124 if (!output->enabled) {
125 sway_log(SWAY_DEBUG,
126 "Workspace output %s is disabled, trying another one",
127 output->wlr_output->name);
128 output = NULL;
129 }
130 ws = workspace_create(output, ctx->fallback_name);
131 }
132 break;
133 case N_ROOT:
134 ws = workspace_create(NULL, ctx->fallback_name);
135 break;
136 }
137
138 return ws;
139}
140
141static void ctx_handle_node_destroy(struct wl_listener *listener, void *data) {
142 struct launcher_ctx *ctx = wl_container_of(listener, ctx, node_destroy);
143 switch (ctx->node->type) {
144 case N_CONTAINER:
145 // Unimplemented
146 break;
147 case N_WORKSPACE:;
148 struct sway_workspace *ws = ctx->node->sway_workspace;
149 wl_list_remove(&ctx->node_destroy.link);
150 wl_list_init(&ctx->node_destroy.link);
151 // We want to save this ws name to recreate later, hopefully on the
152 // same output
153 free(ctx->fallback_name);
154 ctx->fallback_name = strdup(ws->name);
155 if (!ws->output || ws->output->node.destroying) {
156 // If the output is being destroyed it would be pointless to track
157 // If the output is being disabled, we'll find out if it's still
158 // disabled when we try to match it.
159 ctx->node = &root->node;
160 break;
161 }
162 ctx->node = &ws->output->node;
163 wl_signal_add(&ctx->node->events.destroy, &ctx->node_destroy);
164 break;
165 case N_OUTPUT:
166 wl_list_remove(&ctx->node_destroy.link);
167 wl_list_init(&ctx->node_destroy.link);
168 // We'll make the ws ctx->name somewhere else
169 ctx->node = &root->node;
170 break;
171 case N_ROOT:
172 // Unreachable
173 break;
174 }
175}
176
177static void token_handle_destroy(struct wl_listener *listener, void *data) {
178 struct launcher_ctx *ctx = wl_container_of(listener, ctx, token_destroy);
179 ctx->token = NULL;
180 launcher_ctx_destroy(ctx);
181}
182
183struct launcher_ctx *launcher_ctx_create(struct wlr_xdg_activation_token_v1 *token,
184 struct sway_node *node) {
185 struct launcher_ctx *ctx = calloc(1, sizeof(struct launcher_ctx));
186
187 const char *fallback_name = NULL;
188 struct sway_workspace *ws = NULL;
189 switch (node->type) {
190 case N_CONTAINER:
191 // Unimplemented
192 free(ctx);
193 return NULL;
194 case N_WORKSPACE:
195 ws = node->sway_workspace;
196 fallback_name = ws->name;
197 break;
198 case N_OUTPUT:;
199 struct sway_output *output = node->sway_output;
200 ws = output_get_active_workspace(output);
201 fallback_name = ws ? ws->name : NULL;
202 break;
203 case N_ROOT:
204 // Unimplemented
205 free(ctx);
206 return NULL;
207 }
208
209 if (!fallback_name) {
210 // TODO: implement a better fallback.
211 free(ctx);
212 return NULL;
213 }
214
215 ctx->fallback_name = strdup(fallback_name);
216 ctx->token = token;
217 ctx->node = node;
218 // Having surface set means that the focus check in wlroots has passed
219 ctx->had_focused_surface = token->surface != NULL;
220
221 ctx->node_destroy.notify = ctx_handle_node_destroy;
222 wl_signal_add(&ctx->node->events.destroy, &ctx->node_destroy);
223
224 ctx->token_destroy.notify = token_handle_destroy;
225 wl_signal_add(&token->events.destroy, &ctx->token_destroy);
226
227 wl_list_init(&ctx->link);
228 wl_list_insert(&server.pending_launcher_ctxs, &ctx->link);
229
230 token->data = ctx;
231 return ctx;
232}
233
234static void launch_ctx_handle_seat_destroy(struct wl_listener *listener, void *data) {
235 struct launcher_ctx *ctx = wl_container_of(listener, ctx, seat_destroy);
236 ctx->seat = NULL;
237 wl_list_remove(&ctx->seat_destroy.link);
238}
239
240// Creates a context with a new token for the internal launcher
241struct launcher_ctx *launcher_ctx_create_internal(void) {
242 struct sway_seat *seat = input_manager_current_seat();
243 struct sway_workspace *ws = seat_get_focused_workspace(seat);
244 if (!ws) {
245 sway_log(SWAY_DEBUG, "Failed to create launch context. No workspace.");
246 return NULL;
247 }
248
249 struct wlr_xdg_activation_token_v1 *token =
250 wlr_xdg_activation_token_v1_create(server.xdg_activation_v1);
251
252 struct launcher_ctx *ctx = launcher_ctx_create(token, &ws->node);
253 if (!ctx) {
254 wlr_xdg_activation_token_v1_destroy(token);
255 return NULL;
256 }
257 ctx->seat = seat;
258 ctx->seat_destroy.notify = launch_ctx_handle_seat_destroy;
259 wl_signal_add(&seat->wlr_seat->events.destroy, &ctx->seat_destroy);
260
261 return ctx;
262}
263
264const char *launcher_ctx_get_token_name(struct launcher_ctx *ctx) {
265 const char *token = wlr_xdg_activation_token_v1_get_name(ctx->token);
266 return token;
267}
diff --git a/sway/desktop/layer_shell.c b/sway/desktop/layer_shell.c
index cadc702a..6221b7b9 100644
--- a/sway/desktop/layer_shell.c
+++ b/sway/desktop/layer_shell.c
@@ -2,10 +2,14 @@
2#include <stdlib.h> 2#include <stdlib.h>
3#include <string.h> 3#include <string.h>
4#include <wayland-server-core.h> 4#include <wayland-server-core.h>
5#include <wlr/types/wlr_fractional_scale_v1.h>
5#include <wlr/types/wlr_layer_shell_v1.h> 6#include <wlr/types/wlr_layer_shell_v1.h>
6#include <wlr/types/wlr_output_damage.h>
7#include <wlr/types/wlr_output.h> 7#include <wlr/types/wlr_output.h>
8#include <wlr/types/wlr_scene.h>
9#include <wlr/types/wlr_subcompositor.h>
10#include <wlr/types/wlr_xdg_shell.h>
8#include "log.h" 11#include "log.h"
12#include "sway/scene_descriptor.h"
9#include "sway/desktop/transaction.h" 13#include "sway/desktop/transaction.h"
10#include "sway/input/cursor.h" 14#include "sway/input/cursor.h"
11#include "sway/input/input-manager.h" 15#include "sway/input/input-manager.h"
@@ -16,163 +20,55 @@
16#include "sway/tree/arrange.h" 20#include "sway/tree/arrange.h"
17#include "sway/tree/workspace.h" 21#include "sway/tree/workspace.h"
18 22
19static void apply_exclusive(struct wlr_box *usable_area, 23struct wlr_layer_surface_v1 *toplevel_layer_surface_from_surface(
20 uint32_t anchor, int32_t exclusive, 24 struct wlr_surface *surface) {
21 int32_t margin_top, int32_t margin_right, 25 struct wlr_layer_surface_v1 *layer;
22 int32_t margin_bottom, int32_t margin_left) { 26 do {
23 if (exclusive <= 0) { 27 if (!surface) {
24 return; 28 return NULL;
25 } 29 }
26 struct { 30 // Topmost layer surface
27 uint32_t singular_anchor; 31 if ((layer = wlr_layer_surface_v1_try_from_wlr_surface(surface))) {
28 uint32_t anchor_triplet; 32 return layer;
29 int *positive_axis; 33 }
30 int *negative_axis; 34 // Layer subsurface
31 int margin; 35 if (wlr_subsurface_try_from_wlr_surface(surface)) {
32 } edges[] = { 36 surface = wlr_surface_get_root_surface(surface);
33 // Top 37 continue;
34 { 38 }
35 .singular_anchor = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP, 39
36 .anchor_triplet = 40 // Layer surface popup
37 ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | 41 struct wlr_xdg_surface *xdg_surface = NULL;
38 ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT | 42 if ((xdg_surface = wlr_xdg_surface_try_from_wlr_surface(surface)) &&
39 ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP, 43 xdg_surface->role == WLR_XDG_SURFACE_ROLE_POPUP && xdg_surface->popup != NULL) {
40 .positive_axis = &usable_area->y, 44 if (!xdg_surface->popup->parent) {
41 .negative_axis = &usable_area->height, 45 return NULL;
42 .margin = margin_top,
43 },
44 // Bottom
45 {
46 .singular_anchor = ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM,
47 .anchor_triplet =
48 ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT |
49 ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT |
50 ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM,
51 .positive_axis = NULL,
52 .negative_axis = &usable_area->height,
53 .margin = margin_bottom,
54 },
55 // Left
56 {
57 .singular_anchor = ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT,
58 .anchor_triplet =
59 ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT |
60 ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP |
61 ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM,
62 .positive_axis = &usable_area->x,
63 .negative_axis = &usable_area->width,
64 .margin = margin_left,
65 },
66 // Right
67 {
68 .singular_anchor = ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT,
69 .anchor_triplet =
70 ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT |
71 ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP |
72 ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM,
73 .positive_axis = NULL,
74 .negative_axis = &usable_area->width,
75 .margin = margin_right,
76 },
77 };
78 for (size_t i = 0; i < sizeof(edges) / sizeof(edges[0]); ++i) {
79 if ((anchor == edges[i].singular_anchor || anchor == edges[i].anchor_triplet)
80 && exclusive + edges[i].margin > 0) {
81 if (edges[i].positive_axis) {
82 *edges[i].positive_axis += exclusive + edges[i].margin;
83 }
84 if (edges[i].negative_axis) {
85 *edges[i].negative_axis -= exclusive + edges[i].margin;
86 } 46 }
87 break; 47 surface = wlr_surface_get_root_surface(xdg_surface->popup->parent);
48 continue;
88 } 49 }
89 } 50
51 // Return early if the surface is not a layer/xdg_popup/sub surface
52 return NULL;
53 } while (true);
90} 54}
91 55
92static void arrange_layer(struct sway_output *output, struct wl_list *list, 56static void arrange_surface(struct sway_output *output, const struct wlr_box *full_area,
93 struct wlr_box *usable_area, bool exclusive) { 57 struct wlr_box *usable_area, struct wlr_scene_tree *tree) {
94 struct sway_layer_surface *sway_layer; 58 struct wlr_scene_node *node;
95 struct wlr_box full_area = { 0 }; 59 wl_list_for_each(node, &tree->children, link) {
96 wlr_output_effective_resolution(output->wlr_output, 60 struct sway_layer_surface *surface = scene_descriptor_try_get(node,
97 &full_area.width, &full_area.height); 61 SWAY_SCENE_DESC_LAYER_SHELL);
98 wl_list_for_each(sway_layer, list, link) { 62 // surface could be null during destruction
99 struct wlr_layer_surface_v1 *layer = sway_layer->layer_surface; 63 if (!surface) {
100 struct wlr_layer_surface_v1_state *state = &layer->current;
101 if (exclusive != (state->exclusive_zone > 0)) {
102 continue; 64 continue;
103 } 65 }
104 struct wlr_box bounds; 66
105 if (state->exclusive_zone == -1) { 67 if (!surface->scene->layer_surface->initialized) {
106 bounds = full_area;
107 } else {
108 bounds = *usable_area;
109 }
110 struct wlr_box box = {
111 .width = state->desired_width,
112 .height = state->desired_height
113 };
114 // Horizontal axis
115 const uint32_t both_horiz = ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT
116 | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT;
117 if (box.width == 0) {
118 box.x = bounds.x;
119 } else if ((state->anchor & both_horiz) == both_horiz) {
120 box.x = bounds.x + ((bounds.width / 2) - (box.width / 2));
121 } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT)) {
122 box.x = bounds.x;
123 } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT)) {
124 box.x = bounds.x + (bounds.width - box.width);
125 } else {
126 box.x = bounds.x + ((bounds.width / 2) - (box.width / 2));
127 }
128 // Vertical axis
129 const uint32_t both_vert = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP
130 | ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM;
131 if (box.height == 0) {
132 box.y = bounds.y;
133 } else if ((state->anchor & both_vert) == both_vert) {
134 box.y = bounds.y + ((bounds.height / 2) - (box.height / 2));
135 } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP)) {
136 box.y = bounds.y;
137 } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM)) {
138 box.y = bounds.y + (bounds.height - box.height);
139 } else {
140 box.y = bounds.y + ((bounds.height / 2) - (box.height / 2));
141 }
142 // Margin
143 if (box.width == 0) {
144 box.x += state->margin.left;
145 box.width = bounds.width -
146 (state->margin.left + state->margin.right);
147 } else if ((state->anchor & both_horiz) == both_horiz) {
148 // don't apply margins
149 } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT)) {
150 box.x += state->margin.left;
151 } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT)) {
152 box.x -= state->margin.right;
153 }
154 if (box.height == 0) {
155 box.y += state->margin.top;
156 box.height = bounds.height -
157 (state->margin.top + state->margin.bottom);
158 } else if ((state->anchor & both_vert) == both_vert) {
159 // don't apply margins
160 } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP)) {
161 box.y += state->margin.top;
162 } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM)) {
163 box.y -= state->margin.bottom;
164 }
165 if (box.width < 0 || box.height < 0) {
166 // TODO: Bubble up a protocol error?
167 wlr_layer_surface_v1_close(layer);
168 continue; 68 continue;
169 } 69 }
170 // Apply 70
171 sway_layer->geo = box; 71 wlr_scene_layer_surface_v1_configure(surface->scene, full_area, usable_area);
172 apply_exclusive(usable_area, state->anchor, state->exclusive_zone,
173 state->margin.top, state->margin.right,
174 state->margin.bottom, state->margin.left);
175 wlr_layer_surface_v1_configure(layer, box.width, box.height);
176 } 72 }
177} 73}
178 74
@@ -180,81 +76,94 @@ void arrange_layers(struct sway_output *output) {
180 struct wlr_box usable_area = { 0 }; 76 struct wlr_box usable_area = { 0 };
181 wlr_output_effective_resolution(output->wlr_output, 77 wlr_output_effective_resolution(output->wlr_output,
182 &usable_area.width, &usable_area.height); 78 &usable_area.width, &usable_area.height);
79 const struct wlr_box full_area = usable_area;
80
81 arrange_surface(output, &full_area, &usable_area, output->layers.shell_background);
82 arrange_surface(output, &full_area, &usable_area, output->layers.shell_bottom);
83 arrange_surface(output, &full_area, &usable_area, output->layers.shell_top);
84 arrange_surface(output, &full_area, &usable_area, output->layers.shell_overlay);
183 85
184 // Arrange exclusive surfaces from top->bottom 86 if (!wlr_box_equal(&usable_area, &output->usable_area)) {
185 arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY],
186 &usable_area, true);
187 arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP],
188 &usable_area, true);
189 arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM],
190 &usable_area, true);
191 arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND],
192 &usable_area, true);
193
194 if (memcmp(&usable_area, &output->usable_area,
195 sizeof(struct wlr_box)) != 0) {
196 sway_log(SWAY_DEBUG, "Usable area changed, rearranging output"); 87 sway_log(SWAY_DEBUG, "Usable area changed, rearranging output");
197 memcpy(&output->usable_area, &usable_area, sizeof(struct wlr_box)); 88 output->usable_area = usable_area;
198 arrange_output(output); 89 arrange_output(output);
90 } else {
91 arrange_popups(root->layers.popup);
199 } 92 }
93}
200 94
201 // Arrange non-exclusive surfaces from top->bottom 95static struct wlr_scene_tree *sway_layer_get_scene(struct sway_output *output,
202 arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], 96 enum zwlr_layer_shell_v1_layer type) {
203 &usable_area, false); 97 switch (type) {
204 arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP], 98 case ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND:
205 &usable_area, false); 99 return output->layers.shell_background;
206 arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM], 100 case ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM:
207 &usable_area, false); 101 return output->layers.shell_bottom;
208 arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND], 102 case ZWLR_LAYER_SHELL_V1_LAYER_TOP:
209 &usable_area, false); 103 return output->layers.shell_top;
210 104 case ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY:
211 // Find topmost keyboard interactive layer, if such a layer exists 105 return output->layers.shell_overlay;
212 uint32_t layers_above_shell[] = {
213 ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY,
214 ZWLR_LAYER_SHELL_V1_LAYER_TOP,
215 };
216 size_t nlayers = sizeof(layers_above_shell) / sizeof(layers_above_shell[0]);
217 struct sway_layer_surface *layer, *topmost = NULL;
218 for (size_t i = 0; i < nlayers; ++i) {
219 wl_list_for_each_reverse(layer,
220 &output->layers[layers_above_shell[i]], link) {
221 if (layer->layer_surface->current.keyboard_interactive &&
222 layer->layer_surface->mapped) {
223 topmost = layer;
224 break;
225 }
226 }
227 if (topmost != NULL) {
228 break;
229 }
230 } 106 }
231 107
232 struct sway_seat *seat; 108 sway_assert(false, "unreachable");
233 wl_list_for_each(seat, &server.input->seats, link) { 109 return NULL;
234 if (topmost != NULL) { 110}
235 seat_set_focus_layer(seat, topmost->layer_surface); 111
236 } else if (seat->focused_layer && 112static struct sway_layer_surface *sway_layer_surface_create(
237 !seat->focused_layer->current.keyboard_interactive) { 113 struct wlr_scene_layer_surface_v1 *scene) {
238 seat_set_focus_layer(seat, NULL); 114 struct sway_layer_surface *surface = calloc(1, sizeof(*surface));
239 } 115 if (!surface) {
116 sway_log(SWAY_ERROR, "Could not allocate a scene_layer surface");
117 return NULL;
118 }
119
120 struct wlr_scene_tree *popups = wlr_scene_tree_create(root->layers.popup);
121 if (!popups) {
122 sway_log(SWAY_ERROR, "Could not allocate a scene_layer popup node");
123 free(surface);
124 return NULL;
125 }
126
127 surface->desc.relative = &scene->tree->node;
128
129 if (!scene_descriptor_assign(&popups->node,
130 SWAY_SCENE_DESC_POPUP, &surface->desc)) {
131 sway_log(SWAY_ERROR, "Failed to allocate a popup scene descriptor");
132 wlr_scene_node_destroy(&popups->node);
133 free(surface);
134 return NULL;
240 } 135 }
136
137 surface->tree = scene->tree;
138 surface->scene = scene;
139 surface->layer_surface = scene->layer_surface;
140 surface->popups = popups;
141 surface->layer_surface->data = surface;
142
143 return surface;
241} 144}
242 145
243static struct sway_layer_surface *find_mapped_layer_by_client( 146static struct sway_layer_surface *find_mapped_layer_by_client(
244 struct wl_client *client, struct wlr_output *ignore_output) { 147 struct wl_client *client, struct sway_output *ignore_output) {
245 for (int i = 0; i < root->outputs->length; ++i) { 148 for (int i = 0; i < root->outputs->length; ++i) {
246 struct sway_output *output = root->outputs->items[i]; 149 struct sway_output *output = root->outputs->items[i];
247 if (output->wlr_output == ignore_output) { 150 if (output == ignore_output) {
248 continue; 151 continue;
249 } 152 }
250 // For now we'll only check the overlay layer 153 // For now we'll only check the overlay layer
251 struct sway_layer_surface *lsurface; 154 struct wlr_scene_node *node;
252 wl_list_for_each(lsurface, 155 wl_list_for_each (node, &output->layers.shell_overlay->children, link) {
253 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], link) { 156 struct sway_layer_surface *surface = scene_descriptor_try_get(node,
254 struct wl_resource *resource = lsurface->layer_surface->resource; 157 SWAY_SCENE_DESC_LAYER_SHELL);
158 if (!surface) {
159 continue;
160 }
161
162 struct wlr_layer_surface_v1 *layer_surface = surface->layer_surface;
163 struct wl_resource *resource = layer_surface->resource;
255 if (wl_resource_get_client(resource) == client 164 if (wl_resource_get_client(resource) == client
256 && lsurface->layer_surface->mapped) { 165 && layer_surface->surface->mapped) {
257 return lsurface; 166 return surface;
258 } 167 }
259 } 168 }
260 } 169 }
@@ -262,280 +171,142 @@ static struct sway_layer_surface *find_mapped_layer_by_client(
262} 171}
263 172
264static void handle_output_destroy(struct wl_listener *listener, void *data) { 173static void handle_output_destroy(struct wl_listener *listener, void *data) {
265 struct sway_layer_surface *sway_layer = 174 struct sway_layer_surface *layer =
266 wl_container_of(listener, sway_layer, output_destroy); 175 wl_container_of(listener, layer, output_destroy);
267 // Determine if this layer is being used by an exclusive client. If it is,
268 // try and find another layer owned by this client to pass focus to.
269 struct sway_seat *seat = input_manager_get_default_seat();
270 struct wl_client *client =
271 wl_resource_get_client(sway_layer->layer_surface->resource);
272 bool set_focus = seat->exclusive_client == client;
273
274 wl_list_remove(&sway_layer->output_destroy.link);
275 wl_list_remove(&sway_layer->link);
276 wl_list_init(&sway_layer->link);
277
278 if (set_focus) {
279 struct sway_layer_surface *layer =
280 find_mapped_layer_by_client(client, sway_layer->layer_surface->output);
281 if (layer) {
282 seat_set_focus_layer(seat, layer->layer_surface);
283 }
284 }
285 176
286 sway_layer->layer_surface->output = NULL; 177 layer->output = NULL;
287 wlr_layer_surface_v1_close(sway_layer->layer_surface); 178 wlr_scene_node_destroy(&layer->scene->tree->node);
288} 179}
289 180
290static void handle_surface_commit(struct wl_listener *listener, void *data) { 181static void handle_node_destroy(struct wl_listener *listener, void *data) {
291 struct sway_layer_surface *layer = 182 struct sway_layer_surface *layer =
292 wl_container_of(listener, layer, surface_commit); 183 wl_container_of(listener, layer, node_destroy);
293 struct wlr_layer_surface_v1 *layer_surface = layer->layer_surface;
294 struct wlr_output *wlr_output = layer_surface->output;
295 if (wlr_output == NULL) {
296 return;
297 }
298 184
299 struct sway_output *output = wlr_output->data; 185 // destroy the scene descriptor straight away if it exists, otherwise
300 struct wlr_box old_geo = layer->geo; 186 // we will try to reflow still considering the destroyed node.
301 arrange_layers(output); 187 scene_descriptor_destroy(&layer->tree->node, SWAY_SCENE_DESC_LAYER_SHELL);
302
303 bool geo_changed =
304 memcmp(&old_geo, &layer->geo, sizeof(struct wlr_box)) != 0;
305 bool layer_changed = layer->layer != layer_surface->current.layer;
306 if (layer_changed) {
307 wl_list_remove(&layer->link);
308 wl_list_insert(&output->layers[layer_surface->current.layer],
309 &layer->link);
310 layer->layer = layer_surface->current.layer;
311 }
312 if (geo_changed || layer_changed) {
313 output_damage_surface(output, old_geo.x, old_geo.y,
314 layer_surface->surface, true);
315 output_damage_surface(output, layer->geo.x, layer->geo.y,
316 layer_surface->surface, true);
317 } else {
318 output_damage_surface(output, layer->geo.x, layer->geo.y,
319 layer_surface->surface, false);
320 }
321
322 transaction_commit_dirty();
323}
324 188
325static void unmap(struct sway_layer_surface *sway_layer) { 189 // Determine if this layer is being used by an exclusive client. If it is,
326 struct sway_seat *seat; 190 // try and find another layer owned by this client to pass focus to.
327 wl_list_for_each(seat, &server.input->seats, link) { 191 struct sway_seat *seat = input_manager_get_default_seat();
328 if (seat->focused_layer == sway_layer->layer_surface) { 192 struct wl_client *client =
329 seat_set_focus_layer(seat, NULL); 193 wl_resource_get_client(layer->layer_surface->resource);
194 if (!server.session_lock.lock) {
195 struct sway_layer_surface *consider_layer =
196 find_mapped_layer_by_client(client, layer->output);
197 if (consider_layer) {
198 seat_set_focus_layer(seat, consider_layer->layer_surface);
330 } 199 }
331 } 200 }
332 201
333 cursor_rebase_all(); 202 if (layer->output) {
334 203 arrange_layers(layer->output);
335 struct wlr_output *wlr_output = sway_layer->layer_surface->output; 204 transaction_commit_dirty();
336 if (wlr_output == NULL) {
337 return;
338 } 205 }
339 struct sway_output *output = wlr_output->data;
340 if (output == NULL) {
341 return;
342 }
343 output_damage_surface(output, sway_layer->geo.x, sway_layer->geo.y,
344 sway_layer->layer_surface->surface, true);
345}
346 206
347static void handle_destroy(struct wl_listener *listener, void *data) { 207 wlr_scene_node_destroy(&layer->popups->node);
348 struct sway_layer_surface *sway_layer =
349 wl_container_of(listener, sway_layer, destroy);
350 sway_log(SWAY_DEBUG, "Layer surface destroyed (%s)",
351 sway_layer->layer_surface->namespace);
352 if (sway_layer->layer_surface->mapped) {
353 unmap(sway_layer);
354 }
355 wl_list_remove(&sway_layer->link);
356 wl_list_remove(&sway_layer->destroy.link);
357 wl_list_remove(&sway_layer->map.link);
358 wl_list_remove(&sway_layer->unmap.link);
359 wl_list_remove(&sway_layer->surface_commit.link);
360 wl_list_remove(&sway_layer->new_popup.link);
361 wl_list_remove(&sway_layer->new_subsurface.link);
362 if (sway_layer->layer_surface->output != NULL) {
363 struct sway_output *output = sway_layer->layer_surface->output->data;
364 if (output != NULL) {
365 arrange_layers(output);
366 transaction_commit_dirty();
367 }
368 wl_list_remove(&sway_layer->output_destroy.link);
369 sway_layer->layer_surface->output = NULL;
370 }
371 free(sway_layer);
372}
373 208
374static void handle_map(struct wl_listener *listener, void *data) { 209 wl_list_remove(&layer->map.link);
375 struct sway_layer_surface *sway_layer = wl_container_of(listener, 210 wl_list_remove(&layer->unmap.link);
376 sway_layer, map); 211 wl_list_remove(&layer->surface_commit.link);
377 struct sway_output *output = sway_layer->layer_surface->output->data; 212 wl_list_remove(&layer->node_destroy.link);
378 output_damage_surface(output, sway_layer->geo.x, sway_layer->geo.y, 213 wl_list_remove(&layer->output_destroy.link);
379 sway_layer->layer_surface->surface, true);
380 wlr_surface_send_enter(sway_layer->layer_surface->surface,
381 sway_layer->layer_surface->output);
382 cursor_rebase_all();
383}
384 214
385static void handle_unmap(struct wl_listener *listener, void *data) { 215 layer->layer_surface->data = NULL;
386 struct sway_layer_surface *sway_layer = wl_container_of(
387 listener, sway_layer, unmap);
388 unmap(sway_layer);
389}
390 216
391static void subsurface_damage(struct sway_layer_subsurface *subsurface, 217 free(layer);
392 bool whole) {
393 struct sway_layer_surface *layer = subsurface->layer_surface;
394 struct wlr_output *wlr_output = layer->layer_surface->output;
395 if (!wlr_output) {
396 return;
397 }
398 struct sway_output *output = wlr_output->data;
399 int ox = subsurface->wlr_subsurface->current.x + layer->geo.x;
400 int oy = subsurface->wlr_subsurface->current.y + layer->geo.y;
401 output_damage_surface(
402 output, ox, oy, subsurface->wlr_subsurface->surface, whole);
403} 218}
404 219
405static void subsurface_handle_unmap(struct wl_listener *listener, void *data) { 220static void handle_surface_commit(struct wl_listener *listener, void *data) {
406 struct sway_layer_subsurface *subsurface = 221 struct sway_layer_surface *surface =
407 wl_container_of(listener, subsurface, unmap); 222 wl_container_of(listener, surface, surface_commit);
408 subsurface_damage(subsurface, true);
409}
410
411static void subsurface_handle_map(struct wl_listener *listener, void *data) {
412 struct sway_layer_subsurface *subsurface =
413 wl_container_of(listener, subsurface, map);
414 subsurface_damage(subsurface, true);
415}
416
417static void subsurface_handle_commit(struct wl_listener *listener, void *data) {
418 struct sway_layer_subsurface *subsurface =
419 wl_container_of(listener, subsurface, commit);
420 subsurface_damage(subsurface, false);
421}
422
423static void subsurface_handle_destroy(struct wl_listener *listener,
424 void *data) {
425 struct sway_layer_subsurface *subsurface =
426 wl_container_of(listener, subsurface, destroy);
427
428 wl_list_remove(&subsurface->map.link);
429 wl_list_remove(&subsurface->unmap.link);
430 wl_list_remove(&subsurface->destroy.link);
431 wl_list_remove(&subsurface->commit.link);
432 free(subsurface);
433}
434 223
435static struct sway_layer_subsurface *create_subsurface( 224 struct wlr_layer_surface_v1 *layer_surface = surface->layer_surface;
436 struct wlr_subsurface *wlr_subsurface, 225 if (!layer_surface->initialized) {
437 struct sway_layer_surface *layer_surface) { 226 return;
438 struct sway_layer_subsurface *subsurface =
439 calloc(1, sizeof(struct sway_layer_subsurface));
440 if (subsurface == NULL) {
441 return NULL;
442 } 227 }
443 228
444 subsurface->wlr_subsurface = wlr_subsurface; 229 uint32_t committed = layer_surface->current.committed;
445 subsurface->layer_surface = layer_surface; 230 if (committed & WLR_LAYER_SURFACE_V1_STATE_LAYER) {
446 231 enum zwlr_layer_shell_v1_layer layer_type = layer_surface->current.layer;
447 subsurface->map.notify = subsurface_handle_map; 232 struct wlr_scene_tree *output_layer = sway_layer_get_scene(
448 wl_signal_add(&wlr_subsurface->events.map, &subsurface->map); 233 surface->output, layer_type);
449 subsurface->unmap.notify = subsurface_handle_unmap; 234 wlr_scene_node_reparent(&surface->scene->tree->node, output_layer);
450 wl_signal_add(&wlr_subsurface->events.unmap, &subsurface->unmap); 235 }
451 subsurface->destroy.notify = subsurface_handle_destroy;
452 wl_signal_add(&wlr_subsurface->events.destroy, &subsurface->destroy);
453 subsurface->commit.notify = subsurface_handle_commit;
454 wl_signal_add(&wlr_subsurface->surface->events.commit, &subsurface->commit);
455
456 return subsurface;
457}
458
459static void handle_new_subsurface(struct wl_listener *listener, void *data) {
460 struct sway_layer_surface *sway_layer_surface =
461 wl_container_of(listener, sway_layer_surface, new_subsurface);
462 struct wlr_subsurface *wlr_subsurface = data;
463 create_subsurface(wlr_subsurface, sway_layer_surface);
464}
465
466 236
467static struct sway_layer_surface *popup_get_layer( 237 if (layer_surface->initial_commit || committed || layer_surface->surface->mapped != surface->mapped) {
468 struct sway_layer_popup *popup) { 238 surface->mapped = layer_surface->surface->mapped;
469 while (popup->parent_type == LAYER_PARENT_POPUP) { 239 arrange_layers(surface->output);
470 popup = popup->parent_popup; 240 transaction_commit_dirty();
471 } 241 }
472 return popup->parent_layer;
473} 242}
474 243
475static void popup_damage(struct sway_layer_popup *layer_popup, bool whole) { 244static void handle_map(struct wl_listener *listener, void *data) {
476 struct wlr_xdg_popup *popup = layer_popup->wlr_popup; 245 struct sway_layer_surface *surface = wl_container_of(listener,
477 struct wlr_surface *surface = popup->base->surface; 246 surface, map);
478 int popup_sx = popup->geometry.x - popup->base->geometry.x; 247
479 int popup_sy = popup->geometry.y - popup->base->geometry.y; 248 struct wlr_layer_surface_v1 *layer_surface =
480 int ox = popup_sx, oy = popup_sy; 249 surface->scene->layer_surface;
481 struct sway_layer_surface *layer; 250
482 while (true) { 251 // focus on new surface
483 if (layer_popup->parent_type == LAYER_PARENT_POPUP) { 252 if (layer_surface->current.keyboard_interactive &&
484 layer_popup = layer_popup->parent_popup; 253 (layer_surface->current.layer == ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY ||
485 ox += layer_popup->wlr_popup->geometry.x; 254 layer_surface->current.layer == ZWLR_LAYER_SHELL_V1_LAYER_TOP)) {
486 oy += layer_popup->wlr_popup->geometry.y; 255 struct sway_seat *seat;
487 } else { 256 wl_list_for_each(seat, &server.input->seats, link) {
488 layer = layer_popup->parent_layer; 257 // but only if the currently focused layer has a lower precedence
489 ox += layer->geo.x; 258 if (!seat->focused_layer ||
490 oy += layer->geo.y; 259 seat->focused_layer->current.layer >= layer_surface->current.layer) {
491 break; 260 seat_set_focus_layer(seat, layer_surface);
261 }
492 } 262 }
263 arrange_layers(surface->output);
493 } 264 }
494 struct wlr_output *wlr_output = layer->layer_surface->output;
495 struct sway_output *output = wlr_output->data;
496 output_damage_surface(output, ox, oy, surface, whole);
497}
498 265
499static void popup_handle_map(struct wl_listener *listener, void *data) { 266 cursor_rebase_all();
500 struct sway_layer_popup *popup = wl_container_of(listener, popup, map);
501 struct sway_layer_surface *layer = popup_get_layer(popup);
502 struct wlr_output *wlr_output = layer->layer_surface->output;
503 wlr_surface_send_enter(popup->wlr_popup->base->surface, wlr_output);
504 popup_damage(popup, true);
505} 267}
506 268
507static void popup_handle_unmap(struct wl_listener *listener, void *data) { 269static void handle_unmap(struct wl_listener *listener, void *data) {
508 struct sway_layer_popup *popup = wl_container_of(listener, popup, unmap); 270 struct sway_layer_surface *surface = wl_container_of(
509 popup_damage(popup, true); 271 listener, surface, unmap);
510} 272 struct sway_seat *seat;
273 wl_list_for_each(seat, &server.input->seats, link) {
274 if (seat->focused_layer == surface->layer_surface) {
275 seat_set_focus_layer(seat, NULL);
276 }
277 }
511 278
512static void popup_handle_commit(struct wl_listener *listener, void *data) { 279 cursor_rebase_all();
513 struct sway_layer_popup *popup = wl_container_of(listener, popup, commit);
514 popup_damage(popup, false);
515} 280}
516 281
517static void popup_handle_destroy(struct wl_listener *listener, void *data) { 282static void popup_handle_destroy(struct wl_listener *listener, void *data) {
518 struct sway_layer_popup *popup = 283 struct sway_layer_popup *popup =
519 wl_container_of(listener, popup, destroy); 284 wl_container_of(listener, popup, destroy);
520 285
521 wl_list_remove(&popup->map.link);
522 wl_list_remove(&popup->unmap.link);
523 wl_list_remove(&popup->destroy.link); 286 wl_list_remove(&popup->destroy.link);
287 wl_list_remove(&popup->new_popup.link);
524 wl_list_remove(&popup->commit.link); 288 wl_list_remove(&popup->commit.link);
525 free(popup); 289 free(popup);
526} 290}
527 291
528static void popup_unconstrain(struct sway_layer_popup *popup) { 292static void popup_unconstrain(struct sway_layer_popup *popup) {
529 struct sway_layer_surface *layer = popup_get_layer(popup);
530 struct wlr_xdg_popup *wlr_popup = popup->wlr_popup; 293 struct wlr_xdg_popup *wlr_popup = popup->wlr_popup;
294 struct sway_output *output = popup->toplevel->output;
531 295
532 struct sway_output *output = layer->layer_surface->output->data; 296 // if a client tries to create a popup while we are in the process of destroying
297 // its output, don't crash.
298 if (!output) {
299 return;
300 }
301
302 int lx, ly;
303 wlr_scene_node_coords(&popup->toplevel->scene->tree->node, &lx, &ly);
533 304
534 // the output box expressed in the coordinate system of the toplevel parent 305 // the output box expressed in the coordinate system of the toplevel parent
535 // of the popup 306 // of the popup
536 struct wlr_box output_toplevel_sx_box = { 307 struct wlr_box output_toplevel_sx_box = {
537 .x = -layer->geo.x, 308 .x = output->lx - lx,
538 .y = -layer->geo.y, 309 .y = output->ly - ly,
539 .width = output->width, 310 .width = output->width,
540 .height = output->height, 311 .height = output->height,
541 }; 312 };
@@ -543,32 +314,38 @@ static void popup_unconstrain(struct sway_layer_popup *popup) {
543 wlr_xdg_popup_unconstrain_from_box(wlr_popup, &output_toplevel_sx_box); 314 wlr_xdg_popup_unconstrain_from_box(wlr_popup, &output_toplevel_sx_box);
544} 315}
545 316
317static void popup_handle_commit(struct wl_listener *listener, void *data) {
318 struct sway_layer_popup *popup = wl_container_of(listener, popup, commit);
319 if (popup->wlr_popup->base->initial_commit) {
320 popup_unconstrain(popup);
321 }
322}
323
546static void popup_handle_new_popup(struct wl_listener *listener, void *data); 324static void popup_handle_new_popup(struct wl_listener *listener, void *data);
547 325
548static struct sway_layer_popup *create_popup(struct wlr_xdg_popup *wlr_popup, 326static struct sway_layer_popup *create_popup(struct wlr_xdg_popup *wlr_popup,
549 enum layer_parent parent_type, void *parent) { 327 struct sway_layer_surface *toplevel, struct wlr_scene_tree *parent) {
550 struct sway_layer_popup *popup = 328 struct sway_layer_popup *popup = calloc(1, sizeof(*popup));
551 calloc(1, sizeof(struct sway_layer_popup));
552 if (popup == NULL) { 329 if (popup == NULL) {
553 return NULL; 330 return NULL;
554 } 331 }
555 332
333 popup->toplevel = toplevel;
556 popup->wlr_popup = wlr_popup; 334 popup->wlr_popup = wlr_popup;
557 popup->parent_type = parent_type; 335 popup->scene = wlr_scene_xdg_surface_create(parent,
558 popup->parent_layer = parent; 336 wlr_popup->base);
337
338 if (!popup->scene) {
339 free(popup);
340 return NULL;
341 }
559 342
560 popup->map.notify = popup_handle_map;
561 wl_signal_add(&wlr_popup->base->events.map, &popup->map);
562 popup->unmap.notify = popup_handle_unmap;
563 wl_signal_add(&wlr_popup->base->events.unmap, &popup->unmap);
564 popup->destroy.notify = popup_handle_destroy; 343 popup->destroy.notify = popup_handle_destroy;
565 wl_signal_add(&wlr_popup->base->events.destroy, &popup->destroy); 344 wl_signal_add(&wlr_popup->base->events.destroy, &popup->destroy);
566 popup->commit.notify = popup_handle_commit;
567 wl_signal_add(&wlr_popup->base->surface->events.commit, &popup->commit);
568 popup->new_popup.notify = popup_handle_new_popup; 345 popup->new_popup.notify = popup_handle_new_popup;
569 wl_signal_add(&wlr_popup->base->events.new_popup, &popup->new_popup); 346 wl_signal_add(&wlr_popup->base->events.new_popup, &popup->new_popup);
570 347 popup->commit.notify = popup_handle_commit;
571 popup_unconstrain(popup); 348 wl_signal_add(&wlr_popup->base->surface->events.commit, &popup->commit);
572 349
573 return popup; 350 return popup;
574} 351}
@@ -577,19 +354,14 @@ static void popup_handle_new_popup(struct wl_listener *listener, void *data) {
577 struct sway_layer_popup *sway_layer_popup = 354 struct sway_layer_popup *sway_layer_popup =
578 wl_container_of(listener, sway_layer_popup, new_popup); 355 wl_container_of(listener, sway_layer_popup, new_popup);
579 struct wlr_xdg_popup *wlr_popup = data; 356 struct wlr_xdg_popup *wlr_popup = data;
580 create_popup(wlr_popup, LAYER_PARENT_POPUP, sway_layer_popup); 357 create_popup(wlr_popup, sway_layer_popup->toplevel, sway_layer_popup->scene);
581} 358}
582 359
583static void handle_new_popup(struct wl_listener *listener, void *data) { 360static void handle_new_popup(struct wl_listener *listener, void *data) {
584 struct sway_layer_surface *sway_layer_surface = 361 struct sway_layer_surface *sway_layer_surface =
585 wl_container_of(listener, sway_layer_surface, new_popup); 362 wl_container_of(listener, sway_layer_surface, new_popup);
586 struct wlr_xdg_popup *wlr_popup = data; 363 struct wlr_xdg_popup *wlr_popup = data;
587 create_popup(wlr_popup, LAYER_PARENT_LAYER, sway_layer_surface); 364 create_popup(wlr_popup, sway_layer_surface, sway_layer_surface->popups);
588}
589
590struct sway_layer_surface *layer_from_wlr_layer_surface_v1(
591 struct wlr_layer_surface_v1 *layer_surface) {
592 return layer_surface->data;
593} 365}
594 366
595void handle_layer_shell_surface(struct wl_listener *listener, void *data) { 367void handle_layer_shell_surface(struct wl_listener *listener, void *data) {
@@ -597,14 +369,14 @@ void handle_layer_shell_surface(struct wl_listener *listener, void *data) {
597 sway_log(SWAY_DEBUG, "new layer surface: namespace %s layer %d anchor %" PRIu32 369 sway_log(SWAY_DEBUG, "new layer surface: namespace %s layer %d anchor %" PRIu32
598 " size %" PRIu32 "x%" PRIu32 " margin %" PRIu32 ",%" PRIu32 ",%" PRIu32 ",%" PRIu32 ",", 370 " size %" PRIu32 "x%" PRIu32 " margin %" PRIu32 ",%" PRIu32 ",%" PRIu32 ",%" PRIu32 ",",
599 layer_surface->namespace, 371 layer_surface->namespace,
600 layer_surface->client_pending.layer, 372 layer_surface->pending.layer,
601 layer_surface->client_pending.anchor, 373 layer_surface->pending.anchor,
602 layer_surface->client_pending.desired_width, 374 layer_surface->pending.desired_width,
603 layer_surface->client_pending.desired_height, 375 layer_surface->pending.desired_height,
604 layer_surface->client_pending.margin.top, 376 layer_surface->pending.margin.top,
605 layer_surface->client_pending.margin.right, 377 layer_surface->pending.margin.right,
606 layer_surface->client_pending.margin.bottom, 378 layer_surface->pending.margin.bottom,
607 layer_surface->client_pending.margin.left); 379 layer_surface->pending.margin.left);
608 380
609 if (!layer_surface->output) { 381 if (!layer_surface->output) {
610 // Assign last active output 382 // Assign last active output
@@ -616,12 +388,12 @@ void handle_layer_shell_surface(struct wl_listener *listener, void *data) {
616 output = ws->output; 388 output = ws->output;
617 } 389 }
618 } 390 }
619 if (!output || output == root->noop_output) { 391 if (!output || output == root->fallback_output) {
620 if (!root->outputs->length) { 392 if (!root->outputs->length) {
621 sway_log(SWAY_ERROR, 393 sway_log(SWAY_ERROR,
622 "no output to auto-assign layer surface '%s' to", 394 "no output to auto-assign layer surface '%s' to",
623 layer_surface->namespace); 395 layer_surface->namespace);
624 wlr_layer_surface_v1_close(layer_surface); 396 wlr_layer_surface_v1_destroy(layer_surface);
625 return; 397 return;
626 } 398 }
627 output = root->outputs->items[0]; 399 output = root->outputs->items[0];
@@ -629,42 +401,57 @@ void handle_layer_shell_surface(struct wl_listener *listener, void *data) {
629 layer_surface->output = output->wlr_output; 401 layer_surface->output = output->wlr_output;
630 } 402 }
631 403
632 struct sway_layer_surface *sway_layer = 404 struct sway_output *output = layer_surface->output->data;
633 calloc(1, sizeof(struct sway_layer_surface)); 405
634 if (!sway_layer) { 406 enum zwlr_layer_shell_v1_layer layer_type = layer_surface->pending.layer;
407 struct wlr_scene_tree *output_layer = sway_layer_get_scene(
408 output, layer_type);
409 struct wlr_scene_layer_surface_v1 *scene_surface =
410 wlr_scene_layer_surface_v1_create(output_layer, layer_surface);
411 if (!scene_surface) {
412 sway_log(SWAY_ERROR, "Could not allocate a layer_surface_v1");
635 return; 413 return;
636 } 414 }
637 415
638 sway_layer->surface_commit.notify = handle_surface_commit; 416 struct sway_layer_surface *surface =
639 wl_signal_add(&layer_surface->surface->events.commit, 417 sway_layer_surface_create(scene_surface);
640 &sway_layer->surface_commit); 418 if (!surface) {
641 419 wlr_layer_surface_v1_destroy(layer_surface);
642 sway_layer->destroy.notify = handle_destroy;
643 wl_signal_add(&layer_surface->events.destroy, &sway_layer->destroy);
644 sway_layer->map.notify = handle_map;
645 wl_signal_add(&layer_surface->events.map, &sway_layer->map);
646 sway_layer->unmap.notify = handle_unmap;
647 wl_signal_add(&layer_surface->events.unmap, &sway_layer->unmap);
648 sway_layer->new_popup.notify = handle_new_popup;
649 wl_signal_add(&layer_surface->events.new_popup, &sway_layer->new_popup);
650 sway_layer->new_subsurface.notify = handle_new_subsurface;
651 wl_signal_add(&layer_surface->surface->events.new_subsurface,
652 &sway_layer->new_subsurface);
653
654 sway_layer->layer_surface = layer_surface;
655 layer_surface->data = sway_layer;
656 420
657 struct sway_output *output = layer_surface->output->data; 421 sway_log(SWAY_ERROR, "Could not allocate a sway_layer_surface");
658 sway_layer->output_destroy.notify = handle_output_destroy; 422 return;
659 wl_signal_add(&output->events.destroy, &sway_layer->output_destroy); 423 }
660 424
661 wl_list_insert(&output->layers[layer_surface->client_pending.layer], 425 if (!scene_descriptor_assign(&scene_surface->tree->node,
662 &sway_layer->link); 426 SWAY_SCENE_DESC_LAYER_SHELL, surface)) {
663 427 sway_log(SWAY_ERROR, "Failed to allocate a layer surface descriptor");
664 // Temporarily set the layer's current state to client_pending 428 // destroying the layer_surface will also destroy its corresponding
665 // So that we can easily arrange it 429 // scene node
666 struct wlr_layer_surface_v1_state old_state = layer_surface->current; 430 wlr_layer_surface_v1_destroy(layer_surface);
667 layer_surface->current = layer_surface->client_pending; 431 return;
668 arrange_layers(output); 432 }
669 layer_surface->current = old_state; 433
434 surface->output = output;
435
436 // now that the surface's output is known, we can advertise its scale
437 wlr_fractional_scale_v1_notify_scale(surface->layer_surface->surface,
438 layer_surface->output->scale);
439 wlr_surface_set_preferred_buffer_scale(surface->layer_surface->surface,
440 ceil(layer_surface->output->scale));
441
442 surface->surface_commit.notify = handle_surface_commit;
443 wl_signal_add(&layer_surface->surface->events.commit,
444 &surface->surface_commit);
445 surface->map.notify = handle_map;
446 wl_signal_add(&layer_surface->surface->events.map, &surface->map);
447 surface->unmap.notify = handle_unmap;
448 wl_signal_add(&layer_surface->surface->events.unmap, &surface->unmap);
449 surface->new_popup.notify = handle_new_popup;
450 wl_signal_add(&layer_surface->events.new_popup, &surface->new_popup);
451
452 surface->output_destroy.notify = handle_output_destroy;
453 wl_signal_add(&output->events.disable, &surface->output_destroy);
454
455 surface->node_destroy.notify = handle_node_destroy;
456 wl_signal_add(&scene_surface->tree->node.events.destroy, &surface->node_destroy);
670} 457}
diff --git a/sway/desktop/output.c b/sway/desktop/output.c
index 2f2ab4bc..2722e556 100644
--- a/sway/desktop/output.c
+++ b/sway/desktop/output.c
@@ -1,41 +1,61 @@
1#define _POSIX_C_SOURCE 200809L
2#include <assert.h> 1#include <assert.h>
3#include <stdlib.h> 2#include <stdlib.h>
4#include <strings.h> 3#include <strings.h>
5#include <time.h> 4#include <time.h>
6#include <wayland-server-core.h> 5#include <wayland-server-core.h>
6#include <wlr/config.h>
7#include <wlr/backend/headless.h>
8#include <wlr/render/swapchain.h>
7#include <wlr/render/wlr_renderer.h> 9#include <wlr/render/wlr_renderer.h>
8#include <wlr/types/wlr_buffer.h> 10#include <wlr/types/wlr_buffer.h>
11#include <wlr/types/wlr_gamma_control_v1.h>
9#include <wlr/types/wlr_matrix.h> 12#include <wlr/types/wlr_matrix.h>
10#include <wlr/types/wlr_output_damage.h>
11#include <wlr/types/wlr_output_layout.h> 13#include <wlr/types/wlr_output_layout.h>
14#include <wlr/types/wlr_output_management_v1.h>
15#include <wlr/types/wlr_output_power_management_v1.h>
12#include <wlr/types/wlr_output.h> 16#include <wlr/types/wlr_output.h>
13#include <wlr/types/wlr_presentation_time.h> 17#include <wlr/types/wlr_presentation_time.h>
14#include <wlr/types/wlr_surface.h> 18#include <wlr/types/wlr_compositor.h>
15#include <wlr/util/region.h> 19#include <wlr/util/region.h>
20#include <wlr/util/transform.h>
16#include "config.h" 21#include "config.h"
17#include "log.h" 22#include "log.h"
18#include "sway/config.h" 23#include "sway/config.h"
19#include "sway/desktop/transaction.h" 24#include "sway/desktop/transaction.h"
20#include "sway/input/input-manager.h" 25#include "sway/input/input-manager.h"
21#include "sway/input/seat.h" 26#include "sway/input/seat.h"
27#include "sway/ipc-server.h"
22#include "sway/layers.h" 28#include "sway/layers.h"
23#include "sway/output.h" 29#include "sway/output.h"
30#include "sway/scene_descriptor.h"
24#include "sway/server.h" 31#include "sway/server.h"
25#include "sway/surface.h"
26#include "sway/tree/arrange.h" 32#include "sway/tree/arrange.h"
27#include "sway/tree/container.h" 33#include "sway/tree/container.h"
28#include "sway/tree/root.h" 34#include "sway/tree/root.h"
29#include "sway/tree/view.h" 35#include "sway/tree/view.h"
30#include "sway/tree/workspace.h" 36#include "sway/tree/workspace.h"
31 37
38#if WLR_HAS_DRM_BACKEND
39#include <wlr/backend/drm.h>
40#include <wlr/types/wlr_drm_lease_v1.h>
41#endif
42
43bool output_match_name_or_id(struct sway_output *output,
44 const char *name_or_id) {
45 if (strcmp(name_or_id, "*") == 0) {
46 return true;
47 }
48
49 char identifier[128];
50 output_get_identifier(identifier, sizeof(identifier), output);
51 return strcasecmp(identifier, name_or_id) == 0
52 || strcasecmp(output->wlr_output->name, name_or_id) == 0;
53}
54
32struct sway_output *output_by_name_or_id(const char *name_or_id) { 55struct sway_output *output_by_name_or_id(const char *name_or_id) {
33 for (int i = 0; i < root->outputs->length; ++i) { 56 for (int i = 0; i < root->outputs->length; ++i) {
34 struct sway_output *output = root->outputs->items[i]; 57 struct sway_output *output = root->outputs->items[i];
35 char identifier[128]; 58 if (output_match_name_or_id(output, name_or_id)) {
36 output_get_identifier(identifier, sizeof(identifier), output);
37 if (strcasecmp(identifier, name_or_id) == 0
38 || strcasecmp(output->wlr_output->name, name_or_id) == 0) {
39 return output; 59 return output;
40 } 60 }
41 } 61 }
@@ -45,513 +65,217 @@ struct sway_output *output_by_name_or_id(const char *name_or_id) {
45struct sway_output *all_output_by_name_or_id(const char *name_or_id) { 65struct sway_output *all_output_by_name_or_id(const char *name_or_id) {
46 struct sway_output *output; 66 struct sway_output *output;
47 wl_list_for_each(output, &root->all_outputs, link) { 67 wl_list_for_each(output, &root->all_outputs, link) {
48 char identifier[128]; 68 if (output_match_name_or_id(output, name_or_id)) {
49 output_get_identifier(identifier, sizeof(identifier), output);
50 if (strcasecmp(identifier, name_or_id) == 0
51 || strcasecmp(output->wlr_output->name, name_or_id) == 0) {
52 return output; 69 return output;
53 } 70 }
54 } 71 }
55 return NULL; 72 return NULL;
56} 73}
57 74
58struct surface_iterator_data {
59 sway_surface_iterator_func_t user_iterator;
60 void *user_data;
61
62 struct sway_output *output;
63 struct sway_view *view;
64 double ox, oy;
65 int width, height;
66};
67
68static bool get_surface_box(struct surface_iterator_data *data,
69 struct wlr_surface *surface, int sx, int sy,
70 struct wlr_box *surface_box) {
71 struct sway_output *output = data->output;
72
73 if (!wlr_surface_has_buffer(surface)) {
74 return false;
75 }
76
77 int sw = surface->current.width;
78 int sh = surface->current.height;
79 75
80 struct wlr_box box = { 76struct sway_workspace *output_get_active_workspace(struct sway_output *output) {
81 .x = floor(data->ox + sx), 77 struct sway_seat *seat = input_manager_current_seat();
82 .y = floor(data->oy + sy), 78 struct sway_node *focus = seat_get_active_tiling_child(seat, &output->node);
83 .width = sw, 79 if (!focus) {
84 .height = sh, 80 if (!output->workspaces->length) {
85 }; 81 return NULL;
86 if (surface_box != NULL) { 82 }
87 memcpy(surface_box, &box, sizeof(struct wlr_box)); 83 return output->workspaces->items[0];
88 } 84 }
89 85 return focus->sway_workspace;
90 struct wlr_box output_box = {
91 .width = output->width,
92 .height = output->height,
93 };
94
95 struct wlr_box intersection;
96 return wlr_box_intersection(&intersection, &output_box, &box);
97} 86}
98 87
99static void output_for_each_surface_iterator(struct wlr_surface *surface, 88struct send_frame_done_data {
100 int sx, int sy, void *_data) { 89 struct timespec when;
101 struct surface_iterator_data *data = _data; 90 int msec_until_refresh;
102 91 struct sway_output *output;
103 struct wlr_box box; 92};
104 bool intersects = get_surface_box(data, surface, sx, sy, &box);
105 if (!intersects) {
106 return;
107 }
108 93
109 data->user_iterator(data->output, data->view, surface, &box, 94struct buffer_timer {
110 data->user_data); 95 struct wl_listener destroy;
111} 96 struct wl_event_source *frame_done_timer;
97};
112 98
113void output_surface_for_each_surface(struct sway_output *output, 99static int handle_buffer_timer(void *data) {
114 struct wlr_surface *surface, double ox, double oy, 100 struct wlr_scene_buffer *buffer = data;
115 sway_surface_iterator_func_t iterator, void *user_data) {
116 struct surface_iterator_data data = {
117 .user_iterator = iterator,
118 .user_data = user_data,
119 .output = output,
120 .view = NULL,
121 .ox = ox,
122 .oy = oy,
123 .width = surface->current.width,
124 .height = surface->current.height,
125 };
126
127 wlr_surface_for_each_surface(surface,
128 output_for_each_surface_iterator, &data);
129}
130 101
131void output_view_for_each_surface(struct sway_output *output, 102 struct timespec now;
132 struct sway_view *view, sway_surface_iterator_func_t iterator, 103 clock_gettime(CLOCK_MONOTONIC, &now);
133 void *user_data) { 104 wlr_scene_buffer_send_frame_done(buffer, &now);
134 struct surface_iterator_data data = { 105 return 0;
135 .user_iterator = iterator,
136 .user_data = user_data,
137 .output = output,
138 .view = view,
139 .ox = view->container->surface_x - output->lx
140 - view->geometry.x,
141 .oy = view->container->surface_y - output->ly
142 - view->geometry.y,
143 .width = view->container->current.content_width,
144 .height = view->container->current.content_height,
145 };
146
147 view_for_each_surface(view, output_for_each_surface_iterator, &data);
148} 106}
149 107
150void output_view_for_each_popup_surface(struct sway_output *output, 108static void handle_buffer_timer_destroy(struct wl_listener *listener,
151 struct sway_view *view, sway_surface_iterator_func_t iterator, 109 void *data) {
152 void *user_data) { 110 struct buffer_timer *timer = wl_container_of(listener, timer, destroy);
153 struct surface_iterator_data data = {
154 .user_iterator = iterator,
155 .user_data = user_data,
156 .output = output,
157 .view = view,
158 .ox = view->container->surface_x - output->lx
159 - view->geometry.x,
160 .oy = view->container->surface_y - output->ly
161 - view->geometry.y,
162 .width = view->container->current.content_width,
163 .height = view->container->current.content_height,
164 };
165
166 view_for_each_popup_surface(view, output_for_each_surface_iterator, &data);
167}
168 111
169void output_layer_for_each_surface(struct sway_output *output, 112 wl_list_remove(&timer->destroy.link);
170 struct wl_list *layer_surfaces, sway_surface_iterator_func_t iterator, 113 wl_event_source_remove(timer->frame_done_timer);
171 void *user_data) { 114 free(timer);
172 struct sway_layer_surface *layer_surface;
173 wl_list_for_each(layer_surface, layer_surfaces, link) {
174 struct wlr_layer_surface_v1 *wlr_layer_surface_v1 =
175 layer_surface->layer_surface;
176 struct wlr_surface *surface = wlr_layer_surface_v1->surface;
177 struct surface_iterator_data data = {
178 .user_iterator = iterator,
179 .user_data = user_data,
180 .output = output,
181 .view = NULL,
182 .ox = layer_surface->geo.x,
183 .oy = layer_surface->geo.y,
184 .width = surface->current.width,
185 .height = surface->current.height,
186 };
187 wlr_layer_surface_v1_for_each_surface(wlr_layer_surface_v1,
188 output_for_each_surface_iterator, &data);
189 }
190} 115}
191 116
192void output_layer_for_each_toplevel_surface(struct sway_output *output, 117static struct buffer_timer *buffer_timer_get_or_create(struct wlr_scene_buffer *buffer) {
193 struct wl_list *layer_surfaces, sway_surface_iterator_func_t iterator, 118 struct buffer_timer *timer =
194 void *user_data) { 119 scene_descriptor_try_get(&buffer->node, SWAY_SCENE_DESC_BUFFER_TIMER);
195 struct sway_layer_surface *layer_surface; 120 if (timer) {
196 wl_list_for_each(layer_surface, layer_surfaces, link) { 121 return timer;
197 struct wlr_layer_surface_v1 *wlr_layer_surface_v1 =
198 layer_surface->layer_surface;
199 output_surface_for_each_surface(output, wlr_layer_surface_v1->surface,
200 layer_surface->geo.x, layer_surface->geo.y, iterator,
201 user_data);
202 } 122 }
203}
204 123
205 124 timer = calloc(1, sizeof(struct buffer_timer));
206void output_layer_for_each_popup_surface(struct sway_output *output, 125 if (!timer) {
207 struct wl_list *layer_surfaces, sway_surface_iterator_func_t iterator, 126 return NULL;
208 void *user_data) {
209 struct sway_layer_surface *layer_surface;
210 wl_list_for_each(layer_surface, layer_surfaces, link) {
211 struct wlr_layer_surface_v1 *wlr_layer_surface_v1 =
212 layer_surface->layer_surface;
213 struct wlr_surface *surface = wlr_layer_surface_v1->surface;
214 struct surface_iterator_data data = {
215 .user_iterator = iterator,
216 .user_data = user_data,
217 .output = output,
218 .view = NULL,
219 .ox = layer_surface->geo.x,
220 .oy = layer_surface->geo.y,
221 .width = surface->current.width,
222 .height = surface->current.height,
223 };
224 wlr_layer_surface_v1_for_each_popup_surface(wlr_layer_surface_v1,
225 output_for_each_surface_iterator, &data);
226 } 127 }
227}
228 128
229#if HAVE_XWAYLAND 129 timer->frame_done_timer = wl_event_loop_add_timer(server.wl_event_loop,
230void output_unmanaged_for_each_surface(struct sway_output *output, 130 handle_buffer_timer, buffer);
231 struct wl_list *unmanaged, sway_surface_iterator_func_t iterator, 131 if (!timer->frame_done_timer) {
232 void *user_data) { 132 free(timer);
233 struct sway_xwayland_unmanaged *unmanaged_surface; 133 return NULL;
234 wl_list_for_each(unmanaged_surface, unmanaged, link) {
235 struct wlr_xwayland_surface *xsurface =
236 unmanaged_surface->wlr_xwayland_surface;
237 double ox = unmanaged_surface->lx - output->lx;
238 double oy = unmanaged_surface->ly - output->ly;
239
240 output_surface_for_each_surface(output, xsurface->surface, ox, oy,
241 iterator, user_data);
242 } 134 }
243}
244#endif
245 135
246void output_drag_icons_for_each_surface(struct sway_output *output, 136 scene_descriptor_assign(&buffer->node, SWAY_SCENE_DESC_BUFFER_TIMER, timer);
247 struct wl_list *drag_icons, sway_surface_iterator_func_t iterator,
248 void *user_data) {
249 struct sway_drag_icon *drag_icon;
250 wl_list_for_each(drag_icon, drag_icons, link) {
251 double ox = drag_icon->x - output->lx;
252 double oy = drag_icon->y - output->ly;
253
254 if (drag_icon->wlr_drag_icon->mapped) {
255 output_surface_for_each_surface(output,
256 drag_icon->wlr_drag_icon->surface, ox, oy,
257 iterator, user_data);
258 }
259 }
260}
261 137
262static void for_each_surface_container_iterator(struct sway_container *con, 138 timer->destroy.notify = handle_buffer_timer_destroy;
263 void *_data) { 139 wl_signal_add(&buffer->node.events.destroy, &timer->destroy);
264 if (!con->view || !view_is_visible(con->view)) {
265 return;
266 }
267 140
268 struct surface_iterator_data *data = _data; 141 return timer;
269 output_view_for_each_surface(data->output, con->view,
270 data->user_iterator, data->user_data);
271} 142}
272 143
273static void output_for_each_surface(struct sway_output *output, 144static void send_frame_done_iterator(struct wlr_scene_buffer *buffer,
274 sway_surface_iterator_func_t iterator, void *user_data) { 145 int x, int y, void *user_data) {
275 if (output_has_opaque_overlay_layer_surface(output)) { 146 struct send_frame_done_data *data = user_data;
276 goto overlay; 147 struct sway_output *output = data->output;
277 } 148 int view_max_render_time = 0;
278
279 struct surface_iterator_data data = {
280 .user_iterator = iterator,
281 .user_data = user_data,
282 .output = output,
283 .view = NULL,
284 };
285 149
286 struct sway_workspace *workspace = output_get_active_workspace(output); 150 if (buffer->primary_output != data->output->scene_output) {
287 struct sway_container *fullscreen_con = root->fullscreen_global; 151 return;
288 if (!fullscreen_con) {
289 if (!workspace) {
290 return;
291 }
292 fullscreen_con = workspace->current.fullscreen;
293 }
294 if (fullscreen_con) {
295 for_each_surface_container_iterator(fullscreen_con, &data);
296 container_for_each_child(fullscreen_con,
297 for_each_surface_container_iterator, &data);
298
299 // TODO: Show transient containers for fullscreen global
300 if (fullscreen_con == workspace->current.fullscreen) {
301 for (int i = 0; i < workspace->current.floating->length; ++i) {
302 struct sway_container *floater =
303 workspace->current.floating->items[i];
304 if (container_is_transient_for(floater, fullscreen_con)) {
305 for_each_surface_container_iterator(floater, &data);
306 }
307 }
308 }
309#if HAVE_XWAYLAND
310 output_unmanaged_for_each_surface(output, &root->xwayland_unmanaged,
311 iterator, user_data);
312#endif
313 } else {
314 output_layer_for_each_surface(output,
315 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND],
316 iterator, user_data);
317 output_layer_for_each_surface(output,
318 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM],
319 iterator, user_data);
320
321 workspace_for_each_container(workspace,
322 for_each_surface_container_iterator, &data);
323
324#if HAVE_XWAYLAND
325 output_unmanaged_for_each_surface(output, &root->xwayland_unmanaged,
326 iterator, user_data);
327#endif
328 output_layer_for_each_surface(output,
329 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP],
330 iterator, user_data);
331 } 152 }
332 153
333overlay: 154 struct wlr_scene_node *current = &buffer->node;
334 output_layer_for_each_surface(output, 155 while (true) {
335 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], 156 struct sway_view *view = scene_descriptor_try_get(current,
336 iterator, user_data); 157 SWAY_SCENE_DESC_VIEW);
337 output_drag_icons_for_each_surface(output, &root->drag_icons, 158 if (view) {
338 iterator, user_data); 159 view_max_render_time = view->max_render_time;
339} 160 break;
340
341static int scale_length(int length, int offset, float scale) {
342 return round((offset + length) * scale) - round(offset * scale);
343}
344
345void scale_box(struct wlr_box *box, float scale) {
346 box->width = scale_length(box->width, box->x, scale);
347 box->height = scale_length(box->height, box->y, scale);
348 box->x = round(box->x * scale);
349 box->y = round(box->y * scale);
350}
351
352struct sway_workspace *output_get_active_workspace(struct sway_output *output) {
353 struct sway_seat *seat = input_manager_current_seat();
354 struct sway_node *focus = seat_get_active_tiling_child(seat, &output->node);
355 if (!focus) {
356 if (!output->workspaces->length) {
357 return NULL;
358 } 161 }
359 return output->workspaces->items[0];
360 }
361 return focus->sway_workspace;
362}
363 162
364bool output_has_opaque_overlay_layer_surface(struct sway_output *output) { 163 if (!current->parent) {
365 struct sway_layer_surface *sway_layer_surface; 164 break;
366 wl_list_for_each(sway_layer_surface,
367 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], link) {
368 struct wlr_surface *wlr_surface = sway_layer_surface->layer_surface->surface;
369 pixman_box32_t output_box = {
370 .x2 = output->width,
371 .y2 = output->height,
372 };
373 pixman_region32_t surface_opaque_box;
374 pixman_region32_init(&surface_opaque_box);
375 pixman_region32_copy(&surface_opaque_box, &wlr_surface->opaque_region);
376 pixman_region32_translate(&surface_opaque_box,
377 sway_layer_surface->geo.x, sway_layer_surface->geo.y);
378 pixman_region_overlap_t contains =
379 pixman_region32_contains_rectangle(&surface_opaque_box, &output_box);
380 pixman_region32_fini(&surface_opaque_box);
381
382 if (contains == PIXMAN_REGION_IN) {
383 return true;
384 } 165 }
385 }
386 return false;
387}
388 166
389struct send_frame_done_data { 167 current = &current->parent->node;
390 struct timespec when;
391 int msec_until_refresh;
392};
393
394static void send_frame_done_iterator(struct sway_output *output,
395 struct sway_view *view, struct wlr_surface *surface,
396 struct wlr_box *box, void *user_data) {
397 int view_max_render_time = 0;
398 if (view != NULL) {
399 view_max_render_time = view->max_render_time;
400 } 168 }
401 169
402 struct send_frame_done_data *data = user_data;
403
404 int delay = data->msec_until_refresh - output->max_render_time 170 int delay = data->msec_until_refresh - output->max_render_time
405 - view_max_render_time; 171 - view_max_render_time;
406 172
407 if (output->max_render_time == 0 || view_max_render_time == 0 || delay < 1) { 173 struct buffer_timer *timer = NULL;
408 wlr_surface_send_frame_done(surface, &data->when);
409 } else {
410 struct sway_surface *sway_surface = surface->data;
411 wl_event_source_timer_update(sway_surface->frame_done_timer, delay);
412 }
413}
414
415static void send_frame_done(struct sway_output *output, struct send_frame_done_data *data) {
416 output_for_each_surface(output, send_frame_done_iterator, data);
417}
418
419static void count_surface_iterator(struct sway_output *output,
420 struct sway_view *view, struct wlr_surface *surface,
421 struct wlr_box *box, void *data) {
422 size_t *n = data;
423 (*n)++;
424}
425 174
426static bool scan_out_fullscreen_view(struct sway_output *output, 175 if (output->max_render_time != 0 && view_max_render_time != 0 && delay > 0) {
427 struct sway_view *view) { 176 timer = buffer_timer_get_or_create(buffer);
428 struct wlr_output *wlr_output = output->wlr_output;
429 struct sway_workspace *workspace = output->current.active_workspace;
430 if (!sway_assert(workspace, "Expected an active workspace")) {
431 return false;
432 } 177 }
433 178
434 if (!wl_list_empty(&view->saved_buffers)) { 179 if (timer) {
435 return false; 180 wl_event_source_timer_update(timer->frame_done_timer, delay);
181 } else {
182 wlr_scene_buffer_send_frame_done(buffer, &data->when);
436 } 183 }
184}
437 185
438 for (int i = 0; i < workspace->current.floating->length; ++i) { 186static enum wlr_scale_filter_mode get_scale_filter(struct sway_output *output,
439 struct sway_container *floater = 187 struct wlr_scene_buffer *buffer) {
440 workspace->current.floating->items[i]; 188 // if we are scaling down, we should always choose linear
441 if (container_is_transient_for(floater, view->container)) { 189 if (buffer->dst_width > 0 && buffer->dst_height > 0 && (
442 return false; 190 buffer->dst_width < buffer->buffer_width ||
443 } 191 buffer->dst_height < buffer->buffer_height)) {
192 return WLR_SCALE_FILTER_BILINEAR;
444 } 193 }
445 194
446#if HAVE_XWAYLAND 195 switch (output->scale_filter) {
447 if (!wl_list_empty(&root->xwayland_unmanaged)) { 196 case SCALE_FILTER_LINEAR:
448 return false; 197 return WLR_SCALE_FILTER_BILINEAR;
198 case SCALE_FILTER_NEAREST:
199 return WLR_SCALE_FILTER_NEAREST;
200 default:
201 abort(); // unreachable
449 } 202 }
450#endif 203}
451 204
452 if (!wl_list_empty(&output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY])) { 205static void output_configure_scene(struct sway_output *output,
453 return false; 206 struct wlr_scene_node *node, float opacity) {
454 } 207 if (!node->enabled) {
455 if (!wl_list_empty(&root->drag_icons)) { 208 return;
456 return false;
457 } 209 }
458 210
459 struct wlr_surface *surface = view->surface; 211 struct sway_container *con =
460 if (surface == NULL) { 212 scene_descriptor_try_get(node, SWAY_SCENE_DESC_CONTAINER);
461 return false; 213 if (con) {
462 } 214 opacity = con->alpha;
463 size_t n_surfaces = 0;
464 output_view_for_each_surface(output, view,
465 count_surface_iterator, &n_surfaces);
466 if (n_surfaces != 1) {
467 return false;
468 } 215 }
469 216
470 if (surface->buffer == NULL) { 217 if (node->type == WLR_SCENE_NODE_BUFFER) {
471 return false; 218 struct wlr_scene_buffer *buffer = wlr_scene_buffer_from_node(node);
472 }
473 219
474 if ((float)surface->current.scale != wlr_output->scale || 220 // hack: don't call the scene setter because that will damage all outputs
475 surface->current.transform != wlr_output->transform) { 221 // We don't want to damage outputs that aren't our current output that
476 return false; 222 // we're configuring
477 } 223 buffer->filter_mode = get_scale_filter(output, buffer);
478 224
479 wlr_output_attach_buffer(wlr_output, &surface->buffer->base); 225 wlr_scene_buffer_set_opacity(buffer, opacity);
480 if (!wlr_output_test(wlr_output)) { 226 } else if (node->type == WLR_SCENE_NODE_TREE) {
481 return false; 227 struct wlr_scene_tree *tree = wlr_scene_tree_from_node(node);
228 struct wlr_scene_node *node;
229 wl_list_for_each(node, &tree->children, link) {
230 output_configure_scene(output, node, opacity);
231 }
482 } 232 }
483
484 wlr_presentation_surface_sampled_on_output(server.presentation, surface,
485 wlr_output);
486
487 return wlr_output_commit(wlr_output);
488} 233}
489 234
490static int output_repaint_timer_handler(void *data) { 235static int output_repaint_timer_handler(void *data) {
491 struct sway_output *output = data; 236 struct sway_output *output = data;
492 if (output->wlr_output == NULL) {
493 return 0;
494 }
495
496 output->wlr_output->frame_pending = false;
497 237
498 struct sway_workspace *workspace = output->current.active_workspace; 238 if (!output->enabled) {
499 if (workspace == NULL) {
500 return 0; 239 return 0;
501 } 240 }
502 241
503 struct sway_container *fullscreen_con = root->fullscreen_global; 242 output->wlr_output->frame_pending = false;
504 if (!fullscreen_con) {
505 fullscreen_con = workspace->current.fullscreen;
506 }
507 243
508 if (fullscreen_con && fullscreen_con->view) { 244 output_configure_scene(output, &root->root_scene->tree.node, 1.0f);
509 // Try to scan-out the fullscreen view
510 static bool last_scanned_out = false;
511 bool scanned_out =
512 scan_out_fullscreen_view(output, fullscreen_con->view);
513 245
514 if (scanned_out && !last_scanned_out) { 246 if (output->gamma_lut_changed) {
515 sway_log(SWAY_DEBUG, "Scanning out fullscreen view on %s", 247 struct wlr_output_state pending;
516 output->wlr_output->name); 248 wlr_output_state_init(&pending);
249 if (!wlr_scene_output_build_state(output->scene_output, &pending, NULL)) {
250 return 0;
517 } 251 }
518 if (last_scanned_out && !scanned_out) { 252
519 sway_log(SWAY_DEBUG, "Stopping fullscreen view scan out on %s", 253 output->gamma_lut_changed = false;
520 output->wlr_output->name); 254 struct wlr_gamma_control_v1 *gamma_control =
521 output_damage_whole(output); 255 wlr_gamma_control_manager_v1_get_control(
256 server.gamma_control_manager_v1, output->wlr_output);
257 if (!wlr_gamma_control_v1_apply(gamma_control, &pending)) {
258 wlr_output_state_finish(&pending);
259 return 0;
522 } 260 }
523 last_scanned_out = scanned_out;
524 261
525 if (scanned_out) { 262 if (!wlr_output_commit_state(output->wlr_output, &pending)) {
263 wlr_gamma_control_v1_send_failed_and_destroy(gamma_control);
264 wlr_output_state_finish(&pending);
526 return 0; 265 return 0;
527 } 266 }
528 }
529 267
530 bool needs_frame; 268 wlr_output_state_finish(&pending);
531 pixman_region32_t damage;
532 pixman_region32_init(&damage);
533 if (!wlr_output_damage_attach_render(output->damage,
534 &needs_frame, &damage)) {
535 return 0; 269 return 0;
536 } 270 }
537 271
538 if (needs_frame) { 272 wlr_scene_output_commit(output->scene_output, NULL);
539 struct timespec now;
540 clock_gettime(CLOCK_MONOTONIC, &now);
541
542 output_render(output, &now, &damage);
543 } else {
544 wlr_output_rollback(output->wlr_output);
545 }
546
547 pixman_region32_fini(&damage);
548
549 return 0; 273 return 0;
550} 274}
551 275
552static void damage_handle_frame(struct wl_listener *listener, void *user_data) { 276static void handle_frame(struct wl_listener *listener, void *user_data) {
553 struct sway_output *output = 277 struct sway_output *output =
554 wl_container_of(listener, output, damage_frame); 278 wl_container_of(listener, output, frame);
555 if (!output->enabled || !output->wlr_output->enabled) { 279 if (!output->enabled || !output->wlr_output->enabled) {
556 return; 280 return;
557 } 281 }
@@ -562,9 +286,7 @@ static void damage_handle_frame(struct wl_listener *listener, void *user_data) {
562 286
563 if (output->max_render_time != 0) { 287 if (output->max_render_time != 0) {
564 struct timespec now; 288 struct timespec now;
565 clockid_t presentation_clock 289 clock_gettime(CLOCK_MONOTONIC, &now);
566 = wlr_backend_get_presentation_clock(server.backend);
567 clock_gettime(presentation_clock, &now);
568 290
569 const long NSEC_IN_SECONDS = 1000000000; 291 const long NSEC_IN_SECONDS = 1000000000;
570 struct timespec predicted_refresh = output->last_presentation; 292 struct timespec predicted_refresh = output->last_presentation;
@@ -611,118 +333,8 @@ static void damage_handle_frame(struct wl_listener *listener, void *user_data) {
611 struct send_frame_done_data data = {0}; 333 struct send_frame_done_data data = {0};
612 clock_gettime(CLOCK_MONOTONIC, &data.when); 334 clock_gettime(CLOCK_MONOTONIC, &data.when);
613 data.msec_until_refresh = msec_until_refresh; 335 data.msec_until_refresh = msec_until_refresh;
614 send_frame_done(output, &data); 336 data.output = output;
615} 337 wlr_scene_output_for_each_buffer(output->scene_output, send_frame_done_iterator, &data);
616
617void output_damage_whole(struct sway_output *output) {
618 // The output can exist with no wlr_output if it's just been disconnected
619 // and the transaction to evacuate it has't completed yet.
620 if (output && output->wlr_output && output->damage) {
621 wlr_output_damage_add_whole(output->damage);
622 }
623}
624
625static void damage_surface_iterator(struct sway_output *output,
626 struct sway_view *view, struct wlr_surface *surface,
627 struct wlr_box *_box, void *_data) {
628 bool *data = _data;
629 bool whole = *data;
630
631 struct wlr_box box = *_box;
632 scale_box(&box, output->wlr_output->scale);
633
634 if (pixman_region32_not_empty(&surface->buffer_damage)) {
635 pixman_region32_t damage;
636 pixman_region32_init(&damage);
637 wlr_surface_get_effective_damage(surface, &damage);
638 wlr_region_scale(&damage, &damage, output->wlr_output->scale);
639 if (ceil(output->wlr_output->scale) > surface->current.scale) {
640 // When scaling up a surface, it'll become blurry so we need to
641 // expand the damage region
642 wlr_region_expand(&damage, &damage,
643 ceil(output->wlr_output->scale) - surface->current.scale);
644 }
645 pixman_region32_translate(&damage, box.x, box.y);
646 wlr_output_damage_add(output->damage, &damage);
647 pixman_region32_fini(&damage);
648 }
649
650 if (whole) {
651 wlr_output_damage_add_box(output->damage, &box);
652 }
653
654 if (!wl_list_empty(&surface->current.frame_callback_list)) {
655 wlr_output_schedule_frame(output->wlr_output);
656 }
657}
658
659void output_damage_surface(struct sway_output *output, double ox, double oy,
660 struct wlr_surface *surface, bool whole) {
661 output_surface_for_each_surface(output, surface, ox, oy,
662 damage_surface_iterator, &whole);
663}
664
665void output_damage_from_view(struct sway_output *output,
666 struct sway_view *view) {
667 if (!view_is_visible(view)) {
668 return;
669 }
670 bool whole = false;
671 output_view_for_each_surface(output, view, damage_surface_iterator, &whole);
672}
673
674// Expecting an unscaled box in layout coordinates
675void output_damage_box(struct sway_output *output, struct wlr_box *_box) {
676 struct wlr_box box;
677 memcpy(&box, _box, sizeof(struct wlr_box));
678 box.x -= output->lx;
679 box.y -= output->ly;
680 scale_box(&box, output->wlr_output->scale);
681 wlr_output_damage_add_box(output->damage, &box);
682}
683
684static void damage_child_views_iterator(struct sway_container *con,
685 void *data) {
686 if (!con->view || !view_is_visible(con->view)) {
687 return;
688 }
689 struct sway_output *output = data;
690 bool whole = true;
691 output_view_for_each_surface(output, con->view, damage_surface_iterator,
692 &whole);
693}
694
695void output_damage_whole_container(struct sway_output *output,
696 struct sway_container *con) {
697 // Pad the box by 1px, because the width is a double and might be a fraction
698 struct wlr_box box = {
699 .x = con->current.x - output->lx - 1,
700 .y = con->current.y - output->ly - 1,
701 .width = con->current.width + 2,
702 .height = con->current.height + 2,
703 };
704 scale_box(&box, output->wlr_output->scale);
705 wlr_output_damage_add_box(output->damage, &box);
706 // Damage subsurfaces as well, which may extend outside the box
707 if (con->view) {
708 damage_child_views_iterator(con, output);
709 } else {
710 container_for_each_child(con, damage_child_views_iterator, output);
711 }
712}
713
714static void damage_handle_destroy(struct wl_listener *listener, void *data) {
715 struct sway_output *output =
716 wl_container_of(listener, output, damage_destroy);
717 if (!output->enabled) {
718 return;
719 }
720 output_disable(output);
721
722 wl_list_remove(&output->damage_destroy.link);
723 wl_list_remove(&output->damage_frame.link);
724
725 transaction_commit_dirty();
726} 338}
727 339
728static void update_output_manager_config(struct sway_server *server) { 340static void update_output_manager_config(struct sway_server *server) {
@@ -731,73 +343,61 @@ static void update_output_manager_config(struct sway_server *server) {
731 343
732 struct sway_output *output; 344 struct sway_output *output;
733 wl_list_for_each(output, &root->all_outputs, link) { 345 wl_list_for_each(output, &root->all_outputs, link) {
734 if (output == root->noop_output) { 346 if (output == root->fallback_output) {
735 continue; 347 continue;
736 } 348 }
737 struct wlr_output_configuration_head_v1 *config_head = 349 struct wlr_output_configuration_head_v1 *config_head =
738 wlr_output_configuration_head_v1_create(config, output->wlr_output); 350 wlr_output_configuration_head_v1_create(config, output->wlr_output);
739 struct wlr_box *output_box = wlr_output_layout_get_box( 351 struct wlr_box output_box;
740 root->output_layout, output->wlr_output); 352 wlr_output_layout_get_box(root->output_layout,
741 // We mark the output enabled even if it is switched off by DPMS 353 output->wlr_output, &output_box);
742 config_head->state.enabled = output->current_mode != NULL && output->enabled; 354 // We mark the output enabled when it's switched off but not disabled
743 config_head->state.mode = output->current_mode; 355 config_head->state.enabled = !wlr_box_empty(&output_box);
744 if (output_box) { 356 config_head->state.x = output_box.x;
745 config_head->state.x = output_box->x; 357 config_head->state.y = output_box.y;
746 config_head->state.y = output_box->y;
747 }
748 } 358 }
749 359
750 wlr_output_manager_v1_set_configuration(server->output_manager_v1, config); 360 wlr_output_manager_v1_set_configuration(server->output_manager_v1, config);
361
362 ipc_event_output();
751} 363}
752 364
753static void handle_destroy(struct wl_listener *listener, void *data) { 365static void begin_destroy(struct sway_output *output) {
754 struct sway_output *output = wl_container_of(listener, output, destroy);
755 struct sway_server *server = output->server; 366 struct sway_server *server = output->server;
756 wl_signal_emit(&output->events.destroy, output);
757 367
758 if (output->enabled) { 368 if (output->enabled) {
759 output_disable(output); 369 output_disable(output);
760 } 370 }
371
761 output_begin_destroy(output); 372 output_begin_destroy(output);
762 373
374 wl_list_remove(&output->link);
375
376 wl_list_remove(&output->layout_destroy.link);
763 wl_list_remove(&output->destroy.link); 377 wl_list_remove(&output->destroy.link);
764 wl_list_remove(&output->commit.link); 378 wl_list_remove(&output->commit.link);
765 wl_list_remove(&output->mode.link);
766 wl_list_remove(&output->present.link); 379 wl_list_remove(&output->present.link);
380 wl_list_remove(&output->frame.link);
381 wl_list_remove(&output->request_state.link);
382
383 wlr_scene_output_destroy(output->scene_output);
384 output->scene_output = NULL;
385 output->wlr_output->data = NULL;
386 output->wlr_output = NULL;
767 387
768 transaction_commit_dirty(); 388 transaction_commit_dirty();
769 389
770 update_output_manager_config(server); 390 update_output_manager_config(server);
771} 391}
772 392
773static void handle_mode(struct wl_listener *listener, void *data) { 393static void handle_destroy(struct wl_listener *listener, void *data) {
774 struct sway_output *output = wl_container_of(listener, output, mode); 394 struct sway_output *output = wl_container_of(listener, output, destroy);
775 if (!output->enabled && !output->enabling) { 395 begin_destroy(output);
776 struct output_config *oc = find_output_config(output);
777 if (output->wlr_output->current_mode != NULL &&
778 (!oc || oc->enabled)) {
779 // We want to enable this output, but it didn't work last time,
780 // possibly because we hadn't enough CRTCs. Try again now that the
781 // output has a mode.
782 sway_log(SWAY_DEBUG, "Output %s has gained a CRTC, "
783 "trying to enable it", output->wlr_output->name);
784 apply_output_config(oc, output);
785 }
786 return;
787 }
788 if (!output->enabled) {
789 return;
790 }
791 arrange_layers(output);
792 arrange_output(output);
793 transaction_commit_dirty();
794
795 update_output_manager_config(output->server);
796} 396}
797 397
798static void update_textures(struct sway_container *con, void *data) { 398static void handle_layout_destroy(struct wl_listener *listener, void *data) {
799 container_update_title_textures(con); 399 struct sway_output *output = wl_container_of(listener, output, layout_destroy);
800 container_update_marks_textures(con); 400 begin_destroy(output);
801} 401}
802 402
803static void handle_commit(struct wl_listener *listener, void *data) { 403static void handle_commit(struct wl_listener *listener, void *data) {
@@ -808,24 +408,28 @@ static void handle_commit(struct wl_listener *listener, void *data) {
808 return; 408 return;
809 } 409 }
810 410
811 if (event->committed & WLR_OUTPUT_STATE_SCALE) { 411 if (event->state->committed & (
812 output_for_each_container(output, update_textures, NULL); 412 WLR_OUTPUT_STATE_MODE |
813 } 413 WLR_OUTPUT_STATE_TRANSFORM |
814 414 WLR_OUTPUT_STATE_SCALE)) {
815 if (event->committed & (WLR_OUTPUT_STATE_TRANSFORM | WLR_OUTPUT_STATE_SCALE)) {
816 arrange_layers(output); 415 arrange_layers(output);
817 arrange_output(output); 416 arrange_output(output);
818 transaction_commit_dirty(); 417 transaction_commit_dirty();
819 418
820 update_output_manager_config(output->server); 419 update_output_manager_config(output->server);
821 } 420 }
421
422 // Next time the output is enabled, try to re-apply the gamma LUT
423 if ((event->state->committed & WLR_OUTPUT_STATE_ENABLED) && !output->wlr_output->enabled) {
424 output->gamma_lut_changed = true;
425 }
822} 426}
823 427
824static void handle_present(struct wl_listener *listener, void *data) { 428static void handle_present(struct wl_listener *listener, void *data) {
825 struct sway_output *output = wl_container_of(listener, output, present); 429 struct sway_output *output = wl_container_of(listener, output, present);
826 struct wlr_output_event_present *output_event = data; 430 struct wlr_output_event_present *output_event = data;
827 431
828 if (!output->enabled) { 432 if (!output->enabled || !output_event->presented) {
829 return; 433 return;
830 } 434 }
831 435
@@ -833,37 +437,91 @@ static void handle_present(struct wl_listener *listener, void *data) {
833 output->refresh_nsec = output_event->refresh; 437 output->refresh_nsec = output_event->refresh;
834} 438}
835 439
440static void handle_request_state(struct wl_listener *listener, void *data) {
441 struct sway_output *output =
442 wl_container_of(listener, output, request_state);
443 const struct wlr_output_event_request_state *event = data;
444 wlr_output_commit_state(output->wlr_output, event->state);
445}
446
447static unsigned int last_headless_num = 0;
448
836void handle_new_output(struct wl_listener *listener, void *data) { 449void handle_new_output(struct wl_listener *listener, void *data) {
837 struct sway_server *server = wl_container_of(listener, server, new_output); 450 struct sway_server *server = wl_container_of(listener, server, new_output);
838 struct wlr_output *wlr_output = data; 451 struct wlr_output *wlr_output = data;
839 sway_log(SWAY_DEBUG, "New output %p: %s", wlr_output, wlr_output->name); 452
453 if (wlr_output == root->fallback_output->wlr_output) {
454 return;
455 }
456
457 if (wlr_output_is_headless(wlr_output)) {
458 char name[64];
459 snprintf(name, sizeof(name), "HEADLESS-%u", ++last_headless_num);
460 wlr_output_set_name(wlr_output, name);
461 }
462
463 sway_log(SWAY_DEBUG, "New output %p: %s (non-desktop: %d)",
464 wlr_output, wlr_output->name, wlr_output->non_desktop);
465
466 if (wlr_output->non_desktop) {
467 sway_log(SWAY_DEBUG, "Not configuring non-desktop output");
468 struct sway_output_non_desktop *non_desktop = output_non_desktop_create(wlr_output);
469#if WLR_HAS_DRM_BACKEND
470 if (server->drm_lease_manager) {
471 wlr_drm_lease_v1_manager_offer_output(server->drm_lease_manager,
472 wlr_output);
473 }
474#endif
475 list_add(root->non_desktop_outputs, non_desktop);
476 return;
477 }
478
479 if (!wlr_output_init_render(wlr_output, server->allocator,
480 server->renderer)) {
481 sway_log(SWAY_ERROR, "Failed to init output render");
482 return;
483 }
484
485 // Create the scene output here so we're not accidentally creating one for
486 // the fallback output
487 struct wlr_scene_output *scene_output =
488 wlr_scene_output_create(root->root_scene, wlr_output);
489 if (!scene_output) {
490 sway_log(SWAY_ERROR, "Failed to create a scene output");
491 return;
492 }
840 493
841 struct sway_output *output = output_create(wlr_output); 494 struct sway_output *output = output_create(wlr_output);
842 if (!output) { 495 if (!output) {
496 sway_log(SWAY_ERROR, "Failed to create a sway output");
497 wlr_scene_output_destroy(scene_output);
843 return; 498 return;
844 } 499 }
500
845 output->server = server; 501 output->server = server;
846 output->damage = wlr_output_damage_create(wlr_output); 502 output->scene_output = scene_output;
847 503
504 wl_signal_add(&root->output_layout->events.destroy, &output->layout_destroy);
505 output->layout_destroy.notify = handle_layout_destroy;
848 wl_signal_add(&wlr_output->events.destroy, &output->destroy); 506 wl_signal_add(&wlr_output->events.destroy, &output->destroy);
849 output->destroy.notify = handle_destroy; 507 output->destroy.notify = handle_destroy;
850 wl_signal_add(&wlr_output->events.commit, &output->commit); 508 wl_signal_add(&wlr_output->events.commit, &output->commit);
851 output->commit.notify = handle_commit; 509 output->commit.notify = handle_commit;
852 wl_signal_add(&wlr_output->events.mode, &output->mode);
853 output->mode.notify = handle_mode;
854 wl_signal_add(&wlr_output->events.present, &output->present); 510 wl_signal_add(&wlr_output->events.present, &output->present);
855 output->present.notify = handle_present; 511 output->present.notify = handle_present;
856 wl_signal_add(&output->damage->events.frame, &output->damage_frame); 512 wl_signal_add(&wlr_output->events.frame, &output->frame);
857 output->damage_frame.notify = damage_handle_frame; 513 output->frame.notify = handle_frame;
858 wl_signal_add(&output->damage->events.destroy, &output->damage_destroy); 514 wl_signal_add(&wlr_output->events.request_state, &output->request_state);
859 output->damage_destroy.notify = damage_handle_destroy; 515 output->request_state.notify = handle_request_state;
860 516
861 output->repaint_timer = wl_event_loop_add_timer(server->wl_event_loop, 517 output->repaint_timer = wl_event_loop_add_timer(server->wl_event_loop,
862 output_repaint_timer_handler, output); 518 output_repaint_timer_handler, output);
863 519
864 struct output_config *oc = find_output_config(output); 520 if (server->session_lock.lock) {
865 apply_output_config(oc, output); 521 sway_session_lock_add_output(server->session_lock.lock, output);
866 free_output_config(oc); 522 }
523
524 apply_all_output_configs();
867 525
868 transaction_commit_dirty(); 526 transaction_commit_dirty();
869 527
@@ -877,62 +535,104 @@ void handle_output_layout_change(struct wl_listener *listener,
877 update_output_manager_config(server); 535 update_output_manager_config(server);
878} 536}
879 537
538void handle_gamma_control_set_gamma(struct wl_listener *listener, void *data) {
539 struct sway_server *server =
540 wl_container_of(listener, server, gamma_control_set_gamma);
541 const struct wlr_gamma_control_manager_v1_set_gamma_event *event = data;
542
543 struct sway_output *output = event->output->data;
544
545 if(!output) {
546 return;
547 }
548
549 output->gamma_lut_changed = true;
550 wlr_output_schedule_frame(output->wlr_output);
551}
552
553static struct output_config *output_config_for_config_head(
554 struct wlr_output_configuration_head_v1 *config_head,
555 struct sway_output *output) {
556 struct output_config *oc = new_output_config(output->wlr_output->name);
557 oc->enabled = config_head->state.enabled;
558 if (!oc->enabled) {
559 return oc;
560 }
561
562 if (config_head->state.mode != NULL) {
563 struct wlr_output_mode *mode = config_head->state.mode;
564 oc->width = mode->width;
565 oc->height = mode->height;
566 oc->refresh_rate = mode->refresh / 1000.f;
567 } else {
568 oc->width = config_head->state.custom_mode.width;
569 oc->height = config_head->state.custom_mode.height;
570 oc->refresh_rate =
571 config_head->state.custom_mode.refresh / 1000.f;
572 }
573 oc->x = config_head->state.x;
574 oc->y = config_head->state.y;
575 oc->transform = config_head->state.transform;
576 oc->scale = config_head->state.scale;
577 oc->adaptive_sync = config_head->state.adaptive_sync_enabled;
578 return oc;
579}
580
880static void output_manager_apply(struct sway_server *server, 581static void output_manager_apply(struct sway_server *server,
881 struct wlr_output_configuration_v1 *config, bool test_only) { 582 struct wlr_output_configuration_v1 *config, bool test_only) {
882 // TODO: perform atomic tests on the whole backend atomically 583 size_t configs_len = wl_list_length(&root->all_outputs);
883 584 struct matched_output_config *configs = calloc(configs_len, sizeof(*configs));
884 struct wlr_output_configuration_head_v1 *config_head; 585 if (!configs) {
885 // First disable outputs we need to disable 586 return;
886 bool ok = true; 587 }
887 wl_list_for_each(config_head, &config->heads, link) { 588
888 struct wlr_output *wlr_output = config_head->state.output; 589 int config_idx = 0;
889 struct sway_output *output = wlr_output->data; 590 struct sway_output *sway_output;
890 if (!output->enabled || config_head->state.enabled) { 591 wl_list_for_each(sway_output, &root->all_outputs, link) {
592 if (sway_output == root->fallback_output) {
593 configs_len--;
891 continue; 594 continue;
892 } 595 }
893 struct output_config *oc = new_output_config(output->wlr_output->name);
894 oc->enabled = false;
895 596
896 if (test_only) { 597 struct matched_output_config *cfg = &configs[config_idx++];
897 ok &= test_output_config(oc, output); 598 cfg->output = sway_output;
898 } else {
899 oc = store_output_config(oc);
900 ok &= apply_output_config(oc, output);
901 }
902 }
903 599
904 // Then enable outputs that need to 600 struct wlr_output_configuration_head_v1 *config_head;
905 wl_list_for_each(config_head, &config->heads, link) { 601 wl_list_for_each(config_head, &config->heads, link) {
906 struct wlr_output *wlr_output = config_head->state.output; 602 if (config_head->state.output == sway_output->wlr_output) {
907 struct sway_output *output = wlr_output->data; 603 cfg->config = output_config_for_config_head(config_head, sway_output);
908 if (!config_head->state.enabled) { 604 break;
909 continue; 605 }
910 } 606 }
911 struct output_config *oc = new_output_config(output->wlr_output->name); 607 if (!cfg->config) {
912 oc->enabled = true; 608 cfg->config = find_output_config(sway_output);
913 if (config_head->state.mode != NULL) {
914 struct wlr_output_mode *mode = config_head->state.mode;
915 oc->width = mode->width;
916 oc->height = mode->height;
917 oc->refresh_rate = mode->refresh / 1000.f;
918 } else {
919 oc->width = config_head->state.custom_mode.width;
920 oc->height = config_head->state.custom_mode.height;
921 oc->refresh_rate =
922 config_head->state.custom_mode.refresh / 1000.f;
923 } 609 }
924 oc->x = config_head->state.x; 610 }
925 oc->y = config_head->state.y;
926 oc->transform = config_head->state.transform;
927 oc->scale = config_head->state.scale;
928 611
929 if (test_only) { 612 sort_output_configs_by_priority(configs, configs_len);
930 ok &= test_output_config(oc, output); 613 bool ok = apply_output_configs(configs, configs_len, test_only, false);
614 for (size_t idx = 0; idx < configs_len; idx++) {
615 struct matched_output_config *cfg = &configs[idx];
616
617 // Only store new configs for successful non-test commits. Old configs,
618 // test-only and failed commits just get freed.
619 bool store_config = false;
620 if (!test_only && ok) {
621 struct wlr_output_configuration_head_v1 *config_head;
622 wl_list_for_each(config_head, &config->heads, link) {
623 if (config_head->state.output == cfg->output->wlr_output) {
624 store_config = true;
625 break;
626 }
627 }
628 }
629 if (store_config) {
630 store_output_config(cfg->config);
931 } else { 631 } else {
932 oc = store_output_config(oc); 632 free_output_config(cfg->config);
933 ok &= apply_output_config(oc, output);
934 } 633 }
935 } 634 }
635 free(configs);
936 636
937 if (ok) { 637 if (ok) {
938 wlr_output_configuration_v1_send_succeeded(config); 638 wlr_output_configuration_v1_send_succeeded(config);
@@ -970,12 +670,12 @@ void handle_output_power_manager_set_mode(struct wl_listener *listener,
970 struct output_config *oc = new_output_config(output->wlr_output->name); 670 struct output_config *oc = new_output_config(output->wlr_output->name);
971 switch (event->mode) { 671 switch (event->mode) {
972 case ZWLR_OUTPUT_POWER_V1_MODE_OFF: 672 case ZWLR_OUTPUT_POWER_V1_MODE_OFF:
973 oc->dpms_state = DPMS_OFF; 673 oc->power = 0;
974 break; 674 break;
975 case ZWLR_OUTPUT_POWER_V1_MODE_ON: 675 case ZWLR_OUTPUT_POWER_V1_MODE_ON:
976 oc->dpms_state = DPMS_ON; 676 oc->power = 1;
977 break; 677 break;
978 } 678 }
979 oc = store_output_config(oc); 679 store_output_config(oc);
980 apply_output_config(oc, output); 680 apply_all_output_configs();
981} 681}
diff --git a/sway/desktop/render.c b/sway/desktop/render.c
deleted file mode 100644
index a5bd8a5f..00000000
--- a/sway/desktop/render.c
+++ /dev/null
@@ -1,1160 +0,0 @@
1#define _POSIX_C_SOURCE 200809L
2#include <assert.h>
3#include <GLES2/gl2.h>
4#include <stdlib.h>
5#include <strings.h>
6#include <time.h>
7#include <wayland-server-core.h>
8#include <wlr/render/gles2.h>
9#include <wlr/render/wlr_renderer.h>
10#include <wlr/types/wlr_buffer.h>
11#include <wlr/types/wlr_matrix.h>
12#include <wlr/types/wlr_output_damage.h>
13#include <wlr/types/wlr_output_layout.h>
14#include <wlr/types/wlr_output.h>
15#include <wlr/types/wlr_surface.h>
16#include <wlr/util/region.h>
17#include "log.h"
18#include "config.h"
19#include "sway/config.h"
20#include "sway/input/input-manager.h"
21#include "sway/input/seat.h"
22#include "sway/layers.h"
23#include "sway/output.h"
24#include "sway/server.h"
25#include "sway/tree/arrange.h"
26#include "sway/tree/container.h"
27#include "sway/tree/root.h"
28#include "sway/tree/view.h"
29#include "sway/tree/workspace.h"
30
31struct render_data {
32 pixman_region32_t *damage;
33 float alpha;
34 struct wlr_box *clip_box;
35};
36
37/**
38 * Apply scale to a width or height.
39 *
40 * One does not simply multiply the width by the scale. We allow fractional
41 * scaling, which means the resulting scaled width might be a decimal.
42 * So we round it.
43 *
44 * But even this can produce undesirable results depending on the X or Y offset
45 * of the box. For example, with a scale of 1.5, a box with width=1 should not
46 * scale to 2px if its X coordinate is 1, because the X coordinate would have
47 * scaled to 2px.
48 */
49static int scale_length(int length, int offset, float scale) {
50 return round((offset + length) * scale) - round(offset * scale);
51}
52
53static void scissor_output(struct wlr_output *wlr_output,
54 pixman_box32_t *rect) {
55 struct wlr_renderer *renderer = wlr_backend_get_renderer(wlr_output->backend);
56 assert(renderer);
57
58 struct wlr_box box = {
59 .x = rect->x1,
60 .y = rect->y1,
61 .width = rect->x2 - rect->x1,
62 .height = rect->y2 - rect->y1,
63 };
64
65 int ow, oh;
66 wlr_output_transformed_resolution(wlr_output, &ow, &oh);
67
68 enum wl_output_transform transform =
69 wlr_output_transform_invert(wlr_output->transform);
70 wlr_box_transform(&box, &box, transform, ow, oh);
71
72 wlr_renderer_scissor(renderer, &box);
73}
74
75static void set_scale_filter(struct wlr_output *wlr_output,
76 struct wlr_texture *texture, enum scale_filter_mode scale_filter) {
77 if (!wlr_texture_is_gles2(texture)) {
78 return;
79 }
80
81 struct wlr_gles2_texture_attribs attribs;
82 wlr_gles2_texture_get_attribs(texture, &attribs);
83
84 glBindTexture(attribs.target, attribs.tex);
85
86 switch (scale_filter) {
87 case SCALE_FILTER_LINEAR:
88 glTexParameteri(attribs.target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
89 break;
90 case SCALE_FILTER_NEAREST:
91 glTexParameteri(attribs.target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
92 break;
93 case SCALE_FILTER_DEFAULT:
94 case SCALE_FILTER_SMART:
95 assert(false); // unreachable
96 }
97}
98
99static void render_texture(struct wlr_output *wlr_output,
100 pixman_region32_t *output_damage, struct wlr_texture *texture,
101 const struct wlr_fbox *src_box, const struct wlr_box *dst_box,
102 const float matrix[static 9], float alpha) {
103 struct wlr_renderer *renderer =
104 wlr_backend_get_renderer(wlr_output->backend);
105 struct sway_output *output = wlr_output->data;
106
107 pixman_region32_t damage;
108 pixman_region32_init(&damage);
109 pixman_region32_union_rect(&damage, &damage, dst_box->x, dst_box->y,
110 dst_box->width, dst_box->height);
111 pixman_region32_intersect(&damage, &damage, output_damage);
112 bool damaged = pixman_region32_not_empty(&damage);
113 if (!damaged) {
114 goto damage_finish;
115 }
116
117 int nrects;
118 pixman_box32_t *rects = pixman_region32_rectangles(&damage, &nrects);
119 for (int i = 0; i < nrects; ++i) {
120 scissor_output(wlr_output, &rects[i]);
121 set_scale_filter(wlr_output, texture, output->scale_filter);
122 if (src_box != NULL) {
123 wlr_render_subtexture_with_matrix(renderer, texture, src_box, matrix, alpha);
124 } else {
125 wlr_render_texture_with_matrix(renderer, texture, matrix, alpha);
126 }
127 }
128
129damage_finish:
130 pixman_region32_fini(&damage);
131}
132
133static void render_surface_iterator(struct sway_output *output,
134 struct sway_view *view, struct wlr_surface *surface,
135 struct wlr_box *_box, void *_data) {
136 struct render_data *data = _data;
137 struct wlr_output *wlr_output = output->wlr_output;
138 pixman_region32_t *output_damage = data->damage;
139 float alpha = data->alpha;
140
141 struct wlr_texture *texture = wlr_surface_get_texture(surface);
142 if (!texture) {
143 return;
144 }
145
146 struct wlr_fbox src_box;
147 wlr_surface_get_buffer_source_box(surface, &src_box);
148
149 struct wlr_box proj_box = *_box;
150 scale_box(&proj_box, wlr_output->scale);
151
152 float matrix[9];
153 enum wl_output_transform transform =
154 wlr_output_transform_invert(surface->current.transform);
155 wlr_matrix_project_box(matrix, &proj_box, transform, 0.0,
156 wlr_output->transform_matrix);
157
158 struct wlr_box dst_box = *_box;
159 struct wlr_box *clip_box = data->clip_box;
160 if (clip_box != NULL) {
161 dst_box.width = fmin(dst_box.width, clip_box->width);
162 dst_box.height = fmin(dst_box.height, clip_box->height);
163 }
164 scale_box(&dst_box, wlr_output->scale);
165
166 render_texture(wlr_output, output_damage, texture,
167 &src_box, &dst_box, matrix, alpha);
168
169 wlr_presentation_surface_sampled_on_output(server.presentation, surface,
170 wlr_output);
171}
172
173static void render_layer_toplevel(struct sway_output *output,
174 pixman_region32_t *damage, struct wl_list *layer_surfaces) {
175 struct render_data data = {
176 .damage = damage,
177 .alpha = 1.0f,
178 };
179 output_layer_for_each_toplevel_surface(output, layer_surfaces,
180 render_surface_iterator, &data);
181}
182
183static void render_layer_popups(struct sway_output *output,
184 pixman_region32_t *damage, struct wl_list *layer_surfaces) {
185 struct render_data data = {
186 .damage = damage,
187 .alpha = 1.0f,
188 };
189 output_layer_for_each_popup_surface(output, layer_surfaces,
190 render_surface_iterator, &data);
191}
192
193#if HAVE_XWAYLAND
194static void render_unmanaged(struct sway_output *output,
195 pixman_region32_t *damage, struct wl_list *unmanaged) {
196 struct render_data data = {
197 .damage = damage,
198 .alpha = 1.0f,
199 };
200 output_unmanaged_for_each_surface(output, unmanaged,
201 render_surface_iterator, &data);
202}
203#endif
204
205static void render_drag_icons(struct sway_output *output,
206 pixman_region32_t *damage, struct wl_list *drag_icons) {
207 struct render_data data = {
208 .damage = damage,
209 .alpha = 1.0f,
210 };
211 output_drag_icons_for_each_surface(output, drag_icons,
212 render_surface_iterator, &data);
213}
214
215// _box.x and .y are expected to be layout-local
216// _box.width and .height are expected to be output-buffer-local
217void render_rect(struct sway_output *output,
218 pixman_region32_t *output_damage, const struct wlr_box *_box,
219 float color[static 4]) {
220 struct wlr_output *wlr_output = output->wlr_output;
221 struct wlr_renderer *renderer =
222 wlr_backend_get_renderer(wlr_output->backend);
223
224 struct wlr_box box;
225 memcpy(&box, _box, sizeof(struct wlr_box));
226 box.x -= output->lx * wlr_output->scale;
227 box.y -= output->ly * wlr_output->scale;
228
229 pixman_region32_t damage;
230 pixman_region32_init(&damage);
231 pixman_region32_union_rect(&damage, &damage, box.x, box.y,
232 box.width, box.height);
233 pixman_region32_intersect(&damage, &damage, output_damage);
234 bool damaged = pixman_region32_not_empty(&damage);
235 if (!damaged) {
236 goto damage_finish;
237 }
238
239 int nrects;
240 pixman_box32_t *rects = pixman_region32_rectangles(&damage, &nrects);
241 for (int i = 0; i < nrects; ++i) {
242 scissor_output(wlr_output, &rects[i]);
243 wlr_render_rect(renderer, &box, color,
244 wlr_output->transform_matrix);
245 }
246
247damage_finish:
248 pixman_region32_fini(&damage);
249}
250
251void premultiply_alpha(float color[4], float opacity) {
252 color[3] *= opacity;
253 color[0] *= color[3];
254 color[1] *= color[3];
255 color[2] *= color[3];
256}
257
258static void render_view_toplevels(struct sway_view *view,
259 struct sway_output *output, pixman_region32_t *damage, float alpha) {
260 struct render_data data = {
261 .damage = damage,
262 .alpha = alpha,
263 };
264 struct wlr_box clip_box;
265 if (!container_is_current_floating(view->container)) {
266 // As we pass the geometry offsets to the surface iterator, we will
267 // need to account for the offsets in the clip dimensions.
268 clip_box.width = view->container->current.content_width + view->geometry.x;
269 clip_box.height = view->container->current.content_height + view->geometry.y;
270 data.clip_box = &clip_box;
271 }
272 // Render all toplevels without descending into popups
273 double ox = view->container->surface_x -
274 output->lx - view->geometry.x;
275 double oy = view->container->surface_y -
276 output->ly - view->geometry.y;
277 output_surface_for_each_surface(output, view->surface, ox, oy,
278 render_surface_iterator, &data);
279}
280
281static void render_view_popups(struct sway_view *view,
282 struct sway_output *output, pixman_region32_t *damage, float alpha) {
283 struct render_data data = {
284 .damage = damage,
285 .alpha = alpha,
286 };
287 output_view_for_each_popup_surface(output, view,
288 render_surface_iterator, &data);
289}
290
291static void render_saved_view(struct sway_view *view,
292 struct sway_output *output, pixman_region32_t *damage, float alpha) {
293 struct wlr_output *wlr_output = output->wlr_output;
294
295 if (wl_list_empty(&view->saved_buffers)) {
296 return;
297 }
298
299 bool floating = container_is_current_floating(view->container);
300
301 struct sway_saved_buffer *saved_buf;
302 wl_list_for_each(saved_buf, &view->saved_buffers, link) {
303 if (!saved_buf->buffer->texture) {
304 continue;
305 }
306
307 struct wlr_box proj_box = {
308 .x = saved_buf->x - view->saved_geometry.x - output->lx,
309 .y = saved_buf->y - view->saved_geometry.y - output->ly,
310 .width = saved_buf->width,
311 .height = saved_buf->height,
312 };
313
314 struct wlr_box output_box = {
315 .width = output->width,
316 .height = output->height,
317 };
318
319 struct wlr_box intersection;
320 bool intersects = wlr_box_intersection(&intersection, &output_box, &proj_box);
321 if (!intersects) {
322 continue;
323 }
324
325 struct wlr_box dst_box = proj_box;
326 scale_box(&proj_box, wlr_output->scale);
327
328 float matrix[9];
329 enum wl_output_transform transform = wlr_output_transform_invert(saved_buf->transform);
330 wlr_matrix_project_box(matrix, &proj_box, transform, 0,
331 wlr_output->transform_matrix);
332
333 if (!floating) {
334 dst_box.width = fmin(dst_box.width,
335 view->container->current.content_width -
336 (saved_buf->x - view->container->current.content_x) + view->saved_geometry.x);
337 dst_box.height = fmin(dst_box.height,
338 view->container->current.content_height -
339 (saved_buf->y - view->container->current.content_y) + view->saved_geometry.y);
340 }
341 scale_box(&dst_box, wlr_output->scale);
342
343 render_texture(wlr_output, damage, saved_buf->buffer->texture,
344 &saved_buf->source_box, &dst_box, matrix, alpha);
345 }
346
347 // FIXME: we should set the surface that this saved buffer originates from
348 // as sampled here.
349 // https://github.com/swaywm/sway/pull/4465#discussion_r321082059
350}
351
352/**
353 * Render a view's surface and left/bottom/right borders.
354 */
355static void render_view(struct sway_output *output, pixman_region32_t *damage,
356 struct sway_container *con, struct border_colors *colors) {
357 struct sway_view *view = con->view;
358 if (!wl_list_empty(&view->saved_buffers)) {
359 render_saved_view(view, output, damage, view->container->alpha);
360 } else if (view->surface) {
361 render_view_toplevels(view, output, damage, view->container->alpha);
362 }
363
364 if (con->current.border == B_NONE || con->current.border == B_CSD) {
365 return;
366 }
367
368 struct wlr_box box;
369 float output_scale = output->wlr_output->scale;
370 float color[4];
371 struct sway_container_state *state = &con->current;
372
373 if (state->border_left) {
374 memcpy(&color, colors->child_border, sizeof(float) * 4);
375 premultiply_alpha(color, con->alpha);
376 box.x = floor(state->x);
377 box.y = floor(state->content_y);
378 box.width = state->border_thickness;
379 box.height = state->content_height;
380 scale_box(&box, output_scale);
381 render_rect(output, damage, &box, color);
382 }
383
384 list_t *siblings = container_get_current_siblings(con);
385 enum sway_container_layout layout =
386 container_current_parent_layout(con);
387
388 if (state->border_right) {
389 if (!container_is_current_floating(con) && siblings->length == 1 && layout == L_HORIZ) {
390 memcpy(&color, colors->indicator, sizeof(float) * 4);
391 } else {
392 memcpy(&color, colors->child_border, sizeof(float) * 4);
393 }
394 premultiply_alpha(color, con->alpha);
395 box.x = floor(state->content_x + state->content_width);
396 box.y = floor(state->content_y);
397 box.width = state->border_thickness;
398 box.height = state->content_height;
399 scale_box(&box, output_scale);
400 render_rect(output, damage, &box, color);
401 }
402
403 if (state->border_bottom) {
404 if (!container_is_current_floating(con) && siblings->length == 1 && layout == L_VERT) {
405 memcpy(&color, colors->indicator, sizeof(float) * 4);
406 } else {
407 memcpy(&color, colors->child_border, sizeof(float) * 4);
408 }
409 premultiply_alpha(color, con->alpha);
410 box.x = floor(state->x);
411 box.y = floor(state->content_y + state->content_height);
412 box.width = state->width;
413 box.height = state->border_thickness;
414 scale_box(&box, output_scale);
415 render_rect(output, damage, &box, color);
416 }
417}
418
419/**
420 * Render a titlebar.
421 *
422 * Care must be taken not to render over the same pixel multiple times,
423 * otherwise the colors will be incorrect when using opacity.
424 *
425 * The height is: 1px border, 3px padding, font height, 3px padding, 1px border
426 * The left side is: 1px border, 2px padding, title
427 */
428static void render_titlebar(struct sway_output *output,
429 pixman_region32_t *output_damage, struct sway_container *con,
430 int x, int y, int width,
431 struct border_colors *colors, struct wlr_texture *title_texture,
432 struct wlr_texture *marks_texture) {
433 struct wlr_box box;
434 float color[4];
435 float output_scale = output->wlr_output->scale;
436 double output_x = output->lx;
437 double output_y = output->ly;
438 int titlebar_border_thickness = config->titlebar_border_thickness;
439 int titlebar_h_padding = config->titlebar_h_padding;
440 int titlebar_v_padding = config->titlebar_v_padding;
441 enum alignment title_align = config->title_align;
442
443 // Single pixel bar above title
444 memcpy(&color, colors->border, sizeof(float) * 4);
445 premultiply_alpha(color, con->alpha);
446 box.x = x;
447 box.y = y;
448 box.width = width;
449 box.height = titlebar_border_thickness;
450 scale_box(&box, output_scale);
451 render_rect(output, output_damage, &box, color);
452
453 // Single pixel bar below title
454 box.x = x;
455 box.y = y + container_titlebar_height() - titlebar_border_thickness;
456 box.width = width;
457 box.height = titlebar_border_thickness;
458 scale_box(&box, output_scale);
459 render_rect(output, output_damage, &box, color);
460
461 // Single pixel left edge
462 box.x = x;
463 box.y = y + titlebar_border_thickness;
464 box.width = titlebar_border_thickness;
465 box.height = container_titlebar_height() - titlebar_border_thickness * 2;
466 scale_box(&box, output_scale);
467 render_rect(output, output_damage, &box, color);
468
469 // Single pixel right edge
470 box.x = x + width - titlebar_border_thickness;
471 box.y = y + titlebar_border_thickness;
472 box.width = titlebar_border_thickness;
473 box.height = container_titlebar_height() - titlebar_border_thickness * 2;
474 scale_box(&box, output_scale);
475 render_rect(output, output_damage, &box, color);
476
477 int inner_x = x - output_x + titlebar_h_padding;
478 int bg_y = y + titlebar_border_thickness;
479 size_t inner_width = width - titlebar_h_padding * 2;
480
481 // output-buffer local
482 int ob_inner_x = round(inner_x * output_scale);
483 int ob_inner_width = scale_length(inner_width, inner_x, output_scale);
484 int ob_bg_height = scale_length(
485 (titlebar_v_padding - titlebar_border_thickness) * 2 +
486 config->font_height, bg_y, output_scale);
487
488 // Marks
489 int ob_marks_x = 0; // output-buffer-local
490 int ob_marks_width = 0; // output-buffer-local
491 if (config->show_marks && marks_texture) {
492 struct wlr_box texture_box = {
493 .width = marks_texture->width,
494 .height = marks_texture->height,
495 };
496 ob_marks_width = texture_box.width;
497
498 // The marks texture might be shorter than the config->font_height, in
499 // which case we need to pad it as evenly as possible above and below.
500 int ob_padding_total = ob_bg_height - texture_box.height;
501 int ob_padding_above = floor(ob_padding_total / 2.0);
502 int ob_padding_below = ceil(ob_padding_total / 2.0);
503
504 // Render texture. If the title is on the right, the marks will be on
505 // the left. Otherwise, they will be on the right.
506 if (title_align == ALIGN_RIGHT || texture_box.width > ob_inner_width) {
507 texture_box.x = ob_inner_x;
508 } else {
509 texture_box.x = ob_inner_x + ob_inner_width - texture_box.width;
510 }
511 ob_marks_x = texture_box.x;
512
513 texture_box.y = round((bg_y - output_y) * output_scale) +
514 ob_padding_above;
515
516 float matrix[9];
517 wlr_matrix_project_box(matrix, &texture_box,
518 WL_OUTPUT_TRANSFORM_NORMAL,
519 0.0, output->wlr_output->transform_matrix);
520
521 if (ob_inner_width < texture_box.width) {
522 texture_box.width = ob_inner_width;
523 }
524 render_texture(output->wlr_output, output_damage, marks_texture,
525 NULL, &texture_box, matrix, con->alpha);
526
527 // Padding above
528 memcpy(&color, colors->background, sizeof(float) * 4);
529 premultiply_alpha(color, con->alpha);
530 box.x = texture_box.x + round(output_x * output_scale);
531 box.y = round((y + titlebar_border_thickness) * output_scale);
532 box.width = texture_box.width;
533 box.height = ob_padding_above;
534 render_rect(output, output_damage, &box, color);
535
536 // Padding below
537 box.y += ob_padding_above + texture_box.height;
538 box.height = ob_padding_below;
539 render_rect(output, output_damage, &box, color);
540 }
541
542 // Title text
543 int ob_title_x = 0; // output-buffer-local
544 int ob_title_width = 0; // output-buffer-local
545 if (title_texture) {
546 struct wlr_box texture_box = {
547 .width = title_texture->width,
548 .height = title_texture->height,
549 };
550
551 // The effective output may be NULL when con is not on any output.
552 // This can happen because we render all children of containers,
553 // even those that are out of the bounds of any output.
554 struct sway_output *effective = container_get_effective_output(con);
555 float title_scale = effective ? effective->wlr_output->scale : output_scale;
556 texture_box.width = texture_box.width * output_scale / title_scale;
557 texture_box.height = texture_box.height * output_scale / title_scale;
558 ob_title_width = texture_box.width;
559
560 // The title texture might be shorter than the config->font_height,
561 // in which case we need to pad it above and below.
562 int ob_padding_above = round((config->font_baseline -
563 con->title_baseline + titlebar_v_padding -
564 titlebar_border_thickness) * output_scale);
565 int ob_padding_below = ob_bg_height - ob_padding_above -
566 texture_box.height;
567
568 // Render texture
569 if (texture_box.width > ob_inner_width - ob_marks_width) {
570 texture_box.x = (title_align == ALIGN_RIGHT && ob_marks_width)
571 ? ob_marks_x + ob_marks_width : ob_inner_x;
572 } else if (title_align == ALIGN_LEFT) {
573 texture_box.x = ob_inner_x;
574 } else if (title_align == ALIGN_CENTER) {
575 // If there are marks visible, center between the edge and marks.
576 // Otherwise, center in the inner area.
577 if (ob_marks_width) {
578 texture_box.x = (ob_inner_x + ob_marks_x) / 2
579 - texture_box.width / 2;
580 } else {
581 texture_box.x = ob_inner_x + ob_inner_width / 2
582 - texture_box.width / 2;
583 }
584 } else {
585 texture_box.x = ob_inner_x + ob_inner_width - texture_box.width;
586 }
587 ob_title_x = texture_box.x;
588
589 texture_box.y =
590 round((bg_y - output_y) * output_scale) + ob_padding_above;
591
592 float matrix[9];
593 wlr_matrix_project_box(matrix, &texture_box,
594 WL_OUTPUT_TRANSFORM_NORMAL,
595 0.0, output->wlr_output->transform_matrix);
596
597 if (ob_inner_width - ob_marks_width < texture_box.width) {
598 texture_box.width = ob_inner_width - ob_marks_width;
599 }
600
601 render_texture(output->wlr_output, output_damage, title_texture,
602 NULL, &texture_box, matrix, con->alpha);
603
604 // Padding above
605 memcpy(&color, colors->background, sizeof(float) * 4);
606 premultiply_alpha(color, con->alpha);
607 box.x = texture_box.x + round(output_x * output_scale);
608 box.y = round((y + titlebar_border_thickness) * output_scale);
609 box.width = texture_box.width;
610 box.height = ob_padding_above;
611 render_rect(output, output_damage, &box, color);
612
613 // Padding below
614 box.y += ob_padding_above + texture_box.height;
615 box.height = ob_padding_below;
616 render_rect(output, output_damage, &box, color);
617 }
618
619 // Determine the left + right extends of the textures (output-buffer local)
620 int ob_left_x, ob_left_width, ob_right_x, ob_right_width;
621 if (ob_title_width == 0 && ob_marks_width == 0) {
622 ob_left_x = ob_inner_x;
623 ob_left_width = 0;
624 ob_right_x = ob_inner_x;
625 ob_right_width = 0;
626 } else if (ob_title_x < ob_marks_x) {
627 ob_left_x = ob_title_x;
628 ob_left_width = ob_title_width;
629 ob_right_x = ob_marks_x;
630 ob_right_width = ob_marks_width;
631 } else {
632 ob_left_x = ob_marks_x;
633 ob_left_width = ob_marks_width;
634 ob_right_x = ob_title_x;
635 ob_right_width = ob_title_width;
636 }
637 if (ob_left_x < ob_inner_x) {
638 ob_left_x = ob_inner_x;
639 } else if (ob_left_x + ob_left_width > ob_right_x + ob_right_width) {
640 ob_right_x = ob_left_x;
641 ob_right_width = ob_left_width;
642 }
643
644 // Filler between title and marks
645 box.width = ob_right_x - ob_left_x - ob_left_width;
646 if (box.width > 0) {
647 box.x = ob_left_x + ob_left_width + round(output_x * output_scale);
648 box.y = round(bg_y * output_scale);
649 box.height = ob_bg_height;
650 render_rect(output, output_damage, &box, color);
651 }
652
653 // Padding on left side
654 box.x = x + titlebar_border_thickness;
655 box.y = y + titlebar_border_thickness;
656 box.width = titlebar_h_padding - titlebar_border_thickness;
657 box.height = (titlebar_v_padding - titlebar_border_thickness) * 2 +
658 config->font_height;
659 scale_box(&box, output_scale);
660 int left_x = ob_left_x + round(output_x * output_scale);
661 if (box.x + box.width < left_x) {
662 box.width += left_x - box.x - box.width;
663 }
664 render_rect(output, output_damage, &box, color);
665
666 // Padding on right side
667 box.x = x + width - titlebar_h_padding;
668 box.y = y + titlebar_border_thickness;
669 box.width = titlebar_h_padding - titlebar_border_thickness;
670 box.height = (titlebar_v_padding - titlebar_border_thickness) * 2 +
671 config->font_height;
672 scale_box(&box, output_scale);
673 int right_rx = ob_right_x + ob_right_width + round(output_x * output_scale);
674 if (right_rx < box.x) {
675 box.width += box.x - right_rx;
676 box.x = right_rx;
677 }
678 render_rect(output, output_damage, &box, color);
679}
680
681/**
682 * Render the top border line for a view using "border pixel".
683 */
684static void render_top_border(struct sway_output *output,
685 pixman_region32_t *output_damage, struct sway_container *con,
686 struct border_colors *colors) {
687 struct sway_container_state *state = &con->current;
688 if (!state->border_top) {
689 return;
690 }
691 struct wlr_box box;
692 float color[4];
693 float output_scale = output->wlr_output->scale;
694
695 // Child border - top edge
696 memcpy(&color, colors->child_border, sizeof(float) * 4);
697 premultiply_alpha(color, con->alpha);
698 box.x = floor(state->x);
699 box.y = floor(state->y);
700 box.width = state->width;
701 box.height = state->border_thickness;
702 scale_box(&box, output_scale);
703 render_rect(output, output_damage, &box, color);
704}
705
706struct parent_data {
707 enum sway_container_layout layout;
708 struct wlr_box box;
709 list_t *children;
710 bool focused;
711 struct sway_container *active_child;
712};
713
714static void render_container(struct sway_output *output,
715 pixman_region32_t *damage, struct sway_container *con, bool parent_focused);
716
717/**
718 * Render a container's children using a L_HORIZ or L_VERT layout.
719 *
720 * Wrap child views in borders and leave child containers borderless because
721 * they'll apply their own borders to their children.
722 */
723static void render_containers_linear(struct sway_output *output,
724 pixman_region32_t *damage, struct parent_data *parent) {
725 for (int i = 0; i < parent->children->length; ++i) {
726 struct sway_container *child = parent->children->items[i];
727
728 if (child->view) {
729 struct sway_view *view = child->view;
730 struct border_colors *colors;
731 struct wlr_texture *title_texture;
732 struct wlr_texture *marks_texture;
733 struct sway_container_state *state = &child->current;
734
735 if (view_is_urgent(view)) {
736 colors = &config->border_colors.urgent;
737 title_texture = child->title_urgent;
738 marks_texture = child->marks_urgent;
739 } else if (state->focused || parent->focused) {
740 colors = &config->border_colors.focused;
741 title_texture = child->title_focused;
742 marks_texture = child->marks_focused;
743 } else if (child == parent->active_child) {
744 colors = &config->border_colors.focused_inactive;
745 title_texture = child->title_focused_inactive;
746 marks_texture = child->marks_focused_inactive;
747 } else {
748 colors = &config->border_colors.unfocused;
749 title_texture = child->title_unfocused;
750 marks_texture = child->marks_unfocused;
751 }
752
753 if (state->border == B_NORMAL) {
754 render_titlebar(output, damage, child, floor(state->x),
755 floor(state->y), state->width, colors,
756 title_texture, marks_texture);
757 } else if (state->border == B_PIXEL) {
758 render_top_border(output, damage, child, colors);
759 }
760 render_view(output, damage, child, colors);
761 } else {
762 render_container(output, damage, child,
763 parent->focused || child->current.focused);
764 }
765 }
766}
767
768/**
769 * Render a container's children using the L_TABBED layout.
770 */
771static void render_containers_tabbed(struct sway_output *output,
772 pixman_region32_t *damage, struct parent_data *parent) {
773 if (!parent->children->length) {
774 return;
775 }
776 struct sway_container *current = parent->active_child;
777 struct border_colors *current_colors = &config->border_colors.unfocused;
778 int tab_width = parent->box.width / parent->children->length;
779
780 // Render tabs
781 for (int i = 0; i < parent->children->length; ++i) {
782 struct sway_container *child = parent->children->items[i];
783 struct sway_view *view = child->view;
784 struct sway_container_state *cstate = &child->current;
785 struct border_colors *colors;
786 struct wlr_texture *title_texture;
787 struct wlr_texture *marks_texture;
788 bool urgent = view ?
789 view_is_urgent(view) : container_has_urgent_child(child);
790
791 if (urgent) {
792 colors = &config->border_colors.urgent;
793 title_texture = child->title_urgent;
794 marks_texture = child->marks_urgent;
795 } else if (cstate->focused || parent->focused) {
796 colors = &config->border_colors.focused;
797 title_texture = child->title_focused;
798 marks_texture = child->marks_focused;
799 } else if (child == parent->active_child) {
800 colors = &config->border_colors.focused_inactive;
801 title_texture = child->title_focused_inactive;
802 marks_texture = child->marks_focused_inactive;
803 } else {
804 colors = &config->border_colors.unfocused;
805 title_texture = child->title_unfocused;
806 marks_texture = child->marks_unfocused;
807 }
808
809 int x = floor(cstate->x + tab_width * i);
810
811 // Make last tab use the remaining width of the parent
812 if (i == parent->children->length - 1) {
813 tab_width = parent->box.width - tab_width * i;
814 }
815
816 render_titlebar(output, damage, child, x, parent->box.y, tab_width,
817 colors, title_texture, marks_texture);
818
819 if (child == current) {
820 current_colors = colors;
821 }
822 }
823
824 // Render surface and left/right/bottom borders
825 if (current->view) {
826 render_view(output, damage, current, current_colors);
827 } else {
828 render_container(output, damage, current,
829 parent->focused || current->current.focused);
830 }
831}
832
833/**
834 * Render a container's children using the L_STACKED layout.
835 */
836static void render_containers_stacked(struct sway_output *output,
837 pixman_region32_t *damage, struct parent_data *parent) {
838 if (!parent->children->length) {
839 return;
840 }
841 struct sway_container *current = parent->active_child;
842 struct border_colors *current_colors = &config->border_colors.unfocused;
843 size_t titlebar_height = container_titlebar_height();
844
845 // Render titles
846 for (int i = 0; i < parent->children->length; ++i) {
847 struct sway_container *child = parent->children->items[i];
848 struct sway_view *view = child->view;
849 struct sway_container_state *cstate = &child->current;
850 struct border_colors *colors;
851 struct wlr_texture *title_texture;
852 struct wlr_texture *marks_texture;
853 bool urgent = view ?
854 view_is_urgent(view) : container_has_urgent_child(child);
855
856 if (urgent) {
857 colors = &config->border_colors.urgent;
858 title_texture = child->title_urgent;
859 marks_texture = child->marks_urgent;
860 } else if (cstate->focused || parent->focused) {
861 colors = &config->border_colors.focused;
862 title_texture = child->title_focused;
863 marks_texture = child->marks_focused;
864 } else if (child == parent->active_child) {
865 colors = &config->border_colors.focused_inactive;
866 title_texture = child->title_focused_inactive;
867 marks_texture = child->marks_focused_inactive;
868 } else {
869 colors = &config->border_colors.unfocused;
870 title_texture = child->title_unfocused;
871 marks_texture = child->marks_unfocused;
872 }
873
874 int y = parent->box.y + titlebar_height * i;
875 render_titlebar(output, damage, child, parent->box.x, y,
876 parent->box.width, colors, title_texture, marks_texture);
877
878 if (child == current) {
879 current_colors = colors;
880 }
881 }
882
883 // Render surface and left/right/bottom borders
884 if (current->view) {
885 render_view(output, damage, current, current_colors);
886 } else {
887 render_container(output, damage, current,
888 parent->focused || current->current.focused);
889 }
890}
891
892static void render_containers(struct sway_output *output,
893 pixman_region32_t *damage, struct parent_data *parent) {
894 if (config->hide_lone_tab && parent->children->length == 1) {
895 struct sway_container *child = parent->children->items[0];
896 if (child->view) {
897 render_containers_linear(output,damage, parent);
898 return;
899 }
900 }
901
902 switch (parent->layout) {
903 case L_NONE:
904 case L_HORIZ:
905 case L_VERT:
906 render_containers_linear(output, damage, parent);
907 break;
908 case L_STACKED:
909 render_containers_stacked(output, damage, parent);
910 break;
911 case L_TABBED:
912 render_containers_tabbed(output, damage, parent);
913 break;
914 }
915}
916
917static void render_container(struct sway_output *output,
918 pixman_region32_t *damage, struct sway_container *con, bool focused) {
919 struct parent_data data = {
920 .layout = con->current.layout,
921 .box = {
922 .x = floor(con->current.x),
923 .y = floor(con->current.y),
924 .width = con->current.width,
925 .height = con->current.height,
926 },
927 .children = con->current.children,
928 .focused = focused,
929 .active_child = con->current.focused_inactive_child,
930 };
931 render_containers(output, damage, &data);
932}
933
934static void render_workspace(struct sway_output *output,
935 pixman_region32_t *damage, struct sway_workspace *ws, bool focused) {
936 struct parent_data data = {
937 .layout = ws->current.layout,
938 .box = {
939 .x = floor(ws->current.x),
940 .y = floor(ws->current.y),
941 .width = ws->current.width,
942 .height = ws->current.height,
943 },
944 .children = ws->current.tiling,
945 .focused = focused,
946 .active_child = ws->current.focused_inactive_child,
947 };
948 render_containers(output, damage, &data);
949}
950
951static void render_floating_container(struct sway_output *soutput,
952 pixman_region32_t *damage, struct sway_container *con) {
953 if (con->view) {
954 struct sway_view *view = con->view;
955 struct border_colors *colors;
956 struct wlr_texture *title_texture;
957 struct wlr_texture *marks_texture;
958
959 if (view_is_urgent(view)) {
960 colors = &config->border_colors.urgent;
961 title_texture = con->title_urgent;
962 marks_texture = con->marks_urgent;
963 } else if (con->current.focused) {
964 colors = &config->border_colors.focused;
965 title_texture = con->title_focused;
966 marks_texture = con->marks_focused;
967 } else {
968 colors = &config->border_colors.unfocused;
969 title_texture = con->title_unfocused;
970 marks_texture = con->marks_unfocused;
971 }
972
973 if (con->current.border == B_NORMAL) {
974 render_titlebar(soutput, damage, con, floor(con->current.x),
975 floor(con->current.y), con->current.width, colors,
976 title_texture, marks_texture);
977 } else if (con->current.border == B_PIXEL) {
978 render_top_border(soutput, damage, con, colors);
979 }
980 render_view(soutput, damage, con, colors);
981 } else {
982 render_container(soutput, damage, con, con->current.focused);
983 }
984}
985
986static void render_floating(struct sway_output *soutput,
987 pixman_region32_t *damage) {
988 for (int i = 0; i < root->outputs->length; ++i) {
989 struct sway_output *output = root->outputs->items[i];
990 for (int j = 0; j < output->current.workspaces->length; ++j) {
991 struct sway_workspace *ws = output->current.workspaces->items[j];
992 if (!workspace_is_visible(ws)) {
993 continue;
994 }
995 for (int k = 0; k < ws->current.floating->length; ++k) {
996 struct sway_container *floater = ws->current.floating->items[k];
997 if (floater->current.fullscreen_mode != FULLSCREEN_NONE) {
998 continue;
999 }
1000 render_floating_container(soutput, damage, floater);
1001 }
1002 }
1003 }
1004}
1005
1006static void render_seatops(struct sway_output *output,
1007 pixman_region32_t *damage) {
1008 struct sway_seat *seat;
1009 wl_list_for_each(seat, &server.input->seats, link) {
1010 seatop_render(seat, output, damage);
1011 }
1012}
1013
1014void output_render(struct sway_output *output, struct timespec *when,
1015 pixman_region32_t *damage) {
1016 struct wlr_output *wlr_output = output->wlr_output;
1017
1018 struct wlr_renderer *renderer =
1019 wlr_backend_get_renderer(wlr_output->backend);
1020 if (!sway_assert(renderer != NULL,
1021 "expected the output backend to have a renderer")) {
1022 return;
1023 }
1024
1025 struct sway_workspace *workspace = output->current.active_workspace;
1026 if (workspace == NULL) {
1027 return;
1028 }
1029
1030 struct sway_container *fullscreen_con = root->fullscreen_global;
1031 if (!fullscreen_con) {
1032 fullscreen_con = workspace->current.fullscreen;
1033 }
1034
1035 wlr_renderer_begin(renderer, wlr_output->width, wlr_output->height);
1036
1037 if (!pixman_region32_not_empty(damage)) {
1038 // Output isn't damaged but needs buffer swap
1039 goto renderer_end;
1040 }
1041
1042 if (debug.damage == DAMAGE_HIGHLIGHT) {
1043 wlr_renderer_clear(renderer, (float[]){1, 1, 0, 1});
1044 } else if (debug.damage == DAMAGE_RERENDER) {
1045 int width, height;
1046 wlr_output_transformed_resolution(wlr_output, &width, &height);
1047 pixman_region32_union_rect(damage, damage, 0, 0, width, height);
1048 }
1049
1050 if (output_has_opaque_overlay_layer_surface(output)) {
1051 goto render_overlay;
1052 }
1053
1054 if (fullscreen_con) {
1055 float clear_color[] = {0.0f, 0.0f, 0.0f, 1.0f};
1056
1057 int nrects;
1058 pixman_box32_t *rects = pixman_region32_rectangles(damage, &nrects);
1059 for (int i = 0; i < nrects; ++i) {
1060 scissor_output(wlr_output, &rects[i]);
1061 wlr_renderer_clear(renderer, clear_color);
1062 }
1063
1064 if (fullscreen_con->view) {
1065 if (!wl_list_empty(&fullscreen_con->view->saved_buffers)) {
1066 render_saved_view(fullscreen_con->view, output, damage, 1.0f);
1067 } else if (fullscreen_con->view->surface) {
1068 render_view_toplevels(fullscreen_con->view,
1069 output, damage, 1.0f);
1070 }
1071 } else {
1072 render_container(output, damage, fullscreen_con,
1073 fullscreen_con->current.focused);
1074 }
1075
1076 for (int i = 0; i < workspace->current.floating->length; ++i) {
1077 struct sway_container *floater =
1078 workspace->current.floating->items[i];
1079 if (container_is_transient_for(floater, fullscreen_con)) {
1080 render_floating_container(output, damage, floater);
1081 }
1082 }
1083#if HAVE_XWAYLAND
1084 render_unmanaged(output, damage, &root->xwayland_unmanaged);
1085#endif
1086 } else {
1087 float clear_color[] = {0.25f, 0.25f, 0.25f, 1.0f};
1088
1089 int nrects;
1090 pixman_box32_t *rects = pixman_region32_rectangles(damage, &nrects);
1091 for (int i = 0; i < nrects; ++i) {
1092 scissor_output(wlr_output, &rects[i]);
1093 wlr_renderer_clear(renderer, clear_color);
1094 }
1095
1096 render_layer_toplevel(output, damage,
1097 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND]);
1098 render_layer_toplevel(output, damage,
1099 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM]);
1100
1101 render_workspace(output, damage, workspace, workspace->current.focused);
1102 render_floating(output, damage);
1103#if HAVE_XWAYLAND
1104 render_unmanaged(output, damage, &root->xwayland_unmanaged);
1105#endif
1106 render_layer_toplevel(output, damage,
1107 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]);
1108
1109 render_layer_popups(output, damage,
1110 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND]);
1111 render_layer_popups(output, damage,
1112 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM]);
1113 render_layer_popups(output, damage,
1114 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]);
1115 }
1116
1117 render_seatops(output, damage);
1118
1119 struct sway_seat *seat = input_manager_current_seat();
1120 struct sway_container *focus = seat_get_focused_container(seat);
1121 if (focus && focus->view) {
1122 render_view_popups(focus->view, output, damage, focus->alpha);
1123 }
1124
1125render_overlay:
1126 render_layer_toplevel(output, damage,
1127 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY]);
1128 render_layer_popups(output, damage,
1129 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY]);
1130 render_drag_icons(output, damage, &root->drag_icons);
1131
1132renderer_end:
1133 wlr_renderer_scissor(renderer, NULL);
1134 wlr_output_render_software_cursors(wlr_output, damage);
1135 wlr_renderer_end(renderer);
1136
1137 int width, height;
1138 wlr_output_transformed_resolution(wlr_output, &width, &height);
1139
1140 pixman_region32_t frame_damage;
1141 pixman_region32_init(&frame_damage);
1142
1143 enum wl_output_transform transform =
1144 wlr_output_transform_invert(wlr_output->transform);
1145 wlr_region_transform(&frame_damage, &output->damage->current,
1146 transform, width, height);
1147
1148 if (debug.damage == DAMAGE_HIGHLIGHT) {
1149 pixman_region32_union_rect(&frame_damage, &frame_damage,
1150 0, 0, wlr_output->width, wlr_output->height);
1151 }
1152
1153 wlr_output_set_damage(wlr_output, &frame_damage);
1154 pixman_region32_fini(&frame_damage);
1155
1156 if (!wlr_output_commit(wlr_output)) {
1157 return;
1158 }
1159 output->last_frame = *when;
1160}
diff --git a/sway/desktop/surface.c b/sway/desktop/surface.c
deleted file mode 100644
index 767b2045..00000000
--- a/sway/desktop/surface.c
+++ /dev/null
@@ -1,46 +0,0 @@
1#define _POSIX_C_SOURCE 200112L
2#include <stdlib.h>
3#include <time.h>
4#include <wlr/types/wlr_surface.h>
5#include "sway/server.h"
6#include "sway/surface.h"
7
8static void handle_destroy(struct wl_listener *listener, void *data) {
9 struct sway_surface *surface = wl_container_of(listener, surface, destroy);
10
11 surface->wlr_surface->data = NULL;
12 wl_list_remove(&surface->destroy.link);
13
14 if (surface->frame_done_timer) {
15 wl_event_source_remove(surface->frame_done_timer);
16 }
17
18 free(surface);
19}
20
21static int surface_frame_done_timer_handler(void *data) {
22 struct sway_surface *surface = data;
23
24 struct timespec now;
25 clock_gettime(CLOCK_MONOTONIC, &now);
26 wlr_surface_send_frame_done(surface->wlr_surface, &now);
27
28 return 0;
29}
30
31void handle_compositor_new_surface(struct wl_listener *listener, void *data) {
32 struct wlr_surface *wlr_surface = data;
33
34 struct sway_surface *surface = calloc(1, sizeof(struct sway_surface));
35 surface->wlr_surface = wlr_surface;
36 wlr_surface->data = surface;
37
38 surface->destroy.notify = handle_destroy;
39 wl_signal_add(&wlr_surface->events.destroy, &surface->destroy);
40
41 surface->frame_done_timer = wl_event_loop_add_timer(server.wl_event_loop,
42 surface_frame_done_timer_handler, surface);
43 if (!surface->frame_done_timer) {
44 wl_resource_post_no_memory(wlr_surface->resource);
45 }
46}
diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c
index b1f3fb32..e464ff1a 100644
--- a/sway/desktop/transaction.c
+++ b/sway/desktop/transaction.c
@@ -1,11 +1,10 @@
1#define _POSIX_C_SOURCE 200809L
2#include <stdbool.h> 1#include <stdbool.h>
3#include <stdlib.h> 2#include <stdlib.h>
4#include <string.h> 3#include <string.h>
5#include <time.h> 4#include <time.h>
6#include <wlr/types/wlr_buffer.h> 5#include <wlr/types/wlr_buffer.h>
7#include "sway/config.h" 6#include "sway/config.h"
8#include "sway/desktop.h" 7#include "sway/scene_descriptor.h"
9#include "sway/desktop/idle_inhibit_v1.h" 8#include "sway/desktop/idle_inhibit_v1.h"
10#include "sway/desktop/transaction.h" 9#include "sway/desktop/transaction.h"
11#include "sway/input/cursor.h" 10#include "sway/input/cursor.h"
@@ -214,39 +213,20 @@ static void transaction_add_node(struct sway_transaction *transaction,
214 213
215static void apply_output_state(struct sway_output *output, 214static void apply_output_state(struct sway_output *output,
216 struct sway_output_state *state) { 215 struct sway_output_state *state) {
217 output_damage_whole(output);
218 list_free(output->current.workspaces); 216 list_free(output->current.workspaces);
219 memcpy(&output->current, state, sizeof(struct sway_output_state)); 217 memcpy(&output->current, state, sizeof(struct sway_output_state));
220 output_damage_whole(output);
221} 218}
222 219
223static void apply_workspace_state(struct sway_workspace *ws, 220static void apply_workspace_state(struct sway_workspace *ws,
224 struct sway_workspace_state *state) { 221 struct sway_workspace_state *state) {
225 output_damage_whole(ws->current.output);
226 list_free(ws->current.floating); 222 list_free(ws->current.floating);
227 list_free(ws->current.tiling); 223 list_free(ws->current.tiling);
228 memcpy(&ws->current, state, sizeof(struct sway_workspace_state)); 224 memcpy(&ws->current, state, sizeof(struct sway_workspace_state));
229 output_damage_whole(ws->current.output);
230} 225}
231 226
232static void apply_container_state(struct sway_container *container, 227static void apply_container_state(struct sway_container *container,
233 struct sway_container_state *state) { 228 struct sway_container_state *state) {
234 struct sway_view *view = container->view; 229 struct sway_view *view = container->view;
235 // Damage the old location
236 desktop_damage_whole_container(container);
237 if (view && !wl_list_empty(&view->saved_buffers)) {
238 struct sway_saved_buffer *saved_buf;
239 wl_list_for_each(saved_buf, &view->saved_buffers, link) {
240 struct wlr_box box = {
241 .x = saved_buf->x - view->saved_geometry.x,
242 .y = saved_buf->y - view->saved_geometry.y,
243 .width = saved_buf->width,
244 .height = saved_buf->height,
245 };
246 desktop_damage_box(&box);
247 }
248 }
249
250 // There are separate children lists for each instruction state, the 230 // There are separate children lists for each instruction state, the
251 // container's current state and the container's pending state 231 // container's current state and the container's pending state
252 // (ie. con->children). The list itself needs to be freed here. 232 // (ie. con->children). The list itself needs to be freed here.
@@ -256,35 +236,443 @@ static void apply_container_state(struct sway_container *container,
256 236
257 memcpy(&container->current, state, sizeof(struct sway_container_state)); 237 memcpy(&container->current, state, sizeof(struct sway_container_state));
258 238
259 if (view && !wl_list_empty(&view->saved_buffers)) { 239 if (view) {
260 if (!container->node.destroying || container->node.ntxnrefs == 1) { 240 if (view->saved_surface_tree) {
261 view_remove_saved_buffer(view); 241 if (!container->node.destroying || container->node.ntxnrefs == 1) {
242 view_remove_saved_buffer(view);
243 }
244 }
245
246 // If the view hasn't responded to the configure, center it within
247 // the container. This is important for fullscreen views which
248 // refuse to resize to the size of the output.
249 if (view->surface) {
250 view_center_and_clip_surface(view);
251 }
252 }
253}
254
255static void arrange_title_bar(struct sway_container *con,
256 int x, int y, int width, int height) {
257 container_update(con);
258
259 bool has_title_bar = height > 0;
260 wlr_scene_node_set_enabled(&con->title_bar.tree->node, has_title_bar);
261 if (!has_title_bar) {
262 return;
263 }
264
265 wlr_scene_node_set_position(&con->title_bar.tree->node, x, y);
266
267 con->title_width = width;
268 container_arrange_title_bar(con);
269}
270
271static void disable_container(struct sway_container *con) {
272 if (con->view) {
273 wlr_scene_node_reparent(&con->view->scene_tree->node, con->content_tree);
274 } else {
275 for (int i = 0; i < con->current.children->length; i++) {
276 struct sway_container *child = con->current.children->items[i];
277
278 wlr_scene_node_reparent(&child->scene_tree->node, con->content_tree);
279
280 disable_container(child);
281 }
282 }
283}
284
285static void arrange_container(struct sway_container *con,
286 int width, int height, bool title_bar, int gaps);
287
288static void arrange_children(enum sway_container_layout layout, list_t *children,
289 struct sway_container *active, struct wlr_scene_tree *content,
290 int width, int height, int gaps) {
291 int title_bar_height = container_titlebar_height();
292
293 if (layout == L_TABBED) {
294 struct sway_container *first = children->length == 1 ?
295 ((struct sway_container *)children->items[0]) : NULL;
296 if (config->hide_lone_tab && first && first->view &&
297 first->current.border != B_NORMAL) {
298 title_bar_height = 0;
299 }
300
301 double w = (double) width / children->length;
302 int title_offset = 0;
303 for (int i = 0; i < children->length; i++) {
304 struct sway_container *child = children->items[i];
305 bool activated = child == active;
306 int next_title_offset = round(w * i + w);
307
308 arrange_title_bar(child, title_offset, -title_bar_height,
309 next_title_offset - title_offset, title_bar_height);
310 wlr_scene_node_set_enabled(&child->border.tree->node, activated);
311 wlr_scene_node_set_position(&child->scene_tree->node, 0, title_bar_height);
312 wlr_scene_node_reparent(&child->scene_tree->node, content);
313
314 if (activated) {
315 arrange_container(child, width, height - title_bar_height,
316 false, 0);
317 } else {
318 disable_container(child);
319 }
320
321 title_offset = next_title_offset;
322 }
323 } else if (layout == L_STACKED) {
324 struct sway_container *first = children->length == 1 ?
325 ((struct sway_container *)children->items[0]) : NULL;
326 if (config->hide_lone_tab && first && first->view &&
327 first->current.border != B_NORMAL) {
328 title_bar_height = 0;
329 }
330
331 int title_height = title_bar_height * children->length;
332
333 int y = 0;
334 for (int i = 0; i < children->length; i++) {
335 struct sway_container *child = children->items[i];
336 bool activated = child == active;
337
338 arrange_title_bar(child, 0, y - title_height, width, title_bar_height);
339 wlr_scene_node_set_enabled(&child->border.tree->node, activated);
340 wlr_scene_node_set_position(&child->scene_tree->node, 0, title_height);
341 wlr_scene_node_reparent(&child->scene_tree->node, content);
342
343 if (activated) {
344 arrange_container(child, width, height - title_height,
345 false, 0);
346 } else {
347 disable_container(child);
348 }
349
350 y += title_bar_height;
351 }
352 } else if (layout == L_VERT) {
353 int off = 0;
354 for (int i = 0; i < children->length; i++) {
355 struct sway_container *child = children->items[i];
356 int cheight = child->current.height;
357
358 wlr_scene_node_set_enabled(&child->border.tree->node, true);
359 wlr_scene_node_set_position(&child->scene_tree->node, 0, off);
360 wlr_scene_node_reparent(&child->scene_tree->node, content);
361 arrange_container(child, width, cheight, true, gaps);
362 off += cheight + gaps;
363 }
364 } else if (layout == L_HORIZ) {
365 int off = 0;
366 for (int i = 0; i < children->length; i++) {
367 struct sway_container *child = children->items[i];
368 int cwidth = child->current.width;
369
370 wlr_scene_node_set_enabled(&child->border.tree->node, true);
371 wlr_scene_node_set_position(&child->scene_tree->node, off, 0);
372 wlr_scene_node_reparent(&child->scene_tree->node, content);
373 arrange_container(child, cwidth, height, true, gaps);
374 off += cwidth + gaps;
375 }
376 } else {
377 sway_assert(false, "unreachable");
378 }
379}
380
381static void arrange_container(struct sway_container *con,
382 int width, int height, bool title_bar, int gaps) {
383 // this container might have previously been in the scratchpad,
384 // make sure it's enabled for viewing
385 wlr_scene_node_set_enabled(&con->scene_tree->node, true);
386
387 if (con->output_handler) {
388 wlr_scene_buffer_set_dest_size(con->output_handler, width, height);
389 }
390
391 if (con->view) {
392 int border_top = container_titlebar_height();
393 int border_width = con->current.border_thickness;
394
395 if (title_bar && con->current.border != B_NORMAL) {
396 wlr_scene_node_set_enabled(&con->title_bar.tree->node, false);
397 wlr_scene_node_set_enabled(&con->border.top->node, true);
398 } else {
399 wlr_scene_node_set_enabled(&con->border.top->node, false);
400 }
401
402 if (con->current.border == B_NORMAL) {
403 if (title_bar) {
404 arrange_title_bar(con, 0, 0, width, border_top);
405 } else {
406 border_top = 0;
407 // should be handled by the parent container
408 }
409 } else if (con->current.border == B_PIXEL) {
410 container_update(con);
411 border_top = title_bar && con->current.border_top ? border_width : 0;
412 } else if (con->current.border == B_NONE) {
413 container_update(con);
414 border_top = 0;
415 border_width = 0;
416 } else if (con->current.border == B_CSD) {
417 border_top = 0;
418 border_width = 0;
419 } else {
420 sway_assert(false, "unreachable");
421 }
422
423 int border_bottom = con->current.border_bottom ? border_width : 0;
424 int border_left = con->current.border_left ? border_width : 0;
425 int border_right = con->current.border_right ? border_width : 0;
426
427 wlr_scene_rect_set_size(con->border.top, width, border_top);
428 wlr_scene_rect_set_size(con->border.bottom, width, border_bottom);
429 wlr_scene_rect_set_size(con->border.left,
430 border_left, height - border_top - border_bottom);
431 wlr_scene_rect_set_size(con->border.right,
432 border_right, height - border_top - border_bottom);
433
434 wlr_scene_node_set_position(&con->border.top->node, 0, 0);
435 wlr_scene_node_set_position(&con->border.bottom->node,
436 0, height - border_bottom);
437 wlr_scene_node_set_position(&con->border.left->node,
438 0, border_top);
439 wlr_scene_node_set_position(&con->border.right->node,
440 width - border_right, border_top);
441
442 // make sure to reparent, it's possible that the client just came out of
443 // fullscreen mode where the parent of the surface is not the container
444 wlr_scene_node_reparent(&con->view->scene_tree->node, con->content_tree);
445 wlr_scene_node_set_position(&con->view->scene_tree->node,
446 border_left, border_top);
447 } else {
448 // make sure to disable the title bar if the parent is not managing it
449 if (title_bar) {
450 wlr_scene_node_set_enabled(&con->title_bar.tree->node, false);
451 }
452
453 arrange_children(con->current.layout, con->current.children,
454 con->current.focused_inactive_child, con->content_tree,
455 width, height, gaps);
456 }
457}
458
459static int container_get_gaps(struct sway_container *con) {
460 struct sway_workspace *ws = con->current.workspace;
461 struct sway_container *temp = con;
462 while (temp) {
463 enum sway_container_layout layout;
464 if (temp->current.parent) {
465 layout = temp->current.parent->current.layout;
466 } else {
467 layout = ws->current.layout;
468 }
469 if (layout == L_TABBED || layout == L_STACKED) {
470 return 0;
471 }
472 temp = temp->pending.parent;
473 }
474 return ws->gaps_inner;
475}
476
477static void arrange_fullscreen(struct wlr_scene_tree *tree,
478 struct sway_container *fs, struct sway_workspace *ws,
479 int width, int height) {
480 struct wlr_scene_node *fs_node;
481 if (fs->view) {
482 fs_node = &fs->view->scene_tree->node;
483
484 // if we only care about the view, disable any decorations
485 wlr_scene_node_set_enabled(&fs->scene_tree->node, false);
486 } else {
487 fs_node = &fs->scene_tree->node;
488 arrange_container(fs, width, height, true, container_get_gaps(fs));
489 }
490
491 wlr_scene_node_reparent(fs_node, tree);
492 wlr_scene_node_lower_to_bottom(fs_node);
493 wlr_scene_node_set_position(fs_node, 0, 0);
494}
495
496static void arrange_workspace_floating(struct sway_workspace *ws) {
497 for (int i = 0; i < ws->current.floating->length; i++) {
498 struct sway_container *floater = ws->current.floating->items[i];
499 struct wlr_scene_tree *layer = root->layers.floating;
500
501 if (floater->current.fullscreen_mode != FULLSCREEN_NONE) {
502 continue;
503 }
504
505 if (root->fullscreen_global) {
506 if (container_is_transient_for(floater, root->fullscreen_global)) {
507 layer = root->layers.fullscreen_global;
508 }
509 } else {
510 for (int i = 0; i < root->outputs->length; i++) {
511 struct sway_output *output = root->outputs->items[i];
512 struct sway_workspace *active = output->current.active_workspace;
513
514 if (active && active->fullscreen &&
515 container_is_transient_for(floater, active->fullscreen)) {
516 layer = root->layers.fullscreen;
517 }
518 }
519 }
520
521 wlr_scene_node_reparent(&floater->scene_tree->node, layer);
522 wlr_scene_node_set_position(&floater->scene_tree->node,
523 floater->current.x, floater->current.y);
524 wlr_scene_node_set_enabled(&floater->scene_tree->node, true);
525
526 arrange_container(floater, floater->current.width, floater->current.height,
527 true, ws->gaps_inner);
528 }
529}
530
531static void arrange_workspace_tiling(struct sway_workspace *ws,
532 int width, int height) {
533 arrange_children(ws->current.layout, ws->current.tiling,
534 ws->current.focused_inactive_child, ws->layers.tiling,
535 width, height, ws->gaps_inner);
536}
537
538static void disable_workspace(struct sway_workspace *ws) {
539 // if any containers were just moved to a disabled workspace it will
540 // have the parent of the old workspace. Move the workspace so that it won't
541 // be shown.
542 for (int i = 0; i < ws->current.tiling->length; i++) {
543 struct sway_container *child = ws->current.tiling->items[i];
544
545 wlr_scene_node_reparent(&child->scene_tree->node, ws->layers.tiling);
546 disable_container(child);
547 }
548
549 for (int i = 0; i < ws->current.floating->length; i++) {
550 struct sway_container *floater = ws->current.floating->items[i];
551 wlr_scene_node_reparent(&floater->scene_tree->node, root->layers.floating);
552 disable_container(floater);
553 wlr_scene_node_set_enabled(&floater->scene_tree->node, false);
554 }
555}
556
557static void arrange_output(struct sway_output *output, int width, int height) {
558 for (int i = 0; i < output->current.workspaces->length; i++) {
559 struct sway_workspace *child = output->current.workspaces->items[i];
560
561 bool activated = output->current.active_workspace == child;
562
563 wlr_scene_node_reparent(&child->layers.tiling->node, output->layers.tiling);
564 wlr_scene_node_reparent(&child->layers.fullscreen->node, output->layers.fullscreen);
565
566 for (int i = 0; i < child->current.floating->length; i++) {
567 struct sway_container *floater = child->current.floating->items[i];
568 wlr_scene_node_reparent(&floater->scene_tree->node, root->layers.floating);
569 wlr_scene_node_set_enabled(&floater->scene_tree->node, activated);
570 }
571
572 if (activated) {
573 struct sway_container *fs = child->current.fullscreen;
574 wlr_scene_node_set_enabled(&child->layers.tiling->node, !fs);
575 wlr_scene_node_set_enabled(&child->layers.fullscreen->node, fs);
576
577 arrange_workspace_floating(child);
578
579 wlr_scene_node_set_enabled(&output->layers.shell_background->node, !fs);
580 wlr_scene_node_set_enabled(&output->layers.shell_bottom->node, !fs);
581 wlr_scene_node_set_enabled(&output->layers.fullscreen->node, fs);
582
583 if (fs) {
584 wlr_scene_rect_set_size(output->fullscreen_background, width, height);
585
586 arrange_fullscreen(child->layers.fullscreen, fs, child,
587 width, height);
588 } else {
589 struct wlr_box *area = &output->usable_area;
590 struct side_gaps *gaps = &child->current_gaps;
591
592 wlr_scene_node_set_position(&child->layers.tiling->node,
593 gaps->left + area->x, gaps->top + area->y);
594
595 arrange_workspace_tiling(child,
596 area->width - gaps->left - gaps->right,
597 area->height - gaps->top - gaps->bottom);
598 }
599 } else {
600 wlr_scene_node_set_enabled(&child->layers.tiling->node, false);
601 wlr_scene_node_set_enabled(&child->layers.fullscreen->node, false);
602
603 disable_workspace(child);
262 } 604 }
263 } 605 }
606}
264 607
265 // If the view hasn't responded to the configure, center it within 608void arrange_popups(struct wlr_scene_tree *popups) {
266 // the container. This is important for fullscreen views which 609 struct wlr_scene_node *node;
267 // refuse to resize to the size of the output. 610 wl_list_for_each(node, &popups->children, link) {
268 if (view && view->surface) { 611 struct sway_popup_desc *popup = scene_descriptor_try_get(node,
269 view_center_surface(view); 612 SWAY_SCENE_DESC_POPUP);
613
614 int lx, ly;
615 wlr_scene_node_coords(popup->relative, &lx, &ly);
616 wlr_scene_node_set_position(node, lx, ly);
270 } 617 }
618}
619
620static void arrange_root(struct sway_root *root) {
621 struct sway_container *fs = root->fullscreen_global;
622
623 wlr_scene_node_set_enabled(&root->layers.shell_background->node, !fs);
624 wlr_scene_node_set_enabled(&root->layers.shell_bottom->node, !fs);
625 wlr_scene_node_set_enabled(&root->layers.tiling->node, !fs);
626 wlr_scene_node_set_enabled(&root->layers.floating->node, !fs);
627 wlr_scene_node_set_enabled(&root->layers.shell_top->node, !fs);
628 wlr_scene_node_set_enabled(&root->layers.fullscreen->node, !fs);
271 629
272 // Damage the new location 630 // hide all contents in the scratchpad
273 desktop_damage_whole_container(container); 631 for (int i = 0; i < root->scratchpad->length; i++) {
274 if (view && view->surface) { 632 struct sway_container *con = root->scratchpad->items[i];
275 struct wlr_surface *surface = view->surface; 633
276 struct wlr_box box = { 634 wlr_scene_node_set_enabled(&con->scene_tree->node, false);
277 .x = container->current.content_x - view->geometry.x,
278 .y = container->current.content_y - view->geometry.y,
279 .width = surface->current.width,
280 .height = surface->current.height,
281 };
282 desktop_damage_box(&box);
283 } 635 }
284 636
285 if (!container->node.destroying) { 637 if (fs) {
286 container_discover_outputs(container); 638 for (int i = 0; i < root->outputs->length; i++) {
639 struct sway_output *output = root->outputs->items[i];
640 struct sway_workspace *ws = output->current.active_workspace;
641
642 if (ws) {
643 arrange_workspace_floating(ws);
644 }
645 }
646
647 arrange_fullscreen(root->layers.fullscreen_global, fs, NULL,
648 root->width, root->height);
649 } else {
650 for (int i = 0; i < root->outputs->length; i++) {
651 struct sway_output *output = root->outputs->items[i];
652
653 wlr_scene_output_set_position(output->scene_output, output->lx, output->ly);
654
655 wlr_scene_node_reparent(&output->layers.shell_background->node, root->layers.shell_background);
656 wlr_scene_node_reparent(&output->layers.shell_bottom->node, root->layers.shell_bottom);
657 wlr_scene_node_reparent(&output->layers.tiling->node, root->layers.tiling);
658 wlr_scene_node_reparent(&output->layers.shell_top->node, root->layers.shell_top);
659 wlr_scene_node_reparent(&output->layers.shell_overlay->node, root->layers.shell_overlay);
660 wlr_scene_node_reparent(&output->layers.fullscreen->node, root->layers.fullscreen);
661 wlr_scene_node_reparent(&output->layers.session_lock->node, root->layers.session_lock);
662
663 wlr_scene_node_set_position(&output->layers.shell_background->node, output->lx, output->ly);
664 wlr_scene_node_set_position(&output->layers.shell_bottom->node, output->lx, output->ly);
665 wlr_scene_node_set_position(&output->layers.tiling->node, output->lx, output->ly);
666 wlr_scene_node_set_position(&output->layers.fullscreen->node, output->lx, output->ly);
667 wlr_scene_node_set_position(&output->layers.shell_top->node, output->lx, output->ly);
668 wlr_scene_node_set_position(&output->layers.shell_overlay->node, output->lx, output->ly);
669 wlr_scene_node_set_position(&output->layers.session_lock->node, output->lx, output->ly);
670
671 arrange_output(output, output->width, output->height);
672 }
287 } 673 }
674
675 arrange_popups(root->layers.popup);
288} 676}
289 677
290/** 678/**
@@ -326,8 +714,6 @@ static void transaction_apply(struct sway_transaction *transaction) {
326 714
327 node->instruction = NULL; 715 node->instruction = NULL;
328 } 716 }
329
330 cursor_rebase_all();
331} 717}
332 718
333static void transaction_commit_pending(void); 719static void transaction_commit_pending(void);
@@ -340,11 +726,13 @@ static void transaction_progress(void) {
340 return; 726 return;
341 } 727 }
342 transaction_apply(server.queued_transaction); 728 transaction_apply(server.queued_transaction);
729 arrange_root(root);
730 cursor_rebase_all();
343 transaction_destroy(server.queued_transaction); 731 transaction_destroy(server.queued_transaction);
344 server.queued_transaction = NULL; 732 server.queued_transaction = NULL;
345 733
346 if (!server.pending_transaction) { 734 if (!server.pending_transaction) {
347 sway_idle_inhibit_v1_check_active(server.idle_inhibit_manager_v1); 735 sway_idle_inhibit_v1_check_active();
348 return; 736 return;
349 } 737 }
350 738
@@ -373,7 +761,7 @@ static bool should_configure(struct sway_node *node,
373 } 761 }
374 struct sway_container_state *cstate = &node->sway_container->current; 762 struct sway_container_state *cstate = &node->sway_container->current;
375 struct sway_container_state *istate = &instruction->container_state; 763 struct sway_container_state *istate = &instruction->container_state;
376#if HAVE_XWAYLAND 764#if WLR_HAS_XWAYLAND
377 // Xwayland views are position-aware and need to be reconfigured 765 // Xwayland views are position-aware and need to be reconfigured
378 // when their position changes. 766 // when their position changes.
379 if (node->sway_container->view->type == SWAY_VIEW_XWAYLAND) { 767 if (node->sway_container->view->type == SWAY_VIEW_XWAYLAND) {
@@ -402,7 +790,7 @@ static void transaction_commit(struct sway_transaction *transaction) {
402 struct sway_transaction_instruction *instruction = 790 struct sway_transaction_instruction *instruction =
403 transaction->instructions->items[i]; 791 transaction->instructions->items[i];
404 struct sway_node *node = instruction->node; 792 struct sway_node *node = instruction->node;
405 bool hidden = node_is_view(node) && 793 bool hidden = node_is_view(node) && !node->destroying &&
406 !view_is_visible(node->sway_container->view); 794 !view_is_visible(node->sway_container->view);
407 if (should_configure(node, instruction)) { 795 if (should_configure(node, instruction)) {
408 instruction->serial = view_configure(node->sway_container->view, 796 instruction->serial = view_configure(node->sway_container->view,
@@ -415,21 +803,11 @@ static void transaction_commit(struct sway_transaction *transaction) {
415 ++transaction->num_waiting; 803 ++transaction->num_waiting;
416 } 804 }
417 805
418 // From here on we are rendering a saved buffer of the view, which 806 view_send_frame_done(node->sway_container->view);
419 // means we can send a frame done event to make the client redraw it
420 // as soon as possible. Additionally, this is required if a view is
421 // mapping and its default geometry doesn't intersect an output.
422 struct timespec now;
423 clock_gettime(CLOCK_MONOTONIC, &now);
424 wlr_surface_send_frame_done(
425 node->sway_container->view->surface, &now);
426 } 807 }
427 if (!hidden && node_is_view(node) && 808 if (!hidden && node_is_view(node) &&
428 wl_list_empty(&node->sway_container->view->saved_buffers)) { 809 !node->sway_container->view->saved_surface_tree) {
429 view_save_buffer(node->sway_container->view); 810 view_save_buffer(node->sway_container->view);
430 memcpy(&node->sway_container->view->saved_geometry,
431 &node->sway_container->view->geometry,
432 sizeof(struct wlr_box));
433 } 811 }
434 node->instruction = instruction; 812 node->instruction = instruction;
435 } 813 }
@@ -499,16 +877,18 @@ static void set_instruction_ready(
499 transaction_progress(); 877 transaction_progress();
500} 878}
501 879
502void transaction_notify_view_ready_by_serial(struct sway_view *view, 880bool transaction_notify_view_ready_by_serial(struct sway_view *view,
503 uint32_t serial) { 881 uint32_t serial) {
504 struct sway_transaction_instruction *instruction = 882 struct sway_transaction_instruction *instruction =
505 view->container->node.instruction; 883 view->container->node.instruction;
506 if (instruction != NULL && instruction->serial == serial) { 884 if (instruction != NULL && instruction->serial == serial) {
507 set_instruction_ready(instruction); 885 set_instruction_ready(instruction);
886 return true;
508 } 887 }
888 return false;
509} 889}
510 890
511void transaction_notify_view_ready_by_geometry(struct sway_view *view, 891bool transaction_notify_view_ready_by_geometry(struct sway_view *view,
512 double x, double y, int width, int height) { 892 double x, double y, int width, int height) {
513 struct sway_transaction_instruction *instruction = 893 struct sway_transaction_instruction *instruction =
514 view->container->node.instruction; 894 view->container->node.instruction;
@@ -518,7 +898,9 @@ void transaction_notify_view_ready_by_geometry(struct sway_view *view,
518 instruction->container_state.content_width == width && 898 instruction->container_state.content_width == width &&
519 instruction->container_state.content_height == height) { 899 instruction->container_state.content_height == height) {
520 set_instruction_ready(instruction); 900 set_instruction_ready(instruction);
901 return true;
521 } 902 }
903 return false;
522} 904}
523 905
524static void _transaction_commit_dirty(bool server_request) { 906static void _transaction_commit_dirty(bool server_request) {
diff --git a/sway/desktop/xdg_shell.c b/sway/desktop/xdg_shell.c
index d34654fd..7c417891 100644
--- a/sway/desktop/xdg_shell.c
+++ b/sway/desktop/xdg_shell.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 199309L
2#include <float.h> 1#include <float.h>
3#include <stdbool.h> 2#include <stdbool.h>
4#include <stdlib.h> 3#include <stdlib.h>
@@ -7,7 +6,7 @@
7#include <wlr/util/edges.h> 6#include <wlr/util/edges.h>
8#include "log.h" 7#include "log.h"
9#include "sway/decoration.h" 8#include "sway/decoration.h"
10#include "sway/desktop.h" 9#include "sway/scene_descriptor.h"
11#include "sway/desktop/transaction.h" 10#include "sway/desktop/transaction.h"
12#include "sway/input/cursor.h" 11#include "sway/input/cursor.h"
13#include "sway/input/input-manager.h" 12#include "sway/input/input-manager.h"
@@ -19,64 +18,45 @@
19#include "sway/tree/workspace.h" 18#include "sway/tree/workspace.h"
20#include "sway/xdg_decoration.h" 19#include "sway/xdg_decoration.h"
21 20
22static const struct sway_view_child_impl popup_impl;
23
24static void popup_get_root_coords(struct sway_view_child *child,
25 int *root_sx, int *root_sy) {
26 struct sway_xdg_popup *popup = (struct sway_xdg_popup *)child;
27 struct wlr_xdg_surface *surface = popup->wlr_xdg_surface;
28
29 int x_offset = -child->view->geometry.x - surface->geometry.x;
30 int y_offset = -child->view->geometry.y - surface->geometry.y;
31
32 wlr_xdg_popup_get_toplevel_coords(surface->popup,
33 x_offset + surface->popup->geometry.x,
34 y_offset + surface->popup->geometry.y,
35 root_sx, root_sy);
36}
37
38static void popup_destroy(struct sway_view_child *child) {
39 if (!sway_assert(child->impl == &popup_impl,
40 "Expected an xdg_shell popup")) {
41 return;
42 }
43 struct sway_xdg_popup *popup = (struct sway_xdg_popup *)child;
44 wl_list_remove(&popup->new_popup.link);
45 wl_list_remove(&popup->destroy.link);
46 free(popup);
47}
48
49static const struct sway_view_child_impl popup_impl = {
50 .get_root_coords = popup_get_root_coords,
51 .destroy = popup_destroy,
52};
53
54static struct sway_xdg_popup *popup_create( 21static struct sway_xdg_popup *popup_create(
55 struct wlr_xdg_popup *wlr_popup, struct sway_view *view); 22 struct wlr_xdg_popup *wlr_popup, struct sway_view *view,
23 struct wlr_scene_tree *parent);
56 24
57static void popup_handle_new_popup(struct wl_listener *listener, void *data) { 25static void popup_handle_new_popup(struct wl_listener *listener, void *data) {
58 struct sway_xdg_popup *popup = 26 struct sway_xdg_popup *popup =
59 wl_container_of(listener, popup, new_popup); 27 wl_container_of(listener, popup, new_popup);
60 struct wlr_xdg_popup *wlr_popup = data; 28 struct wlr_xdg_popup *wlr_popup = data;
61 popup_create(wlr_popup, popup->child.view); 29 popup_create(wlr_popup, popup->view, popup->xdg_surface_tree);
62} 30}
63 31
64static void popup_handle_destroy(struct wl_listener *listener, void *data) { 32static void popup_handle_destroy(struct wl_listener *listener, void *data) {
65 struct sway_xdg_popup *popup = wl_container_of(listener, popup, destroy); 33 struct sway_xdg_popup *popup = wl_container_of(listener, popup, destroy);
66 view_child_destroy(&popup->child); 34
35 wl_list_remove(&popup->new_popup.link);
36 wl_list_remove(&popup->destroy.link);
37 wl_list_remove(&popup->surface_commit.link);
38 wl_list_remove(&popup->reposition.link);
39 wlr_scene_node_destroy(&popup->scene_tree->node);
40 free(popup);
67} 41}
68 42
69static void popup_unconstrain(struct sway_xdg_popup *popup) { 43static void popup_unconstrain(struct sway_xdg_popup *popup) {
70 struct sway_view *view = popup->child.view; 44 struct sway_view *view = popup->view;
71 struct wlr_xdg_popup *wlr_popup = popup->wlr_xdg_surface->popup; 45 struct wlr_xdg_popup *wlr_popup = popup->wlr_xdg_popup;
46
47 struct sway_workspace *workspace = view->container->pending.workspace;
48 if (!workspace) {
49 // is null if in the scratchpad
50 return;
51 }
72 52
73 struct sway_output *output = view->container->pending.workspace->output; 53 struct sway_output *output = workspace->output;
74 54
75 // the output box expressed in the coordinate system of the toplevel parent 55 // the output box expressed in the coordinate system of the toplevel parent
76 // of the popup 56 // of the popup
77 struct wlr_box output_toplevel_sx_box = { 57 struct wlr_box output_toplevel_sx_box = {
78 .x = output->lx - view->container->pending.content_x, 58 .x = output->lx - view->container->pending.content_x + view->geometry.x,
79 .y = output->ly - view->container->pending.content_y, 59 .y = output->ly - view->container->pending.content_y + view->geometry.y,
80 .width = output->width, 60 .width = output->width,
81 .height = output->height, 61 .height = output->height,
82 }; 62 };
@@ -84,32 +64,72 @@ static void popup_unconstrain(struct sway_xdg_popup *popup) {
84 wlr_xdg_popup_unconstrain_from_box(wlr_popup, &output_toplevel_sx_box); 64 wlr_xdg_popup_unconstrain_from_box(wlr_popup, &output_toplevel_sx_box);
85} 65}
86 66
87static struct sway_xdg_popup *popup_create( 67static void popup_handle_surface_commit(struct wl_listener *listener, void *data) {
88 struct wlr_xdg_popup *wlr_popup, struct sway_view *view) { 68 struct sway_xdg_popup *popup = wl_container_of(listener, popup, surface_commit);
69 if (popup->wlr_xdg_popup->base->initial_commit) {
70 popup_unconstrain(popup);
71 }
72}
73
74static void popup_handle_reposition(struct wl_listener *listener, void *data) {
75 struct sway_xdg_popup *popup = wl_container_of(listener, popup, reposition);
76 popup_unconstrain(popup);
77}
78
79static struct sway_xdg_popup *popup_create(struct wlr_xdg_popup *wlr_popup,
80 struct sway_view *view, struct wlr_scene_tree *parent) {
89 struct wlr_xdg_surface *xdg_surface = wlr_popup->base; 81 struct wlr_xdg_surface *xdg_surface = wlr_popup->base;
90 82
91 struct sway_xdg_popup *popup = 83 struct sway_xdg_popup *popup = calloc(1, sizeof(struct sway_xdg_popup));
92 calloc(1, sizeof(struct sway_xdg_popup)); 84 if (!popup) {
93 if (popup == NULL) { 85 return NULL;
86 }
87
88 popup->wlr_xdg_popup = wlr_popup;
89 popup->view = view;
90
91 popup->scene_tree = wlr_scene_tree_create(parent);
92 if (!popup->scene_tree) {
93 free(popup);
94 return NULL;
95 }
96
97 popup->xdg_surface_tree = wlr_scene_xdg_surface_create(
98 popup->scene_tree, xdg_surface);
99 if (!popup->xdg_surface_tree) {
100 wlr_scene_node_destroy(&popup->scene_tree->node);
101 free(popup);
94 return NULL; 102 return NULL;
95 } 103 }
96 view_child_init(&popup->child, &popup_impl, view, xdg_surface->surface);
97 popup->wlr_xdg_surface = xdg_surface;
98 104
105 popup->desc.relative = &view->content_tree->node;
106 popup->desc.view = view;
107
108 if (!scene_descriptor_assign(&popup->scene_tree->node,
109 SWAY_SCENE_DESC_POPUP, &popup->desc)) {
110 sway_log(SWAY_ERROR, "Failed to allocate a popup scene descriptor");
111 wlr_scene_node_destroy(&popup->scene_tree->node);
112 free(popup);
113 return NULL;
114 }
115
116 popup->wlr_xdg_popup = xdg_surface->popup;
117 struct sway_xdg_shell_view *shell_view =
118 wl_container_of(view, shell_view, view);
119 xdg_surface->data = shell_view;
120
121 wl_signal_add(&xdg_surface->surface->events.commit, &popup->surface_commit);
122 popup->surface_commit.notify = popup_handle_surface_commit;
99 wl_signal_add(&xdg_surface->events.new_popup, &popup->new_popup); 123 wl_signal_add(&xdg_surface->events.new_popup, &popup->new_popup);
100 popup->new_popup.notify = popup_handle_new_popup; 124 popup->new_popup.notify = popup_handle_new_popup;
101 wl_signal_add(&xdg_surface->events.destroy, &popup->destroy); 125 wl_signal_add(&wlr_popup->events.reposition, &popup->reposition);
126 popup->reposition.notify = popup_handle_reposition;
127 wl_signal_add(&wlr_popup->events.destroy, &popup->destroy);
102 popup->destroy.notify = popup_handle_destroy; 128 popup->destroy.notify = popup_handle_destroy;
103 129
104 wl_signal_add(&xdg_surface->events.map, &popup->child.surface_map);
105 wl_signal_add(&xdg_surface->events.unmap, &popup->child.surface_unmap);
106
107 popup_unconstrain(popup);
108
109 return popup; 130 return popup;
110} 131}
111 132
112
113static struct sway_xdg_shell_view *xdg_shell_view_from_view( 133static struct sway_xdg_shell_view *xdg_shell_view_from_view(
114 struct sway_view *view) { 134 struct sway_view *view) {
115 if (!sway_assert(view->type == SWAY_VIEW_XDG_SHELL, 135 if (!sway_assert(view->type == SWAY_VIEW_XDG_SHELL,
@@ -122,7 +142,7 @@ static struct sway_xdg_shell_view *xdg_shell_view_from_view(
122static void get_constraints(struct sway_view *view, double *min_width, 142static void get_constraints(struct sway_view *view, double *min_width,
123 double *max_width, double *min_height, double *max_height) { 143 double *max_width, double *min_height, double *max_height) {
124 struct wlr_xdg_toplevel_state *state = 144 struct wlr_xdg_toplevel_state *state =
125 &view->wlr_xdg_surface->toplevel->current; 145 &view->wlr_xdg_toplevel->current;
126 *min_width = state->min_width > 0 ? state->min_width : DBL_MIN; 146 *min_width = state->min_width > 0 ? state->min_width : DBL_MIN;
127 *max_width = state->max_width > 0 ? state->max_width : DBL_MAX; 147 *max_width = state->max_width > 0 ? state->max_width : DBL_MAX;
128 *min_height = state->min_height > 0 ? state->min_height : DBL_MIN; 148 *min_height = state->min_height > 0 ? state->min_height : DBL_MIN;
@@ -136,9 +156,9 @@ static const char *get_string_prop(struct sway_view *view,
136 } 156 }
137 switch (prop) { 157 switch (prop) {
138 case VIEW_PROP_TITLE: 158 case VIEW_PROP_TITLE:
139 return view->wlr_xdg_surface->toplevel->title; 159 return view->wlr_xdg_toplevel->title;
140 case VIEW_PROP_APP_ID: 160 case VIEW_PROP_APP_ID:
141 return view->wlr_xdg_surface->toplevel->app_id; 161 return view->wlr_xdg_toplevel->app_id;
142 default: 162 default:
143 return NULL; 163 return NULL;
144 } 164 }
@@ -151,50 +171,52 @@ static uint32_t configure(struct sway_view *view, double lx, double ly,
151 if (xdg_shell_view == NULL) { 171 if (xdg_shell_view == NULL) {
152 return 0; 172 return 0;
153 } 173 }
154 return wlr_xdg_toplevel_set_size(view->wlr_xdg_surface, width, height); 174 return wlr_xdg_toplevel_set_size(view->wlr_xdg_toplevel,
175 width, height);
155} 176}
156 177
157static void set_activated(struct sway_view *view, bool activated) { 178static void set_activated(struct sway_view *view, bool activated) {
158 if (xdg_shell_view_from_view(view) == NULL) { 179 if (xdg_shell_view_from_view(view) == NULL) {
159 return; 180 return;
160 } 181 }
161 struct wlr_xdg_surface *surface = view->wlr_xdg_surface; 182 wlr_xdg_toplevel_set_activated(view->wlr_xdg_toplevel, activated);
162 if (surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL) {
163 wlr_xdg_toplevel_set_activated(surface, activated);
164 }
165} 183}
166 184
167static void set_tiled(struct sway_view *view, bool tiled) { 185static void set_tiled(struct sway_view *view, bool tiled) {
168 if (xdg_shell_view_from_view(view) == NULL) { 186 if (xdg_shell_view_from_view(view) == NULL) {
169 return; 187 return;
170 } 188 }
171 struct wlr_xdg_surface *surface = view->wlr_xdg_surface; 189 if (wl_resource_get_version(view->wlr_xdg_toplevel->resource) >=
172 enum wlr_edges edges = WLR_EDGE_NONE; 190 XDG_TOPLEVEL_STATE_TILED_LEFT_SINCE_VERSION) {
173 if (tiled) { 191 enum wlr_edges edges = WLR_EDGE_NONE;
174 edges = WLR_EDGE_LEFT | WLR_EDGE_RIGHT | WLR_EDGE_TOP | 192 if (tiled) {
175 WLR_EDGE_BOTTOM; 193 edges = WLR_EDGE_LEFT | WLR_EDGE_RIGHT | WLR_EDGE_TOP |
194 WLR_EDGE_BOTTOM;
195 }
196 wlr_xdg_toplevel_set_tiled(view->wlr_xdg_toplevel, edges);
197 } else {
198 // The version is too low for the tiled state; configure as maximized instead
199 // to stop the client from drawing decorations outside of the toplevel geometry.
200 wlr_xdg_toplevel_set_maximized(view->wlr_xdg_toplevel, tiled);
176 } 201 }
177 wlr_xdg_toplevel_set_tiled(surface, edges);
178} 202}
179 203
180static void set_fullscreen(struct sway_view *view, bool fullscreen) { 204static void set_fullscreen(struct sway_view *view, bool fullscreen) {
181 if (xdg_shell_view_from_view(view) == NULL) { 205 if (xdg_shell_view_from_view(view) == NULL) {
182 return; 206 return;
183 } 207 }
184 struct wlr_xdg_surface *surface = view->wlr_xdg_surface; 208 wlr_xdg_toplevel_set_fullscreen(view->wlr_xdg_toplevel, fullscreen);
185 wlr_xdg_toplevel_set_fullscreen(surface, fullscreen);
186} 209}
187 210
188static void set_resizing(struct sway_view *view, bool resizing) { 211static void set_resizing(struct sway_view *view, bool resizing) {
189 if (xdg_shell_view_from_view(view) == NULL) { 212 if (xdg_shell_view_from_view(view) == NULL) {
190 return; 213 return;
191 } 214 }
192 struct wlr_xdg_surface *surface = view->wlr_xdg_surface; 215 wlr_xdg_toplevel_set_resizing(view->wlr_xdg_toplevel, resizing);
193 wlr_xdg_toplevel_set_resizing(surface, resizing);
194} 216}
195 217
196static bool wants_floating(struct sway_view *view) { 218static bool wants_floating(struct sway_view *view) {
197 struct wlr_xdg_toplevel *toplevel = view->wlr_xdg_surface->toplevel; 219 struct wlr_xdg_toplevel *toplevel = view->wlr_xdg_toplevel;
198 struct wlr_xdg_toplevel_state *state = &toplevel->current; 220 struct wlr_xdg_toplevel_state *state = &toplevel->current;
199 return (state->min_width != 0 && state->min_height != 0 221 return (state->min_width != 0 && state->min_height != 0
200 && (state->min_width == state->max_width 222 && (state->min_width == state->max_width
@@ -202,35 +224,17 @@ static bool wants_floating(struct sway_view *view) {
202 || toplevel->parent; 224 || toplevel->parent;
203} 225}
204 226
205static void for_each_surface(struct sway_view *view,
206 wlr_surface_iterator_func_t iterator, void *user_data) {
207 if (xdg_shell_view_from_view(view) == NULL) {
208 return;
209 }
210 wlr_xdg_surface_for_each_surface(view->wlr_xdg_surface, iterator,
211 user_data);
212}
213
214static void for_each_popup_surface(struct sway_view *view,
215 wlr_surface_iterator_func_t iterator, void *user_data) {
216 if (xdg_shell_view_from_view(view) == NULL) {
217 return;
218 }
219 wlr_xdg_surface_for_each_popup_surface(view->wlr_xdg_surface, iterator,
220 user_data);
221}
222
223static bool is_transient_for(struct sway_view *child, 227static bool is_transient_for(struct sway_view *child,
224 struct sway_view *ancestor) { 228 struct sway_view *ancestor) {
225 if (xdg_shell_view_from_view(child) == NULL) { 229 if (xdg_shell_view_from_view(child) == NULL) {
226 return false; 230 return false;
227 } 231 }
228 struct wlr_xdg_surface *surface = child->wlr_xdg_surface; 232 struct wlr_xdg_toplevel *toplevel = child->wlr_xdg_toplevel;
229 while (surface && surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL) { 233 while (toplevel) {
230 if (surface->toplevel->parent == ancestor->wlr_xdg_surface) { 234 if (toplevel->parent == ancestor->wlr_xdg_toplevel) {
231 return true; 235 return true;
232 } 236 }
233 surface = surface->toplevel->parent; 237 toplevel = toplevel->parent;
234 } 238 }
235 return false; 239 return false;
236} 240}
@@ -239,17 +243,13 @@ static void _close(struct sway_view *view) {
239 if (xdg_shell_view_from_view(view) == NULL) { 243 if (xdg_shell_view_from_view(view) == NULL) {
240 return; 244 return;
241 } 245 }
242 struct wlr_xdg_surface *surface = view->wlr_xdg_surface; 246 wlr_xdg_toplevel_send_close(view->wlr_xdg_toplevel);
243 if (surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL
244 && surface->toplevel) {
245 wlr_xdg_toplevel_send_close(surface);
246 }
247} 247}
248 248
249static void close_popups(struct sway_view *view) { 249static void close_popups(struct sway_view *view) {
250 struct wlr_xdg_popup *popup, *tmp; 250 struct wlr_xdg_popup *popup, *tmp;
251 wl_list_for_each_safe(popup, tmp, &view->wlr_xdg_surface->popups, link) { 251 wl_list_for_each_safe(popup, tmp, &view->wlr_xdg_toplevel->base->popups, link) {
252 wlr_xdg_popup_destroy(popup->base); 252 wlr_xdg_popup_destroy(popup);
253 } 253 }
254} 254}
255 255
@@ -271,8 +271,6 @@ static const struct sway_view_impl view_impl = {
271 .set_fullscreen = set_fullscreen, 271 .set_fullscreen = set_fullscreen,
272 .set_resizing = set_resizing, 272 .set_resizing = set_resizing,
273 .wants_floating = wants_floating, 273 .wants_floating = wants_floating,
274 .for_each_surface = for_each_surface,
275 .for_each_popup_surface = for_each_popup_surface,
276 .is_transient_for = is_transient_for, 274 .is_transient_for = is_transient_for,
277 .close = _close, 275 .close = _close,
278 .close_popups = close_popups, 276 .close_popups = close_popups,
@@ -283,7 +281,21 @@ static void handle_commit(struct wl_listener *listener, void *data) {
283 struct sway_xdg_shell_view *xdg_shell_view = 281 struct sway_xdg_shell_view *xdg_shell_view =
284 wl_container_of(listener, xdg_shell_view, commit); 282 wl_container_of(listener, xdg_shell_view, commit);
285 struct sway_view *view = &xdg_shell_view->view; 283 struct sway_view *view = &xdg_shell_view->view;
286 struct wlr_xdg_surface *xdg_surface = view->wlr_xdg_surface; 284 struct wlr_xdg_surface *xdg_surface = view->wlr_xdg_toplevel->base;
285
286 if (xdg_surface->initial_commit) {
287 if (view->xdg_decoration != NULL) {
288 set_xdg_decoration_mode(view->xdg_decoration);
289 }
290 // XXX: https://github.com/swaywm/sway/issues/2176
291 wlr_xdg_surface_schedule_configure(xdg_surface);
292 // TODO: wlr_xdg_toplevel_set_bounds()
293 return;
294 }
295
296 if (!xdg_surface->surface->mapped) {
297 return;
298 }
287 299
288 struct wlr_box new_geo; 300 struct wlr_box new_geo;
289 wlr_xdg_surface_get_geometry(xdg_surface, &new_geo); 301 wlr_xdg_surface_get_geometry(xdg_surface, &new_geo);
@@ -296,23 +308,32 @@ static void handle_commit(struct wl_listener *listener, void *data) {
296 // The client changed its surface size in this commit. For floating 308 // The client changed its surface size in this commit. For floating
297 // containers, we resize the container to match. For tiling containers, 309 // containers, we resize the container to match. For tiling containers,
298 // we only recenter the surface. 310 // we only recenter the surface.
299 desktop_damage_view(view);
300 memcpy(&view->geometry, &new_geo, sizeof(struct wlr_box)); 311 memcpy(&view->geometry, &new_geo, sizeof(struct wlr_box));
301 if (container_is_floating(view->container)) { 312 if (container_is_floating(view->container)) {
302 view_update_size(view); 313 view_update_size(view);
314 // Only set the toplevel size the current container actually has a size.
315 if (view->container->current.width) {
316 wlr_xdg_toplevel_set_size(view->wlr_xdg_toplevel, view->geometry.width,
317 view->geometry.height);
318 }
303 transaction_commit_dirty_client(); 319 transaction_commit_dirty_client();
304 } else {
305 view_center_surface(view);
306 } 320 }
307 desktop_damage_view(view); 321
322 view_center_and_clip_surface(view);
308 } 323 }
309 324
310 if (view->container->node.instruction) { 325 if (view->container->node.instruction) {
311 transaction_notify_view_ready_by_serial(view, 326 bool successful = transaction_notify_view_ready_by_serial(view,
312 xdg_surface->configure_serial); 327 xdg_surface->current.configure_serial);
328
329 // If we saved the view and this commit isn't what we're looking for
330 // that means the user will never actually see the buffers submitted to
331 // us here. Just send frame done events to these surfaces so they can
332 // commit another time for us.
333 if (view->saved_surface_tree && !successful) {
334 view_send_frame_done(view);
335 }
313 } 336 }
314
315 view_damage_from(view);
316} 337}
317 338
318static void handle_set_title(struct wl_listener *listener, void *data) { 339static void handle_set_title(struct wl_listener *listener, void *data) {
@@ -327,6 +348,7 @@ static void handle_set_app_id(struct wl_listener *listener, void *data) {
327 struct sway_xdg_shell_view *xdg_shell_view = 348 struct sway_xdg_shell_view *xdg_shell_view =
328 wl_container_of(listener, xdg_shell_view, set_app_id); 349 wl_container_of(listener, xdg_shell_view, set_app_id);
329 struct sway_view *view = &xdg_shell_view->view; 350 struct sway_view *view = &xdg_shell_view->view;
351 view_update_app_id(view);
330 view_execute_criteria(view); 352 view_execute_criteria(view);
331} 353}
332 354
@@ -334,29 +356,39 @@ static void handle_new_popup(struct wl_listener *listener, void *data) {
334 struct sway_xdg_shell_view *xdg_shell_view = 356 struct sway_xdg_shell_view *xdg_shell_view =
335 wl_container_of(listener, xdg_shell_view, new_popup); 357 wl_container_of(listener, xdg_shell_view, new_popup);
336 struct wlr_xdg_popup *wlr_popup = data; 358 struct wlr_xdg_popup *wlr_popup = data;
337 popup_create(wlr_popup, &xdg_shell_view->view); 359
360 struct sway_xdg_popup *popup = popup_create(wlr_popup,
361 &xdg_shell_view->view, root->layers.popup);
362 if (!popup) {
363 return;
364 }
365
366 int lx, ly;
367 wlr_scene_node_coords(&popup->view->content_tree->node, &lx, &ly);
368 wlr_scene_node_set_position(&popup->scene_tree->node, lx, ly);
369}
370
371static void handle_request_maximize(struct wl_listener *listener, void *data) {
372 struct sway_xdg_shell_view *xdg_shell_view =
373 wl_container_of(listener, xdg_shell_view, request_maximize);
374 struct wlr_xdg_toplevel *toplevel = xdg_shell_view->view.wlr_xdg_toplevel;
375 wlr_xdg_surface_schedule_configure(toplevel->base);
338} 376}
339 377
340static void handle_request_fullscreen(struct wl_listener *listener, void *data) { 378static void handle_request_fullscreen(struct wl_listener *listener, void *data) {
341 struct sway_xdg_shell_view *xdg_shell_view = 379 struct sway_xdg_shell_view *xdg_shell_view =
342 wl_container_of(listener, xdg_shell_view, request_fullscreen); 380 wl_container_of(listener, xdg_shell_view, request_fullscreen);
343 struct wlr_xdg_toplevel_set_fullscreen_event *e = data; 381 struct wlr_xdg_toplevel *toplevel = xdg_shell_view->view.wlr_xdg_toplevel;
344 struct wlr_xdg_surface *xdg_surface =
345 xdg_shell_view->view.wlr_xdg_surface;
346 struct sway_view *view = &xdg_shell_view->view; 382 struct sway_view *view = &xdg_shell_view->view;
347 383
348 if (!sway_assert(xdg_surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL, 384 if (!toplevel->base->surface->mapped) {
349 "xdg_shell requested fullscreen of surface with role %i",
350 xdg_surface->role)) {
351 return;
352 }
353 if (!xdg_surface->mapped) {
354 return; 385 return;
355 } 386 }
356 387
357 struct sway_container *container = view->container; 388 struct sway_container *container = view->container;
358 if (e->fullscreen && e->output && e->output->data) { 389 struct wlr_xdg_toplevel_requested *req = &toplevel->requested;
359 struct sway_output *output = e->output->data; 390 if (req->fullscreen && req->fullscreen_output && req->fullscreen_output->data) {
391 struct sway_output *output = req->fullscreen_output->data;
360 struct sway_workspace *ws = output_get_active_workspace(output); 392 struct sway_workspace *ws = output_get_active_workspace(output);
361 if (ws && !container_is_scratchpad_hidden(container) && 393 if (ws && !container_is_scratchpad_hidden(container) &&
362 container->pending.workspace != ws) { 394 container->pending.workspace != ws) {
@@ -368,22 +400,18 @@ static void handle_request_fullscreen(struct wl_listener *listener, void *data)
368 } 400 }
369 } 401 }
370 402
371 container_set_fullscreen(container, e->fullscreen); 403 container_set_fullscreen(container, req->fullscreen);
372 404
373 arrange_root(); 405 arrange_root();
374 transaction_commit_dirty(); 406 transaction_commit_dirty();
375} 407}
376 408
377static void handle_request_maximize(struct wl_listener *listener, void *data) {
378 struct wlr_xdg_surface *surface = data;
379 wlr_xdg_surface_schedule_configure(surface);
380}
381
382static void handle_request_move(struct wl_listener *listener, void *data) { 409static void handle_request_move(struct wl_listener *listener, void *data) {
383 struct sway_xdg_shell_view *xdg_shell_view = 410 struct sway_xdg_shell_view *xdg_shell_view =
384 wl_container_of(listener, xdg_shell_view, request_move); 411 wl_container_of(listener, xdg_shell_view, request_move);
385 struct sway_view *view = &xdg_shell_view->view; 412 struct sway_view *view = &xdg_shell_view->view;
386 if (!container_is_floating(view->container)) { 413 if (!container_is_floating(view->container) ||
414 view->container->pending.fullscreen_mode) {
387 return; 415 return;
388 } 416 }
389 struct wlr_xdg_toplevel_move_event *e = data; 417 struct wlr_xdg_toplevel_move_event *e = data;
@@ -418,10 +446,9 @@ static void handle_unmap(struct wl_listener *listener, void *data) {
418 446
419 view_unmap(view); 447 view_unmap(view);
420 448
421 wl_list_remove(&xdg_shell_view->commit.link);
422 wl_list_remove(&xdg_shell_view->new_popup.link); 449 wl_list_remove(&xdg_shell_view->new_popup.link);
423 wl_list_remove(&xdg_shell_view->request_fullscreen.link);
424 wl_list_remove(&xdg_shell_view->request_maximize.link); 450 wl_list_remove(&xdg_shell_view->request_maximize.link);
451 wl_list_remove(&xdg_shell_view->request_fullscreen.link);
425 wl_list_remove(&xdg_shell_view->request_move.link); 452 wl_list_remove(&xdg_shell_view->request_move.link);
426 wl_list_remove(&xdg_shell_view->request_resize.link); 453 wl_list_remove(&xdg_shell_view->request_resize.link);
427 wl_list_remove(&xdg_shell_view->set_title.link); 454 wl_list_remove(&xdg_shell_view->set_title.link);
@@ -432,62 +459,61 @@ static void handle_map(struct wl_listener *listener, void *data) {
432 struct sway_xdg_shell_view *xdg_shell_view = 459 struct sway_xdg_shell_view *xdg_shell_view =
433 wl_container_of(listener, xdg_shell_view, map); 460 wl_container_of(listener, xdg_shell_view, map);
434 struct sway_view *view = &xdg_shell_view->view; 461 struct sway_view *view = &xdg_shell_view->view;
435 struct wlr_xdg_surface *xdg_surface = view->wlr_xdg_surface; 462 struct wlr_xdg_toplevel *toplevel = view->wlr_xdg_toplevel;
436 463
437 view->natural_width = view->wlr_xdg_surface->geometry.width; 464 view->natural_width = toplevel->base->current.geometry.width;
438 view->natural_height = view->wlr_xdg_surface->geometry.height; 465 view->natural_height = toplevel->base->current.geometry.height;
439 if (!view->natural_width && !view->natural_height) { 466 if (!view->natural_width && !view->natural_height) {
440 view->natural_width = view->wlr_xdg_surface->surface->current.width; 467 view->natural_width = toplevel->base->surface->current.width;
441 view->natural_height = view->wlr_xdg_surface->surface->current.height; 468 view->natural_height = toplevel->base->surface->current.height;
442 } 469 }
443 470
444 bool csd = false; 471 bool csd = false;
445 472
446 if (!view->xdg_decoration) { 473 if (view->xdg_decoration) {
474 enum wlr_xdg_toplevel_decoration_v1_mode mode =
475 view->xdg_decoration->wlr_xdg_decoration->requested_mode;
476 csd = mode == WLR_XDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE;
477 } else {
447 struct sway_server_decoration *deco = 478 struct sway_server_decoration *deco =
448 decoration_from_surface(xdg_surface->surface); 479 decoration_from_surface(toplevel->base->surface);
449 csd = !deco || deco->wlr_server_decoration->mode == 480 csd = !deco || deco->wlr_server_decoration->mode ==
450 WLR_SERVER_DECORATION_MANAGER_MODE_CLIENT; 481 WLR_SERVER_DECORATION_MANAGER_MODE_CLIENT;
451
452 } 482 }
453 483
454 view_map(view, view->wlr_xdg_surface->surface, 484 view_map(view, toplevel->base->surface,
455 xdg_surface->toplevel->client_pending.fullscreen, 485 toplevel->requested.fullscreen,
456 xdg_surface->toplevel->client_pending.fullscreen_output, 486 toplevel->requested.fullscreen_output,
457 csd); 487 csd);
458 488
459 transaction_commit_dirty(); 489 transaction_commit_dirty();
460 490
461 xdg_shell_view->commit.notify = handle_commit;
462 wl_signal_add(&xdg_surface->surface->events.commit,
463 &xdg_shell_view->commit);
464
465 xdg_shell_view->new_popup.notify = handle_new_popup; 491 xdg_shell_view->new_popup.notify = handle_new_popup;
466 wl_signal_add(&xdg_surface->events.new_popup, 492 wl_signal_add(&toplevel->base->events.new_popup,
467 &xdg_shell_view->new_popup); 493 &xdg_shell_view->new_popup);
468 494
469 xdg_shell_view->request_fullscreen.notify = handle_request_fullscreen;
470 wl_signal_add(&xdg_surface->toplevel->events.request_fullscreen,
471 &xdg_shell_view->request_fullscreen);
472
473 xdg_shell_view->request_maximize.notify = handle_request_maximize; 495 xdg_shell_view->request_maximize.notify = handle_request_maximize;
474 wl_signal_add(&xdg_surface->toplevel->events.request_maximize, 496 wl_signal_add(&toplevel->events.request_maximize,
475 &xdg_shell_view->request_maximize); 497 &xdg_shell_view->request_maximize);
476 498
499 xdg_shell_view->request_fullscreen.notify = handle_request_fullscreen;
500 wl_signal_add(&toplevel->events.request_fullscreen,
501 &xdg_shell_view->request_fullscreen);
502
477 xdg_shell_view->request_move.notify = handle_request_move; 503 xdg_shell_view->request_move.notify = handle_request_move;
478 wl_signal_add(&xdg_surface->toplevel->events.request_move, 504 wl_signal_add(&toplevel->events.request_move,
479 &xdg_shell_view->request_move); 505 &xdg_shell_view->request_move);
480 506
481 xdg_shell_view->request_resize.notify = handle_request_resize; 507 xdg_shell_view->request_resize.notify = handle_request_resize;
482 wl_signal_add(&xdg_surface->toplevel->events.request_resize, 508 wl_signal_add(&toplevel->events.request_resize,
483 &xdg_shell_view->request_resize); 509 &xdg_shell_view->request_resize);
484 510
485 xdg_shell_view->set_title.notify = handle_set_title; 511 xdg_shell_view->set_title.notify = handle_set_title;
486 wl_signal_add(&xdg_surface->toplevel->events.set_title, 512 wl_signal_add(&toplevel->events.set_title,
487 &xdg_shell_view->set_title); 513 &xdg_shell_view->set_title);
488 514
489 xdg_shell_view->set_app_id.notify = handle_set_app_id; 515 xdg_shell_view->set_app_id.notify = handle_set_app_id;
490 wl_signal_add(&xdg_surface->toplevel->events.set_app_id, 516 wl_signal_add(&toplevel->events.set_app_id,
491 &xdg_shell_view->set_app_id); 517 &xdg_shell_view->set_app_id);
492} 518}
493 519
@@ -501,7 +527,8 @@ static void handle_destroy(struct wl_listener *listener, void *data) {
501 wl_list_remove(&xdg_shell_view->destroy.link); 527 wl_list_remove(&xdg_shell_view->destroy.link);
502 wl_list_remove(&xdg_shell_view->map.link); 528 wl_list_remove(&xdg_shell_view->map.link);
503 wl_list_remove(&xdg_shell_view->unmap.link); 529 wl_list_remove(&xdg_shell_view->unmap.link);
504 view->wlr_xdg_surface = NULL; 530 wl_list_remove(&xdg_shell_view->commit.link);
531 view->wlr_xdg_toplevel = NULL;
505 if (view->xdg_decoration) { 532 if (view->xdg_decoration) {
506 view->xdg_decoration->view = NULL; 533 view->xdg_decoration->view = NULL;
507 } 534 }
@@ -513,17 +540,12 @@ struct sway_view *view_from_wlr_xdg_surface(
513 return xdg_surface->data; 540 return xdg_surface->data;
514} 541}
515 542
516void handle_xdg_shell_surface(struct wl_listener *listener, void *data) { 543void handle_xdg_shell_toplevel(struct wl_listener *listener, void *data) {
517 struct wlr_xdg_surface *xdg_surface = data; 544 struct wlr_xdg_toplevel *xdg_toplevel = data;
518
519 if (xdg_surface->role == WLR_XDG_SURFACE_ROLE_POPUP) {
520 sway_log(SWAY_DEBUG, "New xdg_shell popup");
521 return;
522 }
523 545
524 sway_log(SWAY_DEBUG, "New xdg_shell toplevel title='%s' app_id='%s'", 546 sway_log(SWAY_DEBUG, "New xdg_shell toplevel title='%s' app_id='%s'",
525 xdg_surface->toplevel->title, xdg_surface->toplevel->app_id); 547 xdg_toplevel->title, xdg_toplevel->app_id);
526 wlr_xdg_surface_ping(xdg_surface); 548 wlr_xdg_surface_ping(xdg_toplevel->base);
527 549
528 struct sway_xdg_shell_view *xdg_shell_view = 550 struct sway_xdg_shell_view *xdg_shell_view =
529 calloc(1, sizeof(struct sway_xdg_shell_view)); 551 calloc(1, sizeof(struct sway_xdg_shell_view));
@@ -531,17 +553,29 @@ void handle_xdg_shell_surface(struct wl_listener *listener, void *data) {
531 return; 553 return;
532 } 554 }
533 555
534 view_init(&xdg_shell_view->view, SWAY_VIEW_XDG_SHELL, &view_impl); 556 if (!view_init(&xdg_shell_view->view, SWAY_VIEW_XDG_SHELL, &view_impl)) {
535 xdg_shell_view->view.wlr_xdg_surface = xdg_surface; 557 free(xdg_shell_view);
558 return;
559 }
560 xdg_shell_view->view.wlr_xdg_toplevel = xdg_toplevel;
536 561
537 xdg_shell_view->map.notify = handle_map; 562 xdg_shell_view->map.notify = handle_map;
538 wl_signal_add(&xdg_surface->events.map, &xdg_shell_view->map); 563 wl_signal_add(&xdg_toplevel->base->surface->events.map, &xdg_shell_view->map);
539 564
540 xdg_shell_view->unmap.notify = handle_unmap; 565 xdg_shell_view->unmap.notify = handle_unmap;
541 wl_signal_add(&xdg_surface->events.unmap, &xdg_shell_view->unmap); 566 wl_signal_add(&xdg_toplevel->base->surface->events.unmap, &xdg_shell_view->unmap);
567
568 xdg_shell_view->commit.notify = handle_commit;
569 wl_signal_add(&xdg_toplevel->base->surface->events.commit,
570 &xdg_shell_view->commit);
542 571
543 xdg_shell_view->destroy.notify = handle_destroy; 572 xdg_shell_view->destroy.notify = handle_destroy;
544 wl_signal_add(&xdg_surface->events.destroy, &xdg_shell_view->destroy); 573 wl_signal_add(&xdg_toplevel->events.destroy, &xdg_shell_view->destroy);
574
575 wlr_scene_xdg_surface_create(xdg_shell_view->view.content_tree, xdg_toplevel->base);
576
577 xdg_toplevel->base->data = xdg_shell_view;
545 578
546 xdg_surface->data = xdg_shell_view; 579 wlr_xdg_toplevel_set_wm_capabilities(xdg_toplevel,
580 XDG_TOPLEVEL_WM_CAPABILITIES_FULLSCREEN);
547} 581}
diff --git a/sway/desktop/xwayland.c b/sway/desktop/xwayland.c
index 66cb3b02..270cf08f 100644
--- a/sway/desktop/xwayland.c
+++ b/sway/desktop/xwayland.c
@@ -1,20 +1,23 @@
1#define _POSIX_C_SOURCE 199309L
2#include <float.h> 1#include <float.h>
3#include <stdbool.h> 2#include <stdbool.h>
4#include <stdlib.h> 3#include <stdlib.h>
5#include <wayland-server-core.h> 4#include <wayland-server-core.h>
6#include <wlr/types/wlr_output_layout.h> 5#include <wlr/types/wlr_output_layout.h>
7#include <wlr/types/wlr_output.h> 6#include <wlr/types/wlr_output.h>
7#include <wlr/types/wlr_xdg_activation_v1.h>
8#include <wlr/types/wlr_scene.h>
8#include <wlr/xwayland.h> 9#include <wlr/xwayland.h>
10#include <xcb/xcb_icccm.h>
9#include "log.h" 11#include "log.h"
10#include "sway/desktop.h"
11#include "sway/desktop/transaction.h" 12#include "sway/desktop/transaction.h"
12#include "sway/input/cursor.h" 13#include "sway/input/cursor.h"
13#include "sway/input/input-manager.h" 14#include "sway/input/input-manager.h"
14#include "sway/input/seat.h" 15#include "sway/input/seat.h"
15#include "sway/output.h" 16#include "sway/output.h"
17#include "sway/scene_descriptor.h"
16#include "sway/tree/arrange.h" 18#include "sway/tree/arrange.h"
17#include "sway/tree/container.h" 19#include "sway/tree/container.h"
20#include "sway/server.h"
18#include "sway/tree/view.h" 21#include "sway/tree/view.h"
19#include "sway/tree/workspace.h" 22#include "sway/tree/workspace.h"
20 23
@@ -42,29 +45,12 @@ static void unmanaged_handle_request_configure(struct wl_listener *listener,
42 ev->width, ev->height); 45 ev->width, ev->height);
43} 46}
44 47
45static void unmanaged_handle_commit(struct wl_listener *listener, void *data) {
46 struct sway_xwayland_unmanaged *surface =
47 wl_container_of(listener, surface, commit);
48 struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface;
49
50 desktop_damage_surface(xsurface->surface, surface->lx, surface->ly,
51 false);
52}
53
54static void unmanaged_handle_set_geometry(struct wl_listener *listener, void *data) { 48static void unmanaged_handle_set_geometry(struct wl_listener *listener, void *data) {
55 struct sway_xwayland_unmanaged *surface = 49 struct sway_xwayland_unmanaged *surface =
56 wl_container_of(listener, surface, set_geometry); 50 wl_container_of(listener, surface, set_geometry);
57 struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface; 51 struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface;
58 52
59 if (xsurface->x != surface->lx || xsurface->y != surface->ly) { 53 wlr_scene_node_set_position(&surface->surface_scene->buffer->node, xsurface->x, xsurface->y);
60 // Surface has moved
61 desktop_damage_surface(xsurface->surface, surface->lx, surface->ly,
62 true);
63 surface->lx = xsurface->x;
64 surface->ly = xsurface->y;
65 desktop_damage_surface(xsurface->surface, surface->lx, surface->ly,
66 true);
67 }
68} 54}
69 55
70static void unmanaged_handle_map(struct wl_listener *listener, void *data) { 56static void unmanaged_handle_map(struct wl_listener *listener, void *data) {
@@ -72,17 +58,18 @@ static void unmanaged_handle_map(struct wl_listener *listener, void *data) {
72 wl_container_of(listener, surface, map); 58 wl_container_of(listener, surface, map);
73 struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface; 59 struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface;
74 60
75 wl_list_insert(root->xwayland_unmanaged.prev, &surface->link); 61 surface->surface_scene = wlr_scene_surface_create(root->layers.unmanaged,
76 62 xsurface->surface);
77 wl_signal_add(&xsurface->events.set_geometry, &surface->set_geometry);
78 surface->set_geometry.notify = unmanaged_handle_set_geometry;
79 63
80 wl_signal_add(&xsurface->surface->events.commit, &surface->commit); 64 if (surface->surface_scene) {
81 surface->commit.notify = unmanaged_handle_commit; 65 scene_descriptor_assign(&surface->surface_scene->buffer->node,
66 SWAY_SCENE_DESC_XWAYLAND_UNMANAGED, surface);
67 wlr_scene_node_set_position(&surface->surface_scene->buffer->node,
68 xsurface->x, xsurface->y);
82 69
83 surface->lx = xsurface->x; 70 wl_signal_add(&xsurface->events.set_geometry, &surface->set_geometry);
84 surface->ly = xsurface->y; 71 surface->set_geometry.notify = unmanaged_handle_set_geometry;
85 desktop_damage_surface(xsurface->surface, surface->lx, surface->ly, true); 72 }
86 73
87 if (wlr_xwayland_or_surface_wants_focus(xsurface)) { 74 if (wlr_xwayland_or_surface_wants_focus(xsurface)) {
88 struct sway_seat *seat = input_manager_current_seat(); 75 struct sway_seat *seat = input_manager_current_seat();
@@ -96,10 +83,13 @@ static void unmanaged_handle_unmap(struct wl_listener *listener, void *data) {
96 struct sway_xwayland_unmanaged *surface = 83 struct sway_xwayland_unmanaged *surface =
97 wl_container_of(listener, surface, unmap); 84 wl_container_of(listener, surface, unmap);
98 struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface; 85 struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface;
99 desktop_damage_surface(xsurface->surface, xsurface->x, xsurface->y, true); 86
100 wl_list_remove(&surface->link); 87 if (surface->surface_scene) {
101 wl_list_remove(&surface->set_geometry.link); 88 wl_list_remove(&surface->set_geometry.link);
102 wl_list_remove(&surface->commit.link); 89
90 wlr_scene_node_destroy(&surface->surface_scene->buffer->node);
91 surface->surface_scene = NULL;
92 }
103 93
104 struct sway_seat *seat = input_manager_current_seat(); 94 struct sway_seat *seat = input_manager_current_seat();
105 if (seat->wlr_seat->keyboard_state.focused_surface == xsurface->surface) { 95 if (seat->wlr_seat->keyboard_state.focused_surface == xsurface->surface) {
@@ -121,18 +111,53 @@ static void unmanaged_handle_unmap(struct wl_listener *listener, void *data) {
121 } 111 }
122} 112}
123 113
114static void unmanaged_handle_request_activate(struct wl_listener *listener, void *data) {
115 struct sway_xwayland_unmanaged *surface =
116 wl_container_of(listener, surface, request_activate);
117 struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface;
118 if (xsurface->surface == NULL || !xsurface->surface->mapped) {
119 return;
120 }
121 struct sway_seat *seat = input_manager_current_seat();
122 struct sway_container *focus = seat_get_focused_container(seat);
123 if (focus && focus->view && focus->view->pid != xsurface->pid) {
124 return;
125 }
126
127 seat_set_focus_surface(seat, xsurface->surface, false);
128}
129
130static void unmanaged_handle_associate(struct wl_listener *listener, void *data) {
131 struct sway_xwayland_unmanaged *surface =
132 wl_container_of(listener, surface, associate);
133 struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface;
134 wl_signal_add(&xsurface->surface->events.map, &surface->map);
135 surface->map.notify = unmanaged_handle_map;
136 wl_signal_add(&xsurface->surface->events.unmap, &surface->unmap);
137 surface->unmap.notify = unmanaged_handle_unmap;
138}
139
140static void unmanaged_handle_dissociate(struct wl_listener *listener, void *data) {
141 struct sway_xwayland_unmanaged *surface =
142 wl_container_of(listener, surface, dissociate);
143 wl_list_remove(&surface->map.link);
144 wl_list_remove(&surface->unmap.link);
145}
146
124static void unmanaged_handle_destroy(struct wl_listener *listener, void *data) { 147static void unmanaged_handle_destroy(struct wl_listener *listener, void *data) {
125 struct sway_xwayland_unmanaged *surface = 148 struct sway_xwayland_unmanaged *surface =
126 wl_container_of(listener, surface, destroy); 149 wl_container_of(listener, surface, destroy);
127 wl_list_remove(&surface->request_configure.link); 150 wl_list_remove(&surface->request_configure.link);
128 wl_list_remove(&surface->map.link); 151 wl_list_remove(&surface->associate.link);
129 wl_list_remove(&surface->unmap.link); 152 wl_list_remove(&surface->dissociate.link);
130 wl_list_remove(&surface->destroy.link); 153 wl_list_remove(&surface->destroy.link);
131 wl_list_remove(&surface->override_redirect.link); 154 wl_list_remove(&surface->override_redirect.link);
155 wl_list_remove(&surface->request_activate.link);
132 free(surface); 156 free(surface);
133} 157}
134 158
135static void handle_map(struct wl_listener *listener, void *data); 159static void handle_map(struct wl_listener *listener, void *data);
160static void handle_associate(struct wl_listener *listener, void *data);
136 161
137struct sway_xwayland_view *create_xwayland_view(struct wlr_xwayland_surface *xsurface); 162struct sway_xwayland_view *create_xwayland_view(struct wlr_xwayland_surface *xsurface);
138 163
@@ -141,14 +166,22 @@ static void unmanaged_handle_override_redirect(struct wl_listener *listener, voi
141 wl_container_of(listener, surface, override_redirect); 166 wl_container_of(listener, surface, override_redirect);
142 struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface; 167 struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface;
143 168
144 bool mapped = xsurface->mapped; 169 bool associated = xsurface->surface != NULL;
170 bool mapped = associated && xsurface->surface->mapped;
145 if (mapped) { 171 if (mapped) {
146 unmanaged_handle_unmap(&surface->unmap, NULL); 172 unmanaged_handle_unmap(&surface->unmap, NULL);
147 } 173 }
174 if (associated) {
175 unmanaged_handle_dissociate(&surface->dissociate, NULL);
176 }
148 177
149 unmanaged_handle_destroy(&surface->destroy, NULL); 178 unmanaged_handle_destroy(&surface->destroy, NULL);
150 xsurface->data = NULL; 179 xsurface->data = NULL;
180
151 struct sway_xwayland_view *xwayland_view = create_xwayland_view(xsurface); 181 struct sway_xwayland_view *xwayland_view = create_xwayland_view(xsurface);
182 if (associated) {
183 handle_associate(&xwayland_view->associate, NULL);
184 }
152 if (mapped) { 185 if (mapped) {
153 handle_map(&xwayland_view->map, xsurface); 186 handle_map(&xwayland_view->map, xsurface);
154 } 187 }
@@ -168,14 +201,16 @@ static struct sway_xwayland_unmanaged *create_unmanaged(
168 wl_signal_add(&xsurface->events.request_configure, 201 wl_signal_add(&xsurface->events.request_configure,
169 &surface->request_configure); 202 &surface->request_configure);
170 surface->request_configure.notify = unmanaged_handle_request_configure; 203 surface->request_configure.notify = unmanaged_handle_request_configure;
171 wl_signal_add(&xsurface->events.map, &surface->map); 204 wl_signal_add(&xsurface->events.associate, &surface->associate);
172 surface->map.notify = unmanaged_handle_map; 205 surface->associate.notify = unmanaged_handle_associate;
173 wl_signal_add(&xsurface->events.unmap, &surface->unmap); 206 wl_signal_add(&xsurface->events.dissociate, &surface->dissociate);
174 surface->unmap.notify = unmanaged_handle_unmap; 207 surface->dissociate.notify = unmanaged_handle_dissociate;
175 wl_signal_add(&xsurface->events.destroy, &surface->destroy); 208 wl_signal_add(&xsurface->events.destroy, &surface->destroy);
176 surface->destroy.notify = unmanaged_handle_destroy; 209 surface->destroy.notify = unmanaged_handle_destroy;
177 wl_signal_add(&xsurface->events.set_override_redirect, &surface->override_redirect); 210 wl_signal_add(&xsurface->events.set_override_redirect, &surface->override_redirect);
178 surface->override_redirect.notify = unmanaged_handle_override_redirect; 211 surface->override_redirect.notify = unmanaged_handle_override_redirect;
212 wl_signal_add(&xsurface->events.request_activate, &surface->request_activate);
213 surface->request_activate.notify = unmanaged_handle_request_activate;
179 214
180 return surface; 215 return surface;
181} 216}
@@ -254,6 +289,7 @@ static void set_activated(struct sway_view *view, bool activated) {
254 } 289 }
255 290
256 wlr_xwayland_surface_activate(surface, activated); 291 wlr_xwayland_surface_activate(surface, activated);
292 wlr_xwayland_surface_restack(surface, NULL, XCB_STACK_MODE_ABOVE);
257} 293}
258 294
259static void set_tiled(struct sway_view *view, bool tiled) { 295static void set_tiled(struct sway_view *view, bool tiled) {
@@ -293,7 +329,7 @@ static bool wants_floating(struct sway_view *view) {
293 } 329 }
294 } 330 }
295 331
296 struct wlr_xwayland_surface_size_hints *size_hints = surface->size_hints; 332 xcb_size_hints_t *size_hints = surface->size_hints;
297 if (size_hints != NULL && 333 if (size_hints != NULL &&
298 size_hints->min_width > 0 && size_hints->min_height > 0 && 334 size_hints->min_width > 0 && size_hints->min_height > 0 &&
299 (size_hints->max_width == size_hints->min_width || 335 (size_hints->max_width == size_hints->min_width ||
@@ -347,7 +383,7 @@ static void destroy(struct sway_view *view) {
347static void get_constraints(struct sway_view *view, double *min_width, 383static void get_constraints(struct sway_view *view, double *min_width,
348 double *max_width, double *min_height, double *max_height) { 384 double *max_width, double *min_height, double *max_height) {
349 struct wlr_xwayland_surface *surface = view->wlr_xwayland_surface; 385 struct wlr_xwayland_surface *surface = view->wlr_xwayland_surface;
350 struct wlr_xwayland_surface_size_hints *size_hints = surface->size_hints; 386 xcb_size_hints_t *size_hints = surface->size_hints;
351 387
352 if (size_hints == NULL) { 388 if (size_hints == NULL) {
353 *min_width = DBL_MIN; 389 *min_width = DBL_MIN;
@@ -377,17 +413,6 @@ static const struct sway_view_impl view_impl = {
377 .destroy = destroy, 413 .destroy = destroy,
378}; 414};
379 415
380static void get_geometry(struct sway_view *view, struct wlr_box *box) {
381 box->x = box->y = 0;
382 if (view->surface) {
383 box->width = view->surface->current.width;
384 box->height = view->surface->current.height;
385 } else {
386 box->width = 0;
387 box->height = 0;
388 }
389}
390
391static void handle_commit(struct wl_listener *listener, void *data) { 416static void handle_commit(struct wl_listener *listener, void *data) {
392 struct sway_xwayland_view *xwayland_view = 417 struct sway_xwayland_view *xwayland_view =
393 wl_container_of(listener, xwayland_view, commit); 418 wl_container_of(listener, xwayland_view, commit);
@@ -395,34 +420,38 @@ static void handle_commit(struct wl_listener *listener, void *data) {
395 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; 420 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface;
396 struct wlr_surface_state *state = &xsurface->surface->current; 421 struct wlr_surface_state *state = &xsurface->surface->current;
397 422
398 struct wlr_box new_geo; 423 struct wlr_box new_geo = {0};
399 get_geometry(view, &new_geo); 424 new_geo.width = state->width;
425 new_geo.height = state->height;
426
400 bool new_size = new_geo.width != view->geometry.width || 427 bool new_size = new_geo.width != view->geometry.width ||
401 new_geo.height != view->geometry.height || 428 new_geo.height != view->geometry.height;
402 new_geo.x != view->geometry.x ||
403 new_geo.y != view->geometry.y;
404 429
405 if (new_size) { 430 if (new_size) {
406 // The client changed its surface size in this commit. For floating 431 // The client changed its surface size in this commit. For floating
407 // containers, we resize the container to match. For tiling containers, 432 // containers, we resize the container to match. For tiling containers,
408 // we only recenter the surface. 433 // we only recenter the surface.
409 desktop_damage_view(view);
410 memcpy(&view->geometry, &new_geo, sizeof(struct wlr_box)); 434 memcpy(&view->geometry, &new_geo, sizeof(struct wlr_box));
411 if (container_is_floating(view->container)) { 435 if (container_is_floating(view->container)) {
412 view_update_size(view); 436 view_update_size(view);
413 transaction_commit_dirty_client(); 437 transaction_commit_dirty_client();
414 } else {
415 view_center_surface(view);
416 } 438 }
417 desktop_damage_view(view); 439
440 view_center_and_clip_surface(view);
418 } 441 }
419 442
420 if (view->container->node.instruction) { 443 if (view->container->node.instruction) {
421 transaction_notify_view_ready_by_geometry(view, 444 bool successful = transaction_notify_view_ready_by_geometry(view,
422 xsurface->x, xsurface->y, state->width, state->height); 445 xsurface->x, xsurface->y, state->width, state->height);
423 }
424 446
425 view_damage_from(view); 447 // If we saved the view and this commit isn't what we're looking for
448 // that means the user will never actually see the buffers submitted to
449 // us here. Just send frame done events to these surfaces so they can
450 // commit another time for us.
451 if (view->saved_surface_tree && !successful) {
452 view_send_frame_done(view);
453 }
454 }
426} 455}
427 456
428static void handle_destroy(struct wl_listener *listener, void *data) { 457static void handle_destroy(struct wl_listener *listener, void *data) {
@@ -435,6 +464,8 @@ static void handle_destroy(struct wl_listener *listener, void *data) {
435 wl_list_remove(&xwayland_view->commit.link); 464 wl_list_remove(&xwayland_view->commit.link);
436 } 465 }
437 466
467 xwayland_view->view.wlr_xwayland_surface = NULL;
468
438 wl_list_remove(&xwayland_view->destroy.link); 469 wl_list_remove(&xwayland_view->destroy.link);
439 wl_list_remove(&xwayland_view->request_configure.link); 470 wl_list_remove(&xwayland_view->request_configure.link);
440 wl_list_remove(&xwayland_view->request_fullscreen.link); 471 wl_list_remove(&xwayland_view->request_fullscreen.link);
@@ -445,11 +476,12 @@ static void handle_destroy(struct wl_listener *listener, void *data) {
445 wl_list_remove(&xwayland_view->set_title.link); 476 wl_list_remove(&xwayland_view->set_title.link);
446 wl_list_remove(&xwayland_view->set_class.link); 477 wl_list_remove(&xwayland_view->set_class.link);
447 wl_list_remove(&xwayland_view->set_role.link); 478 wl_list_remove(&xwayland_view->set_role.link);
479 wl_list_remove(&xwayland_view->set_startup_id.link);
448 wl_list_remove(&xwayland_view->set_window_type.link); 480 wl_list_remove(&xwayland_view->set_window_type.link);
449 wl_list_remove(&xwayland_view->set_hints.link); 481 wl_list_remove(&xwayland_view->set_hints.link);
450 wl_list_remove(&xwayland_view->set_decorations.link); 482 wl_list_remove(&xwayland_view->set_decorations.link);
451 wl_list_remove(&xwayland_view->map.link); 483 wl_list_remove(&xwayland_view->associate.link);
452 wl_list_remove(&xwayland_view->unmap.link); 484 wl_list_remove(&xwayland_view->dissociate.link);
453 wl_list_remove(&xwayland_view->override_redirect.link); 485 wl_list_remove(&xwayland_view->override_redirect.link);
454 view_begin_destroy(&xwayland_view->view); 486 view_begin_destroy(&xwayland_view->view);
455} 487}
@@ -463,16 +495,28 @@ static void handle_unmap(struct wl_listener *listener, void *data) {
463 return; 495 return;
464 } 496 }
465 497
498 wl_list_remove(&xwayland_view->commit.link);
499 wl_list_remove(&xwayland_view->surface_tree_destroy.link);
500
501 if (xwayland_view->surface_tree) {
502 wlr_scene_node_destroy(&xwayland_view->surface_tree->node);
503 xwayland_view->surface_tree = NULL;
504 }
505
466 view_unmap(view); 506 view_unmap(view);
507}
467 508
468 wl_list_remove(&xwayland_view->commit.link); 509static void handle_surface_tree_destroy(struct wl_listener *listener, void *data) {
510 struct sway_xwayland_view *xwayland_view = wl_container_of(listener, xwayland_view,
511 surface_tree_destroy);
512 xwayland_view->surface_tree = NULL;
469} 513}
470 514
471static void handle_map(struct wl_listener *listener, void *data) { 515static void handle_map(struct wl_listener *listener, void *data) {
472 struct sway_xwayland_view *xwayland_view = 516 struct sway_xwayland_view *xwayland_view =
473 wl_container_of(listener, xwayland_view, map); 517 wl_container_of(listener, xwayland_view, map);
474 struct wlr_xwayland_surface *xsurface = data;
475 struct sway_view *view = &xwayland_view->view; 518 struct sway_view *view = &xwayland_view->view;
519 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface;
476 520
477 view->natural_width = xsurface->width; 521 view->natural_width = xsurface->width;
478 view->natural_height = xsurface->height; 522 view->natural_height = xsurface->height;
@@ -485,23 +529,42 @@ static void handle_map(struct wl_listener *listener, void *data) {
485 // Put it back into the tree 529 // Put it back into the tree
486 view_map(view, xsurface->surface, xsurface->fullscreen, NULL, false); 530 view_map(view, xsurface->surface, xsurface->fullscreen, NULL, false);
487 531
532 xwayland_view->surface_tree = wlr_scene_subsurface_tree_create(
533 xwayland_view->view.content_tree, xsurface->surface);
534
535 if (xwayland_view->surface_tree) {
536 xwayland_view->surface_tree_destroy.notify = handle_surface_tree_destroy;
537 wl_signal_add(&xwayland_view->surface_tree->node.events.destroy,
538 &xwayland_view->surface_tree_destroy);
539 }
540
488 transaction_commit_dirty(); 541 transaction_commit_dirty();
489} 542}
490 543
544static void handle_dissociate(struct wl_listener *listener, void *data);
545
491static void handle_override_redirect(struct wl_listener *listener, void *data) { 546static void handle_override_redirect(struct wl_listener *listener, void *data) {
492 struct sway_xwayland_view *xwayland_view = 547 struct sway_xwayland_view *xwayland_view =
493 wl_container_of(listener, xwayland_view, override_redirect); 548 wl_container_of(listener, xwayland_view, override_redirect);
494 struct wlr_xwayland_surface *xsurface = data;
495 struct sway_view *view = &xwayland_view->view; 549 struct sway_view *view = &xwayland_view->view;
550 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface;
496 551
497 bool mapped = xsurface->mapped; 552 bool associated = xsurface->surface != NULL;
553 bool mapped = associated && xsurface->surface->mapped;
498 if (mapped) { 554 if (mapped) {
499 handle_unmap(&xwayland_view->unmap, NULL); 555 handle_unmap(&xwayland_view->unmap, NULL);
500 } 556 }
557 if (associated) {
558 handle_dissociate(&xwayland_view->dissociate, NULL);
559 }
501 560
502 handle_destroy(&xwayland_view->destroy, view); 561 handle_destroy(&xwayland_view->destroy, view);
503 xsurface->data = NULL; 562 xsurface->data = NULL;
563
504 struct sway_xwayland_unmanaged *unmanaged = create_unmanaged(xsurface); 564 struct sway_xwayland_unmanaged *unmanaged = create_unmanaged(xsurface);
565 if (associated) {
566 unmanaged_handle_associate(&unmanaged->associate, NULL);
567 }
505 if (mapped) { 568 if (mapped) {
506 unmanaged_handle_map(&unmanaged->map, xsurface); 569 unmanaged_handle_map(&unmanaged->map, xsurface);
507 } 570 }
@@ -513,7 +576,7 @@ static void handle_request_configure(struct wl_listener *listener, void *data) {
513 struct wlr_xwayland_surface_configure_event *ev = data; 576 struct wlr_xwayland_surface_configure_event *ev = data;
514 struct sway_view *view = &xwayland_view->view; 577 struct sway_view *view = &xwayland_view->view;
515 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; 578 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface;
516 if (!xsurface->mapped) { 579 if (xsurface->surface == NULL || !xsurface->surface->mapped) {
517 wlr_xwayland_surface_configure(xsurface, ev->x, ev->y, 580 wlr_xwayland_surface_configure(xsurface, ev->x, ev->y,
518 ev->width, ev->height); 581 ev->width, ev->height);
519 return; 582 return;
@@ -542,7 +605,7 @@ static void handle_request_fullscreen(struct wl_listener *listener, void *data)
542 wl_container_of(listener, xwayland_view, request_fullscreen); 605 wl_container_of(listener, xwayland_view, request_fullscreen);
543 struct sway_view *view = &xwayland_view->view; 606 struct sway_view *view = &xwayland_view->view;
544 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; 607 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface;
545 if (!xsurface->mapped) { 608 if (xsurface->surface == NULL || !xsurface->surface->mapped) {
546 return; 609 return;
547 } 610 }
548 container_set_fullscreen(view->container, xsurface->fullscreen); 611 container_set_fullscreen(view->container, xsurface->fullscreen);
@@ -556,7 +619,7 @@ static void handle_request_minimize(struct wl_listener *listener, void *data) {
556 wl_container_of(listener, xwayland_view, request_minimize); 619 wl_container_of(listener, xwayland_view, request_minimize);
557 struct sway_view *view = &xwayland_view->view; 620 struct sway_view *view = &xwayland_view->view;
558 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; 621 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface;
559 if (!xsurface->mapped) { 622 if (xsurface->surface == NULL || !xsurface->surface->mapped) {
560 return; 623 return;
561 } 624 }
562 625
@@ -571,10 +634,11 @@ static void handle_request_move(struct wl_listener *listener, void *data) {
571 wl_container_of(listener, xwayland_view, request_move); 634 wl_container_of(listener, xwayland_view, request_move);
572 struct sway_view *view = &xwayland_view->view; 635 struct sway_view *view = &xwayland_view->view;
573 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; 636 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface;
574 if (!xsurface->mapped) { 637 if (xsurface->surface == NULL || !xsurface->surface->mapped) {
575 return; 638 return;
576 } 639 }
577 if (!container_is_floating(view->container)) { 640 if (!container_is_floating(view->container) ||
641 view->container->pending.fullscreen_mode) {
578 return; 642 return;
579 } 643 }
580 struct sway_seat *seat = input_manager_current_seat(); 644 struct sway_seat *seat = input_manager_current_seat();
@@ -586,7 +650,7 @@ static void handle_request_resize(struct wl_listener *listener, void *data) {
586 wl_container_of(listener, xwayland_view, request_resize); 650 wl_container_of(listener, xwayland_view, request_resize);
587 struct sway_view *view = &xwayland_view->view; 651 struct sway_view *view = &xwayland_view->view;
588 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; 652 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface;
589 if (!xsurface->mapped) { 653 if (xsurface->surface == NULL || !xsurface->surface->mapped) {
590 return; 654 return;
591 } 655 }
592 if (!container_is_floating(view->container)) { 656 if (!container_is_floating(view->container)) {
@@ -602,10 +666,10 @@ static void handle_request_activate(struct wl_listener *listener, void *data) {
602 wl_container_of(listener, xwayland_view, request_activate); 666 wl_container_of(listener, xwayland_view, request_activate);
603 struct sway_view *view = &xwayland_view->view; 667 struct sway_view *view = &xwayland_view->view;
604 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; 668 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface;
605 if (!xsurface->mapped) { 669 if (xsurface->surface == NULL || !xsurface->surface->mapped) {
606 return; 670 return;
607 } 671 }
608 view_request_activate(view); 672 view_request_activate(view, NULL);
609 673
610 transaction_commit_dirty(); 674 transaction_commit_dirty();
611} 675}
@@ -615,7 +679,7 @@ static void handle_set_title(struct wl_listener *listener, void *data) {
615 wl_container_of(listener, xwayland_view, set_title); 679 wl_container_of(listener, xwayland_view, set_title);
616 struct sway_view *view = &xwayland_view->view; 680 struct sway_view *view = &xwayland_view->view;
617 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; 681 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface;
618 if (!xsurface->mapped) { 682 if (xsurface->surface == NULL || !xsurface->surface->mapped) {
619 return; 683 return;
620 } 684 }
621 view_update_title(view, false); 685 view_update_title(view, false);
@@ -627,7 +691,7 @@ static void handle_set_class(struct wl_listener *listener, void *data) {
627 wl_container_of(listener, xwayland_view, set_class); 691 wl_container_of(listener, xwayland_view, set_class);
628 struct sway_view *view = &xwayland_view->view; 692 struct sway_view *view = &xwayland_view->view;
629 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; 693 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface;
630 if (!xsurface->mapped) { 694 if (xsurface->surface == NULL || !xsurface->surface->mapped) {
631 return; 695 return;
632 } 696 }
633 view_execute_criteria(view); 697 view_execute_criteria(view);
@@ -638,18 +702,43 @@ static void handle_set_role(struct wl_listener *listener, void *data) {
638 wl_container_of(listener, xwayland_view, set_role); 702 wl_container_of(listener, xwayland_view, set_role);
639 struct sway_view *view = &xwayland_view->view; 703 struct sway_view *view = &xwayland_view->view;
640 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; 704 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface;
641 if (!xsurface->mapped) { 705 if (xsurface->surface == NULL || !xsurface->surface->mapped) {
642 return; 706 return;
643 } 707 }
644 view_execute_criteria(view); 708 view_execute_criteria(view);
645} 709}
646 710
711static void handle_set_startup_id(struct wl_listener *listener, void *data) {
712 struct sway_xwayland_view *xwayland_view =
713 wl_container_of(listener, xwayland_view, set_startup_id);
714 struct sway_view *view = &xwayland_view->view;
715 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface;
716 if (xsurface->startup_id == NULL) {
717 return;
718 }
719
720 struct wlr_xdg_activation_token_v1 *token =
721 wlr_xdg_activation_v1_find_token(
722 server.xdg_activation_v1, xsurface->startup_id);
723 if (token == NULL) {
724 // Tried to activate with an unknown or expired token
725 return;
726 }
727
728 struct launcher_ctx *ctx = token->data;
729 if (token->data == NULL) {
730 // TODO: support external launchers in X
731 return;
732 }
733 view_assign_ctx(view, ctx);
734}
735
647static void handle_set_window_type(struct wl_listener *listener, void *data) { 736static void handle_set_window_type(struct wl_listener *listener, void *data) {
648 struct sway_xwayland_view *xwayland_view = 737 struct sway_xwayland_view *xwayland_view =
649 wl_container_of(listener, xwayland_view, set_window_type); 738 wl_container_of(listener, xwayland_view, set_window_type);
650 struct sway_view *view = &xwayland_view->view; 739 struct sway_view *view = &xwayland_view->view;
651 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; 740 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface;
652 if (!xsurface->mapped) { 741 if (xsurface->surface == NULL || !xsurface->surface->mapped) {
653 return; 742 return;
654 } 743 }
655 view_execute_criteria(view); 744 view_execute_criteria(view);
@@ -660,20 +749,39 @@ static void handle_set_hints(struct wl_listener *listener, void *data) {
660 wl_container_of(listener, xwayland_view, set_hints); 749 wl_container_of(listener, xwayland_view, set_hints);
661 struct sway_view *view = &xwayland_view->view; 750 struct sway_view *view = &xwayland_view->view;
662 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; 751 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface;
663 if (!xsurface->mapped) { 752 if (xsurface->surface == NULL || !xsurface->surface->mapped) {
664 return; 753 return;
665 } 754 }
666 if (!xsurface->hints_urgency && view->urgent_timer) { 755 const bool hints_urgency = xcb_icccm_wm_hints_get_urgency(xsurface->hints);
756 if (!hints_urgency && view->urgent_timer) {
667 // The view is in the timeout period. We'll ignore the request to 757 // The view is in the timeout period. We'll ignore the request to
668 // unset urgency so that the view remains urgent until the timer clears 758 // unset urgency so that the view remains urgent until the timer clears
669 // it. 759 // it.
670 return; 760 return;
671 } 761 }
672 if (view->allow_request_urgent) { 762 if (view->allow_request_urgent) {
673 view_set_urgent(view, (bool)xsurface->hints_urgency); 763 view_set_urgent(view, hints_urgency);
674 } 764 }
675} 765}
676 766
767static void handle_associate(struct wl_listener *listener, void *data) {
768 struct sway_xwayland_view *xwayland_view =
769 wl_container_of(listener, xwayland_view, associate);
770 struct wlr_xwayland_surface *xsurface =
771 xwayland_view->view.wlr_xwayland_surface;
772 wl_signal_add(&xsurface->surface->events.unmap, &xwayland_view->unmap);
773 xwayland_view->unmap.notify = handle_unmap;
774 wl_signal_add(&xsurface->surface->events.map, &xwayland_view->map);
775 xwayland_view->map.notify = handle_map;
776}
777
778static void handle_dissociate(struct wl_listener *listener, void *data) {
779 struct sway_xwayland_view *xwayland_view =
780 wl_container_of(listener, xwayland_view, dissociate);
781 wl_list_remove(&xwayland_view->map.link);
782 wl_list_remove(&xwayland_view->unmap.link);
783}
784
677struct sway_view *view_from_wlr_xwayland_surface( 785struct sway_view *view_from_wlr_xwayland_surface(
678 struct wlr_xwayland_surface *xsurface) { 786 struct wlr_xwayland_surface *xsurface) {
679 return xsurface->data; 787 return xsurface->data;
@@ -689,7 +797,10 @@ struct sway_xwayland_view *create_xwayland_view(struct wlr_xwayland_surface *xsu
689 return NULL; 797 return NULL;
690 } 798 }
691 799
692 view_init(&xwayland_view->view, SWAY_VIEW_XWAYLAND, &view_impl); 800 if (!view_init(&xwayland_view->view, SWAY_VIEW_XWAYLAND, &view_impl)) {
801 free(xwayland_view);
802 return NULL;
803 }
693 xwayland_view->view.wlr_xwayland_surface = xsurface; 804 xwayland_view->view.wlr_xwayland_surface = xsurface;
694 805
695 wl_signal_add(&xsurface->events.destroy, &xwayland_view->destroy); 806 wl_signal_add(&xsurface->events.destroy, &xwayland_view->destroy);
@@ -728,6 +839,10 @@ struct sway_xwayland_view *create_xwayland_view(struct wlr_xwayland_surface *xsu
728 wl_signal_add(&xsurface->events.set_role, &xwayland_view->set_role); 839 wl_signal_add(&xsurface->events.set_role, &xwayland_view->set_role);
729 xwayland_view->set_role.notify = handle_set_role; 840 xwayland_view->set_role.notify = handle_set_role;
730 841
842 wl_signal_add(&xsurface->events.set_startup_id,
843 &xwayland_view->set_startup_id);
844 xwayland_view->set_startup_id.notify = handle_set_startup_id;
845
731 wl_signal_add(&xsurface->events.set_window_type, 846 wl_signal_add(&xsurface->events.set_window_type,
732 &xwayland_view->set_window_type); 847 &xwayland_view->set_window_type);
733 xwayland_view->set_window_type.notify = handle_set_window_type; 848 xwayland_view->set_window_type.notify = handle_set_window_type;
@@ -739,11 +854,11 @@ struct sway_xwayland_view *create_xwayland_view(struct wlr_xwayland_surface *xsu
739 &xwayland_view->set_decorations); 854 &xwayland_view->set_decorations);
740 xwayland_view->set_decorations.notify = handle_set_decorations; 855 xwayland_view->set_decorations.notify = handle_set_decorations;
741 856
742 wl_signal_add(&xsurface->events.unmap, &xwayland_view->unmap); 857 wl_signal_add(&xsurface->events.associate, &xwayland_view->associate);
743 xwayland_view->unmap.notify = handle_unmap; 858 xwayland_view->associate.notify = handle_associate;
744 859
745 wl_signal_add(&xsurface->events.map, &xwayland_view->map); 860 wl_signal_add(&xsurface->events.dissociate, &xwayland_view->dissociate);
746 xwayland_view->map.notify = handle_map; 861 xwayland_view->dissociate.notify = handle_dissociate;
747 862
748 wl_signal_add(&xsurface->events.set_override_redirect, 863 wl_signal_add(&xsurface->events.set_override_redirect,
749 &xwayland_view->override_redirect); 864 &xwayland_view->override_redirect);
diff --git a/sway/input/cursor.c b/sway/input/cursor.c
index 2fe5b202..0b4a36d9 100644
--- a/sway/input/cursor.c
+++ b/sway/input/cursor.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <assert.h> 1#include <assert.h>
3#include <math.h> 2#include <math.h>
4#include <libevdev/libevdev.h> 3#include <libevdev/libevdev.h>
@@ -7,8 +6,9 @@
7#include <time.h> 6#include <time.h>
8#include <strings.h> 7#include <strings.h>
9#include <wlr/types/wlr_cursor.h> 8#include <wlr/types/wlr_cursor.h>
10#include <wlr/types/wlr_idle.h> 9#include <wlr/types/wlr_cursor_shape_v1.h>
11#include <wlr/types/wlr_pointer.h> 10#include <wlr/types/wlr_pointer.h>
11#include <wlr/types/wlr_relative_pointer_v1.h>
12#include <wlr/types/wlr_touch.h> 12#include <wlr/types/wlr_touch.h>
13#include <wlr/types/wlr_tablet_v2.h> 13#include <wlr/types/wlr_tablet_v2.h>
14#include <wlr/types/wlr_tablet_pad.h> 14#include <wlr/types/wlr_tablet_pad.h>
@@ -19,12 +19,12 @@
19#include "log.h" 19#include "log.h"
20#include "util.h" 20#include "util.h"
21#include "sway/commands.h" 21#include "sway/commands.h"
22#include "sway/desktop.h"
23#include "sway/input/cursor.h" 22#include "sway/input/cursor.h"
24#include "sway/input/keyboard.h" 23#include "sway/input/keyboard.h"
25#include "sway/input/tablet.h" 24#include "sway/input/tablet.h"
26#include "sway/layers.h" 25#include "sway/layers.h"
27#include "sway/output.h" 26#include "sway/output.h"
27#include "sway/scene_descriptor.h"
28#include "sway/tree/container.h" 28#include "sway/tree/container.h"
29#include "sway/tree/root.h" 29#include "sway/tree/root.h"
30#include "sway/tree/view.h" 30#include "sway/tree/view.h"
@@ -37,45 +37,6 @@ static uint32_t get_current_time_msec(void) {
37 return now.tv_sec * 1000 + now.tv_nsec / 1000000; 37 return now.tv_sec * 1000 + now.tv_nsec / 1000000;
38} 38}
39 39
40static struct wlr_surface *layer_surface_at(struct sway_output *output,
41 struct wl_list *layer, double ox, double oy, double *sx, double *sy) {
42 struct sway_layer_surface *sway_layer;
43 wl_list_for_each_reverse(sway_layer, layer, link) {
44 double _sx = ox - sway_layer->geo.x;
45 double _sy = oy - sway_layer->geo.y;
46 struct wlr_surface *sub = wlr_layer_surface_v1_surface_at(
47 sway_layer->layer_surface, _sx, _sy, sx, sy);
48 if (sub) {
49 return sub;
50 }
51 }
52 return NULL;
53}
54
55static bool surface_is_xdg_popup(struct wlr_surface *surface) {
56 if (wlr_surface_is_xdg_surface(surface)) {
57 struct wlr_xdg_surface *xdg_surface =
58 wlr_xdg_surface_from_wlr_surface(surface);
59 return xdg_surface->role == WLR_XDG_SURFACE_ROLE_POPUP;
60 }
61 return false;
62}
63
64static struct wlr_surface *layer_surface_popup_at(struct sway_output *output,
65 struct wl_list *layer, double ox, double oy, double *sx, double *sy) {
66 struct sway_layer_surface *sway_layer;
67 wl_list_for_each_reverse(sway_layer, layer, link) {
68 double _sx = ox - sway_layer->geo.x;
69 double _sy = oy - sway_layer->geo.y;
70 struct wlr_surface *sub = wlr_layer_surface_v1_surface_at(
71 sway_layer->layer_surface, _sx, _sy, sx, sy);
72 if (sub && surface_is_xdg_popup(sub)) {
73 return sub;
74 }
75 }
76 return NULL;
77}
78
79/** 40/**
80 * Returns the node at the cursor's position. If there is a surface at that 41 * Returns the node at the cursor's position. If there is a surface at that
81 * location, it is stored in **surface (it may not be a view). 42 * location, it is stored in **surface (it may not be a view).
@@ -83,116 +44,101 @@ static struct wlr_surface *layer_surface_popup_at(struct sway_output *output,
83struct sway_node *node_at_coords( 44struct sway_node *node_at_coords(
84 struct sway_seat *seat, double lx, double ly, 45 struct sway_seat *seat, double lx, double ly,
85 struct wlr_surface **surface, double *sx, double *sy) { 46 struct wlr_surface **surface, double *sx, double *sy) {
86 // check for unmanaged views first 47 struct wlr_scene_node *scene_node = NULL;
87#if HAVE_XWAYLAND 48
88 struct wl_list *unmanaged = &root->xwayland_unmanaged; 49 struct wlr_scene_node *node;
89 struct sway_xwayland_unmanaged *unmanaged_surface; 50 wl_list_for_each_reverse(node, &root->layer_tree->children, link) {
90 wl_list_for_each_reverse(unmanaged_surface, unmanaged, link) { 51 struct wlr_scene_tree *layer = wlr_scene_tree_from_node(node);
91 struct wlr_xwayland_surface *xsurface = 52
92 unmanaged_surface->wlr_xwayland_surface; 53 bool non_interactive = scene_descriptor_try_get(&layer->node,
93 54 SWAY_SCENE_DESC_NON_INTERACTIVE);
94 double _sx = lx - unmanaged_surface->lx; 55 if (non_interactive) {
95 double _sy = ly - unmanaged_surface->ly; 56 continue;
96 if (wlr_surface_point_accepts_input(xsurface->surface, _sx, _sy)) { 57 }
97 *surface = xsurface->surface; 58
98 *sx = _sx; 59 scene_node = wlr_scene_node_at(&layer->node, lx, ly, sx, sy);
99 *sy = _sy; 60 if (scene_node) {
100 return NULL; 61 break;
101 } 62 }
102 } 63 }
64
65 if (scene_node) {
66 // determine what wlr_surface we clicked on
67 if (scene_node->type == WLR_SCENE_NODE_BUFFER) {
68 struct wlr_scene_buffer *scene_buffer =
69 wlr_scene_buffer_from_node(scene_node);
70 struct wlr_scene_surface *scene_surface =
71 wlr_scene_surface_try_from_buffer(scene_buffer);
72
73 if (scene_surface) {
74 *surface = scene_surface->surface;
75 }
76 }
77
78 // determine what container we clicked on
79 struct wlr_scene_node *current = scene_node;
80 while (true) {
81 struct sway_container *con = scene_descriptor_try_get(current,
82 SWAY_SCENE_DESC_CONTAINER);
83
84 if (!con) {
85 struct sway_view *view = scene_descriptor_try_get(current,
86 SWAY_SCENE_DESC_VIEW);
87 if (view) {
88 con = view->container;
89 }
90 }
91
92 if (!con) {
93 struct sway_popup_desc *popup =
94 scene_descriptor_try_get(current, SWAY_SCENE_DESC_POPUP);
95 if (popup && popup->view) {
96 con = popup->view->container;
97 }
98 }
99
100 if (con && (!con->view || con->view->surface)) {
101 return &con->node;
102 }
103
104 if (scene_descriptor_try_get(current, SWAY_SCENE_DESC_LAYER_SHELL)) {
105 // We don't want to feed through the current workspace on
106 // layer shells
107 return NULL;
108 }
109
110#if WLR_HAS_XWAYLAND
111 if (scene_descriptor_try_get(current, SWAY_SCENE_DESC_XWAYLAND_UNMANAGED)) {
112 return NULL;
113 }
103#endif 114#endif
104 // find the output the cursor is on 115
116 if (!current->parent) {
117 break;
118 }
119
120 current = &current->parent->node;
121 }
122 }
123
124 // if we aren't on a container, determine what workspace we are on
105 struct wlr_output *wlr_output = wlr_output_layout_output_at( 125 struct wlr_output *wlr_output = wlr_output_layout_output_at(
106 root->output_layout, lx, ly); 126 root->output_layout, lx, ly);
107 if (wlr_output == NULL) { 127 if (wlr_output == NULL) {
108 return NULL; 128 return NULL;
109 } 129 }
130
110 struct sway_output *output = wlr_output->data; 131 struct sway_output *output = wlr_output->data;
111 if (!output || !output->enabled) { 132 if (!output || !output->enabled) {
112 // output is being destroyed or is being enabled 133 // output is being destroyed or is being enabled
113 return NULL; 134 return NULL;
114 } 135 }
115 double ox = lx, oy = ly;
116 wlr_output_layout_output_coords(root->output_layout, wlr_output, &ox, &oy);
117 136
118 if (root->fullscreen_global) {
119 // Try fullscreen container
120 struct sway_container *con = tiling_container_at(
121 &root->fullscreen_global->node, lx, ly, surface, sx, sy);
122 if (con) {
123 return &con->node;
124 }
125 return NULL;
126 }
127
128 // find the focused workspace on the output for this seat
129 struct sway_workspace *ws = output_get_active_workspace(output); 137 struct sway_workspace *ws = output_get_active_workspace(output);
130 if (!ws) { 138 if (!ws) {
131 return NULL; 139 return NULL;
132 } 140 }
133 141
134 if ((*surface = layer_surface_at(output,
135 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY],
136 ox, oy, sx, sy))) {
137 return NULL;
138 }
139 if (ws->fullscreen) {
140 // Try transient containers
141 for (int i = 0; i < ws->floating->length; ++i) {
142 struct sway_container *floater = ws->floating->items[i];
143 if (container_is_transient_for(floater, ws->fullscreen)) {
144 struct sway_container *con = tiling_container_at(
145 &floater->node, lx, ly, surface, sx, sy);
146 if (con) {
147 return &con->node;
148 }
149 }
150 }
151 // Try fullscreen container
152 struct sway_container *con =
153 tiling_container_at(&ws->fullscreen->node, lx, ly, surface, sx, sy);
154 if (con) {
155 return &con->node;
156 }
157 return NULL;
158 }
159 if ((*surface = layer_surface_popup_at(output,
160 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP],
161 ox, oy, sx, sy))) {
162 return NULL;
163 }
164 if ((*surface = layer_surface_popup_at(output,
165 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM],
166 ox, oy, sx, sy))) {
167 return NULL;
168 }
169 if ((*surface = layer_surface_popup_at(output,
170 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND],
171 ox, oy, sx, sy))) {
172 return NULL;
173 }
174 if ((*surface = layer_surface_at(output,
175 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP],
176 ox, oy, sx, sy))) {
177 return NULL;
178 }
179
180 struct sway_container *c;
181 if ((c = container_at(ws, lx, ly, surface, sx, sy))) {
182 return &c->node;
183 }
184
185 if ((*surface = layer_surface_at(output,
186 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM],
187 ox, oy, sx, sy))) {
188 return NULL;
189 }
190 if ((*surface = layer_surface_at(output,
191 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND],
192 ox, oy, sx, sy))) {
193 return NULL;
194 }
195
196 return &ws->node; 142 return &ws->node;
197} 143}
198 144
@@ -218,7 +164,7 @@ void cursor_update_image(struct sway_cursor *cursor,
218 // Try a node's resize edge 164 // Try a node's resize edge
219 enum wlr_edges edge = find_resize_edge(node->sway_container, NULL, cursor); 165 enum wlr_edges edge = find_resize_edge(node->sway_container, NULL, cursor);
220 if (edge == WLR_EDGE_NONE) { 166 if (edge == WLR_EDGE_NONE) {
221 cursor_set_image(cursor, "left_ptr", NULL); 167 cursor_set_image(cursor, "default", NULL);
222 } else if (container_is_floating(node->sway_container)) { 168 } else if (container_is_floating(node->sway_container)) {
223 cursor_set_image(cursor, wlr_xcursor_get_resize_name(edge), NULL); 169 cursor_set_image(cursor, wlr_xcursor_get_resize_name(edge), NULL);
224 } else { 170 } else {
@@ -229,12 +175,12 @@ void cursor_update_image(struct sway_cursor *cursor,
229 } 175 }
230 } 176 }
231 } else { 177 } else {
232 cursor_set_image(cursor, "left_ptr", NULL); 178 cursor_set_image(cursor, "default", NULL);
233 } 179 }
234} 180}
235 181
236static void cursor_hide(struct sway_cursor *cursor) { 182static void cursor_hide(struct sway_cursor *cursor) {
237 wlr_cursor_set_image(cursor->cursor, NULL, 0, 0, 0, 0, 0, 0); 183 wlr_cursor_unset_image(cursor->cursor);
238 cursor->hidden = true; 184 cursor->hidden = true;
239 wlr_seat_pointer_notify_clear_focus(cursor->seat->wlr_seat); 185 wlr_seat_pointer_notify_clear_focus(cursor->seat->wlr_seat);
240} 186}
@@ -297,7 +243,7 @@ static enum sway_input_idle_source idle_source_from_device(
297 return IDLE_SOURCE_POINTER; 243 return IDLE_SOURCE_POINTER;
298 case WLR_INPUT_DEVICE_TOUCH: 244 case WLR_INPUT_DEVICE_TOUCH:
299 return IDLE_SOURCE_TOUCH; 245 return IDLE_SOURCE_TOUCH;
300 case WLR_INPUT_DEVICE_TABLET_TOOL: 246 case WLR_INPUT_DEVICE_TABLET:
301 return IDLE_SOURCE_TABLET_TOOL; 247 return IDLE_SOURCE_TABLET_TOOL;
302 case WLR_INPUT_DEVICE_TABLET_PAD: 248 case WLR_INPUT_DEVICE_TABLET_PAD:
303 return IDLE_SOURCE_TABLET_PAD; 249 return IDLE_SOURCE_TABLET_PAD;
@@ -346,7 +292,7 @@ void cursor_unhide(struct sway_cursor *cursor) {
346 wl_event_source_timer_update(cursor->hide_source, cursor_get_timeout(cursor)); 292 wl_event_source_timer_update(cursor->hide_source, cursor_get_timeout(cursor));
347} 293}
348 294
349static void pointer_motion(struct sway_cursor *cursor, uint32_t time_msec, 295void pointer_motion(struct sway_cursor *cursor, uint32_t time_msec,
350 struct wlr_input_device *device, double dx, double dy, 296 struct wlr_input_device *device, double dx, double dy,
351 double dx_unaccel, double dy_unaccel) { 297 double dx_unaccel, double dy_unaccel) {
352 wlr_relative_pointer_manager_v1_send_relative_motion( 298 wlr_relative_pointer_manager_v1_send_relative_motion(
@@ -383,33 +329,34 @@ static void pointer_motion(struct sway_cursor *cursor, uint32_t time_msec,
383static void handle_pointer_motion_relative( 329static void handle_pointer_motion_relative(
384 struct wl_listener *listener, void *data) { 330 struct wl_listener *listener, void *data) {
385 struct sway_cursor *cursor = wl_container_of(listener, cursor, motion); 331 struct sway_cursor *cursor = wl_container_of(listener, cursor, motion);
386 struct wlr_event_pointer_motion *e = data; 332 struct wlr_pointer_motion_event *e = data;
387 cursor_handle_activity_from_device(cursor, e->device); 333 cursor_handle_activity_from_device(cursor, &e->pointer->base);
388 334
389 pointer_motion(cursor, e->time_msec, e->device, e->delta_x, e->delta_y, 335 pointer_motion(cursor, e->time_msec, &e->pointer->base, e->delta_x,
390 e->unaccel_dx, e->unaccel_dy); 336 e->delta_y, e->unaccel_dx, e->unaccel_dy);
391} 337}
392 338
393static void handle_pointer_motion_absolute( 339static void handle_pointer_motion_absolute(
394 struct wl_listener *listener, void *data) { 340 struct wl_listener *listener, void *data) {
395 struct sway_cursor *cursor = 341 struct sway_cursor *cursor =
396 wl_container_of(listener, cursor, motion_absolute); 342 wl_container_of(listener, cursor, motion_absolute);
397 struct wlr_event_pointer_motion_absolute *event = data; 343 struct wlr_pointer_motion_absolute_event *event = data;
398 cursor_handle_activity_from_device(cursor, event->device); 344 cursor_handle_activity_from_device(cursor, &event->pointer->base);
399 345
400 double lx, ly; 346 double lx, ly;
401 wlr_cursor_absolute_to_layout_coords(cursor->cursor, event->device, 347 wlr_cursor_absolute_to_layout_coords(cursor->cursor, &event->pointer->base,
402 event->x, event->y, &lx, &ly); 348 event->x, event->y, &lx, &ly);
403 349
404 double dx = lx - cursor->cursor->x; 350 double dx = lx - cursor->cursor->x;
405 double dy = ly - cursor->cursor->y; 351 double dy = ly - cursor->cursor->y;
406 352
407 pointer_motion(cursor, event->time_msec, event->device, dx, dy, dx, dy); 353 pointer_motion(cursor, event->time_msec, &event->pointer->base, dx, dy,
354 dx, dy);
408} 355}
409 356
410void dispatch_cursor_button(struct sway_cursor *cursor, 357void dispatch_cursor_button(struct sway_cursor *cursor,
411 struct wlr_input_device *device, uint32_t time_msec, uint32_t button, 358 struct wlr_input_device *device, uint32_t time_msec, uint32_t button,
412 enum wlr_button_state state) { 359 enum wl_pointer_button_state state) {
413 if (time_msec == 0) { 360 if (time_msec == 0) {
414 time_msec = get_current_time_msec(); 361 time_msec = get_current_time_msec();
415 } 362 }
@@ -419,9 +366,9 @@ void dispatch_cursor_button(struct sway_cursor *cursor,
419 366
420static void handle_pointer_button(struct wl_listener *listener, void *data) { 367static void handle_pointer_button(struct wl_listener *listener, void *data) {
421 struct sway_cursor *cursor = wl_container_of(listener, cursor, button); 368 struct sway_cursor *cursor = wl_container_of(listener, cursor, button);
422 struct wlr_event_pointer_button *event = data; 369 struct wlr_pointer_button_event *event = data;
423 370
424 if (event->state == WLR_BUTTON_PRESSED) { 371 if (event->state == WL_POINTER_BUTTON_STATE_PRESSED) {
425 cursor->pressed_button_count++; 372 cursor->pressed_button_count++;
426 } else { 373 } else {
427 if (cursor->pressed_button_count > 0) { 374 if (cursor->pressed_button_count > 0) {
@@ -431,20 +378,20 @@ static void handle_pointer_button(struct wl_listener *listener, void *data) {
431 } 378 }
432 } 379 }
433 380
434 cursor_handle_activity_from_device(cursor, event->device); 381 cursor_handle_activity_from_device(cursor, &event->pointer->base);
435 dispatch_cursor_button(cursor, event->device, 382 dispatch_cursor_button(cursor, &event->pointer->base,
436 event->time_msec, event->button, event->state); 383 event->time_msec, event->button, event->state);
437} 384}
438 385
439void dispatch_cursor_axis(struct sway_cursor *cursor, 386void dispatch_cursor_axis(struct sway_cursor *cursor,
440 struct wlr_event_pointer_axis *event) { 387 struct wlr_pointer_axis_event *event) {
441 seatop_pointer_axis(cursor->seat, event); 388 seatop_pointer_axis(cursor->seat, event);
442} 389}
443 390
444static void handle_pointer_axis(struct wl_listener *listener, void *data) { 391static void handle_pointer_axis(struct wl_listener *listener, void *data) {
445 struct sway_cursor *cursor = wl_container_of(listener, cursor, axis); 392 struct sway_cursor *cursor = wl_container_of(listener, cursor, axis);
446 struct wlr_event_pointer_axis *event = data; 393 struct wlr_pointer_axis_event *event = data;
447 cursor_handle_activity_from_device(cursor, event->device); 394 cursor_handle_activity_from_device(cursor, &event->pointer->base);
448 dispatch_cursor_axis(cursor, event); 395 dispatch_cursor_axis(cursor, event);
449} 396}
450 397
@@ -455,93 +402,76 @@ static void handle_pointer_frame(struct wl_listener *listener, void *data) {
455 402
456static void handle_touch_down(struct wl_listener *listener, void *data) { 403static void handle_touch_down(struct wl_listener *listener, void *data) {
457 struct sway_cursor *cursor = wl_container_of(listener, cursor, touch_down); 404 struct sway_cursor *cursor = wl_container_of(listener, cursor, touch_down);
458 struct wlr_event_touch_down *event = data; 405 struct wlr_touch_down_event *event = data;
459 cursor_handle_activity_from_device(cursor, event->device); 406 cursor_handle_activity_from_device(cursor, &event->touch->base);
460 cursor_hide(cursor); 407 cursor_hide(cursor);
461 408
462 struct sway_seat *seat = cursor->seat; 409 struct sway_seat *seat = cursor->seat;
463 struct wlr_seat *wlr_seat = seat->wlr_seat;
464 struct wlr_surface *surface = NULL;
465 410
466 double lx, ly; 411 double lx, ly;
467 wlr_cursor_absolute_to_layout_coords(cursor->cursor, event->device, 412 wlr_cursor_absolute_to_layout_coords(cursor->cursor, &event->touch->base,
468 event->x, event->y, &lx, &ly); 413 event->x, event->y, &lx, &ly);
469 double sx, sy;
470 struct sway_node *focused_node = node_at_coords(seat, lx, ly, &surface, &sx, &sy);
471 414
472 seat->touch_id = event->touch_id; 415 seat->touch_id = event->touch_id;
473 seat->touch_x = lx; 416 seat->touch_x = lx;
474 seat->touch_y = ly; 417 seat->touch_y = ly;
475 418
476 if (surface && wlr_surface_accepts_touch(wlr_seat, surface)) { 419 seatop_touch_down(seat, event, lx, ly);
477 if (seat_is_input_allowed(seat, surface)) { 420}
478 wlr_seat_touch_notify_down(wlr_seat, surface, event->time_msec,
479 event->touch_id, sx, sy);
480 421
481 if (focused_node) { 422static void handle_touch_up(struct wl_listener *listener, void *data) {
482 seat_set_focus(seat, focused_node); 423 struct sway_cursor *cursor = wl_container_of(listener, cursor, touch_up);
483 } 424 struct wlr_touch_up_event *event = data;
425 cursor_handle_activity_from_device(cursor, &event->touch->base);
426
427 struct sway_seat *seat = cursor->seat;
428
429 if (cursor->simulating_pointer_from_touch) {
430 if (cursor->pointer_touch_id == cursor->seat->touch_id) {
431 cursor->pointer_touch_up = true;
432 dispatch_cursor_button(cursor, &event->touch->base,
433 event->time_msec, BTN_LEFT, WL_POINTER_BUTTON_STATE_RELEASED);
484 } 434 }
485 } else if (!cursor->simulating_pointer_from_touch && 435 } else {
486 (!surface || seat_is_input_allowed(seat, surface))) { 436 seatop_touch_up(seat, event);
487 // Fallback to cursor simulation.
488 // The pointer_touch_id state is needed, so drags are not aborted when over
489 // a surface supporting touch and multi touch events don't interfere.
490 cursor->simulating_pointer_from_touch = true;
491 cursor->pointer_touch_id = seat->touch_id;
492 double dx, dy;
493 dx = lx - cursor->cursor->x;
494 dy = ly - cursor->cursor->y;
495 pointer_motion(cursor, event->time_msec, event->device, dx, dy, dx, dy);
496 dispatch_cursor_button(cursor, event->device, event->time_msec,
497 BTN_LEFT, WLR_BUTTON_PRESSED);
498 } 437 }
499} 438}
500 439
501static void handle_touch_up(struct wl_listener *listener, void *data) { 440static void handle_touch_cancel(struct wl_listener *listener, void *data) {
502 struct sway_cursor *cursor = wl_container_of(listener, cursor, touch_up); 441 struct sway_cursor *cursor = wl_container_of(listener, cursor, touch_cancel);
503 struct wlr_event_touch_up *event = data; 442 struct wlr_touch_cancel_event *event = data;
504 cursor_handle_activity_from_device(cursor, event->device); 443 cursor_handle_activity_from_device(cursor, &event->touch->base);
505 444
506 struct wlr_seat *wlr_seat = cursor->seat->wlr_seat; 445 struct sway_seat *seat = cursor->seat;
507 446
508 if (cursor->simulating_pointer_from_touch) { 447 if (cursor->simulating_pointer_from_touch) {
509 if (cursor->pointer_touch_id == cursor->seat->touch_id) { 448 if (cursor->pointer_touch_id == cursor->seat->touch_id) {
510 cursor->pointer_touch_up = true; 449 cursor->pointer_touch_up = true;
511 dispatch_cursor_button(cursor, event->device, event->time_msec, 450 dispatch_cursor_button(cursor, &event->touch->base,
512 BTN_LEFT, WLR_BUTTON_RELEASED); 451 event->time_msec, BTN_LEFT, WL_POINTER_BUTTON_STATE_RELEASED);
513 } 452 }
514 } else { 453 } else {
515 wlr_seat_touch_notify_up(wlr_seat, event->time_msec, event->touch_id); 454 seatop_touch_cancel(seat, event);
516 } 455 }
517} 456}
518 457
519static void handle_touch_motion(struct wl_listener *listener, void *data) { 458static void handle_touch_motion(struct wl_listener *listener, void *data) {
520 struct sway_cursor *cursor = 459 struct sway_cursor *cursor =
521 wl_container_of(listener, cursor, touch_motion); 460 wl_container_of(listener, cursor, touch_motion);
522 struct wlr_event_touch_motion *event = data; 461 struct wlr_touch_motion_event *event = data;
523 cursor_handle_activity_from_device(cursor, event->device); 462 cursor_handle_activity_from_device(cursor, &event->touch->base);
524 463
525 struct sway_seat *seat = cursor->seat; 464 struct sway_seat *seat = cursor->seat;
526 struct wlr_seat *wlr_seat = seat->wlr_seat;
527 struct wlr_surface *surface = NULL;
528 465
529 double lx, ly; 466 double lx, ly;
530 wlr_cursor_absolute_to_layout_coords(cursor->cursor, event->device, 467 wlr_cursor_absolute_to_layout_coords(cursor->cursor, &event->touch->base,
531 event->x, event->y, &lx, &ly); 468 event->x, event->y, &lx, &ly);
532 double sx, sy;
533 node_at_coords(cursor->seat, lx, ly, &surface, &sx, &sy);
534 469
535 if (seat->touch_id == event->touch_id) { 470 if (seat->touch_id == event->touch_id) {
536 seat->touch_x = lx; 471 seat->touch_x = lx;
537 seat->touch_y = ly; 472 seat->touch_y = ly;
538 473
539 struct sway_drag_icon *drag_icon; 474 drag_icons_update_position(seat);
540 wl_list_for_each(drag_icon, &root->drag_icons, link) {
541 if (drag_icon->seat == seat) {
542 drag_icon_update_position(drag_icon);
543 }
544 }
545 } 475 }
546 476
547 if (cursor->simulating_pointer_from_touch) { 477 if (cursor->simulating_pointer_from_touch) {
@@ -549,11 +479,11 @@ static void handle_touch_motion(struct wl_listener *listener, void *data) {
549 double dx, dy; 479 double dx, dy;
550 dx = lx - cursor->cursor->x; 480 dx = lx - cursor->cursor->x;
551 dy = ly - cursor->cursor->y; 481 dy = ly - cursor->cursor->y;
552 pointer_motion(cursor, event->time_msec, event->device, dx, dy, dx, dy); 482 pointer_motion(cursor, event->time_msec, &event->touch->base,
483 dx, dy, dx, dy);
553 } 484 }
554 } else if (surface) { 485 } else {
555 wlr_seat_touch_notify_motion(wlr_seat, event->time_msec, 486 seatop_touch_motion(seat, event, lx, ly);
556 event->touch_id, sx, sy);
557 } 487 }
558} 488}
559 489
@@ -588,14 +518,15 @@ static void apply_mapping_from_region(struct wlr_input_device *device,
588 double x1 = region->x1, x2 = region->x2; 518 double x1 = region->x1, x2 = region->x2;
589 double y1 = region->y1, y2 = region->y2; 519 double y1 = region->y1, y2 = region->y2;
590 520
591 if (region->mm) { 521 if (region->mm && device->type == WLR_INPUT_DEVICE_TABLET) {
592 if (device->width_mm == 0 || device->height_mm == 0) { 522 struct wlr_tablet *tablet = wlr_tablet_from_input_device(device);
523 if (tablet->width_mm == 0 || tablet->height_mm == 0) {
593 return; 524 return;
594 } 525 }
595 x1 /= device->width_mm; 526 x1 /= tablet->width_mm;
596 x2 /= device->width_mm; 527 x2 /= tablet->width_mm;
597 y1 /= device->height_mm; 528 y1 /= tablet->height_mm;
598 y2 /= device->height_mm; 529 y2 /= tablet->height_mm;
599 } 530 }
600 531
601 *x = apply_mapping_from_coord(x1, x2, *x); 532 *x = apply_mapping_from_coord(x1, x2, *x);
@@ -657,8 +588,8 @@ static void handle_tablet_tool_position(struct sway_cursor *cursor,
657 588
658static void handle_tool_axis(struct wl_listener *listener, void *data) { 589static void handle_tool_axis(struct wl_listener *listener, void *data) {
659 struct sway_cursor *cursor = wl_container_of(listener, cursor, tool_axis); 590 struct sway_cursor *cursor = wl_container_of(listener, cursor, tool_axis);
660 struct wlr_event_tablet_tool_axis *event = data; 591 struct wlr_tablet_tool_axis_event *event = data;
661 cursor_handle_activity_from_device(cursor, event->device); 592 cursor_handle_activity_from_device(cursor, &event->tablet->base);
662 593
663 struct sway_tablet_tool *sway_tool = event->tool->data; 594 struct sway_tablet_tool *sway_tool = event->tool->data;
664 if (!sway_tool) { 595 if (!sway_tool) {
@@ -713,8 +644,8 @@ static void handle_tool_axis(struct wl_listener *listener, void *data) {
713 644
714static void handle_tool_tip(struct wl_listener *listener, void *data) { 645static void handle_tool_tip(struct wl_listener *listener, void *data) {
715 struct sway_cursor *cursor = wl_container_of(listener, cursor, tool_tip); 646 struct sway_cursor *cursor = wl_container_of(listener, cursor, tool_tip);
716 struct wlr_event_tablet_tool_tip *event = data; 647 struct wlr_tablet_tool_tip_event *event = data;
717 cursor_handle_activity_from_device(cursor, event->device); 648 cursor_handle_activity_from_device(cursor, &event->tablet->base);
718 649
719 struct sway_tablet_tool *sway_tool = event->tool->data; 650 struct sway_tablet_tool *sway_tool = event->tool->data;
720 struct wlr_tablet_v2_tablet *tablet_v2 = sway_tool->tablet->tablet_v2; 651 struct wlr_tablet_v2_tablet *tablet_v2 = sway_tool->tablet->tablet_v2;
@@ -729,8 +660,8 @@ static void handle_tool_tip(struct wl_listener *listener, void *data) {
729 if (cursor->simulating_pointer_from_tool_tip && 660 if (cursor->simulating_pointer_from_tool_tip &&
730 event->state == WLR_TABLET_TOOL_TIP_UP) { 661 event->state == WLR_TABLET_TOOL_TIP_UP) {
731 cursor->simulating_pointer_from_tool_tip = false; 662 cursor->simulating_pointer_from_tool_tip = false;
732 dispatch_cursor_button(cursor, event->device, event->time_msec, 663 dispatch_cursor_button(cursor, &event->tablet->base, event->time_msec,
733 BTN_LEFT, WLR_BUTTON_RELEASED); 664 BTN_LEFT, WL_POINTER_BUTTON_STATE_RELEASED);
734 wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat); 665 wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat);
735 } else if (!surface || !wlr_surface_accepts_tablet_v2(tablet_v2, surface)) { 666 } else if (!surface || !wlr_surface_accepts_tablet_v2(tablet_v2, surface)) {
736 // If we started holding the tool tip down on a surface that accepts 667 // If we started holding the tool tip down on a surface that accepts
@@ -741,8 +672,8 @@ static void handle_tool_tip(struct wl_listener *listener, void *data) {
741 WLR_TABLET_TOOL_TIP_UP); 672 WLR_TABLET_TOOL_TIP_UP);
742 } else { 673 } else {
743 cursor->simulating_pointer_from_tool_tip = true; 674 cursor->simulating_pointer_from_tool_tip = true;
744 dispatch_cursor_button(cursor, event->device, event->time_msec, 675 dispatch_cursor_button(cursor, &event->tablet->base,
745 BTN_LEFT, WLR_BUTTON_PRESSED); 676 event->time_msec, BTN_LEFT, WL_POINTER_BUTTON_STATE_PRESSED);
746 wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat); 677 wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat);
747 } 678 }
748 } else { 679 } else {
@@ -764,12 +695,13 @@ static struct sway_tablet *get_tablet_for_device(struct sway_cursor *cursor,
764static void handle_tool_proximity(struct wl_listener *listener, void *data) { 695static void handle_tool_proximity(struct wl_listener *listener, void *data) {
765 struct sway_cursor *cursor = 696 struct sway_cursor *cursor =
766 wl_container_of(listener, cursor, tool_proximity); 697 wl_container_of(listener, cursor, tool_proximity);
767 struct wlr_event_tablet_tool_proximity *event = data; 698 struct wlr_tablet_tool_proximity_event *event = data;
768 cursor_handle_activity_from_device(cursor, event->device); 699 cursor_handle_activity_from_device(cursor, &event->tablet->base);
769 700
770 struct wlr_tablet_tool *tool = event->tool; 701 struct wlr_tablet_tool *tool = event->tool;
771 if (!tool->data) { 702 if (!tool->data) {
772 struct sway_tablet *tablet = get_tablet_for_device(cursor, event->device); 703 struct sway_tablet *tablet = get_tablet_for_device(cursor,
704 &event->tablet->base);
773 if (!tablet) { 705 if (!tablet) {
774 sway_log(SWAY_ERROR, "no tablet for tablet tool"); 706 sway_log(SWAY_ERROR, "no tablet for tablet tool");
775 return; 707 return;
@@ -794,8 +726,8 @@ static void handle_tool_proximity(struct wl_listener *listener, void *data) {
794 726
795static void handle_tool_button(struct wl_listener *listener, void *data) { 727static void handle_tool_button(struct wl_listener *listener, void *data) {
796 struct sway_cursor *cursor = wl_container_of(listener, cursor, tool_button); 728 struct sway_cursor *cursor = wl_container_of(listener, cursor, tool_button);
797 struct wlr_event_tablet_tool_button *event = data; 729 struct wlr_tablet_tool_button_event *event = data;
798 cursor_handle_activity_from_device(cursor, event->device); 730 cursor_handle_activity_from_device(cursor, &event->tablet->base);
799 731
800 struct sway_tablet_tool *sway_tool = event->tool->data; 732 struct sway_tablet_tool *sway_tool = event->tool->data;
801 if (!sway_tool) { 733 if (!sway_tool) {
@@ -810,31 +742,71 @@ static void handle_tool_button(struct wl_listener *listener, void *data) {
810 node_at_coords(cursor->seat, cursor->cursor->x, cursor->cursor->y, 742 node_at_coords(cursor->seat, cursor->cursor->x, cursor->cursor->y,
811 &surface, &sx, &sy); 743 &surface, &sx, &sy);
812 744
813 if (!surface || !wlr_surface_accepts_tablet_v2(tablet_v2, surface)) { 745 // TODO: floating resize should support graphics tablet events
746 struct wlr_keyboard *keyboard = wlr_seat_get_keyboard(cursor->seat->wlr_seat);
747 uint32_t modifiers = keyboard ? wlr_keyboard_get_modifiers(keyboard) : 0;
748 bool mod_pressed = modifiers & config->floating_mod;
749
750 bool surface_supports_tablet_events =
751 surface && wlr_surface_accepts_tablet_v2(tablet_v2, surface);
752
753 // Simulate pointer when:
754 // 1. The modifier key is pressed, OR
755 // 2. The surface under the cursor does not support tablet events.
756 bool should_simulate_pointer = mod_pressed || !surface_supports_tablet_events;
757
758 // Similar to tool tip, we need to selectively simulate mouse events, but we
759 // want to make sure that it is always consistent. Because all tool buttons
760 // currently map to BTN_RIGHT, we need to keep count of how many tool
761 // buttons are currently pressed down so we can send consistent events.
762 //
763 // The logic follows:
764 // - If we are already simulating the pointer, we should continue to do so
765 // until at least no tool button is held down.
766 // - If we should simulate the pointer and no tool button is currently held
767 // down, begin simulating the pointer.
768 // - If neither of the above are true, send the tablet events.
769 if ((cursor->tool_buttons > 0 && cursor->simulating_pointer_from_tool_button)
770 || (cursor->tool_buttons == 0 && should_simulate_pointer)) {
771 cursor->simulating_pointer_from_tool_button = true;
772
814 // TODO: the user may want to configure which tool buttons are mapped to 773 // TODO: the user may want to configure which tool buttons are mapped to
815 // which simulated pointer buttons 774 // which simulated pointer buttons
816 switch (event->state) { 775 switch (event->state) {
817 case WLR_BUTTON_PRESSED: 776 case WLR_BUTTON_PRESSED:
818 if (cursor->tool_buttons == 0) { 777 if (cursor->tool_buttons == 0) {
819 dispatch_cursor_button(cursor, event->device, 778 dispatch_cursor_button(cursor, &event->tablet->base,
820 event->time_msec, BTN_RIGHT, event->state); 779 event->time_msec, BTN_RIGHT, WL_POINTER_BUTTON_STATE_PRESSED);
821 } 780 }
822 cursor->tool_buttons++;
823 break; 781 break;
824 case WLR_BUTTON_RELEASED: 782 case WLR_BUTTON_RELEASED:
825 if (cursor->tool_buttons == 1) { 783 if (cursor->tool_buttons <= 1) {
826 dispatch_cursor_button(cursor, event->device, 784 dispatch_cursor_button(cursor, &event->tablet->base,
827 event->time_msec, BTN_RIGHT, event->state); 785 event->time_msec, BTN_RIGHT, WL_POINTER_BUTTON_STATE_RELEASED);
828 } 786 }
829 cursor->tool_buttons--;
830 break; 787 break;
831 } 788 }
832 wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat); 789 wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat);
833 return; 790 } else {
791 cursor->simulating_pointer_from_tool_button = false;
792
793 wlr_tablet_v2_tablet_tool_notify_button(sway_tool->tablet_v2_tool,
794 event->button, (enum zwp_tablet_pad_v2_button_state)event->state);
834 } 795 }
835 796
836 wlr_tablet_v2_tablet_tool_notify_button(sway_tool->tablet_v2_tool, 797 // Update tool button count.
837 event->button, (enum zwp_tablet_pad_v2_button_state)event->state); 798 switch (event->state) {
799 case WLR_BUTTON_PRESSED:
800 cursor->tool_buttons++;
801 break;
802 case WLR_BUTTON_RELEASED:
803 if (cursor->tool_buttons == 0) {
804 sway_log(SWAY_ERROR, "inconsistent tablet tool button events");
805 } else {
806 cursor->tool_buttons--;
807 }
808 break;
809 }
838} 810}
839 811
840static void check_constraint_region(struct sway_cursor *cursor) { 812static void check_constraint_region(struct sway_cursor *cursor) {
@@ -920,59 +892,68 @@ static void handle_request_pointer_set_cursor(struct wl_listener *listener,
920 event->hotspot_y, focused_client); 892 event->hotspot_y, focused_client);
921} 893}
922 894
895static void handle_pointer_hold_begin(struct wl_listener *listener, void *data) {
896 struct sway_cursor *cursor = wl_container_of(
897 listener, cursor, hold_begin);
898 struct wlr_pointer_hold_begin_event *event = data;
899 cursor_handle_activity_from_device(cursor, &event->pointer->base);
900 seatop_hold_begin(cursor->seat, event);
901}
902
903static void handle_pointer_hold_end(struct wl_listener *listener, void *data) {
904 struct sway_cursor *cursor = wl_container_of(
905 listener, cursor, hold_end);
906 struct wlr_pointer_hold_end_event *event = data;
907 cursor_handle_activity_from_device(cursor, &event->pointer->base);
908 seatop_hold_end(cursor->seat, event);
909}
910
923static void handle_pointer_pinch_begin(struct wl_listener *listener, void *data) { 911static void handle_pointer_pinch_begin(struct wl_listener *listener, void *data) {
924 struct sway_cursor *cursor = wl_container_of( 912 struct sway_cursor *cursor = wl_container_of(
925 listener, cursor, pinch_begin); 913 listener, cursor, pinch_begin);
926 struct wlr_event_pointer_pinch_begin *event = data; 914 struct wlr_pointer_pinch_begin_event *event = data;
927 wlr_pointer_gestures_v1_send_pinch_begin( 915 cursor_handle_activity_from_device(cursor, &event->pointer->base);
928 cursor->pointer_gestures, cursor->seat->wlr_seat, 916 seatop_pinch_begin(cursor->seat, event);
929 event->time_msec, event->fingers);
930} 917}
931 918
932static void handle_pointer_pinch_update(struct wl_listener *listener, void *data) { 919static void handle_pointer_pinch_update(struct wl_listener *listener, void *data) {
933 struct sway_cursor *cursor = wl_container_of( 920 struct sway_cursor *cursor = wl_container_of(
934 listener, cursor, pinch_update); 921 listener, cursor, pinch_update);
935 struct wlr_event_pointer_pinch_update *event = data; 922 struct wlr_pointer_pinch_update_event *event = data;
936 wlr_pointer_gestures_v1_send_pinch_update( 923 cursor_handle_activity_from_device(cursor, &event->pointer->base);
937 cursor->pointer_gestures, cursor->seat->wlr_seat, 924 seatop_pinch_update(cursor->seat, event);
938 event->time_msec, event->dx, event->dy,
939 event->scale, event->rotation);
940} 925}
941 926
942static void handle_pointer_pinch_end(struct wl_listener *listener, void *data) { 927static void handle_pointer_pinch_end(struct wl_listener *listener, void *data) {
943 struct sway_cursor *cursor = wl_container_of( 928 struct sway_cursor *cursor = wl_container_of(
944 listener, cursor, pinch_end); 929 listener, cursor, pinch_end);
945 struct wlr_event_pointer_pinch_end *event = data; 930 struct wlr_pointer_pinch_end_event *event = data;
946 wlr_pointer_gestures_v1_send_pinch_end( 931 cursor_handle_activity_from_device(cursor, &event->pointer->base);
947 cursor->pointer_gestures, cursor->seat->wlr_seat, 932 seatop_pinch_end(cursor->seat, event);
948 event->time_msec, event->cancelled);
949} 933}
950 934
951static void handle_pointer_swipe_begin(struct wl_listener *listener, void *data) { 935static void handle_pointer_swipe_begin(struct wl_listener *listener, void *data) {
952 struct sway_cursor *cursor = wl_container_of( 936 struct sway_cursor *cursor = wl_container_of(
953 listener, cursor, swipe_begin); 937 listener, cursor, swipe_begin);
954 struct wlr_event_pointer_swipe_begin *event = data; 938 struct wlr_pointer_swipe_begin_event *event = data;
955 wlr_pointer_gestures_v1_send_swipe_begin( 939 cursor_handle_activity_from_device(cursor, &event->pointer->base);
956 cursor->pointer_gestures, cursor->seat->wlr_seat, 940 seatop_swipe_begin(cursor->seat, event);
957 event->time_msec, event->fingers);
958} 941}
959 942
960static void handle_pointer_swipe_update(struct wl_listener *listener, void *data) { 943static void handle_pointer_swipe_update(struct wl_listener *listener, void *data) {
961 struct sway_cursor *cursor = wl_container_of( 944 struct sway_cursor *cursor = wl_container_of(
962 listener, cursor, swipe_update); 945 listener, cursor, swipe_update);
963 struct wlr_event_pointer_swipe_update *event = data; 946 struct wlr_pointer_swipe_update_event *event = data;
964 wlr_pointer_gestures_v1_send_swipe_update( 947 cursor_handle_activity_from_device(cursor, &event->pointer->base);
965 cursor->pointer_gestures, cursor->seat->wlr_seat, 948 seatop_swipe_update(cursor->seat, event);
966 event->time_msec, event->dx, event->dy);
967} 949}
968 950
969static void handle_pointer_swipe_end(struct wl_listener *listener, void *data) { 951static void handle_pointer_swipe_end(struct wl_listener *listener, void *data) {
970 struct sway_cursor *cursor = wl_container_of( 952 struct sway_cursor *cursor = wl_container_of(
971 listener, cursor, swipe_end); 953 listener, cursor, swipe_end);
972 struct wlr_event_pointer_swipe_end *event = data; 954 struct wlr_pointer_swipe_end_event *event = data;
973 wlr_pointer_gestures_v1_send_swipe_end( 955 cursor_handle_activity_from_device(cursor, &event->pointer->base);
974 cursor->pointer_gestures, cursor->seat->wlr_seat, 956 seatop_swipe_end(cursor->seat, event);
975 event->time_msec, event->cancelled);
976} 957}
977 958
978static void handle_image_surface_destroy(struct wl_listener *listener, 959static void handle_image_surface_destroy(struct wl_listener *listener,
@@ -1011,10 +992,9 @@ void cursor_set_image(struct sway_cursor *cursor, const char *image,
1011 } 992 }
1012 993
1013 if (!image) { 994 if (!image) {
1014 wlr_cursor_set_image(cursor->cursor, NULL, 0, 0, 0, 0, 0, 0); 995 wlr_cursor_unset_image(cursor->cursor);
1015 } else if (!current_image || strcmp(current_image, image) != 0) { 996 } else if (!current_image || strcmp(current_image, image) != 0) {
1016 wlr_xcursor_manager_set_cursor_image(cursor->xcursor_manager, image, 997 wlr_cursor_set_xcursor(cursor->cursor, cursor->xcursor_manager, image);
1017 cursor->cursor);
1018 } 998 }
1019} 999}
1020 1000
@@ -1046,6 +1026,8 @@ void sway_cursor_destroy(struct sway_cursor *cursor) {
1046 wl_event_source_remove(cursor->hide_source); 1026 wl_event_source_remove(cursor->hide_source);
1047 1027
1048 wl_list_remove(&cursor->image_surface_destroy.link); 1028 wl_list_remove(&cursor->image_surface_destroy.link);
1029 wl_list_remove(&cursor->hold_begin.link);
1030 wl_list_remove(&cursor->hold_end.link);
1049 wl_list_remove(&cursor->pinch_begin.link); 1031 wl_list_remove(&cursor->pinch_begin.link);
1050 wl_list_remove(&cursor->pinch_update.link); 1032 wl_list_remove(&cursor->pinch_update.link);
1051 wl_list_remove(&cursor->pinch_end.link); 1033 wl_list_remove(&cursor->pinch_end.link);
@@ -1059,6 +1041,7 @@ void sway_cursor_destroy(struct sway_cursor *cursor) {
1059 wl_list_remove(&cursor->frame.link); 1041 wl_list_remove(&cursor->frame.link);
1060 wl_list_remove(&cursor->touch_down.link); 1042 wl_list_remove(&cursor->touch_down.link);
1061 wl_list_remove(&cursor->touch_up.link); 1043 wl_list_remove(&cursor->touch_up.link);
1044 wl_list_remove(&cursor->touch_cancel.link);
1062 wl_list_remove(&cursor->touch_motion.link); 1045 wl_list_remove(&cursor->touch_motion.link);
1063 wl_list_remove(&cursor->touch_frame.link); 1046 wl_list_remove(&cursor->touch_frame.link);
1064 wl_list_remove(&cursor->tool_axis.link); 1047 wl_list_remove(&cursor->tool_axis.link);
@@ -1095,19 +1078,24 @@ struct sway_cursor *sway_cursor_create(struct sway_seat *seat) {
1095 wl_list_init(&cursor->image_surface_destroy.link); 1078 wl_list_init(&cursor->image_surface_destroy.link);
1096 cursor->image_surface_destroy.notify = handle_image_surface_destroy; 1079 cursor->image_surface_destroy.notify = handle_image_surface_destroy;
1097 1080
1098 cursor->pointer_gestures = wlr_pointer_gestures_v1_create(server.wl_display); 1081 wl_signal_add(&wlr_cursor->events.hold_begin, &cursor->hold_begin);
1099 cursor->pinch_begin.notify = handle_pointer_pinch_begin; 1082 cursor->hold_begin.notify = handle_pointer_hold_begin;
1083 wl_signal_add(&wlr_cursor->events.hold_end, &cursor->hold_end);
1084 cursor->hold_end.notify = handle_pointer_hold_end;
1085
1100 wl_signal_add(&wlr_cursor->events.pinch_begin, &cursor->pinch_begin); 1086 wl_signal_add(&wlr_cursor->events.pinch_begin, &cursor->pinch_begin);
1101 cursor->pinch_update.notify = handle_pointer_pinch_update; 1087 cursor->pinch_begin.notify = handle_pointer_pinch_begin;
1102 wl_signal_add(&wlr_cursor->events.pinch_update, &cursor->pinch_update); 1088 wl_signal_add(&wlr_cursor->events.pinch_update, &cursor->pinch_update);
1103 cursor->pinch_end.notify = handle_pointer_pinch_end; 1089 cursor->pinch_update.notify = handle_pointer_pinch_update;
1104 wl_signal_add(&wlr_cursor->events.pinch_end, &cursor->pinch_end); 1090 wl_signal_add(&wlr_cursor->events.pinch_end, &cursor->pinch_end);
1105 cursor->swipe_begin.notify = handle_pointer_swipe_begin; 1091 cursor->pinch_end.notify = handle_pointer_pinch_end;
1092
1106 wl_signal_add(&wlr_cursor->events.swipe_begin, &cursor->swipe_begin); 1093 wl_signal_add(&wlr_cursor->events.swipe_begin, &cursor->swipe_begin);
1107 cursor->swipe_update.notify = handle_pointer_swipe_update; 1094 cursor->swipe_begin.notify = handle_pointer_swipe_begin;
1108 wl_signal_add(&wlr_cursor->events.swipe_update, &cursor->swipe_update); 1095 wl_signal_add(&wlr_cursor->events.swipe_update, &cursor->swipe_update);
1109 cursor->swipe_end.notify = handle_pointer_swipe_end; 1096 cursor->swipe_update.notify = handle_pointer_swipe_update;
1110 wl_signal_add(&wlr_cursor->events.swipe_end, &cursor->swipe_end); 1097 wl_signal_add(&wlr_cursor->events.swipe_end, &cursor->swipe_end);
1098 cursor->swipe_end.notify = handle_pointer_swipe_end;
1111 1099
1112 // input events 1100 // input events
1113 wl_signal_add(&wlr_cursor->events.motion, &cursor->motion); 1101 wl_signal_add(&wlr_cursor->events.motion, &cursor->motion);
@@ -1132,6 +1120,9 @@ struct sway_cursor *sway_cursor_create(struct sway_seat *seat) {
1132 wl_signal_add(&wlr_cursor->events.touch_up, &cursor->touch_up); 1120 wl_signal_add(&wlr_cursor->events.touch_up, &cursor->touch_up);
1133 cursor->touch_up.notify = handle_touch_up; 1121 cursor->touch_up.notify = handle_touch_up;
1134 1122
1123 wl_signal_add(&wlr_cursor->events.touch_cancel, &cursor->touch_cancel);
1124 cursor->touch_cancel.notify = handle_touch_cancel;
1125
1135 wl_signal_add(&wlr_cursor->events.touch_motion, 1126 wl_signal_add(&wlr_cursor->events.touch_motion,
1136 &cursor->touch_motion); 1127 &cursor->touch_motion);
1137 cursor->touch_motion.notify = handle_touch_motion; 1128 cursor->touch_motion.notify = handle_touch_motion;
@@ -1224,11 +1215,7 @@ uint32_t get_mouse_bindsym(const char *name, char **error) {
1224 // Get event code from name 1215 // Get event code from name
1225 int code = libevdev_event_code_from_name(EV_KEY, name); 1216 int code = libevdev_event_code_from_name(EV_KEY, name);
1226 if (code == -1) { 1217 if (code == -1) {
1227 size_t len = snprintf(NULL, 0, "Unknown event %s", name) + 1; 1218 *error = format_str("Unknown event %s", name);
1228 *error = malloc(len);
1229 if (*error) {
1230 snprintf(*error, len, "Unknown event %s", name);
1231 }
1232 return 0; 1219 return 0;
1233 } 1220 }
1234 return code; 1221 return code;
@@ -1250,13 +1237,8 @@ uint32_t get_mouse_bindcode(const char *name, char **error) {
1250 } 1237 }
1251 const char *event = libevdev_event_code_get_name(EV_KEY, code); 1238 const char *event = libevdev_event_code_get_name(EV_KEY, code);
1252 if (!event || strncmp(event, "BTN_", strlen("BTN_")) != 0) { 1239 if (!event || strncmp(event, "BTN_", strlen("BTN_")) != 0) {
1253 size_t len = snprintf(NULL, 0, "Event code %d (%s) is not a button", 1240 *error = format_str("Event code %d (%s) is not a button",
1254 code, event ? event : "(null)") + 1; 1241 code, event ? event : "(null)");
1255 *error = malloc(len);
1256 if (*error) {
1257 snprintf(*error, len, "Event code %d (%s) is not a button",
1258 code, event ? event : "(null)");
1259 }
1260 return 0; 1242 return 0;
1261 } 1243 }
1262 return code; 1244 return code;
@@ -1289,12 +1271,15 @@ const char *get_mouse_button_name(uint32_t button) {
1289static void warp_to_constraint_cursor_hint(struct sway_cursor *cursor) { 1271static void warp_to_constraint_cursor_hint(struct sway_cursor *cursor) {
1290 struct wlr_pointer_constraint_v1 *constraint = cursor->active_constraint; 1272 struct wlr_pointer_constraint_v1 *constraint = cursor->active_constraint;
1291 1273
1292 if (constraint->current.committed & 1274 if (constraint->current.cursor_hint.enabled) {
1293 WLR_POINTER_CONSTRAINT_V1_STATE_CURSOR_HINT) {
1294 double sx = constraint->current.cursor_hint.x; 1275 double sx = constraint->current.cursor_hint.x;
1295 double sy = constraint->current.cursor_hint.y; 1276 double sy = constraint->current.cursor_hint.y;
1296 1277
1297 struct sway_view *view = view_from_wlr_surface(constraint->surface); 1278 struct sway_view *view = view_from_wlr_surface(constraint->surface);
1279 if (!view) {
1280 return;
1281 }
1282
1298 struct sway_container *con = view->container; 1283 struct sway_container *con = view->container;
1299 1284
1300 double lx = sx + con->pending.content_x - view->geometry.x; 1285 double lx = sx + con->pending.content_x - view->geometry.x;
@@ -1345,12 +1330,9 @@ void handle_pointer_constraint(struct wl_listener *listener, void *data) {
1345 sway_constraint->destroy.notify = handle_constraint_destroy; 1330 sway_constraint->destroy.notify = handle_constraint_destroy;
1346 wl_signal_add(&constraint->events.destroy, &sway_constraint->destroy); 1331 wl_signal_add(&constraint->events.destroy, &sway_constraint->destroy);
1347 1332
1348 struct sway_node *focus = seat_get_focus(seat); 1333 struct wlr_surface *surface = seat->wlr_seat->keyboard_state.focused_surface;
1349 if (focus && focus->type == N_CONTAINER && focus->sway_container->view) { 1334 if (surface && surface == constraint->surface) {
1350 struct wlr_surface *surface = focus->sway_container->view->surface; 1335 sway_cursor_constrain(seat->cursor, constraint);
1351 if (surface == constraint->surface) {
1352 sway_cursor_constrain(seat->cursor, constraint);
1353 }
1354 } 1336 }
1355} 1337}
1356 1338
@@ -1408,3 +1390,26 @@ void sway_cursor_constrain(struct sway_cursor *cursor,
1408 wl_signal_add(&constraint->surface->events.commit, 1390 wl_signal_add(&constraint->surface->events.commit,
1409 &cursor->constraint_commit); 1391 &cursor->constraint_commit);
1410} 1392}
1393
1394void handle_request_set_cursor_shape(struct wl_listener *listener, void *data) {
1395 const struct wlr_cursor_shape_manager_v1_request_set_shape_event *event = data;
1396 struct sway_seat *seat = event->seat_client->seat->data;
1397
1398 if (!seatop_allows_set_cursor(seat)) {
1399 return;
1400 }
1401
1402 struct wl_client *focused_client = NULL;
1403 struct wlr_surface *focused_surface = seat->wlr_seat->pointer_state.focused_surface;
1404 if (focused_surface != NULL) {
1405 focused_client = wl_resource_get_client(focused_surface->resource);
1406 }
1407
1408 // TODO: check cursor mode
1409 if (focused_client == NULL || event->seat_client->client != focused_client) {
1410 sway_log(SWAY_DEBUG, "denying request to set cursor from unfocused client");
1411 return;
1412 }
1413
1414 cursor_set_image(seat->cursor, wlr_cursor_shape_v1_name(event->shape), focused_client);
1415}
diff --git a/sway/input/input-manager.c b/sway/input/input-manager.c
index f04a8ce0..248ca34e 100644
--- a/sway/input/input-manager.c
+++ b/sway/input/input-manager.c
@@ -1,12 +1,12 @@
1#define _POSIX_C_SOURCE 200809L
2#include <ctype.h> 1#include <ctype.h>
3#include <stdio.h> 2#include <stdio.h>
4#include <string.h> 3#include <string.h>
5#include <math.h> 4#include <math.h>
5#include <assert.h>
6#include <wlr/config.h>
6#include <wlr/backend/libinput.h> 7#include <wlr/backend/libinput.h>
7#include <wlr/types/wlr_cursor.h> 8#include <wlr/types/wlr_cursor.h>
8#include <wlr/types/wlr_keyboard_group.h> 9#include <wlr/types/wlr_keyboard_group.h>
9#include <wlr/types/wlr_input_inhibitor.h>
10#include <wlr/types/wlr_virtual_keyboard_v1.h> 10#include <wlr/types/wlr_virtual_keyboard_v1.h>
11#include <wlr/types/wlr_virtual_pointer_v1.h> 11#include <wlr/types/wlr_virtual_pointer_v1.h>
12#include "sway/config.h" 12#include "sway/config.h"
@@ -22,6 +22,10 @@
22#include "list.h" 22#include "list.h"
23#include "log.h" 23#include "log.h"
24 24
25#if WLR_HAS_LIBINPUT_BACKEND
26#include <wlr/backend/libinput.h>
27#endif
28
25#define DEFAULT_SEAT "seat0" 29#define DEFAULT_SEAT "seat0"
26 30
27struct input_config *current_input_config = NULL; 31struct input_config *current_input_config = NULL;
@@ -63,8 +67,15 @@ struct sway_seat *input_manager_sway_seat_from_wlr_seat(struct wlr_seat *wlr_sea
63} 67}
64 68
65char *input_device_get_identifier(struct wlr_input_device *device) { 69char *input_device_get_identifier(struct wlr_input_device *device) {
66 int vendor = device->vendor; 70 int vendor = 0, product = 0;
67 int product = device->product; 71#if WLR_HAS_LIBINPUT_BACKEND
72 if (wlr_input_device_is_libinput(device)) {
73 struct libinput_device *libinput_dev = wlr_libinput_get_device_handle(device);
74 vendor = libinput_device_get_id_vendor(libinput_dev);
75 product = libinput_device_get_id_product(libinput_dev);
76 }
77#endif
78
68 char *name = strdup(device->name ? device->name : ""); 79 char *name = strdup(device->name ? device->name : "");
69 strip_whitespace(name); 80 strip_whitespace(name);
70 81
@@ -76,20 +87,13 @@ char *input_device_get_identifier(struct wlr_input_device *device) {
76 } 87 }
77 } 88 }
78 89
79 const char *fmt = "%d:%d:%s"; 90 char *identifier = format_str("%d:%d:%s", vendor, product, name);
80 int len = snprintf(NULL, 0, fmt, vendor, product, name) + 1;
81 char *identifier = malloc(len);
82 if (!identifier) {
83 sway_log(SWAY_ERROR, "Unable to allocate unique input device name");
84 return NULL;
85 }
86
87 snprintf(identifier, len, fmt, vendor, product, name);
88 free(name); 91 free(name);
89 return identifier; 92 return identifier;
90} 93}
91 94
92static bool device_is_touchpad(struct sway_input_device *device) { 95static bool device_is_touchpad(struct sway_input_device *device) {
96#if WLR_HAS_LIBINPUT_BACKEND
93 if (device->wlr_device->type != WLR_INPUT_DEVICE_POINTER || 97 if (device->wlr_device->type != WLR_INPUT_DEVICE_POINTER ||
94 !wlr_input_device_is_libinput(device->wlr_device)) { 98 !wlr_input_device_is_libinput(device->wlr_device)) {
95 return false; 99 return false;
@@ -99,6 +103,9 @@ static bool device_is_touchpad(struct sway_input_device *device) {
99 wlr_libinput_get_device_handle(device->wlr_device); 103 wlr_libinput_get_device_handle(device->wlr_device);
100 104
101 return libinput_device_config_tap_get_finger_count(libinput_device) > 0; 105 return libinput_device_config_tap_get_finger_count(libinput_device) > 0;
106#else
107 return false;
108#endif
102} 109}
103 110
104const char *input_device_get_type(struct sway_input_device *device) { 111const char *input_device_get_type(struct sway_input_device *device) {
@@ -113,7 +120,7 @@ const char *input_device_get_type(struct sway_input_device *device) {
113 return "keyboard"; 120 return "keyboard";
114 case WLR_INPUT_DEVICE_TOUCH: 121 case WLR_INPUT_DEVICE_TOUCH:
115 return "touch"; 122 return "touch";
116 case WLR_INPUT_DEVICE_TABLET_TOOL: 123 case WLR_INPUT_DEVICE_TABLET:
117 return "tablet_tool"; 124 return "tablet_tool";
118 case WLR_INPUT_DEVICE_TABLET_PAD: 125 case WLR_INPUT_DEVICE_TABLET_PAD:
119 return "tablet_pad"; 126 return "tablet_pad";
@@ -236,7 +243,11 @@ static void handle_new_input(struct wl_listener *listener, void *data) {
236 243
237 apply_input_type_config(input_device); 244 apply_input_type_config(input_device);
238 245
239 sway_input_configure_libinput_device(input_device); 246#if WLR_HAS_LIBINPUT_BACKEND
247 bool config_changed = sway_input_configure_libinput_device(input_device);
248#else
249 bool config_changed = false;
250#endif
240 251
241 wl_signal_add(&device->events.destroy, &input_device->device_destroy); 252 wl_signal_add(&device->events.destroy, &input_device->device_destroy);
242 input_device->device_destroy.notify = handle_device_destroy; 253 input_device->device_destroy.notify = handle_device_destroy;
@@ -274,29 +285,9 @@ static void handle_new_input(struct wl_listener *listener, void *data) {
274 } 285 }
275 286
276 ipc_event_input("added", input_device); 287 ipc_event_input("added", input_device);
277}
278 288
279static void handle_inhibit_activate(struct wl_listener *listener, void *data) { 289 if (config_changed) {
280 struct sway_input_manager *input_manager = wl_container_of( 290 ipc_event_input("libinput_config", input_device);
281 listener, input_manager, inhibit_activate);
282 struct sway_seat *seat;
283 wl_list_for_each(seat, &input_manager->seats, link) {
284 seat_set_exclusive_client(seat, input_manager->inhibit->active_client);
285 }
286}
287
288static void handle_inhibit_deactivate(struct wl_listener *listener, void *data) {
289 struct sway_input_manager *input_manager = wl_container_of(
290 listener, input_manager, inhibit_deactivate);
291 struct sway_seat *seat;
292 wl_list_for_each(seat, &input_manager->seats, link) {
293 seat_set_exclusive_client(seat, NULL);
294 struct sway_node *previous = seat_get_focus(seat);
295 if (previous) {
296 // Hack to get seat to re-focus the return value of get_focus
297 seat_set_focus(seat, NULL);
298 seat_set_focus(seat, previous);
299 }
300 } 291 }
301} 292}
302 293
@@ -377,7 +368,7 @@ void handle_virtual_keyboard(struct wl_listener *listener, void *data) {
377 struct sway_input_manager *input_manager = 368 struct sway_input_manager *input_manager =
378 wl_container_of(listener, input_manager, virtual_keyboard_new); 369 wl_container_of(listener, input_manager, virtual_keyboard_new);
379 struct wlr_virtual_keyboard_v1 *keyboard = data; 370 struct wlr_virtual_keyboard_v1 *keyboard = data;
380 struct wlr_input_device *device = &keyboard->input_device; 371 struct wlr_input_device *device = &keyboard->keyboard.base;
381 372
382 // TODO: Amend protocol to allow NULL seat 373 // TODO: Amend protocol to allow NULL seat
383 struct sway_seat *seat = keyboard->seat ? 374 struct sway_seat *seat = keyboard->seat ?
@@ -410,7 +401,7 @@ void handle_virtual_pointer(struct wl_listener *listener, void *data) {
410 wl_container_of(listener, input_manager, virtual_pointer_new); 401 wl_container_of(listener, input_manager, virtual_pointer_new);
411 struct wlr_virtual_pointer_v1_new_pointer_event *event = data; 402 struct wlr_virtual_pointer_v1_new_pointer_event *event = data;
412 struct wlr_virtual_pointer_v1 *pointer = event->new_pointer; 403 struct wlr_virtual_pointer_v1 *pointer = event->new_pointer;
413 struct wlr_input_device *device = &pointer->input_device; 404 struct wlr_input_device *device = &pointer->pointer.base;
414 405
415 struct sway_seat *seat = event->suggested_seat ? 406 struct sway_seat *seat = event->suggested_seat ?
416 input_manager_sway_seat_from_wlr_seat(event->suggested_seat) : 407 input_manager_sway_seat_from_wlr_seat(event->suggested_seat) :
@@ -442,6 +433,20 @@ void handle_virtual_pointer(struct wl_listener *listener, void *data) {
442 } 433 }
443} 434}
444 435
436static void handle_transient_seat_manager_create_seat(
437 struct wl_listener *listener, void *data) {
438 struct wlr_transient_seat_v1 *transient_seat = data;
439 static uint64_t i;
440 char name[256];
441 snprintf(name, sizeof(name), "transient-%"PRIx64, i++);
442 struct sway_seat *seat = seat_create(name);
443 if (seat && seat->wlr_seat) {
444 wlr_transient_seat_v1_ready(transient_seat, seat->wlr_seat);
445 } else {
446 wlr_transient_seat_v1_deny(transient_seat);
447 }
448}
449
445struct sway_input_manager *input_manager_create(struct sway_server *server) { 450struct sway_input_manager *input_manager_create(struct sway_server *server) {
446 struct sway_input_manager *input = 451 struct sway_input_manager *input =
447 calloc(1, sizeof(struct sway_input_manager)); 452 calloc(1, sizeof(struct sway_input_manager));
@@ -468,14 +473,6 @@ struct sway_input_manager *input_manager_create(struct sway_server *server) {
468 &input->virtual_pointer_new); 473 &input->virtual_pointer_new);
469 input->virtual_pointer_new.notify = handle_virtual_pointer; 474 input->virtual_pointer_new.notify = handle_virtual_pointer;
470 475
471 input->inhibit = wlr_input_inhibit_manager_create(server->wl_display);
472 input->inhibit_activate.notify = handle_inhibit_activate;
473 wl_signal_add(&input->inhibit->events.activate,
474 &input->inhibit_activate);
475 input->inhibit_deactivate.notify = handle_inhibit_deactivate;
476 wl_signal_add(&input->inhibit->events.deactivate,
477 &input->inhibit_deactivate);
478
479 input->keyboard_shortcuts_inhibit = 476 input->keyboard_shortcuts_inhibit =
480 wlr_keyboard_shortcuts_inhibit_v1_create(server->wl_display); 477 wlr_keyboard_shortcuts_inhibit_v1_create(server->wl_display);
481 input->keyboard_shortcuts_inhibit_new_inhibitor.notify = 478 input->keyboard_shortcuts_inhibit_new_inhibitor.notify =
@@ -483,6 +480,17 @@ struct sway_input_manager *input_manager_create(struct sway_server *server) {
483 wl_signal_add(&input->keyboard_shortcuts_inhibit->events.new_inhibitor, 480 wl_signal_add(&input->keyboard_shortcuts_inhibit->events.new_inhibitor,
484 &input->keyboard_shortcuts_inhibit_new_inhibitor); 481 &input->keyboard_shortcuts_inhibit_new_inhibitor);
485 482
483 input->pointer_gestures = wlr_pointer_gestures_v1_create(server->wl_display);
484
485 input->transient_seat_manager =
486 wlr_transient_seat_manager_v1_create(server->wl_display);
487 assert(input->transient_seat_manager);
488
489 input->transient_seat_create.notify =
490 handle_transient_seat_manager_create_seat;
491 wl_signal_add(&input->transient_seat_manager->events.create_seat,
492 &input->transient_seat_create);
493
486 return input; 494 return input;
487} 495}
488 496
@@ -520,21 +528,50 @@ static void retranslate_keysyms(struct input_config *input_config) {
520 return; 528 return;
521 } 529 }
522 } 530 }
531
532 for (int i = 0; i < config->input_type_configs->length; ++i) {
533 struct input_config *ic = config->input_type_configs->items[i];
534 if (ic->xkb_layout || ic->xkb_file) {
535 // this is the first config with xkb_layout or xkb_file
536 if (ic->identifier == input_config->identifier) {
537 translate_keysyms(ic);
538 }
539
540 return;
541 }
542 }
523} 543}
524 544
525static void input_manager_configure_input( 545static void input_manager_configure_input(
526 struct sway_input_device *input_device) { 546 struct sway_input_device *input_device) {
527 sway_input_configure_libinput_device(input_device); 547#if WLR_HAS_LIBINPUT_BACKEND
548 bool config_changed = sway_input_configure_libinput_device(input_device);
549#else
550 bool config_changed = false;
551#endif
528 struct sway_seat *seat = NULL; 552 struct sway_seat *seat = NULL;
529 wl_list_for_each(seat, &server.input->seats, link) { 553 wl_list_for_each(seat, &server.input->seats, link) {
530 seat_configure_device(seat, input_device); 554 seat_configure_device(seat, input_device);
531 } 555 }
556 if (config_changed) {
557 ipc_event_input("libinput_config", input_device);
558 }
532} 559}
533 560
534void input_manager_configure_all_inputs(void) { 561void input_manager_configure_all_input_mappings(void) {
535 struct sway_input_device *input_device = NULL; 562 struct sway_input_device *input_device;
536 wl_list_for_each(input_device, &server.input->devices, link) { 563 wl_list_for_each(input_device, &server.input->devices, link) {
537 input_manager_configure_input(input_device); 564 struct sway_seat *seat;
565 wl_list_for_each(seat, &server.input->seats, link) {
566 seat_configure_device_mapping(seat, input_device);
567 }
568
569#if WLR_HAS_LIBINPUT_BACKEND
570 // Input devices mapped to unavailable outputs get their libinput
571 // send_events setting switched to false. We need to re-enable this
572 // when the output appears.
573 sway_input_configure_libinput_device_send_events(input_device);
574#endif
538 } 575 }
539} 576}
540 577
@@ -556,7 +593,9 @@ void input_manager_apply_input_config(struct input_config *input_config) {
556} 593}
557 594
558void input_manager_reset_input(struct sway_input_device *input_device) { 595void input_manager_reset_input(struct sway_input_device *input_device) {
596#if WLR_HAS_LIBINPUT_BACKEND
559 sway_input_reset_libinput_device(input_device); 597 sway_input_reset_libinput_device(input_device);
598#endif
560 struct sway_seat *seat = NULL; 599 struct sway_seat *seat = NULL;
561 wl_list_for_each(seat, &server.input->seats, link) { 600 wl_list_for_each(seat, &server.input->seats, link) {
562 seat_reset_device(seat, input_device); 601 seat_reset_device(seat, input_device);
@@ -564,6 +603,13 @@ void input_manager_reset_input(struct sway_input_device *input_device) {
564} 603}
565 604
566void input_manager_reset_all_inputs(void) { 605void input_manager_reset_all_inputs(void) {
606 // Set the active keyboard to NULL to avoid spamming configuration updates
607 // for all keyboard devices.
608 struct sway_seat *seat;
609 wl_list_for_each(seat, &server.input->seats, link) {
610 wlr_seat_set_keyboard(seat->wlr_seat, NULL);
611 }
612
567 struct sway_input_device *input_device = NULL; 613 struct sway_input_device *input_device = NULL;
568 wl_list_for_each(input_device, &server.input->devices, link) { 614 wl_list_for_each(input_device, &server.input->devices, link) {
569 input_manager_reset_input(input_device); 615 input_manager_reset_input(input_device);
@@ -572,7 +618,6 @@ void input_manager_reset_all_inputs(void) {
572 // If there is at least one keyboard using the default keymap, repeat delay, 618 // If there is at least one keyboard using the default keymap, repeat delay,
573 // and repeat rate, then it is possible that there is a keyboard group that 619 // and repeat rate, then it is possible that there is a keyboard group that
574 // need their keyboard disarmed. 620 // need their keyboard disarmed.
575 struct sway_seat *seat;
576 wl_list_for_each(seat, &server.input->seats, link) { 621 wl_list_for_each(seat, &server.input->seats, link) {
577 struct sway_keyboard_group *group; 622 struct sway_keyboard_group *group;
578 wl_list_for_each(group, &seat->keyboard_groups, link) { 623 wl_list_for_each(group, &seat->keyboard_groups, link) {
diff --git a/sway/input/keyboard.c b/sway/input/keyboard.c
index f258ac7d..f74d0658 100644
--- a/sway/input/keyboard.c
+++ b/sway/input/keyboard.c
@@ -1,10 +1,9 @@
1#include <assert.h> 1#include <assert.h>
2#include <limits.h> 2#include <limits.h>
3#include <strings.h> 3#include <strings.h>
4#include <wlr/config.h>
4#include <wlr/backend/multi.h> 5#include <wlr/backend/multi.h>
5#include <wlr/backend/session.h>
6#include <wlr/interfaces/wlr_keyboard.h> 6#include <wlr/interfaces/wlr_keyboard.h>
7#include <wlr/types/wlr_idle.h>
8#include <wlr/types/wlr_keyboard.h> 7#include <wlr/types/wlr_keyboard.h>
9#include <wlr/types/wlr_keyboard_group.h> 8#include <wlr/types/wlr_keyboard_group.h>
10#include <xkbcommon/xkbcommon-names.h> 9#include <xkbcommon/xkbcommon-names.h>
@@ -16,6 +15,10 @@
16#include "sway/ipc-server.h" 15#include "sway/ipc-server.h"
17#include "log.h" 16#include "log.h"
18 17
18#if WLR_HAS_SESSION
19#include <wlr/backend/session.h>
20#endif
21
19static struct modifier_key { 22static struct modifier_key {
20 char *name; 23 char *name;
21 uint32_t mod; 24 uint32_t mod;
@@ -29,6 +32,7 @@ static struct modifier_key {
29 { XKB_MOD_NAME_NUM, WLR_MODIFIER_MOD2 }, 32 { XKB_MOD_NAME_NUM, WLR_MODIFIER_MOD2 },
30 { "Mod3", WLR_MODIFIER_MOD3 }, 33 { "Mod3", WLR_MODIFIER_MOD3 },
31 { XKB_MOD_NAME_LOGO, WLR_MODIFIER_LOGO }, 34 { XKB_MOD_NAME_LOGO, WLR_MODIFIER_LOGO },
35 { "Super", WLR_MODIFIER_LOGO },
32 { "Mod5", WLR_MODIFIER_MOD5 }, 36 { "Mod5", WLR_MODIFIER_MOD5 },
33}; 37};
34 38
@@ -264,14 +268,12 @@ static bool keyboard_execute_compositor_binding(struct sway_keyboard *keyboard,
264 xkb_keysym_t keysym = pressed_keysyms[i]; 268 xkb_keysym_t keysym = pressed_keysyms[i];
265 if (keysym >= XKB_KEY_XF86Switch_VT_1 && 269 if (keysym >= XKB_KEY_XF86Switch_VT_1 &&
266 keysym <= XKB_KEY_XF86Switch_VT_12) { 270 keysym <= XKB_KEY_XF86Switch_VT_12) {
267 if (wlr_backend_is_multi(server.backend)) { 271#if WLR_HAS_SESSION
268 struct wlr_session *session = 272 if (server.session) {
269 wlr_backend_get_session(server.backend); 273 unsigned vt = keysym - XKB_KEY_XF86Switch_VT_1 + 1;
270 if (session) { 274 wlr_session_change_vt(server.session, vt);
271 unsigned vt = keysym - XKB_KEY_XF86Switch_VT_1 + 1;
272 wlr_session_change_vt(session, vt);
273 }
274 } 275 }
276#endif
275 return true; 277 return true;
276 } 278 }
277 } 279 }
@@ -291,14 +293,12 @@ static bool keyboard_execute_compositor_binding(struct sway_keyboard *keyboard,
291static size_t keyboard_keysyms_translated(struct sway_keyboard *keyboard, 293static size_t keyboard_keysyms_translated(struct sway_keyboard *keyboard,
292 xkb_keycode_t keycode, const xkb_keysym_t **keysyms, 294 xkb_keycode_t keycode, const xkb_keysym_t **keysyms,
293 uint32_t *modifiers) { 295 uint32_t *modifiers) {
294 struct wlr_input_device *device = 296 *modifiers = wlr_keyboard_get_modifiers(keyboard->wlr);
295 keyboard->seat_device->input_device->wlr_device;
296 *modifiers = wlr_keyboard_get_modifiers(device->keyboard);
297 xkb_mod_mask_t consumed = xkb_state_key_get_consumed_mods2( 297 xkb_mod_mask_t consumed = xkb_state_key_get_consumed_mods2(
298 device->keyboard->xkb_state, keycode, XKB_CONSUMED_MODE_XKB); 298 keyboard->wlr->xkb_state, keycode, XKB_CONSUMED_MODE_XKB);
299 *modifiers = *modifiers & ~consumed; 299 *modifiers = *modifiers & ~consumed;
300 300
301 return xkb_state_key_get_syms(device->keyboard->xkb_state, 301 return xkb_state_key_get_syms(keyboard->wlr->xkb_state,
302 keycode, keysyms); 302 keycode, keysyms);
303} 303}
304 304
@@ -314,13 +314,11 @@ static size_t keyboard_keysyms_translated(struct sway_keyboard *keyboard,
314static size_t keyboard_keysyms_raw(struct sway_keyboard *keyboard, 314static size_t keyboard_keysyms_raw(struct sway_keyboard *keyboard,
315 xkb_keycode_t keycode, const xkb_keysym_t **keysyms, 315 xkb_keycode_t keycode, const xkb_keysym_t **keysyms,
316 uint32_t *modifiers) { 316 uint32_t *modifiers) {
317 struct wlr_input_device *device = 317 *modifiers = wlr_keyboard_get_modifiers(keyboard->wlr);
318 keyboard->seat_device->input_device->wlr_device;
319 *modifiers = wlr_keyboard_get_modifiers(device->keyboard);
320 318
321 xkb_layout_index_t layout_index = xkb_state_key_get_layout( 319 xkb_layout_index_t layout_index = xkb_state_key_get_layout(
322 device->keyboard->xkb_state, keycode); 320 keyboard->wlr->xkb_state, keycode);
323 return xkb_keymap_key_get_syms_by_level(device->keyboard->keymap, 321 return xkb_keymap_key_get_syms_by_level(keyboard->wlr->keymap,
324 keycode, layout_index, 0, keysyms); 322 keycode, layout_index, 0, keysyms);
325} 323}
326 324
@@ -360,8 +358,7 @@ static void update_keyboard_state(struct sway_keyboard *keyboard,
360 keyinfo->keycode, &keyinfo->translated_keysyms, 358 keyinfo->keycode, &keyinfo->translated_keysyms,
361 &keyinfo->translated_modifiers); 359 &keyinfo->translated_modifiers);
362 360
363 keyinfo->code_modifiers = wlr_keyboard_get_modifiers( 361 keyinfo->code_modifiers = wlr_keyboard_get_modifiers(keyboard->wlr);
364 keyboard->seat_device->input_device->wlr_device->keyboard);
365 362
366 // Update shortcut model keyinfo 363 // Update shortcut model keyinfo
367 update_shortcut_state(&keyboard->state_keycodes, raw_keycode, keystate, 364 update_shortcut_state(&keyboard->state_keycodes, raw_keycode, keystate,
@@ -401,15 +398,15 @@ static struct wlr_input_method_keyboard_grab_v2 *keyboard_get_im_grab(
401} 398}
402 399
403static void handle_key_event(struct sway_keyboard *keyboard, 400static void handle_key_event(struct sway_keyboard *keyboard,
404 struct wlr_event_keyboard_key *event) { 401 struct wlr_keyboard_key_event *event) {
405 struct sway_seat *seat = keyboard->seat_device->sway_seat; 402 struct sway_seat *seat = keyboard->seat_device->sway_seat;
406 struct wlr_seat *wlr_seat = seat->wlr_seat; 403 struct wlr_seat *wlr_seat = seat->wlr_seat;
407 struct wlr_input_device *wlr_device = 404 struct wlr_input_device *wlr_device =
408 keyboard->seat_device->input_device->wlr_device; 405 keyboard->seat_device->input_device->wlr_device;
409 char *device_identifier = input_device_get_identifier(wlr_device); 406 char *device_identifier = input_device_get_identifier(wlr_device);
410 bool exact_identifier = wlr_device->keyboard->group != NULL; 407 bool exact_identifier = keyboard->wlr->group != NULL;
411 seat_idle_notify_activity(seat, IDLE_SOURCE_KEYBOARD); 408 seat_idle_notify_activity(seat, IDLE_SOURCE_KEYBOARD);
412 bool input_inhibited = seat->exclusive_client != NULL; 409 bool locked = server.session_lock.lock;
413 struct sway_keyboard_shortcuts_inhibitor *sway_inhibitor = 410 struct sway_keyboard_shortcuts_inhibitor *sway_inhibitor =
414 keyboard_shortcuts_inhibitor_get_for_focused_surface(seat); 411 keyboard_shortcuts_inhibitor_get_for_focused_surface(seat);
415 bool shortcuts_inhibited = sway_inhibitor && sway_inhibitor->inhibitor->active; 412 bool shortcuts_inhibited = sway_inhibitor && sway_inhibitor->inhibitor->active;
@@ -427,17 +424,17 @@ static void handle_key_event(struct sway_keyboard *keyboard,
427 struct sway_binding *binding_released = NULL; 424 struct sway_binding *binding_released = NULL;
428 get_active_binding(&keyboard->state_keycodes, 425 get_active_binding(&keyboard->state_keycodes,
429 config->current_mode->keycode_bindings, &binding_released, 426 config->current_mode->keycode_bindings, &binding_released,
430 keyinfo.code_modifiers, true, input_inhibited, 427 keyinfo.code_modifiers, true, locked,
431 shortcuts_inhibited, device_identifier, 428 shortcuts_inhibited, device_identifier,
432 exact_identifier, keyboard->effective_layout); 429 exact_identifier, keyboard->effective_layout);
433 get_active_binding(&keyboard->state_keysyms_raw, 430 get_active_binding(&keyboard->state_keysyms_raw,
434 config->current_mode->keysym_bindings, &binding_released, 431 config->current_mode->keysym_bindings, &binding_released,
435 keyinfo.raw_modifiers, true, input_inhibited, 432 keyinfo.raw_modifiers, true, locked,
436 shortcuts_inhibited, device_identifier, 433 shortcuts_inhibited, device_identifier,
437 exact_identifier, keyboard->effective_layout); 434 exact_identifier, keyboard->effective_layout);
438 get_active_binding(&keyboard->state_keysyms_translated, 435 get_active_binding(&keyboard->state_keysyms_translated,
439 config->current_mode->keysym_bindings, &binding_released, 436 config->current_mode->keysym_bindings, &binding_released,
440 keyinfo.translated_modifiers, true, input_inhibited, 437 keyinfo.translated_modifiers, true, locked,
441 shortcuts_inhibited, device_identifier, 438 shortcuts_inhibited, device_identifier,
442 exact_identifier, keyboard->effective_layout); 439 exact_identifier, keyboard->effective_layout);
443 440
@@ -459,17 +456,17 @@ static void handle_key_event(struct sway_keyboard *keyboard,
459 if (event->state == WL_KEYBOARD_KEY_STATE_PRESSED) { 456 if (event->state == WL_KEYBOARD_KEY_STATE_PRESSED) {
460 get_active_binding(&keyboard->state_keycodes, 457 get_active_binding(&keyboard->state_keycodes,
461 config->current_mode->keycode_bindings, &binding, 458 config->current_mode->keycode_bindings, &binding,
462 keyinfo.code_modifiers, false, input_inhibited, 459 keyinfo.code_modifiers, false, locked,
463 shortcuts_inhibited, device_identifier, 460 shortcuts_inhibited, device_identifier,
464 exact_identifier, keyboard->effective_layout); 461 exact_identifier, keyboard->effective_layout);
465 get_active_binding(&keyboard->state_keysyms_raw, 462 get_active_binding(&keyboard->state_keysyms_raw,
466 config->current_mode->keysym_bindings, &binding, 463 config->current_mode->keysym_bindings, &binding,
467 keyinfo.raw_modifiers, false, input_inhibited, 464 keyinfo.raw_modifiers, false, locked,
468 shortcuts_inhibited, device_identifier, 465 shortcuts_inhibited, device_identifier,
469 exact_identifier, keyboard->effective_layout); 466 exact_identifier, keyboard->effective_layout);
470 get_active_binding(&keyboard->state_keysyms_translated, 467 get_active_binding(&keyboard->state_keysyms_translated,
471 config->current_mode->keysym_bindings, &binding, 468 config->current_mode->keysym_bindings, &binding,
472 keyinfo.translated_modifiers, false, input_inhibited, 469 keyinfo.translated_modifiers, false, locked,
473 shortcuts_inhibited, device_identifier, 470 shortcuts_inhibited, device_identifier,
474 exact_identifier, keyboard->effective_layout); 471 exact_identifier, keyboard->effective_layout);
475 } 472 }
@@ -477,10 +474,10 @@ static void handle_key_event(struct sway_keyboard *keyboard,
477 // Set up (or clear) keyboard repeat for a pressed binding. Since the 474 // Set up (or clear) keyboard repeat for a pressed binding. Since the
478 // binding may remove the keyboard, the timer needs to be updated first 475 // binding may remove the keyboard, the timer needs to be updated first
479 if (binding && !(binding->flags & BINDING_NOREPEAT) && 476 if (binding && !(binding->flags & BINDING_NOREPEAT) &&
480 wlr_device->keyboard->repeat_info.delay > 0) { 477 keyboard->wlr->repeat_info.delay > 0) {
481 keyboard->repeat_binding = binding; 478 keyboard->repeat_binding = binding;
482 if (wl_event_source_timer_update(keyboard->key_repeat_source, 479 if (wl_event_source_timer_update(keyboard->key_repeat_source,
483 wlr_device->keyboard->repeat_info.delay) < 0) { 480 keyboard->wlr->repeat_info.delay) < 0) {
484 sway_log(SWAY_DEBUG, "failed to set key repeat timer"); 481 sway_log(SWAY_DEBUG, "failed to set key repeat timer");
485 } 482 }
486 } else if (keyboard->repeat_binding) { 483 } else if (keyboard->repeat_binding) {
@@ -492,7 +489,7 @@ static void handle_key_event(struct sway_keyboard *keyboard,
492 handled = true; 489 handled = true;
493 } 490 }
494 491
495 if (!handled && wlr_device->keyboard->group) { 492 if (!handled && keyboard->wlr->group) {
496 // Only handle device specific bindings for keyboards in a group 493 // Only handle device specific bindings for keyboards in a group
497 free(device_identifier); 494 free(device_identifier);
498 return; 495 return;
@@ -517,7 +514,7 @@ static void handle_key_event(struct sway_keyboard *keyboard,
517 &keyboard->state_pressed_sent, event->keycode, 514 &keyboard->state_pressed_sent, event->keycode,
518 event->state, keyinfo.keycode, 0); 515 event->state, keyinfo.keycode, 0);
519 if (pressed_sent) { 516 if (pressed_sent) {
520 wlr_seat_set_keyboard(wlr_seat, wlr_device); 517 wlr_seat_set_keyboard(wlr_seat, keyboard->wlr);
521 wlr_seat_keyboard_notify_key(wlr_seat, event->time_msec, 518 wlr_seat_keyboard_notify_key(wlr_seat, event->time_msec,
522 event->keycode, event->state); 519 event->keycode, event->state);
523 handled = true; 520 handled = true;
@@ -528,8 +525,7 @@ static void handle_key_event(struct sway_keyboard *keyboard,
528 struct wlr_input_method_keyboard_grab_v2 *kb_grab = keyboard_get_im_grab(keyboard); 525 struct wlr_input_method_keyboard_grab_v2 *kb_grab = keyboard_get_im_grab(keyboard);
529 526
530 if (kb_grab) { 527 if (kb_grab) {
531 wlr_input_method_keyboard_grab_v2_set_keyboard(kb_grab, 528 wlr_input_method_keyboard_grab_v2_set_keyboard(kb_grab, keyboard->wlr);
532 wlr_device->keyboard);
533 wlr_input_method_keyboard_grab_v2_send_key(kb_grab, 529 wlr_input_method_keyboard_grab_v2_send_key(kb_grab,
534 event->time_msec, event->keycode, event->state); 530 event->time_msec, event->keycode, event->state);
535 handled = true; 531 handled = true;
@@ -542,7 +538,7 @@ static void handle_key_event(struct sway_keyboard *keyboard,
542 update_shortcut_state( 538 update_shortcut_state(
543 &keyboard->state_pressed_sent, event->keycode, event->state, 539 &keyboard->state_pressed_sent, event->keycode, event->state,
544 keyinfo.keycode, 0); 540 keyinfo.keycode, 0);
545 wlr_seat_set_keyboard(wlr_seat, wlr_device); 541 wlr_seat_set_keyboard(wlr_seat, keyboard->wlr);
546 wlr_seat_keyboard_notify_key(wlr_seat, event->time_msec, 542 wlr_seat_keyboard_notify_key(wlr_seat, event->time_msec,
547 event->keycode, event->state); 543 event->keycode, event->state);
548 } 544 }
@@ -618,14 +614,12 @@ static void handle_keyboard_group_leave(struct wl_listener *listener,
618} 614}
619 615
620static int handle_keyboard_repeat(void *data) { 616static int handle_keyboard_repeat(void *data) {
621 struct sway_keyboard *keyboard = (struct sway_keyboard *)data; 617 struct sway_keyboard *keyboard = data;
622 struct wlr_keyboard *wlr_device =
623 keyboard->seat_device->input_device->wlr_device->keyboard;
624 if (keyboard->repeat_binding) { 618 if (keyboard->repeat_binding) {
625 if (wlr_device->repeat_info.rate > 0) { 619 if (keyboard->wlr->repeat_info.rate > 0) {
626 // We queue the next event first, as the command might cancel it 620 // We queue the next event first, as the command might cancel it
627 if (wl_event_source_timer_update(keyboard->key_repeat_source, 621 if (wl_event_source_timer_update(keyboard->key_repeat_source,
628 1000 / wlr_device->repeat_info.rate) < 0) { 622 1000 / keyboard->wlr->repeat_info.rate) < 0) {
629 sway_log(SWAY_DEBUG, "failed to update key repeat timer"); 623 sway_log(SWAY_DEBUG, "failed to update key repeat timer");
630 } 624 }
631 } 625 }
@@ -658,31 +652,28 @@ static void determine_bar_visibility(uint32_t modifiers) {
658} 652}
659 653
660static void handle_modifier_event(struct sway_keyboard *keyboard) { 654static void handle_modifier_event(struct sway_keyboard *keyboard) {
661 struct wlr_input_device *wlr_device = 655 if (!keyboard->wlr->group) {
662 keyboard->seat_device->input_device->wlr_device;
663 if (!wlr_device->keyboard->group) {
664 struct wlr_input_method_keyboard_grab_v2 *kb_grab = keyboard_get_im_grab(keyboard); 656 struct wlr_input_method_keyboard_grab_v2 *kb_grab = keyboard_get_im_grab(keyboard);
665 657
666 if (kb_grab) { 658 if (kb_grab) {
667 wlr_input_method_keyboard_grab_v2_set_keyboard(kb_grab, 659 wlr_input_method_keyboard_grab_v2_set_keyboard(kb_grab, keyboard->wlr);
668 wlr_device->keyboard);
669 wlr_input_method_keyboard_grab_v2_send_modifiers(kb_grab, 660 wlr_input_method_keyboard_grab_v2_send_modifiers(kb_grab,
670 &wlr_device->keyboard->modifiers); 661 &keyboard->wlr->modifiers);
671 } else { 662 } else {
672 struct wlr_seat *wlr_seat = keyboard->seat_device->sway_seat->wlr_seat; 663 struct wlr_seat *wlr_seat = keyboard->seat_device->sway_seat->wlr_seat;
673 wlr_seat_set_keyboard(wlr_seat, wlr_device); 664 wlr_seat_set_keyboard(wlr_seat, keyboard->wlr);
674 wlr_seat_keyboard_notify_modifiers(wlr_seat, 665 wlr_seat_keyboard_notify_modifiers(wlr_seat,
675 &wlr_device->keyboard->modifiers); 666 &keyboard->wlr->modifiers);
676 } 667 }
677 668
678 uint32_t modifiers = wlr_keyboard_get_modifiers(wlr_device->keyboard); 669 uint32_t modifiers = wlr_keyboard_get_modifiers(keyboard->wlr);
679 determine_bar_visibility(modifiers); 670 determine_bar_visibility(modifiers);
680 } 671 }
681 672
682 if (wlr_device->keyboard->modifiers.group != keyboard->effective_layout) { 673 if (keyboard->wlr->modifiers.group != keyboard->effective_layout) {
683 keyboard->effective_layout = wlr_device->keyboard->modifiers.group; 674 keyboard->effective_layout = keyboard->wlr->modifiers.group;
684 675
685 if (!wlr_keyboard_group_from_wlr_keyboard(wlr_device->keyboard)) { 676 if (!wlr_keyboard_group_from_wlr_keyboard(keyboard->wlr)) {
686 ipc_event_input("xkb_layout", keyboard->seat_device->input_device); 677 ipc_event_input("xkb_layout", keyboard->seat_device->input_device);
687 } 678 }
688 } 679 }
@@ -711,6 +702,7 @@ struct sway_keyboard *sway_keyboard_create(struct sway_seat *seat,
711 } 702 }
712 703
713 keyboard->seat_device = device; 704 keyboard->seat_device = device;
705 keyboard->wlr = wlr_keyboard_from_input_device(device->input_device->wlr_device);
714 device->keyboard = keyboard; 706 device->keyboard = keyboard;
715 707
716 wl_list_init(&keyboard->keyboard_key.link); 708 wl_list_init(&keyboard->keyboard_key.link);
@@ -724,23 +716,11 @@ struct sway_keyboard *sway_keyboard_create(struct sway_seat *seat,
724 716
725static void handle_xkb_context_log(struct xkb_context *context, 717static void handle_xkb_context_log(struct xkb_context *context,
726 enum xkb_log_level level, const char *format, va_list args) { 718 enum xkb_log_level level, const char *format, va_list args) {
727 va_list args_copy; 719 char *error = vformat_str(format, args);
728 va_copy(args_copy, args);
729 size_t length = vsnprintf(NULL, 0, format, args_copy) + 1;
730 va_end(args_copy);
731
732 char *error = malloc(length);
733 if (!error) {
734 sway_log(SWAY_ERROR, "Failed to allocate libxkbcommon log message");
735 return;
736 }
737
738 va_copy(args_copy, args);
739 vsnprintf(error, length, format, args_copy);
740 va_end(args_copy);
741 720
742 if (error[length - 2] == '\n') { 721 size_t len = strlen(error);
743 error[length - 2] = '\0'; 722 if (error[len - 1] == '\n') {
723 error[len - 1] = '\0';
744 } 724 }
745 725
746 sway_log_importance_t importance = SWAY_DEBUG; 726 sway_log_importance_t importance = SWAY_DEBUG;
@@ -761,7 +741,7 @@ static void handle_xkb_context_log(struct xkb_context *context,
761 741
762struct xkb_keymap *sway_keyboard_compile_keymap(struct input_config *ic, 742struct xkb_keymap *sway_keyboard_compile_keymap(struct input_config *ic,
763 char **error) { 743 char **error) {
764 struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); 744 struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_SECURE_GETENV);
765 if (!sway_assert(context, "cannot create XKB context")) { 745 if (!sway_assert(context, "cannot create XKB context")) {
766 return NULL; 746 return NULL;
767 } 747 }
@@ -775,13 +755,8 @@ struct xkb_keymap *sway_keyboard_compile_keymap(struct input_config *ic,
775 if (!keymap_file) { 755 if (!keymap_file) {
776 sway_log_errno(SWAY_ERROR, "cannot read xkb file %s", ic->xkb_file); 756 sway_log_errno(SWAY_ERROR, "cannot read xkb file %s", ic->xkb_file);
777 if (error) { 757 if (error) {
778 size_t len = snprintf(NULL, 0, "cannot read xkb file %s: %s", 758 *error = format_str("cannot read xkb file %s: %s",
779 ic->xkb_file, strerror(errno)) + 1; 759 ic->xkb_file, strerror(errno));
780 *error = malloc(len);
781 if (*error) {
782 snprintf(*error, len, "cannot read xkb_file %s: %s",
783 ic->xkb_file, strerror(errno));
784 }
785 } 760 }
786 goto cleanup; 761 goto cleanup;
787 } 762 }
@@ -819,13 +794,12 @@ static void destroy_empty_wlr_keyboard_group(void *data) {
819 794
820static void sway_keyboard_group_remove(struct sway_keyboard *keyboard) { 795static void sway_keyboard_group_remove(struct sway_keyboard *keyboard) {
821 struct sway_input_device *device = keyboard->seat_device->input_device; 796 struct sway_input_device *device = keyboard->seat_device->input_device;
822 struct wlr_keyboard *wlr_keyboard = device->wlr_device->keyboard; 797 struct wlr_keyboard_group *wlr_group = keyboard->wlr->group;
823 struct wlr_keyboard_group *wlr_group = wlr_keyboard->group;
824 798
825 sway_log(SWAY_DEBUG, "Removing keyboard %s from group %p", 799 sway_log(SWAY_DEBUG, "Removing keyboard %s from group %p",
826 device->identifier, wlr_group); 800 device->identifier, wlr_group);
827 801
828 wlr_keyboard_group_remove_keyboard(wlr_keyboard->group, wlr_keyboard); 802 wlr_keyboard_group_remove_keyboard(keyboard->wlr->group, keyboard->wlr);
829 803
830 if (wl_list_empty(&wlr_group->devices)) { 804 if (wl_list_empty(&wlr_group->devices)) {
831 sway_log(SWAY_DEBUG, "Destroying empty keyboard group %p", 805 sway_log(SWAY_DEBUG, "Destroying empty keyboard group %p",
@@ -850,9 +824,7 @@ static void sway_keyboard_group_remove(struct sway_keyboard *keyboard) {
850} 824}
851 825
852static void sway_keyboard_group_remove_invalid(struct sway_keyboard *keyboard) { 826static void sway_keyboard_group_remove_invalid(struct sway_keyboard *keyboard) {
853 struct sway_input_device *device = keyboard->seat_device->input_device; 827 if (!keyboard->wlr->group) {
854 struct wlr_keyboard *wlr_keyboard = device->wlr_device->keyboard;
855 if (!wlr_keyboard->group) {
856 return; 828 return;
857 } 829 }
858 830
@@ -868,7 +840,7 @@ static void sway_keyboard_group_remove_invalid(struct sway_keyboard *keyboard) {
868 break; 840 break;
869 case KEYBOARD_GROUP_DEFAULT: /* fallthrough */ 841 case KEYBOARD_GROUP_DEFAULT: /* fallthrough */
870 case KEYBOARD_GROUP_SMART:; 842 case KEYBOARD_GROUP_SMART:;
871 struct wlr_keyboard_group *group = wlr_keyboard->group; 843 struct wlr_keyboard_group *group = keyboard->wlr->group;
872 if (!wlr_keyboard_keymaps_match(keyboard->keymap, group->keyboard.keymap) || 844 if (!wlr_keyboard_keymaps_match(keyboard->keymap, group->keyboard.keymap) ||
873 !repeat_info_match(keyboard, &group->keyboard)) { 845 !repeat_info_match(keyboard, &group->keyboard)) {
874 sway_keyboard_group_remove(keyboard); 846 sway_keyboard_group_remove(keyboard);
@@ -879,7 +851,6 @@ static void sway_keyboard_group_remove_invalid(struct sway_keyboard *keyboard) {
879 851
880static void sway_keyboard_group_add(struct sway_keyboard *keyboard) { 852static void sway_keyboard_group_add(struct sway_keyboard *keyboard) {
881 struct sway_input_device *device = keyboard->seat_device->input_device; 853 struct sway_input_device *device = keyboard->seat_device->input_device;
882 struct wlr_keyboard *wlr_keyboard = device->wlr_device->keyboard;
883 struct sway_seat *seat = keyboard->seat_device->sway_seat; 854 struct sway_seat *seat = keyboard->seat_device->sway_seat;
884 struct seat_config *sc = seat_get_config(seat); 855 struct seat_config *sc = seat_get_config(seat);
885 856
@@ -911,7 +882,7 @@ static void sway_keyboard_group_add(struct sway_keyboard *keyboard) {
911 repeat_info_match(keyboard, &wlr_group->keyboard)) { 882 repeat_info_match(keyboard, &wlr_group->keyboard)) {
912 sway_log(SWAY_DEBUG, "Adding keyboard %s to group %p", 883 sway_log(SWAY_DEBUG, "Adding keyboard %s to group %p",
913 device->identifier, wlr_group); 884 device->identifier, wlr_group);
914 wlr_keyboard_group_add_keyboard(wlr_group, wlr_keyboard); 885 wlr_keyboard_group_add_keyboard(wlr_group, keyboard->wlr);
915 return; 886 return;
916 } 887 }
917 break; 888 break;
@@ -950,7 +921,7 @@ static void sway_keyboard_group_add(struct sway_keyboard *keyboard) {
950 goto cleanup; 921 goto cleanup;
951 } 922 }
952 sway_group->seat_device->input_device->wlr_device = 923 sway_group->seat_device->input_device->wlr_device =
953 sway_group->wlr_group->input_device; 924 &sway_group->wlr_group->keyboard.base;
954 925
955 if (!sway_keyboard_create(seat, sway_group->seat_device)) { 926 if (!sway_keyboard_create(seat, sway_group->seat_device)) {
956 sway_log(SWAY_ERROR, "Failed to allocate sway_keyboard for group"); 927 sway_log(SWAY_ERROR, "Failed to allocate sway_keyboard for group");
@@ -959,7 +930,7 @@ static void sway_keyboard_group_add(struct sway_keyboard *keyboard) {
959 930
960 sway_log(SWAY_DEBUG, "Adding keyboard %s to group %p", 931 sway_log(SWAY_DEBUG, "Adding keyboard %s to group %p",
961 device->identifier, sway_group->wlr_group); 932 device->identifier, sway_group->wlr_group);
962 wlr_keyboard_group_add_keyboard(sway_group->wlr_group, wlr_keyboard); 933 wlr_keyboard_group_add_keyboard(sway_group->wlr_group, keyboard->wlr);
963 934
964 wl_list_insert(&seat->keyboard_groups, &sway_group->link); 935 wl_list_insert(&seat->keyboard_groups, &sway_group->link);
965 936
@@ -991,10 +962,8 @@ cleanup:
991void sway_keyboard_configure(struct sway_keyboard *keyboard) { 962void sway_keyboard_configure(struct sway_keyboard *keyboard) {
992 struct input_config *input_config = 963 struct input_config *input_config =
993 input_device_get_config(keyboard->seat_device->input_device); 964 input_device_get_config(keyboard->seat_device->input_device);
994 struct wlr_input_device *wlr_device =
995 keyboard->seat_device->input_device->wlr_device;
996 965
997 if (!sway_assert(!wlr_keyboard_group_from_wlr_keyboard(wlr_device->keyboard), 966 if (!sway_assert(!wlr_keyboard_group_from_wlr_keyboard(keyboard->wlr),
998 "sway_keyboard_configure should not be called with a " 967 "sway_keyboard_configure should not be called with a "
999 "keyboard group's keyboard")) { 968 "keyboard group's keyboard")) {
1000 return; 969 return;
@@ -1036,11 +1005,11 @@ void sway_keyboard_configure(struct sway_keyboard *keyboard) {
1036 1005
1037 sway_keyboard_group_remove_invalid(keyboard); 1006 sway_keyboard_group_remove_invalid(keyboard);
1038 1007
1039 wlr_keyboard_set_keymap(wlr_device->keyboard, keyboard->keymap); 1008 wlr_keyboard_set_keymap(keyboard->wlr, keyboard->keymap);
1040 wlr_keyboard_set_repeat_info(wlr_device->keyboard, 1009 wlr_keyboard_set_repeat_info(keyboard->wlr,
1041 keyboard->repeat_rate, keyboard->repeat_delay); 1010 keyboard->repeat_rate, keyboard->repeat_delay);
1042 1011
1043 if (!wlr_device->keyboard->group) { 1012 if (!keyboard->wlr->group) {
1044 sway_keyboard_group_add(keyboard); 1013 sway_keyboard_group_add(keyboard);
1045 } 1014 }
1046 1015
@@ -1060,40 +1029,42 @@ void sway_keyboard_configure(struct sway_keyboard *keyboard) {
1060 } 1029 }
1061 } 1030 }
1062 if (locked_mods) { 1031 if (locked_mods) {
1063 wlr_keyboard_notify_modifiers(wlr_device->keyboard, 0, 0, 1032 wlr_keyboard_notify_modifiers(keyboard->wlr, 0, 0,
1064 locked_mods, 0); 1033 locked_mods, 0);
1065 uint32_t leds = 0; 1034 uint32_t leds = 0;
1066 for (uint32_t i = 0; i < WLR_LED_COUNT; ++i) { 1035 for (uint32_t i = 0; i < WLR_LED_COUNT; ++i) {
1067 if (xkb_state_led_index_is_active( 1036 if (xkb_state_led_index_is_active(keyboard->wlr->xkb_state,
1068 wlr_device->keyboard->xkb_state, 1037 keyboard->wlr->led_indexes[i])) {
1069 wlr_device->keyboard->led_indexes[i])) {
1070 leds |= (1 << i); 1038 leds |= (1 << i);
1071 } 1039 }
1072 } 1040 }
1073 if (wlr_device->keyboard->group) { 1041 if (keyboard->wlr->group) {
1074 wlr_keyboard_led_update( 1042 wlr_keyboard_led_update(&keyboard->wlr->group->keyboard, leds);
1075 &wlr_device->keyboard->group->keyboard, leds);
1076 } else { 1043 } else {
1077 wlr_keyboard_led_update(wlr_device->keyboard, leds); 1044 wlr_keyboard_led_update(keyboard->wlr, leds);
1078 } 1045 }
1079 } 1046 }
1080 } else { 1047 } else {
1081 xkb_keymap_unref(keymap); 1048 xkb_keymap_unref(keymap);
1082 sway_keyboard_group_remove_invalid(keyboard); 1049 sway_keyboard_group_remove_invalid(keyboard);
1083 if (!wlr_device->keyboard->group) { 1050 if (!keyboard->wlr->group) {
1084 sway_keyboard_group_add(keyboard); 1051 sway_keyboard_group_add(keyboard);
1085 } 1052 }
1086 } 1053 }
1087 1054
1055 // If the seat has no active keyboard, set this one
1088 struct wlr_seat *seat = keyboard->seat_device->sway_seat->wlr_seat; 1056 struct wlr_seat *seat = keyboard->seat_device->sway_seat->wlr_seat;
1089 wlr_seat_set_keyboard(seat, wlr_device); 1057 struct wlr_keyboard *current_keyboard = seat->keyboard_state.keyboard;
1058 if (current_keyboard == NULL) {
1059 wlr_seat_set_keyboard(seat, keyboard->wlr);
1060 }
1090 1061
1091 wl_list_remove(&keyboard->keyboard_key.link); 1062 wl_list_remove(&keyboard->keyboard_key.link);
1092 wl_signal_add(&wlr_device->keyboard->events.key, &keyboard->keyboard_key); 1063 wl_signal_add(&keyboard->wlr->events.key, &keyboard->keyboard_key);
1093 keyboard->keyboard_key.notify = handle_keyboard_key; 1064 keyboard->keyboard_key.notify = handle_keyboard_key;
1094 1065
1095 wl_list_remove(&keyboard->keyboard_modifiers.link); 1066 wl_list_remove(&keyboard->keyboard_modifiers.link);
1096 wl_signal_add(&wlr_device->keyboard->events.modifiers, 1067 wl_signal_add(&keyboard->wlr->events.modifiers,
1097 &keyboard->keyboard_modifiers); 1068 &keyboard->keyboard_modifiers);
1098 keyboard->keyboard_modifiers.notify = handle_keyboard_modifiers; 1069 keyboard->keyboard_modifiers.notify = handle_keyboard_modifiers;
1099 1070
@@ -1110,12 +1081,11 @@ void sway_keyboard_destroy(struct sway_keyboard *keyboard) {
1110 if (!keyboard) { 1081 if (!keyboard) {
1111 return; 1082 return;
1112 } 1083 }
1113 if (keyboard->seat_device->input_device->wlr_device->keyboard->group) { 1084 if (keyboard->wlr->group) {
1114 sway_keyboard_group_remove(keyboard); 1085 sway_keyboard_group_remove(keyboard);
1115 } 1086 }
1116 struct wlr_seat *wlr_seat = keyboard->seat_device->sway_seat->wlr_seat; 1087 struct wlr_seat *wlr_seat = keyboard->seat_device->sway_seat->wlr_seat;
1117 struct sway_input_device *device = keyboard->seat_device->input_device; 1088 if (wlr_seat_get_keyboard(wlr_seat) == keyboard->wlr) {
1118 if (wlr_seat_get_keyboard(wlr_seat) == device->wlr_device->keyboard) {
1119 wlr_seat_set_keyboard(wlr_seat, NULL); 1089 wlr_seat_set_keyboard(wlr_seat, NULL);
1120 } 1090 }
1121 if (keyboard->keymap) { 1091 if (keyboard->keymap) {
diff --git a/sway/input/libinput.c b/sway/input/libinput.c
index 060a584a..0266c7a9 100644
--- a/sway/input/libinput.c
+++ b/sway/input/libinput.c
@@ -79,6 +79,16 @@ static bool set_accel_speed(struct libinput_device *device, double speed) {
79 return true; 79 return true;
80} 80}
81 81
82static bool set_rotation_angle(struct libinput_device *device, double angle) {
83 if (!libinput_device_config_rotation_is_available(device) ||
84 libinput_device_config_rotation_get_angle(device) == angle) {
85 return false;
86 }
87 sway_log(SWAY_DEBUG, "rotation_set_angle(%f)", angle);
88 log_status(libinput_device_config_rotation_set_angle(device, angle));
89 return true;
90}
91
82static bool set_accel_profile(struct libinput_device *device, 92static bool set_accel_profile(struct libinput_device *device,
83 enum libinput_config_accel_profile profile) { 93 enum libinput_config_accel_profile profile) {
84 if (!libinput_device_config_accel_is_available(device) || 94 if (!libinput_device_config_accel_is_available(device) ||
@@ -156,6 +166,18 @@ static bool set_scroll_button(struct libinput_device *dev, uint32_t button) {
156 return true; 166 return true;
157} 167}
158 168
169static bool set_scroll_button_lock(struct libinput_device *dev,
170 enum libinput_config_scroll_button_lock_state lock) {
171 uint32_t scroll = libinput_device_config_scroll_get_methods(dev);
172 if ((scroll & ~LIBINPUT_CONFIG_SCROLL_NO_SCROLL) == 0 ||
173 libinput_device_config_scroll_get_button_lock(dev) == lock) {
174 return false;
175 }
176 sway_log(SWAY_DEBUG, "scroll_set_button_lock(%" PRIu32 ")", lock);
177 log_status(libinput_device_config_scroll_set_button_lock(dev, lock));
178 return true;
179}
180
159static bool set_dwt(struct libinput_device *device, bool dwt) { 181static bool set_dwt(struct libinput_device *device, bool dwt) {
160 if (!libinput_device_config_dwt_is_available(device) || 182 if (!libinput_device_config_dwt_is_available(device) ||
161 libinput_device_config_dwt_get_enabled(device) == dwt) { 183 libinput_device_config_dwt_get_enabled(device) == dwt) {
@@ -166,6 +188,16 @@ static bool set_dwt(struct libinput_device *device, bool dwt) {
166 return true; 188 return true;
167} 189}
168 190
191static bool set_dwtp(struct libinput_device *device, bool dwtp) {
192 if (!libinput_device_config_dwtp_is_available(device) ||
193 libinput_device_config_dwtp_get_enabled(device) == dwtp) {
194 return false;
195 }
196 sway_log(SWAY_DEBUG, "dwtp_set_enabled(%d)", dwtp);
197 log_status(libinput_device_config_dwtp_set_enabled(device, dwtp));
198 return true;
199}
200
169static bool set_calibration_matrix(struct libinput_device *dev, float mat[6]) { 201static bool set_calibration_matrix(struct libinput_device *dev, float mat[6]) {
170 if (!libinput_device_config_calibration_has_matrix(dev)) { 202 if (!libinput_device_config_calibration_has_matrix(dev)) {
171 return false; 203 return false;
@@ -187,35 +219,38 @@ static bool set_calibration_matrix(struct libinput_device *dev, float mat[6]) {
187 return changed; 219 return changed;
188} 220}
189 221
190void sway_input_configure_libinput_device(struct sway_input_device *input_device) { 222static bool configure_send_events(struct libinput_device *device,
191 struct input_config *ic = input_device_get_config(input_device); 223 struct input_config *ic) {
192 if (!ic || !wlr_input_device_is_libinput(input_device->wlr_device)) {
193 return;
194 }
195
196 struct libinput_device *device =
197 wlr_libinput_get_device_handle(input_device->wlr_device);
198 sway_log(SWAY_DEBUG, "sway_input_configure_libinput_device('%s' on '%s')",
199 ic->identifier, input_device->identifier);
200
201 bool changed = false;
202 if (ic->mapped_to_output && 224 if (ic->mapped_to_output &&
225 strcmp("*", ic->mapped_to_output) != 0 &&
203 !output_by_name_or_id(ic->mapped_to_output)) { 226 !output_by_name_or_id(ic->mapped_to_output)) {
204 sway_log(SWAY_DEBUG, 227 sway_log(SWAY_DEBUG,
205 "%s '%s' is mapped to offline output '%s'; disabling input", 228 "%s '%s' is mapped to offline output '%s'; disabling input",
206 ic->input_type, ic->identifier, ic->mapped_to_output); 229 ic->input_type, ic->identifier, ic->mapped_to_output);
207 changed |= set_send_events(device, 230 return set_send_events(device, LIBINPUT_CONFIG_SEND_EVENTS_DISABLED);
208 LIBINPUT_CONFIG_SEND_EVENTS_DISABLED);
209 } else if (ic->send_events != INT_MIN) { 231 } else if (ic->send_events != INT_MIN) {
210 changed |= set_send_events(device, ic->send_events); 232 return set_send_events(device, ic->send_events);
211 } else { 233 } else {
212 // Have to reset to the default mode here, otherwise if ic->send_events 234 // Have to reset to the default mode here, otherwise if ic->send_events
213 // is unset and a mapped output just came online after being disabled, 235 // is unset and a mapped output just came online after being disabled,
214 // we'd remain stuck sending no events. 236 // we'd remain stuck sending no events.
215 changed |= set_send_events(device, 237 return set_send_events(device,
216 libinput_device_config_send_events_get_default_mode(device)); 238 libinput_device_config_send_events_get_default_mode(device));
217 } 239 }
240}
241
242bool sway_input_configure_libinput_device(struct sway_input_device *input_device) {
243 struct input_config *ic = input_device_get_config(input_device);
244 if (!ic || !wlr_input_device_is_libinput(input_device->wlr_device)) {
245 return false;
246 }
247
248 struct libinput_device *device =
249 wlr_libinput_get_device_handle(input_device->wlr_device);
250 sway_log(SWAY_DEBUG, "sway_input_configure_libinput_device('%s' on '%s')",
251 ic->identifier, input_device->identifier);
218 252
253 bool changed = configure_send_events(device, ic);
219 if (ic->tap != INT_MIN) { 254 if (ic->tap != INT_MIN) {
220 changed |= set_tap(device, ic->tap); 255 changed |= set_tap(device, ic->tap);
221 } 256 }
@@ -231,6 +266,9 @@ void sway_input_configure_libinput_device(struct sway_input_device *input_device
231 if (ic->pointer_accel != FLT_MIN) { 266 if (ic->pointer_accel != FLT_MIN) {
232 changed |= set_accel_speed(device, ic->pointer_accel); 267 changed |= set_accel_speed(device, ic->pointer_accel);
233 } 268 }
269 if (ic->rotation_angle != FLT_MIN) {
270 changed |= set_rotation_angle(device, ic->rotation_angle);
271 }
234 if (ic->accel_profile != INT_MIN) { 272 if (ic->accel_profile != INT_MIN) {
235 changed |= set_accel_profile(device, ic->accel_profile); 273 changed |= set_accel_profile(device, ic->accel_profile);
236 } 274 }
@@ -252,13 +290,33 @@ void sway_input_configure_libinput_device(struct sway_input_device *input_device
252 if (ic->scroll_button != INT_MIN) { 290 if (ic->scroll_button != INT_MIN) {
253 changed |= set_scroll_button(device, ic->scroll_button); 291 changed |= set_scroll_button(device, ic->scroll_button);
254 } 292 }
293 if (ic->scroll_button_lock != INT_MIN) {
294 changed |= set_scroll_button_lock(device, ic->scroll_button_lock);
295 }
255 if (ic->dwt != INT_MIN) { 296 if (ic->dwt != INT_MIN) {
256 changed |= set_dwt(device, ic->dwt); 297 changed |= set_dwt(device, ic->dwt);
257 } 298 }
299 if (ic->dwtp != INT_MIN) {
300 changed |= set_dwtp(device, ic->dwtp);
301 }
258 if (ic->calibration_matrix.configured) { 302 if (ic->calibration_matrix.configured) {
259 changed |= set_calibration_matrix(device, ic->calibration_matrix.matrix); 303 changed |= set_calibration_matrix(device, ic->calibration_matrix.matrix);
260 } 304 }
261 305
306 return changed;
307}
308
309void sway_input_configure_libinput_device_send_events(
310 struct sway_input_device *input_device) {
311 struct input_config *ic = input_device_get_config(input_device);
312 if (!ic || !wlr_input_device_is_libinput(input_device->wlr_device)) {
313 return;
314 }
315
316 struct libinput_device *device =
317 wlr_libinput_get_device_handle(input_device->wlr_device);
318 bool changed = configure_send_events(device, ic);
319
262 if (changed) { 320 if (changed) {
263 ipc_event_input("libinput_config", input_device); 321 ipc_event_input("libinput_config", input_device);
264 } 322 }
@@ -287,6 +345,8 @@ void sway_input_reset_libinput_device(struct sway_input_device *input_device) {
287 libinput_device_config_tap_get_default_drag_lock_enabled(device)); 345 libinput_device_config_tap_get_default_drag_lock_enabled(device));
288 changed |= set_accel_speed(device, 346 changed |= set_accel_speed(device,
289 libinput_device_config_accel_get_default_speed(device)); 347 libinput_device_config_accel_get_default_speed(device));
348 changed |= set_rotation_angle(device,
349 libinput_device_config_rotation_get_default_angle(device));
290 changed |= set_accel_profile(device, 350 changed |= set_accel_profile(device,
291 libinput_device_config_accel_get_default_profile(device)); 351 libinput_device_config_accel_get_default_profile(device));
292 changed |= set_natural_scroll(device, 352 changed |= set_natural_scroll(device,
@@ -304,6 +364,8 @@ void sway_input_reset_libinput_device(struct sway_input_device *input_device) {
304 libinput_device_config_scroll_get_default_button(device)); 364 libinput_device_config_scroll_get_default_button(device));
305 changed |= set_dwt(device, 365 changed |= set_dwt(device,
306 libinput_device_config_dwt_get_default_enabled(device)); 366 libinput_device_config_dwt_get_default_enabled(device));
367 changed |= set_dwtp(device,
368 libinput_device_config_dwtp_get_default_enabled(device));
307 369
308 float matrix[6]; 370 float matrix[6];
309 libinput_device_config_calibration_get_default_matrix(device, matrix); 371 libinput_device_config_calibration_get_default_matrix(device, matrix);
@@ -332,6 +394,13 @@ bool sway_libinput_device_is_builtin(struct sway_input_device *sway_device) {
332 return false; 394 return false;
333 } 395 }
334 396
335 const char prefix[] = "platform-"; 397 const char prefix_platform[] = "platform-";
336 return strncmp(id_path, prefix, strlen(prefix)) == 0; 398 if (strncmp(id_path, prefix_platform, strlen(prefix_platform)) != 0) {
399 return false;
400 }
401
402 const char prefix_pci[] = "pci-";
403 const char infix_platform[] = "-platform-";
404 return (strncmp(id_path, prefix_pci, strlen(prefix_pci)) == 0) &&
405 strstr(id_path, infix_platform);
337} 406}
diff --git a/sway/input/seat.c b/sway/input/seat.c
index 2d714acd..da4bb12a 100644
--- a/sway/input/seat.c
+++ b/sway/input/seat.c
@@ -1,22 +1,23 @@
1#define _POSIX_C_SOURCE 200809L
2#include <assert.h> 1#include <assert.h>
3#include <linux/input-event-codes.h> 2#include <linux/input-event-codes.h>
4#include <string.h> 3#include <string.h>
5#include <strings.h> 4#include <strings.h>
6#include <time.h> 5#include <time.h>
6#include <wlr/config.h>
7#include <wlr/types/wlr_cursor.h> 7#include <wlr/types/wlr_cursor.h>
8#include <wlr/types/wlr_data_device.h> 8#include <wlr/types/wlr_data_device.h>
9#include <wlr/types/wlr_idle.h> 9#include <wlr/types/wlr_idle_notify_v1.h>
10#include <wlr/types/wlr_keyboard_group.h> 10#include <wlr/types/wlr_keyboard_group.h>
11#include <wlr/types/wlr_output_layout.h> 11#include <wlr/types/wlr_output_layout.h>
12#include <wlr/types/wlr_primary_selection.h> 12#include <wlr/types/wlr_primary_selection.h>
13#include <wlr/types/wlr_tablet_v2.h> 13#include <wlr/types/wlr_tablet_v2.h>
14#include <wlr/types/wlr_touch.h>
14#include <wlr/types/wlr_xcursor_manager.h> 15#include <wlr/types/wlr_xcursor_manager.h>
15#include "config.h" 16#include "config.h"
16#include "list.h" 17#include "list.h"
17#include "log.h" 18#include "log.h"
18#include "sway/config.h" 19#include "sway/config.h"
19#include "sway/desktop.h" 20#include "sway/scene_descriptor.h"
20#include "sway/input/cursor.h" 21#include "sway/input/cursor.h"
21#include "sway/input/input-manager.h" 22#include "sway/input/input-manager.h"
22#include "sway/input/keyboard.h" 23#include "sway/input/keyboard.h"
@@ -42,6 +43,7 @@ static void seat_device_destroy(struct sway_seat_device *seat_device) {
42 sway_keyboard_destroy(seat_device->keyboard); 43 sway_keyboard_destroy(seat_device->keyboard);
43 sway_tablet_destroy(seat_device->tablet); 44 sway_tablet_destroy(seat_device->tablet);
44 sway_tablet_pad_destroy(seat_device->tablet_pad); 45 sway_tablet_pad_destroy(seat_device->tablet_pad);
46 sway_switch_destroy(seat_device->switch_device);
45 wlr_cursor_detach_input_device(seat_device->sway_seat->cursor->cursor, 47 wlr_cursor_detach_input_device(seat_device->sway_seat->cursor->cursor,
46 seat_device->input_device->wlr_device); 48 seat_device->input_device->wlr_device);
47 wl_list_remove(&seat_device->link); 49 wl_list_remove(&seat_device->link);
@@ -51,10 +53,26 @@ static void seat_device_destroy(struct sway_seat_device *seat_device) {
51static void seat_node_destroy(struct sway_seat_node *seat_node) { 53static void seat_node_destroy(struct sway_seat_node *seat_node) {
52 wl_list_remove(&seat_node->destroy.link); 54 wl_list_remove(&seat_node->destroy.link);
53 wl_list_remove(&seat_node->link); 55 wl_list_remove(&seat_node->link);
56
57 /*
58 * This is the only time we remove items from the focus stack without
59 * immediately re-adding them. If we just removed the last thing,
60 * mark that nothing has focus anymore.
61 */
62 if (wl_list_empty(&seat_node->seat->focus_stack)) {
63 seat_node->seat->has_focus = false;
64 }
65
54 free(seat_node); 66 free(seat_node);
55} 67}
56 68
57void seat_destroy(struct sway_seat *seat) { 69void seat_destroy(struct sway_seat *seat) {
70 wlr_seat_destroy(seat->wlr_seat);
71}
72
73static void handle_seat_destroy(struct wl_listener *listener, void *data) {
74 struct sway_seat *seat = wl_container_of(listener, seat, destroy);
75
58 if (seat == config->handler_context.seat) { 76 if (seat == config->handler_context.seat) {
59 config->handler_context.seat = input_manager_get_default_seat(); 77 config->handler_context.seat = input_manager_get_default_seat();
60 } 78 }
@@ -75,10 +93,11 @@ void seat_destroy(struct sway_seat *seat) {
75 wl_list_remove(&seat->request_set_selection.link); 93 wl_list_remove(&seat->request_set_selection.link);
76 wl_list_remove(&seat->request_set_primary_selection.link); 94 wl_list_remove(&seat->request_set_primary_selection.link);
77 wl_list_remove(&seat->link); 95 wl_list_remove(&seat->link);
78 wlr_seat_destroy(seat->wlr_seat); 96 wl_list_remove(&seat->destroy.link);
79 for (int i = 0; i < seat->deferred_bindings->length; i++) { 97 for (int i = 0; i < seat->deferred_bindings->length; i++) {
80 free_sway_binding(seat->deferred_bindings->items[i]); 98 free_sway_binding(seat->deferred_bindings->items[i]);
81 } 99 }
100 wlr_scene_node_destroy(&seat->scene_tree->node);
82 list_free(seat->deferred_bindings); 101 list_free(seat->deferred_bindings);
83 free(seat->prev_workspace_name); 102 free(seat->prev_workspace_name);
84 free(seat); 103 free(seat);
@@ -86,21 +105,10 @@ void seat_destroy(struct sway_seat *seat) {
86 105
87void seat_idle_notify_activity(struct sway_seat *seat, 106void seat_idle_notify_activity(struct sway_seat *seat,
88 enum sway_input_idle_source source) { 107 enum sway_input_idle_source source) {
89 uint32_t mask = seat->idle_inhibit_sources; 108 if ((source & seat->idle_inhibit_sources) == 0) {
90 struct wlr_idle_timeout *timeout; 109 return;
91 int ntimers = 0, nidle = 0;
92 wl_list_for_each(timeout, &server.idle->idle_timers, link) {
93 ++ntimers;
94 if (timeout->idle_state) {
95 ++nidle;
96 }
97 }
98 if (nidle == ntimers) {
99 mask = seat->idle_wake_sources;
100 }
101 if ((source & mask) > 0) {
102 wlr_idle_notify_activity(server.idle, seat->wlr_seat);
103 } 110 }
111 wlr_idle_notifier_v1_notify_activity(server.idle_notifier_v1, seat->wlr_seat);
104} 112}
105 113
106/** 114/**
@@ -130,7 +138,7 @@ static struct sway_keyboard *sway_keyboard_for_wlr_keyboard(
130 if (input_device->wlr_device->type != WLR_INPUT_DEVICE_KEYBOARD) { 138 if (input_device->wlr_device->type != WLR_INPUT_DEVICE_KEYBOARD) {
131 continue; 139 continue;
132 } 140 }
133 if (input_device->wlr_device->keyboard == wlr_keyboard) { 141 if (input_device->wlr_device == &wlr_keyboard->base) {
134 return seat_device->keyboard; 142 return seat_device->keyboard;
135 } 143 }
136 } 144 }
@@ -138,7 +146,7 @@ static struct sway_keyboard *sway_keyboard_for_wlr_keyboard(
138 wl_list_for_each(group, &seat->keyboard_groups, link) { 146 wl_list_for_each(group, &seat->keyboard_groups, link) {
139 struct sway_input_device *input_device = 147 struct sway_input_device *input_device =
140 group->seat_device->input_device; 148 group->seat_device->input_device;
141 if (input_device->wlr_device->keyboard == wlr_keyboard) { 149 if (input_device->wlr_device == &wlr_keyboard->base) {
142 return group->seat_device->keyboard; 150 return group->seat_device->keyboard;
143 } 151 }
144 } 152 }
@@ -162,11 +170,11 @@ static void seat_keyboard_notify_enter(struct sway_seat *seat,
162 state->pressed_keycodes, state->npressed, &keyboard->modifiers); 170 state->pressed_keycodes, state->npressed, &keyboard->modifiers);
163} 171}
164 172
165static void seat_tablet_pads_notify_enter(struct sway_seat *seat, 173static void seat_tablet_pads_set_focus(struct sway_seat *seat,
166 struct wlr_surface *surface) { 174 struct wlr_surface *surface) {
167 struct sway_seat_device *seat_device; 175 struct sway_seat_device *seat_device;
168 wl_list_for_each(seat_device, &seat->devices, link) { 176 wl_list_for_each(seat_device, &seat->devices, link) {
169 sway_tablet_pad_notify_enter(seat_device->tablet_pad, surface); 177 sway_tablet_pad_set_focus(seat_device->tablet_pad, surface);
170 } 178 }
171} 179}
172 180
@@ -182,7 +190,7 @@ static void seat_send_focus(struct sway_node *node, struct sway_seat *seat) {
182 node->sway_container->view : NULL; 190 node->sway_container->view : NULL;
183 191
184 if (view && seat_is_input_allowed(seat, view->surface)) { 192 if (view && seat_is_input_allowed(seat, view->surface)) {
185#if HAVE_XWAYLAND 193#if WLR_HAS_XWAYLAND
186 if (view->type == SWAY_VIEW_XWAYLAND) { 194 if (view->type == SWAY_VIEW_XWAYLAND) {
187 struct wlr_xwayland *xwayland = server.xwayland.wlr_xwayland; 195 struct wlr_xwayland *xwayland = server.xwayland.wlr_xwayland;
188 wlr_xwayland_set_seat(xwayland, seat->wlr_seat); 196 wlr_xwayland_set_seat(xwayland, seat->wlr_seat);
@@ -190,7 +198,7 @@ static void seat_send_focus(struct sway_node *node, struct sway_seat *seat) {
190#endif 198#endif
191 199
192 seat_keyboard_notify_enter(seat, view->surface); 200 seat_keyboard_notify_enter(seat, view->surface);
193 seat_tablet_pads_notify_enter(seat, view->surface); 201 seat_tablet_pads_set_focus(seat, view->surface);
194 sway_input_method_relay_set_focus(&seat->im_relay, view->surface); 202 sway_input_method_relay_set_focus(&seat->im_relay, view->surface);
195 203
196 struct wlr_pointer_constraint_v1 *constraint = 204 struct wlr_pointer_constraint_v1 *constraint =
@@ -210,14 +218,13 @@ void seat_for_each_node(struct sway_seat *seat,
210 218
211struct sway_container *seat_get_focus_inactive_view(struct sway_seat *seat, 219struct sway_container *seat_get_focus_inactive_view(struct sway_seat *seat,
212 struct sway_node *ancestor) { 220 struct sway_node *ancestor) {
213 if (ancestor->type == N_CONTAINER && ancestor->sway_container->view) { 221 if (node_is_view(ancestor)) {
214 return ancestor->sway_container; 222 return ancestor->sway_container;
215 } 223 }
216 struct sway_seat_node *current; 224 struct sway_seat_node *current;
217 wl_list_for_each(current, &seat->focus_stack, link) { 225 wl_list_for_each(current, &seat->focus_stack, link) {
218 struct sway_node *node = current->node; 226 struct sway_node *node = current->node;
219 if (node->type == N_CONTAINER && node->sway_container->view && 227 if (node_is_view(node) && node_has_ancestor(node, ancestor)) {
220 node_has_ancestor(node, ancestor)) {
221 return node->sway_container; 228 return node->sway_container;
222 } 229 }
223 } 230 }
@@ -236,7 +243,7 @@ static void handle_seat_node_destroy(struct wl_listener *listener, void *data) {
236 seat_node_destroy(seat_node); 243 seat_node_destroy(seat_node);
237 // If an unmanaged or layer surface is focused when an output gets 244 // If an unmanaged or layer surface is focused when an output gets
238 // disabled and an empty workspace on the output was focused by the 245 // disabled and an empty workspace on the output was focused by the
239 // seat, the seat needs to refocus it's focus inactive to update the 246 // seat, the seat needs to refocus its focus inactive to update the
240 // value of seat->workspace. 247 // value of seat->workspace.
241 if (seat->workspace == node->sway_workspace) { 248 if (seat->workspace == node->sway_workspace) {
242 struct sway_node *node = seat_get_focus_inactive(seat, &root->node); 249 struct sway_node *node = seat_get_focus_inactive(seat, &root->node);
@@ -352,25 +359,15 @@ static void handle_new_node(struct wl_listener *listener, void *data) {
352 seat_node_from_node(seat, node); 359 seat_node_from_node(seat, node);
353} 360}
354 361
355static void drag_icon_damage_whole(struct sway_drag_icon *icon) { 362static void drag_icon_update_position(struct sway_seat *seat, struct wlr_scene_node *node) {
356 if (!icon->wlr_drag_icon->mapped) { 363 struct wlr_drag_icon *wlr_icon = scene_descriptor_try_get(node, SWAY_SCENE_DESC_DRAG_ICON);
357 return;
358 }
359 desktop_damage_surface(icon->wlr_drag_icon->surface, icon->x, icon->y, true);
360}
361
362void drag_icon_update_position(struct sway_drag_icon *icon) {
363 drag_icon_damage_whole(icon);
364
365 struct wlr_drag_icon *wlr_icon = icon->wlr_drag_icon;
366 struct sway_seat *seat = icon->seat;
367 struct wlr_cursor *cursor = seat->cursor->cursor; 364 struct wlr_cursor *cursor = seat->cursor->cursor;
365
368 switch (wlr_icon->drag->grab_type) { 366 switch (wlr_icon->drag->grab_type) {
369 case WLR_DRAG_GRAB_KEYBOARD: 367 case WLR_DRAG_GRAB_KEYBOARD:
370 return; 368 return;
371 case WLR_DRAG_GRAB_KEYBOARD_POINTER: 369 case WLR_DRAG_GRAB_KEYBOARD_POINTER:
372 icon->x = cursor->x; 370 wlr_scene_node_set_position(node, cursor->x, cursor->y);
373 icon->y = cursor->y;
374 break; 371 break;
375 case WLR_DRAG_GRAB_KEYBOARD_TOUCH:; 372 case WLR_DRAG_GRAB_KEYBOARD_TOUCH:;
376 struct wlr_touch_point *point = 373 struct wlr_touch_point *point =
@@ -378,39 +375,15 @@ void drag_icon_update_position(struct sway_drag_icon *icon) {
378 if (point == NULL) { 375 if (point == NULL) {
379 return; 376 return;
380 } 377 }
381 icon->x = seat->touch_x; 378 wlr_scene_node_set_position(node, seat->touch_x, seat->touch_y);
382 icon->y = seat->touch_y;
383 } 379 }
384
385 drag_icon_damage_whole(icon);
386} 380}
387 381
388static void drag_icon_handle_surface_commit(struct wl_listener *listener, 382void drag_icons_update_position(struct sway_seat *seat) {
389 void *data) { 383 struct wlr_scene_node *node;
390 struct sway_drag_icon *icon = 384 wl_list_for_each(node, &seat->drag_icons->children, link) {
391 wl_container_of(listener, icon, surface_commit); 385 drag_icon_update_position(seat, node);
392 drag_icon_update_position(icon); 386 }
393}
394
395static void drag_icon_handle_map(struct wl_listener *listener, void *data) {
396 struct sway_drag_icon *icon = wl_container_of(listener, icon, map);
397 drag_icon_damage_whole(icon);
398}
399
400static void drag_icon_handle_unmap(struct wl_listener *listener, void *data) {
401 struct sway_drag_icon *icon = wl_container_of(listener, icon, unmap);
402 drag_icon_damage_whole(icon);
403}
404
405static void drag_icon_handle_destroy(struct wl_listener *listener, void *data) {
406 struct sway_drag_icon *icon = wl_container_of(listener, icon, destroy);
407 icon->wlr_drag_icon->data = NULL;
408 wl_list_remove(&icon->link);
409 wl_list_remove(&icon->surface_commit.link);
410 wl_list_remove(&icon->unmap.link);
411 wl_list_remove(&icon->map.link);
412 wl_list_remove(&icon->destroy.link);
413 free(icon);
414} 387}
415 388
416static void drag_handle_destroy(struct wl_listener *listener, void *data) { 389static void drag_handle_destroy(struct wl_listener *listener, void *data) {
@@ -482,27 +455,20 @@ static void handle_start_drag(struct wl_listener *listener, void *data) {
482 455
483 struct wlr_drag_icon *wlr_drag_icon = wlr_drag->icon; 456 struct wlr_drag_icon *wlr_drag_icon = wlr_drag->icon;
484 if (wlr_drag_icon != NULL) { 457 if (wlr_drag_icon != NULL) {
485 struct sway_drag_icon *icon = calloc(1, sizeof(struct sway_drag_icon)); 458 struct wlr_scene_tree *tree = wlr_scene_drag_icon_create(seat->drag_icons, wlr_drag_icon);
486 if (icon == NULL) { 459 if (!tree) {
487 sway_log(SWAY_ERROR, "Allocation failed"); 460 sway_log(SWAY_ERROR, "Failed to allocate a drag icon scene tree");
488 return; 461 return;
489 } 462 }
490 icon->seat = seat;
491 icon->wlr_drag_icon = wlr_drag_icon;
492 wlr_drag_icon->data = icon;
493 463
494 icon->surface_commit.notify = drag_icon_handle_surface_commit; 464 if (!scene_descriptor_assign(&tree->node, SWAY_SCENE_DESC_DRAG_ICON,
495 wl_signal_add(&wlr_drag_icon->surface->events.commit, &icon->surface_commit); 465 wlr_drag_icon)) {
496 icon->unmap.notify = drag_icon_handle_unmap; 466 sway_log(SWAY_ERROR, "Failed to allocate a drag icon scene descriptor");
497 wl_signal_add(&wlr_drag_icon->events.unmap, &icon->unmap); 467 wlr_scene_node_destroy(&tree->node);
498 icon->map.notify = drag_icon_handle_map; 468 return;
499 wl_signal_add(&wlr_drag_icon->events.map, &icon->map); 469 }
500 icon->destroy.notify = drag_icon_handle_destroy;
501 wl_signal_add(&wlr_drag_icon->events.destroy, &icon->destroy);
502
503 wl_list_insert(&root->drag_icons, &icon->link);
504 470
505 drag_icon_update_position(icon); 471 drag_icon_update_position(seat, &tree->node);
506 } 472 }
507 seatop_begin_default(seat); 473 seatop_begin_default(seat);
508} 474}
@@ -549,8 +515,18 @@ struct sway_seat *seat_create(const char *seat_name) {
549 return NULL; 515 return NULL;
550 } 516 }
551 517
518 bool failed = false;
519 seat->scene_tree = alloc_scene_tree(root->layers.seat, &failed);
520 seat->drag_icons = alloc_scene_tree(seat->scene_tree, &failed);
521 if (failed) {
522 wlr_scene_node_destroy(&seat->scene_tree->node);
523 free(seat);
524 return NULL;
525 }
526
552 seat->wlr_seat = wlr_seat_create(server.wl_display, seat_name); 527 seat->wlr_seat = wlr_seat_create(server.wl_display, seat_name);
553 if (!sway_assert(seat->wlr_seat, "could not allocate seat")) { 528 if (!sway_assert(seat->wlr_seat, "could not allocate seat")) {
529 wlr_scene_node_destroy(&seat->scene_tree->node);
554 free(seat); 530 free(seat);
555 return NULL; 531 return NULL;
556 } 532 }
@@ -558,11 +534,15 @@ struct sway_seat *seat_create(const char *seat_name) {
558 534
559 seat->cursor = sway_cursor_create(seat); 535 seat->cursor = sway_cursor_create(seat);
560 if (!seat->cursor) { 536 if (!seat->cursor) {
537 wlr_scene_node_destroy(&seat->scene_tree->node);
561 wlr_seat_destroy(seat->wlr_seat); 538 wlr_seat_destroy(seat->wlr_seat);
562 free(seat); 539 free(seat);
563 return NULL; 540 return NULL;
564 } 541 }
565 542
543 seat->destroy.notify = handle_seat_destroy;
544 wl_signal_add(&seat->wlr_seat->events.destroy, &seat->destroy);
545
566 seat->idle_inhibit_sources = seat->idle_wake_sources = 546 seat->idle_inhibit_sources = seat->idle_wake_sources =
567 IDLE_SOURCE_KEYBOARD | 547 IDLE_SOURCE_KEYBOARD |
568 IDLE_SOURCE_POINTER | 548 IDLE_SOURCE_POINTER |
@@ -636,7 +616,7 @@ static void seat_update_capabilities(struct sway_seat *seat) {
636 case WLR_INPUT_DEVICE_TOUCH: 616 case WLR_INPUT_DEVICE_TOUCH:
637 caps |= WL_SEAT_CAPABILITY_TOUCH; 617 caps |= WL_SEAT_CAPABILITY_TOUCH;
638 break; 618 break;
639 case WLR_INPUT_DEVICE_TABLET_TOOL: 619 case WLR_INPUT_DEVICE_TABLET:
640 caps |= WL_SEAT_CAPABILITY_POINTER; 620 caps |= WL_SEAT_CAPABILITY_POINTER;
641 break; 621 break;
642 case WLR_INPUT_DEVICE_SWITCH: 622 case WLR_INPUT_DEVICE_SWITCH:
@@ -654,7 +634,7 @@ static void seat_update_capabilities(struct sway_seat *seat) {
654 } else { 634 } else {
655 wlr_seat_set_capabilities(seat->wlr_seat, caps); 635 wlr_seat_set_capabilities(seat->wlr_seat, caps);
656 if ((previous_caps & WL_SEAT_CAPABILITY_POINTER) == 0) { 636 if ((previous_caps & WL_SEAT_CAPABILITY_POINTER) == 0) {
657 cursor_set_image(seat->cursor, "left_ptr", NULL); 637 cursor_set_image(seat->cursor, "default", NULL);
658 } 638 }
659 } 639 }
660} 640}
@@ -694,19 +674,28 @@ static const char *get_builtin_output_name(void) {
694static bool is_touch_or_tablet_tool(struct sway_seat_device *seat_device) { 674static bool is_touch_or_tablet_tool(struct sway_seat_device *seat_device) {
695 switch (seat_device->input_device->wlr_device->type) { 675 switch (seat_device->input_device->wlr_device->type) {
696 case WLR_INPUT_DEVICE_TOUCH: 676 case WLR_INPUT_DEVICE_TOUCH:
697 case WLR_INPUT_DEVICE_TABLET_TOOL: 677 case WLR_INPUT_DEVICE_TABLET:
698 return true; 678 return true;
699 default: 679 default:
700 return false; 680 return false;
701 } 681 }
702} 682}
703 683
704static void seat_apply_input_config(struct sway_seat *seat, 684static void seat_apply_input_mapping(struct sway_seat *seat,
705 struct sway_seat_device *sway_device) { 685 struct sway_seat_device *sway_device) {
706 struct input_config *ic = 686 struct input_config *ic =
707 input_device_get_config(sway_device->input_device); 687 input_device_get_config(sway_device->input_device);
708 688
709 sway_log(SWAY_DEBUG, "Applying input config to %s", 689 switch (sway_device->input_device->wlr_device->type) {
690 case WLR_INPUT_DEVICE_POINTER:
691 case WLR_INPUT_DEVICE_TOUCH:
692 case WLR_INPUT_DEVICE_TABLET:
693 break;
694 default:
695 return; // these devices don't support mappings
696 }
697
698 sway_log(SWAY_DEBUG, "Applying input mapping to %s",
710 sway_device->input_device->identifier); 699 sway_device->input_device->identifier);
711 700
712 const char *mapped_to_output = ic == NULL ? NULL : ic->mapped_to_output; 701 const char *mapped_to_output = ic == NULL ? NULL : ic->mapped_to_output;
@@ -715,14 +704,26 @@ static void seat_apply_input_config(struct sway_seat *seat,
715 ic == NULL ? MAPPED_TO_DEFAULT : ic->mapped_to; 704 ic == NULL ? MAPPED_TO_DEFAULT : ic->mapped_to;
716 705
717 switch (mapped_to) { 706 switch (mapped_to) {
718 case MAPPED_TO_DEFAULT: 707 case MAPPED_TO_DEFAULT:;
719 /* 708 /*
720 * If the wlroots backend provides an output name, use that. 709 * If the wlroots backend provides an output name, use that.
721 * 710 *
722 * Otherwise, try to map built-in touch and tablet tool devices to the 711 * Otherwise, try to map built-in touch and pointer devices to the
723 * built-in output. 712 * built-in output.
724 */ 713 */
725 mapped_to_output = sway_device->input_device->wlr_device->output_name; 714 struct wlr_input_device *dev = sway_device->input_device->wlr_device;
715 switch (dev->type) {
716 case WLR_INPUT_DEVICE_POINTER:
717 mapped_to_output = wlr_pointer_from_input_device(dev)->output_name;
718 break;
719 case WLR_INPUT_DEVICE_TOUCH:
720 mapped_to_output = wlr_touch_from_input_device(dev)->output_name;
721 break;
722 default:
723 mapped_to_output = NULL;
724 break;
725 }
726#if WLR_HAS_LIBINPUT_BACKEND
726 if (mapped_to_output == NULL && is_touch_or_tablet_tool(sway_device) && 727 if (mapped_to_output == NULL && is_touch_or_tablet_tool(sway_device) &&
727 sway_libinput_device_is_builtin(sway_device->input_device)) { 728 sway_libinput_device_is_builtin(sway_device->input_device)) {
728 mapped_to_output = get_builtin_output_name(); 729 mapped_to_output = get_builtin_output_name();
@@ -731,6 +732,10 @@ static void seat_apply_input_config(struct sway_seat *seat,
731 mapped_to_output, sway_device->input_device->identifier); 732 mapped_to_output, sway_device->input_device->identifier);
732 } 733 }
733 } 734 }
735#else
736 (void)is_touch_or_tablet_tool;
737 (void)get_builtin_output_name;
738#endif
734 if (mapped_to_output == NULL) { 739 if (mapped_to_output == NULL) {
735 return; 740 return;
736 } 741 }
@@ -774,12 +779,9 @@ static void seat_apply_input_config(struct sway_seat *seat,
774 779
775static void seat_configure_pointer(struct sway_seat *seat, 780static void seat_configure_pointer(struct sway_seat *seat,
776 struct sway_seat_device *sway_device) { 781 struct sway_seat_device *sway_device) {
777 if ((seat->wlr_seat->capabilities & WL_SEAT_CAPABILITY_POINTER) == 0) { 782 seat_configure_xcursor(seat);
778 seat_configure_xcursor(seat);
779 }
780 wlr_cursor_attach_input_device(seat->cursor->cursor, 783 wlr_cursor_attach_input_device(seat->cursor->cursor,
781 sway_device->input_device->wlr_device); 784 sway_device->input_device->wlr_device);
782 seat_apply_input_config(seat, sway_device);
783 wl_event_source_timer_update( 785 wl_event_source_timer_update(
784 seat->cursor->hide_source, cursor_get_timeout(seat->cursor)); 786 seat->cursor->hide_source, cursor_get_timeout(seat->cursor));
785} 787}
@@ -790,13 +792,22 @@ static void seat_configure_keyboard(struct sway_seat *seat,
790 sway_keyboard_create(seat, seat_device); 792 sway_keyboard_create(seat, seat_device);
791 } 793 }
792 sway_keyboard_configure(seat_device->keyboard); 794 sway_keyboard_configure(seat_device->keyboard);
793 wlr_seat_set_keyboard(seat->wlr_seat, 795
794 seat_device->input_device->wlr_device); 796 // We only need to update the current keyboard, as the rest will be updated
795 struct sway_node *focus = seat_get_focus(seat); 797 // as they are activated.
796 if (focus && node_is_view(focus)) { 798 struct wlr_keyboard *wlr_keyboard =
797 // force notify reenter to pick up the new configuration 799 wlr_keyboard_from_input_device(seat_device->input_device->wlr_device);
800 struct wlr_keyboard *current_keyboard = seat->wlr_seat->keyboard_state.keyboard;
801 if (wlr_keyboard != current_keyboard) {
802 return;
803 }
804
805 // force notify reenter to pick up the new configuration. This reuses
806 // the current focused surface to avoid breaking input grabs.
807 struct wlr_surface *surface = seat->wlr_seat->keyboard_state.focused_surface;
808 if (surface) {
798 wlr_seat_keyboard_notify_clear_focus(seat->wlr_seat); 809 wlr_seat_keyboard_notify_clear_focus(seat->wlr_seat);
799 seat_keyboard_notify_enter(seat, focus->sway_container->view->surface); 810 seat_keyboard_notify_enter(seat, surface);
800 } 811 }
801} 812}
802 813
@@ -805,7 +816,6 @@ static void seat_configure_switch(struct sway_seat *seat,
805 if (!seat_device->switch_device) { 816 if (!seat_device->switch_device) {
806 sway_switch_create(seat, seat_device); 817 sway_switch_create(seat, seat_device);
807 } 818 }
808 seat_apply_input_config(seat, seat_device);
809 sway_switch_configure(seat_device->switch_device); 819 sway_switch_configure(seat_device->switch_device);
810} 820}
811 821
@@ -813,7 +823,6 @@ static void seat_configure_touch(struct sway_seat *seat,
813 struct sway_seat_device *sway_device) { 823 struct sway_seat_device *sway_device) {
814 wlr_cursor_attach_input_device(seat->cursor->cursor, 824 wlr_cursor_attach_input_device(seat->cursor->cursor,
815 sway_device->input_device->wlr_device); 825 sway_device->input_device->wlr_device);
816 seat_apply_input_config(seat, sway_device);
817} 826}
818 827
819static void seat_configure_tablet_tool(struct sway_seat *seat, 828static void seat_configure_tablet_tool(struct sway_seat *seat,
@@ -824,7 +833,6 @@ static void seat_configure_tablet_tool(struct sway_seat *seat,
824 sway_configure_tablet(sway_device->tablet); 833 sway_configure_tablet(sway_device->tablet);
825 wlr_cursor_attach_input_device(seat->cursor->cursor, 834 wlr_cursor_attach_input_device(seat->cursor->cursor,
826 sway_device->input_device->wlr_device); 835 sway_device->input_device->wlr_device);
827 seat_apply_input_config(seat, sway_device);
828} 836}
829 837
830static void seat_configure_tablet_pad(struct sway_seat *seat, 838static void seat_configure_tablet_pad(struct sway_seat *seat,
@@ -874,13 +882,25 @@ void seat_configure_device(struct sway_seat *seat,
874 case WLR_INPUT_DEVICE_TOUCH: 882 case WLR_INPUT_DEVICE_TOUCH:
875 seat_configure_touch(seat, seat_device); 883 seat_configure_touch(seat, seat_device);
876 break; 884 break;
877 case WLR_INPUT_DEVICE_TABLET_TOOL: 885 case WLR_INPUT_DEVICE_TABLET:
878 seat_configure_tablet_tool(seat, seat_device); 886 seat_configure_tablet_tool(seat, seat_device);
879 break; 887 break;
880 case WLR_INPUT_DEVICE_TABLET_PAD: 888 case WLR_INPUT_DEVICE_TABLET_PAD:
881 seat_configure_tablet_pad(seat, seat_device); 889 seat_configure_tablet_pad(seat, seat_device);
882 break; 890 break;
883 } 891 }
892
893 seat_apply_input_mapping(seat, seat_device);
894}
895
896void seat_configure_device_mapping(struct sway_seat *seat,
897 struct sway_input_device *input_device) {
898 struct sway_seat_device *seat_device = seat_get_device(seat, input_device);
899 if (!seat_device) {
900 return;
901 }
902
903 seat_apply_input_mapping(seat, seat_device);
884} 904}
885 905
886void seat_reset_device(struct sway_seat *seat, 906void seat_reset_device(struct sway_seat *seat,
@@ -901,7 +921,7 @@ void seat_reset_device(struct sway_seat *seat,
901 case WLR_INPUT_DEVICE_TOUCH: 921 case WLR_INPUT_DEVICE_TOUCH:
902 seat_reset_input_config(seat, seat_device); 922 seat_reset_input_config(seat, seat_device);
903 break; 923 break;
904 case WLR_INPUT_DEVICE_TABLET_TOOL: 924 case WLR_INPUT_DEVICE_TABLET:
905 seat_reset_input_config(seat, seat_device); 925 seat_reset_input_config(seat, seat_device);
906 break; 926 break;
907 case WLR_INPUT_DEVICE_TABLET_PAD: 927 case WLR_INPUT_DEVICE_TABLET_PAD:
@@ -982,7 +1002,7 @@ void seat_configure_xcursor(struct sway_seat *seat) {
982 setenv("XCURSOR_THEME", cursor_theme, 1); 1002 setenv("XCURSOR_THEME", cursor_theme, 1);
983 } 1003 }
984 1004
985#if HAVE_XWAYLAND 1005#if WLR_HAS_XWAYLAND
986 if (server.xwayland.wlr_xwayland && (!server.xwayland.xcursor_manager || 1006 if (server.xwayland.wlr_xwayland && (!server.xwayland.xcursor_manager ||
987 !xcursor_manager_is_named(server.xwayland.xcursor_manager, 1007 !xcursor_manager_is_named(server.xwayland.xcursor_manager,
988 cursor_theme) || 1008 cursor_theme) ||
@@ -997,7 +1017,7 @@ void seat_configure_xcursor(struct sway_seat *seat) {
997 1017
998 wlr_xcursor_manager_load(server.xwayland.xcursor_manager, 1); 1018 wlr_xcursor_manager_load(server.xwayland.xcursor_manager, 1);
999 struct wlr_xcursor *xcursor = wlr_xcursor_manager_get_xcursor( 1019 struct wlr_xcursor *xcursor = wlr_xcursor_manager_get_xcursor(
1000 server.xwayland.xcursor_manager, "left_ptr", 1); 1020 server.xwayland.xcursor_manager, "default", 1);
1001 if (xcursor != NULL) { 1021 if (xcursor != NULL) {
1002 struct wlr_xcursor_image *image = xcursor->images[0]; 1022 struct wlr_xcursor_image *image = xcursor->images[0];
1003 wlr_xwayland_set_cursor( 1023 wlr_xwayland_set_cursor(
@@ -1023,32 +1043,35 @@ void seat_configure_xcursor(struct sway_seat *seat) {
1023 sway_log(SWAY_ERROR, 1043 sway_log(SWAY_ERROR,
1024 "Cannot create XCursor manager for theme '%s'", cursor_theme); 1044 "Cannot create XCursor manager for theme '%s'", cursor_theme);
1025 } 1045 }
1026 }
1027 1046
1028 for (int i = 0; i < root->outputs->length; ++i) { 1047
1029 struct sway_output *sway_output = root->outputs->items[i]; 1048 for (int i = 0; i < root->outputs->length; ++i) {
1030 struct wlr_output *output = sway_output->wlr_output; 1049 struct sway_output *sway_output = root->outputs->items[i];
1031 bool result = 1050 struct wlr_output *output = sway_output->wlr_output;
1032 wlr_xcursor_manager_load(seat->cursor->xcursor_manager, 1051 bool result =
1033 output->scale); 1052 wlr_xcursor_manager_load(seat->cursor->xcursor_manager,
1034 if (!result) { 1053 output->scale);
1035 sway_log(SWAY_ERROR, 1054 if (!result) {
1036 "Cannot load xcursor theme for output '%s' with scale %f", 1055 sway_log(SWAY_ERROR,
1037 output->name, output->scale); 1056 "Cannot load xcursor theme for output '%s' with scale %f",
1057 output->name, output->scale);
1058 }
1038 } 1059 }
1039 }
1040 1060
1041 // Reset the cursor so that we apply it to outputs that just appeared 1061 // Reset the cursor so that we apply it to outputs that just appeared
1042 cursor_set_image(seat->cursor, NULL, NULL); 1062 cursor_set_image(seat->cursor, NULL, NULL);
1043 cursor_set_image(seat->cursor, "left_ptr", NULL); 1063 cursor_set_image(seat->cursor, "default", NULL);
1044 wlr_cursor_warp(seat->cursor->cursor, NULL, seat->cursor->cursor->x, 1064 wlr_cursor_warp(seat->cursor->cursor, NULL, seat->cursor->cursor->x,
1045 seat->cursor->cursor->y); 1065 seat->cursor->cursor->y);
1066 }
1046} 1067}
1047 1068
1048bool seat_is_input_allowed(struct sway_seat *seat, 1069bool seat_is_input_allowed(struct sway_seat *seat,
1049 struct wlr_surface *surface) { 1070 struct wlr_surface *surface) {
1050 struct wl_client *client = wl_resource_get_client(surface->resource); 1071 if (server.session_lock.lock) {
1051 return !seat->exclusive_client || seat->exclusive_client == client; 1072 return sway_session_lock_has_surface(server.session_lock.lock, surface);
1073 }
1074 return true;
1052} 1075}
1053 1076
1054static void send_unfocus(struct sway_container *con, void *data) { 1077static void send_unfocus(struct sway_container *con, void *data) {
@@ -1107,15 +1130,7 @@ void seat_set_raw_focus(struct sway_seat *seat, struct sway_node *node) {
1107 } 1130 }
1108} 1131}
1109 1132
1110void seat_set_focus(struct sway_seat *seat, struct sway_node *node) { 1133static void seat_set_workspace_focus(struct sway_seat *seat, struct sway_node *node) {
1111 if (seat->focused_layer) {
1112 struct wlr_layer_surface_v1 *layer = seat->focused_layer;
1113 seat_set_focus_layer(seat, NULL);
1114 seat_set_focus(seat, node);
1115 seat_set_focus_layer(seat, layer);
1116 return;
1117 }
1118
1119 struct sway_node *last_focus = seat_get_focus(seat); 1134 struct sway_node *last_focus = seat_get_focus(seat);
1120 if (last_focus == node) { 1135 if (last_focus == node) {
1121 return; 1136 return;
@@ -1248,6 +1263,24 @@ void seat_set_focus(struct sway_seat *seat, struct sway_node *node) {
1248 } 1263 }
1249} 1264}
1250 1265
1266void seat_set_focus(struct sway_seat *seat, struct sway_node *node) {
1267 // Prevents the layer from losing focus if it has keyboard exclusivity
1268 if (seat->has_exclusive_layer) {
1269 struct wlr_layer_surface_v1 *layer = seat->focused_layer;
1270 seat_set_focus_layer(seat, NULL);
1271 seat_set_workspace_focus(seat, node);
1272 seat_set_focus_layer(seat, layer);
1273 } else if (seat->focused_layer) {
1274 seat_set_focus_layer(seat, NULL);
1275 seat_set_workspace_focus(seat, node);
1276 } else {
1277 seat_set_workspace_focus(seat, node);
1278 }
1279 if (server.session_lock.lock) {
1280 seat_set_focus_surface(seat, server.session_lock.lock->focused, false);
1281 }
1282}
1283
1251void seat_set_focus_container(struct sway_seat *seat, 1284void seat_set_focus_container(struct sway_seat *seat,
1252 struct sway_container *con) { 1285 struct sway_container *con) {
1253 seat_set_focus(seat, con ? &con->node : NULL); 1286 seat_set_focus(seat, con ? &con->node : NULL);
@@ -1273,7 +1306,7 @@ void seat_set_focus_surface(struct sway_seat *seat,
1273 } 1306 }
1274 1307
1275 sway_input_method_relay_set_focus(&seat->im_relay, surface); 1308 sway_input_method_relay_set_focus(&seat->im_relay, surface);
1276 seat_tablet_pads_notify_enter(seat, surface); 1309 seat_tablet_pads_set_focus(seat, surface);
1277} 1310}
1278 1311
1279void seat_set_focus_layer(struct sway_seat *seat, 1312void seat_set_focus_layer(struct sway_seat *seat,
@@ -1287,28 +1320,23 @@ void seat_set_focus_layer(struct sway_seat *seat,
1287 seat_set_focus(seat, previous); 1320 seat_set_focus(seat, previous);
1288 } 1321 }
1289 return; 1322 return;
1290 } else if (!layer || seat->focused_layer == layer) { 1323 } else if (!layer) {
1291 return; 1324 return;
1292 } 1325 }
1293 assert(layer->mapped); 1326 assert(layer->surface->mapped);
1294 seat_set_focus_surface(seat, layer->surface, true); 1327 if (layer->current.layer >= ZWLR_LAYER_SHELL_V1_LAYER_TOP &&
1295 if (layer->current.layer >= ZWLR_LAYER_SHELL_V1_LAYER_TOP) { 1328 layer->current.keyboard_interactive
1296 seat->focused_layer = layer; 1329 == ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_EXCLUSIVE) {
1330 seat->has_exclusive_layer = true;
1297 } 1331 }
1298} 1332 if (seat->focused_layer == layer) {
1299
1300void seat_set_exclusive_client(struct sway_seat *seat,
1301 struct wl_client *client) {
1302 if (!client) {
1303 seat->exclusive_client = client;
1304 // Triggers a refocus of the topmost surface layer if necessary
1305 // TODO: Make layer surface focus per-output based on cursor position
1306 for (int i = 0; i < root->outputs->length; ++i) {
1307 struct sway_output *output = root->outputs->items[i];
1308 arrange_layers(output);
1309 }
1310 return; 1333 return;
1311 } 1334 }
1335 seat_set_focus_surface(seat, layer->surface, true);
1336 seat->focused_layer = layer;
1337}
1338
1339void seat_unfocus_unless_client(struct sway_seat *seat, struct wl_client *client) {
1312 if (seat->focused_layer) { 1340 if (seat->focused_layer) {
1313 if (wl_resource_get_client(seat->focused_layer->resource) != client) { 1341 if (wl_resource_get_client(seat->focused_layer->resource) != client) {
1314 seat_set_focus_layer(seat, NULL); 1342 seat_set_focus_layer(seat, NULL);
@@ -1335,7 +1363,6 @@ void seat_set_exclusive_client(struct sway_seat *seat,
1335 now.tv_nsec / 1000, point->touch_id); 1363 now.tv_nsec / 1000, point->touch_id);
1336 } 1364 }
1337 } 1365 }
1338 seat->exclusive_client = client;
1339} 1366}
1340 1367
1341struct sway_node *seat_get_focus_inactive(struct sway_seat *seat, 1368struct sway_node *seat_get_focus_inactive(struct sway_seat *seat,
@@ -1416,9 +1443,8 @@ struct sway_node *seat_get_focus(struct sway_seat *seat) {
1416 if (!seat->has_focus) { 1443 if (!seat->has_focus) {
1417 return NULL; 1444 return NULL;
1418 } 1445 }
1419 if (wl_list_empty(&seat->focus_stack)) { 1446 sway_assert(!wl_list_empty(&seat->focus_stack),
1420 return NULL; 1447 "focus_stack is empty, but has_focus is true");
1421 }
1422 struct sway_seat_node *current = 1448 struct sway_seat_node *current =
1423 wl_container_of(seat->focus_stack.next, current, link); 1449 wl_container_of(seat->focus_stack.next, current, link);
1424 return current->node; 1450 return current->node;
@@ -1503,7 +1529,7 @@ struct seat_config *seat_get_config_by_name(const char *name) {
1503} 1529}
1504 1530
1505void seat_pointer_notify_button(struct sway_seat *seat, uint32_t time_msec, 1531void seat_pointer_notify_button(struct sway_seat *seat, uint32_t time_msec,
1506 uint32_t button, enum wlr_button_state state) { 1532 uint32_t button, enum wl_pointer_button_state state) {
1507 seat->last_button_serial = wlr_seat_pointer_notify_button(seat->wlr_seat, 1533 seat->last_button_serial = wlr_seat_pointer_notify_button(seat->wlr_seat,
1508 time_msec, button, state); 1534 time_msec, button, state);
1509} 1535}
@@ -1540,7 +1566,7 @@ void seatop_unref(struct sway_seat *seat, struct sway_container *con) {
1540 1566
1541void seatop_button(struct sway_seat *seat, uint32_t time_msec, 1567void seatop_button(struct sway_seat *seat, uint32_t time_msec,
1542 struct wlr_input_device *device, uint32_t button, 1568 struct wlr_input_device *device, uint32_t button,
1543 enum wlr_button_state state) { 1569 enum wl_pointer_button_state state) {
1544 if (seat->seatop_impl->button) { 1570 if (seat->seatop_impl->button) {
1545 seat->seatop_impl->button(seat, time_msec, device, button, state); 1571 seat->seatop_impl->button(seat, time_msec, device, button, state);
1546 } 1572 }
@@ -1553,12 +1579,38 @@ void seatop_pointer_motion(struct sway_seat *seat, uint32_t time_msec) {
1553} 1579}
1554 1580
1555void seatop_pointer_axis(struct sway_seat *seat, 1581void seatop_pointer_axis(struct sway_seat *seat,
1556 struct wlr_event_pointer_axis *event) { 1582 struct wlr_pointer_axis_event *event) {
1557 if (seat->seatop_impl->pointer_axis) { 1583 if (seat->seatop_impl->pointer_axis) {
1558 seat->seatop_impl->pointer_axis(seat, event); 1584 seat->seatop_impl->pointer_axis(seat, event);
1559 } 1585 }
1560} 1586}
1561 1587
1588void seatop_touch_motion(struct sway_seat *seat, struct wlr_touch_motion_event *event,
1589 double lx, double ly) {
1590 if (seat->seatop_impl->touch_motion) {
1591 seat->seatop_impl->touch_motion(seat, event, lx, ly);
1592 }
1593}
1594
1595void seatop_touch_up(struct sway_seat *seat, struct wlr_touch_up_event *event) {
1596 if (seat->seatop_impl->touch_up) {
1597 seat->seatop_impl->touch_up(seat, event);
1598 }
1599}
1600
1601void seatop_touch_down(struct sway_seat *seat, struct wlr_touch_down_event *event,
1602 double lx, double ly) {
1603 if (seat->seatop_impl->touch_down) {
1604 seat->seatop_impl->touch_down(seat, event, lx, ly);
1605 }
1606}
1607
1608void seatop_touch_cancel(struct sway_seat *seat, struct wlr_touch_cancel_event *event) {
1609 if (seat->seatop_impl->touch_cancel) {
1610 seat->seatop_impl->touch_cancel(seat, event);
1611 }
1612}
1613
1562void seatop_tablet_tool_tip(struct sway_seat *seat, 1614void seatop_tablet_tool_tip(struct sway_seat *seat,
1563 struct sway_tablet_tool *tool, uint32_t time_msec, 1615 struct sway_tablet_tool *tool, uint32_t time_msec,
1564 enum wlr_tablet_tool_tip_state state) { 1616 enum wlr_tablet_tool_tip_state state) {
@@ -1576,6 +1628,62 @@ void seatop_tablet_tool_motion(struct sway_seat *seat,
1576 } 1628 }
1577} 1629}
1578 1630
1631void seatop_hold_begin(struct sway_seat *seat,
1632 struct wlr_pointer_hold_begin_event *event) {
1633 if (seat->seatop_impl->hold_begin) {
1634 seat->seatop_impl->hold_begin(seat, event);
1635 }
1636}
1637
1638void seatop_hold_end(struct sway_seat *seat,
1639 struct wlr_pointer_hold_end_event *event) {
1640 if (seat->seatop_impl->hold_end) {
1641 seat->seatop_impl->hold_end(seat, event);
1642 }
1643}
1644
1645void seatop_pinch_begin(struct sway_seat *seat,
1646 struct wlr_pointer_pinch_begin_event *event) {
1647 if (seat->seatop_impl->pinch_begin) {
1648 seat->seatop_impl->pinch_begin(seat, event);
1649 }
1650}
1651
1652void seatop_pinch_update(struct sway_seat *seat,
1653 struct wlr_pointer_pinch_update_event *event) {
1654 if (seat->seatop_impl->pinch_update) {
1655 seat->seatop_impl->pinch_update(seat, event);
1656 }
1657}
1658
1659void seatop_pinch_end(struct sway_seat *seat,
1660 struct wlr_pointer_pinch_end_event *event) {
1661 if (seat->seatop_impl->pinch_end) {
1662 seat->seatop_impl->pinch_end(seat, event);
1663 }
1664}
1665
1666void seatop_swipe_begin(struct sway_seat *seat,
1667 struct wlr_pointer_swipe_begin_event *event) {
1668 if (seat->seatop_impl->swipe_begin) {
1669 seat->seatop_impl->swipe_begin(seat, event);
1670 }
1671}
1672
1673void seatop_swipe_update(struct sway_seat *seat,
1674 struct wlr_pointer_swipe_update_event *event) {
1675 if (seat->seatop_impl->swipe_update) {
1676 seat->seatop_impl->swipe_update(seat, event);
1677 }
1678}
1679
1680void seatop_swipe_end(struct sway_seat *seat,
1681 struct wlr_pointer_swipe_end_event *event) {
1682 if (seat->seatop_impl->swipe_end) {
1683 seat->seatop_impl->swipe_end(seat, event);
1684 }
1685}
1686
1579void seatop_rebase(struct sway_seat *seat, uint32_t time_msec) { 1687void seatop_rebase(struct sway_seat *seat, uint32_t time_msec) {
1580 if (seat->seatop_impl->rebase) { 1688 if (seat->seatop_impl->rebase) {
1581 seat->seatop_impl->rebase(seat, time_msec); 1689 seat->seatop_impl->rebase(seat, time_msec);
@@ -1591,13 +1699,6 @@ void seatop_end(struct sway_seat *seat) {
1591 seat->seatop_impl = NULL; 1699 seat->seatop_impl = NULL;
1592} 1700}
1593 1701
1594void seatop_render(struct sway_seat *seat, struct sway_output *output,
1595 pixman_region32_t *damage) {
1596 if (seat->seatop_impl->render) {
1597 seat->seatop_impl->render(seat, output, damage);
1598 }
1599}
1600
1601bool seatop_allows_set_cursor(struct sway_seat *seat) { 1702bool seatop_allows_set_cursor(struct sway_seat *seat) {
1602 return seat->seatop_impl->allow_set_cursor; 1703 return seat->seatop_impl->allow_set_cursor;
1603} 1704}
diff --git a/sway/input/seatop_default.c b/sway/input/seatop_default.c
index f9eb8c8a..e01fa933 100644
--- a/sway/input/seatop_default.c
+++ b/sway/input/seatop_default.c
@@ -1,18 +1,21 @@
1#define _POSIX_C_SOURCE 200809L
2#include <float.h> 1#include <float.h>
3#include <libevdev/libevdev.h> 2#include <libevdev/libevdev.h>
4#include <wlr/types/wlr_cursor.h> 3#include <wlr/types/wlr_cursor.h>
4#include <wlr/types/wlr_subcompositor.h>
5#include <wlr/types/wlr_tablet_v2.h> 5#include <wlr/types/wlr_tablet_v2.h>
6#include <wlr/types/wlr_xcursor_manager.h> 6#include <wlr/types/wlr_xcursor_manager.h>
7#include "gesture.h"
7#include "sway/desktop/transaction.h" 8#include "sway/desktop/transaction.h"
8#include "sway/input/cursor.h" 9#include "sway/input/cursor.h"
9#include "sway/input/seat.h" 10#include "sway/input/seat.h"
10#include "sway/input/tablet.h" 11#include "sway/input/tablet.h"
12#include "sway/layers.h"
11#include "sway/output.h" 13#include "sway/output.h"
14#include "sway/scene_descriptor.h"
12#include "sway/tree/view.h" 15#include "sway/tree/view.h"
13#include "sway/tree/workspace.h" 16#include "sway/tree/workspace.h"
14#include "log.h" 17#include "log.h"
15#if HAVE_XWAYLAND 18#if WLR_HAS_XWAYLAND
16#include "sway/xwayland.h" 19#include "sway/xwayland.h"
17#endif 20#endif
18 21
@@ -20,6 +23,7 @@ struct seatop_default_event {
20 struct sway_node *previous_node; 23 struct sway_node *previous_node;
21 uint32_t pressed_buttons[SWAY_CURSOR_PRESSED_BUTTONS_CAP]; 24 uint32_t pressed_buttons[SWAY_CURSOR_PRESSED_BUTTONS_CAP];
22 size_t pressed_button_count; 25 size_t pressed_button_count;
26 struct gesture_tracker gestures;
23}; 27};
24 28
25/*-----------------------------------------\ 29/*-----------------------------------------\
@@ -51,6 +55,9 @@ static bool edge_is_external(struct sway_container *cont, enum wlr_edges edge) {
51 while (cont) { 55 while (cont) {
52 if (container_parent_layout(cont) == layout) { 56 if (container_parent_layout(cont) == layout) {
53 list_t *siblings = container_get_siblings(cont); 57 list_t *siblings = container_get_siblings(cont);
58 if (!siblings) {
59 return false;
60 }
54 int index = list_find(siblings, cont); 61 int index = list_find(siblings, cont);
55 if (index > 0 && (edge == WLR_EDGE_LEFT || edge == WLR_EDGE_TOP)) { 62 if (index > 0 && (edge == WLR_EDGE_LEFT || edge == WLR_EDGE_TOP)) {
56 return false; 63 return false;
@@ -226,14 +233,15 @@ static void handle_tablet_tool_tip(struct sway_seat *seat,
226 struct sway_container *cont = node && node->type == N_CONTAINER ? 233 struct sway_container *cont = node && node->type == N_CONTAINER ?
227 node->sway_container : NULL; 234 node->sway_container : NULL;
228 235
229 if (wlr_surface_is_layer_surface(surface)) { 236 struct wlr_layer_surface_v1 *layer;
237#if WLR_HAS_XWAYLAND
238 struct wlr_xwayland_surface *xsurface;
239#endif
240 if ((layer = wlr_layer_surface_v1_try_from_wlr_surface(surface)) &&
241 layer->current.keyboard_interactive) {
230 // Handle tapping a layer surface 242 // Handle tapping a layer surface
231 struct wlr_layer_surface_v1 *layer = 243 seat_set_focus_layer(seat, layer);
232 wlr_layer_surface_v1_from_wlr_surface(surface); 244 transaction_commit_dirty();
233 if (layer->current.keyboard_interactive) {
234 seat_set_focus_layer(seat, layer);
235 transaction_commit_dirty();
236 }
237 } else if (cont) { 245 } else if (cont) {
238 bool is_floating_or_child = container_is_floating_or_child(cont); 246 bool is_floating_or_child = container_is_floating_or_child(cont);
239 bool is_fullscreen_or_child = container_is_fullscreen_or_child(cont); 247 bool is_fullscreen_or_child = container_is_fullscreen_or_child(cont);
@@ -258,20 +266,17 @@ static void handle_tablet_tool_tip(struct sway_seat *seat,
258 266
259 // Handle tapping on a container surface 267 // Handle tapping on a container surface
260 seat_set_focus_container(seat, cont); 268 seat_set_focus_container(seat, cont);
261 seatop_begin_down(seat, node->sway_container, time_msec, sx, sy); 269 seatop_begin_down(seat, node->sway_container, sx, sy);
262 } 270 }
263#if HAVE_XWAYLAND 271#if WLR_HAS_XWAYLAND
264 // Handle tapping on an xwayland unmanaged view 272 // Handle tapping on an xwayland unmanaged view
265 else if (wlr_surface_is_xwayland_surface(surface)) { 273 else if ((xsurface = wlr_xwayland_surface_try_from_wlr_surface(surface)) &&
266 struct wlr_xwayland_surface *xsurface = 274 xsurface->override_redirect &&
267 wlr_xwayland_surface_from_wlr_surface(surface); 275 wlr_xwayland_or_surface_wants_focus(xsurface)) {
268 if (xsurface->override_redirect && 276 struct wlr_xwayland *xwayland = server.xwayland.wlr_xwayland;
269 wlr_xwayland_or_surface_wants_focus(xsurface)) { 277 wlr_xwayland_set_seat(xwayland, seat->wlr_seat);
270 struct wlr_xwayland *xwayland = server.xwayland.wlr_xwayland; 278 seat_set_focus_surface(seat, xsurface->surface, false);
271 wlr_xwayland_set_seat(xwayland, seat->wlr_seat); 279 transaction_commit_dirty();
272 seat_set_focus_surface(seat, xsurface->surface, false);
273 transaction_commit_dirty();
274 }
275 } 280 }
276#endif 281#endif
277 282
@@ -285,7 +290,7 @@ static void handle_tablet_tool_tip(struct sway_seat *seat,
285 290
286static bool trigger_pointer_button_binding(struct sway_seat *seat, 291static bool trigger_pointer_button_binding(struct sway_seat *seat,
287 struct wlr_input_device *device, uint32_t button, 292 struct wlr_input_device *device, uint32_t button,
288 enum wlr_button_state state, uint32_t modifiers, 293 enum wl_pointer_button_state state, uint32_t modifiers,
289 bool on_titlebar, bool on_border, bool on_contents, bool on_workspace) { 294 bool on_titlebar, bool on_border, bool on_contents, bool on_workspace) {
290 // We can reach this for non-pointer devices if we're currently emulating 295 // We can reach this for non-pointer devices if we're currently emulating
291 // pointer input for one. Emulated input should not trigger bindings. The 296 // pointer input for one. Emulated input should not trigger bindings. The
@@ -299,7 +304,7 @@ static bool trigger_pointer_button_binding(struct sway_seat *seat,
299 char *device_identifier = device ? input_device_get_identifier(device) 304 char *device_identifier = device ? input_device_get_identifier(device)
300 : strdup("*"); 305 : strdup("*");
301 struct sway_binding *binding = NULL; 306 struct sway_binding *binding = NULL;
302 if (state == WLR_BUTTON_PRESSED) { 307 if (state == WL_POINTER_BUTTON_STATE_PRESSED) {
303 state_add_button(e, button); 308 state_add_button(e, button);
304 binding = get_active_mouse_binding(e, 309 binding = get_active_mouse_binding(e,
305 config->current_mode->mouse_bindings, modifiers, false, 310 config->current_mode->mouse_bindings, modifiers, false,
@@ -324,7 +329,7 @@ static bool trigger_pointer_button_binding(struct sway_seat *seat,
324 329
325static void handle_button(struct sway_seat *seat, uint32_t time_msec, 330static void handle_button(struct sway_seat *seat, uint32_t time_msec,
326 struct wlr_input_device *device, uint32_t button, 331 struct wlr_input_device *device, uint32_t button,
327 enum wlr_button_state state) { 332 enum wl_pointer_button_state state) {
328 struct sway_cursor *cursor = seat->cursor; 333 struct sway_cursor *cursor = seat->cursor;
329 334
330 // Determine what's under the cursor 335 // Determine what's under the cursor
@@ -357,7 +362,7 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec,
357 362
358 // Handle clicking an empty workspace 363 // Handle clicking an empty workspace
359 if (node && node->type == N_WORKSPACE) { 364 if (node && node->type == N_WORKSPACE) {
360 if (state == WLR_BUTTON_PRESSED) { 365 if (state == WL_POINTER_BUTTON_STATE_PRESSED) {
361 seat_set_focus(seat, node); 366 seat_set_focus(seat, node);
362 transaction_commit_dirty(); 367 transaction_commit_dirty();
363 } 368 }
@@ -365,21 +370,23 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec,
365 return; 370 return;
366 } 371 }
367 372
368 // Handle clicking a layer surface 373 // Handle clicking a layer surface and its popups/subsurfaces
369 if (surface && wlr_surface_is_layer_surface(surface)) { 374 struct wlr_layer_surface_v1 *layer = NULL;
370 struct wlr_layer_surface_v1 *layer = 375 if ((layer = toplevel_layer_surface_from_surface(surface))) {
371 wlr_layer_surface_v1_from_wlr_surface(surface);
372 if (layer->current.keyboard_interactive) { 376 if (layer->current.keyboard_interactive) {
373 seat_set_focus_layer(seat, layer); 377 seat_set_focus_layer(seat, layer);
374 transaction_commit_dirty(); 378 transaction_commit_dirty();
375 } 379 }
380 if (state == WL_POINTER_BUTTON_STATE_PRESSED) {
381 seatop_begin_down_on_surface(seat, surface, sx, sy);
382 }
376 seat_pointer_notify_button(seat, time_msec, button, state); 383 seat_pointer_notify_button(seat, time_msec, button, state);
377 return; 384 return;
378 } 385 }
379 386
380 // Handle tiling resize via border 387 // Handle tiling resize via border
381 if (cont && resize_edge && button == BTN_LEFT && 388 if (cont && resize_edge && button == BTN_LEFT &&
382 state == WLR_BUTTON_PRESSED && !is_floating) { 389 state == WL_POINTER_BUTTON_STATE_PRESSED && !is_floating) {
383 // If a resize is triggered on a tabbed or stacked container, change 390 // If a resize is triggered on a tabbed or stacked container, change
384 // focus to the tab which already had inactive focus -- otherwise, we'd 391 // focus to the tab which already had inactive focus -- otherwise, we'd
385 // change the active tab when the user probably just wanted to resize. 392 // change the active tab when the user probably just wanted to resize.
@@ -397,7 +404,7 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec,
397 // Handle tiling resize via mod 404 // Handle tiling resize via mod
398 bool mod_pressed = modifiers & config->floating_mod; 405 bool mod_pressed = modifiers & config->floating_mod;
399 if (cont && !is_floating_or_child && mod_pressed && 406 if (cont && !is_floating_or_child && mod_pressed &&
400 state == WLR_BUTTON_PRESSED) { 407 state == WL_POINTER_BUTTON_STATE_PRESSED) {
401 uint32_t btn_resize = config->floating_mod_inverse ? 408 uint32_t btn_resize = config->floating_mod_inverse ?
402 BTN_LEFT : BTN_RIGHT; 409 BTN_LEFT : BTN_RIGHT;
403 if (button == btn_resize) { 410 if (button == btn_resize) {
@@ -424,13 +431,31 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec,
424 } 431 }
425 } 432 }
426 433
434 // Handle changing focus when clicking on a container
435 if (cont && state == WL_POINTER_BUTTON_STATE_PRESSED) {
436 // Default case: focus the container that was just clicked.
437 node = &cont->node;
438
439 // If the container is a tab/stacked container and the click happened
440 // on a tab, switch to the tab. If the tab contents were already
441 // focused, focus the tab container itself. If the tab container was
442 // already focused, cycle back to focusing the tab contents.
443 if (on_titlebar) {
444 struct sway_container *focus = seat_get_focused_container(seat);
445 if (focus == cont || !container_has_ancestor(focus, cont)) {
446 node = seat_get_focus_inactive(seat, &cont->node);
447 }
448 }
449
450 seat_set_focus(seat, node);
451 transaction_commit_dirty();
452 }
453
427 // Handle beginning floating move 454 // Handle beginning floating move
428 if (cont && is_floating_or_child && !is_fullscreen_or_child && 455 if (cont && is_floating_or_child && !is_fullscreen_or_child &&
429 state == WLR_BUTTON_PRESSED) { 456 state == WL_POINTER_BUTTON_STATE_PRESSED) {
430 uint32_t btn_move = config->floating_mod_inverse ? BTN_RIGHT : BTN_LEFT; 457 uint32_t btn_move = config->floating_mod_inverse ? BTN_RIGHT : BTN_LEFT;
431 if (button == btn_move && (mod_pressed || on_titlebar)) { 458 if (button == btn_move && (mod_pressed || on_titlebar)) {
432 seat_set_focus_container(seat,
433 seat_get_focus_inactive_view(seat, &cont->node));
434 seatop_begin_move_floating(seat, container_toplevel_ancestor(cont)); 459 seatop_begin_move_floating(seat, container_toplevel_ancestor(cont));
435 return; 460 return;
436 } 461 }
@@ -438,9 +463,10 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec,
438 463
439 // Handle beginning floating resize 464 // Handle beginning floating resize
440 if (cont && is_floating_or_child && !is_fullscreen_or_child && 465 if (cont && is_floating_or_child && !is_fullscreen_or_child &&
441 state == WLR_BUTTON_PRESSED) { 466 state == WL_POINTER_BUTTON_STATE_PRESSED) {
442 // Via border 467 // Via border
443 if (button == BTN_LEFT && resize_edge != WLR_EDGE_NONE) { 468 if (button == BTN_LEFT && resize_edge != WLR_EDGE_NONE) {
469 seat_set_focus_container(seat, cont);
444 seatop_begin_resize_floating(seat, cont, resize_edge); 470 seatop_begin_resize_floating(seat, cont, resize_edge);
445 return; 471 return;
446 } 472 }
@@ -455,6 +481,7 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec,
455 WLR_EDGE_RIGHT : WLR_EDGE_LEFT; 481 WLR_EDGE_RIGHT : WLR_EDGE_LEFT;
456 edge |= cursor->cursor->y > floater->pending.y + floater->pending.height / 2 ? 482 edge |= cursor->cursor->y > floater->pending.y + floater->pending.height / 2 ?
457 WLR_EDGE_BOTTOM : WLR_EDGE_TOP; 483 WLR_EDGE_BOTTOM : WLR_EDGE_TOP;
484 seat_set_focus_container(seat, floater);
458 seatop_begin_resize_floating(seat, floater, edge); 485 seatop_begin_resize_floating(seat, floater, edge);
459 return; 486 return;
460 } 487 }
@@ -462,55 +489,43 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec,
462 489
463 // Handle moving a tiling container 490 // Handle moving a tiling container
464 if (config->tiling_drag && (mod_pressed || on_titlebar) && 491 if (config->tiling_drag && (mod_pressed || on_titlebar) &&
465 state == WLR_BUTTON_PRESSED && !is_floating_or_child && 492 state == WL_POINTER_BUTTON_STATE_PRESSED && !is_floating_or_child &&
466 cont && cont->pending.fullscreen_mode == FULLSCREEN_NONE) { 493 cont && cont->pending.fullscreen_mode == FULLSCREEN_NONE) {
467 struct sway_container *focus = seat_get_focused_container(seat); 494 // If moving a container by its title bar, use a threshold for the drag
468 bool focused = focus == cont || container_has_ancestor(focus, cont);
469 if (on_titlebar && !focused) {
470 node = seat_get_focus_inactive(seat, &cont->node);
471 seat_set_focus(seat, node);
472 }
473
474 // If moving a container by it's title bar, use a threshold for the drag
475 if (!mod_pressed && config->tiling_drag_threshold > 0) { 495 if (!mod_pressed && config->tiling_drag_threshold > 0) {
476 seatop_begin_move_tiling_threshold(seat, cont); 496 seatop_begin_move_tiling_threshold(seat, cont);
477 } else { 497 } else {
478 seatop_begin_move_tiling(seat, cont); 498 seatop_begin_move_tiling(seat, cont);
479 } 499 }
500
480 return; 501 return;
481 } 502 }
482 503
483 // Handle mousedown on a container surface 504 // Handle mousedown on a container surface
484 if (surface && cont && state == WLR_BUTTON_PRESSED) { 505 if (surface && cont && state == WL_POINTER_BUTTON_STATE_PRESSED) {
485 seat_set_focus_container(seat, cont); 506 seatop_begin_down(seat, cont, sx, sy);
486 seatop_begin_down(seat, cont, time_msec, sx, sy); 507 seat_pointer_notify_button(seat, time_msec, button, WL_POINTER_BUTTON_STATE_PRESSED);
487 seat_pointer_notify_button(seat, time_msec, button, WLR_BUTTON_PRESSED);
488 return; 508 return;
489 } 509 }
490 510
491 // Handle clicking a container surface or decorations 511 // Handle clicking a container surface or decorations
492 if (cont && state == WLR_BUTTON_PRESSED) { 512 if (cont && state == WL_POINTER_BUTTON_STATE_PRESSED) {
493 node = seat_get_focus_inactive(seat, &cont->node);
494 seat_set_focus(seat, node);
495 transaction_commit_dirty();
496 seat_pointer_notify_button(seat, time_msec, button, state); 513 seat_pointer_notify_button(seat, time_msec, button, state);
497 return; 514 return;
498 } 515 }
499 516
500#if HAVE_XWAYLAND 517#if WLR_HAS_XWAYLAND
501 // Handle clicking on xwayland unmanaged view 518 // Handle clicking on xwayland unmanaged view
502 if (surface && wlr_surface_is_xwayland_surface(surface)) { 519 struct wlr_xwayland_surface *xsurface;
503 struct wlr_xwayland_surface *xsurface = 520 if (surface &&
504 wlr_xwayland_surface_from_wlr_surface(surface); 521 (xsurface = wlr_xwayland_surface_try_from_wlr_surface(surface)) &&
505 if (xsurface->override_redirect && 522 xsurface->override_redirect &&
506 wlr_xwayland_or_surface_wants_focus(xsurface)) { 523 wlr_xwayland_or_surface_wants_focus(xsurface)) {
507 struct wlr_xwayland *xwayland = server.xwayland.wlr_xwayland; 524 struct wlr_xwayland *xwayland = server.xwayland.wlr_xwayland;
508 wlr_xwayland_set_seat(xwayland, seat->wlr_seat); 525 wlr_xwayland_set_seat(xwayland, seat->wlr_seat);
509 seat_set_focus_surface(seat, xsurface->surface, false); 526 seat_set_focus_surface(seat, xsurface->surface, false);
510 transaction_commit_dirty(); 527 transaction_commit_dirty();
511 seat_pointer_notify_button(seat, time_msec, button, state); 528 seat_pointer_notify_button(seat, time_msec, button, state);
512 return;
513 }
514 } 529 }
515#endif 530#endif
516 531
@@ -533,6 +548,21 @@ static void check_focus_follows_mouse(struct sway_seat *seat,
533 if (wlr_output == NULL) { 548 if (wlr_output == NULL) {
534 return; 549 return;
535 } 550 }
551
552 struct wlr_surface *surface = NULL;
553 double sx, sy;
554 node_at_coords(seat, seat->cursor->cursor->x, seat->cursor->cursor->y,
555 &surface, &sx, &sy);
556
557 // Focus topmost layer surface
558 struct wlr_layer_surface_v1 *layer = NULL;
559 if ((layer = toplevel_layer_surface_from_surface(surface)) &&
560 layer->current.keyboard_interactive) {
561 seat_set_focus_layer(seat, layer);
562 transaction_commit_dirty();
563 return;
564 }
565
536 struct sway_output *hovered_output = wlr_output->data; 566 struct sway_output *hovered_output = wlr_output->data;
537 if (focus && hovered_output != node_get_output(focus)) { 567 if (focus && hovered_output != node_get_output(focus)) {
538 struct sway_workspace *ws = output_get_active_workspace(hovered_output); 568 struct sway_workspace *ws = output_get_active_workspace(hovered_output);
@@ -593,12 +623,7 @@ static void handle_pointer_motion(struct sway_seat *seat, uint32_t time_msec) {
593 wlr_seat_pointer_notify_clear_focus(seat->wlr_seat); 623 wlr_seat_pointer_notify_clear_focus(seat->wlr_seat);
594 } 624 }
595 625
596 struct sway_drag_icon *drag_icon; 626 drag_icons_update_position(seat);
597 wl_list_for_each(drag_icon, &root->drag_icons, link) {
598 if (drag_icon->seat == seat) {
599 drag_icon_update_position(drag_icon);
600 }
601 }
602 627
603 e->previous_node = node; 628 e->previous_node = node;
604} 629}
@@ -628,25 +653,50 @@ static void handle_tablet_tool_motion(struct sway_seat *seat,
628 wlr_tablet_v2_tablet_tool_notify_proximity_out(tool->tablet_v2_tool); 653 wlr_tablet_v2_tablet_tool_notify_proximity_out(tool->tablet_v2_tool);
629 } 654 }
630 655
631 struct sway_drag_icon *drag_icon; 656 drag_icons_update_position(seat);
632 wl_list_for_each(drag_icon, &root->drag_icons, link) {
633 if (drag_icon->seat == seat) {
634 drag_icon_update_position(drag_icon);
635 }
636 }
637 657
638 e->previous_node = node; 658 e->previous_node = node;
639} 659}
640 660
661static void handle_touch_down(struct sway_seat *seat,
662 struct wlr_touch_down_event *event, double lx, double ly) {
663 struct wlr_surface *surface = NULL;
664 struct wlr_seat *wlr_seat = seat->wlr_seat;
665 struct sway_cursor *cursor = seat->cursor;
666 double sx, sy;
667 node_at_coords(seat, seat->touch_x, seat->touch_y, &surface, &sx, &sy);
668
669 if (surface && wlr_surface_accepts_touch(wlr_seat, surface)) {
670 if (seat_is_input_allowed(seat, surface)) {
671 cursor->simulating_pointer_from_touch = false;
672 seatop_begin_touch_down(seat, surface, event, sx, sy, lx, ly);
673 }
674 } else if (!cursor->simulating_pointer_from_touch &&
675 (!surface || seat_is_input_allowed(seat, surface))) {
676 // Fallback to cursor simulation.
677 // The pointer_touch_id state is needed, so drags are not aborted when over
678 // a surface supporting touch and multi touch events don't interfere.
679 cursor->simulating_pointer_from_touch = true;
680 cursor->pointer_touch_id = seat->touch_id;
681 double dx, dy;
682 dx = seat->touch_x - cursor->cursor->x;
683 dy = seat->touch_y - cursor->cursor->y;
684 pointer_motion(cursor, event->time_msec, &event->touch->base, dx, dy,
685 dx, dy);
686 dispatch_cursor_button(cursor, &event->touch->base, event->time_msec,
687 BTN_LEFT, WL_POINTER_BUTTON_STATE_PRESSED);
688 }
689}
690
641/*----------------------------------------\ 691/*----------------------------------------\
642 * Functions used by handle_pointer_axis / 692 * Functions used by handle_pointer_axis /
643 *--------------------------------------*/ 693 *--------------------------------------*/
644 694
645static uint32_t wl_axis_to_button(struct wlr_event_pointer_axis *event) { 695static uint32_t wl_axis_to_button(struct wlr_pointer_axis_event *event) {
646 switch (event->orientation) { 696 switch (event->orientation) {
647 case WLR_AXIS_ORIENTATION_VERTICAL: 697 case WL_POINTER_AXIS_VERTICAL_SCROLL:
648 return event->delta < 0 ? SWAY_SCROLL_UP : SWAY_SCROLL_DOWN; 698 return event->delta < 0 ? SWAY_SCROLL_UP : SWAY_SCROLL_DOWN;
649 case WLR_AXIS_ORIENTATION_HORIZONTAL: 699 case WL_POINTER_AXIS_HORIZONTAL_SCROLL:
650 return event->delta < 0 ? SWAY_SCROLL_LEFT : SWAY_SCROLL_RIGHT; 700 return event->delta < 0 ? SWAY_SCROLL_LEFT : SWAY_SCROLL_RIGHT;
651 default: 701 default:
652 sway_log(SWAY_DEBUG, "Unknown axis orientation"); 702 sway_log(SWAY_DEBUG, "Unknown axis orientation");
@@ -655,9 +705,9 @@ static uint32_t wl_axis_to_button(struct wlr_event_pointer_axis *event) {
655} 705}
656 706
657static void handle_pointer_axis(struct sway_seat *seat, 707static void handle_pointer_axis(struct sway_seat *seat,
658 struct wlr_event_pointer_axis *event) { 708 struct wlr_pointer_axis_event *event) {
659 struct sway_input_device *input_device = 709 struct sway_input_device *input_device =
660 event->device ? event->device->data : NULL; 710 event->pointer ? event->pointer->base.data : NULL;
661 struct input_config *ic = 711 struct input_config *ic =
662 input_device ? input_device_get_config(input_device) : NULL; 712 input_device ? input_device_get_config(input_device) : NULL;
663 struct sway_cursor *cursor = seat->cursor; 713 struct sway_cursor *cursor = seat->cursor;
@@ -703,6 +753,7 @@ static void handle_pointer_axis(struct sway_seat *seat,
703 753
704 // Scrolling on a tabbed or stacked title bar (handled as press event) 754 // Scrolling on a tabbed or stacked title bar (handled as press event)
705 if (!handled && (on_titlebar || on_titlebar_border)) { 755 if (!handled && (on_titlebar || on_titlebar_border)) {
756 struct sway_node *new_focus;
706 enum sway_container_layout layout = container_parent_layout(cont); 757 enum sway_container_layout layout = container_parent_layout(cont);
707 if (layout == L_TABBED || layout == L_STACKED) { 758 if (layout == L_TABBED || layout == L_STACKED) {
708 struct sway_node *tabcontainer = node_get_parent(node); 759 struct sway_node *tabcontainer = node_get_parent(node);
@@ -710,7 +761,7 @@ static void handle_pointer_axis(struct sway_seat *seat,
710 seat_get_active_tiling_child(seat, tabcontainer); 761 seat_get_active_tiling_child(seat, tabcontainer);
711 list_t *siblings = container_get_siblings(cont); 762 list_t *siblings = container_get_siblings(cont);
712 int desired = list_find(siblings, active->sway_container) + 763 int desired = list_find(siblings, active->sway_container) +
713 round(scroll_factor * event->delta_discrete); 764 roundf(scroll_factor * event->delta_discrete / WLR_POINTER_AXIS_DISCRETE_STEP);
714 if (desired < 0) { 765 if (desired < 0) {
715 desired = 0; 766 desired = 0;
716 } else if (desired >= siblings->length) { 767 } else if (desired >= siblings->length) {
@@ -719,14 +770,16 @@ static void handle_pointer_axis(struct sway_seat *seat,
719 770
720 struct sway_container *new_sibling_con = siblings->items[desired]; 771 struct sway_container *new_sibling_con = siblings->items[desired];
721 struct sway_node *new_sibling = &new_sibling_con->node; 772 struct sway_node *new_sibling = &new_sibling_con->node;
722 struct sway_node *new_focus =
723 seat_get_focus_inactive(seat, new_sibling);
724 // Use the focused child of the tabbed/stacked container, not the 773 // Use the focused child of the tabbed/stacked container, not the
725 // container the user scrolled on. 774 // container the user scrolled on.
726 seat_set_focus(seat, new_focus); 775 new_focus = seat_get_focus_inactive(seat, new_sibling);
727 transaction_commit_dirty(); 776 } else {
728 handled = true; 777 new_focus = seat_get_focus_inactive(seat, &cont->node);
729 } 778 }
779
780 seat_set_focus(seat, new_focus);
781 transaction_commit_dirty();
782 handled = true;
730 } 783 }
731 784
732 // Handle mouse bindings - x11 mouse buttons 4-7 - release event 785 // Handle mouse bindings - x11 mouse buttons 4-7 - release event
@@ -742,8 +795,307 @@ static void handle_pointer_axis(struct sway_seat *seat,
742 795
743 if (!handled) { 796 if (!handled) {
744 wlr_seat_pointer_notify_axis(cursor->seat->wlr_seat, event->time_msec, 797 wlr_seat_pointer_notify_axis(cursor->seat->wlr_seat, event->time_msec,
745 event->orientation, scroll_factor * event->delta, 798 event->orientation, scroll_factor * event->delta,
746 round(scroll_factor * event->delta_discrete), event->source); 799 roundf(scroll_factor * event->delta_discrete), event->source,
800 event->relative_direction);
801 }
802}
803
804/*------------------------------------\
805 * Functions used by gesture support /
806 *----------------------------------*/
807
808/**
809 * Check gesture binding for a specific gesture type and finger count.
810 * Returns true if binding is present, false otherwise
811 */
812static bool gesture_binding_check(list_t *bindings, enum gesture_type type,
813 uint8_t fingers, struct sway_input_device *device) {
814 char *input =
815 device ? input_device_get_identifier(device->wlr_device) : strdup("*");
816
817 for (int i = 0; i < bindings->length; ++i) {
818 struct sway_gesture_binding *binding = bindings->items[i];
819
820 // Check type and finger count
821 if (!gesture_check(&binding->gesture, type, fingers)) {
822 continue;
823 }
824
825 // Check that input matches
826 if (strcmp(binding->input, "*") != 0 &&
827 strcmp(binding->input, input) != 0) {
828 continue;
829 }
830
831 free(input);
832
833 return true;
834 }
835
836 free(input);
837
838 return false;
839}
840
841/**
842 * Return the gesture binding which matches gesture type, finger count
843 * and direction, otherwise return null.
844 */
845static struct sway_gesture_binding* gesture_binding_match(
846 list_t *bindings, struct gesture *gesture, const char *input) {
847 struct sway_gesture_binding *current = NULL;
848
849 // Find best matching binding
850 for (int i = 0; i < bindings->length; ++i) {
851 struct sway_gesture_binding *binding = bindings->items[i];
852 bool exact = binding->flags & BINDING_EXACT;
853
854 // Check gesture matching
855 if (!gesture_match(&binding->gesture, gesture, exact)) {
856 continue;
857 }
858
859 // Check input matching
860 if (strcmp(binding->input, "*") != 0 &&
861 strcmp(binding->input, input) != 0) {
862 continue;
863 }
864
865 // If we already have a match ...
866 if (current) {
867 // ... check if input matching is equivalent
868 if (strcmp(current->input, binding->input) == 0) {
869
870 // ... - do not override an exact binding
871 if (!exact && current->flags & BINDING_EXACT) {
872 continue;
873 }
874
875 // ... - and ensure direction matching is better or equal
876 if (gesture_compare(&current->gesture, &binding->gesture) > 0) {
877 continue;
878 }
879 } else if (strcmp(binding->input, "*") == 0) {
880 // ... do not accept worse input match
881 continue;
882 }
883 }
884
885 // Accept newer or better match
886 current = binding;
887
888 // If exact binding and input is found, quit search
889 if (strcmp(current->input, input) == 0 &&
890 gesture_compare(&current->gesture, gesture) == 0) {
891 break;
892 }
893 } // for all gesture bindings
894
895 return current;
896}
897
898// Wrapper around gesture_tracker_end to use tracker with sway bindings
899static struct sway_gesture_binding* gesture_tracker_end_and_match(
900 struct gesture_tracker *tracker, struct sway_input_device* device) {
901 // Determine name of input that received gesture
902 char *input = device
903 ? input_device_get_identifier(device->wlr_device)
904 : strdup("*");
905
906 // Match tracking result to binding
907 struct gesture *gesture = gesture_tracker_end(tracker);
908 struct sway_gesture_binding *binding = gesture_binding_match(
909 config->current_mode->gesture_bindings, gesture, input);
910 free(gesture);
911 free(input);
912
913 return binding;
914}
915
916// Small wrapper around seat_execute_command to work on gesture bindings
917static void gesture_binding_execute(struct sway_seat *seat,
918 struct sway_gesture_binding *binding) {
919 struct sway_binding *dummy_binding =
920 calloc(1, sizeof(struct sway_binding));
921 dummy_binding->type = BINDING_GESTURE;
922 dummy_binding->command = binding->command;
923
924 char *description = gesture_to_string(&binding->gesture);
925 sway_log(SWAY_DEBUG, "executing gesture binding: %s", description);
926 free(description);
927
928 seat_execute_command(seat, dummy_binding);
929
930 free(dummy_binding);
931}
932
933static void handle_hold_begin(struct sway_seat *seat,
934 struct wlr_pointer_hold_begin_event *event) {
935 // Start tracking gesture if there is a matching binding ...
936 struct sway_input_device *device =
937 event->pointer ? event->pointer->base.data : NULL;
938 list_t *bindings = config->current_mode->gesture_bindings;
939 if (gesture_binding_check(bindings, GESTURE_TYPE_HOLD, event->fingers, device)) {
940 struct seatop_default_event *seatop = seat->seatop_data;
941 gesture_tracker_begin(&seatop->gestures, GESTURE_TYPE_HOLD, event->fingers);
942 } else {
943 // ... otherwise forward to client
944 struct sway_cursor *cursor = seat->cursor;
945 wlr_pointer_gestures_v1_send_hold_begin(
946 server.input->pointer_gestures, cursor->seat->wlr_seat,
947 event->time_msec, event->fingers);
948 }
949}
950
951static void handle_hold_end(struct sway_seat *seat,
952 struct wlr_pointer_hold_end_event *event) {
953 // Ensure that gesture is being tracked and was not cancelled
954 struct seatop_default_event *seatop = seat->seatop_data;
955 if (!gesture_tracker_check(&seatop->gestures, GESTURE_TYPE_HOLD)) {
956 struct sway_cursor *cursor = seat->cursor;
957 wlr_pointer_gestures_v1_send_hold_end(
958 server.input->pointer_gestures, cursor->seat->wlr_seat,
959 event->time_msec, event->cancelled);
960 return;
961 }
962 if (event->cancelled) {
963 gesture_tracker_cancel(&seatop->gestures);
964 return;
965 }
966
967 // End gesture tracking and execute matched binding
968 struct sway_input_device *device =
969 event->pointer ? event->pointer->base.data : NULL;
970 struct sway_gesture_binding *binding = gesture_tracker_end_and_match(
971 &seatop->gestures, device);
972
973 if (binding) {
974 gesture_binding_execute(seat, binding);
975 }
976}
977
978static void handle_pinch_begin(struct sway_seat *seat,
979 struct wlr_pointer_pinch_begin_event *event) {
980 // Start tracking gesture if there is a matching binding ...
981 struct sway_input_device *device =
982 event->pointer ? event->pointer->base.data : NULL;
983 list_t *bindings = config->current_mode->gesture_bindings;
984 if (gesture_binding_check(bindings, GESTURE_TYPE_PINCH, event->fingers, device)) {
985 struct seatop_default_event *seatop = seat->seatop_data;
986 gesture_tracker_begin(&seatop->gestures, GESTURE_TYPE_PINCH, event->fingers);
987 } else {
988 // ... otherwise forward to client
989 struct sway_cursor *cursor = seat->cursor;
990 wlr_pointer_gestures_v1_send_pinch_begin(
991 server.input->pointer_gestures, cursor->seat->wlr_seat,
992 event->time_msec, event->fingers);
993 }
994}
995
996static void handle_pinch_update(struct sway_seat *seat,
997 struct wlr_pointer_pinch_update_event *event) {
998 // Update any ongoing tracking ...
999 struct seatop_default_event *seatop = seat->seatop_data;
1000 if (gesture_tracker_check(&seatop->gestures, GESTURE_TYPE_PINCH)) {
1001 gesture_tracker_update(&seatop->gestures, event->dx, event->dy,
1002 event->scale, event->rotation);
1003 } else {
1004 // ... otherwise forward to client
1005 struct sway_cursor *cursor = seat->cursor;
1006 wlr_pointer_gestures_v1_send_pinch_update(
1007 server.input->pointer_gestures,
1008 cursor->seat->wlr_seat,
1009 event->time_msec, event->dx, event->dy,
1010 event->scale, event->rotation);
1011 }
1012}
1013
1014static void handle_pinch_end(struct sway_seat *seat,
1015 struct wlr_pointer_pinch_end_event *event) {
1016 // Ensure that gesture is being tracked and was not cancelled
1017 struct seatop_default_event *seatop = seat->seatop_data;
1018 if (!gesture_tracker_check(&seatop->gestures, GESTURE_TYPE_PINCH)) {
1019 struct sway_cursor *cursor = seat->cursor;
1020 wlr_pointer_gestures_v1_send_pinch_end(
1021 server.input->pointer_gestures, cursor->seat->wlr_seat,
1022 event->time_msec, event->cancelled);
1023 return;
1024 }
1025 if (event->cancelled) {
1026 gesture_tracker_cancel(&seatop->gestures);
1027 return;
1028 }
1029
1030 // End gesture tracking and execute matched binding
1031 struct sway_input_device *device =
1032 event->pointer ? event->pointer->base.data : NULL;
1033 struct sway_gesture_binding *binding = gesture_tracker_end_and_match(
1034 &seatop->gestures, device);
1035
1036 if (binding) {
1037 gesture_binding_execute(seat, binding);
1038 }
1039}
1040
1041static void handle_swipe_begin(struct sway_seat *seat,
1042 struct wlr_pointer_swipe_begin_event *event) {
1043 // Start tracking gesture if there is a matching binding ...
1044 struct sway_input_device *device =
1045 event->pointer ? event->pointer->base.data : NULL;
1046 list_t *bindings = config->current_mode->gesture_bindings;
1047 if (gesture_binding_check(bindings, GESTURE_TYPE_SWIPE, event->fingers, device)) {
1048 struct seatop_default_event *seatop = seat->seatop_data;
1049 gesture_tracker_begin(&seatop->gestures, GESTURE_TYPE_SWIPE, event->fingers);
1050 } else {
1051 // ... otherwise forward to client
1052 struct sway_cursor *cursor = seat->cursor;
1053 wlr_pointer_gestures_v1_send_swipe_begin(
1054 server.input->pointer_gestures, cursor->seat->wlr_seat,
1055 event->time_msec, event->fingers);
1056 }
1057}
1058
1059static void handle_swipe_update(struct sway_seat *seat,
1060 struct wlr_pointer_swipe_update_event *event) {
1061
1062 // Update any ongoing tracking ...
1063 struct seatop_default_event *seatop = seat->seatop_data;
1064 if (gesture_tracker_check(&seatop->gestures, GESTURE_TYPE_SWIPE)) {
1065 gesture_tracker_update(&seatop->gestures,
1066 event->dx, event->dy, NAN, NAN);
1067 } else {
1068 // ... otherwise forward to client
1069 struct sway_cursor *cursor = seat->cursor;
1070 wlr_pointer_gestures_v1_send_swipe_update(
1071 server.input->pointer_gestures, cursor->seat->wlr_seat,
1072 event->time_msec, event->dx, event->dy);
1073 }
1074}
1075
1076static void handle_swipe_end(struct sway_seat *seat,
1077 struct wlr_pointer_swipe_end_event *event) {
1078 // Ensure gesture is being tracked and was not cancelled
1079 struct seatop_default_event *seatop = seat->seatop_data;
1080 if (!gesture_tracker_check(&seatop->gestures, GESTURE_TYPE_SWIPE)) {
1081 struct sway_cursor *cursor = seat->cursor;
1082 wlr_pointer_gestures_v1_send_swipe_end(server.input->pointer_gestures,
1083 cursor->seat->wlr_seat, event->time_msec, event->cancelled);
1084 return;
1085 }
1086 if (event->cancelled) {
1087 gesture_tracker_cancel(&seatop->gestures);
1088 return;
1089 }
1090
1091 // End gesture tracking and execute matched binding
1092 struct sway_input_device *device =
1093 event->pointer ? event->pointer->base.data : NULL;
1094 struct sway_gesture_binding *binding = gesture_tracker_end_and_match(
1095 &seatop->gestures, device);
1096
1097 if (binding) {
1098 gesture_binding_execute(seat, binding);
747 } 1099 }
748} 1100}
749 1101
@@ -776,6 +1128,15 @@ static const struct sway_seatop_impl seatop_impl = {
776 .pointer_axis = handle_pointer_axis, 1128 .pointer_axis = handle_pointer_axis,
777 .tablet_tool_tip = handle_tablet_tool_tip, 1129 .tablet_tool_tip = handle_tablet_tool_tip,
778 .tablet_tool_motion = handle_tablet_tool_motion, 1130 .tablet_tool_motion = handle_tablet_tool_motion,
1131 .hold_begin = handle_hold_begin,
1132 .hold_end = handle_hold_end,
1133 .pinch_begin = handle_pinch_begin,
1134 .pinch_update = handle_pinch_update,
1135 .pinch_end = handle_pinch_end,
1136 .swipe_begin = handle_swipe_begin,
1137 .swipe_update = handle_swipe_update,
1138 .swipe_end = handle_swipe_end,
1139 .touch_down = handle_touch_down,
779 .rebase = handle_rebase, 1140 .rebase = handle_rebase,
780 .allow_set_cursor = true, 1141 .allow_set_cursor = true,
781}; 1142};
@@ -786,8 +1147,8 @@ void seatop_begin_default(struct sway_seat *seat) {
786 struct seatop_default_event *e = 1147 struct seatop_default_event *e =
787 calloc(1, sizeof(struct seatop_default_event)); 1148 calloc(1, sizeof(struct seatop_default_event));
788 sway_assert(e, "Unable to allocate seatop_default_event"); 1149 sway_assert(e, "Unable to allocate seatop_default_event");
1150
789 seat->seatop_impl = &seatop_impl; 1151 seat->seatop_impl = &seatop_impl;
790 seat->seatop_data = e; 1152 seat->seatop_data = e;
791
792 seatop_rebase(seat, 0); 1153 seatop_rebase(seat, 0);
793} 1154}
diff --git a/sway/input/seatop_down.c b/sway/input/seatop_down.c
index 844cf5ab..340e334b 100644
--- a/sway/input/seatop_down.c
+++ b/sway/input/seatop_down.c
@@ -1,23 +1,138 @@
1#define _POSIX_C_SOURCE 200809L
2#include <float.h> 1#include <float.h>
3#include <wlr/types/wlr_cursor.h> 2#include <wlr/types/wlr_cursor.h>
4#include <wlr/types/wlr_tablet_v2.h> 3#include <wlr/types/wlr_tablet_v2.h>
4#include <wlr/types/wlr_touch.h>
5#include "sway/input/cursor.h" 5#include "sway/input/cursor.h"
6#include "sway/input/seat.h" 6#include "sway/input/seat.h"
7#include "sway/tree/view.h" 7#include "sway/tree/view.h"
8#include "sway/desktop/transaction.h" 8#include "sway/desktop/transaction.h"
9#include "log.h" 9#include "log.h"
10 10
11struct seatop_touch_point_event {
12 double ref_lx, ref_ly; // touch's x/y at start of op
13 double ref_con_lx, ref_con_ly; // container's x/y at start of op
14 int32_t touch_id;
15 struct wl_list link;
16};
17
11struct seatop_down_event { 18struct seatop_down_event {
12 struct sway_container *con; 19 struct sway_container *con;
20 struct sway_seat *seat;
21 struct wl_listener surface_destroy;
22 struct wlr_surface *surface;
13 double ref_lx, ref_ly; // cursor's x/y at start of op 23 double ref_lx, ref_ly; // cursor's x/y at start of op
14 double ref_con_lx, ref_con_ly; // container's x/y at start of op 24 double ref_con_lx, ref_con_ly; // container's x/y at start of op
25 struct wl_list point_events; // seatop_touch_point_event::link
15}; 26};
16 27
28static void handle_touch_motion(struct sway_seat *seat,
29 struct wlr_touch_motion_event *event, double lx, double ly) {
30 struct seatop_down_event *e = seat->seatop_data;
31
32 struct seatop_touch_point_event *point_event;
33 bool found = false;
34 wl_list_for_each(point_event, &e->point_events, link) {
35 if (point_event->touch_id == event->touch_id) {
36 found = true;
37 break;
38 }
39 }
40 if (!found) {
41 return; // Probably not a point_event from this seatop_down
42 }
43
44 double moved_x = lx - point_event->ref_lx;
45 double moved_y = ly - point_event->ref_ly;
46 double sx = point_event->ref_con_lx + moved_x;
47 double sy = point_event->ref_con_ly + moved_y;
48
49 wlr_seat_touch_notify_motion(seat->wlr_seat, event->time_msec,
50 event->touch_id, sx, sy);
51}
52
53static void handle_touch_up(struct sway_seat *seat,
54 struct wlr_touch_up_event *event) {
55 struct seatop_down_event *e = seat->seatop_data;
56 struct seatop_touch_point_event *point_event, *tmp;
57
58 wl_list_for_each_safe(point_event, tmp, &e->point_events, link) {
59 if (point_event->touch_id == event->touch_id) {
60 wl_list_remove(&point_event->link);
61 free(point_event);
62 break;
63 }
64 }
65
66 wlr_seat_touch_notify_up(seat->wlr_seat, event->time_msec, event->touch_id);
67
68 if (wl_list_empty(&e->point_events)) {
69 seatop_begin_default(seat);
70 }
71}
72
73static void handle_touch_down(struct sway_seat *seat,
74 struct wlr_touch_down_event *event, double lx, double ly) {
75 struct seatop_down_event *e = seat->seatop_data;
76 double sx, sy;
77 struct wlr_surface *surface = NULL;
78 struct sway_node *focused_node = node_at_coords(seat, seat->touch_x,
79 seat->touch_y, &surface, &sx, &sy);
80
81 if (!surface || surface != e->surface) { // Must start from the initial surface
82 return;
83 }
84
85 struct seatop_touch_point_event *point_event =
86 calloc(1, sizeof(struct seatop_touch_point_event));
87 if (!sway_assert(point_event, "Unable to allocate point_event")) {
88 return;
89 }
90 point_event->touch_id = event->touch_id;
91 point_event->ref_lx = lx;
92 point_event->ref_ly = ly;
93 point_event->ref_con_lx = sx;
94 point_event->ref_con_ly = sy;
95
96 wl_list_insert(&e->point_events, &point_event->link);
97
98 wlr_seat_touch_notify_down(seat->wlr_seat, surface, event->time_msec,
99 event->touch_id, sx, sy);
100
101 if (focused_node) {
102 seat_set_focus(seat, focused_node);
103 }
104}
105
106static void handle_touch_cancel(struct sway_seat *seat,
107 struct wlr_touch_cancel_event *event) {
108 struct seatop_down_event *e = seat->seatop_data;
109 struct seatop_touch_point_event *point_event, *tmp;
110
111 wl_list_for_each_safe(point_event, tmp, &e->point_events, link) {
112 if (point_event->touch_id == event->touch_id) {
113 wl_list_remove(&point_event->link);
114 free(point_event);
115 break;
116 }
117 }
118
119 if (e->surface) {
120 struct wl_client *client = wl_resource_get_client(e->surface->resource);
121 struct wlr_seat_client *seat_client = wlr_seat_client_for_wl_client(seat->wlr_seat, client);
122 if (seat_client != NULL) {
123 wlr_seat_touch_notify_cancel(seat->wlr_seat, seat_client);
124 }
125 }
126
127 if (wl_list_empty(&e->point_events)) {
128 seatop_begin_default(seat);
129 }
130}
131
17static void handle_pointer_axis(struct sway_seat *seat, 132static void handle_pointer_axis(struct sway_seat *seat,
18 struct wlr_event_pointer_axis *event) { 133 struct wlr_pointer_axis_event *event) {
19 struct sway_input_device *input_device = 134 struct sway_input_device *input_device =
20 event->device ? event->device->data : NULL; 135 event->pointer ? event->pointer->base.data : NULL;
21 struct input_config *ic = 136 struct input_config *ic =
22 input_device ? input_device_get_config(input_device) : NULL; 137 input_device ? input_device_get_config(input_device) : NULL;
23 float scroll_factor = 138 float scroll_factor =
@@ -25,12 +140,13 @@ static void handle_pointer_axis(struct sway_seat *seat,
25 140
26 wlr_seat_pointer_notify_axis(seat->wlr_seat, event->time_msec, 141 wlr_seat_pointer_notify_axis(seat->wlr_seat, event->time_msec,
27 event->orientation, scroll_factor * event->delta, 142 event->orientation, scroll_factor * event->delta,
28 round(scroll_factor * event->delta_discrete), event->source); 143 roundf(scroll_factor * event->delta_discrete), event->source,
144 event->relative_direction);
29} 145}
30 146
31static void handle_button(struct sway_seat *seat, uint32_t time_msec, 147static void handle_button(struct sway_seat *seat, uint32_t time_msec,
32 struct wlr_input_device *device, uint32_t button, 148 struct wlr_input_device *device, uint32_t button,
33 enum wlr_button_state state) { 149 enum wl_pointer_button_state state) {
34 seat_pointer_notify_button(seat, time_msec, button, state); 150 seat_pointer_notify_button(seat, time_msec, button, state);
35 151
36 if (seat->cursor->pressed_button_count == 0) { 152 if (seat->cursor->pressed_button_count == 0) {
@@ -40,8 +156,7 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec,
40 156
41static void handle_pointer_motion(struct sway_seat *seat, uint32_t time_msec) { 157static void handle_pointer_motion(struct sway_seat *seat, uint32_t time_msec) {
42 struct seatop_down_event *e = seat->seatop_data; 158 struct seatop_down_event *e = seat->seatop_data;
43 struct sway_container *con = e->con; 159 if (seat_is_input_allowed(seat, e->surface)) {
44 if (seat_is_input_allowed(seat, con->view->surface)) {
45 double moved_x = seat->cursor->cursor->x - e->ref_lx; 160 double moved_x = seat->cursor->cursor->x - e->ref_lx;
46 double moved_y = seat->cursor->cursor->y - e->ref_ly; 161 double moved_y = seat->cursor->cursor->y - e->ref_ly;
47 double sx = e->ref_con_lx + moved_x; 162 double sx = e->ref_con_lx + moved_x;
@@ -62,8 +177,7 @@ static void handle_tablet_tool_tip(struct sway_seat *seat,
62static void handle_tablet_tool_motion(struct sway_seat *seat, 177static void handle_tablet_tool_motion(struct sway_seat *seat,
63 struct sway_tablet_tool *tool, uint32_t time_msec) { 178 struct sway_tablet_tool *tool, uint32_t time_msec) {
64 struct seatop_down_event *e = seat->seatop_data; 179 struct seatop_down_event *e = seat->seatop_data;
65 struct sway_container *con = e->con; 180 if (seat_is_input_allowed(seat, e->surface)) {
66 if (seat_is_input_allowed(seat, con->view->surface)) {
67 double moved_x = seat->cursor->cursor->x - e->ref_lx; 181 double moved_x = seat->cursor->cursor->x - e->ref_lx;
68 double moved_y = seat->cursor->cursor->y - e->ref_ly; 182 double moved_y = seat->cursor->cursor->y - e->ref_ly;
69 double sx = e->ref_con_lx + moved_x; 183 double sx = e->ref_con_lx + moved_x;
@@ -72,6 +186,14 @@ static void handle_tablet_tool_motion(struct sway_seat *seat,
72 } 186 }
73} 187}
74 188
189static void handle_destroy(struct wl_listener *listener, void *data) {
190 struct seatop_down_event *e =
191 wl_container_of(listener, e, surface_destroy);
192 if (e) {
193 seatop_begin_default(e->seat);
194 }
195}
196
75static void handle_unref(struct sway_seat *seat, struct sway_container *con) { 197static void handle_unref(struct sway_seat *seat, struct sway_container *con) {
76 struct seatop_down_event *e = seat->seatop_data; 198 struct seatop_down_event *e = seat->seatop_data;
77 if (e->con == con) { 199 if (e->con == con) {
@@ -79,34 +201,63 @@ static void handle_unref(struct sway_seat *seat, struct sway_container *con) {
79 } 201 }
80} 202}
81 203
204static void handle_end(struct sway_seat *seat) {
205 struct seatop_down_event *e = seat->seatop_data;
206 wl_list_remove(&e->surface_destroy.link);
207}
208
82static const struct sway_seatop_impl seatop_impl = { 209static const struct sway_seatop_impl seatop_impl = {
83 .button = handle_button, 210 .button = handle_button,
84 .pointer_motion = handle_pointer_motion, 211 .pointer_motion = handle_pointer_motion,
85 .pointer_axis = handle_pointer_axis, 212 .pointer_axis = handle_pointer_axis,
86 .tablet_tool_tip = handle_tablet_tool_tip, 213 .tablet_tool_tip = handle_tablet_tool_tip,
87 .tablet_tool_motion = handle_tablet_tool_motion, 214 .tablet_tool_motion = handle_tablet_tool_motion,
215 .touch_motion = handle_touch_motion,
216 .touch_up = handle_touch_up,
217 .touch_down = handle_touch_down,
218 .touch_cancel = handle_touch_cancel,
88 .unref = handle_unref, 219 .unref = handle_unref,
220 .end = handle_end,
89 .allow_set_cursor = true, 221 .allow_set_cursor = true,
90}; 222};
91 223
92void seatop_begin_down(struct sway_seat *seat, struct sway_container *con, 224void seatop_begin_down(struct sway_seat *seat, struct sway_container *con,
93 uint32_t time_msec, int sx, int sy) { 225 double sx, double sy) {
226 seatop_begin_down_on_surface(seat, con->view->surface, sx, sy);
227 struct seatop_down_event *e = seat->seatop_data;
228 e->con = con;
229
230 container_raise_floating(con);
231 transaction_commit_dirty();
232}
233
234void seatop_begin_touch_down(struct sway_seat *seat,
235 struct wlr_surface *surface, struct wlr_touch_down_event *event,
236 double sx, double sy, double lx, double ly) {
237 seatop_begin_down_on_surface(seat, surface, sx, sy);
238 handle_touch_down(seat, event, lx, ly);
239}
240
241void seatop_begin_down_on_surface(struct sway_seat *seat,
242 struct wlr_surface *surface, double sx, double sy) {
94 seatop_end(seat); 243 seatop_end(seat);
95 244
96 struct seatop_down_event *e = 245 struct seatop_down_event *e =
97 calloc(1, sizeof(struct seatop_down_event)); 246 calloc(1, sizeof(struct seatop_down_event));
98 if (!e) { 247 if (!sway_assert(e, "Unable to allocate e")) {
99 return; 248 return;
100 } 249 }
101 e->con = con; 250 e->con = NULL;
251 e->seat = seat;
252 e->surface = surface;
253 wl_signal_add(&e->surface->events.destroy, &e->surface_destroy);
254 e->surface_destroy.notify = handle_destroy;
102 e->ref_lx = seat->cursor->cursor->x; 255 e->ref_lx = seat->cursor->cursor->x;
103 e->ref_ly = seat->cursor->cursor->y; 256 e->ref_ly = seat->cursor->cursor->y;
104 e->ref_con_lx = sx; 257 e->ref_con_lx = sx;
105 e->ref_con_ly = sy; 258 e->ref_con_ly = sy;
259 wl_list_init(&e->point_events);
106 260
107 seat->seatop_impl = &seatop_impl; 261 seat->seatop_impl = &seatop_impl;
108 seat->seatop_data = e; 262 seat->seatop_data = e;
109
110 container_raise_floating(con);
111 transaction_commit_dirty();
112} 263}
diff --git a/sway/input/seatop_move_floating.c b/sway/input/seatop_move_floating.c
index ddcd4c53..83668d88 100644
--- a/sway/input/seatop_move_floating.c
+++ b/sway/input/seatop_move_floating.c
@@ -1,6 +1,4 @@
1#define _POSIX_C_SOURCE 200809L
2#include <wlr/types/wlr_cursor.h> 1#include <wlr/types/wlr_cursor.h>
3#include "sway/desktop.h"
4#include "sway/desktop/transaction.h" 2#include "sway/desktop/transaction.h"
5#include "sway/input/cursor.h" 3#include "sway/input/cursor.h"
6#include "sway/input/seat.h" 4#include "sway/input/seat.h"
@@ -23,7 +21,7 @@ static void finalize_move(struct sway_seat *seat) {
23 21
24static void handle_button(struct sway_seat *seat, uint32_t time_msec, 22static void handle_button(struct sway_seat *seat, uint32_t time_msec,
25 struct wlr_input_device *device, uint32_t button, 23 struct wlr_input_device *device, uint32_t button,
26 enum wlr_button_state state) { 24 enum wl_pointer_button_state state) {
27 if (seat->cursor->pressed_button_count == 0) { 25 if (seat->cursor->pressed_button_count == 0) {
28 finalize_move(seat); 26 finalize_move(seat);
29 } 27 }
@@ -39,9 +37,7 @@ static void handle_tablet_tool_tip(struct sway_seat *seat,
39static void handle_pointer_motion(struct sway_seat *seat, uint32_t time_msec) { 37static void handle_pointer_motion(struct sway_seat *seat, uint32_t time_msec) {
40 struct seatop_move_floating_event *e = seat->seatop_data; 38 struct seatop_move_floating_event *e = seat->seatop_data;
41 struct wlr_cursor *cursor = seat->cursor->cursor; 39 struct wlr_cursor *cursor = seat->cursor->cursor;
42 desktop_damage_whole_container(e->con);
43 container_floating_move_to(e->con, cursor->x - e->dx, cursor->y - e->dy); 40 container_floating_move_to(e->con, cursor->x - e->dx, cursor->y - e->dy);
44 desktop_damage_whole_container(e->con);
45 transaction_commit_dirty(); 41 transaction_commit_dirty();
46} 42}
47 43
diff --git a/sway/input/seatop_move_tiling.c b/sway/input/seatop_move_tiling.c
index 223c6c08..c525b77a 100644
--- a/sway/input/seatop_move_tiling.c
+++ b/sway/input/seatop_move_tiling.c
@@ -1,8 +1,6 @@
1#define _POSIX_C_SOURCE 200809L
2#include <limits.h> 1#include <limits.h>
3#include <wlr/types/wlr_cursor.h> 2#include <wlr/types/wlr_cursor.h>
4#include <wlr/util/edges.h> 3#include <wlr/util/edges.h>
5#include "sway/desktop.h"
6#include "sway/desktop/transaction.h" 4#include "sway/desktop/transaction.h"
7#include "sway/input/cursor.h" 5#include "sway/input/cursor.h"
8#include "sway/input/seat.h" 6#include "sway/input/seat.h"
@@ -24,29 +22,17 @@ struct seatop_move_tiling_event {
24 struct sway_container *con; 22 struct sway_container *con;
25 struct sway_node *target_node; 23 struct sway_node *target_node;
26 enum wlr_edges target_edge; 24 enum wlr_edges target_edge;
27 struct wlr_box drop_box;
28 double ref_lx, ref_ly; // cursor's x/y at start of op 25 double ref_lx, ref_ly; // cursor's x/y at start of op
29 bool threshold_reached; 26 bool threshold_reached;
30 bool split_target; 27 bool split_target;
31 bool insert_after_target; 28 bool insert_after_target;
29 struct wlr_scene_rect *indicator_rect;
32}; 30};
33 31
34static void handle_render(struct sway_seat *seat, 32static void handle_end(struct sway_seat *seat) {
35 struct sway_output *output, pixman_region32_t *damage) {
36 struct seatop_move_tiling_event *e = seat->seatop_data; 33 struct seatop_move_tiling_event *e = seat->seatop_data;
37 if (!e->threshold_reached) { 34 wlr_scene_node_destroy(&e->indicator_rect->node);
38 return; 35 e->indicator_rect = NULL;
39 }
40 if (e->target_node && node_get_output(e->target_node) == output) {
41 float color[4];
42 memcpy(&color, config->border_colors.focused.indicator,
43 sizeof(float) * 4);
44 premultiply_alpha(color, 0.5);
45 struct wlr_box box;
46 memcpy(&box, &e->drop_box, sizeof(struct wlr_box));
47 scale_box(&box, output->wlr_output->scale);
48 render_rect(output, damage, &box, color);
49 }
50} 36}
51 37
52static void handle_motion_prethreshold(struct sway_seat *seat) { 38static void handle_motion_prethreshold(struct sway_seat *seat) {
@@ -67,6 +53,7 @@ static void handle_motion_prethreshold(struct sway_seat *seat) {
67 53
68 // If the threshold has been exceeded, start the actual drag 54 // If the threshold has been exceeded, start the actual drag
69 if ((cx - sx) * (cx - sx) + (cy - sy) * (cy - sy) > threshold) { 55 if ((cx - sx) * (cx - sx) + (cy - sy) * (cy - sy) > threshold) {
56 wlr_scene_node_set_enabled(&e->indicator_rect->node, true);
70 e->threshold_reached = true; 57 e->threshold_reached = true;
71 cursor_set_image(seat->cursor, "grab", NULL); 58 cursor_set_image(seat->cursor, "grab", NULL);
72 } 59 }
@@ -165,6 +152,11 @@ static bool split_titlebar(struct sway_node *node, struct sway_container *avoid,
165 return false; 152 return false;
166} 153}
167 154
155static void update_indicator(struct seatop_move_tiling_event *e, struct wlr_box *box) {
156 wlr_scene_node_set_position(&e->indicator_rect->node, box->x, box->y);
157 wlr_scene_rect_set_size(e->indicator_rect, box->width, box->height);
158}
159
168static void handle_motion_postthreshold(struct sway_seat *seat) { 160static void handle_motion_postthreshold(struct sway_seat *seat) {
169 struct seatop_move_tiling_event *e = seat->seatop_data; 161 struct seatop_move_tiling_event *e = seat->seatop_data;
170 e->split_target = false; 162 e->split_target = false;
@@ -173,8 +165,6 @@ static void handle_motion_postthreshold(struct sway_seat *seat) {
173 struct sway_cursor *cursor = seat->cursor; 165 struct sway_cursor *cursor = seat->cursor;
174 struct sway_node *node = node_at_coords(seat, 166 struct sway_node *node = node_at_coords(seat,
175 cursor->cursor->x, cursor->cursor->y, &surface, &sx, &sy); 167 cursor->cursor->x, cursor->cursor->y, &surface, &sx, &sy);
176 // Damage the old location
177 desktop_damage_box(&e->drop_box);
178 168
179 if (!node) { 169 if (!node) {
180 // Eg. hovered over a layer surface such as swaybar 170 // Eg. hovered over a layer surface such as swaybar
@@ -187,8 +177,10 @@ static void handle_motion_postthreshold(struct sway_seat *seat) {
187 // Empty workspace 177 // Empty workspace
188 e->target_node = node; 178 e->target_node = node;
189 e->target_edge = WLR_EDGE_NONE; 179 e->target_edge = WLR_EDGE_NONE;
190 workspace_get_box(node->sway_workspace, &e->drop_box); 180
191 desktop_damage_box(&e->drop_box); 181 struct wlr_box drop_box;
182 workspace_get_box(node->sway_workspace, &drop_box);
183 update_indicator(e, &drop_box);
192 return; 184 return;
193 } 185 }
194 186
@@ -201,11 +193,18 @@ static void handle_motion_postthreshold(struct sway_seat *seat) {
201 return; 193 return;
202 } 194 }
203 195
196 struct wlr_box drop_box = {
197 .x = con->pending.content_x,
198 .y = con->pending.content_y,
199 .width = con->pending.content_width,
200 .height = con->pending.content_height,
201 };
202
204 // Check if the cursor is over a tilebar only if the destination 203 // Check if the cursor is over a tilebar only if the destination
205 // container is not a descendant of the source container. 204 // container is not a descendant of the source container.
206 if (!surface && !container_has_ancestor(con, e->con) && 205 if (!surface && !container_has_ancestor(con, e->con) &&
207 split_titlebar(node, e->con, cursor->cursor, 206 split_titlebar(node, e->con, cursor->cursor,
208 &e->drop_box, &e->insert_after_target)) { 207 &drop_box, &e->insert_after_target)) {
209 // Don't allow dropping over the source container's titlebar 208 // Don't allow dropping over the source container's titlebar
210 // to give users a chance to cancel a drag operation. 209 // to give users a chance to cancel a drag operation.
211 if (con == e->con) { 210 if (con == e->con) {
@@ -215,6 +214,7 @@ static void handle_motion_postthreshold(struct sway_seat *seat) {
215 e->split_target = true; 214 e->split_target = true;
216 } 215 }
217 e->target_edge = WLR_EDGE_NONE; 216 e->target_edge = WLR_EDGE_NONE;
217 update_indicator(e, &drop_box);
218 return; 218 return;
219 } 219 }
220 220
@@ -256,8 +256,7 @@ static void handle_motion_postthreshold(struct sway_seat *seat) {
256 e->target_node = node_get_parent(e->target_node); 256 e->target_node = node_get_parent(e->target_node);
257 } 257 }
258 e->target_edge = edge; 258 e->target_edge = edge;
259 e->drop_box = box; 259 update_indicator(e, &box);
260 desktop_damage_box(&e->drop_box);
261 return; 260 return;
262 } 261 }
263 con = con->pending.parent; 262 con = con->pending.parent;
@@ -299,12 +298,8 @@ static void handle_motion_postthreshold(struct sway_seat *seat) {
299 } 298 }
300 299
301 e->target_node = node; 300 e->target_node = node;
302 e->drop_box.x = con->pending.content_x; 301 resize_box(&drop_box, e->target_edge, thickness);
303 e->drop_box.y = con->pending.content_y; 302 update_indicator(e, &drop_box);
304 e->drop_box.width = con->pending.content_width;
305 e->drop_box.height = con->pending.content_height;
306 resize_box(&e->drop_box, e->target_edge, thickness);
307 desktop_damage_box(&e->drop_box);
308} 303}
309 304
310static void handle_pointer_motion(struct sway_seat *seat, uint32_t time_msec) { 305static void handle_pointer_motion(struct sway_seat *seat, uint32_t time_msec) {
@@ -410,7 +405,7 @@ static void finalize_move(struct sway_seat *seat) {
410 405
411static void handle_button(struct sway_seat *seat, uint32_t time_msec, 406static void handle_button(struct sway_seat *seat, uint32_t time_msec,
412 struct wlr_input_device *device, uint32_t button, 407 struct wlr_input_device *device, uint32_t button,
413 enum wlr_button_state state) { 408 enum wl_pointer_button_state state) {
414 if (seat->cursor->pressed_button_count == 0) { 409 if (seat->cursor->pressed_button_count == 0) {
415 finalize_move(seat); 410 finalize_move(seat);
416 } 411 }
@@ -439,7 +434,7 @@ static const struct sway_seatop_impl seatop_impl = {
439 .pointer_motion = handle_pointer_motion, 434 .pointer_motion = handle_pointer_motion,
440 .tablet_tool_tip = handle_tablet_tool_tip, 435 .tablet_tool_tip = handle_tablet_tool_tip,
441 .unref = handle_unref, 436 .unref = handle_unref,
442 .render = handle_render, 437 .end = handle_end,
443}; 438};
444 439
445void seatop_begin_move_tiling_threshold(struct sway_seat *seat, 440void seatop_begin_move_tiling_threshold(struct sway_seat *seat,
@@ -451,6 +446,20 @@ void seatop_begin_move_tiling_threshold(struct sway_seat *seat,
451 if (!e) { 446 if (!e) {
452 return; 447 return;
453 } 448 }
449
450 const float *indicator = config->border_colors.focused.indicator;
451 float color[4] = {
452 indicator[0] * .5,
453 indicator[1] * .5,
454 indicator[2] * .5,
455 indicator[3] * .5,
456 };
457 e->indicator_rect = wlr_scene_rect_create(seat->scene_tree, 0, 0, color);
458 if (!e->indicator_rect) {
459 free(e);
460 return;
461 }
462
454 e->con = con; 463 e->con = con;
455 e->ref_lx = seat->cursor->cursor->x; 464 e->ref_lx = seat->cursor->cursor->x;
456 e->ref_ly = seat->cursor->cursor->y; 465 e->ref_ly = seat->cursor->cursor->y;
diff --git a/sway/input/seatop_resize_floating.c b/sway/input/seatop_resize_floating.c
index 8400a4b3..bec86e33 100644
--- a/sway/input/seatop_resize_floating.c
+++ b/sway/input/seatop_resize_floating.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <limits.h> 1#include <limits.h>
3#include <wlr/types/wlr_cursor.h> 2#include <wlr/types/wlr_cursor.h>
4#include <wlr/types/wlr_xcursor_manager.h> 3#include <wlr/types/wlr_xcursor_manager.h>
@@ -21,7 +20,7 @@ struct seatop_resize_floating_event {
21 20
22static void handle_button(struct sway_seat *seat, uint32_t time_msec, 21static void handle_button(struct sway_seat *seat, uint32_t time_msec,
23 struct wlr_input_device *device, uint32_t button, 22 struct wlr_input_device *device, uint32_t button,
24 enum wlr_button_state state) { 23 enum wl_pointer_button_state state) {
25 struct seatop_resize_floating_event *e = seat->seatop_data; 24 struct seatop_resize_floating_event *e = seat->seatop_data;
26 struct sway_container *con = e->con; 25 struct sway_container *con = e->con;
27 26
@@ -80,17 +79,25 @@ static void handle_pointer_motion(struct sway_seat *seat, uint32_t time_msec) {
80 double height = e->ref_height + grow_height; 79 double height = e->ref_height + grow_height;
81 int min_width, max_width, min_height, max_height; 80 int min_width, max_width, min_height, max_height;
82 floating_calculate_constraints(&min_width, &max_width, 81 floating_calculate_constraints(&min_width, &max_width,
83 &min_height, &max_height); 82 &min_height, &max_height);
84 width = fmax(min_width + border_width, fmin(width, max_width)); 83 width = fmin(width, max_width - border_width);
85 height = fmax(min_height + border_height, fmin(height, max_height)); 84 width = fmax(width, min_width + border_width);
85 width = fmax(width, 1);
86 height = fmin(height, max_height - border_height);
87 height = fmax(height, min_height + border_height);
88 height = fmax(height, 1);
86 89
87 // Apply the view's min/max size 90 // Apply the view's min/max size
88 if (con->view) { 91 if (con->view) {
89 double view_min_width, view_max_width, view_min_height, view_max_height; 92 double view_min_width, view_max_width, view_min_height, view_max_height;
90 view_get_constraints(con->view, &view_min_width, &view_max_width, 93 view_get_constraints(con->view, &view_min_width, &view_max_width,
91 &view_min_height, &view_max_height); 94 &view_min_height, &view_max_height);
92 width = fmax(view_min_width + border_width, fmin(width, view_max_width)); 95 width = fmin(width, view_max_width - border_width);
93 height = fmax(view_min_height + border_height, fmin(height, view_max_height)); 96 width = fmax(width, view_min_width + border_width);
97 width = fmax(width, 1);
98 height = fmin(height, view_max_height - border_height);
99 height = fmax(height, view_min_height + border_height);
100 height = fmax(height, 1);
94 101
95 } 102 }
96 103
diff --git a/sway/input/seatop_resize_tiling.c b/sway/input/seatop_resize_tiling.c
index 869d11b5..15fd333b 100644
--- a/sway/input/seatop_resize_tiling.c
+++ b/sway/input/seatop_resize_tiling.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <wlr/types/wlr_cursor.h> 1#include <wlr/types/wlr_cursor.h>
3#include <wlr/util/edges.h> 2#include <wlr/util/edges.h>
4#include "sway/commands.h" 3#include "sway/commands.h"
@@ -46,7 +45,7 @@ static struct sway_container *container_get_resize_sibling(
46 45
47static void handle_button(struct sway_seat *seat, uint32_t time_msec, 46static void handle_button(struct sway_seat *seat, uint32_t time_msec,
48 struct wlr_input_device *device, uint32_t button, 47 struct wlr_input_device *device, uint32_t button,
49 enum wlr_button_state state) { 48 enum wl_pointer_button_state state) {
50 struct seatop_resize_tiling_event *e = seat->seatop_data; 49 struct seatop_resize_tiling_event *e = seat->seatop_data;
51 50
52 if (seat->cursor->pressed_button_count == 0) { 51 if (seat->cursor->pressed_button_count == 0) {
diff --git a/sway/input/switch.c b/sway/input/switch.c
index 9ea87a1a..831f4dbf 100644
--- a/sway/input/switch.c
+++ b/sway/input/switch.c
@@ -1,6 +1,5 @@
1#include "sway/config.h" 1#include "sway/config.h"
2#include "sway/input/switch.h" 2#include "sway/input/switch.h"
3#include <wlr/types/wlr_idle.h>
4#include "log.h" 3#include "log.h"
5 4
6struct sway_switch *sway_switch_create(struct sway_seat *seat, 5struct sway_switch *sway_switch_create(struct sway_seat *seat,
@@ -11,6 +10,7 @@ struct sway_switch *sway_switch_create(struct sway_seat *seat,
11 return NULL; 10 return NULL;
12 } 11 }
13 device->switch_device = switch_device; 12 device->switch_device = switch_device;
13 switch_device->wlr = wlr_switch_from_input_device(device->input_device->wlr_device);
14 switch_device->seat_device = device; 14 switch_device->seat_device = device;
15 switch_device->state = WLR_SWITCH_STATE_OFF; 15 switch_device->state = WLR_SWITCH_STATE_OFF;
16 wl_list_init(&switch_device->switch_toggle.link); 16 wl_list_init(&switch_device->switch_toggle.link);
@@ -19,9 +19,22 @@ struct sway_switch *sway_switch_create(struct sway_seat *seat,
19 return switch_device; 19 return switch_device;
20} 20}
21 21
22static bool sway_switch_trigger_test(enum sway_switch_trigger trigger,
23 enum wlr_switch_state state) {
24 switch (trigger) {
25 case SWAY_SWITCH_TRIGGER_ON:
26 return state == WLR_SWITCH_STATE_ON;
27 case SWAY_SWITCH_TRIGGER_OFF:
28 return state == WLR_SWITCH_STATE_OFF;
29 case SWAY_SWITCH_TRIGGER_TOGGLE:
30 return true;
31 }
32 abort(); // unreachable
33}
34
22static void execute_binding(struct sway_switch *sway_switch) { 35static void execute_binding(struct sway_switch *sway_switch) {
23 struct sway_seat* seat = sway_switch->seat_device->sway_seat; 36 struct sway_seat *seat = sway_switch->seat_device->sway_seat;
24 bool input_inhibited = seat->exclusive_client != NULL; 37 bool locked = server.session_lock.lock;
25 38
26 list_t *bindings = config->current_mode->switch_bindings; 39 list_t *bindings = config->current_mode->switch_bindings;
27 struct sway_switch_binding *matched_binding = NULL; 40 struct sway_switch_binding *matched_binding = NULL;
@@ -30,22 +43,21 @@ static void execute_binding(struct sway_switch *sway_switch) {
30 if (binding->type != sway_switch->type) { 43 if (binding->type != sway_switch->type) {
31 continue; 44 continue;
32 } 45 }
33 if (binding->state != WLR_SWITCH_STATE_TOGGLE && 46 if (!sway_switch_trigger_test(binding->trigger, sway_switch->state)) {
34 binding->state != sway_switch->state) {
35 continue; 47 continue;
36 } 48 }
37 if (config->reloading && (binding->state == WLR_SWITCH_STATE_TOGGLE 49 if (config->reloading && (binding->trigger == SWAY_SWITCH_TRIGGER_TOGGLE
38 || (binding->flags & BINDING_RELOAD) == 0)) { 50 || (binding->flags & BINDING_RELOAD) == 0)) {
39 continue; 51 continue;
40 } 52 }
41 bool binding_locked = binding->flags & BINDING_LOCKED; 53 bool binding_locked = binding->flags & BINDING_LOCKED;
42 if (!binding_locked && input_inhibited) { 54 if (!binding_locked && locked) {
43 continue; 55 continue;
44 } 56 }
45 57
46 matched_binding = binding; 58 matched_binding = binding;
47 59
48 if (binding_locked == input_inhibited) { 60 if (binding_locked == locked) {
49 break; 61 break;
50 } 62 }
51 } 63 }
@@ -65,7 +77,7 @@ static void execute_binding(struct sway_switch *sway_switch) {
65static void handle_switch_toggle(struct wl_listener *listener, void *data) { 77static void handle_switch_toggle(struct wl_listener *listener, void *data) {
66 struct sway_switch *sway_switch = 78 struct sway_switch *sway_switch =
67 wl_container_of(listener, sway_switch, switch_toggle); 79 wl_container_of(listener, sway_switch, switch_toggle);
68 struct wlr_event_switch_toggle *event = data; 80 struct wlr_switch_toggle_event *event = data;
69 struct sway_seat *seat = sway_switch->seat_device->sway_seat; 81 struct sway_seat *seat = sway_switch->seat_device->sway_seat;
70 seat_idle_notify_activity(seat, IDLE_SOURCE_SWITCH); 82 seat_idle_notify_activity(seat, IDLE_SOURCE_SWITCH);
71 83
@@ -82,10 +94,8 @@ static void handle_switch_toggle(struct wl_listener *listener, void *data) {
82} 94}
83 95
84void sway_switch_configure(struct sway_switch *sway_switch) { 96void sway_switch_configure(struct sway_switch *sway_switch) {
85 struct wlr_input_device *wlr_device =
86 sway_switch->seat_device->input_device->wlr_device;
87 wl_list_remove(&sway_switch->switch_toggle.link); 97 wl_list_remove(&sway_switch->switch_toggle.link);
88 wl_signal_add(&wlr_device->switch_device->events.toggle, 98 wl_signal_add(&sway_switch->wlr->events.toggle,
89 &sway_switch->switch_toggle); 99 &sway_switch->switch_toggle);
90 sway_switch->switch_toggle.notify = handle_switch_toggle; 100 sway_switch->switch_toggle.notify = handle_switch_toggle;
91 sway_log(SWAY_DEBUG, "Configured switch for device"); 101 sway_log(SWAY_DEBUG, "Configured switch for device");
diff --git a/sway/input/tablet.c b/sway/input/tablet.c
index 26e86e36..2863642a 100644
--- a/sway/input/tablet.c
+++ b/sway/input/tablet.c
@@ -1,6 +1,5 @@
1#define _POSIX_C_SOURCE 200809L
2#include <stdlib.h> 1#include <stdlib.h>
3#include <wlr/backend/libinput.h> 2#include <wlr/config.h>
4#include <wlr/types/wlr_tablet_v2.h> 3#include <wlr/types/wlr_tablet_v2.h>
5#include <wlr/types/wlr_tablet_tool.h> 4#include <wlr/types/wlr_tablet_tool.h>
6#include <wlr/types/wlr_tablet_pad.h> 5#include <wlr/types/wlr_tablet_pad.h>
@@ -9,6 +8,10 @@
9#include "sway/input/seat.h" 8#include "sway/input/seat.h"
10#include "sway/input/tablet.h" 9#include "sway/input/tablet.h"
11 10
11#if WLR_HAS_LIBINPUT_BACKEND
12#include <wlr/backend/libinput.h>
13#endif
14
12static void handle_pad_tablet_destroy(struct wl_listener *listener, void *data) { 15static void handle_pad_tablet_destroy(struct wl_listener *listener, void *data) {
13 struct sway_tablet_pad *pad = 16 struct sway_tablet_pad *pad =
14 wl_container_of(listener, pad, tablet_destroy); 17 wl_container_of(listener, pad, tablet_destroy);
@@ -54,15 +57,14 @@ void sway_configure_tablet(struct sway_tablet *tablet) {
54 tablet->seat_device->input_device->wlr_device; 57 tablet->seat_device->input_device->wlr_device;
55 struct sway_seat *seat = tablet->seat_device->sway_seat; 58 struct sway_seat *seat = tablet->seat_device->sway_seat;
56 59
57 if ((seat->wlr_seat->capabilities & WL_SEAT_CAPABILITY_POINTER) == 0) { 60 seat_configure_xcursor(seat);
58 seat_configure_xcursor(seat);
59 }
60 61
61 if (!tablet->tablet_v2) { 62 if (!tablet->tablet_v2) {
62 tablet->tablet_v2 = 63 tablet->tablet_v2 =
63 wlr_tablet_create(server.tablet_v2, seat->wlr_seat, device); 64 wlr_tablet_create(server.tablet_v2, seat->wlr_seat, device);
64 } 65 }
65 66
67#if WLR_HAS_LIBINPUT_BACKEND
66 /* Search for a sibling tablet pad */ 68 /* Search for a sibling tablet pad */
67 if (!wlr_input_device_is_libinput(device)) { 69 if (!wlr_input_device_is_libinput(device)) {
68 /* We can only do this on libinput devices */ 70 /* We can only do this on libinput devices */
@@ -87,6 +89,7 @@ void sway_configure_tablet(struct sway_tablet *tablet) {
87 break; 89 break;
88 } 90 }
89 } 91 }
92#endif
90} 93}
91 94
92void sway_tablet_destroy(struct sway_tablet *tablet) { 95void sway_tablet_destroy(struct sway_tablet *tablet) {
@@ -196,7 +199,7 @@ static void handle_tablet_pad_attach(struct wl_listener *listener,
196 199
197static void handle_tablet_pad_ring(struct wl_listener *listener, void *data) { 200static void handle_tablet_pad_ring(struct wl_listener *listener, void *data) {
198 struct sway_tablet_pad *pad = wl_container_of(listener, pad, ring); 201 struct sway_tablet_pad *pad = wl_container_of(listener, pad, ring);
199 struct wlr_event_tablet_pad_ring *event = data; 202 struct wlr_tablet_pad_ring_event *event = data;
200 203
201 if (!pad->current_surface) { 204 if (!pad->current_surface) {
202 return; 205 return;
@@ -210,7 +213,7 @@ static void handle_tablet_pad_ring(struct wl_listener *listener, void *data) {
210 213
211static void handle_tablet_pad_strip(struct wl_listener *listener, void *data) { 214static void handle_tablet_pad_strip(struct wl_listener *listener, void *data) {
212 struct sway_tablet_pad *pad = wl_container_of(listener, pad, strip); 215 struct sway_tablet_pad *pad = wl_container_of(listener, pad, strip);
213 struct wlr_event_tablet_pad_strip *event = data; 216 struct wlr_tablet_pad_strip_event *event = data;
214 217
215 if (!pad->current_surface) { 218 if (!pad->current_surface) {
216 return; 219 return;
@@ -224,7 +227,7 @@ static void handle_tablet_pad_strip(struct wl_listener *listener, void *data) {
224 227
225static void handle_tablet_pad_button(struct wl_listener *listener, void *data) { 228static void handle_tablet_pad_button(struct wl_listener *listener, void *data) {
226 struct sway_tablet_pad *pad = wl_container_of(listener, pad, button); 229 struct sway_tablet_pad *pad = wl_container_of(listener, pad, button);
227 struct wlr_event_tablet_pad_button *event = data; 230 struct wlr_tablet_pad_button_event *event = data;
228 231
229 if (!pad->current_surface) { 232 if (!pad->current_surface) {
230 return; 233 return;
@@ -246,6 +249,7 @@ struct sway_tablet_pad *sway_tablet_pad_create(struct sway_seat *seat,
246 return NULL; 249 return NULL;
247 } 250 }
248 251
252 tablet_pad->wlr = wlr_tablet_pad_from_input_device(device->input_device->wlr_device);
249 tablet_pad->seat_device = device; 253 tablet_pad->seat_device = device;
250 wl_list_init(&tablet_pad->attach.link); 254 wl_list_init(&tablet_pad->attach.link);
251 wl_list_init(&tablet_pad->button.link); 255 wl_list_init(&tablet_pad->button.link);
@@ -260,40 +264,41 @@ struct sway_tablet_pad *sway_tablet_pad_create(struct sway_seat *seat,
260} 264}
261 265
262void sway_configure_tablet_pad(struct sway_tablet_pad *tablet_pad) { 266void sway_configure_tablet_pad(struct sway_tablet_pad *tablet_pad) {
263 struct wlr_input_device *device = 267 struct wlr_input_device *wlr_device =
264 tablet_pad->seat_device->input_device->wlr_device; 268 tablet_pad->seat_device->input_device->wlr_device;
265 struct sway_seat *seat = tablet_pad->seat_device->sway_seat; 269 struct sway_seat *seat = tablet_pad->seat_device->sway_seat;
266 270
267 if (!tablet_pad->tablet_v2_pad) { 271 if (!tablet_pad->tablet_v2_pad) {
268 tablet_pad->tablet_v2_pad = 272 tablet_pad->tablet_v2_pad =
269 wlr_tablet_pad_create(server.tablet_v2, seat->wlr_seat, device); 273 wlr_tablet_pad_create(server.tablet_v2, seat->wlr_seat, wlr_device);
270 } 274 }
271 275
272 wl_list_remove(&tablet_pad->attach.link); 276 wl_list_remove(&tablet_pad->attach.link);
273 tablet_pad->attach.notify = handle_tablet_pad_attach; 277 tablet_pad->attach.notify = handle_tablet_pad_attach;
274 wl_signal_add(&device->tablet_pad->events.attach_tablet, 278 wl_signal_add(&tablet_pad->wlr->events.attach_tablet,
275 &tablet_pad->attach); 279 &tablet_pad->attach);
276 280
277 wl_list_remove(&tablet_pad->button.link); 281 wl_list_remove(&tablet_pad->button.link);
278 tablet_pad->button.notify = handle_tablet_pad_button; 282 tablet_pad->button.notify = handle_tablet_pad_button;
279 wl_signal_add(&device->tablet_pad->events.button, &tablet_pad->button); 283 wl_signal_add(&tablet_pad->wlr->events.button, &tablet_pad->button);
280 284
281 wl_list_remove(&tablet_pad->strip.link); 285 wl_list_remove(&tablet_pad->strip.link);
282 tablet_pad->strip.notify = handle_tablet_pad_strip; 286 tablet_pad->strip.notify = handle_tablet_pad_strip;
283 wl_signal_add(&device->tablet_pad->events.strip, &tablet_pad->strip); 287 wl_signal_add(&tablet_pad->wlr->events.strip, &tablet_pad->strip);
284 288
285 wl_list_remove(&tablet_pad->ring.link); 289 wl_list_remove(&tablet_pad->ring.link);
286 tablet_pad->ring.notify = handle_tablet_pad_ring; 290 tablet_pad->ring.notify = handle_tablet_pad_ring;
287 wl_signal_add(&device->tablet_pad->events.ring, &tablet_pad->ring); 291 wl_signal_add(&tablet_pad->wlr->events.ring, &tablet_pad->ring);
288 292
293#if WLR_HAS_LIBINPUT_BACKEND
289 /* Search for a sibling tablet */ 294 /* Search for a sibling tablet */
290 if (!wlr_input_device_is_libinput(device)) { 295 if (!wlr_input_device_is_libinput(wlr_device)) {
291 /* We can only do this on libinput devices */ 296 /* We can only do this on libinput devices */
292 return; 297 return;
293 } 298 }
294 299
295 struct libinput_device_group *group = 300 struct libinput_device_group *group =
296 libinput_device_get_device_group(wlr_libinput_get_device_handle(device)); 301 libinput_device_get_device_group(wlr_libinput_get_device_handle(wlr_device));
297 struct sway_tablet *tool; 302 struct sway_tablet *tool;
298 wl_list_for_each(tool, &seat->cursor->tablets, link) { 303 wl_list_for_each(tool, &seat->cursor->tablets, link) {
299 struct wlr_input_device *tablet = 304 struct wlr_input_device *tablet =
@@ -310,6 +315,7 @@ void sway_configure_tablet_pad(struct sway_tablet_pad *tablet_pad) {
310 break; 315 break;
311 } 316 }
312 } 317 }
318#endif
313} 319}
314 320
315void sway_tablet_pad_destroy(struct sway_tablet_pad *tablet_pad) { 321void sway_tablet_pad_destroy(struct sway_tablet_pad *tablet_pad) {
@@ -333,14 +339,10 @@ static void handle_pad_tablet_surface_destroy(struct wl_listener *listener,
333 struct sway_tablet_pad *tablet_pad = 339 struct sway_tablet_pad *tablet_pad =
334 wl_container_of(listener, tablet_pad, surface_destroy); 340 wl_container_of(listener, tablet_pad, surface_destroy);
335 341
336 wlr_tablet_v2_tablet_pad_notify_leave(tablet_pad->tablet_v2_pad, 342 sway_tablet_pad_set_focus(tablet_pad, NULL);
337 tablet_pad->current_surface);
338 wl_list_remove(&tablet_pad->surface_destroy.link);
339 wl_list_init(&tablet_pad->surface_destroy.link);
340 tablet_pad->current_surface = NULL;
341} 343}
342 344
343void sway_tablet_pad_notify_enter(struct sway_tablet_pad *tablet_pad, 345void sway_tablet_pad_set_focus(struct sway_tablet_pad *tablet_pad,
344 struct wlr_surface *surface) { 346 struct wlr_surface *surface) {
345 if (!tablet_pad || !tablet_pad->tablet) { 347 if (!tablet_pad || !tablet_pad->tablet) {
346 return; 348 return;
@@ -359,7 +361,8 @@ void sway_tablet_pad_notify_enter(struct sway_tablet_pad *tablet_pad,
359 tablet_pad->current_surface = NULL; 361 tablet_pad->current_surface = NULL;
360 } 362 }
361 363
362 if (!wlr_surface_accepts_tablet_v2(tablet_pad->tablet->tablet_v2, surface)) { 364 if (surface == NULL ||
365 !wlr_surface_accepts_tablet_v2(tablet_pad->tablet->tablet_v2, surface)) {
363 return; 366 return;
364 } 367 }
365 368
@@ -367,7 +370,6 @@ void sway_tablet_pad_notify_enter(struct sway_tablet_pad *tablet_pad,
367 tablet_pad->tablet->tablet_v2, surface); 370 tablet_pad->tablet->tablet_v2, surface);
368 371
369 tablet_pad->current_surface = surface; 372 tablet_pad->current_surface = surface;
370 wl_list_remove(&tablet_pad->surface_destroy.link);
371 tablet_pad->surface_destroy.notify = handle_pad_tablet_surface_destroy; 373 tablet_pad->surface_destroy.notify = handle_pad_tablet_surface_destroy;
372 wl_signal_add(&surface->events.destroy, &tablet_pad->surface_destroy); 374 wl_signal_add(&surface->events.destroy, &tablet_pad->surface_destroy);
373} 375}
diff --git a/sway/input/text_input.c b/sway/input/text_input.c
index b8c19c17..c38a3bb2 100644
--- a/sway/input/text_input.c
+++ b/sway/input/text_input.c
@@ -2,7 +2,14 @@
2#include <stdlib.h> 2#include <stdlib.h>
3#include "log.h" 3#include "log.h"
4#include "sway/input/seat.h" 4#include "sway/input/seat.h"
5#include "sway/scene_descriptor.h"
6#include "sway/tree/root.h"
7#include "sway/tree/view.h"
8#include "sway/output.h"
5#include "sway/input/text_input.h" 9#include "sway/input/text_input.h"
10#include "sway/input/text_input_popup.h"
11#include "sway/layers.h"
12static void input_popup_update(struct sway_input_popup *popup);
6 13
7static struct sway_text_input *relay_get_focusable_text_input( 14static struct sway_text_input *relay_get_focusable_text_input(
8 struct sway_input_method_relay *relay) { 15 struct sway_input_method_relay *relay) {
@@ -77,8 +84,6 @@ static void handle_im_grab_keyboard(struct wl_listener *listener, void *data) {
77 struct wlr_keyboard *active_keyboard = wlr_seat_get_keyboard(relay->seat->wlr_seat); 84 struct wlr_keyboard *active_keyboard = wlr_seat_get_keyboard(relay->seat->wlr_seat);
78 wlr_input_method_keyboard_grab_v2_set_keyboard(keyboard_grab, 85 wlr_input_method_keyboard_grab_v2_set_keyboard(keyboard_grab,
79 active_keyboard); 86 active_keyboard);
80 wlr_input_method_keyboard_grab_v2_send_modifiers(keyboard_grab,
81 &active_keyboard->modifiers);
82 87
83 wl_signal_add(&keyboard_grab->events.destroy, 88 wl_signal_add(&keyboard_grab->events.destroy,
84 &relay->input_method_keyboard_grab_destroy); 89 &relay->input_method_keyboard_grab_destroy);
@@ -104,6 +109,10 @@ static void handle_im_destroy(struct wl_listener *listener, void *data) {
104 input_method_destroy); 109 input_method_destroy);
105 struct wlr_input_method_v2 *context = data; 110 struct wlr_input_method_v2 *context = data;
106 assert(context == relay->input_method); 111 assert(context == relay->input_method);
112 wl_list_remove(&relay->input_method_commit.link);
113 wl_list_remove(&relay->input_method_grab_keyboard.link);
114 wl_list_remove(&relay->input_method_destroy.link);
115 wl_list_remove(&relay->input_method_new_popup_surface.link);
107 relay->input_method = NULL; 116 relay->input_method = NULL;
108 struct sway_text_input *text_input = relay_get_focused_text_input(relay); 117 struct sway_text_input *text_input = relay_get_focused_text_input(relay);
109 if (text_input) { 118 if (text_input) {
@@ -135,6 +144,11 @@ static void relay_send_im_state(struct sway_input_method_relay *relay,
135 input->current.content_type.hint, 144 input->current.content_type.hint,
136 input->current.content_type.purpose); 145 input->current.content_type.purpose);
137 } 146 }
147 struct sway_input_popup *popup;
148 wl_list_for_each(popup, &relay->input_popups, link) {
149 // send_text_input_rectangle is called in this function
150 input_popup_update(popup);
151 }
138 wlr_input_method_v2_send_done(input_method); 152 wlr_input_method_v2_send_done(input_method);
139 // TODO: pass intent, display popup size 153 // TODO: pass intent, display popup size
140} 154}
@@ -257,6 +271,211 @@ static void relay_handle_text_input(struct wl_listener *listener,
257 sway_text_input_create(relay, wlr_text_input); 271 sway_text_input_create(relay, wlr_text_input);
258} 272}
259 273
274static void input_popup_update(struct sway_input_popup *popup) {
275 struct sway_text_input *text_input =
276 relay_get_focused_text_input(popup->relay);
277
278 if (text_input == NULL || text_input->input->focused_surface == NULL) {
279 return;
280 }
281
282 if (popup->scene_tree != NULL) {
283 wlr_scene_node_destroy(&popup->scene_tree->node);
284 popup->scene_tree = NULL;
285 }
286 if (popup->desc.relative != NULL) {
287 wlr_scene_node_destroy(popup->desc.relative);
288 popup->desc.relative = NULL;
289 }
290 popup->desc.view = NULL;
291
292 if (!popup->popup_surface->surface->mapped) {
293 return;
294 }
295
296 bool cursor_rect = text_input->input->current.features
297 & WLR_TEXT_INPUT_V3_FEATURE_CURSOR_RECTANGLE;
298 struct wlr_surface *focused_surface = text_input->input->focused_surface;
299 struct wlr_box cursor_area = text_input->input->current.cursor_rectangle;
300
301 struct wlr_box output_box;
302 struct wlr_box parent = {0};
303 struct wlr_layer_surface_v1 *layer_surface =
304 wlr_layer_surface_v1_try_from_wlr_surface(focused_surface);
305 struct wlr_scene_tree *relative_parent;
306
307 struct wlr_box geo = {0};
308
309 popup->scene_tree = wlr_scene_subsurface_tree_create(root->layers.popup, popup->popup_surface->surface);
310 if (layer_surface != NULL) {
311 struct sway_layer_surface *layer =
312 layer_surface->data;
313 if (layer == NULL) {
314 return;
315 }
316
317 relative_parent = layer->scene->tree;
318 struct wlr_output *output = layer->layer_surface->output;
319 wlr_output_layout_get_box(root->output_layout, output, &output_box);
320 int lx, ly;
321 wlr_scene_node_coords(&layer->tree->node, &lx, &ly);
322 parent.x = lx;
323 parent.y = ly;
324 popup->desc.view = NULL;
325 } else {
326 struct sway_view *view = view_from_wlr_surface(focused_surface);
327 relative_parent = view->scene_tree;
328 geo = view->geometry;
329 int lx, ly;
330 wlr_scene_node_coords(&view->scene_tree->node, &lx, &ly);
331 struct wlr_output *output = wlr_output_layout_output_at(root->output_layout,
332 view->container->pending.content_x + view->geometry.x,
333 view->container->pending.content_y + view->geometry.y);
334 wlr_output_layout_get_box(root->output_layout, output, &output_box);
335 parent.x = lx;
336 parent.y = ly;
337
338 parent.width = view->geometry.width;
339 parent.height = view->geometry.height;
340 popup->desc.view = view;
341 }
342
343 struct wlr_scene_tree *relative = wlr_scene_tree_create(relative_parent);
344
345 popup->desc.relative = &relative->node;
346 if (!scene_descriptor_assign(&popup->scene_tree->node,
347 SWAY_SCENE_DESC_POPUP, &popup->desc)) {
348 wlr_scene_node_destroy(&popup->scene_tree->node);
349 popup->scene_tree = NULL;
350 return;
351 }
352
353 if (!cursor_rect) {
354 cursor_area.x = 0;
355 cursor_area.y = 0;
356 cursor_area.width = parent.width;
357 cursor_area.height = parent.height;
358 }
359
360 int popup_width = popup->popup_surface->surface->current.width;
361 int popup_height = popup->popup_surface->surface->current.height;
362 int x1 = parent.x + cursor_area.x;
363 int x2 = parent.x + cursor_area.x + cursor_area.width;
364 int y1 = parent.y + cursor_area.y;
365 int y2 = parent.y + cursor_area.y + cursor_area.height;
366 int x = x1;
367 int y = y2;
368
369 int available_right = output_box.x + output_box.width - x1;
370 int available_left = x2 - output_box.x;
371 if (available_right < popup_width && available_left > available_right) {
372 x = x2 - popup_width;
373 }
374
375 int available_down = output_box.y + output_box.height - y2;
376 int available_up = y1 - output_box.y;
377 if (available_down < popup_height && available_up > available_down) {
378 y = y1 - popup_height;
379 }
380
381 wlr_scene_node_set_position(&relative->node, x - parent.x - geo.x, y - parent.y - geo.y);
382 if (cursor_rect) {
383 struct wlr_box box = {
384 .x = x1 - x,
385 .y = y1 - y,
386 .width = cursor_area.width,
387 .height = cursor_area.height,
388 };
389 wlr_input_popup_surface_v2_send_text_input_rectangle(
390 popup->popup_surface, &box);
391 }
392 wlr_scene_node_set_position(&popup->scene_tree->node, x - geo.x, y - geo.y);
393}
394
395static void input_popup_set_focus(struct sway_input_popup *popup,
396 struct wlr_surface *surface) {
397 wl_list_remove(&popup->focused_surface_unmap.link);
398
399 if (surface == NULL) {
400 wl_list_init(&popup->focused_surface_unmap.link);
401 input_popup_update(popup);
402 return;
403 }
404 struct wlr_layer_surface_v1 *layer_surface =
405 wlr_layer_surface_v1_try_from_wlr_surface(surface);
406 if (layer_surface != NULL) {
407 wl_signal_add(
408 &layer_surface->surface->events.unmap, &popup->focused_surface_unmap);
409 input_popup_update(popup);
410 return;
411 }
412
413 struct sway_view *view = view_from_wlr_surface(surface);
414 wl_signal_add(&view->events.unmap, &popup->focused_surface_unmap);
415}
416
417static void handle_im_popup_destroy(struct wl_listener *listener, void *data) {
418 struct sway_input_popup *popup =
419 wl_container_of(listener, popup, popup_destroy);
420 wl_list_remove(&popup->focused_surface_unmap.link);
421 wl_list_remove(&popup->popup_surface_commit.link);
422 wl_list_remove(&popup->popup_destroy.link);
423 wl_list_remove(&popup->link);
424
425 free(popup);
426}
427
428static void handle_im_popup_surface_commit(struct wl_listener *listener,
429 void *data) {
430 struct sway_input_popup *popup =
431 wl_container_of(listener, popup, popup_surface_commit);
432 input_popup_update(popup);
433}
434
435static void handle_im_focused_surface_unmap(
436 struct wl_listener *listener, void *data) {
437 struct sway_input_popup *popup =
438 wl_container_of(listener, popup, focused_surface_unmap);
439 input_popup_update(popup);
440}
441
442static void handle_im_new_popup_surface(struct wl_listener *listener,
443 void *data) {
444 struct sway_input_method_relay *relay = wl_container_of(listener, relay,
445 input_method_new_popup_surface);
446 struct sway_input_popup *popup = calloc(1, sizeof(*popup));
447 popup->relay = relay;
448 popup->popup_surface = data;
449 popup->popup_surface->data = popup;
450
451 wl_signal_add(
452 &popup->popup_surface->events.destroy, &popup->popup_destroy);
453 popup->popup_destroy.notify = handle_im_popup_destroy;
454 wl_signal_add(&popup->popup_surface->surface->events.commit,
455 &popup->popup_surface_commit);
456 popup->popup_surface_commit.notify = handle_im_popup_surface_commit;
457 wl_list_init(&popup->focused_surface_unmap.link);
458 popup->focused_surface_unmap.notify = handle_im_focused_surface_unmap;
459
460 struct sway_text_input *text_input = relay_get_focused_text_input(relay);
461 if (text_input != NULL) {
462 input_popup_set_focus(popup, text_input->input->focused_surface);
463 } else {
464 input_popup_set_focus(popup, NULL);
465 }
466
467 wl_list_insert(&relay->input_popups, &popup->link);
468}
469
470static void text_input_send_enter(struct sway_text_input *text_input,
471 struct wlr_surface *surface) {
472 wlr_text_input_v3_send_enter(text_input->input, surface);
473 struct sway_input_popup *popup;
474 wl_list_for_each(popup, &text_input->relay->input_popups, link) {
475 input_popup_set_focus(popup, surface);
476 }
477}
478
260static void relay_handle_input_method(struct wl_listener *listener, 479static void relay_handle_input_method(struct wl_listener *listener,
261 void *data) { 480 void *data) {
262 struct sway_input_method_relay *relay = wl_container_of(listener, relay, 481 struct sway_input_method_relay *relay = wl_container_of(listener, relay,
@@ -282,10 +501,13 @@ static void relay_handle_input_method(struct wl_listener *listener,
282 wl_signal_add(&relay->input_method->events.destroy, 501 wl_signal_add(&relay->input_method->events.destroy,
283 &relay->input_method_destroy); 502 &relay->input_method_destroy);
284 relay->input_method_destroy.notify = handle_im_destroy; 503 relay->input_method_destroy.notify = handle_im_destroy;
504 wl_signal_add(&relay->input_method->events.new_popup_surface,
505 &relay->input_method_new_popup_surface);
506 relay->input_method_new_popup_surface.notify = handle_im_new_popup_surface;
285 507
286 struct sway_text_input *text_input = relay_get_focusable_text_input(relay); 508 struct sway_text_input *text_input = relay_get_focusable_text_input(relay);
287 if (text_input) { 509 if (text_input) {
288 wlr_text_input_v3_send_enter(text_input->input, 510 text_input_send_enter(text_input,
289 text_input->pending_focused_surface); 511 text_input->pending_focused_surface);
290 text_input_set_pending_focused_surface(text_input, NULL); 512 text_input_set_pending_focused_surface(text_input, NULL);
291 } 513 }
@@ -295,6 +517,7 @@ void sway_input_method_relay_init(struct sway_seat *seat,
295 struct sway_input_method_relay *relay) { 517 struct sway_input_method_relay *relay) {
296 relay->seat = seat; 518 relay->seat = seat;
297 wl_list_init(&relay->text_inputs); 519 wl_list_init(&relay->text_inputs);
520 wl_list_init(&relay->input_popups);
298 521
299 relay->text_input_new.notify = relay_handle_text_input; 522 relay->text_input_new.notify = relay_handle_text_input;
300 wl_signal_add(&server.text_input->events.text_input, 523 wl_signal_add(&server.text_input->events.text_input,
diff --git a/sway/ipc-json.c b/sway/ipc-json.c
index 1b64f86e..b7370aa6 100644
--- a/sway/ipc-json.c
+++ b/sway/ipc-json.c
@@ -3,7 +3,8 @@
3#include <json.h> 3#include <json.h>
4#include <libevdev/libevdev.h> 4#include <libevdev/libevdev.h>
5#include <stdio.h> 5#include <stdio.h>
6#include <wlr/backend/libinput.h> 6#include <wlr/config.h>
7#include <wlr/types/wlr_content_type_v1.h>
7#include <wlr/types/wlr_output.h> 8#include <wlr/types/wlr_output.h>
8#include <xkbcommon/xkbcommon.h> 9#include <xkbcommon/xkbcommon.h>
9#include "config.h" 10#include "config.h"
@@ -20,6 +21,10 @@
20#include "wlr-layer-shell-unstable-v1-protocol.h" 21#include "wlr-layer-shell-unstable-v1-protocol.h"
21#include "sway/desktop/idle_inhibit_v1.h" 22#include "sway/desktop/idle_inhibit_v1.h"
22 23
24#if WLR_HAS_LIBINPUT_BACKEND
25#include <wlr/backend/libinput.h>
26#endif
27
23static const int i3_output_id = INT32_MAX; 28static const int i3_output_id = INT32_MAX;
24static const int i3_scratch_id = INT32_MAX - 1; 29static const int i3_scratch_id = INT32_MAX - 1;
25 30
@@ -112,13 +117,44 @@ static const char *ipc_json_output_adaptive_sync_status_description(
112 return "disabled"; 117 return "disabled";
113 case WLR_OUTPUT_ADAPTIVE_SYNC_ENABLED: 118 case WLR_OUTPUT_ADAPTIVE_SYNC_ENABLED:
114 return "enabled"; 119 return "enabled";
115 case WLR_OUTPUT_ADAPTIVE_SYNC_UNKNOWN:
116 return "unknown";
117 } 120 }
118 return NULL; 121 return NULL;
119} 122}
120 123
121#if HAVE_XWAYLAND 124static const char *ipc_json_output_mode_aspect_ratio_description(
125 enum wlr_output_mode_aspect_ratio aspect_ratio) {
126 switch (aspect_ratio) {
127 case WLR_OUTPUT_MODE_ASPECT_RATIO_NONE:
128 return "none";
129 case WLR_OUTPUT_MODE_ASPECT_RATIO_4_3:
130 return "4:3";
131 case WLR_OUTPUT_MODE_ASPECT_RATIO_16_9:
132 return "16:9";
133 case WLR_OUTPUT_MODE_ASPECT_RATIO_64_27:
134 return "64:27";
135 case WLR_OUTPUT_MODE_ASPECT_RATIO_256_135:
136 return "256:135";
137 }
138 return NULL;
139}
140
141static json_object *ipc_json_output_mode_description(
142 const struct wlr_output_mode *mode) {
143 const char *pic_ar =
144 ipc_json_output_mode_aspect_ratio_description(mode->picture_aspect_ratio);
145 json_object *mode_object = json_object_new_object();
146 json_object_object_add(mode_object, "width",
147 json_object_new_int(mode->width));
148 json_object_object_add(mode_object, "height",
149 json_object_new_int(mode->height));
150 json_object_object_add(mode_object, "refresh",
151 json_object_new_int(mode->refresh));
152 json_object_object_add(mode_object, "picture_aspect_ratio",
153 json_object_new_string(pic_ar));
154 return mode_object;
155}
156
157#if WLR_HAS_XWAYLAND
122static const char *ipc_json_xwindow_type_description(struct sway_view *view) { 158static const char *ipc_json_xwindow_type_description(struct sway_view *view) {
123 struct wlr_xwayland_surface *surface = view->wlr_xwayland_surface; 159 struct wlr_xwayland_surface *surface = view->wlr_xwayland_surface;
124 struct sway_xwayland *xwayland = &server.xwayland; 160 struct sway_xwayland *xwayland = &server.xwayland;
@@ -170,6 +206,20 @@ static const char *ipc_json_user_idle_inhibitor_description(enum sway_idle_inhib
170 return NULL; 206 return NULL;
171} 207}
172 208
209static const char *ipc_json_content_type_description(enum wp_content_type_v1_type type) {
210 switch (type) {
211 case WP_CONTENT_TYPE_V1_TYPE_NONE:
212 return "none";
213 case WP_CONTENT_TYPE_V1_TYPE_PHOTO:
214 return "photo";
215 case WP_CONTENT_TYPE_V1_TYPE_VIDEO:
216 return "video";
217 case WP_CONTENT_TYPE_V1_TYPE_GAME:
218 return "game";
219 }
220 return NULL;
221}
222
173json_object *ipc_json_get_version(void) { 223json_object *ipc_json_get_version(void) {
174 int major = 0, minor = 0, patch = 0; 224 int major = 0, minor = 0, patch = 0;
175 json_object *version = json_object_new_object(); 225 json_object *version = json_object_new_object();
@@ -238,27 +288,56 @@ static json_object *ipc_json_create_node(int id, const char* type, char *name,
238 json_object_object_add(object, "focus", focus); 288 json_object_object_add(object, "focus", focus);
239 json_object_object_add(object, "fullscreen_mode", json_object_new_int(0)); 289 json_object_object_add(object, "fullscreen_mode", json_object_new_int(0));
240 json_object_object_add(object, "sticky", json_object_new_boolean(false)); 290 json_object_object_add(object, "sticky", json_object_new_boolean(false));
291 json_object_object_add(object, "floating", NULL);
292 json_object_object_add(object, "scratchpad_state", NULL);
241 293
242 return object; 294 return object;
243} 295}
244 296
297static void ipc_json_describe_wlr_output(struct wlr_output *wlr_output, json_object *object) {
298 json_object_object_add(object, "primary", json_object_new_boolean(false));
299 json_object_object_add(object, "make",
300 json_object_new_string(wlr_output->make ? wlr_output->make : "Unknown"));
301 json_object_object_add(object, "model",
302 json_object_new_string(wlr_output->model ? wlr_output->model : "Unknown"));
303 json_object_object_add(object, "serial",
304 json_object_new_string(wlr_output->serial ? wlr_output->serial : "Unknown"));
305
306 json_object *modes_array = json_object_new_array();
307 struct wlr_output_mode *mode;
308 wl_list_for_each(mode, &wlr_output->modes, link) {
309 json_object *mode_object = json_object_new_object();
310 json_object_object_add(mode_object, "width",
311 json_object_new_int(mode->width));
312 json_object_object_add(mode_object, "height",
313 json_object_new_int(mode->height));
314 json_object_object_add(mode_object, "refresh",
315 json_object_new_int(mode->refresh));
316 json_object_array_add(modes_array, mode_object);
317 }
318 json_object_object_add(object, "modes", modes_array);
319}
320
245static void ipc_json_describe_output(struct sway_output *output, 321static void ipc_json_describe_output(struct sway_output *output,
246 json_object *object) { 322 json_object *object) {
323 ipc_json_describe_wlr_output(output->wlr_output, object);
324}
325
326static void ipc_json_describe_enabled_output(struct sway_output *output,
327 json_object *object) {
328 ipc_json_describe_output(output, object);
329
247 struct wlr_output *wlr_output = output->wlr_output; 330 struct wlr_output *wlr_output = output->wlr_output;
331 json_object_object_add(object, "non_desktop", json_object_new_boolean(false));
248 json_object_object_add(object, "active", json_object_new_boolean(true)); 332 json_object_object_add(object, "active", json_object_new_boolean(true));
249 json_object_object_add(object, "dpms", 333 json_object_object_add(object, "dpms",
250 json_object_new_boolean(wlr_output->enabled)); 334 json_object_new_boolean(wlr_output->enabled));
251 json_object_object_add(object, "primary", json_object_new_boolean(false)); 335 json_object_object_add(object, "power",
336 json_object_new_boolean(wlr_output->enabled));
252 json_object_object_add(object, "layout", json_object_new_string("output")); 337 json_object_object_add(object, "layout", json_object_new_string("output"));
253 json_object_object_add(object, "orientation", 338 json_object_object_add(object, "orientation",
254 json_object_new_string( 339 json_object_new_string(
255 ipc_json_orientation_description(L_NONE))); 340 ipc_json_orientation_description(L_NONE)));
256 json_object_object_add(object, "make",
257 json_object_new_string(wlr_output->make));
258 json_object_object_add(object, "model",
259 json_object_new_string(wlr_output->model));
260 json_object_object_add(object, "serial",
261 json_object_new_string(wlr_output->serial));
262 json_object_object_add(object, "scale", 341 json_object_object_add(object, "scale",
263 json_object_new_double(wlr_output->scale)); 342 json_object_new_double(wlr_output->scale));
264 json_object_object_add(object, "scale_filter", 343 json_object_object_add(object, "scale_filter",
@@ -283,25 +362,26 @@ static void ipc_json_describe_output(struct sway_output *output,
283 json_object *modes_array = json_object_new_array(); 362 json_object *modes_array = json_object_new_array();
284 struct wlr_output_mode *mode; 363 struct wlr_output_mode *mode;
285 wl_list_for_each(mode, &wlr_output->modes, link) { 364 wl_list_for_each(mode, &wlr_output->modes, link) {
286 json_object *mode_object = json_object_new_object(); 365 json_object *mode_object =
287 json_object_object_add(mode_object, "width", 366 ipc_json_output_mode_description(mode);
288 json_object_new_int(mode->width));
289 json_object_object_add(mode_object, "height",
290 json_object_new_int(mode->height));
291 json_object_object_add(mode_object, "refresh",
292 json_object_new_int(mode->refresh));
293 json_object_array_add(modes_array, mode_object); 367 json_object_array_add(modes_array, mode_object);
294 } 368 }
295 369
296 json_object_object_add(object, "modes", modes_array); 370 json_object_object_add(object, "modes", modes_array);
297 371
298 json_object *current_mode_object = json_object_new_object(); 372 json_object *current_mode_object;
299 json_object_object_add(current_mode_object, "width", 373 if (wlr_output->current_mode != NULL) {
300 json_object_new_int(wlr_output->width)); 374 current_mode_object =
301 json_object_object_add(current_mode_object, "height", 375 ipc_json_output_mode_description(wlr_output->current_mode);
302 json_object_new_int(wlr_output->height)); 376 } else {
303 json_object_object_add(current_mode_object, "refresh", 377 current_mode_object = json_object_new_object();
304 json_object_new_int(wlr_output->refresh)); 378 json_object_object_add(current_mode_object, "width",
379 json_object_new_int(wlr_output->width));
380 json_object_object_add(current_mode_object, "height",
381 json_object_new_int(wlr_output->height));
382 json_object_object_add(current_mode_object, "refresh",
383 json_object_new_int(wlr_output->refresh));
384 }
305 json_object_object_add(object, "current_mode", current_mode_object); 385 json_object_object_add(object, "current_mode", current_mode_object);
306 386
307 struct sway_node *parent = node_get_parent(&output->node); 387 struct sway_node *parent = node_get_parent(&output->node);
@@ -325,33 +405,15 @@ json_object *ipc_json_describe_disabled_output(struct sway_output *output) {
325 405
326 json_object *object = json_object_new_object(); 406 json_object *object = json_object_new_object();
327 407
408 ipc_json_describe_output(output, object);
409
410 json_object_object_add(object, "non_desktop", json_object_new_boolean(false));
328 json_object_object_add(object, "type", json_object_new_string("output")); 411 json_object_object_add(object, "type", json_object_new_string("output"));
329 json_object_object_add(object, "name", 412 json_object_object_add(object, "name",
330 json_object_new_string(wlr_output->name)); 413 json_object_new_string(wlr_output->name));
331 json_object_object_add(object, "active", json_object_new_boolean(false)); 414 json_object_object_add(object, "active", json_object_new_boolean(false));
332 json_object_object_add(object, "dpms", json_object_new_boolean(false)); 415 json_object_object_add(object, "dpms", json_object_new_boolean(false));
333 json_object_object_add(object, "primary", json_object_new_boolean(false)); 416 json_object_object_add(object, "power", json_object_new_boolean(false));
334 json_object_object_add(object, "make",
335 json_object_new_string(wlr_output->make));
336 json_object_object_add(object, "model",
337 json_object_new_string(wlr_output->model));
338 json_object_object_add(object, "serial",
339 json_object_new_string(wlr_output->serial));
340
341 json_object *modes_array = json_object_new_array();
342 struct wlr_output_mode *mode;
343 wl_list_for_each(mode, &wlr_output->modes, link) {
344 json_object *mode_object = json_object_new_object();
345 json_object_object_add(mode_object, "width",
346 json_object_new_int(mode->width));
347 json_object_object_add(mode_object, "height",
348 json_object_new_int(mode->height));
349 json_object_object_add(mode_object, "refresh",
350 json_object_new_int(mode->refresh));
351 json_object_array_add(modes_array, mode_object);
352 }
353
354 json_object_object_add(object, "modes", modes_array);
355 417
356 json_object_object_add(object, "current_workspace", NULL); 418 json_object_object_add(object, "current_workspace", NULL);
357 419
@@ -367,6 +429,21 @@ json_object *ipc_json_describe_disabled_output(struct sway_output *output) {
367 return object; 429 return object;
368} 430}
369 431
432json_object *ipc_json_describe_non_desktop_output(struct sway_output_non_desktop *output) {
433 struct wlr_output *wlr_output = output->wlr_output;
434
435 json_object *object = json_object_new_object();
436
437 ipc_json_describe_wlr_output(wlr_output, object);
438
439 json_object_object_add(object, "non_desktop", json_object_new_boolean(true));
440 json_object_object_add(object, "type", json_object_new_string("output"));
441 json_object_object_add(object, "name",
442 json_object_new_string(wlr_output->name));
443
444 return object;
445}
446
370static json_object *ipc_json_describe_scratchpad_output(void) { 447static json_object *ipc_json_describe_scratchpad_output(void) {
371 struct wlr_box box; 448 struct wlr_box box;
372 root_get_box(root, &box); 449 root_get_box(root, &box);
@@ -453,7 +530,9 @@ static void ipc_json_describe_workspace(struct sway_workspace *workspace,
453 530
454static void get_deco_rect(struct sway_container *c, struct wlr_box *deco_rect) { 531static void get_deco_rect(struct sway_container *c, struct wlr_box *deco_rect) {
455 enum sway_container_layout parent_layout = container_parent_layout(c); 532 enum sway_container_layout parent_layout = container_parent_layout(c);
456 bool tab_or_stack = parent_layout == L_TABBED || parent_layout == L_STACKED; 533 list_t *siblings = container_get_siblings(c);
534 bool tab_or_stack = (parent_layout == L_TABBED || parent_layout == L_STACKED)
535 && ((siblings && siblings->length > 1) || !config->hide_lone_tab);
457 if (((!tab_or_stack || container_is_floating(c)) && 536 if (((!tab_or_stack || container_is_floating(c)) &&
458 c->current.border != B_NORMAL) || 537 c->current.border != B_NORMAL) ||
459 c->pending.fullscreen_mode != FULLSCREEN_NONE || 538 c->pending.fullscreen_mode != FULLSCREEN_NONE ||
@@ -500,7 +579,7 @@ static void ipc_json_describe_view(struct sway_container *c, json_object *object
500 579
501 struct wlr_box window_box = { 580 struct wlr_box window_box = {
502 c->pending.content_x - c->pending.x, 581 c->pending.content_x - c->pending.x,
503 (c->current.border == B_PIXEL) ? c->current.border_thickness : 0, 582 (c->current.border == B_PIXEL) ? c->pending.content_y - c->pending.y : 0,
504 c->pending.content_width, 583 c->pending.content_width,
505 c->pending.content_height 584 c->pending.content_height
506 }; 585 };
@@ -544,7 +623,17 @@ static void ipc_json_describe_view(struct sway_container *c, json_object *object
544 623
545 json_object_object_add(object, "idle_inhibitors", idle_inhibitors); 624 json_object_object_add(object, "idle_inhibitors", idle_inhibitors);
546 625
547#if HAVE_XWAYLAND 626 enum wp_content_type_v1_type content_type = WP_CONTENT_TYPE_V1_TYPE_NONE;
627 if (c->view->surface != NULL) {
628 content_type = wlr_surface_get_content_type_v1(server.content_type_manager_v1,
629 c->view->surface);
630 }
631 if (content_type != WP_CONTENT_TYPE_V1_TYPE_NONE) {
632 json_object_object_add(object, "content_type",
633 json_object_new_string(ipc_json_content_type_description(content_type)));
634 }
635
636#if WLR_HAS_XWAYLAND
548 if (c->view->type == SWAY_VIEW_XWAYLAND) { 637 if (c->view->type == SWAY_VIEW_XWAYLAND) {
549 json_object_object_add(object, "window", 638 json_object_object_add(object, "window",
550 json_object_new_int(view_get_x11_window_id(c->view))); 639 json_object_new_int(view_get_x11_window_id(c->view)));
@@ -588,7 +677,8 @@ static void ipc_json_describe_view(struct sway_container *c, json_object *object
588static void ipc_json_describe_container(struct sway_container *c, json_object *object) { 677static void ipc_json_describe_container(struct sway_container *c, json_object *object) {
589 json_object_object_add(object, "name", 678 json_object_object_add(object, "name",
590 c->title ? json_object_new_string(c->title) : NULL); 679 c->title ? json_object_new_string(c->title) : NULL);
591 if (container_is_floating(c)) { 680 bool floating = container_is_floating(c);
681 if (floating) {
592 json_object_object_add(object, "type", 682 json_object_object_add(object, "type",
593 json_object_new_string("floating_con")); 683 json_object_new_string("floating_con"));
594 } 684 }
@@ -606,9 +696,17 @@ static void ipc_json_describe_container(struct sway_container *c, json_object *o
606 json_object_object_add(object, "urgent", json_object_new_boolean(urgent)); 696 json_object_object_add(object, "urgent", json_object_new_boolean(urgent));
607 json_object_object_add(object, "sticky", json_object_new_boolean(c->is_sticky)); 697 json_object_object_add(object, "sticky", json_object_new_boolean(c->is_sticky));
608 698
699 // sway doesn't track the floating reason, so we can't use "auto_on" or "user_off"
700 json_object_object_add(object, "floating",
701 json_object_new_string(floating ? "user_on" : "auto_off"));
702
609 json_object_object_add(object, "fullscreen_mode", 703 json_object_object_add(object, "fullscreen_mode",
610 json_object_new_int(c->pending.fullscreen_mode)); 704 json_object_new_int(c->pending.fullscreen_mode));
611 705
706 // sway doesn't track if window was resized in scratchpad, so we can't use "changed"
707 json_object_object_add(object, "scratchpad_state",
708 json_object_new_string(!c->scratchpad ? "none" : "fresh"));
709
612 struct sway_node *parent = node_get_parent(&c->node); 710 struct sway_node *parent = node_get_parent(&c->node);
613 struct wlr_box parent_box = {0, 0, 0, 0}; 711 struct wlr_box parent_box = {0, 0, 0, 0};
614 712
@@ -706,7 +804,7 @@ json_object *ipc_json_describe_node(struct sway_node *node) {
706 case N_ROOT: 804 case N_ROOT:
707 break; 805 break;
708 case N_OUTPUT: 806 case N_OUTPUT:
709 ipc_json_describe_output(node->sway_output, object); 807 ipc_json_describe_enabled_output(node->sway_output, object);
710 break; 808 break;
711 case N_CONTAINER: 809 case N_CONTAINER:
712 ipc_json_describe_container(node->sway_container, object); 810 ipc_json_describe_container(node->sway_container, object);
@@ -764,6 +862,7 @@ json_object *ipc_json_describe_node_recursive(struct sway_node *node) {
764 return object; 862 return object;
765} 863}
766 864
865#if WLR_HAS_LIBINPUT_BACKEND
767static json_object *describe_libinput_device(struct libinput_device *device) { 866static json_object *describe_libinput_device(struct libinput_device *device) {
768 json_object *object = json_object_new_object(); 867 json_object *object = json_object_new_object();
769 868
@@ -847,6 +946,11 @@ static json_object *describe_libinput_device(struct libinput_device *device) {
847 case LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE: 946 case LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE:
848 accel_profile = "adaptive"; 947 accel_profile = "adaptive";
849 break; 948 break;
949#if HAVE_LIBINPUT_CONFIG_ACCEL_PROFILE_CUSTOM
950 case LIBINPUT_CONFIG_ACCEL_PROFILE_CUSTOM:
951 accel_profile = "custom";
952 break;
953#endif
850 } 954 }
851 json_object_object_add(object, "accel_profile", 955 json_object_object_add(object, "accel_profile",
852 json_object_new_string(accel_profile)); 956 json_object_new_string(accel_profile));
@@ -926,6 +1030,17 @@ static json_object *describe_libinput_device(struct libinput_device *device) {
926 uint32_t button = libinput_device_config_scroll_get_button(device); 1030 uint32_t button = libinput_device_config_scroll_get_button(device);
927 json_object_object_add(object, "scroll_button", 1031 json_object_object_add(object, "scroll_button",
928 json_object_new_int(button)); 1032 json_object_new_int(button));
1033 const char *lock = "unknown";
1034 switch (libinput_device_config_scroll_get_button_lock(device)) {
1035 case LIBINPUT_CONFIG_SCROLL_BUTTON_LOCK_ENABLED:
1036 lock = "enabled";
1037 break;
1038 case LIBINPUT_CONFIG_SCROLL_BUTTON_LOCK_DISABLED:
1039 lock = "disabled";
1040 break;
1041 }
1042 json_object_object_add(object, "scroll_button_lock",
1043 json_object_new_string(lock));
929 } 1044 }
930 } 1045 }
931 1046
@@ -942,6 +1057,19 @@ static json_object *describe_libinput_device(struct libinput_device *device) {
942 json_object_object_add(object, "dwt", json_object_new_string(dwt)); 1057 json_object_object_add(object, "dwt", json_object_new_string(dwt));
943 } 1058 }
944 1059
1060 if (libinput_device_config_dwtp_is_available(device)) {
1061 const char *dwtp = "unknown";
1062 switch (libinput_device_config_dwtp_get_enabled(device)) {
1063 case LIBINPUT_CONFIG_DWTP_ENABLED:
1064 dwtp = "enabled";
1065 break;
1066 case LIBINPUT_CONFIG_DWTP_DISABLED:
1067 dwtp = "disabled";
1068 break;
1069 }
1070 json_object_object_add(object, "dwtp", json_object_new_string(dwtp));
1071 }
1072
945 if (libinput_device_config_calibration_has_matrix(device)) { 1073 if (libinput_device_config_calibration_has_matrix(device)) {
946 float matrix[6]; 1074 float matrix[6];
947 libinput_device_config_calibration_get_matrix(device, matrix); 1075 libinput_device_config_calibration_get_matrix(device, matrix);
@@ -956,6 +1084,7 @@ static json_object *describe_libinput_device(struct libinput_device *device) {
956 1084
957 return object; 1085 return object;
958} 1086}
1087#endif
959 1088
960json_object *ipc_json_describe_input(struct sway_input_device *device) { 1089json_object *ipc_json_describe_input(struct sway_input_device *device) {
961 if (!(sway_assert(device, "Device must not be null"))) { 1090 if (!(sway_assert(device, "Device must not be null"))) {
@@ -968,19 +1097,21 @@ json_object *ipc_json_describe_input(struct sway_input_device *device) {
968 json_object_new_string(device->identifier)); 1097 json_object_new_string(device->identifier));
969 json_object_object_add(object, "name", 1098 json_object_object_add(object, "name",
970 json_object_new_string(device->wlr_device->name)); 1099 json_object_new_string(device->wlr_device->name));
971 json_object_object_add(object, "vendor",
972 json_object_new_int(device->wlr_device->vendor));
973 json_object_object_add(object, "product",
974 json_object_new_int(device->wlr_device->product));
975 json_object_object_add(object, "type", 1100 json_object_object_add(object, "type",
976 json_object_new_string( 1101 json_object_new_string(
977 input_device_get_type(device))); 1102 input_device_get_type(device)));
978 1103
979 if (device->wlr_device->type == WLR_INPUT_DEVICE_KEYBOARD) { 1104 if (device->wlr_device->type == WLR_INPUT_DEVICE_KEYBOARD) {
980 struct wlr_keyboard *keyboard = device->wlr_device->keyboard; 1105 struct wlr_keyboard *keyboard =
1106 wlr_keyboard_from_input_device(device->wlr_device);
981 struct xkb_keymap *keymap = keyboard->keymap; 1107 struct xkb_keymap *keymap = keyboard->keymap;
982 struct xkb_state *state = keyboard->xkb_state; 1108 struct xkb_state *state = keyboard->xkb_state;
983 1109
1110 json_object_object_add(object, "repeat_delay",
1111 json_object_new_int(keyboard->repeat_info.delay));
1112 json_object_object_add(object, "repeat_rate",
1113 json_object_new_int(keyboard->repeat_info.rate));
1114
984 json_object *layouts_arr = json_object_new_array(); 1115 json_object *layouts_arr = json_object_new_array();
985 json_object_object_add(object, "xkb_layout_names", layouts_arr); 1116 json_object_object_add(object, "xkb_layout_names", layouts_arr);
986 1117
@@ -1005,20 +1136,26 @@ json_object *ipc_json_describe_input(struct sway_input_device *device) {
1005 if (device->wlr_device->type == WLR_INPUT_DEVICE_POINTER) { 1136 if (device->wlr_device->type == WLR_INPUT_DEVICE_POINTER) {
1006 struct input_config *ic = input_device_get_config(device); 1137 struct input_config *ic = input_device_get_config(device);
1007 float scroll_factor = 1.0f; 1138 float scroll_factor = 1.0f;
1008 if (ic != NULL && !isnan(ic->scroll_factor) && 1139 if (ic != NULL && !isnan(ic->scroll_factor) &&
1009 ic->scroll_factor != FLT_MIN) { 1140 ic->scroll_factor != FLT_MIN) {
1010 scroll_factor = ic->scroll_factor; 1141 scroll_factor = ic->scroll_factor;
1011 } 1142 }
1012 json_object_object_add(object, "scroll_factor", 1143 json_object_object_add(object, "scroll_factor",
1013 json_object_new_double(scroll_factor)); 1144 json_object_new_double(scroll_factor));
1014 } 1145 }
1015 1146
1147#if WLR_HAS_LIBINPUT_BACKEND
1016 if (wlr_input_device_is_libinput(device->wlr_device)) { 1148 if (wlr_input_device_is_libinput(device->wlr_device)) {
1017 struct libinput_device *libinput_dev; 1149 struct libinput_device *libinput_dev;
1018 libinput_dev = wlr_libinput_get_device_handle(device->wlr_device); 1150 libinput_dev = wlr_libinput_get_device_handle(device->wlr_device);
1019 json_object_object_add(object, "libinput", 1151 json_object_object_add(object, "libinput",
1020 describe_libinput_device(libinput_dev)); 1152 describe_libinput_device(libinput_dev));
1153 json_object_object_add(object, "vendor",
1154 json_object_new_int(libinput_device_get_id_vendor(libinput_dev)));
1155 json_object_object_add(object, "product",
1156 json_object_new_int(libinput_device_get_id_product(libinput_dev)));
1021 } 1157 }
1158#endif
1022 1159
1023 return object; 1160 return object;
1024} 1161}
diff --git a/sway/ipc-server.c b/sway/ipc-server.c
index aad9a7b5..7f353c0e 100644
--- a/sway/ipc-server.c
+++ b/sway/ipc-server.c
@@ -1,5 +1,4 @@
1// See https://i3wm.org/docs/ipc.html for protocol information 1// See https://i3wm.org/docs/ipc.html for protocol information
2#define _POSIX_C_SOURCE 200112L
3#include <linux/input-event-codes.h> 2#include <linux/input-event-codes.h>
4#include <assert.h> 3#include <assert.h>
5#include <errno.h> 4#include <errno.h>
@@ -56,7 +55,6 @@ struct ipc_client {
56 enum ipc_command_type pending_type; 55 enum ipc_command_type pending_type;
57}; 56};
58 57
59struct sockaddr_un *ipc_user_sockaddr(void);
60int ipc_handle_connection(int fd, uint32_t mask, void *data); 58int ipc_handle_connection(int fd, uint32_t mask, void *data);
61int ipc_client_handle_readable(int client_fd, uint32_t mask, void *data); 59int ipc_client_handle_readable(int client_fd, uint32_t mask, void *data);
62int ipc_client_handle_writable(int client_fd, uint32_t mask, void *data); 60int ipc_client_handle_writable(int client_fd, uint32_t mask, void *data);
@@ -150,7 +148,6 @@ struct sockaddr_un *ipc_user_sockaddr(void) {
150int ipc_handle_connection(int fd, uint32_t mask, void *data) { 148int ipc_handle_connection(int fd, uint32_t mask, void *data) {
151 (void) fd; 149 (void) fd;
152 struct sway_server *server = data; 150 struct sway_server *server = data;
153 sway_log(SWAY_DEBUG, "Event on IPC listening socket");
154 assert(mask == WL_EVENT_READABLE); 151 assert(mask == WL_EVENT_READABLE);
155 152
156 int client_fd = accept(ipc_socket, NULL, NULL); 153 int client_fd = accept(ipc_socket, NULL, NULL);
@@ -211,13 +208,10 @@ int ipc_client_handle_readable(int client_fd, uint32_t mask, void *data) {
211 } 208 }
212 209
213 if (mask & WL_EVENT_HANGUP) { 210 if (mask & WL_EVENT_HANGUP) {
214 sway_log(SWAY_DEBUG, "Client %d hung up", client->fd);
215 ipc_client_disconnect(client); 211 ipc_client_disconnect(client);
216 return 0; 212 return 0;
217 } 213 }
218 214
219 sway_log(SWAY_DEBUG, "Client %d readable", client->fd);
220
221 int read_available; 215 int read_available;
222 if (ioctl(client_fd, FIONREAD, &read_available) == -1) { 216 if (ioctl(client_fd, FIONREAD, &read_available) == -1) {
223 sway_log_errno(SWAY_INFO, "Unable to read IPC socket buffer size"); 217 sway_log_errno(SWAY_INFO, "Unable to read IPC socket buffer size");
@@ -513,6 +507,20 @@ void ipc_event_input(const char *change, struct sway_input_device *device) {
513 json_object_put(json); 507 json_object_put(json);
514} 508}
515 509
510void ipc_event_output(void) {
511 if (!ipc_has_event_listeners(IPC_EVENT_OUTPUT)) {
512 return;
513 }
514 sway_log(SWAY_DEBUG, "Sending output event");
515
516 json_object *json = json_object_new_object();
517 json_object_object_add(json, "change", json_object_new_string("unspecified"));
518
519 const char *json_string = json_object_to_json_string(json);
520 ipc_send_event(json_string, IPC_EVENT_OUTPUT);
521 json_object_put(json);
522}
523
516int ipc_client_handle_writable(int client_fd, uint32_t mask, void *data) { 524int ipc_client_handle_writable(int client_fd, uint32_t mask, void *data) {
517 struct ipc_client *client = data; 525 struct ipc_client *client = data;
518 526
@@ -523,7 +531,6 @@ int ipc_client_handle_writable(int client_fd, uint32_t mask, void *data) {
523 } 531 }
524 532
525 if (mask & WL_EVENT_HANGUP) { 533 if (mask & WL_EVENT_HANGUP) {
526 sway_log(SWAY_DEBUG, "Client %d hung up", client->fd);
527 ipc_client_disconnect(client); 534 ipc_client_disconnect(client);
528 return 0; 535 return 0;
529 } 536 }
@@ -532,8 +539,6 @@ int ipc_client_handle_writable(int client_fd, uint32_t mask, void *data) {
532 return 0; 539 return 0;
533 } 540 }
534 541
535 sway_log(SWAY_DEBUG, "Client %d writable", client->fd);
536
537 ssize_t written = write(client->fd, client->write_buffer, client->write_buffer_len); 542 ssize_t written = write(client->fd, client->write_buffer, client->write_buffer_len);
538 543
539 if (written == -1 && errno == EAGAIN) { 544 if (written == -1 && errno == EAGAIN) {
@@ -687,11 +692,17 @@ void ipc_client_handle_command(struct ipc_client *client, uint32_t payload_lengt
687 } 692 }
688 struct sway_output *output; 693 struct sway_output *output;
689 wl_list_for_each(output, &root->all_outputs, link) { 694 wl_list_for_each(output, &root->all_outputs, link) {
690 if (!output->enabled && output != root->noop_output) { 695 if (!output->enabled && output != root->fallback_output) {
691 json_object_array_add(outputs, 696 json_object_array_add(outputs,
692 ipc_json_describe_disabled_output(output)); 697 ipc_json_describe_disabled_output(output));
693 } 698 }
694 } 699 }
700
701 for (int i = 0; i < root->non_desktop_outputs->length; i++) {
702 struct sway_output_non_desktop *non_desktop_output = root->non_desktop_outputs->items[i];
703 json_object_array_add(outputs, ipc_json_describe_non_desktop_output(non_desktop_output));
704 }
705
695 const char *json_string = json_object_to_json_string(outputs); 706 const char *json_string = json_object_to_json_string(outputs);
696 ipc_send_reply(client, payload_type, json_string, 707 ipc_send_reply(client, payload_type, json_string,
697 (uint32_t)strlen(json_string)); 708 (uint32_t)strlen(json_string));
@@ -727,6 +738,8 @@ void ipc_client_handle_command(struct ipc_client *client, uint32_t payload_lengt
727 const char *event_type = json_object_get_string(json_object_array_get_idx(request, i)); 738 const char *event_type = json_object_get_string(json_object_array_get_idx(request, i));
728 if (strcmp(event_type, "workspace") == 0) { 739 if (strcmp(event_type, "workspace") == 0) {
729 client->subscribed_events |= event_mask(IPC_EVENT_WORKSPACE); 740 client->subscribed_events |= event_mask(IPC_EVENT_WORKSPACE);
741 } else if (strcmp(event_type, "output") == 0) {
742 client->subscribed_events |= event_mask(IPC_EVENT_OUTPUT);
730 } else if (strcmp(event_type, "barconfig_update") == 0) { 743 } else if (strcmp(event_type, "barconfig_update") == 0) {
731 client->subscribed_events |= event_mask(IPC_EVENT_BARCONFIG_UPDATE); 744 client->subscribed_events |= event_mask(IPC_EVENT_BARCONFIG_UPDATE);
732 } else if (strcmp(event_type, "bar_state_update") == 0) { 745 } else if (strcmp(event_type, "bar_state_update") == 0) {
@@ -911,7 +924,6 @@ void ipc_client_handle_command(struct ipc_client *client, uint32_t payload_lengt
911 924
912exit_cleanup: 925exit_cleanup:
913 free(buf); 926 free(buf);
914 return;
915} 927}
916 928
917bool ipc_send_reply(struct ipc_client *client, enum ipc_command_type payload_type, 929bool ipc_send_reply(struct ipc_client *client, enum ipc_command_type payload_type,
@@ -955,7 +967,5 @@ bool ipc_send_reply(struct ipc_client *client, enum ipc_command_type payload_typ
955 ipc_client_handle_writable, client); 967 ipc_client_handle_writable, client);
956 } 968 }
957 969
958 sway_log(SWAY_DEBUG, "Added IPC reply of type 0x%x to client %d queue: %s",
959 payload_type, client->fd, payload);
960 return true; 970 return true;
961} 971}
diff --git a/sway/lock.c b/sway/lock.c
new file mode 100644
index 00000000..289e8ca4
--- /dev/null
+++ b/sway/lock.c
@@ -0,0 +1,354 @@
1#include <assert.h>
2#include <wlr/types/wlr_scene.h>
3#include <wlr/types/wlr_session_lock_v1.h>
4#include "log.h"
5#include "sway/input/cursor.h"
6#include "sway/input/keyboard.h"
7#include "sway/input/seat.h"
8#include "sway/layers.h"
9#include "sway/output.h"
10#include "sway/server.h"
11
12struct sway_session_lock_output {
13 struct wlr_scene_tree *tree;
14 struct wlr_scene_rect *background;
15 struct sway_session_lock *lock;
16
17 struct sway_output *output;
18
19 struct wl_list link; // sway_session_lock::outputs
20
21 struct wl_listener destroy;
22 struct wl_listener commit;
23
24 struct wlr_session_lock_surface_v1 *surface;
25
26 // invalid if surface is NULL
27 struct wl_listener surface_destroy;
28 struct wl_listener surface_map;
29};
30
31static void focus_surface(struct sway_session_lock *lock,
32 struct wlr_surface *focused) {
33 lock->focused = focused;
34
35 struct sway_seat *seat;
36 wl_list_for_each(seat, &server.input->seats, link) {
37 seat_set_focus_surface(seat, focused, false);
38 }
39}
40
41static void refocus_output(struct sway_session_lock_output *output) {
42 // Move the seat focus to another surface if one is available
43 if (output->lock->focused == output->surface->surface) {
44 struct wlr_surface *next_focus = NULL;
45
46 struct sway_session_lock_output *candidate;
47 wl_list_for_each(candidate, &output->lock->outputs, link) {
48 if (candidate == output || !candidate->surface) {
49 continue;
50 }
51
52 if (candidate->surface->surface->mapped) {
53 next_focus = candidate->surface->surface;
54 break;
55 }
56 }
57
58 focus_surface(output->lock, next_focus);
59 }
60}
61
62static void handle_surface_map(struct wl_listener *listener, void *data) {
63 struct sway_session_lock_output *surf = wl_container_of(listener, surf, surface_map);
64 if (surf->lock->focused == NULL) {
65 focus_surface(surf->lock, surf->surface->surface);
66 }
67 cursor_rebase_all();
68}
69
70static void handle_surface_destroy(struct wl_listener *listener, void *data) {
71 struct sway_session_lock_output *output =
72 wl_container_of(listener, output, surface_destroy);
73 refocus_output(output);
74
75 sway_assert(output->surface, "Trying to destroy a surface that the lock doesn't think exists");
76 output->surface = NULL;
77 wl_list_remove(&output->surface_destroy.link);
78 wl_list_remove(&output->surface_map.link);
79}
80
81static void lock_output_reconfigure(struct sway_session_lock_output *output) {
82 int width = output->output->width;
83 int height = output->output->height;
84
85 wlr_scene_rect_set_size(output->background, width, height);
86
87 if (output->surface) {
88 wlr_session_lock_surface_v1_configure(output->surface, width, height);
89 }
90}
91
92static void handle_new_surface(struct wl_listener *listener, void *data) {
93 struct sway_session_lock *lock = wl_container_of(listener, lock, new_surface);
94 struct wlr_session_lock_surface_v1 *lock_surface = data;
95 struct sway_output *output = lock_surface->output->data;
96
97 sway_log(SWAY_DEBUG, "new lock layer surface");
98
99 struct sway_session_lock_output *current_lock_output, *lock_output = NULL;
100 wl_list_for_each(current_lock_output, &lock->outputs, link) {
101 if (current_lock_output->output == output) {
102 lock_output = current_lock_output;
103 break;
104 }
105 }
106 sway_assert(lock_output, "Couldn't find output to lock");
107 sway_assert(!lock_output->surface, "Tried to reassign a surface to an existing output");
108
109 lock_output->surface = lock_surface;
110
111 wlr_scene_subsurface_tree_create(lock_output->tree, lock_surface->surface);
112
113 lock_output->surface_destroy.notify = handle_surface_destroy;
114 wl_signal_add(&lock_surface->events.destroy, &lock_output->surface_destroy);
115 lock_output->surface_map.notify = handle_surface_map;
116 wl_signal_add(&lock_surface->surface->events.map, &lock_output->surface_map);
117
118 lock_output_reconfigure(lock_output);
119}
120
121static void sway_session_lock_output_destroy(struct sway_session_lock_output *output) {
122 if (output->surface) {
123 refocus_output(output);
124 wl_list_remove(&output->surface_destroy.link);
125 wl_list_remove(&output->surface_map.link);
126 }
127
128 wl_list_remove(&output->commit.link);
129 wl_list_remove(&output->destroy.link);
130 wl_list_remove(&output->link);
131
132 free(output);
133}
134
135static void lock_node_handle_destroy(struct wl_listener *listener, void *data) {
136 struct sway_session_lock_output *output =
137 wl_container_of(listener, output, destroy);
138 sway_session_lock_output_destroy(output);
139}
140
141static void lock_output_handle_commit(struct wl_listener *listener, void *data) {
142 struct wlr_output_event_commit *event = data;
143 struct sway_session_lock_output *output =
144 wl_container_of(listener, output, commit);
145 if (event->state->committed & (
146 WLR_OUTPUT_STATE_MODE |
147 WLR_OUTPUT_STATE_SCALE |
148 WLR_OUTPUT_STATE_TRANSFORM)) {
149 lock_output_reconfigure(output);
150 }
151}
152
153static struct sway_session_lock_output *session_lock_output_create(
154 struct sway_session_lock *lock, struct sway_output *output) {
155 struct sway_session_lock_output *lock_output = calloc(1, sizeof(*lock_output));
156 if (!lock_output) {
157 sway_log(SWAY_ERROR, "failed to allocate a session lock output");
158 return NULL;
159 }
160
161 struct wlr_scene_tree *tree = wlr_scene_tree_create(output->layers.session_lock);
162 if (!tree) {
163 sway_log(SWAY_ERROR, "failed to allocate a session lock output scene tree");
164 free(lock_output);
165 return NULL;
166 }
167
168 struct wlr_scene_rect *background = wlr_scene_rect_create(tree, 0, 0, (float[4]){
169 lock->abandoned ? 1.f : 0.f,
170 0.f,
171 0.f,
172 1.f,
173 });
174 if (!background) {
175 sway_log(SWAY_ERROR, "failed to allocate a session lock output scene background");
176 wlr_scene_node_destroy(&tree->node);
177 free(lock_output);
178 return NULL;
179 }
180
181 lock_output->output = output;
182 lock_output->tree = tree;
183 lock_output->background = background;
184 lock_output->lock = lock;
185
186 lock_output->destroy.notify = lock_node_handle_destroy;
187 wl_signal_add(&tree->node.events.destroy, &lock_output->destroy);
188
189 lock_output->commit.notify = lock_output_handle_commit;
190 wl_signal_add(&output->wlr_output->events.commit, &lock_output->commit);
191
192 lock_output_reconfigure(lock_output);
193
194 wl_list_insert(&lock->outputs, &lock_output->link);
195
196 return lock_output;
197}
198
199static void sway_session_lock_destroy(struct sway_session_lock* lock) {
200 struct sway_session_lock_output *lock_output, *tmp_lock_output;
201 wl_list_for_each_safe(lock_output, tmp_lock_output, &lock->outputs, link) {
202 // destroying the node will also destroy the whole lock output
203 wlr_scene_node_destroy(&lock_output->tree->node);
204 }
205
206 if (server.session_lock.lock == lock) {
207 server.session_lock.lock = NULL;
208 }
209
210 if (!lock->abandoned) {
211 wl_list_remove(&lock->destroy.link);
212 wl_list_remove(&lock->unlock.link);
213 wl_list_remove(&lock->new_surface.link);
214 }
215
216 free(lock);
217}
218
219static void handle_unlock(struct wl_listener *listener, void *data) {
220 struct sway_session_lock *lock = wl_container_of(listener, lock, unlock);
221 sway_log(SWAY_DEBUG, "session unlocked");
222
223 sway_session_lock_destroy(lock);
224
225 struct sway_seat *seat;
226 wl_list_for_each(seat, &server.input->seats, link) {
227 // copied from seat_set_focus_layer -- deduplicate?
228 struct sway_node *previous = seat_get_focus_inactive(seat, &root->node);
229 if (previous) {
230 // Hack to get seat to re-focus the return value of get_focus
231 seat_set_focus(seat, NULL);
232 seat_set_focus(seat, previous);
233 }
234 }
235
236 // Triggers a refocus of the topmost surface layer if necessary
237 // TODO: Make layer surface focus per-output based on cursor position
238 for (int i = 0; i < root->outputs->length; ++i) {
239 struct sway_output *output = root->outputs->items[i];
240 arrange_layers(output);
241 }
242}
243
244static void handle_abandon(struct wl_listener *listener, void *data) {
245 struct sway_session_lock *lock = wl_container_of(listener, lock, destroy);
246 sway_log(SWAY_INFO, "session lock abandoned");
247
248 struct sway_session_lock_output *lock_output;
249 wl_list_for_each(lock_output, &lock->outputs, link) {
250 wlr_scene_rect_set_color(lock_output->background,
251 (float[4]){ 1.f, 0.f, 0.f, 1.f });
252 }
253
254 lock->abandoned = true;
255 wl_list_remove(&lock->destroy.link);
256 wl_list_remove(&lock->unlock.link);
257 wl_list_remove(&lock->new_surface.link);
258}
259
260static void handle_session_lock(struct wl_listener *listener, void *data) {
261 struct wlr_session_lock_v1 *lock = data;
262 struct wl_client *client = wl_resource_get_client(lock->resource);
263
264 if (server.session_lock.lock) {
265 if (server.session_lock.lock->abandoned) {
266 sway_log(SWAY_INFO, "Replacing abandoned lock");
267 sway_session_lock_destroy(server.session_lock.lock);
268 } else {
269 sway_log(SWAY_ERROR, "Cannot lock an already locked session");
270 wlr_session_lock_v1_destroy(lock);
271 return;
272 }
273 }
274
275 struct sway_session_lock *sway_lock = calloc(1, sizeof(*sway_lock));
276 if (!sway_lock) {
277 sway_log(SWAY_ERROR, "failed to allocate a session lock object");
278 wlr_session_lock_v1_destroy(lock);
279 return;
280 }
281
282 wl_list_init(&sway_lock->outputs);
283
284 sway_log(SWAY_DEBUG, "session locked");
285
286 struct sway_seat *seat;
287 wl_list_for_each(seat, &server.input->seats, link) {
288 seat_unfocus_unless_client(seat, client);
289 }
290
291 struct sway_output *output;
292 wl_list_for_each(output, &root->all_outputs, link) {
293 sway_session_lock_add_output(sway_lock, output);
294 }
295
296 sway_lock->new_surface.notify = handle_new_surface;
297 wl_signal_add(&lock->events.new_surface, &sway_lock->new_surface);
298 sway_lock->unlock.notify = handle_unlock;
299 wl_signal_add(&lock->events.unlock, &sway_lock->unlock);
300 sway_lock->destroy.notify = handle_abandon;
301 wl_signal_add(&lock->events.destroy, &sway_lock->destroy);
302
303 wlr_session_lock_v1_send_locked(lock);
304 server.session_lock.lock = sway_lock;
305}
306
307static void handle_session_lock_destroy(struct wl_listener *listener, void *data) {
308 // if the server shuts down while a lock is active, destroy the lock
309 if (server.session_lock.lock) {
310 sway_session_lock_destroy(server.session_lock.lock);
311 }
312
313 wl_list_remove(&server.session_lock.new_lock.link);
314 wl_list_remove(&server.session_lock.manager_destroy.link);
315
316 server.session_lock.manager = NULL;
317}
318
319void sway_session_lock_add_output(struct sway_session_lock *lock,
320 struct sway_output *output) {
321 struct sway_session_lock_output *lock_output =
322 session_lock_output_create(lock, output);
323
324 // if we run out of memory while trying to lock the screen, the best we
325 // can do is kill the sway process. Security conscious users will have
326 // the sway session fall back to a login shell.
327 if (!lock_output) {
328 sway_log(SWAY_ERROR, "aborting: failed to allocate a lock output");
329 abort();
330 }
331}
332
333bool sway_session_lock_has_surface(struct sway_session_lock *lock,
334 struct wlr_surface *surface) {
335 struct sway_session_lock_output *lock_output;
336 wl_list_for_each(lock_output, &lock->outputs, link) {
337 if (lock_output->surface && lock_output->surface->surface == surface) {
338 return true;
339 }
340 }
341
342 return false;
343}
344
345void sway_session_lock_init(void) {
346 server.session_lock.manager = wlr_session_lock_manager_v1_create(server.wl_display);
347
348 server.session_lock.new_lock.notify = handle_session_lock;
349 server.session_lock.manager_destroy.notify = handle_session_lock_destroy;
350 wl_signal_add(&server.session_lock.manager->events.new_lock,
351 &server.session_lock.new_lock);
352 wl_signal_add(&server.session_lock.manager->events.destroy,
353 &server.session_lock.manager_destroy);
354}
diff --git a/sway/main.c b/sway/main.c
index 0611e80b..1c4939aa 100644
--- a/sway/main.c
+++ b/sway/main.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <getopt.h> 1#include <getopt.h>
3#include <pango/pangocairo.h> 2#include <pango/pangocairo.h>
4#include <signal.h> 3#include <signal.h>
@@ -6,6 +5,7 @@
6#include <stdio.h> 5#include <stdio.h>
7#include <stdlib.h> 6#include <stdlib.h>
8#include <string.h> 7#include <string.h>
8#include <sys/resource.h>
9#include <sys/stat.h> 9#include <sys/stat.h>
10#include <sys/types.h> 10#include <sys/types.h>
11#include <sys/wait.h> 11#include <sys/wait.h>
@@ -27,6 +27,7 @@
27 27
28static bool terminate_request = false; 28static bool terminate_request = false;
29static int exit_value = 0; 29static int exit_value = 0;
30static struct rlimit original_nofile_rlimit = {0};
30struct sway_server server = {0}; 31struct sway_server server = {0};
31struct sway_debug debug = {0}; 32struct sway_debug debug = {0};
32 33
@@ -47,44 +48,6 @@ void sig_handler(int signal) {
47 sway_terminate(EXIT_SUCCESS); 48 sway_terminate(EXIT_SUCCESS);
48} 49}
49 50
50void detect_proprietary(int allow_unsupported_gpu) {
51 FILE *f = fopen("/proc/modules", "r");
52 if (!f) {
53 return;
54 }
55 char *line = NULL;
56 size_t line_size = 0;
57 while (getline(&line, &line_size, f) != -1) {
58 if (strncmp(line, "nvidia ", 7) == 0) {
59 if (allow_unsupported_gpu) {
60 sway_log(SWAY_ERROR,
61 "!!! Proprietary Nvidia drivers are in use !!!");
62 } else {
63 sway_log(SWAY_ERROR,
64 "Proprietary Nvidia drivers are NOT supported. "
65 "Use Nouveau. To launch sway anyway, launch with "
66 "--my-next-gpu-wont-be-nvidia and DO NOT report issues.");
67 exit(EXIT_FAILURE);
68 }
69 break;
70 }
71 if (strstr(line, "fglrx")) {
72 if (allow_unsupported_gpu) {
73 sway_log(SWAY_ERROR,
74 "!!! Proprietary AMD drivers are in use !!!");
75 } else {
76 sway_log(SWAY_ERROR, "Proprietary AMD drivers do NOT support "
77 "Wayland. Use radeon. To try anyway, launch sway with "
78 "--unsupported-gpu and DO NOT report issues.");
79 exit(EXIT_FAILURE);
80 }
81 break;
82 }
83 }
84 free(line);
85 fclose(f);
86}
87
88void run_as_ipc_client(char *command, char *socket_path) { 51void run_as_ipc_client(char *command, char *socket_path) {
89 int socketfd = ipc_open_socket(socket_path); 52 int socketfd = ipc_open_socket(socket_path);
90 uint32_t len = strlen(command); 53 uint32_t len = strlen(command);
@@ -148,33 +111,49 @@ static void log_kernel(void) {
148 pclose(f); 111 pclose(f);
149} 112}
150 113
151 114static bool detect_suid(void) {
152static bool drop_permissions(void) { 115 if (geteuid() != 0 && getegid() != 0) {
153 if (getuid() != geteuid() || getgid() != getegid()) { 116 return false;
154 // Set the gid and uid in the correct order.
155 if (setgid(getgid()) != 0) {
156 sway_log(SWAY_ERROR, "Unable to drop root group, refusing to start");
157 return false;
158 }
159 if (setuid(getuid()) != 0) {
160 sway_log(SWAY_ERROR, "Unable to drop root user, refusing to start");
161 return false;
162 }
163 } 117 }
164 if (setgid(0) != -1 || setuid(0) != -1) { 118
165 sway_log(SWAY_ERROR, "Unable to drop root (we shouldn't be able to " 119 if (getuid() == geteuid() && getgid() == getegid()) {
166 "restore it after setuid), refusing to start");
167 return false; 120 return false;
168 } 121 }
122
123 sway_log(SWAY_ERROR, "SUID operation is no longer supported, refusing to start. "
124 "This check will be removed in a future release.");
169 return true; 125 return true;
170} 126}
171 127
128static void increase_nofile_limit(void) {
129 if (getrlimit(RLIMIT_NOFILE, &original_nofile_rlimit) != 0) {
130 sway_log_errno(SWAY_ERROR, "Failed to bump max open files limit: "
131 "getrlimit(NOFILE) failed");
132 return;
133 }
134
135 struct rlimit new_rlimit = original_nofile_rlimit;
136 new_rlimit.rlim_cur = new_rlimit.rlim_max;
137 if (setrlimit(RLIMIT_NOFILE, &new_rlimit) != 0) {
138 sway_log_errno(SWAY_ERROR, "Failed to bump max open files limit: "
139 "setrlimit(NOFILE) failed");
140 sway_log(SWAY_INFO, "Running with %d max open files",
141 (int)original_nofile_rlimit.rlim_cur);
142 }
143}
144
145void restore_nofile_limit(void) {
146 if (original_nofile_rlimit.rlim_cur == 0) {
147 return;
148 }
149 if (setrlimit(RLIMIT_NOFILE, &original_nofile_rlimit) != 0) {
150 sway_log_errno(SWAY_ERROR, "Failed to restore max open files limit: "
151 "setrlimit(NOFILE) failed");
152 }
153}
154
172void enable_debug_flag(const char *flag) { 155void enable_debug_flag(const char *flag) {
173 if (strcmp(flag, "damage=highlight") == 0) { 156 if (strcmp(flag, "noatomic") == 0) {
174 debug.damage = DAMAGE_HIGHLIGHT;
175 } else if (strcmp(flag, "damage=rerender") == 0) {
176 debug.damage = DAMAGE_RERENDER;
177 } else if (strcmp(flag, "noatomic") == 0) {
178 debug.noatomic = true; 157 debug.noatomic = true;
179 } else if (strcmp(flag, "txn-wait") == 0) { 158 } else if (strcmp(flag, "txn-wait") == 0) {
180 debug.txn_wait = true; 159 debug.txn_wait = true;
@@ -182,6 +161,8 @@ void enable_debug_flag(const char *flag) {
182 debug.txn_timings = true; 161 debug.txn_timings = true;
183 } else if (strncmp(flag, "txn-timeout=", 12) == 0) { 162 } else if (strncmp(flag, "txn-timeout=", 12) == 0) {
184 server.txn_timeout_ms = atoi(&flag[12]); 163 server.txn_timeout_ms = atoi(&flag[12]);
164 } else if (strcmp(flag, "legacy-wl-drm") == 0) {
165 debug.legacy_wl_drm = true;
185 } else { 166 } else {
186 sway_log(SWAY_ERROR, "Unknown debug flag: %s", flag); 167 sway_log(SWAY_ERROR, "Unknown debug flag: %s", flag);
187 } 168 }
@@ -206,36 +187,35 @@ static void handle_wlr_log(enum wlr_log_importance importance,
206 _sway_vlog(convert_wlr_log_importance(importance), sway_fmt, args); 187 _sway_vlog(convert_wlr_log_importance(importance), sway_fmt, args);
207} 188}
208 189
190static const struct option long_options[] = {
191 {"help", no_argument, NULL, 'h'},
192 {"config", required_argument, NULL, 'c'},
193 {"validate", no_argument, NULL, 'C'},
194 {"debug", no_argument, NULL, 'd'},
195 {"version", no_argument, NULL, 'v'},
196 {"verbose", no_argument, NULL, 'V'},
197 {"get-socketpath", no_argument, NULL, 'p'},
198 {"unsupported-gpu", no_argument, NULL, 'u'},
199 {0, 0, 0, 0}
200};
201
202static const char usage[] =
203 "Usage: sway [options] [command]\n"
204 "\n"
205 " -h, --help Show help message and quit.\n"
206 " -c, --config <config> Specify a config file.\n"
207 " -C, --validate Check the validity of the config file, then exit.\n"
208 " -d, --debug Enables full logging, including debug information.\n"
209 " -v, --version Show the version number and quit.\n"
210 " -V, --verbose Enables more verbose logging.\n"
211 " --get-socketpath Gets the IPC socket path and prints it, then exits.\n"
212 "\n";
213
209int main(int argc, char **argv) { 214int main(int argc, char **argv) {
210 static int verbose = 0, debug = 0, validate = 0, allow_unsupported_gpu = 0; 215 static bool verbose = false, debug = false, validate = false;
211
212 static const struct option long_options[] = {
213 {"help", no_argument, NULL, 'h'},
214 {"config", required_argument, NULL, 'c'},
215 {"validate", no_argument, NULL, 'C'},
216 {"debug", no_argument, NULL, 'd'},
217 {"version", no_argument, NULL, 'v'},
218 {"verbose", no_argument, NULL, 'V'},
219 {"get-socketpath", no_argument, NULL, 'p'},
220 {"unsupported-gpu", no_argument, NULL, 'u'},
221 {"my-next-gpu-wont-be-nvidia", no_argument, NULL, 'u'},
222 {0, 0, 0, 0}
223 };
224 216
225 char *config_path = NULL; 217 char *config_path = NULL;
226 218
227 const char* usage =
228 "Usage: sway [options] [command]\n"
229 "\n"
230 " -h, --help Show help message and quit.\n"
231 " -c, --config <config> Specify a config file.\n"
232 " -C, --validate Check the validity of the config file, then exit.\n"
233 " -d, --debug Enables full logging, including debug information.\n"
234 " -v, --version Show the version number and quit.\n"
235 " -V, --verbose Enables more verbose logging.\n"
236 " --get-socketpath Gets the IPC socket path and prints it, then exits.\n"
237 "\n";
238
239 int c; 219 int c;
240 while (1) { 220 while (1) {
241 int option_index = 0; 221 int option_index = 0;
@@ -253,25 +233,25 @@ int main(int argc, char **argv) {
253 config_path = strdup(optarg); 233 config_path = strdup(optarg);
254 break; 234 break;
255 case 'C': // validate 235 case 'C': // validate
256 validate = 1; 236 validate = true;
257 break; 237 break;
258 case 'd': // debug 238 case 'd': // debug
259 debug = 1; 239 debug = true;
260 break; 240 break;
261 case 'D': // extended debug options 241 case 'D': // extended debug options
262 enable_debug_flag(optarg); 242 enable_debug_flag(optarg);
263 break; 243 break;
264 case 'u': 244 case 'u':
265 allow_unsupported_gpu = 1; 245 allow_unsupported_gpu = true;
266 break; 246 break;
267 case 'v': // version 247 case 'v': // version
268 printf("sway version " SWAY_VERSION "\n"); 248 printf("sway version " SWAY_VERSION "\n");
269 exit(EXIT_SUCCESS); 249 exit(EXIT_SUCCESS);
270 break; 250 break;
271 case 'V': // verbose 251 case 'V': // verbose
272 verbose = 1; 252 verbose = true;
273 break; 253 break;
274 case 'p': ; // --get-socketpath 254 case 'p': // --get-socketpath
275 if (getenv("SWAYSOCK")) { 255 if (getenv("SWAYSOCK")) {
276 printf("%s\n", getenv("SWAYSOCK")); 256 printf("%s\n", getenv("SWAYSOCK"));
277 exit(EXIT_SUCCESS); 257 exit(EXIT_SUCCESS);
@@ -286,6 +266,11 @@ int main(int argc, char **argv) {
286 } 266 }
287 } 267 }
288 268
269 // SUID operation is deprecated, so block it for now.
270 if (detect_suid()) {
271 exit(EXIT_FAILURE);
272 }
273
289 // Since wayland requires XDG_RUNTIME_DIR to be set, abort with just the 274 // Since wayland requires XDG_RUNTIME_DIR to be set, abort with just the
290 // clear error message (when not running as an IPC client). 275 // clear error message (when not running as an IPC client).
291 if (!getenv("XDG_RUNTIME_DIR") && optind == argc) { 276 if (!getenv("XDG_RUNTIME_DIR") && optind == argc) {
@@ -312,7 +297,6 @@ int main(int argc, char **argv) {
312 log_kernel(); 297 log_kernel();
313 log_distro(); 298 log_distro();
314 log_env(); 299 log_env();
315 detect_proprietary(allow_unsupported_gpu);
316 300
317 if (optind < argc) { // Behave as IPC client 301 if (optind < argc) { // Behave as IPC client
318 if (optind != 1) { 302 if (optind != 1) {
@@ -325,9 +309,6 @@ int main(int argc, char **argv) {
325 "`sway -d 2>sway.log`."); 309 "`sway -d 2>sway.log`.");
326 exit(EXIT_FAILURE); 310 exit(EXIT_FAILURE);
327 } 311 }
328 if (!drop_permissions()) {
329 exit(EXIT_FAILURE);
330 }
331 char *socket_path = getenv("SWAYSOCK"); 312 char *socket_path = getenv("SWAYSOCK");
332 if (!socket_path) { 313 if (!socket_path) {
333 sway_log(SWAY_ERROR, "Unable to retrieve socket path"); 314 sway_log(SWAY_ERROR, "Unable to retrieve socket path");
@@ -339,14 +320,7 @@ int main(int argc, char **argv) {
339 return 0; 320 return 0;
340 } 321 }
341 322
342 if (!server_privileged_prepare(&server)) { 323 increase_nofile_limit();
343 return 1;
344 }
345
346 if (!drop_permissions()) {
347 server_fini(&server);
348 exit(EXIT_FAILURE);
349 }
350 324
351 // handle SIGTERM signals 325 // handle SIGTERM signals
352 signal(SIGTERM, sig_handler); 326 signal(SIGTERM, sig_handler);
@@ -357,12 +331,14 @@ int main(int argc, char **argv) {
357 331
358 sway_log(SWAY_INFO, "Starting sway version " SWAY_VERSION); 332 sway_log(SWAY_INFO, "Starting sway version " SWAY_VERSION);
359 333
360 root = root_create();
361
362 if (!server_init(&server)) { 334 if (!server_init(&server)) {
363 return 1; 335 return 1;
364 } 336 }
365 337
338 if (server.linux_dmabuf_v1) {
339 wlr_scene_set_linux_dmabuf_v1(root->root_scene, server.linux_dmabuf_v1);
340 }
341
366 if (validate) { 342 if (validate) {
367 bool valid = load_main_config(config_path, false, true); 343 bool valid = load_main_config(config_path, false, true);
368 free(config_path); 344 free(config_path);
@@ -377,6 +353,8 @@ int main(int argc, char **argv) {
377 goto shutdown; 353 goto shutdown;
378 } 354 }
379 355
356 set_rr_scheduling();
357
380 if (!server_start(&server)) { 358 if (!server_start(&server)) {
381 sway_terminate(EXIT_FAILURE); 359 sway_terminate(EXIT_FAILURE);
382 goto shutdown; 360 goto shutdown;
diff --git a/sway/meson.build b/sway/meson.build
index 1402db15..47b51d0c 100644
--- a/sway/meson.build
+++ b/sway/meson.build
@@ -5,25 +5,26 @@ sway_sources = files(
5 'decoration.c', 5 'decoration.c',
6 'ipc-json.c', 6 'ipc-json.c',
7 'ipc-server.c', 7 'ipc-server.c',
8 'lock.c',
8 'main.c', 9 'main.c',
10 'realtime.c',
11 'scene_descriptor.c',
9 'server.c', 12 'server.c',
13 'sway_text_node.c',
10 'swaynag.c', 14 'swaynag.c',
11 'xdg_activation_v1.c', 15 'xdg_activation_v1.c',
12 'xdg_decoration.c', 16 'xdg_decoration.c',
13 17
14 'desktop/desktop.c',
15 'desktop/idle_inhibit_v1.c', 18 'desktop/idle_inhibit_v1.c',
16 'desktop/layer_shell.c', 19 'desktop/layer_shell.c',
17 'desktop/output.c', 20 'desktop/output.c',
18 'desktop/render.c',
19 'desktop/surface.c',
20 'desktop/transaction.c', 21 'desktop/transaction.c',
21 'desktop/xdg_shell.c', 22 'desktop/xdg_shell.c',
23 'desktop/launcher.c',
22 24
23 'input/input-manager.c', 25 'input/input-manager.c',
24 'input/cursor.c', 26 'input/cursor.c',
25 'input/keyboard.c', 27 'input/keyboard.c',
26 'input/libinput.c',
27 'input/seat.c', 28 'input/seat.c',
28 'input/seatop_default.c', 29 'input/seatop_default.c',
29 'input/seatop_down.c', 30 'input/seatop_down.c',
@@ -65,6 +66,7 @@ sway_sources = files(
65 'commands/force_focus_wrapping.c', 66 'commands/force_focus_wrapping.c',
66 'commands/fullscreen.c', 67 'commands/fullscreen.c',
67 'commands/gaps.c', 68 'commands/gaps.c',
69 'commands/gesture.c',
68 'commands/hide_edge_borders.c', 70 'commands/hide_edge_borders.c',
69 'commands/inhibit_idle.c', 71 'commands/inhibit_idle.c',
70 'commands/kill.c', 72 'commands/kill.c',
@@ -83,6 +85,7 @@ sway_sources = files(
83 'commands/nop.c', 85 'commands/nop.c',
84 'commands/output.c', 86 'commands/output.c',
85 'commands/popup_during_fullscreen.c', 87 'commands/popup_during_fullscreen.c',
88 'commands/primary_selection.c',
86 'commands/reload.c', 89 'commands/reload.c',
87 'commands/rename.c', 90 'commands/rename.c',
88 'commands/resize.c', 91 'commands/resize.c',
@@ -154,6 +157,7 @@ sway_sources = files(
154 'commands/input/drag.c', 157 'commands/input/drag.c',
155 'commands/input/drag_lock.c', 158 'commands/input/drag_lock.c',
156 'commands/input/dwt.c', 159 'commands/input/dwt.c',
160 'commands/input/dwtp.c',
157 'commands/input/events.c', 161 'commands/input/events.c',
158 'commands/input/left_handed.c', 162 'commands/input/left_handed.c',
159 'commands/input/map_from_region.c', 163 'commands/input/map_from_region.c',
@@ -162,9 +166,11 @@ sway_sources = files(
162 'commands/input/middle_emulation.c', 166 'commands/input/middle_emulation.c',
163 'commands/input/natural_scroll.c', 167 'commands/input/natural_scroll.c',
164 'commands/input/pointer_accel.c', 168 'commands/input/pointer_accel.c',
169 'commands/input/rotation_angle.c',
165 'commands/input/repeat_delay.c', 170 'commands/input/repeat_delay.c',
166 'commands/input/repeat_rate.c', 171 'commands/input/repeat_rate.c',
167 'commands/input/scroll_button.c', 172 'commands/input/scroll_button.c',
173 'commands/input/scroll_button_lock.c',
168 'commands/input/scroll_factor.c', 174 'commands/input/scroll_factor.c',
169 'commands/input/scroll_method.c', 175 'commands/input/scroll_method.c',
170 'commands/input/tap.c', 176 'commands/input/tap.c',
@@ -188,11 +194,14 @@ sway_sources = files(
188 'commands/output/max_render_time.c', 194 'commands/output/max_render_time.c',
189 'commands/output/mode.c', 195 'commands/output/mode.c',
190 'commands/output/position.c', 196 'commands/output/position.c',
197 'commands/output/power.c',
198 'commands/output/render_bit_depth.c',
191 'commands/output/scale.c', 199 'commands/output/scale.c',
192 'commands/output/scale_filter.c', 200 'commands/output/scale_filter.c',
193 'commands/output/subpixel.c', 201 'commands/output/subpixel.c',
194 'commands/output/toggle.c', 202 'commands/output/toggle.c',
195 'commands/output/transform.c', 203 'commands/output/transform.c',
204 'commands/output/unplug.c',
196 205
197 'tree/arrange.c', 206 'tree/arrange.c',
198 'tree/container.c', 207 'tree/container.c',
@@ -212,23 +221,27 @@ sway_deps = [
212 libudev, 221 libudev,
213 math, 222 math,
214 pango, 223 pango,
215 pcre, 224 pcre2,
216 glesv2,
217 pixman, 225 pixman,
218 server_protos, 226 threads,
219 wayland_server, 227 wayland_server,
220 wlroots, 228 wlroots,
221 xkbcommon, 229 xkbcommon,
230 xcb,
231 xcb_icccm,
222] 232]
223 233
224if have_xwayland 234if wlroots_features['xwayland']
225 sway_sources += 'desktop/xwayland.c' 235 sway_sources += 'desktop/xwayland.c'
226 sway_deps += xcb 236endif
237
238if wlroots_features['libinput_backend']
239 sway_sources += 'input/libinput.c'
227endif 240endif
228 241
229executable( 242executable(
230 'sway', 243 'sway',
231 sway_sources, 244 sway_sources + wl_protos_src,
232 include_directories: [sway_inc], 245 include_directories: [sway_inc],
233 dependencies: sway_deps, 246 dependencies: sway_deps,
234 link_with: [lib_sway_common], 247 link_with: [lib_sway_common],
diff --git a/sway/realtime.c b/sway/realtime.c
new file mode 100644
index 00000000..11154af0
--- /dev/null
+++ b/sway/realtime.c
@@ -0,0 +1,40 @@
1#include <sys/resource.h>
2#include <sched.h>
3#include <unistd.h>
4#include <pthread.h>
5#include "sway/server.h"
6#include "log.h"
7
8static void child_fork_callback(void) {
9 struct sched_param param;
10
11 param.sched_priority = 0;
12
13 int ret = pthread_setschedparam(pthread_self(), SCHED_OTHER, &param);
14 if (ret != 0) {
15 sway_log(SWAY_ERROR, "Failed to reset scheduler policy on fork");
16 }
17}
18
19void set_rr_scheduling(void) {
20 int prio = sched_get_priority_min(SCHED_RR);
21 int old_policy;
22 int ret;
23 struct sched_param param;
24
25 ret = pthread_getschedparam(pthread_self(), &old_policy, &param);
26 if (ret != 0) {
27 sway_log(SWAY_DEBUG, "Failed to get old scheduling priority");
28 return;
29 }
30
31 param.sched_priority = prio;
32
33 ret = pthread_setschedparam(pthread_self(), SCHED_RR, &param);
34 if (ret != 0) {
35 sway_log(SWAY_INFO, "Failed to set scheduling priority to %d", prio);
36 return;
37 }
38
39 pthread_atfork(NULL, NULL, child_fork_callback);
40}
diff --git a/sway/scene_descriptor.c b/sway/scene_descriptor.c
new file mode 100644
index 00000000..a30d4664
--- /dev/null
+++ b/sway/scene_descriptor.c
@@ -0,0 +1,66 @@
1#include <stdlib.h>
2#include <wlr/util/addon.h>
3#include "log.h"
4#include "sway/scene_descriptor.h"
5
6struct scene_descriptor {
7 void *data;
8 struct wlr_addon addon;
9};
10
11static const struct wlr_addon_interface addon_interface;
12
13static struct scene_descriptor *scene_node_get_descriptor(
14 struct wlr_scene_node *node, enum sway_scene_descriptor_type type) {
15 struct wlr_addon *addon = wlr_addon_find(&node->addons, (void *)type, &addon_interface);
16 if (!addon) {
17 return NULL;
18 }
19
20 struct scene_descriptor *desc = wl_container_of(addon, desc, addon);
21 return desc;
22}
23
24static void descriptor_destroy(struct scene_descriptor *desc) {
25 wlr_addon_finish(&desc->addon);
26 free(desc);
27}
28
29void *scene_descriptor_try_get(struct wlr_scene_node *node,
30 enum sway_scene_descriptor_type type) {
31 struct scene_descriptor *desc = scene_node_get_descriptor(node, type);
32 if (!desc) {
33 return NULL;
34 }
35
36 return desc->data;
37}
38
39void scene_descriptor_destroy(struct wlr_scene_node *node,
40 enum sway_scene_descriptor_type type) {
41 struct scene_descriptor *desc = scene_node_get_descriptor(node, type);
42 descriptor_destroy(desc);
43}
44
45static void addon_handle_destroy(struct wlr_addon *addon) {
46 struct scene_descriptor *desc = wl_container_of(addon, desc, addon);
47 descriptor_destroy(desc);
48}
49
50static const struct wlr_addon_interface addon_interface = {
51 .name = "sway_scene_descriptor",
52 .destroy = addon_handle_destroy,
53};
54
55bool scene_descriptor_assign(struct wlr_scene_node *node,
56 enum sway_scene_descriptor_type type, void *data) {
57 struct scene_descriptor *desc = calloc(1, sizeof(*desc));
58 if (!desc) {
59 sway_log(SWAY_ERROR, "Could not allocate a scene descriptor");
60 return false;
61 }
62
63 wlr_addon_init(&desc->addon, &node->addons, (void *)type, &addon_interface);
64 desc->data = data;
65 return true;
66}
diff --git a/sway/server.c b/sway/server.c
index 2e5ab104..4b48e8e5 100644
--- a/sway/server.c
+++ b/sway/server.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <assert.h> 1#include <assert.h>
3#include <stdbool.h> 2#include <stdbool.h>
4#include <stdlib.h> 3#include <stdlib.h>
@@ -7,21 +6,35 @@
7#include <wlr/backend.h> 6#include <wlr/backend.h>
8#include <wlr/backend/headless.h> 7#include <wlr/backend/headless.h>
9#include <wlr/backend/multi.h> 8#include <wlr/backend/multi.h>
10#include <wlr/backend/noop.h>
11#include <wlr/backend/session.h>
12#include <wlr/config.h> 9#include <wlr/config.h>
10#include <wlr/render/allocator.h>
13#include <wlr/render/wlr_renderer.h> 11#include <wlr/render/wlr_renderer.h>
14#include <wlr/types/wlr_compositor.h> 12#include <wlr/types/wlr_compositor.h>
13#include <wlr/types/wlr_content_type_v1.h>
14#include <wlr/types/wlr_cursor_shape_v1.h>
15#include <wlr/types/wlr_data_control_v1.h> 15#include <wlr/types/wlr_data_control_v1.h>
16#include <wlr/types/wlr_data_device.h>
17#include <wlr/types/wlr_drm.h>
16#include <wlr/types/wlr_export_dmabuf_v1.h> 18#include <wlr/types/wlr_export_dmabuf_v1.h>
19#include <wlr/types/wlr_ext_foreign_toplevel_list_v1.h>
20#include <wlr/types/wlr_foreign_toplevel_management_v1.h>
21#include <wlr/types/wlr_fractional_scale_v1.h>
17#include <wlr/types/wlr_gamma_control_v1.h> 22#include <wlr/types/wlr_gamma_control_v1.h>
18#include <wlr/types/wlr_idle.h> 23#include <wlr/types/wlr_idle_notify_v1.h>
19#include <wlr/types/wlr_layer_shell_v1.h> 24#include <wlr/types/wlr_layer_shell_v1.h>
25#include <wlr/types/wlr_linux_dmabuf_v1.h>
26#include <wlr/types/wlr_output_management_v1.h>
27#include <wlr/types/wlr_output_power_management_v1.h>
20#include <wlr/types/wlr_pointer_constraints_v1.h> 28#include <wlr/types/wlr_pointer_constraints_v1.h>
29#include <wlr/types/wlr_presentation_time.h>
21#include <wlr/types/wlr_primary_selection_v1.h> 30#include <wlr/types/wlr_primary_selection_v1.h>
22#include <wlr/types/wlr_relative_pointer_v1.h> 31#include <wlr/types/wlr_relative_pointer_v1.h>
23#include <wlr/types/wlr_screencopy_v1.h> 32#include <wlr/types/wlr_screencopy_v1.h>
33#include <wlr/types/wlr_security_context_v1.h>
24#include <wlr/types/wlr_server_decoration.h> 34#include <wlr/types/wlr_server_decoration.h>
35#include <wlr/types/wlr_session_lock_v1.h>
36#include <wlr/types/wlr_single_pixel_buffer_v1.h>
37#include <wlr/types/wlr_subcompositor.h>
25#include <wlr/types/wlr_tablet_v2.h> 38#include <wlr/types/wlr_tablet_v2.h>
26#include <wlr/types/wlr_viewporter.h> 39#include <wlr/types/wlr_viewporter.h>
27#include <wlr/types/wlr_xcursor_manager.h> 40#include <wlr/types/wlr_xcursor_manager.h>
@@ -31,6 +44,7 @@
31#include <wlr/types/wlr_xdg_foreign_v1.h> 44#include <wlr/types/wlr_xdg_foreign_v1.h>
32#include <wlr/types/wlr_xdg_foreign_v2.h> 45#include <wlr/types/wlr_xdg_foreign_v2.h>
33#include <wlr/types/wlr_xdg_output_v1.h> 46#include <wlr/types/wlr_xdg_output_v1.h>
47#include <xf86drm.h>
34#include "config.h" 48#include "config.h"
35#include "list.h" 49#include "list.h"
36#include "log.h" 50#include "log.h"
@@ -39,41 +53,221 @@
39#include "sway/input/input-manager.h" 53#include "sway/input/input-manager.h"
40#include "sway/output.h" 54#include "sway/output.h"
41#include "sway/server.h" 55#include "sway/server.h"
56#include "sway/input/cursor.h"
42#include "sway/tree/root.h" 57#include "sway/tree/root.h"
43#if HAVE_XWAYLAND 58
59#if WLR_HAS_XWAYLAND
60#include <wlr/xwayland/shell.h>
44#include "sway/xwayland.h" 61#include "sway/xwayland.h"
45#endif 62#endif
46 63
47bool server_privileged_prepare(struct sway_server *server) { 64#if WLR_HAS_DRM_BACKEND
48 sway_log(SWAY_DEBUG, "Preparing Wayland server initialization"); 65#include <wlr/types/wlr_drm_lease_v1.h>
66#endif
67
68#define SWAY_XDG_SHELL_VERSION 5
69#define SWAY_LAYER_SHELL_VERSION 4
70#define SWAY_FOREIGN_TOPLEVEL_LIST_VERSION 1
71
72bool allow_unsupported_gpu = false;
73
74#if WLR_HAS_DRM_BACKEND
75static void handle_drm_lease_request(struct wl_listener *listener, void *data) {
76 /* We only offer non-desktop outputs, but in the future we might want to do
77 * more logic here. */
78
79 struct wlr_drm_lease_request_v1 *req = data;
80 struct wlr_drm_lease_v1 *lease = wlr_drm_lease_request_v1_grant(req);
81 if (!lease) {
82 sway_log(SWAY_ERROR, "Failed to grant lease request");
83 wlr_drm_lease_request_v1_reject(req);
84 }
85}
86#endif
87
88static bool is_privileged(const struct wl_global *global) {
89#if WLR_HAS_DRM_BACKEND
90 if (server.drm_lease_manager != NULL) {
91 struct wlr_drm_lease_device_v1 *drm_lease_dev;
92 wl_list_for_each(drm_lease_dev, &server.drm_lease_manager->devices, link) {
93 if (drm_lease_dev->global == global) {
94 return true;
95 }
96 }
97 }
98#endif
99
100 return
101 global == server.output_manager_v1->global ||
102 global == server.output_power_manager_v1->global ||
103 global == server.input_method->global ||
104 global == server.foreign_toplevel_list->global ||
105 global == server.foreign_toplevel_manager->global ||
106 global == server.data_control_manager_v1->global ||
107 global == server.screencopy_manager_v1->global ||
108 global == server.export_dmabuf_manager_v1->global ||
109 global == server.security_context_manager_v1->global ||
110 global == server.gamma_control_manager_v1->global ||
111 global == server.layer_shell->global ||
112 global == server.session_lock.manager->global ||
113 global == server.input->keyboard_shortcuts_inhibit->global ||
114 global == server.input->virtual_keyboard->global ||
115 global == server.input->virtual_pointer->global ||
116 global == server.input->transient_seat_manager->global;
117}
118
119static bool filter_global(const struct wl_client *client,
120 const struct wl_global *global, void *data) {
121#if WLR_HAS_XWAYLAND
122 struct wlr_xwayland *xwayland = server.xwayland.wlr_xwayland;
123 if (xwayland && global == xwayland->shell_v1->global) {
124 return xwayland->server != NULL && client == xwayland->server->client;
125 }
126#endif
127
128 // Restrict usage of privileged protocols to unsandboxed clients
129 // TODO: add a way for users to configure an allow-list
130 const struct wlr_security_context_v1_state *security_context =
131 wlr_security_context_manager_v1_lookup_client(
132 server.security_context_manager_v1, (struct wl_client *)client);
133 if (is_privileged(global)) {
134 return security_context == NULL;
135 }
136
137 return true;
138}
139
140static void detect_proprietary(struct wlr_backend *backend, void *data) {
141 int drm_fd = wlr_backend_get_drm_fd(backend);
142 if (drm_fd < 0) {
143 return;
144 }
145
146 drmVersion *version = drmGetVersion(drm_fd);
147 if (version == NULL) {
148 sway_log(SWAY_ERROR, "drmGetVersion() failed");
149 return;
150 }
151
152 bool is_unsupported = false;
153 if (strcmp(version->name, "nvidia-drm") == 0) {
154 is_unsupported = true;
155 sway_log(SWAY_ERROR, "!!! Proprietary Nvidia drivers are in use !!!");
156 if (!allow_unsupported_gpu) {
157 sway_log(SWAY_ERROR, "Use Nouveau instead");
158 }
159 }
160
161 if (strcmp(version->name, "evdi") == 0) {
162 is_unsupported = true;
163 sway_log(SWAY_ERROR, "!!! Proprietary DisplayLink drivers are in use !!!");
164 }
165
166 if (!allow_unsupported_gpu && is_unsupported) {
167 sway_log(SWAY_ERROR,
168 "Proprietary drivers are NOT supported. To launch sway anyway, "
169 "launch with --unsupported-gpu and DO NOT report issues.");
170 exit(EXIT_FAILURE);
171 }
172
173 drmFreeVersion(version);
174}
175
176static void handle_renderer_lost(struct wl_listener *listener, void *data) {
177 struct sway_server *server = wl_container_of(listener, server, renderer_lost);
178
179 sway_log(SWAY_INFO, "Re-creating renderer after GPU reset");
180
181 struct wlr_renderer *renderer = wlr_renderer_autocreate(server->backend);
182 if (renderer == NULL) {
183 sway_log(SWAY_ERROR, "Unable to create renderer");
184 return;
185 }
186
187 struct wlr_allocator *allocator =
188 wlr_allocator_autocreate(server->backend, renderer);
189 if (allocator == NULL) {
190 sway_log(SWAY_ERROR, "Unable to create allocator");
191 wlr_renderer_destroy(renderer);
192 return;
193 }
194
195 struct wlr_renderer *old_renderer = server->renderer;
196 struct wlr_allocator *old_allocator = server->allocator;
197 server->renderer = renderer;
198 server->allocator = allocator;
199
200 wl_list_remove(&server->renderer_lost.link);
201 wl_signal_add(&server->renderer->events.lost, &server->renderer_lost);
202
203 wlr_compositor_set_renderer(server->compositor, renderer);
204
205 for (int i = 0; i < root->outputs->length; ++i) {
206 struct sway_output *output = root->outputs->items[i];
207 wlr_output_init_render(output->wlr_output,
208 server->allocator, server->renderer);
209 }
210
211 wlr_allocator_destroy(old_allocator);
212 wlr_renderer_destroy(old_renderer);
213}
214
215bool server_init(struct sway_server *server) {
216 sway_log(SWAY_DEBUG, "Initializing Wayland server");
49 server->wl_display = wl_display_create(); 217 server->wl_display = wl_display_create();
50 server->wl_event_loop = wl_display_get_event_loop(server->wl_display); 218 server->wl_event_loop = wl_display_get_event_loop(server->wl_display);
51 server->backend = wlr_backend_autocreate(server->wl_display);
52 219
220 wl_display_set_global_filter(server->wl_display, filter_global, NULL);
221
222 root = root_create(server->wl_display);
223
224 server->backend = wlr_backend_autocreate(server->wl_event_loop, &server->session);
53 if (!server->backend) { 225 if (!server->backend) {
54 sway_log(SWAY_ERROR, "Unable to create backend"); 226 sway_log(SWAY_ERROR, "Unable to create backend");
55 return false; 227 return false;
56 } 228 }
57 return true;
58}
59 229
60bool server_init(struct sway_server *server) { 230 wlr_multi_for_each_backend(server->backend, detect_proprietary, NULL);
61 sway_log(SWAY_DEBUG, "Initializing Wayland server");
62 231
63 struct wlr_renderer *renderer = wlr_backend_get_renderer(server->backend); 232 server->renderer = wlr_renderer_autocreate(server->backend);
64 assert(renderer); 233 if (!server->renderer) {
234 sway_log(SWAY_ERROR, "Failed to create renderer");
235 return false;
236 }
237
238 server->renderer_lost.notify = handle_renderer_lost;
239 wl_signal_add(&server->renderer->events.lost, &server->renderer_lost);
65 240
66 wlr_renderer_init_wl_display(renderer, server->wl_display); 241 wlr_renderer_init_wl_shm(server->renderer, server->wl_display);
67 242
68 server->compositor = wlr_compositor_create(server->wl_display, renderer); 243 if (wlr_renderer_get_texture_formats(server->renderer, WLR_BUFFER_CAP_DMABUF) != NULL) {
69 server->compositor_new_surface.notify = handle_compositor_new_surface; 244 server->linux_dmabuf_v1 = wlr_linux_dmabuf_v1_create_with_renderer(
70 wl_signal_add(&server->compositor->events.new_surface, 245 server->wl_display, 4, server->renderer);
71 &server->compositor_new_surface); 246 if (debug.legacy_wl_drm) {
247 wlr_drm_create(server->wl_display, server->renderer);
248 }
249 }
250
251 server->allocator = wlr_allocator_autocreate(server->backend,
252 server->renderer);
253 if (!server->allocator) {
254 sway_log(SWAY_ERROR, "Failed to create allocator");
255 return false;
256 }
257
258 server->compositor = wlr_compositor_create(server->wl_display, 6,
259 server->renderer);
260
261 wlr_subcompositor_create(server->wl_display);
72 262
73 server->data_device_manager = 263 server->data_device_manager =
74 wlr_data_device_manager_create(server->wl_display); 264 wlr_data_device_manager_create(server->wl_display);
75 265
76 wlr_gamma_control_manager_v1_create(server->wl_display); 266 server->gamma_control_manager_v1 =
267 wlr_gamma_control_manager_v1_create(server->wl_display);
268 server->gamma_control_set_gamma.notify = handle_gamma_control_set_gamma;
269 wl_signal_add(&server->gamma_control_manager_v1->events.set_gamma,
270 &server->gamma_control_set_gamma);
77 271
78 server->new_output.notify = handle_new_output; 272 server->new_output.notify = handle_new_output;
79 wl_signal_add(&server->backend->events.new_output, &server->new_output); 273 wl_signal_add(&server->backend->events.new_output, &server->new_output);
@@ -83,19 +277,20 @@ bool server_init(struct sway_server *server) {
83 277
84 wlr_xdg_output_manager_v1_create(server->wl_display, root->output_layout); 278 wlr_xdg_output_manager_v1_create(server->wl_display, root->output_layout);
85 279
86 server->idle = wlr_idle_create(server->wl_display); 280 server->idle_notifier_v1 = wlr_idle_notifier_v1_create(server->wl_display);
87 server->idle_inhibit_manager_v1 = 281 sway_idle_inhibit_manager_v1_init();
88 sway_idle_inhibit_manager_v1_create(server->wl_display, server->idle);
89 282
90 server->layer_shell = wlr_layer_shell_v1_create(server->wl_display); 283 server->layer_shell = wlr_layer_shell_v1_create(server->wl_display,
284 SWAY_LAYER_SHELL_VERSION);
91 wl_signal_add(&server->layer_shell->events.new_surface, 285 wl_signal_add(&server->layer_shell->events.new_surface,
92 &server->layer_shell_surface); 286 &server->layer_shell_surface);
93 server->layer_shell_surface.notify = handle_layer_shell_surface; 287 server->layer_shell_surface.notify = handle_layer_shell_surface;
94 288
95 server->xdg_shell = wlr_xdg_shell_create(server->wl_display); 289 server->xdg_shell = wlr_xdg_shell_create(server->wl_display,
96 wl_signal_add(&server->xdg_shell->events.new_surface, 290 SWAY_XDG_SHELL_VERSION);
97 &server->xdg_shell_surface); 291 wl_signal_add(&server->xdg_shell->events.new_toplevel,
98 server->xdg_shell_surface.notify = handle_xdg_shell_surface; 292 &server->xdg_shell_toplevel);
293 server->xdg_shell_toplevel.notify = handle_xdg_shell_toplevel;
99 294
100 server->tablet_v2 = wlr_tablet_v2_create(server->wl_display); 295 server->tablet_v2 = wlr_tablet_v2_create(server->wl_display);
101 296
@@ -126,8 +321,7 @@ bool server_init(struct sway_server *server) {
126 wl_signal_add(&server->pointer_constraints->events.new_constraint, 321 wl_signal_add(&server->pointer_constraints->events.new_constraint,
127 &server->pointer_constraint); 322 &server->pointer_constraint);
128 323
129 server->presentation = 324 wlr_presentation_create(server->wl_display, server->backend);
130 wlr_presentation_create(server->wl_display, server->backend);
131 325
132 server->output_manager_v1 = 326 server->output_manager_v1 =
133 wlr_output_manager_v1_create(server->wl_display); 327 wlr_output_manager_v1_create(server->wl_display);
@@ -146,14 +340,35 @@ bool server_init(struct sway_server *server) {
146 &server->output_power_manager_set_mode); 340 &server->output_power_manager_set_mode);
147 server->input_method = wlr_input_method_manager_v2_create(server->wl_display); 341 server->input_method = wlr_input_method_manager_v2_create(server->wl_display);
148 server->text_input = wlr_text_input_manager_v3_create(server->wl_display); 342 server->text_input = wlr_text_input_manager_v3_create(server->wl_display);
343 server->foreign_toplevel_list =
344 wlr_ext_foreign_toplevel_list_v1_create(server->wl_display, SWAY_FOREIGN_TOPLEVEL_LIST_VERSION);
149 server->foreign_toplevel_manager = 345 server->foreign_toplevel_manager =
150 wlr_foreign_toplevel_manager_v1_create(server->wl_display); 346 wlr_foreign_toplevel_manager_v1_create(server->wl_display);
151 347
152 wlr_export_dmabuf_manager_v1_create(server->wl_display); 348 sway_session_lock_init();
153 wlr_screencopy_manager_v1_create(server->wl_display); 349
154 wlr_data_control_manager_v1_create(server->wl_display); 350#if WLR_HAS_DRM_BACKEND
155 wlr_primary_selection_v1_device_manager_create(server->wl_display); 351 server->drm_lease_manager=
352 wlr_drm_lease_v1_manager_create(server->wl_display, server->backend);
353 if (server->drm_lease_manager) {
354 server->drm_lease_request.notify = handle_drm_lease_request;
355 wl_signal_add(&server->drm_lease_manager->events.request,
356 &server->drm_lease_request);
357 } else {
358 sway_log(SWAY_DEBUG, "Failed to create wlr_drm_lease_device_v1");
359 sway_log(SWAY_INFO, "VR will not be available");
360 }
361#endif
362
363 server->export_dmabuf_manager_v1 = wlr_export_dmabuf_manager_v1_create(server->wl_display);
364 server->screencopy_manager_v1 = wlr_screencopy_manager_v1_create(server->wl_display);
365 server->data_control_manager_v1 = wlr_data_control_manager_v1_create(server->wl_display);
366 server->security_context_manager_v1 = wlr_security_context_manager_v1_create(server->wl_display);
156 wlr_viewporter_create(server->wl_display); 367 wlr_viewporter_create(server->wl_display);
368 wlr_single_pixel_buffer_manager_v1_create(server->wl_display);
369 server->content_type_manager_v1 =
370 wlr_content_type_manager_v1_create(server->wl_display, 1);
371 wlr_fractional_scale_manager_v1_create(server->wl_display, 1);
157 372
158 struct wlr_xdg_foreign_registry *foreign_registry = 373 struct wlr_xdg_foreign_registry *foreign_registry =
159 wlr_xdg_foreign_registry_create(server->wl_display); 374 wlr_xdg_foreign_registry_create(server->wl_display);
@@ -165,11 +380,22 @@ bool server_init(struct sway_server *server) {
165 xdg_activation_v1_handle_request_activate; 380 xdg_activation_v1_handle_request_activate;
166 wl_signal_add(&server->xdg_activation_v1->events.request_activate, 381 wl_signal_add(&server->xdg_activation_v1->events.request_activate,
167 &server->xdg_activation_v1_request_activate); 382 &server->xdg_activation_v1_request_activate);
383 server->xdg_activation_v1_new_token.notify =
384 xdg_activation_v1_handle_new_token;
385 wl_signal_add(&server->xdg_activation_v1->events.new_token,
386 &server->xdg_activation_v1_new_token);
387
388 struct wlr_cursor_shape_manager_v1 *cursor_shape_manager =
389 wlr_cursor_shape_manager_v1_create(server->wl_display, 1);
390 server->request_set_cursor_shape.notify = handle_request_set_cursor_shape;
391 wl_signal_add(&cursor_shape_manager->events.request_set_shape, &server->request_set_cursor_shape);
392
393 wl_list_init(&server->pending_launcher_ctxs);
168 394
169 // Avoid using "wayland-0" as display socket 395 // Avoid using "wayland-0" as display socket
170 char name_candidate[16]; 396 char name_candidate[16];
171 for (int i = 1; i <= 32; ++i) { 397 for (unsigned int i = 1; i <= 32; ++i) {
172 sprintf(name_candidate, "wayland-%d", i); 398 snprintf(name_candidate, sizeof(name_candidate), "wayland-%u", i);
173 if (wl_display_add_socket(server->wl_display, name_candidate) >= 0) { 399 if (wl_display_add_socket(server->wl_display, name_candidate) >= 0) {
174 server->socket = strdup(name_candidate); 400 server->socket = strdup(name_candidate);
175 break; 401 break;
@@ -182,20 +408,20 @@ bool server_init(struct sway_server *server) {
182 return false; 408 return false;
183 } 409 }
184 410
185 server->noop_backend = wlr_noop_backend_create(server->wl_display); 411 server->headless_backend = wlr_headless_backend_create(server->wl_event_loop);
186
187 struct wlr_output *wlr_output = wlr_noop_add_output(server->noop_backend);
188 root->noop_output = output_create(wlr_output);
189
190 server->headless_backend =
191 wlr_headless_backend_create_with_renderer(server->wl_display, renderer);
192 if (!server->headless_backend) { 412 if (!server->headless_backend) {
193 sway_log(SWAY_INFO, "Failed to create secondary headless backend, " 413 sway_log(SWAY_ERROR, "Failed to create secondary headless backend");
194 "starting without it"); 414 wlr_backend_destroy(server->backend);
415 return false;
195 } else { 416 } else {
196 wlr_multi_backend_add(server->backend, server->headless_backend); 417 wlr_multi_backend_add(server->backend, server->headless_backend);
197 } 418 }
198 419
420 struct wlr_output *wlr_output =
421 wlr_headless_add_output(server->headless_backend, 800, 600);
422 wlr_output_set_name(wlr_output, "FALLBACK");
423 root->fallback_output = output_create(wlr_output);
424
199 // This may have been set already via -Dtxn-timeout 425 // This may have been set already via -Dtxn-timeout
200 if (!server->txn_timeout_ms) { 426 if (!server->txn_timeout_ms) {
201 server->txn_timeout_ms = 200; 427 server->txn_timeout_ms = 200;
@@ -211,16 +437,17 @@ bool server_init(struct sway_server *server) {
211 437
212void server_fini(struct sway_server *server) { 438void server_fini(struct sway_server *server) {
213 // TODO: free sway-specific resources 439 // TODO: free sway-specific resources
214#if HAVE_XWAYLAND 440#if WLR_HAS_XWAYLAND
215 wlr_xwayland_destroy(server->xwayland.wlr_xwayland); 441 wlr_xwayland_destroy(server->xwayland.wlr_xwayland);
216#endif 442#endif
217 wl_display_destroy_clients(server->wl_display); 443 wl_display_destroy_clients(server->wl_display);
444 wlr_backend_destroy(server->backend);
218 wl_display_destroy(server->wl_display); 445 wl_display_destroy(server->wl_display);
219 list_free(server->dirty_nodes); 446 list_free(server->dirty_nodes);
220} 447}
221 448
222bool server_start(struct sway_server *server) { 449bool server_start(struct sway_server *server) {
223#if HAVE_XWAYLAND 450#if WLR_HAS_XWAYLAND
224 if (config->xwayland != XWAYLAND_MODE_DISABLED) { 451 if (config->xwayland != XWAYLAND_MODE_DISABLED) {
225 sway_log(SWAY_DEBUG, "Initializing Xwayland (lazy=%d)", 452 sway_log(SWAY_DEBUG, "Initializing Xwayland (lazy=%d)",
226 config->xwayland == XWAYLAND_MODE_LAZY); 453 config->xwayland == XWAYLAND_MODE_LAZY);
@@ -245,6 +472,10 @@ bool server_start(struct sway_server *server) {
245 } 472 }
246#endif 473#endif
247 474
475 if (config->primary_selection) {
476 wlr_primary_selection_v1_device_manager_create(server->wl_display);
477 }
478
248 sway_log(SWAY_INFO, "Starting backend on wayland display '%s'", 479 sway_log(SWAY_INFO, "Starting backend on wayland display '%s'",
249 server->socket); 480 server->socket);
250 if (!wlr_backend_start(server->backend)) { 481 if (!wlr_backend_start(server->backend)) {
@@ -252,6 +483,7 @@ bool server_start(struct sway_server *server) {
252 wlr_backend_destroy(server->backend); 483 wlr_backend_destroy(server->backend);
253 return false; 484 return false;
254 } 485 }
486
255 return true; 487 return true;
256} 488}
257 489
diff --git a/sway/sway-bar.5.scd b/sway/sway-bar.5.scd
index 80d08449..42e59d57 100644
--- a/sway/sway-bar.5.scd
+++ b/sway/sway-bar.5.scd
@@ -40,7 +40,7 @@ runtime.
40*font* <font> 40*font* <font>
41 Specifies the font to be used in the bar. _font_ should be specified as a 41 Specifies the font to be used in the bar. _font_ should be specified as a
42 pango font description. For more information on pango font descriptions, 42 pango font description. For more information on pango font descriptions,
43 see https://developer.gnome.org/pango/stable/pango-Fonts.html#pango-font-description-from-string 43 see https://docs.gtk.org/Pango/type_func.FontDescription.from_string.html#description
44 44
45*gaps* <all> | <horizontal> <vertical> | <top> <right> <bottom> <left> 45*gaps* <all> | <horizontal> <vertical> | <top> <right> <bottom> <left>
46 Sets the gaps from the edge of the screen for the bar. Gaps can either be 46 Sets the gaps from the edge of the screen for the bar. Gaps can either be
diff --git a/sway/sway-input.5.scd b/sway/sway-input.5.scd
index 25f6de18..442311bb 100644
--- a/sway/sway-input.5.scd
+++ b/sway/sway-input.5.scd
@@ -121,11 +121,16 @@ The following commands may only be used in the configuration file.
121 121
122*input* <identifier> map_from_region <X1xY1> <X2xY2> 122*input* <identifier> map_from_region <X1xY1> <X2xY2>
123 Ignores inputs from this device that do not occur within the specified 123 Ignores inputs from this device that do not occur within the specified
124 region. Can be in millimeters (e.g. 10x20mm 20x40mm) or in terms of 0..1 124 region. Can be in millimeters (e.g. 10x20mm 20x40mm) or the fraction of the
125 (e.g. 0.5x0.5 0.7x0.7). Not all devices support millimeters. Only meaningful 125 full available space in terms of 0..1 (e.g. 0.5x0.5 0.7x0.7). Not all
126 if the device is not a keyboard and provides events in absolute terms (such 126 devices support millimeters. Only meaningful if the device is not a
127 as a drawing tablet or touch screen - most pointers provide events relative 127 keyboard and provides events in absolute terms (such as a drawing tablet
128 to the previous frame). 128 or touch screen - most pointers provide events relative to the previous
129 frame).
130
131 Commonly used to maintain the aspect ratio of the input device and screen.
132 Cropping a 16:10 input region to match a 16:9 display can use 0x0 1x0.9 as
133 the argument.
129 134
130## LIBINPUT CONFIGURATION 135## LIBINPUT CONFIGURATION
131 136
@@ -147,6 +152,10 @@ The following commands may only be used in the configuration file.
147*input* <identifier> dwt enabled|disabled 152*input* <identifier> dwt enabled|disabled
148 Enables or disables disable-while-typing for the specified input device. 153 Enables or disables disable-while-typing for the specified input device.
149 154
155*input* <identifier> dwtp enabled|disabled
156 Enables or disables disable-while-trackpointing for the specified input
157 device.
158
150*input* <identifier> events enabled|disabled|disabled_on_external_mouse|toggle [<toggle-modes>] 159*input* <identifier> events enabled|disabled|disabled_on_external_mouse|toggle [<toggle-modes>]
151 Enables or disables send_events for specified input device. Disabling 160 Enables or disables send_events for specified input device. Disabling
152 send_events disables the input device. 161 send_events disables the input device.
@@ -171,12 +180,19 @@ The following commands may only be used in the configuration file.
171*input* <identifier> pointer_accel [<-1|1>] 180*input* <identifier> pointer_accel [<-1|1>]
172 Changes the pointer acceleration for the specified input device. 181 Changes the pointer acceleration for the specified input device.
173 182
183*input* <identifier> rotation_angle <angle>
184 Sets the rotation angle of the device to the given clockwise angle in
185 degrees. The angle must be between 0.0 (inclusive) and 360.0 (exclusive).
186
174*input* <identifier> scroll_button disable|button[1-3,8,9]|<event-code-or-name> 187*input* <identifier> scroll_button disable|button[1-3,8,9]|<event-code-or-name>
175 Sets the button used for scroll_method on_button_down. The button can 188 Sets the button used for scroll_method on_button_down. The button can
176 be given as an event name or code, which can be obtained from *libinput 189 be given as an event name or code, which can be obtained from *libinput
177 debug-events*, or as a x11 mouse button (button[1-3,8,9]). If set to 190 debug-events*, or as a x11 mouse button (button[1-3,8,9]). If set to
178 _disable_, it disables the scroll_method on_button_down. 191 _disable_, it disables the scroll_method on_button_down.
179 192
193*input* <identifier> scroll_button_lock enabled|disabled
194 Enables or disables scroll button lock for specified input device.
195
180*input* <identifier> scroll_factor <floating point value> 196*input* <identifier> scroll_factor <floating point value>
181 Changes the scroll factor for the specified input device. Scroll speed will 197 Changes the scroll factor for the specified input device. Scroll speed will
182 be scaled by the given value, which must be non-negative. 198 be scaled by the given value, which must be non-negative.
@@ -220,6 +236,8 @@ correct seat.
220 absolute coordinates (with respect to the global coordinate space). 236 absolute coordinates (with respect to the global coordinate space).
221 Specifying either value as 0 will not update that coordinate. 237 Specifying either value as 0 will not update that coordinate.
222 238
239 Deprecated: use the virtual-pointer Wayland protocol instead.
240
223*seat* <seat> cursor press|release button[1-9]|<event-name-or-code> 241*seat* <seat> cursor press|release button[1-9]|<event-name-or-code>
224 Simulate pressing (or releasing) the specified mouse button on the 242 Simulate pressing (or releasing) the specified mouse button on the
225 specified seat. The button can either be provided as a button event name or 243 specified seat. The button can either be provided as a button event name or
@@ -228,12 +246,14 @@ correct seat.
228 event will be simulated, however _press_ and _release_ will be ignored and 246 event will be simulated, however _press_ and _release_ will be ignored and
229 both will occur. 247 both will occur.
230 248
249 Deprecated: use the virtual-pointer Wayland protocol instead.
250
231*seat* <name> fallback true|false 251*seat* <name> fallback true|false
232 Set this seat as the fallback seat. A fallback seat will attach any device 252 Set this seat as the fallback seat. A fallback seat will attach any device
233 not explicitly attached to another seat (similar to a "default" seat). 253 not explicitly attached to another seat (similar to a "default" seat).
234 254
235*seat* <name> hide_cursor <timeout>|when-typing [enable|disable] 255*seat* <name> hide_cursor <timeout>|when-typing [enable|disable]
236 Hides the cursor image after the specified event occured. 256 Hides the cursor image after the specified event occurred.
237 257
238 If _timeout_ is specified, then the cursor will be hidden after _timeout_ 258 If _timeout_ is specified, then the cursor will be hidden after _timeout_
239 (in milliseconds) has elapsed with no activity on the cursor. A timeout of 0 259 (in milliseconds) has elapsed with no activity on the cursor. A timeout of 0
@@ -243,18 +263,16 @@ correct seat.
243 If _when-typing_ is enabled, then the cursor will be hidden whenever a key 263 If _when-typing_ is enabled, then the cursor will be hidden whenever a key
244 is pressed. 264 is pressed.
245 265
266 Be aware that this setting can interfere with input handling in games and
267 certain types of software (Gimp, Blender etc) that rely on simultaneous
268 input from mouse and keyboard.
269
246*seat* <name> idle_inhibit <sources...> 270*seat* <name> idle_inhibit <sources...>
247 Sets the set of input event sources which can prevent the seat from 271 Sets the set of input event sources which can prevent the seat from
248 becoming idle, as a space separated list of source names. Valid names are 272 becoming idle, as a space separated list of source names. Valid names are
249 "keyboard", "pointer", "touchpad", "touch", "tablet_pad", "tablet_tool", 273 "keyboard", "pointer", "touchpad", "touch", "tablet_pad", "tablet_tool",
250 and "switch". The default behavior is to prevent idle on any event. 274 and "switch". The default behavior is to prevent idle on any event.
251 275
252*seat* <name> idle_wake <sources...>
253 Sets the set of input event sources which can wake the seat from
254 its idle state, as a space separated list of source names. Valid names are
255 "keyboard", "pointer", "touchpad", "touch", "tablet_pad", "tablet_tool",
256 and "switch". The default behavior is to wake from idle on any event.
257
258*seat* <name> keyboard_grouping none|smart 276*seat* <name> keyboard_grouping none|smart
259 Set how the keyboards in the seat are grouped together. Currently, there 277 Set how the keyboards in the seat are grouped together. Currently, there
260 are two options. _none_ will disable all keyboard grouping. This will make 278 are two options. _none_ will disable all keyboard grouping. This will make
@@ -277,7 +295,7 @@ correct seat.
277 whether future inhibitors are honoured by default, i.e. activated 295 whether future inhibitors are honoured by default, i.e. activated
278 automatically, the default being _enable_. When used at runtime, 296 automatically, the default being _enable_. When used at runtime,
279 _disable_ also disables any currently active inhibitors. _activate_, 297 _disable_ also disables any currently active inhibitors. _activate_,
280 _deactivate_ and _toggle_ are only useable at runtime and change the 298 _deactivate_ and _toggle_ are only usable at runtime and change the
281 state of a potentially existing inhibitor on the currently focused 299 state of a potentially existing inhibitor on the currently focused
282 window. This can be used with the current seat alias (_-_) to affect 300 window. This can be used with the current seat alias (_-_) to affect
283 only the currently focused window of the current seat. Subcommand 301 only the currently focused window of the current seat. Subcommand
diff --git a/sway/sway-ipc.7.scd b/sway/sway-ipc.7.scd
index 373e9dce..2f697248 100644
--- a/sway/sway-ipc.7.scd
+++ b/sway/sway-ipc.7.scd
@@ -213,7 +213,10 @@ following properties:
213: Whether this output is active/enabled 213: Whether this output is active/enabled
214|- dpms 214|- dpms
215: boolean 215: boolean
216: Whether this output is on/off (via DPMS) 216: (Deprecated, use _power_ instead) Whether this output is on/off (via DPMS)
217|- power
218: boolean
219: Whether this output is on/off
217|- primary 220|- primary
218: boolean 221: boolean
219: For i3 compatibility, this will be false. It does not make sense in Wayland 222: For i3 compatibility, this will be false. It does not make sense in Wayland
@@ -294,7 +297,7 @@ following properties:
294Retrieve a JSON representation of the tree 297Retrieve a JSON representation of the tree
295 298
296*REPLY*++ 299*REPLY*++
297An array of object the represent the current tree. Each object represents one 300An array of objects that represent the current tree. Each object represents one
298node and will have the following properties: 301node and will have the following properties:
299 302
300[- *PROPERTY* 303[- *PROPERTY*
@@ -334,8 +337,9 @@ node and will have the following properties:
334 this, but borders are included. 337 this, but borders are included.
335|- window_rect 338|- window_rect
336: object 339: object
337: The geometry of the contents inside the node. The window decorations are 340: The geometry of the content inside the node. These coordinates are relative
338 excluded from this calculation, but borders are included. 341 to the node itself. Window decorations and borders are outside the
342 _window_rect_
339|- deco_rect 343|- deco_rect
340: object 344: object
341: The geometry of the decorations for the node relative to the parent node 345: The geometry of the decorations for the node relative to the parent node
@@ -370,8 +374,14 @@ node and will have the following properties:
370 that can be used as an aid in submitting reproduction steps for bug reports 374 that can be used as an aid in submitting reproduction steps for bug reports
371|- fullscreen_mode 375|- fullscreen_mode
372: integer 376: integer
373: (Only containers and views) The fullscreen mode of the node. 0 means none, 1 means 377: (Only containers and views) The fullscreen mode of the node. 0 means none, 1 means
374 full workspace, and 2 means global fullscreen 378 full workspace, and 2 means global fullscreen
379|- floating
380: string
381: Floating state of container. Can be either "auto_off" or "user_on"
382|- scratchpad_state
383: string
384: Whether the window is in the scratchpad. Can be either "none" or "fresh"
375|- app_id 385|- app_id
376: string 386: string
377: (Only views) For an xdg-shell view, the name of the application, if set. 387: (Only views) For an xdg-shell view, the name of the application, if set.
@@ -1036,7 +1046,7 @@ An object with a single string property containing the contents of the config
1036*Example Reply:* 1046*Example Reply:*
1037``` 1047```
1038{ 1048{
1039 "config": "set $mod Mod4\nbindsym $mod+q exit\n" 1049 "config": "set $mod Mod4\\nbindsym $mod+q exit\\n"
1040} 1050}
1041``` 1051```
1042 1052
@@ -1191,9 +1201,16 @@ following properties will be included for devices that support them:
1191: int 1201: int
1192: The scroll button to use when _scroll_method_ is _on_button_down_. This 1202: The scroll button to use when _scroll_method_ is _on_button_down_. This
1193 will be given as an input event code 1203 will be given as an input event code
1204|- scroll_button_lock
1205: string
1206: Whether scroll button lock is enabled. It can be _enabled_ or _disabled_
1194|- dwt 1207|- dwt
1195: string 1208: string
1196: Whether disable-while-typing is enabled. It can be _enabled_ or _disabled_ 1209: Whether disable-while-typing is enabled. It can be _enabled_ or _disabled_
1210|- dwtp
1211: string
1212: Whether disable-while-trackpointing is enabled. It can be _enabled_ or
1213 _disabled_
1197|- calibration_matrix 1214|- calibration_matrix
1198: array 1215: array
1199: An array of 6 floats representing the calibration matrix for absolute 1216: An array of 6 floats representing the calibration matrix for absolute
@@ -1233,7 +1250,8 @@ following properties will be included for devices that support them:
1233 "click_method": "button_areas", 1250 "click_method": "button_areas",
1234 "middle_emulation": "disabled", 1251 "middle_emulation": "disabled",
1235 "scroll_method": "edge", 1252 "scroll_method": "edge",
1236 "dwt": "enabled" 1253 "dwt": "enabled",
1254 "dwtp": "enabled"
1237 } 1255 }
1238 }, 1256 },
1239 { 1257 {
@@ -1360,7 +1378,8 @@ one seat. Each object has the following properties:
1360 "click_method": "button_areas", 1378 "click_method": "button_areas",
1361 "middle_emulation": "disabled", 1379 "middle_emulation": "disabled",
1362 "scroll_method": "edge", 1380 "scroll_method": "edge",
1363 "dwt": "enabled" 1381 "dwt": "enabled",
1382 "dwtp": "enabled"
1364 } 1383 }
1365 }, 1384 },
1366 { 1385 {
@@ -1436,6 +1455,9 @@ available:
1436: workspace 1455: workspace
1437:[ Sent whenever an event involving a workspace occurs such as initialization 1456:[ Sent whenever an event involving a workspace occurs such as initialization
1438 of a new workspace or a different workspace gains focus 1457 of a new workspace or a different workspace gains focus
1458|- 0x80000001
1459: output
1460: Sent when outputs are updated
1439|- 0x80000002 1461|- 0x80000002
1440: mode 1462: mode
1441: Sent whenever the binding mode changes 1463: Sent whenever the binding mode changes
@@ -1556,6 +1578,20 @@ The following change types are currently available:
1556} 1578}
1557``` 1579```
1558 1580
1581## 0x80000001. OUTPUT
1582
1583Sent whenever an output is added, removed, or its configuration is changed.
1584The event is a single object with the property _change_, which is a string
1585containing the reason for the change. Currently, the only value for _change_ is
1586_unspecified_.
1587
1588*Example Event:*
1589```
1590{
1591 "change": "unspecified"
1592}
1593```
1594
1559## 0x80000002. MODE 1595## 0x80000002. MODE
1560 1596
1561Sent whenever the binding mode changes. The event consists of a single object 1597Sent whenever the binding mode changes. The event consists of a single object
diff --git a/sway/sway-output.5.scd b/sway/sway-output.5.scd
index 2828c841..7d088d5d 100644
--- a/sway/sway-output.5.scd
+++ b/sway/sway-output.5.scd
@@ -24,7 +24,7 @@ must be separated by one space. For example:
24 24
25# COMMANDS 25# COMMANDS
26 26
27*output* <name> mode|resolution|res [--custom] <WIDTHxHEIGHT>[@<RATE>Hz] 27*output* <name> mode|resolution|res [--custom] <width>x<height>[@<rate>Hz]
28 Configures the specified output to use the given mode. Modes are a 28 Configures the specified output to use the given mode. Modes are a
29 combination of width and height (in pixels) and a refresh rate that your 29 combination of width and height (in pixels) and a refresh rate that your
30 display can be configured to use. For a list of available modes for each 30 display can be configured to use. For a list of available modes for each
@@ -40,6 +40,16 @@ must be separated by one space. For example:
40 40
41 output HDMI-A-1 mode 1920x1080@60Hz 41 output HDMI-A-1 mode 1920x1080@60Hz
42 42
43*output* <name> modeline <clock> <hdisplay> <hsync_start> <hsync_end> <htotal> <vdisplay> <vsync_start> <vsync_end> <vtotal> <hsync> <vsync>
44 Configures the specified output to use the given modeline. It can be
45 generated using *cvt*(1) and *gtf*(1) commands. See *xorg.conf*(5).
46 Only supported on DRM backend.
47
48 Example:
49
50 output HDMI-A-1 modeline 173.00 1920 2048 2248 2576 1080 1083 1088 1120 -hsync +vsync
51
52
43*output* <name> position|pos <X> <Y> 53*output* <name> position|pos <X> <Y>
44 Places the specified output at the specific position in the global 54 Places the specified output at the specific position in the global
45 coordinate space. The cursor may only be moved between immediately 55 coordinate space. The cursor may only be moved between immediately
@@ -62,13 +72,11 @@ must be separated by one space. For example:
62 72
63*output* <name> scale <factor> 73*output* <name> scale <factor>
64 Scales the specified output by the specified scale _factor_. An integer is 74 Scales the specified output by the specified scale _factor_. An integer is
65 recommended, but fractional values are also supported. If a fractional 75 recommended, but fractional values are also supported. You may be better
66 value are specified, be warned that it is not possible to faithfully 76 served by setting an integer scale factor and adjusting the font size of
67 represent the contents of your windows - they will be rendered at the next 77 your applications to taste. HiDPI isn't supported with Xwayland clients
68 highest integer scale factor and downscaled. You may be better served by 78 (windows will blur). A fractional scale may be slightly adjusted to match
69 setting an integer scale factor and adjusting the font size of your 79 requirements of the protocol.
70 applications to taste. HiDPI isn't supported with Xwayland clients (windows
71 will blur).
72 80
73*output* <name> scale_filter linear|nearest|smart 81*output* <name> scale_filter linear|nearest|smart
74 Indicates how to scale application buffers that are rendered at a scale 82 Indicates how to scale application buffers that are rendered at a scale
@@ -109,12 +117,20 @@ must be separated by one space. For example:
109 Enables or disables the specified output (all outputs are enabled by 117 Enables or disables the specified output (all outputs are enabled by
110 default). 118 default).
111 119
120 As opposed to the _power_ command, the output will lose its current
121 workspace and windows.
122
112*output* <name> toggle 123*output* <name> toggle
113 Toggle the specified output. 124 Toggle the specified output.
114 125
126*output* <name> power on|off|toggle
127 Turns on or off the specified output.
128
129 As opposed to the _enable_ and _disable_ commands, the output keeps its
130 current workspaces and windows.
131
115*output* <name> dpms on|off|toggle 132*output* <name> dpms on|off|toggle
116 Enables or disables the specified output via DPMS. To turn an output off 133 Deprecated. Alias for _power_.
117 (ie. blank the screen but keep workspaces as-is), one can set DPMS to off.
118 134
119*output* <name> max_render_time off|<msec> 135*output* <name> max_render_time off|<msec>
120 Controls when sway composites the output, as a positive number of 136 Controls when sway composites the output, as a positive number of
@@ -147,6 +163,21 @@ must be separated by one space. For example:
147 adaptive sync can improve latency, but can cause flickering on some 163 adaptive sync can improve latency, but can cause flickering on some
148 hardware. 164 hardware.
149 165
166*output* <name> render_bit_depth 8|10
167 Controls the color channel bit depth at which frames are rendered; the
168 default is currently 8 bits per channel.
169
170 Setting higher values will not have an effect if hardware and software lack
171 support for such bit depths. Successfully increasing the render bit depth
172 will not necessarily increase the bit depth of the frames sent to a display.
173 An increased render bit depth may provide smoother rendering of gradients,
174 and screenshots which can more precisely store the colors of programs
175 which display high bit depth colors.
176
177 Warnings: this can break screenshot/screencast programs which have not been
178 updated to work with different bit depths. This command is experimental,
179 and may be removed or changed in the future.
180
150# SEE ALSO 181# SEE ALSO
151 182
152*sway*(5) *sway-input*(5) 183*sway*(5) *sway-input*(5)
diff --git a/sway/sway.5.scd b/sway/sway.5.scd
index 8958c7e3..9f823947 100644
--- a/sway/sway.5.scd
+++ b/sway/sway.5.scd
@@ -176,6 +176,12 @@ set|plus|minus|toggle <amount>
176*layout* default|splith|splitv|stacking|tabbed 176*layout* default|splith|splitv|stacking|tabbed
177 Sets the layout mode of the focused container. 177 Sets the layout mode of the focused container.
178 178
179 When using the _stacking_ layout, only the focused window in the container is
180 displayed, with the opened windows' list on the top of the container.
181
182 The _tabbed_ layout is similar to _stacking_, but the windows’ list is vertically
183 split.
184
179*layout* toggle [split|all] 185*layout* toggle [split|all]
180 Cycles the layout mode of the focused container though a preset list of 186 Cycles the layout mode of the focused container though a preset list of
181 layouts. If no argument is given, then it cycles through stacking, tabbed 187 layouts. If no argument is given, then it cycles through stacking, tabbed
@@ -210,15 +216,14 @@ set|plus|minus|toggle <amount>
210 further details. 216 further details.
211 217
212*move* left|right|up|down [<px> px] 218*move* left|right|up|down [<px> px]
213 Moves the focused container in the direction specified. If the container, 219 Moves the focused container in the direction specified. The optional _px_
214 the optional _px_ argument specifies how many pixels to move the container. 220 argument specifies how many pixels to move the container. If unspecified,
215 If unspecified, the default is 10 pixels. Pixels are ignored when moving 221 the default is 10 pixels. Pixels are ignored when moving tiled containers.
216 tiled containers.
217 222
218*move* [absolute] position <pos_x> [px|ppt] <pos_y> [px|ptt] 223*move* [absolute] position <pos_x> [px|ppt] <pos_y> [px|ppt]
219 Moves the focused container to the specified position in the workspace. 224 Moves the focused container to the specified position in the workspace.
220 The position can be specified in pixels or percentage points, omitting 225 The position can be specified in pixels or percentage points, omitting
221 the unit defaults to pixels. If _absolute_ is used, the position is 226 the unit defaults to pixels. If _absolute_ is used, the position is
222 relative to all outputs. _absolute_ can not be used with percentage points. 227 relative to all outputs. _absolute_ can not be used with percentage points.
223 228
224*move* [absolute] position center 229*move* [absolute] position center
@@ -319,8 +324,10 @@ set|plus|minus|toggle <amount>
319 established by the *seat* subcommand of the same name. See 324 established by the *seat* subcommand of the same name. See
320 *sway-input*(5) for more ways to affect inhibitors. 325 *sway-input*(5) for more ways to affect inhibitors.
321 326
322*split* vertical|v|horizontal|h|toggle|t 327*split* vertical|v|horizontal|h|none|n|toggle|t
323 Splits the current container, vertically or horizontally. When _toggle_ is 328 Splits the current container, vertically or horizontally. When _none_ is
329 specified, the effect of a previous split is undone if the current
330 container is the only child of a split parent. When _toggle_ is
324 specified, the current container is split opposite to the parent 331 specified, the current container is split opposite to the parent
325 container's layout. 332 container's layout.
326 333
@@ -382,8 +389,8 @@ runtime.
382 for_window <criteria> move container to output <output> 389 for_window <criteria> move container to output <output>
383 390
384*bindsym* [--whole-window] [--border] [--exclude-titlebar] [--release] [--locked] \ 391*bindsym* [--whole-window] [--border] [--exclude-titlebar] [--release] [--locked] \
385[--to-code] [--input-device=<device>] [--no-warn] [--no-repeat] [Group<1-4>+]<key combo> \ 392[--to-code] [--input-device=<device>] [--no-warn] [--no-repeat] [--inhibited] \
386<command> 393[Group<1-4>+]<key combo> <command>
387 Binds _key combo_ to execute the sway command _command_ when pressed. You 394 Binds _key combo_ to execute the sway command _command_ when pressed. You
388 may use XKB key names here (*wev*(1) is a good tool for discovering these). 395 may use XKB key names here (*wev*(1) is a good tool for discovering these).
389 With the flag _--release_, the command is executed when the key combo is 396 With the flag _--release_, the command is executed when the key combo is
@@ -393,6 +400,12 @@ runtime.
393 only be available for that group. By default, if you overwrite a binding, 400 only be available for that group. By default, if you overwrite a binding,
394 swaynag will give you a warning. To silence this, use the _--no-warn_ flag. 401 swaynag will give you a warning. To silence this, use the _--no-warn_ flag.
395 402
403 For specifying modifier keys, you can use the XKB modifier names _Shift_,
404 _Lock_ (for Caps Lock), _Control_, _Mod1_ (for Alt), _Mod2_ (for Num Lock),
405 _Mod3_ (for XKB modifier Mod3), _Mod4_ (for the Logo key), and _Mod5_ (for
406 AltGr). In addition, you can use the aliases _Ctrl_ (for Control), _Alt_
407 (for Alt), and _Super_ (for the Logo key).
408
396 Unless the flag _--locked_ is set, the command will not be run when a 409 Unless the flag _--locked_ is set, the command will not be run when a
397 screen locking program is active. If there is a matching binding with 410 screen locking program is active. If there is a matching binding with
398 and without _--locked_, the one with will be preferred when locked and the 411 and without _--locked_, the one with will be preferred when locked and the
@@ -404,7 +417,7 @@ runtime.
404 a keyboard shortcuts inhibitor is active for the currently focused 417 a keyboard shortcuts inhibitor is active for the currently focused
405 window. Such inhibitors are usually requested by remote desktop and 418 window. Such inhibitors are usually requested by remote desktop and
406 virtualization software to enable the user to send keyboard shortcuts 419 virtualization software to enable the user to send keyboard shortcuts
407 to the remote or virtual session. The _--inhibited_ flag allows to 420 to the remote or virtual session. The _--inhibited_ flag allows one to
408 define bindings which will be exempt from pass-through to such 421 define bindings which will be exempt from pass-through to such
409 software. The same preference logic as for _--locked_ applies. 422 software. The same preference logic as for _--locked_ applies.
410 423
@@ -447,7 +460,8 @@ runtime.
447``` 460```
448 461
449 *bindcode* [--whole-window] [--border] [--exclude-titlebar] [--release] \ 462 *bindcode* [--whole-window] [--border] [--exclude-titlebar] [--release] \
450[--locked] [--input-device=<device>] [--no-warn] [Group<1-4>+]<code> <command> 463[--locked] [--input-device=<device>] [--no-warn] [--no-repeat] [--inhibited] \
464[Group<1-4>+]<code> <command>
451 is also available for binding with key/button codes instead of key/button names. 465 is also available for binding with key/button codes instead of key/button names.
452 466
453*bindswitch* [--locked] [--no-warn] [--reload] <switch>:<state> <command> 467*bindswitch* [--locked] [--no-warn] [--reload] <switch>:<state> <command>
@@ -480,6 +494,62 @@ runtime.
480 bindswitch lid:toggle exec echo "Lid moved" 494 bindswitch lid:toggle exec echo "Lid moved"
481``` 495```
482 496
497*bindgesture* [--exact] [--input-device=<device>] [--no-warn] \
498<gesture>[:<fingers>][:directions] <command>
499 Binds _gesture_ to execute the sway command _command_ when detected.
500 Currently supports the _hold_, _pinch_ or _swipe_ gesture. Optionally
501 can be limited to bind to a certain number of _fingers_ or, for a
502 _pinch_ or _swipe_ gesture, to certain _directions_.
503
504[[ *type*
505:[ *fingers*
506:< *direction*
507| hold
508:- 1 - 5
509: none
510| swipe
511: 3 - 5
512: up, down, left, right
513| pinch
514: 2 - 5
515: all above + inward, outward, clockwise, counterclockwise
516
517 The _fingers_ can be limited to any sensible number or left empty to accept
518 any finger counts.
519 Valid directions are _up_, _down_, _left_ and _right_, as well as _inward_,
520 _outward_, _clockwise_, _counterclockwise_ for the _pinch_ gesture.
521 Multiple directions can be combined by a plus.
522
523 If a _input-device_ is given, the binding will only be executed for
524 that input device and will be executed instead of any binding that is
525 generic to all devices. By default, if you overwrite a binding,
526 swaynag will give you a warning. To silence this, use the _--no-warn_ flag.
527
528 The _--exact_ flag can be used to ensure a binding only matches when exactly
529 all specified directions are matched and nothing more. If there is matching
530 binding with _--exact_, it will be preferred.
531
532 The priority for matching bindings is as follows: input device, then
533 exact matches followed by matches with the highest number of matching
534 directions.
535
536 Gestures executed while the pointer is above a bar are not handled by sway.
537 See the respective documentation, e.g. *bindgesture* in *sway-bar*(5).
538
539 Example:
540```
541 # Allow switching between workspaces with left and right swipes
542 bindgesture swipe:right workspace prev
543 bindgesture swipe:left workspace next
544
545 # Allow container movements by pinching them
546 bindgesture pinch:inward+up move up
547 bindgesture pinch:inward+down move down
548 bindgesture pinch:inward+left move left
549 bindgesture pinch:inward+right move right
550
551```
552
483*client.background* <color> 553*client.background* <color>
484 This command is ignored and is only present for i3 compatibility. 554 This command is ignored and is only present for i3 compatibility.
485 555
@@ -497,6 +567,12 @@ runtime.
497 *client.focused_inactive* 567 *client.focused_inactive*
498 The most recently focused view within a container which is not focused. 568 The most recently focused view within a container which is not focused.
499 569
570 *client.focused_tab_title*
571 A view that has focused descendant container.
572 Tab or stack container title that is the parent of the focused container
573 but is not directly focused. Defaults to focused_inactive if not
574 specified and does not use the indicator and child_border colors.
575
500 *client.placeholder* 576 *client.placeholder*
501 Ignored (present for i3 compatibility). 577 Ignored (present for i3 compatibility).
502 578
@@ -552,6 +628,12 @@ The default colors are:
552: #ffffff 628: #ffffff
553: #484e50 629: #484e50
554: #5f676a 630: #5f676a
631| *focused_tab_title*
632: #333333
633: #5f676a
634: #ffffff
635: n/a
636: n/a
555| *unfocused* 637| *unfocused*
556: #333333 638: #333333
557: #222222 639: #222222
@@ -573,7 +655,8 @@ The default colors are:
573 655
574 656
575*default_border* normal|none|pixel [<n>] 657*default_border* normal|none|pixel [<n>]
576 Set default border style for new tiled windows. 658 Set default border style for new tiled windows. Config reload won't affect
659 existing windows, only newly created ones after the reload.
577 660
578*default_floating_border* normal|none|pixel [<n>] 661*default_floating_border* normal|none|pixel [<n>]
579 Set default border style for new floating windows. This only applies to 662 Set default border style for new floating windows. This only applies to
@@ -606,11 +689,11 @@ The default colors are:
606 after switching between workspaces. 689 after switching between workspaces.
607 690
608*focus_on_window_activation* smart|urgent|focus|none 691*focus_on_window_activation* smart|urgent|focus|none
609 This option determines what to do when an xwayland client requests 692 This option determines what to do when a client requests window activation.
610 window activation. If set to _urgent_, the urgent state will be set 693 If set to _urgent_, the urgent state will be set for that window. If set to
611 for that window. If set to _focus_, the window will become focused. 694 _focus_, the window will become focused. If set to _smart_, the window will
612 If set to _smart_, the window will become focused only if it is already 695 become focused only if it is already visible, otherwise the urgent state
613 visible, otherwise the urgent state will be set. Default is _urgent_. 696 will be set. Default is _urgent_.
614 697
615*focus_wrapping* yes|no|force|workspace 698*focus_wrapping* yes|no|force|workspace
616 This option determines what to do when attempting to focus over the edge 699 This option determines what to do when attempting to focus over the edge
@@ -630,14 +713,14 @@ The default colors are:
630 should be used instead. Regardless of whether pango markup is enabled, 713 should be used instead. Regardless of whether pango markup is enabled,
631 _font_ should be specified as a pango font description. For more 714 _font_ should be specified as a pango font description. For more
632 information on pango font descriptions, see 715 information on pango font descriptions, see
633 https://developer.gnome.org/pango/stable/pango-Fonts.html#pango-font-description-from-string 716 https://docs.gtk.org/Pango/type_func.FontDescription.from_string.html#description
634 717
635*force_display_urgency_hint* <timeout> [ms] 718*force_display_urgency_hint* <timeout> [ms]
636 If an application on another workspace sets an urgency hint, switching to this 719 If an application on another workspace sets an urgency hint, switching to this
637 workspace may lead to immediate focus of the application, which also means the 720 workspace may lead to immediate focus of the application, which also means the
638 window decoration color would be immediately resetted to *client.focused*. This 721 window decoration color would be immediately reset to *client.focused*. This
639 may make it unnecessarily hard to tell which window originally raised the 722 may make it unnecessarily hard to tell which window originally raised the
640 event. This option allows to set a _timeout_ in ms to delay the urgency hint reset. 723 event. This option allows one to set a _timeout_ in ms to delay the urgency hint reset.
641 724
642*titlebar_border_thickness* <thickness> 725*titlebar_border_thickness* <thickness>
643 Thickness of the titlebar border in pixels 726 Thickness of the titlebar border in pixels
@@ -690,9 +773,10 @@ The default colors are:
690 borders will only be enabled if the workspace has more than one visible 773 borders will only be enabled if the workspace has more than one visible
691 child and gaps equal to zero. 774 child and gaps equal to zero.
692 775
693*smart_gaps* on|off 776*smart_gaps* on|off|toggle|inverse_outer
694 If smart_gaps are _on_ gaps will only be enabled if a workspace has more 777 If smart_gaps are _on_ gaps will only be enabled if a workspace has more
695 than one child. 778 than one child. If smart_gaps are _inverse_outer_ outer gaps will only
779 be enabled if a workspace has exactly one child.
696 780
697*mark* --add|--replace [--toggle] <identifier> 781*mark* --add|--replace [--toggle] <identifier>
698 Marks are arbitrary labels that can be used to identify certain windows and 782 Marks are arbitrary labels that can be used to identify certain windows and
@@ -731,6 +815,10 @@ The default colors are:
731 dialog will not be rendered. If _leave_fullscreen_, the view will exit 815 dialog will not be rendered. If _leave_fullscreen_, the view will exit
732 fullscreen mode and the dialog will be rendered. 816 fullscreen mode and the dialog will be rendered.
733 817
818*primary_selection* enabled|disabled
819 Enable or disable the primary selection clipboard. May only be configured
820 at launch. Default is _enabled_.
821
734*set* $<name> <value> 822*set* $<name> <value>
735 Sets variable $_name_ to _value_. You can use the new variable in the 823 Sets variable $_name_ to _value_. You can use the new variable in the
736 arguments of future commands. When the variable is used, it can be escaped 824 arguments of future commands. When the variable is used, it can be escaped
@@ -771,6 +859,11 @@ The default colors are:
771*unbindswitch* <switch>:<state> 859*unbindswitch* <switch>:<state>
772 Removes a binding for when <switch> changes to <state>. 860 Removes a binding for when <switch> changes to <state>.
773 861
862*unbindgesture* [--exact] [--input-device=<device>] \
863<gesture>[:<fingers>][:directions]
864 Removes a binding for the specified _gesture_, _fingers_
865 and _directions_ combination.
866
774*unbindsym* [--whole-window] [--border] [--exclude-titlebar] [--release] [--locked] \ 867*unbindsym* [--whole-window] [--border] [--exclude-titlebar] [--release] [--locked] \
775[--to-code] [--input-device=<device>] <key combo> 868[--to-code] [--input-device=<device>] <key combo>
776 Removes the binding for _key combo_ that was previously bound with the 869 Removes the binding for _key combo_ that was previously bound with the
@@ -876,6 +969,9 @@ properties in practice for your applications.
876 969
877The following attributes may be matched with: 970The following attributes may be matched with:
878 971
972*all*
973 Matches all windows.
974
879*app_id* 975*app_id*
880 Compare value against the app id. Can be a regular expression. If value is 976 Compare value against the app id. Can be a regular expression. If value is
881 \_\_focused\_\_, then the app id must be the same as that of the currently 977 \_\_focused\_\_, then the app id must be the same as that of the currently
@@ -884,7 +980,8 @@ The following attributes may be matched with:
884*class* 980*class*
885 Compare value against the window class. Can be a regular expression. If 981 Compare value against the window class. Can be a regular expression. If
886 value is \_\_focused\_\_, then the window class must be the same as that of 982 value is \_\_focused\_\_, then the window class must be the same as that of
887 the currently focused window. _class_ are specific to X11 applications. 983 the currently focused window. _class_ are specific to X11 applications and
984 require XWayland.
888 985
889*con_id* 986*con_id*
890 Compare against the internal container ID, which you can find via IPC. If 987 Compare against the internal container ID, which you can find via IPC. If
@@ -898,12 +995,14 @@ The following attributes may be matched with:
898 Matches floating windows. 995 Matches floating windows.
899 996
900*id* 997*id*
901 Compare value against the X11 window ID. Must be numeric. 998 Compare value against the X11 window ID. Must be numeric. id is specific to
999 X11 applications and requires XWayland.
902 1000
903*instance* 1001*instance*
904 Compare value against the window instance. Can be a regular expression. If 1002 Compare value against the window instance. Can be a regular expression. If
905 value is \_\_focused\_\_, then the window instance must be the same as that 1003 value is \_\_focused\_\_, then the window instance must be the same as that
906 of the currently focused window. 1004 of the currently focused window. instance is specific to X11 applications and
1005 requires XWayland.
907 1006
908*pid* 1007*pid*
909 Compare value against the window's process ID. Must be numeric. 1008 Compare value against the window's process ID. Must be numeric.
@@ -928,12 +1027,14 @@ The following attributes may be matched with:
928*window_role* 1027*window_role*
929 Compare against the window role (WM_WINDOW_ROLE). Can be a regular 1028 Compare against the window role (WM_WINDOW_ROLE). Can be a regular
930 expression. If value is \_\_focused\_\_, then the window role must be the 1029 expression. If value is \_\_focused\_\_, then the window role must be the
931 same as that of the currently focused window. 1030 same as that of the currently focused window. window_role is specific to X11
1031 applications and requires XWayland.
932 1032
933*window_type* 1033*window_type*
934 Compare against the window type (\_NET_WM_WINDOW_TYPE). Possible values 1034 Compare against the window type (\_NET_WM_WINDOW_TYPE). Possible values
935 are normal, dialog, utility, toolbar, splash, menu, dropdown_menu, 1035 are normal, dialog, utility, toolbar, splash, menu, dropdown_menu,
936 popup_menu, tooltip and notification. 1036 popup_menu, tooltip and notification. window_type is specific to X11
1037 applications and requires XWayland.
937 1038
938*workspace* 1039*workspace*
939 Compare against the workspace name for this view. Can be a regular 1040 Compare against the workspace name for this view. Can be a regular
diff --git a/sway/sway_text_node.c b/sway/sway_text_node.c
new file mode 100644
index 00000000..4b7ee999
--- /dev/null
+++ b/sway/sway_text_node.c
@@ -0,0 +1,308 @@
1#include <drm_fourcc.h>
2#include <stdio.h>
3#include <stdlib.h>
4#include <string.h>
5#include <wlr/types/wlr_buffer.h>
6#include <wlr/interfaces/wlr_buffer.h>
7#include "cairo_util.h"
8#include "log.h"
9#include "pango.h"
10#include "sway/config.h"
11#include "sway/sway_text_node.h"
12
13struct cairo_buffer {
14 struct wlr_buffer base;
15 cairo_surface_t *surface;
16 cairo_t *cairo;
17};
18
19static void cairo_buffer_handle_destroy(struct wlr_buffer *wlr_buffer) {
20 struct cairo_buffer *buffer = wl_container_of(wlr_buffer, buffer, base);
21
22 cairo_surface_destroy(buffer->surface);
23 cairo_destroy(buffer->cairo);
24 free(buffer);
25}
26
27static bool cairo_buffer_handle_begin_data_ptr_access(struct wlr_buffer *wlr_buffer,
28 uint32_t flags, void **data, uint32_t *format, size_t *stride) {
29 struct cairo_buffer *buffer = wl_container_of(wlr_buffer, buffer, base);
30 *data = cairo_image_surface_get_data(buffer->surface);
31 *stride = cairo_image_surface_get_stride(buffer->surface);
32 *format = DRM_FORMAT_ARGB8888;
33 return true;
34}
35
36static void cairo_buffer_handle_end_data_ptr_access(struct wlr_buffer *wlr_buffer) {
37 // This space is intentionally left blank
38}
39
40static const struct wlr_buffer_impl cairo_buffer_impl = {
41 .destroy = cairo_buffer_handle_destroy,
42 .begin_data_ptr_access = cairo_buffer_handle_begin_data_ptr_access,
43 .end_data_ptr_access = cairo_buffer_handle_end_data_ptr_access,
44};
45
46struct text_buffer {
47 struct wlr_scene_buffer *buffer_node;
48 char *text;
49 struct sway_text_node props;
50
51 bool visible;
52 float scale;
53 enum wl_output_subpixel subpixel;
54
55 struct wl_listener outputs_update;
56 struct wl_listener destroy;
57};
58
59static int get_text_width(struct sway_text_node *props) {
60 int width = props->width;
61 if (props->max_width >= 0) {
62 width = MIN(width, props->max_width);
63 }
64 return MAX(width, 0);
65}
66
67static void update_source_box(struct text_buffer *buffer) {
68 struct sway_text_node *props = &buffer->props;
69 struct wlr_fbox source_box = {
70 .x = 0,
71 .y = 0,
72 .width = ceil(get_text_width(props) * buffer->scale),
73 .height = ceil(props->height * buffer->scale),
74 };
75
76 wlr_scene_buffer_set_source_box(buffer->buffer_node, &source_box);
77}
78
79static void render_backing_buffer(struct text_buffer *buffer) {
80 if (!buffer->visible) {
81 return;
82 }
83
84 if (buffer->props.max_width == 0) {
85 wlr_scene_buffer_set_buffer(buffer->buffer_node, NULL);
86 return;
87 }
88
89 float scale = buffer->scale;
90 int width = ceil(buffer->props.width * scale);
91 int height = ceil(buffer->props.height * scale);
92 float *color = (float *)&buffer->props.color;
93 float *background = (float *)&buffer->props.background;
94 PangoContext *pango = NULL;
95
96 cairo_font_options_t *fo = cairo_font_options_create();
97 cairo_font_options_set_hint_style(fo, CAIRO_HINT_STYLE_FULL);
98 enum wl_output_subpixel subpixel = buffer->subpixel;
99 if (subpixel == WL_OUTPUT_SUBPIXEL_NONE || subpixel == WL_OUTPUT_SUBPIXEL_UNKNOWN) {
100 cairo_font_options_set_antialias(fo, CAIRO_ANTIALIAS_GRAY);
101 } else {
102 cairo_font_options_set_antialias(fo, CAIRO_ANTIALIAS_SUBPIXEL);
103 cairo_font_options_set_subpixel_order(fo, to_cairo_subpixel_order(subpixel));
104 }
105
106 cairo_surface_t *surface = cairo_image_surface_create(
107 CAIRO_FORMAT_ARGB32, width, height);
108 cairo_status_t status = cairo_surface_status(surface);
109 if (status != CAIRO_STATUS_SUCCESS) {
110 sway_log(SWAY_ERROR, "cairo_image_surface_create failed: %s",
111 cairo_status_to_string(status));
112 goto err;
113 }
114
115 struct cairo_buffer *cairo_buffer = calloc(1, sizeof(*cairo_buffer));
116 if (!cairo_buffer) {
117 sway_log(SWAY_ERROR, "cairo_buffer allocation failed");
118 goto err;
119 }
120
121 cairo_t *cairo = cairo_create(surface);
122 if (!cairo) {
123 sway_log(SWAY_ERROR, "cairo_create failed");
124 free(cairo_buffer);
125 goto err;
126 }
127
128 cairo_set_antialias(cairo, CAIRO_ANTIALIAS_BEST);
129 cairo_set_font_options(cairo, fo);
130 pango = pango_cairo_create_context(cairo);
131
132 cairo_set_source_rgba(cairo, background[0], background[1], background[2], background[3]);
133 cairo_rectangle(cairo, 0, 0, width, height);
134 cairo_fill(cairo);
135
136 cairo_set_source_rgba(cairo, color[0], color[1], color[2], color[3]);
137 cairo_move_to(cairo, 0, (config->font_baseline - buffer->props.baseline) * scale);
138
139 render_text(cairo, config->font_description, scale, buffer->props.pango_markup,
140 "%s", buffer->text);
141
142 cairo_surface_flush(surface);
143
144 wlr_buffer_init(&cairo_buffer->base, &cairo_buffer_impl, width, height);
145 cairo_buffer->surface = surface;
146 cairo_buffer->cairo = cairo;
147
148 wlr_scene_buffer_set_buffer(buffer->buffer_node, &cairo_buffer->base);
149 wlr_buffer_drop(&cairo_buffer->base);
150 update_source_box(buffer);
151
152 pixman_region32_t opaque;
153 pixman_region32_init(&opaque);
154 if (background[3] == 1) {
155 pixman_region32_union_rect(&opaque, &opaque, 0, 0,
156 buffer->props.width, buffer->props.height);
157 }
158 wlr_scene_buffer_set_opaque_region(buffer->buffer_node, &opaque);
159 pixman_region32_fini(&opaque);
160
161err:
162 if (pango) g_object_unref(pango);
163 cairo_font_options_destroy(fo);
164}
165
166static void handle_outputs_update(struct wl_listener *listener, void *data) {
167 struct text_buffer *buffer = wl_container_of(listener, buffer, outputs_update);
168 struct wlr_scene_outputs_update_event *event = data;
169
170 float scale = 0;
171 enum wl_output_subpixel subpixel = WL_OUTPUT_SUBPIXEL_UNKNOWN;
172
173 for (size_t i = 0; i < event->size; i++) {
174 struct wlr_scene_output *output = event->active[i];
175 if (subpixel == WL_OUTPUT_SUBPIXEL_UNKNOWN) {
176 subpixel = output->output->subpixel;
177 } else if (subpixel != output->output->subpixel) {
178 subpixel = WL_OUTPUT_SUBPIXEL_NONE;
179 }
180
181 if (scale != 0 && scale != output->output->scale) {
182 // drop down to gray scale if we encounter outputs with different
183 // scales or else we will have chromatic aberations
184 subpixel = WL_OUTPUT_SUBPIXEL_NONE;
185 }
186
187 if (scale < output->output->scale) {
188 scale = output->output->scale;
189 }
190 }
191
192 buffer->visible = event->size > 0;
193
194 if (scale != buffer->scale || subpixel != buffer->subpixel) {
195 buffer->scale = scale;
196 buffer->subpixel = subpixel;
197 render_backing_buffer(buffer);
198 }
199}
200
201static void handle_destroy(struct wl_listener *listener, void *data) {
202 struct text_buffer *buffer = wl_container_of(listener, buffer, destroy);
203
204 wl_list_remove(&buffer->outputs_update.link);
205 wl_list_remove(&buffer->destroy.link);
206
207 free(buffer->text);
208 free(buffer);
209}
210
211static void text_calc_size(struct text_buffer *buffer) {
212 struct sway_text_node *props = &buffer->props;
213
214 cairo_t *c = cairo_create(NULL);
215 if (!c) {
216 sway_log(SWAY_ERROR, "cairo_t allocation failed");
217 return;
218 }
219
220 cairo_set_antialias(c, CAIRO_ANTIALIAS_BEST);
221 get_text_size(c, config->font_description, &props->width, NULL,
222 &props->baseline, 1, props->pango_markup, "%s", buffer->text);
223 cairo_destroy(c);
224
225 wlr_scene_buffer_set_dest_size(buffer->buffer_node,
226 get_text_width(props), props->height);
227}
228
229struct sway_text_node *sway_text_node_create(struct wlr_scene_tree *parent,
230 char *text, float color[4], bool pango_markup) {
231 struct text_buffer *buffer = calloc(1, sizeof(*buffer));
232 if (!buffer) {
233 return NULL;
234 }
235
236 struct wlr_scene_buffer *node = wlr_scene_buffer_create(parent, NULL);
237 if (!node) {
238 free(buffer);
239 return NULL;
240 }
241
242 buffer->buffer_node = node;
243 buffer->props.node = &node->node;
244 buffer->props.max_width = -1;
245 buffer->text = strdup(text);
246 if (!buffer->text) {
247 free(buffer);
248 wlr_scene_node_destroy(&node->node);
249 return NULL;
250 }
251
252 buffer->props.height = config->font_height;
253 buffer->props.pango_markup = pango_markup;
254 memcpy(&buffer->props.color, color, sizeof(*color) * 4);
255
256 buffer->destroy.notify = handle_destroy;
257 wl_signal_add(&node->node.events.destroy, &buffer->destroy);
258 buffer->outputs_update.notify = handle_outputs_update;
259 wl_signal_add(&node->events.outputs_update, &buffer->outputs_update);
260
261 text_calc_size(buffer);
262
263 return &buffer->props;
264}
265
266void sway_text_node_set_color(struct sway_text_node *node, float color[4]) {
267 if (memcmp(&node->color, color, sizeof(*color) * 4) == 0) {
268 return;
269 }
270
271 memcpy(&node->color, color, sizeof(*color) * 4);
272 struct text_buffer *buffer = wl_container_of(node, buffer, props);
273
274 render_backing_buffer(buffer);
275}
276
277void sway_text_node_set_text(struct sway_text_node *node, char *text) {
278 struct text_buffer *buffer = wl_container_of(node, buffer, props);
279 if (strcmp(buffer->text, text) == 0) {
280 return;
281 }
282
283 char *new_text = strdup(text);
284 if (!new_text) {
285 return;
286 }
287
288 free(buffer->text);
289 buffer->text = new_text;
290
291 text_calc_size(buffer);
292 render_backing_buffer(buffer);
293}
294
295void sway_text_node_set_max_width(struct sway_text_node *node, int max_width) {
296 struct text_buffer *buffer = wl_container_of(node, buffer, props);
297 buffer->props.max_width = max_width;
298 wlr_scene_buffer_set_dest_size(buffer->buffer_node,
299 get_text_width(&buffer->props), buffer->props.height);
300 update_source_box(buffer);
301 render_backing_buffer(buffer);
302}
303
304void sway_text_node_set_background(struct sway_text_node *node, float background[4]) {
305 struct text_buffer *buffer = wl_container_of(node, buffer, props);
306 memcpy(&node->background, background, sizeof(*background) * 4);
307 render_backing_buffer(buffer);
308}
diff --git a/sway/swaynag.c b/sway/swaynag.c
index ba582989..bc5e23ea 100644
--- a/sway/swaynag.c
+++ b/sway/swaynag.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <signal.h> 1#include <signal.h>
3#include <stdbool.h> 2#include <stdbool.h>
4#include <stdlib.h> 3#include <stdlib.h>
@@ -64,6 +63,8 @@ bool swaynag_spawn(const char *swaynag_command,
64 sway_log(SWAY_ERROR, "Failed to create fork for swaynag"); 63 sway_log(SWAY_ERROR, "Failed to create fork for swaynag");
65 goto failed; 64 goto failed;
66 } else if (pid == 0) { 65 } else if (pid == 0) {
66 restore_nofile_limit();
67
67 pid = fork(); 68 pid = fork();
68 if (pid < 0) { 69 if (pid < 0) {
69 sway_log_errno(SWAY_ERROR, "fork failed"); 70 sway_log_errno(SWAY_ERROR, "fork failed");
@@ -143,22 +144,16 @@ void swaynag_log(const char *swaynag_command, struct swaynag_instance *swaynag,
143 144
144 va_list args; 145 va_list args;
145 va_start(args, fmt); 146 va_start(args, fmt);
146 size_t length = vsnprintf(NULL, 0, fmt, args) + 1; 147 char *str = vformat_str(fmt, args);
147 va_end(args); 148 va_end(args);
148 149 if (!str) {
149 char *temp = malloc(length + 1);
150 if (!temp) {
151 sway_log(SWAY_ERROR, "Failed to allocate buffer for swaynag log entry."); 150 sway_log(SWAY_ERROR, "Failed to allocate buffer for swaynag log entry.");
152 return; 151 return;
153 } 152 }
154 153
155 va_start(args, fmt); 154 write(swaynag->fd[1], str, strlen(str));
156 vsnprintf(temp, length, fmt, args);
157 va_end(args);
158
159 write(swaynag->fd[1], temp, length);
160 155
161 free(temp); 156 free(str);
162} 157}
163 158
164void swaynag_show(struct swaynag_instance *swaynag) { 159void swaynag_show(struct swaynag_instance *swaynag) {
diff --git a/sway/tree/arrange.c b/sway/tree/arrange.c
index 4aa82c35..d4003fe6 100644
--- a/sway/tree/arrange.c
+++ b/sway/tree/arrange.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <ctype.h> 1#include <ctype.h>
3#include <stdbool.h> 2#include <stdbool.h>
4#include <stdlib.h> 3#include <stdlib.h>
@@ -264,6 +263,9 @@ void arrange_workspace(struct sway_workspace *workspace) {
264 area->width, area->height, area->x, area->y); 263 area->width, area->height, area->x, area->y);
265 264
266 bool first_arrange = workspace->width == 0 && workspace->height == 0; 265 bool first_arrange = workspace->width == 0 && workspace->height == 0;
266 struct wlr_box prev_box;
267 workspace_get_box(workspace, &prev_box);
268
267 double prev_x = workspace->x - workspace->current_gaps.left; 269 double prev_x = workspace->x - workspace->current_gaps.left;
268 double prev_y = workspace->y - workspace->current_gaps.top; 270 double prev_y = workspace->y - workspace->current_gaps.top;
269 workspace->width = area->width; 271 workspace->width = area->width;
@@ -277,13 +279,14 @@ void arrange_workspace(struct sway_workspace *workspace) {
277 if (!first_arrange && (diff_x != 0 || diff_y != 0)) { 279 if (!first_arrange && (diff_x != 0 || diff_y != 0)) {
278 for (int i = 0; i < workspace->floating->length; ++i) { 280 for (int i = 0; i < workspace->floating->length; ++i) {
279 struct sway_container *floater = workspace->floating->items[i]; 281 struct sway_container *floater = workspace->floating->items[i];
280 container_floating_translate(floater, diff_x, diff_y);
281 double center_x = floater->pending.x + floater->pending.width / 2;
282 double center_y = floater->pending.y + floater->pending.height / 2;
283 struct wlr_box workspace_box; 282 struct wlr_box workspace_box;
284 workspace_get_box(workspace, &workspace_box); 283 workspace_get_box(workspace, &workspace_box);
285 if (!wlr_box_contains_point(&workspace_box, center_x, center_y)) { 284 floating_fix_coordinates(floater, &prev_box, &workspace_box);
286 container_floating_move_to_center(floater); 285 // Set transformation for scratchpad windows.
286 if (floater->scratchpad) {
287 struct wlr_box output_box;
288 output_get_box(output, &output_box);
289 floater->transform = output_box;
287 } 290 }
288 } 291 }
289 } 292 }
@@ -311,12 +314,13 @@ void arrange_output(struct sway_output *output) {
311 if (config->reloading) { 314 if (config->reloading) {
312 return; 315 return;
313 } 316 }
314 const struct wlr_box *output_box = wlr_output_layout_get_box( 317 struct wlr_box output_box;
315 root->output_layout, output->wlr_output); 318 wlr_output_layout_get_box(root->output_layout,
316 output->lx = output_box->x; 319 output->wlr_output, &output_box);
317 output->ly = output_box->y; 320 output->lx = output_box.x;
318 output->width = output_box->width; 321 output->ly = output_box.y;
319 output->height = output_box->height; 322 output->width = output_box.width;
323 output->height = output_box.height;
320 324
321 for (int i = 0; i < output->workspaces->length; ++i) { 325 for (int i = 0; i < output->workspaces->length; ++i) {
322 struct sway_workspace *workspace = output->workspaces->items[i]; 326 struct sway_workspace *workspace = output->workspaces->items[i];
@@ -328,12 +332,12 @@ void arrange_root(void) {
328 if (config->reloading) { 332 if (config->reloading) {
329 return; 333 return;
330 } 334 }
331 const struct wlr_box *layout_box = 335 struct wlr_box layout_box;
332 wlr_output_layout_get_box(root->output_layout, NULL); 336 wlr_output_layout_get_box(root->output_layout, NULL, &layout_box);
333 root->x = layout_box->x; 337 root->x = layout_box.x;
334 root->y = layout_box->y; 338 root->y = layout_box.y;
335 root->width = layout_box->width; 339 root->width = layout_box.width;
336 root->height = layout_box->height; 340 root->height = layout_box.height;
337 341
338 if (root->fullscreen_global) { 342 if (root->fullscreen_global) {
339 struct sway_container *fs = root->fullscreen_global; 343 struct sway_container *fs = root->fullscreen_global;
diff --git a/sway/tree/container.c b/sway/tree/container.c
index 1e84e603..80ef34fe 100644
--- a/sway/tree/container.c
+++ b/sway/tree/container.c
@@ -1,29 +1,77 @@
1#define _POSIX_C_SOURCE 200809L
2#include <assert.h> 1#include <assert.h>
3#include <drm_fourcc.h> 2#include <drm_fourcc.h>
4#include <stdint.h> 3#include <stdint.h>
5#include <stdlib.h> 4#include <stdlib.h>
6#include <string.h>
7#include <strings.h>
8#include <wayland-server-core.h> 5#include <wayland-server-core.h>
6#include <wlr/types/wlr_foreign_toplevel_management_v1.h>
7#include <wlr/types/wlr_linux_dmabuf_v1.h>
9#include <wlr/types/wlr_output_layout.h> 8#include <wlr/types/wlr_output_layout.h>
10#include "cairo_util.h" 9#include <wlr/types/wlr_subcompositor.h>
11#include "pango.h" 10#include "linux-dmabuf-unstable-v1-protocol.h"
12#include "sway/config.h" 11#include "sway/config.h"
13#include "sway/desktop.h"
14#include "sway/desktop/transaction.h" 12#include "sway/desktop/transaction.h"
15#include "sway/input/input-manager.h" 13#include "sway/input/input-manager.h"
16#include "sway/input/seat.h" 14#include "sway/input/seat.h"
17#include "sway/ipc-server.h" 15#include "sway/ipc-server.h"
16#include "sway/scene_descriptor.h"
17#include "sway/sway_text_node.h"
18#include "sway/output.h" 18#include "sway/output.h"
19#include "sway/server.h" 19#include "sway/server.h"
20#include "sway/tree/arrange.h" 20#include "sway/tree/arrange.h"
21#include "sway/tree/view.h" 21#include "sway/tree/view.h"
22#include "sway/tree/workspace.h" 22#include "sway/tree/workspace.h"
23#include "sway/xdg_decoration.h"
23#include "list.h" 24#include "list.h"
24#include "log.h" 25#include "log.h"
25#include "stringop.h" 26#include "stringop.h"
26 27
28static void handle_output_enter(
29 struct wl_listener *listener, void *data) {
30 struct sway_container *con = wl_container_of(
31 listener, con, output_enter);
32 struct wlr_scene_output *output = data;
33
34 if (con->view->foreign_toplevel) {
35 wlr_foreign_toplevel_handle_v1_output_enter(
36 con->view->foreign_toplevel, output->output);
37 }
38}
39
40static void handle_output_leave(
41 struct wl_listener *listener, void *data) {
42 struct sway_container *con = wl_container_of(
43 listener, con, output_leave);
44 struct wlr_scene_output *output = data;
45
46 if (con->view->foreign_toplevel) {
47 wlr_foreign_toplevel_handle_v1_output_leave(
48 con->view->foreign_toplevel, output->output);
49 }
50}
51
52static bool handle_point_accepts_input(
53 struct wlr_scene_buffer *buffer, double *x, double *y) {
54 return false;
55}
56
57static struct wlr_scene_rect *alloc_rect_node(struct wlr_scene_tree *parent,
58 bool *failed) {
59 if (*failed) {
60 return NULL;
61 }
62
63 // just pass in random values. These will be overwritten when
64 // they need to be used.
65 struct wlr_scene_rect *rect = wlr_scene_rect_create(
66 parent, 0, 0, (float[4]){0.f, 0.f, 0.f, 1.f});
67 if (!rect) {
68 sway_log(SWAY_ERROR, "Failed to allocate a wlr_scene_rect");
69 *failed = true;
70 }
71
72 return rect;
73}
74
27struct sway_container *container_create(struct sway_view *view) { 75struct sway_container *container_create(struct sway_view *view) {
28 struct sway_container *c = calloc(1, sizeof(struct sway_container)); 76 struct sway_container *c = calloc(1, sizeof(struct sway_container));
29 if (!c) { 77 if (!c) {
@@ -31,23 +79,415 @@ struct sway_container *container_create(struct sway_view *view) {
31 return NULL; 79 return NULL;
32 } 80 }
33 node_init(&c->node, N_CONTAINER, c); 81 node_init(&c->node, N_CONTAINER, c);
34 c->pending.layout = L_NONE; 82
35 c->view = view; 83 // Container tree structure
36 c->alpha = 1.0f; 84 // - scene tree
85 // - title bar
86 // - border
87 // - background
88 // - title text
89 // - marks text
90 // - border
91 // - border top/bottom/left/right
92 // - content_tree (we put the content node here so when we disable the
93 // border everything gets disabled. We only render the content iff there
94 // is a border as well)
95 // - buffer used for output enter/leave events for foreign_toplevel
96 bool failed = false;
97 c->scene_tree = alloc_scene_tree(root->staging, &failed);
98
99 c->title_bar.tree = alloc_scene_tree(c->scene_tree, &failed);
100 c->title_bar.border = alloc_scene_tree(c->title_bar.tree, &failed);
101 c->title_bar.background = alloc_scene_tree(c->title_bar.tree, &failed);
102
103 // for opacity purposes we need to carfully create the scene such that
104 // none of our rect nodes as well as text buffers don't overlap. To do
105 // this we have to create rects such that they go around text buffers
106 for (int i = 0; i < 4; i++) {
107 alloc_rect_node(c->title_bar.border, &failed);
108 }
109
110 for (int i = 0; i < 5; i++) {
111 alloc_rect_node(c->title_bar.background, &failed);
112 }
113
114 c->border.tree = alloc_scene_tree(c->scene_tree, &failed);
115 c->content_tree = alloc_scene_tree(c->border.tree, &failed);
116
117 if (view) {
118 // only containers with views can have borders
119 c->border.top = alloc_rect_node(c->border.tree, &failed);
120 c->border.bottom = alloc_rect_node(c->border.tree, &failed);
121 c->border.left = alloc_rect_node(c->border.tree, &failed);
122 c->border.right = alloc_rect_node(c->border.tree, &failed);
123
124 c->output_handler = wlr_scene_buffer_create(c->border.tree, NULL);
125 if (!c->output_handler) {
126 sway_log(SWAY_ERROR, "Failed to allocate a scene node");
127 failed = true;
128 }
129
130 if (!failed) {
131 c->output_enter.notify = handle_output_enter;
132 wl_signal_add(&c->output_handler->events.output_enter,
133 &c->output_enter);
134 c->output_leave.notify = handle_output_leave;
135 wl_signal_add(&c->output_handler->events.output_leave,
136 &c->output_leave);
137 c->output_handler->point_accepts_input = handle_point_accepts_input;
138 }
139 }
140
141 if (!failed && !scene_descriptor_assign(&c->scene_tree->node,
142 SWAY_SCENE_DESC_CONTAINER, c)) {
143 failed = true;
144 }
145
146 if (failed) {
147 wlr_scene_node_destroy(&c->scene_tree->node);
148 free(c);
149 return NULL;
150 }
37 151
38 if (!view) { 152 if (!view) {
39 c->pending.children = create_list(); 153 c->pending.children = create_list();
40 c->current.children = create_list(); 154 c->current.children = create_list();
41 } 155 }
156
157 c->pending.layout = L_NONE;
158 c->view = view;
159 c->alpha = 1.0f;
42 c->marks = create_list(); 160 c->marks = create_list();
43 c->outputs = create_list();
44 161
45 wl_signal_init(&c->events.destroy); 162 wl_signal_init(&c->events.destroy);
46 wl_signal_emit(&root->events.new_node, &c->node); 163 wl_signal_emit_mutable(&root->events.new_node, &c->node);
164
165 container_update(c);
47 166
48 return c; 167 return c;
49} 168}
50 169
170static bool container_is_focused(struct sway_container *con, void *data) {
171 return con->current.focused;
172}
173
174static bool container_has_focused_child(struct sway_container *con) {
175 return container_find_child(con, container_is_focused, NULL);
176}
177
178static bool container_is_current_parent_focused(struct sway_container *con) {
179 if (con->current.parent) {
180 struct sway_container *parent = con->current.parent;
181 return parent->current.focused || container_is_current_parent_focused(parent);
182 } else if (con->current.workspace) {
183 struct sway_workspace *ws = con->current.workspace;
184 return ws->current.focused;
185 }
186
187 return false;
188}
189
190static struct border_colors *container_get_current_colors(
191 struct sway_container *con) {
192 struct border_colors *colors;
193
194 bool urgent = con->view ?
195 view_is_urgent(con->view) : container_has_urgent_child(con);
196 struct sway_container *active_child;
197
198 if (con->current.parent) {
199 active_child = con->current.parent->current.focused_inactive_child;
200 } else if (con->current.workspace) {
201 active_child = con->current.workspace->current.focused_inactive_child;
202 } else {
203 active_child = NULL;
204 }
205
206 if (urgent) {
207 colors = &config->border_colors.urgent;
208 } else if (con->current.focused || container_is_current_parent_focused(con)) {
209 colors = &config->border_colors.focused;
210 } else if (config->has_focused_tab_title && container_has_focused_child(con)) {
211 colors = &config->border_colors.focused_tab_title;
212 } else if (con == active_child) {
213 colors = &config->border_colors.focused_inactive;
214 } else {
215 colors = &config->border_colors.unfocused;
216 }
217
218 return colors;
219}
220
221static bool container_is_current_floating(struct sway_container *container) {
222 if (!container->current.parent && container->current.workspace &&
223 list_find(container->current.workspace->floating, container) != -1) {
224 return true;
225 }
226 if (container->scratchpad) {
227 return true;
228 }
229 return false;
230}
231
232// scene rect wants premultiplied colors
233static void scene_rect_set_color(struct wlr_scene_rect *rect,
234 const float color[4], float opacity) {
235 const float premultiplied[] = {
236 color[0] * color[3] * opacity,
237 color[1] * color[3] * opacity,
238 color[2] * color[3] * opacity,
239 color[3] * opacity,
240 };
241
242 wlr_scene_rect_set_color(rect, premultiplied);
243}
244
245void container_update(struct sway_container *con) {
246 struct border_colors *colors = container_get_current_colors(con);
247 list_t *siblings = NULL;
248 enum sway_container_layout layout = L_NONE;
249 float alpha = con->alpha;
250
251 if (con->current.parent) {
252 siblings = con->current.parent->current.children;
253 layout = con->current.parent->current.layout;
254 } else if (con->current.workspace) {
255 siblings = con->current.workspace->current.tiling;
256 layout = con->current.workspace->current.layout;
257 }
258
259 float bottom[4], right[4];
260 memcpy(bottom, colors->child_border, sizeof(bottom));
261 memcpy(right, colors->child_border, sizeof(right));
262
263 if (!container_is_current_floating(con) && siblings && siblings->length == 1) {
264 if (layout == L_HORIZ) {
265 memcpy(right, colors->indicator, sizeof(right));
266 } else if (layout == L_VERT) {
267 memcpy(bottom, colors->indicator, sizeof(bottom));
268 }
269 }
270
271 struct wlr_scene_node *node;
272 wl_list_for_each(node, &con->title_bar.border->children, link) {
273 struct wlr_scene_rect *rect = wlr_scene_rect_from_node(node);
274 scene_rect_set_color(rect, colors->border, alpha);
275 }
276
277 wl_list_for_each(node, &con->title_bar.background->children, link) {
278 struct wlr_scene_rect *rect = wlr_scene_rect_from_node(node);
279 scene_rect_set_color(rect, colors->background, alpha);
280 }
281
282 if (con->view) {
283 scene_rect_set_color(con->border.top, colors->child_border, alpha);
284 scene_rect_set_color(con->border.bottom, bottom, alpha);
285 scene_rect_set_color(con->border.left, colors->child_border, alpha);
286 scene_rect_set_color(con->border.right, right, alpha);
287 }
288
289 if (con->title_bar.title_text) {
290 sway_text_node_set_color(con->title_bar.title_text, colors->text);
291 sway_text_node_set_background(con->title_bar.title_text, colors->background);
292 }
293
294 if (con->title_bar.marks_text) {
295 sway_text_node_set_color(con->title_bar.marks_text, colors->text);
296 sway_text_node_set_background(con->title_bar.marks_text, colors->background);
297 }
298}
299
300void container_update_itself_and_parents(struct sway_container *con) {
301 container_update(con);
302
303 if (con->current.parent) {
304 container_update_itself_and_parents(con->current.parent);
305 }
306}
307
308static void update_rect_list(struct wlr_scene_tree *tree, pixman_region32_t *region) {
309 int len;
310 const pixman_box32_t *rects = pixman_region32_rectangles(region, &len);
311
312 wlr_scene_node_set_enabled(&tree->node, len > 0);
313 if (len == 0) {
314 return;
315 }
316
317 int i = 0;
318 struct wlr_scene_node *node;
319 wl_list_for_each(node, &tree->children, link) {
320 struct wlr_scene_rect *rect = wlr_scene_rect_from_node(node);
321 wlr_scene_node_set_enabled(&rect->node, i < len);
322
323 if (i < len) {
324 const pixman_box32_t *box = &rects[i++];
325 wlr_scene_node_set_position(&rect->node, box->x1, box->y1);
326 wlr_scene_rect_set_size(rect, box->x2 - box->x1, box->y2 - box->y1);
327 }
328 }
329}
330
331void container_arrange_title_bar(struct sway_container *con) {
332 enum alignment title_align = config->title_align;
333 int marks_buffer_width = 0;
334 int width = con->title_width;
335 int height = container_titlebar_height();
336
337 pixman_region32_t text_area;
338 pixman_region32_init(&text_area);
339
340 if (con->title_bar.marks_text) {
341 struct sway_text_node *node = con->title_bar.marks_text;
342 marks_buffer_width = node->width;
343
344 int h_padding;
345 if (title_align == ALIGN_RIGHT) {
346 h_padding = config->titlebar_h_padding;
347 } else {
348 h_padding = width - config->titlebar_h_padding - marks_buffer_width;
349 }
350
351 h_padding = MAX(h_padding, 0);
352
353 int alloc_width = MIN((int)node->width,
354 width - h_padding - config->titlebar_h_padding);
355 alloc_width = MAX(alloc_width, 0);
356
357 sway_text_node_set_max_width(node, alloc_width);
358 wlr_scene_node_set_position(node->node,
359 h_padding, (height - node->height) >> 1);
360
361 pixman_region32_union_rect(&text_area, &text_area,
362 node->node->x, node->node->y, alloc_width, node->height);
363 }
364
365 if (con->title_bar.title_text) {
366 struct sway_text_node *node = con->title_bar.title_text;
367
368 int h_padding;
369 if (title_align == ALIGN_RIGHT) {
370 h_padding = width - config->titlebar_h_padding - node->width;
371 } else if (title_align == ALIGN_CENTER) {
372 h_padding = ((int)width - marks_buffer_width - node->width) >> 1;
373 } else {
374 h_padding = config->titlebar_h_padding;
375 }
376
377 h_padding = MAX(h_padding, 0);
378
379 int alloc_width = MIN((int) node->width,
380 width - h_padding - config->titlebar_h_padding);
381 alloc_width = MAX(alloc_width, 0);
382
383 sway_text_node_set_max_width(node, alloc_width);
384 wlr_scene_node_set_position(node->node,
385 h_padding, (height - node->height) >> 1);
386
387 pixman_region32_union_rect(&text_area, &text_area,
388 node->node->x, node->node->y, alloc_width, node->height);
389 }
390
391 // silence pixman errors
392 if (width <= 0 || height <= 0) {
393 pixman_region32_fini(&text_area);
394 return;
395 }
396
397 pixman_region32_t background, border;
398
399 int thickness = config->titlebar_border_thickness;
400 pixman_region32_init_rect(&background,
401 thickness, thickness,
402 width - thickness * 2, height - thickness * 2);
403 pixman_region32_init_rect(&border, 0, 0, width, height);
404 pixman_region32_subtract(&border, &border, &background);
405
406 pixman_region32_subtract(&background, &background, &text_area);
407 pixman_region32_fini(&text_area);
408
409 update_rect_list(con->title_bar.background, &background);
410 pixman_region32_fini(&background);
411
412 update_rect_list(con->title_bar.border, &border);
413 pixman_region32_fini(&border);
414
415 container_update(con);
416}
417
418void container_update_marks(struct sway_container *con) {
419 char *buffer = NULL;
420
421 if (config->show_marks && con->marks->length) {
422 size_t len = 0;
423 for (int i = 0; i < con->marks->length; ++i) {
424 char *mark = con->marks->items[i];
425 if (mark[0] != '_') {
426 len += strlen(mark) + 2;
427 }
428 }
429 buffer = calloc(len + 1, 1);
430 char *part = malloc(len + 1);
431
432 if (!sway_assert(buffer && part, "Unable to allocate memory")) {
433 free(buffer);
434 return;
435 }
436
437 for (int i = 0; i < con->marks->length; ++i) {
438 char *mark = con->marks->items[i];
439 if (mark[0] != '_') {
440 snprintf(part, len + 1, "[%s]", mark);
441 strcat(buffer, part);
442 }
443 }
444 free(part);
445 }
446
447 if (!buffer) {
448 if (con->title_bar.marks_text) {
449 wlr_scene_node_destroy(con->title_bar.marks_text->node);
450 con->title_bar.marks_text = NULL;
451 }
452 } else if (!con->title_bar.marks_text) {
453 struct border_colors *colors = container_get_current_colors(con);
454
455 con->title_bar.marks_text = sway_text_node_create(con->title_bar.tree,
456 buffer, colors->text, false);
457 } else {
458 sway_text_node_set_text(con->title_bar.marks_text, buffer);
459 }
460
461 container_arrange_title_bar(con);
462 free(buffer);
463}
464
465void container_update_title_bar(struct sway_container *con) {
466 if (!con->formatted_title) {
467 return;
468 }
469
470 struct border_colors *colors = container_get_current_colors(con);
471
472 if (con->title_bar.title_text) {
473 wlr_scene_node_destroy(con->title_bar.title_text->node);
474 con->title_bar.title_text = NULL;
475 }
476
477 con->title_bar.title_text = sway_text_node_create(con->title_bar.tree,
478 con->formatted_title, colors->text, config->pango_markup);
479
480 // we always have to remake these text buffers completely for text font
481 // changes etc...
482 if (con->title_bar.marks_text) {
483 wlr_scene_node_destroy(con->title_bar.marks_text->node);
484 con->title_bar.marks_text = NULL;
485 }
486
487 container_update_marks(con);
488 container_arrange_title_bar(con);
489}
490
51void container_destroy(struct sway_container *con) { 491void container_destroy(struct sway_container *con) {
52 if (!sway_assert(con->node.destroying, 492 if (!sway_assert(con->node.destroying,
53 "Tried to free container which wasn't marked as destroying")) { 493 "Tried to free container which wasn't marked as destroying")) {
@@ -59,29 +499,21 @@ void container_destroy(struct sway_container *con) {
59 } 499 }
60 free(con->title); 500 free(con->title);
61 free(con->formatted_title); 501 free(con->formatted_title);
62 wlr_texture_destroy(con->title_focused);
63 wlr_texture_destroy(con->title_focused_inactive);
64 wlr_texture_destroy(con->title_unfocused);
65 wlr_texture_destroy(con->title_urgent);
66 list_free(con->pending.children); 502 list_free(con->pending.children);
67 list_free(con->current.children); 503 list_free(con->current.children);
68 list_free(con->outputs);
69 504
70 list_free_items_and_destroy(con->marks); 505 list_free_items_and_destroy(con->marks);
71 wlr_texture_destroy(con->marks_focused);
72 wlr_texture_destroy(con->marks_focused_inactive);
73 wlr_texture_destroy(con->marks_unfocused);
74 wlr_texture_destroy(con->marks_urgent);
75 506
76 if (con->view) { 507 if (con->view && con->view->container == con) {
77 if (con->view->container == con) { 508 con->view->container = NULL;
78 con->view->container = NULL; 509 wlr_scene_node_destroy(&con->output_handler->node);
79 }
80 if (con->view->destroying) { 510 if (con->view->destroying) {
81 view_destroy(con->view); 511 view_destroy(con->view);
82 } 512 }
83 } 513 }
84 514
515 scene_node_disown_children(con->content_tree);
516 wlr_scene_node_destroy(&con->scene_tree->node);
85 free(con); 517 free(con);
86} 518}
87 519
@@ -98,7 +530,7 @@ void container_begin_destroy(struct sway_container *con) {
98 container_fullscreen_disable(con); 530 container_fullscreen_disable(con);
99 } 531 }
100 532
101 wl_signal_emit(&con->node.events.destroy, &con->node); 533 wl_signal_emit_mutable(&con->node.events.destroy, &con->node);
102 534
103 container_end_mouse_operation(con); 535 container_end_mouse_operation(con);
104 536
@@ -168,267 +600,6 @@ struct sway_container *container_find_child(struct sway_container *container,
168 return NULL; 600 return NULL;
169} 601}
170 602
171static struct sway_container *surface_at_view(struct sway_container *con, double lx, double ly,
172 struct wlr_surface **surface, double *sx, double *sy) {
173 if (!sway_assert(con->view, "Expected a view")) {
174 return NULL;
175 }
176 struct sway_view *view = con->view;
177 double view_sx = lx - con->surface_x + view->geometry.x;
178 double view_sy = ly - con->surface_y + view->geometry.y;
179
180 double _sx, _sy;
181 struct wlr_surface *_surface = NULL;
182 switch (view->type) {
183#if HAVE_XWAYLAND
184 case SWAY_VIEW_XWAYLAND:
185 _surface = wlr_surface_surface_at(view->surface,
186 view_sx, view_sy, &_sx, &_sy);
187 break;
188#endif
189 case SWAY_VIEW_XDG_SHELL:
190 _surface = wlr_xdg_surface_surface_at(
191 view->wlr_xdg_surface,
192 view_sx, view_sy, &_sx, &_sy);
193 break;
194 }
195 if (_surface) {
196 *sx = _sx;
197 *sy = _sy;
198 *surface = _surface;
199 return con;
200 }
201 return NULL;
202}
203
204/**
205 * container_at for a container with layout L_TABBED.
206 */
207static struct sway_container *container_at_tabbed(struct sway_node *parent,
208 double lx, double ly,
209 struct wlr_surface **surface, double *sx, double *sy) {
210 struct wlr_box box;
211 node_get_box(parent, &box);
212 if (lx < box.x || lx > box.x + box.width ||
213 ly < box.y || ly > box.y + box.height) {
214 return NULL;
215 }
216 struct sway_seat *seat = input_manager_current_seat();
217 list_t *children = node_get_children(parent);
218 if (!children->length) {
219 return NULL;
220 }
221
222 // Tab titles
223 int title_height = container_titlebar_height();
224 if (ly < box.y + title_height) {
225 int tab_width = box.width / children->length;
226 int child_index = (lx - box.x) / tab_width;
227 if (child_index >= children->length) {
228 child_index = children->length - 1;
229 }
230 struct sway_container *child = children->items[child_index];
231 return child;
232 }
233
234 // Surfaces
235 struct sway_node *current = seat_get_active_tiling_child(seat, parent);
236 return current ? tiling_container_at(current, lx, ly, surface, sx, sy) : NULL;
237}
238
239/**
240 * container_at for a container with layout L_STACKED.
241 */
242static struct sway_container *container_at_stacked(struct sway_node *parent,
243 double lx, double ly,
244 struct wlr_surface **surface, double *sx, double *sy) {
245 struct wlr_box box;
246 node_get_box(parent, &box);
247 if (lx < box.x || lx > box.x + box.width ||
248 ly < box.y || ly > box.y + box.height) {
249 return NULL;
250 }
251 struct sway_seat *seat = input_manager_current_seat();
252 list_t *children = node_get_children(parent);
253
254 // Title bars
255 int title_height = container_titlebar_height();
256 if (title_height > 0) {
257 int child_index = (ly - box.y) / title_height;
258 if (child_index < children->length) {
259 struct sway_container *child = children->items[child_index];
260 return child;
261 }
262 }
263
264 // Surfaces
265 struct sway_node *current = seat_get_active_tiling_child(seat, parent);
266 return current ? tiling_container_at(current, lx, ly, surface, sx, sy) : NULL;
267}
268
269/**
270 * container_at for a container with layout L_HORIZ or L_VERT.
271 */
272static struct sway_container *container_at_linear(struct sway_node *parent,
273 double lx, double ly,
274 struct wlr_surface **surface, double *sx, double *sy) {
275 list_t *children = node_get_children(parent);
276 for (int i = 0; i < children->length; ++i) {
277 struct sway_container *child = children->items[i];
278 struct sway_container *container =
279 tiling_container_at(&child->node, lx, ly, surface, sx, sy);
280 if (container) {
281 return container;
282 }
283 }
284 return NULL;
285}
286
287static struct sway_container *floating_container_at(double lx, double ly,
288 struct wlr_surface **surface, double *sx, double *sy) {
289 // For outputs with floating containers that overhang the output bounds,
290 // those at the end of the output list appear on top of floating
291 // containers from other outputs, so iterate the list in reverse.
292 for (int i = root->outputs->length - 1; i >= 0; --i) {
293 struct sway_output *output = root->outputs->items[i];
294 for (int j = 0; j < output->workspaces->length; ++j) {
295 struct sway_workspace *ws = output->workspaces->items[j];
296 if (!workspace_is_visible(ws)) {
297 continue;
298 }
299 // Items at the end of the list are on top, so iterate the list in
300 // reverse.
301 for (int k = ws->floating->length - 1; k >= 0; --k) {
302 struct sway_container *floater = ws->floating->items[k];
303 struct sway_container *container =
304 tiling_container_at(&floater->node, lx, ly, surface, sx, sy);
305 if (container) {
306 return container;
307 }
308 }
309 }
310 }
311 return NULL;
312}
313
314static struct sway_container *view_container_content_at(struct sway_node *parent,
315 double lx, double ly,
316 struct wlr_surface **surface, double *sx, double *sy) {
317 if (!sway_assert(node_is_view(parent), "Expected a view")) {
318 return NULL;
319 }
320
321 struct sway_container *container = parent->sway_container;
322 struct wlr_box box = {
323 .x = container->pending.content_x,
324 .y = container->pending.content_y,
325 .width = container->pending.content_width,
326 .height = container->pending.content_height,
327 };
328
329 if (wlr_box_contains_point(&box, lx, ly)) {
330 surface_at_view(parent->sway_container, lx, ly, surface, sx, sy);
331 return container;
332 }
333
334 return NULL;
335}
336
337static struct sway_container *view_container_at(struct sway_node *parent,
338 double lx, double ly,
339 struct wlr_surface **surface, double *sx, double *sy) {
340 if (!sway_assert(node_is_view(parent), "Expected a view")) {
341 return NULL;
342 }
343
344 struct sway_container *container = parent->sway_container;
345 struct wlr_box box = {
346 .x = container->pending.x,
347 .y = container->pending.y,
348 .width = container->pending.width,
349 .height = container->pending.height,
350 };
351
352 if (wlr_box_contains_point(&box, lx, ly)) {
353 surface_at_view(parent->sway_container, lx, ly, surface, sx, sy);
354 return container;
355 }
356
357 return NULL;
358}
359
360struct sway_container *tiling_container_at(struct sway_node *parent,
361 double lx, double ly,
362 struct wlr_surface **surface, double *sx, double *sy) {
363 if (node_is_view(parent)) {
364 return view_container_at(parent, lx, ly, surface, sx, sy);
365 }
366 if (!node_get_children(parent)) {
367 return NULL;
368 }
369 switch (node_get_layout(parent)) {
370 case L_HORIZ:
371 case L_VERT:
372 return container_at_linear(parent, lx, ly, surface, sx, sy);
373 case L_TABBED:
374 return container_at_tabbed(parent, lx, ly, surface, sx, sy);
375 case L_STACKED:
376 return container_at_stacked(parent, lx, ly, surface, sx, sy);
377 case L_NONE:
378 return NULL;
379 }
380 return NULL;
381}
382
383static bool surface_is_popup(struct wlr_surface *surface) {
384 if (wlr_surface_is_xdg_surface(surface)) {
385 struct wlr_xdg_surface *xdg_surface =
386 wlr_xdg_surface_from_wlr_surface(surface);
387 while (xdg_surface && xdg_surface->role != WLR_XDG_SURFACE_ROLE_NONE) {
388 if (xdg_surface->role == WLR_XDG_SURFACE_ROLE_POPUP) {
389 return true;
390 }
391 xdg_surface = xdg_surface->toplevel->parent;
392 }
393 return false;
394 }
395
396 return false;
397}
398
399struct sway_container *container_at(struct sway_workspace *workspace,
400 double lx, double ly,
401 struct wlr_surface **surface, double *sx, double *sy) {
402 struct sway_container *c;
403
404 struct sway_seat *seat = input_manager_current_seat();
405 struct sway_container *focus = seat_get_focused_container(seat);
406 bool is_floating = focus && container_is_floating_or_child(focus);
407 // Focused view's popups
408 if (focus && focus->view) {
409 c = surface_at_view(focus, lx, ly, surface, sx, sy);
410 if (c && surface_is_popup(*surface)) {
411 return c;
412 }
413 *surface = NULL;
414 }
415 // Floating
416 if ((c = floating_container_at(lx, ly, surface ,sx ,sy))) {
417 return c;
418 }
419 // Tiling (focused)
420 if (focus && focus->view && !is_floating) {
421 if ((c = view_container_content_at(&focus->node, lx, ly, surface, sx, sy))) {
422 return c;
423 }
424 }
425 // Tiling (non-focused)
426 if ((c = tiling_container_at(&workspace->node, lx, ly, surface, sx, sy))) {
427 return c;
428 }
429 return NULL;
430}
431
432void container_for_each_child(struct sway_container *container, 603void container_for_each_child(struct sway_container *container,
433 void (*f)(struct sway_container *container, void *data), 604 void (*f)(struct sway_container *container, void *data),
434 void *data) { 605 void *data) {
@@ -474,123 +645,6 @@ bool container_has_ancestor(struct sway_container *descendant,
474 return false; 645 return false;
475} 646}
476 647
477void container_damage_whole(struct sway_container *container) {
478 for (int i = 0; i < root->outputs->length; ++i) {
479 struct sway_output *output = root->outputs->items[i];
480 output_damage_whole_container(output, container);
481 }
482}
483
484/**
485 * Return the output which will be used for scale purposes.
486 * This is the most recently entered output.
487 */
488struct sway_output *container_get_effective_output(struct sway_container *con) {
489 if (con->outputs->length == 0) {
490 return NULL;
491 }
492 return con->outputs->items[con->outputs->length - 1];
493}
494
495static void update_title_texture(struct sway_container *con,
496 struct wlr_texture **texture, struct border_colors *class) {
497 struct sway_output *output = container_get_effective_output(con);
498 if (!output) {
499 return;
500 }
501 if (*texture) {
502 wlr_texture_destroy(*texture);
503 *texture = NULL;
504 }
505 if (!con->formatted_title) {
506 return;
507 }
508
509 double scale = output->wlr_output->scale;
510 int width = 0;
511 int height = con->title_height * scale;
512
513 // We must use a non-nil cairo_t for cairo_set_font_options to work.
514 // Therefore, we cannot use cairo_create(NULL).
515 cairo_surface_t *dummy_surface = cairo_image_surface_create(
516 CAIRO_FORMAT_ARGB32, 0, 0);
517 cairo_t *c = cairo_create(dummy_surface);
518 cairo_set_antialias(c, CAIRO_ANTIALIAS_BEST);
519 cairo_font_options_t *fo = cairo_font_options_create();
520 cairo_font_options_set_hint_style(fo, CAIRO_HINT_STYLE_FULL);
521 if (output->wlr_output->subpixel == WL_OUTPUT_SUBPIXEL_NONE) {
522 cairo_font_options_set_antialias(fo, CAIRO_ANTIALIAS_GRAY);
523 } else {
524 cairo_font_options_set_antialias(fo, CAIRO_ANTIALIAS_SUBPIXEL);
525 cairo_font_options_set_subpixel_order(fo,
526 to_cairo_subpixel_order(output->wlr_output->subpixel));
527 }
528 cairo_set_font_options(c, fo);
529 get_text_size(c, config->font, &width, NULL, NULL, scale,
530 config->pango_markup, "%s", con->formatted_title);
531 cairo_surface_destroy(dummy_surface);
532 cairo_destroy(c);
533
534 if (width == 0 || height == 0) {
535 return;
536 }
537
538 cairo_surface_t *surface = cairo_image_surface_create(
539 CAIRO_FORMAT_ARGB32, width, height);
540 cairo_t *cairo = cairo_create(surface);
541 cairo_set_antialias(cairo, CAIRO_ANTIALIAS_BEST);
542 cairo_set_font_options(cairo, fo);
543 cairo_font_options_destroy(fo);
544 cairo_set_source_rgba(cairo, class->background[0], class->background[1],
545 class->background[2], class->background[3]);
546 cairo_paint(cairo);
547 PangoContext *pango = pango_cairo_create_context(cairo);
548 cairo_set_source_rgba(cairo, class->text[0], class->text[1],
549 class->text[2], class->text[3]);
550 cairo_move_to(cairo, 0, 0);
551
552 pango_printf(cairo, config->font, scale, config->pango_markup,
553 "%s", con->formatted_title);
554
555 cairo_surface_flush(surface);
556 unsigned char *data = cairo_image_surface_get_data(surface);
557 int stride = cairo_image_surface_get_stride(surface);
558 struct wlr_renderer *renderer = wlr_backend_get_renderer(
559 output->wlr_output->backend);
560 *texture = wlr_texture_from_pixels(
561 renderer, DRM_FORMAT_ARGB8888, stride, width, height, data);
562 cairo_surface_destroy(surface);
563 g_object_unref(pango);
564 cairo_destroy(cairo);
565}
566
567void container_update_title_textures(struct sway_container *container) {
568 update_title_texture(container, &container->title_focused,
569 &config->border_colors.focused);
570 update_title_texture(container, &container->title_focused_inactive,
571 &config->border_colors.focused_inactive);
572 update_title_texture(container, &container->title_unfocused,
573 &config->border_colors.unfocused);
574 update_title_texture(container, &container->title_urgent,
575 &config->border_colors.urgent);
576 container_damage_whole(container);
577}
578
579void container_calculate_title_height(struct sway_container *container) {
580 if (!container->formatted_title) {
581 container->title_height = 0;
582 return;
583 }
584 cairo_t *cairo = cairo_create(NULL);
585 int height;
586 int baseline;
587 get_text_size(cairo, config->font, NULL, &height, &baseline, 1,
588 config->pango_markup, "%s", container->formatted_title);
589 cairo_destroy(cairo);
590 container->title_height = height;
591 container->title_baseline = baseline;
592}
593
594/** 648/**
595 * Calculate and return the length of the tree representation. 649 * Calculate and return the length of the tree representation.
596 * An example tree representation is: V[Terminal, Firefox] 650 * An example tree representation is: V[Terminal, Firefox]
@@ -656,8 +710,13 @@ void container_update_representation(struct sway_container *con) {
656 } 710 }
657 container_build_representation(con->pending.layout, con->pending.children, 711 container_build_representation(con->pending.layout, con->pending.children,
658 con->formatted_title); 712 con->formatted_title);
659 container_calculate_title_height(con); 713
660 container_update_title_textures(con); 714 if (con->title_bar.title_text) {
715 sway_text_node_set_text(con->title_bar.title_text, con->formatted_title);
716 container_arrange_title_bar(con);
717 } else {
718 container_update_title_bar(con);
719 }
661 } 720 }
662 if (con->pending.parent) { 721 if (con->pending.parent) {
663 container_update_representation(con->pending.parent); 722 container_update_representation(con->pending.parent);
@@ -688,12 +747,13 @@ void floating_calculate_constraints(int *min_width, int *max_width,
688 *min_height = config->floating_minimum_height; 747 *min_height = config->floating_minimum_height;
689 } 748 }
690 749
691 struct wlr_box *box = wlr_output_layout_get_box(root->output_layout, NULL); 750 struct wlr_box box;
751 wlr_output_layout_get_box(root->output_layout, NULL, &box);
692 752
693 if (config->floating_maximum_width == -1) { // no maximum 753 if (config->floating_maximum_width == -1) { // no maximum
694 *max_width = INT_MAX; 754 *max_width = INT_MAX;
695 } else if (config->floating_maximum_width == 0) { // automatic 755 } else if (config->floating_maximum_width == 0) { // automatic
696 *max_width = box->width; 756 *max_width = box.width;
697 } else { 757 } else {
698 *max_width = config->floating_maximum_width; 758 *max_width = config->floating_maximum_width;
699 } 759 }
@@ -701,13 +761,28 @@ void floating_calculate_constraints(int *min_width, int *max_width,
701 if (config->floating_maximum_height == -1) { // no maximum 761 if (config->floating_maximum_height == -1) { // no maximum
702 *max_height = INT_MAX; 762 *max_height = INT_MAX;
703 } else if (config->floating_maximum_height == 0) { // automatic 763 } else if (config->floating_maximum_height == 0) { // automatic
704 *max_height = box->height; 764 *max_height = box.height;
705 } else { 765 } else {
706 *max_height = config->floating_maximum_height; 766 *max_height = config->floating_maximum_height;
707 } 767 }
708 768
709} 769}
710 770
771void floating_fix_coordinates(struct sway_container *con, struct wlr_box *old, struct wlr_box *new) {
772 if (!old->width || !old->height) {
773 // Fall back to centering on the workspace.
774 container_floating_move_to_center(con);
775 } else {
776 int rel_x = con->pending.x - old->x + (con->pending.width / 2);
777 int rel_y = con->pending.y - old->y + (con->pending.height / 2);
778
779 con->pending.x = new->x + (double)(rel_x * new->width) / old->width - (con->pending.width / 2);
780 con->pending.y = new->y + (double)(rel_y * new->height) / old->height - (con->pending.height / 2);
781
782 sway_log(SWAY_DEBUG, "Transformed container %p to coords (%f, %f)", con, con->pending.x, con->pending.y);
783 }
784}
785
711static void floating_natural_resize(struct sway_container *con) { 786static void floating_natural_resize(struct sway_container *con) {
712 int min_width, max_width, min_height, max_height; 787 int min_width, max_width, min_height, max_height;
713 floating_calculate_constraints(&min_width, &max_width, 788 floating_calculate_constraints(&min_width, &max_width,
@@ -733,9 +808,9 @@ void container_floating_resize_and_center(struct sway_container *con) {
733 return; 808 return;
734 } 809 }
735 810
736 struct wlr_box *ob = wlr_output_layout_get_box(root->output_layout, 811 struct wlr_box ob;
737 ws->output->wlr_output); 812 wlr_output_layout_get_box(root->output_layout, ws->output->wlr_output, &ob);
738 if (!ob) { 813 if (wlr_box_empty(&ob)) {
739 // On NOOP output. Will be called again when moved to an output 814 // On NOOP output. Will be called again when moved to an output
740 con->pending.x = 0; 815 con->pending.x = 0;
741 con->pending.y = 0; 816 con->pending.y = 0;
@@ -747,8 +822,8 @@ void container_floating_resize_and_center(struct sway_container *con) {
747 floating_natural_resize(con); 822 floating_natural_resize(con);
748 if (!con->view) { 823 if (!con->view) {
749 if (con->pending.width > ws->width || con->pending.height > ws->height) { 824 if (con->pending.width > ws->width || con->pending.height > ws->height) {
750 con->pending.x = ob->x + (ob->width - con->pending.width) / 2; 825 con->pending.x = ob.x + (ob.width - con->pending.width) / 2;
751 con->pending.y = ob->y + (ob->height - con->pending.height) / 2; 826 con->pending.y = ob.y + (ob.height - con->pending.height) / 2;
752 } else { 827 } else {
753 con->pending.x = ws->x + (ws->width - con->pending.width) / 2; 828 con->pending.x = ws->x + (ws->width - con->pending.width) / 2;
754 con->pending.y = ws->y + (ws->height - con->pending.height) / 2; 829 con->pending.y = ws->y + (ws->height - con->pending.height) / 2;
@@ -756,8 +831,8 @@ void container_floating_resize_and_center(struct sway_container *con) {
756 } else { 831 } else {
757 if (con->pending.content_width > ws->width 832 if (con->pending.content_width > ws->width
758 || con->pending.content_height > ws->height) { 833 || con->pending.content_height > ws->height) {
759 con->pending.content_x = ob->x + (ob->width - con->pending.content_width) / 2; 834 con->pending.content_x = ob.x + (ob.width - con->pending.content_width) / 2;
760 con->pending.content_y = ob->y + (ob->height - con->pending.content_height) / 2; 835 con->pending.content_y = ob.y + (ob.height - con->pending.content_height) / 2;
761 } else { 836 } else {
762 con->pending.content_x = ws->x + (ws->width - con->pending.content_width) / 2; 837 con->pending.content_x = ws->x + (ws->width - con->pending.content_width) / 2;
763 con->pending.content_y = ws->y + (ws->height - con->pending.content_height) / 2; 838 con->pending.content_y = ws->y + (ws->height - con->pending.content_height) / 2;
@@ -779,11 +854,11 @@ void container_floating_set_default_size(struct sway_container *con) {
779 int min_width, max_width, min_height, max_height; 854 int min_width, max_width, min_height, max_height;
780 floating_calculate_constraints(&min_width, &max_width, 855 floating_calculate_constraints(&min_width, &max_width,
781 &min_height, &max_height); 856 &min_height, &max_height);
782 struct wlr_box *box = calloc(1, sizeof(struct wlr_box)); 857 struct wlr_box box;
783 workspace_get_box(con->pending.workspace, box); 858 workspace_get_box(con->pending.workspace, &box);
784 859
785 double width = fmax(min_width, fmin(box->width * 0.5, max_width)); 860 double width = fmax(min_width, fmin(box.width * 0.5, max_width));
786 double height = fmax(min_height, fmin(box->height * 0.75, max_height)); 861 double height = fmax(min_height, fmin(box.height * 0.75, max_height));
787 if (!con->view) { 862 if (!con->view) {
788 con->pending.width = width; 863 con->pending.width = width;
789 con->pending.height = height; 864 con->pending.height = height;
@@ -792,8 +867,6 @@ void container_floating_set_default_size(struct sway_container *con) {
792 con->pending.content_height = height; 867 con->pending.content_height = height;
793 container_set_geometry_from_content(con); 868 container_set_geometry_from_content(con);
794 } 869 }
795
796 free(box);
797} 870}
798 871
799 872
@@ -835,7 +908,13 @@ void container_set_floating(struct sway_container *container, bool enable) {
835 if (container->view) { 908 if (container->view) {
836 view_set_tiled(container->view, false); 909 view_set_tiled(container->view, false);
837 if (container->view->using_csd) { 910 if (container->view->using_csd) {
911 container->saved_border = container->pending.border;
838 container->pending.border = B_CSD; 912 container->pending.border = B_CSD;
913 if (container->view->xdg_decoration) {
914 struct sway_xdg_decoration *deco = container->view->xdg_decoration;
915 wlr_xdg_toplevel_decoration_v1_set_mode(deco->wlr_xdg_decoration,
916 WLR_XDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE);
917 }
839 } 918 }
840 } 919 }
841 container_floating_set_default_size(container); 920 container_floating_set_default_size(container);
@@ -873,6 +952,11 @@ void container_set_floating(struct sway_container *container, bool enable) {
873 view_set_tiled(container->view, true); 952 view_set_tiled(container->view, true);
874 if (container->view->using_csd) { 953 if (container->view->using_csd) {
875 container->pending.border = container->saved_border; 954 container->pending.border = container->saved_border;
955 if (container->view->xdg_decoration) {
956 struct sway_xdg_decoration *deco = container->view->xdg_decoration;
957 wlr_xdg_toplevel_decoration_v1_set_mode(deco->wlr_xdg_decoration,
958 WLR_XDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE);
959 }
876 } 960 }
877 } 961 }
878 container->width_fraction = 0; 962 container->width_fraction = 0;
@@ -918,17 +1002,6 @@ bool container_is_floating(struct sway_container *container) {
918 return false; 1002 return false;
919} 1003}
920 1004
921bool container_is_current_floating(struct sway_container *container) {
922 if (!container->current.parent && container->current.workspace &&
923 list_find(container->current.workspace->floating, container) != -1) {
924 return true;
925 }
926 if (container->scratchpad) {
927 return true;
928 }
929 return false;
930}
931
932void container_get_box(struct sway_container *container, struct wlr_box *box) { 1005void container_get_box(struct sway_container *container, struct wlr_box *box) {
933 box->x = container->pending.x; 1006 box->x = container->pending.x;
934 box->y = container->pending.y; 1007 box->y = container->pending.y;
@@ -1012,6 +1085,13 @@ void container_floating_move_to(struct sway_container *con,
1012 workspace_add_floating(new_workspace, con); 1085 workspace_add_floating(new_workspace, con);
1013 arrange_workspace(old_workspace); 1086 arrange_workspace(old_workspace);
1014 arrange_workspace(new_workspace); 1087 arrange_workspace(new_workspace);
1088 // If the moved container was a visible scratchpad container, then
1089 // update its transform.
1090 if (con->scratchpad) {
1091 struct wlr_box output_box;
1092 output_get_box(new_output, &output_box);
1093 con->transform = output_box;
1094 }
1015 workspace_detect_urgent(old_workspace); 1095 workspace_detect_urgent(old_workspace);
1016 workspace_detect_urgent(new_workspace); 1096 workspace_detect_urgent(new_workspace);
1017 } 1097 }
@@ -1223,72 +1303,6 @@ bool container_is_fullscreen_or_child(struct sway_container *container) {
1223 return false; 1303 return false;
1224} 1304}
1225 1305
1226static void surface_send_enter_iterator(struct wlr_surface *surface,
1227 int x, int y, void *data) {
1228 struct wlr_output *wlr_output = data;
1229 wlr_surface_send_enter(surface, wlr_output);
1230}
1231
1232static void surface_send_leave_iterator(struct wlr_surface *surface,
1233 int x, int y, void *data) {
1234 struct wlr_output *wlr_output = data;
1235 wlr_surface_send_leave(surface, wlr_output);
1236}
1237
1238void container_discover_outputs(struct sway_container *con) {
1239 struct wlr_box con_box = {
1240 .x = con->current.x,
1241 .y = con->current.y,
1242 .width = con->current.width,
1243 .height = con->current.height,
1244 };
1245 struct sway_output *old_output = container_get_effective_output(con);
1246
1247 for (int i = 0; i < root->outputs->length; ++i) {
1248 struct sway_output *output = root->outputs->items[i];
1249 struct wlr_box output_box;
1250 output_get_box(output, &output_box);
1251 struct wlr_box intersection;
1252 bool intersects =
1253 wlr_box_intersection(&intersection, &con_box, &output_box);
1254 int index = list_find(con->outputs, output);
1255
1256 if (intersects && index == -1) {
1257 // Send enter
1258 sway_log(SWAY_DEBUG, "Container %p entered output %p", con, output);
1259 if (con->view) {
1260 view_for_each_surface(con->view,
1261 surface_send_enter_iterator, output->wlr_output);
1262 if (con->view->foreign_toplevel) {
1263 wlr_foreign_toplevel_handle_v1_output_enter(
1264 con->view->foreign_toplevel, output->wlr_output);
1265 }
1266 }
1267 list_add(con->outputs, output);
1268 } else if (!intersects && index != -1) {
1269 // Send leave
1270 sway_log(SWAY_DEBUG, "Container %p left output %p", con, output);
1271 if (con->view) {
1272 view_for_each_surface(con->view,
1273 surface_send_leave_iterator, output->wlr_output);
1274 if (con->view->foreign_toplevel) {
1275 wlr_foreign_toplevel_handle_v1_output_leave(
1276 con->view->foreign_toplevel, output->wlr_output);
1277 }
1278 }
1279 list_del(con->outputs, index);
1280 }
1281 }
1282 struct sway_output *new_output = container_get_effective_output(con);
1283 double old_scale = old_output && old_output->enabled ?
1284 old_output->wlr_output->scale : -1;
1285 double new_scale = new_output ? new_output->wlr_output->scale : -1;
1286 if (old_scale != new_scale) {
1287 container_update_title_textures(con);
1288 container_update_marks_textures(con);
1289 }
1290}
1291
1292enum sway_container_layout container_parent_layout(struct sway_container *con) { 1306enum sway_container_layout container_parent_layout(struct sway_container *con) {
1293 if (con->pending.parent) { 1307 if (con->pending.parent) {
1294 return con->pending.parent->pending.layout; 1308 return con->pending.parent->pending.layout;
@@ -1299,19 +1313,11 @@ enum sway_container_layout container_parent_layout(struct sway_container *con) {
1299 return L_NONE; 1313 return L_NONE;
1300} 1314}
1301 1315
1302enum sway_container_layout container_current_parent_layout(
1303 struct sway_container *con) {
1304 if (con->current.parent) {
1305 return con->current.parent->current.layout;
1306 }
1307 return con->current.workspace->current.layout;
1308}
1309
1310list_t *container_get_siblings(struct sway_container *container) { 1316list_t *container_get_siblings(struct sway_container *container) {
1311 if (container->pending.parent) { 1317 if (container->pending.parent) {
1312 return container->pending.parent->pending.children; 1318 return container->pending.parent->pending.children;
1313 } 1319 }
1314 if (container_is_scratchpad_hidden(container)) { 1320 if (!container->pending.workspace) {
1315 return NULL; 1321 return NULL;
1316 } 1322 }
1317 if (list_find(container->pending.workspace->tiling, container) != -1) { 1323 if (list_find(container->pending.workspace->tiling, container) != -1) {
@@ -1324,13 +1330,6 @@ int container_sibling_index(struct sway_container *child) {
1324 return list_find(container_get_siblings(child), child); 1330 return list_find(container_get_siblings(child), child);
1325} 1331}
1326 1332
1327list_t *container_get_current_siblings(struct sway_container *container) {
1328 if (container->current.parent) {
1329 return container->current.parent->current.children;
1330 }
1331 return container->current.workspace->current.tiling;
1332}
1333
1334void container_handle_fullscreen_reparent(struct sway_container *con) { 1333void container_handle_fullscreen_reparent(struct sway_container *con) {
1335 if (con->pending.fullscreen_mode != FULLSCREEN_WORKSPACE || !con->pending.workspace || 1334 if (con->pending.fullscreen_mode != FULLSCREEN_WORKSPACE || !con->pending.workspace ||
1336 con->pending.workspace->fullscreen == con) { 1335 con->pending.workspace->fullscreen == con) {
@@ -1545,7 +1544,7 @@ bool container_find_and_unmark(char *mark) {
1545 if (strcmp(con_mark, mark) == 0) { 1544 if (strcmp(con_mark, mark) == 0) {
1546 free(con_mark); 1545 free(con_mark);
1547 list_del(con->marks, i); 1546 list_del(con->marks, i);
1548 container_update_marks_textures(con); 1547 container_update_marks(con);
1549 ipc_event_window(con, "mark"); 1548 ipc_event_window(con, "mark");
1550 return true; 1549 return true;
1551 } 1550 }
@@ -1576,103 +1575,15 @@ void container_add_mark(struct sway_container *con, char *mark) {
1576 ipc_event_window(con, "mark"); 1575 ipc_event_window(con, "mark");
1577} 1576}
1578 1577
1579static void update_marks_texture(struct sway_container *con,
1580 struct wlr_texture **texture, struct border_colors *class) {
1581 struct sway_output *output = container_get_effective_output(con);
1582 if (!output) {
1583 return;
1584 }
1585 if (*texture) {
1586 wlr_texture_destroy(*texture);
1587 *texture = NULL;
1588 }
1589 if (!con->marks->length) {
1590 return;
1591 }
1592
1593 size_t len = 0;
1594 for (int i = 0; i < con->marks->length; ++i) {
1595 char *mark = con->marks->items[i];
1596 if (mark[0] != '_') {
1597 len += strlen(mark) + 2;
1598 }
1599 }
1600 char *buffer = calloc(len + 1, 1);
1601 char *part = malloc(len + 1);
1602
1603 if (!sway_assert(buffer && part, "Unable to allocate memory")) {
1604 free(buffer);
1605 return;
1606 }
1607
1608 for (int i = 0; i < con->marks->length; ++i) {
1609 char *mark = con->marks->items[i];
1610 if (mark[0] != '_') {
1611 sprintf(part, "[%s]", mark);
1612 strcat(buffer, part);
1613 }
1614 }
1615 free(part);
1616
1617 double scale = output->wlr_output->scale;
1618 int width = 0;
1619 int height = con->title_height * scale;
1620
1621 cairo_t *c = cairo_create(NULL);
1622 get_text_size(c, config->font, &width, NULL, NULL, scale, false,
1623 "%s", buffer);
1624 cairo_destroy(c);
1625
1626 if (width == 0 || height == 0) {
1627 return;
1628 }
1629
1630 cairo_surface_t *surface = cairo_image_surface_create(
1631 CAIRO_FORMAT_ARGB32, width, height);
1632 cairo_t *cairo = cairo_create(surface);
1633 cairo_set_source_rgba(cairo, class->background[0], class->background[1],
1634 class->background[2], class->background[3]);
1635 cairo_paint(cairo);
1636 PangoContext *pango = pango_cairo_create_context(cairo);
1637 cairo_set_antialias(cairo, CAIRO_ANTIALIAS_BEST);
1638 cairo_set_source_rgba(cairo, class->text[0], class->text[1],
1639 class->text[2], class->text[3]);
1640 cairo_move_to(cairo, 0, 0);
1641
1642 pango_printf(cairo, config->font, scale, false, "%s", buffer);
1643
1644 cairo_surface_flush(surface);
1645 unsigned char *data = cairo_image_surface_get_data(surface);
1646 int stride = cairo_image_surface_get_stride(surface);
1647 struct wlr_renderer *renderer = wlr_backend_get_renderer(
1648 output->wlr_output->backend);
1649 *texture = wlr_texture_from_pixels(
1650 renderer, DRM_FORMAT_ARGB8888, stride, width, height, data);
1651 cairo_surface_destroy(surface);
1652 g_object_unref(pango);
1653 cairo_destroy(cairo);
1654 free(buffer);
1655}
1656
1657void container_update_marks_textures(struct sway_container *con) {
1658 if (!config->show_marks) {
1659 return;
1660 }
1661 update_marks_texture(con, &con->marks_focused,
1662 &config->border_colors.focused);
1663 update_marks_texture(con, &con->marks_focused_inactive,
1664 &config->border_colors.focused_inactive);
1665 update_marks_texture(con, &con->marks_unfocused,
1666 &config->border_colors.unfocused);
1667 update_marks_texture(con, &con->marks_urgent,
1668 &config->border_colors.urgent);
1669 container_damage_whole(con);
1670}
1671
1672void container_raise_floating(struct sway_container *con) { 1578void container_raise_floating(struct sway_container *con) {
1673 // Bring container to front by putting it at the end of the floating list. 1579 // Bring container to front by putting it at the end of the floating list.
1674 struct sway_container *floater = container_toplevel_ancestor(con); 1580 struct sway_container *floater = container_toplevel_ancestor(con);
1675 if (container_is_floating(floater) && floater->pending.workspace) { 1581 if (container_is_floating(floater) && floater->pending.workspace) {
1582 // it's okay to just raise the scene directly instead of waiting
1583 // for the transaction to go through. We won't be reconfiguring
1584 // surfaces
1585 wlr_scene_node_raise_to_top(&floater->scene_tree->node);
1586
1676 list_move_to_end(floater->pending.workspace->floating, floater); 1587 list_move_to_end(floater->pending.workspace->floating, floater);
1677 node_set_dirty(&floater->pending.workspace->node); 1588 node_set_dirty(&floater->pending.workspace->node);
1678 } 1589 }
@@ -1756,3 +1667,177 @@ int container_squash(struct sway_container *con) {
1756 } 1667 }
1757 return change; 1668 return change;
1758} 1669}
1670
1671static void swap_places(struct sway_container *con1,
1672 struct sway_container *con2) {
1673 struct sway_container *temp = malloc(sizeof(struct sway_container));
1674 temp->pending.x = con1->pending.x;
1675 temp->pending.y = con1->pending.y;
1676 temp->pending.width = con1->pending.width;
1677 temp->pending.height = con1->pending.height;
1678 temp->width_fraction = con1->width_fraction;
1679 temp->height_fraction = con1->height_fraction;
1680 temp->pending.parent = con1->pending.parent;
1681 temp->pending.workspace = con1->pending.workspace;
1682 bool temp_floating = container_is_floating(con1);
1683
1684 con1->pending.x = con2->pending.x;
1685 con1->pending.y = con2->pending.y;
1686 con1->pending.width = con2->pending.width;
1687 con1->pending.height = con2->pending.height;
1688 con1->width_fraction = con2->width_fraction;
1689 con1->height_fraction = con2->height_fraction;
1690
1691 con2->pending.x = temp->pending.x;
1692 con2->pending.y = temp->pending.y;
1693 con2->pending.width = temp->pending.width;
1694 con2->pending.height = temp->pending.height;
1695 con2->width_fraction = temp->width_fraction;
1696 con2->height_fraction = temp->height_fraction;
1697
1698 int temp_index = container_sibling_index(con1);
1699 if (con2->pending.parent) {
1700 container_insert_child(con2->pending.parent, con1,
1701 container_sibling_index(con2));
1702 } else if (container_is_floating(con2)) {
1703 workspace_add_floating(con2->pending.workspace, con1);
1704 } else {
1705 workspace_insert_tiling(con2->pending.workspace, con1,
1706 container_sibling_index(con2));
1707 }
1708 if (temp->pending.parent) {
1709 container_insert_child(temp->pending.parent, con2, temp_index);
1710 } else if (temp_floating) {
1711 workspace_add_floating(temp->pending.workspace, con2);
1712 } else {
1713 workspace_insert_tiling(temp->pending.workspace, con2, temp_index);
1714 }
1715
1716 free(temp);
1717}
1718
1719static void swap_focus(struct sway_container *con1,
1720 struct sway_container *con2, struct sway_seat *seat,
1721 struct sway_container *focus) {
1722 if (focus == con1 || focus == con2) {
1723 struct sway_workspace *ws1 = con1->pending.workspace;
1724 struct sway_workspace *ws2 = con2->pending.workspace;
1725 enum sway_container_layout layout1 = container_parent_layout(con1);
1726 enum sway_container_layout layout2 = container_parent_layout(con2);
1727 if (focus == con1 && (layout2 == L_TABBED || layout2 == L_STACKED)) {
1728 if (workspace_is_visible(ws2)) {
1729 seat_set_focus(seat, &con2->node);
1730 }
1731 seat_set_focus_container(seat, ws1 != ws2 ? con2 : con1);
1732 } else if (focus == con2 && (layout1 == L_TABBED
1733 || layout1 == L_STACKED)) {
1734 if (workspace_is_visible(ws1)) {
1735 seat_set_focus(seat, &con1->node);
1736 }
1737 seat_set_focus_container(seat, ws1 != ws2 ? con1 : con2);
1738 } else if (ws1 != ws2) {
1739 seat_set_focus_container(seat, focus == con1 ? con2 : con1);
1740 } else {
1741 seat_set_focus_container(seat, focus);
1742 }
1743 } else {
1744 seat_set_focus_container(seat, focus);
1745 }
1746
1747 if (root->fullscreen_global) {
1748 seat_set_focus(seat,
1749 seat_get_focus_inactive(seat, &root->fullscreen_global->node));
1750 }
1751}
1752
1753void container_swap(struct sway_container *con1, struct sway_container *con2) {
1754 if (!sway_assert(con1 && con2, "Cannot swap with nothing")) {
1755 return;
1756 }
1757 if (!sway_assert(!container_has_ancestor(con1, con2)
1758 && !container_has_ancestor(con2, con1),
1759 "Cannot swap ancestor and descendant")) {
1760 return;
1761 }
1762
1763 sway_log(SWAY_DEBUG, "Swapping containers %zu and %zu",
1764 con1->node.id, con2->node.id);
1765
1766 bool scratch1 = con1->scratchpad;
1767 bool hidden1 = container_is_scratchpad_hidden(con1);
1768 bool scratch2 = con2->scratchpad;
1769 bool hidden2 = container_is_scratchpad_hidden(con2);
1770 if (scratch1) {
1771 if (hidden1) {
1772 root_scratchpad_show(con1);
1773 }
1774 root_scratchpad_remove_container(con1);
1775 }
1776 if (scratch2) {
1777 if (hidden2) {
1778 root_scratchpad_show(con2);
1779 }
1780 root_scratchpad_remove_container(con2);
1781 }
1782
1783 enum sway_fullscreen_mode fs1 = con1->pending.fullscreen_mode;
1784 if (fs1) {
1785 container_fullscreen_disable(con1);
1786 }
1787 enum sway_fullscreen_mode fs2 = con2->pending.fullscreen_mode;
1788 if (fs2) {
1789 container_fullscreen_disable(con2);
1790 }
1791
1792 struct sway_seat *seat = input_manager_current_seat();
1793 struct sway_container *focus = seat_get_focused_container(seat);
1794 struct sway_workspace *vis1 =
1795 output_get_active_workspace(con1->pending.workspace->output);
1796 struct sway_workspace *vis2 =
1797 output_get_active_workspace(con2->pending.workspace->output);
1798 if (!sway_assert(vis1 && vis2, "con1 or con2 are on an output without a"
1799 "workspace. This should not happen")) {
1800 return;
1801 }
1802
1803 char *stored_prev_name = NULL;
1804 if (seat->prev_workspace_name) {
1805 stored_prev_name = strdup(seat->prev_workspace_name);
1806 }
1807
1808 swap_places(con1, con2);
1809
1810 if (!workspace_is_visible(vis1)) {
1811 seat_set_focus(seat, seat_get_focus_inactive(seat, &vis1->node));
1812 }
1813 if (!workspace_is_visible(vis2)) {
1814 seat_set_focus(seat, seat_get_focus_inactive(seat, &vis2->node));
1815 }
1816
1817 swap_focus(con1, con2, seat, focus);
1818
1819 if (stored_prev_name) {
1820 free(seat->prev_workspace_name);
1821 seat->prev_workspace_name = stored_prev_name;
1822 }
1823
1824 if (scratch1) {
1825 root_scratchpad_add_container(con2, NULL);
1826 if (!hidden1) {
1827 root_scratchpad_show(con2);
1828 }
1829 }
1830 if (scratch2) {
1831 root_scratchpad_add_container(con1, NULL);
1832 if (!hidden2) {
1833 root_scratchpad_show(con1);
1834 }
1835 }
1836
1837 if (fs1) {
1838 container_set_fullscreen(con2, fs1);
1839 }
1840 if (fs2) {
1841 container_set_fullscreen(con1, fs2);
1842 }
1843}
diff --git a/sway/tree/node.c b/sway/tree/node.c
index bc7e2aa5..7aaf9762 100644
--- a/sway/tree/node.c
+++ b/sway/tree/node.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include "sway/output.h" 1#include "sway/output.h"
3#include "sway/server.h" 2#include "sway/server.h"
4#include "sway/tree/container.h" 3#include "sway/tree/container.h"
@@ -18,13 +17,13 @@ void node_init(struct sway_node *node, enum sway_node_type type, void *thing) {
18const char *node_type_to_str(enum sway_node_type type) { 17const char *node_type_to_str(enum sway_node_type type) {
19 switch (type) { 18 switch (type) {
20 case N_ROOT: 19 case N_ROOT:
21 return "N_ROOT"; 20 return "root";
22 case N_OUTPUT: 21 case N_OUTPUT:
23 return "N_OUTPUT"; 22 return "output";
24 case N_WORKSPACE: 23 case N_WORKSPACE:
25 return "N_WORKSPACE"; 24 return "workspace";
26 case N_CONTAINER: 25 case N_CONTAINER:
27 return "N_CONTAINER"; 26 return "container";
28 } 27 }
29 return ""; 28 return "";
30} 29}
@@ -159,3 +158,32 @@ bool node_has_ancestor(struct sway_node *node, struct sway_node *ancestor) {
159 } 158 }
160 return false; 159 return false;
161} 160}
161
162void scene_node_disown_children(struct wlr_scene_tree *tree) {
163 // this function can be called as part of destruction code that will be invoked
164 // upon an allocation failure. Let's not crash on NULL due to an allocation error.
165 if (!tree) {
166 return;
167 }
168
169 struct wlr_scene_node *child, *tmp_child;
170 wl_list_for_each_safe(child, tmp_child, &tree->children, link) {
171 wlr_scene_node_reparent(child, root->staging);
172 }
173}
174
175struct wlr_scene_tree *alloc_scene_tree(struct wlr_scene_tree *parent,
176 bool *failed) {
177 // fallthrough
178 if (*failed) {
179 return NULL;
180 }
181
182 struct wlr_scene_tree *tree = wlr_scene_tree_create(parent);
183 if (!tree) {
184 sway_log(SWAY_ERROR, "Failed to allocate a scene node");
185 *failed = true;
186 }
187
188 return tree;
189}
diff --git a/sway/tree/output.c b/sway/tree/output.c
index c095dce0..2d11195e 100644
--- a/sway/tree/output.c
+++ b/sway/tree/output.c
@@ -1,14 +1,13 @@
1#define _POSIX_C_SOURCE 200809L
2#include <assert.h> 1#include <assert.h>
3#include <ctype.h> 2#include <ctype.h>
4#include <string.h> 3#include <string.h>
5#include <strings.h> 4#include <strings.h>
6#include <wlr/types/wlr_output_damage.h>
7#include "sway/ipc-server.h" 5#include "sway/ipc-server.h"
8#include "sway/layers.h" 6#include "sway/layers.h"
9#include "sway/output.h" 7#include "sway/output.h"
10#include "sway/tree/arrange.h" 8#include "sway/tree/arrange.h"
11#include "sway/tree/workspace.h" 9#include "sway/tree/workspace.h"
10#include "sway/server.h"
12#include "log.h" 11#include "log.h"
13#include "util.h" 12#include "util.h"
14 13
@@ -56,8 +55,8 @@ static void restore_workspaces(struct sway_output *output) {
56 } 55 }
57 56
58 // Saved workspaces 57 // Saved workspaces
59 while (root->noop_output->workspaces->length) { 58 while (root->fallback_output->workspaces->length) {
60 struct sway_workspace *ws = root->noop_output->workspaces->items[0]; 59 struct sway_workspace *ws = root->fallback_output->workspaces->items[0];
61 workspace_detach(ws); 60 workspace_detach(ws);
62 output_add_workspace(output, ws); 61 output_add_workspace(output, ws);
63 62
@@ -87,26 +86,63 @@ static void restore_workspaces(struct sway_output *output) {
87 output_sort_workspaces(output); 86 output_sort_workspaces(output);
88} 87}
89 88
89static void destroy_scene_layers(struct sway_output *output) {
90 wlr_scene_node_destroy(&output->fullscreen_background->node);
91
92 scene_node_disown_children(output->layers.tiling);
93 scene_node_disown_children(output->layers.fullscreen);
94
95 wlr_scene_node_destroy(&output->layers.shell_background->node);
96 wlr_scene_node_destroy(&output->layers.shell_bottom->node);
97 wlr_scene_node_destroy(&output->layers.tiling->node);
98 wlr_scene_node_destroy(&output->layers.fullscreen->node);
99 wlr_scene_node_destroy(&output->layers.shell_top->node);
100 wlr_scene_node_destroy(&output->layers.shell_overlay->node);
101 wlr_scene_node_destroy(&output->layers.session_lock->node);
102}
103
90struct sway_output *output_create(struct wlr_output *wlr_output) { 104struct sway_output *output_create(struct wlr_output *wlr_output) {
91 struct sway_output *output = calloc(1, sizeof(struct sway_output)); 105 struct sway_output *output = calloc(1, sizeof(struct sway_output));
92 node_init(&output->node, N_OUTPUT, output); 106 node_init(&output->node, N_OUTPUT, output);
107
108 bool failed = false;
109 output->layers.shell_background = alloc_scene_tree(root->staging, &failed);
110 output->layers.shell_bottom = alloc_scene_tree(root->staging, &failed);
111 output->layers.tiling = alloc_scene_tree(root->staging, &failed);
112 output->layers.fullscreen = alloc_scene_tree(root->staging, &failed);
113 output->layers.shell_top = alloc_scene_tree(root->staging, &failed);
114 output->layers.shell_overlay = alloc_scene_tree(root->staging, &failed);
115 output->layers.session_lock = alloc_scene_tree(root->staging, &failed);
116
117 if (!failed) {
118 output->fullscreen_background = wlr_scene_rect_create(
119 output->layers.fullscreen, 0, 0, (float[4]){0.f, 0.f, 0.f, 1.f});
120
121 if (!output->fullscreen_background) {
122 sway_log(SWAY_ERROR, "Unable to allocate a background rect");
123 failed = true;
124 }
125 }
126
127 if (failed) {
128 destroy_scene_layers(output);
129 wlr_scene_output_destroy(output->scene_output);
130 free(output);
131 return NULL;
132 }
133
93 output->wlr_output = wlr_output; 134 output->wlr_output = wlr_output;
94 wlr_output->data = output; 135 wlr_output->data = output;
95 output->detected_subpixel = wlr_output->subpixel; 136 output->detected_subpixel = wlr_output->subpixel;
96 output->scale_filter = SCALE_FILTER_NEAREST; 137 output->scale_filter = SCALE_FILTER_NEAREST;
97 138
98 wl_signal_init(&output->events.destroy); 139 wl_signal_init(&output->events.disable);
99 140
100 wl_list_insert(&root->all_outputs, &output->link); 141 wl_list_insert(&root->all_outputs, &output->link);
101 142
102 output->workspaces = create_list(); 143 output->workspaces = create_list();
103 output->current.workspaces = create_list(); 144 output->current.workspaces = create_list();
104 145
105 size_t len = sizeof(output->layers) / sizeof(output->layers[0]);
106 for (size_t i = 0; i < len; ++i) {
107 wl_list_init(&output->layers[i]);
108 }
109
110 return output; 146 return output;
111} 147}
112 148
@@ -146,7 +182,7 @@ void output_enable(struct sway_output *output) {
146 182
147 input_manager_configure_xcursor(); 183 input_manager_configure_xcursor();
148 184
149 wl_signal_emit(&root->events.new_node, &output->node); 185 wl_signal_emit_mutable(&root->events.new_node, &output->node);
150 186
151 arrange_layers(output); 187 arrange_layers(output);
152 arrange_root(); 188 arrange_root();
@@ -192,7 +228,7 @@ static void output_evacuate(struct sway_output *output) {
192 new_output = fallback_output; 228 new_output = fallback_output;
193 } 229 }
194 if (!new_output) { 230 if (!new_output) {
195 new_output = root->noop_output; 231 new_output = root->fallback_output;
196 } 232 }
197 233
198 struct sway_workspace *new_output_ws = 234 struct sway_workspace *new_output_ws =
@@ -238,20 +274,14 @@ void output_destroy(struct sway_output *output) {
238 "which is still referenced by transactions")) { 274 "which is still referenced by transactions")) {
239 return; 275 return;
240 } 276 }
277
278 destroy_scene_layers(output);
241 list_free(output->workspaces); 279 list_free(output->workspaces);
242 list_free(output->current.workspaces); 280 list_free(output->current.workspaces);
243 wl_event_source_remove(output->repaint_timer); 281 wl_event_source_remove(output->repaint_timer);
244 free(output); 282 free(output);
245} 283}
246 284
247static void untrack_output(struct sway_container *con, void *data) {
248 struct sway_output *output = data;
249 int index = list_find(con->outputs, output);
250 if (index != -1) {
251 list_del(con->outputs, index);
252 }
253}
254
255void output_disable(struct sway_output *output) { 285void output_disable(struct sway_output *output) {
256 if (!sway_assert(output->enabled, "Expected an enabled output")) { 286 if (!sway_assert(output->enabled, "Expected an enabled output")) {
257 return; 287 return;
@@ -262,23 +292,20 @@ void output_disable(struct sway_output *output) {
262 } 292 }
263 293
264 sway_log(SWAY_DEBUG, "Disabling output '%s'", output->wlr_output->name); 294 sway_log(SWAY_DEBUG, "Disabling output '%s'", output->wlr_output->name);
265 wl_signal_emit(&output->events.destroy, output); 295 wl_signal_emit_mutable(&output->events.disable, output);
266 296
267 output_evacuate(output); 297 output_evacuate(output);
268 298
269 root_for_each_container(untrack_output, output);
270
271 list_del(root->outputs, index); 299 list_del(root->outputs, index);
272 300
273 output->enabled = false; 301 output->enabled = false;
274 output->current_mode = NULL;
275 302
276 arrange_root(); 303 arrange_root();
277 304
278 // Reconfigure all devices, since devices with map_to_output directives for 305 // Reconfigure all devices, since devices with map_to_output directives for
279 // an output that goes offline should stop sending events as long as the 306 // an output that goes offline should stop sending events as long as the
280 // output remains offline. 307 // output remains offline.
281 input_manager_configure_all_inputs(); 308 input_manager_configure_all_input_mappings();
282} 309}
283 310
284void output_begin_destroy(struct sway_output *output) { 311void output_begin_destroy(struct sway_output *output) {
@@ -286,13 +313,10 @@ void output_begin_destroy(struct sway_output *output) {
286 return; 313 return;
287 } 314 }
288 sway_log(SWAY_DEBUG, "Destroying output '%s'", output->wlr_output->name); 315 sway_log(SWAY_DEBUG, "Destroying output '%s'", output->wlr_output->name);
316 wl_signal_emit_mutable(&output->node.events.destroy, &output->node);
289 317
290 output->node.destroying = true; 318 output->node.destroying = true;
291 node_set_dirty(&output->node); 319 node_set_dirty(&output->node);
292
293 wl_list_remove(&output->link);
294 output->wlr_output->data = NULL;
295 output->wlr_output = NULL;
296} 320}
297 321
298struct sway_output *output_from_wlr_output(struct wlr_output *output) { 322struct sway_output *output_from_wlr_output(struct wlr_output *output) {
@@ -304,10 +328,10 @@ struct sway_output *output_get_in_direction(struct sway_output *reference,
304 if (!sway_assert(direction, "got invalid direction: %d", direction)) { 328 if (!sway_assert(direction, "got invalid direction: %d", direction)) {
305 return NULL; 329 return NULL;
306 } 330 }
307 struct wlr_box *output_box = 331 struct wlr_box output_box;
308 wlr_output_layout_get_box(root->output_layout, reference->wlr_output); 332 wlr_output_layout_get_box(root->output_layout, reference->wlr_output, &output_box);
309 int lx = output_box->x + output_box->width / 2; 333 int lx = output_box.x + output_box.width / 2;
310 int ly = output_box->y + output_box->height / 2; 334 int ly = output_box.y + output_box.height / 2;
311 struct wlr_output *wlr_adjacent = wlr_output_layout_adjacent_output( 335 struct wlr_output *wlr_adjacent = wlr_output_layout_adjacent_output(
312 root->output_layout, direction, reference->wlr_output, lx, ly); 336 root->output_layout, direction, reference->wlr_output, lx, ly);
313 if (!wlr_adjacent) { 337 if (!wlr_adjacent) {
@@ -393,6 +417,33 @@ void output_get_box(struct sway_output *output, struct wlr_box *box) {
393 box->height = output->height; 417 box->height = output->height;
394} 418}
395 419
420static void handle_destroy_non_desktop(struct wl_listener *listener, void *data) {
421 struct sway_output_non_desktop *output =
422 wl_container_of(listener, output, destroy);
423
424 sway_log(SWAY_DEBUG, "Destroying non-desktop output '%s'", output->wlr_output->name);
425
426 int index = list_find(root->non_desktop_outputs, output);
427 list_del(root->non_desktop_outputs, index);
428
429 wl_list_remove(&output->destroy.link);
430
431 free(output);
432}
433
434struct sway_output_non_desktop *output_non_desktop_create(
435 struct wlr_output *wlr_output) {
436 struct sway_output_non_desktop *output =
437 calloc(1, sizeof(struct sway_output_non_desktop));
438
439 output->wlr_output = wlr_output;
440
441 wl_signal_add(&wlr_output->events.destroy, &output->destroy);
442 output->destroy.notify = handle_destroy_non_desktop;
443
444 return output;
445}
446
396enum sway_container_layout output_get_default_layout( 447enum sway_container_layout output_get_default_layout(
397 struct sway_output *output) { 448 struct sway_output *output) {
398 if (config->default_orientation != L_NONE) { 449 if (config->default_orientation != L_NONE) {
diff --git a/sway/tree/root.c b/sway/tree/root.c
index dd4d8e33..20fcfa59 100644
--- a/sway/tree/root.c
+++ b/sway/tree/root.c
@@ -1,12 +1,14 @@
1#define _POSIX_C_SOURCE 200809L
2#include <stdbool.h> 1#include <stdbool.h>
3#include <stdlib.h> 2#include <stdlib.h>
4#include <string.h> 3#include <string.h>
5#include <wlr/types/wlr_output_layout.h> 4#include <wlr/types/wlr_output_layout.h>
5#include <wlr/types/wlr_scene.h>
6#include <wlr/util/transform.h>
6#include "sway/desktop/transaction.h" 7#include "sway/desktop/transaction.h"
7#include "sway/input/seat.h" 8#include "sway/input/seat.h"
8#include "sway/ipc-server.h" 9#include "sway/ipc-server.h"
9#include "sway/output.h" 10#include "sway/output.h"
11#include "sway/scene_descriptor.h"
10#include "sway/tree/arrange.h" 12#include "sway/tree/arrange.h"
11#include "sway/tree/container.h" 13#include "sway/tree/container.h"
12#include "sway/tree/root.h" 14#include "sway/tree/root.h"
@@ -23,21 +25,60 @@ static void output_layout_handle_change(struct wl_listener *listener,
23 transaction_commit_dirty(); 25 transaction_commit_dirty();
24} 26}
25 27
26struct sway_root *root_create(void) { 28struct sway_root *root_create(struct wl_display *wl_display) {
27 struct sway_root *root = calloc(1, sizeof(struct sway_root)); 29 struct sway_root *root = calloc(1, sizeof(struct sway_root));
28 if (!root) { 30 if (!root) {
29 sway_log(SWAY_ERROR, "Unable to allocate sway_root"); 31 sway_log(SWAY_ERROR, "Unable to allocate sway_root");
30 return NULL; 32 return NULL;
31 } 33 }
34
35 struct wlr_scene *root_scene = wlr_scene_create();
36 if (!root_scene) {
37 sway_log(SWAY_ERROR, "Unable to allocate root scene node");
38 free(root);
39 return NULL;
40 }
41
32 node_init(&root->node, N_ROOT, root); 42 node_init(&root->node, N_ROOT, root);
33 root->output_layout = wlr_output_layout_create(); 43 root->root_scene = root_scene;
34 wl_list_init(&root->all_outputs); 44
35#if HAVE_XWAYLAND 45 bool failed = false;
36 wl_list_init(&root->xwayland_unmanaged); 46 root->staging = alloc_scene_tree(&root_scene->tree, &failed);
47 root->layer_tree = alloc_scene_tree(&root_scene->tree, &failed);
48
49 root->layers.shell_background = alloc_scene_tree(root->layer_tree, &failed);
50 root->layers.shell_bottom = alloc_scene_tree(root->layer_tree, &failed);
51 root->layers.tiling = alloc_scene_tree(root->layer_tree, &failed);
52 root->layers.floating = alloc_scene_tree(root->layer_tree, &failed);
53 root->layers.shell_top = alloc_scene_tree(root->layer_tree, &failed);
54 root->layers.fullscreen = alloc_scene_tree(root->layer_tree, &failed);
55 root->layers.fullscreen_global = alloc_scene_tree(root->layer_tree, &failed);
56#if WLR_HAS_XWAYLAND
57 root->layers.unmanaged = alloc_scene_tree(root->layer_tree, &failed);
37#endif 58#endif
38 wl_list_init(&root->drag_icons); 59 root->layers.shell_overlay = alloc_scene_tree(root->layer_tree, &failed);
60 root->layers.popup = alloc_scene_tree(root->layer_tree, &failed);
61 root->layers.seat = alloc_scene_tree(root->layer_tree, &failed);
62 root->layers.session_lock = alloc_scene_tree(root->layer_tree, &failed);
63
64 if (!failed && !scene_descriptor_assign(&root->layers.seat->node,
65 SWAY_SCENE_DESC_NON_INTERACTIVE, (void *)1)) {
66 failed = true;
67 }
68
69 if (failed) {
70 wlr_scene_node_destroy(&root_scene->tree.node);
71 free(root);
72 return NULL;
73 }
74
75 wlr_scene_node_set_enabled(&root->staging->node, false);
76
77 root->output_layout = wlr_output_layout_create(wl_display);
78 wl_list_init(&root->all_outputs);
39 wl_signal_init(&root->events.new_node); 79 wl_signal_init(&root->events.new_node);
40 root->outputs = create_list(); 80 root->outputs = create_list();
81 root->non_desktop_outputs = create_list();
41 root->scratchpad = create_list(); 82 root->scratchpad = create_list();
42 83
43 root->output_layout_change.notify = output_layout_handle_change; 84 root->output_layout_change.notify = output_layout_handle_change;
@@ -49,11 +90,22 @@ struct sway_root *root_create(void) {
49void root_destroy(struct sway_root *root) { 90void root_destroy(struct sway_root *root) {
50 wl_list_remove(&root->output_layout_change.link); 91 wl_list_remove(&root->output_layout_change.link);
51 list_free(root->scratchpad); 92 list_free(root->scratchpad);
93 list_free(root->non_desktop_outputs);
52 list_free(root->outputs); 94 list_free(root->outputs);
53 wlr_output_layout_destroy(root->output_layout); 95 wlr_scene_node_destroy(&root->root_scene->tree.node);
54 free(root); 96 free(root);
55} 97}
56 98
99static void set_container_transform(struct sway_workspace *ws,
100 struct sway_container *con) {
101 struct sway_output *output = ws->output;
102 struct wlr_box box = {0};
103 if (output) {
104 output_get_box(output, &box);
105 }
106 con->transform = box;
107}
108
57void root_scratchpad_add_container(struct sway_container *con, struct sway_workspace *ws) { 109void root_scratchpad_add_container(struct sway_container *con, struct sway_workspace *ws) {
58 if (!sway_assert(!con->scratchpad, "Container is already in scratchpad")) { 110 if (!sway_assert(!con->scratchpad, "Container is already in scratchpad")) {
59 return; 111 return;
@@ -62,6 +114,8 @@ void root_scratchpad_add_container(struct sway_container *con, struct sway_works
62 struct sway_container *parent = con->pending.parent; 114 struct sway_container *parent = con->pending.parent;
63 struct sway_workspace *workspace = con->pending.workspace; 115 struct sway_workspace *workspace = con->pending.workspace;
64 116
117 set_container_transform(workspace, con);
118
65 // Clear the fullscreen mode when sending to the scratchpad 119 // Clear the fullscreen mode when sending to the scratchpad
66 if (con->pending.fullscreen_mode != FULLSCREEN_NONE) { 120 if (con->pending.fullscreen_mode != FULLSCREEN_NONE) {
67 container_fullscreen_disable(con); 121 container_fullscreen_disable(con);
@@ -131,7 +185,10 @@ void root_scratchpad_show(struct sway_container *con) {
131 // Show the container 185 // Show the container
132 if (old_ws) { 186 if (old_ws) {
133 container_detach(con); 187 container_detach(con);
134 workspace_consider_destroy(old_ws); 188 // Make sure the last inactive container on the old workspace is above
189 // the workspace itself in the focus stack.
190 struct sway_node *node = seat_get_focus_inactive(seat, &old_ws->node);
191 seat_set_raw_focus(seat, node);
135 } else { 192 } else {
136 // Act on the ancestor of scratchpad hidden split containers 193 // Act on the ancestor of scratchpad hidden split containers
137 while (con->pending.parent) { 194 while (con->pending.parent) {
@@ -140,18 +197,18 @@ void root_scratchpad_show(struct sway_container *con) {
140 } 197 }
141 workspace_add_floating(new_ws, con); 198 workspace_add_floating(new_ws, con);
142 199
143 // Make sure the container's center point overlaps this workspace 200 if (new_ws->output) {
144 double center_lx = con->pending.x + con->pending.width / 2; 201 struct wlr_box output_box;
145 double center_ly = con->pending.y + con->pending.height / 2; 202 output_get_box(new_ws->output, &output_box);
146 203 floating_fix_coordinates(con, &con->transform, &output_box);
147 struct wlr_box workspace_box;
148 workspace_get_box(new_ws, &workspace_box);
149 if (!wlr_box_contains_point(&workspace_box, center_lx, center_ly)) {
150 container_floating_resize_and_center(con);
151 } 204 }
205 set_container_transform(new_ws, con);
152 206
153 arrange_workspace(new_ws); 207 arrange_workspace(new_ws);
154 seat_set_focus(seat, seat_get_focus_inactive(seat, &con->node)); 208 seat_set_focus(seat, seat_get_focus_inactive(seat, &con->node));
209 if (old_ws) {
210 workspace_consider_destroy(old_ws);
211 }
155} 212}
156 213
157static void disable_fullscreen(struct sway_container *con, void *data) { 214static void disable_fullscreen(struct sway_container *con, void *data) {
@@ -171,6 +228,8 @@ void root_scratchpad_hide(struct sway_container *con) {
171 return; 228 return;
172 } 229 }
173 230
231 set_container_transform(con->pending.workspace, con);
232
174 disable_fullscreen(con, NULL); 233 disable_fullscreen(con, NULL);
175 container_for_each_child(con, disable_fullscreen, NULL); 234 container_for_each_child(con, disable_fullscreen, NULL);
176 container_detach(con); 235 container_detach(con);
@@ -183,172 +242,6 @@ void root_scratchpad_hide(struct sway_container *con) {
183 ipc_event_window(con, "move"); 242 ipc_event_window(con, "move");
184} 243}
185 244
186struct pid_workspace {
187 pid_t pid;
188 char *workspace;
189 struct timespec time_added;
190
191 struct sway_output *output;
192 struct wl_listener output_destroy;
193
194 struct wl_list link;
195};
196
197static struct wl_list pid_workspaces;
198
199/**
200 * Get the pid of a parent process given the pid of a child process.
201 *
202 * Returns the parent pid or NULL if the parent pid cannot be determined.
203 */
204static pid_t get_parent_pid(pid_t child) {
205 pid_t parent = -1;
206 char file_name[100];
207 char *buffer = NULL;
208 const char *sep = " ";
209 FILE *stat = NULL;
210 size_t buf_size = 0;
211
212 sprintf(file_name, "/proc/%d/stat", child);
213
214 if ((stat = fopen(file_name, "r"))) {
215 if (getline(&buffer, &buf_size, stat) != -1) {
216 strtok(buffer, sep); // pid
217 strtok(NULL, sep); // executable name
218 strtok(NULL, sep); // state
219 char *token = strtok(NULL, sep); // parent pid
220 parent = strtol(token, NULL, 10);
221 }
222 free(buffer);
223 fclose(stat);
224 }
225
226 if (parent) {
227 return (parent == child) ? -1 : parent;
228 }
229
230 return -1;
231}
232
233static void pid_workspace_destroy(struct pid_workspace *pw) {
234 wl_list_remove(&pw->output_destroy.link);
235 wl_list_remove(&pw->link);
236 free(pw->workspace);
237 free(pw);
238}
239
240struct sway_workspace *root_workspace_for_pid(pid_t pid) {
241 if (!pid_workspaces.prev && !pid_workspaces.next) {
242 wl_list_init(&pid_workspaces);
243 return NULL;
244 }
245
246 struct sway_workspace *ws = NULL;
247 struct pid_workspace *pw = NULL;
248
249 sway_log(SWAY_DEBUG, "Looking up workspace for pid %d", pid);
250
251 do {
252 struct pid_workspace *_pw = NULL;
253 wl_list_for_each(_pw, &pid_workspaces, link) {
254 if (pid == _pw->pid) {
255 pw = _pw;
256 sway_log(SWAY_DEBUG,
257 "found pid_workspace for pid %d, workspace %s",
258 pid, pw->workspace);
259 goto found;
260 }
261 }
262 pid = get_parent_pid(pid);
263 } while (pid > 1);
264found:
265
266 if (pw && pw->workspace) {
267 ws = workspace_by_name(pw->workspace);
268
269 if (!ws) {
270 sway_log(SWAY_DEBUG,
271 "Creating workspace %s for pid %d because it disappeared",
272 pw->workspace, pid);
273
274 struct sway_output *output = pw->output;
275 if (pw->output && !pw->output->enabled) {
276 sway_log(SWAY_DEBUG,
277 "Workspace output %s is disabled, trying another one",
278 pw->output->wlr_output->name);
279 output = NULL;
280 }
281
282 ws = workspace_create(output, pw->workspace);
283 }
284
285 pid_workspace_destroy(pw);
286 }
287
288 return ws;
289}
290
291static void pw_handle_output_destroy(struct wl_listener *listener, void *data) {
292 struct pid_workspace *pw = wl_container_of(listener, pw, output_destroy);
293 pw->output = NULL;
294 wl_list_remove(&pw->output_destroy.link);
295 wl_list_init(&pw->output_destroy.link);
296}
297
298void root_record_workspace_pid(pid_t pid) {
299 sway_log(SWAY_DEBUG, "Recording workspace for process %d", pid);
300 if (!pid_workspaces.prev && !pid_workspaces.next) {
301 wl_list_init(&pid_workspaces);
302 }
303
304 struct sway_seat *seat = input_manager_current_seat();
305 struct sway_workspace *ws = seat_get_focused_workspace(seat);
306 if (!ws) {
307 sway_log(SWAY_DEBUG, "Bailing out, no workspace");
308 return;
309 }
310 struct sway_output *output = ws->output;
311 if (!output) {
312 sway_log(SWAY_DEBUG, "Bailing out, no output");
313 return;
314 }
315
316 struct timespec now;
317 clock_gettime(CLOCK_MONOTONIC, &now);
318
319 // Remove expired entries
320 static const int timeout = 60;
321 struct pid_workspace *old, *_old;
322 wl_list_for_each_safe(old, _old, &pid_workspaces, link) {
323 if (now.tv_sec - old->time_added.tv_sec >= timeout) {
324 pid_workspace_destroy(old);
325 }
326 }
327
328 struct pid_workspace *pw = calloc(1, sizeof(struct pid_workspace));
329 pw->workspace = strdup(ws->name);
330 pw->output = output;
331 pw->pid = pid;
332 memcpy(&pw->time_added, &now, sizeof(struct timespec));
333 pw->output_destroy.notify = pw_handle_output_destroy;
334 wl_signal_add(&output->wlr_output->events.destroy, &pw->output_destroy);
335 wl_list_insert(&pid_workspaces, &pw->link);
336}
337
338void root_remove_workspace_pid(pid_t pid) {
339 if (!pid_workspaces.prev || !pid_workspaces.next) {
340 return;
341 }
342
343 struct pid_workspace *pw, *tmp;
344 wl_list_for_each_safe(pw, tmp, &pid_workspaces, link) {
345 if (pid == pw->pid) {
346 pid_workspace_destroy(pw);
347 return;
348 }
349 }
350}
351
352void root_for_each_workspace(void (*f)(struct sway_workspace *ws, void *data), 245void root_for_each_workspace(void (*f)(struct sway_workspace *ws, void *data),
353 void *data) { 246 void *data) {
354 for (int i = 0; i < root->outputs->length; ++i) { 247 for (int i = 0; i < root->outputs->length; ++i) {
@@ -374,8 +267,8 @@ void root_for_each_container(void (*f)(struct sway_container *con, void *data),
374 } 267 }
375 268
376 // Saved workspaces 269 // Saved workspaces
377 for (int i = 0; i < root->noop_output->workspaces->length; ++i) { 270 for (int i = 0; i < root->fallback_output->workspaces->length; ++i) {
378 struct sway_workspace *ws = root->noop_output->workspaces->items[i]; 271 struct sway_workspace *ws = root->fallback_output->workspaces->items[i];
379 workspace_for_each_container(ws, f, data); 272 workspace_for_each_container(ws, f, data);
380 } 273 }
381} 274}
@@ -427,8 +320,8 @@ struct sway_container *root_find_container(
427 } 320 }
428 321
429 // Saved workspaces 322 // Saved workspaces
430 for (int i = 0; i < root->noop_output->workspaces->length; ++i) { 323 for (int i = 0; i < root->fallback_output->workspaces->length; ++i) {
431 struct sway_workspace *ws = root->noop_output->workspaces->items[i]; 324 struct sway_workspace *ws = root->fallback_output->workspaces->items[i];
432 if ((result = workspace_find_container(ws, test, data))) { 325 if ((result = workspace_find_container(ws, test, data))) {
433 return result; 326 return result;
434 } 327 }
@@ -443,17 +336,3 @@ void root_get_box(struct sway_root *root, struct wlr_box *box) {
443 box->width = root->width; 336 box->width = root->width;
444 box->height = root->height; 337 box->height = root->height;
445} 338}
446
447void root_rename_pid_workspaces(const char *old_name, const char *new_name) {
448 if (!pid_workspaces.prev && !pid_workspaces.next) {
449 wl_list_init(&pid_workspaces);
450 }
451
452 struct pid_workspace *pw = NULL;
453 wl_list_for_each(pw, &pid_workspaces, link) {
454 if (strcmp(pw->workspace, old_name) == 0) {
455 free(pw->workspace);
456 pw->workspace = strdup(new_name);
457 }
458 }
459}
diff --git a/sway/tree/view.c b/sway/tree/view.c
index fcdd06f7..1c1c8ee8 100644
--- a/sway/tree/view.c
+++ b/sway/tree/view.c
@@ -1,28 +1,32 @@
1#define _POSIX_C_SOURCE 200809L
2#include <stdlib.h> 1#include <stdlib.h>
3#include <strings.h> 2#include <strings.h>
4#include <wayland-server-core.h> 3#include <wayland-server-core.h>
4#include <wlr/config.h>
5#include <wlr/render/wlr_renderer.h> 5#include <wlr/render/wlr_renderer.h>
6#include <wlr/types/wlr_buffer.h> 6#include <wlr/types/wlr_buffer.h>
7#include <wlr/types/wlr_ext_foreign_toplevel_list_v1.h>
8#include <wlr/types/wlr_foreign_toplevel_management_v1.h>
7#include <wlr/types/wlr_output_layout.h> 9#include <wlr/types/wlr_output_layout.h>
8#include <wlr/types/wlr_server_decoration.h> 10#include <wlr/types/wlr_server_decoration.h>
11#include <wlr/types/wlr_subcompositor.h>
9#include <wlr/types/wlr_xdg_decoration_v1.h> 12#include <wlr/types/wlr_xdg_decoration_v1.h>
10#include "config.h" 13#if WLR_HAS_XWAYLAND
11#if HAVE_XWAYLAND
12#include <wlr/xwayland.h> 14#include <wlr/xwayland.h>
13#endif 15#endif
14#include "list.h" 16#include "list.h"
15#include "log.h" 17#include "log.h"
16#include "sway/criteria.h" 18#include "sway/criteria.h"
17#include "sway/commands.h" 19#include "sway/commands.h"
18#include "sway/desktop.h"
19#include "sway/desktop/transaction.h" 20#include "sway/desktop/transaction.h"
20#include "sway/desktop/idle_inhibit_v1.h" 21#include "sway/desktop/idle_inhibit_v1.h"
22#include "sway/desktop/launcher.h"
21#include "sway/input/cursor.h" 23#include "sway/input/cursor.h"
22#include "sway/ipc-server.h" 24#include "sway/ipc-server.h"
23#include "sway/output.h" 25#include "sway/output.h"
24#include "sway/input/seat.h" 26#include "sway/input/seat.h"
27#include "sway/scene_descriptor.h"
25#include "sway/server.h" 28#include "sway/server.h"
29#include "sway/sway_text_node.h"
26#include "sway/tree/arrange.h" 30#include "sway/tree/arrange.h"
27#include "sway/tree/container.h" 31#include "sway/tree/container.h"
28#include "sway/tree/view.h" 32#include "sway/tree/view.h"
@@ -32,15 +36,29 @@
32#include "pango.h" 36#include "pango.h"
33#include "stringop.h" 37#include "stringop.h"
34 38
35void view_init(struct sway_view *view, enum sway_view_type type, 39bool view_init(struct sway_view *view, enum sway_view_type type,
36 const struct sway_view_impl *impl) { 40 const struct sway_view_impl *impl) {
41 bool failed = false;
42 view->scene_tree = alloc_scene_tree(root->staging, &failed);
43 view->content_tree = alloc_scene_tree(view->scene_tree, &failed);
44
45 if (!failed && !scene_descriptor_assign(&view->scene_tree->node,
46 SWAY_SCENE_DESC_VIEW, view)) {
47 failed = true;
48 }
49
50 if (failed) {
51 wlr_scene_node_destroy(&view->scene_tree->node);
52 return false;
53 }
54
37 view->type = type; 55 view->type = type;
38 view->impl = impl; 56 view->impl = impl;
39 view->executed_criteria = create_list(); 57 view->executed_criteria = create_list();
40 wl_list_init(&view->saved_buffers);
41 view->allow_request_urgent = true; 58 view->allow_request_urgent = true;
42 view->shortcuts_inhibit = SHORTCUTS_INHIBIT_DEFAULT; 59 view->shortcuts_inhibit = SHORTCUTS_INHIBIT_DEFAULT;
43 wl_signal_init(&view->events.unmap); 60 wl_signal_init(&view->events.unmap);
61 return true;
44} 62}
45 63
46void view_destroy(struct sway_view *view) { 64void view_destroy(struct sway_view *view) {
@@ -57,11 +75,10 @@ void view_destroy(struct sway_view *view) {
57 return; 75 return;
58 } 76 }
59 wl_list_remove(&view->events.unmap.listener_list); 77 wl_list_remove(&view->events.unmap.listener_list);
60 if (!wl_list_empty(&view->saved_buffers)) {
61 view_remove_saved_buffer(view);
62 }
63 list_free(view->executed_criteria); 78 list_free(view->executed_criteria);
64 79
80 view_assign_ctx(view, NULL);
81 wlr_scene_node_destroy(&view->scene_tree->node);
65 free(view->title_format); 82 free(view->title_format);
66 83
67 if (view->impl->destroy) { 84 if (view->impl->destroy) {
@@ -109,7 +126,7 @@ const char *view_get_instance(struct sway_view *view) {
109 } 126 }
110 return NULL; 127 return NULL;
111} 128}
112#if HAVE_XWAYLAND 129#if WLR_HAS_XWAYLAND
113uint32_t view_get_x11_window_id(struct sway_view *view) { 130uint32_t view_get_x11_window_id(struct sway_view *view) {
114 if (view->impl->get_int_prop) { 131 if (view->impl->get_int_prop) {
115 return view->impl->get_int_prop(view, VIEW_PROP_X11_WINDOW_ID); 132 return view->impl->get_int_prop(view, VIEW_PROP_X11_WINDOW_ID);
@@ -142,7 +159,7 @@ const char *view_get_shell(struct sway_view *view) {
142 switch(view->type) { 159 switch(view->type) {
143 case SWAY_VIEW_XDG_SHELL: 160 case SWAY_VIEW_XDG_SHELL:
144 return "xdg_shell"; 161 return "xdg_shell";
145#if HAVE_XWAYLAND 162#if WLR_HAS_XWAYLAND
146 case SWAY_VIEW_XWAYLAND: 163 case SWAY_VIEW_XWAYLAND:
147 return "xwayland"; 164 return "xwayland";
148#endif 165#endif
@@ -362,17 +379,17 @@ void view_set_activated(struct sway_view *view, bool activated) {
362 } 379 }
363} 380}
364 381
365void view_request_activate(struct sway_view *view) { 382void view_request_activate(struct sway_view *view, struct sway_seat *seat) {
366 struct sway_workspace *ws = view->container->pending.workspace; 383 struct sway_workspace *ws = view->container->pending.workspace;
367 if (!ws) { // hidden scratchpad container 384 if (!seat) {
368 return; 385 seat = input_manager_current_seat();
369 } 386 }
370 struct sway_seat *seat = input_manager_current_seat();
371 387
372 switch (config->focus_on_window_activation) { 388 switch (config->focus_on_window_activation) {
373 case FOWA_SMART: 389 case FOWA_SMART:
374 if (workspace_is_visible(ws)) { 390 if (ws && workspace_is_visible(ws)) {
375 seat_set_focus_container(seat, view->container); 391 seat_set_focus_container(seat, view->container);
392 container_raise_floating(view->container);
376 } else { 393 } else {
377 view_set_urgent(view, true); 394 view_set_urgent(view, true);
378 } 395 }
@@ -381,11 +398,23 @@ void view_request_activate(struct sway_view *view) {
381 view_set_urgent(view, true); 398 view_set_urgent(view, true);
382 break; 399 break;
383 case FOWA_FOCUS: 400 case FOWA_FOCUS:
384 seat_set_focus_container(seat, view->container); 401 if (container_is_scratchpad_hidden_or_child(view->container)) {
402 root_scratchpad_show(view->container);
403 } else {
404 seat_set_focus_container(seat, view->container);
405 container_raise_floating(view->container);
406 }
385 break; 407 break;
386 case FOWA_NONE: 408 case FOWA_NONE:
387 break; 409 break;
388 } 410 }
411 transaction_commit_dirty();
412}
413
414void view_request_urgent(struct sway_view *view) {
415 if (config->focus_on_window_activation != FOWA_NONE) {
416 view_set_urgent(view, true);
417 }
389} 418}
390 419
391void view_set_csd_from_server(struct sway_view *view, bool enabled) { 420void view_set_csd_from_server(struct sway_view *view, bool enabled) {
@@ -432,52 +461,6 @@ void view_close_popups(struct sway_view *view) {
432 } 461 }
433} 462}
434 463
435void view_damage_from(struct sway_view *view) {
436 for (int i = 0; i < root->outputs->length; ++i) {
437 struct sway_output *output = root->outputs->items[i];
438 output_damage_from_view(output, view);
439 }
440}
441
442void view_for_each_surface(struct sway_view *view,
443 wlr_surface_iterator_func_t iterator, void *user_data) {
444 if (!view->surface) {
445 return;
446 }
447 if (view->impl->for_each_surface) {
448 view->impl->for_each_surface(view, iterator, user_data);
449 } else {
450 wlr_surface_for_each_surface(view->surface, iterator, user_data);
451 }
452}
453
454void view_for_each_popup_surface(struct sway_view *view,
455 wlr_surface_iterator_func_t iterator, void *user_data) {
456 if (!view->surface) {
457 return;
458 }
459 if (view->impl->for_each_popup_surface) {
460 view->impl->for_each_popup_surface(view, iterator, user_data);
461 }
462}
463
464static void view_subsurface_create(struct sway_view *view,
465 struct wlr_subsurface *subsurface);
466
467static void view_init_subsurfaces(struct sway_view *view,
468 struct wlr_surface *surface);
469
470static void view_child_init_subsurfaces(struct sway_view_child *view_child,
471 struct wlr_surface *surface);
472
473static void view_handle_surface_new_subsurface(struct wl_listener *listener,
474 void *data) {
475 struct sway_view *view =
476 wl_container_of(listener, view, surface_new_subsurface);
477 struct wlr_subsurface *subsurface = data;
478 view_subsurface_create(view, subsurface);
479}
480
481static bool view_has_executed_criteria(struct sway_view *view, 464static bool view_has_executed_criteria(struct sway_view *view,
482 struct criteria *criteria) { 465 struct criteria *criteria) {
483 for (int i = 0; i < view->executed_criteria->length; ++i) { 466 for (int i = 0; i < view->executed_criteria->length; ++i) {
@@ -516,10 +499,10 @@ void view_execute_criteria(struct sway_view *view) {
516static void view_populate_pid(struct sway_view *view) { 499static void view_populate_pid(struct sway_view *view) {
517 pid_t pid; 500 pid_t pid;
518 switch (view->type) { 501 switch (view->type) {
519#if HAVE_XWAYLAND 502#if WLR_HAS_XWAYLAND
520 case SWAY_VIEW_XWAYLAND:; 503 case SWAY_VIEW_XWAYLAND:;
521 struct wlr_xwayland_surface *surf = 504 struct wlr_xwayland_surface *surf =
522 wlr_xwayland_surface_from_wlr_surface(view->surface); 505 wlr_xwayland_surface_try_from_wlr_surface(view->surface);
523 pid = surf->pid; 506 pid = surf->pid;
524 break; 507 break;
525#endif 508#endif
@@ -532,6 +515,20 @@ static void view_populate_pid(struct sway_view *view) {
532 view->pid = pid; 515 view->pid = pid;
533} 516}
534 517
518void view_assign_ctx(struct sway_view *view, struct launcher_ctx *ctx) {
519 if (view->ctx) {
520 // This ctx has been replaced
521 launcher_ctx_destroy(view->ctx);
522 view->ctx = NULL;
523 }
524 if (ctx == NULL) {
525 return;
526 }
527 launcher_ctx_consume(ctx);
528
529 view->ctx = ctx;
530}
531
535static struct sway_workspace *select_workspace(struct sway_view *view) { 532static struct sway_workspace *select_workspace(struct sway_view *view) {
536 struct sway_seat *seat = input_manager_current_seat(); 533 struct sway_seat *seat = input_manager_current_seat();
537 534
@@ -567,13 +564,14 @@ static struct sway_workspace *select_workspace(struct sway_view *view) {
567 } 564 }
568 list_free(criterias); 565 list_free(criterias);
569 if (ws) { 566 if (ws) {
570 root_remove_workspace_pid(view->pid); 567 view_assign_ctx(view, NULL);
571 return ws; 568 return ws;
572 } 569 }
573 570
574 // Check if there's a PID mapping 571 // Check if there's a PID mapping
575 ws = root_workspace_for_pid(view->pid); 572 ws = view->ctx ? launcher_ctx_get_workspace(view->ctx) : NULL;
576 if (ws) { 573 if (ws) {
574 view_assign_ctx(view, NULL);
577 return ws; 575 return ws;
578 } 576 }
579 577
@@ -591,6 +589,14 @@ static struct sway_workspace *select_workspace(struct sway_view *view) {
591 return NULL; 589 return NULL;
592} 590}
593 591
592static void update_ext_foreign_toplevel(struct sway_view *view) {
593 struct wlr_ext_foreign_toplevel_handle_v1_state toplevel_state = {
594 .app_id = view_get_app_id(view),
595 .title = view_get_title(view),
596 };
597 wlr_ext_foreign_toplevel_handle_v1_update_state(view->ext_foreign_toplevel, &toplevel_state);
598}
599
594static bool should_focus(struct sway_view *view) { 600static bool should_focus(struct sway_view *view) {
595 struct sway_seat *seat = input_manager_current_seat(); 601 struct sway_seat *seat = input_manager_current_seat();
596 struct sway_container *prev_con = seat_get_focused_container(seat); 602 struct sway_container *prev_con = seat_get_focused_container(seat);
@@ -716,6 +722,13 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface,
716 view_populate_pid(view); 722 view_populate_pid(view);
717 view->container = container_create(view); 723 view->container = container_create(view);
718 724
725 if (view->ctx == NULL) {
726 struct launcher_ctx *ctx = launcher_ctx_find_pid(view->pid);
727 if (ctx != NULL) {
728 view_assign_ctx(view, ctx);
729 }
730 }
731
719 // If there is a request to be opened fullscreen on a specific output, try 732 // If there is a request to be opened fullscreen on a specific output, try
720 // to honor that request. Otherwise, fallback to assigns, pid mappings, 733 // to honor that request. Otherwise, fallback to assigns, pid mappings,
721 // focused workspace, etc 734 // focused workspace, etc
@@ -729,10 +742,36 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface,
729 } 742 }
730 743
731 struct sway_seat *seat = input_manager_current_seat(); 744 struct sway_seat *seat = input_manager_current_seat();
732 struct sway_node *node = ws ? seat_get_focus_inactive(seat, &ws->node) 745 struct sway_node *node =
733 : seat_get_focus_inactive(seat, &root->node); 746 seat_get_focus_inactive(seat, ws ? &ws->node : &root->node);
734 struct sway_container *target_sibling = node->type == N_CONTAINER ? 747 struct sway_container *target_sibling = NULL;
735 node->sway_container : NULL; 748 if (node && node->type == N_CONTAINER) {
749 if (container_is_floating(node->sway_container)) {
750 // If we're about to launch the view into the floating container, then
751 // launch it as a tiled view instead.
752 if (ws) {
753 target_sibling = seat_get_focus_inactive_tiling(seat, ws);
754 if (target_sibling) {
755 struct sway_container *con =
756 seat_get_focus_inactive_view(seat, &target_sibling->node);
757 if (con) {
758 target_sibling = con;
759 }
760 }
761 } else {
762 ws = seat_get_last_known_workspace(seat);
763 }
764 } else {
765 target_sibling = node->sway_container;
766 }
767 }
768
769 struct wlr_ext_foreign_toplevel_handle_v1_state foreign_toplevel_state = {
770 .app_id = view_get_app_id(view),
771 .title = view_get_title(view),
772 };
773 view->ext_foreign_toplevel =
774 wlr_ext_foreign_toplevel_handle_v1_create(server.foreign_toplevel_list, &foreign_toplevel_state);
736 775
737 view->foreign_toplevel = 776 view->foreign_toplevel =
738 wlr_foreign_toplevel_handle_v1_create(server.foreign_toplevel_manager); 777 wlr_foreign_toplevel_handle_v1_create(server.foreign_toplevel_manager);
@@ -749,13 +788,6 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface,
749 wl_signal_add(&view->foreign_toplevel->events.destroy, 788 wl_signal_add(&view->foreign_toplevel->events.destroy,
750 &view->foreign_destroy); 789 &view->foreign_destroy);
751 790
752 // If we're about to launch the view into the floating container, then
753 // launch it as a tiled view in the root of the workspace instead.
754 if (target_sibling && container_is_floating(target_sibling)) {
755 target_sibling = NULL;
756 ws = seat_get_last_known_workspace(seat);
757 }
758
759 struct sway_container *container = view->container; 791 struct sway_container *container = view->container;
760 if (target_sibling) { 792 if (target_sibling) {
761 container_add_sibling(target_sibling, container, 1); 793 container_add_sibling(target_sibling, container, 1);
@@ -764,11 +796,6 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface,
764 } 796 }
765 ipc_event_window(view->container, "new"); 797 ipc_event_window(view->container, "new");
766 798
767 view_init_subsurfaces(view, wlr_surface);
768 wl_signal_add(&wlr_surface->events.new_subsurface,
769 &view->surface_new_subsurface);
770 view->surface_new_subsurface.notify = view_handle_surface_new_subsurface;
771
772 if (decoration) { 799 if (decoration) {
773 view_update_csd_from_client(view, decoration); 800 view_update_csd_from_client(view, decoration);
774 } 801 }
@@ -811,10 +838,9 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface,
811 838
812 bool set_focus = should_focus(view); 839 bool set_focus = should_focus(view);
813 840
814#if HAVE_XWAYLAND 841#if WLR_HAS_XWAYLAND
815 if (wlr_surface_is_xwayland_surface(wlr_surface)) { 842 struct wlr_xwayland_surface *xsurface;
816 struct wlr_xwayland_surface *xsurface = 843 if ((xsurface = wlr_xwayland_surface_try_from_wlr_surface(wlr_surface))) {
817 wlr_xwayland_surface_from_wlr_surface(wlr_surface);
818 set_focus &= wlr_xwayland_icccm_input_model(xsurface) != 844 set_focus &= wlr_xwayland_icccm_input_model(xsurface) !=
819 WLR_ICCCM_INPUT_MODEL_NONE; 845 WLR_ICCCM_INPUT_MODEL_NONE;
820 } 846 }
@@ -824,6 +850,10 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface,
824 input_manager_set_focus(&view->container->node); 850 input_manager_set_focus(&view->container->node);
825 } 851 }
826 852
853 if (view->ext_foreign_toplevel) {
854 update_ext_foreign_toplevel(view);
855 }
856
827 const char *app_id; 857 const char *app_id;
828 const char *class; 858 const char *class;
829 if ((app_id = view_get_app_id(view)) != NULL) { 859 if ((app_id = view_get_app_id(view)) != NULL) {
@@ -834,15 +864,20 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface,
834} 864}
835 865
836void view_unmap(struct sway_view *view) { 866void view_unmap(struct sway_view *view) {
837 wl_signal_emit(&view->events.unmap, view); 867 wl_signal_emit_mutable(&view->events.unmap, view);
838 868
839 wl_list_remove(&view->surface_new_subsurface.link); 869 view->executed_criteria->length = 0;
840 870
841 if (view->urgent_timer) { 871 if (view->urgent_timer) {
842 wl_event_source_remove(view->urgent_timer); 872 wl_event_source_remove(view->urgent_timer);
843 view->urgent_timer = NULL; 873 view->urgent_timer = NULL;
844 } 874 }
845 875
876 if (view->ext_foreign_toplevel) {
877 wlr_ext_foreign_toplevel_handle_v1_destroy(view->ext_foreign_toplevel);
878 view->ext_foreign_toplevel = NULL;
879 }
880
846 if (view->foreign_toplevel) { 881 if (view->foreign_toplevel) {
847 wlr_foreign_toplevel_handle_v1_destroy(view->foreign_toplevel); 882 wlr_foreign_toplevel_handle_v1_destroy(view->foreign_toplevel);
848 view->foreign_toplevel = NULL; 883 view->foreign_toplevel = NULL;
@@ -889,293 +924,54 @@ void view_update_size(struct sway_view *view) {
889 container_set_geometry_from_content(con); 924 container_set_geometry_from_content(con);
890} 925}
891 926
892void view_center_surface(struct sway_view *view) { 927void view_center_and_clip_surface(struct sway_view *view) {
893 struct sway_container *con = view->container; 928 struct sway_container *con = view->container;
894 // We always center the current coordinates rather than the next, as the
895 // geometry immediately affects the currently active rendering.
896 con->surface_x = fmax(con->current.content_x, con->current.content_x +
897 (con->current.content_width - view->geometry.width) / 2);
898 con->surface_y = fmax(con->current.content_y, con->current.content_y +
899 (con->current.content_height - view->geometry.height) / 2);
900}
901 929
902static const struct sway_view_child_impl subsurface_impl; 930 bool clip_to_geometry = true;
903 931
904static void subsurface_get_root_coords(struct sway_view_child *child, 932 if (container_is_floating(con)) {
905 int *root_sx, int *root_sy) { 933 // We always center the current coordinates rather than the next, as the
906 struct wlr_surface *surface = child->surface; 934 // geometry immediately affects the currently active rendering.
907 *root_sx = -child->view->geometry.x; 935 int x = (int) fmax(0, (con->current.content_width - view->geometry.width) / 2);
908 *root_sy = -child->view->geometry.y; 936 int y = (int) fmax(0, (con->current.content_height - view->geometry.height) / 2);
937 clip_to_geometry = !view->using_csd;
909 938
910 if (child->parent && child->parent->impl && 939 wlr_scene_node_set_position(&view->content_tree->node, x, y);
911 child->parent->impl->get_root_coords) {
912 int sx, sy;
913 child->parent->impl->get_root_coords(child->parent, &sx, &sy);
914 *root_sx += sx;
915 *root_sy += sy;
916 } else { 940 } else {
917 while (surface && wlr_surface_is_subsurface(surface)) { 941 wlr_scene_node_set_position(&view->content_tree->node, 0, 0);
918 struct wlr_subsurface *subsurface = 942 }
919 wlr_subsurface_from_wlr_surface(surface); 943
920 if (subsurface == NULL) { 944 // only make sure to clip the content if there is content to clip
921 break; 945 if (!wl_list_empty(&con->view->content_tree->children)) {
922 } 946 struct wlr_box clip = {0};
923 *root_sx += subsurface->current.x; 947 if (clip_to_geometry) {
924 *root_sy += subsurface->current.y; 948 clip = (struct wlr_box){
925 surface = subsurface->parent; 949 .x = con->view->geometry.x,
950 .y = con->view->geometry.y,
951 .width = con->current.content_width,
952 .height = con->current.content_height,
953 };
926 } 954 }
927 } 955 wlr_scene_subsurface_tree_set_clip(&con->view->content_tree->node, &clip);
928}
929
930static void subsurface_destroy(struct sway_view_child *child) {
931 if (!sway_assert(child->impl == &subsurface_impl,
932 "Expected a subsurface")) {
933 return;
934 }
935 struct sway_subsurface *subsurface = (struct sway_subsurface *)child;
936 wl_list_remove(&subsurface->destroy.link);
937 free(subsurface);
938}
939
940static const struct sway_view_child_impl subsurface_impl = {
941 .get_root_coords = subsurface_get_root_coords,
942 .destroy = subsurface_destroy,
943};
944
945static void subsurface_handle_destroy(struct wl_listener *listener,
946 void *data) {
947 struct sway_subsurface *subsurface =
948 wl_container_of(listener, subsurface, destroy);
949 struct sway_view_child *child = &subsurface->child;
950 view_child_destroy(child);
951}
952
953static void view_child_damage(struct sway_view_child *child, bool whole);
954
955static void view_subsurface_create(struct sway_view *view,
956 struct wlr_subsurface *wlr_subsurface) {
957 struct sway_subsurface *subsurface =
958 calloc(1, sizeof(struct sway_subsurface));
959 if (subsurface == NULL) {
960 sway_log(SWAY_ERROR, "Allocation failed");
961 return;
962 }
963 view_child_init(&subsurface->child, &subsurface_impl, view,
964 wlr_subsurface->surface);
965
966 wl_signal_add(&wlr_subsurface->events.destroy, &subsurface->destroy);
967 subsurface->destroy.notify = subsurface_handle_destroy;
968
969 subsurface->child.mapped = true;
970
971 view_child_damage(&subsurface->child, true);
972}
973
974static void view_child_subsurface_create(struct sway_view_child *child,
975 struct wlr_subsurface *wlr_subsurface) {
976 struct sway_subsurface *subsurface =
977 calloc(1, sizeof(struct sway_subsurface));
978 if (subsurface == NULL) {
979 sway_log(SWAY_ERROR, "Allocation failed");
980 return;
981 }
982 subsurface->child.parent = child;
983 wl_list_insert(&child->children, &subsurface->child.link);
984 view_child_init(&subsurface->child, &subsurface_impl, child->view,
985 wlr_subsurface->surface);
986
987 wl_signal_add(&wlr_subsurface->events.destroy, &subsurface->destroy);
988 subsurface->destroy.notify = subsurface_handle_destroy;
989
990 subsurface->child.mapped = true;
991
992 view_child_damage(&subsurface->child, true);
993}
994
995static bool view_child_is_mapped(struct sway_view_child *child) {
996 while (child) {
997 if (!child->mapped) {
998 return false;
999 }
1000 child = child->parent;
1001 }
1002 return true;
1003}
1004
1005static void view_child_damage(struct sway_view_child *child, bool whole) {
1006 if (!child || !view_child_is_mapped(child) || !child->view || !child->view->container) {
1007 return;
1008 }
1009 int sx, sy;
1010 child->impl->get_root_coords(child, &sx, &sy);
1011 desktop_damage_surface(child->surface,
1012 child->view->container->pending.content_x + sx,
1013 child->view->container->pending.content_y + sy, whole);
1014}
1015
1016static void view_child_handle_surface_commit(struct wl_listener *listener,
1017 void *data) {
1018 struct sway_view_child *child =
1019 wl_container_of(listener, child, surface_commit);
1020 view_child_damage(child, false);
1021}
1022
1023static void view_child_handle_surface_new_subsurface(
1024 struct wl_listener *listener, void *data) {
1025 struct sway_view_child *child =
1026 wl_container_of(listener, child, surface_new_subsurface);
1027 struct wlr_subsurface *subsurface = data;
1028 view_child_subsurface_create(child, subsurface);
1029}
1030
1031static void view_child_handle_surface_destroy(struct wl_listener *listener,
1032 void *data) {
1033 struct sway_view_child *child =
1034 wl_container_of(listener, child, surface_destroy);
1035 view_child_destroy(child);
1036}
1037
1038static void view_init_subsurfaces(struct sway_view *view,
1039 struct wlr_surface *surface) {
1040 struct wlr_subsurface *subsurface;
1041 wl_list_for_each(subsurface, &surface->subsurfaces_below, parent_link) {
1042 view_subsurface_create(view, subsurface);
1043 }
1044 wl_list_for_each(subsurface, &surface->subsurfaces_above, parent_link) {
1045 view_subsurface_create(view, subsurface);
1046 }
1047}
1048
1049static void view_child_init_subsurfaces(struct sway_view_child *view_child,
1050 struct wlr_surface *surface) {
1051 struct wlr_subsurface *subsurface;
1052 wl_list_for_each(subsurface, &surface->subsurfaces_below, parent_link) {
1053 view_child_subsurface_create(view_child, subsurface);
1054 }
1055 wl_list_for_each(subsurface, &surface->subsurfaces_above, parent_link) {
1056 view_child_subsurface_create(view_child, subsurface);
1057 }
1058}
1059
1060static void view_child_handle_surface_map(struct wl_listener *listener,
1061 void *data) {
1062 struct sway_view_child *child =
1063 wl_container_of(listener, child, surface_map);
1064 child->mapped = true;
1065 view_child_damage(child, true);
1066}
1067
1068static void view_child_handle_surface_unmap(struct wl_listener *listener,
1069 void *data) {
1070 struct sway_view_child *child =
1071 wl_container_of(listener, child, surface_unmap);
1072 view_child_damage(child, true);
1073 child->mapped = false;
1074}
1075
1076static void view_child_handle_view_unmap(struct wl_listener *listener,
1077 void *data) {
1078 struct sway_view_child *child =
1079 wl_container_of(listener, child, view_unmap);
1080 view_child_damage(child, true);
1081 child->mapped = false;
1082}
1083
1084void view_child_init(struct sway_view_child *child,
1085 const struct sway_view_child_impl *impl, struct sway_view *view,
1086 struct wlr_surface *surface) {
1087 child->impl = impl;
1088 child->view = view;
1089 child->surface = surface;
1090 wl_list_init(&child->children);
1091
1092 wl_signal_add(&surface->events.commit, &child->surface_commit);
1093 child->surface_commit.notify = view_child_handle_surface_commit;
1094 wl_signal_add(&surface->events.new_subsurface,
1095 &child->surface_new_subsurface);
1096 child->surface_new_subsurface.notify =
1097 view_child_handle_surface_new_subsurface;
1098 wl_signal_add(&surface->events.destroy, &child->surface_destroy);
1099 child->surface_destroy.notify = view_child_handle_surface_destroy;
1100
1101 // Not all child views have a map/unmap event
1102 child->surface_map.notify = view_child_handle_surface_map;
1103 wl_list_init(&child->surface_map.link);
1104 child->surface_unmap.notify = view_child_handle_surface_unmap;
1105 wl_list_init(&child->surface_unmap.link);
1106
1107 wl_signal_add(&view->events.unmap, &child->view_unmap);
1108 child->view_unmap.notify = view_child_handle_view_unmap;
1109
1110 struct sway_workspace *workspace = child->view->container->pending.workspace;
1111 if (workspace) {
1112 wlr_surface_send_enter(child->surface, workspace->output->wlr_output);
1113 }
1114
1115 view_child_init_subsurfaces(child, surface);
1116}
1117
1118void view_child_destroy(struct sway_view_child *child) {
1119 if (view_child_is_mapped(child) && child->view->container != NULL) {
1120 view_child_damage(child, true);
1121 }
1122
1123 if (child->parent != NULL) {
1124 wl_list_remove(&child->link);
1125 child->parent = NULL;
1126 }
1127
1128 struct sway_view_child *subchild, *tmpchild;
1129 wl_list_for_each_safe(subchild, tmpchild, &child->children, link) {
1130 wl_list_remove(&subchild->link);
1131 subchild->parent = NULL;
1132 // The subchild lost its parent link, so it cannot see that the parent
1133 // is unmapped. Unmap it directly.
1134 subchild->mapped = false;
1135 }
1136
1137 wl_list_remove(&child->surface_commit.link);
1138 wl_list_remove(&child->surface_destroy.link);
1139 wl_list_remove(&child->surface_map.link);
1140 wl_list_remove(&child->surface_unmap.link);
1141 wl_list_remove(&child->view_unmap.link);
1142 wl_list_remove(&child->surface_new_subsurface.link);
1143
1144 if (child->impl && child->impl->destroy) {
1145 child->impl->destroy(child);
1146 } else {
1147 free(child);
1148 } 956 }
1149} 957}
1150 958
1151struct sway_view *view_from_wlr_surface(struct wlr_surface *wlr_surface) { 959struct sway_view *view_from_wlr_surface(struct wlr_surface *wlr_surface) {
1152 if (wlr_surface_is_xdg_surface(wlr_surface)) { 960 struct wlr_xdg_surface *xdg_surface;
1153 struct wlr_xdg_surface *xdg_surface = 961 if ((xdg_surface = wlr_xdg_surface_try_from_wlr_surface(wlr_surface))) {
1154 wlr_xdg_surface_from_wlr_surface(wlr_surface);
1155 if (xdg_surface == NULL) {
1156 return NULL;
1157 }
1158 return view_from_wlr_xdg_surface(xdg_surface); 962 return view_from_wlr_xdg_surface(xdg_surface);
1159 } 963 }
1160#if HAVE_XWAYLAND 964#if WLR_HAS_XWAYLAND
1161 if (wlr_surface_is_xwayland_surface(wlr_surface)) { 965 struct wlr_xwayland_surface *xsurface;
1162 struct wlr_xwayland_surface *xsurface = 966 if ((xsurface = wlr_xwayland_surface_try_from_wlr_surface(wlr_surface))) {
1163 wlr_xwayland_surface_from_wlr_surface(wlr_surface);
1164 if (xsurface == NULL) {
1165 return NULL;
1166 }
1167 return view_from_wlr_xwayland_surface(xsurface); 967 return view_from_wlr_xwayland_surface(xsurface);
1168 } 968 }
1169#endif 969#endif
1170 if (wlr_surface_is_subsurface(wlr_surface)) { 970 struct wlr_subsurface *subsurface;
1171 struct wlr_subsurface *subsurface = 971 if ((subsurface = wlr_subsurface_try_from_wlr_surface(wlr_surface))) {
1172 wlr_subsurface_from_wlr_surface(wlr_surface);
1173 if (subsurface == NULL) {
1174 return NULL;
1175 }
1176 return view_from_wlr_surface(subsurface->parent); 972 return view_from_wlr_surface(subsurface->parent);
1177 } 973 }
1178 if (wlr_surface_is_layer_surface(wlr_surface)) { 974 if (wlr_layer_surface_v1_try_from_wlr_surface(wlr_surface) != NULL) {
1179 return NULL; 975 return NULL;
1180 } 976 }
1181 977
@@ -1256,6 +1052,18 @@ static size_t parse_title_format(struct sway_view *view, char *buffer) {
1256 return len; 1052 return len;
1257} 1053}
1258 1054
1055void view_update_app_id(struct sway_view *view) {
1056 const char *app_id = view_get_app_id(view);
1057
1058 if (view->foreign_toplevel && app_id) {
1059 wlr_foreign_toplevel_handle_v1_set_app_id(view->foreign_toplevel, app_id);
1060 }
1061
1062 if (view->ext_foreign_toplevel) {
1063 update_ext_foreign_toplevel(view);
1064 }
1065}
1066
1259void view_update_title(struct sway_view *view, bool force) { 1067void view_update_title(struct sway_view *view, bool force) {
1260 const char *title = view_get_title(view); 1068 const char *title = view_get_title(view);
1261 1069
@@ -1271,31 +1079,41 @@ void view_update_title(struct sway_view *view, bool force) {
1271 1079
1272 free(view->container->title); 1080 free(view->container->title);
1273 free(view->container->formatted_title); 1081 free(view->container->formatted_title);
1274 if (title) { 1082
1275 size_t len = parse_title_format(view, NULL); 1083 size_t len = parse_title_format(view, NULL);
1084
1085 if (len) {
1276 char *buffer = calloc(len + 1, sizeof(char)); 1086 char *buffer = calloc(len + 1, sizeof(char));
1277 if (!sway_assert(buffer, "Unable to allocate title string")) { 1087 if (!sway_assert(buffer, "Unable to allocate title string")) {
1278 return; 1088 return;
1279 } 1089 }
1280 parse_title_format(view, buffer);
1281 1090
1282 view->container->title = strdup(title); 1091 parse_title_format(view, buffer);
1283 view->container->formatted_title = buffer; 1092 view->container->formatted_title = buffer;
1284 } else { 1093 } else {
1285 view->container->title = NULL;
1286 view->container->formatted_title = NULL; 1094 view->container->formatted_title = NULL;
1287 } 1095 }
1288 container_calculate_title_height(view->container); 1096
1289 config_update_font_height(false); 1097 view->container->title = title ? strdup(title) : NULL;
1290 1098
1291 // Update title after the global font height is updated 1099 // Update title after the global font height is updated
1292 container_update_title_textures(view->container); 1100 if (view->container->title_bar.title_text && len) {
1101 sway_text_node_set_text(view->container->title_bar.title_text,
1102 view->container->formatted_title);
1103 container_arrange_title_bar(view->container);
1104 } else {
1105 container_update_title_bar(view->container);
1106 }
1293 1107
1294 ipc_event_window(view->container, "title"); 1108 ipc_event_window(view->container, "title");
1295 1109
1296 if (view->foreign_toplevel && title) { 1110 if (view->foreign_toplevel && title) {
1297 wlr_foreign_toplevel_handle_v1_set_title(view->foreign_toplevel, title); 1111 wlr_foreign_toplevel_handle_v1_set_title(view->foreign_toplevel, title);
1298 } 1112 }
1113
1114 if (view->ext_foreign_toplevel) {
1115 update_ext_foreign_toplevel(view);
1116 }
1299} 1117}
1300 1118
1301bool view_is_visible(struct sway_view *view) { 1119bool view_is_visible(struct sway_view *view) {
@@ -1356,6 +1174,7 @@ void view_set_urgent(struct sway_view *view, bool enable) {
1356 return; 1174 return;
1357 } 1175 }
1358 clock_gettime(CLOCK_MONOTONIC, &view->urgent); 1176 clock_gettime(CLOCK_MONOTONIC, &view->urgent);
1177 container_update_itself_and_parents(view->container);
1359 } else { 1178 } else {
1360 view->urgent = (struct timespec){ 0 }; 1179 view->urgent = (struct timespec){ 0 };
1361 if (view->urgent_timer) { 1180 if (view->urgent_timer) {
@@ -1363,7 +1182,6 @@ void view_set_urgent(struct sway_view *view, bool enable) {
1363 view->urgent_timer = NULL; 1182 view->urgent_timer = NULL;
1364 } 1183 }
1365 } 1184 }
1366 container_damage_whole(view->container);
1367 1185
1368 ipc_event_window(view->container, "urgent"); 1186 ipc_event_window(view->container, "urgent");
1369 1187
@@ -1377,40 +1195,54 @@ bool view_is_urgent(struct sway_view *view) {
1377} 1195}
1378 1196
1379void view_remove_saved_buffer(struct sway_view *view) { 1197void view_remove_saved_buffer(struct sway_view *view) {
1380 if (!sway_assert(!wl_list_empty(&view->saved_buffers), "Expected a saved buffer")) { 1198 if (!sway_assert(view->saved_surface_tree, "Expected a saved buffer")) {
1381 return; 1199 return;
1382 } 1200 }
1383 struct sway_saved_buffer *saved_buf, *tmp; 1201
1384 wl_list_for_each_safe(saved_buf, tmp, &view->saved_buffers, link) { 1202 wlr_scene_node_destroy(&view->saved_surface_tree->node);
1385 wlr_buffer_unlock(&saved_buf->buffer->base); 1203 view->saved_surface_tree = NULL;
1386 wl_list_remove(&saved_buf->link); 1204 wlr_scene_node_set_enabled(&view->content_tree->node, true);
1387 free(saved_buf);
1388 }
1389} 1205}
1390 1206
1391static void view_save_buffer_iterator(struct wlr_surface *surface, 1207static void view_save_buffer_iterator(struct wlr_scene_buffer *buffer,
1392 int sx, int sy, void *data) { 1208 int sx, int sy, void *data) {
1393 struct sway_view *view = data; 1209 struct wlr_scene_tree *tree = data;
1394 1210
1395 if (surface && wlr_surface_has_buffer(surface)) { 1211 struct wlr_scene_buffer *sbuf = wlr_scene_buffer_create(tree, NULL);
1396 wlr_buffer_lock(&surface->buffer->base); 1212 if (!sbuf) {
1397 struct sway_saved_buffer *saved_buffer = calloc(1, sizeof(struct sway_saved_buffer)); 1213 sway_log(SWAY_ERROR, "Could not allocate a scene buffer when saving a surface");
1398 saved_buffer->buffer = surface->buffer; 1214 return;
1399 saved_buffer->width = surface->current.width;
1400 saved_buffer->height = surface->current.height;
1401 saved_buffer->x = view->container->surface_x + sx;
1402 saved_buffer->y = view->container->surface_y + sy;
1403 saved_buffer->transform = surface->current.transform;
1404 wlr_surface_get_buffer_source_box(surface, &saved_buffer->source_box);
1405 wl_list_insert(&view->saved_buffers, &saved_buffer->link);
1406 } 1215 }
1216
1217 wlr_scene_buffer_set_dest_size(sbuf,
1218 buffer->dst_width, buffer->dst_height);
1219 wlr_scene_buffer_set_opaque_region(sbuf, &buffer->opaque_region);
1220 wlr_scene_buffer_set_source_box(sbuf, &buffer->src_box);
1221 wlr_scene_node_set_position(&sbuf->node, sx, sy);
1222 wlr_scene_buffer_set_transform(sbuf, buffer->transform);
1223 wlr_scene_buffer_set_buffer(sbuf, buffer->buffer);
1407} 1224}
1408 1225
1409void view_save_buffer(struct sway_view *view) { 1226void view_save_buffer(struct sway_view *view) {
1410 if (!sway_assert(wl_list_empty(&view->saved_buffers), "Didn't expect saved buffer")) { 1227 if (!sway_assert(!view->saved_surface_tree, "Didn't expect saved buffer")) {
1411 view_remove_saved_buffer(view); 1228 view_remove_saved_buffer(view);
1412 } 1229 }
1413 view_for_each_surface(view, view_save_buffer_iterator, view); 1230
1231 view->saved_surface_tree = wlr_scene_tree_create(view->scene_tree);
1232 if (!view->saved_surface_tree) {
1233 sway_log(SWAY_ERROR, "Could not allocate a scene tree node when saving a surface");
1234 return;
1235 }
1236
1237 // Enable and disable the saved surface tree like so to atomitaclly update
1238 // the tree. This will prevent over damaging or other weirdness.
1239 wlr_scene_node_set_enabled(&view->saved_surface_tree->node, false);
1240
1241 wlr_scene_node_for_each_buffer(&view->content_tree->node,
1242 view_save_buffer_iterator, view->saved_surface_tree);
1243
1244 wlr_scene_node_set_enabled(&view->content_tree->node, false);
1245 wlr_scene_node_set_enabled(&view->saved_surface_tree->node, true);
1414} 1246}
1415 1247
1416bool view_is_transient_for(struct sway_view *child, 1248bool view_is_transient_for(struct sway_view *child,
@@ -1418,3 +1250,19 @@ bool view_is_transient_for(struct sway_view *child,
1418 return child->impl->is_transient_for && 1250 return child->impl->is_transient_for &&
1419 child->impl->is_transient_for(child, ancestor); 1251 child->impl->is_transient_for(child, ancestor);
1420} 1252}
1253
1254static void send_frame_done_iterator(struct wlr_scene_buffer *scene_buffer,
1255 int x, int y, void *data) {
1256 struct timespec *when = data;
1257 wl_signal_emit_mutable(&scene_buffer->events.frame_done, when);
1258}
1259
1260void view_send_frame_done(struct sway_view *view) {
1261 struct timespec when;
1262 clock_gettime(CLOCK_MONOTONIC, &when);
1263
1264 struct wlr_scene_node *node;
1265 wl_list_for_each(node, &view->content_tree->children, link) {
1266 wlr_scene_node_for_each_buffer(node, send_frame_done_iterator, &when);
1267 }
1268}
diff --git a/sway/tree/workspace.c b/sway/tree/workspace.c
index 8dd7789d..a68dc927 100644
--- a/sway/tree/workspace.c
+++ b/sway/tree/workspace.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809
2#include <ctype.h> 1#include <ctype.h>
3#include <limits.h> 2#include <limits.h>
4#include <stdbool.h> 3#include <stdbool.h>
@@ -50,12 +49,14 @@ struct sway_output *workspace_get_initial_output(const char *name) {
50 } else if (focus && focus->type == N_CONTAINER) { 49 } else if (focus && focus->type == N_CONTAINER) {
51 return focus->sway_container->pending.workspace->output; 50 return focus->sway_container->pending.workspace->output;
52 } 51 }
53 // Fallback to the first output or noop output for headless 52 // Fallback to the first output or the headless output
54 return root->outputs->length ? root->outputs->items[0] : root->noop_output; 53 return root->outputs->length ? root->outputs->items[0] : root->fallback_output;
55} 54}
56 55
57struct sway_workspace *workspace_create(struct sway_output *output, 56struct sway_workspace *workspace_create(struct sway_output *output,
58 const char *name) { 57 const char *name) {
58 sway_assert(name, "NULL name given to workspace_create");
59
59 if (output == NULL) { 60 if (output == NULL) {
60 output = workspace_get_initial_output(name); 61 output = workspace_get_initial_output(name);
61 } 62 }
@@ -69,7 +70,19 @@ struct sway_workspace *workspace_create(struct sway_output *output,
69 return NULL; 70 return NULL;
70 } 71 }
71 node_init(&ws->node, N_WORKSPACE, ws); 72 node_init(&ws->node, N_WORKSPACE, ws);
72 ws->name = name ? strdup(name) : NULL; 73
74 bool failed = false;
75 ws->layers.tiling = alloc_scene_tree(root->staging, &failed);
76 ws->layers.fullscreen = alloc_scene_tree(root->staging, &failed);
77
78 if (failed) {
79 wlr_scene_node_destroy(&ws->layers.tiling->node);
80 wlr_scene_node_destroy(&ws->layers.fullscreen->node);
81 free(ws);
82 return NULL;
83 }
84
85 ws->name = strdup(name);
73 ws->prev_split_layout = L_NONE; 86 ws->prev_split_layout = L_NONE;
74 ws->layout = output_get_default_layout(output); 87 ws->layout = output_get_default_layout(output);
75 ws->floating = create_list(); 88 ws->floating = create_list();
@@ -114,7 +127,7 @@ struct sway_workspace *workspace_create(struct sway_output *output,
114 output_sort_workspaces(output); 127 output_sort_workspaces(output);
115 128
116 ipc_event_workspace(NULL, ws, "init"); 129 ipc_event_workspace(NULL, ws, "init");
117 wl_signal_emit(&root->events.new_node, &ws->node); 130 wl_signal_emit_mutable(&root->events.new_node, &ws->node);
118 131
119 return ws; 132 return ws;
120} 133}
@@ -129,6 +142,11 @@ void workspace_destroy(struct sway_workspace *workspace) {
129 return; 142 return;
130 } 143 }
131 144
145 scene_node_disown_children(workspace->layers.tiling);
146 scene_node_disown_children(workspace->layers.fullscreen);
147 wlr_scene_node_destroy(&workspace->layers.tiling->node);
148 wlr_scene_node_destroy(&workspace->layers.fullscreen->node);
149
132 free(workspace->name); 150 free(workspace->name);
133 free(workspace->representation); 151 free(workspace->representation);
134 list_free_items_and_destroy(workspace->output_priority); 152 list_free_items_and_destroy(workspace->output_priority);
@@ -142,7 +160,7 @@ void workspace_destroy(struct sway_workspace *workspace) {
142void workspace_begin_destroy(struct sway_workspace *workspace) { 160void workspace_begin_destroy(struct sway_workspace *workspace) {
143 sway_log(SWAY_DEBUG, "Destroying workspace '%s'", workspace->name); 161 sway_log(SWAY_DEBUG, "Destroying workspace '%s'", workspace->name);
144 ipc_event_workspace(NULL, workspace, "empty"); // intentional 162 ipc_event_workspace(NULL, workspace, "empty"); // intentional
145 wl_signal_emit(&workspace->node.events.destroy, &workspace->node); 163 wl_signal_emit_mutable(&workspace->node.events.destroy, &workspace->node);
146 164
147 if (workspace->output) { 165 if (workspace->output) {
148 workspace_detach(workspace); 166 workspace_detach(workspace);
@@ -174,22 +192,16 @@ void workspace_consider_destroy(struct sway_workspace *ws) {
174static bool workspace_valid_on_output(const char *output_name, 192static bool workspace_valid_on_output(const char *output_name,
175 const char *ws_name) { 193 const char *ws_name) {
176 struct workspace_config *wsc = workspace_find_config(ws_name); 194 struct workspace_config *wsc = workspace_find_config(ws_name);
177 char identifier[128];
178 struct sway_output *output = output_by_name_or_id(output_name); 195 struct sway_output *output = output_by_name_or_id(output_name);
179 if (!output) { 196 if (!output) {
180 return false; 197 return false;
181 } 198 }
182 output_name = output->wlr_output->name;
183 output_get_identifier(identifier, sizeof(identifier), output);
184
185 if (!wsc) { 199 if (!wsc) {
186 return true; 200 return true;
187 } 201 }
188 202
189 for (int i = 0; i < wsc->outputs->length; i++) { 203 for (int i = 0; i < wsc->outputs->length; i++) {
190 if (strcmp(wsc->outputs->items[i], "*") == 0 || 204 if (output_match_name_or_id(output, wsc->outputs->items[i])) {
191 strcmp(wsc->outputs->items[i], output_name) == 0 ||
192 strcmp(wsc->outputs->items[i], identifier) == 0) {
193 return true; 205 return true;
194 } 206 }
195 } 207 }
@@ -284,13 +296,10 @@ char *workspace_next_name(const char *output_name) {
284 // assignments primarily, falling back to bindings and numbers. 296 // assignments primarily, falling back to bindings and numbers.
285 struct sway_mode *mode = config->current_mode; 297 struct sway_mode *mode = config->current_mode;
286 298
287 char identifier[128];
288 struct sway_output *output = output_by_name_or_id(output_name); 299 struct sway_output *output = output_by_name_or_id(output_name);
289 if (!output) { 300 if (!output) {
290 return NULL; 301 return NULL;
291 } 302 }
292 output_name = output->wlr_output->name;
293 output_get_identifier(identifier, sizeof(identifier), output);
294 303
295 int order = INT_MAX; 304 int order = INT_MAX;
296 char *target = NULL; 305 char *target = NULL;
@@ -310,9 +319,7 @@ char *workspace_next_name(const char *output_name) {
310 } 319 }
311 bool found = false; 320 bool found = false;
312 for (int j = 0; j < wsc->outputs->length; ++j) { 321 for (int j = 0; j < wsc->outputs->length; ++j) {
313 if (strcmp(wsc->outputs->items[j], "*") == 0 || 322 if (output_match_name_or_id(output, wsc->outputs->items[j])) {
314 strcmp(wsc->outputs->items[j], output_name) == 0 ||
315 strcmp(wsc->outputs->items[j], identifier) == 0) {
316 found = true; 323 found = true;
317 free(target); 324 free(target);
318 target = strdup(wsc->workspace); 325 target = strdup(wsc->workspace);
@@ -652,15 +659,9 @@ void workspace_output_add_priority(struct sway_workspace *workspace,
652 659
653struct sway_output *workspace_output_get_highest_available( 660struct sway_output *workspace_output_get_highest_available(
654 struct sway_workspace *ws, struct sway_output *exclude) { 661 struct sway_workspace *ws, struct sway_output *exclude) {
655 char exclude_id[128] = {'\0'};
656 if (exclude) {
657 output_get_identifier(exclude_id, sizeof(exclude_id), exclude);
658 }
659
660 for (int i = 0; i < ws->output_priority->length; i++) { 662 for (int i = 0; i < ws->output_priority->length; i++) {
661 char *name = ws->output_priority->items[i]; 663 const char *name = ws->output_priority->items[i];
662 if (exclude && (strcmp(name, exclude->wlr_output->name) == 0 664 if (exclude && output_match_name_or_id(exclude, name)) {
663 || strcmp(name, exclude_id) == 0)) {
664 continue; 665 continue;
665 } 666 }
666 667
@@ -684,7 +685,6 @@ void workspace_detect_urgent(struct sway_workspace *workspace) {
684 if (workspace->urgent != new_urgent) { 685 if (workspace->urgent != new_urgent) {
685 workspace->urgent = new_urgent; 686 workspace->urgent = new_urgent;
686 ipc_event_workspace(NULL, workspace, "urgent"); 687 ipc_event_workspace(NULL, workspace, "urgent");
687 output_damage_whole(workspace->output);
688 } 688 }
689} 689}
690 690
@@ -844,24 +844,36 @@ struct sway_container *workspace_insert_tiling(struct sway_workspace *workspace,
844 return con; 844 return con;
845} 845}
846 846
847bool workspace_has_single_visible_container(struct sway_workspace *ws) {
848 struct sway_seat *seat = input_manager_get_default_seat();
849 struct sway_container *focus =
850 seat_get_focus_inactive_tiling(seat, ws);
851 if (focus && !focus->view) {
852 focus = seat_get_focus_inactive_view(seat, &focus->node);
853 }
854 return (focus && focus->view && view_ancestor_is_only_visible(focus->view));
855}
856
847void workspace_add_gaps(struct sway_workspace *ws) { 857void workspace_add_gaps(struct sway_workspace *ws) {
848 if (config->smart_gaps) { 858 if (config->smart_gaps == SMART_GAPS_ON
849 struct sway_seat *seat = input_manager_get_default_seat(); 859 && workspace_has_single_visible_container(ws)) {
850 struct sway_container *focus = 860 ws->current_gaps.top = 0;
851 seat_get_focus_inactive_tiling(seat, ws); 861 ws->current_gaps.right = 0;
852 if (focus && !focus->view) { 862 ws->current_gaps.bottom = 0;
853 focus = seat_get_focus_inactive_view(seat, &focus->node); 863 ws->current_gaps.left = 0;
854 } 864 return;
855 if (focus && focus->view && view_ancestor_is_only_visible(focus->view)) { 865 }
856 ws->current_gaps.top = 0; 866
857 ws->current_gaps.right = 0; 867 if (config->smart_gaps == SMART_GAPS_INVERSE_OUTER
858 ws->current_gaps.bottom = 0; 868 && !workspace_has_single_visible_container(ws)) {
859 ws->current_gaps.left = 0; 869 ws->current_gaps.top = 0;
860 return; 870 ws->current_gaps.right = 0;
861 } 871 ws->current_gaps.bottom = 0;
872 ws->current_gaps.left = 0;
873 } else {
874 ws->current_gaps = ws->gaps_outer;
862 } 875 }
863 876
864 ws->current_gaps = ws->gaps_outer;
865 // Add inner gaps and make sure we don't turn out negative 877 // Add inner gaps and make sure we don't turn out negative
866 ws->current_gaps.top = fmax(0, ws->current_gaps.top + ws->gaps_inner); 878 ws->current_gaps.top = fmax(0, ws->current_gaps.top + ws->gaps_inner);
867 ws->current_gaps.right = fmax(0, ws->current_gaps.right + ws->gaps_inner); 879 ws->current_gaps.right = fmax(0, ws->current_gaps.right + ws->gaps_inner);
diff --git a/sway/xdg_activation_v1.c b/sway/xdg_activation_v1.c
index 6c70c785..b7c80dd4 100644
--- a/sway/xdg_activation_v1.c
+++ b/sway/xdg_activation_v1.c
@@ -1,20 +1,65 @@
1#include <wlr/types/wlr_xdg_activation_v1.h> 1#include <wlr/types/wlr_xdg_activation_v1.h>
2#include <wlr/types/wlr_xdg_shell.h>
3#include "sway/desktop/launcher.h"
2#include "sway/tree/view.h" 4#include "sway/tree/view.h"
5#include "sway/tree/workspace.h"
3 6
4void xdg_activation_v1_handle_request_activate(struct wl_listener *listener, 7void xdg_activation_v1_handle_request_activate(struct wl_listener *listener,
5 void *data) { 8 void *data) {
6 const struct wlr_xdg_activation_v1_request_activate_event *event = data; 9 const struct wlr_xdg_activation_v1_request_activate_event *event = data;
7 10
8 if (!wlr_surface_is_xdg_surface(event->surface)) { 11 struct wlr_xdg_surface *xdg_surface =
12 wlr_xdg_surface_try_from_wlr_surface(event->surface);
13 if (xdg_surface == NULL) {
9 return; 14 return;
10 } 15 }
11
12 struct wlr_xdg_surface *xdg_surface =
13 wlr_xdg_surface_from_wlr_surface(event->surface);
14 struct sway_view *view = xdg_surface->data; 16 struct sway_view *view = xdg_surface->data;
15 if (!xdg_surface->mapped || view == NULL) { 17 if (view == NULL) {
18 return;
19 }
20
21 struct launcher_ctx *ctx = event->token->data;
22 if (ctx == NULL) {
23 return;
24 }
25
26 if (!xdg_surface->surface->mapped) {
27 // This is a startup notification. If we are tracking it, the data
28 // field is a launcher_ctx.
29 if (ctx->activated) {
30 // This ctx has already been activated and cannot be used again
31 // for a startup notification. It will be destroyed
32 return;
33 } else {
34 ctx->activated = true;
35 view_assign_ctx(view, ctx);
36 }
16 return; 37 return;
17 } 38 }
18 39
19 view_request_activate(view); 40 // This is an activation request. If this context is internal we have ctx->seat.
41 struct sway_seat *seat = ctx->seat;
42 if (!seat) {
43 // Otherwise, use the seat indicated by the launcher client in set_serial
44 seat = ctx->token->seat ? ctx->token->seat->data : NULL;
45 }
46
47 if (seat && ctx->had_focused_surface) {
48 view_request_activate(view, seat);
49 } else {
50 // The token is valid, but cannot be used to activate a window
51 view_request_urgent(view);
52 }
53}
54
55void xdg_activation_v1_handle_new_token(struct wl_listener *listener, void *data) {
56 struct wlr_xdg_activation_token_v1 *token = data;
57 struct sway_seat *seat = token->seat ? token->seat->data :
58 input_manager_current_seat();
59
60 struct sway_workspace *ws = seat_get_focused_workspace(seat);
61 if (ws) {
62 launcher_ctx_create(token, &ws->node);
63 return;
64 }
20} 65}
diff --git a/sway/xdg_decoration.c b/sway/xdg_decoration.c
index e7c3ea73..fa8c6279 100644
--- a/sway/xdg_decoration.c
+++ b/sway/xdg_decoration.c
@@ -10,7 +10,7 @@ static void xdg_decoration_handle_destroy(struct wl_listener *listener,
10 void *data) { 10 void *data) {
11 struct sway_xdg_decoration *deco = 11 struct sway_xdg_decoration *deco =
12 wl_container_of(listener, deco, destroy); 12 wl_container_of(listener, deco, destroy);
13 if(deco->view) { 13 if (deco->view) {
14 deco->view->xdg_decoration = NULL; 14 deco->view->xdg_decoration = NULL;
15 } 15 }
16 wl_list_remove(&deco->destroy.link); 16 wl_list_remove(&deco->destroy.link);
@@ -23,13 +23,12 @@ static void xdg_decoration_handle_request_mode(struct wl_listener *listener,
23 void *data) { 23 void *data) {
24 struct sway_xdg_decoration *deco = 24 struct sway_xdg_decoration *deco =
25 wl_container_of(listener, deco, request_mode); 25 wl_container_of(listener, deco, request_mode);
26 wlr_xdg_toplevel_decoration_v1_set_mode(deco->wlr_xdg_decoration, 26 set_xdg_decoration_mode(deco);
27 WLR_XDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE);
28} 27}
29 28
30void handle_xdg_decoration(struct wl_listener *listener, void *data) { 29void handle_xdg_decoration(struct wl_listener *listener, void *data) {
31 struct wlr_xdg_toplevel_decoration_v1 *wlr_deco = data; 30 struct wlr_xdg_toplevel_decoration_v1 *wlr_deco = data;
32 struct sway_xdg_shell_view *xdg_shell_view = wlr_deco->surface->data; 31 struct sway_xdg_shell_view *xdg_shell_view = wlr_deco->toplevel->base->data;
33 32
34 struct sway_xdg_decoration *deco = calloc(1, sizeof(*deco)); 33 struct sway_xdg_decoration *deco = calloc(1, sizeof(*deco));
35 if (deco == NULL) { 34 if (deco == NULL) {
@@ -48,16 +47,46 @@ void handle_xdg_decoration(struct wl_listener *listener, void *data) {
48 47
49 wl_list_insert(&server.xdg_decorations, &deco->link); 48 wl_list_insert(&server.xdg_decorations, &deco->link);
50 49
51 xdg_decoration_handle_request_mode(&deco->request_mode, wlr_deco); 50 set_xdg_decoration_mode(deco);
52} 51}
53 52
54struct sway_xdg_decoration *xdg_decoration_from_surface( 53struct sway_xdg_decoration *xdg_decoration_from_surface(
55 struct wlr_surface *surface) { 54 struct wlr_surface *surface) {
56 struct sway_xdg_decoration *deco; 55 struct sway_xdg_decoration *deco;
57 wl_list_for_each(deco, &server.xdg_decorations, link) { 56 wl_list_for_each(deco, &server.xdg_decorations, link) {
58 if (deco->wlr_xdg_decoration->surface->surface == surface) { 57 if (deco->wlr_xdg_decoration->toplevel->base->surface == surface) {
59 return deco; 58 return deco;
60 } 59 }
61 } 60 }
62 return NULL; 61 return NULL;
63} 62}
63
64void set_xdg_decoration_mode(struct sway_xdg_decoration *deco) {
65 struct sway_view *view = deco->view;
66 enum wlr_xdg_toplevel_decoration_v1_mode mode =
67 WLR_XDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE;
68 enum wlr_xdg_toplevel_decoration_v1_mode client_mode =
69 deco->wlr_xdg_decoration->requested_mode;
70
71 bool floating;
72 if (view->container) {
73 floating = container_is_floating(view->container);
74 bool csd = false;
75 csd = client_mode ==
76 WLR_XDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE;
77 view_update_csd_from_client(view, csd);
78 arrange_container(view->container);
79 transaction_commit_dirty();
80 } else {
81 floating = view->impl->wants_floating &&
82 view->impl->wants_floating(view);
83 }
84
85 if (floating && client_mode) {
86 mode = client_mode;
87 }
88
89 if (view->wlr_xdg_toplevel->base->initialized) {
90 wlr_xdg_toplevel_decoration_v1_set_mode(deco->wlr_xdg_decoration, mode);
91 }
92}