Restrict pointer input to lock surfaces during session lock

f986577dac5119f3bd8b9580a2d5765e5aa92082
Alexis Sellier committed ago 1 parent ca81fdcc
swm.c +10 -1
4047 4047
    focus_client(nullptr, 0);
4048 4048
4049 4049
    cur_lock = lock->lock = session_lock;
4050 4050
    locked                = true;
4051 4051
4052 +
    if (cursor_mode == CURSOR_RESIZE && grabc)
4053 +
        client_set_resizing(grabc, 0);
4054 +
    cursor_mode = CURSOR_NORMAL;
4055 +
    grabc       = nullptr;
4056 +
    grabws      = nullptr;
4057 +
    grab_button = 0;
4058 +
    cursor_constrain(nullptr);
4059 +
    wlr_seat_pointer_clear_focus(seat);
4060 +
4052 4061
    LISTEN(&session_lock->events.new_surface, &lock->new_surface, create_lock_surface);
4053 4062
    LISTEN(&session_lock->events.destroy, &lock->destroy, destroy_session_lock);
4054 4063
    LISTEN(&session_lock->events.unlock, &lock->unlock, unlock_session);
4055 4064
4056 4065
    wlr_session_lock_v1_send_locked(session_lock);
6962 6971
    struct wlr_surface       *surface = nullptr;
6963 6972
    client_t                 *c       = nullptr;
6964 6973
    layer_surface_t          *l       = nullptr;
6965 6974
    int                       layer;
