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