window.c 19 KB
Newer Older
1
2
3
4
5
6
7
8
9
/**
 * @file window.c
 * @brief X C Bindings window provider 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
 * 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 <stdarg.h>
#include <assert.h>
29
#include <poll.h>
30
31
#include <unistd.h> /* gethostname() and sysconf() */
#include <limits.h> /* _POSIX_HOST_NAME_MAX */
32
33

#include <xcb/xcb.h>
34
35
typedef xcb_atom_t Atom;
#include <X11/Xatom.h> /* XA_WM_NAME */
36
37
38

#include <vlc_common.h>
#include <vlc_plugin.h>
39
#include <vlc_vout_window.h>
40

41
42
#include "xcb_vlc.h"

43
44
45
46
47
#define XID_TEXT N_("ID of the video output X window")
#define XID_LONGTEXT N_( \
    "VLC can embed its video output in an existing X11 window. " \
    "This is the X identifier of that window (0 means none).")

48
49
static int  Open (vlc_object_t *);
static void Close (vlc_object_t *);
50
51
static int  EmOpen (vlc_object_t *);
static void EmClose (vlc_object_t *);
52
53
54
55
56

/*
 * Module descriptor
 */
vlc_module_begin ()
57
58
    set_shortname (N_("X window"))
    set_description (N_("X11 video window (XCB)"))
59
60
    set_category (CAT_VIDEO)
    set_subcategory (SUBCAT_VIDEO_VOUT)
61
    set_capability ("vout window xid", 10)
62
63
    set_callbacks (Open, Close)

64
65
66
67
68
    /* Obsolete since 1.1.0: */
    add_obsolete_bool ("x11-altfullscreen")
    add_obsolete_bool ("xvideo-altfullscreen")
    add_obsolete_bool ("xvmc-altfullscreen")
    add_obsolete_bool ("glx-altfullscreen")
69
70
71
72
73
74
75
76
77
78
79
80

    add_submodule ()
    set_shortname (N_("Drawable"))
    set_description (N_("Embedded window video"))
    set_category (CAT_VIDEO)
    set_subcategory (SUBCAT_VIDEO_VOUT)
    set_capability ("vout window xid", 70)
    set_callbacks (EmOpen, EmClose)

    add_integer ("drawable-xid", 0, NULL, XID_TEXT, XID_LONGTEXT, true)
        change_unsaveable ()

81
82
83
vlc_module_end ()

static int Control (vout_window_t *, int, va_list ap);
84
85
static void *Thread (void *);

86
87
#define MATCHBOX_HACK 1 /* Matchbox focus hack */

88
89
90
91
92
struct vout_window_sys_t
{
    xcb_connection_t *conn;
    key_handler_t *keys;
    vlc_thread_t thread;
93
94
95
96

    xcb_window_t root;
    xcb_atom_t wm_state;
    xcb_atom_t wm_state_above;
97
    xcb_atom_t wm_state_below;
98
    xcb_atom_t wm_state_fullscreen;
99
100
101
#ifdef MATCHBOX_HACK
    xcb_atom_t mb_current_app_window;
#endif
102
};
103

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
104
/** Set an X window property from a nul-terminated string */
105
106
107
108
109
110
111
112
static inline
void set_string (xcb_connection_t *conn, xcb_window_t window,
                 xcb_atom_t type, xcb_atom_t atom, const char *str)
{
    xcb_change_property (conn, XCB_PROP_MODE_REPLACE, window, atom, type,
                         /* format */ 8, strlen (str), str);
}

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
113
/** Set an X window string property */
114
115
116
117
static inline
void set_ascii_prop (xcb_connection_t *conn, xcb_window_t window,
                     xcb_atom_t atom, const char *value)
{
118
    set_string (conn, window, XA_STRING, atom, value);
119
120
}

