summaryrefslogtreecommitdiffstats
path: root/sway/input/seat.c
diff options
context:
space:
mode:
Diffstat (limited to 'sway/input/seat.c')
-rw-r--r--sway/input/seat.c675
1 files changed, 675 insertions, 0 deletions
diff --git a/sway/input/seat.c b/sway/input/seat.c
new file mode 100644
index 00000000..467e5087
--- /dev/null
+++ b/sway/input/seat.c
@@ -0,0 +1,675 @@
1#define _XOPEN_SOURCE 700
2#define _POSIX_C_SOURCE 199309L
3#include <assert.h>
4#include <strings.h>
5#include <time.h>
6#include <wlr/types/wlr_cursor.h>
7#include <wlr/types/wlr_output_layout.h>
8#include <wlr/types/wlr_xcursor_manager.h>
9#include "sway/debug.h"
10#include "sway/tree/container.h"
11#include "sway/tree/workspace.h"
12#include "sway/input/seat.h"
13#include "sway/input/cursor.h"
14#include "sway/input/input-manager.h"
15#include "sway/input/keyboard.h"
16#include "sway/ipc-server.h"
17#include "sway/layers.h"
18#include "sway/output.h"
19#include "sway/tree/container.h"
20#include "sway/tree/view.h"
21#include "log.h"
22
23static void seat_device_destroy(struct sway_seat_device *seat_device) {
24 if (!seat_device) {
25 return;
26 }
27
28 sway_keyboard_destroy(seat_device->keyboard);
29 wlr_cursor_detach_input_device(seat_device->sway_seat->cursor->cursor,
30 seat_device->input_device->wlr_device);
31 wl_list_remove(&seat_device->link);
32 free(seat_device);
33}
34
35void seat_destroy(struct sway_seat *seat) {
36 struct sway_seat_device *seat_device, *next;
37 wl_list_for_each_safe(seat_device, next, &seat->devices, link) {
38 seat_device_destroy(seat_device);
39 }
40 sway_cursor_destroy(seat->cursor);
41 wl_list_remove(&seat->link);
42 wlr_seat_destroy(seat->wlr_seat);
43}
44
45static struct sway_seat_container *seat_container_from_container(
46 struct sway_seat *seat, struct sway_container *con);
47
48static void seat_container_destroy(struct sway_seat_container *seat_con) {
49 struct sway_container *con = seat_con->container;
50 struct sway_container *child = NULL;
51
52 if (con->children != NULL) {
53 for (int i = 0; i < con->children->length; ++i) {
54 child = con->children->items[i];
55 struct sway_seat_container *seat_child =
56 seat_container_from_container(seat_con->seat, child);
57 seat_container_destroy(seat_child);
58 }
59 }
60
61 wl_list_remove(&seat_con->destroy.link);
62 wl_list_remove(&seat_con->link);
63 free(seat_con);
64}
65
66static void seat_send_focus(struct sway_seat *seat,
67 struct sway_container *con) {
68 if (con->type != C_VIEW) {
69 return;
70 }
71 struct sway_view *view = con->sway_view;
72 if (view->type == SWAY_VIEW_XWAYLAND) {
73 struct wlr_xwayland *xwayland =
74 seat->input->server->xwayland;
75 wlr_xwayland_set_seat(xwayland, seat->wlr_seat);
76 }
77 view_set_activated(view, true);
78 struct wlr_keyboard *keyboard =
79 wlr_seat_get_keyboard(seat->wlr_seat);
80 if (keyboard) {
81 wlr_seat_keyboard_notify_enter(seat->wlr_seat,
82 view->surface, keyboard->keycodes,
83 keyboard->num_keycodes, &keyboard->modifiers);
84 } else {
85 wlr_seat_keyboard_notify_enter(
86 seat->wlr_seat, view->surface, NULL, 0, NULL);
87 }
88}
89
90static struct sway_container *seat_get_focus_by_type(struct sway_seat *seat,
91 struct sway_container *container, enum sway_container_type type) {
92 if (container->type == C_VIEW || container->children->length == 0) {
93 return container;
94 }
95
96 struct sway_seat_container *current = NULL;
97 wl_list_for_each(current, &seat->focus_stack, link) {
98 if (current->container->type != type && type != C_TYPES) {
99 continue;
100 }
101
102 if (container_has_child(container, current->container)) {
103 return current->container;
104 }
105 }
106
107 return NULL;
108}
109
110void seat_focus_inactive_children_for_each(struct sway_seat *seat,
111 struct sway_container *container,
112 void (*f)(struct sway_container *container, void *data), void *data) {
113 struct sway_seat_container *current = NULL;
114 wl_list_for_each(current, &seat->focus_stack, link) {
115 if (current->container->parent == NULL) {
116 continue;
117 }
118 if (current->container->parent == container) {
119 f(current->container, data);
120 }
121 }
122}
123
124struct sway_container *seat_get_focus_inactive_view(struct sway_seat *seat,
125 struct sway_container *container) {
126 return seat_get_focus_by_type(seat, container, C_VIEW);
127}
128
129static void handle_seat_container_destroy(struct wl_listener *listener,
130 void *data) {
131 struct sway_seat_container *seat_con =
132 wl_container_of(listener, seat_con, destroy);
133 struct sway_seat *seat = seat_con->seat;
134 struct sway_container *con = seat_con->container;
135 struct sway_container *parent = con->parent;
136 struct sway_container *focus = seat_get_focus(seat);
137
138 bool set_focus =
139 focus != NULL &&
140 (focus == con || container_has_child(con, focus)) &&
141 con->type != C_WORKSPACE;
142
143 seat_container_destroy(seat_con);
144
145 if (set_focus) {
146 struct sway_container *next_focus = NULL;
147 while (next_focus == NULL) {
148 next_focus = seat_get_focus_by_type(seat, parent, C_VIEW);
149
150 if (next_focus == NULL && parent->type == C_WORKSPACE) {
151 next_focus = parent;
152 break;
153 }
154
155 parent = parent->parent;
156 }
157
158 // the structure change might have caused it to move up to the top of
159 // the focus stack without sending focus notifications to the view
160 if (seat_get_focus(seat) == next_focus) {
161 seat_send_focus(seat, next_focus);
162 } else {
163 seat_set_focus(seat, next_focus);
164 }
165 }
166}
167
168static struct sway_seat_container *seat_container_from_container(
169 struct sway_seat *seat, struct sway_container *con) {
170 if (con->type == C_ROOT || con->type == C_OUTPUT) {
171 // these don't get seat containers ever
172 return NULL;
173 }
174
175 struct sway_seat_container *seat_con = NULL;
176 wl_list_for_each(seat_con, &seat->focus_stack, link) {
177 if (seat_con->container == con) {
178 return seat_con;
179 }
180 }
181
182 seat_con = calloc(1, sizeof(struct sway_seat_container));
183 if (seat_con == NULL) {
184 wlr_log(L_ERROR, "could not allocate seat container");
185 return NULL;
186 }
187
188 seat_con->container = con;
189 seat_con->seat = seat;
190 wl_list_insert(seat->focus_stack.prev, &seat_con->link);
191 wl_signal_add(&con->events.destroy, &seat_con->destroy);
192 seat_con->destroy.notify = handle_seat_container_destroy;
193
194 return seat_con;
195}
196
197static void handle_new_container(struct wl_listener *listener, void *data) {
198 struct sway_seat *seat = wl_container_of(listener, seat, new_container);
199 struct sway_container *con = data;
200 seat_container_from_container(seat, con);
201}
202
203static void collect_focus_iter(struct sway_container *con, void *data) {
204 struct sway_seat *seat = data;
205 if (con->type > C_WORKSPACE) {
206 return;
207 }
208 struct sway_seat_container *seat_con =
209 seat_container_from_container(seat, con);
210 if (!seat_con) {
211 return;
212 }
213 wl_list_remove(&seat_con->link);
214 wl_list_insert(&seat->focus_stack, &seat_con->link);
215}
216
217struct sway_seat *seat_create(struct sway_input_manager *input,
218 const char *seat_name) {
219 struct sway_seat *seat = calloc(1, sizeof(struct sway_seat));
220 if (!seat) {
221 return NULL;
222 }
223
224 seat->wlr_seat = wlr_seat_create(input->server->wl_display, seat_name);
225 if (!sway_assert(seat->wlr_seat, "could not allocate seat")) {
226 free(seat);
227 return NULL;
228 }
229
230 seat->cursor = sway_cursor_create(seat);
231 if (!seat->cursor) {
232 wlr_seat_destroy(seat->wlr_seat);
233 free(seat);
234 return NULL;
235 }
236
237 // init the focus stack
238 wl_list_init(&seat->focus_stack);
239
240 container_for_each_descendant_dfs(&root_container,
241 collect_focus_iter, seat);
242
243 wl_signal_add(&root_container.sway_root->events.new_container,
244 &seat->new_container);
245 seat->new_container.notify = handle_new_container;
246
247 seat->input = input;
248 wl_list_init(&seat->devices);
249
250 wlr_seat_set_capabilities(seat->wlr_seat,
251 WL_SEAT_CAPABILITY_KEYBOARD |
252 WL_SEAT_CAPABILITY_POINTER |
253 WL_SEAT_CAPABILITY_TOUCH);
254
255 seat_configure_xcursor(seat);
256
257 wl_list_insert(&input->seats, &seat->link);
258
259 return seat;
260}
261
262static void seat_apply_input_config(struct sway_seat *seat,
263 struct sway_seat_device *sway_device) {
264 struct input_config *ic = input_device_get_config(
265 sway_device->input_device);
266 if (!ic) {
267 return;
268 }
269 wlr_log(L_DEBUG, "Applying input config to %s",
270 sway_device->input_device->identifier);
271 if (ic->mapped_output) {
272 struct sway_container *output = NULL;
273 for (int i = 0; i < root_container.children->length; ++i) {
274 struct sway_container *_output = root_container.children->items[i];
275 if (strcasecmp(_output->name, ic->mapped_output) == 0) {
276 output = _output;
277 break;
278 }
279 }
280 if (output) {
281 wlr_cursor_map_input_to_output(seat->cursor->cursor,
282 sway_device->input_device->wlr_device,
283 output->sway_output->wlr_output);
284 wlr_log(L_DEBUG, "Mapped to output %s", output->name);
285 }
286 }
287}
288
289static void seat_configure_pointer(struct sway_seat *seat,
290 struct sway_seat_device *sway_device) {
291 wlr_cursor_attach_input_device(seat->cursor->cursor,
292 sway_device->input_device->wlr_device);
293 seat_apply_input_config(seat, sway_device);
294}
295
296static void seat_configure_keyboard(struct sway_seat *seat,
297 struct sway_seat_device *seat_device) {
298 if (!seat_device->keyboard) {
299 sway_keyboard_create(seat, seat_device);
300 }
301 struct wlr_keyboard *wlr_keyboard =
302 seat_device->input_device->wlr_device->keyboard;
303 sway_keyboard_configure(seat_device->keyboard);
304 wlr_seat_set_keyboard(seat->wlr_seat,
305 seat_device->input_device->wlr_device);
306 struct sway_container *focus = seat_get_focus(seat);
307 if (focus && focus->type == C_VIEW) {
308 // force notify reenter to pick up the new configuration
309 wlr_seat_keyboard_clear_focus(seat->wlr_seat);
310 wlr_seat_keyboard_notify_enter(seat->wlr_seat,
311 focus->sway_view->surface, wlr_keyboard->keycodes,
312 wlr_keyboard->num_keycodes, &wlr_keyboard->modifiers);
313 }
314}
315
316static void seat_configure_tablet_tool(struct sway_seat *seat,
317 struct sway_seat_device *sway_device) {
318 wlr_cursor_attach_input_device(seat->cursor->cursor,
319 sway_device->input_device->wlr_device);
320 seat_apply_input_config(seat, sway_device);
321}
322
323static struct sway_seat_device *seat_get_device(struct sway_seat *seat,
324 struct sway_input_device *input_device) {
325 struct sway_seat_device *seat_device = NULL;
326 wl_list_for_each(seat_device, &seat->devices, link) {
327 if (seat_device->input_device == input_device) {
328 return seat_device;
329 }
330 }
331
332 return NULL;
333}
334
335void seat_configure_device(struct sway_seat *seat,
336 struct sway_input_device *input_device) {
337 struct sway_seat_device *seat_device =
338 seat_get_device(seat, input_device);
339 if (!seat_device) {
340 return;
341 }
342
343 switch (input_device->wlr_device->type) {
344 case WLR_INPUT_DEVICE_POINTER:
345 seat_configure_pointer(seat, seat_device);
346 break;
347 case WLR_INPUT_DEVICE_KEYBOARD:
348 seat_configure_keyboard(seat, seat_device);
349 break;
350 case WLR_INPUT_DEVICE_TABLET_TOOL:
351 seat_configure_tablet_tool(seat, seat_device);
352 break;
353 case WLR_INPUT_DEVICE_TABLET_PAD:
354 case WLR_INPUT_DEVICE_TOUCH:
355 wlr_log(L_DEBUG, "TODO: configure other devices");
356 break;
357 }
358}
359
360void seat_add_device(struct sway_seat *seat,
361 struct sway_input_device *input_device) {
362 if (seat_get_device(seat, input_device)) {
363 seat_configure_device(seat, input_device);
364 return;
365 }
366
367 struct sway_seat_device *seat_device =
368 calloc(1, sizeof(struct sway_seat_device));
369 if (!seat_device) {
370 wlr_log(L_DEBUG, "could not allocate seat device");
371 return;
372 }
373
374 wlr_log(L_DEBUG, "adding device %s to seat %s",
375 input_device->identifier, seat->wlr_seat->name);
376
377 seat_device->sway_seat = seat;
378 seat_device->input_device = input_device;
379 wl_list_insert(&seat->devices, &seat_device->link);
380
381 seat_configure_device(seat, input_device);
382}
383
384void seat_remove_device(struct sway_seat *seat,
385 struct sway_input_device *input_device) {
386 struct sway_seat_device *seat_device =
387 seat_get_device(seat, input_device);
388
389 if (!seat_device) {
390 return;
391 }
392
393 wlr_log(L_DEBUG, "removing device %s from seat %s",
394 input_device->identifier, seat->wlr_seat->name);
395
396 seat_device_destroy(seat_device);
397}
398
399void seat_configure_xcursor(struct sway_seat *seat) {
400 // TODO configure theme and size
401 const char *cursor_theme = NULL;
402
403 if (!seat->cursor->xcursor_manager) {
404 seat->cursor->xcursor_manager =
405 wlr_xcursor_manager_create(cursor_theme, 24);
406 if (sway_assert(seat->cursor->xcursor_manager,
407 "Cannot create XCursor manager for theme %s",
408 cursor_theme)) {
409 return;
410 }
411 }
412
413 for (int i = 0; i < root_container.children->length; ++i) {
414 struct sway_container *output_container =
415 root_container.children->items[i];
416 struct wlr_output *output =
417 output_container->sway_output->wlr_output;
418 bool result =
419 wlr_xcursor_manager_load(seat->cursor->xcursor_manager,
420 output->scale);
421
422 sway_assert(!result,
423 "Cannot load xcursor theme for output '%s' with scale %f",
424 // TODO: Fractional scaling
425 output->name, (double)output->scale);
426 }
427
428 wlr_xcursor_manager_set_cursor_image(seat->cursor->xcursor_manager,
429 "left_ptr", seat->cursor->cursor);
430 wlr_cursor_warp(seat->cursor->cursor, NULL, seat->cursor->cursor->x,
431 seat->cursor->cursor->y);
432}
433
434bool seat_is_input_allowed(struct sway_seat *seat,
435 struct wlr_surface *surface) {
436 struct wl_client *client = wl_resource_get_client(surface->resource);
437 return !seat->exclusive_client || seat->exclusive_client == client;
438}
439
440void seat_set_focus_warp(struct sway_seat *seat,
441 struct sway_container *container, bool warp) {
442 if (seat->focused_layer) {
443 return;
444 }
445
446 struct sway_container *last_focus = seat_get_focus(seat);
447 if (container && last_focus == container) {
448 return;
449 }
450
451 if (container) {
452 struct sway_seat_container *seat_con =
453 seat_container_from_container(seat, container);
454 if (seat_con == NULL) {
455 return;
456 }
457
458 // put all the anscestors of this container on top of the focus stack
459 struct sway_seat_container *parent =
460 seat_container_from_container(seat,
461 seat_con->container->parent);
462 while (parent) {
463 wl_list_remove(&parent->link);
464 wl_list_insert(&seat->focus_stack, &parent->link);
465
466 parent =
467 seat_container_from_container(seat,
468 parent->container->parent);
469 }
470
471 wl_list_remove(&seat_con->link);
472 wl_list_insert(&seat->focus_stack, &seat_con->link);
473
474 if (container->type == C_VIEW && !seat_is_input_allowed(
475 seat, container->sway_view->surface)) {
476 wlr_log(L_DEBUG, "Refusing to set focus, input is inhibited");
477 return;
478 }
479
480 if (container->type == C_VIEW) {
481 seat_send_focus(seat, container);
482 }
483 }
484
485 if (last_focus) {
486 struct sway_container *last_ws = last_focus;
487 if (last_ws && last_ws->type != C_WORKSPACE) {
488 last_ws = container_parent(last_ws, C_WORKSPACE);
489 }
490 if (last_ws) {
491 ipc_event_workspace(last_ws, container, "focus");
492 if (!workspace_is_visible(last_ws)
493 && last_ws->children->length == 0) {
494 container_destroy(last_ws);
495 }
496 }
497
498 if (config->mouse_warping && warp) {
499 struct sway_container *last_output = last_focus;
500 if (last_output && last_output->type != C_OUTPUT) {
501 last_output = container_parent(last_output, C_OUTPUT);
502 }
503 struct sway_container *new_output = container;
504 if (new_output && new_output->type != C_OUTPUT) {
505 new_output = container_parent(new_output, C_OUTPUT);
506 }
507 if (new_output && last_output && new_output != last_output) {
508 double x = new_output->x + container->x +
509 container->width / 2.0;
510 double y = new_output->y + container->y +
511 container->height / 2.0;
512 struct wlr_output *wlr_output =
513 new_output->sway_output->wlr_output;
514 if (!wlr_output_layout_contains_point(
515 root_container.sway_root->output_layout,
516 wlr_output, seat->cursor->cursor->x,
517 seat->cursor->cursor->y)) {
518 wlr_cursor_warp(seat->cursor->cursor, NULL, x, y);
519 struct timespec now;
520 clock_gettime(CLOCK_MONOTONIC, &now);
521 cursor_send_pointer_motion(seat->cursor, now.tv_nsec / 1000);
522 }
523 }
524 }
525 }
526
527 if (last_focus && last_focus->type == C_VIEW &&
528 !input_manager_has_focus(seat->input, last_focus)) {
529 struct sway_view *view = last_focus->sway_view;
530 view_set_activated(view, false);
531 }
532
533 seat->has_focus = (container != NULL);
534
535 update_debug_tree();
536}
537
538void seat_set_focus(struct sway_seat *seat,
539 struct sway_container *container) {
540 seat_set_focus_warp(seat, container, true);
541}
542
543void seat_set_focus_surface(struct sway_seat *seat,
544 struct wlr_surface *surface) {
545 if (seat->focused_layer != NULL) {
546 return;
547 }
548 if (seat->has_focus) {
549 struct sway_container *focus = seat_get_focus(seat);
550 if (focus->type == C_VIEW) {
551 wlr_seat_keyboard_clear_focus(seat->wlr_seat);
552 view_set_activated(focus->sway_view, false);
553 }
554 seat->has_focus = false;
555 }
556 struct wlr_keyboard *keyboard =
557 wlr_seat_get_keyboard(seat->wlr_seat);
558 if (keyboard) {
559 wlr_seat_keyboard_notify_enter(seat->wlr_seat, surface,
560 keyboard->keycodes, keyboard->num_keycodes, &keyboard->modifiers);
561 } else {
562 wlr_seat_keyboard_notify_enter(seat->wlr_seat, surface, NULL, 0, NULL);
563 }
564}
565
566void seat_set_focus_layer(struct sway_seat *seat,
567 struct wlr_layer_surface *layer) {
568 if (!layer && seat->focused_layer) {
569 seat->focused_layer = NULL;
570 struct sway_container *previous = seat_get_focus(seat);
571 if (previous) {
572 wlr_log(L_DEBUG, "Returning focus to %p %s '%s'", previous,
573 container_type_to_str(previous->type), previous->name);
574 // Hack to get seat to re-focus the return value of get_focus
575 seat_set_focus(seat, previous->parent);
576 seat_set_focus(seat, previous);
577 }
578 return;
579 } else if (!layer || seat->focused_layer == layer) {
580 return;
581 }
582 seat_set_focus_surface(seat, layer->surface);
583 if (layer->layer >= ZWLR_LAYER_SHELL_V1_LAYER_TOP) {
584 seat->focused_layer = layer;
585 }
586}
587
588void seat_set_exclusive_client(struct sway_seat *seat,
589 struct wl_client *client) {
590 if (!client) {
591 seat->exclusive_client = client;
592 // Triggers a refocus of the topmost surface layer if necessary
593 // TODO: Make layer surface focus per-output based on cursor position
594 for (int i = 0; i < root_container.children->length; ++i) {
595 struct sway_container *output = root_container.children->items[i];
596 if (!sway_assert(output->type == C_OUTPUT,
597 "root container has non-output child")) {
598 continue;
599 }
600 arrange_layers(output->sway_output);
601 }
602 return;
603 }
604 if (seat->focused_layer) {
605 if (wl_resource_get_client(seat->focused_layer->resource) != client) {
606 seat_set_focus_layer(seat, NULL);
607 }
608 }
609 if (seat->has_focus) {
610 struct sway_container *focus = seat_get_focus(seat);
611 if (focus->type == C_VIEW && wl_resource_get_client(
612 focus->sway_view->surface->resource) != client) {
613 seat_set_focus(seat, NULL);
614 }
615 }
616 if (seat->wlr_seat->pointer_state.focused_client) {
617 if (seat->wlr_seat->pointer_state.focused_client->client != client) {
618 wlr_seat_pointer_clear_focus(seat->wlr_seat);
619 }
620 }
621 struct timespec now;
622 clock_gettime(CLOCK_MONOTONIC, &now);
623 struct wlr_touch_point *point;
624 wl_list_for_each(point, &seat->wlr_seat->touch_state.touch_points, link) {
625 if (point->client->client != client) {
626 wlr_seat_touch_point_clear_focus(seat->wlr_seat,
627 now.tv_nsec / 1000, point->touch_id);
628 }
629 }
630 seat->exclusive_client = client;
631}
632
633struct sway_container *seat_get_focus_inactive(struct sway_seat *seat,
634 struct sway_container *container) {
635 return seat_get_focus_by_type(seat, container, C_TYPES);
636}
637
638struct sway_container *sway_seat_get_focus(struct sway_seat *seat) {
639 if (!seat->has_focus) {
640 return NULL;
641 }
642 return seat_get_focus_inactive(seat, &root_container);
643}
644
645struct sway_container *seat_get_focus(struct sway_seat *seat) {
646 if (!seat->has_focus) {
647 return NULL;
648 }
649 return seat_get_focus_inactive(seat, &root_container);
650}
651
652void seat_apply_config(struct sway_seat *seat,
653 struct seat_config *seat_config) {
654 struct sway_seat_device *seat_device = NULL;
655
656 if (!seat_config) {
657 return;
658 }
659
660 wl_list_for_each(seat_device, &seat->devices, link) {
661 seat_configure_device(seat, seat_device->input_device);
662 }
663}
664
665struct seat_config *seat_get_config(struct sway_seat *seat) {
666 struct seat_config *seat_config = NULL;
667 for (int i = 0; i < config->seat_configs->length; ++i ) {
668 seat_config = config->seat_configs->items[i];
669 if (strcmp(seat->wlr_seat->name, seat_config->name) == 0) {
670 return seat_config;
671 }
672 }
673
674 return NULL;
675}