From aba56acd328d0843eaa69401e951f95fc6e56eaf Mon Sep 17 00:00:00 2001 From: Jeffy Chen Date: Thu, 5 May 2022 17:56:46 +0800 Subject: [PATCH 31/35] waylandsink: Support pointer and touch Based on weston's client window and simple-egl. Signed-off-by: Jeffy Chen --- ext/wayland/meson.build | 3 +- ext/wayland/wldisplay.c | 278 ++++++++++++++++++++++++++++++++++++++++ ext/wayland/wldisplay.h | 8 ++ ext/wayland/wlwindow.c | 4 + 4 files changed, 292 insertions(+), 1 deletion(-) diff --git a/ext/wayland/meson.build b/ext/wayland/meson.build index a3ffb70..c8a32da 100644 --- a/ext/wayland/meson.build +++ b/ext/wayland/meson.build @@ -37,12 +37,13 @@ if use_wayland command : [wl_scanner, 'client-header', '@INPUT@', '@OUTPUT@'])] endforeach + wl_cursor_dep = dependency('wayland-cursor') gstwaylandsink = library('gstwaylandsink', wl_sources + protocols_files, c_args : gst_plugins_bad_args + ['-DGST_USE_UNSTABLE_API'], include_directories : [configinc], dependencies : [gst_dep, gstvideo_dep, gstwayland_dep, gstallocators_dep, - wl_client_dep, wl_protocol_dep, libdrm_dep], + wl_client_dep, wl_protocol_dep, wl_cursor_dep, libdrm_dep], install : true, install_dir : plugins_install_dir, ) diff --git a/ext/wayland/wldisplay.c b/ext/wayland/wldisplay.c index fcf2853..701ee5e 100644 --- a/ext/wayland/wldisplay.c +++ b/ext/wayland/wldisplay.c @@ -24,9 +24,11 @@ #include "wldisplay.h" #include "wlbuffer.h" +#include "wlwindow.h" #include "wlvideoformat.h" #include +#include GST_DEBUG_CATEGORY_EXTERN (gstwayland_debug); #define GST_CAT_DEFAULT gstwayland_debug @@ -35,6 +37,18 @@ G_DEFINE_TYPE (GstWlDisplay, gst_wl_display, G_TYPE_OBJECT); static void gst_wl_display_finalize (GObject * gobject); +struct input +{ + GstWlDisplay *display; + struct wl_seat *seat; + struct wl_pointer *pointer; + struct wl_touch *touch; + + void *pointer_focus; + + struct wl_list link; +}; + static void gst_wl_display_class_init (GstWlDisplayClass * klass) { @@ -58,6 +72,29 @@ gst_wl_ref_wl_buffer (gpointer key, gpointer value, gpointer user_data) g_object_ref (value); } +static void +input_destroy (struct input *input) +{ + if (input->touch) + wl_touch_destroy (input->touch); + if (input->pointer) + wl_pointer_destroy (input->pointer); + + wl_list_remove (&input->link); + wl_seat_destroy (input->seat); + free (input); +} + +static void +display_destroy_inputs (GstWlDisplay * self) +{ + struct input *tmp; + struct input *input; + + wl_list_for_each_safe (input, tmp, &self->input_list, link) + input_destroy (input); +} + static void gst_wl_display_finalize (GObject * gobject) { @@ -67,6 +104,14 @@ gst_wl_display_finalize (GObject * gobject) if (self->thread) g_thread_join (self->thread); + display_destroy_inputs (self); + + if (self->cursor_surface) + wl_surface_destroy (self->cursor_surface); + + if (self->cursor_theme) + wl_cursor_theme_destroy (self->cursor_theme); + /* to avoid buffers being unregistered from another thread * at the same time, take their ownership */ g_mutex_lock (&self->buffers_mutex); @@ -223,6 +268,225 @@ static const struct xdg_wm_base_listener xdg_wm_base_listener = { handle_xdg_wm_base_ping }; +static void +display_set_cursor (GstWlDisplay *self, struct wl_pointer *pointer, + uint32_t serial) +{ + struct wl_buffer *buffer; + struct wl_cursor_image *image; + + if (!self->default_cursor) + return; + + if (!self->cursor_surface) { + self->cursor_surface = + wl_compositor_create_surface (self->compositor); + if (!self->cursor_surface) + return; + } + + image = self->default_cursor->images[0]; + buffer = wl_cursor_image_get_buffer (image); + if (!buffer) + return; + + wl_pointer_set_cursor (pointer, serial, + self->cursor_surface, image->hotspot_x, image->hotspot_y); + wl_surface_attach (self->cursor_surface, buffer, 0, 0); + wl_surface_damage (self->cursor_surface, 0, 0, + image->width, image->height); + wl_surface_commit (self->cursor_surface); +} + +static void +pointer_handle_enter (void *data, struct wl_pointer *pointer, + uint32_t serial, struct wl_surface *surface, + wl_fixed_t sx_w, wl_fixed_t sy_w) +{ + struct input *input = data; + GstWlDisplay *display = input->display; + GstWlWindow *window; + + if (!surface) { + /* enter event for a window we've just destroyed */ + return; + } + + if (surface != display->touch_surface) { + /* Ignoring input event from other surfaces */ + return; + } + + window = wl_surface_get_user_data (surface); + if (!window || !gst_wl_window_is_toplevel (window)) { + /* Ignoring input event from subsurface */ + return; + } + + input->pointer_focus = window; + display_set_cursor (window->display, pointer, serial); +} + +static void +pointer_handle_leave (void *data, struct wl_pointer *pointer, + uint32_t serial, struct wl_surface *surface) +{ + struct input *input = data; + + if (input->pointer_focus) { + input->pointer_focus = NULL; + wl_pointer_set_cursor (pointer, serial, NULL, 0, 0); + } +} + +static void +pointer_handle_motion (void *data, struct wl_pointer *pointer, + uint32_t time, wl_fixed_t sx, wl_fixed_t sy) +{ +} + +static void +pointer_handle_button (void *data, struct wl_pointer *pointer, uint32_t serial, + uint32_t time, uint32_t button, uint32_t state) +{ + struct input *input = data; + GstWlWindow *window; + + window = input->pointer_focus; + if (!window) + return; + + if (button == BTN_LEFT && state == WL_POINTER_BUTTON_STATE_PRESSED) { + if (window->display->xdg_wm_base) + xdg_toplevel_move (window->xdg_toplevel, input->seat, serial); + else + wl_shell_surface_move (window->wl_shell_surface, input->seat, serial); + } +} + +static void +pointer_handle_axis(void *data, struct wl_pointer *wl_pointer, + uint32_t time, uint32_t axis, wl_fixed_t value) +{ +} + +static const struct wl_pointer_listener pointer_listener = { + pointer_handle_enter, + pointer_handle_leave, + pointer_handle_motion, + pointer_handle_button, + pointer_handle_axis, +}; + +static void +touch_handle_down (void *data, struct wl_touch *wl_touch, + uint32_t serial, uint32_t time, struct wl_surface *surface, + int32_t id, wl_fixed_t x_w, wl_fixed_t y_w) +{ + struct input *input = data; + GstWlDisplay *display = input->display; + GstWlWindow *window; + + if (!surface) { + /* enter event for a window we've just destroyed */ + return; + } + + if (surface != display->touch_surface) { + /* Ignoring input event from other surfaces */ + return; + } + + window = wl_surface_get_user_data (surface); + if (!window || !gst_wl_window_is_toplevel (window)) { + /* Ignoring input event from subsurface */ + return; + } + + if (window->display->xdg_wm_base) + xdg_toplevel_move (window->xdg_toplevel, input->seat, serial); + else + wl_shell_surface_move (window->wl_shell_surface, input->seat, serial); +} + +static void +touch_handle_up (void *data, struct wl_touch *wl_touch, + uint32_t serial, uint32_t time, int32_t id) +{ +} + +static void +touch_handle_motion (void *data, struct wl_touch *wl_touch, + uint32_t time, int32_t id, wl_fixed_t x_w, wl_fixed_t y_w) +{ +} + +static void +touch_handle_frame (void *data, struct wl_touch *wl_touch) +{ +} + +static void +touch_handle_cancel (void *data, struct wl_touch *wl_touch) +{ +} + +static const struct wl_touch_listener touch_listener = { + touch_handle_down, + touch_handle_up, + touch_handle_motion, + touch_handle_frame, + touch_handle_cancel, +}; + +static void +seat_handle_capabilities (void *data, struct wl_seat *seat, + enum wl_seat_capability caps) +{ + struct input *input = data; + + if ((caps & WL_SEAT_CAPABILITY_POINTER) && !input->pointer) { + input->pointer = wl_seat_get_pointer (seat); + wl_pointer_add_listener (input->pointer, &pointer_listener, input); + } else if (!(caps & WL_SEAT_CAPABILITY_POINTER) && input->pointer) { + wl_pointer_destroy (input->pointer); + input->pointer = NULL; + } + + if ((caps & WL_SEAT_CAPABILITY_TOUCH) && !input->touch) { + input->touch = wl_seat_get_touch (seat); + wl_touch_add_listener (input->touch, &touch_listener, input); + } else if (!(caps & WL_SEAT_CAPABILITY_TOUCH) && input->touch) { + wl_touch_destroy (input->touch); + input->touch = NULL; + } +} + +static const struct wl_seat_listener seat_listener = { + seat_handle_capabilities, +}; + +static void +display_add_input (GstWlDisplay *self, uint32_t id) +{ + struct input *input; + + input = calloc (1, sizeof (*input)); + if (input == NULL) { + GST_ERROR ("Error out of memory"); + return; + } + + input->display = self; + + input->seat = wl_registry_bind (self->registry, id, &wl_seat_interface, 1); + + wl_seat_add_listener (input->seat, &seat_listener, input); + wl_seat_set_user_data (input->seat, input); + + wl_list_insert(self->input_list.prev, &input->link); +} + static void registry_handle_global (void *data, struct wl_registry *registry, uint32_t id, const char *interface, uint32_t version) @@ -247,6 +511,18 @@ registry_handle_global (void *data, struct wl_registry *registry, } else if (g_strcmp0 (interface, "wl_shm") == 0) { self->shm = wl_registry_bind (registry, id, &wl_shm_interface, 1); wl_shm_add_listener (self->shm, &shm_listener, self); + + self->cursor_theme = wl_cursor_theme_load (NULL, 32, self->shm); + if (!self->cursor_theme) { + GST_ERROR ("Error loading default cursor theme"); + } else { + self->default_cursor = + wl_cursor_theme_get_cursor (self->cursor_theme, "left_ptr"); + if (!self->default_cursor) + GST_ERROR ("Error loading default left cursor pointer"); + } + } else if (g_strcmp0 (interface, "wl_seat") == 0) { + display_add_input (self, id); } else if (g_strcmp0 (interface, "wp_viewporter") == 0) { self->viewporter = wl_registry_bind (registry, id, &wp_viewporter_interface, 1); @@ -337,6 +613,8 @@ gst_wl_display_new_existing (struct wl_display * display, self->display_wrapper = wl_proxy_create_wrapper (display); self->own_display = take_ownership; + wl_list_init (&self->input_list); + self->queue = wl_display_create_queue (self->display); wl_proxy_set_queue ((struct wl_proxy *) self->display_wrapper, self->queue); self->registry = wl_display_get_registry (self->display_wrapper); diff --git a/ext/wayland/wldisplay.h b/ext/wayland/wldisplay.h index 29c15f6..8914d31 100644 --- a/ext/wayland/wldisplay.h +++ b/ext/wayland/wldisplay.h @@ -24,6 +24,7 @@ #include #include #include +#include #include "xdg-shell-client-protocol.h" #include "viewporter-client-protocol.h" #include "linux-dmabuf-unstable-v1-client-protocol.h" @@ -50,6 +51,13 @@ struct _GstWlDisplay struct wl_display *display_wrapper; struct wl_event_queue *queue; + struct wl_list input_list; + + struct wl_cursor_theme *cursor_theme; + struct wl_cursor *default_cursor; + struct wl_surface *cursor_surface; + struct wl_surface *touch_surface; + /* globals */ struct wl_registry *registry; struct wl_compositor *compositor; diff --git a/ext/wayland/wlwindow.c b/ext/wayland/wlwindow.c index 2ba0eee..9917beb 100644 --- a/ext/wayland/wlwindow.c +++ b/ext/wayland/wlwindow.c @@ -205,6 +205,8 @@ gst_wl_window_new_internal (GstWlDisplay * display, GMutex * render_lock) window->area_surface = wl_compositor_create_surface (display->compositor); window->video_surface = wl_compositor_create_surface (display->compositor); + display->touch_surface = window->area_surface; + window->area_surface_wrapper = wl_proxy_create_wrapper (window->area_surface); window->video_surface_wrapper = wl_proxy_create_wrapper (window->video_surface); @@ -308,6 +310,8 @@ gst_wl_window_new_toplevel (GstWlDisplay * display, const GstVideoInfo * info, window = gst_wl_window_new_internal (display, render_lock); + wl_surface_set_user_data (window->area_surface, window); + /* Check which protocol we will use (in order of preference) */ if (display->xdg_wm_base) { gint64 timeout; -- 2.20.1