]> git.sesse.net Git - vlc/blob - modules/video_output/xcb/common.c
6e39ad543ae04f655dbc144fd8e676fdec56511c
[vlc] / modules / video_output / xcb / common.c
1 /**
2  * @file common.c
3  * @brief Common code for XCB video output plugins
4  */
5 /*****************************************************************************
6  * Copyright © 2009 Rémi Denis-Courmont
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License
10  * as published by the Free Software Foundation; either version 2.0
11  * of the License, or (at your option) any later version.
12  *
13  * This library 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 General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
21  ****************************************************************************/
22
23 #ifdef HAVE_CONFIG_H
24 # include <config.h>
25 #endif
26
27 #include <stdlib.h>
28 #include <assert.h>
29
30 #include <sys/types.h>
31 #include <sys/shm.h>
32
33 #include <xcb/xcb.h>
34 #include <xcb/shm.h>
35
36 #include <vlc_common.h>
37 #include <vlc_vout_display.h>
38
39 #include "xcb_vlc.h"
40
41 /**
42  * Check for an error
43  */
44 int CheckError (vout_display_t *vd, xcb_connection_t *conn,
45                 const char *str, xcb_void_cookie_t ck)
46 {
47     xcb_generic_error_t *err;
48
49     err = xcb_request_check (conn, ck);
50     if (err)
51     {
52         msg_Err (vd, "%s: X11 error %d", str, err->error_code);
53         free (err);
54         return VLC_EGENERIC;
55     }
56     return VLC_SUCCESS;
57 }
58
59 /**
60  * Connect to the X server.
61  */
62 xcb_connection_t *Connect (vlc_object_t *obj)
63 {
64     char *display = var_CreateGetNonEmptyString (obj, "x11-display");
65     xcb_connection_t *conn = xcb_connect (display, NULL);
66
67     free (display);
68     if (xcb_connection_has_error (conn) /*== NULL*/)
69     {
70         msg_Err (obj, "cannot connect to X server");
71         xcb_disconnect (conn);
72         return NULL;
73     }
74
75     const xcb_setup_t *setup = xcb_get_setup (conn);
76     msg_Dbg (obj, "connected to X%"PRIu16".%"PRIu16" server",
77              setup->protocol_major_version, setup->protocol_minor_version);
78     char *vendor = strndup (xcb_setup_vendor (setup), setup->vendor_len);
79     if (vendor)
80     {
81         msg_Dbg (obj, " vendor : %s", vendor);
82         free (vendor);
83     }
84     msg_Dbg (obj, " version: %"PRIu32, setup->release_number);
85     return conn;
86 }
87
88
89 /**
90  * Create a VLC video X window object, find the corresponding X server screen,
91  * and probe the MIT-SHM extension.
92  */
93 vout_window_t *GetWindow (vout_display_t *vd,
94                           xcb_connection_t *conn,
95                           const xcb_screen_t **restrict pscreen,
96                           bool *restrict pshm)
97 {
98     /* Get window */
99     xcb_window_t root;
100     vout_window_cfg_t wnd_cfg;
101
102     memset( &wnd_cfg, 0, sizeof(wnd_cfg) );
103     wnd_cfg.type = VOUT_WINDOW_TYPE_XID;
104     wnd_cfg.width  = vd->cfg->display.width;
105     wnd_cfg.height = vd->cfg->display.height;
106
107     vout_window_t *wnd = vout_display_NewWindow (vd, &wnd_cfg);
108     if (wnd == NULL)
109     {
110         msg_Err (vd, "parent window not available");
111         return NULL;
112     }
113     else
114     {
115         xcb_get_geometry_reply_t *geo;
116         xcb_get_geometry_cookie_t ck;
117
118         ck = xcb_get_geometry (conn, wnd->handle.xid);
119         geo = xcb_get_geometry_reply (conn, ck, NULL);
120         if (geo == NULL)
121         {
122             msg_Err (vd, "parent window not valid");
123             goto error;
124         }
125         root = geo->root;
126         free (geo);
127
128         /* Subscribe to parent window resize events */
129         uint32_t value = XCB_EVENT_MASK_STRUCTURE_NOTIFY;
130         xcb_change_window_attributes (conn, wnd->handle.xid,
131                                       XCB_CW_EVENT_MASK, &value);
132         /* Try to subscribe to click events */
133         /* (only one X11 client can get them, so might not work) */
134         value |= XCB_EVENT_MASK_BUTTON_PRESS | XCB_EVENT_MASK_BUTTON_RELEASE;
135         xcb_change_window_attributes (conn, wnd->handle.xid,
136                                       XCB_CW_EVENT_MASK, &value);
137     }
138
139     /* Find the selected screen */
140     const xcb_setup_t *setup = xcb_get_setup (conn);
141     const xcb_screen_t *screen = NULL;
142     for (xcb_screen_iterator_t i = xcb_setup_roots_iterator (setup);
143          i.rem > 0 && screen == NULL; xcb_screen_next (&i))
144     {
145         if (i.data->root == root)
146             screen = i.data;
147     }
148
149     if (screen == NULL)
150     {
151         msg_Err (vd, "parent window screen not found");
152         goto error;
153     }
154     msg_Dbg (vd, "using screen 0x%"PRIx32, root);
155
156     /* Check MIT-SHM shared memory support */
157     bool shm = var_CreateGetBool (vd, "x11-shm") > 0;
158     if (shm)
159     {
160         xcb_shm_query_version_cookie_t ck;
161         xcb_shm_query_version_reply_t *r;
162
163         ck = xcb_shm_query_version (conn);
164         r = xcb_shm_query_version_reply (conn, ck, NULL);
165         if (!r)
166         {
167             msg_Err (vd, "shared memory (MIT-SHM) not available");
168             msg_Warn (vd, "display will be slow");
169             shm = false;
170         }
171         free (r);
172     }
173
174     *pscreen = screen;
175     *pshm = shm;
176     return wnd;
177
178 error:
179     vout_display_DeleteWindow (vd, wnd);
180     return NULL;
181 }
182
183 /**
184  * Gets the size of an X window.
185  */
186 int GetWindowSize (struct vout_window_t *wnd, xcb_connection_t *conn,
187                    unsigned *restrict width, unsigned *restrict height)
188 {
189     xcb_get_geometry_cookie_t ck = xcb_get_geometry (conn, wnd->handle.xid);
190     xcb_get_geometry_reply_t *geo = xcb_get_geometry_reply (conn, ck, NULL);
191
192     if (!geo)
193         return -1;
194
195     *width = geo->width;
196     *height = geo->height;
197     free (geo);
198     return 0;
199 }
200
201 /**
202  * Initialize a picture buffer as shared memory, according to the video output
203  * format. If a attach is true, the segment is attached to
204  * the X server (MIT-SHM extension).
205  */
206 int PictureResourceAlloc (vout_display_t *vd, picture_resource_t *res, size_t size,
207                           xcb_connection_t *conn, bool attach)
208 {
209     res->p_sys = malloc (sizeof(*res->p_sys));
210     if (!res->p_sys)
211         return VLC_EGENERIC;
212
213     /* Allocate shared memory segment */
214     int id = shmget (IPC_PRIVATE, size, IPC_CREAT | 0700);
215     if (id == -1)
216     {
217         msg_Err (vd, "shared memory allocation error: %m");
218         free (res->p_sys);
219         return VLC_EGENERIC;
220     }
221
222     /* Attach the segment to VLC */
223     void *shm = shmat (id, NULL, 0 /* read/write */);
224     if (-1 == (intptr_t)shm)
225     {
226         msg_Err (vd, "shared memory attachment error: %m");
227         shmctl (id, IPC_RMID, 0);
228         free (res->p_sys);
229         return VLC_EGENERIC;
230     }
231
232     xcb_shm_seg_t segment;
233     if (attach)
234     {
235         /* Attach the segment to X */
236         xcb_void_cookie_t ck;
237
238         segment = xcb_generate_id (conn);
239         ck = xcb_shm_attach_checked (conn, segment, id, 1);
240
241         if (CheckError (vd, conn, "shared memory server-side error", ck))
242         {
243             msg_Info (vd, "using buggy X11 server - SSH proxying?");
244             segment = 0;
245         }
246     }
247     else
248         segment = 0;
249
250     shmctl (id, IPC_RMID, 0);
251     res->p_sys->segment = segment;
252     res->p->p_pixels = shm;
253     return VLC_SUCCESS;
254 }
255
256 /**
257  * Release picture private data: detach the shared memory segment.
258  */
259 void PictureResourceFree (picture_resource_t *res, xcb_connection_t *conn)
260 {
261     xcb_shm_seg_t segment = res->p_sys->segment;
262
263     if (segment != 0)
264     {
265         assert (conn != NULL);
266         xcb_shm_detach (conn, segment);
267     }
268     shmdt (res->p->p_pixels);
269 }
270