diff options
-rw-r--r-- | include/sway/config.h | 21 | ||||
-rw-r--r-- | sway/commands/bind.c | 152 | ||||
-rw-r--r-- | sway/config.c | 7 | ||||
-rw-r--r-- | sway/input/keyboard.c | 6 |
4 files changed, 134 insertions, 52 deletions
diff --git a/include/sway/config.h b/include/sway/config.h index b8da29c5..92536d10 100644 --- a/include/sway/config.h +++ b/include/sway/config.h | |||
@@ -22,14 +22,28 @@ struct sway_variable { | |||
22 | char *value; | 22 | char *value; |
23 | }; | 23 | }; |
24 | 24 | ||
25 | |||
26 | enum binding_input_type { | ||
27 | BINDING_KEYCODE, | ||
28 | BINDING_KEYSYM, | ||
29 | BINDING_MOUSE, | ||
30 | }; | ||
31 | |||
32 | enum binding_flags { | ||
33 | BINDING_RELEASE=1, | ||
34 | BINDING_LOCKED=2, // keyboard only | ||
35 | BINDING_BORDER=4, // mouse only; trigger on container border | ||
36 | BINDING_CONTENTS=8, // mouse only; trigger on container contents | ||
37 | BINDING_TITLEBAR=16 // mouse only; trigger on container titlebar | ||
38 | }; | ||
39 | |||
25 | /** | 40 | /** |
26 | * A key binding and an associated command. | 41 | * A key binding and an associated command. |
27 | */ | 42 | */ |
28 | struct sway_binding { | 43 | struct sway_binding { |
44 | enum binding_input_type type; | ||
29 | int order; | 45 | int order; |
30 | bool release; | 46 | uint32_t flags; |
31 | bool locked; | ||
32 | bool bindcode; | ||
33 | list_t *keys; // sorted in ascending order | 47 | list_t *keys; // sorted in ascending order |
34 | uint32_t modifiers; | 48 | uint32_t modifiers; |
35 | char *command; | 49 | char *command; |
@@ -50,6 +64,7 @@ struct sway_mode { | |||
50 | char *name; | 64 | char *name; |
51 | list_t *keysym_bindings; | 65 | list_t *keysym_bindings; |
52 | list_t *keycode_bindings; | 66 | list_t *keycode_bindings; |
67 | list_t *mouse_bindings; | ||
53 | bool pango; | 68 | bool pango; |
54 | }; | 69 | }; |
55 | 70 | ||
diff --git a/sway/commands/bind.c b/sway/commands/bind.c index 83e9e432..6910237f 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 | */ |
35 | static bool binding_key_compare(struct sway_binding *binding_a, | 35 | static 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 | */ | ||
83 | static 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 | |||
72 | static struct cmd_results *cmd_bindsym_or_bindcode(int argc, char **argv, | 135 | static 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 |
diff --git a/sway/config.c b/sway/config.c index ed624bfa..c2310ff7 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 | ||
@@ -172,6 +178,7 @@ static void config_defaults(struct sway_config *config) { | |||
172 | strcpy(config->current_mode->name, "default"); | 178 | strcpy(config->current_mode->name, "default"); |
173 | if (!(config->current_mode->keysym_bindings = create_list())) goto cleanup; | 179 | if (!(config->current_mode->keysym_bindings = create_list())) goto cleanup; |
174 | if (!(config->current_mode->keycode_bindings = create_list())) goto cleanup; | 180 | if (!(config->current_mode->keycode_bindings = create_list())) goto cleanup; |
181 | if (!(config->current_mode->mouse_bindings = create_list())) goto cleanup; | ||
175 | list_add(config->modes, config->current_mode); | 182 | list_add(config->modes, config->current_mode); |
176 | 183 | ||
177 | config->floating_mod = 0; | 184 | config->floating_mod = 0; |
diff --git a/sway/input/keyboard.c b/sway/input/keyboard.c index ede38519..e6c5c335 100644 --- a/sway/input/keyboard.c +++ b/sway/input/keyboard.c | |||
@@ -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 | ||