1 /*
2  * Copyright (C) 2018-2019 the xine project
3  * Copyright (C) 2018-2019 Petri Hintukainen <phintuka@users.sourceforge.net>
4  *
5  * This file is part of xine, a free video player.
6  *
7  * xine is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * xine is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
20  *
21  * xine_egl.c, EGL bindings for OpenGL video output
22  *
23  */
24 
25 #ifdef HAVE_CONFIG_H
26 #include "config.h"
27 #endif
28 
29 #include "xine_gl.h"
30 
31 #include <stdlib.h>
32 
33 #include <xine.h> /* visual types */
34 #include <xine/xine_internal.h>
35 
36 #include "xine_gl_plugin.h"
37 
38 #if defined(XINE_EGL_USE_X11)
39 #elif defined(XINE_EGL_USE_WAYLAND)
40 #  include <wayland-egl.h>
41 #else
42 #  error EGL platform undefined
43 #endif
44 
45 #include <EGL/egl.h>
46 
47 #define EGL(_gl) xine_container_of(_gl, xine_egl_t, p.gl)
48 
49 typedef struct {
50   xine_gl_plugin_t p;
51 
52   EGLDisplay display;
53   EGLContext context;
54   EGLSurface surface;
55   EGLConfig  config;
56 
57 #if defined(XINE_EGL_USE_WAYLAND)
58   struct wl_egl_window *window;
59   int width, height;
60 #endif
61 
62   /* DEBUG */
63   int         is_current;
64 } xine_egl_t;
65 
_egl_error_str(EGLint error)66 static const char *_egl_error_str(EGLint error)
67 {
68   switch (error) {
69     case EGL_SUCCESS:             return "No error";
70     case EGL_NOT_INITIALIZED:     return "EGL not initialized or failed to initialize";
71     case EGL_BAD_ACCESS:          return "Resource inaccessible";
72     case EGL_BAD_ALLOC:           return "Cannot allocate resources";
73     case EGL_BAD_ATTRIBUTE:       return "Unrecognized attribute or attribute value";
74     case EGL_BAD_CONTEXT:         return "Invalid EGL context";
75     case EGL_BAD_CONFIG:          return "Invalid EGL frame buffer configuration";
76     case EGL_BAD_CURRENT_SURFACE: return "Current surface is no longer valid";
77     case EGL_BAD_DISPLAY:         return "Invalid EGL display";
78     case EGL_BAD_SURFACE:         return "Invalid surface";
79     case EGL_BAD_MATCH:           return "Inconsistent arguments";
80     case EGL_BAD_PARAMETER:       return "Invalid argument";
81     case EGL_BAD_NATIVE_PIXMAP:   return "Invalid native pixmap";
82     case EGL_BAD_NATIVE_WINDOW:   return "Invalid native window";
83     case EGL_CONTEXT_LOST:        return "Context lost";
84   }
85   return "Unknown error ";
86 }
87 
_egl_log_error(xine_t * xine,const char * msg)88 static inline void _egl_log_error(xine_t *xine, const char *msg)
89 {
90   EGLint error = eglGetError();
91   xprintf(xine, XINE_VERBOSITY_LOG, "egl: %s : %s (%d)\n",
92           msg, _egl_error_str(error), error);
93 }
94 
_egl_make_current(xine_gl_t * gl)95 static int _egl_make_current(xine_gl_t *gl)
96 {
97   xine_egl_t *egl = EGL(gl);
98   int result;
99 
100   _x_assert(!egl->is_current);
101 
102   result = eglMakeCurrent(egl->display, egl->surface, egl->surface, egl->context);
103   if (!result) {
104     _egl_log_error(egl->p.xine, "eglMakeCurrent() failed");
105     return 0;
106   }
107 
108   egl->is_current = 1;
109   return result;
110 }
111 
_egl_release_current(xine_gl_t * gl)112 static void _egl_release_current(xine_gl_t *gl)
113 {
114   xine_egl_t *egl = EGL(gl);
115 
116   _x_assert(egl->is_current);
117 
118   eglMakeCurrent(egl->display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
119   egl->is_current = 0;
120 }
121 
_egl_swap_buffers(xine_gl_t * gl)122 static void _egl_swap_buffers(xine_gl_t *gl)
123 {
124   xine_egl_t *egl = EGL(gl);
125 
126   eglSwapBuffers(egl->display, egl->surface);
127 }
128 
_egl_set_native_window(xine_gl_t * gl,void * drawable)129 static void _egl_set_native_window(xine_gl_t *gl, void *drawable)
130 {
131   xine_egl_t *egl = EGL(gl);
132   EGLNativeWindowType window;
133 
134   _x_assert(!egl->is_current);
135 
136   eglDestroySurface (egl->display, egl->surface);
137 #if defined(XINE_EGL_USE_X11)
138   window = (intptr_t)drawable;
139 #elif defined(XINE_EGL_USE_WAYLAND)
140   wl_egl_window_destroy(egl->window);
141   window = egl->window = wl_egl_window_create(drawable, egl->width, egl->height);
142 #endif
143   egl->surface = eglCreateWindowSurface(egl->display, egl->config, window, NULL);
144 
145   if (egl->surface == EGL_NO_SURFACE) {
146     _egl_log_error(egl->p.xine, "eglCreateWindowSurface() failed");
147   }
148 }
149 
_egl_resize(xine_gl_t * gl,int w,int h)150 static void _egl_resize(xine_gl_t *gl, int w, int h)
151 {
152 #if defined(XINE_EGL_USE_WAYLAND)
153   xine_egl_t *egl = EGL(gl);
154   wl_egl_window_resize(egl->window, w, h, 0, 0);
155   egl->width = w;
156   egl->height = h;
157 #else
158   (void)gl;
159   (void)w;
160   (void)h;
161 #endif
162 }
163 
_egl_init(xine_egl_t * egl,EGLNativeDisplayType native_display)164 static int _egl_init(xine_egl_t *egl, EGLNativeDisplayType native_display)
165 {
166   static const EGLint attributes[] = {
167     EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
168     EGL_RED_SIZE, 8,
169     EGL_GREEN_SIZE, 8,
170     EGL_BLUE_SIZE, 8,
171     EGL_DEPTH_SIZE, 16,
172     EGL_NONE
173   };
174   static const EGLint context_attribs[] = {
175     //EGL_CONTEXT_CLIENT_VERSION, 2,
176     EGL_NONE
177   };
178   EGLint num_config;
179 
180   egl->display = eglGetDisplay(native_display);
181   if (egl->display == EGL_NO_DISPLAY) {
182     _egl_log_error(egl->p.xine, "eglGetDisplay() failed");
183     return 0;
184   }
185 
186   if (!eglInitialize (egl->display, NULL, NULL)) {
187     _egl_log_error(egl->p.xine, "eglInitialize() failed");
188     goto fail;
189   }
190 
191   eglChooseConfig(egl->display, attributes, &egl->config, 1, &num_config);
192 
193   if (!eglBindAPI (EGL_OPENGL_API)) {
194     _egl_log_error(egl->p.xine, "OpenGL API unavailable");
195     goto fail;
196   }
197 
198   egl->context = eglCreateContext(egl->display, egl->config, EGL_NO_CONTEXT, context_attribs);
199   if (egl->context == EGL_NO_CONTEXT) {
200     _egl_log_error(egl->p.xine, "eglCreateContext() failed");
201     goto fail;
202   }
203 
204   return 1;
205 
206  fail:
207   eglTerminate(egl->display);
208   return 0;
209 }
210 
_egl_dispose(xine_gl_t * gl)211 static void _egl_dispose(xine_gl_t *gl)
212 {
213   xine_egl_t *egl = EGL(gl);
214 
215   lprintf("Destroying egl context %p\n", (void*)egl->context);
216 
217   _x_assert(!egl->is_current);
218 
219   if (egl->is_current) {
220     eglMakeCurrent(egl->display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
221   }
222 
223   eglDestroySurface (egl->display, egl->surface);
224 #if defined(XINE_EGL_USE_WAYLAND)
225   wl_egl_window_destroy(egl->window);
226 #endif
227   eglDestroyContext(egl->display, egl->context);
228   eglTerminate(egl->display);
229   free(egl);
230 }
231 
232 /*
233  * xine module
234  */
235 
_module_dispose(xine_module_t * module)236 static void _module_dispose(xine_module_t *module)
237 {
238   xine_egl_t *egl = xine_container_of(module, xine_egl_t, p.module);
239 
240   _egl_dispose(&egl->p.gl);
241 }
242 
_egl_get_instance(xine_module_class_t * class_gen,const void * data)243 static xine_module_t *_egl_get_instance(xine_module_class_t *class_gen, const void *data)
244 {
245   const gl_plugin_params_t *params = data;
246   EGLNativeWindowType native_window;
247   xine_egl_t *egl;
248 
249 #if defined(XINE_EGL_USE_X11)
250   const x11_visual_t *vis = params->visual;
251   _x_assert(params->visual_type == XINE_VISUAL_TYPE_X11 ||
252             params->visual_type == XINE_VISUAL_TYPE_X11_2);
253 #elif defined(XINE_EGL_USE_WAYLAND)
254   const xine_wayland_visual_t *vis = params->visual;
255   _x_assert (params->visual_type == XINE_VISUAL_TYPE_WAYLAND);
256 #endif
257 
258   (void)class_gen;
259 
260   if (!(params->flags & XINE_GL_API_OPENGL)) {
261     return NULL;
262   }
263   _x_assert(params->visual);
264   _x_assert(vis->display);
265 
266   egl = calloc(1, sizeof(*egl));
267   if (!egl) {
268     return NULL;
269   }
270 
271   egl->p.module.dispose       = _module_dispose;
272 
273   egl->p.gl.make_current      = _egl_make_current;
274   egl->p.gl.release_current   = _egl_release_current;
275   egl->p.gl.swap_buffers      = _egl_swap_buffers;
276   egl->p.gl.resize            = _egl_resize;
277   egl->p.gl.set_native_window = _egl_set_native_window;
278   egl->p.gl.dispose           = NULL;
279 
280   egl->p.xine = params->xine;
281 
282   if (!_egl_init(egl, vis->display)) {
283     free(egl);
284     return NULL;
285   }
286 
287 #if defined(XINE_EGL_USE_X11)
288   native_window       = vis->d;
289 #elif defined(XINE_EGL_USE_WAYLAND)
290   egl->width = 720;
291   egl->height = 576;
292   native_window = wl_egl_window_create(vis->surface, egl->width, egl->height);
293   egl->window = native_window;
294   egl->surface = vis->surface;
295 #endif
296 
297   egl->surface = eglCreateWindowSurface(egl->display, egl->config, native_window, NULL);
298   if (egl->surface == EGL_NO_SURFACE) {
299     _egl_log_error(egl->p.xine, "eglCreateWindowSurface() failed");
300     goto fail;
301   }
302 
303   return &egl->p.module;
304 
305  fail:
306   eglDestroyContext(egl->display, egl->context);
307   eglTerminate(egl->display);
308   free(egl);
309   return NULL;
310 }
311 
312 /*
313  * plugin
314  */
315 
egl_init_class(xine_t * xine,const void * params)316 static void *egl_init_class(xine_t *xine, const void *params)
317 {
318   static const xine_module_class_t xine_egl_class = {
319     .get_instance  = _egl_get_instance,
320 #if defined(XINE_EGL_USE_X11)
321     .description   = "GL provider (EGL/X11)",
322 #elif defined(XINE_EGL_USE_WAYLAND)
323     .description   = "GL provider (EGL/Wayland)",
324 #endif
325     .identifier    = "egl",
326     .dispose       = NULL,
327   };
328 
329   (void)xine;
330   (void)params;
331 
332   return (void *)&xine_egl_class;
333 }
334 
335 #if defined(XINE_EGL_USE_X11)
336 static const xine_module_info_t module_info_egl = {
337   .priority  = 9,
338   .type      = "gl_v1",
339   .sub_type  = XINE_VISUAL_TYPE_X11,
340 };
341 #elif defined(XINE_EGL_USE_WAYLAND)
342 static const xine_module_info_t module_info_egl = {
343   .priority  = 10,
344   .type      = "gl_v1",
345   .sub_type  = XINE_VISUAL_TYPE_WAYLAND,
346 };
347 #endif
348 
349 const plugin_info_t xine_plugin_info[] EXPORTED = {
350   /* type, API, "name", version, special_info, init_function */
351   { PLUGIN_XINE_MODULE, 1, "egl", XINE_VERSION_CODE, &module_info_egl, egl_init_class },
352   { PLUGIN_NONE, 0, NULL,  0, NULL, NULL },
353 };
354