]> git.sesse.net Git - vlc/blob - modules/video_output/xcb/common.c
XCB/XVideo: the supported resolution is not always maximum
[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 #ifdef HAVE_SYS_SHM_H
32 # include <sys/shm.h>
33 # include <sys/stat.h>
34 #endif
35
36 #include <xcb/xcb.h>
37 #include <xcb/shm.h>
38
39 #include <vlc_common.h>
40 #include <vlc_vout_display.h>
41
42 #include "xcb_vlc.h"
43
44 /**
45  * Connect to the X server.
46  */
47 static xcb_connection_t *Connect (vlc_object_t *obj, const char *display)
48 {
49     xcb_connection_t *conn = xcb_connect (display, NULL);
50     if (xcb_connection_has_error (conn) /*== NULL*/)
51     {
52         msg_Err (obj, "cannot connect to X server (%s)",
53                  display ? display : "default");
54         xcb_disconnect (conn);
55         return NULL;
56     }
57
58     const xcb_setup_t *setup = xcb_get_setup (conn);
59     msg_Dbg (obj, "connected to X%"PRIu16".%"PRIu16" server",
60              setup->protocol_major_version, setup->protocol_minor_version);
61     char *vendor = strndup (xcb_setup_vendor (setup), setup->vendor_len);
62     if (vendor)
63     {
64         msg_Dbg (obj, " vendor : %s", vendor);
65         free (vendor);
66     }
67     msg_Dbg (obj, " version: %"PRIu32, setup->release_number);
68     return conn;
69 }
70
71 /**
72  * Find screen matching a given root window.
73  */
74 static const xcb_screen_t *FindScreen (vlc_object_t *obj,
75                                        xcb_connection_t *conn,
76                                        xcb_window_t root)
77 {
78     /* Find the selected screen */
79     const xcb_setup_t *setup = xcb_get_setup (conn);
80     const xcb_screen_t *screen = NULL;
81     for (xcb_screen_iterator_t i = xcb_setup_roots_iterator (setup);
82          i.rem > 0 && screen == NULL; xcb_screen_next (&i))
83     {
84         if (i.data->root == root)
85             screen = i.data;
86     }
87
88     if (screen == NULL)
89     {
90         msg_Err (obj, "parent window screen not found");
91         return NULL;
92     }
93     msg_Dbg (obj, "using screen 0x%"PRIx32, root);
94     return screen;
95 }
96
97 static const xcb_screen_t *FindWindow (vlc_object_t *obj,
98                                        xcb_connection_t *conn,
99                                        xcb_window_t xid,
100                                        uint8_t *restrict pdepth)
101 {
102     xcb_get_geometry_reply_t *geo =
103         xcb_get_geometry_reply (conn, xcb_get_geometry (conn, xid), NULL);
104     if (geo == NULL)
105     {
106         msg_Err (obj, "parent window not valid");
107         return NULL;
108     }
109
110     const xcb_screen_t *screen = FindScreen (obj, conn, geo->root);
111     *pdepth = geo->depth;
112     free (geo);
113     return screen;
114 }
115
116
117 /**
118  * Create a VLC video X window object, connect to the corresponding X server,
119  * find the corresponding X server screen.
120  */
121 vout_window_t *GetWindow (vout_display_t *vd,
122                           xcb_connection_t **restrict pconn,
123                           const xcb_screen_t **restrict pscreen,
124                           uint8_t *restrict pdepth)
125 {
126     /* Get window */
127     vout_window_cfg_t wnd_cfg;
128
129     memset( &wnd_cfg, 0, sizeof(wnd_cfg) );
130     wnd_cfg.type = VOUT_WINDOW_TYPE_XID;
131     wnd_cfg.x = var_InheritInteger (vd, "video-x");
132     wnd_cfg.y = var_InheritInteger (vd, "video-y");
133     wnd_cfg.width  = vd->cfg->display.width;
134     wnd_cfg.height = vd->cfg->display.height;
135
136     vout_window_t *wnd = vout_display_NewWindow (vd, &wnd_cfg);
137     if (wnd == NULL)
138     {
139         msg_Err (vd, "parent window not available");
140         return NULL;
141     }
142
143     xcb_connection_t *conn = Connect (VLC_OBJECT(vd), wnd->display.x11);
144     if (conn == NULL)
145         goto error;
146     *pconn = conn;
147
148     *pscreen = FindWindow (VLC_OBJECT(vd), conn, wnd->handle.xid, pdepth);
149     if (*pscreen == NULL)
150     {
151         xcb_disconnect (conn);
152         goto error;
153     }
154
155     RegisterMouseEvents (VLC_OBJECT(vd), conn, wnd->handle.xid);
156     return wnd;
157
158 error:
159     vout_display_DeleteWindow (vd, wnd);
160     return NULL;
161 }
162
163 /** Check MIT-SHM shared memory support */
164 bool CheckSHM (vlc_object_t *obj, xcb_connection_t *conn)
165 {
166 #ifdef HAVE_SYS_SHM_H
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 != NULL)
173     {
174         free (r);
175         return true;
176     }
177     msg_Err (obj, "shared memory (MIT-SHM) not available");
178     msg_Warn (obj, "display will be slow");
179 #else
180     msg_Warn (obj, "shared memory (MIT-SHM) not implemented");
181     (void) conn;
182 #endif
183     return false;
184 }
185
186 /**
187  * Initialize a picture buffer as shared memory, according to the video output
188  * format. If a attach is true, the segment is attached to
189  * the X server (MIT-SHM extension).
190  */
191 int PictureResourceAlloc (vout_display_t *vd, picture_resource_t *res, size_t size,
192                           xcb_connection_t *conn, bool attach)
193 {
194     res->p_sys = malloc (sizeof(*res->p_sys));
195     if (!res->p_sys)
196         return VLC_EGENERIC;
197
198 #ifdef HAVE_SYS_SHM_H
199     /* Allocate shared memory segment */
200     int id = shmget (IPC_PRIVATE, size, IPC_CREAT | S_IRWXU);
201     if (id == -1)
202     {
203         msg_Err (vd, "shared memory allocation error: %m");
204         free (res->p_sys);
205         return VLC_EGENERIC;
206     }
207
208     /* Attach the segment to VLC */
209     void *shm = shmat (id, NULL, 0 /* read/write */);
210     if (-1 == (intptr_t)shm)
211     {
212         msg_Err (vd, "shared memory attachment error: %m");
213         shmctl (id, IPC_RMID, 0);
214         free (res->p_sys);
215         return VLC_EGENERIC;
216     }
217
218     xcb_shm_seg_t segment;
219     if (attach)
220     {
221         /* Attach the segment to X */
222         xcb_void_cookie_t ck;
223
224         segment = xcb_generate_id (conn);
225         ck = xcb_shm_attach_checked (conn, segment, id, 1);
226
227         switch (CheckError (vd, conn, "shared memory server-side error", ck))
228         {
229             case 0:
230                 break;
231
232             case XCB_ACCESS:
233             {
234                 struct shmid_ds buf;
235                 /* Retry with promiscuous permissions */
236                 shmctl (id, IPC_STAT, &buf);
237                 buf.shm_perm.mode |= S_IRGRP|S_IROTH;
238                 shmctl (id, IPC_SET, &buf);
239                 ck = xcb_shm_attach_checked (conn, segment, id, 1);
240                 if (CheckError (vd, conn, "same error on retry", ck) == 0)
241                     break;
242                 /* fall through */
243             }
244
245             default:
246                 msg_Info (vd, "using buggy X11 server - SSH proxying?");
247                 segment = 0;
248         }
249     }
250     else
251         segment = 0;
252
253     shmctl (id, IPC_RMID, NULL);
254     res->p_sys->segment = segment;
255     res->p->p_pixels = shm;
256 #else
257     assert (!attach);
258     res->p_sys->segment = 0;
259
260     /* XXX: align on 32 bytes for VLC chroma filters */
261     res->p->p_pixels = malloc (size);
262     if (unlikely(res->p->p_pixels == NULL))
263     {
264         free (res->p_sys);
265         return VLC_EGENERIC;
266     }
267 #endif
268     return VLC_SUCCESS;
269 }
270
271 /**
272  * Release picture private data: detach the shared memory segment.
273  */
274 void PictureResourceFree (picture_resource_t *res, xcb_connection_t *conn)
275 {
276 #ifdef HAVE_SYS_SHM_H
277     xcb_shm_seg_t segment = res->p_sys->segment;
278
279     if (conn != NULL && segment != 0)
280         xcb_shm_detach (conn, segment);
281     shmdt (res->p->p_pixels);
282 #else
283     free (res->p->p_pixels);
284 #endif
285 }
286