]> git.sesse.net Git - vlc/blob - modules/video_output/glx.c
android/opaque: check if subtitles_picture is not NULL
[vlc] / modules / video_output / glx.c
1 /**
2  * @file glx.c
3  * @brief GLX OpenGL extension module
4  */
5 /*****************************************************************************
6  * Copyright © 2010-2012 Rémi Denis-Courmont
7  *
8  * This program is free software; you can redistribute it and/or modify it
9  * under the terms of the GNU Lesser General Public License as published by
10  * the Free Software Foundation; either version 2.1 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  * GNU Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public License
19  * along with this program; if not, write to the Free Software Foundation,
20  * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
21  *****************************************************************************/
22
23 #ifdef HAVE_CONFIG_H
24 # include <config.h>
25 #endif
26
27 #include <stdlib.h>
28 #include <assert.h>
29 #include <GL/glx.h>
30 #include <GL/glxext.h>
31
32 #include <vlc_common.h>
33 #include <vlc_plugin.h>
34 #include <vlc_opengl.h>
35 #include <vlc_vout_window.h>
36 #include <vlc_xlib.h>
37
38 typedef struct vlc_gl_sys_t
39 {
40     Display *display;
41     GLXWindow win;
42     GLXContext ctx;
43 } vlc_gl_sys_t;
44
45 static int MakeCurrent (vlc_gl_t *gl)
46 {
47     vlc_gl_sys_t *sys = gl->sys;
48
49     if (!glXMakeContextCurrent (sys->display, sys->win, sys->win, sys->ctx))
50         return VLC_EGENERIC;
51     return VLC_SUCCESS;
52 }
53
54 static void ReleaseCurrent (vlc_gl_t *gl)
55 {
56     vlc_gl_sys_t *sys = gl->sys;
57
58     glXMakeContextCurrent (sys->display, None, None, NULL);
59 }
60
61 static void SwapBuffers (vlc_gl_t *gl)
62 {
63     vlc_gl_sys_t *sys = gl->sys;
64
65     glXSwapBuffers (sys->display, sys->win);
66 }
67
68 static void *GetSymbol(vlc_gl_t *gl, const char *procname)
69 {
70     (void) gl;
71 #ifdef GLX_ARB_get_proc_address
72     return glXGetProcAddressARB ((const GLubyte *)procname);
73 #else
74     return NULL;
75 #endif
76 }
77
78 static bool CheckGLX (vlc_object_t *vd, Display *dpy)
79 {
80     int major, minor;
81     bool ok = false;
82
83     if (!glXQueryVersion (dpy, &major, &minor))
84         msg_Dbg (vd, "GLX extension not available");
85     else
86     if (major != 1)
87         msg_Dbg (vd, "GLX extension version %d.%d unknown", major, minor);
88     else
89     if (minor < 3)
90         msg_Dbg (vd, "GLX extension version %d.%d too old", major, minor);
91     else
92     {
93         msg_Dbg (vd, "using GLX extension version %d.%d", major, minor);
94         ok = true;
95     }
96     return ok;
97 }
98
99 static bool CheckGLXext (Display *dpy, unsigned snum, const char *ext)
100 {
101     const char *exts = glXQueryExtensionsString (dpy, snum);
102     const size_t extlen = strlen (ext);
103
104     while (*exts)
105     {
106         exts += strspn (exts, " ");
107
108         size_t len = strcspn (exts, " ");
109         if (len == extlen && !memcmp (exts, ext, extlen))
110             return true;
111         exts += len;
112     }
113     return false;
114 }
115
116 static int Open (vlc_object_t *obj)
117 {
118     vlc_gl_t *gl = (vlc_gl_t *)obj;
119
120     if (gl->surface->type != VOUT_WINDOW_TYPE_XID || !vlc_xlib_init (obj))
121         return VLC_EGENERIC;
122
123     /* Initialize GLX display */
124     Display *dpy = XOpenDisplay (gl->surface->display.x11);
125     if (dpy == NULL)
126         return VLC_EGENERIC;
127
128     vlc_gl_sys_t *sys = malloc (sizeof (*sys));
129     if (unlikely(sys == NULL))
130     {
131         XCloseDisplay (dpy);
132         return VLC_ENOMEM;
133     }
134     gl->sys = sys;
135     sys->display = dpy;
136
137     if (!CheckGLX (obj, dpy))
138         goto error;
139
140     /* Determine our pixel format */
141     XWindowAttributes wa;
142     if (!XGetWindowAttributes (dpy, gl->surface->handle.xid, &wa))
143         goto error;
144
145     const int snum = XScreenNumberOfScreen (wa.screen);
146     const VisualID visual = XVisualIDFromVisual (wa.visual);
147     static const int attr[] = {
148         GLX_RED_SIZE, 5,
149         GLX_GREEN_SIZE, 5,
150         GLX_BLUE_SIZE, 5,
151         GLX_DOUBLEBUFFER, True,
152         GLX_X_RENDERABLE, True,
153         GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT,
154         None
155     };
156
157     int nelem;
158     GLXFBConfig *confs = glXChooseFBConfig (dpy, snum, attr, &nelem);
159     if (confs == NULL)
160     {
161         msg_Err (obj, "cannot choose GLX frame buffer configurations");
162         goto error;
163     }
164
165     GLXFBConfig conf;
166     bool found = false;
167     for (int i = 0; i < nelem && !found; i++)
168     {
169         conf = confs[i];
170
171         XVisualInfo *vi = glXGetVisualFromFBConfig (dpy, conf);
172         if (vi == NULL)
173             continue;
174
175         if (vi->visualid == visual)
176             found = true;
177         XFree (vi);
178     }
179     XFree (confs);
180
181     if (!found)
182     {
183         msg_Err (obj, "cannot match GLX frame buffer configuration");
184         goto error;
185     }
186
187     /* Create a drawing surface */
188     sys->win = glXCreateWindow (dpy, conf, gl->surface->handle.xid, NULL);
189     if (sys->win == None)
190     {
191         msg_Err (obj, "cannot create GLX window");
192         goto error;
193     }
194
195     /* Create an OpenGL context */
196     sys->ctx = glXCreateNewContext (dpy, conf, GLX_RGBA_TYPE, NULL, True);
197     if (sys->ctx == NULL)
198     {
199         glXDestroyWindow (dpy, sys->win);
200         msg_Err (obj, "cannot create GLX context");
201         goto error;
202     }
203
204     /* Initialize OpenGL callbacks */
205     gl->sys = sys;
206     gl->makeCurrent = MakeCurrent;
207     gl->releaseCurrent = ReleaseCurrent;
208     gl->resize = NULL;
209     gl->swap = SwapBuffers;
210     gl->getProcAddress = GetSymbol;
211     gl->lock = NULL;
212     gl->unlock = NULL;
213
214 #ifdef GLX_ARB_get_proc_address
215     bool is_swap_interval_set = false;
216
217     MakeCurrent (gl);
218 # ifdef GLX_SGI_swap_control
219     if (!is_swap_interval_set
220      && CheckGLXext (dpy, snum, "GLX_SGI_swap_control"))
221     {
222         PFNGLXSWAPINTERVALSGIPROC SwapIntervalSGI = (PFNGLXSWAPINTERVALSGIPROC)
223             glXGetProcAddressARB ((const GLubyte *)"glXSwapIntervalSGI");
224         assert (SwapIntervalSGI != NULL);
225         is_swap_interval_set = !SwapIntervalSGI (1);
226     }
227 # endif
228 # ifdef GLX_EXT_swap_control
229     if (!is_swap_interval_set
230      && CheckGLXext (dpy, snum, "GLX_EXT_swap_control"))
231     {
232         PFNGLXSWAPINTERVALEXTPROC SwapIntervalEXT = (PFNGLXSWAPINTERVALEXTPROC)
233             glXGetProcAddress ((const GLubyte *)"glXSwapIntervalEXT");
234         assert (SwapIntervalEXT != NULL);
235         SwapIntervalEXT (dpy, sys->win, 1);
236         is_swap_interval_set = true;
237     }
238 # endif
239     ReleaseCurrent (gl);
240 #endif
241
242     return VLC_SUCCESS;
243
244 error:
245     XCloseDisplay (dpy);
246     free (sys);
247     return VLC_EGENERIC;
248 }
249
250 static void Close (vlc_object_t *obj)
251 {
252     vlc_gl_t *gl = (vlc_gl_t *)obj;
253     vlc_gl_sys_t *sys = gl->sys;
254     Display *dpy = sys->display;
255
256     glXDestroyContext (dpy, sys->ctx);
257     glXDestroyWindow (dpy, sys->win);
258     XCloseDisplay (dpy);
259     free (sys);
260 }
261
262 vlc_module_begin ()
263     set_shortname (N_("GLX"))
264     set_description (N_("GLX extension for OpenGL"))
265     set_category (CAT_VIDEO)
266     set_subcategory (SUBCAT_VIDEO_VOUT)
267     set_capability ("opengl", 20)
268     set_callbacks (Open, Close)
269 vlc_module_end ()