screen: fix mouse file location
[vlc.git] / modules / access / screen / xcb.c
1 /**
2  * @file xcb.c
3  * @brief X11 C Bindings screen capture demux module for VLC media player
4  */
5 /*****************************************************************************
6  * Copyright © 2009 Rémi Denis-Courmont
7  *
8  * This program is free software; you can redistribute it and/or modify it
9  * under the terms of the GNU Lesser General Public License as published by
10  * the Free Software Foundation; either version 2.1 of the License, or
11  * (at your option) any later version.
12  *
13  * This program 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 Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public License
19  * along with this program; if not, write to the Free Software Foundation,
20  * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
21  *****************************************************************************/
22
23 #ifdef HAVE_CONFIG_H
24 # include <config.h>
25 #endif
26 #include <stdarg.h>
27 #include <assert.h>
28 #include <xcb/xcb.h>
29 #include <xcb/composite.h>
30 #ifdef HAVE_SYS_SHM_H
31 # include <sys/shm.h>
32 # include <xcb/shm.h>
33 #endif
34 #include <vlc_common.h>
35 #include <vlc_demux.h>
36 #include <vlc_plugin.h>
37
38 #define FPS_TEXT N_("Frame rate")
39 #define FPS_LONGTEXT N_( \
40     "How many times the screen content should be refreshed per second.")
41
42 #define LEFT_TEXT N_("Region left column")
43 #define LEFT_LONGTEXT N_( \
44     "Abscissa of the capture region in pixels.")
45
46 #define TOP_TEXT N_("Region top row")
47 #define TOP_LONGTEXT N_( \
48     "Ordinate of the capture region in pixels.")
49
50 #define WIDTH_TEXT N_("Capture region width")
51 #define WIDTH_LONGTEXT N_( \
52     "Pixel width of the capture region, or 0 for full width")
53
54 #define HEIGHT_TEXT N_("Capture region height")
55 #define HEIGHT_LONGTEXT N_( \
56     "Pixel height of the capture region, or 0 for full height")
57
58 #define FOLLOW_MOUSE_TEXT N_( "Follow the mouse" )
59 #define FOLLOW_MOUSE_LONGTEXT N_( \
60     "Follow the mouse when capturing a subscreen." )
61
62 static int  Open (vlc_object_t *);
63 static void Close (vlc_object_t *);
64
65 /*
66  * Module descriptor
67  */
68 vlc_module_begin ()
69     set_shortname (N_("Screen"))
70     set_description (N_("Screen capture (with X11/XCB)"))
71     set_category (CAT_INPUT)
72     set_subcategory (SUBCAT_INPUT_ACCESS)
73     set_capability ("access_demux", 0)
74     set_callbacks (Open, Close)
75
76     add_float ("screen-fps", 2.0, FPS_TEXT, FPS_LONGTEXT, true)
77     add_integer ("screen-left", 0, LEFT_TEXT, LEFT_LONGTEXT, true)
78         change_integer_range (-32768, 32767)
79         change_safe ()
80     add_integer ("screen-top", 0, TOP_TEXT, TOP_LONGTEXT, true)
81         change_integer_range (-32768, 32767)
82         change_safe ()
83     add_integer ("screen-width", 0, WIDTH_TEXT, WIDTH_LONGTEXT, true)
84         change_integer_range (0, 65535)
85         change_safe ()
86     add_integer ("screen-height", 0, HEIGHT_TEXT, HEIGHT_LONGTEXT, true)
87         change_integer_range (0, 65535)
88         change_safe ()
89     add_bool ("screen-follow-mouse", false, FOLLOW_MOUSE_TEXT,
90               FOLLOW_MOUSE_LONGTEXT, true)
91
92     add_shortcut ("screen", "window")
93 vlc_module_end ()
94
95 /*
96  * Local prototypes
97  */
98 static void Demux (void *);
99 static int Control (demux_t *, int, va_list);
100 static es_out_id_t *InitES (demux_t *, uint_fast16_t, uint_fast16_t,
101                             uint_fast8_t, uint8_t *);
102
103 struct demux_sys_t
104 {
105     /* All owned by timer thread while timer is armed: */
106     xcb_connection_t *conn; /**< XCB connection */
107     es_out_id_t      *es; /**< Window capture stream */
108     float             rate; /**< Frame rate */
109     xcb_window_t      window; /**< Captured window XID  */
110     xcb_pixmap_t      pixmap; /**< Pixmap for composited capture */
111     xcb_shm_seg_t     segment; /**< SHM segment XID */
112     int16_t           x, y; /**< Requested capture top-left coordinates */
113     uint16_t          w, h; /**< Requested capture pixel dimensions */
114     uint8_t           bpp; /**< Actual bytes per pixel *es */
115     bool              shm; /**< Whether to use MIT-SHM */
116     bool              follow_mouse;
117     uint16_t          cur_w, cur_h; /**< Actual capture pixel dimensions */
118     /* Timer does not use this, only input thread: */
119     vlc_timer_t       timer;
120 };
121
122 /** Checks MIT-SHM shared memory support */
123 static bool CheckSHM (xcb_connection_t *conn)
124 {
125 #ifdef HAVE_SYS_SHM_H
126     xcb_shm_query_version_cookie_t ck = xcb_shm_query_version (conn);
127     xcb_shm_query_version_reply_t *r;
128
129     r = xcb_shm_query_version_reply (conn, ck, NULL);
130     free (r);
131     return r != NULL;
132 #else
133     (void) conn;
134     return false;
135 #endif
136 }
137
138 /**
139  * Probes and initializes.
140  */
141 static int Open (vlc_object_t *obj)
142 {
143     demux_t *demux = (demux_t *)obj;
144     demux_sys_t *p_sys = malloc (sizeof (*p_sys));
145
146     if (p_sys == NULL)
147         return VLC_ENOMEM;
148     demux->p_sys = p_sys;
149
150     /* Connect to X server */
151     char *display = var_InheritString (obj, "x11-display");
152     int snum;
153     xcb_connection_t *conn = xcb_connect (display, &snum);
154     free (display);
155     if (xcb_connection_has_error (conn))
156     {
157         free (p_sys);
158         return VLC_EGENERIC;
159     }
160     p_sys->conn = conn;
161
162    /* Find configured screen */
163     if (!strcmp (demux->psz_access, "screen"))
164     {
165         const xcb_setup_t *setup = xcb_get_setup (conn);
166         const xcb_screen_t *scr = NULL;
167         for (xcb_screen_iterator_t i = xcb_setup_roots_iterator (setup);
168              i.rem > 0; xcb_screen_next (&i))
169         {
170             if (snum == 0)
171             {
172                scr = i.data;
173                 break;
174             }
175             snum--;
176         }
177         if (scr == NULL)
178         {
179             msg_Err (obj, "bad X11 screen number");
180             goto error;
181         }
182         p_sys->window = scr->root;
183     }
184     else
185     /* Determine capture window */
186     if (!strcmp (demux->psz_access, "window"))
187     {
188         char *end;
189         unsigned long ul = strtoul (demux->psz_location, &end, 0);
190         if (*end || ul > 0xffffffff)
191         {
192             msg_Err (obj, "bad X11 drawable %s", demux->psz_location);
193             goto error;
194         }
195         p_sys->window = ul;
196
197         xcb_composite_query_version_reply_t *r =
198             xcb_composite_query_version_reply (conn,
199                 xcb_composite_query_version (conn, 0, 4), NULL);
200         if (r == NULL || r->minor_version < 2)
201         {
202             msg_Err (obj, "X Composite extension not available");
203             free (r);
204             goto error;
205         }
206         msg_Dbg (obj, "using Composite extension v%"PRIu32".%"PRIu32,
207                  r->major_version, r->minor_version);
208         free (r);
209
210         xcb_composite_redirect_window (conn, p_sys->window,
211                                        XCB_COMPOSITE_REDIRECT_AUTOMATIC);
212     }
213     else
214         goto error;
215
216     /* Window properties */
217     p_sys->pixmap = xcb_generate_id (conn);
218     p_sys->segment = xcb_generate_id (conn);
219     p_sys->shm = CheckSHM (conn);
220     p_sys->w = var_InheritInteger (obj, "screen-width");
221     p_sys->h = var_InheritInteger (obj, "screen-height");
222     if (p_sys->w != 0 || p_sys->h != 0)
223         p_sys->follow_mouse = var_InheritBool (obj, "screen-follow-mouse");
224     else /* Following mouse is meaningless if width&height are dynamic. */
225         p_sys->follow_mouse = false;
226     if (!p_sys->follow_mouse) /* X and Y are meaningless if following mouse */
227     {
228         p_sys->x = var_InheritInteger (obj, "screen-left");
229         p_sys->y = var_InheritInteger (obj, "screen-top");
230     }
231
232     /* Initializes format */
233     p_sys->rate = var_InheritFloat (obj, "screen-fps");
234     if (!p_sys->rate)
235         goto error;
236
237     mtime_t interval = (float)CLOCK_FREQ / p_sys->rate;
238     if (!interval)
239         goto error;
240
241     p_sys->cur_w = 0;
242     p_sys->cur_h = 0;
243     p_sys->bpp = 0;
244     p_sys->es = NULL;
245     if (vlc_timer_create (&p_sys->timer, Demux, demux))
246         goto error;
247     vlc_timer_schedule (p_sys->timer, false, 1, interval);
248
249     /* Initializes demux */
250     demux->pf_demux   = NULL;
251     demux->pf_control = Control;
252     return VLC_SUCCESS;
253
254 error:
255     xcb_disconnect (p_sys->conn);
256     free (p_sys);
257     return VLC_EGENERIC;
258 }
259
260
261 /**
262  * Releases resources
263  */
264 static void Close (vlc_object_t *obj)
265 {
266     demux_t *demux = (demux_t *)obj;
267     demux_sys_t *p_sys = demux->p_sys;
268
269     vlc_timer_destroy (p_sys->timer);
270     xcb_disconnect (p_sys->conn);
271     free (p_sys);
272 }
273
274
275 /**
276  * Control callback
277  */
278 static int Control (demux_t *demux, int query, va_list args)
279 {
280     switch (query)
281     {
282         case DEMUX_GET_POSITION:
283         {
284             float *v = va_arg (args, float *);
285             *v = 0.;
286             return VLC_SUCCESS;
287         }
288
289         case DEMUX_GET_LENGTH:
290         case DEMUX_GET_TIME:
291         {
292             int64_t *v = va_arg (args, int64_t *);
293             *v = 0;
294             return VLC_SUCCESS;
295         }
296
297         /* TODO: get title info -> crawl visible windows */
298
299         case DEMUX_GET_PTS_DELAY:
300         {
301             int64_t *v = va_arg (args, int64_t *);
302             *v = INT64_C(1000) * var_InheritInteger (demux, "live-caching");
303             return VLC_SUCCESS;
304         }
305
306         case DEMUX_CAN_PAUSE:
307         case DEMUX_CAN_CONTROL_PACE:
308         case DEMUX_CAN_CONTROL_RATE:
309         case DEMUX_CAN_SEEK:
310         {
311             bool *v = va_arg (args, bool *);
312             *v = false;
313             return VLC_SUCCESS;
314         }
315
316         case DEMUX_SET_PAUSE_STATE:
317             return VLC_SUCCESS; /* should not happen */
318     }
319
320     return VLC_EGENERIC;
321 }
322
323
324 /**
325  * Processing callback
326  */
327 static void Demux (void *opaque)
328 {
329     demux_t *demux = opaque;
330     demux_sys_t *sys = demux->p_sys;
331     xcb_connection_t *conn = sys->conn;
332
333     /* Determine capture region */
334     xcb_get_geometry_cookie_t gc;
335     xcb_query_pointer_cookie_t qc;
336
337     gc = xcb_get_geometry (conn, sys->window);
338     if (sys->follow_mouse)
339         qc = xcb_query_pointer (conn, sys->window);
340
341     xcb_get_geometry_reply_t *geo = xcb_get_geometry_reply (conn, gc, NULL);
342     if (geo == NULL)
343     {
344         msg_Err (demux, "bad X11 drawable 0x%08"PRIx32, sys->window);
345 discard:
346         if (sys->follow_mouse)
347             xcb_discard_reply (conn, gc.sequence);
348         return;
349     }
350
351     int w = sys->w;
352     int h = sys->h;
353     int x, y;
354
355     if (sys->follow_mouse)
356     {
357         xcb_query_pointer_reply_t *ptr =
358             xcb_query_pointer_reply (conn, qc, NULL);
359         if (ptr == NULL)
360         {
361             free (geo);
362             return;
363         }
364
365         if (w == 0 || w > geo->width)
366             w = geo->width;
367         x = ptr->win_x;
368         if (x < w / 2)
369             x = 0;
370         else if (x >= (int)geo->width - (w / 2))
371             x = geo->width - w;
372         else
373             x -= w / 2;
374
375         if (h == 0 || h > geo->height)
376             h = geo->height;
377         y = ptr->win_y;
378         if (y < h / 2)
379             y = 0;
380         else if (y >= (int)geo->height - (h / 2))
381             y = geo->height - h;
382         else
383             y -= h / 2;
384     }
385     else
386     {
387         int max;
388
389         x = sys->x;
390         max = (int)geo->width - x;
391         if (max <= 0)
392             goto discard;
393         if (w == 0 || w > max)
394             w = max;
395
396         y = sys->y;
397         max = (int)geo->height - y;
398         if (max <= 0)
399             goto discard;
400         if (h == 0 || h > max)
401             h = max;
402     }
403
404     /* Update elementary stream format (if needed) */
405     if (w != sys->cur_w || h != sys->cur_h)
406     {
407         if (sys->es != NULL)
408             es_out_Del (demux->out, sys->es);
409
410         /* Update composite pixmap */
411         if (sys->window != geo->root)
412         {
413             xcb_free_pixmap (conn, sys->pixmap); /* no-op first time */
414             xcb_composite_name_window_pixmap (conn, sys->window, sys->pixmap);
415             xcb_create_pixmap (conn, geo->depth, sys->pixmap,
416                                geo->root, geo->width, geo->height);
417         }
418
419         sys->es = InitES (demux, w, h, geo->depth, &sys->bpp);
420         if (sys->es != NULL)
421         {
422             sys->cur_w = w;
423             sys->cur_h = h;
424             sys->bpp /= 8; /* bits -> bytes */
425         }
426     }
427
428     /* Capture screen */
429     xcb_drawable_t drawable =
430         (sys->window != geo->root) ? sys->pixmap : sys->window;
431     free (geo);
432
433     block_t *block = NULL;
434 #if HAVE_SYS_SHM_H
435     if (sys->shm)
436     {   /* Capture screen through shared memory */
437         size_t size = w * h * sys->bpp;
438         int id = shmget (IPC_PRIVATE, size, IPC_CREAT | 0777);
439         if (id == -1) /* XXX: fallback */
440         {
441             msg_Err (demux, "shared memory allocation error: %m");
442             goto noshm;
443         }
444
445         /* Attach the segment to X and capture */
446         xcb_shm_get_image_reply_t *img;
447         xcb_shm_get_image_cookie_t ck;
448
449         xcb_shm_attach (conn, sys->segment, id, 0 /* read/write */);
450         ck = xcb_shm_get_image (conn, drawable, x, y, w, h, ~0,
451                                 XCB_IMAGE_FORMAT_Z_PIXMAP, sys->segment, 0);
452         xcb_shm_detach (conn, sys->segment);
453         img = xcb_shm_get_image_reply (conn, ck, NULL);
454         xcb_flush (conn); /* ensure eventual detach */
455
456         if (img == NULL)
457         {
458             shmctl (id, IPC_RMID, 0);
459             goto noshm;
460         }
461         free (img);
462
463         /* Attach the segment to VLC */
464         void *shm = shmat (id, NULL, 0 /* read/write */);
465         shmctl (id, IPC_RMID, 0);
466         if (-1 == (intptr_t)shm)
467         {
468             msg_Err (demux, "shared memory attachment error: %m");
469             return;
470         }
471
472         block = block_shm_Alloc (shm, size);
473         if (unlikely(block == NULL))
474             shmdt (shm);
475     }
476 noshm:
477 #endif
478     if (block == NULL)
479     {   /* Capture screen through socket (fallback) */
480         xcb_get_image_reply_t *img;
481
482         img = xcb_get_image_reply (conn,
483             xcb_get_image (conn, XCB_IMAGE_FORMAT_Z_PIXMAP, drawable,
484                            x, y, w, h, ~0), NULL);
485         if (img == NULL)
486             return;
487
488         uint8_t *data = xcb_get_image_data (img);
489         size_t datalen = xcb_get_image_data_length (img);
490         block = block_heap_Alloc (img, data + datalen - (uint8_t *)img);
491         if (block == NULL)
492             return;
493         block->p_buffer = data;
494         block->i_buffer = datalen;
495     }
496
497     /* Send block - zero copy */
498     if (sys->es != NULL)
499     {
500         block->i_pts = block->i_dts = mdate ();
501
502         es_out_Control (demux->out, ES_OUT_SET_PCR, block->i_pts);
503         es_out_Send (demux->out, sys->es, block);
504     }
505 }
506
507 static es_out_id_t *InitES (demux_t *demux, uint_fast16_t width,
508                             uint_fast16_t height, uint_fast8_t depth,
509                             uint8_t *bpp)
510 {
511     demux_sys_t *p_sys = demux->p_sys;
512     const xcb_setup_t *setup = xcb_get_setup (p_sys->conn);
513     uint32_t chroma = 0;
514
515     for (const xcb_format_t *fmt = xcb_setup_pixmap_formats (setup),
516              *end = fmt + xcb_setup_pixmap_formats_length (setup);
517          fmt < end; fmt++)
518     {
519         if (fmt->depth != depth)
520             continue;
521         switch (fmt->depth)
522         {
523             case 32:
524                 if (fmt->bits_per_pixel == 32)
525                     chroma = VLC_CODEC_RGBA;
526                 break;
527             case 24:
528                 if (fmt->bits_per_pixel == 32)
529                     chroma = VLC_CODEC_RGB32;
530                 else if (fmt->bits_per_pixel == 24)
531                     chroma = VLC_CODEC_RGB24;
532                 break;
533             case 16:
534                 if (fmt->bits_per_pixel == 16)
535                     chroma = VLC_CODEC_RGB16;
536                 break;
537             case 15:
538                 if (fmt->bits_per_pixel == 16)
539                     chroma = VLC_CODEC_RGB15;
540                 break;
541             case 8: /* XXX: screw grey scale! */
542                 if (fmt->bits_per_pixel == 8)
543                     chroma = VLC_CODEC_RGB8;
544                 break;
545         }
546         if (chroma != 0)
547         {
548             *bpp = fmt->bits_per_pixel;
549             break;
550         }
551     }
552
553     if (!chroma)
554     {
555         msg_Err (demux, "unsupported pixmap formats");
556         return NULL;
557     }
558
559     es_format_t fmt;
560
561     es_format_Init (&fmt, VIDEO_ES, chroma);
562     fmt.video.i_chroma = chroma;
563     fmt.video.i_bits_per_pixel = *bpp;
564     fmt.video.i_sar_num = fmt.video.i_sar_den = 1;
565     fmt.video.i_frame_rate = 1000 * p_sys->rate;
566     fmt.video.i_frame_rate_base = 1000;
567     fmt.video.i_visible_width = fmt.video.i_width = width;
568     fmt.video.i_visible_height = fmt.video.i_height = height;
569
570     return es_out_Add (demux->out, &fmt);
571 }