64dc76a2f89b7e3a97dc77f20920762d2bc0f619
[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 void
399 screenshot_set_buffer_size_per_output(struct buffer_size *buff_size,
400                                       struct screenshooter_output *output)
401 {
402         buff_size->min_x = MIN(buff_size->min_x, output->offset_x);
403         buff_size->min_y = MIN(buff_size->min_y, output->offset_y);
404         buff_size->max_x = MAX(buff_size->max_x, output->offset_x + output->width);
405         buff_size->max_y = MAX(buff_size->max_y, output->offset_y + output->height);
406
407 }
408
409 static void
410 screenshot_compute_output_offset(int *pos, struct screenshooter_output *sh_output)
411 {
412         sh_output->offset_x = *pos;
413         *pos += sh_output->width;
414 }
415
416 static int
417 screenshot_set_buffer_size(struct buffer_size *buff_size, struct wl_list *output_list)
418 {
419         struct screenshooter_output *output;
420         int pos = 0;
421
422         buff_size->min_x = buff_size->min_y = INT_MAX;
423         buff_size->max_x = buff_size->max_y = INT_MIN;
424
425         wl_list_for_each_reverse(output, output_list, link)
426                 screenshot_compute_output_offset(&pos, output);
427
428         wl_list_for_each(output, output_list, link)
429                 screenshot_set_buffer_size_per_output(buff_size, output);
430
431         if (buff_size->max_x <= buff_size->min_x ||
432             buff_size->max_y <= buff_size->min_y)
433                 return -1;
434
435         buff_size->width = buff_size->max_x - buff_size->min_x;
436         buff_size->height = buff_size->max_y - buff_size->min_y;
437
438         return 0;
439 }
440
441 static struct screenshooter_output *
442 agl_shooter_search_for_output(const char *output_name,
443                               struct screenshooter_data *sh_data)
444 {
445         struct screenshooter_output *found_output = NULL;
446         struct xdg_output_v1_info *output;
447
448         if (!output_name)
449                 return found_output;
450
451         wl_list_for_each(output, &sh_data->xdg_output_list, link) {
452                 if (output->name && strcmp(output->name, output_name) == 0) {
453                         found_output = output->output;
454                         break;
455                 }
456         }
457
458         return found_output;
459 }
460
461 static void
462 agl_shooter_display_all_outputs(struct screenshooter_data *sh_data)
463 {
464         struct xdg_output_v1_info *xdg_output;
465         wl_list_for_each(xdg_output, &sh_data->xdg_output_list, link) {
466                 fprintf(stdout, "Output '%s', desc: '%s'\n", xdg_output->name,
467                                 xdg_output->description);
468         }
469 }
470
471
472 static void
473 agl_shooter_screenshot_all_outputs(struct screenshooter_data *sh_data)
474 {
475         struct screenshooter_output *output;
476         struct buffer_size buff_size = {};
477
478         if (screenshot_set_buffer_size(&buff_size, &sh_data->output_list))
479                 return;
480
481         wl_list_for_each(output, &sh_data->output_list, link) {
482                 output->buffer =
483                         screenshot_create_shm_buffer(output->width,
484                                                      output->height,
485                                                      &output->data,
486                                                      sh_data->shm);
487
488                 agl_screenshooter_take_shot(sh_data->screenshooter,
489                                             output->output,
490                                             output->buffer);
491
492                 sh_data->buffer_copy_done = 0;
493                 while (!sh_data->buffer_copy_done)
494                         wl_display_roundtrip(sh_data->display);
495         }
496
497         screenshot_write_png(&buff_size, &sh_data->output_list);
498 }
499
500 static void
501 agl_shooter_screenshot_output(struct screenshooter_output *sh_output)
502 {
503         int pos = 0;
504         struct buffer_size buff_size = {};
505         struct screenshooter_data *sh_data = sh_output->sh_data;
506
507         screenshot_compute_output_offset(&pos, sh_output);
508         screenshot_set_buffer_size_per_output(&buff_size, sh_output);
509
510         sh_output->buffer =
511                 screenshot_create_shm_buffer(sh_output->width,
512                                              sh_output->height,
513                                              &sh_output->data, sh_data->shm);
514
515         agl_screenshooter_take_shot(sh_data->screenshooter,
516                                     sh_output->output,
517                                     sh_output->buffer);
518
519         sh_data->buffer_copy_done = 0;
520         while (!sh_data->buffer_copy_done)
521                 wl_display_roundtrip(sh_data->display);
522
523         screenshot_write_png_per_output(&buff_size, sh_output);
524 }
525
526 static void
527 agl_shooter_destroy_xdg_output_manager(struct screenshooter_data *sh_data)
528 {
529         struct xdg_output_v1_info *xdg_output;
530
531         wl_list_for_each(xdg_output, &sh_data->xdg_output_list, link) {
532                 free(xdg_output->name);
533                 free(xdg_output->description);
534                 zxdg_output_v1_destroy(xdg_output->xdg_output);
535         }
536
537         zxdg_output_manager_v1_destroy(sh_data->xdg_output_manager);
538 }
539
540 static void
541 print_usage_and_exit(void)
542 {
543         fprintf(stderr, "./agl-screenshooter [-o OUTPUT_NAME] [-l] [-a]\n");
544
545         fprintf(stderr, "\t-o OUTPUT_NAME -- take a screenshot of the output "
546                                 "specified by OUTPUT_NAME\n");
547         fprintf(stderr, "\t-a  -- take a screenshot of all the outputs found\n");
548         fprintf(stderr, "\t-l  -- list all the outputs found\n");
549         exit(EXIT_FAILURE);
550 }
551
552 int main(int argc, char *argv[])
553 {
554         struct wl_display *display;
555         struct wl_registry *registry;
556
557         struct screenshooter_data sh_data = {};
558         struct screenshooter_output *sh_output = NULL;
559         int c, option_index;
560
561         char *output_name = NULL;
562
563         static struct option long_options[] = {
564                 {"output",      required_argument, 0,  'o' },
565                 {"list",        required_argument, 0,  'l' },
566                 {"all",         required_argument, 0,  'a' },
567                 {"help",        no_argument      , 0,  'h' },
568                 {0, 0, 0, 0}
569         };
570
571         while ((c = getopt_long(argc, argv, "o:lah",
572                                 long_options, &option_index)) != -1) {
573                 switch (c) {
574                 case 'o':
575                         output_name = optarg;
576                         opts |= (1 << OPT_SCREENSHOT_OUTPUT);
577                         break;
578                 case 'l':
579                         opts |= (1 << OPT_SHOW_ALL_OUTPUTS);
580                         break;
581                 case 'a':
582                         opts |= (1 << OPT_SCREENSHOT_ALL_OUTPUTS);
583                         break;
584                 default:
585                         print_usage_and_exit();
586                 }
587         }
588
589         display = wl_display_connect(NULL);
590         if (display == NULL) {
591                 fprintf(stderr, "failed to create display: %s\n",
592                         strerror(errno));
593                 return EXIT_FAILURE;
594         }
595
596         wl_list_init(&sh_data.output_list);
597         wl_list_init(&sh_data.xdg_output_list);
598         sh_data.display = display;
599
600         registry = wl_display_get_registry(display);
601         wl_registry_add_listener(registry, &registry_listener, &sh_data);
602
603         wl_display_dispatch(display);
604         wl_display_roundtrip(display);
605
606
607         if (sh_data.screenshooter == NULL) {
608                 fprintf(stderr, "Compositor doesn't support screenshooter\n");
609                 return EXIT_FAILURE;
610         }
611
612         wl_list_for_each(sh_output, &sh_data.output_list, link)
613                 add_xdg_output_v1_info(&sh_data, sh_output);
614
615         /* do another round-trip for xdg_output */
616         wl_display_roundtrip(sh_data.display);
617
618         if (opts & (1 << OPT_SHOW_ALL_OUTPUTS)) {
619                 agl_shooter_display_all_outputs(&sh_data);
620                 agl_shooter_destroy_xdg_output_manager(&sh_data);
621                 return EXIT_SUCCESS;
622         }
623
624         if (opts & (1 << OPT_SCREENSHOT_ALL_OUTPUTS)) {
625                 agl_shooter_screenshot_all_outputs(&sh_data);
626                 agl_shooter_destroy_xdg_output_manager(&sh_data);
627                 return EXIT_SUCCESS;
628         }
629
630         sh_output = NULL;
631         if (output_name)
632                 sh_output = agl_shooter_search_for_output(output_name, &sh_data);
633
634         if (!sh_output && (opts & (1 << OPT_SCREENSHOT_OUTPUT))) {
635                 fprintf(stderr, "Could not find an output matching '%s'\n",
636                                 output_name);
637                 agl_shooter_destroy_xdg_output_manager(&sh_data);
638                 return EXIT_FAILURE;
639         }
640
641         /* if we're still here just pick the first one available
642          * and use that. Still useful in case we are run without
643          * any args whatsoever */
644         if (!sh_output)
645                 sh_output = container_of(sh_data.output_list.next,
646                                          struct screenshooter_output, link);
647
648         /* take a screenshot only of that specific output */
649         agl_shooter_screenshot_output(sh_output);
650         agl_shooter_destroy_xdg_output_manager(&sh_data);
651
652         return 0;
653 }