diff options
Diffstat (limited to 'sway/input/seatop_default.c')
-rw-r--r-- | sway/input/seatop_default.c | 629 |
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 | |||
11 | struct 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 | */ | ||
25 | static 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 | |||
60 | static 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 | */ | ||
91 | static 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 | */ | ||
104 | static 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 | */ | ||
154 | static 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 | */ | ||
175 | static 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 | |||
192 | static 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 | |||
236 | static 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 | |||
427 | static 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 | |||
458 | static 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 | |||
488 | static 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 | |||
500 | static 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 | |||
601 | static 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 | |||
611 | static 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 | |||
619 | void 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 | } | ||