summaryrefslogtreecommitdiffstats
path: root/sway/input/input-manager.c
diff options
context:
space:
mode:
Diffstat (limited to 'sway/input/input-manager.c')
-rw-r--r--sway/input/input-manager.c445
1 files changed, 445 insertions, 0 deletions
diff --git a/sway/input/input-manager.c b/sway/input/input-manager.c
new file mode 100644
index 00000000..ae55d2a1
--- /dev/null
+++ b/sway/input/input-manager.c
@@ -0,0 +1,445 @@
1#define _XOPEN_SOURCE 700
2#include <ctype.h>
3#include <float.h>
4#include <limits.h>
5#include <stdio.h>
6#include <string.h>
7#include <libinput.h>
8#include <math.h>
9#include <wlr/backend/libinput.h>
10#include <wlr/types/wlr_input_inhibitor.h>
11#include "sway/config.h"
12#include "sway/input/input-manager.h"
13#include "sway/input/seat.h"
14#include "sway/server.h"
15#include "stringop.h"
16#include "list.h"
17#include "log.h"
18
19static const char *default_seat = "seat0";
20
21// TODO make me not global
22struct sway_input_manager *input_manager;
23
24struct input_config *current_input_config = NULL;
25struct seat_config *current_seat_config = NULL;
26
27struct sway_seat *input_manager_current_seat(struct sway_input_manager *input) {
28 struct sway_seat *seat = config->handler_context.seat;
29 if (!seat) {
30 seat = input_manager_get_default_seat(input_manager);
31 }
32 return seat;
33}
34
35struct sway_seat *input_manager_get_seat(
36 struct sway_input_manager *input, const char *seat_name) {
37 struct sway_seat *seat = NULL;
38 wl_list_for_each(seat, &input->seats, link) {
39 if (strcmp(seat->wlr_seat->name, seat_name) == 0) {
40 return seat;
41 }
42 }
43
44 return seat_create(input, seat_name);
45}
46
47static char *get_device_identifier(struct wlr_input_device *device) {
48 int vendor = device->vendor;
49 int product = device->product;
50 char *name = strdup(device->name);
51 name = strip_whitespace(name);
52
53 char *p = name;
54 for (; *p; ++p) {
55 if (*p == ' ') {
56 *p = '_';
57 }
58 }
59
60 const char *fmt = "%d:%d:%s";
61 int len = snprintf(NULL, 0, fmt, vendor, product, name) + 1;
62 char *identifier = malloc(len);
63 if (!identifier) {
64 wlr_log(L_ERROR, "Unable to allocate unique input device name");
65 return NULL;
66 }
67
68 snprintf(identifier, len, fmt, vendor, product, name);
69 free(name);
70 return identifier;
71}
72
73static struct sway_input_device *input_sway_device_from_wlr(
74 struct sway_input_manager *input, struct wlr_input_device *device) {
75 struct sway_input_device *input_device = NULL;
76 wl_list_for_each(input_device, &input->devices, link) {
77 if (input_device->wlr_device == device) {
78 return input_device;
79 }
80 }
81 return NULL;
82}
83
84static bool input_has_seat_configuration(struct sway_input_manager *input) {
85 struct sway_seat *seat = NULL;
86 wl_list_for_each(seat, &input->seats, link) {
87 struct seat_config *seat_config = seat_get_config(seat);
88 if (seat_config) {
89 return true;
90 }
91 }
92
93 return false;
94}
95
96static void input_manager_libinput_config_pointer(
97 struct sway_input_device *input_device) {
98 struct wlr_input_device *wlr_device = input_device->wlr_device;
99 struct input_config *ic = input_device_get_config(input_device);
100 struct libinput_device *libinput_device;
101
102 if (!ic || !wlr_input_device_is_libinput(wlr_device)) {
103 return;
104 }
105
106 libinput_device = wlr_libinput_get_device_handle(wlr_device);
107 wlr_log(L_DEBUG, "input_manager_libinput_config_pointer(%s)",
108 ic->identifier);
109
110 if (ic->accel_profile != INT_MIN) {
111 wlr_log(L_DEBUG, "libinput_config_pointer(%s) accel_set_profile(%d)",
112 ic->identifier, ic->accel_profile);
113 libinput_device_config_accel_set_profile(libinput_device,
114 ic->accel_profile);
115 }
116 if (ic->click_method != INT_MIN) {
117 wlr_log(L_DEBUG, "libinput_config_pointer(%s) click_set_method(%d)",
118 ic->identifier, ic->click_method);
119 libinput_device_config_click_set_method(libinput_device,
120 ic->click_method);
121 }
122 if (ic->drag_lock != INT_MIN) {
123 wlr_log(L_DEBUG,
124 "libinput_config_pointer(%s) tap_set_drag_lock_enabled(%d)",
125 ic->identifier, ic->click_method);
126 libinput_device_config_tap_set_drag_lock_enabled(libinput_device,
127 ic->drag_lock);
128 }
129 if (ic->dwt != INT_MIN) {
130 wlr_log(L_DEBUG, "libinput_config_pointer(%s) dwt_set_enabled(%d)",
131 ic->identifier, ic->dwt);
132 libinput_device_config_dwt_set_enabled(libinput_device, ic->dwt);
133 }
134 if (ic->left_handed != INT_MIN) {
135 wlr_log(L_DEBUG,
136 "libinput_config_pointer(%s) left_handed_set_enabled(%d)",
137 ic->identifier, ic->left_handed);
138 libinput_device_config_left_handed_set(libinput_device,
139 ic->left_handed);
140 }
141 if (ic->middle_emulation != INT_MIN) {
142 wlr_log(L_DEBUG,
143 "libinput_config_pointer(%s) middle_emulation_set_enabled(%d)",
144 ic->identifier, ic->middle_emulation);
145 libinput_device_config_middle_emulation_set_enabled(libinput_device,
146 ic->middle_emulation);
147 }
148 if (ic->natural_scroll != INT_MIN) {
149 wlr_log(L_DEBUG,
150 "libinput_config_pointer(%s) natural_scroll_set_enabled(%d)",
151 ic->identifier, ic->natural_scroll);
152 libinput_device_config_scroll_set_natural_scroll_enabled(
153 libinput_device, ic->natural_scroll);
154 }
155 if (ic->pointer_accel != FLT_MIN) {
156 wlr_log(L_DEBUG, "libinput_config_pointer(%s) accel_set_speed(%f)",
157 ic->identifier, ic->pointer_accel);
158 libinput_device_config_accel_set_speed(libinput_device,
159 ic->pointer_accel);
160 }
161 if (ic->scroll_method != INT_MIN) {
162 wlr_log(L_DEBUG, "libinput_config_pointer(%s) scroll_set_method(%d)",
163 ic->identifier, ic->scroll_method);
164 libinput_device_config_scroll_set_method(libinput_device,
165 ic->scroll_method);
166 }
167 if (ic->send_events != INT_MIN) {
168 wlr_log(L_DEBUG, "libinput_config_pointer(%s) send_events_set_mode(%d)",
169 ic->identifier, ic->send_events);
170 libinput_device_config_send_events_set_mode(libinput_device,
171 ic->send_events);
172 }
173 if (ic->tap != INT_MIN) {
174 wlr_log(L_DEBUG, "libinput_config_pointer(%s) tap_set_enabled(%d)",
175 ic->identifier, ic->tap);
176 libinput_device_config_tap_set_enabled(libinput_device, ic->tap);
177 }
178}
179
180static void handle_device_destroy(struct wl_listener *listener, void *data) {
181 struct wlr_input_device *device = data;
182
183 struct sway_input_device *input_device =
184 input_sway_device_from_wlr(input_manager, device);
185
186 if (!sway_assert(input_device, "could not find sway device")) {
187 return;
188 }
189
190 wlr_log(L_DEBUG, "removing device: '%s'",
191 input_device->identifier);
192
193 struct sway_seat *seat = NULL;
194 wl_list_for_each(seat, &input_manager->seats, link) {
195 seat_remove_device(seat, input_device);
196 }
197
198 wl_list_remove(&input_device->link);
199 wl_list_remove(&input_device->device_destroy.link);
200 free(input_device->identifier);
201 free(input_device);
202}
203
204static void handle_new_input(struct wl_listener *listener, void *data) {
205 struct sway_input_manager *input =
206 wl_container_of(listener, input, new_input);
207 struct wlr_input_device *device = data;
208
209 struct sway_input_device *input_device =
210 calloc(1, sizeof(struct sway_input_device));
211 if (!sway_assert(input_device, "could not allocate input device")) {
212 return;
213 }
214
215 input_device->wlr_device = device;
216 input_device->identifier = get_device_identifier(device);
217 wl_list_insert(&input->devices, &input_device->link);
218
219 wlr_log(L_DEBUG, "adding device: '%s'",
220 input_device->identifier);
221
222 if (input_device->wlr_device->type == WLR_INPUT_DEVICE_POINTER) {
223 input_manager_libinput_config_pointer(input_device);
224 }
225
226 struct sway_seat *seat = NULL;
227 if (!input_has_seat_configuration(input)) {
228 wlr_log(L_DEBUG, "no seat configuration, using default seat");
229 seat = input_manager_get_seat(input, default_seat);
230 seat_add_device(seat, input_device);
231 return;
232 }
233
234 bool added = false;
235 wl_list_for_each(seat, &input->seats, link) {
236 struct seat_config *seat_config = seat_get_config(seat);
237 bool has_attachment = seat_config &&
238 (seat_config_get_attachment(seat_config, input_device->identifier) ||
239 seat_config_get_attachment(seat_config, "*"));
240
241 if (has_attachment) {
242 seat_add_device(seat, input_device);
243 added = true;
244 }
245 }
246
247 if (!added) {
248 wl_list_for_each(seat, &input->seats, link) {
249 struct seat_config *seat_config = seat_get_config(seat);
250 if (seat_config && seat_config->fallback == 1) {
251 seat_add_device(seat, input_device);
252 added = true;
253 }
254 }
255 }
256
257 if (!added) {
258 wlr_log(L_DEBUG,
259 "device '%s' is not configured on any seats",
260 input_device->identifier);
261 }
262
263 wl_signal_add(&device->events.destroy, &input_device->device_destroy);
264 input_device->device_destroy.notify = handle_device_destroy;
265}
266
267static void handle_inhibit_activate(struct wl_listener *listener, void *data) {
268 struct sway_input_manager *input_manager = wl_container_of(
269 listener, input_manager, inhibit_activate);
270 struct sway_seat *seat;
271 wl_list_for_each(seat, &input_manager->seats, link) {
272 seat_set_exclusive_client(seat, input_manager->inhibit->active_client);
273 }
274}
275
276static void handle_inhibit_deactivate(struct wl_listener *listener, void *data) {
277 struct sway_input_manager *input_manager = wl_container_of(
278 listener, input_manager, inhibit_deactivate);
279 struct sway_seat *seat;
280 wl_list_for_each(seat, &input_manager->seats, link) {
281 seat_set_exclusive_client(seat, NULL);
282 struct sway_container *previous = seat_get_focus(seat);
283 if (previous) {
284 wlr_log(L_DEBUG, "Returning focus to %p %s '%s'", previous,
285 container_type_to_str(previous->type), previous->name);
286 // Hack to get seat to re-focus the return value of get_focus
287 seat_set_focus(seat, previous->parent);
288 seat_set_focus(seat, previous);
289 }
290 }
291}
292
293struct sway_input_manager *input_manager_create(
294 struct sway_server *server) {
295 struct sway_input_manager *input =
296 calloc(1, sizeof(struct sway_input_manager));
297 if (!input) {
298 return NULL;
299 }
300 input->server = server;
301
302 wl_list_init(&input->devices);
303 wl_list_init(&input->seats);
304
305 // create the default seat
306 input_manager_get_seat(input, default_seat);
307
308 input->new_input.notify = handle_new_input;
309 wl_signal_add(&server->backend->events.new_input, &input->new_input);
310
311 input->inhibit = wlr_input_inhibit_manager_create(server->wl_display);
312 input->inhibit_activate.notify = handle_inhibit_activate;
313 wl_signal_add(&input->inhibit->events.activate,
314 &input->inhibit_activate);
315 input->inhibit_deactivate.notify = handle_inhibit_deactivate;
316 wl_signal_add(&input->inhibit->events.deactivate,
317 &input->inhibit_deactivate);
318
319 return input;
320}
321
322bool input_manager_has_focus(struct sway_input_manager *input,
323 struct sway_container *container) {
324 struct sway_seat *seat = NULL;
325 wl_list_for_each(seat, &input->seats, link) {
326 if (seat_get_focus(seat) == container) {
327 return true;
328 }
329 }
330
331 return false;
332}
333
334void input_manager_set_focus(struct sway_input_manager *input,
335 struct sway_container *container) {
336 struct sway_seat *seat;
337 wl_list_for_each(seat, &input->seats, link) {
338 seat_set_focus(seat, container);
339 }
340}
341
342void input_manager_apply_input_config(struct sway_input_manager *input,
343 struct input_config *input_config) {
344 struct sway_input_device *input_device = NULL;
345 wl_list_for_each(input_device, &input->devices, link) {
346 if (strcmp(input_device->identifier, input_config->identifier) == 0) {
347 if (input_device->wlr_device->type == WLR_INPUT_DEVICE_POINTER) {
348 input_manager_libinput_config_pointer(input_device);
349 }
350
351 struct sway_seat *seat = NULL;
352 wl_list_for_each(seat, &input->seats, link) {
353 seat_configure_device(seat, input_device);
354 }
355 }
356 }
357}
358
359void input_manager_apply_seat_config(struct sway_input_manager *input,
360 struct seat_config *seat_config) {
361 wlr_log(L_DEBUG, "applying new seat config for seat %s",
362 seat_config->name);
363 struct sway_seat *seat = input_manager_get_seat(input, seat_config->name);
364 if (!seat) {
365 return;
366 }
367
368 seat_apply_config(seat, seat_config);
369
370 // for every device, try to add it to a seat and if no seat has it
371 // attached, add it to the fallback seats.
372 struct sway_input_device *input_device = NULL;
373 wl_list_for_each(input_device, &input->devices, link) {
374 list_t *seat_list = create_list();
375 struct sway_seat *seat = NULL;
376 wl_list_for_each(seat, &input->seats, link) {
377 struct seat_config *seat_config = seat_get_config(seat);
378 if (!seat_config) {
379 continue;
380 }
381 if (seat_config_get_attachment(seat_config, "*") ||
382 seat_config_get_attachment(seat_config,
383 input_device->identifier)) {
384 list_add(seat_list, seat);
385 }
386 }
387
388 if (seat_list->length) {
389 wl_list_for_each(seat, &input->seats, link) {
390 bool attached = false;
391 for (int i = 0; i < seat_list->length; ++i) {
392 if (seat == seat_list->items[i]) {
393 attached = true;
394 break;
395 }
396 }
397 if (attached) {
398 seat_add_device(seat, input_device);
399 } else {
400 seat_remove_device(seat, input_device);
401 }
402 }
403 } else {
404 wl_list_for_each(seat, &input->seats, link) {
405 struct seat_config *seat_config = seat_get_config(seat);
406 if (seat_config && seat_config->fallback == 1) {
407 seat_add_device(seat, input_device);
408 } else {
409 seat_remove_device(seat, input_device);
410 }
411 }
412 }
413 list_free(seat_list);
414 }
415}
416
417void input_manager_configure_xcursor(struct sway_input_manager *input) {
418 struct sway_seat *seat = NULL;
419 wl_list_for_each(seat, &input->seats, link) {
420 seat_configure_xcursor(seat);
421 }
422}
423
424struct sway_seat *input_manager_get_default_seat(
425 struct sway_input_manager *input) {
426 struct sway_seat *seat = NULL;
427 wl_list_for_each(seat, &input->seats, link) {
428 if (strcmp(seat->wlr_seat->name, "seat0") == 0) {
429 return seat;
430 }
431 }
432 return seat;
433}
434
435struct input_config *input_device_get_config(struct sway_input_device *device) {
436 struct input_config *input_config = NULL;
437 for (int i = 0; i < config->input_configs->length; ++i) {
438 input_config = config->input_configs->items[i];
439 if (strcmp(input_config->identifier, device->identifier) == 0) {
440 return input_config;
441 }
442 }
443
444 return NULL;
445}