Route keyboard input through input-method grabs

20eaf8cfbf494ea4c7741ea80fdcb98d815ec48f
Alexis Sellier committed ago 1 parent 8f3a9fa3
swm.c +46 -20
253 253
    const arg_t arg;
254 254
} key_t;
255 255
256 256
/* Physical or virtual keyboard group. */
257 257
typedef struct {
258 -
    struct wlr_keyboard_group *wlr_group;
259 -
    bool                       is_virtual;
260 -
    int                        nsyms;
261 -
    uint32_t                   repeat_keycode; /* Valid only when nsyms is nonzero. */
262 -
    const xkb_keysym_t        *keysyms;        /* Valid only when nsyms is nonzero. */
263 -
    uint32_t                   mods;           /* Valid only when nsyms is nonzero. */
264 -
    struct wl_event_source    *key_repeat_source;
265 -
    uint32_t                   handled_keys[(KEY_CNT + 31) / 32];
266 -
    struct wl_listener         modifiers;
267 -
    struct wl_listener         key;
268 -
    struct wl_listener         destroy;
258 +
    struct wlr_keyboard_group      *wlr_group;
259 +
    bool                            is_virtual;
260 +
    struct wlr_virtual_keyboard_v1 *virtual_keyboard; /* Source of a virtual group. */
261 +
    int                             nsyms;
262 +
    uint32_t                        repeat_keycode; /* Valid only when nsyms is nonzero. */
263 +
    const xkb_keysym_t             *keysyms;        /* Valid only when nsyms is nonzero. */
264 +
    uint32_t                        mods;           /* Valid only when nsyms is nonzero. */
265 +
    struct wl_event_source         *key_repeat_source;
266 +
    uint32_t                        handled_keys[(KEY_CNT + 31) / 32];
267 +
    struct wl_listener              modifiers;
268 +
    struct wl_listener              key;
269 +
    struct wl_listener              destroy;
269 270
} keyboard_group_t;
270 271
271 272
/* Layer-shell surface. */
272 273
typedef struct {
273 274
    /* This must remain first so generic surface code can read it. */
3911 3912
    if (!group->nsyms)
3912 3913
        return false;
3913 3914
    return event->state == WL_KEYBOARD_KEY_STATE_PRESSED || event->keycode == group->repeat_keycode;
3914 3915
}
3915 3916
3916 -
/* Handle shortcuts and forward unhandled key events to the focused application. */
3917 +
/* Select the input-method grab for a keyboard's events. */
3918 +
static struct wlr_input_method_keyboard_grab_v2 *input_method_grab(keyboard_group_t *group) {
3919 +
    if (locked || !input_method || !input_method->keyboard_grab)
3920 +
        return nullptr;
3921 +
    if (group->virtual_keyboard && wl_resource_get_client(group->virtual_keyboard->resource) ==
3922 +
                                       wl_resource_get_client(input_method->resource))
3923 +
        return nullptr;
3924 +
    return input_method->keyboard_grab;
3925 +
}
3926 +
3927 +
/* Handle shortcuts and send unhandled keys to the input recipient. */
3917 3928
void key_press(struct wl_listener *listener, void *data) {
3918 3929
    int i;
3919 3930
3920 3931
    /* This event is raised when a key is pressed or released. */
3921 -
    keyboard_group_t              *group = wl_container_of(listener, group, key);
3922 -
    struct wlr_keyboard_key_event *event = data;
3932 +
    keyboard_group_t                         *group = wl_container_of(listener, group, key);
3933 +
    struct wlr_keyboard_key_event            *event = data;
3934 +
    struct wlr_input_method_keyboard_grab_v2 *grab;
3923 3935
3924 3936
    /* Convert the input key code to the numbering used by XKB. */
3925 3937
    uint32_t keycode = event->keycode + 8;
3926 3938
3927 3939
    /* Resolve the key to one or more symbols using the active key map. */
3946 3958
    }
3947 3959
    if (!key_event_should_forward(group, event->keycode, event->state, handled))
3948 3960
        return;
3949 3961
3950 3962
    wlr_seat_set_keyboard(seat, &group->wlr_group->keyboard);
3951 -
    /* Forward unhandled keys to the focused application. */
3952 -
    wlr_seat_keyboard_notify_key(seat, event->time_msec, event->keycode, event->state);
3963 +
    /* Send unhandled keys to the input method or focused application. */
3964 +
    if ((grab = input_method_grab(group))) {
3965 +
        wlr_input_method_keyboard_grab_v2_set_keyboard(grab, &group->wlr_group->keyboard);
3966 +
        wlr_input_method_keyboard_grab_v2_send_key(
3967 +
            grab, event->time_msec, event->keycode, event->state
3968 +
        );
3969 +
    } else
3970 +
        wlr_seat_keyboard_notify_key(seat, event->time_msec, event->keycode, event->state);
