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