1From 75c7ee95b6d561ee0030cfe5d7680b1fb48041b1 Mon Sep 17 00:00:00 2001
2From: Jeffy Chen <jeffy.chen@rock-chips.com>
3Date: Thu, 5 May 2022 17:56:46 +0800
4Subject: [PATCH 32/41] waylandsink: Support pointer and touch
5
6Based on weston's client window and simple-egl.
7
8Signed-off-by: Jeffy Chen <jeffy.chen@rock-chips.com>
9---
10 gst-libs/gst/wayland/gstwldisplay.c | 293 ++++++++++++++++++++++++++++
11 gst-libs/gst/wayland/gstwldisplay.h |   4 +
12 gst-libs/gst/wayland/gstwlwindow.c  |  17 ++
13 gst-libs/gst/wayland/gstwlwindow.h  |   4 +
14 gst-libs/gst/wayland/meson.build    |   7 +-
15 5 files changed, 322 insertions(+), 3 deletions(-)
16
17diff --git a/gst-libs/gst/wayland/gstwldisplay.c b/gst-libs/gst/wayland/gstwldisplay.c
18index 3e11211..b860a8c 100644
19--- a/gst-libs/gst/wayland/gstwldisplay.c
20+++ b/gst-libs/gst/wayland/gstwldisplay.c
21@@ -23,6 +23,7 @@
22 #endif
23
24 #include "gstwldisplay.h"
25+#include "gstwlwindow.h"
26
27 #include "fullscreen-shell-unstable-v1-client-protocol.h"
28 #include "linux-dmabuf-unstable-v1-client-protocol.h"
29@@ -30,6 +31,9 @@
30 #include "xdg-shell-client-protocol.h"
31
32 #include <errno.h>
33+#include <linux/input.h>
34+
35+#include <wayland-cursor.h>
36
37 #define GST_CAT_DEFAULT gst_wl_display_debug
38 GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
39@@ -41,6 +45,13 @@ typedef struct _GstWlDisplayPrivate
40   struct wl_display *display_wrapper;
41   struct wl_event_queue *queue;
42
43+  struct wl_list input_list;
44+
45+  struct wl_cursor_theme *cursor_theme;
46+  struct wl_cursor *default_cursor;
47+  struct wl_surface *cursor_surface;
48+  struct wl_surface *touch_surface;
49+
50   /* globals */
51   struct wl_registry *registry;
52   struct wl_compositor *compositor;
53@@ -72,6 +83,14 @@ G_DEFINE_TYPE_WITH_CODE (GstWlDisplay, gst_wl_display, G_TYPE_OBJECT,
54         "wldisplay", 0, "wldisplay library");
55     );
56
57+void
58+gst_wl_display_set_touch_surface (GstWlDisplay * self,
59+    struct wl_surface *touch_surface)
60+{
61+  GstWlDisplayPrivate *priv = gst_wl_display_get_instance_private (self);
62+  priv->touch_surface = touch_surface;
63+}
64+
65 gboolean
66 gst_wl_display_support_afbc (GstWlDisplay * self)
67 {
68@@ -88,6 +107,18 @@ gst_wl_display_support_nv12_10le40 (GstWlDisplay * self)
69
70 static void gst_wl_display_finalize (GObject * gobject);
71
72+struct input
73+{
74+  GstWlDisplay *display;
75+  struct wl_seat *seat;
76+  struct wl_pointer *pointer;
77+  struct wl_touch *touch;
78+
79+  void *pointer_focus;
80+
81+  struct wl_list link;
82+};
83+
84 static void
85 gst_wl_display_class_init (GstWlDisplayClass * klass)
86 {
87@@ -117,6 +148,30 @@ gst_wl_ref_wl_buffer (gpointer key, gpointer value, gpointer user_data)
88   g_object_ref (value);
89 }
90
91+static void
92+input_destroy (struct input *input)
93+{
94+  if (input->touch)
95+    wl_touch_destroy (input->touch);
96+  if (input->pointer)
97+    wl_pointer_destroy (input->pointer);
98+
99+  wl_list_remove (&input->link);
100+  wl_seat_destroy (input->seat);
101+  free (input);
102+}
103+
104+static void
105+display_destroy_inputs (GstWlDisplay * self)
106+{
107+  GstWlDisplayPrivate *priv = gst_wl_display_get_instance_private (self);
108+  struct input *tmp;
109+  struct input *input;
110+
111+  wl_list_for_each_safe (input, tmp, &priv->input_list, link)
112+      input_destroy (input);
113+}
114+
115 static void
116 gst_wl_display_finalize (GObject * gobject)
117 {
118@@ -127,6 +182,14 @@ gst_wl_display_finalize (GObject * gobject)
119   if (priv->thread)
120     g_thread_join (priv->thread);
121
122+  display_destroy_inputs (self);
123+
124+  if (priv->cursor_surface)
125+    wl_surface_destroy (priv->cursor_surface);
126+
127+  if (priv->cursor_theme)
128+    wl_cursor_theme_destroy (priv->cursor_theme);
129+
130   /* to avoid buffers being unregistered from another thread
131    * at the same time, take their ownership */
132   g_mutex_lock (&priv->buffers_mutex);
133@@ -284,6 +347,222 @@ static const struct xdg_wm_base_listener xdg_wm_base_listener = {
134   handle_xdg_wm_base_ping
135 };
136
137+static void
138+display_set_cursor (GstWlDisplay *self, struct wl_pointer *pointer,
139+    uint32_t serial)
140+{
141+  GstWlDisplayPrivate *priv = gst_wl_display_get_instance_private (self);
142+  struct wl_buffer *buffer;
143+  struct wl_cursor_image *image;
144+
145+  if (!priv->default_cursor)
146+    return;
147+
148+  if (!priv->cursor_surface) {
149+      priv->cursor_surface =
150+          wl_compositor_create_surface (priv->compositor);
151+      if (!priv->cursor_surface)
152+        return;
153+  }
154+
155+  image = priv->default_cursor->images[0];
156+  buffer = wl_cursor_image_get_buffer (image);
157+  if (!buffer)
158+    return;
159+
160+  wl_pointer_set_cursor (pointer, serial,
161+      priv->cursor_surface, image->hotspot_x, image->hotspot_y);
162+  wl_surface_attach (priv->cursor_surface, buffer, 0, 0);
163+  wl_surface_damage (priv->cursor_surface, 0, 0,
164+      image->width, image->height);
165+  wl_surface_commit (priv->cursor_surface);
166+}
167+
168+static void
169+pointer_handle_enter (void *data, struct wl_pointer *pointer,
170+    uint32_t serial, struct wl_surface *surface,
171+    wl_fixed_t sx_w, wl_fixed_t sy_w)
172+{
173+  struct input *input = data;
174+  GstWlDisplay *self = input->display;
175+  GstWlDisplayPrivate *priv = gst_wl_display_get_instance_private (self);
176+  GstWlWindow *window;
177+
178+  if (!surface) {
179+    /* enter event for a window we've just destroyed */
180+    return;
181+  }
182+
183+  if (surface != priv->touch_surface) {
184+    /* Ignoring input event from other surfaces */
185+    return;
186+  }
187+
188+  window = wl_surface_get_user_data (surface);
189+  if (!window || !gst_wl_window_is_toplevel (window)) {
190+    /* Ignoring input event from subsurface */
191+    return;
192+  }
193+
194+  input->pointer_focus = window;
195+  display_set_cursor (self, pointer, serial);
196+}
197+
198+static void
199+pointer_handle_leave (void *data, struct wl_pointer *pointer,
200+    uint32_t serial, struct wl_surface *surface)
201+{
202+  struct input *input = data;
203+
204+  if (input->pointer_focus) {
205+    input->pointer_focus = NULL;
206+    wl_pointer_set_cursor (pointer, serial, NULL, 0, 0);
207+  }
208+}
209+
210+static void
211+pointer_handle_motion (void *data, struct wl_pointer *pointer,
212+    uint32_t time, wl_fixed_t sx, wl_fixed_t sy)
213+{
214+}
215+
216+static void
217+pointer_handle_button (void *data, struct wl_pointer *pointer, uint32_t serial,
218+    uint32_t time, uint32_t button, uint32_t state)
219+{
220+  struct input *input = data;
221+  GstWlWindow *window;
222+
223+  window = input->pointer_focus;
224+  if (!window)
225+    return;
226+
227+  if (button == BTN_LEFT && state == WL_POINTER_BUTTON_STATE_PRESSED)
228+    gst_wl_window_toplevel_move (window, input->seat, serial);
229+}
230+
231+static void
232+pointer_handle_axis (void *data, struct wl_pointer *wl_pointer,
233+    uint32_t time, uint32_t axis, wl_fixed_t value)
234+{
235+}
236+
237+static const struct wl_pointer_listener pointer_listener = {
238+  pointer_handle_enter,
239+  pointer_handle_leave,
240+  pointer_handle_motion,
241+  pointer_handle_button,
242+  pointer_handle_axis,
243+};
244+
245+static void
246+touch_handle_down (void *data, struct wl_touch *wl_touch,
247+    uint32_t serial, uint32_t time, struct wl_surface *surface,
248+    int32_t id, wl_fixed_t x_w, wl_fixed_t y_w)
249+{
250+  struct input *input = data;
251+  GstWlDisplay *self = input->display;
252+  GstWlDisplayPrivate *priv = gst_wl_display_get_instance_private (self);
253+  GstWlWindow *window;
254+
255+  if (!surface) {
256+    /* enter event for a window we've just destroyed */
257+    return;
258+  }
259+
260+  if (surface != priv->touch_surface) {
261+    /* Ignoring input event from other surfaces */
262+    return;
263+  }
264+
265+  window = wl_surface_get_user_data (surface);
266+  if (!window || !gst_wl_window_is_toplevel (window)) {
267+    /* Ignoring input event from subsurface */
268+    return;
269+  }
270+
271+  gst_wl_window_toplevel_move (window, input->seat, serial);
272+}
273+
274+static void
275+touch_handle_up (void *data, struct wl_touch *wl_touch,
276+    uint32_t serial, uint32_t time, int32_t id)
277+{
278+}
279+
280+static void
281+touch_handle_motion (void *data, struct wl_touch *wl_touch,
282+    uint32_t time, int32_t id, wl_fixed_t x_w, wl_fixed_t y_w)
283+{
284+}
285+
286+static void
287+touch_handle_frame (void *data, struct wl_touch *wl_touch)
288+{
289+}
290+
291+static void
292+touch_handle_cancel (void *data, struct wl_touch *wl_touch)
293+{
294+}
295+
296+static const struct wl_touch_listener touch_listener = {
297+  touch_handle_down,
298+  touch_handle_up,
299+  touch_handle_motion,
300+  touch_handle_frame,
301+  touch_handle_cancel,
302+};
303+
304+static void
305+seat_handle_capabilities (void *data, struct wl_seat *seat,
306+    enum wl_seat_capability caps)
307+{
308+  struct input *input = data;
309+
310+  if ((caps & WL_SEAT_CAPABILITY_POINTER) && !input->pointer) {
311+    input->pointer = wl_seat_get_pointer (seat);
312+    wl_pointer_add_listener (input->pointer, &pointer_listener, input);
313+  } else if (!(caps & WL_SEAT_CAPABILITY_POINTER) && input->pointer) {
314+    wl_pointer_destroy (input->pointer);
315+    input->pointer = NULL;
316+  }
317+
318+  if ((caps & WL_SEAT_CAPABILITY_TOUCH) && !input->touch) {
319+    input->touch = wl_seat_get_touch (seat);
320+    wl_touch_add_listener (input->touch, &touch_listener, input);
321+  } else if (!(caps & WL_SEAT_CAPABILITY_TOUCH) && input->touch) {
322+    wl_touch_destroy (input->touch);
323+    input->touch = NULL;
324+  }
325+}
326+
327+static const struct wl_seat_listener seat_listener = {
328+  seat_handle_capabilities,
329+};
330+
331+static void
332+display_add_input (GstWlDisplay *self, uint32_t id)
333+{
334+  GstWlDisplayPrivate *priv = gst_wl_display_get_instance_private (self);
335+  struct input *input;
336+
337+  input = calloc (1, sizeof (*input));
338+  if (input == NULL) {
339+    GST_ERROR ("Error out of memory");
340+    return;
341+  }
342+
343+  input->display = self;
344+
345+  input->seat = wl_registry_bind (priv->registry, id, &wl_seat_interface, 1);
346+
347+  wl_seat_add_listener (input->seat, &seat_listener, input);
348+  wl_seat_set_user_data (input->seat, input);
349+
350+  wl_list_insert (priv->input_list.prev, &input->link);
351+}
352+
353 static void
354 registry_handle_global (void *data, struct wl_registry *registry,
355     uint32_t id, const char *interface, uint32_t version)
356@@ -307,6 +586,18 @@ registry_handle_global (void *data, struct wl_registry *registry,
357   } else if (g_strcmp0 (interface, "wl_shm") == 0) {
358     priv->shm = wl_registry_bind (registry, id, &wl_shm_interface, 1);
359     wl_shm_add_listener (priv->shm, &shm_listener, self);
360+
361+    priv->cursor_theme = wl_cursor_theme_load (NULL, 32, priv->shm);
362+    if (!priv->cursor_theme) {
363+      GST_ERROR ("Error loading default cursor theme");
364+    } else {
365+      priv->default_cursor =
366+          wl_cursor_theme_get_cursor (priv->cursor_theme, "left_ptr");
367+      if (!priv->default_cursor)
368+        GST_ERROR ("Error loading default left cursor pointer");
369+    }
370+  } else if (g_strcmp0 (interface, "wl_seat") == 0) {
371+    display_add_input (self, id);
372   } else if (g_strcmp0 (interface, "wp_viewporter") == 0) {
373     priv->viewporter =
374         wl_registry_bind (registry, id, &wp_viewporter_interface, 1);
375@@ -400,6 +691,8 @@ gst_wl_display_new_existing (struct wl_display * display,
376   priv->display_wrapper = wl_proxy_create_wrapper (display);
377   priv->own_display = take_ownership;
378
379+  wl_list_init (&priv->input_list);
380+
381   priv->queue = wl_display_create_queue (priv->display);
382   wl_proxy_set_queue ((struct wl_proxy *) priv->display_wrapper, priv->queue);
383   priv->registry = wl_display_get_registry (priv->display_wrapper);
384diff --git a/gst-libs/gst/wayland/gstwldisplay.h b/gst-libs/gst/wayland/gstwldisplay.h
385index c130b79..c6b4c8d 100644
386--- a/gst-libs/gst/wayland/gstwldisplay.h
387+++ b/gst-libs/gst/wayland/gstwldisplay.h
388@@ -35,6 +35,10 @@ struct _GstWlDisplay
389   GObject parent_instance;
390 };
391
392+GST_WL_API
393+void gst_wl_display_set_touch_surface (GstWlDisplay * self,
394+    struct wl_surface *touch_surface);
395+
396 GST_WL_API
397 gboolean gst_wl_display_support_afbc (GstWlDisplay * self);
398
399diff --git a/gst-libs/gst/wayland/gstwlwindow.c b/gst-libs/gst/wayland/gstwlwindow.c
400index df60a67..f55778d 100644
401--- a/gst-libs/gst/wayland/gstwlwindow.c
402+++ b/gst-libs/gst/wayland/gstwlwindow.c
403@@ -96,6 +96,19 @@ static void gst_wl_window_finalize (GObject * gobject);
404
405 static void gst_wl_window_update_borders (GstWlWindow * self);
406
407+void
408+gst_wl_window_toplevel_move (GstWlWindow * self,
409+    struct wl_seat *seat, uint32_t serial)
410+{
411+  GstWlWindowPrivate *priv;
412+
413+  if (!gst_wl_window_is_toplevel (self))
414+    return;
415+
416+  priv = gst_wl_window_get_instance_private (self);
417+  xdg_toplevel_move (priv->xdg_toplevel, seat, serial);
418+}
419+
420 static void
421 handle_xdg_toplevel_close (void *data, struct xdg_toplevel *xdg_toplevel)
422 {
423@@ -227,6 +240,8 @@ gst_wl_window_new_internal (GstWlDisplay * display, GMutex * render_lock)
424   priv->area_surface = wl_compositor_create_surface (compositor);
425   priv->video_surface = wl_compositor_create_surface (compositor);
426
427+  gst_wl_display_set_touch_surface (display, priv->area_surface);
428+
429   priv->area_surface_wrapper = wl_proxy_create_wrapper (priv->area_surface);
430   priv->video_surface_wrapper = wl_proxy_create_wrapper (priv->video_surface);
431
432@@ -368,6 +383,8 @@ gst_wl_window_new_toplevel (GstWlDisplay * display, const GstVideoInfo * info,
433   self = gst_wl_window_new_internal (display, render_lock);
434   priv = gst_wl_window_get_instance_private (self);
435
436+  wl_surface_set_user_data (priv->area_surface, self);
437+
438   xdg_wm_base = gst_wl_display_get_xdg_wm_base (display);
439   fullscreen_shell = gst_wl_display_get_fullscreen_shell_v1 (display);
440
441diff --git a/gst-libs/gst/wayland/gstwlwindow.h b/gst-libs/gst/wayland/gstwlwindow.h
442index 8f02f00..9425666 100644
443--- a/gst-libs/gst/wayland/gstwlwindow.h
444+++ b/gst-libs/gst/wayland/gstwlwindow.h
445@@ -47,6 +47,10 @@ struct _GstWlWindow
446   GObject parent_instance;
447 };
448
449+GST_WL_API
450+void gst_wl_window_toplevel_move (GstWlWindow * self,
451+        struct wl_seat *seat, uint32_t serial);
452+
453 GST_WL_API
454 void gst_wl_window_ensure_crop (GstWlWindow * self,
455         gint x, gint y, gint w, gint h);
456diff --git a/gst-libs/gst/wayland/meson.build b/gst-libs/gst/wayland/meson.build
457index 3aa63cb..9c958d7 100644
458--- a/gst-libs/gst/wayland/meson.build
459+++ b/gst-libs/gst/wayland/meson.build
460@@ -1,10 +1,11 @@
461 wl_req = '>= 1.15'
462 wl_client_dep = dependency('wayland-client', version: wl_req, required: get_option('wayland'))
463+wl_cursor_dep = dependency('wayland-cursor', version: wl_req, required: get_option('wayland'))
464 libdrm_dep = dependency('libdrm', version: '>= 2.4.55', required: get_option('wayland'))
465 wl_protocol_dep = dependency('wayland-protocols', version: wl_req, required: get_option('wayland'))
466 wl_scanner = find_program('wayland-scanner', required: get_option('wayland'))
467 # Also used in ext/wayland
468-use_wayland = wl_protocol_dep.found() and wl_client_dep.found() and wl_scanner.found() and libdrm_dep.found()
469+use_wayland = wl_protocol_dep.found() and wl_client_dep.found() and wl_cursor_dep.found() and wl_scanner.found() and libdrm_dep.found()
470
471 if use_wayland
472   wl_sources = [
473@@ -74,7 +75,7 @@ if use_wayland
474     darwin_versions : osxversion,
475     install : true,
476     dependencies : [gst_dep, gstallocators_dep, gstvideo_dep, libdrm_dep,
477-                    wl_client_dep, wl_protocol_dep]
478+                    wl_client_dep, wl_cursor_dep, wl_protocol_dep]
479   )
480
481   pkg_name = 'gstreamer-wayland-1.0'
482@@ -90,7 +91,7 @@ if use_wayland
483   gstwayland_dep = declare_dependency(link_with : gstwayland,
484     include_directories : [libsinc],
485     dependencies : [gst_dep, gstallocators_dep, gstvideo_dep, libdrm_dep,
486-                    wl_client_dep, wl_protocol_dep])
487+                    wl_client_dep, wl_cursor_dep, wl_protocol_dep])
488
489   install_headers(wl_headers, subdir: 'gstreamer-1.0/gst/wayland')
490   meson.override_dependency(pkg_name, gstwayland_dep)
491--
4922.20.1
493
494