diff --git a/examples/compositor.c b/examples/compositor.c index 53b385b7..8154c2e3 100644 --- a/examples/compositor.c +++ b/examples/compositor.c @@ -128,6 +128,15 @@ static void example_set_focused_surface(struct sample_state *sample, } } + if (surface) { + // TODO: send array of currently pressed keys + struct wl_array keys; + wl_array_init(&keys); + wlr_seat_keyboard_enter(sample->wl_seat, surface->surface, keys); + } else { + wlr_seat_keyboard_clear_focus(sample->wl_seat); + } + sample->focused_surface = surface; } @@ -332,64 +341,30 @@ static void handle_output_frame(struct output_state *output, } static void handle_keyboard_key(struct keyboard_state *keyboard, - uint32_t keycode, xkb_keysym_t sym, enum wlr_key_state key_state) { + uint32_t keycode, xkb_keysym_t sym, enum wlr_key_state key_state, + uint64_t time_usec) { struct compositor_state *state = keyboard->compositor; struct sample_state *sample = state->data; - struct wl_resource *res = NULL; - struct wlr_seat_handle *seat_handle = NULL; - wl_list_for_each(res, &sample->wlr_compositor->surfaces, link) { - break; - } + uint32_t depressed = xkb_state_serialize_mods(keyboard->xkb_state, + XKB_STATE_MODS_DEPRESSED); + uint32_t latched = xkb_state_serialize_mods(keyboard->xkb_state, + XKB_STATE_MODS_LATCHED); + uint32_t locked = xkb_state_serialize_mods(keyboard->xkb_state, + XKB_STATE_MODS_LOCKED); + uint32_t group = xkb_state_serialize_layout(keyboard->xkb_state, + XKB_STATE_LAYOUT_EFFECTIVE); - if (res) { - seat_handle = wlr_seat_handle_for_client(sample->wl_seat, - wl_resource_get_client(res)); - } - - if (res != sample->focus && seat_handle && seat_handle->keyboard) { - struct wl_array keys; - wl_array_init(&keys); - uint32_t serial = wl_display_next_serial(state->display); - wl_keyboard_send_enter(seat_handle->keyboard, serial, res, &keys); - sample->focus = res; - } - - if (seat_handle && seat_handle->keyboard) { - uint32_t depressed = xkb_state_serialize_mods(keyboard->xkb_state, - XKB_STATE_MODS_DEPRESSED); - uint32_t latched = xkb_state_serialize_mods(keyboard->xkb_state, - XKB_STATE_MODS_LATCHED); - uint32_t locked = xkb_state_serialize_mods(keyboard->xkb_state, - XKB_STATE_MODS_LOCKED); - uint32_t group = xkb_state_serialize_layout(keyboard->xkb_state, - XKB_STATE_LAYOUT_EFFECTIVE); - uint32_t modifiers_serial = wl_display_next_serial(state->display); - uint32_t key_serial = wl_display_next_serial(state->display); - wl_keyboard_send_modifiers(seat_handle->keyboard, modifiers_serial, - depressed, latched, locked, group); - wl_keyboard_send_key(seat_handle->keyboard, key_serial, 0, keycode, - key_state); - } + wlr_seat_keyboard_send_modifiers(sample->wl_seat, depressed, latched, + locked, group); + wlr_seat_keyboard_send_key(sample->wl_seat, (uint32_t)time_usec, keycode, + key_state); if (sym == XKB_KEY_Super_L || sym == XKB_KEY_Super_R) { sample->mod_down = key_state == WLR_KEY_PRESSED; } } -static void handle_keyboard_bound(struct wl_listener *listener, void *data) { - struct wlr_seat_handle *handle = data; - struct sample_state *state = - wl_container_of(listener, state, keyboard_bound); - - wl_keyboard_send_keymap(handle->keyboard, WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1, - state->keymap_fd, state->keymap_size); - - if (wl_resource_get_version(handle->keyboard) >= 2) { - wl_keyboard_send_repeat_info(handle->keyboard, 25, 600); - } -} - static struct wlr_xdg_surface_v6 *example_xdg_surface_at( struct sample_state *sample, int lx, int ly) { struct wlr_xdg_surface_v6 *xdg_surface; @@ -669,8 +644,6 @@ int main(int argc, char *argv[]) { state.wl_seat = wlr_seat_create(compositor.display, "seat0"); assert(state.wl_seat); - state.keyboard_bound.notify = handle_keyboard_bound; - wl_signal_add(&state.wl_seat->events.keyboard_bound, &state.keyboard_bound); wlr_seat_set_capabilities(state.wl_seat, WL_SEAT_CAPABILITY_KEYBOARD | WL_SEAT_CAPABILITY_POINTER | WL_SEAT_CAPABILITY_TOUCH); @@ -687,6 +660,10 @@ int main(int argc, char *argv[]) { free(keymap); break; } + + wlr_seat_keyboard_set_keymap(state.wl_seat, state.keymap_fd, + state.keymap_size); + state.xwayland = wlr_xwayland_create(compositor.display, state.wlr_compositor); diff --git a/examples/output-layout.c b/examples/output-layout.c index be542630..69cb241a 100644 --- a/examples/output-layout.c +++ b/examples/output-layout.c @@ -157,7 +157,8 @@ static void update_velocities(struct compositor_state *state, } static void handle_keyboard_key(struct keyboard_state *kbstate, - uint32_t keycode, xkb_keysym_t sym, enum wlr_key_state key_state) { + uint32_t keycode, xkb_keysym_t sym, enum wlr_key_state key_state, + uint64_t time_usec) { // NOTE: It may be better to simply refer to our key state during each frame // and make this change in pixels/sec^2 // Also, key repeat diff --git a/examples/rotation.c b/examples/rotation.c index 2596e492..dfb81e59 100644 --- a/examples/rotation.c +++ b/examples/rotation.c @@ -99,7 +99,8 @@ static void update_velocities(struct compositor_state *state, } static void handle_keyboard_key(struct keyboard_state *kbstate, - uint32_t keycode, xkb_keysym_t sym, enum wlr_key_state key_state) { + uint32_t keycode, xkb_keysym_t sym, enum wlr_key_state key_state, + uint64_t time_usec) { // NOTE: It may be better to simply refer to our key state during each frame // and make this change in pixels/sec^2 // Also, key repeat diff --git a/examples/shared.c b/examples/shared.c index 0346c96d..19cf83f0 100644 --- a/examples/shared.c +++ b/examples/shared.c @@ -48,7 +48,8 @@ static void keyboard_key_notify(struct wl_listener *listener, void *data) { key_state == WLR_KEY_PRESSED ? "pressed" : "released"); } if (kbstate->compositor->keyboard_key_cb) { - kbstate->compositor->keyboard_key_cb(kbstate, event->keycode, sym, key_state); + kbstate->compositor->keyboard_key_cb(kbstate, event->keycode, sym, + key_state, event->time_usec); } if (sym == XKB_KEY_Escape) { wl_display_terminate(kbstate->compositor->display); diff --git a/examples/shared.h b/examples/shared.h index 7cf4db63..f564bffa 100644 --- a/examples/shared.h +++ b/examples/shared.h @@ -87,7 +87,7 @@ struct compositor_state { struct output_state *s); void (*keyboard_remove_cb)(struct keyboard_state *s); void (*keyboard_key_cb)(struct keyboard_state *s, uint32_t keycode, - xkb_keysym_t sym, enum wlr_key_state key_state); + xkb_keysym_t sym, enum wlr_key_state key_state, uint64_t time_usec); void (*pointer_motion_cb)(struct pointer_state *s, double d_x, double d_y); void (*pointer_motion_absolute_cb)(struct pointer_state *s, diff --git a/include/wlr/types/wlr_seat.h b/include/wlr/types/wlr_seat.h index e7bd82fb..0270ad4e 100644 --- a/include/wlr/types/wlr_seat.h +++ b/include/wlr/types/wlr_seat.h @@ -31,6 +31,18 @@ struct wlr_seat_pointer_state { struct wl_listener focus_resource_destroy_listener; }; +struct wlr_seat_keyboard_state { + struct wlr_seat *wlr_seat; + struct wlr_seat_handle *focused_handle; + struct wlr_surface *focused_surface; + + int keymap_fd; + size_t keymap_size; + + struct wl_listener focus_surface_destroy_listener; + struct wl_listener focus_resource_destroy_listener; +}; + struct wlr_seat { struct wl_global *wl_global; struct wl_display *display; @@ -40,6 +52,7 @@ struct wlr_seat { struct wlr_data_device *data_device; struct wlr_seat_pointer_state pointer_state; + struct wlr_seat_keyboard_state keyboard_state; struct { struct wl_signal client_bound; @@ -113,4 +126,38 @@ uint32_t wlr_seat_pointer_send_button(struct wlr_seat *wlr_seat, uint32_t time, void wlr_seat_pointer_send_axis(struct wlr_seat *wlr_seat, uint32_t time, enum wlr_axis_orientation orientation, double value); +/** + * Send a keyboard enter event to the given surface and consider it to be the + * focused surface for the keyboard. This will send a leave event to the last + * surface that was entered. Pass an array of currently pressed keys. + */ +void wlr_seat_keyboard_enter(struct wlr_seat *wlr_seat, + struct wlr_surface *surface, struct wl_array keys); + +/** + * Clear the focused surface for the keyboard and leave all entered surfaces. + */ +void wlr_seat_keyboard_clear_focus(struct wlr_seat *wlr_seat); + +/** + * Send a key event to the surface with keyboard focus. Returns the event + * serial. + */ +uint32_t wlr_seat_keyboard_send_key(struct wlr_seat *wlr_seat, uint32_t time, + uint32_t key, uint32_t state); + +/** + * Send the modifiers event to the surface with keyboard focus. Also sends the + * event to the surface with pointer focus. + */ +void wlr_seat_keyboard_send_modifiers(struct wlr_seat *wlr_seat, + uint32_t mods_depressed, uint32_t mods_latched, uint32_t mods_locked, + uint32_t group); + +/** + * Set the keymap and send it to seat keyboard resources. + */ +void wlr_seat_keyboard_set_keymap(struct wlr_seat *wlr_seat, int keymap_fd, + size_t keymap_size); + #endif diff --git a/types/wlr_seat.c b/types/wlr_seat.c index cbf1abc7..1d544902 100644 --- a/types/wlr_seat.c +++ b/types/wlr_seat.c @@ -70,7 +70,7 @@ static void wl_seat_get_keyboard(struct wl_client *client, } if (handle->keyboard) { // TODO: this is probably a protocol violation but it simplifies our - // code and it'd be stupid for clients to create several pointers for + // code and it'd be stupid for clients to create several keyboards for // the same seat wl_resource_destroy(handle->keyboard); } @@ -78,6 +78,20 @@ static void wl_seat_get_keyboard(struct wl_client *client, wl_resource_get_version(_handle), id); wl_resource_set_implementation(handle->keyboard, &wl_keyboard_impl, handle, &wl_keyboard_destroy); + + if (wl_resource_get_version(handle->keyboard) >= + WL_KEYBOARD_REPEAT_INFO_SINCE_VERSION) { + wl_keyboard_send_repeat_info(handle->keyboard, 25, 600); + } + + if (handle->wlr_seat->keyboard_state.keymap_size) { + // TODO: handle no keymap + wl_keyboard_send_keymap(handle->keyboard, + WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1, + handle->wlr_seat->keyboard_state.keymap_fd, + handle->wlr_seat->keyboard_state.keymap_size); + } + wl_signal_emit(&handle->wlr_seat->events.keyboard_bound, handle); } @@ -115,6 +129,9 @@ static void wlr_seat_handle_resource_destroy(struct wl_resource *resource) { if (handle == handle->wlr_seat->pointer_state.focused_handle) { handle->wlr_seat->pointer_state.focused_handle = NULL; } + if (handle == handle->wlr_seat->keyboard_state.focused_handle) { + handle->wlr_seat->keyboard_state.focused_handle = NULL; + } if (handle->pointer) { wl_resource_destroy(handle->pointer); @@ -168,6 +185,14 @@ struct wlr_seat *wlr_seat_create(struct wl_display *display, const char *name) { } wlr_seat->pointer_state.wlr_seat = wlr_seat; + wl_list_init(&wlr_seat->pointer_state.focus_resource_destroy_listener.link); + wl_list_init(&wlr_seat->pointer_state.focus_surface_destroy_listener.link); + + wlr_seat->keyboard_state.wlr_seat = wlr_seat; + wl_list_init( + &wlr_seat->keyboard_state.focus_resource_destroy_listener.link); + wl_list_init( + &wlr_seat->keyboard_state.focus_surface_destroy_listener.link); struct wl_global *wl_global = wl_global_create(display, &wl_seat_interface, 6, wlr_seat, wl_seat_bind); @@ -184,9 +209,6 @@ struct wlr_seat *wlr_seat_create(struct wl_display *display, const char *name) { wl_signal_init(&wlr_seat->events.client_unbound); wl_signal_init(&wlr_seat->events.keyboard_bound); - wl_list_init(&wlr_seat->pointer_state.focus_resource_destroy_listener.link); - wl_list_init(&wlr_seat->pointer_state.focus_surface_destroy_listener.link); - return wlr_seat; } @@ -372,3 +394,145 @@ void wlr_seat_pointer_send_axis(struct wlr_seat *wlr_seat, uint32_t time, wl_pointer_send_frame(pointer); } + +static void handle_keyboard_focus_surface_destroyed( + struct wl_listener *listener, void *data) { + struct wlr_seat_keyboard_state *state = + wl_container_of(listener, state, focus_surface_destroy_listener); + + state->focused_surface = NULL; + wlr_seat_keyboard_clear_focus(state->wlr_seat); +} + +static void handle_keyboard_focus_resource_destroyed( + struct wl_listener *listener, void *data) { + struct wlr_seat_keyboard_state *state = + wl_container_of(listener, state, focus_resource_destroy_listener); + + state->focused_surface = NULL; + wlr_seat_keyboard_clear_focus(state->wlr_seat); +} + +void wlr_seat_keyboard_enter(struct wlr_seat *wlr_seat, + struct wlr_surface *surface, struct wl_array keys) { + if (wlr_seat->keyboard_state.focused_surface == surface) { + // this surface already got an enter notify + return; + } + + struct wlr_seat_handle *handle = NULL; + + if (surface) { + struct wl_client *client = wl_resource_get_client(surface->resource); + handle = wlr_seat_handle_for_client(wlr_seat, client); + } + + struct wlr_seat_handle *focused_handle = + wlr_seat->keyboard_state.focused_handle; + struct wlr_surface *focused_surface = + wlr_seat->keyboard_state.focused_surface; + + // leave the previously entered surface + if (focused_handle && focused_handle->keyboard && focused_surface) { + uint32_t serial = wl_display_next_serial(wlr_seat->display); + wl_keyboard_send_leave(focused_handle->keyboard, serial, + focused_surface->resource); + } + + // enter the current surface + if (handle && handle->keyboard) { + uint32_t serial = wl_display_next_serial(wlr_seat->display); + wl_keyboard_send_enter(handle->keyboard, serial, + surface->resource, &keys); + + // TODO: send modifiers + } + + // reinitialize the focus destroy events + wl_list_remove( + &wlr_seat->keyboard_state.focus_surface_destroy_listener.link); + wl_list_init(&wlr_seat->keyboard_state.focus_surface_destroy_listener.link); + wl_list_remove( + &wlr_seat->keyboard_state.focus_resource_destroy_listener.link); + wl_list_init( + &wlr_seat->keyboard_state.focus_resource_destroy_listener.link); + if (surface) { + wl_signal_add(&surface->signals.destroy, + &wlr_seat->keyboard_state.focus_surface_destroy_listener); + wl_resource_add_destroy_listener(surface->resource, + &wlr_seat->keyboard_state.focus_resource_destroy_listener); + wlr_seat->keyboard_state.focus_resource_destroy_listener.notify = + handle_keyboard_focus_resource_destroyed; + wlr_seat->keyboard_state.focus_surface_destroy_listener.notify = + handle_keyboard_focus_surface_destroyed; + } + + wlr_seat->keyboard_state.focused_handle = handle; + wlr_seat->keyboard_state.focused_surface = surface; +} + +void wlr_seat_keyboard_clear_focus(struct wlr_seat *wlr_seat) { + struct wl_array keys; + wl_array_init(&keys); + wlr_seat_keyboard_enter(wlr_seat, NULL, keys); +} + +static bool wlr_seat_keyboard_has_focus_resource(struct wlr_seat *wlr_seat) { + return wlr_seat->keyboard_state.focused_handle && + wlr_seat->keyboard_state.focused_handle->keyboard; +} + +uint32_t wlr_seat_keyboard_send_key(struct wlr_seat *wlr_seat, uint32_t time, + uint32_t key, uint32_t state) { + if (!wlr_seat_keyboard_has_focus_resource(wlr_seat)) { + return 0; + } + + uint32_t serial = wl_display_next_serial(wlr_seat->display); + wl_keyboard_send_key(wlr_seat->keyboard_state.focused_handle->keyboard, + serial, time, key, state); + + return serial; +} + +void wlr_seat_keyboard_send_modifiers(struct wlr_seat *wlr_seat, + uint32_t mods_depressed, uint32_t mods_latched, uint32_t mods_locked, + uint32_t group) { + uint32_t serial = 0; + struct wl_resource *keyboard; + + if (wlr_seat_keyboard_has_focus_resource(wlr_seat)) { + serial = wl_display_next_serial(wlr_seat->display); + keyboard = wlr_seat->keyboard_state.focused_handle->keyboard; + wl_keyboard_send_modifiers(keyboard, serial, mods_depressed, + mods_latched, mods_locked, group); + } + + if (wlr_seat_pointer_has_focus_resource(wlr_seat) && + wlr_seat->pointer_state.focused_handle->keyboard && + wlr_seat->pointer_state.focused_handle != + wlr_seat->keyboard_state.focused_handle) { + if (serial == 0) { + serial = wl_display_next_serial(wlr_seat->display); + } + keyboard = wlr_seat->pointer_state.focused_handle->keyboard; + + wl_keyboard_send_modifiers(keyboard, serial, mods_depressed, + mods_latched, mods_locked, group); + } +} + +void wlr_seat_keyboard_set_keymap(struct wlr_seat *wlr_seat, int keymap_fd, + size_t keymap_size) { + // TODO: we probably should wait to send the keymap if keys are pressed + struct wlr_seat_handle *handle; + wl_list_for_each(handle, &wlr_seat->handles, link) { + if (handle->keyboard) { + wl_keyboard_send_keymap(handle->keyboard, + WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1, keymap_fd, keymap_size); + } + } + + wlr_seat->keyboard_state.keymap_fd = keymap_fd; + wlr_seat->keyboard_state.keymap_size = keymap_size; +}