aboutsummaryrefslogtreecommitdiffstats
path: root/sway/input/cursor.c
diff options
context:
space:
mode:
Diffstat (limited to 'sway/input/cursor.c')
-rw-r--r--sway/input/cursor.c384
1 files changed, 384 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}