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