121
122
123
124
125
126
127
128
129
130
131
132
133
static inline
void set_wm_hints (xcb_connection_t *conn, xcb_window_t window)
{
    static const uint32_t wm_hints[8] = {
        3, /* flags: Input, Initial state */
        1, /* input: True */
        1, /* initial state: Normal */
        0, 0, 0, 0, 0, /* Icon */
    };
    xcb_change_property (conn, XCB_PROP_MODE_REPLACE, window, XA_WM_HINTS,
                         XA_WM_HINTS, 32, 8, wm_hints);
}

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
134
/** Set the Window ICCCM client machine property */
135
136
137
static inline
void set_hostname_prop (xcb_connection_t *conn, xcb_window_t window)
{
138
139
140
141
142
    char* hostname;
    long host_name_max = sysconf (_SC_HOST_NAME_MAX);
    if (host_name_max <= 0) host_name_max = _POSIX_HOST_NAME_MAX;
    hostname = malloc (host_name_max);
    if(!hostname) return;
143

144
    if (gethostname (hostname, host_name_max) == 0)
145
    {
146
        hostname[host_name_max - 1] = '\0';
147
148
        set_ascii_prop (conn, window, XA_WM_CLIENT_MACHINE, hostname);
    }
149
    free(hostname);
150
151
}

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
152
/** Request the X11 server to internalize a string into an atom */
153
154
155
156
157
158
static inline
xcb_intern_atom_cookie_t intern_string (xcb_connection_t *c, const char *s)
{
    return xcb_intern_atom (c, 0, strlen (s), s);
}

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
159
/** Extract the X11 atom from an intern request cookie */
160
161
162
163
164
165
166
167
168
169
170
171
172
173
static
xcb_atom_t get_atom (xcb_connection_t *conn, xcb_intern_atom_cookie_t ck)
{
    xcb_intern_atom_reply_t *reply;
    xcb_atom_t atom;

    reply = xcb_intern_atom_reply (conn, ck, NULL);
    if (reply == NULL)
        return 0;

    atom = reply->atom;
    free (reply);
    return atom;
}
174

175
176
177
178
#define NET_WM_STATE_REMOVE 0
#define NET_WM_STATE_ADD    1
#define NET_WM_STATE_TOGGLE 2

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
179
180
181
static void CacheAtoms (vout_window_sys_t *p_sys)
{
    xcb_connection_t *conn = p_sys->conn;
182
183
    xcb_intern_atom_cookie_t wm_state_ck, wm_state_above_ck,
                             wm_state_below_ck, wm_state_fs_ck;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
184
185
186

    wm_state_ck = intern_string (conn, "_NET_WM_STATE");
    wm_state_above_ck = intern_string (conn, "_NET_WM_STATE_ABOVE");
187
    wm_state_below_ck = intern_string (conn, "_NET_WM_STATE_ABOVE");
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
188
    wm_state_fs_ck = intern_string (conn, "_NET_WM_STATE_FULLSCREEN");
189
190
#ifdef MATCHBOX_HACK
    xcb_intern_atom_cookie_t mb_current_app_window;
191
192
193
    mb_current_app_window = xcb_intern_atom (conn, true,
                                             strlen ("_MB_CURRENT_APP_WINDOW"),
                                             "_MB_CURRENT_APP_WINDOW");
194
#endif
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
195
196
197

    p_sys->wm_state = get_atom (conn, wm_state_ck);
    p_sys->wm_state_above = get_atom (conn, wm_state_above_ck);
198
    p_sys->wm_state_below = get_atom (conn, wm_state_below_ck);
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
199
    p_sys->wm_state_fullscreen = get_atom (conn, wm_state_fs_ck);
200
201
202
#ifdef MATCHBOX_HACK
    p_sys->mb_current_app_window = get_atom (conn, mb_current_app_window);
#endif
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
203
204
}

205
206
207
208
209
210
211
212
213
/**
 * Create an X11 window.
 */