3953 3971
}
3954 3972
3955 -
/* Forward a keyboard's updated modifier state to the focused application. */
3973 +
/* Send a keyboard's modifier state to the input recipient. */
3956 3974
void key_press_modifiers(struct wl_listener *listener, void *data) {
3957 3975
    /* A modifier such as Shift or Alt changed. */
3958 -
    keyboard_group_t *group = wl_container_of(listener, group, modifiers);
3976 +
    keyboard_group_t                         *group = wl_container_of(listener, group, modifiers);
3977 +
    struct wlr_input_method_keyboard_grab_v2 *grab;
3959 3978
3960 3979
    wlr_seat_set_keyboard(seat, &group->wlr_group->keyboard);
3961 -
    /* Forward the new modifier state to the focused application. */
3962 -
    wlr_seat_keyboard_notify_modifiers(seat, &group->wlr_group->keyboard.modifiers);
3980 +
    /* Send modifiers to the input method or focused application. */
3981 +
    if ((grab = input_method_grab(group))) {
3982 +
        wlr_input_method_keyboard_grab_v2_set_keyboard(grab, &group->wlr_group->keyboard);
3983 +
        wlr_input_method_keyboard_grab_v2_send_modifiers(
3984 +
            grab, &group->wlr_group->keyboard.modifiers
3985 +
        );
3986 +
    } else
3987 +
        wlr_seat_keyboard_notify_modifiers(seat, &group->wlr_group->keyboard.modifiers);
3963 3988
    tiled_resize_update();
3964 3989
}
3965 3990
3966 3991
/* Repeat the active shortcut at the keyboard's configured rate. */
3967 3992
int key_repeat(void *data) {
6863 6888
    if (!group) {
6864 6889
        wl_resource_post_no_memory(kb->resource);
6865 6890
        return;
6866 6891
    }
6867 6892
    /* Use the virtual keyboard's key mapping. */
6893 +
    group->virtual_keyboard = kb;
6868 6894
    wlr_keyboard_set_keymap(&kb->keyboard, group->wlr_group->keyboard.keymap);
6869 6895
    LISTEN(&kb->keyboard.base.events.destroy, &group->destroy, destroy_keyboard_group);
6870 6896
6871 6897
    /* Add the virtual keyboard to its group. */
6872 6898
    wlr_keyboard_group_add_keyboard(group->wlr_group, &kb->keyboard);
test/clients.py +61 -0
852 852
            lock.destroy()
853 853
        connection.roundtrips()
854 854
    connection.close()
855 855
856 856
857 +
def input_method_keys() -> None:
858 +
    """Check keyboard grabs and input-method key reinjection."""
859 +
860 +
    connection = Connection(
861 +
        WlCompositor, WlShm, WlSeat, XdgWmBase,
862 +
        ZwpInputMethodManagerV2, ZwpVirtualKeyboardManagerV1,
863 +
    )
864 +
    seat = connection.get(WlSeat)
865 +
    virtual = connection.get(ZwpVirtualKeyboardManagerV1).create_virtual_keyboard(seat)
866 +
    keymap, indices = keymap_text()
867 +
    with tempfile.TemporaryFile(dir=os.environ.get("SWM_TEST_DIR")) as stream:
868 +
        stream.write(keymap + b"\0")
869 +
        stream.flush()
870 +
        virtual.keymap(WlKeyboard.keymap_format.xkb_v1, stream.fileno(), len(keymap) + 1)
871 +
    connection.roundtrips()
872 +
    keyboard = seat.get_keyboard()
873 +
    app_keys = []
874 +
    keyboard.dispatcher["key"] = lambda proxy, serial, time, key, state: app_keys.append((key, state))
875 +
    window = Window(connection, "input-method-keys", 0xFF556677)
876 +
    connection.roundtrips(4)
877 +
    method = connection.get(ZwpInputMethodManagerV2).get_input_method(seat)
878 +
    grab = method.grab_keyboard()
879 +
    grabbed_keys = []
880 +
    grabbed_modifiers = []
881 +
    grab.dispatcher["keymap"] = lambda proxy, format, fd, size: os.close(fd)
882 +
    grab.dispatcher["key"] = lambda proxy, serial, time, key, state: grabbed_keys.append((key, state))
883 +
    grab.dispatcher["modifiers"] = lambda proxy, serial, depressed, latched, locked, group: (
884 +
        grabbed_modifiers.append(depressed)
885 +
    )
886 +
    connection.roundtrips(2)
887 +
    subprocess.run([sys.executable, __file__, "keyboard", "30", "1"], check=True)
888 +
    connection.roundtrips(2)
889 +
    if (30, 1) not in grabbed_keys or (30, 0) not in grabbed_keys:
890 +
        raise Failure("input method did not receive the grabbed key")
891 +
    if not any(grabbed_modifiers):
892 +
        raise Failure("input method did not receive Shift")
893 +
    if app_keys:
894 +
        raise Failure("grabbed keys reached the application")
895 +
896 +
    count = len(grabbed_keys)
897 +
    virtual.modifiers(1 << indices["shift"], 0, 0, 0)
898 +
    virtual.key(3, 30, 1)
899 +
    virtual.key(4, 30, 0)
900 +
    virtual.modifiers(0, 0, 0, 0)
901 +
    connection.roundtrips(2)
902 +
    if app_keys != [(30, 1), (30, 0)] or len(grabbed_keys) != count:
903 +
        raise Failure("input-method keys did not reach the application directly")
904 +
    grab.release()
905 +
    connection.roundtrips()
906 +
    app_keys.clear()
907 +
    subprocess.run([sys.executable, __file__, "keyboard", "48", "0"], check=True)
908 +
    connection.roundtrips(2)
909 +
    if app_keys != [(48, 1), (48, 0)]:
910 +
        raise Failure("keyboard input did not resume after the grab ended")
911 +
    method.destroy()
912 +
    window.destroy()
913 +
    virtual.destroy()
914 +
    connection.close()
915 +
916 +
857 917
def text_input(arguments: list[str]) -> None:
858 918
    """Exercise text input, input methods, and input inhibitors together."""
859 919
860 920
    connection = Connection(
861 921
        WlCompositor,
1006 1066
    popup.destroy()
1007 1067
    popup_surface.destroy()
1008 1068
    text.destroy()
1009 1069
    method.destroy()
1010 1070
    connection.close()
1071 +
    input_method_keys()
1011 1072
1012 1073
1013 1074
def x11(arguments: list[str]) -> None:
1014 1075
    """Exercise managed, configured, fullscreen, and unmanaged X11 windows."""
1015 1076