diff options
Diffstat (limited to 'sway/input/cursor.c')
-rw-r--r-- | sway/input/cursor.c | 680 |
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 | */ | ||
165 | static 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 | |||
200 | static 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 | */ | ||
231 | static 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 | |||
240 | static 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 | |||
280 | void cursor_rebase(struct sway_cursor *cursor) { | 161 | void 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 | ||
289 | void cursor_rebase_all(void) { | 166 | void 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 | ||
348 | void cursor_send_pointer_motion(struct sway_cursor *cursor, uint32_t time_msec, | 223 | static 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 | |||
414 | static 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 | ||
462 | static void cursor_motion_absolute(struct sway_cursor *cursor, | 258 | static 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 | */ | ||
511 | static 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 | */ | ||
531 | static 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 | */ | ||
552 | static 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 | ||
598 | void dispatch_cursor_button(struct sway_cursor *cursor, | 285 | void 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 | ||
801 | static void handle_cursor_button(struct wl_listener *listener, void *data) { | 295 | static 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 | ||
810 | static 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 | |||
822 | void dispatch_cursor_axis(struct sway_cursor *cursor, | 315 | void 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 | ||
918 | static void handle_cursor_axis(struct wl_listener *listener, void *data) { | 320 | static 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 | ||
1061 | static void handle_tool_tip(struct wl_listener *listener, void *data) { | 471 | static 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); |