1 #define _POSIX_C_SOURCE 200809L
2 #include <stdlib.h>
3 #include <limits.h>
4 #include <float.h>
5 #include "sway/config.h"
6 #include "sway/input/keyboard.h"
7 #include "log.h"
8
new_input_config(const char * identifier)9 struct input_config *new_input_config(const char* identifier) {
10 struct input_config *input = calloc(1, sizeof(struct input_config));
11 if (!input) {
12 sway_log(SWAY_DEBUG, "Unable to allocate input config");
13 return NULL;
14 }
15 sway_log(SWAY_DEBUG, "new_input_config(%s)", identifier);
16 if (!(input->identifier = strdup(identifier))) {
17 free(input);
18 sway_log(SWAY_DEBUG, "Unable to allocate input config");
19 return NULL;
20 }
21
22 input->input_type = NULL;
23 input->tap = INT_MIN;
24 input->tap_button_map = INT_MIN;
25 input->drag = INT_MIN;
26 input->drag_lock = INT_MIN;
27 input->dwt = INT_MIN;
28 input->send_events = INT_MIN;
29 input->click_method = INT_MIN;
30 input->middle_emulation = INT_MIN;
31 input->natural_scroll = INT_MIN;
32 input->accel_profile = INT_MIN;
33 input->pointer_accel = FLT_MIN;
34 input->scroll_factor = FLT_MIN;
35 input->scroll_button = INT_MIN;
36 input->scroll_method = INT_MIN;
37 input->left_handed = INT_MIN;
38 input->repeat_delay = INT_MIN;
39 input->repeat_rate = INT_MIN;
40 input->xkb_numlock = INT_MIN;
41 input->xkb_capslock = INT_MIN;
42 input->xkb_file_is_set = false;
43
44 return input;
45 }
46
merge_input_config(struct input_config * dst,struct input_config * src)47 void merge_input_config(struct input_config *dst, struct input_config *src) {
48 if (src->accel_profile != INT_MIN) {
49 dst->accel_profile = src->accel_profile;
50 }
51 if (src->click_method != INT_MIN) {
52 dst->click_method = src->click_method;
53 }
54 if (src->drag != INT_MIN) {
55 dst->drag = src->drag;
56 }
57 if (src->drag_lock != INT_MIN) {
58 dst->drag_lock = src->drag_lock;
59 }
60 if (src->dwt != INT_MIN) {
61 dst->dwt = src->dwt;
62 }
63 if (src->left_handed != INT_MIN) {
64 dst->left_handed = src->left_handed;
65 }
66 if (src->middle_emulation != INT_MIN) {
67 dst->middle_emulation = src->middle_emulation;
68 }
69 if (src->natural_scroll != INT_MIN) {
70 dst->natural_scroll = src->natural_scroll;
71 }
72 if (src->pointer_accel != FLT_MIN) {
73 dst->pointer_accel = src->pointer_accel;
74 }
75 if (src->scroll_factor != FLT_MIN) {
76 dst->scroll_factor = src->scroll_factor;
77 }
78 if (src->repeat_delay != INT_MIN) {
79 dst->repeat_delay = src->repeat_delay;
80 }
81 if (src->repeat_rate != INT_MIN) {
82 dst->repeat_rate = src->repeat_rate;
83 }
84 if (src->scroll_method != INT_MIN) {
85 dst->scroll_method = src->scroll_method;
86 }
87 if (src->scroll_button != INT_MIN) {
88 dst->scroll_button = src->scroll_button;
89 }
90 if (src->send_events != INT_MIN) {
91 dst->send_events = src->send_events;
92 }
93 if (src->tap != INT_MIN) {
94 dst->tap = src->tap;
95 }
96 if (src->tap_button_map != INT_MIN) {
97 dst->tap_button_map = src->tap_button_map;
98 }
99 if (src->xkb_file_is_set) {
100 free(dst->xkb_file);
101 dst->xkb_file = src->xkb_file ? strdup(src->xkb_file) : NULL;
102 dst->xkb_file_is_set = dst->xkb_file != NULL;
103 }
104 if (src->xkb_layout) {
105 free(dst->xkb_layout);
106 dst->xkb_layout = strdup(src->xkb_layout);
107 }
108 if (src->xkb_model) {
109 free(dst->xkb_model);
110 dst->xkb_model = strdup(src->xkb_model);
111 }
112 if (src->xkb_options) {
113 free(dst->xkb_options);
114 dst->xkb_options = strdup(src->xkb_options);
115 }
116 if (src->xkb_rules) {
117 free(dst->xkb_rules);
118 dst->xkb_rules = strdup(src->xkb_rules);
119 }
120 if (src->xkb_variant) {
121 free(dst->xkb_variant);
122 dst->xkb_variant = strdup(src->xkb_variant);
123 }
124 if (src->xkb_numlock != INT_MIN) {
125 dst->xkb_numlock = src->xkb_numlock;
126 }
127 if (src->xkb_capslock != INT_MIN) {
128 dst->xkb_capslock = src->xkb_capslock;
129 }
130 if (src->mapped_from_region) {
131 free(dst->mapped_from_region);
132 dst->mapped_from_region =
133 malloc(sizeof(struct input_config_mapped_from_region));
134 memcpy(dst->mapped_from_region, src->mapped_from_region,
135 sizeof(struct input_config_mapped_from_region));
136 }
137 if (src->mapped_to) {
138 dst->mapped_to = src->mapped_to;
139 }
140 if (src->mapped_to_output) {
141 free(dst->mapped_to_output);
142 dst->mapped_to_output = strdup(src->mapped_to_output);
143 }
144 if (src->mapped_to_region) {
145 free(dst->mapped_to_region);
146 dst->mapped_to_region =
147 malloc(sizeof(struct wlr_box));
148 memcpy(dst->mapped_to_region, src->mapped_to_region,
149 sizeof(struct wlr_box));
150 }
151 if (src->calibration_matrix.configured) {
152 dst->calibration_matrix.configured = src->calibration_matrix.configured;
153 memcpy(dst->calibration_matrix.matrix, src->calibration_matrix.matrix,
154 sizeof(src->calibration_matrix.matrix));
155 }
156 }
157
validate_xkb_merge(struct input_config * dest,struct input_config * src,char ** xkb_error)158 static bool validate_xkb_merge(struct input_config *dest,
159 struct input_config *src, char **xkb_error) {
160 struct input_config *temp = new_input_config("temp");
161 if (dest) {
162 merge_input_config(temp, dest);
163 }
164 merge_input_config(temp, src);
165
166 struct xkb_keymap *keymap = sway_keyboard_compile_keymap(temp, xkb_error);
167 free_input_config(temp);
168 if (!keymap) {
169 return false;
170 }
171
172 xkb_keymap_unref(keymap);
173 return true;
174 }
175
validate_wildcard_on_all(struct input_config * wildcard,char ** error)176 static bool validate_wildcard_on_all(struct input_config *wildcard,
177 char **error) {
178 for (int i = 0; i < config->input_configs->length; i++) {
179 struct input_config *ic = config->input_configs->items[i];
180 if (strcmp(wildcard->identifier, ic->identifier) != 0) {
181 sway_log(SWAY_DEBUG, "Validating xkb merge of * on %s",
182 ic->identifier);
183 if (!validate_xkb_merge(ic, wildcard, error)) {
184 return false;
185 }
186 }
187 }
188
189 for (int i = 0; i < config->input_type_configs->length; i++) {
190 struct input_config *ic = config->input_type_configs->items[i];
191 sway_log(SWAY_DEBUG, "Validating xkb merge of * config on %s",
192 ic->identifier);
193 if (!validate_xkb_merge(ic, wildcard, error)) {
194 return false;
195 }
196 }
197
198 return true;
199 }
200
merge_wildcard_on_all(struct input_config * wildcard)201 static void merge_wildcard_on_all(struct input_config *wildcard) {
202 for (int i = 0; i < config->input_configs->length; i++) {
203 struct input_config *ic = config->input_configs->items[i];
204 if (strcmp(wildcard->identifier, ic->identifier) != 0) {
205 sway_log(SWAY_DEBUG, "Merging input * config on %s", ic->identifier);
206 merge_input_config(ic, wildcard);
207 }
208 }
209
210 for (int i = 0; i < config->input_type_configs->length; i++) {
211 struct input_config *ic = config->input_type_configs->items[i];
212 sway_log(SWAY_DEBUG, "Merging input * config on %s", ic->identifier);
213 merge_input_config(ic, wildcard);
214 }
215 }
216
validate_type_on_existing(struct input_config * type_wildcard,char ** error)217 static bool validate_type_on_existing(struct input_config *type_wildcard,
218 char **error) {
219 for (int i = 0; i < config->input_configs->length; i++) {
220 struct input_config *ic = config->input_configs->items[i];
221 if (ic->input_type == NULL) {
222 continue;
223 }
224
225 if (strcmp(ic->input_type, type_wildcard->identifier + 5) == 0) {
226 sway_log(SWAY_DEBUG, "Validating merge of %s on %s",
227 type_wildcard->identifier, ic->identifier);
228 if (!validate_xkb_merge(ic, type_wildcard, error)) {
229 return false;
230 }
231 }
232 }
233 return true;
234 }
235
merge_type_on_existing(struct input_config * type_wildcard)236 static void merge_type_on_existing(struct input_config *type_wildcard) {
237 for (int i = 0; i < config->input_configs->length; i++) {
238 struct input_config *ic = config->input_configs->items[i];
239 if (ic->input_type == NULL) {
240 continue;
241 }
242
243 if (strcmp(ic->input_type, type_wildcard->identifier + 5) == 0) {
244 sway_log(SWAY_DEBUG, "Merging %s top of %s",
245 type_wildcard->identifier,
246 ic->identifier);
247 merge_input_config(ic, type_wildcard);
248 }
249 }
250 }
251
set_input_type(struct input_config * ic)252 static const char *set_input_type(struct input_config *ic) {
253 struct sway_input_device *input_device;
254 wl_list_for_each(input_device, &server.input->devices, link) {
255 if (strcmp(input_device->identifier, ic->identifier) == 0) {
256 ic->input_type = input_device_get_type(input_device);
257 break;
258 }
259 }
260 return ic->input_type;
261 }
262
store_input_config(struct input_config * ic,char ** error)263 struct input_config *store_input_config(struct input_config *ic,
264 char **error) {
265 bool wildcard = strcmp(ic->identifier, "*") == 0;
266 if (wildcard && error && !validate_wildcard_on_all(ic, error)) {
267 return NULL;
268 }
269
270 bool type = strncmp(ic->identifier, "type:", strlen("type:")) == 0;
271 if (type && error && !validate_type_on_existing(ic, error)) {
272 return NULL;
273 }
274
275 list_t *config_list = type ? config->input_type_configs
276 : config->input_configs;
277
278 struct input_config *current = NULL;
279 bool new_current = false;
280
281 int i = list_seq_find(config_list, input_identifier_cmp, ic->identifier);
282 if (i >= 0) {
283 current = config_list->items[i];
284 }
285
286 if (!current && !wildcard && !type && set_input_type(ic)) {
287 for (i = 0; i < config->input_type_configs->length; i++) {
288 struct input_config *tc = config->input_type_configs->items[i];
289 if (strcmp(ic->input_type, tc->identifier + 5) == 0) {
290 current = new_input_config(ic->identifier);
291 current->input_type = ic->input_type;
292 merge_input_config(current, tc);
293 new_current = true;
294 break;
295 }
296 }
297 }
298
299 i = list_seq_find(config->input_configs, input_identifier_cmp, "*");
300 if (!current && i >= 0) {
301 current = new_input_config(ic->identifier);
302 merge_input_config(current, config->input_configs->items[i]);
303 new_current = true;
304 }
305
306 if (error && !validate_xkb_merge(current, ic, error)) {
307 if (new_current) {
308 free_input_config(current);
309 }
310 return NULL;
311 }
312
313 if (wildcard) {
314 merge_wildcard_on_all(ic);
315 }
316
317 if (type) {
318 merge_type_on_existing(ic);
319 }
320
321 if (current) {
322 merge_input_config(current, ic);
323 free_input_config(ic);
324 ic = current;
325 }
326
327 ic->xkb_file_is_set = ic->xkb_file != NULL;
328
329 if (!current || new_current) {
330 list_add(config_list, ic);
331 }
332
333 sway_log(SWAY_DEBUG, "Config stored for input %s", ic->identifier);
334
335 return ic;
336 }
337
input_config_fill_rule_names(struct input_config * ic,struct xkb_rule_names * rules)338 void input_config_fill_rule_names(struct input_config *ic,
339 struct xkb_rule_names *rules) {
340 rules->layout = ic->xkb_layout;
341 rules->model = ic->xkb_model;
342 rules->options = ic->xkb_options;
343 rules->rules = ic->xkb_rules;
344 rules->variant = ic->xkb_variant;
345 }
346
free_input_config(struct input_config * ic)347 void free_input_config(struct input_config *ic) {
348 if (!ic) {
349 return;
350 }
351 free(ic->identifier);
352 free(ic->xkb_file);
353 free(ic->xkb_layout);
354 free(ic->xkb_model);
355 free(ic->xkb_options);
356 free(ic->xkb_rules);
357 free(ic->xkb_variant);
358 free(ic->mapped_from_region);
359 free(ic->mapped_to_output);
360 free(ic->mapped_to_region);
361 free(ic);
362 }
363
input_identifier_cmp(const void * item,const void * data)364 int input_identifier_cmp(const void *item, const void *data) {
365 const struct input_config *ic = item;
366 const char *identifier = data;
367 return strcmp(ic->identifier, identifier);
368 }
369