glx.c 17.4 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
/**
 * @file glx.c
 * @brief GLX video output module for VLC media player
 */
/*****************************************************************************
 * Copyright © 2004 the VideoLAN team
 * Copyright © 2009 Rémi Denis-Courmont
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 ****************************************************************************/

#ifdef HAVE_CONFIG_H
# include <config.h>
#endif

#include <stdlib.h>
#include <assert.h>

#include <xcb/xcb.h>
#include <X11/Xlib-xcb.h>
#include <GL/glx.h>
34
#include <GL/glxext.h>
35
36
37

#include <vlc_common.h>
#include <vlc_plugin.h>
38
#include <vlc_xlib.h>
39
#include <vlc_vout_display.h>
40
#include <vlc_opengl.h>
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
#include "../opengl.h"

#include "xcb_vlc.h"

static int  Open (vlc_object_t *);
static void Close (vlc_object_t *);

/*
 * Module descriptor
 */
vlc_module_begin ()
    set_shortname (N_("GLX"))
    set_description (N_("GLX video output (XCB)"))
    set_category (CAT_VIDEO)
    set_subcategory (SUBCAT_VIDEO_VOUT)
56
    set_capability ("vout display", 150)
57
58
    set_callbacks (Open, Close)

59
    add_shortcut ("xcb-glx", "glx", "opengl", "xid")
60
61
62
63
64
65
66
67
68
vlc_module_end ()

struct vout_display_sys_t
{
    Display *display; /* Xlib instance */
    vout_window_t *embed; /* VLC window (when windowed) */

    xcb_cursor_t cursor; /* blank cursor */
    xcb_window_t window; /* drawable X window */
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
69
    xcb_window_t glwin; /* GLX window */
70
    bool visible; /* whether to draw */
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
71
    bool v1_3; /* whether GLX >= 1.3 is available */
72
73

    GLXContext ctx;
74
    vlc_gl_t gl;
75
    vout_display_opengl_t *vgl;
76
77
78
    picture_pool_t *pool; /* picture pool */
};

79
static picture_pool_t *Pool (vout_display_t *, unsigned);
80
81
static void PictureRender (vout_display_t *, picture_t *, subpicture_t *);
static void PictureDisplay (vout_display_t *, picture_t *, subpicture_t *);
82
83
84
static int Control (vout_display_t *, int, va_list);
static void Manage (vout_display_t *);

85
static void SwapBuffers (vlc_gl_t *gl);
86
static void *GetProcAddress (vlc_gl_t *gl, const char *);
87
88
89
90
91
92
93

static vout_window_t *MakeWindow (vout_display_t *vd)
{
    vout_window_cfg_t wnd_cfg;

    memset (&wnd_cfg, 0, sizeof (wnd_cfg));
    wnd_cfg.type = VOUT_WINDOW_TYPE_XID;
94
95
    wnd_cfg.x = var_InheritInteger (vd, "video-x");
    wnd_cfg.y = var_InheritInteger (vd, "video-y");
96
97
98
99
100
101
102
103
104
105
106
    wnd_cfg.width  = vd->cfg->display.width;
    wnd_cfg.height = vd->cfg->display.height;

    vout_window_t *wnd = vout_display_NewWindow (vd, &wnd_cfg);
    if (wnd == NULL)
        msg_Err (vd, "parent window not available");
    return wnd;
}

static const xcb_screen_t *
FindWindow (vout_display_t *vd, xcb_connection_t *conn,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
107
108
            unsigned *restrict pnum, uint8_t *restrict pdepth,
            uint16_t *restrict pwidth, uint16_t *restrict pheight)
