Updated xcb vout module to the new API.
[vlc.git] / 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.h>
38 #include <vlc_vout_window.h>
39
40 #include "xcb_vlc.h"
41
42 /**
43  * Connect to the X server.
44  */
45 xcb_connection_t *Connect (vlc_object_t *obj)
46 {
47     char *display = var_CreateGetNonEmptyString (obj, "x11-display");
48     xcb_connection_t *conn = xcb_connect (display, NULL);
49
50     free (display);
51     if (xcb_connection_has_error (conn) /*== NULL*/)
52     {
53         msg_Err (obj, "cannot connect to X server");
54         xcb_disconnect (conn);
55         return NULL;
56     }
57     return conn;
58 }
59
60
61 /**
62  * Create a VLC video X window object, find the corresponding X server screen,
63  * and probe the MIT-SHM extension.
64  */
65 vout_window_t *GetWindow (vout_thread_t *obj,
66                           xcb_connection_t *conn,
67                           const xcb_screen_t **restrict pscreen,
68                           bool *restrict pshm)
69 {
70     /* Get window */
71     xcb_window_t root;
72     vout_window_cfg_t wnd_cfg;
73
74     memset( &wnd_cfg, 0, sizeof(wnd_cfg) );
75     wnd_cfg.type = VOUT_WINDOW_TYPE_XWINDOW;
76     wnd_cfg.width  = obj->i_window_width;
77     wnd_cfg.height = obj->i_window_height;
78
79     vout_window_t *wnd = vout_window_New (VLC_OBJECT(obj), NULL, &wnd_cfg);
80
81     if (wnd == NULL)
82     {
83         msg_Err (obj, "parent window not available");
84         return NULL;
85     }
86     else
87     {
88         xcb_get_geometry_reply_t *geo;
89         xcb_get_geometry_cookie_t ck;
90
91         ck = xcb_get_geometry (conn, wnd->handle.xid);
92         geo = xcb_get_geometry_reply (conn, ck, NULL);
93         if (geo == NULL)
94         {
95             msg_Err (obj, "parent window not valid");
96             goto error;
97         }
98         root = geo->root;
99         free (geo);
100
101         /* Subscribe to parent window resize events */
102         const uint32_t value = XCB_EVENT_MASK_STRUCTURE_NOTIFY;
103         xcb_change_window_attributes (conn, wnd->handle.xid,
104                                       XCB_CW_EVENT_MASK, &value);
105     }
106
107     /* Find the selected screen */
108     const xcb_setup_t *setup = xcb_get_setup (conn);
109     const xcb_screen_t *screen = NULL;
110     for (xcb_screen_iterator_t i = xcb_setup_roots_iterator (setup);
111          i.rem > 0 && screen == NULL; xcb_screen_next (&i))
112     {
113         if (i.data->root == root)
114             screen = i.data;
115     }
116
117     if (screen == NULL)
118     {
119         msg_Err (obj, "parent window screen not found");
120         goto error;
121     }
122     msg_Dbg (obj, "using screen 0x%"PRIx32, root);
123
124     /* Check MIT-SHM shared memory support */
125     bool shm = var_CreateGetBool (obj, "x11-shm") > 0;
126     if (shm)
127     {
128         xcb_shm_query_version_cookie_t ck;
129         xcb_shm_query_version_reply_t *r;
130
131         ck = xcb_shm_query_version (conn);
132         r = xcb_shm_query_version_reply (conn, ck, NULL);
133         if (!r)
134         {
135             msg_Err (obj, "shared memory (MIT-SHM) not available");
136             msg_Warn (obj, "display will be slow");
137             shm = false;
138         }
139         free (r);
140     }
141
142     *pscreen = screen;
143     *pshm = shm;
144     return wnd;
145
146 error:
147     vout_window_Delete (wnd);
148     return NULL;
149 }
150
151 /**
152  * Gets the size of an X window.
153  */
154 int GetWindowSize (struct vout_window_t *wnd, xcb_connection_t *conn,
155                    unsigned *restrict width, unsigned *restrict height)
156 {
157     xcb_get_geometry_cookie_t ck = xcb_get_geometry (conn, wnd->handle.xid);
158     xcb_get_geometry_reply_t *geo = xcb_get_geometry_reply (conn, ck, NULL);
159
160     if (!geo)
161         return -1;
162
163     *width = geo->width;
164     *height = geo->height;
165     free (geo);
166     return 0;
167 }
168
169 /**
170  * Initialize a picture buffer as shared memory, according to the video output
171  * format. If a XCB connection pointer is supplied, the segment is attached to
172  * the X server (MIT-SHM extension).
173  */
174 int PictureAlloc (vout_thread_t *vout, picture_t *pic, size_t size,
175                   xcb_connection_t *conn)
176 {
177     assert (pic->i_status == FREE_PICTURE);
178
179     /* Allocate shared memory segment */
180     int id = shmget (IPC_PRIVATE, size, IPC_CREAT | 0700);
181     if (id == -1)
182     {
183         msg_Err (vout, "shared memory allocation error: %m");
184         return VLC_EGENERIC;
185     }
186
187     /* Attach the segment to VLC */
188     void *shm = shmat (id, NULL, 0 /* read/write */);
189     if (-1 == (intptr_t)shm)
190     {
191         msg_Err (vout, "shared memory attachment error: %m");
192         shmctl (id, IPC_RMID, 0);
193         return VLC_EGENERIC;
194     }
195
196     xcb_shm_seg_t segment;
197     if (conn != NULL)
198     {
199         /* Attach the segment to X */
200         xcb_void_cookie_t ck;
201
202         segment = xcb_generate_id (conn);
203         ck = xcb_shm_attach_checked (conn, segment, id, 1);
204
205         if (CheckError (vout, "shared memory server-side error", ck))
206         {
207             msg_Info (vout, "using buggy X11 server - SSH proxying?");
208             segment = 0;
209         }
210     }
211     else
212         segment = 0;
213
214     shmctl (id, IPC_RMID, 0);
215     pic->p_sys = (void *)(uintptr_t)segment;
216     pic->p->p_pixels = shm;
217     pic->i_status = DESTROYED_PICTURE;
218     pic->i_type = DIRECT_PICTURE;
219     return VLC_SUCCESS;
220 }
221
222 /**
223  * Release picture private data: detach the shared memory segment.
224  */
225 void PictureFree (picture_t *pic, xcb_connection_t *conn)
226 {
227     xcb_shm_seg_t segment = (uintptr_t)pic->p_sys;
228
229     if (segment != 0)
230     {
231         assert (conn != NULL);
232         xcb_shm_detach (conn, segment);
233     }
234     shmdt (pic->p->p_pixels);
235 }
236
237 /**
238  * Video output thread management stuff.
239  * FIXME: Much of this should move to core
240  */
241 void CommonManage (vout_thread_t *vout)
242 {
243     if (vout->i_changes & VOUT_SCALE_CHANGE)
244     {
245         vout->b_autoscale = var_GetBool (vout, "autoscale");
246         vout->i_zoom = ZOOM_FP_FACTOR;
247         vout->i_changes &= ~VOUT_SCALE_CHANGE;
248         vout->i_changes |= VOUT_SIZE_CHANGE;
249     }
250
251     if (vout->i_changes & VOUT_ZOOM_CHANGE)
252     {
253         vout->b_autoscale = false;
254         vout->i_zoom = var_GetFloat (vout, "scale") * ZOOM_FP_FACTOR;
255         vout->i_changes &= ~VOUT_ZOOM_CHANGE;
256         vout->i_changes |= VOUT_SIZE_CHANGE;
257     }
258
259     if (vout->i_changes & VOUT_CROP_CHANGE)
260     {
261         vout->fmt_out.i_x_offset = vout->fmt_in.i_x_offset;
262         vout->fmt_out.i_y_offset = vout->fmt_in.i_y_offset;
263         vout->fmt_out.i_visible_width = vout->fmt_in.i_visible_width;
264         vout->fmt_out.i_visible_height = vout->fmt_in.i_visible_height;
265         vout->i_changes &= ~VOUT_CROP_CHANGE;
266         vout->i_changes |= VOUT_SIZE_CHANGE;
267     }
268
269     if (vout->i_changes & VOUT_ASPECT_CHANGE)
270     {
271         vout->fmt_out.i_aspect = vout->fmt_in.i_aspect;
272         vout->fmt_out.i_sar_num = vout->fmt_in.i_sar_num;
273         vout->fmt_out.i_sar_den = vout->fmt_in.i_sar_den;
274         vout->output.i_aspect = vout->fmt_in.i_aspect;
275         vout->i_changes &= ~VOUT_ASPECT_CHANGE;
276         vout->i_changes |= VOUT_SIZE_CHANGE;
277     }
278 }