static int Open (vlc_object_t *obj)
{
    vout_window_t *wnd = (vout_window_t *)obj;
    xcb_generic_error_t *err;
    xcb_void_cookie_t ck;

214
    vout_window_sys_t *p_sys = malloc (sizeof (*p_sys));
215
216
217
    if (p_sys == NULL)
        return VLC_ENOMEM;

218
219
220
221
222
223
224
225
    /* Connect to X */
    char *display = var_CreateGetNonEmptyString (wnd, "x11-display");
    int snum;

    xcb_connection_t *conn = xcb_connect (display, &snum);
    if (xcb_connection_has_error (conn) /*== NULL*/)
        goto error;

226
227
    /* Find configured screen */
    const xcb_setup_t *setup = xcb_get_setup (conn);
228
    const xcb_screen_t *scr = NULL;
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
    for (xcb_screen_iterator_t i = xcb_setup_roots_iterator (setup);
         i.rem > 0; xcb_screen_next (&i))
    {
        if (snum == 0)
        {
            scr = i.data;
            break;
        }
        snum--;
    }
    if (scr == NULL)
    {
        msg_Err (wnd, "bad X11 screen number");
        goto error;
    }

245
    /* Create window */
246
247
    const uint32_t mask = XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK;
    uint32_t values[2] = {
248
249
        /* XCB_CW_BACK_PIXEL */
        scr->black_pixel,
250
251
        /* XCB_CW_EVENT_MASK */
        XCB_EVENT_MASK_KEY_PRESS,
252
    };
253
254
255

    xcb_window_t window = xcb_generate_id (conn);
    ck = xcb_create_window_checked (conn, scr->root_depth, window, scr->root,
256
                                    0, 0, wnd->cfg->width, wnd->cfg->height, 0,
257
                                    XCB_WINDOW_CLASS_INPUT_OUTPUT,
258
                                    scr->root_visual, mask, values);
259
260
261
262
    err = xcb_request_check (conn, ck);
    if (err)
    {
        msg_Err (wnd, "creating window: X11 error %d", err->error_code);
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
263
        free (err);
264
265
266
        goto error;
    }

267
    wnd->xid = window;
268
    wnd->x11_display = display;
269
    wnd->control = Control;
270
    wnd->sys = p_sys;
271
272

    p_sys->conn = conn;
273
274
275
276
    if (var_CreateGetBool (obj, "keyboard-events"))
        p_sys->keys = CreateKeyHandler (obj, conn);
    else
        p_sys->keys = NULL;
277
278
    p_sys->root = scr->root;

279
280
    /* ICCCM
     * No cut&paste nor drag&drop, only Window Manager communication. */
281
282
    /* Plain ASCII localization of VLC for ICCCM window name */
    set_ascii_prop (conn, window, XA_WM_NAME,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
283
                  vlc_pgettext ("ASCII", "VLC media player"));
284
    set_ascii_prop (conn, window, XA_WM_ICON_NAME,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
285
                    vlc_pgettext ("ASCII", "VLC"));
286
    set_wm_hints (conn, window);
287
    xcb_change_property (conn, XCB_PROP_MODE_REPLACE, window, XA_WM_CLASS,
288
                         XA_STRING, 8, 8, "vlc\0Vlc");
289
    set_hostname_prop (conn, window);
290

291
292
293
294
295
296
297
    /* EWMH */
    xcb_intern_atom_cookie_t utf8_string_ck
        = intern_string (conn, "UTF8_STRING");;
    xcb_intern_atom_cookie_t net_wm_name_ck
        = intern_string (conn, "_NET_WM_NAME");
    xcb_intern_atom_cookie_t net_wm_icon_name_ck
        = intern_string (conn, "_NET_WM_ICON_NAME");
Nick Pope's avatar
Nick Pope committed
298
299
    xcb_intern_atom_cookie_t wm_window_role_ck
        = intern_string (conn, "WM_WINDOW_ROLE");
300

301
302
303
304
305
306
307
308
309
310
311
312
313
314
    xcb_atom_t utf8 = get_atom (conn, utf8_string_ck);

    xcb_atom_t net_wm_name = get_atom (conn, net_wm_name_ck);
    char *title = var_CreateGetNonEmptyString (wnd, "video-title");
    if (title)
    {
        set_string (conn, window, utf8, net_wm_name, title);
        free (title);
    }
    else
        set_string (conn, window, utf8, net_wm_name, _("VLC media player"));

    xcb_atom_t net_wm_icon_name = get_atom (conn, net_wm_icon_name_ck);
    set_string (conn, window, utf8, net_wm_icon_name, _("VLC"));
315

Nick Pope's avatar
Nick Pope committed
316
317
318
    xcb_atom_t wm_window_role = get_atom (conn, wm_window_role_ck);
    set_ascii_prop (conn, window, wm_window_role, "vlc-video");

319
320
    /* Cache any EWMH atom we may need later */
    CacheAtoms (p_sys);
321
#ifdef MATCHBOX_HACK
322
323
324
325
326
327
    if (p_sys->mb_current_app_window)
    {
        uint32_t value = XCB_EVENT_MASK_PROPERTY_CHANGE;
        xcb_change_window_attributes (conn, scr->root,
                                      XCB_CW_EVENT_MASK, &value);
    }
328
329
#endif

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
330
331
    /* Make the window visible */
    xcb_map_window (conn, window);
332
333
334

    /* Create the event thread. It will dequeue all events, so any checked
     * request from this thread must be completed at this point. */
335
336
337
338
    if ((p_sys->keys != NULL)
     && vlc_clone (&p_sys->thread, Thread, wnd, VLC_THREAD_PRIORITY_LOW))
        DestroyKeyHandler (p_sys->keys);

339
#ifdef MATCHBOX_HACK
340
341
    if (p_sys->mb_current_app_window)
        xcb_set_input_focus (p_sys->conn, XCB_INPUT_FOCUS_POINTER_ROOT,
342
                             wnd->xid, XCB_CURRENT_TIME);
343
#endif
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
344
    xcb_flush (conn); /* Make sure map_window is sent (should be useless) */
345
346
347
348
    return VLC_SUCCESS;

error:
    xcb_disconnect (conn);
349
    free (display);
350
    free (p_sys);
351
352
353
354
355
356
357
358
359
360
    return VLC_EGENERIC;
}


