xvideo.c 24 KB
Newer Older
1
2
3
4
5
6
7
8
9
/**
 * @file xvideo.c
 * @brief X C Bindings video output module for VLC media player
 */
/*****************************************************************************
 * 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
10
 * as published by the Free Software Foundation; either version 2
11
12
13
14
15
16
17
 * 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.
 *
18
 * You should have received a copy of the GNU General Public
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
 * 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 <xcb/shm.h>
#include <xcb/xv.h>

#include <vlc_common.h>
#include <vlc_plugin.h>
36
37
#include <vlc_vout_display.h>
#include <vlc_picture_pool.h>
38
39
40
41
42

#include "xcb_vlc.h"

#define DISPLAY_TEXT N_("X11 display")
#define DISPLAY_LONGTEXT N_( \
43
    "X11 hardware display to use. By default, VLC will " \
44
45
    "use the value of the DISPLAY environment variable.")

46
47
48
49
50
#define ADAPTOR_TEXT N_("XVideo adaptor number")
#define ADAPTOR_LONGTEXT N_( \
    "XVideo hardware adaptor to use. By default, VLC will " \
    "use the first functional adaptor.")

51
52
53
54
55
56
57
58
59
60
61
62
#define SHM_TEXT N_("Use shared memory")
#define SHM_LONGTEXT N_( \
    "Use shared memory to communicate between VLC and the X server.")

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

/*
 * Module descriptor
 */
vlc_module_begin ()
    set_shortname (N_("XVideo"))
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
63
    set_description (N_("XVideo output (XCB)"))
64
65
    set_category (CAT_VIDEO)
    set_subcategory (SUBCAT_VIDEO_VOUT)
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
66
    set_capability ("vout display", 155)
67
68
69
70
    set_callbacks (Open, Close)

    add_string ("x11-display", NULL, NULL,
                DISPLAY_TEXT, DISPLAY_LONGTEXT, true)
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
71
        add_deprecated_alias ("xvideo-display")
72
73
    add_integer ("xvideo-adaptor", -1, NULL,
                 ADAPTOR_TEXT, ADAPTOR_LONGTEXT, true)
74
    add_bool ("x11-shm", true, NULL, SHM_TEXT, SHM_LONGTEXT, true)
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
75
        add_deprecated_alias ("xvideo-shm")
76
    add_shortcut ("xcb-xv")
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
77
    add_shortcut ("xv")
78
    add_shortcut ("xvideo")
79
80
vlc_module_end ()

81
82
83
#define MAX_PICTURES (VOUT_MAX_PICTURES)

struct vout_display_sys_t
84
85
86
87
{
    xcb_connection_t *conn;
    vout_window_t *embed;/* VLC window */

88
    xcb_cursor_t cursor; /* blank cursor */
89
90
91
92
93
94
    xcb_window_t window; /* drawable X window */
    xcb_gcontext_t gc;   /* context to put images */
    xcb_xv_port_t port;  /* XVideo port */
    uint32_t id;         /* XVideo format */
    uint16_t width;      /* display width */
    uint16_t height;     /* display height */
95
    uint32_t data_size;  /* picture byte size (for non-SHM) */
96
    bool shm;            /* whether to use MIT-SHM */
97
    bool visible;        /* whether it makes sense to draw at all */
98

99
100
101
102
    xcb_xv_query_image_attributes_reply_t *att;
    picture_pool_t *pool; /* picture pool */
    picture_resource_t resource[MAX_PICTURES];
};
103

104
105
106
107
static picture_t *Get (vout_display_t *);
static void Display (vout_display_t *, picture_t *);
static int Control (vout_display_t *, int, va_list);
static void Manage (vout_display_t *);
108
109
110
111

/**
 * Check that the X server supports the XVideo extension.
 */
