summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--common/util.c16
-rw-r--r--config.in3
-rw-r--r--include/sway/config.h35
-rw-r--r--include/sway/criteria.h3
-rw-r--r--include/sway/input/cursor.h6
-rw-r--r--include/sway/server.h10
-rw-r--r--include/sway/tree/layout.h4
-rw-r--r--include/sway/tree/view.h16
-rw-r--r--include/sway/tree/workspace.h4
-rw-r--r--include/util.h8
-rw-r--r--meson.build6
-rw-r--r--meson_options.txt1
-rw-r--r--sway/commands/bind.c168
-rw-r--r--sway/commands/exec_always.c2
-rw-r--r--sway/commands/floating_modifier.c12
-rw-r--r--sway/commands/focus_follows_mouse.c4
-rw-r--r--sway/commands/focus_wrapping.c12
-rw-r--r--sway/commands/force_focus_wrapping.c8
-rw-r--r--sway/commands/fullscreen.c14
-rw-r--r--sway/commands/input/drag_lock.c9
-rw-r--r--sway/commands/input/dwt.c9
-rw-r--r--sway/commands/input/left_handed.c11
-rw-r--r--sway/commands/input/middle_emulation.c9
-rw-r--r--sway/commands/input/natural_scroll.c11
-rw-r--r--sway/commands/input/tap.c9
-rw-r--r--sway/commands/output/dpms.c8
-rw-r--r--sway/commands/show_marks.c10
-rw-r--r--sway/commands/swap.c7
-rw-r--r--sway/commands/urgent.c10
-rw-r--r--sway/config.c10
-rw-r--r--sway/criteria.c13
-rw-r--r--sway/desktop/output.c14
-rw-r--r--sway/desktop/render.c11
-rw-r--r--sway/desktop/xdg_shell.c3
-rw-r--r--sway/desktop/xdg_shell_v6.c3
-rw-r--r--sway/desktop/xwayland.c3
-rw-r--r--sway/input/cursor.c120
-rw-r--r--sway/input/keyboard.c42
-rw-r--r--sway/input/seat.c4
-rw-r--r--sway/meson.build5
-rw-r--r--sway/server.c5
-rw-r--r--sway/tree/container.c2
-rw-r--r--sway/tree/layout.c3
-rw-r--r--sway/tree/view.c59
-rw-r--r--sway/tree/workspace.c271
45 files changed, 700 insertions, 293 deletions
diff --git a/common/util.c b/common/util.c
index e8a88772..467aa4b5 100644
--- a/common/util.c
+++ b/common/util.c
@@ -123,6 +123,22 @@ uint32_t parse_color(const char *color) {
123 return res; 123 return res;
124} 124}
125 125
126bool parse_boolean(const char *boolean, bool current) {
127 if (strcasecmp(boolean, "1") == 0
128 || strcasecmp(boolean, "yes") == 0
129 || strcasecmp(boolean, "on") == 0
130 || strcasecmp(boolean, "true") == 0
131 || strcasecmp(boolean, "enable") == 0
132 || strcasecmp(boolean, "enabled") == 0
133 || strcasecmp(boolean, "active") == 0) {
134 return true;
135 } else if (strcasecmp(boolean, "toggle") == 0) {
136 return !current;
137 }
138 // All other values are false to match i3
139 return false;
140}
141
126char* resolve_path(const char* path) { 142char* resolve_path(const char* path) {
127 struct stat sb; 143 struct stat sb;
128 ssize_t r; 144 ssize_t r;
diff --git a/config.in b/config.in
index 4a11762a..41f53461 100644
--- a/config.in
+++ b/config.in
@@ -16,7 +16,8 @@ set $right l
16# Your preferred terminal emulator 16# Your preferred terminal emulator
17set $term urxvt 17set $term urxvt
18# Your preferred application launcher 18# Your preferred application launcher
19set $menu dmenu_run 19# Note: it's recommended that you pass the final command to sway
20set $menu dmenu_path | dmenu | xargs swaymsg exec
20 21
21### Output configuration 22### Output configuration
22# 23#
diff --git a/include/sway/config.h b/include/sway/config.h
index b8da29c5..4a6bb780 100644
--- a/include/sway/config.h
+++ b/include/sway/config.h
@@ -1,6 +1,5 @@
1#ifndef _SWAY_CONFIG_H 1#ifndef _SWAY_CONFIG_H
2#define _SWAY_CONFIG_H 2#define _SWAY_CONFIG_H
3#define PID_WORKSPACE_TIMEOUT 60
4#include <libinput.h> 3#include <libinput.h>
5#include <stdint.h> 4#include <stdint.h>
6#include <string.h> 5#include <string.h>
@@ -22,14 +21,28 @@ struct sway_variable {
22 char *value; 21 char *value;
23}; 22};
24 23
24
25enum binding_input_type {
26 BINDING_KEYCODE,
27 BINDING_KEYSYM,
28 BINDING_MOUSE,
29};
30
31enum binding_flags {
32 BINDING_RELEASE=1,
33 BINDING_LOCKED=2, // keyboard only
34 BINDING_BORDER=4, // mouse only; trigger on container border
35 BINDING_CONTENTS=8, // mouse only; trigger on container contents
36 BINDING_TITLEBAR=16 // mouse only; trigger on container titlebar
37};
38
25/** 39/**
26 * A key binding and an associated command. 40 * A key binding and an associated command.
27 */ 41 */
28struct sway_binding { 42struct sway_binding {
43 enum binding_input_type type;
29 int order; 44 int order;
30 bool release; 45 uint32_t flags;
31 bool locked;
32 bool bindcode;
33 list_t *keys; // sorted in ascending order 46 list_t *keys; // sorted in ascending order
34 uint32_t modifiers; 47 uint32_t modifiers;
35 char *command; 48 char *command;
@@ -50,6 +63,7 @@ struct sway_mode {
50 char *name; 63 char *name;
51 list_t *keysym_bindings; 64 list_t *keysym_bindings;
52 list_t *keycode_bindings; 65 list_t *keycode_bindings;
66 list_t *mouse_bindings;
53 bool pango; 67 bool pango;
54}; 68};
55 69
@@ -146,12 +160,6 @@ struct workspace_output {
146 char *workspace; 160 char *workspace;
147}; 161};
148 162
149struct pid_workspace {
150 pid_t *pid;
151 char *workspace;
152 time_t *time_added;
153};
154
155struct bar_config { 163struct bar_config {
156 /** 164 /**
157 * One of "dock", "hide", "invisible" 165 * One of "dock", "hide", "invisible"
@@ -302,7 +310,6 @@ struct sway_config {
302 list_t *bars; 310 list_t *bars;
303 list_t *cmd_queue; 311 list_t *cmd_queue;
304 list_t *workspace_outputs; 312 list_t *workspace_outputs;
305 list_t *pid_workspaces;
306 list_t *output_configs; 313 list_t *output_configs;
307 list_t *input_configs; 314 list_t *input_configs;
308 list_t *seat_configs; 315 list_t *seat_configs;
@@ -313,6 +320,7 @@ struct sway_config {
313 struct bar_config *current_bar; 320 struct bar_config *current_bar;
314 char *swaybg_command; 321 char *swaybg_command;
315 uint32_t floating_mod; 322 uint32_t floating_mod;
323 bool floating_mod_inverse;
316 uint32_t dragging_key; 324 uint32_t dragging_key;
317 uint32_t resizing_key; 325 uint32_t resizing_key;
318 char *floating_scroll_up_cmd; 326 char *floating_scroll_up_cmd;
@@ -388,9 +396,6 @@ struct sway_config {
388 } handler_context; 396 } handler_context;
389}; 397};
390 398
391void pid_workspace_add(struct pid_workspace *pw);
392void free_pid_workspace(struct pid_workspace *pw);
393
394/** 399/**
395 * Loads the main config from the given path. is_active should be true when 400 * Loads the main config from the given path. is_active should be true when
396 * reloading the config. 401 * reloading the config.
@@ -482,6 +487,8 @@ void free_sway_binding(struct sway_binding *sb);
482 487
483struct sway_binding *sway_binding_dup(struct sway_binding *sb); 488struct sway_binding *sway_binding_dup(struct sway_binding *sb);
484 489
490void seat_execute_command(struct sway_seat *seat, struct sway_binding *binding);
491
485void load_swaybars(); 492void load_swaybars();
486 493
487void invoke_swaybar(struct bar_config *bar); 494void invoke_swaybar(struct bar_config *bar);
diff --git a/include/sway/criteria.h b/include/sway/criteria.h
index 6a8337c5..b4ff7d49 100644
--- a/include/sway/criteria.h
+++ b/include/sway/criteria.h
@@ -2,6 +2,7 @@
2#define _SWAY_CRITERIA_H 2#define _SWAY_CRITERIA_H
3 3
4#include <pcre.h> 4#include <pcre.h>
5#include "config.h"
5#include "list.h" 6#include "list.h"
6#include "tree/view.h" 7#include "tree/view.h"
7 8
@@ -25,7 +26,9 @@ struct criteria {
25 pcre *instance; 26 pcre *instance;
26 pcre *con_mark; 27 pcre *con_mark;
27 uint32_t con_id; // internal ID 28 uint32_t con_id; // internal ID
29#ifdef HAVE_XWAYLAND
28 uint32_t id; // X11 window ID 30 uint32_t id; // X11 window ID
31#endif
29 pcre *window_role; 32 pcre *window_role;
30 uint32_t window_type; 33 uint32_t window_type;
31 bool floating; 34 bool floating;
diff --git a/include/sway/input/cursor.h b/include/sway/input/cursor.h
index b0a3a7c5..7ec45120 100644
--- a/include/sway/input/cursor.h
+++ b/include/sway/input/cursor.h
@@ -3,6 +3,8 @@
3#include <stdint.h> 3#include <stdint.h>
4#include "sway/input/seat.h" 4#include "sway/input/seat.h"
5 5
6#define SWAY_CURSOR_PRESSED_BUTTONS_CAP 32
7
6struct sway_cursor { 8struct sway_cursor {
7 struct sway_seat *seat; 9 struct sway_seat *seat;
8 struct wlr_cursor *cursor; 10 struct wlr_cursor *cursor;
@@ -29,6 +31,10 @@ struct sway_cursor {
29 uint32_t tool_buttons; 31 uint32_t tool_buttons;
30 32
31 struct wl_listener request_set_cursor; 33 struct wl_listener request_set_cursor;
34
35 // Mouse binding state
36 uint32_t pressed_buttons[SWAY_CURSOR_PRESSED_BUTTONS_CAP];
37 size_t pressed_button_count;
32}; 38};
33 39
34void sway_cursor_destroy(struct sway_cursor *cursor); 40void sway_cursor_destroy(struct sway_cursor *cursor);
diff --git a/include/sway/server.h b/include/sway/server.h
index 70bde6d4..a3782f91 100644
--- a/include/sway/server.h
+++ b/include/sway/server.h
@@ -12,7 +12,10 @@
12#include <wlr/render/wlr_renderer.h> 12#include <wlr/render/wlr_renderer.h>
13// TODO WLR: make Xwayland optional 13// TODO WLR: make Xwayland optional
14#include "list.h" 14#include "list.h"
15#include "config.h"
16#ifdef HAVE_XWAYLAND
15#include "sway/xwayland.h" 17#include "sway/xwayland.h"
18#endif
16 19
17struct sway_server { 20struct sway_server {
18 struct wl_display *wl_display; 21 struct wl_display *wl_display;
@@ -39,11 +42,11 @@ struct sway_server {
39 42
40 struct wlr_xdg_shell *xdg_shell; 43 struct wlr_xdg_shell *xdg_shell;
41 struct wl_listener xdg_shell_surface; 44 struct wl_listener xdg_shell_surface;
42 45#ifdef HAVE_XWAYLAND
43 struct sway_xwayland xwayland; 46 struct sway_xwayland xwayland;
44 struct wl_listener xwayland_surface; 47 struct wl_listener xwayland_surface;
45 struct wl_listener xwayland_ready; 48 struct wl_listener xwayland_ready;
46 49#endif
47 bool debug_txn_timings; 50 bool debug_txn_timings;
48 51
49 list_t *transactions; 52 list_t *transactions;
@@ -65,6 +68,7 @@ void handle_idle_inhibitor_v1(struct wl_listener *listener, void *data);
65void handle_layer_shell_surface(struct wl_listener *listener, void *data); 68void handle_layer_shell_surface(struct wl_listener *listener, void *data);
66void handle_xdg_shell_v6_surface(struct wl_listener *listener, void *data); 69void handle_xdg_shell_v6_surface(struct wl_listener *listener, void *data);
67void handle_xdg_shell_surface(struct wl_listener *listener, void *data); 70void handle_xdg_shell_surface(struct wl_listener *listener, void *data);
71#ifdef HAVE_XWAYLAND
68void handle_xwayland_surface(struct wl_listener *listener, void *data); 72void handle_xwayland_surface(struct wl_listener *listener, void *data);
69 73#endif
70#endif 74#endif
diff --git a/include/sway/tree/layout.h b/include/sway/tree/layout.h
index 7d7da2d7..a4c31bf6 100644
--- a/include/sway/tree/layout.h
+++ b/include/sway/tree/layout.h
@@ -3,6 +3,7 @@
3#include <wlr/types/wlr_output_layout.h> 3#include <wlr/types/wlr_output_layout.h>
4#include <wlr/render/wlr_texture.h> 4#include <wlr/render/wlr_texture.h>
5#include "sway/tree/container.h" 5#include "sway/tree/container.h"
6#include "config.h"
6 7
7enum movement_direction { 8enum movement_direction {
8 MOVE_LEFT, 9 MOVE_LEFT,
@@ -27,8 +28,9 @@ struct sway_root {
27 struct wlr_output_layout *output_layout; 28 struct wlr_output_layout *output_layout;
28 29
29 struct wl_listener output_layout_change; 30 struct wl_listener output_layout_change;
30 31#ifdef HAVE_XWAYLAND
31 struct wl_list xwayland_unmanaged; // sway_xwayland_unmanaged::link 32 struct wl_list xwayland_unmanaged; // sway_xwayland_unmanaged::link
33#endif
32 struct wl_list drag_icons; // sway_drag_icon::link 34 struct wl_list drag_icons; // sway_drag_icon::link
33 35
34 struct wlr_texture *debug_tree; 36 struct wlr_texture *debug_tree;
diff --git a/include/sway/tree/view.h b/include/sway/tree/view.h
index 3bdfe252..1972447b 100644
--- a/include/sway/tree/view.h
+++ b/include/sway/tree/view.h
@@ -3,7 +3,10 @@
3#include <wayland-server.h> 3#include <wayland-server.h>
4#include <wlr/types/wlr_surface.h> 4#include <wlr/types/wlr_surface.h>
5#include <wlr/types/wlr_xdg_shell_v6.h> 5#include <wlr/types/wlr_xdg_shell_v6.h>
6#include "config.h"
7#ifdef HAVE_XWAYLAND
6#include <wlr/xwayland.h> 8#include <wlr/xwayland.h>
9#endif
7#include "sway/input/input-manager.h" 10#include "sway/input/input-manager.h"
8#include "sway/input/seat.h" 11#include "sway/input/seat.h"
9 12
@@ -12,7 +15,9 @@ struct sway_container;
12enum sway_view_type { 15enum sway_view_type {
13 SWAY_VIEW_XDG_SHELL_V6, 16 SWAY_VIEW_XDG_SHELL_V6,
14 SWAY_VIEW_XDG_SHELL, 17 SWAY_VIEW_XDG_SHELL,
18#ifdef HAVE_XWAYLAND
15 SWAY_VIEW_XWAYLAND, 19 SWAY_VIEW_XWAYLAND,
20#endif
16}; 21};
17 22
18enum sway_view_prop { 23enum sway_view_prop {
@@ -22,7 +27,9 @@ enum sway_view_prop {
22 VIEW_PROP_INSTANCE, 27 VIEW_PROP_INSTANCE,
23 VIEW_PROP_WINDOW_TYPE, 28 VIEW_PROP_WINDOW_TYPE,
24 VIEW_PROP_WINDOW_ROLE, 29 VIEW_PROP_WINDOW_ROLE,
30#ifdef HAVE_XWAYLAND
25 VIEW_PROP_X11_WINDOW_ID, 31 VIEW_PROP_X11_WINDOW_ID,
32#endif
26}; 33};
27 34
28struct sway_view_impl { 35struct sway_view_impl {
@@ -90,7 +97,9 @@ struct sway_view {
90 union { 97 union {
91 struct wlr_xdg_surface_v6 *wlr_xdg_surface_v6; 98 struct wlr_xdg_surface_v6 *wlr_xdg_surface_v6;
92 struct wlr_xdg_surface *wlr_xdg_surface; 99 struct wlr_xdg_surface *wlr_xdg_surface;
100#ifdef HAVE_XWAYLAND
93 struct wlr_xwayland_surface *wlr_xwayland_surface; 101 struct wlr_xwayland_surface *wlr_xwayland_surface;
102#endif
94 struct wlr_wl_shell_surface *wlr_wl_shell_surface; 103 struct wlr_wl_shell_surface *wlr_wl_shell_surface;
95 }; 104 };
96 105
@@ -133,7 +142,7 @@ struct sway_xdg_shell_view {
133 struct wl_listener unmap; 142 struct wl_listener unmap;
134 struct wl_listener destroy; 143 struct wl_listener destroy;
135}; 144};
136 145#ifdef HAVE_XWAYLAND
137struct sway_xwayland_view { 146struct sway_xwayland_view {
138 struct sway_view view; 147 struct sway_view view;
139 148
@@ -165,7 +174,7 @@ struct sway_xwayland_unmanaged {
165 struct wl_listener unmap; 174 struct wl_listener unmap;
166 struct wl_listener destroy; 175 struct wl_listener destroy;
167}; 176};
168 177#endif
169struct sway_view_child; 178struct sway_view_child;
170 179
171struct sway_view_child_impl { 180struct sway_view_child_impl {
@@ -281,9 +290,10 @@ struct sway_view *view_from_wlr_xdg_surface(
281 struct wlr_xdg_surface *xdg_surface); 290 struct wlr_xdg_surface *xdg_surface);
282struct sway_view *view_from_wlr_xdg_surface_v6( 291struct sway_view *view_from_wlr_xdg_surface_v6(
283 struct wlr_xdg_surface_v6 *xdg_surface_v6); 292 struct wlr_xdg_surface_v6 *xdg_surface_v6);
293#ifdef HAVE_XWAYLAND
284struct sway_view *view_from_wlr_xwayland_surface( 294struct sway_view *view_from_wlr_xwayland_surface(
285 struct wlr_xwayland_surface *xsurface); 295 struct wlr_xwayland_surface *xsurface);
286 296#endif
287struct sway_view *view_from_wlr_surface(struct wlr_surface *surface); 297struct sway_view *view_from_wlr_surface(struct wlr_surface *surface);
288 298
289/** 299/**
diff --git a/include/sway/tree/workspace.h b/include/sway/tree/workspace.h
index bc95317a..ff66da6b 100644
--- a/include/sway/tree/workspace.h
+++ b/include/sway/tree/workspace.h
@@ -44,6 +44,10 @@ void workspace_output_add_priority(struct sway_container *workspace,
44struct sway_container *workspace_output_get_highest_available( 44struct sway_container *workspace_output_get_highest_available(
45 struct sway_container *ws, struct sway_container *exclude); 45 struct sway_container *ws, struct sway_container *exclude);
46 46
47struct sway_container *workspace_for_pid(pid_t pid);
48
49void workspace_record_pid(pid_t pid);
50
47void workspace_detect_urgent(struct sway_container *workspace); 51void workspace_detect_urgent(struct sway_container *workspace);
48 52
49#endif 53#endif
diff --git a/include/util.h b/include/util.h
index f68deae8..bda941ce 100644
--- a/include/util.h
+++ b/include/util.h
@@ -51,6 +51,14 @@ pid_t get_parent_pid(pid_t pid);
51uint32_t parse_color(const char *color); 51uint32_t parse_color(const char *color);
52 52
53/** 53/**
54 * Given a string that represents a boolean, return the boolean value. This
55 * function also takes in the current boolean value to support toggling. If
56 * toggling is not desired, pass in true for current so that toggling values
57 * get parsed as not true.
58 */
59bool parse_boolean(const char *boolean, bool current);
60
61/**
54 * Given a path string, recurseively resolves any symlinks to their targets 62 * Given a path string, recurseively resolves any symlinks to their targets
55 * (which may be a file, directory) and returns the result. 63 * (which may be a file, directory) and returns the result.
56 * argument is returned. Caller must free the returned buffer. 64 * argument is returned. Caller must free the returned buffer.
diff --git a/meson.build b/meson.build
index 1d40581a..06299618 100644
--- a/meson.build
+++ b/meson.build
@@ -48,6 +48,12 @@ git = find_program('git', required: false)
48 48
49conf_data = configuration_data() 49conf_data = configuration_data()
50 50
51if get_option('enable-xwayland')
52 conf_data.set('HAVE_XWAYLAND', true)
53else
54 conf_data.set('HAVE_XWAYLAND', false)
55endif
56
51if gdk_pixbuf.found() 57if gdk_pixbuf.found()
52 conf_data.set('HAVE_GDK_PIXBUF', true) 58 conf_data.set('HAVE_GDK_PIXBUF', true)
53endif 59endif
diff --git a/meson_options.txt b/meson_options.txt
index 541ccf13..6c7f241d 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -1,3 +1,4 @@
1option('sway_version', type : 'string', description: 'The version string reported in `sway --version`.') 1option('sway_version', type : 'string', description: 'The version string reported in `sway --version`.')
2option('default_wallpaper', type: 'boolean', value: true, description: 'Install the default wallpaper.') 2option('default_wallpaper', type: 'boolean', value: true, description: 'Install the default wallpaper.')
3option('zsh_completions', type: 'boolean', value: true, description: 'Install zsh shell completions.') 3option('zsh_completions', type: 'boolean', value: true, description: 'Install zsh shell completions.')
4option('enable-xwayland', type: 'boolean', value: true, description: 'Enable support for X11 applications')
diff --git a/sway/commands/bind.c b/sway/commands/bind.c
index 83e9e432..133fd089 100644
--- a/sway/commands/bind.c
+++ b/sway/commands/bind.c
@@ -34,11 +34,14 @@ void free_sway_binding(struct sway_binding *binding) {
34 */ 34 */
35static bool binding_key_compare(struct sway_binding *binding_a, 35static bool binding_key_compare(struct sway_binding *binding_a,
36 struct sway_binding *binding_b) { 36 struct sway_binding *binding_b) {
37 if (binding_a->release != binding_b->release) { 37 if (binding_a->type != binding_b->type) {
38 return false; 38 return false;
39 } 39 }
40 40
41 if (binding_a->bindcode != binding_b->bindcode) { 41 uint32_t conflict_generating_flags = BINDING_RELEASE | BINDING_BORDER
42 | BINDING_CONTENTS | BINDING_TITLEBAR;
43 if ((binding_a->flags & conflict_generating_flags) !=
44 (binding_b->flags & conflict_generating_flags)) {
42 return false; 45 return false;
43 } 46 }
44 47
@@ -69,6 +72,66 @@ static int key_qsort_cmp(const void *keyp_a, const void *keyp_b) {
69 return (key_a < key_b) ? -1 : ((key_a > key_b) ? 1 : 0); 72 return (key_a < key_b) ? -1 : ((key_a > key_b) ? 1 : 0);
70} 73}
71 74
75
76/**
77 * From a keycode, bindcode, or bindsym name and the most likely binding type,
78 * identify the appropriate numeric value corresponding to the key. Return NULL
79 * and set *key_val if successful, otherwise return a specific error. Change
80 * the value of *type if the initial type guess was incorrect and if this
81 * was the first identified key.
82 */
83static struct cmd_results *identify_key(const char* name, bool first_key,
84 uint32_t* key_val, enum binding_input_type* type) {
85 if (*type == BINDING_KEYCODE) {
86 // check for keycode
87 xkb_keycode_t keycode = strtol(name, NULL, 10);
88 if (!xkb_keycode_is_legal_ext(keycode)) {
89 return cmd_results_new(CMD_INVALID, "bindcode",
90 "Invalid keycode '%s'", name);
91 }
92 *key_val = keycode;
93 } else {
94 // check for keysym
95 xkb_keysym_t keysym = xkb_keysym_from_name(name,
96 XKB_KEYSYM_CASE_INSENSITIVE);
97
98 // Check for mouse binding
99 uint32_t button = 0;
100 if (strncasecmp(name, "button", strlen("button")) == 0 &&
101 strlen(name) == strlen("button0")) {
102 button = name[strlen("button")] - '1' + BTN_LEFT;
103 }
104
105 if (*type == BINDING_KEYSYM) {
106 if (button) {
107 if (first_key) {
108 *type = BINDING_MOUSE;
109 *key_val = button;
110 } else {
111 return cmd_results_new(CMD_INVALID, "bindsym",
112 "Mixed button '%s' into key sequence", name);
113 }
114 } else if (keysym) {
115 *key_val = keysym;
116 } else {
117 return cmd_results_new(CMD_INVALID, "bindsym",
118 "Unknown key '%s'", name);
119 }
120 } else {
121 if (button) {
122 *key_val = button;
123 } else if (keysym) {
124 return cmd_results_new(CMD_INVALID, "bindsym",
125 "Mixed keysym '%s' into button sequence", name);
126 } else {
127 return cmd_results_new(CMD_INVALID, "bindsym",
128 "Unknown button '%s'", name);
129 }
130 }
131 }
132 return NULL;
133}
134
72static struct cmd_results *cmd_bindsym_or_bindcode(int argc, char **argv, 135static struct cmd_results *cmd_bindsym_or_bindcode(int argc, char **argv,
73 bool bindcode) { 136 bool bindcode) {
74 const char *bindtype = bindcode ? "bindcode" : "bindsym"; 137 const char *bindtype = bindcode ? "bindcode" : "bindsym";
@@ -85,22 +148,34 @@ static struct cmd_results *cmd_bindsym_or_bindcode(int argc, char **argv,
85 } 148 }
86 binding->keys = create_list(); 149 binding->keys = create_list();
87 binding->modifiers = 0; 150 binding->modifiers = 0;
88 binding->release = false; 151 binding->flags = 0;
89 binding->locked = false; 152 binding->type = bindcode ? BINDING_KEYCODE : BINDING_KEYSYM;
90 binding->bindcode = bindcode; 153
154 bool exclude_titlebar = false;
91 155
92 // Handle --release and --locked 156 // Handle --release and --locked
93 while (argc > 0) { 157 while (argc > 0) {
94 if (strcmp("--release", argv[0]) == 0) { 158 if (strcmp("--release", argv[0]) == 0) {
95 binding->release = true; 159 binding->flags |= BINDING_RELEASE;
96 } else if (strcmp("--locked", argv[0]) == 0) { 160 } else if (strcmp("--locked", argv[0]) == 0) {
97 binding->locked = true; 161 binding->flags |= BINDING_LOCKED;
162 } else if (strcmp("--whole-window", argv[0]) == 0) {
163 binding->flags |= BINDING_BORDER | BINDING_CONTENTS | BINDING_TITLEBAR;
164 } else if (strcmp("--border", argv[0]) == 0) {
165 binding->flags |= BINDING_BORDER;
166 } else if (strcmp("--exclude-titlebar", argv[0]) == 0) {
167 exclude_titlebar = true;
98 } else { 168 } else {
99 break; 169 break;
100 } 170 }
101 argv++; 171 argv++;
102 argc--; 172 argc--;
103 } 173 }
174 if (binding->flags & (BINDING_BORDER | BINDING_CONTENTS | BINDING_TITLEBAR)
175 || exclude_titlebar) {
176 binding->type = BINDING_MOUSE;
177 }
178
104 if (argc < 2) { 179 if (argc < 2) {
105 free_sway_binding(binding); 180 free_sway_binding(binding);
106 return cmd_results_new(CMD_FAILURE, bindtype, 181 return cmd_results_new(CMD_FAILURE, bindtype,
@@ -119,64 +194,47 @@ static struct cmd_results *cmd_bindsym_or_bindcode(int argc, char **argv,
119 continue; 194 continue;
120 } 195 }
121 196
122 xkb_keycode_t keycode; 197 // Identify the key and possibly change binding->type
123 xkb_keysym_t keysym; 198 uint32_t key_val = 0;
124 if (bindcode) { 199 error = identify_key(split->items[i], binding->keys->length == 0,
125 // parse keycode 200 &key_val, &binding->type);
126 keycode = (int)strtol(split->items[i], NULL, 10); 201 if (error) {
127 if (!xkb_keycode_is_legal_ext(keycode)) { 202 free_sway_binding(binding);
128 error = 203 list_free(split);
129 cmd_results_new(CMD_INVALID, "bindcode", 204 return error;
130 "Invalid keycode '%s'", (char *)split->items[i]);
131 free_sway_binding(binding);
132 list_free(split);
133 return error;
134 }
135 } else {
136 // Check for xkb key
137 keysym = xkb_keysym_from_name(split->items[i],
138 XKB_KEYSYM_CASE_INSENSITIVE);
139
140 // Check for mouse binding
141 if (strncasecmp(split->items[i], "button", strlen("button")) == 0 &&
142 strlen(split->items[i]) == strlen("button0")) {
143 keysym = ((char *)split->items[i])[strlen("button")] - '1' + BTN_LEFT;
144 }
145 if (!keysym) {
146 struct cmd_results *ret = cmd_results_new(CMD_INVALID, "bindsym",
147 "Unknown key '%s'", (char *)split->items[i]);
148 free_sway_binding(binding);
149 free_flat_list(split);
150 return ret;
151 }
152 } 205 }
206
153 uint32_t *key = calloc(1, sizeof(uint32_t)); 207 uint32_t *key = calloc(1, sizeof(uint32_t));
154 if (!key) { 208 if (!key) {
155 free_sway_binding(binding); 209 free_sway_binding(binding);
156 free_flat_list(split); 210 free_flat_list(split);
157 return cmd_results_new(CMD_FAILURE, bindtype, 211 return cmd_results_new(CMD_FAILURE, bindtype,
158 "Unable to allocate binding"); 212 "Unable to allocate binding key");
159 } 213 }
160 214 *key = key_val;
161 if (bindcode) {
162 *key = (uint32_t)keycode;
163 } else {
164 *key = (uint32_t)keysym;
165 }
166
167 list_add(binding->keys, key); 215 list_add(binding->keys, key);
168 } 216 }
169 free_flat_list(split); 217 free_flat_list(split);
170 binding->order = binding_order++; 218 binding->order = binding_order++;
171 219
220 // refine region of interest for mouse binding once we are certain
221 // that this is one
222 if (exclude_titlebar) {
223 binding->flags &= ~BINDING_TITLEBAR;
224 } else if (binding->type == BINDING_MOUSE) {
225 binding->flags |= BINDING_TITLEBAR;
226 }
227
172 // sort ascending 228 // sort ascending
173 list_qsort(binding->keys, key_qsort_cmp); 229 list_qsort(binding->keys, key_qsort_cmp);
174 230
175 list_t *mode_bindings; 231 list_t *mode_bindings;
176 if (bindcode) { 232 if (binding->type == BINDING_KEYCODE) {
177 mode_bindings = config->current_mode->keycode_bindings; 233 mode_bindings = config->current_mode->keycode_bindings;
178 } else { 234 } else if (binding->type == BINDING_KEYSYM) {
179 mode_bindings = config->current_mode->keysym_bindings; 235 mode_bindings = config->current_mode->keysym_bindings;
236 } else {
237 mode_bindings = config->current_mode->mouse_bindings;
180 } 238 }
181 239
182 // overwrite the binding if it already exists 240 // overwrite the binding if it already exists
@@ -209,3 +267,19 @@ struct cmd_results *cmd_bindsym(int argc, char **argv) {
209struct cmd_results *cmd_bindcode(int argc, char **argv) { 267struct cmd_results *cmd_bindcode(int argc, char **argv) {
210 return cmd_bindsym_or_bindcode(argc, argv, true); 268 return cmd_bindsym_or_bindcode(argc, argv, true);
211} 269}
270
271
272/**
273 * Execute the command associated to a binding
274 */
275void seat_execute_command(struct sway_seat *seat, struct sway_binding *binding) {
276 wlr_log(WLR_DEBUG, "running command for binding: %s",
277 binding->command);
278 config->handler_context.seat = seat;
279 struct cmd_results *results = execute_command(binding->command, NULL);
280 if (results->status != CMD_SUCCESS) {
281 wlr_log(WLR_DEBUG, "could not run command for binding: %s (%s)",
282 binding->command, results->error);
283 }
284 free_cmd_results(results);
285}
diff --git a/sway/commands/exec_always.c b/sway/commands/exec_always.c
index c7727857..9bf2b320 100644
--- a/sway/commands/exec_always.c
+++ b/sway/commands/exec_always.c
@@ -74,7 +74,7 @@ struct cmd_results *cmd_exec_always(int argc, char **argv) {
74 waitpid(pid, NULL, 0); 74 waitpid(pid, NULL, 0);
75 if (child > 0) { 75 if (child > 0) {
76 wlr_log(WLR_DEBUG, "Child process created with pid %d", child); 76 wlr_log(WLR_DEBUG, "Child process created with pid %d", child);
77 // TODO: add PID to active workspace 77 workspace_record_pid(child);
78 } else { 78 } else {
79 return cmd_results_new(CMD_FAILURE, "exec_always", 79 return cmd_results_new(CMD_FAILURE, "exec_always",
80 "Second fork() failed"); 80 "Second fork() failed");
diff --git a/sway/commands/floating_modifier.c b/sway/commands/floating_modifier.c
index 9432c9f1..f5d2b3fe 100644
--- a/sway/commands/floating_modifier.c
+++ b/sway/commands/floating_modifier.c
@@ -1,10 +1,11 @@
1#include "strings.h"
1#include "sway/commands.h" 2#include "sway/commands.h"
2#include "sway/config.h" 3#include "sway/config.h"
3#include "util.h" 4#include "util.h"
4 5
5struct cmd_results *cmd_floating_modifier(int argc, char **argv) { 6struct cmd_results *cmd_floating_modifier(int argc, char **argv) {
6 struct cmd_results *error = NULL; 7 struct cmd_results *error = NULL;
7 if ((error = checkarg(argc, "floating_modifier", EXPECTED_EQUAL_TO, 1))) { 8 if ((error = checkarg(argc, "floating_modifier", EXPECTED_AT_LEAST, 1))) {
8 return error; 9 return error;
9 } 10 }
10 11
@@ -14,6 +15,15 @@ struct cmd_results *cmd_floating_modifier(int argc, char **argv) {
14 "Invalid modifier"); 15 "Invalid modifier");
15 } 16 }
16 17
18 if (argc == 1 || strcasecmp(argv[1], "normal") == 0) {
19 config->floating_mod_inverse = false;
20 } else if (strcasecmp(argv[1], "inverse") == 0) {
21 config->floating_mod_inverse = true;
22 } else {
23 return cmd_results_new(CMD_INVALID, "floating_modifier",
24 "Usage: floating_modifier <mod> [inverse|normal]");
25 }
26
17 config->floating_mod = mod; 27 config->floating_mod = mod;
18 28
19 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 29 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
diff --git a/sway/commands/focus_follows_mouse.c b/sway/commands/focus_follows_mouse.c
index 661e7852..0b0e334c 100644
--- a/sway/commands/focus_follows_mouse.c
+++ b/sway/commands/focus_follows_mouse.c
@@ -1,12 +1,14 @@
1#include <string.h> 1#include <string.h>
2#include <strings.h> 2#include <strings.h>
3#include "sway/commands.h" 3#include "sway/commands.h"
4#include "util.h"
4 5
5struct cmd_results *cmd_focus_follows_mouse(int argc, char **argv) { 6struct cmd_results *cmd_focus_follows_mouse(int argc, char **argv) {
6 struct cmd_results *error = NULL; 7 struct cmd_results *error = NULL;
7 if ((error = checkarg(argc, "focus_follows_mouse", EXPECTED_EQUAL_TO, 1))) { 8 if ((error = checkarg(argc, "focus_follows_mouse", EXPECTED_EQUAL_TO, 1))) {
8 return error; 9 return error;
9 } 10 }
10 config->focus_follows_mouse = !strcasecmp(argv[0], "yes"); 11 config->focus_follows_mouse =
12 parse_boolean(argv[0], config->focus_follows_mouse);
11 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 13 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
12} 14}
diff --git a/sway/commands/focus_wrapping.c b/sway/commands/focus_wrapping.c
index 0a9e0bf2..562ee4f9 100644
--- a/sway/commands/focus_wrapping.c
+++ b/sway/commands/focus_wrapping.c
@@ -1,6 +1,7 @@
1#include <strings.h> 1#include <strings.h>
2#include "sway/commands.h" 2#include "sway/commands.h"
3#include "sway/config.h" 3#include "sway/config.h"
4#include "util.h"
4 5
5struct cmd_results *cmd_focus_wrapping(int argc, char **argv) { 6struct cmd_results *cmd_focus_wrapping(int argc, char **argv) {
6 struct cmd_results *error = NULL; 7 struct cmd_results *error = NULL;
@@ -8,15 +9,12 @@ struct cmd_results *cmd_focus_wrapping(int argc, char **argv) {
8 return error; 9 return error;
9 } 10 }
10 11
11 if (strcasecmp(argv[0], "no") == 0) { 12 if (strcasecmp(argv[0], "force") == 0) {
12 config->focus_wrapping = WRAP_NO;
13 } else if (strcasecmp(argv[0], "yes") == 0) {
14 config->focus_wrapping = WRAP_YES;
15 } else if (strcasecmp(argv[0], "force") == 0) {
16 config->focus_wrapping = WRAP_FORCE; 13 config->focus_wrapping = WRAP_FORCE;
14 } else if (parse_boolean(argv[0], config->focus_wrapping == WRAP_YES)) {
15 config->focus_wrapping = WRAP_YES;
17 } else { 16 } else {
18 return cmd_results_new(CMD_INVALID, "focus_wrapping", 17 config->focus_wrapping = WRAP_NO;
19 "Expected 'focus_wrapping yes|no|force'");
20 } 18 }
21 19
22 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 20 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
diff --git a/sway/commands/force_focus_wrapping.c b/sway/commands/force_focus_wrapping.c
index bc1d067f..0892d9e9 100644
--- a/sway/commands/force_focus_wrapping.c
+++ b/sway/commands/force_focus_wrapping.c
@@ -1,6 +1,7 @@
1#include <strings.h> 1#include <strings.h>
2#include "sway/commands.h" 2#include "sway/commands.h"
3#include "sway/config.h" 3#include "sway/config.h"
4#include "util.h"
4 5
5struct cmd_results *cmd_force_focus_wrapping(int argc, char **argv) { 6struct cmd_results *cmd_force_focus_wrapping(int argc, char **argv) {
6 struct cmd_results *error = 7 struct cmd_results *error =
@@ -9,13 +10,10 @@ struct cmd_results *cmd_force_focus_wrapping(int argc, char **argv) {
9 return error; 10 return error;
10 } 11 }
11 12
12 if (strcasecmp(argv[0], "no") == 0) { 13 if (parse_boolean(argv[0], config->focus_wrapping == WRAP_FORCE)) {
13 config->focus_wrapping = WRAP_YES;
14 } else if (strcasecmp(argv[0], "yes") == 0) {
15 config->focus_wrapping = WRAP_FORCE; 14 config->focus_wrapping = WRAP_FORCE;
16 } else { 15 } else {
17 return cmd_results_new(CMD_INVALID, "force_focus_wrapping", 16 config->focus_wrapping = WRAP_YES;
18 "Expected 'force_focus_wrapping yes|no'");
19 } 17 }
20 18
21 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 19 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
diff --git a/sway/commands/fullscreen.c b/sway/commands/fullscreen.c
index 0b5beaa2..b423fd23 100644
--- a/sway/commands/fullscreen.c
+++ b/sway/commands/fullscreen.c
@@ -5,6 +5,7 @@
5#include "sway/tree/container.h" 5#include "sway/tree/container.h"
6#include "sway/tree/view.h" 6#include "sway/tree/view.h"
7#include "sway/tree/layout.h" 7#include "sway/tree/layout.h"
8#include "util.h"
8 9
9struct cmd_results *cmd_fullscreen(int argc, char **argv) { 10struct cmd_results *cmd_fullscreen(int argc, char **argv) {
10 struct cmd_results *error = NULL; 11 struct cmd_results *error = NULL;
@@ -18,17 +19,10 @@ struct cmd_results *cmd_fullscreen(int argc, char **argv) {
18 "Only views can fullscreen"); 19 "Only views can fullscreen");
19 } 20 }
20 struct sway_view *view = container->sway_view; 21 struct sway_view *view = container->sway_view;
21 bool wants_fullscreen; 22 bool wants_fullscreen = !view->is_fullscreen;
22 23
23 if (argc == 0 || strcmp(argv[0], "toggle") == 0) { 24 if (argc) {
24 wants_fullscreen = !view->is_fullscreen; 25 wants_fullscreen = parse_boolean(argv[0], view->is_fullscreen);
25 } else if (strcmp(argv[0], "enable") == 0) {
26 wants_fullscreen = true;
27 } else if (strcmp(argv[0], "disable") == 0) {
28 wants_fullscreen = false;
29 } else {
30 return cmd_results_new(CMD_INVALID, "fullscreen",
31 "Expected 'fullscreen' or 'fullscreen <enable|disable|toggle>'");
32 } 26 }
33 27
34 view_set_fullscreen(view, wants_fullscreen); 28 view_set_fullscreen(view, wants_fullscreen);
diff --git a/sway/commands/input/drag_lock.c b/sway/commands/input/drag_lock.c
index 9e32816f..f9ddeef2 100644
--- a/sway/commands/input/drag_lock.c
+++ b/sway/commands/input/drag_lock.c
@@ -3,6 +3,7 @@
3#include "sway/config.h" 3#include "sway/config.h"
4#include "sway/commands.h" 4#include "sway/commands.h"
5#include "sway/input/input-manager.h" 5#include "sway/input/input-manager.h"
6#include "util.h"
6 7
7struct cmd_results *input_cmd_drag_lock(int argc, char **argv) { 8struct cmd_results *input_cmd_drag_lock(int argc, char **argv) {
8 struct cmd_results *error = NULL; 9 struct cmd_results *error = NULL;
@@ -18,14 +19,10 @@ struct cmd_results *input_cmd_drag_lock(int argc, char **argv) {
18 struct input_config *new_config = 19 struct input_config *new_config =
19 new_input_config(current_input_config->identifier); 20 new_input_config(current_input_config->identifier);
20 21
21 if (strcasecmp(argv[0], "enabled") == 0) { 22 if (parse_boolean(argv[0], true)) {
22 new_config->drag_lock = LIBINPUT_CONFIG_DRAG_LOCK_ENABLED; 23 new_config->drag_lock = LIBINPUT_CONFIG_DRAG_LOCK_ENABLED;
23 } else if (strcasecmp(argv[0], "disabled") == 0) {
24 new_config->drag_lock = LIBINPUT_CONFIG_DRAG_LOCK_DISABLED;
25 } else { 24 } else {
26 free_input_config(new_config); 25 new_config->drag_lock = LIBINPUT_CONFIG_DRAG_LOCK_DISABLED;
27 return cmd_results_new(CMD_INVALID, "drag_lock",
28 "Expected 'drag_lock <enabled|disabled>'");
29 } 26 }
30 27
31 apply_input_config(new_config); 28 apply_input_config(new_config);
diff --git a/sway/commands/input/dwt.c b/sway/commands/input/dwt.c
index 73937507..15134268 100644
--- a/sway/commands/input/dwt.c
+++ b/sway/commands/input/dwt.c
@@ -3,6 +3,7 @@
3#include "sway/config.h" 3#include "sway/config.h"
4#include "sway/commands.h" 4#include "sway/commands.h"
5#include "sway/input/input-manager.h" 5#include "sway/input/input-manager.h"
6#include "util.h"
6 7
7struct cmd_results *input_cmd_dwt(int argc, char **argv) { 8struct cmd_results *input_cmd_dwt(int argc, char **argv) {
8 struct cmd_results *error = NULL; 9 struct cmd_results *error = NULL;
@@ -17,14 +18,10 @@ struct cmd_results *input_cmd_dwt(int argc, char **argv) {
17 struct input_config *new_config = 18 struct input_config *new_config =
18 new_input_config(current_input_config->identifier); 19 new_input_config(current_input_config->identifier);
19 20
20 if (strcasecmp(argv[0], "enabled") == 0) { 21 if (parse_boolean(argv[0], true)) {
21 new_config->dwt = LIBINPUT_CONFIG_DWT_ENABLED; 22 new_config->dwt = LIBINPUT_CONFIG_DWT_ENABLED;
22 } else if (strcasecmp(argv[0], "disabled") == 0) {
23 new_config->dwt = LIBINPUT_CONFIG_DWT_DISABLED;
24 } else { 23 } else {
25 free_input_config(new_config); 24 new_config->dwt = LIBINPUT_CONFIG_DWT_DISABLED;
26 return cmd_results_new(CMD_INVALID, "dwt",
27 "Expected 'dwt <enabled|disabled>'");
28 } 25 }
29 26
30 apply_input_config(new_config); 27 apply_input_config(new_config);
diff --git a/sway/commands/input/left_handed.c b/sway/commands/input/left_handed.c
index 769ce98c..e770043a 100644
--- a/sway/commands/input/left_handed.c
+++ b/sway/commands/input/left_handed.c
@@ -3,6 +3,7 @@
3#include "sway/config.h" 3#include "sway/config.h"
4#include "sway/commands.h" 4#include "sway/commands.h"
5#include "sway/input/input-manager.h" 5#include "sway/input/input-manager.h"
6#include "util.h"
6 7
7struct cmd_results *input_cmd_left_handed(int argc, char **argv) { 8struct cmd_results *input_cmd_left_handed(int argc, char **argv) {
8 struct cmd_results *error = NULL; 9 struct cmd_results *error = NULL;
@@ -18,15 +19,7 @@ struct cmd_results *input_cmd_left_handed(int argc, char **argv) {
18 struct input_config *new_config = 19 struct input_config *new_config =
19 new_input_config(current_input_config->identifier); 20 new_input_config(current_input_config->identifier);
20 21
21 if (strcasecmp(argv[0], "enabled") == 0) { 22 new_config->left_handed = parse_boolean(argv[0], true);
22 new_config->left_handed = 1;
23 } else if (strcasecmp(argv[0], "disabled") == 0) {
24 new_config->left_handed = 0;
25 } else {
26 free_input_config(new_config);
27 return cmd_results_new(CMD_INVALID, "left_handed",
28 "Expected 'left_handed <enabled|disabled>'");
29 }
30 23
31 apply_input_config(new_config); 24 apply_input_config(new_config);
32 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 25 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
diff --git a/sway/commands/input/middle_emulation.c b/sway/commands/input/middle_emulation.c
index 7ca01629..414d4d2b 100644
--- a/sway/commands/input/middle_emulation.c
+++ b/sway/commands/input/middle_emulation.c
@@ -3,6 +3,7 @@
3#include "sway/config.h" 3#include "sway/config.h"
4#include "sway/commands.h" 4#include "sway/commands.h"
5#include "sway/input/input-manager.h" 5#include "sway/input/input-manager.h"
6#include "util.h"
6 7
7struct cmd_results *input_cmd_middle_emulation(int argc, char **argv) { 8struct cmd_results *input_cmd_middle_emulation(int argc, char **argv) {
8 struct cmd_results *error = NULL; 9 struct cmd_results *error = NULL;
@@ -18,15 +19,11 @@ struct cmd_results *input_cmd_middle_emulation(int argc, char **argv) {
18 struct input_config *new_config = 19 struct input_config *new_config =
19 new_input_config(current_input_config->identifier); 20 new_input_config(current_input_config->identifier);
20 21
21 if (strcasecmp(argv[0], "enabled") == 0) { 22 if (parse_boolean(argv[0], true)) {
22 new_config->middle_emulation = LIBINPUT_CONFIG_MIDDLE_EMULATION_ENABLED; 23 new_config->middle_emulation = LIBINPUT_CONFIG_MIDDLE_EMULATION_ENABLED;
23 } else if (strcasecmp(argv[0], "disabled") == 0) { 24 } else {
24 new_config->middle_emulation = 25 new_config->middle_emulation =
25 LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED; 26 LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED;
26 } else {
27 free_input_config(new_config);
28 return cmd_results_new(CMD_INVALID, "middle_emulation",
29 "Expected 'middle_emulation <enabled|disabled>'");
30 } 27 }
31 28
32 apply_input_config(new_config); 29 apply_input_config(new_config);
diff --git a/sway/commands/input/natural_scroll.c b/sway/commands/input/natural_scroll.c
index 55236790..77c3ff00 100644
--- a/sway/commands/input/natural_scroll.c
+++ b/sway/commands/input/natural_scroll.c
@@ -3,6 +3,7 @@
3#include "sway/config.h" 3#include "sway/config.h"
4#include "sway/commands.h" 4#include "sway/commands.h"
5#include "sway/input/input-manager.h" 5#include "sway/input/input-manager.h"
6#include "util.h"
6 7
7struct cmd_results *input_cmd_natural_scroll(int argc, char **argv) { 8struct cmd_results *input_cmd_natural_scroll(int argc, char **argv) {
8 struct cmd_results *error = NULL; 9 struct cmd_results *error = NULL;
@@ -18,15 +19,7 @@ struct cmd_results *input_cmd_natural_scroll(int argc, char **argv) {
18 struct input_config *new_config = 19 struct input_config *new_config =
19 new_input_config(current_input_config->identifier); 20 new_input_config(current_input_config->identifier);
20 21
21 if (strcasecmp(argv[0], "enabled") == 0) { 22 new_config->natural_scroll = parse_boolean(argv[0], true);
22 new_config->natural_scroll = 1;
23 } else if (strcasecmp(argv[0], "disabled") == 0) {
24 new_config->natural_scroll = 0;
25 } else {
26 free_input_config(new_config);
27 return cmd_results_new(CMD_INVALID, "natural_scroll",
28 "Expected 'natural_scroll <enabled|disabled>'");
29 }
30 23
31 apply_input_config(new_config); 24 apply_input_config(new_config);
32 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 25 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
diff --git a/sway/commands/input/tap.c b/sway/commands/input/tap.c
index a8d1a10c..ac3b8237 100644
--- a/sway/commands/input/tap.c
+++ b/sway/commands/input/tap.c
@@ -4,6 +4,7 @@
4#include "sway/commands.h" 4#include "sway/commands.h"
5#include "sway/input/input-manager.h" 5#include "sway/input/input-manager.h"
6#include "log.h" 6#include "log.h"
7#include "util.h"
7 8
8struct cmd_results *input_cmd_tap(int argc, char **argv) { 9struct cmd_results *input_cmd_tap(int argc, char **argv) {
9 struct cmd_results *error = NULL; 10 struct cmd_results *error = NULL;
@@ -18,14 +19,10 @@ struct cmd_results *input_cmd_tap(int argc, char **argv) {
18 struct input_config *new_config = 19 struct input_config *new_config =
19 new_input_config(current_input_config->identifier); 20 new_input_config(current_input_config->identifier);
20 21
21 if (strcasecmp(argv[0], "enabled") == 0) { 22 if (parse_boolean(argv[0], true)) {
22 new_config->tap = LIBINPUT_CONFIG_TAP_ENABLED; 23 new_config->tap = LIBINPUT_CONFIG_TAP_ENABLED;
23 } else if (strcasecmp(argv[0], "disabled") == 0) {
24 new_config->tap = LIBINPUT_CONFIG_TAP_DISABLED;
25 } else { 24 } else {
26 free_input_config(new_config); 25 new_config->tap = LIBINPUT_CONFIG_TAP_DISABLED;
27 return cmd_results_new(CMD_INVALID, "tap",
28 "Expected 'tap <enabled|disabled>'");
29 } 26 }
30 27
31 wlr_log(WLR_DEBUG, "apply-tap for device: %s", 28 wlr_log(WLR_DEBUG, "apply-tap for device: %s",
diff --git a/sway/commands/output/dpms.c b/sway/commands/output/dpms.c
index 0959ea6b..3492061e 100644
--- a/sway/commands/output/dpms.c
+++ b/sway/commands/output/dpms.c
@@ -1,5 +1,6 @@
1#include "sway/commands.h" 1#include "sway/commands.h"
2#include "sway/config.h" 2#include "sway/config.h"
3#include "util.h"
3 4
4struct cmd_results *output_cmd_dpms(int argc, char **argv) { 5struct cmd_results *output_cmd_dpms(int argc, char **argv) {
5 if (!config->handler_context.output_config) { 6 if (!config->handler_context.output_config) {
@@ -9,13 +10,10 @@ struct cmd_results *output_cmd_dpms(int argc, char **argv) {
9 return cmd_results_new(CMD_INVALID, "output", "Missing dpms argument."); 10 return cmd_results_new(CMD_INVALID, "output", "Missing dpms argument.");
10 } 11 }
11 12
12 if (strcmp(*argv, "on") == 0) { 13 if (parse_boolean(argv[0], true)) {
13 config->handler_context.output_config->dpms_state = DPMS_ON; 14 config->handler_context.output_config->dpms_state = DPMS_ON;
14 } else if (strcmp(*argv, "off") == 0) {
15 config->handler_context.output_config->dpms_state = DPMS_OFF;
16 } else { 15 } else {
17 return cmd_results_new(CMD_INVALID, "output", 16 config->handler_context.output_config->dpms_state = DPMS_OFF;
18 "Invalid dpms state, valid states are on/off.");
19 } 17 }
20 18
21 config->handler_context.leftovers.argc = argc - 1; 19 config->handler_context.leftovers.argc = argc - 1;
diff --git a/sway/commands/show_marks.c b/sway/commands/show_marks.c
index c7fdc538..434a0e27 100644
--- a/sway/commands/show_marks.c
+++ b/sway/commands/show_marks.c
@@ -7,6 +7,7 @@
7#include "list.h" 7#include "list.h"
8#include "log.h" 8#include "log.h"
9#include "stringop.h" 9#include "stringop.h"
10#include "util.h"
10 11
11static void rebuild_marks_iterator(struct sway_container *con, void *data) { 12static void rebuild_marks_iterator(struct sway_container *con, void *data) {
12 if (con->type == C_VIEW) { 13 if (con->type == C_VIEW) {
@@ -20,14 +21,7 @@ struct cmd_results *cmd_show_marks(int argc, char **argv) {
20 return error; 21 return error;
21 } 22 }
22 23
23 if (strcmp(*argv, "yes") == 0) { 24 config->show_marks = parse_boolean(argv[0], config->show_marks);
24 config->show_marks = true;
25 } else if (strcmp(*argv, "no") == 0) {
26 config->show_marks = false;
27 } else {
28 return cmd_results_new(CMD_INVALID, "show_marks",
29 "Expected 'show_marks <yes|no>'");
30 }
31 25
32 if (config->show_marks) { 26 if (config->show_marks) {
33 container_for_each_descendant_dfs(&root_container, 27 container_for_each_descendant_dfs(&root_container,
diff --git a/sway/commands/swap.c b/sway/commands/swap.c
index 2fc88308..4e3a9cce 100644
--- a/sway/commands/swap.c
+++ b/sway/commands/swap.c
@@ -1,5 +1,6 @@
1#include <strings.h> 1#include <strings.h>
2#include <wlr/util/log.h> 2#include <wlr/util/log.h>
3#include "config.h"
3#include "sway/commands.h" 4#include "sway/commands.h"
4#include "sway/tree/arrange.h" 5#include "sway/tree/arrange.h"
5#include "sway/tree/layout.h" 6#include "sway/tree/layout.h"
@@ -14,10 +15,14 @@ static bool test_con_id(struct sway_container *container, void *con_id) {
14} 15}
15 16
16static bool test_id(struct sway_container *container, void *id) { 17static bool test_id(struct sway_container *container, void *id) {
18#ifdef HAVE_XWAYLAND
17 xcb_window_t *wid = id; 19 xcb_window_t *wid = id;
18 return (container->type == C_VIEW 20 return (container->type == C_VIEW
19 && container->sway_view->type == SWAY_VIEW_XWAYLAND 21 && container->sway_view->type == SWAY_VIEW_XWAYLAND
20 && container->sway_view->wlr_xwayland_surface->window_id == *wid); 22 && container->sway_view->wlr_xwayland_surface->window_id == *wid);
23#else
24 return false;
25#endif
21} 26}
22 27
23static bool test_mark(struct sway_container *container, void *mark) { 28static bool test_mark(struct sway_container *container, void *mark) {
@@ -43,8 +48,10 @@ struct cmd_results *cmd_swap(int argc, char **argv) {
43 48
44 char *value = join_args(argv + 3, argc - 3); 49 char *value = join_args(argv + 3, argc - 3);
45 if (strcasecmp(argv[2], "id") == 0) { 50 if (strcasecmp(argv[2], "id") == 0) {
51#ifdef HAVE_XWAYLAND
46 xcb_window_t id = strtol(value, NULL, 0); 52 xcb_window_t id = strtol(value, NULL, 0);
47 other = container_find(&root_container, test_id, (void *)&id); 53 other = container_find(&root_container, test_id, (void *)&id);
54#endif
48 } else if (strcasecmp(argv[2], "con_id") == 0) { 55 } else if (strcasecmp(argv[2], "con_id") == 0) {
49 size_t con_id = atoi(value); 56 size_t con_id = atoi(value);
50 other = container_find(&root_container, test_con_id, (void *)con_id); 57 other = container_find(&root_container, test_con_id, (void *)con_id);
diff --git a/sway/commands/urgent.c b/sway/commands/urgent.c
index d199858a..51c497c4 100644
--- a/sway/commands/urgent.c
+++ b/sway/commands/urgent.c
@@ -5,6 +5,7 @@
5#include "sway/tree/container.h" 5#include "sway/tree/container.h"
6#include "sway/tree/view.h" 6#include "sway/tree/view.h"
7#include "sway/tree/layout.h" 7#include "sway/tree/layout.h"
8#include "util.h"
8 9
9struct cmd_results *cmd_urgent(int argc, char **argv) { 10struct cmd_results *cmd_urgent(int argc, char **argv) {
10 struct cmd_results *error = NULL; 11 struct cmd_results *error = NULL;
@@ -19,17 +20,12 @@ struct cmd_results *cmd_urgent(int argc, char **argv) {
19 } 20 }
20 struct sway_view *view = container->sway_view; 21 struct sway_view *view = container->sway_view;
21 22
22 if (strcmp(argv[0], "enable") == 0) { 23 if (strcmp(argv[0], "allow") == 0) {
23 view_set_urgent(view, true);
24 } else if (strcmp(argv[0], "disable") == 0) {
25 view_set_urgent(view, false);
26 } else if (strcmp(argv[0], "allow") == 0) {
27 view->allow_request_urgent = true; 24 view->allow_request_urgent = true;
28 } else if (strcmp(argv[0], "deny") == 0) { 25 } else if (strcmp(argv[0], "deny") == 0) {
29 view->allow_request_urgent = false; 26 view->allow_request_urgent = false;
30 } else { 27 } else {
31 return cmd_results_new(CMD_INVALID, "urgent", 28 view_set_urgent(view, parse_boolean(argv[0], view_is_urgent(view)));
32 "Expected 'urgent <enable|disable|allow|deny>'");
33 } 29 }
34 30
35 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 31 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
diff --git a/sway/config.c b/sway/config.c
index ed624bfa..2afffab1 100644
--- a/sway/config.c
+++ b/sway/config.c
@@ -56,6 +56,12 @@ static void free_mode(struct sway_mode *mode) {
56 } 56 }
57 list_free(mode->keycode_bindings); 57 list_free(mode->keycode_bindings);
58 } 58 }
59 if (mode->mouse_bindings) {
60 for (i = 0; i < mode->mouse_bindings->length; i++) {
61 free_sway_binding(mode->mouse_bindings->items[i]);
62 }
63 list_free(mode->mouse_bindings);
64 }
59 free(mode); 65 free(mode);
60} 66}
61 67
@@ -87,7 +93,6 @@ void free_config(struct sway_config *config) {
87 } 93 }
88 list_free(config->cmd_queue); 94 list_free(config->cmd_queue);
89 list_free(config->workspace_outputs); 95 list_free(config->workspace_outputs);
90 list_free(config->pid_workspaces);
91 if (config->output_configs) { 96 if (config->output_configs) {
92 for (int i = 0; i < config->output_configs->length; i++) { 97 for (int i = 0; i < config->output_configs->length; i++) {
93 free_output_config(config->output_configs->items[i]); 98 free_output_config(config->output_configs->items[i]);
@@ -157,7 +162,6 @@ static void config_defaults(struct sway_config *config) {
157 if (!(config->modes = create_list())) goto cleanup; 162 if (!(config->modes = create_list())) goto cleanup;
158 if (!(config->bars = create_list())) goto cleanup; 163 if (!(config->bars = create_list())) goto cleanup;
159 if (!(config->workspace_outputs = create_list())) goto cleanup; 164 if (!(config->workspace_outputs = create_list())) goto cleanup;
160 if (!(config->pid_workspaces = create_list())) goto cleanup;
161 if (!(config->criteria = create_list())) goto cleanup; 165 if (!(config->criteria = create_list())) goto cleanup;
162 if (!(config->no_focus = create_list())) goto cleanup; 166 if (!(config->no_focus = create_list())) goto cleanup;
163 if (!(config->input_configs = create_list())) goto cleanup; 167 if (!(config->input_configs = create_list())) goto cleanup;
@@ -172,9 +176,11 @@ static void config_defaults(struct sway_config *config) {
172 strcpy(config->current_mode->name, "default"); 176 strcpy(config->current_mode->name, "default");
173 if (!(config->current_mode->keysym_bindings = create_list())) goto cleanup; 177 if (!(config->current_mode->keysym_bindings = create_list())) goto cleanup;
174 if (!(config->current_mode->keycode_bindings = create_list())) goto cleanup; 178 if (!(config->current_mode->keycode_bindings = create_list())) goto cleanup;
179 if (!(config->current_mode->mouse_bindings = create_list())) goto cleanup;
175 list_add(config->modes, config->current_mode); 180 list_add(config->modes, config->current_mode);
176 181
177 config->floating_mod = 0; 182 config->floating_mod = 0;
183 config->floating_mod_inverse = false;
178 config->dragging_key = BTN_LEFT; 184 config->dragging_key = BTN_LEFT;
179 config->resizing_key = BTN_RIGHT; 185 config->resizing_key = BTN_RIGHT;
180 186
diff --git a/sway/criteria.c b/sway/criteria.c
index c2e9c07e..39d300ea 100644
--- a/sway/criteria.c
+++ b/sway/criteria.c
@@ -10,6 +10,7 @@
10#include "stringop.h" 10#include "stringop.h"
11#include "list.h" 11#include "list.h"
12#include "log.h" 12#include "log.h"
13#include "config.h"
13 14
14bool criteria_is_empty(struct criteria *criteria) { 15bool criteria_is_empty(struct criteria *criteria) {
15 return !criteria->title 16 return !criteria->title
@@ -19,7 +20,9 @@ bool criteria_is_empty(struct criteria *criteria) {
19 && !criteria->instance 20 && !criteria->instance
20 && !criteria->con_mark 21 && !criteria->con_mark
21 && !criteria->con_id 22 && !criteria->con_id
23#ifdef HAVE_XWAYLAND
22 && !criteria->id 24 && !criteria->id
25#endif
23 && !criteria->window_role 26 && !criteria->window_role
24 && !criteria->window_type 27 && !criteria->window_type
25 && !criteria->floating 28 && !criteria->floating
@@ -127,12 +130,14 @@ static bool criteria_matches_view(struct criteria *criteria,
127 } 130 }
128 } 131 }
129 132
133#ifdef HAVE_XWAYLAND
130 if (criteria->id) { // X11 window ID 134 if (criteria->id) { // X11 window ID
131 uint32_t x11_window_id = view_get_x11_window_id(view); 135 uint32_t x11_window_id = view_get_x11_window_id(view);
132 if (!x11_window_id || x11_window_id != criteria->id) { 136 if (!x11_window_id || x11_window_id != criteria->id) {
133 return false; 137 return false;
134 } 138 }
135 } 139 }
140#endif
136 141
137 if (criteria->window_role) { 142 if (criteria->window_role) {
138 // TODO 143 // TODO
@@ -265,7 +270,9 @@ enum criteria_token {
265 T_CON_ID, 270 T_CON_ID,
266 T_CON_MARK, 271 T_CON_MARK,
267 T_FLOATING, 272 T_FLOATING,
273#ifdef HAVE_XWAYLAND
268 T_ID, 274 T_ID,
275#endif
269 T_INSTANCE, 276 T_INSTANCE,
270 T_SHELL, 277 T_SHELL,
271 T_TILING, 278 T_TILING,
@@ -287,8 +294,10 @@ static enum criteria_token token_from_name(char *name) {
287 return T_CON_ID; 294 return T_CON_ID;
288 } else if (strcmp(name, "con_mark") == 0) { 295 } else if (strcmp(name, "con_mark") == 0) {
289 return T_CON_MARK; 296 return T_CON_MARK;
297#ifdef HAVE_XWAYLAND
290 } else if (strcmp(name, "id") == 0) { 298 } else if (strcmp(name, "id") == 0) {
291 return T_ID; 299 return T_ID;
300#endif
292 } else if (strcmp(name, "instance") == 0) { 301 } else if (strcmp(name, "instance") == 0) {
293 return T_INSTANCE; 302 return T_INSTANCE;
294 } else if (strcmp(name, "shell") == 0) { 303 } else if (strcmp(name, "shell") == 0) {
@@ -355,7 +364,9 @@ static char *get_focused_prop(enum criteria_token token) {
355 case T_CON_ID: // These do not support __focused__ 364 case T_CON_ID: // These do not support __focused__
356 case T_CON_MARK: 365 case T_CON_MARK:
357 case T_FLOATING: 366 case T_FLOATING:
367#ifdef HAVE_XWAYLAND
358 case T_ID: 368 case T_ID:
369#endif
359 case T_TILING: 370 case T_TILING:
360 case T_URGENT: 371 case T_URGENT:
361 case T_WINDOW_TYPE: 372 case T_WINDOW_TYPE:
@@ -426,12 +437,14 @@ static bool parse_token(struct criteria *criteria, char *name, char *value) {
426 case T_WINDOW_TYPE: 437 case T_WINDOW_TYPE:
427 // TODO: This is a string but will be stored as an enum or integer 438 // TODO: This is a string but will be stored as an enum or integer
428 break; 439 break;
440#ifdef HAVE_XWAYLAND
429 case T_ID: 441 case T_ID:
430 criteria->id = strtoul(effective_value, &endptr, 10); 442 criteria->id = strtoul(effective_value, &endptr, 10);
431 if (*endptr != 0) { 443 if (*endptr != 0) {
432 error = strdup("The value for 'id' should be numeric"); 444 error = strdup("The value for 'id' should be numeric");
433 } 445 }
434 break; 446 break;
447#endif
435 case T_FLOATING: 448 case T_FLOATING:
436 criteria->floating = true; 449 criteria->floating = true;
437 break; 450 break;
diff --git a/sway/desktop/output.c b/sway/desktop/output.c
index cbd6ef86..1764b4e3 100644
--- a/sway/desktop/output.c
+++ b/sway/desktop/output.c
@@ -14,6 +14,7 @@
14#include <wlr/types/wlr_surface.h> 14#include <wlr/types/wlr_surface.h>
15#include <wlr/util/region.h> 15#include <wlr/util/region.h>
16#include "log.h" 16#include "log.h"
17#include "config.h"
17#include "sway/config.h" 18#include "sway/config.h"
18#include "sway/input/input-manager.h" 19#include "sway/input/input-manager.h"
19#include "sway/input/seat.h" 20#include "sway/input/seat.h"
@@ -128,7 +129,7 @@ void output_layer_for_each_surface(struct wl_list *layer_surfaces,
128 user_data); 129 user_data);
129 } 130 }
130} 131}
131 132#ifdef HAVE_XWAYLAND
132void output_unmanaged_for_each_surface(struct wl_list *unmanaged, 133void output_unmanaged_for_each_surface(struct wl_list *unmanaged,
133 struct sway_output *output, struct root_geometry *geo, 134 struct sway_output *output, struct root_geometry *geo,
134 wlr_surface_iterator_func_t iterator, void *user_data) { 135 wlr_surface_iterator_func_t iterator, void *user_data) {
@@ -143,7 +144,7 @@ void output_unmanaged_for_each_surface(struct wl_list *unmanaged,
143 iterator, user_data); 144 iterator, user_data);
144 } 145 }
145} 146}
146 147#endif
147void output_drag_icons_for_each_surface(struct wl_list *drag_icons, 148void output_drag_icons_for_each_surface(struct wl_list *drag_icons,
148 struct sway_output *output, struct root_geometry *geo, 149 struct sway_output *output, struct root_geometry *geo,
149 wlr_surface_iterator_func_t iterator, void *user_data) { 150 wlr_surface_iterator_func_t iterator, void *user_data) {
@@ -232,13 +233,13 @@ static void send_frame_done_layer(struct send_frame_done_data *data,
232 output_layer_for_each_surface(layer_surfaces, &data->root_geo, 233 output_layer_for_each_surface(layer_surfaces, &data->root_geo,
233 send_frame_done_iterator, data); 234 send_frame_done_iterator, data);
234} 235}
235 236#ifdef HAVE_XWAYLAND
236static void send_frame_done_unmanaged(struct send_frame_done_data *data, 237static void send_frame_done_unmanaged(struct send_frame_done_data *data,
237 struct wl_list *unmanaged) { 238 struct wl_list *unmanaged) {
238 output_unmanaged_for_each_surface(unmanaged, data->output, &data->root_geo, 239 output_unmanaged_for_each_surface(unmanaged, data->output, &data->root_geo,
239 send_frame_done_iterator, data); 240 send_frame_done_iterator, data);
240} 241}
241 242#endif
242static void send_frame_done_drag_icons(struct send_frame_done_data *data, 243static void send_frame_done_drag_icons(struct send_frame_done_data *data,
243 struct wl_list *drag_icons) { 244 struct wl_list *drag_icons) {
244 output_drag_icons_for_each_surface(drag_icons, data->output, &data->root_geo, 245 output_drag_icons_for_each_surface(drag_icons, data->output, &data->root_geo,
@@ -280,11 +281,12 @@ static void send_frame_done(struct sway_output *output, struct timespec *when) {
280 if (workspace->current.ws_fullscreen) { 281 if (workspace->current.ws_fullscreen) {
281 send_frame_done_container_iterator( 282 send_frame_done_container_iterator(
282 workspace->current.ws_fullscreen->swayc, &data); 283 workspace->current.ws_fullscreen->swayc, &data);
283 284#ifdef HAVE_XWAYLAND
284 if (workspace->current.ws_fullscreen->type == SWAY_VIEW_XWAYLAND) { 285 if (workspace->current.ws_fullscreen->type == SWAY_VIEW_XWAYLAND) {
285 send_frame_done_unmanaged(&data, 286 send_frame_done_unmanaged(&data,
286 &root_container.sway_root->xwayland_unmanaged); 287 &root_container.sway_root->xwayland_unmanaged);
287 } 288 }
289#endif
288 } else { 290 } else {
289 send_frame_done_layer(&data, 291 send_frame_done_layer(&data,
290 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND]); 292 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND]);
@@ -294,8 +296,10 @@ static void send_frame_done(struct sway_output *output, struct timespec *when) {
294 send_frame_done_container(&data, workspace); 296 send_frame_done_container(&data, workspace);
295 send_frame_done_container(&data, workspace->sway_workspace->floating); 297 send_frame_done_container(&data, workspace->sway_workspace->floating);
296 298
299#ifdef HAVE_XWAYLAND
297 send_frame_done_unmanaged(&data, 300 send_frame_done_unmanaged(&data,
298 &root_container.sway_root->xwayland_unmanaged); 301 &root_container.sway_root->xwayland_unmanaged);
302#endif
299 send_frame_done_layer(&data, 303 send_frame_done_layer(&data,
300 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]); 304 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]);
301 } 305 }
diff --git a/sway/desktop/render.c b/sway/desktop/render.c
index d6c3fa8c..15c5b94c 100644
--- a/sway/desktop/render.c
+++ b/sway/desktop/render.c
@@ -14,6 +14,7 @@
14#include <wlr/types/wlr_surface.h> 14#include <wlr/types/wlr_surface.h>
15#include <wlr/util/region.h> 15#include <wlr/util/region.h>
16#include "log.h" 16#include "log.h"
17#include "config.h"
17#include "sway/config.h" 18#include "sway/config.h"
18#include "sway/debug.h" 19#include "sway/debug.h"
19#include "sway/input/input-manager.h" 20#include "sway/input/input-manager.h"
@@ -132,7 +133,7 @@ static void render_layer(struct sway_output *output,
132 output_layer_for_each_surface(layer_surfaces, &data.root_geo, 133 output_layer_for_each_surface(layer_surfaces, &data.root_geo,
133 render_surface_iterator, &data); 134 render_surface_iterator, &data);
134} 135}
135 136#ifdef HAVE_XWAYLAND
136static void render_unmanaged(struct sway_output *output, 137static void render_unmanaged(struct sway_output *output,
137 pixman_region32_t *damage, struct wl_list *unmanaged) { 138 pixman_region32_t *damage, struct wl_list *unmanaged) {
138 struct render_data data = { 139 struct render_data data = {
@@ -143,7 +144,7 @@ static void render_unmanaged(struct sway_output *output,
143 output_unmanaged_for_each_surface(unmanaged, output, &data.root_geo, 144 output_unmanaged_for_each_surface(unmanaged, output, &data.root_geo,
144 render_surface_iterator, &data); 145 render_surface_iterator, &data);
145} 146}
146 147#endif
147static void render_drag_icons(struct sway_output *output, 148static void render_drag_icons(struct sway_output *output,
148 pixman_region32_t *damage, struct wl_list *drag_icons) { 149 pixman_region32_t *damage, struct wl_list *drag_icons) {
149 struct render_data data = { 150 struct render_data data = {
@@ -857,11 +858,12 @@ void output_render(struct sway_output *output, struct timespec *when,
857 } else { 858 } else {
858 render_view_surfaces(fullscreen_view, output, damage, 1.0f); 859 render_view_surfaces(fullscreen_view, output, damage, 1.0f);
859 } 860 }
860 861#ifdef HAVE_XWAYLAND
861 if (fullscreen_view->type == SWAY_VIEW_XWAYLAND) { 862 if (fullscreen_view->type == SWAY_VIEW_XWAYLAND) {
862 render_unmanaged(output, damage, 863 render_unmanaged(output, damage,
863 &root_container.sway_root->xwayland_unmanaged); 864 &root_container.sway_root->xwayland_unmanaged);
864 } 865 }
866#endif
865 } else { 867 } else {
866 float clear_color[] = {0.25f, 0.25f, 0.25f, 1.0f}; 868 float clear_color[] = {0.25f, 0.25f, 0.25f, 1.0f};
867 869
@@ -879,9 +881,10 @@ void output_render(struct sway_output *output, struct timespec *when,
879 881
880 render_container(output, damage, workspace, workspace->current.focused); 882 render_container(output, damage, workspace, workspace->current.focused);
881 render_floating(output, damage); 883 render_floating(output, damage);
882 884#ifdef HAVE_XWAYLAND
883 render_unmanaged(output, damage, 885 render_unmanaged(output, damage,
884 &root_container.sway_root->xwayland_unmanaged); 886 &root_container.sway_root->xwayland_unmanaged);
887#endif
885 render_layer(output, damage, 888 render_layer(output, damage,
886 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]); 889 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]);
887 } 890 }
diff --git a/sway/desktop/xdg_shell.c b/sway/desktop/xdg_shell.c
index 62c3abc8..f3e4fef8 100644
--- a/sway/desktop/xdg_shell.c
+++ b/sway/desktop/xdg_shell.c
@@ -418,9 +418,6 @@ void handle_xdg_shell_surface(struct wl_listener *listener, void *data) {
418 view_init(&xdg_shell_view->view, SWAY_VIEW_XDG_SHELL, &view_impl); 418 view_init(&xdg_shell_view->view, SWAY_VIEW_XDG_SHELL, &view_impl);
419 xdg_shell_view->view.wlr_xdg_surface = xdg_surface; 419 xdg_shell_view->view.wlr_xdg_surface = xdg_surface;
420 420
421 // TODO:
422 // - Look up pid and open on appropriate workspace
423
424 xdg_shell_view->map.notify = handle_map; 421 xdg_shell_view->map.notify = handle_map;
425 wl_signal_add(&xdg_surface->events.map, &xdg_shell_view->map); 422 wl_signal_add(&xdg_surface->events.map, &xdg_shell_view->map);
426 423
diff --git a/sway/desktop/xdg_shell_v6.c b/sway/desktop/xdg_shell_v6.c
index 7fb85410..46fd4769 100644
--- a/sway/desktop/xdg_shell_v6.c
+++ b/sway/desktop/xdg_shell_v6.c
@@ -409,9 +409,6 @@ void handle_xdg_shell_v6_surface(struct wl_listener *listener, void *data) {
409 view_init(&xdg_shell_v6_view->view, SWAY_VIEW_XDG_SHELL_V6, &view_impl); 409 view_init(&xdg_shell_v6_view->view, SWAY_VIEW_XDG_SHELL_V6, &view_impl);
410 xdg_shell_v6_view->view.wlr_xdg_surface_v6 = xdg_surface; 410 xdg_shell_v6_view->view.wlr_xdg_surface_v6 = xdg_surface;
411 411
412 // TODO:
413 // - Look up pid and open on appropriate workspace
414
415 xdg_shell_v6_view->map.notify = handle_map; 412 xdg_shell_v6_view->map.notify = handle_map;
416 wl_signal_add(&xdg_surface->events.map, &xdg_shell_v6_view->map); 413 wl_signal_add(&xdg_surface->events.map, &xdg_shell_v6_view->map);
417 414
diff --git a/sway/desktop/xwayland.c b/sway/desktop/xwayland.c
index 2546168b..65d4fcd4 100644
--- a/sway/desktop/xwayland.c
+++ b/sway/desktop/xwayland.c
@@ -514,9 +514,6 @@ void handle_xwayland_surface(struct wl_listener *listener, void *data) {
514 view_init(&xwayland_view->view, SWAY_VIEW_XWAYLAND, &view_impl); 514 view_init(&xwayland_view->view, SWAY_VIEW_XWAYLAND, &view_impl);
515 xwayland_view->view.wlr_xwayland_surface = xsurface; 515 xwayland_view->view.wlr_xwayland_surface = xsurface;
516 516
517 // TODO:
518 // - Look up pid and open on appropriate workspace
519
520 wl_signal_add(&xsurface->events.destroy, &xwayland_view->destroy); 517 wl_signal_add(&xsurface->events.destroy, &xwayland_view->destroy);
521 xwayland_view->destroy.notify = handle_destroy; 518 xwayland_view->destroy.notify = handle_destroy;
522 519
diff --git a/sway/input/cursor.c b/sway/input/cursor.c
index 65d04cac..02bd2239 100644
--- a/sway/input/cursor.c
+++ b/sway/input/cursor.c
@@ -11,6 +11,7 @@
11#include <wlr/types/wlr_idle.h> 11#include <wlr/types/wlr_idle.h>
12#include "list.h" 12#include "list.h"
13#include "log.h" 13#include "log.h"
14#include "config.h"
14#include "sway/desktop.h" 15#include "sway/desktop.h"
15#include "sway/desktop/transaction.h" 16#include "sway/desktop/transaction.h"
16#include "sway/input/cursor.h" 17#include "sway/input/cursor.h"
@@ -54,6 +55,7 @@ static struct sway_container *container_at_coords(
54 struct sway_seat *seat, double lx, double ly, 55 struct sway_seat *seat, double lx, double ly,
55 struct wlr_surface **surface, double *sx, double *sy) { 56 struct wlr_surface **surface, double *sx, double *sy) {
56 // check for unmanaged views first 57 // check for unmanaged views first
58#ifdef HAVE_XWAYLAND
57 struct wl_list *unmanaged = &root_container.sway_root->xwayland_unmanaged; 59 struct wl_list *unmanaged = &root_container.sway_root->xwayland_unmanaged;
58 struct sway_xwayland_unmanaged *unmanaged_surface; 60 struct sway_xwayland_unmanaged *unmanaged_surface;
59 wl_list_for_each_reverse(unmanaged_surface, unmanaged, link) { 61 wl_list_for_each_reverse(unmanaged_surface, unmanaged, link) {
@@ -69,7 +71,7 @@ static struct sway_container *container_at_coords(
69 return NULL; 71 return NULL;
70 } 72 }
71 } 73 }
72 74#endif
73 // find the output the cursor is on 75 // find the output the cursor is on
74 struct wlr_output_layout *output_layout = 76 struct wlr_output_layout *output_layout =
75 root_container.sway_root->output_layout; 77 root_container.sway_root->output_layout;
@@ -449,17 +451,25 @@ static void dispatch_cursor_button_floating(struct sway_cursor *cursor,
449 bool over_title = edge == WLR_EDGE_NONE && !surface; 451 bool over_title = edge == WLR_EDGE_NONE && !surface;
450 452
451 // Check for beginning move 453 // Check for beginning move
452 if (button == BTN_LEFT && state == WLR_BUTTON_PRESSED && 454 uint32_t btn_move = config->floating_mod_inverse ? BTN_RIGHT : BTN_LEFT;
455 if (button == btn_move && state == WLR_BUTTON_PRESSED &&
453 (mod_pressed || over_title)) { 456 (mod_pressed || over_title)) {
454 seat_begin_move(seat, cont, BTN_LEFT); 457 seat_begin_move(seat, cont, button);
455 return; 458 return;
456 } 459 }
457 460
458 // Check for beginning resize 461 // Check for beginning resize
459 bool resizing_via_border = button == BTN_LEFT && edge != WLR_EDGE_NONE; 462 bool resizing_via_border = button == BTN_LEFT && edge != WLR_EDGE_NONE;
460 bool resizing_via_mod = button == BTN_RIGHT && mod_pressed; 463 uint32_t btn_resize = config->floating_mod_inverse ? BTN_LEFT : BTN_RIGHT;
464 bool resizing_via_mod = button == btn_resize && mod_pressed;
461 if ((resizing_via_border || resizing_via_mod) && 465 if ((resizing_via_border || resizing_via_mod) &&
462 state == WLR_BUTTON_PRESSED) { 466 state == WLR_BUTTON_PRESSED) {
467 if (edge == WLR_EDGE_NONE) {
468 edge |= cursor->cursor->x > cont->x + cont->width / 2 ?
469 WLR_EDGE_RIGHT : WLR_EDGE_LEFT;
470 edge |= cursor->cursor->y > cont->y + cont->height / 2 ?
471 WLR_EDGE_BOTTOM : WLR_EDGE_TOP;
472 }
463 seat_begin_resize(seat, cont, button, edge); 473 seat_begin_resize(seat, cont, button, edge);
464 return; 474 return;
465 } 475 }
@@ -469,6 +479,83 @@ static void dispatch_cursor_button_floating(struct sway_cursor *cursor,
469 seat_pointer_notify_button(seat, time_msec, button, state); 479 seat_pointer_notify_button(seat, time_msec, button, state);
470} 480}
471 481
482/**
483 * Remove a button (and duplicates) to the sorted list of currently pressed buttons
484 */
485static void state_erase_button(struct sway_cursor *cursor, uint32_t button) {
486 size_t j = 0;
487 for (size_t i = 0; i < cursor->pressed_button_count; ++i) {
488 if (i > j) {
489 cursor->pressed_buttons[j] = cursor->pressed_buttons[i];
490 }
491 if (cursor->pressed_buttons[i] != button) {
492 ++j;
493 }
494 }
495 while (cursor->pressed_button_count > j) {
496 --cursor->pressed_button_count;
497 cursor->pressed_buttons[cursor->pressed_button_count] = 0;
498 }
499}
500
501/**
502 * Add a button to the sorted list of currently pressed buttons, if there
503 * is space.
504 */
505static void state_add_button(struct sway_cursor *cursor, uint32_t button) {
506 if (cursor->pressed_button_count >= SWAY_CURSOR_PRESSED_BUTTONS_CAP) {
507 return;
508 }
509 size_t i = 0;
510 while (i < cursor->pressed_button_count && cursor->pressed_buttons[i] < button) {
511 ++i;
512 }
513 size_t j = cursor->pressed_button_count;
514 while (j > i) {
515 cursor->pressed_buttons[j] = cursor->pressed_buttons[j - 1];
516 --j;
517 }
518 cursor->pressed_buttons[i] = button;
519 cursor->pressed_button_count++;
520}
521
522/**
523 * Return the mouse binding which matches modifier, click location, release,
524 * and pressed button state, otherwise return null.
525 */
526static struct sway_binding* get_active_mouse_binding(const struct sway_cursor *cursor,
527 list_t *bindings, uint32_t modifiers, bool release, bool on_titlebar,
528 bool on_border, bool on_content) {
529 uint32_t click_region = (on_titlebar ? BINDING_TITLEBAR : 0) |
530 (on_border ? BINDING_BORDER : 0) |
531 (on_content ? BINDING_CONTENTS : 0);
532
533 for (int i = 0; i < bindings->length; ++i) {
534 struct sway_binding *binding = bindings->items[i];
535 if (modifiers ^ binding->modifiers ||
536 cursor->pressed_button_count != (size_t)binding->keys->length ||
537 release != (binding->flags & BINDING_RELEASE) ||
538 !(click_region & binding->flags)) {
539 continue;
540 }
541
542 bool match = true;
543 for (size_t j = 0; j < cursor->pressed_button_count; j++) {
544 uint32_t key = *(uint32_t *)binding->keys->items[j];
545 if (key != cursor->pressed_buttons[j]) {
546 match = false;
547 break;
548 }
549 }
550 if (!match) {
551 continue;
552 }
553
554 return binding;
555 }
556 return NULL;
557}
558
472void dispatch_cursor_button(struct sway_cursor *cursor, 559void dispatch_cursor_button(struct sway_cursor *cursor,
473 uint32_t time_msec, uint32_t button, enum wlr_button_state state) { 560 uint32_t time_msec, uint32_t button, enum wlr_button_state state) {
474 if (cursor->seat->operation != OP_NONE && 561 if (cursor->seat->operation != OP_NONE &&
@@ -485,6 +572,31 @@ void dispatch_cursor_button(struct sway_cursor *cursor,
485 double sx, sy; 572 double sx, sy;
486 struct sway_container *cont = container_at_coords(cursor->seat, 573 struct sway_container *cont = container_at_coords(cursor->seat,
487 cursor->cursor->x, cursor->cursor->y, &surface, &sx, &sy); 574 cursor->cursor->x, cursor->cursor->y, &surface, &sx, &sy);
575
576 // Handle mouse bindings
577 bool on_border = cont && (find_resize_edge(cont, cursor) != WLR_EDGE_NONE);
578 bool on_contents = !on_border && surface;
579 bool on_titlebar = !on_border && !surface;
580 struct wlr_keyboard *keyboard = wlr_seat_get_keyboard(cursor->seat->wlr_seat);
581 uint32_t modifiers = keyboard ? wlr_keyboard_get_modifiers(keyboard) : 0;
582
583 struct sway_binding *binding = NULL;
584 if (state == WLR_BUTTON_PRESSED) {
585 state_add_button(cursor, button);
586 binding = get_active_mouse_binding(cursor,
587 config->current_mode->mouse_bindings, modifiers, false,
588 on_titlebar, on_border, on_contents);
589 } else {
590 binding = get_active_mouse_binding(cursor,
591 config->current_mode->mouse_bindings, modifiers, true,
592 on_titlebar, on_border, on_contents);
593 state_erase_button(cursor, button);
594 }
595 if (binding) {
596 seat_execute_command(cursor->seat, binding);
597 // TODO: do we want to pass on the event?
598 }
599
488 if (surface && wlr_surface_is_layer_surface(surface)) { 600 if (surface && wlr_surface_is_layer_surface(surface)) {
489 struct wlr_layer_surface *layer = 601 struct wlr_layer_surface *layer =
490 wlr_layer_surface_from_wlr_surface(surface); 602 wlr_layer_surface_from_wlr_surface(surface);
diff --git a/sway/input/keyboard.c b/sway/input/keyboard.c
index ede38519..49241db8 100644
--- a/sway/input/keyboard.c
+++ b/sway/input/keyboard.c
@@ -3,11 +3,11 @@
3#include <wlr/backend/multi.h> 3#include <wlr/backend/multi.h>
4#include <wlr/backend/session.h> 4#include <wlr/backend/session.h>
5#include <wlr/types/wlr_idle.h> 5#include <wlr/types/wlr_idle.h>
6#include "sway/commands.h"
6#include "sway/desktop/transaction.h" 7#include "sway/desktop/transaction.h"
7#include "sway/input/seat.h"
8#include "sway/input/keyboard.h"
9#include "sway/input/input-manager.h" 8#include "sway/input/input-manager.h"
10#include "sway/commands.h" 9#include "sway/input/keyboard.h"
10#include "sway/input/seat.h"
11#include "log.h" 11#include "log.h"
12 12
13/** 13/**
@@ -88,11 +88,13 @@ static void get_active_binding(const struct sway_shortcut_state *state,
88 uint32_t modifiers, bool release, bool locked) { 88 uint32_t modifiers, bool release, bool locked) {
89 for (int i = 0; i < bindings->length; ++i) { 89 for (int i = 0; i < bindings->length; ++i) {
90 struct sway_binding *binding = bindings->items[i]; 90 struct sway_binding *binding = bindings->items[i];
91 bool binding_locked = binding->flags & BINDING_LOCKED;
92 bool binding_release = binding->flags & BINDING_RELEASE;
91 93
92 if (modifiers ^ binding->modifiers || 94 if (modifiers ^ binding->modifiers ||
93 state->npressed != (size_t)binding->keys->length || 95 state->npressed != (size_t)binding->keys->length ||
94 locked > binding->locked || 96 release != binding_release ||
95 release != binding->release) { 97 locked > binding_locked) {
96 continue; 98 continue;
97 } 99 }
98 100
@@ -119,23 +121,6 @@ static void get_active_binding(const struct sway_shortcut_state *state,
119} 121}
120 122
121/** 123/**
122 * Execute the command associated to a binding
123 */
124static void keyboard_execute_command(struct sway_keyboard *keyboard,
125 struct sway_binding *binding) {
126 wlr_log(WLR_DEBUG, "running command for binding: %s",
127 binding->command);
128 config->handler_context.seat = keyboard->seat_device->sway_seat;
129 struct cmd_results *results = execute_command(binding->command, NULL);
130 transaction_commit_dirty();
131 if (results->status != CMD_SUCCESS) {
132 wlr_log(WLR_DEBUG, "could not run command for binding: %s (%s)",
133 binding->command, results->error);
134 }
135 free_cmd_results(results);
136}
137
138/**
139 * Execute a built-in, hardcoded compositor binding. These are triggered from a 124 * Execute a built-in, hardcoded compositor binding. These are triggered from a
140 * single keysym. 125 * single keysym.
141 * 126 *
@@ -211,12 +196,13 @@ static size_t keyboard_keysyms_raw(struct sway_keyboard *keyboard,
211static void handle_keyboard_key(struct wl_listener *listener, void *data) { 196static void handle_keyboard_key(struct wl_listener *listener, void *data) {
212 struct sway_keyboard *keyboard = 197 struct sway_keyboard *keyboard =
213 wl_container_of(listener, keyboard, keyboard_key); 198 wl_container_of(listener, keyboard, keyboard_key);
214 struct wlr_seat *wlr_seat = keyboard->seat_device->sway_seat->wlr_seat; 199 struct sway_seat* seat = keyboard->seat_device->sway_seat;
200 struct wlr_seat *wlr_seat = seat->wlr_seat;
215 struct wlr_input_device *wlr_device = 201 struct wlr_input_device *wlr_device =
216 keyboard->seat_device->input_device->wlr_device; 202 keyboard->seat_device->input_device->wlr_device;
217 wlr_idle_notify_activity(keyboard->seat_device->sway_seat->input->server->idle, wlr_seat); 203 wlr_idle_notify_activity(seat->input->server->idle, wlr_seat);
218 struct wlr_event_keyboard_key *event = data; 204 struct wlr_event_keyboard_key *event = data;
219 bool input_inhibited = keyboard->seat_device->sway_seat->exclusive_client != NULL; 205 bool input_inhibited = seat->exclusive_client != NULL;
220 206
221 // Identify new keycode, raw keysym(s), and translated keysym(s) 207 // Identify new keycode, raw keysym(s), and translated keysym(s)
222 xkb_keycode_t keycode = event->keycode + 8; 208 xkb_keycode_t keycode = event->keycode + 8;
@@ -266,7 +252,7 @@ static void handle_keyboard_key(struct wl_listener *listener, void *data) {
266 // Execute stored release binding once no longer active 252 // Execute stored release binding once no longer active
267 if (keyboard->held_binding && binding_released != keyboard->held_binding && 253 if (keyboard->held_binding && binding_released != keyboard->held_binding &&
268 event->state == WLR_KEY_RELEASED) { 254 event->state == WLR_KEY_RELEASED) {
269 keyboard_execute_command(keyboard, keyboard->held_binding); 255 seat_execute_command(seat, keyboard->held_binding);
270 handled = true; 256 handled = true;
271 } 257 }
272 if (binding_released != keyboard->held_binding) { 258 if (binding_released != keyboard->held_binding) {
@@ -290,7 +276,7 @@ static void handle_keyboard_key(struct wl_listener *listener, void *data) {
290 raw_modifiers, false, input_inhibited); 276 raw_modifiers, false, input_inhibited);
291 277
292 if (binding_pressed) { 278 if (binding_pressed) {
293 keyboard_execute_command(keyboard, binding_pressed); 279 seat_execute_command(seat, binding_pressed);
294 handled = true; 280 handled = true;
295 } 281 }
296 } 282 }
@@ -312,6 +298,8 @@ static void handle_keyboard_key(struct wl_listener *listener, void *data) {
312 wlr_seat_keyboard_notify_key(wlr_seat, event->time_msec, 298 wlr_seat_keyboard_notify_key(wlr_seat, event->time_msec,
313 event->keycode, event->state); 299 event->keycode, event->state);
314 } 300 }
301
302 transaction_commit_dirty();
315} 303}
316 304
317static void handle_keyboard_modifiers(struct wl_listener *listener, 305static void handle_keyboard_modifiers(struct wl_listener *listener,
diff --git a/sway/input/seat.c b/sway/input/seat.c
index fc9e54b6..8698d69e 100644
--- a/sway/input/seat.c
+++ b/sway/input/seat.c
@@ -12,6 +12,7 @@
12#include <wlr/types/wlr_output_layout.h> 12#include <wlr/types/wlr_output_layout.h>
13#include <wlr/types/wlr_xcursor_manager.h> 13#include <wlr/types/wlr_xcursor_manager.h>
14#include "log.h" 14#include "log.h"
15#include "config.h"
15#include "sway/debug.h" 16#include "sway/debug.h"
16#include "sway/desktop.h" 17#include "sway/desktop.h"
17#include "sway/input/cursor.h" 18#include "sway/input/cursor.h"
@@ -103,11 +104,13 @@ static void seat_send_focus(struct sway_container *con,
103 104
104 if (con->type == C_VIEW 105 if (con->type == C_VIEW
105 && seat_is_input_allowed(seat, con->sway_view->surface)) { 106 && seat_is_input_allowed(seat, con->sway_view->surface)) {
107#ifdef HAVE_XWAYLAND
106 if (con->sway_view->type == SWAY_VIEW_XWAYLAND) { 108 if (con->sway_view->type == SWAY_VIEW_XWAYLAND) {
107 struct wlr_xwayland *xwayland = 109 struct wlr_xwayland *xwayland =
108 seat->input->server->xwayland.wlr_xwayland; 110 seat->input->server->xwayland.wlr_xwayland;
109 wlr_xwayland_set_seat(xwayland, seat->wlr_seat); 111 wlr_xwayland_set_seat(xwayland, seat->wlr_seat);
110 } 112 }
113#endif
111 struct wlr_keyboard *keyboard = wlr_seat_get_keyboard(seat->wlr_seat); 114 struct wlr_keyboard *keyboard = wlr_seat_get_keyboard(seat->wlr_seat);
112 if (keyboard) { 115 if (keyboard) {
113 wlr_seat_keyboard_notify_enter(seat->wlr_seat, 116 wlr_seat_keyboard_notify_enter(seat->wlr_seat,
@@ -181,6 +184,7 @@ static void handle_seat_container_destroy(struct wl_listener *listener,
181 bool set_focus = 184 bool set_focus =
182 focus != NULL && 185 focus != NULL &&
183 (focus == con || container_has_child(con, focus)) && 186 (focus == con || container_has_child(con, focus)) &&
187 con->parent && con->parent->children->length > 1 &&
184 con->type != C_WORKSPACE; 188 con->type != C_WORKSPACE;
185 189
186 seat_container_destroy(seat_con); 190 seat_container_destroy(seat_con);
diff --git a/sway/meson.build b/sway/meson.build
index 30c848e2..649a3ac2 100644
--- a/sway/meson.build
+++ b/sway/meson.build
@@ -18,7 +18,6 @@ sway_sources = files(
18 'desktop/transaction.c', 18 'desktop/transaction.c',
19 'desktop/xdg_shell_v6.c', 19 'desktop/xdg_shell_v6.c',
20 'desktop/xdg_shell.c', 20 'desktop/xdg_shell.c',
21 'desktop/xwayland.c',
22 21
23 'input/input-manager.c', 22 'input/input-manager.c',
24 'input/seat.c', 23 'input/seat.c',
@@ -152,6 +151,10 @@ sway_sources = files(
152 'tree/output.c', 151 'tree/output.c',
153) 152)
154 153
154if get_option('enable-xwayland')
155 sway_sources += 'desktop/xwayland.c'
156endif
157
155sway_deps = [ 158sway_deps = [
156 cairo, 159 cairo,
157 gdk_pixbuf, 160 gdk_pixbuf,
diff --git a/sway/server.c b/sway/server.c
index 89dfbf8c..10ca9614 100644
--- a/sway/server.c
+++ b/sway/server.c
@@ -25,7 +25,10 @@
25#include "sway/input/input-manager.h" 25#include "sway/input/input-manager.h"
26#include "sway/server.h" 26#include "sway/server.h"
27#include "sway/tree/layout.h" 27#include "sway/tree/layout.h"
28#include "config.h"
29#ifdef HAVE_XWAYLAND
28#include "sway/xwayland.h" 30#include "sway/xwayland.h"
31#endif
29 32
30bool server_privileged_prepare(struct sway_server *server) { 33bool server_privileged_prepare(struct sway_server *server) {
31 wlr_log(WLR_DEBUG, "Preparing Wayland server initialization"); 34 wlr_log(WLR_DEBUG, "Preparing Wayland server initialization");
@@ -81,6 +84,7 @@ bool server_init(struct sway_server *server) {
81 server->xdg_shell_surface.notify = handle_xdg_shell_surface; 84 server->xdg_shell_surface.notify = handle_xdg_shell_surface;
82 85
83 // TODO make xwayland optional 86 // TODO make xwayland optional
87#ifdef HAVE_XWAYLAND
84 server->xwayland.wlr_xwayland = 88 server->xwayland.wlr_xwayland =
85 wlr_xwayland_create(server->wl_display, server->compositor, true); 89 wlr_xwayland_create(server->wl_display, server->compositor, true);
86 wl_signal_add(&server->xwayland.wlr_xwayland->events.new_surface, 90 wl_signal_add(&server->xwayland.wlr_xwayland->events.new_surface,
@@ -101,6 +105,7 @@ bool server_init(struct sway_server *server) {
101 image->width * 4, image->width, image->height, image->hotspot_x, 105 image->width * 4, image->width, image->height, image->hotspot_x,
102 image->hotspot_y); 106 image->hotspot_y);
103 } 107 }
108#endif
104 109
105 // TODO: Integration with sway borders 110 // TODO: Integration with sway borders
106 struct wlr_server_decoration_manager *deco_manager = 111 struct wlr_server_decoration_manager *deco_manager =
diff --git a/sway/tree/container.c b/sway/tree/container.c
index 4f743c40..b56b4e87 100644
--- a/sway/tree/container.c
+++ b/sway/tree/container.c
@@ -527,10 +527,12 @@ static struct sway_container *container_at_view(struct sway_container *swayc,
527 double _sx, _sy; 527 double _sx, _sy;
528 struct wlr_surface *_surface = NULL; 528 struct wlr_surface *_surface = NULL;
529 switch (sview->type) { 529 switch (sview->type) {
530#ifdef HAVE_XWAYLAND
530 case SWAY_VIEW_XWAYLAND: 531 case SWAY_VIEW_XWAYLAND:
531 _surface = wlr_surface_surface_at(sview->surface, 532 _surface = wlr_surface_surface_at(sview->surface,
532 view_sx, view_sy, &_sx, &_sy); 533 view_sx, view_sy, &_sx, &_sy);
533 break; 534 break;
535#endif
534 case SWAY_VIEW_XDG_SHELL_V6: 536 case SWAY_VIEW_XDG_SHELL_V6:
535 _surface = wlr_xdg_surface_v6_surface_at( 537 _surface = wlr_xdg_surface_v6_surface_at(
536 sview->wlr_xdg_surface_v6, 538 sview->wlr_xdg_surface_v6,
diff --git a/sway/tree/layout.c b/sway/tree/layout.c
index a2be0ef3..2b3263f8 100644
--- a/sway/tree/layout.c
+++ b/sway/tree/layout.c
@@ -6,6 +6,7 @@
6#include <string.h> 6#include <string.h>
7#include <wlr/types/wlr_output.h> 7#include <wlr/types/wlr_output.h>
8#include <wlr/types/wlr_output_layout.h> 8#include <wlr/types/wlr_output_layout.h>
9#include "config.h"
9#include "sway/debug.h" 10#include "sway/debug.h"
10#include "sway/tree/arrange.h" 11#include "sway/tree/arrange.h"
11#include "sway/tree/container.h" 12#include "sway/tree/container.h"
@@ -39,7 +40,9 @@ void layout_init(void) {
39 root_container.sway_root = calloc(1, sizeof(*root_container.sway_root)); 40 root_container.sway_root = calloc(1, sizeof(*root_container.sway_root));
40 root_container.sway_root->output_layout = wlr_output_layout_create(); 41 root_container.sway_root->output_layout = wlr_output_layout_create();
41 wl_list_init(&root_container.sway_root->outputs); 42 wl_list_init(&root_container.sway_root->outputs);
43#ifdef HAVE_XWAYLAND
42 wl_list_init(&root_container.sway_root->xwayland_unmanaged); 44 wl_list_init(&root_container.sway_root->xwayland_unmanaged);
45#endif
43 wl_list_init(&root_container.sway_root->drag_icons); 46 wl_list_init(&root_container.sway_root->drag_icons);
44 wl_signal_init(&root_container.sway_root->events.new_container); 47 wl_signal_init(&root_container.sway_root->events.new_container);
45 root_container.sway_root->scratchpad = create_list(); 48 root_container.sway_root->scratchpad = create_list();
diff --git a/sway/tree/view.c b/sway/tree/view.c
index 9d88d7aa..beeb8144 100644
--- a/sway/tree/view.c
+++ b/sway/tree/view.c
@@ -3,6 +3,10 @@
3#include <wayland-server.h> 3#include <wayland-server.h>
4#include <wlr/render/wlr_renderer.h> 4#include <wlr/render/wlr_renderer.h>
5#include <wlr/types/wlr_output_layout.h> 5#include <wlr/types/wlr_output_layout.h>
6#include "config.h"
7#ifdef HAVE_XWAYLAND
8#include <wlr/xwayland.h>
9#endif
6#include "list.h" 10#include "list.h"
7#include "log.h" 11#include "log.h"
8#include "sway/criteria.h" 12#include "sway/criteria.h"
@@ -107,14 +111,14 @@ const char *view_get_instance(struct sway_view *view) {
107 } 111 }
108 return NULL; 112 return NULL;
109} 113}
110 114#ifdef HAVE_XWAYLAND
111uint32_t view_get_x11_window_id(struct sway_view *view) { 115uint32_t view_get_x11_window_id(struct sway_view *view) {
112 if (view->impl->get_int_prop) { 116 if (view->impl->get_int_prop) {
113 return view->impl->get_int_prop(view, VIEW_PROP_X11_WINDOW_ID); 117 return view->impl->get_int_prop(view, VIEW_PROP_X11_WINDOW_ID);
114 } 118 }
115 return 0; 119 return 0;
116} 120}
117 121#endif
118const char *view_get_window_role(struct sway_view *view) { 122const char *view_get_window_role(struct sway_view *view) {
119 if (view->impl->get_string_prop) { 123 if (view->impl->get_string_prop) {
120 return view->impl->get_string_prop(view, VIEW_PROP_WINDOW_ROLE); 124 return view->impl->get_string_prop(view, VIEW_PROP_WINDOW_ROLE);
@@ -135,8 +139,10 @@ const char *view_get_shell(struct sway_view *view) {
135 return "xdg_shell_v6"; 139 return "xdg_shell_v6";
136 case SWAY_VIEW_XDG_SHELL: 140 case SWAY_VIEW_XDG_SHELL:
137 return "xdg_shell"; 141 return "xdg_shell";
142#ifdef HAVE_XWAYLAND
138 case SWAY_VIEW_XWAYLAND: 143 case SWAY_VIEW_XWAYLAND:
139 return "xwayland"; 144 return "xwayland";
145#endif
140 } 146 }
141 return "unknown"; 147 return "unknown";
142} 148}
@@ -561,9 +567,27 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface) {
561 return; 567 return;
562 } 568 }
563 569
570 pid_t pid;
571#ifdef HAVE_XWAYLAND
572 if (view->type == SWAY_VIEW_XWAYLAND) {
573 struct wlr_xwayland_surface *surf =
574 wlr_xwayland_surface_from_wlr_surface(wlr_surface);
575 pid = surf->pid;
576 } else {
577 struct wl_client *client =
578 wl_resource_get_client(wlr_surface->resource);
579 wl_client_get_credentials(client, &pid, NULL, NULL);
580 }
581#else
582 struct wl_client *client =
583 wl_resource_get_client(wlr_surface->resource);
584 wl_client_get_credentials(client, &pid, NULL, NULL);
585#endif
586
564 struct sway_seat *seat = input_manager_current_seat(input_manager); 587 struct sway_seat *seat = input_manager_current_seat(input_manager);
565 struct sway_container *focus = 588 struct sway_container *target_sibling =
566 seat_get_focus_inactive(seat, &root_container); 589 seat_get_focus_inactive(seat, &root_container);
590 struct sway_container *prev_focus = target_sibling;
567 struct sway_container *cont = NULL; 591 struct sway_container *cont = NULL;
568 592
569 // Check if there's any `assign` criteria for the view 593 // Check if there's any `assign` criteria for the view
@@ -577,22 +601,35 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface) {
577 if (!workspace) { 601 if (!workspace) {
578 workspace = workspace_create(NULL, criteria->target); 602 workspace = workspace_create(NULL, criteria->target);
579 } 603 }
580 focus = seat_get_focus_inactive(seat, workspace); 604 prev_focus = target_sibling;
605 target_sibling = seat_get_focus_inactive(seat, workspace);
581 } else { 606 } else {
582 // CT_ASSIGN_OUTPUT 607 // CT_ASSIGN_OUTPUT
583 struct sway_container *output = output_by_name(criteria->target); 608 struct sway_container *output = output_by_name(criteria->target);
584 if (output) { 609 if (output) {
585 focus = seat_get_focus_inactive(seat, output); 610 prev_focus = seat_get_focus_inactive(seat, output);
586 } 611 }
587 } 612 }
588 } 613 }
614 list_free(criterias);
615
616 if (!workspace) {
617 workspace = workspace_for_pid(pid);
618 if (workspace) {
619 prev_focus = target_sibling;
620 target_sibling = seat_get_focus_inactive(seat, workspace);
621 }
622 }
589 // If we're about to launch the view into the floating container, then 623 // If we're about to launch the view into the floating container, then
590 // launch it as a tiled view in the root of the workspace instead. 624 // launch it as a tiled view in the root of the workspace instead.
591 if (container_is_floating(focus)) { 625 if (container_is_floating(target_sibling)) {
592 focus = focus->parent->parent; 626 if (prev_focus == target_sibling) {
627 prev_focus = target_sibling->parent->parent;
628 }
629 target_sibling = target_sibling->parent->parent;
593 } 630 }
594 list_free(criterias); 631
595 cont = container_view_create(focus, view); 632 cont = container_view_create(target_sibling, view);
596 633
597 view->surface = wlr_surface; 634 view->surface = wlr_surface;
598 view->swayc = cont; 635 view->swayc = cont;
@@ -615,7 +652,7 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface) {
615 view_set_tiled(view, true); 652 view_set_tiled(view, true);
616 } 653 }
617 654
618 if (should_focus(view)) { 655 if (should_focus(view) && prev_focus == target_sibling) {
619 input_manager_set_focus(input_manager, cont); 656 input_manager_set_focus(input_manager, cont);
620 if (workspace) { 657 if (workspace) {
621 workspace_switch(workspace); 658 workspace_switch(workspace);
@@ -799,11 +836,13 @@ struct sway_view *view_from_wlr_surface(struct wlr_surface *wlr_surface) {
799 wlr_xdg_surface_v6_from_wlr_surface(wlr_surface); 836 wlr_xdg_surface_v6_from_wlr_surface(wlr_surface);
800 return view_from_wlr_xdg_surface_v6(xdg_surface_v6); 837 return view_from_wlr_xdg_surface_v6(xdg_surface_v6);
801 } 838 }
839#ifdef HAVE_XWAYLAND
802 if (wlr_surface_is_xwayland_surface(wlr_surface)) { 840 if (wlr_surface_is_xwayland_surface(wlr_surface)) {
803 struct wlr_xwayland_surface *xsurface = 841 struct wlr_xwayland_surface *xsurface =
804 wlr_xwayland_surface_from_wlr_surface(wlr_surface); 842 wlr_xwayland_surface_from_wlr_surface(wlr_surface);
805 return view_from_wlr_xwayland_surface(xsurface); 843 return view_from_wlr_xwayland_surface(xsurface);
806 } 844 }
845#endif
807 if (wlr_surface_is_subsurface(wlr_surface)) { 846 if (wlr_surface_is_subsurface(wlr_surface)) {
808 struct wlr_subsurface *subsurface = 847 struct wlr_subsurface *subsurface =
809 wlr_subsurface_from_wlr_surface(wlr_surface); 848 wlr_subsurface_from_wlr_surface(wlr_surface);
diff --git a/sway/tree/workspace.c b/sway/tree/workspace.c
index 622f01ec..62974cd7 100644
--- a/sway/tree/workspace.c
+++ b/sway/tree/workspace.c
@@ -9,6 +9,7 @@
9#include "sway/input/input-manager.h" 9#include "sway/input/input-manager.h"
10#include "sway/input/seat.h" 10#include "sway/input/seat.h"
11#include "sway/ipc-server.h" 11#include "sway/ipc-server.h"
12#include "sway/output.h"
12#include "sway/tree/arrange.h" 13#include "sway/tree/arrange.h"
13#include "sway/tree/container.h" 14#include "sway/tree/container.h"
14#include "sway/tree/view.h" 15#include "sway/tree/view.h"
@@ -107,96 +108,103 @@ static bool workspace_valid_on_output(const char *output_name,
107 return true; 108 return true;
108} 109}
109 110
110char *workspace_next_name(const char *output_name) { 111static void workspace_name_from_binding(const struct sway_binding * binding,
111 wlr_log(WLR_DEBUG, "Workspace: Generating new workspace name for output %s", 112 const char* output_name, int *min_order, char **earliest_name) {
112 output_name); 113 char *cmdlist = strdup(binding->command);
113 // Scan all workspace bindings to find the next available workspace name, 114 char *dup = cmdlist;
114 // if none are found/available then default to a number 115 char *name = NULL;
115 struct sway_mode *mode = config->current_mode;
116 116
117 // TODO: iterate over keycode bindings too 117 // workspace n
118 int order = INT_MAX; 118 char *cmd = argsep(&cmdlist, " ");
119 char *target = NULL; 119 if (cmdlist) {
120 for (int i = 0; i < mode->keysym_bindings->length; ++i) { 120 name = argsep(&cmdlist, ",;");
121 struct sway_binding *binding = mode->keysym_bindings->items[i]; 121 }
122 char *cmdlist = strdup(binding->command);
123 char *dup = cmdlist;
124 char *name = NULL;
125
126 // workspace n
127 char *cmd = argsep(&cmdlist, " ");
128 if (cmdlist) {
129 name = argsep(&cmdlist, ",;");
130 }
131 122
132 if (strcmp("workspace", cmd) == 0 && name) { 123 if (strcmp("workspace", cmd) == 0 && name) {
133 char *_target = strdup(name); 124 char *_target = strdup(name);
134 _target = do_var_replacement(_target); 125 _target = do_var_replacement(_target);
135 strip_quotes(_target); 126 strip_quotes(_target);
136 while (isspace(*_target)) { 127 while (isspace(*_target)) {
137 memmove(_target, _target+1, strlen(_target+1)); 128 memmove(_target, _target+1, strlen(_target+1));
138 } 129 }
139 wlr_log(WLR_DEBUG, "Got valid workspace command for target: '%s'", 130 wlr_log(WLR_DEBUG, "Got valid workspace command for target: '%s'",
140 _target); 131 _target);
141 132
142 // Make sure that the command references an actual workspace 133 // Make sure that the command references an actual workspace
143 // not a command about workspaces 134 // not a command about workspaces
144 if (strcmp(_target, "next") == 0 || 135 if (strcmp(_target, "next") == 0 ||
145 strcmp(_target, "prev") == 0 || 136 strcmp(_target, "prev") == 0 ||
146 strcmp(_target, "next_on_output") == 0 || 137 strcmp(_target, "next_on_output") == 0 ||
147 strcmp(_target, "prev_on_output") == 0 || 138 strcmp(_target, "prev_on_output") == 0 ||
148 strcmp(_target, "number") == 0 || 139 strcmp(_target, "number") == 0 ||
149 strcmp(_target, "back_and_forth") == 0 || 140 strcmp(_target, "back_and_forth") == 0 ||
150 strcmp(_target, "current") == 0) 141 strcmp(_target, "current") == 0) {
151 { 142 free(_target);
152 free(_target); 143 free(dup);
153 free(dup); 144 return;
154 continue; 145 }
155 }
156
157 // If the command is workspace number <name>, isolate the name
158 if (strncmp(_target, "number ", strlen("number ")) == 0) {
159 size_t length = strlen(_target) - strlen("number ") + 1;
160 char *temp = malloc(length);
161 strncpy(temp, _target + strlen("number "), length - 1);
162 temp[length - 1] = '\0';
163 free(_target);
164 _target = temp;
165 wlr_log(WLR_DEBUG, "Isolated name from workspace number: '%s'", _target);
166
167 // Make sure the workspace number doesn't already exist
168 if (workspace_by_number(_target)) {
169 free(_target);
170 free(dup);
171 continue;
172 }
173 }
174 146
175 // Make sure that the workspace doesn't already exist 147 // If the command is workspace number <name>, isolate the name
176 if (workspace_by_name(_target)) { 148 if (strncmp(_target, "number ", strlen("number ")) == 0) {
149 size_t length = strlen(_target) - strlen("number ") + 1;
150 char *temp = malloc(length);
151 strncpy(temp, _target + strlen("number "), length - 1);
152 temp[length - 1] = '\0';
153 free(_target);
154 _target = temp;
155 wlr_log(WLR_DEBUG, "Isolated name from workspace number: '%s'", _target);
156
157 // Make sure the workspace number doesn't already exist
158 if (workspace_by_number(_target)) {
177 free(_target); 159 free(_target);
178 free(dup); 160 free(dup);
179 continue; 161 return;
180 } 162 }
163 }
181 164
182 // make sure that the workspace can appear on the given 165 // Make sure that the workspace doesn't already exist
183 // output 166 if (workspace_by_name(_target)) {
184 if (!workspace_valid_on_output(output_name, _target)) { 167 free(_target);
185 free(_target); 168 free(dup);
186 free(dup); 169 return;
187 continue; 170 }
188 }
189 171
190 if (binding->order < order) { 172 // make sure that the workspace can appear on the given
191 order = binding->order; 173 // output
192 free(target); 174 if (!workspace_valid_on_output(output_name, _target)) {
193 target = _target; 175 free(_target);
194 wlr_log(WLR_DEBUG, "Workspace: Found free name %s", _target); 176 free(dup);
195 } else { 177 return;
196 free(_target); 178 }
197 } 179
180 if (binding->order < *min_order) {
181 *min_order = binding->order;
182 free(*earliest_name);
183 *earliest_name = _target;
184 wlr_log(WLR_DEBUG, "Workspace: Found free name %s", _target);
185 } else {
186 free(_target);
198 } 187 }
199 free(dup); 188 }
189 free(dup);
190}
191
192char *workspace_next_name(const char *output_name) {
193 wlr_log(WLR_DEBUG, "Workspace: Generating new workspace name for output %s",
194 output_name);
195 // Scan all workspace bindings to find the next available workspace name,
196 // if none are found/available then default to a number
197 struct sway_mode *mode = config->current_mode;
198
199 int order = INT_MAX;
200 char *target = NULL;
201 for (int i = 0; i < mode->keysym_bindings->length; ++i) {
202 workspace_name_from_binding(mode->keysym_bindings->items[i],
203 output_name, &order, &target);
204 }
205 for (int i = 0; i < mode->keycode_bindings->length; ++i) {
206 workspace_name_from_binding(mode->keycode_bindings->items[i],
207 output_name, &order, &target);
200 } 208 }
201 if (target != NULL) { 209 if (target != NULL) {
202 return target; 210 return target;
@@ -529,3 +537,116 @@ void workspace_detect_urgent(struct sway_container *workspace) {
529 container_damage_whole(workspace); 537 container_damage_whole(workspace);
530 } 538 }
531} 539}
540
541struct pid_workspace {
542 pid_t pid;
543 char *workspace;
544 struct timespec time_added;
545
546 struct sway_container *output;
547 struct wl_listener output_destroy;
548
549 struct wl_list link;
550};
551
552static struct wl_list pid_workspaces;
553
554struct sway_container *workspace_for_pid(pid_t pid) {
555 if (!pid_workspaces.prev && !pid_workspaces.next) {
556 wl_list_init(&pid_workspaces);
557 return NULL;
558 }
559
560 struct sway_container *ws = NULL;
561 struct pid_workspace *pw = NULL;
562
563 wlr_log(WLR_DEBUG, "Looking up workspace for pid %d", pid);
564
565 do {
566 struct pid_workspace *_pw = NULL;
567 wl_list_for_each(_pw, &pid_workspaces, link) {
568 if (pid == _pw->pid) {
569 pw = _pw;
570 wlr_log(WLR_DEBUG,
571 "found pid_workspace for pid %d, workspace %s",
572 pid, pw->workspace);
573 goto found;
574 }
575 }
576 pid = get_parent_pid(pid);
577 } while (pid > 1);
578found:
579
580 if (pw && pw->workspace) {
581 ws = workspace_by_name(pw->workspace);
582
583 if (!ws) {
584 wlr_log(WLR_DEBUG,
585 "Creating workspace %s for pid %d because it disappeared",
586 pw->workspace, pid);
587 ws = workspace_create(pw->output, pw->workspace);
588 }
589
590 wl_list_remove(&pw->output_destroy.link);
591 wl_list_remove(&pw->link);
592 free(pw->workspace);
593 free(pw);
594 }
595
596 return ws;
597}
598
599static void pw_handle_output_destroy(struct wl_listener *listener, void *data) {
600 struct pid_workspace *pw = wl_container_of(listener, pw, output_destroy);
601 pw->output = NULL;
602 wl_list_remove(&pw->output_destroy.link);
603 wl_list_init(&pw->output_destroy.link);
604}
605
606void workspace_record_pid(pid_t pid) {
607 wlr_log(WLR_DEBUG, "Recording workspace for process %d", pid);
608 if (!pid_workspaces.prev && !pid_workspaces.next) {
609 wl_list_init(&pid_workspaces);
610 }
611
612 struct sway_seat *seat = input_manager_current_seat(input_manager);
613 struct sway_container *ws =
614 seat_get_focus_inactive(seat, &root_container);
615 if (ws && ws->type != C_WORKSPACE) {
616 ws = container_parent(ws, C_WORKSPACE);
617 }
618 if (!ws) {
619 wlr_log(WLR_DEBUG, "Bailing out, no workspace");
620 return;
621 }
622 struct sway_container *output = ws->parent;
623 if (!output) {
624 wlr_log(WLR_DEBUG, "Bailing out, no output");
625 return;
626 }
627
628 struct timespec now;
629 clock_gettime(CLOCK_MONOTONIC, &now);
630
631 // Remove expired entries
632 static const int timeout = 60;
633 struct pid_workspace *old, *_old;
634 wl_list_for_each_safe(old, _old, &pid_workspaces, link) {
635 if (now.tv_sec - old->time_added.tv_sec >= timeout) {
636 wl_list_remove(&old->output_destroy.link);
637 wl_list_remove(&old->link);
638 free(old->workspace);
639 free(old);
640 }
641 }
642
643 struct pid_workspace *pw = calloc(1, sizeof(struct pid_workspace));
644 pw->workspace = strdup(ws->name);
645 pw->output = output;
646 pw->pid = pid;
647 memcpy(&pw->time_added, &now, sizeof(struct timespec));
648 pw->output_destroy.notify = pw_handle_output_destroy;
649 wl_signal_add(&output->sway_output->wlr_output->events.destroy,
650 &pw->output_destroy);
651 wl_list_insert(&pid_workspaces, &pw->link);
652}