aboutsummaryrefslogtreecommitdiffstats
path: root/sway/input/seatop_default.c
diff options
context:
space:
mode:
Diffstat (limited to 'sway/input/seatop_default.c')
-rw-r--r--sway/input/seatop_default.c629
1 files changed, 629 insertions, 0 deletions
diff --git a/sway/input/seatop_default.c b/sway/input/seatop_default.c
new file mode 100644
index 00000000..fc785cda
--- /dev/null
+++ b/sway/input/seatop_default.c
@@ -0,0 +1,629 @@
1#define _POSIX_C_SOURCE 200809L
2#include <float.h>
3#include <libevdev/libevdev.h>
4#include <wlr/types/wlr_cursor.h>
5#include <wlr/types/wlr_xcursor_manager.h>
6#include "sway/input/cursor.h"
7#include "sway/input/seat.h"
8#include "sway/tree/view.h"
9#include "log.h"
10
11struct seatop_default_event {
12 struct sway_node *previous_node;
13 uint32_t pressed_buttons[SWAY_CURSOR_PRESSED_BUTTONS_CAP];
14 size_t pressed_button_count;
15};
16
17/*-----------------------------------------\
18 * Functions shared by multiple callbacks /
19 *---------------------------------------*/
20
21/**
22 * Determine if the edge of the given container is on the edge of the
23 * workspace/output.
24 */
25static bool edge_is_external(struct sway_container *cont, enum wlr_edges edge) {
26 enum sway_container_layout layout = L_NONE;
27 switch (edge) {
28 case WLR_EDGE_TOP:
29 case WLR_EDGE_BOTTOM:
30 layout = L_VERT;
31 break;
32 case WLR_EDGE_LEFT:
33 case WLR_EDGE_RIGHT:
34 layout = L_HORIZ;
35 break;
36 case WLR_EDGE_NONE:
37 sway_assert(false, "Never reached");
38 return false;
39 }
40
41 // Iterate the parents until we find one with the layout we want,
42 // then check if the child has siblings between it and the edge.
43 while (cont) {
44 if (container_parent_layout(cont) == layout) {
45 list_t *siblings = container_get_siblings(cont);
46 int index = list_find(siblings, cont);
47 if (index > 0 && (edge == WLR_EDGE_LEFT || edge == WLR_EDGE_TOP)) {
48 return false;
49 }
50 if (index < siblings->length - 1 &&
51 (edge == WLR_EDGE_RIGHT || edge == WLR_EDGE_BOTTOM)) {
52 return false;
53 }
54 }
55 cont = cont->parent;
56 }
57 return true;
58}
59
60static enum wlr_edges find_edge(struct sway_container *cont,
61 struct sway_cursor *cursor) {
62 if (!cont->view) {
63 return WLR_EDGE_NONE;
64 }
65 if (cont->border == B_NONE || !cont->border_thickness ||
66 cont->border == B_CSD) {
67 return WLR_EDGE_NONE;
68 }
69
70 enum wlr_edges edge = 0;
71 if (cursor->cursor->x < cont->x + cont->border_thickness) {
72 edge |= WLR_EDGE_LEFT;
73 }
74 if (cursor->cursor->y < cont->y + cont->border_thickness) {
75 edge |= WLR_EDGE_TOP;
76 }
77 if (cursor->cursor->x >= cont->x + cont->width - cont->border_thickness) {
78 edge |= WLR_EDGE_RIGHT;
79 }
80 if (cursor->cursor->y >= cont->y + cont->height - cont->border_thickness) {
81 edge |= WLR_EDGE_BOTTOM;
82 }
83
84 return edge;
85}
86
87/**
88 * If the cursor is over a _resizable_ edge, return the edge.
89 * Edges that can't be resized are edges of the workspace.
90 */
91static enum wlr_edges find_resize_edge(struct sway_container *cont,
92 struct sway_cursor *cursor) {
93 enum wlr_edges edge = find_edge(cont, cursor);
94 if (edge && !container_is_floating(cont) && edge_is_external(cont, edge)) {
95 return WLR_EDGE_NONE;
96 }
97 return edge;
98}
99
100/**
101 * Return the mouse binding which matches modifier, click location, release,
102 * and pressed button state, otherwise return null.
103 */
104static struct sway_binding* get_active_mouse_binding(
105 struct seatop_default_event *e, list_t *bindings, uint32_t modifiers,
106 bool release, bool on_titlebar, bool on_border, bool on_content,
107 bool on_workspace, const char *identifier) {
108 uint32_t click_region =
109 ((on_titlebar || on_workspace) ? BINDING_TITLEBAR : 0) |
110 ((on_border || on_workspace) ? BINDING_BORDER : 0) |
111 ((on_content || on_workspace) ? BINDING_CONTENTS : 0);
112
113 struct sway_binding *current = NULL;
114 for (int i = 0; i < bindings->length; ++i) {
115 struct sway_binding *binding = bindings->items[i];
116 if (modifiers ^ binding->modifiers ||
117 e->pressed_button_count != (size_t)binding->keys->length ||
118 release != (binding->flags & BINDING_RELEASE) ||
119 !(click_region & binding->flags) ||
120 (on_workspace &&
121 (click_region & binding->flags) != click_region) ||
122 (strcmp(binding->input, identifier) != 0 &&
123 strcmp(binding->input, "*") != 0)) {
124 continue;
125 }
126
127 bool match = true;
128 for (size_t j = 0; j < e->pressed_button_count; j++) {
129 uint32_t key = *(uint32_t *)binding->keys->items[j];
130 if (key != e->pressed_buttons[j]) {
131 match = false;
132 break;
133 }
134 }
135 if (!match) {
136 continue;
137 }
138
139 if (!current || strcmp(current->input, "*") == 0) {
140 current = binding;
141 if (strcmp(current->input, identifier) == 0) {
142 // If a binding is found for the exact input, quit searching
143 break;
144 }
145 }
146 }
147 return current;
148}
149
150/**
151 * Remove a button (and duplicates) from the sorted list of currently pressed
152 * buttons.
153 */
154static void state_erase_button(struct seatop_default_event *e,
155 uint32_t button) {
156 size_t j = 0;
157 for (size_t i = 0; i < e->pressed_button_count; ++i) {
158 if (i > j) {
159 e->pressed_buttons[j] = e->pressed_buttons[i];
160 }
161 if (e->pressed_buttons[i] != button) {
162 ++j;
163 }
164 }
165 while (e->pressed_button_count > j) {
166 --e->pressed_button_count;
167 e->pressed_buttons[e->pressed_button_count] = 0;
168 }
169}
170
171/**
172 * Add a button to the sorted list of currently pressed buttons, if there
173 * is space.
174 */
175static void state_add_button(struct seatop_default_event *e, uint32_t button) {
176 if (e->pressed_button_count >= SWAY_CURSOR_PRESSED_BUTTONS_CAP) {
177 return;
178 }
179 size_t i = 0;
180 while (i < e->pressed_button_count && e->pressed_buttons[i] < button) {
181 ++i;
182 }
183 size_t j = e->pressed_button_count;
184 while (j > i) {
185 e->pressed_buttons[j] = e->pressed_buttons[j - 1];
186 --j;
187 }
188 e->pressed_buttons[i] = button;
189 e->pressed_button_count++;
190}
191
192static void cursor_do_rebase(struct sway_cursor *cursor, uint32_t time_msec,
193 struct sway_node *node, struct wlr_surface *surface,
194 double sx, double sy) {
195 // Handle cursor image
196 if (surface) {
197 // Reset cursor if switching between clients
198 struct wl_client *client = wl_resource_get_client(surface->resource);
199 if (client != cursor->image_client) {
200 cursor_set_image(cursor, "left_ptr", client);
201 }
202 } else if (node && node->type == N_CONTAINER) {
203 // Try a node's resize edge
204 enum wlr_edges edge = find_resize_edge(node->sway_container, cursor);
205 if (edge == WLR_EDGE_NONE) {
206 cursor_set_image(cursor, "left_ptr", NULL);
207 } else if (container_is_floating(node->sway_container)) {
208 cursor_set_image(cursor, wlr_xcursor_get_resize_name(edge), NULL);
209 } else {
210 if (edge & (WLR_EDGE_LEFT | WLR_EDGE_RIGHT)) {
211 cursor_set_image(cursor, "col-resize", NULL);
212 } else {
213 cursor_set_image(cursor, "row-resize", NULL);
214 }
215 }
216 } else {
217 cursor_set_image(cursor, "left_ptr", NULL);
218 }
219
220 // Send pointer enter/leave
221 struct wlr_seat *wlr_seat = cursor->seat->wlr_seat;
222 if (surface) {
223 if (seat_is_input_allowed(cursor->seat, surface)) {
224 wlr_seat_pointer_notify_enter(wlr_seat, surface, sx, sy);
225 wlr_seat_pointer_notify_motion(wlr_seat, time_msec, sx, sy);
226 }
227 } else {
228 wlr_seat_pointer_clear_focus(wlr_seat);
229 }
230}
231
232/*----------------------------------\
233 * Functions used by handle_button /
234 *--------------------------------*/
235
236static void handle_button(struct sway_seat *seat, uint32_t time_msec,
237 struct wlr_input_device *device, uint32_t button,
238 enum wlr_button_state state) {
239 struct seatop_default_event *e = seat->seatop_data;
240 struct sway_cursor *cursor = seat->cursor;
241
242 // Determine what's under the cursor
243 struct wlr_surface *surface = NULL;
244 double sx, sy;
245 struct sway_node *node = node_at_coords(seat,
246 cursor->cursor->x, cursor->cursor->y, &surface, &sx, &sy);
247 struct sway_container *cont = node && node->type == N_CONTAINER ?
248 node->sway_container : NULL;
249 bool is_floating = cont && container_is_floating(cont);
250 bool is_floating_or_child = cont && container_is_floating_or_child(cont);
251 bool is_fullscreen_or_child = cont && container_is_fullscreen_or_child(cont);
252 enum wlr_edges edge = cont ? find_edge(cont, cursor) : WLR_EDGE_NONE;
253 enum wlr_edges resize_edge = edge ?
254 find_resize_edge(cont, cursor) : WLR_EDGE_NONE;
255 bool on_border = edge != WLR_EDGE_NONE;
256 bool on_contents = cont && !on_border && surface;
257 bool on_workspace = node && node->type == N_WORKSPACE;
258 bool on_titlebar = cont && !on_border && !surface;
259
260 // Handle mouse bindings
261 struct wlr_keyboard *keyboard = wlr_seat_get_keyboard(seat->wlr_seat);
262 uint32_t modifiers = keyboard ? wlr_keyboard_get_modifiers(keyboard) : 0;
263
264 char *device_identifier = device ? input_device_get_identifier(device)
265 : strdup("*");
266 struct sway_binding *binding = NULL;
267 if (state == WLR_BUTTON_PRESSED) {
268 state_add_button(e, button);
269 binding = get_active_mouse_binding(e,
270 config->current_mode->mouse_bindings, modifiers, false,
271 on_titlebar, on_border, on_contents, on_workspace,
272 device_identifier);
273 } else {
274 binding = get_active_mouse_binding(e,
275 config->current_mode->mouse_bindings, modifiers, true,
276 on_titlebar, on_border, on_contents, on_workspace,
277 device_identifier);
278 state_erase_button(e, button);
279 }
280 free(device_identifier);
281 if (binding) {
282 seat_execute_command(seat, binding);
283 return;
284 }
285
286 // Handle clicking an empty workspace
287 if (node && node->type == N_WORKSPACE) {
288 seat_set_focus(seat, node);
289 return;
290 }
291
292 // Handle clicking a layer surface
293 if (surface && wlr_surface_is_layer_surface(surface)) {
294 struct wlr_layer_surface_v1 *layer =
295 wlr_layer_surface_v1_from_wlr_surface(surface);
296 if (layer->current.keyboard_interactive) {
297 seat_set_focus_layer(seat, layer);
298 }
299 seat_pointer_notify_button(seat, time_msec, button, state);
300 return;
301 }
302
303 // Handle tiling resize via border
304 if (cont && resize_edge && button == BTN_LEFT &&
305 state == WLR_BUTTON_PRESSED && !is_floating) {
306 seat_set_focus_container(seat, cont);
307 seatop_begin_resize_tiling(seat, cont, edge);
308 return;
309 }
310
311 // Handle tiling resize via mod
312 bool mod_pressed = keyboard &&
313 (wlr_keyboard_get_modifiers(keyboard) & config->floating_mod);
314 if (cont && !is_floating_or_child && mod_pressed &&
315 state == WLR_BUTTON_PRESSED) {
316 uint32_t btn_resize = config->floating_mod_inverse ?
317 BTN_LEFT : BTN_RIGHT;
318 if (button == btn_resize) {
319 edge = 0;
320 edge |= cursor->cursor->x > cont->x + cont->width / 2 ?
321 WLR_EDGE_RIGHT : WLR_EDGE_LEFT;
322 edge |= cursor->cursor->y > cont->y + cont->height / 2 ?
323 WLR_EDGE_BOTTOM : WLR_EDGE_TOP;
324
325 const char *image = NULL;
326 if (edge == (WLR_EDGE_LEFT | WLR_EDGE_TOP)) {
327 image = "nw-resize";
328 } else if (edge == (WLR_EDGE_TOP | WLR_EDGE_RIGHT)) {
329 image = "ne-resize";
330 } else if (edge == (WLR_EDGE_RIGHT | WLR_EDGE_BOTTOM)) {
331 image = "se-resize";
332 } else if (edge == (WLR_EDGE_BOTTOM | WLR_EDGE_LEFT)) {
333 image = "sw-resize";
334 }
335 cursor_set_image(seat->cursor, image, NULL);
336 seat_set_focus_container(seat, cont);
337 seatop_begin_resize_tiling(seat, cont, edge);
338 return;
339 }
340 }
341
342 // Handle beginning floating move
343 if (cont && is_floating_or_child && !is_fullscreen_or_child &&
344 state == WLR_BUTTON_PRESSED) {
345 uint32_t btn_move = config->floating_mod_inverse ? BTN_RIGHT : BTN_LEFT;
346 if (button == btn_move && state == WLR_BUTTON_PRESSED &&
347 (mod_pressed || on_titlebar)) {
348 while (cont->parent) {
349 cont = cont->parent;
350 }
351 seat_set_focus_container(seat, cont);
352 seatop_begin_move_floating(seat, cont);
353 return;
354 }
355 }
356
357 // Handle beginning floating resize
358 if (cont && is_floating_or_child && !is_fullscreen_or_child &&
359 state == WLR_BUTTON_PRESSED) {
360 // Via border
361 if (button == BTN_LEFT && resize_edge != WLR_EDGE_NONE) {
362 seatop_begin_resize_floating(seat, cont, resize_edge);
363 return;
364 }
365
366 // Via mod+click
367 uint32_t btn_resize = config->floating_mod_inverse ?
368 BTN_LEFT : BTN_RIGHT;
369 if (mod_pressed && button == btn_resize) {
370 struct sway_container *floater = cont;
371 while (floater->parent) {
372 floater = floater->parent;
373 }
374 edge = 0;
375 edge |= cursor->cursor->x > floater->x + floater->width / 2 ?
376 WLR_EDGE_RIGHT : WLR_EDGE_LEFT;
377 edge |= cursor->cursor->y > floater->y + floater->height / 2 ?
378 WLR_EDGE_BOTTOM : WLR_EDGE_TOP;
379 seatop_begin_resize_floating(seat, floater, edge);
380 return;
381 }
382 }
383
384 // Handle moving a tiling container
385 if (config->tiling_drag && (mod_pressed || on_titlebar) &&
386 state == WLR_BUTTON_PRESSED && !is_floating_or_child &&
387 cont && cont->fullscreen_mode == FULLSCREEN_NONE) {
388 struct sway_container *focus = seat_get_focused_container(seat);
389 bool focused = focus == cont || container_has_ancestor(focus, cont);
390 if (on_titlebar && !focused) {
391 node = seat_get_focus_inactive(seat, &cont->node);
392 seat_set_focus(seat, node);
393 }
394
395 // If moving a container by it's title bar, use a threshold for the drag
396 if (!mod_pressed && config->tiling_drag_threshold > 0) {
397 seatop_begin_move_tiling_threshold(seat, cont);
398 } else {
399 seatop_begin_move_tiling(seat, cont);
400 }
401 return;
402 }
403
404 // Handle mousedown on a container surface
405 if (surface && cont && state == WLR_BUTTON_PRESSED) {
406 seat_set_focus_container(seat, cont);
407 seatop_begin_down(seat, cont, time_msec, sx, sy);
408 seat_pointer_notify_button(seat, time_msec, button, WLR_BUTTON_PRESSED);
409 return;
410 }
411
412 // Handle clicking a container surface or decorations
413 if (cont) {
414 node = seat_get_focus_inactive(seat, &cont->node);
415 seat_set_focus(seat, node);
416 seat_pointer_notify_button(seat, time_msec, button, state);
417 return;
418 }
419
420 seat_pointer_notify_button(seat, time_msec, button, state);
421}
422
423/*----------------------------------\
424 * Functions used by handle_motion /
425 *--------------------------------*/
426
427static void check_focus_follows_mouse(struct sway_seat *seat,
428 struct seatop_default_event *e, struct sway_node *hovered_node) {
429 struct sway_node *focus = seat_get_focus(seat);
430
431 // If a workspace node is hovered (eg. in the gap area), only set focus if
432 // the workspace is on a different output to the previous focus.
433 if (focus && hovered_node->type == N_WORKSPACE) {
434 struct sway_output *focused_output = node_get_output(focus);
435 struct sway_output *hovered_output = node_get_output(hovered_node);
436 if (hovered_output != focused_output) {
437 seat_set_focus(seat, seat_get_focus_inactive(seat, hovered_node));
438 }
439 return;
440 }
441
442 if (node_is_view(hovered_node)) {
443 if (hovered_node != e->previous_node ||
444 config->focus_follows_mouse == FOLLOWS_ALWAYS) {
445 seat_set_focus(seat, hovered_node);
446 } else {
447 // Focusing a tab which contains a split child
448 struct sway_node *next_focus =
449 seat_get_focus_inactive(seat, &root->node);
450 if (next_focus && node_is_view(next_focus) &&
451 view_is_visible(next_focus->sway_container->view)) {
452 seat_set_focus(seat, next_focus);
453 }
454 }
455 }
456}
457
458static void handle_motion(struct sway_seat *seat, uint32_t time_msec,
459 double dx, double dy) {
460 struct seatop_default_event *e = seat->seatop_data;
461 struct sway_cursor *cursor = seat->cursor;
462
463 struct wlr_surface *surface = NULL;
464 double sx, sy;
465 struct sway_node *node = node_at_coords(seat,
466 cursor->cursor->x, cursor->cursor->y, &surface, &sx, &sy);
467
468 if (node && config->focus_follows_mouse != FOLLOWS_NO) {
469 check_focus_follows_mouse(seat, e, node);
470 }
471
472 cursor_do_rebase(cursor, time_msec, node, surface, sx, sy);
473
474 struct sway_drag_icon *drag_icon;
475 wl_list_for_each(drag_icon, &root->drag_icons, link) {
476 if (drag_icon->seat == seat) {
477 drag_icon_update_position(drag_icon);
478 }
479 }
480
481 e->previous_node = node;
482}
483
484/*--------------------------------\
485 * Functions used by handle_axis /
486 *------------------------------*/
487
488static uint32_t wl_axis_to_button(struct wlr_event_pointer_axis *event) {
489 switch (event->orientation) {
490 case WLR_AXIS_ORIENTATION_VERTICAL:
491 return event->delta < 0 ? SWAY_SCROLL_UP : SWAY_SCROLL_DOWN;
492 case WLR_AXIS_ORIENTATION_HORIZONTAL:
493 return event->delta < 0 ? SWAY_SCROLL_LEFT : SWAY_SCROLL_RIGHT;
494 default:
495 sway_log(SWAY_DEBUG, "Unknown axis orientation");
496 return 0;
497 }
498}
499
500static void handle_axis(struct sway_seat *seat,
501 struct wlr_event_pointer_axis *event) {
502 struct sway_input_device *input_device =
503 event->device ? event->device->data : NULL;
504 struct input_config *ic =
505 input_device ? input_device_get_config(input_device) : NULL;
506 struct sway_cursor *cursor = seat->cursor;
507 struct seatop_default_event *e = seat->seatop_data;
508
509 // Determine what's under the cursor
510 struct wlr_surface *surface = NULL;
511 double sx, sy;
512 struct sway_node *node = node_at_coords(seat,
513 cursor->cursor->x, cursor->cursor->y, &surface, &sx, &sy);
514 struct sway_container *cont = node && node->type == N_CONTAINER ?
515 node->sway_container : NULL;
516 enum wlr_edges edge = cont ? find_edge(cont, cursor) : WLR_EDGE_NONE;
517 bool on_border = edge != WLR_EDGE_NONE;
518 bool on_titlebar = cont && !on_border && !surface;
519 bool on_titlebar_border = cont && on_border &&
520 cursor->cursor->y < cont->content_y;
521 bool on_contents = cont && !on_border && surface;
522 bool on_workspace = node && node->type == N_WORKSPACE;
523 float scroll_factor =
524 (ic == NULL || ic->scroll_factor == FLT_MIN) ? 1.0f : ic->scroll_factor;
525
526 bool handled = false;
527
528 // Gather information needed for mouse bindings
529 struct wlr_keyboard *keyboard = wlr_seat_get_keyboard(seat->wlr_seat);
530 uint32_t modifiers = keyboard ? wlr_keyboard_get_modifiers(keyboard) : 0;
531 struct wlr_input_device *device =
532 input_device ? input_device->wlr_device : NULL;
533 char *dev_id = device ? input_device_get_identifier(device) : strdup("*");
534 uint32_t button = wl_axis_to_button(event);
535
536 // Handle mouse bindings - x11 mouse buttons 4-7 - press event
537 struct sway_binding *binding = NULL;
538 state_add_button(e, button);
539 binding = get_active_mouse_binding(e, config->current_mode->mouse_bindings,
540 modifiers, false, on_titlebar, on_border, on_contents, on_workspace,
541 dev_id);
542 if (binding) {
543 seat_execute_command(seat, binding);
544 handled = true;
545 }
546
547 // Scrolling on a tabbed or stacked title bar (handled as press event)
548 if (!handled && (on_titlebar || on_titlebar_border)) {
549 enum sway_container_layout layout = container_parent_layout(cont);
550 if (layout == L_TABBED || layout == L_STACKED) {
551 struct sway_node *tabcontainer = node_get_parent(node);
552 struct sway_node *active =
553 seat_get_active_tiling_child(seat, tabcontainer);
554 list_t *siblings = container_get_siblings(cont);
555 int desired = list_find(siblings, active->sway_container) +
556 round(scroll_factor * event->delta_discrete);
557 if (desired < 0) {
558 desired = 0;
559 } else if (desired >= siblings->length) {
560 desired = siblings->length - 1;
561 }
562 struct sway_node *old_focus = seat_get_focus(seat);
563 struct sway_container *new_sibling_con = siblings->items[desired];
564 struct sway_node *new_sibling = &new_sibling_con->node;
565 struct sway_node *new_focus =
566 seat_get_focus_inactive(seat, new_sibling);
567 if (node_has_ancestor(old_focus, tabcontainer)) {
568 seat_set_focus(seat, new_focus);
569 } else {
570 // Scrolling when focus is not in the tabbed container at all
571 seat_set_raw_focus(seat, new_sibling);
572 seat_set_raw_focus(seat, new_focus);
573 seat_set_raw_focus(seat, old_focus);
574 }
575 handled = true;
576 }
577 }
578
579 // Handle mouse bindings - x11 mouse buttons 4-7 - release event
580 binding = get_active_mouse_binding(e, config->current_mode->mouse_bindings,
581 modifiers, true, on_titlebar, on_border, on_contents, on_workspace,
582 dev_id);
583 state_erase_button(e, button);
584 if (binding) {
585 seat_execute_command(seat, binding);
586 handled = true;
587 }
588 free(dev_id);
589
590 if (!handled) {
591 wlr_seat_pointer_notify_axis(cursor->seat->wlr_seat, event->time_msec,
592 event->orientation, scroll_factor * event->delta,
593 round(scroll_factor * event->delta_discrete), event->source);
594 }
595}
596
597/*----------------------------------\
598 * Functions used by handle_rebase /
599 *--------------------------------*/
600
601static void handle_rebase(struct sway_seat *seat, uint32_t time_msec) {
602 struct seatop_default_event *e = seat->seatop_data;
603 struct sway_cursor *cursor = seat->cursor;
604 struct wlr_surface *surface = NULL;
605 double sx = 0.0, sy = 0.0;
606 e->previous_node = node_at_coords(seat,
607 cursor->cursor->x, cursor->cursor->y, &surface, &sx, &sy);
608 cursor_do_rebase(cursor, time_msec, e->previous_node, surface, sx, sy);
609}
610
611static const struct sway_seatop_impl seatop_impl = {
612 .button = handle_button,
613 .motion = handle_motion,
614 .axis = handle_axis,
615 .rebase = handle_rebase,
616 .allow_set_cursor = true,
617};
618
619void seatop_begin_default(struct sway_seat *seat) {
620 seatop_end(seat);
621
622 struct seatop_default_event *e =
623 calloc(1, sizeof(struct seatop_default_event));
624 sway_assert(e, "Unable to allocate seatop_default_event");
625 seat->seatop_impl = &seatop_impl;
626 seat->seatop_data = e;
627
628 seatop_rebase(seat, 0);
629}