summaryrefslogtreecommitdiffstats
path: root/sway/commands/bind.c
diff options
context:
space:
mode:
Diffstat (limited to 'sway/commands/bind.c')
-rw-r--r--sway/commands/bind.c245
1 files changed, 245 insertions, 0 deletions
diff --git a/sway/commands/bind.c b/sway/commands/bind.c
new file mode 100644
index 00000000..79121404
--- /dev/null
+++ b/sway/commands/bind.c
@@ -0,0 +1,245 @@
1#ifdef __linux__
2#include <linux/input-event-codes.h>
3#elif __FreeBSD__
4#include <dev/evdev/input-event-codes.h>
5#endif
6#include <xkbcommon/xkbcommon.h>
7#include <xkbcommon/xkbcommon-names.h>
8#include <strings.h>
9#include "sway/commands.h"
10#include "sway/config.h"
11#include "list.h"
12#include "log.h"
13#include "stringop.h"
14#include "util.h"
15
16int binding_order = 0;
17
18void free_sway_binding(struct sway_binding *binding) {
19 if (!binding) {
20 return;
21 }
22
23 if (binding->keys) {
24 free_flat_list(binding->keys);
25 }
26 free(binding->command);
27 free(binding);
28}
29
30/**
31 * Returns true if the bindings have the same key and modifier combinations.
32 * Note that keyboard layout is not considered, so the bindings might actually
33 * not be equivalent on some layouts.
34 */
35bool binding_key_compare(struct sway_binding *binding_a,
36 struct sway_binding *binding_b) {
37 if (binding_a->release != binding_b->release) {
38 return false;
39 }
40
41 if (binding_a->bindcode != binding_b->bindcode) {
42 return false;
43 }
44
45 if (binding_a->modifiers ^ binding_b->modifiers) {
46 return false;
47 }
48
49 if (binding_a->keys->length != binding_b->keys->length) {
50 return false;
51 }
52
53 int keys_len = binding_a->keys->length;
54 for (int i = 0; i < keys_len; ++i) {
55 uint32_t key_a = *(uint32_t*)binding_a->keys->items[i];
56 bool found = false;
57 for (int j = 0; j < keys_len; ++j) {
58 uint32_t key_b = *(uint32_t*)binding_b->keys->items[j];
59 if (key_b == key_a) {
60 found = true;
61 break;
62 }
63 }
64 if (!found) {
65 return false;
66 }
67 }
68
69 return true;
70}
71
72struct cmd_results *cmd_bindsym(int argc, char **argv) {
73 struct cmd_results *error = NULL;
74 if ((error = checkarg(argc, "bindsym", EXPECTED_MORE_THAN, 1))) {
75 return error;
76 }
77
78 struct sway_binding *binding = calloc(1, sizeof(struct sway_binding));
79 if (!binding) {
80 return cmd_results_new(CMD_FAILURE, "bindsym",
81 "Unable to allocate binding");
82 }
83 binding->keys = create_list();
84 binding->modifiers = 0;
85 binding->release = false;
86 binding->bindcode = false;
87
88 // Handle --release
89 if (strcmp("--release", argv[0]) == 0) {
90 if (argc >= 3) {
91 binding->release = true;
92 argv++;
93 argc--;
94 } else {
95 free_sway_binding(binding);
96 return cmd_results_new(CMD_FAILURE, "bindsym",
97 "Invalid bindsym command "
98 "(expected more than 2 arguments, got %d)", argc);
99 }
100 }
101
102 binding->command = join_args(argv + 1, argc - 1);
103
104 list_t *split = split_string(argv[0], "+");
105 for (int i = 0; i < split->length; ++i) {
106 // Check for a modifier key
107 uint32_t mod;
108 if ((mod = get_modifier_mask_by_name(split->items[i])) > 0) {
109 binding->modifiers |= mod;
110 continue;
111 }
112 // Check for xkb key
113 xkb_keysym_t sym = xkb_keysym_from_name(split->items[i],
114 XKB_KEYSYM_CASE_INSENSITIVE);
115
116 // Check for mouse binding
117 if (strncasecmp(split->items[i], "button", strlen("button")) == 0 &&
118 strlen(split->items[i]) == strlen("button0")) {
119 sym = ((char *)split->items[i])[strlen("button")] - '1' + BTN_LEFT;
120 }
121 if (!sym) {
122 struct cmd_results *ret = cmd_results_new(CMD_INVALID, "bindsym",
123 "Unknown key '%s'", (char *)split->items[i]);
124 free_sway_binding(binding);
125 free_flat_list(split);
126 return ret;
127 }
128 xkb_keysym_t *key = calloc(1, sizeof(xkb_keysym_t));
129 if (!key) {
130 free_sway_binding(binding);
131 free_flat_list(split);
132 return cmd_results_new(CMD_FAILURE, "bindsym",
133 "Unable to allocate binding");
134 }
135 *key = sym;
136 list_add(binding->keys, key);
137 }
138 free_flat_list(split);
139 binding->order = binding_order++;
140
141 list_t *mode_bindings = config->current_mode->keysym_bindings;
142
143 // overwrite the binding if it already exists
144 bool overwritten = false;
145 for (int i = 0; i < mode_bindings->length; ++i) {
146 struct sway_binding *config_binding = mode_bindings->items[i];
147 if (binding_key_compare(binding, config_binding)) {
148 sway_log(L_DEBUG, "overwriting old binding with command '%s'",
149 config_binding->command);
150 free_sway_binding(config_binding);
151 mode_bindings->items[i] = binding;
152 overwritten = true;
153 }
154 }
155
156 if (!overwritten) {
157 list_add(mode_bindings, binding);
158 }
159
160 sway_log(L_DEBUG, "bindsym - Bound %s to command %s",
161 argv[0], binding->command);
162 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
163}
164
165struct cmd_results *cmd_bindcode(int argc, char **argv) {
166 struct cmd_results *error = NULL;
167 if ((error = checkarg(argc, "bindcode", EXPECTED_MORE_THAN, 1))) {
168 return error;
169 }
170
171 struct sway_binding *binding = calloc(1, sizeof(struct sway_binding));
172 if (!binding) {
173 return cmd_results_new(CMD_FAILURE, "bindsym",
174 "Unable to allocate binding");
175 }
176 binding->keys = create_list();
177 binding->modifiers = 0;
178 binding->release = false;
179 binding->bindcode = true;
180
181 // Handle --release
182 if (strcmp("--release", argv[0]) == 0) {
183 if (argc >= 3) {
184 binding->release = true;
185 argv++;
186 argc--;
187 } else {
188 free_sway_binding(binding);
189 return cmd_results_new(CMD_FAILURE, "bindcode",
190 "Invalid bindcode command "
191 "(expected more than 2 arguments, got %d)", argc);
192 }
193 }
194
195 binding->command = join_args(argv + 1, argc - 1);
196
197 list_t *split = split_string(argv[0], "+");
198 for (int i = 0; i < split->length; ++i) {
199 // Check for a modifier key
200 uint32_t mod;
201 if ((mod = get_modifier_mask_by_name(split->items[i])) > 0) {
202 binding->modifiers |= mod;
203 continue;
204 }
205 // parse keycode
206 xkb_keycode_t keycode = (int)strtol(split->items[i], NULL, 10);
207 if (!xkb_keycode_is_legal_ext(keycode)) {
208 error =
209 cmd_results_new(CMD_INVALID, "bindcode",
210 "Invalid keycode '%s'", (char *)split->items[i]);
211 free_sway_binding(binding);
212 list_free(split);
213 return error;
214 }
215 xkb_keycode_t *key = calloc(1, sizeof(xkb_keycode_t));
216 *key = keycode - 8;
217 list_add(binding->keys, key);
218 }
219 free_flat_list(split);
220
221 binding->order = binding_order++;
222
223 list_t *mode_bindings = config->current_mode->keycode_bindings;
224
225 // overwrite the binding if it already exists
226 bool overwritten = false;
227 for (int i = 0; i < mode_bindings->length; ++i) {
228 struct sway_binding *config_binding = mode_bindings->items[i];
229 if (binding_key_compare(binding, config_binding)) {
230 sway_log(L_DEBUG, "overwriting old binding with command '%s'",
231 config_binding->command);
232 free_sway_binding(config_binding);
233 mode_bindings->items[i] = binding;
234 overwritten = true;
235 }
236 }
237
238 if (!overwritten) {
239 list_add(mode_bindings, binding);
240 }
241
242 sway_log(L_DEBUG, "bindcode - Bound %s to command %s",
243 argv[0], binding->command);
244 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
245}