summaryrefslogtreecommitdiffstats
path: root/sway
diff options
context:
space:
mode:
Diffstat (limited to 'sway')
-rw-r--r--sway/commands.c45
-rw-r--r--sway/commands/bar/binding_mode_indicator.c6
-rw-r--r--sway/commands/bind.c2
-rw-r--r--sway/commands/gaps.c306
-rw-r--r--sway/commands/input.c11
-rw-r--r--sway/commands/input/accel_profile.c13
-rw-r--r--sway/commands/input/click_method.c15
-rw-r--r--sway/commands/input/drag.c26
-rw-r--r--sway/commands/input/drag_lock.c12
-rw-r--r--sway/commands/input/dwt.c12
-rw-r--r--sway/commands/input/events.c17
-rw-r--r--sway/commands/input/left_handed.c10
-rw-r--r--sway/commands/input/map_from_region.c42
-rw-r--r--sway/commands/input/map_to_output.c10
-rw-r--r--sway/commands/input/middle_emulation.c13
-rw-r--r--sway/commands/input/natural_scroll.c10
-rw-r--r--sway/commands/input/pointer_accel.c11
-rw-r--r--sway/commands/input/repeat_delay.c11
-rw-r--r--sway/commands/input/repeat_rate.c11
-rw-r--r--sway/commands/input/scroll_button.c13
-rw-r--r--sway/commands/input/scroll_method.c17
-rw-r--r--sway/commands/input/tap.c14
-rw-r--r--sway/commands/input/tap_button_map.c13
-rw-r--r--sway/commands/input/xkb_capslock.c13
-rw-r--r--sway/commands/input/xkb_layout.c17
-rw-r--r--sway/commands/input/xkb_model.c17
-rw-r--r--sway/commands/input/xkb_numlock.c13
-rw-r--r--sway/commands/input/xkb_options.c17
-rw-r--r--sway/commands/input/xkb_rules.c17
-rw-r--r--sway/commands/input/xkb_variant.c17
-rw-r--r--sway/commands/layout.c3
-rw-r--r--sway/commands/move.c64
-rw-r--r--sway/commands/output/background.c18
-rw-r--r--sway/commands/resize.c30
-rw-r--r--sway/commands/workspace.c74
-rw-r--r--sway/config.c11
-rw-r--r--sway/config/bar.c26
-rw-r--r--sway/config/input.c62
-rw-r--r--sway/config/output.c4
-rw-r--r--sway/config/seat.c2
-rw-r--r--sway/debug-tree.c2
-rw-r--r--sway/desktop/transaction.c66
-rw-r--r--sway/desktop/xdg_shell.c2
-rw-r--r--sway/desktop/xdg_shell_v6.c2
-rw-r--r--sway/desktop/xwayland.c2
-rw-r--r--sway/input/cursor.c62
-rw-r--r--sway/input/input-manager.c22
-rw-r--r--sway/input/seat.c44
-rw-r--r--sway/ipc-json.c2
-rw-r--r--sway/ipc-server.c15
-rw-r--r--sway/main.c84
-rw-r--r--sway/meson.build3
-rw-r--r--sway/sway-bar.5.scd3
-rw-r--r--sway/sway-input.5.scd7
-rw-r--r--sway/sway.5.scd82
-rw-r--r--sway/tree/arrange.c3
-rw-r--r--sway/tree/container.c38
-rw-r--r--sway/tree/output.c30
-rw-r--r--sway/tree/root.c21
-rw-r--r--sway/tree/view.c29
-rw-r--r--sway/tree/workspace.c81
61 files changed, 831 insertions, 814 deletions
diff --git a/sway/commands.c b/sway/commands.c
index 07169f1e..03761c52 100644
--- a/sway/commands.c
+++ b/sway/commands.c
@@ -55,22 +55,6 @@ struct cmd_results *checkarg(int argc, const char *name, enum expected_args type
55 return error; 55 return error;
56} 56}
57 57
58void apply_input_config(struct input_config *input) {
59 int i;
60 i = list_seq_find(config->input_configs, input_identifier_cmp, input->identifier);
61 if (i >= 0) {
62 // merge existing config
63 struct input_config *ic = config->input_configs->items[i];
64 merge_input_config(ic, input);
65 free_input_config(input);
66 input = ic;
67 } else {
68 list_add(config->input_configs, input);
69 }
70
71 input_manager_apply_input_config(input_manager, input);
72}
73
74void apply_seat_config(struct seat_config *seat_config) { 58void apply_seat_config(struct seat_config *seat_config) {
75 int i; 59 int i;
76 i = list_seq_find(config->seat_configs, seat_name_cmp, seat_config->name); 60 i = list_seq_find(config->seat_configs, seat_name_cmp, seat_config->name);
@@ -237,7 +221,8 @@ static void set_config_node(struct sway_node *node) {
237 } 221 }
238} 222}
239 223
240struct cmd_results *execute_command(char *_exec, struct sway_seat *seat) { 224struct cmd_results *execute_command(char *_exec, struct sway_seat *seat,
225 struct sway_container *con) {
241 // Even though this function will process multiple commands we will only 226 // Even though this function will process multiple commands we will only
242 // return the last error, if any (for now). (Since we have access to an 227 // return the last error, if any (for now). (Since we have access to an
243 // error string we could e.g. concatenate all errors there.) 228 // error string we could e.g. concatenate all errors there.)
@@ -256,6 +241,15 @@ struct cmd_results *execute_command(char *_exec, struct sway_seat *seat) {
256 } 241 }
257 } 242 }
258 243
244 // This is the container or workspace which this command will run on.
245 // Ignored if the command string contains criteria.
246 struct sway_node *node;
247 if (con) {
248 node = &con->node;
249 } else {
250 node = seat_get_focus_inactive(seat, &root->node);
251 }
252
259 config->handler_context.seat = seat; 253 config->handler_context.seat = seat;
260 254
261 head = exec; 255 head = exec;
@@ -318,9 +312,7 @@ struct cmd_results *execute_command(char *_exec, struct sway_seat *seat) {
318 } 312 }
319 313
320 if (!config->handler_context.using_criteria) { 314 if (!config->handler_context.using_criteria) {
321 // without criteria, the command acts upon the focused 315 set_config_node(node);
322 // container
323 set_config_node(seat_get_focus_inactive(seat, &root->node));
324 struct cmd_results *res = handler->handle(argc-1, argv+1); 316 struct cmd_results *res = handler->handle(argc-1, argv+1);
325 if (res->status != CMD_SUCCESS) { 317 if (res->status != CMD_SUCCESS) {
326 free_argv(argc, argv); 318 free_argv(argc, argv);
@@ -399,14 +391,12 @@ struct cmd_results *config_command(char *exec) {
399 // Var replacement, for all but first argument of set 391 // Var replacement, for all but first argument of set
400 // TODO commands 392 // TODO commands
401 for (i = handler->handle == cmd_set ? 2 : 1; i < argc; ++i) { 393 for (i = handler->handle == cmd_set ? 2 : 1; i < argc; ++i) {
394 if (*argv[i] == '\"' || *argv[i] == '\'') {
395 strip_quotes(argv[i]);
396 }
402 argv[i] = do_var_replacement(argv[i]); 397 argv[i] = do_var_replacement(argv[i]);
403 unescape_string(argv[i]); 398 unescape_string(argv[i]);
404 } 399 }
405 // Strip quotes for first argument.
406 // TODO This part needs to be handled much better
407 if (argc>1 && (*argv[1] == '\"' || *argv[1] == '\'')) {
408 strip_quotes(argv[1]);
409 }
410 if (handler->handle) { 400 if (handler->handle) {
411 results = handler->handle(argc-1, argv+1); 401 results = handler->handle(argc-1, argv+1);
412 } else { 402 } else {
@@ -430,11 +420,6 @@ struct cmd_results *config_subcommand(char **argv, int argc,
430 char *input = argv[0] ? argv[0] : "(empty)"; 420 char *input = argv[0] ? argv[0] : "(empty)";
431 return cmd_results_new(CMD_INVALID, input, "Unknown/invalid command"); 421 return cmd_results_new(CMD_INVALID, input, "Unknown/invalid command");
432 } 422 }
433 // Strip quotes for first argument.
434 // TODO This part needs to be handled much better
435 if (argc > 1 && (*argv[1] == '\"' || *argv[1] == '\'')) {
436 strip_quotes(argv[1]);
437 }
438 if (handler->handle) { 423 if (handler->handle) {
439 return handler->handle(argc - 1, argv + 1); 424 return handler->handle(argc - 1, argv + 1);
440 } 425 }
diff --git a/sway/commands/bar/binding_mode_indicator.c b/sway/commands/bar/binding_mode_indicator.c
index 0c48bee9..f18b8d7c 100644
--- a/sway/commands/bar/binding_mode_indicator.c
+++ b/sway/commands/bar/binding_mode_indicator.c
@@ -21,7 +21,9 @@ struct cmd_results *bar_cmd_binding_mode_indicator(int argc, char **argv) {
21 config->current_bar->binding_mode_indicator = false; 21 config->current_bar->binding_mode_indicator = false;
22 wlr_log(WLR_DEBUG, "Disabling binding mode indicator on bar: %s", 22 wlr_log(WLR_DEBUG, "Disabling binding mode indicator on bar: %s",
23 config->current_bar->id); 23 config->current_bar->id);
24 } else {
25 return cmd_results_new(CMD_INVALID, "binding_mode_indicator",
26 "Invalid value %s", argv[0]);
24 } 27 }
25 return cmd_results_new(CMD_INVALID, "binding_mode_indicator", 28 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
26 "Invalid value %s", argv[0]);
27} 29}
diff --git a/sway/commands/bind.c b/sway/commands/bind.c
index 047018e0..820c2a6a 100644
--- a/sway/commands/bind.c
+++ b/sway/commands/bind.c
@@ -321,7 +321,7 @@ void seat_execute_command(struct sway_seat *seat, struct sway_binding *binding)
321 } 321 }
322 322
323 config->handler_context.seat = seat; 323 config->handler_context.seat = seat;
324 struct cmd_results *results = execute_command(binding->command, NULL); 324 struct cmd_results *results = execute_command(binding->command, NULL, NULL);
325 if (results->status == CMD_SUCCESS) { 325 if (results->status == CMD_SUCCESS) {
326 ipc_event_binding(binding_copy); 326 ipc_event_binding(binding_copy);
327 } else { 327 } else {
diff --git a/sway/commands/gaps.c b/sway/commands/gaps.c
index d676e475..2e0876a9 100644
--- a/sway/commands/gaps.c
+++ b/sway/commands/gaps.c
@@ -1,4 +1,5 @@
1#include <string.h> 1#include <string.h>
2#include <strings.h>
2#include "sway/commands.h" 3#include "sway/commands.h"
3#include "sway/config.h" 4#include "sway/config.h"
4#include "sway/tree/arrange.h" 5#include "sway/tree/arrange.h"
@@ -13,172 +14,173 @@ enum gaps_op {
13 GAPS_OP_SUBTRACT 14 GAPS_OP_SUBTRACT
14}; 15};
15 16
16enum gaps_scope { 17struct gaps_data {
17 GAPS_SCOPE_ALL, 18 bool inner;
18 GAPS_SCOPE_WORKSPACE, 19 enum gaps_op operation;
19 GAPS_SCOPE_CURRENT 20 int amount;
20}; 21};
21 22
22struct cmd_results *cmd_gaps(int argc, char **argv) { 23// gaps edge_gaps on|off|toggle
23 struct cmd_results *error = checkarg(argc, "gaps", EXPECTED_AT_LEAST, 1); 24static struct cmd_results *gaps_edge_gaps(int argc, char **argv) {
24 if (error) { 25 struct cmd_results *error;
26 if ((error = checkarg(argc, "gaps", EXPECTED_AT_LEAST, 2))) {
25 return error; 27 return error;
26 } 28 }
27 29
28 if (strcmp(argv[0], "edge_gaps") == 0) { 30 if (strcmp(argv[1], "on") == 0) {
29 if ((error = checkarg(argc, "gaps", EXPECTED_AT_LEAST, 2))) { 31 config->edge_gaps = true;
30 return error; 32 } else if (strcmp(argv[1], "off") == 0) {
31 } 33 config->edge_gaps = false;
32 34 } else if (strcmp(argv[1], "toggle") == 0) {
33 if (strcmp(argv[1], "on") == 0) { 35 if (!config->active) {
34 config->edge_gaps = true;
35 } else if (strcmp(argv[1], "off") == 0) {
36 config->edge_gaps = false;
37 } else if (strcmp(argv[1], "toggle") == 0) {
38 if (!config->active) {
39 return cmd_results_new(CMD_INVALID, "gaps",
40 "Cannot toggle gaps while not running.");
41 }
42 config->edge_gaps = !config->edge_gaps;
43 } else {
44 return cmd_results_new(CMD_INVALID, "gaps", 36 return cmd_results_new(CMD_INVALID, "gaps",
45 "gaps edge_gaps on|off|toggle"); 37 "Cannot toggle gaps while not running.");
46 } 38 }
47 arrange_root(); 39 config->edge_gaps = !config->edge_gaps;
48 } else { 40 } else {
49 int amount_idx = 0; // the current index in argv 41 return cmd_results_new(CMD_INVALID, "gaps",
50 enum gaps_op op = GAPS_OP_SET; 42 "gaps edge_gaps on|off|toggle");
51 enum gaps_scope scope = GAPS_SCOPE_ALL; 43 }
52 bool inner = true; 44 arrange_root();
53 45 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
54 if (strcmp(argv[0], "inner") == 0) { 46}
55 amount_idx++;
56 inner = true;
57 } else if (strcmp(argv[0], "outer") == 0) {
58 amount_idx++;
59 inner = false;
60 }
61 47
62 // If one of the long variants of the gaps command is used 48// gaps inner|outer <px>
63 // (which starts with inner|outer) check the number of args 49static struct cmd_results *gaps_set_defaults(int argc, char **argv) {
64 if (amount_idx > 0) { // if we've seen inner|outer 50 struct cmd_results *error = checkarg(argc, "gaps", EXPECTED_EQUAL_TO, 2);
65 if (argc > 2) { // check the longest variant 51 if (error) {
66 error = checkarg(argc, "gaps", EXPECTED_EQUAL_TO, 4); 52 return error;
67 if (error) { 53 }
68 return error;
69 }
70 } else { // check the next longest format
71 error = checkarg(argc, "gaps", EXPECTED_EQUAL_TO, 2);
72 if (error) {
73 return error;
74 }
75 }
76 } else {
77 error = checkarg(argc, "gaps", EXPECTED_EQUAL_TO, 1);
78 if (error) {
79 return error;
80 }
81 }
82 54
83 if (argc == 4) { 55 bool inner;
84 // Long format: all|workspace|current. 56 if (strcasecmp(argv[0], "inner") == 0) {
85 if (strcmp(argv[amount_idx], "all") == 0) { 57 inner = true;
86 amount_idx++; 58 } else if (strcasecmp(argv[0], "outer") == 0) {
87 scope = GAPS_SCOPE_ALL; 59 inner = false;
88 } else if (strcmp(argv[amount_idx], "workspace") == 0) { 60 } else {
89 amount_idx++; 61 return cmd_results_new(CMD_INVALID, "gaps",
90 scope = GAPS_SCOPE_WORKSPACE; 62 "Expected 'gaps inner|outer <px>'");
91 } else if (strcmp(argv[amount_idx], "current") == 0) { 63 }
92 amount_idx++;
93 scope = GAPS_SCOPE_CURRENT;
94 }
95
96 // Long format: set|plus|minus
97 if (strcmp(argv[amount_idx], "set") == 0) {
98 amount_idx++;
99 op = GAPS_OP_SET;
100 } else if (strcmp(argv[amount_idx], "plus") == 0) {
101 amount_idx++;
102 op = GAPS_OP_ADD;
103 } else if (strcmp(argv[amount_idx], "minus") == 0) {
104 amount_idx++;
105 op = GAPS_OP_SUBTRACT;
106 }
107 }
108 64
109 char *end; 65 char *end;
110 double val = strtod(argv[amount_idx], &end); 66 int amount = strtol(argv[1], &end, 10);
111 67 if (strlen(end) && strcasecmp(end, "px") != 0) {
112 if (strlen(end) && val == 0.0) { // invalid <amount> 68 return cmd_results_new(CMD_INVALID, "gaps",
113 // guess which variant of the command was attempted 69 "Expected 'gaps inner|outer <px>'");
114 if (argc == 1) { 70 }
115 return cmd_results_new(CMD_INVALID, "gaps", "gaps <amount>"); 71 if (amount < 0) {
116 } 72 amount = 0;
117 if (argc == 2) { 73 }
118 return cmd_results_new(CMD_INVALID, "gaps",
119 "gaps inner|outer <amount>");
120 }
121 return cmd_results_new(CMD_INVALID, "gaps",
122 "gaps inner|outer all|workspace|current set|plus|minus <amount>");
123 }
124 74
125 if (amount_idx == 0) { // gaps <amount> 75 if (inner) {
126 config->gaps_inner = val; 76 config->gaps_inner = amount;
127 config->gaps_outer = val; 77 } else {
128 arrange_root(); 78 config->gaps_outer = amount;
129 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 79 }
130 } 80 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
131 // Other variants. The middle-length variant (gaps inner|outer <amount>) 81}
132 // just defaults the scope to "all" and defaults the op to "set".
133
134 double total;
135 switch (op) {
136 case GAPS_OP_SUBTRACT: {
137 total = (inner ? config->gaps_inner : config->gaps_outer) - val;
138 if (total < 0) {
139 total = 0;
140 }
141 break;
142 }
143 case GAPS_OP_ADD: {
144 total = (inner ? config->gaps_inner : config->gaps_outer) + val;
145 break;
146 }
147 case GAPS_OP_SET: {
148 total = val;
149 break;
150 }
151 }
152 82
153 if (scope == GAPS_SCOPE_ALL) { 83static void configure_gaps(struct sway_workspace *ws, void *_data) {
154 if (inner) { 84 struct gaps_data *data = _data;
155 config->gaps_inner = total; 85 int *prop = data->inner ? &ws->gaps_inner : &ws->gaps_outer;
156 } else { 86
157 config->gaps_outer = total; 87 switch (data->operation) {
158 } 88 case GAPS_OP_SET:
159 arrange_root(); 89 *prop = data->amount;
160 } else { 90 break;
161 if (scope == GAPS_SCOPE_WORKSPACE) { 91 case GAPS_OP_ADD:
162 struct sway_workspace *ws = config->handler_context.workspace; 92 *prop += data->amount;
163 ws->has_gaps = true; 93 break;
164 if (inner) { 94 case GAPS_OP_SUBTRACT:
165 ws->gaps_inner = total; 95 *prop -= data->amount;
166 } else { 96 break;
167 ws->gaps_outer = total; 97 }
168 } 98 if (*prop < 0) {
169 arrange_workspace(ws); 99 *prop = 0;
170 } else { 100 }
171 struct sway_container *c = config->handler_context.container; 101 arrange_workspace(ws);
172 c->has_gaps = true; 102}
173 if (inner) { 103
174 c->gaps_inner = total; 104// gaps inner|outer current|all set|plus|minus <px>
175 } else { 105static struct cmd_results *gaps_set_runtime(int argc, char **argv) {
176 c->gaps_outer = total; 106 struct cmd_results *error = checkarg(argc, "gaps", EXPECTED_EQUAL_TO, 4);
177 } 107 if (error) {
178 arrange_workspace(c->workspace); 108 return error;
179 } 109 }
180 } 110
111 struct gaps_data data;
112
113 if (strcasecmp(argv[0], "inner") == 0) {
114 data.inner = true;
115 } else if (strcasecmp(argv[0], "outer") == 0) {
116 data.inner = false;
117 } else {
118 return cmd_results_new(CMD_INVALID, "gaps",
119 "Expected 'gaps inner|outer current|all set|plus|minus <px>'");
120 }
121
122 bool all;
123 if (strcasecmp(argv[1], "current") == 0) {
124 all = false;
125 } else if (strcasecmp(argv[1], "all") == 0) {
126 all = true;
127 } else {
128 return cmd_results_new(CMD_INVALID, "gaps",
129 "Expected 'gaps inner|outer current|all set|plus|minus <px>'");
130 }
131
132 if (strcasecmp(argv[2], "set") == 0) {
133 data.operation = GAPS_OP_SET;
134 } else if (strcasecmp(argv[2], "plus") == 0) {
135 data.operation = GAPS_OP_ADD;
136 } else if (strcasecmp(argv[2], "minus") == 0) {
137 data.operation = GAPS_OP_SUBTRACT;
138 } else {
139 return cmd_results_new(CMD_INVALID, "gaps",
140 "Expected 'gaps inner|outer current|all set|plus|minus <px>'");
141 }
142
143 char *end;
144 data.amount = strtol(argv[3], &end, 10);
145 if (strlen(end) && strcasecmp(end, "px") != 0) {
146 return cmd_results_new(CMD_INVALID, "gaps",
147 "Expected 'gaps inner|outer current|all set|plus|minus <px>'");
148 }
149
150 if (all) {
151 root_for_each_workspace(configure_gaps, &data);
152 } else {
153 configure_gaps(config->handler_context.workspace, &data);
181 } 154 }
182 155
183 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 156 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
184} 157}
158
159// gaps edge_gaps on|off|toggle
160// gaps inner|outer <px> - sets defaults for workspaces
161// gaps inner|outer current|all set|plus|minus <px> - runtime only
162struct cmd_results *cmd_gaps(int argc, char **argv) {
163 struct cmd_results *error = checkarg(argc, "gaps", EXPECTED_AT_LEAST, 2);
164 if (error) {
165 return error;
166 }
167
168 if (strcmp(argv[0], "edge_gaps") == 0) {
169 return gaps_edge_gaps(argc, argv);
170 }
171
172 if (argc == 2) {
173 return gaps_set_defaults(argc, argv);
174 }
175 if (argc == 4) {
176 if (config->active) {
177 return gaps_set_runtime(argc, argv);
178 } else {
179 return cmd_results_new(CMD_INVALID, "gaps",
180 "This syntax can only be used when sway is running");
181 }
182 }
183 return cmd_results_new(CMD_INVALID, "gaps",
184 "Expected 'gaps inner|outer <px>' or "
185 "'gaps inner|outer current|all set|plus|minus <px>'");
186}
diff --git a/sway/commands/input.c b/sway/commands/input.c
index 84888fbb..2889d47d 100644
--- a/sway/commands/input.c
+++ b/sway/commands/input.c
@@ -9,6 +9,7 @@
9static struct cmd_handler input_handlers[] = { 9static struct cmd_handler input_handlers[] = {
10 { "accel_profile", input_cmd_accel_profile }, 10 { "accel_profile", input_cmd_accel_profile },
11 { "click_method", input_cmd_click_method }, 11 { "click_method", input_cmd_click_method },
12 { "drag", input_cmd_drag },
12 { "drag_lock", input_cmd_drag_lock }, 13 { "drag_lock", input_cmd_drag_lock },
13 { "dwt", input_cmd_dwt }, 14 { "dwt", input_cmd_dwt },
14 { "events", input_cmd_events }, 15 { "events", input_cmd_events },
@@ -66,7 +67,15 @@ struct cmd_results *cmd_input(int argc, char **argv) {
66 input_handlers, sizeof(input_handlers)); 67 input_handlers, sizeof(input_handlers));
67 } 68 }
68 69
69 free_input_config(config->handler_context.input_config); 70 if (!res || res->status == CMD_SUCCESS) {
71 struct input_config *ic =
72 store_input_config(config->handler_context.input_config);
73
74 input_manager_apply_input_config(input_manager, ic);
75 } else {
76 free_input_config(config->handler_context.input_config);
77 }
78
70 config->handler_context.input_config = NULL; 79 config->handler_context.input_config = NULL;
71 80
72 return res; 81 return res;
diff --git a/sway/commands/input/accel_profile.c b/sway/commands/input/accel_profile.c
index a4108ec3..f7016790 100644
--- a/sway/commands/input/accel_profile.c
+++ b/sway/commands/input/accel_profile.c
@@ -9,25 +9,20 @@ struct cmd_results *input_cmd_accel_profile(int argc, char **argv) {
9 if ((error = checkarg(argc, "accel_profile", EXPECTED_AT_LEAST, 1))) { 9 if ((error = checkarg(argc, "accel_profile", EXPECTED_AT_LEAST, 1))) {
10 return error; 10 return error;
11 } 11 }
12 struct input_config *current_input_config = 12 struct input_config *ic = config->handler_context.input_config;
13 config->handler_context.input_config; 13 if (!ic) {
14 if (!current_input_config) {
15 return cmd_results_new(CMD_FAILURE, "accel_profile", 14 return cmd_results_new(CMD_FAILURE, "accel_profile",
16 "No input device defined."); 15 "No input device defined.");
17 } 16 }
18 struct input_config *new_config =
19 new_input_config(current_input_config->identifier);
20 17
21 if (strcasecmp(argv[0], "adaptive") == 0) { 18 if (strcasecmp(argv[0], "adaptive") == 0) {
22 new_config->accel_profile = LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE; 19 ic->accel_profile = LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE;
23 } else if (strcasecmp(argv[0], "flat") == 0) { 20 } else if (strcasecmp(argv[0], "flat") == 0) {
24 new_config->accel_profile = LIBINPUT_CONFIG_ACCEL_PROFILE_FLAT; 21 ic->accel_profile = LIBINPUT_CONFIG_ACCEL_PROFILE_FLAT;
25 } else { 22 } else {
26 free_input_config(new_config);
27 return cmd_results_new(CMD_INVALID, "accel_profile", 23 return cmd_results_new(CMD_INVALID, "accel_profile",
28 "Expected 'accel_profile <adaptive|flat>'"); 24 "Expected 'accel_profile <adaptive|flat>'");
29 } 25 }
30 26
31 apply_input_config(new_config);
32 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 27 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
33} 28}
diff --git a/sway/commands/input/click_method.c b/sway/commands/input/click_method.c
index 5d0d8cc2..4d7e1c93 100644
--- a/sway/commands/input/click_method.c
+++ b/sway/commands/input/click_method.c
@@ -10,27 +10,22 @@ struct cmd_results *input_cmd_click_method(int argc, char **argv) {
10 if ((error = checkarg(argc, "click_method", EXPECTED_AT_LEAST, 1))) { 10 if ((error = checkarg(argc, "click_method", EXPECTED_AT_LEAST, 1))) {
11 return error; 11 return error;
12 } 12 }
13 struct input_config *current_input_config = 13 struct input_config *ic = config->handler_context.input_config;
14 config->handler_context.input_config; 14 if (!ic) {
15 if (!current_input_config) {
16 return cmd_results_new(CMD_FAILURE, "click_method", 15 return cmd_results_new(CMD_FAILURE, "click_method",
17 "No input device defined."); 16 "No input device defined.");
18 } 17 }
19 struct input_config *new_config =
20 new_input_config(current_input_config->identifier);
21 18
22 if (strcasecmp(argv[0], "none") == 0) { 19 if (strcasecmp(argv[0], "none") == 0) {
23 new_config->click_method = LIBINPUT_CONFIG_CLICK_METHOD_NONE; 20 ic->click_method = LIBINPUT_CONFIG_CLICK_METHOD_NONE;
24 } else if (strcasecmp(argv[0], "button_areas") == 0) { 21 } else if (strcasecmp(argv[0], "button_areas") == 0) {
25 new_config->click_method = LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS; 22 ic->click_method = LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS;
26 } else if (strcasecmp(argv[0], "clickfinger") == 0) { 23 } else if (strcasecmp(argv[0], "clickfinger") == 0) {
27 new_config->click_method = LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER; 24 ic->click_method = LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER;
28 } else { 25 } else {
29 free_input_config(new_config);
30 return cmd_results_new(CMD_INVALID, "click_method", 26 return cmd_results_new(CMD_INVALID, "click_method",
31 "Expected 'click_method <none|button_areas|clickfinger'"); 27 "Expected 'click_method <none|button_areas|clickfinger'");
32 } 28 }
33 29
34 apply_input_config(new_config);
35 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 30 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
36} 31}
diff --git a/sway/commands/input/drag.c b/sway/commands/input/drag.c
new file mode 100644
index 00000000..e325df29
--- /dev/null
+++ b/sway/commands/input/drag.c
@@ -0,0 +1,26 @@
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_drag(int argc, char **argv) {
9 struct cmd_results *error = NULL;
10 if ((error = checkarg(argc, "drag", 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,
16 "drag", "No input device defined.");
17 }
18
19 if (parse_boolean(argv[0], true)) {
20 ic->drag = LIBINPUT_CONFIG_DRAG_ENABLED;
21 } else {
22 ic->drag = LIBINPUT_CONFIG_DRAG_DISABLED;
23 }
24
25 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
26}
diff --git a/sway/commands/input/drag_lock.c b/sway/commands/input/drag_lock.c
index f9ddeef2..db5d5afa 100644
--- a/sway/commands/input/drag_lock.c
+++ b/sway/commands/input/drag_lock.c
@@ -10,21 +10,17 @@ struct cmd_results *input_cmd_drag_lock(int argc, char **argv) {
10 if ((error = checkarg(argc, "drag_lock", EXPECTED_AT_LEAST, 1))) { 10 if ((error = checkarg(argc, "drag_lock", EXPECTED_AT_LEAST, 1))) {
11 return error; 11 return error;
12 } 12 }
13 struct input_config *current_input_config = 13 struct input_config *ic = config->handler_context.input_config;
14 config->handler_context.input_config; 14 if (!ic) {
15 if (!current_input_config) {
16 return cmd_results_new(CMD_FAILURE, 15 return cmd_results_new(CMD_FAILURE,
17 "drag_lock", "No input device defined."); 16 "drag_lock", "No input device defined.");
18 } 17 }
19 struct input_config *new_config =
20 new_input_config(current_input_config->identifier);
21 18
22 if (parse_boolean(argv[0], true)) { 19 if (parse_boolean(argv[0], true)) {
23 new_config->drag_lock = LIBINPUT_CONFIG_DRAG_LOCK_ENABLED; 20 ic->drag_lock = LIBINPUT_CONFIG_DRAG_LOCK_ENABLED;
24 } else { 21 } else {
25 new_config->drag_lock = LIBINPUT_CONFIG_DRAG_LOCK_DISABLED; 22 ic->drag_lock = LIBINPUT_CONFIG_DRAG_LOCK_DISABLED;
26 } 23 }
27 24
28 apply_input_config(new_config);
29 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 25 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
30} 26}
diff --git a/sway/commands/input/dwt.c b/sway/commands/input/dwt.c
index 15134268..0c3881dd 100644
--- a/sway/commands/input/dwt.c
+++ b/sway/commands/input/dwt.c
@@ -10,20 +10,16 @@ struct cmd_results *input_cmd_dwt(int argc, char **argv) {
10 if ((error = checkarg(argc, "dwt", EXPECTED_AT_LEAST, 1))) { 10 if ((error = checkarg(argc, "dwt", EXPECTED_AT_LEAST, 1))) {
11 return error; 11 return error;
12 } 12 }
13 struct input_config *current_input_config = 13 struct input_config *ic = config->handler_context.input_config;
14 config->handler_context.input_config; 14 if (!ic) {
15 if (!current_input_config) {
16 return cmd_results_new(CMD_FAILURE, "dwt", "No input device defined."); 15 return cmd_results_new(CMD_FAILURE, "dwt", "No input device defined.");
17 } 16 }
18 struct input_config *new_config =
19 new_input_config(current_input_config->identifier);
20 17
21 if (parse_boolean(argv[0], true)) { 18 if (parse_boolean(argv[0], true)) {
22 new_config->dwt = LIBINPUT_CONFIG_DWT_ENABLED; 19 ic->dwt = LIBINPUT_CONFIG_DWT_ENABLED;
23 } else { 20 } else {
24 new_config->dwt = LIBINPUT_CONFIG_DWT_DISABLED; 21 ic->dwt = LIBINPUT_CONFIG_DWT_DISABLED;
25 } 22 }
26 23
27 apply_input_config(new_config);
28 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 24 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
29} 25}
diff --git a/sway/commands/input/events.c b/sway/commands/input/events.c
index abfe3b12..e7ed69c6 100644
--- a/sway/commands/input/events.c
+++ b/sway/commands/input/events.c
@@ -10,30 +10,23 @@ struct cmd_results *input_cmd_events(int argc, char **argv) {
10 if ((error = checkarg(argc, "events", EXPECTED_AT_LEAST, 1))) { 10 if ((error = checkarg(argc, "events", EXPECTED_AT_LEAST, 1))) {
11 return error; 11 return error;
12 } 12 }
13 struct input_config *current_input_config = 13 struct input_config *ic = config->handler_context.input_config;
14 config->handler_context.input_config; 14 if (!ic) {
15 if (!current_input_config) {
16 return cmd_results_new(CMD_FAILURE, "events", 15 return cmd_results_new(CMD_FAILURE, "events",
17 "No input device defined."); 16 "No input device defined.");
18 } 17 }
19 wlr_log(WLR_DEBUG, "events for device: %s",
20 current_input_config->identifier);
21 struct input_config *new_config =
22 new_input_config(current_input_config->identifier);
23 18
24 if (strcasecmp(argv[0], "enabled") == 0) { 19 if (strcasecmp(argv[0], "enabled") == 0) {
25 new_config->send_events = LIBINPUT_CONFIG_SEND_EVENTS_ENABLED; 20 ic->send_events = LIBINPUT_CONFIG_SEND_EVENTS_ENABLED;
26 } else if (strcasecmp(argv[0], "disabled") == 0) { 21 } else if (strcasecmp(argv[0], "disabled") == 0) {
27 new_config->send_events = LIBINPUT_CONFIG_SEND_EVENTS_DISABLED; 22 ic->send_events = LIBINPUT_CONFIG_SEND_EVENTS_DISABLED;
28 } else if (strcasecmp(argv[0], "disabled_on_external_mouse") == 0) { 23 } else if (strcasecmp(argv[0], "disabled_on_external_mouse") == 0) {
29 new_config->send_events = 24 ic->send_events =
30 LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE; 25 LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE;
31 } else { 26 } else {
32 free_input_config(new_config);
33 return cmd_results_new(CMD_INVALID, "events", 27 return cmd_results_new(CMD_INVALID, "events",
34 "Expected 'events <enabled|disabled|disabled_on_external_mouse>'"); 28 "Expected 'events <enabled|disabled|disabled_on_external_mouse>'");
35 } 29 }
36 30
37 apply_input_config(new_config);
38 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 31 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
39} 32}
diff --git a/sway/commands/input/left_handed.c b/sway/commands/input/left_handed.c
index e770043a..2e0f757b 100644
--- a/sway/commands/input/left_handed.c
+++ b/sway/commands/input/left_handed.c
@@ -10,17 +10,13 @@ struct cmd_results *input_cmd_left_handed(int argc, char **argv) {
10 if ((error = checkarg(argc, "left_handed", EXPECTED_AT_LEAST, 1))) { 10 if ((error = checkarg(argc, "left_handed", EXPECTED_AT_LEAST, 1))) {
11 return error; 11 return error;
12 } 12 }
13 struct input_config *current_input_config = 13 struct input_config *ic = config->handler_context.input_config;
14 config->handler_context.input_config; 14 if (!ic) {
15 if (!current_input_config) {
16 return cmd_results_new(CMD_FAILURE, "left_handed", 15 return cmd_results_new(CMD_FAILURE, "left_handed",
17 "No input device defined."); 16 "No input device defined.");
18 } 17 }
19 struct input_config *new_config =
20 new_input_config(current_input_config->identifier);
21 18
22 new_config->left_handed = parse_boolean(argv[0], true); 19 ic->left_handed = parse_boolean(argv[0], true);
23 20
24 apply_input_config(new_config);
25 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 21 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
26} 22}
diff --git a/sway/commands/input/map_from_region.c b/sway/commands/input/map_from_region.c
index 40f04214..53608a67 100644
--- a/sway/commands/input/map_from_region.c
+++ b/sway/commands/input/map_from_region.c
@@ -38,50 +38,44 @@ struct cmd_results *input_cmd_map_from_region(int argc, char **argv) {
38 if ((error = checkarg(argc, "map_from_region", EXPECTED_EQUAL_TO, 2))) { 38 if ((error = checkarg(argc, "map_from_region", EXPECTED_EQUAL_TO, 2))) {
39 return error; 39 return error;
40 } 40 }
41 struct input_config *current_input_config = 41 struct input_config *ic = config->handler_context.input_config;
42 config->handler_context.input_config; 42 if (!ic) {
43 if (!current_input_config) {
44 return cmd_results_new(CMD_FAILURE, "map_from_region", 43 return cmd_results_new(CMD_FAILURE, "map_from_region",
45 "No input device defined"); 44 "No input device defined");
46 } 45 }
47 46
48 struct input_config *new_config = 47 ic->mapped_from_region =
49 new_input_config(current_input_config->identifier);
50
51 new_config->mapped_from_region =
52 calloc(1, sizeof(struct input_config_mapped_from_region)); 48 calloc(1, sizeof(struct input_config_mapped_from_region));
53 49
54 bool mm1, mm2; 50 bool mm1, mm2;
55 if (!parse_coords(argv[0], &new_config->mapped_from_region->x1, 51 if (!parse_coords(argv[0], &ic->mapped_from_region->x1,
56 &new_config->mapped_from_region->y1, &mm1)) { 52 &ic->mapped_from_region->y1, &mm1)) {
57 free(new_config->mapped_from_region); 53 free(ic->mapped_from_region);
58 free_input_config(new_config); 54 ic->mapped_from_region = NULL;
59 return cmd_results_new(CMD_FAILURE, "map_from_region", 55 return cmd_results_new(CMD_FAILURE, "map_from_region",
60 "Invalid top-left coordinates"); 56 "Invalid top-left coordinates");
61 } 57 }
62 if (!parse_coords(argv[1], &new_config->mapped_from_region->x2, 58 if (!parse_coords(argv[1], &ic->mapped_from_region->x2,
63 &new_config->mapped_from_region->y2, &mm2)) { 59 &ic->mapped_from_region->y2, &mm2)) {
64 free(new_config->mapped_from_region); 60 free(ic->mapped_from_region);
65 free_input_config(new_config); 61 ic->mapped_from_region = NULL;
66 return cmd_results_new(CMD_FAILURE, "map_from_region", 62 return cmd_results_new(CMD_FAILURE, "map_from_region",
67 "Invalid bottom-right coordinates"); 63 "Invalid bottom-right coordinates");
68 } 64 }
69 if (new_config->mapped_from_region->x1 > new_config->mapped_from_region->x2 || 65 if (ic->mapped_from_region->x1 > ic->mapped_from_region->x2 ||
70 new_config->mapped_from_region->y1 > new_config->mapped_from_region->y2) { 66 ic->mapped_from_region->y1 > ic->mapped_from_region->y2) {
71 free(new_config->mapped_from_region); 67 free(ic->mapped_from_region);
72 free_input_config(new_config); 68 ic->mapped_from_region = NULL;
73 return cmd_results_new(CMD_FAILURE, "map_from_region", 69 return cmd_results_new(CMD_FAILURE, "map_from_region",
74 "Invalid rectangle"); 70 "Invalid rectangle");
75 } 71 }
76 if (mm1 != mm2) { 72 if (mm1 != mm2) {
77 free(new_config->mapped_from_region); 73 free(ic->mapped_from_region);
78 free_input_config(new_config); 74 ic->mapped_from_region = NULL;
79 return cmd_results_new(CMD_FAILURE, "map_from_region", 75 return cmd_results_new(CMD_FAILURE, "map_from_region",
80 "Both coordinates must be in the same unit"); 76 "Both coordinates must be in the same unit");
81 } 77 }
82 new_config->mapped_from_region->mm = mm1; 78 ic->mapped_from_region->mm = mm1;
83
84 apply_input_config(new_config);
85 79
86 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 80 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
87} 81}
diff --git a/sway/commands/input/map_to_output.c b/sway/commands/input/map_to_output.c
index 68439bec..8b16c557 100644
--- a/sway/commands/input/map_to_output.c
+++ b/sway/commands/input/map_to_output.c
@@ -11,17 +11,13 @@ struct cmd_results *input_cmd_map_to_output(int argc, char **argv) {
11 if ((error = checkarg(argc, "map_to_output", EXPECTED_EQUAL_TO, 1))) { 11 if ((error = checkarg(argc, "map_to_output", EXPECTED_EQUAL_TO, 1))) {
12 return error; 12 return error;
13 } 13 }
14 struct input_config *current_input_config = 14 struct input_config *ic = config->handler_context.input_config;
15 config->handler_context.input_config; 15 if (!ic) {
16 if (!current_input_config) {
17 return cmd_results_new(CMD_FAILURE, "map_to_output", 16 return cmd_results_new(CMD_FAILURE, "map_to_output",
18 "No input device defined."); 17 "No input device defined.");
19 } 18 }
20 struct input_config *new_config =
21 new_input_config(current_input_config->identifier);
22 19
23 new_config->mapped_to_output = strdup(argv[0]); 20 ic->mapped_to_output = strdup(argv[0]);
24 apply_input_config(new_config);
25 21
26 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 22 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
27} 23}
diff --git a/sway/commands/input/middle_emulation.c b/sway/commands/input/middle_emulation.c
index 414d4d2b..80d26838 100644
--- a/sway/commands/input/middle_emulation.c
+++ b/sway/commands/input/middle_emulation.c
@@ -10,22 +10,17 @@ struct cmd_results *input_cmd_middle_emulation(int argc, char **argv) {
10 if ((error = checkarg(argc, "middle_emulation", EXPECTED_AT_LEAST, 1))) { 10 if ((error = checkarg(argc, "middle_emulation", EXPECTED_AT_LEAST, 1))) {
11 return error; 11 return error;
12 } 12 }
13 struct input_config *current_input_config = 13 struct input_config *ic = config->handler_context.input_config;
14 config->handler_context.input_config; 14 if (!ic) {
15 if (!current_input_config) {
16 return cmd_results_new(CMD_FAILURE, "middle_emulation", 15 return cmd_results_new(CMD_FAILURE, "middle_emulation",
17 "No input device defined."); 16 "No input device defined.");
18 } 17 }
19 struct input_config *new_config =
20 new_input_config(current_input_config->identifier);
21 18
22 if (parse_boolean(argv[0], true)) { 19 if (parse_boolean(argv[0], true)) {
23 new_config->middle_emulation = LIBINPUT_CONFIG_MIDDLE_EMULATION_ENABLED; 20 ic->middle_emulation = LIBINPUT_CONFIG_MIDDLE_EMULATION_ENABLED;
24 } else { 21 } else {
25 new_config->middle_emulation = 22 ic->middle_emulation = LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED;
26 LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED;
27 } 23 }
28 24
29 apply_input_config(new_config);
30 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 25 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
31} 26}
diff --git a/sway/commands/input/natural_scroll.c b/sway/commands/input/natural_scroll.c
index 77c3ff00..e2a93500 100644
--- a/sway/commands/input/natural_scroll.c
+++ b/sway/commands/input/natural_scroll.c
@@ -10,17 +10,13 @@ struct cmd_results *input_cmd_natural_scroll(int argc, char **argv) {
10 if ((error = checkarg(argc, "natural_scroll", EXPECTED_AT_LEAST, 1))) { 10 if ((error = checkarg(argc, "natural_scroll", EXPECTED_AT_LEAST, 1))) {
11 return error; 11 return error;
12 } 12 }
13 struct input_config *current_input_config = 13 struct input_config *ic = config->handler_context.input_config;
14 config->handler_context.input_config; 14 if (!ic) {
15 if (!current_input_config) {
16 return cmd_results_new(CMD_FAILURE, "natural_scoll", 15 return cmd_results_new(CMD_FAILURE, "natural_scoll",
17 "No input device defined."); 16 "No input device defined.");
18 } 17 }
19 struct input_config *new_config =
20 new_input_config(current_input_config->identifier);
21 18
22 new_config->natural_scroll = parse_boolean(argv[0], true); 19 ic->natural_scroll = parse_boolean(argv[0], true);
23 20
24 apply_input_config(new_config);
25 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 21 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
26} 22}
diff --git a/sway/commands/input/pointer_accel.c b/sway/commands/input/pointer_accel.c
index 8bbd0724..df487b1c 100644
--- a/sway/commands/input/pointer_accel.c
+++ b/sway/commands/input/pointer_accel.c
@@ -9,23 +9,18 @@ struct cmd_results *input_cmd_pointer_accel(int argc, char **argv) {
9 if ((error = checkarg(argc, "pointer_accel", EXPECTED_AT_LEAST, 1))) { 9 if ((error = checkarg(argc, "pointer_accel", EXPECTED_AT_LEAST, 1))) {
10 return error; 10 return error;
11 } 11 }
12 struct input_config *current_input_config = 12 struct input_config *ic = config->handler_context.input_config;
13 config->handler_context.input_config; 13 if (!ic) {
14 if (!current_input_config) {
15 return cmd_results_new(CMD_FAILURE, 14 return cmd_results_new(CMD_FAILURE,
16 "pointer_accel", "No input device defined."); 15 "pointer_accel", "No input device defined.");
17 } 16 }
18 struct input_config *new_config =
19 new_input_config(current_input_config->identifier);
20 17
21 float pointer_accel = atof(argv[0]); 18 float pointer_accel = atof(argv[0]);
22 if (pointer_accel < -1 || pointer_accel > 1) { 19 if (pointer_accel < -1 || pointer_accel > 1) {
23 free_input_config(new_config);
24 return cmd_results_new(CMD_INVALID, "pointer_accel", 20 return cmd_results_new(CMD_INVALID, "pointer_accel",
25 "Input out of range [-1, 1]"); 21 "Input out of range [-1, 1]");
26 } 22 }
27 new_config->pointer_accel = pointer_accel; 23 ic->pointer_accel = pointer_accel;
28 24
29 apply_input_config(new_config);
30 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 25 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
31} 26}
diff --git a/sway/commands/input/repeat_delay.c b/sway/commands/input/repeat_delay.c
index c9ddbf0e..d94b3e4d 100644
--- a/sway/commands/input/repeat_delay.c
+++ b/sway/commands/input/repeat_delay.c
@@ -9,23 +9,18 @@ struct cmd_results *input_cmd_repeat_delay(int argc, char **argv) {
9 if ((error = checkarg(argc, "repeat_delay", EXPECTED_EQUAL_TO, 1))) { 9 if ((error = checkarg(argc, "repeat_delay", EXPECTED_EQUAL_TO, 1))) {
10 return error; 10 return error;
11 } 11 }
12 struct input_config *current_input_config = 12 struct input_config *ic = config->handler_context.input_config;
13 config->handler_context.input_config; 13 if (!ic) {
14 if (!current_input_config) {
15 return cmd_results_new(CMD_FAILURE, 14 return cmd_results_new(CMD_FAILURE,
16 "repeat_delay", "No input device defined."); 15 "repeat_delay", "No input device defined.");
17 } 16 }
18 struct input_config *new_config =
19 new_input_config(current_input_config->identifier);
20 17
21 int repeat_delay = atoi(argv[0]); 18 int repeat_delay = atoi(argv[0]);
22 if (repeat_delay < 0) { 19 if (repeat_delay < 0) {
23 free_input_config(new_config);
24 return cmd_results_new(CMD_INVALID, "repeat_delay", 20 return cmd_results_new(CMD_INVALID, "repeat_delay",
25 "Repeat delay cannot be negative"); 21 "Repeat delay cannot be negative");
26 } 22 }
27 new_config->repeat_delay = repeat_delay; 23 ic->repeat_delay = repeat_delay;
28 24
29 apply_input_config(new_config);
30 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 25 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
31} 26}
diff --git a/sway/commands/input/repeat_rate.c b/sway/commands/input/repeat_rate.c
index 56878176..ebec4cdb 100644
--- a/sway/commands/input/repeat_rate.c
+++ b/sway/commands/input/repeat_rate.c
@@ -9,23 +9,18 @@ struct cmd_results *input_cmd_repeat_rate(int argc, char **argv) {
9 if ((error = checkarg(argc, "repeat_rate", EXPECTED_EQUAL_TO, 1))) { 9 if ((error = checkarg(argc, "repeat_rate", EXPECTED_EQUAL_TO, 1))) {
10 return error; 10 return error;
11 } 11 }
12 struct input_config *current_input_config = 12 struct input_config *ic = config->handler_context.input_config;
13 config->handler_context.input_config; 13 if (!ic) {
14 if (!current_input_config) {
15 return cmd_results_new(CMD_FAILURE, 14 return cmd_results_new(CMD_FAILURE,
16 "repeat_rate", "No input device defined."); 15 "repeat_rate", "No input device defined.");
17 } 16 }
18 struct input_config *new_config =
19 new_input_config(current_input_config->identifier);
20 17
21 int repeat_rate = atoi(argv[0]); 18 int repeat_rate = atoi(argv[0]);
22 if (repeat_rate < 0) { 19 if (repeat_rate < 0) {
23 free_input_config(new_config);
24 return cmd_results_new(CMD_INVALID, "repeat_rate", 20 return cmd_results_new(CMD_INVALID, "repeat_rate",
25 "Repeat rate cannot be negative"); 21 "Repeat rate cannot be negative");
26 } 22 }
27 new_config->repeat_rate = repeat_rate; 23 ic->repeat_rate = repeat_rate;
28 24
29 apply_input_config(new_config);
30 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 25 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
31} 26}
diff --git a/sway/commands/input/scroll_button.c b/sway/commands/input/scroll_button.c
index 350fcca2..1958f23c 100644
--- a/sway/commands/input/scroll_button.c
+++ b/sway/commands/input/scroll_button.c
@@ -10,35 +10,28 @@ struct cmd_results *input_cmd_scroll_button(int argc, char **argv) {
10 if ((error = checkarg(argc, "scroll_button", EXPECTED_AT_LEAST, 1))) { 10 if ((error = checkarg(argc, "scroll_button", EXPECTED_AT_LEAST, 1))) {
11 return error; 11 return error;
12 } 12 }
13 struct input_config *current_input_config = 13 struct input_config *ic = config->handler_context.input_config;
14 config->handler_context.input_config; 14 if (!ic) {
15 if (!current_input_config) {
16 return cmd_results_new(CMD_FAILURE, "scroll_button", 15 return cmd_results_new(CMD_FAILURE, "scroll_button",
17 "No input device defined."); 16 "No input device defined.");
18 } 17 }
19 struct input_config *new_config =
20 new_input_config(current_input_config->identifier);
21 18
22 errno = 0; 19 errno = 0;
23 char *endptr; 20 char *endptr;
24 int scroll_button = strtol(*argv, &endptr, 10); 21 int scroll_button = strtol(*argv, &endptr, 10);
25 if (endptr == *argv && scroll_button == 0) { 22 if (endptr == *argv && scroll_button == 0) {
26 free_input_config(new_config);
27 return cmd_results_new(CMD_INVALID, "scroll_button", 23 return cmd_results_new(CMD_INVALID, "scroll_button",
28 "Scroll button identifier must be an integer."); 24 "Scroll button identifier must be an integer.");
29 } 25 }
30 if (errno == ERANGE) { 26 if (errno == ERANGE) {
31 free_input_config(new_config);
32 return cmd_results_new(CMD_INVALID, "scroll_button", 27 return cmd_results_new(CMD_INVALID, "scroll_button",
33 "Scroll button identifier out of range."); 28 "Scroll button identifier out of range.");
34 } 29 }
35 if (scroll_button < 0) { 30 if (scroll_button < 0) {
36 free_input_config(new_config);
37 return cmd_results_new(CMD_INVALID, "scroll_button", 31 return cmd_results_new(CMD_INVALID, "scroll_button",
38 "Scroll button identifier cannot be negative."); 32 "Scroll button identifier cannot be negative.");
39 } 33 }
40 new_config->scroll_button = scroll_button; 34 ic->scroll_button = scroll_button;
41 35
42 apply_input_config(new_config);
43 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 36 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
44} 37}
diff --git a/sway/commands/input/scroll_method.c b/sway/commands/input/scroll_method.c
index 4c6ac6b6..c116b052 100644
--- a/sway/commands/input/scroll_method.c
+++ b/sway/commands/input/scroll_method.c
@@ -9,29 +9,24 @@ struct cmd_results *input_cmd_scroll_method(int argc, char **argv) {
9 if ((error = checkarg(argc, "scroll_method", EXPECTED_AT_LEAST, 1))) { 9 if ((error = checkarg(argc, "scroll_method", EXPECTED_AT_LEAST, 1))) {
10 return error; 10 return error;
11 } 11 }
12 struct input_config *current_input_config = 12 struct input_config *ic = config->handler_context.input_config;
13 config->handler_context.input_config; 13 if (!ic) {
14 if (!current_input_config) {
15 return cmd_results_new(CMD_FAILURE, "scroll_method", 14 return cmd_results_new(CMD_FAILURE, "scroll_method",
16 "No input device defined."); 15 "No input device defined.");
17 } 16 }
18 struct input_config *new_config =
19 new_input_config(current_input_config->identifier);
20 17
21 if (strcasecmp(argv[0], "none") == 0) { 18 if (strcasecmp(argv[0], "none") == 0) {
22 new_config->scroll_method = LIBINPUT_CONFIG_SCROLL_NO_SCROLL; 19 ic->scroll_method = LIBINPUT_CONFIG_SCROLL_NO_SCROLL;
23 } else if (strcasecmp(argv[0], "two_finger") == 0) { 20 } else if (strcasecmp(argv[0], "two_finger") == 0) {
24 new_config->scroll_method = LIBINPUT_CONFIG_SCROLL_2FG; 21 ic->scroll_method = LIBINPUT_CONFIG_SCROLL_2FG;
25 } else if (strcasecmp(argv[0], "edge") == 0) { 22 } else if (strcasecmp(argv[0], "edge") == 0) {
26 new_config->scroll_method = LIBINPUT_CONFIG_SCROLL_EDGE; 23 ic->scroll_method = LIBINPUT_CONFIG_SCROLL_EDGE;
27 } else if (strcasecmp(argv[0], "on_button_down") == 0) { 24 } else if (strcasecmp(argv[0], "on_button_down") == 0) {
28 new_config->scroll_method = LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN; 25 ic->scroll_method = LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN;
29 } else { 26 } else {
30 free_input_config(new_config);
31 return cmd_results_new(CMD_INVALID, "scroll_method", 27 return cmd_results_new(CMD_INVALID, "scroll_method",
32 "Expected 'scroll_method <none|two_finger|edge|on_button_down>'"); 28 "Expected 'scroll_method <none|two_finger|edge|on_button_down>'");
33 } 29 }
34 30
35 apply_input_config(new_config);
36 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 31 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
37} 32}
diff --git a/sway/commands/input/tap.c b/sway/commands/input/tap.c
index ac3b8237..c455b696 100644
--- a/sway/commands/input/tap.c
+++ b/sway/commands/input/tap.c
@@ -11,22 +11,16 @@ struct cmd_results *input_cmd_tap(int argc, char **argv) {
11 if ((error = checkarg(argc, "tap", EXPECTED_AT_LEAST, 1))) { 11 if ((error = checkarg(argc, "tap", EXPECTED_AT_LEAST, 1))) {
12 return error; 12 return error;
13 } 13 }
14 struct input_config *current_input_config = 14 struct input_config *ic = config->handler_context.input_config;
15 config->handler_context.input_config; 15 if (!ic) {
16 if (!current_input_config) {
17 return cmd_results_new(CMD_FAILURE, "tap", "No input device defined."); 16 return cmd_results_new(CMD_FAILURE, "tap", "No input device defined.");
18 } 17 }
19 struct input_config *new_config =
20 new_input_config(current_input_config->identifier);
21 18
22 if (parse_boolean(argv[0], true)) { 19 if (parse_boolean(argv[0], true)) {
23 new_config->tap = LIBINPUT_CONFIG_TAP_ENABLED; 20 ic->tap = LIBINPUT_CONFIG_TAP_ENABLED;
24 } else { 21 } else {
25 new_config->tap = LIBINPUT_CONFIG_TAP_DISABLED; 22 ic->tap = LIBINPUT_CONFIG_TAP_DISABLED;
26 } 23 }
27 24
28 wlr_log(WLR_DEBUG, "apply-tap for device: %s",
29 current_input_config->identifier);
30 apply_input_config(new_config);
31 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 25 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
32} 26}
diff --git a/sway/commands/input/tap_button_map.c b/sway/commands/input/tap_button_map.c
index bdbba472..dff2985b 100644
--- a/sway/commands/input/tap_button_map.c
+++ b/sway/commands/input/tap_button_map.c
@@ -9,25 +9,20 @@ struct cmd_results *input_cmd_tap_button_map(int argc, char **argv) {
9 if ((error = checkarg(argc, "tap_button_map", EXPECTED_AT_LEAST, 1))) { 9 if ((error = checkarg(argc, "tap_button_map", EXPECTED_AT_LEAST, 1))) {
10 return error; 10 return error;
11 } 11 }
12 struct input_config *current_input_config = 12 struct input_config *ic = config->handler_context.input_config;
13 config->handler_context.input_config; 13 if (!ic) {
14 if (!current_input_config) {
15 return cmd_results_new(CMD_FAILURE, "tap_button_map", 14 return cmd_results_new(CMD_FAILURE, "tap_button_map",
16 "No input device defined."); 15 "No input device defined.");
17 } 16 }
18 struct input_config *new_config =
19 new_input_config(current_input_config->identifier);
20 17
21 if (strcasecmp(argv[0], "lrm") == 0) { 18 if (strcasecmp(argv[0], "lrm") == 0) {
22 new_config->tap_button_map = LIBINPUT_CONFIG_TAP_MAP_LRM; 19 ic->tap_button_map = LIBINPUT_CONFIG_TAP_MAP_LRM;
23 } else if (strcasecmp(argv[0], "lmr") == 0) { 20 } else if (strcasecmp(argv[0], "lmr") == 0) {
24 new_config->tap_button_map = LIBINPUT_CONFIG_TAP_MAP_LMR; 21 ic->tap_button_map = LIBINPUT_CONFIG_TAP_MAP_LMR;
25 } else { 22 } else {
26 free_input_config(new_config);
27 return cmd_results_new(CMD_INVALID, "tap_button_map", 23 return cmd_results_new(CMD_INVALID, "tap_button_map",
28 "Expected 'tap_button_map <lrm|lmr>'"); 24 "Expected 'tap_button_map <lrm|lmr>'");
29 } 25 }
30 26
31 apply_input_config(new_config);
32 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 27 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
33} 28}
diff --git a/sway/commands/input/xkb_capslock.c b/sway/commands/input/xkb_capslock.c
index 5442c463..669b4ea9 100644
--- a/sway/commands/input/xkb_capslock.c
+++ b/sway/commands/input/xkb_capslock.c
@@ -9,25 +9,20 @@ struct cmd_results *input_cmd_xkb_capslock(int argc, char **argv) {
9 if ((error = checkarg(argc, "xkb_capslock", EXPECTED_AT_LEAST, 1))) { 9 if ((error = checkarg(argc, "xkb_capslock", EXPECTED_AT_LEAST, 1))) {
10 return error; 10 return error;
11 } 11 }
12 struct input_config *current_input_config = 12 struct input_config *ic = config->handler_context.input_config;
13 config->handler_context.input_config; 13 if (!ic) {
14 if (!current_input_config) {
15 return cmd_results_new(CMD_FAILURE, "xkb_capslock", 14 return cmd_results_new(CMD_FAILURE, "xkb_capslock",
16 "No input device defined."); 15 "No input device defined.");
17 } 16 }
18 struct input_config *new_config =
19 new_input_config(current_input_config->identifier);
20 17
21 if (strcasecmp(argv[0], "enabled") == 0) { 18 if (strcasecmp(argv[0], "enabled") == 0) {
22 new_config->xkb_capslock = 1; 19 ic->xkb_capslock = 1;
23 } else if (strcasecmp(argv[0], "disabled") == 0) { 20 } else if (strcasecmp(argv[0], "disabled") == 0) {
24 new_config->xkb_capslock = 0; 21 ic->xkb_capslock = 0;
25 } else { 22 } else {
26 free_input_config(new_config);
27 return cmd_results_new(CMD_INVALID, "xkb_capslock", 23 return cmd_results_new(CMD_INVALID, "xkb_capslock",
28 "Expected 'xkb_capslock <enabled|disabled>'"); 24 "Expected 'xkb_capslock <enabled|disabled>'");
29 } 25 }
30 26
31 apply_input_config(new_config);
32 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 27 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
33} 28}
diff --git a/sway/commands/input/xkb_layout.c b/sway/commands/input/xkb_layout.c
index 9fa5a344..5fccd4a3 100644
--- a/sway/commands/input/xkb_layout.c
+++ b/sway/commands/input/xkb_layout.c
@@ -9,18 +9,15 @@ struct cmd_results *input_cmd_xkb_layout(int argc, char **argv) {
9 if ((error = checkarg(argc, "xkb_layout", EXPECTED_EQUAL_TO, 1))) { 9 if ((error = checkarg(argc, "xkb_layout", EXPECTED_EQUAL_TO, 1))) {
10 return error; 10 return error;
11 } 11 }
12 struct input_config *current_input_config = 12 struct input_config *ic = config->handler_context.input_config;
13 config->handler_context.input_config; 13 if (!ic) {
14 if (!current_input_config) { 14 return cmd_results_new(CMD_FAILURE, "xkb_layout",
15 return cmd_results_new(CMD_FAILURE, "xkb_layout", "No input device defined."); 15 "No input device defined.");
16 } 16 }
17 struct input_config *new_config =
18 new_input_config(current_input_config->identifier);
19 17
20 new_config->xkb_layout = strdup(argv[0]); 18 ic->xkb_layout = strdup(argv[0]);
21 19
22 wlr_log(WLR_DEBUG, "apply-xkb_layout for device: %s layout: %s", 20 wlr_log(WLR_DEBUG, "set-xkb_layout for config: %s layout: %s",
23 current_input_config->identifier, new_config->xkb_layout); 21 ic->identifier, ic->xkb_layout);
24 apply_input_config(new_config);
25 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 22 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
26} 23}
diff --git a/sway/commands/input/xkb_model.c b/sway/commands/input/xkb_model.c
index 0d082625..c4d04638 100644
--- a/sway/commands/input/xkb_model.c
+++ b/sway/commands/input/xkb_model.c
@@ -9,18 +9,15 @@ struct cmd_results *input_cmd_xkb_model(int argc, char **argv) {
9 if ((error = checkarg(argc, "xkb_model", EXPECTED_EQUAL_TO, 1))) { 9 if ((error = checkarg(argc, "xkb_model", EXPECTED_EQUAL_TO, 1))) {
10 return error; 10 return error;
11 } 11 }
12 struct input_config *current_input_config = 12 struct input_config *ic = config->handler_context.input_config;
13 config->handler_context.input_config; 13 if (!ic) {
14 if (!current_input_config) { 14 return cmd_results_new(CMD_FAILURE, "xkb_model",
15 return cmd_results_new(CMD_FAILURE, "xkb_model", "No input device defined."); 15 "No input device defined.");
16 } 16 }
17 struct input_config *new_config =
18 new_input_config(current_input_config->identifier);
19 17
20 new_config->xkb_model = strdup(argv[0]); 18 ic->xkb_model = strdup(argv[0]);
21 19
22 wlr_log(WLR_DEBUG, "apply-xkb_model for device: %s model: %s", 20 wlr_log(WLR_DEBUG, "set-xkb_model for config: %s model: %s",
23 current_input_config->identifier, new_config->xkb_model); 21 ic->identifier, ic->xkb_model);
24 apply_input_config(new_config);
25 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 22 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
26} 23}
diff --git a/sway/commands/input/xkb_numlock.c b/sway/commands/input/xkb_numlock.c
index 39675366..1367da44 100644
--- a/sway/commands/input/xkb_numlock.c
+++ b/sway/commands/input/xkb_numlock.c
@@ -9,25 +9,20 @@ struct cmd_results *input_cmd_xkb_numlock(int argc, char **argv) {
9 if ((error = checkarg(argc, "xkb_numlock", EXPECTED_AT_LEAST, 1))) { 9 if ((error = checkarg(argc, "xkb_numlock", EXPECTED_AT_LEAST, 1))) {
10 return error; 10 return error;
11 } 11 }
12 struct input_config *current_input_config = 12 struct input_config *ic = config->handler_context.input_config;
13 config->handler_context.input_config; 13 if (!ic) {
14 if (!current_input_config) {
15 return cmd_results_new(CMD_FAILURE, "xkb_numlock", 14 return cmd_results_new(CMD_FAILURE, "xkb_numlock",
16 "No input device defined."); 15 "No input device defined.");
17 } 16 }
18 struct input_config *new_config =
19 new_input_config(current_input_config->identifier);
20 17
21 if (strcasecmp(argv[0], "enabled") == 0) { 18 if (strcasecmp(argv[0], "enabled") == 0) {
22 new_config->xkb_numlock = 1; 19 ic->xkb_numlock = 1;
23 } else if (strcasecmp(argv[0], "disabled") == 0) { 20 } else if (strcasecmp(argv[0], "disabled") == 0) {
24 new_config->xkb_numlock = 0; 21 ic->xkb_numlock = 0;
25 } else { 22 } else {
26 free_input_config(new_config);
27 return cmd_results_new(CMD_INVALID, "xkb_numlock", 23 return cmd_results_new(CMD_INVALID, "xkb_numlock",
28 "Expected 'xkb_numlock <enabled|disabled>'"); 24 "Expected 'xkb_numlock <enabled|disabled>'");
29 } 25 }
30 26
31 apply_input_config(new_config);
32 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 27 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
33} 28}
diff --git a/sway/commands/input/xkb_options.c b/sway/commands/input/xkb_options.c
index 3059d941..794ab6e9 100644
--- a/sway/commands/input/xkb_options.c
+++ b/sway/commands/input/xkb_options.c
@@ -9,18 +9,15 @@ struct cmd_results *input_cmd_xkb_options(int argc, char **argv) {
9 if ((error = checkarg(argc, "xkb_options", EXPECTED_EQUAL_TO, 1))) { 9 if ((error = checkarg(argc, "xkb_options", EXPECTED_EQUAL_TO, 1))) {
10 return error; 10 return error;
11 } 11 }
12 struct input_config *current_input_config = 12 struct input_config *ic = config->handler_context.input_config;
13 config->handler_context.input_config; 13 if (!ic) {
14 if (!current_input_config) { 14 return cmd_results_new(CMD_FAILURE, "xkb_options",
15 return cmd_results_new(CMD_FAILURE, "xkb_options", "No input device defined."); 15 "No input device defined.");
16 } 16 }
17 struct input_config *new_config =
18 new_input_config(current_input_config->identifier);
19 17
20 new_config->xkb_options = strdup(argv[0]); 18 ic->xkb_options = strdup(argv[0]);
21 19
22 wlr_log(WLR_DEBUG, "apply-xkb_options for device: %s options: %s", 20 wlr_log(WLR_DEBUG, "set-xkb_options for config: %s options: %s",
23 current_input_config->identifier, new_config->xkb_options); 21 ic->identifier, ic->xkb_options);
24 apply_input_config(new_config);
25 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 22 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
26} 23}
diff --git a/sway/commands/input/xkb_rules.c b/sway/commands/input/xkb_rules.c
index 560f088e..257c3288 100644
--- a/sway/commands/input/xkb_rules.c
+++ b/sway/commands/input/xkb_rules.c
@@ -9,18 +9,15 @@ struct cmd_results *input_cmd_xkb_rules(int argc, char **argv) {
9 if ((error = checkarg(argc, "xkb_rules", EXPECTED_EQUAL_TO, 1))) { 9 if ((error = checkarg(argc, "xkb_rules", EXPECTED_EQUAL_TO, 1))) {
10 return error; 10 return error;
11 } 11 }
12 struct input_config *current_input_config = 12 struct input_config *ic = config->handler_context.input_config;
13 config->handler_context.input_config; 13 if (!ic) {
14 if (!current_input_config) { 14 return cmd_results_new(CMD_FAILURE, "xkb_rules",
15 return cmd_results_new(CMD_FAILURE, "xkb_rules", "No input device defined."); 15 "No input device defined.");
16 } 16 }
17 struct input_config *new_config =
18 new_input_config(current_input_config->identifier);
19 17
20 new_config->xkb_rules = strdup(argv[0]); 18 ic->xkb_rules = strdup(argv[0]);
21 19
22 wlr_log(WLR_DEBUG, "apply-xkb_rules for device: %s rules: %s", 20 wlr_log(WLR_DEBUG, "set-xkb_rules for config: %s rules: %s",
23 current_input_config->identifier, new_config->xkb_rules); 21 ic->identifier, ic->xkb_rules);
24 apply_input_config(new_config);
25 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 22 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
26} 23}
diff --git a/sway/commands/input/xkb_variant.c b/sway/commands/input/xkb_variant.c
index 0aa03440..3832dc8e 100644
--- a/sway/commands/input/xkb_variant.c
+++ b/sway/commands/input/xkb_variant.c
@@ -9,18 +9,15 @@ struct cmd_results *input_cmd_xkb_variant(int argc, char **argv) {
9 if ((error = checkarg(argc, "xkb_variant", EXPECTED_EQUAL_TO, 1))) { 9 if ((error = checkarg(argc, "xkb_variant", EXPECTED_EQUAL_TO, 1))) {
10 return error; 10 return error;
11 } 11 }
12 struct input_config *current_input_config = 12 struct input_config *ic = config->handler_context.input_config;
13 config->handler_context.input_config; 13 if (!ic) {
14 if (!current_input_config) { 14 return cmd_results_new(CMD_FAILURE, "xkb_variant",
15 return cmd_results_new(CMD_FAILURE, "xkb_variant", "No input device defined."); 15 "No input device defined.");
16 } 16 }
17 struct input_config *new_config =
18 new_input_config(current_input_config->identifier);
19 17
20 new_config->xkb_variant = strdup(argv[0]); 18 ic->xkb_variant = strdup(argv[0]);
21 19
22 wlr_log(WLR_DEBUG, "apply-xkb_variant for device: %s variant: %s", 20 wlr_log(WLR_DEBUG, "set-xkb_variant for config: %s variant: %s",
23 current_input_config->identifier, new_config->xkb_variant); 21 ic->identifier, ic->xkb_variant);
24 apply_input_config(new_config);
25 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 22 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
26} 23}
diff --git a/sway/commands/layout.c b/sway/commands/layout.c
index ef3ec1cb..c2ce2e78 100644
--- a/sway/commands/layout.c
+++ b/sway/commands/layout.c
@@ -138,15 +138,14 @@ struct cmd_results *cmd_layout(int argc, char **argv) {
138 } 138 }
139 container->layout = new_layout; 139 container->layout = new_layout;
140 container_update_representation(container); 140 container_update_representation(container);
141 arrange_container(container);
142 } else { 141 } else {
143 if (old_layout != L_TABBED && old_layout != L_STACKED) { 142 if (old_layout != L_TABBED && old_layout != L_STACKED) {
144 workspace->prev_split_layout = old_layout; 143 workspace->prev_split_layout = old_layout;
145 } 144 }
146 workspace->layout = new_layout; 145 workspace->layout = new_layout;
147 workspace_update_representation(workspace); 146 workspace_update_representation(workspace);
148 arrange_workspace(workspace);
149 } 147 }
148 arrange_workspace(workspace);
150 } 149 }
151 150
152 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 151 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
diff --git a/sway/commands/move.c b/sway/commands/move.c
index 849a18ad..fc2f1cc1 100644
--- a/sway/commands/move.c
+++ b/sway/commands/move.c
@@ -259,50 +259,24 @@ static void container_move_to_container(struct sway_container *container,
259 * In other words, rejigger it. */ 259 * In other words, rejigger it. */
260static void workspace_rejigger(struct sway_workspace *ws, 260static void workspace_rejigger(struct sway_workspace *ws,
261 struct sway_container *child, enum movement_direction move_dir) { 261 struct sway_container *child, enum movement_direction move_dir) {
262 if (!child->parent && ws->tiling->length == 1) {
263 ws->layout =
264 move_dir == MOVE_LEFT || move_dir == MOVE_RIGHT ? L_HORIZ : L_VERT;
265 workspace_update_representation(ws);
266 return;
267 }
262 container_detach(child); 268 container_detach(child);
263 workspace_wrap_children(ws); 269 struct sway_container *new_parent = workspace_wrap_children(ws);
264 270
265 int index = move_dir == MOVE_LEFT || move_dir == MOVE_UP ? 0 : 1; 271 int index = move_dir == MOVE_LEFT || move_dir == MOVE_UP ? 0 : 1;
266 workspace_insert_tiling(ws, child, index); 272 workspace_insert_tiling(ws, child, index);
273 container_flatten(new_parent);
267 ws->layout = 274 ws->layout =
268 move_dir == MOVE_LEFT || move_dir == MOVE_RIGHT ? L_HORIZ : L_VERT; 275 move_dir == MOVE_LEFT || move_dir == MOVE_RIGHT ? L_HORIZ : L_VERT;
269 workspace_update_representation(ws); 276 workspace_update_representation(ws);
270 child->width = child->height = 0; 277 child->width = child->height = 0;
271} 278}
272 279
273static void move_out_of_tabs_stacks(struct sway_container *container,
274 struct sway_container *current, enum movement_direction move_dir,
275 int offs) {
276 enum sway_container_layout layout = move_dir ==
277 MOVE_LEFT || move_dir == MOVE_RIGHT ? L_HORIZ : L_VERT;
278 list_t *siblings = container_get_siblings(container);
279 if (container == current && siblings->length == 1) {
280 wlr_log(WLR_DEBUG, "Changing layout of parent");
281 if (container->parent) {
282 container->parent->layout = layout;
283 container_update_representation(container);
284 } else {
285 container->workspace->layout = layout;
286 workspace_update_representation(container->workspace);
287 }
288 return;
289 }
290
291 wlr_log(WLR_DEBUG, "Moving out of tab/stack into a split");
292 if (current->parent) {
293 struct sway_container *new_parent =
294 container_split(current->parent, layout);
295 container_insert_child(new_parent, container, offs < 0 ? 0 : 1);
296 container_reap_empty(new_parent);
297 container_flatten(new_parent);
298 } else {
299 // Changing a workspace
300 struct sway_workspace *workspace = container->workspace;
301 workspace_split(workspace, layout);
302 workspace_insert_tiling(workspace, container, offs < 0 ? 0 : 1);
303 }
304}
305
306// Returns true if moved 280// Returns true if moved
307static bool container_move_in_direction(struct sway_container *container, 281static bool container_move_in_direction(struct sway_container *container,
308 enum movement_direction move_dir) { 282 enum movement_direction move_dir) {
@@ -334,7 +308,6 @@ static bool container_move_in_direction(struct sway_container *container,
334 int offs = move_dir == MOVE_LEFT || move_dir == MOVE_UP ? -1 : 1; 308 int offs = move_dir == MOVE_LEFT || move_dir == MOVE_UP ? -1 : 1;
335 309
336 while (current) { 310 while (current) {
337 struct sway_container *parent = current->parent;
338 list_t *siblings = container_get_siblings(current); 311 list_t *siblings = container_get_siblings(current);
339 enum sway_container_layout layout = container_parent_layout(current); 312 enum sway_container_layout layout = container_parent_layout(current);
340 int index = list_find(siblings, current); 313 int index = list_find(siblings, current);
@@ -343,15 +316,8 @@ static bool container_move_in_direction(struct sway_container *container,
343 if (is_parallel(layout, move_dir)) { 316 if (is_parallel(layout, move_dir)) {
344 if (desired == -1 || desired == siblings->length) { 317 if (desired == -1 || desired == siblings->length) {
345 if (current->parent == container->parent) { 318 if (current->parent == container->parent) {
346 if (!(parent && parent->is_fullscreen) && 319 current = current->parent;
347 (layout == L_TABBED || layout == L_STACKED)) { 320 continue;
348 move_out_of_tabs_stacks(container, current,
349 move_dir, offs);
350 return true;
351 } else {
352 current = current->parent;
353 continue;
354 }
355 } else { 321 } else {
356 // Special case 322 // Special case
357 if (current->parent) { 323 if (current->parent) {
@@ -369,10 +335,6 @@ static bool container_move_in_direction(struct sway_container *container,
369 siblings->items[desired], move_dir); 335 siblings->items[desired], move_dir);
370 return true; 336 return true;
371 } 337 }
372 } else if (!(parent && parent->is_fullscreen) &&
373 (layout == L_TABBED || layout == L_STACKED)) {
374 move_out_of_tabs_stacks(container, current, move_dir, offs);
375 return true;
376 } 338 }
377 339
378 current = current->parent; 340 current = current->parent;
@@ -388,10 +350,8 @@ static bool container_move_in_direction(struct sway_container *container,
388 // Maybe rejigger the workspace 350 // Maybe rejigger the workspace
389 struct sway_workspace *ws = container->workspace; 351 struct sway_workspace *ws = container->workspace;
390 if (!is_parallel(ws->layout, move_dir)) { 352 if (!is_parallel(ws->layout, move_dir)) {
391 if (ws->tiling->length >= 2) { 353 workspace_rejigger(ws, container, move_dir);
392 workspace_rejigger(ws, container, move_dir); 354 return true;
393 return true;
394 }
395 } else if (ws->layout == L_TABBED || ws->layout == L_STACKED) { 355 } else if (ws->layout == L_TABBED || ws->layout == L_STACKED) {
396 workspace_rejigger(ws, container, move_dir); 356 workspace_rejigger(ws, container, move_dir);
397 return true; 357 return true;
diff --git a/sway/commands/output/background.c b/sway/commands/output/background.c
index 9e370d43..30fb47c4 100644
--- a/sway/commands/output/background.c
+++ b/sway/commands/output/background.c
@@ -123,19 +123,13 @@ struct cmd_results *output_cmd_background(int argc, char **argv) {
123 } 123 }
124 free(src); 124 free(src);
125 } else { 125 } else {
126 // Escape spaces and quotes in the final path for swaybg 126 // Escape double quotes in the final path for swaybg
127 for (size_t i = 0; i < strlen(src); i++) { 127 for (size_t i = 0; i < strlen(src); i++) {
128 switch (src[i]) { 128 if (src[i] == '"') {
129 case ' ': 129 src = realloc(src, strlen(src) + 2);
130 case '\'': 130 memmove(src + i + 1, src + i, strlen(src + i) + 1);
131 case '\"': 131 *(src + i) = '\\';
132 src = realloc(src, strlen(src) + 2); 132 i++;
133 memmove(src + i + 1, src + i, strlen(src + i) + 1);
134 *(src + i) = '\\';
135 i++;
136 break;
137 default:
138 break;
139 } 133 }
140 } 134 }
141 135
diff --git a/sway/commands/resize.c b/sway/commands/resize.c
index 99e9dbda..1343b165 100644
--- a/sway/commands/resize.c
+++ b/sway/commands/resize.c
@@ -179,11 +179,11 @@ static void container_recursive_resize(struct sway_container *container,
179 } 179 }
180} 180}
181 181
182static void resize_tiled(struct sway_container *parent, int amount, 182static bool resize_tiled(struct sway_container *parent, int amount,
183 enum resize_axis axis) { 183 enum resize_axis axis) {
184 struct sway_container *focused = parent; 184 struct sway_container *focused = parent;
185 if (!parent) { 185 if (!parent) {
186 return; 186 return false;
187 } 187 }
188 188
189 enum sway_container_layout parallel_layout = 189 enum sway_container_layout parallel_layout =
@@ -216,7 +216,7 @@ static void resize_tiled(struct sway_container *parent, int amount,
216 } 216 }
217 if (!parent) { 217 if (!parent) {
218 // Can't resize in this direction 218 // Can't resize in this direction
219 return; 219 return false;
220 } 220 }
221 221
222 // Implement up/down/left/right direction by zeroing one of the weights, 222 // Implement up/down/left/right direction by zeroing one of the weights,
@@ -248,22 +248,22 @@ static void resize_tiled(struct sway_container *parent, int amount,
248 if (sibling_pos < parent_pos && minor_weight) { 248 if (sibling_pos < parent_pos && minor_weight) {
249 double pixels = -amount / minor_weight; 249 double pixels = -amount / minor_weight;
250 if (major_weight && (sibling_size + pixels / 2) < min_sane) { 250 if (major_weight && (sibling_size + pixels / 2) < min_sane) {
251 return; // Too small 251 return false; // Too small
252 } else if (!major_weight && sibling_size + pixels < min_sane) { 252 } else if (!major_weight && sibling_size + pixels < min_sane) {
253 return; // Too small 253 return false; // Too small
254 } 254 }
255 } else if (sibling_pos > parent_pos && major_weight) { 255 } else if (sibling_pos > parent_pos && major_weight) {
256 double pixels = -amount / major_weight; 256 double pixels = -amount / major_weight;
257 if (minor_weight && (sibling_size + pixels / 2) < min_sane) { 257 if (minor_weight && (sibling_size + pixels / 2) < min_sane) {
258 return; // Too small 258 return false; // Too small
259 } else if (!minor_weight && sibling_size + pixels < min_sane) { 259 } else if (!minor_weight && sibling_size + pixels < min_sane) {
260 return; // Too small 260 return false; // Too small
261 } 261 }
262 } 262 }
263 } else { 263 } else {
264 double pixels = amount; 264 double pixels = amount;
265 if (parent_size + pixels < min_sane) { 265 if (parent_size + pixels < min_sane) {
266 return; // Too small 266 return false; // Too small
267 } 267 }
268 } 268 }
269 } 269 }
@@ -317,9 +317,10 @@ static void resize_tiled(struct sway_container *parent, int amount,
317 } else { 317 } else {
318 arrange_workspace(parent->workspace); 318 arrange_workspace(parent->workspace);
319 } 319 }
320 return true;
320} 321}
321 322
322void container_resize_tiled(struct sway_container *parent, 323bool container_resize_tiled(struct sway_container *parent,
323 enum wlr_edges edge, int amount) { 324 enum wlr_edges edge, int amount) {
324 enum resize_axis axis = RESIZE_AXIS_INVALID; 325 enum resize_axis axis = RESIZE_AXIS_INVALID;
325 switch (edge) { 326 switch (edge) {
@@ -338,7 +339,7 @@ void container_resize_tiled(struct sway_container *parent,
338 case WLR_EDGE_NONE: 339 case WLR_EDGE_NONE:
339 break; 340 break;
340 } 341 }
341 resize_tiled(parent, amount, axis); 342 return resize_tiled(parent, amount, axis);
342} 343}
343 344
344/** 345/**
@@ -395,6 +396,10 @@ static struct cmd_results *resize_adjust_floating(enum resize_axis axis,
395 case RESIZE_AXIS_INVALID: 396 case RESIZE_AXIS_INVALID:
396 return cmd_results_new(CMD_INVALID, "resize", "Invalid axis/direction"); 397 return cmd_results_new(CMD_INVALID, "resize", "Invalid axis/direction");
397 } 398 }
399 if (grow_x == 0 && grow_y == 0) {
400 return cmd_results_new(CMD_INVALID, "resize",
401 "Cannot resize any further");
402 }
398 con->x += grow_x; 403 con->x += grow_x;
399 con->y += grow_y; 404 con->y += grow_y;
400 con->width += grow_width; 405 con->width += grow_width;
@@ -442,7 +447,10 @@ static struct cmd_results *resize_adjust_tiled(enum resize_axis axis,
442 } 447 }
443 } 448 }
444 449
445 resize_tiled(current, amount->amount, axis); 450 if (!resize_tiled(current, amount->amount, axis)) {
451 return cmd_results_new(CMD_INVALID, "resize",
452 "Cannot resize any further");
453 }
446 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 454 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
447} 455}
448 456
diff --git a/sway/commands/workspace.c b/sway/commands/workspace.c
index f026a39d..63f29641 100644
--- a/sway/commands/workspace.c
+++ b/sway/commands/workspace.c
@@ -10,6 +10,28 @@
10#include "log.h" 10#include "log.h"
11#include "stringop.h" 11#include "stringop.h"
12 12
13static struct workspace_config *workspace_config_find_or_create(char *ws_name) {
14 struct workspace_config *wsc = workspace_find_config(ws_name);
15 if (wsc) {
16 return wsc;
17 }
18 wsc = calloc(1, sizeof(struct workspace_config));
19 if (!wsc) {
20 return NULL;
21 }
22 wsc->workspace = strdup(ws_name);
23 wsc->gaps_inner = -1;
24 wsc->gaps_outer = -1;
25 list_add(config->workspace_configs, wsc);
26 return wsc;
27}
28
29void free_workspace_config(struct workspace_config *wsc) {
30 free(wsc->workspace);
31 free(wsc->output);
32 free(wsc);
33}
34
13struct cmd_results *cmd_workspace(int argc, char **argv) { 35struct cmd_results *cmd_workspace(int argc, char **argv) {
14 struct cmd_results *error = NULL; 36 struct cmd_results *error = NULL;
15 if ((error = checkarg(argc, "workspace", EXPECTED_AT_LEAST, 1))) { 37 if ((error = checkarg(argc, "workspace", EXPECTED_AT_LEAST, 1))) {
@@ -17,6 +39,7 @@ struct cmd_results *cmd_workspace(int argc, char **argv) {
17 } 39 }
18 40
19 int output_location = -1; 41 int output_location = -1;
42 int gaps_location = -1;
20 43
21 for (int i = 0; i < argc; ++i) { 44 for (int i = 0; i < argc; ++i) {
22 if (strcasecmp(argv[i], "output") == 0) { 45 if (strcasecmp(argv[i], "output") == 0) {
@@ -24,25 +47,54 @@ struct cmd_results *cmd_workspace(int argc, char **argv) {
24 break; 47 break;
25 } 48 }
26 } 49 }
50 for (int i = 0; i < argc; ++i) {
51 if (strcasecmp(argv[i], "gaps") == 0) {
52 gaps_location = i;
53 break;
54 }
55 }
27 if (output_location >= 0) { 56 if (output_location >= 0) {
28 if ((error = checkarg(argc, "workspace", EXPECTED_EQUAL_TO, output_location + 2))) { 57 if ((error = checkarg(argc, "workspace", EXPECTED_EQUAL_TO, output_location + 2))) {
29 return error; 58 return error;
30 } 59 }
31 struct workspace_output *wso = calloc(1, sizeof(struct workspace_output)); 60 char *ws_name = join_args(argv, argc - 2);
32 if (!wso) { 61 struct workspace_config *wsc = workspace_config_find_or_create(ws_name);
62 free(ws_name);
63 if (!wsc) {
33 return cmd_results_new(CMD_FAILURE, "workspace output", 64 return cmd_results_new(CMD_FAILURE, "workspace output",
34 "Unable to allocate workspace output"); 65 "Unable to allocate workspace output");
35 } 66 }
36 wso->workspace = join_args(argv, argc - 2); 67 free(wsc->output);
37 wso->output = strdup(argv[output_location + 1]); 68 wsc->output = strdup(argv[output_location + 1]);
38 int i = -1; 69 } else if (gaps_location >= 0) {
39 if ((i = list_seq_find(config->workspace_outputs, workspace_output_cmp_workspace, wso)) != -1) { 70 if ((error = checkarg(argc, "workspace", EXPECTED_EQUAL_TO, gaps_location + 3))) {
40 struct workspace_output *old = config->workspace_outputs->items[i]; 71 return error;
41 free(old); // workspaces can only be assigned to a single output 72 }
42 list_del(config->workspace_outputs, i); 73 char *ws_name = join_args(argv, argc - 3);
74 struct workspace_config *wsc = workspace_config_find_or_create(ws_name);
75 free(ws_name);
76 if (!wsc) {
77 return cmd_results_new(CMD_FAILURE, "workspace gaps",
78 "Unable to allocate workspace output");
79 }
80 int *prop = NULL;
81 if (strcasecmp(argv[gaps_location + 1], "inner") == 0) {
82 prop = &wsc->gaps_inner;
83 } else if (strcasecmp(argv[gaps_location + 1], "outer") == 0) {
84 prop = &wsc->gaps_outer;
85 } else {
86 return cmd_results_new(CMD_FAILURE, "workspace gaps",
87 "Expected 'workspace <ws> gaps inner|outer <px>'");
88 }
89 char *end;
90 int val = strtol(argv[gaps_location + 2], &end, 10);
91
92 if (strlen(end)) {
93 free(end);
94 return cmd_results_new(CMD_FAILURE, "workspace gaps",
95 "Expected 'workspace <ws> gaps inner|outer <px>'");
43 } 96 }
44 wlr_log(WLR_DEBUG, "Assigning workspace %s to output %s", wso->workspace, wso->output); 97 *prop = val >= 0 ? val : 0;
45 list_add(config->workspace_outputs, wso);
46 } else { 98 } else {
47 if (config->reading || !config->active) { 99 if (config->reading || !config->active) {
48 return cmd_results_new(CMD_DEFER, "workspace", NULL); 100 return cmd_results_new(CMD_DEFER, "workspace", NULL);
diff --git a/sway/config.c b/sway/config.c
index 830fb65f..1e08559d 100644
--- a/sway/config.c
+++ b/sway/config.c
@@ -95,7 +95,12 @@ void free_config(struct sway_config *config) {
95 list_free(config->bars); 95 list_free(config->bars);
96 } 96 }
97 list_free(config->cmd_queue); 97 list_free(config->cmd_queue);
98 list_free(config->workspace_outputs); 98 if (config->workspace_configs) {
99 for (int i = 0; i < config->workspace_configs->length; i++) {
100 free_workspace_config(config->workspace_configs->items[i]);
101 }
102 list_free(config->workspace_configs);
103 }
99 if (config->output_configs) { 104 if (config->output_configs) {
100 for (int i = 0; i < config->output_configs->length; i++) { 105 for (int i = 0; i < config->output_configs->length; i++) {
101 free_output_config(config->output_configs->items[i]); 106 free_output_config(config->output_configs->items[i]);
@@ -175,7 +180,7 @@ static void config_defaults(struct sway_config *config) {
175 if (!(config->symbols = create_list())) goto cleanup; 180 if (!(config->symbols = create_list())) goto cleanup;
176 if (!(config->modes = create_list())) goto cleanup; 181 if (!(config->modes = create_list())) goto cleanup;
177 if (!(config->bars = create_list())) goto cleanup; 182 if (!(config->bars = create_list())) goto cleanup;
178 if (!(config->workspace_outputs = create_list())) goto cleanup; 183 if (!(config->workspace_configs = create_list())) goto cleanup;
179 if (!(config->criteria = create_list())) goto cleanup; 184 if (!(config->criteria = create_list())) goto cleanup;
180 if (!(config->no_focus = create_list())) goto cleanup; 185 if (!(config->no_focus = create_list())) goto cleanup;
181 if (!(config->input_configs = create_list())) goto cleanup; 186 if (!(config->input_configs = create_list())) goto cleanup;
@@ -804,7 +809,7 @@ char *do_var_replacement(char *str) {
804// would compare two structs in full, while this method only compares the 809// would compare two structs in full, while this method only compares the
805// workspace. 810// workspace.
806int workspace_output_cmp_workspace(const void *a, const void *b) { 811int workspace_output_cmp_workspace(const void *a, const void *b) {
807 const struct workspace_output *wsa = a, *wsb = b; 812 const struct workspace_config *wsa = a, *wsb = b;
808 return lenient_strcmp(wsa->workspace, wsb->workspace); 813 return lenient_strcmp(wsa->workspace, wsb->workspace);
809} 814}
810 815
diff --git a/sway/config/bar.c b/sway/config/bar.c
index f83b37d1..48a632fb 100644
--- a/sway/config/bar.c
+++ b/sway/config/bar.c
@@ -165,7 +165,7 @@ cleanup:
165 return NULL; 165 return NULL;
166} 166}
167 167
168void invoke_swaybar(struct bar_config *bar) { 168static void invoke_swaybar(struct bar_config *bar) {
169 // Pipe to communicate errors 169 // Pipe to communicate errors
170 int filedes[2]; 170 int filedes[2];
171 if (pipe(filedes) == -1) { 171 if (pipe(filedes) == -1) {
@@ -219,27 +219,13 @@ void invoke_swaybar(struct bar_config *bar) {
219 close(filedes[1]); 219 close(filedes[1]);
220} 220}
221 221
222void load_swaybars() { 222void load_swaybars(void) {
223 for (int i = 0; i < config->bars->length; ++i) { 223 for (int i = 0; i < config->bars->length; ++i) {
224 struct bar_config *bar = config->bars->items[i]; 224 struct bar_config *bar = config->bars->items[i];
225 bool apply = false; 225 if (bar->pid != 0) {
226 if (bar->outputs) { 226 terminate_swaybar(bar->pid);
227 for (int j = 0; j < bar->outputs->length; ++j) {
228 char *o = bar->outputs->items[j];
229 if (!strcmp(o, "*") || output_by_name(o)) {
230 apply = true;
231 break;
232 }
233 }
234 } else {
235 apply = true;
236 }
237 if (apply) {
238 if (bar->pid != 0) {
239 terminate_swaybar(bar->pid);
240 }
241 wlr_log(WLR_DEBUG, "Invoking swaybar for bar id '%s'", bar->id);
242 invoke_swaybar(bar);
243 } 227 }
228 wlr_log(WLR_DEBUG, "Invoking swaybar for bar id '%s'", bar->id);
229 invoke_swaybar(bar);
244 } 230 }
245} 231}
diff --git a/sway/config/input.c b/sway/config/input.c
index ad5b96c8..794d5194 100644
--- a/sway/config/input.c
+++ b/sway/config/input.c
@@ -20,6 +20,7 @@ struct input_config *new_input_config(const char* identifier) {
20 20
21 input->tap = INT_MIN; 21 input->tap = INT_MIN;
22 input->tap_button_map = INT_MIN; 22 input->tap_button_map = INT_MIN;
23 input->drag = INT_MIN;
23 input->drag_lock = INT_MIN; 24 input->drag_lock = INT_MIN;
24 input->dwt = INT_MIN; 25 input->dwt = INT_MIN;
25 input->send_events = INT_MIN; 26 input->send_events = INT_MIN;
@@ -40,22 +41,24 @@ struct input_config *new_input_config(const char* identifier) {
40} 41}
41 42
42void merge_input_config(struct input_config *dst, struct input_config *src) { 43void merge_input_config(struct input_config *dst, struct input_config *src) {
43 if (src->identifier) {
44 free(dst->identifier);
45 dst->identifier = strdup(src->identifier);
46 }
47 if (src->accel_profile != INT_MIN) { 44 if (src->accel_profile != INT_MIN) {
48 dst->accel_profile = src->accel_profile; 45 dst->accel_profile = src->accel_profile;
49 } 46 }
50 if (src->click_method != INT_MIN) { 47 if (src->click_method != INT_MIN) {
51 dst->click_method = src->click_method; 48 dst->click_method = src->click_method;
52 } 49 }
50 if (src->drag != INT_MIN) {
51 dst->drag = src->drag;
52 }
53 if (src->drag_lock != INT_MIN) { 53 if (src->drag_lock != INT_MIN) {
54 dst->drag_lock = src->drag_lock; 54 dst->drag_lock = src->drag_lock;
55 } 55 }
56 if (src->dwt != INT_MIN) { 56 if (src->dwt != INT_MIN) {
57 dst->dwt = src->dwt; 57 dst->dwt = src->dwt;
58 } 58 }
59 if (src->left_handed != INT_MIN) {
60 dst->left_handed = src->left_handed;
61 }
59 if (src->middle_emulation != INT_MIN) { 62 if (src->middle_emulation != INT_MIN) {
60 dst->middle_emulation = src->middle_emulation; 63 dst->middle_emulation = src->middle_emulation;
61 } 64 }
@@ -125,14 +128,51 @@ void merge_input_config(struct input_config *dst, struct input_config *src) {
125 } 128 }
126} 129}
127 130
128struct input_config *copy_input_config(struct input_config *ic) { 131static void merge_wildcard_on_all(struct input_config *wildcard) {
129 struct input_config *copy = calloc(1, sizeof(struct input_config)); 132 for (int i = 0; i < config->input_configs->length; i++) {
130 if (copy == NULL) { 133 struct input_config *ic = config->input_configs->items[i];
131 wlr_log(WLR_ERROR, "could not allocate input config"); 134 if (strcmp(wildcard->identifier, ic->identifier) != 0) {
132 return NULL; 135 wlr_log(WLR_DEBUG, "Merging input * config on %s", ic->identifier);
136 merge_input_config(ic, wildcard);
137 }
133 } 138 }
134 merge_input_config(copy, ic); 139}
135 return copy; 140
141struct input_config *store_input_config(struct input_config *ic) {
142 bool wildcard = strcmp(ic->identifier, "*") == 0;
143 if (wildcard) {
144 merge_wildcard_on_all(ic);
145 }
146
147 int i = list_seq_find(config->input_configs, input_identifier_cmp,
148 ic->identifier);
149 if (i >= 0) {
150 wlr_log(WLR_DEBUG, "Merging on top of existing input config");
151 struct input_config *current = config->input_configs->items[i];
152 merge_input_config(current, ic);
153 free_input_config(ic);
154 ic = current;
155 } else if (!wildcard) {
156 wlr_log(WLR_DEBUG, "Adding non-wildcard input config");
157 i = list_seq_find(config->input_configs, input_identifier_cmp, "*");
158 if (i >= 0) {
159 wlr_log(WLR_DEBUG, "Merging on top of input * config");
160 struct input_config *current = new_input_config(ic->identifier);
161 merge_input_config(current, config->input_configs->items[i]);
162 merge_input_config(current, ic);
163 free_input_config(ic);
164 ic = current;
165 }
166 list_add(config->input_configs, ic);
167 } else {
168 // New wildcard config. Just add it
169 wlr_log(WLR_DEBUG, "Adding input * config");
170 list_add(config->input_configs, ic);
171 }
172
173 wlr_log(WLR_DEBUG, "Config stored for input %s", ic->identifier);
174
175 return ic;
136} 176}
137 177
138void free_input_config(struct input_config *ic) { 178void free_input_config(struct input_config *ic) {
diff --git a/sway/config/output.c b/sway/config/output.c
index 74d79130..6f337b66 100644
--- a/sway/config/output.c
+++ b/sway/config/output.c
@@ -237,7 +237,7 @@ void apply_output_config(struct output_config *oc, struct sway_output *output) {
237 wlr_log(WLR_DEBUG, "Setting background for output %d to %s", 237 wlr_log(WLR_DEBUG, "Setting background for output %d to %s",
238 output_i, oc->background); 238 output_i, oc->background);
239 239
240 size_t len = snprintf(NULL, 0, "%s %d %s %s %s", 240 size_t len = snprintf(NULL, 0, "%s %d \"%s\" %s %s",
241 config->swaybg_command ? config->swaybg_command : "swaybg", 241 config->swaybg_command ? config->swaybg_command : "swaybg",
242 output_i, oc->background, oc->background_option, 242 output_i, oc->background, oc->background_option,
243 oc->background_fallback ? oc->background_fallback : ""); 243 oc->background_fallback ? oc->background_fallback : "");
@@ -246,7 +246,7 @@ void apply_output_config(struct output_config *oc, struct sway_output *output) {
246 wlr_log(WLR_DEBUG, "Unable to allocate swaybg command"); 246 wlr_log(WLR_DEBUG, "Unable to allocate swaybg command");
247 return; 247 return;
248 } 248 }
249 snprintf(command, len + 1, "%s %d %s %s %s", 249 snprintf(command, len + 1, "%s %d \"%s\" %s %s",
250 config->swaybg_command ? config->swaybg_command : "swaybg", 250 config->swaybg_command ? config->swaybg_command : "swaybg",
251 output_i, oc->background, oc->background_option, 251 output_i, oc->background, oc->background_option,
252 oc->background_fallback ? oc->background_fallback : ""); 252 oc->background_fallback ? oc->background_fallback : "");
diff --git a/sway/config/seat.c b/sway/config/seat.c
index 83dac4c0..46456caf 100644
--- a/sway/config/seat.c
+++ b/sway/config/seat.c
@@ -30,7 +30,7 @@ struct seat_config *new_seat_config(const char* name) {
30 return seat; 30 return seat;
31} 31}
32 32
33struct seat_attachment_config *seat_attachment_config_new() { 33struct seat_attachment_config *seat_attachment_config_new(void) {
34 struct seat_attachment_config *attachment = 34 struct seat_attachment_config *attachment =
35 calloc(1, sizeof(struct seat_attachment_config)); 35 calloc(1, sizeof(struct seat_attachment_config));
36 if (!attachment) { 36 if (!attachment) {
diff --git a/sway/debug-tree.c b/sway/debug-tree.c
index 9644f4e5..16b479f9 100644
--- a/sway/debug-tree.c
+++ b/sway/debug-tree.c
@@ -120,7 +120,7 @@ static int draw_node(cairo_t *cairo, struct sway_node *node,
120 return height; 120 return height;
121} 121}
122 122
123void update_debug_tree() { 123void update_debug_tree(void) {
124 if (!debug.render_tree) { 124 if (!debug.render_tree) {
125 return; 125 return;
126 } 126 }
diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c
index d747e279..34d99d52 100644
--- a/sway/desktop/transaction.c
+++ b/sway/desktop/transaction.c
@@ -6,6 +6,7 @@
6#include <string.h> 6#include <string.h>
7#include <time.h> 7#include <time.h>
8#include <wlr/types/wlr_buffer.h> 8#include <wlr/types/wlr_buffer.h>
9#include "sway/config.h"
9#include "sway/debug.h" 10#include "sway/debug.h"
10#include "sway/desktop.h" 11#include "sway/desktop.h"
11#include "sway/desktop/idle_inhibit_v1.h" 12#include "sway/desktop/idle_inhibit_v1.h"
@@ -30,14 +31,14 @@ struct sway_transaction_instruction {
30 struct sway_transaction *transaction; 31 struct sway_transaction *transaction;
31 struct sway_node *node; 32 struct sway_node *node;
32 union { 33 union {
33 struct sway_output_state *output_state; 34 struct sway_output_state output_state;
34 struct sway_workspace_state *workspace_state; 35 struct sway_workspace_state workspace_state;
35 struct sway_container_state *container_state; 36 struct sway_container_state container_state;
36 }; 37 };
37 uint32_t serial; 38 uint32_t serial;
38}; 39};
39 40
40static struct sway_transaction *transaction_create() { 41static struct sway_transaction *transaction_create(void) {
41 struct sway_transaction *transaction = 42 struct sway_transaction *transaction =
42 calloc(1, sizeof(struct sway_transaction)); 43 calloc(1, sizeof(struct sway_transaction));
43 if (!sway_assert(transaction, "Unable to allocate transaction")) { 44 if (!sway_assert(transaction, "Unable to allocate transaction")) {
@@ -85,14 +86,7 @@ static void transaction_destroy(struct sway_transaction *transaction) {
85 86
86static void copy_output_state(struct sway_output *output, 87static void copy_output_state(struct sway_output *output,
87 struct sway_transaction_instruction *instruction) { 88 struct sway_transaction_instruction *instruction) {
88 struct sway_output_state *state = 89 struct sway_output_state *state = &instruction->output_state;
89 calloc(1, sizeof(struct sway_output_state));
90 if (!state) {
91 wlr_log(WLR_ERROR, "Could not allocate output state");
92 return;
93 }
94 instruction->output_state = state;
95
96 state->workspaces = create_list(); 90 state->workspaces = create_list();
97 list_cat(state->workspaces, output->workspaces); 91 list_cat(state->workspaces, output->workspaces);
98 92
@@ -101,13 +95,7 @@ static void copy_output_state(struct sway_output *output,
101 95
102static void copy_workspace_state(struct sway_workspace *ws, 96static void copy_workspace_state(struct sway_workspace *ws,
103 struct sway_transaction_instruction *instruction) { 97 struct sway_transaction_instruction *instruction) {
104 struct sway_workspace_state *state = 98 struct sway_workspace_state *state = &instruction->workspace_state;
105 calloc(1, sizeof(struct sway_workspace_state));
106 if (!state) {
107 wlr_log(WLR_ERROR, "Could not allocate workspace state");
108 return;
109 }
110 instruction->workspace_state = state;
111 99
112 state->fullscreen = ws->fullscreen; 100 state->fullscreen = ws->fullscreen;
113 state->x = ws->x; 101 state->x = ws->x;
@@ -137,13 +125,7 @@ static void copy_workspace_state(struct sway_workspace *ws,
137 125
138static void copy_container_state(struct sway_container *container, 126static void copy_container_state(struct sway_container *container,
139 struct sway_transaction_instruction *instruction) { 127 struct sway_transaction_instruction *instruction) {
140 struct sway_container_state *state = 128 struct sway_container_state *state = &instruction->container_state;
141 calloc(1, sizeof(struct sway_container_state));
142 if (!state) {
143 wlr_log(WLR_ERROR, "Could not allocate container state");
144 return;
145 }
146 instruction->container_state = state;
147 129
148 state->layout = container->layout; 130 state->layout = container->layout;
149 state->con_x = container->x; 131 state->con_x = container->x;
@@ -300,15 +282,15 @@ static void transaction_apply(struct sway_transaction *transaction) {
300 case N_ROOT: 282 case N_ROOT:
301 break; 283 break;
302 case N_OUTPUT: 284 case N_OUTPUT:
303 apply_output_state(node->sway_output, instruction->output_state); 285 apply_output_state(node->sway_output, &instruction->output_state);
304 break; 286 break;
305 case N_WORKSPACE: 287 case N_WORKSPACE:
306 apply_workspace_state(node->sway_workspace, 288 apply_workspace_state(node->sway_workspace,
307 instruction->workspace_state); 289 &instruction->workspace_state);
308 break; 290 break;
309 case N_CONTAINER: 291 case N_CONTAINER:
310 apply_container_state(node->sway_container, 292 apply_container_state(node->sway_container,
311 instruction->container_state); 293 &instruction->container_state);
312 break; 294 break;
313 } 295 }
314 296
@@ -334,7 +316,7 @@ static bool transaction_same_nodes(struct sway_transaction *a,
334 return true; 316 return true;
335} 317}
336 318
337static void transaction_progress_queue() { 319static void transaction_progress_queue(void) {
338 if (!server.transactions->length) { 320 if (!server.transactions->length) {
339 return; 321 return;
340 } 322 }
@@ -389,7 +371,17 @@ static bool should_configure(struct sway_node *node,
389 return false; 371 return false;
390 } 372 }
391 struct sway_container_state *cstate = &node->sway_container->current; 373 struct sway_container_state *cstate = &node->sway_container->current;
392 struct sway_container_state *istate = instruction->container_state; 374 struct sway_container_state *istate = &instruction->container_state;
375#ifdef HAVE_XWAYLAND
376 // Xwayland views are position-aware and need to be reconfigured
377 // when their position changes.
378 if (node->sway_container->view->type == SWAY_VIEW_XWAYLAND) {
379 if (cstate->view_x != istate->view_x ||
380 cstate->view_y != istate->view_y) {
381 return true;
382 }
383 }
384#endif
393 if (cstate->view_width == istate->view_width && 385 if (cstate->view_width == istate->view_width &&
394 cstate->view_height == istate->view_height) { 386 cstate->view_height == istate->view_height) {
395 return false; 387 return false;
@@ -407,10 +399,10 @@ static void transaction_commit(struct sway_transaction *transaction) {
407 struct sway_node *node = instruction->node; 399 struct sway_node *node = instruction->node;
408 if (should_configure(node, instruction)) { 400 if (should_configure(node, instruction)) {
409 instruction->serial = view_configure(node->sway_container->view, 401 instruction->serial = view_configure(node->sway_container->view,
410 instruction->container_state->view_x, 402 instruction->container_state.view_x,
411 instruction->container_state->view_y, 403 instruction->container_state.view_y,
412 instruction->container_state->view_width, 404 instruction->container_state.view_width,
413 instruction->container_state->view_height); 405 instruction->container_state.view_height);
414 ++transaction->num_waiting; 406 ++transaction->num_waiting;
415 407
416 // From here on we are rendering a saved buffer of the view, which 408 // From here on we are rendering a saved buffer of the view, which
@@ -502,8 +494,8 @@ void transaction_notify_view_ready_by_size(struct sway_view *view,
502 int width, int height) { 494 int width, int height) {
503 struct sway_transaction_instruction *instruction = 495 struct sway_transaction_instruction *instruction =
504 view->container->node.instruction; 496 view->container->node.instruction;
505 if (instruction->container_state->view_width == width && 497 if (instruction->container_state.view_width == width &&
506 instruction->container_state->view_height == height) { 498 instruction->container_state.view_height == height) {
507 set_instruction_ready(instruction); 499 set_instruction_ready(instruction);
508 } 500 }
509} 501}
diff --git a/sway/desktop/xdg_shell.c b/sway/desktop/xdg_shell.c
index 00448be7..6d1ccdd7 100644
--- a/sway/desktop/xdg_shell.c
+++ b/sway/desktop/xdg_shell.c
@@ -401,7 +401,7 @@ static void handle_map(struct wl_listener *listener, void *data) {
401 } else { 401 } else {
402 if (view->container->parent) { 402 if (view->container->parent) {
403 arrange_container(view->container->parent); 403 arrange_container(view->container->parent);
404 } else { 404 } else if (view->container->workspace) {
405 arrange_workspace(view->container->workspace); 405 arrange_workspace(view->container->workspace);
406 } 406 }
407 } 407 }
diff --git a/sway/desktop/xdg_shell_v6.c b/sway/desktop/xdg_shell_v6.c
index d2c9a68b..95ca396c 100644
--- a/sway/desktop/xdg_shell_v6.c
+++ b/sway/desktop/xdg_shell_v6.c
@@ -398,7 +398,7 @@ static void handle_map(struct wl_listener *listener, void *data) {
398 } else { 398 } else {
399 if (view->container->parent) { 399 if (view->container->parent) {
400 arrange_container(view->container->parent); 400 arrange_container(view->container->parent);
401 } else { 401 } else if (view->container->workspace) {
402 arrange_workspace(view->container->workspace); 402 arrange_workspace(view->container->workspace);
403 } 403 }
404 } 404 }
diff --git a/sway/desktop/xwayland.c b/sway/desktop/xwayland.c
index 3619f202..a12ac854 100644
--- a/sway/desktop/xwayland.c
+++ b/sway/desktop/xwayland.c
@@ -394,7 +394,7 @@ static void handle_map(struct wl_listener *listener, void *data) {
394 } else { 394 } else {
395 if (view->container->parent) { 395 if (view->container->parent) {
396 arrange_container(view->container->parent); 396 arrange_container(view->container->parent);
397 } else { 397 } else if (view->container->workspace) {
398 arrange_workspace(view->container->workspace); 398 arrange_workspace(view->container->workspace);
399 } 399 }
400 } 400 }
diff --git a/sway/input/cursor.c b/sway/input/cursor.c
index 85951c09..3ddc27a0 100644
--- a/sway/input/cursor.c
+++ b/sway/input/cursor.c
@@ -30,7 +30,7 @@
30// when dragging to the edge of a layout container. 30// when dragging to the edge of a layout container.
31#define DROP_LAYOUT_BORDER 30 31#define DROP_LAYOUT_BORDER 30
32 32
33static uint32_t get_current_time_msec() { 33static uint32_t get_current_time_msec(void) {
34 struct timespec now; 34 struct timespec now;
35 clock_gettime(CLOCK_MONOTONIC, &now); 35 clock_gettime(CLOCK_MONOTONIC, &now);
36 return now.tv_nsec / 1000; 36 return now.tv_nsec / 1000;
@@ -897,7 +897,7 @@ void dispatch_cursor_button(struct sway_cursor *cursor,
897 897
898 // Handle moving a tiling container 898 // Handle moving a tiling container
899 if (config->tiling_drag && mod_pressed && state == WLR_BUTTON_PRESSED && 899 if (config->tiling_drag && mod_pressed && state == WLR_BUTTON_PRESSED &&
900 !is_floating_or_child && !cont->is_fullscreen) { 900 !is_floating_or_child && cont && !cont->is_fullscreen) {
901 seat_pointer_notify_button(seat, time_msec, button, state); 901 seat_pointer_notify_button(seat, time_msec, button, state);
902 seat_begin_move_tiling(seat, cont, button); 902 seat_begin_move_tiling(seat, cont, button);
903 return; 903 return;
@@ -911,9 +911,10 @@ void dispatch_cursor_button(struct sway_cursor *cursor,
911 return; 911 return;
912 } 912 }
913 913
914 // Handle clicking a container surface 914 // Handle clicking a container surface or decorations
915 if (cont) { 915 if (cont) {
916 seat_set_focus_container(seat, cont); 916 node = seat_get_focus_inactive(seat, &cont->node);
917 seat_set_focus(seat, node);
917 seat_pointer_notify_button(seat, time_msec, button, state); 918 seat_pointer_notify_button(seat, time_msec, button, state);
918 return; 919 return;
919 } 920 }
@@ -930,12 +931,52 @@ static void handle_cursor_button(struct wl_listener *listener, void *data) {
930 transaction_commit_dirty(); 931 transaction_commit_dirty();
931} 932}
932 933
934static void dispatch_cursor_axis(struct sway_cursor *cursor,
935 struct wlr_event_pointer_axis *event) {
936 struct sway_seat *seat = cursor->seat;
937
938 // Determine what's under the cursor
939 struct wlr_surface *surface = NULL;
940 double sx, sy;
941 struct sway_node *node = node_at_coords(seat,
942 cursor->cursor->x, cursor->cursor->y, &surface, &sx, &sy);
943 struct sway_container *cont = node && node->type == N_CONTAINER ?
944 node->sway_container : NULL;
945 enum wlr_edges edge = cont ? find_edge(cont, cursor) : WLR_EDGE_NONE;
946 bool on_border = edge != WLR_EDGE_NONE;
947 bool on_titlebar = cont && !on_border && !surface;
948
949 // Scrolling on a tabbed or stacked title bar
950 if (on_titlebar) {
951 enum sway_container_layout layout = container_parent_layout(cont);
952 if (layout == L_TABBED || layout == L_STACKED) {
953 struct sway_node *active =
954 seat_get_active_tiling_child(seat, node_get_parent(node));
955 list_t *siblings = container_get_siblings(cont);
956 int desired = list_find(siblings, active->sway_container) +
957 event->delta_discrete;
958 if (desired < 0) {
959 desired = 0;
960 } else if (desired >= siblings->length) {
961 desired = siblings->length - 1;
962 }
963 struct sway_container *new_focus = siblings->items[desired];
964 node = seat_get_focus_inactive(seat, &new_focus->node);
965 seat_set_focus(seat, node);
966 return;
967 }
968 }
969
970 wlr_seat_pointer_notify_axis(cursor->seat->wlr_seat, event->time_msec,
971 event->orientation, event->delta, event->delta_discrete, event->source);
972}
973
933static void handle_cursor_axis(struct wl_listener *listener, void *data) { 974static void handle_cursor_axis(struct wl_listener *listener, void *data) {
934 struct sway_cursor *cursor = wl_container_of(listener, cursor, axis); 975 struct sway_cursor *cursor = wl_container_of(listener, cursor, axis);
935 wlr_idle_notify_activity(cursor->seat->input->server->idle, cursor->seat->wlr_seat); 976 wlr_idle_notify_activity(cursor->seat->input->server->idle, cursor->seat->wlr_seat);
936 struct wlr_event_pointer_axis *event = data; 977 struct wlr_event_pointer_axis *event = data;
937 wlr_seat_pointer_notify_axis(cursor->seat->wlr_seat, event->time_msec, 978 dispatch_cursor_axis(cursor, event);
938 event->orientation, event->delta, event->delta_discrete, event->source); 979 transaction_commit_dirty();
939} 980}
940 981
941static void handle_touch_down(struct wl_listener *listener, void *data) { 982static void handle_touch_down(struct wl_listener *listener, void *data) {
@@ -965,8 +1006,7 @@ static void handle_touch_down(struct wl_listener *listener, void *data) {
965 if (seat_is_input_allowed(seat, surface)) { 1006 if (seat_is_input_allowed(seat, surface)) {
966 wlr_seat_touch_notify_down(wlr_seat, surface, event->time_msec, 1007 wlr_seat_touch_notify_down(wlr_seat, surface, event->time_msec,
967 event->touch_id, sx, sy); 1008 event->touch_id, sx, sy);
968 cursor->image_client = NULL; 1009 cursor_set_image(cursor, NULL, NULL);
969 wlr_cursor_set_image(cursor->cursor, NULL, 0, 0, 0, 0, 0, 0);
970 } 1010 }
971} 1011}
972 1012
@@ -1134,11 +1174,13 @@ static void handle_request_set_cursor(struct wl_listener *listener,
1134 1174
1135void cursor_set_image(struct sway_cursor *cursor, const char *image, 1175void cursor_set_image(struct sway_cursor *cursor, const char *image,
1136 struct wl_client *client) { 1176 struct wl_client *client) {
1137 if (!cursor->image || strcmp(cursor->image, image) != 0) { 1177 if (!image) {
1178 wlr_cursor_set_image(cursor->cursor, NULL, 0, 0, 0, 0, 0, 0);
1179 } else if (!cursor->image || strcmp(cursor->image, image) != 0) {
1138 wlr_xcursor_manager_set_cursor_image(cursor->xcursor_manager, image, 1180 wlr_xcursor_manager_set_cursor_image(cursor->xcursor_manager, image,
1139 cursor->cursor); 1181 cursor->cursor);
1140 cursor->image = image;
1141 } 1182 }
1183 cursor->image = image;
1142 cursor->image_client = client; 1184 cursor->image_client = client;
1143} 1185}
1144 1186
diff --git a/sway/input/input-manager.c b/sway/input/input-manager.c
index b4352c6a..32f0355e 100644
--- a/sway/input/input-manager.c
+++ b/sway/input/input-manager.c
@@ -120,6 +120,13 @@ static void input_manager_libinput_config_pointer(
120 libinput_device_config_click_set_method(libinput_device, 120 libinput_device_config_click_set_method(libinput_device,
121 ic->click_method); 121 ic->click_method);
122 } 122 }
123 if (ic->drag != INT_MIN) {
124 wlr_log(WLR_DEBUG,
125 "libinput_config_pointer(%s) tap_set_drag_enabled(%d)",
126 ic->identifier, ic->click_method);
127 libinput_device_config_tap_set_drag_enabled(libinput_device,
128 ic->drag);
129 }
123 if (ic->drag_lock != INT_MIN) { 130 if (ic->drag_lock != INT_MIN) {
124 wlr_log(WLR_DEBUG, 131 wlr_log(WLR_DEBUG,
125 "libinput_config_pointer(%s) tap_set_drag_lock_enabled(%d)", 132 "libinput_config_pointer(%s) tap_set_drag_lock_enabled(%d)",
@@ -233,7 +240,8 @@ static void handle_new_input(struct wl_listener *listener, void *data) {
233 wlr_log(WLR_DEBUG, "adding device: '%s'", 240 wlr_log(WLR_DEBUG, "adding device: '%s'",
234 input_device->identifier); 241 input_device->identifier);
235 242
236 if (input_device->wlr_device->type == WLR_INPUT_DEVICE_POINTER) { 243 if (input_device->wlr_device->type == WLR_INPUT_DEVICE_POINTER ||
244 input_device->wlr_device->type == WLR_INPUT_DEVICE_TABLET_TOOL) {
237 input_manager_libinput_config_pointer(input_device); 245 input_manager_libinput_config_pointer(input_device);
238 } 246 }
239 247
@@ -389,9 +397,12 @@ void input_manager_set_focus(struct sway_input_manager *input,
389void input_manager_apply_input_config(struct sway_input_manager *input, 397void input_manager_apply_input_config(struct sway_input_manager *input,
390 struct input_config *input_config) { 398 struct input_config *input_config) {
391 struct sway_input_device *input_device = NULL; 399 struct sway_input_device *input_device = NULL;
400 bool wildcard = strcmp(input_config->identifier, "*") == 0;
392 wl_list_for_each(input_device, &input->devices, link) { 401 wl_list_for_each(input_device, &input->devices, link) {
393 if (strcmp(input_device->identifier, input_config->identifier) == 0) { 402 if (strcmp(input_device->identifier, input_config->identifier) == 0
394 if (input_device->wlr_device->type == WLR_INPUT_DEVICE_POINTER) { 403 || wildcard) {
404 if (input_device->wlr_device->type == WLR_INPUT_DEVICE_POINTER ||
405 input_device->wlr_device->type == WLR_INPUT_DEVICE_TABLET_TOOL) {
395 input_manager_libinput_config_pointer(input_device); 406 input_manager_libinput_config_pointer(input_device);
396 } 407 }
397 408
@@ -480,13 +491,16 @@ struct sway_seat *input_manager_get_default_seat(
480} 491}
481 492
482struct input_config *input_device_get_config(struct sway_input_device *device) { 493struct input_config *input_device_get_config(struct sway_input_device *device) {
494 struct input_config *wildcard_config = NULL;
483 struct input_config *input_config = NULL; 495 struct input_config *input_config = NULL;
484 for (int i = 0; i < config->input_configs->length; ++i) { 496 for (int i = 0; i < config->input_configs->length; ++i) {
485 input_config = config->input_configs->items[i]; 497 input_config = config->input_configs->items[i];
486 if (strcmp(input_config->identifier, device->identifier) == 0) { 498 if (strcmp(input_config->identifier, device->identifier) == 0) {
487 return input_config; 499 return input_config;
500 } else if (strcmp(input_config->identifier, "*") == 0) {
501 wildcard_config = input_config;
488 } 502 }
489 } 503 }
490 504
491 return NULL; 505 return wildcard_config;
492} 506}
diff --git a/sway/input/seat.c b/sway/input/seat.c
index 49fe46ba..4817eae7 100644
--- a/sway/input/seat.c
+++ b/sway/input/seat.c
@@ -348,17 +348,42 @@ struct sway_seat *seat_create(struct sway_input_manager *input,
348 seat->input = input; 348 seat->input = input;
349 wl_list_init(&seat->devices); 349 wl_list_init(&seat->devices);
350 350
351 wlr_seat_set_capabilities(seat->wlr_seat,
352 WL_SEAT_CAPABILITY_KEYBOARD |
353 WL_SEAT_CAPABILITY_POINTER |
354 WL_SEAT_CAPABILITY_TOUCH);
355
356
357 wl_list_insert(&input->seats, &seat->link); 351 wl_list_insert(&input->seats, &seat->link);
358 352
359 return seat; 353 return seat;
360} 354}
361 355
356static void seat_update_capabilities(struct sway_seat *seat) {
357 uint32_t caps = 0;
358 struct sway_seat_device *seat_device;
359 wl_list_for_each(seat_device, &seat->devices, link) {
360 switch (seat_device->input_device->wlr_device->type) {
361 case WLR_INPUT_DEVICE_KEYBOARD:
362 caps |= WL_SEAT_CAPABILITY_KEYBOARD;
363 break;
364 case WLR_INPUT_DEVICE_POINTER:
365 caps |= WL_SEAT_CAPABILITY_POINTER;
366 break;
367 case WLR_INPUT_DEVICE_TOUCH:
368 caps |= WL_SEAT_CAPABILITY_TOUCH;
369 break;
370 case WLR_INPUT_DEVICE_TABLET_TOOL:
371 caps |= WL_SEAT_CAPABILITY_POINTER;
372 break;
373 case WLR_INPUT_DEVICE_TABLET_PAD:
374 break;
375 }
376 }
377 wlr_seat_set_capabilities(seat->wlr_seat, caps);
378
379 // Hide cursor if seat doesn't have pointer capability
380 if ((caps & WL_SEAT_CAPABILITY_POINTER) == 0) {
381 cursor_set_image(seat->cursor, NULL, NULL);
382 } else {
383 cursor_set_image(seat->cursor, "left_ptr", NULL);
384 }
385}
386
362static void seat_apply_input_config(struct sway_seat *seat, 387static void seat_apply_input_config(struct sway_seat *seat,
363 struct sway_seat_device *sway_device) { 388 struct sway_seat_device *sway_device) {
364 const char *mapped_to_output = NULL; 389 const char *mapped_to_output = NULL;
@@ -489,6 +514,8 @@ void seat_add_device(struct sway_seat *seat,
489 wl_list_insert(&seat->devices, &seat_device->link); 514 wl_list_insert(&seat->devices, &seat_device->link);
490 515
491 seat_configure_device(seat, input_device); 516 seat_configure_device(seat, input_device);
517
518 seat_update_capabilities(seat);
492} 519}
493 520
494void seat_remove_device(struct sway_seat *seat, 521void seat_remove_device(struct sway_seat *seat,
@@ -503,6 +530,8 @@ void seat_remove_device(struct sway_seat *seat,
503 input_device->identifier, seat->wlr_seat->name); 530 input_device->identifier, seat->wlr_seat->name);
504 531
505 seat_device_destroy(seat_device); 532 seat_device_destroy(seat_device);
533
534 seat_update_capabilities(seat);
506} 535}
507 536
508void seat_configure_xcursor(struct sway_seat *seat) { 537void seat_configure_xcursor(struct sway_seat *seat) {
@@ -532,8 +561,7 @@ void seat_configure_xcursor(struct sway_seat *seat) {
532 output->name, (double)output->scale); 561 output->name, (double)output->scale);
533 } 562 }
534 563
535 wlr_xcursor_manager_set_cursor_image(seat->cursor->xcursor_manager, 564 cursor_set_image(seat->cursor, "left_ptr", NULL);
536 "left_ptr", seat->cursor->cursor);
537 wlr_cursor_warp(seat->cursor->cursor, NULL, seat->cursor->cursor->x, 565 wlr_cursor_warp(seat->cursor->cursor, NULL, seat->cursor->cursor->x,
538 seat->cursor->cursor->y); 566 seat->cursor->cursor->y);
539} 567}
diff --git a/sway/ipc-json.c b/sway/ipc-json.c
index f054ac9f..45915094 100644
--- a/sway/ipc-json.c
+++ b/sway/ipc-json.c
@@ -42,7 +42,7 @@ static const char *ipc_json_orientation_description(enum sway_container_layout l
42 return "none"; 42 return "none";
43} 43}
44 44
45json_object *ipc_json_get_version() { 45json_object *ipc_json_get_version(void) {
46 int major = 0, minor = 0, patch = 0; 46 int major = 0, minor = 0, patch = 0;
47 json_object *version = json_object_new_object(); 47 json_object *version = json_object_new_object();
48 48
diff --git a/sway/ipc-server.c b/sway/ipc-server.c
index 8ae265f6..2d915502 100644
--- a/sway/ipc-server.c
+++ b/sway/ipc-server.c
@@ -1,8 +1,5 @@
1// See https://i3wm.org/docs/ipc.html for protocol information 1// See https://i3wm.org/docs/ipc.html for protocol information
2#ifndef __FreeBSD__ 2#define _POSIX_C_SOURCE 200112L
3// Any value will hide SOCK_CLOEXEC on FreeBSD (__BSD_VISIBLE=0)
4#define _XOPEN_SOURCE 700
5#endif
6#ifdef __linux__ 3#ifdef __linux__
7#include <linux/input-event-codes.h> 4#include <linux/input-event-codes.h>
8#elif __FreeBSD__ 5#elif __FreeBSD__
@@ -89,10 +86,16 @@ static void handle_display_destroy(struct wl_listener *listener, void *data) {
89} 86}
90 87
91void ipc_init(struct sway_server *server) { 88void ipc_init(struct sway_server *server) {
92 ipc_socket = socket(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0); 89 ipc_socket = socket(AF_UNIX, SOCK_STREAM, 0);
93 if (ipc_socket == -1) { 90 if (ipc_socket == -1) {
94 sway_abort("Unable to create IPC socket"); 91 sway_abort("Unable to create IPC socket");
95 } 92 }
93 if (fcntl(ipc_socket, F_SETFD, FD_CLOEXEC) == -1) {
94 sway_abort("Unable to set CLOEXEC on IPC socket");
95 }
96 if (fcntl(ipc_socket, F_SETFL, O_NONBLOCK) == -1) {
97 sway_abort("Unable to set NONBLOCK on IPC socket");
98 }
96 99
97 ipc_sockaddr = ipc_user_sockaddr(); 100 ipc_sockaddr = ipc_user_sockaddr();
98 101
@@ -580,7 +583,7 @@ void ipc_client_handle_command(struct ipc_client *client) {
580 switch (client->current_command) { 583 switch (client->current_command) {
581 case IPC_COMMAND: 584 case IPC_COMMAND:
582 { 585 {
583 struct cmd_results *results = execute_command(buf, NULL); 586 struct cmd_results *results = execute_command(buf, NULL, NULL);
584 transaction_commit_dirty(); 587 transaction_commit_dirty();
585 char *json = cmd_results_to_json(results); 588 char *json = cmd_results_to_json(results);
586 int length = strlen(json); 589 int length = strlen(json);
diff --git a/sway/main.c b/sway/main.c
index fb4f0d8c..dea4a31c 100644
--- a/sway/main.c
+++ b/sway/main.c
@@ -12,10 +12,6 @@
12#include <sys/wait.h> 12#include <sys/wait.h>
13#include <sys/un.h> 13#include <sys/un.h>
14#include <unistd.h> 14#include <unistd.h>
15#ifdef __linux__
16#include <sys/capability.h>
17#include <sys/prctl.h>
18#endif
19#include <wlr/util/log.h> 15#include <wlr/util/log.h>
20#include "sway/commands.h" 16#include "sway/commands.h"
21#include "sway/config.h" 17#include "sway/config.h"
@@ -45,7 +41,7 @@ void sig_handler(int signal) {
45 sway_terminate(EXIT_SUCCESS); 41 sway_terminate(EXIT_SUCCESS);
46} 42}
47 43
48void detect_raspi() { 44void detect_raspi(void) {
49 bool raspi = false; 45 bool raspi = false;
50 FILE *f = fopen("/sys/firmware/devicetree/base/model", "r"); 46 FILE *f = fopen("/sys/firmware/devicetree/base/model", "r");
51 if (!f) { 47 if (!f) {
@@ -85,7 +81,7 @@ void detect_raspi() {
85 } 81 }
86} 82}
87 83
88void detect_proprietary() { 84void detect_proprietary(void) {
89 FILE *f = fopen("/proc/modules", "r"); 85 FILE *f = fopen("/proc/modules", "r");
90 if (!f) { 86 if (!f) {
91 return; 87 return;
@@ -120,7 +116,7 @@ void run_as_ipc_client(char *command, char *socket_path) {
120 close(socketfd); 116 close(socketfd);
121} 117}
122 118
123static void log_env() { 119static void log_env(void) {
124 const char *log_vars[] = { 120 const char *log_vars[] = {
125 "PATH", 121 "PATH",
126 "LD_LIBRARY_PATH", 122 "LD_LIBRARY_PATH",
@@ -135,7 +131,7 @@ static void log_env() {
135 } 131 }
136} 132}
137 133
138static void log_distro() { 134static void log_distro(void) {
139 const char *paths[] = { 135 const char *paths[] = {
140 "/etc/lsb-release", 136 "/etc/lsb-release",
141 "/etc/os-release", 137 "/etc/os-release",
@@ -162,7 +158,7 @@ static void log_distro() {
162 } 158 }
163} 159}
164 160
165static void log_kernel() { 161static void log_kernel(void) {
166 FILE *f = popen("uname -a", "r"); 162 FILE *f = popen("uname -a", "r");
167 if (!f) { 163 if (!f) {
168 wlr_log(WLR_INFO, "Unable to determine kernel version"); 164 wlr_log(WLR_INFO, "Unable to determine kernel version");
@@ -181,28 +177,8 @@ static void log_kernel() {
181 pclose(f); 177 pclose(f);
182} 178}
183 179
184static void executable_sanity_check() {
185#ifdef __linux__
186 struct stat sb;
187 char *exe = realpath("/proc/self/exe", NULL);
188 stat(exe, &sb);
189 // We assume that cap_get_file returning NULL implies ENODATA
190 if (sb.st_mode & (S_ISUID|S_ISGID) && cap_get_file(exe)) {
191 wlr_log(WLR_ERROR,
192 "sway executable has both the s(g)uid bit AND file caps set.");
193 wlr_log(WLR_ERROR,
194 "This is strongly discouraged (and completely broken).");
195 wlr_log(WLR_ERROR,
196 "Please clear one of them (either the suid bit, or the file caps).");
197 wlr_log(WLR_ERROR,
198 "If unsure, strip the file caps.");
199 exit(EXIT_FAILURE);
200 }
201 free(exe);
202#endif
203}
204 180
205static void drop_permissions(bool keep_caps) { 181static void drop_permissions(void) {
206 if (getuid() != geteuid() || getgid() != getegid()) { 182 if (getuid() != geteuid() || getgid() != getegid()) {
207 if (setgid(getgid()) != 0) { 183 if (setgid(getgid()) != 0) {
208 wlr_log(WLR_ERROR, "Unable to drop root"); 184 wlr_log(WLR_ERROR, "Unable to drop root");
@@ -217,20 +193,6 @@ static void drop_permissions(bool keep_caps) {
217 wlr_log(WLR_ERROR, "Root privileges can be restored."); 193 wlr_log(WLR_ERROR, "Root privileges can be restored.");
218 exit(EXIT_FAILURE); 194 exit(EXIT_FAILURE);
219 } 195 }
220#ifdef __linux__
221 if (keep_caps) {
222 // Drop every cap except CAP_SYS_PTRACE
223 cap_t caps = cap_init();
224 cap_value_t keep = CAP_SYS_PTRACE;
225 wlr_log(WLR_INFO, "Dropping extra capabilities");
226 if (cap_set_flag(caps, CAP_PERMITTED, 1, &keep, CAP_SET) ||
227 cap_set_flag(caps, CAP_EFFECTIVE, 1, &keep, CAP_SET) ||
228 cap_set_proc(caps)) {
229 wlr_log(WLR_ERROR, "Failed to drop extra capabilities");
230 exit(EXIT_FAILURE);
231 }
232 }
233#endif
234} 196}
235 197
236void enable_debug_flag(const char *flag) { 198void enable_debug_flag(const char *flag) {
@@ -279,14 +241,6 @@ int main(int argc, char **argv) {
279 " --get-socketpath Gets the IPC socket path and prints it, then exits.\n" 241 " --get-socketpath Gets the IPC socket path and prints it, then exits.\n"
280 "\n"; 242 "\n";
281 243
282 // Security:
283 unsetenv("LD_PRELOAD");
284#ifdef _LD_LIBRARY_PATH
285 setenv("LD_LIBRARY_PATH", _LD_LIBRARY_PATH, 1);
286#else
287 unsetenv("LD_LIBRARY_PATH");
288#endif
289
290 int c; 244 int c;
291 while (1) { 245 while (1) {
292 int option_index = 0; 246 int option_index = 0;
@@ -347,7 +301,7 @@ int main(int argc, char **argv) {
347 wlr_log(WLR_ERROR, "Don't use options with the IPC client"); 301 wlr_log(WLR_ERROR, "Don't use options with the IPC client");
348 exit(EXIT_FAILURE); 302 exit(EXIT_FAILURE);
349 } 303 }
350 drop_permissions(false); 304 drop_permissions();
351 char *socket_path = getenv("SWAYSOCK"); 305 char *socket_path = getenv("SWAYSOCK");
352 if (!socket_path) { 306 if (!socket_path) {
353 wlr_log(WLR_ERROR, "Unable to retrieve socket path"); 307 wlr_log(WLR_ERROR, "Unable to retrieve socket path");
@@ -358,34 +312,17 @@ int main(int argc, char **argv) {
358 return 0; 312 return 0;
359 } 313 }
360 314
361 executable_sanity_check();
362 bool suid = false;
363
364 if (!server_privileged_prepare(&server)) { 315 if (!server_privileged_prepare(&server)) {
365 return 1; 316 return 1;
366 } 317 }
367 318
368#if defined(__linux__) || defined(__FreeBSD__)
369 if (getuid() != geteuid() || getgid() != getegid()) {
370#ifdef __linux__
371 // Retain capabilities after setuid()
372 if (prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0)) {
373 wlr_log(WLR_ERROR, "Cannot keep caps after setuid()");
374 exit(EXIT_FAILURE);
375 }
376#endif
377 suid = true;
378 }
379#endif
380
381 log_kernel(); 319 log_kernel();
382 log_distro(); 320 log_distro();
383 detect_proprietary(); 321 detect_proprietary();
384 detect_raspi(); 322 detect_raspi();
385 323
386#if defined(__linux__) || defined(__FreeBSD__) 324 drop_permissions();
387 drop_permissions(suid); 325
388#endif
389 // handle SIGTERM signals 326 // handle SIGTERM signals
390 signal(SIGTERM, sig_handler); 327 signal(SIGTERM, sig_handler);
391 328
@@ -424,11 +361,12 @@ int main(int argc, char **argv) {
424 } 361 }
425 362
426 config->active = true; 363 config->active = true;
364 load_swaybars();
427 // Execute commands until there are none left 365 // Execute commands until there are none left
428 wlr_log(WLR_DEBUG, "Running deferred commands"); 366 wlr_log(WLR_DEBUG, "Running deferred commands");
429 while (config->cmd_queue->length) { 367 while (config->cmd_queue->length) {
430 char *line = config->cmd_queue->items[0]; 368 char *line = config->cmd_queue->items[0];
431 struct cmd_results *res = execute_command(line, NULL); 369 struct cmd_results *res = execute_command(line, NULL, NULL);
432 if (res->status != CMD_SUCCESS) { 370 if (res->status != CMD_SUCCESS) {
433 wlr_log(WLR_ERROR, "Error on line '%s': %s", line, res->error); 371 wlr_log(WLR_ERROR, "Error on line '%s': %s", line, res->error);
434 } 372 }
diff --git a/sway/meson.build b/sway/meson.build
index d67a4c64..6eb9a9d7 100644
--- a/sway/meson.build
+++ b/sway/meson.build
@@ -119,6 +119,7 @@ sway_sources = files(
119 119
120 'commands/input/accel_profile.c', 120 'commands/input/accel_profile.c',
121 'commands/input/click_method.c', 121 'commands/input/click_method.c',
122 'commands/input/drag.c',
122 'commands/input/drag_lock.c', 123 'commands/input/drag_lock.c',
123 'commands/input/dwt.c', 124 'commands/input/dwt.c',
124 'commands/input/events.c', 125 'commands/input/events.c',
@@ -164,7 +165,6 @@ sway_deps = [
164 cairo, 165 cairo,
165 gdk_pixbuf, 166 gdk_pixbuf,
166 jsonc, 167 jsonc,
167 libcap,
168 libinput, 168 libinput,
169 math, 169 math,
170 pango, 170 pango,
@@ -187,5 +187,6 @@ executable(
187 include_directories: [sway_inc], 187 include_directories: [sway_inc],
188 dependencies: sway_deps, 188 dependencies: sway_deps,
189 link_with: [lib_sway_common], 189 link_with: [lib_sway_common],
190 install_rpath : rpathdir,
190 install: true 191 install: true
191) 192)
diff --git a/sway/sway-bar.5.scd b/sway/sway-bar.5.scd
index a61e2829..00b9386e 100644
--- a/sway/sway-bar.5.scd
+++ b/sway/sway-bar.5.scd
@@ -6,8 +6,7 @@ sway-bar - bar configuration file and commands
6 6
7# DESCRIPTION 7# DESCRIPTION
8 8
9Sway allows configuring swaybar in the sway configuration file. Swaybar 9Sway allows configuring swaybar in the sway configuration file.
10commands must be used inside a _bar { }_ block in the config file.
11 10
12# COMMANDS 11# COMMANDS
13 12
diff --git a/sway/sway-input.5.scd b/sway/sway-input.5.scd
index 707c36af..14f2a007 100644
--- a/sway/sway-input.5.scd
+++ b/sway/sway-input.5.scd
@@ -7,7 +7,6 @@ sway-input - input configuration file and commands
7# DESCRIPTION 7# DESCRIPTION
8 8
9Sway allows for configuration of devices within the sway configuration file. 9Sway allows for configuration of devices within the sway configuration file.
10sway-input commands must be used inside an _input { }_ block in the config.
11To obtain a list of available device identifiers, run *swaymsg -t get\_inputs*. 10To obtain a list of available device identifiers, run *swaymsg -t get\_inputs*.
12 11
13# INPUT COMMANDS 12# INPUT COMMANDS
@@ -68,6 +67,9 @@ The following commands may only be used in the configuration file.
68*input* <identifier> click\_method none|button\_areas|clickfinger 67*input* <identifier> click\_method none|button\_areas|clickfinger
69 Changes the click method for the specified device. 68 Changes the click method for the specified device.
70 69
70*input* <identifier> drag enabled|disabled
71 Enables or disables tap-and-drag for specified input device.
72
71*input* <identifier> drag\_lock enabled|disabled 73*input* <identifier> drag\_lock enabled|disabled
72 Enables or disables drag lock for specified input device. 74 Enables or disables drag lock for specified input device.
73 75
@@ -116,8 +118,7 @@ The following commands may only be used in the configuration file.
116 118
117## SEAT CONFIGURATION 119## SEAT CONFIGURATION
118 120
119Configure options for multiseat mode. sway-seat commands must be used inside a 121Configure options for multiseat mode.
120_seat { }_ block in the config.
121 122
122A *seat* is a collection of input devices that act independently of each other. 123A *seat* is a collection of input devices that act independently of each other.
123Seats are identified by name and the default seat is _seat0_ if no seats are 124Seats are identified by name and the default seat is _seat0_ if no seats are
diff --git a/sway/sway.5.scd b/sway/sway.5.scd
index 927bf55c..aa5b38ab 100644
--- a/sway/sway.5.scd
+++ b/sway/sway.5.scd
@@ -19,6 +19,24 @@ bindsym Shift+XF86AudioRaiseVolume exec \\
19 pactl set-sink-volume @DEFAULT_SINK@ -1% 19 pactl set-sink-volume @DEFAULT_SINK@ -1%
20``` 20```
21 21
22Commands can also be given as a block in the form *command { <subcommands...>
23}*. Anything before the opening *{* will be prepended to the lines inside the
24block. For example:
25
26```
27output eDP-1 {
28 background ~/wallpaper.png
29 resolution 1920x1080
30}
31```
32
33is identical to
34
35```
36output eDP-1 background ~/wallpaper.png
37output eDP-1 resolution 1920x1080
38```
39
22These commands can be executed in your config file, via *swaymsg*(1), or via 40These commands can be executed in your config file, via *swaymsg*(1), or via
23the bindsym command. 41the bindsym command.
24 42
@@ -37,10 +55,8 @@ which you may only select one. *[...]* is used for optional arguments, and
37 55
38The following commands may only be used in the configuration file. 56The following commands may only be used in the configuration file.
39 57
40*bar {* <commands...> *}* 58*bar* [<bar-id>] <bar-subcommands...>
41 _commands..._ after *{* will be interpreted as bar commands. For 59 For details on bar subcommands, see *sway-bar*(5).
42 details, see *sway-bar*(5). A newline is required between *{* and the
43 first command, and *}* must be alone on a line.
44 60
45*default\_orientation* horizontal|vertical|auto 61*default\_orientation* horizontal|vertical|auto
46 Sets the default container layout for tiled containers. 62 Sets the default container layout for tiled containers.
@@ -51,10 +67,6 @@ The following commands may only be used in the configuration file.
51 *wordexp*(3) for details). The same include file can only be included once; 67 *wordexp*(3) for details). The same include file can only be included once;
52 subsequent attempts will be ignored. 68 subsequent attempts will be ignored.
53 69
54*set* $<name> <value>
55 Sets variable $_name_ to _value_. You can use the new variable in the
56 arguments of future commands.
57
58*swaybg\_command* <command> 70*swaybg\_command* <command>
59 Executes custom background _command_. Default is _swaybg_. Refer to 71 Executes custom background _command_. Default is _swaybg_. Refer to
60 *output* below for more information. 72 *output* below for more information.
@@ -407,37 +419,30 @@ The default colors are:
407 inner gap is nonzero. When _off_, gaps will only be added between views. 419 inner gap is nonzero. When _off_, gaps will only be added between views.
408 _toggle_ cannot be used in the configuration file. 420 _toggle_ cannot be used in the configuration file.
409 421
410*gaps* <amount>
411 Sets _amount_ pixels of gap between windows and around each workspace.
412
413*gaps* inner|outer <amount> 422*gaps* inner|outer <amount>
414 Sets default _amount_ pixels of _inner_ or _outer_ gap, where the former 423 Sets default _amount_ pixels of _inner_ or _outer_ gap, where the inner
415 affects spacing between views and the latter affects the space around each 424 affects spacing around each view and outer affects the spacing around each
416 workspace. 425 workspace. Outer gaps are in addition to inner gaps.
426
427 This affects new workspaces only, and is used when the workspace doesn't
428 have its own gaps settings (see: workspace <ws> gaps inner|outer <amount>).
417 429
418*gaps* inner|outer all|workspace|current set|plus|minus <amount> 430*gaps* inner|outer all|current set|plus|minus <amount>
419 Changes the gaps for the _inner_ or _outer_ gap. _all_ changes the gaps for 431 Changes the _inner_ or _outer_ gaps for either _all_ workspaces or the
420 all views or workspace, _workspace_ changes gaps for all views in current 432 _current_ workspace.
421 workspace (or current workspace), and _current_ changes gaps for the current
422 view or workspace.
423 433
424*hide\_edge\_borders* none|vertical|horizontal|both|smart 434*hide\_edge\_borders* none|vertical|horizontal|both|smart
425 Hides window borders adjacent to the screen edges. Default is _none_. 435 Hides window borders adjacent to the screen edges. Default is _none_.
426 436
427*input* <input\_device> *{* <commands...> *}* 437*input* <input\_device> <input-subcommands...>
428 _commands..._ after *{* will be interpreted as input commands applying to 438 For details on input subcommands, see *sway-input*(5).
429 the specified input device. For details, see *sway-input*(5). A newline is
430 required between *{* and the first command, and *}* must be alone on a
431 line.
432 439
433 \* may be used in lieu of a specific device name to configure all input 440 \* may be used in lieu of a specific device name to configure all input
434 devices. A list of input device names may be obtained via *swaymsg -t 441 devices. A list of input device names may be obtained via *swaymsg -t
435 get\_inputs*. 442 get\_inputs*.
436 443
437*seat* <seat> *{* <commands...> *}* 444*seat* <seat> <seat-subcommands...>
438 _commands..._ after *{* will be interpreted as seat commands applying to 445 For details on seat subcommands, see *sway-input*(5).
439 the specified seat. For details, see *sway-input*(5). A newline is required
440 between *{* and the first command, and *}* must be alone on a line.
441 446
442*seat* <seat> cursor move|set <x> <y> 447*seat* <seat> cursor move|set <x> <y>
443 Move specified seat's cursor relative to current position or wrap to 448 Move specified seat's cursor relative to current position or wrap to
@@ -465,10 +470,8 @@ The default colors are:
465*mode* <mode> 470*mode* <mode>
466 Switches to the specified mode. The default mode _default_. 471 Switches to the specified mode. The default mode _default_.
467 472
468*mode* [--pango\_markup] <mode> *{* <commands...> *}* 473*mode* [--pango\_markup] <mode> <mode-subcommands...>
469 _commands..._ after *{* will be added to the specified mode. A newline is 474 The only two valid _mode-subcommands..._ are *bindsym* and *bindcode*.
470 required between *{* and the first command, and *}* must be alone on a
471 line. Only *bindsym* and *bindcode* commands are permitted in mode blocks.
472 If _--pango\_markup_ is given, then _mode_ will be interpreted as pango 475 If _--pango\_markup_ is given, then _mode_ will be interpreted as pango
473 markup. 476 markup.
474 477
@@ -533,8 +536,15 @@ You may combine output commands into one, like so:
533 output HDMI-A-1 mode 1920x1080 pos 1920,0 bg ~/wallpaper.png stretch 536 output HDMI-A-1 mode 1920x1080 pos 1920,0 bg ~/wallpaper.png stretch
534 537
535You can get a list of output names with *swaymsg -t get\_outputs*. You may also 538You can get a list of output names with *swaymsg -t get\_outputs*. You may also
536match any output by using the output name "\*". Be sure to add this output 539match any output by using the output name "\*".
537config after the others, or it will be matched instead of the others. 540
541*set* $<name> <value>
542 Sets variable $_name_ to _value_. You can use the new variable in the
543 arguments of future commands. When the variable is used, it can be escaped
544 with an additional $ (ie $$_name_) to have the replacement happen at run
545 time instead of when reading the config. However, it does not always make
546 sense for the variable to be replaced at run time since some arguments do
547 need to be known at config time.
538 548
539*show\_marks* yes|no 549*show\_marks* yes|no
540 If *show\_marks* is yes, marks will be displayed in the window borders. 550 If *show\_marks* is yes, marks will be displayed in the window borders.
@@ -568,6 +578,10 @@ config after the others, or it will be matched instead of the others.
568*workspace* back_and_forth 578*workspace* back_and_forth
569 Switches to the previously focused workspace. 579 Switches to the previously focused workspace.
570 580
581*workspace* <name> gaps inner|outer <amount>
582 Specifies that workspace _name_ should have the given gaps settings when it
583 is created.
584
571*workspace* <name> output <output> 585*workspace* <name> output <output>
572 Specifies that workspace _name_ should be shown on the specified _output_. 586 Specifies that workspace _name_ should be shown on the specified _output_.
573 587
diff --git a/sway/tree/arrange.c b/sway/tree/arrange.c
index d50be25d..373460a2 100644
--- a/sway/tree/arrange.c
+++ b/sway/tree/arrange.c
@@ -186,6 +186,7 @@ void arrange_workspace(struct sway_workspace *workspace) {
186 area->width, area->height, area->x, area->y); 186 area->width, area->height, area->x, area->y);
187 workspace_remove_gaps(workspace); 187 workspace_remove_gaps(workspace);
188 188
189 bool first_arrange = workspace->width == 0 && workspace->height == 0;
189 double prev_x = workspace->x; 190 double prev_x = workspace->x;
190 double prev_y = workspace->y; 191 double prev_y = workspace->y;
191 workspace->width = area->width; 192 workspace->width = area->width;
@@ -196,7 +197,7 @@ void arrange_workspace(struct sway_workspace *workspace) {
196 // Adjust any floating containers 197 // Adjust any floating containers
197 double diff_x = workspace->x - prev_x; 198 double diff_x = workspace->x - prev_x;
198 double diff_y = workspace->y - prev_y; 199 double diff_y = workspace->y - prev_y;
199 if (diff_x != 0 || diff_y != 0) { 200 if (!first_arrange && (diff_x != 0 || diff_y != 0)) {
200 for (int i = 0; i < workspace->floating->length; ++i) { 201 for (int i = 0; i < workspace->floating->length; ++i) {
201 struct sway_container *floater = workspace->floating->items[i]; 202 struct sway_container *floater = workspace->floating->items[i];
202 container_floating_translate(floater, diff_x, diff_y); 203 container_floating_translate(floater, diff_x, diff_y);
diff --git a/sway/tree/container.c b/sway/tree/container.c
index 47687744..788300cc 100644
--- a/sway/tree/container.c
+++ b/sway/tree/container.c
@@ -67,12 +67,10 @@ void container_destroy(struct sway_container *con) {
67 list_free(con->outputs); 67 list_free(con->outputs);
68 68
69 if (con->view) { 69 if (con->view) {
70 struct sway_view *view = con->view; 70 if (con->view->container == con) {
71 view->container = NULL; 71 con->view->container = NULL;
72 free(view->title_format); 72 }
73 view->title_format = NULL; 73 if (con->view->destroying) {
74
75 if (view->destroying) {
76 view_destroy(con->view); 74 view_destroy(con->view);
77 } 75 }
78 } 76 }
@@ -215,8 +213,7 @@ static struct sway_container *container_at_tabbed(struct sway_node *parent,
215 child_index = children->length - 1; 213 child_index = children->length - 1;
216 } 214 }
217 struct sway_container *child = children->items[child_index]; 215 struct sway_container *child = children->items[child_index];
218 struct sway_node *node = seat_get_focus_inactive(seat, &child->node); 216 return child;
219 return node->sway_container;
220 } 217 }
221 218
222 // Surfaces 219 // Surfaces
@@ -243,8 +240,7 @@ static struct sway_container *container_at_stacked(struct sway_node *parent,
243 int child_index = (ly - box.y) / title_height; 240 int child_index = (ly - box.y) / title_height;
244 if (child_index < children->length) { 241 if (child_index < children->length) {
245 struct sway_container *child = children->items[child_index]; 242 struct sway_container *child = children->items[child_index];
246 struct sway_node *node = seat_get_focus_inactive(seat, &child->node); 243 return child;
247 return node->sway_container;
248 } 244 }
249 245
250 // Surfaces 246 // Surfaces
@@ -465,11 +461,17 @@ static void update_title_texture(struct sway_container *con,
465 cairo_surface_t *surface = cairo_image_surface_create( 461 cairo_surface_t *surface = cairo_image_surface_create(
466 CAIRO_FORMAT_ARGB32, width, height); 462 CAIRO_FORMAT_ARGB32, width, height);
467 cairo_t *cairo = cairo_create(surface); 463 cairo_t *cairo = cairo_create(surface);
464 cairo_set_antialias(cairo, CAIRO_ANTIALIAS_BEST);
465 cairo_font_options_t *fo = cairo_font_options_create();
466 cairo_font_options_set_hint_style(fo, CAIRO_HINT_STYLE_FULL);
467 cairo_font_options_set_antialias(fo, CAIRO_ANTIALIAS_SUBPIXEL);
468 cairo_font_options_set_subpixel_order(fo, to_cairo_subpixel_order(output->wlr_output->subpixel));
469 cairo_set_font_options(cairo, fo);
470 cairo_font_options_destroy(fo);
468 cairo_set_source_rgba(cairo, class->background[0], class->background[1], 471 cairo_set_source_rgba(cairo, class->background[0], class->background[1],
469 class->background[2], class->background[3]); 472 class->background[2], class->background[3]);
470 cairo_paint(cairo); 473 cairo_paint(cairo);
471 PangoContext *pango = pango_cairo_create_context(cairo); 474 PangoContext *pango = pango_cairo_create_context(cairo);
472 cairo_set_antialias(cairo, CAIRO_ANTIALIAS_BEST);
473 cairo_set_source_rgba(cairo, class->text[0], class->text[1], 475 cairo_set_source_rgba(cairo, class->text[0], class->text[1],
474 class->text[2], class->text[3]); 476 class->text[2], class->text[3]);
475 cairo_move_to(cairo, 0, 0); 477 cairo_move_to(cairo, 0, 0);
@@ -591,7 +593,7 @@ void container_update_representation(struct sway_container *con) {
591 } 593 }
592} 594}
593 595
594size_t container_titlebar_height() { 596size_t container_titlebar_height(void) {
595 return config->font_height + TITLEBAR_V_PADDING * 2; 597 return config->font_height + TITLEBAR_V_PADDING * 2;
596} 598}
597 599
@@ -821,9 +823,16 @@ void container_floating_move_to_center(struct sway_container *con) {
821 return; 823 return;
822 } 824 }
823 struct sway_workspace *ws = con->workspace; 825 struct sway_workspace *ws = con->workspace;
826 bool full = con->is_fullscreen;
827 if (full) {
828 container_set_fullscreen(con, false);
829 }
824 double new_lx = ws->x + (ws->width - con->width) / 2; 830 double new_lx = ws->x + (ws->width - con->width) / 2;
825 double new_ly = ws->y + (ws->height - con->height) / 2; 831 double new_ly = ws->y + (ws->height - con->height) / 2;
826 container_floating_translate(con, new_lx - con->x, new_ly - con->y); 832 container_floating_translate(con, new_lx - con->x, new_ly - con->y);
833 if (full) {
834 container_set_fullscreen(con, true);
835 }
827} 836}
828 837
829static bool find_urgent_iterator(struct sway_container *con, void *data) { 838static bool find_urgent_iterator(struct sway_container *con, void *data) {
@@ -981,7 +990,8 @@ void container_discover_outputs(struct sway_container *con) {
981 } 990 }
982 } 991 }
983 struct sway_output *new_output = container_get_effective_output(con); 992 struct sway_output *new_output = container_get_effective_output(con);
984 double old_scale = old_output ? old_output->wlr_output->scale : -1; 993 double old_scale = old_output && old_output->enabled ?
994 old_output->wlr_output->scale : -1;
985 double new_scale = new_output ? new_output->wlr_output->scale : -1; 995 double new_scale = new_output ? new_output->wlr_output->scale : -1;
986 if (old_scale != new_scale) { 996 if (old_scale != new_scale) {
987 container_update_title_textures(con); 997 container_update_title_textures(con);
@@ -1019,7 +1029,7 @@ void container_add_gaps(struct sway_container *c) {
1019 1029
1020 struct sway_workspace *ws = c->workspace; 1030 struct sway_workspace *ws = c->workspace;
1021 1031
1022 c->current_gaps = ws->has_gaps ? ws->gaps_inner : config->gaps_inner; 1032 c->current_gaps = ws->gaps_inner;
1023 c->x += c->current_gaps; 1033 c->x += c->current_gaps;
1024 c->y += c->current_gaps; 1034 c->y += c->current_gaps;
1025 c->width -= 2 * c->current_gaps; 1035 c->width -= 2 * c->current_gaps;
diff --git a/sway/tree/output.c b/sway/tree/output.c
index 1976ad51..c3176325 100644
--- a/sway/tree/output.c
+++ b/sway/tree/output.c
@@ -109,12 +109,24 @@ void output_enable(struct sway_output *output, struct output_config *oc) {
109 109
110 wl_signal_emit(&root->events.new_node, &output->node); 110 wl_signal_emit(&root->events.new_node, &output->node);
111 111
112 load_swaybars();
113
114 arrange_layers(output); 112 arrange_layers(output);
115 arrange_root(); 113 arrange_root();
116} 114}
117 115
116static void evacuate_sticky(struct sway_workspace *old_ws,
117 struct sway_output *new_output) {
118 struct sway_workspace *new_ws = output_get_active_workspace(new_output);
119 while (old_ws->floating->length) {
120 struct sway_container *sticky = old_ws->floating->items[0];
121 container_detach(sticky);
122 workspace_add_floating(new_ws, sticky);
123 container_handle_fullscreen_reparent(sticky);
124 container_floating_move_to_center(sticky);
125 ipc_event_window(sticky, "move");
126 }
127 workspace_detect_urgent(new_ws);
128}
129
118static void output_evacuate(struct sway_output *output) { 130static void output_evacuate(struct sway_output *output) {
119 if (!output->workspaces->length) { 131 if (!output->workspaces->length) {
120 return; 132 return;
@@ -132,17 +144,21 @@ static void output_evacuate(struct sway_output *output) {
132 144
133 workspace_detach(workspace); 145 workspace_detach(workspace);
134 146
135 if (workspace_is_empty(workspace)) {
136 workspace_begin_destroy(workspace);
137 continue;
138 }
139
140 struct sway_output *new_output = 147 struct sway_output *new_output =
141 workspace_output_get_highest_available(workspace, output); 148 workspace_output_get_highest_available(workspace, output);
142 if (!new_output) { 149 if (!new_output) {
143 new_output = fallback_output; 150 new_output = fallback_output;
144 } 151 }
145 152
153 if (workspace_is_empty(workspace)) {
154 // If floating is not empty, there are sticky containers to move
155 if (workspace->floating->length) {
156 evacuate_sticky(workspace, new_output);
157 }
158 workspace_begin_destroy(workspace);
159 continue;
160 }
161
146 if (new_output) { 162 if (new_output) {
147 workspace_output_add_priority(workspace, new_output); 163 workspace_output_add_priority(workspace, new_output);
148 output_add_workspace(new_output, workspace); 164 output_add_workspace(new_output, workspace);
diff --git a/sway/tree/root.c b/sway/tree/root.c
index ecc04ddb..6748e9c9 100644
--- a/sway/tree/root.c
+++ b/sway/tree/root.c
@@ -265,14 +265,20 @@ void root_for_each_container(void (*f)(struct sway_container *con, void *data),
265 // Scratchpad 265 // Scratchpad
266 for (int i = 0; i < root->scratchpad->length; ++i) { 266 for (int i = 0; i < root->scratchpad->length; ++i) {
267 struct sway_container *container = root->scratchpad->items[i]; 267 struct sway_container *container = root->scratchpad->items[i];
268 // If the container has a parent then it's visible on a workspace 268 // If the container has a workspace then it's visible on a workspace
269 // and will have been iterated in the previous for loop. So we only 269 // and will have been iterated in the previous for loop. So we only
270 // iterate the hidden scratchpad containers here. 270 // iterate the hidden scratchpad containers here.
271 if (!container->parent) { 271 if (!container->workspace) {
272 f(container, data); 272 f(container, data);
273 container_for_each_child(container, f, data); 273 container_for_each_child(container, f, data);
274 } 274 }
275 } 275 }
276
277 // Saved workspaces
278 for (int i = 0; i < root->saved_workspaces->length; ++i) {
279 struct sway_workspace *ws = root->saved_workspaces->items[i];
280 workspace_for_each_container(ws, f, data);
281 }
276} 282}
277 283
278struct sway_output *root_find_output( 284struct sway_output *root_find_output(
@@ -311,7 +317,7 @@ struct sway_container *root_find_container(
311 // Scratchpad 317 // Scratchpad
312 for (int i = 0; i < root->scratchpad->length; ++i) { 318 for (int i = 0; i < root->scratchpad->length; ++i) {
313 struct sway_container *container = root->scratchpad->items[i]; 319 struct sway_container *container = root->scratchpad->items[i];
314 if (!container->parent) { 320 if (!container->workspace) {
315 if (test(container, data)) { 321 if (test(container, data)) {
316 return container; 322 return container;
317 } 323 }
@@ -320,6 +326,15 @@ struct sway_container *root_find_container(
320 } 326 }
321 } 327 }
322 } 328 }
329
330 // Saved workspaces
331 for (int i = 0; i < root->saved_workspaces->length; ++i) {
332 struct sway_workspace *ws = root->saved_workspaces->items[i];
333 if ((result = workspace_find_container(ws, test, data))) {
334 return result;
335 }
336 }
337
323 return NULL; 338 return NULL;
324} 339}
325 340
diff --git a/sway/tree/view.c b/sway/tree/view.c
index 4398f518..a024f325 100644
--- a/sway/tree/view.c
+++ b/sway/tree/view.c
@@ -391,8 +391,6 @@ static bool view_has_executed_criteria(struct sway_view *view,
391} 391}
392 392
393void view_execute_criteria(struct sway_view *view) { 393void view_execute_criteria(struct sway_view *view) {
394 struct sway_seat *seat = input_manager_current_seat(input_manager);
395 struct sway_node *prior_focus = seat_get_focus(seat);
396 list_t *criterias = criteria_for_view(view, CT_COMMAND); 394 list_t *criterias = criteria_for_view(view, CT_COMMAND);
397 for (int i = 0; i < criterias->length; i++) { 395 for (int i = 0; i < criterias->length; i++) {
398 struct criteria *criteria = criterias->items[i]; 396 struct criteria *criteria = criterias->items[i];
@@ -403,16 +401,12 @@ void view_execute_criteria(struct sway_view *view) {
403 } 401 }
404 wlr_log(WLR_DEBUG, "for_window '%s' matches view %p, cmd: '%s'", 402 wlr_log(WLR_DEBUG, "for_window '%s' matches view %p, cmd: '%s'",
405 criteria->raw, view, criteria->cmdlist); 403 criteria->raw, view, criteria->cmdlist);
406 seat_set_focus_container(seat, view->container);
407 list_add(view->executed_criteria, criteria); 404 list_add(view->executed_criteria, criteria);
408 struct cmd_results *res = execute_command(criteria->cmdlist, NULL); 405 struct cmd_results *res = execute_command(
409 if (res->status != CMD_SUCCESS) { 406 criteria->cmdlist, NULL, view->container);
410 wlr_log(WLR_ERROR, "Command '%s' failed: %s", res->input, res->error);
411 }
412 free_cmd_results(res); 407 free_cmd_results(res);
413 } 408 }
414 list_free(criterias); 409 list_free(criterias);
415 seat_set_focus(seat, prior_focus);
416} 410}
417 411
418static struct sway_workspace *select_workspace(struct sway_view *view) { 412static struct sway_workspace *select_workspace(struct sway_view *view) {
@@ -785,14 +779,9 @@ static size_t parse_title_format(struct sway_view *view, char *buffer) {
785} 779}
786 780
787static char *escape_title(char *buffer) { 781static char *escape_title(char *buffer) {
788 int length = escape_markup_text(buffer, NULL, 0); 782 size_t length = escape_markup_text(buffer, NULL);
789 char *escaped_title = calloc(length + 1, sizeof(char)); 783 char *escaped_title = calloc(length + 1, sizeof(char));
790 int result = escape_markup_text(buffer, escaped_title, length); 784 escape_markup_text(buffer, escaped_title);
791 if (result != length) {
792 wlr_log(WLR_ERROR, "Could not escape title: %s", buffer);
793 free(escaped_title);
794 return buffer;
795 }
796 free(buffer); 785 free(buffer);
797 return escaped_title; 786 return escaped_title;
798} 787}
@@ -1000,12 +989,16 @@ bool view_is_visible(struct sway_view *view) {
1000 floater = floater->parent; 989 floater = floater->parent;
1001 } 990 }
1002 bool is_sticky = container_is_floating(floater) && floater->is_sticky; 991 bool is_sticky = container_is_floating(floater) && floater->is_sticky;
992 if (!is_sticky && !workspace_is_visible(workspace)) {
993 return false;
994 }
1003 // Check view isn't in a tabbed or stacked container on an inactive tab 995 // Check view isn't in a tabbed or stacked container on an inactive tab
1004 struct sway_seat *seat = input_manager_current_seat(input_manager); 996 struct sway_seat *seat = input_manager_current_seat(input_manager);
1005 struct sway_container *con = view->container; 997 struct sway_container *con = view->container;
1006 while (con) { 998 while (con) {
1007 enum sway_container_layout layout = container_parent_layout(con); 999 enum sway_container_layout layout = container_parent_layout(con);
1008 if (layout == L_TABBED || layout == L_STACKED) { 1000 if ((layout == L_TABBED || layout == L_STACKED)
1001 && !container_is_floating(con)) {
1009 struct sway_node *parent = con->parent ? 1002 struct sway_node *parent = con->parent ?
1010 &con->parent->node : &con->workspace->node; 1003 &con->parent->node : &con->workspace->node;
1011 if (seat_get_active_tiling_child(seat, parent) != &con->node) { 1004 if (seat_get_active_tiling_child(seat, parent) != &con->node) {
@@ -1019,10 +1012,6 @@ bool view_is_visible(struct sway_view *view) {
1019 !container_is_fullscreen_or_child(view->container)) { 1012 !container_is_fullscreen_or_child(view->container)) {
1020 return false; 1013 return false;
1021 } 1014 }
1022 // Check the workspace is visible
1023 if (!is_sticky) {
1024 return workspace_is_visible(workspace);
1025 }
1026 return true; 1015 return true;
1027} 1016}
1028 1017
diff --git a/sway/tree/workspace.c b/sway/tree/workspace.c
index 18746430..9dd5c815 100644
--- a/sway/tree/workspace.c
+++ b/sway/tree/workspace.c
@@ -20,17 +20,23 @@
20#include "log.h" 20#include "log.h"
21#include "util.h" 21#include "util.h"
22 22
23struct workspace_config *workspace_find_config(const char *ws_name) {
24 for (int i = 0; i < config->workspace_configs->length; ++i) {
25 struct workspace_config *wsc = config->workspace_configs->items[i];
26 if (strcmp(wsc->workspace, ws_name) == 0) {
27 return wsc;
28 }
29 }
30 return NULL;
31}
32
23struct sway_output *workspace_get_initial_output(const char *name) { 33struct sway_output *workspace_get_initial_output(const char *name) {
24 // Search for workspace<->output pair 34 // Check workspace configs for a workspace<->output pair
25 for (int i = 0; i < config->workspace_outputs->length; ++i) { 35 struct workspace_config *wsc = workspace_find_config(name);
26 struct workspace_output *wso = config->workspace_outputs->items[i]; 36 if (wsc && wsc->output) {
27 if (strcasecmp(wso->workspace, name) == 0) { 37 struct sway_output *output = output_by_name(wsc->output);
28 // Find output to use if it exists 38 if (output) {
29 struct sway_output *output = output_by_name(wso->output); 39 return output;
30 if (output) {
31 return output;
32 }
33 break;
34 } 40 }
35 } 41 }
36 // Otherwise put it on the focused output 42 // Otherwise put it on the focused output
@@ -54,10 +60,6 @@ struct sway_workspace *workspace_create(struct sway_output *output,
54 return NULL; 60 return NULL;
55 } 61 }
56 node_init(&ws->node, N_WORKSPACE, ws); 62 node_init(&ws->node, N_WORKSPACE, ws);
57 ws->x = output->lx;
58 ws->y = output->ly;
59 ws->width = output->width;
60 ws->height = output->height;
61 ws->name = name ? strdup(name) : NULL; 63 ws->name = name ? strdup(name) : NULL;
62 ws->prev_split_layout = L_NONE; 64 ws->prev_split_layout = L_NONE;
63 ws->layout = output_get_default_layout(output); 65 ws->layout = output_get_default_layout(output);
@@ -66,6 +68,20 @@ struct sway_workspace *workspace_create(struct sway_output *output,
66 ws->output_priority = create_list(); 68 ws->output_priority = create_list();
67 workspace_output_add_priority(ws, output); 69 workspace_output_add_priority(ws, output);
68 70
71 ws->gaps_outer = config->gaps_outer;
72 ws->gaps_inner = config->gaps_inner;
73 if (name) {
74 struct workspace_config *wsc = workspace_find_config(name);
75 if (wsc) {
76 if (wsc->gaps_outer != -1) {
77 ws->gaps_outer = wsc->gaps_outer;
78 }
79 if (wsc->gaps_inner != -1) {
80 ws->gaps_inner = wsc->gaps_inner;
81 }
82 }
83 }
84
69 output_add_workspace(output, ws); 85 output_add_workspace(output, ws);
70 output_sort_workspaces(output); 86 output_sort_workspaces(output);
71 87
@@ -125,17 +141,8 @@ void next_name_map(struct sway_container *ws, void *data) {
125 141
126static bool workspace_valid_on_output(const char *output_name, 142static bool workspace_valid_on_output(const char *output_name,
127 const char *ws_name) { 143 const char *ws_name) {
128 int i; 144 struct workspace_config *wsc = workspace_find_config(ws_name);
129 for (i = 0; i < config->workspace_outputs->length; ++i) { 145 return !wsc || !wsc->output || strcmp(wsc->output, output_name) == 0;
130 struct workspace_output *wso = config->workspace_outputs->items[i];
131 if (strcasecmp(wso->workspace, ws_name) == 0) {
132 if (strcasecmp(wso->output, output_name) != 0) {
133 return false;
134 }
135 }
136 }
137
138 return true;
139} 146}
140 147
141static void workspace_name_from_binding(const struct sway_binding * binding, 148static void workspace_name_from_binding(const struct sway_binding * binding,
@@ -235,13 +242,13 @@ char *workspace_next_name(const char *output_name) {
235 workspace_name_from_binding(mode->keycode_bindings->items[i], 242 workspace_name_from_binding(mode->keycode_bindings->items[i],
236 output_name, &order, &target); 243 output_name, &order, &target);
237 } 244 }
238 for (int i = 0; i < config->workspace_outputs->length; ++i) { 245 for (int i = 0; i < config->workspace_configs->length; ++i) {
239 // Unlike with bindings, this does not guarantee order 246 // Unlike with bindings, this does not guarantee order
240 const struct workspace_output *wso = config->workspace_outputs->items[i]; 247 const struct workspace_config *wsc = config->workspace_configs->items[i];
241 if (strcmp(wso->output, output_name) == 0 248 if (wsc->output && strcmp(wsc->output, output_name) == 0
242 && workspace_by_name(wso->workspace) == NULL) { 249 && workspace_by_name(wsc->workspace) == NULL) {
243 free(target); 250 free(target);
244 target = strdup(wso->workspace); 251 target = strdup(wsc->workspace);
245 break; 252 break;
246 } 253 }
247 } 254 }
@@ -389,13 +396,11 @@ bool workspace_switch(struct sway_workspace *workspace,
389 struct sway_output *next_output = workspace->output; 396 struct sway_output *next_output = workspace->output;
390 struct sway_workspace *next_output_prev_ws = 397 struct sway_workspace *next_output_prev_ws =
391 output_get_active_workspace(next_output); 398 output_get_active_workspace(next_output);
392 bool has_sticky = false;
393 if (workspace != next_output_prev_ws) { 399 if (workspace != next_output_prev_ws) {
394 for (int i = 0; i < next_output_prev_ws->floating->length; ++i) { 400 for (int i = 0; i < next_output_prev_ws->floating->length; ++i) {
395 struct sway_container *floater = 401 struct sway_container *floater =
396 next_output_prev_ws->floating->items[i]; 402 next_output_prev_ws->floating->items[i];
397 if (floater->is_sticky) { 403 if (floater->is_sticky) {
398 has_sticky = true;
399 container_detach(floater); 404 container_detach(floater);
400 workspace_add_floating(workspace, floater); 405 workspace_add_floating(workspace, floater);
401 if (&floater->node == focus) { 406 if (&floater->node == focus) {
@@ -414,14 +419,6 @@ bool workspace_switch(struct sway_workspace *workspace,
414 if (next == NULL) { 419 if (next == NULL) {
415 next = &workspace->node; 420 next = &workspace->node;
416 } 421 }
417 if (has_sticky) {
418 // If there's a sticky container, we might be setting focus to the same
419 // container that's already focused, so seat_set_focus is effectively a
420 // no op. We therefore need to send the IPC event and clean up the old
421 // workspace here.
422 ipc_event_workspace(active_ws, workspace, "focus");
423 workspace_consider_destroy(active_ws);
424 }
425 seat_set_focus(seat, next); 422 seat_set_focus(seat, next);
426 arrange_workspace(workspace); 423 arrange_workspace(workspace);
427 cursor_send_pointer_motion(seat->cursor, 0, true); 424 cursor_send_pointer_motion(seat->cursor, 0, true);
@@ -649,13 +646,13 @@ void workspace_add_gaps(struct sway_workspace *ws) {
649 return; 646 return;
650 } 647 }
651 648
652 ws->current_gaps = ws->has_gaps ? ws->gaps_outer : config->gaps_outer; 649 ws->current_gaps = ws->gaps_outer;
653 650
654 if (ws->layout == L_TABBED || ws->layout == L_STACKED) { 651 if (ws->layout == L_TABBED || ws->layout == L_STACKED) {
655 // We have to add inner gaps for this, because children of tabbed and 652 // We have to add inner gaps for this, because children of tabbed and
656 // stacked containers don't apply their own gaps - they assume the 653 // stacked containers don't apply their own gaps - they assume the
657 // tabbed/stacked container is using gaps. 654 // tabbed/stacked container is using gaps.
658 ws->current_gaps += ws->has_gaps ? ws->gaps_inner : config->gaps_inner; 655 ws->current_gaps += ws->gaps_inner;
659 } 656 }
660 657
661 ws->x += ws->current_gaps; 658 ws->x += ws->current_gaps;