]> git.sesse.net Git - vlc/blob - modules/video_output/xcb/common.c
vout_window_t: simplify via anynomous union
[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
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 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                           uint8_t *restrict pdepth,
97                           bool *restrict pshm)
98 {
99     /* Get window */
100     xcb_window_t root;
101     vout_window_cfg_t wnd_cfg;
102
103     memset( &wnd_cfg, 0, sizeof(wnd_cfg) );
104     wnd_cfg.type = VOUT_WINDOW_TYPE_XID;
105     wnd_cfg.width  = vd->cfg->display.width;
106     wnd_cfg.height = vd->cfg->display.height;
107
108     vout_window_t *wnd = vout_display_NewWindow (vd, &wnd_cfg);
109     if (wnd == NULL)
110     {
111         msg_Err (vd, "parent window not available");
112         return NULL;
113     }
114     else
115     {
116         xcb_get_geometry_reply_t *geo;
117         xcb_get_geometry_cookie_t ck;
118
119         ck = xcb_get_geometry (conn, wnd->xid);
120         geo = xcb_get_geometry_reply (conn, ck, NULL);
121         if (geo == NULL)
122         {
123             msg_Err (vd, "parent window not valid");
124             goto error;
125         }
126         root = geo->root;
127         *pdepth = geo->depth;
128         free (geo);
129
130         /* Subscribe to parent window resize events */
131         uint32_t value = XCB_EVENT_MASK_POINTER_MOTION
132                        | XCB_EVENT_MASK_STRUCTURE_NOTIFY;
133         xcb_change_window_attributes (conn, wnd->xid,
134                                       XCB_CW_EVENT_MASK, &value);
135         /* Try to subscribe to click events */
136         /* (only one X11 client can get them, so might not work) */
137         if (var_CreateGetBool (vd, "mouse-events"))
138         {
139             value |= XCB_EVENT_MASK_BUTTON_PRESS
140                    | XCB_EVENT_MASK_BUTTON_RELEASE;
141             xcb_change_window_attributes (conn, wnd->xid,
142                                           XCB_CW_EVENT_MASK, &value);
143         }
144     }
145
146     /* Find the selected screen */
147     const xcb_setup_t *setup = xcb_get_setup (conn);
148     const xcb_screen_t *screen = NULL;
149     for (xcb_screen_iterator_t i = xcb_setup_roots_iterator (setup);
150          i.rem > 0 && screen == NULL; xcb_screen_next (&i))
151     {
152         if (i.data->root == root)
153             screen = i.data;
154     }
155
156     if (screen == NULL)
157     {
158         msg_Err (vd, "parent window screen not found");
159         goto error;
160     }
161     msg_Dbg (vd, "using screen 0x%"PRIx32, root);
162
163     /* Check MIT-SHM shared memory support */
164     bool shm = var_CreateGetBool (vd, "x11-shm") > 0;
165     if (shm)
166     {
167         xcb_shm_query_version_cookie_t ck;
168         xcb_shm_query_version_reply_t *r;
169
170         ck = xcb_shm_query_version (conn);
171         r = xcb_shm_query_version_reply (conn, ck, NULL);
172         if (!r)
173         {
174             msg_Err (vd, "shared memory (MIT-SHM) not available");
175             msg_Warn (vd, "display will be slow");
176             shm = false;
177         }
178         free (r);
179     }
180
181     *pscreen = screen;
182     *pshm = shm;
183     return wnd;
184
185 error:
186     vout_display_DeleteWindow (vd, wnd);
187     return NULL;
188 }
189
190 /**
191  * Gets the size of an X window.
192  */
193 int GetWindowSize (struct vout_window_t *wnd, xcb_connection_t *conn,
194                    unsigned *restrict width, unsigned *restrict height)
195 {
196     xcb_get_geometry_cookie_t ck = xcb_get_geometry (conn, wnd->xid);
197     xcb_get_geometry_reply_t *geo = xcb_get_geometry_reply (conn, ck, NULL);
198
199     if (!geo)
200         return -1;
201
202     *width = geo->width;
203     *height = geo->height;
204     free (geo);
205     return 0;
206 }
207
208 /**
209  * Create a blank cursor.
210  * Note that the pixmaps are leaked (until the X disconnection). Hence, this
211  * function should be called no more than once per X connection.
212  * @param conn XCB connection
213  * @param scr target XCB screen
214  */
215 xcb_cursor_t CreateBlankCursor (xcb_connection_t *conn,
216                                 const xcb_screen_t *scr)
217 {
218     xcb_cursor_t cur = xcb_generate_id (conn);
219     xcb_pixmap_t pix = xcb_generate_id (conn);
220
221     xcb_create_pixmap (conn, 1, pix, scr->root, 1, 1);
222     xcb_create_cursor (conn, cur, pix, pix, 0, 0, 0, 1, 1, 1, 0, 0);
223     return cur;
224 }
225
226 /**
227  * Initialize a picture buffer as shared memory, according to the video output
228  * format. If a attach is true, the segment is attached to
229  * the X server (MIT-SHM extension).
230  */
231 int PictureResourceAlloc (vout_display_t *vd, picture_resource_t *res, size_t size,
232                           xcb_connection_t *conn, bool attach)
233 {
234     res->p_sys = malloc (sizeof(*res->p_sys));
235     if (!res->p_sys)
236         return VLC_EGENERIC;
237
238     /* Allocate shared memory segment */
239     int id = shmget (IPC_PRIVATE, size, IPC_CREAT | 0700);
240     if (id == -1)
241     {
242         msg_Err (vd, "shared memory allocation error: %m");
243         free (res->p_sys);
244         return VLC_EGENERIC;
245     }
246
247     /* Attach the segment to VLC */
248     void *shm = shmat (id, NULL, 0 /* read/write */);
249     if (-1 == (intptr_t)shm)
250     {
251         msg_Err (vd, "shared memory attachment error: %m");
252         shmctl (id, IPC_RMID, 0);
253         free (res->p_sys);
254         return VLC_EGENERIC;
255     }
256
257     xcb_shm_seg_t segment;
258     if (attach)
259     {
260         /* Attach the segment to X */
261         xcb_void_cookie_t ck;
262
263         segment = xcb_generate_id (conn);
264         ck = xcb_shm_attach_checked (conn, segment, id, 1);
265
266         if (CheckError (vd, conn, "shared memory server-side error", ck))
267         {
268             msg_Info (vd, "using buggy X11 server - SSH proxying?");
269             segment = 0;
270         }
271     }
272     else
273         segment = 0;
274
275     shmctl (id, IPC_RMID, 0);
276     res->p_sys->segment = segment;
277     res->p->p_pixels = shm;
278     return VLC_SUCCESS;
279 }
280
281 /**
282  * Release picture private data: detach the shared memory segment.
283  */
284 void PictureResourceFree (picture_resource_t *res, xcb_connection_t *conn)
285 {
286     xcb_shm_seg_t segment = res->p_sys->segment;
287
288     if (conn != NULL && segment != 0)
289         xcb_shm_detach (conn, segment);
290     shmdt (res->p->p_pixels);
291 }
292