aboutsummaryrefslogtreecommitdiffstats
path: root/sway/input/cursor.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/cursor.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/cursor.c')
-rw-r--r--sway/input/cursor.c680
1 files changed, 45 insertions, 635 deletions
diff --git a/sway/input/cursor.c b/sway/input/cursor.c
index 011b4929..61084447 100644
--- a/sway/input/cursor.c
+++ b/sway/input/cursor.c
@@ -158,132 +158,9 @@ struct sway_node *node_at_coords(
158 return &ws->node; 158 return &ws->node;
159} 159}
160 160
161/**
162 * Determine if the edge of the given container is on the edge of the
163 * workspace/output.
164 */
165static bool edge_is_external(struct sway_container *cont, enum wlr_edges edge) {
166 enum sway_container_layout layout = L_NONE;
167 switch (edge) {
168 case WLR_EDGE_TOP:
169 case WLR_EDGE_BOTTOM:
170 layout = L_VERT;
171 break;
172 case WLR_EDGE_LEFT:
173 case WLR_EDGE_RIGHT:
174 layout = L_HORIZ;
175 break;
176 case WLR_EDGE_NONE:
177 sway_assert(false, "Never reached");
178 return false;
179 }
180
181 // Iterate the parents until we find one with the layout we want,
182 // then check if the child has siblings between it and the edge.
183 while (cont) {
184 if (container_parent_layout(cont) == layout) {
185 list_t *siblings = container_get_siblings(cont);
186 int index = list_find(siblings, cont);
187 if (index > 0 && (edge == WLR_EDGE_LEFT || edge == WLR_EDGE_TOP)) {
188 return false;
189 }
190 if (index < siblings->length - 1 &&
191 (edge == WLR_EDGE_RIGHT || edge == WLR_EDGE_BOTTOM)) {
192 return false;
193 }
194 }
195 cont = cont->parent;
196 }
197 return true;
198}
199
200static enum wlr_edges find_edge(struct sway_container *cont,
201 struct sway_cursor *cursor) {
202 if (!cont->view) {
203 return WLR_EDGE_NONE;
204 }
205 if (cont->border == B_NONE || !cont->border_thickness ||
206 cont->border == B_CSD) {
207 return WLR_EDGE_NONE;
208 }
209
210 enum wlr_edges edge = 0;
211 if (cursor->cursor->x < cont->x + cont->border_thickness) {
212 edge |= WLR_EDGE_LEFT;
213 }
214 if (cursor->cursor->y < cont->y + cont->border_thickness) {
215 edge |= WLR_EDGE_TOP;
216 }
217 if (cursor->cursor->x >= cont->x + cont->width - cont->border_thickness) {
218 edge |= WLR_EDGE_RIGHT;
219 }
220 if (cursor->cursor->y >= cont->y + cont->height - cont->border_thickness) {
221 edge |= WLR_EDGE_BOTTOM;
222 }
223
224 return edge;
225}
226
227/**
228 * If the cursor is over a _resizable_ edge, return the edge.
229 * Edges that can't be resized are edges of the workspace.
230 */
231static enum wlr_edges find_resize_edge(struct sway_container *cont,
232 struct sway_cursor *cursor) {
233 enum wlr_edges edge = find_edge(cont, cursor);
234 if (edge && !container_is_floating(cont) && edge_is_external(cont, edge)) {
235 return WLR_EDGE_NONE;
236 }
237 return edge;
238}
239
240static void cursor_do_rebase(struct sway_cursor *cursor, uint32_t time_msec,
241 struct sway_node *node, struct wlr_surface *surface,
242 double sx, double sy) {
243 // Handle cursor image
244 if (surface) {
245 // Reset cursor if switching between clients
246 struct wl_client *client = wl_resource_get_client(surface->resource);
247 if (client != cursor->image_client) {
248 cursor_set_image(cursor, "left_ptr", client);
249 }
250 } else if (node && node->type == N_CONTAINER) {
251 // Try a node's resize edge
252 enum wlr_edges edge = find_resize_edge(node->sway_container, cursor);
253 if (edge == WLR_EDGE_NONE) {
254 cursor_set_image(cursor, "left_ptr", NULL);
255 } else if (container_is_floating(node->sway_container)) {
256 cursor_set_image(cursor, wlr_xcursor_get_resize_name(edge), NULL);
257 } else {
258 if (edge & (WLR_EDGE_LEFT | WLR_EDGE_RIGHT)) {
259 cursor_set_image(cursor, "col-resize", NULL);
260 } else {
261 cursor_set_image(cursor, "row-resize", NULL);
262 }
263 }
264 } else {
265 cursor_set_image(cursor, "left_ptr", NULL);
266 }
267
268 // Send pointer enter/leave
269 struct wlr_seat *wlr_seat = cursor->seat->wlr_seat;
270 if (surface) {
271 if (seat_is_input_allowed(cursor->seat, surface)) {
272 wlr_seat_pointer_notify_enter(wlr_seat, surface, sx, sy);
273 wlr_seat_pointer_notify_motion(wlr_seat, time_msec, sx, sy);
274 }
275 } else {
276 wlr_seat_pointer_clear_focus(wlr_seat);
277 }
278}
279
280void cursor_rebase(struct sway_cursor *cursor) { 161void cursor_rebase(struct sway_cursor *cursor) {
281 uint32_t time_msec = get_current_time_msec(); 162 uint32_t time_msec = get_current_time_msec();
282 struct wlr_surface *surface = NULL; 163 seatop_rebase(cursor->seat, time_msec);
283 double sx = 0.0, sy = 0.0;
284 cursor->previous.node = node_at_coords(cursor->seat,
285 cursor->cursor->x, cursor->cursor->y, &surface, &sx, &sy);
286 cursor_do_rebase(cursor, time_msec, cursor->previous.node, surface, sx, sy);
287} 164}
288 165
289void cursor_rebase_all(void) { 166void cursor_rebase_all(void) {
@@ -293,9 +170,7 @@ void cursor_rebase_all(void) {
293 170
294 struct sway_seat *seat; 171 struct sway_seat *seat;
295 wl_list_for_each(seat, &server.input->seats, link) { 172 wl_list_for_each(seat, &server.input->seats, link) {
296 if (!seat_doing_seatop(seat)) { 173 cursor_rebase(seat->cursor);
297 cursor_rebase(seat->cursor);
298 }
299 } 174 }
300} 175}
301 176
@@ -345,90 +220,17 @@ void cursor_unhide(struct sway_cursor *cursor) {
345 cursor_rebase(cursor); 220 cursor_rebase(cursor);
346} 221}
347 222
348void cursor_send_pointer_motion(struct sway_cursor *cursor, uint32_t time_msec, 223static void cursor_motion(struct sway_cursor *cursor, uint32_t time_msec,
349 struct sway_node *node, struct wlr_surface *surface, 224 struct wlr_input_device *device, double dx, double dy,
350 double sx, double sy) { 225 double dx_unaccel, double dy_unaccel) {
351 if (time_msec == 0) {
352 time_msec = get_current_time_msec();
353 }
354
355 struct sway_seat *seat = cursor->seat;
356
357 if (seat_doing_seatop(seat)) {
358 seatop_motion(seat, time_msec);
359 cursor->previous.x = cursor->cursor->x;
360 cursor->previous.y = cursor->cursor->y;
361 return;
362 }
363
364 struct sway_node *prev_node = cursor->previous.node;
365
366 // Update the stored previous position
367 cursor->previous.x = cursor->cursor->x;
368 cursor->previous.y = cursor->cursor->y;
369 cursor->previous.node = node;
370
371 if (node && (config->focus_follows_mouse == FOLLOWS_YES ||
372 config->focus_follows_mouse == FOLLOWS_ALWAYS)) {
373 struct sway_node *focus = seat_get_focus(seat);
374 if (focus && node->type == N_WORKSPACE) {
375 // Only follow the mouse if it would move to a new output
376 // Otherwise we'll focus the workspace, which is probably wrong
377 struct sway_output *focused_output = node_get_output(focus);
378 struct sway_output *output = node_get_output(node);
379 if (output != focused_output) {
380 seat_set_focus(seat, seat_get_focus_inactive(seat, node));
381 }
382 } else if (node->type == N_CONTAINER && node->sway_container->view) {
383 // Focus node if the following are true:
384 // - cursor is over a new view, i.e. entered a new window; and
385 // - the new view is visible, i.e. not hidden in a stack or tab; and
386 // - the seat does not have a keyboard grab
387 if ((!wlr_seat_keyboard_has_grab(cursor->seat->wlr_seat) &&
388 node != prev_node &&
389 view_is_visible(node->sway_container->view)) ||
390 config->focus_follows_mouse == FOLLOWS_ALWAYS) {
391 seat_set_focus(seat, node);
392 } else {
393 struct sway_node *next_focus =
394 seat_get_focus_inactive(seat, &root->node);
395 if (next_focus && next_focus->type == N_CONTAINER &&
396 next_focus->sway_container->view &&
397 view_is_visible(next_focus->sway_container->view)) {
398 seat_set_focus(seat, next_focus);
399 }
400 }
401 }
402 }
403
404 cursor_do_rebase(cursor, time_msec, node, surface, sx, sy);
405
406 struct sway_drag_icon *drag_icon;
407 wl_list_for_each(drag_icon, &root->drag_icons, link) {
408 if (drag_icon->seat == seat) {
409 drag_icon_update_position(drag_icon);
410 }
411 }
412}
413
414static void handle_cursor_motion(struct wl_listener *listener, void *data) {
415 struct sway_cursor *cursor = wl_container_of(listener, cursor, motion);
416 struct wlr_event_pointer_motion *event = data;
417 cursor_handle_activity(cursor); 226 cursor_handle_activity(cursor);
418 227
419 double dx = event->delta_x;
420 double dy = event->delta_y;
421
422 double dx_unaccel = event->unaccel_dx;
423 double dy_unaccel = event->unaccel_dy;
424
425 wlr_relative_pointer_manager_v1_send_relative_motion( 228 wlr_relative_pointer_manager_v1_send_relative_motion(
426 server.relative_pointer_manager, 229 server.relative_pointer_manager,
427 cursor->seat->wlr_seat, (uint64_t)event->time_msec * 1000, 230 cursor->seat->wlr_seat, (uint64_t)time_msec * 1000,
428 dx, dy, dx_unaccel, dy_unaccel); 231 dx, dy, dx_unaccel, dy_unaccel);
429 232
430 struct wlr_surface *surface = NULL; 233 struct wlr_surface *surface = NULL;
431 struct sway_node *node = NULL;
432 double sx, sy; 234 double sx, sy;
433 if (cursor->active_constraint) { 235 if (cursor->active_constraint) {
434 node_at_coords(cursor->seat, 236 node_at_coords(cursor->seat,
@@ -448,50 +250,18 @@ static void handle_cursor_motion(struct wl_listener *listener, void *data) {
448 dy = sy_confined - sy; 250 dy = sy_confined - sy;
449 } 251 }
450 252
451 wlr_cursor_move(cursor->cursor, event->device, dx, dy); 253 wlr_cursor_move(cursor->cursor, device, dx, dy);
452
453 // Recalculate pointer location after layout checks
454 node = node_at_coords(cursor->seat,
455 cursor->cursor->x, cursor->cursor->y, &surface, &sx, &sy);
456 254
457 cursor_send_pointer_motion(cursor, event->time_msec, node, surface, 255 seatop_motion(cursor->seat, time_msec, dx, dy);
458 sx, sy);
459 transaction_commit_dirty();
460} 256}
461 257
462static void cursor_motion_absolute(struct sway_cursor *cursor, 258static void handle_cursor_motion_relative(
463 uint32_t time_msec, struct wlr_input_device *dev, 259 struct wl_listener *listener, void *data) {
464 double x, double y) { 260 struct sway_cursor *cursor = wl_container_of(listener, cursor, motion);
465 cursor_handle_activity(cursor); 261 struct wlr_event_pointer_motion *e = data;
466
467 double lx, ly;
468 wlr_cursor_absolute_to_layout_coords(cursor->cursor, dev,
469 x, y, &lx, &ly);
470
471 double dx = lx - cursor->cursor->x;
472 double dy = ly - cursor->cursor->y;
473 wlr_relative_pointer_manager_v1_send_relative_motion(
474 server.relative_pointer_manager,
475 cursor->seat->wlr_seat, (uint64_t)time_msec * 1000,
476 dx, dy, dx, dy);
477
478 struct wlr_surface *surface = NULL;
479 double sx = 0.0, sy = 0.0;
480 struct sway_node *node = node_at_coords(cursor->seat,
481 lx, ly, &surface, &sx, &sy);
482
483 if (cursor->active_constraint) {
484 if (cursor->active_constraint->surface != surface) {
485 return;
486 }
487 if (!pixman_region32_contains_point(&cursor->confine,
488 floor(sx), floor(sy), NULL)) {
489 return;
490 }
491 }
492 262
493 wlr_cursor_warp_closest(cursor->cursor, dev, lx, ly); 263 cursor_motion(cursor, e->time_msec, e->device, e->delta_x, e->delta_y,
494 cursor_send_pointer_motion(cursor, time_msec, node, surface, sx, sy); 264 e->unaccel_dx, e->unaccel_dy);
495 transaction_commit_dirty(); 265 transaction_commit_dirty();
496} 266}
497 267
@@ -501,98 +271,15 @@ static void handle_cursor_motion_absolute(
501 wl_container_of(listener, cursor, motion_absolute); 271 wl_container_of(listener, cursor, motion_absolute);
502 struct wlr_event_pointer_motion_absolute *event = data; 272 struct wlr_event_pointer_motion_absolute *event = data;
503 273
504 cursor_motion_absolute(cursor, event->time_msec, event->device, 274 double lx, ly;
505 event->x, event->y); 275 wlr_cursor_absolute_to_layout_coords(cursor->cursor, event->device,
506} 276 event->x, event->y, &lx, &ly);
507
508/**
509 * Remove a button (and duplicates) to the sorted list of currently pressed buttons
510 */
511static void state_erase_button(struct sway_cursor *cursor, uint32_t button) {
512 size_t j = 0;
513 for (size_t i = 0; i < cursor->pressed_button_count; ++i) {
514 if (i > j) {
515 cursor->pressed_buttons[j] = cursor->pressed_buttons[i];
516 }
517 if (cursor->pressed_buttons[i] != button) {
518 ++j;
519 }
520 }
521 while (cursor->pressed_button_count > j) {
522 --cursor->pressed_button_count;
523 cursor->pressed_buttons[cursor->pressed_button_count] = 0;
524 }
525}
526
527/**
528 * Add a button to the sorted list of currently pressed buttons, if there
529 * is space.
530 */
531static void state_add_button(struct sway_cursor *cursor, uint32_t button) {
532 if (cursor->pressed_button_count >= SWAY_CURSOR_PRESSED_BUTTONS_CAP) {
533 return;
534 }
535 size_t i = 0;
536 while (i < cursor->pressed_button_count && cursor->pressed_buttons[i] < button) {
537 ++i;
538 }
539 size_t j = cursor->pressed_button_count;
540 while (j > i) {
541 cursor->pressed_buttons[j] = cursor->pressed_buttons[j - 1];
542 --j;
543 }
544 cursor->pressed_buttons[i] = button;
545 cursor->pressed_button_count++;
546}
547
548/**
549 * Return the mouse binding which matches modifier, click location, release,
550 * and pressed button state, otherwise return null.
551 */
552static struct sway_binding* get_active_mouse_binding(
553 const struct sway_cursor *cursor, list_t *bindings, uint32_t modifiers,
554 bool release, bool on_titlebar, bool on_border, bool on_content,
555 bool on_workspace, const char *identifier) {
556 uint32_t click_region =
557 ((on_titlebar || on_workspace) ? BINDING_TITLEBAR : 0) |
558 ((on_border || on_workspace) ? BINDING_BORDER : 0) |
559 ((on_content || on_workspace) ? BINDING_CONTENTS : 0);
560
561 struct sway_binding *current = NULL;
562 for (int i = 0; i < bindings->length; ++i) {
563 struct sway_binding *binding = bindings->items[i];
564 if (modifiers ^ binding->modifiers ||
565 cursor->pressed_button_count != (size_t)binding->keys->length ||
566 release != (binding->flags & BINDING_RELEASE) ||
567 !(click_region & binding->flags) ||
568 (on_workspace &&
569 (click_region & binding->flags) != click_region) ||
570 (strcmp(binding->input, identifier) != 0 &&
571 strcmp(binding->input, "*") != 0)) {
572 continue;
573 }
574 277
575 bool match = true; 278 double dx = lx - cursor->cursor->x;
576 for (size_t j = 0; j < cursor->pressed_button_count; j++) { 279 double dy = ly - cursor->cursor->y;
577 uint32_t key = *(uint32_t *)binding->keys->items[j];
578 if (key != cursor->pressed_buttons[j]) {
579 match = false;
580 break;
581 }
582 }
583 if (!match) {
584 continue;
585 }
586 280
587 if (!current || strcmp(current->input, "*") == 0) { 281 cursor_motion(cursor, event->time_msec, event->device, dx, dy, dx, dy);
588 current = binding; 282 transaction_commit_dirty();
589 if (strcmp(current->input, identifier) == 0) {
590 // If a binding is found for the exact input, quit searching
591 break;
592 }
593 }
594 }
595 return current;
596} 283}
597 284
598void dispatch_cursor_button(struct sway_cursor *cursor, 285void dispatch_cursor_button(struct sway_cursor *cursor,
@@ -601,318 +288,33 @@ void dispatch_cursor_button(struct sway_cursor *cursor,
601 if (time_msec == 0) { 288 if (time_msec == 0) {
602 time_msec = get_current_time_msec(); 289 time_msec = get_current_time_msec();
603 } 290 }
604 struct sway_seat *seat = cursor->seat;
605 291
606 // Handle existing seat operation 292 seatop_button(cursor->seat, time_msec, device, button, state);
607 if (seat_doing_seatop(seat)) {
608 if (state == WLR_BUTTON_PRESSED) {
609 state_add_button(cursor, button);
610 } else {
611 state_erase_button(cursor, button);
612 }
613 seatop_button(seat, time_msec, device, button, state);
614 if (button == seat->seatop_button && state == WLR_BUTTON_RELEASED) {
615 seatop_finish(seat, time_msec);
616 }
617 return;
618 }
619
620 // Determine what's under the cursor
621 struct wlr_surface *surface = NULL;
622 double sx, sy;
623 struct sway_node *node = node_at_coords(seat,
624 cursor->cursor->x, cursor->cursor->y, &surface, &sx, &sy);
625 struct sway_container *cont = node && node->type == N_CONTAINER ?
626 node->sway_container : NULL;
627 bool is_floating = cont && container_is_floating(cont);
628 bool is_floating_or_child = cont && container_is_floating_or_child(cont);
629 bool is_fullscreen_or_child = cont && container_is_fullscreen_or_child(cont);
630 enum wlr_edges edge = cont ? find_edge(cont, cursor) : WLR_EDGE_NONE;
631 enum wlr_edges resize_edge = edge ?
632 find_resize_edge(cont, cursor) : WLR_EDGE_NONE;
633 bool on_border = edge != WLR_EDGE_NONE;
634 bool on_contents = cont && !on_border && surface;
635 bool on_workspace = node && node->type == N_WORKSPACE;
636 bool on_titlebar = cont && !on_border && !surface;
637
638 // Handle mouse bindings
639 struct wlr_keyboard *keyboard = wlr_seat_get_keyboard(seat->wlr_seat);
640 uint32_t modifiers = keyboard ? wlr_keyboard_get_modifiers(keyboard) : 0;
641
642 char *device_identifier = device ? input_device_get_identifier(device)
643 : strdup("*");
644 struct sway_binding *binding = NULL;
645 if (state == WLR_BUTTON_PRESSED) {
646 state_add_button(cursor, button);
647 binding = get_active_mouse_binding(cursor,
648 config->current_mode->mouse_bindings, modifiers, false,
649 on_titlebar, on_border, on_contents, on_workspace,
650 device_identifier);
651 } else {
652 binding = get_active_mouse_binding(cursor,
653 config->current_mode->mouse_bindings, modifiers, true,
654 on_titlebar, on_border, on_contents, on_workspace,
655 device_identifier);
656 state_erase_button(cursor, button);
657 }
658 free(device_identifier);
659 if (binding) {
660 seat_execute_command(seat, binding);
661 return;
662 }
663
664 // Handle clicking an empty workspace
665 if (node && node->type == N_WORKSPACE) {
666 seat_set_focus(seat, node);
667 return;
668 }
669
670 // Handle clicking a layer surface
671 if (surface && wlr_surface_is_layer_surface(surface)) {
672 struct wlr_layer_surface_v1 *layer =
673 wlr_layer_surface_v1_from_wlr_surface(surface);
674 if (layer->current.keyboard_interactive) {
675 seat_set_focus_layer(seat, layer);
676 }
677 seat_pointer_notify_button(seat, time_msec, button, state);
678 return;
679 }
680
681 // Handle tiling resize via border
682 if (cont && resize_edge && button == BTN_LEFT &&
683 state == WLR_BUTTON_PRESSED && !is_floating) {
684 seat_set_focus_container(seat, cont);
685 seatop_begin_resize_tiling(seat, cont, button, edge);
686 return;
687 }
688
689 // Handle tiling resize via mod
690 bool mod_pressed = keyboard &&
691 (wlr_keyboard_get_modifiers(keyboard) & config->floating_mod);
692 if (cont && !is_floating_or_child && mod_pressed &&
693 state == WLR_BUTTON_PRESSED) {
694 uint32_t btn_resize = config->floating_mod_inverse ?
695 BTN_LEFT : BTN_RIGHT;
696 if (button == btn_resize) {
697 edge = 0;
698 edge |= cursor->cursor->x > cont->x + cont->width / 2 ?
699 WLR_EDGE_RIGHT : WLR_EDGE_LEFT;
700 edge |= cursor->cursor->y > cont->y + cont->height / 2 ?
701 WLR_EDGE_BOTTOM : WLR_EDGE_TOP;
702
703 const char *image = NULL;
704 if (edge == (WLR_EDGE_LEFT | WLR_EDGE_TOP)) {
705 image = "nw-resize";
706 } else if (edge == (WLR_EDGE_TOP | WLR_EDGE_RIGHT)) {
707 image = "ne-resize";
708 } else if (edge == (WLR_EDGE_RIGHT | WLR_EDGE_BOTTOM)) {
709 image = "se-resize";
710 } else if (edge == (WLR_EDGE_BOTTOM | WLR_EDGE_LEFT)) {
711 image = "sw-resize";
712 }
713 cursor_set_image(seat->cursor, image, NULL);
714 seat_set_focus_container(seat, cont);
715 seatop_begin_resize_tiling(seat, cont, button, edge);
716 return;
717 }
718 }
719
720 // Handle beginning floating move
721 if (cont && is_floating_or_child && !is_fullscreen_or_child &&
722 state == WLR_BUTTON_PRESSED) {
723 uint32_t btn_move = config->floating_mod_inverse ? BTN_RIGHT : BTN_LEFT;
724 if (button == btn_move && state == WLR_BUTTON_PRESSED &&
725 (mod_pressed || on_titlebar)) {
726 while (cont->parent) {
727 cont = cont->parent;
728 }
729 seat_set_focus_container(seat, cont);
730 seatop_begin_move_floating(seat, cont, button);
731 return;
732 }
733 }
734
735 // Handle beginning floating resize
736 if (cont && is_floating_or_child && !is_fullscreen_or_child &&
737 state == WLR_BUTTON_PRESSED) {
738 // Via border
739 if (button == BTN_LEFT && resize_edge != WLR_EDGE_NONE) {
740 seatop_begin_resize_floating(seat, cont, button, resize_edge);
741 return;
742 }
743
744 // Via mod+click
745 uint32_t btn_resize = config->floating_mod_inverse ?
746 BTN_LEFT : BTN_RIGHT;
747 if (mod_pressed && button == btn_resize) {
748 struct sway_container *floater = cont;
749 while (floater->parent) {
750 floater = floater->parent;
751 }
752 edge = 0;
753 edge |= cursor->cursor->x > floater->x + floater->width / 2 ?
754 WLR_EDGE_RIGHT : WLR_EDGE_LEFT;
755 edge |= cursor->cursor->y > floater->y + floater->height / 2 ?
756 WLR_EDGE_BOTTOM : WLR_EDGE_TOP;
757 seatop_begin_resize_floating(seat, floater, button, edge);
758 return;
759 }
760 }
761
762 // Handle moving a tiling container
763 if (config->tiling_drag && (mod_pressed || on_titlebar) &&
764 state == WLR_BUTTON_PRESSED && !is_floating_or_child &&
765 cont && cont->fullscreen_mode == FULLSCREEN_NONE) {
766 struct sway_container *focus = seat_get_focused_container(seat);
767 bool focused = focus == cont || container_has_ancestor(focus, cont);
768 if (on_titlebar && !focused) {
769 node = seat_get_focus_inactive(seat, &cont->node);
770 seat_set_focus(seat, node);
771 }
772
773 // If moving a container by it's title bar, use a threshold for the drag
774 if (!mod_pressed && config->tiling_drag_threshold > 0) {
775 seatop_begin_move_tiling_threshold(seat, cont, button);
776 } else {
777 seatop_begin_move_tiling(seat, cont, button);
778 }
779 return;
780 }
781
782 // Handle mousedown on a container surface
783 if (surface && cont && state == WLR_BUTTON_PRESSED) {
784 seat_set_focus_container(seat, cont);
785 seatop_begin_down(seat, cont, time_msec, button, sx, sy);
786 seat_pointer_notify_button(seat, time_msec, button, WLR_BUTTON_PRESSED);
787 return;
788 }
789
790 // Handle clicking a container surface or decorations
791 if (cont) {
792 node = seat_get_focus_inactive(seat, &cont->node);
793 seat_set_focus(seat, node);
794 seat_pointer_notify_button(seat, time_msec, button, state);
795 return;
796 }
797
798 seat_pointer_notify_button(seat, time_msec, button, state);
799} 293}
800 294
801static void handle_cursor_button(struct wl_listener *listener, void *data) { 295static void handle_cursor_button(struct wl_listener *listener, void *data) {
802 struct sway_cursor *cursor = wl_container_of(listener, cursor, button); 296 struct sway_cursor *cursor = wl_container_of(listener, cursor, button);
803 struct wlr_event_pointer_button *event = data; 297 struct wlr_event_pointer_button *event = data;
804 cursor_handle_activity(cursor); 298 cursor_handle_activity(cursor);
299
300 if (event->state == WLR_BUTTON_PRESSED) {
301 cursor->pressed_button_count++;
302 } else {
303 if (cursor->pressed_button_count > 0) {
304 cursor->pressed_button_count--;
305 } else {
306 sway_log(SWAY_ERROR, "Pressed button count was wrong");
307 }
308 }
309
805 dispatch_cursor_button(cursor, event->device, 310 dispatch_cursor_button(cursor, event->device,
806 event->time_msec, event->button, event->state); 311 event->time_msec, event->button, event->state);
807 transaction_commit_dirty(); 312 transaction_commit_dirty();
808} 313}
809 314
810static uint32_t wl_axis_to_button(struct wlr_event_pointer_axis *event) {
811 switch (event->orientation) {
812 case WLR_AXIS_ORIENTATION_VERTICAL:
813 return event->delta < 0 ? SWAY_SCROLL_UP : SWAY_SCROLL_DOWN;
814 case WLR_AXIS_ORIENTATION_HORIZONTAL:
815 return event->delta < 0 ? SWAY_SCROLL_LEFT : SWAY_SCROLL_RIGHT;
816 default:
817 sway_log(SWAY_DEBUG, "Unknown axis orientation");
818 return 0;
819 }
820}
821
822void dispatch_cursor_axis(struct sway_cursor *cursor, 315void dispatch_cursor_axis(struct sway_cursor *cursor,
823 struct wlr_event_pointer_axis *event) { 316 struct wlr_event_pointer_axis *event) {
824 struct sway_seat *seat = cursor->seat; 317 seatop_axis(cursor->seat, event);
825 struct sway_input_device *input_device =
826 event->device ? event->device->data : NULL;
827 struct input_config *ic =
828 input_device ? input_device_get_config(input_device) : NULL;
829
830 // Determine what's under the cursor
831 struct wlr_surface *surface = NULL;
832 double sx, sy;
833 struct sway_node *node = node_at_coords(seat,
834 cursor->cursor->x, cursor->cursor->y, &surface, &sx, &sy);
835 struct sway_container *cont = node && node->type == N_CONTAINER ?
836 node->sway_container : NULL;
837 enum wlr_edges edge = cont ? find_edge(cont, cursor) : WLR_EDGE_NONE;
838 bool on_border = edge != WLR_EDGE_NONE;
839 bool on_titlebar = cont && !on_border && !surface;
840 bool on_titlebar_border = cont && on_border &&
841 cursor->cursor->y < cont->content_y;
842 bool on_contents = cont && !on_border && surface;
843 bool on_workspace = node && node->type == N_WORKSPACE;
844 float scroll_factor =
845 (ic == NULL || ic->scroll_factor == FLT_MIN) ? 1.0f : ic->scroll_factor;
846
847 bool handled = false;
848
849 // Gather information needed for mouse bindings
850 struct wlr_keyboard *keyboard = wlr_seat_get_keyboard(seat->wlr_seat);
851 uint32_t modifiers = keyboard ? wlr_keyboard_get_modifiers(keyboard) : 0;
852 struct wlr_input_device *device =
853 input_device ? input_device->wlr_device : NULL;
854 char *dev_id = device ? input_device_get_identifier(device) : strdup("*");
855 uint32_t button = wl_axis_to_button(event);
856
857 // Handle mouse bindings - x11 mouse buttons 4-7 - press event
858 struct sway_binding *binding = NULL;
859 state_add_button(cursor, button);
860 binding = get_active_mouse_binding(cursor,
861 config->current_mode->mouse_bindings, modifiers, false,
862 on_titlebar, on_border, on_contents, on_workspace, dev_id);
863 if (binding) {
864 seat_execute_command(seat, binding);
865 handled = true;
866 }
867
868 // Scrolling on a tabbed or stacked title bar (handled as press event)
869 if (!handled && (on_titlebar || on_titlebar_border)) {
870 enum sway_container_layout layout = container_parent_layout(cont);
871 if (layout == L_TABBED || layout == L_STACKED) {
872 struct sway_node *tabcontainer = node_get_parent(node);
873 struct sway_node *active =
874 seat_get_active_tiling_child(seat, tabcontainer);
875 list_t *siblings = container_get_siblings(cont);
876 int desired = list_find(siblings, active->sway_container) +
877 round(scroll_factor * event->delta_discrete);
878 if (desired < 0) {
879 desired = 0;
880 } else if (desired >= siblings->length) {
881 desired = siblings->length - 1;
882 }
883 struct sway_node *old_focus = seat_get_focus(seat);
884 struct sway_container *new_sibling_con = siblings->items[desired];
885 struct sway_node *new_sibling = &new_sibling_con->node;
886 struct sway_node *new_focus =
887 seat_get_focus_inactive(seat, new_sibling);
888 if (node_has_ancestor(old_focus, tabcontainer)) {
889 seat_set_focus(seat, new_focus);
890 } else {
891 // Scrolling when focus is not in the tabbed container at all
892 seat_set_raw_focus(seat, new_sibling);
893 seat_set_raw_focus(seat, new_focus);
894 seat_set_raw_focus(seat, old_focus);
895 }
896 handled = true;
897 }
898 }
899
900 // Handle mouse bindings - x11 mouse buttons 4-7 - release event
901 binding = get_active_mouse_binding(cursor,
902 config->current_mode->mouse_bindings, modifiers, true,
903 on_titlebar, on_border, on_contents, on_workspace, dev_id);
904 state_erase_button(cursor, button);
905 if (binding) {
906 seat_execute_command(seat, binding);
907 handled = true;
908 }
909 free(dev_id);
910
911 if (!handled) {
912 wlr_seat_pointer_notify_axis(cursor->seat->wlr_seat, event->time_msec,
913 event->orientation, scroll_factor * event->delta,
914 round(scroll_factor * event->delta_discrete), event->source);
915 }
916} 318}
917 319
918static void handle_cursor_axis(struct wl_listener *listener, void *data) { 320static void handle_cursor_axis(struct wl_listener *listener, void *data) {
@@ -1054,8 +456,16 @@ static void handle_tool_axis(struct wl_listener *listener, void *data) {
1054 apply_mapping_from_region(event->device, ic->mapped_from_region, &x, &y); 456 apply_mapping_from_region(event->device, ic->mapped_from_region, &x, &y);
1055 } 457 }
1056 458
1057 cursor_motion_absolute(cursor, event->time_msec, event->device, x, y); 459 double lx, ly;
460 wlr_cursor_absolute_to_layout_coords(cursor->cursor, event->device,
461 x, y, &lx, &ly);
462
463 double dx = lx - cursor->cursor->x;
464 double dy = ly - cursor->cursor->y;
465
466 cursor_motion(cursor, event->time_msec, event->device, dx, dy, dx, dy);
1058 wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat); 467 wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat);
468 transaction_commit_dirty();
1059} 469}
1060 470
1061static void handle_tool_tip(struct wl_listener *listener, void *data) { 471static void handle_tool_tip(struct wl_listener *listener, void *data) {
@@ -1142,7 +552,7 @@ static void handle_request_set_cursor(struct wl_listener *listener,
1142 void *data) { 552 void *data) {
1143 struct sway_cursor *cursor = 553 struct sway_cursor *cursor =
1144 wl_container_of(listener, cursor, request_set_cursor); 554 wl_container_of(listener, cursor, request_set_cursor);
1145 if (seat_doing_seatop(cursor->seat)) { 555 if (!seatop_allows_set_cursor(cursor->seat)) {
1146 return; 556 return;
1147 } 557 }
1148 struct wlr_seat_pointer_request_set_cursor_event *event = data; 558 struct wlr_seat_pointer_request_set_cursor_event *event = data;
@@ -1257,7 +667,7 @@ struct sway_cursor *sway_cursor_create(struct sway_seat *seat) {
1257 667
1258 // input events 668 // input events
1259 wl_signal_add(&wlr_cursor->events.motion, &cursor->motion); 669 wl_signal_add(&wlr_cursor->events.motion, &cursor->motion);
1260 cursor->motion.notify = handle_cursor_motion; 670 cursor->motion.notify = handle_cursor_motion_relative;
1261 671
1262 wl_signal_add(&wlr_cursor->events.motion_absolute, 672 wl_signal_add(&wlr_cursor->events.motion_absolute,
1263 &cursor->motion_absolute); 673 &cursor->motion_absolute);