3 * @brief GLX video output module for VLC media player
5 /*****************************************************************************
6 * Copyright © 2004 the VideoLAN team
7 * Copyright © 2009 Rémi Denis-Courmont
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License
11 * as published by the Free Software Foundation; either version 2
12 * of the License, or (at your option) any later version.
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public
20 * License along with this library; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
22 ****************************************************************************/
32 #include <X11/Xlib-xcb.h>
35 #include <vlc_common.h>
36 #include <vlc_plugin.h>
38 #include <vlc_vout_display.h>
39 #include <vlc_vout_opengl.h>
40 #include "../opengl.h"
44 static int Open (vlc_object_t
*);
45 static void Close (vlc_object_t
*);
51 set_shortname (N_("GLX"))
52 set_description (N_("GLX video output (XCB)"))
53 set_category (CAT_VIDEO
)
54 set_subcategory (SUBCAT_VIDEO_VOUT
)
55 set_capability ("vout display", 50)
56 set_callbacks (Open
, Close
)
58 add_shortcut ("xcb-glx", "glx", "opengl", "xid")
61 struct vout_display_sys_t
63 Display
*display
; /* Xlib instance */
64 vout_window_t
*embed
; /* VLC window (when windowed) */
66 xcb_cursor_t cursor
; /* blank cursor */
67 xcb_window_t window
; /* drawable X window */
68 xcb_window_t glwin
; /* GLX window */
69 bool visible
; /* whether to draw */
70 bool v1_3
; /* whether GLX >= 1.3 is available */
74 vout_display_opengl_t vgl
;
75 picture_pool_t
*pool
; /* picture pool */
78 static picture_pool_t
*Pool (vout_display_t
*, unsigned);
79 static void PictureRender (vout_display_t
*, picture_t
*);
80 static void PictureDisplay (vout_display_t
*, picture_t
*);
81 static int Control (vout_display_t
*, int, va_list);
82 static void Manage (vout_display_t
*);
84 static void SwapBuffers (vout_opengl_t
*gl
);
86 static vout_window_t
*MakeWindow (vout_display_t
*vd
)
88 vout_window_cfg_t wnd_cfg
;
90 memset (&wnd_cfg
, 0, sizeof (wnd_cfg
));
91 wnd_cfg
.type
= VOUT_WINDOW_TYPE_XID
;
92 wnd_cfg
.x
= var_InheritInteger (vd
, "video-x");
93 wnd_cfg
.y
= var_InheritInteger (vd
, "video-y");
94 wnd_cfg
.width
= vd
->cfg
->display
.width
;
95 wnd_cfg
.height
= vd
->cfg
->display
.height
;
97 vout_window_t
*wnd
= vout_display_NewWindow (vd
, &wnd_cfg
);
99 msg_Err (vd
, "parent window not available");
103 static const xcb_screen_t
*
104 FindWindow (vout_display_t
*vd
, xcb_connection_t
*conn
,
105 unsigned *restrict pnum
, uint8_t *restrict pdepth
,
106 uint16_t *restrict pwidth
, uint16_t *restrict pheight
)
108 vout_display_sys_t
*sys
= vd
->sys
;
110 xcb_get_geometry_reply_t
*geo
=
111 xcb_get_geometry_reply (conn
,
112 xcb_get_geometry (conn
, sys
->embed
->handle
.xid
), NULL
);
115 msg_Err (vd
, "parent window not valid");
119 xcb_window_t root
= geo
->root
;
120 *pdepth
= geo
->depth
;
121 *pwidth
= geo
->width
;
122 *pheight
= geo
->height
;
125 /* Find the selected screen */
126 const xcb_setup_t
*setup
= xcb_get_setup (conn
);
127 const xcb_screen_t
*screen
= NULL
;
130 for (xcb_screen_iterator_t i
= xcb_setup_roots_iterator (setup
);
132 xcb_screen_next (&i
))
134 if (i
.data
->root
== root
)
144 msg_Err (vd
, "parent window screen not found");
147 msg_Dbg (vd
, "using screen 0x%"PRIx32
" (number: %u)", root
, num
);
152 static bool CheckGLX (vout_display_t
*vd
, Display
*dpy
, bool *restrict pv13
)
157 if (!glXQueryVersion (dpy
, &major
, &minor
))
158 msg_Dbg (vd
, "GLX extension not available");
161 msg_Dbg (vd
, "GLX extension version %d.%d unknown", major
, minor
);
164 msg_Dbg (vd
, "GLX extension version %d.%d too old", major
, minor
);
167 msg_Dbg (vd
, "using GLX extension version %d.%d", major
, minor
);
174 static int CreateWindow (vout_display_t
*vd
, xcb_connection_t
*conn
,
175 uint_fast8_t depth
, xcb_visualid_t vid
,
176 uint_fast16_t width
, uint_fast16_t height
)
178 vout_display_sys_t
*sys
= vd
->sys
;
179 const uint32_t mask
= XCB_CW_EVENT_MASK
;
180 const uint32_t values
[] = {
181 /* XCB_CW_EVENT_MASK */
182 XCB_EVENT_MASK_VISIBILITY_CHANGE
,
184 xcb_void_cookie_t cc
, cm
;
186 cc
= xcb_create_window_checked (conn
, depth
, sys
->window
,
187 sys
->embed
->handle
.xid
, 0, 0,
189 XCB_WINDOW_CLASS_INPUT_OUTPUT
,
191 cm
= xcb_map_window_checked (conn
, sys
->window
);
192 if (CheckError (vd
, conn
, "cannot create X11 window", cc
)
193 || CheckError (vd
, conn
, "cannot map X11 window", cm
))
196 msg_Dbg (vd
, "using X11 window %08"PRIx32
, sys
->window
);
201 * Probe the X server.
203 static int Open (vlc_object_t
*obj
)
205 if (!vlc_xlib_init (obj
))
208 vout_display_t
*vd
= (vout_display_t
*)obj
;
209 vout_display_sys_t
*sys
= malloc (sizeof (*sys
));
219 sys
->embed
= MakeWindow (vd
);
220 if (sys
->embed
== NULL
)
226 /* Connect to X server */
227 Display
*dpy
= XOpenDisplay (sys
->embed
->display
.x11
);
230 vout_display_DeleteWindow (vd
, sys
->embed
);
236 XSetEventQueueOwner (dpy
, XCBOwnsEventQueue
);
238 if (!CheckGLX (vd
, dpy
, &sys
->v1_3
))
241 xcb_connection_t
*conn
= XGetXCBConnection (dpy
);
243 RegisterMouseEvents (obj
, conn
, sys
->embed
->handle
.xid
);
245 /* Find window parameters */
248 uint16_t width
, height
;
249 const xcb_screen_t
*scr
= FindWindow (vd
, conn
, &snum
, &depth
,
254 sys
->window
= xcb_generate_id (conn
);
256 /* Determine our pixel format */
259 static const int attr
[] = {
263 GLX_DOUBLEBUFFER
, True
,
264 GLX_X_RENDERABLE
, True
,
265 GLX_DRAWABLE_TYPE
, GLX_WINDOW_BIT
,
268 xcb_get_window_attributes_reply_t
*wa
=
269 xcb_get_window_attributes_reply (conn
,
270 xcb_get_window_attributes (conn
, sys
->embed
->handle
.xid
),
274 xcb_visualid_t visual
= wa
->visual
;
278 GLXFBConfig
*confs
= glXChooseFBConfig (dpy
, snum
, attr
, &nelem
);
281 msg_Err (vd
, "no GLX frame bufer configurations");
288 for (int i
= 0; i
< nelem
&& !found
; i
++)
292 XVisualInfo
*vi
= glXGetVisualFromFBConfig (dpy
, conf
);
296 if (vi
->visualid
== visual
)
304 msg_Err (vd
, "no matching GLX frame buffer configuration");
309 if (!CreateWindow (vd
, conn
, depth
, 0 /* ??? */, width
, height
))
310 sys
->glwin
= glXCreateWindow (dpy
, conf
, sys
->window
, NULL
);
311 if (sys
->glwin
== None
)
313 msg_Err (vd
, "cannot create GLX window");
317 /* Create an OpenGL context */
318 sys
->ctx
= glXCreateNewContext (dpy
, conf
, GLX_RGBA_TYPE
, NULL
,
320 if (sys
->ctx
== NULL
)
322 msg_Err (vd
, "cannot create GLX context");
326 if (!glXMakeContextCurrent (dpy
, sys
->glwin
, sys
->glwin
, sys
->ctx
))
339 XVisualInfo
*vi
= glXChooseVisual (dpy
, snum
, attr
);
342 msg_Err (vd
, "cannot find GLX 1.2 visual" );
345 msg_Dbg (vd
, "using GLX visual ID 0x%"PRIx32
, (uint32_t)vi
->visualid
);
347 if (CreateWindow (vd
, conn
, depth
, 0 /* ??? */, width
, height
) == 0)
348 sys
->ctx
= glXCreateContext (dpy
, vi
, 0, True
);
350 if (sys
->ctx
== NULL
)
352 msg_Err (vd
, "cannot create GLX context");
356 if (glXMakeCurrent (dpy
, sys
->window
, sys
->ctx
) == False
)
358 sys
->glwin
= sys
->window
;
361 /* Initialize common OpenGL video display */
363 sys
->gl
.unlock
= NULL
;
364 sys
->gl
.swap
= SwapBuffers
;
367 if (vout_display_opengl_Init (&sys
->vgl
, &vd
->fmt
, &sys
->gl
))
373 sys
->cursor
= CreateBlankCursor (conn
, scr
);
374 sys
->visible
= false;
377 vout_display_info_t info
= vd
->info
;
378 info
.has_pictures_invalid
= false;
379 info
.has_event_thread
= true;
381 /* Setup vout_display_t once everything is fine */
385 vd
->prepare
= PictureRender
;
386 vd
->display
= PictureDisplay
;
387 vd
->control
= Control
;
391 bool is_fullscreen
= vd
->cfg
->is_fullscreen
;
392 if (is_fullscreen
&& vout_window_SetFullScreen (sys
->embed
, true))
393 is_fullscreen
= false;
394 vout_display_SendEventFullscreen (vd
, is_fullscreen
);
395 vout_display_SendEventDisplaySize (vd
, width
, height
, is_fullscreen
);
406 * Disconnect from the X server.
408 static void Close (vlc_object_t
*obj
)
410 vout_display_t
*vd
= (vout_display_t
*)obj
;
411 vout_display_sys_t
*sys
= vd
->sys
;
412 Display
*dpy
= sys
->display
;
414 if (sys
->gl
.sys
!= NULL
)
415 vout_display_opengl_Clean (&sys
->vgl
);
417 if (sys
->ctx
!= NULL
)
420 glXMakeContextCurrent (dpy
, None
, None
, NULL
);
422 glXMakeCurrent (dpy
, None
, NULL
);
423 glXDestroyContext (dpy
, sys
->ctx
);
425 glXDestroyWindow (dpy
, sys
->glwin
);
428 /* show the default cursor */
429 xcb_change_window_attributes (XGetXCBConnection (sys
->display
),
430 sys
->embed
->handle
.xid
, XCB_CW_CURSOR
,
431 &(uint32_t) { XCB_CURSOR_NONE
});
432 xcb_flush (XGetXCBConnection (sys
->display
));
435 vout_display_DeleteWindow (vd
, sys
->embed
);
439 static void SwapBuffers (vout_opengl_t
*gl
)
441 vout_display_sys_t
*sys
= gl
->sys
;
443 glXSwapBuffers (sys
->display
, sys
->glwin
);
447 * Return a direct buffer
449 static picture_pool_t
*Pool (vout_display_t
*vd
, unsigned requested_count
)
451 vout_display_sys_t
*sys
= vd
->sys
;
452 (void)requested_count
;
455 sys
->pool
= vout_display_opengl_GetPool (&sys
->vgl
);
459 static void PictureRender (vout_display_t
*vd
, picture_t
*pic
)
461 vout_display_sys_t
*sys
= vd
->sys
;
463 vout_display_opengl_Prepare (&sys
->vgl
, pic
);
466 static void PictureDisplay (vout_display_t
*vd
, picture_t
*pic
)
468 vout_display_sys_t
*sys
= vd
->sys
;
470 vout_display_opengl_Display (&sys
->vgl
, &vd
->source
);
471 picture_Release (pic
);
474 static int Control (vout_display_t
*vd
, int query
, va_list ap
)
476 vout_display_sys_t
*sys
= vd
->sys
;
480 case VOUT_DISPLAY_CHANGE_FULLSCREEN
:
482 const vout_display_cfg_t
*c
= va_arg (ap
, const vout_display_cfg_t
*);
483 return vout_window_SetFullScreen (sys
->embed
, c
->is_fullscreen
);
486 case VOUT_DISPLAY_CHANGE_WINDOW_STATE
:
488 unsigned state
= va_arg (ap
, unsigned);
489 return vout_window_SetState (sys
->embed
, state
);
492 case VOUT_DISPLAY_CHANGE_DISPLAY_SIZE
:
493 case VOUT_DISPLAY_CHANGE_DISPLAY_FILLED
:
494 case VOUT_DISPLAY_CHANGE_ZOOM
:
495 case VOUT_DISPLAY_CHANGE_SOURCE_ASPECT
:
496 case VOUT_DISPLAY_CHANGE_SOURCE_CROP
:
498 xcb_connection_t
*conn
= XGetXCBConnection (sys
->display
);
499 const vout_display_cfg_t
*cfg
;
500 const video_format_t
*source
;
501 bool is_forced
= false;
503 if (query
== VOUT_DISPLAY_CHANGE_SOURCE_ASPECT
504 || query
== VOUT_DISPLAY_CHANGE_SOURCE_CROP
)
506 source
= (const video_format_t
*)va_arg (ap
, const video_format_t
*);
511 source
= &vd
->source
;
512 cfg
= (const vout_display_cfg_t
*)va_arg (ap
, const vout_display_cfg_t
*);
513 if (query
== VOUT_DISPLAY_CHANGE_DISPLAY_SIZE
)
514 is_forced
= (bool)va_arg (ap
, int);
518 if (query
== VOUT_DISPLAY_CHANGE_DISPLAY_SIZE
520 && (cfg
->display
.width
!= vd
->cfg
->display
.width
521 ||cfg
->display
.height
!= vd
->cfg
->display
.height
)
522 && vout_window_SetSize (sys
->embed
,
523 cfg
->display
.width
, cfg
->display
.height
))
526 vout_display_place_t place
;
527 vout_display_PlacePicture (&place
, source
, cfg
, false);
529 /* Move the picture within the window */
530 const uint32_t values
[] = { place
.x
, place
.y
,
531 place
.width
, place
.height
, };
532 xcb_void_cookie_t ck
=
533 xcb_configure_window_checked (conn
, sys
->window
,
534 XCB_CONFIG_WINDOW_X
| XCB_CONFIG_WINDOW_Y
535 | XCB_CONFIG_WINDOW_WIDTH
| XCB_CONFIG_WINDOW_HEIGHT
,
537 if (CheckError (vd
, conn
, "cannot resize X11 window", ck
))
540 glViewport (0, 0, place
.width
, place
.height
);
544 /* Hide the mouse. It will be send when
545 * vout_display_t::info.b_hide_mouse is false */
546 case VOUT_DISPLAY_HIDE_MOUSE
:
547 xcb_change_window_attributes (XGetXCBConnection (sys
->display
),
548 sys
->embed
->handle
.xid
,
549 XCB_CW_CURSOR
, &(uint32_t){ sys
->cursor
});
552 case VOUT_DISPLAY_GET_OPENGL
:
554 vout_opengl_t
**gl
= va_arg (ap
, vout_opengl_t
**);
559 case VOUT_DISPLAY_RESET_PICTURES
:
562 msg_Err (vd
, "Unknown request in XCB vout display");
567 static void Manage (vout_display_t
*vd
)
569 vout_display_sys_t
*sys
= vd
->sys
;
570 xcb_connection_t
*conn
= XGetXCBConnection (sys
->display
);
572 ManageEvent (vd
, conn
, &sys
->visible
);