Restrict pointer input to lock surfaces during session lock
f986577dac5119f3bd8b9580a2d5765e5aa92082
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) |