112
static bool CheckXVideo (vout_display_t *vd, xcb_connection_t *conn)
113
114
115
116
117
118
119
120
121
122
{
    xcb_xv_query_extension_reply_t *r;
    xcb_xv_query_extension_cookie_t ck = xcb_xv_query_extension (conn);
    bool ok = false;

    r = xcb_xv_query_extension_reply (conn, ck, NULL);
    if (r != NULL)
    {   /* We need XVideo 2.2 for PutImage */
        if ((r->major > 2) || (r->major == 2 && r->minor >= 2))
        {
123
            msg_Dbg (vd, "using XVideo extension v%"PRIu8".%"PRIu8,
124
125
126
127
                     r->major, r->minor);
            ok = true;
        }
        else
128
            msg_Dbg (vd, "XVideo extension too old (v%"PRIu8".%"PRIu8,
129
130
131
132
                     r->major, r->minor);
        free (r);
    }
    else
133
        msg_Dbg (vd, "XVideo extension not available");
134
135
136
    return ok;
}

137
static vlc_fourcc_t ParseFormat (vout_display_t *vd,
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
                                 const xcb_xv_image_format_info_t *restrict f)
{
    if (f->byte_order != ORDER && f->bpp != 8)
        return 0; /* Argh! */

    switch (f->type)
    {
      case XCB_XV_IMAGE_FORMAT_INFO_TYPE_RGB:
        switch (f->num_planes)
        {
          case 1:
            switch (f->bpp)
            {
              case 32:
                if (f->depth == 24)
153
                    return VLC_CODEC_RGB32;
154
155
156
                break;
              case 24:
                if (f->depth == 24)
157
                    return VLC_CODEC_RGB24;
158
159
160
                break;
              case 16:
                if (f->depth == 16)
161
                    return VLC_CODEC_RGB16;
162
                if (f->depth == 15)
163
                    return VLC_CODEC_RGB15;
164
165
166
                break;
              case 8:
                if (f->depth == 8)
167
                    return VLC_CODEC_RGB8;
168
169
170
171
                break;
            }
            break;
        }
172
        msg_Err (vd, "unknown XVideo RGB format %"PRIx32" (%.4s)",
173
                 f->id, f->guid);
174
        msg_Dbg (vd, " %"PRIu8" planes, %"PRIu8" bits/pixel, "
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
                 "depth %"PRIu8, f->num_planes, f->bpp, f->depth);
        break;

      case XCB_XV_IMAGE_FORMAT_INFO_TYPE_YUV:
        if (f->u_sample_bits != f->v_sample_bits
         || f->vhorz_u_period != f->vhorz_v_period
         || f->vvert_u_period != f->vvert_v_period
         || f->y_sample_bits != 8 || f->u_sample_bits != 8
         || f->vhorz_y_period != 1 || f->vvert_y_period != 1)
            goto bad;
        switch (f->num_planes)
        {
          case 1:
            switch (f->bpp)
            {
              /*untested: case 24:
                if (f->vhorz_u_period == 1 && f->vvert_u_period == 1)
192
                    return VLC_CODEC_I444;
193
194
195
196
197
                break;*/
              case 16:
                if (f->vhorz_u_period == 2 && f->vvert_u_period == 1)
                {
                    if (!strcmp ((const char *)f->vcomp_order, "YUYV"))
198
                        return VLC_CODEC_YUYV;
199
                    if (!strcmp ((const char *)f->vcomp_order, "UYVY"))
200
                        return VLC_CODEC_UYVY;
201
202
203
204
205
206
207
208
209
210
211
                }
                break;
            }
            break;
          case 3:
            switch (f->bpp)
            {
              case 12:
                if (f->vhorz_u_period == 2 && f->vvert_u_period == 2)
                {
                    if (!strcmp ((const char *)f->vcomp_order, "YVU"))
212
                        return VLC_CODEC_YV12;
213
                    if (!strcmp ((const char *)f->vcomp_order, "YUV"))
214
                        return VLC_CODEC_I420;
215
216
217
218
219
                }
            }
            break;
        }
    bad:
220
        msg_Err (vd, "unknown XVideo YUV format %"PRIx32" (%.4s)", f->id,
221
                 f->guid);
222
        msg_Dbg (vd, " %"PRIu8" planes, %"PRIu32" bits/pixel, "
223
224
                 "%"PRIu32"/%"PRIu32"/%"PRIu32" bits/sample", f->num_planes,
                 f->bpp, f->y_sample_bits, f->u_sample_bits, f->v_sample_bits);
225
        msg_Dbg (vd, " period: %"PRIu32"/%"PRIu32"/%"PRIu32"x"
226
227
228
                 "%"PRIu32"/%"PRIu32"/%"PRIu32,
                 f->vhorz_y_period, f->vhorz_u_period, f->vhorz_v_period,
                 f->vvert_y_period, f->vvert_u_period, f->vvert_v_period);
229
        msg_Warn (vd, " order: %.32s", f->vcomp_order);
230
231
232
233
234
235
        break;
    }
    return 0;
}