109
110
111
112
113
{
    vout_display_sys_t *sys = vd->sys;

    xcb_get_geometry_reply_t *geo =
        xcb_get_geometry_reply (conn,
114
            xcb_get_geometry (conn, sys->embed->handle.xid), NULL);
115
116
117
118
119
120
121
122
    if (geo == NULL)
    {
        msg_Err (vd, "parent window not valid");
        return NULL;
    }

    xcb_window_t root = geo->root;
    *pdepth = geo->depth;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
123
124
    *pwidth = geo->width;
    *pheight = geo->height;
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
    free (geo);

    /* Find the selected screen */
    const xcb_setup_t *setup = xcb_get_setup (conn);
    const xcb_screen_t *screen = NULL;
    unsigned num = 0;

    for (xcb_screen_iterator_t i = xcb_setup_roots_iterator (setup);
         i.rem > 0;
         xcb_screen_next (&i))
    {
        if (i.data->root == root)
        {
            screen = i.data;
            break;
        }
        num++;
    }

    if (screen == NULL)
    {
        msg_Err (vd, "parent window screen not found");
        return NULL;
    }
    msg_Dbg (vd, "using screen 0x%"PRIx32 " (number: %u)", root, num);
    *pnum = num;
    return screen;
}

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
154
static bool CheckGLX (vout_display_t *vd, Display *dpy, bool *restrict pv13)
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
{
    int major, minor;
    bool ok = false;

    if (!glXQueryVersion (dpy, &major, &minor))
        msg_Dbg (vd, "GLX extension not available");
    else
    if (major != 1)
        msg_Dbg (vd, "GLX extension version %d.%d unknown", major, minor);
    else
    if (minor < 2)
        msg_Dbg (vd, "GLX extension version %d.%d too old", major, minor);
    else
    {
        msg_Dbg (vd, "using GLX extension version %d.%d", major, minor);
        ok = true;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
171
        *pv13 = minor >= 3;
172
173
174
175
    }
    return ok;
}

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
176
static int CreateWindow (vout_display_t *vd, xcb_connection_t *conn,
177
178
                         uint_fast8_t depth, xcb_visualid_t vid,
                         uint_fast16_t width, uint_fast16_t height)
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
179
180
181
182
183
184
185
186
187
188
{
    vout_display_sys_t *sys = vd->sys;
    const uint32_t mask = XCB_CW_EVENT_MASK;
    const uint32_t values[] = {
        /* XCB_CW_EVENT_MASK */
        XCB_EVENT_MASK_VISIBILITY_CHANGE,
    };
    xcb_void_cookie_t cc, cm;

    cc = xcb_create_window_checked (conn, depth, sys->window,
189
                                    sys->embed->handle.xid, 0, 0,
190
                                    width, height, 0,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
191
192
193
194
195
196
197
198
199
200
201
                                    XCB_WINDOW_CLASS_INPUT_OUTPUT,
                                    vid, mask, values);
    cm = xcb_map_window_checked (conn, sys->window);
    if (CheckError (vd, conn, "cannot create X11 window", cc)
     || CheckError (vd, conn, "cannot map X11 window", cm))
        return VLC_EGENERIC;

    msg_Dbg (vd, "using X11 window %08"PRIx32, sys->window);
    return VLC_SUCCESS;
}

202
203
204
205
206
/**
 * Probe the X server.
 */
