b68f4add867ba58e1d76791b9334d442a541f304
[src/agl-compositor.git] / clients / screenshooter.c
1 /*
2  * Copyright © 2020 Collabora, Ltd.
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining
5  * a copy of this software and associated documentation files (the
6  * "Software"), to deal in the Software without restriction, including
7  * without limitation the rights to use, copy, modify, merge, publish,
8  * distribute, sublicense, and/or sell copies of the Software, and to
9  * permit persons to whom the Software is furnished to do so, subject to
10  * the following conditions:
11  *
12  * The above copyright notice and this permission notice (including the
13  * next paragraph) shall be included in all copies or substantial
14  * portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19  * NONINFRINGEMENT.  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
20  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
21  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23  * SOFTWARE.
24  */
25
26 #include <stdint.h>
27 #include <errno.h>
28 #include <stdlib.h>
29 #include <stdio.h>
30 #include <string.h>
31 #include <fcntl.h>
32 #include <unistd.h>
33 #include <limits.h>
34 #include <sys/param.h>
35 #include <sys/mman.h>
36 #include <getopt.h>
37 #include <cairo.h>
38
39 #include <wayland-client.h>
40 #include "shared/helpers.h"
41 #include "shared/xalloc.h"
42 #include "shared/file-util.h"
43 #include "shared/os-compatibility.h"
44 #include "agl-screenshooter-client-protocol.h"
45 #include "xdg-output-unstable-v1-client-protocol.h"
46
47 struct screenshooter_data;
48
49 struct screenshooter_output {
50         struct wl_output *output;
51         struct wl_buffer *buffer;
52
53         int width, height, offset_x, offset_y, scale;
54         void *data;
55         struct screenshooter_data *sh_data;
56
57         struct wl_list link;    /** screenshooter_data::output_list */
58 };
59
60 struct xdg_output_v1_info {
61         struct zxdg_output_v1 *xdg_output;
62         struct screenshooter_output *output;
63
64         char *name, *description;
65         struct wl_list link;    /** screenshooter_data::xdg_output_list */
66 };
67
68
69 struct buffer_size {
70         int width, height;
71
72         int min_x, min_y;
73         int max_x, max_y;
74 };
75
76 struct screenshooter_data {
77         struct wl_display *display;
78         struct wl_shm *shm;
79         struct wl_list output_list;     /** screenshooter_output::link */
80         struct wl_list xdg_output_list; /** xdg_output_v1_info::link */
81
82         struct zxdg_output_manager_v1 *xdg_output_manager;
83         struct agl_screenshooter *screenshooter;
84         int buffer_copy_done;
85 };
86
87 static int opts = 0x0;
88
89 #define OPT_SCREENSHOT_OUTPUT           1
90 #define OPT_SHOW_ALL_OUTPUTS            2
91 #define OPT_SCREENSHOT_ALL_OUTPUTS      3
92
93 static void
94 display_handle_geometry(void *data,
95                         struct wl_output *wl_output,
96                         int x,
97                         int y,
98                         int physical_width,
99                         int physical_height,
100                         int subpixel,
101                         const char *make,
102                         const char *model,
103                         int transform)
104 {
105         struct screenshooter_output *output;
106
107         output = wl_output_get_user_data(wl_output);
108
109         if (wl_output == output->output) {
110                 output->offset_x = x;
111                 output->offset_y = y;
112         }
113 }
114
115 static void
116 display_handle_mode(void *data,
117                     struct wl_output *wl_output,
118                     uint32_t flags,
119                     int width,
120                     int height,
121                     int refresh)
122 {
123         struct screenshooter_output *output;
124
125         output = wl_output_get_user_data(wl_output);
126
127         if (wl_output == output->output && (flags & WL_OUTPUT_MODE_CURRENT)) {
128                 output->width = width;
129                 output->height = height;
130         }
131 }
132
133 static void
134 display_handle_done(void *data, struct wl_output *wl_output)
135 {
136
137 }
138
139 static void
140 display_handle_scale(void *data, struct wl_output *wl_output,
141                 int32_t scale)
142 {
143         struct screenshooter_output *output = data;
144         output->scale = scale;
145 }
146
147
148 static const struct wl_output_listener output_listener = {
149         display_handle_geometry,
150         display_handle_mode,
151         display_handle_done,
152         display_handle_scale,
153 };
154
155 static void
156 handle_xdg_output_v1_logical_position(void *data, struct zxdg_output_v1 *output,
157                                       int32_t x, int32_t y)
158 {
159 }
160
161 static void
162 handle_xdg_output_v1_logical_size(void *data, struct zxdg_output_v1 *output,
163                                       int32_t width, int32_t height)
164 {
165 }
166
167 static void
168 handle_xdg_output_v1_done(void *data, struct zxdg_output_v1 *output)
169 {
170         /* Don't bother waiting for this; there's no good reason a
171          * compositor will wait more than one roundtrip before sending
172          * these initial events. */
173 }
174
175 static void
176 handle_xdg_output_v1_name(void *data, struct zxdg_output_v1 *output,
177                           const char *name)
178 {
179         struct xdg_output_v1_info *xdg_output = data;
180         xdg_output->name = strdup(name);
181 }
182
183 static void
184 handle_xdg_output_v1_description(void *data, struct zxdg_output_v1 *output,
185                           const char *description)
186 {
187         struct xdg_output_v1_info *xdg_output = data;
188         xdg_output->description = strdup(description);
189 }
190
191 static const struct zxdg_output_v1_listener xdg_output_v1_listener = {
192         .logical_position = handle_xdg_output_v1_logical_position,
193         .logical_size = handle_xdg_output_v1_logical_size,
194         .done = handle_xdg_output_v1_done,
195         .name = handle_xdg_output_v1_name,
196         .description = handle_xdg_output_v1_description,
197 };
198
199 static void
200 add_xdg_output_v1_info(struct screenshooter_data *shooter_data,
201                        struct screenshooter_output *output)
202 {
203         struct xdg_output_v1_info *xdg_output = zalloc(sizeof(*xdg_output));
204         if (!xdg_output)
205                 return;
206
207         wl_list_insert(&shooter_data->xdg_output_list, &xdg_output->link);
208
209         xdg_output->xdg_output =
210                 zxdg_output_manager_v1_get_xdg_output(shooter_data->xdg_output_manager,
211                                                       output->output);
212
213         zxdg_output_v1_add_listener(xdg_output->xdg_output,
214                                     &xdg_output_v1_listener, xdg_output);
215         xdg_output->output = output;
216 }
217
218 static void
219 screenshot_done(void *data, struct agl_screenshooter *screenshooter, uint32_t status)
220 {
221         struct screenshooter_data *sh_data = data;
222         sh_data->buffer_copy_done = 1;
223 }
224
225 static const struct agl_screenshooter_listener screenshooter_listener = {
226         screenshot_done
227 };
228
229 static void
230 handle_global(void *data, struct wl_registry *registry,
231               uint32_t name, const char *interface, uint32_t version)
232 {
233         struct screenshooter_output *output;
234         struct screenshooter_data *sh_data = data;
235
236         if (strcmp(interface, "wl_output") == 0) {
237                 output = zalloc(sizeof(*output));
238                 if (!output)
239                         return;
240
241                 output->output = wl_registry_bind(registry, name,
242                                                   &wl_output_interface, 1);
243                 output->sh_data = sh_data;
244                 wl_list_insert(&sh_data->output_list, &output->link);
245                 wl_output_add_listener(output->output, &output_listener, output);
246         } else if (strcmp(interface, "wl_shm") == 0) {
247                 sh_data->shm = wl_registry_bind(registry, name, &wl_shm_interface, 1);
248         } else if (strcmp(interface, "agl_screenshooter") == 0) {
249                 sh_data->screenshooter = wl_registry_bind(registry, name,
250                                                           &agl_screenshooter_interface, 1);
251
252                 agl_screenshooter_add_listener(sh_data->screenshooter,
253                                                &screenshooter_listener, sh_data);
254         } else if (strcmp(interface, "zxdg_output_manager_v1") == 0) {
255                 sh_data->xdg_output_manager = wl_registry_bind(registry, name,
256                                         &zxdg_output_manager_v1_interface, version);
257         }
258 }
259
260 static void
261 handle_global_remove(void *data, struct wl_registry *registry, uint32_t name)
262 {
263         /* XXX: unimplemented */
264 }
265
266 static const struct wl_registry_listener registry_listener = {
267         handle_global,
268         handle_global_remove
269 };
270
271 static struct wl_buffer *
272 screenshot_create_shm_buffer(int width, int height, void **data_out,
273                              struct wl_shm *shm)
274 {
275         struct wl_shm_pool *pool;
276         struct wl_buffer *buffer;
277         int fd, size, stride;
278         void *data;
279
280         stride = width * 4;
281         size = stride * height;
282
283         fd = os_create_anonymous_file(size);
284         if (fd < 0) {
285                 fprintf(stderr, "creating a buffer file for %d B failed: %s\n",
286                         size, strerror(errno));
287                 return NULL;
288         }
289
290         data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
291         if (data == MAP_FAILED) {
292                 fprintf(stderr, "mmap failed: %s\n", strerror(errno));
293                 close(fd);
294                 return NULL;
295         }
296
297         pool = wl_shm_create_pool(shm, fd, size);
298         close(fd);
299         buffer = wl_shm_pool_create_buffer(pool, 0, width, height, stride,
300                                            WL_SHM_FORMAT_XRGB8888);
301         wl_shm_pool_destroy(pool);
302
303         *data_out = data;
304
305         return buffer;
306 }
307
308 static void
309 screenshot_write_png_per_output(const struct buffer_size *buff_size,
310                                 struct screenshooter_output *sh_output)
311 {
312         int output_stride, buffer_stride, i;
313         cairo_surface_t *surface;
314         void *data, *d, *s;
315         FILE *fp;
316         char filepath[PATH_MAX];
317
318         buffer_stride = buff_size->width * 4;
319         data = xmalloc(buffer_stride * buff_size->height);
320         if (!data)
321                 return;
322
323         output_stride = sh_output->width * 4;
324         s = sh_output->data;
325         d = data + (sh_output->offset_y - buff_size->min_y) * buffer_stride +
326                    (sh_output->offset_x - buff_size->min_x) * 4;
327
328         for (i = 0; i < sh_output->height; i++) {
329                 memcpy(d, s, output_stride);
330                 d += buffer_stride;
331                 s += output_stride;
332         }
333
334         surface = cairo_image_surface_create_for_data(data,
335                                                       CAIRO_FORMAT_ARGB32,
336                                                       buff_size->width,
337                                                       buff_size->height,
338                                                       buffer_stride);
339
340         fp = file_create_dated(getenv("XDG_PICTURES_DIR"), "agl-screenshot-",
341                                ".png", filepath, sizeof(filepath));
342         if (fp) {
343                 fclose(fp);
344                 cairo_surface_write_to_png(surface, filepath);
345         }
346
347         cairo_surface_destroy(surface);
348         free(data);
349 }
350
351 static void
352 screenshot_write_png(const struct buffer_size *buff_size,
353                      struct wl_list *output_list)
354 {
355         int output_stride, buffer_stride, i;
356         cairo_surface_t *surface;
357         void *data, *d, *s;
358         struct screenshooter_output *output, *next;
359         FILE *fp;
360         char filepath[PATH_MAX];
361
362         buffer_stride = buff_size->width * 4;
363
364         data = xmalloc(buffer_stride * buff_size->height);
365         if (!data)
366                 return;
367
368         wl_list_for_each_safe(output, next, output_list, link) {
369                 output_stride = output->width * 4;
370                 s = output->data;
371                 d = data + (output->offset_y - buff_size->min_y) * buffer_stride +
372                            (output->offset_x - buff_size->min_x) * 4;
373
374                 for (i = 0; i < output->height; i++) {
375                         memcpy(d, s, output_stride);
376                         d += buffer_stride;
377                         s += output_stride;
378                 }
379
380                 free(output);
381         }
382
383         surface = cairo_image_surface_create_for_data(data,
384                                                       CAIRO_FORMAT_ARGB32,
385                                                       buff_size->width,
386                                                       buff_size->height,
387                                                       buffer_stride);
388
389         fp = file_create_dated(getenv("XDG_PICTURES_DIR"), "agl-screenshot-",
390                                ".png", filepath, sizeof(filepath));
391         if (fp) {
392                 fclose(fp);
393                 cairo_surface_write_to_png(surface, filepath);
394         }
395
396         cairo_surface_destroy(surface);
397         free(data);
398 }
399
400 static void
401 screenshot_set_buffer_size_per_output(struct buffer_size *buff_size,
402                                       struct screenshooter_output *output)
403 {
404         buff_size->min_x = MIN(buff_size->min_x, output->offset_x);
405         buff_size->min_y = MIN(buff_size->min_y, output->offset_y);
406         buff_size->max_x = MAX(buff_size->max_x, output->offset_x + output->width);
407         buff_size->max_y = MAX(buff_size->max_y, output->offset_y + output->height);
408
409         buff_size->width = buff_size->max_x - buff_size->min_x;
410         buff_size->height = buff_size->max_y - buff_size->min_y;
411 }
412
413 static void
414 screenshot_compute_output_offset(int *pos, struct screenshooter_output *sh_output)
415 {
416         sh_output->offset_x = *pos;
417         *pos += sh_output->width;
418 }
419
420 static int
421 screenshot_set_buffer_size(struct buffer_size *buff_size, struct wl_list *output_list)
422 {
423         struct screenshooter_output *output;
424         int pos = 0;
425
426         buff_size->min_x = buff_size->min_y = INT_MAX;
427         buff_size->max_x = buff_size->max_y = INT_MIN;
428
429         wl_list_for_each_reverse(output, output_list, link)
430                 screenshot_compute_output_offset(&pos, output);
431
432         wl_list_for_each(output, output_list, link)
433                 screenshot_set_buffer_size_per_output(buff_size, output);
434
435         if (buff_size->max_x <= buff_size->min_x ||
436             buff_size->max_y <= buff_size->min_y)
437                 return -1;
438
439         buff_size->width = buff_size->max_x - buff_size->min_x;
440         buff_size->height = buff_size->max_y - buff_size->min_y;
441
442         return 0;
443 }
444
445 static struct screenshooter_output *
446 agl_shooter_search_for_output(const char *output_name,
447                               struct screenshooter_data *sh_data)
448 {
449         struct screenshooter_output *found_output = NULL;
450         struct xdg_output_v1_info *output;
451
452         if (!output_name)
453                 return found_output;
454
455         wl_list_for_each(output, &sh_data->xdg_output_list, link) {
456                 if (output->name && strcmp(output->name, output_name) == 0) {
457                         found_output = output->output;
458                         break;
459                 }
460         }
461
462         return found_output;
463 }
464
465 static void
466 agl_shooter_display_all_outputs(struct screenshooter_data *sh_data)
467 {
468         struct xdg_output_v1_info *xdg_output;
469         wl_list_for_each(xdg_output, &sh_data->xdg_output_list, link) {
470                 fprintf(stdout, "Output '%s', desc: '%s'\n", xdg_output->name,
471                                 xdg_output->description);
472         }
473 }
474
475
476 static void
477 agl_shooter_screenshot_all_outputs(struct screenshooter_data *sh_data)
478 {
479         struct screenshooter_output *output;
480         struct buffer_size buff_size = {};
481
482         if (screenshot_set_buffer_size(&buff_size, &sh_data->output_list))
483                 return;
484
485         wl_list_for_each(output, &sh_data->output_list, link) {
486                 output->buffer =
487                         screenshot_create_shm_buffer(output->width,
488                                                      output->height,
489                                                      &output->data,
490                                                      sh_data->shm);
491
492                 agl_screenshooter_take_shot(sh_data->screenshooter,
493                                             output->output,
494                                             output->buffer);
495
496                 sh_data->buffer_copy_done = 0;
497                 while (!sh_data->buffer_copy_done)
498                         wl_display_roundtrip(sh_data->display);
499         }
500
501         screenshot_write_png(&buff_size, &sh_data->output_list);
502 }
503
504 static void
505 agl_shooter_screenshot_output(struct screenshooter_output *sh_output)
506 {
507         int pos = 0;
508         struct buffer_size buff_size = {};
509         struct screenshooter_data *sh_data = sh_output->sh_data;
510
511         screenshot_compute_output_offset(&pos, sh_output);
512         screenshot_set_buffer_size_per_output(&buff_size, sh_output);
513
514         sh_output->buffer =
515                 screenshot_create_shm_buffer(sh_output->width,
516                                              sh_output->height,
517                                              &sh_output->data, sh_data->shm);
518
519         agl_screenshooter_take_shot(sh_data->screenshooter,
520                                     sh_output->output,
521                                     sh_output->buffer);
522
523         sh_data->buffer_copy_done = 0;
524         while (!sh_data->buffer_copy_done)
525                 wl_display_roundtrip(sh_data->display);
526
527         screenshot_write_png_per_output(&buff_size, sh_output);
528 }
529
530 static void
531 agl_shooter_destroy_xdg_output_manager(struct screenshooter_data *sh_data)
532 {
533         struct xdg_output_v1_info *xdg_output;
534
535         wl_list_for_each(xdg_output, &sh_data->xdg_output_list, link) {
536                 free(xdg_output->name);
537                 free(xdg_output->description);
538                 zxdg_output_v1_destroy(xdg_output->xdg_output);
539         }
540
541         zxdg_output_manager_v1_destroy(sh_data->xdg_output_manager);
542 }
543
544 static void
545 print_usage_and_exit(void)
546 {
547         fprintf(stderr, "./agl-screenshooter [-o OUTPUT_NAME] [-l] [-a]\n");
548
549         fprintf(stderr, "\t-o OUTPUT_NAME -- take a screenshot of the output "
550                                 "specified by OUTPUT_NAME\n");
551         fprintf(stderr, "\t-a  -- take a screenshot of all the outputs found\n");
552         fprintf(stderr, "\t-l  -- list all the outputs found\n");
553         exit(EXIT_FAILURE);
554 }
555
556 int main(int argc, char *argv[])
557 {
558         struct wl_display *display;
559         struct wl_registry *registry;
560
561         struct screenshooter_data sh_data = {};
562         struct screenshooter_output *sh_output = NULL;
563         int c, option_index;
564
565         char *output_name = NULL;
566
567         static struct option long_options[] = {
568                 {"output",      required_argument, 0,  'o' },
569                 {"list",        required_argument, 0,  'l' },
570                 {"all",         required_argument, 0,  'a' },
571                 {"help",        no_argument      , 0,  'h' },
572                 {0, 0, 0, 0}
573         };
574
575         while ((c = getopt_long(argc, argv, "o:lah",
576                                 long_options, &option_index)) != -1) {
577                 switch (c) {
578                 case 'o':
579                         output_name = optarg;
580                         opts |= (1 << OPT_SCREENSHOT_OUTPUT);
581                         break;
582                 case 'l':
583                         opts |= (1 << OPT_SHOW_ALL_OUTPUTS);
584                         break;
585                 case 'a':
586                         opts |= (1 << OPT_SCREENSHOT_ALL_OUTPUTS);
587                         break;
588                 default:
589                         print_usage_and_exit();
590                 }
591         }
592
593         display = wl_display_connect(NULL);
594         if (display == NULL) {
595                 fprintf(stderr, "failed to create display: %s\n",
596                         strerror(errno));
597                 return EXIT_FAILURE;
598         }
599
600         wl_list_init(&sh_data.output_list);
601         wl_list_init(&sh_data.xdg_output_list);
602         sh_data.display = display;
603
604         registry = wl_display_get_registry(display);
605         wl_registry_add_listener(registry, &registry_listener, &sh_data);
606
607         wl_display_dispatch(display);
608         wl_display_roundtrip(display);
609
610
611         if (sh_data.screenshooter == NULL) {
612                 fprintf(stderr, "Compositor doesn't support screenshooter\n");
613                 return EXIT_FAILURE;
614         }
615
616         wl_list_for_each(sh_output, &sh_data.output_list, link)
617                 add_xdg_output_v1_info(&sh_data, sh_output);
618
619         /* do another round-trip for xdg_output */
620         wl_display_roundtrip(sh_data.display);
621
622         if (opts & (1 << OPT_SHOW_ALL_OUTPUTS)) {
623                 agl_shooter_display_all_outputs(&sh_data);
624                 agl_shooter_destroy_xdg_output_manager(&sh_data);
625                 return EXIT_SUCCESS;
626         }
627
628         if (opts & (1 << OPT_SCREENSHOT_ALL_OUTPUTS)) {
629                 agl_shooter_screenshot_all_outputs(&sh_data);
630                 agl_shooter_destroy_xdg_output_manager(&sh_data);
631                 return EXIT_SUCCESS;
632         }
633
634         sh_output = NULL;
635         if (output_name)
636                 sh_output = agl_shooter_search_for_output(output_name, &sh_data);
637
638         if (!sh_output && (opts & (1 << OPT_SCREENSHOT_OUTPUT))) {
639                 fprintf(stderr, "Could not find an output matching '%s'\n",
640                                 output_name);
641                 agl_shooter_destroy_xdg_output_manager(&sh_data);
642                 return EXIT_FAILURE;
643         }
644
645         /* if we're still here just pick the first one available
646          * and use that. Still useful in case we are run without
647          * any args whatsoever */
648         if (!sh_output)
649                 sh_output = container_of(sh_data.output_list.next,
650                                          struct screenshooter_output, link);
651
652         /* take a screenshot only of that specific output */
653         agl_shooter_screenshot_output(sh_output);
654         agl_shooter_destroy_xdg_output_manager(&sh_data);
655
656         return 0;
657 }