236
static const xcb_xv_image_format_info_t *
237
238
239
FindFormat (vout_display_t *vd,
            vlc_fourcc_t chroma, const video_format_t *fmt,
            xcb_xv_port_t port,
240
241
242
            const xcb_xv_list_image_formats_reply_t *list,
            xcb_xv_query_image_attributes_reply_t **restrict pa)
{
243
    xcb_connection_t *conn = vd->sys->conn;
244
245
    const xcb_xv_image_format_info_t *f, *end;

246
#ifndef XCB_XV_OLD
247
    f = xcb_xv_list_image_formats_format (list);
248
249
250
#else
    f = (xcb_xv_image_format_info_t *) (list + 1);
#endif
251
252
253
    end = f + xcb_xv_list_image_formats_format_length (list);
    for (; f < end; f++)
    {
254
        if (chroma != ParseFormat (vd, f))
255
256
            continue;

257
258
        /* VLC pads scanline to 16 pixels internally */
        unsigned width = (fmt->i_width + 15) & ~15;
259
        unsigned height = (fmt->i_height + 15) & ~15;
260
261
262
        xcb_xv_query_image_attributes_reply_t *i;
        i = xcb_xv_query_image_attributes_reply (conn,
            xcb_xv_query_image_attributes (conn, port, f->id,
263
                                           width, height), NULL);
264
265
266
        if (i == NULL)
            continue;

267
        if (i->width != width || i->height != height)
268
        {
269
270
            msg_Warn (vd, "incompatible size %ux%u -> %"PRIu32"x%"PRIu32,
                      fmt->i_width, fmt->i_height,
271
272
273
274
275
276
277
278
279
280
                      i->width, i->height);
            free (i);
            continue;
        }
        *pa = i;
        return f;
    }
    return NULL;
}

281
282
283
284
285
286
287
288
289
290
291
292
293
294

/**
 * Probe the X server.
 */
