faced55e5bcdd68bbd05dad35913dc367b74f7d3
[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
205         wl_list_insert(&shooter_data->xdg_output_list, &xdg_output->link);
206
207         xdg_output->xdg_output =
208                 zxdg_output_manager_v1_get_xdg_output(shooter_data->xdg_output_manager,
209                                                       output->output);
210
211         zxdg_output_v1_add_listener(xdg_output->xdg_output,
212                                     &xdg_output_v1_listener, xdg_output);
213         xdg_output->output = output;
214 }
215
216 static void
217 screenshot_done(void *data, struct agl_screenshooter *screenshooter, uint32_t status)
218 {
219         struct screenshooter_data *sh_data = data;
220         sh_data->buffer_copy_done = 1;
221 }
222
223 static const struct agl_screenshooter_listener screenshooter_listener = {
224         screenshot_done
225 };
226
227 static void
228 handle_global(void *data, struct wl_registry *registry,
229               uint32_t name, const char *interface, uint32_t version)
230 {
231         struct screenshooter_output *output;
232         struct screenshooter_data *sh_data = data;
233
234         if (strcmp(interface, "wl_output") == 0) {
235                 output = zalloc(sizeof(*output));
236                 if (!output)
237                         return;
238
239                 output->output = wl_registry_bind(registry, name,
240                                                   &wl_output_interface, 1);
241                 output->sh_data = sh_data;
242                 wl_list_insert(&sh_data->output_list, &output->link);
243                 wl_output_add_listener(output->output, &output_listener, output);
244         } else if (strcmp(interface, "wl_shm") == 0) {
245                 sh_data->shm = wl_registry_bind(registry, name, &wl_shm_interface, 1);
246         } else if (strcmp(interface, "agl_screenshooter") == 0) {
247                 sh_data->screenshooter = wl_registry_bind(registry, name,
248                                                           &agl_screenshooter_interface, 1);
249
250                 agl_screenshooter_add_listener(sh_data->screenshooter,
251                                                &screenshooter_listener, sh_data);
252         } else if (strcmp(interface, "zxdg_output_manager_v1") == 0) {
253                 sh_data->xdg_output_manager = wl_registry_bind(registry, name,
254                                         &zxdg_output_manager_v1_interface, version);
255         }
256 }
257
258 static void
259 handle_global_remove(void *data, struct wl_registry *registry, uint32_t name)
260 {
261         /* XXX: unimplemented */
262 }
263
264 static const struct wl_registry_listener registry_listener = {
265         handle_global,
266         handle_global_remove
267 };
268
269 static struct wl_buffer *
270 screenshot_create_shm_buffer(int width, int height, void **data_out,
271                              struct wl_shm *shm)
272 {
273         struct wl_shm_pool *pool;
274         struct wl_buffer *buffer;
275         int fd, size, stride;
276         void *data;
277
278         stride = width * 4;
279         size = stride * height;
280
281         fd = os_create_anonymous_file(size);
282         if (fd < 0) {
283                 fprintf(stderr, "creating a buffer file for %d B failed: %s\n",
284                         size, strerror(errno));
285                 return NULL;
286         }
287
288         data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
289         if (data == MAP_FAILED) {
290                 fprintf(stderr, "mmap failed: %s\n", strerror(errno));
291                 close(fd);
292                 return NULL;
293         }
294
295         pool = wl_shm_create_pool(shm, fd, size);
296         close(fd);
297         buffer = wl_shm_pool_create_buffer(pool, 0, width, height, stride,
298                                            WL_SHM_FORMAT_XRGB8888);
299         wl_shm_pool_destroy(pool);
300
301         *data_out = data;
302
303         return buffer;
304 }
305
306 static void
307 screenshot_write_png_per_output(const struct buffer_size *buff_size,
308                                 struct screenshooter_output *sh_output)
309 {
310         int output_stride, buffer_stride, i;
311         cairo_surface_t *surface;
312         void *data, *d, *s;
313         FILE *fp;
314         char filepath[PATH_MAX];
315
316         buffer_stride = buff_size->width * 4;
317         data = xmalloc(buffer_stride * buff_size->height);
318         if (!data)
319                 return;
320
321         output_stride = sh_output->width * 4;
322         s = sh_output->data;
323         d = data + (sh_output->offset_y - buff_size->min_y) * buffer_stride +
324                    (sh_output->offset_x - buff_size->min_x) * 4;
325
326         for (i = 0; i < sh_output->height; i++) {
327                 memcpy(d, s, output_stride);
328                 d += buffer_stride;
329                 s += output_stride;
330         }
331
332         surface = cairo_image_surface_create_for_data(data,
333                                                       CAIRO_FORMAT_ARGB32,
334                                                       buff_size->width,
335                                                       buff_size->height,
336                                                       buffer_stride);
337
338         fp = file_create_dated(getenv("XDG_PICTURES_DIR"), "agl-screenshot-",
339                                ".png", filepath, sizeof(filepath));
340         if (fp) {
341                 fclose(fp);
342                 cairo_surface_write_to_png(surface, filepath);
343         }
344
345         cairo_surface_destroy(surface);
346         free(data);
347 }
348
349 static void
350 screenshot_write_png(const struct buffer_size *buff_size,
351                      struct wl_list *output_list)
352 {
353         int output_stride, buffer_stride, i;
354         cairo_surface_t *surface;
355         void *data, *d, *s;
356         struct screenshooter_output *output, *next;
357         FILE *fp;
358         char filepath[PATH_MAX];
359
360         buffer_stride = buff_size->width * 4;
361
362         data = xmalloc(buffer_stride * buff_size->height);
363         if (!data)
364                 return;
365
366         wl_list_for_each_safe(output, next, output_list, link) {
367                 output_stride = output->width * 4;
368                 s = output->data;
369                 d = data + (output->offset_y - buff_size->min_y) * buffer_stride +
370                            (output->offset_x - buff_size->min_x) * 4;
371
372                 for (i = 0; i < output->height; i++) {
373                         memcpy(d, s, output_stride);
374                         d += buffer_stride;
375                         s += output_stride;
376                 }
377
378                 free(output);
379         }
380
381         surface = cairo_image_surface_create_for_data(data,
382                                                       CAIRO_FORMAT_ARGB32,
383                                                       buff_size->width,
384                                                       buff_size->height,
385                                                       buffer_stride);
386
387         fp = file_create_dated(getenv("XDG_PICTURES_DIR"), "agl-screenshot-",
388                                ".png", filepath, sizeof(filepath));
389         if (fp) {
390                 fclose(fp);
391                 cairo_surface_write_to_png(surface, filepath);
392         }
393
394         cairo_surface_destroy(surface);
395         free(data);
396 }
397
398 static int
399 screenshot_set_buffer_size_per_output(struct buffer_size *buff_size,
400                                       struct screenshooter_output *output)
401 {
402         buff_size->min_x = buff_size->min_y = INT_MAX;
403         buff_size->max_x = buff_size->max_y = INT_MIN;
404
405         buff_size->min_x = MIN(buff_size->min_x, output->offset_x);
406         buff_size->min_y = MIN(buff_size->min_y, output->offset_y);
407         buff_size->max_x =
408                 MAX(buff_size->max_x, output->offset_x + output->width);
409         buff_size->max_y =
410                 MAX(buff_size->max_y, output->offset_y + output->height);
411
412         if (buff_size->max_x <= buff_size->min_x ||
413             buff_size->max_y <= buff_size->min_y)
414                 return -1;
415
416         buff_size->width = buff_size->max_x - buff_size->min_x;
417         buff_size->height = buff_size->max_y - buff_size->min_y;
418
419         return 0;
420 }
421
422 static void
423 screenshot_compute_output_offset(int *pos, struct screenshooter_output *sh_output)
424 {
425         sh_output->offset_x = *pos;
426         *pos += sh_output->width;
427 }
428
429 static int
430 screenshot_set_buffer_size(struct buffer_size *buff_size, struct wl_list *output_list)
431 {
432         struct screenshooter_output *output;
433         int pos = 0;
434
435         wl_list_for_each_reverse(output, output_list, link)
436                 screenshot_compute_output_offset(&pos, output);
437
438         wl_list_for_each(output, output_list, link)
439                 if (screenshot_set_buffer_size_per_output(buff_size, output))
440                         return -1;
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 }