Wayland/screen: remove useless callbacks
[vlc.git] / modules / access / screen / wayland.c
1 /**
2  * @file wayland.c
3  * @brief Wayland screenshooter extension module for VLC media player
4  */
5 /*****************************************************************************
6  * Copyright © 2014 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
27 #include <errno.h>
28 #include <stdarg.h>
29 #include <stdbool.h>
30 #include <stdint.h>
31 #include <fcntl.h>
32 #include <poll.h>
33 #include <sys/types.h>
34 #include <unistd.h>
35
36 #include <wayland-client.h>
37 #include "screenshooter-client-protocol.h"
38
39 #include <vlc_common.h>
40 #include <vlc_demux.h>
41 #include <vlc_plugin.h>
42
43 struct demux_sys_t
44 {
45     struct wl_display *display;
46     struct wl_output *output;
47     struct wl_shm *shm;
48     struct screenshooter *screenshooter;
49     es_out_id_t *es;
50
51     long pagemask;
52     float rate;
53     int32_t x; /*< Requested horizontal offset */
54     int32_t y; /*< Requested vertical offset */
55     int32_t w; /*< Requested width */
56     int32_t h; /*< Requested height */
57     int32_t width; /*< Actual width */
58     int32_t height; /*< Actual height */
59
60     bool done;
61     mtime_t start;
62
63     vlc_thread_t thread;
64 };
65
66 static bool DisplayError(vlc_object_t *obj, struct wl_display *display)
67 {
68     int val = wl_display_get_error(display);
69     if (val == 0)
70         return false;
71
72     if (val == EPROTO)
73     {
74         const struct wl_interface *iface;
75         uint32_t id;
76
77         val = wl_display_get_protocol_error(display, &iface, &id);
78         msg_Err(obj, "display protocol error %d on %s object %"PRIu32,
79                 val, iface->name, id);
80     }
81     else
82         msg_Err(obj, "display fatal error: %s", vlc_strerror_c(val));
83
84     return true;
85 }
86 #define DisplayError(o,d) DisplayError(VLC_OBJECT(o), (d))
87
88 static void output_geometry_cb(void *data, struct wl_output *output, int32_t x,
89                                int32_t y, int32_t width, int32_t height,
90                                int32_t subpixel, const char *vendor,
91                                const char *model, int32_t transform)
92 {
93     demux_t *demux = data;
94
95     msg_Dbg(demux, "output geometry: %s %s %"PRId32"x%"PRId32"mm "
96             "@ %"PRId32"x%"PRId32" subpixel: %"PRId32" transform: %"PRId32,
97             vendor, model, width, height, x, y, subpixel, transform);
98     (void) output;
99 }
100
101 static void output_mode_cb(void *data, struct wl_output *output,
102                            uint32_t flags, int32_t width, int32_t height,
103                            int32_t refresh)
104 {
105     demux_t *demux = data;
106     demux_sys_t *sys = demux->p_sys;
107
108     msg_Dbg(demux, "output mode: 0x%08"PRIX32" %"PRId32"x%"PRId32
109             " %"PRId32"mHz%s", flags, width, height, refresh,
110             (flags & WL_OUTPUT_MODE_CURRENT) ? " (current)" : "");
111
112     if (!(flags & WL_OUTPUT_MODE_CURRENT))
113         return;
114     if (width <= sys->x || height <= sys->y)
115         return;
116
117     if (sys->es != NULL)
118         es_out_Del(demux->out, sys->es);
119
120     es_format_t fmt;
121
122     es_format_Init(&fmt, VIDEO_ES, VLC_CODEC_RGB32);
123     fmt.video.i_chroma = VLC_CODEC_RGB32;
124     fmt.video.i_bits_per_pixel = 32;
125     fmt.video.i_sar_num = fmt.video.i_sar_den = 1;
126     fmt.video.i_frame_rate = lroundf(1000.f * sys->rate);
127     fmt.video.i_frame_rate_base = 1000;
128     fmt.video.i_width = width;
129
130     if (sys->w != 0 && width > sys->w + sys->x)
131         fmt.video.i_visible_width = sys->w;
132     else
133         fmt.video.i_visible_width = width - sys->x;
134
135     if (sys->h != 0 && height > sys->h + sys->y)
136         fmt.video.i_visible_height = sys->h;
137     else
138         fmt.video.i_visible_height = height - sys->y;
139
140     fmt.video.i_height = fmt.video.i_visible_height;
141
142     sys->es = es_out_Add(demux->out, &fmt);
143     sys->width = width;
144     sys->height = height;
145     (void) output;
146 }
147
148 const struct wl_output_listener output_cbs =
149 {
150     output_geometry_cb,
151     output_mode_cb,
152     NULL,
153     NULL,
154 };
155
156 static void screenshooter_done_cb(void *data,
157                                   struct screenshooter *screenshooter)
158 {
159     bool *done = data;
160
161     *done = true;
162     (void) screenshooter;
163 }
164
165 const struct screenshooter_listener screenshooter_cbs =
166 {
167     screenshooter_done_cb,
168 };
169
170 static block_t *Shoot(demux_t *demux)
171 {
172     demux_sys_t *sys = demux->p_sys;
173
174     char bufpath[] = "/tmp/"PACKAGE_NAME"XXXXXX";
175     int fd = mkostemp(bufpath, O_CLOEXEC);
176     if (fd == -1)
177     {
178         msg_Err(demux, "buffer creation error: %s", vlc_strerror_c(errno));
179         return NULL;
180     }
181     unlink(bufpath);
182
183     /* NOTE: one extra line for overflow if screen-left > 0 */
184     uint32_t pitch = 4u * sys->width;
185     size_t size = (pitch * (sys->height + 1) + sys->pagemask) & ~sys->pagemask;
186     block_t *block = NULL;
187
188     if (ftruncate(fd, size) < 0)
189     {
190         msg_Err(demux, "buffer allocation error: %s", vlc_strerror_c(errno));
191         goto out;
192     }
193
194     struct wl_shm_pool *pool = wl_shm_create_pool(sys->shm, fd, size);
195     if (pool == NULL)
196         goto out;
197
198     struct wl_buffer *buffer;
199     buffer = wl_shm_pool_create_buffer(pool, 0, sys->width, sys->height,
200                                        pitch, WL_SHM_FORMAT_XRGB8888);
201     wl_shm_pool_destroy(pool);
202     if (buffer == NULL)
203         goto out;
204
205     sys->done = false;
206     screenshooter_shoot(sys->screenshooter, sys->output, buffer);
207
208     while (!sys->done)
209         wl_display_roundtrip(sys->display);
210
211     wl_buffer_destroy(buffer);
212     block = block_File(fd);
213
214     if (block != NULL)
215     {
216         size_t skip = (sys->y * sys->width + sys->x) * 4;
217
218         block->p_buffer += skip;
219         block->i_buffer -= skip;
220     }
221
222 out:
223     close(fd);
224     return block;
225 }
226
227 static void cleanup_wl_display_read(void *data)
228 {
229     struct wl_display *display = data;
230
231     wl_display_cancel_read(display);
232 }
233
234 static void *Thread(void *data)
235 {
236     demux_t *demux = data;
237     demux_sys_t *sys = demux->p_sys;
238     struct wl_display *display = sys->display;
239     struct pollfd ufd[1];
240     unsigned interval = lroundf(CLOCK_FREQ / (sys->rate * 1000.f));
241
242     int canc = vlc_savecancel();
243     vlc_cleanup_push(cleanup_wl_display_read, display);
244
245     ufd[0].fd = wl_display_get_fd(display);
246     ufd[0].events = POLLIN;
247
248     for (;;)
249     {
250         if (DisplayError(demux, display))
251             break;
252
253         if (sys->es != NULL)
254         {
255             block_t *block = Shoot(demux);
256
257             block->i_pts = block->i_dts = mdate();
258             es_out_Control(demux->out, ES_OUT_SET_PCR, block->i_pts);
259             es_out_Send(demux->out, sys->es, block);
260         }
261
262         while (wl_display_prepare_read(display) != 0)
263             wl_display_dispatch_pending(display);
264         wl_display_flush(display);
265         vlc_restorecancel(canc);
266
267         while (poll(ufd, 1, interval) < 0);
268
269         canc = vlc_savecancel();
270         wl_display_read_events(display);
271         wl_display_dispatch_pending(display);
272     }
273     vlc_cleanup_pop();
274     vlc_restorecancel(canc);
275     return NULL;
276 }
277
278 static int Control(demux_t *demux, int query, va_list args)
279 {
280     demux_sys_t *sys = demux->p_sys;
281
282     switch (query)
283     {
284         case DEMUX_GET_POSITION:
285             *va_arg(args, float *) = 0.f;
286             break;
287
288         case DEMUX_GET_LENGTH:
289             *va_arg(args, int64_t *) = 0;
290             break;
291
292         case DEMUX_GET_TIME:
293             *va_arg(args, int64_t *) = mdate() - sys->start;
294             break;
295
296         case DEMUX_GET_FPS:
297             *va_arg(args, float *) = sys->rate;
298             break;
299
300         case DEMUX_CAN_PAUSE:
301             *va_arg(args, bool *) = false; /* TODO */
302             break;
303         //case DEMUX_SET_PAUSE_STATE:
304         //    break;
305
306         case DEMUX_GET_PTS_DELAY:
307             *va_arg(args, int64_t *) = INT64_C(1000)
308                 * var_InheritInteger(demux, "live-caching");
309             break;
310
311         case DEMUX_CAN_CONTROL_PACE:
312         case DEMUX_CAN_CONTROL_RATE:
313         case DEMUX_CAN_SEEK:
314             *va_arg(args, bool *) = false;
315             break;
316
317         default:
318             return VLC_EGENERIC;
319     }
320
321     return VLC_SUCCESS;
322 }
323
324 static void registry_global_cb(void *data, struct wl_registry *registry,
325                                uint32_t name, const char *iface, uint32_t vers)
326 {
327     demux_t *demux = data;
328     demux_sys_t *sys = demux->p_sys;
329
330     msg_Dbg(demux, "global %3"PRIu32": %s version %"PRIu32, name, iface, vers);
331
332     if (!strcmp(iface, "wl_output"))
333         sys->output = wl_registry_bind(registry, name, &wl_output_interface,
334                                        1);
335     else
336     if (!strcmp(iface, "wl_shm"))
337         sys->shm = wl_registry_bind(registry, name, &wl_shm_interface, 1);
338     else
339     if (!strcmp(iface, "screenshooter"))
340         sys->screenshooter = wl_registry_bind(registry, name,
341                                               &screenshooter_interface, 1);
342 }
343
344 static void registry_global_remove_cb(void *data, struct wl_registry *registry,
345                                       uint32_t name)
346 {
347     demux_t *demux = data;
348
349     msg_Dbg(demux, "global remove %3"PRIu32, name);
350     (void) registry;
351 }
352
353 static const struct wl_registry_listener registry_cbs =
354 {
355     registry_global_cb,
356     registry_global_remove_cb,
357 };
358
359 static int Open(vlc_object_t *obj)
360 {
361     demux_t *demux = (demux_t *)obj;
362     demux_sys_t *sys = malloc(sizeof (*sys));
363     if (unlikely(sys == NULL))
364         return VLC_ENOMEM;
365
366     /* Connect to the display server */
367     char *dpy_name = var_InheritString(demux, "wl-display");
368     sys->display = wl_display_connect(dpy_name);
369     free(dpy_name);
370
371     if (sys->display == NULL)
372     {
373         free(sys);
374         return VLC_EGENERIC;
375     }
376
377     sys->output = NULL;
378     sys->shm = NULL;
379     sys->screenshooter = NULL;
380     sys->es = NULL;
381     sys->pagemask = sysconf(_SC_PAGE_SIZE) - 1;
382     sys->rate = var_InheritFloat(demux, "screen-fps");
383     sys->x = var_InheritInteger(demux, "screen-left");
384     sys->y = var_InheritInteger(demux, "screen-top");
385     sys->w = var_InheritInteger(demux, "screen-width");
386     sys->h = var_InheritInteger(demux, "screen-height");
387
388     if (1000.f * sys->rate <= 0x1.p-30)
389         goto error;
390
391     demux->p_sys = sys;
392
393     /* Find the interesting singleton(s) */
394     struct wl_registry *registry = wl_display_get_registry(sys->display);
395     if (registry == NULL)
396         goto error;
397
398     wl_registry_add_listener(registry, &registry_cbs, demux);
399     wl_display_roundtrip(sys->display);
400     wl_registry_destroy(registry);
401
402     if (sys->output == NULL || sys->shm == NULL || sys->screenshooter == NULL)
403     {
404         msg_Err(demux, "screenshooter extension not supported");
405         goto error;
406     }
407
408     wl_output_add_listener(sys->output, &output_cbs, demux);
409     screenshooter_add_listener(sys->screenshooter, &screenshooter_cbs,
410                                &sys->done);
411     wl_display_roundtrip(sys->display);
412
413     if (DisplayError(demux, sys->display))
414         goto error;
415
416     /* Initializes demux */
417     sys->start = mdate();
418
419     if (vlc_clone(&sys->thread, Thread, demux, VLC_THREAD_PRIORITY_INPUT))
420         goto error;
421
422     demux->pf_demux = NULL;
423     demux->pf_control = Control;
424     return VLC_SUCCESS;
425
426 error:
427     if (sys->screenshooter != NULL)
428         screenshooter_destroy(sys->screenshooter);
429     if (sys->shm != NULL)
430         wl_shm_destroy(sys->shm);
431     if (sys->output != NULL)
432         wl_output_destroy(sys->output);
433     wl_display_disconnect(sys->display);
434     free(sys);
435     return VLC_EGENERIC;
436 }
437
438 static void Close(vlc_object_t *obj)
439 {
440     demux_t *demux = (demux_t *)obj;
441     demux_sys_t *sys = demux->p_sys;
442
443     vlc_cancel(sys->thread);
444     vlc_join(sys->thread, NULL);
445
446     screenshooter_destroy(sys->screenshooter);
447     wl_shm_destroy(sys->shm);
448     wl_output_destroy(sys->output);
449     wl_display_disconnect(sys->display);
450     free(sys);
451 }
452
453 #define FPS_TEXT N_("Frame rate")
454 #define FPS_LONGTEXT N_( \
455     "How many times the screen content should be refreshed per second.")
456
457 #define LEFT_TEXT N_("Region left column")
458 #define LEFT_LONGTEXT N_( \
459     "Abscissa of the capture region in pixels.")
460
461 #define TOP_TEXT N_("Region top row")
462 #define TOP_LONGTEXT N_( \
463     "Ordinate of the capture region in pixels.")
464
465 #define WIDTH_TEXT N_("Capture region width")
466 #define WIDTH_LONGTEXT N_( \
467     "Pixel width of the capture region, or 0 for full width")
468
469 #define HEIGHT_TEXT N_("Capture region height")
470 #define HEIGHT_LONGTEXT N_( \
471     "Pixel height of the capture region, or 0 for full height")
472
473 vlc_module_begin ()
474     set_shortname (N_("Screen"))
475     set_description (N_("Screen capture (with Wayland)"))
476     set_category (CAT_INPUT)
477     set_subcategory (SUBCAT_INPUT_ACCESS)
478     set_capability ("access_demux", 0)
479     set_callbacks (Open, Close)
480
481     /* XXX: VLC core does not support multiple configuration items with the
482      * same name. So all default values and ranges must be the same as for XCB
483      * for the time being. */
484     add_float ("screen-fps", 2.0, FPS_TEXT, FPS_LONGTEXT, true)
485     add_integer ("screen-left", 0, LEFT_TEXT, LEFT_LONGTEXT, true)
486         change_integer_range (-32768, 32767)
487         change_safe ()
488     add_integer ("screen-top", 0, TOP_TEXT, TOP_LONGTEXT, true)
489         change_integer_range (-32768, 32767)
490         change_safe ()
491     add_integer ("screen-width", 0, WIDTH_TEXT, WIDTH_LONGTEXT, true)
492         change_integer_range (0, 65535)
493         change_safe ()
494     add_integer ("screen-height", 0, HEIGHT_TEXT, HEIGHT_LONGTEXT, true)
495         change_integer_range (0, 65535)
496         change_safe ()
497     add_shortcut ("screen")
498 vlc_module_end ()