/**
 * Destroys the X11 window.
 */
static void Close (vlc_object_t *obj)
{
    vout_window_t *wnd = (vout_window_t *)obj;
361
    vout_window_sys_t *p_sys = wnd->sys;
362
    xcb_connection_t *conn = p_sys->conn;
363

364
365
366
367
368
369
    if (p_sys->keys)
    {
        vlc_cancel (p_sys->thread);
        vlc_join (p_sys->thread, NULL);
        DestroyKeyHandler (p_sys->keys);
    }
370
    xcb_disconnect (conn);
371
    free (wnd->x11_display);
372
373
374
375
    free (p_sys);
}


Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
376
/** Background thread for X11 events handling */
377
378
379
static void *Thread (void *data)
{
    vout_window_t *wnd = data;
380
    vout_window_sys_t *p_sys = wnd->sys;
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
    xcb_connection_t *conn = p_sys->conn;

    int fd = xcb_get_file_descriptor (conn);
    if (fd == -1)
        return NULL;

    for (;;)
    {
        xcb_generic_event_t *ev;
        struct pollfd ufd = { .fd = fd, .events = POLLIN, };

        poll (&ufd, 1, -1);

        int canc = vlc_savecancel ();
        while ((ev = xcb_poll_for_event (conn)) != NULL)
        {
            if (ProcessKeyEvent (p_sys->keys, ev) == 0)
                continue;
399
#ifdef MATCHBOX_HACK
400
401
            if (p_sys->mb_current_app_window
             && (ev->response_type & 0x7f) == XCB_PROPERTY_NOTIFY)
402
403
404
405
406
407
408
409
410
411
412
            {
                const xcb_property_notify_event_t *pne =
                    (xcb_property_notify_event_t *)ev;
                if (pne->atom == p_sys->mb_current_app_window
                 && pne->state == XCB_PROPERTY_NEW_VALUE)
                {
                    xcb_get_property_reply_t *r =
                        xcb_get_property_reply (conn,
                            xcb_get_property (conn, 0, pne->window, pne->atom,
                                              XA_WINDOW, 0, 4), NULL);
                    if (r != NULL
413
                     && !memcmp (xcb_get_property_value (r), &wnd->xid,
414
415
416
417
418
                                 4))
                    {
                        msg_Dbg (wnd, "asking Matchbox for input focus");
                        xcb_set_input_focus (conn,
                                             XCB_INPUT_FOCUS_POINTER_ROOT,
419
                                             wnd->xid, pne->time);
420
421
422
423
424
425
426
427
                        xcb_flush (conn);
                    }
                    free (r);
                }
            }
            else
#endif
                msg_Dbg (wnd, "unhandled event: %"PRIu8, ev->response_type);
428
429
430
431
432
433
434
435
436
437
438
            free (ev);
        }
        vlc_restorecancel (canc);

        if (xcb_connection_has_error (conn))
        {
            msg_Err (wnd, "X server failure");
            break;
        }
    }
    return NULL;
439
440
}