6966 6975
6967 -
    for (layer = NUM_LAYERS - 1; !surface && layer >= 0; layer--) {
6976 +
    for (layer = NUM_LAYERS - 1; !surface && layer >= (locked ? LAYER_BLOCK : 0); layer--) {
6968 6977
        if (!(node = wlr_scene_node_at(&layers[layer]->node, x, y, nx, ny)))
6969 6978
            continue;
6970 6979
6971 6980
        if (node->type == WLR_SCENE_NODE_BUFFER &&
6972 6981
            (scene_surface = wlr_scene_surface_try_from_buffer(wlr_scene_buffer_from_node(node))))
test/clients.py +95 -0
905 905
            lock.destroy()
906 906
        connection.roundtrips()
907 907
    connection.close()
908 908
909 909
910 +
def lock_pointer(arguments: list[str]) -> None:
911 +
    """Keep application pointer events outside an active session lock."""
912 +
913 +
    app = Connection(
914 +
        WlCompositor, WlShm, XdgWmBase, WlSeat,
915 +
        ZwlrVirtualPointerManagerV1, ZwpVirtualKeyboardManagerV1,
916 +
    )
917 +
    seat = app.get(WlSeat)
918 +
    virtual = app.get(ZwlrVirtualPointerManagerV1).create_virtual_pointer(seat)
919 +
    keyboard = app.get(ZwpVirtualKeyboardManagerV1).create_virtual_keyboard(seat)
920 +
    app.roundtrips(2)
921 +
    pointer = seat.get_pointer()
922 +
    buttons = []
923 +
    pointer.dispatcher["button"] = lambda proxy, serial, time, button, state: buttons.append(state)
924 +
    window = Window(app, "lock-pointer", 0xFF335577, fullscreen=True)
925 +
    app.roundtrips(5)
926 +
    virtual.motion_absolute(1, 500, 500, 1000, 1000)
927 +
    virtual.frame()
928 +
    app.roundtrips(2)
929 +
930 +
    def click() -> None:
931 +
        """Send one click and collect its application events."""
932 +
933 +
        virtual.button(2, 0x110, 1)
934 +
        virtual.button(3, 0x110, 0)
935 +
        virtual.frame()
936 +
        app.roundtrips(2)
937 +
938 +
    click()
939 +
    if buttons != [1, 0]:
940 +
        raise Failure("application did not receive the unlocked pointer")
941 +
    locker = Connection(WlCompositor, WlShm, WlOutput, WlSeat, ExtSessionLockManagerV1)
942 +
    lock = locker.get(ExtSessionLockManagerV1).lock()
943 +
    acknowledged = []
944 +
    lock.dispatcher["locked"] = lambda proxy: acknowledged.append(True)
945 +
    locker.roundtrips(2)
946 +
    if not acknowledged:
947 +
        raise Failure("session lock was not acknowledged")
948 +
    buttons.clear()
949 +
    click()
950 +
    if buttons:
951 +
        raise Failure("stationary pointer click reached the application while locked")
952 +
    virtual.motion(4, 1.0, 1.0)
953 +
    virtual.frame()
954 +
    click()
955 +
    if buttons:
956 +
        raise Failure("moving pointer click reached the application while locked")
957 +
958 +
    surface = locker.get(WlCompositor).create_surface()
959 +
    role = lock.get_lock_surface(surface, locker.get(WlOutput))
960 +
961 +
    def configure(proxy, serial, width, height) -> None:
962 +
        """Fill the output with the lock surface."""
963 +
964 +
        proxy.ack_configure(serial)
965 +
        surface.attach(create_buffer(locker.get(WlShm), width, height, 0xFF101010), 0, 0)
966 +
        surface.damage_buffer(0, 0, width, height)
967 +
        surface.commit()
968 +
969 +
    role.dispatcher["configure"] = configure
970 +
    lock_buttons = []
971 +
    lock_pointer = locker.get(WlSeat).get_pointer()
972 +
    lock_pointer.dispatcher["button"] = (
973 +
        lambda proxy, serial, time, button, state: lock_buttons.append(state)
974 +
    )
975 +
    locker.roundtrips(4)
976 +
    virtual.motion(5, 1.0, 1.0)
977 +
    virtual.frame()
978 +
    click()
979 +
    locker.roundtrips(2)
980 +
    if buttons or lock_buttons != [1, 0]:
981 +
        raise Failure("pointer did not reach only the lock surface")
982 +
    role.destroy()
983 +
    surface.destroy()
984 +
    locker.roundtrips(2)
985 +
    virtual.motion(6, 1.0, 1.0)
986 +
    virtual.frame()
987 +
    click()
988 +
    if buttons:
989 +
        raise Failure("pointer reached the application after the lock surface closed")
990 +
    lock.unlock_and_destroy()
991 +
    locker.roundtrips(2)
992 +
    virtual.motion(7, 1.0, 1.0)
993 +
    virtual.frame()
994 +
    click()
995 +
    if buttons != [1, 0]:
996 +
        raise Failure("application pointer input did not resume after unlock")
997 +
    locker.close()
998 +
    window.destroy()
999 +
    virtual.destroy()
1000 +
    keyboard.destroy()
1001 +
    app.close()
1002 +
1003 +
910 1004
def shortcut_repeat() -> None:
911 1005
    """Stop a held focus shortcut when its modifier is released."""
912 1006
913 1007
    connection = Connection(WlCompositor, WlShm, WlSeat, XdgWmBase, ZwpVirtualKeyboardManagerV1)
914 1008
    seat = connection.get(WlSeat)
1321 1415
    "workspace-groups": workspace_groups,
1322 1416
    "output-power": output_power,
1323 1417
    "output-management": output_management,
1324 1418
    "layer": layer,
1325 1419
    "session-lock": session_lock,
1420 +
    "lock-pointer": lock_pointer,
1326 1421
    "text-input": text_input,
1327 1422
    "x11": x11,
1328 1423
}
1329 1424
1330 1425
test/run.py +1 -0
681 681
        terminate(one)
682 682
        terminate(two)
683 683
        protocol("xdg-lifecycle", 2)
684 684
        protocol("transient", 2)
685 685
        protocol("layer", 2)
686 +
        protocol("lock-pointer")
686 687
        protocol("session-lock", 2)
687 688
        protocol("text-input")
688 689
        protocol("x11")
689 690
690 691
    test("protocol lifecycle", lifecycle)