aboutsummaryrefslogtreecommitdiffstats
path: root/sway
diff options
context:
space:
mode:
Diffstat (limited to 'sway')
-rw-r--r--sway/commands.c1
-rw-r--r--sway/commands/bind.c48
-rw-r--r--sway/commands/swap.c80
-rw-r--r--sway/criteria.c18
-rw-r--r--sway/desktop/xdg_shell.c12
-rw-r--r--sway/input/keyboard.c25
-rw-r--r--sway/meson.build1
-rw-r--r--sway/sway.5.scd23
-rw-r--r--sway/tree/container.c94
-rw-r--r--sway/tree/layout.c124
-rw-r--r--sway/tree/view.c10
11 files changed, 357 insertions, 79 deletions
diff --git a/sway/commands.c b/sway/commands.c
index 6cba0a1c..c3728afd 100644
--- a/sway/commands.c
+++ b/sway/commands.c
@@ -186,6 +186,7 @@ static struct cmd_handler command_handlers[] = {
186 { "splith", cmd_splith }, 186 { "splith", cmd_splith },
187 { "splitt", cmd_splitt }, 187 { "splitt", cmd_splitt },
188 { "splitv", cmd_splitv }, 188 { "splitv", cmd_splitv },
189 { "swap", cmd_swap },
189 { "title_format", cmd_title_format }, 190 { "title_format", cmd_title_format },
190 { "unmark", cmd_unmark }, 191 { "unmark", cmd_unmark },
191}; 192};
diff --git a/sway/commands/bind.c b/sway/commands/bind.c
index cbabb07b..c6b3368a 100644
--- a/sway/commands/bind.c
+++ b/sway/commands/bind.c
@@ -83,20 +83,26 @@ struct cmd_results *cmd_bindsym(int argc, char **argv) {
83 binding->keys = create_list(); 83 binding->keys = create_list();
84 binding->modifiers = 0; 84 binding->modifiers = 0;
85 binding->release = false; 85 binding->release = false;
86 binding->locked = false;
86 binding->bindcode = false; 87 binding->bindcode = false;
87 88
88 // Handle --release 89 // Handle --release and --locked
89 if (strcmp("--release", argv[0]) == 0) { 90 while (argc > 0) {
90 if (argc >= 3) { 91 if (strcmp("--release", argv[0]) == 0) {
91 binding->release = true; 92 binding->release = true;
92 argv++; 93 } else if (strcmp("--locked", argv[0]) == 0) {
93 argc--; 94 binding->locked = true;
94 } else { 95 } else {
95 free_sway_binding(binding); 96 break;
96 return cmd_results_new(CMD_FAILURE, "bindsym",
97 "Invalid bindsym command "
98 "(expected more than 2 arguments, got %d)", argc);
99 } 97 }
98 argv++;
99 argc--;
100 }
101 if (argc < 2) {
102 free_sway_binding(binding);
103 return cmd_results_new(CMD_FAILURE, "bindsym",
104 "Invalid bindsym command "
105 "(expected at least 2 non-option arguments, got %d)", argc);
100 } 106 }
101 107
102 binding->command = join_args(argv + 1, argc - 1); 108 binding->command = join_args(argv + 1, argc - 1);
@@ -176,20 +182,26 @@ struct cmd_results *cmd_bindcode(int argc, char **argv) {
176 binding->keys = create_list(); 182 binding->keys = create_list();
177 binding->modifiers = 0; 183 binding->modifiers = 0;
178 binding->release = false; 184 binding->release = false;
185 binding->locked = false;
179 binding->bindcode = true; 186 binding->bindcode = true;
180 187
181 // Handle --release 188 // Handle --release and --locked
182 if (strcmp("--release", argv[0]) == 0) { 189 while (argc > 0) {
183 if (argc >= 3) { 190 if (strcmp("--release", argv[0]) == 0) {
184 binding->release = true; 191 binding->release = true;
185 argv++; 192 } else if (strcmp("--locked", argv[0]) == 0) {
186 argc--; 193 binding->locked = true;
187 } else { 194 } else {
188 free_sway_binding(binding); 195 break;
189 return cmd_results_new(CMD_FAILURE, "bindcode",
190 "Invalid bindcode command "
191 "(expected more than 2 arguments, got %d)", argc);
192 } 196 }
197 argv++;
198 argc--;
199 }
200 if (argc < 2) {
201 free_sway_binding(binding);
202 return cmd_results_new(CMD_FAILURE, "bindcode",
203 "Invalid bindcode command "
204 "(expected at least 2 non-option arguments, got %d)", argc);
193 } 205 }
194 206
195 binding->command = join_args(argv + 1, argc - 1); 207 binding->command = join_args(argv + 1, argc - 1);
diff --git a/sway/commands/swap.c b/sway/commands/swap.c
new file mode 100644
index 00000000..e925ad33
--- /dev/null
+++ b/sway/commands/swap.c
@@ -0,0 +1,80 @@
1#include <strings.h>
2#include <wlr/util/log.h>
3#include "sway/commands.h"
4#include "sway/tree/layout.h"
5#include "sway/tree/view.h"
6#include "stringop.h"
7
8static const char* EXPECTED_SYNTAX =
9 "Expected 'swap container with id|con_id|mark <arg>'";
10
11static bool test_con_id(struct sway_container *container, void *con_id) {
12 return container->id == (size_t)con_id;
13}
14
15static bool test_id(struct sway_container *container, void *id) {
16 xcb_window_t *wid = id;
17 return (container->type == C_VIEW
18 && container->sway_view->type == SWAY_VIEW_XWAYLAND
19 && container->sway_view->wlr_xwayland_surface->window_id == *wid);
20}
21
22static bool test_mark(struct sway_container *container, void *mark) {
23 if (container->type == C_VIEW && container->sway_view->marks->length) {
24 return !list_seq_find(container->sway_view->marks,
25 (int (*)(const void *, const void *))strcmp, mark);
26 }
27 return false;
28}
29
30struct cmd_results *cmd_swap(int argc, char **argv) {
31 struct cmd_results *error = NULL;
32 if ((error = checkarg(argc, "swap", EXPECTED_AT_LEAST, 4))) {
33 return error;
34 }
35
36 if (strcasecmp(argv[0], "container") || strcasecmp(argv[1], "with")) {
37 return cmd_results_new(CMD_INVALID, "swap", EXPECTED_SYNTAX);
38 }
39
40 struct sway_container *current = config->handler_context.current_container;
41 struct sway_container *other;
42
43 char *value = join_args(argv + 3, argc - 3);
44 if (strcasecmp(argv[2], "id") == 0) {
45 xcb_window_t id = strtol(value, NULL, 0);
46 other = container_find(&root_container, test_id, (void *)&id);
47 } else if (strcasecmp(argv[2], "con_id") == 0) {
48 size_t con_id = atoi(value);
49 other = container_find(&root_container, test_con_id, (void *)con_id);
50 } else if (strcasecmp(argv[2], "mark") == 0) {
51 other = container_find(&root_container, test_mark, (void *)value);
52 } else {
53 free(value);
54 return cmd_results_new(CMD_INVALID, "swap", EXPECTED_SYNTAX);
55 }
56
57 if (!other) {
58 error = cmd_results_new(CMD_FAILURE, "swap",
59 "Failed to find %s '%s'", argv[2], value);
60 } else if (current->type < C_CONTAINER || other->type < C_CONTAINER) {
61 error = cmd_results_new(CMD_FAILURE, "swap",
62 "Can only swap with containers and views");
63 } else if (container_has_anscestor(current, other)
64 || container_has_anscestor(other, current)) {
65 error = cmd_results_new(CMD_FAILURE, "swap",
66 "Cannot swap ancestor and descendant");
67 } else if (current->layout == L_FLOATING || other->layout == L_FLOATING) {
68 error = cmd_results_new(CMD_FAILURE, "swap",
69 "Swapping with floating containers is not supported");
70 }
71
72 free(value);
73
74 if (error) {
75 return error;
76 }
77
78 container_swap(current, other);
79 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
80}
diff --git a/sway/criteria.c b/sway/criteria.c
index 4295cacc..dec5fed7 100644
--- a/sway/criteria.c
+++ b/sway/criteria.c
@@ -13,6 +13,7 @@
13 13
14bool criteria_is_empty(struct criteria *criteria) { 14bool criteria_is_empty(struct criteria *criteria) {
15 return !criteria->title 15 return !criteria->title
16 && !criteria->shell
16 && !criteria->app_id 17 && !criteria->app_id
17 && !criteria->class 18 && !criteria->class
18 && !criteria->instance 19 && !criteria->instance
@@ -29,6 +30,7 @@ bool criteria_is_empty(struct criteria *criteria) {
29 30
30void criteria_destroy(struct criteria *criteria) { 31void criteria_destroy(struct criteria *criteria) {
31 pcre_free(criteria->title); 32 pcre_free(criteria->title);
33 pcre_free(criteria->shell);
32 pcre_free(criteria->app_id); 34 pcre_free(criteria->app_id);
33 pcre_free(criteria->class); 35 pcre_free(criteria->class);
34 pcre_free(criteria->instance); 36 pcre_free(criteria->instance);
@@ -53,6 +55,13 @@ static bool criteria_matches_view(struct criteria *criteria,
53 } 55 }
54 } 56 }
55 57
58 if (criteria->shell) {
59 const char *shell = view_get_shell(view);
60 if (!shell || regex_cmp(shell, criteria->shell) != 0) {
61 return false;
62 }
63 }
64
56 if (criteria->app_id) { 65 if (criteria->app_id) {
57 const char *app_id = view_get_app_id(view); 66 const char *app_id = view_get_app_id(view);
58 if (!app_id || regex_cmp(app_id, criteria->app_id) != 0) { 67 if (!app_id || regex_cmp(app_id, criteria->app_id) != 0) {
@@ -206,6 +215,7 @@ enum criteria_token {
206 T_FLOATING, 215 T_FLOATING,
207 T_ID, 216 T_ID,
208 T_INSTANCE, 217 T_INSTANCE,
218 T_SHELL,
209 T_TILING, 219 T_TILING,
210 T_TITLE, 220 T_TITLE,
211 T_URGENT, 221 T_URGENT,
@@ -229,6 +239,8 @@ static enum criteria_token token_from_name(char *name) {
229 return T_ID; 239 return T_ID;
230 } else if (strcmp(name, "instance") == 0) { 240 } else if (strcmp(name, "instance") == 0) {
231 return T_INSTANCE; 241 return T_INSTANCE;
242 } else if (strcmp(name, "shell") == 0) {
243 return T_SHELL;
232 } else if (strcmp(name, "title") == 0) { 244 } else if (strcmp(name, "title") == 0) {
233 return T_TITLE; 245 return T_TITLE;
234 } else if (strcmp(name, "urgent") == 0) { 246 } else if (strcmp(name, "urgent") == 0) {
@@ -271,6 +283,9 @@ static char *get_focused_prop(enum criteria_token token) {
271 case T_INSTANCE: 283 case T_INSTANCE:
272 value = view_get_instance(view); 284 value = view_get_instance(view);
273 break; 285 break;
286 case T_SHELL:
287 value = view_get_shell(view);
288 break;
274 case T_TITLE: 289 case T_TITLE:
275 value = view_get_class(view); 290 value = view_get_class(view);
276 break; 291 break;
@@ -332,6 +347,9 @@ static bool parse_token(struct criteria *criteria, char *name, char *value) {
332 case T_TITLE: 347 case T_TITLE:
333 generate_regex(&criteria->title, effective_value); 348 generate_regex(&criteria->title, effective_value);
334 break; 349 break;
350 case T_SHELL:
351 generate_regex(&criteria->shell, effective_value);
352 break;
335 case T_APP_ID: 353 case T_APP_ID:
336 generate_regex(&criteria->app_id, effective_value); 354 generate_regex(&criteria->app_id, effective_value);
337 break; 355 break;
diff --git a/sway/desktop/xdg_shell.c b/sway/desktop/xdg_shell.c
index 9a0d282b..b2b95fa0 100644
--- a/sway/desktop/xdg_shell.c
+++ b/sway/desktop/xdg_shell.c
@@ -3,13 +3,14 @@
3#include <stdlib.h> 3#include <stdlib.h>
4#include <wayland-server.h> 4#include <wayland-server.h>
5#include <wlr/types/wlr_xdg_shell.h> 5#include <wlr/types/wlr_xdg_shell.h>
6#include <wlr/util/edges.h>
7#include "log.h"
8#include "sway/input/input-manager.h"
9#include "sway/input/seat.h"
10#include "sway/server.h"
6#include "sway/tree/container.h" 11#include "sway/tree/container.h"
7#include "sway/tree/layout.h" 12#include "sway/tree/layout.h"
8#include "sway/server.h"
9#include "sway/tree/view.h" 13#include "sway/tree/view.h"
10#include "sway/input/seat.h"
11#include "sway/input/input-manager.h"
12#include "log.h"
13 14
14static const struct sway_view_child_impl popup_impl; 15static const struct sway_view_child_impl popup_impl;
15 16
@@ -248,7 +249,8 @@ void handle_xdg_shell_surface(struct wl_listener *listener, void *data) {
248 wlr_log(L_DEBUG, "New xdg_shell toplevel title='%s' app_id='%s'", 249 wlr_log(L_DEBUG, "New xdg_shell toplevel title='%s' app_id='%s'",
249 xdg_surface->toplevel->title, xdg_surface->toplevel->app_id); 250 xdg_surface->toplevel->title, xdg_surface->toplevel->app_id);
250 wlr_xdg_surface_ping(xdg_surface); 251 wlr_xdg_surface_ping(xdg_surface);
251 wlr_xdg_toplevel_set_maximized(xdg_surface, true); 252 wlr_xdg_toplevel_set_tiled(xdg_surface, WLR_EDGE_LEFT | WLR_EDGE_RIGHT |
253 WLR_EDGE_TOP | WLR_EDGE_BOTTOM);
252 254
253 struct sway_xdg_shell_view *xdg_shell_view = 255 struct sway_xdg_shell_view *xdg_shell_view =
254 calloc(1, sizeof(struct sway_xdg_shell_view)); 256 calloc(1, sizeof(struct sway_xdg_shell_view));
diff --git a/sway/input/keyboard.c b/sway/input/keyboard.c
index c07557db..e873eea3 100644
--- a/sway/input/keyboard.c
+++ b/sway/input/keyboard.c
@@ -141,7 +141,7 @@ static bool keyboard_execute_compositor_binding(struct sway_keyboard *keyboard,
141 */ 141 */
142static bool keyboard_execute_bindsym(struct sway_keyboard *keyboard, 142static bool keyboard_execute_bindsym(struct sway_keyboard *keyboard,
143 xkb_keysym_t *pressed_keysyms, uint32_t modifiers, 143 xkb_keysym_t *pressed_keysyms, uint32_t modifiers,
144 enum wlr_key_state key_state) { 144 enum wlr_key_state key_state, bool locked) {
145 // configured bindings 145 // configured bindings
146 int n = pressed_keysyms_length(pressed_keysyms); 146 int n = pressed_keysyms_length(pressed_keysyms);
147 list_t *keysym_bindings = config->current_mode->keysym_bindings; 147 list_t *keysym_bindings = config->current_mode->keysym_bindings;
@@ -149,7 +149,7 @@ static bool keyboard_execute_bindsym(struct sway_keyboard *keyboard,
149 struct sway_binding *binding = keysym_bindings->items[i]; 149 struct sway_binding *binding = keysym_bindings->items[i];
150 if (!binding_matches_key_state(binding, key_state) || 150 if (!binding_matches_key_state(binding, key_state) ||
151 modifiers ^ binding->modifiers || 151 modifiers ^ binding->modifiers ||
152 n != binding->keys->length) { 152 n != binding->keys->length || locked > binding->locked) {
153 continue; 153 continue;
154 } 154 }
155 155
@@ -174,7 +174,7 @@ static bool keyboard_execute_bindsym(struct sway_keyboard *keyboard,
174} 174}
175 175
176static bool binding_matches_keycodes(struct wlr_keyboard *keyboard, 176static bool binding_matches_keycodes(struct wlr_keyboard *keyboard,
177 struct sway_binding *binding, struct wlr_event_keyboard_key *event) { 177 struct sway_binding *binding, struct wlr_event_keyboard_key *event, bool locked) {
178 assert(binding->bindcode); 178 assert(binding->bindcode);
179 179
180 uint32_t keycode = event->keycode + 8; 180 uint32_t keycode = event->keycode + 8;
@@ -183,6 +183,10 @@ static bool binding_matches_keycodes(struct wlr_keyboard *keyboard,
183 return false; 183 return false;
184 } 184 }
185 185
186 if (locked > binding->locked) {
187 return false;
188 }
189
186 uint32_t modifiers = wlr_keyboard_get_modifiers(keyboard); 190 uint32_t modifiers = wlr_keyboard_get_modifiers(keyboard);
187 if (modifiers ^ binding->modifiers) { 191 if (modifiers ^ binding->modifiers) {
188 return false; 192 return false;
@@ -265,13 +269,13 @@ static bool binding_matches_keycodes(struct wlr_keyboard *keyboard,
265 * should be propagated to clients. 269 * should be propagated to clients.
266 */ 270 */
267static bool keyboard_execute_bindcode(struct sway_keyboard *keyboard, 271static bool keyboard_execute_bindcode(struct sway_keyboard *keyboard,
268 struct wlr_event_keyboard_key *event) { 272 struct wlr_event_keyboard_key *event, bool locked) {
269 struct wlr_keyboard *wlr_keyboard = 273 struct wlr_keyboard *wlr_keyboard =
270 keyboard->seat_device->input_device->wlr_device->keyboard; 274 keyboard->seat_device->input_device->wlr_device->keyboard;
271 list_t *keycode_bindings = config->current_mode->keycode_bindings; 275 list_t *keycode_bindings = config->current_mode->keycode_bindings;
272 for (int i = 0; i < keycode_bindings->length; ++i) { 276 for (int i = 0; i < keycode_bindings->length; ++i) {
273 struct sway_binding *binding = keycode_bindings->items[i]; 277 struct sway_binding *binding = keycode_bindings->items[i];
274 if (binding_matches_keycodes(wlr_keyboard, binding, event)) { 278 if (binding_matches_keycodes(wlr_keyboard, binding, event, locked)) {
275 keyboard_execute_command(keyboard, binding); 279 keyboard_execute_command(keyboard, binding);
276 return true; 280 return true;
277 } 281 }
@@ -333,19 +337,20 @@ static void handle_keyboard_key(struct wl_listener *listener, void *data) {
333 keyboard->seat_device->input_device->wlr_device; 337 keyboard->seat_device->input_device->wlr_device;
334 wlr_idle_notify_activity(keyboard->seat_device->sway_seat->input->server->idle, wlr_seat); 338 wlr_idle_notify_activity(keyboard->seat_device->sway_seat->input->server->idle, wlr_seat);
335 struct wlr_event_keyboard_key *event = data; 339 struct wlr_event_keyboard_key *event = data;
340 bool input_inhibited = keyboard->seat_device->sway_seat->exclusive_client != NULL;
336 341
337 xkb_keycode_t keycode = event->keycode + 8; 342 xkb_keycode_t keycode = event->keycode + 8;
338 bool handled = false; 343 bool handled = false;
339 344
340 // handle keycodes 345 // handle keycodes
341 handled = keyboard_execute_bindcode(keyboard, event); 346 handled = keyboard_execute_bindcode(keyboard, event, input_inhibited);
342 347
343 // handle translated keysyms 348 // handle translated keysyms
344 if (!handled && event->state == WLR_KEY_RELEASED) { 349 if (!handled && event->state == WLR_KEY_RELEASED) {
345 handled = keyboard_execute_bindsym(keyboard, 350 handled = keyboard_execute_bindsym(keyboard,
346 keyboard->pressed_keysyms_translated, 351 keyboard->pressed_keysyms_translated,
347 keyboard->modifiers_translated, 352 keyboard->modifiers_translated,
348 event->state); 353 event->state, input_inhibited);
349 } 354 }
350 const xkb_keysym_t *translated_keysyms; 355 const xkb_keysym_t *translated_keysyms;
351 size_t translated_keysyms_len = 356 size_t translated_keysyms_len =
@@ -357,14 +362,14 @@ static void handle_keyboard_key(struct wl_listener *listener, void *data) {
357 handled = keyboard_execute_bindsym(keyboard, 362 handled = keyboard_execute_bindsym(keyboard,
358 keyboard->pressed_keysyms_translated, 363 keyboard->pressed_keysyms_translated,
359 keyboard->modifiers_translated, 364 keyboard->modifiers_translated,
360 event->state); 365 event->state, input_inhibited);
361 } 366 }
362 367
363 // Handle raw keysyms 368 // Handle raw keysyms
364 if (!handled && event->state == WLR_KEY_RELEASED) { 369 if (!handled && event->state == WLR_KEY_RELEASED) {
365 handled = keyboard_execute_bindsym(keyboard, 370 handled = keyboard_execute_bindsym(keyboard,
366 keyboard->pressed_keysyms_raw, keyboard->modifiers_raw, 371 keyboard->pressed_keysyms_raw, keyboard->modifiers_raw,
367 event->state); 372 event->state, input_inhibited);
368 } 373 }
369 const xkb_keysym_t *raw_keysyms; 374 const xkb_keysym_t *raw_keysyms;
370 size_t raw_keysyms_len = 375 size_t raw_keysyms_len =
@@ -374,7 +379,7 @@ static void handle_keyboard_key(struct wl_listener *listener, void *data) {
374 if (!handled && event->state == WLR_KEY_PRESSED) { 379 if (!handled && event->state == WLR_KEY_PRESSED) {
375 handled = keyboard_execute_bindsym(keyboard, 380 handled = keyboard_execute_bindsym(keyboard,
376 keyboard->pressed_keysyms_raw, keyboard->modifiers_raw, 381 keyboard->pressed_keysyms_raw, keyboard->modifiers_raw,
377 event->state); 382 event->state, input_inhibited);
378 } 383 }
379 384
380 // Compositor bindings 385 // Compositor bindings
diff --git a/sway/meson.build b/sway/meson.build
index 72347d51..9c942e8e 100644
--- a/sway/meson.build
+++ b/sway/meson.build
@@ -63,6 +63,7 @@ sway_sources = files(
63 'commands/show_marks.c', 63 'commands/show_marks.c',
64 'commands/split.c', 64 'commands/split.c',
65 'commands/swaybg_command.c', 65 'commands/swaybg_command.c',
66 'commands/swap.c',
66 'commands/title_format.c', 67 'commands/title_format.c',
67 'commands/unmark.c', 68 'commands/unmark.c',
68 'commands/workspace.c', 69 'commands/workspace.c',
diff --git a/sway/sway.5.scd b/sway/sway.5.scd
index ff138562..5d99c9d6 100644
--- a/sway/sway.5.scd
+++ b/sway/sway.5.scd
@@ -167,6 +167,15 @@ They are expected to be used with *bindsym* or at runtime through *swaymsg*(1).
167 "Sticks" a floating window to the current output so that it shows up on all 167 "Sticks" a floating window to the current output so that it shows up on all
168 workspaces. 168 workspaces.
169 169
170*swap* container with id|con\_id|mark <arg>
171 Swaps the position, geometry, and fullscreen status of two containers. The
172 first container can be selected either by criteria or focus. The second
173 container can be selected by _id_, _con\_id_, or _mark_. _id_ can only be
174 used with xwayland views. If the first container has focus, it will retain
175 focus unless it is moved to a different workspace or the second container
176 becomes fullscreen on the same workspace as the first container. In either
177 of those cases, the second container will gain focus.
178
170The following commands may be used either in the configuration file or at 179The following commands may be used either in the configuration file or at
171runtime. 180runtime.
172 181
@@ -177,17 +186,20 @@ runtime.
177 186
178 for\_window <criteria> move container to workspace <workspace> 187 for\_window <criteria> move container to workspace <workspace>
179 188
180*bindsym* <key combo> <command> 189*bindsym* [--release|--locked] <key combo> <command>
181 Binds _key combo_ to execute the sway command _command_ when pressed. You 190 Binds _key combo_ to execute the sway command _command_ when pressed. You
182 may use XKB key names here (*xev*(1) is a good tool for discovering these). 191 may use XKB key names here (*xev*(1) is a good tool for discovering these).
192 With the flag _--release_, the command is executed when the key combo is
193 released. Unless the flag _--locked_ is set, the command will not be run
194 when a screen locking program is active.
183 195
184 Example: 196 Example:
185 197
186 # Execute firefox when alt, shift, and f are pressed together 198 # Execute firefox when alt, shift, and f are pressed together
187 bindsym Mod1+Shift+f exec firefox 199 bindsym Mod1+Shift+f exec firefox
188 200
189 *bindcode* <code> <command> is also available for binding with key codes 201 *bindcode* [--release|--locked] <code> <command> is also available for
190 instead of key names. 202 binding with key codes instead of key names.
191 203
192*client.<class>* <border> <background> <text> <indicator> <child\_border> 204*client.<class>* <border> <background> <text> <indicator> <child\_border>
193 Configures the color of window borders and title bars. All 5 colors are 205 Configures the color of window borders and title bars. All 5 colors are
@@ -551,6 +563,11 @@ The following attributes may be matched with:
551 value is \_\_focused\_\_, then the window instance must be the same as that 563 value is \_\_focused\_\_, then the window instance must be the same as that
552 of the currently focused window. 564 of the currently focused window.
553 565
566*shell*
567 Compare value against the window shell, such as "xdg\_shell" or "xwayland".
568 Can be a regular expression. If value is \_\_focused\_\_, then the shell
569 must be the same as that of the currently focused window.
570
554*tiling* 571*tiling*
555 Matches tiling windows. 572 Matches tiling windows.
556 573
diff --git a/sway/tree/container.c b/sway/tree/container.c
index f29a9adc..a4798c7e 100644
--- a/sway/tree/container.c
+++ b/sway/tree/container.c
@@ -176,45 +176,6 @@ static void _container_destroy(struct sway_container *cont) {
176 free(cont); 176 free(cont);
177} 177}
178 178
179static struct sway_container *container_output_destroy(
180 struct sway_container *output) {
181 if (!sway_assert(output, "cannot destroy null output")) {
182 return NULL;
183 }
184
185 if (output->children->length > 0) {
186 // TODO save workspaces when there are no outputs.
187 // TODO also check if there will ever be no outputs except for exiting
188 // program
189 if (root_container.children->length > 1) {
190 int p = root_container.children->items[0] == output;
191 // Move workspace from this output to another output
192 while (output->children->length) {
193 struct sway_container *child = output->children->items[0];
194 container_remove_child(child);
195 container_add_child(root_container.children->items[p], child);
196 }
197 container_sort_workspaces(root_container.children->items[p]);
198 arrange_output(root_container.children->items[p]);
199 }
200 }
201
202 wl_list_remove(&output->sway_output->destroy.link);
203 wl_list_remove(&output->sway_output->mode.link);
204 wl_list_remove(&output->sway_output->transform.link);
205 wl_list_remove(&output->sway_output->scale.link);
206
207 wl_list_remove(&output->sway_output->damage_destroy.link);
208 wl_list_remove(&output->sway_output->damage_frame.link);
209
210 // clear the wlr_output reference to this container
211 output->sway_output->wlr_output->data = NULL;
212
213 wlr_log(L_DEBUG, "OUTPUT: Destroying output '%s'", output->name);
214 _container_destroy(output);
215 return &root_container;
216}
217
218static struct sway_container *container_workspace_destroy( 179static struct sway_container *container_workspace_destroy(
219 struct sway_container *workspace) { 180 struct sway_container *workspace) {
220 if (!sway_assert(workspace, "cannot destroy null workspace")) { 181 if (!sway_assert(workspace, "cannot destroy null workspace")) {
@@ -232,7 +193,7 @@ static struct sway_container *container_workspace_destroy(
232 // destroy the WS if there are no children (TODO check for floating) 193 // destroy the WS if there are no children (TODO check for floating)
233 wlr_log(L_DEBUG, "destroying workspace '%s'", workspace->name); 194 wlr_log(L_DEBUG, "destroying workspace '%s'", workspace->name);
234 ipc_event_workspace(workspace, NULL, "empty"); 195 ipc_event_workspace(workspace, NULL, "empty");
235 } else { 196 } else if (output) {
236 // Move children to a different workspace on this output 197 // Move children to a different workspace on this output
237 struct sway_container *new_workspace = NULL; 198 struct sway_container *new_workspace = NULL;
238 // TODO move floating 199 // TODO move floating
@@ -253,11 +214,62 @@ static struct sway_container *container_workspace_destroy(
253 free(workspace->sway_workspace); 214 free(workspace->sway_workspace);
254 _container_destroy(workspace); 215 _container_destroy(workspace);
255 216
256 output_damage_whole(output->sway_output); 217 if (output) {
218 output_damage_whole(output->sway_output);
219 }
257 220
258 return parent; 221 return parent;
259} 222}
260 223
224static struct sway_container *container_output_destroy(
225 struct sway_container *output) {
226 if (!sway_assert(output, "cannot destroy null output")) {
227 return NULL;
228 }
229
230 if (output->children->length > 0) {
231 // TODO save workspaces when there are no outputs.
232 // TODO also check if there will ever be no outputs except for exiting
233 // program
234 if (root_container.children->length > 1) {
235 // Move workspace from this output to another output
236 struct sway_container *other_output =
237 root_container.children->items[0];
238 if (other_output == output) {
239 other_output = root_container.children->items[1];
240 }
241
242 while (output->children->length) {
243 struct sway_container *workspace = output->children->items[0];
244 container_remove_child(workspace);
245 if (workspace->children->length > 0) {
246 container_add_child(other_output, workspace);
247 ipc_event_workspace(workspace, NULL, "move");
248 } else {
249 container_workspace_destroy(workspace);
250 }
251 }
252 container_sort_workspaces(other_output);
253 arrange_output(other_output);
254 }
255 }
256
257 wl_list_remove(&output->sway_output->destroy.link);
258 wl_list_remove(&output->sway_output->mode.link);
259 wl_list_remove(&output->sway_output->transform.link);
260 wl_list_remove(&output->sway_output->scale.link);
261
262 wl_list_remove(&output->sway_output->damage_destroy.link);
263 wl_list_remove(&output->sway_output->damage_frame.link);
264
265 // clear the wlr_output reference to this container
266 output->sway_output->wlr_output->data = NULL;
267
268 wlr_log(L_DEBUG, "OUTPUT: Destroying output '%s'", output->name);
269 _container_destroy(output);
270 return &root_container;
271}
272
261static void container_root_finish(struct sway_container *con) { 273static void container_root_finish(struct sway_container *con) {
262 wlr_log(L_ERROR, "TODO: destroy the root container"); 274 wlr_log(L_ERROR, "TODO: destroy the root container");
263} 275}
diff --git a/sway/tree/layout.c b/sway/tree/layout.c
index 21cec529..624d5516 100644
--- a/sway/tree/layout.c
+++ b/sway/tree/layout.c
@@ -882,3 +882,127 @@ void container_recursive_resize(struct sway_container *container,
882 } 882 }
883 } 883 }
884} 884}
885
886static void swap_places(struct sway_container *con1,
887 struct sway_container *con2) {
888 struct sway_container *temp = malloc(sizeof(struct sway_container));
889 temp->x = con1->x;
890 temp->y = con1->y;
891 temp->width = con1->width;
892 temp->height = con1->height;
893 temp->parent = con1->parent;
894
895 con1->x = con2->x;
896 con1->y = con2->y;
897 con1->width = con2->width;
898 con1->height = con2->height;
899
900 con2->x = temp->x;
901 con2->y = temp->y;
902 con2->width = temp->width;
903 con2->height = temp->height;
904
905 int temp_index = index_child(con1);
906 container_insert_child(con2->parent, con1, index_child(con2));
907 container_insert_child(temp->parent, con2, temp_index);
908
909 free(temp);
910}
911
912static void swap_focus(struct sway_container *con1,
913 struct sway_container *con2, struct sway_seat *seat,
914 struct sway_container *focus) {
915 if (focus == con1 || focus == con2) {
916 struct sway_container *ws1 = container_parent(con1, C_WORKSPACE);
917 struct sway_container *ws2 = container_parent(con2, C_WORKSPACE);
918 if (focus == con1 && (con2->parent->layout == L_TABBED
919 || con2->parent->layout == L_STACKED)) {
920 if (workspace_is_visible(ws2)) {
921 seat_set_focus_warp(seat, con2, false);
922 }
923 seat_set_focus(seat, ws1 != ws2 ? con2 : con1);
924 } else if (focus == con2 && (con1->parent->layout == L_TABBED
925 || con1->parent->layout == L_STACKED)) {
926 if (workspace_is_visible(ws1)) {
927 seat_set_focus_warp(seat, con1, false);
928 }
929 seat_set_focus(seat, ws1 != ws2 ? con1 : con2);
930 } else if (ws1 != ws2) {
931 seat_set_focus(seat, focus == con1 ? con2 : con1);
932 } else {
933 seat_set_focus(seat, focus);
934 }
935 } else {
936 seat_set_focus(seat, focus);
937 }
938}
939
940void container_swap(struct sway_container *con1, struct sway_container *con2) {
941 if (!sway_assert(con1 && con2, "Cannot swap with nothing")) {
942 return;
943 }
944 if (!sway_assert(con1->type >= C_CONTAINER && con2->type >= C_CONTAINER,
945 "Can only swap containers and views")) {
946 return;
947 }
948 if (!sway_assert(!container_has_anscestor(con1, con2)
949 && !container_has_anscestor(con2, con1),
950 "Cannot swap anscestor and descendant")) {
951 return;
952 }
953 if (!sway_assert(con1->layout != L_FLOATING && con2->layout != L_FLOATING,
954 "Swapping with floating containers is not supported")) {
955 return;
956 }
957
958 wlr_log(L_DEBUG, "Swapping containers %zu and %zu", con1->id, con2->id);
959
960 int fs1 = con1->type == C_VIEW && con1->sway_view->is_fullscreen;
961 int fs2 = con2->type == C_VIEW && con2->sway_view->is_fullscreen;
962 if (fs1) {
963 view_set_fullscreen(con1->sway_view, false);
964 }
965 if (fs2) {
966 view_set_fullscreen(con2->sway_view, false);
967 }
968
969 struct sway_seat *seat = input_manager_get_default_seat(input_manager);
970 struct sway_container *focus = seat_get_focus(seat);
971 struct sway_container *vis1 = container_parent(
972 seat_get_focus_inactive(seat, container_parent(con1, C_OUTPUT)),
973 C_WORKSPACE);
974 struct sway_container *vis2 = container_parent(
975 seat_get_focus_inactive(seat, container_parent(con2, C_OUTPUT)),
976 C_WORKSPACE);
977
978 char *stored_prev_name = NULL;
979 if (prev_workspace_name) {
980 stored_prev_name = strdup(prev_workspace_name);
981 }
982
983 swap_places(con1, con2);
984
985 if (!workspace_is_visible(vis1)) {
986 seat_set_focus(seat, seat_get_focus_inactive(seat, vis1));
987 }
988 if (!workspace_is_visible(vis2)) {
989 seat_set_focus(seat, seat_get_focus_inactive(seat, vis2));
990 }
991
992 swap_focus(con1, con2, seat, focus);
993
994 if (stored_prev_name) {
995 free(prev_workspace_name);
996 prev_workspace_name = stored_prev_name;
997 }
998
999 arrange_children_of(con1->parent);
1000 arrange_children_of(con2->parent);
1001
1002 if (fs1 && con2->type == C_VIEW) {
1003 view_set_fullscreen(con2->sway_view, true);
1004 }
1005 if (fs2 && con1->type == C_VIEW) {
1006 view_set_fullscreen(con1->sway_view, true);
1007 }
1008}
diff --git a/sway/tree/view.c b/sway/tree/view.c
index 812d7740..d91182ed 100644
--- a/sway/tree/view.c
+++ b/sway/tree/view.c
@@ -107,7 +107,7 @@ uint32_t view_get_window_type(struct sway_view *view) {
107 return 0; 107 return 0;
108} 108}
109 109
110const char *view_get_type(struct sway_view *view) { 110const char *view_get_shell(struct sway_view *view) {
111 switch(view->type) { 111 switch(view->type) {
112 case SWAY_VIEW_XDG_SHELL_V6: 112 case SWAY_VIEW_XDG_SHELL_V6:
113 return "xdg_shell_v6"; 113 return "xdg_shell_v6";
@@ -654,10 +654,12 @@ static size_t parse_title_format(struct sway_view *view, char *buffer) {
654 return title ? strlen(title) : 0; 654 return title ? strlen(title) : 0;
655 } 655 }
656 const char *title = view_get_title(view); 656 const char *title = view_get_title(view);
657 const char *app_id = view_get_app_id(view);
657 const char *class = view_get_class(view); 658 const char *class = view_get_class(view);
658 const char *instance = view_get_instance(view); 659 const char *instance = view_get_instance(view);
659 const char *shell = view_get_type(view); 660 const char *shell = view_get_shell(view);
660 size_t title_len = title ? strlen(title) : 0; 661 size_t title_len = title ? strlen(title) : 0;
662 size_t app_id_len = app_id ? strlen(app_id) : 0;
661 size_t class_len = class ? strlen(class) : 0; 663 size_t class_len = class ? strlen(class) : 0;
662 size_t instance_len = instance ? strlen(instance) : 0; 664 size_t instance_len = instance ? strlen(instance) : 0;
663 size_t shell_len = shell ? strlen(shell) : 0; 665 size_t shell_len = shell ? strlen(shell) : 0;
@@ -675,6 +677,10 @@ static size_t parse_title_format(struct sway_view *view, char *buffer) {
675 lenient_strcat(buffer, title); 677 lenient_strcat(buffer, title);
676 len += title_len; 678 len += title_len;
677 format += 6; 679 format += 6;
680 } else if (strncmp(next, "%app_id", 7) == 0) {
681 lenient_strcat(buffer, app_id);
682 len += app_id_len;
683 format += 7;
678 } else if (strncmp(next, "%class", 6) == 0) { 684 } else if (strncmp(next, "%class", 6) == 0) {
679 lenient_strcat(buffer, class); 685 lenient_strcat(buffer, class);
680 len += class_len; 686 len += class_len;