static int Open (vlc_object_t *obj)
{
    vout_display_t *vd = (vout_display_t *)obj;
    vout_display_sys_t *p_sys = malloc (sizeof (*p_sys));
    if (p_sys == NULL)
        return VLC_ENOMEM;

    vd->sys = p_sys;

    /* Connect to X */
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
295
296
    xcb_connection_t *conn = Connect (obj);
    if (conn == NULL)
297
298
299
300
    {
        free (p_sys);
        return VLC_EGENERIC;
    }
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
301
    p_sys->conn = conn;
302

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
303
    if (!CheckXVideo (vd, conn))
304
305
    {
        msg_Warn (vd, "Please enable XVideo 2.2 for faster video display");
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
306
        xcb_disconnect (conn);
307
308
309
310
311
        free (p_sys);
        return VLC_EGENERIC;
    }

    const xcb_screen_t *screen;
312
313
    uint8_t depth;
    p_sys->embed = GetWindow (vd, conn, &screen, &depth, &p_sys->shm);
314
315
    if (p_sys->embed == NULL)
    {
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
316
        xcb_disconnect (conn);
317
318
319
320
        free (p_sys);
        return VLC_EGENERIC;
    }

321
322
323
    /* */
    p_sys->att = NULL;
    p_sys->pool = NULL;
324
    p_sys->window = xcb_generate_id (conn);
325

326
    /* Cache adaptors infos */
327
328
329
330
    xcb_xv_query_adaptors_reply_t *adaptors =
        xcb_xv_query_adaptors_reply (conn,
            xcb_xv_query_adaptors (conn, p_sys->embed->handle.xid), NULL);
    if (adaptors == NULL)
331
332
        goto error;

333
334
    int forced_adaptor = var_CreateGetInteger (obj, "xvideo-adaptor");

335
336
337
    /* */
    video_format_t fmt = vd->fmt;
    bool found_adaptor = false;
338
339

    xcb_xv_adaptor_info_iterator_t it;
340
    for (it = xcb_xv_query_adaptors_info_iterator (adaptors);
341
         it.rem > 0 && !found_adaptor;
342
343
344
         xcb_xv_adaptor_info_next (&it))
    {
        const xcb_xv_adaptor_info_t *a = it.data;
345
        char *name;
346

347
348
349
350
351
352
        if (forced_adaptor != -1 && forced_adaptor != 0)
        {
            forced_adaptor--;
            continue;
        }

353
354
355
        if (!(a->type & XCB_XV_TYPE_IMAGE_MASK))
            continue;

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
356
357
358
        xcb_xv_list_image_formats_reply_t *r =
            xcb_xv_list_image_formats_reply (conn,
                xcb_xv_list_image_formats (conn, a->base_id), NULL);
359
360
361
        if (r == NULL)
            continue;

362
363
        /* Look for an image format */
        const xcb_xv_image_format_info_t *xfmt = NULL;
364
        const vlc_fourcc_t *chromas, chromas_default[] = {
365
            fmt.i_chroma,
366
            VLC_CODEC_RGB32,
367
            VLC_CODEC_RGB24,
368
            VLC_CODEC_RGB16,
369
            VLC_CODEC_RGB15,
370
            VLC_CODEC_YUYV,
Laurent Aimar's avatar
Laurent Aimar committed
371
            0
372
        };
Laurent Aimar's avatar
Laurent Aimar committed
373
374
375
376
377
        if (vlc_fourcc_IsYUV (fmt.i_chroma))
            chromas = vlc_fourcc_GetYUVFallback (fmt.i_chroma);
        else
            chromas = chromas_default;

378
        vlc_fourcc_t chroma;
379
        for (size_t i = 0; chromas[i]; i++)
380
        {
381
            chroma = chromas[i];
382
383
384
385
386
387
388
389
390
391

            /* Oink oink! */
            if ((chroma == VLC_CODEC_I420 || chroma == VLC_CODEC_YV12)
             && a->name_size >= 4
             && !memcmp ("OMAP", xcb_xv_adaptor_info_name (a), 4))
            {
                msg_Dbg (vd, "skipping slow I420 format");
                continue; /* OMAP framebuffer sucks at YUV 4:2:0 */
            }

392
            xfmt = FindFormat (vd, chroma, &fmt, a->base_id, r, &p_sys->att);
393
394
395
396
397
398
399
400
401
402
403
404
            if (xfmt != NULL)
            {
                p_sys->id = xfmt->id;
                fmt.i_chroma = chroma;
                if (xfmt->type == XCB_XV_IMAGE_FORMAT_INFO_TYPE_RGB)
                {
                    fmt.i_rmask = xfmt->red_mask;
                    fmt.i_gmask = xfmt->green_mask;
                    fmt.i_bmask = xfmt->blue_mask;
                }
                break;
            }
405
        }
406
        free (r);
407
        if (xfmt == NULL) /* No acceptable image formats */
408
            continue;
409

410
411
412
413
414
        /* Grab a port */
        for (unsigned i = 0; i < a->num_ports; i++)
        {
             xcb_xv_port_t port = a->base_id + i;
             xcb_xv_grab_port_reply_t *gr =
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
415
416
                 xcb_xv_grab_port_reply (conn,
                     xcb_xv_grab_port (conn, port, XCB_CURRENT_TIME), NULL);
417
418
419
420
421
422
             uint8_t result = gr ? gr->result : 0xff;

             free (gr);
             if (result == 0)
             {
                 p_sys->port = port;
423
                 goto grabbed_port;
424
425
426
             }
             msg_Dbg (vd, "cannot grab port %"PRIu32, port);
        }
427
        continue; /* No usable port */
428

429
    grabbed_port:
430
        /* Found port - initialize selected format */
431
432
433
434
435
436
        name = strndup (xcb_xv_adaptor_info_name (a), a->name_size);
        if (name != NULL)
        {
            msg_Dbg (vd, "using adaptor %s", name);
            free (name);
        }
437
438
        msg_Dbg (vd, "using port %"PRIu32, p_sys->port);
        msg_Dbg (vd, "using image format 0x%"PRIx32, p_sys->id);
439
440
441
442
443

        /* Look for an X11 visual, create a window */
        xcb_xv_format_t *f = xcb_xv_adaptor_info_formats (a);
        for (uint_fast16_t i = a->num_formats; i > 0; i--, f++)
        {
444
445
446
            if (f->depth != depth)
                continue; /* this would fail anyway */

447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
            const uint32_t mask =
                /* XCB_CW_EVENT_MASK */
                XCB_EVENT_MASK_VISIBILITY_CHANGE;
            xcb_void_cookie_t c;

            c = xcb_create_window_checked (conn, f->depth, p_sys->window,
                 p_sys->embed->handle.xid, 0, 0, 1, 1, 0,
                 XCB_WINDOW_CLASS_INPUT_OUTPUT, f->visual,
                 XCB_CW_EVENT_MASK, &mask);

            if (!CheckError (vd, conn, "cannot create X11 window", c))
            {
                msg_Dbg (vd, "using X11 visual ID 0x%"PRIx32
                         " (depth: %"PRIu8")", f->visual, f->depth);
                msg_Dbg (vd, "using X11 window 0x%08"PRIx32, p_sys->window);
                goto created_window;
            }
        }
        xcb_xv_ungrab_port (conn, p_sys->port, XCB_CURRENT_TIME);
        continue; /* No workable XVideo format (visual/depth) */

    created_window:
        found_adaptor = true;
        break;
471
    }
472
    free (adaptors);
473
474
475
476
477
    if (!found_adaptor)
    {
        msg_Err (vd, "no available XVideo adaptor");
        goto error;
    }
478
    else
479
    {
480
        xcb_map_window (conn, p_sys->window);
481

482
483
484
485
486
487
488
        vout_display_place_t place;

        vout_display_PlacePicture (&place, &vd->source, vd->cfg, false);
        p_sys->width  = place.width;
        p_sys->height = place.height;

        /* */
489
490
491
        const uint32_t values[] = {
            place.x, place.y, place.width, place.height };
        xcb_configure_window (conn, p_sys->window,
492
                              XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y |
493
494
                              XCB_CONFIG_WINDOW_WIDTH |
                              XCB_CONFIG_WINDOW_HEIGHT,
495
                              values);
496
    }
497
    p_sys->visible = false;
498

499
    /* Create graphic context */
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
500
501
    p_sys->gc = xcb_generate_id (conn);
    xcb_create_gc (conn, p_sys->gc, p_sys->window, 0, NULL);
502
    msg_Dbg (vd, "using X11 graphic context 0x%08"PRIx32, p_sys->gc);
503

504
505
506
    /* Create cursor */
    p_sys->cursor = CreateBlankCursor (conn, screen);

507
508
    /* */
    p_sys->pool = NULL;
509

510
511
512
    /* */
    vout_display_info_t info = vd->info;
    info.has_pictures_invalid = false;
513

514
515
516
    /* Setup vout_display_t once everything is fine */
    vd->fmt = fmt;
    vd->info = info;
517

518
519
520
521
522
    vd->get = Get;
    vd->prepare = NULL;
    vd->display = Display;
    vd->control = Control;
    vd->manage = Manage;
523

524
    /* */
525
    vout_display_SendEventFullscreen (vd, false);
526
    unsigned width, height;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
527
    if (!GetWindowSize (p_sys->embed, conn, &width, &height))
528
        vout_display_SendEventDisplaySize (vd, width, height, false);
529
530

    return VLC_SUCCESS;
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553

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 *p_sys = vd->sys;

    if (p_sys->pool)
    {
        for (unsigned i = 0; i < MAX_PICTURES; i++)
        {
            picture_resource_t *res = &p_sys->resource[i];

            if (!res->p->p_pixels)
                break;
554
            PictureResourceFree (res, NULL);
555
556
557
558
559
560
561
562
        }
        picture_pool_Delete (p_sys->pool);
    }

    free (p_sys->att);
    vout_display_DeleteWindow (vd, p_sys->embed);
    xcb_disconnect (p_sys->conn);
    free (p_sys);
563
564
565
}

