summaryrefslogtreecommitdiffstats
path: root/sway/input
diff options
context:
space:
mode:
Diffstat (limited to 'sway/input')
-rw-r--r--sway/input/cursor.c384
-rw-r--r--sway/input/input-manager.c445
-rw-r--r--sway/input/keyboard.c504
-rw-r--r--sway/input/seat.c675
4 files changed, 2008 insertions, 0 deletions
diff --git a/sway/input/cursor.c b/sway/input/cursor.c
new file mode 100644
index 00000000..15a61cbf
--- /dev/null
+++ b/sway/input/cursor.c
@@ -0,0 +1,384 @@
1#define _XOPEN_SOURCE 700
2#ifdef __linux__
3#include <linux/input-event-codes.h>
4#elif __FreeBSD__
5#include <dev/evdev/input-event-codes.h>
6#endif
7#include <wlr/types/wlr_cursor.h>
8#include <wlr/types/wlr_xcursor_manager.h>
9#include "list.h"
10#include "log.h"
11#include "sway/input/cursor.h"
12#include "sway/layers.h"
13#include "sway/output.h"
14#include "sway/tree/view.h"
15#include "wlr-layer-shell-unstable-v1-protocol.h"
16
17static struct wlr_surface *layer_surface_at(struct sway_output *output,
18 struct wl_list *layer, double ox, double oy, double *sx, double *sy) {
19 struct sway_layer_surface *sway_layer;
20 wl_list_for_each_reverse(sway_layer, layer, link) {
21 struct wlr_surface *wlr_surface =
22 sway_layer->layer_surface->surface;
23 double _sx = ox - sway_layer->geo.x;
24 double _sy = oy - sway_layer->geo.y;
25 // TODO: Test popups/subsurfaces
26 if (wlr_surface_point_accepts_input(wlr_surface, _sx, _sy)) {
27 *sx = _sx;
28 *sy = _sy;
29 return wlr_surface;
30 }
31 }
32 return NULL;
33}
34
35/**
36 * Returns the container at the cursor's position. If there is a surface at that
37 * location, it is stored in **surface (it may not be a view).
38 */
39static struct sway_container *container_at_cursor(struct sway_cursor *cursor,
40 struct wlr_surface **surface, double *sx, double *sy) {
41 // check for unmanaged views first
42 struct wl_list *unmanaged = &root_container.sway_root->xwayland_unmanaged;
43 struct sway_xwayland_unmanaged *unmanaged_surface;
44 wl_list_for_each_reverse(unmanaged_surface, unmanaged, link) {
45 struct wlr_xwayland_surface *xsurface =
46 unmanaged_surface->wlr_xwayland_surface;
47
48 double _sx = cursor->cursor->x - unmanaged_surface->lx;
49 double _sy = cursor->cursor->y - unmanaged_surface->ly;
50 if (wlr_surface_point_accepts_input(xsurface->surface, _sx, _sy)) {
51 *surface = xsurface->surface;
52 *sx = _sx;
53 *sy = _sy;
54 return NULL;
55 }
56 }
57
58 // find the output the cursor is on
59 struct wlr_output_layout *output_layout =
60 root_container.sway_root->output_layout;
61 struct wlr_output *wlr_output =
62 wlr_output_layout_output_at(output_layout,
63 cursor->cursor->x, cursor->cursor->y);
64 if (wlr_output == NULL) {
65 return NULL;
66 }
67 struct sway_output *output = wlr_output->data;
68 double ox = cursor->cursor->x, oy = cursor->cursor->y;
69 wlr_output_layout_output_coords(output_layout, wlr_output, &ox, &oy);
70
71 // find the focused workspace on the output for this seat
72 struct sway_container *ws =
73 seat_get_focus_inactive(cursor->seat, output->swayc);
74 if (ws && ws->type != C_WORKSPACE) {
75 ws = container_parent(ws, C_WORKSPACE);
76 }
77 if (!ws) {
78 return output->swayc;
79 }
80
81 if ((*surface = layer_surface_at(output,
82 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY],
83 ox, oy, sx, sy))) {
84 return ws;
85 }
86 if ((*surface = layer_surface_at(output,
87 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP],
88 ox, oy, sx, sy))) {
89 return ws;
90 }
91
92 struct sway_container *c;
93 if ((c = container_at(ws, cursor->cursor->x, cursor->cursor->y,
94 surface, sx, sy))) {
95 return c;
96 }
97
98 if ((*surface = layer_surface_at(output,
99 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM],
100 ox, oy, sx, sy))) {
101 return ws;
102 }
103 if ((*surface = layer_surface_at(output,
104 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND],
105 ox, oy, sx, sy))) {
106 return ws;
107 }
108
109 c = seat_get_focus_inactive(cursor->seat, output->swayc);
110 if (c) {
111 return c;
112 }
113 if (!c && output->swayc->children->length) {
114 c = output->swayc->children->items[0];
115 return c;
116 }
117
118 return output->swayc;
119}
120
121void cursor_send_pointer_motion(struct sway_cursor *cursor, uint32_t time) {
122 struct wlr_seat *seat = cursor->seat->wlr_seat;
123 struct wlr_surface *surface = NULL;
124 double sx, sy;
125 struct sway_container *c = container_at_cursor(cursor, &surface, &sx, &sy);
126 if (c && config->focus_follows_mouse) {
127 seat_set_focus_warp(cursor->seat, c, false);
128 }
129
130 // reset cursor if switching between clients
131 struct wl_client *client = NULL;
132 if (surface != NULL) {
133 client = wl_resource_get_client(surface->resource);
134 }
135 if (client != cursor->image_client) {
136 wlr_xcursor_manager_set_cursor_image(cursor->xcursor_manager,
137 "left_ptr", cursor->cursor);
138 cursor->image_client = client;
139 }
140
141 // send pointer enter/leave
142 if (surface != NULL) {
143 if (seat_is_input_allowed(cursor->seat, surface)) {
144 wlr_seat_pointer_notify_enter(seat, surface, sx, sy);
145 wlr_seat_pointer_notify_motion(seat, time, sx, sy);
146 }
147 } else {
148 wlr_seat_pointer_clear_focus(seat);
149 }
150}
151
152static void handle_cursor_motion(struct wl_listener *listener, void *data) {
153 struct sway_cursor *cursor = wl_container_of(listener, cursor, motion);
154 struct wlr_event_pointer_motion *event = data;
155 wlr_cursor_move(cursor->cursor, event->device,
156 event->delta_x, event->delta_y);
157 cursor_send_pointer_motion(cursor, event->time_msec);
158}
159
160static void handle_cursor_motion_absolute(
161 struct wl_listener *listener, void *data) {
162 struct sway_cursor *cursor =
163 wl_container_of(listener, cursor, motion_absolute);
164 struct wlr_event_pointer_motion_absolute *event = data;
165 wlr_cursor_warp_absolute(cursor->cursor, event->device, event->x, event->y);
166 cursor_send_pointer_motion(cursor, event->time_msec);
167}
168
169void dispatch_cursor_button(struct sway_cursor *cursor,
170 uint32_t time_msec, uint32_t button, enum wlr_button_state state) {
171 struct wlr_surface *surface = NULL;
172 double sx, sy;
173 struct sway_container *cont =
174 container_at_cursor(cursor, &surface, &sx, &sy);
175 if (surface && wlr_surface_is_layer_surface(surface)) {
176 struct wlr_layer_surface *layer =
177 wlr_layer_surface_from_wlr_surface(surface);
178 if (layer->current.keyboard_interactive) {
179 seat_set_focus_layer(cursor->seat, layer);
180 return;
181 }
182 }
183 // Avoid moving keyboard focus from a surface that accepts it to one
184 // that does not unless the change would move us to a new workspace.
185 //
186 // This prevents, for example, losing focus when clicking on swaybar.
187 if (surface && cont && cont->type != C_VIEW) {
188 struct sway_container *new_ws = cont;
189 if (new_ws && new_ws->type != C_WORKSPACE) {
190 new_ws = container_parent(new_ws, C_WORKSPACE);
191 }
192 struct sway_container *old_ws = seat_get_focus(cursor->seat);
193 if (old_ws && old_ws->type != C_WORKSPACE) {
194 old_ws = container_parent(old_ws, C_WORKSPACE);
195 }
196 if (new_ws != old_ws) {
197 seat_set_focus(cursor->seat, cont);
198 }
199 } else {
200 seat_set_focus(cursor->seat, cont);
201 }
202
203 wlr_seat_pointer_notify_button(cursor->seat->wlr_seat,
204 time_msec, button, state);
205}
206
207static void handle_cursor_button(struct wl_listener *listener, void *data) {
208 struct sway_cursor *cursor = wl_container_of(listener, cursor, button);
209 struct wlr_event_pointer_button *event = data;
210 dispatch_cursor_button(cursor,
211 event->time_msec, event->button, event->state);
212}
213
214static void handle_cursor_axis(struct wl_listener *listener, void *data) {
215 struct sway_cursor *cursor = wl_container_of(listener, cursor, axis);
216 struct wlr_event_pointer_axis *event = data;
217 wlr_seat_pointer_notify_axis(cursor->seat->wlr_seat, event->time_msec,
218 event->orientation, event->delta);
219}
220
221static void handle_touch_down(struct wl_listener *listener, void *data) {
222 struct sway_cursor *cursor = wl_container_of(listener, cursor, touch_down);
223 struct wlr_event_touch_down *event = data;
224 wlr_log(L_DEBUG, "TODO: handle touch down event: %p", event);
225}
226
227static void handle_touch_up(struct wl_listener *listener, void *data) {
228 struct sway_cursor *cursor = wl_container_of(listener, cursor, touch_up);
229 struct wlr_event_touch_up *event = data;
230 wlr_log(L_DEBUG, "TODO: handle touch up event: %p", event);
231}
232
233static void handle_touch_motion(struct wl_listener *listener, void *data) {
234 struct sway_cursor *cursor =
235 wl_container_of(listener, cursor, touch_motion);
236 struct wlr_event_touch_motion *event = data;
237 wlr_log(L_DEBUG, "TODO: handle touch motion event: %p", event);
238}
239
240static void handle_tool_axis(struct wl_listener *listener, void *data) {
241 struct sway_cursor *cursor = wl_container_of(listener, cursor, tool_axis);
242 struct wlr_event_tablet_tool_axis *event = data;
243
244 if ((event->updated_axes & WLR_TABLET_TOOL_AXIS_X) &&
245 (event->updated_axes & WLR_TABLET_TOOL_AXIS_Y)) {
246 wlr_cursor_warp_absolute(cursor->cursor, event->device,
247 event->x, event->y);
248 cursor_send_pointer_motion(cursor, event->time_msec);
249 } else if ((event->updated_axes & WLR_TABLET_TOOL_AXIS_X)) {
250 wlr_cursor_warp_absolute(cursor->cursor, event->device, event->x, -1);
251 cursor_send_pointer_motion(cursor, event->time_msec);
252 } else if ((event->updated_axes & WLR_TABLET_TOOL_AXIS_Y)) {
253 wlr_cursor_warp_absolute(cursor->cursor, event->device, -1, event->y);
254 cursor_send_pointer_motion(cursor, event->time_msec);
255 }
256}
257
258static void handle_tool_tip(struct wl_listener *listener, void *data) {
259 struct sway_cursor *cursor = wl_container_of(listener, cursor, tool_tip);
260 struct wlr_event_tablet_tool_tip *event = data;
261 dispatch_cursor_button(cursor, event->time_msec,
262 BTN_LEFT, event->state == WLR_TABLET_TOOL_TIP_DOWN ?
263 WLR_BUTTON_PRESSED : WLR_BUTTON_RELEASED);
264}
265
266static void handle_tool_button(struct wl_listener *listener, void *data) {
267 struct sway_cursor *cursor = wl_container_of(listener, cursor, tool_button);
268 struct wlr_event_tablet_tool_button *event = data;
269 // TODO: the user may want to configure which tool buttons are mapped to
270 // which simulated pointer buttons
271 switch (event->state) {
272 case WLR_BUTTON_PRESSED:
273 if (cursor->tool_buttons == 0) {
274 dispatch_cursor_button(cursor,
275 event->time_msec, BTN_RIGHT, event->state);
276 }
277 cursor->tool_buttons++;
278 break;
279 case WLR_BUTTON_RELEASED:
280 if (cursor->tool_buttons == 1) {
281 dispatch_cursor_button(cursor,
282 event->time_msec, BTN_RIGHT, event->state);
283 }
284 cursor->tool_buttons--;
285 break;
286 }
287}
288
289static void handle_request_set_cursor(struct wl_listener *listener,
290 void *data) {
291 struct sway_cursor *cursor =
292 wl_container_of(listener, cursor, request_set_cursor);
293 struct wlr_seat_pointer_request_set_cursor_event *event = data;
294
295 struct wl_client *focused_client = NULL;
296 struct wlr_surface *focused_surface =
297 cursor->seat->wlr_seat->pointer_state.focused_surface;
298 if (focused_surface != NULL) {
299 focused_client = wl_resource_get_client(focused_surface->resource);
300 }
301
302 // TODO: check cursor mode
303 if (focused_client == NULL ||
304 event->seat_client->client != focused_client) {
305 wlr_log(L_DEBUG, "denying request to set cursor from unfocused client");
306 return;
307 }
308
309 wlr_cursor_set_surface(cursor->cursor, event->surface, event->hotspot_x,
310 event->hotspot_y);
311 cursor->image_client = focused_client;
312}
313
314void sway_cursor_destroy(struct sway_cursor *cursor) {
315 if (!cursor) {
316 return;
317 }
318
319 wlr_xcursor_manager_destroy(cursor->xcursor_manager);
320 wlr_cursor_destroy(cursor->cursor);
321 free(cursor);
322}
323
324struct sway_cursor *sway_cursor_create(struct sway_seat *seat) {
325 struct sway_cursor *cursor = calloc(1, sizeof(struct sway_cursor));
326 if (!sway_assert(cursor, "could not allocate sway cursor")) {
327 return NULL;
328 }
329
330 struct wlr_cursor *wlr_cursor = wlr_cursor_create();
331 if (!sway_assert(wlr_cursor, "could not allocate wlr cursor")) {
332 free(cursor);
333 return NULL;
334 }
335
336 cursor->seat = seat;
337 wlr_cursor_attach_output_layout(wlr_cursor,
338 root_container.sway_root->output_layout);
339
340 // input events
341 wl_signal_add(&wlr_cursor->events.motion, &cursor->motion);
342 cursor->motion.notify = handle_cursor_motion;
343
344 wl_signal_add(&wlr_cursor->events.motion_absolute,
345 &cursor->motion_absolute);
346 cursor->motion_absolute.notify = handle_cursor_motion_absolute;
347
348 wl_signal_add(&wlr_cursor->events.button, &cursor->button);
349 cursor->button.notify = handle_cursor_button;
350
351 wl_signal_add(&wlr_cursor->events.axis, &cursor->axis);
352 cursor->axis.notify = handle_cursor_axis;
353
354 wl_signal_add(&wlr_cursor->events.touch_down, &cursor->touch_down);
355 cursor->touch_down.notify = handle_touch_down;
356
357 wl_signal_add(&wlr_cursor->events.touch_up, &cursor->touch_up);
358 cursor->touch_up.notify = handle_touch_up;
359
360 wl_signal_add(&wlr_cursor->events.touch_motion,
361 &cursor->touch_motion);
362 cursor->touch_motion.notify = handle_touch_motion;
363
364 // TODO: tablet protocol support
365 // Note: We should emulate pointer events for clients that don't support the
366 // tablet protocol when the time comes
367 wl_signal_add(&wlr_cursor->events.tablet_tool_axis,
368 &cursor->tool_axis);
369 cursor->tool_axis.notify = handle_tool_axis;
370
371 wl_signal_add(&wlr_cursor->events.tablet_tool_tip, &cursor->tool_tip);
372 cursor->tool_tip.notify = handle_tool_tip;
373
374 wl_signal_add(&wlr_cursor->events.tablet_tool_button, &cursor->tool_button);
375 cursor->tool_button.notify = handle_tool_button;
376
377 wl_signal_add(&seat->wlr_seat->events.request_set_cursor,
378 &cursor->request_set_cursor);
379 cursor->request_set_cursor.notify = handle_request_set_cursor;
380
381 cursor->cursor = wlr_cursor;
382
383 return cursor;
384}
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}
diff --git a/sway/input/keyboard.c b/sway/input/keyboard.c
new file mode 100644
index 00000000..dbb0c359
--- /dev/null
+++ b/sway/input/keyboard.c
@@ -0,0 +1,504 @@
1#include <assert.h>
2#include <wlr/backend/multi.h>
3#include <wlr/backend/session.h>
4#include "sway/input/seat.h"
5#include "sway/input/keyboard.h"
6#include "sway/input/input-manager.h"
7#include "sway/commands.h"
8#include "log.h"
9
10static bool keysym_is_modifier(xkb_keysym_t keysym) {
11 switch (keysym) {
12 case XKB_KEY_Shift_L: case XKB_KEY_Shift_R:
13 case XKB_KEY_Control_L: case XKB_KEY_Control_R:
14 case XKB_KEY_Caps_Lock:
15 case XKB_KEY_Shift_Lock:
16 case XKB_KEY_Meta_L: case XKB_KEY_Meta_R:
17 case XKB_KEY_Alt_L: case XKB_KEY_Alt_R:
18 case XKB_KEY_Super_L: case XKB_KEY_Super_R:
19 case XKB_KEY_Hyper_L: case XKB_KEY_Hyper_R:
20 return true;
21 default:
22 return false;
23 }
24}
25
26static size_t pressed_keysyms_length(xkb_keysym_t *pressed_keysyms) {
27 size_t n = 0;
28 for (size_t i = 0; i < SWAY_KEYBOARD_PRESSED_KEYSYMS_CAP; ++i) {
29 if (pressed_keysyms[i] != XKB_KEY_NoSymbol) {
30 ++n;
31 }
32 }
33 return n;
34}
35
36static ssize_t pressed_keysyms_index(xkb_keysym_t *pressed_keysyms,
37 xkb_keysym_t keysym) {
38 for (size_t i = 0; i < SWAY_KEYBOARD_PRESSED_KEYSYMS_CAP; ++i) {
39 if (pressed_keysyms[i] == keysym) {
40 return i;
41 }
42 }
43 return -1;
44}
45
46static void pressed_keysyms_add(xkb_keysym_t *pressed_keysyms,
47 xkb_keysym_t keysym) {
48 ssize_t i = pressed_keysyms_index(pressed_keysyms, keysym);
49 if (i < 0) {
50 i = pressed_keysyms_index(pressed_keysyms, XKB_KEY_NoSymbol);
51 if (i >= 0) {
52 pressed_keysyms[i] = keysym;
53 }
54 }
55}
56
57static void pressed_keysyms_remove(xkb_keysym_t *pressed_keysyms,
58 xkb_keysym_t keysym) {
59 ssize_t i = pressed_keysyms_index(pressed_keysyms, keysym);
60 if (i >= 0) {
61 pressed_keysyms[i] = XKB_KEY_NoSymbol;
62 }
63}
64
65static void pressed_keysyms_update(xkb_keysym_t *pressed_keysyms,
66 const xkb_keysym_t *keysyms, size_t keysyms_len,
67 enum wlr_key_state state) {
68 for (size_t i = 0; i < keysyms_len; ++i) {
69 if (keysym_is_modifier(keysyms[i])) {
70 continue;
71 }
72 if (state == WLR_KEY_PRESSED) {
73 pressed_keysyms_add(pressed_keysyms, keysyms[i]);
74 } else { // WLR_KEY_RELEASED
75 pressed_keysyms_remove(pressed_keysyms, keysyms[i]);
76 }
77 }
78}
79
80static bool binding_matches_key_state(struct sway_binding *binding,
81 enum wlr_key_state key_state) {
82 if (key_state == WLR_KEY_PRESSED && !binding->release) {
83 return true;
84 }
85 if (key_state == WLR_KEY_RELEASED && binding->release) {
86 return true;
87 }
88
89 return false;
90}
91
92static void keyboard_execute_command(struct sway_keyboard *keyboard,
93 struct sway_binding *binding) {
94 wlr_log(L_DEBUG, "running command for binding: %s",
95 binding->command);
96 config_clear_handler_context(config);
97 config->handler_context.seat = keyboard->seat_device->sway_seat;
98 struct cmd_results *results = execute_command(binding->command, NULL);
99 if (results->status != CMD_SUCCESS) {
100 wlr_log(L_DEBUG, "could not run command for binding: %s (%s)",
101 binding->command, results->error);
102 }
103 free_cmd_results(results);
104}
105
106/**
107 * Execute a built-in, hardcoded compositor binding. These are triggered from a
108 * single keysym.
109 *
110 * Returns true if the keysym was handled by a binding and false if the event
111 * should be propagated to clients.
112 */
113static bool keyboard_execute_compositor_binding(struct sway_keyboard *keyboard,
114 xkb_keysym_t *pressed_keysyms, uint32_t modifiers, size_t keysyms_len) {
115 for (size_t i = 0; i < keysyms_len; ++i) {
116 xkb_keysym_t keysym = pressed_keysyms[i];
117 if (keysym >= XKB_KEY_XF86Switch_VT_1 &&
118 keysym <= XKB_KEY_XF86Switch_VT_12) {
119 if (wlr_backend_is_multi(server.backend)) {
120 struct wlr_session *session =
121 wlr_multi_get_session(server.backend);
122 if (session) {
123 unsigned vt = keysym - XKB_KEY_XF86Switch_VT_1 + 1;
124 wlr_session_change_vt(session, vt);
125 }
126 }
127 return true;
128 }
129 }
130
131 return false;
132}
133
134/**
135 * Execute keyboard bindings bound with `bindysm` for the given keyboard state.
136 *
137 * Returns true if the keysym was handled by a binding and false if the event
138 * should be propagated to clients.
139 */
140static bool keyboard_execute_bindsym(struct sway_keyboard *keyboard,
141 xkb_keysym_t *pressed_keysyms, uint32_t modifiers,
142 enum wlr_key_state key_state) {
143 // configured bindings
144 int n = pressed_keysyms_length(pressed_keysyms);
145 list_t *keysym_bindings = config->current_mode->keysym_bindings;
146 for (int i = 0; i < keysym_bindings->length; ++i) {
147 struct sway_binding *binding = keysym_bindings->items[i];
148 if (!binding_matches_key_state(binding, key_state) ||
149 modifiers ^ binding->modifiers ||
150 n != binding->keys->length) {
151 continue;
152 }
153
154 bool match = true;
155 for (int j = 0; j < binding->keys->length; ++j) {
156 match =
157 pressed_keysyms_index(pressed_keysyms,
158 *(int*)binding->keys->items[j]) >= 0;
159
160 if (!match) {
161 break;
162 }
163 }
164
165 if (match) {
166 keyboard_execute_command(keyboard, binding);
167 return true;
168 }
169 }
170
171 return false;
172}
173
174static bool binding_matches_keycodes(struct wlr_keyboard *keyboard,
175 struct sway_binding *binding, struct wlr_event_keyboard_key *event) {
176 assert(binding->bindcode);
177
178 uint32_t keycode = event->keycode + 8;
179
180 if (!binding_matches_key_state(binding, event->state)) {
181 return false;
182 }
183
184 uint32_t modifiers = wlr_keyboard_get_modifiers(keyboard);
185 if (modifiers ^ binding->modifiers) {
186 return false;
187 }
188
189 // on release, the released key must be in the binding
190 if (event->state == WLR_KEY_RELEASED) {
191 bool found = false;
192 for (int i = 0; i < binding->keys->length; ++i) {
193 uint32_t binding_keycode = *(uint32_t*)binding->keys->items[i] + 8;
194 if (binding_keycode == keycode) {
195 found = true;
196 break;
197 }
198 }
199 if (!found) {
200 return false;
201 }
202 }
203
204 // every keycode in the binding must be present in the pressed keys on the
205 // keyboard
206 for (int i = 0; i < binding->keys->length; ++i) {
207 uint32_t binding_keycode = *(uint32_t*)binding->keys->items[i] + 8;
208 if (event->state == WLR_KEY_RELEASED && keycode == binding_keycode) {
209 continue;
210 }
211
212 bool found = false;
213 for (size_t j = 0; j < keyboard->num_keycodes; ++j) {
214 xkb_keycode_t keycode = keyboard->keycodes[j] + 8;
215 if (keycode == binding_keycode) {
216 found = true;
217 break;
218 }
219 }
220
221 if (!found) {
222 return false;
223 }
224 }
225
226 // every keycode pressed on the keyboard must be present within the binding
227 // keys (unless it is a modifier)
228 for (size_t i = 0; i < keyboard->num_keycodes; ++i) {
229 xkb_keycode_t keycode = keyboard->keycodes[i] + 8;
230 bool found = false;
231 for (int j = 0; j < binding->keys->length; ++j) {
232 uint32_t binding_keycode = *(uint32_t*)binding->keys->items[j] + 8;
233 if (binding_keycode == keycode) {
234 found = true;
235 break;
236 }
237 }
238
239 if (!found) {
240 if (!binding->modifiers) {
241 return false;
242 }
243
244 // check if it is a modifier, which we know matched from the check
245 // above
246 const xkb_keysym_t *keysyms;
247 int num_keysyms =
248 xkb_state_key_get_syms(keyboard->xkb_state,
249 keycode, &keysyms);
250 if (num_keysyms != 1 || !keysym_is_modifier(keysyms[0])) {
251 return false;
252 }
253 }
254 }
255
256 return true;
257}
258
259/**
260 * Execute keyboard bindings bound with `bindcode` for the given keyboard state.
261 *
262 * Returns true if the keysym was handled by a binding and false if the event
263 * should be propagated to clients.
264 */
265static bool keyboard_execute_bindcode(struct sway_keyboard *keyboard,
266 struct wlr_event_keyboard_key *event) {
267 struct wlr_keyboard *wlr_keyboard =
268 keyboard->seat_device->input_device->wlr_device->keyboard;
269 list_t *keycode_bindings = config->current_mode->keycode_bindings;
270 for (int i = 0; i < keycode_bindings->length; ++i) {
271 struct sway_binding *binding = keycode_bindings->items[i];
272 if (binding_matches_keycodes(wlr_keyboard, binding, event)) {
273 keyboard_execute_command(keyboard, binding);
274 return true;
275 }
276 }
277
278 return false;
279}
280
281/**
282 * Get keysyms and modifiers from the keyboard as xkb sees them.
283 *
284 * This uses the xkb keysyms translation based on pressed modifiers and clears
285 * the consumed modifiers from the list of modifiers passed to keybind
286 * detection.
287 *
288 * On US layout, pressing Alt+Shift+2 will trigger Alt+@.
289 */
290static size_t keyboard_keysyms_translated(struct sway_keyboard *keyboard,
291 xkb_keycode_t keycode, const xkb_keysym_t **keysyms,
292 uint32_t *modifiers) {
293 struct wlr_input_device *device =
294 keyboard->seat_device->input_device->wlr_device;
295 *modifiers = wlr_keyboard_get_modifiers(device->keyboard);
296 xkb_mod_mask_t consumed = xkb_state_key_get_consumed_mods2(
297 device->keyboard->xkb_state, keycode, XKB_CONSUMED_MODE_XKB);
298 *modifiers = *modifiers & ~consumed;
299
300 return xkb_state_key_get_syms(device->keyboard->xkb_state,
301 keycode, keysyms);
302}
303
304/**
305 * Get keysyms and modifiers from the keyboard as if modifiers didn't change
306 * keysyms.
307 *
308 * This avoids the xkb keysym translation based on modifiers considered pressed
309 * in the state.
310 *
311 * This will trigger keybinds such as Alt+Shift+2.
312 */
313static size_t keyboard_keysyms_raw(struct sway_keyboard *keyboard,
314 xkb_keycode_t keycode, const xkb_keysym_t **keysyms,
315 uint32_t *modifiers) {
316 struct wlr_input_device *device =
317 keyboard->seat_device->input_device->wlr_device;
318 *modifiers = wlr_keyboard_get_modifiers(device->keyboard);
319
320 xkb_layout_index_t layout_index = xkb_state_key_get_layout(
321 device->keyboard->xkb_state, keycode);
322 return xkb_keymap_key_get_syms_by_level(device->keyboard->keymap,
323 keycode, layout_index, 0, keysyms);
324}
325
326static void handle_keyboard_key(struct wl_listener *listener, void *data) {
327 struct sway_keyboard *keyboard =
328 wl_container_of(listener, keyboard, keyboard_key);
329 struct wlr_seat *wlr_seat = keyboard->seat_device->sway_seat->wlr_seat;
330 struct wlr_input_device *wlr_device =
331 keyboard->seat_device->input_device->wlr_device;
332 struct wlr_event_keyboard_key *event = data;
333
334 xkb_keycode_t keycode = event->keycode + 8;
335 bool handled = false;
336
337 // handle keycodes
338 handled = keyboard_execute_bindcode(keyboard, event);
339
340 // handle translated keysyms
341 if (!handled && event->state == WLR_KEY_RELEASED) {
342 handled = keyboard_execute_bindsym(keyboard,
343 keyboard->pressed_keysyms_translated,
344 keyboard->modifiers_translated,
345 event->state);
346 }
347 const xkb_keysym_t *translated_keysyms;
348 size_t translated_keysyms_len =
349 keyboard_keysyms_translated(keyboard, keycode, &translated_keysyms,
350 &keyboard->modifiers_translated);
351 pressed_keysyms_update(keyboard->pressed_keysyms_translated,
352 translated_keysyms, translated_keysyms_len, event->state);
353 if (!handled && event->state == WLR_KEY_PRESSED) {
354 handled = keyboard_execute_bindsym(keyboard,
355 keyboard->pressed_keysyms_translated,
356 keyboard->modifiers_translated,
357 event->state);
358 }
359
360 // Handle raw keysyms
361 if (!handled && event->state == WLR_KEY_RELEASED) {
362 handled = keyboard_execute_bindsym(keyboard,
363 keyboard->pressed_keysyms_raw, keyboard->modifiers_raw,
364 event->state);
365 }
366 const xkb_keysym_t *raw_keysyms;
367 size_t raw_keysyms_len =
368 keyboard_keysyms_raw(keyboard, keycode, &raw_keysyms, &keyboard->modifiers_raw);
369 pressed_keysyms_update(keyboard->pressed_keysyms_raw, raw_keysyms,
370 raw_keysyms_len, event->state);
371 if (!handled && event->state == WLR_KEY_PRESSED) {
372 handled = keyboard_execute_bindsym(keyboard,
373 keyboard->pressed_keysyms_raw, keyboard->modifiers_raw,
374 event->state);
375 }
376
377 // Compositor bindings
378 if (!handled && event->state == WLR_KEY_PRESSED) {
379 handled =
380 keyboard_execute_compositor_binding(keyboard,
381 keyboard->pressed_keysyms_translated,
382 keyboard->modifiers_translated,
383 translated_keysyms_len);
384 }
385 if (!handled && event->state == WLR_KEY_PRESSED) {
386 handled =
387 keyboard_execute_compositor_binding(keyboard,
388 keyboard->pressed_keysyms_raw, keyboard->modifiers_raw,
389 raw_keysyms_len);
390 }
391
392 if (!handled || event->state == WLR_KEY_RELEASED) {
393 wlr_seat_set_keyboard(wlr_seat, wlr_device);
394 wlr_seat_keyboard_notify_key(wlr_seat, event->time_msec,
395 event->keycode, event->state);
396 }
397}
398
399static void handle_keyboard_modifiers(struct wl_listener *listener,
400 void *data) {
401 struct sway_keyboard *keyboard =
402 wl_container_of(listener, keyboard, keyboard_modifiers);
403 struct wlr_seat *wlr_seat = keyboard->seat_device->sway_seat->wlr_seat;
404 struct wlr_input_device *wlr_device =
405 keyboard->seat_device->input_device->wlr_device;
406 wlr_seat_set_keyboard(wlr_seat, wlr_device);
407 wlr_seat_keyboard_notify_modifiers(wlr_seat, &wlr_device->keyboard->modifiers);
408}
409
410struct sway_keyboard *sway_keyboard_create(struct sway_seat *seat,
411 struct sway_seat_device *device) {
412 struct sway_keyboard *keyboard =
413 calloc(1, sizeof(struct sway_keyboard));
414 if (!sway_assert(keyboard, "could not allocate sway keyboard")) {
415 return NULL;
416 }
417
418 keyboard->seat_device = device;
419 device->keyboard = keyboard;
420
421 wl_list_init(&keyboard->keyboard_key.link);
422 wl_list_init(&keyboard->keyboard_modifiers.link);
423
424 return keyboard;
425}
426
427void sway_keyboard_configure(struct sway_keyboard *keyboard) {
428 struct xkb_rule_names rules;
429 memset(&rules, 0, sizeof(rules));
430 struct input_config *input_config =
431 input_device_get_config(keyboard->seat_device->input_device);
432 struct wlr_input_device *wlr_device =
433 keyboard->seat_device->input_device->wlr_device;
434
435 if (input_config && input_config->xkb_layout) {
436 rules.layout = input_config->xkb_layout;
437 } else {
438 rules.layout = getenv("XKB_DEFAULT_LAYOUT");
439 }
440 if (input_config && input_config->xkb_model) {
441 rules.model = input_config->xkb_model;
442 } else {
443 rules.model = getenv("XKB_DEFAULT_MODEL");
444 }
445
446 if (input_config && input_config->xkb_options) {
447 rules.options = input_config->xkb_options;
448 } else {
449 rules.options = getenv("XKB_DEFAULT_OPTIONS");
450 }
451
452 if (input_config && input_config->xkb_rules) {
453 rules.rules = input_config->xkb_rules;
454 } else {
455 rules.rules = getenv("XKB_DEFAULT_RULES");
456 }
457
458 if (input_config && input_config->xkb_variant) {
459 rules.variant = input_config->xkb_variant;
460 } else {
461 rules.variant = getenv("XKB_DEFAULT_VARIANT");
462 }
463
464 struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
465 if (!sway_assert(context, "cannot create XKB context")) {
466 return;
467 }
468
469 struct xkb_keymap *keymap =
470 xkb_keymap_new_from_names(context, &rules, XKB_KEYMAP_COMPILE_NO_FLAGS);
471
472 if (!keymap) {
473 wlr_log(L_DEBUG, "cannot configure keyboard: keymap does not exist");
474 xkb_context_unref(context);
475 return;
476 }
477
478 xkb_keymap_unref(keyboard->keymap);
479 keyboard->keymap = keymap;
480 wlr_keyboard_set_keymap(wlr_device->keyboard, keyboard->keymap);
481
482 wlr_keyboard_set_repeat_info(wlr_device->keyboard, 25, 600);
483 xkb_context_unref(context);
484 struct wlr_seat *seat = keyboard->seat_device->sway_seat->wlr_seat;
485 wlr_seat_set_keyboard(seat, wlr_device);
486
487 wl_list_remove(&keyboard->keyboard_key.link);
488 wl_signal_add(&wlr_device->keyboard->events.key, &keyboard->keyboard_key);
489 keyboard->keyboard_key.notify = handle_keyboard_key;
490
491 wl_list_remove(&keyboard->keyboard_modifiers.link);
492 wl_signal_add( &wlr_device->keyboard->events.modifiers,
493 &keyboard->keyboard_modifiers);
494 keyboard->keyboard_modifiers.notify = handle_keyboard_modifiers;
495}
496
497void sway_keyboard_destroy(struct sway_keyboard *keyboard) {
498 if (!keyboard) {
499 return;
500 }
501 wl_list_remove(&keyboard->keyboard_key.link);
502 wl_list_remove(&keyboard->keyboard_modifiers.link);
503 free(keyboard);
504}
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}