441
442
443
444
445
446
447
448
/** Changes the EWMH state of the window */
static void set_wm_state (vout_window_t *wnd, bool on, xcb_atom_t state)
{
    vout_window_sys_t *sys = wnd->sys;
    /* From EWMH "_WM_STATE" */
    xcb_client_message_event_t ev = {
         .response_type = XCB_CLIENT_MESSAGE,
         .format = 32,
449
         .window = wnd->xid,
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
         .type = sys->wm_state,
    };

    ev.data.data32[0] = on ? NET_WM_STATE_ADD : NET_WM_STATE_REMOVE;
    ev.data.data32[1] = state;
    ev.data.data32[2] = 0;
    ev.data.data32[3] = 1;

    /* From ICCCM "Changing Window State" */
    xcb_send_event (sys->conn, 0, sys->root,
                    XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY |
                    XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT,
                    (const char *)&ev);
}

465
466
467

static int Control (vout_window_t *wnd, int cmd, va_list ap)
{
468
    vout_window_sys_t *p_sys = wnd->sys;
469
470
471
472
    xcb_connection_t *conn = p_sys->conn;

    switch (cmd)
    {
473
        case VOUT_WINDOW_SET_SIZE:
474
475
476
477
478
        {
            unsigned width = va_arg (ap, unsigned);
            unsigned height = va_arg (ap, unsigned);
            const uint32_t values[] = { width, height, };

479
            xcb_configure_window (conn, wnd->xid,
480
481
482
483
484
                                  XCB_CONFIG_WINDOW_WIDTH |
                                  XCB_CONFIG_WINDOW_HEIGHT, values);
            break;
        }

485
        case VOUT_WINDOW_SET_STATE:
486
487
488
489
490
491
492
        {
            unsigned state = va_arg (ap, unsigned);
            bool above = (state & VOUT_WINDOW_STATE_ABOVE) != 0;
            bool below = (state & VOUT_WINDOW_STATE_BELOW) != 0;

            set_wm_state (wnd, above, p_sys->wm_state_above);
            set_wm_state (wnd, below, p_sys->wm_state_below);
493
            break;
494
        }
495

496
497
498
        case VOUT_WINDOW_SET_FULLSCREEN:
            set_wm_state (wnd, va_arg (ap, int), p_sys->wm_state_fullscreen);
            break;
499

500
501
502
503
        default:
            msg_Err (wnd, "request %d not implemented", cmd);
            return VLC_EGENERIC;
    }
504
    xcb_flush (p_sys->conn);
505
    return VLC_SUCCESS;
506
507
}

508
509
510
511
512
513
514
515
/*** Embedded drawable support ***/

static vlc_mutex_t serializer = VLC_STATIC_MUTEX;

