]> git.sesse.net Git - vlc/blob - modules/video_output/xcb/common.c
Use var_InheritString for --decklink-video-connection.
[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  * Connect to the X server.
43  */
44 static xcb_connection_t *Connect (vlc_object_t *obj, const char *display)
45 {
46     xcb_connection_t *conn = xcb_connect (display, NULL);
47     if (xcb_connection_has_error (conn) /*== NULL*/)
48     {
49         msg_Err (obj, "cannot connect to X server (%s)",
50                  display ? display : "default");
51         xcb_disconnect (conn);
52         return NULL;
53     }
54
55     const xcb_setup_t *setup = xcb_get_setup (conn);
56     msg_Dbg (obj, "connected to X%"PRIu16".%"PRIu16" server",
57              setup->protocol_major_version, setup->protocol_minor_version);
58     char *vendor = strndup (xcb_setup_vendor (setup), setup->vendor_len);
59     if (vendor)
60     {
61         msg_Dbg (obj, " vendor : %s", vendor);
62         free (vendor);
63     }
64     msg_Dbg (obj, " version: %"PRIu32, setup->release_number);
65     return conn;
66 }
67
68 /**
69  * Find screen matching a given root window.
70  */
71 static const xcb_screen_t *FindScreen (vlc_object_t *obj,
72                                        xcb_connection_t *conn,
73                                        xcb_window_t root)
74 {
75     /* Find the selected screen */
76     const xcb_setup_t *setup = xcb_get_setup (conn);
77     const xcb_screen_t *screen = NULL;
78     for (xcb_screen_iterator_t i = xcb_setup_roots_iterator (setup);
79          i.rem > 0 && screen == NULL; xcb_screen_next (&i))
80     {
81         if (i.data->root == root)
82             screen = i.data;
83     }
84
85     if (screen == NULL)
86     {
87         msg_Err (obj, "parent window screen not found");
88         return NULL;
89     }
90     msg_Dbg (obj, "using screen 0x%"PRIx32, root);
91     return screen;
92 }
93
94 static const xcb_screen_t *FindWindow (vlc_object_t *obj,
95                                        xcb_connection_t *conn,
96                                        xcb_window_t xid,
97                                        uint8_t *restrict pdepth)
98 {
99     xcb_get_geometry_reply_t *geo =
100         xcb_get_geometry_reply (conn, xcb_get_geometry (conn, xid), NULL);
101     if (geo == NULL)
102     {
103         msg_Err (obj, "parent window not valid");
104         return NULL;
105     }
106
107     const xcb_screen_t *screen = FindScreen (obj, conn, geo->root);
108     *pdepth = geo->depth;
109     free (geo);
110     return screen;
111 }
112
113
114 /**
115  * Create a VLC video X window object, connect to the corresponding X server,
116  * find the corresponding X server screen.
117  */
118 vout_window_t *GetWindow (vout_display_t *vd,
119                           xcb_connection_t **restrict pconn,
120                           const xcb_screen_t **restrict pscreen,
121                           uint8_t *restrict pdepth)
122 {
123     /* Get window */
124     vout_window_cfg_t wnd_cfg;
125
126     memset( &wnd_cfg, 0, sizeof(wnd_cfg) );
127     wnd_cfg.type = VOUT_WINDOW_TYPE_XID;
128     wnd_cfg.x = var_InheritInteger (vd, "video-x");
129     wnd_cfg.y = var_InheritInteger (vd, "video-y");
130     wnd_cfg.width  = vd->cfg->display.width;
131     wnd_cfg.height = vd->cfg->display.height;
132
133     vout_window_t *wnd = vout_display_NewWindow (vd, &wnd_cfg);
134     if (wnd == NULL)
135     {
136         msg_Err (vd, "parent window not available");
137         return NULL;
138     }
139
140     xcb_connection_t *conn = Connect (VLC_OBJECT(vd), wnd->display.x11);
141     if (conn == NULL)
142         goto error;
143     *pconn = conn;
144
145     *pscreen = FindWindow (VLC_OBJECT(vd), conn, wnd->handle.xid, pdepth);
146     if (*pscreen == NULL)
147     {
148         xcb_disconnect (conn);
149         goto error;
150     }
151
152     RegisterMouseEvents (VLC_OBJECT(vd), conn, wnd->handle.xid);
153     return wnd;
154
155 error:
156     vout_display_DeleteWindow (vd, wnd);
157     return NULL;
158 }
159
160 /** Check MIT-SHM shared memory support */
161 void CheckSHM (vlc_object_t *obj, xcb_connection_t *conn, bool *restrict pshm)
162 {
163     bool shm = var_InheritBool (obj, "x11-shm") > 0;
164     if (shm)
165     {
166         xcb_shm_query_version_cookie_t ck;
167         xcb_shm_query_version_reply_t *r;
168
169         ck = xcb_shm_query_version (conn);
170         r = xcb_shm_query_version_reply (conn, ck, NULL);
171         if (!r)
172         {
173             msg_Err (obj, "shared memory (MIT-SHM) not available");
174             msg_Warn (obj, "display will be slow");
175             shm = false;
176         }
177         free (r);
178     }
179     *pshm = shm;
180 }
181
182 /**
183  * Initialize a picture buffer as shared memory, according to the video output
184  * format. If a attach is true, the segment is attached to
185  * the X server (MIT-SHM extension).
186  */
187 int PictureResourceAlloc (vout_display_t *vd, picture_resource_t *res, size_t size,
188                           xcb_connection_t *conn, bool attach)
189 {
190     res->p_sys = malloc (sizeof(*res->p_sys));
191     if (!res->p_sys)
192         return VLC_EGENERIC;
193
194     /* Allocate shared memory segment */
195     int id = shmget (IPC_PRIVATE, size, IPC_CREAT | 0700);
196     if (id == -1)
197     {
198         msg_Err (vd, "shared memory allocation error: %m");
199         free (res->p_sys);
200         return VLC_EGENERIC;
201     }
202
203     /* Attach the segment to VLC */
204     void *shm = shmat (id, NULL, 0 /* read/write */);
205     if (-1 == (intptr_t)shm)
206     {
207         msg_Err (vd, "shared memory attachment error: %m");
208         shmctl (id, IPC_RMID, 0);
209         free (res->p_sys);
210         return VLC_EGENERIC;
211     }
212
213     xcb_shm_seg_t segment;
214     if (attach)
215     {
216         /* Attach the segment to X */
217         xcb_void_cookie_t ck;
218
219         segment = xcb_generate_id (conn);
220         ck = xcb_shm_attach_checked (conn, segment, id, 1);
221
222         if (CheckError (vd, conn, "shared memory server-side error", ck))
223         {
224             msg_Info (vd, "using buggy X11 server - SSH proxying?");
225             segment = 0;
226         }
227     }
228     else
229         segment = 0;
230
231     shmctl (id, IPC_RMID, 0);
232     res->p_sys->segment = segment;
233     res->p->p_pixels = shm;
234     return VLC_SUCCESS;
235 }
236
237 /**
238  * Release picture private data: detach the shared memory segment.
239  */
240 void PictureResourceFree (picture_resource_t *res, xcb_connection_t *conn)
241 {
242     xcb_shm_seg_t segment = res->p_sys->segment;
243
244     if (conn != NULL && segment != 0)
245         xcb_shm_detach (conn, segment);
246     shmdt (res->p->p_pixels);
247 }
248