1 /*
2 * GStreamer
3 * Copyright (C) 2010 Intel Corporation.
4 * Copyright (C) 2012 Matthew Waters <ystreet00@gmail.com>
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Library General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Library General Public License for more details.
15 *
16 * You should have received a copy of the GNU Library General Public
17 * License along with this library; if not, write to the
18 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
19 * Boston, MA 02110-1301, USA.
20 *
21 * Authors:
22 * Matthew Allum
23 * Robert Bragg
24 * Kristian Høgsberg
25 */
26
27 /* code originally from clutter's wayland backend found here
28 * http://git.gnome.org/browse/clutter/tree/clutter/wayland/clutter-event-wayland.c
29 */
30
31 #ifdef HAVE_CONFIG_H
32 #include "config.h"
33 #endif
34
35 #include <stdint.h>
36 #include <stdlib.h>
37 #include <wayland-client.h>
38 #include <gst/gst.h>
39
40 #include "wayland_event_source.h"
41
42 static void
sync_callback(void * data,struct wl_callback * callback,uint32_t serial)43 sync_callback (void *data, struct wl_callback *callback, uint32_t serial)
44 {
45 gboolean *done = data;
46
47 GST_TRACE ("roundtrip done. callback:%p", callback);
48
49 *done = TRUE;
50 wl_callback_destroy (callback);
51 }
52
53 static const struct wl_callback_listener sync_listener = {
54 sync_callback
55 };
56
57 /* only thread safe iff called on the same thread @queue is being dispatched on.
58 * Otherwise, two prepare_read{_queue}()'s can be indicated for the same
59 * queue and dispatch{_queue}() may be called for different threads which
60 * will cause deadlocks as no guarantees for thread-safety are given when
61 * pumping the same queue from multiple threads.
62 * As a concrete example, if the wayland event source (below) for a @queue is
63 * running on a certain thread, then this function must only be called in that
64 * thread (with that @queue). */
65 gint
gst_gl_wl_display_roundtrip_queue(struct wl_display * display,struct wl_event_queue * queue)66 gst_gl_wl_display_roundtrip_queue (struct wl_display *display,
67 struct wl_event_queue *queue)
68 {
69 struct wl_callback *callback;
70 gboolean done = FALSE;
71 gint ret = 0;
72
73 GST_TRACE ("roundtrip start for dpy %p and queue %p", display, queue);
74
75 if (queue) {
76 /* creating a wl_proxy and setting the queue is racy with the dispatching
77 * of the default queue */
78 while (wl_display_prepare_read_queue (display, queue) != 0) {
79 if ((ret = wl_display_dispatch_queue_pending (display, queue)) < 0) {
80 return ret;
81 }
82 }
83 }
84 if (!(callback = wl_display_sync (display))) {
85 return -1;
86 }
87 GST_TRACE ("create roundtrip callback %p", callback);
88 wl_callback_add_listener (callback, &sync_listener, &done);
89 if (queue) {
90 wl_proxy_set_queue ((struct wl_proxy *) callback, queue);
91 wl_display_cancel_read (display);
92 while (!done && ret >= 0) {
93 ret = wl_display_dispatch_queue (display, queue);
94 }
95 } else {
96 while (!done && ret >= 0) {
97 ret = wl_display_dispatch (display);
98 }
99 }
100
101 if (ret == -1 && !done)
102 wl_callback_destroy (callback);
103 GST_TRACE ("roundtrip done for dpy %p and queue %p. ret %i", display, queue,
104 ret);
105
106 return ret;
107 }
108
109 typedef struct _WaylandEventSource
110 {
111 GSource source;
112 GPollFD pfd;
113 uint32_t mask;
114 struct wl_display *display;
115 struct wl_event_queue *queue;
116 gboolean reading;
117 } WaylandEventSource;
118
119 static gboolean
wayland_event_source_prepare(GSource * base,gint * timeout)120 wayland_event_source_prepare (GSource * base, gint * timeout)
121 {
122 WaylandEventSource *source = (WaylandEventSource *) base;
123
124 *timeout = -1;
125
126 /* we may be called multiple times for prepare */
127 if (source->reading)
128 wl_display_cancel_read (source->display);
129
130 if (source->queue) {
131 if (wl_display_prepare_read_queue (source->display, source->queue) != 0)
132 return TRUE;
133 } else {
134 if (wl_display_prepare_read (source->display) != 0)
135 return TRUE;
136 }
137
138 source->reading = TRUE;
139
140 /* FIXME: this may return EAGAIN if the fd is full */
141 if (wl_display_flush (source->display) < 0)
142 g_critical ("Failed to flush Wayland connection\n");
143
144 return FALSE;
145 }
146
147 static gboolean
wayland_event_source_check(GSource * base)148 wayland_event_source_check (GSource * base)
149 {
150 WaylandEventSource *source = (WaylandEventSource *) base;
151
152 source->reading = FALSE;
153
154 if (source->pfd.revents & G_IO_IN) {
155 if (wl_display_read_events (source->display) == 0)
156 return TRUE;
157 } else {
158 wl_display_cancel_read (source->display);
159 }
160
161 return FALSE;
162 }
163
164 static gboolean
wayland_event_source_dispatch(GSource * base,GSourceFunc callback,gpointer data)165 wayland_event_source_dispatch (GSource * base,
166 GSourceFunc callback, gpointer data)
167 {
168 WaylandEventSource *source = (WaylandEventSource *) base;
169
170 if (source->queue) {
171 wl_display_dispatch_queue_pending (source->display, source->queue);
172 } else {
173 wl_display_dispatch_pending (source->display);
174 }
175 source->pfd.revents = 0;
176
177 if (callback)
178 callback (data);
179
180 return TRUE;
181 }
182
183 static void
wayland_event_source_finalize(GSource * base)184 wayland_event_source_finalize (GSource * base)
185 {
186 WaylandEventSource *source = (WaylandEventSource *) base;
187
188 if (source->reading) {
189 wl_display_cancel_read (source->display);
190 }
191 source->reading = FALSE;
192 }
193
194 static GSourceFuncs wayland_event_source_funcs = {
195 wayland_event_source_prepare,
196 wayland_event_source_check,
197 wayland_event_source_dispatch,
198 wayland_event_source_finalize
199 };
200
201 GSource *
wayland_event_source_new(struct wl_display * display,struct wl_event_queue * queue)202 wayland_event_source_new (struct wl_display *display,
203 struct wl_event_queue *queue)
204 {
205 WaylandEventSource *source;
206
207 source = (WaylandEventSource *)
208 g_source_new (&wayland_event_source_funcs, sizeof (WaylandEventSource));
209 source->display = display;
210 source->queue = queue;
211 source->pfd.fd = wl_display_get_fd (display);
212 source->pfd.events = G_IO_IN | G_IO_ERR;
213 g_source_add_poll (&source->source, &source->pfd);
214
215 return &source->source;
216 }
217