/**
566
 * Return a direct buffer
567
 */
568
static picture_t *Get (vout_display_t *vd)
569
{
570
571
572
573
    vout_display_sys_t *p_sys = vd->sys;

    if (!p_sys->pool)
    {
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
574
575
        picture_t *pic = picture_New (vd->fmt.i_chroma, p_sys->att->width,
                                      p_sys->att->height, 0);
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
        if (!pic)
            return NULL;

        memset (p_sys->resource, 0, sizeof(p_sys->resource));

        const uint32_t *offsets =
            xcb_xv_query_image_attributes_offsets (p_sys->att);
        p_sys->data_size = p_sys->att->data_size;

        unsigned count;
        picture_t *pic_array[MAX_PICTURES];
        for (count = 0; count < MAX_PICTURES; count++)
        {
            picture_resource_t *res = &p_sys->resource[count];

            for (int i = 0; i < pic->i_planes; i++)
            {
                res->p[i].i_lines = pic->p[i].i_lines; /* FIXME seems wrong*/
                res->p[i].i_pitch = pic->p[i].i_pitch;
            }
            if (PictureResourceAlloc (vd, res, p_sys->att->data_size,
                                      p_sys->conn, p_sys->shm))
                break;

            /* Allocate further planes as specified by XVideo */
            /* We assume that offsets[0] is zero */
            for (int i = 1; i < pic->i_planes; i++)
                res->p[i].p_pixels = res->p[0].p_pixels + offsets[i];
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
604
605
606
607
608
609
610
            if (vd->fmt.i_chroma == VLC_CODEC_YV12)
            {   /* YVU: swap U and V planes */
                uint8_t *buf = res->p[2].p_pixels;
                res->p[2].p_pixels = res->p[1].p_pixels;
                res->p[1].p_pixels = buf;
            }

611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
            pic_array[count] = picture_NewFromResource (&vd->fmt, res);
            if (!pic_array[count])
            {
                PictureResourceFree (res, p_sys->conn);
                memset (res, 0, sizeof(*res));
                break;
            }
        }
        picture_Release (pic);

        if (count == 0)
            return NULL;

        p_sys->pool = picture_pool_New (count, pic_array);
        if (!p_sys->pool)
        {
            /* TODO release picture resources */
            return NULL;
        }
        /* FIXME should also do it in case of error ? */
        xcb_flush (p_sys->conn);
    }
633

634
    return picture_pool_Get (p_sys->pool);
635
636
637
638
639
}