static int Open (vlc_object_t *obj)
{
207
    if (!vlc_xlib_init (obj))
208
209
        return VLC_EGENERIC;

210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
    vout_display_t *vd = (vout_display_t *)obj;
    vout_display_sys_t *sys = malloc (sizeof (*sys));

    if (sys == NULL)
        return VLC_ENOMEM;

    vd->sys = sys;
    sys->pool = NULL;
    sys->gl.sys = NULL;

    /* Get window */
    sys->embed = MakeWindow (vd);
    if (sys->embed == NULL)
    {
        free (sys);
        return VLC_EGENERIC;
    }

    /* Connect to X server */
229
    Display *dpy = XOpenDisplay (sys->embed->display.x11);
230
231
232
233
234
235
236
237
238
239
    if (dpy == NULL)
    {
        vout_display_DeleteWindow (vd, sys->embed);
        free (sys);
        return VLC_EGENERIC;
    }
    sys->display = dpy;
    sys->ctx = NULL;
    XSetEventQueueOwner (dpy, XCBOwnsEventQueue);

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
240
    if (!CheckGLX (vd, dpy, &sys->v1_3))
241
242
        goto error;

243
244
    xcb_connection_t *conn = XGetXCBConnection (dpy);
    assert (conn);
245
    RegisterMouseEvents (obj, conn, sys->embed->handle.xid);
246
247
248
249

    /* Find window parameters */
    unsigned snum;
    uint8_t depth;
250
    uint16_t width, height;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
251
    const xcb_screen_t *scr = FindWindow (vd, conn, &snum, &depth,
252
                                          &width, &height);
253
254
255
    if (scr == NULL)
        goto error;

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
256
257
    sys->window = xcb_generate_id (conn);

258
    /* Determine our pixel format */
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
259
260
261
262
263
264
265
266
267
268
269
    if (sys->v1_3)
    {   /* GLX 1.3 */
        static const int attr[] = {
            GLX_RED_SIZE, 5,
            GLX_GREEN_SIZE, 5,
            GLX_BLUE_SIZE, 5,
            GLX_DOUBLEBUFFER, True,
            GLX_X_RENDERABLE, True,
            GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT,
            None };

270
271
        xcb_get_window_attributes_reply_t *wa =
            xcb_get_window_attributes_reply (conn,
272
273
                xcb_get_window_attributes (conn, sys->embed->handle.xid),
                NULL);
274
275
276
277
278
        if (wa == NULL)
            goto error;
        xcb_visualid_t visual = wa->visual;
        free (wa);

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
279
280
281
282
283
284
285
286
        int nelem;
        GLXFBConfig *confs = glXChooseFBConfig (dpy, snum, attr, &nelem);
        if (confs == NULL)
        {
            msg_Err (vd, "no GLX frame bufer configurations");
            goto error;
        }

287
288
289
290
291
292
        GLXFBConfig conf;
        bool found = false;

        for (int i = 0; i < nelem && !found; i++)
        {
            conf = confs[i];
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
293

294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
            XVisualInfo *vi = glXGetVisualFromFBConfig (dpy, conf);
            if (vi == NULL)
                continue;

            if (vi->visualid == visual)
                found = true;
            XFree (vi);
        }
        XFree (confs);

        if (!found)
        {
            msg_Err (vd, "no matching GLX frame buffer configuration");
            goto error;
        }

        sys->glwin = None;
        if (!CreateWindow (vd, conn, depth, 0 /* ??? */, width, height))
            sys->glwin = glXCreateWindow (dpy, conf, sys->window, NULL );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
313
314
315
316
317
318
319
        if (sys->glwin == None)
        {
            msg_Err (vd, "cannot create GLX window");
            goto error;
        }

        /* Create an OpenGL context */
320
        sys->ctx = glXCreateNewContext (dpy, conf, GLX_RGBA_TYPE, NULL,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
321
322
323
324
325
326
327
                                        True);
        if (sys->ctx == NULL)
        {
            msg_Err (vd, "cannot create GLX context");
            goto error;
        }

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
328
        if (!glXMakeContextCurrent (dpy, sys->glwin, sys->glwin, sys->ctx))
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
329
330
331
332
            goto error;
    }
    else
    {   /* GLX 1.2 */
333
334
335
336
337
338
        int attr[] = {
            GLX_RGBA,
            GLX_RED_SIZE, 5,
            GLX_GREEN_SIZE, 5,
            GLX_BLUE_SIZE, 5,
            GLX_DOUBLEBUFFER,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
339
            None };
340
341
342
343
344
345
346

        XVisualInfo *vi = glXChooseVisual (dpy, snum, attr);
        if (vi == NULL)
        {
            msg_Err (vd, "cannot find GLX 1.2 visual" );
            goto error;
        }
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
347
        msg_Dbg (vd, "using GLX visual ID 0x%"PRIx32, (uint32_t)vi->visualid);
348

349
        if (CreateWindow (vd, conn, depth, 0 /* ??? */, width, height) == 0)
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
350
            sys->ctx = glXCreateContext (dpy, vi, 0, True);
351
352
353
354
355
356
357
        XFree (vi);
        if (sys->ctx == NULL)
        {
            msg_Err (vd, "cannot create GLX context");
            goto error;
        }

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
358
        if (glXMakeCurrent (dpy, sys->window, sys->ctx) == False)
359
            goto error;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
360
        sys->glwin = sys->window;
361
362
    }

363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
    const char *glx_extensions = glXQueryExtensionsString (dpy, snum);

    bool is_swap_interval_set = false;
#ifdef GLX_SGI_swap_control
    if (strstr (glx_extensions, "GLX_SGI_swap_control")) {
        PFNGLXSWAPINTERVALSGIPROC SwapIntervalSGI = (PFNGLXSWAPINTERVALSGIPROC)GetProcAddress (NULL, "glXSwapIntervalSGI");
        if (!is_swap_interval_set && SwapIntervalSGI)
            is_swap_interval_set = !SwapIntervalSGI (1);
    }
#endif
#ifdef GLX_EXT_swap_control
    if (strstr (glx_extensions, "GLX_EXT_swap_control")) {
        PFNGLXSWAPINTERVALEXTPROC SwapIntervalEXT = (PFNGLXSWAPINTERVALEXTPROC)GetProcAddress (NULL, "glXSwapIntervalEXT");
        if (!is_swap_interval_set && SwapIntervalEXT)
            is_swap_interval_set = !SwapIntervalEXT (dpy, sys->glwin, 1);
    }
#endif

381
382
383
384
    /* Initialize common OpenGL video display */
    sys->gl.lock = NULL;
    sys->gl.unlock = NULL;
    sys->gl.swap = SwapBuffers;
385
    sys->gl.getProcAddress = GetProcAddress;
386
387
    sys->gl.sys = sys;

388
389
    const vlc_fourcc_t *subpicture_chromas;
    sys->vgl = vout_display_opengl_New (&vd->fmt, &subpicture_chromas, &sys->gl);
390
    if (!sys->vgl)
391
392
393
394
395
396
397
398
399
400
401
    {
        sys->gl.sys = NULL;
        goto error;
    }

    sys->cursor = CreateBlankCursor (conn, scr);
    sys->visible = false;

    /* */
    vout_display_info_t info = vd->info;
    info.has_pictures_invalid = false;
402
    info.has_event_thread = true;
403
    info.subpicture_chromas = subpicture_chromas;
404
405
406
407

    /* Setup vout_display_t once everything is fine */
    vd->info = info;

408
    vd->pool = Pool;
409
410
411
412
413
414
    vd->prepare = PictureRender;
    vd->display = PictureDisplay;
    vd->control = Control;
    vd->manage = Manage;

    /* */
415
416
417
418
419
    bool is_fullscreen = vd->cfg->is_fullscreen;
    if (is_fullscreen && vout_window_SetFullScreen (sys->embed, true))
        is_fullscreen = false;
    vout_display_SendEventFullscreen (vd, is_fullscreen);
    vout_display_SendEventDisplaySize (vd, width, height, is_fullscreen);
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438

    return VLC_SUCCESS;

error:
    Close (obj);
    return VLC_EGENERIC;
}


