blob: ea0a2b2e55f25f06fa74bfd9b2351bca4fe13712 [file] [log] [blame]
Kristian Høgsbergffd710e2008-12-02 15:15:01 -05001/*
2 * Copyright © 2008 Kristian Høgsberg
3 *
Bryce Harrington1f6b0d12015-06-10 22:48:59 -07004 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
Kristian Høgsbergffd710e2008-12-02 15:15:01 -050010 *
Bryce Harrington1f6b0d12015-06-10 22:48:59 -070011 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
13 * Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21 * DEALINGS IN THE SOFTWARE.
Kristian Høgsbergffd710e2008-12-02 15:15:01 -050022 */
23
Kristian Høgsbergbdd83772013-08-12 21:45:19 -070024#include "config.h"
25
Kristian Høgsberg1e4b86a2008-11-23 23:41:08 -050026#include <stdint.h>
Kristian Høgsbergbdd83772013-08-12 21:45:19 -070027#include <errno.h>
Kristian Høgsberg1e4b86a2008-11-23 23:41:08 -050028#include <stdlib.h>
29#include <stdio.h>
30#include <string.h>
31#include <fcntl.h>
Kristian Høgsberg85449032011-05-02 12:11:07 -040032#include <unistd.h>
Scott Moreau2074f1d2012-04-20 13:37:35 -060033#include <limits.h>
34#include <sys/param.h>
Kristian Høgsberg85449032011-05-02 12:11:07 -040035#include <sys/mman.h>
Kristian Høgsbergf02a6492012-03-12 01:05:25 -040036#include <cairo.h>
Kristian Høgsberg1e4b86a2008-11-23 23:41:08 -050037
Pekka Paalanen50719bc2011-11-22 14:18:50 +020038#include <wayland-client.h>
Jonas Ådahlcf1efd22015-11-17 16:00:34 +080039#include "weston-screenshooter-client-protocol.h"
Jon Cruz4678bab2015-06-15 15:37:07 -070040#include "shared/os-compatibility.h"
Bryce Harringtone99e4bf2016-03-16 14:15:18 -070041#include "shared/xalloc.h"
Aleksander Morgadoe3c2a762018-01-23 01:05:20 +010042#include "shared/file-util.h"
Kristian Høgsberg1e4b86a2008-11-23 23:41:08 -050043
44/* The screenshooter is a good example of a custom object exposed by
45 * the compositor and serves as a test bed for implementing client
46 * side marshalling outside libwayland.so */
47
Scott Moreau80d27b72012-04-04 11:49:21 -060048
49struct screenshooter_output {
50 struct wl_output *output;
51 struct wl_buffer *buffer;
52 int width, height, offset_x, offset_y;
Scott Moreau72c23722012-04-20 13:37:34 -060053 void *data;
Scott Moreau80d27b72012-04-04 11:49:21 -060054 struct wl_list link;
55};
Kristian Høgsberg85449032011-05-02 12:11:07 -040056
Marius Vlad3c7cfbb2018-12-13 15:24:08 +020057struct buffer_size {
58 int width, height;
59
60 int min_x, min_y;
61 int max_x, max_y;
62};
63
64struct screenshooter_data {
65 struct wl_shm *shm;
66 struct wl_list output_list;
67
68 struct weston_screenshooter *screenshooter;
69 int buffer_copy_done;
70};
71
72
Kristian Høgsberg85449032011-05-02 12:11:07 -040073static void
74display_handle_geometry(void *data,
Kristian Høgsberg8f0ce052011-06-21 11:16:58 -040075 struct wl_output *wl_output,
76 int x,
77 int y,
78 int physical_width,
79 int physical_height,
80 int subpixel,
81 const char *make,
Kristian Høgsberg0e696472012-07-22 15:49:57 -040082 const char *model,
83 int transform)
Kristian Høgsberg8f0ce052011-06-21 11:16:58 -040084{
Scott Moreau80d27b72012-04-04 11:49:21 -060085 struct screenshooter_output *output;
86
87 output = wl_output_get_user_data(wl_output);
88
89 if (wl_output == output->output) {
90 output->offset_x = x;
91 output->offset_y = y;
92 }
Kristian Høgsberg8f0ce052011-06-21 11:16:58 -040093}
94
95static void
96display_handle_mode(void *data,
97 struct wl_output *wl_output,
98 uint32_t flags,
99 int width,
100 int height,
101 int refresh)
Kristian Høgsberg85449032011-05-02 12:11:07 -0400102{
Scott Moreau80d27b72012-04-04 11:49:21 -0600103 struct screenshooter_output *output;
104
105 output = wl_output_get_user_data(wl_output);
106
Kristian Høgsberg1a361562012-04-04 14:52:35 -0400107 if (wl_output == output->output && (flags & WL_OUTPUT_MODE_CURRENT)) {
Scott Moreau80d27b72012-04-04 11:49:21 -0600108 output->width = width;
109 output->height = height;
110 }
Kristian Høgsberg85449032011-05-02 12:11:07 -0400111}
112
113static const struct wl_output_listener output_listener = {
114 display_handle_geometry,
Kristian Høgsberg8f0ce052011-06-21 11:16:58 -0400115 display_handle_mode
Kristian Høgsberg85449032011-05-02 12:11:07 -0400116};
117
Kristian Høgsberg4fe1a3e2010-08-10 14:02:48 -0400118static void
Jonas Ådahlcf1efd22015-11-17 16:00:34 +0800119screenshot_done(void *data, struct weston_screenshooter *screenshooter)
Scott Moreau062be7e2012-04-20 13:37:33 -0600120{
Marius Vlad3c7cfbb2018-12-13 15:24:08 +0200121 struct screenshooter_data *sh_data = data;
122 sh_data->buffer_copy_done = 1;
Scott Moreau062be7e2012-04-20 13:37:33 -0600123}
124
Jonas Ådahlcf1efd22015-11-17 16:00:34 +0800125static const struct weston_screenshooter_listener screenshooter_listener = {
Scott Moreau062be7e2012-04-20 13:37:33 -0600126 screenshot_done
127};
128
129static void
Kristian Høgsbergfa80e112012-10-10 21:34:26 -0400130handle_global(void *data, struct wl_registry *registry,
131 uint32_t name, const char *interface, uint32_t version)
Kristian Høgsberg4fe1a3e2010-08-10 14:02:48 -0400132{
Scott Moreau80d27b72012-04-04 11:49:21 -0600133 static struct screenshooter_output *output;
Marius Vlad3c7cfbb2018-12-13 15:24:08 +0200134 struct screenshooter_data *sh_data = data;
Scott Moreau80d27b72012-04-04 11:49:21 -0600135
Kristian Høgsberg85449032011-05-02 12:11:07 -0400136 if (strcmp(interface, "wl_output") == 0) {
Brian Lovinbc919262013-08-07 15:34:59 -0700137 output = xmalloc(sizeof *output);
Kristian Høgsbergfa80e112012-10-10 21:34:26 -0400138 output->output = wl_registry_bind(registry, name,
139 &wl_output_interface, 1);
Marius Vlad3c7cfbb2018-12-13 15:24:08 +0200140 wl_list_insert(&sh_data->output_list, &output->link);
Scott Moreau80d27b72012-04-04 11:49:21 -0600141 wl_output_add_listener(output->output, &output_listener, output);
Kristian Høgsberg85449032011-05-02 12:11:07 -0400142 } else if (strcmp(interface, "wl_shm") == 0) {
Marius Vlad3c7cfbb2018-12-13 15:24:08 +0200143 sh_data->shm = wl_registry_bind(registry, name, &wl_shm_interface, 1);
Jonas Ådahlcf1efd22015-11-17 16:00:34 +0800144 } else if (strcmp(interface, "weston_screenshooter") == 0) {
Marius Vlad3c7cfbb2018-12-13 15:24:08 +0200145 sh_data->screenshooter = wl_registry_bind(registry, name,
146 &weston_screenshooter_interface,
147 1);
Kristian Høgsberg85449032011-05-02 12:11:07 -0400148 }
149}
150
Pekka Paalanen0eab05d2013-01-22 14:53:55 +0200151static void
152handle_global_remove(void *data, struct wl_registry *registry, uint32_t name)
153{
154 /* XXX: unimplemented */
155}
156
Kristian Høgsbergfa80e112012-10-10 21:34:26 -0400157static const struct wl_registry_listener registry_listener = {
Pekka Paalanen0eab05d2013-01-22 14:53:55 +0200158 handle_global,
159 handle_global_remove
Kristian Høgsbergfa80e112012-10-10 21:34:26 -0400160};
161
Kristian Høgsberg85449032011-05-02 12:11:07 -0400162static struct wl_buffer *
Marius Vlad3c7cfbb2018-12-13 15:24:08 +0200163screenshot_create_shm_buffer(int width, int height, void **data_out,
164 struct wl_shm *shm)
Kristian Høgsberg85449032011-05-02 12:11:07 -0400165{
Kristian Høgsberg16626282012-04-03 11:21:27 -0400166 struct wl_shm_pool *pool;
Kristian Høgsberg85449032011-05-02 12:11:07 -0400167 struct wl_buffer *buffer;
168 int fd, size, stride;
169 void *data;
170
Kristian Høgsberg85449032011-05-02 12:11:07 -0400171 stride = width * 4;
172 size = stride * height;
Pekka Paalanen1da1b8f2012-06-06 16:59:43 +0300173
174 fd = os_create_anonymous_file(size);
175 if (fd < 0) {
Antonio Borneo39578632019-04-26 23:57:31 +0200176 fprintf(stderr, "creating a buffer file for %d B failed: %s\n",
177 size, strerror(errno));
Kristian Høgsberg85449032011-05-02 12:11:07 -0400178 return NULL;
179 }
180
181 data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
Kristian Høgsberg85449032011-05-02 12:11:07 -0400182 if (data == MAP_FAILED) {
Antonio Borneo39578632019-04-26 23:57:31 +0200183 fprintf(stderr, "mmap failed: %s\n", strerror(errno));
Kristian Høgsberg85449032011-05-02 12:11:07 -0400184 close(fd);
185 return NULL;
186 }
187
Kristian Høgsberg16626282012-04-03 11:21:27 -0400188 pool = wl_shm_create_pool(shm, fd, size);
Kristian Høgsberg85449032011-05-02 12:11:07 -0400189 close(fd);
Kristian Høgsberg16626282012-04-03 11:21:27 -0400190 buffer = wl_shm_pool_create_buffer(pool, 0, width, height, stride,
191 WL_SHM_FORMAT_XRGB8888);
192 wl_shm_pool_destroy(pool);
Kristian Høgsberg85449032011-05-02 12:11:07 -0400193
194 *data_out = data;
195
196 return buffer;
Kristian Høgsberg1e4b86a2008-11-23 23:41:08 -0500197}
198
Tiago Vignatti4d0d2032011-07-26 11:42:59 +0300199static void
Marius Vlad3c7cfbb2018-12-13 15:24:08 +0200200screenshot_write_png(const struct buffer_size *buff_size,
201 struct wl_list *output_list)
Kristian Høgsberg8417d432011-07-27 05:58:57 -0700202{
Scott Moreau72c23722012-04-20 13:37:34 -0600203 int output_stride, buffer_stride, i;
Kristian Høgsbergf02a6492012-03-12 01:05:25 -0400204 cairo_surface_t *surface;
Scott Moreau72c23722012-04-20 13:37:34 -0600205 void *data, *d, *s;
206 struct screenshooter_output *output, *next;
Aleksander Morgadoe3c2a762018-01-23 01:05:20 +0100207 FILE *fp;
208 char filepath[PATH_MAX];
Scott Moreau72c23722012-04-20 13:37:34 -0600209
Marius Vlad3c7cfbb2018-12-13 15:24:08 +0200210 buffer_stride = buff_size->width * 4;
Scott Moreau72c23722012-04-20 13:37:34 -0600211
Marius Vlad3c7cfbb2018-12-13 15:24:08 +0200212 data = xmalloc(buffer_stride * buff_size->height);
Scott Moreau72c23722012-04-20 13:37:34 -0600213 if (!data)
214 return;
215
Marius Vlad3c7cfbb2018-12-13 15:24:08 +0200216 wl_list_for_each_safe(output, next, output_list, link) {
Scott Moreau72c23722012-04-20 13:37:34 -0600217 output_stride = output->width * 4;
218 s = output->data;
Marius Vlad3c7cfbb2018-12-13 15:24:08 +0200219 d = data + (output->offset_y - buff_size->min_y) * buffer_stride +
220 (output->offset_x - buff_size->min_x) * 4;
Scott Moreau72c23722012-04-20 13:37:34 -0600221
222 for (i = 0; i < output->height; i++) {
223 memcpy(d, s, output_stride);
224 d += buffer_stride;
225 s += output_stride;
226 }
227
228 free(output);
229 }
Tiago Vignatti4d0d2032011-07-26 11:42:59 +0300230
Kristian Høgsbergf02a6492012-03-12 01:05:25 -0400231 surface = cairo_image_surface_create_for_data(data,
232 CAIRO_FORMAT_ARGB32,
Marius Vlad3c7cfbb2018-12-13 15:24:08 +0200233 buff_size->width,
234 buff_size->height,
235 buffer_stride);
Aleksander Morgadoe3c2a762018-01-23 01:05:20 +0100236
Aleksander Morgadoc34a9f52018-01-23 01:05:22 +0100237 fp = file_create_dated(getenv("XDG_PICTURES_DIR"), "wayland-screenshot-",
238 ".png", filepath, sizeof(filepath));
Aleksander Morgadoe3c2a762018-01-23 01:05:20 +0100239 if (fp) {
240 fclose (fp);
241 cairo_surface_write_to_png(surface, filepath);
242 }
Kristian Høgsbergf02a6492012-03-12 01:05:25 -0400243 cairo_surface_destroy(surface);
Scott Moreau72c23722012-04-20 13:37:34 -0600244 free(data);
Tiago Vignatti4d0d2032011-07-26 11:42:59 +0300245}
246
Scott Moreau2074f1d2012-04-20 13:37:35 -0600247static int
Marius Vlad3c7cfbb2018-12-13 15:24:08 +0200248screenshot_set_buffer_size(struct buffer_size *buff_size, struct wl_list *output_list)
Scott Moreau2074f1d2012-04-20 13:37:35 -0600249{
250 struct screenshooter_output *output;
Marius Vlad3c7cfbb2018-12-13 15:24:08 +0200251 buff_size->min_x = buff_size->min_y = INT_MAX;
252 buff_size->max_x = buff_size->max_y = INT_MIN;
Scott Moreaubb589832012-08-18 19:52:42 -0600253 int position = 0;
254
Marius Vlad3c7cfbb2018-12-13 15:24:08 +0200255 wl_list_for_each_reverse(output, output_list, link) {
Scott Moreaubb589832012-08-18 19:52:42 -0600256 output->offset_x = position;
257 position += output->width;
258 }
Scott Moreau2074f1d2012-04-20 13:37:35 -0600259
Marius Vlad3c7cfbb2018-12-13 15:24:08 +0200260 wl_list_for_each(output, output_list, link) {
261 buff_size->min_x = MIN(buff_size->min_x, output->offset_x);
262 buff_size->min_y = MIN(buff_size->min_y, output->offset_y);
263 buff_size->max_x =
264 MAX(buff_size->max_x, output->offset_x + output->width);
265 buff_size->max_y =
266 MAX(buff_size->max_y, output->offset_y + output->height);
Scott Moreau2074f1d2012-04-20 13:37:35 -0600267 }
268
Marius Vlad3c7cfbb2018-12-13 15:24:08 +0200269 if (buff_size->max_x <= buff_size->min_x ||
270 buff_size->max_y <= buff_size->min_y)
Scott Moreau2074f1d2012-04-20 13:37:35 -0600271 return -1;
272
Marius Vlad3c7cfbb2018-12-13 15:24:08 +0200273 buff_size->width = buff_size->max_x - buff_size->min_x;
274 buff_size->height = buff_size->max_y - buff_size->min_y;
Scott Moreau2074f1d2012-04-20 13:37:35 -0600275
276 return 0;
277}
278
Kristian Høgsberg1e4b86a2008-11-23 23:41:08 -0500279int main(int argc, char *argv[])
280{
281 struct wl_display *display;
Kristian Høgsbergfa80e112012-10-10 21:34:26 -0400282 struct wl_registry *registry;
Scott Moreau72c23722012-04-20 13:37:34 -0600283 struct screenshooter_output *output;
Marius Vlad3c7cfbb2018-12-13 15:24:08 +0200284 struct buffer_size buff_size = {};
285 struct screenshooter_data sh_data = {};
Kristian Høgsberg1e4b86a2008-11-23 23:41:08 -0500286
Kristian Høgsberg2bb3ebe2010-12-01 15:36:20 -0500287 display = wl_display_connect(NULL);
Kristian Høgsberg1e4b86a2008-11-23 23:41:08 -0500288 if (display == NULL) {
Antonio Borneo39578632019-04-26 23:57:31 +0200289 fprintf(stderr, "failed to create display: %s\n",
290 strerror(errno));
Kristian Høgsberg1e4b86a2008-11-23 23:41:08 -0500291 return -1;
292 }
293
Marius Vlad3c7cfbb2018-12-13 15:24:08 +0200294 wl_list_init(&sh_data.output_list);
Kristian Høgsbergfa80e112012-10-10 21:34:26 -0400295 registry = wl_display_get_registry(display);
Marius Vlad3c7cfbb2018-12-13 15:24:08 +0200296 wl_registry_add_listener(registry, &registry_listener, &sh_data);
Kristian Høgsbergfa80e112012-10-10 21:34:26 -0400297 wl_display_dispatch(display);
Kristian Høgsberga8d1fa72011-08-23 18:14:06 -0400298 wl_display_roundtrip(display);
Marius Vlad3c7cfbb2018-12-13 15:24:08 +0200299 if (sh_data.screenshooter == NULL) {
Kristian Høgsberg4fe1a3e2010-08-10 14:02:48 -0400300 fprintf(stderr, "display doesn't support screenshooter\n");
301 return -1;
302 }
303
Marius Vlad3c7cfbb2018-12-13 15:24:08 +0200304 weston_screenshooter_add_listener(sh_data.screenshooter,
Jonas Ådahlcf1efd22015-11-17 16:00:34 +0800305 &screenshooter_listener,
Marius Vlad3c7cfbb2018-12-13 15:24:08 +0200306 &sh_data);
Scott Moreau062be7e2012-04-20 13:37:33 -0600307
Marius Vlad3c7cfbb2018-12-13 15:24:08 +0200308 if (screenshot_set_buffer_size(&buff_size, &sh_data.output_list))
Scott Moreau2074f1d2012-04-20 13:37:35 -0600309 return -1;
310
Scott Moreau062be7e2012-04-20 13:37:33 -0600311
Marius Vlad3c7cfbb2018-12-13 15:24:08 +0200312 wl_list_for_each(output, &sh_data.output_list, link) {
313 output->buffer =
314 screenshot_create_shm_buffer(output->width,
315 output->height,
316 &output->data,
317 sh_data.shm);
Leandro Ribeirobc567292020-10-17 13:16:37 -0300318 weston_screenshooter_take_shot(sh_data.screenshooter,
319 output->output,
320 output->buffer);
Marius Vlad3c7cfbb2018-12-13 15:24:08 +0200321 sh_data.buffer_copy_done = 0;
322 while (!sh_data.buffer_copy_done)
Scott Moreau062be7e2012-04-20 13:37:33 -0600323 wl_display_roundtrip(display);
Scott Moreau80d27b72012-04-04 11:49:21 -0600324 }
325
Marius Vlad3c7cfbb2018-12-13 15:24:08 +0200326 screenshot_write_png(&buff_size, &sh_data.output_list);
Kristian Høgsberg1e4b86a2008-11-23 23:41:08 -0500327
328 return 0;
329}