diff options
Diffstat (limited to 'sway/input')
-rw-r--r-- | sway/input/cursor.c | 384 | ||||
-rw-r--r-- | sway/input/input-manager.c | 445 | ||||
-rw-r--r-- | sway/input/keyboard.c | 504 | ||||
-rw-r--r-- | sway/input/seat.c | 675 |
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 | |||
17 | static 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 | */ | ||
39 | static 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 | |||
121 | void 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 | |||
152 | static 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 | |||
160 | static 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 | |||
169 | void 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 | |||
207 | static 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 | |||
214 | static 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 | |||
221 | static 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 | |||
227 | static 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 | |||
233 | static 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 | |||
240 | static 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 | |||
258 | static 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 | |||
266 | static 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 | |||
289 | static 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 | |||
314 | void 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 | |||
324 | struct 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 | |||
19 | static const char *default_seat = "seat0"; | ||
20 | |||
21 | // TODO make me not global | ||
22 | struct sway_input_manager *input_manager; | ||
23 | |||
24 | struct input_config *current_input_config = NULL; | ||
25 | struct seat_config *current_seat_config = NULL; | ||
26 | |||
27 | struct 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 | |||
35 | struct 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 | |||
47 | static 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 | |||
73 | static 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 | |||
84 | static 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 | |||
96 | static 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 | |||
180 | static 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 | |||
204 | static 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 | |||
267 | static 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 | |||
276 | static 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 | |||
293 | struct 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 | |||
322 | bool 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 | |||
334 | void 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 | |||
342 | void 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 | |||
359 | void 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 | |||
417 | void 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 | |||
424 | struct 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 | |||
435 | struct 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 | |||
10 | static 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 | |||
26 | static 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 | |||
36 | static 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 | |||
46 | static 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 | |||
57 | static 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 | |||
65 | static 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 | |||
80 | static 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 | |||
92 | static 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 | */ | ||
113 | static 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 | */ | ||
140 | static 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 | |||
174 | static 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 | */ | ||
265 | static 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 | */ | ||
290 | static 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 | */ | ||
313 | static 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 | |||
326 | static 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 | |||
399 | static 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 | |||
410 | struct 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 | |||
427 | void 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 | |||
497 | void 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 | |||
23 | static 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 | |||
35 | void 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 | |||
45 | static struct sway_seat_container *seat_container_from_container( | ||
46 | struct sway_seat *seat, struct sway_container *con); | ||
47 | |||
48 | static 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 | |||
66 | static 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 | |||
90 | static 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 | |||
110 | void 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 | |||
124 | struct 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 | |||
129 | static 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 | |||
168 | static 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 | |||
197 | static 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 | |||
203 | static 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 | |||
217 | struct 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 | |||
262 | static 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 | |||
289 | static 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 | |||
296 | static 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 | |||
316 | static 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 | |||
323 | static 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 | |||
335 | void 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 | |||
360 | void 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 | |||
384 | void 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 | |||
399 | void 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 | |||
434 | bool 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 | |||
440 | void 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 | |||
538 | void seat_set_focus(struct sway_seat *seat, | ||
539 | struct sway_container *container) { | ||
540 | seat_set_focus_warp(seat, container, true); | ||
541 | } | ||
542 | |||
543 | void 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 | |||
566 | void 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 | |||
588 | void 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 | |||
633 | struct 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 | |||
638 | struct 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 | |||
645 | struct 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 | |||
652 | void 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 | |||
665 | struct 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 | } | ||