/**
 * Disconnect from the X server.
 */
static void Close (vlc_object_t *obj)
{
    vout_display_t *vd = (vout_display_t *)obj;
    vout_display_sys_t *sys = vd->sys;
    Display *dpy = sys->display;

    if (sys->gl.sys != NULL)
439
        vout_display_opengl_Delete (sys->vgl);
440
441

    if (sys->ctx != NULL)
442
    {
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
443
444
445
446
        if (sys->v1_3)
            glXMakeContextCurrent (dpy, None, None, NULL);
        else
            glXMakeCurrent (dpy, None, NULL);
447
        glXDestroyContext (dpy, sys->ctx);
448
449
        if (sys->v1_3)
            glXDestroyWindow (dpy, sys->glwin);
450
    }
451
452
453
454
455
456
457

    /* show the default cursor */
    xcb_change_window_attributes (XGetXCBConnection (sys->display),
                                  sys->embed->handle.xid, XCB_CW_CURSOR,
                                  &(uint32_t) { XCB_CURSOR_NONE });
    xcb_flush (XGetXCBConnection (sys->display));

458
459
460
461
462
    XCloseDisplay (dpy);
    vout_display_DeleteWindow (vd, sys->embed);
    free (sys);
}

463
static void SwapBuffers (vlc_gl_t *gl)
464
465
466
{
    vout_display_sys_t *sys = gl->sys;

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
467
    glXSwapBuffers (sys->display, sys->glwin);
468
469
}

470
471
472
473
474
475
static void *GetProcAddress (vlc_gl_t *gl, const char *name)
{
    (void)gl;
    return glXGetProcAddress ((const GLubyte *)name);
}

476
477
478
/**
 * Return a direct buffer
 */
479
static picture_pool_t *Pool (vout_display_t *vd, unsigned requested_count)
480
481
482
483
{
    vout_display_sys_t *sys = vd->sys;

    if (!sys->pool)
484
        sys->pool = vout_display_opengl_GetPool (sys->vgl, requested_count);
485
    return sys->pool;
486
487
}

488
static void PictureRender (vout_display_t *vd, picture_t *pic, subpicture_t *subpicture)
489
{
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
490
    vout_display_sys_t *sys = vd->sys;
491

492
    vout_display_opengl_Prepare (sys->vgl, pic, subpicture);
493
494
}

