Keep pointer grabs tied to their initiating button

cfe3258e3c3df3c9b6d87d25361dc93faf27d069
Alexis Sellier committed ago 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 },