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