compositor-x11: Rename the output make to "weston-X11"
[platform/upstream/weston.git] / clients / screenshot.c
1 /*
2  * Copyright © 2008 Kristian Høgsberg
3  *
4  * Permission to use, copy, modify, distribute, and sell this software and its
5  * documentation for any purpose is hereby granted without fee, provided that
6  * the above copyright notice appear in all copies and that both that copyright
7  * notice and this permission notice appear in supporting documentation, and
8  * that the name of the copyright holders not be used in advertising or
9  * publicity pertaining to distribution of the software without specific,
10  * written prior permission.  The copyright holders make no representations
11  * about the suitability of this software for any purpose.  It is provided "as
12  * is" without express or implied warranty.
13  *
14  * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
15  * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
16  * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
17  * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
18  * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
19  * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
20  * OF THIS SOFTWARE.
21  */
22
23 #include "config.h"
24
25 #include <stdint.h>
26 #include <errno.h>
27 #include <stdlib.h>
28 #include <stdio.h>
29 #include <string.h>
30 #include <fcntl.h>
31 #include <unistd.h>
32 #include <limits.h>
33 #include <sys/param.h>
34 #include <sys/mman.h>
35 #include <cairo.h>
36
37 #include <wayland-client.h>
38 #include "screenshooter-client-protocol.h"
39 #include "../shared/os-compatibility.h"
40
41 /* The screenshooter is a good example of a custom object exposed by
42  * the compositor and serves as a test bed for implementing client
43  * side marshalling outside libwayland.so */
44
45 static struct wl_shm *shm;
46 static struct screenshooter *screenshooter;
47 static struct wl_list output_list;
48 int min_x, min_y, max_x, max_y;
49 int buffer_copy_done;
50
51 struct screenshooter_output {
52         struct wl_output *output;
53         struct wl_buffer *buffer;
54         int width, height, offset_x, offset_y;
55         void *data;
56         struct wl_list link;
57 };
58
59 static void
60 display_handle_geometry(void *data,
61                         struct wl_output *wl_output,
62                         int x,
63                         int y,
64                         int physical_width,
65                         int physical_height,
66                         int subpixel,
67                         const char *make,
68                         const char *model,
69                         int transform)
70 {
71         struct screenshooter_output *output;
72
73         output = wl_output_get_user_data(wl_output);
74
75         if (wl_output == output->output) {
76                 output->offset_x = x;
77                 output->offset_y = y;
78         }
79 }
80
81 static void *
82 xmalloc(size_t size)
83 {
84         void *p;
85
86         p = malloc(size);
87         if (p == NULL) {
88                 fprintf(stderr, "%s: out of memory\n",
89                         program_invocation_short_name);
90                 exit(EXIT_FAILURE);
91         }
92
93         return p;
94 }
95
96 static void
97 display_handle_mode(void *data,
98                     struct wl_output *wl_output,
99                     uint32_t flags,
100                     int width,
101                     int height,
102                     int refresh)
103 {
104         struct screenshooter_output *output;
105
106         output = wl_output_get_user_data(wl_output);
107
108         if (wl_output == output->output && (flags & WL_OUTPUT_MODE_CURRENT)) {
109                 output->width = width;
110                 output->height = height;
111         }
112 }
113
114 static const struct wl_output_listener output_listener = {
115         display_handle_geometry,
116         display_handle_mode
117 };
118
119 static void
120 screenshot_done(void *data, struct screenshooter *screenshooter)
121 {
122         buffer_copy_done = 1;
123 }
124
125 static const struct screenshooter_listener screenshooter_listener = {
126         screenshot_done
127 };
128
129 static void
130 handle_global(void *data, struct wl_registry *registry,
131               uint32_t name, const char *interface, uint32_t version)
132 {
133         static struct screenshooter_output *output;
134
135         if (strcmp(interface, "wl_output") == 0) {
136                 output = xmalloc(sizeof *output);
137                 output->output = wl_registry_bind(registry, name,
138                                                   &wl_output_interface, 1);
139                 wl_list_insert(&output_list, &output->link);
140                 wl_output_add_listener(output->output, &output_listener, output);
141         } else if (strcmp(interface, "wl_shm") == 0) {
142                 shm = wl_registry_bind(registry, name, &wl_shm_interface, 1);
143         } else if (strcmp(interface, "screenshooter") == 0) {
144                 screenshooter = wl_registry_bind(registry, name,
145                                                  &screenshooter_interface, 1);
146         }
147 }
148
149 static void
150 handle_global_remove(void *data, struct wl_registry *registry, uint32_t name)
151 {
152         /* XXX: unimplemented */
153 }
154
155 static const struct wl_registry_listener registry_listener = {
156         handle_global,
157         handle_global_remove
158 };
159
160 static struct wl_buffer *
161 create_shm_buffer(int width, int height, void **data_out)
162 {
163         struct wl_shm_pool *pool;
164         struct wl_buffer *buffer;
165         int fd, size, stride;
166         void *data;
167
168         stride = width * 4;
169         size = stride * height;
170
171         fd = os_create_anonymous_file(size);
172         if (fd < 0) {
173                 fprintf(stderr, "creating a buffer file for %d B failed: %m\n",
174                         size);
175                 return NULL;
176         }
177
178         data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
179         if (data == MAP_FAILED) {
180                 fprintf(stderr, "mmap failed: %m\n");
181                 close(fd);
182                 return NULL;
183         }
184
185         pool = wl_shm_create_pool(shm, fd, size);
186         close(fd);
187         buffer = wl_shm_pool_create_buffer(pool, 0, width, height, stride,
188                                            WL_SHM_FORMAT_XRGB8888);
189         wl_shm_pool_destroy(pool);
190
191         *data_out = data;
192
193         return buffer;
194 }
195
196 static void
197 write_png(int width, int height)
198 {
199         int output_stride, buffer_stride, i;
200         cairo_surface_t *surface;
201         void *data, *d, *s;
202         struct screenshooter_output *output, *next;
203
204         buffer_stride = width * 4;
205
206         data = xmalloc(buffer_stride * height);
207         if (!data)
208                 return;
209
210         wl_list_for_each_safe(output, next, &output_list, link) {
211                 output_stride = output->width * 4;
212                 s = output->data;
213                 d = data + (output->offset_y - min_y) * buffer_stride +
214                            (output->offset_x - min_x) * 4;
215
216                 for (i = 0; i < output->height; i++) {
217                         memcpy(d, s, output_stride);
218                         d += buffer_stride;
219                         s += output_stride;
220                 }
221
222                 free(output);
223         }
224
225         surface = cairo_image_surface_create_for_data(data,
226                                                       CAIRO_FORMAT_ARGB32,
227                                                       width, height, buffer_stride);
228         cairo_surface_write_to_png(surface, "wayland-screenshot.png");
229         cairo_surface_destroy(surface);
230         free(data);
231 }
232
233 static int
234 set_buffer_size(int *width, int *height)
235 {
236         struct screenshooter_output *output;
237         min_x = min_y = INT_MAX;
238         max_x = max_y = INT_MIN;
239         int position = 0;
240
241         wl_list_for_each_reverse(output, &output_list, link) {
242                 output->offset_x = position;
243                 position += output->width;
244         }
245
246         wl_list_for_each(output, &output_list, link) {
247                 min_x = MIN(min_x, output->offset_x);
248                 min_y = MIN(min_y, output->offset_y);
249                 max_x = MAX(max_x, output->offset_x + output->width);
250                 max_y = MAX(max_y, output->offset_y + output->height);
251         }
252
253         if (max_x <= min_x || max_y <= min_y)
254                 return -1;
255
256         *width = max_x - min_x;
257         *height = max_y - min_y;
258
259         return 0;
260 }
261
262 int main(int argc, char *argv[])
263 {
264         struct wl_display *display;
265         struct wl_registry *registry;
266         struct screenshooter_output *output;
267         int width, height;
268
269         if (getenv("WAYLAND_SOCKET") == NULL) {
270                 fprintf(stderr, "%s must be launched by weston.\n"
271                         "Use the MOD+S shortcut to take a screenshot.\n",
272                         program_invocation_short_name);
273                 return -1;
274         }
275
276         display = wl_display_connect(NULL);
277         if (display == NULL) {
278                 fprintf(stderr, "failed to create display: %m\n");
279                 return -1;
280         }
281
282         wl_list_init(&output_list);
283         registry = wl_display_get_registry(display);
284         wl_registry_add_listener(registry, &registry_listener, NULL);
285         wl_display_dispatch(display);
286         wl_display_roundtrip(display);
287         if (screenshooter == NULL) {
288                 fprintf(stderr, "display doesn't support screenshooter\n");
289                 return -1;
290         }
291
292         screenshooter_add_listener(screenshooter, &screenshooter_listener, screenshooter);
293
294         if (set_buffer_size(&width, &height))
295                 return -1;
296
297
298         wl_list_for_each(output, &output_list, link) {
299                 output->buffer = create_shm_buffer(output->width, output->height, &output->data);
300                 screenshooter_shoot(screenshooter, output->output, output->buffer);
301                 buffer_copy_done = 0;
302                 while (!buffer_copy_done)
303                         wl_display_roundtrip(display);
304         }
305
306         write_png(width, height);
307
308         return 0;
309 }