From 8b74450b398dbe9afa38fdad5e5b9335a2ac1d0b Mon Sep 17 00:00:00 2001 From: Tony Crisci Date: Wed, 20 Sep 2017 07:32:28 -0400 Subject: [PATCH] wlr_seat: pointer events --- examples/compositor.c | 82 ++++++++++++++++++++-- include/wlr/types/wlr_seat.h | 47 +++++++++++++ types/wlr_seat.c | 131 ++++++++++++++++++++++++++++++++++- 3 files changed, 254 insertions(+), 6 deletions(-) diff --git a/examples/compositor.c b/examples/compositor.c index 63041615..7595c729 100644 --- a/examples/compositor.c +++ b/examples/compositor.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include "wlr/types/wlr_compositor.h" @@ -27,6 +28,7 @@ #include #include "config.h" #include "shared.h" +#include // TODO: move to common header? int os_create_anonymous_file(off_t size); @@ -58,6 +60,8 @@ struct sample_state { struct wl_listener cursor_axis; struct wl_listener new_xdg_surface_v6; + + struct wlr_xdg_surface_v6 *focused_surface; }; struct example_xdg_surface_v6 { @@ -76,6 +80,25 @@ struct example_xdg_surface_v6 { struct wl_listener request_show_window_menu_listener; }; +static void example_set_focused_surface(struct sample_state *sample, + struct wlr_xdg_surface_v6 *surface) { + if (sample->focused_surface == surface) { + return; + } + + // set activated state of the xdg surfaces + struct wlr_xdg_surface_v6 *xdg_surface; + struct wlr_xdg_client_v6 *xdg_client; + wl_list_for_each(xdg_client, &sample->xdg_shell->clients, link) { + wl_list_for_each(xdg_surface, &xdg_client->surfaces, link) { + wlr_xdg_toplevel_v6_set_activated(xdg_surface, + xdg_surface == surface); + } + } + + sample->focused_surface = surface; +} + /* * Convert timespec to milliseconds */ @@ -307,12 +330,53 @@ static void handle_keyboard_bound(struct wl_listener *listener, void *data) { } } +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; + struct wlr_xdg_client_v6 *xdg_client; + wl_list_for_each(xdg_client, &sample->xdg_shell->clients, link) { + wl_list_for_each(xdg_surface, &xdg_client->surfaces, link) { + struct example_xdg_surface_v6 *esurface = xdg_surface->data; + + if (sample->cursor->x >= esurface->position.lx && + sample->cursor->y >= esurface->position.ly && + sample->cursor->x <= esurface->position.lx + + xdg_surface->geometry->width && + sample->cursor->y <= esurface->position.ly + + xdg_surface->geometry->height) { + return xdg_surface; + } + } + } + + return NULL; +} + static void handle_cursor_motion(struct wl_listener *listener, void *data) { struct sample_state *sample = wl_container_of(listener, sample, cursor_motion); struct wlr_event_pointer_motion *event = data; + wlr_cursor_move(sample->cursor, event->device, event->delta_x, event->delta_y); + + struct wlr_xdg_surface_v6 *surface = + example_xdg_surface_at(sample, sample->cursor->x, sample->cursor->y); + + if (surface) { + struct example_xdg_surface_v6 *esurface = surface->data; + + int32_t sx = sample->cursor->x - esurface->position.lx; + int32_t sy = sample->cursor->y - esurface->position.ly; + + // TODO z-order + wlr_seat_pointer_enter(sample->wl_seat, surface->surface, + sx, sy); + wlr_seat_pointer_send_motion(sample->wl_seat, event->time_sec, + sx, sy); + } else { + wlr_seat_pointer_clear_focus(sample->wl_seat); + } } static void handle_cursor_motion_absolute(struct wl_listener *listener, @@ -323,6 +387,8 @@ static void handle_cursor_motion_absolute(struct wl_listener *listener, wlr_cursor_warp_absolute(sample->cursor, event->device, event->x_mm, event->y_mm); + + // TODO move pointer } static void handle_cursor_axis(struct wl_listener *listener, void *data) { @@ -333,10 +399,17 @@ static void handle_cursor_axis(struct wl_listener *listener, void *data) { } static void handle_cursor_button(struct wl_listener *listener, void *data) { - //struct sample_state *sample = - //wl_container_of(listener, sample, cursor_button); - //struct wlr_event_pointer_button *event = data; - wlr_log(L_DEBUG, "TODO: handle cursor button"); + struct sample_state *sample = + wl_container_of(listener, sample, cursor_button); + struct wlr_event_pointer_button *event = data; + + struct wlr_xdg_surface_v6 *surface = + example_xdg_surface_at(sample, sample->cursor->x, sample->cursor->y); + + example_set_focused_surface(sample, surface); + + wlr_seat_pointer_send_button(sample->wl_seat, event->time_sec, + event->button, event->state); } static void handle_input_add(struct compositor_state *state, @@ -462,6 +535,7 @@ int main(int argc, char *argv[]) { wlr_gamma_control_manager_create(compositor.display); 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 diff --git a/include/wlr/types/wlr_seat.h b/include/wlr/types/wlr_seat.h index 8459e2bd..2993d693 100644 --- a/include/wlr/types/wlr_seat.h +++ b/include/wlr/types/wlr_seat.h @@ -1,5 +1,7 @@ #ifndef _WLR_TYPES_SEAT_H #define _WLR_TYPES_SEAT_H +#include +#include #include /** @@ -19,13 +21,25 @@ struct wlr_seat_handle { struct wl_list link; }; +struct wlr_seat_pointer_state { + struct wlr_seat *wlr_seat; + struct wlr_seat_handle *focused_handle; + struct wlr_surface *focused_surface; + + 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; struct wl_list handles; char *name; uint32_t capabilities; struct wlr_data_device *data_device; + struct wlr_seat_pointer_state pointer_state; + struct { struct wl_signal client_bound; struct wl_signal client_unbound; @@ -62,4 +76,37 @@ void wlr_seat_set_capabilities(struct wlr_seat *wlr_seat, */ void wlr_seat_set_name(struct wlr_seat *wlr_seat, const char *name); +/** + * Whether or not the surface has pointer focus + */ +bool wlr_seat_pointer_surface_has_focus(struct wlr_seat *wlr_seat, + struct wlr_surface *surface); + +/** + * Send a pointer enter event to the given surface and consider it to be the + * focused surface for the pointer. This will send a leave event to the last + * surface that was entered. Coordinates for the enter event are surface-local. + */ +void wlr_seat_pointer_enter(struct wlr_seat *wlr_seat, + struct wlr_surface *surface, int32_t sx, int32_t sy); + +/** + * Clear the focused surface for the pointer and leave all entered surfaces. + */ +void wlr_seat_pointer_clear_focus(struct wlr_seat *wlr_seat); + +/** + * Send a motion event to the surface with pointer focus. Coordinates for the + * motion event are surface-local. + */ +void wlr_seat_pointer_send_motion(struct wlr_seat *wlr_seat, uint32_t time, + int32_t sx, int32_t sy); + +/** + * Send a button event to the surface with pointer focus. Coordinates for the + * button event are surface-local. + */ +void wlr_seat_pointer_send_button(struct wlr_seat *wlr_seat, uint32_t time, + uint32_t button, enum wlr_button_state state); + #endif diff --git a/types/wlr_seat.c b/types/wlr_seat.c index 2ca287ac..2a0a1dcd 100644 --- a/types/wlr_seat.c +++ b/types/wlr_seat.c @@ -4,6 +4,7 @@ #include #include #include +#include #include static void resource_destroy(struct wl_client *client, @@ -109,8 +110,12 @@ static void wl_seat_get_touch(struct wl_client *client, handle, &wl_touch_destroy); } -static void wl_seat_destroy(struct wl_resource *resource) { +static void wlr_seat_handle_resource_destroy(struct wl_resource *resource) { struct wlr_seat_handle *handle = wl_resource_get_user_data(resource); + if (handle == handle->wlr_seat->pointer_state.focused_handle) { + handle->wlr_seat->pointer_state.focused_handle = NULL; + } + if (handle->pointer) { wl_resource_destroy(handle->pointer); } @@ -150,7 +155,7 @@ static void wl_seat_bind(struct wl_client *wl_client, void *_wlr_seat, wl_client, &wl_seat_interface, version, id); handle->wlr_seat = wlr_seat; wl_resource_set_implementation(handle->wl_resource, &wl_seat_impl, - handle, wl_seat_destroy); + handle, wlr_seat_handle_resource_destroy); wl_list_insert(&wlr_seat->handles, &handle->link); wl_seat_send_capabilities(handle->wl_resource, wlr_seat->capabilities); wl_signal_emit(&wlr_seat->events.client_bound, handle); @@ -161,6 +166,9 @@ struct wlr_seat *wlr_seat_create(struct wl_display *display, const char *name) { if (!wlr_seat) { return NULL; } + + wlr_seat->pointer_state.wlr_seat = wlr_seat; + struct wl_global *wl_global = wl_global_create(display, &wl_seat_interface, 6, wlr_seat, wl_seat_bind); if (!wl_global) { @@ -168,11 +176,17 @@ struct wlr_seat *wlr_seat_create(struct wl_display *display, const char *name) { return NULL; } wlr_seat->wl_global = wl_global; + wlr_seat->display = display; wlr_seat->name = strdup(name); wl_list_init(&wlr_seat->handles); + wl_signal_init(&wlr_seat->events.client_bound); 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; } @@ -222,3 +236,116 @@ void wlr_seat_set_name(struct wlr_seat *wlr_seat, const char *name) { wl_seat_send_name(handle->wl_resource, name); } } + +bool wlr_seat_pointer_surface_has_focus(struct wlr_seat *wlr_seat, + struct wlr_surface *surface) { + return surface == wlr_seat->pointer_state.focused_surface; +} + +static void handle_pointer_focus_surface_destroyed( + struct wl_listener *listener, void *data) { + struct wlr_seat_pointer_state *state = + wl_container_of(listener, state, focus_surface_destroy_listener); + + state->focused_surface = NULL; + wlr_seat_pointer_clear_focus(state->wlr_seat); +} + +static void handle_pointer_focus_resource_destroyed( + struct wl_listener *listener, void *data) { + struct wlr_seat_pointer_state *state = + wl_container_of(listener, state, focus_resource_destroy_listener); + + state->focused_surface = NULL; + wlr_seat_pointer_clear_focus(state->wlr_seat); +} + +void wlr_seat_pointer_enter(struct wlr_seat *wlr_seat, + struct wlr_surface *surface, int32_t sx, int32_t sy) { + assert(wlr_seat); + + if (wlr_seat->pointer_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->pointer_state.focused_handle; + struct wlr_surface *focused_surface = + wlr_seat->pointer_state.focused_surface; + + // leave the previously entered surface + if (focused_handle && focused_surface) { + uint32_t serial = wl_display_next_serial(wlr_seat->display); + wl_pointer_send_leave(focused_handle->pointer, serial, + focused_surface->resource); + wl_pointer_send_frame(focused_handle->pointer); + } + + // enter the current surface + if (handle) { + uint32_t serial = wl_display_next_serial(wlr_seat->display); + wl_pointer_send_enter(handle->pointer, serial, surface->resource, + wl_fixed_from_int(sx), wl_fixed_from_int(sy)); + wl_pointer_send_frame(handle->pointer); + } + + // reinitialize the focus destroy events + wl_list_remove( + &wlr_seat->pointer_state.focus_surface_destroy_listener.link); + wl_list_init(&wlr_seat->pointer_state.focus_surface_destroy_listener.link); + wl_list_remove( + &wlr_seat->pointer_state.focus_resource_destroy_listener.link); + wl_list_init(&wlr_seat->pointer_state.focus_resource_destroy_listener.link); + if (surface) { + wl_signal_add(&surface->signals.destroy, + &wlr_seat->pointer_state.focus_surface_destroy_listener); + wl_resource_add_destroy_listener(surface->resource, + &wlr_seat->pointer_state.focus_resource_destroy_listener); + wlr_seat->pointer_state.focus_resource_destroy_listener.notify = + handle_pointer_focus_resource_destroyed; + wlr_seat->pointer_state.focus_surface_destroy_listener.notify = + handle_pointer_focus_surface_destroyed; + } + + wlr_seat->pointer_state.focused_handle = handle; + wlr_seat->pointer_state.focused_surface = surface; + + // TODO: send focus change event +} + +void wlr_seat_pointer_clear_focus(struct wlr_seat *wlr_seat) { + wlr_seat_pointer_enter(wlr_seat, NULL, 0, 0); +} + +void wlr_seat_pointer_send_motion(struct wlr_seat *wlr_seat, uint32_t time, + int32_t sx, int32_t sy) { + if (!wlr_seat->pointer_state.focused_handle) { + // nobody to send the event to + return; + } + + wl_pointer_send_motion(wlr_seat->pointer_state.focused_handle->pointer, + time, wl_fixed_from_int(sx), wl_fixed_from_int(sy)); + wl_pointer_send_frame(wlr_seat->pointer_state.focused_handle->pointer); +} + +void wlr_seat_pointer_send_button(struct wlr_seat *wlr_seat, uint32_t time, + uint32_t button, enum wlr_button_state state) { + if (!wlr_seat->pointer_state.focused_handle) { + // nobody to send the event to + return; + } + + uint32_t serial = wl_display_next_serial(wlr_seat->display); + wl_pointer_send_button(wlr_seat->pointer_state.focused_handle->pointer, + serial, time, button, state); + wl_pointer_send_frame(wlr_seat->pointer_state.focused_handle->pointer); +}