Keep pointer grabs tied to their initiating button
cfe3258e3c3df3c9b6d87d25361dc93faf27d069
1 parent
a6b31967
Makefile
+5 -1
| 189 | 189 | @echo "cc test/unit.c => $@" |
|
| 190 | 190 | @$(CC) $(TEST_CPPFLAGS) $(TEST_CFLAGS) test/unit.c util.c \ |
|
| 191 | 191 | ext-workspace-v1-protocol.c swm-toplevel-v1-protocol.c \ |
|
| 192 | 192 | swm-workspace-v1-protocol.c \ |
|
| 193 | 193 | -Wl,--wrap=wlr_fractional_scale_v1_notify_scale \ |
|
| 194 | - | -Wl,--wrap=wlr_surface_set_preferred_buffer_scale $(LDLIBS) -o $@ |
|
| 194 | + | -Wl,--wrap=wlr_surface_set_preferred_buffer_scale \ |
|
| 195 | + | -Wl,--wrap=wlr_idle_notifier_v1_notify_activity \ |
|
| 196 | + | -Wl,--wrap=wlr_seat_pointer_notify_button \ |
|
| 197 | + | -Wl,--wrap=wlr_cursor_set_xcursor \ |
|
| 198 | + | -Wl,--wrap=wlr_output_layout_output_at $(LDLIBS) -o $@ |
|
| 195 | 199 | ||
| 196 | 200 | $(TEST_BIN)/ctl-unit: test/ctl_unit.c swmctl.c \ |
|
| 197 | 201 | ext-foreign-toplevel-list-v1-client-protocol.h \ |
|
| 198 | 202 | swm-toplevel-v1-client-protocol.h swm-workspace-v1-client-protocol.h \ |
|
| 199 | 203 | ext-foreign-toplevel-list-v1-protocol.c swm-toplevel-v1-protocol.c \ |
swm.c
+13 -1
| 738 | 738 | static unsigned int cursor_mode; |
|
| 739 | 739 | static client_t *grabc; |
|
| 740 | 740 | static int grabcx, grabcy; /* Pointer position within the grabbed window. */ |
|
| 741 | 741 | static int grabx, graby; /* Pointer position within the display layout. */ |
|
| 742 | 742 | static uint32_t grabedges; |
|
| 743 | + | static uint32_t pressed_button, grab_button; |
|
| 743 | 744 | static struct wlr_box grabgeom; |
|
| 744 | 745 | static workspace_t *grabws; /* Workspace changed by a tiled resize. */ |
|
| 745 | 746 | static bool grab_horizontal; /* Tiled resize changes a horizontal boundary. */ |
|
| 746 | 747 | static bool tiled_resize_hover; /* Pointer uses a tiled resize cursor. */ |
|
| 747 | 748 | static bool tiled_resize_hover_horizontal; /* Hovered boundary orientation. */ |
| 1986 | 1987 | ||
| 1987 | 1988 | wlr_idle_notifier_v1_notify_activity(idle_notifier, seat); |
|
| 1988 | 1989 | ||
| 1989 | 1990 | switch (event->state) { |
|
| 1990 | 1991 | case WL_POINTER_BUTTON_STATE_PRESSED: |
|
| 1992 | + | if (cursor_mode != CURSOR_NORMAL && cursor_mode != CURSOR_PRESSED) |
|
| 1993 | + | break; |
|
| 1994 | + | pressed_button = event->button; |
|
| 1991 | 1995 | cursor_mode = CURSOR_PRESSED; |
|
| 1992 | 1996 | selmon = point_to_monitor(cursor->x, cursor->y); |
|
| 1993 | 1997 | ||
| 1994 | 1998 | if (locked) |
|
| 1995 | 1999 | break; |
| 2014 | 2018 | tiled_resize_boundary( |
|
| 2015 | 2019 | selmon, (int)round(cursor->x), (int)round(cursor->y), &grab_horizontal |
|
| 2016 | 2020 | )) { |
|
| 2017 | 2021 | grabws = selmon->ws; |
|
| 2018 | 2022 | cursor_mode = CURSOR_TILE_RESIZE; |
|
| 2023 | + | grab_button = event->button; |
|
| 2019 | 2024 | tiled_resize_cursor(true, grab_horizontal); |
|
| 2020 | 2025 | return; |
|
| 2021 | 2026 | } |
|
| 2022 | 2027 | tiled_resize_cursor(false, false); |
|
| 2023 | 2028 |
| 2027 | 2032 | return; |
|
| 2028 | 2033 | } |
|
| 2029 | 2034 | } |
|
| 2030 | 2035 | break; |
|
| 2031 | 2036 | case WL_POINTER_BUTTON_STATE_RELEASED: |
|
| 2032 | - | /* Releasing any button ends an interactive move or resize. */ |
|
| 2037 | + | /* Only the button that started a grab can end it. Other buttons remain |
|
| 2038 | + | * available to the application throughout the operation. */ |
|
| 2039 | + | if (cursor_mode != CURSOR_NORMAL && cursor_mode != CURSOR_PRESSED && |
|
| 2040 | + | event->button != grab_button) |
|
| 2041 | + | break; |
|
| 2033 | 2042 | /* TODO: Restore the cursor requested by the window under the pointer. */ |
|
| 2034 | 2043 | if (!locked && cursor_mode != CURSOR_NORMAL && cursor_mode != CURSOR_PRESSED) { |
|
| 2035 | 2044 | if (cursor_mode == CURSOR_TILE_RESIZE) { |
|
| 2036 | 2045 | cursor_mode = CURSOR_NORMAL; |
|
| 2046 | + | grab_button = 0; |
|
| 2037 | 2047 | grabws = nullptr; |
|
| 2038 | 2048 | tiled_resize_update(); |
|
| 2039 | 2049 | return; |
|
| 2040 | 2050 | } |
|
| 2041 | 2051 | if (cursor_mode == CURSOR_RESIZE) |
|
| 2042 | 2052 | client_set_resizing(grabc, 0); |
|
| 2043 | 2053 | wlr_cursor_set_xcursor(cursor, cursor_mgr, "default"); |
|
| 2044 | 2054 | cursor_mode = CURSOR_NORMAL; |
|
| 2055 | + | grab_button = 0; |
|
| 2045 | 2056 | /* Move the window to the workspace on its new display. The |
|
| 2046 | 2057 | * cursor can sit outside every output (on a layout edge or in a |
|
| 2047 | 2058 | * gap); keep the window on its current display in that case. */ |
|
| 2048 | 2059 | selmon = point_to_monitor(cursor->x, cursor->y); |
|
| 2049 | 2060 | set_monitor(grabc, selmon ? selmon : grabc->mon, 0); |
| 4515 | 4526 | ||
| 4516 | 4527 | restore_max_stack(grabc->mon); |
|
| 4517 | 4528 | ||
| 4518 | 4529 | /* Float the window and let pointer motion move or resize it. */ |
|
| 4519 | 4530 | grabc->persist_float = 1; |
|
| 4531 | + | grab_button = pressed_button; |
|
| 4520 | 4532 | set_floating(grabc, 1); |
|
| 4521 | 4533 | ||
| 4522 | 4534 | switch (cursor_mode = arg->u) { |
|
| 4523 | 4535 | case CURSOR_MOVE: |
|
| 4524 | 4536 | grabcx = (int)round(cursor->x) - grabc->geom.x; |
test/unit.c
+41 -0
| 21 | 21 | void __wrap_wlr_surface_set_preferred_buffer_scale(struct wlr_surface *surface, int32_t scale) { |
|
| 22 | 22 | scaled_surface = surface; |
|
| 23 | 23 | buffer_scale = scale; |
|
| 24 | 24 | } |
|
| 25 | 25 | ||
| 26 | + | void __wrap_wlr_idle_notifier_v1_notify_activity( |
|
| 27 | + | struct wlr_idle_notifier_v1 *notifier, struct wlr_seat *seat |
|
| 28 | + | ) {} |
|
| 29 | + | ||
| 30 | + | void __wrap_wlr_cursor_set_xcursor( |
|
| 31 | + | struct wlr_cursor *cursor, struct wlr_xcursor_manager *manager, const char *name |
|
| 32 | + | ) {} |
|
| 33 | + | ||
| 34 | + | void __wrap_wlr_seat_pointer_notify_button( |
|
| 35 | + | struct wlr_seat *seat, uint32_t time_msec, uint32_t button, uint32_t state |
|
| 36 | + | ) {} |
|
| 37 | + | ||
| 38 | + | struct wlr_output *__wrap_wlr_output_layout_output_at( |
|
| 39 | + | struct wlr_output_layout *layout, double lx, double ly |
|
| 40 | + | ) { |
|
| 41 | + | return nullptr; |
|
| 42 | + | } |
|
| 43 | + | ||
| 26 | 44 | /* Verify that releasing a pool item clears its storage. */ |
|
| 27 | 45 | static void test_pool(pool_t *pool) { |
|
| 28 | 46 | unsigned char *item = pool_take(pool); |
|
| 29 | 47 | size_t i; |
|
| 30 | 48 |
| 727 | 745 | struct wlr_pointer_swipe_end_event swipe_end = { .time_msec = 3, .cancelled = false }; |
|
| 728 | 746 | struct wlr_pointer_pinch_begin_event pinch_begin = { .time_msec = 4, .fingers = 2 }; |
|
| 729 | 747 | struct wlr_pointer_pinch_update_event pinch_update = { |
|
| 730 | 748 | .time_msec = 5, |
|
| 731 | 749 | .dx = 1, |
|
| 750 | + | ||
| 732 | 751 | .dy = 2, |
|
| 733 | 752 | .scale = 1.1, |
|
| 734 | 753 | .rotation = 3, |
|
| 735 | 754 | }; |
|
| 736 | 755 | struct wlr_pointer_pinch_end_event pinch_end = { .time_msec = 6, .cancelled = true }; |
| 752 | 771 | gesture_hold_begin(nullptr, &hold_begin); |
|
| 753 | 772 | gesture_hold_end(nullptr, &hold_end); |
|
| 754 | 773 | wl_display_destroy(dpy); |
|
| 755 | 774 | dpy = nullptr; |
|
| 756 | 775 | } |
|
| 776 | + | /* Verify an unrelated button release does not cancel a pointer grab. */ |
|
| 777 | + | static void test_pointer_grab_button(void) { |
|
| 778 | + | struct wlr_pointer_button_event event = { |
|
| 779 | + | .button = BTN_RIGHT, |
|
| 780 | + | .state = WL_POINTER_BUTTON_STATE_RELEASED, |
|
| 781 | + | }; |
|
| 782 | + | struct wlr_cursor fake_cursor = {}; |
|
| 783 | + | client_t client = {}; |
|
| 784 | + | ||
| 785 | + | cursor = &fake_cursor; |
|
| 786 | + | grabc = &client; |
|
| 787 | + | grab_button = BTN_LEFT; |
|
| 788 | + | cursor_mode = CURSOR_MOVE; |
|
| 789 | + | client.type = XDG_SHELL; |
|
| 790 | + | client.mon = nullptr; |
|
| 791 | + | client.ws = nullptr; |
|
| 792 | + | client.persist_float = 0; |
|
| 793 | + | button_press(nullptr, &event); |
|
| 794 | + | assert(cursor_mode == CURSOR_MOVE); |
|
| 795 | + | assert(grabc == &client); |
|
| 796 | + | } |
|
| 757 | 797 | ||
| 758 | 798 | /* Verify text-input and input-method wrappers are created and released. */ |
|
| 759 | 799 | static void test_input_method_lifecycle(void) { |
|
| 760 | 800 | struct wlr_seat fake_seat = {}; |
|
| 761 | 801 | struct wlr_text_input_manager_v3 fake_ti_manager = {}; |
| 970 | 1010 | { "child-reaping", test_child_reaping }, |
|
| 971 | 1011 | { "spawn-tracking", test_spawn_tracking }, |
|
| 972 | 1012 | { "command-failure-notification", test_command_failure_notification }, |
|
| 973 | 1013 | { "commands-without-clients", test_commands_without_clients }, |
|
| 974 | 1014 | { "small-helpers", test_small_helpers }, |
|
| 1015 | + | { "pointer-grab-button", test_pointer_grab_button }, |
|
| 975 | 1016 | { "client-accessors", test_client_accessors }, |
|
| 976 | 1017 | { "autostart", test_autostart_execution }, |
|
| 977 | 1018 | { "pointer-gestures", test_pointer_gestures }, |
|
| 978 | 1019 | { "input-method", test_input_method_lifecycle }, |
|
| 979 | 1020 | { "input-early-paths", test_input_early_paths }, |