aboutsummaryrefslogtreecommitdiffstats
path: root/sway/input/seatop_default.c
diff options
context:
space:
mode:
authorLibravatar Ryan Dwyer <ryandwyer1@gmail.com>2019-03-16 17:47:39 +1000
committerLibravatar Drew DeVault <sir@cmpwn.com>2019-03-17 10:02:04 -0600
commit7b9ae42331e49fea0f566fa7592855dee5da1991 (patch)
treeba03e7826af566f871580cd2a1ec344013cebd4f /sway/input/seatop_default.c
parentReplace seatup allows_events with button callback (diff)
downloadsway-7b9ae42331e49fea0f566fa7592855dee5da1991.tar.gz
sway-7b9ae42331e49fea0f566fa7592855dee5da1991.tar.zst
sway-7b9ae42331e49fea0f566fa7592855dee5da1991.zip
Introduce default seatop
This introduces a `default` seat operation which is used when no mouse buttons are being held. This means there is now always a seat operation in progress. It allows us to separate `default` code from the standard cursor management code. The sway_seatop_impl struct has gained callbacks `axis`, `rebase` and `end`, and lost callbacks `finish` and `abort`. `axis` and `rebase` are only used by the default seatop. `end` is called when a seatop is being replaced by another one and allows the seatop to free any resources, though no seatop currently needs to do this. `finish` is no longer required, as each seatop can gracefully finish in their `button` callback. And `abort` is not needed, as calling `end` would achieve the same thing. The struct has also gained a bool named allow_set_cursor which allows the client to set a new cursor during `default` and `down` seatops. Seatops would previously store which button they were started with and stop when that button was released. This behaviour is changed so that it only ends once all buttons are released. So you can start a drag with $mod+left, then click and hold right, release left and it'll continue dragging while the right button is held. The motion callback now accepts dx and dy. Most seatops don't use this as they store the cursor position when the seatop is started and compare it with the current cursor position. This approach doesn't make sense for the default seatop though, hence why dx and dy are needed. The pressed_buttons array has been moved from the sway_cursor struct to the default seatop's data. This is only used for the default seatop to check bindings. The total pressed button count remains in the sway_cursor struct though, because all the other seatops check it to know if they should end. The `down` seatop no longer has a `moved` property. This was used to track if the cursor moved and to recheck focus_follows_mouse, but seems to work without it. The logic for focus_follows_mouse has been refactored. As part of this I've removed the call to wlr_seat_keyboard_has_grab as we don't appear to use keyboard grabs. The functions for handling relative motion, absolute motion and tool axis have been changed. Previously the handler functions were handle_cursor_motion, handle_cursor_motion_absolute and handle_tool_axis. The latter two both called cursor_motion_absolute. Both handle_cursor_motion and cursor_motion_absolute did very similar things. These are now simplified into three handlers and a single common function called cursor_motion. All three handlers call cursor_motion. As cursor_motion works with relative distances, the absolute and tool axis handlers convert them to relative first.
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}