aboutsummaryrefslogtreecommitdiffstats
path: root/sway
diff options
context:
space:
mode:
Diffstat (limited to 'sway')
-rw-r--r--sway/commands.c24
-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.c1
-rw-r--r--sway/commands/bar/icon_theme.c1
-rw-r--r--sway/commands/bar/id.c1
-rw-r--r--sway/commands/bar/mode.c1
-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.c15
-rw-r--r--sway/commands/client.c12
-rw-r--r--sway/commands/exec_always.c28
-rw-r--r--sway/commands/floating_minmax_size.c6
-rw-r--r--sway/commands/font.c28
-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.c11
-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.c7
-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.c50
-rw-r--r--sway/commands/no_focus.c2
-rw-r--r--sway/commands/opacity.c3
-rw-r--r--sway/commands/output.c11
-rw-r--r--sway/commands/output/background.c4
-rw-r--r--sway/commands/output/dpms.c45
-rw-r--r--sway/commands/output/power.c43
-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.c8
-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.c26
-rw-r--r--sway/commands/seat/hide_cursor.c1
-rw-r--r--sway/commands/seat/idle.c7
-rw-r--r--sway/commands/seat/pointer_constraint.c1
-rw-r--r--sway/commands/seat/shortcuts_inhibitor.c1
-rw-r--r--sway/commands/seat/xcursor_theme.c1
-rw-r--r--sway/commands/set.c1
-rw-r--r--sway/commands/shortcuts_inhibitor.c1
-rw-r--r--sway/commands/show_marks.c12
-rw-r--r--sway/commands/split.c2
-rw-r--r--sway/commands/swap.c184
-rw-r--r--sway/commands/title_align.c9
-rw-r--r--sway/commands/title_format.c1
-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.c132
-rw-r--r--sway/config/bar.c3
-rw-r--r--sway/config/input.c14
-rw-r--r--sway/config/output.c922
-rw-r--r--sway/config/seat.c2
-rw-r--r--sway/criteria.c52
-rw-r--r--sway/desktop/desktop.c40
-rw-r--r--sway/desktop/idle_inhibit_v1.c46
-rw-r--r--sway/desktop/launcher.c268
-rw-r--r--sway/desktop/layer_shell.c780
-rw-r--r--sway/desktop/output.c986
-rw-r--r--sway/desktop/render.c1204
-rw-r--r--sway/desktop/surface.c46
-rw-r--r--sway/desktop/transaction.c503
-rw-r--r--sway/desktop/xdg_shell.c264
-rw-r--r--sway/desktop/xwayland.c268
-rw-r--r--sway/input/cursor.c514
-rw-r--r--sway/input/input-manager.c149
-rw-r--r--sway/input/keyboard.c188
-rw-r--r--sway/input/libinput.c94
-rw-r--r--sway/input/seat.c415
-rw-r--r--sway/input/seatop_default.c537
-rw-r--r--sway/input/seatop_down.c139
-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.c3
-rw-r--r--sway/input/seatop_resize_tiling.c3
-rw-r--r--sway/input/switch.c16
-rw-r--r--sway/input/tablet.c45
-rw-r--r--sway/input/text_input.c233
-rw-r--r--sway/ipc-json.c199
-rw-r--r--sway/ipc-server.c34
-rw-r--r--sway/lock.c352
-rw-r--r--sway/main.c104
-rw-r--r--sway/meson.build27
-rw-r--r--sway/scene_descriptor.c66
-rw-r--r--sway/server.c269
-rw-r--r--sway/sway-input.5.scd40
-rw-r--r--sway/sway-ipc.7.scd48
-rw-r--r--sway/sway-output.5.scd26
-rw-r--r--sway/sway.5.scd108
-rw-r--r--sway/sway_text_node.c308
-rw-r--r--sway/swaynag.c15
-rw-r--r--sway/tree/arrange.c15
-rw-r--r--sway/tree/container.c1310
-rw-r--r--sway/tree/node.c38
-rw-r--r--sway/tree/output.c98
-rw-r--r--sway/tree/root.c273
-rw-r--r--sway/tree/view.c593
-rw-r--r--sway/tree/workspace.c53
-rw-r--r--sway/xdg_activation_v1.c57
-rw-r--r--sway/xdg_decoration.c63
129 files changed, 7053 insertions, 6024 deletions
diff --git a/sway/commands.c b/sway/commands.c
index 5a1fd32e..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,6 +45,7 @@ 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 },
@@ -92,6 +92,7 @@ static const struct cmd_handler handlers[] = {
92 { "titlebar_border_thickness", cmd_titlebar_border_thickness }, 92 { "titlebar_border_thickness", cmd_titlebar_border_thickness },
93 { "titlebar_padding", cmd_titlebar_padding }, 93 { "titlebar_padding", cmd_titlebar_padding },
94 { "unbindcode", cmd_unbindcode }, 94 { "unbindcode", cmd_unbindcode },
95 { "unbindgesture", cmd_unbindgesture },
95 { "unbindswitch", cmd_unbindswitch }, 96 { "unbindswitch", cmd_unbindswitch },
96 { "unbindsym", cmd_unbindsym }, 97 { "unbindsym", cmd_unbindsym },
97 { "workspace", cmd_workspace }, 98 { "workspace", cmd_workspace },
@@ -102,6 +103,7 @@ static const struct cmd_handler handlers[] = {
102static const struct cmd_handler config_handlers[] = { 103static const struct cmd_handler config_handlers[] = {
103 { "default_orientation", cmd_default_orientation }, 104 { "default_orientation", cmd_default_orientation },
104 { "include", cmd_include }, 105 { "include", cmd_include },
106 { "primary_selection", cmd_primary_selection },
105 { "swaybg_command", cmd_swaybg_command }, 107 { "swaybg_command", cmd_swaybg_command },
106 { "swaynag_command", cmd_swaynag_command }, 108 { "swaynag_command", cmd_swaynag_command },
107 { "workspace_layout", cmd_workspace_layout }, 109 { "workspace_layout", cmd_workspace_layout },
@@ -145,7 +147,7 @@ static int handler_compare(const void *_a, const void *_b) {
145 return strcasecmp(a->command, b->command); 147 return strcasecmp(a->command, b->command);
146} 148}
147 149
148const struct cmd_handler *find_handler(char *line, 150const struct cmd_handler *find_handler(const char *line,
149 const struct cmd_handler *handlers, size_t handlers_size) { 151 const struct cmd_handler *handlers, size_t handlers_size) {
150 if (!handlers || !handlers_size) { 152 if (!handlers || !handlers_size) {
151 return NULL; 153 return NULL;
@@ -378,10 +380,13 @@ struct cmd_results *config_command(char *exec, char **new_block) {
378 sway_log(SWAY_INFO, "Config command: %s", exec); 380 sway_log(SWAY_INFO, "Config command: %s", exec);
379 const struct cmd_handler *handler = find_core_handler(argv[0]); 381 const struct cmd_handler *handler = find_core_handler(argv[0]);
380 if (!handler || !handler->handle) { 382 if (!handler || !handler->handle) {
381 const char *error = handler 383 if (handler) {
382 ? "Command '%s' is shimmed, but unimplemented" 384 results = cmd_results_new(CMD_INVALID,
383 : "Unknown/invalid command '%s'"; 385 "Command '%s' is shimmed, but unimplemented", argv[0]);
384 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 }
385 goto cleanup; 390 goto cleanup;
386 } 391 }
387 392
@@ -407,6 +412,7 @@ struct cmd_results *config_command(char *exec, char **new_block) {
407 && handler->handle != cmd_bindsym 412 && handler->handle != cmd_bindsym
408 && handler->handle != cmd_bindcode 413 && handler->handle != cmd_bindcode
409 && handler->handle != cmd_bindswitch 414 && handler->handle != cmd_bindswitch
415 && handler->handle != cmd_bindgesture
410 && handler->handle != cmd_set 416 && handler->handle != cmd_set
411 && handler->handle != cmd_for_window 417 && handler->handle != cmd_for_window
412 && (*argv[i] == '\"' || *argv[i] == '\'')) { 418 && (*argv[i] == '\"' || *argv[i] == '\'')) {
@@ -482,14 +488,10 @@ struct cmd_results *cmd_results_new(enum cmd_status status,
482 } 488 }
483 results->status = status; 489 results->status = status;
484 if (format) { 490 if (format) {
485 char *error = malloc(256);
486 va_list args; 491 va_list args;
487 va_start(args, format); 492 va_start(args, format);
488 if (error) { 493 results->error = vformat_str(format, args);
489 vsnprintf(error, 256, format, args);
490 }
491 va_end(args); 494 va_end(args);
492 results->error = error;
493 } else { 495 } else {
494 results->error = NULL; 496 results->error = NULL;
495 } 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 8b661e3a..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"
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 7c2f423b..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"
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 26c99e63..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>
@@ -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,7 +548,8 @@ 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->trigger = SWAY_SWITCH_TRIGGER_ON; 555 binding->trigger = SWAY_SWITCH_TRIGGER_ON;
@@ -561,7 +562,7 @@ struct cmd_results *cmd_bind_or_unbind_switch(int argc, char **argv,
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 %s)", 564 "(expected switch state: unknown state %s)",
564 bindtype, split->items[1]); 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 77263145..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,
@@ -51,12 +50,7 @@ static struct cmd_results *handle_command(int argc, char **argv, char *cmd_name,
51 memcpy(class, &colors, sizeof(struct border_colors)); 50 memcpy(class, &colors, sizeof(struct border_colors));
52 51
53 if (config->active) { 52 if (config->active) {
54 root_for_each_container(rebuild_textures_iterator, NULL); 53 root_for_each_container(container_update_iterator, NULL);
55
56 for (int i = 0; i < root->outputs->length; ++i) {
57 struct sway_output *output = root->outputs->items[i];
58 output_damage_whole(output);
59 }
60 } 54 }
61 55
62 return cmd_results_new(CMD_SUCCESS, NULL); 56 return cmd_results_new(CMD_SUCCESS, NULL);
diff --git a/sway/commands/exec_always.c b/sway/commands/exec_always.c
index b35065c1..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>
@@ -8,6 +7,7 @@
8#include "sway/commands.h" 7#include "sway/commands.h"
9#include "sway/config.h" 8#include "sway/config.h"
10#include "sway/server.h" 9#include "sway/server.h"
10#include "sway/desktop/launcher.h"
11#include "sway/tree/container.h" 11#include "sway/tree/container.h"
12#include "sway/tree/root.h" 12#include "sway/tree/root.h"
13#include "sway/tree/workspace.h" 13#include "sway/tree/workspace.h"
@@ -25,11 +25,22 @@ struct cmd_results *cmd_exec_validate(int argc, char **argv) {
25 return error; 25 return error;
26} 26}
27 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
28struct cmd_results *cmd_exec_process(int argc, char **argv) { 38struct cmd_results *cmd_exec_process(int argc, char **argv) {
29 struct cmd_results *error = NULL; 39 struct cmd_results *error = NULL;
30 char *cmd = NULL; 40 char *cmd = NULL;
41 bool no_startup_id = false;
31 if (strcmp(argv[0], "--no-startup-id") == 0) { 42 if (strcmp(argv[0], "--no-startup-id") == 0) {
32 sway_log(SWAY_INFO, "exec switch '--no-startup-id' not supported, ignored."); 43 no_startup_id = true;
33 --argc; ++argv; 44 --argc; ++argv;
34 if ((error = checkarg(argc, argv[-1], EXPECTED_AT_LEAST, 1))) { 45 if ((error = checkarg(argc, argv[-1], EXPECTED_AT_LEAST, 1))) {
35 return error; 46 return error;
@@ -51,6 +62,7 @@ struct cmd_results *cmd_exec_process(int argc, char **argv) {
51 } 62 }
52 63
53 pid_t pid, child; 64 pid_t pid, child;
65 struct launcher_ctx *ctx = launcher_ctx_create_internal();
54 // Fork process 66 // Fork process
55 if ((pid = fork()) == 0) { 67 if ((pid = fork()) == 0) {
56 // Fork child process again 68 // Fork child process again
@@ -63,6 +75,12 @@ struct cmd_results *cmd_exec_process(int argc, char **argv) {
63 close(fd[0]); 75 close(fd[0]);
64 if ((child = fork()) == 0) { 76 if ((child = fork()) == 0) {
65 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 }
66 execlp("sh", "sh", "-c", cmd, (void *)NULL); 84 execlp("sh", "sh", "-c", cmd, (void *)NULL);
67 sway_log_errno(SWAY_ERROR, "execlp failed"); 85 sway_log_errno(SWAY_ERROR, "execlp failed");
68 _exit(1); 86 _exit(1);
@@ -90,8 +108,12 @@ struct cmd_results *cmd_exec_process(int argc, char **argv) {
90 waitpid(pid, NULL, 0); 108 waitpid(pid, NULL, 0);
91 if (child > 0) { 109 if (child > 0) {
92 sway_log(SWAY_DEBUG, "Child process created with pid %d", child); 110 sway_log(SWAY_DEBUG, "Child process created with pid %d", child);
93 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 }
94 } else { 115 } else {
116 launcher_ctx_destroy(ctx);
95 return cmd_results_new(CMD_FAILURE, "Second fork() failed"); 117 return cmd_results_new(CMD_FAILURE, "Second fork() failed");
96 } 118 }
97 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/font.c b/sway/commands/font.c
index cea720f5..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.
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;
25 config_update_font_height(); 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..3cea026e 100644
--- a/sway/commands/input/events.c
+++ b/sway/commands/input/events.c
@@ -1,14 +1,20 @@
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 "sway/server.h"
8#include "log.h" 9#include "log.h"
9 10
11#if WLR_HAS_LIBINPUT_BACKEND
12#include <wlr/backend/libinput.h>
13#endif
14
10static void toggle_supported_send_events_for_device(struct input_config *ic, 15static void toggle_supported_send_events_for_device(struct input_config *ic,
11 struct sway_input_device *input_device) { 16 struct sway_input_device *input_device) {
17#if WLR_HAS_LIBINPUT_BACKEND
12 struct wlr_input_device *wlr_device = input_device->wlr_device; 18 struct wlr_input_device *wlr_device = input_device->wlr_device;
13 if (!wlr_input_device_is_libinput(wlr_device)) { 19 if (!wlr_input_device_is_libinput(wlr_device)) {
14 return; 20 return;
@@ -41,6 +47,7 @@ static void toggle_supported_send_events_for_device(struct input_config *ic,
41 } 47 }
42 48
43 ic->send_events = mode; 49 ic->send_events = mode;
50#endif
44} 51}
45 52
46static int mode_for_name(const char *name) { 53static int mode_for_name(const char *name) {
@@ -56,6 +63,7 @@ static int mode_for_name(const char *name) {
56 63
57static void toggle_select_send_events_for_device(struct input_config *ic, 64static void toggle_select_send_events_for_device(struct input_config *ic,
58 struct sway_input_device *input_device, int argc, char **argv) { 65 struct sway_input_device *input_device, int argc, char **argv) {
66#if WLR_HAS_LIBINPUT_BACKEND
59 if (!wlr_input_device_is_libinput(input_device->wlr_device)) { 67 if (!wlr_input_device_is_libinput(input_device->wlr_device)) {
60 return; 68 return;
61 } 69 }
@@ -72,6 +80,7 @@ static void toggle_select_send_events_for_device(struct input_config *ic,
72 } 80 }
73 } 81 }
74 ic->send_events = mode_for_name(argv[index % argc]); 82 ic->send_events = mode_for_name(argv[index % argc]);
83#endif
75} 84}
76 85
77static void toggle_send_events(int argc, char **argv) { 86static 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 dabc6697..8d600fca 100644
--- a/sway/commands/input/xkb_switch_layout.c
+++ b/sway/commands/input/xkb_switch_layout.c
@@ -1,9 +1,9 @@
1#define _POSIX_C_SOURCE 200809L
2#include <assert.h> 1#include <assert.h>
3#include <wlr/interfaces/wlr_keyboard.h> 2#include <wlr/interfaces/wlr_keyboard.h>
4#include "sway/config.h" 3#include "sway/config.h"
5#include "sway/commands.h" 4#include "sway/commands.h"
6#include "sway/input/input-manager.h" 5#include "sway/input/input-manager.h"
6#include "sway/server.h"
7#include "log.h" 7#include "log.h"
8 8
9struct xkb_switch_layout_action { 9struct xkb_switch_layout_action {
@@ -98,10 +98,9 @@ struct cmd_results *input_cmd_xkb_switch_layout(int argc, char **argv) {
98 struct xkb_switch_layout_action *action = 98 struct xkb_switch_layout_action *action =
99 &actions[actions_len++]; 99 &actions[actions_len++];
100 100
101 action->keyboard = dev->wlr_device->keyboard; 101 action->keyboard = wlr_keyboard_from_input_device(dev->wlr_device);
102 if (relative) { 102 if (relative) {
103 action->layout = get_layout_relative( 103 action->layout = get_layout_relative(action->keyboard, relative);
104 dev->wlr_device->keyboard, relative);
105 } else { 104 } else {
106 action->layout = layout; 105 action->layout = layout;
107 } 106 }
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 0d0d9727..ff656cfb 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>
@@ -12,6 +11,7 @@
12#include "sway/input/seat.h" 11#include "sway/input/seat.h"
13#include "sway/ipc-server.h" 12#include "sway/ipc-server.h"
14#include "sway/output.h" 13#include "sway/output.h"
14#include "sway/server.h"
15#include "sway/tree/arrange.h" 15#include "sway/tree/arrange.h"
16#include "sway/tree/container.h" 16#include "sway/tree/container.h"
17#include "sway/tree/root.h" 17#include "sway/tree/root.h"
@@ -206,9 +206,17 @@ static void container_move_to_workspace(struct sway_container *container,
206 container_detach(container); 206 container_detach(container);
207 workspace_add_floating(workspace, container); 207 workspace_add_floating(workspace, container);
208 container_handle_fullscreen_reparent(container); 208 container_handle_fullscreen_reparent(container);
209 // If changing output, center it within the workspace 209 // If changing output, adjust the coordinates of the window.
210 if (old_output != workspace->output && !container->pending.fullscreen_mode) { 210 if (old_output != workspace->output && !container->pending.fullscreen_mode) {
211 container_floating_move_to_center(container); 211 struct wlr_box workspace_box, old_workspace_box;
212 workspace_get_box(workspace, &workspace_box);
213 workspace_get_box(old_workspace, &old_workspace_box);
214 floating_fix_coordinates(container, &old_workspace_box, &workspace_box);
215 if (container->scratchpad && workspace->output) {
216 struct wlr_box output_box;
217 output_get_box(workspace->output, &output_box);
218 container->transform = workspace_box;
219 }
212 } 220 }
213 } else { 221 } else {
214 container_detach(container); 222 container_detach(container);
@@ -462,7 +470,7 @@ static struct cmd_results *cmd_move_container(bool no_auto_back_and_forth,
462 if (strcasecmp(argv[1], "number") == 0) { 470 if (strcasecmp(argv[1], "number") == 0) {
463 // move [window|container] [to] "workspace number x" 471 // move [window|container] [to] "workspace number x"
464 if (argc < 3) { 472 if (argc < 3) {
465 return cmd_results_new(CMD_INVALID, expected_syntax); 473 return cmd_results_new(CMD_INVALID, "%s", expected_syntax);
466 } 474 }
467 if (!isdigit(argv[2][0])) { 475 if (!isdigit(argv[2][0])) {
468 return cmd_results_new(CMD_INVALID, 476 return cmd_results_new(CMD_INVALID,
@@ -522,7 +530,7 @@ static struct cmd_results *cmd_move_container(bool no_auto_back_and_forth,
522 } 530 }
523 destination = &dest_con->node; 531 destination = &dest_con->node;
524 } else { 532 } else {
525 return cmd_results_new(CMD_INVALID, expected_syntax); 533 return cmd_results_new(CMD_INVALID, "%s", expected_syntax);
526 } 534 }
527 535
528 if (destination->type == N_CONTAINER && 536 if (destination->type == N_CONTAINER &&
@@ -686,6 +694,9 @@ static struct cmd_results *cmd_move_workspace(int argc, char **argv) {
686 arrange_output(old_output); 694 arrange_output(old_output);
687 arrange_output(new_output); 695 arrange_output(new_output);
688 696
697 struct sway_seat *seat = config->handler_context.seat;
698 seat_consider_warp_to_focus(seat);
699
689 return cmd_results_new(CMD_SUCCESS, NULL); 700 return cmd_results_new(CMD_SUCCESS, NULL);
690} 701}
691 702
@@ -759,15 +770,6 @@ static struct cmd_results *cmd_move_in_direction(
759 ipc_event_window(container, "move"); 770 ipc_event_window(container, "move");
760 } 771 }
761 772
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); 773 container_end_mouse_operation(container);
772 774
773 return cmd_results_new(CMD_SUCCESS, NULL); 775 return cmd_results_new(CMD_SUCCESS, NULL);
@@ -818,7 +820,7 @@ static struct cmd_results *cmd_move_to_position(int argc, char **argv) {
818 } 820 }
819 821
820 if (!argc) { 822 if (!argc) {
821 return cmd_results_new(CMD_INVALID, expected_position_syntax); 823 return cmd_results_new(CMD_INVALID, "%s", expected_position_syntax);
822 } 824 }
823 825
824 bool absolute = false; 826 bool absolute = false;
@@ -828,19 +830,19 @@ static struct cmd_results *cmd_move_to_position(int argc, char **argv) {
828 ++argv; 830 ++argv;
829 } 831 }
830 if (!argc) { 832 if (!argc) {
831 return cmd_results_new(CMD_INVALID, expected_position_syntax); 833 return cmd_results_new(CMD_INVALID, "%s", expected_position_syntax);
832 } 834 }
833 if (strcmp(argv[0], "position") == 0) { 835 if (strcmp(argv[0], "position") == 0) {
834 --argc; 836 --argc;
835 ++argv; 837 ++argv;
836 } 838 }
837 if (!argc) { 839 if (!argc) {
838 return cmd_results_new(CMD_INVALID, expected_position_syntax); 840 return cmd_results_new(CMD_INVALID, "%s", expected_position_syntax);
839 } 841 }
840 if (strcmp(argv[0], "cursor") == 0 || strcmp(argv[0], "mouse") == 0 || 842 if (strcmp(argv[0], "cursor") == 0 || strcmp(argv[0], "mouse") == 0 ||
841 strcmp(argv[0], "pointer") == 0) { 843 strcmp(argv[0], "pointer") == 0) {
842 if (absolute) { 844 if (absolute) {
843 return cmd_results_new(CMD_INVALID, expected_position_syntax); 845 return cmd_results_new(CMD_INVALID, "%s", expected_position_syntax);
844 } 846 }
845 return cmd_move_to_position_pointer(container); 847 return cmd_move_to_position_pointer(container);
846 } else if (strcmp(argv[0], "center") == 0) { 848 } else if (strcmp(argv[0], "center") == 0) {
@@ -862,7 +864,7 @@ static struct cmd_results *cmd_move_to_position(int argc, char **argv) {
862 } 864 }
863 865
864 if (argc < 2) { 866 if (argc < 2) {
865 return cmd_results_new(CMD_FAILURE, expected_position_syntax); 867 return cmd_results_new(CMD_FAILURE, "%s", expected_position_syntax);
866 } 868 }
867 869
868 struct movement_amount lx = { .amount = 0, .unit = MOVEMENT_UNIT_INVALID }; 870 struct movement_amount lx = { .amount = 0, .unit = MOVEMENT_UNIT_INVALID };
@@ -875,7 +877,7 @@ static struct cmd_results *cmd_move_to_position(int argc, char **argv) {
875 } 877 }
876 878
877 if (argc < 1) { 879 if (argc < 1) {
878 return cmd_results_new(CMD_FAILURE, expected_position_syntax); 880 return cmd_results_new(CMD_FAILURE, "%s", expected_position_syntax);
879 } 881 }
880 882
881 struct movement_amount ly = { .amount = 0, .unit = MOVEMENT_UNIT_INVALID }; 883 struct movement_amount ly = { .amount = 0, .unit = MOVEMENT_UNIT_INVALID };
@@ -884,7 +886,7 @@ static struct cmd_results *cmd_move_to_position(int argc, char **argv) {
884 argc -= num_consumed_args; 886 argc -= num_consumed_args;
885 argv += num_consumed_args; 887 argv += num_consumed_args;
886 if (argc > 0) { 888 if (argc > 0) {
887 return cmd_results_new(CMD_INVALID, expected_position_syntax); 889 return cmd_results_new(CMD_INVALID, "%s", expected_position_syntax);
888 } 890 }
889 if (ly.unit == MOVEMENT_UNIT_INVALID) { 891 if (ly.unit == MOVEMENT_UNIT_INVALID) {
890 return cmd_results_new(CMD_INVALID, "Invalid y position specified"); 892 return cmd_results_new(CMD_INVALID, "Invalid y position specified");
@@ -1030,13 +1032,13 @@ struct cmd_results *cmd_move(int argc, char **argv) {
1030 } 1032 }
1031 1033
1032 if (!argc) { 1034 if (!argc) {
1033 return cmd_results_new(CMD_INVALID, expected_full_syntax); 1035 return cmd_results_new(CMD_INVALID, "%s", expected_full_syntax);
1034 } 1036 }
1035 1037
1036 // Only `move [window|container] [to] workspace` supports 1038 // Only `move [window|container] [to] workspace` supports
1037 // `--no-auto-back-and-forth` so treat others as invalid syntax 1039 // `--no-auto-back-and-forth` so treat others as invalid syntax
1038 if (no_auto_back_and_forth && strcasecmp(argv[0], "workspace") != 0) { 1040 if (no_auto_back_and_forth && strcasecmp(argv[0], "workspace") != 0) {
1039 return cmd_results_new(CMD_INVALID, expected_full_syntax); 1041 return cmd_results_new(CMD_INVALID, "%s", expected_full_syntax);
1040 } 1042 }
1041 1043
1042 if (strcasecmp(argv[0], "workspace") == 0 || 1044 if (strcasecmp(argv[0], "workspace") == 0 ||
@@ -1050,5 +1052,5 @@ struct cmd_results *cmd_move(int argc, char **argv) {
1050 strcasecmp(argv[1], "position") == 0)) { 1052 strcasecmp(argv[1], "position") == 0)) {
1051 return cmd_move_to_position(argc, argv); 1053 return cmd_move_to_position(argc, argv);
1052 } 1054 }
1053 return cmd_results_new(CMD_INVALID, expected_full_syntax); 1055 return cmd_results_new(CMD_INVALID, "%s", expected_full_syntax);
1054} 1056}
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 125df5a7..5e5d31b3 100644
--- a/sway/commands/output.c
+++ b/sway/commands/output.c
@@ -18,6 +18,7 @@ static const struct cmd_handler output_handlers[] = {
18 { "modeline", output_cmd_modeline }, 18 { "modeline", output_cmd_modeline },
19 { "pos", output_cmd_position }, 19 { "pos", output_cmd_position },
20 { "position", output_cmd_position }, 20 { "position", output_cmd_position },
21 { "power", output_cmd_power },
21 { "render_bit_depth", output_cmd_render_bit_depth }, 22 { "render_bit_depth", output_cmd_render_bit_depth },
22 { "res", output_cmd_mode }, 23 { "res", output_cmd_mode },
23 { "resolution", output_cmd_mode }, 24 { "resolution", output_cmd_mode },
@@ -26,6 +27,7 @@ static const struct cmd_handler output_handlers[] = {
26 { "subpixel", output_cmd_subpixel }, 27 { "subpixel", output_cmd_subpixel },
27 { "toggle", output_cmd_toggle }, 28 { "toggle", output_cmd_toggle },
28 { "transform", output_cmd_transform }, 29 { "transform", output_cmd_transform },
30 { "unplug", output_cmd_unplug },
29}; 31};
30 32
31struct cmd_results *cmd_output(int argc, char **argv) { 33struct cmd_results *cmd_output(int argc, char **argv) {
@@ -101,15 +103,18 @@ struct cmd_results *cmd_output(int argc, char **argv) {
101 103
102 bool background = output->background; 104 bool background = output->background;
103 105
104 output = store_output_config(output); 106 store_output_config(output);
105 107
106 // If reloading, the output configs will be applied after reading the 108 // If reloading, the output configs will be applied after reading the
107 // 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
108 // workspace name is not given to re-enabled outputs. 110 // workspace name is not given to re-enabled outputs.
109 if (!config->reloading && !config->validating) { 111 if (!config->reloading && !config->validating) {
110 apply_output_config_to_outputs(output); 112 apply_all_output_configs();
111 if (background) { 113 if (background) {
112 spawn_swaybg(); 114 if (!spawn_swaybg()) {
115 return cmd_results_new(CMD_FAILURE,
116 "Failed to apply background configuration");
117 }
113 } 118 }
114 } 119 }
115 120
diff --git a/sway/commands/output/background.c b/sway/commands/output/background.c
index 67f212ff..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>
@@ -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/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/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 76f14bba..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,7 +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 root_for_each_container(rebuild_textures_iterator, NULL); 49 root_for_each_container(title_bar_update_iterator, NULL);
52 50
53 arrange_root(); 51 arrange_root();
54} 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 504a9f5e..434e6bbb 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>
@@ -6,6 +5,7 @@
6#include <wlr/types/wlr_pointer.h> 5#include <wlr/types/wlr_pointer.h>
7#include "sway/commands.h" 6#include "sway/commands.h"
8#include "sway/input/cursor.h" 7#include "sway/input/cursor.h"
8#include "sway/server.h"
9 9
10static struct cmd_results *press_or_release(struct sway_cursor *cursor, 10static struct cmd_results *press_or_release(struct sway_cursor *cursor,
11 char *action, char *button_str); 11 char *action, char *button_str);
@@ -18,7 +18,7 @@ static struct cmd_results *handle_command(struct sway_cursor *cursor,
18 int argc, char **argv) { 18 int argc, char **argv) {
19 if (strcasecmp(argv[0], "move") == 0) { 19 if (strcasecmp(argv[0], "move") == 0) {
20 if (argc < 3) { 20 if (argc < 3) {
21 return cmd_results_new(CMD_INVALID, expected_syntax); 21 return cmd_results_new(CMD_INVALID, "%s", expected_syntax);
22 } 22 }
23 int delta_x = strtol(argv[1], NULL, 10); 23 int delta_x = strtol(argv[1], NULL, 10);
24 int delta_y = strtol(argv[2], NULL, 10); 24 int delta_y = strtol(argv[2], NULL, 10);
@@ -27,7 +27,7 @@ static struct cmd_results *handle_command(struct sway_cursor *cursor,
27 wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat); 27 wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat);
28 } else if (strcasecmp(argv[0], "set") == 0) { 28 } else if (strcasecmp(argv[0], "set") == 0) {
29 if (argc < 3) { 29 if (argc < 3) {
30 return cmd_results_new(CMD_INVALID, expected_syntax); 30 return cmd_results_new(CMD_INVALID, "%s", expected_syntax);
31 } 31 }
32 // map absolute coords (0..1,0..1) to root container coords 32 // map absolute coords (0..1,0..1) to root container coords
33 float x = strtof(argv[1], NULL) / root->width; 33 float x = strtof(argv[1], NULL) / root->width;
@@ -37,7 +37,7 @@ static struct cmd_results *handle_command(struct sway_cursor *cursor,
37 wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat); 37 wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat);
38 } else { 38 } else {
39 if (argc < 2) { 39 if (argc < 2) {
40 return cmd_results_new(CMD_INVALID, expected_syntax); 40 return cmd_results_new(CMD_INVALID, "%s", expected_syntax);
41 } 41 }
42 struct cmd_results *error = NULL; 42 struct cmd_results *error = NULL;
43 if ((error = press_or_release(cursor, argv[0], argv[1]))) { 43 if ((error = press_or_release(cursor, argv[0], argv[1]))) {
@@ -85,36 +85,36 @@ struct cmd_results *seat_cmd_cursor(int argc, char **argv) {
85 85
86static struct cmd_results *press_or_release(struct sway_cursor *cursor, 86static struct cmd_results *press_or_release(struct sway_cursor *cursor,
87 char *action, char *button_str) { 87 char *action, char *button_str) {
88 enum wlr_button_state state; 88 enum wl_pointer_button_state state;
89 uint32_t button; 89 uint32_t button;
90 if (strcasecmp(action, "press") == 0) { 90 if (strcasecmp(action, "press") == 0) {
91 state = WLR_BUTTON_PRESSED; 91 state = WL_POINTER_BUTTON_STATE_PRESSED;
92 } else if (strcasecmp(action, "release") == 0) { 92 } else if (strcasecmp(action, "release") == 0) {
93 state = WLR_BUTTON_RELEASED; 93 state = WL_POINTER_BUTTON_STATE_RELEASED;
94 } else { 94 } else {
95 return cmd_results_new(CMD_INVALID, expected_syntax); 95 return cmd_results_new(CMD_INVALID, "%s", expected_syntax);
96 } 96 }
97 97
98 char *message = NULL; 98 char *message = NULL;
99 button = get_mouse_button(button_str, &message); 99 button = get_mouse_button(button_str, &message);
100 if (message) { 100 if (message) {
101 struct cmd_results *error = 101 struct cmd_results *error =
102 cmd_results_new(CMD_INVALID, message); 102 cmd_results_new(CMD_INVALID, "%s", message);
103 free(message); 103 free(message);
104 return error; 104 return error;
105 } else if (button == SWAY_SCROLL_UP || button == SWAY_SCROLL_DOWN 105 } else if (button == SWAY_SCROLL_UP || button == SWAY_SCROLL_DOWN
106 || button == SWAY_SCROLL_LEFT || button == SWAY_SCROLL_RIGHT) { 106 || button == SWAY_SCROLL_LEFT || button == SWAY_SCROLL_RIGHT) {
107 // Dispatch axis event 107 // Dispatch axis event
108 enum wlr_axis_orientation orientation = 108 enum wl_pointer_axis orientation =
109 (button == SWAY_SCROLL_UP || button == SWAY_SCROLL_DOWN) 109 (button == SWAY_SCROLL_UP || button == SWAY_SCROLL_DOWN)
110 ? WLR_AXIS_ORIENTATION_VERTICAL 110 ? WL_POINTER_AXIS_VERTICAL_SCROLL
111 : WLR_AXIS_ORIENTATION_HORIZONTAL; 111 : WL_POINTER_AXIS_HORIZONTAL_SCROLL;
112 double delta = (button == SWAY_SCROLL_UP || button == SWAY_SCROLL_LEFT) 112 double delta = (button == SWAY_SCROLL_UP || button == SWAY_SCROLL_LEFT)
113 ? -1 : 1; 113 ? -1 : 1;
114 struct wlr_pointer_axis_event event = { 114 struct wlr_pointer_axis_event event = {
115 .pointer = NULL, 115 .pointer = NULL,
116 .time_msec = 0, 116 .time_msec = 0,
117 .source = WLR_AXIS_SOURCE_WHEEL, 117 .source = WL_POINTER_AXIS_SOURCE_WHEEL,
118 .orientation = orientation, 118 .orientation = orientation,
119 .delta = delta * 15, 119 .delta = delta * 15,
120 .delta_discrete = delta 120 .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/pointer_constraint.c b/sway/commands/seat/pointer_constraint.c
index 3890ebde..38f85bcd 100644
--- a/sway/commands/seat/pointer_constraint.c
+++ b/sway/commands/seat/pointer_constraint.c
@@ -4,6 +4,7 @@
4#include "sway/config.h" 4#include "sway/config.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/server.h"
7 8
8enum operation { 9enum operation {
9 OP_ENABLE, 10 OP_ENABLE,
diff --git a/sway/commands/seat/shortcuts_inhibitor.c b/sway/commands/seat/shortcuts_inhibitor.c
index 7c7f99cf..df68618d 100644
--- a/sway/commands/seat/shortcuts_inhibitor.c
+++ b/sway/commands/seat/shortcuts_inhibitor.c
@@ -2,6 +2,7 @@
2#include "sway/commands.h" 2#include "sway/commands.h"
3#include "sway/input/seat.h" 3#include "sway/input/seat.h"
4#include "sway/input/input-manager.h" 4#include "sway/input/input-manager.h"
5#include "sway/server.h"
5#include "util.h" 6#include "util.h"
6 7
7static struct cmd_results *handle_action(struct seat_config *sc, 8static struct cmd_results *handle_action(struct seat_config *sc,
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/shortcuts_inhibitor.c b/sway/commands/shortcuts_inhibitor.c
index ffa1a5c9..2dfd1b9f 100644
--- a/sway/commands/shortcuts_inhibitor.c
+++ b/sway/commands/shortcuts_inhibitor.c
@@ -3,6 +3,7 @@
3#include "sway/commands.h" 3#include "sway/commands.h"
4#include "sway/config.h" 4#include "sway/config.h"
5#include "sway/input/seat.h" 5#include "sway/input/seat.h"
6#include "sway/server.h"
6#include "sway/tree/container.h" 7#include "sway/tree/container.h"
7#include "sway/tree/view.h" 8#include "sway/tree/view.h"
8 9
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/split.c b/sway/commands/split.c
index c8a2cfc1..500a497d 100644
--- a/sway/commands/split.c
+++ b/sway/commands/split.c
@@ -32,7 +32,7 @@ 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() { 35static struct cmd_results *do_unsplit(void) {
36 struct sway_container *con = config->handler_context.container; 36 struct sway_container *con = config->handler_context.container;
37 struct sway_workspace *ws = config->handler_context.workspace; 37 struct sway_workspace *ws = config->handler_context.workspace;
38 38
diff --git a/sway/commands/swap.c b/sway/commands/swap.c
index 9355944d..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 if (fs1) {
130 container_fullscreen_disable(con1);
131 }
132 enum sway_fullscreen_mode fs2 = con2->pending.fullscreen_mode;
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) {
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 a2446b7e..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"
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 e4745a5c..5058efcc 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>
@@ -22,6 +23,7 @@
22#include "sway/config.h" 23#include "sway/config.h"
23#include "sway/criteria.h" 24#include "sway/criteria.h"
24#include "sway/desktop/transaction.h" 25#include "sway/desktop/transaction.h"
26#include "sway/server.h"
25#include "sway/swaynag.h" 27#include "sway/swaynag.h"
26#include "sway/tree/arrange.h" 28#include "sway/tree/arrange.h"
27#include "sway/tree/root.h" 29#include "sway/tree/root.h"
@@ -36,19 +38,26 @@
36struct sway_config *config = NULL; 38struct sway_config *config = NULL;
37 39
38static struct xkb_state *keysym_translation_state_create( 40static struct xkb_state *keysym_translation_state_create(
39 struct xkb_rule_names rules) { 41 struct xkb_rule_names rules, uint32_t context_flags) {
40 struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); 42 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( 43 struct xkb_keymap *xkb_keymap = xkb_keymap_new_from_names(
42 context, 44 context,
43 &rules, 45 &rules,
44 XKB_KEYMAP_COMPILE_NO_FLAGS); 46 XKB_KEYMAP_COMPILE_NO_FLAGS);
45
46 xkb_context_unref(context); 47 xkb_context_unref(context);
48 if (xkb_keymap == NULL) {
49 sway_log(SWAY_ERROR, "Failed to compile keysym translation XKB keymap");
50 return NULL;
51 }
52
47 return xkb_state_new(xkb_keymap); 53 return xkb_state_new(xkb_keymap);
48} 54}
49 55
50static void keysym_translation_state_destroy( 56static void keysym_translation_state_destroy(
51 struct xkb_state *state) { 57 struct xkb_state *state) {
58 if (state == NULL) {
59 return;
60 }
52 xkb_keymap_unref(xkb_state_get_keymap(state)); 61 xkb_keymap_unref(xkb_state_get_keymap(state));
53 xkb_state_unref(state); 62 xkb_state_unref(state);
54} 63}
@@ -82,6 +91,12 @@ static void free_mode(struct sway_mode *mode) {
82 } 91 }
83 list_free(mode->switch_bindings); 92 list_free(mode->switch_bindings);
84 } 93 }
94 if (mode->gesture_bindings) {
95 for (int i = 0; i < mode->gesture_bindings->length; i++) {
96 free_gesture_binding(mode->gesture_bindings->items[i]);
97 }
98 list_free(mode->gesture_bindings);
99 }
85 free(mode); 100 free(mode);
86} 101}
87 102
@@ -222,6 +237,7 @@ static void config_defaults(struct sway_config *config) {
222 if (!(config->current_mode->keycode_bindings = create_list())) goto cleanup; 237 if (!(config->current_mode->keycode_bindings = create_list())) goto cleanup;
223 if (!(config->current_mode->mouse_bindings = create_list())) goto cleanup; 238 if (!(config->current_mode->mouse_bindings = create_list())) goto cleanup;
224 if (!(config->current_mode->switch_bindings = create_list())) goto cleanup; 239 if (!(config->current_mode->switch_bindings = create_list())) goto cleanup;
240 if (!(config->current_mode->gesture_bindings = create_list())) goto cleanup;
225 list_add(config->modes, config->current_mode); 241 list_add(config->modes, config->current_mode);
226 242
227 config->floating_mod = 0; 243 config->floating_mod = 0;
@@ -236,6 +252,7 @@ static void config_defaults(struct sway_config *config) {
236 config->default_layout = L_NONE; 252 config->default_layout = L_NONE;
237 config->default_orientation = L_NONE; 253 config->default_orientation = L_NONE;
238 if (!(config->font = strdup("monospace 10"))) goto cleanup; 254 if (!(config->font = strdup("monospace 10"))) goto cleanup;
255 config->font_description = pango_font_description_from_string(config->font);
239 config->urgent_timeout = 500; 256 config->urgent_timeout = 500;
240 config->focus_on_window_activation = FOWA_URGENT; 257 config->focus_on_window_activation = FOWA_URGENT;
241 config->popup_during_fullscreen = POPUP_SMART; 258 config->popup_during_fullscreen = POPUP_SMART;
@@ -265,6 +282,7 @@ static void config_defaults(struct sway_config *config) {
265 config->title_align = ALIGN_LEFT; 282 config->title_align = ALIGN_LEFT;
266 config->tiling_drag = true; 283 config->tiling_drag = true;
267 config->tiling_drag_threshold = 9; 284 config->tiling_drag_threshold = 9;
285 config->primary_selection = true;
268 286
269 config->smart_gaps = SMART_GAPS_OFF; 287 config->smart_gaps = SMART_GAPS_OFF;
270 config->gaps_inner = 0; 288 config->gaps_inner = 0;
@@ -327,8 +345,14 @@ static void config_defaults(struct sway_config *config) {
327 345
328 // The keysym to keycode translation 346 // The keysym to keycode translation
329 struct xkb_rule_names rules = {0}; 347 struct xkb_rule_names rules = {0};
330 config->keysym_translation_state = 348 config->keysym_translation_state = keysym_translation_state_create(rules, 0);
331 keysym_translation_state_create(rules); 349 if (config->keysym_translation_state == NULL) {
350 config->keysym_translation_state = keysym_translation_state_create(rules,
351 XKB_CONTEXT_NO_ENVIRONMENT_NAMES);
352 }
353 if (config->keysym_translation_state == NULL) {
354 goto cleanup;
355 }
332 356
333 return; 357 return;
334cleanup: 358cleanup:
@@ -343,13 +367,7 @@ static char *config_path(const char *prefix, const char *config_folder) {
343 if (!prefix || !prefix[0] || !config_folder || !config_folder[0]) { 367 if (!prefix || !prefix[0] || !config_folder || !config_folder[0]) {
344 return NULL; 368 return NULL;
345 } 369 }
346 370 return format_str("%s/%s/config", prefix, config_folder);
347 const char *filename = "config";
348
349 size_t size = 3 + strlen(prefix) + strlen(config_folder) + strlen(filename);
350 char *path = calloc(size, sizeof(char));
351 snprintf(path, size, "%s/%s/%s", prefix, config_folder, filename);
352 return path;
353} 371}
354 372
355static char *get_config_path(void) { 373static char *get_config_path(void) {
@@ -359,10 +377,7 @@ static char *get_config_path(void) {
359 377
360 const char *config_home = getenv("XDG_CONFIG_HOME"); 378 const char *config_home = getenv("XDG_CONFIG_HOME");
361 if ((config_home == NULL || config_home[0] == '\0') && home != NULL) { 379 if ((config_home == NULL || config_home[0] == '\0') && home != NULL) {
362 size_t size_fallback = 1 + strlen(home) + strlen("/.config"); 380 config_home_fallback = format_str("%s/.config", home);
363 config_home_fallback = calloc(size_fallback, sizeof(char));
364 if (config_home_fallback != NULL)
365 snprintf(config_home_fallback, size_fallback, "%s/.config", home);
366 config_home = config_home_fallback; 381 config_home = config_home_fallback;
367 } 382 }
368 383
@@ -466,6 +481,11 @@ bool load_main_config(const char *file, bool is_active, bool validating) {
466 old_config->xwayland ? "enabled" : "disabled"); 481 old_config->xwayland ? "enabled" : "disabled");
467 config->xwayland = old_config->xwayland; 482 config->xwayland = old_config->xwayland;
468 483
484 // primary_selection can only be enabled/disabled at launch
485 sway_log(SWAY_DEBUG, "primary_selection will remain %s",
486 old_config->primary_selection ? "enabled" : "disabled");
487 config->primary_selection = old_config->primary_selection;
488
469 if (!config->validating) { 489 if (!config->validating) {
470 if (old_config->swaybg_client != NULL) { 490 if (old_config->swaybg_client != NULL) {
471 wl_client_destroy(old_config->swaybg_client); 491 wl_client_destroy(old_config->swaybg_client);
@@ -485,56 +505,7 @@ bool load_main_config(const char *file, bool is_active, bool validating) {
485 505
486 config->reading = true; 506 config->reading = true;
487 507
488 // Read security configs 508 bool success = load_config(path, config, &config->swaynag_config_errors);
489 // TODO: Security
490 bool success = true;
491 /*
492 DIR *dir = opendir(SYSCONFDIR "/sway/security.d");
493 if (!dir) {
494 sway_log(SWAY_ERROR,
495 "%s does not exist, sway will have no security configuration"
496 " and will probably be broken", SYSCONFDIR "/sway/security.d");
497 } else {
498 list_t *secconfigs = create_list();
499 char *base = SYSCONFDIR "/sway/security.d/";
500 struct dirent *ent = readdir(dir);
501 struct stat s;
502 while (ent != NULL) {
503 char *_path = malloc(strlen(ent->d_name) + strlen(base) + 1);
504 strcpy(_path, base);
505 strcat(_path, ent->d_name);
506 lstat(_path, &s);
507 if (S_ISREG(s.st_mode) && ent->d_name[0] != '.') {
508 list_add(secconfigs, _path);
509 }
510 else {
511 free(_path);
512 }
513 ent = readdir(dir);
514 }
515 closedir(dir);
516
517 list_qsort(secconfigs, qstrcmp);
518 for (int i = 0; i < secconfigs->length; ++i) {
519 char *_path = secconfigs->items[i];
520 if (stat(_path, &s) || s.st_uid != 0 || s.st_gid != 0 ||
521 (((s.st_mode & 0777) != 0644) &&
522 (s.st_mode & 0777) != 0444)) {
523 sway_log(SWAY_ERROR,
524 "Refusing to load %s - it must be owned by root "
525 "and mode 644 or 444", _path);
526 success = false;
527 } else {
528 success = success && load_config(_path, config);
529 }
530 }
531
532 list_free_items_and_destroy(secconfigs);
533 }
534 */
535
536 success = success && load_config(path, config,
537 &config->swaynag_config_errors);
538 509
539 if (validating) { 510 if (validating) {
540 free_config(config); 511 free_config(config);
@@ -562,7 +533,7 @@ bool load_main_config(const char *file, bool is_active, bool validating) {
562 } 533 }
563 sway_switch_retrigger_bindings_for_all(); 534 sway_switch_retrigger_bindings_for_all();
564 535
565 reset_outputs(); 536 apply_all_output_configs();
566 spawn_swaybg(); 537 spawn_swaybg();
567 538
568 config->reloading = false; 539 config->reloading = false;
@@ -915,23 +886,18 @@ void config_add_swaynag_warning(char *fmt, ...) {
915 if (config->reading && !config->validating) { 886 if (config->reading && !config->validating) {
916 va_list args; 887 va_list args;
917 va_start(args, fmt); 888 va_start(args, fmt);
918 size_t length = vsnprintf(NULL, 0, fmt, args) + 1; 889 char *str = vformat_str(fmt, args);
919 va_end(args); 890 va_end(args);
920 891 if (str == NULL) {
921 char *temp = malloc(length + 1);
922 if (!temp) {
923 sway_log(SWAY_ERROR, "Failed to allocate buffer for warning.");
924 return; 892 return;
925 } 893 }
926 894
927 va_start(args, fmt);
928 vsnprintf(temp, length, fmt, args);
929 va_end(args);
930
931 swaynag_log(config->swaynag_command, &config->swaynag_config_errors, 895 swaynag_log(config->swaynag_command, &config->swaynag_config_errors,
932 "Warning on line %i (%s) '%s': %s", 896 "Warning on line %i (%s) '%s': %s",
933 config->current_config_line_number, config->current_config_path, 897 config->current_config_line_number, config->current_config_path,
934 config->current_config_line, temp); 898 config->current_config_line, str);
899
900 free(str);
935 } 901 }
936} 902}
937 903
@@ -971,7 +937,7 @@ char *do_var_replacement(char *str) {
971 int offset = find - str; 937 int offset = find - str;
972 strncpy(newptr, str, offset); 938 strncpy(newptr, str, offset);
973 newptr += offset; 939 newptr += offset;
974 strncpy(newptr, var->value, vvlen); 940 memcpy(newptr, var->value, vvlen);
975 newptr += vvlen; 941 newptr += vvlen;
976 strcpy(newptr, find + vnlen); 942 strcpy(newptr, find + vnlen);
977 free(str); 943 free(str);
@@ -999,7 +965,7 @@ int workspace_output_cmp_workspace(const void *a, const void *b) {
999void config_update_font_height(void) { 965void config_update_font_height(void) {
1000 int prev_max_height = config->font_height; 966 int prev_max_height = config->font_height;
1001 967
1002 get_text_metrics(config->font, &config->font_height, &config->font_baseline); 968 get_text_metrics(config->font_description, &config->font_height, &config->font_baseline);
1003 969
1004 if (config->font_height != prev_max_height) { 970 if (config->font_height != prev_max_height) {
1005 arrange_root(); 971 arrange_root();
@@ -1033,8 +999,12 @@ void translate_keysyms(struct input_config *input_config) {
1033 999
1034 struct xkb_rule_names rules = {0}; 1000 struct xkb_rule_names rules = {0};
1035 input_config_fill_rule_names(input_config, &rules); 1001 input_config_fill_rule_names(input_config, &rules);
1036 config->keysym_translation_state = 1002 config->keysym_translation_state = keysym_translation_state_create(rules, 0);
1037 keysym_translation_state_create(rules); 1003 if (config->keysym_translation_state == NULL) {
1004 sway_log(SWAY_ERROR, "Failed to create keysym translation XKB state "
1005 "for device '%s'", input_config->identifier);
1006 return;
1007 }
1038 1008
1039 for (int i = 0; i < config->modes->length; ++i) { 1009 for (int i = 0; i < config->modes->length; ++i) {
1040 struct sway_mode *mode = config->modes->items[i]; 1010 struct sway_mode *mode = config->modes->items[i];
diff --git a/sway/config/bar.c b/sway/config/bar.c
index d1b342e6..ecefb61a 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>
@@ -13,6 +12,7 @@
13#include "sway/config.h" 12#include "sway/config.h"
14#include "sway/input/keyboard.h" 13#include "sway/input/keyboard.h"
15#include "sway/output.h" 14#include "sway/output.h"
15#include "sway/server.h"
16#include "config.h" 16#include "config.h"
17#include "list.h" 17#include "list.h"
18#include "log.h" 18#include "log.h"
@@ -256,7 +256,6 @@ static void invoke_swaybar(struct bar_config *bar) {
256 } 256 }
257 257
258 sway_log(SWAY_DEBUG, "Spawned swaybar %s", bar->id); 258 sway_log(SWAY_DEBUG, "Spawned swaybar %s", bar->id);
259 return;
260} 259}
261 260
262void load_swaybar(struct bar_config *bar) { 261void load_swaybar(struct bar_config *bar) {
diff --git a/sway/config/input.c b/sway/config/input.c
index a998e170..613270df 100644
--- a/sway/config/input.c
+++ b/sway/config/input.c
@@ -1,9 +1,9 @@
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>
5#include "sway/config.h" 4#include "sway/config.h"
6#include "sway/input/keyboard.h" 5#include "sway/input/keyboard.h"
6#include "sway/server.h"
7#include "log.h" 7#include "log.h"
8 8
9struct input_config *new_input_config(const char* identifier) { 9struct input_config *new_input_config(const char* identifier) {
@@ -25,14 +25,17 @@ struct input_config *new_input_config(const char* identifier) {
25 input->drag = INT_MIN; 25 input->drag = INT_MIN;
26 input->drag_lock = INT_MIN; 26 input->drag_lock = INT_MIN;
27 input->dwt = INT_MIN; 27 input->dwt = INT_MIN;
28 input->dwtp = INT_MIN;
28 input->send_events = INT_MIN; 29 input->send_events = INT_MIN;
29 input->click_method = INT_MIN; 30 input->click_method = INT_MIN;
30 input->middle_emulation = INT_MIN; 31 input->middle_emulation = INT_MIN;
31 input->natural_scroll = INT_MIN; 32 input->natural_scroll = INT_MIN;
32 input->accel_profile = INT_MIN; 33 input->accel_profile = INT_MIN;
34 input->rotation_angle = FLT_MIN;
33 input->pointer_accel = FLT_MIN; 35 input->pointer_accel = FLT_MIN;
34 input->scroll_factor = FLT_MIN; 36 input->scroll_factor = FLT_MIN;
35 input->scroll_button = INT_MIN; 37 input->scroll_button = INT_MIN;
38 input->scroll_button_lock = INT_MIN;
36 input->scroll_method = INT_MIN; 39 input->scroll_method = INT_MIN;
37 input->left_handed = INT_MIN; 40 input->left_handed = INT_MIN;
38 input->repeat_delay = INT_MIN; 41 input->repeat_delay = INT_MIN;
@@ -61,6 +64,9 @@ void merge_input_config(struct input_config *dst, struct input_config *src) {
61 if (src->dwt != INT_MIN) { 64 if (src->dwt != INT_MIN) {
62 dst->dwt = src->dwt; 65 dst->dwt = src->dwt;
63 } 66 }
67 if (src->dwtp != INT_MIN) {
68 dst->dwtp = src->dwtp;
69 }
64 if (src->left_handed != INT_MIN) { 70 if (src->left_handed != INT_MIN) {
65 dst->left_handed = src->left_handed; 71 dst->left_handed = src->left_handed;
66 } 72 }
@@ -70,6 +76,9 @@ void merge_input_config(struct input_config *dst, struct input_config *src) {
70 if (src->natural_scroll != INT_MIN) { 76 if (src->natural_scroll != INT_MIN) {
71 dst->natural_scroll = src->natural_scroll; 77 dst->natural_scroll = src->natural_scroll;
72 } 78 }
79 if (src->rotation_angle != FLT_MIN) {
80 dst->rotation_angle = src->rotation_angle;
81 }
73 if (src->pointer_accel != FLT_MIN) { 82 if (src->pointer_accel != FLT_MIN) {
74 dst->pointer_accel = src->pointer_accel; 83 dst->pointer_accel = src->pointer_accel;
75 } 84 }
@@ -88,6 +97,9 @@ void merge_input_config(struct input_config *dst, struct input_config *src) {
88 if (src->scroll_button != INT_MIN) { 97 if (src->scroll_button != INT_MIN) {
89 dst->scroll_button = src->scroll_button; 98 dst->scroll_button = src->scroll_button;
90 } 99 }
100 if (src->scroll_button_lock != INT_MIN) {
101 dst->scroll_button_lock = src->scroll_button_lock;
102 }
91 if (src->send_events != INT_MIN) { 103 if (src->send_events != INT_MIN) {
92 dst->send_events = src->send_events; 104 dst->send_events = src->send_events;
93 } 105 }
diff --git a/sway/config/output.c b/sway/config/output.c
index aa4cf946..9a447388 100644
--- a/sway/config/output.c
+++ b/sway/config/output.c
@@ -1,4 +1,3 @@
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 <stdbool.h> 3#include <stdbool.h>
@@ -6,17 +5,23 @@
6#include <sys/socket.h> 5#include <sys/socket.h>
7#include <sys/wait.h> 6#include <sys/wait.h>
8#include <unistd.h> 7#include <unistd.h>
8#include <wlr/config.h>
9#include <wlr/types/wlr_cursor.h> 9#include <wlr/types/wlr_cursor.h>
10#include <wlr/types/wlr_output_layout.h> 10#include <wlr/types/wlr_output_layout.h>
11#include <wlr/types/wlr_output.h> 11#include <wlr/types/wlr_output.h>
12#include <wlr/backend/drm.h> 12#include <wlr/types/wlr_output_swapchain_manager.h>
13#include "sway/config.h" 13#include "sway/config.h"
14#include "sway/input/cursor.h" 14#include "sway/input/cursor.h"
15#include "sway/output.h" 15#include "sway/output.h"
16#include "sway/server.h"
16#include "sway/tree/root.h" 17#include "sway/tree/root.h"
17#include "log.h" 18#include "log.h"
18#include "util.h" 19#include "util.h"
19 20
21#if WLR_HAS_DRM_BACKEND
22#include <wlr/backend/drm.h>
23#endif
24
20int output_name_cmp(const void *item, const void *data) { 25int output_name_cmp(const void *item, const void *data) {
21 const struct output_config *output = item; 26 const struct output_config *output = item;
22 const char *name = data; 27 const char *name = data;
@@ -71,10 +76,76 @@ struct output_config *new_output_config(const char *name) {
71 oc->max_render_time = -1; 76 oc->max_render_time = -1;
72 oc->adaptive_sync = -1; 77 oc->adaptive_sync = -1;
73 oc->render_bit_depth = RENDER_BIT_DEPTH_DEFAULT; 78 oc->render_bit_depth = RENDER_BIT_DEPTH_DEFAULT;
79 oc->power = -1;
74 return oc; 80 return oc;
75} 81}
76 82
77void merge_output_config(struct output_config *dst, struct output_config *src) { 83// supersede_output_config clears all fields in dst that were set in src
84static void supersede_output_config(struct output_config *dst, struct output_config *src) {
85 if (src->enabled != -1) {
86 dst->enabled = -1;
87 }
88 if (src->width != -1) {
89 dst->width = -1;
90 }
91 if (src->height != -1) {
92 dst->height = -1;
93 }
94 if (src->x != -1) {
95 dst->x = -1;
96 }
97 if (src->y != -1) {
98 dst->y = -1;
99 }
100 if (src->scale != -1) {
101 dst->scale = -1;
102 }
103 if (src->scale_filter != SCALE_FILTER_DEFAULT) {
104 dst->scale_filter = SCALE_FILTER_DEFAULT;
105 }
106 if (src->subpixel != WL_OUTPUT_SUBPIXEL_UNKNOWN) {
107 dst->subpixel = WL_OUTPUT_SUBPIXEL_UNKNOWN;
108 }
109 if (src->refresh_rate != -1) {
110 dst->refresh_rate = -1;
111 }
112 if (src->custom_mode != -1) {
113 dst->custom_mode = -1;
114 }
115 if (src->drm_mode.type != (uint32_t) -1) {
116 dst->drm_mode.type = -1;
117 }
118 if (src->transform != -1) {
119 dst->transform = -1;
120 }
121 if (src->max_render_time != -1) {
122 dst->max_render_time = -1;
123 }
124 if (src->adaptive_sync != -1) {
125 dst->adaptive_sync = -1;
126 }
127 if (src->render_bit_depth != RENDER_BIT_DEPTH_DEFAULT) {
128 dst->render_bit_depth = RENDER_BIT_DEPTH_DEFAULT;
129 }
130 if (src->background) {
131 free(dst->background);
132 dst->background = NULL;
133 }
134 if (src->background_option) {
135 free(dst->background_option);
136 dst->background_option = NULL;
137 }
138 if (src->background_fallback) {
139 free(dst->background_fallback);
140 dst->background_fallback = NULL;
141 }
142 if (src->power != -1) {
143 dst->power = -1;
144 }
145}
146
147// merge_output_config sets all fields in dst that were set in src
148static void merge_output_config(struct output_config *dst, struct output_config *src) {
78 if (src->enabled != -1) { 149 if (src->enabled != -1) {
79 dst->enabled = src->enabled; 150 dst->enabled = src->enabled;
80 } 151 }
@@ -132,158 +203,112 @@ void merge_output_config(struct output_config *dst, struct output_config *src) {
132 free(dst->background_fallback); 203 free(dst->background_fallback);
133 dst->background_fallback = strdup(src->background_fallback); 204 dst->background_fallback = strdup(src->background_fallback);
134 } 205 }
135 if (src->dpms_state != 0) { 206 if (src->power != -1) {
136 dst->dpms_state = src->dpms_state; 207 dst->power = src->power;
137 } 208 }
138} 209}
139 210
140static void merge_wildcard_on_all(struct output_config *wildcard) { 211void store_output_config(struct output_config *oc) {
141 for (int i = 0; i < config->output_configs->length; i++) { 212 bool merged = false;
142 struct output_config *oc = config->output_configs->items[i]; 213 bool wildcard = strcmp(oc->name, "*") == 0;
143 if (strcmp(wildcard->name, oc->name) != 0) { 214 struct sway_output *output = wildcard ? NULL : all_output_by_name_or_id(oc->name);
144 sway_log(SWAY_DEBUG, "Merging output * config on %s", oc->name);
145 merge_output_config(oc, wildcard);
146 }
147 }
148}
149 215
150static void merge_id_on_name(struct output_config *oc) {
151 char *id_on_name = NULL;
152 char id[128]; 216 char id[128];
153 char *name = NULL; 217 if (output) {
154 struct sway_output *output;
155 wl_list_for_each(output, &root->all_outputs, link) {
156 name = output->wlr_output->name;
157 output_get_identifier(id, sizeof(id), output); 218 output_get_identifier(id, sizeof(id), output);
158 if (strcmp(name, oc->name) == 0 || strcmp(id, oc->name) == 0) {
159 size_t length = snprintf(NULL, 0, "%s on %s", id, name) + 1;
160 id_on_name = malloc(length);
161 if (!id_on_name) {
162 sway_log(SWAY_ERROR, "Failed to allocate id on name string");
163 return;
164 }
165 snprintf(id_on_name, length, "%s on %s", id, name);
166 break;
167 }
168 }
169
170 if (!id_on_name) {
171 return;
172 } 219 }
173 220
174 int i = list_seq_find(config->output_configs, output_name_cmp, id_on_name); 221 for (int i = 0; i < config->output_configs->length; i++) {
175 if (i >= 0) { 222 struct output_config *old = config->output_configs->items[i];
176 sway_log(SWAY_DEBUG, "Merging on top of existing id on name config"); 223
177 merge_output_config(config->output_configs->items[i], oc); 224 // If the old config matches the new config's name, regardless of
178 } else { 225 // whether it was name or identifier, merge on top of the existing
179 // If both a name and identifier config, exist generate an id on name 226 // config. If the new config is a wildcard, this also merges on top of
180 int ni = list_seq_find(config->output_configs, output_name_cmp, name); 227 // old wildcard configs.
181 int ii = list_seq_find(config->output_configs, output_name_cmp, id); 228 if (strcmp(old->name, oc->name) == 0) {
182 if ((ni >= 0 && ii >= 0) || (ni >= 0 && strcmp(oc->name, id) == 0) 229 merge_output_config(old, oc);
183 || (ii >= 0 && strcmp(oc->name, name) == 0)) { 230 merged = true;
184 struct output_config *ion_oc = new_output_config(id_on_name); 231 continue;
185 if (ni >= 0) {
186 merge_output_config(ion_oc, config->output_configs->items[ni]);
187 }
188 if (ii >= 0) {
189 merge_output_config(ion_oc, config->output_configs->items[ii]);
190 }
191 merge_output_config(ion_oc, oc);
192 list_add(config->output_configs, ion_oc);
193 sway_log(SWAY_DEBUG, "Generated id on name output config \"%s\""
194 " (enabled: %d) (%dx%d@%fHz position %d,%d scale %f "
195 "transform %d) (bg %s %s) (dpms %d) (max render time: %d)",
196 ion_oc->name, ion_oc->enabled, ion_oc->width, ion_oc->height,
197 ion_oc->refresh_rate, ion_oc->x, ion_oc->y, ion_oc->scale,
198 ion_oc->transform, ion_oc->background,
199 ion_oc->background_option, ion_oc->dpms_state,
200 ion_oc->max_render_time);
201 } 232 }
202 }
203 free(id_on_name);
204}
205 233
206struct output_config *store_output_config(struct output_config *oc) { 234 // If the new config is a wildcard config we supersede all non-wildcard
207 bool wildcard = strcmp(oc->name, "*") == 0; 235 // configs. Old wildcard configs have already been handled above.
208 if (wildcard) { 236 if (wildcard) {
209 merge_wildcard_on_all(oc); 237 supersede_output_config(old, oc);
210 } else { 238 continue;
211 merge_id_on_name(oc); 239 }
212 }
213 240
214 int i = list_seq_find(config->output_configs, output_name_cmp, oc->name); 241 // If the new config matches an output's name, and the old config
215 if (i >= 0) { 242 // matches on that output's identifier, supersede it.
216 sway_log(SWAY_DEBUG, "Merging on top of existing output config"); 243 if (output && strcmp(old->name, id) == 0 &&
217 struct output_config *current = config->output_configs->items[i]; 244 strcmp(oc->name, output->wlr_output->name) == 0) {
218 merge_output_config(current, oc); 245 supersede_output_config(old, oc);
219 free_output_config(oc);
220 oc = current;
221 } else if (!wildcard) {
222 sway_log(SWAY_DEBUG, "Adding non-wildcard output config");
223 i = list_seq_find(config->output_configs, output_name_cmp, "*");
224 if (i >= 0) {
225 sway_log(SWAY_DEBUG, "Merging on top of output * config");
226 struct output_config *current = new_output_config(oc->name);
227 merge_output_config(current, config->output_configs->items[i]);
228 merge_output_config(current, oc);
229 free_output_config(oc);
230 oc = current;
231 } 246 }
232 list_add(config->output_configs, oc);
233 } else {
234 // New wildcard config. Just add it
235 sway_log(SWAY_DEBUG, "Adding output * config");
236 list_add(config->output_configs, oc);
237 } 247 }
238 248
239 sway_log(SWAY_DEBUG, "Config stored for output %s (enabled: %d) (%dx%d@%fHz " 249 sway_log(SWAY_DEBUG, "Config stored for output %s (enabled: %d) (%dx%d@%fHz "
240 "position %d,%d scale %f subpixel %s transform %d) (bg %s %s) (dpms %d) " 250 "position %d,%d scale %f subpixel %s transform %d) (bg %s %s) (power %d) "
241 "(max render time: %d)", 251 "(max render time: %d)",
242 oc->name, oc->enabled, oc->width, oc->height, oc->refresh_rate, 252 oc->name, oc->enabled, oc->width, oc->height, oc->refresh_rate,
243 oc->x, oc->y, oc->scale, sway_wl_output_subpixel_to_string(oc->subpixel), 253 oc->x, oc->y, oc->scale, sway_wl_output_subpixel_to_string(oc->subpixel),
244 oc->transform, oc->background, oc->background_option, oc->dpms_state, 254 oc->transform, oc->background, oc->background_option, oc->power,
245 oc->max_render_time); 255 oc->max_render_time);
246 256
247 return oc; 257 // If the configuration was not merged into an existing configuration, add
258 // it to the list. Otherwise we're done with it and can free it.
259 if (!merged) {
260 list_add(config->output_configs, oc);
261 } else {
262 free_output_config(oc);
263 }
248} 264}
249 265
250static void set_mode(struct wlr_output *output, int width, int height, 266static void set_mode(struct wlr_output *output, struct wlr_output_state *pending,
251 float refresh_rate, bool custom) { 267 int width, int height, float refresh_rate, bool custom) {
252 // Not all floating point integers can be represented exactly 268 // Not all floating point integers can be represented exactly
253 // as (int)(1000 * mHz / 1000.f) 269 // as (int)(1000 * mHz / 1000.f)
254 // round() the result to avoid any error 270 // round() the result to avoid any error
255 int mhz = (int)round(refresh_rate * 1000); 271 int mhz = (int)roundf(refresh_rate * 1000);
272 // If no target refresh rate is given, match highest available
273 mhz = mhz <= 0 ? INT_MAX : mhz;
256 274
257 if (wl_list_empty(&output->modes) || custom) { 275 if (wl_list_empty(&output->modes) || custom) {
258 sway_log(SWAY_DEBUG, "Assigning custom mode to %s", output->name); 276 sway_log(SWAY_DEBUG, "Assigning custom mode to %s", output->name);
259 wlr_output_set_custom_mode(output, width, height, 277 wlr_output_state_set_custom_mode(pending, width, height,
260 refresh_rate > 0 ? mhz : 0); 278 refresh_rate > 0 ? mhz : 0);
261 return; 279 return;
262 } 280 }
263 281
264 struct wlr_output_mode *mode, *best = NULL; 282 struct wlr_output_mode *mode, *best = NULL;
283 int best_diff_mhz = INT_MAX;
265 wl_list_for_each(mode, &output->modes, link) { 284 wl_list_for_each(mode, &output->modes, link) {
266 if (mode->width == width && mode->height == height) { 285 if (mode->width == width && mode->height == height) {
267 if (mode->refresh == mhz) { 286 int diff_mhz = abs(mode->refresh - mhz);
268 best = mode; 287 if (diff_mhz < best_diff_mhz) {
269 break; 288 best_diff_mhz = diff_mhz;
270 }
271 if (best == NULL || mode->refresh > best->refresh) {
272 best = mode; 289 best = mode;
290 if (best_diff_mhz == 0) {
291 break;
292 }
273 } 293 }
274 } 294 }
275 } 295 }
276 if (!best) { 296 if (best) {
277 sway_log(SWAY_ERROR, "Configured mode for %s not available", output->name); 297 sway_log(SWAY_INFO, "Assigning configured mode (%dx%d@%.3fHz) to %s",
278 sway_log(SWAY_INFO, "Picking preferred mode instead"); 298 best->width, best->height, best->refresh / 1000.f, output->name);
279 best = wlr_output_preferred_mode(output);
280 } else { 299 } else {
281 sway_log(SWAY_DEBUG, "Assigning configured mode to %s", output->name); 300 best = wlr_output_preferred_mode(output);
301 sway_log(SWAY_INFO, "Configured mode (%dx%d@%.3fHz) not available, "
302 "applying preferred mode (%dx%d@%.3fHz)",
303 width, height, refresh_rate,
304 best->width, best->height, best->refresh / 1000.f);
282 } 305 }
283 wlr_output_set_mode(output, best); 306 wlr_output_state_set_mode(pending, best);
284} 307}
285 308
286static void set_modeline(struct wlr_output *output, drmModeModeInfo *drm_mode) { 309static void set_modeline(struct wlr_output *output,
310 struct wlr_output_state *pending, drmModeModeInfo *drm_mode) {
311#if WLR_HAS_DRM_BACKEND
287 if (!wlr_output_is_drm(output)) { 312 if (!wlr_output_is_drm(output)) {
288 sway_log(SWAY_ERROR, "Modeline can only be set to DRM output"); 313 sway_log(SWAY_ERROR, "Modeline can only be set to DRM output");
289 return; 314 return;
@@ -291,8 +316,11 @@ static void set_modeline(struct wlr_output *output, drmModeModeInfo *drm_mode) {
291 sway_log(SWAY_DEBUG, "Assigning custom modeline to %s", output->name); 316 sway_log(SWAY_DEBUG, "Assigning custom modeline to %s", output->name);
292 struct wlr_output_mode *mode = wlr_drm_connector_add_mode(output, drm_mode); 317 struct wlr_output_mode *mode = wlr_drm_connector_add_mode(output, drm_mode);
293 if (mode) { 318 if (mode) {
294 wlr_output_set_mode(output, mode); 319 wlr_output_state_set_mode(pending, mode);
295 } 320 }
321#else
322 sway_log(SWAY_ERROR, "Modeline can only be set to DRM output");
323#endif
296} 324}
297 325
298/* Some manufacturers hardcode the aspect-ratio of the output in the physical 326/* Some manufacturers hardcode the aspect-ratio of the output in the physical
@@ -313,23 +341,24 @@ static bool phys_size_is_aspect_ratio(struct wlr_output *output) {
313// 1 inch = 25.4 mm 341// 1 inch = 25.4 mm
314#define MM_PER_INCH 25.4 342#define MM_PER_INCH 25.4
315 343
316static int compute_default_scale(struct wlr_output *output) { 344static int compute_default_scale(struct wlr_output *output,
345 struct wlr_output_state *pending) {
317 struct wlr_box box = { .width = output->width, .height = output->height }; 346 struct wlr_box box = { .width = output->width, .height = output->height };
318 if (output->pending.committed & WLR_OUTPUT_STATE_MODE) { 347 if (pending->committed & WLR_OUTPUT_STATE_MODE) {
319 switch (output->pending.mode_type) { 348 switch (pending->mode_type) {
320 case WLR_OUTPUT_STATE_MODE_FIXED: 349 case WLR_OUTPUT_STATE_MODE_FIXED:
321 box.width = output->pending.mode->width; 350 box.width = pending->mode->width;
322 box.height = output->pending.mode->height; 351 box.height = pending->mode->height;
323 break; 352 break;
324 case WLR_OUTPUT_STATE_MODE_CUSTOM: 353 case WLR_OUTPUT_STATE_MODE_CUSTOM:
325 box.width = output->pending.custom_mode.width; 354 box.width = pending->custom_mode.width;
326 box.height = output->pending.custom_mode.height; 355 box.height = pending->custom_mode.height;
327 break; 356 break;
328 } 357 }
329 } 358 }
330 enum wl_output_transform transform = output->transform; 359 enum wl_output_transform transform = output->transform;
331 if (output->pending.committed & WLR_OUTPUT_STATE_TRANSFORM) { 360 if (pending->committed & WLR_OUTPUT_STATE_TRANSFORM) {
332 transform = output->pending.transform; 361 transform = pending->transform;
333 } 362 }
334 wlr_box_transform(&box, &box, transform, box.width, box.height); 363 wlr_box_transform(&box, &box, transform, box.width, box.height);
335 364
@@ -358,88 +387,70 @@ static int compute_default_scale(struct wlr_output *output) {
358 return 2; 387 return 2;
359} 388}
360 389
361/* Lists of formats to try, in order, when a specific render bit depth has 390static bool render_format_is_10bit(uint32_t render_format) {
362 * been asked for. The second to last format in each list should always 391 return render_format == DRM_FORMAT_XRGB2101010 ||
363 * be XRGB8888, as a reliable backup in case the others are not available; 392 render_format == DRM_FORMAT_XBGR2101010;
364 * the last should be DRM_FORMAT_INVALID, to indicate the end of the list. */ 393}
365static const uint32_t *bit_depth_preferences[] = { 394
366 [RENDER_BIT_DEPTH_8] = (const uint32_t []){ 395static bool render_format_is_bgr(uint32_t fmt) {
367 DRM_FORMAT_XRGB8888, 396 return fmt == DRM_FORMAT_XBGR2101010 || fmt == DRM_FORMAT_XBGR8888;
368 DRM_FORMAT_INVALID, 397}
369 }, 398
370 [RENDER_BIT_DEPTH_10] = (const uint32_t []){ 399static bool output_config_is_disabling(struct output_config *oc) {
371 DRM_FORMAT_XRGB2101010, 400 return oc && (!oc->enabled || oc->power == 0);
372 DRM_FORMAT_XBGR2101010, 401}
373 DRM_FORMAT_XRGB8888,
374 DRM_FORMAT_INVALID,
375 },
376};
377 402
378static void queue_output_config(struct output_config *oc, 403static void queue_output_config(struct output_config *oc,
379 struct sway_output *output) { 404 struct sway_output *output, struct wlr_output_state *pending) {
380 if (output == root->fallback_output) { 405 if (output == root->fallback_output) {
381 return; 406 return;
382 } 407 }
383 408
384 struct wlr_output *wlr_output = output->wlr_output; 409 struct wlr_output *wlr_output = output->wlr_output;
385 410
386 if (oc && (!oc->enabled || oc->dpms_state == DPMS_OFF)) { 411 if (output_config_is_disabling(oc)) {
387 sway_log(SWAY_DEBUG, "Turning off output %s", wlr_output->name); 412 sway_log(SWAY_DEBUG, "Turning off output %s", wlr_output->name);
388 wlr_output_enable(wlr_output, false); 413 wlr_output_state_set_enabled(pending, false);
389 return; 414 return;
390 } 415 }
391 416
392 sway_log(SWAY_DEBUG, "Turning on output %s", wlr_output->name); 417 sway_log(SWAY_DEBUG, "Turning on output %s", wlr_output->name);
393 wlr_output_enable(wlr_output, true); 418 wlr_output_state_set_enabled(pending, true);
394 419
395 if (oc && oc->drm_mode.type != 0 && oc->drm_mode.type != (uint32_t) -1) { 420 if (oc && oc->drm_mode.type != 0 && oc->drm_mode.type != (uint32_t) -1) {
396 sway_log(SWAY_DEBUG, "Set %s modeline", 421 sway_log(SWAY_DEBUG, "Set %s modeline",
397 wlr_output->name); 422 wlr_output->name);
398 set_modeline(wlr_output, &oc->drm_mode); 423 set_modeline(wlr_output, pending, &oc->drm_mode);
399 } else if (oc && oc->width > 0 && oc->height > 0) { 424 } else if (oc && oc->width > 0 && oc->height > 0) {
400 sway_log(SWAY_DEBUG, "Set %s mode to %dx%d (%f Hz)", 425 sway_log(SWAY_DEBUG, "Set %s mode to %dx%d (%f Hz)",
401 wlr_output->name, oc->width, oc->height, oc->refresh_rate); 426 wlr_output->name, oc->width, oc->height, oc->refresh_rate);
402 set_mode(wlr_output, oc->width, oc->height, 427 set_mode(wlr_output, pending, oc->width, oc->height,
403 oc->refresh_rate, oc->custom_mode == 1); 428 oc->refresh_rate, oc->custom_mode == 1);
404 } else if (!wl_list_empty(&wlr_output->modes)) { 429 } else if (!wl_list_empty(&wlr_output->modes)) {
405 sway_log(SWAY_DEBUG, "Set preferred mode"); 430 sway_log(SWAY_DEBUG, "Set preferred mode");
406 struct wlr_output_mode *preferred_mode = 431 struct wlr_output_mode *preferred_mode =
407 wlr_output_preferred_mode(wlr_output); 432 wlr_output_preferred_mode(wlr_output);
408 wlr_output_set_mode(wlr_output, preferred_mode); 433 wlr_output_state_set_mode(pending, preferred_mode);
409
410 if (!wlr_output_test(wlr_output)) {
411 sway_log(SWAY_DEBUG, "Preferred mode rejected, "
412 "falling back to another mode");
413 struct wlr_output_mode *mode;
414 wl_list_for_each(mode, &wlr_output->modes, link) {
415 if (mode == preferred_mode) {
416 continue;
417 }
418
419 wlr_output_set_mode(wlr_output, mode);
420 if (wlr_output_test(wlr_output)) {
421 break;
422 }
423 }
424 }
425 } 434 }
426 435
427 if (oc && (oc->subpixel != WL_OUTPUT_SUBPIXEL_UNKNOWN || config->reloading)) { 436 if (oc && (oc->subpixel != WL_OUTPUT_SUBPIXEL_UNKNOWN || config->reloading)) {
428 sway_log(SWAY_DEBUG, "Set %s subpixel to %s", oc->name, 437 sway_log(SWAY_DEBUG, "Set %s subpixel to %s", oc->name,
429 sway_wl_output_subpixel_to_string(oc->subpixel)); 438 sway_wl_output_subpixel_to_string(oc->subpixel));
430 wlr_output_set_subpixel(wlr_output, oc->subpixel); 439 wlr_output_state_set_subpixel(pending, oc->subpixel);
431 } 440 }
432 441
433 enum wl_output_transform tr = WL_OUTPUT_TRANSFORM_NORMAL; 442 enum wl_output_transform tr = WL_OUTPUT_TRANSFORM_NORMAL;
434 if (oc && oc->transform >= 0) { 443 if (oc && oc->transform >= 0) {
435 tr = oc->transform; 444 tr = oc->transform;
445#if WLR_HAS_DRM_BACKEND
436 } else if (wlr_output_is_drm(wlr_output)) { 446 } else if (wlr_output_is_drm(wlr_output)) {
437 tr = wlr_drm_connector_get_panel_orientation(wlr_output); 447 tr = wlr_drm_connector_get_panel_orientation(wlr_output);
438 sway_log(SWAY_DEBUG, "Auto-detected output transform: %d", tr); 448 sway_log(SWAY_DEBUG, "Auto-detected output transform: %d", tr);
449#endif
439 } 450 }
440 if (wlr_output->transform != tr) { 451 if (wlr_output->transform != tr) {
441 sway_log(SWAY_DEBUG, "Set %s transform to %d", oc->name, tr); 452 sway_log(SWAY_DEBUG, "Set %s transform to %d", wlr_output->name, tr);
442 wlr_output_set_transform(wlr_output, tr); 453 wlr_output_state_set_transform(pending, tr);
443 } 454 }
444 455
445 // Apply the scale last before the commit, because the scale auto-detection 456 // Apply the scale last before the commit, because the scale auto-detection
@@ -447,66 +458,50 @@ static void queue_output_config(struct output_config *oc,
447 float scale; 458 float scale;
448 if (oc && oc->scale > 0) { 459 if (oc && oc->scale > 0) {
449 scale = oc->scale; 460 scale = oc->scale;
461
462 // The factional-scale-v1 protocol uses increments of 120ths to send
463 // the scale factor to the client. Adjust the scale so that we use the
464 // same value as the clients'.
465 float adjusted_scale = round(scale * 120) / 120;
466 if (scale != adjusted_scale) {
467 sway_log(SWAY_INFO, "Adjusting output scale from %f to %f",
468 scale, adjusted_scale);
469 scale = adjusted_scale;
470 }
450 } else { 471 } else {
451 scale = compute_default_scale(wlr_output); 472 scale = compute_default_scale(wlr_output, pending);
452 sway_log(SWAY_DEBUG, "Auto-detected output scale: %f", scale); 473 sway_log(SWAY_DEBUG, "Auto-detected output scale: %f", scale);
453 } 474 }
454 if (scale != wlr_output->scale) { 475 if (scale != wlr_output->scale) {
455 sway_log(SWAY_DEBUG, "Set %s scale to %f", wlr_output->name, scale); 476 sway_log(SWAY_DEBUG, "Set %s scale to %f", wlr_output->name, scale);
456 wlr_output_set_scale(wlr_output, scale); 477 wlr_output_state_set_scale(pending, scale);
457 } 478 }
458 479
459 if (oc && oc->adaptive_sync != -1) { 480 if (oc && oc->adaptive_sync != -1) {
460 sway_log(SWAY_DEBUG, "Set %s adaptive sync to %d", wlr_output->name, 481 sway_log(SWAY_DEBUG, "Set %s adaptive sync to %d", wlr_output->name,
461 oc->adaptive_sync); 482 oc->adaptive_sync);
462 wlr_output_enable_adaptive_sync(wlr_output, oc->adaptive_sync == 1); 483 wlr_output_state_set_adaptive_sync_enabled(pending, oc->adaptive_sync == 1);
463 } 484 }
464 485
465 if (oc && oc->render_bit_depth != RENDER_BIT_DEPTH_DEFAULT) { 486 if (oc && oc->render_bit_depth != RENDER_BIT_DEPTH_DEFAULT) {
466 const uint32_t *fmts = bit_depth_preferences[oc->render_bit_depth]; 487 if (oc->render_bit_depth == RENDER_BIT_DEPTH_10 &&
467 assert(fmts); 488 render_format_is_10bit(output->wlr_output->render_format)) {
468 489 // 10-bit was set successfully before, try to save some tests by reusing the format
469 for (size_t i = 0; fmts[i] != DRM_FORMAT_INVALID; i++) { 490 wlr_output_state_set_render_format(pending, output->wlr_output->render_format);
470 wlr_output_set_render_format(wlr_output, fmts[i]); 491 } else if (oc->render_bit_depth == RENDER_BIT_DEPTH_10) {
471 if (wlr_output_test(wlr_output)) { 492 wlr_output_state_set_render_format(pending, DRM_FORMAT_XRGB2101010);
472 break; 493 } else {
473 } 494 wlr_output_state_set_render_format(pending, DRM_FORMAT_XRGB8888);
474
475 sway_log(SWAY_DEBUG, "Preferred output format 0x%08x "
476 "failed to work, falling back to next in "
477 "list, 0x%08x", fmts[i], fmts[i + 1]);
478 } 495 }
479 } 496 }
480} 497}
481 498
482bool apply_output_config(struct output_config *oc, struct sway_output *output) { 499static bool finalize_output_config(struct output_config *oc, struct sway_output *output) {
483 if (output == root->fallback_output) { 500 if (output == root->fallback_output) {
484 return false; 501 return false;
485 } 502 }
486 503
487 struct wlr_output *wlr_output = output->wlr_output; 504 struct wlr_output *wlr_output = output->wlr_output;
488
489 // Flag to prevent the output mode event handler from calling us
490 output->enabling = (!oc || oc->enabled);
491
492 queue_output_config(oc, output);
493
494 if (!oc || oc->dpms_state != DPMS_OFF) {
495 output->current_mode = wlr_output->pending.mode;
496 }
497
498 sway_log(SWAY_DEBUG, "Committing output %s", wlr_output->name);
499 if (!wlr_output_commit(wlr_output)) {
500 // Failed to commit output changes, maybe the output is missing a CRTC.
501 // Leave the output disabled for now and try again when the output gets
502 // the mode we asked for.
503 sway_log(SWAY_ERROR, "Failed to commit output %s", wlr_output->name);
504 output->enabling = false;
505 return false;
506 }
507
508 output->enabling = false;
509
510 if (oc && !oc->enabled) { 505 if (oc && !oc->enabled) {
511 sway_log(SWAY_DEBUG, "Disabling output %s", oc->name); 506 sway_log(SWAY_DEBUG, "Disabling output %s", oc->name);
512 if (output->enabled) { 507 if (output->enabled) {
@@ -516,10 +511,6 @@ bool apply_output_config(struct output_config *oc, struct sway_output *output) {
516 return true; 511 return true;
517 } 512 }
518 513
519 if (config->reloading) {
520 output_damage_whole(output);
521 }
522
523 if (oc) { 514 if (oc) {
524 enum scale_filter_mode scale_filter_old = output->scale_filter; 515 enum scale_filter_mode scale_filter_old = output->scale_filter;
525 switch (oc->scale_filter) { 516 switch (oc->scale_filter) {
@@ -536,6 +527,7 @@ bool apply_output_config(struct output_config *oc, struct sway_output *output) {
536 if (scale_filter_old != output->scale_filter) { 527 if (scale_filter_old != output->scale_filter) {
537 sway_log(SWAY_DEBUG, "Set %s scale_filter to %s", oc->name, 528 sway_log(SWAY_DEBUG, "Set %s scale_filter to %s", oc->name,
538 sway_output_scale_filter_to_string(output->scale_filter)); 529 sway_output_scale_filter_to_string(output->scale_filter));
530 wlr_damage_ring_add_whole(&output->scene_output->damage_ring);
539 } 531 }
540 } 532 }
541 533
@@ -565,29 +557,13 @@ bool apply_output_config(struct output_config *oc, struct sway_output *output) {
565 output->max_render_time = oc->max_render_time; 557 output->max_render_time = oc->max_render_time;
566 } 558 }
567 559
568 // Reconfigure all devices, since input config may have been applied before
569 // this output came online, and some config items (like map_to_output) are
570 // dependent on an output being present.
571 input_manager_configure_all_inputs();
572 // Reconfigure the cursor images, since the scale may have changed.
573 input_manager_configure_xcursor();
574 return true; 560 return true;
575} 561}
576 562
577bool test_output_config(struct output_config *oc, struct sway_output *output) {
578 if (output == root->fallback_output) {
579 return false;
580 }
581
582 queue_output_config(oc, output);
583 bool ok = wlr_output_test(output->wlr_output);
584 wlr_output_rollback(output->wlr_output);
585 return ok;
586}
587
588static void default_output_config(struct output_config *oc, 563static void default_output_config(struct output_config *oc,
589 struct wlr_output *wlr_output) { 564 struct wlr_output *wlr_output) {
590 oc->enabled = 1; 565 oc->enabled = 1;
566 oc->power = 1;
591 struct wlr_output_mode *mode = wlr_output_preferred_mode(wlr_output); 567 struct wlr_output_mode *mode = wlr_output_preferred_mode(wlr_output);
592 if (mode != NULL) { 568 if (mode != NULL) {
593 oc->width = mode->width; 569 oc->width = mode->width;
@@ -600,147 +576,418 @@ static void default_output_config(struct output_config *oc,
600 struct sway_output *output = wlr_output->data; 576 struct sway_output *output = wlr_output->data;
601 oc->subpixel = output->detected_subpixel; 577 oc->subpixel = output->detected_subpixel;
602 oc->transform = WL_OUTPUT_TRANSFORM_NORMAL; 578 oc->transform = WL_OUTPUT_TRANSFORM_NORMAL;
603 oc->dpms_state = DPMS_ON;
604 oc->max_render_time = 0; 579 oc->max_render_time = 0;
605} 580}
606 581
607static struct output_config *get_output_config(char *identifier, 582// find_output_config returns a merged output_config containing all stored
608 struct sway_output *sway_output) { 583// configuration that applies to the specified output.
584struct output_config *find_output_config(struct sway_output *sway_output) {
609 const char *name = sway_output->wlr_output->name; 585 const char *name = sway_output->wlr_output->name;
586 struct output_config *oc = NULL;
610 587
611 struct output_config *oc_id_on_name = NULL; 588 struct output_config *result = new_output_config(name);
612 struct output_config *oc_name = NULL; 589 if (config->reloading) {
613 struct output_config *oc_id = NULL; 590 default_output_config(result, sway_output->wlr_output);
591 }
614 592
615 size_t length = snprintf(NULL, 0, "%s on %s", identifier, name) + 1; 593 char id[128];
616 char *id_on_name = malloc(length); 594 output_get_identifier(id, sizeof(id), sway_output);
617 snprintf(id_on_name, length, "%s on %s", identifier, name); 595
618 int i = list_seq_find(config->output_configs, output_name_cmp, id_on_name); 596 int i;
619 if (i >= 0) { 597 bool match = false;
620 oc_id_on_name = config->output_configs->items[i]; 598 if ((i = list_seq_find(config->output_configs, output_name_cmp, "*")) >= 0) {
621 } else { 599 match = true;
622 i = list_seq_find(config->output_configs, output_name_cmp, name); 600 oc = config->output_configs->items[i];
623 if (i >= 0) { 601 merge_output_config(result, oc);
624 oc_name = config->output_configs->items[i]; 602 }
603 if ((i = list_seq_find(config->output_configs, output_name_cmp, name)) >= 0) {
604 match = true;
605 oc = config->output_configs->items[i];
606 merge_output_config(result, oc);
607 }
608 if ((i = list_seq_find(config->output_configs, output_name_cmp, id)) >= 0) {
609 match = true;
610 oc = config->output_configs->items[i];
611 merge_output_config(result, oc);
612 }
613
614 if (!match && !config->reloading) {
615 // No name, identifier, or wildcard config. Since we are not
616 // reloading with defaults, the output config will be empty, so
617 // just return NULL
618 free_output_config(result);
619 return NULL;
620 }
621
622 return result;
623}
624
625static bool config_has_auto_mode(struct output_config *oc) {
626 if (!oc) {
627 return true;
628 }
629 if (oc->drm_mode.type != 0 && oc->drm_mode.type != (uint32_t)-1) {
630 return true;
631 } else if (oc->width > 0 && oc->height > 0) {
632 return true;
633 }
634 return false;
635}
636
637struct search_context {
638 struct wlr_output_swapchain_manager *swapchain_mgr;
639 struct wlr_backend_output_state *states;
640 struct matched_output_config *configs;
641 size_t configs_len;
642 bool degrade_to_off;
643};
644
645static void dump_output_state(struct wlr_output *wlr_output, struct wlr_output_state *state) {
646 sway_log(SWAY_DEBUG, "Output state for %s", wlr_output->name);
647 if (state->committed & WLR_OUTPUT_STATE_ENABLED) {
648 sway_log(SWAY_DEBUG, " enabled: %s", state->enabled ? "yes" : "no");
649 }
650 if (state->committed & WLR_OUTPUT_STATE_RENDER_FORMAT) {
651 sway_log(SWAY_DEBUG, " render_format: %d", state->render_format);
652 }
653 if (state->committed & WLR_OUTPUT_STATE_MODE) {
654 if (state->mode_type == WLR_OUTPUT_STATE_MODE_CUSTOM) {
655 sway_log(SWAY_DEBUG, " custom mode: %dx%d@%dmHz",
656 state->custom_mode.width, state->custom_mode.height, state->custom_mode.refresh);
657 } else {
658 sway_log(SWAY_DEBUG, " mode: %dx%d@%dmHz%s",
659 state->mode->width, state->mode->height, state->mode->refresh,
660 state->mode->preferred ? " (preferred)" : "");
661 }
662 }
663 if (state->committed & WLR_OUTPUT_STATE_ADAPTIVE_SYNC_ENABLED) {
664 sway_log(SWAY_DEBUG, " adaptive_sync: %s",
665 state->adaptive_sync_enabled ? "enabled": "disabled");
666 }
667}
668
669static bool search_valid_config(struct search_context *ctx, size_t output_idx);
670
671static void reset_output_state(struct wlr_output_state *state) {
672 wlr_output_state_finish(state);
673 wlr_output_state_init(state);
674 state->committed = 0;
675}
676
677static void clear_later_output_states(struct wlr_backend_output_state *states,
678 size_t configs_len, size_t output_idx) {
679
680 // Clear and disable all output states after this one to avoid conflict
681 // with previous tests.
682 for (size_t idx = output_idx+1; idx < configs_len; idx++) {
683 struct wlr_backend_output_state *backend_state = &states[idx];
684 struct wlr_output_state *state = &backend_state->base;
685
686 reset_output_state(state);
687 wlr_output_state_set_enabled(state, false);
688 }
689}
690
691static bool search_finish(struct search_context *ctx, size_t output_idx) {
692 struct wlr_backend_output_state *backend_state = &ctx->states[output_idx];
693 struct wlr_output_state *state = &backend_state->base;
694 struct wlr_output *wlr_output = backend_state->output;
695
696 clear_later_output_states(ctx->states, ctx->configs_len, output_idx);
697 dump_output_state(wlr_output, state);
698 return wlr_output_swapchain_manager_prepare(ctx->swapchain_mgr, ctx->states, ctx->configs_len) &&
699 search_valid_config(ctx, output_idx+1);
700}
701
702static bool search_adaptive_sync(struct search_context *ctx, size_t output_idx) {
703 struct matched_output_config *cfg = &ctx->configs[output_idx];
704 struct wlr_backend_output_state *backend_state = &ctx->states[output_idx];
705 struct wlr_output_state *state = &backend_state->base;
706
707 if (cfg->config && cfg->config->adaptive_sync == 1) {
708 wlr_output_state_set_adaptive_sync_enabled(state, true);
709 if (search_finish(ctx, output_idx)) {
710 return true;
711 }
712 }
713 if (!cfg->config || cfg->config->adaptive_sync != -1) {
714 wlr_output_state_set_adaptive_sync_enabled(state, false);
715 if (search_finish(ctx, output_idx)) {
716 return true;
625 } 717 }
718 }
719 // If adaptive sync has not been set, or fallback in case we are on a
720 // backend that cannot disable adaptive sync such as the wayland backend.
721 state->committed &= ~WLR_OUTPUT_STATE_ADAPTIVE_SYNC_ENABLED;
722 return search_finish(ctx, output_idx);
723}
724
725static bool search_mode(struct search_context *ctx, size_t output_idx) {
726 struct matched_output_config *cfg = &ctx->configs[output_idx];
727 struct wlr_backend_output_state *backend_state = &ctx->states[output_idx];
728 struct wlr_output_state *state = &backend_state->base;
729 struct wlr_output *wlr_output = backend_state->output;
626 730
627 i = list_seq_find(config->output_configs, output_name_cmp, identifier); 731 if (!config_has_auto_mode(cfg->config)) {
628 if (i >= 0) { 732 return search_adaptive_sync(ctx, output_idx);
629 oc_id = config->output_configs->items[i]; 733 }
734
735 struct wlr_output_mode *preferred_mode = wlr_output_preferred_mode(wlr_output);
736 if (preferred_mode) {
737 wlr_output_state_set_mode(state, preferred_mode);
738 if (search_adaptive_sync(ctx, output_idx)) {
739 return true;
630 } 740 }
631 } 741 }
632 742
633 struct output_config *result = new_output_config("temp"); 743 if (wl_list_empty(&wlr_output->modes)) {
634 if (config->reloading) { 744 state->committed &= ~WLR_OUTPUT_STATE_MODE;
635 default_output_config(result, sway_output->wlr_output); 745 return search_adaptive_sync(ctx, output_idx);
636 } 746 }
637 if (oc_id_on_name) { 747
638 // Already have an identifier on name config, use that 748 struct wlr_output_mode *mode;
639 free(result->name); 749 wl_list_for_each(mode, &backend_state->output->modes, link) {
640 result->name = strdup(id_on_name); 750 if (mode == preferred_mode) {
641 merge_output_config(result, oc_id_on_name); 751 continue;
642 } else if (oc_name && oc_id) { 752 }
643 // Generate a config named `<identifier> on <name>` which contains a 753 wlr_output_state_set_mode(state, mode);
644 // merged copy of the identifier on name. This will make sure that both 754 if (search_adaptive_sync(ctx, output_idx)) {
645 // identifier and name configs are respected, with identifier getting 755 return true;
646 // priority
647 struct output_config *temp = new_output_config(id_on_name);
648 merge_output_config(temp, oc_name);
649 merge_output_config(temp, oc_id);
650 list_add(config->output_configs, temp);
651
652 free(result->name);
653 result->name = strdup(id_on_name);
654 merge_output_config(result, temp);
655
656 sway_log(SWAY_DEBUG, "Generated output config \"%s\" (enabled: %d)"
657 " (%dx%d@%fHz position %d,%d scale %f transform %d) (bg %s %s)"
658 " (dpms %d) (max render time: %d)", result->name, result->enabled,
659 result->width, result->height, result->refresh_rate,
660 result->x, result->y, result->scale, result->transform,
661 result->background, result->background_option, result->dpms_state,
662 result->max_render_time);
663 } else if (oc_name) {
664 // No identifier config, just return a copy of the name config
665 free(result->name);
666 result->name = strdup(name);
667 merge_output_config(result, oc_name);
668 } else if (oc_id) {
669 // No name config, just return a copy of the identifier config
670 free(result->name);
671 result->name = strdup(identifier);
672 merge_output_config(result, oc_id);
673 } else {
674 i = list_seq_find(config->output_configs, output_name_cmp, "*");
675 if (i >= 0) {
676 // No name or identifier config, but there is a wildcard config
677 free(result->name);
678 result->name = strdup("*");
679 merge_output_config(result, config->output_configs->items[i]);
680 } else if (!config->reloading) {
681 // No name, identifier, or wildcard config. Since we are not
682 // reloading with defaults, the output config will be empty, so
683 // just return NULL
684 free_output_config(result);
685 result = NULL;
686 } 756 }
687 } 757 }
688 758
689 free(id_on_name); 759 return false;
690 return result;
691} 760}
692 761
693struct output_config *find_output_config(struct sway_output *output) { 762static bool search_render_format(struct search_context *ctx, size_t output_idx) {
694 char id[128]; 763 struct matched_output_config *cfg = &ctx->configs[output_idx];
695 output_get_identifier(id, sizeof(id), output); 764 struct wlr_backend_output_state *backend_state = &ctx->states[output_idx];
696 return get_output_config(id, output); 765 struct wlr_output_state *state = &backend_state->base;
766 struct wlr_output *wlr_output = backend_state->output;
767
768 uint32_t fmts[] = {
769 DRM_FORMAT_XRGB2101010,
770 DRM_FORMAT_XBGR2101010,
771 DRM_FORMAT_XRGB8888,
772 DRM_FORMAT_INVALID,
773 };
774 if (render_format_is_bgr(wlr_output->render_format)) {
775 // Start with BGR in the unlikely event that we previously required it.
776 fmts[0] = DRM_FORMAT_XBGR2101010;
777 fmts[1] = DRM_FORMAT_XRGB2101010;
778 }
779
780 const struct wlr_drm_format_set *primary_formats =
781 wlr_output_get_primary_formats(wlr_output, WLR_BUFFER_CAP_DMABUF);
782 bool need_10bit = cfg->config && cfg->config->render_bit_depth == RENDER_BIT_DEPTH_10;
783 for (size_t idx = 0; fmts[idx] != DRM_FORMAT_INVALID; idx++) {
784 if (!need_10bit && render_format_is_10bit(fmts[idx])) {
785 continue;
786 }
787 if (!wlr_drm_format_set_get(primary_formats, fmts[idx])) {
788 // This is not a supported format for this output
789 continue;
790 }
791 wlr_output_state_set_render_format(state, fmts[idx]);
792 if (search_mode(ctx, output_idx)) {
793 return true;
794 }
795 }
796 return false;
697} 797}
698 798
699void apply_output_config_to_outputs(struct output_config *oc) { 799static bool search_valid_config(struct search_context *ctx, size_t output_idx) {
700 // Try to find the output container and apply configuration now. If 800 if (output_idx >= ctx->configs_len) {
701 // this is during startup then there will be no container and config 801 // We reached the end of the search, all good!
702 // will be applied during normal "new output" event from wlroots. 802 return true;
703 bool wildcard = strcmp(oc->name, "*") == 0; 803 }
704 char id[128];
705 struct sway_output *sway_output, *tmp;
706 wl_list_for_each_safe(sway_output, tmp, &root->all_outputs, link) {
707 char *name = sway_output->wlr_output->name;
708 output_get_identifier(id, sizeof(id), sway_output);
709 if (wildcard || !strcmp(name, oc->name) || !strcmp(id, oc->name)) {
710 struct output_config *current = get_output_config(id, sway_output);
711 if (!current) {
712 // No stored output config matched, apply oc directly
713 sway_log(SWAY_DEBUG, "Applying oc directly");
714 current = new_output_config(oc->name);
715 merge_output_config(current, oc);
716 }
717 apply_output_config(current, sway_output);
718 free_output_config(current);
719 804
720 if (!wildcard) { 805 struct matched_output_config *cfg = &ctx->configs[output_idx];
721 // Stop looking if the output config isn't applicable to all 806 struct wlr_backend_output_state *backend_state = &ctx->states[output_idx];
722 // outputs 807 struct wlr_output_state *state = &backend_state->base;
723 break; 808 struct wlr_output *wlr_output = backend_state->output;
724 } 809
810 if (!output_config_is_disabling(cfg->config)) {
811 // Search through our possible configurations, doing a depth-first
812 // through render_format, modes, adaptive_sync and the next output's
813 // config.
814 queue_output_config(cfg->config, cfg->output, &backend_state->base);
815 if (search_render_format(ctx, output_idx)) {
816 return true;
817 } else if (!ctx->degrade_to_off) {
818 return false;
819 }
820 // We could not get anything to work, try to disable this output to see
821 // if we can at least make the outputs before us work.
822 sway_log(SWAY_DEBUG, "Unable to find valid config with output %s, disabling",
823 wlr_output->name);
824 reset_output_state(state);
825 }
826
827 wlr_output_state_set_enabled(state, false);
828 return search_finish(ctx, output_idx);
829}
830
831static int compare_matched_output_config_priority(const void *a, const void *b) {
832
833 const struct matched_output_config *amc = a;
834 const struct matched_output_config *bmc = b;
835 bool a_disabling = output_config_is_disabling(amc->config);
836 bool b_disabling = output_config_is_disabling(bmc->config);
837 bool a_enabled = amc->output->enabled;
838 bool b_enabled = bmc->output->enabled;
839
840 // We want to give priority to existing enabled outputs. To do so, we want
841 // the configuration order to be:
842 // 1. Existing, enabled outputs
843 // 2. Outputs that need to be enabled
844 // 3. Disabled or disabling outputs
845 if (a_enabled && !a_disabling) {
846 return -1;
847 } else if (b_enabled && !b_disabling) {
848 return 1;
849 } else if (b_disabling && !a_disabling) {
850 return -1;
851 } else if (a_disabling && !b_disabling) {
852 return 1;
853 }
854 return 0;
855}
856
857void sort_output_configs_by_priority(struct matched_output_config *configs,
858 size_t configs_len) {
859 qsort(configs, configs_len, sizeof(*configs), compare_matched_output_config_priority);
860}
861
862bool apply_output_configs(struct matched_output_config *configs,
863 size_t configs_len, bool test_only, bool degrade_to_off) {
864 struct wlr_backend_output_state *states = calloc(configs_len, sizeof(*states));
865 if (!states) {
866 return false;
867 }
868
869 sway_log(SWAY_DEBUG, "Committing %zd outputs", configs_len);
870 for (size_t idx = 0; idx < configs_len; idx++) {
871 struct matched_output_config *cfg = &configs[idx];
872 struct wlr_backend_output_state *backend_state = &states[idx];
873
874 backend_state->output = cfg->output->wlr_output;
875 wlr_output_state_init(&backend_state->base);
876
877 sway_log(SWAY_DEBUG, "Preparing config for %s",
878 cfg->output->wlr_output->name);
879 queue_output_config(cfg->config, cfg->output, &backend_state->base);
880 }
881
882 struct wlr_output_swapchain_manager swapchain_mgr;
883 wlr_output_swapchain_manager_init(&swapchain_mgr, server.backend);
884
885 bool ok = wlr_output_swapchain_manager_prepare(&swapchain_mgr, states, configs_len);
886 if (!ok) {
887 sway_log(SWAY_ERROR, "Requested backend configuration failed, searching for valid fallbacks");
888 struct search_context ctx = {
889 .swapchain_mgr = &swapchain_mgr,
890 .states = states,
891 .configs = configs,
892 .configs_len = configs_len,
893 .degrade_to_off = degrade_to_off,
894 };
895 if (!search_valid_config(&ctx, 0)) {
896 sway_log(SWAY_ERROR, "Search for valid config failed");
897 goto out;
725 } 898 }
726 } 899 }
727 900
901 if (test_only) {
902 // The swapchain manager already did a test for us
903 goto out;
904 }
905
906 for (size_t idx = 0; idx < configs_len; idx++) {
907 struct matched_output_config *cfg = &configs[idx];
908 struct wlr_backend_output_state *backend_state = &states[idx];
909
910 struct wlr_scene_output_state_options opts = {
911 .swapchain = wlr_output_swapchain_manager_get_swapchain(
912 &swapchain_mgr, backend_state->output),
913 };
914 struct wlr_scene_output *scene_output = cfg->output->scene_output;
915 struct wlr_output_state *state = &backend_state->base;
916 if (!wlr_scene_output_build_state(scene_output, state, &opts)) {
917 sway_log(SWAY_ERROR, "Building output state for '%s' failed",
918 backend_state->output->name);
919 goto out;
920 }
921 }
922
923 ok = wlr_backend_commit(server.backend, states, configs_len);
924 if (!ok) {
925 sway_log(SWAY_ERROR, "Backend commit failed");
926 goto out;
927 }
928
929 sway_log(SWAY_DEBUG, "Commit of %zd outputs succeeded", configs_len);
930
931 wlr_output_swapchain_manager_apply(&swapchain_mgr);
932
933 for (size_t idx = 0; idx < configs_len; idx++) {
934 struct matched_output_config *cfg = &configs[idx];
935 sway_log(SWAY_DEBUG, "Finalizing config for %s",
936 cfg->output->wlr_output->name);
937 finalize_output_config(cfg->config, cfg->output);
938 }
939
940out:
941 wlr_output_swapchain_manager_finish(&swapchain_mgr);
942 for (size_t idx = 0; idx < configs_len; idx++) {
943 struct wlr_backend_output_state *backend_state = &states[idx];
944 wlr_output_state_finish(&backend_state->base);
945 }
946 free(states);
947
948 // Reconfigure all devices, since input config may have been applied before
949 // this output came online, and some config items (like map_to_output) are
950 // dependent on an output being present.
951 input_manager_configure_all_input_mappings();
952 // Reconfigure the cursor images, since the scale may have changed.
953 input_manager_configure_xcursor();
954
728 struct sway_seat *seat; 955 struct sway_seat *seat;
729 wl_list_for_each(seat, &server.input->seats, link) { 956 wl_list_for_each(seat, &server.input->seats, link) {
730 wlr_seat_pointer_notify_clear_focus(seat->wlr_seat); 957 wlr_seat_pointer_notify_clear_focus(seat->wlr_seat);
731 cursor_rebase(seat->cursor); 958 cursor_rebase(seat->cursor);
732 } 959 }
960
961 return ok;
733} 962}
734 963
735void reset_outputs(void) { 964void apply_all_output_configs(void) {
736 struct output_config *oc = NULL; 965 size_t configs_len = wl_list_length(&root->all_outputs);
737 int i = list_seq_find(config->output_configs, output_name_cmp, "*"); 966 struct matched_output_config *configs = calloc(configs_len, sizeof(*configs));
738 if (i >= 0) { 967 if (!configs) {
739 oc = config->output_configs->items[i]; 968 return;
740 } else {
741 oc = store_output_config(new_output_config("*"));
742 } 969 }
743 apply_output_config_to_outputs(oc); 970
971 int config_idx = 0;
972 struct sway_output *sway_output;
973 wl_list_for_each(sway_output, &root->all_outputs, link) {
974 if (sway_output == root->fallback_output) {
975 configs_len--;
976 continue;
977 }
978
979 struct matched_output_config *config = &configs[config_idx++];
980 config->output = sway_output;
981 config->config = find_output_config(sway_output);
982 }
983
984 sort_output_configs_by_priority(configs, configs_len);
985 apply_output_configs(configs, configs_len, false, true);
986 for (size_t idx = 0; idx < configs_len; idx++) {
987 struct matched_output_config *cfg = &configs[idx];
988 free_output_config(cfg->config);
989 }
990 free(configs);
744} 991}
745 992
746void free_output_config(struct output_config *oc) { 993void free_output_config(struct output_config *oc) {
@@ -807,7 +1054,9 @@ static bool _spawn_swaybg(char **command) {
807 setenv("WAYLAND_SOCKET", wayland_socket_str, true); 1054 setenv("WAYLAND_SOCKET", wayland_socket_str, true);
808 1055
809 execvp(command[0], command); 1056 execvp(command[0], command);
810 sway_log_errno(SWAY_ERROR, "execvp failed"); 1057 sway_log_errno(SWAY_ERROR, "failed to execute '%s' "
1058 "(background configuration probably not applied)",
1059 command[0]);
811 _exit(EXIT_FAILURE); 1060 _exit(EXIT_FAILURE);
812 } 1061 }
813 _exit(EXIT_SUCCESS); 1062 _exit(EXIT_SUCCESS);
@@ -817,12 +1066,13 @@ static bool _spawn_swaybg(char **command) {
817 sway_log_errno(SWAY_ERROR, "close failed"); 1066 sway_log_errno(SWAY_ERROR, "close failed");
818 return false; 1067 return false;
819 } 1068 }
820 if (waitpid(pid, NULL, 0) < 0) { 1069 int fork_status = 0;
1070 if (waitpid(pid, &fork_status, 0) < 0) {
821 sway_log_errno(SWAY_ERROR, "waitpid failed"); 1071 sway_log_errno(SWAY_ERROR, "waitpid failed");
822 return false; 1072 return false;
823 } 1073 }
824 1074
825 return true; 1075 return WIFEXITED(fork_status) && WEXITSTATUS(fork_status) == EXIT_SUCCESS;
826} 1076}
827 1077
828bool spawn_swaybg(void) { 1078bool 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 97cf667e..2b7290c0 100644
--- a/sway/criteria.c
+++ b/sway/criteria.c
@@ -1,4 +1,3 @@
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>
@@ -8,6 +7,7 @@
8#include "sway/criteria.h" 7#include "sway/criteria.h"
9#include "sway/tree/container.h" 8#include "sway/tree/container.h"
10#include "sway/config.h" 9#include "sway/config.h"
10#include "sway/server.h"
11#include "sway/tree/root.h" 11#include "sway/tree/root.h"
12#include "sway/tree/view.h" 12#include "sway/tree/view.h"
13#include "sway/tree/workspace.h" 13#include "sway/tree/workspace.h"
@@ -19,10 +19,11 @@
19bool criteria_is_empty(struct criteria *criteria) { 19bool criteria_is_empty(struct criteria *criteria) {
20 return !criteria->title 20 return !criteria->title
21 && !criteria->shell 21 && !criteria->shell
22 && !criteria->all
22 && !criteria->app_id 23 && !criteria->app_id
23 && !criteria->con_mark 24 && !criteria->con_mark
24 && !criteria->con_id 25 && !criteria->con_id
25#if HAVE_XWAYLAND 26#if WLR_HAS_XWAYLAND
26 && !criteria->class 27 && !criteria->class
27 && !criteria->id 28 && !criteria->id
28 && !criteria->instance 29 && !criteria->instance
@@ -90,13 +91,14 @@ void criteria_destroy(struct criteria *criteria) {
90 pattern_destroy(criteria->title); 91 pattern_destroy(criteria->title);
91 pattern_destroy(criteria->shell); 92 pattern_destroy(criteria->shell);
92 pattern_destroy(criteria->app_id); 93 pattern_destroy(criteria->app_id);
93#if HAVE_XWAYLAND 94#if WLR_HAS_XWAYLAND
94 pattern_destroy(criteria->class); 95 pattern_destroy(criteria->class);
95 pattern_destroy(criteria->instance); 96 pattern_destroy(criteria->instance);
96 pattern_destroy(criteria->window_role); 97 pattern_destroy(criteria->window_role);
97#endif 98#endif
98 pattern_destroy(criteria->con_mark); 99 pattern_destroy(criteria->con_mark);
99 free(criteria->workspace); 100 pattern_destroy(criteria->workspace);
101 free(criteria->target);
100 free(criteria->cmdlist); 102 free(criteria->cmdlist);
101 free(criteria->raw); 103 free(criteria->raw);
102 free(criteria); 104 free(criteria);
@@ -109,7 +111,7 @@ static int regex_cmp(const char *item, const pcre2_code *regex) {
109 return result; 111 return result;
110} 112}
111 113
112#if HAVE_XWAYLAND 114#if WLR_HAS_XWAYLAND
113static bool view_has_window_type(struct sway_view *view, enum atom_name name) { 115static bool view_has_window_type(struct sway_view *view, enum atom_name name) {
114 if (view->type != SWAY_VIEW_XWAYLAND) { 116 if (view->type != SWAY_VIEW_XWAYLAND) {
115 return false; 117 return false;
@@ -189,7 +191,7 @@ static bool criteria_matches_view(struct criteria *criteria,
189 if (criteria->title) { 191 if (criteria->title) {
190 const char *title = view_get_title(view); 192 const char *title = view_get_title(view);
191 if (!title) { 193 if (!title) {
192 return false; 194 title = "";
193 } 195 }
194 196
195 switch (criteria->title->match_type) { 197 switch (criteria->title->match_type) {
@@ -209,7 +211,7 @@ static bool criteria_matches_view(struct criteria *criteria,
209 if (criteria->shell) { 211 if (criteria->shell) {
210 const char *shell = view_get_shell(view); 212 const char *shell = view_get_shell(view);
211 if (!shell) { 213 if (!shell) {
212 return false; 214 shell = "";
213 } 215 }
214 216
215 switch (criteria->shell->match_type) { 217 switch (criteria->shell->match_type) {
@@ -229,7 +231,7 @@ static bool criteria_matches_view(struct criteria *criteria,
229 if (criteria->app_id) { 231 if (criteria->app_id) {
230 const char *app_id = view_get_app_id(view); 232 const char *app_id = view_get_app_id(view);
231 if (!app_id) { 233 if (!app_id) {
232 return false; 234 app_id = "";
233 } 235 }
234 236
235 switch (criteria->app_id->match_type) { 237 switch (criteria->app_id->match_type) {
@@ -250,7 +252,7 @@ static bool criteria_matches_view(struct criteria *criteria,
250 return false; 252 return false;
251 } 253 }
252 254
253#if HAVE_XWAYLAND 255#if WLR_HAS_XWAYLAND
254 if (criteria->id) { // X11 window ID 256 if (criteria->id) { // X11 window ID
255 uint32_t x11_window_id = view_get_x11_window_id(view); 257 uint32_t x11_window_id = view_get_x11_window_id(view);
256 if (!x11_window_id || x11_window_id != criteria->id) { 258 if (!x11_window_id || x11_window_id != criteria->id) {
@@ -261,7 +263,7 @@ static bool criteria_matches_view(struct criteria *criteria,
261 if (criteria->class) { 263 if (criteria->class) {
262 const char *class = view_get_class(view); 264 const char *class = view_get_class(view);
263 if (!class) { 265 if (!class) {
264 return false; 266 class = "";
265 } 267 }
266 268
267 switch (criteria->class->match_type) { 269 switch (criteria->class->match_type) {
@@ -281,12 +283,12 @@ static bool criteria_matches_view(struct criteria *criteria,
281 if (criteria->instance) { 283 if (criteria->instance) {
282 const char *instance = view_get_instance(view); 284 const char *instance = view_get_instance(view);
283 if (!instance) { 285 if (!instance) {
284 return false; 286 instance = "";
285 } 287 }
286 288
287 switch (criteria->instance->match_type) { 289 switch (criteria->instance->match_type) {
288 case PATTERN_FOCUSED: 290 case PATTERN_FOCUSED:
289 if (focused && strcmp(instance, view_get_instance(focused))) { 291 if (focused && lenient_strcmp(instance, view_get_instance(focused))) {
290 return false; 292 return false;
291 } 293 }
292 break; 294 break;
@@ -301,12 +303,12 @@ static bool criteria_matches_view(struct criteria *criteria,
301 if (criteria->window_role) { 303 if (criteria->window_role) {
302 const char *window_role = view_get_window_role(view); 304 const char *window_role = view_get_window_role(view);
303 if (!window_role) { 305 if (!window_role) {
304 return false; 306 window_role = "";
305 } 307 }
306 308
307 switch (criteria->window_role->match_type) { 309 switch (criteria->window_role->match_type) {
308 case PATTERN_FOCUSED: 310 case PATTERN_FOCUSED:
309 if (focused && strcmp(window_role, view_get_window_role(focused))) { 311 if (focused && lenient_strcmp(window_role, view_get_window_role(focused))) {
310 return false; 312 return false;
311 } 313 }
312 break; 314 break;
@@ -427,7 +429,7 @@ list_t *criteria_get_containers(struct criteria *criteria) {
427 return matches; 429 return matches;
428} 430}
429 431
430#if HAVE_XWAYLAND 432#if WLR_HAS_XWAYLAND
431static enum atom_name parse_window_type(const char *type) { 433static enum atom_name parse_window_type(const char *type) {
432 if (strcasecmp(type, "normal") == 0) { 434 if (strcasecmp(type, "normal") == 0) {
433 return NET_WM_WINDOW_TYPE_NORMAL; 435 return NET_WM_WINDOW_TYPE_NORMAL;
@@ -455,11 +457,12 @@ static enum atom_name parse_window_type(const char *type) {
455#endif 457#endif
456 458
457enum criteria_token { 459enum criteria_token {
460 T_ALL,
458 T_APP_ID, 461 T_APP_ID,
459 T_CON_ID, 462 T_CON_ID,
460 T_CON_MARK, 463 T_CON_MARK,
461 T_FLOATING, 464 T_FLOATING,
462#if HAVE_XWAYLAND 465#if WLR_HAS_XWAYLAND
463 T_CLASS, 466 T_CLASS,
464 T_ID, 467 T_ID,
465 T_INSTANCE, 468 T_INSTANCE,
@@ -477,13 +480,15 @@ enum criteria_token {
477}; 480};
478 481
479static enum criteria_token token_from_name(char *name) { 482static enum criteria_token token_from_name(char *name) {
480 if (strcmp(name, "app_id") == 0) { 483 if (strcmp(name, "all") == 0) {
484 return T_ALL;
485 } else if (strcmp(name, "app_id") == 0) {
481 return T_APP_ID; 486 return T_APP_ID;
482 } else if (strcmp(name, "con_id") == 0) { 487 } else if (strcmp(name, "con_id") == 0) {
483 return T_CON_ID; 488 return T_CON_ID;
484 } else if (strcmp(name, "con_mark") == 0) { 489 } else if (strcmp(name, "con_mark") == 0) {
485 return T_CON_MARK; 490 return T_CON_MARK;
486#if HAVE_XWAYLAND 491#if WLR_HAS_XWAYLAND
487 } else if (strcmp(name, "class") == 0) { 492 } else if (strcmp(name, "class") == 0) {
488 return T_CLASS; 493 return T_CLASS;
489 } else if (strcmp(name, "id") == 0) { 494 } else if (strcmp(name, "id") == 0) {
@@ -523,8 +528,8 @@ static bool parse_token(struct criteria *criteria, char *name, char *value) {
523 return false; 528 return false;
524 } 529 }
525 530
526 // Require value, unless token is floating or tiled 531 // Require value, unless token is all, floating or tiled
527 if (!value && token != T_FLOATING && token != T_TILING) { 532 if (!value && token != T_ALL && token != T_FLOATING && token != T_TILING) {
528 const char *fmt = "Token '%s' requires a value"; 533 const char *fmt = "Token '%s' requires a value";
529 int len = strlen(fmt) + strlen(name) - 1; 534 int len = strlen(fmt) + strlen(name) - 1;
530 error = malloc(len); 535 error = malloc(len);
@@ -534,6 +539,9 @@ static bool parse_token(struct criteria *criteria, char *name, char *value) {
534 539
535 char *endptr = NULL; 540 char *endptr = NULL;
536 switch (token) { 541 switch (token) {
542 case T_ALL:
543 criteria->all = true;
544 break;
537 case T_TITLE: 545 case T_TITLE:
538 pattern_create(&criteria->title, value); 546 pattern_create(&criteria->title, value);
539 break; 547 break;
@@ -559,7 +567,7 @@ static bool parse_token(struct criteria *criteria, char *name, char *value) {
559 case T_CON_MARK: 567 case T_CON_MARK:
560 pattern_create(&criteria->con_mark, value); 568 pattern_create(&criteria->con_mark, value);
561 break; 569 break;
562#if HAVE_XWAYLAND 570#if WLR_HAS_XWAYLAND
563 case T_CLASS: 571 case T_CLASS:
564 pattern_create(&criteria->class, value); 572 pattern_create(&criteria->class, value);
565 break; 573 break;
@@ -667,7 +675,7 @@ struct criteria *criteria_parse(char *raw, char **error_arg) {
667 ++head; 675 ++head;
668 676
669 struct criteria *criteria = calloc(1, sizeof(struct criteria)); 677 struct criteria *criteria = calloc(1, sizeof(struct criteria));
670#if HAVE_XWAYLAND 678#if WLR_HAS_XWAYLAND
671 criteria->window_type = ATOM_LAST; // default value 679 criteria->window_type = ATOM_LAST; // default value
672#endif 680#endif
673 char *name = NULL, *value = NULL; 681 char *name = NULL, *value = NULL;
diff --git a/sway/desktop/desktop.c b/sway/desktop/desktop.c
deleted file mode 100644
index c8d4502c..00000000
--- a/sway/desktop/desktop.c
+++ /dev/null
@@ -1,40 +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;
10 wlr_output_layout_get_box(root->output_layout,
11 output->wlr_output, &output_box);
12 output_damage_surface(output, lx - output_box.x,
13 ly - output_box.y, surface, whole);
14 }
15}
16
17void desktop_damage_whole_container(struct sway_container *con) {
18 for (int i = 0; i < root->outputs->length; ++i) {
19 struct sway_output *output = root->outputs->items[i];
20 output_damage_whole_container(output, con);
21 }
22}
23
24void desktop_damage_box(struct wlr_box *box) {
25 for (int i = 0; i < root->outputs->length; ++i) {
26 struct sway_output *output = root->outputs->items[i];
27 output_damage_box(output, box);
28 }
29}
30
31void desktop_damage_view(struct sway_view *view) {
32 desktop_damage_whole_container(view->container);
33 struct wlr_box box = {
34 .x = view->container->current.content_x - view->geometry.x,
35 .y = view->container->current.content_y - view->geometry.y,
36 .width = view->surface->current.width,
37 .height = view->surface->current.height,
38 };
39 desktop_damage_box(&box);
40}
diff --git a/sway/desktop/idle_inhibit_v1.c b/sway/desktop/idle_inhibit_v1.c
index 82353038..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,33 +41,34 @@ 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->mode != INHIBIT_IDLE_APPLICATION &&
73 inhibitor->view == view) { 73 inhibitor->view == view) {
74 return inhibitor; 74 return inhibitor;
@@ -79,9 +79,9 @@ 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->mode == INHIBIT_IDLE_APPLICATION &&
86 view_from_wlr_surface(inhibitor->wlr_inhibitor->surface) == view) { 86 view_from_wlr_surface(inhibitor->wlr_inhibitor->surface) == view) {
87 return inhibitor; 87 return inhibitor;
@@ -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..2362e1ba
--- /dev/null
+++ b/sway/desktop/launcher.c
@@ -0,0 +1,268 @@
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/server.h"
8#include "sway/tree/node.h"
9#include "sway/tree/container.h"
10#include "sway/tree/workspace.h"
11#include "sway/tree/root.h"
12#include "log.h"
13
14/**
15 * Get the pid of a parent process given the pid of a child process.
16 *
17 * Returns the parent pid or NULL if the parent pid cannot be determined.
18 */
19static pid_t get_parent_pid(pid_t child) {
20 pid_t parent = -1;
21 char file_name[100];
22 char *buffer = NULL;
23 const char *sep = " ";
24 FILE *stat = NULL;
25 size_t buf_size = 0;
26
27 snprintf(file_name, sizeof(file_name), "/proc/%d/stat", child);
28
29 if ((stat = fopen(file_name, "r"))) {
30 if (getline(&buffer, &buf_size, stat) != -1) {
31 strtok(buffer, sep); // pid
32 strtok(NULL, sep); // executable name
33 strtok(NULL, sep); // state
34 char *token = strtok(NULL, sep); // parent pid
35 parent = strtol(token, NULL, 10);
36 }
37 free(buffer);
38 fclose(stat);
39 }
40
41 if (parent) {
42 return (parent == child) ? -1 : parent;
43 }
44
45 return -1;
46}
47
48void launcher_ctx_consume(struct launcher_ctx *ctx) {
49 // The view is now responsible for destroying this ctx
50 wl_list_remove(&ctx->token_destroy.link);
51 wl_list_init(&ctx->token_destroy.link);
52
53 if (!ctx->activated) {
54 // An unactivated token hasn't been destroyed yet
55 wlr_xdg_activation_token_v1_destroy(ctx->token);
56 }
57 ctx->token = NULL;
58
59 // Prevent additional matches
60 wl_list_remove(&ctx->link);
61 wl_list_init(&ctx->link);
62}
63
64void launcher_ctx_destroy(struct launcher_ctx *ctx) {
65 if (ctx == NULL) {
66 return;
67 }
68 wl_list_remove(&ctx->node_destroy.link);
69 wl_list_remove(&ctx->token_destroy.link);
70 if (ctx->seat) {
71 wl_list_remove(&ctx->seat_destroy.link);
72 }
73 wl_list_remove(&ctx->link);
74 wlr_xdg_activation_token_v1_destroy(ctx->token);
75 free(ctx->fallback_name);
76 free(ctx);
77}
78
79struct launcher_ctx *launcher_ctx_find_pid(pid_t pid) {
80 if (wl_list_empty(&server.pending_launcher_ctxs)) {
81 return NULL;
82 }
83
84 struct launcher_ctx *ctx = NULL;
85 sway_log(SWAY_DEBUG, "Looking up workspace for pid %d", pid);
86
87 do {
88 struct launcher_ctx *_ctx = NULL;
89 wl_list_for_each(_ctx, &server.pending_launcher_ctxs, link) {
90 if (pid == _ctx->pid) {
91 ctx = _ctx;
92 sway_log(SWAY_DEBUG,
93 "found %s match for pid %d: %s",
94 node_type_to_str(ctx->node->type), pid, node_get_name(ctx->node));
95 break;
96 }
97 }
98 pid = get_parent_pid(pid);
99 } while (pid > 1);
100
101 return ctx;
102}
103
104struct sway_workspace *launcher_ctx_get_workspace(
105 struct launcher_ctx *ctx) {
106 struct sway_workspace *ws = NULL;
107 struct sway_output *output = NULL;
108
109 switch (ctx->node->type) {
110 case N_CONTAINER:
111 // Unimplemented
112 // TODO: add container matching?
113 ws = ctx->node->sway_container->pending.workspace;
114 break;
115 case N_WORKSPACE:
116 ws = ctx->node->sway_workspace;
117 break;
118 case N_OUTPUT:
119 output = ctx->node->sway_output;
120 ws = workspace_by_name(ctx->fallback_name);
121 if (!ws) {
122 sway_log(SWAY_DEBUG,
123 "Creating workspace %s for pid %d because it disappeared",
124 ctx->fallback_name, ctx->pid);
125 if (!output->enabled) {
126 sway_log(SWAY_DEBUG,
127 "Workspace output %s is disabled, trying another one",
128 output->wlr_output->name);
129 output = NULL;
130 }
131 ws = workspace_create(output, ctx->fallback_name);
132 }
133 break;
134 case N_ROOT:
135 ws = workspace_create(NULL, ctx->fallback_name);
136 break;
137 }
138
139 return ws;
140}
141
142static void ctx_handle_node_destroy(struct wl_listener *listener, void *data) {
143 struct launcher_ctx *ctx = wl_container_of(listener, ctx, node_destroy);
144 switch (ctx->node->type) {
145 case N_CONTAINER:
146 // Unimplemented
147 break;
148 case N_WORKSPACE:;
149 struct sway_workspace *ws = ctx->node->sway_workspace;
150 wl_list_remove(&ctx->node_destroy.link);
151 wl_list_init(&ctx->node_destroy.link);
152 // We want to save this ws name to recreate later, hopefully on the
153 // same output
154 free(ctx->fallback_name);
155 ctx->fallback_name = strdup(ws->name);
156 if (!ws->output || ws->output->node.destroying) {
157 // If the output is being destroyed it would be pointless to track
158 // If the output is being disabled, we'll find out if it's still
159 // disabled when we try to match it.
160 ctx->node = &root->node;
161 break;
162 }
163 ctx->node = &ws->output->node;
164 wl_signal_add(&ctx->node->events.destroy, &ctx->node_destroy);
165 break;
166 case N_OUTPUT:
167 wl_list_remove(&ctx->node_destroy.link);
168 wl_list_init(&ctx->node_destroy.link);
169 // We'll make the ws ctx->name somewhere else
170 ctx->node = &root->node;
171 break;
172 case N_ROOT:
173 // Unreachable
174 break;
175 }
176}
177
178static void token_handle_destroy(struct wl_listener *listener, void *data) {
179 struct launcher_ctx *ctx = wl_container_of(listener, ctx, token_destroy);
180 ctx->token = NULL;
181 launcher_ctx_destroy(ctx);
182}
183
184struct launcher_ctx *launcher_ctx_create(struct wlr_xdg_activation_token_v1 *token,
185 struct sway_node *node) {
186 struct launcher_ctx *ctx = calloc(1, sizeof(struct launcher_ctx));
187
188 const char *fallback_name = NULL;
189 struct sway_workspace *ws = NULL;
190 switch (node->type) {
191 case N_CONTAINER:
192 // Unimplemented
193 free(ctx);
194 return NULL;
195 case N_WORKSPACE:
196 ws = node->sway_workspace;
197 fallback_name = ws->name;
198 break;
199 case N_OUTPUT:;
200 struct sway_output *output = node->sway_output;
201 ws = output_get_active_workspace(output);
202 fallback_name = ws ? ws->name : NULL;
203 break;
204 case N_ROOT:
205 // Unimplemented
206 free(ctx);
207 return NULL;
208 }
209
210 if (!fallback_name) {
211 // TODO: implement a better fallback.
212 free(ctx);
213 return NULL;
214 }
215
216 ctx->fallback_name = strdup(fallback_name);
217 ctx->token = token;
218 ctx->node = node;
219 // Having surface set means that the focus check in wlroots has passed
220 ctx->had_focused_surface = token->surface != NULL;
221
222 ctx->node_destroy.notify = ctx_handle_node_destroy;
223 wl_signal_add(&ctx->node->events.destroy, &ctx->node_destroy);
224
225 ctx->token_destroy.notify = token_handle_destroy;
226 wl_signal_add(&token->events.destroy, &ctx->token_destroy);
227
228 wl_list_init(&ctx->link);
229 wl_list_insert(&server.pending_launcher_ctxs, &ctx->link);
230
231 token->data = ctx;
232 return ctx;
233}
234
235static void launch_ctx_handle_seat_destroy(struct wl_listener *listener, void *data) {
236 struct launcher_ctx *ctx = wl_container_of(listener, ctx, seat_destroy);
237 ctx->seat = NULL;
238 wl_list_remove(&ctx->seat_destroy.link);
239}
240
241// Creates a context with a new token for the internal launcher
242struct launcher_ctx *launcher_ctx_create_internal(void) {
243 struct sway_seat *seat = input_manager_current_seat();
244 struct sway_workspace *ws = seat_get_focused_workspace(seat);
245 if (!ws) {
246 sway_log(SWAY_DEBUG, "Failed to create launch context. No workspace.");
247 return NULL;
248 }
249
250 struct wlr_xdg_activation_token_v1 *token =
251 wlr_xdg_activation_token_v1_create(server.xdg_activation_v1);
252
253 struct launcher_ctx *ctx = launcher_ctx_create(token, &ws->node);
254 if (!ctx) {
255 wlr_xdg_activation_token_v1_destroy(token);
256 return NULL;
257 }
258 ctx->seat = seat;
259 ctx->seat_destroy.notify = launch_ctx_handle_seat_destroy;
260 wl_signal_add(&seat->wlr_seat->events.destroy, &ctx->seat_destroy);
261
262 return ctx;
263}
264
265const char *launcher_ctx_get_token_name(struct launcher_ctx *ctx) {
266 const char *token = wlr_xdg_activation_token_v1_get_name(ctx->token);
267 return token;
268}
diff --git a/sway/desktop/layer_shell.c b/sway/desktop/layer_shell.c
index 159f3336..6221b7b9 100644
--- a/sway/desktop/layer_shell.c
+++ b/sway/desktop/layer_shell.c
@@ -2,11 +2,14 @@
2#include <stdlib.h> 2#include <stdlib.h>
3#include <string.h> 3#include <string.h>
4#include <wayland-server-core.h> 4#include <wayland-server-core.h>
5#include <wlr/types/wlr_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>
8#include <wlr/types/wlr_subcompositor.h> 9#include <wlr/types/wlr_subcompositor.h>
10#include <wlr/types/wlr_xdg_shell.h>
9#include "log.h" 11#include "log.h"
12#include "sway/scene_descriptor.h"
10#include "sway/desktop/transaction.h" 13#include "sway/desktop/transaction.h"
11#include "sway/input/cursor.h" 14#include "sway/input/cursor.h"
12#include "sway/input/input-manager.h" 15#include "sway/input/input-manager.h"
@@ -17,162 +20,55 @@
17#include "sway/tree/arrange.h" 20#include "sway/tree/arrange.h"
18#include "sway/tree/workspace.h" 21#include "sway/tree/workspace.h"
19 22
20static void apply_exclusive(struct wlr_box *usable_area, 23struct wlr_layer_surface_v1 *toplevel_layer_surface_from_surface(
21 uint32_t anchor, int32_t exclusive, 24 struct wlr_surface *surface) {
22 int32_t margin_top, int32_t margin_right, 25 struct wlr_layer_surface_v1 *layer;
23 int32_t margin_bottom, int32_t margin_left) { 26 do {
24 if (exclusive <= 0) { 27 if (!surface) {
25 return; 28 return NULL;
26 } 29 }
27 struct { 30 // Topmost layer surface
28 uint32_t singular_anchor; 31 if ((layer = wlr_layer_surface_v1_try_from_wlr_surface(surface))) {
29 uint32_t anchor_triplet; 32 return layer;
30 int *positive_axis; 33 }
31 int *negative_axis; 34 // Layer subsurface
32 int margin; 35 if (wlr_subsurface_try_from_wlr_surface(surface)) {
33 } edges[] = { 36 surface = wlr_surface_get_root_surface(surface);
34 // Top 37 continue;
35 { 38 }
36 .singular_anchor = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP, 39
37 .anchor_triplet = 40 // Layer surface popup
38 ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | 41 struct wlr_xdg_surface *xdg_surface = NULL;
39 ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT | 42 if ((xdg_surface = wlr_xdg_surface_try_from_wlr_surface(surface)) &&
40 ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP, 43 xdg_surface->role == WLR_XDG_SURFACE_ROLE_POPUP && xdg_surface->popup != NULL) {
41 .positive_axis = &usable_area->y, 44 if (!xdg_surface->popup->parent) {
42 .negative_axis = &usable_area->height, 45 return NULL;
43 .margin = margin_top,
44 },
45 // Bottom
46 {
47 .singular_anchor = ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM,
48 .anchor_triplet =
49 ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT |
50 ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT |
51 ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM,
52 .positive_axis = NULL,
53 .negative_axis = &usable_area->height,
54 .margin = margin_bottom,
55 },
56 // Left
57 {
58 .singular_anchor = ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT,
59 .anchor_triplet =
60 ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT |
61 ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP |
62 ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM,
63 .positive_axis = &usable_area->x,
64 .negative_axis = &usable_area->width,
65 .margin = margin_left,
66 },
67 // Right
68 {
69 .singular_anchor = ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT,
70 .anchor_triplet =
71 ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT |
72 ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP |
73 ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM,
74 .positive_axis = NULL,
75 .negative_axis = &usable_area->width,
76 .margin = margin_right,
77 },
78 };
79 for (size_t i = 0; i < sizeof(edges) / sizeof(edges[0]); ++i) {
80 if ((anchor == edges[i].singular_anchor || anchor == edges[i].anchor_triplet)
81 && exclusive + edges[i].margin > 0) {
82 if (edges[i].positive_axis) {
83 *edges[i].positive_axis += exclusive + edges[i].margin;
84 }
85 if (edges[i].negative_axis) {
86 *edges[i].negative_axis -= exclusive + edges[i].margin;
87 } 46 }
88 break; 47 surface = wlr_surface_get_root_surface(xdg_surface->popup->parent);
48 continue;
89 } 49 }
90 } 50
51 // Return early if the surface is not a layer/xdg_popup/sub surface
52 return NULL;
53 } while (true);
91} 54}
92 55
93static 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,
94 struct wlr_box *usable_area, bool exclusive) { 57 struct wlr_box *usable_area, struct wlr_scene_tree *tree) {
95 struct sway_layer_surface *sway_layer; 58 struct wlr_scene_node *node;
96 struct wlr_box full_area = { 0 }; 59 wl_list_for_each(node, &tree->children, link) {
97 wlr_output_effective_resolution(output->wlr_output, 60 struct sway_layer_surface *surface = scene_descriptor_try_get(node,
98 &full_area.width, &full_area.height); 61 SWAY_SCENE_DESC_LAYER_SHELL);
99 wl_list_for_each(sway_layer, list, link) { 62 // surface could be null during destruction
100 struct wlr_layer_surface_v1 *layer = sway_layer->layer_surface; 63 if (!surface) {
101 struct wlr_layer_surface_v1_state *state = &layer->current;
102 if (exclusive != (state->exclusive_zone > 0)) {
103 continue; 64 continue;
104 } 65 }
105 struct wlr_box bounds; 66
106 if (state->exclusive_zone == -1) { 67 if (!surface->scene->layer_surface->initialized) {
107 bounds = full_area;
108 } else {
109 bounds = *usable_area;
110 }
111 struct wlr_box box = {
112 .width = state->desired_width,
113 .height = state->desired_height
114 };
115 // Horizontal axis
116 const uint32_t both_horiz = ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT
117 | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT;
118 if (box.width == 0) {
119 box.x = bounds.x;
120 } else if ((state->anchor & both_horiz) == both_horiz) {
121 box.x = bounds.x + ((bounds.width / 2) - (box.width / 2));
122 } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT)) {
123 box.x = bounds.x;
124 } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT)) {
125 box.x = bounds.x + (bounds.width - box.width);
126 } else {
127 box.x = bounds.x + ((bounds.width / 2) - (box.width / 2));
128 }
129 // Vertical axis
130 const uint32_t both_vert = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP
131 | ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM;
132 if (box.height == 0) {
133 box.y = bounds.y;
134 } else if ((state->anchor & both_vert) == both_vert) {
135 box.y = bounds.y + ((bounds.height / 2) - (box.height / 2));
136 } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP)) {
137 box.y = bounds.y;
138 } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM)) {
139 box.y = bounds.y + (bounds.height - box.height);
140 } else {
141 box.y = bounds.y + ((bounds.height / 2) - (box.height / 2));
142 }
143 // Margin
144 if (box.width == 0) {
145 box.x += state->margin.left;
146 box.width = bounds.width -
147 (state->margin.left + state->margin.right);
148 } else if ((state->anchor & both_horiz) == both_horiz) {
149 // don't apply margins
150 } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT)) {
151 box.x += state->margin.left;
152 } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT)) {
153 box.x -= state->margin.right;
154 }
155 if (box.height == 0) {
156 box.y += state->margin.top;
157 box.height = bounds.height -
158 (state->margin.top + state->margin.bottom);
159 } else if ((state->anchor & both_vert) == both_vert) {
160 // don't apply margins
161 } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP)) {
162 box.y += state->margin.top;
163 } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM)) {
164 box.y -= state->margin.bottom;
165 }
166 if (!sway_assert(box.width >= 0 && box.height >= 0,
167 "Expected layer surface to have positive size")) {
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,293 +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 if (set_focus) {
275 struct sway_layer_surface *layer =
276 find_mapped_layer_by_client(client, sway_layer->layer_surface->output);
277 if (layer) {
278 seat_set_focus_layer(seat, layer->layer_surface);
279 }
280 }
281 176
282 wlr_layer_surface_v1_destroy(sway_layer->layer_surface); 177 layer->output = NULL;
178 wlr_scene_node_destroy(&layer->scene->tree->node);
283} 179}
284 180
285static void handle_surface_commit(struct wl_listener *listener, void *data) { 181static void handle_node_destroy(struct wl_listener *listener, void *data) {
286 struct sway_layer_surface *layer = 182 struct sway_layer_surface *layer =
287 wl_container_of(listener, layer, surface_commit); 183 wl_container_of(listener, layer, node_destroy);
288 struct wlr_layer_surface_v1 *layer_surface = layer->layer_surface;
289 struct wlr_output *wlr_output = layer_surface->output;
290 sway_assert(wlr_output, "wlr_layer_surface_v1 has null output");
291 struct sway_output *output = wlr_output->data;
292 struct wlr_box old_extent = layer->extent;
293
294 bool layer_changed = false;
295 if (layer_surface->current.committed != 0
296 || layer->mapped != layer_surface->mapped) {
297 layer->mapped = layer_surface->mapped;
298 layer_changed = layer->layer != layer_surface->current.layer;
299 if (layer_changed) {
300 wl_list_remove(&layer->link);
301 wl_list_insert(&output->layers[layer_surface->current.layer],
302 &layer->link);
303 layer->layer = layer_surface->current.layer;
304 }
305 arrange_layers(output);
306 }
307 184
308 wlr_surface_get_extends(layer_surface->surface, &layer->extent); 185 // destroy the scene descriptor straight away if it exists, otherwise
309 layer->extent.x += layer->geo.x; 186 // we will try to reflow still considering the destroyed node.
310 layer->extent.y += layer->geo.y; 187 scene_descriptor_destroy(&layer->tree->node, SWAY_SCENE_DESC_LAYER_SHELL);
311 188
312 bool extent_changed = 189 // Determine if this layer is being used by an exclusive client. If it is,
313 memcmp(&old_extent, &layer->extent, sizeof(struct wlr_box)) != 0; 190 // try and find another layer owned by this client to pass focus to.
314 if (extent_changed || layer_changed) { 191 struct sway_seat *seat = input_manager_get_default_seat();
315 output_damage_box(output, &old_extent); 192 struct wl_client *client =
316 output_damage_surface(output, layer->geo.x, layer->geo.y, 193 wl_resource_get_client(layer->layer_surface->resource);
317 layer_surface->surface, true); 194 if (!server.session_lock.lock) {
318 } else { 195 struct sway_layer_surface *consider_layer =
319 output_damage_surface(output, layer->geo.x, layer->geo.y, 196 find_mapped_layer_by_client(client, layer->output);
320 layer_surface->surface, false); 197 if (consider_layer) {
321 } 198 seat_set_focus_layer(seat, consider_layer->layer_surface);
322
323 transaction_commit_dirty();
324}
325
326static void unmap(struct sway_layer_surface *sway_layer) {
327 struct sway_seat *seat;
328 wl_list_for_each(seat, &server.input->seats, link) {
329 if (seat->focused_layer == sway_layer->layer_surface) {
330 seat_set_focus_layer(seat, NULL);
331 } 199 }
332 } 200 }
333 201
334 cursor_rebase_all(); 202 if (layer->output) {
335 203 arrange_layers(layer->output);
336 struct wlr_output *wlr_output = sway_layer->layer_surface->output; 204 transaction_commit_dirty();
337 sway_assert(wlr_output, "wlr_layer_surface_v1 has null output");
338 struct sway_output *output = wlr_output->data;
339 output_damage_surface(output, sway_layer->geo.x, sway_layer->geo.y,
340 sway_layer->layer_surface->surface, true);
341}
342
343static void layer_subsurface_destroy(struct sway_layer_subsurface *subsurface);
344
345static void handle_destroy(struct wl_listener *listener, void *data) {
346 struct sway_layer_surface *sway_layer =
347 wl_container_of(listener, sway_layer, destroy);
348 sway_log(SWAY_DEBUG, "Layer surface destroyed (%s)",
349 sway_layer->layer_surface->namespace);
350 if (sway_layer->layer_surface->mapped) {
351 unmap(sway_layer);
352 } 205 }
353 206
354 struct sway_layer_subsurface *subsurface, *subsurface_tmp; 207 wlr_scene_node_destroy(&layer->popups->node);
355 wl_list_for_each_safe(subsurface, subsurface_tmp, &sway_layer->subsurfaces, link) {
356 layer_subsurface_destroy(subsurface);
357 }
358
359 wl_list_remove(&sway_layer->link);
360 wl_list_remove(&sway_layer->destroy.link);
361 wl_list_remove(&sway_layer->map.link);
362 wl_list_remove(&sway_layer->unmap.link);
363 wl_list_remove(&sway_layer->surface_commit.link);
364 wl_list_remove(&sway_layer->new_popup.link);
365 wl_list_remove(&sway_layer->new_subsurface.link);
366
367 struct wlr_output *wlr_output = sway_layer->layer_surface->output;
368 sway_assert(wlr_output, "wlr_layer_surface_v1 has null output");
369 struct sway_output *output = wlr_output->data;
370 arrange_layers(output);
371 transaction_commit_dirty();
372 wl_list_remove(&sway_layer->output_destroy.link);
373 sway_layer->layer_surface->output = NULL;
374
375 free(sway_layer);
376}
377
378static void handle_map(struct wl_listener *listener, void *data) {
379 struct sway_layer_surface *sway_layer = wl_container_of(listener,
380 sway_layer, map);
381 struct wlr_output *wlr_output = sway_layer->layer_surface->output;
382 sway_assert(wlr_output, "wlr_layer_surface_v1 has null output");
383 struct sway_output *output = wlr_output->data;
384 output_damage_surface(output, sway_layer->geo.x, sway_layer->geo.y,
385 sway_layer->layer_surface->surface, true);
386 wlr_surface_send_enter(sway_layer->layer_surface->surface,
387 sway_layer->layer_surface->output);
388 cursor_rebase_all();
389}
390 208
391static void handle_unmap(struct wl_listener *listener, void *data) { 209 wl_list_remove(&layer->map.link);
392 struct sway_layer_surface *sway_layer = wl_container_of( 210 wl_list_remove(&layer->unmap.link);
393 listener, sway_layer, unmap); 211 wl_list_remove(&layer->surface_commit.link);
394 unmap(sway_layer); 212 wl_list_remove(&layer->node_destroy.link);
395} 213 wl_list_remove(&layer->output_destroy.link);
396 214
397static void subsurface_damage(struct sway_layer_subsurface *subsurface, 215 layer->layer_surface->data = NULL;
398 bool whole) {
399 struct sway_layer_surface *layer = subsurface->layer_surface;
400 struct wlr_output *wlr_output = layer->layer_surface->output;
401 sway_assert(wlr_output, "wlr_layer_surface_v1 has null output");
402 struct sway_output *output = wlr_output->data;
403 int ox = subsurface->wlr_subsurface->current.x + layer->geo.x;
404 int oy = subsurface->wlr_subsurface->current.y + layer->geo.y;
405 output_damage_surface(
406 output, ox, oy, subsurface->wlr_subsurface->surface, whole);
407}
408 216
409static void subsurface_handle_unmap(struct wl_listener *listener, void *data) { 217 free(layer);
410 struct sway_layer_subsurface *subsurface =
411 wl_container_of(listener, subsurface, unmap);
412 subsurface_damage(subsurface, true);
413} 218}
414 219
415static void subsurface_handle_map(struct wl_listener *listener, void *data) { 220static void handle_surface_commit(struct wl_listener *listener, void *data) {
416 struct sway_layer_subsurface *subsurface = 221 struct sway_layer_surface *surface =
417 wl_container_of(listener, subsurface, map); 222 wl_container_of(listener, surface, surface_commit);
418 subsurface_damage(subsurface, true);
419}
420
421static void subsurface_handle_commit(struct wl_listener *listener, void *data) {
422 struct sway_layer_subsurface *subsurface =
423 wl_container_of(listener, subsurface, commit);
424 subsurface_damage(subsurface, false);
425}
426
427static void layer_subsurface_destroy(struct sway_layer_subsurface *subsurface) {
428 wl_list_remove(&subsurface->link);
429 wl_list_remove(&subsurface->map.link);
430 wl_list_remove(&subsurface->unmap.link);
431 wl_list_remove(&subsurface->destroy.link);
432 wl_list_remove(&subsurface->commit.link);
433 free(subsurface);
434}
435
436static void subsurface_handle_destroy(struct wl_listener *listener,
437 void *data) {
438 struct sway_layer_subsurface *subsurface =
439 wl_container_of(listener, subsurface, destroy);
440 layer_subsurface_destroy(subsurface);
441}
442 223
443static struct sway_layer_subsurface *create_subsurface( 224 struct wlr_layer_surface_v1 *layer_surface = surface->layer_surface;
444 struct wlr_subsurface *wlr_subsurface, 225 if (!layer_surface->initialized) {
445 struct sway_layer_surface *layer_surface) { 226 return;
446 struct sway_layer_subsurface *subsurface =
447 calloc(1, sizeof(struct sway_layer_subsurface));
448 if (subsurface == NULL) {
449 return NULL;
450 } 227 }
451 228
452 subsurface->wlr_subsurface = wlr_subsurface; 229 uint32_t committed = layer_surface->current.committed;
453 subsurface->layer_surface = layer_surface; 230 if (committed & WLR_LAYER_SURFACE_V1_STATE_LAYER) {
454 wl_list_insert(&layer_surface->subsurfaces, &subsurface->link); 231 enum zwlr_layer_shell_v1_layer layer_type = layer_surface->current.layer;
455 232 struct wlr_scene_tree *output_layer = sway_layer_get_scene(
456 subsurface->map.notify = subsurface_handle_map; 233 surface->output, layer_type);
457 wl_signal_add(&wlr_subsurface->events.map, &subsurface->map); 234 wlr_scene_node_reparent(&surface->scene->tree->node, output_layer);
458 subsurface->unmap.notify = subsurface_handle_unmap; 235 }
459 wl_signal_add(&wlr_subsurface->events.unmap, &subsurface->unmap);
460 subsurface->destroy.notify = subsurface_handle_destroy;
461 wl_signal_add(&wlr_subsurface->events.destroy, &subsurface->destroy);
462 subsurface->commit.notify = subsurface_handle_commit;
463 wl_signal_add(&wlr_subsurface->surface->events.commit, &subsurface->commit);
464
465 return subsurface;
466}
467
468static void handle_new_subsurface(struct wl_listener *listener, void *data) {
469 struct sway_layer_surface *sway_layer_surface =
470 wl_container_of(listener, sway_layer_surface, new_subsurface);
471 struct wlr_subsurface *wlr_subsurface = data;
472 create_subsurface(wlr_subsurface, sway_layer_surface);
473}
474
475 236
476static struct sway_layer_surface *popup_get_layer( 237 if (layer_surface->initial_commit || committed || layer_surface->surface->mapped != surface->mapped) {
477 struct sway_layer_popup *popup) { 238 surface->mapped = layer_surface->surface->mapped;
478 while (popup->parent_type == LAYER_PARENT_POPUP) { 239 arrange_layers(surface->output);
479 popup = popup->parent_popup; 240 transaction_commit_dirty();
480 } 241 }
481 return popup->parent_layer;
482} 242}
483 243
484static void popup_damage(struct sway_layer_popup *layer_popup, bool whole) { 244static void handle_map(struct wl_listener *listener, void *data) {
485 struct wlr_xdg_popup *popup = layer_popup->wlr_popup; 245 struct sway_layer_surface *surface = wl_container_of(listener,
486 struct wlr_surface *surface = popup->base->surface; 246 surface, map);
487 int popup_sx = popup->geometry.x - popup->base->current.geometry.x; 247
488 int popup_sy = popup->geometry.y - popup->base->current.geometry.y; 248 struct wlr_layer_surface_v1 *layer_surface =
489 int ox = popup_sx, oy = popup_sy; 249 surface->scene->layer_surface;
490 struct sway_layer_surface *layer; 250
491 while (true) { 251 // focus on new surface
492 if (layer_popup->parent_type == LAYER_PARENT_POPUP) { 252 if (layer_surface->current.keyboard_interactive &&
493 layer_popup = layer_popup->parent_popup; 253 (layer_surface->current.layer == ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY ||
494 ox += layer_popup->wlr_popup->geometry.x; 254 layer_surface->current.layer == ZWLR_LAYER_SHELL_V1_LAYER_TOP)) {
495 oy += layer_popup->wlr_popup->geometry.y; 255 struct sway_seat *seat;
496 } else { 256 wl_list_for_each(seat, &server.input->seats, link) {
497 layer = layer_popup->parent_layer; 257 // but only if the currently focused layer has a lower precedence
498 ox += layer->geo.x; 258 if (!seat->focused_layer ||
499 oy += layer->geo.y; 259 seat->focused_layer->current.layer >= layer_surface->current.layer) {
500 break; 260 seat_set_focus_layer(seat, layer_surface);
261 }
501 } 262 }
263 arrange_layers(surface->output);
502 } 264 }
503 struct wlr_output *wlr_output = layer->layer_surface->output;
504 sway_assert(wlr_output, "wlr_layer_surface_v1 has null output");
505 struct sway_output *output = wlr_output->data;
506 output_damage_surface(output, ox, oy, surface, whole);
507}
508 265
509static void popup_handle_map(struct wl_listener *listener, void *data) { 266 cursor_rebase_all();
510 struct sway_layer_popup *popup = wl_container_of(listener, popup, map);
511 struct sway_layer_surface *layer = popup_get_layer(popup);
512 struct wlr_output *wlr_output = layer->layer_surface->output;
513 sway_assert(wlr_output, "wlr_layer_surface_v1 has null output");
514 wlr_surface_send_enter(popup->wlr_popup->base->surface, wlr_output);
515 popup_damage(popup, true);
516} 267}
517 268
518static void popup_handle_unmap(struct wl_listener *listener, void *data) { 269static void handle_unmap(struct wl_listener *listener, void *data) {
519 struct sway_layer_popup *popup = wl_container_of(listener, popup, unmap); 270 struct sway_layer_surface *surface = wl_container_of(
520 popup_damage(popup, true); 271 listener, surface, unmap);
521} 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 }
522 278
523static void popup_handle_commit(struct wl_listener *listener, void *data) { 279 cursor_rebase_all();
524 struct sway_layer_popup *popup = wl_container_of(listener, popup, commit);
525 popup_damage(popup, false);
526} 280}
527 281
528static void popup_handle_destroy(struct wl_listener *listener, void *data) { 282static void popup_handle_destroy(struct wl_listener *listener, void *data) {
529 struct sway_layer_popup *popup = 283 struct sway_layer_popup *popup =
530 wl_container_of(listener, popup, destroy); 284 wl_container_of(listener, popup, destroy);
531 285
532 wl_list_remove(&popup->map.link);
533 wl_list_remove(&popup->unmap.link);
534 wl_list_remove(&popup->destroy.link); 286 wl_list_remove(&popup->destroy.link);
287 wl_list_remove(&popup->new_popup.link);
535 wl_list_remove(&popup->commit.link); 288 wl_list_remove(&popup->commit.link);
536 free(popup); 289 free(popup);
537} 290}
538 291
539static void popup_unconstrain(struct sway_layer_popup *popup) { 292static void popup_unconstrain(struct sway_layer_popup *popup) {
540 struct sway_layer_surface *layer = popup_get_layer(popup);
541 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;
542 295
543 struct wlr_output *wlr_output = layer->layer_surface->output; 296 // if a client tries to create a popup while we are in the process of destroying
544 sway_assert(wlr_output, "wlr_layer_surface_v1 has null output"); 297 // its output, don't crash.
545 struct sway_output *output = wlr_output->data; 298 if (!output) {
299 return;
300 }
301
302 int lx, ly;
303 wlr_scene_node_coords(&popup->toplevel->scene->tree->node, &lx, &ly);
546 304
547 // 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
548 // of the popup 306 // of the popup
549 struct wlr_box output_toplevel_sx_box = { 307 struct wlr_box output_toplevel_sx_box = {
550 .x = -layer->geo.x, 308 .x = output->lx - lx,
551 .y = -layer->geo.y, 309 .y = output->ly - ly,
552 .width = output->width, 310 .width = output->width,
553 .height = output->height, 311 .height = output->height,
554 }; 312 };
@@ -556,32 +314,38 @@ static void popup_unconstrain(struct sway_layer_popup *popup) {
556 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);
557} 315}
558 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
559static void popup_handle_new_popup(struct wl_listener *listener, void *data); 324static void popup_handle_new_popup(struct wl_listener *listener, void *data);
560 325
561static 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,
562 enum layer_parent parent_type, void *parent) { 327 struct sway_layer_surface *toplevel, struct wlr_scene_tree *parent) {
563 struct sway_layer_popup *popup = 328 struct sway_layer_popup *popup = calloc(1, sizeof(*popup));
564 calloc(1, sizeof(struct sway_layer_popup));
565 if (popup == NULL) { 329 if (popup == NULL) {
566 return NULL; 330 return NULL;
567 } 331 }
568 332
333 popup->toplevel = toplevel;
569 popup->wlr_popup = wlr_popup; 334 popup->wlr_popup = wlr_popup;
570 popup->parent_type = parent_type; 335 popup->scene = wlr_scene_xdg_surface_create(parent,
571 popup->parent_layer = parent; 336 wlr_popup->base);
337
338 if (!popup->scene) {
339 free(popup);
340 return NULL;
341 }
572 342
573 popup->map.notify = popup_handle_map;
574 wl_signal_add(&wlr_popup->base->events.map, &popup->map);
575 popup->unmap.notify = popup_handle_unmap;
576 wl_signal_add(&wlr_popup->base->events.unmap, &popup->unmap);
577 popup->destroy.notify = popup_handle_destroy; 343 popup->destroy.notify = popup_handle_destroy;
578 wl_signal_add(&wlr_popup->base->events.destroy, &popup->destroy); 344 wl_signal_add(&wlr_popup->base->events.destroy, &popup->destroy);
579 popup->commit.notify = popup_handle_commit;
580 wl_signal_add(&wlr_popup->base->surface->events.commit, &popup->commit);
581 popup->new_popup.notify = popup_handle_new_popup; 345 popup->new_popup.notify = popup_handle_new_popup;
582 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);
583 347 popup->commit.notify = popup_handle_commit;
584 popup_unconstrain(popup); 348 wl_signal_add(&wlr_popup->base->surface->events.commit, &popup->commit);
585 349
586 return popup; 350 return popup;
587} 351}
@@ -590,19 +354,14 @@ static void popup_handle_new_popup(struct wl_listener *listener, void *data) {
590 struct sway_layer_popup *sway_layer_popup = 354 struct sway_layer_popup *sway_layer_popup =
591 wl_container_of(listener, sway_layer_popup, new_popup); 355 wl_container_of(listener, sway_layer_popup, new_popup);
592 struct wlr_xdg_popup *wlr_popup = data; 356 struct wlr_xdg_popup *wlr_popup = data;
593 create_popup(wlr_popup, LAYER_PARENT_POPUP, sway_layer_popup); 357 create_popup(wlr_popup, sway_layer_popup->toplevel, sway_layer_popup->scene);
594} 358}
595 359
596static void handle_new_popup(struct wl_listener *listener, void *data) { 360static void handle_new_popup(struct wl_listener *listener, void *data) {
597 struct sway_layer_surface *sway_layer_surface = 361 struct sway_layer_surface *sway_layer_surface =
598 wl_container_of(listener, sway_layer_surface, new_popup); 362 wl_container_of(listener, sway_layer_surface, new_popup);
599 struct wlr_xdg_popup *wlr_popup = data; 363 struct wlr_xdg_popup *wlr_popup = data;
600 create_popup(wlr_popup, LAYER_PARENT_LAYER, sway_layer_surface); 364 create_popup(wlr_popup, sway_layer_surface, sway_layer_surface->popups);
601}
602
603struct sway_layer_surface *layer_from_wlr_layer_surface_v1(
604 struct wlr_layer_surface_v1 *layer_surface) {
605 return layer_surface->data;
606} 365}
607 366
608void handle_layer_shell_surface(struct wl_listener *listener, void *data) { 367void handle_layer_shell_surface(struct wl_listener *listener, void *data) {
@@ -634,10 +393,6 @@ void handle_layer_shell_surface(struct wl_listener *listener, void *data) {
634 sway_log(SWAY_ERROR, 393 sway_log(SWAY_ERROR,
635 "no output to auto-assign layer surface '%s' to", 394 "no output to auto-assign layer surface '%s' to",
636 layer_surface->namespace); 395 layer_surface->namespace);
637 // Note that layer_surface->output can be NULL
638 // here, but none of our destroy callbacks are
639 // registered yet so we don't have to make them
640 // handle that case.
641 wlr_layer_surface_v1_destroy(layer_surface); 396 wlr_layer_surface_v1_destroy(layer_surface);
642 return; 397 return;
643 } 398 }
@@ -646,44 +401,57 @@ void handle_layer_shell_surface(struct wl_listener *listener, void *data) {
646 layer_surface->output = output->wlr_output; 401 layer_surface->output = output->wlr_output;
647 } 402 }
648 403
649 struct sway_layer_surface *sway_layer = 404 struct sway_output *output = layer_surface->output->data;
650 calloc(1, sizeof(struct sway_layer_surface)); 405
651 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");
652 return; 413 return;
653 } 414 }
654 415
655 wl_list_init(&sway_layer->subsurfaces); 416 struct sway_layer_surface *surface =
417 sway_layer_surface_create(scene_surface);
418 if (!surface) {
419 wlr_layer_surface_v1_destroy(layer_surface);
656 420
657 sway_layer->surface_commit.notify = handle_surface_commit; 421 sway_log(SWAY_ERROR, "Could not allocate a sway_layer_surface");
658 wl_signal_add(&layer_surface->surface->events.commit, 422 return;
659 &sway_layer->surface_commit); 423 }
660
661 sway_layer->destroy.notify = handle_destroy;
662 wl_signal_add(&layer_surface->events.destroy, &sway_layer->destroy);
663 sway_layer->map.notify = handle_map;
664 wl_signal_add(&layer_surface->events.map, &sway_layer->map);
665 sway_layer->unmap.notify = handle_unmap;
666 wl_signal_add(&layer_surface->events.unmap, &sway_layer->unmap);
667 sway_layer->new_popup.notify = handle_new_popup;
668 wl_signal_add(&layer_surface->events.new_popup, &sway_layer->new_popup);
669 sway_layer->new_subsurface.notify = handle_new_subsurface;
670 wl_signal_add(&layer_surface->surface->events.new_subsurface,
671 &sway_layer->new_subsurface);
672
673 sway_layer->layer_surface = layer_surface;
674 layer_surface->data = sway_layer;
675 424
676 struct sway_output *output = layer_surface->output->data; 425 if (!scene_descriptor_assign(&scene_surface->tree->node,
677 sway_layer->output_destroy.notify = handle_output_destroy; 426 SWAY_SCENE_DESC_LAYER_SHELL, surface)) {
678 wl_signal_add(&output->events.disable, &sway_layer->output_destroy); 427 sway_log(SWAY_ERROR, "Failed to allocate a layer surface descriptor");
679 428 // destroying the layer_surface will also destroy its corresponding
680 wl_list_insert(&output->layers[layer_surface->pending.layer], 429 // scene node
681 &sway_layer->link); 430 wlr_layer_surface_v1_destroy(layer_surface);
682 431 return;
683 // Temporarily set the layer's current state to pending 432 }
684 // So that we can easily arrange it 433
685 struct wlr_layer_surface_v1_state old_state = layer_surface->current; 434 surface->output = output;
686 layer_surface->current = layer_surface->pending; 435
687 arrange_layers(output); 436 // now that the surface's output is known, we can advertise its scale
688 layer_surface->current = old_state; 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);
689} 457}
diff --git a/sway/desktop/output.c b/sway/desktop/output.c
index 5b7ad4ee..2722e556 100644
--- a/sway/desktop/output.c
+++ b/sway/desktop/output.c
@@ -1,44 +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>
7#include <wlr/backend/drm.h> 6#include <wlr/config.h>
8#include <wlr/backend/headless.h> 7#include <wlr/backend/headless.h>
8#include <wlr/render/swapchain.h>
9#include <wlr/render/wlr_renderer.h> 9#include <wlr/render/wlr_renderer.h>
10#include <wlr/types/wlr_buffer.h> 10#include <wlr/types/wlr_buffer.h>
11#include <wlr/types/wlr_drm_lease_v1.h> 11#include <wlr/types/wlr_gamma_control_v1.h>
12#include <wlr/types/wlr_matrix.h> 12#include <wlr/types/wlr_matrix.h>
13#include <wlr/types/wlr_output_damage.h>
14#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>
15#include <wlr/types/wlr_output.h> 16#include <wlr/types/wlr_output.h>
16#include <wlr/types/wlr_presentation_time.h> 17#include <wlr/types/wlr_presentation_time.h>
17#include <wlr/types/wlr_compositor.h> 18#include <wlr/types/wlr_compositor.h>
18#include <wlr/util/region.h> 19#include <wlr/util/region.h>
20#include <wlr/util/transform.h>
19#include "config.h" 21#include "config.h"
20#include "log.h" 22#include "log.h"
21#include "sway/config.h" 23#include "sway/config.h"
22#include "sway/desktop/transaction.h" 24#include "sway/desktop/transaction.h"
23#include "sway/input/input-manager.h" 25#include "sway/input/input-manager.h"
24#include "sway/input/seat.h" 26#include "sway/input/seat.h"
27#include "sway/ipc-server.h"
25#include "sway/layers.h" 28#include "sway/layers.h"
26#include "sway/output.h" 29#include "sway/output.h"
30#include "sway/scene_descriptor.h"
27#include "sway/server.h" 31#include "sway/server.h"
28#include "sway/surface.h"
29#include "sway/tree/arrange.h" 32#include "sway/tree/arrange.h"
30#include "sway/tree/container.h" 33#include "sway/tree/container.h"
31#include "sway/tree/root.h" 34#include "sway/tree/root.h"
32#include "sway/tree/view.h" 35#include "sway/tree/view.h"
33#include "sway/tree/workspace.h" 36#include "sway/tree/workspace.h"
34 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
35struct 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) {
36 for (int i = 0; i < root->outputs->length; ++i) { 56 for (int i = 0; i < root->outputs->length; ++i) {
37 struct sway_output *output = root->outputs->items[i]; 57 struct sway_output *output = root->outputs->items[i];
38 char identifier[128]; 58 if (output_match_name_or_id(output, name_or_id)) {
39 output_get_identifier(identifier, sizeof(identifier), output);
40 if (strcasecmp(identifier, name_or_id) == 0
41 || strcasecmp(output->wlr_output->name, name_or_id) == 0) {
42 return output; 59 return output;
43 } 60 }
44 } 61 }
@@ -48,536 +65,217 @@ struct sway_output *output_by_name_or_id(const char *name_or_id) {
48struct 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) {
49 struct sway_output *output; 66 struct sway_output *output;
50 wl_list_for_each(output, &root->all_outputs, link) { 67 wl_list_for_each(output, &root->all_outputs, link) {
51 char identifier[128]; 68 if (output_match_name_or_id(output, name_or_id)) {
52 output_get_identifier(identifier, sizeof(identifier), output);
53 if (strcasecmp(identifier, name_or_id) == 0
54 || strcasecmp(output->wlr_output->name, name_or_id) == 0) {
55 return output; 69 return output;
56 } 70 }
57 } 71 }
58 return NULL; 72 return NULL;
59} 73}
60 74
61struct surface_iterator_data {
62 sway_surface_iterator_func_t user_iterator;
63 void *user_data;
64
65 struct sway_output *output;
66 struct sway_view *view;
67 double ox, oy;
68 int width, height;
69};
70
71static bool get_surface_box(struct surface_iterator_data *data,
72 struct wlr_surface *surface, int sx, int sy,
73 struct wlr_box *surface_box) {
74 struct sway_output *output = data->output;
75
76 if (!wlr_surface_has_buffer(surface)) {
77 return false;
78 }
79 75
80 int sw = surface->current.width; 76struct sway_workspace *output_get_active_workspace(struct sway_output *output) {
81 int sh = surface->current.height; 77 struct sway_seat *seat = input_manager_current_seat();
82 78 struct sway_node *focus = seat_get_active_tiling_child(seat, &output->node);
83 struct wlr_box box = { 79 if (!focus) {
84 .x = floor(data->ox + sx), 80 if (!output->workspaces->length) {
85 .y = floor(data->oy + sy), 81 return NULL;
86 .width = sw, 82 }
87 .height = sh, 83 return output->workspaces->items[0];
88 };
89 if (surface_box != NULL) {
90 memcpy(surface_box, &box, sizeof(struct wlr_box));
91 } 84 }
92 85 return focus->sway_workspace;
93 struct wlr_box output_box = {
94 .width = output->width,
95 .height = output->height,
96 };
97
98 struct wlr_box intersection;
99 return wlr_box_intersection(&intersection, &output_box, &box);
100} 86}
101 87
102static void output_for_each_surface_iterator(struct wlr_surface *surface, 88struct send_frame_done_data {
103 int sx, int sy, void *_data) { 89 struct timespec when;
104 struct surface_iterator_data *data = _data; 90 int msec_until_refresh;
105 91 struct sway_output *output;
106 struct wlr_box box; 92};
107 bool intersects = get_surface_box(data, surface, sx, sy, &box);
108 if (!intersects) {
109 return;
110 }
111 93
112 data->user_iterator(data->output, data->view, surface, &box, 94struct buffer_timer {
113 data->user_data); 95 struct wl_listener destroy;
114} 96 struct wl_event_source *frame_done_timer;
97};
115 98
116void output_surface_for_each_surface(struct sway_output *output, 99static int handle_buffer_timer(void *data) {
117 struct wlr_surface *surface, double ox, double oy, 100 struct wlr_scene_buffer *buffer = data;
118 sway_surface_iterator_func_t iterator, void *user_data) {
119 struct surface_iterator_data data = {
120 .user_iterator = iterator,
121 .user_data = user_data,
122 .output = output,
123 .view = NULL,
124 .ox = ox,
125 .oy = oy,
126 .width = surface->current.width,
127 .height = surface->current.height,
128 };
129
130 wlr_surface_for_each_surface(surface,
131 output_for_each_surface_iterator, &data);
132}
133 101
134void output_view_for_each_surface(struct sway_output *output, 102 struct timespec now;
135 struct sway_view *view, sway_surface_iterator_func_t iterator, 103 clock_gettime(CLOCK_MONOTONIC, &now);
136 void *user_data) { 104 wlr_scene_buffer_send_frame_done(buffer, &now);
137 struct surface_iterator_data data = { 105 return 0;
138 .user_iterator = iterator,
139 .user_data = user_data,
140 .output = output,
141 .view = view,
142 .ox = view->container->surface_x - output->lx
143 - view->geometry.x,
144 .oy = view->container->surface_y - output->ly
145 - view->geometry.y,
146 .width = view->container->current.content_width,
147 .height = view->container->current.content_height,
148 };
149
150 view_for_each_surface(view, output_for_each_surface_iterator, &data);
151} 106}
152 107
153void output_view_for_each_popup_surface(struct sway_output *output, 108static void handle_buffer_timer_destroy(struct wl_listener *listener,
154 struct sway_view *view, sway_surface_iterator_func_t iterator, 109 void *data) {
155 void *user_data) { 110 struct buffer_timer *timer = wl_container_of(listener, timer, destroy);
156 struct surface_iterator_data data = {
157 .user_iterator = iterator,
158 .user_data = user_data,
159 .output = output,
160 .view = view,
161 .ox = view->container->surface_x - output->lx
162 - view->geometry.x,
163 .oy = view->container->surface_y - output->ly
164 - view->geometry.y,
165 .width = view->container->current.content_width,
166 .height = view->container->current.content_height,
167 };
168
169 view_for_each_popup_surface(view, output_for_each_surface_iterator, &data);
170}
171 111
172void output_layer_for_each_surface(struct sway_output *output, 112 wl_list_remove(&timer->destroy.link);
173 struct wl_list *layer_surfaces, sway_surface_iterator_func_t iterator, 113 wl_event_source_remove(timer->frame_done_timer);
174 void *user_data) { 114 free(timer);
175 struct sway_layer_surface *layer_surface;
176 wl_list_for_each(layer_surface, layer_surfaces, link) {
177 struct wlr_layer_surface_v1 *wlr_layer_surface_v1 =
178 layer_surface->layer_surface;
179 struct wlr_surface *surface = wlr_layer_surface_v1->surface;
180 struct surface_iterator_data data = {
181 .user_iterator = iterator,
182 .user_data = user_data,
183 .output = output,
184 .view = NULL,
185 .ox = layer_surface->geo.x,
186 .oy = layer_surface->geo.y,
187 .width = surface->current.width,
188 .height = surface->current.height,
189 };
190 wlr_layer_surface_v1_for_each_surface(wlr_layer_surface_v1,
191 output_for_each_surface_iterator, &data);
192 }
193} 115}
194 116
195void output_layer_for_each_toplevel_surface(struct sway_output *output, 117static struct buffer_timer *buffer_timer_get_or_create(struct wlr_scene_buffer *buffer) {
196 struct wl_list *layer_surfaces, sway_surface_iterator_func_t iterator, 118 struct buffer_timer *timer =
197 void *user_data) { 119 scene_descriptor_try_get(&buffer->node, SWAY_SCENE_DESC_BUFFER_TIMER);
198 struct sway_layer_surface *layer_surface; 120 if (timer) {
199 wl_list_for_each(layer_surface, layer_surfaces, link) { 121 return timer;
200 struct wlr_layer_surface_v1 *wlr_layer_surface_v1 =
201 layer_surface->layer_surface;
202 output_surface_for_each_surface(output, wlr_layer_surface_v1->surface,
203 layer_surface->geo.x, layer_surface->geo.y, iterator,
204 user_data);
205 } 122 }
206}
207
208 123
209void output_layer_for_each_popup_surface(struct sway_output *output, 124 timer = calloc(1, sizeof(struct buffer_timer));
210 struct wl_list *layer_surfaces, sway_surface_iterator_func_t iterator, 125 if (!timer) {
211 void *user_data) { 126 return NULL;
212 struct sway_layer_surface *layer_surface;
213 wl_list_for_each(layer_surface, layer_surfaces, link) {
214 struct wlr_layer_surface_v1 *wlr_layer_surface_v1 =
215 layer_surface->layer_surface;
216 struct wlr_surface *surface = wlr_layer_surface_v1->surface;
217 struct surface_iterator_data data = {
218 .user_iterator = iterator,
219 .user_data = user_data,
220 .output = output,
221 .view = NULL,
222 .ox = layer_surface->geo.x,
223 .oy = layer_surface->geo.y,
224 .width = surface->current.width,
225 .height = surface->current.height,
226 };
227 wlr_layer_surface_v1_for_each_popup_surface(wlr_layer_surface_v1,
228 output_for_each_surface_iterator, &data);
229 } 127 }
230}
231 128
232#if HAVE_XWAYLAND 129 timer->frame_done_timer = wl_event_loop_add_timer(server.wl_event_loop,
233void output_unmanaged_for_each_surface(struct sway_output *output, 130 handle_buffer_timer, buffer);
234 struct wl_list *unmanaged, sway_surface_iterator_func_t iterator, 131 if (!timer->frame_done_timer) {
235 void *user_data) { 132 free(timer);
236 struct sway_xwayland_unmanaged *unmanaged_surface; 133 return NULL;
237 wl_list_for_each(unmanaged_surface, unmanaged, link) {
238 struct wlr_xwayland_surface *xsurface =
239 unmanaged_surface->wlr_xwayland_surface;
240 double ox = unmanaged_surface->lx - output->lx;
241 double oy = unmanaged_surface->ly - output->ly;
242
243 output_surface_for_each_surface(output, xsurface->surface, ox, oy,
244 iterator, user_data);
245 } 134 }
246}
247#endif
248 135
249void output_drag_icons_for_each_surface(struct sway_output *output, 136 scene_descriptor_assign(&buffer->node, SWAY_SCENE_DESC_BUFFER_TIMER, timer);
250 struct wl_list *drag_icons, sway_surface_iterator_func_t iterator,
251 void *user_data) {
252 struct sway_drag_icon *drag_icon;
253 wl_list_for_each(drag_icon, drag_icons, link) {
254 double ox = drag_icon->x - output->lx;
255 double oy = drag_icon->y - output->ly;
256
257 if (drag_icon->wlr_drag_icon->mapped) {
258 output_surface_for_each_surface(output,
259 drag_icon->wlr_drag_icon->surface, ox, oy,
260 iterator, user_data);
261 }
262 }
263}
264 137
265static void for_each_surface_container_iterator(struct sway_container *con, 138 timer->destroy.notify = handle_buffer_timer_destroy;
266 void *_data) { 139 wl_signal_add(&buffer->node.events.destroy, &timer->destroy);
267 if (!con->view || !view_is_visible(con->view)) {
268 return;
269 }
270 140
271 struct surface_iterator_data *data = _data; 141 return timer;
272 output_view_for_each_surface(data->output, con->view,
273 data->user_iterator, data->user_data);
274} 142}
275 143
276static void output_for_each_surface(struct sway_output *output, 144static void send_frame_done_iterator(struct wlr_scene_buffer *buffer,
277 sway_surface_iterator_func_t iterator, void *user_data) { 145 int x, int y, void *user_data) {
278 if (server.session_lock.locked) { 146 struct send_frame_done_data *data = user_data;
279 if (server.session_lock.lock == NULL) { 147 struct sway_output *output = data->output;
280 return; 148 int view_max_render_time = 0;
281 }
282 struct wlr_session_lock_surface_v1 *lock_surface;
283 wl_list_for_each(lock_surface, &server.session_lock.lock->surfaces, link) {
284 if (lock_surface->output != output->wlr_output) {
285 continue;
286 }
287 if (!lock_surface->mapped) {
288 continue;
289 }
290 149
291 output_surface_for_each_surface(output, lock_surface->surface, 150 if (buffer->primary_output != data->output->scene_output) {
292 0.0, 0.0, iterator, user_data);
293 }
294 return; 151 return;
295 } 152 }
296 153
297 if (output_has_opaque_overlay_layer_surface(output)) { 154 struct wlr_scene_node *current = &buffer->node;
298 goto overlay; 155 while (true) {
299 } 156 struct sway_view *view = scene_descriptor_try_get(current,
300 157 SWAY_SCENE_DESC_VIEW);
301 struct surface_iterator_data data = { 158 if (view) {
302 .user_iterator = iterator, 159 view_max_render_time = view->max_render_time;
303 .user_data = user_data, 160 break;
304 .output = output,
305 .view = NULL,
306 };
307
308 struct sway_workspace *workspace = output_get_active_workspace(output);
309 struct sway_container *fullscreen_con = root->fullscreen_global;
310 if (!fullscreen_con) {
311 if (!workspace) {
312 return;
313 }
314 fullscreen_con = workspace->current.fullscreen;
315 }
316 if (fullscreen_con) {
317 for_each_surface_container_iterator(fullscreen_con, &data);
318 container_for_each_child(fullscreen_con,
319 for_each_surface_container_iterator, &data);
320
321 // TODO: Show transient containers for fullscreen global
322 if (fullscreen_con == workspace->current.fullscreen) {
323 for (int i = 0; i < workspace->current.floating->length; ++i) {
324 struct sway_container *floater =
325 workspace->current.floating->items[i];
326 if (container_is_transient_for(floater, fullscreen_con)) {
327 for_each_surface_container_iterator(floater, &data);
328 }
329 }
330 } 161 }
331#if HAVE_XWAYLAND
332 output_unmanaged_for_each_surface(output, &root->xwayland_unmanaged,
333 iterator, user_data);
334#endif
335 } else {
336 output_layer_for_each_surface(output,
337 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND],
338 iterator, user_data);
339 output_layer_for_each_surface(output,
340 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM],
341 iterator, user_data);
342
343 workspace_for_each_container(workspace,
344 for_each_surface_container_iterator, &data);
345
346#if HAVE_XWAYLAND
347 output_unmanaged_for_each_surface(output, &root->xwayland_unmanaged,
348 iterator, user_data);
349#endif
350 output_layer_for_each_surface(output,
351 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP],
352 iterator, user_data);
353 }
354
355overlay:
356 output_layer_for_each_surface(output,
357 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY],
358 iterator, user_data);
359 output_drag_icons_for_each_surface(output, &root->drag_icons,
360 iterator, user_data);
361}
362
363static int scale_length(int length, int offset, float scale) {
364 return round((offset + length) * scale) - round(offset * scale);
365}
366 162
367void scale_box(struct wlr_box *box, float scale) { 163 if (!current->parent) {
368 box->width = scale_length(box->width, box->x, scale); 164 break;
369 box->height = scale_length(box->height, box->y, scale);
370 box->x = round(box->x * scale);
371 box->y = round(box->y * scale);
372}
373
374struct sway_workspace *output_get_active_workspace(struct sway_output *output) {
375 struct sway_seat *seat = input_manager_current_seat();
376 struct sway_node *focus = seat_get_active_tiling_child(seat, &output->node);
377 if (!focus) {
378 if (!output->workspaces->length) {
379 return NULL;
380 } 165 }
381 return output->workspaces->items[0];
382 }
383 return focus->sway_workspace;
384}
385 166
386bool output_has_opaque_overlay_layer_surface(struct sway_output *output) { 167 current = &current->parent->node;
387 struct sway_layer_surface *sway_layer_surface;
388 wl_list_for_each(sway_layer_surface,
389 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], link) {
390 struct wlr_surface *wlr_surface = sway_layer_surface->layer_surface->surface;
391 pixman_box32_t output_box = {
392 .x2 = output->width,
393 .y2 = output->height,
394 };
395 pixman_region32_t surface_opaque_box;
396 pixman_region32_init(&surface_opaque_box);
397 pixman_region32_copy(&surface_opaque_box, &wlr_surface->opaque_region);
398 pixman_region32_translate(&surface_opaque_box,
399 sway_layer_surface->geo.x, sway_layer_surface->geo.y);
400 pixman_region_overlap_t contains =
401 pixman_region32_contains_rectangle(&surface_opaque_box, &output_box);
402 pixman_region32_fini(&surface_opaque_box);
403
404 if (contains == PIXMAN_REGION_IN) {
405 return true;
406 }
407 } 168 }
408 return false;
409}
410
411struct send_frame_done_data {
412 struct timespec when;
413 int msec_until_refresh;
414};
415
416static void send_frame_done_iterator(struct sway_output *output,
417 struct sway_view *view, struct wlr_surface *surface,
418 struct wlr_box *box, void *user_data) {
419 int view_max_render_time = 0;
420 if (view != NULL) {
421 view_max_render_time = view->max_render_time;
422 }
423
424 struct send_frame_done_data *data = user_data;
425 169
426 int delay = data->msec_until_refresh - output->max_render_time 170 int delay = data->msec_until_refresh - output->max_render_time
427 - view_max_render_time; 171 - view_max_render_time;
428 172
429 if (output->max_render_time == 0 || view_max_render_time == 0 || delay < 1) { 173 struct buffer_timer *timer = NULL;
430 wlr_surface_send_frame_done(surface, &data->when);
431 } else {
432 struct sway_surface *sway_surface = surface->data;
433 wl_event_source_timer_update(sway_surface->frame_done_timer, delay);
434 }
435}
436 174
437static void send_frame_done(struct sway_output *output, struct send_frame_done_data *data) { 175 if (output->max_render_time != 0 && view_max_render_time != 0 && delay > 0) {
438 output_for_each_surface(output, send_frame_done_iterator, data); 176 timer = buffer_timer_get_or_create(buffer);
439}
440
441static void count_surface_iterator(struct sway_output *output,
442 struct sway_view *view, struct wlr_surface *surface,
443 struct wlr_box *box, void *data) {
444 size_t *n = data;
445 (*n)++;
446}
447
448static bool scan_out_fullscreen_view(struct sway_output *output,
449 struct sway_view *view) {
450 struct wlr_output *wlr_output = output->wlr_output;
451 struct sway_workspace *workspace = output->current.active_workspace;
452 if (!sway_assert(workspace, "Expected an active workspace")) {
453 return false;
454 }
455
456 if (server.session_lock.locked) {
457 return false;
458 } 177 }
459 178
460 if (!wl_list_empty(&view->saved_buffers)) { 179 if (timer) {
461 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);
462 } 183 }
184}
463 185
464 for (int i = 0; i < workspace->current.floating->length; ++i) { 186static enum wlr_scale_filter_mode get_scale_filter(struct sway_output *output,
465 struct sway_container *floater = 187 struct wlr_scene_buffer *buffer) {
466 workspace->current.floating->items[i]; 188 // if we are scaling down, we should always choose linear
467 if (container_is_transient_for(floater, view->container)) { 189 if (buffer->dst_width > 0 && buffer->dst_height > 0 && (
468 return false; 190 buffer->dst_width < buffer->buffer_width ||
469 } 191 buffer->dst_height < buffer->buffer_height)) {
192 return WLR_SCALE_FILTER_BILINEAR;
470 } 193 }
471 194
472#if HAVE_XWAYLAND 195 switch (output->scale_filter) {
473 if (!wl_list_empty(&root->xwayland_unmanaged)) { 196 case SCALE_FILTER_LINEAR:
474 return false; 197 return WLR_SCALE_FILTER_BILINEAR;
198 case SCALE_FILTER_NEAREST:
199 return WLR_SCALE_FILTER_NEAREST;
200 default:
201 abort(); // unreachable
475 } 202 }
476#endif 203}
477 204
478 if (!wl_list_empty(&output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY])) { 205static void output_configure_scene(struct sway_output *output,
479 return false; 206 struct wlr_scene_node *node, float opacity) {
480 } 207 if (!node->enabled) {
481 if (!wl_list_empty(&root->drag_icons)) { 208 return;
482 return false;
483 } 209 }
484 210
485 struct wlr_surface *surface = view->surface; 211 struct sway_container *con =
486 if (surface == NULL) { 212 scene_descriptor_try_get(node, SWAY_SCENE_DESC_CONTAINER);
487 return false; 213 if (con) {
488 } 214 opacity = con->alpha;
489 size_t n_surfaces = 0;
490 output_view_for_each_surface(output, view,
491 count_surface_iterator, &n_surfaces);
492 if (n_surfaces != 1) {
493 return false;
494 } 215 }
495 216
496 if (surface->buffer == NULL) { 217 if (node->type == WLR_SCENE_NODE_BUFFER) {
497 return false; 218 struct wlr_scene_buffer *buffer = wlr_scene_buffer_from_node(node);
498 }
499 219
500 if ((float)surface->current.scale != wlr_output->scale || 220 // hack: don't call the scene setter because that will damage all outputs
501 surface->current.transform != wlr_output->transform) { 221 // We don't want to damage outputs that aren't our current output that
502 return false; 222 // we're configuring
503 } 223 buffer->filter_mode = get_scale_filter(output, buffer);
504 224
505 wlr_output_attach_buffer(wlr_output, &surface->buffer->base); 225 wlr_scene_buffer_set_opacity(buffer, opacity);
506 if (!wlr_output_test(wlr_output)) { 226 } else if (node->type == WLR_SCENE_NODE_TREE) {
507 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 }
508 } 232 }
509
510 wlr_presentation_surface_sampled_on_output(server.presentation, surface,
511 wlr_output);
512
513 return wlr_output_commit(wlr_output);
514} 233}
515 234
516static int output_repaint_timer_handler(void *data) { 235static int output_repaint_timer_handler(void *data) {
517 struct sway_output *output = data; 236 struct sway_output *output = data;
518 if (output->wlr_output == NULL) {
519 return 0;
520 }
521 237
522 output->wlr_output->frame_pending = false; 238 if (!output->enabled) {
523
524 struct sway_workspace *workspace = output->current.active_workspace;
525 if (workspace == NULL) {
526 return 0; 239 return 0;
527 } 240 }
528 241
529 struct sway_container *fullscreen_con = root->fullscreen_global; 242 output->wlr_output->frame_pending = false;
530 if (!fullscreen_con) {
531 fullscreen_con = workspace->current.fullscreen;
532 }
533 243
534 if (fullscreen_con && fullscreen_con->view && !debug.noscanout) { 244 output_configure_scene(output, &root->root_scene->tree.node, 1.0f);
535 // Try to scan-out the fullscreen view
536 static bool last_scanned_out = false;
537 bool scanned_out =
538 scan_out_fullscreen_view(output, fullscreen_con->view);
539 245
540 if (scanned_out && !last_scanned_out) { 246 if (output->gamma_lut_changed) {
541 sway_log(SWAY_DEBUG, "Scanning out fullscreen view on %s", 247 struct wlr_output_state pending;
542 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;
543 } 251 }
544 if (last_scanned_out && !scanned_out) { 252
545 sway_log(SWAY_DEBUG, "Stopping fullscreen view scan out on %s", 253 output->gamma_lut_changed = false;
546 output->wlr_output->name); 254 struct wlr_gamma_control_v1 *gamma_control =
547 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;
548 } 260 }
549 last_scanned_out = scanned_out;
550 261
551 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);
552 return 0; 265 return 0;
553 } 266 }
554 }
555 267
556 bool needs_frame; 268 wlr_output_state_finish(&pending);
557 pixman_region32_t damage;
558 pixman_region32_init(&damage);
559 if (!wlr_output_damage_attach_render(output->damage,
560 &needs_frame, &damage)) {
561 return 0; 269 return 0;
562 } 270 }
563 271
564 if (needs_frame) { 272 wlr_scene_output_commit(output->scene_output, NULL);
565 struct timespec now;
566 clock_gettime(CLOCK_MONOTONIC, &now);
567
568 output_render(output, &now, &damage);
569 } else {
570 wlr_output_rollback(output->wlr_output);
571 }
572
573 pixman_region32_fini(&damage);
574
575 return 0; 273 return 0;
576} 274}
577 275
578static void damage_handle_frame(struct wl_listener *listener, void *user_data) { 276static void handle_frame(struct wl_listener *listener, void *user_data) {
579 struct sway_output *output = 277 struct sway_output *output =
580 wl_container_of(listener, output, damage_frame); 278 wl_container_of(listener, output, frame);
581 if (!output->enabled || !output->wlr_output->enabled) { 279 if (!output->enabled || !output->wlr_output->enabled) {
582 return; 280 return;
583 } 281 }
@@ -588,9 +286,7 @@ static void damage_handle_frame(struct wl_listener *listener, void *user_data) {
588 286
589 if (output->max_render_time != 0) { 287 if (output->max_render_time != 0) {
590 struct timespec now; 288 struct timespec now;
591 clockid_t presentation_clock 289 clock_gettime(CLOCK_MONOTONIC, &now);
592 = wlr_backend_get_presentation_clock(server.backend);
593 clock_gettime(presentation_clock, &now);
594 290
595 const long NSEC_IN_SECONDS = 1000000000; 291 const long NSEC_IN_SECONDS = 1000000000;
596 struct timespec predicted_refresh = output->last_presentation; 292 struct timespec predicted_refresh = output->last_presentation;
@@ -637,116 +333,8 @@ static void damage_handle_frame(struct wl_listener *listener, void *user_data) {
637 struct send_frame_done_data data = {0}; 333 struct send_frame_done_data data = {0};
638 clock_gettime(CLOCK_MONOTONIC, &data.when); 334 clock_gettime(CLOCK_MONOTONIC, &data.when);
639 data.msec_until_refresh = msec_until_refresh; 335 data.msec_until_refresh = msec_until_refresh;
640 send_frame_done(output, &data); 336 data.output = output;
641} 337 wlr_scene_output_for_each_buffer(output->scene_output, send_frame_done_iterator, &data);
642
643void output_damage_whole(struct sway_output *output) {
644 // The output can exist with no wlr_output if it's just been disconnected
645 // and the transaction to evacuate it has't completed yet.
646 if (output && output->wlr_output && output->damage) {
647 wlr_output_damage_add_whole(output->damage);
648 }
649}
650
651static void damage_surface_iterator(struct sway_output *output,
652 struct sway_view *view, struct wlr_surface *surface,
653 struct wlr_box *_box, void *_data) {
654 bool *data = _data;
655 bool whole = *data;
656
657 struct wlr_box box = *_box;
658 scale_box(&box, output->wlr_output->scale);
659
660 pixman_region32_t damage;
661 pixman_region32_init(&damage);
662 wlr_surface_get_effective_damage(surface, &damage);
663 wlr_region_scale(&damage, &damage, output->wlr_output->scale);
664 if (ceil(output->wlr_output->scale) > surface->current.scale) {
665 // When scaling up a surface, it'll become blurry so we need to
666 // expand the damage region
667 wlr_region_expand(&damage, &damage,
668 ceil(output->wlr_output->scale) - surface->current.scale);
669 }
670 pixman_region32_translate(&damage, box.x, box.y);
671 wlr_output_damage_add(output->damage, &damage);
672 pixman_region32_fini(&damage);
673
674 if (whole) {
675 wlr_output_damage_add_box(output->damage, &box);
676 }
677
678 if (!wl_list_empty(&surface->current.frame_callback_list)) {
679 wlr_output_schedule_frame(output->wlr_output);
680 }
681}
682
683void output_damage_surface(struct sway_output *output, double ox, double oy,
684 struct wlr_surface *surface, bool whole) {
685 output_surface_for_each_surface(output, surface, ox, oy,
686 damage_surface_iterator, &whole);
687}
688
689void output_damage_from_view(struct sway_output *output,
690 struct sway_view *view) {
691 if (!view_is_visible(view)) {
692 return;
693 }
694 bool whole = false;
695 output_view_for_each_surface(output, view, damage_surface_iterator, &whole);
696}
697
698// Expecting an unscaled box in layout coordinates
699void output_damage_box(struct sway_output *output, struct wlr_box *_box) {
700 struct wlr_box box;
701 memcpy(&box, _box, sizeof(struct wlr_box));
702 box.x -= output->lx;
703 box.y -= output->ly;
704 scale_box(&box, output->wlr_output->scale);
705 wlr_output_damage_add_box(output->damage, &box);
706}
707
708static void damage_child_views_iterator(struct sway_container *con,
709 void *data) {
710 if (!con->view || !view_is_visible(con->view)) {
711 return;
712 }
713 struct sway_output *output = data;
714 bool whole = true;
715 output_view_for_each_surface(output, con->view, damage_surface_iterator,
716 &whole);
717}
718
719void output_damage_whole_container(struct sway_output *output,
720 struct sway_container *con) {
721 // Pad the box by 1px, because the width is a double and might be a fraction
722 struct wlr_box box = {
723 .x = con->current.x - output->lx - 1,
724 .y = con->current.y - output->ly - 1,
725 .width = con->current.width + 2,
726 .height = con->current.height + 2,
727 };
728 scale_box(&box, output->wlr_output->scale);
729 wlr_output_damage_add_box(output->damage, &box);
730 // Damage subsurfaces as well, which may extend outside the box
731 if (con->view) {
732 damage_child_views_iterator(con, output);
733 } else {
734 container_for_each_child(con, damage_child_views_iterator, output);
735 }
736}
737
738static void damage_handle_destroy(struct wl_listener *listener, void *data) {
739 struct sway_output *output =
740 wl_container_of(listener, output, damage_destroy);
741 if (!output->enabled) {
742 return;
743 }
744 output_disable(output);
745
746 wl_list_remove(&output->damage_destroy.link);
747 wl_list_remove(&output->damage_frame.link);
748
749 transaction_commit_dirty();
750} 338}
751 339
752static void update_output_manager_config(struct sway_server *server) { 340static void update_output_manager_config(struct sway_server *server) {
@@ -763,34 +351,37 @@ static void update_output_manager_config(struct sway_server *server) {
763 struct wlr_box output_box; 351 struct wlr_box output_box;
764 wlr_output_layout_get_box(root->output_layout, 352 wlr_output_layout_get_box(root->output_layout,
765 output->wlr_output, &output_box); 353 output->wlr_output, &output_box);
766 // We mark the output enabled even if it is switched off by DPMS 354 // We mark the output enabled when it's switched off but not disabled
767 config_head->state.enabled = output->current_mode != NULL && output->enabled; 355 config_head->state.enabled = !wlr_box_empty(&output_box);
768 config_head->state.mode = output->current_mode; 356 config_head->state.x = output_box.x;
769 if (!wlr_box_empty(&output_box)) { 357 config_head->state.y = output_box.y;
770 config_head->state.x = output_box.x;
771 config_head->state.y = output_box.y;
772 }
773 } 358 }
774 359
775 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();
776} 363}
777 364
778static void handle_destroy(struct wl_listener *listener, void *data) { 365static void begin_destroy(struct sway_output *output) {
779 struct sway_output *output = wl_container_of(listener, output, destroy);
780 struct sway_server *server = output->server; 366 struct sway_server *server = output->server;
781 output_begin_destroy(output);
782 367
783 if (output->enabled) { 368 if (output->enabled) {
784 output_disable(output); 369 output_disable(output);
785 } 370 }
786 371
372 output_begin_destroy(output);
373
787 wl_list_remove(&output->link); 374 wl_list_remove(&output->link);
788 375
376 wl_list_remove(&output->layout_destroy.link);
789 wl_list_remove(&output->destroy.link); 377 wl_list_remove(&output->destroy.link);
790 wl_list_remove(&output->commit.link); 378 wl_list_remove(&output->commit.link);
791 wl_list_remove(&output->mode.link);
792 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);
793 382
383 wlr_scene_output_destroy(output->scene_output);
384 output->scene_output = NULL;
794 output->wlr_output->data = NULL; 385 output->wlr_output->data = NULL;
795 output->wlr_output = NULL; 386 output->wlr_output = NULL;
796 387
@@ -799,34 +390,14 @@ static void handle_destroy(struct wl_listener *listener, void *data) {
799 update_output_manager_config(server); 390 update_output_manager_config(server);
800} 391}
801 392
802static void handle_mode(struct wl_listener *listener, void *data) { 393static void handle_destroy(struct wl_listener *listener, void *data) {
803 struct sway_output *output = wl_container_of(listener, output, mode); 394 struct sway_output *output = wl_container_of(listener, output, destroy);
804 if (!output->enabled && !output->enabling) { 395 begin_destroy(output);
805 struct output_config *oc = find_output_config(output);
806 if (output->wlr_output->current_mode != NULL &&
807 (!oc || oc->enabled)) {
808 // We want to enable this output, but it didn't work last time,
809 // possibly because we hadn't enough CRTCs. Try again now that the
810 // output has a mode.
811 sway_log(SWAY_DEBUG, "Output %s has gained a CRTC, "
812 "trying to enable it", output->wlr_output->name);
813 apply_output_config(oc, output);
814 }
815 return;
816 }
817 if (!output->enabled) {
818 return;
819 }
820 arrange_layers(output);
821 arrange_output(output);
822 transaction_commit_dirty();
823
824 update_output_manager_config(output->server);
825} 396}
826 397
827static void update_textures(struct sway_container *con, void *data) { 398static void handle_layout_destroy(struct wl_listener *listener, void *data) {
828 container_update_title_textures(con); 399 struct sway_output *output = wl_container_of(listener, output, layout_destroy);
829 container_update_marks_textures(con); 400 begin_destroy(output);
830} 401}
831 402
832static void handle_commit(struct wl_listener *listener, void *data) { 403static void handle_commit(struct wl_listener *listener, void *data) {
@@ -837,17 +408,21 @@ static void handle_commit(struct wl_listener *listener, void *data) {
837 return; 408 return;
838 } 409 }
839 410
840 if (event->committed & WLR_OUTPUT_STATE_SCALE) { 411 if (event->state->committed & (
841 output_for_each_container(output, update_textures, NULL); 412 WLR_OUTPUT_STATE_MODE |
842 } 413 WLR_OUTPUT_STATE_TRANSFORM |
843 414 WLR_OUTPUT_STATE_SCALE)) {
844 if (event->committed & (WLR_OUTPUT_STATE_TRANSFORM | WLR_OUTPUT_STATE_SCALE)) {
845 arrange_layers(output); 415 arrange_layers(output);
846 arrange_output(output); 416 arrange_output(output);
847 transaction_commit_dirty(); 417 transaction_commit_dirty();
848 418
849 update_output_manager_config(output->server); 419 update_output_manager_config(output->server);
850 } 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 }
851} 426}
852 427
853static void handle_present(struct wl_listener *listener, void *data) { 428static void handle_present(struct wl_listener *listener, void *data) {
@@ -862,6 +437,13 @@ static void handle_present(struct wl_listener *listener, void *data) {
862 output->refresh_nsec = output_event->refresh; 437 output->refresh_nsec = output_event->refresh;
863} 438}
864 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
865static unsigned int last_headless_num = 0; 447static unsigned int last_headless_num = 0;
866 448
867void handle_new_output(struct wl_listener *listener, void *data) { 449void handle_new_output(struct wl_listener *listener, void *data) {
@@ -883,10 +465,14 @@ void handle_new_output(struct wl_listener *listener, void *data) {
883 465
884 if (wlr_output->non_desktop) { 466 if (wlr_output->non_desktop) {
885 sway_log(SWAY_DEBUG, "Not configuring non-desktop output"); 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
886 if (server->drm_lease_manager) { 470 if (server->drm_lease_manager) {
887 wlr_drm_lease_v1_manager_offer_output(server->drm_lease_manager, 471 wlr_drm_lease_v1_manager_offer_output(server->drm_lease_manager,
888 wlr_output); 472 wlr_output);
889 } 473 }
474#endif
475 list_add(root->non_desktop_outputs, non_desktop);
890 return; 476 return;
891 } 477 }
892 478
@@ -896,32 +482,46 @@ void handle_new_output(struct wl_listener *listener, void *data) {
896 return; 482 return;
897 } 483 }
898 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 }
493
899 struct sway_output *output = output_create(wlr_output); 494 struct sway_output *output = output_create(wlr_output);
900 if (!output) { 495 if (!output) {
496 sway_log(SWAY_ERROR, "Failed to create a sway output");
497 wlr_scene_output_destroy(scene_output);
901 return; 498 return;
902 } 499 }
500
903 output->server = server; 501 output->server = server;
904 output->damage = wlr_output_damage_create(wlr_output); 502 output->scene_output = scene_output;
905 503
504 wl_signal_add(&root->output_layout->events.destroy, &output->layout_destroy);
505 output->layout_destroy.notify = handle_layout_destroy;
906 wl_signal_add(&wlr_output->events.destroy, &output->destroy); 506 wl_signal_add(&wlr_output->events.destroy, &output->destroy);
907 output->destroy.notify = handle_destroy; 507 output->destroy.notify = handle_destroy;
908 wl_signal_add(&wlr_output->events.commit, &output->commit); 508 wl_signal_add(&wlr_output->events.commit, &output->commit);
909 output->commit.notify = handle_commit; 509 output->commit.notify = handle_commit;
910 wl_signal_add(&wlr_output->events.mode, &output->mode);
911 output->mode.notify = handle_mode;
912 wl_signal_add(&wlr_output->events.present, &output->present); 510 wl_signal_add(&wlr_output->events.present, &output->present);
913 output->present.notify = handle_present; 511 output->present.notify = handle_present;
914 wl_signal_add(&output->damage->events.frame, &output->damage_frame); 512 wl_signal_add(&wlr_output->events.frame, &output->frame);
915 output->damage_frame.notify = damage_handle_frame; 513 output->frame.notify = handle_frame;
916 wl_signal_add(&output->damage->events.destroy, &output->damage_destroy); 514 wl_signal_add(&wlr_output->events.request_state, &output->request_state);
917 output->damage_destroy.notify = damage_handle_destroy; 515 output->request_state.notify = handle_request_state;
918 516
919 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,
920 output_repaint_timer_handler, output); 518 output_repaint_timer_handler, output);
921 519
922 struct output_config *oc = find_output_config(output); 520 if (server->session_lock.lock) {
923 apply_output_config(oc, output); 521 sway_session_lock_add_output(server->session_lock.lock, output);
924 free_output_config(oc); 522 }
523
524 apply_all_output_configs();
925 525
926 transaction_commit_dirty(); 526 transaction_commit_dirty();
927 527
@@ -935,62 +535,104 @@ void handle_output_layout_change(struct wl_listener *listener,
935 update_output_manager_config(server); 535 update_output_manager_config(server);
936} 536}
937 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
938static void output_manager_apply(struct sway_server *server, 581static void output_manager_apply(struct sway_server *server,
939 struct wlr_output_configuration_v1 *config, bool test_only) { 582 struct wlr_output_configuration_v1 *config, bool test_only) {
940 // TODO: perform atomic tests on the whole backend atomically 583 size_t configs_len = wl_list_length(&root->all_outputs);
941 584 struct matched_output_config *configs = calloc(configs_len, sizeof(*configs));
942 struct wlr_output_configuration_head_v1 *config_head; 585 if (!configs) {
943 // First disable outputs we need to disable 586 return;
944 bool ok = true; 587 }
945 wl_list_for_each(config_head, &config->heads, link) { 588
946 struct wlr_output *wlr_output = config_head->state.output; 589 int config_idx = 0;
947 struct sway_output *output = wlr_output->data; 590 struct sway_output *sway_output;
948 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--;
949 continue; 594 continue;
950 } 595 }
951 struct output_config *oc = new_output_config(output->wlr_output->name);
952 oc->enabled = false;
953 596
954 if (test_only) { 597 struct matched_output_config *cfg = &configs[config_idx++];
955 ok &= test_output_config(oc, output); 598 cfg->output = sway_output;
956 } else {
957 oc = store_output_config(oc);
958 ok &= apply_output_config(oc, output);
959 }
960 }
961 599
962 // Then enable outputs that need to 600 struct wlr_output_configuration_head_v1 *config_head;
963 wl_list_for_each(config_head, &config->heads, link) { 601 wl_list_for_each(config_head, &config->heads, link) {
964 struct wlr_output *wlr_output = config_head->state.output; 602 if (config_head->state.output == sway_output->wlr_output) {
965 struct sway_output *output = wlr_output->data; 603 cfg->config = output_config_for_config_head(config_head, sway_output);
966 if (!config_head->state.enabled) { 604 break;
967 continue; 605 }
968 } 606 }
969 struct output_config *oc = new_output_config(output->wlr_output->name); 607 if (!cfg->config) {
970 oc->enabled = true; 608 cfg->config = find_output_config(sway_output);
971 if (config_head->state.mode != NULL) {
972 struct wlr_output_mode *mode = config_head->state.mode;
973 oc->width = mode->width;
974 oc->height = mode->height;
975 oc->refresh_rate = mode->refresh / 1000.f;
976 } else {
977 oc->width = config_head->state.custom_mode.width;
978 oc->height = config_head->state.custom_mode.height;
979 oc->refresh_rate =
980 config_head->state.custom_mode.refresh / 1000.f;
981 } 609 }
982 oc->x = config_head->state.x; 610 }
983 oc->y = config_head->state.y;
984 oc->transform = config_head->state.transform;
985 oc->scale = config_head->state.scale;
986 611
987 if (test_only) { 612 sort_output_configs_by_priority(configs, configs_len);
988 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);
989 } else { 631 } else {
990 oc = store_output_config(oc); 632 free_output_config(cfg->config);
991 ok &= apply_output_config(oc, output);
992 } 633 }
993 } 634 }
635 free(configs);
994 636
995 if (ok) { 637 if (ok) {
996 wlr_output_configuration_v1_send_succeeded(config); 638 wlr_output_configuration_v1_send_succeeded(config);
@@ -1028,12 +670,12 @@ void handle_output_power_manager_set_mode(struct wl_listener *listener,
1028 struct output_config *oc = new_output_config(output->wlr_output->name); 670 struct output_config *oc = new_output_config(output->wlr_output->name);
1029 switch (event->mode) { 671 switch (event->mode) {
1030 case ZWLR_OUTPUT_POWER_V1_MODE_OFF: 672 case ZWLR_OUTPUT_POWER_V1_MODE_OFF:
1031 oc->dpms_state = DPMS_OFF; 673 oc->power = 0;
1032 break; 674 break;
1033 case ZWLR_OUTPUT_POWER_V1_MODE_ON: 675 case ZWLR_OUTPUT_POWER_V1_MODE_ON:
1034 oc->dpms_state = DPMS_ON; 676 oc->power = 1;
1035 break; 677 break;
1036 } 678 }
1037 oc = store_output_config(oc); 679 store_output_config(oc);
1038 apply_output_config(oc, output); 680 apply_all_output_configs();
1039} 681}
diff --git a/sway/desktop/render.c b/sway/desktop/render.c
deleted file mode 100644
index ed9ad490..00000000
--- a/sway/desktop/render.c
+++ /dev/null
@@ -1,1204 +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_compositor.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_output->renderer;
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 = wlr_output->renderer;
104 struct sway_output *output = wlr_output->data;
105
106 pixman_region32_t damage;
107 pixman_region32_init(&damage);
108 pixman_region32_union_rect(&damage, &damage, dst_box->x, dst_box->y,
109 dst_box->width, dst_box->height);
110 pixman_region32_intersect(&damage, &damage, output_damage);
111 bool damaged = pixman_region32_not_empty(&damage);
112 if (!damaged) {
113 goto damage_finish;
114 }
115
116 int nrects;
117 pixman_box32_t *rects = pixman_region32_rectangles(&damage, &nrects);
118 for (int i = 0; i < nrects; ++i) {
119 scissor_output(wlr_output, &rects[i]);
120 set_scale_filter(wlr_output, texture, output->scale_filter);
121 if (src_box != NULL) {
122 wlr_render_subtexture_with_matrix(renderer, texture, src_box, matrix, alpha);
123 } else {
124 wlr_render_texture_with_matrix(renderer, texture, matrix, alpha);
125 }
126 }
127
128damage_finish:
129 pixman_region32_fini(&damage);
130}
131
132static void render_surface_iterator(struct sway_output *output,
133 struct sway_view *view, struct wlr_surface *surface,
134 struct wlr_box *_box, void *_data) {
135 struct render_data *data = _data;
136 struct wlr_output *wlr_output = output->wlr_output;
137 pixman_region32_t *output_damage = data->damage;
138 float alpha = data->alpha;
139
140 struct wlr_texture *texture = wlr_surface_get_texture(surface);
141 if (!texture) {
142 return;
143 }
144
145 struct wlr_fbox src_box;
146 wlr_surface_get_buffer_source_box(surface, &src_box);
147
148 struct wlr_box proj_box = *_box;
149 scale_box(&proj_box, wlr_output->scale);
150
151 float matrix[9];
152 enum wl_output_transform transform =
153 wlr_output_transform_invert(surface->current.transform);
154 wlr_matrix_project_box(matrix, &proj_box, transform, 0.0,
155 wlr_output->transform_matrix);
156
157 struct wlr_box dst_box = *_box;
158 struct wlr_box *clip_box = data->clip_box;
159 if (clip_box != NULL) {
160 dst_box.width = fmin(dst_box.width, clip_box->width);
161 dst_box.height = fmin(dst_box.height, clip_box->height);
162 }
163 scale_box(&dst_box, wlr_output->scale);
164
165 render_texture(wlr_output, output_damage, texture,
166 &src_box, &dst_box, matrix, alpha);
167
168 wlr_presentation_surface_sampled_on_output(server.presentation, surface,
169 wlr_output);
170}
171
172static void render_layer_toplevel(struct sway_output *output,
173 pixman_region32_t *damage, struct wl_list *layer_surfaces) {
174 struct render_data data = {
175 .damage = damage,
176 .alpha = 1.0f,
177 };
178 output_layer_for_each_toplevel_surface(output, layer_surfaces,
179 render_surface_iterator, &data);
180}
181
182static void render_layer_popups(struct sway_output *output,
183 pixman_region32_t *damage, struct wl_list *layer_surfaces) {
184 struct render_data data = {
185 .damage = damage,
186 .alpha = 1.0f,
187 };
188 output_layer_for_each_popup_surface(output, layer_surfaces,
189 render_surface_iterator, &data);
190}
191
192#if HAVE_XWAYLAND
193static void render_unmanaged(struct sway_output *output,
194 pixman_region32_t *damage, struct wl_list *unmanaged) {
195 struct render_data data = {
196 .damage = damage,
197 .alpha = 1.0f,
198 };
199 output_unmanaged_for_each_surface(output, unmanaged,
200 render_surface_iterator, &data);
201}
202#endif
203
204static void render_drag_icons(struct sway_output *output,
205 pixman_region32_t *damage, struct wl_list *drag_icons) {
206 struct render_data data = {
207 .damage = damage,
208 .alpha = 1.0f,
209 };
210 output_drag_icons_for_each_surface(output, drag_icons,
211 render_surface_iterator, &data);
212}
213
214// _box.x and .y are expected to be layout-local
215// _box.width and .height are expected to be output-buffer-local
216void render_rect(struct sway_output *output,
217 pixman_region32_t *output_damage, const struct wlr_box *_box,
218 float color[static 4]) {
219 struct wlr_output *wlr_output = output->wlr_output;
220 struct wlr_renderer *renderer = wlr_output->renderer;
221
222 struct wlr_box box;
223 memcpy(&box, _box, sizeof(struct wlr_box));
224 box.x -= output->lx * wlr_output->scale;
225 box.y -= output->ly * wlr_output->scale;
226
227 pixman_region32_t damage;
228 pixman_region32_init(&damage);
229 pixman_region32_union_rect(&damage, &damage, box.x, box.y,
230 box.width, box.height);
231 pixman_region32_intersect(&damage, &damage, output_damage);
232 bool damaged = pixman_region32_not_empty(&damage);
233 if (!damaged) {
234 goto damage_finish;
235 }
236
237 int nrects;
238 pixman_box32_t *rects = pixman_region32_rectangles(&damage, &nrects);
239 for (int i = 0; i < nrects; ++i) {
240 scissor_output(wlr_output, &rects[i]);
241 wlr_render_rect(renderer, &box, color,
242 wlr_output->transform_matrix);
243 }
244
245damage_finish:
246 pixman_region32_fini(&damage);
247}
248
249void premultiply_alpha(float color[4], float opacity) {
250 color[3] *= opacity;
251 color[0] *= color[3];
252 color[1] *= color[3];
253 color[2] *= color[3];
254}
255
256static void render_view_toplevels(struct sway_view *view,
257 struct sway_output *output, pixman_region32_t *damage, float alpha) {
258 struct render_data data = {
259 .damage = damage,
260 .alpha = alpha,
261 };
262 struct wlr_box clip_box;
263 if (!container_is_current_floating(view->container)) {
264 // As we pass the geometry offsets to the surface iterator, we will
265 // need to account for the offsets in the clip dimensions.
266 clip_box.width = view->container->current.content_width + view->geometry.x;
267 clip_box.height = view->container->current.content_height + view->geometry.y;
268 data.clip_box = &clip_box;
269 }
270 // Render all toplevels without descending into popups
271 double ox = view->container->surface_x -
272 output->lx - view->geometry.x;
273 double oy = view->container->surface_y -
274 output->ly - view->geometry.y;
275 output_surface_for_each_surface(output, view->surface, ox, oy,
276 render_surface_iterator, &data);
277}
278
279static void render_view_popups(struct sway_view *view,
280 struct sway_output *output, pixman_region32_t *damage, float alpha) {
281 struct render_data data = {
282 .damage = damage,
283 .alpha = alpha,
284 };
285 output_view_for_each_popup_surface(output, view,
286 render_surface_iterator, &data);
287}
288
289static void render_saved_view(struct sway_view *view,
290 struct sway_output *output, pixman_region32_t *damage, float alpha) {
291 struct wlr_output *wlr_output = output->wlr_output;
292
293 if (wl_list_empty(&view->saved_buffers)) {
294 return;
295 }
296
297 bool floating = container_is_current_floating(view->container);
298
299 struct sway_saved_buffer *saved_buf;
300 wl_list_for_each(saved_buf, &view->saved_buffers, link) {
301 if (!saved_buf->buffer->texture) {
302 continue;
303 }
304
305 struct wlr_box proj_box = {
306 .x = saved_buf->x - view->saved_geometry.x - output->lx,
307 .y = saved_buf->y - view->saved_geometry.y - output->ly,
308 .width = saved_buf->width,
309 .height = saved_buf->height,
310 };
311
312 struct wlr_box output_box = {
313 .width = output->width,
314 .height = output->height,
315 };
316
317 struct wlr_box intersection;
318 bool intersects = wlr_box_intersection(&intersection, &output_box, &proj_box);
319 if (!intersects) {
320 continue;
321 }
322
323 struct wlr_box dst_box = proj_box;
324 scale_box(&proj_box, wlr_output->scale);
325
326 float matrix[9];
327 enum wl_output_transform transform = wlr_output_transform_invert(saved_buf->transform);
328 wlr_matrix_project_box(matrix, &proj_box, transform, 0,
329 wlr_output->transform_matrix);
330
331 if (!floating) {
332 dst_box.width = fmin(dst_box.width,
333 view->container->current.content_width -
334 (saved_buf->x - view->container->current.content_x) + view->saved_geometry.x);
335 dst_box.height = fmin(dst_box.height,
336 view->container->current.content_height -
337 (saved_buf->y - view->container->current.content_y) + view->saved_geometry.y);
338 }
339 scale_box(&dst_box, wlr_output->scale);
340
341 render_texture(wlr_output, damage, saved_buf->buffer->texture,
342 &saved_buf->source_box, &dst_box, matrix, alpha);
343 }
344
345 // FIXME: we should set the surface that this saved buffer originates from
346 // as sampled here.
347 // https://github.com/swaywm/sway/pull/4465#discussion_r321082059
348}
349
350/**
351 * Render a view's surface and left/bottom/right borders.
352 */
353static void render_view(struct sway_output *output, pixman_region32_t *damage,
354 struct sway_container *con, struct border_colors *colors) {
355 struct sway_view *view = con->view;
356 if (!wl_list_empty(&view->saved_buffers)) {
357 render_saved_view(view, output, damage, view->container->alpha);
358 } else if (view->surface) {
359 render_view_toplevels(view, output, damage, view->container->alpha);
360 }
361
362 if (con->current.border == B_NONE || con->current.border == B_CSD) {
363 return;
364 }
365
366 struct wlr_box box;
367 float output_scale = output->wlr_output->scale;
368 float color[4];
369 struct sway_container_state *state = &con->current;
370
371 if (state->border_left) {
372 memcpy(&color, colors->child_border, sizeof(float) * 4);
373 premultiply_alpha(color, con->alpha);
374 box.x = floor(state->x);
375 box.y = floor(state->content_y);
376 box.width = state->border_thickness;
377 box.height = state->content_height;
378 scale_box(&box, output_scale);
379 render_rect(output, damage, &box, color);
380 }
381
382 list_t *siblings = container_get_current_siblings(con);
383 enum sway_container_layout layout =
384 container_current_parent_layout(con);
385
386 if (state->border_right) {
387 if (!container_is_current_floating(con) && siblings->length == 1 && layout == L_HORIZ) {
388 memcpy(&color, colors->indicator, sizeof(float) * 4);
389 } else {
390 memcpy(&color, colors->child_border, sizeof(float) * 4);
391 }
392 premultiply_alpha(color, con->alpha);
393 box.x = floor(state->content_x + state->content_width);
394 box.y = floor(state->content_y);
395 box.width = state->border_thickness;
396 box.height = state->content_height;
397 scale_box(&box, output_scale);
398 render_rect(output, damage, &box, color);
399 }
400
401 if (state->border_bottom) {
402 if (!container_is_current_floating(con) && siblings->length == 1 && layout == L_VERT) {
403 memcpy(&color, colors->indicator, sizeof(float) * 4);
404 } else {
405 memcpy(&color, colors->child_border, sizeof(float) * 4);
406 }
407 premultiply_alpha(color, con->alpha);
408 box.x = floor(state->x);
409 box.y = floor(state->content_y + state->content_height);
410 box.width = state->width;
411 box.height = state->border_thickness;
412 scale_box(&box, output_scale);
413 render_rect(output, damage, &box, color);
414 }
415}
416
417/**
418 * Render a titlebar.
419 *
420 * Care must be taken not to render over the same pixel multiple times,
421 * otherwise the colors will be incorrect when using opacity.
422 *
423 * The height is: 1px border, 3px padding, font height, 3px padding, 1px border
424 * The left side is: 1px border, 2px padding, title
425 */
426static void render_titlebar(struct sway_output *output,
427 pixman_region32_t *output_damage, struct sway_container *con,
428 int x, int y, int width,
429 struct border_colors *colors, struct wlr_texture *title_texture,
430 struct wlr_texture *marks_texture) {
431 struct wlr_box box;
432 float color[4];
433 float output_scale = output->wlr_output->scale;
434 double output_x = output->lx;
435 double output_y = output->ly;
436 int titlebar_border_thickness = config->titlebar_border_thickness;
437 int titlebar_h_padding = config->titlebar_h_padding;
438 int titlebar_v_padding = config->titlebar_v_padding;
439 enum alignment title_align = config->title_align;
440
441 // Single pixel bar above title
442 memcpy(&color, colors->border, sizeof(float) * 4);
443 premultiply_alpha(color, con->alpha);
444 box.x = x;
445 box.y = y;
446 box.width = width;
447 box.height = titlebar_border_thickness;
448 scale_box(&box, output_scale);
449 render_rect(output, output_damage, &box, color);
450
451 // Single pixel bar below title
452 box.x = x;
453 box.y = y + container_titlebar_height() - titlebar_border_thickness;
454 box.width = width;
455 box.height = titlebar_border_thickness;
456 scale_box(&box, output_scale);
457 render_rect(output, output_damage, &box, color);
458
459 // Single pixel left edge
460 box.x = x;
461 box.y = y + titlebar_border_thickness;
462 box.width = titlebar_border_thickness;
463 box.height = container_titlebar_height() - titlebar_border_thickness * 2;
464 scale_box(&box, output_scale);
465 render_rect(output, output_damage, &box, color);
466
467 // Single pixel right edge
468 box.x = x + width - titlebar_border_thickness;
469 box.y = y + titlebar_border_thickness;
470 box.width = titlebar_border_thickness;
471 box.height = container_titlebar_height() - titlebar_border_thickness * 2;
472 scale_box(&box, output_scale);
473 render_rect(output, output_damage, &box, color);
474
475 int inner_x = x - output_x + titlebar_h_padding;
476 int bg_y = y + titlebar_border_thickness;
477 size_t inner_width = width - titlebar_h_padding * 2;
478
479 // output-buffer local
480 int ob_inner_x = round(inner_x * output_scale);
481 int ob_inner_width = scale_length(inner_width, inner_x, output_scale);
482 int ob_bg_height = scale_length(
483 (titlebar_v_padding - titlebar_border_thickness) * 2 +
484 config->font_height, bg_y, output_scale);
485
486 // Marks
487 int ob_marks_x = 0; // output-buffer-local
488 int ob_marks_width = 0; // output-buffer-local
489 if (config->show_marks && marks_texture) {
490 struct wlr_box texture_box = {
491 .width = marks_texture->width,
492 .height = marks_texture->height,
493 };
494 ob_marks_width = texture_box.width;
495
496 // The marks texture might be shorter than the config->font_height, in
497 // which case we need to pad it as evenly as possible above and below.
498 int ob_padding_total = ob_bg_height - texture_box.height;
499 int ob_padding_above = floor(ob_padding_total / 2.0);
500 int ob_padding_below = ceil(ob_padding_total / 2.0);
501
502 // Render texture. If the title is on the right, the marks will be on
503 // the left. Otherwise, they will be on the right.
504 if (title_align == ALIGN_RIGHT || texture_box.width > ob_inner_width) {
505 texture_box.x = ob_inner_x;
506 } else {
507 texture_box.x = ob_inner_x + ob_inner_width - texture_box.width;
508 }
509 ob_marks_x = texture_box.x;
510
511 texture_box.y = round((bg_y - output_y) * output_scale) +
512 ob_padding_above;
513
514 float matrix[9];
515 wlr_matrix_project_box(matrix, &texture_box,
516 WL_OUTPUT_TRANSFORM_NORMAL,
517 0.0, output->wlr_output->transform_matrix);
518
519 if (ob_inner_width < texture_box.width) {
520 texture_box.width = ob_inner_width;
521 }
522 render_texture(output->wlr_output, output_damage, marks_texture,
523 NULL, &texture_box, matrix, con->alpha);
524
525 // Padding above
526 memcpy(&color, colors->background, sizeof(float) * 4);
527 premultiply_alpha(color, con->alpha);
528 box.x = texture_box.x + round(output_x * output_scale);
529 box.y = round((y + titlebar_border_thickness) * output_scale);
530 box.width = texture_box.width;
531 box.height = ob_padding_above;
532 render_rect(output, output_damage, &box, color);
533
534 // Padding below
535 box.y += ob_padding_above + texture_box.height;
536 box.height = ob_padding_below;
537 render_rect(output, output_damage, &box, color);
538 }
539
540 // Title text
541 int ob_title_x = 0; // output-buffer-local
542 int ob_title_width = 0; // output-buffer-local
543 if (title_texture) {
544 struct wlr_box texture_box = {
545 .width = title_texture->width,
546 .height = title_texture->height,
547 };
548
549 // The effective output may be NULL when con is not on any output.
550 // This can happen because we render all children of containers,
551 // even those that are out of the bounds of any output.
552 struct sway_output *effective = container_get_effective_output(con);
553 float title_scale = effective ? effective->wlr_output->scale : output_scale;
554 texture_box.width = texture_box.width * output_scale / title_scale;
555 texture_box.height = texture_box.height * output_scale / title_scale;
556 ob_title_width = texture_box.width;
557
558 // The title texture might be shorter than the config->font_height,
559 // in which case we need to pad it above and below.
560 int ob_padding_above = round((titlebar_v_padding -
561 titlebar_border_thickness) * output_scale);
562 int ob_padding_below = ob_bg_height - ob_padding_above -
563 texture_box.height;
564
565 // Render texture
566 if (texture_box.width > ob_inner_width - ob_marks_width) {
567 texture_box.x = (title_align == ALIGN_RIGHT && ob_marks_width)
568 ? ob_marks_x + ob_marks_width : ob_inner_x;
569 } else if (title_align == ALIGN_LEFT) {
570 texture_box.x = ob_inner_x;
571 } else if (title_align == ALIGN_CENTER) {
572 // If there are marks visible, center between the edge and marks.
573 // Otherwise, center in the inner area.
574 if (ob_marks_width) {
575 texture_box.x = (ob_inner_x + ob_marks_x) / 2
576 - texture_box.width / 2;
577 } else {
578 texture_box.x = ob_inner_x + ob_inner_width / 2
579 - texture_box.width / 2;
580 }
581 } else {
582 texture_box.x = ob_inner_x + ob_inner_width - texture_box.width;
583 }
584 ob_title_x = texture_box.x;
585
586 texture_box.y =
587 round((bg_y - output_y) * output_scale) + ob_padding_above;
588
589 float matrix[9];
590 wlr_matrix_project_box(matrix, &texture_box,
591 WL_OUTPUT_TRANSFORM_NORMAL,
592 0.0, output->wlr_output->transform_matrix);
593
594 if (ob_inner_width - ob_marks_width < texture_box.width) {
595 texture_box.width = ob_inner_width - ob_marks_width;
596 }
597
598 render_texture(output->wlr_output, output_damage, title_texture,
599 NULL, &texture_box, matrix, con->alpha);
600
601 // Padding above
602 memcpy(&color, colors->background, sizeof(float) * 4);
603 premultiply_alpha(color, con->alpha);
604 box.x = texture_box.x + round(output_x * output_scale);
605 box.y = round((y + titlebar_border_thickness) * output_scale);
606 box.width = texture_box.width;
607 box.height = ob_padding_above;
608 render_rect(output, output_damage, &box, color);
609
610 // Padding below
611 box.y += ob_padding_above + texture_box.height;
612 box.height = ob_padding_below;
613 render_rect(output, output_damage, &box, color);
614 }
615
616 // Determine the left + right extends of the textures (output-buffer local)
617 int ob_left_x, ob_left_width, ob_right_x, ob_right_width;
618 if (ob_title_width == 0 && ob_marks_width == 0) {
619 ob_left_x = ob_inner_x;
620 ob_left_width = 0;
621 ob_right_x = ob_inner_x;
622 ob_right_width = 0;
623 } else if (ob_title_x < ob_marks_x) {
624 ob_left_x = ob_title_x;
625 ob_left_width = ob_title_width;
626 ob_right_x = ob_marks_x;
627 ob_right_width = ob_marks_width;
628 } else {
629 ob_left_x = ob_marks_x;
630 ob_left_width = ob_marks_width;
631 ob_right_x = ob_title_x;
632 ob_right_width = ob_title_width;
633 }
634 if (ob_left_x < ob_inner_x) {
635 ob_left_x = ob_inner_x;
636 } else if (ob_left_x + ob_left_width > ob_right_x + ob_right_width) {
637 ob_right_x = ob_left_x;
638 ob_right_width = ob_left_width;
639 }
640
641 // Filler between title and marks
642 box.width = ob_right_x - ob_left_x - ob_left_width;
643 if (box.width > 0) {
644 box.x = ob_left_x + ob_left_width + round(output_x * output_scale);
645 box.y = round(bg_y * output_scale);
646 box.height = ob_bg_height;
647 render_rect(output, output_damage, &box, color);
648 }
649
650 // Padding on left side
651 box.x = x + titlebar_border_thickness;
652 box.y = y + titlebar_border_thickness;
653 box.width = titlebar_h_padding - titlebar_border_thickness;
654 box.height = (titlebar_v_padding - titlebar_border_thickness) * 2 +
655 config->font_height;
656 scale_box(&box, output_scale);
657 int left_x = ob_left_x + round(output_x * output_scale);
658 if (box.x + box.width < left_x) {
659 box.width += left_x - box.x - box.width;
660 }
661 render_rect(output, output_damage, &box, color);
662
663 // Padding on right side
664 box.x = x + width - titlebar_h_padding;
665 box.y = y + titlebar_border_thickness;
666 box.width = titlebar_h_padding - titlebar_border_thickness;
667 box.height = (titlebar_v_padding - titlebar_border_thickness) * 2 +
668 config->font_height;
669 scale_box(&box, output_scale);
670 int right_rx = ob_right_x + ob_right_width + round(output_x * output_scale);
671 if (right_rx < box.x) {
672 box.width += box.x - right_rx;
673 box.x = right_rx;
674 }
675 render_rect(output, output_damage, &box, color);
676}
677
678/**
679 * Render the top border line for a view using "border pixel".
680 */
681static void render_top_border(struct sway_output *output,
682 pixman_region32_t *output_damage, struct sway_container *con,
683 struct border_colors *colors) {
684 struct sway_container_state *state = &con->current;
685 if (!state->border_top) {
686 return;
687 }
688 struct wlr_box box;
689 float color[4];
690 float output_scale = output->wlr_output->scale;
691
692 // Child border - top edge
693 memcpy(&color, colors->child_border, sizeof(float) * 4);
694 premultiply_alpha(color, con->alpha);
695 box.x = floor(state->x);
696 box.y = floor(state->y);
697 box.width = state->width;
698 box.height = state->border_thickness;
699 scale_box(&box, output_scale);
700 render_rect(output, output_damage, &box, color);
701}
702
703struct parent_data {
704 enum sway_container_layout layout;
705 struct wlr_box box;
706 list_t *children;
707 bool focused;
708 struct sway_container *active_child;
709};
710
711static void render_container(struct sway_output *output,
712 pixman_region32_t *damage, struct sway_container *con, bool parent_focused);
713
714/**
715 * Render a container's children using a L_HORIZ or L_VERT layout.
716 *
717 * Wrap child views in borders and leave child containers borderless because
718 * they'll apply their own borders to their children.
719 */
720static void render_containers_linear(struct sway_output *output,
721 pixman_region32_t *damage, struct parent_data *parent) {
722 for (int i = 0; i < parent->children->length; ++i) {
723 struct sway_container *child = parent->children->items[i];
724
725 if (child->view) {
726 struct sway_view *view = child->view;
727 struct border_colors *colors;
728 struct wlr_texture *title_texture;
729 struct wlr_texture *marks_texture;
730 struct sway_container_state *state = &child->current;
731
732 if (view_is_urgent(view)) {
733 colors = &config->border_colors.urgent;
734 title_texture = child->title_urgent;
735 marks_texture = child->marks_urgent;
736 } else if (state->focused || parent->focused) {
737 colors = &config->border_colors.focused;
738 title_texture = child->title_focused;
739 marks_texture = child->marks_focused;
740 } else if (child == parent->active_child) {
741 colors = &config->border_colors.focused_inactive;
742 title_texture = child->title_focused_inactive;
743 marks_texture = child->marks_focused_inactive;
744 } else {
745 colors = &config->border_colors.unfocused;
746 title_texture = child->title_unfocused;
747 marks_texture = child->marks_unfocused;
748 }
749
750 if (state->border == B_NORMAL) {
751 render_titlebar(output, damage, child, floor(state->x),
752 floor(state->y), state->width, colors,
753 title_texture, marks_texture);
754 } else if (state->border == B_PIXEL) {
755 render_top_border(output, damage, child, colors);
756 }
757 render_view(output, damage, child, colors);
758 } else {
759 render_container(output, damage, child,
760 parent->focused || child->current.focused);
761 }
762 }
763}
764
765static bool container_is_focused(struct sway_container *con, void *data) {
766 return con->current.focused;
767}
768
769static bool container_has_focused_child(struct sway_container *con) {
770 return container_find_child(con, container_is_focused, NULL);
771}
772
773/**
774 * Render a container's children using the L_TABBED layout.
775 */
776static void render_containers_tabbed(struct sway_output *output,
777 pixman_region32_t *damage, struct parent_data *parent) {
778 if (!parent->children->length) {
779 return;
780 }
781 struct sway_container *current = parent->active_child;
782 struct border_colors *current_colors = &config->border_colors.unfocused;
783 int tab_width = parent->box.width / parent->children->length;
784
785 // Render tabs
786 for (int i = 0; i < parent->children->length; ++i) {
787 struct sway_container *child = parent->children->items[i];
788 struct sway_view *view = child->view;
789 struct sway_container_state *cstate = &child->current;
790 struct border_colors *colors;
791 struct wlr_texture *title_texture;
792 struct wlr_texture *marks_texture;
793 bool urgent = view ?
794 view_is_urgent(view) : container_has_urgent_child(child);
795
796 if (urgent) {
797 colors = &config->border_colors.urgent;
798 title_texture = child->title_urgent;
799 marks_texture = child->marks_urgent;
800 } else if (cstate->focused || parent->focused) {
801 colors = &config->border_colors.focused;
802 title_texture = child->title_focused;
803 marks_texture = child->marks_focused;
804 } else if (config->has_focused_tab_title && container_has_focused_child(child)) {
805 colors = &config->border_colors.focused_tab_title;
806 title_texture = child->title_focused_tab_title;
807 marks_texture = child->marks_focused_tab_title;
808 } else if (child == parent->active_child) {
809 colors = &config->border_colors.focused_inactive;
810 title_texture = child->title_focused_inactive;
811 marks_texture = child->marks_focused_inactive;
812 } else {
813 colors = &config->border_colors.unfocused;
814 title_texture = child->title_unfocused;
815 marks_texture = child->marks_unfocused;
816 }
817
818 int x = floor(cstate->x + tab_width * i);
819
820 // Make last tab use the remaining width of the parent
821 if (i == parent->children->length - 1) {
822 tab_width = parent->box.width - tab_width * i;
823 }
824
825 render_titlebar(output, damage, child, x, parent->box.y, tab_width,
826 colors, title_texture, marks_texture);
827
828 if (child == current) {
829 current_colors = colors;
830 }
831 }
832
833 // Render surface and left/right/bottom borders
834 if (current->view) {
835 render_view(output, damage, current, current_colors);
836 } else {
837 render_container(output, damage, current,
838 parent->focused || current->current.focused);
839 }
840}
841
842/**
843 * Render a container's children using the L_STACKED layout.
844 */
845static void render_containers_stacked(struct sway_output *output,
846 pixman_region32_t *damage, struct parent_data *parent) {
847 if (!parent->children->length) {
848 return;
849 }
850 struct sway_container *current = parent->active_child;
851 struct border_colors *current_colors = &config->border_colors.unfocused;
852 size_t titlebar_height = container_titlebar_height();
853
854 // Render titles
855 for (int i = 0; i < parent->children->length; ++i) {
856 struct sway_container *child = parent->children->items[i];
857 struct sway_view *view = child->view;
858 struct sway_container_state *cstate = &child->current;
859 struct border_colors *colors;
860 struct wlr_texture *title_texture;
861 struct wlr_texture *marks_texture;
862 bool urgent = view ?
863 view_is_urgent(view) : container_has_urgent_child(child);
864
865 if (urgent) {
866 colors = &config->border_colors.urgent;
867 title_texture = child->title_urgent;
868 marks_texture = child->marks_urgent;
869 } else if (cstate->focused || parent->focused) {
870 colors = &config->border_colors.focused;
871 title_texture = child->title_focused;
872 marks_texture = child->marks_focused;
873 } else if (config->has_focused_tab_title && container_has_focused_child(child)) {
874 colors = &config->border_colors.focused_tab_title;
875 title_texture = child->title_focused_tab_title;
876 marks_texture = child->marks_focused_tab_title;
877 } else if (child == parent->active_child) {
878 colors = &config->border_colors.focused_inactive;
879 title_texture = child->title_focused_inactive;
880 marks_texture = child->marks_focused_inactive;
881 } else {
882 colors = &config->border_colors.unfocused;
883 title_texture = child->title_unfocused;
884 marks_texture = child->marks_unfocused;
885 }
886
887 int y = parent->box.y + titlebar_height * i;
888 render_titlebar(output, damage, child, parent->box.x, y,
889 parent->box.width, colors, title_texture, marks_texture);
890
891 if (child == current) {
892 current_colors = colors;
893 }
894 }
895
896 // Render surface and left/right/bottom borders
897 if (current->view) {
898 render_view(output, damage, current, current_colors);
899 } else {
900 render_container(output, damage, current,
901 parent->focused || current->current.focused);
902 }
903}
904
905static void render_containers(struct sway_output *output,
906 pixman_region32_t *damage, struct parent_data *parent) {
907 if (config->hide_lone_tab && parent->children->length == 1) {
908 struct sway_container *child = parent->children->items[0];
909 if (child->view) {
910 render_containers_linear(output,damage, parent);
911 return;
912 }
913 }
914
915 switch (parent->layout) {
916 case L_NONE:
917 case L_HORIZ:
918 case L_VERT:
919 render_containers_linear(output, damage, parent);
920 break;
921 case L_STACKED:
922 render_containers_stacked(output, damage, parent);
923 break;
924 case L_TABBED:
925 render_containers_tabbed(output, damage, parent);
926 break;
927 }
928}
929
930static void render_container(struct sway_output *output,
931 pixman_region32_t *damage, struct sway_container *con, bool focused) {
932 struct parent_data data = {
933 .layout = con->current.layout,
934 .box = {
935 .x = floor(con->current.x),
936 .y = floor(con->current.y),
937 .width = con->current.width,
938 .height = con->current.height,
939 },
940 .children = con->current.children,
941 .focused = focused,
942 .active_child = con->current.focused_inactive_child,
943 };
944 render_containers(output, damage, &data);
945}
946
947static void render_workspace(struct sway_output *output,
948 pixman_region32_t *damage, struct sway_workspace *ws, bool focused) {
949 struct parent_data data = {
950 .layout = ws->current.layout,
951 .box = {
952 .x = floor(ws->current.x),
953 .y = floor(ws->current.y),
954 .width = ws->current.width,
955 .height = ws->current.height,
956 },
957 .children = ws->current.tiling,
958 .focused = focused,
959 .active_child = ws->current.focused_inactive_child,
960 };
961 render_containers(output, damage, &data);
962}
963
964static void render_floating_container(struct sway_output *soutput,
965 pixman_region32_t *damage, struct sway_container *con) {
966 if (con->view) {
967 struct sway_view *view = con->view;
968 struct border_colors *colors;
969 struct wlr_texture *title_texture;
970 struct wlr_texture *marks_texture;
971
972 if (view_is_urgent(view)) {
973 colors = &config->border_colors.urgent;
974 title_texture = con->title_urgent;
975 marks_texture = con->marks_urgent;
976 } else if (con->current.focused) {
977 colors = &config->border_colors.focused;
978 title_texture = con->title_focused;
979 marks_texture = con->marks_focused;
980 } else {
981 colors = &config->border_colors.unfocused;
982 title_texture = con->title_unfocused;
983 marks_texture = con->marks_unfocused;
984 }
985
986 if (con->current.border == B_NORMAL) {
987 render_titlebar(soutput, damage, con, floor(con->current.x),
988 floor(con->current.y), con->current.width, colors,
989 title_texture, marks_texture);
990 } else if (con->current.border == B_PIXEL) {
991 render_top_border(soutput, damage, con, colors);
992 }
993 render_view(soutput, damage, con, colors);
994 } else {
995 render_container(soutput, damage, con, con->current.focused);
996 }
997}
998
999static void render_floating(struct sway_output *soutput,
1000 pixman_region32_t *damage) {
1001 for (int i = 0; i < root->outputs->length; ++i) {
1002 struct sway_output *output = root->outputs->items[i];
1003 for (int j = 0; j < output->current.workspaces->length; ++j) {
1004 struct sway_workspace *ws = output->current.workspaces->items[j];
1005 if (!workspace_is_visible(ws)) {
1006 continue;
1007 }
1008 for (int k = 0; k < ws->current.floating->length; ++k) {
1009 struct sway_container *floater = ws->current.floating->items[k];
1010 if (floater->current.fullscreen_mode != FULLSCREEN_NONE) {
1011 continue;
1012 }
1013 render_floating_container(soutput, damage, floater);
1014 }
1015 }
1016 }
1017}
1018
1019static void render_seatops(struct sway_output *output,
1020 pixman_region32_t *damage) {
1021 struct sway_seat *seat;
1022 wl_list_for_each(seat, &server.input->seats, link) {
1023 seatop_render(seat, output, damage);
1024 }
1025}
1026
1027void output_render(struct sway_output *output, struct timespec *when,
1028 pixman_region32_t *damage) {
1029 struct wlr_output *wlr_output = output->wlr_output;
1030 struct wlr_renderer *renderer = output->server->renderer;
1031
1032 struct sway_workspace *workspace = output->current.active_workspace;
1033 if (workspace == NULL) {
1034 return;
1035 }
1036
1037 struct sway_container *fullscreen_con = root->fullscreen_global;
1038 if (!fullscreen_con) {
1039 fullscreen_con = workspace->current.fullscreen;
1040 }
1041
1042 wlr_renderer_begin(renderer, wlr_output->width, wlr_output->height);
1043
1044 if (debug.damage == DAMAGE_RERENDER) {
1045 int width, height;
1046 wlr_output_transformed_resolution(wlr_output, &width, &height);
1047 pixman_region32_union_rect(damage, damage, 0, 0, width, height);
1048 }
1049
1050 if (!pixman_region32_not_empty(damage)) {
1051 // Output isn't damaged but needs buffer swap
1052 goto renderer_end;
1053 }
1054
1055 if (debug.damage == DAMAGE_HIGHLIGHT) {
1056 wlr_renderer_clear(renderer, (float[]){1, 1, 0, 1});
1057 }
1058
1059 if (server.session_lock.locked) {
1060 float clear_color[] = {0.0f, 0.0f, 0.0f, 1.0f};
1061 if (server.session_lock.lock == NULL) {
1062 // abandoned lock -> red BG
1063 clear_color[0] = 1.f;
1064 }
1065 int nrects;
1066 pixman_box32_t *rects = pixman_region32_rectangles(damage, &nrects);
1067 for (int i = 0; i < nrects; ++i) {
1068 scissor_output(wlr_output, &rects[i]);
1069 wlr_renderer_clear(renderer, clear_color);
1070 }
1071
1072 if (server.session_lock.lock != NULL) {
1073 struct render_data data = {
1074 .damage = damage,
1075 .alpha = 1.0f,
1076 };
1077
1078 struct wlr_session_lock_surface_v1 *lock_surface;
1079 wl_list_for_each(lock_surface, &server.session_lock.lock->surfaces, link) {
1080 if (lock_surface->output != wlr_output) {
1081 continue;
1082 }
1083 if (!lock_surface->mapped) {
1084 continue;
1085 }
1086
1087 output_surface_for_each_surface(output, lock_surface->surface,
1088 0.0, 0.0, render_surface_iterator, &data);
1089 }
1090 }
1091 goto renderer_end;
1092 }
1093
1094 if (output_has_opaque_overlay_layer_surface(output)) {
1095 goto render_overlay;
1096 }
1097
1098 if (fullscreen_con) {
1099 float clear_color[] = {0.0f, 0.0f, 0.0f, 1.0f};
1100
1101 int nrects;
1102 pixman_box32_t *rects = pixman_region32_rectangles(damage, &nrects);
1103 for (int i = 0; i < nrects; ++i) {
1104 scissor_output(wlr_output, &rects[i]);
1105 wlr_renderer_clear(renderer, clear_color);
1106 }
1107
1108 if (fullscreen_con->view) {
1109 if (!wl_list_empty(&fullscreen_con->view->saved_buffers)) {
1110 render_saved_view(fullscreen_con->view, output, damage, 1.0f);
1111 } else if (fullscreen_con->view->surface) {
1112 render_view_toplevels(fullscreen_con->view,
1113 output, damage, 1.0f);
1114 }
1115 } else {
1116 render_container(output, damage, fullscreen_con,
1117 fullscreen_con->current.focused);
1118 }
1119
1120 for (int i = 0; i < workspace->current.floating->length; ++i) {
1121 struct sway_container *floater =
1122 workspace->current.floating->items[i];
1123 if (container_is_transient_for(floater, fullscreen_con)) {
1124 render_floating_container(output, damage, floater);
1125 }
1126 }
1127#if HAVE_XWAYLAND
1128 render_unmanaged(output, damage, &root->xwayland_unmanaged);
1129#endif
1130 } else {
1131 float clear_color[] = {0.25f, 0.25f, 0.25f, 1.0f};
1132
1133 int nrects;
1134 pixman_box32_t *rects = pixman_region32_rectangles(damage, &nrects);
1135 for (int i = 0; i < nrects; ++i) {
1136 scissor_output(wlr_output, &rects[i]);
1137 wlr_renderer_clear(renderer, clear_color);
1138 }
1139
1140 render_layer_toplevel(output, damage,
1141 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND]);
1142 render_layer_toplevel(output, damage,
1143 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM]);
1144
1145 render_workspace(output, damage, workspace, workspace->current.focused);
1146 render_floating(output, damage);
1147#if HAVE_XWAYLAND
1148 render_unmanaged(output, damage, &root->xwayland_unmanaged);
1149#endif
1150 render_layer_toplevel(output, damage,
1151 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]);
1152
1153 render_layer_popups(output, damage,
1154 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND]);
1155 render_layer_popups(output, damage,
1156 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM]);
1157 render_layer_popups(output, damage,
1158 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]);
1159 }
1160
1161 render_seatops(output, damage);
1162
1163 struct sway_seat *seat = input_manager_current_seat();
1164 struct sway_container *focus = seat_get_focused_container(seat);
1165 if (focus && focus->view) {
1166 render_view_popups(focus->view, output, damage, focus->alpha);
1167 }
1168
1169render_overlay:
1170 render_layer_toplevel(output, damage,
1171 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY]);
1172 render_layer_popups(output, damage,
1173 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY]);
1174 render_drag_icons(output, damage, &root->drag_icons);
1175
1176renderer_end:
1177 wlr_renderer_scissor(renderer, NULL);
1178 wlr_output_render_software_cursors(wlr_output, damage);
1179 wlr_renderer_end(renderer);
1180
1181 int width, height;
1182 wlr_output_transformed_resolution(wlr_output, &width, &height);
1183
1184 pixman_region32_t frame_damage;
1185 pixman_region32_init(&frame_damage);
1186
1187 enum wl_output_transform transform =
1188 wlr_output_transform_invert(wlr_output->transform);
1189 wlr_region_transform(&frame_damage, &output->damage->current,
1190 transform, width, height);
1191
1192 if (debug.damage != DAMAGE_DEFAULT) {
1193 pixman_region32_union_rect(&frame_damage, &frame_damage,
1194 0, 0, wlr_output->width, wlr_output->height);
1195 }
1196
1197 wlr_output_set_damage(wlr_output, &frame_damage);
1198 pixman_region32_fini(&frame_damage);
1199
1200 if (!wlr_output_commit(wlr_output)) {
1201 return;
1202 }
1203 output->last_frame = *when;
1204}
diff --git a/sway/desktop/surface.c b/sway/desktop/surface.c
deleted file mode 100644
index 1d7b536d..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_compositor.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 f5a3a053..d1898843 100644
--- a/sway/desktop/transaction.c
+++ b/sway/desktop/transaction.c
@@ -1,16 +1,16 @@
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"
12#include "sway/input/input-manager.h" 11#include "sway/input/input-manager.h"
13#include "sway/output.h" 12#include "sway/output.h"
13#include "sway/server.h"
14#include "sway/tree/container.h" 14#include "sway/tree/container.h"
15#include "sway/tree/node.h" 15#include "sway/tree/node.h"
16#include "sway/tree/view.h" 16#include "sway/tree/view.h"
@@ -214,39 +214,20 @@ static void transaction_add_node(struct sway_transaction *transaction,
214 214
215static void apply_output_state(struct sway_output *output, 215static void apply_output_state(struct sway_output *output,
216 struct sway_output_state *state) { 216 struct sway_output_state *state) {
217 output_damage_whole(output);
218 list_free(output->current.workspaces); 217 list_free(output->current.workspaces);
219 memcpy(&output->current, state, sizeof(struct sway_output_state)); 218 memcpy(&output->current, state, sizeof(struct sway_output_state));
220 output_damage_whole(output);
221} 219}
222 220
223static void apply_workspace_state(struct sway_workspace *ws, 221static void apply_workspace_state(struct sway_workspace *ws,
224 struct sway_workspace_state *state) { 222 struct sway_workspace_state *state) {
225 output_damage_whole(ws->current.output);
226 list_free(ws->current.floating); 223 list_free(ws->current.floating);
227 list_free(ws->current.tiling); 224 list_free(ws->current.tiling);
228 memcpy(&ws->current, state, sizeof(struct sway_workspace_state)); 225 memcpy(&ws->current, state, sizeof(struct sway_workspace_state));
229 output_damage_whole(ws->current.output);
230} 226}
231 227
232static void apply_container_state(struct sway_container *container, 228static void apply_container_state(struct sway_container *container,
233 struct sway_container_state *state) { 229 struct sway_container_state *state) {
234 struct sway_view *view = container->view; 230 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 231 // There are separate children lists for each instruction state, the
251 // container's current state and the container's pending state 232 // container's current state and the container's pending state
252 // (ie. con->children). The list itself needs to be freed here. 233 // (ie. con->children). The list itself needs to be freed here.
@@ -256,35 +237,443 @@ static void apply_container_state(struct sway_container *container,
256 237
257 memcpy(&container->current, state, sizeof(struct sway_container_state)); 238 memcpy(&container->current, state, sizeof(struct sway_container_state));
258 239
259 if (view && !wl_list_empty(&view->saved_buffers)) { 240 if (view) {
260 if (!container->node.destroying || container->node.ntxnrefs == 1) { 241 if (view->saved_surface_tree) {
261 view_remove_saved_buffer(view); 242 if (!container->node.destroying || container->node.ntxnrefs == 1) {
243 view_remove_saved_buffer(view);
244 }
245 }
246
247 // If the view hasn't responded to the configure, center it within
248 // the container. This is important for fullscreen views which
249 // refuse to resize to the size of the output.
250 if (view->surface) {
251 view_center_and_clip_surface(view);
252 }
253 }
254}
255
256static void arrange_title_bar(struct sway_container *con,
257 int x, int y, int width, int height) {
258 container_update(con);
259
260 bool has_title_bar = height > 0;
261 wlr_scene_node_set_enabled(&con->title_bar.tree->node, has_title_bar);
262 if (!has_title_bar) {
263 return;
264 }
265
266 wlr_scene_node_set_position(&con->title_bar.tree->node, x, y);
267
268 con->title_width = width;
269 container_arrange_title_bar(con);
270}
271
272static void disable_container(struct sway_container *con) {
273 if (con->view) {
274 wlr_scene_node_reparent(&con->view->scene_tree->node, con->content_tree);
275 } else {
276 for (int i = 0; i < con->current.children->length; i++) {
277 struct sway_container *child = con->current.children->items[i];
278
279 wlr_scene_node_reparent(&child->scene_tree->node, con->content_tree);
280
281 disable_container(child);
282 }
283 }
284}
285
286static void arrange_container(struct sway_container *con,
287 int width, int height, bool title_bar, int gaps);
288
289static void arrange_children(enum sway_container_layout layout, list_t *children,
290 struct sway_container *active, struct wlr_scene_tree *content,
291 int width, int height, int gaps) {
292 int title_bar_height = container_titlebar_height();
293
294 if (layout == L_TABBED) {
295 struct sway_container *first = children->length == 1 ?
296 ((struct sway_container *)children->items[0]) : NULL;
297 if (config->hide_lone_tab && first && first->view &&
298 first->current.border != B_NORMAL) {
299 title_bar_height = 0;
300 }
301
302 double w = (double) width / children->length;
303 int title_offset = 0;
304 for (int i = 0; i < children->length; i++) {
305 struct sway_container *child = children->items[i];
306 bool activated = child == active;
307 int next_title_offset = round(w * i + w);
308
309 arrange_title_bar(child, title_offset, -title_bar_height,
310 next_title_offset - title_offset, title_bar_height);
311 wlr_scene_node_set_enabled(&child->border.tree->node, activated);
312 wlr_scene_node_set_position(&child->scene_tree->node, 0, title_bar_height);
313 wlr_scene_node_reparent(&child->scene_tree->node, content);
314
315 if (activated) {
316 arrange_container(child, width, height - title_bar_height,
317 false, 0);
318 } else {
319 disable_container(child);
320 }
321
322 title_offset = next_title_offset;
323 }
324 } else if (layout == L_STACKED) {
325 struct sway_container *first = children->length == 1 ?
326 ((struct sway_container *)children->items[0]) : NULL;
327 if (config->hide_lone_tab && first && first->view &&
328 first->current.border != B_NORMAL) {
329 title_bar_height = 0;
330 }
331
332 int title_height = title_bar_height * children->length;
333
334 int y = 0;
335 for (int i = 0; i < children->length; i++) {
336 struct sway_container *child = children->items[i];
337 bool activated = child == active;
338
339 arrange_title_bar(child, 0, y - title_height, width, title_bar_height);
340 wlr_scene_node_set_enabled(&child->border.tree->node, activated);
341 wlr_scene_node_set_position(&child->scene_tree->node, 0, title_height);
342 wlr_scene_node_reparent(&child->scene_tree->node, content);
343
344 if (activated) {
345 arrange_container(child, width, height - title_height,
346 false, 0);
347 } else {
348 disable_container(child);
349 }
350
351 y += title_bar_height;
352 }
353 } else if (layout == L_VERT) {
354 int off = 0;
355 for (int i = 0; i < children->length; i++) {
356 struct sway_container *child = children->items[i];
357 int cheight = child->current.height;
358
359 wlr_scene_node_set_enabled(&child->border.tree->node, true);
360 wlr_scene_node_set_position(&child->scene_tree->node, 0, off);
361 wlr_scene_node_reparent(&child->scene_tree->node, content);
362 arrange_container(child, width, cheight, true, gaps);
363 off += cheight + gaps;
364 }
365 } else if (layout == L_HORIZ) {
366 int off = 0;
367 for (int i = 0; i < children->length; i++) {
368 struct sway_container *child = children->items[i];
369 int cwidth = child->current.width;
370
371 wlr_scene_node_set_enabled(&child->border.tree->node, true);
372 wlr_scene_node_set_position(&child->scene_tree->node, off, 0);
373 wlr_scene_node_reparent(&child->scene_tree->node, content);
374 arrange_container(child, cwidth, height, true, gaps);
375 off += cwidth + gaps;
376 }
377 } else {
378 sway_assert(false, "unreachable");
379 }
380}
381
382static void arrange_container(struct sway_container *con,
383 int width, int height, bool title_bar, int gaps) {
384 // this container might have previously been in the scratchpad,
385 // make sure it's enabled for viewing
386 wlr_scene_node_set_enabled(&con->scene_tree->node, true);
387
388 if (con->output_handler) {
389 wlr_scene_buffer_set_dest_size(con->output_handler, width, height);
390 }
391
392 if (con->view) {
393 int border_top = container_titlebar_height();
394 int border_width = con->current.border_thickness;
395
396 if (title_bar && con->current.border != B_NORMAL) {
397 wlr_scene_node_set_enabled(&con->title_bar.tree->node, false);
398 wlr_scene_node_set_enabled(&con->border.top->node, true);
399 } else {
400 wlr_scene_node_set_enabled(&con->border.top->node, false);
401 }
402
403 if (con->current.border == B_NORMAL) {
404 if (title_bar) {
405 arrange_title_bar(con, 0, 0, width, border_top);
406 } else {
407 border_top = 0;
408 // should be handled by the parent container
409 }
410 } else if (con->current.border == B_PIXEL) {
411 container_update(con);
412 border_top = title_bar && con->current.border_top ? border_width : 0;
413 } else if (con->current.border == B_NONE) {
414 container_update(con);
415 border_top = 0;
416 border_width = 0;
417 } else if (con->current.border == B_CSD) {
418 border_top = 0;
419 border_width = 0;
420 } else {
421 sway_assert(false, "unreachable");
422 }
423
424 int border_bottom = con->current.border_bottom ? border_width : 0;
425 int border_left = con->current.border_left ? border_width : 0;
426 int border_right = con->current.border_right ? border_width : 0;
427
428 wlr_scene_rect_set_size(con->border.top, width, border_top);
429 wlr_scene_rect_set_size(con->border.bottom, width, border_bottom);
430 wlr_scene_rect_set_size(con->border.left,
431 border_left, height - border_top - border_bottom);
432 wlr_scene_rect_set_size(con->border.right,
433 border_right, height - border_top - border_bottom);
434
435 wlr_scene_node_set_position(&con->border.top->node, 0, 0);
436 wlr_scene_node_set_position(&con->border.bottom->node,
437 0, height - border_bottom);
438 wlr_scene_node_set_position(&con->border.left->node,
439 0, border_top);
440 wlr_scene_node_set_position(&con->border.right->node,
441 width - border_right, border_top);
442
443 // make sure to reparent, it's possible that the client just came out of
444 // fullscreen mode where the parent of the surface is not the container
445 wlr_scene_node_reparent(&con->view->scene_tree->node, con->content_tree);
446 wlr_scene_node_set_position(&con->view->scene_tree->node,
447 border_left, border_top);
448 } else {
449 // make sure to disable the title bar if the parent is not managing it
450 if (title_bar) {
451 wlr_scene_node_set_enabled(&con->title_bar.tree->node, false);
452 }
453
454 arrange_children(con->current.layout, con->current.children,
455 con->current.focused_inactive_child, con->content_tree,
456 width, height, gaps);
457 }
458}
459
460static int container_get_gaps(struct sway_container *con) {
461 struct sway_workspace *ws = con->current.workspace;
462 struct sway_container *temp = con;
463 while (temp) {
464 enum sway_container_layout layout;
465 if (temp->current.parent) {
466 layout = temp->current.parent->current.layout;
467 } else {
468 layout = ws->current.layout;
469 }
470 if (layout == L_TABBED || layout == L_STACKED) {
471 return 0;
472 }
473 temp = temp->pending.parent;
474 }
475 return ws->gaps_inner;
476}
477
478static void arrange_fullscreen(struct wlr_scene_tree *tree,
479 struct sway_container *fs, struct sway_workspace *ws,
480 int width, int height) {
481 struct wlr_scene_node *fs_node;
482 if (fs->view) {
483 fs_node = &fs->view->scene_tree->node;
484
485 // if we only care about the view, disable any decorations
486 wlr_scene_node_set_enabled(&fs->scene_tree->node, false);
487 } else {
488 fs_node = &fs->scene_tree->node;
489 arrange_container(fs, width, height, true, container_get_gaps(fs));
490 }
491
492 wlr_scene_node_reparent(fs_node, tree);
493 wlr_scene_node_lower_to_bottom(fs_node);
494 wlr_scene_node_set_position(fs_node, 0, 0);
495}
496
497static void arrange_workspace_floating(struct sway_workspace *ws) {
498 for (int i = 0; i < ws->current.floating->length; i++) {
499 struct sway_container *floater = ws->current.floating->items[i];
500 struct wlr_scene_tree *layer = root->layers.floating;
501
502 if (floater->current.fullscreen_mode != FULLSCREEN_NONE) {
503 continue;
504 }
505
506 if (root->fullscreen_global) {
507 if (container_is_transient_for(floater, root->fullscreen_global)) {
508 layer = root->layers.fullscreen_global;
509 }
510 } else {
511 for (int i = 0; i < root->outputs->length; i++) {
512 struct sway_output *output = root->outputs->items[i];
513 struct sway_workspace *active = output->current.active_workspace;
514
515 if (active && active->fullscreen &&
516 container_is_transient_for(floater, active->fullscreen)) {
517 layer = root->layers.fullscreen;
518 }
519 }
520 }
521
522 wlr_scene_node_reparent(&floater->scene_tree->node, layer);
523 wlr_scene_node_set_position(&floater->scene_tree->node,
524 floater->current.x, floater->current.y);
525 wlr_scene_node_set_enabled(&floater->scene_tree->node, true);
526
527 arrange_container(floater, floater->current.width, floater->current.height,
528 true, ws->gaps_inner);
529 }
530}
531
532static void arrange_workspace_tiling(struct sway_workspace *ws,
533 int width, int height) {
534 arrange_children(ws->current.layout, ws->current.tiling,
535 ws->current.focused_inactive_child, ws->layers.tiling,
536 width, height, ws->gaps_inner);
537}
538
539static void disable_workspace(struct sway_workspace *ws) {
540 // if any containers were just moved to a disabled workspace it will
541 // have the parent of the old workspace. Move the workspace so that it won't
542 // be shown.
543 for (int i = 0; i < ws->current.tiling->length; i++) {
544 struct sway_container *child = ws->current.tiling->items[i];
545
546 wlr_scene_node_reparent(&child->scene_tree->node, ws->layers.tiling);
547 disable_container(child);
548 }
549
550 for (int i = 0; i < ws->current.floating->length; i++) {
551 struct sway_container *floater = ws->current.floating->items[i];
552 wlr_scene_node_reparent(&floater->scene_tree->node, root->layers.floating);
553 disable_container(floater);
554 wlr_scene_node_set_enabled(&floater->scene_tree->node, false);
555 }
556}
557
558static void arrange_output(struct sway_output *output, int width, int height) {
559 for (int i = 0; i < output->current.workspaces->length; i++) {
560 struct sway_workspace *child = output->current.workspaces->items[i];
561
562 bool activated = output->current.active_workspace == child;
563
564 wlr_scene_node_reparent(&child->layers.tiling->node, output->layers.tiling);
565 wlr_scene_node_reparent(&child->layers.fullscreen->node, output->layers.fullscreen);
566
567 for (int i = 0; i < child->current.floating->length; i++) {
568 struct sway_container *floater = child->current.floating->items[i];
569 wlr_scene_node_reparent(&floater->scene_tree->node, root->layers.floating);
570 wlr_scene_node_set_enabled(&floater->scene_tree->node, activated);
571 }
572
573 if (activated) {
574 struct sway_container *fs = child->current.fullscreen;
575 wlr_scene_node_set_enabled(&child->layers.tiling->node, !fs);
576 wlr_scene_node_set_enabled(&child->layers.fullscreen->node, fs);
577
578 arrange_workspace_floating(child);
579
580 wlr_scene_node_set_enabled(&output->layers.shell_background->node, !fs);
581 wlr_scene_node_set_enabled(&output->layers.shell_bottom->node, !fs);
582 wlr_scene_node_set_enabled(&output->layers.fullscreen->node, fs);
583
584 if (fs) {
585 wlr_scene_rect_set_size(output->fullscreen_background, width, height);
586
587 arrange_fullscreen(child->layers.fullscreen, fs, child,
588 width, height);
589 } else {
590 struct wlr_box *area = &output->usable_area;
591 struct side_gaps *gaps = &child->current_gaps;
592
593 wlr_scene_node_set_position(&child->layers.tiling->node,
594 gaps->left + area->x, gaps->top + area->y);
595
596 arrange_workspace_tiling(child,
597 area->width - gaps->left - gaps->right,
598 area->height - gaps->top - gaps->bottom);
599 }
600 } else {
601 wlr_scene_node_set_enabled(&child->layers.tiling->node, false);
602 wlr_scene_node_set_enabled(&child->layers.fullscreen->node, false);
603
604 disable_workspace(child);
262 } 605 }
263 } 606 }
607}
264 608
265 // If the view hasn't responded to the configure, center it within 609void arrange_popups(struct wlr_scene_tree *popups) {
266 // the container. This is important for fullscreen views which 610 struct wlr_scene_node *node;
267 // refuse to resize to the size of the output. 611 wl_list_for_each(node, &popups->children, link) {
268 if (view && view->surface) { 612 struct sway_popup_desc *popup = scene_descriptor_try_get(node,
269 view_center_surface(view); 613 SWAY_SCENE_DESC_POPUP);
614
615 int lx, ly;
616 wlr_scene_node_coords(popup->relative, &lx, &ly);
617 wlr_scene_node_set_position(node, lx, ly);
270 } 618 }
619}
620
621static void arrange_root(struct sway_root *root) {
622 struct sway_container *fs = root->fullscreen_global;
623
624 wlr_scene_node_set_enabled(&root->layers.shell_background->node, !fs);
625 wlr_scene_node_set_enabled(&root->layers.shell_bottom->node, !fs);
626 wlr_scene_node_set_enabled(&root->layers.tiling->node, !fs);
627 wlr_scene_node_set_enabled(&root->layers.floating->node, !fs);
628 wlr_scene_node_set_enabled(&root->layers.shell_top->node, !fs);
629 wlr_scene_node_set_enabled(&root->layers.fullscreen->node, !fs);
271 630
272 // Damage the new location 631 // hide all contents in the scratchpad
273 desktop_damage_whole_container(container); 632 for (int i = 0; i < root->scratchpad->length; i++) {
274 if (view && view->surface) { 633 struct sway_container *con = root->scratchpad->items[i];
275 struct wlr_surface *surface = view->surface; 634
276 struct wlr_box box = { 635 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 } 636 }
284 637
285 if (!container->node.destroying) { 638 if (fs) {
286 container_discover_outputs(container); 639 for (int i = 0; i < root->outputs->length; i++) {
640 struct sway_output *output = root->outputs->items[i];
641 struct sway_workspace *ws = output->current.active_workspace;
642
643 if (ws) {
644 arrange_workspace_floating(ws);
645 }
646 }
647
648 arrange_fullscreen(root->layers.fullscreen_global, fs, NULL,
649 root->width, root->height);
650 } else {
651 for (int i = 0; i < root->outputs->length; i++) {
652 struct sway_output *output = root->outputs->items[i];
653
654 wlr_scene_output_set_position(output->scene_output, output->lx, output->ly);
655
656 wlr_scene_node_reparent(&output->layers.shell_background->node, root->layers.shell_background);
657 wlr_scene_node_reparent(&output->layers.shell_bottom->node, root->layers.shell_bottom);
658 wlr_scene_node_reparent(&output->layers.tiling->node, root->layers.tiling);
659 wlr_scene_node_reparent(&output->layers.shell_top->node, root->layers.shell_top);
660 wlr_scene_node_reparent(&output->layers.shell_overlay->node, root->layers.shell_overlay);
661 wlr_scene_node_reparent(&output->layers.fullscreen->node, root->layers.fullscreen);
662 wlr_scene_node_reparent(&output->layers.session_lock->node, root->layers.session_lock);
663
664 wlr_scene_node_set_position(&output->layers.shell_background->node, output->lx, output->ly);
665 wlr_scene_node_set_position(&output->layers.shell_bottom->node, output->lx, output->ly);
666 wlr_scene_node_set_position(&output->layers.tiling->node, output->lx, output->ly);
667 wlr_scene_node_set_position(&output->layers.fullscreen->node, output->lx, output->ly);
668 wlr_scene_node_set_position(&output->layers.shell_top->node, output->lx, output->ly);
669 wlr_scene_node_set_position(&output->layers.shell_overlay->node, output->lx, output->ly);
670 wlr_scene_node_set_position(&output->layers.session_lock->node, output->lx, output->ly);
671
672 arrange_output(output, output->width, output->height);
673 }
287 } 674 }
675
676 arrange_popups(root->layers.popup);
288} 677}
289 678
290/** 679/**
@@ -326,8 +715,6 @@ static void transaction_apply(struct sway_transaction *transaction) {
326 715
327 node->instruction = NULL; 716 node->instruction = NULL;
328 } 717 }
329
330 cursor_rebase_all();
331} 718}
332 719
333static void transaction_commit_pending(void); 720static void transaction_commit_pending(void);
@@ -340,11 +727,13 @@ static void transaction_progress(void) {
340 return; 727 return;
341 } 728 }
342 transaction_apply(server.queued_transaction); 729 transaction_apply(server.queued_transaction);
730 arrange_root(root);
731 cursor_rebase_all();
343 transaction_destroy(server.queued_transaction); 732 transaction_destroy(server.queued_transaction);
344 server.queued_transaction = NULL; 733 server.queued_transaction = NULL;
345 734
346 if (!server.pending_transaction) { 735 if (!server.pending_transaction) {
347 sway_idle_inhibit_v1_check_active(server.idle_inhibit_manager_v1); 736 sway_idle_inhibit_v1_check_active();
348 return; 737 return;
349 } 738 }
350 739
@@ -373,7 +762,7 @@ static bool should_configure(struct sway_node *node,
373 } 762 }
374 struct sway_container_state *cstate = &node->sway_container->current; 763 struct sway_container_state *cstate = &node->sway_container->current;
375 struct sway_container_state *istate = &instruction->container_state; 764 struct sway_container_state *istate = &instruction->container_state;
376#if HAVE_XWAYLAND 765#if WLR_HAS_XWAYLAND
377 // Xwayland views are position-aware and need to be reconfigured 766 // Xwayland views are position-aware and need to be reconfigured
378 // when their position changes. 767 // when their position changes.
379 if (node->sway_container->view->type == SWAY_VIEW_XWAYLAND) { 768 if (node->sway_container->view->type == SWAY_VIEW_XWAYLAND) {
@@ -415,21 +804,11 @@ static void transaction_commit(struct sway_transaction *transaction) {
415 ++transaction->num_waiting; 804 ++transaction->num_waiting;
416 } 805 }
417 806
418 // From here on we are rendering a saved buffer of the view, which 807 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 } 808 }
427 if (!hidden && node_is_view(node) && 809 if (!hidden && node_is_view(node) &&
428 wl_list_empty(&node->sway_container->view->saved_buffers)) { 810 !node->sway_container->view->saved_surface_tree) {
429 view_save_buffer(node->sway_container->view); 811 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 } 812 }
434 node->instruction = instruction; 813 node->instruction = instruction;
435 } 814 }
@@ -499,16 +878,18 @@ static void set_instruction_ready(
499 transaction_progress(); 878 transaction_progress();
500} 879}
501 880
502void transaction_notify_view_ready_by_serial(struct sway_view *view, 881bool transaction_notify_view_ready_by_serial(struct sway_view *view,
503 uint32_t serial) { 882 uint32_t serial) {
504 struct sway_transaction_instruction *instruction = 883 struct sway_transaction_instruction *instruction =
505 view->container->node.instruction; 884 view->container->node.instruction;
506 if (instruction != NULL && instruction->serial == serial) { 885 if (instruction != NULL && instruction->serial == serial) {
507 set_instruction_ready(instruction); 886 set_instruction_ready(instruction);
887 return true;
508 } 888 }
889 return false;
509} 890}
510 891
511void transaction_notify_view_ready_by_geometry(struct sway_view *view, 892bool transaction_notify_view_ready_by_geometry(struct sway_view *view,
512 double x, double y, int width, int height) { 893 double x, double y, int width, int height) {
513 struct sway_transaction_instruction *instruction = 894 struct sway_transaction_instruction *instruction =
514 view->container->node.instruction; 895 view->container->node.instruction;
@@ -518,7 +899,9 @@ void transaction_notify_view_ready_by_geometry(struct sway_view *view,
518 instruction->container_state.content_width == width && 899 instruction->container_state.content_width == width &&
519 instruction->container_state.content_height == height) { 900 instruction->container_state.content_height == height) {
520 set_instruction_ready(instruction); 901 set_instruction_ready(instruction);
902 return true;
521 } 903 }
904 return false;
522} 905}
523 906
524static void _transaction_commit_dirty(bool server_request) { 907static void _transaction_commit_dirty(bool server_request) {
diff --git a/sway/desktop/xdg_shell.c b/sway/desktop/xdg_shell.c
index 51168f4c..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,55 +18,39 @@
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_view_coords(struct sway_view_child *child,
25 int *sx, int *sy) {
26 struct sway_xdg_popup *popup = (struct sway_xdg_popup *)child;
27 struct wlr_xdg_popup *wlr_popup = popup->wlr_xdg_popup;
28
29 wlr_xdg_popup_get_toplevel_coords(wlr_popup,
30 wlr_popup->geometry.x - wlr_popup->base->current.geometry.x,
31 wlr_popup->geometry.y - wlr_popup->base->current.geometry.y,
32 sx, sy);
33}
34
35static void popup_destroy(struct sway_view_child *child) {
36 if (!sway_assert(child->impl == &popup_impl,
37 "Expected an xdg_shell popup")) {
38 return;
39 }
40 struct sway_xdg_popup *popup = (struct sway_xdg_popup *)child;
41 wl_list_remove(&popup->new_popup.link);
42 wl_list_remove(&popup->destroy.link);
43 free(popup);
44}
45
46static const struct sway_view_child_impl popup_impl = {
47 .get_view_coords = popup_get_view_coords,
48 .destroy = popup_destroy,
49};
50
51static struct sway_xdg_popup *popup_create( 21static struct sway_xdg_popup *popup_create(
52 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);
53 24
54static void popup_handle_new_popup(struct wl_listener *listener, void *data) { 25static void popup_handle_new_popup(struct wl_listener *listener, void *data) {
55 struct sway_xdg_popup *popup = 26 struct sway_xdg_popup *popup =
56 wl_container_of(listener, popup, new_popup); 27 wl_container_of(listener, popup, new_popup);
57 struct wlr_xdg_popup *wlr_popup = data; 28 struct wlr_xdg_popup *wlr_popup = data;
58 popup_create(wlr_popup, popup->child.view); 29 popup_create(wlr_popup, popup->view, popup->xdg_surface_tree);
59} 30}
60 31
61static void popup_handle_destroy(struct wl_listener *listener, void *data) { 32static void popup_handle_destroy(struct wl_listener *listener, void *data) {
62 struct sway_xdg_popup *popup = wl_container_of(listener, popup, destroy); 33 struct sway_xdg_popup *popup = wl_container_of(listener, popup, destroy);
63 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);
64} 41}
65 42
66static void popup_unconstrain(struct sway_xdg_popup *popup) { 43static void popup_unconstrain(struct sway_xdg_popup *popup) {
67 struct sway_view *view = popup->child.view; 44 struct sway_view *view = popup->view;
68 struct wlr_xdg_popup *wlr_popup = popup->wlr_xdg_popup; 45 struct wlr_xdg_popup *wlr_popup = popup->wlr_xdg_popup;
69 46
70 struct sway_output *output = view->container->pending.workspace->output; 47 struct sway_workspace *workspace = view->container->pending.workspace;
48 if (!workspace) {
49 // is null if in the scratchpad
50 return;
51 }
52
53 struct sway_output *output = workspace->output;
71 54
72 // 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
73 // of the popup 56 // of the popup
@@ -81,32 +64,72 @@ static void popup_unconstrain(struct sway_xdg_popup *popup) {
81 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);
82} 65}
83 66
84static struct sway_xdg_popup *popup_create( 67static void popup_handle_surface_commit(struct wl_listener *listener, void *data) {
85 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) {
86 struct wlr_xdg_surface *xdg_surface = wlr_popup->base; 81 struct wlr_xdg_surface *xdg_surface = wlr_popup->base;
87 82
88 struct sway_xdg_popup *popup = 83 struct sway_xdg_popup *popup = calloc(1, sizeof(struct sway_xdg_popup));
89 calloc(1, sizeof(struct sway_xdg_popup)); 84 if (!popup) {
90 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);
91 return NULL; 102 return NULL;
92 } 103 }
93 view_child_init(&popup->child, &popup_impl, view, xdg_surface->surface); 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
94 popup->wlr_xdg_popup = xdg_surface->popup; 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;
95 120
121 wl_signal_add(&xdg_surface->surface->events.commit, &popup->surface_commit);
122 popup->surface_commit.notify = popup_handle_surface_commit;
96 wl_signal_add(&xdg_surface->events.new_popup, &popup->new_popup); 123 wl_signal_add(&xdg_surface->events.new_popup, &popup->new_popup);
97 popup->new_popup.notify = popup_handle_new_popup; 124 popup->new_popup.notify = popup_handle_new_popup;
98 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);
99 popup->destroy.notify = popup_handle_destroy; 128 popup->destroy.notify = popup_handle_destroy;
100 129
101 wl_signal_add(&xdg_surface->events.map, &popup->child.surface_map);
102 wl_signal_add(&xdg_surface->events.unmap, &popup->child.surface_unmap);
103
104 popup_unconstrain(popup);
105
106 return popup; 130 return popup;
107} 131}
108 132
109
110static struct sway_xdg_shell_view *xdg_shell_view_from_view( 133static struct sway_xdg_shell_view *xdg_shell_view_from_view(
111 struct sway_view *view) { 134 struct sway_view *view) {
112 if (!sway_assert(view->type == SWAY_VIEW_XDG_SHELL, 135 if (!sway_assert(view->type == SWAY_VIEW_XDG_SHELL,
@@ -163,12 +186,19 @@ static void set_tiled(struct sway_view *view, bool tiled) {
163 if (xdg_shell_view_from_view(view) == NULL) { 186 if (xdg_shell_view_from_view(view) == NULL) {
164 return; 187 return;
165 } 188 }
166 enum wlr_edges edges = WLR_EDGE_NONE; 189 if (wl_resource_get_version(view->wlr_xdg_toplevel->resource) >=
167 if (tiled) { 190 XDG_TOPLEVEL_STATE_TILED_LEFT_SINCE_VERSION) {
168 edges = WLR_EDGE_LEFT | WLR_EDGE_RIGHT | WLR_EDGE_TOP | 191 enum wlr_edges edges = WLR_EDGE_NONE;
169 WLR_EDGE_BOTTOM; 192 if (tiled) {
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);
170 } 201 }
171 wlr_xdg_toplevel_set_tiled(view->wlr_xdg_toplevel, edges);
172} 202}
173 203
174static void set_fullscreen(struct sway_view *view, bool fullscreen) { 204static void set_fullscreen(struct sway_view *view, bool fullscreen) {
@@ -194,24 +224,6 @@ static bool wants_floating(struct sway_view *view) {
194 || toplevel->parent; 224 || toplevel->parent;
195} 225}
196 226
197static void for_each_surface(struct sway_view *view,
198 wlr_surface_iterator_func_t iterator, void *user_data) {
199 if (xdg_shell_view_from_view(view) == NULL) {
200 return;
201 }
202 wlr_xdg_surface_for_each_surface(view->wlr_xdg_toplevel->base, iterator,
203 user_data);
204}
205
206static void for_each_popup_surface(struct sway_view *view,
207 wlr_surface_iterator_func_t iterator, void *user_data) {
208 if (xdg_shell_view_from_view(view) == NULL) {
209 return;
210 }
211 wlr_xdg_surface_for_each_popup_surface(view->wlr_xdg_toplevel->base,
212 iterator, user_data);
213}
214
215static bool is_transient_for(struct sway_view *child, 227static bool is_transient_for(struct sway_view *child,
216 struct sway_view *ancestor) { 228 struct sway_view *ancestor) {
217 if (xdg_shell_view_from_view(child) == NULL) { 229 if (xdg_shell_view_from_view(child) == NULL) {
@@ -259,8 +271,6 @@ static const struct sway_view_impl view_impl = {
259 .set_fullscreen = set_fullscreen, 271 .set_fullscreen = set_fullscreen,
260 .set_resizing = set_resizing, 272 .set_resizing = set_resizing,
261 .wants_floating = wants_floating, 273 .wants_floating = wants_floating,
262 .for_each_surface = for_each_surface,
263 .for_each_popup_surface = for_each_popup_surface,
264 .is_transient_for = is_transient_for, 274 .is_transient_for = is_transient_for,
265 .close = _close, 275 .close = _close,
266 .close_popups = close_popups, 276 .close_popups = close_popups,
@@ -273,6 +283,20 @@ static void handle_commit(struct wl_listener *listener, void *data) {
273 struct sway_view *view = &xdg_shell_view->view; 283 struct sway_view *view = &xdg_shell_view->view;
274 struct wlr_xdg_surface *xdg_surface = view->wlr_xdg_toplevel->base; 284 struct wlr_xdg_surface *xdg_surface = view->wlr_xdg_toplevel->base;
275 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 }
299
276 struct wlr_box new_geo; 300 struct wlr_box new_geo;
277 wlr_xdg_surface_get_geometry(xdg_surface, &new_geo); 301 wlr_xdg_surface_get_geometry(xdg_surface, &new_geo);
278 bool new_size = new_geo.width != view->geometry.width || 302 bool new_size = new_geo.width != view->geometry.width ||
@@ -284,23 +308,32 @@ static void handle_commit(struct wl_listener *listener, void *data) {
284 // The client changed its surface size in this commit. For floating 308 // The client changed its surface size in this commit. For floating
285 // containers, we resize the container to match. For tiling containers, 309 // containers, we resize the container to match. For tiling containers,
286 // we only recenter the surface. 310 // we only recenter the surface.
287 desktop_damage_view(view);
288 memcpy(&view->geometry, &new_geo, sizeof(struct wlr_box)); 311 memcpy(&view->geometry, &new_geo, sizeof(struct wlr_box));
289 if (container_is_floating(view->container)) { 312 if (container_is_floating(view->container)) {
290 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 }
291 transaction_commit_dirty_client(); 319 transaction_commit_dirty_client();
292 } else {
293 view_center_surface(view);
294 } 320 }
295 desktop_damage_view(view); 321
322 view_center_and_clip_surface(view);
296 } 323 }
297 324
298 if (view->container->node.instruction) { 325 if (view->container->node.instruction) {
299 transaction_notify_view_ready_by_serial(view, 326 bool successful = transaction_notify_view_ready_by_serial(view,
300 xdg_surface->current.configure_serial); 327 xdg_surface->current.configure_serial);
301 }
302 328
303 view_damage_from(view); 329 // If we saved the view and this commit isn't what we're looking for
330 // that means the user will never actually see the buffers submitted to
331 // us here. Just send frame done events to these surfaces so they can
332 // commit another time for us.
333 if (view->saved_surface_tree && !successful) {
334 view_send_frame_done(view);
335 }
336 }
304} 337}
305 338
306static void handle_set_title(struct wl_listener *listener, void *data) { 339static void handle_set_title(struct wl_listener *listener, void *data) {
@@ -315,6 +348,7 @@ static void handle_set_app_id(struct wl_listener *listener, void *data) {
315 struct sway_xdg_shell_view *xdg_shell_view = 348 struct sway_xdg_shell_view *xdg_shell_view =
316 wl_container_of(listener, xdg_shell_view, set_app_id); 349 wl_container_of(listener, xdg_shell_view, set_app_id);
317 struct sway_view *view = &xdg_shell_view->view; 350 struct sway_view *view = &xdg_shell_view->view;
351 view_update_app_id(view);
318 view_execute_criteria(view); 352 view_execute_criteria(view);
319} 353}
320 354
@@ -322,7 +356,23 @@ static void handle_new_popup(struct wl_listener *listener, void *data) {
322 struct sway_xdg_shell_view *xdg_shell_view = 356 struct sway_xdg_shell_view *xdg_shell_view =
323 wl_container_of(listener, xdg_shell_view, new_popup); 357 wl_container_of(listener, xdg_shell_view, new_popup);
324 struct wlr_xdg_popup *wlr_popup = data; 358 struct wlr_xdg_popup *wlr_popup = data;
325 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);
326} 376}
327 377
328static void handle_request_fullscreen(struct wl_listener *listener, void *data) { 378static void handle_request_fullscreen(struct wl_listener *listener, void *data) {
@@ -331,7 +381,7 @@ static void handle_request_fullscreen(struct wl_listener *listener, void *data)
331 struct wlr_xdg_toplevel *toplevel = xdg_shell_view->view.wlr_xdg_toplevel; 381 struct wlr_xdg_toplevel *toplevel = xdg_shell_view->view.wlr_xdg_toplevel;
332 struct sway_view *view = &xdg_shell_view->view; 382 struct sway_view *view = &xdg_shell_view->view;
333 383
334 if (!toplevel->base->mapped) { 384 if (!toplevel->base->surface->mapped) {
335 return; 385 return;
336 } 386 }
337 387
@@ -396,8 +446,8 @@ static void handle_unmap(struct wl_listener *listener, void *data) {
396 446
397 view_unmap(view); 447 view_unmap(view);
398 448
399 wl_list_remove(&xdg_shell_view->commit.link);
400 wl_list_remove(&xdg_shell_view->new_popup.link); 449 wl_list_remove(&xdg_shell_view->new_popup.link);
450 wl_list_remove(&xdg_shell_view->request_maximize.link);
401 wl_list_remove(&xdg_shell_view->request_fullscreen.link); 451 wl_list_remove(&xdg_shell_view->request_fullscreen.link);
402 wl_list_remove(&xdg_shell_view->request_move.link); 452 wl_list_remove(&xdg_shell_view->request_move.link);
403 wl_list_remove(&xdg_shell_view->request_resize.link); 453 wl_list_remove(&xdg_shell_view->request_resize.link);
@@ -438,14 +488,14 @@ static void handle_map(struct wl_listener *listener, void *data) {
438 488
439 transaction_commit_dirty(); 489 transaction_commit_dirty();
440 490
441 xdg_shell_view->commit.notify = handle_commit;
442 wl_signal_add(&toplevel->base->surface->events.commit,
443 &xdg_shell_view->commit);
444
445 xdg_shell_view->new_popup.notify = handle_new_popup; 491 xdg_shell_view->new_popup.notify = handle_new_popup;
446 wl_signal_add(&toplevel->base->events.new_popup, 492 wl_signal_add(&toplevel->base->events.new_popup,
447 &xdg_shell_view->new_popup); 493 &xdg_shell_view->new_popup);
448 494
495 xdg_shell_view->request_maximize.notify = handle_request_maximize;
496 wl_signal_add(&toplevel->events.request_maximize,
497 &xdg_shell_view->request_maximize);
498
449 xdg_shell_view->request_fullscreen.notify = handle_request_fullscreen; 499 xdg_shell_view->request_fullscreen.notify = handle_request_fullscreen;
450 wl_signal_add(&toplevel->events.request_fullscreen, 500 wl_signal_add(&toplevel->events.request_fullscreen,
451 &xdg_shell_view->request_fullscreen); 501 &xdg_shell_view->request_fullscreen);
@@ -477,6 +527,7 @@ static void handle_destroy(struct wl_listener *listener, void *data) {
477 wl_list_remove(&xdg_shell_view->destroy.link); 527 wl_list_remove(&xdg_shell_view->destroy.link);
478 wl_list_remove(&xdg_shell_view->map.link); 528 wl_list_remove(&xdg_shell_view->map.link);
479 wl_list_remove(&xdg_shell_view->unmap.link); 529 wl_list_remove(&xdg_shell_view->unmap.link);
530 wl_list_remove(&xdg_shell_view->commit.link);
480 view->wlr_xdg_toplevel = NULL; 531 view->wlr_xdg_toplevel = NULL;
481 if (view->xdg_decoration) { 532 if (view->xdg_decoration) {
482 view->xdg_decoration->view = NULL; 533 view->xdg_decoration->view = NULL;
@@ -489,17 +540,12 @@ struct sway_view *view_from_wlr_xdg_surface(
489 return xdg_surface->data; 540 return xdg_surface->data;
490} 541}
491 542
492void handle_xdg_shell_surface(struct wl_listener *listener, void *data) { 543void handle_xdg_shell_toplevel(struct wl_listener *listener, void *data) {
493 struct wlr_xdg_surface *xdg_surface = data; 544 struct wlr_xdg_toplevel *xdg_toplevel = data;
494
495 if (xdg_surface->role == WLR_XDG_SURFACE_ROLE_POPUP) {
496 sway_log(SWAY_DEBUG, "New xdg_shell popup");
497 return;
498 }
499 545
500 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'",
501 xdg_surface->toplevel->title, xdg_surface->toplevel->app_id); 547 xdg_toplevel->title, xdg_toplevel->app_id);
502 wlr_xdg_surface_ping(xdg_surface); 548 wlr_xdg_surface_ping(xdg_toplevel->base);
503 549
504 struct sway_xdg_shell_view *xdg_shell_view = 550 struct sway_xdg_shell_view *xdg_shell_view =
505 calloc(1, sizeof(struct sway_xdg_shell_view)); 551 calloc(1, sizeof(struct sway_xdg_shell_view));
@@ -507,17 +553,29 @@ void handle_xdg_shell_surface(struct wl_listener *listener, void *data) {
507 return; 553 return;
508 } 554 }
509 555
510 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)) {
511 xdg_shell_view->view.wlr_xdg_toplevel = xdg_surface->toplevel; 557 free(xdg_shell_view);
558 return;
559 }
560 xdg_shell_view->view.wlr_xdg_toplevel = xdg_toplevel;
512 561
513 xdg_shell_view->map.notify = handle_map; 562 xdg_shell_view->map.notify = handle_map;
514 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);
515 564
516 xdg_shell_view->unmap.notify = handle_unmap; 565 xdg_shell_view->unmap.notify = handle_unmap;
517 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);
518 571
519 xdg_shell_view->destroy.notify = handle_destroy; 572 xdg_shell_view->destroy.notify = handle_destroy;
520 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;
521 578
522 xdg_surface->data = xdg_shell_view; 579 wlr_xdg_toplevel_set_wm_capabilities(xdg_toplevel,
580 XDG_TOPLEVEL_WM_CAPABILITIES_FULLSCREEN);
523} 581}
diff --git a/sway/desktop/xwayland.c b/sway/desktop/xwayland.c
index 7c5dde53..270cf08f 100644
--- a/sway/desktop/xwayland.c
+++ b/sway/desktop/xwayland.c
@@ -1,21 +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>
9#include <xcb/xcb_icccm.h> 10#include <xcb/xcb_icccm.h>
10#include "log.h" 11#include "log.h"
11#include "sway/desktop.h"
12#include "sway/desktop/transaction.h" 12#include "sway/desktop/transaction.h"
13#include "sway/input/cursor.h" 13#include "sway/input/cursor.h"
14#include "sway/input/input-manager.h" 14#include "sway/input/input-manager.h"
15#include "sway/input/seat.h" 15#include "sway/input/seat.h"
16#include "sway/output.h" 16#include "sway/output.h"
17#include "sway/scene_descriptor.h"
17#include "sway/tree/arrange.h" 18#include "sway/tree/arrange.h"
18#include "sway/tree/container.h" 19#include "sway/tree/container.h"
20#include "sway/server.h"
19#include "sway/tree/view.h" 21#include "sway/tree/view.h"
20#include "sway/tree/workspace.h" 22#include "sway/tree/workspace.h"
21 23
@@ -43,29 +45,12 @@ static void unmanaged_handle_request_configure(struct wl_listener *listener,
43 ev->width, ev->height); 45 ev->width, ev->height);
44} 46}
45 47
46static void unmanaged_handle_commit(struct wl_listener *listener, void *data) {
47 struct sway_xwayland_unmanaged *surface =
48 wl_container_of(listener, surface, commit);
49 struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface;
50
51 desktop_damage_surface(xsurface->surface, surface->lx, surface->ly,
52 false);
53}
54
55static void unmanaged_handle_set_geometry(struct wl_listener *listener, void *data) { 48static void unmanaged_handle_set_geometry(struct wl_listener *listener, void *data) {
56 struct sway_xwayland_unmanaged *surface = 49 struct sway_xwayland_unmanaged *surface =
57 wl_container_of(listener, surface, set_geometry); 50 wl_container_of(listener, surface, set_geometry);
58 struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface; 51 struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface;
59 52
60 if (xsurface->x != surface->lx || xsurface->y != surface->ly) { 53 wlr_scene_node_set_position(&surface->surface_scene->buffer->node, xsurface->x, xsurface->y);
61 // Surface has moved
62 desktop_damage_surface(xsurface->surface, surface->lx, surface->ly,
63 true);
64 surface->lx = xsurface->x;
65 surface->ly = xsurface->y;
66 desktop_damage_surface(xsurface->surface, surface->lx, surface->ly,
67 true);
68 }
69} 54}
70 55
71static void unmanaged_handle_map(struct wl_listener *listener, void *data) { 56static void unmanaged_handle_map(struct wl_listener *listener, void *data) {
@@ -73,17 +58,18 @@ static void unmanaged_handle_map(struct wl_listener *listener, void *data) {
73 wl_container_of(listener, surface, map); 58 wl_container_of(listener, surface, map);
74 struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface; 59 struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface;
75 60
76 wl_list_insert(root->xwayland_unmanaged.prev, &surface->link); 61 surface->surface_scene = wlr_scene_surface_create(root->layers.unmanaged,
62 xsurface->surface);
77 63
78 wl_signal_add(&xsurface->events.set_geometry, &surface->set_geometry); 64 if (surface->surface_scene) {
79 surface->set_geometry.notify = unmanaged_handle_set_geometry; 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);
80 69
81 wl_signal_add(&xsurface->surface->events.commit, &surface->commit); 70 wl_signal_add(&xsurface->events.set_geometry, &surface->set_geometry);
82 surface->commit.notify = unmanaged_handle_commit; 71 surface->set_geometry.notify = unmanaged_handle_set_geometry;
83 72 }
84 surface->lx = xsurface->x;
85 surface->ly = xsurface->y;
86 desktop_damage_surface(xsurface->surface, surface->lx, surface->ly, true);
87 73
88 if (wlr_xwayland_or_surface_wants_focus(xsurface)) { 74 if (wlr_xwayland_or_surface_wants_focus(xsurface)) {
89 struct sway_seat *seat = input_manager_current_seat(); 75 struct sway_seat *seat = input_manager_current_seat();
@@ -97,10 +83,13 @@ static void unmanaged_handle_unmap(struct wl_listener *listener, void *data) {
97 struct sway_xwayland_unmanaged *surface = 83 struct sway_xwayland_unmanaged *surface =
98 wl_container_of(listener, surface, unmap); 84 wl_container_of(listener, surface, unmap);
99 struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface; 85 struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface;
100 desktop_damage_surface(xsurface->surface, xsurface->x, xsurface->y, true); 86
101 wl_list_remove(&surface->link); 87 if (surface->surface_scene) {
102 wl_list_remove(&surface->set_geometry.link); 88 wl_list_remove(&surface->set_geometry.link);
103 wl_list_remove(&surface->commit.link); 89
90 wlr_scene_node_destroy(&surface->surface_scene->buffer->node);
91 surface->surface_scene = NULL;
92 }
104 93
105 struct sway_seat *seat = input_manager_current_seat(); 94 struct sway_seat *seat = input_manager_current_seat();
106 if (seat->wlr_seat->keyboard_state.focused_surface == xsurface->surface) { 95 if (seat->wlr_seat->keyboard_state.focused_surface == xsurface->surface) {
@@ -123,8 +112,10 @@ static void unmanaged_handle_unmap(struct wl_listener *listener, void *data) {
123} 112}
124 113
125static void unmanaged_handle_request_activate(struct wl_listener *listener, void *data) { 114static void unmanaged_handle_request_activate(struct wl_listener *listener, void *data) {
126 struct wlr_xwayland_surface *xsurface = data; 115 struct sway_xwayland_unmanaged *surface =
127 if (!xsurface->mapped) { 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) {
128 return; 119 return;
129 } 120 }
130 struct sway_seat *seat = input_manager_current_seat(); 121 struct sway_seat *seat = input_manager_current_seat();
@@ -136,12 +127,29 @@ static void unmanaged_handle_request_activate(struct wl_listener *listener, void
136 seat_set_focus_surface(seat, xsurface->surface, false); 127 seat_set_focus_surface(seat, xsurface->surface, false);
137} 128}
138 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
139static void unmanaged_handle_destroy(struct wl_listener *listener, void *data) { 147static void unmanaged_handle_destroy(struct wl_listener *listener, void *data) {
140 struct sway_xwayland_unmanaged *surface = 148 struct sway_xwayland_unmanaged *surface =
141 wl_container_of(listener, surface, destroy); 149 wl_container_of(listener, surface, destroy);
142 wl_list_remove(&surface->request_configure.link); 150 wl_list_remove(&surface->request_configure.link);
143 wl_list_remove(&surface->map.link); 151 wl_list_remove(&surface->associate.link);
144 wl_list_remove(&surface->unmap.link); 152 wl_list_remove(&surface->dissociate.link);
145 wl_list_remove(&surface->destroy.link); 153 wl_list_remove(&surface->destroy.link);
146 wl_list_remove(&surface->override_redirect.link); 154 wl_list_remove(&surface->override_redirect.link);
147 wl_list_remove(&surface->request_activate.link); 155 wl_list_remove(&surface->request_activate.link);
@@ -149,6 +157,7 @@ static void unmanaged_handle_destroy(struct wl_listener *listener, void *data) {
149} 157}
150 158
151static 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);
152 161
153struct sway_xwayland_view *create_xwayland_view(struct wlr_xwayland_surface *xsurface); 162struct sway_xwayland_view *create_xwayland_view(struct wlr_xwayland_surface *xsurface);
154 163
@@ -157,14 +166,22 @@ static void unmanaged_handle_override_redirect(struct wl_listener *listener, voi
157 wl_container_of(listener, surface, override_redirect); 166 wl_container_of(listener, surface, override_redirect);
158 struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface; 167 struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface;
159 168
160 bool mapped = xsurface->mapped; 169 bool associated = xsurface->surface != NULL;
170 bool mapped = associated && xsurface->surface->mapped;
161 if (mapped) { 171 if (mapped) {
162 unmanaged_handle_unmap(&surface->unmap, NULL); 172 unmanaged_handle_unmap(&surface->unmap, NULL);
163 } 173 }
174 if (associated) {
175 unmanaged_handle_dissociate(&surface->dissociate, NULL);
176 }
164 177
165 unmanaged_handle_destroy(&surface->destroy, NULL); 178 unmanaged_handle_destroy(&surface->destroy, NULL);
166 xsurface->data = NULL; 179 xsurface->data = NULL;
180
167 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 }
168 if (mapped) { 185 if (mapped) {
169 handle_map(&xwayland_view->map, xsurface); 186 handle_map(&xwayland_view->map, xsurface);
170 } 187 }
@@ -184,10 +201,10 @@ static struct sway_xwayland_unmanaged *create_unmanaged(
184 wl_signal_add(&xsurface->events.request_configure, 201 wl_signal_add(&xsurface->events.request_configure,
185 &surface->request_configure); 202 &surface->request_configure);
186 surface->request_configure.notify = unmanaged_handle_request_configure; 203 surface->request_configure.notify = unmanaged_handle_request_configure;
187 wl_signal_add(&xsurface->events.map, &surface->map); 204 wl_signal_add(&xsurface->events.associate, &surface->associate);
188 surface->map.notify = unmanaged_handle_map; 205 surface->associate.notify = unmanaged_handle_associate;
189 wl_signal_add(&xsurface->events.unmap, &surface->unmap); 206 wl_signal_add(&xsurface->events.dissociate, &surface->dissociate);
190 surface->unmap.notify = unmanaged_handle_unmap; 207 surface->dissociate.notify = unmanaged_handle_dissociate;
191 wl_signal_add(&xsurface->events.destroy, &surface->destroy); 208 wl_signal_add(&xsurface->events.destroy, &surface->destroy);
192 surface->destroy.notify = unmanaged_handle_destroy; 209 surface->destroy.notify = unmanaged_handle_destroy;
193 wl_signal_add(&xsurface->events.set_override_redirect, &surface->override_redirect); 210 wl_signal_add(&xsurface->events.set_override_redirect, &surface->override_redirect);
@@ -396,17 +413,6 @@ static const struct sway_view_impl view_impl = {
396 .destroy = destroy, 413 .destroy = destroy,
397}; 414};
398 415
399static void get_geometry(struct sway_view *view, struct wlr_box *box) {
400 box->x = box->y = 0;
401 if (view->surface) {
402 box->width = view->surface->current.width;
403 box->height = view->surface->current.height;
404 } else {
405 box->width = 0;
406 box->height = 0;
407 }
408}
409
410static void handle_commit(struct wl_listener *listener, void *data) { 416static void handle_commit(struct wl_listener *listener, void *data) {
411 struct sway_xwayland_view *xwayland_view = 417 struct sway_xwayland_view *xwayland_view =
412 wl_container_of(listener, xwayland_view, commit); 418 wl_container_of(listener, xwayland_view, commit);
@@ -414,34 +420,38 @@ static void handle_commit(struct wl_listener *listener, void *data) {
414 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; 420 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface;
415 struct wlr_surface_state *state = &xsurface->surface->current; 421 struct wlr_surface_state *state = &xsurface->surface->current;
416 422
417 struct wlr_box new_geo; 423 struct wlr_box new_geo = {0};
418 get_geometry(view, &new_geo); 424 new_geo.width = state->width;
425 new_geo.height = state->height;
426
419 bool new_size = new_geo.width != view->geometry.width || 427 bool new_size = new_geo.width != view->geometry.width ||
420 new_geo.height != view->geometry.height || 428 new_geo.height != view->geometry.height;
421 new_geo.x != view->geometry.x ||
422 new_geo.y != view->geometry.y;
423 429
424 if (new_size) { 430 if (new_size) {
425 // The client changed its surface size in this commit. For floating 431 // The client changed its surface size in this commit. For floating
426 // containers, we resize the container to match. For tiling containers, 432 // containers, we resize the container to match. For tiling containers,
427 // we only recenter the surface. 433 // we only recenter the surface.
428 desktop_damage_view(view);
429 memcpy(&view->geometry, &new_geo, sizeof(struct wlr_box)); 434 memcpy(&view->geometry, &new_geo, sizeof(struct wlr_box));
430 if (container_is_floating(view->container)) { 435 if (container_is_floating(view->container)) {
431 view_update_size(view); 436 view_update_size(view);
432 transaction_commit_dirty_client(); 437 transaction_commit_dirty_client();
433 } else {
434 view_center_surface(view);
435 } 438 }
436 desktop_damage_view(view); 439
440 view_center_and_clip_surface(view);
437 } 441 }
438 442
439 if (view->container->node.instruction) { 443 if (view->container->node.instruction) {
440 transaction_notify_view_ready_by_geometry(view, 444 bool successful = transaction_notify_view_ready_by_geometry(view,
441 xsurface->x, xsurface->y, state->width, state->height); 445 xsurface->x, xsurface->y, state->width, state->height);
442 }
443 446
444 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 }
445} 455}
446 456
447static void handle_destroy(struct wl_listener *listener, void *data) { 457static void handle_destroy(struct wl_listener *listener, void *data) {
@@ -466,11 +476,12 @@ static void handle_destroy(struct wl_listener *listener, void *data) {
466 wl_list_remove(&xwayland_view->set_title.link); 476 wl_list_remove(&xwayland_view->set_title.link);
467 wl_list_remove(&xwayland_view->set_class.link); 477 wl_list_remove(&xwayland_view->set_class.link);
468 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);
469 wl_list_remove(&xwayland_view->set_window_type.link); 480 wl_list_remove(&xwayland_view->set_window_type.link);
470 wl_list_remove(&xwayland_view->set_hints.link); 481 wl_list_remove(&xwayland_view->set_hints.link);
471 wl_list_remove(&xwayland_view->set_decorations.link); 482 wl_list_remove(&xwayland_view->set_decorations.link);
472 wl_list_remove(&xwayland_view->map.link); 483 wl_list_remove(&xwayland_view->associate.link);
473 wl_list_remove(&xwayland_view->unmap.link); 484 wl_list_remove(&xwayland_view->dissociate.link);
474 wl_list_remove(&xwayland_view->override_redirect.link); 485 wl_list_remove(&xwayland_view->override_redirect.link);
475 view_begin_destroy(&xwayland_view->view); 486 view_begin_destroy(&xwayland_view->view);
476} 487}
@@ -484,16 +495,28 @@ static void handle_unmap(struct wl_listener *listener, void *data) {
484 return; 495 return;
485 } 496 }
486 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
487 view_unmap(view); 506 view_unmap(view);
507}
488 508
489 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;
490} 513}
491 514
492static void handle_map(struct wl_listener *listener, void *data) { 515static void handle_map(struct wl_listener *listener, void *data) {
493 struct sway_xwayland_view *xwayland_view = 516 struct sway_xwayland_view *xwayland_view =
494 wl_container_of(listener, xwayland_view, map); 517 wl_container_of(listener, xwayland_view, map);
495 struct wlr_xwayland_surface *xsurface = data;
496 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;
497 520
498 view->natural_width = xsurface->width; 521 view->natural_width = xsurface->width;
499 view->natural_height = xsurface->height; 522 view->natural_height = xsurface->height;
@@ -506,23 +529,42 @@ static void handle_map(struct wl_listener *listener, void *data) {
506 // Put it back into the tree 529 // Put it back into the tree
507 view_map(view, xsurface->surface, xsurface->fullscreen, NULL, false); 530 view_map(view, xsurface->surface, xsurface->fullscreen, NULL, false);
508 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
509 transaction_commit_dirty(); 541 transaction_commit_dirty();
510} 542}
511 543
544static void handle_dissociate(struct wl_listener *listener, void *data);
545
512static void handle_override_redirect(struct wl_listener *listener, void *data) { 546static void handle_override_redirect(struct wl_listener *listener, void *data) {
513 struct sway_xwayland_view *xwayland_view = 547 struct sway_xwayland_view *xwayland_view =
514 wl_container_of(listener, xwayland_view, override_redirect); 548 wl_container_of(listener, xwayland_view, override_redirect);
515 struct wlr_xwayland_surface *xsurface = data;
516 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;
517 551
518 bool mapped = xsurface->mapped; 552 bool associated = xsurface->surface != NULL;
553 bool mapped = associated && xsurface->surface->mapped;
519 if (mapped) { 554 if (mapped) {
520 handle_unmap(&xwayland_view->unmap, NULL); 555 handle_unmap(&xwayland_view->unmap, NULL);
521 } 556 }
557 if (associated) {
558 handle_dissociate(&xwayland_view->dissociate, NULL);
559 }
522 560
523 handle_destroy(&xwayland_view->destroy, view); 561 handle_destroy(&xwayland_view->destroy, view);
524 xsurface->data = NULL; 562 xsurface->data = NULL;
563
525 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 }
526 if (mapped) { 568 if (mapped) {
527 unmanaged_handle_map(&unmanaged->map, xsurface); 569 unmanaged_handle_map(&unmanaged->map, xsurface);
528 } 570 }
@@ -534,7 +576,7 @@ static void handle_request_configure(struct wl_listener *listener, void *data) {
534 struct wlr_xwayland_surface_configure_event *ev = data; 576 struct wlr_xwayland_surface_configure_event *ev = data;
535 struct sway_view *view = &xwayland_view->view; 577 struct sway_view *view = &xwayland_view->view;
536 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; 578 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface;
537 if (!xsurface->mapped) { 579 if (xsurface->surface == NULL || !xsurface->surface->mapped) {
538 wlr_xwayland_surface_configure(xsurface, ev->x, ev->y, 580 wlr_xwayland_surface_configure(xsurface, ev->x, ev->y,
539 ev->width, ev->height); 581 ev->width, ev->height);
540 return; 582 return;
@@ -563,7 +605,7 @@ static void handle_request_fullscreen(struct wl_listener *listener, void *data)
563 wl_container_of(listener, xwayland_view, request_fullscreen); 605 wl_container_of(listener, xwayland_view, request_fullscreen);
564 struct sway_view *view = &xwayland_view->view; 606 struct sway_view *view = &xwayland_view->view;
565 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; 607 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface;
566 if (!xsurface->mapped) { 608 if (xsurface->surface == NULL || !xsurface->surface->mapped) {
567 return; 609 return;
568 } 610 }
569 container_set_fullscreen(view->container, xsurface->fullscreen); 611 container_set_fullscreen(view->container, xsurface->fullscreen);
@@ -577,7 +619,7 @@ static void handle_request_minimize(struct wl_listener *listener, void *data) {
577 wl_container_of(listener, xwayland_view, request_minimize); 619 wl_container_of(listener, xwayland_view, request_minimize);
578 struct sway_view *view = &xwayland_view->view; 620 struct sway_view *view = &xwayland_view->view;
579 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; 621 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface;
580 if (!xsurface->mapped) { 622 if (xsurface->surface == NULL || !xsurface->surface->mapped) {
581 return; 623 return;
582 } 624 }
583 625
@@ -592,7 +634,7 @@ static void handle_request_move(struct wl_listener *listener, void *data) {
592 wl_container_of(listener, xwayland_view, request_move); 634 wl_container_of(listener, xwayland_view, request_move);
593 struct sway_view *view = &xwayland_view->view; 635 struct sway_view *view = &xwayland_view->view;
594 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; 636 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface;
595 if (!xsurface->mapped) { 637 if (xsurface->surface == NULL || !xsurface->surface->mapped) {
596 return; 638 return;
597 } 639 }
598 if (!container_is_floating(view->container) || 640 if (!container_is_floating(view->container) ||
@@ -608,7 +650,7 @@ static void handle_request_resize(struct wl_listener *listener, void *data) {
608 wl_container_of(listener, xwayland_view, request_resize); 650 wl_container_of(listener, xwayland_view, request_resize);
609 struct sway_view *view = &xwayland_view->view; 651 struct sway_view *view = &xwayland_view->view;
610 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; 652 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface;
611 if (!xsurface->mapped) { 653 if (xsurface->surface == NULL || !xsurface->surface->mapped) {
612 return; 654 return;
613 } 655 }
614 if (!container_is_floating(view->container)) { 656 if (!container_is_floating(view->container)) {
@@ -624,10 +666,10 @@ static void handle_request_activate(struct wl_listener *listener, void *data) {
624 wl_container_of(listener, xwayland_view, request_activate); 666 wl_container_of(listener, xwayland_view, request_activate);
625 struct sway_view *view = &xwayland_view->view; 667 struct sway_view *view = &xwayland_view->view;
626 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; 668 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface;
627 if (!xsurface->mapped) { 669 if (xsurface->surface == NULL || !xsurface->surface->mapped) {
628 return; 670 return;
629 } 671 }
630 view_request_activate(view); 672 view_request_activate(view, NULL);
631 673
632 transaction_commit_dirty(); 674 transaction_commit_dirty();
633} 675}
@@ -637,7 +679,7 @@ static void handle_set_title(struct wl_listener *listener, void *data) {
637 wl_container_of(listener, xwayland_view, set_title); 679 wl_container_of(listener, xwayland_view, set_title);
638 struct sway_view *view = &xwayland_view->view; 680 struct sway_view *view = &xwayland_view->view;
639 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; 681 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface;
640 if (!xsurface->mapped) { 682 if (xsurface->surface == NULL || !xsurface->surface->mapped) {
641 return; 683 return;
642 } 684 }
643 view_update_title(view, false); 685 view_update_title(view, false);
@@ -649,7 +691,7 @@ static void handle_set_class(struct wl_listener *listener, void *data) {
649 wl_container_of(listener, xwayland_view, set_class); 691 wl_container_of(listener, xwayland_view, set_class);
650 struct sway_view *view = &xwayland_view->view; 692 struct sway_view *view = &xwayland_view->view;
651 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; 693 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface;
652 if (!xsurface->mapped) { 694 if (xsurface->surface == NULL || !xsurface->surface->mapped) {
653 return; 695 return;
654 } 696 }
655 view_execute_criteria(view); 697 view_execute_criteria(view);
@@ -660,18 +702,43 @@ static void handle_set_role(struct wl_listener *listener, void *data) {
660 wl_container_of(listener, xwayland_view, set_role); 702 wl_container_of(listener, xwayland_view, set_role);
661 struct sway_view *view = &xwayland_view->view; 703 struct sway_view *view = &xwayland_view->view;
662 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; 704 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface;
663 if (!xsurface->mapped) { 705 if (xsurface->surface == NULL || !xsurface->surface->mapped) {
664 return; 706 return;
665 } 707 }
666 view_execute_criteria(view); 708 view_execute_criteria(view);
667} 709}
668 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
669static void handle_set_window_type(struct wl_listener *listener, void *data) { 736static void handle_set_window_type(struct wl_listener *listener, void *data) {
670 struct sway_xwayland_view *xwayland_view = 737 struct sway_xwayland_view *xwayland_view =
671 wl_container_of(listener, xwayland_view, set_window_type); 738 wl_container_of(listener, xwayland_view, set_window_type);
672 struct sway_view *view = &xwayland_view->view; 739 struct sway_view *view = &xwayland_view->view;
673 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; 740 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface;
674 if (!xsurface->mapped) { 741 if (xsurface->surface == NULL || !xsurface->surface->mapped) {
675 return; 742 return;
676 } 743 }
677 view_execute_criteria(view); 744 view_execute_criteria(view);
@@ -682,7 +749,7 @@ static void handle_set_hints(struct wl_listener *listener, void *data) {
682 wl_container_of(listener, xwayland_view, set_hints); 749 wl_container_of(listener, xwayland_view, set_hints);
683 struct sway_view *view = &xwayland_view->view; 750 struct sway_view *view = &xwayland_view->view;
684 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; 751 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface;
685 if (!xsurface->mapped) { 752 if (xsurface->surface == NULL || !xsurface->surface->mapped) {
686 return; 753 return;
687 } 754 }
688 const bool hints_urgency = xcb_icccm_wm_hints_get_urgency(xsurface->hints); 755 const bool hints_urgency = xcb_icccm_wm_hints_get_urgency(xsurface->hints);
@@ -697,6 +764,24 @@ static void handle_set_hints(struct wl_listener *listener, void *data) {
697 } 764 }
698} 765}
699 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
700struct sway_view *view_from_wlr_xwayland_surface( 785struct sway_view *view_from_wlr_xwayland_surface(
701 struct wlr_xwayland_surface *xsurface) { 786 struct wlr_xwayland_surface *xsurface) {
702 return xsurface->data; 787 return xsurface->data;
@@ -712,7 +797,10 @@ struct sway_xwayland_view *create_xwayland_view(struct wlr_xwayland_surface *xsu
712 return NULL; 797 return NULL;
713 } 798 }
714 799
715 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 }
716 xwayland_view->view.wlr_xwayland_surface = xsurface; 804 xwayland_view->view.wlr_xwayland_surface = xsurface;
717 805
718 wl_signal_add(&xsurface->events.destroy, &xwayland_view->destroy); 806 wl_signal_add(&xsurface->events.destroy, &xwayland_view->destroy);
@@ -751,6 +839,10 @@ struct sway_xwayland_view *create_xwayland_view(struct wlr_xwayland_surface *xsu
751 wl_signal_add(&xsurface->events.set_role, &xwayland_view->set_role); 839 wl_signal_add(&xsurface->events.set_role, &xwayland_view->set_role);
752 xwayland_view->set_role.notify = handle_set_role; 840 xwayland_view->set_role.notify = handle_set_role;
753 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
754 wl_signal_add(&xsurface->events.set_window_type, 846 wl_signal_add(&xsurface->events.set_window_type,
755 &xwayland_view->set_window_type); 847 &xwayland_view->set_window_type);
756 xwayland_view->set_window_type.notify = handle_set_window_type; 848 xwayland_view->set_window_type.notify = handle_set_window_type;
@@ -762,11 +854,11 @@ struct sway_xwayland_view *create_xwayland_view(struct wlr_xwayland_surface *xsu
762 &xwayland_view->set_decorations); 854 &xwayland_view->set_decorations);
763 xwayland_view->set_decorations.notify = handle_set_decorations; 855 xwayland_view->set_decorations.notify = handle_set_decorations;
764 856
765 wl_signal_add(&xsurface->events.unmap, &xwayland_view->unmap); 857 wl_signal_add(&xsurface->events.associate, &xwayland_view->associate);
766 xwayland_view->unmap.notify = handle_unmap; 858 xwayland_view->associate.notify = handle_associate;
767 859
768 wl_signal_add(&xsurface->events.map, &xwayland_view->map); 860 wl_signal_add(&xsurface->events.dissociate, &xwayland_view->dissociate);
769 xwayland_view->map.notify = handle_map; 861 xwayland_view->dissociate.notify = handle_dissociate;
770 862
771 wl_signal_add(&xsurface->events.set_override_redirect, 863 wl_signal_add(&xsurface->events.set_override_redirect,
772 &xwayland_view->override_redirect); 864 &xwayland_view->override_redirect);
diff --git a/sway/input/cursor.c b/sway/input/cursor.c
index 0b2f03a2..235951d4 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,13 @@
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/server.h"
28#include "sway/tree/container.h" 29#include "sway/tree/container.h"
29#include "sway/tree/root.h" 30#include "sway/tree/root.h"
30#include "sway/tree/view.h" 31#include "sway/tree/view.h"
@@ -37,45 +38,6 @@ static uint32_t get_current_time_msec(void) {
37 return now.tv_sec * 1000 + now.tv_nsec / 1000000; 38 return now.tv_sec * 1000 + now.tv_nsec / 1000000;
38} 39}
39 40
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/** 41/**
80 * Returns the node at the cursor's position. If there is a surface at that 42 * 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). 43 * location, it is stored in **surface (it may not be a view).
@@ -83,116 +45,98 @@ static struct wlr_surface *layer_surface_popup_at(struct sway_output *output,
83struct sway_node *node_at_coords( 45struct sway_node *node_at_coords(
84 struct sway_seat *seat, double lx, double ly, 46 struct sway_seat *seat, double lx, double ly,
85 struct wlr_surface **surface, double *sx, double *sy) { 47 struct wlr_surface **surface, double *sx, double *sy) {
86 // find the output the cursor is on 48 struct wlr_scene_node *scene_node = NULL;
87 struct wlr_output *wlr_output = wlr_output_layout_output_at(
88 root->output_layout, lx, ly);
89 if (wlr_output == NULL) {
90 return NULL;
91 }
92 struct sway_output *output = wlr_output->data;
93 if (!output || !output->enabled) {
94 // output is being destroyed or is being enabled
95 return NULL;
96 }
97 double ox = lx, oy = ly;
98 wlr_output_layout_output_coords(root->output_layout, wlr_output, &ox, &oy);
99 49
100 // layer surfaces on the overlay layer are rendered on top 50 struct wlr_scene_node *node;
101 if ((*surface = layer_surface_at(output, 51 wl_list_for_each_reverse(node, &root->layer_tree->children, link) {
102 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], 52 struct wlr_scene_tree *layer = wlr_scene_tree_from_node(node);
103 ox, oy, sx, sy))) {
104 return NULL;
105 }
106 53
107 // check for unmanaged views 54 bool non_interactive = scene_descriptor_try_get(&layer->node,
108#if HAVE_XWAYLAND 55 SWAY_SCENE_DESC_NON_INTERACTIVE);
109 struct wl_list *unmanaged = &root->xwayland_unmanaged; 56 if (non_interactive) {
110 struct sway_xwayland_unmanaged *unmanaged_surface; 57 continue;
111 wl_list_for_each_reverse(unmanaged_surface, unmanaged, link) {
112 struct wlr_xwayland_surface *xsurface =
113 unmanaged_surface->wlr_xwayland_surface;
114
115 double _sx = lx - unmanaged_surface->lx;
116 double _sy = ly - unmanaged_surface->ly;
117 if (wlr_surface_point_accepts_input(xsurface->surface, _sx, _sy)) {
118 *surface = xsurface->surface;
119 *sx = _sx;
120 *sy = _sy;
121 return NULL;
122 } 58 }
123 }
124#endif
125 59
126 if (root->fullscreen_global) { 60 scene_node = wlr_scene_node_at(&layer->node, lx, ly, sx, sy);
127 // Try fullscreen container 61 if (scene_node) {
128 struct sway_container *con = tiling_container_at( 62 break;
129 &root->fullscreen_global->node, lx, ly, surface, sx, sy);
130 if (con) {
131 return &con->node;
132 } 63 }
133 return NULL;
134 } 64 }
135 65
136 // find the focused workspace on the output for this seat 66 if (scene_node) {
137 struct sway_workspace *ws = output_get_active_workspace(output); 67 // determine what wlr_surface we clicked on
138 if (!ws) { 68 if (scene_node->type == WLR_SCENE_NODE_BUFFER) {
139 return NULL; 69 struct wlr_scene_buffer *scene_buffer =
140 } 70 wlr_scene_buffer_from_node(scene_node);
71 struct wlr_scene_surface *scene_surface =
72 wlr_scene_surface_try_from_buffer(scene_buffer);
141 73
142 if (ws->fullscreen) { 74 if (scene_surface) {
143 // Try transient containers 75 *surface = scene_surface->surface;
144 for (int i = 0; i < ws->floating->length; ++i) {
145 struct sway_container *floater = ws->floating->items[i];
146 if (container_is_transient_for(floater, ws->fullscreen)) {
147 struct sway_container *con = tiling_container_at(
148 &floater->node, lx, ly, surface, sx, sy);
149 if (con) {
150 return &con->node;
151 }
152 } 76 }
153 } 77 }
154 // Try fullscreen container 78
155 struct sway_container *con = 79 // determine what container we clicked on
156 tiling_container_at(&ws->fullscreen->node, lx, ly, surface, sx, sy); 80 struct wlr_scene_node *current = scene_node;
157 if (con) { 81 while (true) {
158 return &con->node; 82 struct sway_container *con = scene_descriptor_try_get(current,
83 SWAY_SCENE_DESC_CONTAINER);
84
85 if (!con) {
86 struct sway_view *view = scene_descriptor_try_get(current,
87 SWAY_SCENE_DESC_VIEW);
88 if (view) {
89 con = view->container;
90 }
91 }
92
93 if (!con) {
94 struct sway_popup_desc *popup =
95 scene_descriptor_try_get(current, SWAY_SCENE_DESC_POPUP);
96 if (popup && popup->view) {
97 con = popup->view->container;
98 }
99 }
100
101 if (con && (!con->view || con->view->surface)) {
102 return &con->node;
103 }
104
105 if (scene_descriptor_try_get(current, SWAY_SCENE_DESC_LAYER_SHELL)) {
106 // We don't want to feed through the current workspace on
107 // layer shells
108 return NULL;
109 }
110
111#if WLR_HAS_XWAYLAND
112 if (scene_descriptor_try_get(current, SWAY_SCENE_DESC_XWAYLAND_UNMANAGED)) {
113 return NULL;
114 }
115#endif
116
117 if (!current->parent) {
118 break;
119 }
120
121 current = &current->parent->node;
159 } 122 }
160 return NULL;
161 }
162 if ((*surface = layer_surface_popup_at(output,
163 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP],
164 ox, oy, sx, sy))) {
165 return NULL;
166 }
167 if ((*surface = layer_surface_popup_at(output,
168 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM],
169 ox, oy, sx, sy))) {
170 return NULL;
171 }
172 if ((*surface = layer_surface_popup_at(output,
173 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND],
174 ox, oy, sx, sy))) {
175 return NULL;
176 }
177 if ((*surface = layer_surface_at(output,
178 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP],
179 ox, oy, sx, sy))) {
180 return NULL;
181 } 123 }
182 124
183 struct sway_container *c; 125 // if we aren't on a container, determine what workspace we are on
184 if ((c = container_at(ws, lx, ly, surface, sx, sy))) { 126 struct wlr_output *wlr_output = wlr_output_layout_output_at(
185 return &c->node; 127 root->output_layout, lx, ly);
128 if (wlr_output == NULL) {
129 return NULL;
186 } 130 }
187 131
188 if ((*surface = layer_surface_at(output, 132 struct sway_output *output = wlr_output->data;
189 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM], 133 if (!output || !output->enabled) {
190 ox, oy, sx, sy))) { 134 // output is being destroyed or is being enabled
191 return NULL; 135 return NULL;
192 } 136 }
193 if ((*surface = layer_surface_at(output, 137
194 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND], 138 struct sway_workspace *ws = output_get_active_workspace(output);
195 ox, oy, sx, sy))) { 139 if (!ws) {
196 return NULL; 140 return NULL;
197 } 141 }
198 142
@@ -221,7 +165,7 @@ void cursor_update_image(struct sway_cursor *cursor,
221 // Try a node's resize edge 165 // Try a node's resize edge
222 enum wlr_edges edge = find_resize_edge(node->sway_container, NULL, cursor); 166 enum wlr_edges edge = find_resize_edge(node->sway_container, NULL, cursor);
223 if (edge == WLR_EDGE_NONE) { 167 if (edge == WLR_EDGE_NONE) {
224 cursor_set_image(cursor, "left_ptr", NULL); 168 cursor_set_image(cursor, "default", NULL);
225 } else if (container_is_floating(node->sway_container)) { 169 } else if (container_is_floating(node->sway_container)) {
226 cursor_set_image(cursor, wlr_xcursor_get_resize_name(edge), NULL); 170 cursor_set_image(cursor, wlr_xcursor_get_resize_name(edge), NULL);
227 } else { 171 } else {
@@ -232,12 +176,12 @@ void cursor_update_image(struct sway_cursor *cursor,
232 } 176 }
233 } 177 }
234 } else { 178 } else {
235 cursor_set_image(cursor, "left_ptr", NULL); 179 cursor_set_image(cursor, "default", NULL);
236 } 180 }
237} 181}
238 182
239static void cursor_hide(struct sway_cursor *cursor) { 183static void cursor_hide(struct sway_cursor *cursor) {
240 wlr_cursor_set_image(cursor->cursor, NULL, 0, 0, 0, 0, 0, 0); 184 wlr_cursor_unset_image(cursor->cursor);
241 cursor->hidden = true; 185 cursor->hidden = true;
242 wlr_seat_pointer_notify_clear_focus(cursor->seat->wlr_seat); 186 wlr_seat_pointer_notify_clear_focus(cursor->seat->wlr_seat);
243} 187}
@@ -300,7 +244,7 @@ static enum sway_input_idle_source idle_source_from_device(
300 return IDLE_SOURCE_POINTER; 244 return IDLE_SOURCE_POINTER;
301 case WLR_INPUT_DEVICE_TOUCH: 245 case WLR_INPUT_DEVICE_TOUCH:
302 return IDLE_SOURCE_TOUCH; 246 return IDLE_SOURCE_TOUCH;
303 case WLR_INPUT_DEVICE_TABLET_TOOL: 247 case WLR_INPUT_DEVICE_TABLET:
304 return IDLE_SOURCE_TABLET_TOOL; 248 return IDLE_SOURCE_TABLET_TOOL;
305 case WLR_INPUT_DEVICE_TABLET_PAD: 249 case WLR_INPUT_DEVICE_TABLET_PAD:
306 return IDLE_SOURCE_TABLET_PAD; 250 return IDLE_SOURCE_TABLET_PAD;
@@ -349,7 +293,7 @@ void cursor_unhide(struct sway_cursor *cursor) {
349 wl_event_source_timer_update(cursor->hide_source, cursor_get_timeout(cursor)); 293 wl_event_source_timer_update(cursor->hide_source, cursor_get_timeout(cursor));
350} 294}
351 295
352static void pointer_motion(struct sway_cursor *cursor, uint32_t time_msec, 296void pointer_motion(struct sway_cursor *cursor, uint32_t time_msec,
353 struct wlr_input_device *device, double dx, double dy, 297 struct wlr_input_device *device, double dx, double dy,
354 double dx_unaccel, double dy_unaccel) { 298 double dx_unaccel, double dy_unaccel) {
355 wlr_relative_pointer_manager_v1_send_relative_motion( 299 wlr_relative_pointer_manager_v1_send_relative_motion(
@@ -413,7 +357,7 @@ static void handle_pointer_motion_absolute(
413 357
414void dispatch_cursor_button(struct sway_cursor *cursor, 358void dispatch_cursor_button(struct sway_cursor *cursor,
415 struct wlr_input_device *device, uint32_t time_msec, uint32_t button, 359 struct wlr_input_device *device, uint32_t time_msec, uint32_t button,
416 enum wlr_button_state state) { 360 enum wl_pointer_button_state state) {
417 if (time_msec == 0) { 361 if (time_msec == 0) {
418 time_msec = get_current_time_msec(); 362 time_msec = get_current_time_msec();
419 } 363 }
@@ -425,7 +369,7 @@ static void handle_pointer_button(struct wl_listener *listener, void *data) {
425 struct sway_cursor *cursor = wl_container_of(listener, cursor, button); 369 struct sway_cursor *cursor = wl_container_of(listener, cursor, button);
426 struct wlr_pointer_button_event *event = data; 370 struct wlr_pointer_button_event *event = data;
427 371
428 if (event->state == WLR_BUTTON_PRESSED) { 372 if (event->state == WL_POINTER_BUTTON_STATE_PRESSED) {
429 cursor->pressed_button_count++; 373 cursor->pressed_button_count++;
430 } else { 374 } else {
431 if (cursor->pressed_button_count > 0) { 375 if (cursor->pressed_button_count > 0) {
@@ -464,43 +408,16 @@ static void handle_touch_down(struct wl_listener *listener, void *data) {
464 cursor_hide(cursor); 408 cursor_hide(cursor);
465 409
466 struct sway_seat *seat = cursor->seat; 410 struct sway_seat *seat = cursor->seat;
467 struct wlr_seat *wlr_seat = seat->wlr_seat;
468 struct wlr_surface *surface = NULL;
469 411
470 double lx, ly; 412 double lx, ly;
471 wlr_cursor_absolute_to_layout_coords(cursor->cursor, &event->touch->base, 413 wlr_cursor_absolute_to_layout_coords(cursor->cursor, &event->touch->base,
472 event->x, event->y, &lx, &ly); 414 event->x, event->y, &lx, &ly);
473 double sx, sy;
474 struct sway_node *focused_node = node_at_coords(seat, lx, ly, &surface, &sx, &sy);
475 415
476 seat->touch_id = event->touch_id; 416 seat->touch_id = event->touch_id;
477 seat->touch_x = lx; 417 seat->touch_x = lx;
478 seat->touch_y = ly; 418 seat->touch_y = ly;
479 419
480 if (surface && wlr_surface_accepts_touch(wlr_seat, surface)) { 420 seatop_touch_down(seat, event, lx, ly);
481 if (seat_is_input_allowed(seat, surface)) {
482 wlr_seat_touch_notify_down(wlr_seat, surface, event->time_msec,
483 event->touch_id, sx, sy);
484
485 if (focused_node) {
486 seat_set_focus(seat, focused_node);
487 }
488 }
489 } else if (!cursor->simulating_pointer_from_touch &&
490 (!surface || seat_is_input_allowed(seat, surface))) {
491 // Fallback to cursor simulation.
492 // The pointer_touch_id state is needed, so drags are not aborted when over
493 // a surface supporting touch and multi touch events don't interfere.
494 cursor->simulating_pointer_from_touch = true;
495 cursor->pointer_touch_id = seat->touch_id;
496 double dx, dy;
497 dx = lx - cursor->cursor->x;
498 dy = ly - cursor->cursor->y;
499 pointer_motion(cursor, event->time_msec, &event->touch->base, dx, dy,
500 dx, dy);
501 dispatch_cursor_button(cursor, &event->touch->base, event->time_msec,
502 BTN_LEFT, WLR_BUTTON_PRESSED);
503 }
504} 421}
505 422
506static void handle_touch_up(struct wl_listener *listener, void *data) { 423static void handle_touch_up(struct wl_listener *listener, void *data) {
@@ -508,16 +425,34 @@ static void handle_touch_up(struct wl_listener *listener, void *data) {
508 struct wlr_touch_up_event *event = data; 425 struct wlr_touch_up_event *event = data;
509 cursor_handle_activity_from_device(cursor, &event->touch->base); 426 cursor_handle_activity_from_device(cursor, &event->touch->base);
510 427
511 struct wlr_seat *wlr_seat = cursor->seat->wlr_seat; 428 struct sway_seat *seat = cursor->seat;
429
430 if (cursor->simulating_pointer_from_touch) {
431 if (cursor->pointer_touch_id == cursor->seat->touch_id) {
432 cursor->pointer_touch_up = true;
433 dispatch_cursor_button(cursor, &event->touch->base,
434 event->time_msec, BTN_LEFT, WL_POINTER_BUTTON_STATE_RELEASED);
435 }
436 } else {
437 seatop_touch_up(seat, event);
438 }
439}
440
441static void handle_touch_cancel(struct wl_listener *listener, void *data) {
442 struct sway_cursor *cursor = wl_container_of(listener, cursor, touch_cancel);
443 struct wlr_touch_cancel_event *event = data;
444 cursor_handle_activity_from_device(cursor, &event->touch->base);
445
446 struct sway_seat *seat = cursor->seat;
512 447
513 if (cursor->simulating_pointer_from_touch) { 448 if (cursor->simulating_pointer_from_touch) {
514 if (cursor->pointer_touch_id == cursor->seat->touch_id) { 449 if (cursor->pointer_touch_id == cursor->seat->touch_id) {
515 cursor->pointer_touch_up = true; 450 cursor->pointer_touch_up = true;
516 dispatch_cursor_button(cursor, &event->touch->base, 451 dispatch_cursor_button(cursor, &event->touch->base,
517 event->time_msec, BTN_LEFT, WLR_BUTTON_RELEASED); 452 event->time_msec, BTN_LEFT, WL_POINTER_BUTTON_STATE_RELEASED);
518 } 453 }
519 } else { 454 } else {
520 wlr_seat_touch_notify_up(wlr_seat, event->time_msec, event->touch_id); 455 seatop_touch_cancel(seat, event);
521 } 456 }
522} 457}
523 458
@@ -528,25 +463,16 @@ static void handle_touch_motion(struct wl_listener *listener, void *data) {
528 cursor_handle_activity_from_device(cursor, &event->touch->base); 463 cursor_handle_activity_from_device(cursor, &event->touch->base);
529 464
530 struct sway_seat *seat = cursor->seat; 465 struct sway_seat *seat = cursor->seat;
531 struct wlr_seat *wlr_seat = seat->wlr_seat;
532 struct wlr_surface *surface = NULL;
533 466
534 double lx, ly; 467 double lx, ly;
535 wlr_cursor_absolute_to_layout_coords(cursor->cursor, &event->touch->base, 468 wlr_cursor_absolute_to_layout_coords(cursor->cursor, &event->touch->base,
536 event->x, event->y, &lx, &ly); 469 event->x, event->y, &lx, &ly);
537 double sx, sy;
538 node_at_coords(cursor->seat, lx, ly, &surface, &sx, &sy);
539 470
540 if (seat->touch_id == event->touch_id) { 471 if (seat->touch_id == event->touch_id) {
541 seat->touch_x = lx; 472 seat->touch_x = lx;
542 seat->touch_y = ly; 473 seat->touch_y = ly;
543 474
544 struct sway_drag_icon *drag_icon; 475 drag_icons_update_position(seat);
545 wl_list_for_each(drag_icon, &root->drag_icons, link) {
546 if (drag_icon->seat == seat) {
547 drag_icon_update_position(drag_icon);
548 }
549 }
550 } 476 }
551 477
552 if (cursor->simulating_pointer_from_touch) { 478 if (cursor->simulating_pointer_from_touch) {
@@ -557,9 +483,8 @@ static void handle_touch_motion(struct wl_listener *listener, void *data) {
557 pointer_motion(cursor, event->time_msec, &event->touch->base, 483 pointer_motion(cursor, event->time_msec, &event->touch->base,
558 dx, dy, dx, dy); 484 dx, dy, dx, dy);
559 } 485 }
560 } else if (surface) { 486 } else {
561 wlr_seat_touch_notify_motion(wlr_seat, event->time_msec, 487 seatop_touch_motion(seat, event, lx, ly);
562 event->touch_id, sx, sy);
563 } 488 }
564} 489}
565 490
@@ -594,8 +519,8 @@ static void apply_mapping_from_region(struct wlr_input_device *device,
594 double x1 = region->x1, x2 = region->x2; 519 double x1 = region->x1, x2 = region->x2;
595 double y1 = region->y1, y2 = region->y2; 520 double y1 = region->y1, y2 = region->y2;
596 521
597 if (region->mm && device->type == WLR_INPUT_DEVICE_TABLET_TOOL) { 522 if (region->mm && device->type == WLR_INPUT_DEVICE_TABLET) {
598 struct wlr_tablet *tablet = device->tablet; 523 struct wlr_tablet *tablet = wlr_tablet_from_input_device(device);
599 if (tablet->width_mm == 0 || tablet->height_mm == 0) { 524 if (tablet->width_mm == 0 || tablet->height_mm == 0) {
600 return; 525 return;
601 } 526 }
@@ -737,7 +662,7 @@ static void handle_tool_tip(struct wl_listener *listener, void *data) {
737 event->state == WLR_TABLET_TOOL_TIP_UP) { 662 event->state == WLR_TABLET_TOOL_TIP_UP) {
738 cursor->simulating_pointer_from_tool_tip = false; 663 cursor->simulating_pointer_from_tool_tip = false;
739 dispatch_cursor_button(cursor, &event->tablet->base, event->time_msec, 664 dispatch_cursor_button(cursor, &event->tablet->base, event->time_msec,
740 BTN_LEFT, WLR_BUTTON_RELEASED); 665 BTN_LEFT, WL_POINTER_BUTTON_STATE_RELEASED);
741 wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat); 666 wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat);
742 } else if (!surface || !wlr_surface_accepts_tablet_v2(tablet_v2, surface)) { 667 } else if (!surface || !wlr_surface_accepts_tablet_v2(tablet_v2, surface)) {
743 // If we started holding the tool tip down on a surface that accepts 668 // If we started holding the tool tip down on a surface that accepts
@@ -749,7 +674,7 @@ static void handle_tool_tip(struct wl_listener *listener, void *data) {
749 } else { 674 } else {
750 cursor->simulating_pointer_from_tool_tip = true; 675 cursor->simulating_pointer_from_tool_tip = true;
751 dispatch_cursor_button(cursor, &event->tablet->base, 676 dispatch_cursor_button(cursor, &event->tablet->base,
752 event->time_msec, BTN_LEFT, WLR_BUTTON_PRESSED); 677 event->time_msec, BTN_LEFT, WL_POINTER_BUTTON_STATE_PRESSED);
753 wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat); 678 wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat);
754 } 679 }
755 } else { 680 } else {
@@ -818,31 +743,71 @@ static void handle_tool_button(struct wl_listener *listener, void *data) {
818 node_at_coords(cursor->seat, cursor->cursor->x, cursor->cursor->y, 743 node_at_coords(cursor->seat, cursor->cursor->x, cursor->cursor->y,
819 &surface, &sx, &sy); 744 &surface, &sx, &sy);
820 745
821 if (!surface || !wlr_surface_accepts_tablet_v2(tablet_v2, surface)) { 746 // TODO: floating resize should support graphics tablet events
747 struct wlr_keyboard *keyboard = wlr_seat_get_keyboard(cursor->seat->wlr_seat);
748 uint32_t modifiers = keyboard ? wlr_keyboard_get_modifiers(keyboard) : 0;
749 bool mod_pressed = modifiers & config->floating_mod;
750
751 bool surface_supports_tablet_events =
752 surface && wlr_surface_accepts_tablet_v2(tablet_v2, surface);
753
754 // Simulate pointer when:
755 // 1. The modifier key is pressed, OR
756 // 2. The surface under the cursor does not support tablet events.
757 bool should_simulate_pointer = mod_pressed || !surface_supports_tablet_events;
758
759 // Similar to tool tip, we need to selectively simulate mouse events, but we
760 // want to make sure that it is always consistent. Because all tool buttons
761 // currently map to BTN_RIGHT, we need to keep count of how many tool
762 // buttons are currently pressed down so we can send consistent events.
763 //
764 // The logic follows:
765 // - If we are already simulating the pointer, we should continue to do so
766 // until at least no tool button is held down.
767 // - If we should simulate the pointer and no tool button is currently held
768 // down, begin simulating the pointer.
769 // - If neither of the above are true, send the tablet events.
770 if ((cursor->tool_buttons > 0 && cursor->simulating_pointer_from_tool_button)
771 || (cursor->tool_buttons == 0 && should_simulate_pointer)) {
772 cursor->simulating_pointer_from_tool_button = true;
773
822 // TODO: the user may want to configure which tool buttons are mapped to 774 // TODO: the user may want to configure which tool buttons are mapped to
823 // which simulated pointer buttons 775 // which simulated pointer buttons
824 switch (event->state) { 776 switch (event->state) {
825 case WLR_BUTTON_PRESSED: 777 case WLR_BUTTON_PRESSED:
826 if (cursor->tool_buttons == 0) { 778 if (cursor->tool_buttons == 0) {
827 dispatch_cursor_button(cursor, &event->tablet->base, 779 dispatch_cursor_button(cursor, &event->tablet->base,
828 event->time_msec, BTN_RIGHT, event->state); 780 event->time_msec, BTN_RIGHT, WL_POINTER_BUTTON_STATE_PRESSED);
829 } 781 }
830 cursor->tool_buttons++;
831 break; 782 break;
832 case WLR_BUTTON_RELEASED: 783 case WLR_BUTTON_RELEASED:
833 if (cursor->tool_buttons == 1) { 784 if (cursor->tool_buttons <= 1) {
834 dispatch_cursor_button(cursor, &event->tablet->base, 785 dispatch_cursor_button(cursor, &event->tablet->base,
835 event->time_msec, BTN_RIGHT, event->state); 786 event->time_msec, BTN_RIGHT, WL_POINTER_BUTTON_STATE_RELEASED);
836 } 787 }
837 cursor->tool_buttons--;
838 break; 788 break;
839 } 789 }
840 wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat); 790 wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat);
841 return; 791 } else {
792 cursor->simulating_pointer_from_tool_button = false;
793
794 wlr_tablet_v2_tablet_tool_notify_button(sway_tool->tablet_v2_tool,
795 event->button, (enum zwp_tablet_pad_v2_button_state)event->state);
842 } 796 }
843 797
844 wlr_tablet_v2_tablet_tool_notify_button(sway_tool->tablet_v2_tool, 798 // Update tool button count.
845 event->button, (enum zwp_tablet_pad_v2_button_state)event->state); 799 switch (event->state) {
800 case WLR_BUTTON_PRESSED:
801 cursor->tool_buttons++;
802 break;
803 case WLR_BUTTON_RELEASED:
804 if (cursor->tool_buttons == 0) {
805 sway_log(SWAY_ERROR, "inconsistent tablet tool button events");
806 } else {
807 cursor->tool_buttons--;
808 }
809 break;
810 }
846} 811}
847 812
848static void check_constraint_region(struct sway_cursor *cursor) { 813static void check_constraint_region(struct sway_cursor *cursor) {
@@ -928,14 +893,28 @@ static void handle_request_pointer_set_cursor(struct wl_listener *listener,
928 event->hotspot_y, focused_client); 893 event->hotspot_y, focused_client);
929} 894}
930 895
896static void handle_pointer_hold_begin(struct wl_listener *listener, void *data) {
897 struct sway_cursor *cursor = wl_container_of(
898 listener, cursor, hold_begin);
899 struct wlr_pointer_hold_begin_event *event = data;
900 cursor_handle_activity_from_device(cursor, &event->pointer->base);
901 seatop_hold_begin(cursor->seat, event);
902}
903
904static void handle_pointer_hold_end(struct wl_listener *listener, void *data) {
905 struct sway_cursor *cursor = wl_container_of(
906 listener, cursor, hold_end);
907 struct wlr_pointer_hold_end_event *event = data;
908 cursor_handle_activity_from_device(cursor, &event->pointer->base);
909 seatop_hold_end(cursor->seat, event);
910}
911
931static void handle_pointer_pinch_begin(struct wl_listener *listener, void *data) { 912static void handle_pointer_pinch_begin(struct wl_listener *listener, void *data) {
932 struct sway_cursor *cursor = wl_container_of( 913 struct sway_cursor *cursor = wl_container_of(
933 listener, cursor, pinch_begin); 914 listener, cursor, pinch_begin);
934 struct wlr_pointer_pinch_begin_event *event = data; 915 struct wlr_pointer_pinch_begin_event *event = data;
935 cursor_handle_activity_from_device(cursor, &event->pointer->base); 916 cursor_handle_activity_from_device(cursor, &event->pointer->base);
936 wlr_pointer_gestures_v1_send_pinch_begin( 917 seatop_pinch_begin(cursor->seat, event);
937 cursor->pointer_gestures, cursor->seat->wlr_seat,
938 event->time_msec, event->fingers);
939} 918}
940 919
941static void handle_pointer_pinch_update(struct wl_listener *listener, void *data) { 920static void handle_pointer_pinch_update(struct wl_listener *listener, void *data) {
@@ -943,10 +922,7 @@ static void handle_pointer_pinch_update(struct wl_listener *listener, void *data
943 listener, cursor, pinch_update); 922 listener, cursor, pinch_update);
944 struct wlr_pointer_pinch_update_event *event = data; 923 struct wlr_pointer_pinch_update_event *event = data;
945 cursor_handle_activity_from_device(cursor, &event->pointer->base); 924 cursor_handle_activity_from_device(cursor, &event->pointer->base);
946 wlr_pointer_gestures_v1_send_pinch_update( 925 seatop_pinch_update(cursor->seat, event);
947 cursor->pointer_gestures, cursor->seat->wlr_seat,
948 event->time_msec, event->dx, event->dy,
949 event->scale, event->rotation);
950} 926}
951 927
952static void handle_pointer_pinch_end(struct wl_listener *listener, void *data) { 928static void handle_pointer_pinch_end(struct wl_listener *listener, void *data) {
@@ -954,9 +930,7 @@ static void handle_pointer_pinch_end(struct wl_listener *listener, void *data) {
954 listener, cursor, pinch_end); 930 listener, cursor, pinch_end);
955 struct wlr_pointer_pinch_end_event *event = data; 931 struct wlr_pointer_pinch_end_event *event = data;
956 cursor_handle_activity_from_device(cursor, &event->pointer->base); 932 cursor_handle_activity_from_device(cursor, &event->pointer->base);
957 wlr_pointer_gestures_v1_send_pinch_end( 933 seatop_pinch_end(cursor->seat, event);
958 cursor->pointer_gestures, cursor->seat->wlr_seat,
959 event->time_msec, event->cancelled);
960} 934}
961 935
962static void handle_pointer_swipe_begin(struct wl_listener *listener, void *data) { 936static void handle_pointer_swipe_begin(struct wl_listener *listener, void *data) {
@@ -964,9 +938,7 @@ static void handle_pointer_swipe_begin(struct wl_listener *listener, void *data)
964 listener, cursor, swipe_begin); 938 listener, cursor, swipe_begin);
965 struct wlr_pointer_swipe_begin_event *event = data; 939 struct wlr_pointer_swipe_begin_event *event = data;
966 cursor_handle_activity_from_device(cursor, &event->pointer->base); 940 cursor_handle_activity_from_device(cursor, &event->pointer->base);
967 wlr_pointer_gestures_v1_send_swipe_begin( 941 seatop_swipe_begin(cursor->seat, event);
968 cursor->pointer_gestures, cursor->seat->wlr_seat,
969 event->time_msec, event->fingers);
970} 942}
971 943
972static void handle_pointer_swipe_update(struct wl_listener *listener, void *data) { 944static void handle_pointer_swipe_update(struct wl_listener *listener, void *data) {
@@ -974,9 +946,7 @@ static void handle_pointer_swipe_update(struct wl_listener *listener, void *data
974 listener, cursor, swipe_update); 946 listener, cursor, swipe_update);
975 struct wlr_pointer_swipe_update_event *event = data; 947 struct wlr_pointer_swipe_update_event *event = data;
976 cursor_handle_activity_from_device(cursor, &event->pointer->base); 948 cursor_handle_activity_from_device(cursor, &event->pointer->base);
977 wlr_pointer_gestures_v1_send_swipe_update( 949 seatop_swipe_update(cursor->seat, event);
978 cursor->pointer_gestures, cursor->seat->wlr_seat,
979 event->time_msec, event->dx, event->dy);
980} 950}
981 951
982static void handle_pointer_swipe_end(struct wl_listener *listener, void *data) { 952static void handle_pointer_swipe_end(struct wl_listener *listener, void *data) {
@@ -984,29 +954,7 @@ static void handle_pointer_swipe_end(struct wl_listener *listener, void *data) {
984 listener, cursor, swipe_end); 954 listener, cursor, swipe_end);
985 struct wlr_pointer_swipe_end_event *event = data; 955 struct wlr_pointer_swipe_end_event *event = data;
986 cursor_handle_activity_from_device(cursor, &event->pointer->base); 956 cursor_handle_activity_from_device(cursor, &event->pointer->base);
987 wlr_pointer_gestures_v1_send_swipe_end( 957 seatop_swipe_end(cursor->seat, event);
988 cursor->pointer_gestures, cursor->seat->wlr_seat,
989 event->time_msec, event->cancelled);
990}
991
992static void handle_pointer_hold_begin(struct wl_listener *listener, void *data) {
993 struct sway_cursor *cursor = wl_container_of(
994 listener, cursor, hold_begin);
995 struct wlr_pointer_hold_begin_event *event = data;
996 cursor_handle_activity_from_device(cursor, &event->pointer->base);
997 wlr_pointer_gestures_v1_send_hold_begin(
998 cursor->pointer_gestures, cursor->seat->wlr_seat,
999 event->time_msec, event->fingers);
1000}
1001
1002static void handle_pointer_hold_end(struct wl_listener *listener, void *data) {
1003 struct sway_cursor *cursor = wl_container_of(
1004 listener, cursor, hold_end);
1005 struct wlr_pointer_hold_end_event *event = data;
1006 cursor_handle_activity_from_device(cursor, &event->pointer->base);
1007 wlr_pointer_gestures_v1_send_hold_end(
1008 cursor->pointer_gestures, cursor->seat->wlr_seat,
1009 event->time_msec, event->cancelled);
1010} 958}
1011 959
1012static void handle_image_surface_destroy(struct wl_listener *listener, 960static void handle_image_surface_destroy(struct wl_listener *listener,
@@ -1045,10 +993,9 @@ void cursor_set_image(struct sway_cursor *cursor, const char *image,
1045 } 993 }
1046 994
1047 if (!image) { 995 if (!image) {
1048 wlr_cursor_set_image(cursor->cursor, NULL, 0, 0, 0, 0, 0, 0); 996 wlr_cursor_unset_image(cursor->cursor);
1049 } else if (!current_image || strcmp(current_image, image) != 0) { 997 } else if (!current_image || strcmp(current_image, image) != 0) {
1050 wlr_xcursor_manager_set_cursor_image(cursor->xcursor_manager, image, 998 wlr_cursor_set_xcursor(cursor->cursor, cursor->xcursor_manager, image);
1051 cursor->cursor);
1052 } 999 }
1053} 1000}
1054 1001
@@ -1080,14 +1027,14 @@ void sway_cursor_destroy(struct sway_cursor *cursor) {
1080 wl_event_source_remove(cursor->hide_source); 1027 wl_event_source_remove(cursor->hide_source);
1081 1028
1082 wl_list_remove(&cursor->image_surface_destroy.link); 1029 wl_list_remove(&cursor->image_surface_destroy.link);
1030 wl_list_remove(&cursor->hold_begin.link);
1031 wl_list_remove(&cursor->hold_end.link);
1083 wl_list_remove(&cursor->pinch_begin.link); 1032 wl_list_remove(&cursor->pinch_begin.link);
1084 wl_list_remove(&cursor->pinch_update.link); 1033 wl_list_remove(&cursor->pinch_update.link);
1085 wl_list_remove(&cursor->pinch_end.link); 1034 wl_list_remove(&cursor->pinch_end.link);
1086 wl_list_remove(&cursor->swipe_begin.link); 1035 wl_list_remove(&cursor->swipe_begin.link);
1087 wl_list_remove(&cursor->swipe_update.link); 1036 wl_list_remove(&cursor->swipe_update.link);
1088 wl_list_remove(&cursor->swipe_end.link); 1037 wl_list_remove(&cursor->swipe_end.link);
1089 wl_list_remove(&cursor->hold_begin.link);
1090 wl_list_remove(&cursor->hold_end.link);
1091 wl_list_remove(&cursor->motion.link); 1038 wl_list_remove(&cursor->motion.link);
1092 wl_list_remove(&cursor->motion_absolute.link); 1039 wl_list_remove(&cursor->motion_absolute.link);
1093 wl_list_remove(&cursor->button.link); 1040 wl_list_remove(&cursor->button.link);
@@ -1095,6 +1042,7 @@ void sway_cursor_destroy(struct sway_cursor *cursor) {
1095 wl_list_remove(&cursor->frame.link); 1042 wl_list_remove(&cursor->frame.link);
1096 wl_list_remove(&cursor->touch_down.link); 1043 wl_list_remove(&cursor->touch_down.link);
1097 wl_list_remove(&cursor->touch_up.link); 1044 wl_list_remove(&cursor->touch_up.link);
1045 wl_list_remove(&cursor->touch_cancel.link);
1098 wl_list_remove(&cursor->touch_motion.link); 1046 wl_list_remove(&cursor->touch_motion.link);
1099 wl_list_remove(&cursor->touch_frame.link); 1047 wl_list_remove(&cursor->touch_frame.link);
1100 wl_list_remove(&cursor->tool_axis.link); 1048 wl_list_remove(&cursor->tool_axis.link);
@@ -1131,23 +1079,24 @@ struct sway_cursor *sway_cursor_create(struct sway_seat *seat) {
1131 wl_list_init(&cursor->image_surface_destroy.link); 1079 wl_list_init(&cursor->image_surface_destroy.link);
1132 cursor->image_surface_destroy.notify = handle_image_surface_destroy; 1080 cursor->image_surface_destroy.notify = handle_image_surface_destroy;
1133 1081
1134 cursor->pointer_gestures = wlr_pointer_gestures_v1_create(server.wl_display); 1082 wl_signal_add(&wlr_cursor->events.hold_begin, &cursor->hold_begin);
1135 cursor->pinch_begin.notify = handle_pointer_pinch_begin; 1083 cursor->hold_begin.notify = handle_pointer_hold_begin;
1084 wl_signal_add(&wlr_cursor->events.hold_end, &cursor->hold_end);
1085 cursor->hold_end.notify = handle_pointer_hold_end;
1086
1136 wl_signal_add(&wlr_cursor->events.pinch_begin, &cursor->pinch_begin); 1087 wl_signal_add(&wlr_cursor->events.pinch_begin, &cursor->pinch_begin);
1137 cursor->pinch_update.notify = handle_pointer_pinch_update; 1088 cursor->pinch_begin.notify = handle_pointer_pinch_begin;
1138 wl_signal_add(&wlr_cursor->events.pinch_update, &cursor->pinch_update); 1089 wl_signal_add(&wlr_cursor->events.pinch_update, &cursor->pinch_update);
1139 cursor->pinch_end.notify = handle_pointer_pinch_end; 1090 cursor->pinch_update.notify = handle_pointer_pinch_update;
1140 wl_signal_add(&wlr_cursor->events.pinch_end, &cursor->pinch_end); 1091 wl_signal_add(&wlr_cursor->events.pinch_end, &cursor->pinch_end);
1141 cursor->swipe_begin.notify = handle_pointer_swipe_begin; 1092 cursor->pinch_end.notify = handle_pointer_pinch_end;
1093
1142 wl_signal_add(&wlr_cursor->events.swipe_begin, &cursor->swipe_begin); 1094 wl_signal_add(&wlr_cursor->events.swipe_begin, &cursor->swipe_begin);
1143 cursor->swipe_update.notify = handle_pointer_swipe_update; 1095 cursor->swipe_begin.notify = handle_pointer_swipe_begin;
1144 wl_signal_add(&wlr_cursor->events.swipe_update, &cursor->swipe_update); 1096 wl_signal_add(&wlr_cursor->events.swipe_update, &cursor->swipe_update);
1145 cursor->swipe_end.notify = handle_pointer_swipe_end; 1097 cursor->swipe_update.notify = handle_pointer_swipe_update;
1146 wl_signal_add(&wlr_cursor->events.swipe_end, &cursor->swipe_end); 1098 wl_signal_add(&wlr_cursor->events.swipe_end, &cursor->swipe_end);
1147 cursor->hold_begin.notify = handle_pointer_hold_begin; 1099 cursor->swipe_end.notify = handle_pointer_swipe_end;
1148 wl_signal_add(&wlr_cursor->events.hold_begin, &cursor->hold_begin);
1149 cursor->hold_end.notify = handle_pointer_hold_end;
1150 wl_signal_add(&wlr_cursor->events.hold_end, &cursor->hold_end);
1151 1100
1152 // input events 1101 // input events
1153 wl_signal_add(&wlr_cursor->events.motion, &cursor->motion); 1102 wl_signal_add(&wlr_cursor->events.motion, &cursor->motion);
@@ -1172,6 +1121,9 @@ struct sway_cursor *sway_cursor_create(struct sway_seat *seat) {
1172 wl_signal_add(&wlr_cursor->events.touch_up, &cursor->touch_up); 1121 wl_signal_add(&wlr_cursor->events.touch_up, &cursor->touch_up);
1173 cursor->touch_up.notify = handle_touch_up; 1122 cursor->touch_up.notify = handle_touch_up;
1174 1123
1124 wl_signal_add(&wlr_cursor->events.touch_cancel, &cursor->touch_cancel);
1125 cursor->touch_cancel.notify = handle_touch_cancel;
1126
1175 wl_signal_add(&wlr_cursor->events.touch_motion, 1127 wl_signal_add(&wlr_cursor->events.touch_motion,
1176 &cursor->touch_motion); 1128 &cursor->touch_motion);
1177 cursor->touch_motion.notify = handle_touch_motion; 1129 cursor->touch_motion.notify = handle_touch_motion;
@@ -1264,11 +1216,7 @@ uint32_t get_mouse_bindsym(const char *name, char **error) {
1264 // Get event code from name 1216 // Get event code from name
1265 int code = libevdev_event_code_from_name(EV_KEY, name); 1217 int code = libevdev_event_code_from_name(EV_KEY, name);
1266 if (code == -1) { 1218 if (code == -1) {
1267 size_t len = snprintf(NULL, 0, "Unknown event %s", name) + 1; 1219 *error = format_str("Unknown event %s", name);
1268 *error = malloc(len);
1269 if (*error) {
1270 snprintf(*error, len, "Unknown event %s", name);
1271 }
1272 return 0; 1220 return 0;
1273 } 1221 }
1274 return code; 1222 return code;
@@ -1290,13 +1238,8 @@ uint32_t get_mouse_bindcode(const char *name, char **error) {
1290 } 1238 }
1291 const char *event = libevdev_event_code_get_name(EV_KEY, code); 1239 const char *event = libevdev_event_code_get_name(EV_KEY, code);
1292 if (!event || strncmp(event, "BTN_", strlen("BTN_")) != 0) { 1240 if (!event || strncmp(event, "BTN_", strlen("BTN_")) != 0) {
1293 size_t len = snprintf(NULL, 0, "Event code %d (%s) is not a button", 1241 *error = format_str("Event code %d (%s) is not a button",
1294 code, event ? event : "(null)") + 1; 1242 code, event ? event : "(null)");
1295 *error = malloc(len);
1296 if (*error) {
1297 snprintf(*error, len, "Event code %d (%s) is not a button",
1298 code, event ? event : "(null)");
1299 }
1300 return 0; 1243 return 0;
1301 } 1244 }
1302 return code; 1245 return code;
@@ -1329,12 +1272,15 @@ const char *get_mouse_button_name(uint32_t button) {
1329static void warp_to_constraint_cursor_hint(struct sway_cursor *cursor) { 1272static void warp_to_constraint_cursor_hint(struct sway_cursor *cursor) {
1330 struct wlr_pointer_constraint_v1 *constraint = cursor->active_constraint; 1273 struct wlr_pointer_constraint_v1 *constraint = cursor->active_constraint;
1331 1274
1332 if (constraint->current.committed & 1275 if (constraint->current.cursor_hint.enabled) {
1333 WLR_POINTER_CONSTRAINT_V1_STATE_CURSOR_HINT) {
1334 double sx = constraint->current.cursor_hint.x; 1276 double sx = constraint->current.cursor_hint.x;
1335 double sy = constraint->current.cursor_hint.y; 1277 double sy = constraint->current.cursor_hint.y;
1336 1278
1337 struct sway_view *view = view_from_wlr_surface(constraint->surface); 1279 struct sway_view *view = view_from_wlr_surface(constraint->surface);
1280 if (!view) {
1281 return;
1282 }
1283
1338 struct sway_container *con = view->container; 1284 struct sway_container *con = view->container;
1339 1285
1340 double lx = sx + con->pending.content_x - view->geometry.x; 1286 double lx = sx + con->pending.content_x - view->geometry.x;
@@ -1385,12 +1331,9 @@ void handle_pointer_constraint(struct wl_listener *listener, void *data) {
1385 sway_constraint->destroy.notify = handle_constraint_destroy; 1331 sway_constraint->destroy.notify = handle_constraint_destroy;
1386 wl_signal_add(&constraint->events.destroy, &sway_constraint->destroy); 1332 wl_signal_add(&constraint->events.destroy, &sway_constraint->destroy);
1387 1333
1388 struct sway_node *focus = seat_get_focus(seat); 1334 struct wlr_surface *surface = seat->wlr_seat->keyboard_state.focused_surface;
1389 if (focus && node_is_view(focus)) { 1335 if (surface && surface == constraint->surface) {
1390 struct wlr_surface *surface = focus->sway_container->view->surface; 1336 sway_cursor_constrain(seat->cursor, constraint);
1391 if (surface == constraint->surface) {
1392 sway_cursor_constrain(seat->cursor, constraint);
1393 }
1394 } 1337 }
1395} 1338}
1396 1339
@@ -1448,3 +1391,26 @@ void sway_cursor_constrain(struct sway_cursor *cursor,
1448 wl_signal_add(&constraint->surface->events.commit, 1391 wl_signal_add(&constraint->surface->events.commit,
1449 &cursor->constraint_commit); 1392 &cursor->constraint_commit);
1450} 1393}
1394
1395void handle_request_set_cursor_shape(struct wl_listener *listener, void *data) {
1396 const struct wlr_cursor_shape_manager_v1_request_set_shape_event *event = data;
1397 struct sway_seat *seat = event->seat_client->seat->data;
1398
1399 if (!seatop_allows_set_cursor(seat)) {
1400 return;
1401 }
1402
1403 struct wl_client *focused_client = NULL;
1404 struct wlr_surface *focused_surface = seat->wlr_seat->pointer_state.focused_surface;
1405 if (focused_surface != NULL) {
1406 focused_client = wl_resource_get_client(focused_surface->resource);
1407 }
1408
1409 // TODO: check cursor mode
1410 if (focused_client == NULL || event->seat_client->client != focused_client) {
1411 sway_log(SWAY_DEBUG, "denying request to set cursor from unfocused client");
1412 return;
1413 }
1414
1415 cursor_set_image(seat->cursor, wlr_cursor_shape_v1_name(event->shape), focused_client);
1416}
diff --git a/sway/input/input-manager.c b/sway/input/input-manager.c
index 4a0bce0e..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,33 +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
279static void handle_inhibit_activate(struct wl_listener *listener, void *data) {
280 struct sway_input_manager *input_manager = wl_container_of(
281 listener, input_manager, inhibit_activate);
282 struct sway_seat *seat;
283 wl_list_for_each(seat, &input_manager->seats, link) {
284 seat_set_exclusive_client(seat, input_manager->inhibit->active_client);
285 }
286}
287 288
288static void handle_inhibit_deactivate(struct wl_listener *listener, void *data) { 289 if (config_changed) {
289 struct sway_input_manager *input_manager = wl_container_of( 290 ipc_event_input("libinput_config", input_device);
290 listener, input_manager, inhibit_deactivate);
291 struct sway_seat *seat;
292 if (server.session_lock.locked) {
293 // Don't deactivate the grab of a screenlocker
294 return;
295 }
296 wl_list_for_each(seat, &input_manager->seats, link) {
297 seat_set_exclusive_client(seat, NULL);
298 struct sway_node *previous = seat_get_focus(seat);
299 if (previous) {
300 // Hack to get seat to re-focus the return value of get_focus
301 seat_set_focus(seat, NULL);
302 seat_set_focus(seat, previous);
303 }
304 } 291 }
305} 292}
306 293
@@ -446,6 +433,20 @@ void handle_virtual_pointer(struct wl_listener *listener, void *data) {
446 } 433 }
447} 434}
448 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
449struct sway_input_manager *input_manager_create(struct sway_server *server) { 450struct sway_input_manager *input_manager_create(struct sway_server *server) {
450 struct sway_input_manager *input = 451 struct sway_input_manager *input =
451 calloc(1, sizeof(struct sway_input_manager)); 452 calloc(1, sizeof(struct sway_input_manager));
@@ -472,14 +473,6 @@ struct sway_input_manager *input_manager_create(struct sway_server *server) {
472 &input->virtual_pointer_new); 473 &input->virtual_pointer_new);
473 input->virtual_pointer_new.notify = handle_virtual_pointer; 474 input->virtual_pointer_new.notify = handle_virtual_pointer;
474 475
475 input->inhibit = wlr_input_inhibit_manager_create(server->wl_display);
476 input->inhibit_activate.notify = handle_inhibit_activate;
477 wl_signal_add(&input->inhibit->events.activate,
478 &input->inhibit_activate);
479 input->inhibit_deactivate.notify = handle_inhibit_deactivate;
480 wl_signal_add(&input->inhibit->events.deactivate,
481 &input->inhibit_deactivate);
482
483 input->keyboard_shortcuts_inhibit = 476 input->keyboard_shortcuts_inhibit =
484 wlr_keyboard_shortcuts_inhibit_v1_create(server->wl_display); 477 wlr_keyboard_shortcuts_inhibit_v1_create(server->wl_display);
485 input->keyboard_shortcuts_inhibit_new_inhibitor.notify = 478 input->keyboard_shortcuts_inhibit_new_inhibitor.notify =
@@ -487,6 +480,17 @@ struct sway_input_manager *input_manager_create(struct sway_server *server) {
487 wl_signal_add(&input->keyboard_shortcuts_inhibit->events.new_inhibitor, 480 wl_signal_add(&input->keyboard_shortcuts_inhibit->events.new_inhibitor,
488 &input->keyboard_shortcuts_inhibit_new_inhibitor); 481 &input->keyboard_shortcuts_inhibit_new_inhibitor);
489 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
490 return input; 494 return input;
491} 495}
492 496
@@ -524,21 +528,50 @@ static void retranslate_keysyms(struct input_config *input_config) {
524 return; 528 return;
525 } 529 }
526 } 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 }
527} 543}
528 544
529static void input_manager_configure_input( 545static void input_manager_configure_input(
530 struct sway_input_device *input_device) { 546 struct sway_input_device *input_device) {
531 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
532 struct sway_seat *seat = NULL; 552 struct sway_seat *seat = NULL;
533 wl_list_for_each(seat, &server.input->seats, link) { 553 wl_list_for_each(seat, &server.input->seats, link) {
534 seat_configure_device(seat, input_device); 554 seat_configure_device(seat, input_device);
535 } 555 }
556 if (config_changed) {
557 ipc_event_input("libinput_config", input_device);
558 }
536} 559}
537 560
538void input_manager_configure_all_inputs(void) { 561void input_manager_configure_all_input_mappings(void) {
539 struct sway_input_device *input_device = NULL; 562 struct sway_input_device *input_device;
540 wl_list_for_each(input_device, &server.input->devices, link) { 563 wl_list_for_each(input_device, &server.input->devices, link) {
541 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
542 } 575 }
543} 576}
544 577
@@ -560,7 +593,9 @@ void input_manager_apply_input_config(struct input_config *input_config) {
560} 593}
561 594
562void 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
563 sway_input_reset_libinput_device(input_device); 597 sway_input_reset_libinput_device(input_device);
598#endif
564 struct sway_seat *seat = NULL; 599 struct sway_seat *seat = NULL;
565 wl_list_for_each(seat, &server.input->seats, link) { 600 wl_list_for_each(seat, &server.input->seats, link) {
566 seat_reset_device(seat, input_device); 601 seat_reset_device(seat, input_device);
@@ -568,6 +603,13 @@ void input_manager_reset_input(struct sway_input_device *input_device) {
568} 603}
569 604
570void 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
571 struct sway_input_device *input_device = NULL; 613 struct sway_input_device *input_device = NULL;
572 wl_list_for_each(input_device, &server.input->devices, link) { 614 wl_list_for_each(input_device, &server.input->devices, link) {
573 input_manager_reset_input(input_device); 615 input_manager_reset_input(input_device);
@@ -576,7 +618,6 @@ void input_manager_reset_all_inputs(void) {
576 // 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,
577 // 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
578 // need their keyboard disarmed. 620 // need their keyboard disarmed.
579 struct sway_seat *seat;
580 wl_list_for_each(seat, &server.input->seats, link) { 621 wl_list_for_each(seat, &server.input->seats, link) {
581 struct sway_keyboard_group *group; 622 struct sway_keyboard_group *group;
582 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 8f18b8ba..9ac21664 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>
@@ -14,8 +13,13 @@
14#include "sway/input/seat.h" 13#include "sway/input/seat.h"
15#include "sway/input/cursor.h" 14#include "sway/input/cursor.h"
16#include "sway/ipc-server.h" 15#include "sway/ipc-server.h"
16#include "sway/server.h"
17#include "log.h" 17#include "log.h"
18 18
19#if WLR_HAS_SESSION
20#include <wlr/backend/session.h>
21#endif
22
19static struct modifier_key { 23static struct modifier_key {
20 char *name; 24 char *name;
21 uint32_t mod; 25 uint32_t mod;
@@ -29,6 +33,7 @@ static struct modifier_key {
29 { XKB_MOD_NAME_NUM, WLR_MODIFIER_MOD2 }, 33 { XKB_MOD_NAME_NUM, WLR_MODIFIER_MOD2 },
30 { "Mod3", WLR_MODIFIER_MOD3 }, 34 { "Mod3", WLR_MODIFIER_MOD3 },
31 { XKB_MOD_NAME_LOGO, WLR_MODIFIER_LOGO }, 35 { XKB_MOD_NAME_LOGO, WLR_MODIFIER_LOGO },
36 { "Super", WLR_MODIFIER_LOGO },
32 { "Mod5", WLR_MODIFIER_MOD5 }, 37 { "Mod5", WLR_MODIFIER_MOD5 },
33}; 38};
34 39
@@ -264,14 +269,12 @@ static bool keyboard_execute_compositor_binding(struct sway_keyboard *keyboard,
264 xkb_keysym_t keysym = pressed_keysyms[i]; 269 xkb_keysym_t keysym = pressed_keysyms[i];
265 if (keysym >= XKB_KEY_XF86Switch_VT_1 && 270 if (keysym >= XKB_KEY_XF86Switch_VT_1 &&
266 keysym <= XKB_KEY_XF86Switch_VT_12) { 271 keysym <= XKB_KEY_XF86Switch_VT_12) {
267 if (wlr_backend_is_multi(server.backend)) { 272#if WLR_HAS_SESSION
268 struct wlr_session *session = 273 if (server.session) {
269 wlr_backend_get_session(server.backend); 274 unsigned vt = keysym - XKB_KEY_XF86Switch_VT_1 + 1;
270 if (session) { 275 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 } 276 }
277#endif
275 return true; 278 return true;
276 } 279 }
277 } 280 }
@@ -291,14 +294,12 @@ static bool keyboard_execute_compositor_binding(struct sway_keyboard *keyboard,
291static size_t keyboard_keysyms_translated(struct sway_keyboard *keyboard, 294static size_t keyboard_keysyms_translated(struct sway_keyboard *keyboard,
292 xkb_keycode_t keycode, const xkb_keysym_t **keysyms, 295 xkb_keycode_t keycode, const xkb_keysym_t **keysyms,
293 uint32_t *modifiers) { 296 uint32_t *modifiers) {
294 struct wlr_input_device *device = 297 *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( 298 xkb_mod_mask_t consumed = xkb_state_key_get_consumed_mods2(
298 device->keyboard->xkb_state, keycode, XKB_CONSUMED_MODE_XKB); 299 keyboard->wlr->xkb_state, keycode, XKB_CONSUMED_MODE_XKB);
299 *modifiers = *modifiers & ~consumed; 300 *modifiers = *modifiers & ~consumed;
300 301
301 return xkb_state_key_get_syms(device->keyboard->xkb_state, 302 return xkb_state_key_get_syms(keyboard->wlr->xkb_state,
302 keycode, keysyms); 303 keycode, keysyms);
303} 304}
304 305
@@ -314,13 +315,11 @@ static size_t keyboard_keysyms_translated(struct sway_keyboard *keyboard,
314static size_t keyboard_keysyms_raw(struct sway_keyboard *keyboard, 315static size_t keyboard_keysyms_raw(struct sway_keyboard *keyboard,
315 xkb_keycode_t keycode, const xkb_keysym_t **keysyms, 316 xkb_keycode_t keycode, const xkb_keysym_t **keysyms,
316 uint32_t *modifiers) { 317 uint32_t *modifiers) {
317 struct wlr_input_device *device = 318 *modifiers = wlr_keyboard_get_modifiers(keyboard->wlr);
318 keyboard->seat_device->input_device->wlr_device;
319 *modifiers = wlr_keyboard_get_modifiers(device->keyboard);
320 319
321 xkb_layout_index_t layout_index = xkb_state_key_get_layout( 320 xkb_layout_index_t layout_index = xkb_state_key_get_layout(
322 device->keyboard->xkb_state, keycode); 321 keyboard->wlr->xkb_state, keycode);
323 return xkb_keymap_key_get_syms_by_level(device->keyboard->keymap, 322 return xkb_keymap_key_get_syms_by_level(keyboard->wlr->keymap,
324 keycode, layout_index, 0, keysyms); 323 keycode, layout_index, 0, keysyms);
325} 324}
326 325
@@ -360,8 +359,7 @@ static void update_keyboard_state(struct sway_keyboard *keyboard,
360 keyinfo->keycode, &keyinfo->translated_keysyms, 359 keyinfo->keycode, &keyinfo->translated_keysyms,
361 &keyinfo->translated_modifiers); 360 &keyinfo->translated_modifiers);
362 361
363 keyinfo->code_modifiers = wlr_keyboard_get_modifiers( 362 keyinfo->code_modifiers = wlr_keyboard_get_modifiers(keyboard->wlr);
364 keyboard->seat_device->input_device->wlr_device->keyboard);
365 363
366 // Update shortcut model keyinfo 364 // Update shortcut model keyinfo
367 update_shortcut_state(&keyboard->state_keycodes, raw_keycode, keystate, 365 update_shortcut_state(&keyboard->state_keycodes, raw_keycode, keystate,
@@ -407,10 +405,9 @@ static void handle_key_event(struct sway_keyboard *keyboard,
407 struct wlr_input_device *wlr_device = 405 struct wlr_input_device *wlr_device =
408 keyboard->seat_device->input_device->wlr_device; 406 keyboard->seat_device->input_device->wlr_device;
409 char *device_identifier = input_device_get_identifier(wlr_device); 407 char *device_identifier = input_device_get_identifier(wlr_device);
410 bool exact_identifier = wlr_device->keyboard->group != NULL; 408 bool exact_identifier = keyboard->wlr->group != NULL;
411 seat_idle_notify_activity(seat, IDLE_SOURCE_KEYBOARD); 409 seat_idle_notify_activity(seat, IDLE_SOURCE_KEYBOARD);
412 bool input_inhibited = seat->exclusive_client != NULL || 410 bool locked = server.session_lock.lock;
413 server.session_lock.locked;
414 struct sway_keyboard_shortcuts_inhibitor *sway_inhibitor = 411 struct sway_keyboard_shortcuts_inhibitor *sway_inhibitor =
415 keyboard_shortcuts_inhibitor_get_for_focused_surface(seat); 412 keyboard_shortcuts_inhibitor_get_for_focused_surface(seat);
416 bool shortcuts_inhibited = sway_inhibitor && sway_inhibitor->inhibitor->active; 413 bool shortcuts_inhibited = sway_inhibitor && sway_inhibitor->inhibitor->active;
@@ -428,17 +425,17 @@ static void handle_key_event(struct sway_keyboard *keyboard,
428 struct sway_binding *binding_released = NULL; 425 struct sway_binding *binding_released = NULL;
429 get_active_binding(&keyboard->state_keycodes, 426 get_active_binding(&keyboard->state_keycodes,
430 config->current_mode->keycode_bindings, &binding_released, 427 config->current_mode->keycode_bindings, &binding_released,
431 keyinfo.code_modifiers, true, input_inhibited, 428 keyinfo.code_modifiers, true, locked,
432 shortcuts_inhibited, device_identifier, 429 shortcuts_inhibited, device_identifier,
433 exact_identifier, keyboard->effective_layout); 430 exact_identifier, keyboard->effective_layout);
434 get_active_binding(&keyboard->state_keysyms_raw, 431 get_active_binding(&keyboard->state_keysyms_raw,
435 config->current_mode->keysym_bindings, &binding_released, 432 config->current_mode->keysym_bindings, &binding_released,
436 keyinfo.raw_modifiers, true, input_inhibited, 433 keyinfo.raw_modifiers, true, locked,
437 shortcuts_inhibited, device_identifier, 434 shortcuts_inhibited, device_identifier,
438 exact_identifier, keyboard->effective_layout); 435 exact_identifier, keyboard->effective_layout);
439 get_active_binding(&keyboard->state_keysyms_translated, 436 get_active_binding(&keyboard->state_keysyms_translated,
440 config->current_mode->keysym_bindings, &binding_released, 437 config->current_mode->keysym_bindings, &binding_released,
441 keyinfo.translated_modifiers, true, input_inhibited, 438 keyinfo.translated_modifiers, true, locked,
442 shortcuts_inhibited, device_identifier, 439 shortcuts_inhibited, device_identifier,
443 exact_identifier, keyboard->effective_layout); 440 exact_identifier, keyboard->effective_layout);
444 441
@@ -460,17 +457,17 @@ static void handle_key_event(struct sway_keyboard *keyboard,
460 if (event->state == WL_KEYBOARD_KEY_STATE_PRESSED) { 457 if (event->state == WL_KEYBOARD_KEY_STATE_PRESSED) {
461 get_active_binding(&keyboard->state_keycodes, 458 get_active_binding(&keyboard->state_keycodes,
462 config->current_mode->keycode_bindings, &binding, 459 config->current_mode->keycode_bindings, &binding,
463 keyinfo.code_modifiers, false, input_inhibited, 460 keyinfo.code_modifiers, false, locked,
464 shortcuts_inhibited, device_identifier, 461 shortcuts_inhibited, device_identifier,
465 exact_identifier, keyboard->effective_layout); 462 exact_identifier, keyboard->effective_layout);
466 get_active_binding(&keyboard->state_keysyms_raw, 463 get_active_binding(&keyboard->state_keysyms_raw,
467 config->current_mode->keysym_bindings, &binding, 464 config->current_mode->keysym_bindings, &binding,
468 keyinfo.raw_modifiers, false, input_inhibited, 465 keyinfo.raw_modifiers, false, locked,
469 shortcuts_inhibited, device_identifier, 466 shortcuts_inhibited, device_identifier,
470 exact_identifier, keyboard->effective_layout); 467 exact_identifier, keyboard->effective_layout);
471 get_active_binding(&keyboard->state_keysyms_translated, 468 get_active_binding(&keyboard->state_keysyms_translated,
472 config->current_mode->keysym_bindings, &binding, 469 config->current_mode->keysym_bindings, &binding,
473 keyinfo.translated_modifiers, false, input_inhibited, 470 keyinfo.translated_modifiers, false, locked,
474 shortcuts_inhibited, device_identifier, 471 shortcuts_inhibited, device_identifier,
475 exact_identifier, keyboard->effective_layout); 472 exact_identifier, keyboard->effective_layout);
476 } 473 }
@@ -478,10 +475,10 @@ static void handle_key_event(struct sway_keyboard *keyboard,
478 // Set up (or clear) keyboard repeat for a pressed binding. Since the 475 // Set up (or clear) keyboard repeat for a pressed binding. Since the
479 // binding may remove the keyboard, the timer needs to be updated first 476 // binding may remove the keyboard, the timer needs to be updated first
480 if (binding && !(binding->flags & BINDING_NOREPEAT) && 477 if (binding && !(binding->flags & BINDING_NOREPEAT) &&
481 wlr_device->keyboard->repeat_info.delay > 0) { 478 keyboard->wlr->repeat_info.delay > 0) {
482 keyboard->repeat_binding = binding; 479 keyboard->repeat_binding = binding;
483 if (wl_event_source_timer_update(keyboard->key_repeat_source, 480 if (wl_event_source_timer_update(keyboard->key_repeat_source,
484 wlr_device->keyboard->repeat_info.delay) < 0) { 481 keyboard->wlr->repeat_info.delay) < 0) {
485 sway_log(SWAY_DEBUG, "failed to set key repeat timer"); 482 sway_log(SWAY_DEBUG, "failed to set key repeat timer");
486 } 483 }
487 } else if (keyboard->repeat_binding) { 484 } else if (keyboard->repeat_binding) {
@@ -493,7 +490,7 @@ static void handle_key_event(struct sway_keyboard *keyboard,
493 handled = true; 490 handled = true;
494 } 491 }
495 492
496 if (!handled && wlr_device->keyboard->group) { 493 if (!handled && keyboard->wlr->group) {
497 // Only handle device specific bindings for keyboards in a group 494 // Only handle device specific bindings for keyboards in a group
498 free(device_identifier); 495 free(device_identifier);
499 return; 496 return;
@@ -518,7 +515,7 @@ static void handle_key_event(struct sway_keyboard *keyboard,
518 &keyboard->state_pressed_sent, event->keycode, 515 &keyboard->state_pressed_sent, event->keycode,
519 event->state, keyinfo.keycode, 0); 516 event->state, keyinfo.keycode, 0);
520 if (pressed_sent) { 517 if (pressed_sent) {
521 wlr_seat_set_keyboard(wlr_seat, wlr_device->keyboard); 518 wlr_seat_set_keyboard(wlr_seat, keyboard->wlr);
522 wlr_seat_keyboard_notify_key(wlr_seat, event->time_msec, 519 wlr_seat_keyboard_notify_key(wlr_seat, event->time_msec,
523 event->keycode, event->state); 520 event->keycode, event->state);
524 handled = true; 521 handled = true;
@@ -529,8 +526,7 @@ static void handle_key_event(struct sway_keyboard *keyboard,
529 struct wlr_input_method_keyboard_grab_v2 *kb_grab = keyboard_get_im_grab(keyboard); 526 struct wlr_input_method_keyboard_grab_v2 *kb_grab = keyboard_get_im_grab(keyboard);
530 527
531 if (kb_grab) { 528 if (kb_grab) {
532 wlr_input_method_keyboard_grab_v2_set_keyboard(kb_grab, 529 wlr_input_method_keyboard_grab_v2_set_keyboard(kb_grab, keyboard->wlr);
533 wlr_device->keyboard);
534 wlr_input_method_keyboard_grab_v2_send_key(kb_grab, 530 wlr_input_method_keyboard_grab_v2_send_key(kb_grab,
535 event->time_msec, event->keycode, event->state); 531 event->time_msec, event->keycode, event->state);
536 handled = true; 532 handled = true;
@@ -543,7 +539,7 @@ static void handle_key_event(struct sway_keyboard *keyboard,
543 update_shortcut_state( 539 update_shortcut_state(
544 &keyboard->state_pressed_sent, event->keycode, event->state, 540 &keyboard->state_pressed_sent, event->keycode, event->state,
545 keyinfo.keycode, 0); 541 keyinfo.keycode, 0);
546 wlr_seat_set_keyboard(wlr_seat, wlr_device->keyboard); 542 wlr_seat_set_keyboard(wlr_seat, keyboard->wlr);
547 wlr_seat_keyboard_notify_key(wlr_seat, event->time_msec, 543 wlr_seat_keyboard_notify_key(wlr_seat, event->time_msec,
548 event->keycode, event->state); 544 event->keycode, event->state);
549 } 545 }
@@ -619,14 +615,12 @@ static void handle_keyboard_group_leave(struct wl_listener *listener,
619} 615}
620 616
621static int handle_keyboard_repeat(void *data) { 617static int handle_keyboard_repeat(void *data) {
622 struct sway_keyboard *keyboard = (struct sway_keyboard *)data; 618 struct sway_keyboard *keyboard = data;
623 struct wlr_keyboard *wlr_device =
624 keyboard->seat_device->input_device->wlr_device->keyboard;
625 if (keyboard->repeat_binding) { 619 if (keyboard->repeat_binding) {
626 if (wlr_device->repeat_info.rate > 0) { 620 if (keyboard->wlr->repeat_info.rate > 0) {
627 // We queue the next event first, as the command might cancel it 621 // We queue the next event first, as the command might cancel it
628 if (wl_event_source_timer_update(keyboard->key_repeat_source, 622 if (wl_event_source_timer_update(keyboard->key_repeat_source,
629 1000 / wlr_device->repeat_info.rate) < 0) { 623 1000 / keyboard->wlr->repeat_info.rate) < 0) {
630 sway_log(SWAY_DEBUG, "failed to update key repeat timer"); 624 sway_log(SWAY_DEBUG, "failed to update key repeat timer");
631 } 625 }
632 } 626 }
@@ -659,31 +653,28 @@ static void determine_bar_visibility(uint32_t modifiers) {
659} 653}
660 654
661static void handle_modifier_event(struct sway_keyboard *keyboard) { 655static void handle_modifier_event(struct sway_keyboard *keyboard) {
662 struct wlr_input_device *wlr_device = 656 if (!keyboard->wlr->group) {
663 keyboard->seat_device->input_device->wlr_device;
664 if (!wlr_device->keyboard->group) {
665 struct wlr_input_method_keyboard_grab_v2 *kb_grab = keyboard_get_im_grab(keyboard); 657 struct wlr_input_method_keyboard_grab_v2 *kb_grab = keyboard_get_im_grab(keyboard);
666 658
667 if (kb_grab) { 659 if (kb_grab) {
668 wlr_input_method_keyboard_grab_v2_set_keyboard(kb_grab, 660 wlr_input_method_keyboard_grab_v2_set_keyboard(kb_grab, keyboard->wlr);
669 wlr_device->keyboard);
670 wlr_input_method_keyboard_grab_v2_send_modifiers(kb_grab, 661 wlr_input_method_keyboard_grab_v2_send_modifiers(kb_grab,
671 &wlr_device->keyboard->modifiers); 662 &keyboard->wlr->modifiers);
672 } else { 663 } else {
673 struct wlr_seat *wlr_seat = keyboard->seat_device->sway_seat->wlr_seat; 664 struct wlr_seat *wlr_seat = keyboard->seat_device->sway_seat->wlr_seat;
674 wlr_seat_set_keyboard(wlr_seat, wlr_device->keyboard); 665 wlr_seat_set_keyboard(wlr_seat, keyboard->wlr);
675 wlr_seat_keyboard_notify_modifiers(wlr_seat, 666 wlr_seat_keyboard_notify_modifiers(wlr_seat,
676 &wlr_device->keyboard->modifiers); 667 &keyboard->wlr->modifiers);
677 } 668 }
678 669
679 uint32_t modifiers = wlr_keyboard_get_modifiers(wlr_device->keyboard); 670 uint32_t modifiers = wlr_keyboard_get_modifiers(keyboard->wlr);
680 determine_bar_visibility(modifiers); 671 determine_bar_visibility(modifiers);
681 } 672 }
682 673
683 if (wlr_device->keyboard->modifiers.group != keyboard->effective_layout) { 674 if (keyboard->wlr->modifiers.group != keyboard->effective_layout) {
684 keyboard->effective_layout = wlr_device->keyboard->modifiers.group; 675 keyboard->effective_layout = keyboard->wlr->modifiers.group;
685 676
686 if (!wlr_keyboard_group_from_wlr_keyboard(wlr_device->keyboard)) { 677 if (!wlr_keyboard_group_from_wlr_keyboard(keyboard->wlr)) {
687 ipc_event_input("xkb_layout", keyboard->seat_device->input_device); 678 ipc_event_input("xkb_layout", keyboard->seat_device->input_device);
688 } 679 }
689 } 680 }
@@ -712,6 +703,7 @@ struct sway_keyboard *sway_keyboard_create(struct sway_seat *seat,
712 } 703 }
713 704
714 keyboard->seat_device = device; 705 keyboard->seat_device = device;
706 keyboard->wlr = wlr_keyboard_from_input_device(device->input_device->wlr_device);
715 device->keyboard = keyboard; 707 device->keyboard = keyboard;
716 708
717 wl_list_init(&keyboard->keyboard_key.link); 709 wl_list_init(&keyboard->keyboard_key.link);
@@ -725,23 +717,11 @@ struct sway_keyboard *sway_keyboard_create(struct sway_seat *seat,
725 717
726static void handle_xkb_context_log(struct xkb_context *context, 718static void handle_xkb_context_log(struct xkb_context *context,
727 enum xkb_log_level level, const char *format, va_list args) { 719 enum xkb_log_level level, const char *format, va_list args) {
728 va_list args_copy; 720 char *error = vformat_str(format, args);
729 va_copy(args_copy, args);
730 size_t length = vsnprintf(NULL, 0, format, args_copy) + 1;
731 va_end(args_copy);
732
733 char *error = malloc(length);
734 if (!error) {
735 sway_log(SWAY_ERROR, "Failed to allocate libxkbcommon log message");
736 return;
737 }
738
739 va_copy(args_copy, args);
740 vsnprintf(error, length, format, args_copy);
741 va_end(args_copy);
742 721
743 if (error[length - 2] == '\n') { 722 size_t len = strlen(error);
744 error[length - 2] = '\0'; 723 if (error[len - 1] == '\n') {
724 error[len - 1] = '\0';
745 } 725 }
746 726
747 sway_log_importance_t importance = SWAY_DEBUG; 727 sway_log_importance_t importance = SWAY_DEBUG;
@@ -762,7 +742,7 @@ static void handle_xkb_context_log(struct xkb_context *context,
762 742
763struct xkb_keymap *sway_keyboard_compile_keymap(struct input_config *ic, 743struct xkb_keymap *sway_keyboard_compile_keymap(struct input_config *ic,
764 char **error) { 744 char **error) {
765 struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); 745 struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_SECURE_GETENV);
766 if (!sway_assert(context, "cannot create XKB context")) { 746 if (!sway_assert(context, "cannot create XKB context")) {
767 return NULL; 747 return NULL;
768 } 748 }
@@ -776,13 +756,8 @@ struct xkb_keymap *sway_keyboard_compile_keymap(struct input_config *ic,
776 if (!keymap_file) { 756 if (!keymap_file) {
777 sway_log_errno(SWAY_ERROR, "cannot read xkb file %s", ic->xkb_file); 757 sway_log_errno(SWAY_ERROR, "cannot read xkb file %s", ic->xkb_file);
778 if (error) { 758 if (error) {
779 size_t len = snprintf(NULL, 0, "cannot read xkb file %s: %s", 759 *error = format_str("cannot read xkb file %s: %s",
780 ic->xkb_file, strerror(errno)) + 1; 760 ic->xkb_file, strerror(errno));
781 *error = malloc(len);
782 if (*error) {
783 snprintf(*error, len, "cannot read xkb_file %s: %s",
784 ic->xkb_file, strerror(errno));
785 }
786 } 761 }
787 goto cleanup; 762 goto cleanup;
788 } 763 }
@@ -820,13 +795,12 @@ static void destroy_empty_wlr_keyboard_group(void *data) {
820 795
821static void sway_keyboard_group_remove(struct sway_keyboard *keyboard) { 796static void sway_keyboard_group_remove(struct sway_keyboard *keyboard) {
822 struct sway_input_device *device = keyboard->seat_device->input_device; 797 struct sway_input_device *device = keyboard->seat_device->input_device;
823 struct wlr_keyboard *wlr_keyboard = device->wlr_device->keyboard; 798 struct wlr_keyboard_group *wlr_group = keyboard->wlr->group;
824 struct wlr_keyboard_group *wlr_group = wlr_keyboard->group;
825 799
826 sway_log(SWAY_DEBUG, "Removing keyboard %s from group %p", 800 sway_log(SWAY_DEBUG, "Removing keyboard %s from group %p",
827 device->identifier, wlr_group); 801 device->identifier, wlr_group);
828 802
829 wlr_keyboard_group_remove_keyboard(wlr_keyboard->group, wlr_keyboard); 803 wlr_keyboard_group_remove_keyboard(keyboard->wlr->group, keyboard->wlr);
830 804
831 if (wl_list_empty(&wlr_group->devices)) { 805 if (wl_list_empty(&wlr_group->devices)) {
832 sway_log(SWAY_DEBUG, "Destroying empty keyboard group %p", 806 sway_log(SWAY_DEBUG, "Destroying empty keyboard group %p",
@@ -851,9 +825,7 @@ static void sway_keyboard_group_remove(struct sway_keyboard *keyboard) {
851} 825}
852 826
853static void sway_keyboard_group_remove_invalid(struct sway_keyboard *keyboard) { 827static void sway_keyboard_group_remove_invalid(struct sway_keyboard *keyboard) {
854 struct sway_input_device *device = keyboard->seat_device->input_device; 828 if (!keyboard->wlr->group) {
855 struct wlr_keyboard *wlr_keyboard = device->wlr_device->keyboard;
856 if (!wlr_keyboard->group) {
857 return; 829 return;
858 } 830 }
859 831
@@ -869,7 +841,7 @@ static void sway_keyboard_group_remove_invalid(struct sway_keyboard *keyboard) {
869 break; 841 break;
870 case KEYBOARD_GROUP_DEFAULT: /* fallthrough */ 842 case KEYBOARD_GROUP_DEFAULT: /* fallthrough */
871 case KEYBOARD_GROUP_SMART:; 843 case KEYBOARD_GROUP_SMART:;
872 struct wlr_keyboard_group *group = wlr_keyboard->group; 844 struct wlr_keyboard_group *group = keyboard->wlr->group;
873 if (!wlr_keyboard_keymaps_match(keyboard->keymap, group->keyboard.keymap) || 845 if (!wlr_keyboard_keymaps_match(keyboard->keymap, group->keyboard.keymap) ||
874 !repeat_info_match(keyboard, &group->keyboard)) { 846 !repeat_info_match(keyboard, &group->keyboard)) {
875 sway_keyboard_group_remove(keyboard); 847 sway_keyboard_group_remove(keyboard);
@@ -880,7 +852,6 @@ static void sway_keyboard_group_remove_invalid(struct sway_keyboard *keyboard) {
880 852
881static void sway_keyboard_group_add(struct sway_keyboard *keyboard) { 853static void sway_keyboard_group_add(struct sway_keyboard *keyboard) {
882 struct sway_input_device *device = keyboard->seat_device->input_device; 854 struct sway_input_device *device = keyboard->seat_device->input_device;
883 struct wlr_keyboard *wlr_keyboard = device->wlr_device->keyboard;
884 struct sway_seat *seat = keyboard->seat_device->sway_seat; 855 struct sway_seat *seat = keyboard->seat_device->sway_seat;
885 struct seat_config *sc = seat_get_config(seat); 856 struct seat_config *sc = seat_get_config(seat);
886 857
@@ -912,7 +883,7 @@ static void sway_keyboard_group_add(struct sway_keyboard *keyboard) {
912 repeat_info_match(keyboard, &wlr_group->keyboard)) { 883 repeat_info_match(keyboard, &wlr_group->keyboard)) {
913 sway_log(SWAY_DEBUG, "Adding keyboard %s to group %p", 884 sway_log(SWAY_DEBUG, "Adding keyboard %s to group %p",
914 device->identifier, wlr_group); 885 device->identifier, wlr_group);
915 wlr_keyboard_group_add_keyboard(wlr_group, wlr_keyboard); 886 wlr_keyboard_group_add_keyboard(wlr_group, keyboard->wlr);
916 return; 887 return;
917 } 888 }
918 break; 889 break;
@@ -960,7 +931,7 @@ static void sway_keyboard_group_add(struct sway_keyboard *keyboard) {
960 931
961 sway_log(SWAY_DEBUG, "Adding keyboard %s to group %p", 932 sway_log(SWAY_DEBUG, "Adding keyboard %s to group %p",
962 device->identifier, sway_group->wlr_group); 933 device->identifier, sway_group->wlr_group);
963 wlr_keyboard_group_add_keyboard(sway_group->wlr_group, wlr_keyboard); 934 wlr_keyboard_group_add_keyboard(sway_group->wlr_group, keyboard->wlr);
964 935
965 wl_list_insert(&seat->keyboard_groups, &sway_group->link); 936 wl_list_insert(&seat->keyboard_groups, &sway_group->link);
966 937
@@ -992,10 +963,8 @@ cleanup:
992void sway_keyboard_configure(struct sway_keyboard *keyboard) { 963void sway_keyboard_configure(struct sway_keyboard *keyboard) {
993 struct input_config *input_config = 964 struct input_config *input_config =
994 input_device_get_config(keyboard->seat_device->input_device); 965 input_device_get_config(keyboard->seat_device->input_device);
995 struct wlr_input_device *wlr_device =
996 keyboard->seat_device->input_device->wlr_device;
997 966
998 if (!sway_assert(!wlr_keyboard_group_from_wlr_keyboard(wlr_device->keyboard), 967 if (!sway_assert(!wlr_keyboard_group_from_wlr_keyboard(keyboard->wlr),
999 "sway_keyboard_configure should not be called with a " 968 "sway_keyboard_configure should not be called with a "
1000 "keyboard group's keyboard")) { 969 "keyboard group's keyboard")) {
1001 return; 970 return;
@@ -1037,11 +1006,11 @@ void sway_keyboard_configure(struct sway_keyboard *keyboard) {
1037 1006
1038 sway_keyboard_group_remove_invalid(keyboard); 1007 sway_keyboard_group_remove_invalid(keyboard);
1039 1008
1040 wlr_keyboard_set_keymap(wlr_device->keyboard, keyboard->keymap); 1009 wlr_keyboard_set_keymap(keyboard->wlr, keyboard->keymap);
1041 wlr_keyboard_set_repeat_info(wlr_device->keyboard, 1010 wlr_keyboard_set_repeat_info(keyboard->wlr,
1042 keyboard->repeat_rate, keyboard->repeat_delay); 1011 keyboard->repeat_rate, keyboard->repeat_delay);
1043 1012
1044 if (!wlr_device->keyboard->group) { 1013 if (!keyboard->wlr->group) {
1045 sway_keyboard_group_add(keyboard); 1014 sway_keyboard_group_add(keyboard);
1046 } 1015 }
1047 1016
@@ -1061,40 +1030,42 @@ void sway_keyboard_configure(struct sway_keyboard *keyboard) {
1061 } 1030 }
1062 } 1031 }
1063 if (locked_mods) { 1032 if (locked_mods) {
1064 wlr_keyboard_notify_modifiers(wlr_device->keyboard, 0, 0, 1033 wlr_keyboard_notify_modifiers(keyboard->wlr, 0, 0,
1065 locked_mods, 0); 1034 locked_mods, 0);
1066 uint32_t leds = 0; 1035 uint32_t leds = 0;
1067 for (uint32_t i = 0; i < WLR_LED_COUNT; ++i) { 1036 for (uint32_t i = 0; i < WLR_LED_COUNT; ++i) {
1068 if (xkb_state_led_index_is_active( 1037 if (xkb_state_led_index_is_active(keyboard->wlr->xkb_state,
1069 wlr_device->keyboard->xkb_state, 1038 keyboard->wlr->led_indexes[i])) {
1070 wlr_device->keyboard->led_indexes[i])) {
1071 leds |= (1 << i); 1039 leds |= (1 << i);
1072 } 1040 }
1073 } 1041 }
1074 if (wlr_device->keyboard->group) { 1042 if (keyboard->wlr->group) {
1075 wlr_keyboard_led_update( 1043 wlr_keyboard_led_update(&keyboard->wlr->group->keyboard, leds);
1076 &wlr_device->keyboard->group->keyboard, leds);
1077 } else { 1044 } else {
1078 wlr_keyboard_led_update(wlr_device->keyboard, leds); 1045 wlr_keyboard_led_update(keyboard->wlr, leds);
1079 } 1046 }
1080 } 1047 }
1081 } else { 1048 } else {
1082 xkb_keymap_unref(keymap); 1049 xkb_keymap_unref(keymap);
1083 sway_keyboard_group_remove_invalid(keyboard); 1050 sway_keyboard_group_remove_invalid(keyboard);
1084 if (!wlr_device->keyboard->group) { 1051 if (!keyboard->wlr->group) {
1085 sway_keyboard_group_add(keyboard); 1052 sway_keyboard_group_add(keyboard);
1086 } 1053 }
1087 } 1054 }
1088 1055
1056 // If the seat has no active keyboard, set this one
1089 struct wlr_seat *seat = keyboard->seat_device->sway_seat->wlr_seat; 1057 struct wlr_seat *seat = keyboard->seat_device->sway_seat->wlr_seat;
1090 wlr_seat_set_keyboard(seat, wlr_device->keyboard); 1058 struct wlr_keyboard *current_keyboard = seat->keyboard_state.keyboard;
1059 if (current_keyboard == NULL) {
1060 wlr_seat_set_keyboard(seat, keyboard->wlr);
1061 }
1091 1062
1092 wl_list_remove(&keyboard->keyboard_key.link); 1063 wl_list_remove(&keyboard->keyboard_key.link);
1093 wl_signal_add(&wlr_device->keyboard->events.key, &keyboard->keyboard_key); 1064 wl_signal_add(&keyboard->wlr->events.key, &keyboard->keyboard_key);
1094 keyboard->keyboard_key.notify = handle_keyboard_key; 1065 keyboard->keyboard_key.notify = handle_keyboard_key;
1095 1066
1096 wl_list_remove(&keyboard->keyboard_modifiers.link); 1067 wl_list_remove(&keyboard->keyboard_modifiers.link);
1097 wl_signal_add(&wlr_device->keyboard->events.modifiers, 1068 wl_signal_add(&keyboard->wlr->events.modifiers,
1098 &keyboard->keyboard_modifiers); 1069 &keyboard->keyboard_modifiers);
1099 keyboard->keyboard_modifiers.notify = handle_keyboard_modifiers; 1070 keyboard->keyboard_modifiers.notify = handle_keyboard_modifiers;
1100 1071
@@ -1111,12 +1082,11 @@ void sway_keyboard_destroy(struct sway_keyboard *keyboard) {
1111 if (!keyboard) { 1082 if (!keyboard) {
1112 return; 1083 return;
1113 } 1084 }
1114 if (keyboard->seat_device->input_device->wlr_device->keyboard->group) { 1085 if (keyboard->wlr->group) {
1115 sway_keyboard_group_remove(keyboard); 1086 sway_keyboard_group_remove(keyboard);
1116 } 1087 }
1117 struct wlr_seat *wlr_seat = keyboard->seat_device->sway_seat->wlr_seat; 1088 struct wlr_seat *wlr_seat = keyboard->seat_device->sway_seat->wlr_seat;
1118 struct sway_input_device *device = keyboard->seat_device->input_device; 1089 if (wlr_seat_get_keyboard(wlr_seat) == keyboard->wlr) {
1119 if (wlr_seat_get_keyboard(wlr_seat) == device->wlr_device->keyboard) {
1120 wlr_seat_set_keyboard(wlr_seat, NULL); 1090 wlr_seat_set_keyboard(wlr_seat, NULL);
1121 } 1091 }
1122 if (keyboard->keymap) { 1092 if (keyboard->keymap) {
diff --git a/sway/input/libinput.c b/sway/input/libinput.c
index 3c0f359d..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 }
218 247
248 struct libinput_device *device =
249 wlr_libinput_get_device_handle(input_device->wlr_device);
250 sway_log(SWAY_DEBUG, "sway_input_configure_libinput_device('%s' on '%s')",
251 ic->identifier, input_device->identifier);
252
253 bool changed = configure_send_events(device, ic);
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);
diff --git a/sway/input/seat.c b/sway/input/seat.c
index 11c78154..da4bb12a 100644
--- a/sway/input/seat.c
+++ b/sway/input/seat.c
@@ -1,12 +1,12 @@
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>
@@ -17,7 +17,7 @@
17#include "list.h" 17#include "list.h"
18#include "log.h" 18#include "log.h"
19#include "sway/config.h" 19#include "sway/config.h"
20#include "sway/desktop.h" 20#include "sway/scene_descriptor.h"
21#include "sway/input/cursor.h" 21#include "sway/input/cursor.h"
22#include "sway/input/input-manager.h" 22#include "sway/input/input-manager.h"
23#include "sway/input/keyboard.h" 23#include "sway/input/keyboard.h"
@@ -67,6 +67,12 @@ static void seat_node_destroy(struct sway_seat_node *seat_node) {
67} 67}
68 68
69void 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
70 if (seat == config->handler_context.seat) { 76 if (seat == config->handler_context.seat) {
71 config->handler_context.seat = input_manager_get_default_seat(); 77 config->handler_context.seat = input_manager_get_default_seat();
72 } 78 }
@@ -87,10 +93,11 @@ void seat_destroy(struct sway_seat *seat) {
87 wl_list_remove(&seat->request_set_selection.link); 93 wl_list_remove(&seat->request_set_selection.link);
88 wl_list_remove(&seat->request_set_primary_selection.link); 94 wl_list_remove(&seat->request_set_primary_selection.link);
89 wl_list_remove(&seat->link); 95 wl_list_remove(&seat->link);
90 wlr_seat_destroy(seat->wlr_seat); 96 wl_list_remove(&seat->destroy.link);
91 for (int i = 0; i < seat->deferred_bindings->length; i++) { 97 for (int i = 0; i < seat->deferred_bindings->length; i++) {
92 free_sway_binding(seat->deferred_bindings->items[i]); 98 free_sway_binding(seat->deferred_bindings->items[i]);
93 } 99 }
100 wlr_scene_node_destroy(&seat->scene_tree->node);
94 list_free(seat->deferred_bindings); 101 list_free(seat->deferred_bindings);
95 free(seat->prev_workspace_name); 102 free(seat->prev_workspace_name);
96 free(seat); 103 free(seat);
@@ -98,21 +105,10 @@ void seat_destroy(struct sway_seat *seat) {
98 105
99void seat_idle_notify_activity(struct sway_seat *seat, 106void seat_idle_notify_activity(struct sway_seat *seat,
100 enum sway_input_idle_source source) { 107 enum sway_input_idle_source source) {
101 uint32_t mask = seat->idle_inhibit_sources; 108 if ((source & seat->idle_inhibit_sources) == 0) {
102 struct wlr_idle_timeout *timeout; 109 return;
103 int ntimers = 0, nidle = 0;
104 wl_list_for_each(timeout, &server.idle->idle_timers, link) {
105 ++ntimers;
106 if (timeout->idle_state) {
107 ++nidle;
108 }
109 }
110 if (nidle == ntimers) {
111 mask = seat->idle_wake_sources;
112 }
113 if ((source & mask) > 0) {
114 wlr_idle_notify_activity(server.idle, seat->wlr_seat);
115 } 110 }
111 wlr_idle_notifier_v1_notify_activity(server.idle_notifier_v1, seat->wlr_seat);
116} 112}
117 113
118/** 114/**
@@ -142,7 +138,7 @@ static struct sway_keyboard *sway_keyboard_for_wlr_keyboard(
142 if (input_device->wlr_device->type != WLR_INPUT_DEVICE_KEYBOARD) { 138 if (input_device->wlr_device->type != WLR_INPUT_DEVICE_KEYBOARD) {
143 continue; 139 continue;
144 } 140 }
145 if (input_device->wlr_device->keyboard == wlr_keyboard) { 141 if (input_device->wlr_device == &wlr_keyboard->base) {
146 return seat_device->keyboard; 142 return seat_device->keyboard;
147 } 143 }
148 } 144 }
@@ -150,7 +146,7 @@ static struct sway_keyboard *sway_keyboard_for_wlr_keyboard(
150 wl_list_for_each(group, &seat->keyboard_groups, link) { 146 wl_list_for_each(group, &seat->keyboard_groups, link) {
151 struct sway_input_device *input_device = 147 struct sway_input_device *input_device =
152 group->seat_device->input_device; 148 group->seat_device->input_device;
153 if (input_device->wlr_device->keyboard == wlr_keyboard) { 149 if (input_device->wlr_device == &wlr_keyboard->base) {
154 return group->seat_device->keyboard; 150 return group->seat_device->keyboard;
155 } 151 }
156 } 152 }
@@ -174,11 +170,11 @@ static void seat_keyboard_notify_enter(struct sway_seat *seat,
174 state->pressed_keycodes, state->npressed, &keyboard->modifiers); 170 state->pressed_keycodes, state->npressed, &keyboard->modifiers);
175} 171}
176 172
177static void seat_tablet_pads_notify_enter(struct sway_seat *seat, 173static void seat_tablet_pads_set_focus(struct sway_seat *seat,
178 struct wlr_surface *surface) { 174 struct wlr_surface *surface) {
179 struct sway_seat_device *seat_device; 175 struct sway_seat_device *seat_device;
180 wl_list_for_each(seat_device, &seat->devices, link) { 176 wl_list_for_each(seat_device, &seat->devices, link) {
181 sway_tablet_pad_notify_enter(seat_device->tablet_pad, surface); 177 sway_tablet_pad_set_focus(seat_device->tablet_pad, surface);
182 } 178 }
183} 179}
184 180
@@ -194,7 +190,7 @@ static void seat_send_focus(struct sway_node *node, struct sway_seat *seat) {
194 node->sway_container->view : NULL; 190 node->sway_container->view : NULL;
195 191
196 if (view && seat_is_input_allowed(seat, view->surface)) { 192 if (view && seat_is_input_allowed(seat, view->surface)) {
197#if HAVE_XWAYLAND 193#if WLR_HAS_XWAYLAND
198 if (view->type == SWAY_VIEW_XWAYLAND) { 194 if (view->type == SWAY_VIEW_XWAYLAND) {
199 struct wlr_xwayland *xwayland = server.xwayland.wlr_xwayland; 195 struct wlr_xwayland *xwayland = server.xwayland.wlr_xwayland;
200 wlr_xwayland_set_seat(xwayland, seat->wlr_seat); 196 wlr_xwayland_set_seat(xwayland, seat->wlr_seat);
@@ -202,7 +198,7 @@ static void seat_send_focus(struct sway_node *node, struct sway_seat *seat) {
202#endif 198#endif
203 199
204 seat_keyboard_notify_enter(seat, view->surface); 200 seat_keyboard_notify_enter(seat, view->surface);
205 seat_tablet_pads_notify_enter(seat, view->surface); 201 seat_tablet_pads_set_focus(seat, view->surface);
206 sway_input_method_relay_set_focus(&seat->im_relay, view->surface); 202 sway_input_method_relay_set_focus(&seat->im_relay, view->surface);
207 203
208 struct wlr_pointer_constraint_v1 *constraint = 204 struct wlr_pointer_constraint_v1 *constraint =
@@ -212,15 +208,6 @@ static void seat_send_focus(struct sway_node *node, struct sway_seat *seat) {
212 } 208 }
213} 209}
214 210
215void sway_force_focus(struct wlr_surface *surface) {
216 struct sway_seat *seat;
217 wl_list_for_each(seat, &server.input->seats, link) {
218 seat_keyboard_notify_enter(seat, surface);
219 seat_tablet_pads_notify_enter(seat, surface);
220 sway_input_method_relay_set_focus(&seat->im_relay, surface);
221 }
222}
223
224void seat_for_each_node(struct sway_seat *seat, 211void seat_for_each_node(struct sway_seat *seat,
225 void (*f)(struct sway_node *node, void *data), void *data) { 212 void (*f)(struct sway_node *node, void *data), void *data) {
226 struct sway_seat_node *current = NULL; 213 struct sway_seat_node *current = NULL;
@@ -372,25 +359,15 @@ static void handle_new_node(struct wl_listener *listener, void *data) {
372 seat_node_from_node(seat, node); 359 seat_node_from_node(seat, node);
373} 360}
374 361
375static 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) {
376 if (!icon->wlr_drag_icon->mapped) { 363 struct wlr_drag_icon *wlr_icon = scene_descriptor_try_get(node, SWAY_SCENE_DESC_DRAG_ICON);
377 return;
378 }
379 desktop_damage_surface(icon->wlr_drag_icon->surface, icon->x, icon->y, true);
380}
381
382void drag_icon_update_position(struct sway_drag_icon *icon) {
383 drag_icon_damage_whole(icon);
384
385 struct wlr_drag_icon *wlr_icon = icon->wlr_drag_icon;
386 struct sway_seat *seat = icon->seat;
387 struct wlr_cursor *cursor = seat->cursor->cursor; 364 struct wlr_cursor *cursor = seat->cursor->cursor;
365
388 switch (wlr_icon->drag->grab_type) { 366 switch (wlr_icon->drag->grab_type) {
389 case WLR_DRAG_GRAB_KEYBOARD: 367 case WLR_DRAG_GRAB_KEYBOARD:
390 return; 368 return;
391 case WLR_DRAG_GRAB_KEYBOARD_POINTER: 369 case WLR_DRAG_GRAB_KEYBOARD_POINTER:
392 icon->x = cursor->x + wlr_icon->surface->sx; 370 wlr_scene_node_set_position(node, cursor->x, cursor->y);
393 icon->y = cursor->y + wlr_icon->surface->sy;
394 break; 371 break;
395 case WLR_DRAG_GRAB_KEYBOARD_TOUCH:; 372 case WLR_DRAG_GRAB_KEYBOARD_TOUCH:;
396 struct wlr_touch_point *point = 373 struct wlr_touch_point *point =
@@ -398,39 +375,15 @@ void drag_icon_update_position(struct sway_drag_icon *icon) {
398 if (point == NULL) { 375 if (point == NULL) {
399 return; 376 return;
400 } 377 }
401 icon->x = seat->touch_x + wlr_icon->surface->sx; 378 wlr_scene_node_set_position(node, seat->touch_x, seat->touch_y);
402 icon->y = seat->touch_y + wlr_icon->surface->sy;
403 } 379 }
404
405 drag_icon_damage_whole(icon);
406} 380}
407 381
408static void drag_icon_handle_surface_commit(struct wl_listener *listener, 382void drag_icons_update_position(struct sway_seat *seat) {
409 void *data) { 383 struct wlr_scene_node *node;
410 struct sway_drag_icon *icon = 384 wl_list_for_each(node, &seat->drag_icons->children, link) {
411 wl_container_of(listener, icon, surface_commit); 385 drag_icon_update_position(seat, node);
412 drag_icon_update_position(icon); 386 }
413}
414
415static void drag_icon_handle_map(struct wl_listener *listener, void *data) {
416 struct sway_drag_icon *icon = wl_container_of(listener, icon, map);
417 drag_icon_damage_whole(icon);
418}
419
420static void drag_icon_handle_unmap(struct wl_listener *listener, void *data) {
421 struct sway_drag_icon *icon = wl_container_of(listener, icon, unmap);
422 drag_icon_damage_whole(icon);
423}
424
425static void drag_icon_handle_destroy(struct wl_listener *listener, void *data) {
426 struct sway_drag_icon *icon = wl_container_of(listener, icon, destroy);
427 icon->wlr_drag_icon->data = NULL;
428 wl_list_remove(&icon->link);
429 wl_list_remove(&icon->surface_commit.link);
430 wl_list_remove(&icon->unmap.link);
431 wl_list_remove(&icon->map.link);
432 wl_list_remove(&icon->destroy.link);
433 free(icon);
434} 387}
435 388
436static void drag_handle_destroy(struct wl_listener *listener, void *data) { 389static void drag_handle_destroy(struct wl_listener *listener, void *data) {
@@ -502,27 +455,20 @@ static void handle_start_drag(struct wl_listener *listener, void *data) {
502 455
503 struct wlr_drag_icon *wlr_drag_icon = wlr_drag->icon; 456 struct wlr_drag_icon *wlr_drag_icon = wlr_drag->icon;
504 if (wlr_drag_icon != NULL) { 457 if (wlr_drag_icon != NULL) {
505 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);
506 if (icon == NULL) { 459 if (!tree) {
507 sway_log(SWAY_ERROR, "Allocation failed"); 460 sway_log(SWAY_ERROR, "Failed to allocate a drag icon scene tree");
508 return; 461 return;
509 } 462 }
510 icon->seat = seat;
511 icon->wlr_drag_icon = wlr_drag_icon;
512 wlr_drag_icon->data = icon;
513 463
514 icon->surface_commit.notify = drag_icon_handle_surface_commit; 464 if (!scene_descriptor_assign(&tree->node, SWAY_SCENE_DESC_DRAG_ICON,
515 wl_signal_add(&wlr_drag_icon->surface->events.commit, &icon->surface_commit); 465 wlr_drag_icon)) {
516 icon->unmap.notify = drag_icon_handle_unmap; 466 sway_log(SWAY_ERROR, "Failed to allocate a drag icon scene descriptor");
517 wl_signal_add(&wlr_drag_icon->events.unmap, &icon->unmap); 467 wlr_scene_node_destroy(&tree->node);
518 icon->map.notify = drag_icon_handle_map; 468 return;
519 wl_signal_add(&wlr_drag_icon->events.map, &icon->map); 469 }
520 icon->destroy.notify = drag_icon_handle_destroy;
521 wl_signal_add(&wlr_drag_icon->events.destroy, &icon->destroy);
522
523 wl_list_insert(&root->drag_icons, &icon->link);
524 470
525 drag_icon_update_position(icon); 471 drag_icon_update_position(seat, &tree->node);
526 } 472 }
527 seatop_begin_default(seat); 473 seatop_begin_default(seat);
528} 474}
@@ -569,8 +515,18 @@ struct sway_seat *seat_create(const char *seat_name) {
569 return NULL; 515 return NULL;
570 } 516 }
571 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
572 seat->wlr_seat = wlr_seat_create(server.wl_display, seat_name); 527 seat->wlr_seat = wlr_seat_create(server.wl_display, seat_name);
573 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);
574 free(seat); 530 free(seat);
575 return NULL; 531 return NULL;
576 } 532 }
@@ -578,11 +534,15 @@ struct sway_seat *seat_create(const char *seat_name) {
578 534
579 seat->cursor = sway_cursor_create(seat); 535 seat->cursor = sway_cursor_create(seat);
580 if (!seat->cursor) { 536 if (!seat->cursor) {
537 wlr_scene_node_destroy(&seat->scene_tree->node);
581 wlr_seat_destroy(seat->wlr_seat); 538 wlr_seat_destroy(seat->wlr_seat);
582 free(seat); 539 free(seat);
583 return NULL; 540 return NULL;
584 } 541 }
585 542
543 seat->destroy.notify = handle_seat_destroy;
544 wl_signal_add(&seat->wlr_seat->events.destroy, &seat->destroy);
545
586 seat->idle_inhibit_sources = seat->idle_wake_sources = 546 seat->idle_inhibit_sources = seat->idle_wake_sources =
587 IDLE_SOURCE_KEYBOARD | 547 IDLE_SOURCE_KEYBOARD |
588 IDLE_SOURCE_POINTER | 548 IDLE_SOURCE_POINTER |
@@ -656,7 +616,7 @@ static void seat_update_capabilities(struct sway_seat *seat) {
656 case WLR_INPUT_DEVICE_TOUCH: 616 case WLR_INPUT_DEVICE_TOUCH:
657 caps |= WL_SEAT_CAPABILITY_TOUCH; 617 caps |= WL_SEAT_CAPABILITY_TOUCH;
658 break; 618 break;
659 case WLR_INPUT_DEVICE_TABLET_TOOL: 619 case WLR_INPUT_DEVICE_TABLET:
660 caps |= WL_SEAT_CAPABILITY_POINTER; 620 caps |= WL_SEAT_CAPABILITY_POINTER;
661 break; 621 break;
662 case WLR_INPUT_DEVICE_SWITCH: 622 case WLR_INPUT_DEVICE_SWITCH:
@@ -674,7 +634,7 @@ static void seat_update_capabilities(struct sway_seat *seat) {
674 } else { 634 } else {
675 wlr_seat_set_capabilities(seat->wlr_seat, caps); 635 wlr_seat_set_capabilities(seat->wlr_seat, caps);
676 if ((previous_caps & WL_SEAT_CAPABILITY_POINTER) == 0) { 636 if ((previous_caps & WL_SEAT_CAPABILITY_POINTER) == 0) {
677 cursor_set_image(seat->cursor, "left_ptr", NULL); 637 cursor_set_image(seat->cursor, "default", NULL);
678 } 638 }
679 } 639 }
680} 640}
@@ -714,19 +674,28 @@ static const char *get_builtin_output_name(void) {
714static 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) {
715 switch (seat_device->input_device->wlr_device->type) { 675 switch (seat_device->input_device->wlr_device->type) {
716 case WLR_INPUT_DEVICE_TOUCH: 676 case WLR_INPUT_DEVICE_TOUCH:
717 case WLR_INPUT_DEVICE_TABLET_TOOL: 677 case WLR_INPUT_DEVICE_TABLET:
718 return true; 678 return true;
719 default: 679 default:
720 return false; 680 return false;
721 } 681 }
722} 682}
723 683
724static void seat_apply_input_config(struct sway_seat *seat, 684static void seat_apply_input_mapping(struct sway_seat *seat,
725 struct sway_seat_device *sway_device) { 685 struct sway_seat_device *sway_device) {
726 struct input_config *ic = 686 struct input_config *ic =
727 input_device_get_config(sway_device->input_device); 687 input_device_get_config(sway_device->input_device);
728 688
729 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",
730 sway_device->input_device->identifier); 699 sway_device->input_device->identifier);
731 700
732 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;
@@ -745,15 +714,16 @@ static void seat_apply_input_config(struct sway_seat *seat,
745 struct wlr_input_device *dev = sway_device->input_device->wlr_device; 714 struct wlr_input_device *dev = sway_device->input_device->wlr_device;
746 switch (dev->type) { 715 switch (dev->type) {
747 case WLR_INPUT_DEVICE_POINTER: 716 case WLR_INPUT_DEVICE_POINTER:
748 mapped_to_output = dev->pointer->output_name; 717 mapped_to_output = wlr_pointer_from_input_device(dev)->output_name;
749 break; 718 break;
750 case WLR_INPUT_DEVICE_TOUCH: 719 case WLR_INPUT_DEVICE_TOUCH:
751 mapped_to_output = dev->touch->output_name; 720 mapped_to_output = wlr_touch_from_input_device(dev)->output_name;
752 break; 721 break;
753 default: 722 default:
754 mapped_to_output = NULL; 723 mapped_to_output = NULL;
755 break; 724 break;
756 } 725 }
726#if WLR_HAS_LIBINPUT_BACKEND
757 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) &&
758 sway_libinput_device_is_builtin(sway_device->input_device)) { 728 sway_libinput_device_is_builtin(sway_device->input_device)) {
759 mapped_to_output = get_builtin_output_name(); 729 mapped_to_output = get_builtin_output_name();
@@ -762,6 +732,10 @@ static void seat_apply_input_config(struct sway_seat *seat,
762 mapped_to_output, sway_device->input_device->identifier); 732 mapped_to_output, sway_device->input_device->identifier);
763 } 733 }
764 } 734 }
735#else
736 (void)is_touch_or_tablet_tool;
737 (void)get_builtin_output_name;
738#endif
765 if (mapped_to_output == NULL) { 739 if (mapped_to_output == NULL) {
766 return; 740 return;
767 } 741 }
@@ -805,12 +779,9 @@ static void seat_apply_input_config(struct sway_seat *seat,
805 779
806static void seat_configure_pointer(struct sway_seat *seat, 780static void seat_configure_pointer(struct sway_seat *seat,
807 struct sway_seat_device *sway_device) { 781 struct sway_seat_device *sway_device) {
808 if ((seat->wlr_seat->capabilities & WL_SEAT_CAPABILITY_POINTER) == 0) { 782 seat_configure_xcursor(seat);
809 seat_configure_xcursor(seat);
810 }
811 wlr_cursor_attach_input_device(seat->cursor->cursor, 783 wlr_cursor_attach_input_device(seat->cursor->cursor,
812 sway_device->input_device->wlr_device); 784 sway_device->input_device->wlr_device);
813 seat_apply_input_config(seat, sway_device);
814 wl_event_source_timer_update( 785 wl_event_source_timer_update(
815 seat->cursor->hide_source, cursor_get_timeout(seat->cursor)); 786 seat->cursor->hide_source, cursor_get_timeout(seat->cursor));
816} 787}
@@ -821,8 +792,15 @@ static void seat_configure_keyboard(struct sway_seat *seat,
821 sway_keyboard_create(seat, seat_device); 792 sway_keyboard_create(seat, seat_device);
822 } 793 }
823 sway_keyboard_configure(seat_device->keyboard); 794 sway_keyboard_configure(seat_device->keyboard);
824 wlr_seat_set_keyboard(seat->wlr_seat, 795
825 seat_device->input_device->wlr_device->keyboard); 796 // We only need to update the current keyboard, as the rest will be updated
797 // as they are activated.
798 struct wlr_keyboard *wlr_keyboard =
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 }
826 804
827 // force notify reenter to pick up the new configuration. This reuses 805 // force notify reenter to pick up the new configuration. This reuses
828 // the current focused surface to avoid breaking input grabs. 806 // the current focused surface to avoid breaking input grabs.
@@ -838,7 +816,6 @@ static void seat_configure_switch(struct sway_seat *seat,
838 if (!seat_device->switch_device) { 816 if (!seat_device->switch_device) {
839 sway_switch_create(seat, seat_device); 817 sway_switch_create(seat, seat_device);
840 } 818 }
841 seat_apply_input_config(seat, seat_device);
842 sway_switch_configure(seat_device->switch_device); 819 sway_switch_configure(seat_device->switch_device);
843} 820}
844 821
@@ -846,7 +823,6 @@ static void seat_configure_touch(struct sway_seat *seat,
846 struct sway_seat_device *sway_device) { 823 struct sway_seat_device *sway_device) {
847 wlr_cursor_attach_input_device(seat->cursor->cursor, 824 wlr_cursor_attach_input_device(seat->cursor->cursor,
848 sway_device->input_device->wlr_device); 825 sway_device->input_device->wlr_device);
849 seat_apply_input_config(seat, sway_device);
850} 826}
851 827
852static void seat_configure_tablet_tool(struct sway_seat *seat, 828static void seat_configure_tablet_tool(struct sway_seat *seat,
@@ -857,7 +833,6 @@ static void seat_configure_tablet_tool(struct sway_seat *seat,
857 sway_configure_tablet(sway_device->tablet); 833 sway_configure_tablet(sway_device->tablet);
858 wlr_cursor_attach_input_device(seat->cursor->cursor, 834 wlr_cursor_attach_input_device(seat->cursor->cursor,
859 sway_device->input_device->wlr_device); 835 sway_device->input_device->wlr_device);
860 seat_apply_input_config(seat, sway_device);
861} 836}
862 837
863static void seat_configure_tablet_pad(struct sway_seat *seat, 838static void seat_configure_tablet_pad(struct sway_seat *seat,
@@ -907,13 +882,25 @@ void seat_configure_device(struct sway_seat *seat,
907 case WLR_INPUT_DEVICE_TOUCH: 882 case WLR_INPUT_DEVICE_TOUCH:
908 seat_configure_touch(seat, seat_device); 883 seat_configure_touch(seat, seat_device);
909 break; 884 break;
910 case WLR_INPUT_DEVICE_TABLET_TOOL: 885 case WLR_INPUT_DEVICE_TABLET:
911 seat_configure_tablet_tool(seat, seat_device); 886 seat_configure_tablet_tool(seat, seat_device);
912 break; 887 break;
913 case WLR_INPUT_DEVICE_TABLET_PAD: 888 case WLR_INPUT_DEVICE_TABLET_PAD:
914 seat_configure_tablet_pad(seat, seat_device); 889 seat_configure_tablet_pad(seat, seat_device);
915 break; 890 break;
916 } 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);
917} 904}
918 905
919void seat_reset_device(struct sway_seat *seat, 906void seat_reset_device(struct sway_seat *seat,
@@ -934,7 +921,7 @@ void seat_reset_device(struct sway_seat *seat,
934 case WLR_INPUT_DEVICE_TOUCH: 921 case WLR_INPUT_DEVICE_TOUCH:
935 seat_reset_input_config(seat, seat_device); 922 seat_reset_input_config(seat, seat_device);
936 break; 923 break;
937 case WLR_INPUT_DEVICE_TABLET_TOOL: 924 case WLR_INPUT_DEVICE_TABLET:
938 seat_reset_input_config(seat, seat_device); 925 seat_reset_input_config(seat, seat_device);
939 break; 926 break;
940 case WLR_INPUT_DEVICE_TABLET_PAD: 927 case WLR_INPUT_DEVICE_TABLET_PAD:
@@ -1015,7 +1002,7 @@ void seat_configure_xcursor(struct sway_seat *seat) {
1015 setenv("XCURSOR_THEME", cursor_theme, 1); 1002 setenv("XCURSOR_THEME", cursor_theme, 1);
1016 } 1003 }
1017 1004
1018#if HAVE_XWAYLAND 1005#if WLR_HAS_XWAYLAND
1019 if (server.xwayland.wlr_xwayland && (!server.xwayland.xcursor_manager || 1006 if (server.xwayland.wlr_xwayland && (!server.xwayland.xcursor_manager ||
1020 !xcursor_manager_is_named(server.xwayland.xcursor_manager, 1007 !xcursor_manager_is_named(server.xwayland.xcursor_manager,
1021 cursor_theme) || 1008 cursor_theme) ||
@@ -1030,7 +1017,7 @@ void seat_configure_xcursor(struct sway_seat *seat) {
1030 1017
1031 wlr_xcursor_manager_load(server.xwayland.xcursor_manager, 1); 1018 wlr_xcursor_manager_load(server.xwayland.xcursor_manager, 1);
1032 struct wlr_xcursor *xcursor = wlr_xcursor_manager_get_xcursor( 1019 struct wlr_xcursor *xcursor = wlr_xcursor_manager_get_xcursor(
1033 server.xwayland.xcursor_manager, "left_ptr", 1); 1020 server.xwayland.xcursor_manager, "default", 1);
1034 if (xcursor != NULL) { 1021 if (xcursor != NULL) {
1035 struct wlr_xcursor_image *image = xcursor->images[0]; 1022 struct wlr_xcursor_image *image = xcursor->images[0];
1036 wlr_xwayland_set_cursor( 1023 wlr_xwayland_set_cursor(
@@ -1056,33 +1043,35 @@ void seat_configure_xcursor(struct sway_seat *seat) {
1056 sway_log(SWAY_ERROR, 1043 sway_log(SWAY_ERROR,
1057 "Cannot create XCursor manager for theme '%s'", cursor_theme); 1044 "Cannot create XCursor manager for theme '%s'", cursor_theme);
1058 } 1045 }
1059 }
1060 1046
1061 for (int i = 0; i < root->outputs->length; ++i) { 1047
1062 struct sway_output *sway_output = root->outputs->items[i]; 1048 for (int i = 0; i < root->outputs->length; ++i) {
1063 struct wlr_output *output = sway_output->wlr_output; 1049 struct sway_output *sway_output = root->outputs->items[i];
1064 bool result = 1050 struct wlr_output *output = sway_output->wlr_output;
1065 wlr_xcursor_manager_load(seat->cursor->xcursor_manager, 1051 bool result =
1066 output->scale); 1052 wlr_xcursor_manager_load(seat->cursor->xcursor_manager,
1067 if (!result) { 1053 output->scale);
1068 sway_log(SWAY_ERROR, 1054 if (!result) {
1069 "Cannot load xcursor theme for output '%s' with scale %f", 1055 sway_log(SWAY_ERROR,
1070 output->name, output->scale); 1056 "Cannot load xcursor theme for output '%s' with scale %f",
1057 output->name, output->scale);
1058 }
1071 } 1059 }
1072 }
1073 1060
1074 // 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
1075 cursor_set_image(seat->cursor, NULL, NULL); 1062 cursor_set_image(seat->cursor, NULL, NULL);
1076 cursor_set_image(seat->cursor, "left_ptr", NULL); 1063 cursor_set_image(seat->cursor, "default", NULL);
1077 wlr_cursor_warp(seat->cursor->cursor, NULL, seat->cursor->cursor->x, 1064 wlr_cursor_warp(seat->cursor->cursor, NULL, seat->cursor->cursor->x,
1078 seat->cursor->cursor->y); 1065 seat->cursor->cursor->y);
1066 }
1079} 1067}
1080 1068
1081bool seat_is_input_allowed(struct sway_seat *seat, 1069bool seat_is_input_allowed(struct sway_seat *seat,
1082 struct wlr_surface *surface) { 1070 struct wlr_surface *surface) {
1083 struct wl_client *client = wl_resource_get_client(surface->resource); 1071 if (server.session_lock.lock) {
1084 return seat->exclusive_client == client || 1072 return sway_session_lock_has_surface(server.session_lock.lock, surface);
1085 (seat->exclusive_client == NULL && !server.session_lock.locked); 1073 }
1074 return true;
1086} 1075}
1087 1076
1088static void send_unfocus(struct sway_container *con, void *data) { 1077static void send_unfocus(struct sway_container *con, void *data) {
@@ -1141,15 +1130,7 @@ void seat_set_raw_focus(struct sway_seat *seat, struct sway_node *node) {
1141 } 1130 }
1142} 1131}
1143 1132
1144void 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) {
1145 if (seat->focused_layer) {
1146 struct wlr_layer_surface_v1 *layer = seat->focused_layer;
1147 seat_set_focus_layer(seat, NULL);
1148 seat_set_focus(seat, node);
1149 seat_set_focus_layer(seat, layer);
1150 return;
1151 }
1152
1153 struct sway_node *last_focus = seat_get_focus(seat); 1134 struct sway_node *last_focus = seat_get_focus(seat);
1154 if (last_focus == node) { 1135 if (last_focus == node) {
1155 return; 1136 return;
@@ -1183,11 +1164,6 @@ void seat_set_focus(struct sway_seat *seat, struct sway_node *node) {
1183 return; 1164 return;
1184 } 1165 }
1185 1166
1186 // Deny setting focus when an input grab or lockscreen is active
1187 if (container && container->view && !seat_is_input_allowed(seat, container->view->surface)) {
1188 return;
1189 }
1190
1191 struct sway_output *new_output = 1167 struct sway_output *new_output =
1192 new_workspace ? new_workspace->output : NULL; 1168 new_workspace ? new_workspace->output : NULL;
1193 1169
@@ -1287,6 +1263,24 @@ void seat_set_focus(struct sway_seat *seat, struct sway_node *node) {
1287 } 1263 }
1288} 1264}
1289 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
1290void seat_set_focus_container(struct sway_seat *seat, 1284void seat_set_focus_container(struct sway_seat *seat,
1291 struct sway_container *con) { 1285 struct sway_container *con) {
1292 seat_set_focus(seat, con ? &con->node : NULL); 1286 seat_set_focus(seat, con ? &con->node : NULL);
@@ -1312,7 +1306,7 @@ void seat_set_focus_surface(struct sway_seat *seat,
1312 } 1306 }
1313 1307
1314 sway_input_method_relay_set_focus(&seat->im_relay, surface); 1308 sway_input_method_relay_set_focus(&seat->im_relay, surface);
1315 seat_tablet_pads_notify_enter(seat, surface); 1309 seat_tablet_pads_set_focus(seat, surface);
1316} 1310}
1317 1311
1318void seat_set_focus_layer(struct sway_seat *seat, 1312void seat_set_focus_layer(struct sway_seat *seat,
@@ -1326,28 +1320,23 @@ void seat_set_focus_layer(struct sway_seat *seat,
1326 seat_set_focus(seat, previous); 1320 seat_set_focus(seat, previous);
1327 } 1321 }
1328 return; 1322 return;
1329 } else if (!layer || seat->focused_layer == layer) { 1323 } else if (!layer) {
1330 return; 1324 return;
1331 } 1325 }
1332 assert(layer->mapped); 1326 assert(layer->surface->mapped);
1333 seat_set_focus_surface(seat, layer->surface, true); 1327 if (layer->current.layer >= ZWLR_LAYER_SHELL_V1_LAYER_TOP &&
1334 if (layer->current.layer >= ZWLR_LAYER_SHELL_V1_LAYER_TOP) { 1328 layer->current.keyboard_interactive
1335 seat->focused_layer = layer; 1329 == ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_EXCLUSIVE) {
1330 seat->has_exclusive_layer = true;
1336 } 1331 }
1337} 1332 if (seat->focused_layer == layer) {
1338
1339void seat_set_exclusive_client(struct sway_seat *seat,
1340 struct wl_client *client) {
1341 if (!client) {
1342 seat->exclusive_client = client;
1343 // Triggers a refocus of the topmost surface layer if necessary
1344 // TODO: Make layer surface focus per-output based on cursor position
1345 for (int i = 0; i < root->outputs->length; ++i) {
1346 struct sway_output *output = root->outputs->items[i];
1347 arrange_layers(output);
1348 }
1349 return; 1333 return;
1350 } 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) {
1351 if (seat->focused_layer) { 1340 if (seat->focused_layer) {
1352 if (wl_resource_get_client(seat->focused_layer->resource) != client) { 1341 if (wl_resource_get_client(seat->focused_layer->resource) != client) {
1353 seat_set_focus_layer(seat, NULL); 1342 seat_set_focus_layer(seat, NULL);
@@ -1374,7 +1363,6 @@ void seat_set_exclusive_client(struct sway_seat *seat,
1374 now.tv_nsec / 1000, point->touch_id); 1363 now.tv_nsec / 1000, point->touch_id);
1375 } 1364 }
1376 } 1365 }
1377 seat->exclusive_client = client;
1378} 1366}
1379 1367
1380struct sway_node *seat_get_focus_inactive(struct sway_seat *seat, 1368struct sway_node *seat_get_focus_inactive(struct sway_seat *seat,
@@ -1541,7 +1529,7 @@ struct seat_config *seat_get_config_by_name(const char *name) {
1541} 1529}
1542 1530
1543void 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,
1544 uint32_t button, enum wlr_button_state state) { 1532 uint32_t button, enum wl_pointer_button_state state) {
1545 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,
1546 time_msec, button, state); 1534 time_msec, button, state);
1547} 1535}
@@ -1578,7 +1566,7 @@ void seatop_unref(struct sway_seat *seat, struct sway_container *con) {
1578 1566
1579void seatop_button(struct sway_seat *seat, uint32_t time_msec, 1567void seatop_button(struct sway_seat *seat, uint32_t time_msec,
1580 struct wlr_input_device *device, uint32_t button, 1568 struct wlr_input_device *device, uint32_t button,
1581 enum wlr_button_state state) { 1569 enum wl_pointer_button_state state) {
1582 if (seat->seatop_impl->button) { 1570 if (seat->seatop_impl->button) {
1583 seat->seatop_impl->button(seat, time_msec, device, button, state); 1571 seat->seatop_impl->button(seat, time_msec, device, button, state);
1584 } 1572 }
@@ -1597,6 +1585,32 @@ void seatop_pointer_axis(struct sway_seat *seat,
1597 } 1585 }
1598} 1586}
1599 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
1600void seatop_tablet_tool_tip(struct sway_seat *seat, 1614void seatop_tablet_tool_tip(struct sway_seat *seat,
1601 struct sway_tablet_tool *tool, uint32_t time_msec, 1615 struct sway_tablet_tool *tool, uint32_t time_msec,
1602 enum wlr_tablet_tool_tip_state state) { 1616 enum wlr_tablet_tool_tip_state state) {
@@ -1614,6 +1628,62 @@ void seatop_tablet_tool_motion(struct sway_seat *seat,
1614 } 1628 }
1615} 1629}
1616 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
1617void seatop_rebase(struct sway_seat *seat, uint32_t time_msec) { 1687void seatop_rebase(struct sway_seat *seat, uint32_t time_msec) {
1618 if (seat->seatop_impl->rebase) { 1688 if (seat->seatop_impl->rebase) {
1619 seat->seatop_impl->rebase(seat, time_msec); 1689 seat->seatop_impl->rebase(seat, time_msec);
@@ -1629,13 +1699,6 @@ void seatop_end(struct sway_seat *seat) {
1629 seat->seatop_impl = NULL; 1699 seat->seatop_impl = NULL;
1630} 1700}
1631 1701
1632void seatop_render(struct sway_seat *seat, struct sway_output *output,
1633 pixman_region32_t *damage) {
1634 if (seat->seatop_impl->render) {
1635 seat->seatop_impl->render(seat, output, damage);
1636 }
1637}
1638
1639bool seatop_allows_set_cursor(struct sway_seat *seat) { 1702bool seatop_allows_set_cursor(struct sway_seat *seat) {
1640 return seat->seatop_impl->allow_set_cursor; 1703 return seat->seatop_impl->allow_set_cursor;
1641} 1704}
diff --git a/sway/input/seatop_default.c b/sway/input/seatop_default.c
index 15d1ca8b..f4a0f463 100644
--- a/sway/input/seatop_default.c
+++ b/sway/input/seatop_default.c
@@ -1,18 +1,22 @@
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/server.h"
15#include "sway/scene_descriptor.h"
12#include "sway/tree/view.h" 16#include "sway/tree/view.h"
13#include "sway/tree/workspace.h" 17#include "sway/tree/workspace.h"
14#include "log.h" 18#include "log.h"
15#if HAVE_XWAYLAND 19#if WLR_HAS_XWAYLAND
16#include "sway/xwayland.h" 20#include "sway/xwayland.h"
17#endif 21#endif
18 22
@@ -20,6 +24,7 @@ struct seatop_default_event {
20 struct sway_node *previous_node; 24 struct sway_node *previous_node;
21 uint32_t pressed_buttons[SWAY_CURSOR_PRESSED_BUTTONS_CAP]; 25 uint32_t pressed_buttons[SWAY_CURSOR_PRESSED_BUTTONS_CAP];
22 size_t pressed_button_count; 26 size_t pressed_button_count;
27 struct gesture_tracker gestures;
23}; 28};
24 29
25/*-----------------------------------------\ 30/*-----------------------------------------\
@@ -51,6 +56,9 @@ static bool edge_is_external(struct sway_container *cont, enum wlr_edges edge) {
51 while (cont) { 56 while (cont) {
52 if (container_parent_layout(cont) == layout) { 57 if (container_parent_layout(cont) == layout) {
53 list_t *siblings = container_get_siblings(cont); 58 list_t *siblings = container_get_siblings(cont);
59 if (!siblings) {
60 return false;
61 }
54 int index = list_find(siblings, cont); 62 int index = list_find(siblings, cont);
55 if (index > 0 && (edge == WLR_EDGE_LEFT || edge == WLR_EDGE_TOP)) { 63 if (index > 0 && (edge == WLR_EDGE_LEFT || edge == WLR_EDGE_TOP)) {
56 return false; 64 return false;
@@ -226,14 +234,15 @@ static void handle_tablet_tool_tip(struct sway_seat *seat,
226 struct sway_container *cont = node && node->type == N_CONTAINER ? 234 struct sway_container *cont = node && node->type == N_CONTAINER ?
227 node->sway_container : NULL; 235 node->sway_container : NULL;
228 236
229 if (wlr_surface_is_layer_surface(surface)) { 237 struct wlr_layer_surface_v1 *layer;
238#if WLR_HAS_XWAYLAND
239 struct wlr_xwayland_surface *xsurface;
240#endif
241 if ((layer = wlr_layer_surface_v1_try_from_wlr_surface(surface)) &&
242 layer->current.keyboard_interactive) {
230 // Handle tapping a layer surface 243 // Handle tapping a layer surface
231 struct wlr_layer_surface_v1 *layer = 244 seat_set_focus_layer(seat, layer);
232 wlr_layer_surface_v1_from_wlr_surface(surface); 245 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) { 246 } else if (cont) {
238 bool is_floating_or_child = container_is_floating_or_child(cont); 247 bool is_floating_or_child = container_is_floating_or_child(cont);
239 bool is_fullscreen_or_child = container_is_fullscreen_or_child(cont); 248 bool is_fullscreen_or_child = container_is_fullscreen_or_child(cont);
@@ -258,20 +267,17 @@ static void handle_tablet_tool_tip(struct sway_seat *seat,
258 267
259 // Handle tapping on a container surface 268 // Handle tapping on a container surface
260 seat_set_focus_container(seat, cont); 269 seat_set_focus_container(seat, cont);
261 seatop_begin_down(seat, node->sway_container, time_msec, sx, sy); 270 seatop_begin_down(seat, node->sway_container, sx, sy);
262 } 271 }
263#if HAVE_XWAYLAND 272#if WLR_HAS_XWAYLAND
264 // Handle tapping on an xwayland unmanaged view 273 // Handle tapping on an xwayland unmanaged view
265 else if (wlr_surface_is_xwayland_surface(surface)) { 274 else if ((xsurface = wlr_xwayland_surface_try_from_wlr_surface(surface)) &&
266 struct wlr_xwayland_surface *xsurface = 275 xsurface->override_redirect &&
267 wlr_xwayland_surface_from_wlr_surface(surface); 276 wlr_xwayland_or_surface_wants_focus(xsurface)) {
268 if (xsurface->override_redirect && 277 struct wlr_xwayland *xwayland = server.xwayland.wlr_xwayland;
269 wlr_xwayland_or_surface_wants_focus(xsurface)) { 278 wlr_xwayland_set_seat(xwayland, seat->wlr_seat);
270 struct wlr_xwayland *xwayland = server.xwayland.wlr_xwayland; 279 seat_set_focus_surface(seat, xsurface->surface, false);
271 wlr_xwayland_set_seat(xwayland, seat->wlr_seat); 280 transaction_commit_dirty();
272 seat_set_focus_surface(seat, xsurface->surface, false);
273 transaction_commit_dirty();
274 }
275 } 281 }
276#endif 282#endif
277 283
@@ -285,7 +291,7 @@ static void handle_tablet_tool_tip(struct sway_seat *seat,
285 291
286static bool trigger_pointer_button_binding(struct sway_seat *seat, 292static bool trigger_pointer_button_binding(struct sway_seat *seat,
287 struct wlr_input_device *device, uint32_t button, 293 struct wlr_input_device *device, uint32_t button,
288 enum wlr_button_state state, uint32_t modifiers, 294 enum wl_pointer_button_state state, uint32_t modifiers,
289 bool on_titlebar, bool on_border, bool on_contents, bool on_workspace) { 295 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 296 // 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 297 // pointer input for one. Emulated input should not trigger bindings. The
@@ -299,7 +305,7 @@ static bool trigger_pointer_button_binding(struct sway_seat *seat,
299 char *device_identifier = device ? input_device_get_identifier(device) 305 char *device_identifier = device ? input_device_get_identifier(device)
300 : strdup("*"); 306 : strdup("*");
301 struct sway_binding *binding = NULL; 307 struct sway_binding *binding = NULL;
302 if (state == WLR_BUTTON_PRESSED) { 308 if (state == WL_POINTER_BUTTON_STATE_PRESSED) {
303 state_add_button(e, button); 309 state_add_button(e, button);
304 binding = get_active_mouse_binding(e, 310 binding = get_active_mouse_binding(e,
305 config->current_mode->mouse_bindings, modifiers, false, 311 config->current_mode->mouse_bindings, modifiers, false,
@@ -324,7 +330,7 @@ static bool trigger_pointer_button_binding(struct sway_seat *seat,
324 330
325static void handle_button(struct sway_seat *seat, uint32_t time_msec, 331static void handle_button(struct sway_seat *seat, uint32_t time_msec,
326 struct wlr_input_device *device, uint32_t button, 332 struct wlr_input_device *device, uint32_t button,
327 enum wlr_button_state state) { 333 enum wl_pointer_button_state state) {
328 struct sway_cursor *cursor = seat->cursor; 334 struct sway_cursor *cursor = seat->cursor;
329 335
330 // Determine what's under the cursor 336 // Determine what's under the cursor
@@ -357,7 +363,7 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec,
357 363
358 // Handle clicking an empty workspace 364 // Handle clicking an empty workspace
359 if (node && node->type == N_WORKSPACE) { 365 if (node && node->type == N_WORKSPACE) {
360 if (state == WLR_BUTTON_PRESSED) { 366 if (state == WL_POINTER_BUTTON_STATE_PRESSED) {
361 seat_set_focus(seat, node); 367 seat_set_focus(seat, node);
362 transaction_commit_dirty(); 368 transaction_commit_dirty();
363 } 369 }
@@ -365,16 +371,15 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec,
365 return; 371 return;
366 } 372 }
367 373
368 // Handle clicking a layer surface 374 // Handle clicking a layer surface and its popups/subsurfaces
369 if (surface && wlr_surface_is_layer_surface(surface)) { 375 struct wlr_layer_surface_v1 *layer = NULL;
370 struct wlr_layer_surface_v1 *layer = 376 if ((layer = toplevel_layer_surface_from_surface(surface))) {
371 wlr_layer_surface_v1_from_wlr_surface(surface);
372 if (layer->current.keyboard_interactive) { 377 if (layer->current.keyboard_interactive) {
373 seat_set_focus_layer(seat, layer); 378 seat_set_focus_layer(seat, layer);
374 transaction_commit_dirty(); 379 transaction_commit_dirty();
375 } 380 }
376 if (state == WLR_BUTTON_PRESSED) { 381 if (state == WL_POINTER_BUTTON_STATE_PRESSED) {
377 seatop_begin_down_on_surface(seat, surface, time_msec, sx, sy); 382 seatop_begin_down_on_surface(seat, surface, sx, sy);
378 } 383 }
379 seat_pointer_notify_button(seat, time_msec, button, state); 384 seat_pointer_notify_button(seat, time_msec, button, state);
380 return; 385 return;
@@ -382,7 +387,7 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec,
382 387
383 // Handle tiling resize via border 388 // Handle tiling resize via border
384 if (cont && resize_edge && button == BTN_LEFT && 389 if (cont && resize_edge && button == BTN_LEFT &&
385 state == WLR_BUTTON_PRESSED && !is_floating) { 390 state == WL_POINTER_BUTTON_STATE_PRESSED && !is_floating) {
386 // If a resize is triggered on a tabbed or stacked container, change 391 // If a resize is triggered on a tabbed or stacked container, change
387 // focus to the tab which already had inactive focus -- otherwise, we'd 392 // focus to the tab which already had inactive focus -- otherwise, we'd
388 // change the active tab when the user probably just wanted to resize. 393 // change the active tab when the user probably just wanted to resize.
@@ -400,7 +405,7 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec,
400 // Handle tiling resize via mod 405 // Handle tiling resize via mod
401 bool mod_pressed = modifiers & config->floating_mod; 406 bool mod_pressed = modifiers & config->floating_mod;
402 if (cont && !is_floating_or_child && mod_pressed && 407 if (cont && !is_floating_or_child && mod_pressed &&
403 state == WLR_BUTTON_PRESSED) { 408 state == WL_POINTER_BUTTON_STATE_PRESSED) {
404 uint32_t btn_resize = config->floating_mod_inverse ? 409 uint32_t btn_resize = config->floating_mod_inverse ?
405 BTN_LEFT : BTN_RIGHT; 410 BTN_LEFT : BTN_RIGHT;
406 if (button == btn_resize) { 411 if (button == btn_resize) {
@@ -427,13 +432,31 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec,
427 } 432 }
428 } 433 }
429 434
435 // Handle changing focus when clicking on a container
436 if (cont && state == WL_POINTER_BUTTON_STATE_PRESSED) {
437 // Default case: focus the container that was just clicked.
438 node = &cont->node;
439
440 // If the container is a tab/stacked container and the click happened
441 // on a tab, switch to the tab. If the tab contents were already
442 // focused, focus the tab container itself. If the tab container was
443 // already focused, cycle back to focusing the tab contents.
444 if (on_titlebar) {
445 struct sway_container *focus = seat_get_focused_container(seat);
446 if (focus == cont || !container_has_ancestor(focus, cont)) {
447 node = seat_get_focus_inactive(seat, &cont->node);
448 }
449 }
450
451 seat_set_focus(seat, node);
452 transaction_commit_dirty();
453 }
454
430 // Handle beginning floating move 455 // Handle beginning floating move
431 if (cont && is_floating_or_child && !is_fullscreen_or_child && 456 if (cont && is_floating_or_child && !is_fullscreen_or_child &&
432 state == WLR_BUTTON_PRESSED) { 457 state == WL_POINTER_BUTTON_STATE_PRESSED) {
433 uint32_t btn_move = config->floating_mod_inverse ? BTN_RIGHT : BTN_LEFT; 458 uint32_t btn_move = config->floating_mod_inverse ? BTN_RIGHT : BTN_LEFT;
434 if (button == btn_move && (mod_pressed || on_titlebar)) { 459 if (button == btn_move && (mod_pressed || on_titlebar)) {
435 seat_set_focus_container(seat,
436 seat_get_focus_inactive_view(seat, &cont->node));
437 seatop_begin_move_floating(seat, container_toplevel_ancestor(cont)); 460 seatop_begin_move_floating(seat, container_toplevel_ancestor(cont));
438 return; 461 return;
439 } 462 }
@@ -441,9 +464,10 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec,
441 464
442 // Handle beginning floating resize 465 // Handle beginning floating resize
443 if (cont && is_floating_or_child && !is_fullscreen_or_child && 466 if (cont && is_floating_or_child && !is_fullscreen_or_child &&
444 state == WLR_BUTTON_PRESSED) { 467 state == WL_POINTER_BUTTON_STATE_PRESSED) {
445 // Via border 468 // Via border
446 if (button == BTN_LEFT && resize_edge != WLR_EDGE_NONE) { 469 if (button == BTN_LEFT && resize_edge != WLR_EDGE_NONE) {
470 seat_set_focus_container(seat, cont);
447 seatop_begin_resize_floating(seat, cont, resize_edge); 471 seatop_begin_resize_floating(seat, cont, resize_edge);
448 return; 472 return;
449 } 473 }
@@ -458,6 +482,7 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec,
458 WLR_EDGE_RIGHT : WLR_EDGE_LEFT; 482 WLR_EDGE_RIGHT : WLR_EDGE_LEFT;
459 edge |= cursor->cursor->y > floater->pending.y + floater->pending.height / 2 ? 483 edge |= cursor->cursor->y > floater->pending.y + floater->pending.height / 2 ?
460 WLR_EDGE_BOTTOM : WLR_EDGE_TOP; 484 WLR_EDGE_BOTTOM : WLR_EDGE_TOP;
485 seat_set_focus_container(seat, floater);
461 seatop_begin_resize_floating(seat, floater, edge); 486 seatop_begin_resize_floating(seat, floater, edge);
462 return; 487 return;
463 } 488 }
@@ -465,55 +490,43 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec,
465 490
466 // Handle moving a tiling container 491 // Handle moving a tiling container
467 if (config->tiling_drag && (mod_pressed || on_titlebar) && 492 if (config->tiling_drag && (mod_pressed || on_titlebar) &&
468 state == WLR_BUTTON_PRESSED && !is_floating_or_child && 493 state == WL_POINTER_BUTTON_STATE_PRESSED && !is_floating_or_child &&
469 cont && cont->pending.fullscreen_mode == FULLSCREEN_NONE) { 494 cont && cont->pending.fullscreen_mode == FULLSCREEN_NONE) {
470 struct sway_container *focus = seat_get_focused_container(seat);
471 bool focused = focus == cont || container_has_ancestor(focus, cont);
472 if (on_titlebar && !focused) {
473 node = seat_get_focus_inactive(seat, &cont->node);
474 seat_set_focus(seat, node);
475 }
476
477 // If moving a container by its title bar, use a threshold for the drag 495 // If moving a container by its title bar, use a threshold for the drag
478 if (!mod_pressed && config->tiling_drag_threshold > 0) { 496 if (!mod_pressed && config->tiling_drag_threshold > 0) {
479 seatop_begin_move_tiling_threshold(seat, cont); 497 seatop_begin_move_tiling_threshold(seat, cont);
480 } else { 498 } else {
481 seatop_begin_move_tiling(seat, cont); 499 seatop_begin_move_tiling(seat, cont);
482 } 500 }
501
483 return; 502 return;
484 } 503 }
485 504
486 // Handle mousedown on a container surface 505 // Handle mousedown on a container surface
487 if (surface && cont && state == WLR_BUTTON_PRESSED) { 506 if (surface && cont && state == WL_POINTER_BUTTON_STATE_PRESSED) {
488 seat_set_focus_container(seat, cont); 507 seatop_begin_down(seat, cont, sx, sy);
489 seatop_begin_down(seat, cont, time_msec, sx, sy); 508 seat_pointer_notify_button(seat, time_msec, button, WL_POINTER_BUTTON_STATE_PRESSED);
490 seat_pointer_notify_button(seat, time_msec, button, WLR_BUTTON_PRESSED);
491 return; 509 return;
492 } 510 }
493 511
494 // Handle clicking a container surface or decorations 512 // Handle clicking a container surface or decorations
495 if (cont && state == WLR_BUTTON_PRESSED) { 513 if (cont && state == WL_POINTER_BUTTON_STATE_PRESSED) {
496 node = seat_get_focus_inactive(seat, &cont->node);
497 seat_set_focus(seat, node);
498 transaction_commit_dirty();
499 seat_pointer_notify_button(seat, time_msec, button, state); 514 seat_pointer_notify_button(seat, time_msec, button, state);
500 return; 515 return;
501 } 516 }
502 517
503#if HAVE_XWAYLAND 518#if WLR_HAS_XWAYLAND
504 // Handle clicking on xwayland unmanaged view 519 // Handle clicking on xwayland unmanaged view
505 if (surface && wlr_surface_is_xwayland_surface(surface)) { 520 struct wlr_xwayland_surface *xsurface;
506 struct wlr_xwayland_surface *xsurface = 521 if (surface &&
507 wlr_xwayland_surface_from_wlr_surface(surface); 522 (xsurface = wlr_xwayland_surface_try_from_wlr_surface(surface)) &&
508 if (xsurface->override_redirect && 523 xsurface->override_redirect &&
509 wlr_xwayland_or_surface_wants_focus(xsurface)) { 524 wlr_xwayland_or_surface_wants_focus(xsurface)) {
510 struct wlr_xwayland *xwayland = server.xwayland.wlr_xwayland; 525 struct wlr_xwayland *xwayland = server.xwayland.wlr_xwayland;
511 wlr_xwayland_set_seat(xwayland, seat->wlr_seat); 526 wlr_xwayland_set_seat(xwayland, seat->wlr_seat);
512 seat_set_focus_surface(seat, xsurface->surface, false); 527 seat_set_focus_surface(seat, xsurface->surface, false);
513 transaction_commit_dirty(); 528 transaction_commit_dirty();
514 seat_pointer_notify_button(seat, time_msec, button, state); 529 seat_pointer_notify_button(seat, time_msec, button, state);
515 return;
516 }
517 } 530 }
518#endif 531#endif
519 532
@@ -536,6 +549,21 @@ static void check_focus_follows_mouse(struct sway_seat *seat,
536 if (wlr_output == NULL) { 549 if (wlr_output == NULL) {
537 return; 550 return;
538 } 551 }
552
553 struct wlr_surface *surface = NULL;
554 double sx, sy;
555 node_at_coords(seat, seat->cursor->cursor->x, seat->cursor->cursor->y,
556 &surface, &sx, &sy);
557
558 // Focus topmost layer surface
559 struct wlr_layer_surface_v1 *layer = NULL;
560 if ((layer = toplevel_layer_surface_from_surface(surface)) &&
561 layer->current.keyboard_interactive) {
562 seat_set_focus_layer(seat, layer);
563 transaction_commit_dirty();
564 return;
565 }
566
539 struct sway_output *hovered_output = wlr_output->data; 567 struct sway_output *hovered_output = wlr_output->data;
540 if (focus && hovered_output != node_get_output(focus)) { 568 if (focus && hovered_output != node_get_output(focus)) {
541 struct sway_workspace *ws = output_get_active_workspace(hovered_output); 569 struct sway_workspace *ws = output_get_active_workspace(hovered_output);
@@ -596,12 +624,7 @@ static void handle_pointer_motion(struct sway_seat *seat, uint32_t time_msec) {
596 wlr_seat_pointer_notify_clear_focus(seat->wlr_seat); 624 wlr_seat_pointer_notify_clear_focus(seat->wlr_seat);
597 } 625 }
598 626
599 struct sway_drag_icon *drag_icon; 627 drag_icons_update_position(seat);
600 wl_list_for_each(drag_icon, &root->drag_icons, link) {
601 if (drag_icon->seat == seat) {
602 drag_icon_update_position(drag_icon);
603 }
604 }
605 628
606 e->previous_node = node; 629 e->previous_node = node;
607} 630}
@@ -631,25 +654,50 @@ static void handle_tablet_tool_motion(struct sway_seat *seat,
631 wlr_tablet_v2_tablet_tool_notify_proximity_out(tool->tablet_v2_tool); 654 wlr_tablet_v2_tablet_tool_notify_proximity_out(tool->tablet_v2_tool);
632 } 655 }
633 656
634 struct sway_drag_icon *drag_icon; 657 drag_icons_update_position(seat);
635 wl_list_for_each(drag_icon, &root->drag_icons, link) {
636 if (drag_icon->seat == seat) {
637 drag_icon_update_position(drag_icon);
638 }
639 }
640 658
641 e->previous_node = node; 659 e->previous_node = node;
642} 660}
643 661
662static void handle_touch_down(struct sway_seat *seat,
663 struct wlr_touch_down_event *event, double lx, double ly) {
664 struct wlr_surface *surface = NULL;
665 struct wlr_seat *wlr_seat = seat->wlr_seat;
666 struct sway_cursor *cursor = seat->cursor;
667 double sx, sy;
668 node_at_coords(seat, seat->touch_x, seat->touch_y, &surface, &sx, &sy);
669
670 if (surface && wlr_surface_accepts_touch(wlr_seat, surface)) {
671 if (seat_is_input_allowed(seat, surface)) {
672 cursor->simulating_pointer_from_touch = false;
673 seatop_begin_touch_down(seat, surface, event, sx, sy, lx, ly);
674 }
675 } else if (!cursor->simulating_pointer_from_touch &&
676 (!surface || seat_is_input_allowed(seat, surface))) {
677 // Fallback to cursor simulation.
678 // The pointer_touch_id state is needed, so drags are not aborted when over
679 // a surface supporting touch and multi touch events don't interfere.
680 cursor->simulating_pointer_from_touch = true;
681 cursor->pointer_touch_id = seat->touch_id;
682 double dx, dy;
683 dx = seat->touch_x - cursor->cursor->x;
684 dy = seat->touch_y - cursor->cursor->y;
685 pointer_motion(cursor, event->time_msec, &event->touch->base, dx, dy,
686 dx, dy);
687 dispatch_cursor_button(cursor, &event->touch->base, event->time_msec,
688 BTN_LEFT, WL_POINTER_BUTTON_STATE_PRESSED);
689 }
690}
691
644/*----------------------------------------\ 692/*----------------------------------------\
645 * Functions used by handle_pointer_axis / 693 * Functions used by handle_pointer_axis /
646 *--------------------------------------*/ 694 *--------------------------------------*/
647 695
648static uint32_t wl_axis_to_button(struct wlr_pointer_axis_event *event) { 696static uint32_t wl_axis_to_button(struct wlr_pointer_axis_event *event) {
649 switch (event->orientation) { 697 switch (event->orientation) {
650 case WLR_AXIS_ORIENTATION_VERTICAL: 698 case WL_POINTER_AXIS_VERTICAL_SCROLL:
651 return event->delta < 0 ? SWAY_SCROLL_UP : SWAY_SCROLL_DOWN; 699 return event->delta < 0 ? SWAY_SCROLL_UP : SWAY_SCROLL_DOWN;
652 case WLR_AXIS_ORIENTATION_HORIZONTAL: 700 case WL_POINTER_AXIS_HORIZONTAL_SCROLL:
653 return event->delta < 0 ? SWAY_SCROLL_LEFT : SWAY_SCROLL_RIGHT; 701 return event->delta < 0 ? SWAY_SCROLL_LEFT : SWAY_SCROLL_RIGHT;
654 default: 702 default:
655 sway_log(SWAY_DEBUG, "Unknown axis orientation"); 703 sway_log(SWAY_DEBUG, "Unknown axis orientation");
@@ -706,6 +754,7 @@ static void handle_pointer_axis(struct sway_seat *seat,
706 754
707 // Scrolling on a tabbed or stacked title bar (handled as press event) 755 // Scrolling on a tabbed or stacked title bar (handled as press event)
708 if (!handled && (on_titlebar || on_titlebar_border)) { 756 if (!handled && (on_titlebar || on_titlebar_border)) {
757 struct sway_node *new_focus;
709 enum sway_container_layout layout = container_parent_layout(cont); 758 enum sway_container_layout layout = container_parent_layout(cont);
710 if (layout == L_TABBED || layout == L_STACKED) { 759 if (layout == L_TABBED || layout == L_STACKED) {
711 struct sway_node *tabcontainer = node_get_parent(node); 760 struct sway_node *tabcontainer = node_get_parent(node);
@@ -713,7 +762,7 @@ static void handle_pointer_axis(struct sway_seat *seat,
713 seat_get_active_tiling_child(seat, tabcontainer); 762 seat_get_active_tiling_child(seat, tabcontainer);
714 list_t *siblings = container_get_siblings(cont); 763 list_t *siblings = container_get_siblings(cont);
715 int desired = list_find(siblings, active->sway_container) + 764 int desired = list_find(siblings, active->sway_container) +
716 round(scroll_factor * event->delta_discrete); 765 roundf(scroll_factor * event->delta_discrete / WLR_POINTER_AXIS_DISCRETE_STEP);
717 if (desired < 0) { 766 if (desired < 0) {
718 desired = 0; 767 desired = 0;
719 } else if (desired >= siblings->length) { 768 } else if (desired >= siblings->length) {
@@ -722,14 +771,16 @@ static void handle_pointer_axis(struct sway_seat *seat,
722 771
723 struct sway_container *new_sibling_con = siblings->items[desired]; 772 struct sway_container *new_sibling_con = siblings->items[desired];
724 struct sway_node *new_sibling = &new_sibling_con->node; 773 struct sway_node *new_sibling = &new_sibling_con->node;
725 struct sway_node *new_focus =
726 seat_get_focus_inactive(seat, new_sibling);
727 // Use the focused child of the tabbed/stacked container, not the 774 // Use the focused child of the tabbed/stacked container, not the
728 // container the user scrolled on. 775 // container the user scrolled on.
729 seat_set_focus(seat, new_focus); 776 new_focus = seat_get_focus_inactive(seat, new_sibling);
730 transaction_commit_dirty(); 777 } else {
731 handled = true; 778 new_focus = seat_get_focus_inactive(seat, &cont->node);
732 } 779 }
780
781 seat_set_focus(seat, new_focus);
782 transaction_commit_dirty();
783 handled = true;
733 } 784 }
734 785
735 // Handle mouse bindings - x11 mouse buttons 4-7 - release event 786 // Handle mouse bindings - x11 mouse buttons 4-7 - release event
@@ -745,8 +796,307 @@ static void handle_pointer_axis(struct sway_seat *seat,
745 796
746 if (!handled) { 797 if (!handled) {
747 wlr_seat_pointer_notify_axis(cursor->seat->wlr_seat, event->time_msec, 798 wlr_seat_pointer_notify_axis(cursor->seat->wlr_seat, event->time_msec,
748 event->orientation, scroll_factor * event->delta, 799 event->orientation, scroll_factor * event->delta,
749 round(scroll_factor * event->delta_discrete), event->source); 800 roundf(scroll_factor * event->delta_discrete), event->source,
801 event->relative_direction);
802 }
803}
804
805/*------------------------------------\
806 * Functions used by gesture support /
807 *----------------------------------*/
808
809/**
810 * Check gesture binding for a specific gesture type and finger count.
811 * Returns true if binding is present, false otherwise
812 */
813static bool gesture_binding_check(list_t *bindings, enum gesture_type type,
814 uint8_t fingers, struct sway_input_device *device) {
815 char *input =
816 device ? input_device_get_identifier(device->wlr_device) : strdup("*");
817
818 for (int i = 0; i < bindings->length; ++i) {
819 struct sway_gesture_binding *binding = bindings->items[i];
820
821 // Check type and finger count
822 if (!gesture_check(&binding->gesture, type, fingers)) {
823 continue;
824 }
825
826 // Check that input matches
827 if (strcmp(binding->input, "*") != 0 &&
828 strcmp(binding->input, input) != 0) {
829 continue;
830 }
831
832 free(input);
833
834 return true;
835 }
836
837 free(input);
838
839 return false;
840}
841
842/**
843 * Return the gesture binding which matches gesture type, finger count
844 * and direction, otherwise return null.
845 */
846static struct sway_gesture_binding* gesture_binding_match(
847 list_t *bindings, struct gesture *gesture, const char *input) {
848 struct sway_gesture_binding *current = NULL;
849
850 // Find best matching binding
851 for (int i = 0; i < bindings->length; ++i) {
852 struct sway_gesture_binding *binding = bindings->items[i];
853 bool exact = binding->flags & BINDING_EXACT;
854
855 // Check gesture matching
856 if (!gesture_match(&binding->gesture, gesture, exact)) {
857 continue;
858 }
859
860 // Check input matching
861 if (strcmp(binding->input, "*") != 0 &&
862 strcmp(binding->input, input) != 0) {
863 continue;
864 }
865
866 // If we already have a match ...
867 if (current) {
868 // ... check if input matching is equivalent
869 if (strcmp(current->input, binding->input) == 0) {
870
871 // ... - do not override an exact binding
872 if (!exact && current->flags & BINDING_EXACT) {
873 continue;
874 }
875
876 // ... - and ensure direction matching is better or equal
877 if (gesture_compare(&current->gesture, &binding->gesture) > 0) {
878 continue;
879 }
880 } else if (strcmp(binding->input, "*") == 0) {
881 // ... do not accept worse input match
882 continue;
883 }
884 }
885
886 // Accept newer or better match
887 current = binding;
888
889 // If exact binding and input is found, quit search
890 if (strcmp(current->input, input) == 0 &&
891 gesture_compare(&current->gesture, gesture) == 0) {
892 break;
893 }
894 } // for all gesture bindings
895
896 return current;
897}
898
899// Wrapper around gesture_tracker_end to use tracker with sway bindings
900static struct sway_gesture_binding* gesture_tracker_end_and_match(
901 struct gesture_tracker *tracker, struct sway_input_device* device) {
902 // Determine name of input that received gesture
903 char *input = device
904 ? input_device_get_identifier(device->wlr_device)
905 : strdup("*");
906
907 // Match tracking result to binding
908 struct gesture *gesture = gesture_tracker_end(tracker);
909 struct sway_gesture_binding *binding = gesture_binding_match(
910 config->current_mode->gesture_bindings, gesture, input);
911 free(gesture);
912 free(input);
913
914 return binding;
915}
916
917// Small wrapper around seat_execute_command to work on gesture bindings
918static void gesture_binding_execute(struct sway_seat *seat,
919 struct sway_gesture_binding *binding) {
920 struct sway_binding *dummy_binding =
921 calloc(1, sizeof(struct sway_binding));
922 dummy_binding->type = BINDING_GESTURE;
923 dummy_binding->command = binding->command;
924
925 char *description = gesture_to_string(&binding->gesture);
926 sway_log(SWAY_DEBUG, "executing gesture binding: %s", description);
927 free(description);
928
929 seat_execute_command(seat, dummy_binding);
930
931 free(dummy_binding);
932}
933
934static void handle_hold_begin(struct sway_seat *seat,
935 struct wlr_pointer_hold_begin_event *event) {
936 // Start tracking gesture if there is a matching binding ...
937 struct sway_input_device *device =
938 event->pointer ? event->pointer->base.data : NULL;
939 list_t *bindings = config->current_mode->gesture_bindings;
940 if (gesture_binding_check(bindings, GESTURE_TYPE_HOLD, event->fingers, device)) {
941 struct seatop_default_event *seatop = seat->seatop_data;
942 gesture_tracker_begin(&seatop->gestures, GESTURE_TYPE_HOLD, event->fingers);
943 } else {
944 // ... otherwise forward to client
945 struct sway_cursor *cursor = seat->cursor;
946 wlr_pointer_gestures_v1_send_hold_begin(
947 server.input->pointer_gestures, cursor->seat->wlr_seat,
948 event->time_msec, event->fingers);
949 }
950}
951
952static void handle_hold_end(struct sway_seat *seat,
953 struct wlr_pointer_hold_end_event *event) {
954 // Ensure that gesture is being tracked and was not cancelled
955 struct seatop_default_event *seatop = seat->seatop_data;
956 if (!gesture_tracker_check(&seatop->gestures, GESTURE_TYPE_HOLD)) {
957 struct sway_cursor *cursor = seat->cursor;
958 wlr_pointer_gestures_v1_send_hold_end(
959 server.input->pointer_gestures, cursor->seat->wlr_seat,
960 event->time_msec, event->cancelled);
961 return;
962 }
963 if (event->cancelled) {
964 gesture_tracker_cancel(&seatop->gestures);
965 return;
966 }
967
968 // End gesture tracking and execute matched binding
969 struct sway_input_device *device =
970 event->pointer ? event->pointer->base.data : NULL;
971 struct sway_gesture_binding *binding = gesture_tracker_end_and_match(
972 &seatop->gestures, device);
973
974 if (binding) {
975 gesture_binding_execute(seat, binding);
976 }
977}
978
979static void handle_pinch_begin(struct sway_seat *seat,
980 struct wlr_pointer_pinch_begin_event *event) {
981 // Start tracking gesture if there is a matching binding ...
982 struct sway_input_device *device =
983 event->pointer ? event->pointer->base.data : NULL;
984 list_t *bindings = config->current_mode->gesture_bindings;
985 if (gesture_binding_check(bindings, GESTURE_TYPE_PINCH, event->fingers, device)) {
986 struct seatop_default_event *seatop = seat->seatop_data;
987 gesture_tracker_begin(&seatop->gestures, GESTURE_TYPE_PINCH, event->fingers);
988 } else {
989 // ... otherwise forward to client
990 struct sway_cursor *cursor = seat->cursor;
991 wlr_pointer_gestures_v1_send_pinch_begin(
992 server.input->pointer_gestures, cursor->seat->wlr_seat,
993 event->time_msec, event->fingers);
994 }
995}
996
997static void handle_pinch_update(struct sway_seat *seat,
998 struct wlr_pointer_pinch_update_event *event) {
999 // Update any ongoing tracking ...
1000 struct seatop_default_event *seatop = seat->seatop_data;
1001 if (gesture_tracker_check(&seatop->gestures, GESTURE_TYPE_PINCH)) {
1002 gesture_tracker_update(&seatop->gestures, event->dx, event->dy,
1003 event->scale, event->rotation);
1004 } else {
1005 // ... otherwise forward to client
1006 struct sway_cursor *cursor = seat->cursor;
1007 wlr_pointer_gestures_v1_send_pinch_update(
1008 server.input->pointer_gestures,
1009 cursor->seat->wlr_seat,
1010 event->time_msec, event->dx, event->dy,
1011 event->scale, event->rotation);
1012 }
1013}
1014
1015static void handle_pinch_end(struct sway_seat *seat,
1016 struct wlr_pointer_pinch_end_event *event) {
1017 // Ensure that gesture is being tracked and was not cancelled
1018 struct seatop_default_event *seatop = seat->seatop_data;
1019 if (!gesture_tracker_check(&seatop->gestures, GESTURE_TYPE_PINCH)) {
1020 struct sway_cursor *cursor = seat->cursor;
1021 wlr_pointer_gestures_v1_send_pinch_end(
1022 server.input->pointer_gestures, cursor->seat->wlr_seat,
1023 event->time_msec, event->cancelled);
1024 return;
1025 }
1026 if (event->cancelled) {
1027 gesture_tracker_cancel(&seatop->gestures);
1028 return;
1029 }
1030
1031 // End gesture tracking and execute matched binding
1032 struct sway_input_device *device =
1033 event->pointer ? event->pointer->base.data : NULL;
1034 struct sway_gesture_binding *binding = gesture_tracker_end_and_match(
1035 &seatop->gestures, device);
1036
1037 if (binding) {
1038 gesture_binding_execute(seat, binding);
1039 }
1040}
1041
1042static void handle_swipe_begin(struct sway_seat *seat,
1043 struct wlr_pointer_swipe_begin_event *event) {
1044 // Start tracking gesture if there is a matching binding ...
1045 struct sway_input_device *device =
1046 event->pointer ? event->pointer->base.data : NULL;
1047 list_t *bindings = config->current_mode->gesture_bindings;
1048 if (gesture_binding_check(bindings, GESTURE_TYPE_SWIPE, event->fingers, device)) {
1049 struct seatop_default_event *seatop = seat->seatop_data;
1050 gesture_tracker_begin(&seatop->gestures, GESTURE_TYPE_SWIPE, event->fingers);
1051 } else {
1052 // ... otherwise forward to client
1053 struct sway_cursor *cursor = seat->cursor;
1054 wlr_pointer_gestures_v1_send_swipe_begin(
1055 server.input->pointer_gestures, cursor->seat->wlr_seat,
1056 event->time_msec, event->fingers);
1057 }
1058}
1059
1060static void handle_swipe_update(struct sway_seat *seat,
1061 struct wlr_pointer_swipe_update_event *event) {
1062
1063 // Update any ongoing tracking ...
1064 struct seatop_default_event *seatop = seat->seatop_data;
1065 if (gesture_tracker_check(&seatop->gestures, GESTURE_TYPE_SWIPE)) {
1066 gesture_tracker_update(&seatop->gestures,
1067 event->dx, event->dy, NAN, NAN);
1068 } else {
1069 // ... otherwise forward to client
1070 struct sway_cursor *cursor = seat->cursor;
1071 wlr_pointer_gestures_v1_send_swipe_update(
1072 server.input->pointer_gestures, cursor->seat->wlr_seat,
1073 event->time_msec, event->dx, event->dy);
1074 }
1075}
1076
1077static void handle_swipe_end(struct sway_seat *seat,
1078 struct wlr_pointer_swipe_end_event *event) {
1079 // Ensure gesture is being tracked and was not cancelled
1080 struct seatop_default_event *seatop = seat->seatop_data;
1081 if (!gesture_tracker_check(&seatop->gestures, GESTURE_TYPE_SWIPE)) {
1082 struct sway_cursor *cursor = seat->cursor;
1083 wlr_pointer_gestures_v1_send_swipe_end(server.input->pointer_gestures,
1084 cursor->seat->wlr_seat, event->time_msec, event->cancelled);
1085 return;
1086 }
1087 if (event->cancelled) {
1088 gesture_tracker_cancel(&seatop->gestures);
1089 return;
1090 }
1091
1092 // End gesture tracking and execute matched binding
1093 struct sway_input_device *device =
1094 event->pointer ? event->pointer->base.data : NULL;
1095 struct sway_gesture_binding *binding = gesture_tracker_end_and_match(
1096 &seatop->gestures, device);
1097
1098 if (binding) {
1099 gesture_binding_execute(seat, binding);
750 } 1100 }
751} 1101}
752 1102
@@ -779,6 +1129,15 @@ static const struct sway_seatop_impl seatop_impl = {
779 .pointer_axis = handle_pointer_axis, 1129 .pointer_axis = handle_pointer_axis,
780 .tablet_tool_tip = handle_tablet_tool_tip, 1130 .tablet_tool_tip = handle_tablet_tool_tip,
781 .tablet_tool_motion = handle_tablet_tool_motion, 1131 .tablet_tool_motion = handle_tablet_tool_motion,
1132 .hold_begin = handle_hold_begin,
1133 .hold_end = handle_hold_end,
1134 .pinch_begin = handle_pinch_begin,
1135 .pinch_update = handle_pinch_update,
1136 .pinch_end = handle_pinch_end,
1137 .swipe_begin = handle_swipe_begin,
1138 .swipe_update = handle_swipe_update,
1139 .swipe_end = handle_swipe_end,
1140 .touch_down = handle_touch_down,
782 .rebase = handle_rebase, 1141 .rebase = handle_rebase,
783 .allow_set_cursor = true, 1142 .allow_set_cursor = true,
784}; 1143};
@@ -789,8 +1148,8 @@ void seatop_begin_default(struct sway_seat *seat) {
789 struct seatop_default_event *e = 1148 struct seatop_default_event *e =
790 calloc(1, sizeof(struct seatop_default_event)); 1149 calloc(1, sizeof(struct seatop_default_event));
791 sway_assert(e, "Unable to allocate seatop_default_event"); 1150 sway_assert(e, "Unable to allocate seatop_default_event");
1151
792 seat->seatop_impl = &seatop_impl; 1152 seat->seatop_impl = &seatop_impl;
793 seat->seatop_data = e; 1153 seat->seatop_data = e;
794
795 seatop_rebase(seat, 0); 1154 seatop_rebase(seat, 0);
796} 1155}
diff --git a/sway/input/seatop_down.c b/sway/input/seatop_down.c
index b40773d0..340e334b 100644
--- a/sway/input/seatop_down.c
+++ b/sway/input/seatop_down.c
@@ -1,13 +1,20 @@
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;
13 struct sway_seat *seat; 20 struct sway_seat *seat;
@@ -15,8 +22,113 @@ struct seatop_down_event {
15 struct wlr_surface *surface; 22 struct wlr_surface *surface;
16 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
17 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
18}; 26};
19 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
20static void handle_pointer_axis(struct sway_seat *seat, 132static void handle_pointer_axis(struct sway_seat *seat,
21 struct wlr_pointer_axis_event *event) { 133 struct wlr_pointer_axis_event *event) {
22 struct sway_input_device *input_device = 134 struct sway_input_device *input_device =
@@ -28,12 +140,13 @@ static void handle_pointer_axis(struct sway_seat *seat,
28 140
29 wlr_seat_pointer_notify_axis(seat->wlr_seat, event->time_msec, 141 wlr_seat_pointer_notify_axis(seat->wlr_seat, event->time_msec,
30 event->orientation, scroll_factor * event->delta, 142 event->orientation, scroll_factor * event->delta,
31 round(scroll_factor * event->delta_discrete), event->source); 143 roundf(scroll_factor * event->delta_discrete), event->source,
144 event->relative_direction);
32} 145}
33 146
34static void handle_button(struct sway_seat *seat, uint32_t time_msec, 147static void handle_button(struct sway_seat *seat, uint32_t time_msec,
35 struct wlr_input_device *device, uint32_t button, 148 struct wlr_input_device *device, uint32_t button,
36 enum wlr_button_state state) { 149 enum wl_pointer_button_state state) {
37 seat_pointer_notify_button(seat, time_msec, button, state); 150 seat_pointer_notify_button(seat, time_msec, button, state);
38 151
39 if (seat->cursor->pressed_button_count == 0) { 152 if (seat->cursor->pressed_button_count == 0) {
@@ -99,14 +212,18 @@ static const struct sway_seatop_impl seatop_impl = {
99 .pointer_axis = handle_pointer_axis, 212 .pointer_axis = handle_pointer_axis,
100 .tablet_tool_tip = handle_tablet_tool_tip, 213 .tablet_tool_tip = handle_tablet_tool_tip,
101 .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,
102 .unref = handle_unref, 219 .unref = handle_unref,
103 .end = handle_end, 220 .end = handle_end,
104 .allow_set_cursor = true, 221 .allow_set_cursor = true,
105}; 222};
106 223
107void seatop_begin_down(struct sway_seat *seat, struct sway_container *con, 224void seatop_begin_down(struct sway_seat *seat, struct sway_container *con,
108 uint32_t time_msec, double sx, double sy) { 225 double sx, double sy) {
109 seatop_begin_down_on_surface(seat, con->view->surface, time_msec, sx, sy); 226 seatop_begin_down_on_surface(seat, con->view->surface, sx, sy);
110 struct seatop_down_event *e = seat->seatop_data; 227 struct seatop_down_event *e = seat->seatop_data;
111 e->con = con; 228 e->con = con;
112 229
@@ -114,13 +231,20 @@ void seatop_begin_down(struct sway_seat *seat, struct sway_container *con,
114 transaction_commit_dirty(); 231 transaction_commit_dirty();
115} 232}
116 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
117void seatop_begin_down_on_surface(struct sway_seat *seat, 241void seatop_begin_down_on_surface(struct sway_seat *seat,
118 struct wlr_surface *surface, uint32_t time_msec, double sx, double sy) { 242 struct wlr_surface *surface, double sx, double sy) {
119 seatop_end(seat); 243 seatop_end(seat);
120 244
121 struct seatop_down_event *e = 245 struct seatop_down_event *e =
122 calloc(1, sizeof(struct seatop_down_event)); 246 calloc(1, sizeof(struct seatop_down_event));
123 if (!e) { 247 if (!sway_assert(e, "Unable to allocate e")) {
124 return; 248 return;
125 } 249 }
126 e->con = NULL; 250 e->con = NULL;
@@ -132,6 +256,7 @@ void seatop_begin_down_on_surface(struct sway_seat *seat,
132 e->ref_ly = seat->cursor->cursor->y; 256 e->ref_ly = seat->cursor->cursor->y;
133 e->ref_con_lx = sx; 257 e->ref_con_lx = sx;
134 e->ref_con_ly = sy; 258 e->ref_con_ly = sy;
259 wl_list_init(&e->point_events);
135 260
136 seat->seatop_impl = &seatop_impl; 261 seat->seatop_impl = &seatop_impl;
137 seat->seatop_data = e; 262 seat->seatop_data = e;
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 df683026..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
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 ac4baece..6aab4ad0 100644
--- a/sway/input/switch.c
+++ b/sway/input/switch.c
@@ -1,6 +1,6 @@
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> 3#include "sway/server.h"
4#include "log.h" 4#include "log.h"
5 5
6struct sway_switch *sway_switch_create(struct sway_seat *seat, 6struct sway_switch *sway_switch_create(struct sway_seat *seat,
@@ -11,6 +11,7 @@ struct sway_switch *sway_switch_create(struct sway_seat *seat,
11 return NULL; 11 return NULL;
12 } 12 }
13 device->switch_device = switch_device; 13 device->switch_device = switch_device;
14 switch_device->wlr = wlr_switch_from_input_device(device->input_device->wlr_device);
14 switch_device->seat_device = device; 15 switch_device->seat_device = device;
15 switch_device->state = WLR_SWITCH_STATE_OFF; 16 switch_device->state = WLR_SWITCH_STATE_OFF;
16 wl_list_init(&switch_device->switch_toggle.link); 17 wl_list_init(&switch_device->switch_toggle.link);
@@ -33,9 +34,8 @@ static bool sway_switch_trigger_test(enum sway_switch_trigger trigger,
33} 34}
34 35
35static void execute_binding(struct sway_switch *sway_switch) { 36static void execute_binding(struct sway_switch *sway_switch) {
36 struct sway_seat* seat = sway_switch->seat_device->sway_seat; 37 struct sway_seat *seat = sway_switch->seat_device->sway_seat;
37 bool input_inhibited = seat->exclusive_client != NULL || 38 bool locked = server.session_lock.lock;
38 server.session_lock.locked;
39 39
40 list_t *bindings = config->current_mode->switch_bindings; 40 list_t *bindings = config->current_mode->switch_bindings;
41 struct sway_switch_binding *matched_binding = NULL; 41 struct sway_switch_binding *matched_binding = NULL;
@@ -52,13 +52,13 @@ static void execute_binding(struct sway_switch *sway_switch) {
52 continue; 52 continue;
53 } 53 }
54 bool binding_locked = binding->flags & BINDING_LOCKED; 54 bool binding_locked = binding->flags & BINDING_LOCKED;
55 if (!binding_locked && input_inhibited) { 55 if (!binding_locked && locked) {
56 continue; 56 continue;
57 } 57 }
58 58
59 matched_binding = binding; 59 matched_binding = binding;
60 60
61 if (binding_locked == input_inhibited) { 61 if (binding_locked == locked) {
62 break; 62 break;
63 } 63 }
64 } 64 }
@@ -95,10 +95,8 @@ static void handle_switch_toggle(struct wl_listener *listener, void *data) {
95} 95}
96 96
97void sway_switch_configure(struct sway_switch *sway_switch) { 97void sway_switch_configure(struct sway_switch *sway_switch) {
98 struct wlr_input_device *wlr_device =
99 sway_switch->seat_device->input_device->wlr_device;
100 wl_list_remove(&sway_switch->switch_toggle.link); 98 wl_list_remove(&sway_switch->switch_toggle.link);
101 wl_signal_add(&wlr_device->switch_device->events.toggle, 99 wl_signal_add(&sway_switch->wlr->events.toggle,
102 &sway_switch->switch_toggle); 100 &sway_switch->switch_toggle);
103 sway_switch->switch_toggle.notify = handle_switch_toggle; 101 sway_switch->switch_toggle.notify = handle_switch_toggle;
104 sway_log(SWAY_DEBUG, "Configured switch for device"); 102 sway_log(SWAY_DEBUG, "Configured switch for device");
diff --git a/sway/input/tablet.c b/sway/input/tablet.c
index db2f93ec..ec1e4f68 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>
@@ -8,6 +7,11 @@
8#include "sway/input/cursor.h" 7#include "sway/input/cursor.h"
9#include "sway/input/seat.h" 8#include "sway/input/seat.h"
10#include "sway/input/tablet.h" 9#include "sway/input/tablet.h"
10#include "sway/server.h"
11
12#if WLR_HAS_LIBINPUT_BACKEND
13#include <wlr/backend/libinput.h>
14#endif
11 15
12static void handle_pad_tablet_destroy(struct wl_listener *listener, void *data) { 16static void handle_pad_tablet_destroy(struct wl_listener *listener, void *data) {
13 struct sway_tablet_pad *pad = 17 struct sway_tablet_pad *pad =
@@ -54,15 +58,14 @@ void sway_configure_tablet(struct sway_tablet *tablet) {
54 tablet->seat_device->input_device->wlr_device; 58 tablet->seat_device->input_device->wlr_device;
55 struct sway_seat *seat = tablet->seat_device->sway_seat; 59 struct sway_seat *seat = tablet->seat_device->sway_seat;
56 60
57 if ((seat->wlr_seat->capabilities & WL_SEAT_CAPABILITY_POINTER) == 0) { 61 seat_configure_xcursor(seat);
58 seat_configure_xcursor(seat);
59 }
60 62
61 if (!tablet->tablet_v2) { 63 if (!tablet->tablet_v2) {
62 tablet->tablet_v2 = 64 tablet->tablet_v2 =
63 wlr_tablet_create(server.tablet_v2, seat->wlr_seat, device); 65 wlr_tablet_create(server.tablet_v2, seat->wlr_seat, device);
64 } 66 }
65 67
68#if WLR_HAS_LIBINPUT_BACKEND
66 /* Search for a sibling tablet pad */ 69 /* Search for a sibling tablet pad */
67 if (!wlr_input_device_is_libinput(device)) { 70 if (!wlr_input_device_is_libinput(device)) {
68 /* We can only do this on libinput devices */ 71 /* We can only do this on libinput devices */
@@ -87,6 +90,7 @@ void sway_configure_tablet(struct sway_tablet *tablet) {
87 break; 90 break;
88 } 91 }
89 } 92 }
93#endif
90} 94}
91 95
92void sway_tablet_destroy(struct sway_tablet *tablet) { 96void sway_tablet_destroy(struct sway_tablet *tablet) {
@@ -246,6 +250,7 @@ struct sway_tablet_pad *sway_tablet_pad_create(struct sway_seat *seat,
246 return NULL; 250 return NULL;
247 } 251 }
248 252
253 tablet_pad->wlr = wlr_tablet_pad_from_input_device(device->input_device->wlr_device);
249 tablet_pad->seat_device = device; 254 tablet_pad->seat_device = device;
250 wl_list_init(&tablet_pad->attach.link); 255 wl_list_init(&tablet_pad->attach.link);
251 wl_list_init(&tablet_pad->button.link); 256 wl_list_init(&tablet_pad->button.link);
@@ -260,40 +265,41 @@ struct sway_tablet_pad *sway_tablet_pad_create(struct sway_seat *seat,
260} 265}
261 266
262void sway_configure_tablet_pad(struct sway_tablet_pad *tablet_pad) { 267void sway_configure_tablet_pad(struct sway_tablet_pad *tablet_pad) {
263 struct wlr_input_device *device = 268 struct wlr_input_device *wlr_device =
264 tablet_pad->seat_device->input_device->wlr_device; 269 tablet_pad->seat_device->input_device->wlr_device;
265 struct sway_seat *seat = tablet_pad->seat_device->sway_seat; 270 struct sway_seat *seat = tablet_pad->seat_device->sway_seat;
266 271
267 if (!tablet_pad->tablet_v2_pad) { 272 if (!tablet_pad->tablet_v2_pad) {
268 tablet_pad->tablet_v2_pad = 273 tablet_pad->tablet_v2_pad =
269 wlr_tablet_pad_create(server.tablet_v2, seat->wlr_seat, device); 274 wlr_tablet_pad_create(server.tablet_v2, seat->wlr_seat, wlr_device);
270 } 275 }
271 276
272 wl_list_remove(&tablet_pad->attach.link); 277 wl_list_remove(&tablet_pad->attach.link);
273 tablet_pad->attach.notify = handle_tablet_pad_attach; 278 tablet_pad->attach.notify = handle_tablet_pad_attach;
274 wl_signal_add(&device->tablet_pad->events.attach_tablet, 279 wl_signal_add(&tablet_pad->wlr->events.attach_tablet,
275 &tablet_pad->attach); 280 &tablet_pad->attach);
276 281
277 wl_list_remove(&tablet_pad->button.link); 282 wl_list_remove(&tablet_pad->button.link);
278 tablet_pad->button.notify = handle_tablet_pad_button; 283 tablet_pad->button.notify = handle_tablet_pad_button;
279 wl_signal_add(&device->tablet_pad->events.button, &tablet_pad->button); 284 wl_signal_add(&tablet_pad->wlr->events.button, &tablet_pad->button);
280 285
281 wl_list_remove(&tablet_pad->strip.link); 286 wl_list_remove(&tablet_pad->strip.link);
282 tablet_pad->strip.notify = handle_tablet_pad_strip; 287 tablet_pad->strip.notify = handle_tablet_pad_strip;
283 wl_signal_add(&device->tablet_pad->events.strip, &tablet_pad->strip); 288 wl_signal_add(&tablet_pad->wlr->events.strip, &tablet_pad->strip);
284 289
285 wl_list_remove(&tablet_pad->ring.link); 290 wl_list_remove(&tablet_pad->ring.link);
286 tablet_pad->ring.notify = handle_tablet_pad_ring; 291 tablet_pad->ring.notify = handle_tablet_pad_ring;
287 wl_signal_add(&device->tablet_pad->events.ring, &tablet_pad->ring); 292 wl_signal_add(&tablet_pad->wlr->events.ring, &tablet_pad->ring);
288 293
294#if WLR_HAS_LIBINPUT_BACKEND
289 /* Search for a sibling tablet */ 295 /* Search for a sibling tablet */
290 if (!wlr_input_device_is_libinput(device)) { 296 if (!wlr_input_device_is_libinput(wlr_device)) {
291 /* We can only do this on libinput devices */ 297 /* We can only do this on libinput devices */
292 return; 298 return;
293 } 299 }
294 300
295 struct libinput_device_group *group = 301 struct libinput_device_group *group =
296 libinput_device_get_device_group(wlr_libinput_get_device_handle(device)); 302 libinput_device_get_device_group(wlr_libinput_get_device_handle(wlr_device));
297 struct sway_tablet *tool; 303 struct sway_tablet *tool;
298 wl_list_for_each(tool, &seat->cursor->tablets, link) { 304 wl_list_for_each(tool, &seat->cursor->tablets, link) {
299 struct wlr_input_device *tablet = 305 struct wlr_input_device *tablet =
@@ -310,6 +316,7 @@ void sway_configure_tablet_pad(struct sway_tablet_pad *tablet_pad) {
310 break; 316 break;
311 } 317 }
312 } 318 }
319#endif
313} 320}
314 321
315void sway_tablet_pad_destroy(struct sway_tablet_pad *tablet_pad) { 322void sway_tablet_pad_destroy(struct sway_tablet_pad *tablet_pad) {
@@ -333,14 +340,10 @@ static void handle_pad_tablet_surface_destroy(struct wl_listener *listener,
333 struct sway_tablet_pad *tablet_pad = 340 struct sway_tablet_pad *tablet_pad =
334 wl_container_of(listener, tablet_pad, surface_destroy); 341 wl_container_of(listener, tablet_pad, surface_destroy);
335 342
336 wlr_tablet_v2_tablet_pad_notify_leave(tablet_pad->tablet_v2_pad, 343 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} 344}
342 345
343void sway_tablet_pad_notify_enter(struct sway_tablet_pad *tablet_pad, 346void sway_tablet_pad_set_focus(struct sway_tablet_pad *tablet_pad,
344 struct wlr_surface *surface) { 347 struct wlr_surface *surface) {
345 if (!tablet_pad || !tablet_pad->tablet) { 348 if (!tablet_pad || !tablet_pad->tablet) {
346 return; 349 return;
@@ -359,7 +362,8 @@ void sway_tablet_pad_notify_enter(struct sway_tablet_pad *tablet_pad,
359 tablet_pad->current_surface = NULL; 362 tablet_pad->current_surface = NULL;
360 } 363 }
361 364
362 if (!wlr_surface_accepts_tablet_v2(tablet_pad->tablet->tablet_v2, surface)) { 365 if (surface == NULL ||
366 !wlr_surface_accepts_tablet_v2(tablet_pad->tablet->tablet_v2, surface)) {
363 return; 367 return;
364 } 368 }
365 369
@@ -367,7 +371,6 @@ void sway_tablet_pad_notify_enter(struct sway_tablet_pad *tablet_pad,
367 tablet_pad->tablet->tablet_v2, surface); 371 tablet_pad->tablet->tablet_v2, surface);
368 372
369 tablet_pad->current_surface = surface; 373 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; 374 tablet_pad->surface_destroy.notify = handle_pad_tablet_surface_destroy;
372 wl_signal_add(&surface->events.destroy, &tablet_pad->surface_destroy); 375 wl_signal_add(&surface->events.destroy, &tablet_pad->surface_destroy);
373} 376}
diff --git a/sway/input/text_input.c b/sway/input/text_input.c
index 58911c2d..580a9f54 100644
--- a/sway/input/text_input.c
+++ b/sway/input/text_input.c
@@ -2,7 +2,16 @@
2#include <stdlib.h> 2#include <stdlib.h>
3#include "log.h" 3#include "log.h"
4#include "sway/input/seat.h" 4#include "sway/input/seat.h"
5#include "sway/scene_descriptor.h"
6#include "sway/tree/root.h"
7#include "sway/tree/view.h"
8#include "sway/output.h"
5#include "sway/input/text_input.h" 9#include "sway/input/text_input.h"
10#include "sway/input/text_input_popup.h"
11#include "sway/layers.h"
12#include "sway/server.h"
13
14static void input_popup_update(struct sway_input_popup *popup);
6 15
7static struct sway_text_input *relay_get_focusable_text_input( 16static struct sway_text_input *relay_get_focusable_text_input(
8 struct sway_input_method_relay *relay) { 17 struct sway_input_method_relay *relay) {
@@ -59,11 +68,13 @@ static void handle_im_keyboard_grab_destroy(struct wl_listener *listener, void *
59 struct sway_input_method_relay *relay = wl_container_of(listener, relay, 68 struct sway_input_method_relay *relay = wl_container_of(listener, relay,
60 input_method_keyboard_grab_destroy); 69 input_method_keyboard_grab_destroy);
61 struct wlr_input_method_keyboard_grab_v2 *keyboard_grab = data; 70 struct wlr_input_method_keyboard_grab_v2 *keyboard_grab = data;
71 struct wlr_seat *wlr_seat = keyboard_grab->input_method->seat;
62 wl_list_remove(&relay->input_method_keyboard_grab_destroy.link); 72 wl_list_remove(&relay->input_method_keyboard_grab_destroy.link);
63 73
64 if (keyboard_grab->keyboard) { 74 if (keyboard_grab->keyboard) {
65 // send modifier state to original client 75 // send modifier state to original client
66 wlr_seat_keyboard_notify_modifiers(keyboard_grab->input_method->seat, 76 wlr_seat_set_keyboard(wlr_seat, keyboard_grab->keyboard);
77 wlr_seat_keyboard_notify_modifiers(wlr_seat,
67 &keyboard_grab->keyboard->modifiers); 78 &keyboard_grab->keyboard->modifiers);
68 } 79 }
69} 80}
@@ -102,6 +113,10 @@ static void handle_im_destroy(struct wl_listener *listener, void *data) {
102 input_method_destroy); 113 input_method_destroy);
103 struct wlr_input_method_v2 *context = data; 114 struct wlr_input_method_v2 *context = data;
104 assert(context == relay->input_method); 115 assert(context == relay->input_method);
116 wl_list_remove(&relay->input_method_commit.link);
117 wl_list_remove(&relay->input_method_grab_keyboard.link);
118 wl_list_remove(&relay->input_method_destroy.link);
119 wl_list_remove(&relay->input_method_new_popup_surface.link);
105 relay->input_method = NULL; 120 relay->input_method = NULL;
106 struct sway_text_input *text_input = relay_get_focused_text_input(relay); 121 struct sway_text_input *text_input = relay_get_focused_text_input(relay);
107 if (text_input) { 122 if (text_input) {
@@ -133,6 +148,11 @@ static void relay_send_im_state(struct sway_input_method_relay *relay,
133 input->current.content_type.hint, 148 input->current.content_type.hint,
134 input->current.content_type.purpose); 149 input->current.content_type.purpose);
135 } 150 }
151 struct sway_input_popup *popup;
152 wl_list_for_each(popup, &relay->input_popups, link) {
153 // send_text_input_rectangle is called in this function
154 input_popup_update(popup);
155 }
136 wlr_input_method_v2_send_done(input_method); 156 wlr_input_method_v2_send_done(input_method);
137 // TODO: pass intent, display popup size 157 // TODO: pass intent, display popup size
138} 158}
@@ -255,6 +275,211 @@ static void relay_handle_text_input(struct wl_listener *listener,
255 sway_text_input_create(relay, wlr_text_input); 275 sway_text_input_create(relay, wlr_text_input);
256} 276}
257 277
278static void input_popup_update(struct sway_input_popup *popup) {
279 struct sway_text_input *text_input =
280 relay_get_focused_text_input(popup->relay);
281
282 if (text_input == NULL || text_input->input->focused_surface == NULL) {
283 return;
284 }
285
286 if (popup->scene_tree != NULL) {
287 wlr_scene_node_destroy(&popup->scene_tree->node);
288 popup->scene_tree = NULL;
289 }
290 if (popup->desc.relative != NULL) {
291 wlr_scene_node_destroy(popup->desc.relative);
292 popup->desc.relative = NULL;
293 }
294 popup->desc.view = NULL;
295
296 if (!popup->popup_surface->surface->mapped) {
297 return;
298 }
299
300 bool cursor_rect = text_input->input->current.features
301 & WLR_TEXT_INPUT_V3_FEATURE_CURSOR_RECTANGLE;
302 struct wlr_surface *focused_surface = text_input->input->focused_surface;
303 struct wlr_box cursor_area = text_input->input->current.cursor_rectangle;
304
305 struct wlr_box output_box;
306 struct wlr_box parent = {0};
307 struct wlr_layer_surface_v1 *layer_surface =
308 wlr_layer_surface_v1_try_from_wlr_surface(focused_surface);
309 struct wlr_scene_tree *relative_parent;
310
311 struct wlr_box geo = {0};
312
313 popup->scene_tree = wlr_scene_subsurface_tree_create(root->layers.popup, popup->popup_surface->surface);
314 if (layer_surface != NULL) {
315 struct sway_layer_surface *layer =
316 layer_surface->data;
317 if (layer == NULL) {
318 return;
319 }
320
321 relative_parent = layer->scene->tree;
322 struct wlr_output *output = layer->layer_surface->output;
323 wlr_output_layout_get_box(root->output_layout, output, &output_box);
324 int lx, ly;
325 wlr_scene_node_coords(&layer->tree->node, &lx, &ly);
326 parent.x = lx;
327 parent.y = ly;
328 popup->desc.view = NULL;
329 } else {
330 struct sway_view *view = view_from_wlr_surface(focused_surface);
331 relative_parent = view->scene_tree;
332 geo = view->geometry;
333 int lx, ly;
334 wlr_scene_node_coords(&view->scene_tree->node, &lx, &ly);
335 struct wlr_output *output = wlr_output_layout_output_at(root->output_layout,
336 view->container->pending.content_x + view->geometry.x,
337 view->container->pending.content_y + view->geometry.y);
338 wlr_output_layout_get_box(root->output_layout, output, &output_box);
339 parent.x = lx;
340 parent.y = ly;
341
342 parent.width = view->geometry.width;
343 parent.height = view->geometry.height;
344 popup->desc.view = view;
345 }
346
347 struct wlr_scene_tree *relative = wlr_scene_tree_create(relative_parent);
348
349 popup->desc.relative = &relative->node;
350 if (!scene_descriptor_assign(&popup->scene_tree->node,
351 SWAY_SCENE_DESC_POPUP, &popup->desc)) {
352 wlr_scene_node_destroy(&popup->scene_tree->node);
353 popup->scene_tree = NULL;
354 return;
355 }
356
357 if (!cursor_rect) {
358 cursor_area.x = 0;
359 cursor_area.y = 0;
360 cursor_area.width = parent.width;
361 cursor_area.height = parent.height;
362 }
363
364 int popup_width = popup->popup_surface->surface->current.width;
365 int popup_height = popup->popup_surface->surface->current.height;
366 int x1 = parent.x + cursor_area.x;
367 int x2 = parent.x + cursor_area.x + cursor_area.width;
368 int y1 = parent.y + cursor_area.y;
369 int y2 = parent.y + cursor_area.y + cursor_area.height;
370 int x = x1;
371 int y = y2;
372
373 int available_right = output_box.x + output_box.width - x1;
374 int available_left = x2 - output_box.x;
375 if (available_right < popup_width && available_left > available_right) {
376 x = x2 - popup_width;
377 }
378
379 int available_down = output_box.y + output_box.height - y2;
380 int available_up = y1 - output_box.y;
381 if (available_down < popup_height && available_up > available_down) {
382 y = y1 - popup_height;
383 }
384
385 wlr_scene_node_set_position(&relative->node, x - parent.x - geo.x, y - parent.y - geo.y);
386 if (cursor_rect) {
387 struct wlr_box box = {
388 .x = x1 - x,
389 .y = y1 - y,
390 .width = cursor_area.width,
391 .height = cursor_area.height,
392 };
393 wlr_input_popup_surface_v2_send_text_input_rectangle(
394 popup->popup_surface, &box);
395 }
396 wlr_scene_node_set_position(&popup->scene_tree->node, x - geo.x, y - geo.y);
397}
398
399static void input_popup_set_focus(struct sway_input_popup *popup,
400 struct wlr_surface *surface) {
401 wl_list_remove(&popup->focused_surface_unmap.link);
402
403 if (surface == NULL) {
404 wl_list_init(&popup->focused_surface_unmap.link);
405 input_popup_update(popup);
406 return;
407 }
408 struct wlr_layer_surface_v1 *layer_surface =
409 wlr_layer_surface_v1_try_from_wlr_surface(surface);
410 if (layer_surface != NULL) {
411 wl_signal_add(
412 &layer_surface->surface->events.unmap, &popup->focused_surface_unmap);
413 input_popup_update(popup);
414 return;
415 }
416
417 struct sway_view *view = view_from_wlr_surface(surface);
418 wl_signal_add(&view->events.unmap, &popup->focused_surface_unmap);
419}
420
421static void handle_im_popup_destroy(struct wl_listener *listener, void *data) {
422 struct sway_input_popup *popup =
423 wl_container_of(listener, popup, popup_destroy);
424 wl_list_remove(&popup->focused_surface_unmap.link);
425 wl_list_remove(&popup->popup_surface_commit.link);
426 wl_list_remove(&popup->popup_destroy.link);
427 wl_list_remove(&popup->link);
428
429 free(popup);
430}
431
432static void handle_im_popup_surface_commit(struct wl_listener *listener,
433 void *data) {
434 struct sway_input_popup *popup =
435 wl_container_of(listener, popup, popup_surface_commit);
436 input_popup_update(popup);
437}
438
439static void handle_im_focused_surface_unmap(
440 struct wl_listener *listener, void *data) {
441 struct sway_input_popup *popup =
442 wl_container_of(listener, popup, focused_surface_unmap);
443 input_popup_update(popup);
444}
445
446static void handle_im_new_popup_surface(struct wl_listener *listener,
447 void *data) {
448 struct sway_input_method_relay *relay = wl_container_of(listener, relay,
449 input_method_new_popup_surface);
450 struct sway_input_popup *popup = calloc(1, sizeof(*popup));
451 popup->relay = relay;
452 popup->popup_surface = data;
453 popup->popup_surface->data = popup;
454
455 wl_signal_add(
456 &popup->popup_surface->events.destroy, &popup->popup_destroy);
457 popup->popup_destroy.notify = handle_im_popup_destroy;
458 wl_signal_add(&popup->popup_surface->surface->events.commit,
459 &popup->popup_surface_commit);
460 popup->popup_surface_commit.notify = handle_im_popup_surface_commit;
461 wl_list_init(&popup->focused_surface_unmap.link);
462 popup->focused_surface_unmap.notify = handle_im_focused_surface_unmap;
463
464 struct sway_text_input *text_input = relay_get_focused_text_input(relay);
465 if (text_input != NULL) {
466 input_popup_set_focus(popup, text_input->input->focused_surface);
467 } else {
468 input_popup_set_focus(popup, NULL);
469 }
470
471 wl_list_insert(&relay->input_popups, &popup->link);
472}
473
474static void text_input_send_enter(struct sway_text_input *text_input,
475 struct wlr_surface *surface) {
476 wlr_text_input_v3_send_enter(text_input->input, surface);
477 struct sway_input_popup *popup;
478 wl_list_for_each(popup, &text_input->relay->input_popups, link) {
479 input_popup_set_focus(popup, surface);
480 }
481}
482
258static void relay_handle_input_method(struct wl_listener *listener, 483static void relay_handle_input_method(struct wl_listener *listener,
259 void *data) { 484 void *data) {
260 struct sway_input_method_relay *relay = wl_container_of(listener, relay, 485 struct sway_input_method_relay *relay = wl_container_of(listener, relay,
@@ -280,10 +505,13 @@ static void relay_handle_input_method(struct wl_listener *listener,
280 wl_signal_add(&relay->input_method->events.destroy, 505 wl_signal_add(&relay->input_method->events.destroy,
281 &relay->input_method_destroy); 506 &relay->input_method_destroy);
282 relay->input_method_destroy.notify = handle_im_destroy; 507 relay->input_method_destroy.notify = handle_im_destroy;
508 wl_signal_add(&relay->input_method->events.new_popup_surface,
509 &relay->input_method_new_popup_surface);
510 relay->input_method_new_popup_surface.notify = handle_im_new_popup_surface;
283 511
284 struct sway_text_input *text_input = relay_get_focusable_text_input(relay); 512 struct sway_text_input *text_input = relay_get_focusable_text_input(relay);
285 if (text_input) { 513 if (text_input) {
286 wlr_text_input_v3_send_enter(text_input->input, 514 text_input_send_enter(text_input,
287 text_input->pending_focused_surface); 515 text_input->pending_focused_surface);
288 text_input_set_pending_focused_surface(text_input, NULL); 516 text_input_set_pending_focused_surface(text_input, NULL);
289 } 517 }
@@ -293,6 +521,7 @@ void sway_input_method_relay_init(struct sway_seat *seat,
293 struct sway_input_method_relay *relay) { 521 struct sway_input_method_relay *relay) {
294 relay->seat = seat; 522 relay->seat = seat;
295 wl_list_init(&relay->text_inputs); 523 wl_list_init(&relay->text_inputs);
524 wl_list_init(&relay->input_popups);
296 525
297 relay->text_input_new.notify = relay_handle_text_input; 526 relay->text_input_new.notify = relay_handle_text_input;
298 wl_signal_add(&server.text_input->events.text_input, 527 wl_signal_add(&server.text_input->events.text_input,
diff --git a/sway/ipc-json.c b/sway/ipc-json.c
index e682bc36..1ee39124 100644
--- a/sway/ipc-json.c
+++ b/sway/ipc-json.c
@@ -3,13 +3,15 @@
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"
10#include "log.h" 11#include "log.h"
11#include "sway/config.h" 12#include "sway/config.h"
12#include "sway/ipc-json.h" 13#include "sway/ipc-json.h"
14#include "sway/server.h"
13#include "sway/tree/container.h" 15#include "sway/tree/container.h"
14#include "sway/tree/view.h" 16#include "sway/tree/view.h"
15#include "sway/tree/workspace.h" 17#include "sway/tree/workspace.h"
@@ -20,6 +22,10 @@
20#include "wlr-layer-shell-unstable-v1-protocol.h" 22#include "wlr-layer-shell-unstable-v1-protocol.h"
21#include "sway/desktop/idle_inhibit_v1.h" 23#include "sway/desktop/idle_inhibit_v1.h"
22 24
25#if WLR_HAS_LIBINPUT_BACKEND
26#include <wlr/backend/libinput.h>
27#endif
28
23static const int i3_output_id = INT32_MAX; 29static const int i3_output_id = INT32_MAX;
24static const int i3_scratch_id = INT32_MAX - 1; 30static const int i3_scratch_id = INT32_MAX - 1;
25 31
@@ -112,13 +118,44 @@ static const char *ipc_json_output_adaptive_sync_status_description(
112 return "disabled"; 118 return "disabled";
113 case WLR_OUTPUT_ADAPTIVE_SYNC_ENABLED: 119 case WLR_OUTPUT_ADAPTIVE_SYNC_ENABLED:
114 return "enabled"; 120 return "enabled";
115 case WLR_OUTPUT_ADAPTIVE_SYNC_UNKNOWN:
116 return "unknown";
117 } 121 }
118 return NULL; 122 return NULL;
119} 123}
120 124
121#if HAVE_XWAYLAND 125static const char *ipc_json_output_mode_aspect_ratio_description(
126 enum wlr_output_mode_aspect_ratio aspect_ratio) {
127 switch (aspect_ratio) {
128 case WLR_OUTPUT_MODE_ASPECT_RATIO_NONE:
129 return "none";
130 case WLR_OUTPUT_MODE_ASPECT_RATIO_4_3:
131 return "4:3";
132 case WLR_OUTPUT_MODE_ASPECT_RATIO_16_9:
133 return "16:9";
134 case WLR_OUTPUT_MODE_ASPECT_RATIO_64_27:
135 return "64:27";
136 case WLR_OUTPUT_MODE_ASPECT_RATIO_256_135:
137 return "256:135";
138 }
139 return NULL;
140}
141
142static json_object *ipc_json_output_mode_description(
143 const struct wlr_output_mode *mode) {
144 const char *pic_ar =
145 ipc_json_output_mode_aspect_ratio_description(mode->picture_aspect_ratio);
146 json_object *mode_object = json_object_new_object();
147 json_object_object_add(mode_object, "width",
148 json_object_new_int(mode->width));
149 json_object_object_add(mode_object, "height",
150 json_object_new_int(mode->height));
151 json_object_object_add(mode_object, "refresh",
152 json_object_new_int(mode->refresh));
153 json_object_object_add(mode_object, "picture_aspect_ratio",
154 json_object_new_string(pic_ar));
155 return mode_object;
156}
157
158#if WLR_HAS_XWAYLAND
122static const char *ipc_json_xwindow_type_description(struct sway_view *view) { 159static const char *ipc_json_xwindow_type_description(struct sway_view *view) {
123 struct wlr_xwayland_surface *surface = view->wlr_xwayland_surface; 160 struct wlr_xwayland_surface *surface = view->wlr_xwayland_surface;
124 struct sway_xwayland *xwayland = &server.xwayland; 161 struct sway_xwayland *xwayland = &server.xwayland;
@@ -170,6 +207,20 @@ static const char *ipc_json_user_idle_inhibitor_description(enum sway_idle_inhib
170 return NULL; 207 return NULL;
171} 208}
172 209
210static const char *ipc_json_content_type_description(enum wp_content_type_v1_type type) {
211 switch (type) {
212 case WP_CONTENT_TYPE_V1_TYPE_NONE:
213 return "none";
214 case WP_CONTENT_TYPE_V1_TYPE_PHOTO:
215 return "photo";
216 case WP_CONTENT_TYPE_V1_TYPE_VIDEO:
217 return "video";
218 case WP_CONTENT_TYPE_V1_TYPE_GAME:
219 return "game";
220 }
221 return NULL;
222}
223
173json_object *ipc_json_get_version(void) { 224json_object *ipc_json_get_version(void) {
174 int major = 0, minor = 0, patch = 0; 225 int major = 0, minor = 0, patch = 0;
175 json_object *version = json_object_new_object(); 226 json_object *version = json_object_new_object();
@@ -238,14 +289,13 @@ static json_object *ipc_json_create_node(int id, const char* type, char *name,
238 json_object_object_add(object, "focus", focus); 289 json_object_object_add(object, "focus", focus);
239 json_object_object_add(object, "fullscreen_mode", json_object_new_int(0)); 290 json_object_object_add(object, "fullscreen_mode", json_object_new_int(0));
240 json_object_object_add(object, "sticky", json_object_new_boolean(false)); 291 json_object_object_add(object, "sticky", json_object_new_boolean(false));
292 json_object_object_add(object, "floating", NULL);
293 json_object_object_add(object, "scratchpad_state", NULL);
241 294
242 return object; 295 return object;
243} 296}
244 297
245static void ipc_json_describe_output(struct sway_output *output, 298static void ipc_json_describe_wlr_output(struct wlr_output *wlr_output, json_object *object) {
246 json_object *object) {
247 struct wlr_output *wlr_output = output->wlr_output;
248
249 json_object_object_add(object, "primary", json_object_new_boolean(false)); 299 json_object_object_add(object, "primary", json_object_new_boolean(false));
250 json_object_object_add(object, "make", 300 json_object_object_add(object, "make",
251 json_object_new_string(wlr_output->make ? wlr_output->make : "Unknown")); 301 json_object_new_string(wlr_output->make ? wlr_output->make : "Unknown"));
@@ -269,14 +319,22 @@ static void ipc_json_describe_output(struct sway_output *output,
269 json_object_object_add(object, "modes", modes_array); 319 json_object_object_add(object, "modes", modes_array);
270} 320}
271 321
322static void ipc_json_describe_output(struct sway_output *output,
323 json_object *object) {
324 ipc_json_describe_wlr_output(output->wlr_output, object);
325}
326
272static void ipc_json_describe_enabled_output(struct sway_output *output, 327static void ipc_json_describe_enabled_output(struct sway_output *output,
273 json_object *object) { 328 json_object *object) {
274 ipc_json_describe_output(output, object); 329 ipc_json_describe_output(output, object);
275 330
276 struct wlr_output *wlr_output = output->wlr_output; 331 struct wlr_output *wlr_output = output->wlr_output;
332 json_object_object_add(object, "non_desktop", json_object_new_boolean(false));
277 json_object_object_add(object, "active", json_object_new_boolean(true)); 333 json_object_object_add(object, "active", json_object_new_boolean(true));
278 json_object_object_add(object, "dpms", 334 json_object_object_add(object, "dpms",
279 json_object_new_boolean(wlr_output->enabled)); 335 json_object_new_boolean(wlr_output->enabled));
336 json_object_object_add(object, "power",
337 json_object_new_boolean(wlr_output->enabled));
280 json_object_object_add(object, "layout", json_object_new_string("output")); 338 json_object_object_add(object, "layout", json_object_new_string("output"));
281 json_object_object_add(object, "orientation", 339 json_object_object_add(object, "orientation",
282 json_object_new_string( 340 json_object_new_string(
@@ -305,25 +363,26 @@ static void ipc_json_describe_enabled_output(struct sway_output *output,
305 json_object *modes_array = json_object_new_array(); 363 json_object *modes_array = json_object_new_array();
306 struct wlr_output_mode *mode; 364 struct wlr_output_mode *mode;
307 wl_list_for_each(mode, &wlr_output->modes, link) { 365 wl_list_for_each(mode, &wlr_output->modes, link) {
308 json_object *mode_object = json_object_new_object(); 366 json_object *mode_object =
309 json_object_object_add(mode_object, "width", 367 ipc_json_output_mode_description(mode);
310 json_object_new_int(mode->width));
311 json_object_object_add(mode_object, "height",
312 json_object_new_int(mode->height));
313 json_object_object_add(mode_object, "refresh",
314 json_object_new_int(mode->refresh));
315 json_object_array_add(modes_array, mode_object); 368 json_object_array_add(modes_array, mode_object);
316 } 369 }
317 370
318 json_object_object_add(object, "modes", modes_array); 371 json_object_object_add(object, "modes", modes_array);
319 372
320 json_object *current_mode_object = json_object_new_object(); 373 json_object *current_mode_object;
321 json_object_object_add(current_mode_object, "width", 374 if (wlr_output->current_mode != NULL) {
322 json_object_new_int(wlr_output->width)); 375 current_mode_object =
323 json_object_object_add(current_mode_object, "height", 376 ipc_json_output_mode_description(wlr_output->current_mode);
324 json_object_new_int(wlr_output->height)); 377 } else {
325 json_object_object_add(current_mode_object, "refresh", 378 current_mode_object = json_object_new_object();
326 json_object_new_int(wlr_output->refresh)); 379 json_object_object_add(current_mode_object, "width",
380 json_object_new_int(wlr_output->width));
381 json_object_object_add(current_mode_object, "height",
382 json_object_new_int(wlr_output->height));
383 json_object_object_add(current_mode_object, "refresh",
384 json_object_new_int(wlr_output->refresh));
385 }
327 json_object_object_add(object, "current_mode", current_mode_object); 386 json_object_object_add(object, "current_mode", current_mode_object);
328 387
329 struct sway_node *parent = node_get_parent(&output->node); 388 struct sway_node *parent = node_get_parent(&output->node);
@@ -349,11 +408,13 @@ json_object *ipc_json_describe_disabled_output(struct sway_output *output) {
349 408
350 ipc_json_describe_output(output, object); 409 ipc_json_describe_output(output, object);
351 410
411 json_object_object_add(object, "non_desktop", json_object_new_boolean(false));
352 json_object_object_add(object, "type", json_object_new_string("output")); 412 json_object_object_add(object, "type", json_object_new_string("output"));
353 json_object_object_add(object, "name", 413 json_object_object_add(object, "name",
354 json_object_new_string(wlr_output->name)); 414 json_object_new_string(wlr_output->name));
355 json_object_object_add(object, "active", json_object_new_boolean(false)); 415 json_object_object_add(object, "active", json_object_new_boolean(false));
356 json_object_object_add(object, "dpms", json_object_new_boolean(false)); 416 json_object_object_add(object, "dpms", json_object_new_boolean(false));
417 json_object_object_add(object, "power", json_object_new_boolean(false));
357 418
358 json_object_object_add(object, "current_workspace", NULL); 419 json_object_object_add(object, "current_workspace", NULL);
359 420
@@ -369,6 +430,21 @@ json_object *ipc_json_describe_disabled_output(struct sway_output *output) {
369 return object; 430 return object;
370} 431}
371 432
433json_object *ipc_json_describe_non_desktop_output(struct sway_output_non_desktop *output) {
434 struct wlr_output *wlr_output = output->wlr_output;
435
436 json_object *object = json_object_new_object();
437
438 ipc_json_describe_wlr_output(wlr_output, object);
439
440 json_object_object_add(object, "non_desktop", json_object_new_boolean(true));
441 json_object_object_add(object, "type", json_object_new_string("output"));
442 json_object_object_add(object, "name",
443 json_object_new_string(wlr_output->name));
444
445 return object;
446}
447
372static json_object *ipc_json_describe_scratchpad_output(void) { 448static json_object *ipc_json_describe_scratchpad_output(void) {
373 struct wlr_box box; 449 struct wlr_box box;
374 root_get_box(root, &box); 450 root_get_box(root, &box);
@@ -455,7 +531,9 @@ static void ipc_json_describe_workspace(struct sway_workspace *workspace,
455 531
456static void get_deco_rect(struct sway_container *c, struct wlr_box *deco_rect) { 532static void get_deco_rect(struct sway_container *c, struct wlr_box *deco_rect) {
457 enum sway_container_layout parent_layout = container_parent_layout(c); 533 enum sway_container_layout parent_layout = container_parent_layout(c);
458 bool tab_or_stack = parent_layout == L_TABBED || parent_layout == L_STACKED; 534 list_t *siblings = container_get_siblings(c);
535 bool tab_or_stack = (parent_layout == L_TABBED || parent_layout == L_STACKED)
536 && ((siblings && siblings->length > 1) || !config->hide_lone_tab);
459 if (((!tab_or_stack || container_is_floating(c)) && 537 if (((!tab_or_stack || container_is_floating(c)) &&
460 c->current.border != B_NORMAL) || 538 c->current.border != B_NORMAL) ||
461 c->pending.fullscreen_mode != FULLSCREEN_NONE || 539 c->pending.fullscreen_mode != FULLSCREEN_NONE ||
@@ -502,7 +580,7 @@ static void ipc_json_describe_view(struct sway_container *c, json_object *object
502 580
503 struct wlr_box window_box = { 581 struct wlr_box window_box = {
504 c->pending.content_x - c->pending.x, 582 c->pending.content_x - c->pending.x,
505 (c->current.border == B_PIXEL) ? c->current.border_thickness : 0, 583 (c->current.border == B_PIXEL) ? c->pending.content_y - c->pending.y : 0,
506 c->pending.content_width, 584 c->pending.content_width,
507 c->pending.content_height 585 c->pending.content_height
508 }; 586 };
@@ -546,7 +624,17 @@ static void ipc_json_describe_view(struct sway_container *c, json_object *object
546 624
547 json_object_object_add(object, "idle_inhibitors", idle_inhibitors); 625 json_object_object_add(object, "idle_inhibitors", idle_inhibitors);
548 626
549#if HAVE_XWAYLAND 627 enum wp_content_type_v1_type content_type = WP_CONTENT_TYPE_V1_TYPE_NONE;
628 if (c->view->surface != NULL) {
629 content_type = wlr_surface_get_content_type_v1(server.content_type_manager_v1,
630 c->view->surface);
631 }
632 if (content_type != WP_CONTENT_TYPE_V1_TYPE_NONE) {
633 json_object_object_add(object, "content_type",
634 json_object_new_string(ipc_json_content_type_description(content_type)));
635 }
636
637#if WLR_HAS_XWAYLAND
550 if (c->view->type == SWAY_VIEW_XWAYLAND) { 638 if (c->view->type == SWAY_VIEW_XWAYLAND) {
551 json_object_object_add(object, "window", 639 json_object_object_add(object, "window",
552 json_object_new_int(view_get_x11_window_id(c->view))); 640 json_object_new_int(view_get_x11_window_id(c->view)));
@@ -590,7 +678,8 @@ static void ipc_json_describe_view(struct sway_container *c, json_object *object
590static void ipc_json_describe_container(struct sway_container *c, json_object *object) { 678static void ipc_json_describe_container(struct sway_container *c, json_object *object) {
591 json_object_object_add(object, "name", 679 json_object_object_add(object, "name",
592 c->title ? json_object_new_string(c->title) : NULL); 680 c->title ? json_object_new_string(c->title) : NULL);
593 if (container_is_floating(c)) { 681 bool floating = container_is_floating(c);
682 if (floating) {
594 json_object_object_add(object, "type", 683 json_object_object_add(object, "type",
595 json_object_new_string("floating_con")); 684 json_object_new_string("floating_con"));
596 } 685 }
@@ -608,9 +697,17 @@ static void ipc_json_describe_container(struct sway_container *c, json_object *o
608 json_object_object_add(object, "urgent", json_object_new_boolean(urgent)); 697 json_object_object_add(object, "urgent", json_object_new_boolean(urgent));
609 json_object_object_add(object, "sticky", json_object_new_boolean(c->is_sticky)); 698 json_object_object_add(object, "sticky", json_object_new_boolean(c->is_sticky));
610 699
700 // sway doesn't track the floating reason, so we can't use "auto_on" or "user_off"
701 json_object_object_add(object, "floating",
702 json_object_new_string(floating ? "user_on" : "auto_off"));
703
611 json_object_object_add(object, "fullscreen_mode", 704 json_object_object_add(object, "fullscreen_mode",
612 json_object_new_int(c->pending.fullscreen_mode)); 705 json_object_new_int(c->pending.fullscreen_mode));
613 706
707 // sway doesn't track if window was resized in scratchpad, so we can't use "changed"
708 json_object_object_add(object, "scratchpad_state",
709 json_object_new_string(!c->scratchpad ? "none" : "fresh"));
710
614 struct sway_node *parent = node_get_parent(&c->node); 711 struct sway_node *parent = node_get_parent(&c->node);
615 struct wlr_box parent_box = {0, 0, 0, 0}; 712 struct wlr_box parent_box = {0, 0, 0, 0};
616 713
@@ -766,6 +863,7 @@ json_object *ipc_json_describe_node_recursive(struct sway_node *node) {
766 return object; 863 return object;
767} 864}
768 865
866#if WLR_HAS_LIBINPUT_BACKEND
769static json_object *describe_libinput_device(struct libinput_device *device) { 867static json_object *describe_libinput_device(struct libinput_device *device) {
770 json_object *object = json_object_new_object(); 868 json_object *object = json_object_new_object();
771 869
@@ -849,6 +947,11 @@ static json_object *describe_libinput_device(struct libinput_device *device) {
849 case LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE: 947 case LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE:
850 accel_profile = "adaptive"; 948 accel_profile = "adaptive";
851 break; 949 break;
950#if HAVE_LIBINPUT_CONFIG_ACCEL_PROFILE_CUSTOM
951 case LIBINPUT_CONFIG_ACCEL_PROFILE_CUSTOM:
952 accel_profile = "custom";
953 break;
954#endif
852 } 955 }
853 json_object_object_add(object, "accel_profile", 956 json_object_object_add(object, "accel_profile",
854 json_object_new_string(accel_profile)); 957 json_object_new_string(accel_profile));
@@ -928,6 +1031,17 @@ static json_object *describe_libinput_device(struct libinput_device *device) {
928 uint32_t button = libinput_device_config_scroll_get_button(device); 1031 uint32_t button = libinput_device_config_scroll_get_button(device);
929 json_object_object_add(object, "scroll_button", 1032 json_object_object_add(object, "scroll_button",
930 json_object_new_int(button)); 1033 json_object_new_int(button));
1034 const char *lock = "unknown";
1035 switch (libinput_device_config_scroll_get_button_lock(device)) {
1036 case LIBINPUT_CONFIG_SCROLL_BUTTON_LOCK_ENABLED:
1037 lock = "enabled";
1038 break;
1039 case LIBINPUT_CONFIG_SCROLL_BUTTON_LOCK_DISABLED:
1040 lock = "disabled";
1041 break;
1042 }
1043 json_object_object_add(object, "scroll_button_lock",
1044 json_object_new_string(lock));
931 } 1045 }
932 } 1046 }
933 1047
@@ -944,6 +1058,19 @@ static json_object *describe_libinput_device(struct libinput_device *device) {
944 json_object_object_add(object, "dwt", json_object_new_string(dwt)); 1058 json_object_object_add(object, "dwt", json_object_new_string(dwt));
945 } 1059 }
946 1060
1061 if (libinput_device_config_dwtp_is_available(device)) {
1062 const char *dwtp = "unknown";
1063 switch (libinput_device_config_dwtp_get_enabled(device)) {
1064 case LIBINPUT_CONFIG_DWTP_ENABLED:
1065 dwtp = "enabled";
1066 break;
1067 case LIBINPUT_CONFIG_DWTP_DISABLED:
1068 dwtp = "disabled";
1069 break;
1070 }
1071 json_object_object_add(object, "dwtp", json_object_new_string(dwtp));
1072 }
1073
947 if (libinput_device_config_calibration_has_matrix(device)) { 1074 if (libinput_device_config_calibration_has_matrix(device)) {
948 float matrix[6]; 1075 float matrix[6];
949 libinput_device_config_calibration_get_matrix(device, matrix); 1076 libinput_device_config_calibration_get_matrix(device, matrix);
@@ -958,6 +1085,7 @@ static json_object *describe_libinput_device(struct libinput_device *device) {
958 1085
959 return object; 1086 return object;
960} 1087}
1088#endif
961 1089
962json_object *ipc_json_describe_input(struct sway_input_device *device) { 1090json_object *ipc_json_describe_input(struct sway_input_device *device) {
963 if (!(sway_assert(device, "Device must not be null"))) { 1091 if (!(sway_assert(device, "Device must not be null"))) {
@@ -970,19 +1098,16 @@ json_object *ipc_json_describe_input(struct sway_input_device *device) {
970 json_object_new_string(device->identifier)); 1098 json_object_new_string(device->identifier));
971 json_object_object_add(object, "name", 1099 json_object_object_add(object, "name",
972 json_object_new_string(device->wlr_device->name)); 1100 json_object_new_string(device->wlr_device->name));
973 json_object_object_add(object, "vendor",
974 json_object_new_int(device->wlr_device->vendor));
975 json_object_object_add(object, "product",
976 json_object_new_int(device->wlr_device->product));
977 json_object_object_add(object, "type", 1101 json_object_object_add(object, "type",
978 json_object_new_string( 1102 json_object_new_string(
979 input_device_get_type(device))); 1103 input_device_get_type(device)));
980 1104
981 if (device->wlr_device->type == WLR_INPUT_DEVICE_KEYBOARD) { 1105 if (device->wlr_device->type == WLR_INPUT_DEVICE_KEYBOARD) {
982 struct wlr_keyboard *keyboard = device->wlr_device->keyboard; 1106 struct wlr_keyboard *keyboard =
1107 wlr_keyboard_from_input_device(device->wlr_device);
983 struct xkb_keymap *keymap = keyboard->keymap; 1108 struct xkb_keymap *keymap = keyboard->keymap;
984 struct xkb_state *state = keyboard->xkb_state; 1109 struct xkb_state *state = keyboard->xkb_state;
985 1110
986 json_object_object_add(object, "repeat_delay", 1111 json_object_object_add(object, "repeat_delay",
987 json_object_new_int(keyboard->repeat_info.delay)); 1112 json_object_new_int(keyboard->repeat_info.delay));
988 json_object_object_add(object, "repeat_rate", 1113 json_object_object_add(object, "repeat_rate",
@@ -1012,20 +1137,26 @@ json_object *ipc_json_describe_input(struct sway_input_device *device) {
1012 if (device->wlr_device->type == WLR_INPUT_DEVICE_POINTER) { 1137 if (device->wlr_device->type == WLR_INPUT_DEVICE_POINTER) {
1013 struct input_config *ic = input_device_get_config(device); 1138 struct input_config *ic = input_device_get_config(device);
1014 float scroll_factor = 1.0f; 1139 float scroll_factor = 1.0f;
1015 if (ic != NULL && !isnan(ic->scroll_factor) && 1140 if (ic != NULL && !isnan(ic->scroll_factor) &&
1016 ic->scroll_factor != FLT_MIN) { 1141 ic->scroll_factor != FLT_MIN) {
1017 scroll_factor = ic->scroll_factor; 1142 scroll_factor = ic->scroll_factor;
1018 } 1143 }
1019 json_object_object_add(object, "scroll_factor", 1144 json_object_object_add(object, "scroll_factor",
1020 json_object_new_double(scroll_factor)); 1145 json_object_new_double(scroll_factor));
1021 } 1146 }
1022 1147
1148#if WLR_HAS_LIBINPUT_BACKEND
1023 if (wlr_input_device_is_libinput(device->wlr_device)) { 1149 if (wlr_input_device_is_libinput(device->wlr_device)) {
1024 struct libinput_device *libinput_dev; 1150 struct libinput_device *libinput_dev;
1025 libinput_dev = wlr_libinput_get_device_handle(device->wlr_device); 1151 libinput_dev = wlr_libinput_get_device_handle(device->wlr_device);
1026 json_object_object_add(object, "libinput", 1152 json_object_object_add(object, "libinput",
1027 describe_libinput_device(libinput_dev)); 1153 describe_libinput_device(libinput_dev));
1154 json_object_object_add(object, "vendor",
1155 json_object_new_int(libinput_device_get_id_vendor(libinput_dev)));
1156 json_object_object_add(object, "product",
1157 json_object_new_int(libinput_device_get_id_product(libinput_dev)));
1028 } 1158 }
1159#endif
1029 1160
1030 return object; 1161 return object;
1031} 1162}
diff --git a/sway/ipc-server.c b/sway/ipc-server.c
index 1bf5a05f..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) {
@@ -692,6 +697,12 @@ void ipc_client_handle_command(struct ipc_client *client, uint32_t payload_lengt
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
index 04f80079..289e8ca4 100644
--- a/sway/lock.c
+++ b/sway/lock.c
@@ -1,101 +1,229 @@
1#define _POSIX_C_SOURCE 200809L
2#include <assert.h> 1#include <assert.h>
2#include <wlr/types/wlr_scene.h>
3#include <wlr/types/wlr_session_lock_v1.h>
3#include "log.h" 4#include "log.h"
5#include "sway/input/cursor.h"
4#include "sway/input/keyboard.h" 6#include "sway/input/keyboard.h"
5#include "sway/input/seat.h" 7#include "sway/input/seat.h"
8#include "sway/layers.h"
6#include "sway/output.h" 9#include "sway/output.h"
7#include "sway/server.h" 10#include "sway/server.h"
8 11
9struct sway_session_lock_surface { 12struct sway_session_lock_output {
10 struct wlr_session_lock_surface_v1 *lock_surface; 13 struct wlr_scene_tree *tree;
14 struct wlr_scene_rect *background;
15 struct sway_session_lock *lock;
16
11 struct sway_output *output; 17 struct sway_output *output;
12 struct wlr_surface *surface; 18
13 struct wl_listener map; 19 struct wl_list link; // sway_session_lock::outputs
20
14 struct wl_listener destroy; 21 struct wl_listener destroy;
15 struct wl_listener surface_commit; 22 struct wl_listener commit;
16 struct wl_listener output_mode; 23
17 struct wl_listener output_commit; 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;
18}; 29};
19 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
20static void handle_surface_map(struct wl_listener *listener, void *data) { 62static void handle_surface_map(struct wl_listener *listener, void *data) {
21 struct sway_session_lock_surface *surf = wl_container_of(listener, surf, map); 63 struct sway_session_lock_output *surf = wl_container_of(listener, surf, surface_map);
22 sway_force_focus(surf->surface); 64 if (surf->lock->focused == NULL) {
23 output_damage_whole(surf->output); 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);
24} 119}
25 120
26static void handle_surface_commit(struct wl_listener *listener, void *data) { 121static void sway_session_lock_output_destroy(struct sway_session_lock_output *output) {
27 struct sway_session_lock_surface *surf = wl_container_of(listener, surf, surface_commit); 122 if (output->surface) {
28 output_damage_surface(surf->output, 0, 0, surf->surface, false); 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);
29} 133}
30 134
31static void handle_output_mode(struct wl_listener *listener, void *data) { 135static void lock_node_handle_destroy(struct wl_listener *listener, void *data) {
32 struct sway_session_lock_surface *surf = wl_container_of(listener, surf, output_mode); 136 struct sway_session_lock_output *output =
33 wlr_session_lock_surface_v1_configure(surf->lock_surface, 137 wl_container_of(listener, output, destroy);
34 surf->output->width, surf->output->height); 138 sway_session_lock_output_destroy(output);
35} 139}
36 140
37static void handle_output_commit(struct wl_listener *listener, void *data) { 141static void lock_output_handle_commit(struct wl_listener *listener, void *data) {
38 struct wlr_output_event_commit *event = data; 142 struct wlr_output_event_commit *event = data;
39 struct sway_session_lock_surface *surf = wl_container_of(listener, surf, output_commit); 143 struct sway_session_lock_output *output =
40 if (event->committed & ( 144 wl_container_of(listener, output, commit);
145 if (event->state->committed & (
41 WLR_OUTPUT_STATE_MODE | 146 WLR_OUTPUT_STATE_MODE |
42 WLR_OUTPUT_STATE_SCALE | 147 WLR_OUTPUT_STATE_SCALE |
43 WLR_OUTPUT_STATE_TRANSFORM)) { 148 WLR_OUTPUT_STATE_TRANSFORM)) {
44 wlr_session_lock_surface_v1_configure(surf->lock_surface, 149 lock_output_reconfigure(output);
45 surf->output->width, surf->output->height);
46 } 150 }
47} 151}
48 152
49static void handle_surface_destroy(struct wl_listener *listener, void *data) { 153static struct sway_session_lock_output *session_lock_output_create(
50 struct sway_session_lock_surface *surf = wl_container_of(listener, surf, destroy); 154 struct sway_session_lock *lock, struct sway_output *output) {
51 wl_list_remove(&surf->map.link); 155 struct sway_session_lock_output *lock_output = calloc(1, sizeof(*lock_output));
52 wl_list_remove(&surf->destroy.link); 156 if (!lock_output) {
53 wl_list_remove(&surf->surface_commit.link); 157 sway_log(SWAY_ERROR, "failed to allocate a session lock output");
54 wl_list_remove(&surf->output_mode.link); 158 return NULL;
55 wl_list_remove(&surf->output_commit.link); 159 }
56 output_damage_whole(surf->output); 160
57 free(surf); 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;
58} 197}
59 198
60static void handle_new_surface(struct wl_listener *listener, void *data) { 199static void sway_session_lock_destroy(struct sway_session_lock* lock) {
61 struct wlr_session_lock_surface_v1 *lock_surface = data; 200 struct sway_session_lock_output *lock_output, *tmp_lock_output;
62 struct sway_session_lock_surface *surf = calloc(1, sizeof(*surf)); 201 wl_list_for_each_safe(lock_output, tmp_lock_output, &lock->outputs, link) {
63 if (surf == NULL) { 202 // destroying the node will also destroy the whole lock output
64 return; 203 wlr_scene_node_destroy(&lock_output->tree->node);
65 } 204 }
66 205
67 sway_log(SWAY_DEBUG, "new lock layer surface"); 206 if (server.session_lock.lock == lock) {
207 server.session_lock.lock = NULL;
208 }
68 209
69 struct sway_output *output = lock_surface->output->data; 210 if (!lock->abandoned) {
70 wlr_session_lock_surface_v1_configure(lock_surface, output->width, output->height); 211 wl_list_remove(&lock->destroy.link);
71 212 wl_list_remove(&lock->unlock.link);
72 surf->lock_surface = lock_surface; 213 wl_list_remove(&lock->new_surface.link);
73 surf->surface = lock_surface->surface; 214 }
74 surf->output = output; 215
75 surf->map.notify = handle_surface_map; 216 free(lock);
76 wl_signal_add(&lock_surface->events.map, &surf->map);
77 surf->destroy.notify = handle_surface_destroy;
78 wl_signal_add(&lock_surface->events.destroy, &surf->destroy);
79 surf->surface_commit.notify = handle_surface_commit;
80 wl_signal_add(&surf->surface->events.commit, &surf->surface_commit);
81 surf->output_mode.notify = handle_output_mode;
82 wl_signal_add(&output->wlr_output->events.mode, &surf->output_mode);
83 surf->output_commit.notify = handle_output_commit;
84 wl_signal_add(&output->wlr_output->events.commit, &surf->output_commit);
85} 217}
86 218
87static void handle_unlock(struct wl_listener *listener, void *data) { 219static void handle_unlock(struct wl_listener *listener, void *data) {
220 struct sway_session_lock *lock = wl_container_of(listener, lock, unlock);
88 sway_log(SWAY_DEBUG, "session unlocked"); 221 sway_log(SWAY_DEBUG, "session unlocked");
89 server.session_lock.locked = false;
90 server.session_lock.lock = NULL;
91 222
92 wl_list_remove(&server.session_lock.lock_new_surface.link); 223 sway_session_lock_destroy(lock);
93 wl_list_remove(&server.session_lock.lock_unlock.link);
94 wl_list_remove(&server.session_lock.lock_destroy.link);
95 224
96 struct sway_seat *seat; 225 struct sway_seat *seat;
97 wl_list_for_each(seat, &server.input->seats, link) { 226 wl_list_for_each(seat, &server.input->seats, link) {
98 seat_set_exclusive_client(seat, NULL);
99 // copied from seat_set_focus_layer -- deduplicate? 227 // copied from seat_set_focus_layer -- deduplicate?
100 struct sway_node *previous = seat_get_focus_inactive(seat, &root->node); 228 struct sway_node *previous = seat_get_focus_inactive(seat, &root->node);
101 if (previous) { 229 if (previous) {
@@ -105,31 +233,28 @@ static void handle_unlock(struct wl_listener *listener, void *data) {
105 } 233 }
106 } 234 }
107 235
108 // redraw everything 236 // Triggers a refocus of the topmost surface layer if necessary
237 // TODO: Make layer surface focus per-output based on cursor position
109 for (int i = 0; i < root->outputs->length; ++i) { 238 for (int i = 0; i < root->outputs->length; ++i) {
110 struct sway_output *output = root->outputs->items[i]; 239 struct sway_output *output = root->outputs->items[i];
111 output_damage_whole(output); 240 arrange_layers(output);
112 } 241 }
113} 242}
114 243
115static void handle_abandon(struct wl_listener *listener, void *data) { 244static void handle_abandon(struct wl_listener *listener, void *data) {
245 struct sway_session_lock *lock = wl_container_of(listener, lock, destroy);
116 sway_log(SWAY_INFO, "session lock abandoned"); 246 sway_log(SWAY_INFO, "session lock abandoned");
117 server.session_lock.lock = NULL;
118
119 wl_list_remove(&server.session_lock.lock_new_surface.link);
120 wl_list_remove(&server.session_lock.lock_unlock.link);
121 wl_list_remove(&server.session_lock.lock_destroy.link);
122 247
123 struct sway_seat *seat; 248 struct sway_session_lock_output *lock_output;
124 wl_list_for_each(seat, &server.input->seats, link) { 249 wl_list_for_each(lock_output, &lock->outputs, link) {
125 seat->exclusive_client = NULL; 250 wlr_scene_rect_set_color(lock_output->background,
251 (float[4]){ 1.f, 0.f, 0.f, 1.f });
126 } 252 }
127 253
128 // redraw everything 254 lock->abandoned = true;
129 for (int i = 0; i < root->outputs->length; ++i) { 255 wl_list_remove(&lock->destroy.link);
130 struct sway_output *output = root->outputs->items[i]; 256 wl_list_remove(&lock->unlock.link);
131 output_damage_whole(output); 257 wl_list_remove(&lock->new_surface.link);
132 }
133} 258}
134 259
135static void handle_session_lock(struct wl_listener *listener, void *data) { 260static void handle_session_lock(struct wl_listener *listener, void *data) {
@@ -137,44 +262,89 @@ static void handle_session_lock(struct wl_listener *listener, void *data) {
137 struct wl_client *client = wl_resource_get_client(lock->resource); 262 struct wl_client *client = wl_resource_get_client(lock->resource);
138 263
139 if (server.session_lock.lock) { 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");
140 wlr_session_lock_v1_destroy(lock); 278 wlr_session_lock_v1_destroy(lock);
141 return; 279 return;
142 } 280 }
143 281
282 wl_list_init(&sway_lock->outputs);
283
144 sway_log(SWAY_DEBUG, "session locked"); 284 sway_log(SWAY_DEBUG, "session locked");
145 server.session_lock.locked = true;
146 server.session_lock.lock = lock;
147 285
148 struct sway_seat *seat; 286 struct sway_seat *seat;
149 wl_list_for_each(seat, &server.input->seats, link) { 287 wl_list_for_each(seat, &server.input->seats, link) {
150 seat_set_exclusive_client(seat, client); 288 seat_unfocus_unless_client(seat, client);
151 } 289 }
152 290
153 wl_signal_add(&lock->events.new_surface, &server.session_lock.lock_new_surface); 291 struct sway_output *output;
154 wl_signal_add(&lock->events.unlock, &server.session_lock.lock_unlock); 292 wl_list_for_each(output, &root->all_outputs, link) {
155 wl_signal_add(&lock->events.destroy, &server.session_lock.lock_destroy); 293 sway_session_lock_add_output(sway_lock, output);
294 }
156 295
157 wlr_session_lock_v1_send_locked(lock); 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);
158 302
159 // redraw everything 303 wlr_session_lock_v1_send_locked(lock);
160 for (int i = 0; i < root->outputs->length; ++i) { 304 server.session_lock.lock = sway_lock;
161 struct sway_output *output = root->outputs->items[i];
162 output_damage_whole(output);
163 }
164} 305}
165 306
166static void handle_session_lock_destroy(struct wl_listener *listener, void *data) { 307static void handle_session_lock_destroy(struct wl_listener *listener, void *data) {
167 assert(server.session_lock.lock == NULL); 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
168 wl_list_remove(&server.session_lock.new_lock.link); 313 wl_list_remove(&server.session_lock.new_lock.link);
169 wl_list_remove(&server.session_lock.manager_destroy.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;
170} 343}
171 344
172void sway_session_lock_init(void) { 345void sway_session_lock_init(void) {
173 server.session_lock.manager = wlr_session_lock_manager_v1_create(server.wl_display); 346 server.session_lock.manager = wlr_session_lock_manager_v1_create(server.wl_display);
174 347
175 server.session_lock.lock_new_surface.notify = handle_new_surface;
176 server.session_lock.lock_unlock.notify = handle_unlock;
177 server.session_lock.lock_destroy.notify = handle_abandon;
178 server.session_lock.new_lock.notify = handle_session_lock; 348 server.session_lock.new_lock.notify = handle_session_lock;
179 server.session_lock.manager_destroy.notify = handle_session_lock_destroy; 349 server.session_lock.manager_destroy.notify = handle_session_lock_destroy;
180 wl_signal_add(&server.session_lock.manager->events.new_lock, 350 wl_signal_add(&server.session_lock.manager->events.new_lock,
diff --git a/sway/main.c b/sway/main.c
index a0033c45..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>
@@ -49,44 +48,6 @@ void sig_handler(int signal) {
49 sway_terminate(EXIT_SUCCESS); 48 sway_terminate(EXIT_SUCCESS);
50} 49}
51 50
52void detect_proprietary(int allow_unsupported_gpu) {
53 FILE *f = fopen("/proc/modules", "r");
54 if (!f) {
55 return;
56 }
57 char *line = NULL;
58 size_t line_size = 0;
59 while (getline(&line, &line_size, f) != -1) {
60 if (strncmp(line, "nvidia ", 7) == 0) {
61 if (allow_unsupported_gpu) {
62 sway_log(SWAY_ERROR,
63 "!!! Proprietary Nvidia drivers are in use !!!");
64 } else {
65 sway_log(SWAY_ERROR,
66 "Proprietary Nvidia drivers are NOT supported. "
67 "Use Nouveau. To launch sway anyway, launch with "
68 "--unsupported-gpu and DO NOT report issues.");
69 exit(EXIT_FAILURE);
70 }
71 break;
72 }
73 if (strstr(line, "fglrx")) {
74 if (allow_unsupported_gpu) {
75 sway_log(SWAY_ERROR,
76 "!!! Proprietary AMD drivers are in use !!!");
77 } else {
78 sway_log(SWAY_ERROR, "Proprietary AMD drivers do NOT support "
79 "Wayland. Use radeon. To try anyway, launch sway with "
80 "--unsupported-gpu and DO NOT report issues.");
81 exit(EXIT_FAILURE);
82 }
83 break;
84 }
85 }
86 free(line);
87 fclose(f);
88}
89
90void run_as_ipc_client(char *command, char *socket_path) { 51void run_as_ipc_client(char *command, char *socket_path) {
91 int socketfd = ipc_open_socket(socket_path); 52 int socketfd = ipc_open_socket(socket_path);
92 uint32_t len = strlen(command); 53 uint32_t len = strlen(command);
@@ -150,27 +111,17 @@ static void log_kernel(void) {
150 pclose(f); 111 pclose(f);
151} 112}
152 113
153 114static bool detect_suid(void) {
154static bool drop_permissions(void) { 115 if (geteuid() != 0 && getegid() != 0) {
155 if (getuid() != geteuid() || getgid() != getegid()) { 116 return false;
156 sway_log(SWAY_ERROR, "!!! DEPRECATION WARNING: "
157 "SUID privilege drop will be removed in a future release, please migrate to seatd-launch");
158
159 // Set the gid and uid in the correct order.
160 if (setgid(getgid()) != 0) {
161 sway_log(SWAY_ERROR, "Unable to drop root group, refusing to start");
162 return false;
163 }
164 if (setuid(getuid()) != 0) {
165 sway_log(SWAY_ERROR, "Unable to drop root user, refusing to start");
166 return false;
167 }
168 } 117 }
169 if (setgid(0) != -1 || setuid(0) != -1) { 118
170 sway_log(SWAY_ERROR, "Unable to drop root (we shouldn't be able to " 119 if (getuid() == geteuid() && getgid() == getegid()) {
171 "restore it after setuid), refusing to start");
172 return false; 120 return false;
173 } 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.");
174 return true; 125 return true;
175} 126}
176 127
@@ -202,11 +153,7 @@ void restore_nofile_limit(void) {
202} 153}
203 154
204void enable_debug_flag(const char *flag) { 155void enable_debug_flag(const char *flag) {
205 if (strcmp(flag, "damage=highlight") == 0) { 156 if (strcmp(flag, "noatomic") == 0) {
206 debug.damage = DAMAGE_HIGHLIGHT;
207 } else if (strcmp(flag, "damage=rerender") == 0) {
208 debug.damage = DAMAGE_RERENDER;
209 } else if (strcmp(flag, "noatomic") == 0) {
210 debug.noatomic = true; 157 debug.noatomic = true;
211 } else if (strcmp(flag, "txn-wait") == 0) { 158 } else if (strcmp(flag, "txn-wait") == 0) {
212 debug.txn_wait = true; 159 debug.txn_wait = true;
@@ -214,8 +161,8 @@ void enable_debug_flag(const char *flag) {
214 debug.txn_timings = true; 161 debug.txn_timings = true;
215 } else if (strncmp(flag, "txn-timeout=", 12) == 0) { 162 } else if (strncmp(flag, "txn-timeout=", 12) == 0) {
216 server.txn_timeout_ms = atoi(&flag[12]); 163 server.txn_timeout_ms = atoi(&flag[12]);
217 } else if (strcmp(flag, "noscanout") == 0) { 164 } else if (strcmp(flag, "legacy-wl-drm") == 0) {
218 debug.noscanout = true; 165 debug.legacy_wl_drm = true;
219 } else { 166 } else {
220 sway_log(SWAY_ERROR, "Unknown debug flag: %s", flag); 167 sway_log(SWAY_ERROR, "Unknown debug flag: %s", flag);
221 } 168 }
@@ -265,7 +212,7 @@ static const char usage[] =
265 "\n"; 212 "\n";
266 213
267int main(int argc, char **argv) { 214int main(int argc, char **argv) {
268 static bool verbose = false, debug = false, validate = false, allow_unsupported_gpu = false; 215 static bool verbose = false, debug = false, validate = false;
269 216
270 char *config_path = NULL; 217 char *config_path = NULL;
271 218
@@ -304,7 +251,7 @@ int main(int argc, char **argv) {
304 case 'V': // verbose 251 case 'V': // verbose
305 verbose = true; 252 verbose = true;
306 break; 253 break;
307 case 'p': ; // --get-socketpath 254 case 'p': // --get-socketpath
308 if (getenv("SWAYSOCK")) { 255 if (getenv("SWAYSOCK")) {
309 printf("%s\n", getenv("SWAYSOCK")); 256 printf("%s\n", getenv("SWAYSOCK"));
310 exit(EXIT_SUCCESS); 257 exit(EXIT_SUCCESS);
@@ -319,6 +266,11 @@ int main(int argc, char **argv) {
319 } 266 }
320 } 267 }
321 268
269 // SUID operation is deprecated, so block it for now.
270 if (detect_suid()) {
271 exit(EXIT_FAILURE);
272 }
273
322 // 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
323 // clear error message (when not running as an IPC client). 275 // clear error message (when not running as an IPC client).
324 if (!getenv("XDG_RUNTIME_DIR") && optind == argc) { 276 if (!getenv("XDG_RUNTIME_DIR") && optind == argc) {
@@ -357,9 +309,6 @@ int main(int argc, char **argv) {
357 "`sway -d 2>sway.log`."); 309 "`sway -d 2>sway.log`.");
358 exit(EXIT_FAILURE); 310 exit(EXIT_FAILURE);
359 } 311 }
360 if (!drop_permissions()) {
361 exit(EXIT_FAILURE);
362 }
363 char *socket_path = getenv("SWAYSOCK"); 312 char *socket_path = getenv("SWAYSOCK");
364 if (!socket_path) { 313 if (!socket_path) {
365 sway_log(SWAY_ERROR, "Unable to retrieve socket path"); 314 sway_log(SWAY_ERROR, "Unable to retrieve socket path");
@@ -371,17 +320,6 @@ int main(int argc, char **argv) {
371 return 0; 320 return 0;
372 } 321 }
373 322
374 detect_proprietary(allow_unsupported_gpu);
375
376 if (!server_privileged_prepare(&server)) {
377 return 1;
378 }
379
380 if (!drop_permissions()) {
381 server_fini(&server);
382 exit(EXIT_FAILURE);
383 }
384
385 increase_nofile_limit(); 323 increase_nofile_limit();
386 324
387 // handle SIGTERM signals 325 // handle SIGTERM signals
@@ -393,12 +331,14 @@ int main(int argc, char **argv) {
393 331
394 sway_log(SWAY_INFO, "Starting sway version " SWAY_VERSION); 332 sway_log(SWAY_INFO, "Starting sway version " SWAY_VERSION);
395 333
396 root = root_create();
397
398 if (!server_init(&server)) { 334 if (!server_init(&server)) {
399 return 1; 335 return 1;
400 } 336 }
401 337
338 if (server.linux_dmabuf_v1) {
339 wlr_scene_set_linux_dmabuf_v1(root->root_scene, server.linux_dmabuf_v1);
340 }
341
402 if (validate) { 342 if (validate) {
403 bool valid = load_main_config(config_path, false, true); 343 bool valid = load_main_config(config_path, false, true);
404 free(config_path); 344 free(config_path);
diff --git a/sway/meson.build b/sway/meson.build
index 6762ef2d..47b51d0c 100644
--- a/sway/meson.build
+++ b/sway/meson.build
@@ -8,24 +8,23 @@ sway_sources = files(
8 'lock.c', 8 'lock.c',
9 'main.c', 9 'main.c',
10 'realtime.c', 10 'realtime.c',
11 'scene_descriptor.c',
11 'server.c', 12 'server.c',
13 'sway_text_node.c',
12 'swaynag.c', 14 'swaynag.c',
13 'xdg_activation_v1.c', 15 'xdg_activation_v1.c',
14 'xdg_decoration.c', 16 'xdg_decoration.c',
15 17
16 'desktop/desktop.c',
17 'desktop/idle_inhibit_v1.c', 18 'desktop/idle_inhibit_v1.c',
18 'desktop/layer_shell.c', 19 'desktop/layer_shell.c',
19 'desktop/output.c', 20 'desktop/output.c',
20 'desktop/render.c',
21 'desktop/surface.c',
22 'desktop/transaction.c', 21 'desktop/transaction.c',
23 'desktop/xdg_shell.c', 22 'desktop/xdg_shell.c',
23 'desktop/launcher.c',
24 24
25 'input/input-manager.c', 25 'input/input-manager.c',
26 'input/cursor.c', 26 'input/cursor.c',
27 'input/keyboard.c', 27 'input/keyboard.c',
28 'input/libinput.c',
29 'input/seat.c', 28 'input/seat.c',
30 'input/seatop_default.c', 29 'input/seatop_default.c',
31 'input/seatop_down.c', 30 'input/seatop_down.c',
@@ -67,6 +66,7 @@ sway_sources = files(
67 'commands/force_focus_wrapping.c', 66 'commands/force_focus_wrapping.c',
68 'commands/fullscreen.c', 67 'commands/fullscreen.c',
69 'commands/gaps.c', 68 'commands/gaps.c',
69 'commands/gesture.c',
70 'commands/hide_edge_borders.c', 70 'commands/hide_edge_borders.c',
71 'commands/inhibit_idle.c', 71 'commands/inhibit_idle.c',
72 'commands/kill.c', 72 'commands/kill.c',
@@ -85,6 +85,7 @@ sway_sources = files(
85 'commands/nop.c', 85 'commands/nop.c',
86 'commands/output.c', 86 'commands/output.c',
87 'commands/popup_during_fullscreen.c', 87 'commands/popup_during_fullscreen.c',
88 'commands/primary_selection.c',
88 'commands/reload.c', 89 'commands/reload.c',
89 'commands/rename.c', 90 'commands/rename.c',
90 'commands/resize.c', 91 'commands/resize.c',
@@ -156,6 +157,7 @@ sway_sources = files(
156 'commands/input/drag.c', 157 'commands/input/drag.c',
157 'commands/input/drag_lock.c', 158 'commands/input/drag_lock.c',
158 'commands/input/dwt.c', 159 'commands/input/dwt.c',
160 'commands/input/dwtp.c',
159 'commands/input/events.c', 161 'commands/input/events.c',
160 'commands/input/left_handed.c', 162 'commands/input/left_handed.c',
161 'commands/input/map_from_region.c', 163 'commands/input/map_from_region.c',
@@ -164,9 +166,11 @@ sway_sources = files(
164 'commands/input/middle_emulation.c', 166 'commands/input/middle_emulation.c',
165 'commands/input/natural_scroll.c', 167 'commands/input/natural_scroll.c',
166 'commands/input/pointer_accel.c', 168 'commands/input/pointer_accel.c',
169 'commands/input/rotation_angle.c',
167 'commands/input/repeat_delay.c', 170 'commands/input/repeat_delay.c',
168 'commands/input/repeat_rate.c', 171 'commands/input/repeat_rate.c',
169 'commands/input/scroll_button.c', 172 'commands/input/scroll_button.c',
173 'commands/input/scroll_button_lock.c',
170 'commands/input/scroll_factor.c', 174 'commands/input/scroll_factor.c',
171 'commands/input/scroll_method.c', 175 'commands/input/scroll_method.c',
172 'commands/input/tap.c', 176 'commands/input/tap.c',
@@ -190,12 +194,14 @@ sway_sources = files(
190 'commands/output/max_render_time.c', 194 'commands/output/max_render_time.c',
191 'commands/output/mode.c', 195 'commands/output/mode.c',
192 'commands/output/position.c', 196 'commands/output/position.c',
197 'commands/output/power.c',
193 'commands/output/render_bit_depth.c', 198 'commands/output/render_bit_depth.c',
194 'commands/output/scale.c', 199 'commands/output/scale.c',
195 'commands/output/scale_filter.c', 200 'commands/output/scale_filter.c',
196 'commands/output/subpixel.c', 201 'commands/output/subpixel.c',
197 'commands/output/toggle.c', 202 'commands/output/toggle.c',
198 'commands/output/transform.c', 203 'commands/output/transform.c',
204 'commands/output/unplug.c',
199 205
200 'tree/arrange.c', 206 'tree/arrange.c',
201 'tree/container.c', 207 'tree/container.c',
@@ -216,23 +222,26 @@ sway_deps = [
216 math, 222 math,
217 pango, 223 pango,
218 pcre2, 224 pcre2,
219 glesv2,
220 pixman, 225 pixman,
221 server_protos, 226 threads,
222 wayland_server, 227 wayland_server,
223 wlroots, 228 wlroots,
224 xkbcommon, 229 xkbcommon,
230 xcb,
225 xcb_icccm, 231 xcb_icccm,
226] 232]
227 233
228if have_xwayland 234if wlroots_features['xwayland']
229 sway_sources += 'desktop/xwayland.c' 235 sway_sources += 'desktop/xwayland.c'
230 sway_deps += xcb 236endif
237
238if wlroots_features['libinput_backend']
239 sway_sources += 'input/libinput.c'
231endif 240endif
232 241
233executable( 242executable(
234 'sway', 243 'sway',
235 sway_sources, 244 sway_sources + wl_protos_src,
236 include_directories: [sway_inc], 245 include_directories: [sway_inc],
237 dependencies: sway_deps, 246 dependencies: sway_deps,
238 link_with: [lib_sway_common], 247 link_with: [lib_sway_common],
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 627d80d6..edbc1a4b 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,23 +6,34 @@
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/session.h>
11#include <wlr/config.h> 9#include <wlr/config.h>
10#include <wlr/render/allocator.h>
12#include <wlr/render/wlr_renderer.h> 11#include <wlr/render/wlr_renderer.h>
13#include <wlr/types/wlr_compositor.h> 12#include <wlr/types/wlr_compositor.h>
13#include <wlr/types/wlr_content_type_v1.h>
14#include <wlr/types/wlr_cursor_shape_v1.h>
14#include <wlr/types/wlr_data_control_v1.h> 15#include <wlr/types/wlr_data_control_v1.h>
15#include <wlr/types/wlr_drm_lease_v1.h> 16#include <wlr/types/wlr_data_device.h>
16#include <wlr/types/wlr_drm.h> 17#include <wlr/types/wlr_drm.h>
17#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>
18#include <wlr/types/wlr_gamma_control_v1.h> 22#include <wlr/types/wlr_gamma_control_v1.h>
19#include <wlr/types/wlr_idle.h> 23#include <wlr/types/wlr_idle_notify_v1.h>
20#include <wlr/types/wlr_layer_shell_v1.h> 24#include <wlr/types/wlr_layer_shell_v1.h>
21#include <wlr/types/wlr_linux_dmabuf_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>
22#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>
23#include <wlr/types/wlr_primary_selection_v1.h> 30#include <wlr/types/wlr_primary_selection_v1.h>
24#include <wlr/types/wlr_relative_pointer_v1.h> 31#include <wlr/types/wlr_relative_pointer_v1.h>
25#include <wlr/types/wlr_screencopy_v1.h> 32#include <wlr/types/wlr_screencopy_v1.h>
33#include <wlr/types/wlr_security_context_v1.h>
26#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>
27#include <wlr/types/wlr_subcompositor.h> 37#include <wlr/types/wlr_subcompositor.h>
28#include <wlr/types/wlr_tablet_v2.h> 38#include <wlr/types/wlr_tablet_v2.h>
29#include <wlr/types/wlr_viewporter.h> 39#include <wlr/types/wlr_viewporter.h>
@@ -34,6 +44,7 @@
34#include <wlr/types/wlr_xdg_foreign_v1.h> 44#include <wlr/types/wlr_xdg_foreign_v1.h>
35#include <wlr/types/wlr_xdg_foreign_v2.h> 45#include <wlr/types/wlr_xdg_foreign_v2.h>
36#include <wlr/types/wlr_xdg_output_v1.h> 46#include <wlr/types/wlr_xdg_output_v1.h>
47#include <xf86drm.h>
37#include "config.h" 48#include "config.h"
38#include "list.h" 49#include "list.h"
39#include "log.h" 50#include "log.h"
@@ -42,24 +53,25 @@
42#include "sway/input/input-manager.h" 53#include "sway/input/input-manager.h"
43#include "sway/output.h" 54#include "sway/output.h"
44#include "sway/server.h" 55#include "sway/server.h"
56#include "sway/input/cursor.h"
45#include "sway/tree/root.h" 57#include "sway/tree/root.h"
46#if HAVE_XWAYLAND 58
59#if WLR_HAS_XWAYLAND
60#include <wlr/xwayland/shell.h>
47#include "sway/xwayland.h" 61#include "sway/xwayland.h"
48#endif 62#endif
49 63
50bool server_privileged_prepare(struct sway_server *server) { 64#if WLR_HAS_DRM_BACKEND
51 sway_log(SWAY_DEBUG, "Preparing Wayland server initialization"); 65#include <wlr/types/wlr_drm_lease_v1.h>
52 server->wl_display = wl_display_create(); 66#endif
53 server->wl_event_loop = wl_display_get_event_loop(server->wl_display);
54 server->backend = wlr_backend_autocreate(server->wl_display);
55 67
56 if (!server->backend) { 68#define SWAY_XDG_SHELL_VERSION 5
57 sway_log(SWAY_ERROR, "Unable to create backend"); 69#define SWAY_LAYER_SHELL_VERSION 4
58 return false; 70#define SWAY_FOREIGN_TOPLEVEL_LIST_VERSION 1
59 }
60 return true;
61}
62 71
72bool allow_unsupported_gpu = false;
73
74#if WLR_HAS_DRM_BACKEND
63static void handle_drm_lease_request(struct wl_listener *listener, void *data) { 75static void handle_drm_lease_request(struct wl_listener *listener, void *data) {
64 /* We only offer non-desktop outputs, but in the future we might want to do 76 /* We only offer non-desktop outputs, but in the future we might want to do
65 * more logic here. */ 77 * more logic here. */
@@ -71,11 +83,152 @@ static void handle_drm_lease_request(struct wl_listener *listener, void *data) {
71 wlr_drm_lease_request_v1_reject(req); 83 wlr_drm_lease_request_v1_reject(req);
72 } 84 }
73} 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 global == server.xdg_output_manager_v1->global;
118}
119
120static bool filter_global(const struct wl_client *client,
121 const struct wl_global *global, void *data) {
122#if WLR_HAS_XWAYLAND
123 struct wlr_xwayland *xwayland = server.xwayland.wlr_xwayland;
124 if (xwayland && global == xwayland->shell_v1->global) {
125 return xwayland->server != NULL && client == xwayland->server->client;
126 }
127#endif
128
129 // Restrict usage of privileged protocols to unsandboxed clients
130 // TODO: add a way for users to configure an allow-list
131 const struct wlr_security_context_v1_state *security_context =
132 wlr_security_context_manager_v1_lookup_client(
133 server.security_context_manager_v1, (struct wl_client *)client);
134 if (is_privileged(global)) {
135 return security_context == NULL;
136 }
137
138 return true;
139}
140
141static void detect_proprietary(struct wlr_backend *backend, void *data) {
142 int drm_fd = wlr_backend_get_drm_fd(backend);
143 if (drm_fd < 0) {
144 return;
145 }
146
147 drmVersion *version = drmGetVersion(drm_fd);
148 if (version == NULL) {
149 sway_log(SWAY_ERROR, "drmGetVersion() failed");
150 return;
151 }
152
153 bool is_unsupported = false;
154 if (strcmp(version->name, "nvidia-drm") == 0) {
155 is_unsupported = true;
156 sway_log(SWAY_ERROR, "!!! Proprietary Nvidia drivers are in use !!!");
157 if (!allow_unsupported_gpu) {
158 sway_log(SWAY_ERROR, "Use Nouveau instead");
159 }
160 }
161
162 if (strcmp(version->name, "evdi") == 0) {
163 is_unsupported = true;
164 sway_log(SWAY_ERROR, "!!! Proprietary DisplayLink drivers are in use !!!");
165 }
166
167 if (!allow_unsupported_gpu && is_unsupported) {
168 sway_log(SWAY_ERROR,
169 "Proprietary drivers are NOT supported. To launch sway anyway, "
170 "launch with --unsupported-gpu and DO NOT report issues.");
171 exit(EXIT_FAILURE);
172 }
173
174 drmFreeVersion(version);
175}
176
177static void handle_renderer_lost(struct wl_listener *listener, void *data) {
178 struct sway_server *server = wl_container_of(listener, server, renderer_lost);
179
180 sway_log(SWAY_INFO, "Re-creating renderer after GPU reset");
181
182 struct wlr_renderer *renderer = wlr_renderer_autocreate(server->backend);
183 if (renderer == NULL) {
184 sway_log(SWAY_ERROR, "Unable to create renderer");
185 return;
186 }
187
188 struct wlr_allocator *allocator =
189 wlr_allocator_autocreate(server->backend, renderer);
190 if (allocator == NULL) {
191 sway_log(SWAY_ERROR, "Unable to create allocator");
192 wlr_renderer_destroy(renderer);
193 return;
194 }
195
196 struct wlr_renderer *old_renderer = server->renderer;
197 struct wlr_allocator *old_allocator = server->allocator;
198 server->renderer = renderer;
199 server->allocator = allocator;
200
201 wl_list_remove(&server->renderer_lost.link);
202 wl_signal_add(&server->renderer->events.lost, &server->renderer_lost);
74 203
75#define SWAY_XDG_SHELL_VERSION 2 204 wlr_compositor_set_renderer(server->compositor, renderer);
205
206 for (int i = 0; i < root->outputs->length; ++i) {
207 struct sway_output *output = root->outputs->items[i];
208 wlr_output_init_render(output->wlr_output,
209 server->allocator, server->renderer);
210 }
211
212 wlr_allocator_destroy(old_allocator);
213 wlr_renderer_destroy(old_renderer);
214}
76 215
77bool server_init(struct sway_server *server) { 216bool server_init(struct sway_server *server) {
78 sway_log(SWAY_DEBUG, "Initializing Wayland server"); 217 sway_log(SWAY_DEBUG, "Initializing Wayland server");
218 server->wl_display = wl_display_create();
219 server->wl_event_loop = wl_display_get_event_loop(server->wl_display);
220
221 wl_display_set_global_filter(server->wl_display, filter_global, NULL);
222
223 root = root_create(server->wl_display);
224
225 server->backend = wlr_backend_autocreate(server->wl_event_loop, &server->session);
226 if (!server->backend) {
227 sway_log(SWAY_ERROR, "Unable to create backend");
228 return false;
229 }
230
231 wlr_multi_for_each_backend(server->backend, detect_proprietary, NULL);
79 232
80 server->renderer = wlr_renderer_autocreate(server->backend); 233 server->renderer = wlr_renderer_autocreate(server->backend);
81 if (!server->renderer) { 234 if (!server->renderer) {
@@ -83,12 +236,17 @@ bool server_init(struct sway_server *server) {
83 return false; 236 return false;
84 } 237 }
85 238
239 server->renderer_lost.notify = handle_renderer_lost;
240 wl_signal_add(&server->renderer->events.lost, &server->renderer_lost);
241
86 wlr_renderer_init_wl_shm(server->renderer, server->wl_display); 242 wlr_renderer_init_wl_shm(server->renderer, server->wl_display);
87 243
88 if (wlr_renderer_get_dmabuf_texture_formats(server->renderer) != NULL) { 244 if (wlr_renderer_get_texture_formats(server->renderer, WLR_BUFFER_CAP_DMABUF) != NULL) {
89 wlr_drm_create(server->wl_display, server->renderer); 245 server->linux_dmabuf_v1 = wlr_linux_dmabuf_v1_create_with_renderer(
90 server->linux_dmabuf_v1 = 246 server->wl_display, 4, server->renderer);
91 wlr_linux_dmabuf_v1_create(server->wl_display, server->renderer); 247 if (debug.legacy_wl_drm) {
248 wlr_drm_create(server->wl_display, server->renderer);
249 }
92 } 250 }
93 251
94 server->allocator = wlr_allocator_autocreate(server->backend, 252 server->allocator = wlr_allocator_autocreate(server->backend,
@@ -98,18 +256,19 @@ bool server_init(struct sway_server *server) {
98 return false; 256 return false;
99 } 257 }
100 258
101 server->compositor = wlr_compositor_create(server->wl_display, 259 server->compositor = wlr_compositor_create(server->wl_display, 6,
102 server->renderer); 260 server->renderer);
103 server->compositor_new_surface.notify = handle_compositor_new_surface;
104 wl_signal_add(&server->compositor->events.new_surface,
105 &server->compositor_new_surface);
106 261
107 wlr_subcompositor_create(server->wl_display); 262 wlr_subcompositor_create(server->wl_display);
108 263
109 server->data_device_manager = 264 server->data_device_manager =
110 wlr_data_device_manager_create(server->wl_display); 265 wlr_data_device_manager_create(server->wl_display);
111 266
112 wlr_gamma_control_manager_v1_create(server->wl_display); 267 server->gamma_control_manager_v1 =
268 wlr_gamma_control_manager_v1_create(server->wl_display);
269 server->gamma_control_set_gamma.notify = handle_gamma_control_set_gamma;
270 wl_signal_add(&server->gamma_control_manager_v1->events.set_gamma,
271 &server->gamma_control_set_gamma);
113 272
114 server->new_output.notify = handle_new_output; 273 server->new_output.notify = handle_new_output;
115 wl_signal_add(&server->backend->events.new_output, &server->new_output); 274 wl_signal_add(&server->backend->events.new_output, &server->new_output);
@@ -117,22 +276,23 @@ bool server_init(struct sway_server *server) {
117 wl_signal_add(&root->output_layout->events.change, 276 wl_signal_add(&root->output_layout->events.change,
118 &server->output_layout_change); 277 &server->output_layout_change);
119 278
120 wlr_xdg_output_manager_v1_create(server->wl_display, root->output_layout); 279 server->xdg_output_manager_v1 =
280 wlr_xdg_output_manager_v1_create(server->wl_display, root->output_layout);
121 281
122 server->idle = wlr_idle_create(server->wl_display); 282 server->idle_notifier_v1 = wlr_idle_notifier_v1_create(server->wl_display);
123 server->idle_inhibit_manager_v1 = 283 sway_idle_inhibit_manager_v1_init();
124 sway_idle_inhibit_manager_v1_create(server->wl_display, server->idle);
125 284
126 server->layer_shell = wlr_layer_shell_v1_create(server->wl_display); 285 server->layer_shell = wlr_layer_shell_v1_create(server->wl_display,
286 SWAY_LAYER_SHELL_VERSION);
127 wl_signal_add(&server->layer_shell->events.new_surface, 287 wl_signal_add(&server->layer_shell->events.new_surface,
128 &server->layer_shell_surface); 288 &server->layer_shell_surface);
129 server->layer_shell_surface.notify = handle_layer_shell_surface; 289 server->layer_shell_surface.notify = handle_layer_shell_surface;
130 290
131 server->xdg_shell = wlr_xdg_shell_create(server->wl_display, 291 server->xdg_shell = wlr_xdg_shell_create(server->wl_display,
132 SWAY_XDG_SHELL_VERSION); 292 SWAY_XDG_SHELL_VERSION);
133 wl_signal_add(&server->xdg_shell->events.new_surface, 293 wl_signal_add(&server->xdg_shell->events.new_toplevel,
134 &server->xdg_shell_surface); 294 &server->xdg_shell_toplevel);
135 server->xdg_shell_surface.notify = handle_xdg_shell_surface; 295 server->xdg_shell_toplevel.notify = handle_xdg_shell_toplevel;
136 296
137 server->tablet_v2 = wlr_tablet_v2_create(server->wl_display); 297 server->tablet_v2 = wlr_tablet_v2_create(server->wl_display);
138 298
@@ -163,8 +323,7 @@ bool server_init(struct sway_server *server) {
163 wl_signal_add(&server->pointer_constraints->events.new_constraint, 323 wl_signal_add(&server->pointer_constraints->events.new_constraint,
164 &server->pointer_constraint); 324 &server->pointer_constraint);
165 325
166 server->presentation = 326 wlr_presentation_create(server->wl_display, server->backend);
167 wlr_presentation_create(server->wl_display, server->backend);
168 327
169 server->output_manager_v1 = 328 server->output_manager_v1 =
170 wlr_output_manager_v1_create(server->wl_display); 329 wlr_output_manager_v1_create(server->wl_display);
@@ -183,11 +342,14 @@ bool server_init(struct sway_server *server) {
183 &server->output_power_manager_set_mode); 342 &server->output_power_manager_set_mode);
184 server->input_method = wlr_input_method_manager_v2_create(server->wl_display); 343 server->input_method = wlr_input_method_manager_v2_create(server->wl_display);
185 server->text_input = wlr_text_input_manager_v3_create(server->wl_display); 344 server->text_input = wlr_text_input_manager_v3_create(server->wl_display);
345 server->foreign_toplevel_list =
346 wlr_ext_foreign_toplevel_list_v1_create(server->wl_display, SWAY_FOREIGN_TOPLEVEL_LIST_VERSION);
186 server->foreign_toplevel_manager = 347 server->foreign_toplevel_manager =
187 wlr_foreign_toplevel_manager_v1_create(server->wl_display); 348 wlr_foreign_toplevel_manager_v1_create(server->wl_display);
188 349
189 sway_session_lock_init(); 350 sway_session_lock_init();
190 351
352#if WLR_HAS_DRM_BACKEND
191 server->drm_lease_manager= 353 server->drm_lease_manager=
192 wlr_drm_lease_v1_manager_create(server->wl_display, server->backend); 354 wlr_drm_lease_v1_manager_create(server->wl_display, server->backend);
193 if (server->drm_lease_manager) { 355 if (server->drm_lease_manager) {
@@ -198,12 +360,17 @@ bool server_init(struct sway_server *server) {
198 sway_log(SWAY_DEBUG, "Failed to create wlr_drm_lease_device_v1"); 360 sway_log(SWAY_DEBUG, "Failed to create wlr_drm_lease_device_v1");
199 sway_log(SWAY_INFO, "VR will not be available"); 361 sway_log(SWAY_INFO, "VR will not be available");
200 } 362 }
363#endif
201 364
202 wlr_export_dmabuf_manager_v1_create(server->wl_display); 365 server->export_dmabuf_manager_v1 = wlr_export_dmabuf_manager_v1_create(server->wl_display);
203 wlr_screencopy_manager_v1_create(server->wl_display); 366 server->screencopy_manager_v1 = wlr_screencopy_manager_v1_create(server->wl_display);
204 wlr_data_control_manager_v1_create(server->wl_display); 367 server->data_control_manager_v1 = wlr_data_control_manager_v1_create(server->wl_display);
205 wlr_primary_selection_v1_device_manager_create(server->wl_display); 368 server->security_context_manager_v1 = wlr_security_context_manager_v1_create(server->wl_display);
206 wlr_viewporter_create(server->wl_display); 369 wlr_viewporter_create(server->wl_display);
370 wlr_single_pixel_buffer_manager_v1_create(server->wl_display);
371 server->content_type_manager_v1 =
372 wlr_content_type_manager_v1_create(server->wl_display, 1);
373 wlr_fractional_scale_manager_v1_create(server->wl_display, 1);
207 374
208 struct wlr_xdg_foreign_registry *foreign_registry = 375 struct wlr_xdg_foreign_registry *foreign_registry =
209 wlr_xdg_foreign_registry_create(server->wl_display); 376 wlr_xdg_foreign_registry_create(server->wl_display);
@@ -215,6 +382,17 @@ bool server_init(struct sway_server *server) {
215 xdg_activation_v1_handle_request_activate; 382 xdg_activation_v1_handle_request_activate;
216 wl_signal_add(&server->xdg_activation_v1->events.request_activate, 383 wl_signal_add(&server->xdg_activation_v1->events.request_activate,
217 &server->xdg_activation_v1_request_activate); 384 &server->xdg_activation_v1_request_activate);
385 server->xdg_activation_v1_new_token.notify =
386 xdg_activation_v1_handle_new_token;
387 wl_signal_add(&server->xdg_activation_v1->events.new_token,
388 &server->xdg_activation_v1_new_token);
389
390 struct wlr_cursor_shape_manager_v1 *cursor_shape_manager =
391 wlr_cursor_shape_manager_v1_create(server->wl_display, 1);
392 server->request_set_cursor_shape.notify = handle_request_set_cursor_shape;
393 wl_signal_add(&cursor_shape_manager->events.request_set_shape, &server->request_set_cursor_shape);
394
395 wl_list_init(&server->pending_launcher_ctxs);
218 396
219 // Avoid using "wayland-0" as display socket 397 // Avoid using "wayland-0" as display socket
220 char name_candidate[16]; 398 char name_candidate[16];
@@ -232,7 +410,7 @@ bool server_init(struct sway_server *server) {
232 return false; 410 return false;
233 } 411 }
234 412
235 server->headless_backend = wlr_headless_backend_create(server->wl_display); 413 server->headless_backend = wlr_headless_backend_create(server->wl_event_loop);
236 if (!server->headless_backend) { 414 if (!server->headless_backend) {
237 sway_log(SWAY_ERROR, "Failed to create secondary headless backend"); 415 sway_log(SWAY_ERROR, "Failed to create secondary headless backend");
238 wlr_backend_destroy(server->backend); 416 wlr_backend_destroy(server->backend);
@@ -261,16 +439,17 @@ bool server_init(struct sway_server *server) {
261 439
262void server_fini(struct sway_server *server) { 440void server_fini(struct sway_server *server) {
263 // TODO: free sway-specific resources 441 // TODO: free sway-specific resources
264#if HAVE_XWAYLAND 442#if WLR_HAS_XWAYLAND
265 wlr_xwayland_destroy(server->xwayland.wlr_xwayland); 443 wlr_xwayland_destroy(server->xwayland.wlr_xwayland);
266#endif 444#endif
267 wl_display_destroy_clients(server->wl_display); 445 wl_display_destroy_clients(server->wl_display);
446 wlr_backend_destroy(server->backend);
268 wl_display_destroy(server->wl_display); 447 wl_display_destroy(server->wl_display);
269 list_free(server->dirty_nodes); 448 list_free(server->dirty_nodes);
270} 449}
271 450
272bool server_start(struct sway_server *server) { 451bool server_start(struct sway_server *server) {
273#if HAVE_XWAYLAND 452#if WLR_HAS_XWAYLAND
274 if (config->xwayland != XWAYLAND_MODE_DISABLED) { 453 if (config->xwayland != XWAYLAND_MODE_DISABLED) {
275 sway_log(SWAY_DEBUG, "Initializing Xwayland (lazy=%d)", 454 sway_log(SWAY_DEBUG, "Initializing Xwayland (lazy=%d)",
276 config->xwayland == XWAYLAND_MODE_LAZY); 455 config->xwayland == XWAYLAND_MODE_LAZY);
@@ -295,6 +474,10 @@ bool server_start(struct sway_server *server) {
295 } 474 }
296#endif 475#endif
297 476
477 if (config->primary_selection) {
478 wlr_primary_selection_v1_device_manager_create(server->wl_display);
479 }
480
298 sway_log(SWAY_INFO, "Starting backend on wayland display '%s'", 481 sway_log(SWAY_INFO, "Starting backend on wayland display '%s'",
299 server->socket); 482 server->socket);
300 if (!wlr_backend_start(server->backend)) { 483 if (!wlr_backend_start(server->backend)) {
diff --git a/sway/sway-input.5.scd b/sway/sway-input.5.scd
index 8b702b77..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,6 +246,8 @@ 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).
@@ -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
diff --git a/sway/sway-ipc.7.scd b/sway/sway-ipc.7.scd
index 5cdcb99b..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
@@ -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
@@ -372,6 +376,12 @@ node and will have the following properties:
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 4159a851..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
@@ -72,13 +72,11 @@ must be separated by one space. For example:
72 72
73*output* <name> scale <factor> 73*output* <name> scale <factor>
74 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
75 recommended, but fractional values are also supported. If a fractional 75 recommended, but fractional values are also supported. You may be better
76 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
77 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
78 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
79 setting an integer scale factor and adjusting the font size of your 79 requirements of the protocol.
80 applications to taste. HiDPI isn't supported with Xwayland clients (windows
81 will blur).
82 80
83*output* <name> scale_filter linear|nearest|smart 81*output* <name> scale_filter linear|nearest|smart
84 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
@@ -119,12 +117,20 @@ must be separated by one space. For example:
119 Enables or disables the specified output (all outputs are enabled by 117 Enables or disables the specified output (all outputs are enabled by
120 default). 118 default).
121 119
120 As opposed to the _power_ command, the output will lose its current
121 workspace and windows.
122
122*output* <name> toggle 123*output* <name> toggle
123 Toggle the specified output. 124 Toggle the specified output.
124 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
125*output* <name> dpms on|off|toggle 132*output* <name> dpms on|off|toggle
126 Enables or disables the specified output via DPMS. To turn an output off 133 Deprecated. Alias for _power_.
127 (ie. blank the screen but keep workspaces as-is), one can set DPMS to off.
128 134
129*output* <name> max_render_time off|<msec> 135*output* <name> max_render_time off|<msec>
130 Controls when sway composites the output, as a positive number of 136 Controls when sway composites the output, as a positive number of
diff --git a/sway/sway.5.scd b/sway/sway.5.scd
index 2780370f..9f823947 100644
--- a/sway/sway.5.scd
+++ b/sway/sway.5.scd
@@ -216,15 +216,14 @@ set|plus|minus|toggle <amount>
216 further details. 216 further details.
217 217
218*move* left|right|up|down [<px> px] 218*move* left|right|up|down [<px> px]
219 Moves the focused container in the direction specified. If the container, 219 Moves the focused container in the direction specified. The optional _px_
220 the optional _px_ argument specifies how many pixels to move the container. 220 argument specifies how many pixels to move the container. If unspecified,
221 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.
222 tiled containers.
223 222
224*move* [absolute] position <pos_x> [px|ppt] <pos_y> [px|ppt] 223*move* [absolute] position <pos_x> [px|ppt] <pos_y> [px|ppt]
225 Moves the focused container to the specified position in the workspace. 224 Moves the focused container to the specified position in the workspace.
226 The position can be specified in pixels or percentage points, omitting 225 The position can be specified in pixels or percentage points, omitting
227 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
228 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.
229 228
230*move* [absolute] position center 229*move* [absolute] position center
@@ -390,8 +389,8 @@ runtime.
390 for_window <criteria> move container to output <output> 389 for_window <criteria> move container to output <output>
391 390
392*bindsym* [--whole-window] [--border] [--exclude-titlebar] [--release] [--locked] \ 391*bindsym* [--whole-window] [--border] [--exclude-titlebar] [--release] [--locked] \
393[--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] \
394<command> 393[Group<1-4>+]<key combo> <command>
395 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
396 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).
397 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
@@ -401,6 +400,12 @@ runtime.
401 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,
402 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.
403 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
404 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
405 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
406 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
@@ -455,7 +460,8 @@ runtime.
455``` 460```
456 461
457 *bindcode* [--whole-window] [--border] [--exclude-titlebar] [--release] \ 462 *bindcode* [--whole-window] [--border] [--exclude-titlebar] [--release] \
458[--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>
459 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.
460 466
461*bindswitch* [--locked] [--no-warn] [--reload] <switch>:<state> <command> 467*bindswitch* [--locked] [--no-warn] [--reload] <switch>:<state> <command>
@@ -488,6 +494,62 @@ runtime.
488 bindswitch lid:toggle exec echo "Lid moved" 494 bindswitch lid:toggle exec echo "Lid moved"
489``` 495```
490 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
491*client.background* <color> 553*client.background* <color>
492 This command is ignored and is only present for i3 compatibility. 554 This command is ignored and is only present for i3 compatibility.
493 555
@@ -593,7 +655,8 @@ The default colors are:
593 655
594 656
595*default_border* normal|none|pixel [<n>] 657*default_border* normal|none|pixel [<n>]
596 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.
597 660
598*default_floating_border* normal|none|pixel [<n>] 661*default_floating_border* normal|none|pixel [<n>]
599 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
@@ -752,6 +815,10 @@ The default colors are:
752 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
753 fullscreen mode and the dialog will be rendered. 816 fullscreen mode and the dialog will be rendered.
754 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
755*set* $<name> <value> 822*set* $<name> <value>
756 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
757 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
@@ -792,6 +859,11 @@ The default colors are:
792*unbindswitch* <switch>:<state> 859*unbindswitch* <switch>:<state>
793 Removes a binding for when <switch> changes to <state>. 860 Removes a binding for when <switch> changes to <state>.
794 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
795*unbindsym* [--whole-window] [--border] [--exclude-titlebar] [--release] [--locked] \ 867*unbindsym* [--whole-window] [--border] [--exclude-titlebar] [--release] [--locked] \
796[--to-code] [--input-device=<device>] <key combo> 868[--to-code] [--input-device=<device>] <key combo>
797 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
@@ -897,6 +969,9 @@ properties in practice for your applications.
897 969
898The following attributes may be matched with: 970The following attributes may be matched with:
899 971
972*all*
973 Matches all windows.
974
900*app_id* 975*app_id*
901 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
902 \_\_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
@@ -905,7 +980,8 @@ The following attributes may be matched with:
905*class* 980*class*
906 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
907 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
908 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.
909 985
910*con_id* 986*con_id*
911 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
@@ -919,12 +995,14 @@ The following attributes may be matched with:
919 Matches floating windows. 995 Matches floating windows.
920 996
921*id* 997*id*
922 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.
923 1000
924*instance* 1001*instance*
925 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
926 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
927 of the currently focused window. 1004 of the currently focused window. instance is specific to X11 applications and
1005 requires XWayland.
928 1006
929*pid* 1007*pid*
930 Compare value against the window's process ID. Must be numeric. 1008 Compare value against the window's process ID. Must be numeric.
@@ -949,12 +1027,14 @@ The following attributes may be matched with:
949*window_role* 1027*window_role*
950 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
951 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
952 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.
953 1032
954*window_type* 1033*window_type*
955 Compare against the window type (\_NET_WM_WINDOW_TYPE). Possible values 1034 Compare against the window type (\_NET_WM_WINDOW_TYPE). Possible values
956 are normal, dialog, utility, toolbar, splash, menu, dropdown_menu, 1035 are normal, dialog, utility, toolbar, splash, menu, dropdown_menu,
957 popup_menu, tooltip and notification. 1036 popup_menu, tooltip and notification. window_type is specific to X11
1037 applications and requires XWayland.
958 1038
959*workspace* 1039*workspace*
960 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 4a0a6d30..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>
@@ -145,22 +144,16 @@ void swaynag_log(const char *swaynag_command, struct swaynag_instance *swaynag,
145 144
146 va_list args; 145 va_list args;
147 va_start(args, fmt); 146 va_start(args, fmt);
148 size_t length = vsnprintf(NULL, 0, fmt, args) + 1; 147 char *str = vformat_str(fmt, args);
149 va_end(args); 148 va_end(args);
150 149 if (!str) {
151 char *temp = malloc(length + 1);
152 if (!temp) {
153 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.");
154 return; 151 return;
155 } 152 }
156 153
157 va_start(args, fmt); 154 write(swaynag->fd[1], str, strlen(str));
158 vsnprintf(temp, length, fmt, args);
159 va_end(args);
160
161 write(swaynag->fd[1], temp, length);
162 155
163 free(temp); 156 free(str);
164} 157}
165 158
166void 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 9c1a11e5..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 }
diff --git a/sway/tree/container.c b/sway/tree/container.c
index 09766ce5..80ef34fe 100644
--- a/sway/tree/container.c
+++ b/sway/tree/container.c
@@ -1,25 +1,20 @@
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 <sys/stat.h>
9#include <wayland-server-core.h> 5#include <wayland-server-core.h>
6#include <wlr/types/wlr_foreign_toplevel_management_v1.h>
10#include <wlr/types/wlr_linux_dmabuf_v1.h> 7#include <wlr/types/wlr_linux_dmabuf_v1.h>
11#include <wlr/types/wlr_output_layout.h> 8#include <wlr/types/wlr_output_layout.h>
12#include <wlr/types/wlr_subcompositor.h> 9#include <wlr/types/wlr_subcompositor.h>
13#include <wlr/render/drm_format_set.h>
14#include "linux-dmabuf-unstable-v1-protocol.h" 10#include "linux-dmabuf-unstable-v1-protocol.h"
15#include "cairo_util.h"
16#include "pango.h"
17#include "sway/config.h" 11#include "sway/config.h"
18#include "sway/desktop.h"
19#include "sway/desktop/transaction.h" 12#include "sway/desktop/transaction.h"
20#include "sway/input/input-manager.h" 13#include "sway/input/input-manager.h"
21#include "sway/input/seat.h" 14#include "sway/input/seat.h"
22#include "sway/ipc-server.h" 15#include "sway/ipc-server.h"
16#include "sway/scene_descriptor.h"
17#include "sway/sway_text_node.h"
23#include "sway/output.h" 18#include "sway/output.h"
24#include "sway/server.h" 19#include "sway/server.h"
25#include "sway/tree/arrange.h" 20#include "sway/tree/arrange.h"
@@ -30,6 +25,53 @@
30#include "log.h" 25#include "log.h"
31#include "stringop.h" 26#include "stringop.h"
32 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
33struct sway_container *container_create(struct sway_view *view) { 75struct sway_container *container_create(struct sway_view *view) {
34 struct sway_container *c = calloc(1, sizeof(struct sway_container)); 76 struct sway_container *c = calloc(1, sizeof(struct sway_container));
35 if (!c) { 77 if (!c) {
@@ -37,23 +79,415 @@ struct sway_container *container_create(struct sway_view *view) {
37 return NULL; 79 return NULL;
38 } 80 }
39 node_init(&c->node, N_CONTAINER, c); 81 node_init(&c->node, N_CONTAINER, c);
40 c->pending.layout = L_NONE; 82
41 c->view = view; 83 // Container tree structure
42 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 }
43 151
44 if (!view) { 152 if (!view) {
45 c->pending.children = create_list(); 153 c->pending.children = create_list();
46 c->current.children = create_list(); 154 c->current.children = create_list();
47 } 155 }
156
157 c->pending.layout = L_NONE;
158 c->view = view;
159 c->alpha = 1.0f;
48 c->marks = create_list(); 160 c->marks = create_list();
49 c->outputs = create_list();
50 161
51 wl_signal_init(&c->events.destroy); 162 wl_signal_init(&c->events.destroy);
52 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);
53 166
54 return c; 167 return c;
55} 168}
56 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
57void container_destroy(struct sway_container *con) { 491void container_destroy(struct sway_container *con) {
58 if (!sway_assert(con->node.destroying, 492 if (!sway_assert(con->node.destroying,
59 "Tried to free container which wasn't marked as destroying")) { 493 "Tried to free container which wasn't marked as destroying")) {
@@ -65,29 +499,21 @@ void container_destroy(struct sway_container *con) {
65 } 499 }
66 free(con->title); 500 free(con->title);
67 free(con->formatted_title); 501 free(con->formatted_title);
68 wlr_texture_destroy(con->title_focused);
69 wlr_texture_destroy(con->title_focused_inactive);
70 wlr_texture_destroy(con->title_unfocused);
71 wlr_texture_destroy(con->title_urgent);
72 wlr_texture_destroy(con->title_focused_tab_title);
73 list_free(con->pending.children); 502 list_free(con->pending.children);
74 list_free(con->current.children); 503 list_free(con->current.children);
75 list_free(con->outputs);
76 504
77 list_free_items_and_destroy(con->marks); 505 list_free_items_and_destroy(con->marks);
78 wlr_texture_destroy(con->marks_focused);
79 wlr_texture_destroy(con->marks_focused_inactive);
80 wlr_texture_destroy(con->marks_unfocused);
81 wlr_texture_destroy(con->marks_urgent);
82 wlr_texture_destroy(con->marks_focused_tab_title);
83 506
84 if (con->view && con->view->container == con) { 507 if (con->view && con->view->container == con) {
85 con->view->container = NULL; 508 con->view->container = NULL;
509 wlr_scene_node_destroy(&con->output_handler->node);
86 if (con->view->destroying) { 510 if (con->view->destroying) {
87 view_destroy(con->view); 511 view_destroy(con->view);
88 } 512 }
89 } 513 }
90 514
515 scene_node_disown_children(con->content_tree);
516 wlr_scene_node_destroy(&con->scene_tree->node);
91 free(con); 517 free(con);
92} 518}
93 519
@@ -104,7 +530,7 @@ void container_begin_destroy(struct sway_container *con) {
104 container_fullscreen_disable(con); 530 container_fullscreen_disable(con);
105 } 531 }
106 532
107 wl_signal_emit(&con->node.events.destroy, &con->node); 533 wl_signal_emit_mutable(&con->node.events.destroy, &con->node);
108 534
109 container_end_mouse_operation(con); 535 container_end_mouse_operation(con);
110 536
@@ -174,265 +600,6 @@ struct sway_container *container_find_child(struct sway_container *container,
174 return NULL; 600 return NULL;
175} 601}
176 602
177static struct sway_container *surface_at_view(struct sway_container *con, double lx, double ly,
178 struct wlr_surface **surface, double *sx, double *sy) {
179 if (!sway_assert(con->view, "Expected a view")) {
180 return NULL;
181 }
182 struct sway_view *view = con->view;
183 double view_sx = lx - con->surface_x + view->geometry.x;
184 double view_sy = ly - con->surface_y + view->geometry.y;
185
186 double _sx, _sy;
187 struct wlr_surface *_surface = NULL;
188 switch (view->type) {
189#if HAVE_XWAYLAND
190 case SWAY_VIEW_XWAYLAND:
191 _surface = wlr_surface_surface_at(view->surface,
192 view_sx, view_sy, &_sx, &_sy);
193 break;
194#endif
195 case SWAY_VIEW_XDG_SHELL:
196 _surface = wlr_xdg_surface_surface_at(
197 view->wlr_xdg_toplevel->base,
198 view_sx, view_sy, &_sx, &_sy);
199 break;
200 }
201 if (_surface) {
202 *sx = _sx;
203 *sy = _sy;
204 *surface = _surface;
205 return con;
206 }
207 return NULL;
208}
209
210/**
211 * container_at for a container with layout L_TABBED.
212 */
213static struct sway_container *container_at_tabbed(struct sway_node *parent,
214 double lx, double ly,
215 struct wlr_surface **surface, double *sx, double *sy) {
216 struct wlr_box box;
217 node_get_box(parent, &box);
218 if (lx < box.x || lx > box.x + box.width ||
219 ly < box.y || ly > box.y + box.height) {
220 return NULL;
221 }
222 struct sway_seat *seat = input_manager_current_seat();
223 list_t *children = node_get_children(parent);
224 if (!children->length) {
225 return NULL;
226 }
227
228 // Tab titles
229 int title_height = container_titlebar_height();
230 if (ly < box.y + title_height) {
231 int tab_width = box.width / children->length;
232 int child_index = (lx - box.x) / tab_width;
233 if (child_index >= children->length) {
234 child_index = children->length - 1;
235 }
236 struct sway_container *child = children->items[child_index];
237 return child;
238 }
239
240 // Surfaces
241 struct sway_node *current = seat_get_active_tiling_child(seat, parent);
242 return current ? tiling_container_at(current, lx, ly, surface, sx, sy) : NULL;
243}
244
245/**
246 * container_at for a container with layout L_STACKED.
247 */
248static struct sway_container *container_at_stacked(struct sway_node *parent,
249 double lx, double ly,
250 struct wlr_surface **surface, double *sx, double *sy) {
251 struct wlr_box box;
252 node_get_box(parent, &box);
253 if (lx < box.x || lx > box.x + box.width ||
254 ly < box.y || ly > box.y + box.height) {
255 return NULL;
256 }
257 struct sway_seat *seat = input_manager_current_seat();
258 list_t *children = node_get_children(parent);
259
260 // Title bars
261 int title_height = container_titlebar_height();
262 if (title_height > 0) {
263 int child_index = (ly - box.y) / title_height;
264 if (child_index < children->length) {
265 struct sway_container *child = children->items[child_index];
266 return child;
267 }
268 }
269
270 // Surfaces
271 struct sway_node *current = seat_get_active_tiling_child(seat, parent);
272 return current ? tiling_container_at(current, lx, ly, surface, sx, sy) : NULL;
273}
274
275/**
276 * container_at for a container with layout L_HORIZ or L_VERT.
277 */
278static struct sway_container *container_at_linear(struct sway_node *parent,
279 double lx, double ly,
280 struct wlr_surface **surface, double *sx, double *sy) {
281 list_t *children = node_get_children(parent);
282 for (int i = 0; i < children->length; ++i) {
283 struct sway_container *child = children->items[i];
284 struct sway_container *container =
285 tiling_container_at(&child->node, lx, ly, surface, sx, sy);
286 if (container) {
287 return container;
288 }
289 }
290 return NULL;
291}
292
293static struct sway_container *floating_container_at(double lx, double ly,
294 struct wlr_surface **surface, double *sx, double *sy) {
295 // For outputs with floating containers that overhang the output bounds,
296 // those at the end of the output list appear on top of floating
297 // containers from other outputs, so iterate the list in reverse.
298 for (int i = root->outputs->length - 1; i >= 0; --i) {
299 struct sway_output *output = root->outputs->items[i];
300 for (int j = 0; j < output->workspaces->length; ++j) {
301 struct sway_workspace *ws = output->workspaces->items[j];
302 if (!workspace_is_visible(ws)) {
303 continue;
304 }
305 // Items at the end of the list are on top, so iterate the list in
306 // reverse.
307 for (int k = ws->floating->length - 1; k >= 0; --k) {
308 struct sway_container *floater = ws->floating->items[k];
309 struct sway_container *container =
310 tiling_container_at(&floater->node, lx, ly, surface, sx, sy);
311 if (container) {
312 return container;
313 }
314 }
315 }
316 }
317 return NULL;
318}
319
320static struct sway_container *view_container_content_at(struct sway_node *parent,
321 double lx, double ly,
322 struct wlr_surface **surface, double *sx, double *sy) {
323 if (!sway_assert(node_is_view(parent), "Expected a view")) {
324 return NULL;
325 }
326
327 struct sway_container *container = parent->sway_container;
328 struct wlr_box box = {
329 .x = container->pending.content_x,
330 .y = container->pending.content_y,
331 .width = container->pending.content_width,
332 .height = container->pending.content_height,
333 };
334
335 if (wlr_box_contains_point(&box, lx, ly)) {
336 surface_at_view(parent->sway_container, lx, ly, surface, sx, sy);
337 return container;
338 }
339
340 return NULL;
341}
342
343static struct sway_container *view_container_at(struct sway_node *parent,
344 double lx, double ly,
345 struct wlr_surface **surface, double *sx, double *sy) {
346 if (!sway_assert(node_is_view(parent), "Expected a view")) {
347 return NULL;
348 }
349
350 struct sway_container *container = parent->sway_container;
351 struct wlr_box box = {
352 .x = container->pending.x,
353 .y = container->pending.y,
354 .width = container->pending.width,
355 .height = container->pending.height,
356 };
357
358 if (wlr_box_contains_point(&box, lx, ly)) {
359 surface_at_view(parent->sway_container, lx, ly, surface, sx, sy);
360 return container;
361 }
362
363 return NULL;
364}
365
366struct sway_container *tiling_container_at(struct sway_node *parent,
367 double lx, double ly,
368 struct wlr_surface **surface, double *sx, double *sy) {
369 if (node_is_view(parent)) {
370 return view_container_at(parent, lx, ly, surface, sx, sy);
371 }
372 if (!node_get_children(parent)) {
373 return NULL;
374 }
375 switch (node_get_layout(parent)) {
376 case L_HORIZ:
377 case L_VERT:
378 return container_at_linear(parent, lx, ly, surface, sx, sy);
379 case L_TABBED:
380 return container_at_tabbed(parent, lx, ly, surface, sx, sy);
381 case L_STACKED:
382 return container_at_stacked(parent, lx, ly, surface, sx, sy);
383 case L_NONE:
384 return NULL;
385 }
386 return NULL;
387}
388
389static bool surface_is_popup(struct wlr_surface *surface) {
390 while (!wlr_surface_is_xdg_surface(surface)) {
391 if (!wlr_surface_is_subsurface(surface)) {
392 return false;
393 }
394 struct wlr_subsurface *subsurface =
395 wlr_subsurface_from_wlr_surface(surface);
396 surface = subsurface->parent;
397 }
398 struct wlr_xdg_surface *xdg_surface =
399 wlr_xdg_surface_from_wlr_surface(surface);
400 return xdg_surface->role == WLR_XDG_SURFACE_ROLE_POPUP;
401}
402
403struct sway_container *container_at(struct sway_workspace *workspace,
404 double lx, double ly,
405 struct wlr_surface **surface, double *sx, double *sy) {
406 struct sway_container *c;
407
408 struct sway_seat *seat = input_manager_current_seat();
409 struct sway_container *focus = seat_get_focused_container(seat);
410 bool is_floating = focus && container_is_floating_or_child(focus);
411 // Focused view's popups
412 if (focus && focus->view) {
413 c = surface_at_view(focus, lx, ly, surface, sx, sy);
414 if (c && surface_is_popup(*surface)) {
415 return c;
416 }
417 *surface = NULL;
418 }
419 // Floating
420 if ((c = floating_container_at(lx, ly, surface ,sx ,sy))) {
421 return c;
422 }
423 // Tiling (focused)
424 if (focus && focus->view && !is_floating) {
425 if ((c = view_container_content_at(&focus->node, lx, ly, surface, sx, sy))) {
426 return c;
427 }
428 }
429 // Tiling (non-focused)
430 if ((c = tiling_container_at(&workspace->node, lx, ly, surface, sx, sy))) {
431 return c;
432 }
433 return NULL;
434}
435
436void container_for_each_child(struct sway_container *container, 603void container_for_each_child(struct sway_container *container,
437 void (*f)(struct sway_container *container, void *data), 604 void (*f)(struct sway_container *container, void *data),
438 void *data) { 605 void *data) {
@@ -478,127 +645,6 @@ bool container_has_ancestor(struct sway_container *descendant,
478 return false; 645 return false;
479} 646}
480 647
481void container_damage_whole(struct sway_container *container) {
482 for (int i = 0; i < root->outputs->length; ++i) {
483 struct sway_output *output = root->outputs->items[i];
484 output_damage_whole_container(output, container);
485 }
486}
487
488/**
489 * Return the output which will be used for scale purposes.
490 * This is the most recently entered output.
491 */
492struct sway_output *container_get_effective_output(struct sway_container *con) {
493 if (con->outputs->length == 0) {
494 return NULL;
495 }
496 return con->outputs->items[con->outputs->length - 1];
497}
498
499static void render_titlebar_text_texture(struct sway_output *output,
500 struct sway_container *con, struct wlr_texture **texture,
501 struct border_colors *class, bool pango_markup, char *text) {
502 double scale = output->wlr_output->scale;
503 int width = 0;
504 int height = config->font_height * scale;
505 int baseline;
506
507 // We must use a non-nil cairo_t for cairo_set_font_options to work.
508 // Therefore, we cannot use cairo_create(NULL).
509 cairo_surface_t *dummy_surface = cairo_image_surface_create(
510 CAIRO_FORMAT_ARGB32, 0, 0);
511 cairo_t *c = cairo_create(dummy_surface);
512 cairo_set_antialias(c, CAIRO_ANTIALIAS_BEST);
513 cairo_font_options_t *fo = cairo_font_options_create();
514 cairo_font_options_set_hint_style(fo, CAIRO_HINT_STYLE_FULL);
515 if (output->wlr_output->subpixel == WL_OUTPUT_SUBPIXEL_NONE) {
516 cairo_font_options_set_antialias(fo, CAIRO_ANTIALIAS_GRAY);
517 } else {
518 cairo_font_options_set_antialias(fo, CAIRO_ANTIALIAS_SUBPIXEL);
519 cairo_font_options_set_subpixel_order(fo,
520 to_cairo_subpixel_order(output->wlr_output->subpixel));
521 }
522 cairo_set_font_options(c, fo);
523 get_text_size(c, config->font, &width, NULL, &baseline, scale,
524 config->pango_markup, "%s", text);
525 cairo_surface_destroy(dummy_surface);
526 cairo_destroy(c);
527
528 if (width == 0 || height == 0) {
529 return;
530 }
531
532 if (height > config->font_height * scale) {
533 height = config->font_height * scale;
534 }
535
536 cairo_surface_t *surface = cairo_image_surface_create(
537 CAIRO_FORMAT_ARGB32, width, height);
538 cairo_status_t status = cairo_surface_status(surface);
539 if (status != CAIRO_STATUS_SUCCESS) {
540 sway_log(SWAY_ERROR, "cairo_image_surface_create failed: %s",
541 cairo_status_to_string(status));
542 return;
543 }
544
545 cairo_t *cairo = cairo_create(surface);
546 cairo_set_antialias(cairo, CAIRO_ANTIALIAS_BEST);
547 cairo_set_font_options(cairo, fo);
548 cairo_font_options_destroy(fo);
549 cairo_set_source_rgba(cairo, class->background[0], class->background[1],
550 class->background[2], class->background[3]);
551 cairo_paint(cairo);
552 PangoContext *pango = pango_cairo_create_context(cairo);
553 cairo_set_source_rgba(cairo, class->text[0], class->text[1],
554 class->text[2], class->text[3]);
555 cairo_move_to(cairo, 0, config->font_baseline * scale - baseline);
556
557 render_text(cairo, config->font, scale, pango_markup, "%s", text);
558
559 cairo_surface_flush(surface);
560 unsigned char *data = cairo_image_surface_get_data(surface);
561 int stride = cairo_image_surface_get_stride(surface);
562 struct wlr_renderer *renderer = output->wlr_output->renderer;
563 *texture = wlr_texture_from_pixels(
564 renderer, DRM_FORMAT_ARGB8888, stride, width, height, data);
565 cairo_surface_destroy(surface);
566 g_object_unref(pango);
567 cairo_destroy(cairo);
568}
569
570static void update_title_texture(struct sway_container *con,
571 struct wlr_texture **texture, struct border_colors *class) {
572 struct sway_output *output = container_get_effective_output(con);
573 if (!output) {
574 return;
575 }
576 if (*texture) {
577 wlr_texture_destroy(*texture);
578 *texture = NULL;
579 }
580 if (!con->formatted_title) {
581 return;
582 }
583
584 render_titlebar_text_texture(output, con, texture, class,
585 config->pango_markup, con->formatted_title);
586}
587
588void container_update_title_textures(struct sway_container *container) {
589 update_title_texture(container, &container->title_focused,
590 &config->border_colors.focused);
591 update_title_texture(container, &container->title_focused_inactive,
592 &config->border_colors.focused_inactive);
593 update_title_texture(container, &container->title_unfocused,
594 &config->border_colors.unfocused);
595 update_title_texture(container, &container->title_urgent,
596 &config->border_colors.urgent);
597 update_title_texture(container, &container->title_focused_tab_title,
598 &config->border_colors.focused_tab_title);
599 container_damage_whole(container);
600}
601
602/** 648/**
603 * Calculate and return the length of the tree representation. 649 * Calculate and return the length of the tree representation.
604 * An example tree representation is: V[Terminal, Firefox] 650 * An example tree representation is: V[Terminal, Firefox]
@@ -664,7 +710,13 @@ void container_update_representation(struct sway_container *con) {
664 } 710 }
665 container_build_representation(con->pending.layout, con->pending.children, 711 container_build_representation(con->pending.layout, con->pending.children,
666 con->formatted_title); 712 con->formatted_title);
667 container_update_title_textures(con); 713
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 }
668 } 720 }
669 if (con->pending.parent) { 721 if (con->pending.parent) {
670 container_update_representation(con->pending.parent); 722 container_update_representation(con->pending.parent);
@@ -716,6 +768,21 @@ void floating_calculate_constraints(int *min_width, int *max_width,
716 768
717} 769}
718 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
719static void floating_natural_resize(struct sway_container *con) { 786static void floating_natural_resize(struct sway_container *con) {
720 int min_width, max_width, min_height, max_height; 787 int min_width, max_width, min_height, max_height;
721 floating_calculate_constraints(&min_width, &max_width, 788 floating_calculate_constraints(&min_width, &max_width,
@@ -787,11 +854,11 @@ void container_floating_set_default_size(struct sway_container *con) {
787 int min_width, max_width, min_height, max_height; 854 int min_width, max_width, min_height, max_height;
788 floating_calculate_constraints(&min_width, &max_width, 855 floating_calculate_constraints(&min_width, &max_width,
789 &min_height, &max_height); 856 &min_height, &max_height);
790 struct wlr_box *box = calloc(1, sizeof(struct wlr_box)); 857 struct wlr_box box;
791 workspace_get_box(con->pending.workspace, box); 858 workspace_get_box(con->pending.workspace, &box);
792 859
793 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));
794 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));
795 if (!con->view) { 862 if (!con->view) {
796 con->pending.width = width; 863 con->pending.width = width;
797 con->pending.height = height; 864 con->pending.height = height;
@@ -800,8 +867,6 @@ void container_floating_set_default_size(struct sway_container *con) {
800 con->pending.content_height = height; 867 con->pending.content_height = height;
801 container_set_geometry_from_content(con); 868 container_set_geometry_from_content(con);
802 } 869 }
803
804 free(box);
805} 870}
806 871
807 872
@@ -937,17 +1002,6 @@ bool container_is_floating(struct sway_container *container) {
937 return false; 1002 return false;
938} 1003}
939 1004
940bool container_is_current_floating(struct sway_container *container) {
941 if (!container->current.parent && container->current.workspace &&
942 list_find(container->current.workspace->floating, container) != -1) {
943 return true;
944 }
945 if (container->scratchpad) {
946 return true;
947 }
948 return false;
949}
950
951void container_get_box(struct sway_container *container, struct wlr_box *box) { 1005void container_get_box(struct sway_container *container, struct wlr_box *box) {
952 box->x = container->pending.x; 1006 box->x = container->pending.x;
953 box->y = container->pending.y; 1007 box->y = container->pending.y;
@@ -1031,6 +1085,13 @@ void container_floating_move_to(struct sway_container *con,
1031 workspace_add_floating(new_workspace, con); 1085 workspace_add_floating(new_workspace, con);
1032 arrange_workspace(old_workspace); 1086 arrange_workspace(old_workspace);
1033 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 }
1034 workspace_detect_urgent(old_workspace); 1095 workspace_detect_urgent(old_workspace);
1035 workspace_detect_urgent(new_workspace); 1096 workspace_detect_urgent(new_workspace);
1036 } 1097 }
@@ -1062,16 +1123,6 @@ void container_end_mouse_operation(struct sway_container *container) {
1062 } 1123 }
1063} 1124}
1064 1125
1065static bool devid_from_fd(int fd, dev_t *devid) {
1066 struct stat stat;
1067 if (fstat(fd, &stat) != 0) {
1068 sway_log_errno(SWAY_ERROR, "fstat failed");
1069 return false;
1070 }
1071 *devid = stat.st_rdev;
1072 return true;
1073}
1074
1075static void set_fullscreen(struct sway_container *con, bool enable) { 1126static void set_fullscreen(struct sway_container *con, bool enable) {
1076 if (!con->view) { 1127 if (!con->view) {
1077 return; 1128 return;
@@ -1083,75 +1134,6 @@ static void set_fullscreen(struct sway_container *con, bool enable) {
1083 con->view->foreign_toplevel, enable); 1134 con->view->foreign_toplevel, enable);
1084 } 1135 }
1085 } 1136 }
1086
1087 if (!server.linux_dmabuf_v1 || !con->view->surface) {
1088 return;
1089 }
1090 if (!enable) {
1091 wlr_linux_dmabuf_v1_set_surface_feedback(server.linux_dmabuf_v1,
1092 con->view->surface, NULL);
1093 return;
1094 }
1095
1096 if (!con->pending.workspace || !con->pending.workspace->output) {
1097 return;
1098 }
1099
1100 struct sway_output *output = con->pending.workspace->output;
1101 struct wlr_output *wlr_output = output->wlr_output;
1102
1103 // TODO: add wlroots helpers for all of this stuff
1104
1105 const struct wlr_drm_format_set *renderer_formats =
1106 wlr_renderer_get_dmabuf_texture_formats(server.renderer);
1107 assert(renderer_formats);
1108
1109 int renderer_drm_fd = wlr_renderer_get_drm_fd(server.renderer);
1110 int backend_drm_fd = wlr_backend_get_drm_fd(wlr_output->backend);
1111 if (renderer_drm_fd < 0 || backend_drm_fd < 0) {
1112 return;
1113 }
1114
1115 dev_t render_dev, scanout_dev;
1116 if (!devid_from_fd(renderer_drm_fd, &render_dev) ||
1117 !devid_from_fd(backend_drm_fd, &scanout_dev)) {
1118 return;
1119 }
1120
1121 const struct wlr_drm_format_set *output_formats =
1122 wlr_output_get_primary_formats(output->wlr_output,
1123 WLR_BUFFER_CAP_DMABUF);
1124 if (!output_formats) {
1125 return;
1126 }
1127
1128 struct wlr_drm_format_set scanout_formats = {0};
1129 if (!wlr_drm_format_set_intersect(&scanout_formats,
1130 output_formats, renderer_formats)) {
1131 return;
1132 }
1133
1134 struct wlr_linux_dmabuf_feedback_v1_tranche tranches[] = {
1135 {
1136 .target_device = scanout_dev,
1137 .flags = ZWP_LINUX_DMABUF_FEEDBACK_V1_TRANCHE_FLAGS_SCANOUT,
1138 .formats = &scanout_formats,
1139 },
1140 {
1141 .target_device = render_dev,
1142 .formats = renderer_formats,
1143 },
1144 };
1145
1146 const struct wlr_linux_dmabuf_feedback_v1 feedback = {
1147 .main_device = render_dev,
1148 .tranches = tranches,
1149 .tranches_len = sizeof(tranches) / sizeof(tranches[0]),
1150 };
1151 wlr_linux_dmabuf_v1_set_surface_feedback(server.linux_dmabuf_v1,
1152 con->view->surface, &feedback);
1153
1154 wlr_drm_format_set_finish(&scanout_formats);
1155} 1137}
1156 1138
1157static void container_fullscreen_workspace(struct sway_container *con) { 1139static void container_fullscreen_workspace(struct sway_container *con) {
@@ -1321,72 +1303,6 @@ bool container_is_fullscreen_or_child(struct sway_container *container) {
1321 return false; 1303 return false;
1322} 1304}
1323 1305
1324static void surface_send_enter_iterator(struct wlr_surface *surface,
1325 int x, int y, void *data) {
1326 struct wlr_output *wlr_output = data;
1327 wlr_surface_send_enter(surface, wlr_output);
1328}
1329
1330static void surface_send_leave_iterator(struct wlr_surface *surface,
1331 int x, int y, void *data) {
1332 struct wlr_output *wlr_output = data;
1333 wlr_surface_send_leave(surface, wlr_output);
1334}
1335
1336void container_discover_outputs(struct sway_container *con) {
1337 struct wlr_box con_box = {
1338 .x = con->current.x,
1339 .y = con->current.y,
1340 .width = con->current.width,
1341 .height = con->current.height,
1342 };
1343 struct sway_output *old_output = container_get_effective_output(con);
1344
1345 for (int i = 0; i < root->outputs->length; ++i) {
1346 struct sway_output *output = root->outputs->items[i];
1347 struct wlr_box output_box;
1348 output_get_box(output, &output_box);
1349 struct wlr_box intersection;
1350 bool intersects =
1351 wlr_box_intersection(&intersection, &con_box, &output_box);
1352 int index = list_find(con->outputs, output);
1353
1354 if (intersects && index == -1) {
1355 // Send enter
1356 sway_log(SWAY_DEBUG, "Container %p entered output %p", con, output);
1357 if (con->view) {
1358 view_for_each_surface(con->view,
1359 surface_send_enter_iterator, output->wlr_output);
1360 if (con->view->foreign_toplevel) {
1361 wlr_foreign_toplevel_handle_v1_output_enter(
1362 con->view->foreign_toplevel, output->wlr_output);
1363 }
1364 }
1365 list_add(con->outputs, output);
1366 } else if (!intersects && index != -1) {
1367 // Send leave
1368 sway_log(SWAY_DEBUG, "Container %p left output %p", con, output);
1369 if (con->view) {
1370 view_for_each_surface(con->view,
1371 surface_send_leave_iterator, output->wlr_output);
1372 if (con->view->foreign_toplevel) {
1373 wlr_foreign_toplevel_handle_v1_output_leave(
1374 con->view->foreign_toplevel, output->wlr_output);
1375 }
1376 }
1377 list_del(con->outputs, index);
1378 }
1379 }
1380 struct sway_output *new_output = container_get_effective_output(con);
1381 double old_scale = old_output && old_output->enabled ?
1382 old_output->wlr_output->scale : -1;
1383 double new_scale = new_output ? new_output->wlr_output->scale : -1;
1384 if (old_scale != new_scale) {
1385 container_update_title_textures(con);
1386 container_update_marks_textures(con);
1387 }
1388}
1389
1390enum sway_container_layout container_parent_layout(struct sway_container *con) { 1306enum sway_container_layout container_parent_layout(struct sway_container *con) {
1391 if (con->pending.parent) { 1307 if (con->pending.parent) {
1392 return con->pending.parent->pending.layout; 1308 return con->pending.parent->pending.layout;
@@ -1397,19 +1313,11 @@ enum sway_container_layout container_parent_layout(struct sway_container *con) {
1397 return L_NONE; 1313 return L_NONE;
1398} 1314}
1399 1315
1400enum sway_container_layout container_current_parent_layout(
1401 struct sway_container *con) {
1402 if (con->current.parent) {
1403 return con->current.parent->current.layout;
1404 }
1405 return con->current.workspace->current.layout;
1406}
1407
1408list_t *container_get_siblings(struct sway_container *container) { 1316list_t *container_get_siblings(struct sway_container *container) {
1409 if (container->pending.parent) { 1317 if (container->pending.parent) {
1410 return container->pending.parent->pending.children; 1318 return container->pending.parent->pending.children;
1411 } 1319 }
1412 if (container_is_scratchpad_hidden(container)) { 1320 if (!container->pending.workspace) {
1413 return NULL; 1321 return NULL;
1414 } 1322 }
1415 if (list_find(container->pending.workspace->tiling, container) != -1) { 1323 if (list_find(container->pending.workspace->tiling, container) != -1) {
@@ -1422,13 +1330,6 @@ int container_sibling_index(struct sway_container *child) {
1422 return list_find(container_get_siblings(child), child); 1330 return list_find(container_get_siblings(child), child);
1423} 1331}
1424 1332
1425list_t *container_get_current_siblings(struct sway_container *container) {
1426 if (container->current.parent) {
1427 return container->current.parent->current.children;
1428 }
1429 return container->current.workspace->current.tiling;
1430}
1431
1432void container_handle_fullscreen_reparent(struct sway_container *con) { 1333void container_handle_fullscreen_reparent(struct sway_container *con) {
1433 if (con->pending.fullscreen_mode != FULLSCREEN_WORKSPACE || !con->pending.workspace || 1334 if (con->pending.fullscreen_mode != FULLSCREEN_WORKSPACE || !con->pending.workspace ||
1434 con->pending.workspace->fullscreen == con) { 1335 con->pending.workspace->fullscreen == con) {
@@ -1643,7 +1544,7 @@ bool container_find_and_unmark(char *mark) {
1643 if (strcmp(con_mark, mark) == 0) { 1544 if (strcmp(con_mark, mark) == 0) {
1644 free(con_mark); 1545 free(con_mark);
1645 list_del(con->marks, i); 1546 list_del(con->marks, i);
1646 container_update_marks_textures(con); 1547 container_update_marks(con);
1647 ipc_event_window(con, "mark"); 1548 ipc_event_window(con, "mark");
1648 return true; 1549 return true;
1649 } 1550 }
@@ -1674,70 +1575,15 @@ void container_add_mark(struct sway_container *con, char *mark) {
1674 ipc_event_window(con, "mark"); 1575 ipc_event_window(con, "mark");
1675} 1576}
1676 1577
1677static void update_marks_texture(struct sway_container *con,
1678 struct wlr_texture **texture, struct border_colors *class) {
1679 struct sway_output *output = container_get_effective_output(con);
1680 if (!output) {
1681 return;
1682 }
1683 if (*texture) {
1684 wlr_texture_destroy(*texture);
1685 *texture = NULL;
1686 }
1687 if (!con->marks->length) {
1688 return;
1689 }
1690
1691 size_t len = 0;
1692 for (int i = 0; i < con->marks->length; ++i) {
1693 char *mark = con->marks->items[i];
1694 if (mark[0] != '_') {
1695 len += strlen(mark) + 2;
1696 }
1697 }
1698 char *buffer = calloc(len + 1, 1);
1699 char *part = malloc(len + 1);
1700
1701 if (!sway_assert(buffer && part, "Unable to allocate memory")) {
1702 free(buffer);
1703 return;
1704 }
1705
1706 for (int i = 0; i < con->marks->length; ++i) {
1707 char *mark = con->marks->items[i];
1708 if (mark[0] != '_') {
1709 snprintf(part, len + 1, "[%s]", mark);
1710 strcat(buffer, part);
1711 }
1712 }
1713 free(part);
1714
1715 render_titlebar_text_texture(output, con, texture, class, false, buffer);
1716
1717 free(buffer);
1718}
1719
1720void container_update_marks_textures(struct sway_container *con) {
1721 if (!config->show_marks) {
1722 return;
1723 }
1724 update_marks_texture(con, &con->marks_focused,
1725 &config->border_colors.focused);
1726 update_marks_texture(con, &con->marks_focused_inactive,
1727 &config->border_colors.focused_inactive);
1728 update_marks_texture(con, &con->marks_unfocused,
1729 &config->border_colors.unfocused);
1730 update_marks_texture(con, &con->marks_urgent,
1731 &config->border_colors.urgent);
1732 update_marks_texture(con, &con->marks_focused_tab_title,
1733 &config->border_colors.focused_tab_title);
1734 container_damage_whole(con);
1735}
1736
1737void container_raise_floating(struct sway_container *con) { 1578void container_raise_floating(struct sway_container *con) {
1738 // 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.
1739 struct sway_container *floater = container_toplevel_ancestor(con); 1580 struct sway_container *floater = container_toplevel_ancestor(con);
1740 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
1741 list_move_to_end(floater->pending.workspace->floating, floater); 1587 list_move_to_end(floater->pending.workspace->floating, floater);
1742 node_set_dirty(&floater->pending.workspace->node); 1588 node_set_dirty(&floater->pending.workspace->node);
1743 } 1589 }
@@ -1821,3 +1667,177 @@ int container_squash(struct sway_container *con) {
1821 } 1667 }
1822 return change; 1668 return change;
1823} 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 52826c91..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
@@ -87,9 +86,51 @@ 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;
@@ -102,11 +143,6 @@ struct sway_output *output_create(struct wlr_output *wlr_output) {
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();
@@ -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.disable, 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,7 +313,7 @@ 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);
289 wl_signal_emit(&output->node.events.destroy, &output->node); 316 wl_signal_emit_mutable(&output->node.events.destroy, &output->node);
290 317
291 output->node.destroying = true; 318 output->node.destroying = true;
292 node_set_dirty(&output->node); 319 node_set_dirty(&output->node);
@@ -390,6 +417,33 @@ void output_get_box(struct sway_output *output, struct wlr_box *box) {
390 box->height = output->height; 417 box->height = output->height;
391} 418}
392 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
393enum sway_container_layout output_get_default_layout( 447enum sway_container_layout output_get_default_layout(
394 struct sway_output *output) { 448 struct sway_output *output) {
395 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 8508e9eb..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 snprintf(file_name, sizeof(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) {
@@ -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 7d9e038d..1c1c8ee8 100644
--- a/sway/tree/view.c
+++ b/sway/tree/view.c
@@ -1,29 +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>
9#include <wlr/types/wlr_subcompositor.h> 11#include <wlr/types/wlr_subcompositor.h>
10#include <wlr/types/wlr_xdg_decoration_v1.h> 12#include <wlr/types/wlr_xdg_decoration_v1.h>
11#include "config.h" 13#if WLR_HAS_XWAYLAND
12#if HAVE_XWAYLAND
13#include <wlr/xwayland.h> 14#include <wlr/xwayland.h>
14#endif 15#endif
15#include "list.h" 16#include "list.h"
16#include "log.h" 17#include "log.h"
17#include "sway/criteria.h" 18#include "sway/criteria.h"
18#include "sway/commands.h" 19#include "sway/commands.h"
19#include "sway/desktop.h"
20#include "sway/desktop/transaction.h" 20#include "sway/desktop/transaction.h"
21#include "sway/desktop/idle_inhibit_v1.h" 21#include "sway/desktop/idle_inhibit_v1.h"
22#include "sway/desktop/launcher.h"
22#include "sway/input/cursor.h" 23#include "sway/input/cursor.h"
23#include "sway/ipc-server.h" 24#include "sway/ipc-server.h"
24#include "sway/output.h" 25#include "sway/output.h"
25#include "sway/input/seat.h" 26#include "sway/input/seat.h"
27#include "sway/scene_descriptor.h"
26#include "sway/server.h" 28#include "sway/server.h"
29#include "sway/sway_text_node.h"
27#include "sway/tree/arrange.h" 30#include "sway/tree/arrange.h"
28#include "sway/tree/container.h" 31#include "sway/tree/container.h"
29#include "sway/tree/view.h" 32#include "sway/tree/view.h"
@@ -33,15 +36,29 @@
33#include "pango.h" 36#include "pango.h"
34#include "stringop.h" 37#include "stringop.h"
35 38
36void view_init(struct sway_view *view, enum sway_view_type type, 39bool view_init(struct sway_view *view, enum sway_view_type type,
37 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
38 view->type = type; 55 view->type = type;
39 view->impl = impl; 56 view->impl = impl;
40 view->executed_criteria = create_list(); 57 view->executed_criteria = create_list();
41 wl_list_init(&view->saved_buffers);
42 view->allow_request_urgent = true; 58 view->allow_request_urgent = true;
43 view->shortcuts_inhibit = SHORTCUTS_INHIBIT_DEFAULT; 59 view->shortcuts_inhibit = SHORTCUTS_INHIBIT_DEFAULT;
44 wl_signal_init(&view->events.unmap); 60 wl_signal_init(&view->events.unmap);
61 return true;
45} 62}
46 63
47void view_destroy(struct sway_view *view) { 64void view_destroy(struct sway_view *view) {
@@ -58,11 +75,10 @@ void view_destroy(struct sway_view *view) {
58 return; 75 return;
59 } 76 }
60 wl_list_remove(&view->events.unmap.listener_list); 77 wl_list_remove(&view->events.unmap.listener_list);
61 if (!wl_list_empty(&view->saved_buffers)) {
62 view_remove_saved_buffer(view);
63 }
64 list_free(view->executed_criteria); 78 list_free(view->executed_criteria);
65 79
80 view_assign_ctx(view, NULL);
81 wlr_scene_node_destroy(&view->scene_tree->node);
66 free(view->title_format); 82 free(view->title_format);
67 83
68 if (view->impl->destroy) { 84 if (view->impl->destroy) {
@@ -110,7 +126,7 @@ const char *view_get_instance(struct sway_view *view) {
110 } 126 }
111 return NULL; 127 return NULL;
112} 128}
113#if HAVE_XWAYLAND 129#if WLR_HAS_XWAYLAND
114uint32_t view_get_x11_window_id(struct sway_view *view) { 130uint32_t view_get_x11_window_id(struct sway_view *view) {
115 if (view->impl->get_int_prop) { 131 if (view->impl->get_int_prop) {
116 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);
@@ -143,7 +159,7 @@ const char *view_get_shell(struct sway_view *view) {
143 switch(view->type) { 159 switch(view->type) {
144 case SWAY_VIEW_XDG_SHELL: 160 case SWAY_VIEW_XDG_SHELL:
145 return "xdg_shell"; 161 return "xdg_shell";
146#if HAVE_XWAYLAND 162#if WLR_HAS_XWAYLAND
147 case SWAY_VIEW_XWAYLAND: 163 case SWAY_VIEW_XWAYLAND:
148 return "xwayland"; 164 return "xwayland";
149#endif 165#endif
@@ -363,17 +379,17 @@ void view_set_activated(struct sway_view *view, bool activated) {
363 } 379 }
364} 380}
365 381
366void view_request_activate(struct sway_view *view) { 382void view_request_activate(struct sway_view *view, struct sway_seat *seat) {
367 struct sway_workspace *ws = view->container->pending.workspace; 383 struct sway_workspace *ws = view->container->pending.workspace;
368 if (!ws) { // hidden scratchpad container 384 if (!seat) {
369 return; 385 seat = input_manager_current_seat();
370 } 386 }
371 struct sway_seat *seat = input_manager_current_seat();
372 387
373 switch (config->focus_on_window_activation) { 388 switch (config->focus_on_window_activation) {
374 case FOWA_SMART: 389 case FOWA_SMART:
375 if (workspace_is_visible(ws)) { 390 if (ws && workspace_is_visible(ws)) {
376 seat_set_focus_container(seat, view->container); 391 seat_set_focus_container(seat, view->container);
392 container_raise_floating(view->container);
377 } else { 393 } else {
378 view_set_urgent(view, true); 394 view_set_urgent(view, true);
379 } 395 }
@@ -382,11 +398,23 @@ void view_request_activate(struct sway_view *view) {
382 view_set_urgent(view, true); 398 view_set_urgent(view, true);
383 break; 399 break;
384 case FOWA_FOCUS: 400 case FOWA_FOCUS:
385 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 }
386 break; 407 break;
387 case FOWA_NONE: 408 case FOWA_NONE:
388 break; 409 break;
389 } 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 }
390} 418}
391 419
392void view_set_csd_from_server(struct sway_view *view, bool enabled) { 420void view_set_csd_from_server(struct sway_view *view, bool enabled) {
@@ -433,52 +461,6 @@ void view_close_popups(struct sway_view *view) {
433 } 461 }
434} 462}
435 463
436void view_damage_from(struct sway_view *view) {
437 for (int i = 0; i < root->outputs->length; ++i) {
438 struct sway_output *output = root->outputs->items[i];
439 output_damage_from_view(output, view);
440 }
441}
442
443void view_for_each_surface(struct sway_view *view,
444 wlr_surface_iterator_func_t iterator, void *user_data) {
445 if (!view->surface) {
446 return;
447 }
448 if (view->impl->for_each_surface) {
449 view->impl->for_each_surface(view, iterator, user_data);
450 } else {
451 wlr_surface_for_each_surface(view->surface, iterator, user_data);
452 }
453}
454
455void view_for_each_popup_surface(struct sway_view *view,
456 wlr_surface_iterator_func_t iterator, void *user_data) {
457 if (!view->surface) {
458 return;
459 }
460 if (view->impl->for_each_popup_surface) {
461 view->impl->for_each_popup_surface(view, iterator, user_data);
462 }
463}
464
465static void view_subsurface_create(struct sway_view *view,
466 struct wlr_subsurface *subsurface);
467
468static void view_init_subsurfaces(struct sway_view *view,
469 struct wlr_surface *surface);
470
471static void view_child_init_subsurfaces(struct sway_view_child *view_child,
472 struct wlr_surface *surface);
473
474static void view_handle_surface_new_subsurface(struct wl_listener *listener,
475 void *data) {
476 struct sway_view *view =
477 wl_container_of(listener, view, surface_new_subsurface);
478 struct wlr_subsurface *subsurface = data;
479 view_subsurface_create(view, subsurface);
480}
481
482static bool view_has_executed_criteria(struct sway_view *view, 464static bool view_has_executed_criteria(struct sway_view *view,
483 struct criteria *criteria) { 465 struct criteria *criteria) {
484 for (int i = 0; i < view->executed_criteria->length; ++i) { 466 for (int i = 0; i < view->executed_criteria->length; ++i) {
@@ -517,10 +499,10 @@ void view_execute_criteria(struct sway_view *view) {
517static void view_populate_pid(struct sway_view *view) { 499static void view_populate_pid(struct sway_view *view) {
518 pid_t pid; 500 pid_t pid;
519 switch (view->type) { 501 switch (view->type) {
520#if HAVE_XWAYLAND 502#if WLR_HAS_XWAYLAND
521 case SWAY_VIEW_XWAYLAND:; 503 case SWAY_VIEW_XWAYLAND:;
522 struct wlr_xwayland_surface *surf = 504 struct wlr_xwayland_surface *surf =
523 wlr_xwayland_surface_from_wlr_surface(view->surface); 505 wlr_xwayland_surface_try_from_wlr_surface(view->surface);
524 pid = surf->pid; 506 pid = surf->pid;
525 break; 507 break;
526#endif 508#endif
@@ -533,6 +515,20 @@ static void view_populate_pid(struct sway_view *view) {
533 view->pid = pid; 515 view->pid = pid;
534} 516}
535 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
536static struct sway_workspace *select_workspace(struct sway_view *view) { 532static struct sway_workspace *select_workspace(struct sway_view *view) {
537 struct sway_seat *seat = input_manager_current_seat(); 533 struct sway_seat *seat = input_manager_current_seat();
538 534
@@ -568,13 +564,14 @@ static struct sway_workspace *select_workspace(struct sway_view *view) {
568 } 564 }
569 list_free(criterias); 565 list_free(criterias);
570 if (ws) { 566 if (ws) {
571 root_remove_workspace_pid(view->pid); 567 view_assign_ctx(view, NULL);
572 return ws; 568 return ws;
573 } 569 }
574 570
575 // Check if there's a PID mapping 571 // Check if there's a PID mapping
576 ws = root_workspace_for_pid(view->pid); 572 ws = view->ctx ? launcher_ctx_get_workspace(view->ctx) : NULL;
577 if (ws) { 573 if (ws) {
574 view_assign_ctx(view, NULL);
578 return ws; 575 return ws;
579 } 576 }
580 577
@@ -592,6 +589,14 @@ static struct sway_workspace *select_workspace(struct sway_view *view) {
592 return NULL; 589 return NULL;
593} 590}
594 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
595static bool should_focus(struct sway_view *view) { 600static bool should_focus(struct sway_view *view) {
596 struct sway_seat *seat = input_manager_current_seat(); 601 struct sway_seat *seat = input_manager_current_seat();
597 struct sway_container *prev_con = seat_get_focused_container(seat); 602 struct sway_container *prev_con = seat_get_focused_container(seat);
@@ -717,6 +722,13 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface,
717 view_populate_pid(view); 722 view_populate_pid(view);
718 view->container = container_create(view); 723 view->container = container_create(view);
719 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
720 // 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
721 // to honor that request. Otherwise, fallback to assigns, pid mappings, 733 // to honor that request. Otherwise, fallback to assigns, pid mappings,
722 // focused workspace, etc 734 // focused workspace, etc
@@ -754,6 +766,13 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface,
754 } 766 }
755 } 767 }
756 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);
775
757 view->foreign_toplevel = 776 view->foreign_toplevel =
758 wlr_foreign_toplevel_handle_v1_create(server.foreign_toplevel_manager); 777 wlr_foreign_toplevel_handle_v1_create(server.foreign_toplevel_manager);
759 view->foreign_activate_request.notify = handle_foreign_activate_request; 778 view->foreign_activate_request.notify = handle_foreign_activate_request;
@@ -777,11 +796,6 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface,
777 } 796 }
778 ipc_event_window(view->container, "new"); 797 ipc_event_window(view->container, "new");
779 798
780 view_init_subsurfaces(view, wlr_surface);
781 wl_signal_add(&wlr_surface->events.new_subsurface,
782 &view->surface_new_subsurface);
783 view->surface_new_subsurface.notify = view_handle_surface_new_subsurface;
784
785 if (decoration) { 799 if (decoration) {
786 view_update_csd_from_client(view, decoration); 800 view_update_csd_from_client(view, decoration);
787 } 801 }
@@ -824,10 +838,9 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface,
824 838
825 bool set_focus = should_focus(view); 839 bool set_focus = should_focus(view);
826 840
827#if HAVE_XWAYLAND 841#if WLR_HAS_XWAYLAND
828 if (wlr_surface_is_xwayland_surface(wlr_surface)) { 842 struct wlr_xwayland_surface *xsurface;
829 struct wlr_xwayland_surface *xsurface = 843 if ((xsurface = wlr_xwayland_surface_try_from_wlr_surface(wlr_surface))) {
830 wlr_xwayland_surface_from_wlr_surface(wlr_surface);
831 set_focus &= wlr_xwayland_icccm_input_model(xsurface) != 844 set_focus &= wlr_xwayland_icccm_input_model(xsurface) !=
832 WLR_ICCCM_INPUT_MODEL_NONE; 845 WLR_ICCCM_INPUT_MODEL_NONE;
833 } 846 }
@@ -837,6 +850,10 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface,
837 input_manager_set_focus(&view->container->node); 850 input_manager_set_focus(&view->container->node);
838 } 851 }
839 852
853 if (view->ext_foreign_toplevel) {
854 update_ext_foreign_toplevel(view);
855 }
856
840 const char *app_id; 857 const char *app_id;
841 const char *class; 858 const char *class;
842 if ((app_id = view_get_app_id(view)) != NULL) { 859 if ((app_id = view_get_app_id(view)) != NULL) {
@@ -847,15 +864,20 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface,
847} 864}
848 865
849void view_unmap(struct sway_view *view) { 866void view_unmap(struct sway_view *view) {
850 wl_signal_emit(&view->events.unmap, view); 867 wl_signal_emit_mutable(&view->events.unmap, view);
851 868
852 wl_list_remove(&view->surface_new_subsurface.link); 869 view->executed_criteria->length = 0;
853 870
854 if (view->urgent_timer) { 871 if (view->urgent_timer) {
855 wl_event_source_remove(view->urgent_timer); 872 wl_event_source_remove(view->urgent_timer);
856 view->urgent_timer = NULL; 873 view->urgent_timer = NULL;
857 } 874 }
858 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
859 if (view->foreign_toplevel) { 881 if (view->foreign_toplevel) {
860 wlr_foreign_toplevel_handle_v1_destroy(view->foreign_toplevel); 882 wlr_foreign_toplevel_handle_v1_destroy(view->foreign_toplevel);
861 view->foreign_toplevel = NULL; 883 view->foreign_toplevel = NULL;
@@ -902,291 +924,54 @@ void view_update_size(struct sway_view *view) {
902 container_set_geometry_from_content(con); 924 container_set_geometry_from_content(con);
903} 925}
904 926
905void view_center_surface(struct sway_view *view) { 927void view_center_and_clip_surface(struct sway_view *view) {
906 struct sway_container *con = view->container; 928 struct sway_container *con = view->container;
907 // We always center the current coordinates rather than the next, as the
908 // geometry immediately affects the currently active rendering.
909 con->surface_x = fmax(con->current.content_x, con->current.content_x +
910 (con->current.content_width - view->geometry.width) / 2);
911 con->surface_y = fmax(con->current.content_y, con->current.content_y +
912 (con->current.content_height - view->geometry.height) / 2);
913}
914 929
915static const struct sway_view_child_impl subsurface_impl; 930 bool clip_to_geometry = true;
916 931
917static void subsurface_get_view_coords(struct sway_view_child *child, 932 if (container_is_floating(con)) {
918 int *sx, int *sy) { 933 // We always center the current coordinates rather than the next, as the
919 struct wlr_surface *surface = child->surface; 934 // geometry immediately affects the currently active rendering.
920 if (child->parent && child->parent->impl && 935 int x = (int) fmax(0, (con->current.content_width - view->geometry.width) / 2);
921 child->parent->impl->get_view_coords) { 936 int y = (int) fmax(0, (con->current.content_height - view->geometry.height) / 2);
922 child->parent->impl->get_view_coords(child->parent, sx, sy); 937 clip_to_geometry = !view->using_csd;
923 } else {
924 *sx = *sy = 0;
925 }
926 struct wlr_subsurface *subsurface =
927 wlr_subsurface_from_wlr_surface(surface);
928 *sx += subsurface->current.x;
929 *sy += subsurface->current.y;
930}
931 938
932static void subsurface_destroy(struct sway_view_child *child) { 939 wlr_scene_node_set_position(&view->content_tree->node, x, y);
933 if (!sway_assert(child->impl == &subsurface_impl,
934 "Expected a subsurface")) {
935 return;
936 }
937 struct sway_subsurface *subsurface = (struct sway_subsurface *)child;
938 wl_list_remove(&subsurface->destroy.link);
939 free(subsurface);
940}
941
942static const struct sway_view_child_impl subsurface_impl = {
943 .get_view_coords = subsurface_get_view_coords,
944 .destroy = subsurface_destroy,
945};
946
947static void subsurface_handle_destroy(struct wl_listener *listener,
948 void *data) {
949 struct sway_subsurface *subsurface =
950 wl_container_of(listener, subsurface, destroy);
951 struct sway_view_child *child = &subsurface->child;
952 view_child_destroy(child);
953}
954
955static void view_child_damage(struct sway_view_child *child, bool whole);
956
957static void view_subsurface_create(struct sway_view *view,
958 struct wlr_subsurface *wlr_subsurface) {
959 struct sway_subsurface *subsurface =
960 calloc(1, sizeof(struct sway_subsurface));
961 if (subsurface == NULL) {
962 sway_log(SWAY_ERROR, "Allocation failed");
963 return;
964 }
965 view_child_init(&subsurface->child, &subsurface_impl, view,
966 wlr_subsurface->surface);
967
968 wl_signal_add(&wlr_subsurface->events.destroy, &subsurface->destroy);
969 subsurface->destroy.notify = subsurface_handle_destroy;
970
971 subsurface->child.mapped = true;
972
973 view_child_damage(&subsurface->child, true);
974}
975
976static void view_child_subsurface_create(struct sway_view_child *child,
977 struct wlr_subsurface *wlr_subsurface) {
978 struct sway_subsurface *subsurface =
979 calloc(1, sizeof(struct sway_subsurface));
980 if (subsurface == NULL) {
981 sway_log(SWAY_ERROR, "Allocation failed");
982 return;
983 }
984 subsurface->child.parent = child;
985 wl_list_insert(&child->children, &subsurface->child.link);
986 view_child_init(&subsurface->child, &subsurface_impl, child->view,
987 wlr_subsurface->surface);
988
989 wl_signal_add(&wlr_subsurface->events.destroy, &subsurface->destroy);
990 subsurface->destroy.notify = subsurface_handle_destroy;
991
992 subsurface->child.mapped = true;
993
994 view_child_damage(&subsurface->child, true);
995}
996
997static bool view_child_is_mapped(struct sway_view_child *child) {
998 while (child) {
999 if (!child->mapped) {
1000 return false;
1001 }
1002 child = child->parent;
1003 }
1004 return true;
1005}
1006
1007static void view_child_damage(struct sway_view_child *child, bool whole) {
1008 if (!child || !view_child_is_mapped(child) || !child->view || !child->view->container) {
1009 return;
1010 }
1011 int sx, sy;
1012 child->impl->get_view_coords(child, &sx, &sy);
1013 desktop_damage_surface(child->surface,
1014 child->view->container->pending.content_x -
1015 child->view->geometry.x + sx,
1016 child->view->container->pending.content_y -
1017 child->view->geometry.y + sy, whole);
1018}
1019
1020static void view_child_handle_surface_commit(struct wl_listener *listener,
1021 void *data) {
1022 struct sway_view_child *child =
1023 wl_container_of(listener, child, surface_commit);
1024 view_child_damage(child, false);
1025}
1026
1027static void view_child_handle_surface_new_subsurface(
1028 struct wl_listener *listener, void *data) {
1029 struct sway_view_child *child =
1030 wl_container_of(listener, child, surface_new_subsurface);
1031 struct wlr_subsurface *subsurface = data;
1032 view_child_subsurface_create(child, subsurface);
1033}
1034
1035static void view_child_handle_surface_destroy(struct wl_listener *listener,
1036 void *data) {
1037 struct sway_view_child *child =
1038 wl_container_of(listener, child, surface_destroy);
1039 view_child_destroy(child);
1040}
1041
1042static void view_init_subsurfaces(struct sway_view *view,
1043 struct wlr_surface *surface) {
1044 struct wlr_subsurface *subsurface;
1045 wl_list_for_each(subsurface, &surface->current.subsurfaces_below,
1046 current.link) {
1047 view_subsurface_create(view, subsurface);
1048 }
1049 wl_list_for_each(subsurface, &surface->current.subsurfaces_above,
1050 current.link) {
1051 view_subsurface_create(view, subsurface);
1052 }
1053}
1054
1055static void view_child_init_subsurfaces(struct sway_view_child *view_child,
1056 struct wlr_surface *surface) {
1057 struct wlr_subsurface *subsurface;
1058 wl_list_for_each(subsurface, &surface->current.subsurfaces_below,
1059 current.link) {
1060 view_child_subsurface_create(view_child, subsurface);
1061 }
1062 wl_list_for_each(subsurface, &surface->current.subsurfaces_above,
1063 current.link) {
1064 view_child_subsurface_create(view_child, subsurface);
1065 }
1066}
1067
1068static void view_child_handle_surface_map(struct wl_listener *listener,
1069 void *data) {
1070 struct sway_view_child *child =
1071 wl_container_of(listener, child, surface_map);
1072 child->mapped = true;
1073 view_child_damage(child, true);
1074}
1075
1076static void view_child_handle_surface_unmap(struct wl_listener *listener,
1077 void *data) {
1078 struct sway_view_child *child =
1079 wl_container_of(listener, child, surface_unmap);
1080 view_child_damage(child, true);
1081 child->mapped = false;
1082}
1083
1084static void view_child_handle_view_unmap(struct wl_listener *listener,
1085 void *data) {
1086 struct sway_view_child *child =
1087 wl_container_of(listener, child, view_unmap);
1088 view_child_damage(child, true);
1089 child->mapped = false;
1090}
1091
1092void view_child_init(struct sway_view_child *child,
1093 const struct sway_view_child_impl *impl, struct sway_view *view,
1094 struct wlr_surface *surface) {
1095 child->impl = impl;
1096 child->view = view;
1097 child->surface = surface;
1098 wl_list_init(&child->children);
1099
1100 wl_signal_add(&surface->events.commit, &child->surface_commit);
1101 child->surface_commit.notify = view_child_handle_surface_commit;
1102 wl_signal_add(&surface->events.new_subsurface,
1103 &child->surface_new_subsurface);
1104 child->surface_new_subsurface.notify =
1105 view_child_handle_surface_new_subsurface;
1106 wl_signal_add(&surface->events.destroy, &child->surface_destroy);
1107 child->surface_destroy.notify = view_child_handle_surface_destroy;
1108
1109 // Not all child views have a map/unmap event
1110 child->surface_map.notify = view_child_handle_surface_map;
1111 wl_list_init(&child->surface_map.link);
1112 child->surface_unmap.notify = view_child_handle_surface_unmap;
1113 wl_list_init(&child->surface_unmap.link);
1114
1115 wl_signal_add(&view->events.unmap, &child->view_unmap);
1116 child->view_unmap.notify = view_child_handle_view_unmap;
1117
1118 struct sway_container *container = child->view->container;
1119 if (container != NULL) {
1120 struct sway_workspace *workspace = container->pending.workspace;
1121 if (workspace) {
1122 wlr_surface_send_enter(child->surface, workspace->output->wlr_output);
1123 }
1124 }
1125
1126 view_child_init_subsurfaces(child, surface);
1127}
1128
1129void view_child_destroy(struct sway_view_child *child) {
1130 if (view_child_is_mapped(child) && child->view->container != NULL) {
1131 view_child_damage(child, true);
1132 }
1133
1134 if (child->parent != NULL) {
1135 wl_list_remove(&child->link);
1136 child->parent = NULL;
1137 }
1138
1139 struct sway_view_child *subchild, *tmpchild;
1140 wl_list_for_each_safe(subchild, tmpchild, &child->children, link) {
1141 wl_list_remove(&subchild->link);
1142 subchild->parent = NULL;
1143 // The subchild lost its parent link, so it cannot see that the parent
1144 // is unmapped. Unmap it directly.
1145 subchild->mapped = false;
1146 }
1147
1148 wl_list_remove(&child->surface_commit.link);
1149 wl_list_remove(&child->surface_destroy.link);
1150 wl_list_remove(&child->surface_map.link);
1151 wl_list_remove(&child->surface_unmap.link);
1152 wl_list_remove(&child->view_unmap.link);
1153 wl_list_remove(&child->surface_new_subsurface.link);
1154
1155 if (child->impl && child->impl->destroy) {
1156 child->impl->destroy(child);
1157 } else { 940 } else {
1158 free(child); 941 wlr_scene_node_set_position(&view->content_tree->node, 0, 0);
942 }
943
944 // only make sure to clip the content if there is content to clip
945 if (!wl_list_empty(&con->view->content_tree->children)) {
946 struct wlr_box clip = {0};
947 if (clip_to_geometry) {
948 clip = (struct wlr_box){
949 .x = con->view->geometry.x,
950 .y = con->view->geometry.y,
951 .width = con->current.content_width,
952 .height = con->current.content_height,
953 };
954 }
955 wlr_scene_subsurface_tree_set_clip(&con->view->content_tree->node, &clip);
1159 } 956 }
1160} 957}
1161 958
1162struct sway_view *view_from_wlr_surface(struct wlr_surface *wlr_surface) { 959struct sway_view *view_from_wlr_surface(struct wlr_surface *wlr_surface) {
1163 if (wlr_surface_is_xdg_surface(wlr_surface)) { 960 struct wlr_xdg_surface *xdg_surface;
1164 struct wlr_xdg_surface *xdg_surface = 961 if ((xdg_surface = wlr_xdg_surface_try_from_wlr_surface(wlr_surface))) {
1165 wlr_xdg_surface_from_wlr_surface(wlr_surface);
1166 if (xdg_surface == NULL) {
1167 return NULL;
1168 }
1169 return view_from_wlr_xdg_surface(xdg_surface); 962 return view_from_wlr_xdg_surface(xdg_surface);
1170 } 963 }
1171#if HAVE_XWAYLAND 964#if WLR_HAS_XWAYLAND
1172 if (wlr_surface_is_xwayland_surface(wlr_surface)) { 965 struct wlr_xwayland_surface *xsurface;
1173 struct wlr_xwayland_surface *xsurface = 966 if ((xsurface = wlr_xwayland_surface_try_from_wlr_surface(wlr_surface))) {
1174 wlr_xwayland_surface_from_wlr_surface(wlr_surface);
1175 if (xsurface == NULL) {
1176 return NULL;
1177 }
1178 return view_from_wlr_xwayland_surface(xsurface); 967 return view_from_wlr_xwayland_surface(xsurface);
1179 } 968 }
1180#endif 969#endif
1181 if (wlr_surface_is_subsurface(wlr_surface)) { 970 struct wlr_subsurface *subsurface;
1182 struct wlr_subsurface *subsurface = 971 if ((subsurface = wlr_subsurface_try_from_wlr_surface(wlr_surface))) {
1183 wlr_subsurface_from_wlr_surface(wlr_surface);
1184 if (subsurface == NULL) {
1185 return NULL;
1186 }
1187 return view_from_wlr_surface(subsurface->parent); 972 return view_from_wlr_surface(subsurface->parent);
1188 } 973 }
1189 if (wlr_surface_is_layer_surface(wlr_surface)) { 974 if (wlr_layer_surface_v1_try_from_wlr_surface(wlr_surface) != NULL) {
1190 return NULL; 975 return NULL;
1191 } 976 }
1192 977
@@ -1267,6 +1052,18 @@ static size_t parse_title_format(struct sway_view *view, char *buffer) {
1267 return len; 1052 return len;
1268} 1053}
1269 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
1270void view_update_title(struct sway_view *view, bool force) { 1067void view_update_title(struct sway_view *view, bool force) {
1271 const char *title = view_get_title(view); 1068 const char *title = view_get_title(view);
1272 1069
@@ -1282,29 +1079,41 @@ void view_update_title(struct sway_view *view, bool force) {
1282 1079
1283 free(view->container->title); 1080 free(view->container->title);
1284 free(view->container->formatted_title); 1081 free(view->container->formatted_title);
1285 if (title) { 1082
1286 size_t len = parse_title_format(view, NULL); 1083 size_t len = parse_title_format(view, NULL);
1084
1085 if (len) {
1287 char *buffer = calloc(len + 1, sizeof(char)); 1086 char *buffer = calloc(len + 1, sizeof(char));
1288 if (!sway_assert(buffer, "Unable to allocate title string")) { 1087 if (!sway_assert(buffer, "Unable to allocate title string")) {
1289 return; 1088 return;
1290 } 1089 }
1291 parse_title_format(view, buffer);
1292 1090
1293 view->container->title = strdup(title); 1091 parse_title_format(view, buffer);
1294 view->container->formatted_title = buffer; 1092 view->container->formatted_title = buffer;
1295 } else { 1093 } else {
1296 view->container->title = NULL;
1297 view->container->formatted_title = NULL; 1094 view->container->formatted_title = NULL;
1298 } 1095 }
1299 1096
1097 view->container->title = title ? strdup(title) : NULL;
1098
1300 // Update title after the global font height is updated 1099 // Update title after the global font height is updated
1301 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 }
1302 1107
1303 ipc_event_window(view->container, "title"); 1108 ipc_event_window(view->container, "title");
1304 1109
1305 if (view->foreign_toplevel && title) { 1110 if (view->foreign_toplevel && title) {
1306 wlr_foreign_toplevel_handle_v1_set_title(view->foreign_toplevel, title); 1111 wlr_foreign_toplevel_handle_v1_set_title(view->foreign_toplevel, title);
1307 } 1112 }
1113
1114 if (view->ext_foreign_toplevel) {
1115 update_ext_foreign_toplevel(view);
1116 }
1308} 1117}
1309 1118
1310bool view_is_visible(struct sway_view *view) { 1119bool view_is_visible(struct sway_view *view) {
@@ -1365,6 +1174,7 @@ void view_set_urgent(struct sway_view *view, bool enable) {
1365 return; 1174 return;
1366 } 1175 }
1367 clock_gettime(CLOCK_MONOTONIC, &view->urgent); 1176 clock_gettime(CLOCK_MONOTONIC, &view->urgent);
1177 container_update_itself_and_parents(view->container);
1368 } else { 1178 } else {
1369 view->urgent = (struct timespec){ 0 }; 1179 view->urgent = (struct timespec){ 0 };
1370 if (view->urgent_timer) { 1180 if (view->urgent_timer) {
@@ -1372,7 +1182,6 @@ void view_set_urgent(struct sway_view *view, bool enable) {
1372 view->urgent_timer = NULL; 1182 view->urgent_timer = NULL;
1373 } 1183 }
1374 } 1184 }
1375 container_damage_whole(view->container);
1376 1185
1377 ipc_event_window(view->container, "urgent"); 1186 ipc_event_window(view->container, "urgent");
1378 1187
@@ -1386,40 +1195,54 @@ bool view_is_urgent(struct sway_view *view) {
1386} 1195}
1387 1196
1388void view_remove_saved_buffer(struct sway_view *view) { 1197void view_remove_saved_buffer(struct sway_view *view) {
1389 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")) {
1390 return; 1199 return;
1391 } 1200 }
1392 struct sway_saved_buffer *saved_buf, *tmp; 1201
1393 wl_list_for_each_safe(saved_buf, tmp, &view->saved_buffers, link) { 1202 wlr_scene_node_destroy(&view->saved_surface_tree->node);
1394 wlr_buffer_unlock(&saved_buf->buffer->base); 1203 view->saved_surface_tree = NULL;
1395 wl_list_remove(&saved_buf->link); 1204 wlr_scene_node_set_enabled(&view->content_tree->node, true);
1396 free(saved_buf);
1397 }
1398} 1205}
1399 1206
1400static void view_save_buffer_iterator(struct wlr_surface *surface, 1207static void view_save_buffer_iterator(struct wlr_scene_buffer *buffer,
1401 int sx, int sy, void *data) { 1208 int sx, int sy, void *data) {
1402 struct sway_view *view = data; 1209 struct wlr_scene_tree *tree = data;
1403 1210
1404 if (surface && wlr_surface_has_buffer(surface)) { 1211 struct wlr_scene_buffer *sbuf = wlr_scene_buffer_create(tree, NULL);
1405 wlr_buffer_lock(&surface->buffer->base); 1212 if (!sbuf) {
1406 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");
1407 saved_buffer->buffer = surface->buffer; 1214 return;
1408 saved_buffer->width = surface->current.width;
1409 saved_buffer->height = surface->current.height;
1410 saved_buffer->x = view->container->surface_x + sx;
1411 saved_buffer->y = view->container->surface_y + sy;
1412 saved_buffer->transform = surface->current.transform;
1413 wlr_surface_get_buffer_source_box(surface, &saved_buffer->source_box);
1414 wl_list_insert(view->saved_buffers.prev, &saved_buffer->link);
1415 } 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);
1416} 1224}
1417 1225
1418void view_save_buffer(struct sway_view *view) { 1226void view_save_buffer(struct sway_view *view) {
1419 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")) {
1420 view_remove_saved_buffer(view); 1228 view_remove_saved_buffer(view);
1421 } 1229 }
1422 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);
1423} 1246}
1424 1247
1425bool view_is_transient_for(struct sway_view *child, 1248bool view_is_transient_for(struct sway_view *child,
@@ -1427,3 +1250,19 @@ bool view_is_transient_for(struct sway_view *child,
1427 return child->impl->is_transient_for && 1250 return child->impl->is_transient_for &&
1428 child->impl->is_transient_for(child, ancestor); 1251 child->impl->is_transient_for(child, ancestor);
1429} 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 c84320bd..52e48ad5 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>
@@ -11,6 +10,7 @@
11#include "sway/input/seat.h" 10#include "sway/input/seat.h"
12#include "sway/ipc-server.h" 11#include "sway/ipc-server.h"
13#include "sway/output.h" 12#include "sway/output.h"
13#include "sway/server.h"
14#include "sway/tree/arrange.h" 14#include "sway/tree/arrange.h"
15#include "sway/tree/container.h" 15#include "sway/tree/container.h"
16#include "sway/tree/node.h" 16#include "sway/tree/node.h"
@@ -56,6 +56,8 @@ struct sway_output *workspace_get_initial_output(const char *name) {
56 56
57struct sway_workspace *workspace_create(struct sway_output *output, 57struct sway_workspace *workspace_create(struct sway_output *output,
58 const char *name) { 58 const char *name) {
59 sway_assert(name, "NULL name given to workspace_create");
60
59 if (output == NULL) { 61 if (output == NULL) {
60 output = workspace_get_initial_output(name); 62 output = workspace_get_initial_output(name);
61 } 63 }
@@ -69,7 +71,19 @@ struct sway_workspace *workspace_create(struct sway_output *output,
69 return NULL; 71 return NULL;
70 } 72 }
71 node_init(&ws->node, N_WORKSPACE, ws); 73 node_init(&ws->node, N_WORKSPACE, ws);
72 ws->name = name ? strdup(name) : NULL; 74
75 bool failed = false;
76 ws->layers.tiling = alloc_scene_tree(root->staging, &failed);
77 ws->layers.fullscreen = alloc_scene_tree(root->staging, &failed);
78
79 if (failed) {
80 wlr_scene_node_destroy(&ws->layers.tiling->node);
81 wlr_scene_node_destroy(&ws->layers.fullscreen->node);
82 free(ws);
83 return NULL;
84 }
85
86 ws->name = strdup(name);
73 ws->prev_split_layout = L_NONE; 87 ws->prev_split_layout = L_NONE;
74 ws->layout = output_get_default_layout(output); 88 ws->layout = output_get_default_layout(output);
75 ws->floating = create_list(); 89 ws->floating = create_list();
@@ -114,7 +128,7 @@ struct sway_workspace *workspace_create(struct sway_output *output,
114 output_sort_workspaces(output); 128 output_sort_workspaces(output);
115 129
116 ipc_event_workspace(NULL, ws, "init"); 130 ipc_event_workspace(NULL, ws, "init");
117 wl_signal_emit(&root->events.new_node, &ws->node); 131 wl_signal_emit_mutable(&root->events.new_node, &ws->node);
118 132
119 return ws; 133 return ws;
120} 134}
@@ -129,6 +143,11 @@ void workspace_destroy(struct sway_workspace *workspace) {
129 return; 143 return;
130 } 144 }
131 145
146 scene_node_disown_children(workspace->layers.tiling);
147 scene_node_disown_children(workspace->layers.fullscreen);
148 wlr_scene_node_destroy(&workspace->layers.tiling->node);
149 wlr_scene_node_destroy(&workspace->layers.fullscreen->node);
150
132 free(workspace->name); 151 free(workspace->name);
133 free(workspace->representation); 152 free(workspace->representation);
134 list_free_items_and_destroy(workspace->output_priority); 153 list_free_items_and_destroy(workspace->output_priority);
@@ -142,7 +161,7 @@ void workspace_destroy(struct sway_workspace *workspace) {
142void workspace_begin_destroy(struct sway_workspace *workspace) { 161void workspace_begin_destroy(struct sway_workspace *workspace) {
143 sway_log(SWAY_DEBUG, "Destroying workspace '%s'", workspace->name); 162 sway_log(SWAY_DEBUG, "Destroying workspace '%s'", workspace->name);
144 ipc_event_workspace(NULL, workspace, "empty"); // intentional 163 ipc_event_workspace(NULL, workspace, "empty"); // intentional
145 wl_signal_emit(&workspace->node.events.destroy, &workspace->node); 164 wl_signal_emit_mutable(&workspace->node.events.destroy, &workspace->node);
146 165
147 if (workspace->output) { 166 if (workspace->output) {
148 workspace_detach(workspace); 167 workspace_detach(workspace);
@@ -174,22 +193,16 @@ void workspace_consider_destroy(struct sway_workspace *ws) {
174static bool workspace_valid_on_output(const char *output_name, 193static bool workspace_valid_on_output(const char *output_name,
175 const char *ws_name) { 194 const char *ws_name) {
176 struct workspace_config *wsc = workspace_find_config(ws_name); 195 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); 196 struct sway_output *output = output_by_name_or_id(output_name);
179 if (!output) { 197 if (!output) {
180 return false; 198 return false;
181 } 199 }
182 output_name = output->wlr_output->name;
183 output_get_identifier(identifier, sizeof(identifier), output);
184
185 if (!wsc) { 200 if (!wsc) {
186 return true; 201 return true;
187 } 202 }
188 203
189 for (int i = 0; i < wsc->outputs->length; i++) { 204 for (int i = 0; i < wsc->outputs->length; i++) {
190 if (strcmp(wsc->outputs->items[i], "*") == 0 || 205 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; 206 return true;
194 } 207 }
195 } 208 }
@@ -284,13 +297,10 @@ char *workspace_next_name(const char *output_name) {
284 // assignments primarily, falling back to bindings and numbers. 297 // assignments primarily, falling back to bindings and numbers.
285 struct sway_mode *mode = config->current_mode; 298 struct sway_mode *mode = config->current_mode;
286 299
287 char identifier[128];
288 struct sway_output *output = output_by_name_or_id(output_name); 300 struct sway_output *output = output_by_name_or_id(output_name);
289 if (!output) { 301 if (!output) {
290 return NULL; 302 return NULL;
291 } 303 }
292 output_name = output->wlr_output->name;
293 output_get_identifier(identifier, sizeof(identifier), output);
294 304
295 int order = INT_MAX; 305 int order = INT_MAX;
296 char *target = NULL; 306 char *target = NULL;
@@ -310,9 +320,7 @@ char *workspace_next_name(const char *output_name) {
310 } 320 }
311 bool found = false; 321 bool found = false;
312 for (int j = 0; j < wsc->outputs->length; ++j) { 322 for (int j = 0; j < wsc->outputs->length; ++j) {
313 if (strcmp(wsc->outputs->items[j], "*") == 0 || 323 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; 324 found = true;
317 free(target); 325 free(target);
318 target = strdup(wsc->workspace); 326 target = strdup(wsc->workspace);
@@ -652,15 +660,9 @@ void workspace_output_add_priority(struct sway_workspace *workspace,
652 660
653struct sway_output *workspace_output_get_highest_available( 661struct sway_output *workspace_output_get_highest_available(
654 struct sway_workspace *ws, struct sway_output *exclude) { 662 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++) { 663 for (int i = 0; i < ws->output_priority->length; i++) {
661 char *name = ws->output_priority->items[i]; 664 const char *name = ws->output_priority->items[i];
662 if (exclude && (strcmp(name, exclude->wlr_output->name) == 0 665 if (exclude && output_match_name_or_id(exclude, name)) {
663 || strcmp(name, exclude_id) == 0)) {
664 continue; 666 continue;
665 } 667 }
666 668
@@ -684,7 +686,6 @@ void workspace_detect_urgent(struct sway_workspace *workspace) {
684 if (workspace->urgent != new_urgent) { 686 if (workspace->urgent != new_urgent) {
685 workspace->urgent = new_urgent; 687 workspace->urgent = new_urgent;
686 ipc_event_workspace(NULL, workspace, "urgent"); 688 ipc_event_workspace(NULL, workspace, "urgent");
687 output_damage_whole(workspace->output);
688 } 689 }
689} 690}
690 691
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 ec9e8d68..fa8c6279 100644
--- a/sway/xdg_decoration.c
+++ b/sway/xdg_decoration.c
@@ -23,37 +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 struct sway_view *view = deco->view; 26 set_xdg_decoration_mode(deco);
27 enum wlr_xdg_toplevel_decoration_v1_mode mode =
28 WLR_XDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE;
29 enum wlr_xdg_toplevel_decoration_v1_mode client_mode =
30 deco->wlr_xdg_decoration->requested_mode;
31
32 bool floating;
33 if (view->container) {
34 floating = container_is_floating(view->container);
35 bool csd = false;
36 csd = client_mode ==
37 WLR_XDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE;
38 view_update_csd_from_client(view, csd);
39 arrange_container(view->container);
40 transaction_commit_dirty();
41 } else {
42 floating = view->impl->wants_floating &&
43 view->impl->wants_floating(view);
44 }
45
46 if (floating && client_mode) {
47 mode = client_mode;
48 }
49
50 wlr_xdg_toplevel_decoration_v1_set_mode(deco->wlr_xdg_decoration,
51 mode);
52} 27}
53 28
54void handle_xdg_decoration(struct wl_listener *listener, void *data) { 29void handle_xdg_decoration(struct wl_listener *listener, void *data) {
55 struct wlr_xdg_toplevel_decoration_v1 *wlr_deco = data; 30 struct wlr_xdg_toplevel_decoration_v1 *wlr_deco = data;
56 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;
57 32
58 struct sway_xdg_decoration *deco = calloc(1, sizeof(*deco)); 33 struct sway_xdg_decoration *deco = calloc(1, sizeof(*deco));
59 if (deco == NULL) { 34 if (deco == NULL) {
@@ -72,16 +47,46 @@ void handle_xdg_decoration(struct wl_listener *listener, void *data) {
72 47
73 wl_list_insert(&server.xdg_decorations, &deco->link); 48 wl_list_insert(&server.xdg_decorations, &deco->link);
74 49
75 xdg_decoration_handle_request_mode(&deco->request_mode, wlr_deco); 50 set_xdg_decoration_mode(deco);
76} 51}
77 52
78struct sway_xdg_decoration *xdg_decoration_from_surface( 53struct sway_xdg_decoration *xdg_decoration_from_surface(
79 struct wlr_surface *surface) { 54 struct wlr_surface *surface) {
80 struct sway_xdg_decoration *deco; 55 struct sway_xdg_decoration *deco;
81 wl_list_for_each(deco, &server.xdg_decorations, link) { 56 wl_list_for_each(deco, &server.xdg_decorations, link) {
82 if (deco->wlr_xdg_decoration->surface->surface == surface) { 57 if (deco->wlr_xdg_decoration->toplevel->base->surface == surface) {
83 return deco; 58 return deco;
84 } 59 }
85 } 60 }
86 return NULL; 61 return NULL;
87} 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}