495
static void PictureDisplay (vout_display_t *vd, picture_t *pic, subpicture_t *subpicture)
496
497
498
{
    vout_display_sys_t *sys = vd->sys;

499
    vout_display_opengl_Display (sys->vgl, &vd->source);
500
    picture_Release (pic);
501
502
    if (subpicture)
        subpicture_Delete(subpicture);
503
504
505
506
507
508
509
510
511
512
513
514
515
516
}

static int Control (vout_display_t *vd, int query, va_list ap)
{
    vout_display_sys_t *sys = vd->sys;

    switch (query)
    {
    case VOUT_DISPLAY_CHANGE_FULLSCREEN:
    {
        const vout_display_cfg_t *c = va_arg (ap, const vout_display_cfg_t *);
        return vout_window_SetFullScreen (sys->embed, c->is_fullscreen);
    }

517
    case VOUT_DISPLAY_CHANGE_WINDOW_STATE:
518
    {
519
        unsigned state = va_arg (ap, unsigned);
520
        return vout_window_SetState (sys->embed, state);
521
522
523
524
525
526
527
    }

    case VOUT_DISPLAY_CHANGE_DISPLAY_SIZE:
    case VOUT_DISPLAY_CHANGE_DISPLAY_FILLED:
    case VOUT_DISPLAY_CHANGE_ZOOM:
    case VOUT_DISPLAY_CHANGE_SOURCE_ASPECT:
    case VOUT_DISPLAY_CHANGE_SOURCE_CROP:
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
    {
        xcb_connection_t *conn = XGetXCBConnection (sys->display);
        const vout_display_cfg_t *cfg;
        const video_format_t *source;
        bool is_forced = false;

        if (query == VOUT_DISPLAY_CHANGE_SOURCE_ASPECT
         || query == VOUT_DISPLAY_CHANGE_SOURCE_CROP)
        {
            source = (const video_format_t *)va_arg (ap, const video_format_t *);
            cfg = vd->cfg;
        }
        else
        {
            source = &vd->source;
            cfg = (const vout_display_cfg_t*)va_arg (ap, const vout_display_cfg_t *);
            if (query == VOUT_DISPLAY_CHANGE_DISPLAY_SIZE)
                is_forced = (bool)va_arg (ap, int);
        }

        /* */
        if (query == VOUT_DISPLAY_CHANGE_DISPLAY_SIZE
         && is_forced
         && (cfg->display.width  != vd->cfg->display.width
           ||cfg->display.height != vd->cfg->display.height)
         && vout_window_SetSize (sys->embed,
                                 cfg->display.width, cfg->display.height))
            return VLC_EGENERIC;

        vout_display_place_t place;
        vout_display_PlacePicture (&place, source, cfg, false);

        /* Move the picture within the window */
        const uint32_t values[] = { place.x, place.y,
                                    place.width, place.height, };
563
564
565
566
        xcb_void_cookie_t ck =
            xcb_configure_window_checked (conn, sys->window,
                            XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y
                          | XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
567
                              values);
568
569
570
571
        if (CheckError (vd, conn, "cannot resize X11 window", ck))
            return VLC_EGENERIC;

        glViewport (0, 0, place.width, place.height);
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
572
573
        return VLC_SUCCESS;
    }
574
575
576
577

    /* Hide the mouse. It will be send when
     * vout_display_t::info.b_hide_mouse is false */
    case VOUT_DISPLAY_HIDE_MOUSE:
578
579
580
581
    {
        xcb_connection_t *conn = XGetXCBConnection (sys->display);

        xcb_change_window_attributes (conn, sys->embed->handle.xid,
582
                                    XCB_CW_CURSOR, &(uint32_t){ sys->cursor });
583
        xcb_flush (conn);
584
        return VLC_SUCCESS;
585
    }
586
587
588

    case VOUT_DISPLAY_GET_OPENGL:
    {
589
        vlc_gl_t **gl = va_arg (ap, vlc_gl_t **);
590
591
592
593
        *gl = &sys->gl;
        return VLC_SUCCESS;
    }

594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
    case VOUT_DISPLAY_RESET_PICTURES:
        assert (0);
    default:
        msg_Err (vd, "Unknown request in XCB vout display");
        return VLC_EGENERIC;
    }
}

static void Manage (vout_display_t *vd)
{
    vout_display_sys_t *sys = vd->sys;
    xcb_connection_t *conn = XGetXCBConnection (sys->display);

    ManageEvent (vd, conn, &sys->visible);
}