/**
 * Sends an image to the X server.
 */
640
static void Display (vout_display_t *vd, picture_t *pic)
641
{
642
643
    vout_display_sys_t *p_sys = vd->sys;
    xcb_shm_seg_t segment = pic->p_sys->segment;
644
    xcb_void_cookie_t ck;
645

646
647
    if (!p_sys->visible)
        goto out;
648
    if (segment)
649
650
        ck = xcb_xv_shm_put_image_checked (p_sys->conn, p_sys->port,
                              p_sys->window, p_sys->gc, segment, p_sys->id, 0,
651
652
653
654
655
                   /* Src: */ vd->source.i_x_offset,
                              vd->source.i_y_offset,
                              vd->source.i_visible_width,
                              vd->source.i_visible_height,
                   /* Dst: */ 0, 0, p_sys->width, p_sys->height,
656
                /* Memory: */ pic->p->i_pitch / pic->p->i_pixel_pitch,
657
                              pic->p->i_lines, false);
658
    else
659
        ck = xcb_xv_put_image_checked (p_sys->conn, p_sys->port, p_sys->window,
660
                          p_sys->gc, p_sys->id,
661
662
663
664
                          vd->source.i_x_offset,
                          vd->source.i_y_offset,
                          vd->source.i_visible_width,
                          vd->source.i_visible_height,
665
                          0, 0, p_sys->width, p_sys->height,
666
                          pic->p->i_pitch / pic->p->i_pixel_pitch,
667
                          pic->p->i_lines,
668
                          p_sys->data_size, pic->p->p_pixels);
669

670
671
672
673
674
675
676
    /* Wait for reply. See x11.c for rationale. */
    xcb_generic_error_t *e = xcb_request_check (p_sys->conn, ck);
    if (e != NULL)
    {
        msg_Dbg (vd, "%s: X11 error %d", "cannot put image", e->error_code);
        free (e);
    }
677
out:
678
    picture_Release (pic);
679
680
}

