screenshooter: Grab pixel data directly before buffer swap
[profile/ivi/weston-ivi-shell.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 <stdint.h>
24 #include <stdlib.h>
25 #include <stdio.h>
26 #include <string.h>
27 #include <fcntl.h>
28 #include <unistd.h>
29 #include <sys/mman.h>
30 #include <cairo.h>
31
32 #include <wayland-client.h>
33 #include "screenshooter-client-protocol.h"
34
35 #define MAX(X,Y) ((X) > (Y) ? (X) : (Y))
36
37 /* The screenshooter is a good example of a custom object exposed by
38  * the compositor and serves as a test bed for implementing client
39  * side marshalling outside libwayland.so */
40
41 static struct wl_shm *shm;
42 static struct screenshooter *screenshooter;
43 static struct wl_list output_list;
44 int buffer_copy_done;
45
46 struct screenshooter_output {
47         struct wl_output *output;
48         struct wl_buffer *buffer;
49         int width, height, offset_x, offset_y;
50         struct wl_list link;
51 };
52
53 static void
54 display_handle_geometry(void *data,
55                         struct wl_output *wl_output,
56                         int x,
57                         int y,
58                         int physical_width,
59                         int physical_height,
60                         int subpixel,
61                         const char *make,
62                         const char *model)
63 {
64         struct screenshooter_output *output;
65
66         output = wl_output_get_user_data(wl_output);
67
68         if (wl_output == output->output) {
69                 output->offset_x = x;
70                 output->offset_y = y;
71         }
72 }
73
74 static void
75 display_handle_mode(void *data,
76                     struct wl_output *wl_output,
77                     uint32_t flags,
78                     int width,
79                     int height,
80                     int refresh)
81 {
82         struct screenshooter_output *output;
83
84         output = wl_output_get_user_data(wl_output);
85
86         if (wl_output == output->output && (flags & WL_OUTPUT_MODE_CURRENT)) {
87                 output->width = width;
88                 output->height = height;
89         }
90 }
91
92 static const struct wl_output_listener output_listener = {
93         display_handle_geometry,
94         display_handle_mode
95 };
96
97 static void
98 screenshot_done(void *data, struct screenshooter *screenshooter)
99 {
100         buffer_copy_done = 1;
101 }
102
103 static const struct screenshooter_listener screenshooter_listener = {
104         screenshot_done
105 };
106
107 static void
108 handle_global(struct wl_display *display, uint32_t id,
109               const char *interface, uint32_t version, void *data)
110 {
111         static struct screenshooter_output *output;
112
113         if (strcmp(interface, "wl_output") == 0) {
114                 output = malloc(sizeof *output);
115                 output->output = wl_display_bind(display, id, &wl_output_interface);
116                 wl_list_insert(&output_list, &output->link);
117                 wl_output_add_listener(output->output, &output_listener, output);
118         } else if (strcmp(interface, "wl_shm") == 0) {
119                 shm = wl_display_bind(display, id, &wl_shm_interface);
120         } else if (strcmp(interface, "screenshooter") == 0) {
121                 screenshooter = wl_display_bind(display, id, &screenshooter_interface);
122         }
123 }
124
125 static struct wl_buffer *
126 create_shm_buffer(int width, int height, void **data_out)
127 {
128         char filename[] = "/tmp/wayland-shm-XXXXXX";
129         struct wl_shm_pool *pool;
130         struct wl_buffer *buffer;
131         int fd, size, stride;
132         void *data;
133
134         fd = mkstemp(filename);
135         if (fd < 0) {
136                 fprintf(stderr, "open %s failed: %m\n", filename);
137                 return NULL;
138         }
139         stride = width * 4;
140         size = stride * height;
141         if (ftruncate(fd, size) < 0) {
142                 fprintf(stderr, "ftruncate failed: %m\n");
143                 close(fd);
144                 return NULL;
145         }
146
147         data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
148         unlink(filename);
149
150         if (data == MAP_FAILED) {
151                 fprintf(stderr, "mmap failed: %m\n");
152                 close(fd);
153                 return NULL;
154         }
155
156         pool = wl_shm_create_pool(shm, fd, size);
157         close(fd);
158         buffer = wl_shm_pool_create_buffer(pool, 0, width, height, stride,
159                                            WL_SHM_FORMAT_XRGB8888);
160         wl_shm_pool_destroy(pool);
161
162         *data_out = data;
163
164         return buffer;
165 }
166
167 static void
168 write_png(int width, int height, void *data)
169 {
170         cairo_surface_t *surface;
171
172         surface = cairo_image_surface_create_for_data(data,
173                                                       CAIRO_FORMAT_ARGB32,
174                                                       width, height, width * 4);
175         cairo_surface_write_to_png(surface, "wayland-screenshot.png");
176         cairo_surface_destroy(surface);
177 }
178
179 int main(int argc, char *argv[])
180 {
181         struct wl_display *display;
182         struct wl_buffer *buffer;
183         void *data = NULL;
184         struct screenshooter_output *output, *next;
185         int width = 0, height = 0;
186
187         display = wl_display_connect(NULL);
188         if (display == NULL) {
189                 fprintf(stderr, "failed to create display: %m\n");
190                 return -1;
191         }
192
193         wl_list_init(&output_list);
194         wl_display_add_global_listener(display, handle_global, &screenshooter);
195         wl_display_iterate(display, WL_DISPLAY_READABLE);
196         wl_display_roundtrip(display);
197         if (screenshooter == NULL) {
198                 fprintf(stderr, "display doesn't support screenshooter\n");
199                 return -1;
200         }
201
202         screenshooter_add_listener(screenshooter, &screenshooter_listener, screenshooter);
203
204
205         wl_list_for_each(output, &output_list, link) {
206                 width = MAX(width, output->offset_x + output->width);
207                 height = MAX(height, output->offset_y + output->height);
208         }
209
210         buffer = create_shm_buffer(width, height, &data);
211
212         wl_list_for_each_safe(output, next, &output_list, link) {
213                 screenshooter_shoot(screenshooter, output->output, buffer);
214                 buffer_copy_done = 0;
215                 while (!buffer_copy_done)
216                         wl_display_roundtrip(display);
217                 free(output);
218         }
219
220         write_png(width, height, data);
221
222         return 0;
223 }