From bb0781a38947ec1aa8ecccb283a06822271fcd4c Mon Sep 17 00:00:00 2001 From: Julien Date: Fri, 10 Apr 2009 20:30:46 +0200 Subject: [PATCH] [326/906] add X backend for OpenGL ES 2.0 --- gst-libs/gst/gl/gstgldisplay.h | 168 ++--- gst-libs/gst/gl/gstgles2.h | 37 +- gst-libs/gst/gl/gstglwindow_winCE.c | 280 +++++---- gst-libs/gst/gl/gstglwindow_x11ES2.c | 906 +++++++++++++++++++++++++++ 4 files changed, 1169 insertions(+), 222 deletions(-) create mode 100644 gst-libs/gst/gl/gstglwindow_x11ES2.c diff --git a/gst-libs/gst/gl/gstgldisplay.h b/gst-libs/gst/gl/gstgldisplay.h index 6ff308a97a..983b0354b0 100644 --- a/gst-libs/gst/gl/gstgldisplay.h +++ b/gst-libs/gst/gl/gstgldisplay.h @@ -43,50 +43,54 @@ typedef struct _GstGLDisplay GstGLDisplay; typedef struct _GstGLDisplayClass GstGLDisplayClass; //Color space conversion method -typedef enum { - GST_GL_DISPLAY_CONVERSION_GLSL, //ARB_fragment_shade - GST_GL_DISPLAY_CONVERSION_MATRIX, //ARB_imaging - GST_GL_DISPLAY_CONVERSION_MESA, //MESA_ycbcr_texture +typedef enum +{ + GST_GL_DISPLAY_CONVERSION_GLSL, //ARB_fragment_shade + GST_GL_DISPLAY_CONVERSION_MATRIX, //ARB_imaging + GST_GL_DISPLAY_CONVERSION_MESA, //MESA_ycbcr_texture } GstGLDisplayConversion; //Projection type -typedef enum { +typedef enum +{ GST_GL_DISPLAY_PROJECTION_ORTHO2D, GST_GL_DISPLAY_PROJECTION_PERSPECIVE } GstGLDisplayProjection; //Texture pool elements -typedef struct _GstGLDisplayTex { +typedef struct _GstGLDisplayTex +{ GLuint texture; } GstGLDisplayTex; //Client callbacks -typedef void (* CRCB) ( GLuint, GLuint ); -typedef gboolean (* CDCB) ( GLuint, GLuint, GLuint); +typedef void (*CRCB) (GLuint, GLuint); +typedef gboolean (*CDCB) (GLuint, GLuint, GLuint); -typedef void (* GstGLDisplayThreadFunc) (GstGLDisplay *display, gpointer data); +typedef void (*GstGLDisplayThreadFunc) (GstGLDisplay * display, gpointer data); //opengl scene callback -typedef void (* GLCB) ( gint, gint, guint, gpointer stuff); +typedef void (*GLCB) (gint, gint, guint, gpointer stuff); -struct _GstGLDisplay { +struct _GstGLDisplay +{ GObject object; //thread safe - GMutex* mutex; + GMutex *mutex; //gl context - GThread* gl_thread; - GstGLWindow* gl_window; + GThread *gl_thread; + GstGLWindow *gl_window; gboolean visible; gboolean isAlive; - GHashTable* texture_pool; + GHashTable *texture_pool; //conditions - GCond* cond_create_context; - GCond* cond_destroy_context; + GCond *cond_create_context; + GCond *cond_destroy_context; //generic gl code GstGLDisplayThreadFunc generic_callback; @@ -97,9 +101,9 @@ struct _GstGLDisplay { GLuint redisplay_texture_width; GLuint redisplay_texture_height; #ifdef OPENGL_ES2 - GstGLShader* redisplay_shader; - GLbyte* redisplay_vertex_shader_str; - GLbyte* redisplay_fragment_shader_str; + GstGLShader *redisplay_shader; + gchar *redisplay_vertex_shader_str; + gchar *redisplay_fragment_shader_str; GLint redisplay_attr_position_loc; GLint redisplay_attr_texture_loc; #endif @@ -149,7 +153,7 @@ struct _GstGLDisplay { gdouble use_fbo_proj_param3; gdouble use_fbo_proj_param4; GstGLDisplayProjection use_fbo_projection; - gpointer* use_fbo_stuff; + gpointer *use_fbo_stuff; GLuint input_texture_width; GLuint input_texture_height; GLuint input_texture; @@ -174,51 +178,52 @@ struct _GstGLDisplay { GLuint ouput_texture_height; //action gen and del shader - const gchar* gen_shader_fragment_source; - const gchar* gen_shader_vertex_source; - GstGLShader* gen_shader; - GstGLShader* del_shader; + const gchar *gen_shader_fragment_source; + const gchar *gen_shader_vertex_source; + GstGLShader *gen_shader; + GstGLShader *del_shader; //fragement shader upload - gchar* text_shader_upload_YUY2_UYVY; - GstGLShader* shader_upload_YUY2; - GstGLShader* shader_upload_UYVY; + gchar *text_shader_upload_YUY2_UYVY; + GstGLShader *shader_upload_YUY2; + GstGLShader *shader_upload_UYVY; - gchar* text_shader_upload_I420_YV12; - GstGLShader* shader_upload_I420_YV12; + gchar *text_shader_upload_I420_YV12; + GstGLShader *shader_upload_I420_YV12; - gchar* text_shader_upload_AYUV; - GstGLShader* shader_upload_AYUV; + gchar *text_shader_upload_AYUV; + GstGLShader *shader_upload_AYUV; #ifdef OPENGL_ES2 - GLbyte* text_vertex_shader_upload; + gchar *text_vertex_shader_upload; GLint shader_upload_attr_position_loc; GLint shader_upload_attr_texture_loc; #endif //fragement shader download - gchar* text_shader_download_YUY2_UYVY; - GstGLShader* shader_download_YUY2; - GstGLShader* shader_download_UYVY; + gchar *text_shader_download_YUY2_UYVY; + GstGLShader *shader_download_YUY2; + GstGLShader *shader_download_UYVY; - gchar* text_shader_download_I420_YV12; - GstGLShader* shader_download_I420_YV12; + gchar *text_shader_download_I420_YV12; + GstGLShader *shader_download_I420_YV12; - gchar* text_shader_download_AYUV; - GstGLShader* shader_download_AYUV; + gchar *text_shader_download_AYUV; + GstGLShader *shader_download_AYUV; #ifdef OPENGL_ES2 - GLbyte* text_vertex_shader_download; + gchar *text_vertex_shader_download; GLint shader_download_attr_position_loc; GLint shader_download_attr_texture_loc; - GLbyte* text_fragment_shader_download_RGB; - GstGLShader* shader_download_RGB; + gchar *text_fragment_shader_download_RGB; + GstGLShader *shader_download_RGB; #endif }; -struct _GstGLDisplayClass { +struct _GstGLDisplayClass +{ GObjectClass object_class; }; @@ -228,49 +233,50 @@ GType gst_gl_display_get_type (void); //------------------------------------------------------------ //-------------------- Public declarations ------------------ //------------------------------------------------------------ -GstGLDisplay* gst_gl_display_new (void); +GstGLDisplay *gst_gl_display_new (void); -void gst_gl_display_create_context (GstGLDisplay* display, - GLint width, GLint height); -gboolean gst_gl_display_redisplay (GstGLDisplay* display, GLuint texture, gint width, gint height); +void gst_gl_display_create_context (GstGLDisplay * display, + GLint width, GLint height); +gboolean gst_gl_display_redisplay (GstGLDisplay * display, GLuint texture, + gint width, gint height); -void gst_gl_display_thread_add (GstGLDisplay *display, - GstGLDisplayThreadFunc func, gpointer data); +void gst_gl_display_thread_add (GstGLDisplay * display, + GstGLDisplayThreadFunc func, gpointer data); -void gst_gl_display_gen_texture (GstGLDisplay* display, GLuint* pTexture, GLint width, GLint height); -void gst_gl_display_del_texture (GstGLDisplay* display, GLuint texture, GLint width, GLint height); +void gst_gl_display_gen_texture (GstGLDisplay * display, GLuint * pTexture, + GLint width, GLint height); +void gst_gl_display_del_texture (GstGLDisplay * display, GLuint texture, + GLint width, GLint height); -void gst_gl_display_init_upload (GstGLDisplay* display, GstVideoFormat video_format, - guint gl_width, guint gl_height, - gint video_width, gint video_height); -gboolean gst_gl_display_do_upload (GstGLDisplay* display, GLuint texture, - gint data_width, gint data_height, - gpointer data); -void gst_gl_display_init_download (GstGLDisplay* display, GstVideoFormat video_format, - gint width, gint height); -gboolean gst_gl_display_do_download (GstGLDisplay* display, GLuint texture, - gint width, gint height, - gpointer data); +void gst_gl_display_init_upload (GstGLDisplay * display, + GstVideoFormat video_format, guint gl_width, guint gl_height, + gint video_width, gint video_height); +gboolean gst_gl_display_do_upload (GstGLDisplay * display, GLuint texture, + gint data_width, gint data_height, gpointer data); +void gst_gl_display_init_download (GstGLDisplay * display, + GstVideoFormat video_format, gint width, gint height); +gboolean gst_gl_display_do_download (GstGLDisplay * display, GLuint texture, + gint width, gint height, gpointer data); -void gst_gl_display_gen_fbo (GstGLDisplay* display, gint width, gint height, - GLuint* fbo, GLuint* depthbuffer); -gboolean gst_gl_display_use_fbo (GstGLDisplay* display, gint texture_fbo_width, gint texture_fbo_height, - GLuint fbo, GLuint depth_buffer, GLuint texture_fbo, GLCB cb, - gint input_texture_width, gint input_texture_height, GLuint input_texture, - gdouble proj_param1, gdouble proj_param2, - gdouble proj_param3, gdouble proj_param4, - GstGLDisplayProjection projection, gpointer* stuff); -void gst_gl_display_del_fbo (GstGLDisplay* display, GLuint fbo, - GLuint depth_buffer); +void gst_gl_display_gen_fbo (GstGLDisplay * display, gint width, gint height, + GLuint * fbo, GLuint * depthbuffer); +gboolean gst_gl_display_use_fbo (GstGLDisplay * display, gint texture_fbo_width, + gint texture_fbo_height, GLuint fbo, GLuint depth_buffer, + GLuint texture_fbo, GLCB cb, gint input_texture_width, + gint input_texture_height, GLuint input_texture, gdouble proj_param1, + gdouble proj_param2, gdouble proj_param3, gdouble proj_param4, + GstGLDisplayProjection projection, gpointer * stuff); +void gst_gl_display_del_fbo (GstGLDisplay * display, GLuint fbo, + GLuint depth_buffer); -void gst_gl_display_gen_shader (GstGLDisplay* display, - const gchar* shader_vertex_source, - const gchar* shader_fragment_source, - GstGLShader** shader); -void gst_gl_display_del_shader (GstGLDisplay* display, GstGLShader* shader); +void gst_gl_display_gen_shader (GstGLDisplay * display, + const gchar * shader_vertex_source, + const gchar * shader_fragment_source, GstGLShader ** shader); +void gst_gl_display_del_shader (GstGLDisplay * display, GstGLShader * shader); -void gst_gl_display_set_window_id (GstGLDisplay* display, gulong window_id); -void gst_gl_display_set_client_reshape_callback (GstGLDisplay* display, CRCB cb); -void gst_gl_display_set_client_draw_callback (GstGLDisplay* display, CDCB cb); +void gst_gl_display_set_window_id (GstGLDisplay * display, gulong window_id); +void gst_gl_display_set_client_reshape_callback (GstGLDisplay * display, + CRCB cb); +void gst_gl_display_set_client_draw_callback (GstGLDisplay * display, CDCB cb); #endif diff --git a/gst-libs/gst/gl/gstgles2.h b/gst-libs/gst/gl/gstgles2.h index 76ef4b1645..0f933dde73 100644 --- a/gst-libs/gst/gl/gstgles2.h +++ b/gst-libs/gst/gl/gstgles2.h @@ -1,4 +1,4 @@ -/* +/* * GStreamer * Copyright (C) 2009 Julien Isorce * @@ -27,9 +27,23 @@ #define GLEW_OK 0 #define GLEW_NO_ERROR 0 -static const char* glewGetString (GLenum name) { return "1.5.1"; }; -static unsigned int glewInit() { return GLEW_OK;}; -static const char* glewGetErrorString (GLenum error) { return GLEW_NO_ERROR; }; +static const char * +glewGetString (GLenum name) +{ + return "1.5.1"; +}; + +static unsigned int +glewInit () +{ + return GLEW_OK; +}; + +static const char * +glewGetErrorString (GLenum error) +{ + return GLEW_NO_ERROR; +}; #define GLEW_VERSION 1 #define GLEW_VERSION_MAJOR 5 @@ -56,6 +70,9 @@ static const char* glewGetErrorString (GLenum error) { return GLEW_NO_ERROR; }; #define GL_DEPTH_ATTACHMENT_EXT GL_DEPTH_ATTACHMENT #define GL_FRAMEBUFFER_COMPLETE_EXT GL_FRAMEBUFFER_COMPLETE #define GL_FRAMEBUFFER_UNSUPPORTED_EXT GL_FRAMEBUFFER_UNSUPPORTED +#define GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT +#define GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT +#define GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS #define GL_COLOR_ATTACHMENT0_EXT GL_COLOR_ATTACHMENT0 @@ -109,8 +126,16 @@ static const char* glewGetErrorString (GLenum error) { return GLEW_NO_ERROR; }; #define GL_COLOR_ATTACHMENT1_EXT 0 #define GL_COLOR_ATTACHMENT2_EXT 0 -static void glReadBuffer(GLenum name) {}; -static void glTexEnvi(GLenum name1, GLenum name2, GLenum name3) {}; +static void +glReadBuffer (GLenum name) +{ +}; + +static void +glTexEnvi (GLenum name1, GLenum name2, GLenum name3) +{ +}; + #define GL_TEXTURE_ENV 0 #define GL_TEXTURE_ENV_MODE 0 diff --git a/gst-libs/gst/gl/gstglwindow_winCE.c b/gst-libs/gst/gl/gstglwindow_winCE.c index be736f81a2..1acf63e7f8 100644 --- a/gst-libs/gst/gl/gstglwindow_winCE.c +++ b/gst-libs/gst/gl/gstglwindow_winCE.c @@ -27,10 +27,12 @@ #define WM_GST_GL_WINDOW_CUSTOM (WM_APP+1) #define WM_GST_GL_WINDOW_QUIT (WM_APP+2) -LRESULT CALLBACK window_proc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); -LRESULT FAR PASCAL sub_class_proc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); +LRESULT CALLBACK window_proc (HWND hWnd, UINT uMsg, WPARAM wParam, + LPARAM lParam); +LRESULT FAR PASCAL sub_class_proc (HWND hWnd, UINT uMsg, WPARAM wParam, + LPARAM lParam); -const gchar* EGLErrorString(); +const gchar *EGLErrorString (); #define GST_GL_WINDOW_GET_PRIVATE(o) \ (G_TYPE_INSTANCE_GET_PRIVATE((o), GST_GL_TYPE_WINDOW, GstGLWindowPrivate)) @@ -76,8 +78,8 @@ gst_gl_window_finalize (GObject * object) } static void -gst_gl_window_log_handler (const gchar *domain, GLogLevelFlags flags, - const gchar *message, gpointer user_data) +gst_gl_window_log_handler (const gchar * domain, GLogLevelFlags flags, + const gchar * message, gpointer user_data) { if (_gst_gl_window_debug) { g_log_default_handler (domain, flags, message, user_data); @@ -103,9 +105,8 @@ gst_gl_window_class_init (GstGLWindowClass * klass) atom = GetClassInfo (hinstance, "GSTGL", &wc); - if (atom == 0) - { - ZeroMemory (&wc, sizeof(WNDCLASS)); + if (atom == 0) { + ZeroMemory (&wc, sizeof (WNDCLASS)); wc.lpfnWndProc = window_proc; wc.cbClsExtra = 0; @@ -121,12 +122,12 @@ gst_gl_window_class_init (GstGLWindowClass * klass) atom = RegisterClass (&wc); if (atom == 0) - g_error ("Failed to register window class %x\r\n", GetLastError()); + g_error ("Failed to register window class %x\r\n", GetLastError ()); } } static void -gst_gl_window_init (GstGLWindow *window) +gst_gl_window_init (GstGLWindow * window) { window->priv = GST_GL_WINDOW_GET_PRIVATE (window); @@ -134,7 +135,7 @@ gst_gl_window_init (GstGLWindow *window) _gst_gl_window_debug = TRUE; g_log_set_handler ("GstGLWindow", G_LOG_LEVEL_DEBUG, - gst_gl_window_log_handler, NULL); + gst_gl_window_log_handler, NULL); } /* Must be called in the gl thread */ @@ -143,7 +144,7 @@ gst_gl_window_new (gint width, gint height) { GstGLWindow *window = g_object_new (GST_GL_TYPE_WINDOW, NULL); GstGLWindowPrivate *priv = window->priv; - GstGLWindowClass* klass = GST_GL_WINDOW_GET_CLASS (window); + GstGLWindowClass *klass = GST_GL_WINDOW_GET_CLASS (window); HINSTANCE hinstance = GetModuleHandle (NULL); @@ -167,21 +168,13 @@ gst_gl_window_new (gint width, gint height) priv->visible = FALSE; width += 2 * GetSystemMetrics (SM_CXSIZEFRAME); - height += 2 * GetSystemMetrics (SM_CYSIZEFRAME) + GetSystemMetrics (SM_CYCAPTION); + height += + 2 * GetSystemMetrics (SM_CYSIZEFRAME) + GetSystemMetrics (SM_CYCAPTION); - priv->internal_win_id = CreateWindow ( - "GSTGL", - "OpenGL renderer", - WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_OVERLAPPEDWINDOW, //WS_POPUP | WS_BORDER | WS_SYSMENU | WS_CAPTION - x, y, width, height, - (HWND) NULL, - (HMENU) NULL, - hinstance, - window - ); + priv->internal_win_id = CreateWindow ("GSTGL", "OpenGL renderer", WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_OVERLAPPEDWINDOW, //WS_POPUP | WS_BORDER | WS_SYSMENU | WS_CAPTION + x, y, width, height, (HWND) NULL, (HMENU) NULL, hinstance, window); - if (!priv->internal_win_id) - { + if (!priv->internal_win_id) { g_debug ("failed to create gl window: %d\n", priv->internal_win_id); return NULL; } @@ -206,36 +199,41 @@ gst_gl_window_error_quark (void) } void -gst_gl_window_set_external_window_id (GstGLWindow *window, guint64 id) +gst_gl_window_set_external_window_id (GstGLWindow * window, guint64 id) { GstGLWindowPrivate *priv = window->priv; - WNDPROC window_parent_proc = (WNDPROC) (guint64) GetWindowLongPtr((HWND)id, GWL_WNDPROC); + WNDPROC window_parent_proc = + (WNDPROC) (guint64) GetWindowLongPtr ((HWND) id, GWL_WNDPROC); RECT rect; - SetProp (priv->internal_win_id, "gl_window_parent_id", (HWND)id); - SetProp ((HWND)id, "gl_window_id", priv->internal_win_id); - SetProp ((HWND)id, "gl_window_parent_proc", (WNDPROC) window_parent_proc); - SetWindowLongPtr ((HWND)id, GWL_WNDPROC, (DWORD) (guint64) sub_class_proc); + SetProp (priv->internal_win_id, "gl_window_parent_id", (HWND) id); + SetProp ((HWND) id, "gl_window_id", priv->internal_win_id); + SetProp ((HWND) id, "gl_window_parent_proc", (WNDPROC) window_parent_proc); + SetWindowLongPtr ((HWND) id, GWL_WNDPROC, (DWORD) (guint64) sub_class_proc); SetWindowLongPtr (priv->internal_win_id, GWL_STYLE, WS_CHILD | WS_MAXIMIZE); - SetParent (priv->internal_win_id, (HWND)id); + SetParent (priv->internal_win_id, (HWND) id); //take changes into account: SWP_FRAMECHANGED - GetClientRect ((HWND)id, &rect); - SetWindowPos (priv->internal_win_id, HWND_TOP, rect.left, rect.top, rect.right, rect.bottom, - SWP_ASYNCWINDOWPOS | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED | SWP_NOACTIVATE); - MoveWindow (priv->internal_win_id, rect.left, rect.top, rect.right, rect.bottom, FALSE); + GetClientRect ((HWND) id, &rect); + SetWindowPos (priv->internal_win_id, HWND_TOP, rect.left, rect.top, + rect.right, rect.bottom, + SWP_ASYNCWINDOWPOS | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | + SWP_FRAMECHANGED | SWP_NOACTIVATE); + MoveWindow (priv->internal_win_id, rect.left, rect.top, rect.right, + rect.bottom, FALSE); } void -gst_gl_window_set_external_gl_context (GstGLWindow *window, guint64 context) +gst_gl_window_set_external_gl_context (GstGLWindow * window, guint64 context) { g_warning ("gst_gl_window_set_external_gl_context: not implemented\n"); } /* Must be called in the gl thread */ void -gst_gl_window_set_draw_callback (GstGLWindow *window, GstGLWindowCB callback, gpointer data) +gst_gl_window_set_draw_callback (GstGLWindow * window, GstGLWindowCB callback, + gpointer data) { GstGLWindowPrivate *priv = window->priv; @@ -245,7 +243,8 @@ gst_gl_window_set_draw_callback (GstGLWindow *window, GstGLWindowCB callback, gp /* Must be called in the gl thread */ void -gst_gl_window_set_resize_callback (GstGLWindow *window, GstGLWindowCB2 callback , gpointer data) +gst_gl_window_set_resize_callback (GstGLWindow * window, + GstGLWindowCB2 callback, gpointer data) { GstGLWindowPrivate *priv = window->priv; @@ -255,7 +254,8 @@ gst_gl_window_set_resize_callback (GstGLWindow *window, GstGLWindowCB2 callback /* Must be called in the gl thread */ void -gst_gl_window_set_close_callback (GstGLWindow *window, GstGLWindowCB callback, gpointer data) +gst_gl_window_set_close_callback (GstGLWindow * window, GstGLWindowCB callback, + gpointer data) { GstGLWindowPrivate *priv = window->priv; @@ -264,29 +264,28 @@ gst_gl_window_set_close_callback (GstGLWindow *window, GstGLWindowCB callback, g } void -gst_gl_window_draw_unlocked (GstGLWindow *window) +gst_gl_window_draw_unlocked (GstGLWindow * window) { gst_gl_window_draw (window); } /* Thread safe */ void -gst_gl_window_draw (GstGLWindow *window) +gst_gl_window_draw (GstGLWindow * window) { GstGLWindowPrivate *priv = window->priv; - if (!priv->visible) - { + if (!priv->visible) { ShowWindowAsync (priv->internal_win_id, SW_SHOW); priv->visible = TRUE; } RedrawWindow (priv->internal_win_id, NULL, NULL, - RDW_NOERASE | RDW_INTERNALPAINT | RDW_INVALIDATE); + RDW_NOERASE | RDW_INTERNALPAINT | RDW_INVALIDATE); } void -gst_gl_window_run_loop (GstGLWindow *window) +gst_gl_window_run_loop (GstGLWindow * window) { GstGLWindowPrivate *priv = window->priv; gboolean running = TRUE; @@ -295,18 +294,14 @@ gst_gl_window_run_loop (GstGLWindow *window) g_debug ("begin loop\n"); - while (running && (bRet = GetMessage (&msg, NULL, 0, 0)) != 0) - { - if (bRet == -1) - { - g_error ("Failed to get message %x\r\n", GetLastError()); - running = FALSE; - } - else - { - TranslateMessage (&msg); - DispatchMessage (&msg); - } + while (running && (bRet = GetMessage (&msg, NULL, 0, 0)) != 0) { + if (bRet == -1) { + g_error ("Failed to get message %x\r\n", GetLastError ()); + running = FALSE; + } else { + TranslateMessage (&msg); + DispatchMessage (&msg); + } } g_debug ("end loop\n"); @@ -314,12 +309,13 @@ gst_gl_window_run_loop (GstGLWindow *window) /* Thread safe */ void -gst_gl_window_quit_loop (GstGLWindow *window, GstGLWindowCB callback, gpointer data) +gst_gl_window_quit_loop (GstGLWindow * window, GstGLWindowCB callback, + gpointer data) { - if (window) - { + if (window) { GstGLWindowPrivate *priv = window->priv; - LRESULT res = PostMessage(priv->internal_win_id, WM_GST_GL_WINDOW_QUIT, (WPARAM) data, (LPARAM) callback); + LRESULT res = PostMessage (priv->internal_win_id, WM_GST_GL_WINDOW_QUIT, + (WPARAM) data, (LPARAM) callback); g_assert (SUCCEEDED (res)); g_debug ("end loop requested\n"); } @@ -327,21 +323,24 @@ gst_gl_window_quit_loop (GstGLWindow *window, GstGLWindowCB callback, gpointer d /* Thread safe */ void -gst_gl_window_send_message (GstGLWindow *window, GstGLWindowCB callback, gpointer data) +gst_gl_window_send_message (GstGLWindow * window, GstGLWindowCB callback, + gpointer data) { - if (window) - { + if (window) { GstGLWindowPrivate *priv = window->priv; - LRESULT res = SendMessage (priv->internal_win_id, WM_GST_GL_WINDOW_CUSTOM, (WPARAM) data, (LPARAM) callback); + LRESULT res = SendMessage (priv->internal_win_id, WM_GST_GL_WINDOW_CUSTOM, + (WPARAM) data, (LPARAM) callback); g_assert (SUCCEEDED (res)); } } -LRESULT CALLBACK window_proc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +LRESULT CALLBACK +window_proc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { if (uMsg == WM_CREATE) { - GstGLWindow *window = (GstGLWindow *) (((LPCREATESTRUCT) lParam)->lpCreateParams); + GstGLWindow *window = + (GstGLWindow *) (((LPCREATESTRUCT) lParam)->lpCreateParams); g_debug ("WM_CREATE\n"); @@ -352,68 +351,79 @@ LRESULT CALLBACK window_proc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam EGLint minorVersion; EGLint numConfigs; EGLConfig config; - EGLint contextAttribs[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE, EGL_NONE }; - - EGLint attribList[] = - { - EGL_RED_SIZE, 5, - EGL_GREEN_SIZE, 6, - EGL_BLUE_SIZE, 5, - EGL_ALPHA_SIZE, 8, - EGL_DEPTH_SIZE, 8, - EGL_STENCIL_SIZE, 8, - EGL_SAMPLE_BUFFERS, EGL_DONT_CARE, //1 + EGLint contextAttribs[] = + { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE, EGL_NONE }; + + EGLint attribList[] = { + EGL_RED_SIZE, 5, + EGL_GREEN_SIZE, 6, + EGL_BLUE_SIZE, 5, + EGL_ALPHA_SIZE, 8, + EGL_DEPTH_SIZE, 8, + EGL_STENCIL_SIZE, 8, + EGL_SAMPLE_BUFFERS, EGL_DONT_CARE, //1 EGL_NONE }; - + GstGLWindowPrivate *priv = window->priv; priv->display = eglGetDisplay (GetDC (hWnd)); if (priv->display != EGL_NO_DISPLAY) g_debug ("display retrieved: %d\n", priv->display); else - g_debug ("failed to retrieve display %d, %s\n", hWnd, EGLErrorString()); + g_debug ("failed to retrieve display %d, %s\n", hWnd, + EGLErrorString ()); if (eglInitialize (priv->display, &majorVersion, &minorVersion)) g_debug ("egl initialized: %d.%d\n", majorVersion, minorVersion); else - g_debug ("failed to initialize egl %d, %s\n", priv->display, EGLErrorString()); + g_debug ("failed to initialize egl %d, %s\n", priv->display, + EGLErrorString ()); if (eglGetConfigs (priv->display, NULL, 0, &numConfigs)) g_debug ("configs retrieved: %d\n", numConfigs); else - g_debug ("failed to retrieve configs %d, %s\n", priv->display, EGLErrorString()); + g_debug ("failed to retrieve configs %d, %s\n", priv->display, + EGLErrorString ()); if (eglChooseConfig (priv->display, attribList, &config, 1, &numConfigs)) g_debug ("config set: %d, %d\n", config, numConfigs); else - g_debug ("failed to set config %d, %s\n", priv->display, EGLErrorString()); + g_debug ("failed to set config %d, %s\n", priv->display, + EGLErrorString ()); - priv->surface = eglCreateWindowSurface (priv->display, config, (EGLNativeWindowType)hWnd, NULL); + priv->surface = + eglCreateWindowSurface (priv->display, config, + (EGLNativeWindowType) hWnd, NULL); if (priv->surface != EGL_NO_SURFACE) g_debug ("surface created: %d\n", priv->surface); else - g_debug ("failed to create surface %d, %d, %d, %s\n", priv->display, priv->surface, hWnd, EGLErrorString()); + g_debug ("failed to create surface %d, %d, %d, %s\n", priv->display, + priv->surface, hWnd, EGLErrorString ()); - priv->gl_context = eglCreateContext (priv->display, config, EGL_NO_CONTEXT, contextAttribs); + priv->gl_context = + eglCreateContext (priv->display, config, EGL_NO_CONTEXT, + contextAttribs); if (priv->gl_context != EGL_NO_CONTEXT) g_debug ("gl context created: %d\n", priv->gl_context); else - g_debug ("failed to create glcontext %d, %d, s\n", priv->gl_context, hWnd, EGLErrorString()); + g_debug ("failed to create glcontext %d, %d, %s\n", priv->gl_context, + hWnd, EGLErrorString ()); ReleaseDC (hWnd, priv->display); - if (!eglMakeCurrent (priv->display, priv->surface, priv->surface, priv->gl_context)) - g_debug ("failed to make opengl context current %d, %s\n", hWnd, EGLErrorString()); + if (!eglMakeCurrent (priv->display, priv->surface, priv->surface, + priv->gl_context)) + g_debug ("failed to make opengl context current %d, %s\n", hWnd, + EGLErrorString ()); } SetProp (hWnd, "gl_window", window); return 0; - } - else if (GetProp(hWnd, "gl_window")) { + } else if (GetProp (hWnd, "gl_window")) { - GstGLWindow *window = GetProp(hWnd, "gl_window"); + GstGLWindow *window = GetProp (hWnd, "gl_window"); GstGLWindowPrivate *priv = NULL; g_assert (window); @@ -424,21 +434,20 @@ LRESULT CALLBACK window_proc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam g_assert (priv->internal_win_id == hWnd); - g_assert (priv->gl_context == eglGetCurrentContext()); + g_assert (priv->gl_context == eglGetCurrentContext ()); - switch ( uMsg ) { + switch (uMsg) { case WM_SIZE: { if (priv->resize_cb) - priv->resize_cb (priv->resize_data, LOWORD(lParam), HIWORD(lParam)); + priv->resize_cb (priv->resize_data, LOWORD (lParam), HIWORD (lParam)); break; } case WM_PAINT: { - if (priv->draw_cb) - { + if (priv->draw_cb) { priv->draw_cb (priv->draw_data); eglSwapBuffers (priv->display, priv->surface); ValidateRect (hWnd, NULL); @@ -451,14 +460,14 @@ LRESULT CALLBACK window_proc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam ShowWindowAsync (priv->internal_win_id, SW_HIDE); if (priv->close_cb) - priv->close_cb (priv->close_data); + priv->close_cb (priv->close_data); - priv->draw_cb = NULL; - priv->draw_data = NULL; - priv->resize_cb = NULL; - priv->resize_data = NULL; - priv->close_cb = NULL; - priv->close_data = NULL; + priv->draw_cb = NULL; + priv->draw_data = NULL; + priv->resize_cb = NULL; + priv->resize_data = NULL; + priv->close_cb = NULL; + priv->close_data = NULL; break; } @@ -472,13 +481,13 @@ LRESULT CALLBACK window_proc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam destroy_cb ((gpointer) wParam); parent_id = GetProp (hWnd, "gl_window_parent_id"); - if (parent_id) - { + if (parent_id) { WNDPROC parent_proc = GetProp (parent_id, "gl_window_parent_proc"); g_assert (parent_proc); - SetWindowLongPtr (parent_id, GWL_WNDPROC, (LONG) (guint64) parent_proc); + SetWindowLongPtr (parent_id, GWL_WNDPROC, + (LONG) (guint64) parent_proc); SetParent (hWnd, NULL); RemoveProp (parent_id, "gl_window_parent_proc"); @@ -488,34 +497,36 @@ LRESULT CALLBACK window_proc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam priv->is_closed = TRUE; RemoveProp (hWnd, "gl_window"); - if (!eglMakeCurrent (priv->display, priv->surface, priv->surface, EGL_NO_CONTEXT)) - g_debug ("failed to make current null context %d, %s\n", priv->display, EGLErrorString()); + if (!eglMakeCurrent (priv->display, priv->surface, priv->surface, + EGL_NO_CONTEXT)) + g_debug ("failed to make current null context %d, %s\n", + priv->display, EGLErrorString ()); - if (priv->gl_context) - { + if (priv->gl_context) { if (!eglDestroyContext (priv->display, priv->gl_context)) - g_debug ("failed to destroy context %d, %s\n", priv->gl_context, EGLErrorString()); + g_debug ("failed to destroy context %d, %s\n", priv->gl_context, + EGLErrorString ()); priv->gl_context = NULL; } - if (priv->surface) - { + if (priv->surface) { if (!eglDestroySurface (priv->display, priv->surface)) - g_debug ("failed to destroy surface %d, %s\n", priv->surface, EGLErrorString()); + g_debug ("failed to destroy surface %d, %s\n", priv->surface, + EGLErrorString ()); priv->surface = NULL; } - if (priv->surface) - { + if (priv->surface) { if (!eglTerminate (priv->display)) - g_debug ("failed to terminate display %d, %s\n", priv->display, EGLErrorString()); + g_debug ("failed to terminate display %d, %s\n", priv->display, + EGLErrorString ()); priv->surface = NULL; } - if (priv->internal_win_id) - { - if (!DestroyWindow(priv->internal_win_id)) - g_debug ("failed to destroy window %d, 0x%x\n", hWnd, GetLastError()); + if (priv->internal_win_id) { + if (!DestroyWindow (priv->internal_win_id)) + g_debug ("failed to destroy window %d, 0x%x\n", hWnd, + GetLastError ()); } PostQuitMessage (0); @@ -532,8 +543,7 @@ LRESULT CALLBACK window_proc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam case WM_GST_GL_WINDOW_CUSTOM: { - if (!priv->is_closed) - { + if (!priv->is_closed) { GstGLWindowCB custom_cb = (GstGLWindowCB) lParam; custom_cb ((gpointer) wParam); } @@ -554,28 +564,28 @@ LRESULT CALLBACK window_proc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam } return 0; - } - else - return DefWindowProc( hWnd, uMsg, wParam, lParam ); + } else + return DefWindowProc (hWnd, uMsg, wParam, lParam); } -LRESULT FAR PASCAL sub_class_proc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +LRESULT FAR PASCAL +sub_class_proc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { WNDPROC window_parent_proc = GetProp (hWnd, "gl_window_parent_proc"); - if (uMsg == WM_SIZE) - { + if (uMsg == WM_SIZE) { HWND gl_window_id = GetProp (hWnd, "gl_window_id"); - MoveWindow (gl_window_id, 0, 0, LOWORD(lParam), HIWORD(lParam), FALSE); + MoveWindow (gl_window_id, 0, 0, LOWORD (lParam), HIWORD (lParam), FALSE); } return CallWindowProc (window_parent_proc, hWnd, uMsg, wParam, lParam); } -const gchar* EGLErrorString() +const gchar * +EGLErrorString () { - EGLint nErr = eglGetError(); - switch(nErr){ + EGLint nErr = eglGetError (); + switch (nErr) { case EGL_SUCCESS: return "EGL_SUCCESS"; case EGL_BAD_DISPLAY: diff --git a/gst-libs/gst/gl/gstglwindow_x11ES2.c b/gst-libs/gst/gl/gstglwindow_x11ES2.c new file mode 100644 index 0000000000..e885013971 --- /dev/null +++ b/gst-libs/gst/gl/gstglwindow_x11ES2.c @@ -0,0 +1,906 @@ +/* + * GStreamer + * Copyright (C) 2008 Julien Isorce + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "gstglwindow.h" + +#include + +#include +#include + +#define GST_GL_WINDOW_GET_PRIVATE(o) \ + (G_TYPE_INSTANCE_GET_PRIVATE((o), GST_GL_TYPE_WINDOW, GstGLWindowPrivate)) + + +/* A gl window is created and deleted in a thread dedicated to opengl calls + The name contains "window" because an opengl context is used in cooperation + with a window */ + +const gchar *EGLErrorString (); + +enum +{ + ARG_0, + ARG_DISPLAY +}; + +struct _GstGLWindowPrivate +{ + /* X is not thread safe */ + GMutex *x_lock; + GCond *cond_send_message; + gboolean running; + gboolean visible; + gboolean allow_extra_expose_events; + + /* X context */ + gchar *display_name; + Display *device; + gint screen_num; + Window root; + gint depth; + gint device_width; + gint device_height; + gint connection; + XVisualInfo *visual_info; + Window parent; + Window internal_win_id; + + /* We use a specific connection to send events */ + Display *disp_send; + + /* EGL */ + EGLContext gl_context; + EGLDisplay gl_display; + EGLSurface gl_surface; + + /* frozen callbacks */ + GstGLWindowCB draw_cb; + gpointer draw_data; + GstGLWindowCB2 resize_cb; + gpointer resize_data; + GstGLWindowCB close_cb; + gpointer close_data; +}; + +G_DEFINE_TYPE (GstGLWindow, gst_gl_window, G_TYPE_OBJECT); + +#undef G_LOG_DOMAIN +#define G_LOG_DOMAIN "GstGLWindow" + +gboolean _gst_gl_window_debug = FALSE; + +/* Must be called in the gl thread */ +static void +gst_gl_window_finalize (GObject * object) +{ + GstGLWindow *window = GST_GL_WINDOW (object); + GstGLWindowPrivate *priv = window->priv; + XEvent event; + Bool ret = TRUE; + + g_mutex_lock (priv->x_lock); + + priv->parent = 0; + + XUnmapWindow (priv->device, priv->internal_win_id); + + ret = + eglMakeCurrent (priv->device, EGL_NO_SURFACE, EGL_NO_SURFACE, + EGL_NO_CONTEXT); + if (!ret) + g_debug ("failed to release opengl context\n"); + + eglDestroyContext (priv->device, priv->gl_context); + + eglTerminate (priv->device); + + XFree (priv->visual_info); + + XReparentWindow (priv->device, priv->internal_win_id, priv->root, 0, 0); + + XDestroyWindow (priv->device, priv->internal_win_id); + + XSync (priv->device, FALSE); + + while (XPending (priv->device)) + XNextEvent (priv->device, &event); + + XSetCloseDownMode (priv->device, DestroyAll); + + /*XAddToSaveSet (display, w) + Display *display; + Window w; */ + + //FIXME: it seems it causes destroy all created windows, even by other display connection: + //This is case in: gst-launch-0.10 videotestsrc ! tee name=t t. ! queue ! glimagesink t. ! queue ! glimagesink + //When the first window is closed and so its display is closed by the following line, then the other Window managed by the + //other glimagesink, is not useable and so each opengl call causes a segmentation fault. + //Maybe the solution is to use: XAddToSaveSet + //The following line is commented to avoid the disagreement explained before. + //XCloseDisplay (priv->device); + + g_debug ("display receiver closed\n"); + + XCloseDisplay (priv->disp_send); + + g_debug ("display sender closed\n"); + + if (priv->cond_send_message) { + g_cond_free (priv->cond_send_message); + priv->cond_send_message = NULL; + } + + g_mutex_unlock (priv->x_lock); + + if (priv->x_lock) { + g_mutex_free (priv->x_lock); + priv->x_lock = NULL; + } + + G_OBJECT_CLASS (gst_gl_window_parent_class)->finalize (object); +} + +static void +gst_gl_window_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + GstGLWindow *window; + GstGLWindowPrivate *priv; + + g_return_if_fail (GST_GL_IS_WINDOW (object)); + + window = GST_GL_WINDOW (object); + + priv = window->priv; + + switch (prop_id) { + case ARG_DISPLAY: + priv->display_name = g_strdup (g_value_get_string (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_gl_window_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec) +{ + GstGLWindow *window; + GstGLWindowPrivate *priv; + + g_return_if_fail (GST_GL_IS_WINDOW (object)); + + window = GST_GL_WINDOW (object); + + priv = window->priv; + + switch (prop_id) { + case ARG_DISPLAY: + g_value_set_string (value, priv->display_name); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_gl_window_log_handler (const gchar * domain, GLogLevelFlags flags, + const gchar * message, gpointer user_data) +{ + if (_gst_gl_window_debug) { + g_log_default_handler (domain, flags, message, user_data); + } +} + +static void +gst_gl_window_class_init (GstGLWindowClass * klass) +{ + GObjectClass *obj_class = G_OBJECT_CLASS (klass); + + g_type_class_add_private (klass, sizeof (GstGLWindowPrivate)); + + obj_class->finalize = gst_gl_window_finalize; + obj_class->set_property = gst_gl_window_set_property; + obj_class->get_property = gst_gl_window_get_property; + + g_object_class_install_property (obj_class, ARG_DISPLAY, + g_param_spec_string ("display", "Display", "X Display name", NULL, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); +} + +static void +gst_gl_window_init (GstGLWindow * window) +{ + window->priv = GST_GL_WINDOW_GET_PRIVATE (window); + + if (g_getenv ("GST_GL_WINDOW_DEBUG") != NULL) + _gst_gl_window_debug = TRUE; + + g_log_set_handler ("GstGLWindow", G_LOG_LEVEL_DEBUG, + gst_gl_window_log_handler, NULL); +} + +/* Must be called in the gl thread */ +GstGLWindow * +gst_gl_window_new (gint width, gint height) +{ + GstGLWindow *window = g_object_new (GST_GL_TYPE_WINDOW, NULL); + GstGLWindowPrivate *priv = window->priv; + + EGLint config_attrib[] = { + EGL_SURFACE_TYPE, EGL_WINDOW_BIT, + EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, + EGL_NONE + }; + + EGLint context_attrib[] = { + EGL_CONTEXT_CLIENT_VERSION, 2, + EGL_NONE + }; + + EGLint majorVersion; + EGLint minorVersion; + EGLint numConfigs; + EGLConfig config; + + XSetWindowAttributes win_attr; + XTextProperty text_property; + XWMHints wm_hints; + unsigned long mask; + const gchar *title = "OpenGL renderer"; + Atom wm_atoms[3]; + + static gint x = 0; + static gint y = 0; + + setlocale (LC_NUMERIC, "C"); + + priv->x_lock = g_mutex_new (); + priv->cond_send_message = g_cond_new (); + priv->running = TRUE; + priv->visible = FALSE; + priv->parent = 0; + priv->allow_extra_expose_events = TRUE; + + g_mutex_lock (priv->x_lock); + + priv->device = XOpenDisplay (priv->display_name); + + XSynchronize (priv->device, FALSE); + + g_debug ("gl device id: %ld\n", (gulong) priv->device); + + priv->disp_send = XOpenDisplay (priv->display_name); + + XSynchronize (priv->disp_send, FALSE); + + g_debug ("gl display sender: %ld\n", (gulong) priv->disp_send); + + priv->screen_num = DefaultScreen (priv->device); + priv->root = RootWindow (priv->device, priv->screen_num); + priv->depth = DefaultDepth (priv->device, priv->screen_num); + + g_debug ("gl root id: %" G_GUINT64_FORMAT "\n", (guint64) priv->root); + + priv->device_width = DisplayWidth (priv->device, priv->screen_num); + priv->device_height = DisplayHeight (priv->device, priv->screen_num); + + priv->visual_info = g_new0 (XVisualInfo, 1); + XMatchVisualInfo (priv->device, priv->screen_num, priv->depth, TrueColor, + priv->visual_info); + + win_attr.event_mask = + StructureNotifyMask | ExposureMask | VisibilityChangeMask; + win_attr.do_not_propagate_mask = NoEventMask; + + win_attr.background_pixmap = None; + win_attr.background_pixel = 0; + win_attr.border_pixel = 0; + + win_attr.colormap = + XCreateColormap (priv->device, priv->root, priv->visual_info->visual, + AllocNone); + + mask = CWBackPixmap | CWBorderPixel | CWColormap | CWEventMask; + + priv->internal_win_id = XCreateWindow (priv->device, priv->root, x, y, + width, height, 0, priv->visual_info->depth, InputOutput, + priv->visual_info->visual, mask, &win_attr); + + x += 20; + y += 20; + + XSync (priv->device, FALSE); + + XSetWindowBackgroundPixmap (priv->device, priv->internal_win_id, None); + + g_debug ("gl window id: %" G_GUINT64_FORMAT "\n", + (guint64) priv->internal_win_id); + + g_debug ("gl window props: x:%d y:%d w:%d h:%d\n", x, y, width, height); + + wm_atoms[0] = XInternAtom (priv->device, "WM_DELETE_WINDOW", True); + if (wm_atoms[0] == None) + g_debug ("Cannot create WM_DELETE_WINDOW\n"); + + wm_atoms[1] = XInternAtom (priv->device, "WM_GL_WINDOW", False); + if (wm_atoms[1] == None) + g_debug ("Cannot create WM_GL_WINDOW\n"); + + wm_atoms[2] = XInternAtom (priv->device, "WM_QUIT_LOOP", False); + if (wm_atoms[2] == None) + g_debug ("Cannot create WM_QUIT_LOOP\n"); + + XSetWMProtocols (priv->device, priv->internal_win_id, wm_atoms, 2); + + wm_hints.flags = StateHint; + wm_hints.initial_state = NormalState; + wm_hints.input = False; + + XStringListToTextProperty ((char **) &title, 1, &text_property); + + XSetWMProperties (priv->device, priv->internal_win_id, &text_property, + &text_property, 0, 0, NULL, &wm_hints, NULL); + + XFree (text_property.value); + + priv->gl_display = eglGetDisplay ((EGLNativeDisplayType) priv->device); + + if (eglInitialize (priv->gl_display, &majorVersion, &minorVersion)) + g_debug ("egl initialized: %d.%d\n", majorVersion, minorVersion); + else + g_debug ("failed to initialize egl %ld, %s\n", (gulong) priv->gl_display, + EGLErrorString ()); + + if (eglChooseConfig (priv->gl_display, config_attrib, &config, 1, + &numConfigs)) + g_debug ("config set: %ld, %ld\n", (gulong) config, (gulong) numConfigs); + else + g_debug ("failed to set config %ld, %s\n", (gulong) priv->gl_display, + EGLErrorString ()); + + priv->gl_surface = + eglCreateWindowSurface (priv->gl_display, config, + (EGLNativeWindowType) priv->internal_win_id, NULL); + if (priv->gl_surface != EGL_NO_SURFACE) + g_debug ("surface created: %ld\n", (gulong) priv->gl_surface); + else + g_debug ("failed to create surface %ld, %ld, %ld, %s\n", + (gulong) priv->gl_display, (gulong) priv->gl_surface, + (gulong) priv->gl_display, EGLErrorString ()); + + priv->gl_context = + eglCreateContext (priv->gl_display, config, EGL_NO_CONTEXT, + context_attrib); + if (priv->gl_context != EGL_NO_CONTEXT) + g_debug ("gl context created: %ld\n", (gulong) priv->gl_context); + else + g_debug ("failed to create glcontext %ld, %ld, %s\n", + (gulong) priv->gl_context, (gulong) priv->gl_display, + EGLErrorString ()); + + if (!eglMakeCurrent (priv->gl_display, priv->gl_surface, priv->gl_surface, + priv->gl_context)) + g_debug ("failed to make opengl context current %ld, %s\n", + (gulong) priv->gl_display, EGLErrorString ()); + + g_mutex_unlock (priv->x_lock); + + return window; +} + +GQuark +gst_gl_window_error_quark (void) +{ + return g_quark_from_static_string ("gst-gl-window-error"); +} + +/* Not called by the gl thread */ +void +gst_gl_window_set_external_window_id (GstGLWindow * window, guint64 id) +{ + if (window) { + GstGLWindowPrivate *priv = window->priv; + XWindowAttributes attr; + + g_mutex_lock (priv->x_lock); + + priv->parent = (Window) id; + + g_debug ("set parent window id: %" G_GUINT64_FORMAT "\n", id); + + XGetWindowAttributes (priv->disp_send, priv->parent, &attr); + + XResizeWindow (priv->disp_send, priv->internal_win_id, attr.width, + attr.height); + + XReparentWindow (priv->disp_send, priv->internal_win_id, priv->parent, + attr.x, attr.y); + + XSync (priv->disp_send, FALSE); + + g_mutex_unlock (priv->x_lock); + } +} + +void +gst_gl_window_set_external_gl_context (GstGLWindow * window, guint64 context) +{ + g_warning ("gst_gl_window_set_external_gl_context: not implemented\n"); +} + +void +gst_gl_window_set_draw_callback (GstGLWindow * window, GstGLWindowCB callback, + gpointer data) +{ + GstGLWindowPrivate *priv = window->priv; + + g_mutex_lock (priv->x_lock); + + priv->draw_cb = callback; + priv->draw_data = data; + + g_mutex_unlock (priv->x_lock); +} + +void +gst_gl_window_set_resize_callback (GstGLWindow * window, + GstGLWindowCB2 callback, gpointer data) +{ + GstGLWindowPrivate *priv = window->priv; + + g_mutex_lock (priv->x_lock); + + priv->resize_cb = callback; + priv->resize_data = data; + + g_mutex_unlock (priv->x_lock); +} + +void +gst_gl_window_set_close_callback (GstGLWindow * window, GstGLWindowCB callback, + gpointer data) +{ + GstGLWindowPrivate *priv = window->priv; + + g_mutex_lock (priv->x_lock); + + priv->close_cb = callback; + priv->close_data = data; + + g_mutex_unlock (priv->x_lock); +} + +/* Called in the gl thread */ +void +gst_gl_window_draw_unlocked (GstGLWindow * window) +{ + GstGLWindowPrivate *priv = window->priv; + + if (priv->running && priv->allow_extra_expose_events) { + XEvent event; + XWindowAttributes attr; + + XGetWindowAttributes (priv->device, priv->internal_win_id, &attr); + + event.xexpose.type = Expose; + event.xexpose.send_event = TRUE; + event.xexpose.display = priv->device; + event.xexpose.window = priv->internal_win_id; + event.xexpose.x = attr.x; + event.xexpose.y = attr.y; + event.xexpose.width = attr.width; + event.xexpose.height = attr.height; + event.xexpose.count = 0; + + XSendEvent (priv->device, priv->internal_win_id, FALSE, ExposureMask, + &event); + XSync (priv->disp_send, FALSE); + } +} + +/* Not called by the gl thread */ +void +gst_gl_window_draw (GstGLWindow * window) +{ + if (window) { + GstGLWindowPrivate *priv = window->priv; + + g_mutex_lock (priv->x_lock); + + if (priv->running) { + XEvent event; + XWindowAttributes attr; + + if (!priv->visible) { + XMapWindow (priv->disp_send, priv->internal_win_id); + priv->visible = TRUE; + } + + XGetWindowAttributes (priv->disp_send, priv->internal_win_id, &attr); + + if (priv->parent) { + XWindowAttributes attr_parent; + XGetWindowAttributes (priv->disp_send, priv->parent, &attr_parent); + + if (attr.x != attr_parent.x || attr.y != attr_parent.y || + attr.width != attr_parent.width + || attr.height != attr_parent.height) { + XMoveResizeWindow (priv->disp_send, priv->internal_win_id, + attr_parent.x, attr_parent.y, attr_parent.width, + attr_parent.height); + XSync (priv->disp_send, FALSE); + + attr.x = attr_parent.x; + attr.y = attr_parent.y; + + attr.width = attr_parent.width; + attr.height = attr_parent.height; + + g_debug ("parent resize: %d, %d, %d, %d\n", attr_parent.x, + attr_parent.y, attr_parent.width, attr_parent.height); + } + } + + event.xexpose.type = Expose; + event.xexpose.send_event = TRUE; + event.xexpose.display = priv->disp_send; + event.xexpose.window = priv->internal_win_id; + event.xexpose.x = attr.x; + event.xexpose.y = attr.y; + event.xexpose.width = attr.width; + event.xexpose.height = attr.height; + event.xexpose.count = 0; + + XSendEvent (priv->disp_send, priv->internal_win_id, FALSE, ExposureMask, + &event); + XSync (priv->disp_send, FALSE); + } + + g_mutex_unlock (priv->x_lock); + } +} + +/* Called in the gl thread */ +void +gst_gl_window_run_loop (GstGLWindow * window) +{ + GstGLWindowPrivate *priv = window->priv; + + g_debug ("begin loop\n"); + + g_mutex_lock (priv->x_lock); + + while (priv->running) { + XEvent event; + XEvent pending_event; + + g_mutex_unlock (priv->x_lock); + + /* XSendEvent (which are called in other threads) are done from another display structure */ + XNextEvent (priv->device, &event); + + g_mutex_lock (priv->x_lock); + + // use in generic/cube and other related uses + priv->allow_extra_expose_events = XPending (priv->device) <= 2; + + switch (event.type) { + case ClientMessage: + { + + Atom wm_delete = XInternAtom (priv->device, "WM_DELETE_WINDOW", True); + Atom wm_gl = XInternAtom (priv->device, "WM_GL_WINDOW", True); + Atom wm_quit_loop = XInternAtom (priv->device, "WM_QUIT_LOOP", True); + + if (wm_delete == None) + g_debug ("Cannot create WM_DELETE_WINDOW\n"); + if (wm_gl == None) + g_debug ("Cannot create WM_GL_WINDOW\n"); + if (wm_quit_loop == None) + g_debug ("Cannot create WM_QUIT_LOOP\n"); + + /* Message sent with gst_gl_window_send_message */ + if (wm_gl != None && event.xclient.message_type == wm_gl) { + if (priv->running) { +#if SIZEOF_VOID_P == 8 + GstGLWindowCB custom_cb = + (GstGLWindowCB) (((event.xclient.data. + l[0] & 0xffffffff) << 32) | (event.xclient.data. + l[1] & 0xffffffff)); + gpointer custom_data = + (gpointer) (((event.xclient.data. + l[2] & 0xffffffff) << 32) | (event.xclient.data. + l[3] & 0xffffffff)); +#else + GstGLWindowCB custom_cb = (GstGLWindowCB) event.xclient.data.l[0]; + gpointer custom_data = (gpointer) event.xclient.data.l[1]; +#endif + + if (!custom_cb || !custom_data) + g_debug ("custom cb not initialized\n"); + + custom_cb (custom_data); + } + + g_cond_signal (priv->cond_send_message); + } + + /* User clicked on the cross */ + else if (wm_delete != None + && (Atom) event.xclient.data.l[0] == wm_delete) { + g_debug ("Close %" G_GUINT64_FORMAT "\n", + (guint64) priv->internal_win_id); + + if (priv->close_cb) + priv->close_cb (priv->close_data); + + priv->draw_cb = NULL; + priv->draw_data = NULL; + priv->resize_cb = NULL; + priv->resize_data = NULL; + priv->close_cb = NULL; + priv->close_data = NULL; + } + + /* message sent with gst_gl_window_quit_loop */ + else if (wm_quit_loop != None + && event.xclient.message_type == wm_quit_loop) { +#if SIZEOF_VOID_P == 8 + GstGLWindowCB destroy_cb = + (GstGLWindowCB) (((event.xclient.data. + l[0] & 0xffffffff) << 32) | (event.xclient.data. + l[1] & 0xffffffff)); + gpointer destroy_data = + (gpointer) (((event.xclient.data. + l[2] & 0xffffffff) << 32) | (event.xclient.data. + l[3] & 0xffffffff)); +#else + GstGLWindowCB destroy_cb = (GstGLWindowCB) event.xclient.data.l[0]; + gpointer destroy_data = (gpointer) event.xclient.data.l[1]; +#endif + + g_debug ("Quit loop message %" G_GUINT64_FORMAT "\n", + (guint64) priv->internal_win_id); + + /* exit loop */ + priv->running = FALSE; + + /* make sure last pendings send message calls are executed */ + XFlush (priv->device); + while (XCheckTypedEvent (priv->device, ClientMessage, &pending_event)) { +#if SIZEOF_VOID_P == 8 + GstGLWindowCB custom_cb = + (GstGLWindowCB) (((event.xclient.data. + l[0] & 0xffffffff) << 32) | (event.xclient.data. + l[1] & 0xffffffff)); + gpointer custom_data = + (gpointer) (((event.xclient.data. + l[2] & 0xffffffff) << 32) | (event.xclient.data. + l[3] & 0xffffffff)); +#else + GstGLWindowCB custom_cb = (GstGLWindowCB) event.xclient.data.l[0]; + gpointer custom_data = (gpointer) event.xclient.data.l[1]; +#endif + g_debug ("execute last pending custom x events\n"); + + if (!custom_cb || !custom_data) + g_debug ("custom cb not initialized\n"); + + custom_cb (custom_data); + + g_cond_signal (priv->cond_send_message); + } + + /* Finally we can destroy opengl ressources (texture/shaders/fbo) */ + if (!destroy_cb || !destroy_data) + g_debug ("destroy cb not correclty set\n"); + + destroy_cb (destroy_data); + + } else + g_debug ("client message not reconized \n"); + break; + } + + case CreateNotify: + case ConfigureNotify: + { + if (priv->resize_cb) + priv->resize_cb (priv->resize_data, event.xconfigure.width, + event.xconfigure.height); + break; + } + + case DestroyNotify: + g_debug ("DestroyNotify\n"); + break; + + case Expose: + if (priv->draw_cb) { + priv->draw_cb (priv->draw_data); + glFlush (); + eglSwapBuffers (priv->gl_display, priv->gl_surface); + } + break; + + case VisibilityNotify: + { + switch (event.xvisibility.state) { + case VisibilityUnobscured: + if (priv->draw_cb) + priv->draw_cb (priv->draw_data); + break; + + case VisibilityPartiallyObscured: + if (priv->draw_cb) + priv->draw_cb (priv->draw_data); + break; + + case VisibilityFullyObscured: + break; + + default: + g_debug ("unknown xvisibility event: %d\n", + event.xvisibility.state); + break; + } + break; + } + + default: + g_debug ("unknow\n"); + break; + + } // switch + + } // while running + + g_mutex_unlock (priv->x_lock); + + g_debug ("end loop\n"); +} + +/* Not called by the gl thread */ +void +gst_gl_window_quit_loop (GstGLWindow * window, GstGLWindowCB callback, + gpointer data) +{ + if (window) { + GstGLWindowPrivate *priv = window->priv; + + g_mutex_lock (priv->x_lock); + + if (priv->running) { + XEvent event; + + event.xclient.type = ClientMessage; + event.xclient.send_event = TRUE; + event.xclient.display = priv->disp_send; + event.xclient.window = priv->internal_win_id; + event.xclient.message_type = + XInternAtom (priv->disp_send, "WM_QUIT_LOOP", True);; + event.xclient.format = 32; +#if SIZEOF_VOID_P == 8 + event.xclient.data.l[0] = (((long) callback) >> 32) & 0xffffffff; + event.xclient.data.l[1] = (((long) callback)) & 0xffffffff; + event.xclient.data.l[2] = (((long) data) >> 32) & 0xffffffff; + event.xclient.data.l[3] = (((long) data)) & 0xffffffff; +#else + event.xclient.data.l[0] = (long) callback; + event.xclient.data.l[1] = (long) data; +#endif + + XSendEvent (priv->disp_send, priv->internal_win_id, FALSE, NoEventMask, + &event); + XSync (priv->disp_send, FALSE); + } + + g_mutex_unlock (priv->x_lock); + } +} + +/* Not called by the gl thread */ +void +gst_gl_window_send_message (GstGLWindow * window, GstGLWindowCB callback, + gpointer data) +{ + if (window) { + GstGLWindowPrivate *priv = window->priv; + + g_mutex_lock (priv->x_lock); + + if (priv->running) { + XEvent event; + + event.xclient.type = ClientMessage; + event.xclient.send_event = TRUE; + event.xclient.display = priv->disp_send; + event.xclient.window = priv->internal_win_id; + event.xclient.message_type = + XInternAtom (priv->disp_send, "WM_GL_WINDOW", True); + event.xclient.format = 32; +#if SIZEOF_VOID_P == 8 + event.xclient.data.l[0] = (((long) callback) >> 32) & 0xffffffff; + event.xclient.data.l[1] = (((long) callback)) & 0xffffffff; + event.xclient.data.l[2] = (((long) data) >> 32) & 0xffffffff; + event.xclient.data.l[3] = (((long) data)) & 0xffffffff; +#else + event.xclient.data.l[0] = (long) callback; + event.xclient.data.l[1] = (long) data; +#endif + + XSendEvent (priv->disp_send, priv->internal_win_id, FALSE, NoEventMask, + &event); + XSync (priv->disp_send, FALSE); + + /* block until opengl calls have been executed in the gl thread */ + g_cond_wait (priv->cond_send_message, priv->x_lock); + } + + g_mutex_unlock (priv->x_lock); + } +} + +const gchar * +EGLErrorString () +{ + EGLint nErr = eglGetError (); + switch (nErr) { + case EGL_SUCCESS: + return "EGL_SUCCESS"; + case EGL_BAD_DISPLAY: + return "EGL_BAD_DISPLAY"; + case EGL_NOT_INITIALIZED: + return "EGL_NOT_INITIALIZED"; + case EGL_BAD_ACCESS: + return "EGL_BAD_ACCESS"; + case EGL_BAD_ALLOC: + return "EGL_BAD_ALLOC"; + case EGL_BAD_ATTRIBUTE: + return "EGL_BAD_ATTRIBUTE"; + case EGL_BAD_CONFIG: + return "EGL_BAD_CONFIG"; + case EGL_BAD_CONTEXT: + return "EGL_BAD_CONTEXT"; + case EGL_BAD_CURRENT_SURFACE: + return "EGL_BAD_CURRENT_SURFACE"; + case EGL_BAD_MATCH: + return "EGL_BAD_MATCH"; + case EGL_BAD_NATIVE_PIXMAP: + return "EGL_BAD_NATIVE_PIXMAP"; + case EGL_BAD_NATIVE_WINDOW: + return "EGL_BAD_NATIVE_WINDOW"; + case EGL_BAD_PARAMETER: + return "EGL_BAD_PARAMETER"; + case EGL_BAD_SURFACE: + return "EGL_BAD_SURFACE"; + default: + return "unknown"; + } +}