summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--common/util.c16
-rw-r--r--include/sway/config.h23
-rw-r--r--include/sway/input/cursor.h6
-rw-r--r--include/util.h8
-rw-r--r--sway/commands/bind.c168
-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/urgent.c10
-rw-r--r--sway/config.c7
-rw-r--r--sway/input/cursor.c108
-rw-r--r--sway/input/keyboard.c42
-rw-r--r--sway/tree/workspace.c159
22 files changed, 423 insertions, 238 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/include/sway/config.h b/include/sway/config.h
index 9d2e1bf9..032f4196 100644
--- a/include/sway/config.h
+++ b/include/sway/config.h
@@ -21,14 +21,28 @@ struct sway_variable {
21 char *value; 21 char *value;
22}; 22};
23 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
24/** 39/**
25 * A key binding and an associated command. 40 * A key binding and an associated command.
26 */ 41 */
27struct sway_binding { 42struct sway_binding {
43 enum binding_input_type type;
28 int order; 44 int order;
29 bool release; 45 uint32_t flags;
30 bool locked;
31 bool bindcode;
32 list_t *keys; // sorted in ascending order 46 list_t *keys; // sorted in ascending order
33 uint32_t modifiers; 47 uint32_t modifiers;
34 char *command; 48 char *command;
@@ -49,6 +63,7 @@ struct sway_mode {
49 char *name; 63 char *name;
50 list_t *keysym_bindings; 64 list_t *keysym_bindings;
51 list_t *keycode_bindings; 65 list_t *keycode_bindings;
66 list_t *mouse_bindings;
52 bool pango; 67 bool pango;
53}; 68};
54 69
@@ -471,6 +486,8 @@ void free_sway_binding(struct sway_binding *sb);
471 486
472struct sway_binding *sway_binding_dup(struct sway_binding *sb); 487struct sway_binding *sway_binding_dup(struct sway_binding *sb);
473 488
489void seat_execute_command(struct sway_seat *seat, struct sway_binding *binding);
490
474void load_swaybars(); 491void load_swaybars();
475 492
476void invoke_swaybar(struct bar_config *bar); 493void invoke_swaybar(struct bar_config *bar);
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/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/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/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/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 5f6dd7ad..90dfb9a9 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
@@ -170,6 +176,7 @@ static void config_defaults(struct sway_config *config) {
170 strcpy(config->current_mode->name, "default"); 176 strcpy(config->current_mode->name, "default");
171 if (!(config->current_mode->keysym_bindings = create_list())) goto cleanup; 177 if (!(config->current_mode->keysym_bindings = create_list())) goto cleanup;
172 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;
173 list_add(config->modes, config->current_mode); 180 list_add(config->modes, config->current_mode);
174 181
175 config->floating_mod = 0; 182 config->floating_mod = 0;
diff --git a/sway/input/cursor.c b/sway/input/cursor.c
index 65d04cac..27597640 100644
--- a/sway/input/cursor.c
+++ b/sway/input/cursor.c
@@ -460,6 +460,12 @@ static void dispatch_cursor_button_floating(struct sway_cursor *cursor,
460 bool resizing_via_mod = button == BTN_RIGHT && mod_pressed; 460 bool resizing_via_mod = button == BTN_RIGHT && mod_pressed;
461 if ((resizing_via_border || resizing_via_mod) && 461 if ((resizing_via_border || resizing_via_mod) &&
462 state == WLR_BUTTON_PRESSED) { 462 state == WLR_BUTTON_PRESSED) {
463 if (edge == WLR_EDGE_NONE) {
464 edge |= cursor->cursor->x > cont->x + cont->width / 2 ?
465 WLR_EDGE_RIGHT : WLR_EDGE_LEFT;
466 edge |= cursor->cursor->y > cont->y + cont->height / 2 ?
467 WLR_EDGE_BOTTOM : WLR_EDGE_TOP;
468 }
463 seat_begin_resize(seat, cont, button, edge); 469 seat_begin_resize(seat, cont, button, edge);
464 return; 470 return;
465 } 471 }
@@ -469,6 +475,83 @@ static void dispatch_cursor_button_floating(struct sway_cursor *cursor,
469 seat_pointer_notify_button(seat, time_msec, button, state); 475 seat_pointer_notify_button(seat, time_msec, button, state);
470} 476}
471 477
478/**
479 * Remove a button (and duplicates) to the sorted list of currently pressed buttons
480 */
481static void state_erase_button(struct sway_cursor *cursor, uint32_t button) {
482 size_t j = 0;
483 for (size_t i = 0; i < cursor->pressed_button_count; ++i) {
484 if (i > j) {
485 cursor->pressed_buttons[j] = cursor->pressed_buttons[i];
486 }
487 if (cursor->pressed_buttons[i] != button) {
488 ++j;
489 }
490 }
491 while (cursor->pressed_button_count > j) {
492 --cursor->pressed_button_count;
493 cursor->pressed_buttons[cursor->pressed_button_count] = 0;
494 }
495}
496
497/**
498 * Add a button to the sorted list of currently pressed buttons, if there
499 * is space.
500 */
501static void state_add_button(struct sway_cursor *cursor, uint32_t button) {
502 if (cursor->pressed_button_count >= SWAY_CURSOR_PRESSED_BUTTONS_CAP) {
503 return;
504 }
505 size_t i = 0;
506 while (i < cursor->pressed_button_count && cursor->pressed_buttons[i] < button) {
507 ++i;
508 }
509 size_t j = cursor->pressed_button_count;
510 while (j > i) {
511 cursor->pressed_buttons[j] = cursor->pressed_buttons[j - 1];
512 --j;
513 }
514 cursor->pressed_buttons[i] = button;
515 cursor->pressed_button_count++;
516}
517
518/**
519 * Return the mouse binding which matches modifier, click location, release,
520 * and pressed button state, otherwise return null.
521 */
522static struct sway_binding* get_active_mouse_binding(const struct sway_cursor *cursor,
523 list_t *bindings, uint32_t modifiers, bool release, bool on_titlebar,
524 bool on_border, bool on_content) {
525 uint32_t click_region = (on_titlebar ? BINDING_TITLEBAR : 0) |
526 (on_border ? BINDING_BORDER : 0) |
527 (on_content ? BINDING_CONTENTS : 0);
528
529 for (int i = 0; i < bindings->length; ++i) {
530 struct sway_binding *binding = bindings->items[i];
531 if (modifiers ^ binding->modifiers ||
532 cursor->pressed_button_count != (size_t)binding->keys->length ||
533 release != (binding->flags & BINDING_RELEASE) ||
534 !(click_region & binding->flags)) {
535 continue;
536 }
537
538 bool match = true;
539 for (size_t j = 0; j < cursor->pressed_button_count; j++) {
540 uint32_t key = *(uint32_t *)binding->keys->items[j];
541 if (key != cursor->pressed_buttons[j]) {
542 match = false;
543 break;
544 }
545 }
546 if (!match) {
547 continue;
548 }
549
550 return binding;
551 }
552 return NULL;
553}
554
472void dispatch_cursor_button(struct sway_cursor *cursor, 555void dispatch_cursor_button(struct sway_cursor *cursor,
473 uint32_t time_msec, uint32_t button, enum wlr_button_state state) { 556 uint32_t time_msec, uint32_t button, enum wlr_button_state state) {
474 if (cursor->seat->operation != OP_NONE && 557 if (cursor->seat->operation != OP_NONE &&
@@ -485,6 +568,31 @@ void dispatch_cursor_button(struct sway_cursor *cursor,
485 double sx, sy; 568 double sx, sy;
486 struct sway_container *cont = container_at_coords(cursor->seat, 569 struct sway_container *cont = container_at_coords(cursor->seat,
487 cursor->cursor->x, cursor->cursor->y, &surface, &sx, &sy); 570 cursor->cursor->x, cursor->cursor->y, &surface, &sx, &sy);
571
572 // Handle mouse bindings
573 bool on_border = cont && (find_resize_edge(cont, cursor) != WLR_EDGE_NONE);
574 bool on_contents = !on_border && surface;
575 bool on_titlebar = !on_border && !surface;
576 struct wlr_keyboard *keyboard = wlr_seat_get_keyboard(cursor->seat->wlr_seat);
577 uint32_t modifiers = keyboard ? wlr_keyboard_get_modifiers(keyboard) : 0;
578
579 struct sway_binding *binding = NULL;
580 if (state == WLR_BUTTON_PRESSED) {
581 state_add_button(cursor, button);
582 binding = get_active_mouse_binding(cursor,
583 config->current_mode->mouse_bindings, modifiers, false,
584 on_titlebar, on_border, on_contents);
585 } else {
586 binding = get_active_mouse_binding(cursor,
587 config->current_mode->mouse_bindings, modifiers, true,
588 on_titlebar, on_border, on_contents);
589 state_erase_button(cursor, button);
590 }
591 if (binding) {
592 seat_execute_command(cursor->seat, binding);
593 // TODO: do we want to pass on the event?
594 }
595
488 if (surface && wlr_surface_is_layer_surface(surface)) { 596 if (surface && wlr_surface_is_layer_surface(surface)) {
489 struct wlr_layer_surface *layer = 597 struct wlr_layer_surface *layer =
490 wlr_layer_surface_from_wlr_surface(surface); 598 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/tree/workspace.c b/sway/tree/workspace.c
index b1082e4f..62974cd7 100644
--- a/sway/tree/workspace.c
+++ b/sway/tree/workspace.c
@@ -108,96 +108,103 @@ static bool workspace_valid_on_output(const char *output_name,
108 return true; 108 return true;
109} 109}
110 110
111char *workspace_next_name(const char *output_name) { 111static void workspace_name_from_binding(const struct sway_binding * binding,
112 wlr_log(WLR_DEBUG, "Workspace: Generating new workspace name for output %s", 112 const char* output_name, int *min_order, char **earliest_name) {
113 output_name); 113 char *cmdlist = strdup(binding->command);
114 // Scan all workspace bindings to find the next available workspace name, 114 char *dup = cmdlist;
115 // if none are found/available then default to a number 115 char *name = NULL;
116 struct sway_mode *mode = config->current_mode; 116
117 117 // workspace n
118 // TODO: iterate over keycode bindings too 118 char *cmd = argsep(&cmdlist, " ");
119 int order = INT_MAX; 119 if (cmdlist) {
120 char *target = NULL; 120 name = argsep(&cmdlist, ",;");
121 for (int i = 0; i < mode->keysym_bindings->length; ++i) { 121 }
122 struct sway_binding *binding = mode->keysym_bindings->items[i]; 122
123 char *cmdlist = strdup(binding->command); 123 if (strcmp("workspace", cmd) == 0 && name) {
124 char *dup = cmdlist; 124 char *_target = strdup(name);
125 char *name = NULL; 125 _target = do_var_replacement(_target);
126 126 strip_quotes(_target);
127 // workspace n 127 while (isspace(*_target)) {
128 char *cmd = argsep(&cmdlist, " "); 128 memmove(_target, _target+1, strlen(_target+1));
129 if (cmdlist) {
130 name = argsep(&cmdlist, ",;");
131 } 129 }
130 wlr_log(WLR_DEBUG, "Got valid workspace command for target: '%s'",
131 _target);
132 132
133 if (strcmp("workspace", cmd) == 0 && name) { 133 // Make sure that the command references an actual workspace
134 char *_target = strdup(name); 134 // not a command about workspaces
135 _target = do_var_replacement(_target); 135 if (strcmp(_target, "next") == 0 ||
136 strip_quotes(_target);
137 while (isspace(*_target)) {
138 memmove(_target, _target+1, strlen(_target+1));
139 }
140 wlr_log(WLR_DEBUG, "Got valid workspace command for target: '%s'",
141 _target);
142
143 // Make sure that the command references an actual workspace
144 // not a command about workspaces
145 if (strcmp(_target, "next") == 0 ||
146 strcmp(_target, "prev") == 0 || 136 strcmp(_target, "prev") == 0 ||
147 strcmp(_target, "next_on_output") == 0 || 137 strcmp(_target, "next_on_output") == 0 ||
148 strcmp(_target, "prev_on_output") == 0 || 138 strcmp(_target, "prev_on_output") == 0 ||
149 strcmp(_target, "number") == 0 || 139 strcmp(_target, "number") == 0 ||
150 strcmp(_target, "back_and_forth") == 0 || 140 strcmp(_target, "back_and_forth") == 0 ||
151 strcmp(_target, "current") == 0) 141 strcmp(_target, "current") == 0) {
152 { 142 free(_target);
153 free(_target); 143 free(dup);
154 free(dup); 144 return;
155 continue; 145 }
156 }
157
158 // If the command is workspace number <name>, isolate the name
159 if (strncmp(_target, "number ", strlen("number ")) == 0) {
160 size_t length = strlen(_target) - strlen("number ") + 1;
161 char *temp = malloc(length);
162 strncpy(temp, _target + strlen("number "), length - 1);
163 temp[length - 1] = '\0';
164 free(_target);
165 _target = temp;
166 wlr_log(WLR_DEBUG, "Isolated name from workspace number: '%s'", _target);
167
168 // Make sure the workspace number doesn't already exist
169 if (workspace_by_number(_target)) {
170 free(_target);
171 free(dup);
172 continue;
173 }
174 }
175 146
176 // Make sure that the workspace doesn't already exist 147 // If the command is workspace number <name>, isolate the name
177 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)) {
178 free(_target); 159 free(_target);
179 free(dup); 160 free(dup);
180 continue; 161 return;
181 } 162 }
163 }
182 164
183 // make sure that the workspace can appear on the given 165 // Make sure that the workspace doesn't already exist
184 // output 166 if (workspace_by_name(_target)) {
185 if (!workspace_valid_on_output(output_name, _target)) { 167 free(_target);
186 free(_target); 168 free(dup);
187 free(dup); 169 return;
188 continue; 170 }
189 }
190 171
191 if (binding->order < order) { 172 // make sure that the workspace can appear on the given
192 order = binding->order; 173 // output
193 free(target); 174 if (!workspace_valid_on_output(output_name, _target)) {
194 target = _target; 175 free(_target);
195 wlr_log(WLR_DEBUG, "Workspace: Found free name %s", _target); 176 free(dup);
196 } else { 177 return;
197 free(_target);
198 }
199 } 178 }
200 free(dup); 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);
187 }
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);
201 } 208 }
202 if (target != NULL) { 209 if (target != NULL) {
203 return target; 210 return target;