blob: 57ec9258e308d439059929898bdfa17bd8b9e4bf [file] [log] [blame]
Pekka Paalanen312fe5f2015-02-09 11:23:48 +02001/*
2 * Copyright © 2015 Collabora, Ltd.
3 *
4 * Permission to use, copy, modify, distribute, and sell this software and
5 * its documentation for any purpose is hereby granted without fee, provided
6 * that the above copyright notice appear in all copies and that both that
7 * copyright notice and this permission notice appear in supporting
8 * documentation, and that the name of the copyright holders not be used in
9 * advertising or publicity pertaining to distribution of the software
10 * without specific, written prior permission. The copyright holders make
11 * no representations about the suitability of this software for any
12 * purpose. It is provided "as is" without express or implied warranty.
13 *
14 * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
15 * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
16 * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
17 * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
18 * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
19 * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
20 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
21 */
22
23#include "config.h"
24
25#include <stdlib.h>
26#include <assert.h>
27#include <ctype.h>
28#include <string.h>
29#include <errno.h>
30#include <linux/input.h>
31
32#include "compositor.h"
33#include "file-util.h"
34
35static char *
36encode_PAM_comment_line(const char *comment)
37{
38 size_t len = strlen(comment);
39 char *str = malloc(len + 2);
40 char *dst = str;
41 const char *src = comment;
42 const char *end = src + len;
43
44 *dst++ = '#';
45 *dst++ = ' ';
46 for (; src < end; src++, dst++) {
47 if (*src == '\n' || !isprint(*src))
48 *dst = '_';
49 else
50 *dst = *src;
51 }
52
53 return str;
54}
55
56/*
57 * PAM image format:
58 * http://en.wikipedia.org/wiki/Netpbm#PAM_graphics_format
59 * RGBA is in byte address order.
60 *
61 * ImageMagick 'convert' can convert a PAM image to a more common format.
62 * To view the image metadata: $ head -n7 image.pam
63 */
64static int
65write_PAM_image_rgba(FILE *fp, int width, int height,
66 void *pixels, size_t size, const char *comment)
67{
68 char *str;
69 int ret;
70
71 assert(size == (size_t)4 * width * height);
72
73 ret = fprintf(fp, "P7\nWIDTH %d\nHEIGHT %d\nDEPTH 4\nMAXVAL 255\n"
74 "TUPLTYPE RGB_ALPHA\n", width, height);
75 if (ret < 0)
76 return -1;
77
78 if (comment) {
79 str = encode_PAM_comment_line(comment);
80 ret = fprintf(fp, "%s\n", str);
81 free(str);
82
83 if (ret < 0)
84 return -1;
85 }
86
87 ret = fprintf(fp, "ENDHDR\n");
88 if (ret < 0)
89 return -1;
90
91 if (fwrite(pixels, 1, size, fp) != size)
92 return -1;
93
94 if (ferror(fp))
95 return -1;
96
97 return 0;
98}
99
100static uint32_t
101unmult(uint32_t c, uint32_t a)
102{
103 return (c * 255 + a / 2) / a;
104}
105
106static void
107unpremultiply_and_swap_a8b8g8r8_to_PAMrgba(void *pixels, size_t size)
108{
109 uint32_t *p = pixels;
110 uint32_t *end;
111
112 for (end = p + size / 4; p < end; p++) {
113 uint32_t v = *p;
114 uint32_t a;
115
116 a = (v & 0xff000000) >> 24;
117 if (a == 0) {
118 *p = 0;
119 } else {
120 uint8_t *dst = (uint8_t *)p;
121
122 dst[0] = unmult((v & 0x000000ff) >> 0, a);
123 dst[1] = unmult((v & 0x0000ff00) >> 8, a);
124 dst[2] = unmult((v & 0x00ff0000) >> 16, a);
125 dst[3] = a;
126 }
127 }
128}
129
130static void
131trigger_binding(struct weston_seat *seat, uint32_t time, uint32_t key,
132 void *data)
133{
134 const char *prefix = "surfaceshot-";
135 const char *suffix = ".pam";
136 char fname[1024];
137 struct weston_surface *surface;
138 int width, height;
139 char desc[512];
140 void *pixels;
141 const size_t bytespp = 4; /* PIXMAN_a8b8g8r8 */
142 size_t sz;
143 int ret;
144 FILE *fp;
145
146 if (seat->pointer_device_count == 0 ||
147 !seat->pointer ||
148 !seat->pointer->focus)
149 return;
150
151 surface = seat->pointer->focus->surface;
152
153 weston_surface_get_content_size(surface, &width, &height);
154
155 if (!surface->get_label ||
156 surface->get_label(surface, desc, sizeof(desc)) < 0)
157 snprintf(desc, sizeof(desc), "(unknown)");
158
159 weston_log("surface screenshot of %p: '%s', %dx%d\n",
160 surface, desc, width, height);
161
162 sz = width * bytespp * height;
163 if (sz == 0) {
164 weston_log("no content for %p\n", surface);
165 return;
166 }
167
168 pixels = malloc(sz);
169 if (!pixels) {
170 weston_log("%s: failed to malloc %zu B\n", __func__, sz);
171 return;
172 }
173
174 ret = weston_surface_copy_content(surface, pixels, sz,
175 0, 0, width, height);
176 if (ret < 0) {
177 weston_log("shooting surface %p failed\n", surface);
178 goto out;
179 }
180
181 unpremultiply_and_swap_a8b8g8r8_to_PAMrgba(pixels, sz);
182
183 fp = file_create_dated(prefix, suffix, fname, sizeof(fname));
184 if (!fp) {
185 const char *msg;
186
187 switch (errno) {
188 case ETIME:
189 msg = "failure in datetime formatting";
190 break;
191 default:
192 msg = strerror(errno);
193 }
194
195 weston_log("Cannot open '%s*%s' for writing: %s\n",
196 prefix, suffix, msg);
197 goto out;
198 }
199
200 ret = write_PAM_image_rgba(fp, width, height, pixels, sz, desc);
201 if (fclose(fp) != 0 || ret < 0)
202 weston_log("writing surface %p screenshot failed.\n", surface);
203 else
204 weston_log("successfully shot surface %p into '%s'\n",
205 surface, fname);
206
207out:
208 free(pixels);
209}
210
211WL_EXPORT int
212module_init(struct weston_compositor *ec,
213 int *argc, char *argv[])
214{
215 weston_compositor_add_debug_binding(ec, KEY_H, trigger_binding, ec);
216
217 return 0;
218}