3 * @brief X C Bindings video output module for VLC media player
5 /*****************************************************************************
6 * Copyright © 2009 Rémi Denis-Courmont
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.
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.
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 *****************************************************************************/
33 #include <vlc_common.h>
34 #include <vlc_plugin.h>
35 #include <vlc_vout_display.h>
36 #include <vlc_picture_pool.h>
41 static int Open (vlc_object_t
*);
42 static void Close (vlc_object_t
*);
48 set_shortname (N_("X11"))
49 set_description (N_("X11 video output (XCB)"))
50 set_category (CAT_VIDEO
)
51 set_subcategory (SUBCAT_VIDEO_VOUT
)
52 set_capability ("vout display", 100)
53 set_callbacks (Open
, Close
)
54 add_shortcut ("xcb-x11", "x11", "xid")
56 add_obsolete_bool ("x11-shm") /* obsoleted since 2.0.0 */
59 /* This must be large enough to absorb the server display jitter.
60 * But excessively large value is useless as direct rendering cannot be used
62 #define MAX_PICTURES (3)
64 struct vout_display_sys_t
66 xcb_connection_t
*conn
;
67 vout_window_t
*embed
; /* VLC window */
69 xcb_cursor_t cursor
; /* blank cursor */
70 xcb_window_t window
; /* drawable X window */
71 xcb_gcontext_t gc
; /* context to put images */
72 xcb_shm_seg_t seg_base
; /**< shared memory segment XID base */
73 bool visible
; /* whether to draw */
74 uint8_t depth
; /* useful bits per pixel */
76 picture_pool_t
*pool
; /* picture pool */
79 static picture_pool_t
*Pool (vout_display_t
*, unsigned);
80 static void Display (vout_display_t
*, picture_t
*, subpicture_t
*subpicture
);
81 static int Control (vout_display_t
*, int, va_list);
82 static void Manage (vout_display_t
*);
84 static void ResetPictures (vout_display_t
*);
86 static const xcb_depth_t
*FindDepth (const xcb_screen_t
*scr
,
89 xcb_depth_t
*d
= NULL
;
90 for (xcb_depth_iterator_t it
= xcb_screen_allowed_depths_iterator (scr
);
91 it
.rem
> 0 && d
== NULL
;
94 if (it
.data
->depth
== depth
)
103 * Probe the X server.
105 static int Open (vlc_object_t
*obj
)
107 vout_display_t
*vd
= (vout_display_t
*)obj
;
108 vout_display_sys_t
*sys
= malloc (sizeof (*sys
));
109 if (unlikely(sys
== NULL
))
115 /* Get window, connect to X server */
116 xcb_connection_t
*conn
;
117 const xcb_screen_t
*scr
;
118 uint16_t width
, height
;
119 sys
->embed
= XCB_parent_Create (vd
, &conn
, &scr
, &width
, &height
);
120 if (sys
->embed
== NULL
)
127 const xcb_setup_t
*setup
= xcb_get_setup (conn
);
129 /* Determine our pixel format */
130 video_format_t fmt_pic
;
131 xcb_visualid_t vid
= 0;
134 for (const xcb_format_t
*fmt
= xcb_setup_pixmap_formats (setup
),
135 *end
= fmt
+ xcb_setup_pixmap_formats_length (setup
);
139 if (fmt
->depth
<= sys
->depth
)
140 continue; /* no better than earlier format */
142 video_format_ApplyRotation(&fmt_pic
, &vd
->fmt
);
144 /* Check that the pixmap format is supported by VLC. */
148 if (fmt
->bits_per_pixel
!= 32)
150 fmt_pic
.i_chroma
= VLC_CODEC_ARGB
;
153 if (fmt
->bits_per_pixel
== 32)
154 fmt_pic
.i_chroma
= VLC_CODEC_RGB32
;
155 else if (fmt
->bits_per_pixel
== 24)
156 fmt_pic
.i_chroma
= VLC_CODEC_RGB24
;
161 if (fmt
->bits_per_pixel
!= 16)
163 fmt_pic
.i_chroma
= VLC_CODEC_RGB16
;
166 if (fmt
->bits_per_pixel
!= 16)
168 fmt_pic
.i_chroma
= VLC_CODEC_RGB15
;
171 if (fmt
->bits_per_pixel
!= 8)
173 fmt_pic
.i_chroma
= VLC_CODEC_RGB8
;
179 /* Byte sex is a non-issue for 8-bits. It can be worked around with
180 * RGB masks for 24-bits. Too bad for 15-bits and 16-bits. */
181 if (fmt
->bits_per_pixel
== 16 && setup
->image_byte_order
!= ORDER
)
184 /* Make sure the X server is sane */
185 assert (fmt
->bits_per_pixel
> 0);
186 if (unlikely(fmt
->scanline_pad
% fmt
->bits_per_pixel
))
189 /* Check that the selected screen supports this depth */
190 const xcb_depth_t
*d
= FindDepth (scr
, fmt
->depth
);
194 /* Find a visual type for the selected depth */
195 const xcb_visualtype_t
*vt
= xcb_depth_visuals (d
);
197 /* First try True Color class */
198 for (int i
= xcb_depth_visuals_length (d
); i
> 0; i
--)
200 if (vt
->_class
== XCB_VISUAL_CLASS_TRUE_COLOR
)
202 fmt_pic
.i_rmask
= vt
->red_mask
;
203 fmt_pic
.i_gmask
= vt
->green_mask
;
204 fmt_pic
.i_bmask
= vt
->blue_mask
;
207 msg_Dbg (vd
, "using X11 visual ID 0x%"PRIx32
, vid
);
208 sys
->depth
= fmt
->depth
;
209 msg_Dbg (vd
, " %"PRIu8
" bits depth", sys
->depth
);
210 msg_Dbg (vd
, " %"PRIu8
" bits per pixel", fmt
->bits_per_pixel
);
211 msg_Dbg (vd
, " %"PRIu8
" bits line pad", fmt
->scanline_pad
);
217 /* Then try Static Gray class */
220 vt
= xcb_depth_visuals (d
);
221 for (int i
= xcb_depth_visuals_length (d
); i
> 0 && !vid
; i
--)
223 if (vt
->_class
== XCB_VISUAL_CLASS_STATIC_GRAY
)
225 fmt_pic
.i_chroma
= VLC_CODEC_GREY
;
232 msg_Err (obj
, "no supported pixel format & visual");
236 /* Create colormap (needed to select non-default visual) */
238 if (vid
!= scr
->root_visual
)
240 cmap
= xcb_generate_id (conn
);
241 xcb_create_colormap (conn
, XCB_COLORMAP_ALLOC_NONE
,
242 cmap
, scr
->root
, vid
);
245 cmap
= scr
->default_colormap
;
248 sys
->window
= xcb_generate_id (conn
);
249 sys
->gc
= xcb_generate_id (conn
);
250 xcb_pixmap_t pixmap
= xcb_generate_id (conn
);
252 const uint32_t mask
=
255 XCB_CW_BORDER_PIXMAP
|
256 XCB_CW_BORDER_PIXEL
|
259 const uint32_t values
[] = {
260 /* XCB_CW_BACK_PIXMAP */
262 /* XCB_CW_BACK_PIXEL */
264 /* XCB_CW_BORDER_PIXMAP */
266 /* XCB_CW_BORDER_PIXEL */
268 /* XCB_CW_EVENT_MASK */
269 XCB_EVENT_MASK_VISIBILITY_CHANGE
,
270 /* XCB_CW_COLORMAP */
275 xcb_create_pixmap (conn
, sys
->depth
, pixmap
, scr
->root
, 1, 1);
276 c
= xcb_create_window_checked (conn
, sys
->depth
, sys
->window
,
277 sys
->embed
->handle
.xid
, 0, 0,
279 XCB_WINDOW_CLASS_INPUT_OUTPUT
,
281 xcb_map_window (conn
, sys
->window
);
282 /* Create graphic context (I wonder why the heck do we need this) */
283 xcb_create_gc (conn
, sys
->gc
, sys
->window
, 0, NULL
);
285 if (XCB_error_Check (vd
, conn
, "cannot create X11 window", c
))
288 msg_Dbg (vd
, "using X11 window %08"PRIx32
, sys
->window
);
289 msg_Dbg (vd
, "using X11 graphic context %08"PRIx32
, sys
->gc
);
291 sys
->cursor
= XCB_cursor_Create (conn
, scr
);
292 sys
->visible
= false;
293 if (XCB_shm_Check (obj
, conn
))
295 sys
->seg_base
= xcb_generate_id (conn
);
296 for (unsigned i
= 1; i
< MAX_PICTURES
; i
++)
297 xcb_generate_id (conn
);
302 /* Setup vout_display_t once everything is fine */
303 vd
->info
.has_pictures_invalid
= true;
304 vd
->info
.has_event_thread
= true;
309 vd
->display
= Display
;
310 vd
->control
= Control
;
314 bool is_fullscreen
= vd
->cfg
->is_fullscreen
;
315 if (is_fullscreen
&& vout_window_SetFullScreen (sys
->embed
, true))
316 is_fullscreen
= false;
317 vout_display_SendEventFullscreen (vd
, is_fullscreen
);
318 vout_display_SendEventDisplaySize (vd
, width
, height
, is_fullscreen
);
329 * Disconnect from the X server.
331 static void Close (vlc_object_t
*obj
)
333 vout_display_t
*vd
= (vout_display_t
*)obj
;
334 vout_display_sys_t
*sys
= vd
->sys
;
338 /* show the default cursor */
339 xcb_change_window_attributes (sys
->conn
, sys
->embed
->handle
.xid
, XCB_CW_CURSOR
,
340 &(uint32_t) { XCB_CURSOR_NONE
});
341 xcb_flush (sys
->conn
);
343 /* colormap, window and context are garbage-collected by X */
344 xcb_disconnect (sys
->conn
);
345 vout_display_DeleteWindow (vd
, sys
->embed
);
350 * Return a direct buffer
352 static picture_pool_t
*Pool (vout_display_t
*vd
, unsigned requested_count
)
354 vout_display_sys_t
*sys
= vd
->sys
;
355 (void)requested_count
;
360 vout_display_place_t place
;
362 vout_display_PlacePicture (&place
, &vd
->source
, vd
->cfg
, false);
365 const uint32_t values
[] = { place
.x
, place
.y
, place
.width
, place
.height
};
366 xcb_configure_window (sys
->conn
, sys
->window
,
367 XCB_CONFIG_WINDOW_X
| XCB_CONFIG_WINDOW_Y
|
368 XCB_CONFIG_WINDOW_WIDTH
| XCB_CONFIG_WINDOW_HEIGHT
,
371 picture_t
*pic
= picture_NewFromFormat (&vd
->fmt
);
375 assert (pic
->i_planes
== 1);
377 picture_resource_t res
= {
380 .i_lines
= pic
->p
->i_lines
,
381 .i_pitch
= pic
->p
->i_pitch
,
385 picture_Release (pic
);
388 picture_t
*pic_array
[MAX_PICTURES
];
389 const size_t size
= res
.p
->i_pitch
* res
.p
->i_lines
;
390 for (count
= 0; count
< MAX_PICTURES
; count
++)
392 xcb_shm_seg_t seg
= (sys
->seg_base
!= 0) ? (sys
->seg_base
+ count
) : 0;
394 if (XCB_picture_Alloc (vd
, &res
, size
, sys
->conn
, seg
))
396 pic_array
[count
] = XCB_picture_NewFromResource (&vd
->fmt
, &res
);
397 if (unlikely(pic_array
[count
] == NULL
))
400 xcb_shm_detach (sys
->conn
, seg
);
404 xcb_flush (sys
->conn
);
409 sys
->pool
= picture_pool_New (count
, pic_array
);
410 if (unlikely(sys
->pool
== NULL
))
412 picture_Release(pic_array
[--count
]);
417 * Sends an image to the X server.
419 static void Display (vout_display_t
*vd
, picture_t
*pic
, subpicture_t
*subpicture
)
421 vout_display_sys_t
*sys
= vd
->sys
;
422 xcb_shm_seg_t segment
= XCB_picture_GetSegment(pic
);
423 xcb_void_cookie_t ck
;
428 ck
= xcb_shm_put_image_checked (sys
->conn
, sys
->window
, sys
->gc
,
429 /* real width */ pic
->p
->i_pitch
/ pic
->p
->i_pixel_pitch
,
430 /* real height */ pic
->p
->i_lines
,
431 /* x */ vd
->fmt
.i_x_offset
,
432 /* y */ vd
->fmt
.i_y_offset
,
433 /* width */ vd
->fmt
.i_visible_width
,
434 /* height */ vd
->fmt
.i_visible_height
,
435 0, 0, sys
->depth
, XCB_IMAGE_FORMAT_Z_PIXMAP
,
439 const size_t offset
= vd
->fmt
.i_y_offset
* pic
->p
->i_pitch
;
440 const unsigned lines
= pic
->p
->i_lines
- vd
->fmt
.i_y_offset
;
442 ck
= xcb_put_image_checked (sys
->conn
, XCB_IMAGE_FORMAT_Z_PIXMAP
,
443 sys
->window
, sys
->gc
,
444 pic
->p
->i_pitch
/ pic
->p
->i_pixel_pitch
,
445 lines
, -vd
->fmt
.i_x_offset
, 0, 0, sys
->depth
,
446 pic
->p
->i_pitch
* lines
, pic
->p
->p_pixels
+ offset
);
449 /* Wait for reply. This makes sure that the X server gets CPU time to
450 * display the picture. xcb_flush() is *not* sufficient: especially with
451 * shared memory the PUT requests are so short that many of them can fit in
452 * X11 socket output buffer before the kernel preempts VLC. */
453 xcb_generic_error_t
*e
= xcb_request_check (sys
->conn
, ck
);
456 msg_Dbg (vd
, "%s: X11 error %d", "cannot put image", e
->error_code
);
460 /* FIXME might be WAY better to wait in some case (be carefull with
461 * VOUT_DISPLAY_RESET_PICTURES if done) + does not work with
462 * vout_display wrapper. */
464 picture_Release (pic
);
468 static int Control (vout_display_t
*vd
, int query
, va_list ap
)
470 vout_display_sys_t
*sys
= vd
->sys
;
474 case VOUT_DISPLAY_CHANGE_FULLSCREEN
:
476 const vout_display_cfg_t
*c
= va_arg (ap
, const vout_display_cfg_t
*);
477 return vout_window_SetFullScreen (sys
->embed
, c
->is_fullscreen
);
480 case VOUT_DISPLAY_CHANGE_DISPLAY_SIZE
:
482 const vout_display_cfg_t
*p_cfg
=
483 (const vout_display_cfg_t
*)va_arg (ap
, const vout_display_cfg_t
*);
484 const bool is_forced
= (bool)va_arg (ap
, int);
487 { /* Changing the dimensions of the parent window takes place
488 * asynchronously (in the X server). Also it might fail or result
489 * in different dimensions than requested. Request the size change
490 * and return a failure since the size is not (yet) changed.
491 * If the change eventually succeeds, HandleParentStructure()
492 * will trigger a non-forced display size change later. */
493 vout_window_SetSize (sys
->embed
, p_cfg
->display
.width
,
494 p_cfg
->display
.height
);
498 vout_display_place_t place
;
499 vout_display_PlacePicture (&place
, &vd
->source
, p_cfg
, false);
501 if (place
.width
!= vd
->fmt
.i_visible_width
||
502 place
.height
!= vd
->fmt
.i_visible_height
)
504 vout_display_SendEventPicturesInvalid (vd
);
508 /* Move the picture within the window */
509 const uint32_t values
[] = { place
.x
, place
.y
};
510 xcb_configure_window (sys
->conn
, sys
->window
,
511 XCB_CONFIG_WINDOW_X
| XCB_CONFIG_WINDOW_Y
,
515 case VOUT_DISPLAY_CHANGE_WINDOW_STATE
:
517 unsigned state
= va_arg (ap
, unsigned);
518 return vout_window_SetState (sys
->embed
, state
);
521 case VOUT_DISPLAY_CHANGE_ZOOM
:
522 case VOUT_DISPLAY_CHANGE_DISPLAY_FILLED
:
523 case VOUT_DISPLAY_CHANGE_SOURCE_ASPECT
:
524 case VOUT_DISPLAY_CHANGE_SOURCE_CROP
:
525 /* I am not sure it is always necessary, but it is way simpler ... */
526 vout_display_SendEventPicturesInvalid (vd
);
529 case VOUT_DISPLAY_RESET_PICTURES
:
533 vout_display_place_t place
;
534 vout_display_PlacePicture (&place
, &vd
->source
, vd
->cfg
, false);
537 video_format_ApplyRotation(&src
, &vd
->source
);
539 vd
->fmt
.i_width
= src
.i_width
* place
.width
/ src
.i_visible_width
;
540 vd
->fmt
.i_height
= src
.i_height
* place
.height
/ src
.i_visible_height
;
542 vd
->fmt
.i_visible_width
= place
.width
;
543 vd
->fmt
.i_visible_height
= place
.height
;
544 vd
->fmt
.i_x_offset
= src
.i_x_offset
* place
.width
/ src
.i_visible_width
;
545 vd
->fmt
.i_y_offset
= src
.i_y_offset
* place
.height
/ src
.i_visible_height
;
549 /* Hide the mouse. It will be send when
550 * vout_display_t::info.b_hide_mouse is false */
551 case VOUT_DISPLAY_HIDE_MOUSE
:
552 xcb_change_window_attributes (sys
->conn
, sys
->embed
->handle
.xid
,
553 XCB_CW_CURSOR
, &(uint32_t){ sys
->cursor
});
554 xcb_flush (sys
->conn
);
558 msg_Err (vd
, "Unknown request in XCB vout display");
563 static void Manage (vout_display_t
*vd
)
565 vout_display_sys_t
*sys
= vd
->sys
;
567 XCB_Manage (vd
, sys
->conn
, &sys
->visible
);
570 static void ResetPictures (vout_display_t
*vd
)
572 vout_display_sys_t
*sys
= vd
->sys
;
577 if (sys
->seg_base
!= 0)
578 for (unsigned i
= 0; i
< MAX_PICTURES
; i
++)
579 xcb_shm_detach (sys
->conn
, sys
->seg_base
+ i
);
581 picture_pool_Delete (sys
->pool
);