]> git.sesse.net Git - vlc/blob - modules/video_output/xcb/common.c
Revert "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  * 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.width  = vd->cfg->display.width;
129     wnd_cfg.height = vd->cfg->display.height;
130
131     vout_window_t *wnd = vout_display_NewWindow (vd, &wnd_cfg);
132     if (wnd == NULL)
133     {
134         msg_Err (vd, "parent window not available");
135         return NULL;
136     }
137
138     xcb_connection_t *conn = Connect (VLC_OBJECT(vd), wnd->x11_display);
139     if (conn == NULL)
140         goto error;
141     *pconn = conn;
142
143     *pscreen = FindWindow (VLC_OBJECT(vd), conn, wnd->handle.xid, pdepth);
144     if (*pscreen == NULL)
145     {
146         xcb_disconnect (conn);
147         goto error;
148     }
149
150     RegisterMouseEvents (VLC_OBJECT(vd), conn, wnd->handle.xid);
151     return wnd;
152
153 error:
154     vout_display_DeleteWindow (vd, wnd);
155     return NULL;
156 }
157
158 /** Check MIT-SHM shared memory support */
159 void CheckSHM (vlc_object_t *obj, xcb_connection_t *conn, bool *restrict pshm)
160 {
161     bool shm = var_CreateGetBool (obj, "x11-shm") > 0;
162     if (shm)
163     {
164         xcb_shm_query_version_cookie_t ck;
165         xcb_shm_query_version_reply_t *r;
166
167         ck = xcb_shm_query_version (conn);
168         r = xcb_shm_query_version_reply (conn, ck, NULL);
169         if (!r)
170         {
171             msg_Err (obj, "shared memory (MIT-SHM) not available");
172             msg_Warn (obj, "display will be slow");
173             shm = false;
174         }
175         free (r);
176     }
177     *pshm = shm;
178 }
179
180 /**
181  * Initialize a picture buffer as shared memory, according to the video output
182  * format. If a attach is true, the segment is attached to
183  * the X server (MIT-SHM extension).
184  */
185 int PictureResourceAlloc (vout_display_t *vd, picture_resource_t *res, size_t size,
186                           xcb_connection_t *conn, bool attach)
187 {
188     res->p_sys = malloc (sizeof(*res->p_sys));
189     if (!res->p_sys)
190         return VLC_EGENERIC;
191
192     /* Allocate shared memory segment */
193     int id = shmget (IPC_PRIVATE, size, IPC_CREAT | 0700);
194     if (id == -1)
195     {
196         msg_Err (vd, "shared memory allocation error: %m");
197         free (res->p_sys);
198         return VLC_EGENERIC;
199     }
200
201     /* Attach the segment to VLC */
202     void *shm = shmat (id, NULL, 0 /* read/write */);
203     if (-1 == (intptr_t)shm)
204     {
205         msg_Err (vd, "shared memory attachment error: %m");
206         shmctl (id, IPC_RMID, 0);
207         free (res->p_sys);
208         return VLC_EGENERIC;
209     }
210
211     xcb_shm_seg_t segment;
212     if (attach)
213     {
214         /* Attach the segment to X */
215         xcb_void_cookie_t ck;
216
217         segment = xcb_generate_id (conn);
218         ck = xcb_shm_attach_checked (conn, segment, id, 1);
219
220         if (CheckError (vd, conn, "shared memory server-side error", ck))
221         {
222             msg_Info (vd, "using buggy X11 server - SSH proxying?");
223             segment = 0;
224         }
225     }
226     else
227         segment = 0;
228
229     shmctl (id, IPC_RMID, 0);
230     res->p_sys->segment = segment;
231     res->p->p_pixels = shm;
232     return VLC_SUCCESS;
233 }
234
235 /**
236  * Release picture private data: detach the shared memory segment.
237  */
238 void PictureResourceFree (picture_resource_t *res, xcb_connection_t *conn)
239 {
240     xcb_shm_seg_t segment = res->p_sys->segment;
241
242     if (conn != NULL && segment != 0)
243         xcb_shm_detach (conn, segment);
244     shmdt (res->p->p_pixels);
245 }
246