1 /*
2 * Copyright (C) 2010 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include <errno.h>
18 #include <unistd.h>
19 #include <stdio.h>
20 #include <fcntl.h>
21 #include <stdlib.h>
22 #include <string.h>
23
24 #include <linux/fb.h>
25 #include <sys/ioctl.h>
26 #include <sys/mman.h>
27
28 #include <binder/ProcessState.h>
29
30 #include <gui/SurfaceComposerClient.h>
31 #include <gui/ISurfaceComposer.h>
32
33 #include <ui/DisplayInfo.h>
34 #include <ui/PixelFormat.h>
35
36 // TODO: Fix Skia.
37 #pragma GCC diagnostic push
38 #pragma GCC diagnostic ignored "-Wunused-parameter"
39 #include <SkImageEncoder.h>
40 #include <SkData.h>
41 #pragma GCC diagnostic pop
42
43 using namespace android;
44
45 static uint32_t DEFAULT_DISPLAY_ID = ISurfaceComposer::eDisplayIdMain;
46
usage(const char * pname)47 static void usage(const char* pname)
48 {
49 fprintf(stderr,
50 "usage: %s [-hp] [-d display-id] [FILENAME]\n"
51 " -h: this message\n"
52 " -p: save the file as a png.\n"
53 " -d: specify the display id to capture, default %d.\n"
54 "If FILENAME ends with .png it will be saved as a png.\n"
55 "If FILENAME is not given, the results will be printed to stdout.\n",
56 pname, DEFAULT_DISPLAY_ID
57 );
58 }
59
flinger2skia(PixelFormat f)60 static SkColorType flinger2skia(PixelFormat f)
61 {
62 switch (f) {
63 case PIXEL_FORMAT_RGB_565:
64 return kRGB_565_SkColorType;
65 default:
66 return kN32_SkColorType;
67 }
68 }
69
vinfoToPixelFormat(const fb_var_screeninfo & vinfo,uint32_t * bytespp,uint32_t * f)70 static status_t vinfoToPixelFormat(const fb_var_screeninfo& vinfo,
71 uint32_t* bytespp, uint32_t* f)
72 {
73
74 switch (vinfo.bits_per_pixel) {
75 case 16:
76 *f = PIXEL_FORMAT_RGB_565;
77 *bytespp = 2;
78 break;
79 case 24:
80 *f = PIXEL_FORMAT_RGB_888;
81 *bytespp = 3;
82 break;
83 case 32:
84 // TODO: do better decoding of vinfo here
85 *f = PIXEL_FORMAT_RGBX_8888;
86 *bytespp = 4;
87 break;
88 default:
89 return BAD_VALUE;
90 }
91 return NO_ERROR;
92 }
93
notifyMediaScanner(const char * fileName)94 static status_t notifyMediaScanner(const char* fileName) {
95 String8 cmd("am broadcast -a android.intent.action.MEDIA_SCANNER_SCAN_FILE -d file://");
96 String8 fileUrl("\"");
97 fileUrl.append(fileName);
98 fileUrl.append("\"");
99 cmd.append(fileName);
100 cmd.append(" > /dev/null");
101 int result = system(cmd.string());
102 if (result < 0) {
103 fprintf(stderr, "Unable to broadcast intent for media scanner.\n");
104 return UNKNOWN_ERROR;
105 }
106 return NO_ERROR;
107 }
108
main(int argc,char ** argv)109 int main(int argc, char** argv)
110 {
111 ProcessState::self()->startThreadPool();
112
113 const char* pname = argv[0];
114 bool png = false;
115 int32_t displayId = DEFAULT_DISPLAY_ID;
116 int c;
117 while ((c = getopt(argc, argv, "phd:")) != -1) {
118 switch (c) {
119 case 'p':
120 png = true;
121 break;
122 case 'd':
123 displayId = atoi(optarg);
124 break;
125 case '?':
126 case 'h':
127 usage(pname);
128 return 1;
129 }
130 }
131 argc -= optind;
132 argv += optind;
133
134 int fd = -1;
135 const char* fn = NULL;
136 if (argc == 0) {
137 fd = dup(STDOUT_FILENO);
138 } else if (argc == 1) {
139 fn = argv[0];
140 fd = open(fn, O_WRONLY | O_CREAT | O_TRUNC, 0664);
141 if (fd == -1) {
142 fprintf(stderr, "Error opening file: %s (%s)\n", fn, strerror(errno));
143 return 1;
144 }
145 const int len = strlen(fn);
146 if (len >= 4 && 0 == strcmp(fn+len-4, ".png")) {
147 png = true;
148 }
149 }
150
151 if (fd == -1) {
152 usage(pname);
153 return 1;
154 }
155
156 void const* mapbase = MAP_FAILED;
157 ssize_t mapsize = -1;
158
159 void const* base = NULL;
160 uint32_t w, s, h, f;
161 size_t size = 0;
162
163 // Maps orientations from DisplayInfo to ISurfaceComposer
164 static const uint32_t ORIENTATION_MAP[] = {
165 ISurfaceComposer::eRotateNone, // 0 == DISPLAY_ORIENTATION_0
166 ISurfaceComposer::eRotate270, // 1 == DISPLAY_ORIENTATION_90
167 ISurfaceComposer::eRotate180, // 2 == DISPLAY_ORIENTATION_180
168 ISurfaceComposer::eRotate90, // 3 == DISPLAY_ORIENTATION_270
169 };
170
171 ScreenshotClient screenshot;
172 sp<IBinder> display = SurfaceComposerClient::getBuiltInDisplay(displayId);
173 if (display == NULL) {
174 fprintf(stderr, "Unable to get handle for display %d\n", displayId);
175 return 1;
176 }
177
178 Vector<DisplayInfo> configs;
179 SurfaceComposerClient::getDisplayConfigs(display, &configs);
180 int activeConfig = SurfaceComposerClient::getActiveConfig(display);
181 if (static_cast<size_t>(activeConfig) >= configs.size()) {
182 fprintf(stderr, "Active config %d not inside configs (size %zu)\n",
183 activeConfig, configs.size());
184 return 1;
185 }
186 uint8_t displayOrientation = configs[activeConfig].orientation;
187 uint32_t captureOrientation = ORIENTATION_MAP[displayOrientation];
188
189 status_t result = screenshot.update(display, Rect(), 0, 0, 0, -1U,
190 false, captureOrientation);
191 if (result == NO_ERROR) {
192 base = screenshot.getPixels();
193 w = screenshot.getWidth();
194 h = screenshot.getHeight();
195 s = screenshot.getStride();
196 f = screenshot.getFormat();
197 size = screenshot.getSize();
198 } else {
199 const char* fbpath = "/dev/graphics/fb0";
200 int fb = open(fbpath, O_RDONLY);
201 if (fb >= 0) {
202 struct fb_var_screeninfo vinfo;
203 if (ioctl(fb, FBIOGET_VSCREENINFO, &vinfo) == 0) {
204 uint32_t bytespp;
205 if (vinfoToPixelFormat(vinfo, &bytespp, &f) == NO_ERROR) {
206 size_t offset = (vinfo.xoffset + vinfo.yoffset*vinfo.xres) * bytespp;
207 w = vinfo.xres;
208 h = vinfo.yres;
209 s = vinfo.xres;
210 size = w*h*bytespp;
211 mapsize = offset + size;
212 mapbase = mmap(0, mapsize, PROT_READ, MAP_PRIVATE, fb, 0);
213 if (mapbase != MAP_FAILED) {
214 base = (void const *)((char const *)mapbase + offset);
215 }
216 }
217 }
218 close(fb);
219 }
220 }
221
222 if (base != NULL) {
223 if (png) {
224 const SkImageInfo info = SkImageInfo::Make(w, h, flinger2skia(f),
225 kPremul_SkAlphaType);
226 SkAutoTUnref<SkData> data(SkImageEncoder::EncodeData(info, base, s*bytesPerPixel(f),
227 SkImageEncoder::kPNG_Type, SkImageEncoder::kDefaultQuality));
228 if (data.get()) {
229 write(fd, data->data(), data->size());
230 }
231 if (fn != NULL) {
232 notifyMediaScanner(fn);
233 }
234 } else {
235 write(fd, &w, 4);
236 write(fd, &h, 4);
237 write(fd, &f, 4);
238 size_t Bpp = bytesPerPixel(f);
239 for (size_t y=0 ; y<h ; y++) {
240 write(fd, base, w*Bpp);
241 base = (void *)((char *)base + s*Bpp);
242 }
243 }
244 }
245 close(fd);
246 if (mapbase != MAP_FAILED) {
247 munmap((void *)mapbase, mapsize);
248 }
249 return 0;
250 }
251