aboutsummaryrefslogtreecommitdiffstats
path: root/sway/commands
diff options
context:
space:
mode:
Diffstat (limited to 'sway/commands')
-rw-r--r--sway/commands/assign.c3
-rw-r--r--sway/commands/bar.c10
-rw-r--r--sway/commands/bar/bind.c2
-rw-r--r--sway/commands/bar/colors.c2
-rw-r--r--sway/commands/bar/font.c16
-rw-r--r--sway/commands/bar/hidden_state.c3
-rw-r--r--sway/commands/bar/icon_theme.c1
-rw-r--r--sway/commands/bar/id.c1
-rw-r--r--sway/commands/bar/mode.c3
-rw-r--r--sway/commands/bar/output.c1
-rw-r--r--sway/commands/bar/position.c1
-rw-r--r--sway/commands/bar/separator_symbol.c1
-rw-r--r--sway/commands/bar/tray_bind.c2
-rw-r--r--sway/commands/bar/tray_output.c1
-rw-r--r--sway/commands/bind.c28
-rw-r--r--sway/commands/border.c8
-rw-r--r--sway/commands/client.c28
-rw-r--r--sway/commands/exec_always.c51
-rw-r--r--sway/commands/floating.c8
-rw-r--r--sway/commands/floating_minmax_size.c6
-rw-r--r--sway/commands/focus.c59
-rw-r--r--sway/commands/font.c30
-rw-r--r--sway/commands/for_window.c2
-rw-r--r--sway/commands/fullscreen.c34
-rw-r--r--sway/commands/gaps.c14
-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.c7
-rw-r--r--sway/commands/input/calibration_matrix.c1
-rw-r--r--sway/commands/input/dwtp.c25
-rw-r--r--sway/commands/input/events.c10
-rw-r--r--sway/commands/input/map_from_region.c19
-rw-r--r--sway/commands/input/map_to_output.c1
-rw-r--r--sway/commands/input/map_to_region.c4
-rw-r--r--sway/commands/input/rotation_angle.c29
-rw-r--r--sway/commands/input/scroll_button.c2
-rw-r--r--sway/commands/input/scroll_button_lock.c26
-rw-r--r--sway/commands/input/xkb_file.c1
-rw-r--r--sway/commands/input/xkb_layout.c1
-rw-r--r--sway/commands/input/xkb_model.c1
-rw-r--r--sway/commands/input/xkb_numlock.c1
-rw-r--r--sway/commands/input/xkb_options.c1
-rw-r--r--sway/commands/input/xkb_rules.c1
-rw-r--r--sway/commands/input/xkb_switch_layout.c37
-rw-r--r--sway/commands/input/xkb_variant.c1
-rw-r--r--sway/commands/layout.c14
-rw-r--r--sway/commands/mark.c3
-rw-r--r--sway/commands/mode.c6
-rw-r--r--sway/commands/move.c207
-rw-r--r--sway/commands/no_focus.c2
-rw-r--r--sway/commands/opacity.c3
-rw-r--r--sway/commands/output.c21
-rw-r--r--sway/commands/output/background.c16
-rw-r--r--sway/commands/output/dpms.c22
-rw-r--r--sway/commands/output/mode.c58
-rw-r--r--sway/commands/output/power.c43
-rw-r--r--sway/commands/output/render_bit_depth.c29
-rw-r--r--sway/commands/output/toggle.c2
-rw-r--r--sway/commands/output/transform.c1
-rw-r--r--sway/commands/output/unplug.c54
-rw-r--r--sway/commands/primary_selection.c25
-rw-r--r--sway/commands/reload.c9
-rw-r--r--sway/commands/rename.c7
-rw-r--r--sway/commands/resize.c158
-rw-r--r--sway/commands/scratchpad.c21
-rw-r--r--sway/commands/seat.c4
-rw-r--r--sway/commands/seat/attach.c3
-rw-r--r--sway/commands/seat/cursor.c29
-rw-r--r--sway/commands/seat/hide_cursor.c1
-rw-r--r--sway/commands/seat/idle.c7
-rw-r--r--sway/commands/seat/xcursor_theme.c1
-rw-r--r--sway/commands/set.c1
-rw-r--r--sway/commands/show_marks.c12
-rw-r--r--sway/commands/smart_gaps.c7
-rw-r--r--sway/commands/split.c23
-rw-r--r--sway/commands/sticky.c6
-rw-r--r--sway/commands/swap.c187
-rw-r--r--sway/commands/title_align.c9
-rw-r--r--sway/commands/title_format.c2
-rw-r--r--sway/commands/titlebar_border_thickness.c1
-rw-r--r--sway/commands/titlebar_padding.c1
-rw-r--r--sway/commands/unmark.c14
-rw-r--r--sway/commands/workspace.c32
-rw-r--r--sway/commands/xwayland.c4
85 files changed, 1065 insertions, 634 deletions
diff --git a/sway/commands/assign.c b/sway/commands/assign.c
index 976bc3cc..bf95cf00 100644
--- a/sway/commands/assign.c
+++ b/sway/commands/assign.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <stdio.h> 1#include <stdio.h>
3#include <string.h> 2#include <string.h>
4#include "sway/commands.h" 3#include "sway/commands.h"
@@ -17,7 +16,7 @@ struct cmd_results *cmd_assign(int argc, char **argv) {
17 char *err_str = NULL; 16 char *err_str = NULL;
18 struct criteria *criteria = criteria_parse(argv[0], &err_str); 17 struct criteria *criteria = criteria_parse(argv[0], &err_str);
19 if (!criteria) { 18 if (!criteria) {
20 error = cmd_results_new(CMD_INVALID, err_str); 19 error = cmd_results_new(CMD_INVALID, "%s", err_str);
21 free(err_str); 20 free(err_str);
22 return error; 21 return error;
23 } 22 }
diff --git a/sway/commands/bar.c b/sway/commands/bar.c
index d42b7fc2..635e895b 100644
--- a/sway/commands/bar.c
+++ b/sway/commands/bar.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809
2#include <stdio.h> 1#include <stdio.h>
3#include <string.h> 2#include <string.h>
4#include <strings.h> 3#include <strings.h>
@@ -8,7 +7,7 @@
8#include "log.h" 7#include "log.h"
9 8
10// Must be in alphabetical order for bsearch 9// Must be in alphabetical order for bsearch
11static struct cmd_handler bar_handlers[] = { 10static const struct cmd_handler bar_handlers[] = {
12 { "bindcode", bar_cmd_bindcode }, 11 { "bindcode", bar_cmd_bindcode },
13 { "binding_mode_indicator", bar_cmd_binding_mode_indicator }, 12 { "binding_mode_indicator", bar_cmd_binding_mode_indicator },
14 { "bindsym", bar_cmd_bindsym }, 13 { "bindsym", bar_cmd_bindsym },
@@ -41,7 +40,7 @@ static struct cmd_handler bar_handlers[] = {
41}; 40};
42 41
43// Must be in alphabetical order for bsearch 42// Must be in alphabetical order for bsearch
44static struct cmd_handler bar_config_handlers[] = { 43static const struct cmd_handler bar_config_handlers[] = {
45 { "id", bar_cmd_id }, 44 { "id", bar_cmd_id },
46 { "swaybar_command", bar_cmd_swaybar_command }, 45 { "swaybar_command", bar_cmd_swaybar_command },
47}; 46};
@@ -73,12 +72,10 @@ struct cmd_results *cmd_bar(int argc, char **argv) {
73 } 72 }
74 ++argv; --argc; 73 ++argv; --argc;
75 } else if (config->reading && !config->current_bar) { 74 } else if (config->reading && !config->current_bar) {
76 int len = snprintf(NULL, 0, "bar-%d", config->bars->length) + 1; 75 id = format_str("bar-%d", config->bars->length);
77 id = malloc(len * sizeof(char));
78 if (!id) { 76 if (!id) {
79 return cmd_results_new(CMD_FAILURE, "Unable to allocate bar id"); 77 return cmd_results_new(CMD_FAILURE, "Unable to allocate bar id");
80 } 78 }
81 snprintf(id, len, "bar-%d", config->bars->length);
82 } else if (!config->reading && strcmp(argv[0], "mode") != 0 && 79 } else if (!config->reading && strcmp(argv[0], "mode") != 0 &&
83 strcmp(argv[0], "hidden_state") != 0) { 80 strcmp(argv[0], "hidden_state") != 0) {
84 if (is_subcommand(argv[0])) { 81 if (is_subcommand(argv[0])) {
@@ -116,6 +113,7 @@ struct cmd_results *cmd_bar(int argc, char **argv) {
116 if (res && res->status != CMD_SUCCESS) { 113 if (res && res->status != CMD_SUCCESS) {
117 if (id) { 114 if (id) {
118 free_bar_config(config->current_bar); 115 free_bar_config(config->current_bar);
116 config->current_bar = NULL;
119 id = NULL; 117 id = NULL;
120 } 118 }
121 return res; 119 return res;
diff --git a/sway/commands/bar/bind.c b/sway/commands/bar/bind.c
index b4b5bc45..8a837e3f 100644
--- a/sway/commands/bar/bind.c
+++ b/sway/commands/bar/bind.c
@@ -96,7 +96,7 @@ static struct cmd_results *bar_cmd_bind(int argc, char **argv, bool code,
96 } 96 }
97 if (message) { 97 if (message) {
98 free_bar_binding(binding); 98 free_bar_binding(binding);
99 error = cmd_results_new(CMD_INVALID, message); 99 error = cmd_results_new(CMD_INVALID, "%s", message);
100 free(message); 100 free(message);
101 return error; 101 return error;
102 } else if (!binding->button) { 102 } else if (!binding->button) {
diff --git a/sway/commands/bar/colors.c b/sway/commands/bar/colors.c
index 2d5b22bf..275fa3c6 100644
--- a/sway/commands/bar/colors.c
+++ b/sway/commands/bar/colors.c
@@ -4,7 +4,7 @@
4#include "util.h" 4#include "util.h"
5 5
6// Must be in alphabetical order for bsearch 6// Must be in alphabetical order for bsearch
7static struct cmd_handler bar_colors_handlers[] = { 7static const struct cmd_handler bar_colors_handlers[] = {
8 { "active_workspace", bar_colors_cmd_active_workspace }, 8 { "active_workspace", bar_colors_cmd_active_workspace },
9 { "background", bar_colors_cmd_background }, 9 { "background", bar_colors_cmd_background },
10 { "binding_mode", bar_colors_cmd_binding_mode }, 10 { "binding_mode", bar_colors_cmd_binding_mode },
diff --git a/sway/commands/bar/font.c b/sway/commands/bar/font.c
index 62987f3e..0c074679 100644
--- a/sway/commands/bar/font.c
+++ b/sway/commands/bar/font.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <string.h> 1#include <string.h>
3#include "sway/commands.h" 2#include "sway/commands.h"
4#include "log.h" 3#include "log.h"
@@ -11,7 +10,20 @@ struct cmd_results *bar_cmd_font(int argc, char **argv) {
11 } 10 }
12 char *font = join_args(argv, argc); 11 char *font = join_args(argv, argc);
13 free(config->current_bar->font); 12 free(config->current_bar->font);
14 config->current_bar->font = font; 13
14 if (strncmp(font, "pango:", 6) == 0) {
15 if (config->current_bar->pango_markup == PANGO_MARKUP_DEFAULT) {
16 config->current_bar->pango_markup = true;
17 }
18 config->current_bar->font = strdup(font + 6);
19 } else {
20 if (config->current_bar->pango_markup == PANGO_MARKUP_DEFAULT) {
21 config->current_bar->pango_markup = false;
22 }
23 config->current_bar->font = strdup(font);
24 }
25
26 free(font);
15 sway_log(SWAY_DEBUG, "Settings font '%s' for bar: %s", 27 sway_log(SWAY_DEBUG, "Settings font '%s' for bar: %s",
16 config->current_bar->font, config->current_bar->id); 28 config->current_bar->font, config->current_bar->id);
17 return cmd_results_new(CMD_SUCCESS, NULL); 29 return cmd_results_new(CMD_SUCCESS, NULL);
diff --git a/sway/commands/bar/hidden_state.c b/sway/commands/bar/hidden_state.c
index 1f08a5d2..7b38831e 100644
--- a/sway/commands/bar/hidden_state.c
+++ b/sway/commands/bar/hidden_state.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <string.h> 1#include <string.h>
3#include <strings.h> 2#include <strings.h>
4#include "sway/commands.h" 3#include "sway/commands.h"
@@ -54,7 +53,7 @@ struct cmd_results *bar_cmd_hidden_state(int argc, char **argv) {
54 } 53 }
55 54
56 const char *state = argv[0]; 55 const char *state = argv[0];
57 if (config->reading) { 56 if (config->current_bar) {
58 error = bar_set_hidden_state(config->current_bar, state); 57 error = bar_set_hidden_state(config->current_bar, state);
59 } else { 58 } else {
60 const char *id = argc == 2 ? argv[1] : NULL; 59 const char *id = argc == 2 ? argv[1] : NULL;
diff --git a/sway/commands/bar/icon_theme.c b/sway/commands/bar/icon_theme.c
index 6ac07843..fee21709 100644
--- a/sway/commands/bar/icon_theme.c
+++ b/sway/commands/bar/icon_theme.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <string.h> 1#include <string.h>
3#include "config.h" 2#include "config.h"
4#include "sway/commands.h" 3#include "sway/commands.h"
diff --git a/sway/commands/bar/id.c b/sway/commands/bar/id.c
index a9a61743..46cf4ca9 100644
--- a/sway/commands/bar/id.c
+++ b/sway/commands/bar/id.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <string.h> 1#include <string.h>
3#include "sway/commands.h" 2#include "sway/commands.h"
4#include "log.h" 3#include "log.h"
diff --git a/sway/commands/bar/mode.c b/sway/commands/bar/mode.c
index 8b3fb275..d69e910b 100644
--- a/sway/commands/bar/mode.c
+++ b/sway/commands/bar/mode.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <string.h> 1#include <string.h>
3#include <strings.h> 2#include <strings.h>
4#include "sway/commands.h" 3#include "sway/commands.h"
@@ -58,7 +57,7 @@ struct cmd_results *bar_cmd_mode(int argc, char **argv) {
58 } 57 }
59 58
60 const char *mode = argv[0]; 59 const char *mode = argv[0];
61 if (config->reading) { 60 if (config->current_bar) {
62 error = bar_set_mode(config->current_bar, mode); 61 error = bar_set_mode(config->current_bar, mode);
63 } else { 62 } else {
64 const char *id = argc == 2 ? argv[1] : NULL; 63 const char *id = argc == 2 ? argv[1] : NULL;
diff --git a/sway/commands/bar/output.c b/sway/commands/bar/output.c
index cac1d056..51730176 100644
--- a/sway/commands/bar/output.c
+++ b/sway/commands/bar/output.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <stdbool.h> 1#include <stdbool.h>
3#include <string.h> 2#include <string.h>
4#include "sway/commands.h" 3#include "sway/commands.h"
diff --git a/sway/commands/bar/position.c b/sway/commands/bar/position.c
index b207de0b..94f530ec 100644
--- a/sway/commands/bar/position.c
+++ b/sway/commands/bar/position.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <string.h> 1#include <string.h>
3#include <strings.h> 2#include <strings.h>
4#include "sway/commands.h" 3#include "sway/commands.h"
diff --git a/sway/commands/bar/separator_symbol.c b/sway/commands/bar/separator_symbol.c
index 6737d4d2..50e9a873 100644
--- a/sway/commands/bar/separator_symbol.c
+++ b/sway/commands/bar/separator_symbol.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <string.h> 1#include <string.h>
3#include "sway/commands.h" 2#include "sway/commands.h"
4#include "log.h" 3#include "log.h"
diff --git a/sway/commands/bar/tray_bind.c b/sway/commands/bar/tray_bind.c
index 243834ba..3dc9bc4c 100644
--- a/sway/commands/bar/tray_bind.c
+++ b/sway/commands/bar/tray_bind.c
@@ -26,7 +26,7 @@ static struct cmd_results *tray_bind(int argc, char **argv, bool code) {
26 } 26 }
27 if (message) { 27 if (message) {
28 free(binding); 28 free(binding);
29 error = cmd_results_new(CMD_INVALID, message); 29 error = cmd_results_new(CMD_INVALID, "%s", message);
30 free(message); 30 free(message);
31 return error; 31 return error;
32 } else if (!binding->button) { 32 } else if (!binding->button) {
diff --git a/sway/commands/bar/tray_output.c b/sway/commands/bar/tray_output.c
index eb3b486e..679facf7 100644
--- a/sway/commands/bar/tray_output.c
+++ b/sway/commands/bar/tray_output.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <string.h> 1#include <string.h>
3#include "config.h" 2#include "config.h"
4#include "sway/commands.h" 3#include "sway/commands.h"
diff --git a/sway/commands/bind.c b/sway/commands/bind.c
index f6e58d99..268f2855 100644
--- a/sway/commands/bind.c
+++ b/sway/commands/bind.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <libevdev/libevdev.h> 1#include <libevdev/libevdev.h>
3#include <linux/input-event-codes.h> 2#include <linux/input-event-codes.h>
4#include <string.h> 3#include <string.h>
@@ -8,6 +7,7 @@
8#include <wlr/types/wlr_cursor.h> 7#include <wlr/types/wlr_cursor.h>
9#include "sway/commands.h" 8#include "sway/commands.h"
10#include "sway/config.h" 9#include "sway/config.h"
10#include "sway/desktop/transaction.h"
11#include "sway/input/cursor.h" 11#include "sway/input/cursor.h"
12#include "sway/input/keyboard.h" 12#include "sway/input/keyboard.h"
13#include "sway/ipc-server.h" 13#include "sway/ipc-server.h"
@@ -46,7 +46,7 @@ static bool binding_switch_compare(struct sway_switch_binding *binding_a,
46 if (binding_a->type != binding_b->type) { 46 if (binding_a->type != binding_b->type) {
47 return false; 47 return false;
48 } 48 }
49 if (binding_a->state != binding_b->state) { 49 if (binding_a->trigger != binding_b->trigger) {
50 return false; 50 return false;
51 } 51 }
52 if ((binding_a->flags & BINDING_LOCKED) != 52 if ((binding_a->flags & BINDING_LOCKED) !=
@@ -126,7 +126,7 @@ static struct cmd_results *identify_key(const char* name, bool first_key,
126 if (!button) { 126 if (!button) {
127 if (message) { 127 if (message) {
128 struct cmd_results *error = 128 struct cmd_results *error =
129 cmd_results_new(CMD_INVALID, message); 129 cmd_results_new(CMD_INVALID, "%s", message);
130 free(message); 130 free(message);
131 return error; 131 return error;
132 } else { 132 } else {
@@ -142,7 +142,7 @@ static struct cmd_results *identify_key(const char* name, bool first_key,
142 if (!button) { 142 if (!button) {
143 if (message) { 143 if (message) {
144 struct cmd_results *error = 144 struct cmd_results *error =
145 cmd_results_new(CMD_INVALID, message); 145 cmd_results_new(CMD_INVALID, "%s", message);
146 free(message); 146 free(message);
147 return error; 147 return error;
148 } else { 148 } else {
@@ -181,7 +181,7 @@ static struct cmd_results *identify_key(const char* name, bool first_key,
181 uint32_t button = get_mouse_bindsym(name, &message); 181 uint32_t button = get_mouse_bindsym(name, &message);
182 if (message) { 182 if (message) {
183 struct cmd_results *error = 183 struct cmd_results *error =
184 cmd_results_new(CMD_INVALID, message); 184 cmd_results_new(CMD_INVALID, "%s", message);
185 free(message); 185 free(message);
186 return error; 186 return error;
187 } else if (button) { 187 } else if (button) {
@@ -371,6 +371,7 @@ static struct cmd_results *cmd_bindsym_or_bindcode(int argc, char **argv,
371 strlen("--input-device=")) == 0) { 371 strlen("--input-device=")) == 0) {
372 free(binding->input); 372 free(binding->input);
373 binding->input = strdup(argv[0] + strlen("--input-device=")); 373 binding->input = strdup(argv[0] + strlen("--input-device="));
374 strip_quotes(binding->input);
374 } else if (strcmp("--no-warn", argv[0]) == 0) { 375 } else if (strcmp("--no-warn", argv[0]) == 0) {
375 warn = false; 376 warn = false;
376 } else if (strcmp("--no-repeat", argv[0]) == 0) { 377 } else if (strcmp("--no-repeat", argv[0]) == 0) {
@@ -537,7 +538,7 @@ struct cmd_results *cmd_bind_or_unbind_switch(int argc, char **argv,
537 free_switch_binding(binding); 538 free_switch_binding(binding);
538 return cmd_results_new(CMD_FAILURE, 539 return cmd_results_new(CMD_FAILURE,
539 "Invalid %s command (expected binding with the form " 540 "Invalid %s command (expected binding with the form "
540 "<switch>:<state>)", bindtype, argc); 541 "<switch>:<state>)", bindtype);
541 } 542 }
542 if (strcmp(split->items[0], "tablet") == 0) { 543 if (strcmp(split->items[0], "tablet") == 0) {
543 binding->type = WLR_SWITCH_TYPE_TABLET_MODE; 544 binding->type = WLR_SWITCH_TYPE_TABLET_MODE;
@@ -547,20 +548,21 @@ struct cmd_results *cmd_bind_or_unbind_switch(int argc, char **argv,
547 free_switch_binding(binding); 548 free_switch_binding(binding);
548 return cmd_results_new(CMD_FAILURE, 549 return cmd_results_new(CMD_FAILURE,
549 "Invalid %s command (expected switch binding: " 550 "Invalid %s command (expected switch binding: "
550 "unknown switch %s)", bindtype, split->items[0]); 551 "unknown switch %s)", bindtype,
552 (const char *)split->items[0]);
551 } 553 }
552 if (strcmp(split->items[1], "on") == 0) { 554 if (strcmp(split->items[1], "on") == 0) {
553 binding->state = WLR_SWITCH_STATE_ON; 555 binding->trigger = SWAY_SWITCH_TRIGGER_ON;
554 } else if (strcmp(split->items[1], "off") == 0) { 556 } else if (strcmp(split->items[1], "off") == 0) {
555 binding->state = WLR_SWITCH_STATE_OFF; 557 binding->trigger = SWAY_SWITCH_TRIGGER_OFF;
556 } else if (strcmp(split->items[1], "toggle") == 0) { 558 } else if (strcmp(split->items[1], "toggle") == 0) {
557 binding->state = WLR_SWITCH_STATE_TOGGLE; 559 binding->trigger = SWAY_SWITCH_TRIGGER_TOGGLE;
558 } else { 560 } else {
559 free_switch_binding(binding); 561 free_switch_binding(binding);
560 return cmd_results_new(CMD_FAILURE, 562 return cmd_results_new(CMD_FAILURE,
561 "Invalid %s command " 563 "Invalid %s command "
562 "(expected switch state: unknown state %d)", 564 "(expected switch state: unknown state %s)",
563 bindtype, split->items[0]); 565 bindtype, (const char *)split->items[1]);
564 } 566 }
565 list_free_items_and_destroy(split); 567 list_free_items_and_destroy(split);
566 568
@@ -642,6 +644,8 @@ void seat_execute_command(struct sway_seat *seat, struct sway_binding *binding)
642 if (success) { 644 if (success) {
643 ipc_event_binding(binding); 645 ipc_event_binding(binding);
644 } 646 }
647
648 transaction_commit_dirty();
645} 649}
646 650
647/** 651/**
diff --git a/sway/commands/border.c b/sway/commands/border.c
index 647663ac..7818fc96 100644
--- a/sway/commands/border.c
+++ b/sway/commands/border.c
@@ -19,11 +19,11 @@ static void set_border(struct sway_container *con,
19 view_set_csd_from_server(con->view, false); 19 view_set_csd_from_server(con->view, false);
20 } else if (!con->view->using_csd && new_border == B_CSD) { 20 } else if (!con->view->using_csd && new_border == B_CSD) {
21 view_set_csd_from_server(con->view, true); 21 view_set_csd_from_server(con->view, true);
22 con->saved_border = con->border; 22 con->saved_border = con->pending.border;
23 } 23 }
24 } 24 }
25 if (new_border != B_CSD || container_is_floating(con)) { 25 if (new_border != B_CSD || container_is_floating(con)) {
26 con->border = new_border; 26 con->pending.border = new_border;
27 } 27 }
28 if (con->view) { 28 if (con->view) {
29 con->view->using_csd = new_border == B_CSD; 29 con->view->using_csd = new_border == B_CSD;
@@ -35,7 +35,7 @@ static void border_toggle(struct sway_container *con) {
35 set_border(con, B_NONE); 35 set_border(con, B_NONE);
36 return; 36 return;
37 } 37 }
38 switch (con->border) { 38 switch (con->pending.border) {
39 case B_NONE: 39 case B_NONE:
40 set_border(con, B_PIXEL); 40 set_border(con, B_PIXEL);
41 break; 41 break;
@@ -88,7 +88,7 @@ struct cmd_results *cmd_border(int argc, char **argv) {
88 "or 'border pixel <px>'"); 88 "or 'border pixel <px>'");
89 } 89 }
90 if (argc == 2) { 90 if (argc == 2) {
91 container->border_thickness = atoi(argv[1]); 91 container->pending.border_thickness = atoi(argv[1]);
92 } 92 }
93 93
94 if (container_is_floating(container)) { 94 if (container_is_floating(container)) {
diff --git a/sway/commands/client.c b/sway/commands/client.c
index dd0694df..fd2ac7a8 100644
--- a/sway/commands/client.c
+++ b/sway/commands/client.c
@@ -5,9 +5,8 @@
5#include "sway/tree/container.h" 5#include "sway/tree/container.h"
6#include "util.h" 6#include "util.h"
7 7
8static void rebuild_textures_iterator(struct sway_container *con, void *data) { 8static void container_update_iterator(struct sway_container *con, void *data) {
9 container_update_marks_textures(con); 9 container_update(con);
10 container_update_title_textures(con);
11} 10}
12 11
13static struct cmd_results *handle_command(int argc, char **argv, char *cmd_name, 12static struct cmd_results *handle_command(int argc, char **argv, char *cmd_name,
@@ -18,6 +17,12 @@ static struct cmd_results *handle_command(int argc, char **argv, char *cmd_name,
18 return error; 17 return error;
19 } 18 }
20 19
20 if (argc > 3 && strcmp(cmd_name, "client.focused_tab_title") == 0) {
21 sway_log(SWAY_ERROR,
22 "Warning: indicator and child_border colors have no effect for %s",
23 cmd_name);
24 }
25
21 struct border_colors colors = {0}; 26 struct border_colors colors = {0};
22 const char *ind_hex = argc > 3 ? argv[3] : default_indicator; 27 const char *ind_hex = argc > 3 ? argv[3] : default_indicator;
23 const char *child_hex = argc > 4 ? argv[4] : argv[1]; // def to background 28 const char *child_hex = argc > 4 ? argv[4] : argv[1]; // def to background
@@ -45,12 +50,7 @@ static struct cmd_results *handle_command(int argc, char **argv, char *cmd_name,
45 memcpy(class, &colors, sizeof(struct border_colors)); 50 memcpy(class, &colors, sizeof(struct border_colors));
46 51
47 if (config->active) { 52 if (config->active) {
48 root_for_each_container(rebuild_textures_iterator, NULL); 53 root_for_each_container(container_update_iterator, NULL);
49
50 for (int i = 0; i < root->outputs->length; ++i) {
51 struct sway_output *output = root->outputs->items[i];
52 output_damage_whole(output);
53 }
54 } 54 }
55 55
56 return cmd_results_new(CMD_SUCCESS, NULL); 56 return cmd_results_new(CMD_SUCCESS, NULL);
@@ -80,3 +80,13 @@ struct cmd_results *cmd_client_noop(int argc, char **argv) {
80 sway_log(SWAY_INFO, "Warning: %s is ignored by sway", argv[-1]); 80 sway_log(SWAY_INFO, "Warning: %s is ignored by sway", argv[-1]);
81 return cmd_results_new(CMD_SUCCESS, NULL); 81 return cmd_results_new(CMD_SUCCESS, NULL);
82} 82}
83
84struct cmd_results *cmd_client_focused_tab_title(int argc, char **argv) {
85 struct cmd_results *result = handle_command(argc, argv,
86 "client.focused_tab_title",
87 &config->border_colors.focused_tab_title, "#2e9ef4ff");
88 if (result && result->status == CMD_SUCCESS) {
89 config->has_focused_tab_title = true;
90 }
91 return result;
92}
diff --git a/sway/commands/exec_always.c b/sway/commands/exec_always.c
index 39e48a44..8bc1048c 100644
--- a/sway/commands/exec_always.c
+++ b/sway/commands/exec_always.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <stdlib.h> 1#include <stdlib.h>
3#include <stdint.h> 2#include <stdint.h>
4#include <string.h> 3#include <string.h>
@@ -7,6 +6,8 @@
7#include <signal.h> 6#include <signal.h>
8#include "sway/commands.h" 7#include "sway/commands.h"
9#include "sway/config.h" 8#include "sway/config.h"
9#include "sway/server.h"
10#include "sway/desktop/launcher.h"
10#include "sway/tree/container.h" 11#include "sway/tree/container.h"
11#include "sway/tree/root.h" 12#include "sway/tree/root.h"
12#include "sway/tree/workspace.h" 13#include "sway/tree/workspace.h"
@@ -24,11 +25,22 @@ struct cmd_results *cmd_exec_validate(int argc, char **argv) {
24 return error; 25 return error;
25} 26}
26 27
28static void export_xdga_token(struct launcher_ctx *ctx) {
29 const char *token = launcher_ctx_get_token_name(ctx);
30 setenv("XDG_ACTIVATION_TOKEN", token, 1);
31}
32
33static void export_startup_id(struct launcher_ctx *ctx) {
34 const char *token = launcher_ctx_get_token_name(ctx);
35 setenv("DESKTOP_STARTUP_ID", token, 1);
36}
37
27struct cmd_results *cmd_exec_process(int argc, char **argv) { 38struct cmd_results *cmd_exec_process(int argc, char **argv) {
28 struct cmd_results *error = NULL; 39 struct cmd_results *error = NULL;
29 char *tmp = NULL; 40 char *cmd = NULL;
41 bool no_startup_id = false;
30 if (strcmp(argv[0], "--no-startup-id") == 0) { 42 if (strcmp(argv[0], "--no-startup-id") == 0) {
31 sway_log(SWAY_INFO, "exec switch '--no-startup-id' not supported, ignored."); 43 no_startup_id = true;
32 --argc; ++argv; 44 --argc; ++argv;
33 if ((error = checkarg(argc, argv[-1], EXPECTED_AT_LEAST, 1))) { 45 if ((error = checkarg(argc, argv[-1], EXPECTED_AT_LEAST, 1))) {
34 return error; 46 return error;
@@ -36,17 +48,12 @@ struct cmd_results *cmd_exec_process(int argc, char **argv) {
36 } 48 }
37 49
38 if (argc == 1 && (argv[0][0] == '\'' || argv[0][0] == '"')) { 50 if (argc == 1 && (argv[0][0] == '\'' || argv[0][0] == '"')) {
39 tmp = strdup(argv[0]); 51 cmd = strdup(argv[0]);
40 strip_quotes(tmp); 52 strip_quotes(cmd);
41 } else { 53 } else {
42 tmp = join_args(argv, argc); 54 cmd = join_args(argv, argc);
43 } 55 }
44 56
45 // Put argument into cmd array
46 char cmd[4096];
47 strncpy(cmd, tmp, sizeof(cmd) - 1);
48 cmd[sizeof(cmd) - 1] = 0;
49 free(tmp);
50 sway_log(SWAY_DEBUG, "Executing %s", cmd); 57 sway_log(SWAY_DEBUG, "Executing %s", cmd);
51 58
52 int fd[2]; 59 int fd[2];
@@ -55,18 +62,28 @@ struct cmd_results *cmd_exec_process(int argc, char **argv) {
55 } 62 }
56 63
57 pid_t pid, child; 64 pid_t pid, child;
65 struct launcher_ctx *ctx = launcher_ctx_create_internal();
58 // Fork process 66 // Fork process
59 if ((pid = fork()) == 0) { 67 if ((pid = fork()) == 0) {
60 // Fork child process again 68 // Fork child process again
69 restore_nofile_limit();
61 setsid(); 70 setsid();
62 sigset_t set; 71 sigset_t set;
63 sigemptyset(&set); 72 sigemptyset(&set);
64 sigprocmask(SIG_SETMASK, &set, NULL); 73 sigprocmask(SIG_SETMASK, &set, NULL);
74 signal(SIGPIPE, SIG_DFL);
65 close(fd[0]); 75 close(fd[0]);
66 if ((child = fork()) == 0) { 76 if ((child = fork()) == 0) {
67 close(fd[1]); 77 close(fd[1]);
68 execl("/bin/sh", "/bin/sh", "-c", cmd, (void *)NULL); 78 if (ctx) {
69 _exit(0); 79 export_xdga_token(ctx);
80 }
81 if (ctx && !no_startup_id) {
82 export_startup_id(ctx);
83 }
84 execlp("sh", "sh", "-c", cmd, (void *)NULL);
85 sway_log_errno(SWAY_ERROR, "execlp failed");
86 _exit(1);
70 } 87 }
71 ssize_t s = 0; 88 ssize_t s = 0;
72 while ((size_t)s < sizeof(pid_t)) { 89 while ((size_t)s < sizeof(pid_t)) {
@@ -75,10 +92,12 @@ struct cmd_results *cmd_exec_process(int argc, char **argv) {
75 close(fd[1]); 92 close(fd[1]);
76 _exit(0); // Close child process 93 _exit(0); // Close child process
77 } else if (pid < 0) { 94 } else if (pid < 0) {
95 free(cmd);
78 close(fd[0]); 96 close(fd[0]);
79 close(fd[1]); 97 close(fd[1]);
80 return cmd_results_new(CMD_FAILURE, "fork() failed"); 98 return cmd_results_new(CMD_FAILURE, "fork() failed");
81 } 99 }
100 free(cmd);
82 close(fd[1]); // close write 101 close(fd[1]); // close write
83 ssize_t s = 0; 102 ssize_t s = 0;
84 while ((size_t)s < sizeof(pid_t)) { 103 while ((size_t)s < sizeof(pid_t)) {
@@ -89,8 +108,12 @@ struct cmd_results *cmd_exec_process(int argc, char **argv) {
89 waitpid(pid, NULL, 0); 108 waitpid(pid, NULL, 0);
90 if (child > 0) { 109 if (child > 0) {
91 sway_log(SWAY_DEBUG, "Child process created with pid %d", child); 110 sway_log(SWAY_DEBUG, "Child process created with pid %d", child);
92 root_record_workspace_pid(child); 111 if (ctx != NULL) {
112 sway_log(SWAY_DEBUG, "Recording workspace for process %d", child);
113 ctx->pid = child;
114 }
93 } else { 115 } else {
116 launcher_ctx_destroy(ctx);
94 return cmd_results_new(CMD_FAILURE, "Second fork() failed"); 117 return cmd_results_new(CMD_FAILURE, "Second fork() failed");
95 } 118 }
96 119
diff --git a/sway/commands/floating.c b/sway/commands/floating.c
index ce123345..74f6522c 100644
--- a/sway/commands/floating.c
+++ b/sway/commands/floating.c
@@ -40,8 +40,8 @@ struct cmd_results *cmd_floating(int argc, char **argv) {
40 // If the container is in a floating split container, 40 // If the container is in a floating split container,
41 // operate on the split container instead of the child. 41 // operate on the split container instead of the child.
42 if (container_is_floating_or_child(container)) { 42 if (container_is_floating_or_child(container)) {
43 while (container->parent) { 43 while (container->pending.parent) {
44 container = container->parent; 44 container = container->pending.parent;
45 } 45 }
46 } 46 }
47 47
@@ -51,8 +51,8 @@ struct cmd_results *cmd_floating(int argc, char **argv) {
51 container_set_floating(container, wants_floating); 51 container_set_floating(container, wants_floating);
52 52
53 // Floating containers in the scratchpad should be ignored 53 // Floating containers in the scratchpad should be ignored
54 if (container->workspace) { 54 if (container->pending.workspace) {
55 arrange_workspace(container->workspace); 55 arrange_workspace(container->pending.workspace);
56 } 56 }
57 57
58 return cmd_results_new(CMD_SUCCESS, NULL); 58 return cmd_results_new(CMD_SUCCESS, NULL);
diff --git a/sway/commands/floating_minmax_size.c b/sway/commands/floating_minmax_size.c
index 3a1d606a..e8c24ace 100644
--- a/sway/commands/floating_minmax_size.c
+++ b/sway/commands/floating_minmax_size.c
@@ -23,16 +23,16 @@ static struct cmd_results *handle_command(int argc, char **argv, char *cmd_name,
23 char *err; 23 char *err;
24 int width = (int)strtol(argv[0], &err, 10); 24 int width = (int)strtol(argv[0], &err, 10);
25 if (*err) { 25 if (*err) {
26 return cmd_results_new(CMD_INVALID, cmd_name, usage); 26 return cmd_results_new(CMD_INVALID, "%s", usage);
27 } 27 }
28 28
29 if (strcmp(argv[1], "x") != 0) { 29 if (strcmp(argv[1], "x") != 0) {
30 return cmd_results_new(CMD_INVALID, cmd_name, usage); 30 return cmd_results_new(CMD_INVALID, "%s", usage);
31 } 31 }
32 32
33 int height = (int)strtol(argv[2], &err, 10); 33 int height = (int)strtol(argv[2], &err, 10);
34 if (*err) { 34 if (*err) {
35 return cmd_results_new(CMD_INVALID, cmd_name, usage); 35 return cmd_results_new(CMD_INVALID, "%s", usage);
36 } 36 }
37 37
38 *config_width = width; 38 *config_width = width;
diff --git a/sway/commands/focus.c b/sway/commands/focus.c
index 79b7aed5..facd82de 100644
--- a/sway/commands/focus.c
+++ b/sway/commands/focus.c
@@ -54,7 +54,7 @@ static bool get_direction_from_next_prev(struct sway_container *container,
54 } else { 54 } else {
55 return false; 55 return false;
56 } 56 }
57 57
58 return true; 58 return true;
59} 59}
60 60
@@ -141,9 +141,9 @@ static struct sway_node *node_get_in_direction_tiling(
141 struct sway_container *wrap_candidate = NULL; 141 struct sway_container *wrap_candidate = NULL;
142 struct sway_container *current = container; 142 struct sway_container *current = container;
143 while (current) { 143 while (current) {
144 if (current->fullscreen_mode == FULLSCREEN_WORKSPACE) { 144 if (current->pending.fullscreen_mode == FULLSCREEN_WORKSPACE) {
145 // Fullscreen container with a direction - go straight to outputs 145 // Fullscreen container with a direction - go straight to outputs
146 struct sway_output *output = current->workspace->output; 146 struct sway_output *output = current->pending.workspace->output;
147 struct sway_output *new_output = 147 struct sway_output *new_output =
148 output_get_in_direction(output, dir); 148 output_get_in_direction(output, dir);
149 if (!new_output) { 149 if (!new_output) {
@@ -151,7 +151,7 @@ static struct sway_node *node_get_in_direction_tiling(
151 } 151 }
152 return get_node_in_output_direction(new_output, dir); 152 return get_node_in_output_direction(new_output, dir);
153 } 153 }
154 if (current->fullscreen_mode == FULLSCREEN_GLOBAL) { 154 if (current->pending.fullscreen_mode == FULLSCREEN_GLOBAL) {
155 return NULL; 155 return NULL;
156 } 156 }
157 157
@@ -202,11 +202,11 @@ static struct sway_node *node_get_in_direction_tiling(
202 } 202 }
203 } 203 }
204 204
205 current = current->parent; 205 current = current->pending.parent;
206 } 206 }
207 207
208 // Check a different output 208 // Check a different output
209 struct sway_output *output = container->workspace->output; 209 struct sway_output *output = container->pending.workspace->output;
210 struct sway_output *new_output = output_get_in_direction(output, dir); 210 struct sway_output *new_output = output_get_in_direction(output, dir);
211 if ((config->focus_wrapping != WRAP_WORKSPACE || 211 if ((config->focus_wrapping != WRAP_WORKSPACE ||
212 container->node.type == N_WORKSPACE) && new_output) { 212 container->node.type == N_WORKSPACE) && new_output) {
@@ -226,23 +226,23 @@ static struct sway_node *node_get_in_direction_tiling(
226static struct sway_node *node_get_in_direction_floating( 226static struct sway_node *node_get_in_direction_floating(
227 struct sway_container *con, struct sway_seat *seat, 227 struct sway_container *con, struct sway_seat *seat,
228 enum wlr_direction dir) { 228 enum wlr_direction dir) {
229 double ref_lx = con->x + con->width / 2; 229 double ref_lx = con->pending.x + con->pending.width / 2;
230 double ref_ly = con->y + con->height / 2; 230 double ref_ly = con->pending.y + con->pending.height / 2;
231 double closest_distance = DBL_MAX; 231 double closest_distance = DBL_MAX;
232 struct sway_container *closest_con = NULL; 232 struct sway_container *closest_con = NULL;
233 233
234 if (!con->workspace) { 234 if (!con->pending.workspace) {
235 return NULL; 235 return NULL;
236 } 236 }
237 237
238 for (int i = 0; i < con->workspace->floating->length; i++) { 238 for (int i = 0; i < con->pending.workspace->floating->length; i++) {
239 struct sway_container *floater = con->workspace->floating->items[i]; 239 struct sway_container *floater = con->pending.workspace->floating->items[i];
240 if (floater == con) { 240 if (floater == con) {
241 continue; 241 continue;
242 } 242 }
243 float distance = dir == WLR_DIRECTION_LEFT || dir == WLR_DIRECTION_RIGHT 243 float distance = dir == WLR_DIRECTION_LEFT || dir == WLR_DIRECTION_RIGHT
244 ? (floater->x + floater->width / 2) - ref_lx 244 ? (floater->pending.x + floater->pending.width / 2) - ref_lx
245 : (floater->y + floater->height / 2) - ref_ly; 245 : (floater->pending.y + floater->pending.height / 2) - ref_ly;
246 if (dir == WLR_DIRECTION_LEFT || dir == WLR_DIRECTION_UP) { 246 if (dir == WLR_DIRECTION_LEFT || dir == WLR_DIRECTION_UP) {
247 distance = -distance; 247 distance = -distance;
248 } 248 }
@@ -267,6 +267,11 @@ static struct cmd_results *focus_mode(struct sway_workspace *ws,
267 new_focus = seat_get_focus_inactive_tiling(seat, ws); 267 new_focus = seat_get_focus_inactive_tiling(seat, ws);
268 } 268 }
269 if (new_focus) { 269 if (new_focus) {
270 struct sway_container *new_focus_view =
271 seat_get_focus_inactive_view(seat, &new_focus->node);
272 if (new_focus_view) {
273 new_focus = new_focus_view;
274 }
270 seat_set_focus_container(seat, new_focus); 275 seat_set_focus_container(seat, new_focus);
271 276
272 // If we're on the floating layer and the floating container area 277 // If we're on the floating layer and the floating container area
@@ -280,7 +285,7 @@ static struct cmd_results *focus_mode(struct sway_workspace *ws,
280 } 285 }
281 } else { 286 } else {
282 return cmd_results_new(CMD_FAILURE, 287 return cmd_results_new(CMD_FAILURE,
283 "Failed to find a %s container in workspace", 288 "Failed to find a %s container in workspace.",
284 floating ? "floating" : "tiling"); 289 floating ? "floating" : "tiling");
285 } 290 }
286 return cmd_results_new(CMD_SUCCESS, NULL); 291 return cmd_results_new(CMD_SUCCESS, NULL);
@@ -290,7 +295,7 @@ static struct cmd_results *focus_output(struct sway_seat *seat,
290 int argc, char **argv) { 295 int argc, char **argv) {
291 if (!argc) { 296 if (!argc) {
292 return cmd_results_new(CMD_INVALID, 297 return cmd_results_new(CMD_INVALID,
293 "Expected 'focus output <direction|name>'"); 298 "Expected 'focus output <direction|name>'.");
294 } 299 }
295 char *identifier = join_args(argv, argc); 300 char *identifier = join_args(argv, argc);
296 struct sway_output *output = output_by_name_or_id(identifier); 301 struct sway_output *output = output_by_name_or_id(identifier);
@@ -300,13 +305,13 @@ static struct cmd_results *focus_output(struct sway_seat *seat,
300 if (!parse_direction(identifier, &direction)) { 305 if (!parse_direction(identifier, &direction)) {
301 free(identifier); 306 free(identifier);
302 return cmd_results_new(CMD_INVALID, 307 return cmd_results_new(CMD_INVALID,
303 "There is no output with that name"); 308 "There is no output with that name.");
304 } 309 }
305 struct sway_workspace *ws = seat_get_focused_workspace(seat); 310 struct sway_workspace *ws = seat_get_focused_workspace(seat);
306 if (!ws) { 311 if (!ws) {
307 free(identifier); 312 free(identifier);
308 return cmd_results_new(CMD_FAILURE, 313 return cmd_results_new(CMD_FAILURE,
309 "No focused workspace to base directions off of"); 314 "No focused workspace to base directions off of.");
310 } 315 }
311 output = output_get_in_direction(ws->output, direction); 316 output = output_get_in_direction(ws->output, direction);
312 317
@@ -334,7 +339,7 @@ static struct cmd_results *focus_output(struct sway_seat *seat,
334static struct cmd_results *focus_parent(void) { 339static struct cmd_results *focus_parent(void) {
335 struct sway_seat *seat = config->handler_context.seat; 340 struct sway_seat *seat = config->handler_context.seat;
336 struct sway_container *con = config->handler_context.container; 341 struct sway_container *con = config->handler_context.container;
337 if (!con || con->fullscreen_mode) { 342 if (!con || con->pending.fullscreen_mode) {
338 return cmd_results_new(CMD_SUCCESS, NULL); 343 return cmd_results_new(CMD_SUCCESS, NULL);
339 } 344 }
340 struct sway_node *parent = node_get_parent(&con->node); 345 struct sway_node *parent = node_get_parent(&con->node);
@@ -370,13 +375,24 @@ struct cmd_results *cmd_focus(int argc, char **argv) {
370 struct sway_seat *seat = config->handler_context.seat; 375 struct sway_seat *seat = config->handler_context.seat;
371 if (node->type < N_WORKSPACE) { 376 if (node->type < N_WORKSPACE) {
372 return cmd_results_new(CMD_FAILURE, 377 return cmd_results_new(CMD_FAILURE,
373 "Command 'focus' cannot be used above the workspace level"); 378 "Command 'focus' cannot be used above the workspace level.");
374 } 379 }
375 380
376 if (argc == 0 && container) { 381 if (argc == 0) {
382 if (!container) {
383 return cmd_results_new(CMD_FAILURE, "No container to focus was specified.");
384 }
385
377 if (container_is_scratchpad_hidden_or_child(container)) { 386 if (container_is_scratchpad_hidden_or_child(container)) {
378 root_scratchpad_show(container); 387 root_scratchpad_show(container);
379 } 388 }
389 // if we are switching to a container under a fullscreen window, we first
390 // need to exit fullscreen so that the newly focused container becomes visible
391 struct sway_container *obstructing = container_obstructing_fullscreen_container(container);
392 if (obstructing) {
393 container_fullscreen_disable(obstructing);
394 arrange_root();
395 }
380 seat_set_focus_container(seat, container); 396 seat_set_focus_container(seat, container);
381 seat_consider_warp_to_focus(seat); 397 seat_consider_warp_to_focus(seat);
382 container_raise_floating(container); 398 container_raise_floating(container);
@@ -439,7 +455,8 @@ struct cmd_results *cmd_focus(int argc, char **argv) {
439 return cmd_results_new(CMD_FAILURE, ""); 455 return cmd_results_new(CMD_FAILURE, "");
440 } 456 }
441 struct sway_node *next_focus = NULL; 457 struct sway_node *next_focus = NULL;
442 if (container_is_floating(container)) { 458 if (container_is_floating(container) &&
459 container->pending.fullscreen_mode == FULLSCREEN_NONE) {
443 next_focus = node_get_in_direction_floating(container, seat, direction); 460 next_focus = node_get_in_direction_floating(container, seat, direction);
444 } else { 461 } else {
445 next_focus = node_get_in_direction_tiling(container, seat, direction, descend); 462 next_focus = node_get_in_direction_tiling(container, seat, direction, descend);
diff --git a/sway/commands/font.c b/sway/commands/font.c
index c54365b5..9920d03e 100644
--- a/sway/commands/font.c
+++ b/sway/commands/font.c
@@ -1,9 +1,9 @@
1#define _POSIX_C_SOURCE 200809L
2#include <string.h> 1#include <string.h>
3#include "sway/commands.h" 2#include "sway/commands.h"
4#include "sway/config.h" 3#include "sway/config.h"
5#include "log.h" 4#include "log.h"
6#include "stringop.h" 5#include "stringop.h"
6#include <pango/pangocairo.h>
7 7
8struct cmd_results *cmd_font(int argc, char **argv) { 8struct cmd_results *cmd_font(int argc, char **argv) {
9 struct cmd_results *error = NULL; 9 struct cmd_results *error = NULL;
@@ -16,12 +16,34 @@ struct cmd_results *cmd_font(int argc, char **argv) {
16 if (strncmp(font, "pango:", 6) == 0) { 16 if (strncmp(font, "pango:", 6) == 0) {
17 config->pango_markup = true; 17 config->pango_markup = true;
18 config->font = strdup(font + 6); 18 config->font = strdup(font + 6);
19 free(font);
19 } else { 20 } else {
20 config->pango_markup = false; 21 config->pango_markup = false;
21 config->font = strdup(font); 22 config->font = font;
22 } 23 }
23 24
24 free(font); 25 // Parse the font early so we can reject it if it's not valid for pango.
25 config_update_font_height(true); 26 // Also avoids re-parsing each time we render text.
27 PangoFontDescription *font_description = pango_font_description_from_string(config->font);
28
29 const char *family = pango_font_description_get_family(font_description);
30 if (family == NULL) {
31 pango_font_description_free(font_description);
32 return cmd_results_new(CMD_FAILURE, "Invalid font family.");
33 }
34
35 const gint size = pango_font_description_get_size(font_description);
36 if (size == 0) {
37 pango_font_description_free(font_description);
38 return cmd_results_new(CMD_FAILURE, "Invalid font size.");
39 }
40
41 if (config->font_description != NULL) {
42 pango_font_description_free(config->font_description);
43 }
44
45 config->font_description = font_description;
46 config_update_font_height();
47
26 return cmd_results_new(CMD_SUCCESS, NULL); 48 return cmd_results_new(CMD_SUCCESS, NULL);
27} 49}
diff --git a/sway/commands/for_window.c b/sway/commands/for_window.c
index ee9f4647..905e6776 100644
--- a/sway/commands/for_window.c
+++ b/sway/commands/for_window.c
@@ -14,7 +14,7 @@ struct cmd_results *cmd_for_window(int argc, char **argv) {
14 char *err_str = NULL; 14 char *err_str = NULL;
15 struct criteria *criteria = criteria_parse(argv[0], &err_str); 15 struct criteria *criteria = criteria_parse(argv[0], &err_str);
16 if (!criteria) { 16 if (!criteria) {
17 error = cmd_results_new(CMD_INVALID, err_str); 17 error = cmd_results_new(CMD_INVALID, "%s", err_str);
18 free(err_str); 18 free(err_str);
19 return error; 19 return error;
20 } 20 }
diff --git a/sway/commands/fullscreen.c b/sway/commands/fullscreen.c
index 3392a7f7..21c1e9a0 100644
--- a/sway/commands/fullscreen.c
+++ b/sway/commands/fullscreen.c
@@ -18,30 +18,19 @@ struct cmd_results *cmd_fullscreen(int argc, char **argv) {
18 return cmd_results_new(CMD_FAILURE, 18 return cmd_results_new(CMD_FAILURE,
19 "Can't run this command while there's no outputs connected."); 19 "Can't run this command while there's no outputs connected.");
20 } 20 }
21 struct sway_node *node = config->handler_context.node;
22 struct sway_container *container = config->handler_context.container; 21 struct sway_container *container = config->handler_context.container;
23 struct sway_workspace *workspace = config->handler_context.workspace;
24 if (node->type == N_WORKSPACE && workspace->tiling->length == 0) {
25 return cmd_results_new(CMD_FAILURE,
26 "Can't fullscreen an empty workspace");
27 }
28 22
29 // If in the scratchpad, operate on the highest container 23 if (!container) {
30 if (container && !container->workspace) { 24 // If the focus is not a container, do nothing successfully
31 while (container->parent) { 25 return cmd_results_new(CMD_SUCCESS, NULL);
32 container = container->parent; 26 } else if (!container->pending.workspace) {
33 } 27 // If in the scratchpad, operate on the highest container
34 } 28 while (container->pending.parent) {
35 29 container = container->pending.parent;
36 bool is_fullscreen = false;
37 for (struct sway_container *curr = container; curr; curr = curr->parent) {
38 if (curr->fullscreen_mode != FULLSCREEN_NONE) {
39 container = curr;
40 is_fullscreen = true;
41 break;
42 } 30 }
43 } 31 }
44 32
33 bool is_fullscreen = container->pending.fullscreen_mode != FULLSCREEN_NONE;
45 bool global = false; 34 bool global = false;
46 bool enable = !is_fullscreen; 35 bool enable = !is_fullscreen;
47 36
@@ -57,13 +46,6 @@ struct cmd_results *cmd_fullscreen(int argc, char **argv) {
57 global = strcasecmp(argv[1], "global") == 0; 46 global = strcasecmp(argv[1], "global") == 0;
58 } 47 }
59 48
60 if (enable && node->type == N_WORKSPACE) {
61 // Wrap the workspace's children in a container so we can fullscreen it
62 container = workspace_wrap_children(workspace);
63 workspace->layout = L_HORIZ;
64 seat_set_focus_container(config->handler_context.seat, container);
65 }
66
67 enum sway_fullscreen_mode mode = FULLSCREEN_NONE; 49 enum sway_fullscreen_mode mode = FULLSCREEN_NONE;
68 if (enable) { 50 if (enable) {
69 mode = global ? FULLSCREEN_GLOBAL : FULLSCREEN_WORKSPACE; 51 mode = global ? FULLSCREEN_GLOBAL : FULLSCREEN_WORKSPACE;
diff --git a/sway/commands/gaps.c b/sway/commands/gaps.c
index 021df843..1deeb56e 100644
--- a/sway/commands/gaps.c
+++ b/sway/commands/gaps.c
@@ -11,7 +11,8 @@
11enum gaps_op { 11enum gaps_op {
12 GAPS_OP_SET, 12 GAPS_OP_SET,
13 GAPS_OP_ADD, 13 GAPS_OP_ADD,
14 GAPS_OP_SUBTRACT 14 GAPS_OP_SUBTRACT,
15 GAPS_OP_TOGGLE
15}; 16};
16 17
17struct gaps_data { 18struct gaps_data {
@@ -102,6 +103,9 @@ static void apply_gaps_op(int *prop, enum gaps_op op, int amount) {
102 case GAPS_OP_SUBTRACT: 103 case GAPS_OP_SUBTRACT:
103 *prop -= amount; 104 *prop -= amount;
104 break; 105 break;
106 case GAPS_OP_TOGGLE:
107 *prop = *prop ? 0 : amount;
108 break;
105 } 109 }
106} 110}
107 111
@@ -133,9 +137,9 @@ static void configure_gaps(struct sway_workspace *ws, void *_data) {
133} 137}
134 138
135// gaps inner|outer|horizontal|vertical|top|right|bottom|left current|all 139// gaps inner|outer|horizontal|vertical|top|right|bottom|left current|all
136// set|plus|minus <px> 140// set|plus|minus|toggle <px>
137static const char expected_runtime[] = "'gaps inner|outer|horizontal|vertical|" 141static const char expected_runtime[] = "'gaps inner|outer|horizontal|vertical|"
138 "top|right|bottom|left current|all set|plus|minus <px>'"; 142 "top|right|bottom|left current|all set|plus|minus|toggle <px>'";
139static struct cmd_results *gaps_set_runtime(int argc, char **argv) { 143static struct cmd_results *gaps_set_runtime(int argc, char **argv) {
140 struct cmd_results *error = checkarg(argc, "gaps", EXPECTED_EQUAL_TO, 4); 144 struct cmd_results *error = checkarg(argc, "gaps", EXPECTED_EQUAL_TO, 4);
141 if (error) { 145 if (error) {
@@ -180,6 +184,8 @@ static struct cmd_results *gaps_set_runtime(int argc, char **argv) {
180 data.operation = GAPS_OP_ADD; 184 data.operation = GAPS_OP_ADD;
181 } else if (strcasecmp(argv[2], "minus") == 0) { 185 } else if (strcasecmp(argv[2], "minus") == 0) {
182 data.operation = GAPS_OP_SUBTRACT; 186 data.operation = GAPS_OP_SUBTRACT;
187 } else if (strcasecmp(argv[2], "toggle") == 0) {
188 data.operation = GAPS_OP_TOGGLE;
183 } else { 189 } else {
184 return cmd_results_new(CMD_INVALID, "Expected %s", expected_runtime); 190 return cmd_results_new(CMD_INVALID, "Expected %s", expected_runtime);
185 } 191 }
@@ -200,7 +206,7 @@ static struct cmd_results *gaps_set_runtime(int argc, char **argv) {
200} 206}
201 207
202// gaps inner|outer|<dir>|<side> <px> - sets defaults for workspaces 208// gaps inner|outer|<dir>|<side> <px> - sets defaults for workspaces
203// gaps inner|outer|<dir>|<side> current|all set|plus|minus <px> - runtime only 209// gaps inner|outer|<dir>|<side> current|all set|plus|minus|toggle <px> - runtime only
204// <dir> = horizontal|vertical 210// <dir> = horizontal|vertical
205// <side> = top|right|bottom|left 211// <side> = top|right|bottom|left
206struct cmd_results *cmd_gaps(int argc, char **argv) { 212struct cmd_results *cmd_gaps(int argc, char **argv) {
diff --git a/sway/commands/gesture.c b/sway/commands/gesture.c
new file mode 100644
index 00000000..90a20716
--- /dev/null
+++ b/sway/commands/gesture.c
@@ -0,0 +1,165 @@
1#include "sway/config.h"
2
3#include "gesture.h"
4#include "log.h"
5#include "stringop.h"
6#include "sway/commands.h"
7
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 c9bb8e06..306c40f7 100644
--- a/sway/commands/input.c
+++ b/sway/commands/input.c
@@ -7,13 +7,14 @@
7#include "stringop.h" 7#include "stringop.h"
8 8
9// must be in order for the bsearch 9// must be in order for the bsearch
10static struct cmd_handler input_handlers[] = { 10static const struct cmd_handler input_handlers[] = {
11 { "accel_profile", input_cmd_accel_profile }, 11 { "accel_profile", input_cmd_accel_profile },
12 { "calibration_matrix", input_cmd_calibration_matrix }, 12 { "calibration_matrix", input_cmd_calibration_matrix },
13 { "click_method", input_cmd_click_method }, 13 { "click_method", input_cmd_click_method },
14 { "drag", input_cmd_drag }, 14 { "drag", input_cmd_drag },
15 { "drag_lock", input_cmd_drag_lock }, 15 { "drag_lock", input_cmd_drag_lock },
16 { "dwt", input_cmd_dwt }, 16 { "dwt", input_cmd_dwt },
17 { "dwtp", input_cmd_dwtp },
17 { "events", input_cmd_events }, 18 { "events", input_cmd_events },
18 { "left_handed", input_cmd_left_handed }, 19 { "left_handed", input_cmd_left_handed },
19 { "map_from_region", input_cmd_map_from_region }, 20 { "map_from_region", input_cmd_map_from_region },
@@ -24,7 +25,9 @@ static struct cmd_handler input_handlers[] = {
24 { "pointer_accel", input_cmd_pointer_accel }, 25 { "pointer_accel", input_cmd_pointer_accel },
25 { "repeat_delay", input_cmd_repeat_delay }, 26 { "repeat_delay", input_cmd_repeat_delay },
26 { "repeat_rate", input_cmd_repeat_rate }, 27 { "repeat_rate", input_cmd_repeat_rate },
28 { "rotation_angle", input_cmd_rotation_angle },
27 { "scroll_button", input_cmd_scroll_button }, 29 { "scroll_button", input_cmd_scroll_button },
30 { "scroll_button_lock", input_cmd_scroll_button_lock },
28 { "scroll_factor", input_cmd_scroll_factor }, 31 { "scroll_factor", input_cmd_scroll_factor },
29 { "scroll_method", input_cmd_scroll_method }, 32 { "scroll_method", input_cmd_scroll_method },
30 { "tap", input_cmd_tap }, 33 { "tap", input_cmd_tap },
@@ -40,7 +43,7 @@ static struct cmd_handler input_handlers[] = {
40}; 43};
41 44
42// must be in order for the bsearch 45// must be in order for the bsearch
43static struct cmd_handler input_config_handlers[] = { 46static const struct cmd_handler input_config_handlers[] = {
44 { "xkb_capslock", input_cmd_xkb_capslock }, 47 { "xkb_capslock", input_cmd_xkb_capslock },
45 { "xkb_numlock", input_cmd_xkb_numlock }, 48 { "xkb_numlock", input_cmd_xkb_numlock },
46}; 49};
diff --git a/sway/commands/input/calibration_matrix.c b/sway/commands/input/calibration_matrix.c
index 38749fbb..53fe2c35 100644
--- a/sway/commands/input/calibration_matrix.c
+++ b/sway/commands/input/calibration_matrix.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <string.h> 1#include <string.h>
3#include <strings.h> 2#include <strings.h>
4#include "sway/config.h" 3#include "sway/config.h"
diff --git a/sway/commands/input/dwtp.c b/sway/commands/input/dwtp.c
new file mode 100644
index 00000000..232e2b26
--- /dev/null
+++ b/sway/commands/input/dwtp.c
@@ -0,0 +1,25 @@
1#include <string.h>
2#include <strings.h>
3#include "sway/config.h"
4#include "sway/commands.h"
5#include "sway/input/input-manager.h"
6#include "util.h"
7
8struct cmd_results *input_cmd_dwtp(int argc, char **argv) {
9 struct cmd_results *error = NULL;
10 if ((error = checkarg(argc, "dwtp", EXPECTED_AT_LEAST, 1))) {
11 return error;
12 }
13 struct input_config *ic = config->handler_context.input_config;
14 if (!ic) {
15 return cmd_results_new(CMD_FAILURE, "No input device defined.");
16 }
17
18 if (parse_boolean(argv[0], true)) {
19 ic->dwtp = LIBINPUT_CONFIG_DWTP_ENABLED;
20 } else {
21 ic->dwtp = LIBINPUT_CONFIG_DWTP_DISABLED;
22 }
23
24 return cmd_results_new(CMD_SUCCESS, NULL);
25}
diff --git a/sway/commands/input/events.c b/sway/commands/input/events.c
index 9405181a..08d99bf0 100644
--- a/sway/commands/input/events.c
+++ b/sway/commands/input/events.c
@@ -1,14 +1,19 @@
1#include <limits.h> 1#include <limits.h>
2#include <string.h> 2#include <string.h>
3#include <strings.h> 3#include <strings.h>
4#include <wlr/backend/libinput.h> 4#include <wlr/config.h>
5#include "sway/config.h" 5#include "sway/config.h"
6#include "sway/commands.h" 6#include "sway/commands.h"
7#include "sway/input/input-manager.h" 7#include "sway/input/input-manager.h"
8#include "log.h" 8#include "log.h"
9 9
10#if WLR_HAS_LIBINPUT_BACKEND
11#include <wlr/backend/libinput.h>
12#endif
13
10static void toggle_supported_send_events_for_device(struct input_config *ic, 14static void toggle_supported_send_events_for_device(struct input_config *ic,
11 struct sway_input_device *input_device) { 15 struct sway_input_device *input_device) {
16#if WLR_HAS_LIBINPUT_BACKEND
12 struct wlr_input_device *wlr_device = input_device->wlr_device; 17 struct wlr_input_device *wlr_device = input_device->wlr_device;
13 if (!wlr_input_device_is_libinput(wlr_device)) { 18 if (!wlr_input_device_is_libinput(wlr_device)) {
14 return; 19 return;
@@ -41,6 +46,7 @@ static void toggle_supported_send_events_for_device(struct input_config *ic,
41 } 46 }
42 47
43 ic->send_events = mode; 48 ic->send_events = mode;
49#endif
44} 50}
45 51
46static int mode_for_name(const char *name) { 52static int mode_for_name(const char *name) {
@@ -56,6 +62,7 @@ static int mode_for_name(const char *name) {
56 62
57static void toggle_select_send_events_for_device(struct input_config *ic, 63static void toggle_select_send_events_for_device(struct input_config *ic,
58 struct sway_input_device *input_device, int argc, char **argv) { 64 struct sway_input_device *input_device, int argc, char **argv) {
65#if WLR_HAS_LIBINPUT_BACKEND
59 if (!wlr_input_device_is_libinput(input_device->wlr_device)) { 66 if (!wlr_input_device_is_libinput(input_device->wlr_device)) {
60 return; 67 return;
61 } 68 }
@@ -72,6 +79,7 @@ static void toggle_select_send_events_for_device(struct input_config *ic,
72 } 79 }
73 } 80 }
74 ic->send_events = mode_for_name(argv[index % argc]); 81 ic->send_events = mode_for_name(argv[index % argc]);
82#endif
75} 83}
76 84
77static void toggle_send_events(int argc, char **argv) { 85static void toggle_send_events(int argc, char **argv) {
diff --git a/sway/commands/input/map_from_region.c b/sway/commands/input/map_from_region.c
index de00b714..2f8f753d 100644
--- a/sway/commands/input/map_from_region.c
+++ b/sway/commands/input/map_from_region.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <stdbool.h> 1#include <stdbool.h>
3#include <string.h> 2#include <string.h>
4#include <strings.h> 3#include <strings.h>
@@ -11,11 +10,21 @@ static bool parse_coords(const char *str, double *x, double *y, bool *mm) {
11 *mm = false; 10 *mm = false;
12 11
13 char *end; 12 char *end;
14 *x = strtod(str, &end); 13
15 if (end[0] != 'x') { 14 // Check for "0x" prefix to avoid strtod treating the string as hex
16 return false; 15 if (str[0] == '0' && str[1] == 'x') {
16 if (strlen(str) < 3) {
17 return false;
18 }
19 *x = 0;
20 end = (char *)str + 2;
21 } else {
22 *x = strtod(str, &end);
23 if (end[0] != 'x') {
24 return false;
25 }
26 ++end;
17 } 27 }
18 ++end;
19 28
20 *y = strtod(end, &end); 29 *y = strtod(end, &end);
21 if (end[0] == 'm') { 30 if (end[0] == 'm') {
diff --git a/sway/commands/input/map_to_output.c b/sway/commands/input/map_to_output.c
index f60fb7d5..a7266baa 100644
--- a/sway/commands/input/map_to_output.c
+++ b/sway/commands/input/map_to_output.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <string.h> 1#include <string.h>
3#include <strings.h> 2#include <strings.h>
4#include "sway/config.h" 3#include "sway/config.h"
diff --git a/sway/commands/input/map_to_region.c b/sway/commands/input/map_to_region.c
index e85495e5..9087c589 100644
--- a/sway/commands/input/map_to_region.c
+++ b/sway/commands/input/map_to_region.c
@@ -1,7 +1,5 @@
1#define _POSIX_C_SOURCE 200809L
2#include <stdlib.h> 1#include <stdlib.h>
3#include <string.h> 2#include <string.h>
4#include <wlr/types/wlr_box.h>
5#include "sway/commands.h" 3#include "sway/commands.h"
6#include "sway/config.h" 4#include "sway/config.h"
7 5
@@ -50,5 +48,5 @@ struct cmd_results *input_cmd_map_to_region(int argc, char **argv) {
50error: 48error:
51 free(ic->mapped_to_region); 49 free(ic->mapped_to_region);
52 ic->mapped_to_region = NULL; 50 ic->mapped_to_region = NULL;
53 return cmd_results_new(CMD_FAILURE, errstr); 51 return cmd_results_new(CMD_FAILURE, "%s", errstr);
54} 52}
diff --git a/sway/commands/input/rotation_angle.c b/sway/commands/input/rotation_angle.c
new file mode 100644
index 00000000..5e278fff
--- /dev/null
+++ b/sway/commands/input/rotation_angle.c
@@ -0,0 +1,29 @@
1#include <math.h>
2#include <stdlib.h>
3#include <string.h>
4#include "sway/config.h"
5#include "sway/commands.h"
6#include "sway/input/input-manager.h"
7#include "util.h"
8
9struct cmd_results *input_cmd_rotation_angle(int argc, char **argv) {
10 struct cmd_results *error = NULL;
11 if ((error = checkarg(argc, "rotation_angle", EXPECTED_AT_LEAST, 1))) {
12 return error;
13 }
14 struct input_config *ic = config->handler_context.input_config;
15 if (!ic) {
16 return cmd_results_new(CMD_FAILURE, "No input device defined.");
17 }
18
19 float rotation_angle = parse_float(argv[0]);
20 if (isnan(rotation_angle)) {
21 return cmd_results_new(CMD_INVALID,
22 "Invalid rotation_angle; expected float.");
23 } if (rotation_angle < 0 || rotation_angle > 360) {
24 return cmd_results_new(CMD_INVALID, "Input out of range [0, 360)");
25 }
26 ic->rotation_angle = rotation_angle;
27
28 return cmd_results_new(CMD_SUCCESS, NULL);
29}
diff --git a/sway/commands/input/scroll_button.c b/sway/commands/input/scroll_button.c
index 6b331419..81f69a6d 100644
--- a/sway/commands/input/scroll_button.c
+++ b/sway/commands/input/scroll_button.c
@@ -21,7 +21,7 @@ struct cmd_results *input_cmd_scroll_button(int argc, char **argv) {
21 char *message = NULL; 21 char *message = NULL;
22 uint32_t button = get_mouse_button(*argv, &message); 22 uint32_t button = get_mouse_button(*argv, &message);
23 if (message) { 23 if (message) {
24 error = cmd_results_new(CMD_INVALID, message); 24 error = cmd_results_new(CMD_INVALID, "%s", message);
25 free(message); 25 free(message);
26 return error; 26 return error;
27 } else if (button == SWAY_SCROLL_UP || button == SWAY_SCROLL_DOWN 27 } else if (button == SWAY_SCROLL_UP || button == SWAY_SCROLL_DOWN
diff --git a/sway/commands/input/scroll_button_lock.c b/sway/commands/input/scroll_button_lock.c
new file mode 100644
index 00000000..f96b6514
--- /dev/null
+++ b/sway/commands/input/scroll_button_lock.c
@@ -0,0 +1,26 @@
1#include <libinput.h>
2#include <string.h>
3#include <strings.h>
4#include "sway/config.h"
5#include "sway/commands.h"
6#include "sway/input/input-manager.h"
7#include "util.h"
8
9struct cmd_results *input_cmd_scroll_button_lock(int argc, char **argv) {
10 struct cmd_results *error = NULL;
11 if ((error = checkarg(argc, "scroll_button_lock", EXPECTED_AT_LEAST, 1))) {
12 return error;
13 }
14 struct input_config *ic = config->handler_context.input_config;
15 if (!ic) {
16 return cmd_results_new(CMD_FAILURE, "No input device defined.");
17 }
18
19 if (parse_boolean(argv[0], true)) {
20 ic->scroll_button_lock = LIBINPUT_CONFIG_SCROLL_BUTTON_LOCK_ENABLED;
21 } else {
22 ic->scroll_button_lock = LIBINPUT_CONFIG_SCROLL_BUTTON_LOCK_DISABLED;
23 }
24
25 return cmd_results_new(CMD_SUCCESS, NULL);
26}
diff --git a/sway/commands/input/xkb_file.c b/sway/commands/input/xkb_file.c
index 493f94fb..056f00e5 100644
--- a/sway/commands/input/xkb_file.c
+++ b/sway/commands/input/xkb_file.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <unistd.h> 1#include <unistd.h>
3#include <errno.h> 2#include <errno.h>
4#include "sway/config.h" 3#include "sway/config.h"
diff --git a/sway/commands/input/xkb_layout.c b/sway/commands/input/xkb_layout.c
index 22626517..1d01886c 100644
--- a/sway/commands/input/xkb_layout.c
+++ b/sway/commands/input/xkb_layout.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include "sway/config.h" 1#include "sway/config.h"
3#include "sway/commands.h" 2#include "sway/commands.h"
4#include "log.h" 3#include "log.h"
diff --git a/sway/commands/input/xkb_model.c b/sway/commands/input/xkb_model.c
index f4a33de3..a9144a8a 100644
--- a/sway/commands/input/xkb_model.c
+++ b/sway/commands/input/xkb_model.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include "sway/config.h" 1#include "sway/config.h"
3#include "sway/commands.h" 2#include "sway/commands.h"
4#include "log.h" 3#include "log.h"
diff --git a/sway/commands/input/xkb_numlock.c b/sway/commands/input/xkb_numlock.c
index 87d3e60c..bbe848fe 100644
--- a/sway/commands/input/xkb_numlock.c
+++ b/sway/commands/input/xkb_numlock.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include "sway/config.h" 1#include "sway/config.h"
3#include "sway/commands.h" 2#include "sway/commands.h"
4#include "util.h" 3#include "util.h"
diff --git a/sway/commands/input/xkb_options.c b/sway/commands/input/xkb_options.c
index d609293f..7ca20777 100644
--- a/sway/commands/input/xkb_options.c
+++ b/sway/commands/input/xkb_options.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include "sway/config.h" 1#include "sway/config.h"
3#include "sway/commands.h" 2#include "sway/commands.h"
4#include "log.h" 3#include "log.h"
diff --git a/sway/commands/input/xkb_rules.c b/sway/commands/input/xkb_rules.c
index 3b59622c..8fbd26fb 100644
--- a/sway/commands/input/xkb_rules.c
+++ b/sway/commands/input/xkb_rules.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include "sway/config.h" 1#include "sway/config.h"
3#include "sway/commands.h" 2#include "sway/commands.h"
4#include "log.h" 3#include "log.h"
diff --git a/sway/commands/input/xkb_switch_layout.c b/sway/commands/input/xkb_switch_layout.c
index d6548a68..ecac8e6c 100644
--- a/sway/commands/input/xkb_switch_layout.c
+++ b/sway/commands/input/xkb_switch_layout.c
@@ -1,10 +1,15 @@
1#define _POSIX_C_SOURCE 200809L
2#include <assert.h> 1#include <assert.h>
2#include <wlr/interfaces/wlr_keyboard.h>
3#include "sway/config.h" 3#include "sway/config.h"
4#include "sway/commands.h" 4#include "sway/commands.h"
5#include "sway/input/input-manager.h" 5#include "sway/input/input-manager.h"
6#include "log.h" 6#include "log.h"
7 7
8struct xkb_switch_layout_action {
9 struct wlr_keyboard *keyboard;
10 xkb_layout_index_t layout;
11};
12
8static void switch_layout(struct wlr_keyboard *kbd, xkb_layout_index_t idx) { 13static void switch_layout(struct wlr_keyboard *kbd, xkb_layout_index_t idx) {
9 xkb_layout_index_t num_layouts = xkb_keymap_num_layouts(kbd->keymap); 14 xkb_layout_index_t num_layouts = xkb_keymap_num_layouts(kbd->keymap);
10 if (idx >= num_layouts) { 15 if (idx >= num_layouts) {
@@ -28,10 +33,10 @@ static xkb_layout_index_t get_current_layout_index(struct wlr_keyboard *kbd) {
28 return layout_idx; 33 return layout_idx;
29} 34}
30 35
31static void switch_layout_relative(struct wlr_keyboard *kbd, int dir) { 36static xkb_layout_index_t get_layout_relative(struct wlr_keyboard *kbd, int dir) {
32 xkb_layout_index_t num_layouts = xkb_keymap_num_layouts(kbd->keymap); 37 xkb_layout_index_t num_layouts = xkb_keymap_num_layouts(kbd->keymap);
33 xkb_layout_index_t idx = get_current_layout_index(kbd); 38 xkb_layout_index_t idx = get_current_layout_index(kbd);
34 switch_layout(kbd, (idx + num_layouts + dir) % num_layouts); 39 return (idx + num_layouts + dir) % num_layouts;
35} 40}
36 41
37struct cmd_results *input_cmd_xkb_switch_layout(int argc, char **argv) { 42struct cmd_results *input_cmd_xkb_switch_layout(int argc, char **argv) {
@@ -66,6 +71,18 @@ struct cmd_results *input_cmd_xkb_switch_layout(int argc, char **argv) {
66 relative = 0; 71 relative = 0;
67 } 72 }
68 73
74 struct xkb_switch_layout_action *actions = calloc(
75 wl_list_length(&server.input->devices),
76 sizeof(struct xkb_switch_layout_action));
77 size_t actions_len = 0;
78
79 if (!actions) {
80 return cmd_results_new(CMD_FAILURE, "Unable to allocate actions");
81 }
82
83 /* Calculate new indexes first because switching a layout in one
84 keyboard may result in a change on other keyboards as well because
85 of keyboard groups. */
69 struct sway_input_device *dev; 86 struct sway_input_device *dev;
70 wl_list_for_each(dev, &server.input->devices, link) { 87 wl_list_for_each(dev, &server.input->devices, link) {
71 if (strcmp(ic->identifier, "*") != 0 && 88 if (strcmp(ic->identifier, "*") != 0 &&
@@ -76,12 +93,22 @@ struct cmd_results *input_cmd_xkb_switch_layout(int argc, char **argv) {
76 if (dev->wlr_device->type != WLR_INPUT_DEVICE_KEYBOARD) { 93 if (dev->wlr_device->type != WLR_INPUT_DEVICE_KEYBOARD) {
77 continue; 94 continue;
78 } 95 }
96
97 struct xkb_switch_layout_action *action =
98 &actions[actions_len++];
99
100 action->keyboard = wlr_keyboard_from_input_device(dev->wlr_device);
79 if (relative) { 101 if (relative) {
80 switch_layout_relative(dev->wlr_device->keyboard, relative); 102 action->layout = get_layout_relative(action->keyboard, relative);
81 } else { 103 } else {
82 switch_layout(dev->wlr_device->keyboard, layout); 104 action->layout = layout;
83 } 105 }
84 } 106 }
85 107
108 for (size_t i = 0; i < actions_len; i++) {
109 switch_layout(actions[i].keyboard, actions[i].layout);
110 }
111 free(actions);
112
86 return cmd_results_new(CMD_SUCCESS, NULL); 113 return cmd_results_new(CMD_SUCCESS, NULL);
87} 114}
diff --git a/sway/commands/input/xkb_variant.c b/sway/commands/input/xkb_variant.c
index d0e21d77..2d14ea9c 100644
--- a/sway/commands/input/xkb_variant.c
+++ b/sway/commands/input/xkb_variant.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include "sway/config.h" 1#include "sway/config.h"
3#include "sway/commands.h" 2#include "sway/commands.h"
4#include "log.h" 3#include "log.h"
diff --git a/sway/commands/layout.c b/sway/commands/layout.c
index f2af183b..12ce4839 100644
--- a/sway/commands/layout.c
+++ b/sway/commands/layout.c
@@ -133,7 +133,7 @@ struct cmd_results *cmd_layout(int argc, char **argv) {
133 133
134 // Operate on parent container, like i3. 134 // Operate on parent container, like i3.
135 if (container) { 135 if (container) {
136 container = container->parent; 136 container = container->pending.parent;
137 } 137 }
138 138
139 // We could be working with a container OR a workspace. These are different 139 // We could be working with a container OR a workspace. These are different
@@ -142,10 +142,10 @@ struct cmd_results *cmd_layout(int argc, char **argv) {
142 enum sway_container_layout new_layout = L_NONE; 142 enum sway_container_layout new_layout = L_NONE;
143 enum sway_container_layout old_layout = L_NONE; 143 enum sway_container_layout old_layout = L_NONE;
144 if (container) { 144 if (container) {
145 old_layout = container->layout; 145 old_layout = container->pending.layout;
146 new_layout = get_layout(argc, argv, 146 new_layout = get_layout(argc, argv,
147 container->layout, container->prev_split_layout, 147 container->pending.layout, container->prev_split_layout,
148 container->workspace->output); 148 container->pending.workspace->output);
149 } else { 149 } else {
150 old_layout = workspace->layout; 150 old_layout = workspace->layout;
151 new_layout = get_layout(argc, argv, 151 new_layout = get_layout(argc, argv,
@@ -153,20 +153,20 @@ struct cmd_results *cmd_layout(int argc, char **argv) {
153 workspace->output); 153 workspace->output);
154 } 154 }
155 if (new_layout == L_NONE) { 155 if (new_layout == L_NONE) {
156 return cmd_results_new(CMD_INVALID, expected_syntax); 156 return cmd_results_new(CMD_INVALID, "%s", expected_syntax);
157 } 157 }
158 if (new_layout != old_layout) { 158 if (new_layout != old_layout) {
159 if (container) { 159 if (container) {
160 if (old_layout != L_TABBED && old_layout != L_STACKED) { 160 if (old_layout != L_TABBED && old_layout != L_STACKED) {
161 container->prev_split_layout = old_layout; 161 container->prev_split_layout = old_layout;
162 } 162 }
163 container->layout = new_layout; 163 container->pending.layout = new_layout;
164 container_update_representation(container); 164 container_update_representation(container);
165 } else if (config->handler_context.container) { 165 } else if (config->handler_context.container) {
166 // i3 avoids changing workspace layouts with a new container 166 // i3 avoids changing workspace layouts with a new container
167 // https://github.com/i3/i3/blob/3cd1c45eba6de073bc4300eebb4e1cc1a0c4479a/src/con.c#L1817 167 // https://github.com/i3/i3/blob/3cd1c45eba6de073bc4300eebb4e1cc1a0c4479a/src/con.c#L1817
168 container = workspace_wrap_children(workspace); 168 container = workspace_wrap_children(workspace);
169 container->layout = new_layout; 169 container->pending.layout = new_layout;
170 container_update_representation(container); 170 container_update_representation(container);
171 } else { 171 } else {
172 if (old_layout != L_TABBED && old_layout != L_STACKED) { 172 if (old_layout != L_TABBED && old_layout != L_STACKED) {
diff --git a/sway/commands/mark.c b/sway/commands/mark.c
index aa5f185c..2bfc86b3 100644
--- a/sway/commands/mark.c
+++ b/sway/commands/mark.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <string.h> 1#include <string.h>
3#include "sway/commands.h" 2#include "sway/commands.h"
4#include "sway/config.h" 3#include "sway/config.h"
@@ -59,7 +58,7 @@ struct cmd_results *cmd_mark(int argc, char **argv) {
59 } 58 }
60 59
61 free(mark); 60 free(mark);
62 container_update_marks_textures(container); 61 container_update_marks(container);
63 if (container->view) { 62 if (container->view) {
64 view_execute_criteria(container->view); 63 view_execute_criteria(container->view);
65 } 64 }
diff --git a/sway/commands/mode.c b/sway/commands/mode.c
index a5871dab..b3216967 100644
--- a/sway/commands/mode.c
+++ b/sway/commands/mode.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <stdbool.h> 1#include <stdbool.h>
3#include <string.h> 2#include <string.h>
4#include "sway/commands.h" 3#include "sway/commands.h"
@@ -9,12 +8,14 @@
9#include "stringop.h" 8#include "stringop.h"
10 9
11// Must be in order for the bsearch 10// Must be in order for the bsearch
12static 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 f8f89f18..8addf26e 100644
--- a/sway/commands/move.c
+++ b/sway/commands/move.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <ctype.h> 1#include <ctype.h>
3#include <math.h> 2#include <math.h>
4#include <stdbool.h> 3#include <stdbool.h>
@@ -113,8 +112,8 @@ static void container_move_to_container_from_direction(
113 struct sway_container *container, struct sway_container *destination, 112 struct sway_container *container, struct sway_container *destination,
114 enum wlr_direction move_dir) { 113 enum wlr_direction move_dir) {
115 if (destination->view) { 114 if (destination->view) {
116 if (destination->parent == container->parent && 115 if (destination->pending.parent == container->pending.parent &&
117 destination->workspace == container->workspace) { 116 destination->pending.workspace == container->pending.workspace) {
118 sway_log(SWAY_DEBUG, "Swapping siblings"); 117 sway_log(SWAY_DEBUG, "Swapping siblings");
119 list_t *siblings = container_get_siblings(container); 118 list_t *siblings = container_get_siblings(container);
120 int container_index = list_find(siblings, container); 119 int container_index = list_find(siblings, container);
@@ -126,28 +125,28 @@ static void container_move_to_container_from_direction(
126 int offset = 125 int offset =
127 move_dir == WLR_DIRECTION_LEFT || move_dir == WLR_DIRECTION_UP; 126 move_dir == WLR_DIRECTION_LEFT || move_dir == WLR_DIRECTION_UP;
128 int index = container_sibling_index(destination) + offset; 127 int index = container_sibling_index(destination) + offset;
129 if (destination->parent) { 128 if (destination->pending.parent) {
130 container_insert_child(destination->parent, container, index); 129 container_insert_child(destination->pending.parent, container, index);
131 } else { 130 } else {
132 workspace_insert_tiling(destination->workspace, 131 workspace_insert_tiling(destination->pending.workspace,
133 container, index); 132 container, index);
134 } 133 }
135 container->width = container->height = 0; 134 container->pending.width = container->pending.height = 0;
136 container->width_fraction = container->height_fraction = 0; 135 container->width_fraction = container->height_fraction = 0;
137 workspace_squash(destination->workspace); 136 workspace_squash(destination->pending.workspace);
138 } 137 }
139 return; 138 return;
140 } 139 }
141 140
142 if (is_parallel(destination->layout, move_dir)) { 141 if (is_parallel(destination->pending.layout, move_dir)) {
143 sway_log(SWAY_DEBUG, "Reparenting container (parallel)"); 142 sway_log(SWAY_DEBUG, "Reparenting container (parallel)");
144 int index = 143 int index =
145 move_dir == WLR_DIRECTION_RIGHT || move_dir == WLR_DIRECTION_DOWN ? 144 move_dir == WLR_DIRECTION_RIGHT || move_dir == WLR_DIRECTION_DOWN ?
146 0 : destination->children->length; 145 0 : destination->pending.children->length;
147 container_insert_child(destination, container, index); 146 container_insert_child(destination, container, index);
148 container->width = container->height = 0; 147 container->pending.width = container->pending.height = 0;
149 container->width_fraction = container->height_fraction = 0; 148 container->width_fraction = container->height_fraction = 0;
150 workspace_squash(destination->workspace); 149 workspace_squash(destination->pending.workspace);
151 return; 150 return;
152 } 151 }
153 152
@@ -168,7 +167,7 @@ static void container_move_to_container_from_direction(
168static void container_move_to_workspace_from_direction( 167static void container_move_to_workspace_from_direction(
169 struct sway_container *container, struct sway_workspace *workspace, 168 struct sway_container *container, struct sway_workspace *workspace,
170 enum wlr_direction move_dir) { 169 enum wlr_direction move_dir) {
171 container->width = container->height = 0; 170 container->pending.width = container->pending.height = 0;
172 container->width_fraction = container->height_fraction = 0; 171 container->width_fraction = container->height_fraction = 0;
173 172
174 if (is_parallel(workspace->layout, move_dir)) { 173 if (is_parallel(workspace->layout, move_dir)) {
@@ -188,8 +187,8 @@ static void container_move_to_workspace_from_direction(
188 workspace_add_tiling(workspace, container); 187 workspace_add_tiling(workspace, container);
189 return; 188 return;
190 } 189 }
191 while (focus_inactive->parent) { 190 while (focus_inactive->pending.parent) {
192 focus_inactive = focus_inactive->parent; 191 focus_inactive = focus_inactive->pending.parent;
193 } 192 }
194 container_move_to_container_from_direction(container, focus_inactive, 193 container_move_to_container_from_direction(container, focus_inactive,
195 move_dir); 194 move_dir);
@@ -197,25 +196,33 @@ static void container_move_to_workspace_from_direction(
197 196
198static void container_move_to_workspace(struct sway_container *container, 197static void container_move_to_workspace(struct sway_container *container,
199 struct sway_workspace *workspace) { 198 struct sway_workspace *workspace) {
200 if (container->workspace == workspace) { 199 if (container->pending.workspace == workspace) {
201 return; 200 return;
202 } 201 }
203 struct sway_workspace *old_workspace = container->workspace; 202 struct sway_workspace *old_workspace = container->pending.workspace;
204 if (container_is_floating(container)) { 203 if (container_is_floating(container)) {
205 struct sway_output *old_output = container->workspace->output; 204 struct sway_output *old_output = container->pending.workspace->output;
206 container_detach(container); 205 container_detach(container);
207 workspace_add_floating(workspace, container); 206 workspace_add_floating(workspace, container);
208 container_handle_fullscreen_reparent(container); 207 container_handle_fullscreen_reparent(container);
209 // If changing output, center it within the workspace 208 // If changing output, adjust the coordinates of the window.
210 if (old_output != workspace->output && !container->fullscreen_mode) { 209 if (old_output != workspace->output && !container->pending.fullscreen_mode) {
211 container_floating_move_to_center(container); 210 struct wlr_box workspace_box, old_workspace_box;
211 workspace_get_box(workspace, &workspace_box);
212 workspace_get_box(old_workspace, &old_workspace_box);
213 floating_fix_coordinates(container, &old_workspace_box, &workspace_box);
214 if (container->scratchpad && workspace->output) {
215 struct wlr_box output_box;
216 output_get_box(workspace->output, &output_box);
217 container->transform = workspace_box;
218 }
212 } 219 }
213 } else { 220 } else {
214 container_detach(container); 221 container_detach(container);
215 if (workspace_is_empty(workspace) && container->children) { 222 if (workspace_is_empty(workspace) && container->pending.children) {
216 workspace_unwrap_children(workspace, container); 223 workspace_unwrap_children(workspace, container);
217 } else { 224 } else {
218 container->width = container->height = 0; 225 container->pending.width = container->pending.height = 0;
219 container->width_fraction = container->height_fraction = 0; 226 container->width_fraction = container->height_fraction = 0;
220 workspace_add_tiling(workspace, container); 227 workspace_add_tiling(workspace, container);
221 } 228 }
@@ -237,13 +244,13 @@ static void container_move_to_container(struct sway_container *container,
237 return; 244 return;
238 } 245 }
239 if (container_is_floating(container)) { 246 if (container_is_floating(container)) {
240 container_move_to_workspace(container, destination->workspace); 247 container_move_to_workspace(container, destination->pending.workspace);
241 return; 248 return;
242 } 249 }
243 struct sway_workspace *old_workspace = container->workspace; 250 struct sway_workspace *old_workspace = container->pending.workspace;
244 251
245 container_detach(container); 252 container_detach(container);
246 container->width = container->height = 0; 253 container->pending.width = container->pending.height = 0;
247 container->width_fraction = container->height_fraction = 0; 254 container->width_fraction = container->height_fraction = 0;
248 255
249 if (destination->view) { 256 if (destination->view) {
@@ -256,12 +263,12 @@ static void container_move_to_container(struct sway_container *container,
256 ipc_event_window(container, "move"); 263 ipc_event_window(container, "move");
257 } 264 }
258 265
259 if (destination->workspace) { 266 if (destination->pending.workspace) {
260 workspace_focus_fullscreen(destination->workspace); 267 workspace_focus_fullscreen(destination->pending.workspace);
261 workspace_detect_urgent(destination->workspace); 268 workspace_detect_urgent(destination->pending.workspace);
262 } 269 }
263 270
264 if (old_workspace && old_workspace != destination->workspace) { 271 if (old_workspace && old_workspace != destination->pending.workspace) {
265 workspace_detect_urgent(old_workspace); 272 workspace_detect_urgent(old_workspace);
266 } 273 }
267} 274}
@@ -275,7 +282,7 @@ static bool container_move_to_next_output(struct sway_container *container,
275 if (!sway_assert(ws, "Expected output to have a workspace")) { 282 if (!sway_assert(ws, "Expected output to have a workspace")) {
276 return false; 283 return false;
277 } 284 }
278 switch (container->fullscreen_mode) { 285 switch (container->pending.fullscreen_mode) {
279 case FULLSCREEN_NONE: 286 case FULLSCREEN_NONE:
280 container_move_to_workspace_from_direction(container, ws, move_dir); 287 container_move_to_workspace_from_direction(container, ws, move_dir);
281 return true; 288 return true;
@@ -293,12 +300,12 @@ static bool container_move_to_next_output(struct sway_container *container,
293static bool container_move_in_direction(struct sway_container *container, 300static bool container_move_in_direction(struct sway_container *container,
294 enum wlr_direction move_dir) { 301 enum wlr_direction move_dir) {
295 // If moving a fullscreen view, only consider outputs 302 // If moving a fullscreen view, only consider outputs
296 switch (container->fullscreen_mode) { 303 switch (container->pending.fullscreen_mode) {
297 case FULLSCREEN_NONE: 304 case FULLSCREEN_NONE:
298 break; 305 break;
299 case FULLSCREEN_WORKSPACE: 306 case FULLSCREEN_WORKSPACE:
300 return container_move_to_next_output(container, 307 return container_move_to_next_output(container,
301 container->workspace->output, move_dir); 308 container->pending.workspace->output, move_dir);
302 case FULLSCREEN_GLOBAL: 309 case FULLSCREEN_GLOBAL:
303 return false; 310 return false;
304 } 311 }
@@ -317,26 +324,26 @@ static bool container_move_in_direction(struct sway_container *container,
317 while (!ancestor) { 324 while (!ancestor) {
318 // Don't allow containers to move out of their 325 // Don't allow containers to move out of their
319 // fullscreen or floating parent 326 // fullscreen or floating parent
320 if (current->fullscreen_mode || container_is_floating(current)) { 327 if (current->pending.fullscreen_mode || container_is_floating(current)) {
321 return false; 328 return false;
322 } 329 }
323 330
324 enum sway_container_layout parent_layout = container_parent_layout(current); 331 enum sway_container_layout parent_layout = container_parent_layout(current);
325 if (!is_parallel(parent_layout, move_dir)) { 332 if (!is_parallel(parent_layout, move_dir)) {
326 if (!current->parent) { 333 if (!current->pending.parent) {
327 // No parallel parent, so we reorient the workspace 334 // No parallel parent, so we reorient the workspace
328 current = workspace_wrap_children(current->workspace); 335 current = workspace_wrap_children(current->pending.workspace);
329 current->workspace->layout = 336 current->pending.workspace->layout =
330 move_dir == WLR_DIRECTION_LEFT || 337 move_dir == WLR_DIRECTION_LEFT ||
331 move_dir == WLR_DIRECTION_RIGHT ? 338 move_dir == WLR_DIRECTION_RIGHT ?
332 L_HORIZ : L_VERT; 339 L_HORIZ : L_VERT;
333 container->height = container->width = 0; 340 container->pending.height = container->pending.width = 0;
334 container->height_fraction = container->width_fraction = 0; 341 container->height_fraction = container->width_fraction = 0;
335 workspace_update_representation(current->workspace); 342 workspace_update_representation(current->pending.workspace);
336 wrapped = true; 343 wrapped = true;
337 } else { 344 } else {
338 // Keep looking for a parallel parent 345 // Keep looking for a parallel parent
339 current = current->parent; 346 current = current->pending.parent;
340 } 347 }
341 continue; 348 continue;
342 } 349 }
@@ -356,14 +363,14 @@ static bool container_move_in_direction(struct sway_container *container,
356 container_move_to_container_from_direction(container, 363 container_move_to_container_from_direction(container,
357 target, move_dir); 364 target, move_dir);
358 return true; 365 return true;
359 } else if (!container->parent) { 366 } else if (!container->pending.parent) {
360 // Container is at workspace level so we move it to the 367 // Container is at workspace level so we move it to the
361 // next workspace if possible 368 // next workspace if possible
362 return container_move_to_next_output(container, 369 return container_move_to_next_output(container,
363 current->workspace->output, move_dir); 370 current->pending.workspace->output, move_dir);
364 } else { 371 } else {
365 // Container has escaped its immediate parallel parent 372 // Container has escaped its immediate parallel parent
366 current = current->parent; 373 current = current->pending.parent;
367 continue; 374 continue;
368 } 375 }
369 } 376 }
@@ -377,31 +384,31 @@ static bool container_move_in_direction(struct sway_container *container,
377 container_move_to_container_from_direction(container, 384 container_move_to_container_from_direction(container,
378 target, move_dir); 385 target, move_dir);
379 return true; 386 return true;
380 } else if (!wrapped && !container->parent->parent && 387 } else if (!wrapped && !container->pending.parent->pending.parent &&
381 container->parent->children->length == 1) { 388 container->pending.parent->pending.children->length == 1) {
382 // Treat singleton children as if they are at workspace level like i3 389 // Treat singleton children as if they are at workspace level like i3
383 // https://github.com/i3/i3/blob/1d9160f2d247dbaa83fb62f02fd7041dec767fc2/src/move.c#L367 390 // https://github.com/i3/i3/blob/1d9160f2d247dbaa83fb62f02fd7041dec767fc2/src/move.c#L367
384 return container_move_to_next_output(container, 391 return container_move_to_next_output(container,
385 ancestor->workspace->output, move_dir); 392 ancestor->pending.workspace->output, move_dir);
386 } else { 393 } else {
387 // Container will be promoted 394 // Container will be promoted
388 struct sway_container *old_parent = container->parent; 395 struct sway_container *old_parent = container->pending.parent;
389 if (ancestor->parent) { 396 if (ancestor->pending.parent) {
390 // Container will move in with its parent 397 // Container will move in with its parent
391 container_insert_child(ancestor->parent, container, 398 container_insert_child(ancestor->pending.parent, container,
392 index + (offs < 0 ? 0 : 1)); 399 index + (offs < 0 ? 0 : 1));
393 } else { 400 } else {
394 // Container will move to workspace level, 401 // Container will move to workspace level,
395 // may be re-split by workspace_layout 402 // may be re-split by workspace_layout
396 workspace_insert_tiling(ancestor->workspace, container, 403 workspace_insert_tiling(ancestor->pending.workspace, container,
397 index + (offs < 0 ? 0 : 1)); 404 index + (offs < 0 ? 0 : 1));
398 } 405 }
399 ancestor->height = ancestor->width = 0; 406 ancestor->pending.height = ancestor->pending.width = 0;
400 ancestor->height_fraction = ancestor->width_fraction = 0; 407 ancestor->height_fraction = ancestor->width_fraction = 0;
401 if (old_parent) { 408 if (old_parent) {
402 container_reap_empty(old_parent); 409 container_reap_empty(old_parent);
403 } 410 }
404 workspace_squash(container->workspace); 411 workspace_squash(container->pending.workspace);
405 return true; 412 return true;
406 } 413 }
407} 414}
@@ -427,14 +434,14 @@ static struct cmd_results *cmd_move_container(bool no_auto_back_and_forth,
427 container = workspace_wrap_children(workspace); 434 container = workspace_wrap_children(workspace);
428 } 435 }
429 436
430 if (container->fullscreen_mode == FULLSCREEN_GLOBAL) { 437 if (container->pending.fullscreen_mode == FULLSCREEN_GLOBAL) {
431 return cmd_results_new(CMD_FAILURE, 438 return cmd_results_new(CMD_FAILURE,
432 "Can't move fullscreen global container"); 439 "Can't move fullscreen global container");
433 } 440 }
434 441
435 struct sway_seat *seat = config->handler_context.seat; 442 struct sway_seat *seat = config->handler_context.seat;
436 struct sway_container *old_parent = container->parent; 443 struct sway_container *old_parent = container->pending.parent;
437 struct sway_workspace *old_ws = container->workspace; 444 struct sway_workspace *old_ws = container->pending.workspace;
438 struct sway_output *old_output = old_ws ? old_ws->output : NULL; 445 struct sway_output *old_output = old_ws ? old_ws->output : NULL;
439 struct sway_node *destination = NULL; 446 struct sway_node *destination = NULL;
440 447
@@ -462,7 +469,7 @@ static struct cmd_results *cmd_move_container(bool no_auto_back_and_forth,
462 if (strcasecmp(argv[1], "number") == 0) { 469 if (strcasecmp(argv[1], "number") == 0) {
463 // move [window|container] [to] "workspace number x" 470 // move [window|container] [to] "workspace number x"
464 if (argc < 3) { 471 if (argc < 3) {
465 return cmd_results_new(CMD_INVALID, expected_syntax); 472 return cmd_results_new(CMD_INVALID, "%s", expected_syntax);
466 } 473 }
467 if (!isdigit(argv[2][0])) { 474 if (!isdigit(argv[2][0])) {
468 return cmd_results_new(CMD_INVALID, 475 return cmd_results_new(CMD_INVALID,
@@ -508,7 +515,7 @@ static struct cmd_results *cmd_move_container(bool no_auto_back_and_forth,
508 destination = dst ? &dst->node : &ws->node; 515 destination = dst ? &dst->node : &ws->node;
509 } else if (strcasecmp(argv[0], "output") == 0) { 516 } else if (strcasecmp(argv[0], "output") == 0) {
510 struct sway_output *new_output = output_in_direction(argv[1], 517 struct sway_output *new_output = output_in_direction(argv[1],
511 old_output, container->x, container->y); 518 old_output, container->pending.x, container->pending.y);
512 if (!new_output) { 519 if (!new_output) {
513 return cmd_results_new(CMD_FAILURE, 520 return cmd_results_new(CMD_FAILURE,
514 "Can't find output with name/direction '%s'", argv[1]); 521 "Can't find output with name/direction '%s'", argv[1]);
@@ -522,7 +529,7 @@ static struct cmd_results *cmd_move_container(bool no_auto_back_and_forth,
522 } 529 }
523 destination = &dest_con->node; 530 destination = &dest_con->node;
524 } else { 531 } else {
525 return cmd_results_new(CMD_INVALID, expected_syntax); 532 return cmd_results_new(CMD_INVALID, "%s", expected_syntax);
526 } 533 }
527 534
528 if (destination->type == N_CONTAINER && 535 if (destination->type == N_CONTAINER &&
@@ -686,6 +693,9 @@ static struct cmd_results *cmd_move_workspace(int argc, char **argv) {
686 arrange_output(old_output); 693 arrange_output(old_output);
687 arrange_output(new_output); 694 arrange_output(new_output);
688 695
696 struct sway_seat *seat = config->handler_context.seat;
697 seat_consider_warp_to_focus(seat);
698
689 return cmd_results_new(CMD_SUCCESS, NULL); 699 return cmd_results_new(CMD_SUCCESS, NULL);
690} 700}
691 701
@@ -706,12 +716,12 @@ static struct cmd_results *cmd_move_in_direction(
706 "Cannot move workspaces in a direction"); 716 "Cannot move workspaces in a direction");
707 } 717 }
708 if (container_is_floating(container)) { 718 if (container_is_floating(container)) {
709 if (container->fullscreen_mode) { 719 if (container->pending.fullscreen_mode) {
710 return cmd_results_new(CMD_FAILURE, 720 return cmd_results_new(CMD_FAILURE,
711 "Cannot move fullscreen floating container"); 721 "Cannot move fullscreen floating container");
712 } 722 }
713 double lx = container->x; 723 double lx = container->pending.x;
714 double ly = container->y; 724 double ly = container->pending.y;
715 switch (direction) { 725 switch (direction) {
716 case WLR_DIRECTION_LEFT: 726 case WLR_DIRECTION_LEFT:
717 lx -= move_amt; 727 lx -= move_amt;
@@ -729,8 +739,8 @@ static struct cmd_results *cmd_move_in_direction(
729 container_floating_move_to(container, lx, ly); 739 container_floating_move_to(container, lx, ly);
730 return cmd_results_new(CMD_SUCCESS, NULL); 740 return cmd_results_new(CMD_SUCCESS, NULL);
731 } 741 }
732 struct sway_workspace *old_ws = container->workspace; 742 struct sway_workspace *old_ws = container->pending.workspace;
733 struct sway_container *old_parent = container->parent; 743 struct sway_container *old_parent = container->pending.parent;
734 744
735 if (!container_move_in_direction(container, direction)) { 745 if (!container_move_in_direction(container, direction)) {
736 // Container didn't move 746 // Container didn't move
@@ -744,7 +754,7 @@ static struct cmd_results *cmd_move_in_direction(
744 workspace_consider_destroy(old_ws); 754 workspace_consider_destroy(old_ws);
745 } 755 }
746 756
747 struct sway_workspace *new_ws = container->workspace; 757 struct sway_workspace *new_ws = container->pending.workspace;
748 758
749 if (root->fullscreen_global) { 759 if (root->fullscreen_global) {
750 arrange_root(); 760 arrange_root();
@@ -759,15 +769,6 @@ static struct cmd_results *cmd_move_in_direction(
759 ipc_event_window(container, "move"); 769 ipc_event_window(container, "move");
760 } 770 }
761 771
762 // Hack to re-focus container
763 seat_set_raw_focus(config->handler_context.seat, &new_ws->node);
764 seat_set_focus_container(config->handler_context.seat, container);
765
766 if (old_ws != new_ws) {
767 ipc_event_workspace(old_ws, new_ws, "focus");
768 workspace_detect_urgent(old_ws);
769 workspace_detect_urgent(new_ws);
770 }
771 container_end_mouse_operation(container); 772 container_end_mouse_operation(container);
772 773
773 return cmd_results_new(CMD_SUCCESS, NULL); 774 return cmd_results_new(CMD_SUCCESS, NULL);
@@ -781,22 +782,22 @@ static struct cmd_results *cmd_move_to_position_pointer(
781 } 782 }
782 struct wlr_cursor *cursor = seat->cursor->cursor; 783 struct wlr_cursor *cursor = seat->cursor->cursor;
783 /* Determine where to put the window. */ 784 /* Determine where to put the window. */
784 double lx = cursor->x - container->width / 2; 785 double lx = cursor->x - container->pending.width / 2;
785 double ly = cursor->y - container->height / 2; 786 double ly = cursor->y - container->pending.height / 2;
786 787
787 /* Correct target coordinates to be in bounds (on screen). */ 788 /* Correct target coordinates to be in bounds (on screen). */
788 struct wlr_output *output = wlr_output_layout_output_at( 789 struct wlr_output *output = wlr_output_layout_output_at(
789 root->output_layout, cursor->x, cursor->y); 790 root->output_layout, cursor->x, cursor->y);
790 if (output) { 791 if (output) {
791 struct wlr_box *box = 792 struct wlr_box box;
792 wlr_output_layout_get_box(root->output_layout, output); 793 wlr_output_layout_get_box(root->output_layout, output, &box);
793 lx = fmax(lx, box->x); 794 lx = fmax(lx, box.x);
794 ly = fmax(ly, box->y); 795 ly = fmax(ly, box.y);
795 if (lx + container->width > box->x + box->width) { 796 if (lx + container->pending.width > box.x + box.width) {
796 lx = box->x + box->width - container->width; 797 lx = box.x + box.width - container->pending.width;
797 } 798 }
798 if (ly + container->height > box->y + box->height) { 799 if (ly + container->pending.height > box.y + box.height) {
799 ly = box->y + box->height - container->height; 800 ly = box.y + box.height - container->pending.height;
800 } 801 }
801 } 802 }
802 803
@@ -818,7 +819,7 @@ static struct cmd_results *cmd_move_to_position(int argc, char **argv) {
818 } 819 }
819 820
820 if (!argc) { 821 if (!argc) {
821 return cmd_results_new(CMD_INVALID, expected_position_syntax); 822 return cmd_results_new(CMD_INVALID, "%s", expected_position_syntax);
822 } 823 }
823 824
824 bool absolute = false; 825 bool absolute = false;
@@ -828,41 +829,41 @@ static struct cmd_results *cmd_move_to_position(int argc, char **argv) {
828 ++argv; 829 ++argv;
829 } 830 }
830 if (!argc) { 831 if (!argc) {
831 return cmd_results_new(CMD_INVALID, expected_position_syntax); 832 return cmd_results_new(CMD_INVALID, "%s", expected_position_syntax);
832 } 833 }
833 if (strcmp(argv[0], "position") == 0) { 834 if (strcmp(argv[0], "position") == 0) {
834 --argc; 835 --argc;
835 ++argv; 836 ++argv;
836 } 837 }
837 if (!argc) { 838 if (!argc) {
838 return cmd_results_new(CMD_INVALID, expected_position_syntax); 839 return cmd_results_new(CMD_INVALID, "%s", expected_position_syntax);
839 } 840 }
840 if (strcmp(argv[0], "cursor") == 0 || strcmp(argv[0], "mouse") == 0 || 841 if (strcmp(argv[0], "cursor") == 0 || strcmp(argv[0], "mouse") == 0 ||
841 strcmp(argv[0], "pointer") == 0) { 842 strcmp(argv[0], "pointer") == 0) {
842 if (absolute) { 843 if (absolute) {
843 return cmd_results_new(CMD_INVALID, expected_position_syntax); 844 return cmd_results_new(CMD_INVALID, "%s", expected_position_syntax);
844 } 845 }
845 return cmd_move_to_position_pointer(container); 846 return cmd_move_to_position_pointer(container);
846 } else if (strcmp(argv[0], "center") == 0) { 847 } else if (strcmp(argv[0], "center") == 0) {
847 double lx, ly; 848 double lx, ly;
848 if (absolute) { 849 if (absolute) {
849 lx = root->x + (root->width - container->width) / 2; 850 lx = root->x + (root->width - container->pending.width) / 2;
850 ly = root->y + (root->height - container->height) / 2; 851 ly = root->y + (root->height - container->pending.height) / 2;
851 } else { 852 } else {
852 struct sway_workspace *ws = container->workspace; 853 struct sway_workspace *ws = container->pending.workspace;
853 if (!ws) { 854 if (!ws) {
854 struct sway_seat *seat = config->handler_context.seat; 855 struct sway_seat *seat = config->handler_context.seat;
855 ws = seat_get_focused_workspace(seat); 856 ws = seat_get_focused_workspace(seat);
856 } 857 }
857 lx = ws->x + (ws->width - container->width) / 2; 858 lx = ws->x + (ws->width - container->pending.width) / 2;
858 ly = ws->y + (ws->height - container->height) / 2; 859 ly = ws->y + (ws->height - container->pending.height) / 2;
859 } 860 }
860 container_floating_move_to(container, lx, ly); 861 container_floating_move_to(container, lx, ly);
861 return cmd_results_new(CMD_SUCCESS, NULL); 862 return cmd_results_new(CMD_SUCCESS, NULL);
862 } 863 }
863 864
864 if (argc < 2) { 865 if (argc < 2) {
865 return cmd_results_new(CMD_FAILURE, expected_position_syntax); 866 return cmd_results_new(CMD_FAILURE, "%s", expected_position_syntax);
866 } 867 }
867 868
868 struct movement_amount lx = { .amount = 0, .unit = MOVEMENT_UNIT_INVALID }; 869 struct movement_amount lx = { .amount = 0, .unit = MOVEMENT_UNIT_INVALID };
@@ -874,19 +875,23 @@ static struct cmd_results *cmd_move_to_position(int argc, char **argv) {
874 return cmd_results_new(CMD_INVALID, "Invalid x position specified"); 875 return cmd_results_new(CMD_INVALID, "Invalid x position specified");
875 } 876 }
876 877
878 if (argc < 1) {
879 return cmd_results_new(CMD_FAILURE, "%s", expected_position_syntax);
880 }
881
877 struct movement_amount ly = { .amount = 0, .unit = MOVEMENT_UNIT_INVALID }; 882 struct movement_amount ly = { .amount = 0, .unit = MOVEMENT_UNIT_INVALID };
878 // Y direction 883 // Y direction
879 num_consumed_args = parse_movement_amount(argc, argv, &ly); 884 num_consumed_args = parse_movement_amount(argc, argv, &ly);
880 argc -= num_consumed_args; 885 argc -= num_consumed_args;
881 argv += num_consumed_args; 886 argv += num_consumed_args;
882 if (argc > 0) { 887 if (argc > 0) {
883 return cmd_results_new(CMD_INVALID, expected_position_syntax); 888 return cmd_results_new(CMD_INVALID, "%s", expected_position_syntax);
884 } 889 }
885 if (ly.unit == MOVEMENT_UNIT_INVALID) { 890 if (ly.unit == MOVEMENT_UNIT_INVALID) {
886 return cmd_results_new(CMD_INVALID, "Invalid y position specified"); 891 return cmd_results_new(CMD_INVALID, "Invalid y position specified");
887 } 892 }
888 893
889 struct sway_workspace *ws = container->workspace; 894 struct sway_workspace *ws = container->pending.workspace;
890 if (!ws) { 895 if (!ws) {
891 struct sway_seat *seat = config->handler_context.seat; 896 struct sway_seat *seat = config->handler_context.seat;
892 ws = seat_get_focused_workspace(seat); 897 ws = seat_get_focused_workspace(seat);
@@ -960,14 +965,14 @@ static struct cmd_results *cmd_move_to_scratchpad(void) {
960 // If the container is in a floating split container, 965 // If the container is in a floating split container,
961 // operate on the split container instead of the child. 966 // operate on the split container instead of the child.
962 if (container_is_floating_or_child(con)) { 967 if (container_is_floating_or_child(con)) {
963 while (con->parent) { 968 while (con->pending.parent) {
964 con = con->parent; 969 con = con->pending.parent;
965 } 970 }
966 } 971 }
967 972
968 if (!con->scratchpad) { 973 if (!con->scratchpad) {
969 root_scratchpad_add_container(con, NULL); 974 root_scratchpad_add_container(con, NULL);
970 } else if (con->workspace) { 975 } else if (con->pending.workspace) {
971 root_scratchpad_hide(con); 976 root_scratchpad_hide(con);
972 } 977 }
973 return cmd_results_new(CMD_SUCCESS, NULL); 978 return cmd_results_new(CMD_SUCCESS, NULL);
@@ -1026,13 +1031,13 @@ struct cmd_results *cmd_move(int argc, char **argv) {
1026 } 1031 }
1027 1032
1028 if (!argc) { 1033 if (!argc) {
1029 return cmd_results_new(CMD_INVALID, expected_full_syntax); 1034 return cmd_results_new(CMD_INVALID, "%s", expected_full_syntax);
1030 } 1035 }
1031 1036
1032 // Only `move [window|container] [to] workspace` supports 1037 // Only `move [window|container] [to] workspace` supports
1033 // `--no-auto-back-and-forth` so treat others as invalid syntax 1038 // `--no-auto-back-and-forth` so treat others as invalid syntax
1034 if (no_auto_back_and_forth && strcasecmp(argv[0], "workspace") != 0) { 1039 if (no_auto_back_and_forth && strcasecmp(argv[0], "workspace") != 0) {
1035 return cmd_results_new(CMD_INVALID, expected_full_syntax); 1040 return cmd_results_new(CMD_INVALID, "%s", expected_full_syntax);
1036 } 1041 }
1037 1042
1038 if (strcasecmp(argv[0], "workspace") == 0 || 1043 if (strcasecmp(argv[0], "workspace") == 0 ||
@@ -1046,5 +1051,5 @@ struct cmd_results *cmd_move(int argc, char **argv) {
1046 strcasecmp(argv[1], "position") == 0)) { 1051 strcasecmp(argv[1], "position") == 0)) {
1047 return cmd_move_to_position(argc, argv); 1052 return cmd_move_to_position(argc, argv);
1048 } 1053 }
1049 return cmd_results_new(CMD_INVALID, expected_full_syntax); 1054 return cmd_results_new(CMD_INVALID, "%s", expected_full_syntax);
1050} 1055}
diff --git a/sway/commands/no_focus.c b/sway/commands/no_focus.c
index 2001e04f..ccfdec82 100644
--- a/sway/commands/no_focus.c
+++ b/sway/commands/no_focus.c
@@ -13,7 +13,7 @@ struct cmd_results *cmd_no_focus(int argc, char **argv) {
13 char *err_str = NULL; 13 char *err_str = NULL;
14 struct criteria *criteria = criteria_parse(argv[0], &err_str); 14 struct criteria *criteria = criteria_parse(argv[0], &err_str);
15 if (!criteria) { 15 if (!criteria) {
16 error = cmd_results_new(CMD_INVALID, err_str); 16 error = cmd_results_new(CMD_INVALID, "%s", err_str);
17 free(err_str); 17 free(err_str);
18 return error; 18 return error;
19 } 19 }
diff --git a/sway/commands/opacity.c b/sway/commands/opacity.c
index 96e6228e..610cecc6 100644
--- a/sway/commands/opacity.c
+++ b/sway/commands/opacity.c
@@ -37,6 +37,7 @@ struct cmd_results *cmd_opacity(int argc, char **argv) {
37 } 37 }
38 38
39 con->alpha = val; 39 con->alpha = val;
40 container_damage_whole(con); 40 container_update(con);
41
41 return cmd_results_new(CMD_SUCCESS, NULL); 42 return cmd_results_new(CMD_SUCCESS, NULL);
42} 43}
diff --git a/sway/commands/output.c b/sway/commands/output.c
index 5186a2ba..5e5d31b3 100644
--- a/sway/commands/output.c
+++ b/sway/commands/output.c
@@ -6,7 +6,7 @@
6#include "log.h" 6#include "log.h"
7 7
8// must be in order for the bsearch 8// must be in order for the bsearch
9static struct cmd_handler output_handlers[] = { 9static const struct cmd_handler output_handlers[] = {
10 { "adaptive_sync", output_cmd_adaptive_sync }, 10 { "adaptive_sync", output_cmd_adaptive_sync },
11 { "background", output_cmd_background }, 11 { "background", output_cmd_background },
12 { "bg", output_cmd_background }, 12 { "bg", output_cmd_background },
@@ -15,8 +15,11 @@ static struct cmd_handler output_handlers[] = {
15 { "enable", output_cmd_enable }, 15 { "enable", output_cmd_enable },
16 { "max_render_time", output_cmd_max_render_time }, 16 { "max_render_time", output_cmd_max_render_time },
17 { "mode", output_cmd_mode }, 17 { "mode", output_cmd_mode },
18 { "modeline", output_cmd_modeline },
18 { "pos", output_cmd_position }, 19 { "pos", output_cmd_position },
19 { "position", output_cmd_position }, 20 { "position", output_cmd_position },
21 { "power", output_cmd_power },
22 { "render_bit_depth", output_cmd_render_bit_depth },
20 { "res", output_cmd_mode }, 23 { "res", output_cmd_mode },
21 { "resolution", output_cmd_mode }, 24 { "resolution", output_cmd_mode },
22 { "scale", output_cmd_scale }, 25 { "scale", output_cmd_scale },
@@ -24,6 +27,7 @@ static struct cmd_handler output_handlers[] = {
24 { "subpixel", output_cmd_subpixel }, 27 { "subpixel", output_cmd_subpixel },
25 { "toggle", output_cmd_toggle }, 28 { "toggle", output_cmd_toggle },
26 { "transform", output_cmd_transform }, 29 { "transform", output_cmd_transform },
30 { "unplug", output_cmd_unplug },
27}; 31};
28 32
29struct cmd_results *cmd_output(int argc, char **argv) { 33struct cmd_results *cmd_output(int argc, char **argv) {
@@ -32,9 +36,9 @@ struct cmd_results *cmd_output(int argc, char **argv) {
32 return error; 36 return error;
33 } 37 }
34 38
35 // The NOOP-1 output is a dummy output used when there's no outputs 39 // The HEADLESS-1 output is a dummy output used when there's no outputs
36 // connected. It should never be configured. 40 // connected. It should never be configured.
37 if (strcasecmp(argv[0], root->noop_output->wlr_output->name) == 0) { 41 if (strcasecmp(argv[0], root->fallback_output->wlr_output->name) == 0) {
38 return cmd_results_new(CMD_FAILURE, 42 return cmd_results_new(CMD_FAILURE,
39 "Refusing to configure the no op output"); 43 "Refusing to configure the no op output");
40 } 44 }
@@ -51,7 +55,7 @@ struct cmd_results *cmd_output(int argc, char **argv) {
51 if (!sway_output) { 55 if (!sway_output) {
52 return cmd_results_new(CMD_FAILURE, "Unknown output"); 56 return cmd_results_new(CMD_FAILURE, "Unknown output");
53 } 57 }
54 if (sway_output == root->noop_output) { 58 if (sway_output == root->fallback_output) {
55 return cmd_results_new(CMD_FAILURE, 59 return cmd_results_new(CMD_FAILURE,
56 "Refusing to configure the no op output"); 60 "Refusing to configure the no op output");
57 } 61 }
@@ -99,15 +103,18 @@ struct cmd_results *cmd_output(int argc, char **argv) {
99 103
100 bool background = output->background; 104 bool background = output->background;
101 105
102 output = store_output_config(output); 106 store_output_config(output);
103 107
104 // If reloading, the output configs will be applied after reading the 108 // If reloading, the output configs will be applied after reading the
105 // entire config and before the deferred commands so that an auto generated 109 // entire config and before the deferred commands so that an auto generated
106 // workspace name is not given to re-enabled outputs. 110 // workspace name is not given to re-enabled outputs.
107 if (!config->reloading && !config->validating) { 111 if (!config->reloading && !config->validating) {
108 apply_output_config_to_outputs(output); 112 apply_all_output_configs();
109 if (background) { 113 if (background) {
110 spawn_swaybg(); 114 if (!spawn_swaybg()) {
115 return cmd_results_new(CMD_FAILURE,
116 "Failed to apply background configuration");
117 }
111 } 118 }
112 } 119 }
113 120
diff --git a/sway/commands/output/background.c b/sway/commands/output/background.c
index 68ee9fe1..55bd7671 100644
--- a/sway/commands/output/background.c
+++ b/sway/commands/output/background.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <libgen.h> 1#include <libgen.h>
3#include <stdio.h> 2#include <stdio.h>
4#include <string.h> 3#include <string.h>
@@ -102,19 +101,19 @@ struct cmd_results *output_cmd_background(int argc, char **argv) {
102 } 101 }
103 102
104 char *conf_path = dirname(conf); 103 char *conf_path = dirname(conf);
105 char *rel_path = src; 104 char *real_src = malloc(strlen(conf_path) + strlen(src) + 2);
106 src = malloc(strlen(conf_path) + strlen(src) + 2); 105 if (!real_src) {
107 if (!src) { 106 free(src);
108 free(rel_path);
109 free(conf); 107 free(conf);
110 sway_log(SWAY_ERROR, "Unable to allocate memory"); 108 sway_log(SWAY_ERROR, "Unable to allocate memory");
111 return cmd_results_new(CMD_FAILURE, 109 return cmd_results_new(CMD_FAILURE,
112 "Unable to allocate resources"); 110 "Unable to allocate resources");
113 } 111 }
114 112
115 sprintf(src, "%s/%s", conf_path, rel_path); 113 snprintf(real_src, strlen(conf_path) + strlen(src) + 2, "%s/%s", conf_path, src);
116 free(rel_path); 114 free(src);
117 free(conf); 115 free(conf);
116 src = real_src;
118 } 117 }
119 118
120 bool can_access = access(src, F_OK) != -1; 119 bool can_access = access(src, F_OK) != -1;
@@ -123,7 +122,10 @@ struct cmd_results *output_cmd_background(int argc, char **argv) {
123 src); 122 src);
124 config_add_swaynag_warning("Unable to access background file '%s'", 123 config_add_swaynag_warning("Unable to access background file '%s'",
125 src); 124 src);
125 struct cmd_results *result = cmd_results_new(CMD_FAILURE,
126 "unable to access background file '%s'", src);
126 free(src); 127 free(src);
128 return result;
127 } else { 129 } else {
128 output->background = src; 130 output->background = src;
129 output->background_option = strdup(mode); 131 output->background_option = strdup(mode);
diff --git a/sway/commands/output/dpms.c b/sway/commands/output/dpms.c
index 9d75a80e..c7adbd58 100644
--- a/sway/commands/output/dpms.c
+++ b/sway/commands/output/dpms.c
@@ -1,22 +1,8 @@
1#include "log.h"
1#include "sway/commands.h" 2#include "sway/commands.h"
2#include "sway/config.h"
3#include "util.h"
4 3
5struct cmd_results *output_cmd_dpms(int argc, char **argv) { 4struct cmd_results *output_cmd_dpms(int argc, char **argv) {
6 if (!config->handler_context.output_config) { 5 sway_log(SWAY_INFO, "The \"output dpms\" command is deprecated, "
7 return cmd_results_new(CMD_FAILURE, "Missing output config"); 6 "use \"output power\" instead");
8 } 7 return output_cmd_power(argc, argv);
9 if (!argc) {
10 return cmd_results_new(CMD_INVALID, "Missing dpms argument.");
11 }
12
13 if (parse_boolean(argv[0], true)) {
14 config->handler_context.output_config->dpms_state = DPMS_ON;
15 } else {
16 config->handler_context.output_config->dpms_state = DPMS_OFF;
17 }
18
19 config->handler_context.leftovers.argc = argc - 1;
20 config->handler_context.leftovers.argv = argv + 1;
21 return NULL;
22} 8}
diff --git a/sway/commands/output/mode.c b/sway/commands/output/mode.c
index 5b710713..019d625a 100644
--- a/sway/commands/output/mode.c
+++ b/sway/commands/output/mode.c
@@ -20,6 +20,9 @@ struct cmd_results *output_cmd_mode(int argc, char **argv) {
20 output->custom_mode = 0; 20 output->custom_mode = 0;
21 } 21 }
22 22
23 // Reset custom modeline, if any
24 output->drm_mode.type = 0;
25
23 char *end; 26 char *end;
24 output->width = strtol(*argv, &end, 10); 27 output->width = strtol(*argv, &end, 10);
25 if (*end) { 28 if (*end) {
@@ -58,3 +61,58 @@ struct cmd_results *output_cmd_mode(int argc, char **argv) {
58 return NULL; 61 return NULL;
59} 62}
60 63
64static bool parse_modeline(char **argv, drmModeModeInfo *mode) {
65 mode->type = DRM_MODE_TYPE_USERDEF;
66 mode->clock = strtof(argv[0], NULL) * 1000;
67 mode->hdisplay = strtol(argv[1], NULL, 10);
68 mode->hsync_start = strtol(argv[2], NULL, 10);
69 mode->hsync_end = strtol(argv[3], NULL, 10);
70 mode->htotal = strtol(argv[4], NULL, 10);
71 mode->vdisplay = strtol(argv[5], NULL, 10);
72 mode->vsync_start = strtol(argv[6], NULL, 10);
73 mode->vsync_end = strtol(argv[7], NULL, 10);
74 mode->vtotal = strtol(argv[8], NULL, 10);
75
76 mode->vrefresh = mode->clock * 1000.0 * 1000.0
77 / mode->htotal / mode->vtotal;
78 if (strcasecmp(argv[9], "+hsync") == 0) {
79 mode->flags |= DRM_MODE_FLAG_PHSYNC;
80 } else if (strcasecmp(argv[9], "-hsync") == 0) {
81 mode->flags |= DRM_MODE_FLAG_NHSYNC;
82 } else {
83 return false;
84 }
85
86 if (strcasecmp(argv[10], "+vsync") == 0) {
87 mode->flags |= DRM_MODE_FLAG_PVSYNC;
88 } else if (strcasecmp(argv[10], "-vsync") == 0) {
89 mode->flags |= DRM_MODE_FLAG_NVSYNC;
90 } else {
91 return false;
92 }
93
94 snprintf(mode->name, sizeof(mode->name), "%dx%d@%d",
95 mode->hdisplay, mode->vdisplay, mode->vrefresh / 1000);
96
97 return true;
98}
99
100struct cmd_results *output_cmd_modeline(int argc, char **argv) {
101 if (!config->handler_context.output_config) {
102 return cmd_results_new(CMD_FAILURE, "Missing output config");
103 }
104 if (!argc) {
105 return cmd_results_new(CMD_INVALID, "Missing modeline argument.");
106 }
107
108 struct output_config *output = config->handler_context.output_config;
109
110 if (argc != 11 || !parse_modeline(argv, &output->drm_mode)) {
111 return cmd_results_new(CMD_INVALID, "Invalid modeline");
112 }
113
114 config->handler_context.leftovers.argc = argc - 12;
115 config->handler_context.leftovers.argv = argv + 12;
116 return NULL;
117}
118
diff --git a/sway/commands/output/power.c b/sway/commands/output/power.c
new file mode 100644
index 00000000..e6ae2852
--- /dev/null
+++ b/sway/commands/output/power.c
@@ -0,0 +1,43 @@
1#include <strings.h>
2#include "sway/commands.h"
3#include "sway/config.h"
4#include "sway/output.h"
5#include "util.h"
6
7struct cmd_results *output_cmd_power(int argc, char **argv) {
8 if (!config->handler_context.output_config) {
9 return cmd_results_new(CMD_FAILURE, "Missing output config");
10 }
11 if (argc == 0) {
12 return cmd_results_new(CMD_INVALID, "Missing power argument");
13 }
14
15 bool current = true;
16 if (strcasecmp(argv[0], "toggle") == 0) {
17 const char *oc_name = config->handler_context.output_config->name;
18 if (strcmp(oc_name, "*") == 0) {
19 return cmd_results_new(CMD_INVALID,
20 "Cannot apply toggle to all outputs");
21 }
22
23 struct sway_output *sway_output = all_output_by_name_or_id(oc_name);
24 if (!sway_output || !sway_output->wlr_output) {
25 return cmd_results_new(CMD_FAILURE,
26 "Cannot apply toggle to unknown output %s", oc_name);
27 }
28
29 if (sway_output->enabled && !sway_output->wlr_output->enabled) {
30 current = false;
31 }
32 }
33
34 if (parse_boolean(argv[0], current)) {
35 config->handler_context.output_config->power = 1;
36 } else {
37 config->handler_context.output_config->power = 0;
38 }
39
40 config->handler_context.leftovers.argc = argc - 1;
41 config->handler_context.leftovers.argv = argv + 1;
42 return NULL;
43}
diff --git a/sway/commands/output/render_bit_depth.c b/sway/commands/output/render_bit_depth.c
new file mode 100644
index 00000000..c419321e
--- /dev/null
+++ b/sway/commands/output/render_bit_depth.c
@@ -0,0 +1,29 @@
1#include <drm_fourcc.h>
2#include <strings.h>
3#include "sway/commands.h"
4#include "sway/config.h"
5
6struct cmd_results *output_cmd_render_bit_depth(int argc, char **argv) {
7 if (!config->handler_context.output_config) {
8 return cmd_results_new(CMD_FAILURE, "Missing output config");
9 }
10 if (!argc) {
11 return cmd_results_new(CMD_INVALID, "Missing bit depth argument.");
12 }
13
14 if (strcmp(*argv, "8") == 0) {
15 config->handler_context.output_config->render_bit_depth =
16 RENDER_BIT_DEPTH_8;
17 } else if (strcmp(*argv, "10") == 0) {
18 config->handler_context.output_config->render_bit_depth =
19 RENDER_BIT_DEPTH_10;
20 } else {
21 return cmd_results_new(CMD_INVALID,
22 "Invalid bit depth. Must be a value in (8|10).");
23 }
24
25 config->handler_context.leftovers.argc = argc - 1;
26 config->handler_context.leftovers.argv = argv + 1;
27 return NULL;
28}
29
diff --git a/sway/commands/output/toggle.c b/sway/commands/output/toggle.c
index 6342d526..c6b72845 100644
--- a/sway/commands/output/toggle.c
+++ b/sway/commands/output/toggle.c
@@ -29,7 +29,7 @@ struct cmd_results *output_cmd_toggle(int argc, char **argv) {
29 config->handler_context.output_config->enabled = 1; 29 config->handler_context.output_config->enabled = 1;
30 } 30 }
31 31
32 free(oc); 32 free_output_config(oc);
33 config->handler_context.leftovers.argc = argc; 33 config->handler_context.leftovers.argc = argc;
34 config->handler_context.leftovers.argv = argv; 34 config->handler_context.leftovers.argv = argv;
35 return NULL; 35 return NULL;
diff --git a/sway/commands/output/transform.c b/sway/commands/output/transform.c
index f4fcc8c9..8db71bb3 100644
--- a/sway/commands/output/transform.c
+++ b/sway/commands/output/transform.c
@@ -1,4 +1,5 @@
1#include <string.h> 1#include <string.h>
2#include <wlr/util/transform.h>
2#include "sway/commands.h" 3#include "sway/commands.h"
3#include "sway/config.h" 4#include "sway/config.h"
4#include "log.h" 5#include "log.h"
diff --git a/sway/commands/output/unplug.c b/sway/commands/output/unplug.c
new file mode 100644
index 00000000..dfef626f
--- /dev/null
+++ b/sway/commands/output/unplug.c
@@ -0,0 +1,54 @@
1#include <strings.h>
2#include <wlr/config.h>
3#include <wlr/backend/headless.h>
4#include <wlr/backend/wayland.h>
5#if WLR_HAS_X11_BACKEND
6#include <wlr/backend/x11.h>
7#endif
8#include "sway/commands.h"
9#include "sway/config.h"
10#include "sway/output.h"
11
12static bool is_backend_allowed(struct wlr_backend *backend) {
13 if (wlr_backend_is_headless(backend)) {
14 return true;
15 }
16 if (wlr_backend_is_wl(backend)) {
17 return true;
18 }
19#if WLR_HAS_X11_BACKEND
20 if (wlr_backend_is_x11(backend)) {
21 return true;
22 }
23#endif
24 return false;
25}
26
27/**
28 * This command is intended for developer use only.
29 */
30struct cmd_results *output_cmd_unplug(int argc, char **argv) {
31 if (!config->handler_context.output_config) {
32 return cmd_results_new(CMD_FAILURE, "Missing output config");
33 }
34
35 const char *oc_name = config->handler_context.output_config->name;
36 if (strcmp(oc_name, "*") == 0) {
37 return cmd_results_new(CMD_INVALID, "Won't unplug all outputs");
38 }
39
40 struct sway_output *sway_output = all_output_by_name_or_id(oc_name);
41 if (!sway_output) {
42 return cmd_results_new(CMD_INVALID,
43 "Cannot unplug unknown output %s", oc_name);
44 }
45
46 if (!is_backend_allowed(sway_output->wlr_output->backend)) {
47 return cmd_results_new(CMD_INVALID,
48 "Can only unplug outputs with headless, wayland or x11 backend");
49 }
50
51 wlr_output_destroy(sway_output->wlr_output);
52
53 return cmd_results_new(CMD_SUCCESS, NULL);
54}
diff --git a/sway/commands/primary_selection.c b/sway/commands/primary_selection.c
new file mode 100644
index 00000000..9e2689c2
--- /dev/null
+++ b/sway/commands/primary_selection.c
@@ -0,0 +1,25 @@
1#include <string.h>
2#include <strings.h>
3#include "sway/config.h"
4#include "sway/commands.h"
5#include "util.h"
6
7struct cmd_results *cmd_primary_selection(int argc, char **argv) {
8 struct cmd_results *error = NULL;
9 if ((error = checkarg(argc, "primary_selection", EXPECTED_EQUAL_TO, 1))) {
10 return error;
11 }
12
13 bool primary_selection = parse_boolean(argv[0], true);
14
15 // config->primary_selection is reset to the previous value on reload in
16 // load_main_config()
17 if (config->reloading && config->primary_selection != primary_selection) {
18 return cmd_results_new(CMD_FAILURE,
19 "primary_selection can only be enabled/disabled at launch");
20 }
21
22 config->primary_selection = primary_selection;
23
24 return cmd_results_new(CMD_SUCCESS, NULL);
25}
diff --git a/sway/commands/reload.c b/sway/commands/reload.c
index 3c994d54..6c0aac26 100644
--- a/sway/commands/reload.c
+++ b/sway/commands/reload.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <string.h> 1#include <string.h>
3#include "sway/commands.h" 2#include "sway/commands.h"
4#include "sway/config.h" 3#include "sway/config.h"
@@ -9,9 +8,8 @@
9#include "list.h" 8#include "list.h"
10#include "log.h" 9#include "log.h"
11 10
12static void rebuild_textures_iterator(struct sway_container *con, void *data) { 11static void title_bar_update_iterator(struct sway_container *con, void *data) {
13 container_update_marks_textures(con); 12 container_update_title_bar(con);
14 container_update_title_textures(con);
15} 13}
16 14
17static void do_reload(void *data) { 15static void do_reload(void *data) {
@@ -48,8 +46,7 @@ static void do_reload(void *data) {
48 } 46 }
49 list_free_items_and_destroy(bar_ids); 47 list_free_items_and_destroy(bar_ids);
50 48
51 config_update_font_height(true); 49 root_for_each_container(title_bar_update_iterator, NULL);
52 root_for_each_container(rebuild_textures_iterator, NULL);
53 50
54 arrange_root(); 51 arrange_root();
55} 52}
diff --git a/sway/commands/rename.c b/sway/commands/rename.c
index 3b855fdf..0d36cc21 100644
--- a/sway/commands/rename.c
+++ b/sway/commands/rename.c
@@ -7,6 +7,7 @@
7#include "sway/config.h" 7#include "sway/config.h"
8#include "sway/ipc-server.h" 8#include "sway/ipc-server.h"
9#include "sway/output.h" 9#include "sway/output.h"
10#include "sway/desktop/launcher.h"
10#include "sway/tree/container.h" 11#include "sway/tree/container.h"
11#include "sway/tree/workspace.h" 12#include "sway/tree/workspace.h"
12#include "sway/tree/root.h" 13#include "sway/tree/root.h"
@@ -25,7 +26,7 @@ struct cmd_results *cmd_rename(int argc, char **argv) {
25 "Can't run this command while there's no outputs connected."); 26 "Can't run this command while there's no outputs connected.");
26 } 27 }
27 if (strcasecmp(argv[0], "workspace") != 0) { 28 if (strcasecmp(argv[0], "workspace") != 0) {
28 return cmd_results_new(CMD_INVALID, expected_syntax); 29 return cmd_results_new(CMD_INVALID, "%s", expected_syntax);
29 } 30 }
30 31
31 int argn = 1; 32 int argn = 1;
@@ -64,7 +65,7 @@ struct cmd_results *cmd_rename(int argc, char **argv) {
64 ++argn; // move past "to" 65 ++argn; // move past "to"
65 66
66 if (argn >= argc) { 67 if (argn >= argc) {
67 return cmd_results_new(CMD_INVALID, expected_syntax); 68 return cmd_results_new(CMD_INVALID, "%s", expected_syntax);
68 } 69 }
69 70
70 char *new_name = join_args(argv + argn, argc - argn); 71 char *new_name = join_args(argv + argn, argc - argn);
@@ -91,8 +92,6 @@ struct cmd_results *cmd_rename(int argc, char **argv) {
91 92
92 sway_log(SWAY_DEBUG, "renaming workspace '%s' to '%s'", workspace->name, new_name); 93 sway_log(SWAY_DEBUG, "renaming workspace '%s' to '%s'", workspace->name, new_name);
93 94
94 root_rename_pid_workspaces(workspace->name, new_name);
95
96 free(workspace->name); 95 free(workspace->name);
97 workspace->name = new_name; 96 workspace->name = new_name;
98 97
diff --git a/sway/commands/resize.c b/sway/commands/resize.c
index ca36e858..32b746ea 100644
--- a/sway/commands/resize.c
+++ b/sway/commands/resize.c
@@ -57,7 +57,7 @@ struct sway_container *container_find_resize_parent(struct sway_container *con,
57 (allow_last || index < siblings->length - 1)) { 57 (allow_last || index < siblings->length - 1)) {
58 return con; 58 return con;
59 } 59 }
60 con = con->parent; 60 con = con->pending.parent;
61 } 61 }
62 62
63 return NULL; 63 return NULL;
@@ -75,6 +75,10 @@ void container_resize_tiled(struct sway_container *con,
75 return; 75 return;
76 } 76 }
77 77
78 if (container_is_scratchpad_hidden_or_child(con)) {
79 return;
80 }
81
78 // For HORIZONTAL or VERTICAL, we are growing in two directions so select 82 // For HORIZONTAL or VERTICAL, we are growing in two directions so select
79 // both adjacent siblings. For RIGHT or DOWN, just select the next sibling. 83 // both adjacent siblings. For RIGHT or DOWN, just select the next sibling.
80 // For LEFT or UP, convert it to a RIGHT or DOWN resize and reassign con to 84 // For LEFT or UP, convert it to a RIGHT or DOWN resize and reassign con to
@@ -115,13 +119,13 @@ void container_resize_tiled(struct sway_container *con,
115 int sibling_amount = prev ? ceil((double)amount / 2.0) : amount; 119 int sibling_amount = prev ? ceil((double)amount / 2.0) : amount;
116 120
117 if (is_horizontal(axis)) { 121 if (is_horizontal(axis)) {
118 if (con->width + amount < MIN_SANE_W) { 122 if (con->pending.width + amount < MIN_SANE_W) {
119 return; 123 return;
120 } 124 }
121 if (next->width - sibling_amount < MIN_SANE_W) { 125 if (next->pending.width - sibling_amount < MIN_SANE_W) {
122 return; 126 return;
123 } 127 }
124 if (prev && prev->width - sibling_amount < MIN_SANE_W) { 128 if (prev && prev->pending.width - sibling_amount < MIN_SANE_W) {
125 return; 129 return;
126 } 130 }
127 if (con->child_total_width <= 0) { 131 if (con->child_total_width <= 0) {
@@ -133,7 +137,7 @@ void container_resize_tiled(struct sway_container *con,
133 list_t *siblings = container_get_siblings(con); 137 list_t *siblings = container_get_siblings(con);
134 for (int i = 0; i < siblings->length; ++i) { 138 for (int i = 0; i < siblings->length; ++i) {
135 struct sway_container *con = siblings->items[i]; 139 struct sway_container *con = siblings->items[i];
136 con->width_fraction = con->width / con->child_total_width; 140 con->width_fraction = con->pending.width / con->child_total_width;
137 } 141 }
138 142
139 double amount_fraction = (double)amount / con->child_total_width; 143 double amount_fraction = (double)amount / con->child_total_width;
@@ -146,13 +150,13 @@ void container_resize_tiled(struct sway_container *con,
146 prev->width_fraction -= sibling_amount_fraction; 150 prev->width_fraction -= sibling_amount_fraction;
147 } 151 }
148 } else { 152 } else {
149 if (con->height + amount < MIN_SANE_H) { 153 if (con->pending.height + amount < MIN_SANE_H) {
150 return; 154 return;
151 } 155 }
152 if (next->height - sibling_amount < MIN_SANE_H) { 156 if (next->pending.height - sibling_amount < MIN_SANE_H) {
153 return; 157 return;
154 } 158 }
155 if (prev && prev->height - sibling_amount < MIN_SANE_H) { 159 if (prev && prev->pending.height - sibling_amount < MIN_SANE_H) {
156 return; 160 return;
157 } 161 }
158 if (con->child_total_height <= 0) { 162 if (con->child_total_height <= 0) {
@@ -164,7 +168,7 @@ void container_resize_tiled(struct sway_container *con,
164 list_t *siblings = container_get_siblings(con); 168 list_t *siblings = container_get_siblings(con);
165 for (int i = 0; i < siblings->length; ++i) { 169 for (int i = 0; i < siblings->length; ++i) {
166 struct sway_container *con = siblings->items[i]; 170 struct sway_container *con = siblings->items[i];
167 con->height_fraction = con->height / con->child_total_height; 171 con->height_fraction = con->pending.height / con->child_total_height;
168 } 172 }
169 173
170 double amount_fraction = (double)amount / con->child_total_height; 174 double amount_fraction = (double)amount / con->child_total_height;
@@ -178,10 +182,10 @@ void container_resize_tiled(struct sway_container *con,
178 } 182 }
179 } 183 }
180 184
181 if (con->parent) { 185 if (con->pending.parent) {
182 arrange_container(con->parent); 186 arrange_container(con->pending.parent);
183 } else { 187 } else {
184 arrange_workspace(con->workspace); 188 arrange_workspace(con->pending.workspace);
185 } 189 }
186} 190}
187 191
@@ -203,15 +207,15 @@ static struct cmd_results *resize_adjust_floating(uint32_t axis,
203 int min_width, max_width, min_height, max_height; 207 int min_width, max_width, min_height, max_height;
204 floating_calculate_constraints(&min_width, &max_width, 208 floating_calculate_constraints(&min_width, &max_width,
205 &min_height, &max_height); 209 &min_height, &max_height);
206 if (con->width + grow_width < min_width) { 210 if (con->pending.width + grow_width < min_width) {
207 grow_width = min_width - con->width; 211 grow_width = min_width - con->pending.width;
208 } else if (con->width + grow_width > max_width) { 212 } else if (con->pending.width + grow_width > max_width) {
209 grow_width = max_width - con->width; 213 grow_width = max_width - con->pending.width;
210 } 214 }
211 if (con->height + grow_height < min_height) { 215 if (con->pending.height + grow_height < min_height) {
212 grow_height = min_height - con->height; 216 grow_height = min_height - con->pending.height;
213 } else if (con->height + grow_height > max_height) { 217 } else if (con->pending.height + grow_height > max_height) {
214 grow_height = max_height - con->height; 218 grow_height = max_height - con->pending.height;
215 } 219 }
216 int grow_x = 0, grow_y = 0; 220 int grow_x = 0, grow_y = 0;
217 221
@@ -227,15 +231,15 @@ static struct cmd_results *resize_adjust_floating(uint32_t axis,
227 if (grow_width == 0 && grow_height == 0) { 231 if (grow_width == 0 && grow_height == 0) {
228 return cmd_results_new(CMD_INVALID, "Cannot resize any further"); 232 return cmd_results_new(CMD_INVALID, "Cannot resize any further");
229 } 233 }
230 con->x += grow_x; 234 con->pending.x += grow_x;
231 con->y += grow_y; 235 con->pending.y += grow_y;
232 con->width += grow_width; 236 con->pending.width += grow_width;
233 con->height += grow_height; 237 con->pending.height += grow_height;
234 238
235 con->content_x += grow_x; 239 con->pending.content_x += grow_x;
236 con->content_y += grow_y; 240 con->pending.content_y += grow_y;
237 con->content_width += grow_width; 241 con->pending.content_width += grow_width;
238 con->content_height += grow_height; 242 con->pending.content_height += grow_height;
239 243
240 arrange_container(con); 244 arrange_container(con);
241 245
@@ -249,16 +253,35 @@ static struct cmd_results *resize_adjust_tiled(uint32_t axis,
249 struct movement_amount *amount) { 253 struct movement_amount *amount) {
250 struct sway_container *current = config->handler_context.container; 254 struct sway_container *current = config->handler_context.container;
251 255
256 if (container_is_scratchpad_hidden_or_child(current)) {
257 return cmd_results_new(CMD_FAILURE, "Cannot resize a hidden scratchpad container");
258 }
259
252 if (amount->unit == MOVEMENT_UNIT_DEFAULT) { 260 if (amount->unit == MOVEMENT_UNIT_DEFAULT) {
253 amount->unit = MOVEMENT_UNIT_PPT; 261 amount->unit = MOVEMENT_UNIT_PPT;
254 } 262 }
255 if (amount->unit == MOVEMENT_UNIT_PPT) { 263 if (amount->unit == MOVEMENT_UNIT_PPT) {
264 struct sway_container *parent = current->pending.parent;
256 float pct = amount->amount / 100.0f; 265 float pct = amount->amount / 100.0f;
257 266
258 if (is_horizontal(axis)) { 267 if (is_horizontal(axis)) {
259 amount->amount = (float)current->width * pct; 268 while (parent && parent->pending.layout != L_HORIZ) {
269 parent = parent->pending.parent;
270 }
271 if (parent) {
272 amount->amount = (float)parent->pending.width * pct;
273 } else {
274 amount->amount = (float)current->pending.workspace->width * pct;
275 }
260 } else { 276 } else {
261 amount->amount = (float)current->height * pct; 277 while (parent && parent->pending.layout != L_VERT) {
278 parent = parent->pending.parent;
279 }
280 if (parent) {
281 amount->amount = (float)parent->pending.height * pct;
282 } else {
283 amount->amount = (float)current->pending.workspace->height * pct;
284 }
262 } 285 }
263 } 286 }
264 287
@@ -277,24 +300,29 @@ static struct cmd_results *resize_adjust_tiled(uint32_t axis,
277 */ 300 */
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) {
283 // Convert to px 311 // Convert to px
284 struct sway_container *parent = con->parent; 312 struct sway_container *parent = con->pending.parent;
285 while (parent && parent->layout != L_HORIZ) { 313 while (parent && parent->pending.layout != L_HORIZ) {
286 parent = parent->parent; 314 parent = parent->pending.parent;
287 } 315 }
288 if (parent) { 316 if (parent) {
289 width->amount = parent->width * width->amount / 100; 317 width->amount = parent->pending.width * width->amount / 100;
290 } else { 318 } else {
291 width->amount = con->workspace->width * width->amount / 100; 319 width->amount = con->pending.workspace->width * width->amount / 100;
292 } 320 }
293 width->unit = MOVEMENT_UNIT_PX; 321 width->unit = MOVEMENT_UNIT_PX;
294 } 322 }
295 if (width->unit == MOVEMENT_UNIT_PX) { 323 if (width->unit == MOVEMENT_UNIT_PX) {
296 container_resize_tiled(con, AXIS_HORIZONTAL, 324 container_resize_tiled(con, AXIS_HORIZONTAL,
297 width->amount - con->width); 325 width->amount - con->pending.width);
298 } 326 }
299 } 327 }
300 328
@@ -302,20 +330,20 @@ static struct cmd_results *resize_set_tiled(struct sway_container *con,
302 if (height->unit == MOVEMENT_UNIT_PPT || 330 if (height->unit == MOVEMENT_UNIT_PPT ||
303 height->unit == MOVEMENT_UNIT_DEFAULT) { 331 height->unit == MOVEMENT_UNIT_DEFAULT) {
304 // Convert to px 332 // Convert to px
305 struct sway_container *parent = con->parent; 333 struct sway_container *parent = con->pending.parent;
306 while (parent && parent->layout != L_VERT) { 334 while (parent && parent->pending.layout != L_VERT) {
307 parent = parent->parent; 335 parent = parent->pending.parent;
308 } 336 }
309 if (parent) { 337 if (parent) {
310 height->amount = parent->height * height->amount / 100; 338 height->amount = parent->pending.height * height->amount / 100;
311 } else { 339 } else {
312 height->amount = con->workspace->height * height->amount / 100; 340 height->amount = con->pending.workspace->height * height->amount / 100;
313 } 341 }
314 height->unit = MOVEMENT_UNIT_PX; 342 height->unit = MOVEMENT_UNIT_PX;
315 } 343 }
316 if (height->unit == MOVEMENT_UNIT_PX) { 344 if (height->unit == MOVEMENT_UNIT_PX) {
317 container_resize_tiled(con, AXIS_VERTICAL, 345 container_resize_tiled(con, AXIS_VERTICAL,
318 height->amount - con->height); 346 height->amount - con->pending.height);
319 } 347 }
320 } 348 }
321 349
@@ -339,15 +367,15 @@ static struct cmd_results *resize_set_floating(struct sway_container *con,
339 "Cannot resize a hidden scratchpad container by ppt"); 367 "Cannot resize a hidden scratchpad container by ppt");
340 } 368 }
341 // Convert to px 369 // Convert to px
342 width->amount = con->workspace->width * width->amount / 100; 370 width->amount = con->pending.workspace->width * width->amount / 100;
343 width->unit = MOVEMENT_UNIT_PX; 371 width->unit = MOVEMENT_UNIT_PX;
344 // Falls through 372 // Falls through
345 case MOVEMENT_UNIT_PX: 373 case MOVEMENT_UNIT_PX:
346 case MOVEMENT_UNIT_DEFAULT: 374 case MOVEMENT_UNIT_DEFAULT:
347 width->amount = fmax(min_width, fmin(width->amount, max_width)); 375 width->amount = fmax(min_width, fmin(width->amount, max_width));
348 grow_width = width->amount - con->width; 376 grow_width = width->amount - con->pending.width;
349 con->x -= grow_width / 2; 377 con->pending.x -= grow_width / 2;
350 con->width = width->amount; 378 con->pending.width = width->amount;
351 break; 379 break;
352 case MOVEMENT_UNIT_INVALID: 380 case MOVEMENT_UNIT_INVALID:
353 sway_assert(false, "invalid width unit"); 381 sway_assert(false, "invalid width unit");
@@ -363,15 +391,15 @@ static struct cmd_results *resize_set_floating(struct sway_container *con,
363 "Cannot resize a hidden scratchpad container by ppt"); 391 "Cannot resize a hidden scratchpad container by ppt");
364 } 392 }
365 // Convert to px 393 // Convert to px
366 height->amount = con->workspace->height * height->amount / 100; 394 height->amount = con->pending.workspace->height * height->amount / 100;
367 height->unit = MOVEMENT_UNIT_PX; 395 height->unit = MOVEMENT_UNIT_PX;
368 // Falls through 396 // Falls through
369 case MOVEMENT_UNIT_PX: 397 case MOVEMENT_UNIT_PX:
370 case MOVEMENT_UNIT_DEFAULT: 398 case MOVEMENT_UNIT_DEFAULT:
371 height->amount = fmax(min_height, fmin(height->amount, max_height)); 399 height->amount = fmax(min_height, fmin(height->amount, max_height));
372 grow_height = height->amount - con->height; 400 grow_height = height->amount - con->pending.height;
373 con->y -= grow_height / 2; 401 con->pending.y -= grow_height / 2;
374 con->height = height->amount; 402 con->pending.height = height->amount;
375 break; 403 break;
376 case MOVEMENT_UNIT_INVALID: 404 case MOVEMENT_UNIT_INVALID:
377 sway_assert(false, "invalid height unit"); 405 sway_assert(false, "invalid height unit");
@@ -379,10 +407,10 @@ static struct cmd_results *resize_set_floating(struct sway_container *con,
379 } 407 }
380 } 408 }
381 409
382 con->content_x -= grow_width / 2; 410 con->pending.content_x -= grow_width / 2;
383 con->content_y -= grow_height / 2; 411 con->pending.content_y -= grow_height / 2;
384 con->content_width += grow_width; 412 con->pending.content_width += grow_width;
385 con->content_height += grow_height; 413 con->pending.content_height += grow_height;
386 414
387 arrange_container(con); 415 arrange_container(con);
388 416
@@ -415,7 +443,7 @@ static struct cmd_results *cmd_resize_set(int argc, char **argv) {
415 argc -= num_consumed_args; 443 argc -= num_consumed_args;
416 argv += num_consumed_args; 444 argv += num_consumed_args;
417 if (width.unit == MOVEMENT_UNIT_INVALID) { 445 if (width.unit == MOVEMENT_UNIT_INVALID) {
418 return cmd_results_new(CMD_INVALID, usage); 446 return cmd_results_new(CMD_INVALID, "%s", usage);
419 } 447 }
420 } 448 }
421 449
@@ -427,20 +455,20 @@ static struct cmd_results *cmd_resize_set(int argc, char **argv) {
427 } 455 }
428 int num_consumed_args = parse_movement_amount(argc, argv, &height); 456 int num_consumed_args = parse_movement_amount(argc, argv, &height);
429 if (argc > num_consumed_args) { 457 if (argc > num_consumed_args) {
430 return cmd_results_new(CMD_INVALID, usage); 458 return cmd_results_new(CMD_INVALID, "%s", usage);
431 } 459 }
432 if (width.unit == MOVEMENT_UNIT_INVALID) { 460 if (width.unit == MOVEMENT_UNIT_INVALID) {
433 return cmd_results_new(CMD_INVALID, usage); 461 return cmd_results_new(CMD_INVALID, "%s", usage);
434 } 462 }
435 } 463 }
436 464
437 // If 0, don't resize that dimension 465 // If 0, don't resize that dimension
438 struct sway_container *con = config->handler_context.container; 466 struct sway_container *con = config->handler_context.container;
439 if (width.amount <= 0) { 467 if (width.amount <= 0) {
440 width.amount = con->width; 468 width.amount = con->pending.width;
441 } 469 }
442 if (height.amount <= 0) { 470 if (height.amount <= 0) {
443 height.amount = con->height; 471 height.amount = con->pending.height;
444 } 472 }
445 473
446 if (container_is_floating(con)) { 474 if (container_is_floating(con)) {
@@ -462,7 +490,7 @@ static struct cmd_results *cmd_resize_adjust(int argc, char **argv,
462 "[<amount> px|ppt [or <amount> px|ppt]]'"; 490 "[<amount> px|ppt [or <amount> px|ppt]]'";
463 uint32_t axis = parse_resize_axis(*argv); 491 uint32_t axis = parse_resize_axis(*argv);
464 if (axis == WLR_EDGE_NONE) { 492 if (axis == WLR_EDGE_NONE) {
465 return cmd_results_new(CMD_INVALID, usage); 493 return cmd_results_new(CMD_INVALID, "%s", usage);
466 } 494 }
467 --argc; ++argv; 495 --argc; ++argv;
468 496
@@ -473,7 +501,7 @@ static struct cmd_results *cmd_resize_adjust(int argc, char **argv,
473 argc -= num_consumed_args; 501 argc -= num_consumed_args;
474 argv += num_consumed_args; 502 argv += num_consumed_args;
475 if (first_amount.unit == MOVEMENT_UNIT_INVALID) { 503 if (first_amount.unit == MOVEMENT_UNIT_INVALID) {
476 return cmd_results_new(CMD_INVALID, usage); 504 return cmd_results_new(CMD_INVALID, "%s", usage);
477 } 505 }
478 } else { 506 } else {
479 first_amount.amount = 10; 507 first_amount.amount = 10;
@@ -483,7 +511,7 @@ static struct cmd_results *cmd_resize_adjust(int argc, char **argv,
483 // "or" 511 // "or"
484 if (argc) { 512 if (argc) {
485 if (strcmp(*argv, "or") != 0) { 513 if (strcmp(*argv, "or") != 0) {
486 return cmd_results_new(CMD_INVALID, usage); 514 return cmd_results_new(CMD_INVALID, "%s", usage);
487 } 515 }
488 --argc; ++argv; 516 --argc; ++argv;
489 } 517 }
@@ -493,10 +521,10 @@ static struct cmd_results *cmd_resize_adjust(int argc, char **argv,
493 if (argc) { 521 if (argc) {
494 int num_consumed_args = parse_movement_amount(argc, argv, &second_amount); 522 int num_consumed_args = parse_movement_amount(argc, argv, &second_amount);
495 if (argc > num_consumed_args) { 523 if (argc > num_consumed_args) {
496 return cmd_results_new(CMD_INVALID, usage); 524 return cmd_results_new(CMD_INVALID, "%s", usage);
497 } 525 }
498 if (second_amount.unit == MOVEMENT_UNIT_INVALID) { 526 if (second_amount.unit == MOVEMENT_UNIT_INVALID) {
499 return cmd_results_new(CMD_INVALID, usage); 527 return cmd_results_new(CMD_INVALID, "%s", usage);
500 } 528 }
501 } else { 529 } else {
502 second_amount.amount = 0; 530 second_amount.amount = 0;
@@ -566,5 +594,5 @@ struct cmd_results *cmd_resize(int argc, char **argv) {
566 const char usage[] = "Expected 'resize <shrink|grow> " 594 const char usage[] = "Expected 'resize <shrink|grow> "
567 "<width|height|up|down|left|right> [<amount>] [px|ppt]'"; 595 "<width|height|up|down|left|right> [<amount>] [px|ppt]'";
568 596
569 return cmd_results_new(CMD_INVALID, usage); 597 return cmd_results_new(CMD_INVALID, "%s", usage);
570} 598}
diff --git a/sway/commands/scratchpad.c b/sway/commands/scratchpad.c
index 34871bc6..c995f2f0 100644
--- a/sway/commands/scratchpad.c
+++ b/sway/commands/scratchpad.c
@@ -21,8 +21,8 @@ static void scratchpad_toggle_auto(void) {
21 // If the focus is in a floating split container, 21 // If the focus is in a floating split container,
22 // operate on the split container instead of the child. 22 // operate on the split container instead of the child.
23 if (focus && container_is_floating_or_child(focus)) { 23 if (focus && container_is_floating_or_child(focus)) {
24 while (focus->parent) { 24 while (focus->pending.parent) {
25 focus = focus->parent; 25 focus = focus->pending.parent;
26 } 26 }
27 } 27 }
28 28
@@ -52,7 +52,7 @@ static void scratchpad_toggle_auto(void) {
52 // In this case we move it to the current workspace. 52 // In this case we move it to the current workspace.
53 for (int i = 0; i < root->scratchpad->length; ++i) { 53 for (int i = 0; i < root->scratchpad->length; ++i) {
54 struct sway_container *con = root->scratchpad->items[i]; 54 struct sway_container *con = root->scratchpad->items[i];
55 if (con->parent) { 55 if (con->pending.parent) {
56 sway_log(SWAY_DEBUG, 56 sway_log(SWAY_DEBUG,
57 "Moving a visible scratchpad window (%s) to this workspace", 57 "Moving a visible scratchpad window (%s) to this workspace",
58 con->title); 58 con->title);
@@ -80,7 +80,7 @@ static void scratchpad_toggle_container(struct sway_container *con) {
80 struct sway_seat *seat = input_manager_current_seat(); 80 struct sway_seat *seat = input_manager_current_seat();
81 struct sway_workspace *ws = seat_get_focused_workspace(seat); 81 struct sway_workspace *ws = seat_get_focused_workspace(seat);
82 // Check if it matches a currently visible scratchpad window and hide it. 82 // Check if it matches a currently visible scratchpad window and hide it.
83 if (con->workspace && ws == con->workspace) { 83 if (con->pending.workspace && ws == con->pending.workspace) {
84 root_scratchpad_hide(con); 84 root_scratchpad_hide(con);
85 return; 85 return;
86 } 86 }
@@ -105,21 +105,22 @@ struct cmd_results *cmd_scratchpad(int argc, char **argv) {
105 return cmd_results_new(CMD_INVALID, "Scratchpad is empty"); 105 return cmd_results_new(CMD_INVALID, "Scratchpad is empty");
106 } 106 }
107 107
108 if (config->handler_context.using_criteria) { 108 if (config->handler_context.node_overridden) {
109 struct sway_container *con = config->handler_context.container; 109 struct sway_container *con = config->handler_context.container;
110 110
111 // If the container is in a floating split container, 111 // If the container is in a floating split container,
112 // operate on the split container instead of the child. 112 // operate on the split container instead of the child.
113 if (container_is_floating_or_child(con)) { 113 if (con && container_is_floating_or_child(con)) {
114 while (con->parent) { 114 while (con->pending.parent) {
115 con = con->parent; 115 con = con->pending.parent;
116 } 116 }
117 } 117 }
118 118
119 // If using criteria, this command is executed for every container which 119 // If using criteria, this command is executed for every container which
120 // matches the criteria. If this container isn't in the scratchpad, 120 // matches the criteria. If this container isn't in the scratchpad,
121 // we'll just silently return a success. 121 // we'll just silently return a success. The same is true if the
122 if (!con->scratchpad) { 122 // overridden node is not a container.
123 if (!con || !con->scratchpad) {
123 return cmd_results_new(CMD_SUCCESS, NULL); 124 return cmd_results_new(CMD_SUCCESS, NULL);
124 } 125 }
125 scratchpad_toggle_container(con); 126 scratchpad_toggle_container(con);
diff --git a/sway/commands/seat.c b/sway/commands/seat.c
index 84c6ba53..2d197b69 100644
--- a/sway/commands/seat.c
+++ b/sway/commands/seat.c
@@ -8,13 +8,13 @@
8 8
9// must be in order for the bsearch 9// must be in order for the bsearch
10// these handlers perform actions on the seat 10// these handlers perform actions on the seat
11static struct cmd_handler seat_action_handlers[] = { 11static const struct cmd_handler seat_action_handlers[] = {
12 { "cursor", seat_cmd_cursor }, 12 { "cursor", seat_cmd_cursor },
13}; 13};
14 14
15// must be in order for the bsearch 15// must be in order for the bsearch
16// these handlers alter the seat config 16// these handlers alter the seat config
17static struct cmd_handler seat_handlers[] = { 17static const struct cmd_handler seat_handlers[] = {
18 { "attach", seat_cmd_attach }, 18 { "attach", seat_cmd_attach },
19 { "fallback", seat_cmd_fallback }, 19 { "fallback", seat_cmd_fallback },
20 { "hide_cursor", seat_cmd_hide_cursor }, 20 { "hide_cursor", seat_cmd_hide_cursor },
diff --git a/sway/commands/seat/attach.c b/sway/commands/seat/attach.c
index 7615eef9..47d18546 100644
--- a/sway/commands/seat/attach.c
+++ b/sway/commands/seat/attach.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <string.h> 1#include <string.h>
3#include "sway/commands.h" 2#include "sway/commands.h"
4#include "sway/config.h" 3#include "sway/config.h"
@@ -12,7 +11,7 @@ struct cmd_results *seat_cmd_attach(int argc, char **argv) {
12 if (!config->handler_context.seat_config) { 11 if (!config->handler_context.seat_config) {
13 return cmd_results_new(CMD_FAILURE, "No seat defined"); 12 return cmd_results_new(CMD_FAILURE, "No seat defined");
14 } 13 }
15 if (config->reading) { 14 if (!config->active) {
16 return cmd_results_new(CMD_DEFER, NULL); 15 return cmd_results_new(CMD_DEFER, NULL);
17 } 16 }
18 17
diff --git a/sway/commands/seat/cursor.c b/sway/commands/seat/cursor.c
index 749235eb..df7c379d 100644
--- a/sway/commands/seat/cursor.c
+++ b/sway/commands/seat/cursor.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <linux/input-event-codes.h> 1#include <linux/input-event-codes.h>
3 2
4#include <strings.h> 3#include <strings.h>
@@ -18,7 +17,7 @@ static struct cmd_results *handle_command(struct sway_cursor *cursor,
18 int argc, char **argv) { 17 int argc, char **argv) {
19 if (strcasecmp(argv[0], "move") == 0) { 18 if (strcasecmp(argv[0], "move") == 0) {
20 if (argc < 3) { 19 if (argc < 3) {
21 return cmd_results_new(CMD_INVALID, expected_syntax); 20 return cmd_results_new(CMD_INVALID, "%s", expected_syntax);
22 } 21 }
23 int delta_x = strtol(argv[1], NULL, 10); 22 int delta_x = strtol(argv[1], NULL, 10);
24 int delta_y = strtol(argv[2], NULL, 10); 23 int delta_y = strtol(argv[2], NULL, 10);
@@ -27,7 +26,7 @@ static struct cmd_results *handle_command(struct sway_cursor *cursor,
27 wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat); 26 wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat);
28 } else if (strcasecmp(argv[0], "set") == 0) { 27 } else if (strcasecmp(argv[0], "set") == 0) {
29 if (argc < 3) { 28 if (argc < 3) {
30 return cmd_results_new(CMD_INVALID, expected_syntax); 29 return cmd_results_new(CMD_INVALID, "%s", expected_syntax);
31 } 30 }
32 // map absolute coords (0..1,0..1) to root container coords 31 // map absolute coords (0..1,0..1) to root container coords
33 float x = strtof(argv[1], NULL) / root->width; 32 float x = strtof(argv[1], NULL) / root->width;
@@ -37,7 +36,7 @@ static struct cmd_results *handle_command(struct sway_cursor *cursor,
37 wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat); 36 wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat);
38 } else { 37 } else {
39 if (argc < 2) { 38 if (argc < 2) {
40 return cmd_results_new(CMD_INVALID, expected_syntax); 39 return cmd_results_new(CMD_INVALID, "%s", expected_syntax);
41 } 40 }
42 struct cmd_results *error = NULL; 41 struct cmd_results *error = NULL;
43 if ((error = press_or_release(cursor, argv[0], argv[1]))) { 42 if ((error = press_or_release(cursor, argv[0], argv[1]))) {
@@ -85,36 +84,36 @@ struct cmd_results *seat_cmd_cursor(int argc, char **argv) {
85 84
86static struct cmd_results *press_or_release(struct sway_cursor *cursor, 85static struct cmd_results *press_or_release(struct sway_cursor *cursor,
87 char *action, char *button_str) { 86 char *action, char *button_str) {
88 enum wlr_button_state state; 87 enum wl_pointer_button_state state;
89 uint32_t button; 88 uint32_t button;
90 if (strcasecmp(action, "press") == 0) { 89 if (strcasecmp(action, "press") == 0) {
91 state = WLR_BUTTON_PRESSED; 90 state = WL_POINTER_BUTTON_STATE_PRESSED;
92 } else if (strcasecmp(action, "release") == 0) { 91 } else if (strcasecmp(action, "release") == 0) {
93 state = WLR_BUTTON_RELEASED; 92 state = WL_POINTER_BUTTON_STATE_RELEASED;
94 } else { 93 } else {
95 return cmd_results_new(CMD_INVALID, expected_syntax); 94 return cmd_results_new(CMD_INVALID, "%s", expected_syntax);
96 } 95 }
97 96
98 char *message = NULL; 97 char *message = NULL;
99 button = get_mouse_button(button_str, &message); 98 button = get_mouse_button(button_str, &message);
100 if (message) { 99 if (message) {
101 struct cmd_results *error = 100 struct cmd_results *error =
102 cmd_results_new(CMD_INVALID, message); 101 cmd_results_new(CMD_INVALID, "%s", message);
103 free(message); 102 free(message);
104 return error; 103 return error;
105 } else if (button == SWAY_SCROLL_UP || button == SWAY_SCROLL_DOWN 104 } else if (button == SWAY_SCROLL_UP || button == SWAY_SCROLL_DOWN
106 || button == SWAY_SCROLL_LEFT || button == SWAY_SCROLL_RIGHT) { 105 || button == SWAY_SCROLL_LEFT || button == SWAY_SCROLL_RIGHT) {
107 // Dispatch axis event 106 // Dispatch axis event
108 enum wlr_axis_orientation orientation = 107 enum wl_pointer_axis orientation =
109 (button == SWAY_SCROLL_UP || button == SWAY_SCROLL_DOWN) 108 (button == SWAY_SCROLL_UP || button == SWAY_SCROLL_DOWN)
110 ? WLR_AXIS_ORIENTATION_VERTICAL 109 ? WL_POINTER_AXIS_VERTICAL_SCROLL
111 : WLR_AXIS_ORIENTATION_HORIZONTAL; 110 : WL_POINTER_AXIS_HORIZONTAL_SCROLL;
112 double delta = (button == SWAY_SCROLL_UP || button == SWAY_SCROLL_LEFT) 111 double delta = (button == SWAY_SCROLL_UP || button == SWAY_SCROLL_LEFT)
113 ? -1 : 1; 112 ? -1 : 1;
114 struct wlr_event_pointer_axis event = { 113 struct wlr_pointer_axis_event event = {
115 .device = NULL, 114 .pointer = NULL,
116 .time_msec = 0, 115 .time_msec = 0,
117 .source = WLR_AXIS_SOURCE_WHEEL, 116 .source = WL_POINTER_AXIS_SOURCE_WHEEL,
118 .orientation = orientation, 117 .orientation = orientation,
119 .delta = delta * 15, 118 .delta = delta * 15,
120 .delta_discrete = delta 119 .delta_discrete = delta
diff --git a/sway/commands/seat/hide_cursor.c b/sway/commands/seat/hide_cursor.c
index e09b82d9..f5177a47 100644
--- a/sway/commands/seat/hide_cursor.c
+++ b/sway/commands/seat/hide_cursor.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <string.h> 1#include <string.h>
3#include "sway/commands.h" 2#include "sway/commands.h"
4#include "sway/config.h" 3#include "sway/config.h"
diff --git a/sway/commands/seat/idle.c b/sway/commands/seat/idle.c
index 82428f2c..2974453e 100644
--- a/sway/commands/seat/idle.c
+++ b/sway/commands/seat/idle.c
@@ -1,8 +1,8 @@
1#define _POSIX_C_SOURCE 200809L
2#include <limits.h> 1#include <limits.h>
3#include <string.h> 2#include <string.h>
4#include <strings.h> 3#include <strings.h>
5#include <stdint.h> 4#include <stdint.h>
5#include "log.h"
6#include "sway/commands.h" 6#include "sway/commands.h"
7#include "sway/config.h" 7#include "sway/config.h"
8#include "sway/input/seat.h" 8#include "sway/input/seat.h"
@@ -69,5 +69,10 @@ struct cmd_results *seat_cmd_idle_wake(int argc, char **argv) {
69 return cmd_results_new(CMD_FAILURE, "Invalid idle source"); 69 return cmd_results_new(CMD_FAILURE, "Invalid idle source");
70 } 70 }
71 config->handler_context.seat_config->idle_wake_sources = sources; 71 config->handler_context.seat_config->idle_wake_sources = sources;
72 sway_log(SWAY_INFO, "Warning: seat idle_wake is deprecated");
73 if (config->reading) {
74 config_add_swaynag_warning("seat idle_wake is deprecated. "
75 "Only seat idle_inhibit is supported.");
76 }
72 return cmd_results_new(CMD_SUCCESS, NULL); 77 return cmd_results_new(CMD_SUCCESS, NULL);
73} 78}
diff --git a/sway/commands/seat/xcursor_theme.c b/sway/commands/seat/xcursor_theme.c
index 202f35b9..61322a57 100644
--- a/sway/commands/seat/xcursor_theme.c
+++ b/sway/commands/seat/xcursor_theme.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <string.h> 1#include <string.h>
3#include "sway/commands.h" 2#include "sway/commands.h"
4#include "sway/config.h" 3#include "sway/config.h"
diff --git a/sway/commands/set.c b/sway/commands/set.c
index c539e9fc..ba384c7c 100644
--- a/sway/commands/set.c
+++ b/sway/commands/set.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <stdio.h> 1#include <stdio.h>
3#include <string.h> 2#include <string.h>
4#include <strings.h> 3#include <strings.h>
diff --git a/sway/commands/show_marks.c b/sway/commands/show_marks.c
index 0d373b80..60cef9fa 100644
--- a/sway/commands/show_marks.c
+++ b/sway/commands/show_marks.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <string.h> 1#include <string.h>
3#include "sway/commands.h" 2#include "sway/commands.h"
4#include "sway/config.h" 3#include "sway/config.h"
@@ -10,8 +9,8 @@
10#include "stringop.h" 9#include "stringop.h"
11#include "util.h" 10#include "util.h"
12 11
13static void rebuild_marks_iterator(struct sway_container *con, void *data) { 12static void title_bar_update_iterator(struct sway_container *con, void *data) {
14 container_update_marks_textures(con); 13 container_update_marks(con);
15} 14}
16 15
17struct cmd_results *cmd_show_marks(int argc, char **argv) { 16struct cmd_results *cmd_show_marks(int argc, char **argv) {
@@ -23,12 +22,7 @@ struct cmd_results *cmd_show_marks(int argc, char **argv) {
23 config->show_marks = parse_boolean(argv[0], config->show_marks); 22 config->show_marks = parse_boolean(argv[0], config->show_marks);
24 23
25 if (config->show_marks) { 24 if (config->show_marks) {
26 root_for_each_container(rebuild_marks_iterator, NULL); 25 root_for_each_container(title_bar_update_iterator, NULL);
27 }
28
29 for (int i = 0; i < root->outputs->length; ++i) {
30 struct sway_output *output = root->outputs->items[i];
31 output_damage_whole(output);
32 } 26 }
33 27
34 return cmd_results_new(CMD_SUCCESS, NULL); 28 return cmd_results_new(CMD_SUCCESS, NULL);
diff --git a/sway/commands/smart_gaps.c b/sway/commands/smart_gaps.c
index b27f9ccd..a6d165dc 100644
--- a/sway/commands/smart_gaps.c
+++ b/sway/commands/smart_gaps.c
@@ -15,7 +15,12 @@ struct cmd_results *cmd_smart_gaps(int argc, char **argv) {
15 return error; 15 return error;
16 } 16 }
17 17
18 config->smart_gaps = parse_boolean(argv[0], config->smart_gaps); 18 if (strcmp(argv[0], "inverse_outer") == 0) {
19 config->smart_gaps = SMART_GAPS_INVERSE_OUTER;
20 } else {
21 config->smart_gaps = parse_boolean(argv[0], config->smart_gaps)
22 ? SMART_GAPS_ON : SMART_GAPS_OFF;
23 }
19 24
20 arrange_root(); 25 arrange_root();
21 26
diff --git a/sway/commands/split.c b/sway/commands/split.c
index 782bab02..500a497d 100644
--- a/sway/commands/split.c
+++ b/sway/commands/split.c
@@ -14,7 +14,7 @@ static struct cmd_results *do_split(int layout) {
14 struct sway_workspace *ws = config->handler_context.workspace; 14 struct sway_workspace *ws = config->handler_context.workspace;
15 if (con) { 15 if (con) {
16 if (container_is_scratchpad_hidden_or_child(con) && 16 if (container_is_scratchpad_hidden_or_child(con) &&
17 con->fullscreen_mode != FULLSCREEN_GLOBAL) { 17 con->pending.fullscreen_mode != FULLSCREEN_GLOBAL) {
18 return cmd_results_new(CMD_FAILURE, 18 return cmd_results_new(CMD_FAILURE,
19 "Cannot split a hidden scratchpad container"); 19 "Cannot split a hidden scratchpad container");
20 } 20 }
@@ -32,6 +32,24 @@ static struct cmd_results *do_split(int layout) {
32 return cmd_results_new(CMD_SUCCESS, NULL); 32 return cmd_results_new(CMD_SUCCESS, NULL);
33} 33}
34 34
35static struct cmd_results *do_unsplit(void) {
36 struct sway_container *con = config->handler_context.container;
37 struct sway_workspace *ws = config->handler_context.workspace;
38
39 if (con && con->pending.parent && con->pending.parent->pending.children->length == 1) {
40 container_flatten(con->pending.parent);
41 } else {
42 return cmd_results_new(CMD_FAILURE, "Can only flatten a child container with no siblings");
43 }
44
45 if (root->fullscreen_global) {
46 arrange_root();
47 } else {
48 arrange_workspace(ws);
49 }
50 return cmd_results_new(CMD_SUCCESS, NULL);
51}
52
35struct cmd_results *cmd_split(int argc, char **argv) { 53struct cmd_results *cmd_split(int argc, char **argv) {
36 struct cmd_results *error = NULL; 54 struct cmd_results *error = NULL;
37 if ((error = checkarg(argc, "split", EXPECTED_EQUAL_TO, 1))) { 55 if ((error = checkarg(argc, "split", EXPECTED_EQUAL_TO, 1))) {
@@ -55,6 +73,9 @@ struct cmd_results *cmd_split(int argc, char **argv) {
55 } else { 73 } else {
56 return do_split(L_VERT); 74 return do_split(L_VERT);
57 } 75 }
76 } else if (strcasecmp(argv[0], "n") == 0 ||
77 strcasecmp(argv[0], "none") == 0) {
78 return do_unsplit();
58 } else { 79 } else {
59 return cmd_results_new(CMD_FAILURE, 80 return cmd_results_new(CMD_FAILURE,
60 "Invalid split command (expected either horizontal or vertical)."); 81 "Invalid split command (expected either horizontal or vertical).");
diff --git a/sway/commands/sticky.c b/sway/commands/sticky.c
index 3c93a276..9b09a0f9 100644
--- a/sway/commands/sticky.c
+++ b/sway/commands/sticky.c
@@ -29,14 +29,14 @@ struct cmd_results *cmd_sticky(int argc, char **argv) {
29 !container_is_scratchpad_hidden(container)) { 29 !container_is_scratchpad_hidden(container)) {
30 // move container to active workspace 30 // move container to active workspace
31 struct sway_workspace *active_workspace = 31 struct sway_workspace *active_workspace =
32 output_get_active_workspace(container->workspace->output); 32 output_get_active_workspace(container->pending.workspace->output);
33 if (!sway_assert(active_workspace, 33 if (!sway_assert(active_workspace,
34 "Expected output to have a workspace")) { 34 "Expected output to have a workspace")) {
35 return cmd_results_new(CMD_FAILURE, 35 return cmd_results_new(CMD_FAILURE,
36 "Expected output to have a workspace"); 36 "Expected output to have a workspace");
37 } 37 }
38 if (container->workspace != active_workspace) { 38 if (container->pending.workspace != active_workspace) {
39 struct sway_workspace *old_workspace = container->workspace; 39 struct sway_workspace *old_workspace = container->pending.workspace;
40 container_detach(container); 40 container_detach(container);
41 workspace_add_floating(active_workspace, container); 41 workspace_add_floating(active_workspace, container);
42 container_handle_fullscreen_reparent(container); 42 container_handle_fullscreen_reparent(container);
diff --git a/sway/commands/swap.c b/sway/commands/swap.c
index a7f9691b..c0b0d0b9 100644
--- a/sway/commands/swap.c
+++ b/sway/commands/swap.c
@@ -1,10 +1,10 @@
1#define _POSIX_C_SOURCE 200809L
2#include <strings.h> 1#include <strings.h>
3#include "config.h" 2#include "config.h"
4#include "log.h" 3#include "log.h"
5#include "sway/commands.h" 4#include "sway/commands.h"
6#include "sway/output.h" 5#include "sway/output.h"
7#include "sway/tree/arrange.h" 6#include "sway/tree/arrange.h"
7#include "sway/tree/container.h"
8#include "sway/tree/root.h" 8#include "sway/tree/root.h"
9#include "sway/tree/view.h" 9#include "sway/tree/view.h"
10#include "sway/tree/workspace.h" 10#include "sway/tree/workspace.h"
@@ -13,186 +13,12 @@
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->x = con1->x;
20 temp->y = con1->y;
21 temp->width = con1->width;
22 temp->height = con1->height;
23 temp->width_fraction = con1->width_fraction;
24 temp->height_fraction = con1->height_fraction;
25 temp->parent = con1->parent;
26 temp->workspace = con1->workspace;
27 bool temp_floating = container_is_floating(con1);
28
29 con1->x = con2->x;
30 con1->y = con2->y;
31 con1->width = con2->width;
32 con1->height = con2->height;
33 con1->width_fraction = con2->width_fraction;
34 con1->height_fraction = con2->height_fraction;
35
36 con2->x = temp->x;
37 con2->y = temp->y;
38 con2->width = temp->width;
39 con2->height = temp->height;
40 con2->width_fraction = temp->width_fraction;
41 con2->height_fraction = temp->height_fraction;
42
43 int temp_index = container_sibling_index(con1);
44 if (con2->parent) {
45 container_insert_child(con2->parent, con1,
46 container_sibling_index(con2));
47 } else if (container_is_floating(con2)) {
48 workspace_add_floating(con2->workspace, con1);
49 } else {
50 workspace_insert_tiling(con2->workspace, con1,
51 container_sibling_index(con2));
52 }
53 if (temp->parent) {
54 container_insert_child(temp->parent, con2, temp_index);
55 } else if (temp_floating) {
56 workspace_add_floating(temp->workspace, con2);
57 } else {
58 workspace_insert_tiling(temp->workspace, con2, temp_index);
59 }
60
61 free(temp);
62}
63
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->workspace;
69 struct sway_workspace *ws2 = con2->workspace;
70 enum sway_container_layout layout1 = container_parent_layout(con1);
71 enum sway_container_layout layout2 = container_parent_layout(con2);
72 if (focus == con1 && (layout2 == L_TABBED || layout2 == L_STACKED)) {
73 if (workspace_is_visible(ws2)) {
74 seat_set_focus(seat, &con2->node);
75 }
76 seat_set_focus_container(seat, ws1 != ws2 ? con2 : con1);
77 } else if (focus == con2 && (layout1 == L_TABBED
78 || layout1 == L_STACKED)) {
79 if (workspace_is_visible(ws1)) {
80 seat_set_focus(seat, &con1->node);
81 }
82 seat_set_focus_container(seat, ws1 != ws2 ? con1 : con2);
83 } else if (ws1 != ws2) {
84 seat_set_focus_container(seat, focus == con1 ? con2 : con1);
85 } else {
86 seat_set_focus_container(seat, focus);
87 }
88 } else {
89 seat_set_focus_container(seat, focus);
90 }
91
92 if (root->fullscreen_global) {
93 seat_set_focus(seat,
94 seat_get_focus_inactive(seat, &root->fullscreen_global->node));
95 }
96}
97
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->fullscreen_mode;
129 enum sway_fullscreen_mode fs2 = con2->fullscreen_mode;
130 if (fs1) {
131 container_fullscreen_disable(con1);
132 }
133 if (fs2) {
134 container_fullscreen_disable(con2);
135 }
136
137 struct sway_seat *seat = config->handler_context.seat;
138 struct sway_container *focus = seat_get_focused_container(seat);
139 struct sway_workspace *vis1 =
140 output_get_active_workspace(con1->workspace->output);
141 struct sway_workspace *vis2 =
142 output_get_active_workspace(con2->workspace->output);
143 if (!sway_assert(vis1 && vis2, "con1 or con2 are on an output without a"
144 "workspace. This should not happen")) {
145 return;
146 }
147
148 char *stored_prev_name = NULL;
149 if (seat->prev_workspace_name) {
150 stored_prev_name = strdup(seat->prev_workspace_name);
151 }
152
153 swap_places(con1, con2);
154
155 if (!workspace_is_visible(vis1)) {
156 seat_set_focus(seat, seat_get_focus_inactive(seat, &vis1->node));
157 }
158 if (!workspace_is_visible(vis2)) {
159 seat_set_focus(seat, seat_get_focus_inactive(seat, &vis2->node));
160 }
161
162 swap_focus(con1, con2, seat, focus);
163
164 if (stored_prev_name) {
165 free(seat->prev_workspace_name);
166 seat->prev_workspace_name = stored_prev_name;
167 }
168
169 if (scratch1) {
170 root_scratchpad_add_container(con2, NULL);
171 if (!hidden1) {
172 root_scratchpad_show(con2);
173 }
174 }
175 if (scratch2) {
176 root_scratchpad_add_container(con1, NULL);
177 if (!hidden2) {
178 root_scratchpad_show(con1);
179 }
180 }
181
182 if (fs1) {
183 container_set_fullscreen(con2, fs1);
184 }
185 if (fs2) {
186 container_set_fullscreen(con1, fs2);
187 }
188}
189
190static bool test_con_id(struct sway_container *container, void *data) { 16static bool test_con_id(struct sway_container *container, void *data) {
191 size_t *con_id = data; 17 size_t *con_id = data;
192 return container->node.id == *con_id; 18 return container->node.id == *con_id;
193} 19}
194 20
195#if HAVE_XWAYLAND 21#if WLR_HAS_XWAYLAND
196static bool test_id(struct sway_container *container, void *data) { 22static bool test_id(struct sway_container *container, void *data) {
197 xcb_window_t *wid = data; 23 xcb_window_t *wid = data;
198 return (container->view && container->view->type == SWAY_VIEW_XWAYLAND 24 return (container->view && container->view->type == SWAY_VIEW_XWAYLAND
@@ -219,7 +45,7 @@ struct cmd_results *cmd_swap(int argc, char **argv) {
219 } 45 }
220 46
221 if (strcasecmp(argv[0], "container") || strcasecmp(argv[1], "with")) { 47 if (strcasecmp(argv[0], "container") || strcasecmp(argv[1], "with")) {
222 return cmd_results_new(CMD_INVALID, expected_syntax); 48 return cmd_results_new(CMD_INVALID, "%s", expected_syntax);
223 } 49 }
224 50
225 struct sway_container *current = config->handler_context.container; 51 struct sway_container *current = config->handler_context.container;
@@ -227,7 +53,7 @@ struct cmd_results *cmd_swap(int argc, char **argv) {
227 53
228 char *value = join_args(argv + 3, argc - 3); 54 char *value = join_args(argv + 3, argc - 3);
229 if (strcasecmp(argv[2], "id") == 0) { 55 if (strcasecmp(argv[2], "id") == 0) {
230#if HAVE_XWAYLAND 56#if WLR_HAS_XWAYLAND
231 xcb_window_t id = strtol(value, NULL, 0); 57 xcb_window_t id = strtol(value, NULL, 0);
232 other = root_find_container(test_id, &id); 58 other = root_find_container(test_id, &id);
233#endif 59#endif
@@ -238,7 +64,7 @@ struct cmd_results *cmd_swap(int argc, char **argv) {
238 other = root_find_container(test_mark, value); 64 other = root_find_container(test_mark, value);
239 } else { 65 } else {
240 free(value); 66 free(value);
241 return cmd_results_new(CMD_INVALID, expected_syntax); 67 return cmd_results_new(CMD_INVALID, "%s", expected_syntax);
242 } 68 }
243 69
244 if (!other) { 70 if (!other) {
@@ -247,6 +73,9 @@ struct cmd_results *cmd_swap(int argc, char **argv) {
247 } else if (!current) { 73 } else if (!current) {
248 error = cmd_results_new(CMD_FAILURE, 74 error = cmd_results_new(CMD_FAILURE,
249 "Can only swap with containers and views"); 75 "Can only swap with containers and views");
76 } else if (current == other) {
77 error = cmd_results_new(CMD_FAILURE,
78 "Cannot swap a container with itself");
250 } else if (container_has_ancestor(current, other) 79 } else if (container_has_ancestor(current, other)
251 || container_has_ancestor(other, current)) { 80 || container_has_ancestor(other, current)) {
252 error = cmd_results_new(CMD_FAILURE, 81 error = cmd_results_new(CMD_FAILURE,
diff --git a/sway/commands/title_align.c b/sway/commands/title_align.c
index c30355de..be298a29 100644
--- a/sway/commands/title_align.c
+++ b/sway/commands/title_align.c
@@ -4,6 +4,10 @@
4#include "sway/tree/container.h" 4#include "sway/tree/container.h"
5#include "sway/tree/root.h" 5#include "sway/tree/root.h"
6 6
7static void arrange_title_bar_iterator(struct sway_container *con, void *data) {
8 container_arrange_title_bar(con);
9}
10
7struct cmd_results *cmd_title_align(int argc, char **argv) { 11struct cmd_results *cmd_title_align(int argc, char **argv) {
8 struct cmd_results *error = NULL; 12 struct cmd_results *error = NULL;
9 if ((error = checkarg(argc, "title_align", EXPECTED_AT_LEAST, 1))) { 13 if ((error = checkarg(argc, "title_align", EXPECTED_AT_LEAST, 1))) {
@@ -21,10 +25,7 @@ struct cmd_results *cmd_title_align(int argc, char **argv) {
21 "Expected 'title_align left|center|right'"); 25 "Expected 'title_align left|center|right'");
22 } 26 }
23 27
24 for (int i = 0; i < root->outputs->length; ++i) { 28 root_for_each_container(arrange_title_bar_iterator, NULL);
25 struct sway_output *output = root->outputs->items[i];
26 output_damage_whole(output);
27 }
28 29
29 return cmd_results_new(CMD_SUCCESS, NULL); 30 return cmd_results_new(CMD_SUCCESS, NULL);
30} 31}
diff --git a/sway/commands/title_format.c b/sway/commands/title_format.c
index 9d312470..0b2ea265 100644
--- a/sway/commands/title_format.c
+++ b/sway/commands/title_format.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <string.h> 1#include <string.h>
3#include "sway/commands.h" 2#include "sway/commands.h"
4#include "sway/config.h" 3#include "sway/config.h"
@@ -23,6 +22,5 @@ struct cmd_results *cmd_title_format(int argc, char **argv) {
23 } 22 }
24 view->title_format = format; 23 view->title_format = format;
25 view_update_title(view, true); 24 view_update_title(view, true);
26 config_update_font_height(true);
27 return cmd_results_new(CMD_SUCCESS, NULL); 25 return cmd_results_new(CMD_SUCCESS, NULL);
28} 26}
diff --git a/sway/commands/titlebar_border_thickness.c b/sway/commands/titlebar_border_thickness.c
index 7c27c163..fa3db3c5 100644
--- a/sway/commands/titlebar_border_thickness.c
+++ b/sway/commands/titlebar_border_thickness.c
@@ -27,7 +27,6 @@ struct cmd_results *cmd_titlebar_border_thickness(int argc, char **argv) {
27 "Expected output to have a workspace"); 27 "Expected output to have a workspace");
28 } 28 }
29 arrange_workspace(ws); 29 arrange_workspace(ws);
30 output_damage_whole(output);
31 } 30 }
32 31
33 return cmd_results_new(CMD_SUCCESS, NULL); 32 return cmd_results_new(CMD_SUCCESS, NULL);
diff --git a/sway/commands/titlebar_padding.c b/sway/commands/titlebar_padding.c
index 29ce59ff..6999f7a2 100644
--- a/sway/commands/titlebar_padding.c
+++ b/sway/commands/titlebar_padding.c
@@ -33,7 +33,6 @@ struct cmd_results *cmd_titlebar_padding(int argc, char **argv) {
33 for (int i = 0; i < root->outputs->length; ++i) { 33 for (int i = 0; i < root->outputs->length; ++i) {
34 struct sway_output *output = root->outputs->items[i]; 34 struct sway_output *output = root->outputs->items[i];
35 arrange_workspace(output_get_active_workspace(output)); 35 arrange_workspace(output_get_active_workspace(output));
36 output_damage_whole(output);
37 } 36 }
38 37
39 return cmd_results_new(CMD_SUCCESS, NULL); 38 return cmd_results_new(CMD_SUCCESS, NULL);
diff --git a/sway/commands/unmark.c b/sway/commands/unmark.c
index cedfcfb2..4aba5bae 100644
--- a/sway/commands/unmark.c
+++ b/sway/commands/unmark.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <string.h> 1#include <string.h>
3#include "sway/commands.h" 2#include "sway/commands.h"
4#include "sway/config.h" 3#include "sway/config.h"
@@ -8,9 +7,13 @@
8#include "log.h" 7#include "log.h"
9#include "stringop.h" 8#include "stringop.h"
10 9
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
@@ -21,7 +24,7 @@ static void remove_all_marks_iterator(struct sway_container *con, void *data) {
21struct cmd_results *cmd_unmark(int argc, char **argv) { 24struct cmd_results *cmd_unmark(int argc, char **argv) {
22 // Determine the container 25 // Determine the container
23 struct sway_container *con = NULL; 26 struct sway_container *con = NULL;
24 if (config->handler_context.using_criteria) { 27 if (config->handler_context.node_overridden) {
25 con = config->handler_context.container; 28 con = config->handler_context.container;
26 } 29 }
27 30
@@ -38,8 +41,7 @@ struct cmd_results *cmd_unmark(int argc, char **argv) {
38 } 41 }
39 } else if (con && !mark) { 42 } else if (con && !mark) {
40 // Clear all marks from the given container 43 // Clear all marks from the given container
41 container_clear_marks(con); 44 remove_mark(con);
42 container_update_marks_textures(con);
43 } else if (!con && mark) { 45 } else if (!con && mark) {
44 // Remove mark from whichever container has it 46 // Remove mark from whichever container has it
45 container_find_and_unmark(mark); 47 container_find_and_unmark(mark);
diff --git a/sway/commands/workspace.c b/sway/commands/workspace.c
index 9ff1c97d..37a201b4 100644
--- a/sway/commands/workspace.c
+++ b/sway/commands/workspace.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <ctype.h> 1#include <ctype.h>
3#include <limits.h> 2#include <limits.h>
4#include <string.h> 3#include <string.h>
@@ -61,7 +60,7 @@ static struct cmd_results *cmd_workspace_gaps(int argc, char **argv,
61 const char expected[] = "Expected 'workspace <name> gaps " 60 const char expected[] = "Expected 'workspace <name> gaps "
62 "inner|outer|horizontal|vertical|top|right|bottom|left <px>'"; 61 "inner|outer|horizontal|vertical|top|right|bottom|left <px>'";
63 if (gaps_location == 0) { 62 if (gaps_location == 0) {
64 return cmd_results_new(CMD_INVALID, expected); 63 return cmd_results_new(CMD_INVALID, "%s", expected);
65 } 64 }
66 struct cmd_results *error = NULL; 65 struct cmd_results *error = NULL;
67 if ((error = checkarg(argc, "workspace", EXPECTED_EQUAL_TO, 66 if ((error = checkarg(argc, "workspace", EXPECTED_EQUAL_TO,
@@ -79,7 +78,7 @@ static struct cmd_results *cmd_workspace_gaps(int argc, char **argv,
79 char *end; 78 char *end;
80 int amount = strtol(argv[gaps_location + 2], &end, 10); 79 int amount = strtol(argv[gaps_location + 2], &end, 10);
81 if (strlen(end)) { 80 if (strlen(end)) {
82 return cmd_results_new(CMD_FAILURE, expected); 81 return cmd_results_new(CMD_FAILURE, "%s", expected);
83 } 82 }
84 83
85 bool valid = false; 84 bool valid = false;
@@ -110,7 +109,7 @@ static struct cmd_results *cmd_workspace_gaps(int argc, char **argv,
110 } 109 }
111 } 110 }
112 if (!valid) { 111 if (!valid) {
113 return cmd_results_new(CMD_INVALID, expected); 112 return cmd_results_new(CMD_INVALID, "%s", expected);
114 } 113 }
115 114
116 // Prevent invalid gaps configurations. 115 // Prevent invalid gaps configurations.
@@ -178,25 +177,20 @@ struct cmd_results *cmd_workspace(int argc, char **argv) {
178 } 177 }
179 178
180 if (root->fullscreen_global) { 179 if (root->fullscreen_global) {
181 return cmd_results_new(CMD_FAILURE, "workspace", 180 return cmd_results_new(CMD_FAILURE,
182 "Can't switch workspaces while fullscreen global"); 181 "Can't switch workspaces while fullscreen global");
183 } 182 }
184 183
185 bool no_auto_back_and_forth = false; 184 bool auto_back_and_forth = true;
186 while (strcasecmp(argv[0], "--no-auto-back-and-forth") == 0) { 185 while (strcasecmp(argv[0], "--no-auto-back-and-forth") == 0) {
187 no_auto_back_and_forth = true; 186 auto_back_and_forth = false;
188 if ((error = checkarg(--argc, "workspace", EXPECTED_AT_LEAST, 1))) { 187 if ((error = checkarg(--argc, "workspace", EXPECTED_AT_LEAST, 1))) {
189 return error; 188 return error;
190 } 189 }
191 ++argv; 190 ++argv;
192 } 191 }
193 192
194 bool create = argc > 1 && strcasecmp(argv[1], "--create") == 0;
195 struct sway_seat *seat = config->handler_context.seat; 193 struct sway_seat *seat = config->handler_context.seat;
196 struct sway_workspace *current = seat_get_focused_workspace(seat);
197 if (!current) {
198 return cmd_results_new(CMD_FAILURE, "No workspace to switch from");
199 }
200 194
201 struct sway_workspace *ws = NULL; 195 struct sway_workspace *ws = NULL;
202 if (strcasecmp(argv[0], "number") == 0) { 196 if (strcasecmp(argv[0], "number") == 0) {
@@ -213,14 +207,15 @@ struct cmd_results *cmd_workspace(int argc, char **argv) {
213 ws = workspace_create(NULL, name); 207 ws = workspace_create(NULL, name);
214 free(name); 208 free(name);
215 } 209 }
210 if (ws && auto_back_and_forth) {
211 ws = workspace_auto_back_and_forth(ws);
212 }
216 } else if (strcasecmp(argv[0], "next") == 0 || 213 } else if (strcasecmp(argv[0], "next") == 0 ||
217 strcasecmp(argv[0], "prev") == 0 || 214 strcasecmp(argv[0], "prev") == 0 ||
215 strcasecmp(argv[0], "next_on_output") == 0 ||
216 strcasecmp(argv[0], "prev_on_output") == 0 ||
218 strcasecmp(argv[0], "current") == 0) { 217 strcasecmp(argv[0], "current") == 0) {
219 ws = workspace_by_name(argv[0]); 218 ws = workspace_by_name(argv[0]);
220 } else if (strcasecmp(argv[0], "next_on_output") == 0) {
221 ws = workspace_output_next(current, create);
222 } else if (strcasecmp(argv[0], "prev_on_output") == 0) {
223 ws = workspace_output_prev(current, create);
224 } else if (strcasecmp(argv[0], "back_and_forth") == 0) { 219 } else if (strcasecmp(argv[0], "back_and_forth") == 0) {
225 if (!seat->prev_workspace_name) { 220 if (!seat->prev_workspace_name) {
226 return cmd_results_new(CMD_INVALID, 221 return cmd_results_new(CMD_INVALID,
@@ -235,11 +230,14 @@ struct cmd_results *cmd_workspace(int argc, char **argv) {
235 ws = workspace_create(NULL, name); 230 ws = workspace_create(NULL, name);
236 } 231 }
237 free(name); 232 free(name);
233 if (ws && auto_back_and_forth) {
234 ws = workspace_auto_back_and_forth(ws);
235 }
238 } 236 }
239 if (!ws) { 237 if (!ws) {
240 return cmd_results_new(CMD_FAILURE, "No workspace to switch to"); 238 return cmd_results_new(CMD_FAILURE, "No workspace to switch to");
241 } 239 }
242 workspace_switch(ws, no_auto_back_and_forth); 240 workspace_switch(ws);
243 seat_consider_warp_to_focus(seat); 241 seat_consider_warp_to_focus(seat);
244 } 242 }
245 return cmd_results_new(CMD_SUCCESS, NULL); 243 return cmd_results_new(CMD_SUCCESS, NULL);
diff --git a/sway/commands/xwayland.c b/sway/commands/xwayland.c
index 6ca26923..c0b175fc 100644
--- a/sway/commands/xwayland.c
+++ b/sway/commands/xwayland.c
@@ -10,7 +10,7 @@ struct cmd_results *cmd_xwayland(int argc, char **argv) {
10 return error; 10 return error;
11 } 11 }
12 12
13#ifdef HAVE_XWAYLAND 13#ifdef WLR_HAS_XWAYLAND
14 enum xwayland_mode xwayland; 14 enum xwayland_mode xwayland;
15 if (strcmp(argv[0], "force") == 0) { 15 if (strcmp(argv[0], "force") == 0) {
16 xwayland = XWAYLAND_MODE_IMMEDIATE; 16 xwayland = XWAYLAND_MODE_IMMEDIATE;
@@ -20,6 +20,8 @@ struct cmd_results *cmd_xwayland(int argc, char **argv) {
20 xwayland = XWAYLAND_MODE_DISABLED; 20 xwayland = XWAYLAND_MODE_DISABLED;
21 } 21 }
22 22
23 // config->xwayland is reset to the previous value on reload in
24 // load_main_config()
23 if (config->reloading && config->xwayland != xwayland) { 25 if (config->reloading && config->xwayland != xwayland) {
24 return cmd_results_new(CMD_FAILURE, 26 return cmd_results_new(CMD_FAILURE,
25 "xwayland can only be enabled/disabled at launch"); 27 "xwayland can only be enabled/disabled at launch");