/** Acquire a drawable */
static int AcquireDrawable (vlc_object_t *obj, xcb_window_t window)
{
    xcb_window_t *used;
516
    size_t n = 0;
517
518
519
520
521
522
523

    if (var_Create (obj->p_libvlc, "xid-in-use", VLC_VAR_ADDRESS))
        return VLC_ENOMEM;

    /* Keep a list of busy drawables, so we don't overlap videos if there are
     * more than one video track in the stream. */
    vlc_mutex_lock (&serializer);
ivoire's avatar
ivoire committed
524
    used = var_GetAddress (obj->p_libvlc, "xid-in-use");
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
    if (used != NULL)
    {
        while (used[n])
        {
            if (used[n] == window)
                goto skip;
            n++;
        }
    }

    used = realloc (used, sizeof (*used) * (n + 2));
    if (used != NULL)
    {
        used[n] = window;
        used[n + 1] = 0;
ivoire's avatar
ivoire committed
540
        var_SetAddress (obj->p_libvlc, "xid-in-use", used);
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
    }
    else
    {
skip:
        msg_Warn (obj, "X11 drawable 0x%08"PRIx8" is busy", window);
        window = 0;
    }
    vlc_mutex_unlock (&serializer);

    return (window == 0) ? VLC_EGENERIC : VLC_SUCCESS;
}

/** Remove this drawable from the list of busy ones */
static void ReleaseDrawable (vlc_object_t *obj, xcb_window_t window)
{
    xcb_window_t *used;
    size_t n = 0;

    vlc_mutex_lock (&serializer);
ivoire's avatar
ivoire committed
560
    used = var_GetAddress (obj->p_libvlc, "xid-in-use");
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
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
    assert (used);
    while (used[n] != window)
    {
        assert (used[n]);
        n++;
    }
    do
        used[n] = used[n + 1];
    while (used[++n]);

    if (n == 0)
         var_SetAddress (obj->p_libvlc, "xid-in-use", NULL);
    vlc_mutex_unlock (&serializer);

    if (n == 0)
        free (used);
    /* Variables are reference-counted... */
    var_Destroy (obj->p_libvlc, "xid-in-use");
}

/**
 * Wrap an existing X11 window to embed the video.
 */
static int EmOpen (vlc_object_t *obj)
{
    vout_window_t *wnd = (vout_window_t *)obj;

    xcb_window_t window = var_CreateGetInteger (obj, "drawable-xid");
    if (window == 0)
        return VLC_EGENERIC;
    var_Destroy (obj, "drawable-xid");

    if (AcquireDrawable (obj, window))
        return VLC_EGENERIC;

    vout_window_sys_t *p_sys = malloc (sizeof (*p_sys));
    xcb_connection_t *conn = xcb_connect (NULL, NULL);
    if (p_sys == NULL || xcb_connection_has_error (conn))
        goto error;

601
    wnd->xid = window;
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
    wnd->control = Control;
    wnd->sys = p_sys;

    p_sys->conn = conn;

    xcb_get_geometry_reply_t *geo =
        xcb_get_geometry_reply (conn, xcb_get_geometry (conn, window), NULL);
    if (geo == NULL)
    {
        msg_Err (obj, "bad X11 window 0x%08"PRIx8, window);
        goto error;
    }
    p_sys->root = geo->root;
    free (geo);

617
    if (var_CreateGetBool (obj, "keyboard-events"))
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
    {
        p_sys->keys = CreateKeyHandler (obj, conn);
        if (p_sys->keys != NULL)
        {
            const uint32_t mask = XCB_CW_EVENT_MASK;
            const uint32_t values[1] = {
                XCB_EVENT_MASK_KEY_PRESS,
            };
            xcb_change_window_attributes (conn, window, mask, values);
        }
    }

    CacheAtoms (p_sys);
    if ((p_sys->keys != NULL)
     && vlc_clone (&p_sys->thread, Thread, wnd, VLC_THREAD_PRIORITY_LOW))
        DestroyKeyHandler (p_sys->keys);

    xcb_flush (conn);

    return VLC_SUCCESS;

error:
    xcb_disconnect (conn);
    free (p_sys);
    ReleaseDrawable (obj, window);
    return VLC_EGENERIC;
}

static void EmClose (vlc_object_t *obj)
{
    vout_window_t *wnd = (vout_window_t *)obj;
649
    xcb_window_t window = wnd->xid;
650
651
652
653

    Close (obj);
    ReleaseDrawable (obj, window);
}