681
static int Control (vout_display_t *vd, int query, va_list ap)
682
{
683
    vout_display_sys_t *p_sys = vd->sys;
684

685
    switch (query)
686
    {
687
688
689
690
691
692
    case VOUT_DISPLAY_CHANGE_FULLSCREEN:
    {
        const vout_display_cfg_t *c = va_arg (ap, const vout_display_cfg_t *);
        return vout_window_SetFullScreen (p_sys->embed, c->is_fullscreen);
    }

693
694
695
696
697
698
699
700
    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:
    {
        const vout_display_cfg_t *cfg;
        const video_format_t *source;
701
        bool is_forced;
702

703
704
705
706
707
708
709
710
711
712
        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 *);
713
714
            if (query == VOUT_DISPLAY_CHANGE_DISPLAY_SIZE)
                is_forced = (bool)va_arg (ap, int);
715
        }
716

717
718
        /* */
        if (query == VOUT_DISPLAY_CHANGE_DISPLAY_SIZE
719
         && is_forced
720
721
722
723
724
         && (cfg->display.width  != vd->cfg->display.width
           ||cfg->display.height != vd->cfg->display.height)
         && vout_window_SetSize (p_sys->embed,
                                  cfg->display.width,
                                  cfg->display.height))
725
            return VLC_EGENERIC;
726

727
728
729
730
731
732
733
734
735
736
737
738
739
740
        vout_display_place_t place;
        vout_display_PlacePicture (&place, source, cfg, false);
        p_sys->width  = place.width;
        p_sys->height = place.height;

        /* Move the picture within the window */
        const uint32_t values[] = { place.x, place.y,
                                    place.width, place.height, };
        xcb_configure_window (p_sys->conn, p_sys->window,
                              XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y
                            | XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT,
                              values);
        xcb_flush (p_sys->conn);
        return VLC_SUCCESS;
741
    }
742
    case VOUT_DISPLAY_CHANGE_ON_TOP:
743
    {
744
745
        int on_top = (int)va_arg (ap, int);
        return vout_window_SetOnTop (p_sys->embed, on_top);
746
    }
747
748
749

    /* Hide the mouse. It will be send when
     * vout_display_t::info.b_hide_mouse is false */
750
751
752
753
    case VOUT_DISPLAY_HIDE_MOUSE:
        xcb_change_window_attributes (p_sys->conn, p_sys->embed->handle.xid,
                                  XCB_CW_CURSOR, &(uint32_t){ p_sys->cursor });
        return VLC_SUCCESS;
754
755
    case VOUT_DISPLAY_RESET_PICTURES:
        assert(0);
756
    default:
757
        msg_Err (vd, "Unknown request in XCB vout display");
758
759
        return VLC_EGENERIC;
    }
760
}
761

762
763
764
765
static void Manage (vout_display_t *vd)
{
    vout_display_sys_t *p_sys = vd->sys;

766
    ManageEvent (vd, p_sys->conn, &p_sys->visible);
767
}
768