1 /*
2 * GStreamer
3 * Copyright (C) 2012 Matthew Waters <ystreet00@gmail.com>
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Library General Public License for more details.
14 *
15 * You should have received a copy of the GNU Library General Public
16 * License along with this library; if not, write to the
17 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
18 * Boston, MA 02110-1301, USA.
19 */
20
21 /**
22 * SECTION:gstglwindow
23 * @short_description: window/surface abstraction
24 * @title: GstGLWindow
25 * @see_also: #GstGLContext, #GstGLDisplay
26 *
27 * GstGLWindow represents a window that elements can render into. A window can
28 * either be a user visible window (onscreen) or hidden (offscreen).
29 */
30
31 #ifdef HAVE_CONFIG_H
32 # include "config.h"
33 #endif
34
35 #include <gmodule.h>
36 #include <stdio.h>
37
38 #include "gl.h"
39 #include "gstglwindow.h"
40 #include "gstglwindow_private.h"
41
42 /* FIXME make this work with windowless contexts */
43
44 #if GST_GL_HAVE_WINDOW_X11
45 #include "x11/gstglwindow_x11.h"
46 #endif
47 #if GST_GL_HAVE_WINDOW_WIN32
48 #include "win32/gstglwindow_win32.h"
49 #endif
50 #if GST_GL_HAVE_WINDOW_COCOA
51 #include "cocoa/gstglwindow_cocoa.h"
52 #endif
53 #if GST_GL_HAVE_WINDOW_WAYLAND
54 #include "wayland/gstglwindow_wayland_egl.h"
55 #endif
56 #if GST_GL_HAVE_WINDOW_ANDROID
57 #include "android/gstglwindow_android_egl.h"
58 #endif
59 #if GST_GL_HAVE_WINDOW_EAGL
60 #include "eagl/gstglwindow_eagl.h"
61 #endif
62 #if GST_GL_HAVE_WINDOW_VIV_FB
63 #include "viv-fb/gstglwindow_viv_fb_egl.h"
64 #endif
65 #if GST_GL_HAVE_WINDOW_GBM
66 #include "gbm/gstglwindow_gbm_egl.h"
67 #endif
68 #if GST_GL_HAVE_WINDOW_DISPMANX
69 #include "dispmanx/gstglwindow_dispmanx_egl.h"
70 #endif
71
72 #define USING_OPENGL(context) (gst_gl_context_check_gl_version (context, GST_GL_API_OPENGL, 1, 0))
73 #define USING_OPENGL3(context) (gst_gl_context_check_gl_version (context, GST_GL_API_OPENGL3, 3, 1))
74 #define USING_GLES(context) (gst_gl_context_check_gl_version (context, GST_GL_API_GLES, 1, 0))
75 #define USING_GLES2(context) (gst_gl_context_check_gl_version (context, GST_GL_API_GLES2, 2, 0))
76 #define USING_GLES3(context) (gst_gl_context_check_gl_version (context, GST_GL_API_GLES2, 3, 0))
77
78 #define GST_CAT_DEFAULT gst_gl_window_debug
79 GST_DEBUG_CATEGORY (GST_CAT_DEFAULT);
80
81 static void gst_gl_window_default_draw (GstGLWindow * window);
82 static void gst_gl_window_default_run (GstGLWindow * window);
83 static void gst_gl_window_default_quit (GstGLWindow * window);
84 static void gst_gl_window_default_send_message (GstGLWindow * window,
85 GstGLWindowCB callback, gpointer data);
86 static void gst_gl_window_default_send_message_async (GstGLWindow * window,
87 GstGLWindowCB callback, gpointer data, GDestroyNotify destroy);
88
89 struct _GstGLWindowPrivate
90 {
91 GMainLoop *loop;
92
93 guint surface_width;
94 guint surface_height;
95
96 gboolean alive;
97
98 GMutex sync_message_lock;
99 GCond sync_message_cond;
100 };
101
102 #define gst_gl_window_parent_class parent_class
103 G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (GstGLWindow, gst_gl_window,
104 GST_TYPE_OBJECT);
105
106 static void gst_gl_window_finalize (GObject * object);
107
108 typedef struct _GstGLDummyWindow
109 {
110 GstGLWindow parent;
111
112 guintptr handle;
113 } GstGLDummyWindow;
114
115 typedef struct _GstGLDummyWindowCass
116 {
117 GstGLWindowClass parent;
118 } GstGLDummyWindowClass;
119
120 static GstGLDummyWindow *gst_gl_dummy_window_new (void);
121
122 enum
123 {
124 SIGNAL_0,
125 EVENT_MOUSE_SIGNAL,
126 EVENT_KEY_SIGNAL,
127 LAST_SIGNAL
128 };
129
130 static guint gst_gl_window_signals[LAST_SIGNAL] = { 0 };
131
132 GQuark
gst_gl_window_error_quark(void)133 gst_gl_window_error_quark (void)
134 {
135 return g_quark_from_static_string ("gst-gl-window-error-quark");
136 }
137
138 static gboolean
gst_gl_window_default_open(GstGLWindow * window,GError ** error)139 gst_gl_window_default_open (GstGLWindow * window, GError ** error)
140 {
141 return TRUE;
142 }
143
144 static void
gst_gl_window_default_close(GstGLWindow * window)145 gst_gl_window_default_close (GstGLWindow * window)
146 {
147 }
148
149 static void
_init_debug(void)150 _init_debug (void)
151 {
152 static volatile gsize _init = 0;
153
154 if (g_once_init_enter (&_init)) {
155 GST_DEBUG_CATEGORY_INIT (gst_gl_window_debug, "glwindow", 0,
156 "glwindow element");
157 g_once_init_leave (&_init, 1);
158 }
159 }
160
161 static void
gst_gl_window_init(GstGLWindow * window)162 gst_gl_window_init (GstGLWindow * window)
163 {
164 GstGLWindowPrivate *priv = gst_gl_window_get_instance_private (window);
165 window->priv = priv;
166
167 g_mutex_init (&window->lock);
168 window->is_drawing = FALSE;
169
170 g_weak_ref_init (&window->context_ref, NULL);
171
172 g_mutex_init (&window->priv->sync_message_lock);
173 g_cond_init (&window->priv->sync_message_cond);
174
175 window->main_context = g_main_context_new ();
176 priv->loop = g_main_loop_new (window->main_context, FALSE);
177 }
178
179 static void
gst_gl_window_class_init(GstGLWindowClass * klass)180 gst_gl_window_class_init (GstGLWindowClass * klass)
181 {
182 klass->open = GST_DEBUG_FUNCPTR (gst_gl_window_default_open);
183 klass->close = GST_DEBUG_FUNCPTR (gst_gl_window_default_close);
184 klass->run = GST_DEBUG_FUNCPTR (gst_gl_window_default_run);
185 klass->quit = GST_DEBUG_FUNCPTR (gst_gl_window_default_quit);
186 klass->draw = GST_DEBUG_FUNCPTR (gst_gl_window_default_draw);
187 klass->send_message = GST_DEBUG_FUNCPTR (gst_gl_window_default_send_message);
188 klass->send_message_async =
189 GST_DEBUG_FUNCPTR (gst_gl_window_default_send_message_async);
190
191 G_OBJECT_CLASS (klass)->finalize = gst_gl_window_finalize;
192
193 /**
194 * GstGLWindow::mouse-event:
195 * @object: the #GstGLWindow
196 * @id: the name of the event
197 * @button: the id of the button
198 * @x: the x coordinate of the mouse event
199 * @y: the y coordinate of the mouse event
200 *
201 * Will be emitted when a mouse event is received by the GstGLwindow.
202 *
203 * Since: 1.6
204 */
205 gst_gl_window_signals[EVENT_MOUSE_SIGNAL] =
206 g_signal_new ("mouse-event", G_TYPE_FROM_CLASS (klass),
207 G_SIGNAL_RUN_LAST, 0, NULL, NULL, g_cclosure_marshal_generic,
208 G_TYPE_NONE, 4, G_TYPE_STRING, G_TYPE_INT, G_TYPE_DOUBLE, G_TYPE_DOUBLE);
209
210 /**
211 * GstGLWindow::key-event:
212 * @object: the #GstGLWindow
213 * @id: the name of the event
214 * @key: the id of the key pressed
215 *
216 * Will be emitted when a key event is received by the GstGLwindow.
217 *
218 * Since: 1.6
219 */
220 gst_gl_window_signals[EVENT_KEY_SIGNAL] =
221 g_signal_new ("key-event", G_TYPE_FROM_CLASS (klass),
222 G_SIGNAL_RUN_LAST, 0, NULL, NULL, g_cclosure_marshal_generic,
223 G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_STRING);
224
225 _init_debug ();
226 }
227
228 /**
229 * gst_gl_window_new:
230 * @display: a #GstGLDisplay
231 *
232 * Returns: (transfer full): a new #GstGLWindow using @display's connection
233 *
234 * Since: 1.4
235 */
236 GstGLWindow *
gst_gl_window_new(GstGLDisplay * display)237 gst_gl_window_new (GstGLDisplay * display)
238 {
239 GstGLWindow *window = NULL;
240 const gchar *user_choice;
241
242 g_return_val_if_fail (display != NULL, NULL);
243
244 _init_debug ();
245
246 user_choice = g_getenv ("GST_GL_WINDOW");
247 GST_INFO ("creating a window, user choice:%s", user_choice);
248
249 #if GST_GL_HAVE_WINDOW_COCOA
250 if (!window && (!user_choice || g_strstr_len (user_choice, 5, "cocoa")))
251 window = GST_GL_WINDOW (gst_gl_window_cocoa_new (display));
252 #endif
253 #if GST_GL_HAVE_WINDOW_X11
254 if (!window && (!user_choice || g_strstr_len (user_choice, 3, "x11")))
255 window = GST_GL_WINDOW (gst_gl_window_x11_new (display));
256 #endif
257 #if GST_GL_HAVE_WINDOW_WIN32
258 if (!window && (!user_choice || g_strstr_len (user_choice, 5, "win32")))
259 window = GST_GL_WINDOW (gst_gl_window_win32_new (display));
260 #endif
261 #if GST_GL_HAVE_WINDOW_WAYLAND
262 if (!window && (!user_choice || g_strstr_len (user_choice, 7, "wayland")))
263 window = GST_GL_WINDOW (gst_gl_window_wayland_egl_new (display));
264 #endif
265 #if GST_GL_HAVE_WINDOW_DISPMANX
266 if (!window && (!user_choice || g_strstr_len (user_choice, 8, "dispmanx")))
267 window = GST_GL_WINDOW (gst_gl_window_dispmanx_egl_new (display));
268 #endif
269 #if GST_GL_HAVE_WINDOW_ANDROID
270 if (!window && (!user_choice || g_strstr_len (user_choice, 7, "android")))
271 window = GST_GL_WINDOW (gst_gl_window_android_egl_new (display));
272 #endif
273 #if GST_GL_HAVE_WINDOW_EAGL
274 if (!window && (!user_choice || g_strstr_len (user_choice, 4, "eagl")))
275 window = GST_GL_WINDOW (gst_gl_window_eagl_new (display));
276 #endif
277 #if GST_GL_HAVE_WINDOW_VIV_FB
278 if (!window && (!user_choice || g_strstr_len (user_choice, 6, "viv-fb")))
279 window = GST_GL_WINDOW (gst_gl_window_viv_fb_egl_new (display));
280 #endif
281 #if GST_GL_HAVE_WINDOW_GBM
282 if (!window && (!user_choice || g_strstr_len (user_choice, 3, "gbm")))
283 window = GST_GL_WINDOW (gst_gl_window_gbm_egl_new (display));
284 #endif
285
286 if (!window) {
287 /* subclass returned a NULL window */
288 GST_WARNING ("Could not create window. user specified %s, creating dummy"
289 " window", user_choice ? user_choice : "(null)");
290
291 window = GST_GL_WINDOW (gst_gl_dummy_window_new ());
292 }
293
294 window->display = gst_object_ref (display);
295
296 return window;
297 }
298
299 static void
gst_gl_window_finalize(GObject * object)300 gst_gl_window_finalize (GObject * object)
301 {
302 GstGLWindow *window = GST_GL_WINDOW (object);
303 GstGLWindowPrivate *priv = window->priv;
304
305 if (priv->loop)
306 g_main_loop_unref (priv->loop);
307
308 if (window->main_context)
309 g_main_context_unref (window->main_context);
310 window->main_context = NULL;
311
312 g_weak_ref_clear (&window->context_ref);
313
314 g_mutex_clear (&window->lock);
315 g_mutex_clear (&window->priv->sync_message_lock);
316 g_cond_clear (&window->priv->sync_message_cond);
317 gst_object_unref (window->display);
318
319 G_OBJECT_CLASS (gst_gl_window_parent_class)->finalize (object);
320 }
321
322 typedef struct _GstSetWindowHandleCb
323 {
324 GstGLWindow *window;
325 guintptr handle;
326 } GstSetWindowHandleCb;
327
328 static void
_set_window_handle_cb(GstSetWindowHandleCb * data)329 _set_window_handle_cb (GstSetWindowHandleCb * data)
330 {
331 GstGLContext *context = gst_gl_window_get_context (data->window);
332 GstGLWindowClass *window_class = GST_GL_WINDOW_GET_CLASS (data->window);
333 GThread *thread = NULL;
334
335 /* deactivate if necessary */
336 if (context) {
337 thread = gst_gl_context_get_thread (context);
338 if (thread) {
339 /* This is only thread safe iff the context thread == g_thread_self() */
340 g_assert (thread == g_thread_self ());
341 gst_gl_context_activate (context, FALSE);
342 }
343 }
344
345 window_class->set_window_handle (data->window, data->handle);
346
347 /* reactivate */
348 if (context && thread)
349 gst_gl_context_activate (context, TRUE);
350
351 if (context)
352 gst_object_unref (context);
353 if (thread)
354 g_thread_unref (thread);
355 }
356
357 static void
_free_swh_cb(GstSetWindowHandleCb * data)358 _free_swh_cb (GstSetWindowHandleCb * data)
359 {
360 gst_object_unref (data->window);
361 g_slice_free (GstSetWindowHandleCb, data);
362 }
363
364 /**
365 * gst_gl_window_set_window_handle:
366 * @window: a #GstGLWindow
367 * @handle: handle to the window
368 *
369 * Sets the window that this @window should render into. Some implementations
370 * require this to be called with a valid handle before drawing can commence.
371 *
372 * Since: 1.4
373 */
374 void
gst_gl_window_set_window_handle(GstGLWindow * window,guintptr handle)375 gst_gl_window_set_window_handle (GstGLWindow * window, guintptr handle)
376 {
377 GstGLWindowClass *window_class;
378 GstSetWindowHandleCb *data;
379
380 g_return_if_fail (GST_IS_GL_WINDOW (window));
381 g_return_if_fail (handle != 0);
382 window_class = GST_GL_WINDOW_GET_CLASS (window);
383 g_return_if_fail (window_class->set_window_handle != NULL);
384
385 data = g_slice_new (GstSetWindowHandleCb);
386 data->window = gst_object_ref (window);
387 data->handle = handle;
388
389 /* FIXME: Move to a message which deactivates, calls implementation, activates */
390 gst_gl_window_send_message_async (window,
391 (GstGLWindowCB) _set_window_handle_cb, data,
392 (GDestroyNotify) _free_swh_cb);
393
394 /* window_class->set_window_handle (window, handle); */
395 }
396
397 static void
draw_cb(gpointer data)398 draw_cb (gpointer data)
399 {
400 GstGLWindow *window = GST_GL_WINDOW (data);
401 GstGLContext *context = gst_gl_window_get_context (window);
402
403 if (window->queue_resize) {
404 guint width, height;
405
406 gst_gl_window_get_surface_dimensions (window, &width, &height);
407 gst_gl_window_resize (window, width, height);
408 }
409
410 if (window->draw)
411 window->draw (window->draw_data);
412
413 gst_gl_context_swap_buffers (context);
414
415 gst_object_unref (context);
416 }
417
418 static void
gst_gl_window_default_draw(GstGLWindow * window)419 gst_gl_window_default_draw (GstGLWindow * window)
420 {
421 gst_gl_window_send_message (window, (GstGLWindowCB) draw_cb, window);
422 }
423
424 /**
425 * gst_gl_window_draw:
426 * @window: a #GstGLWindow
427 *
428 * Redraw the window contents. Implementations should invoke the draw callback.
429 *
430 * Since: 1.4
431 */
432 void
gst_gl_window_draw(GstGLWindow * window)433 gst_gl_window_draw (GstGLWindow * window)
434 {
435 GstGLWindowClass *window_class;
436
437 g_return_if_fail (GST_IS_GL_WINDOW (window));
438 window_class = GST_GL_WINDOW_GET_CLASS (window);
439 g_return_if_fail (window_class->draw != NULL);
440
441 /* avoid to overload the drawer */
442 if (window->is_drawing) {
443 return;
444 }
445
446 window_class->draw (window);
447 }
448
449 /**
450 * gst_gl_window_set_preferred_size:
451 * @window: a #GstGLWindow
452 * @width: new preferred width
453 * @height: new preferred height
454 *
455 * Set the preferred width and height of the window. Implementations are free
456 * to ignore this information.
457 *
458 * Since: 1.6
459 */
460 void
gst_gl_window_set_preferred_size(GstGLWindow * window,gint width,gint height)461 gst_gl_window_set_preferred_size (GstGLWindow * window, gint width, gint height)
462 {
463 GstGLWindowClass *window_class;
464
465 g_return_if_fail (GST_IS_GL_WINDOW (window));
466 window_class = GST_GL_WINDOW_GET_CLASS (window);
467
468 if (window_class->set_preferred_size)
469 window_class->set_preferred_size (window, width, height);
470 }
471
472 /**
473 * gst_gl_window_show:
474 * @window: a #GstGLWindow
475 *
476 * Present the window to the screen.
477 *
478 * Since: 1.6
479 */
480 void
gst_gl_window_show(GstGLWindow * window)481 gst_gl_window_show (GstGLWindow * window)
482 {
483 GstGLWindowClass *window_class;
484
485 g_return_if_fail (GST_IS_GL_WINDOW (window));
486 window_class = GST_GL_WINDOW_GET_CLASS (window);
487
488 if (window_class->show)
489 window_class->show (window);
490 }
491
492 static void
gst_gl_window_default_run(GstGLWindow * window)493 gst_gl_window_default_run (GstGLWindow * window)
494 {
495 GstGLWindowPrivate *priv = window->priv;
496
497 g_main_context_push_thread_default (window->main_context);
498
499 g_main_loop_run (priv->loop);
500
501 g_main_context_pop_thread_default (window->main_context);
502 }
503
504 /**
505 * gst_gl_window_run:
506 * @window: a #GstGLWindow
507 *
508 * Start the execution of the runloop.
509 *
510 * Since: 1.4
511 */
512 void
gst_gl_window_run(GstGLWindow * window)513 gst_gl_window_run (GstGLWindow * window)
514 {
515 GstGLWindowClass *window_class;
516
517 g_return_if_fail (GST_IS_GL_WINDOW (window));
518 window_class = GST_GL_WINDOW_GET_CLASS (window);
519 g_return_if_fail (window_class->run != NULL);
520
521 window->priv->alive = TRUE;
522 window_class->run (window);
523 }
524
525 static void
gst_gl_window_default_quit(GstGLWindow * window)526 gst_gl_window_default_quit (GstGLWindow * window)
527 {
528 g_main_loop_quit (window->priv->loop);
529 }
530
531 /**
532 * gst_gl_window_quit:
533 * @window: a #GstGLWindow
534 *
535 * Quit the runloop's execution.
536 *
537 * Since: 1.4
538 */
539 void
gst_gl_window_quit(GstGLWindow * window)540 gst_gl_window_quit (GstGLWindow * window)
541 {
542 GstGLWindowClass *window_class;
543
544 g_return_if_fail (GST_IS_GL_WINDOW (window));
545 window_class = GST_GL_WINDOW_GET_CLASS (window);
546 g_return_if_fail (window_class->quit != NULL);
547
548 GST_GL_WINDOW_LOCK (window);
549
550 window->priv->alive = FALSE;
551
552 window_class->quit (window);
553
554 GST_INFO ("quit sent to gl window loop");
555
556 GST_GL_WINDOW_UNLOCK (window);
557 }
558
559 typedef struct _GstGLSyncMessage
560 {
561 GstGLWindow *window;
562 gboolean fired;
563
564 GstGLWindowCB callback;
565 gpointer data;
566 } GstGLSyncMessage;
567
568 static void
_run_message_sync(GstGLSyncMessage * message)569 _run_message_sync (GstGLSyncMessage * message)
570 {
571
572 if (message->callback)
573 message->callback (message->data);
574
575 g_mutex_lock (&message->window->priv->sync_message_lock);
576 message->fired = TRUE;
577 g_cond_broadcast (&message->window->priv->sync_message_cond);
578 g_mutex_unlock (&message->window->priv->sync_message_lock);
579 }
580
581 void
gst_gl_window_default_send_message(GstGLWindow * window,GstGLWindowCB callback,gpointer data)582 gst_gl_window_default_send_message (GstGLWindow * window,
583 GstGLWindowCB callback, gpointer data)
584 {
585 GstGLSyncMessage message;
586
587 message.window = window;
588 message.callback = callback;
589 message.data = data;
590 message.fired = FALSE;
591
592 gst_gl_window_send_message_async (window, (GstGLWindowCB) _run_message_sync,
593 &message, NULL);
594
595 g_mutex_lock (&window->priv->sync_message_lock);
596
597 /* block until opengl calls have been executed in the gl thread */
598 while (!message.fired)
599 g_cond_wait (&window->priv->sync_message_cond,
600 &window->priv->sync_message_lock);
601 g_mutex_unlock (&window->priv->sync_message_lock);
602 }
603
604 /**
605 * gst_gl_window_send_message:
606 * @window: a #GstGLWindow
607 * @callback: (scope async): function to invoke
608 * @data: (closure): data to invoke @callback with
609 *
610 * Invoke @callback with data on the window thread. @callback is guarenteed to
611 * have executed when this function returns.
612 *
613 * Since: 1.4
614 */
615 void
gst_gl_window_send_message(GstGLWindow * window,GstGLWindowCB callback,gpointer data)616 gst_gl_window_send_message (GstGLWindow * window, GstGLWindowCB callback,
617 gpointer data)
618 {
619 GstGLWindowClass *window_class;
620
621 g_return_if_fail (GST_IS_GL_WINDOW (window));
622 g_return_if_fail (callback != NULL);
623 window_class = GST_GL_WINDOW_GET_CLASS (window);
624 g_return_if_fail (window_class->send_message != NULL);
625
626 window_class->send_message (window, callback, data);
627 }
628
629 typedef struct _GstGLAsyncMessage
630 {
631 GstGLWindowCB callback;
632 gpointer data;
633 GDestroyNotify destroy;
634 } GstGLAsyncMessage;
635
636 static gboolean
_run_message_async(GstGLAsyncMessage * message)637 _run_message_async (GstGLAsyncMessage * message)
638 {
639 if (message->callback)
640 message->callback (message->data);
641
642 if (message->destroy)
643 message->destroy (message->data);
644
645 g_slice_free (GstGLAsyncMessage, message);
646
647 return FALSE;
648 }
649
650 static void
gst_gl_window_default_send_message_async(GstGLWindow * window,GstGLWindowCB callback,gpointer data,GDestroyNotify destroy)651 gst_gl_window_default_send_message_async (GstGLWindow * window,
652 GstGLWindowCB callback, gpointer data, GDestroyNotify destroy)
653 {
654 GstGLAsyncMessage *message = g_slice_new (GstGLAsyncMessage);
655
656 message->callback = callback;
657 message->data = data;
658 message->destroy = destroy;
659
660 g_main_context_invoke (window->main_context, (GSourceFunc) _run_message_async,
661 message);
662 }
663
664 /**
665 * gst_gl_window_send_message_async:
666 * @window: a #GstGLWindow
667 * @callback: (scope async): function to invoke
668 * @data: (closure): data to invoke @callback with
669 * @destroy: called when @data is not needed anymore
670 *
671 * Invoke @callback with @data on the window thread. The callback may not
672 * have been executed when this function returns.
673 *
674 * Since: 1.4
675 */
676 void
gst_gl_window_send_message_async(GstGLWindow * window,GstGLWindowCB callback,gpointer data,GDestroyNotify destroy)677 gst_gl_window_send_message_async (GstGLWindow * window, GstGLWindowCB callback,
678 gpointer data, GDestroyNotify destroy)
679 {
680 GstGLWindowClass *window_class;
681
682 g_return_if_fail (GST_IS_GL_WINDOW (window));
683 g_return_if_fail (callback != NULL);
684 window_class = GST_GL_WINDOW_GET_CLASS (window);
685 g_return_if_fail (window_class->send_message_async != NULL);
686
687 window_class->send_message_async (window, callback, data, destroy);
688 }
689
690 /**
691 * gst_gl_window_set_draw_callback:
692 * @window: a #GstGLWindow
693 * @callback: (scope notified): function to invoke
694 * @data: (closure): data to invoke @callback with
695 * @destroy_notify: called when @data is not needed any more
696 *
697 * Sets the draw callback called everytime gst_gl_window_draw() is called
698 *
699 * Since: 1.4
700 */
701 void
gst_gl_window_set_draw_callback(GstGLWindow * window,GstGLWindowCB callback,gpointer data,GDestroyNotify destroy_notify)702 gst_gl_window_set_draw_callback (GstGLWindow * window, GstGLWindowCB callback,
703 gpointer data, GDestroyNotify destroy_notify)
704 {
705 g_return_if_fail (GST_IS_GL_WINDOW (window));
706
707 GST_GL_WINDOW_LOCK (window);
708
709 if (window->draw_notify)
710 window->draw_notify (window->draw_data);
711
712 window->draw = callback;
713 window->draw_data = data;
714 window->draw_notify = destroy_notify;
715
716 GST_GL_WINDOW_UNLOCK (window);
717 }
718
719 /**
720 * gst_gl_window_set_resize_callback:
721 * @window: a #GstGLWindow
722 * @callback: (scope notified): function to invoke
723 * @data: (closure): data to invoke @callback with
724 * @destroy_notify: called when @data is not needed any more
725 *
726 * Sets the resize callback called everytime a resize of the window occurs.
727 *
728 * Since: 1.4
729 */
730 void
gst_gl_window_set_resize_callback(GstGLWindow * window,GstGLWindowResizeCB callback,gpointer data,GDestroyNotify destroy_notify)731 gst_gl_window_set_resize_callback (GstGLWindow * window,
732 GstGLWindowResizeCB callback, gpointer data, GDestroyNotify destroy_notify)
733 {
734 g_return_if_fail (GST_IS_GL_WINDOW (window));
735
736 GST_GL_WINDOW_LOCK (window);
737
738 if (window->resize_notify)
739 window->resize_notify (window->resize_data);
740
741 window->resize = callback;
742 window->resize_data = data;
743 window->resize_notify = destroy_notify;
744
745 GST_GL_WINDOW_UNLOCK (window);
746 }
747
748 /**
749 * gst_gl_window_set_close_callback:
750 * @window: a #GstGLWindow
751 * @callback: (scope notified): function to invoke
752 * @data: (closure): data to invoke @callback with
753 * @destroy_notify: called when @data is not needed any more
754 *
755 * Sets the callback called when the window is about to close.
756 *
757 * Since: 1.4
758 */
759 void
gst_gl_window_set_close_callback(GstGLWindow * window,GstGLWindowCB callback,gpointer data,GDestroyNotify destroy_notify)760 gst_gl_window_set_close_callback (GstGLWindow * window, GstGLWindowCB callback,
761 gpointer data, GDestroyNotify destroy_notify)
762 {
763 g_return_if_fail (GST_IS_GL_WINDOW (window));
764
765 GST_GL_WINDOW_LOCK (window);
766
767 if (window->close_notify)
768 window->close_notify (window->close_data);
769
770 window->close = callback;
771 window->close_data = data;
772 window->close_notify = destroy_notify;
773
774 GST_GL_WINDOW_UNLOCK (window);
775 }
776
777 /**
778 * gst_gl_window_get_display:
779 * @window: a #GstGLWindow
780 *
781 * Returns: the windowing system display handle for this @window
782 *
783 * Since: 1.4
784 */
785 guintptr
gst_gl_window_get_display(GstGLWindow * window)786 gst_gl_window_get_display (GstGLWindow * window)
787 {
788 GstGLWindowClass *window_class;
789
790 g_return_val_if_fail (GST_IS_GL_WINDOW (window), 0);
791 window_class = GST_GL_WINDOW_GET_CLASS (window);
792 g_return_val_if_fail (window_class->get_display != NULL, 0);
793
794 return window_class->get_display (window);
795 }
796
797 /**
798 * gst_gl_window_get_window_handle:
799 * @window: a #GstGLWindow
800 *
801 * Returns: the window handle we are currently rendering into
802 *
803 * Since: 1.4
804 */
805 guintptr
gst_gl_window_get_window_handle(GstGLWindow * window)806 gst_gl_window_get_window_handle (GstGLWindow * window)
807 {
808 GstGLWindowClass *window_class;
809
810 g_return_val_if_fail (GST_IS_GL_WINDOW (window), 0);
811 window_class = GST_GL_WINDOW_GET_CLASS (window);
812 g_return_val_if_fail (window_class->get_window_handle != NULL, 0);
813
814 return window_class->get_window_handle (window);
815 }
816
817 /**
818 * gst_gl_window_get_context:
819 * @window: a #GstGLWindow
820 *
821 * Returns: (transfer full): the #GstGLContext associated with this @window
822 *
823 * Since: 1.4
824 */
825 GstGLContext *
gst_gl_window_get_context(GstGLWindow * window)826 gst_gl_window_get_context (GstGLWindow * window)
827 {
828 g_return_val_if_fail (GST_IS_GL_WINDOW (window), NULL);
829
830 return (GstGLContext *) g_weak_ref_get (&window->context_ref);
831 }
832
833 /**
834 * gst_gl_window_get_surface_dimensions:
835 * @window: a #GstGLWindow
836 * @width: (out): resulting surface width
837 * @height: (out): resulting surface height
838 *
839 * Since: 1.6
840 */
841 void
gst_gl_window_get_surface_dimensions(GstGLWindow * window,guint * width,guint * height)842 gst_gl_window_get_surface_dimensions (GstGLWindow * window, guint * width,
843 guint * height)
844 {
845 if (width)
846 *width = window->priv->surface_width;
847 if (height)
848 *height = window->priv->surface_height;
849 }
850
851 void
gst_gl_window_send_key_event(GstGLWindow * window,const char * event_type,const char * key_str)852 gst_gl_window_send_key_event (GstGLWindow * window, const char *event_type,
853 const char *key_str)
854 {
855 g_signal_emit (window, gst_gl_window_signals[EVENT_KEY_SIGNAL], 0,
856 event_type, key_str);
857 }
858
859 void
gst_gl_window_send_mouse_event(GstGLWindow * window,const char * event_type,int button,double posx,double posy)860 gst_gl_window_send_mouse_event (GstGLWindow * window, const char *event_type,
861 int button, double posx, double posy)
862 {
863 g_signal_emit (window, gst_gl_window_signals[EVENT_MOUSE_SIGNAL], 0,
864 event_type, button, posx, posy);
865 }
866
867 /**
868 * gst_gl_window_handle_events:
869 * @window: a #GstGLWindow
870 * @handle_events: a #gboolean indicating if events should be handled or not.
871 *
872 * Tell a @window that it should handle events from the window system. These
873 * events are forwarded upstream as navigation events. In some window systems
874 * events are not propagated in the window hierarchy if a client is listening
875 * for them. This method allows you to disable events handling completely
876 * from the @window.
877 */
878 void
gst_gl_window_handle_events(GstGLWindow * window,gboolean handle_events)879 gst_gl_window_handle_events (GstGLWindow * window, gboolean handle_events)
880 {
881 GstGLWindowClass *window_class;
882
883 g_return_if_fail (GST_IS_GL_WINDOW (window));
884 window_class = GST_GL_WINDOW_GET_CLASS (window);
885
886 if (window_class->handle_events)
887 window_class->handle_events (window, handle_events);
888 }
889
890 /**
891 * gst_gl_window_set_render_rectangle:
892 * @window: a #GstGLWindow
893 * @x: x position
894 * @y: y position
895 * @width: width
896 * @height: height
897 *
898 * Tell a @window that it should render into a specific region of the window
899 * according to the #GstVideoOverlay interface.
900 *
901 * Returns: whether the specified region could be set
902 */
903 gboolean
gst_gl_window_set_render_rectangle(GstGLWindow * window,gint x,gint y,gint width,gint height)904 gst_gl_window_set_render_rectangle (GstGLWindow * window, gint x, gint y,
905 gint width, gint height)
906 {
907 GstGLWindowClass *window_class;
908 gboolean ret = FALSE;
909
910 g_return_val_if_fail (GST_IS_GL_WINDOW (window), FALSE);
911 window_class = GST_GL_WINDOW_GET_CLASS (window);
912
913 /* When x/y is smaller then reset the render rectangle */
914 if (x < 0 || y < 0) {
915 x = y = 0;
916 width = window->priv->surface_width;
917 height = window->priv->surface_height;
918 }
919
920 if (x < 0 || y < 0 || width <= 0 || height <= 0)
921 return FALSE;
922
923 if (window_class->set_render_rectangle)
924 ret = window_class->set_render_rectangle (window, x, y, width, height);
925
926 return ret;
927 }
928
929 /**
930 * gst_gl_window_queue_resize:
931 * @window: a #GstGLWindow
932 *
933 * Queue resizing of @window.
934 */
935 void
gst_gl_window_queue_resize(GstGLWindow * window)936 gst_gl_window_queue_resize (GstGLWindow * window)
937 {
938 GstGLWindowClass *window_class;
939
940 g_return_if_fail (GST_IS_GL_WINDOW (window));
941 window_class = GST_GL_WINDOW_GET_CLASS (window);
942
943 window->queue_resize = TRUE;
944 if (window_class->queue_resize)
945 window_class->queue_resize (window);
946 }
947
948 struct resize_data
949 {
950 GstGLWindow *window;
951 guint width, height;
952 };
953
954 static void
_on_resize(gpointer data)955 _on_resize (gpointer data)
956 {
957 struct resize_data *resize = data;
958
959 resize->window->resize (resize->window->resize_data, resize->width,
960 resize->height);
961 }
962
963 /**
964 * gst_gl_window_resize:
965 * @window: a #GstGLWindow
966 * @width: new width
967 * @height: new height
968 *
969 * Resize @window to the given @width and @height.
970 */
971 void
gst_gl_window_resize(GstGLWindow * window,guint width,guint height)972 gst_gl_window_resize (GstGLWindow * window, guint width, guint height)
973 {
974 g_return_if_fail (GST_IS_GL_WINDOW (window));
975
976 if (window->resize) {
977 struct resize_data resize = { 0, };
978
979 resize.window = window;
980 resize.width = width;
981 resize.height = height;
982
983 gst_gl_window_send_message (window, (GstGLWindowCB) _on_resize, &resize);
984 }
985
986 window->priv->surface_width = width;
987 window->priv->surface_height = height;
988
989 window->queue_resize = FALSE;
990 }
991
992 /**
993 * gst_gl_window_controls_viewport:
994 * @window: a #GstGLWindow
995 *
996 * Checks if @window controls the GL viewport.
997 *
998 * Returns: %TRUE if @window controls the GL viewport, otherwise %FALSE
999 *
1000 * Since: 1.16
1001 */
1002 gboolean
gst_gl_window_controls_viewport(GstGLWindow * window)1003 gst_gl_window_controls_viewport (GstGLWindow * window)
1004 {
1005 GstGLWindowClass *window_class;
1006
1007 g_return_val_if_fail (GST_IS_GL_WINDOW (window), FALSE);
1008 window_class = GST_GL_WINDOW_GET_CLASS (window);
1009
1010 if (!window_class->controls_viewport)
1011 return FALSE;
1012
1013 return window_class->controls_viewport (window);
1014 }
1015
1016 static GType gst_gl_dummy_window_get_type (void);
1017
1018 G_DEFINE_TYPE (GstGLDummyWindow, gst_gl_dummy_window, GST_TYPE_GL_WINDOW);
1019
1020 static void
gst_gl_dummy_window_set_window_handle(GstGLWindow * window,guintptr handle)1021 gst_gl_dummy_window_set_window_handle (GstGLWindow * window, guintptr handle)
1022 {
1023 GstGLDummyWindow *dummy = (GstGLDummyWindow *) window;
1024
1025 dummy->handle = handle;
1026 }
1027
1028 static guintptr
gst_gl_dummy_window_get_window_handle(GstGLWindow * window)1029 gst_gl_dummy_window_get_window_handle (GstGLWindow * window)
1030 {
1031 GstGLDummyWindow *dummy = (GstGLDummyWindow *) window;
1032
1033 return (guintptr) dummy->handle;
1034 }
1035
1036 static guintptr
gst_gl_dummy_window_get_display(GstGLWindow * window)1037 gst_gl_dummy_window_get_display (GstGLWindow * window)
1038 {
1039 return 0;
1040 }
1041
1042 static void
gst_gl_dummy_window_class_init(GstGLDummyWindowClass * klass)1043 gst_gl_dummy_window_class_init (GstGLDummyWindowClass * klass)
1044 {
1045 GstGLWindowClass *window_class = (GstGLWindowClass *) klass;
1046
1047 window_class->get_display =
1048 GST_DEBUG_FUNCPTR (gst_gl_dummy_window_get_display);
1049 window_class->get_window_handle =
1050 GST_DEBUG_FUNCPTR (gst_gl_dummy_window_get_window_handle);
1051 window_class->set_window_handle =
1052 GST_DEBUG_FUNCPTR (gst_gl_dummy_window_set_window_handle);
1053 }
1054
1055 static void
gst_gl_dummy_window_init(GstGLDummyWindow * dummy)1056 gst_gl_dummy_window_init (GstGLDummyWindow * dummy)
1057 {
1058 dummy->handle = 0;
1059 }
1060
1061 static GstGLDummyWindow *
gst_gl_dummy_window_new(void)1062 gst_gl_dummy_window_new (void)
1063 {
1064 GstGLDummyWindow *window;
1065
1066 window = g_object_new (gst_gl_dummy_window_get_type (), NULL);
1067 gst_object_ref_sink (window);
1068
1069 return window;
1070 }
1071