aboutsummaryrefslogtreecommitdiffstats
path: root/sway
diff options
context:
space:
mode:
Diffstat (limited to 'sway')
-rw-r--r--sway/commands.c106
-rw-r--r--sway/commands/assign.c2
-rw-r--r--sway/commands/bar.c9
-rw-r--r--sway/commands/bar/activate_button.c8
-rw-r--r--sway/commands/bar/bind.c106
-rw-r--r--sway/commands/bar/bindsym.c69
-rw-r--r--sway/commands/bar/colors.c4
-rw-r--r--sway/commands/bar/context_button.c8
-rw-r--r--sway/commands/bar/gaps.c60
-rw-r--r--sway/commands/bar/hidden_state.c2
-rw-r--r--sway/commands/bar/icon_theme.c26
-rw-r--r--sway/commands/bar/id.c2
-rw-r--r--sway/commands/bar/mode.c2
-rw-r--r--sway/commands/bar/modifier.c5
-rw-r--r--sway/commands/bar/output.c2
-rw-r--r--sway/commands/bar/secondary_button.c8
-rw-r--r--sway/commands/bar/separator_symbol.c2
-rw-r--r--sway/commands/bar/status_edge_padding.c21
-rw-r--r--sway/commands/bar/status_padding.c21
-rw-r--r--sway/commands/bar/strip_workspace_name.c32
-rw-r--r--sway/commands/bar/strip_workspace_numbers.c17
-rw-r--r--sway/commands/bar/tray_bindsym.c55
-rw-r--r--sway/commands/bar/tray_output.c40
-rw-r--r--sway/commands/bar/tray_padding.c37
-rw-r--r--sway/commands/bind.c167
-rw-r--r--sway/commands/exec_always.c2
-rw-r--r--sway/commands/focus.c5
-rw-r--r--sway/commands/for_window.c1
-rw-r--r--sway/commands/fullscreen.c4
-rw-r--r--sway/commands/input/events.c76
-rw-r--r--sway/commands/input/scroll_button.c34
-rw-r--r--sway/commands/input/xkb_layout.c2
-rw-r--r--sway/commands/input/xkb_model.c2
-rw-r--r--sway/commands/input/xkb_options.c2
-rw-r--r--sway/commands/input/xkb_rules.c2
-rw-r--r--sway/commands/input/xkb_variant.c2
-rw-r--r--sway/commands/mode.c2
-rw-r--r--sway/commands/move.c7
-rw-r--r--sway/commands/no_focus.c1
-rw-r--r--sway/commands/output/background.c7
-rw-r--r--sway/commands/output/transform.c2
-rw-r--r--sway/commands/reload.c9
-rw-r--r--sway/commands/rename.c9
-rw-r--r--sway/commands/resize.c28
-rw-r--r--sway/commands/seat.c16
-rw-r--r--sway/commands/seat/attach.c23
-rw-r--r--sway/commands/seat/cursor.c96
-rw-r--r--sway/commands/seat/fallback.c19
-rw-r--r--sway/commands/seat/hide_cursor.c29
-rw-r--r--sway/commands/set.c2
-rw-r--r--sway/commands/split.c4
-rw-r--r--sway/commands/swap.c2
-rw-r--r--sway/commands/tiling_drag_threshold.c22
-rw-r--r--sway/commands/title_align.c30
-rw-r--r--sway/commands/titlebar_border_thickness.c30
-rw-r--r--sway/commands/titlebar_padding.c42
-rw-r--r--sway/commands/workspace.c6
-rw-r--r--sway/config.c223
-rw-r--r--sway/config/bar.c22
-rw-r--r--sway/config/input.c2
-rw-r--r--sway/config/output.c114
-rw-r--r--sway/config/seat.c64
-rw-r--r--sway/criteria.c2
-rw-r--r--sway/desktop/output.c30
-rw-r--r--sway/desktop/render.c217
-rw-r--r--sway/desktop/transaction.c6
-rw-r--r--sway/desktop/xdg_shell.c4
-rw-r--r--sway/desktop/xdg_shell_v6.c4
-rw-r--r--sway/desktop/xwayland.c4
-rw-r--r--sway/input/cursor.c631
-rw-r--r--sway/input/input-manager.c227
-rw-r--r--sway/input/keyboard.c13
-rw-r--r--sway/input/seat.c297
-rw-r--r--sway/input/seatop_down.c77
-rw-r--r--sway/input/seatop_move_floating.c65
-rw-r--r--sway/input/seatop_move_tiling.c335
-rw-r--r--sway/input/seatop_resize_floating.c199
-rw-r--r--sway/input/seatop_resize_tiling.c92
-rw-r--r--sway/ipc-json.c359
-rw-r--r--sway/ipc-server.c68
-rw-r--r--sway/main.c83
-rw-r--r--sway/meson.build21
-rw-r--r--sway/security.c2
-rw-r--r--sway/server.c10
-rw-r--r--sway/sway-bar.5.scd64
-rw-r--r--sway/sway-input.5.scd37
-rw-r--r--sway/sway-output.5.scd22
-rw-r--r--sway/sway.1.scd10
-rw-r--r--sway/sway.5.scd64
-rw-r--r--sway/tree/container.c52
-rw-r--r--sway/tree/output.c18
-rw-r--r--sway/tree/root.c46
-rw-r--r--sway/tree/view.c109
-rw-r--r--sway/tree/workspace.c44
94 files changed, 3444 insertions, 1522 deletions
diff --git a/sway/commands.c b/sway/commands.c
index a68c724a..1d190e0b 100644
--- a/sway/commands.c
+++ b/sway/commands.c
@@ -42,22 +42,6 @@ struct cmd_results *checkarg(int argc, const char *name, enum expected_args type
42 : NULL; 42 : NULL;
43} 43}
44 44
45void apply_seat_config(struct seat_config *seat_config) {
46 int i;
47 i = list_seq_find(config->seat_configs, seat_name_cmp, seat_config->name);
48 if (i >= 0) {
49 // merge existing config
50 struct seat_config *sc = config->seat_configs->items[i];
51 merge_seat_config(sc, seat_config);
52 free_seat_config(seat_config);
53 seat_config = sc;
54 } else {
55 list_add(config->seat_configs, seat_config);
56 }
57
58 input_manager_apply_seat_config(seat_config);
59}
60
61/* Keep alphabetized */ 45/* Keep alphabetized */
62static struct cmd_handler handlers[] = { 46static struct cmd_handler handlers[] = {
63 { "assign", cmd_assign }, 47 { "assign", cmd_assign },
@@ -103,6 +87,10 @@ static struct cmd_handler handlers[] = {
103 { "smart_borders", cmd_smart_borders }, 87 { "smart_borders", cmd_smart_borders },
104 { "smart_gaps", cmd_smart_gaps }, 88 { "smart_gaps", cmd_smart_gaps },
105 { "tiling_drag", cmd_tiling_drag }, 89 { "tiling_drag", cmd_tiling_drag },
90 { "tiling_drag_threshold", cmd_tiling_drag_threshold },
91 { "title_align", cmd_title_align },
92 { "titlebar_border_thickness", cmd_titlebar_border_thickness },
93 { "titlebar_padding", cmd_titlebar_padding },
106 { "workspace", cmd_workspace }, 94 { "workspace", cmd_workspace },
107 { "workspace_auto_back_and_forth", cmd_ws_auto_back_and_forth }, 95 { "workspace_auto_back_and_forth", cmd_ws_auto_back_and_forth },
108}; 96};
@@ -213,12 +201,9 @@ static void set_config_node(struct sway_node *node) {
213 } 201 }
214} 202}
215 203
216struct cmd_results *execute_command(char *_exec, struct sway_seat *seat, 204list_t *execute_command(char *_exec, struct sway_seat *seat,
217 struct sway_container *con) { 205 struct sway_container *con) {
218 // Even though this function will process multiple commands we will only 206 list_t *res_list = create_list();
219 // return the last error, if any (for now). (Since we have access to an
220 // error string we could e.g. concatenate all errors there.)
221 struct cmd_results *results = NULL;
222 char *exec = strdup(_exec); 207 char *exec = strdup(_exec);
223 char *head = exec; 208 char *head = exec;
224 char *cmdlist; 209 char *cmdlist;
@@ -233,15 +218,6 @@ struct cmd_results *execute_command(char *_exec, struct sway_seat *seat,
233 } 218 }
234 } 219 }
235 220
236 // This is the container or workspace which this command will run on.
237 // Ignored if the command string contains criteria.
238 struct sway_node *node;
239 if (con) {
240 node = &con->node;
241 } else {
242 node = seat_get_focus_inactive(seat, &root->node);
243 }
244
245 config->handler_context.seat = seat; 221 config->handler_context.seat = seat;
246 222
247 head = exec; 223 head = exec;
@@ -252,8 +228,8 @@ struct cmd_results *execute_command(char *_exec, struct sway_seat *seat,
252 char *error = NULL; 228 char *error = NULL;
253 struct criteria *criteria = criteria_parse(head, &error); 229 struct criteria *criteria = criteria_parse(head, &error);
254 if (!criteria) { 230 if (!criteria) {
255 results = cmd_results_new(CMD_INVALID, head, 231 list_add(res_list, cmd_results_new(CMD_INVALID, head,
256 "%s", error); 232 "%s", error));
257 free(error); 233 free(error);
258 goto cleanup; 234 goto cleanup;
259 } 235 }
@@ -262,15 +238,15 @@ struct cmd_results *execute_command(char *_exec, struct sway_seat *seat,
262 criteria_destroy(criteria); 238 criteria_destroy(criteria);
263 config->handler_context.using_criteria = true; 239 config->handler_context.using_criteria = true;
264 // Skip leading whitespace 240 // Skip leading whitespace
265 head += strspn(head, whitespace); 241 for (; isspace(*head); ++head) {}
266 } 242 }
267 // Split command list 243 // Split command list
268 cmdlist = argsep(&head, ";"); 244 cmdlist = argsep(&head, ";");
269 cmdlist += strspn(cmdlist, whitespace); 245 for (; isspace(*cmdlist); ++cmdlist) {}
270 do { 246 do {
271 // Split commands 247 // Split commands
272 cmd = argsep(&cmdlist, ","); 248 cmd = argsep(&cmdlist, ",");
273 cmd += strspn(cmd, whitespace); 249 for (; isspace(*cmd); ++cmd) {}
274 if (strcmp(cmd, "") == 0) { 250 if (strcmp(cmd, "") == 0) {
275 wlr_log(WLR_INFO, "Ignoring empty command."); 251 wlr_log(WLR_INFO, "Ignoring empty command.");
276 continue; 252 continue;
@@ -289,10 +265,8 @@ struct cmd_results *execute_command(char *_exec, struct sway_seat *seat,
289 } 265 }
290 struct cmd_handler *handler = find_handler(argv[0], NULL, 0); 266 struct cmd_handler *handler = find_handler(argv[0], NULL, 0);
291 if (!handler) { 267 if (!handler) {
292 if (results) { 268 list_add(res_list, cmd_results_new(CMD_INVALID, cmd,
293 free_cmd_results(results); 269 "Unknown/invalid command"));
294 }
295 results = cmd_results_new(CMD_INVALID, cmd, "Unknown/invalid command");
296 free_argv(argc, argv); 270 free_argv(argc, argv);
297 goto cleanup; 271 goto cleanup;
298 } 272 }
@@ -304,31 +278,26 @@ struct cmd_results *execute_command(char *_exec, struct sway_seat *seat,
304 } 278 }
305 279
306 if (!config->handler_context.using_criteria) { 280 if (!config->handler_context.using_criteria) {
281 // The container or workspace which this command will run on.
282 struct sway_node *node = con ? &con->node :
283 seat_get_focus_inactive(seat, &root->node);
307 set_config_node(node); 284 set_config_node(node);
308 struct cmd_results *res = handler->handle(argc-1, argv+1); 285 struct cmd_results *res = handler->handle(argc-1, argv+1);
309 if (res->status != CMD_SUCCESS) { 286 list_add(res_list, res);
287 if (res->status == CMD_INVALID) {
310 free_argv(argc, argv); 288 free_argv(argc, argv);
311 if (results) {
312 free_cmd_results(results);
313 }
314 results = res;
315 goto cleanup; 289 goto cleanup;
316 } 290 }
317 free_cmd_results(res);
318 } else { 291 } else {
319 for (int i = 0; i < views->length; ++i) { 292 for (int i = 0; i < views->length; ++i) {
320 struct sway_view *view = views->items[i]; 293 struct sway_view *view = views->items[i];
321 set_config_node(&view->container->node); 294 set_config_node(&view->container->node);
322 struct cmd_results *res = handler->handle(argc-1, argv+1); 295 struct cmd_results *res = handler->handle(argc-1, argv+1);
323 if (res->status != CMD_SUCCESS) { 296 list_add(res_list, res);
297 if (res->status == CMD_INVALID) {
324 free_argv(argc, argv); 298 free_argv(argc, argv);
325 if (results) {
326 free_cmd_results(results);
327 }
328 results = res;
329 goto cleanup; 299 goto cleanup;
330 } 300 }
331 free_cmd_results(res);
332 } 301 }
333 } 302 }
334 free_argv(argc, argv); 303 free_argv(argc, argv);
@@ -337,10 +306,7 @@ struct cmd_results *execute_command(char *_exec, struct sway_seat *seat,
337cleanup: 306cleanup:
338 free(exec); 307 free(exec);
339 list_free(views); 308 list_free(views);
340 if (!results) { 309 return res_list;
341 results = cmd_results_new(CMD_SUCCESS, NULL, NULL);
342 }
343 return results;
344} 310}
345 311
346// this is like execute_command above, except: 312// this is like execute_command above, except:
@@ -418,6 +384,7 @@ struct cmd_results *config_command(char *exec) {
418 // Strip quotes and unescape the string 384 // Strip quotes and unescape the string
419 for (int i = handler->handle == cmd_set ? 2 : 1; i < argc; ++i) { 385 for (int i = handler->handle == cmd_set ? 2 : 1; i < argc; ++i) {
420 if (handler->handle != cmd_exec && handler->handle != cmd_exec_always 386 if (handler->handle != cmd_exec && handler->handle != cmd_exec_always
387 && handler->handle != cmd_mode
421 && handler->handle != cmd_bindsym 388 && handler->handle != cmd_bindsym
422 && handler->handle != cmd_bindcode 389 && handler->handle != cmd_bindcode
423 && handler->handle != cmd_set 390 && handler->handle != cmd_set
@@ -572,20 +539,25 @@ void free_cmd_results(struct cmd_results *results) {
572 free(results); 539 free(results);
573} 540}
574 541
575char *cmd_results_to_json(struct cmd_results *results) { 542char *cmd_results_to_json(list_t *res_list) {
576 json_object *result_array = json_object_new_array(); 543 json_object *result_array = json_object_new_array();
577 json_object *root = json_object_new_object(); 544 for (int i = 0; i < res_list->length; ++i) {
578 json_object_object_add(root, "success", 545 struct cmd_results *results = res_list->items[i];
579 json_object_new_boolean(results->status == CMD_SUCCESS)); 546 json_object *root = json_object_new_object();
580 if (results->input) { 547 json_object_object_add(root, "success",
581 json_object_object_add( 548 json_object_new_boolean(results->status == CMD_SUCCESS));
582 root, "input", json_object_new_string(results->input)); 549 if (results->error) {
583 } 550 json_object_object_add(root, "parse_error",
584 if (results->error) { 551 json_object_new_boolean(results->status == CMD_INVALID));
585 json_object_object_add( 552 json_object_object_add(
586 root, "error", json_object_new_string(results->error)); 553 root, "error", json_object_new_string(results->error));
554 }
555 if (results->input) {
556 json_object_object_add(
557 root, "input", json_object_new_string(results->input));
558 }
559 json_object_array_add(result_array, root);
587 } 560 }
588 json_object_array_add(result_array, root);
589 const char *json = json_object_to_json_string(result_array); 561 const char *json = json_object_to_json_string(result_array);
590 char *res = strdup(json); 562 char *res = strdup(json);
591 json_object_put(result_array); 563 json_object_put(result_array);
diff --git a/sway/commands/assign.c b/sway/commands/assign.c
index 04582e88..716d70cf 100644
--- a/sway/commands/assign.c
+++ b/sway/commands/assign.c
@@ -1,4 +1,4 @@
1#define _XOPEN_SOURCE 700 1#define _POSIX_C_SOURCE 200809L
2#include <stdio.h> 2#include <stdio.h>
3#include <string.h> 3#include <string.h>
4#include "sway/commands.h" 4#include "sway/commands.h"
diff --git a/sway/commands/bar.c b/sway/commands/bar.c
index c808aef2..2a82d508 100644
--- a/sway/commands/bar.c
+++ b/sway/commands/bar.c
@@ -8,12 +8,12 @@
8 8
9// Must be in alphabetical order for bsearch 9// Must be in alphabetical order for bsearch
10static struct cmd_handler bar_handlers[] = { 10static struct cmd_handler bar_handlers[] = {
11 { "activate_button", bar_cmd_activate_button }, 11 { "bindcode", bar_cmd_bindcode },
12 { "binding_mode_indicator", bar_cmd_binding_mode_indicator }, 12 { "binding_mode_indicator", bar_cmd_binding_mode_indicator },
13 { "bindsym", bar_cmd_bindsym }, 13 { "bindsym", bar_cmd_bindsym },
14 { "colors", bar_cmd_colors }, 14 { "colors", bar_cmd_colors },
15 { "context_button", bar_cmd_context_button },
16 { "font", bar_cmd_font }, 15 { "font", bar_cmd_font },
16 { "gaps", bar_cmd_gaps },
17 { "height", bar_cmd_height }, 17 { "height", bar_cmd_height },
18 { "hidden_state", bar_cmd_hidden_state }, 18 { "hidden_state", bar_cmd_hidden_state },
19 { "icon_theme", bar_cmd_icon_theme }, 19 { "icon_theme", bar_cmd_icon_theme },
@@ -22,10 +22,13 @@ static struct cmd_handler bar_handlers[] = {
22 { "output", bar_cmd_output }, 22 { "output", bar_cmd_output },
23 { "pango_markup", bar_cmd_pango_markup }, 23 { "pango_markup", bar_cmd_pango_markup },
24 { "position", bar_cmd_position }, 24 { "position", bar_cmd_position },
25 { "secondary_button", bar_cmd_secondary_button },
26 { "separator_symbol", bar_cmd_separator_symbol }, 25 { "separator_symbol", bar_cmd_separator_symbol },
27 { "status_command", bar_cmd_status_command }, 26 { "status_command", bar_cmd_status_command },
27 { "status_edge_padding", bar_cmd_status_edge_padding },
28 { "status_padding", bar_cmd_status_padding },
29 { "strip_workspace_name", bar_cmd_strip_workspace_name },
28 { "strip_workspace_numbers", bar_cmd_strip_workspace_numbers }, 30 { "strip_workspace_numbers", bar_cmd_strip_workspace_numbers },
31 { "tray_bindsym", bar_cmd_tray_bindsym },
29 { "tray_output", bar_cmd_tray_output }, 32 { "tray_output", bar_cmd_tray_output },
30 { "tray_padding", bar_cmd_tray_padding }, 33 { "tray_padding", bar_cmd_tray_padding },
31 { "workspace_buttons", bar_cmd_workspace_buttons }, 34 { "workspace_buttons", bar_cmd_workspace_buttons },
diff --git a/sway/commands/bar/activate_button.c b/sway/commands/bar/activate_button.c
deleted file mode 100644
index 7310e7ec..00000000
--- a/sway/commands/bar/activate_button.c
+++ /dev/null
@@ -1,8 +0,0 @@
1#include <stdlib.h>
2#include "sway/commands.h"
3#include "log.h"
4
5struct cmd_results *bar_cmd_activate_button(int argc, char **argv) {
6 // TODO TRAY
7 return cmd_results_new(CMD_INVALID, "activate_button", "TODO TRAY");
8}
diff --git a/sway/commands/bar/bind.c b/sway/commands/bar/bind.c
new file mode 100644
index 00000000..a4c65ec4
--- /dev/null
+++ b/sway/commands/bar/bind.c
@@ -0,0 +1,106 @@
1#include <libevdev/libevdev.h>
2#include <stdlib.h>
3#include <string.h>
4#include <strings.h>
5#include "sway/commands.h"
6#include "sway/config.h"
7#include "sway/input/cursor.h"
8#include "list.h"
9#include "log.h"
10#include "stringop.h"
11
12static struct cmd_results *bar_cmd_bind(int argc, char **argv, bool code) {
13 const char *command = code ? "bar bindcode" : "bar bindsym";
14 struct cmd_results *error = NULL;
15 if ((error = checkarg(argc, command, EXPECTED_AT_LEAST, 2))) {
16 return error;
17 }
18 if (!config->current_bar) {
19 return cmd_results_new(CMD_FAILURE, command, "No bar defined.");
20 }
21
22 struct bar_binding *binding = calloc(1, sizeof(struct bar_binding));
23 if (!binding) {
24 return cmd_results_new(CMD_FAILURE, command,
25 "Unable to allocate bar binding");
26 }
27
28 binding->release = false;
29 if (strcmp("--release", argv[0]) == 0) {
30 binding->release = true;
31 argv++;
32 argc--;
33 }
34
35 char *message = NULL;
36 if (code) {
37 binding->button = get_mouse_bindcode(argv[0], &message);
38 } else {
39 binding->button = get_mouse_bindsym(argv[0], &message);
40 }
41 if (message) {
42 free_bar_binding(binding);
43 error = cmd_results_new(CMD_INVALID, command, message);
44 free(message);
45 return error;
46 } else if (!binding->button) {
47 free_bar_binding(binding);
48 return cmd_results_new(CMD_INVALID, command,
49 "Unknown button %s", argv[0]);
50 }
51
52 const char *name = libevdev_event_code_get_name(EV_KEY, binding->button);
53 if (!name) {
54 switch (binding->button) {
55 case SWAY_SCROLL_UP:
56 name = "SWAY_SCROLL_UP";
57 break;
58 case SWAY_SCROLL_DOWN:
59 name = "SWAY_SCROLL_DOWN";
60 break;
61 case SWAY_SCROLL_LEFT:
62 name = "SWAY_SCROLL_LEFT";
63 break;
64 case SWAY_SCROLL_RIGHT:
65 name = "SWAY_SCROLL_RIGHT";
66 break;
67 default:
68 // Unreachable
69 break;
70 }
71 }
72
73 binding->command = join_args(argv + 1, argc - 1);
74
75 list_t *bindings = config->current_bar->bindings;
76 bool overwritten = false;
77 for (int i = 0; i < bindings->length; i++) {
78 struct bar_binding *other = bindings->items[i];
79 if (other->button == binding->button &&
80 other->release == binding->release) {
81 overwritten = true;
82 bindings->items[i] = binding;
83 free_bar_binding(other);
84 wlr_log(WLR_DEBUG, "[bar %s] Updated binding for %u (%s)%s",
85 config->current_bar->id, binding->button, name,
86 binding->release ? " - release" : "");
87 break;
88 }
89 }
90 if (!overwritten) {
91 list_add(bindings, binding);
92 wlr_log(WLR_DEBUG, "[bar %s] Added binding for %u (%s)%s",
93 config->current_bar->id, binding->button, name,
94 binding->release ? " - release" : "");
95 }
96
97 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
98}
99
100struct cmd_results *bar_cmd_bindcode(int argc, char **argv) {
101 return bar_cmd_bind(argc, argv, true);
102}
103
104struct cmd_results *bar_cmd_bindsym(int argc, char **argv) {
105 return bar_cmd_bind(argc, argv, false);
106}
diff --git a/sway/commands/bar/bindsym.c b/sway/commands/bar/bindsym.c
deleted file mode 100644
index 965c8903..00000000
--- a/sway/commands/bar/bindsym.c
+++ /dev/null
@@ -1,69 +0,0 @@
1#define _XOPEN_SOURCE 500
2#include <stdlib.h>
3#include <string.h>
4#include <strings.h>
5#include "sway/commands.h"
6#include "sway/config.h"
7#include "list.h"
8#include "log.h"
9#include "stringop.h"
10
11struct cmd_results *bar_cmd_bindsym(int argc, char **argv) {
12 struct cmd_results *error = NULL;
13 if ((error = checkarg(argc, "bar bindsym", EXPECTED_AT_LEAST, 2))) {
14 return error;
15 }
16 if (!config->current_bar) {
17 return cmd_results_new(CMD_FAILURE, "bar bindsym", "No bar defined.");
18 }
19
20 struct bar_binding *binding = calloc(1, sizeof(struct bar_binding));
21 if (!binding) {
22 return cmd_results_new(CMD_FAILURE, "bar bindsym",
23 "Unable to allocate bar binding");
24 }
25
26 binding->release = false;
27 if (strcmp("--release", argv[0]) == 0) {
28 binding->release = true;
29 argv++;
30 argc--;
31 }
32
33 binding->button = 0;
34 if (strncasecmp(argv[0], "button", strlen("button")) == 0 &&
35 strlen(argv[0]) == strlen("button0")) {
36 binding->button = argv[0][strlen("button")] - '0';
37 }
38 if (binding->button < 1 || binding->button > 9) {
39 free_bar_binding(binding);
40 return cmd_results_new(CMD_FAILURE, "bar bindsym",
41 "Only button<1-9> is supported");
42 }
43
44 binding->command = join_args(argv + 1, argc - 1);
45
46 list_t *bindings = config->current_bar->bindings;
47 bool overwritten = false;
48 for (int i = 0; i < bindings->length; i++) {
49 struct bar_binding *other = bindings->items[i];
50 if (other->button == binding->button &&
51 other->release == binding->release) {
52 overwritten = true;
53 bindings->items[i] = binding;
54 free_bar_binding(other);
55 wlr_log(WLR_DEBUG, "[bar %s] Updated binding for button%u%s",
56 config->current_bar->id, binding->button,
57 binding->release ? " (release)" : "");
58 break;
59 }
60 }
61 if (!overwritten) {
62 list_add(bindings, binding);
63 wlr_log(WLR_DEBUG, "[bar %s] Added binding for button%u%s",
64 config->current_bar->id, binding->button,
65 binding->release ? " (release)" : "");
66 }
67
68 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
69}
diff --git a/sway/commands/bar/colors.c b/sway/commands/bar/colors.c
index 8c862ca9..ebf1e3e1 100644
--- a/sway/commands/bar/colors.c
+++ b/sway/commands/bar/colors.c
@@ -118,8 +118,8 @@ struct cmd_results *bar_colors_cmd_statusline(int argc, char **argv) {
118} 118}
119 119
120struct cmd_results *bar_colors_cmd_focused_statusline(int argc, char **argv) { 120struct cmd_results *bar_colors_cmd_focused_statusline(int argc, char **argv) {
121 return parse_single_color(&(config->current_bar->colors.focused_separator), 121 return parse_single_color(&(config->current_bar->colors.focused_statusline),
122 "focused_separator", argc, argv); 122 "focused_statusline", argc, argv);
123} 123}
124 124
125struct cmd_results *bar_colors_cmd_urgent_workspace(int argc, char **argv) { 125struct cmd_results *bar_colors_cmd_urgent_workspace(int argc, char **argv) {
diff --git a/sway/commands/bar/context_button.c b/sway/commands/bar/context_button.c
deleted file mode 100644
index 3b76885a..00000000
--- a/sway/commands/bar/context_button.c
+++ /dev/null
@@ -1,8 +0,0 @@
1#include <stdlib.h>
2#include "sway/commands.h"
3#include "log.h"
4
5struct cmd_results *bar_cmd_context_button(int argc, char **argv) {
6 // TODO TRAY
7 return cmd_results_new(CMD_INVALID, "context_button", "TODO TRAY");
8}
diff --git a/sway/commands/bar/gaps.c b/sway/commands/bar/gaps.c
new file mode 100644
index 00000000..f78f3742
--- /dev/null
+++ b/sway/commands/bar/gaps.c
@@ -0,0 +1,60 @@
1#include <stdlib.h>
2#include <string.h>
3#include <strings.h>
4#include "sway/commands.h"
5#include "sway/ipc-server.h"
6#include "log.h"
7
8struct cmd_results *bar_cmd_gaps(int argc, char **argv) {
9 struct cmd_results *error = NULL;
10 if ((error = checkarg(argc, "gaps", EXPECTED_AT_LEAST, 1))) {
11 return error;
12 }
13 if ((error = checkarg(argc, "gaps", EXPECTED_AT_MOST, 4))) {
14 return error;
15 }
16 if (!config->current_bar) {
17 return cmd_results_new(CMD_FAILURE, "bar gaps", "No bar defined.");
18 }
19
20 int top = 0, right = 0, bottom = 0, left = 0;
21
22 for (int i = 0; i < argc; i++) {
23 char *end;
24 int amount = strtol(argv[i], &end, 10);
25 if (strlen(end) && strcasecmp(end, "px") != 0) {
26 return cmd_results_new(CMD_INVALID, "bar gaps",
27 "Expected 'bar [<bar-id>] gaps <all> | <horizonal> "
28 "<vertical> | <top> <right> <bottom> <left>'");
29 }
30
31 if (i == 0) {
32 top = amount;
33 }
34 if (i == 0 || i == 1) {
35 right = amount;
36 }
37 if (i == 0 || i == 2) {
38 bottom = amount;
39 }
40 if (i == 0 || i == 1 || i == 3) {
41 left = amount;
42 }
43 }
44
45 config->current_bar->gaps.top = top;
46 config->current_bar->gaps.right = right;
47 config->current_bar->gaps.bottom = bottom;
48 config->current_bar->gaps.left = left;
49
50 wlr_log(WLR_DEBUG, "Setting bar gaps to %d %d %d %d on bar: %s",
51 config->current_bar->gaps.top, config->current_bar->gaps.right,
52 config->current_bar->gaps.bottom, config->current_bar->gaps.left,
53 config->current_bar->id);
54
55 if (!config->reading) {
56 ipc_event_barconfig_update(config->current_bar);
57 }
58
59 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
60}
diff --git a/sway/commands/bar/hidden_state.c b/sway/commands/bar/hidden_state.c
index 5be6c2dc..79eaf01c 100644
--- a/sway/commands/bar/hidden_state.c
+++ b/sway/commands/bar/hidden_state.c
@@ -1,4 +1,4 @@
1#define _XOPEN_SOURCE 500 1#define _POSIX_C_SOURCE 200809L
2#include <string.h> 2#include <string.h>
3#include <strings.h> 3#include <strings.h>
4#include "sway/commands.h" 4#include "sway/commands.h"
diff --git a/sway/commands/bar/icon_theme.c b/sway/commands/bar/icon_theme.c
index 44cd3076..9d3b6040 100644
--- a/sway/commands/bar/icon_theme.c
+++ b/sway/commands/bar/icon_theme.c
@@ -1,8 +1,28 @@
1#define _XOPEN_SOURCE 500 1#define _POSIX_C_SOURCE 200809L
2#include <string.h> 2#include <string.h>
3#include "config.h"
3#include "sway/commands.h" 4#include "sway/commands.h"
5#include "sway/config.h"
6#include "log.h"
4 7
5struct cmd_results *bar_cmd_icon_theme(int argc, char **argv) { 8struct cmd_results *bar_cmd_icon_theme(int argc, char **argv) {
6 // TODO TRAY 9#if HAVE_TRAY
7 return cmd_results_new(CMD_INVALID, "icon_theme", "TODO TRAY"); 10 struct cmd_results *error = NULL;
11 if ((error = checkarg(argc, "icon_theme", EXPECTED_EQUAL_TO, 1))) {
12 return error;
13 }
14
15 if (!config->current_bar) {
16 return cmd_results_new(CMD_FAILURE, "tray_padding", "No bar defined.");
17 }
18
19 wlr_log(WLR_DEBUG, "[Bar %s] Setting icon theme to %s",
20 config->current_bar->id, argv[0]);
21 free(config->current_bar->icon_theme);
22 config->current_bar->icon_theme = strdup(argv[0]);
23 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
24#else
25 return cmd_results_new(CMD_INVALID, "icon_theme",
26 "Sway has been compiled without tray support");
27#endif
8} 28}
diff --git a/sway/commands/bar/id.c b/sway/commands/bar/id.c
index 7690a852..35509459 100644
--- a/sway/commands/bar/id.c
+++ b/sway/commands/bar/id.c
@@ -1,4 +1,4 @@
1#define _XOPEN_SOURCE 500 1#define _POSIX_C_SOURCE 200809L
2#include <string.h> 2#include <string.h>
3#include "sway/commands.h" 3#include "sway/commands.h"
4#include "log.h" 4#include "log.h"
diff --git a/sway/commands/bar/mode.c b/sway/commands/bar/mode.c
index 2cba785e..dcaf6da9 100644
--- a/sway/commands/bar/mode.c
+++ b/sway/commands/bar/mode.c
@@ -1,4 +1,4 @@
1#define _XOPEN_SOURCE 500 1#define _POSIX_C_SOURCE 200809L
2#include <string.h> 2#include <string.h>
3#include <strings.h> 3#include <strings.h>
4#include "sway/commands.h" 4#include "sway/commands.h"
diff --git a/sway/commands/bar/modifier.c b/sway/commands/bar/modifier.c
index 09025fff..b5a16f45 100644
--- a/sway/commands/bar/modifier.c
+++ b/sway/commands/bar/modifier.c
@@ -20,15 +20,14 @@ struct cmd_results *bar_cmd_modifier(int argc, char **argv) {
20 uint32_t tmp_mod; 20 uint32_t tmp_mod;
21 if ((tmp_mod = get_modifier_mask_by_name(split->items[i])) > 0) { 21 if ((tmp_mod = get_modifier_mask_by_name(split->items[i])) > 0) {
22 mod |= tmp_mod; 22 mod |= tmp_mod;
23 continue;
24 } else { 23 } else {
25 error = cmd_results_new(CMD_INVALID, "modifier", 24 error = cmd_results_new(CMD_INVALID, "modifier",
26 "Unknown modifier '%s'", split->items[i]); 25 "Unknown modifier '%s'", split->items[i]);
27 free_flat_list(split); 26 list_free_items_and_destroy(split);
28 return error; 27 return error;
29 } 28 }
30 } 29 }
31 free_flat_list(split); 30 list_free_items_and_destroy(split);
32 config->current_bar->modifier = mod; 31 config->current_bar->modifier = mod;
33 wlr_log(WLR_DEBUG, 32 wlr_log(WLR_DEBUG,
34 "Show/Hide the bar when pressing '%s' in hide mode.", argv[0]); 33 "Show/Hide the bar when pressing '%s' in hide mode.", argv[0]);
diff --git a/sway/commands/bar/output.c b/sway/commands/bar/output.c
index 72754e05..930d779d 100644
--- a/sway/commands/bar/output.c
+++ b/sway/commands/bar/output.c
@@ -1,4 +1,4 @@
1#define _XOPEN_SOURCE 500 1#define _POSIX_C_SOURCE 200809L
2#include <stdbool.h> 2#include <stdbool.h>
3#include <string.h> 3#include <string.h>
4#include "sway/commands.h" 4#include "sway/commands.h"
diff --git a/sway/commands/bar/secondary_button.c b/sway/commands/bar/secondary_button.c
deleted file mode 100644
index 449124cb..00000000
--- a/sway/commands/bar/secondary_button.c
+++ /dev/null
@@ -1,8 +0,0 @@
1#include <stdlib.h>
2#include "sway/commands.h"
3#include "log.h"
4
5struct cmd_results *bar_cmd_secondary_button(int argc, char **argv) {
6 // TODO TRAY
7 return cmd_results_new(CMD_INVALID, "secondary_button", "TODO TRAY");
8}
diff --git a/sway/commands/bar/separator_symbol.c b/sway/commands/bar/separator_symbol.c
index 392ab730..060b8f52 100644
--- a/sway/commands/bar/separator_symbol.c
+++ b/sway/commands/bar/separator_symbol.c
@@ -1,4 +1,4 @@
1#define _XOPEN_SOURCE 500 1#define _POSIX_C_SOURCE 200809L
2#include <string.h> 2#include <string.h>
3#include "sway/commands.h" 3#include "sway/commands.h"
4#include "log.h" 4#include "log.h"
diff --git a/sway/commands/bar/status_edge_padding.c b/sway/commands/bar/status_edge_padding.c
new file mode 100644
index 00000000..f3b10631
--- /dev/null
+++ b/sway/commands/bar/status_edge_padding.c
@@ -0,0 +1,21 @@
1#include <stdlib.h>
2#include <string.h>
3#include "sway/commands.h"
4#include "log.h"
5
6struct cmd_results *bar_cmd_status_edge_padding(int argc, char **argv) {
7 struct cmd_results *error = NULL;
8 if ((error = checkarg(argc, "status_edge_padding", EXPECTED_EQUAL_TO, 1))) {
9 return error;
10 }
11 char *end;
12 int padding = strtol(argv[0], &end, 10);
13 if (strlen(end) || padding < 0) {
14 return cmd_results_new(CMD_INVALID, "status_edge_padding",
15 "Padding must be a positive integer");
16 }
17 config->current_bar->status_edge_padding = padding;
18 wlr_log(WLR_DEBUG, "Status edge padding on bar %s: %d",
19 config->current_bar->id, config->current_bar->status_edge_padding);
20 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
21}
diff --git a/sway/commands/bar/status_padding.c b/sway/commands/bar/status_padding.c
new file mode 100644
index 00000000..13b8eb6b
--- /dev/null
+++ b/sway/commands/bar/status_padding.c
@@ -0,0 +1,21 @@
1#include <stdlib.h>
2#include <string.h>
3#include "sway/commands.h"
4#include "log.h"
5
6struct cmd_results *bar_cmd_status_padding(int argc, char **argv) {
7 struct cmd_results *error = NULL;
8 if ((error = checkarg(argc, "status_padding", EXPECTED_EQUAL_TO, 1))) {
9 return error;
10 }
11 char *end;
12 int padding = strtol(argv[0], &end, 10);
13 if (strlen(end) || padding < 0) {
14 return cmd_results_new(CMD_INVALID, "status_padding",
15 "Padding must be a positive integer");
16 }
17 config->current_bar->status_padding = padding;
18 wlr_log(WLR_DEBUG, "Status padding on bar %s: %d",
19 config->current_bar->id, config->current_bar->status_padding);
20 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
21}
diff --git a/sway/commands/bar/strip_workspace_name.c b/sway/commands/bar/strip_workspace_name.c
new file mode 100644
index 00000000..79692f6e
--- /dev/null
+++ b/sway/commands/bar/strip_workspace_name.c
@@ -0,0 +1,32 @@
1#include <string.h>
2#include <strings.h>
3#include "sway/commands.h"
4#include "log.h"
5#include "util.h"
6
7struct cmd_results *bar_cmd_strip_workspace_name(int argc, char **argv) {
8 struct cmd_results *error = NULL;
9 if ((error = checkarg(argc,
10 "strip_workspace_name", EXPECTED_EQUAL_TO, 1))) {
11 return error;
12 }
13 if (!config->current_bar) {
14 return cmd_results_new(CMD_FAILURE,
15 "strip_workspace_name", "No bar defined.");
16 }
17
18 config->current_bar->strip_workspace_name =
19 parse_boolean(argv[0], config->current_bar->strip_workspace_name);
20
21 if (config->current_bar->strip_workspace_name) {
22 config->current_bar->strip_workspace_numbers = false;
23
24 wlr_log(WLR_DEBUG, "Stripping workspace name on bar: %s",
25 config->current_bar->id);
26 } else {
27 wlr_log(WLR_DEBUG, "Enabling workspace name on bar: %s",
28 config->current_bar->id);
29 }
30
31 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
32}
diff --git a/sway/commands/bar/strip_workspace_numbers.c b/sway/commands/bar/strip_workspace_numbers.c
index 4e47d047..b33d01e5 100644
--- a/sway/commands/bar/strip_workspace_numbers.c
+++ b/sway/commands/bar/strip_workspace_numbers.c
@@ -2,6 +2,7 @@
2#include <strings.h> 2#include <strings.h>
3#include "sway/commands.h" 3#include "sway/commands.h"
4#include "log.h" 4#include "log.h"
5#include "util.h"
5 6
6struct cmd_results *bar_cmd_strip_workspace_numbers(int argc, char **argv) { 7struct cmd_results *bar_cmd_strip_workspace_numbers(int argc, char **argv) {
7 struct cmd_results *error = NULL; 8 struct cmd_results *error = NULL;
@@ -13,17 +14,19 @@ struct cmd_results *bar_cmd_strip_workspace_numbers(int argc, char **argv) {
13 return cmd_results_new(CMD_FAILURE, 14 return cmd_results_new(CMD_FAILURE,
14 "strip_workspace_numbers", "No bar defined."); 15 "strip_workspace_numbers", "No bar defined.");
15 } 16 }
16 if (strcasecmp("yes", argv[0]) == 0) { 17
17 config->current_bar->strip_workspace_numbers = true; 18 config->current_bar->strip_workspace_numbers =
19 parse_boolean(argv[0], config->current_bar->strip_workspace_numbers);
20
21 if (config->current_bar->strip_workspace_numbers) {
22 config->current_bar->strip_workspace_name = false;
23
18 wlr_log(WLR_DEBUG, "Stripping workspace numbers on bar: %s", 24 wlr_log(WLR_DEBUG, "Stripping workspace numbers on bar: %s",
19 config->current_bar->id); 25 config->current_bar->id);
20 } else if (strcasecmp("no", argv[0]) == 0) { 26 } else {
21 config->current_bar->strip_workspace_numbers = false;
22 wlr_log(WLR_DEBUG, "Enabling workspace numbers on bar: %s", 27 wlr_log(WLR_DEBUG, "Enabling workspace numbers on bar: %s",
23 config->current_bar->id); 28 config->current_bar->id);
24 } else {
25 return cmd_results_new(CMD_INVALID,
26 "strip_workspace_numbers", "Invalid value %s", argv[0]);
27 } 29 }
30
28 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 31 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
29} 32}
diff --git a/sway/commands/bar/tray_bindsym.c b/sway/commands/bar/tray_bindsym.c
new file mode 100644
index 00000000..ad413446
--- /dev/null
+++ b/sway/commands/bar/tray_bindsym.c
@@ -0,0 +1,55 @@
1#include <strings.h>
2#include "config.h"
3#include "sway/commands.h"
4#include "sway/config.h"
5#include "log.h"
6
7struct cmd_results *bar_cmd_tray_bindsym(int argc, char **argv) {
8#if HAVE_TRAY
9 struct cmd_results *error = NULL;
10 if ((error = checkarg(argc, "tray_bindsym", EXPECTED_EQUAL_TO, 2))) {
11 return error;
12 }
13
14 if (!config->current_bar) {
15 return cmd_results_new(CMD_FAILURE, "tray_bindsym", "No bar defined.");
16 }
17
18 int button = 0;
19 if (strncasecmp(argv[0], "button", strlen("button")) == 0 &&
20 strlen(argv[0]) == strlen("button0")) {
21 button = argv[0][strlen("button")] - '0';
22 }
23 if (button < 1 || button > 9) {
24 return cmd_results_new(CMD_FAILURE, "tray_bindsym",
25 "[Bar %s] Only buttons 1 to 9 are supported",
26 config->current_bar->id);
27 }
28
29 static const char *commands[] = {
30 "ContextMenu",
31 "Activate",
32 "SecondaryActivate",
33 "ScrollDown",
34 "ScrollLeft",
35 "ScrollRight",
36 "ScrollUp",
37 "nop"
38 };
39
40 for (size_t i = 0; i < sizeof(commands) / sizeof(commands[0]); ++i) {
41 if (strcasecmp(argv[1], commands[i]) == 0) {
42 wlr_log(WLR_DEBUG, "[Bar %s] Binding button %d to %s",
43 config->current_bar->id, button, commands[i]);
44 config->current_bar->tray_bindings[button] = commands[i];
45 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
46 }
47 }
48
49 return cmd_results_new(CMD_INVALID, "tray_bindsym",
50 "[Bar %s] Invalid command %s", config->current_bar->id, argv[1]);
51#else
52 return cmd_results_new(CMD_INVALID, "tray_bindsym",
53 "Sway has been compiled without tray support");
54#endif
55}
diff --git a/sway/commands/bar/tray_output.c b/sway/commands/bar/tray_output.c
index 6ab16731..a1169c20 100644
--- a/sway/commands/bar/tray_output.c
+++ b/sway/commands/bar/tray_output.c
@@ -1,8 +1,42 @@
1#define _XOPEN_SOURCE 500 1#define _POSIX_C_SOURCE 200809L
2#include <string.h> 2#include <string.h>
3#include "config.h"
3#include "sway/commands.h" 4#include "sway/commands.h"
5#include "sway/config.h"
6#include "list.h"
7#include "log.h"
4 8
5struct cmd_results *bar_cmd_tray_output(int argc, char **argv) { 9struct cmd_results *bar_cmd_tray_output(int argc, char **argv) {
6 // TODO TRAY 10#if HAVE_TRAY
7 return cmd_results_new(CMD_INVALID, "tray_output", "TODO TRAY"); 11 struct cmd_results *error = NULL;
12 if ((error = checkarg(argc, "tray_output", EXPECTED_EQUAL_TO, 1))) {
13 return error;
14 }
15
16 if (!config->current_bar) {
17 return cmd_results_new(CMD_FAILURE, "tray_output", "No bar defined.");
18 }
19
20 list_t *outputs = config->current_bar->tray_outputs;
21 if (!outputs) {
22 config->current_bar->tray_outputs = outputs = create_list();
23 }
24
25 if (strcmp(argv[0], "none") == 0) {
26 wlr_log(WLR_DEBUG, "Hiding tray on bar: %s", config->current_bar->id);
27 for (int i = 0; i < outputs->length; ++i) {
28 free(outputs->items[i]);
29 }
30 outputs->length = 0;
31 } else {
32 wlr_log(WLR_DEBUG, "Showing tray on output '%s' for bar: %s", argv[0],
33 config->current_bar->id);
34 }
35 list_add(outputs, strdup(argv[0]));
36
37 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
38#else
39 return cmd_results_new(CMD_INVALID, "tray_output",
40 "Sway has been compiled without tray support");
41#endif
8} 42}
diff --git a/sway/commands/bar/tray_padding.c b/sway/commands/bar/tray_padding.c
index 91c56f19..eb795b00 100644
--- a/sway/commands/bar/tray_padding.c
+++ b/sway/commands/bar/tray_padding.c
@@ -1,9 +1,42 @@
1#include <stdlib.h> 1#include <stdlib.h>
2#include <strings.h> 2#include <strings.h>
3#include "config.h"
3#include "sway/commands.h" 4#include "sway/commands.h"
5#include "sway/config.h"
4#include "log.h" 6#include "log.h"
5 7
6struct cmd_results *bar_cmd_tray_padding(int argc, char **argv) { 8struct cmd_results *bar_cmd_tray_padding(int argc, char **argv) {
7 // TODO TRAY 9#if HAVE_TRAY
8 return cmd_results_new(CMD_INVALID, "tray_padding", "TODO TRAY"); 10 struct cmd_results *error = NULL;
11 if ((error = checkarg(argc, "tray_padding", EXPECTED_AT_LEAST, 1))) {
12 return error;
13 }
14 if ((error = checkarg(argc, "tray_padding", EXPECTED_AT_MOST, 2))) {
15 return error;
16 }
17
18 if (!config->current_bar) {
19 return cmd_results_new(CMD_FAILURE, "tray_padding", "No bar defined.");
20 }
21 struct bar_config *bar = config->current_bar;
22
23 char *end;
24 int padding = strtol(argv[0], &end, 10);
25 if (padding < 0 || (*end != '\0' && strcasecmp(end, "px") != 0)) {
26 return cmd_results_new(CMD_INVALID, "tray_padding",
27 "[Bar %s] Invalid tray padding value: %s", bar->id, argv[0]);
28 }
29
30 if (argc == 2 && strcasecmp(argv[1], "px") != 0) {
31 return cmd_results_new(CMD_INVALID, "tray_padding",
32 "Expected 'tray_padding <px> [px]'");
33 }
34
35 wlr_log(WLR_DEBUG, "[Bar %s] Setting tray padding to %d", bar->id, padding);
36 config->current_bar->tray_padding = padding;
37 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
38#else
39 return cmd_results_new(CMD_INVALID, "tray_padding",
40 "Sway has been compiled without tray support");
41#endif
9} 42}
diff --git a/sway/commands/bind.c b/sway/commands/bind.c
index a9de227f..be47d412 100644
--- a/sway/commands/bind.c
+++ b/sway/commands/bind.c
@@ -1,15 +1,13 @@
1#define _XOPEN_SOURCE 500 1#define _POSIX_C_SOURCE 200809L
2#ifdef __linux__ 2#include <libevdev/libevdev.h>
3#include <linux/input-event-codes.h> 3#include <linux/input-event-codes.h>
4#elif __FreeBSD__
5#include <dev/evdev/input-event-codes.h>
6#endif
7#include <xkbcommon/xkbcommon.h> 4#include <xkbcommon/xkbcommon.h>
8#include <xkbcommon/xkbcommon-names.h> 5#include <xkbcommon/xkbcommon-names.h>
9#include <string.h> 6#include <string.h>
10#include <strings.h> 7#include <strings.h>
11#include "sway/commands.h" 8#include "sway/commands.h"
12#include "sway/config.h" 9#include "sway/config.h"
10#include "sway/input/cursor.h"
13#include "sway/ipc-server.h" 11#include "sway/ipc-server.h"
14#include "list.h" 12#include "list.h"
15#include "log.h" 13#include "log.h"
@@ -23,9 +21,7 @@ void free_sway_binding(struct sway_binding *binding) {
23 return; 21 return;
24 } 22 }
25 23
26 if (binding->keys) { 24 list_free_items_and_destroy(binding->keys);
27 free_flat_list(binding->keys);
28 }
29 free(binding->input); 25 free(binding->input);
30 free(binding->command); 26 free(binding->command);
31 free(binding); 27 free(binding);
@@ -80,7 +76,6 @@ static int key_qsort_cmp(const void *keyp_a, const void *keyp_b) {
80 return (key_a < key_b) ? -1 : ((key_a > key_b) ? 1 : 0); 76 return (key_a < key_b) ? -1 : ((key_a > key_b) ? 1 : 0);
81} 77}
82 78
83
84/** 79/**
85 * From a keycode, bindcode, or bindsym name and the most likely binding type, 80 * From a keycode, bindcode, or bindsym name and the most likely binding type,
86 * identify the appropriate numeric value corresponding to the key. Return NULL 81 * identify the appropriate numeric value corresponding to the key. Return NULL
@@ -90,52 +85,91 @@ static int key_qsort_cmp(const void *keyp_a, const void *keyp_b) {
90 */ 85 */
91static struct cmd_results *identify_key(const char* name, bool first_key, 86static struct cmd_results *identify_key(const char* name, bool first_key,
92 uint32_t* key_val, enum binding_input_type* type) { 87 uint32_t* key_val, enum binding_input_type* type) {
93 if (*type == BINDING_KEYCODE) { 88 if (*type == BINDING_MOUSECODE) {
94 // check for keycode 89 // check for mouse bindcodes
90 char *message = NULL;
91 uint32_t button = get_mouse_bindcode(name, &message);
92 if (!button) {
93 if (message) {
94 struct cmd_results *error =
95 cmd_results_new(CMD_INVALID, "bindcode", message);
96 free(message);
97 return error;
98 } else {
99 return cmd_results_new(CMD_INVALID, "bindcode",
100 "Unknown button code %s", name);
101 }
102 }
103 *key_val = button;
104 } else if (*type == BINDING_MOUSESYM) {
105 // check for mouse bindsyms (x11 buttons or event names)
106 char *message = NULL;
107 uint32_t button = get_mouse_bindsym(name, &message);
108 if (!button) {
109 if (message) {
110 struct cmd_results *error =
111 cmd_results_new(CMD_INVALID, "bindsym", message);
112 free(message);
113 return error;
114 } else if (!button) {
115 return cmd_results_new(CMD_INVALID, "bindsym",
116 "Unknown button %s", name);
117 }
118 }
119 *key_val = button;
120 } else if (*type == BINDING_KEYCODE) {
121 // check for keycode. If it is the first key, allow mouse bindcodes
122 if (first_key) {
123 char *message = NULL;
124 uint32_t button = get_mouse_bindcode(name, &message);
125 free(message);
126 if (button) {
127 *type = BINDING_MOUSECODE;
128 *key_val = button;
129 return NULL;
130 }
131 }
132
95 xkb_keycode_t keycode = strtol(name, NULL, 10); 133 xkb_keycode_t keycode = strtol(name, NULL, 10);
96 if (!xkb_keycode_is_legal_ext(keycode)) { 134 if (!xkb_keycode_is_legal_ext(keycode)) {
97 return cmd_results_new(CMD_INVALID, "bindcode", 135 if (first_key) {
98 "Invalid keycode '%s'", name); 136 return cmd_results_new(CMD_INVALID, "bindcode",
137 "Invalid keycode or button code '%s'", name);
138 } else {
139 return cmd_results_new(CMD_INVALID, "bindcode",
140 "Invalid keycode '%s'", name);
141 }
99 } 142 }
100 *key_val = keycode; 143 *key_val = keycode;
101 } else { 144 } else {
102 // check for keysym 145 // check for keysym. If it is the first key, allow mouse bindsyms
103 xkb_keysym_t keysym = xkb_keysym_from_name(name, 146 if (first_key) {
104 XKB_KEYSYM_CASE_INSENSITIVE); 147 char *message = NULL;
105 148 uint32_t button = get_mouse_bindsym(name, &message);
106 // Check for mouse binding 149 if (message) {
107 uint32_t button = 0; 150 struct cmd_results *error =
108 if (strncasecmp(name, "button", strlen("button")) == 0 && 151 cmd_results_new(CMD_INVALID, "bindsym", message);
109 strlen(name) == strlen("button0")) { 152 free(message);
110 button = name[strlen("button")] - '1' + BTN_LEFT; 153 return error;
154 } else if (button) {
155 *type = BINDING_MOUSESYM;
156 *key_val = button;
157 return NULL;
158 }
111 } 159 }
112 160
113 if (*type == BINDING_KEYSYM) { 161 xkb_keysym_t keysym = xkb_keysym_from_name(name,
114 if (button) { 162 XKB_KEYSYM_CASE_INSENSITIVE);
115 if (first_key) { 163 if (!keysym) {
116 *type = BINDING_MOUSE; 164 if (first_key) {
117 *key_val = button;
118 } else {
119 return cmd_results_new(CMD_INVALID, "bindsym",
120 "Mixed button '%s' into key sequence", name);
121 }
122 } else if (keysym) {
123 *key_val = keysym;
124 } else {
125 return cmd_results_new(CMD_INVALID, "bindsym",
126 "Unknown key '%s'", name);
127 }
128 } else {
129 if (button) {
130 *key_val = button;
131 } else if (keysym) {
132 return cmd_results_new(CMD_INVALID, "bindsym", 165 return cmd_results_new(CMD_INVALID, "bindsym",
133 "Mixed keysym '%s' into button sequence", name); 166 "Unknown key or button '%s'", name);
134 } else { 167 } else {
135 return cmd_results_new(CMD_INVALID, "bindsym", 168 return cmd_results_new(CMD_INVALID, "bindsym",
136 "Unknown button '%s'", name); 169 "Unknown key '%s'", name);
137 } 170 }
138 } 171 }
172 *key_val = keysym;
139 } 173 }
140 return NULL; 174 return NULL;
141} 175}
@@ -161,6 +195,7 @@ static struct cmd_results *cmd_bindsym_or_bindcode(int argc, char **argv,
161 binding->type = bindcode ? BINDING_KEYCODE : BINDING_KEYSYM; 195 binding->type = bindcode ? BINDING_KEYCODE : BINDING_KEYSYM;
162 196
163 bool exclude_titlebar = false; 197 bool exclude_titlebar = false;
198 bool warn = true;
164 199
165 // Handle --release and --locked 200 // Handle --release and --locked
166 while (argc > 0) { 201 while (argc > 0) {
@@ -178,6 +213,8 @@ static struct cmd_results *cmd_bindsym_or_bindcode(int argc, char **argv,
178 strlen("--input-device=")) == 0) { 213 strlen("--input-device=")) == 0) {
179 free(binding->input); 214 free(binding->input);
180 binding->input = strdup(argv[0] + strlen("--input-device=")); 215 binding->input = strdup(argv[0] + strlen("--input-device="));
216 } else if (strcmp("--no-warn", argv[0]) == 0) {
217 warn = false;
181 } else { 218 } else {
182 break; 219 break;
183 } 220 }
@@ -186,7 +223,8 @@ static struct cmd_results *cmd_bindsym_or_bindcode(int argc, char **argv,
186 } 223 }
187 if (binding->flags & (BINDING_BORDER | BINDING_CONTENTS | BINDING_TITLEBAR) 224 if (binding->flags & (BINDING_BORDER | BINDING_CONTENTS | BINDING_TITLEBAR)
188 || exclude_titlebar) { 225 || exclude_titlebar) {
189 binding->type = BINDING_MOUSE; 226 binding->type = binding->type == BINDING_KEYCODE ?
227 BINDING_MOUSECODE : BINDING_MOUSESYM;
190 } 228 }
191 229
192 if (argc < 2) { 230 if (argc < 2) {
@@ -220,21 +258,22 @@ static struct cmd_results *cmd_bindsym_or_bindcode(int argc, char **argv,
220 uint32_t *key = calloc(1, sizeof(uint32_t)); 258 uint32_t *key = calloc(1, sizeof(uint32_t));
221 if (!key) { 259 if (!key) {
222 free_sway_binding(binding); 260 free_sway_binding(binding);
223 free_flat_list(split); 261 list_free_items_and_destroy(split);
224 return cmd_results_new(CMD_FAILURE, bindtype, 262 return cmd_results_new(CMD_FAILURE, bindtype,
225 "Unable to allocate binding key"); 263 "Unable to allocate binding key");
226 } 264 }
227 *key = key_val; 265 *key = key_val;
228 list_add(binding->keys, key); 266 list_add(binding->keys, key);
229 } 267 }
230 free_flat_list(split); 268 list_free_items_and_destroy(split);
231 binding->order = binding_order++; 269 binding->order = binding_order++;
232 270
233 // refine region of interest for mouse binding once we are certain 271 // refine region of interest for mouse binding once we are certain
234 // that this is one 272 // that this is one
235 if (exclude_titlebar) { 273 if (exclude_titlebar) {
236 binding->flags &= ~BINDING_TITLEBAR; 274 binding->flags &= ~BINDING_TITLEBAR;
237 } else if (binding->type == BINDING_MOUSE) { 275 } else if (binding->type == BINDING_MOUSECODE
276 || binding->type == BINDING_MOUSESYM) {
238 binding->flags |= BINDING_TITLEBAR; 277 binding->flags |= BINDING_TITLEBAR;
239 } 278 }
240 279
@@ -255,8 +294,15 @@ static struct cmd_results *cmd_bindsym_or_bindcode(int argc, char **argv,
255 for (int i = 0; i < mode_bindings->length; ++i) { 294 for (int i = 0; i < mode_bindings->length; ++i) {
256 struct sway_binding *config_binding = mode_bindings->items[i]; 295 struct sway_binding *config_binding = mode_bindings->items[i];
257 if (binding_key_compare(binding, config_binding)) { 296 if (binding_key_compare(binding, config_binding)) {
258 wlr_log(WLR_DEBUG, "overwriting old binding with command '%s'", 297 wlr_log(WLR_INFO, "Overwriting binding '%s' for device '%s' "
259 config_binding->command); 298 "from `%s` to `%s`", argv[0], binding->input,
299 binding->command, config_binding->command);
300 if (warn) {
301 config_add_swaynag_warning("Overwriting binding"
302 "'%s' for device '%s' to `%s` from `%s`",
303 argv[0], binding->input, binding->command,
304 config_binding->command);
305 }
260 free_sway_binding(config_binding); 306 free_sway_binding(config_binding);
261 mode_bindings->items[i] = binding; 307 mode_bindings->items[i] = binding;
262 overwritten = true; 308 overwritten = true;
@@ -270,7 +316,6 @@ static struct cmd_results *cmd_bindsym_or_bindcode(int argc, char **argv,
270 wlr_log(WLR_DEBUG, "%s - Bound %s to command `%s` for device '%s'", 316 wlr_log(WLR_DEBUG, "%s - Bound %s to command `%s` for device '%s'",
271 bindtype, argv[0], binding->command, binding->input); 317 bindtype, argv[0], binding->command, binding->input);
272 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 318 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
273
274} 319}
275 320
276struct cmd_results *cmd_bindsym(int argc, char **argv) { 321struct cmd_results *cmd_bindsym(int argc, char **argv) {
@@ -281,21 +326,25 @@ struct cmd_results *cmd_bindcode(int argc, char **argv) {
281 return cmd_bindsym_or_bindcode(argc, argv, true); 326 return cmd_bindsym_or_bindcode(argc, argv, true);
282} 327}
283 328
284
285/** 329/**
286 * Execute the command associated to a binding 330 * Execute the command associated to a binding
287 */ 331 */
288void seat_execute_command(struct sway_seat *seat, struct sway_binding *binding) { 332void seat_execute_command(struct sway_seat *seat, struct sway_binding *binding) {
289 wlr_log(WLR_DEBUG, "running command for binding: %s", binding->command); 333 wlr_log(WLR_DEBUG, "running command for binding: %s", binding->command);
290 334
291 config->handler_context.seat = seat; 335 list_t *res_list = execute_command(binding->command, seat, NULL);
292 struct cmd_results *results = execute_command(binding->command, NULL, NULL); 336 bool success = true;
293 if (results->status == CMD_SUCCESS) { 337 for (int i = 0; i < res_list->length; ++i) {
338 struct cmd_results *results = res_list->items[i];
339 if (results->status != CMD_SUCCESS) {
340 wlr_log(WLR_DEBUG, "could not run command for binding: %s (%s)",
341 binding->command, results->error);
342 success = false;
343 }
344 free_cmd_results(results);
345 }
346 list_free(res_list);
347 if (success) {
294 ipc_event_binding(binding); 348 ipc_event_binding(binding);
295 } else {
296 wlr_log(WLR_DEBUG, "could not run command for binding: %s (%s)",
297 binding->command, results->error);
298 } 349 }
299
300 free_cmd_results(results);
301} 350}
diff --git a/sway/commands/exec_always.c b/sway/commands/exec_always.c
index 7a15709b..9ec28d81 100644
--- a/sway/commands/exec_always.c
+++ b/sway/commands/exec_always.c
@@ -1,4 +1,4 @@
1#define _XOPEN_SOURCE 500 1#define _POSIX_C_SOURCE 200809L
2#include <stdlib.h> 2#include <stdlib.h>
3#include <stdint.h> 3#include <stdint.h>
4#include <string.h> 4#include <string.h>
diff --git a/sway/commands/focus.c b/sway/commands/focus.c
index f6338c55..97ffe91c 100644
--- a/sway/commands/focus.c
+++ b/sway/commands/focus.c
@@ -193,7 +193,7 @@ static struct cmd_results *focus_output(struct sway_seat *seat,
193 "Expected 'focus output <direction|name>'"); 193 "Expected 'focus output <direction|name>'");
194 } 194 }
195 char *identifier = join_args(argv, argc); 195 char *identifier = join_args(argv, argc);
196 struct sway_output *output = output_by_name(identifier); 196 struct sway_output *output = output_by_name_or_id(identifier);
197 197
198 if (!output) { 198 if (!output) {
199 enum wlr_direction direction; 199 enum wlr_direction direction;
@@ -269,6 +269,9 @@ struct cmd_results *cmd_focus(int argc, char **argv) {
269 } 269 }
270 270
271 if (argc == 0 && container) { 271 if (argc == 0 && container) {
272 if (container->scratchpad && !container->workspace) {
273 root_scratchpad_show(container);
274 }
272 seat_set_focus_container(seat, container); 275 seat_set_focus_container(seat, container);
273 seat_consider_warp_to_focus(seat); 276 seat_consider_warp_to_focus(seat);
274 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 277 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
diff --git a/sway/commands/for_window.c b/sway/commands/for_window.c
index ac4d6563..7c0f7d7f 100644
--- a/sway/commands/for_window.c
+++ b/sway/commands/for_window.c
@@ -1,4 +1,3 @@
1#define _XOPEN_SOURCE 500
2#include <string.h> 1#include <string.h>
3#include "sway/commands.h" 2#include "sway/commands.h"
4#include "sway/criteria.h" 3#include "sway/criteria.h"
diff --git a/sway/commands/fullscreen.c b/sway/commands/fullscreen.c
index ff7cbba7..b78187d9 100644
--- a/sway/commands/fullscreen.c
+++ b/sway/commands/fullscreen.c
@@ -13,14 +13,14 @@ struct cmd_results *cmd_fullscreen(int argc, char **argv) {
13 return error; 13 return error;
14 } 14 }
15 if (!root->outputs->length) { 15 if (!root->outputs->length) {
16 return cmd_results_new(CMD_INVALID, "fullscreen", 16 return cmd_results_new(CMD_FAILURE, "fullscreen",
17 "Can't run this command while there's no outputs connected."); 17 "Can't run this command while there's no outputs connected.");
18 } 18 }
19 struct sway_node *node = config->handler_context.node; 19 struct sway_node *node = config->handler_context.node;
20 struct sway_container *container = config->handler_context.container; 20 struct sway_container *container = config->handler_context.container;
21 struct sway_workspace *workspace = config->handler_context.workspace; 21 struct sway_workspace *workspace = config->handler_context.workspace;
22 if (node->type == N_WORKSPACE && workspace->tiling->length == 0) { 22 if (node->type == N_WORKSPACE && workspace->tiling->length == 0) {
23 return cmd_results_new(CMD_INVALID, "fullscreen", 23 return cmd_results_new(CMD_FAILURE, "fullscreen",
24 "Can't fullscreen an empty workspace"); 24 "Can't fullscreen an empty workspace");
25 } 25 }
26 if (node->type == N_WORKSPACE) { 26 if (node->type == N_WORKSPACE) {
diff --git a/sway/commands/input/events.c b/sway/commands/input/events.c
index e7ed69c6..69f46269 100644
--- a/sway/commands/input/events.c
+++ b/sway/commands/input/events.c
@@ -1,10 +1,69 @@
1#include <limits.h>
1#include <string.h> 2#include <string.h>
2#include <strings.h> 3#include <strings.h>
4#include <wlr/backend/libinput.h>
3#include "sway/config.h" 5#include "sway/config.h"
4#include "sway/commands.h" 6#include "sway/commands.h"
5#include "sway/input/input-manager.h" 7#include "sway/input/input-manager.h"
6#include "log.h" 8#include "log.h"
7 9
10static void toggle_send_events_for_device(struct input_config *ic,
11 struct sway_input_device *input_device) {
12 struct wlr_input_device *wlr_device = input_device->wlr_device;
13 if (!wlr_input_device_is_libinput(wlr_device)) {
14 return;
15 }
16 struct libinput_device *libinput_dev
17 = wlr_libinput_get_device_handle(wlr_device);
18
19 enum libinput_config_send_events_mode mode =
20 libinput_device_config_send_events_get_mode(libinput_dev);
21 uint32_t possible =
22 libinput_device_config_send_events_get_modes(libinput_dev);
23
24 switch (mode) {
25 case LIBINPUT_CONFIG_SEND_EVENTS_ENABLED:
26 mode = LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE;
27 if (possible & mode) {
28 break;
29 }
30 // fall through
31 case LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE:
32 mode = LIBINPUT_CONFIG_SEND_EVENTS_DISABLED;
33 if (possible & mode) {
34 break;
35 }
36 // fall through
37 case LIBINPUT_CONFIG_SEND_EVENTS_DISABLED:
38 default:
39 mode = LIBINPUT_CONFIG_SEND_EVENTS_ENABLED;
40 break;
41 }
42
43 ic->send_events = mode;
44}
45
46static void toggle_send_events(struct input_config *ic) {
47 struct sway_input_device *input_device = NULL;
48 wl_list_for_each(input_device, &server.input->devices, link) {
49 if (strcmp(input_device->identifier, ic->identifier) == 0) {
50 toggle_send_events_for_device(ic, input_device);
51 }
52 }
53}
54
55static void toggle_wildcard_send_events() {
56 struct sway_input_device *input_device = NULL;
57 wl_list_for_each(input_device, &server.input->devices, link) {
58 struct input_config *ic = new_input_config(input_device->identifier);
59 if (!ic) {
60 break;
61 }
62 toggle_send_events_for_device(ic, input_device);
63 store_input_config(ic);
64 }
65}
66
8struct cmd_results *input_cmd_events(int argc, char **argv) { 67struct cmd_results *input_cmd_events(int argc, char **argv) {
9 struct cmd_results *error = NULL; 68 struct cmd_results *error = NULL;
10 if ((error = checkarg(argc, "events", EXPECTED_AT_LEAST, 1))) { 69 if ((error = checkarg(argc, "events", EXPECTED_AT_LEAST, 1))) {
@@ -23,9 +82,24 @@ struct cmd_results *input_cmd_events(int argc, char **argv) {
23 } else if (strcasecmp(argv[0], "disabled_on_external_mouse") == 0) { 82 } else if (strcasecmp(argv[0], "disabled_on_external_mouse") == 0) {
24 ic->send_events = 83 ic->send_events =
25 LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE; 84 LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE;
26 } else { 85 } else if (config->reading) {
27 return cmd_results_new(CMD_INVALID, "events", 86 return cmd_results_new(CMD_INVALID, "events",
28 "Expected 'events <enabled|disabled|disabled_on_external_mouse>'"); 87 "Expected 'events <enabled|disabled|disabled_on_external_mouse>'");
88 } else if (strcasecmp(argv[0], "toggle") == 0) {
89 if (strcmp(ic->identifier, "*") == 0) {
90 // Update the device input configs and then reset the wildcard
91 // config send events mode so that is does not override the device
92 // ones. The device ones will be applied when attempting to apply
93 // the wildcard config
94 toggle_wildcard_send_events();
95 ic->send_events = INT_MIN;
96 } else {
97 toggle_send_events(ic);
98 }
99 } else {
100 return cmd_results_new(CMD_INVALID, "events",
101 "Expected 'events <enabled|disabled|disabled_on_external_mouse|"
102 "toggle>'");
29 } 103 }
30 104
31 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 105 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
diff --git a/sway/commands/input/scroll_button.c b/sway/commands/input/scroll_button.c
index 1958f23c..d82a1fe1 100644
--- a/sway/commands/input/scroll_button.c
+++ b/sway/commands/input/scroll_button.c
@@ -1,9 +1,7 @@
1#include <string.h> 1#include <libevdev/libevdev.h>
2#include <strings.h>
3#include <errno.h>
4#include "sway/config.h" 2#include "sway/config.h"
5#include "sway/commands.h" 3#include "sway/commands.h"
6#include "sway/input/input-manager.h" 4#include "sway/input/cursor.h"
7 5
8struct cmd_results *input_cmd_scroll_button(int argc, char **argv) { 6struct cmd_results *input_cmd_scroll_button(int argc, char **argv) {
9 struct cmd_results *error = NULL; 7 struct cmd_results *error = NULL;
@@ -16,22 +14,26 @@ struct cmd_results *input_cmd_scroll_button(int argc, char **argv) {
16 "No input device defined."); 14 "No input device defined.");
17 } 15 }
18 16
19 errno = 0; 17 if (strcmp(*argv, "disable") == 0) {
20 char *endptr; 18 ic->scroll_button = 0;
21 int scroll_button = strtol(*argv, &endptr, 10); 19 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
22 if (endptr == *argv && scroll_button == 0) {
23 return cmd_results_new(CMD_INVALID, "scroll_button",
24 "Scroll button identifier must be an integer.");
25 } 20 }
26 if (errno == ERANGE) { 21
22 char *message = NULL;
23 uint32_t button = get_mouse_button(*argv, &message);
24 if (message) {
25 error = cmd_results_new(CMD_INVALID, "scroll_button", message);
26 free(message);
27 return error;
28 } else if (button == SWAY_SCROLL_UP || button == SWAY_SCROLL_DOWN
29 || button == SWAY_SCROLL_LEFT || button == SWAY_SCROLL_RIGHT) {
27 return cmd_results_new(CMD_INVALID, "scroll_button", 30 return cmd_results_new(CMD_INVALID, "scroll_button",
28 "Scroll button identifier out of range."); 31 "X11 axis buttons are not supported for scroll_button");
29 } 32 } else if (!button) {
30 if (scroll_button < 0) {
31 return cmd_results_new(CMD_INVALID, "scroll_button", 33 return cmd_results_new(CMD_INVALID, "scroll_button",
32 "Scroll button identifier cannot be negative."); 34 "Unknown button %s", *argv);
33 } 35 }
34 ic->scroll_button = scroll_button; 36 ic->scroll_button = button;
35 37
36 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 38 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
37} 39}
diff --git a/sway/commands/input/xkb_layout.c b/sway/commands/input/xkb_layout.c
index 5fccd4a3..43166401 100644
--- a/sway/commands/input/xkb_layout.c
+++ b/sway/commands/input/xkb_layout.c
@@ -1,4 +1,4 @@
1#define _XOPEN_SOURCE 700 1#define _POSIX_C_SOURCE 200809L
2#include "sway/config.h" 2#include "sway/config.h"
3#include "sway/commands.h" 3#include "sway/commands.h"
4#include "sway/input/input-manager.h" 4#include "sway/input/input-manager.h"
diff --git a/sway/commands/input/xkb_model.c b/sway/commands/input/xkb_model.c
index c4d04638..066f632b 100644
--- a/sway/commands/input/xkb_model.c
+++ b/sway/commands/input/xkb_model.c
@@ -1,4 +1,4 @@
1#define _XOPEN_SOURCE 700 1#define _POSIX_C_SOURCE 200809L
2#include "sway/config.h" 2#include "sway/config.h"
3#include "sway/commands.h" 3#include "sway/commands.h"
4#include "sway/input/input-manager.h" 4#include "sway/input/input-manager.h"
diff --git a/sway/commands/input/xkb_options.c b/sway/commands/input/xkb_options.c
index 794ab6e9..09dc4a5c 100644
--- a/sway/commands/input/xkb_options.c
+++ b/sway/commands/input/xkb_options.c
@@ -1,4 +1,4 @@
1#define _XOPEN_SOURCE 700 1#define _POSIX_C_SOURCE 200809L
2#include "sway/config.h" 2#include "sway/config.h"
3#include "sway/commands.h" 3#include "sway/commands.h"
4#include "sway/input/input-manager.h" 4#include "sway/input/input-manager.h"
diff --git a/sway/commands/input/xkb_rules.c b/sway/commands/input/xkb_rules.c
index 257c3288..d3e576e6 100644
--- a/sway/commands/input/xkb_rules.c
+++ b/sway/commands/input/xkb_rules.c
@@ -1,4 +1,4 @@
1#define _XOPEN_SOURCE 700 1#define _POSIX_C_SOURCE 200809L
2#include "sway/config.h" 2#include "sway/config.h"
3#include "sway/commands.h" 3#include "sway/commands.h"
4#include "sway/input/input-manager.h" 4#include "sway/input/input-manager.h"
diff --git a/sway/commands/input/xkb_variant.c b/sway/commands/input/xkb_variant.c
index 3832dc8e..2d7581d1 100644
--- a/sway/commands/input/xkb_variant.c
+++ b/sway/commands/input/xkb_variant.c
@@ -1,4 +1,4 @@
1#define _XOPEN_SOURCE 700 1#define _POSIX_C_SOURCE 200809L
2#include "sway/config.h" 2#include "sway/config.h"
3#include "sway/commands.h" 3#include "sway/commands.h"
4#include "sway/input/input-manager.h" 4#include "sway/input/input-manager.h"
diff --git a/sway/commands/mode.c b/sway/commands/mode.c
index 637ca45e..189e3c1a 100644
--- a/sway/commands/mode.c
+++ b/sway/commands/mode.c
@@ -1,4 +1,4 @@
1#define _XOPEN_SOURCE 500 1#define _POSIX_C_SOURCE 200809L
2#include <stdbool.h> 2#include <stdbool.h>
3#include <string.h> 3#include <string.h>
4#include <strings.h> 4#include <strings.h>
diff --git a/sway/commands/move.c b/sway/commands/move.c
index 7d8c1f1a..72e177e8 100644
--- a/sway/commands/move.c
+++ b/sway/commands/move.c
@@ -1,4 +1,4 @@
1#define _XOPEN_SOURCE 500 1#define _POSIX_C_SOURCE 200809L
2#include <ctype.h> 2#include <ctype.h>
3#include <stdbool.h> 3#include <stdbool.h>
4#include <string.h> 4#include <string.h>
@@ -64,7 +64,7 @@ static struct sway_output *output_in_direction(const char *direction_string,
64 } 64 }
65 } 65 }
66 66
67 return output_by_name(direction_string); 67 return output_by_name_or_id(direction_string);
68} 68}
69 69
70static bool is_parallel(enum sway_container_layout layout, 70static bool is_parallel(enum sway_container_layout layout,
@@ -154,6 +154,8 @@ static void container_move_to_container_from_direction(
154static void container_move_to_workspace_from_direction( 154static void container_move_to_workspace_from_direction(
155 struct sway_container *container, struct sway_workspace *workspace, 155 struct sway_container *container, struct sway_workspace *workspace,
156 enum wlr_direction move_dir) { 156 enum wlr_direction move_dir) {
157 container->width = container->height = 0;
158
157 if (is_parallel(workspace->layout, move_dir)) { 159 if (is_parallel(workspace->layout, move_dir)) {
158 wlr_log(WLR_DEBUG, "Reparenting container (parallel)"); 160 wlr_log(WLR_DEBUG, "Reparenting container (parallel)");
159 int index = 161 int index =
@@ -216,6 +218,7 @@ static void container_move_to_container(struct sway_container *container,
216 return; 218 return;
217 } 219 }
218 if (container_is_floating(container)) { 220 if (container_is_floating(container)) {
221 container_move_to_workspace(container, destination->workspace);
219 return; 222 return;
220 } 223 }
221 struct sway_workspace *old_workspace = container->workspace; 224 struct sway_workspace *old_workspace = container->workspace;
diff --git a/sway/commands/no_focus.c b/sway/commands/no_focus.c
index 61a8de7e..cb81a445 100644
--- a/sway/commands/no_focus.c
+++ b/sway/commands/no_focus.c
@@ -1,4 +1,3 @@
1#define _XOPEN_SOURCE 500
2#include <string.h> 1#include <string.h>
3#include "sway/commands.h" 2#include "sway/commands.h"
4#include "sway/criteria.h" 3#include "sway/criteria.h"
diff --git a/sway/commands/output/background.c b/sway/commands/output/background.c
index 30fb47c4..2cd1b76a 100644
--- a/sway/commands/output/background.c
+++ b/sway/commands/output/background.c
@@ -116,11 +116,8 @@ struct cmd_results *output_cmd_background(int argc, char **argv) {
116 if (!can_access) { 116 if (!can_access) {
117 wlr_log(WLR_ERROR, "Unable to access background file '%s': %s", 117 wlr_log(WLR_ERROR, "Unable to access background file '%s': %s",
118 src, strerror(errno)); 118 src, strerror(errno));
119 if (config->reading && !config->validating) { 119 config_add_swaynag_warning("Unable to access background file '%s'",
120 swaynag_log(config->swaynag_command, 120 src);
121 &config->swaynag_config_errors,
122 "Unable to access background file '%s'", src);
123 }
124 free(src); 121 free(src);
125 } else { 122 } else {
126 // Escape double quotes in the final path for swaybg 123 // Escape double quotes in the final path for swaybg
diff --git a/sway/commands/output/transform.c b/sway/commands/output/transform.c
index c1555323..ca6f73a4 100644
--- a/sway/commands/output/transform.c
+++ b/sway/commands/output/transform.c
@@ -45,7 +45,7 @@ struct cmd_results *output_cmd_transform(int argc, char **argv) {
45 return cmd_results_new(CMD_INVALID, "output", 45 return cmd_results_new(CMD_INVALID, "output",
46 "Cannot apply relative transform to all outputs."); 46 "Cannot apply relative transform to all outputs.");
47 } 47 }
48 struct sway_output *s_output = output_by_name(output->name); 48 struct sway_output *s_output = output_by_name_or_id(output->name);
49 if (s_output == NULL) { 49 if (s_output == NULL) {
50 return cmd_results_new(CMD_INVALID, "output", 50 return cmd_results_new(CMD_INVALID, "output",
51 "Cannot apply relative transform to unknown output %s", output->name); 51 "Cannot apply relative transform to unknown output %s", output->name);
diff --git a/sway/commands/reload.c b/sway/commands/reload.c
index 62105cdc..3ccbbf34 100644
--- a/sway/commands/reload.c
+++ b/sway/commands/reload.c
@@ -1,4 +1,4 @@
1#define _XOPEN_SOURCE 500 1#define _POSIX_C_SOURCE 200809L
2#include <string.h> 2#include <string.h>
3#include "sway/commands.h" 3#include "sway/commands.h"
4#include "sway/config.h" 4#include "sway/config.h"
@@ -24,8 +24,7 @@ static void do_reload(void *data) {
24 24
25 if (!load_main_config(config->current_config_path, true, false)) { 25 if (!load_main_config(config->current_config_path, true, false)) {
26 wlr_log(WLR_ERROR, "Error(s) reloading config"); 26 wlr_log(WLR_ERROR, "Error(s) reloading config");
27 list_foreach(bar_ids, free); 27 list_free_items_and_destroy(bar_ids);
28 list_free(bar_ids);
29 return; 28 return;
30 } 29 }
31 30
@@ -42,9 +41,7 @@ static void do_reload(void *data) {
42 } 41 }
43 } 42 }
44 } 43 }
45 44 list_free_items_and_destroy(bar_ids);
46 list_foreach(bar_ids, free);
47 list_free(bar_ids);
48 45
49 config_update_font_height(true); 46 config_update_font_height(true);
50 root_for_each_container(rebuild_textures_iterator, NULL); 47 root_for_each_container(rebuild_textures_iterator, NULL);
diff --git a/sway/commands/rename.c b/sway/commands/rename.c
index 0cee9293..491dbab0 100644
--- a/sway/commands/rename.c
+++ b/sway/commands/rename.c
@@ -1,4 +1,3 @@
1#define _XOPEN_SOURCE 500
2#include <ctype.h> 1#include <ctype.h>
3#include <string.h> 2#include <string.h>
4#include <strings.h> 3#include <strings.h>
@@ -82,8 +81,12 @@ struct cmd_results *cmd_rename(int argc, char **argv) {
82 struct sway_workspace *tmp_workspace = workspace_by_name(new_name); 81 struct sway_workspace *tmp_workspace = workspace_by_name(new_name);
83 if (tmp_workspace) { 82 if (tmp_workspace) {
84 free(new_name); 83 free(new_name);
85 return cmd_results_new(CMD_INVALID, "rename", 84 if (tmp_workspace == workspace) {
86 "Workspace already exists"); 85 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
86 } else {
87 return cmd_results_new(CMD_INVALID, "rename",
88 "Workspace already exists");
89 }
87 } 90 }
88 91
89 wlr_log(WLR_DEBUG, "renaming workspace '%s' to '%s'", workspace->name, new_name); 92 wlr_log(WLR_DEBUG, "renaming workspace '%s' to '%s'", workspace->name, new_name);
diff --git a/sway/commands/resize.c b/sway/commands/resize.c
index a90d578e..cf5dea02 100644
--- a/sway/commands/resize.c
+++ b/sway/commands/resize.c
@@ -512,34 +512,42 @@ static struct cmd_results *resize_set_floating(struct sway_container *con,
512 calculate_constraints(&min_width, &max_width, &min_height, &max_height); 512 calculate_constraints(&min_width, &max_width, &min_height, &max_height);
513 513
514 if (width->amount) { 514 if (width->amount) {
515 if (width->unit == RESIZE_UNIT_PPT || 515 switch (width->unit) {
516 width->unit == RESIZE_UNIT_DEFAULT) { 516 case RESIZE_UNIT_PPT:
517 // Convert to px 517 // Convert to px
518 width->amount = con->workspace->width * width->amount / 100; 518 width->amount = con->workspace->width * width->amount / 100;
519 width->unit = RESIZE_UNIT_PX; 519 width->unit = RESIZE_UNIT_PX;
520 } 520 // Falls through
521 if (width->unit == RESIZE_UNIT_PX) { 521 case RESIZE_UNIT_PX:
522 case RESIZE_UNIT_DEFAULT:
522 width->amount = fmax(min_width, fmin(width->amount, max_width)); 523 width->amount = fmax(min_width, fmin(width->amount, max_width));
523 grow_width = width->amount - con->width; 524 grow_width = width->amount - con->width;
524
525 con->x -= grow_width / 2; 525 con->x -= grow_width / 2;
526 con->width = width->amount; 526 con->width = width->amount;
527 break;
528 case RESIZE_UNIT_INVALID:
529 sway_assert(false, "invalid width unit");
530 break;
527 } 531 }
528 } 532 }
529 533
530 if (height->amount) { 534 if (height->amount) {
531 if (height->unit == RESIZE_UNIT_PPT || 535 switch (height->unit) {
532 height->unit == RESIZE_UNIT_DEFAULT) { 536 case RESIZE_UNIT_PPT:
533 // Convert to px 537 // Convert to px
534 height->amount = con->workspace->height * height->amount / 100; 538 height->amount = con->workspace->height * height->amount / 100;
535 height->unit = RESIZE_UNIT_PX; 539 height->unit = RESIZE_UNIT_PX;
536 } 540 // Falls through
537 if (height->unit == RESIZE_UNIT_PX) { 541 case RESIZE_UNIT_PX:
542 case RESIZE_UNIT_DEFAULT:
538 height->amount = fmax(min_height, fmin(height->amount, max_height)); 543 height->amount = fmax(min_height, fmin(height->amount, max_height));
539 grow_height = height->amount - con->height; 544 grow_height = height->amount - con->height;
540
541 con->y -= grow_height / 2; 545 con->y -= grow_height / 2;
542 con->height = height->amount; 546 con->height = height->amount;
547 break;
548 case RESIZE_UNIT_INVALID:
549 sway_assert(false, "invalid height unit");
550 break;
543 } 551 }
544 } 552 }
545 553
diff --git a/sway/commands/seat.c b/sway/commands/seat.c
index 5abb19b0..b8db862b 100644
--- a/sway/commands/seat.c
+++ b/sway/commands/seat.c
@@ -10,6 +10,7 @@ static struct cmd_handler seat_handlers[] = {
10 { "attach", seat_cmd_attach }, 10 { "attach", seat_cmd_attach },
11 { "cursor", seat_cmd_cursor }, 11 { "cursor", seat_cmd_cursor },
12 { "fallback", seat_cmd_fallback }, 12 { "fallback", seat_cmd_fallback },
13 { "hide_cursor", seat_cmd_hide_cursor },
13}; 14};
14 15
15struct cmd_results *cmd_seat(int argc, char **argv) { 16struct cmd_results *cmd_seat(int argc, char **argv) {
@@ -26,9 +27,18 @@ struct cmd_results *cmd_seat(int argc, char **argv) {
26 27
27 struct cmd_results *res = config_subcommand(argv + 1, argc - 1, 28 struct cmd_results *res = config_subcommand(argv + 1, argc - 1,
28 seat_handlers, sizeof(seat_handlers)); 29 seat_handlers, sizeof(seat_handlers));
30 if (res && res->status != CMD_SUCCESS) {
31 free_seat_config(config->handler_context.seat_config);
32 config->handler_context.seat_config = NULL;
33 return res;
34 }
29 35
30 free_seat_config(config->handler_context.seat_config); 36 struct seat_config *sc =
31 config->handler_context.seat_config = NULL; 37 store_seat_config(config->handler_context.seat_config);
38 if (!config->reading) {
39 input_manager_apply_seat_config(sc);
40 }
32 41
33 return res; 42 config->handler_context.seat_config = NULL;
43 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
34} 44}
diff --git a/sway/commands/seat/attach.c b/sway/commands/seat/attach.c
index 6b4bcf1f..0fb17f1d 100644
--- a/sway/commands/seat/attach.c
+++ b/sway/commands/seat/attach.c
@@ -1,10 +1,7 @@
1#define _XOPEN_SOURCE 700 1#define _POSIX_C_SOURCE 200809L
2#include <string.h> 2#include <string.h>
3#include <strings.h>
4#include "sway/input/input-manager.h"
5#include "sway/commands.h" 3#include "sway/commands.h"
6#include "sway/config.h" 4#include "sway/config.h"
7#include "log.h"
8#include "stringop.h" 5#include "stringop.h"
9 6
10struct cmd_results *seat_cmd_attach(int argc, char **argv) { 7struct cmd_results *seat_cmd_attach(int argc, char **argv) {
@@ -12,19 +9,17 @@ struct cmd_results *seat_cmd_attach(int argc, char **argv) {
12 if ((error = checkarg(argc, "attach", EXPECTED_AT_LEAST, 1))) { 9 if ((error = checkarg(argc, "attach", EXPECTED_AT_LEAST, 1))) {
13 return error; 10 return error;
14 } 11 }
15 struct seat_config *current_seat_config = 12 if (!config->handler_context.seat_config) {
16 config->handler_context.seat_config;
17 if (!current_seat_config) {
18 return cmd_results_new(CMD_FAILURE, "attach", "No seat defined"); 13 return cmd_results_new(CMD_FAILURE, "attach", "No seat defined");
19 } 14 }
20 15
21 struct seat_config *new_config = new_seat_config(current_seat_config->name); 16 struct seat_attachment_config *attachment = seat_attachment_config_new();
22 struct seat_attachment_config *new_attachment = seat_attachment_config_new(); 17 if (!attachment) {
23 new_attachment->identifier = strdup(argv[0]); 18 return cmd_results_new(CMD_FAILURE, "attach",
24 list_add(new_config->attachments, new_attachment); 19 "Failed to allocate seat attachment config");
25
26 if (!config->validating) {
27 apply_seat_config(new_config);
28 } 20 }
21 attachment->identifier = strdup(argv[0]);
22 list_add(config->handler_context.seat_config->attachments, attachment);
23
29 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 24 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
30} 25}
diff --git a/sway/commands/seat/cursor.c b/sway/commands/seat/cursor.c
index 1d41a94e..8d9e426a 100644
--- a/sway/commands/seat/cursor.c
+++ b/sway/commands/seat/cursor.c
@@ -1,12 +1,9 @@
1#define _XOPEN_SOURCE 700 1#define _POSIX_C_SOURCE 200809L
2#ifdef __linux__
3#include <linux/input-event-codes.h> 2#include <linux/input-event-codes.h>
4#elif __FreeBSD__
5#include <dev/evdev/input-event-codes.h>
6#endif
7 3
8#include <strings.h> 4#include <strings.h>
9#include <wlr/types/wlr_cursor.h> 5#include <wlr/types/wlr_cursor.h>
6#include <wlr/types/wlr_pointer.h>
10#include "sway/commands.h" 7#include "sway/commands.h"
11#include "sway/input/cursor.h" 8#include "sway/input/cursor.h"
12 9
@@ -15,20 +12,10 @@ static struct cmd_results *press_or_release(struct sway_cursor *cursor,
15 12
16static const char *expected_syntax = "Expected 'cursor <move> <x> <y>' or " 13static const char *expected_syntax = "Expected 'cursor <move> <x> <y>' or "
17 "'cursor <set> <x> <y>' or " 14 "'cursor <set> <x> <y>' or "
18 "'curor <press|release> <left|right|1|2|3...>'"; 15 "'curor <press|release> <button[1-9]|event-name-or-code>'";
19
20struct cmd_results *seat_cmd_cursor(int argc, char **argv) {
21 struct cmd_results *error = NULL;
22 if ((error = checkarg(argc, "cursor", EXPECTED_AT_LEAST, 2))) {
23 return error;
24 }
25 struct sway_seat *seat = config->handler_context.seat;
26 if (!seat) {
27 return cmd_results_new(CMD_FAILURE, "cursor", "No seat defined");
28 }
29
30 struct sway_cursor *cursor = seat->cursor;
31 16
17static struct cmd_results *handle_command(struct sway_cursor *cursor,
18 int argc, char **argv) {
32 if (strcasecmp(argv[0], "move") == 0) { 19 if (strcasecmp(argv[0], "move") == 0) {
33 if (argc < 3) { 20 if (argc < 3) {
34 return cmd_results_new(CMD_INVALID, "cursor", expected_syntax); 21 return cmd_results_new(CMD_INVALID, "cursor", expected_syntax);
@@ -50,6 +37,7 @@ struct cmd_results *seat_cmd_cursor(int argc, char **argv) {
50 if (argc < 2) { 37 if (argc < 2) {
51 return cmd_results_new(CMD_INVALID, "cursor", expected_syntax); 38 return cmd_results_new(CMD_INVALID, "cursor", expected_syntax);
52 } 39 }
40 struct cmd_results *error = NULL;
53 if ((error = press_or_release(cursor, argv[0], argv[1]))) { 41 if ((error = press_or_release(cursor, argv[0], argv[1]))) {
54 return error; 42 return error;
55 } 43 }
@@ -58,6 +46,40 @@ struct cmd_results *seat_cmd_cursor(int argc, char **argv) {
58 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 46 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
59} 47}
60 48
49struct cmd_results *seat_cmd_cursor(int argc, char **argv) {
50 struct cmd_results *error = NULL;
51 if ((error = checkarg(argc, "cursor", EXPECTED_AT_LEAST, 2))) {
52 return error;
53 }
54 struct seat_config *sc = config->handler_context.seat_config;
55 if (!sc) {
56 return cmd_results_new(CMD_FAILURE, "cursor", "No seat defined");
57 }
58
59 if (config->reading || !config->active) {
60 return cmd_results_new(CMD_DEFER, NULL, NULL);
61 }
62
63 if (strcmp(sc->name, "*") != 0) {
64 struct sway_seat *seat = input_manager_get_seat(sc->name);
65 if (!seat) {
66 return cmd_results_new(CMD_FAILURE, "cursor",
67 "Failed to get seat");
68 }
69 error = handle_command(seat->cursor, argc, argv);
70 } else {
71 struct sway_seat *seat = NULL;
72 wl_list_for_each(seat, &server.input->seats, link) {
73 error = handle_command(seat->cursor, argc, argv);
74 if ((error && error->status != CMD_SUCCESS)) {
75 break;
76 }
77 }
78 }
79
80 return error ? error : cmd_results_new(CMD_SUCCESS, NULL, NULL);
81}
82
61static struct cmd_results *press_or_release(struct sway_cursor *cursor, 83static struct cmd_results *press_or_release(struct sway_cursor *cursor,
62 char *action, char *button_str) { 84 char *action, char *button_str) {
63 enum wlr_button_state state; 85 enum wlr_button_state state;
@@ -70,15 +92,35 @@ static struct cmd_results *press_or_release(struct sway_cursor *cursor,
70 return cmd_results_new(CMD_INVALID, "cursor", expected_syntax); 92 return cmd_results_new(CMD_INVALID, "cursor", expected_syntax);
71 } 93 }
72 94
73 if (strcasecmp(button_str, "left") == 0) { 95 char *message = NULL;
74 button = BTN_LEFT; 96 button = get_mouse_button(button_str, &message);
75 } else if (strcasecmp(button_str, "right") == 0) { 97 if (message) {
76 button = BTN_RIGHT; 98 struct cmd_results *error =
77 } else { 99 cmd_results_new(CMD_INVALID, "cursor", message);
78 button = strtol(button_str, NULL, 10); 100 free(message);
79 if (button == 0) { 101 return error;
80 return cmd_results_new(CMD_INVALID, "cursor", expected_syntax); 102 } else if (button == SWAY_SCROLL_UP || button == SWAY_SCROLL_DOWN
81 } 103 || button == SWAY_SCROLL_LEFT || button == SWAY_SCROLL_RIGHT) {
104 // Dispatch axis event
105 enum wlr_axis_orientation orientation =
106 (button == SWAY_SCROLL_UP || button == SWAY_SCROLL_DOWN)
107 ? WLR_AXIS_ORIENTATION_VERTICAL
108 : WLR_AXIS_ORIENTATION_HORIZONTAL;
109 double delta = (button == SWAY_SCROLL_UP || button == SWAY_SCROLL_LEFT)
110 ? -1 : 1;
111 struct wlr_event_pointer_axis event = {
112 .device = NULL,
113 .time_msec = 0,
114 .source = WLR_AXIS_SOURCE_WHEEL,
115 .orientation = orientation,
116 .delta = delta * 15,
117 .delta_discrete = delta
118 };
119 dispatch_cursor_axis(cursor, &event);
120 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
121 } else if (!button) {
122 return cmd_results_new(CMD_INVALID, "curor",
123 "Unknown button %s", button_str);
82 } 124 }
83 dispatch_cursor_button(cursor, NULL, 0, button, state); 125 dispatch_cursor_button(cursor, NULL, 0, button, state);
84 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 126 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
diff --git a/sway/commands/seat/fallback.c b/sway/commands/seat/fallback.c
index a0ddf3ef..8f1ab12c 100644
--- a/sway/commands/seat/fallback.c
+++ b/sway/commands/seat/fallback.c
@@ -1,27 +1,18 @@
1#include <string.h>
2#include <strings.h>
3#include "sway/config.h" 1#include "sway/config.h"
4#include "sway/commands.h" 2#include "sway/commands.h"
5#include "sway/input/input-manager.h"
6#include "util.h" 3#include "util.h"
7 4
8struct cmd_results *seat_cmd_fallback(int argc, char **argv) { 5struct cmd_results *seat_cmd_fallback(int argc, char **argv) {
9 struct cmd_results *error = NULL; 6 struct cmd_results *error = NULL;
10 if ((error = checkarg(argc, "fallback", EXPECTED_AT_LEAST, 1))) { 7 if ((error = checkarg(argc, "fallback", EXPECTED_EQUAL_TO, 1))) {
11 return error; 8 return error;
12 } 9 }
13 struct seat_config *current_seat_config = 10 if (!config->handler_context.seat_config) {
14 config->handler_context.seat_config;
15 if (!current_seat_config) {
16 return cmd_results_new(CMD_FAILURE, "fallback", "No seat defined"); 11 return cmd_results_new(CMD_FAILURE, "fallback", "No seat defined");
17 } 12 }
18 struct seat_config *new_config =
19 new_seat_config(current_seat_config->name);
20
21 new_config->fallback = parse_boolean(argv[0], false);
22 13
23 if (!config->validating) { 14 config->handler_context.seat_config->fallback =
24 apply_seat_config(new_config); 15 parse_boolean(argv[0], false);
25 } 16
26 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 17 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
27} 18}
diff --git a/sway/commands/seat/hide_cursor.c b/sway/commands/seat/hide_cursor.c
new file mode 100644
index 00000000..343573b5
--- /dev/null
+++ b/sway/commands/seat/hide_cursor.c
@@ -0,0 +1,29 @@
1#define _POSIX_C_SOURCE 200809L
2#include <string.h>
3#include "sway/commands.h"
4#include "sway/config.h"
5#include "sway/input/seat.h"
6#include "stringop.h"
7
8struct cmd_results *seat_cmd_hide_cursor(int argc, char **argv) {
9 struct cmd_results *error = NULL;
10 if ((error = checkarg(argc, "hide_cursor", EXPECTED_EQUAL_TO, 1))) {
11 return error;
12 }
13 if (!config->handler_context.seat_config) {
14 return cmd_results_new(CMD_FAILURE, "hide_cursor", "No seat defined");
15 }
16
17 char *end;
18 int timeout = strtol(argv[0], &end, 10);
19 if (*end) {
20 return cmd_results_new(CMD_INVALID, "hide_cursor",
21 "Expected an integer timeout");
22 }
23 if (timeout < 100 && timeout != 0) {
24 timeout = 100;
25 }
26 config->handler_context.seat_config->hide_cursor_timeout = timeout;
27
28 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
29}
diff --git a/sway/commands/set.c b/sway/commands/set.c
index be51230b..d912e4fd 100644
--- a/sway/commands/set.c
+++ b/sway/commands/set.c
@@ -1,4 +1,4 @@
1#define _XOPEN_SOURCE 700 1#define _POSIX_C_SOURCE 200809L
2#include <stdio.h> 2#include <stdio.h>
3#include <string.h> 3#include <string.h>
4#include <strings.h> 4#include <strings.h>
diff --git a/sway/commands/split.c b/sway/commands/split.c
index ed106a86..84385fa9 100644
--- a/sway/commands/split.c
+++ b/sway/commands/split.c
@@ -18,6 +18,10 @@ static struct cmd_results *do_split(int layout) {
18 workspace_split(ws, layout); 18 workspace_split(ws, layout);
19 } 19 }
20 20
21 if (con && con->parent && con->parent->parent) {
22 container_flatten(con->parent->parent);
23 }
24
21 arrange_workspace(ws); 25 arrange_workspace(ws);
22 26
23 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 27 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
diff --git a/sway/commands/swap.c b/sway/commands/swap.c
index 08860264..670d6bca 100644
--- a/sway/commands/swap.c
+++ b/sway/commands/swap.c
@@ -108,7 +108,7 @@ static void container_swap(struct sway_container *con1,
108 container_set_fullscreen(con2, false); 108 container_set_fullscreen(con2, false);
109 } 109 }
110 110
111 struct sway_seat *seat = input_manager_get_default_seat(); 111 struct sway_seat *seat = config->handler_context.seat;
112 struct sway_container *focus = seat_get_focused_container(seat); 112 struct sway_container *focus = seat_get_focused_container(seat);
113 struct sway_workspace *vis1 = 113 struct sway_workspace *vis1 =
114 output_get_active_workspace(con1->workspace->output); 114 output_get_active_workspace(con1->workspace->output);
diff --git a/sway/commands/tiling_drag_threshold.c b/sway/commands/tiling_drag_threshold.c
new file mode 100644
index 00000000..6b0531c3
--- /dev/null
+++ b/sway/commands/tiling_drag_threshold.c
@@ -0,0 +1,22 @@
1#include <string.h>
2#include "sway/commands.h"
3#include "sway/config.h"
4#include "log.h"
5
6struct cmd_results *cmd_tiling_drag_threshold(int argc, char **argv) {
7 struct cmd_results *error = NULL;
8 if ((error = checkarg(argc, "tiling_drag_threshold", EXPECTED_EQUAL_TO, 1))) {
9 return error;
10 }
11
12 char *inv;
13 int value = strtol(argv[0], &inv, 10);
14 if (*inv != '\0' || value < 0) {
15 return cmd_results_new(CMD_INVALID, "tiling_drag_threshold",
16 "Invalid threshold specified");
17 }
18
19 config->tiling_drag_threshold = value;
20
21 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
22}
diff --git a/sway/commands/title_align.c b/sway/commands/title_align.c
new file mode 100644
index 00000000..82578186
--- /dev/null
+++ b/sway/commands/title_align.c
@@ -0,0 +1,30 @@
1#include "sway/commands.h"
2#include "sway/config.h"
3#include "sway/output.h"
4#include "sway/tree/container.h"
5#include "sway/tree/root.h"
6
7struct cmd_results *cmd_title_align(int argc, char **argv) {
8 struct cmd_results *error = NULL;
9 if ((error = checkarg(argc, "title_align", EXPECTED_AT_LEAST, 1))) {
10 return error;
11 }
12
13 if (strcmp(argv[0], "left") == 0) {
14 config->title_align = ALIGN_LEFT;
15 } else if (strcmp(argv[0], "center") == 0) {
16 config->title_align = ALIGN_CENTER;
17 } else if (strcmp(argv[0], "right") == 0) {
18 config->title_align = ALIGN_RIGHT;
19 } else {
20 return cmd_results_new(CMD_INVALID, "title_align",
21 "Expected 'title_align left|center|right'");
22 }
23
24 for (int i = 0; i < root->outputs->length; ++i) {
25 struct sway_output *output = root->outputs->items[i];
26 output_damage_whole(output);
27 }
28
29 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
30}
diff --git a/sway/commands/titlebar_border_thickness.c b/sway/commands/titlebar_border_thickness.c
new file mode 100644
index 00000000..c1e9bb52
--- /dev/null
+++ b/sway/commands/titlebar_border_thickness.c
@@ -0,0 +1,30 @@
1#include <string.h>
2#include "sway/commands.h"
3#include "sway/config.h"
4#include "sway/output.h"
5#include "sway/tree/arrange.h"
6#include "log.h"
7
8struct cmd_results *cmd_titlebar_border_thickness(int argc, char **argv) {
9 struct cmd_results *error = NULL;
10 if ((error = checkarg(argc, "titlebar_border_thickness", EXPECTED_EQUAL_TO, 1))) {
11 return error;
12 }
13
14 char *inv;
15 int value = strtol(argv[0], &inv, 10);
16 if (*inv != '\0' || value < 0 || value > config->titlebar_v_padding) {
17 return cmd_results_new(CMD_FAILURE, "titlebar_border_thickness",
18 "Invalid size specified");
19 }
20
21 config->titlebar_border_thickness = value;
22
23 for (int i = 0; i < root->outputs->length; ++i) {
24 struct sway_output *output = root->outputs->items[i];
25 arrange_workspace(output_get_active_workspace(output));
26 output_damage_whole(output);
27 }
28
29 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
30}
diff --git a/sway/commands/titlebar_padding.c b/sway/commands/titlebar_padding.c
new file mode 100644
index 00000000..a642e945
--- /dev/null
+++ b/sway/commands/titlebar_padding.c
@@ -0,0 +1,42 @@
1#include <string.h>
2#include "sway/commands.h"
3#include "sway/config.h"
4#include "sway/output.h"
5#include "sway/tree/arrange.h"
6#include "log.h"
7
8struct cmd_results *cmd_titlebar_padding(int argc, char **argv) {
9 struct cmd_results *error = NULL;
10 if ((error = checkarg(argc, "titlebar_padding", EXPECTED_AT_LEAST, 1))) {
11 return error;
12 }
13
14 char *inv;
15 int h_value = strtol(argv[0], &inv, 10);
16 if (*inv != '\0' || h_value < 0 || h_value < config->titlebar_border_thickness) {
17 return cmd_results_new(CMD_FAILURE, "titlebar_padding",
18 "Invalid size specified");
19 }
20
21 int v_value;
22 if (argc == 1) {
23 v_value = h_value;
24 } else {
25 v_value = strtol(argv[1], &inv, 10);
26 if (*inv != '\0' || v_value < 0 || v_value < config->titlebar_border_thickness) {
27 return cmd_results_new(CMD_FAILURE, "titlebar_padding",
28 "Invalid size specified");
29 }
30 }
31
32 config->titlebar_v_padding = v_value;
33 config->titlebar_h_padding = h_value;
34
35 for (int i = 0; i < root->outputs->length; ++i) {
36 struct sway_output *output = root->outputs->items[i];
37 arrange_workspace(output_get_active_workspace(output));
38 output_damage_whole(output);
39 }
40
41 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
42}
diff --git a/sway/commands/workspace.c b/sway/commands/workspace.c
index 92118ecf..211b344d 100644
--- a/sway/commands/workspace.c
+++ b/sway/commands/workspace.c
@@ -1,4 +1,4 @@
1#define _XOPEN_SOURCE 500 1#define _POSIX_C_SOURCE 200809L
2#include <ctype.h> 2#include <ctype.h>
3#include <limits.h> 3#include <limits.h>
4#include <string.h> 4#include <string.h>
@@ -33,12 +33,12 @@ static struct workspace_config *workspace_config_find_or_create(char *ws_name) {
33 33
34void free_workspace_config(struct workspace_config *wsc) { 34void free_workspace_config(struct workspace_config *wsc) {
35 free(wsc->workspace); 35 free(wsc->workspace);
36 free_flat_list(wsc->outputs); 36 list_free_items_and_destroy(wsc->outputs);
37 free(wsc); 37 free(wsc);
38} 38}
39 39
40static void prevent_invalid_outer_gaps(struct workspace_config *wsc) { 40static void prevent_invalid_outer_gaps(struct workspace_config *wsc) {
41 if (wsc->gaps_outer.top != INT_MIN && 41 if (wsc->gaps_outer.top != INT_MIN &&
42 wsc->gaps_outer.top < -wsc->gaps_inner) { 42 wsc->gaps_outer.top < -wsc->gaps_inner) {
43 wsc->gaps_outer.top = -wsc->gaps_inner; 43 wsc->gaps_outer.top = -wsc->gaps_inner;
44 } 44 }
diff --git a/sway/config.c b/sway/config.c
index 6f65d0c2..18fee404 100644
--- a/sway/config.c
+++ b/sway/config.c
@@ -1,5 +1,4 @@
1#define _POSIX_C_SOURCE 200809L 1#define _XOPEN_SOURCE 700 // for realpath
2#define _XOPEN_SOURCE 700
3#include <stdio.h> 2#include <stdio.h>
4#include <stdbool.h> 3#include <stdbool.h>
5#include <stdlib.h> 4#include <stdlib.h>
@@ -14,11 +13,7 @@
14#include <limits.h> 13#include <limits.h>
15#include <dirent.h> 14#include <dirent.h>
16#include <strings.h> 15#include <strings.h>
17#ifdef __linux__
18#include <linux/input-event-codes.h> 16#include <linux/input-event-codes.h>
19#elif __FreeBSD__
20#include <dev/evdev/input-event-codes.h>
21#endif
22#include <wlr/types/wlr_output.h> 17#include <wlr/types/wlr_output.h>
23#include "sway/input/input-manager.h" 18#include "sway/input/input-manager.h"
24#include "sway/input/seat.h" 19#include "sway/input/seat.h"
@@ -31,7 +26,6 @@
31#include "sway/tree/workspace.h" 26#include "sway/tree/workspace.h"
32#include "cairo.h" 27#include "cairo.h"
33#include "pango.h" 28#include "pango.h"
34#include "readline.h"
35#include "stringop.h" 29#include "stringop.h"
36#include "list.h" 30#include "list.h"
37#include "log.h" 31#include "log.h"
@@ -39,26 +33,24 @@
39struct sway_config *config = NULL; 33struct sway_config *config = NULL;
40 34
41static void free_mode(struct sway_mode *mode) { 35static void free_mode(struct sway_mode *mode) {
42 int i;
43
44 if (!mode) { 36 if (!mode) {
45 return; 37 return;
46 } 38 }
47 free(mode->name); 39 free(mode->name);
48 if (mode->keysym_bindings) { 40 if (mode->keysym_bindings) {
49 for (i = 0; i < mode->keysym_bindings->length; i++) { 41 for (int i = 0; i < mode->keysym_bindings->length; i++) {
50 free_sway_binding(mode->keysym_bindings->items[i]); 42 free_sway_binding(mode->keysym_bindings->items[i]);
51 } 43 }
52 list_free(mode->keysym_bindings); 44 list_free(mode->keysym_bindings);
53 } 45 }
54 if (mode->keycode_bindings) { 46 if (mode->keycode_bindings) {
55 for (i = 0; i < mode->keycode_bindings->length; i++) { 47 for (int i = 0; i < mode->keycode_bindings->length; i++) {
56 free_sway_binding(mode->keycode_bindings->items[i]); 48 free_sway_binding(mode->keycode_bindings->items[i]);
57 } 49 }
58 list_free(mode->keycode_bindings); 50 list_free(mode->keycode_bindings);
59 } 51 }
60 if (mode->mouse_bindings) { 52 if (mode->mouse_bindings) {
61 for (i = 0; i < mode->mouse_bindings->length; i++) { 53 for (int i = 0; i < mode->mouse_bindings->length; i++) {
62 free_sway_binding(mode->mouse_bindings->items[i]); 54 free_sway_binding(mode->mouse_bindings->items[i]);
63 } 55 }
64 list_free(mode->mouse_bindings); 56 list_free(mode->mouse_bindings);
@@ -214,6 +206,10 @@ static void config_defaults(struct sway_config *config) {
214 config->popup_during_fullscreen = POPUP_SMART; 206 config->popup_during_fullscreen = POPUP_SMART;
215 config->xwayland = true; 207 config->xwayland = true;
216 208
209 config->titlebar_border_thickness = 1;
210 config->titlebar_h_padding = 5;
211 config->titlebar_v_padding = 4;
212
217 // floating view 213 // floating view
218 config->floating_maximum_width = 0; 214 config->floating_maximum_width = 0;
219 config->floating_maximum_height = 0; 215 config->floating_maximum_height = 0;
@@ -231,7 +227,9 @@ static void config_defaults(struct sway_config *config) {
231 config->auto_back_and_forth = false; 227 config->auto_back_and_forth = false;
232 config->reading = false; 228 config->reading = false;
233 config->show_marks = true; 229 config->show_marks = true;
230 config->title_align = ALIGN_LEFT;
234 config->tiling_drag = true; 231 config->tiling_drag = true;
232 config->tiling_drag_threshold = 9;
235 233
236 config->smart_gaps = false; 234 config->smart_gaps = false;
237 config->gaps_inner = 0; 235 config->gaps_inner = 0;
@@ -313,27 +311,16 @@ static char *get_config_path(void) {
313 SYSCONFDIR "/i3/config", 311 SYSCONFDIR "/i3/config",
314 }; 312 };
315 313
316 if (!getenv("XDG_CONFIG_HOME")) { 314 char *config_home = getenv("XDG_CONFIG_HOME");
317 char *home = getenv("HOME"); 315 if (!config_home || !*config_home) {
318 char *config_home = malloc(strlen(home) + strlen("/.config") + 1); 316 config_paths[1] = "$HOME/.config/sway/config";
319 if (!config_home) { 317 config_paths[3] = "$HOME/.config/i3/config";
320 wlr_log(WLR_ERROR, "Unable to allocate $HOME/.config");
321 } else {
322 strcpy(config_home, home);
323 strcat(config_home, "/.config");
324 setenv("XDG_CONFIG_HOME", config_home, 1);
325 wlr_log(WLR_DEBUG, "Set XDG_CONFIG_HOME to %s", config_home);
326 free(config_home);
327 }
328 } 318 }
329 319
330 wordexp_t p; 320 for (size_t i = 0; i < sizeof(config_paths) / sizeof(char *); ++i) {
331 char *path; 321 wordexp_t p;
332 322 if (wordexp(config_paths[i], &p, WRDE_UNDEF) == 0) {
333 int i; 323 char *path = strdup(p.we_wordv[0]);
334 for (i = 0; i < (int)(sizeof(config_paths) / sizeof(char *)); ++i) {
335 if (wordexp(config_paths[i], &p, 0) == 0) {
336 path = strdup(p.we_wordv[0]);
337 wordfree(&p); 324 wordfree(&p);
338 if (file_exists(path)) { 325 if (file_exists(path)) {
339 return path; 326 return path;
@@ -342,7 +329,7 @@ static char *get_config_path(void) {
342 } 329 }
343 } 330 }
344 331
345 return NULL; // Not reached 332 return NULL;
346} 333}
347 334
348static bool load_config(const char *path, struct sway_config *config, 335static bool load_config(const char *path, struct sway_config *config,
@@ -402,7 +389,7 @@ bool load_main_config(const char *file, bool is_active, bool validating) {
402 &old_config->swaynag_config_errors, 389 &old_config->swaynag_config_errors,
403 sizeof(struct swaynag_instance)); 390 sizeof(struct swaynag_instance));
404 391
405 create_default_output_configs(); 392 input_manager_reset_all_inputs();
406 } 393 }
407 394
408 config->current_config_path = path; 395 config->current_config_path = path;
@@ -454,7 +441,7 @@ bool load_main_config(const char *file, bool is_active, bool validating) {
454 } 441 }
455 } 442 }
456 443
457 free_flat_list(secconfigs); 444 list_free_items_and_destroy(secconfigs);
458 } 445 }
459 */ 446 */
460 447
@@ -475,6 +462,11 @@ bool load_main_config(const char *file, bool is_active, bool validating) {
475 if (config->swaynag_config_errors.pid > 0) { 462 if (config->swaynag_config_errors.pid > 0) {
476 swaynag_show(&config->swaynag_config_errors); 463 swaynag_show(&config->swaynag_config_errors);
477 } 464 }
465
466 input_manager_verify_fallback_seat();
467 for (int i = 0; i < config->seat_configs->length; i++) {
468 input_manager_apply_seat_config(config->seat_configs->items[i]);
469 }
478 } 470 }
479 471
480 if (old_config) { 472 if (old_config) {
@@ -581,29 +573,56 @@ bool load_include_configs(const char *path, struct sway_config *config,
581 return true; 573 return true;
582} 574}
583 575
584static int detect_brace_on_following_line(FILE *file, char *line, 576// get line, with backslash continuation
585 int line_number) { 577static ssize_t getline_with_cont(char **lineptr, size_t *line_size, FILE *file,
586 int lines = 0; 578 int *nlines) {
587 if (line[strlen(line) - 1] != '{' && line[strlen(line) - 1] != '}') { 579 char *next_line = NULL;
588 char *peeked = NULL; 580 size_t next_line_size = 0;
589 long position = 0; 581 ssize_t nread = getline(lineptr, line_size, file);
590 do { 582 *nlines = nread == -1 ? 0 : 1;
591 free(peeked); 583 while (nread >= 2 && strcmp(&(*lineptr)[nread - 2], "\\\n") == 0) {
592 peeked = peek_line(file, lines, &position); 584 ssize_t next_nread = getline(&next_line, &next_line_size, file);
593 if (peeked) { 585 if (next_nread == -1) {
594 peeked = strip_whitespace(peeked); 586 break;
587 }
588 (*nlines)++;
589
590 nread += next_nread - 2;
591 if ((ssize_t) *line_size < nread + 1) {
592 *line_size = nread + 1;
593 *lineptr = realloc(*lineptr, *line_size);
594 if (!*lineptr) {
595 nread = -1;
596 break;
595 } 597 }
596 lines++; 598 }
597 } while (peeked && strlen(peeked) == 0); 599 strcpy(&(*lineptr)[nread - next_nread], next_line);
600 }
601 free(next_line);
602 return nread;
603}
598 604
599 if (peeked && strlen(peeked) == 1 && peeked[0] == '{') { 605static int detect_brace(FILE *file) {
600 fseek(file, position, SEEK_SET); 606 int ret = 0;
601 } else { 607 int lines = 0;
602 lines = 0; 608 long pos = ftell(file);
609 char *line = NULL;
610 size_t line_size = 0;
611 while ((getline(&line, &line_size, file)) != -1) {
612 lines++;
613 strip_whitespace(line);
614 if (*line) {
615 if (strcmp(line, "{") == 0) {
616 ret = lines;
617 }
618 break;
603 } 619 }
604 free(peeked);
605 } 620 }
606 return lines; 621 free(line);
622 if (ret == 0) {
623 fseek(file, pos, SEEK_SET);
624 }
625 return ret;
607} 626}
608 627
609static char *expand_line(const char *block, const char *line, bool add_brace) { 628static char *expand_line(const char *block, const char *line, bool add_brace) {
@@ -645,58 +664,51 @@ bool read_config(FILE *file, struct sway_config *config,
645 664
646 bool success = true; 665 bool success = true;
647 int line_number = 0; 666 int line_number = 0;
648 char *line; 667 char *line = NULL;
668 size_t line_size = 0;
669 ssize_t nread;
649 list_t *stack = create_list(); 670 list_t *stack = create_list();
650 size_t read = 0; 671 size_t read = 0;
651 while (!feof(file)) { 672 int nlines = 0;
652 char *block = stack->length ? stack->items[0] : NULL; 673 while ((nread = getline_with_cont(&line, &line_size, file, &nlines)) != -1) {
653 line = read_line(file);
654 if (!line) {
655 continue;
656 }
657 line_number++;
658 wlr_log(WLR_DEBUG, "Read line %d: %s", line_number, line);
659
660 if (reading_main_config) { 674 if (reading_main_config) {
661 size_t length = strlen(line); 675 if (read + nread > config_size) {
662
663 if (read + length > config_size) {
664 wlr_log(WLR_ERROR, "Config file changed during reading"); 676 wlr_log(WLR_ERROR, "Config file changed during reading");
665 list_foreach(stack, free); 677 success = false;
666 list_free(stack); 678 break;
667 free(line);
668 return false;
669 } 679 }
670 680
671 strcpy(this_config + read, line); 681 strcpy(&this_config[read], line);
672 if (line_number != 1) { 682 read += nread;
673 this_config[read - 1] = '\n';
674 }
675 read += length + 1;
676 } 683 }
677 684
678 line = strip_whitespace(line); 685 if (line[nread - 1] == '\n') {
679 if (line[0] == '#') { 686 line[nread - 1] = '\0';
680 free(line);
681 continue;
682 } 687 }
683 if (strlen(line) == 0) { 688
684 free(line); 689 line_number += nlines;
690 wlr_log(WLR_DEBUG, "Read line %d: %s", line_number, line);
691
692 strip_whitespace(line);
693 if (!*line || line[0] == '#') {
685 continue; 694 continue;
686 } 695 }
687 int brace_detected = detect_brace_on_following_line(file, line, 696 int brace_detected = 0;
688 line_number); 697 if (line[strlen(line) - 1] != '{' && line[strlen(line) - 1] != '}') {
689 if (brace_detected > 0) { 698 brace_detected = detect_brace(file);
690 line_number += brace_detected; 699 if (brace_detected > 0) {
691 wlr_log(WLR_DEBUG, "Detected open brace on line %d", line_number); 700 line_number += brace_detected;
701 wlr_log(WLR_DEBUG, "Detected open brace on line %d", line_number);
702 }
692 } 703 }
704 char *block = stack->length ? stack->items[0] : NULL;
693 char *expanded = expand_line(block, line, brace_detected > 0); 705 char *expanded = expand_line(block, line, brace_detected > 0);
694 if (!expanded) { 706 if (!expanded) {
695 list_foreach(stack, free); 707 success = false;
696 list_free(stack); 708 break;
697 free(line);
698 return false;
699 } 709 }
710 config->current_config_line_number = line_number;
711 config->current_config_line = line;
700 struct cmd_results *res; 712 struct cmd_results *res;
701 if (block && strcmp(block, "<commands>") == 0) { 713 if (block && strcmp(block, "<commands>") == 0) {
702 // Special case 714 // Special case
@@ -753,15 +765,40 @@ bool read_config(FILE *file, struct sway_config *config,
753 default:; 765 default:;
754 } 766 }
755 free(expanded); 767 free(expanded);
756 free(line);
757 free_cmd_results(res); 768 free_cmd_results(res);
758 } 769 }
759 list_foreach(stack, free); 770 free(line);
760 list_free(stack); 771 list_free_items_and_destroy(stack);
772 config->current_config_line_number = 0;
773 config->current_config_line = NULL;
761 774
762 return success; 775 return success;
763} 776}
764 777
778void config_add_swaynag_warning(char *fmt, ...) {
779 if (config->reading && !config->validating) {
780 va_list args;
781 va_start(args, fmt);
782 size_t length = vsnprintf(NULL, 0, fmt, args) + 1;
783 va_end(args);
784
785 char *temp = malloc(length + 1);
786 if (!temp) {
787 wlr_log(WLR_ERROR, "Failed to allocate buffer for warning.");
788 return;
789 }
790
791 va_start(args, fmt);
792 vsnprintf(temp, length, fmt, args);
793 va_end(args);
794
795 swaynag_log(config->swaynag_command, &config->swaynag_config_errors,
796 "Warning on line %i (%s) '%s': %s",
797 config->current_config_line_number, config->current_config_path,
798 config->current_config_line, temp);
799 }
800}
801
765char *do_var_replacement(char *str) { 802char *do_var_replacement(char *str) {
766 int i; 803 int i;
767 char *find = str; 804 char *find = str;
diff --git a/sway/config/bar.c b/sway/config/bar.c
index 7bca5f49..701bf051 100644
--- a/sway/config/bar.c
+++ b/sway/config/bar.c
@@ -1,5 +1,4 @@
1#define _POSIX_C_SOURCE 200809L 1#define _POSIX_C_SOURCE 200809L
2#define _XOPEN_SOURCE 700
3#include <stdio.h> 2#include <stdio.h>
4#include <stdbool.h> 3#include <stdbool.h>
5#include <stdlib.h> 4#include <stdlib.h>
@@ -13,6 +12,7 @@
13#include <signal.h> 12#include <signal.h>
14#include "sway/config.h" 13#include "sway/config.h"
15#include "sway/output.h" 14#include "sway/output.h"
15#include "config.h"
16#include "stringop.h" 16#include "stringop.h"
17#include "list.h" 17#include "list.h"
18#include "log.h" 18#include "log.h"
@@ -50,13 +50,10 @@ void free_bar_config(struct bar_config *bar) {
50 free(bar->font); 50 free(bar->font);
51 free(bar->separator_symbol); 51 free(bar->separator_symbol);
52 for (int i = 0; i < bar->bindings->length; i++) { 52 for (int i = 0; i < bar->bindings->length; i++) {
53 struct bar_binding *binding = bar->bindings->items[i]; 53 free_bar_binding(bar->bindings->items[i]);
54 free_bar_binding(binding);
55 } 54 }
56 list_free(bar->bindings); 55 list_free(bar->bindings);
57 if (bar->outputs) { 56 list_free_items_and_destroy(bar->outputs);
58 free_flat_list(bar->outputs);
59 }
60 if (bar->pid != 0) { 57 if (bar->pid != 0) {
61 terminate_swaybar(bar->pid); 58 terminate_swaybar(bar->pid);
62 } 59 }
@@ -81,6 +78,10 @@ void free_bar_config(struct bar_config *bar) {
81 free(bar->colors.binding_mode_border); 78 free(bar->colors.binding_mode_border);
82 free(bar->colors.binding_mode_bg); 79 free(bar->colors.binding_mode_bg);
83 free(bar->colors.binding_mode_text); 80 free(bar->colors.binding_mode_text);
81#if HAVE_TRAY
82 list_free_items_and_destroy(bar->tray_outputs);
83 free(bar->icon_theme);
84#endif
84 free(bar); 85 free(bar);
85} 86}
86 87
@@ -95,15 +96,18 @@ struct bar_config *default_bar_config(void) {
95 bar->pango_markup = false; 96 bar->pango_markup = false;
96 bar->swaybar_command = NULL; 97 bar->swaybar_command = NULL;
97 bar->font = NULL; 98 bar->font = NULL;
98 bar->height = -1; 99 bar->height = 0;
99 bar->workspace_buttons = true; 100 bar->workspace_buttons = true;
100 bar->wrap_scroll = false; 101 bar->wrap_scroll = false;
101 bar->separator_symbol = NULL; 102 bar->separator_symbol = NULL;
102 bar->strip_workspace_numbers = false; 103 bar->strip_workspace_numbers = false;
104 bar->strip_workspace_name = false;
103 bar->binding_mode_indicator = true; 105 bar->binding_mode_indicator = true;
104 bar->verbose = false; 106 bar->verbose = false;
105 bar->pid = 0; 107 bar->pid = 0;
106 bar->modifier = get_modifier_mask_by_name("Mod4"); 108 bar->modifier = get_modifier_mask_by_name("Mod4");
109 bar->status_padding = 1;
110 bar->status_edge_padding = 3;
107 if (!(bar->mode = strdup("dock"))) { 111 if (!(bar->mode = strdup("dock"))) {
108 goto cleanup; 112 goto cleanup;
109 } 113 }
@@ -168,6 +172,10 @@ struct bar_config *default_bar_config(void) {
168 bar->colors.binding_mode_bg = NULL; 172 bar->colors.binding_mode_bg = NULL;
169 bar->colors.binding_mode_text = NULL; 173 bar->colors.binding_mode_text = NULL;
170 174
175#if HAVE_TRAY
176 bar->tray_padding = 2;
177#endif
178
171 list_add(config->bars, bar); 179 list_add(config->bars, bar);
172 return bar; 180 return bar;
173cleanup: 181cleanup:
diff --git a/sway/config/input.c b/sway/config/input.c
index d5d2d90b..d649d34d 100644
--- a/sway/config/input.c
+++ b/sway/config/input.c
@@ -1,4 +1,4 @@
1#define _XOPEN_SOURCE 700 1#define _POSIX_C_SOURCE 200809L
2#include <stdlib.h> 2#include <stdlib.h>
3#include <limits.h> 3#include <limits.h>
4#include <float.h> 4#include <float.h>
diff --git a/sway/config/output.c b/sway/config/output.c
index 07543e3c..f24e7d66 100644
--- a/sway/config/output.c
+++ b/sway/config/output.c
@@ -1,4 +1,4 @@
1#define _XOPEN_SOURCE 700 1#define _POSIX_C_SOURCE 200809L
2#include <assert.h> 2#include <assert.h>
3#include <stdbool.h> 3#include <stdbool.h>
4#include <string.h> 4#include <string.h>
@@ -179,10 +179,6 @@ void apply_output_config(struct output_config *oc, struct sway_output *output) {
179 179
180 if (oc && oc->enabled == 0) { 180 if (oc && oc->enabled == 0) {
181 if (output->enabled) { 181 if (output->enabled) {
182 if (output->bg_pid != 0) {
183 terminate_swaybg(output->bg_pid);
184 output->bg_pid = 0;
185 }
186 output_disable(output); 182 output_disable(output);
187 wlr_output_layout_remove(root->output_layout, wlr_output); 183 wlr_output_layout_remove(root->output_layout, wlr_output);
188 } 184 }
@@ -276,18 +272,86 @@ void apply_output_config(struct output_config *oc, struct sway_output *output) {
276 } 272 }
277} 273}
278 274
279static struct output_config *get_output_config(char *name, char *identifier) { 275static void default_output_config(struct output_config *oc,
276 struct wlr_output *wlr_output) {
277 oc->enabled = 1;
278 if (!wl_list_empty(&wlr_output->modes)) {
279 struct wlr_output_mode *mode =
280 wl_container_of(wlr_output->modes.prev, mode, link);
281 oc->width = mode->width;
282 oc->height = mode->height;
283 oc->refresh_rate = mode->refresh;
284 }
285 oc->x = oc->y = -1;
286 oc->scale = 1;
287 oc->transform = WL_OUTPUT_TRANSFORM_NORMAL;
288}
289
290static struct output_config *get_output_config(char *identifier,
291 struct sway_output *sway_output) {
292 const char *name = sway_output->wlr_output->name;
293 struct output_config *oc_name = NULL;
280 int i = list_seq_find(config->output_configs, output_name_cmp, name); 294 int i = list_seq_find(config->output_configs, output_name_cmp, name);
281 if (i >= 0) { 295 if (i >= 0) {
282 return config->output_configs->items[i]; 296 oc_name = config->output_configs->items[i];
283 } 297 }
284 298
299 struct output_config *oc_id = NULL;
285 i = list_seq_find(config->output_configs, output_name_cmp, identifier); 300 i = list_seq_find(config->output_configs, output_name_cmp, identifier);
286 if (i >= 0) { 301 if (i >= 0) {
287 return config->output_configs->items[i]; 302 oc_id = config->output_configs->items[i];
303 }
304
305 struct output_config *result = result = new_output_config("temp");
306 if (config->reloading) {
307 default_output_config(result, sway_output->wlr_output);
308 }
309 if (oc_name && oc_id) {
310 // Generate a config named `<identifier> on <name>` which contains a
311 // merged copy of the identifier on name. This will make sure that both
312 // identifier and name configs are respected, with identifier getting
313 // priority
314 size_t length = snprintf(NULL, 0, "%s on %s", identifier, name) + 1;
315 char *temp = malloc(length);
316 snprintf(temp, length, "%s on %s", identifier, name);
317
318 free(result->name);
319 result->name = temp;
320 merge_output_config(result, oc_name);
321 merge_output_config(result, oc_id);
322
323 wlr_log(WLR_DEBUG, "Generated output config \"%s\" (enabled: %d)"
324 " (%dx%d@%fHz position %d,%d scale %f transform %d) (bg %s %s)"
325 " (dpms %d)", result->name, result->enabled, result->width,
326 result->height, result->refresh_rate, result->x, result->y,
327 result->scale, result->transform, result->background,
328 result->background_option, result->dpms_state);
329 } else if (oc_name) {
330 // No identifier config, just return a copy of the name config
331 free(result->name);
332 result->name = strdup(name);
333 merge_output_config(result, oc_name);
334 } else if (oc_id) {
335 // No name config, just return a copy of the identifier config
336 free(result->name);
337 result->name = strdup(identifier);
338 merge_output_config(result, oc_id);
339 } else if (config->reloading) {
340 // Neither config exists, but we need to reset the output so create a
341 // default config for the output and if a wildcard config exists, merge
342 // that on top
343 free(result->name);
344 result->name = strdup("*");
345 i = list_seq_find(config->output_configs, output_name_cmp, "*");
346 if (i >= 0) {
347 merge_output_config(result, config->output_configs->items[i]);
348 }
349 } else {
350 free_output_config(result);
351 result = NULL;
288 } 352 }
289 353
290 return NULL; 354 return result;
291} 355}
292 356
293void apply_output_config_to_outputs(struct output_config *oc) { 357void apply_output_config_to_outputs(struct output_config *oc) {
@@ -301,14 +365,17 @@ void apply_output_config_to_outputs(struct output_config *oc) {
301 char *name = sway_output->wlr_output->name; 365 char *name = sway_output->wlr_output->name;
302 output_get_identifier(id, sizeof(id), sway_output); 366 output_get_identifier(id, sizeof(id), sway_output);
303 if (wildcard || !strcmp(name, oc->name) || !strcmp(id, oc->name)) { 367 if (wildcard || !strcmp(name, oc->name) || !strcmp(id, oc->name)) {
304 struct output_config *current = oc; 368 struct output_config *current = new_output_config(oc->name);
369 merge_output_config(current, oc);
305 if (wildcard) { 370 if (wildcard) {
306 struct output_config *tmp = get_output_config(name, id); 371 struct output_config *tmp = get_output_config(id, sway_output);
307 if (tmp) { 372 if (tmp) {
373 free_output_config(current);
308 current = tmp; 374 current = tmp;
309 } 375 }
310 } 376 }
311 apply_output_config(current, sway_output); 377 apply_output_config(current, sway_output);
378 free_output_config(current);
312 379
313 if (!wildcard) { 380 if (!wildcard) {
314 // Stop looking if the output config isn't applicable to all 381 // Stop looking if the output config isn't applicable to all
@@ -329,28 +396,3 @@ void free_output_config(struct output_config *oc) {
329 free(oc->background_fallback); 396 free(oc->background_fallback);
330 free(oc); 397 free(oc);
331} 398}
332
333static void default_output_config(struct output_config *oc,
334 struct wlr_output *wlr_output) {
335 oc->enabled = 1;
336 if (!wl_list_empty(&wlr_output->modes)) {
337 struct wlr_output_mode *mode =
338 wl_container_of(wlr_output->modes.prev, mode, link);
339 oc->width = mode->width;
340 oc->height = mode->height;
341 oc->refresh_rate = mode->refresh;
342 }
343 oc->x = oc->y = -1;
344 oc->scale = 1;
345 oc->transform = WL_OUTPUT_TRANSFORM_NORMAL;
346}
347
348void create_default_output_configs(void) {
349 struct sway_output *sway_output;
350 wl_list_for_each(sway_output, &root->all_outputs, link) {
351 char *name = sway_output->wlr_output->name;
352 struct output_config *oc = new_output_config(name);
353 default_output_config(oc, sway_output->wlr_output);
354 list_add(config->output_configs, oc);
355 }
356}
diff --git a/sway/config/seat.c b/sway/config/seat.c
index 46456caf..d7316c68 100644
--- a/sway/config/seat.c
+++ b/sway/config/seat.c
@@ -1,4 +1,4 @@
1#define _XOPEN_SOURCE 700 1#define _POSIX_C_SOURCE 200809L
2#include <stdlib.h> 2#include <stdlib.h>
3#include <string.h> 3#include <string.h>
4#include "sway/config.h" 4#include "sway/config.h"
@@ -11,7 +11,6 @@ struct seat_config *new_seat_config(const char* name) {
11 return NULL; 11 return NULL;
12 } 12 }
13 13
14 wlr_log(WLR_DEBUG, "new_seat_config(%s)", name);
15 seat->name = strdup(name); 14 seat->name = strdup(name);
16 if (!sway_assert(seat->name, "could not allocate name for seat")) { 15 if (!sway_assert(seat->name, "could not allocate name for seat")) {
17 free(seat); 16 free(seat);
@@ -26,10 +25,57 @@ struct seat_config *new_seat_config(const char* name) {
26 free(seat); 25 free(seat);
27 return NULL; 26 return NULL;
28 } 27 }
28 seat->hide_cursor_timeout = -1;
29 29
30 return seat; 30 return seat;
31} 31}
32 32
33static void merge_wildcard_on_all(struct seat_config *wildcard) {
34 for (int i = 0; i < config->seat_configs->length; i++) {
35 struct seat_config *sc = config->seat_configs->items[i];
36 if (strcmp(wildcard->name, sc->name) != 0) {
37 wlr_log(WLR_DEBUG, "Merging seat * config on %s", sc->name);
38 merge_seat_config(sc, wildcard);
39 }
40 }
41}
42
43struct seat_config *store_seat_config(struct seat_config *sc) {
44 bool wildcard = strcmp(sc->name, "*") == 0;
45 if (wildcard) {
46 merge_wildcard_on_all(sc);
47 }
48
49 int i = list_seq_find(config->seat_configs, seat_name_cmp, sc->name);
50 if (i >= 0) {
51 wlr_log(WLR_DEBUG, "Merging on top of existing seat config");
52 struct seat_config *current = config->seat_configs->items[i];
53 merge_seat_config(current, sc);
54 free_seat_config(sc);
55 sc = current;
56 } else if (!wildcard) {
57 wlr_log(WLR_DEBUG, "Adding non-wildcard seat config");
58 i = list_seq_find(config->seat_configs, seat_name_cmp, "*");
59 if (i >= 0) {
60 wlr_log(WLR_DEBUG, "Merging on top of seat * config");
61 struct seat_config *current = new_seat_config(sc->name);
62 merge_seat_config(current, config->seat_configs->items[i]);
63 merge_seat_config(current, sc);
64 free_seat_config(sc);
65 sc = current;
66 }
67 list_add(config->seat_configs, sc);
68 } else {
69 // New wildcard config. Just add it
70 wlr_log(WLR_DEBUG, "Adding seat * config");
71 list_add(config->seat_configs, sc);
72 }
73
74 wlr_log(WLR_DEBUG, "Config stored for seat %s", sc->name);
75
76 return sc;
77}
78
33struct seat_attachment_config *seat_attachment_config_new(void) { 79struct seat_attachment_config *seat_attachment_config_new(void) {
34 struct seat_attachment_config *attachment = 80 struct seat_attachment_config *attachment =
35 calloc(1, sizeof(struct seat_attachment_config)); 81 calloc(1, sizeof(struct seat_attachment_config));
@@ -65,11 +111,6 @@ static void merge_seat_attachment_config(struct seat_attachment_config *dest,
65} 111}
66 112
67void merge_seat_config(struct seat_config *dest, struct seat_config *source) { 113void merge_seat_config(struct seat_config *dest, struct seat_config *source) {
68 if (source->name) {
69 free(dest->name);
70 dest->name = strdup(source->name);
71 }
72
73 if (source->fallback != -1) { 114 if (source->fallback != -1) {
74 dest->fallback = source->fallback; 115 dest->fallback = source->fallback;
75 } 116 }
@@ -97,6 +138,10 @@ void merge_seat_config(struct seat_config *dest, struct seat_config *source) {
97 } 138 }
98 } 139 }
99 } 140 }
141
142 if (source->hide_cursor_timeout != -1) {
143 dest->hide_cursor_timeout = source->hide_cursor_timeout;
144 }
100} 145}
101 146
102struct seat_config *copy_seat_config(struct seat_config *seat) { 147struct seat_config *copy_seat_config(struct seat_config *seat) {
@@ -117,11 +162,8 @@ void free_seat_config(struct seat_config *seat) {
117 162
118 free(seat->name); 163 free(seat->name);
119 for (int i = 0; i < seat->attachments->length; ++i) { 164 for (int i = 0; i < seat->attachments->length; ++i) {
120 struct seat_attachment_config *attachment = 165 seat_attachment_config_free(seat->attachments->items[i]);
121 seat->attachments->items[i];
122 seat_attachment_config_free(attachment);
123 } 166 }
124
125 list_free(seat->attachments); 167 list_free(seat->attachments);
126 free(seat); 168 free(seat);
127} 169}
diff --git a/sway/criteria.c b/sway/criteria.c
index 3393852c..54583b04 100644
--- a/sway/criteria.c
+++ b/sway/criteria.c
@@ -1,4 +1,4 @@
1#define _XOPEN_SOURCE 700 1#define _POSIX_C_SOURCE 200809L
2#include <stdlib.h> 2#include <stdlib.h>
3#include <stdio.h> 3#include <stdio.h>
4#include <stdbool.h> 4#include <stdbool.h>
diff --git a/sway/desktop/output.c b/sway/desktop/output.c
index d649100f..04c9b4f6 100644
--- a/sway/desktop/output.c
+++ b/sway/desktop/output.c
@@ -29,23 +29,13 @@
29#include "sway/tree/view.h" 29#include "sway/tree/view.h"
30#include "sway/tree/workspace.h" 30#include "sway/tree/workspace.h"
31 31
32struct sway_output *output_by_name(const char *name) { 32struct sway_output *output_by_name_or_id(const char *name_or_id) {
33 for (int i = 0; i < root->outputs->length; ++i) { 33 for (int i = 0; i < root->outputs->length; ++i) {
34 struct sway_output *output = root->outputs->items[i]; 34 struct sway_output *output = root->outputs->items[i];
35 if (strcasecmp(output->wlr_output->name, name) == 0) { 35 char identifier[128];
36 return output; 36 output_get_identifier(identifier, sizeof(identifier), output);
37 } 37 if (strcasecmp(identifier, name_or_id) == 0
38 } 38 || strcasecmp(output->wlr_output->name, name_or_id) == 0) {
39 return NULL;
40}
41
42struct sway_output *output_by_identifier(const char *identifier) {
43 for (int i = 0; i < root->outputs->length; ++i) {
44 struct sway_output *output = root->outputs->items[i];
45 char output_identifier[128];
46 snprintf(output_identifier, sizeof(output_identifier), "%s %s %s", output->wlr_output->make,
47 output->wlr_output->model, output->wlr_output->serial);
48 if (strcasecmp(output_identifier, identifier) == 0) {
49 return output; 39 return output;
50 } 40 }
51 } 41 }
@@ -110,7 +100,7 @@ static bool get_surface_box(struct surface_iterator_data *data,
110 } 100 }
111 101
112 struct wlr_box rotated_box; 102 struct wlr_box rotated_box;
113 wlr_box_rotated_bounds(&box, data->rotation, &rotated_box); 103 wlr_box_rotated_bounds(&rotated_box, &box, data->rotation);
114 104
115 struct wlr_box output_box = { 105 struct wlr_box output_box = {
116 .width = output->width, 106 .width = output->width,
@@ -118,7 +108,7 @@ static bool get_surface_box(struct surface_iterator_data *data,
118 }; 108 };
119 109
120 struct wlr_box intersection; 110 struct wlr_box intersection;
121 return wlr_box_intersection(&output_box, &rotated_box, &intersection); 111 return wlr_box_intersection(&intersection, &output_box, &rotated_box);
122} 112}
123 113
124static void output_for_each_surface_iterator(struct wlr_surface *surface, 114static void output_for_each_surface_iterator(struct wlr_surface *surface,
@@ -310,7 +300,7 @@ static int scale_length(int length, int offset, float scale) {
310 return round((offset + length) * scale) - round(offset * scale); 300 return round((offset + length) * scale) - round(offset * scale);
311} 301}
312 302
313static void scale_box(struct wlr_box *box, float scale) { 303void scale_box(struct wlr_box *box, float scale) {
314 box->width = scale_length(box->width, box->x, scale); 304 box->width = scale_length(box->width, box->x, scale);
315 box->height = scale_length(box->height, box->y, scale); 305 box->height = scale_length(box->height, box->y, scale);
316 box->x = round(box->x * scale); 306 box->x = round(box->x * scale);
@@ -433,7 +423,7 @@ static void damage_surface_iterator(struct sway_output *output,
433 } 423 }
434 424
435 if (whole) { 425 if (whole) {
436 wlr_box_rotated_bounds(&box, rotation, &box); 426 wlr_box_rotated_bounds(&box, &box, rotation);
437 wlr_output_damage_add_box(output->damage, &box); 427 wlr_output_damage_add_box(output->damage, &box);
438 } 428 }
439 429
@@ -568,6 +558,8 @@ void handle_new_output(struct wl_listener *listener, void *data) {
568 558
569 if (!oc || oc->enabled) { 559 if (!oc || oc->enabled) {
570 output_enable(output, oc); 560 output_enable(output, oc);
561 } else {
562 wlr_output_enable(output->wlr_output, false);
571 } 563 }
572 564
573 transaction_commit_dirty(); 565 transaction_commit_dirty();
diff --git a/sway/desktop/render.c b/sway/desktop/render.c
index 8d4a701b..a38c6a07 100644
--- a/sway/desktop/render.c
+++ b/sway/desktop/render.c
@@ -49,13 +49,6 @@ static int scale_length(int length, int offset, float scale) {
49 return round((offset + length) * scale) - round(offset * scale); 49 return round((offset + length) * scale) - round(offset * scale);
50} 50}
51 51
52static void scale_box(struct wlr_box *box, float scale) {
53 box->width = scale_length(box->width, box->x, scale);
54 box->height = scale_length(box->height, box->y, scale);
55 box->x = round(box->x * scale);
56 box->y = round(box->y * scale);
57}
58
59static void scissor_output(struct wlr_output *wlr_output, 52static void scissor_output(struct wlr_output *wlr_output,
60 pixman_box32_t *rect) { 53 pixman_box32_t *rect) {
61 struct wlr_renderer *renderer = wlr_backend_get_renderer(wlr_output->backend); 54 struct wlr_renderer *renderer = wlr_backend_get_renderer(wlr_output->backend);
@@ -73,7 +66,7 @@ static void scissor_output(struct wlr_output *wlr_output,
73 66
74 enum wl_output_transform transform = 67 enum wl_output_transform transform =
75 wlr_output_transform_invert(wlr_output->transform); 68 wlr_output_transform_invert(wlr_output->transform);
76 wlr_box_transform(&box, transform, ow, oh, &box); 69 wlr_box_transform(&box, &box, transform, ow, oh);
77 70
78 wlr_renderer_scissor(renderer, &box); 71 wlr_renderer_scissor(renderer, &box);
79} 72}
@@ -164,7 +157,7 @@ static void render_drag_icons(struct sway_output *output,
164 157
165// _box.x and .y are expected to be layout-local 158// _box.x and .y are expected to be layout-local
166// _box.width and .height are expected to be output-buffer-local 159// _box.width and .height are expected to be output-buffer-local
167static void render_rect(struct wlr_output *wlr_output, 160void render_rect(struct wlr_output *wlr_output,
168 pixman_region32_t *output_damage, const struct wlr_box *_box, 161 pixman_region32_t *output_damage, const struct wlr_box *_box,
169 float color[static 4]) { 162 float color[static 4]) {
170 struct wlr_renderer *renderer = 163 struct wlr_renderer *renderer =
@@ -197,7 +190,7 @@ damage_finish:
197 pixman_region32_fini(&damage); 190 pixman_region32_fini(&damage);
198} 191}
199 192
200static void premultiply_alpha(float color[4], float opacity) { 193void premultiply_alpha(float color[4], float opacity) {
201 color[3] *= opacity; 194 color[3] *= opacity;
202 color[0] *= color[3]; 195 color[0] *= color[3];
203 color[1] *= color[3]; 196 color[1] *= color[3];
@@ -261,7 +254,7 @@ static void render_saved_view(struct sway_view *view,
261 }; 254 };
262 255
263 struct wlr_box intersection; 256 struct wlr_box intersection;
264 bool intersects = wlr_box_intersection(&output_box, &box, &intersection); 257 bool intersects = wlr_box_intersection(&intersection, &output_box, &box);
265 if (!intersects) { 258 if (!intersects) {
266 return; 259 return;
267 } 260 }
@@ -368,6 +361,10 @@ static void render_titlebar(struct sway_output *output,
368 children->items[children->length - 1] == con; 361 children->items[children->length - 1] == con;
369 double output_x = output->wlr_output->lx; 362 double output_x = output->wlr_output->lx;
370 double output_y = output->wlr_output->ly; 363 double output_y = output->wlr_output->ly;
364 int titlebar_border_thickness = config->titlebar_border_thickness;
365 int titlebar_h_padding = config->titlebar_h_padding;
366 int titlebar_v_padding = config->titlebar_v_padding;
367 enum alignment title_align = config->title_align;
371 368
372 // Single pixel bar above title 369 // Single pixel bar above title
373 memcpy(&color, colors->border, sizeof(float) * 4); 370 memcpy(&color, colors->border, sizeof(float) * 4);
@@ -375,7 +372,7 @@ static void render_titlebar(struct sway_output *output,
375 box.x = x; 372 box.x = x;
376 box.y = y; 373 box.y = y;
377 box.width = width; 374 box.width = width;
378 box.height = TITLEBAR_BORDER_THICKNESS; 375 box.height = titlebar_border_thickness;
379 scale_box(&box, output_scale); 376 scale_box(&box, output_scale);
380 render_rect(output->wlr_output, output_damage, &box, color); 377 render_rect(output->wlr_output, output_damage, &box, color);
381 378
@@ -391,45 +388,51 @@ static void render_titlebar(struct sway_output *output,
391 } 388 }
392 } 389 }
393 box.x = x + left_offset; 390 box.x = x + left_offset;
394 box.y = y + container_titlebar_height() - TITLEBAR_BORDER_THICKNESS; 391 box.y = y + container_titlebar_height() - titlebar_border_thickness;
395 box.width = width - left_offset - right_offset; 392 box.width = width - left_offset - right_offset;
396 box.height = TITLEBAR_BORDER_THICKNESS; 393 box.height = titlebar_border_thickness;
397 scale_box(&box, output_scale); 394 scale_box(&box, output_scale);
398 render_rect(output->wlr_output, output_damage, &box, color); 395 render_rect(output->wlr_output, output_damage, &box, color);
399 396
400 if (layout == L_TABBED) { 397 if (layout == L_TABBED) {
401 // Single pixel left edge 398 // Single pixel left edge
402 box.x = x; 399 box.x = x;
403 box.y = y + TITLEBAR_BORDER_THICKNESS; 400 box.y = y + titlebar_border_thickness;
404 box.width = TITLEBAR_BORDER_THICKNESS; 401 box.width = titlebar_border_thickness;
405 box.height = 402 box.height =
406 container_titlebar_height() - TITLEBAR_BORDER_THICKNESS * 2; 403 container_titlebar_height() - titlebar_border_thickness * 2;
407 scale_box(&box, output_scale); 404 scale_box(&box, output_scale);
408 render_rect(output->wlr_output, output_damage, &box, color); 405 render_rect(output->wlr_output, output_damage, &box, color);
409 406
410 // Single pixel right edge 407 // Single pixel right edge
411 box.x = x + width - TITLEBAR_BORDER_THICKNESS; 408 box.x = x + width - titlebar_border_thickness;
412 box.y = y + TITLEBAR_BORDER_THICKNESS; 409 box.y = y + titlebar_border_thickness;
413 box.width = TITLEBAR_BORDER_THICKNESS; 410 box.width = titlebar_border_thickness;
414 box.height = 411 box.height =
415 container_titlebar_height() - TITLEBAR_BORDER_THICKNESS * 2; 412 container_titlebar_height() - titlebar_border_thickness * 2;
416 scale_box(&box, output_scale); 413 scale_box(&box, output_scale);
417 render_rect(output->wlr_output, output_damage, &box, color); 414 render_rect(output->wlr_output, output_damage, &box, color);
418 } 415 }
419 416
420 size_t inner_width = width - TITLEBAR_H_PADDING * 2; 417 int inner_x = x - output_x + titlebar_h_padding;
421 int bg_y = y + TITLEBAR_BORDER_THICKNESS; 418 int bg_y = y + titlebar_border_thickness;
419 size_t inner_width = width - titlebar_h_padding * 2;
420
421 // output-buffer local
422 int ob_inner_x = round(inner_x * output_scale);
423 int ob_inner_width = scale_length(inner_width, inner_x, output_scale);
422 int ob_bg_height = scale_length( 424 int ob_bg_height = scale_length(
423 (TITLEBAR_V_PADDING - TITLEBAR_BORDER_THICKNESS) * 2 + 425 (titlebar_v_padding - titlebar_border_thickness) * 2 +
424 config->font_height, bg_y, output_scale); 426 config->font_height, bg_y, output_scale);
425 427
426 // Marks 428 // Marks
427 int marks_ob_width = 0; // output-buffer-local 429 int ob_marks_x = 0; // output-buffer-local
430 int ob_marks_width = 0; // output-buffer-local
428 if (config->show_marks && marks_texture) { 431 if (config->show_marks && marks_texture) {
429 struct wlr_box texture_box; 432 struct wlr_box texture_box;
430 wlr_texture_get_size(marks_texture, 433 wlr_texture_get_size(marks_texture,
431 &texture_box.width, &texture_box.height); 434 &texture_box.width, &texture_box.height);
432 marks_ob_width = texture_box.width; 435 ob_marks_width = texture_box.width;
433 436
434 // The marks texture might be shorter than the config->font_height, in 437 // The marks texture might be shorter than the config->font_height, in
435 // which case we need to pad it as evenly as possible above and below. 438 // which case we need to pad it as evenly as possible above and below.
@@ -437,9 +440,15 @@ static void render_titlebar(struct sway_output *output,
437 int ob_padding_above = floor(ob_padding_total / 2.0); 440 int ob_padding_above = floor(ob_padding_total / 2.0);
438 int ob_padding_below = ceil(ob_padding_total / 2.0); 441 int ob_padding_below = ceil(ob_padding_total / 2.0);
439 442
440 // Render texture 443 // Render texture. If the title is on the right, the marks will be on
441 texture_box.x = round((x - output_x + width - TITLEBAR_H_PADDING) 444 // the left. Otherwise, they will be on the right.
442 * output_scale) - texture_box.width; 445 if (title_align == ALIGN_RIGHT || texture_box.width > ob_inner_width) {
446 texture_box.x = ob_inner_x;
447 } else {
448 texture_box.x = ob_inner_x + ob_inner_width - texture_box.width;
449 }
450 ob_marks_x = texture_box.x;
451
443 texture_box.y = round((bg_y - output_y) * output_scale) + 452 texture_box.y = round((bg_y - output_y) * output_scale) +
444 ob_padding_above; 453 ob_padding_above;
445 454
@@ -448,8 +457,8 @@ static void render_titlebar(struct sway_output *output,
448 WL_OUTPUT_TRANSFORM_NORMAL, 457 WL_OUTPUT_TRANSFORM_NORMAL,
449 0.0, output->wlr_output->transform_matrix); 458 0.0, output->wlr_output->transform_matrix);
450 459
451 if (inner_width * output_scale < texture_box.width) { 460 if (ob_inner_width < texture_box.width) {
452 texture_box.width = inner_width * output_scale; 461 texture_box.width = ob_inner_width;
453 } 462 }
454 render_texture(output->wlr_output, output_damage, marks_texture, 463 render_texture(output->wlr_output, output_damage, marks_texture,
455 &texture_box, matrix, con->alpha); 464 &texture_box, matrix, con->alpha);
@@ -458,7 +467,7 @@ static void render_titlebar(struct sway_output *output,
458 memcpy(&color, colors->background, sizeof(float) * 4); 467 memcpy(&color, colors->background, sizeof(float) * 4);
459 premultiply_alpha(color, con->alpha); 468 premultiply_alpha(color, con->alpha);
460 box.x = texture_box.x + round(output_x * output_scale); 469 box.x = texture_box.x + round(output_x * output_scale);
461 box.y = round((y + TITLEBAR_BORDER_THICKNESS) * output_scale); 470 box.y = round((y + titlebar_border_thickness) * output_scale);
462 box.width = texture_box.width; 471 box.width = texture_box.width;
463 box.height = ob_padding_above; 472 box.height = ob_padding_above;
464 render_rect(output->wlr_output, output_damage, &box, color); 473 render_rect(output->wlr_output, output_damage, &box, color);
@@ -470,24 +479,43 @@ static void render_titlebar(struct sway_output *output,
470 } 479 }
471 480
472 // Title text 481 // Title text
473 size_t title_ob_width = 0; // output-buffer-local 482 int ob_title_x = 0; // output-buffer-local
483 int ob_title_width = 0; // output-buffer-local
474 if (title_texture) { 484 if (title_texture) {
475 struct wlr_box texture_box; 485 struct wlr_box texture_box;
476 wlr_texture_get_size(title_texture, 486 wlr_texture_get_size(title_texture,
477 &texture_box.width, &texture_box.height); 487 &texture_box.width, &texture_box.height);
478 title_ob_width = texture_box.width; 488 ob_title_width = texture_box.width;
479 489
480 // The title texture might be shorter than the config->font_height, 490 // The title texture might be shorter than the config->font_height,
481 // in which case we need to pad it above and below. 491 // in which case we need to pad it above and below.
482 int ob_padding_above = round((config->font_baseline - 492 int ob_padding_above = round((config->font_baseline -
483 con->title_baseline + TITLEBAR_V_PADDING - 493 con->title_baseline + titlebar_v_padding -
484 TITLEBAR_BORDER_THICKNESS) * output_scale); 494 titlebar_border_thickness) * output_scale);
485 int ob_padding_below = ob_bg_height - ob_padding_above - 495 int ob_padding_below = ob_bg_height - ob_padding_above -
486 texture_box.height; 496 texture_box.height;
487 497
488 // Render texture 498 // Render texture
489 texture_box.x = 499 if (texture_box.width > ob_inner_width - ob_marks_width) {
490 round((x - output_x + TITLEBAR_H_PADDING) * output_scale); 500 texture_box.x = (title_align == ALIGN_RIGHT && ob_marks_width)
501 ? ob_marks_x + ob_marks_width : ob_inner_x;
502 } else if (title_align == ALIGN_LEFT) {
503 texture_box.x = ob_inner_x;
504 } else if (title_align == ALIGN_CENTER) {
505 // If there are marks visible, center between the edge and marks.
506 // Otherwise, center in the inner area.
507 if (ob_marks_width) {
508 texture_box.x = (ob_inner_x + ob_marks_x) / 2
509 - texture_box.width / 2;
510 } else {
511 texture_box.x = ob_inner_x + ob_inner_width / 2
512 - texture_box.width / 2;
513 }
514 } else {
515 texture_box.x = ob_inner_x + ob_inner_width - texture_box.width;
516 }
517 ob_title_x = texture_box.x;
518
491 texture_box.y = 519 texture_box.y =
492 round((bg_y - output_y) * output_scale) + ob_padding_above; 520 round((bg_y - output_y) * output_scale) + ob_padding_above;
493 521
@@ -496,11 +524,10 @@ static void render_titlebar(struct sway_output *output,
496 WL_OUTPUT_TRANSFORM_NORMAL, 524 WL_OUTPUT_TRANSFORM_NORMAL,
497 0.0, output->wlr_output->transform_matrix); 525 0.0, output->wlr_output->transform_matrix);
498 526
499 int inner_x = x - output_x + TITLEBAR_H_PADDING; 527 if (ob_inner_width - ob_marks_width < texture_box.width) {
500 int ob_inner_width = scale_length(inner_width, inner_x, output_scale); 528 texture_box.width = ob_inner_width - ob_marks_width;
501 if (ob_inner_width - marks_ob_width < texture_box.width) {
502 texture_box.width = ob_inner_width - marks_ob_width;
503 } 529 }
530
504 render_texture(output->wlr_output, output_damage, title_texture, 531 render_texture(output->wlr_output, output_damage, title_texture,
505 &texture_box, matrix, con->alpha); 532 &texture_box, matrix, con->alpha);
506 533
@@ -508,7 +535,7 @@ static void render_titlebar(struct sway_output *output,
508 memcpy(&color, colors->background, sizeof(float) * 4); 535 memcpy(&color, colors->background, sizeof(float) * 4);
509 premultiply_alpha(color, con->alpha); 536 premultiply_alpha(color, con->alpha);
510 box.x = texture_box.x + round(output_x * output_scale); 537 box.x = texture_box.x + round(output_x * output_scale);
511 box.y = round((y + TITLEBAR_BORDER_THICKNESS) * output_scale); 538 box.y = round((y + titlebar_border_thickness) * output_scale);
512 box.width = texture_box.width; 539 box.width = texture_box.width;
513 box.height = ob_padding_above; 540 box.height = ob_padding_above;
514 render_rect(output->wlr_output, output_damage, &box, color); 541 render_rect(output->wlr_output, output_damage, &box, color);
@@ -519,50 +546,83 @@ static void render_titlebar(struct sway_output *output,
519 render_rect(output->wlr_output, output_damage, &box, color); 546 render_rect(output->wlr_output, output_damage, &box, color);
520 } 547 }
521 548
549 // Determine the left + right extends of the textures (output-buffer local)
550 int ob_left_x, ob_left_width, ob_right_x, ob_right_width;
551 if (ob_title_width == 0 && ob_marks_width == 0) {
552 ob_left_x = ob_inner_x;
553 ob_left_width = 0;
554 ob_right_x = ob_inner_x;
555 ob_right_width = 0;
556 } else if (ob_title_x < ob_marks_x) {
557 ob_left_x = ob_title_x;
558 ob_left_width = ob_title_width;
559 ob_right_x = ob_marks_x;
560 ob_right_width = ob_marks_width;
561 } else {
562 ob_left_x = ob_marks_x;
563 ob_left_width = ob_marks_width;
564 ob_right_x = ob_title_x;
565 ob_right_width = ob_title_width;
566 }
567 if (ob_left_x < ob_inner_x) {
568 ob_left_x = ob_inner_x;
569 } else if (ob_left_x + ob_left_width > ob_right_x + ob_right_width) {
570 ob_right_x = ob_left_x;
571 ob_right_width = ob_left_width;
572 }
573
522 // Filler between title and marks 574 // Filler between title and marks
523 box.width = 575 box.width = ob_right_x - ob_left_x - ob_left_width;
524 round(inner_width * output_scale) - title_ob_width - marks_ob_width;
525 if (box.width > 0) { 576 if (box.width > 0) {
526 box.x = round((x + TITLEBAR_H_PADDING) * output_scale) + title_ob_width; 577 box.x = ob_left_x + ob_left_width + round(output_x * output_scale);
527 box.y = round(bg_y * output_scale); 578 box.y = round(bg_y * output_scale);
528 box.height = ob_bg_height; 579 box.height = ob_bg_height;
529 render_rect(output->wlr_output, output_damage, &box, color); 580 render_rect(output->wlr_output, output_damage, &box, color);
530 } 581 }
531 582
532 // Padding left of title 583 // Padding on left side
533 left_offset = (layout == L_TABBED) * TITLEBAR_BORDER_THICKNESS; 584 left_offset = (layout == L_TABBED) * titlebar_border_thickness;
534 box.x = x + left_offset; 585 box.x = x + left_offset;
535 box.y = y + TITLEBAR_BORDER_THICKNESS; 586 box.y = y + titlebar_border_thickness;
536 box.width = TITLEBAR_H_PADDING - left_offset; 587 box.width = titlebar_h_padding - left_offset;
537 box.height = (TITLEBAR_V_PADDING - TITLEBAR_BORDER_THICKNESS) * 2 + 588 box.height = (titlebar_v_padding - titlebar_border_thickness) * 2 +
538 config->font_height; 589 config->font_height;
539 scale_box(&box, output_scale); 590 scale_box(&box, output_scale);
591 int left_x = ob_left_x + round(output_x * output_scale);
592 if (box.x + box.width < left_x) {
593 box.width += left_x - box.x - box.width;
594 }
540 render_rect(output->wlr_output, output_damage, &box, color); 595 render_rect(output->wlr_output, output_damage, &box, color);
541 596
542 // Padding right of marks 597 // Padding on right side
543 right_offset = (layout == L_TABBED) * TITLEBAR_BORDER_THICKNESS; 598 right_offset = (layout == L_TABBED) * titlebar_border_thickness;
544 box.x = x + width - TITLEBAR_H_PADDING; 599 box.x = x + width - titlebar_h_padding;
545 box.y = y + TITLEBAR_BORDER_THICKNESS; 600 box.y = y + titlebar_border_thickness;
546 box.width = TITLEBAR_H_PADDING - right_offset; 601 box.width = titlebar_h_padding - right_offset;
547 box.height = (TITLEBAR_V_PADDING - TITLEBAR_BORDER_THICKNESS) * 2 + 602 box.height = (titlebar_v_padding - titlebar_border_thickness) * 2 +
548 config->font_height; 603 config->font_height;
549 scale_box(&box, output_scale); 604 scale_box(&box, output_scale);
605 int right_rx = ob_right_x + ob_right_width + round(output_x * output_scale);
606 if (right_rx < box.x) {
607 box.width += box.x - right_rx;
608 box.x = right_rx;
609 }
550 render_rect(output->wlr_output, output_damage, &box, color); 610 render_rect(output->wlr_output, output_damage, &box, color);
551 611
552 if (connects_sides) { 612 if (connects_sides) {
553 // Left pixel in line with bottom bar 613 // Left pixel in line with bottom bar
554 box.x = x; 614 box.x = x;
555 box.y = y + container_titlebar_height() - TITLEBAR_BORDER_THICKNESS; 615 box.y = y + container_titlebar_height() - titlebar_border_thickness;
556 box.width = state->border_thickness * state->border_left; 616 box.width = state->border_thickness * state->border_left;
557 box.height = TITLEBAR_BORDER_THICKNESS; 617 box.height = titlebar_border_thickness;
558 scale_box(&box, output_scale); 618 scale_box(&box, output_scale);
559 render_rect(output->wlr_output, output_damage, &box, color); 619 render_rect(output->wlr_output, output_damage, &box, color);
560 620
561 // Right pixel in line with bottom bar 621 // Right pixel in line with bottom bar
562 box.x = x + width - state->border_thickness * state->border_right; 622 box.x = x + width - state->border_thickness * state->border_right;
563 box.y = y + container_titlebar_height() - TITLEBAR_BORDER_THICKNESS; 623 box.y = y + container_titlebar_height() - titlebar_border_thickness;
564 box.width = state->border_thickness * state->border_right; 624 box.width = state->border_thickness * state->border_right;
565 box.height = TITLEBAR_BORDER_THICKNESS; 625 box.height = titlebar_border_thickness;
566 scale_box(&box, output_scale); 626 scale_box(&box, output_scale);
567 render_rect(output->wlr_output, output_damage, &box, color); 627 render_rect(output->wlr_output, output_damage, &box, color);
568 } 628 }
@@ -882,21 +942,11 @@ static void render_floating(struct sway_output *soutput,
882 } 942 }
883} 943}
884 944
885static void render_dropzones(struct sway_output *output, 945static void render_seatops(struct sway_output *output,
886 pixman_region32_t *damage) { 946 pixman_region32_t *damage) {
887 struct sway_seat *seat; 947 struct sway_seat *seat;
888 wl_list_for_each(seat, &server.input->seats, link) { 948 wl_list_for_each(seat, &server.input->seats, link) {
889 if (seat->operation == OP_MOVE_TILING && seat->op_target_node 949 seatop_render(seat, output, damage);
890 && node_get_output(seat->op_target_node) == output) {
891 float color[4];
892 memcpy(&color, config->border_colors.focused.indicator,
893 sizeof(float) * 4);
894 premultiply_alpha(color, 0.5);
895 struct wlr_box box;
896 memcpy(&box, &seat->op_drop_box, sizeof(struct wlr_box));
897 scale_box(&box, output->wlr_output->scale);
898 render_rect(output->wlr_output, damage, &box, color);
899 }
900 } 950 }
901} 951}
902 952
@@ -950,7 +1000,7 @@ void output_render(struct sway_output *output, struct timespec *when,
950 if (fullscreen_con->view) { 1000 if (fullscreen_con->view) {
951 if (fullscreen_con->view->saved_buffer) { 1001 if (fullscreen_con->view->saved_buffer) {
952 render_saved_view(fullscreen_con->view, output, damage, 1.0f); 1002 render_saved_view(fullscreen_con->view, output, damage, 1.0f);
953 } else { 1003 } else if (fullscreen_con->view->surface) {
954 render_view_toplevels(fullscreen_con->view, 1004 render_view_toplevels(fullscreen_con->view,
955 output, damage, 1.0f); 1005 output, damage, 1.0f);
956 } 1006 }
@@ -993,7 +1043,7 @@ void output_render(struct sway_output *output, struct timespec *when,
993 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]); 1043 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]);
994 } 1044 }
995 1045
996 render_dropzones(output, damage); 1046 render_seatops(output, damage);
997 1047
998 struct sway_seat *seat = input_manager_current_seat(); 1048 struct sway_seat *seat = input_manager_current_seat();
999 struct sway_container *focus = seat_get_focused_container(seat); 1049 struct sway_container *focus = seat_get_focused_container(seat);
@@ -1012,15 +1062,22 @@ renderer_end:
1012 wlr_render_texture(renderer, root->debug_tree, 1062 wlr_render_texture(renderer, root->debug_tree,
1013 wlr_output->transform_matrix, 0, 40, 1); 1063 wlr_output->transform_matrix, 0, 40, 1);
1014 } 1064 }
1015 if (debug.damage == DAMAGE_HIGHLIGHT) {
1016 int width, height;
1017 wlr_output_transformed_resolution(wlr_output, &width, &height);
1018 pixman_region32_union_rect(damage, damage, 0, 0, width, height);
1019 }
1020 1065
1021 wlr_renderer_scissor(renderer, NULL); 1066 wlr_renderer_scissor(renderer, NULL);
1022 wlr_output_render_software_cursors(wlr_output, damage); 1067 wlr_output_render_software_cursors(wlr_output, damage);
1023 wlr_renderer_end(renderer); 1068 wlr_renderer_end(renderer);
1069
1070 int width, height;
1071 wlr_output_transformed_resolution(wlr_output, &width, &height);
1072
1073 if (debug.damage == DAMAGE_HIGHLIGHT) {
1074 pixman_region32_union_rect(damage, damage, 0, 0, width, height);
1075 }
1076
1077 enum wl_output_transform transform =
1078 wlr_output_transform_invert(wlr_output->transform);
1079 wlr_region_transform(damage, damage, transform, width, height);
1080
1024 if (!wlr_output_damage_swap_buffers(output->damage, when, damage)) { 1081 if (!wlr_output_damage_swap_buffers(output->damage, when, damage)) {
1025 return; 1082 return;
1026 } 1083 }
diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c
index bf0038b4..1cdd7c6d 100644
--- a/sway/desktop/transaction.c
+++ b/sway/desktop/transaction.c
@@ -299,7 +299,7 @@ static void transaction_apply(struct sway_transaction *transaction) {
299 if (root->outputs->length) { 299 if (root->outputs->length) {
300 struct sway_seat *seat; 300 struct sway_seat *seat;
301 wl_list_for_each(seat, &server.input->seats, link) { 301 wl_list_for_each(seat, &server.input->seats, link) {
302 if (seat->operation == OP_NONE) { 302 if (!seat_doing_seatop(seat)) {
303 cursor_rebase(seat->cursor); 303 cursor_rebase(seat->cursor);
304 } 304 }
305 } 305 }
@@ -363,7 +363,7 @@ static void transaction_progress_queue(void) {
363 363
364static int handle_timeout(void *data) { 364static int handle_timeout(void *data) {
365 struct sway_transaction *transaction = data; 365 struct sway_transaction *transaction = data;
366 wlr_log(WLR_DEBUG, "Transaction %p timed out (%li waiting)", 366 wlr_log(WLR_DEBUG, "Transaction %p timed out (%zi waiting)",
367 transaction, transaction->num_waiting); 367 transaction, transaction->num_waiting);
368 transaction->num_waiting = 0; 368 transaction->num_waiting = 0;
369 transaction_progress_queue(); 369 transaction_progress_queue();
@@ -472,7 +472,7 @@ static void set_instruction_ready(
472 struct timespec *start = &transaction->commit_time; 472 struct timespec *start = &transaction->commit_time;
473 float ms = (now.tv_sec - start->tv_sec) * 1000 + 473 float ms = (now.tv_sec - start->tv_sec) * 1000 +
474 (now.tv_nsec - start->tv_nsec) / 1000000.0; 474 (now.tv_nsec - start->tv_nsec) / 1000000.0;
475 wlr_log(WLR_DEBUG, "Transaction %p: %li/%li ready in %.1fms (%s)", 475 wlr_log(WLR_DEBUG, "Transaction %p: %zi/%zi ready in %.1fms (%s)",
476 transaction, 476 transaction,
477 transaction->num_configures - transaction->num_waiting + 1, 477 transaction->num_configures - transaction->num_waiting + 1,
478 transaction->num_configures, ms, 478 transaction->num_configures, ms,
diff --git a/sway/desktop/xdg_shell.c b/sway/desktop/xdg_shell.c
index 801dcee0..f05e156f 100644
--- a/sway/desktop/xdg_shell.c
+++ b/sway/desktop/xdg_shell.c
@@ -360,7 +360,7 @@ static void handle_request_move(struct wl_listener *listener, void *data) {
360 struct wlr_xdg_toplevel_move_event *e = data; 360 struct wlr_xdg_toplevel_move_event *e = data;
361 struct sway_seat *seat = e->seat->seat->data; 361 struct sway_seat *seat = e->seat->seat->data;
362 if (e->serial == seat->last_button_serial) { 362 if (e->serial == seat->last_button_serial) {
363 seat_begin_move_floating(seat, view->container, seat->last_button); 363 seatop_begin_move_floating(seat, view->container, seat->last_button);
364 } 364 }
365} 365}
366 366
@@ -374,7 +374,7 @@ static void handle_request_resize(struct wl_listener *listener, void *data) {
374 struct wlr_xdg_toplevel_resize_event *e = data; 374 struct wlr_xdg_toplevel_resize_event *e = data;
375 struct sway_seat *seat = e->seat->seat->data; 375 struct sway_seat *seat = e->seat->seat->data;
376 if (e->serial == seat->last_button_serial) { 376 if (e->serial == seat->last_button_serial) {
377 seat_begin_resize_floating(seat, view->container, 377 seatop_begin_resize_floating(seat, view->container,
378 seat->last_button, e->edges); 378 seat->last_button, e->edges);
379 } 379 }
380} 380}
diff --git a/sway/desktop/xdg_shell_v6.c b/sway/desktop/xdg_shell_v6.c
index 4bc83b8e..9f6741c8 100644
--- a/sway/desktop/xdg_shell_v6.c
+++ b/sway/desktop/xdg_shell_v6.c
@@ -357,7 +357,7 @@ static void handle_request_move(struct wl_listener *listener, void *data) {
357 struct wlr_xdg_toplevel_v6_move_event *e = data; 357 struct wlr_xdg_toplevel_v6_move_event *e = data;
358 struct sway_seat *seat = e->seat->seat->data; 358 struct sway_seat *seat = e->seat->seat->data;
359 if (e->serial == seat->last_button_serial) { 359 if (e->serial == seat->last_button_serial) {
360 seat_begin_move_floating(seat, view->container, seat->last_button); 360 seatop_begin_move_floating(seat, view->container, seat->last_button);
361 } 361 }
362} 362}
363 363
@@ -371,7 +371,7 @@ static void handle_request_resize(struct wl_listener *listener, void *data) {
371 struct wlr_xdg_toplevel_v6_resize_event *e = data; 371 struct wlr_xdg_toplevel_v6_resize_event *e = data;
372 struct sway_seat *seat = e->seat->seat->data; 372 struct sway_seat *seat = e->seat->seat->data;
373 if (e->serial == seat->last_button_serial) { 373 if (e->serial == seat->last_button_serial) {
374 seat_begin_resize_floating(seat, view->container, 374 seatop_begin_resize_floating(seat, view->container,
375 seat->last_button, e->edges); 375 seat->last_button, e->edges);
376 } 376 }
377} 377}
diff --git a/sway/desktop/xwayland.c b/sway/desktop/xwayland.c
index 1838ad32..080f6c41 100644
--- a/sway/desktop/xwayland.c
+++ b/sway/desktop/xwayland.c
@@ -470,7 +470,7 @@ static void handle_request_move(struct wl_listener *listener, void *data) {
470 return; 470 return;
471 } 471 }
472 struct sway_seat *seat = input_manager_current_seat(); 472 struct sway_seat *seat = input_manager_current_seat();
473 seat_begin_move_floating(seat, view->container, seat->last_button); 473 seatop_begin_move_floating(seat, view->container, seat->last_button);
474} 474}
475 475
476static void handle_request_resize(struct wl_listener *listener, void *data) { 476static void handle_request_resize(struct wl_listener *listener, void *data) {
@@ -486,7 +486,7 @@ static void handle_request_resize(struct wl_listener *listener, void *data) {
486 } 486 }
487 struct wlr_xwayland_resize_event *e = data; 487 struct wlr_xwayland_resize_event *e = data;
488 struct sway_seat *seat = input_manager_current_seat(); 488 struct sway_seat *seat = input_manager_current_seat();
489 seat_begin_resize_floating(seat, view->container, 489 seatop_begin_resize_floating(seat, view->container,
490 seat->last_button, e->edges); 490 seat->last_button, e->edges);
491} 491}
492 492
diff --git a/sway/input/cursor.c b/sway/input/cursor.c
index d89f64d8..08222494 100644
--- a/sway/input/cursor.c
+++ b/sway/input/cursor.c
@@ -1,12 +1,11 @@
1#define _XOPEN_SOURCE 700 1#define _POSIX_C_SOURCE 200809L
2#include <math.h> 2#include <math.h>
3#ifdef __linux__ 3#include <libevdev/libevdev.h>
4#include <linux/input-event-codes.h> 4#include <linux/input-event-codes.h>
5#elif __FreeBSD__ 5#include <errno.h>
6#include <dev/evdev/input-event-codes.h>
7#endif
8#include <float.h> 6#include <float.h>
9#include <limits.h> 7#include <limits.h>
8#include <strings.h>
10#include <wlr/types/wlr_cursor.h> 9#include <wlr/types/wlr_cursor.h>
11#include <wlr/types/wlr_xcursor_manager.h> 10#include <wlr/types/wlr_xcursor_manager.h>
12#include <wlr/types/wlr_idle.h> 11#include <wlr/types/wlr_idle.h>
@@ -28,10 +27,6 @@
28#include "sway/tree/workspace.h" 27#include "sway/tree/workspace.h"
29#include "wlr-layer-shell-unstable-v1-protocol.h" 28#include "wlr-layer-shell-unstable-v1-protocol.h"
30 29
31// When doing a tiling drag, this is the thickness of the dropzone
32// when dragging to the edge of a layout container.
33#define DROP_LAYOUT_BORDER 30
34
35static uint32_t get_current_time_msec(void) { 30static uint32_t get_current_time_msec(void) {
36 struct timespec now; 31 struct timespec now;
37 clock_gettime(CLOCK_MONOTONIC, &now); 32 clock_gettime(CLOCK_MONOTONIC, &now);
@@ -60,7 +55,7 @@ static struct wlr_surface *layer_surface_at(struct sway_output *output,
60 * Returns the node at the cursor's position. If there is a surface at that 55 * Returns the node at the cursor's position. If there is a surface at that
61 * location, it is stored in **surface (it may not be a view). 56 * location, it is stored in **surface (it may not be a view).
62 */ 57 */
63static struct sway_node *node_at_coords( 58struct sway_node *node_at_coords(
64 struct sway_seat *seat, double lx, double ly, 59 struct sway_seat *seat, double lx, double ly,
65 struct wlr_surface **surface, double *sx, double *sy) { 60 struct wlr_surface **surface, double *sx, double *sy) {
66 // check for unmanaged views first 61 // check for unmanaged views first
@@ -88,6 +83,10 @@ static struct sway_node *node_at_coords(
88 return NULL; 83 return NULL;
89 } 84 }
90 struct sway_output *output = wlr_output->data; 85 struct sway_output *output = wlr_output->data;
86 if (!output) {
87 // output is being destroyed
88 return NULL;
89 }
91 double ox = lx, oy = ly; 90 double ox = lx, oy = ly;
92 wlr_output_layout_output_coords(root->output_layout, wlr_output, &ox, &oy); 91 wlr_output_layout_output_coords(root->output_layout, wlr_output, &ox, &oy);
93 92
@@ -223,323 +222,6 @@ static enum wlr_edges find_resize_edge(struct sway_container *cont,
223 return edge; 222 return edge;
224} 223}
225 224
226static void handle_down_motion(struct sway_seat *seat,
227 struct sway_cursor *cursor, uint32_t time_msec) {
228 struct sway_container *con = seat->op_container;
229 if (seat_is_input_allowed(seat, con->view->surface)) {
230 double moved_x = cursor->cursor->x - seat->op_ref_lx;
231 double moved_y = cursor->cursor->y - seat->op_ref_ly;
232 double sx = seat->op_ref_con_lx + moved_x;
233 double sy = seat->op_ref_con_ly + moved_y;
234 wlr_seat_pointer_notify_motion(seat->wlr_seat, time_msec, sx, sy);
235 }
236 seat->op_moved = true;
237}
238
239static void handle_move_floating_motion(struct sway_seat *seat,
240 struct sway_cursor *cursor) {
241 struct sway_container *con = seat->op_container;
242 desktop_damage_whole_container(con);
243 container_floating_translate(con,
244 cursor->cursor->x - cursor->previous.x,
245 cursor->cursor->y - cursor->previous.y);
246 desktop_damage_whole_container(con);
247}
248
249static void resize_box(struct wlr_box *box, enum wlr_edges edge,
250 int thickness) {
251 switch (edge) {
252 case WLR_EDGE_TOP:
253 box->height = thickness;
254 break;
255 case WLR_EDGE_LEFT:
256 box->width = thickness;
257 break;
258 case WLR_EDGE_RIGHT:
259 box->x = box->x + box->width - thickness;
260 box->width = thickness;
261 break;
262 case WLR_EDGE_BOTTOM:
263 box->y = box->y + box->height - thickness;
264 box->height = thickness;
265 break;
266 case WLR_EDGE_NONE:
267 box->x += thickness;
268 box->y += thickness;
269 box->width -= thickness * 2;
270 box->height -= thickness * 2;
271 break;
272 }
273}
274
275static void handle_move_tiling_motion(struct sway_seat *seat,
276 struct sway_cursor *cursor) {
277 struct wlr_surface *surface = NULL;
278 double sx, sy;
279 struct sway_node *node = node_at_coords(seat,
280 cursor->cursor->x, cursor->cursor->y, &surface, &sx, &sy);
281 // Damage the old location
282 desktop_damage_box(&seat->op_drop_box);
283
284 if (!node) {
285 // Eg. hovered over a layer surface such as swaybar
286 seat->op_target_node = NULL;
287 seat->op_target_edge = WLR_EDGE_NONE;
288 return;
289 }
290
291 if (node->type == N_WORKSPACE) {
292 // Emtpy workspace
293 seat->op_target_node = node;
294 seat->op_target_edge = WLR_EDGE_NONE;
295 workspace_get_box(node->sway_workspace, &seat->op_drop_box);
296 desktop_damage_box(&seat->op_drop_box);
297 return;
298 }
299
300 // Deny moving within own workspace if this is the only child
301 struct sway_container *con = node->sway_container;
302 if (workspace_num_tiling_views(seat->op_container->workspace) == 1 &&
303 con->workspace == seat->op_container->workspace) {
304 seat->op_target_node = NULL;
305 seat->op_target_edge = WLR_EDGE_NONE;
306 return;
307 }
308
309 // Traverse the ancestors, trying to find a layout container perpendicular
310 // to the edge. Eg. close to the top or bottom of a horiz layout.
311 while (con) {
312 enum wlr_edges edge = WLR_EDGE_NONE;
313 enum sway_container_layout layout = container_parent_layout(con);
314 struct wlr_box parent;
315 con->parent ? container_get_box(con->parent, &parent) :
316 workspace_get_box(con->workspace, &parent);
317 if (layout == L_HORIZ || layout == L_TABBED) {
318 if (cursor->cursor->y < parent.y + DROP_LAYOUT_BORDER) {
319 edge = WLR_EDGE_TOP;
320 } else if (cursor->cursor->y > parent.y + parent.height
321 - DROP_LAYOUT_BORDER) {
322 edge = WLR_EDGE_BOTTOM;
323 }
324 } else if (layout == L_VERT || layout == L_STACKED) {
325 if (cursor->cursor->x < parent.x + DROP_LAYOUT_BORDER) {
326 edge = WLR_EDGE_LEFT;
327 } else if (cursor->cursor->x > parent.x + parent.width
328 - DROP_LAYOUT_BORDER) {
329 edge = WLR_EDGE_RIGHT;
330 }
331 }
332 if (edge) {
333 seat->op_target_node = node_get_parent(&con->node);
334 seat->op_target_edge = edge;
335 node_get_box(seat->op_target_node, &seat->op_drop_box);
336 resize_box(&seat->op_drop_box, edge, DROP_LAYOUT_BORDER);
337 desktop_damage_box(&seat->op_drop_box);
338 return;
339 }
340 con = con->parent;
341 }
342
343 // Use the hovered view - but we must be over the actual surface
344 con = node->sway_container;
345 if (!con->view->surface || node == &seat->op_container->node) {
346 seat->op_target_node = NULL;
347 seat->op_target_edge = WLR_EDGE_NONE;
348 return;
349 }
350
351 // Find the closest edge
352 size_t thickness = fmin(con->content_width, con->content_height) * 0.3;
353 size_t closest_dist = INT_MAX;
354 size_t dist;
355 seat->op_target_edge = WLR_EDGE_NONE;
356 if ((dist = cursor->cursor->y - con->y) < closest_dist) {
357 closest_dist = dist;
358 seat->op_target_edge = WLR_EDGE_TOP;
359 }
360 if ((dist = cursor->cursor->x - con->x) < closest_dist) {
361 closest_dist = dist;
362 seat->op_target_edge = WLR_EDGE_LEFT;
363 }
364 if ((dist = con->x + con->width - cursor->cursor->x) < closest_dist) {
365 closest_dist = dist;
366 seat->op_target_edge = WLR_EDGE_RIGHT;
367 }
368 if ((dist = con->y + con->height - cursor->cursor->y) < closest_dist) {
369 closest_dist = dist;
370 seat->op_target_edge = WLR_EDGE_BOTTOM;
371 }
372
373 if (closest_dist > thickness) {
374 seat->op_target_edge = WLR_EDGE_NONE;
375 }
376
377 seat->op_target_node = node;
378 seat->op_drop_box.x = con->content_x;
379 seat->op_drop_box.y = con->content_y;
380 seat->op_drop_box.width = con->content_width;
381 seat->op_drop_box.height = con->content_height;
382 resize_box(&seat->op_drop_box, seat->op_target_edge, thickness);
383 desktop_damage_box(&seat->op_drop_box);
384}
385
386static void calculate_floating_constraints(struct sway_container *con,
387 int *min_width, int *max_width, int *min_height, int *max_height) {
388 if (config->floating_minimum_width == -1) { // no minimum
389 *min_width = 0;
390 } else if (config->floating_minimum_width == 0) { // automatic
391 *min_width = 75;
392 } else {
393 *min_width = config->floating_minimum_width;
394 }
395
396 if (config->floating_minimum_height == -1) { // no minimum
397 *min_height = 0;
398 } else if (config->floating_minimum_height == 0) { // automatic
399 *min_height = 50;
400 } else {
401 *min_height = config->floating_minimum_height;
402 }
403
404 if (config->floating_maximum_width == -1) { // no maximum
405 *max_width = INT_MAX;
406 } else if (config->floating_maximum_width == 0) { // automatic
407 *max_width = con->workspace->width;
408 } else {
409 *max_width = config->floating_maximum_width;
410 }
411
412 if (config->floating_maximum_height == -1) { // no maximum
413 *max_height = INT_MAX;
414 } else if (config->floating_maximum_height == 0) { // automatic
415 *max_height = con->workspace->height;
416 } else {
417 *max_height = config->floating_maximum_height;
418 }
419}
420
421static void handle_resize_floating_motion(struct sway_seat *seat,
422 struct sway_cursor *cursor) {
423 struct sway_container *con = seat->op_container;
424 enum wlr_edges edge = seat->op_resize_edge;
425
426 // The amount the mouse has moved since the start of the resize operation
427 // Positive is down/right
428 double mouse_move_x = cursor->cursor->x - seat->op_ref_lx;
429 double mouse_move_y = cursor->cursor->y - seat->op_ref_ly;
430
431 if (edge == WLR_EDGE_TOP || edge == WLR_EDGE_BOTTOM) {
432 mouse_move_x = 0;
433 }
434 if (edge == WLR_EDGE_LEFT || edge == WLR_EDGE_RIGHT) {
435 mouse_move_y = 0;
436 }
437
438 double grow_width = edge & WLR_EDGE_LEFT ? -mouse_move_x : mouse_move_x;
439 double grow_height = edge & WLR_EDGE_TOP ? -mouse_move_y : mouse_move_y;
440
441 if (seat->op_resize_preserve_ratio) {
442 double x_multiplier = grow_width / seat->op_ref_width;
443 double y_multiplier = grow_height / seat->op_ref_height;
444 double max_multiplier = fmax(x_multiplier, y_multiplier);
445 grow_width = seat->op_ref_width * max_multiplier;
446 grow_height = seat->op_ref_height * max_multiplier;
447 }
448
449 // Determine new width/height, and accommodate for floating min/max values
450 double width = seat->op_ref_width + grow_width;
451 double height = seat->op_ref_height + grow_height;
452 int min_width, max_width, min_height, max_height;
453 calculate_floating_constraints(con, &min_width, &max_width,
454 &min_height, &max_height);
455 width = fmax(min_width, fmin(width, max_width));
456 height = fmax(min_height, fmin(height, max_height));
457
458 // Apply the view's min/max size
459 if (con->view) {
460 double view_min_width, view_max_width, view_min_height, view_max_height;
461 view_get_constraints(con->view, &view_min_width, &view_max_width,
462 &view_min_height, &view_max_height);
463 width = fmax(view_min_width, fmin(width, view_max_width));
464 height = fmax(view_min_height, fmin(height, view_max_height));
465 }
466
467 // Recalculate these, in case we hit a min/max limit
468 grow_width = width - seat->op_ref_width;
469 grow_height = height - seat->op_ref_height;
470
471 // Determine grow x/y values - these are relative to the container's x/y at
472 // the start of the resize operation.
473 double grow_x = 0, grow_y = 0;
474 if (edge & WLR_EDGE_LEFT) {
475 grow_x = -grow_width;
476 } else if (edge & WLR_EDGE_RIGHT) {
477 grow_x = 0;
478 } else {
479 grow_x = -grow_width / 2;
480 }
481 if (edge & WLR_EDGE_TOP) {
482 grow_y = -grow_height;
483 } else if (edge & WLR_EDGE_BOTTOM) {
484 grow_y = 0;
485 } else {
486 grow_y = -grow_height / 2;
487 }
488
489 // Determine the amounts we need to bump everything relative to the current
490 // size.
491 int relative_grow_width = width - con->width;
492 int relative_grow_height = height - con->height;
493 int relative_grow_x = (seat->op_ref_con_lx + grow_x) - con->x;
494 int relative_grow_y = (seat->op_ref_con_ly + grow_y) - con->y;
495
496 // Actually resize stuff
497 con->x += relative_grow_x;
498 con->y += relative_grow_y;
499 con->width += relative_grow_width;
500 con->height += relative_grow_height;
501
502 con->content_x += relative_grow_x;
503 con->content_y += relative_grow_y;
504 con->content_width += relative_grow_width;
505 con->content_height += relative_grow_height;
506
507 arrange_container(con);
508}
509
510static void handle_resize_tiling_motion(struct sway_seat *seat,
511 struct sway_cursor *cursor) {
512 int amount_x = 0;
513 int amount_y = 0;
514 int moved_x = cursor->cursor->x - seat->op_ref_lx;
515 int moved_y = cursor->cursor->y - seat->op_ref_ly;
516 enum wlr_edges edge_x = WLR_EDGE_NONE;
517 enum wlr_edges edge_y = WLR_EDGE_NONE;
518 struct sway_container *con = seat->op_container;
519
520 if (seat->op_resize_edge & WLR_EDGE_TOP) {
521 amount_y = (seat->op_ref_height - moved_y) - con->height;
522 edge_y = WLR_EDGE_TOP;
523 } else if (seat->op_resize_edge & WLR_EDGE_BOTTOM) {
524 amount_y = (seat->op_ref_height + moved_y) - con->height;
525 edge_y = WLR_EDGE_BOTTOM;
526 }
527 if (seat->op_resize_edge & WLR_EDGE_LEFT) {
528 amount_x = (seat->op_ref_width - moved_x) - con->width;
529 edge_x = WLR_EDGE_LEFT;
530 } else if (seat->op_resize_edge & WLR_EDGE_RIGHT) {
531 amount_x = (seat->op_ref_width + moved_x) - con->width;
532 edge_x = WLR_EDGE_RIGHT;
533 }
534
535 if (amount_x != 0) {
536 container_resize_tiled(seat->op_container, edge_x, amount_x);
537 }
538 if (amount_y != 0) {
539 container_resize_tiled(seat->op_container, edge_y, amount_y);
540 }
541}
542
543static void cursor_do_rebase(struct sway_cursor *cursor, uint32_t time_msec, 225static void cursor_do_rebase(struct sway_cursor *cursor, uint32_t time_msec,
544 struct sway_node *node, struct wlr_surface *surface, 226 struct sway_node *node, struct wlr_surface *surface,
545 double sx, double sy) { 227 double sx, double sy) {
@@ -589,6 +271,50 @@ void cursor_rebase(struct sway_cursor *cursor) {
589 cursor_do_rebase(cursor, time_msec, cursor->previous.node, surface, sx, sy); 271 cursor_do_rebase(cursor, time_msec, cursor->previous.node, surface, sx, sy);
590} 272}
591 273
274static int hide_notify(void *data) {
275 struct sway_cursor *cursor = data;
276 wlr_cursor_set_image(cursor->cursor, NULL, 0, 0, 0, 0, 0, 0);
277 cursor->hidden = true;
278 return 1;
279}
280
281int cursor_get_timeout(struct sway_cursor *cursor){
282 struct seat_config *sc = seat_get_config(cursor->seat);
283 if (!sc) {
284 sc = seat_get_config_by_name("*");
285 }
286 int timeout = sc ? sc->hide_cursor_timeout : 0;
287 if (timeout < 0) {
288 timeout = 0;
289 }
290 return timeout;
291}
292
293void cursor_handle_activity(struct sway_cursor *cursor) {
294 wl_event_source_timer_update(
295 cursor->hide_source, cursor_get_timeout(cursor));
296
297 wlr_idle_notify_activity(server.idle, cursor->seat->wlr_seat);
298 if (cursor->hidden) {
299 cursor_unhide(cursor);
300 }
301}
302
303void cursor_unhide(struct sway_cursor *cursor) {
304 cursor->hidden = false;
305 if (cursor->image_surface) {
306 cursor_set_image_surface(cursor,
307 cursor->image_surface,
308 cursor->hotspot_x,
309 cursor->hotspot_y,
310 cursor->image_client);
311 } else {
312 const char *image = cursor->image;
313 cursor->image = NULL;
314 cursor_set_image(cursor, image, cursor->image_client);
315 }
316}
317
592void cursor_send_pointer_motion(struct sway_cursor *cursor, 318void cursor_send_pointer_motion(struct sway_cursor *cursor,
593 uint32_t time_msec) { 319 uint32_t time_msec) {
594 if (time_msec == 0) { 320 if (time_msec == 0) {
@@ -598,26 +324,8 @@ void cursor_send_pointer_motion(struct sway_cursor *cursor,
598 struct sway_seat *seat = cursor->seat; 324 struct sway_seat *seat = cursor->seat;
599 struct wlr_seat *wlr_seat = seat->wlr_seat; 325 struct wlr_seat *wlr_seat = seat->wlr_seat;
600 326
601 if (seat->operation != OP_NONE) { 327 if (seat_doing_seatop(seat)) {
602 switch (seat->operation) { 328 seatop_motion(seat, time_msec);
603 case OP_DOWN:
604 handle_down_motion(seat, cursor, time_msec);
605 break;
606 case OP_MOVE_FLOATING:
607 handle_move_floating_motion(seat, cursor);
608 break;
609 case OP_MOVE_TILING:
610 handle_move_tiling_motion(seat, cursor);
611 break;
612 case OP_RESIZE_FLOATING:
613 handle_resize_floating_motion(seat, cursor);
614 break;
615 case OP_RESIZE_TILING:
616 handle_resize_tiling_motion(seat, cursor);
617 break;
618 case OP_NONE:
619 break;
620 }
621 cursor->previous.x = cursor->cursor->x; 329 cursor->previous.x = cursor->cursor->x;
622 cursor->previous.y = cursor->cursor->y; 330 cursor->previous.y = cursor->cursor->y;
623 return; 331 return;
@@ -679,11 +387,11 @@ void cursor_send_pointer_motion(struct sway_cursor *cursor,
679 387
680static void handle_cursor_motion(struct wl_listener *listener, void *data) { 388static void handle_cursor_motion(struct wl_listener *listener, void *data) {
681 struct sway_cursor *cursor = wl_container_of(listener, cursor, motion); 389 struct sway_cursor *cursor = wl_container_of(listener, cursor, motion);
682 wlr_idle_notify_activity(server.idle, cursor->seat->wlr_seat);
683 struct wlr_event_pointer_motion *event = data; 390 struct wlr_event_pointer_motion *event = data;
684 wlr_cursor_move(cursor->cursor, event->device, 391 wlr_cursor_move(cursor->cursor, event->device,
685 event->delta_x, event->delta_y); 392 event->delta_x, event->delta_y);
686 cursor_send_pointer_motion(cursor, event->time_msec); 393 cursor_send_pointer_motion(cursor, event->time_msec);
394 cursor_handle_activity(cursor);
687 transaction_commit_dirty(); 395 transaction_commit_dirty();
688} 396}
689 397
@@ -691,10 +399,10 @@ static void handle_cursor_motion_absolute(
691 struct wl_listener *listener, void *data) { 399 struct wl_listener *listener, void *data) {
692 struct sway_cursor *cursor = 400 struct sway_cursor *cursor =
693 wl_container_of(listener, cursor, motion_absolute); 401 wl_container_of(listener, cursor, motion_absolute);
694 wlr_idle_notify_activity(server.idle, cursor->seat->wlr_seat);
695 struct wlr_event_pointer_motion_absolute *event = data; 402 struct wlr_event_pointer_motion_absolute *event = data;
696 wlr_cursor_warp_absolute(cursor->cursor, event->device, event->x, event->y); 403 wlr_cursor_warp_absolute(cursor->cursor, event->device, event->x, event->y);
697 cursor_send_pointer_motion(cursor, event->time_msec); 404 cursor_send_pointer_motion(cursor, event->time_msec);
405 cursor_handle_activity(cursor);
698 transaction_commit_dirty(); 406 transaction_commit_dirty();
699} 407}
700 408
@@ -794,11 +502,16 @@ void dispatch_cursor_button(struct sway_cursor *cursor,
794 struct sway_seat *seat = cursor->seat; 502 struct sway_seat *seat = cursor->seat;
795 503
796 // Handle existing seat operation 504 // Handle existing seat operation
797 if (cursor->seat->operation != OP_NONE) { 505 if (seat_doing_seatop(seat)) {
798 if (button == cursor->seat->op_button && state == WLR_BUTTON_RELEASED) { 506 if (button == seat->seatop_button && state == WLR_BUTTON_RELEASED) {
799 seat_end_mouse_operation(seat); 507 seatop_finish(seat);
800 seat_pointer_notify_button(seat, time_msec, button, state); 508 seat_pointer_notify_button(seat, time_msec, button, state);
801 } 509 }
510 if (state == WLR_BUTTON_PRESSED) {
511 state_add_button(cursor, button);
512 } else {
513 state_erase_button(cursor, button);
514 }
802 return; 515 return;
803 } 516 }
804 517
@@ -864,7 +577,7 @@ void dispatch_cursor_button(struct sway_cursor *cursor,
864 if (cont && resize_edge && button == BTN_LEFT && 577 if (cont && resize_edge && button == BTN_LEFT &&
865 state == WLR_BUTTON_PRESSED && !is_floating) { 578 state == WLR_BUTTON_PRESSED && !is_floating) {
866 seat_set_focus_container(seat, cont); 579 seat_set_focus_container(seat, cont);
867 seat_begin_resize_tiling(seat, cont, button, edge); 580 seatop_begin_resize_tiling(seat, cont, button, edge);
868 return; 581 return;
869 } 582 }
870 583
@@ -894,7 +607,7 @@ void dispatch_cursor_button(struct sway_cursor *cursor,
894 } 607 }
895 cursor_set_image(seat->cursor, image, NULL); 608 cursor_set_image(seat->cursor, image, NULL);
896 seat_set_focus_container(seat, cont); 609 seat_set_focus_container(seat, cont);
897 seat_begin_resize_tiling(seat, cont, button, edge); 610 seatop_begin_resize_tiling(seat, cont, button, edge);
898 return; 611 return;
899 } 612 }
900 } 613 }
@@ -909,7 +622,7 @@ void dispatch_cursor_button(struct sway_cursor *cursor,
909 cont = cont->parent; 622 cont = cont->parent;
910 } 623 }
911 seat_set_focus_container(seat, cont); 624 seat_set_focus_container(seat, cont);
912 seat_begin_move_floating(seat, cont, button); 625 seatop_begin_move_floating(seat, cont, button);
913 return; 626 return;
914 } 627 }
915 } 628 }
@@ -919,7 +632,7 @@ void dispatch_cursor_button(struct sway_cursor *cursor,
919 state == WLR_BUTTON_PRESSED) { 632 state == WLR_BUTTON_PRESSED) {
920 // Via border 633 // Via border
921 if (button == BTN_LEFT && resize_edge != WLR_EDGE_NONE) { 634 if (button == BTN_LEFT && resize_edge != WLR_EDGE_NONE) {
922 seat_begin_resize_floating(seat, cont, button, resize_edge); 635 seatop_begin_resize_floating(seat, cont, button, resize_edge);
923 return; 636 return;
924 } 637 }
925 638
@@ -936,16 +649,30 @@ void dispatch_cursor_button(struct sway_cursor *cursor,
936 WLR_EDGE_RIGHT : WLR_EDGE_LEFT; 649 WLR_EDGE_RIGHT : WLR_EDGE_LEFT;
937 edge |= cursor->cursor->y > floater->y + floater->height / 2 ? 650 edge |= cursor->cursor->y > floater->y + floater->height / 2 ?
938 WLR_EDGE_BOTTOM : WLR_EDGE_TOP; 651 WLR_EDGE_BOTTOM : WLR_EDGE_TOP;
939 seat_begin_resize_floating(seat, floater, button, edge); 652 seatop_begin_resize_floating(seat, floater, button, edge);
940 return; 653 return;
941 } 654 }
942 } 655 }
943 656
944 // Handle moving a tiling container 657 // Handle moving a tiling container
945 if (config->tiling_drag && mod_pressed && state == WLR_BUTTON_PRESSED && 658 if (config->tiling_drag && (mod_pressed || on_titlebar) &&
946 !is_floating_or_child && cont && !cont->is_fullscreen) { 659 state == WLR_BUTTON_PRESSED && !is_floating_or_child &&
660 cont && !cont->is_fullscreen) {
661 struct sway_container *focus = seat_get_focused_container(seat);
662 bool focused = focus == cont || container_has_ancestor(focus, cont);
663 if (on_titlebar && !focused) {
664 node = seat_get_focus_inactive(seat, &cont->node);
665 seat_set_focus(seat, node);
666 }
667
947 seat_pointer_notify_button(seat, time_msec, button, state); 668 seat_pointer_notify_button(seat, time_msec, button, state);
948 seat_begin_move_tiling(seat, cont, button); 669
670 // If moving a container by it's title bar, use a threshold for the drag
671 if (!mod_pressed && config->tiling_drag_threshold > 0) {
672 seatop_begin_move_tiling_threshold(seat, cont, button);
673 } else {
674 seatop_begin_move_tiling(seat, cont, button);
675 }
949 return; 676 return;
950 } 677 }
951 678
@@ -953,7 +680,7 @@ void dispatch_cursor_button(struct sway_cursor *cursor,
953 if (surface && cont && state == WLR_BUTTON_PRESSED) { 680 if (surface && cont && state == WLR_BUTTON_PRESSED) {
954 seat_set_focus_container(seat, cont); 681 seat_set_focus_container(seat, cont);
955 seat_pointer_notify_button(seat, time_msec, button, state); 682 seat_pointer_notify_button(seat, time_msec, button, state);
956 seat_begin_down(seat, cont, button, sx, sy); 683 seatop_begin_down(seat, cont, button, sx, sy);
957 return; 684 return;
958 } 685 }
959 686
@@ -970,18 +697,32 @@ void dispatch_cursor_button(struct sway_cursor *cursor,
970 697
971static void handle_cursor_button(struct wl_listener *listener, void *data) { 698static void handle_cursor_button(struct wl_listener *listener, void *data) {
972 struct sway_cursor *cursor = wl_container_of(listener, cursor, button); 699 struct sway_cursor *cursor = wl_container_of(listener, cursor, button);
973 wlr_idle_notify_activity(server.idle, cursor->seat->wlr_seat);
974 struct wlr_event_pointer_button *event = data; 700 struct wlr_event_pointer_button *event = data;
975 dispatch_cursor_button(cursor, event->device, 701 dispatch_cursor_button(cursor, event->device,
976 event->time_msec, event->button, event->state); 702 event->time_msec, event->button, event->state);
703 cursor_handle_activity(cursor);
977 transaction_commit_dirty(); 704 transaction_commit_dirty();
978} 705}
979 706
980static void dispatch_cursor_axis(struct sway_cursor *cursor, 707static uint32_t wl_axis_to_button(struct wlr_event_pointer_axis *event) {
708 switch (event->orientation) {
709 case WLR_AXIS_ORIENTATION_VERTICAL:
710 return event->delta < 0 ? SWAY_SCROLL_UP : SWAY_SCROLL_DOWN;
711 case WLR_AXIS_ORIENTATION_HORIZONTAL:
712 return event->delta < 0 ? SWAY_SCROLL_LEFT : SWAY_SCROLL_RIGHT;
713 default:
714 wlr_log(WLR_DEBUG, "Unknown axis orientation");
715 return 0;
716 }
717}
718
719void dispatch_cursor_axis(struct sway_cursor *cursor,
981 struct wlr_event_pointer_axis *event) { 720 struct wlr_event_pointer_axis *event) {
982 struct sway_seat *seat = cursor->seat; 721 struct sway_seat *seat = cursor->seat;
983 struct sway_input_device *input_device = event->device->data; 722 struct sway_input_device *input_device =
984 struct input_config *ic = input_device_get_config(input_device); 723 event->device ? event->device->data : NULL;
724 struct input_config *ic =
725 input_device ? input_device_get_config(input_device) : NULL;
985 726
986 // Determine what's under the cursor 727 // Determine what's under the cursor
987 struct wlr_surface *surface = NULL; 728 struct wlr_surface *surface = NULL;
@@ -993,11 +734,35 @@ static void dispatch_cursor_axis(struct sway_cursor *cursor,
993 enum wlr_edges edge = cont ? find_edge(cont, cursor) : WLR_EDGE_NONE; 734 enum wlr_edges edge = cont ? find_edge(cont, cursor) : WLR_EDGE_NONE;
994 bool on_border = edge != WLR_EDGE_NONE; 735 bool on_border = edge != WLR_EDGE_NONE;
995 bool on_titlebar = cont && !on_border && !surface; 736 bool on_titlebar = cont && !on_border && !surface;
737 bool on_titlebar_border = cont && on_border &&
738 cursor->cursor->y < cont->content_y;
739 bool on_contents = cont && !on_border && surface;
996 float scroll_factor = 740 float scroll_factor =
997 (ic == NULL || ic->scroll_factor == FLT_MIN) ? 1.0f : ic->scroll_factor; 741 (ic == NULL || ic->scroll_factor == FLT_MIN) ? 1.0f : ic->scroll_factor;
998 742
999 // Scrolling on a tabbed or stacked title bar 743 bool handled = false;
1000 if (on_titlebar) { 744
745 // Gather information needed for mouse bindings
746 struct wlr_keyboard *keyboard = wlr_seat_get_keyboard(seat->wlr_seat);
747 uint32_t modifiers = keyboard ? wlr_keyboard_get_modifiers(keyboard) : 0;
748 struct wlr_input_device *device =
749 input_device ? input_device->wlr_device : NULL;
750 char *dev_id = device ? input_device_get_identifier(device) : strdup("*");
751 uint32_t button = wl_axis_to_button(event);
752
753 // Handle mouse bindings - x11 mouse buttons 4-7 - press event
754 struct sway_binding *binding = NULL;
755 state_add_button(cursor, button);
756 binding = get_active_mouse_binding(cursor,
757 config->current_mode->mouse_bindings, modifiers, false,
758 on_titlebar, on_border, on_contents, dev_id);
759 if (binding) {
760 seat_execute_command(seat, binding);
761 handled = true;
762 }
763
764 // Scrolling on a tabbed or stacked title bar (handled as press event)
765 if (!handled && (on_titlebar || on_titlebar_border)) {
1001 enum sway_container_layout layout = container_parent_layout(cont); 766 enum sway_container_layout layout = container_parent_layout(cont);
1002 if (layout == L_TABBED || layout == L_STACKED) { 767 if (layout == L_TABBED || layout == L_STACKED) {
1003 struct sway_node *tabcontainer = node_get_parent(node); 768 struct sway_node *tabcontainer = node_get_parent(node);
@@ -1024,20 +789,33 @@ static void dispatch_cursor_axis(struct sway_cursor *cursor,
1024 seat_set_raw_focus(seat, new_focus); 789 seat_set_raw_focus(seat, new_focus);
1025 seat_set_raw_focus(seat, old_focus); 790 seat_set_raw_focus(seat, old_focus);
1026 } 791 }
1027 return; 792 handled = true;
1028 } 793 }
1029 } 794 }
1030 795
1031 wlr_seat_pointer_notify_axis(cursor->seat->wlr_seat, event->time_msec, 796 // Handle mouse bindings - x11 mouse buttons 4-7 - release event
1032 event->orientation, scroll_factor * event->delta, 797 binding = get_active_mouse_binding(cursor,
1033 round(scroll_factor * event->delta_discrete), event->source); 798 config->current_mode->mouse_bindings, modifiers, true,
799 on_titlebar, on_border, on_contents, dev_id);
800 state_erase_button(cursor, button);
801 if (binding) {
802 seat_execute_command(seat, binding);
803 handled = true;
804 }
805 free(dev_id);
806
807 if (!handled) {
808 wlr_seat_pointer_notify_axis(cursor->seat->wlr_seat, event->time_msec,
809 event->orientation, scroll_factor * event->delta,
810 round(scroll_factor * event->delta_discrete), event->source);
811 }
1034} 812}
1035 813
1036static void handle_cursor_axis(struct wl_listener *listener, void *data) { 814static void handle_cursor_axis(struct wl_listener *listener, void *data) {
1037 struct sway_cursor *cursor = wl_container_of(listener, cursor, axis); 815 struct sway_cursor *cursor = wl_container_of(listener, cursor, axis);
1038 wlr_idle_notify_activity(server.idle, cursor->seat->wlr_seat);
1039 struct wlr_event_pointer_axis *event = data; 816 struct wlr_event_pointer_axis *event = data;
1040 dispatch_cursor_axis(cursor, event); 817 dispatch_cursor_axis(cursor, event);
818 cursor_handle_activity(cursor);
1041 transaction_commit_dirty(); 819 transaction_commit_dirty();
1042} 820}
1043 821
@@ -1209,7 +987,7 @@ static void handle_request_set_cursor(struct wl_listener *listener,
1209 void *data) { 987 void *data) {
1210 struct sway_cursor *cursor = 988 struct sway_cursor *cursor =
1211 wl_container_of(listener, cursor, request_set_cursor); 989 wl_container_of(listener, cursor, request_set_cursor);
1212 if (cursor->seat->operation != OP_NONE) { 990 if (seat_doing_seatop(cursor->seat)) {
1213 return; 991 return;
1214 } 992 }
1215 struct wlr_seat_pointer_request_set_cursor_event *event = data; 993 struct wlr_seat_pointer_request_set_cursor_event *event = data;
@@ -1228,10 +1006,8 @@ static void handle_request_set_cursor(struct wl_listener *listener,
1228 return; 1006 return;
1229 } 1007 }
1230 1008
1231 wlr_cursor_set_surface(cursor->cursor, event->surface, event->hotspot_x, 1009 cursor_set_image_surface(cursor, event->surface, event->hotspot_x,
1232 event->hotspot_y); 1010 event->hotspot_y, focused_client);
1233 cursor->image = NULL;
1234 cursor->image_client = focused_client;
1235} 1011}
1236 1012
1237void cursor_set_image(struct sway_cursor *cursor, const char *image, 1013void cursor_set_image(struct sway_cursor *cursor, const char *image,
@@ -1239,14 +1015,43 @@ void cursor_set_image(struct sway_cursor *cursor, const char *image,
1239 if (!(cursor->seat->wlr_seat->capabilities & WL_SEAT_CAPABILITY_POINTER)) { 1015 if (!(cursor->seat->wlr_seat->capabilities & WL_SEAT_CAPABILITY_POINTER)) {
1240 return; 1016 return;
1241 } 1017 }
1018
1019 const char *current_image = cursor->image;
1020 cursor->image = image;
1021 cursor->image_surface = NULL;
1022 cursor->hotspot_x = cursor->hotspot_y = 0;
1023 cursor->image_client = client;
1024
1025 if (cursor->hidden) {
1026 return;
1027 }
1028
1242 if (!image) { 1029 if (!image) {
1243 wlr_cursor_set_image(cursor->cursor, NULL, 0, 0, 0, 0, 0, 0); 1030 wlr_cursor_set_image(cursor->cursor, NULL, 0, 0, 0, 0, 0, 0);
1244 } else if (!cursor->image || strcmp(cursor->image, image) != 0) { 1031 } else if (!current_image || strcmp(current_image, image) != 0) {
1245 wlr_xcursor_manager_set_cursor_image(cursor->xcursor_manager, image, 1032 wlr_xcursor_manager_set_cursor_image(cursor->xcursor_manager, image,
1246 cursor->cursor); 1033 cursor->cursor);
1247 } 1034 }
1248 cursor->image = image; 1035}
1036
1037void cursor_set_image_surface(struct sway_cursor *cursor,
1038 struct wlr_surface *surface, int32_t hotspot_x, int32_t hotspot_y,
1039 struct wl_client *client) {
1040 if (!(cursor->seat->wlr_seat->capabilities & WL_SEAT_CAPABILITY_POINTER)) {
1041 return;
1042 }
1043
1044 cursor->image = NULL;
1045 cursor->image_surface = surface;
1046 cursor->hotspot_x = hotspot_x;
1047 cursor->hotspot_y = hotspot_y;
1249 cursor->image_client = client; 1048 cursor->image_client = client;
1049
1050 if (cursor->hidden) {
1051 return;
1052 }
1053
1054 wlr_cursor_set_surface(cursor->cursor, surface, hotspot_x, hotspot_y);
1250} 1055}
1251 1056
1252void sway_cursor_destroy(struct sway_cursor *cursor) { 1057void sway_cursor_destroy(struct sway_cursor *cursor) {
@@ -1254,6 +1059,8 @@ void sway_cursor_destroy(struct sway_cursor *cursor) {
1254 return; 1059 return;
1255 } 1060 }
1256 1061
1062 wl_event_source_remove(cursor->hide_source);
1063
1257 wlr_xcursor_manager_destroy(cursor->xcursor_manager); 1064 wlr_xcursor_manager_destroy(cursor->xcursor_manager);
1258 wlr_cursor_destroy(cursor->cursor); 1065 wlr_cursor_destroy(cursor->cursor);
1259 free(cursor); 1066 free(cursor);
@@ -1277,6 +1084,9 @@ struct sway_cursor *sway_cursor_create(struct sway_seat *seat) {
1277 cursor->seat = seat; 1084 cursor->seat = seat;
1278 wlr_cursor_attach_output_layout(wlr_cursor, root->output_layout); 1085 wlr_cursor_attach_output_layout(wlr_cursor, root->output_layout);
1279 1086
1087 cursor->hide_source = wl_event_loop_add_timer(server.wl_event_loop,
1088 hide_notify, cursor);
1089
1280 // input events 1090 // input events
1281 wl_signal_add(&wlr_cursor->events.motion, &cursor->motion); 1091 wl_signal_add(&wlr_cursor->events.motion, &cursor->motion);
1282 cursor->motion.notify = handle_cursor_motion; 1092 cursor->motion.notify = handle_cursor_motion;
@@ -1362,3 +1172,66 @@ void cursor_warp_to_workspace(struct sway_cursor *cursor,
1362 1172
1363 wlr_cursor_warp(cursor->cursor, NULL, x, y); 1173 wlr_cursor_warp(cursor->cursor, NULL, x, y);
1364} 1174}
1175
1176uint32_t get_mouse_bindsym(const char *name, char **error) {
1177 if (strncasecmp(name, "button", strlen("button")) == 0) {
1178 // Map to x11 mouse buttons
1179 int number = name[strlen("button")] - '0';
1180 if (number < 1 || number > 9 || strlen(name) > strlen("button0")) {
1181 *error = strdup("Only buttons 1-9 are supported. For other mouse "
1182 "buttons, use the name of the event code.");
1183 return 0;
1184 }
1185 static const uint32_t buttons[] = {BTN_LEFT, BTN_MIDDLE, BTN_RIGHT,
1186 SWAY_SCROLL_UP, SWAY_SCROLL_DOWN, SWAY_SCROLL_LEFT,
1187 SWAY_SCROLL_RIGHT, BTN_SIDE, BTN_EXTRA};
1188 return buttons[number - 1];
1189 } else if (strncmp(name, "BTN_", strlen("BTN_")) == 0) {
1190 // Get event code from name
1191 int code = libevdev_event_code_from_name(EV_KEY, name);
1192 if (code == -1) {
1193 size_t len = snprintf(NULL, 0, "Unknown event %s", name) + 1;
1194 *error = malloc(len);
1195 if (*error) {
1196 snprintf(*error, len, "Unknown event %s", name);
1197 }
1198 return 0;
1199 }
1200 return code;
1201 }
1202 return 0;
1203}
1204
1205uint32_t get_mouse_bindcode(const char *name, char **error) {
1206 // Validate event code
1207 errno = 0;
1208 char *endptr;
1209 int code = strtol(name, &endptr, 10);
1210 if (endptr == name && code <= 0) {
1211 *error = strdup("Button event code must be a positive integer.");
1212 return 0;
1213 } else if (errno == ERANGE) {
1214 *error = strdup("Button event code out of range.");
1215 return 0;
1216 }
1217 const char *event = libevdev_event_code_get_name(EV_KEY, code);
1218 if (!event || strncmp(event, "BTN_", strlen("BTN_")) != 0) {
1219 size_t len = snprintf(NULL, 0, "Event code %d (%s) is not a button",
1220 code, event) + 1;
1221 *error = malloc(len);
1222 if (*error) {
1223 snprintf(*error, len, "Event code %d (%s) is not a button",
1224 code, event);
1225 }
1226 return 0;
1227 }
1228 return code;
1229}
1230
1231uint32_t get_mouse_button(const char *name, char **error) {
1232 uint32_t button = get_mouse_bindsym(name, error);
1233 if (!button && !*error) {
1234 button = get_mouse_bindcode(name, error);
1235 }
1236 return button;
1237}
diff --git a/sway/input/input-manager.c b/sway/input/input-manager.c
index 68445d68..d90803f6 100644
--- a/sway/input/input-manager.c
+++ b/sway/input/input-manager.c
@@ -1,4 +1,4 @@
1#define _XOPEN_SOURCE 700 1#define _POSIX_C_SOURCE 200809L
2#include <ctype.h> 2#include <ctype.h>
3#include <float.h> 3#include <float.h>
4#include <limits.h> 4#include <limits.h>
@@ -49,7 +49,7 @@ char *input_device_get_identifier(struct wlr_input_device *device) {
49 int vendor = device->vendor; 49 int vendor = device->vendor;
50 int product = device->product; 50 int product = device->product;
51 char *name = strdup(device->name); 51 char *name = strdup(device->name);
52 name = strip_whitespace(name); 52 strip_whitespace(name);
53 53
54 char *p = name; 54 char *p = name;
55 for (; *p; ++p) { 55 for (; *p; ++p) {
@@ -82,11 +82,12 @@ static struct sway_input_device *input_sway_device_from_wlr(
82 return NULL; 82 return NULL;
83} 83}
84 84
85static bool input_has_seat_configuration(void) { 85static bool input_has_seat_fallback_configuration(void) {
86 struct sway_seat *seat = NULL; 86 struct sway_seat *seat = NULL;
87 wl_list_for_each(seat, &server.input->seats, link) { 87 wl_list_for_each(seat, &server.input->seats, link) {
88 struct seat_config *seat_config = seat_get_config(seat); 88 struct seat_config *seat_config = seat_get_config(seat);
89 if (seat_config) { 89 if (seat_config && strcmp(seat_config->name, "*") != 0
90 && seat_config->fallback != -1) {
90 return true; 91 return true;
91 } 92 }
92 } 93 }
@@ -94,6 +95,18 @@ static bool input_has_seat_configuration(void) {
94 return false; 95 return false;
95} 96}
96 97
98void input_manager_verify_fallback_seat(void) {
99 struct sway_seat *seat = NULL;
100 if (!input_has_seat_fallback_configuration()) {
101 wlr_log(WLR_DEBUG, "no fallback seat config - creating default");
102 seat = input_manager_get_default_seat();
103 struct seat_config *sc = new_seat_config(seat->wlr_seat->name);
104 sc->fallback = true;
105 sc = store_seat_config(sc);
106 input_manager_apply_seat_config(sc);
107 }
108}
109
97static void input_manager_libinput_config_keyboard( 110static void input_manager_libinput_config_keyboard(
98 struct sway_input_device *input_device) { 111 struct sway_input_device *input_device) {
99 struct wlr_input_device *wlr_device = input_device->wlr_device; 112 struct wlr_input_device *wlr_device = input_device->wlr_device;
@@ -116,6 +129,24 @@ static void input_manager_libinput_config_keyboard(
116 } 129 }
117} 130}
118 131
132static void input_manager_libinput_reset_keyboard(
133 struct sway_input_device *input_device) {
134 struct wlr_input_device *wlr_device = input_device->wlr_device;
135 struct libinput_device *libinput_device;
136
137 if (!wlr_input_device_is_libinput(wlr_device)) {
138 return;
139 }
140
141 libinput_device = wlr_libinput_get_device_handle(wlr_device);
142
143 uint32_t send_events =
144 libinput_device_config_send_events_get_default_mode(libinput_device);
145 wlr_log(WLR_DEBUG, "libinput_reset_keyboard(%s) send_events_set_mode(%d)",
146 input_device->identifier, send_events);
147 libinput_device_config_send_events_set_mode(libinput_device, send_events);
148}
149
119static void input_manager_libinput_config_touch( 150static void input_manager_libinput_config_touch(
120 struct sway_input_device *input_device) { 151 struct sway_input_device *input_device) {
121 struct wlr_input_device *wlr_device = input_device->wlr_device; 152 struct wlr_input_device *wlr_device = input_device->wlr_device;
@@ -138,6 +169,24 @@ static void input_manager_libinput_config_touch(
138 } 169 }
139} 170}
140 171
172static void input_manager_libinput_reset_touch(
173 struct sway_input_device *input_device) {
174 struct wlr_input_device *wlr_device = input_device->wlr_device;
175 struct libinput_device *libinput_device;
176
177 if (!wlr_input_device_is_libinput(wlr_device)) {
178 return;
179 }
180
181 libinput_device = wlr_libinput_get_device_handle(wlr_device);
182
183 uint32_t send_events =
184 libinput_device_config_send_events_get_default_mode(libinput_device);
185 wlr_log(WLR_DEBUG, "libinput_reset_touch(%s) send_events_set_mode(%d)",
186 input_device->identifier, send_events);
187 libinput_device_config_send_events_set_mode(libinput_device, send_events);
188}
189
141static void input_manager_libinput_config_pointer( 190static void input_manager_libinput_config_pointer(
142 struct sway_input_device *input_device) { 191 struct sway_input_device *input_device) {
143 struct wlr_input_device *wlr_device = input_device->wlr_device; 192 struct wlr_input_device *wlr_device = input_device->wlr_device;
@@ -167,14 +216,14 @@ static void input_manager_libinput_config_pointer(
167 if (ic->drag != INT_MIN) { 216 if (ic->drag != INT_MIN) {
168 wlr_log(WLR_DEBUG, 217 wlr_log(WLR_DEBUG,
169 "libinput_config_pointer(%s) tap_set_drag_enabled(%d)", 218 "libinput_config_pointer(%s) tap_set_drag_enabled(%d)",
170 ic->identifier, ic->click_method); 219 ic->identifier, ic->drag);
171 libinput_device_config_tap_set_drag_enabled(libinput_device, 220 libinput_device_config_tap_set_drag_enabled(libinput_device,
172 ic->drag); 221 ic->drag);
173 } 222 }
174 if (ic->drag_lock != INT_MIN) { 223 if (ic->drag_lock != INT_MIN) {
175 wlr_log(WLR_DEBUG, 224 wlr_log(WLR_DEBUG,
176 "libinput_config_pointer(%s) tap_set_drag_lock_enabled(%d)", 225 "libinput_config_pointer(%s) tap_set_drag_lock_enabled(%d)",
177 ic->identifier, ic->click_method); 226 ic->identifier, ic->drag_lock);
178 libinput_device_config_tap_set_drag_lock_enabled(libinput_device, 227 libinput_device_config_tap_set_drag_lock_enabled(libinput_device,
179 ic->drag_lock); 228 ic->drag_lock);
180 } 229 }
@@ -235,12 +284,118 @@ static void input_manager_libinput_config_pointer(
235 } 284 }
236 if (ic->tap_button_map != INT_MIN) { 285 if (ic->tap_button_map != INT_MIN) {
237 wlr_log(WLR_DEBUG, "libinput_config_pointer(%s) tap_set_button_map(%d)", 286 wlr_log(WLR_DEBUG, "libinput_config_pointer(%s) tap_set_button_map(%d)",
238 ic->identifier, ic->tap); 287 ic->identifier, ic->tap_button_map);
239 libinput_device_config_tap_set_button_map(libinput_device, 288 libinput_device_config_tap_set_button_map(libinput_device,
240 ic->tap_button_map); 289 ic->tap_button_map);
241 } 290 }
242} 291}
243 292
293static void input_manager_libinput_reset_pointer(
294 struct sway_input_device *input_device) {
295 struct wlr_input_device *wlr_device = input_device->wlr_device;
296
297 if (!wlr_input_device_is_libinput(wlr_device)) {
298 return;
299 }
300
301 struct libinput_device *libinput_device =
302 wlr_libinput_get_device_handle(wlr_device);
303
304 enum libinput_config_accel_profile accel_profile =
305 libinput_device_config_accel_get_default_profile(libinput_device);
306 wlr_log(WLR_DEBUG, "libinput_reset_pointer(%s) accel_set_profile(%d)",
307 input_device->identifier, accel_profile);
308 libinput_device_config_accel_set_profile(libinput_device, accel_profile);
309
310 enum libinput_config_click_method click_method =
311 libinput_device_config_click_get_default_method(libinput_device);
312 wlr_log(WLR_DEBUG, "libinput_reset_pointer(%s) click_set_method(%d)",
313 input_device->identifier, click_method);
314 libinput_device_config_click_set_method(libinput_device, click_method);
315
316 enum libinput_config_drag_state drag =
317 libinput_device_config_tap_get_default_drag_enabled(libinput_device);
318 wlr_log(WLR_DEBUG, "libinput_reset_pointer(%s) tap_set_drag_enabled(%d)",
319 input_device->identifier, drag);
320 libinput_device_config_tap_set_drag_enabled(libinput_device, drag);
321
322 enum libinput_config_drag_lock_state drag_lock =
323 libinput_device_config_tap_get_default_drag_lock_enabled(
324 libinput_device);
325 wlr_log(WLR_DEBUG,
326 "libinput_reset_pointer(%s) tap_set_drag_lock_enabled(%d)",
327 input_device->identifier, drag_lock);
328 libinput_device_config_tap_set_drag_lock_enabled(libinput_device,
329 drag_lock);
330
331 enum libinput_config_dwt_state dwt =
332 libinput_device_config_dwt_get_default_enabled(libinput_device);
333 wlr_log(WLR_DEBUG, "libinput_reset_pointer(%s) dwt_set_enabled(%d)",
334 input_device->identifier, dwt);
335 libinput_device_config_dwt_set_enabled(libinput_device, dwt);
336
337 int left_handed =
338 libinput_device_config_left_handed_get_default(libinput_device);
339 wlr_log(WLR_DEBUG,
340 "libinput_reset_pointer(%s) left_handed_set_enabled(%d)",
341 input_device->identifier, left_handed);
342 libinput_device_config_left_handed_set(libinput_device, left_handed);
343
344 enum libinput_config_middle_emulation_state middle_emulation =
345 libinput_device_config_middle_emulation_get_default_enabled(
346 libinput_device);
347 wlr_log(WLR_DEBUG,
348 "libinput_reset_pointer(%s) middle_emulation_set_enabled(%d)",
349 input_device->identifier, middle_emulation);
350 libinput_device_config_middle_emulation_set_enabled(libinput_device,
351 middle_emulation);
352
353 int natural_scroll =
354 libinput_device_config_scroll_get_default_natural_scroll_enabled(
355 libinput_device);
356 wlr_log(WLR_DEBUG,
357 "libinput_reset_pointer(%s) natural_scroll_set_enabled(%d)",
358 input_device->identifier, natural_scroll);
359 libinput_device_config_scroll_set_natural_scroll_enabled(
360 libinput_device, natural_scroll);
361
362 double pointer_accel =
363 libinput_device_config_accel_get_default_speed(libinput_device);
364 wlr_log(WLR_DEBUG, "libinput_reset_pointer(%s) accel_set_speed(%f)",
365 input_device->identifier, pointer_accel);
366 libinput_device_config_accel_set_speed(libinput_device, pointer_accel);
367
368 uint32_t scroll_button =
369 libinput_device_config_scroll_get_default_button(libinput_device);
370 wlr_log(WLR_DEBUG, "libinput_reset_pointer(%s) scroll_set_button(%d)",
371 input_device->identifier, scroll_button);
372 libinput_device_config_scroll_set_button(libinput_device, scroll_button);
373
374 enum libinput_config_scroll_method scroll_method =
375 libinput_device_config_scroll_get_default_method(libinput_device);
376 wlr_log(WLR_DEBUG, "libinput_reset_pointer(%s) scroll_set_method(%d)",
377 input_device->identifier, scroll_method);
378 libinput_device_config_scroll_set_method(libinput_device, scroll_method);
379
380 uint32_t send_events =
381 libinput_device_config_send_events_get_default_mode(libinput_device);
382 wlr_log(WLR_DEBUG, "libinput_reset_pointer(%s) send_events_set_mode(%d)",
383 input_device->identifier, send_events);
384 libinput_device_config_send_events_set_mode(libinput_device, send_events);
385
386 enum libinput_config_tap_state tap =
387 libinput_device_config_tap_get_default_enabled(libinput_device);
388 wlr_log(WLR_DEBUG, "libinput_reset_pointer(%s) tap_set_enabled(%d)",
389 input_device->identifier, tap);
390 libinput_device_config_tap_set_enabled(libinput_device, tap);
391
392 enum libinput_config_tap_button_map tap_button_map =
393 libinput_device_config_tap_get_button_map(libinput_device);
394 wlr_log(WLR_DEBUG, "libinput_reset_pointer(%s) tap_set_button_map(%d)",
395 input_device->identifier, tap_button_map);
396 libinput_device_config_tap_set_button_map(libinput_device, tap_button_map);
397}
398
244static void handle_device_destroy(struct wl_listener *listener, void *data) { 399static void handle_device_destroy(struct wl_listener *listener, void *data) {
245 struct wlr_input_device *device = data; 400 struct wlr_input_device *device = data;
246 401
@@ -295,15 +450,10 @@ static void handle_new_input(struct wl_listener *listener, void *data) {
295 wl_signal_add(&device->events.destroy, &input_device->device_destroy); 450 wl_signal_add(&device->events.destroy, &input_device->device_destroy);
296 input_device->device_destroy.notify = handle_device_destroy; 451 input_device->device_destroy.notify = handle_device_destroy;
297 452
298 struct sway_seat *seat = NULL; 453 input_manager_verify_fallback_seat();
299 if (!input_has_seat_configuration()) {
300 wlr_log(WLR_DEBUG, "no seat configuration, using default seat");
301 seat = input_manager_get_default_seat();
302 seat_add_device(seat, input_device);
303 return;
304 }
305 454
306 bool added = false; 455 bool added = false;
456 struct sway_seat *seat = NULL;
307 wl_list_for_each(seat, &input->seats, link) { 457 wl_list_for_each(seat, &input->seats, link) {
308 struct seat_config *seat_config = seat_get_config(seat); 458 struct seat_config *seat_config = seat_get_config(seat);
309 bool has_attachment = seat_config && 459 bool has_attachment = seat_config &&
@@ -458,15 +608,50 @@ void input_manager_apply_input_config(struct input_config *input_config) {
458 } 608 }
459} 609}
460 610
461void input_manager_apply_seat_config(struct seat_config *seat_config) { 611void input_manager_reset_input(struct sway_input_device *input_device) {
462 wlr_log(WLR_DEBUG, "applying new seat config for seat %s", 612 if (input_device->wlr_device->type == WLR_INPUT_DEVICE_POINTER ||
463 seat_config->name); 613 input_device->wlr_device->type == WLR_INPUT_DEVICE_TABLET_TOOL) {
464 struct sway_seat *seat = input_manager_get_seat(seat_config->name); 614 input_manager_libinput_reset_pointer(input_device);
465 if (!seat) { 615 } else if (input_device->wlr_device->type == WLR_INPUT_DEVICE_KEYBOARD) {
466 return; 616 input_manager_libinput_reset_keyboard(input_device);
617 } else if (input_device->wlr_device->type == WLR_INPUT_DEVICE_TOUCH) {
618 input_manager_libinput_reset_touch(input_device);
619 }
620
621 struct sway_seat *seat = NULL;
622 wl_list_for_each(seat, &server.input->seats, link) {
623 seat_reset_device(seat, input_device);
467 } 624 }
625}
626
627void input_manager_reset_all_inputs() {
628 struct sway_input_device *input_device = NULL;
629 wl_list_for_each(input_device, &server.input->devices, link) {
630 input_manager_reset_input(input_device);
631 }
632}
468 633
469 seat_apply_config(seat, seat_config); 634
635void input_manager_apply_seat_config(struct seat_config *seat_config) {
636 wlr_log(WLR_DEBUG, "applying seat config for seat %s", seat_config->name);
637 if (strcmp(seat_config->name, "*") == 0) {
638 struct sway_seat *seat = NULL;
639 wl_list_for_each(seat, &server.input->seats, link) {
640 // Only apply the wildcard config directly if there is no seat
641 // specific config
642 struct seat_config *sc = seat_get_config(seat);
643 if (!sc) {
644 sc = seat_config;
645 }
646 seat_apply_config(seat, sc);
647 }
648 } else {
649 struct sway_seat *seat = input_manager_get_seat(seat_config->name);
650 if (!seat) {
651 return;
652 }
653 seat_apply_config(seat, seat_config);
654 }
470 655
471 // for every device, try to add it to a seat and if no seat has it 656 // for every device, try to add it to a seat and if no seat has it
472 // attached, add it to the fallback seats. 657 // attached, add it to the fallback seats.
diff --git a/sway/input/keyboard.c b/sway/input/keyboard.c
index c1b53237..46286410 100644
--- a/sway/input/keyboard.c
+++ b/sway/input/keyboard.c
@@ -295,14 +295,10 @@ static void handle_keyboard_key(struct wl_listener *listener, void *data) {
295 get_active_binding(&keyboard->state_keysyms_raw, 295 get_active_binding(&keyboard->state_keysyms_raw,
296 config->current_mode->keysym_bindings, &binding, 296 config->current_mode->keysym_bindings, &binding,
297 raw_modifiers, false, input_inhibited, device_identifier); 297 raw_modifiers, false, input_inhibited, device_identifier);
298
299 if (binding) {
300 seat_execute_command(seat, binding);
301 handled = true;
302 }
303 } 298 }
304 299
305 // Set up (or clear) keyboard repeat for a pressed binding 300 // Set up (or clear) keyboard repeat for a pressed binding. Since the
301 // binding may remove the keyboard, the timer needs to be updated first
306 if (binding && wlr_device->keyboard->repeat_info.delay > 0) { 302 if (binding && wlr_device->keyboard->repeat_info.delay > 0) {
307 keyboard->repeat_binding = binding; 303 keyboard->repeat_binding = binding;
308 if (wl_event_source_timer_update(keyboard->key_repeat_source, 304 if (wl_event_source_timer_update(keyboard->key_repeat_source,
@@ -316,6 +312,11 @@ static void handle_keyboard_key(struct wl_listener *listener, void *data) {
316 } 312 }
317 } 313 }
318 314
315 if (binding) {
316 seat_execute_command(seat, binding);
317 handled = true;
318 }
319
319 // Compositor bindings 320 // Compositor bindings
320 if (!handled && event->state == WLR_KEY_PRESSED) { 321 if (!handled && event->state == WLR_KEY_PRESSED) {
321 handled = keyboard_execute_compositor_binding( 322 handled = keyboard_execute_compositor_binding(
diff --git a/sway/input/seat.c b/sway/input/seat.c
index 663c5140..a63999b6 100644
--- a/sway/input/seat.c
+++ b/sway/input/seat.c
@@ -1,12 +1,7 @@
1#define _XOPEN_SOURCE 700 1#define _POSIX_C_SOURCE 200809L
2#define _POSIX_C_SOURCE 199309L
3#include <assert.h> 2#include <assert.h>
4#include <errno.h> 3#include <errno.h>
5#ifdef __linux__
6#include <linux/input-event-codes.h> 4#include <linux/input-event-codes.h>
7#elif __FreeBSD__
8#include <dev/evdev/input-event-codes.h>
9#endif
10#include <strings.h> 5#include <strings.h>
11#include <time.h> 6#include <time.h>
12#include <wlr/types/wlr_cursor.h> 7#include <wlr/types/wlr_cursor.h>
@@ -175,6 +170,12 @@ static void handle_seat_node_destroy(struct wl_listener *listener, void *data) {
175 parent = node_get_parent(parent); 170 parent = node_get_parent(parent);
176 } 171 }
177 172
173 if (next_focus->type == N_WORKSPACE &&
174 !workspace_is_visible(next_focus->sway_workspace)) {
175 // Do not change focus to a non-visible workspace
176 return;
177 }
178
178 if (needs_new_focus) { 179 if (needs_new_focus) {
179 // The structure change might have caused it to move up to the top of 180 // The structure change might have caused it to move up to the top of
180 // the focus stack without sending focus notifications to the view 181 // the focus stack without sending focus notifications to the view
@@ -307,7 +308,7 @@ static void handle_new_drag_icon(struct wl_listener *listener, void *data) {
307 wl_list_insert(&root->drag_icons, &icon->link); 308 wl_list_insert(&root->drag_icons, &icon->link);
308 309
309 drag_icon_update_position(icon); 310 drag_icon_update_position(icon);
310 seat_end_mouse_operation(seat); 311 seatop_abort(seat);
311} 312}
312 313
313static void collect_focus_iter(struct sway_node *node, void *data) { 314static void collect_focus_iter(struct sway_node *node, void *data) {
@@ -387,6 +388,7 @@ static void seat_update_capabilities(struct sway_seat *seat) {
387 caps |= WL_SEAT_CAPABILITY_POINTER; 388 caps |= WL_SEAT_CAPABILITY_POINTER;
388 break; 389 break;
389 case WLR_INPUT_DEVICE_TABLET_PAD: 390 case WLR_INPUT_DEVICE_TABLET_PAD:
391 case WLR_INPUT_DEVICE_SWITCH:
390 break; 392 break;
391 } 393 }
392 } 394 }
@@ -403,6 +405,14 @@ static void seat_update_capabilities(struct sway_seat *seat) {
403 } 405 }
404} 406}
405 407
408static void seat_reset_input_config(struct sway_seat *seat,
409 struct sway_seat_device *sway_device) {
410 wlr_log(WLR_DEBUG, "Resetting output mapping for input device %s",
411 sway_device->input_device->identifier);
412 wlr_cursor_map_input_to_output(seat->cursor->cursor,
413 sway_device->input_device->wlr_device, NULL);
414}
415
406static void seat_apply_input_config(struct sway_seat *seat, 416static void seat_apply_input_config(struct sway_seat *seat,
407 struct sway_seat_device *sway_device) { 417 struct sway_seat_device *sway_device) {
408 const char *mapped_to_output = NULL; 418 const char *mapped_to_output = NULL;
@@ -422,7 +432,13 @@ static void seat_apply_input_config(struct sway_seat *seat,
422 if (mapped_to_output != NULL) { 432 if (mapped_to_output != NULL) {
423 wlr_log(WLR_DEBUG, "Mapping input device %s to output %s", 433 wlr_log(WLR_DEBUG, "Mapping input device %s to output %s",
424 sway_device->input_device->identifier, mapped_to_output); 434 sway_device->input_device->identifier, mapped_to_output);
425 struct sway_output *output = output_by_name(mapped_to_output); 435 if (strcmp("*", mapped_to_output) == 0) {
436 wlr_cursor_map_input_to_output(seat->cursor->cursor,
437 sway_device->input_device->wlr_device, NULL);
438 wlr_log(WLR_DEBUG, "Reset output mapping");
439 return;
440 }
441 struct sway_output *output = output_by_name_or_id(mapped_to_output);
426 if (output) { 442 if (output) {
427 wlr_cursor_map_input_to_output(seat->cursor->cursor, 443 wlr_cursor_map_input_to_output(seat->cursor->cursor,
428 sway_device->input_device->wlr_device, output->wlr_output); 444 sway_device->input_device->wlr_device, output->wlr_output);
@@ -508,6 +524,38 @@ void seat_configure_device(struct sway_seat *seat,
508 case WLR_INPUT_DEVICE_TABLET_PAD: 524 case WLR_INPUT_DEVICE_TABLET_PAD:
509 wlr_log(WLR_DEBUG, "TODO: configure tablet pad"); 525 wlr_log(WLR_DEBUG, "TODO: configure tablet pad");
510 break; 526 break;
527 case WLR_INPUT_DEVICE_SWITCH:
528 wlr_log(WLR_DEBUG, "TODO: configure switch device");
529 break;
530 }
531}
532
533void seat_reset_device(struct sway_seat *seat,
534 struct sway_input_device *input_device) {
535 struct sway_seat_device *seat_device = seat_get_device(seat, input_device);
536 if (!seat_device) {
537 return;
538 }
539
540 switch (input_device->wlr_device->type) {
541 case WLR_INPUT_DEVICE_POINTER:
542 seat_reset_input_config(seat, seat_device);
543 break;
544 case WLR_INPUT_DEVICE_KEYBOARD:
545 sway_keyboard_configure(seat_device->keyboard);
546 break;
547 case WLR_INPUT_DEVICE_TOUCH:
548 seat_reset_input_config(seat, seat_device);
549 break;
550 case WLR_INPUT_DEVICE_TABLET_TOOL:
551 seat_reset_input_config(seat, seat_device);
552 break;
553 case WLR_INPUT_DEVICE_TABLET_PAD:
554 wlr_log(WLR_DEBUG, "TODO: reset tablet pad");
555 break;
556 case WLR_INPUT_DEVICE_SWITCH:
557 wlr_log(WLR_DEBUG, "TODO: reset switch device");
558 break;
511 } 559 }
512} 560}
513 561
@@ -614,18 +662,6 @@ static int handle_urgent_timeout(void *data) {
614 return 0; 662 return 0;
615} 663}
616 664
617static void container_raise_floating(struct sway_container *con) {
618 // Bring container to front by putting it at the end of the floating list.
619 struct sway_container *floater = con;
620 while (floater->parent) {
621 floater = floater->parent;
622 }
623 if (container_is_floating(floater)) {
624 list_move_to_end(floater->workspace->floating, floater);
625 node_set_dirty(&floater->workspace->node);
626 }
627}
628
629static void set_workspace(struct sway_seat *seat, 665static void set_workspace(struct sway_seat *seat,
630 struct sway_workspace *new_ws) { 666 struct sway_workspace *new_ws) {
631 if (seat->workspace == new_ws) { 667 if (seat->workspace == new_ws) {
@@ -986,6 +1022,8 @@ void seat_apply_config(struct sway_seat *seat,
986 wl_list_for_each(seat_device, &seat->devices, link) { 1022 wl_list_for_each(seat_device, &seat->devices, link) {
987 seat_configure_device(seat, seat_device->input_device); 1023 seat_configure_device(seat, seat_device->input_device);
988 } 1024 }
1025
1026 cursor_handle_activity(seat->cursor);
989} 1027}
990 1028
991struct seat_config *seat_get_config(struct sway_seat *seat) { 1029struct seat_config *seat_get_config(struct sway_seat *seat) {
@@ -1000,174 +1038,16 @@ struct seat_config *seat_get_config(struct sway_seat *seat) {
1000 return NULL; 1038 return NULL;
1001} 1039}
1002 1040
1003void seat_begin_down(struct sway_seat *seat, struct sway_container *con, 1041struct seat_config *seat_get_config_by_name(const char *name) {
1004 uint32_t button, double sx, double sy) { 1042 struct seat_config *seat_config = NULL;
1005 seat->operation = OP_DOWN; 1043 for (int i = 0; i < config->seat_configs->length; ++i ) {
1006 seat->op_container = con; 1044 seat_config = config->seat_configs->items[i];
1007 seat->op_button = button; 1045 if (strcmp(name, seat_config->name) == 0) {
1008 seat->op_ref_lx = seat->cursor->cursor->x; 1046 return seat_config;
1009 seat->op_ref_ly = seat->cursor->cursor->y;
1010 seat->op_ref_con_lx = sx;
1011 seat->op_ref_con_ly = sy;
1012 seat->op_moved = false;
1013
1014 container_raise_floating(con);
1015}
1016
1017void seat_begin_move_floating(struct sway_seat *seat,
1018 struct sway_container *con, uint32_t button) {
1019 if (!seat->cursor) {
1020 wlr_log(WLR_DEBUG, "Ignoring move request due to no cursor device");
1021 return;
1022 }
1023 seat->operation = OP_MOVE_FLOATING;
1024 seat->op_container = con;
1025 seat->op_button = button;
1026
1027 container_raise_floating(con);
1028
1029 cursor_set_image(seat->cursor, "grab", NULL);
1030}
1031
1032void seat_begin_move_tiling(struct sway_seat *seat,
1033 struct sway_container *con, uint32_t button) {
1034 seat->operation = OP_MOVE_TILING;
1035 seat->op_container = con;
1036 seat->op_button = button;
1037 seat->op_target_node = NULL;
1038 seat->op_target_edge = 0;
1039 cursor_set_image(seat->cursor, "grab", NULL);
1040}
1041
1042void seat_begin_resize_floating(struct sway_seat *seat,
1043 struct sway_container *con, uint32_t button, enum wlr_edges edge) {
1044 if (!seat->cursor) {
1045 wlr_log(WLR_DEBUG, "Ignoring resize request due to no cursor device");
1046 return;
1047 }
1048 struct wlr_keyboard *keyboard = wlr_seat_get_keyboard(seat->wlr_seat);
1049 seat->operation = OP_RESIZE_FLOATING;
1050 seat->op_container = con;
1051 seat->op_resize_preserve_ratio = keyboard &&
1052 (wlr_keyboard_get_modifiers(keyboard) & WLR_MODIFIER_SHIFT);
1053 seat->op_resize_edge = edge == WLR_EDGE_NONE ?
1054 WLR_EDGE_BOTTOM | WLR_EDGE_RIGHT : edge;
1055 seat->op_button = button;
1056 seat->op_ref_lx = seat->cursor->cursor->x;
1057 seat->op_ref_ly = seat->cursor->cursor->y;
1058 seat->op_ref_con_lx = con->x;
1059 seat->op_ref_con_ly = con->y;
1060 seat->op_ref_width = con->width;
1061 seat->op_ref_height = con->height;
1062
1063 container_raise_floating(con);
1064
1065 const char *image = edge == WLR_EDGE_NONE ?
1066 "se-resize" : wlr_xcursor_get_resize_name(edge);
1067 cursor_set_image(seat->cursor, image, NULL);
1068}
1069
1070void seat_begin_resize_tiling(struct sway_seat *seat,
1071 struct sway_container *con, uint32_t button, enum wlr_edges edge) {
1072 seat->operation = OP_RESIZE_TILING;
1073 seat->op_container = con;
1074 seat->op_resize_edge = edge;
1075 seat->op_button = button;
1076 seat->op_ref_lx = seat->cursor->cursor->x;
1077 seat->op_ref_ly = seat->cursor->cursor->y;
1078 seat->op_ref_con_lx = con->x;
1079 seat->op_ref_con_ly = con->y;
1080 seat->op_ref_width = con->width;
1081 seat->op_ref_height = con->height;
1082}
1083
1084static bool is_parallel(enum sway_container_layout layout,
1085 enum wlr_edges edge) {
1086 bool layout_is_horiz = layout == L_HORIZ || layout == L_TABBED;
1087 bool edge_is_horiz = edge == WLR_EDGE_LEFT || edge == WLR_EDGE_RIGHT;
1088 return layout_is_horiz == edge_is_horiz;
1089}
1090
1091static void seat_end_move_tiling(struct sway_seat *seat) {
1092 struct sway_container *con = seat->op_container;
1093 struct sway_container *old_parent = con->parent;
1094 struct sway_workspace *old_ws = con->workspace;
1095 struct sway_node *target_node = seat->op_target_node;
1096 struct sway_workspace *new_ws = target_node->type == N_WORKSPACE ?
1097 target_node->sway_workspace : target_node->sway_container->workspace;
1098 enum wlr_edges edge = seat->op_target_edge;
1099 int after = edge != WLR_EDGE_TOP && edge != WLR_EDGE_LEFT;
1100
1101 container_detach(con);
1102
1103 // Moving container into empty workspace
1104 if (target_node->type == N_WORKSPACE && edge == WLR_EDGE_NONE) {
1105 workspace_add_tiling(new_ws, con);
1106 } else if (target_node->type == N_CONTAINER) {
1107 // Moving container before/after another
1108 struct sway_container *target = target_node->sway_container;
1109 enum sway_container_layout layout = container_parent_layout(target);
1110 if (edge && !is_parallel(layout, edge)) {
1111 enum sway_container_layout new_layout = edge == WLR_EDGE_TOP ||
1112 edge == WLR_EDGE_BOTTOM ? L_VERT : L_HORIZ;
1113 container_split(target, new_layout);
1114 }
1115 container_add_sibling(target, con, after);
1116 } else {
1117 // Target is a workspace which requires splitting
1118 enum sway_container_layout new_layout = edge == WLR_EDGE_TOP ||
1119 edge == WLR_EDGE_BOTTOM ? L_VERT : L_HORIZ;
1120 workspace_split(new_ws, new_layout);
1121 workspace_insert_tiling(new_ws, con, after);
1122 }
1123
1124 if (old_parent) {
1125 container_reap_empty(old_parent);
1126 }
1127
1128 // This is a bit dirty, but we'll set the dimensions to that of a sibling.
1129 // I don't think there's any other way to make it consistent without
1130 // changing how we auto-size containers.
1131 list_t *siblings = container_get_siblings(con);
1132 if (siblings->length > 1) {
1133 int index = list_find(siblings, con);
1134 struct sway_container *sibling = index == 0 ?
1135 siblings->items[1] : siblings->items[index - 1];
1136 con->width = sibling->width;
1137 con->height = sibling->height;
1138 }
1139
1140 arrange_workspace(old_ws);
1141 if (new_ws != old_ws) {
1142 arrange_workspace(new_ws);
1143 }
1144}
1145
1146void seat_end_mouse_operation(struct sway_seat *seat) {
1147 enum sway_seat_operation operation = seat->operation;
1148 if (seat->operation == OP_MOVE_FLOATING) {
1149 // We "move" the container to its own location so it discovers its
1150 // output again.
1151 struct sway_container *con = seat->op_container;
1152 container_floating_move_to(con, con->x, con->y);
1153 } else if (seat->operation == OP_MOVE_TILING && seat->op_target_node) {
1154 seat_end_move_tiling(seat);
1155 }
1156 seat->operation = OP_NONE;
1157 seat->op_container = NULL;
1158 if (operation == OP_DOWN) {
1159 // Set the cursor's previous coords to the x/y at the start of the
1160 // operation, so the container change will be detected if using
1161 // focus_follows_mouse and the cursor moved off the original container
1162 // during the operation.
1163 seat->cursor->previous.x = seat->op_ref_lx;
1164 seat->cursor->previous.y = seat->op_ref_ly;
1165 if (seat->op_moved) {
1166 cursor_send_pointer_motion(seat->cursor, 0);
1167 } 1047 }
1168 } else {
1169 cursor_set_image(seat->cursor, "left_ptr", NULL);
1170 } 1048 }
1049
1050 return NULL;
1171} 1051}
1172 1052
1173void seat_pointer_notify_button(struct sway_seat *seat, uint32_t time_msec, 1053void seat_pointer_notify_button(struct sway_seat *seat, uint32_t time_msec,
@@ -1197,4 +1077,49 @@ void seat_consider_warp_to_focus(struct sway_seat *seat) {
1197 } else { 1077 } else {
1198 cursor_warp_to_workspace(seat->cursor, focus->sway_workspace); 1078 cursor_warp_to_workspace(seat->cursor, focus->sway_workspace);
1199 } 1079 }
1080 if (seat->cursor->hidden){
1081 cursor_unhide(seat->cursor);
1082 wl_event_source_timer_update(seat->cursor->hide_source, cursor_get_timeout(seat->cursor));
1083 }
1084}
1085
1086bool seat_doing_seatop(struct sway_seat *seat) {
1087 return seat->seatop_impl != NULL;
1088}
1089
1090void seatop_unref(struct sway_seat *seat, struct sway_container *con) {
1091 if (seat->seatop_impl && seat->seatop_impl->unref) {
1092 seat->seatop_impl->unref(seat, con);
1093 }
1094}
1095
1096void seatop_motion(struct sway_seat *seat, uint32_t time_msec) {
1097 if (seat->seatop_impl && seat->seatop_impl->motion) {
1098 seat->seatop_impl->motion(seat, time_msec);
1099 }
1100}
1101
1102void seatop_finish(struct sway_seat *seat) {
1103 if (seat->seatop_impl && seat->seatop_impl->finish) {
1104 seat->seatop_impl->finish(seat);
1105 }
1106 free(seat->seatop_data);
1107 seat->seatop_data = NULL;
1108 seat->seatop_impl = NULL;
1109}
1110
1111void seatop_abort(struct sway_seat *seat) {
1112 if (seat->seatop_impl && seat->seatop_impl->abort) {
1113 seat->seatop_impl->abort(seat);
1114 }
1115 free(seat->seatop_data);
1116 seat->seatop_data = NULL;
1117 seat->seatop_impl = NULL;
1118}
1119
1120void seatop_render(struct sway_seat *seat, struct sway_output *output,
1121 pixman_region32_t *damage) {
1122 if (seat->seatop_impl && seat->seatop_impl->render) {
1123 seat->seatop_impl->render(seat, output, damage);
1124 }
1200} 1125}
diff --git a/sway/input/seatop_down.c b/sway/input/seatop_down.c
new file mode 100644
index 00000000..ad11c5ca
--- /dev/null
+++ b/sway/input/seatop_down.c
@@ -0,0 +1,77 @@
1#define _POSIX_C_SOURCE 200809L
2#include <wlr/types/wlr_cursor.h>
3#include "sway/input/cursor.h"
4#include "sway/input/seat.h"
5#include "sway/tree/view.h"
6
7struct seatop_down_event {
8 struct sway_container *con;
9 double ref_lx, ref_ly; // cursor's x/y at start of op
10 double ref_con_lx, ref_con_ly; // container's x/y at start of op
11 bool moved;
12};
13
14static void handle_motion(struct sway_seat *seat, uint32_t time_msec) {
15 struct seatop_down_event *e = seat->seatop_data;
16 struct sway_container *con = e->con;
17 if (seat_is_input_allowed(seat, con->view->surface)) {
18 double moved_x = seat->cursor->cursor->x - e->ref_lx;
19 double moved_y = seat->cursor->cursor->y - e->ref_ly;
20 double sx = e->ref_con_lx + moved_x;
21 double sy = e->ref_con_ly + moved_y;
22 wlr_seat_pointer_notify_motion(seat->wlr_seat, time_msec, sx, sy);
23 }
24 e->moved = true;
25}
26
27static void handle_finish(struct sway_seat *seat) {
28 struct seatop_down_event *e = seat->seatop_data;
29 // Set the cursor's previous coords to the x/y at the start of the
30 // operation, so the container change will be detected if using
31 // focus_follows_mouse and the cursor moved off the original container
32 // during the operation.
33 seat->cursor->previous.x = e->ref_lx;
34 seat->cursor->previous.y = e->ref_ly;
35 if (e->moved) {
36 cursor_send_pointer_motion(seat->cursor, 0);
37 }
38}
39
40static void handle_abort(struct sway_seat *seat) {
41 cursor_set_image(seat->cursor, "left_ptr", NULL);
42}
43
44static void handle_unref(struct sway_seat *seat, struct sway_container *con) {
45 struct seatop_down_event *e = seat->seatop_data;
46 if (e->con == con) {
47 seatop_abort(seat);
48 }
49}
50
51static const struct sway_seatop_impl seatop_impl = {
52 .motion = handle_motion,
53 .finish = handle_finish,
54 .abort = handle_abort,
55 .unref = handle_unref,
56};
57
58void seatop_begin_down(struct sway_seat *seat,
59 struct sway_container *con, uint32_t button, int sx, int sy) {
60 seatop_abort(seat);
61
62 struct seatop_down_event *e =
63 calloc(1, sizeof(struct seatop_down_event));
64 if (!e) {
65 return;
66 }
67 e->con = con;
68 e->ref_lx = seat->cursor->cursor->x;
69 e->ref_ly = seat->cursor->cursor->y;
70 e->ref_con_lx = sx;
71 e->ref_con_ly = sy;
72 e->moved = false;
73
74 seat->seatop_impl = &seatop_impl;
75 seat->seatop_data = e;
76 seat->seatop_button = button;
77}
diff --git a/sway/input/seatop_move_floating.c b/sway/input/seatop_move_floating.c
new file mode 100644
index 00000000..08e3a5a4
--- /dev/null
+++ b/sway/input/seatop_move_floating.c
@@ -0,0 +1,65 @@
1#define _POSIX_C_SOURCE 200809L
2#include <wlr/types/wlr_cursor.h>
3#include "sway/desktop.h"
4#include "sway/input/cursor.h"
5#include "sway/input/seat.h"
6
7struct seatop_move_floating_event {
8 struct sway_container *con;
9};
10
11static void handle_motion(struct sway_seat *seat, uint32_t time_msec) {
12 struct seatop_move_floating_event *e = seat->seatop_data;
13 desktop_damage_whole_container(e->con);
14 container_floating_translate(e->con,
15 seat->cursor->cursor->x - seat->cursor->previous.x,
16 seat->cursor->cursor->y - seat->cursor->previous.y);
17 desktop_damage_whole_container(e->con);
18}
19
20static void handle_finish(struct sway_seat *seat) {
21 struct seatop_move_floating_event *e = seat->seatop_data;
22
23 // We "move" the container to its own location
24 // so it discovers its output again.
25 container_floating_move_to(e->con, e->con->x, e->con->y);
26 cursor_set_image(seat->cursor, "left_ptr", NULL);
27}
28
29static void handle_abort(struct sway_seat *seat) {
30 cursor_set_image(seat->cursor, "left_ptr", NULL);
31}
32
33static void handle_unref(struct sway_seat *seat, struct sway_container *con) {
34 struct seatop_move_floating_event *e = seat->seatop_data;
35 if (e->con == con) {
36 seatop_abort(seat);
37 }
38}
39
40static const struct sway_seatop_impl seatop_impl = {
41 .motion = handle_motion,
42 .finish = handle_finish,
43 .abort = handle_abort,
44 .unref = handle_unref,
45};
46
47void seatop_begin_move_floating(struct sway_seat *seat,
48 struct sway_container *con, uint32_t button) {
49 seatop_abort(seat);
50
51 struct seatop_move_floating_event *e =
52 calloc(1, sizeof(struct seatop_move_floating_event));
53 if (!e) {
54 return;
55 }
56 e->con = con;
57
58 seat->seatop_impl = &seatop_impl;
59 seat->seatop_data = e;
60 seat->seatop_button = button;
61
62 container_raise_floating(con);
63
64 cursor_set_image(seat->cursor, "grab", NULL);
65}
diff --git a/sway/input/seatop_move_tiling.c b/sway/input/seatop_move_tiling.c
new file mode 100644
index 00000000..8b541f80
--- /dev/null
+++ b/sway/input/seatop_move_tiling.c
@@ -0,0 +1,335 @@
1#define _POSIX_C_SOURCE 200809L
2#include <limits.h>
3#include <wlr/types/wlr_cursor.h>
4#include <wlr/util/edges.h>
5#include "sway/desktop.h"
6#include "sway/input/cursor.h"
7#include "sway/input/seat.h"
8#include "sway/output.h"
9#include "sway/tree/arrange.h"
10#include "sway/tree/node.h"
11#include "sway/tree/view.h"
12#include "sway/tree/workspace.h"
13
14// Thickness of the dropzone when dragging to the edge of a layout container
15#define DROP_LAYOUT_BORDER 30
16
17struct seatop_move_tiling_event {
18 struct sway_container *con;
19 struct sway_node *target_node;
20 enum wlr_edges target_edge;
21 struct wlr_box drop_box;
22 double ref_lx, ref_ly; // cursor's x/y at start of op
23 bool threshold_reached;
24};
25
26static void handle_render(struct sway_seat *seat,
27 struct sway_output *output, pixman_region32_t *damage) {
28 struct seatop_move_tiling_event *e = seat->seatop_data;
29 if (!e->threshold_reached) {
30 return;
31 }
32 if (e->target_node && node_get_output(e->target_node) == output) {
33 float color[4];
34 memcpy(&color, config->border_colors.focused.indicator,
35 sizeof(float) * 4);
36 premultiply_alpha(color, 0.5);
37 struct wlr_box box;
38 memcpy(&box, &e->drop_box, sizeof(struct wlr_box));
39 scale_box(&box, output->wlr_output->scale);
40 render_rect(output->wlr_output, damage, &box, color);
41 }
42}
43
44static void handle_motion_prethreshold(struct sway_seat *seat) {
45 struct seatop_move_tiling_event *e = seat->seatop_data;
46 double cx = seat->cursor->cursor->x;
47 double cy = seat->cursor->cursor->y;
48 double sx = e->ref_lx;
49 double sy = e->ref_ly;
50
51 // Get the scaled threshold for the output. Even if the operation goes
52 // across multiple outputs of varying scales, just use the scale for the
53 // output that the cursor is currently on for simplicity.
54 struct wlr_output *wlr_output = wlr_output_layout_output_at(
55 root->output_layout, cx, cy);
56 double output_scale = wlr_output ? wlr_output->scale : 1;
57 double threshold = config->tiling_drag_threshold * output_scale;
58 threshold *= threshold;
59
60 // If the threshold has been exceeded, start the actual drag
61 if ((cx - sx) * (cx - sx) + (cy - sy) * (cy - sy) > threshold) {
62 e->threshold_reached = true;
63 cursor_set_image(seat->cursor, "grab", NULL);
64 }
65}
66
67static void resize_box(struct wlr_box *box, enum wlr_edges edge,
68 int thickness) {
69 switch (edge) {
70 case WLR_EDGE_TOP:
71 box->height = thickness;
72 break;
73 case WLR_EDGE_LEFT:
74 box->width = thickness;
75 break;
76 case WLR_EDGE_RIGHT:
77 box->x = box->x + box->width - thickness;
78 box->width = thickness;
79 break;
80 case WLR_EDGE_BOTTOM:
81 box->y = box->y + box->height - thickness;
82 box->height = thickness;
83 break;
84 case WLR_EDGE_NONE:
85 box->x += thickness;
86 box->y += thickness;
87 box->width -= thickness * 2;
88 box->height -= thickness * 2;
89 break;
90 }
91}
92
93static void handle_motion_postthreshold(struct sway_seat *seat) {
94 struct seatop_move_tiling_event *e = seat->seatop_data;
95 struct wlr_surface *surface = NULL;
96 double sx, sy;
97 struct sway_cursor *cursor = seat->cursor;
98 struct sway_node *node = node_at_coords(seat,
99 cursor->cursor->x, cursor->cursor->y, &surface, &sx, &sy);
100 // Damage the old location
101 desktop_damage_box(&e->drop_box);
102
103 if (!node) {
104 // Eg. hovered over a layer surface such as swaybar
105 e->target_node = NULL;
106 e->target_edge = WLR_EDGE_NONE;
107 return;
108 }
109
110 if (node->type == N_WORKSPACE) {
111 // Emtpy workspace
112 e->target_node = node;
113 e->target_edge = WLR_EDGE_NONE;
114 workspace_get_box(node->sway_workspace, &e->drop_box);
115 desktop_damage_box(&e->drop_box);
116 return;
117 }
118
119 // Deny moving within own workspace if this is the only child
120 struct sway_container *con = node->sway_container;
121 if (workspace_num_tiling_views(e->con->workspace) == 1 &&
122 con->workspace == e->con->workspace) {
123 e->target_node = NULL;
124 e->target_edge = WLR_EDGE_NONE;
125 return;
126 }
127
128 // Traverse the ancestors, trying to find a layout container perpendicular
129 // to the edge. Eg. close to the top or bottom of a horiz layout.
130 while (con) {
131 enum wlr_edges edge = WLR_EDGE_NONE;
132 enum sway_container_layout layout = container_parent_layout(con);
133 struct wlr_box parent;
134 con->parent ? container_get_box(con->parent, &parent) :
135 workspace_get_box(con->workspace, &parent);
136 if (layout == L_HORIZ || layout == L_TABBED) {
137 if (cursor->cursor->y < parent.y + DROP_LAYOUT_BORDER) {
138 edge = WLR_EDGE_TOP;
139 } else if (cursor->cursor->y > parent.y + parent.height
140 - DROP_LAYOUT_BORDER) {
141 edge = WLR_EDGE_BOTTOM;
142 }
143 } else if (layout == L_VERT || layout == L_STACKED) {
144 if (cursor->cursor->x < parent.x + DROP_LAYOUT_BORDER) {
145 edge = WLR_EDGE_LEFT;
146 } else if (cursor->cursor->x > parent.x + parent.width
147 - DROP_LAYOUT_BORDER) {
148 edge = WLR_EDGE_RIGHT;
149 }
150 }
151 if (edge) {
152 e->target_node = node_get_parent(&con->node);
153 e->target_edge = edge;
154 node_get_box(e->target_node, &e->drop_box);
155 resize_box(&e->drop_box, edge, DROP_LAYOUT_BORDER);
156 desktop_damage_box(&e->drop_box);
157 return;
158 }
159 con = con->parent;
160 }
161
162 // Use the hovered view - but we must be over the actual surface
163 con = node->sway_container;
164 if (!con->view->surface || node == &e->con->node) {
165 e->target_node = NULL;
166 e->target_edge = WLR_EDGE_NONE;
167 return;
168 }
169
170 // Find the closest edge
171 size_t thickness = fmin(con->content_width, con->content_height) * 0.3;
172 size_t closest_dist = INT_MAX;
173 size_t dist;
174 e->target_edge = WLR_EDGE_NONE;
175 if ((dist = cursor->cursor->y - con->y) < closest_dist) {
176 closest_dist = dist;
177 e->target_edge = WLR_EDGE_TOP;
178 }
179 if ((dist = cursor->cursor->x - con->x) < closest_dist) {
180 closest_dist = dist;
181 e->target_edge = WLR_EDGE_LEFT;
182 }
183 if ((dist = con->x + con->width - cursor->cursor->x) < closest_dist) {
184 closest_dist = dist;
185 e->target_edge = WLR_EDGE_RIGHT;
186 }
187 if ((dist = con->y + con->height - cursor->cursor->y) < closest_dist) {
188 closest_dist = dist;
189 e->target_edge = WLR_EDGE_BOTTOM;
190 }
191
192 if (closest_dist > thickness) {
193 e->target_edge = WLR_EDGE_NONE;
194 }
195
196 e->target_node = node;
197 e->drop_box.x = con->content_x;
198 e->drop_box.y = con->content_y;
199 e->drop_box.width = con->content_width;
200 e->drop_box.height = con->content_height;
201 resize_box(&e->drop_box, e->target_edge, thickness);
202 desktop_damage_box(&e->drop_box);
203}
204
205static void handle_motion(struct sway_seat *seat, uint32_t time_msec) {
206 struct seatop_move_tiling_event *e = seat->seatop_data;
207 if (e->threshold_reached) {
208 handle_motion_postthreshold(seat);
209 } else {
210 handle_motion_prethreshold(seat);
211 }
212}
213
214static void handle_abort(struct sway_seat *seat) {
215 cursor_set_image(seat->cursor, "left_ptr", NULL);
216}
217
218static bool is_parallel(enum sway_container_layout layout,
219 enum wlr_edges edge) {
220 bool layout_is_horiz = layout == L_HORIZ || layout == L_TABBED;
221 bool edge_is_horiz = edge == WLR_EDGE_LEFT || edge == WLR_EDGE_RIGHT;
222 return layout_is_horiz == edge_is_horiz;
223}
224
225static void handle_finish(struct sway_seat *seat) {
226 struct seatop_move_tiling_event *e = seat->seatop_data;
227
228 if (!e->target_node) {
229 handle_abort(seat);
230 return;
231 }
232
233 struct sway_container *con = e->con;
234 struct sway_container *old_parent = con->parent;
235 struct sway_workspace *old_ws = con->workspace;
236 struct sway_node *target_node = e->target_node;
237 struct sway_workspace *new_ws = target_node->type == N_WORKSPACE ?
238 target_node->sway_workspace : target_node->sway_container->workspace;
239 enum wlr_edges edge = e->target_edge;
240 int after = edge != WLR_EDGE_TOP && edge != WLR_EDGE_LEFT;
241
242 container_detach(con);
243
244 // Moving container into empty workspace
245 if (target_node->type == N_WORKSPACE && edge == WLR_EDGE_NONE) {
246 workspace_add_tiling(new_ws, con);
247 } else if (target_node->type == N_CONTAINER) {
248 // Moving container before/after another
249 struct sway_container *target = target_node->sway_container;
250 enum sway_container_layout layout = container_parent_layout(target);
251 if (edge && !is_parallel(layout, edge)) {
252 enum sway_container_layout new_layout = edge == WLR_EDGE_TOP ||
253 edge == WLR_EDGE_BOTTOM ? L_VERT : L_HORIZ;
254 container_split(target, new_layout);
255 }
256 container_add_sibling(target, con, after);
257 } else {
258 // Target is a workspace which requires splitting
259 enum sway_container_layout new_layout = edge == WLR_EDGE_TOP ||
260 edge == WLR_EDGE_BOTTOM ? L_VERT : L_HORIZ;
261 workspace_split(new_ws, new_layout);
262 workspace_insert_tiling(new_ws, con, after);
263 }
264
265 if (old_parent) {
266 container_reap_empty(old_parent);
267 }
268
269 // This is a bit dirty, but we'll set the dimensions to that of a sibling.
270 // I don't think there's any other way to make it consistent without
271 // changing how we auto-size containers.
272 list_t *siblings = container_get_siblings(con);
273 if (siblings->length > 1) {
274 int index = list_find(siblings, con);
275 struct sway_container *sibling = index == 0 ?
276 siblings->items[1] : siblings->items[index - 1];
277 con->width = sibling->width;
278 con->height = sibling->height;
279 }
280
281 arrange_workspace(old_ws);
282 if (new_ws != old_ws) {
283 arrange_workspace(new_ws);
284 }
285
286 cursor_set_image(seat->cursor, "left_ptr", NULL);
287}
288
289static void handle_unref(struct sway_seat *seat, struct sway_container *con) {
290 struct seatop_move_tiling_event *e = seat->seatop_data;
291 if (e->target_node == &con->node) { // Drop target
292 e->target_node = NULL;
293 }
294 if (e->con == con) { // The container being moved
295 seatop_abort(seat);
296 }
297}
298
299static const struct sway_seatop_impl seatop_impl = {
300 .motion = handle_motion,
301 .finish = handle_finish,
302 .abort = handle_abort,
303 .unref = handle_unref,
304 .render = handle_render,
305};
306
307void seatop_begin_move_tiling_threshold(struct sway_seat *seat,
308 struct sway_container *con, uint32_t button) {
309 seatop_abort(seat);
310
311 struct seatop_move_tiling_event *e =
312 calloc(1, sizeof(struct seatop_move_tiling_event));
313 if (!e) {
314 return;
315 }
316 e->con = con;
317 e->ref_lx = seat->cursor->cursor->x;
318 e->ref_ly = seat->cursor->cursor->y;
319
320 seat->seatop_impl = &seatop_impl;
321 seat->seatop_data = e;
322 seat->seatop_button = button;
323
324 container_raise_floating(con);
325}
326
327void seatop_begin_move_tiling(struct sway_seat *seat,
328 struct sway_container *con, uint32_t button) {
329 seatop_begin_move_tiling_threshold(seat, con, button);
330 struct seatop_move_tiling_event *e = seat->seatop_data;
331 if (e) {
332 e->threshold_reached = true;
333 cursor_set_image(seat->cursor, "grab", NULL);
334 }
335}
diff --git a/sway/input/seatop_resize_floating.c b/sway/input/seatop_resize_floating.c
new file mode 100644
index 00000000..12851b40
--- /dev/null
+++ b/sway/input/seatop_resize_floating.c
@@ -0,0 +1,199 @@
1#define _POSIX_C_SOURCE 200809L
2#include <limits.h>
3#include <wlr/types/wlr_cursor.h>
4#include <wlr/types/wlr_xcursor_manager.h>
5#include "sway/input/cursor.h"
6#include "sway/input/seat.h"
7#include "sway/tree/arrange.h"
8#include "sway/tree/view.h"
9#include "sway/tree/workspace.h"
10
11struct seatop_resize_floating_event {
12 struct sway_container *con;
13 enum wlr_edges edge;
14 bool preserve_ratio;
15 double ref_lx, ref_ly; // cursor's x/y at start of op
16 double ref_width, ref_height; // container's size at start of op
17 double ref_con_lx, ref_con_ly; // container's x/y at start of op
18};
19
20static void calculate_floating_constraints(struct sway_container *con,
21 int *min_width, int *max_width, int *min_height, int *max_height) {
22 if (config->floating_minimum_width == -1) { // no minimum
23 *min_width = 0;
24 } else if (config->floating_minimum_width == 0) { // automatic
25 *min_width = 75;
26 } else {
27 *min_width = config->floating_minimum_width;
28 }
29
30 if (config->floating_minimum_height == -1) { // no minimum
31 *min_height = 0;
32 } else if (config->floating_minimum_height == 0) { // automatic
33 *min_height = 50;
34 } else {
35 *min_height = config->floating_minimum_height;
36 }
37
38 if (config->floating_maximum_width == -1) { // no maximum
39 *max_width = INT_MAX;
40 } else if (config->floating_maximum_width == 0) { // automatic
41 *max_width = con->workspace->width;
42 } else {
43 *max_width = config->floating_maximum_width;
44 }
45
46 if (config->floating_maximum_height == -1) { // no maximum
47 *max_height = INT_MAX;
48 } else if (config->floating_maximum_height == 0) { // automatic
49 *max_height = con->workspace->height;
50 } else {
51 *max_height = config->floating_maximum_height;
52 }
53}
54
55static void handle_motion(struct sway_seat *seat, uint32_t time_msec) {
56 struct seatop_resize_floating_event *e = seat->seatop_data;
57 struct sway_container *con = e->con;
58 enum wlr_edges edge = e->edge;
59 struct sway_cursor *cursor = seat->cursor;
60
61 // The amount the mouse has moved since the start of the resize operation
62 // Positive is down/right
63 double mouse_move_x = cursor->cursor->x - e->ref_lx;
64 double mouse_move_y = cursor->cursor->y - e->ref_ly;
65
66 if (edge == WLR_EDGE_TOP || edge == WLR_EDGE_BOTTOM) {
67 mouse_move_x = 0;
68 }
69 if (edge == WLR_EDGE_LEFT || edge == WLR_EDGE_RIGHT) {
70 mouse_move_y = 0;
71 }
72
73 double grow_width = edge & WLR_EDGE_LEFT ? -mouse_move_x : mouse_move_x;
74 double grow_height = edge & WLR_EDGE_TOP ? -mouse_move_y : mouse_move_y;
75
76 if (e->preserve_ratio) {
77 double x_multiplier = grow_width / e->ref_width;
78 double y_multiplier = grow_height / e->ref_height;
79 double max_multiplier = fmax(x_multiplier, y_multiplier);
80 grow_width = e->ref_width * max_multiplier;
81 grow_height = e->ref_height * max_multiplier;
82 }
83
84 // Determine new width/height, and accommodate for floating min/max values
85 double width = e->ref_width + grow_width;
86 double height = e->ref_height + grow_height;
87 int min_width, max_width, min_height, max_height;
88 calculate_floating_constraints(con, &min_width, &max_width,
89 &min_height, &max_height);
90 width = fmax(min_width, fmin(width, max_width));
91 height = fmax(min_height, fmin(height, max_height));
92
93 // Apply the view's min/max size
94 if (con->view) {
95 double view_min_width, view_max_width, view_min_height, view_max_height;
96 view_get_constraints(con->view, &view_min_width, &view_max_width,
97 &view_min_height, &view_max_height);
98 width = fmax(view_min_width, fmin(width, view_max_width));
99 height = fmax(view_min_height, fmin(height, view_max_height));
100 }
101
102 // Recalculate these, in case we hit a min/max limit
103 grow_width = width - e->ref_width;
104 grow_height = height - e->ref_height;
105
106 // Determine grow x/y values - these are relative to the container's x/y at
107 // the start of the resize operation.
108 double grow_x = 0, grow_y = 0;
109 if (edge & WLR_EDGE_LEFT) {
110 grow_x = -grow_width;
111 } else if (edge & WLR_EDGE_RIGHT) {
112 grow_x = 0;
113 } else {
114 grow_x = -grow_width / 2;
115 }
116 if (edge & WLR_EDGE_TOP) {
117 grow_y = -grow_height;
118 } else if (edge & WLR_EDGE_BOTTOM) {
119 grow_y = 0;
120 } else {
121 grow_y = -grow_height / 2;
122 }
123
124 // Determine the amounts we need to bump everything relative to the current
125 // size.
126 int relative_grow_width = width - con->width;
127 int relative_grow_height = height - con->height;
128 int relative_grow_x = (e->ref_con_lx + grow_x) - con->x;
129 int relative_grow_y = (e->ref_con_ly + grow_y) - con->y;
130
131 // Actually resize stuff
132 con->x += relative_grow_x;
133 con->y += relative_grow_y;
134 con->width += relative_grow_width;
135 con->height += relative_grow_height;
136
137 con->content_x += relative_grow_x;
138 con->content_y += relative_grow_y;
139 con->content_width += relative_grow_width;
140 con->content_height += relative_grow_height;
141
142 arrange_container(con);
143}
144
145static void handle_finish(struct sway_seat *seat) {
146 cursor_set_image(seat->cursor, "left_ptr", NULL);
147}
148
149static void handle_abort(struct sway_seat *seat) {
150 cursor_set_image(seat->cursor, "left_ptr", NULL);
151}
152
153static void handle_unref(struct sway_seat *seat, struct sway_container *con) {
154 struct seatop_resize_floating_event *e = seat->seatop_data;
155 if (e->con == con) {
156 seatop_abort(seat);
157 }
158}
159
160static const struct sway_seatop_impl seatop_impl = {
161 .motion = handle_motion,
162 .finish = handle_finish,
163 .abort = handle_abort,
164 .unref = handle_unref,
165};
166
167void seatop_begin_resize_floating(struct sway_seat *seat,
168 struct sway_container *con, uint32_t button, enum wlr_edges edge) {
169 seatop_abort(seat);
170
171 struct seatop_resize_floating_event *e =
172 calloc(1, sizeof(struct seatop_resize_floating_event));
173 if (!e) {
174 return;
175 }
176 e->con = con;
177
178 struct wlr_keyboard *keyboard = wlr_seat_get_keyboard(seat->wlr_seat);
179 e->preserve_ratio = keyboard &&
180 (wlr_keyboard_get_modifiers(keyboard) & WLR_MODIFIER_SHIFT);
181
182 e->edge = edge == WLR_EDGE_NONE ? WLR_EDGE_BOTTOM | WLR_EDGE_RIGHT : edge;
183 e->ref_lx = seat->cursor->cursor->x;
184 e->ref_ly = seat->cursor->cursor->y;
185 e->ref_con_lx = con->x;
186 e->ref_con_ly = con->y;
187 e->ref_width = con->width;
188 e->ref_height = con->height;
189
190 seat->seatop_impl = &seatop_impl;
191 seat->seatop_data = e;
192 seat->seatop_button = button;
193
194 container_raise_floating(con);
195
196 const char *image = edge == WLR_EDGE_NONE ?
197 "se-resize" : wlr_xcursor_get_resize_name(edge);
198 cursor_set_image(seat->cursor, image, NULL);
199}
diff --git a/sway/input/seatop_resize_tiling.c b/sway/input/seatop_resize_tiling.c
new file mode 100644
index 00000000..30431f04
--- /dev/null
+++ b/sway/input/seatop_resize_tiling.c
@@ -0,0 +1,92 @@
1#define _POSIX_C_SOURCE 200809L
2#include <wlr/types/wlr_cursor.h>
3#include "sway/commands.h"
4#include "sway/input/cursor.h"
5#include "sway/input/seat.h"
6
7struct seatop_resize_tiling_event {
8 struct sway_container *con;
9 enum wlr_edges edge;
10 double ref_lx, ref_ly; // cursor's x/y at start of op
11 double ref_width, ref_height; // container's size at start of op
12 double ref_con_lx, ref_con_ly; // container's x/y at start of op
13};
14
15static void handle_motion(struct sway_seat *seat, uint32_t time_msec) {
16 struct seatop_resize_tiling_event *e = seat->seatop_data;
17 int amount_x = 0;
18 int amount_y = 0;
19 int moved_x = seat->cursor->cursor->x - e->ref_lx;
20 int moved_y = seat->cursor->cursor->y - e->ref_ly;
21 enum wlr_edges edge_x = WLR_EDGE_NONE;
22 enum wlr_edges edge_y = WLR_EDGE_NONE;
23 struct sway_container *con = e->con;
24
25 if (e->edge & WLR_EDGE_TOP) {
26 amount_y = (e->ref_height - moved_y) - con->height;
27 edge_y = WLR_EDGE_TOP;
28 } else if (e->edge & WLR_EDGE_BOTTOM) {
29 amount_y = (e->ref_height + moved_y) - con->height;
30 edge_y = WLR_EDGE_BOTTOM;
31 }
32 if (e->edge & WLR_EDGE_LEFT) {
33 amount_x = (e->ref_width - moved_x) - con->width;
34 edge_x = WLR_EDGE_LEFT;
35 } else if (e->edge & WLR_EDGE_RIGHT) {
36 amount_x = (e->ref_width + moved_x) - con->width;
37 edge_x = WLR_EDGE_RIGHT;
38 }
39
40 if (amount_x != 0) {
41 container_resize_tiled(e->con, edge_x, amount_x);
42 }
43 if (amount_y != 0) {
44 container_resize_tiled(e->con, edge_y, amount_y);
45 }
46}
47
48static void handle_finish(struct sway_seat *seat) {
49 cursor_set_image(seat->cursor, "left_ptr", NULL);
50}
51
52static void handle_abort(struct sway_seat *seat) {
53 cursor_set_image(seat->cursor, "left_ptr", NULL);
54}
55
56static void handle_unref(struct sway_seat *seat, struct sway_container *con) {
57 struct seatop_resize_tiling_event *e = seat->seatop_data;
58 if (e->con == con) {
59 seatop_abort(seat);
60 }
61}
62
63static const struct sway_seatop_impl seatop_impl = {
64 .motion = handle_motion,
65 .finish = handle_finish,
66 .abort = handle_abort,
67 .unref = handle_unref,
68};
69
70void seatop_begin_resize_tiling(struct sway_seat *seat,
71 struct sway_container *con, uint32_t button, enum wlr_edges edge) {
72 seatop_abort(seat);
73
74 struct seatop_resize_tiling_event *e =
75 calloc(1, sizeof(struct seatop_resize_tiling_event));
76 if (!e) {
77 return;
78 }
79 e->con = con;
80 e->edge = edge;
81
82 e->ref_lx = seat->cursor->cursor->x;
83 e->ref_ly = seat->cursor->cursor->y;
84 e->ref_con_lx = con->x;
85 e->ref_con_ly = con->y;
86 e->ref_width = con->width;
87 e->ref_height = con->height;
88
89 seat->seatop_impl = &seatop_impl;
90 seat->seatop_data = e;
91 seat->seatop_button = button;
92}
diff --git a/sway/ipc-json.c b/sway/ipc-json.c
index e3450df1..6e5ba4fd 100644
--- a/sway/ipc-json.c
+++ b/sway/ipc-json.c
@@ -1,6 +1,8 @@
1#include <json-c/json.h> 1#include <json-c/json.h>
2#include <libevdev/libevdev.h>
2#include <stdio.h> 3#include <stdio.h>
3#include <ctype.h> 4#include <ctype.h>
5#include "config.h"
4#include "log.h" 6#include "log.h"
5#include "sway/config.h" 7#include "sway/config.h"
6#include "sway/ipc-json.h" 8#include "sway/ipc-json.h"
@@ -9,12 +11,17 @@
9#include "sway/tree/workspace.h" 11#include "sway/tree/workspace.h"
10#include "sway/output.h" 12#include "sway/output.h"
11#include "sway/input/input-manager.h" 13#include "sway/input/input-manager.h"
14#include "sway/input/cursor.h"
12#include "sway/input/seat.h" 15#include "sway/input/seat.h"
16#include <wlr/backend/libinput.h>
13#include <wlr/types/wlr_box.h> 17#include <wlr/types/wlr_box.h>
14#include <wlr/types/wlr_output.h> 18#include <wlr/types/wlr_output.h>
15#include <xkbcommon/xkbcommon.h> 19#include <xkbcommon/xkbcommon.h>
16#include "wlr-layer-shell-unstable-v1-protocol.h" 20#include "wlr-layer-shell-unstable-v1-protocol.h"
17 21
22static const int i3_output_id = INT32_MAX;
23static const int i3_scratch_id = INT32_MAX - 1;
24
18static const char *ipc_json_layout_description(enum sway_container_layout l) { 25static const char *ipc_json_layout_description(enum sway_container_layout l) {
19 switch (l) { 26 switch (l) {
20 case L_VERT: 27 case L_VERT:
@@ -32,15 +39,68 @@ static const char *ipc_json_layout_description(enum sway_container_layout l) {
32} 39}
33 40
34static const char *ipc_json_orientation_description(enum sway_container_layout l) { 41static const char *ipc_json_orientation_description(enum sway_container_layout l) {
35 if (l == L_VERT) { 42 switch (l) {
43 case L_VERT:
36 return "vertical"; 44 return "vertical";
45 case L_HORIZ:
46 return "horizontal";
47 default:
48 return "none";
37 } 49 }
50}
38 51
39 if (l == L_HORIZ) { 52static const char *ipc_json_border_description(enum sway_container_border border) {
40 return "horizontal"; 53 switch (border) {
54 case B_NONE:
55 return "none";
56 case B_PIXEL:
57 return "pixel";
58 case B_NORMAL:
59 return "normal";
60 case B_CSD:
61 return "csd";
41 } 62 }
63 return "unknown";
64}
42 65
43 return "none"; 66static const char *ipc_json_output_transform_description(enum wl_output_transform transform) {
67 switch (transform) {
68 case WL_OUTPUT_TRANSFORM_NORMAL:
69 return "normal";
70 case WL_OUTPUT_TRANSFORM_90:
71 return "90";
72 case WL_OUTPUT_TRANSFORM_180:
73 return "180";
74 case WL_OUTPUT_TRANSFORM_270:
75 return "270";
76 case WL_OUTPUT_TRANSFORM_FLIPPED:
77 return "flipped";
78 case WL_OUTPUT_TRANSFORM_FLIPPED_90:
79 return "flipped-90";
80 case WL_OUTPUT_TRANSFORM_FLIPPED_180:
81 return "flipped-180";
82 case WL_OUTPUT_TRANSFORM_FLIPPED_270:
83 return "flipped-270";
84 }
85 return NULL;
86}
87
88static const char *ipc_json_device_type_description(struct sway_input_device *device) {
89 switch (device->wlr_device->type) {
90 case WLR_INPUT_DEVICE_POINTER:
91 return "pointer";
92 case WLR_INPUT_DEVICE_KEYBOARD:
93 return "keyboard";
94 case WLR_INPUT_DEVICE_TOUCH:
95 return "touch";
96 case WLR_INPUT_DEVICE_TABLET_TOOL:
97 return "tablet_tool";
98 case WLR_INPUT_DEVICE_TABLET_PAD:
99 return "tablet_pad";
100 case WLR_INPUT_DEVICE_SWITCH:
101 return "switch";
102 }
103 return "unknown";
44} 104}
45 105
46json_object *ipc_json_get_version(void) { 106json_object *ipc_json_get_version(void) {
@@ -76,30 +136,43 @@ static json_object *ipc_json_create_empty_rect(void) {
76 return ipc_json_create_rect(&empty); 136 return ipc_json_create_rect(&empty);
77} 137}
78 138
79static void ipc_json_describe_root(struct sway_root *root, json_object *object) { 139static json_object *ipc_json_create_node(int id, char *name,
80 json_object_object_add(object, "type", json_object_new_string("root")); 140 bool focused, json_object *focus, struct wlr_box *box) {
141 json_object *object = json_object_new_object();
142
143 json_object_object_add(object, "id", json_object_new_int(id));
144 json_object_object_add(object, "name",
145 name ? json_object_new_string(name) : NULL);
146 json_object_object_add(object, "rect", ipc_json_create_rect(box));
147 json_object_object_add(object, "focused", json_object_new_boolean(focused));
148 json_object_object_add(object, "focus", focus);
149
150 // set default values to be compatible with i3
151 json_object_object_add(object, "border",
152 json_object_new_string(
153 ipc_json_border_description(B_NONE)));
154 json_object_object_add(object, "current_border_width",
155 json_object_new_int(0));
156 json_object_object_add(object, "layout",
157 json_object_new_string(
158 ipc_json_layout_description(L_HORIZ)));
159 json_object_object_add(object, "orientation",
160 json_object_new_string(
161 ipc_json_orientation_description(L_HORIZ)));
162 json_object_object_add(object, "percent", NULL);
163 json_object_object_add(object, "window_rect", ipc_json_create_empty_rect());
164 json_object_object_add(object, "deco_rect", ipc_json_create_empty_rect());
165 json_object_object_add(object, "geometry", ipc_json_create_empty_rect());
166 json_object_object_add(object, "window", NULL);
167 json_object_object_add(object, "urgent", json_object_new_boolean(false));
168 json_object_object_add(object, "floating_nodes", json_object_new_array());
169 json_object_object_add(object, "sticky", json_object_new_boolean(false));
170
171 return object;
81} 172}
82 173
83static const char *ipc_json_get_output_transform(enum wl_output_transform transform) { 174static void ipc_json_describe_root(struct sway_root *root, json_object *object) {
84 switch (transform) { 175 json_object_object_add(object, "type", json_object_new_string("root"));
85 case WL_OUTPUT_TRANSFORM_NORMAL:
86 return "normal";
87 case WL_OUTPUT_TRANSFORM_90:
88 return "90";
89 case WL_OUTPUT_TRANSFORM_180:
90 return "180";
91 case WL_OUTPUT_TRANSFORM_270:
92 return "270";
93 case WL_OUTPUT_TRANSFORM_FLIPPED:
94 return "flipped";
95 case WL_OUTPUT_TRANSFORM_FLIPPED_90:
96 return "flipped-90";
97 case WL_OUTPUT_TRANSFORM_FLIPPED_180:
98 return "flipped-180";
99 case WL_OUTPUT_TRANSFORM_FLIPPED_270:
100 return "flipped-270";
101 }
102 return NULL;
103} 176}
104 177
105static void ipc_json_describe_output(struct sway_output *output, 178static void ipc_json_describe_output(struct sway_output *output,
@@ -110,7 +183,8 @@ static void ipc_json_describe_output(struct sway_output *output,
110 json_object_object_add(object, "primary", json_object_new_boolean(false)); 183 json_object_object_add(object, "primary", json_object_new_boolean(false));
111 json_object_object_add(object, "layout", json_object_new_string("output")); 184 json_object_object_add(object, "layout", json_object_new_string("output"));
112 json_object_object_add(object, "orientation", 185 json_object_object_add(object, "orientation",
113 json_object_new_string(ipc_json_orientation_description(L_NONE))); 186 json_object_new_string(
187 ipc_json_orientation_description(L_NONE)));
114 json_object_object_add(object, "make", 188 json_object_object_add(object, "make",
115 json_object_new_string(wlr_output->make)); 189 json_object_new_string(wlr_output->make));
116 json_object_object_add(object, "model", 190 json_object_object_add(object, "model",
@@ -121,7 +195,7 @@ static void ipc_json_describe_output(struct sway_output *output,
121 json_object_new_double(wlr_output->scale)); 195 json_object_new_double(wlr_output->scale));
122 json_object_object_add(object, "transform", 196 json_object_object_add(object, "transform",
123 json_object_new_string( 197 json_object_new_string(
124 ipc_json_get_output_transform(wlr_output->transform))); 198 ipc_json_output_transform_description(wlr_output->transform)));
125 199
126 struct sway_workspace *ws = output_get_active_workspace(output); 200 struct sway_workspace *ws = output_get_active_workspace(output);
127 json_object_object_add(object, "current_workspace", 201 json_object_object_add(object, "current_workspace",
@@ -187,6 +261,52 @@ json_object *ipc_json_describe_disabled_output(struct sway_output *output) {
187 return object; 261 return object;
188} 262}
189 263
264static json_object *ipc_json_describe_scratchpad_output(void) {
265 struct wlr_box box;
266 root_get_box(root, &box);
267
268 // Create focus stack for __i3_scratch workspace
269 json_object *workspace_focus = json_object_new_array();
270 for (int i = root->scratchpad->length - 1; i >= 0; --i) {
271 struct sway_container *container = root->scratchpad->items[i];
272 json_object_array_add(workspace_focus,
273 json_object_new_int(container->node.id));
274 }
275
276 json_object *workspace = ipc_json_create_node(i3_scratch_id,
277 "__i3_scratch", false, workspace_focus, &box);
278 json_object_object_add(workspace, "type",
279 json_object_new_string("workspace"));
280
281 // List all hidden scratchpad containers as floating nodes
282 json_object *floating_array = json_object_new_array();
283 for (int i = 0; i < root->scratchpad->length; ++i) {
284 struct sway_container *container = root->scratchpad->items[i];
285 if (!container->workspace) {
286 json_object_array_add(floating_array,
287 ipc_json_describe_node_recursive(&container->node));
288 }
289 }
290 json_object_object_add(workspace, "floating_nodes", floating_array);
291
292 // Create focus stack for __i3 output
293 json_object *output_focus = json_object_new_array();
294 json_object_array_add(output_focus, json_object_new_int(i3_scratch_id));
295
296 json_object *output = ipc_json_create_node(i3_output_id,
297 "__i3", false, output_focus, &box);
298 json_object_object_add(output, "type",
299 json_object_new_string("output"));
300 json_object_object_add(output, "layout",
301 json_object_new_string("output"));
302
303 json_object *nodes = json_object_new_array();
304 json_object_array_add(nodes, workspace);
305 json_object_object_add(output, "nodes", nodes);
306
307 return output;
308}
309
190static void ipc_json_describe_workspace(struct sway_workspace *workspace, 310static void ipc_json_describe_workspace(struct sway_workspace *workspace,
191 json_object *object) { 311 json_object *object) {
192 int num = isdigit(workspace->name[0]) ? atoi(workspace->name) : -1; 312 int num = isdigit(workspace->name[0]) ? atoi(workspace->name) : -1;
@@ -200,11 +320,12 @@ static void ipc_json_describe_workspace(struct sway_workspace *workspace,
200 json_object_object_add(object, "representation", workspace->representation ? 320 json_object_object_add(object, "representation", workspace->representation ?
201 json_object_new_string(workspace->representation) : NULL); 321 json_object_new_string(workspace->representation) : NULL);
202 322
203 const char *layout = ipc_json_layout_description(workspace->layout); 323 json_object_object_add(object, "layout",
204 json_object_object_add(object, "layout", json_object_new_string(layout)); 324 json_object_new_string(
205 325 ipc_json_layout_description(workspace->layout)));
206 const char *orientation = ipc_json_orientation_description(workspace->layout); 326 json_object_object_add(object, "orientation",
207 json_object_object_add(object, "orientation", json_object_new_string(orientation)); 327 json_object_new_string(
328 ipc_json_orientation_description(workspace->layout)));
208 329
209 // Floating 330 // Floating
210 json_object *floating_array = json_object_new_array(); 331 json_object *floating_array = json_object_new_array();
@@ -216,20 +337,6 @@ static void ipc_json_describe_workspace(struct sway_workspace *workspace,
216 json_object_object_add(object, "floating_nodes", floating_array); 337 json_object_object_add(object, "floating_nodes", floating_array);
217} 338}
218 339
219static const char *describe_container_border(enum sway_container_border border) {
220 switch (border) {
221 case B_NONE:
222 return "none";
223 case B_PIXEL:
224 return "pixel";
225 case B_NORMAL:
226 return "normal";
227 case B_CSD:
228 return "csd";
229 }
230 return "unknown";
231}
232
233static void ipc_json_describe_view(struct sway_container *c, json_object *object) { 340static void ipc_json_describe_view(struct sway_container *c, json_object *object) {
234 json_object_object_add(object, "pid", json_object_new_int(c->view->pid)); 341 json_object_object_add(object, "pid", json_object_new_int(c->view->pid));
235 342
@@ -307,15 +414,18 @@ static void ipc_json_describe_container(struct sway_container *c, json_object *o
307 json_object_new_string(container_is_floating(c) ? "floating_con" : "con")); 414 json_object_new_string(container_is_floating(c) ? "floating_con" : "con"));
308 415
309 json_object_object_add(object, "layout", 416 json_object_object_add(object, "layout",
310 json_object_new_string(ipc_json_layout_description(c->layout))); 417 json_object_new_string(
418 ipc_json_layout_description(c->layout)));
311 419
312 json_object_object_add(object, "orientation", 420 json_object_object_add(object, "orientation",
313 json_object_new_string(ipc_json_orientation_description(c->layout))); 421 json_object_new_string(
422 ipc_json_orientation_description(c->layout)));
314 423
315 bool urgent = c->view ? 424 bool urgent = c->view ?
316 view_is_urgent(c->view) : container_has_urgent_child(c); 425 view_is_urgent(c->view) : container_has_urgent_child(c);
317 json_object_object_add(object, "urgent", json_object_new_boolean(urgent)); 426 json_object_object_add(object, "urgent", json_object_new_boolean(urgent));
318 json_object_object_add(object, "sticky", json_object_new_boolean(c->is_sticky)); 427 json_object_object_add(object, "sticky", json_object_new_boolean(c->is_sticky));
428 json_object_object_add(object, "fullscreen_mode", json_object_new_int(c->is_fullscreen));
319 429
320 struct sway_node *parent = node_get_parent(&c->node); 430 struct sway_node *parent = node_get_parent(&c->node);
321 struct wlr_box parent_box = {0, 0, 0, 0}; 431 struct wlr_box parent_box = {0, 0, 0, 0};
@@ -331,7 +441,8 @@ static void ipc_json_describe_container(struct sway_container *c, json_object *o
331 } 441 }
332 442
333 json_object_object_add(object, "border", 443 json_object_object_add(object, "border",
334 json_object_new_string(describe_container_border(c->current.border))); 444 json_object_new_string(
445 ipc_json_border_description(c->current.border)));
335 json_object_object_add(object, "current_border_width", 446 json_object_object_add(object, "current_border_width",
336 json_object_new_int(c->current.border_thickness)); 447 json_object_new_int(c->current.border_thickness));
337 json_object_object_add(object, "floating_nodes", json_object_new_array()); 448 json_object_object_add(object, "floating_nodes", json_object_new_array());
@@ -372,17 +483,10 @@ static void focus_inactive_children_iterator(struct sway_node *node,
372json_object *ipc_json_describe_node(struct sway_node *node) { 483json_object *ipc_json_describe_node(struct sway_node *node) {
373 struct sway_seat *seat = input_manager_get_default_seat(); 484 struct sway_seat *seat = input_manager_get_default_seat();
374 bool focused = seat_get_focus(seat) == node; 485 bool focused = seat_get_focus(seat) == node;
375
376 json_object *object = json_object_new_object();
377 char *name = node_get_name(node); 486 char *name = node_get_name(node);
378 487
379 struct wlr_box box; 488 struct wlr_box box;
380 node_get_box(node, &box); 489 node_get_box(node, &box);
381 json_object_object_add(object, "id", json_object_new_int((int)node->id));
382 json_object_object_add(object, "name",
383 name ? json_object_new_string(name) : NULL);
384 json_object_object_add(object, "rect", ipc_json_create_rect(&box));
385 json_object_object_add(object, "focused", json_object_new_boolean(focused));
386 490
387 json_object *focus = json_object_new_array(); 491 json_object *focus = json_object_new_array();
388 struct focus_inactive_data data = { 492 struct focus_inactive_data data = {
@@ -390,24 +494,9 @@ json_object *ipc_json_describe_node(struct sway_node *node) {
390 .object = focus, 494 .object = focus,
391 }; 495 };
392 seat_for_each_node(seat, focus_inactive_children_iterator, &data); 496 seat_for_each_node(seat, focus_inactive_children_iterator, &data);
393 json_object_object_add(object, "focus", focus);
394 497
395 // set default values to be compatible with i3 498 json_object *object = ipc_json_create_node(
396 json_object_object_add(object, "border", 499 (int)node->id, name, focused, focus, &box);
397 json_object_new_string(describe_container_border(B_NONE)));
398 json_object_object_add(object, "current_border_width", json_object_new_int(0));
399 json_object_object_add(object, "layout",
400 json_object_new_string(ipc_json_layout_description(L_HORIZ)));
401 json_object_object_add(object, "orientation",
402 json_object_new_string(ipc_json_orientation_description(L_HORIZ)));
403 json_object_object_add(object, "percent", NULL);
404 json_object_object_add(object, "window_rect", ipc_json_create_empty_rect());
405 json_object_object_add(object, "deco_rect", ipc_json_create_empty_rect());
406 json_object_object_add(object, "geometry", ipc_json_create_empty_rect());
407 json_object_object_add(object, "window", NULL);
408 json_object_object_add(object, "urgent", json_object_new_boolean(false));
409 json_object_object_add(object, "floating_nodes", json_object_new_array());
410 json_object_object_add(object, "sticky", json_object_new_boolean(false));
411 500
412 switch (node->type) { 501 switch (node->type) {
413 case N_ROOT: 502 case N_ROOT:
@@ -434,6 +523,8 @@ json_object *ipc_json_describe_node_recursive(struct sway_node *node) {
434 json_object *children = json_object_new_array(); 523 json_object *children = json_object_new_array();
435 switch (node->type) { 524 switch (node->type) {
436 case N_ROOT: 525 case N_ROOT:
526 json_object_array_add(children,
527 ipc_json_describe_scratchpad_output());
437 for (i = 0; i < root->outputs->length; ++i) { 528 for (i = 0; i < root->outputs->length; ++i) {
438 struct sway_output *output = root->outputs->items[i]; 529 struct sway_output *output = root->outputs->items[i];
439 json_object_array_add(children, 530 json_object_array_add(children,
@@ -470,22 +561,6 @@ json_object *ipc_json_describe_node_recursive(struct sway_node *node) {
470 return object; 561 return object;
471} 562}
472 563
473static const char *describe_device_type(struct sway_input_device *device) {
474 switch (device->wlr_device->type) {
475 case WLR_INPUT_DEVICE_POINTER:
476 return "pointer";
477 case WLR_INPUT_DEVICE_KEYBOARD:
478 return "keyboard";
479 case WLR_INPUT_DEVICE_TOUCH:
480 return "touch";
481 case WLR_INPUT_DEVICE_TABLET_TOOL:
482 return "tablet_tool";
483 case WLR_INPUT_DEVICE_TABLET_PAD:
484 return "tablet_pad";
485 }
486 return "unknown";
487}
488
489json_object *ipc_json_describe_input(struct sway_input_device *device) { 564json_object *ipc_json_describe_input(struct sway_input_device *device) {
490 if (!(sway_assert(device, "Device must not be null"))) { 565 if (!(sway_assert(device, "Device must not be null"))) {
491 return NULL; 566 return NULL;
@@ -502,7 +577,8 @@ json_object *ipc_json_describe_input(struct sway_input_device *device) {
502 json_object_object_add(object, "product", 577 json_object_object_add(object, "product",
503 json_object_new_int(device->wlr_device->product)); 578 json_object_new_int(device->wlr_device->product));
504 json_object_object_add(object, "type", 579 json_object_object_add(object, "type",
505 json_object_new_string(describe_device_type(device))); 580 json_object_new_string(
581 ipc_json_device_type_description(device)));
506 582
507 if (device->wlr_device->type == WLR_INPUT_DEVICE_KEYBOARD) { 583 if (device->wlr_device->type == WLR_INPUT_DEVICE_KEYBOARD) {
508 struct wlr_keyboard *keyboard = device->wlr_device->keyboard; 584 struct wlr_keyboard *keyboard = device->wlr_device->keyboard;
@@ -525,6 +601,26 @@ json_object *ipc_json_describe_input(struct sway_input_device *device) {
525 } 601 }
526 } 602 }
527 603
604 if (wlr_input_device_is_libinput(device->wlr_device)) {
605 struct libinput_device *libinput_dev;
606 libinput_dev = wlr_libinput_get_device_handle(device->wlr_device);
607
608 const char *events = "unknown";
609 switch (libinput_device_config_send_events_get_mode(libinput_dev)) {
610 case LIBINPUT_CONFIG_SEND_EVENTS_ENABLED:
611 events = "enabled";
612 break;
613 case LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE:
614 events = "disabled_on_external_mouse";
615 break;
616 case LIBINPUT_CONFIG_SEND_EVENTS_DISABLED:
617 events = "disabled";
618 break;
619 }
620 json_object_object_add(object, "libinput_send_events",
621 json_object_new_string(events));
622 }
623
528 return object; 624 return object;
529} 625}
530 626
@@ -553,6 +649,31 @@ json_object *ipc_json_describe_seat(struct sway_seat *seat) {
553 return object; 649 return object;
554} 650}
555 651
652static uint32_t event_to_x11_button(uint32_t event) {
653 switch (event) {
654 case BTN_LEFT:
655 return 1;
656 case BTN_MIDDLE:
657 return 2;
658 case BTN_RIGHT:
659 return 3;
660 case SWAY_SCROLL_UP:
661 return 4;
662 case SWAY_SCROLL_DOWN:
663 return 5;
664 case SWAY_SCROLL_LEFT:
665 return 6;
666 case SWAY_SCROLL_RIGHT:
667 return 7;
668 case BTN_SIDE:
669 return 8;
670 case BTN_EXTRA:
671 return 9;
672 default:
673 return 0;
674 }
675}
676
556json_object *ipc_json_describe_bar_config(struct bar_config *bar) { 677json_object *ipc_json_describe_bar_config(struct bar_config *bar) {
557 if (!sway_assert(bar, "Bar must not be NULL")) { 678 if (!sway_assert(bar, "Bar must not be NULL")) {
558 return NULL; 679 return NULL;
@@ -569,18 +690,36 @@ json_object *ipc_json_describe_bar_config(struct bar_config *bar) {
569 json_object_new_string(bar->status_command) : NULL); 690 json_object_new_string(bar->status_command) : NULL);
570 json_object_object_add(json, "font", 691 json_object_object_add(json, "font",
571 json_object_new_string((bar->font) ? bar->font : config->font)); 692 json_object_new_string((bar->font) ? bar->font : config->font));
693
694 json_object *gaps = json_object_new_object();
695 json_object_object_add(gaps, "top",
696 json_object_new_int(bar->gaps.top));
697 json_object_object_add(gaps, "right",
698 json_object_new_int(bar->gaps.right));
699 json_object_object_add(gaps, "bottom",
700 json_object_new_int(bar->gaps.bottom));
701 json_object_object_add(gaps, "left",
702 json_object_new_int(bar->gaps.left));
703 json_object_object_add(json, "gaps", gaps);
704
572 if (bar->separator_symbol) { 705 if (bar->separator_symbol) {
573 json_object_object_add(json, "separator_symbol", 706 json_object_object_add(json, "separator_symbol",
574 json_object_new_string(bar->separator_symbol)); 707 json_object_new_string(bar->separator_symbol));
575 } 708 }
576 json_object_object_add(json, "bar_height", 709 json_object_object_add(json, "bar_height",
577 json_object_new_int(bar->height)); 710 json_object_new_int(bar->height));
711 json_object_object_add(json, "status_padding",
712 json_object_new_int(bar->status_padding));
713 json_object_object_add(json, "status_edge_padding",
714 json_object_new_int(bar->status_edge_padding));
578 json_object_object_add(json, "wrap_scroll", 715 json_object_object_add(json, "wrap_scroll",
579 json_object_new_boolean(bar->wrap_scroll)); 716 json_object_new_boolean(bar->wrap_scroll));
580 json_object_object_add(json, "workspace_buttons", 717 json_object_object_add(json, "workspace_buttons",
581 json_object_new_boolean(bar->workspace_buttons)); 718 json_object_new_boolean(bar->workspace_buttons));
582 json_object_object_add(json, "strip_workspace_numbers", 719 json_object_object_add(json, "strip_workspace_numbers",
583 json_object_new_boolean(bar->strip_workspace_numbers)); 720 json_object_new_boolean(bar->strip_workspace_numbers));
721 json_object_object_add(json, "strip_workspace_name",
722 json_object_new_boolean(bar->strip_workspace_name));
584 json_object_object_add(json, "binding_mode_indicator", 723 json_object_object_add(json, "binding_mode_indicator",
585 json_object_new_boolean(bar->binding_mode_indicator)); 724 json_object_new_boolean(bar->binding_mode_indicator));
586 json_object_object_add(json, "verbose", 725 json_object_object_add(json, "verbose",
@@ -680,6 +819,8 @@ json_object *ipc_json_describe_bar_config(struct bar_config *bar) {
680 struct bar_binding *binding = bar->bindings->items[i]; 819 struct bar_binding *binding = bar->bindings->items[i];
681 json_object *bind = json_object_new_object(); 820 json_object *bind = json_object_new_object();
682 json_object_object_add(bind, "input_code", 821 json_object_object_add(bind, "input_code",
822 json_object_new_int(event_to_x11_button(binding->button)));
823 json_object_object_add(bind, "event_code",
683 json_object_new_int(binding->button)); 824 json_object_new_int(binding->button));
684 json_object_object_add(bind, "command", 825 json_object_object_add(bind, "command",
685 json_object_new_string(binding->command)); 826 json_object_new_string(binding->command));
@@ -699,5 +840,41 @@ json_object *ipc_json_describe_bar_config(struct bar_config *bar) {
699 } 840 }
700 json_object_object_add(json, "outputs", outputs); 841 json_object_object_add(json, "outputs", outputs);
701 } 842 }
843#if HAVE_TRAY
844 // Add tray outputs if defined
845 if (bar->tray_outputs && bar->tray_outputs->length > 0) {
846 json_object *tray_outputs = json_object_new_array();
847 for (int i = 0; i < bar->tray_outputs->length; ++i) {
848 const char *name = bar->tray_outputs->items[i];
849 json_object_array_add(tray_outputs, json_object_new_string(name));
850 }
851 json_object_object_add(json, "tray_outputs", tray_outputs);
852 }
853
854 json_object *tray_bindings = json_object_new_array();
855 for (int i = 0; i < 10; ++i) {
856 if (bar->tray_bindings[i]) {
857 json_object *bind = json_object_new_object();
858 json_object_object_add(bind, "input_code",
859 json_object_new_int(i));
860 json_object_object_add(bind, "command",
861 json_object_new_string(bar->tray_bindings[i]));
862 json_object_array_add(tray_bindings, bind);
863 }
864 }
865 if (json_object_array_length(tray_bindings) > 0) {
866 json_object_object_add(json, "tray_bindings", tray_bindings);
867 } else {
868 json_object_put(tray_bindings);
869 }
870
871 if (bar->icon_theme) {
872 json_object_object_add(json, "icon_theme",
873 json_object_new_string(bar->icon_theme));
874 }
875
876 json_object_object_add(json, "tray_padding",
877 json_object_new_int(bar->tray_padding));
878#endif
702 return json; 879 return json;
703} 880}
diff --git a/sway/ipc-server.c b/sway/ipc-server.c
index 6466d263..ff1bc89f 100644
--- a/sway/ipc-server.c
+++ b/sway/ipc-server.c
@@ -1,10 +1,6 @@
1// See https://i3wm.org/docs/ipc.html for protocol information 1// See https://i3wm.org/docs/ipc.html for protocol information
2#define _POSIX_C_SOURCE 200112L 2#define _POSIX_C_SOURCE 200112L
3#ifdef __linux__
4#include <linux/input-event-codes.h> 3#include <linux/input-event-codes.h>
5#elif __FreeBSD__
6#include <dev/evdev/input-event-codes.h>
7#endif
8#include <assert.h> 4#include <assert.h>
9#include <errno.h> 5#include <errno.h>
10#include <fcntl.h> 6#include <fcntl.h>
@@ -73,14 +69,11 @@ static void handle_display_destroy(struct wl_listener *listener, void *data) {
73 unlink(ipc_sockaddr->sun_path); 69 unlink(ipc_sockaddr->sun_path);
74 70
75 while (ipc_client_list->length) { 71 while (ipc_client_list->length) {
76 struct ipc_client *client = ipc_client_list->items[0]; 72 ipc_client_disconnect(ipc_client_list->items[ipc_client_list->length-1]);
77 ipc_client_disconnect(client);
78 } 73 }
79 list_free(ipc_client_list); 74 list_free(ipc_client_list);
80 75
81 if (ipc_sockaddr) { 76 free(ipc_sockaddr);
82 free(ipc_sockaddr);
83 }
84 77
85 wl_list_remove(&ipc_display_destroy.link); 78 wl_list_remove(&ipc_display_destroy.link);
86} 79}
@@ -452,8 +445,12 @@ void ipc_event_binding(struct sway_binding *binding) {
452 json_object_object_add(json_binding, "input_code", json_object_new_int(input_code)); 445 json_object_object_add(json_binding, "input_code", json_object_new_int(input_code));
453 json_object_object_add(json_binding, "symbols", symbols); 446 json_object_object_add(json_binding, "symbols", symbols);
454 json_object_object_add(json_binding, "symbol", symbol); 447 json_object_object_add(json_binding, "symbol", symbol);
455 json_object_object_add(json_binding, "input_type", binding->type == BINDING_MOUSE ? 448
456 json_object_new_string("mouse") : json_object_new_string("keyboard")); 449 bool mouse = binding->type == BINDING_MOUSECODE ||
450 binding->type == BINDING_MOUSESYM;
451 json_object_object_add(json_binding, "input_type", mouse
452 ? json_object_new_string("mouse")
453 : json_object_new_string("keyboard"));
457 454
458 json_object *json = json_object_new_object(); 455 json_object *json = json_object_new_object();
459 json_object_object_add(json, "change", json_object_new_string("run")); 456 json_object_object_add(json, "change", json_object_new_string("run"));
@@ -597,13 +594,18 @@ void ipc_client_handle_command(struct ipc_client *client) {
597 switch (client->current_command) { 594 switch (client->current_command) {
598 case IPC_COMMAND: 595 case IPC_COMMAND:
599 { 596 {
600 struct cmd_results *results = execute_command(buf, NULL, NULL); 597 list_t *res_list = execute_command(buf, NULL, NULL);
601 transaction_commit_dirty(); 598 transaction_commit_dirty();
602 char *json = cmd_results_to_json(results); 599 char *json = cmd_results_to_json(res_list);
603 int length = strlen(json); 600 int length = strlen(json);
604 client_valid = ipc_send_reply(client, json, (uint32_t)length); 601 client_valid = ipc_send_reply(client, json, (uint32_t)length);
605 free(json); 602 free(json);
606 free_cmd_results(results); 603 while (res_list->length) {
604 struct cmd_results *results = res_list->items[0];
605 free_cmd_results(results);
606 list_del(res_list, 0);
607 }
608 list_free(res_list);
607 goto exit_cleanup; 609 goto exit_cleanup;
608 } 610 }
609 611
@@ -619,8 +621,19 @@ void ipc_client_handle_command(struct ipc_client *client) {
619 json_object *outputs = json_object_new_array(); 621 json_object *outputs = json_object_new_array();
620 for (int i = 0; i < root->outputs->length; ++i) { 622 for (int i = 0; i < root->outputs->length; ++i) {
621 struct sway_output *output = root->outputs->items[i]; 623 struct sway_output *output = root->outputs->items[i];
622 json_object_array_add(outputs, 624 json_object *output_json = ipc_json_describe_node(&output->node);
623 ipc_json_describe_node(&output->node)); 625
626 // override the default focused indicator because it's set
627 // differently for the get_outputs reply
628 struct sway_seat *seat = input_manager_get_default_seat();
629 struct sway_workspace *focused_ws =
630 seat_get_focused_workspace(seat);
631 bool focused = focused_ws && output == focused_ws->output;
632 json_object_object_del(output_json, "focused");
633 json_object_object_add(output_json, "focused",
634 json_object_new_boolean(focused));
635
636 json_object_array_add(outputs, output_json);
624 } 637 }
625 struct sway_output *output; 638 struct sway_output *output;
626 wl_list_for_each(output, &root->all_outputs, link) { 639 wl_list_for_each(output, &root->all_outputs, link) {
@@ -651,8 +664,9 @@ void ipc_client_handle_command(struct ipc_client *client) {
651 { 664 {
652 // TODO: Check if they're permitted to use these events 665 // TODO: Check if they're permitted to use these events
653 struct json_object *request = json_tokener_parse(buf); 666 struct json_object *request = json_tokener_parse(buf);
654 if (request == NULL) { 667 if (request == NULL || !json_object_is_type(request, json_type_array)) {
655 client_valid = ipc_send_reply(client, "{\"success\": false}", 18); 668 const char msg[] = "{\"success\": false}";
669 client_valid = ipc_send_reply(client, msg, strlen(msg));
656 wlr_log(WLR_INFO, "Failed to parse subscribe request"); 670 wlr_log(WLR_INFO, "Failed to parse subscribe request");
657 goto exit_cleanup; 671 goto exit_cleanup;
658 } 672 }
@@ -679,8 +693,8 @@ void ipc_client_handle_command(struct ipc_client *client) {
679 client->subscribed_events |= event_mask(IPC_EVENT_TICK); 693 client->subscribed_events |= event_mask(IPC_EVENT_TICK);
680 is_tick = true; 694 is_tick = true;
681 } else { 695 } else {
682 client_valid = 696 const char msg[] = "{\"success\": false}";
683 ipc_send_reply(client, "{\"success\": false}", 18); 697 client_valid = ipc_send_reply(client, msg, strlen(msg));
684 json_object_put(request); 698 json_object_put(request);
685 wlr_log(WLR_INFO, "Unsupported event type in subscribe request"); 699 wlr_log(WLR_INFO, "Unsupported event type in subscribe request");
686 goto exit_cleanup; 700 goto exit_cleanup;
@@ -688,10 +702,12 @@ void ipc_client_handle_command(struct ipc_client *client) {
688 } 702 }
689 703
690 json_object_put(request); 704 json_object_put(request);
691 client_valid = ipc_send_reply(client, "{\"success\": true}", 17); 705 const char msg[] = "{\"success\": true}";
706 client_valid = ipc_send_reply(client, msg, strlen(msg));
692 if (is_tick) { 707 if (is_tick) {
693 client->current_command = IPC_EVENT_TICK; 708 client->current_command = IPC_EVENT_TICK;
694 ipc_send_reply(client, "{\"first\": true, \"payload\": \"\"}", 30); 709 const char tickmsg[] = "{\"first\": true, \"payload\": \"\"}";
710 ipc_send_reply(client, tickmsg, strlen(tickmsg));
695 } 711 }
696 goto exit_cleanup; 712 goto exit_cleanup;
697 } 713 }
@@ -820,6 +836,14 @@ void ipc_client_handle_command(struct ipc_client *client) {
820 goto exit_cleanup; 836 goto exit_cleanup;
821 } 837 }
822 838
839 case IPC_SYNC:
840 {
841 // It was decided sway will not support this, just return success:false
842 const char msg[] = "{\"success\": false}";
843 ipc_send_reply(client, msg, strlen(msg));
844 goto exit_cleanup;
845 }
846
823 default: 847 default:
824 wlr_log(WLR_INFO, "Unknown IPC command type %i", client->current_command); 848 wlr_log(WLR_INFO, "Unknown IPC command type %i", client->current_command);
825 goto exit_cleanup; 849 goto exit_cleanup;
diff --git a/sway/main.c b/sway/main.c
index 86374163..6e3f6b67 100644
--- a/sway/main.c
+++ b/sway/main.c
@@ -1,9 +1,9 @@
1#define _XOPEN_SOURCE 700 1#define _POSIX_C_SOURCE 200809L
2#define _POSIX_C_SOURCE 200112L
3#include <getopt.h> 2#include <getopt.h>
4#include <pango/pangocairo.h> 3#include <pango/pangocairo.h>
5#include <signal.h> 4#include <signal.h>
6#include <stdbool.h> 5#include <stdbool.h>
6#include <stdio.h>
7#include <stdlib.h> 7#include <stdlib.h>
8#include <stdio.h> 8#include <stdio.h>
9#include <string.h> 9#include <string.h>
@@ -23,7 +23,6 @@
23#include "sway/ipc-server.h" 23#include "sway/ipc-server.h"
24#include "ipc-client.h" 24#include "ipc-client.h"
25#include "log.h" 25#include "log.h"
26#include "readline.h"
27#include "stringop.h" 26#include "stringop.h"
28#include "util.h" 27#include "util.h"
29 28
@@ -48,31 +47,28 @@ void detect_raspi(void) {
48 if (!f) { 47 if (!f) {
49 return; 48 return;
50 } 49 }
51 char *line; 50 char *line = NULL;
52 while(!feof(f)) { 51 size_t line_size = 0;
53 if (!(line = read_line(f))) { 52 while (getline(&line, &line_size, f) != -1) {
54 break;
55 }
56 if (strstr(line, "Raspberry Pi")) { 53 if (strstr(line, "Raspberry Pi")) {
57 raspi = true; 54 raspi = true;
55 break;
58 } 56 }
59 free(line);
60 } 57 }
61 fclose(f); 58 fclose(f);
62 FILE *g = fopen("/proc/modules", "r"); 59 FILE *g = fopen("/proc/modules", "r");
63 if (!g) { 60 if (!g) {
61 free(line);
64 return; 62 return;
65 } 63 }
66 bool vc4 = false; 64 bool vc4 = false;
67 while (!feof(g)) { 65 while (getline(&line, &line_size, g) != -1) {
68 if (!(line = read_line(g))) {
69 break;
70 }
71 if (strstr(line, "vc4")) { 66 if (strstr(line, "vc4")) {
72 vc4 = true; 67 vc4 = true;
68 break;
73 } 69 }
74 free(line);
75 } 70 }
71 free(line);
76 fclose(g); 72 fclose(g);
77 if (!vc4 && raspi) { 73 if (!vc4 && raspi) {
78 fprintf(stderr, "\x1B[1;31mWarning: You have a " 74 fprintf(stderr, "\x1B[1;31mWarning: You have a "
@@ -87,13 +83,10 @@ void detect_proprietary(int allow_unsupported_gpu) {
87 if (!f) { 83 if (!f) {
88 return; 84 return;
89 } 85 }
90 while (!feof(f)) { 86 char *line = NULL;
91 char *line; 87 size_t line_size = 0;
92 if (!(line = read_line(f))) { 88 while (getline(&line, &line_size, f) != -1) {
93 break;
94 }
95 if (strstr(line, "nvidia")) { 89 if (strstr(line, "nvidia")) {
96 free(line);
97 if (allow_unsupported_gpu) { 90 if (allow_unsupported_gpu) {
98 wlr_log(WLR_ERROR, 91 wlr_log(WLR_ERROR,
99 "!!! Proprietary Nvidia drivers are in use !!!"); 92 "!!! Proprietary Nvidia drivers are in use !!!");
@@ -107,7 +100,6 @@ void detect_proprietary(int allow_unsupported_gpu) {
107 break; 100 break;
108 } 101 }
109 if (strstr(line, "fglrx")) { 102 if (strstr(line, "fglrx")) {
110 free(line);
111 if (allow_unsupported_gpu) { 103 if (allow_unsupported_gpu) {
112 wlr_log(WLR_ERROR, 104 wlr_log(WLR_ERROR,
113 "!!! Proprietary AMD drivers are in use !!!"); 105 "!!! Proprietary AMD drivers are in use !!!");
@@ -119,8 +111,8 @@ void detect_proprietary(int allow_unsupported_gpu) {
119 } 111 }
120 break; 112 break;
121 } 113 }
122 free(line);
123 } 114 }
115 free(line);
124 fclose(f); 116 fclose(f);
125} 117}
126 118
@@ -147,6 +139,19 @@ static void log_env(void) {
147 } 139 }
148} 140}
149 141
142static void log_file(FILE *f) {
143 char *line = NULL;
144 size_t line_size = 0;
145 ssize_t nread;
146 while ((nread = getline(&line, &line_size, f)) != -1) {
147 if (line[nread - 1] == '\n') {
148 line[nread - 1] = '\0';
149 }
150 wlr_log(WLR_INFO, "%s", line);
151 }
152 free(line);
153}
154
150static void log_distro(void) { 155static void log_distro(void) {
151 const char *paths[] = { 156 const char *paths[] = {
152 "/etc/lsb-release", 157 "/etc/lsb-release",
@@ -159,16 +164,7 @@ static void log_distro(void) {
159 FILE *f = fopen(paths[i], "r"); 164 FILE *f = fopen(paths[i], "r");
160 if (f) { 165 if (f) {
161 wlr_log(WLR_INFO, "Contents of %s:", paths[i]); 166 wlr_log(WLR_INFO, "Contents of %s:", paths[i]);
162 while (!feof(f)) { 167 log_file(f);
163 char *line;
164 if (!(line = read_line(f))) {
165 break;
166 }
167 if (*line) {
168 wlr_log(WLR_INFO, "%s", line);
169 }
170 free(line);
171 }
172 fclose(f); 168 fclose(f);
173 } 169 }
174 } 170 }
@@ -180,16 +176,7 @@ static void log_kernel(void) {
180 wlr_log(WLR_INFO, "Unable to determine kernel version"); 176 wlr_log(WLR_INFO, "Unable to determine kernel version");
181 return; 177 return;
182 } 178 }
183 while (!feof(f)) { 179 log_file(f);
184 char *line;
185 if (!(line = read_line(f))) {
186 break;
187 }
188 if (*line) {
189 wlr_log(WLR_INFO, "%s", line);
190 }
191 free(line);
192 }
193 pclose(f); 180 pclose(f);
194} 181}
195 182
@@ -393,11 +380,15 @@ int main(int argc, char **argv) {
393 wlr_log(WLR_DEBUG, "Running deferred commands"); 380 wlr_log(WLR_DEBUG, "Running deferred commands");
394 while (config->cmd_queue->length) { 381 while (config->cmd_queue->length) {
395 char *line = config->cmd_queue->items[0]; 382 char *line = config->cmd_queue->items[0];
396 struct cmd_results *res = execute_command(line, NULL, NULL); 383 list_t *res_list = execute_command(line, NULL, NULL);
397 if (res->status != CMD_SUCCESS) { 384 for (int i = 0; i < res_list->length; ++i) {
398 wlr_log(WLR_ERROR, "Error on line '%s': %s", line, res->error); 385 struct cmd_results *res = res_list->items[i];
386 if (res->status != CMD_SUCCESS) {
387 wlr_log(WLR_ERROR, "Error on line '%s': %s", line, res->error);
388 }
389 free_cmd_results(res);
399 } 390 }
400 free_cmd_results(res); 391 list_free(res_list);
401 free(line); 392 free(line);
402 list_del(config->cmd_queue, 0); 393 list_del(config->cmd_queue, 0);
403 } 394 }
diff --git a/sway/meson.build b/sway/meson.build
index 4524bac9..c2ed6298 100644
--- a/sway/meson.build
+++ b/sway/meson.build
@@ -23,6 +23,11 @@ sway_sources = files(
23 23
24 'input/input-manager.c', 24 'input/input-manager.c',
25 'input/seat.c', 25 'input/seat.c',
26 'input/seatop_down.c',
27 'input/seatop_move_floating.c',
28 'input/seatop_move_tiling.c',
29 'input/seatop_resize_floating.c',
30 'input/seatop_resize_tiling.c',
26 'input/cursor.c', 31 'input/cursor.c',
27 'input/keyboard.c', 32 'input/keyboard.c',
28 33
@@ -78,6 +83,7 @@ sway_sources = files(
78 'commands/seat/attach.c', 83 'commands/seat/attach.c',
79 'commands/seat/cursor.c', 84 'commands/seat/cursor.c',
80 'commands/seat/fallback.c', 85 'commands/seat/fallback.c',
86 'commands/seat/hide_cursor.c',
81 'commands/set.c', 87 'commands/set.c',
82 'commands/show_marks.c', 88 'commands/show_marks.c',
83 'commands/smart_borders.c', 89 'commands/smart_borders.c',
@@ -88,7 +94,11 @@ sway_sources = files(
88 'commands/swaynag_command.c', 94 'commands/swaynag_command.c',
89 'commands/swap.c', 95 'commands/swap.c',
90 'commands/tiling_drag.c', 96 'commands/tiling_drag.c',
97 'commands/tiling_drag_threshold.c',
98 'commands/title_align.c',
91 'commands/title_format.c', 99 'commands/title_format.c',
100 'commands/titlebar_border_thickness.c',
101 'commands/titlebar_padding.c',
92 'commands/unmark.c', 102 'commands/unmark.c',
93 'commands/urgent.c', 103 'commands/urgent.c',
94 'commands/workspace.c', 104 'commands/workspace.c',
@@ -96,12 +106,11 @@ sway_sources = files(
96 'commands/ws_auto_back_and_forth.c', 106 'commands/ws_auto_back_and_forth.c',
97 'commands/xwayland.c', 107 'commands/xwayland.c',
98 108
99 'commands/bar/activate_button.c', 109 'commands/bar/bind.c',
100 'commands/bar/binding_mode_indicator.c', 110 'commands/bar/binding_mode_indicator.c',
101 'commands/bar/bindsym.c',
102 'commands/bar/colors.c', 111 'commands/bar/colors.c',
103 'commands/bar/context_button.c',
104 'commands/bar/font.c', 112 'commands/bar/font.c',
113 'commands/bar/gaps.c',
105 'commands/bar/height.c', 114 'commands/bar/height.c',
106 'commands/bar/hidden_state.c', 115 'commands/bar/hidden_state.c',
107 'commands/bar/icon_theme.c', 116 'commands/bar/icon_theme.c',
@@ -111,11 +120,14 @@ sway_sources = files(
111 'commands/bar/output.c', 120 'commands/bar/output.c',
112 'commands/bar/pango_markup.c', 121 'commands/bar/pango_markup.c',
113 'commands/bar/position.c', 122 'commands/bar/position.c',
114 'commands/bar/secondary_button.c',
115 'commands/bar/separator_symbol.c', 123 'commands/bar/separator_symbol.c',
116 'commands/bar/status_command.c', 124 'commands/bar/status_command.c',
125 'commands/bar/status_edge_padding.c',
126 'commands/bar/status_padding.c',
117 'commands/bar/strip_workspace_numbers.c', 127 'commands/bar/strip_workspace_numbers.c',
128 'commands/bar/strip_workspace_name.c',
118 'commands/bar/swaybar_command.c', 129 'commands/bar/swaybar_command.c',
130 'commands/bar/tray_bindsym.c',
119 'commands/bar/tray_output.c', 131 'commands/bar/tray_output.c',
120 'commands/bar/tray_padding.c', 132 'commands/bar/tray_padding.c',
121 'commands/bar/workspace_buttons.c', 133 'commands/bar/workspace_buttons.c',
@@ -170,6 +182,7 @@ sway_deps = [
170 cairo, 182 cairo,
171 gdk_pixbuf, 183 gdk_pixbuf,
172 jsonc, 184 jsonc,
185 libevdev,
173 libinput, 186 libinput,
174 math, 187 math,
175 pango, 188 pango,
diff --git a/sway/security.c b/sway/security.c
index cc0d3f66..6a00229e 100644
--- a/sway/security.c
+++ b/sway/security.c
@@ -1,4 +1,4 @@
1#define _XOPEN_SOURCE 700 1#define _POSIX_C_SOURCE 200809L
2#include <stdlib.h> 2#include <stdlib.h>
3#include <string.h> 3#include <string.h>
4#include "sway/security.h" 4#include "sway/security.h"
diff --git a/sway/server.c b/sway/server.c
index 68142e87..0529cab1 100644
--- a/sway/server.c
+++ b/sway/server.c
@@ -7,25 +7,26 @@
7#include <wlr/backend/session.h> 7#include <wlr/backend/session.h>
8#include <wlr/render/wlr_renderer.h> 8#include <wlr/render/wlr_renderer.h>
9#include <wlr/types/wlr_compositor.h> 9#include <wlr/types/wlr_compositor.h>
10#include <wlr/types/wlr_data_control_v1.h>
10#include <wlr/types/wlr_export_dmabuf_v1.h> 11#include <wlr/types/wlr_export_dmabuf_v1.h>
11#include <wlr/types/wlr_gamma_control.h>
12#include <wlr/types/wlr_gamma_control_v1.h> 12#include <wlr/types/wlr_gamma_control_v1.h>
13#include <wlr/types/wlr_gamma_control.h>
14#include <wlr/types/wlr_gtk_primary_selection.h>
13#include <wlr/types/wlr_idle.h> 15#include <wlr/types/wlr_idle.h>
14#include <wlr/types/wlr_layer_shell_v1.h> 16#include <wlr/types/wlr_layer_shell_v1.h>
15#include <wlr/types/wlr_primary_selection.h>
16#include <wlr/types/wlr_screencopy_v1.h> 17#include <wlr/types/wlr_screencopy_v1.h>
17#include <wlr/types/wlr_server_decoration.h> 18#include <wlr/types/wlr_server_decoration.h>
18#include <wlr/types/wlr_xcursor_manager.h> 19#include <wlr/types/wlr_xcursor_manager.h>
19#include <wlr/types/wlr_xdg_decoration_v1.h> 20#include <wlr/types/wlr_xdg_decoration_v1.h>
20#include <wlr/types/wlr_xdg_output_v1.h> 21#include <wlr/types/wlr_xdg_output_v1.h>
21#include <wlr/util/log.h> 22#include <wlr/util/log.h>
23#include "config.h"
22#include "list.h" 24#include "list.h"
23#include "sway/config.h" 25#include "sway/config.h"
24#include "sway/desktop/idle_inhibit_v1.h" 26#include "sway/desktop/idle_inhibit_v1.h"
25#include "sway/input/input-manager.h" 27#include "sway/input/input-manager.h"
26#include "sway/server.h" 28#include "sway/server.h"
27#include "sway/tree/root.h" 29#include "sway/tree/root.h"
28#include "config.h"
29#if HAVE_XWAYLAND 30#if HAVE_XWAYLAND
30#include "sway/xwayland.h" 31#include "sway/xwayland.h"
31#endif 32#endif
@@ -57,7 +58,7 @@ bool server_init(struct sway_server *server) {
57 58
58 wlr_gamma_control_manager_create(server->wl_display); 59 wlr_gamma_control_manager_create(server->wl_display);
59 wlr_gamma_control_manager_v1_create(server->wl_display); 60 wlr_gamma_control_manager_v1_create(server->wl_display);
60 wlr_primary_selection_device_manager_create(server->wl_display); 61 wlr_gtk_primary_selection_device_manager_create(server->wl_display);
61 62
62 server->new_output.notify = handle_new_output; 63 server->new_output.notify = handle_new_output;
63 wl_signal_add(&server->backend->events.new_output, &server->new_output); 64 wl_signal_add(&server->backend->events.new_output, &server->new_output);
@@ -106,6 +107,7 @@ bool server_init(struct sway_server *server) {
106 107
107 wlr_export_dmabuf_manager_v1_create(server->wl_display); 108 wlr_export_dmabuf_manager_v1_create(server->wl_display);
108 wlr_screencopy_manager_v1_create(server->wl_display); 109 wlr_screencopy_manager_v1_create(server->wl_display);
110 wlr_data_control_manager_v1_create(server->wl_display);
109 111
110 server->socket = wl_display_add_socket_auto(server->wl_display); 112 server->socket = wl_display_add_socket_auto(server->wl_display);
111 if (!server->socket) { 113 if (!server->socket) {
diff --git a/sway/sway-bar.5.scd b/sway/sway-bar.5.scd
index 873741c0..3f6b4298 100644
--- a/sway/sway-bar.5.scd
+++ b/sway/sway-bar.5.scd
@@ -50,6 +50,10 @@ Sway allows configuring swaybar in the sway configuration file.
50*workspace\_buttons* yes|no 50*workspace\_buttons* yes|no
51 Enables or disables workspace buttons on the bar. Default is _yes_. 51 Enables or disables workspace buttons on the bar. Default is _yes_.
52 52
53*strip\_workspace\_name* yes|no
54 If set to _yes_, then workspace names will be omitted from the workspace
55 button and only the custom number will be shown. Default is _no_.
56
53*strip\_workspace\_numbers* yes|no 57*strip\_workspace\_numbers* yes|no
54 If set to _yes_, then workspace numbers will be omitted from the workspace 58 If set to _yes_, then workspace numbers will be omitted from the workspace
55 button and only the custom name will be shown. Default is _no_. 59 button and only the custom name will be shown. Default is _no_.
@@ -57,13 +61,28 @@ Sway allows configuring swaybar in the sway configuration file.
57*binding\_mode\_indicator* yes|no 61*binding\_mode\_indicator* yes|no
58 Enable or disable binding mode indicator. Default is _yes_. 62 Enable or disable binding mode indicator. Default is _yes_.
59 63
64*gaps* <all> | <horizontal> <vertical> | <top> <right> <bottom> <left>
65 Sets the gaps from the edge of the screen for the bar. Gaps can either be
66 set all at once, per direction, or per side. Note that only sides that
67 touch an edge of the screen can have gaps. For the side that does not
68 touch an edge of the screen, per-side outer gaps for workspaces may be of
69 use.
70
60*height* <height> 71*height* <height>
61 Sets the height of the bar. Default height will match the font size. 72 Sets the height of the bar. Default height (0) will match the font size.
73
74*bindcode* [--release] <event-code> <command>
75 Executes _command_ when the mouse button has been pressed (or if _released_
76 is given, when the button has been released). The buttons can be given as
77 an event code, which can be obtaining from `libinput debug-events`. To
78 disable the default behavior for a button, use the command _nop_.
62 79
63*bindsym* [--release] button<n> <command> 80*bindsym* [--release] button[1-9]|<event-name> <command>
64 Executes _command_ when mouse button _n_ has been pressed (or if _released_ 81 Executes _command_ when the mouse button has been pressed (or if _released_
65 is given, when mouse button _n_ has been released). To disable the default 82 is given, when the button has been released). The buttons can be given as a
66 behavior for a button, use the command _nop_. 83 x11 button number or an event name, which can be obtained from `libinput
84 debug-events`. To disable the default behavior for a button, use the
85 command _nop_.
67 86
68*mode* dock|hide|invisible 87*mode* dock|hide|invisible
69 Specifies the visibility of the bar. In _dock_ mode, it is permanently 88 Specifies the visibility of the bar. In _dock_ mode, it is permanently
@@ -81,6 +100,16 @@ Sway allows configuring swaybar in the sway configuration file.
81*modifier* <Modifier>|none 100*modifier* <Modifier>|none
82 Specifies the modifier key that shows a hidden bar. Default is _Mod4_. 101 Specifies the modifier key that shows a hidden bar. Default is _Mod4_.
83 102
103*status\_padding* <padding>
104 Sets the vertical padding that is used for the status line. The default is
105 _1_. If _padding_ is _0_, blocks will be able to take up the full height of
106 the bar. This value will be multiplied by the output scale.
107
108*status\_edge\_padding* <padding>
109 Sets the padding that is used when the status line is at the right edge of
110 the bar. This value will be multiplied by the output scale. The default is
111 _3_.
112
84## TRAY 113## TRAY
85 114
86Swaybar provides a system tray where third-party applications may place icons. 115Swaybar provides a system tray where third-party applications may place icons.
@@ -89,27 +118,20 @@ The following commands configure the tray.
89The _button_ argument in all cases is a platform-specific button code. On Linux 118The _button_ argument in all cases is a platform-specific button code. On Linux
90you can find a list of these at linux/input-event-codes.h. 119you can find a list of these at linux/input-event-codes.h.
91 120
92*activate\_button* <button> 121*tray\_bindsym* button<n> ContextMenu|Activate|SecondaryActivate|ScrollDown|ScrollLeft|ScrollRight|ScrollUp|nop
93 Sets the button to be used for the _activate_ (primary click) tray item 122 Binds mouse button _n_ (1 to 9) to the specified action. Use the command
94 event. The default is BTN\_LEFT (0x110). 123 _nop_ to disable the default action (Activate for button 1, ContextMenu for
95 124 button 2 and SecondaryActivate for button 3).
96*context\_button* <button>
97 Sets the button to be used for the _context menu_ (right click) tray item
98 event. The default is BTN\_RIGHT (0x111).
99
100*secondary\_button* <button>
101 Sets the button to be used for the _secondary_ (middle click) tray item
102 event. The default is BTN\_MIDDLE (0x112).
103
104*tray\_output* none|all|<output>
105 Sets the output that the tray will appear on or none. Unlike i3bar, swaybar
106 is able to show icons on any number of bars and outputs without races.
107 The default is _all_.
108 125
109*tray\_padding* <px> [px] 126*tray\_padding* <px> [px]
110 Sets the pixel padding of the system tray. This padding will surround the 127 Sets the pixel padding of the system tray. This padding will surround the
111 tray on all sides and between each item. The default value for _px_ is 2. 128 tray on all sides and between each item. The default value for _px_ is 2.
112 129
130*tray\_output* none|<output>
131 Restrict the tray to a certain output, can be specified multiple times. If
132 omitted, the tray will be displayed on all outputs. Unlike i3bar, swaybar
133 can show icons on any number of bars and outputs without races.
134
113*icon\_theme* <name> 135*icon\_theme* <name>
114 Sets the icon theme that sway will look for item icons in. This option has 136 Sets the icon theme that sway will look for item icons in. This option has
115 no default value, because sway will always default to the fallback theme, 137 no default value, because sway will always default to the fallback theme,
diff --git a/sway/sway-input.5.scd b/sway/sway-input.5.scd
index 45994644..c2673f2a 100644
--- a/sway/sway-input.5.scd
+++ b/sway/sway-input.5.scd
@@ -82,9 +82,12 @@ The following commands may only be used in the configuration file.
82*input* <identifier> dwt enabled|disabled 82*input* <identifier> dwt enabled|disabled
83 Enables or disables disable-while-typing for the specified input device. 83 Enables or disables disable-while-typing for the specified input device.
84 84
85*input* <identifier> events enabled|disabled|disabled\_on\_external\_mouse 85*input* <identifier> events enabled|disabled|disabled\_on\_external\_mouse|toggle
86 Enables or disables send\_events for specified input device. (Disabling 86 Enables or disables send\_events for specified input device. Disabling
87 send\_events disables the input device) 87 send\_events disables the input device. The _toggle_ option cannot be used
88 in the config. The order is enabled, disabled\_on\_external\_mouse,
89 disabled, (loop back to enabled). Any mode which is not supported by the
90 device will be skipped during the toggle.
88 91
89*input* <identifier> left\_handed enabled|disabled 92*input* <identifier> left\_handed enabled|disabled
90 Enables or disables left handed mode for specified input device. 93 Enables or disables left handed mode for specified input device.
@@ -105,10 +108,11 @@ The following commands may only be used in the configuration file.
105*input* <identifier> repeat\_rate <characters per second> 108*input* <identifier> repeat\_rate <characters per second>
106 Sets the frequency of key repeats once the repeat\_delay has passed. 109 Sets the frequency of key repeats once the repeat\_delay has passed.
107 110
108*input* <identifier> scroll\_button <button\_identifier> 111*input* <identifier> scroll\_button disable|button[1-3,8,9]|<event-code-or-name>
109 Sets button used for scroll\_method on\_button\_down. The button identifier 112 Sets the button used for scroll\_method on\_button\_down. The button can
110 can be obtained from `libinput debug-events`. 113 be given as an event name or code, which can be obtained from `libinput
111 If set to 0, it disables the scroll\_button on\_button\_down. 114 debug-events`, or as a x11 mouse button (button[1-3,8,9]). If set to
115 _disable_, it disables the scroll\_method on\_button\_down.
112 116
113*input* <identifier> scroll\_factor <floating point value> 117*input* <identifier> scroll\_factor <floating point value>
114 Changes the scroll factor for the specified input device. Scroll speed will 118 Changes the scroll factor for the specified input device. Scroll speed will
@@ -141,10 +145,29 @@ in their own "seat").
141 Attach an input device to this seat by its input identifier. A special 145 Attach an input device to this seat by its input identifier. A special
142 value of "\*" will attach all devices to the seat. 146 value of "\*" will attach all devices to the seat.
143 147
148*seat* <seat> cursor move|set <x> <y>
149 Move specified seat's cursor relative to current position or wrap to
150 absolute coordinates (with respect to the global coordinate space).
151 Specifying either value as 0 will not update that coordinate.
152
153*seat* <seat> cursor press|release button[1-9]|<event-name-or-code>
154 Simulate pressing (or releasing) the specified mouse button on the
155 specified seat. The button can either be provided as a button event name or
156 event code, which can be obtained from `libinput debug-events`, or as an x11
157 mouse button (button[1-9]). If using button[4-7], which map to axes, an axis
158 event will be simulated, however _press_ and _release_ will be ignored and
159 both will occur.
160
144*seat* <name> fallback true|false 161*seat* <name> fallback true|false
145 Set this seat as the fallback seat. A fallback seat will attach any device 162 Set this seat as the fallback seat. A fallback seat will attach any device
146 not explicitly attached to another seat (similar to a "default" seat). 163 not explicitly attached to another seat (similar to a "default" seat).
147 164
165*seat* <name> hide\_cursor <timeout>
166 Hides the cursor image after the specified _timeout_ (in milliseconds)
167 has elapsed with no activity on that cursor. A timeout of 0 (default)
168 disables hiding the cursor. The minimal timeout is 100 and any value less
169 than that (aside from 0), will be increased to 100.
170
148# SEE ALSO 171# SEE ALSO
149 172
150*sway*(5) *sway-output*(5) 173*sway*(5) *sway-output*(5)
diff --git a/sway/sway-output.5.scd b/sway/sway-output.5.scd
index 37b7108b..28524478 100644
--- a/sway/sway-output.5.scd
+++ b/sway/sway-output.5.scd
@@ -36,7 +36,22 @@ must be separated by one space. For example:
36 36
37*output* <name> position|pos <X> <Y> 37*output* <name> position|pos <X> <Y>
38 Places the specified output at the specific position in the global 38 Places the specified output at the specific position in the global
39 coordinate space. 39 coordinate space. If scaling is active, it has to be considered when
40 positioning. For example, if the scaling factor for the left output is 2,
41 the relative position for the right output has to be divided by 2. The
42 reference point is the top left corner so if you want the bottoms aligned
43 this has to be considered as well.
44
45 Example:
46
47 output HDMI1 scale 2
48
49 output HDMI1 pos 0 1020 res 3200x1800
50
51 output eDP1 pos 1600 0 res 1920x1080
52
53 Note that the left x-pos of eDP1 is 1600 = 3200/2 and the bottom y-pos is
54 1020 + (1800 / 2) = 1920 = 0 + 1920
40 55
41*output* <name> scale <factor> 56*output* <name> scale <factor>
42 Scales the specified output by the specified scale _factor_. An integer is 57 Scales the specified output by the specified scale _factor_. An integer is
@@ -45,7 +60,8 @@ must be separated by one space. For example:
45 represent the contents of your windows - they will be rendered at the next 60 represent the contents of your windows - they will be rendered at the next
46 highest integral scale factor and downscaled. You may be better served by 61 highest integral scale factor and downscaled. You may be better served by
47 setting an integral scale factor and adjusting the font size of your 62 setting an integral scale factor and adjusting the font size of your
48 applications to taste. 63 applications to taste. HiDPI isn't supported with Xwayland clients (windows
64 will blur).
49 65
50*output* <name> background|bg <file> <mode> [<fallback\_color>] 66*output* <name> background|bg <file> <mode> [<fallback\_color>]
51 Sets the wallpaper for the given output to the specified file, using the 67 Sets the wallpaper for the given output to the specified file, using the
@@ -65,7 +81,7 @@ must be separated by one space. For example:
65 to apply a rotation and flip, or "normal" to apply no transform. If a single 81 to apply a rotation and flip, or "normal" to apply no transform. If a single
66 output is chosen and a rotation direction is specified 82 output is chosen and a rotation direction is specified
67 (_clockwise_ or _anticlockwise_) then the transform is added or 83 (_clockwise_ or _anticlockwise_) then the transform is added or
68 subtracted from the current tranform. 84 subtracted from the current transform.
69 85
70*output* <name> disable|enable 86*output* <name> disable|enable
71 Enables or disables the specified output (all outputs are enabled by 87 Enables or disables the specified output (all outputs are enabled by
diff --git a/sway/sway.1.scd b/sway/sway.1.scd
index f66c4cdb..09c8ccfd 100644
--- a/sway/sway.1.scd
+++ b/sway/sway.1.scd
@@ -71,18 +71,14 @@ with *i3-msg*(1) or even with *i3*(1).
71 71
72The following environment variables have an effect on sway: 72The following environment variables have an effect on sway:
73 73
74_SWAY\_CURSOR\_THEME_
75 Specifies the name of the cursor theme to use.
76
77_SWAY\_CURSOR\_SIZE_
78 Specifies the size of the cursor to use.
79
80_SWAYSOCK_ 74_SWAYSOCK_
81 Specifies the path to the sway IPC socket. 75 Specifies the path to the sway IPC socket.
82 76
83_XKB\_DEFAULT\_RULES_, _XKB\_DEFAULT\_MODEL_, _XKB\_DEFAULT\_LAYOUT_, 77_XKB\_DEFAULT\_RULES_, _XKB\_DEFAULT\_MODEL_, _XKB\_DEFAULT\_LAYOUT_,
84_XKB\_DEFAULT\_VARIANT_, _XKB\_DEFAULT\_OPTIONS_ 78_XKB\_DEFAULT\_VARIANT_, _XKB\_DEFAULT\_OPTIONS_
85 Configures the xkb keyboard settings. See *xkeyboard-config*(7). 79 Configures the xkb keyboard settings. See *xkeyboard-config*(7). The
80 preferred way to configure the keyboard is via the configuration file, see
81 *sway-input*(5).
86 82
87# AUTHORS 83# AUTHORS
88 84
diff --git a/sway/sway.5.scd b/sway/sway.5.scd
index 95376ccc..06bc0dbf 100644
--- a/sway/sway.5.scd
+++ b/sway/sway.5.scd
@@ -134,8 +134,9 @@ They are expected to be used with *bindsym* or at runtime through *swaymsg*(1).
134*focus* mode\_toggle 134*focus* mode\_toggle
135 Moves focus between the floating and tiled layers. 135 Moves focus between the floating and tiled layers.
136 136
137*fullscreen* 137*fullscreen* [enable|disable|toggle]
138 Toggles fullscreen for the focused view. 138 Makes focused view fullscreen, non-fullscreen, or the opposite of what it
139 is now. If no argument is given, it does the same as _toggle_.
139 140
140*gaps* inner|outer|horizontal|vertical|top|right|bottom|left all|current 141*gaps* inner|outer|horizontal|vertical|top|right|bottom|left all|current
141set|plus|minus <amount> 142set|plus|minus <amount>
@@ -280,14 +281,23 @@ runtime.
280 281
281 for\_window <criteria> move container to output <output> 282 for\_window <criteria> move container to output <output>
282 283
283*bindsym* [--release|--locked] [--input-device=<device>] <key combo> <command> 284*bindsym* [--release|--locked] [--input-device=<device>] [--no-warn] <key combo> <command>
284 Binds _key combo_ to execute the sway command _command_ when pressed. You 285 Binds _key combo_ to execute the sway command _command_ when pressed. You
285 may use XKB key names here (*xev*(1) is a good tool for discovering these). 286 may use XKB key names here (*xev*(1) is a good tool for discovering these).
286 With the flag _--release_, the command is executed when the key combo is 287 With the flag _--release_, the command is executed when the key combo is
287 released. Unless the flag _--locked_ is set, the command will not be run 288 released. Unless the flag _--locked_ is set, the command will not be run
288 when a screen locking program is active. If _input-device_ is given, the 289 when a screen locking program is active. If _input-device_ is given, the
289 binding will only be executed for that input device and will be executed 290 binding will only be executed for that input device and will be executed
290 instead of any binding that is generic to all devices. 291 instead of any binding that is generic to all devices. By default, if you
292 overwrite a binding, swaynag will give you a warning. To silence this, use
293 the _--no-warn_ flag.
294
295 Mouse buttons can either be specified in the form _button[1-9]_ or by using
296 the name of the event code (ex _BTN\_LEFT_ or _BTN\_RIGHT_). For the former
297 option, the buttons will be mapped to their values in X11 (1=left, 2=middle,
298 3=right, 4=scroll up, 5=scroll down, 6=scroll left, 7=scroll right, 8=back,
299 9=forward). For the latter option, you can find the event names using
300 _libinput debug-events_.
291 301
292 Example: 302 Example:
293``` 303```
@@ -295,8 +305,8 @@ runtime.
295 bindsym Mod1+Shift+f exec firefox 305 bindsym Mod1+Shift+f exec firefox
296``` 306```
297 307
298 *bindcode* [--release|--locked] [--input-device=<device>] <code> <command> 308 *bindcode* [--release|--locked] [--input-device=<device>] [--no-warn] <code> <command>
299 is also available for binding with key codes instead of key names. 309 is also available for binding with key/button codes instead of key/button names.
300 310
301*client.<class>* <border> <background> <text> <indicator> <child\_border> 311*client.<class>* <border> <background> <text> <indicator> <child\_border>
302 Configures the color of window borders and title bars. All 5 colors are 312 Configures the color of window borders and title bars. All 5 colors are
@@ -321,7 +331,8 @@ runtime.
321 A view that does not have focus. 331 A view that does not have focus.
322 332
323 *client.urgent* 333 *client.urgent*
324 A view with an urgency hint. *Note*: This is not currently implemented. 334 A view with an urgency hint. *Note*: Native Wayland windows do not
335 support urgency. Urgency only works for Xwayland windows.
325 336
326 The meaning of each color is: 337 The meaning of each color is:
327 338
@@ -440,6 +451,16 @@ The default colors are:
440*font* <font> 451*font* <font>
441 Sets font for use in title bars in Pango format. 452 Sets font for use in title bars in Pango format.
442 453
454*titlebar\_border\_thickness* <thickness>
455 Thickness of the titlebar border in pixels
456
457*titlebar\_padding* <horizontal> [<vertical>]
458 Padding of the text in the titlebar. _horizontal_ value affects horizontal
459 padding of the text while _vertical_ value affects vertical padding (space
460 above and below text). Padding includes titlebar borders so their value
461 should be greater than titlebar\_border\_thickness. If _vertical_ value is
462 not specified it is set to the _horizontal_ value.
463
443*for\_window* <criteria> <command> 464*for\_window* <criteria> <command>
444 Whenever a window that matches _criteria_ appears, run list of commands. 465 Whenever a window that matches _criteria_ appears, run list of commands.
445 See *CRITERIA* for more details. 466 See *CRITERIA* for more details.
@@ -474,15 +495,6 @@ The default colors are:
474*seat* <seat> <seat-subcommands...> 495*seat* <seat> <seat-subcommands...>
475 For details on seat subcommands, see *sway-input*(5). 496 For details on seat subcommands, see *sway-input*(5).
476 497
477*seat* <seat> cursor move|set <x> <y>
478 Move specified seat's cursor relative to current position or wrap to
479 absolute coordinates (with respect to the global coordinate space).
480 Specifying either value as 0 will not update that coordinate.
481
482*seat* <seat> cursor press|release left|right|1|2|3...
483 Simulate pressing (or releasing) the specified mouse button on the
484 specified seat.
485
486*kill* 498*kill*
487 Kills (closes) the currently focused container and all of its children. 499 Kills (closes) the currently focused container and all of its children.
488 500
@@ -549,6 +561,26 @@ The default colors are:
549 Set the opacity of the window between 0 (completely transparent) and 1 561 Set the opacity of the window between 0 (completely transparent) and 1
550 (completely opaque). 562 (completely opaque).
551 563
564*tiling\_drag* enable|disable|toggle
565 Sets whether or not tiling containers can be dragged with the mouse. If
566 enabled (default), the _floating\_mod_ can be used to drag tiling, as well
567 as floating, containers. Using the left mouse button on title bars without
568 the _floating\_mod_ will also allow the container to be dragged. _toggle_
569 should not be used in the config file.
570
571*tiling\_drag\_threshold* <threshold>
572 Sets the threshold that must be exceeded for a container to be dragged by
573 its titlebar. This has no effect if _floating\_mod_ is used or if
574 _tiling\_drag_ is set to _disable_. Once the threshold has been exceeded
575 once, the drag starts and the cursor can come back inside the threshold
576 without stopping the drag. _threshold_ is multiplied by the scale of the
577 output that the cursor on. The default is 9.
578
579*title\_align* left|center|right
580 Sets the title alignment. If _right_ is selected and _show\_marks_ is set
581 to _yes_, the marks will be shown on the _left_ side instead of the
582 _right_ side.
583
552*unmark* [<identifier>] 584*unmark* [<identifier>]
553 *unmark* will remove _identifier_ from the list of current marks on a 585 *unmark* will remove _identifier_ from the list of current marks on a
554 window. If _identifier_ is omitted, all marks are removed. 586 window. If _identifier_ is omitted, all marks are removed.
diff --git a/sway/tree/container.c b/sway/tree/container.c
index cf6f5b54..d9c721f5 100644
--- a/sway/tree/container.c
+++ b/sway/tree/container.c
@@ -21,6 +21,7 @@
21#include "sway/tree/arrange.h" 21#include "sway/tree/arrange.h"
22#include "sway/tree/view.h" 22#include "sway/tree/view.h"
23#include "sway/tree/workspace.h" 23#include "sway/tree/workspace.h"
24#include "list.h"
24#include "log.h" 25#include "log.h"
25#include "stringop.h" 26#include "stringop.h"
26 27
@@ -67,8 +68,7 @@ void container_destroy(struct sway_container *con) {
67 list_free(con->current.children); 68 list_free(con->current.children);
68 list_free(con->outputs); 69 list_free(con->outputs);
69 70
70 list_foreach(con->marks, free); 71 list_free_items_and_destroy(con->marks);
71 list_free(con->marks);
72 wlr_texture_destroy(con->marks_focused); 72 wlr_texture_destroy(con->marks_focused);
73 wlr_texture_destroy(con->marks_focused_inactive); 73 wlr_texture_destroy(con->marks_focused_inactive);
74 wlr_texture_destroy(con->marks_unfocused); 74 wlr_texture_destroy(con->marks_unfocused);
@@ -453,19 +453,26 @@ static void update_title_texture(struct sway_container *con,
453 int width = 0; 453 int width = 0;
454 int height = con->title_height * scale; 454 int height = con->title_height * scale;
455 455
456 cairo_t *c = cairo_create(NULL); 456 // We must use a non-nil cairo_t for cairo_set_font_options to work.
457 // Therefore, we cannot use cairo_create(NULL).
458 cairo_surface_t *dummy_surface = cairo_image_surface_create(
459 CAIRO_FORMAT_ARGB32, 0, 0);
460 cairo_t *c = cairo_create(dummy_surface);
461 cairo_set_antialias(c, CAIRO_ANTIALIAS_BEST);
462 cairo_font_options_t *fo = cairo_font_options_create();
463 cairo_font_options_set_hint_style(fo, CAIRO_HINT_STYLE_FULL);
464 cairo_font_options_set_antialias(fo, CAIRO_ANTIALIAS_SUBPIXEL);
465 cairo_font_options_set_subpixel_order(fo, to_cairo_subpixel_order(output->wlr_output->subpixel));
466 cairo_set_font_options(c, fo);
457 get_text_size(c, config->font, &width, NULL, NULL, scale, 467 get_text_size(c, config->font, &width, NULL, NULL, scale,
458 config->pango_markup, "%s", con->formatted_title); 468 config->pango_markup, "%s", con->formatted_title);
469 cairo_surface_destroy(dummy_surface);
459 cairo_destroy(c); 470 cairo_destroy(c);
460 471
461 cairo_surface_t *surface = cairo_image_surface_create( 472 cairo_surface_t *surface = cairo_image_surface_create(
462 CAIRO_FORMAT_ARGB32, width, height); 473 CAIRO_FORMAT_ARGB32, width, height);
463 cairo_t *cairo = cairo_create(surface); 474 cairo_t *cairo = cairo_create(surface);
464 cairo_set_antialias(cairo, CAIRO_ANTIALIAS_BEST); 475 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); 476 cairo_set_font_options(cairo, fo);
470 cairo_font_options_destroy(fo); 477 cairo_font_options_destroy(fo);
471 cairo_set_source_rgba(cairo, class->background[0], class->background[1], 478 cairo_set_source_rgba(cairo, class->background[0], class->background[1],
@@ -594,7 +601,7 @@ void container_update_representation(struct sway_container *con) {
594} 601}
595 602
596size_t container_titlebar_height(void) { 603size_t container_titlebar_height(void) {
597 return config->font_height + TITLEBAR_V_PADDING * 2; 604 return config->font_height + config->titlebar_v_padding * 2;
598} 605}
599 606
600void container_init_floating(struct sway_container *con) { 607void container_init_floating(struct sway_container *con) {
@@ -857,15 +864,7 @@ bool container_has_urgent_child(struct sway_container *container) {
857void container_end_mouse_operation(struct sway_container *container) { 864void container_end_mouse_operation(struct sway_container *container) {
858 struct sway_seat *seat; 865 struct sway_seat *seat;
859 wl_list_for_each(seat, &server.input->seats, link) { 866 wl_list_for_each(seat, &server.input->seats, link) {
860 if (seat->op_container == container) { 867 seatop_unref(seat, container);
861 seat->op_target_node = NULL; // ensure tiling move doesn't apply
862 seat_end_mouse_operation(seat);
863 }
864 // If the user is doing a tiling drag over this container,
865 // keep the operation active but unset the target container.
866 if (seat->op_target_node == &container->node) {
867 seat->op_target_node = NULL;
868 }
869 } 868 }
870} 869}
871 870
@@ -979,7 +978,7 @@ void container_discover_outputs(struct sway_container *con) {
979 output_get_box(output, &output_box); 978 output_get_box(output, &output_box);
980 struct wlr_box intersection; 979 struct wlr_box intersection;
981 bool intersects = 980 bool intersects =
982 wlr_box_intersection(&con_box, &output_box, &intersection); 981 wlr_box_intersection(&intersection, &con_box, &output_box);
983 int index = list_find(con->outputs, output); 982 int index = list_find(con->outputs, output);
984 983
985 if (intersects && index == -1) { 984 if (intersects && index == -1) {
@@ -1267,7 +1266,9 @@ bool container_find_and_unmark(char *mark) {
1267} 1266}
1268 1267
1269void container_clear_marks(struct sway_container *con) { 1268void container_clear_marks(struct sway_container *con) {
1270 list_foreach(con->marks, free); 1269 for (int i = 0; i < con->marks->length; ++i) {
1270 free(con->marks->items[i]);
1271 }
1271 con->marks->length = 0; 1272 con->marks->length = 0;
1272 ipc_event_window(con, "mark"); 1273 ipc_event_window(con, "mark");
1273} 1274}
@@ -1375,3 +1376,16 @@ void container_update_marks_textures(struct sway_container *con) {
1375 &config->border_colors.urgent); 1376 &config->border_colors.urgent);
1376 container_damage_whole(con); 1377 container_damage_whole(con);
1377} 1378}
1379
1380void container_raise_floating(struct sway_container *con) {
1381 // Bring container to front by putting it at the end of the floating list.
1382 struct sway_container *floater = con;
1383 while (floater->parent) {
1384 floater = floater->parent;
1385 }
1386 if (container_is_floating(floater)) {
1387 list_move_to_end(floater->workspace->floating, floater);
1388 node_set_dirty(&floater->workspace->node);
1389 }
1390}
1391
diff --git a/sway/tree/output.c b/sway/tree/output.c
index 3c4614a8..f24be010 100644
--- a/sway/tree/output.c
+++ b/sway/tree/output.c
@@ -58,6 +58,7 @@ struct sway_output *output_create(struct wlr_output *wlr_output) {
58 wlr_output->data = output; 58 wlr_output->data = output;
59 59
60 wl_signal_add(&wlr_output->events.destroy, &output->destroy); 60 wl_signal_add(&wlr_output->events.destroy, &output->destroy);
61 wl_signal_init(&output->events.destroy);
61 62
62 wl_list_insert(&root->all_outputs, &output->link); 63 wl_list_insert(&root->all_outputs, &output->link);
63 64
@@ -76,7 +77,6 @@ void output_enable(struct sway_output *output, struct output_config *oc) {
76 for (size_t i = 0; i < len; ++i) { 77 for (size_t i = 0; i < len; ++i) {
77 wl_list_init(&output->layers[i]); 78 wl_list_init(&output->layers[i]);
78 } 79 }
79 wl_signal_init(&output->events.destroy);
80 80
81 output->enabled = true; 81 output->enabled = true;
82 list_add(root->outputs, output); 82 list_add(root->outputs, output);
@@ -88,11 +88,12 @@ void output_enable(struct sway_output *output, struct output_config *oc) {
88 88
89 restore_workspaces(output); 89 restore_workspaces(output);
90 90
91 struct sway_workspace *ws = NULL;
91 if (!output->workspaces->length) { 92 if (!output->workspaces->length) {
92 // Create workspace 93 // Create workspace
93 char *ws_name = workspace_next_name(wlr_output->name); 94 char *ws_name = workspace_next_name(wlr_output->name);
94 wlr_log(WLR_DEBUG, "Creating default workspace %s", ws_name); 95 wlr_log(WLR_DEBUG, "Creating default workspace %s", ws_name);
95 struct sway_workspace *ws = workspace_create(output, ws_name); 96 ws = workspace_create(output, ws_name);
96 // Set each seat's focus if not already set 97 // Set each seat's focus if not already set
97 struct sway_seat *seat = NULL; 98 struct sway_seat *seat = NULL;
98 wl_list_for_each(seat, &server.input->seats, link) { 99 wl_list_for_each(seat, &server.input->seats, link) {
@@ -104,9 +105,15 @@ void output_enable(struct sway_output *output, struct output_config *oc) {
104 ipc_event_workspace(NULL, ws, "init"); 105 ipc_event_workspace(NULL, ws, "init");
105 } 106 }
106 107
107
108 apply_output_config(oc, output); 108 apply_output_config(oc, output);
109 109
110 if (ws && config->default_orientation == L_NONE) {
111 // Since the output transformation and resolution could have changed
112 // due to applying the output config, the previously set layout for the
113 // created workspace may not be correct for `default_orientation auto`
114 ws->layout = output_get_default_layout(output);
115 }
116
110 input_manager_configure_xcursor(); 117 input_manager_configure_xcursor();
111 118
112 wl_signal_add(&wlr_output->events.mode, &output->mode); 119 wl_signal_add(&wlr_output->events.mode, &output->mode);
@@ -218,6 +225,11 @@ void output_disable(struct sway_output *output) {
218 225
219 root_for_each_container(untrack_output, output); 226 root_for_each_container(untrack_output, output);
220 227
228 if (output->bg_pid) {
229 terminate_swaybg(output->bg_pid);
230 output->bg_pid = 0;
231 }
232
221 int index = list_find(root->outputs, output); 233 int index = list_find(root->outputs, output);
222 list_del(root->outputs, index); 234 list_del(root->outputs, index);
223 235
diff --git a/sway/tree/root.c b/sway/tree/root.c
index 544d666a..e1624863 100644
--- a/sway/tree/root.c
+++ b/sway/tree/root.c
@@ -5,6 +5,7 @@
5#include <wlr/types/wlr_output_layout.h> 5#include <wlr/types/wlr_output_layout.h>
6#include "sway/desktop/transaction.h" 6#include "sway/desktop/transaction.h"
7#include "sway/input/seat.h" 7#include "sway/input/seat.h"
8#include "sway/ipc-server.h"
8#include "sway/output.h" 9#include "sway/output.h"
9#include "sway/tree/arrange.h" 10#include "sway/tree/arrange.h"
10#include "sway/tree/container.h" 11#include "sway/tree/container.h"
@@ -68,13 +69,18 @@ void root_scratchpad_add_container(struct sway_container *con) {
68 list_add(root->scratchpad, con); 69 list_add(root->scratchpad, con);
69 70
70 struct sway_seat *seat = input_manager_current_seat(); 71 struct sway_seat *seat = input_manager_current_seat();
72 struct sway_node *new_focus = NULL;
71 if (parent) { 73 if (parent) {
72 arrange_container(parent); 74 arrange_container(parent);
73 seat_set_focus(seat, seat_get_focus_inactive(seat, &parent->node)); 75 new_focus = seat_get_focus_inactive(seat, &parent->node);
74 } else { 76 }
77 if (!new_focus) {
75 arrange_workspace(workspace); 78 arrange_workspace(workspace);
76 seat_set_focus(seat, seat_get_focus_inactive(seat, &workspace->node)); 79 new_focus = seat_get_focus_inactive(seat, &workspace->node);
77 } 80 }
81 seat_set_focus(seat, new_focus);
82
83 ipc_event_window(con, "move");
78} 84}
79 85
80void root_scratchpad_remove_container(struct sway_container *con) { 86void root_scratchpad_remove_container(struct sway_container *con) {
@@ -85,45 +91,51 @@ void root_scratchpad_remove_container(struct sway_container *con) {
85 int index = list_find(root->scratchpad, con); 91 int index = list_find(root->scratchpad, con);
86 if (index != -1) { 92 if (index != -1) {
87 list_del(root->scratchpad, index); 93 list_del(root->scratchpad, index);
94 ipc_event_window(con, "move");
88 } 95 }
89} 96}
90 97
91void root_scratchpad_show(struct sway_container *con) { 98void root_scratchpad_show(struct sway_container *con) {
92 struct sway_seat *seat = input_manager_current_seat(); 99 struct sway_seat *seat = input_manager_current_seat();
93 struct sway_workspace *ws = seat_get_focused_workspace(seat); 100 struct sway_workspace *new_ws = seat_get_focused_workspace(seat);
101 struct sway_workspace *old_ws = con->workspace;
94 102
95 // If the current con or any of its parents are in fullscreen mode, we 103 // If the current con or any of its parents are in fullscreen mode, we
96 // first need to disable it before showing the scratchpad con. 104 // first need to disable it before showing the scratchpad con.
97 if (ws->fullscreen) { 105 if (new_ws->fullscreen) {
98 container_set_fullscreen(ws->fullscreen, false); 106 container_set_fullscreen(new_ws->fullscreen, false);
99 } 107 }
100 108
101 // Show the container 109 // Show the container
102 if (con->workspace) { 110 if (old_ws) {
103 container_detach(con); 111 container_detach(con);
104 } 112 }
105 workspace_add_floating(ws, con); 113 workspace_add_floating(new_ws, con);
106 114
107 // Make sure the container's center point overlaps this workspace 115 // Make sure the container's center point overlaps this workspace
108 double center_lx = con->x + con->width / 2; 116 double center_lx = con->x + con->width / 2;
109 double center_ly = con->y + con->height / 2; 117 double center_ly = con->y + con->height / 2;
110 118
111 struct wlr_box workspace_box; 119 struct wlr_box workspace_box;
112 workspace_get_box(ws, &workspace_box); 120 workspace_get_box(new_ws, &workspace_box);
113 if (!wlr_box_contains_point(&workspace_box, center_lx, center_ly)) { 121 if (!wlr_box_contains_point(&workspace_box, center_lx, center_ly)) {
114 // Maybe resize it 122 // Maybe resize it
115 if (con->width > ws->width || con->height > ws->height) { 123 if (con->width > new_ws->width || con->height > new_ws->height) {
116 container_init_floating(con); 124 container_init_floating(con);
117 } 125 }
118 126
119 // Center it 127 // Center it
120 double new_lx = ws->x + (ws->width - con->width) / 2; 128 double new_lx = new_ws->x + (new_ws->width - con->width) / 2;
121 double new_ly = ws->y + (ws->height - con->height) / 2; 129 double new_ly = new_ws->y + (new_ws->height - con->height) / 2;
122 container_floating_move_to(con, new_lx, new_ly); 130 container_floating_move_to(con, new_lx, new_ly);
123 } 131 }
124 132
125 arrange_workspace(ws); 133 arrange_workspace(new_ws);
126 seat_set_focus(seat, seat_get_focus_inactive(seat, &con->node)); 134 seat_set_focus(seat, seat_get_focus_inactive(seat, &con->node));
135
136 if (new_ws != old_ws) {
137 ipc_event_window(con, "move");
138 }
127} 139}
128 140
129void root_scratchpad_hide(struct sway_container *con) { 141void root_scratchpad_hide(struct sway_container *con) {
@@ -133,10 +145,12 @@ void root_scratchpad_hide(struct sway_container *con) {
133 145
134 container_detach(con); 146 container_detach(con);
135 arrange_workspace(ws); 147 arrange_workspace(ws);
136 if (&con->node == focus) { 148 if (&con->node == focus || node_has_ancestor(focus, &con->node)) {
137 seat_set_focus(seat, seat_get_focus_inactive(seat, &ws->node)); 149 seat_set_focus(seat, seat_get_focus_inactive(seat, &ws->node));
138 } 150 }
139 list_move_to_end(root->scratchpad, con); 151 list_move_to_end(root->scratchpad, con);
152
153 ipc_event_window(con, "move");
140} 154}
141 155
142struct pid_workspace { 156struct pid_workspace {
diff --git a/sway/tree/view.c b/sway/tree/view.c
index d7110619..5371ee20 100644
--- a/sway/tree/view.c
+++ b/sway/tree/view.c
@@ -437,9 +437,14 @@ void view_execute_criteria(struct sway_view *view) {
437 wlr_log(WLR_DEBUG, "for_window '%s' matches view %p, cmd: '%s'", 437 wlr_log(WLR_DEBUG, "for_window '%s' matches view %p, cmd: '%s'",
438 criteria->raw, view, criteria->cmdlist); 438 criteria->raw, view, criteria->cmdlist);
439 list_add(view->executed_criteria, criteria); 439 list_add(view->executed_criteria, criteria);
440 struct cmd_results *res = execute_command( 440 list_t *res_list = execute_command(
441 criteria->cmdlist, NULL, view->container); 441 criteria->cmdlist, NULL, view->container);
442 free_cmd_results(res); 442 while (res_list->length) {
443 struct cmd_results *res = res_list->items[0];
444 free_cmd_results(res);
445 list_del(res_list, 0);
446 }
447 list_free(res_list);
443 } 448 }
444 list_free(criterias); 449 list_free(criterias);
445} 450}
@@ -454,7 +459,7 @@ static struct sway_workspace *select_workspace(struct sway_view *view) {
454 for (int i = 0; i < criterias->length; ++i) { 459 for (int i = 0; i < criterias->length; ++i) {
455 struct criteria *criteria = criterias->items[i]; 460 struct criteria *criteria = criterias->items[i];
456 if (criteria->type == CT_ASSIGN_OUTPUT) { 461 if (criteria->type == CT_ASSIGN_OUTPUT) {
457 struct sway_output *output = output_by_name(criteria->target); 462 struct sway_output *output = output_by_name_or_id(criteria->target);
458 if (output) { 463 if (output) {
459 ws = output_get_active_workspace(output); 464 ws = output_get_active_workspace(output);
460 break; 465 break;
@@ -600,7 +605,6 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface,
600 605
601 view_update_title(view, false); 606 view_update_title(view, false);
602 container_update_representation(view->container); 607 container_update_representation(view->container);
603 view_execute_criteria(view);
604 608
605 if (decoration) { 609 if (decoration) {
606 view_update_csd_from_client(view, decoration); 610 view_update_csd_from_client(view, decoration);
@@ -617,6 +621,8 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface,
617 } 621 }
618 } 622 }
619 623
624 view_execute_criteria(view);
625
620 if (should_focus(view)) { 626 if (should_focus(view)) {
621 input_manager_set_focus(&view->container->node); 627 input_manager_set_focus(&view->container->node);
622 } 628 }
@@ -648,14 +654,8 @@ void view_unmap(struct sway_view *view) {
648 654
649 struct sway_seat *seat; 655 struct sway_seat *seat;
650 wl_list_for_each(seat, &server.input->seats, link) { 656 wl_list_for_each(seat, &server.input->seats, link) {
651 if (config->mouse_warping == WARP_CONTAINER) { 657 seat->cursor->image_surface = NULL;
652 struct sway_node *node = seat_get_focus(seat); 658 seat_consider_warp_to_focus(seat);
653 if (node && node->type == N_CONTAINER) {
654 cursor_warp_to_container(seat->cursor, node->sway_container);
655 } else if (node && node->type == N_WORKSPACE) {
656 cursor_warp_to_workspace(seat->cursor, node->sway_workspace);
657 }
658 }
659 } 659 }
660 660
661 transaction_commit_dirty(); 661 transaction_commit_dirty();
@@ -674,6 +674,8 @@ void view_update_size(struct sway_view *view, int width, int height) {
674 container_set_geometry_from_content(view->container); 674 container_set_geometry_from_content(view->container);
675} 675}
676 676
677static const struct sway_view_child_impl subsurface_impl;
678
677static void subsurface_get_root_coords(struct sway_view_child *child, 679static void subsurface_get_root_coords(struct sway_view_child *child,
678 int *root_sx, int *root_sy) { 680 int *root_sx, int *root_sy) {
679 struct wlr_surface *surface = child->surface; 681 struct wlr_surface *surface = child->surface;
@@ -689,18 +691,47 @@ static void subsurface_get_root_coords(struct sway_view_child *child,
689 } 691 }
690} 692}
691 693
694static void subsurface_destroy(struct sway_view_child *child) {
695 if (!sway_assert(child->impl == &subsurface_impl,
696 "Expected a subsurface")) {
697 return;
698 }
699 struct sway_subsurface *subsurface = (struct sway_subsurface *)child;
700 wl_list_remove(&subsurface->destroy.link);
701 free(subsurface);
702}
703
692static const struct sway_view_child_impl subsurface_impl = { 704static const struct sway_view_child_impl subsurface_impl = {
693 .get_root_coords = subsurface_get_root_coords, 705 .get_root_coords = subsurface_get_root_coords,
706 .destroy = subsurface_destroy,
694}; 707};
695 708
709static void subsurface_handle_destroy(struct wl_listener *listener,
710 void *data) {
711 struct sway_subsurface *subsurface =
712 wl_container_of(listener, subsurface, destroy);
713 struct sway_view_child *child = &subsurface->child;
714 view_child_destroy(child);
715}
716
717static void view_child_damage(struct sway_view_child *child, bool whole);
718
696static void view_subsurface_create(struct sway_view *view, 719static void view_subsurface_create(struct sway_view *view,
697 struct wlr_subsurface *subsurface) { 720 struct wlr_subsurface *wlr_subsurface) {
698 struct sway_view_child *child = calloc(1, sizeof(struct sway_view_child)); 721 struct sway_subsurface *subsurface =
699 if (child == NULL) { 722 calloc(1, sizeof(struct sway_subsurface));
723 if (subsurface == NULL) {
700 wlr_log(WLR_ERROR, "Allocation failed"); 724 wlr_log(WLR_ERROR, "Allocation failed");
701 return; 725 return;
702 } 726 }
703 view_child_init(child, &subsurface_impl, view, subsurface->surface); 727 view_child_init(&subsurface->child, &subsurface_impl, view,
728 wlr_subsurface->surface);
729
730 wl_signal_add(&wlr_subsurface->events.destroy, &subsurface->destroy);
731 subsurface->destroy.notify = subsurface_handle_destroy;
732
733 subsurface->child.mapped = true;
734 view_child_damage(&subsurface->child, true);
704} 735}
705 736
706static void view_child_damage(struct sway_view_child *child, bool whole) { 737static void view_child_damage(struct sway_view_child *child, bool whole) {
@@ -745,6 +776,7 @@ static void view_child_handle_surface_map(struct wl_listener *listener,
745 void *data) { 776 void *data) {
746 struct sway_view_child *child = 777 struct sway_view_child *child =
747 wl_container_of(listener, child, surface_map); 778 wl_container_of(listener, child, surface_map);
779 child->mapped = true;
748 view_child_damage(child, true); 780 view_child_damage(child, true);
749} 781}
750 782
@@ -753,6 +785,7 @@ static void view_child_handle_surface_unmap(struct wl_listener *listener,
753 struct sway_view_child *child = 785 struct sway_view_child *child =
754 wl_container_of(listener, child, surface_unmap); 786 wl_container_of(listener, child, surface_unmap);
755 view_child_damage(child, true); 787 view_child_damage(child, true);
788 child->mapped = false;
756} 789}
757 790
758void view_child_init(struct sway_view_child *child, 791void view_child_init(struct sway_view_child *child,
@@ -771,6 +804,7 @@ void view_child_init(struct sway_view_child *child,
771 wl_signal_add(&surface->events.destroy, &child->surface_destroy); 804 wl_signal_add(&surface->events.destroy, &child->surface_destroy);
772 child->surface_destroy.notify = view_child_handle_surface_destroy; 805 child->surface_destroy.notify = view_child_handle_surface_destroy;
773 806
807 // Not all child views have a map/unmap event
774 child->surface_map.notify = view_child_handle_surface_map; 808 child->surface_map.notify = view_child_handle_surface_map;
775 child->surface_unmap.notify = view_child_handle_surface_unmap; 809 child->surface_unmap.notify = view_child_handle_surface_unmap;
776 810
@@ -781,6 +815,10 @@ void view_child_init(struct sway_view_child *child,
781} 815}
782 816
783void view_child_destroy(struct sway_view_child *child) { 817void view_child_destroy(struct sway_view_child *child) {
818 if (child->mapped && child->view->container != NULL) {
819 view_child_damage(child, true);
820 }
821
784 wl_list_remove(&child->surface_commit.link); 822 wl_list_remove(&child->surface_commit.link);
785 wl_list_remove(&child->surface_destroy.link); 823 wl_list_remove(&child->surface_destroy.link);
786 824
@@ -824,12 +862,29 @@ struct sway_view *view_from_wlr_surface(struct wlr_surface *wlr_surface) {
824 return NULL; 862 return NULL;
825} 863}
826 864
865static char *escape_pango_markup(const char *buffer) {
866 size_t length = escape_markup_text(buffer, NULL);
867 char *escaped_title = calloc(length + 1, sizeof(char));
868 escape_markup_text(buffer, escaped_title);
869 return escaped_title;
870}
871
827static size_t append_prop(char *buffer, const char *value) { 872static size_t append_prop(char *buffer, const char *value) {
828 if (!value) { 873 if (!value) {
829 return 0; 874 return 0;
830 } 875 }
831 lenient_strcat(buffer, value); 876 // If using pango_markup in font, we need to escape all markup chars
832 return strlen(value); 877 // from values to make sure tags are not inserted by clients
878 if (config->pango_markup) {
879 char *escaped_value = escape_pango_markup(value);
880 lenient_strcat(buffer, escaped_value);
881 size_t len = strlen(escaped_value);
882 free(escaped_value);
883 return len;
884 } else {
885 lenient_strcat(buffer, value);
886 return strlen(value);
887 }
833} 888}
834 889
835/** 890/**
@@ -838,11 +893,7 @@ static size_t append_prop(char *buffer, const char *value) {
838 */ 893 */
839static size_t parse_title_format(struct sway_view *view, char *buffer) { 894static size_t parse_title_format(struct sway_view *view, char *buffer) {
840 if (!view->title_format || strcmp(view->title_format, "%title") == 0) { 895 if (!view->title_format || strcmp(view->title_format, "%title") == 0) {
841 const char *title = view_get_title(view); 896 return append_prop(buffer, view_get_title(view));
842 if (buffer && title) {
843 strcpy(buffer, title);
844 }
845 return title ? strlen(title) : 0;
846 } 897 }
847 898
848 size_t len = 0; 899 size_t len = 0;
@@ -882,14 +933,6 @@ static size_t parse_title_format(struct sway_view *view, char *buffer) {
882 return len; 933 return len;
883} 934}
884 935
885static char *escape_title(char *buffer) {
886 size_t length = escape_markup_text(buffer, NULL);
887 char *escaped_title = calloc(length + 1, sizeof(char));
888 escape_markup_text(buffer, escaped_title);
889 free(buffer);
890 return escaped_title;
891}
892
893void view_update_title(struct sway_view *view, bool force) { 936void view_update_title(struct sway_view *view, bool force) {
894 const char *title = view_get_title(view); 937 const char *title = view_get_title(view);
895 938
@@ -912,10 +955,6 @@ void view_update_title(struct sway_view *view, bool force) {
912 return; 955 return;
913 } 956 }
914 parse_title_format(view, buffer); 957 parse_title_format(view, buffer);
915 // now we have the title, but needs to be escaped when using pango markup
916 if (config->pango_markup) {
917 buffer = escape_title(buffer);
918 }
919 958
920 view->container->title = strdup(title); 959 view->container->title = strdup(title);
921 view->container->formatted_title = buffer; 960 view->container->formatted_title = buffer;
diff --git a/sway/tree/workspace.c b/sway/tree/workspace.c
index 4be63311..7f18046d 100644
--- a/sway/tree/workspace.c
+++ b/sway/tree/workspace.c
@@ -35,10 +35,8 @@ struct sway_output *workspace_get_initial_output(const char *name) {
35 struct workspace_config *wsc = workspace_find_config(name); 35 struct workspace_config *wsc = workspace_find_config(name);
36 if (wsc) { 36 if (wsc) {
37 for (int i = 0; i < wsc->outputs->length; i++) { 37 for (int i = 0; i < wsc->outputs->length; i++) {
38 struct sway_output *output = output_by_name(wsc->outputs->items[i]); 38 struct sway_output *output =
39 if (!output) { 39 output_by_name_or_id(wsc->outputs->items[i]);
40 output = output_by_identifier(wsc->outputs->items[i]);
41 }
42 if (output) { 40 if (output) {
43 return output; 41 return output;
44 } 42 }
@@ -113,7 +111,10 @@ struct sway_workspace *workspace_create(struct sway_output *output,
113 111
114 // Add output priorities 112 // Add output priorities
115 for (int i = 0; i < wsc->outputs->length; ++i) { 113 for (int i = 0; i < wsc->outputs->length; ++i) {
116 list_add(ws->output_priority, strdup(wsc->outputs->items[i])); 114 char *name = wsc->outputs->items[i];
115 if (strcmp(name, "*") != 0) {
116 list_add(ws->output_priority, strdup(name));
117 }
117 } 118 }
118 } 119 }
119 } 120 }
@@ -142,7 +143,7 @@ void workspace_destroy(struct sway_workspace *workspace) {
142 143
143 free(workspace->name); 144 free(workspace->name);
144 free(workspace->representation); 145 free(workspace->representation);
145 free_flat_list(workspace->output_priority); 146 list_free_items_and_destroy(workspace->output_priority);
146 list_free(workspace->floating); 147 list_free(workspace->floating);
147 list_free(workspace->tiling); 148 list_free(workspace->tiling);
148 list_free(workspace->current.floating); 149 list_free(workspace->current.floating);
@@ -182,7 +183,11 @@ static bool workspace_valid_on_output(const char *output_name,
182 const char *ws_name) { 183 const char *ws_name) {
183 struct workspace_config *wsc = workspace_find_config(ws_name); 184 struct workspace_config *wsc = workspace_find_config(ws_name);
184 char identifier[128]; 185 char identifier[128];
185 struct sway_output *output = output_by_name(output_name); 186 struct sway_output *output = output_by_name_or_id(output_name);
187 if (!output) {
188 return false;
189 }
190 output_name = output->wlr_output->name;
186 output_get_identifier(identifier, sizeof(identifier), output); 191 output_get_identifier(identifier, sizeof(identifier), output);
187 192
188 if (!wsc) { 193 if (!wsc) {
@@ -190,7 +195,8 @@ static bool workspace_valid_on_output(const char *output_name,
190 } 195 }
191 196
192 for (int i = 0; i < wsc->outputs->length; i++) { 197 for (int i = 0; i < wsc->outputs->length; i++) {
193 if (strcmp(wsc->outputs->items[i], output_name) == 0 || 198 if (strcmp(wsc->outputs->items[i], "*") == 0 ||
199 strcmp(wsc->outputs->items[i], output_name) == 0 ||
194 strcmp(wsc->outputs->items[i], identifier) == 0) { 200 strcmp(wsc->outputs->items[i], identifier) == 0) {
195 return true; 201 return true;
196 } 202 }
@@ -286,6 +292,14 @@ char *workspace_next_name(const char *output_name) {
286 // assignments primarily, falling back to bindings and numbers. 292 // assignments primarily, falling back to bindings and numbers.
287 struct sway_mode *mode = config->current_mode; 293 struct sway_mode *mode = config->current_mode;
288 294
295 char identifier[128];
296 struct sway_output *output = output_by_name_or_id(output_name);
297 if (!output) {
298 return NULL;
299 }
300 output_name = output->wlr_output->name;
301 output_get_identifier(identifier, sizeof(identifier), output);
302
289 int order = INT_MAX; 303 int order = INT_MAX;
290 char *target = NULL; 304 char *target = NULL;
291 for (int i = 0; i < mode->keysym_bindings->length; ++i) { 305 for (int i = 0; i < mode->keysym_bindings->length; ++i) {
@@ -304,7 +318,9 @@ char *workspace_next_name(const char *output_name) {
304 } 318 }
305 bool found = false; 319 bool found = false;
306 for (int j = 0; j < wsc->outputs->length; ++j) { 320 for (int j = 0; j < wsc->outputs->length; ++j) {
307 if (strcmp(wsc->outputs->items[j], output_name) == 0) { 321 if (strcmp(wsc->outputs->items[j], "*") == 0 ||
322 strcmp(wsc->outputs->items[j], output_name) == 0 ||
323 strcmp(wsc->outputs->items[j], identifier) == 0) {
308 found = true; 324 found = true;
309 free(target); 325 free(target);
310 target = strdup(wsc->workspace); 326 target = strdup(wsc->workspace);
@@ -525,13 +541,19 @@ void workspace_output_add_priority(struct sway_workspace *workspace,
525 541
526struct sway_output *workspace_output_get_highest_available( 542struct sway_output *workspace_output_get_highest_available(
527 struct sway_workspace *ws, struct sway_output *exclude) { 543 struct sway_workspace *ws, struct sway_output *exclude) {
544 char exclude_id[128] = {'\0'};
545 if (exclude) {
546 output_get_identifier(exclude_id, sizeof(exclude_id), exclude);
547 }
548
528 for (int i = 0; i < ws->output_priority->length; i++) { 549 for (int i = 0; i < ws->output_priority->length; i++) {
529 char *name = ws->output_priority->items[i]; 550 char *name = ws->output_priority->items[i];
530 if (exclude && strcasecmp(name, exclude->wlr_output->name) == 0) { 551 if (exclude && (strcmp(name, exclude->wlr_output->name) == 0
552 || strcmp(name, exclude_id) == 0)) {
531 continue; 553 continue;
532 } 554 }
533 555
534 struct sway_output *output = output_by_name(name); 556 struct sway_output *output = output_by_name_or_id(name);
535 if (output) { 557 if (output) {
536 return output; 558 return output;
537 } 559 }