blob: cca80c3c1abdf9833db55461f7068f96a16c8853 [file] [log] [blame]
Daniel Sandler27e1a792010-07-27 14:46:34 -04001#include <stdlib.h>
2#include <stdio.h>
3#include <unistd.h>
4#include <string.h>
5#include <fcntl.h>
6#include <errno.h>
7
8#include <linux/fb.h>
9
10#include <zlib.h>
11#include <libpng/png.h>
12
13#include "private/android_filesystem_config.h"
14
15#define LOG_TAG "screenshot"
16#include <utils/Log.h>
17
18void take_screenshot(FILE *fb_in, FILE *fb_out) {
19 int fb;
20 char imgbuf[0x10000];
21 struct fb_var_screeninfo vinfo;
22 png_structp png;
23 png_infop info;
24 unsigned int r,c,rowlen;
25 unsigned int bytespp,offset;
26
27 fb = fileno(fb_in);
28 if(fb < 0) {
Steve Blockc6aacce2012-01-06 19:20:56 +000029 ALOGE("failed to open framebuffer\n");
Daniel Sandler27e1a792010-07-27 14:46:34 -040030 return;
31 }
32 fb_in = fdopen(fb, "r");
33
34 if(ioctl(fb, FBIOGET_VSCREENINFO, &vinfo) < 0) {
Steve Blockc6aacce2012-01-06 19:20:56 +000035 ALOGE("failed to get framebuffer info\n");
Daniel Sandler27e1a792010-07-27 14:46:34 -040036 return;
37 }
38 fcntl(fb, F_SETFD, FD_CLOEXEC);
39
40 png = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
41 if (png == NULL) {
Steve Blockc6aacce2012-01-06 19:20:56 +000042 ALOGE("failed png_create_write_struct\n");
Daniel Sandler27e1a792010-07-27 14:46:34 -040043 fclose(fb_in);
44 return;
45 }
46
47 png_init_io(png, fb_out);
48 info = png_create_info_struct(png);
49 if (info == NULL) {
Steve Blockc6aacce2012-01-06 19:20:56 +000050 ALOGE("failed png_create_info_struct\n");
Daniel Sandler27e1a792010-07-27 14:46:34 -040051 png_destroy_write_struct(&png, NULL);
52 fclose(fb_in);
53 return;
54 }
55 if (setjmp(png_jmpbuf(png))) {
Steve Blockc6aacce2012-01-06 19:20:56 +000056 ALOGE("failed png setjmp\n");
Daniel Sandler27e1a792010-07-27 14:46:34 -040057 png_destroy_write_struct(&png, NULL);
58 fclose(fb_in);
59 return;
60 }
61
62 bytespp = vinfo.bits_per_pixel / 8;
63 png_set_IHDR(png, info,
64 vinfo.xres, vinfo.yres, vinfo.bits_per_pixel / 4,
65 PNG_COLOR_TYPE_RGB_ALPHA, PNG_INTERLACE_NONE,
66 PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
67 png_write_info(png, info);
68
69 rowlen=vinfo.xres * bytespp;
70 if (rowlen > sizeof(imgbuf)) {
Steve Blockc6aacce2012-01-06 19:20:56 +000071 ALOGE("crazy rowlen: %d\n", rowlen);
Daniel Sandler27e1a792010-07-27 14:46:34 -040072 png_destroy_write_struct(&png, NULL);
73 fclose(fb_in);
74 return;
75 }
76
77 offset = vinfo.xoffset * bytespp + vinfo.xres * vinfo.yoffset * bytespp;
78 fseek(fb_in, offset, SEEK_SET);
79
80 for(r=0; r<vinfo.yres; r++) {
81 int len = fread(imgbuf, 1, rowlen, fb_in);
82 if (len <= 0) break;
83 png_write_row(png, (png_bytep)imgbuf);
84 }
85
86 png_write_end(png, info);
87 fclose(fb_in);
88 png_destroy_write_struct(&png, NULL);
89}
90
Daniel Sandlere9ddcba2010-08-20 15:07:53 -040091void fork_sound(const char* path) {
92 pid_t pid = fork();
93 if (pid == 0) {
94 execl("/system/bin/stagefright", "stagefright", "-o", "-a", path, NULL);
95 }
96}
97
98void usage() {
99 fprintf(stderr,
100 "usage: screenshot [-s soundfile] filename.png\n"
101 " -s: play a sound effect to signal success\n"
102 " -i: autoincrement to avoid overwriting filename.png\n"
103 );
104}
105
Daniel Sandler27e1a792010-07-27 14:46:34 -0400106int main(int argc, char**argv) {
107 FILE *png = NULL;
108 FILE *fb_in = NULL;
Daniel Sandlere9ddcba2010-08-20 15:07:53 -0400109 char outfile[PATH_MAX] = "";
110
111 char * soundfile = NULL;
112 int do_increment = 0;
113
114 int c;
115 while ((c = getopt(argc, argv, "s:i")) != -1) {
116 switch (c) {
117 case 's': soundfile = optarg; break;
118 case 'i': do_increment = 1; break;
119 case '?':
120 case 'h':
121 usage(); exit(1);
122 }
Daniel Sandler27e1a792010-07-27 14:46:34 -0400123 }
Daniel Sandlere9ddcba2010-08-20 15:07:53 -0400124 argc -= optind;
125 argv += optind;
126
127 if (argc < 1) {
128 usage(); exit(1);
129 }
130
131 strlcpy(outfile, argv[0], PATH_MAX);
132 if (do_increment) {
133 struct stat st;
134 char base[PATH_MAX] = "";
135 int i = 0;
136 while (stat(outfile, &st) == 0) {
137 if (!base[0]) {
138 char *p = strrchr(outfile, '.');
139 if (p) *p = '\0';
140 strcpy(base, outfile);
141 }
142 snprintf(outfile, PATH_MAX, "%s-%d.png", base, ++i);
143 }
144 }
145
Daniel Sandler27e1a792010-07-27 14:46:34 -0400146 fb_in = fopen("/dev/graphics/fb0", "r");
147 if (!fb_in) {
148 fprintf(stderr, "error: could not read framebuffer\n");
149 exit(1);
150 }
151
152 /* switch to non-root user and group */
153 gid_t groups[] = { AID_LOG, AID_SDCARD_RW };
154 setgroups(sizeof(groups)/sizeof(groups[0]), groups);
155 setuid(AID_SHELL);
156
Daniel Sandlere9ddcba2010-08-20 15:07:53 -0400157 png = fopen(outfile, "w");
Daniel Sandler27e1a792010-07-27 14:46:34 -0400158 if (!png) {
Daniel Sandlere9ddcba2010-08-20 15:07:53 -0400159 fprintf(stderr, "error: writing file %s: %s\n",
160 outfile, strerror(errno));
Daniel Sandler27e1a792010-07-27 14:46:34 -0400161 exit(1);
162 }
163
164 take_screenshot(fb_in, png);
165
Daniel Sandlere9ddcba2010-08-20 15:07:53 -0400166 if (soundfile) {
167 fork_sound(soundfile);
168 }
169
Daniel Sandler27e1a792010-07-27 14:46:34 -0400170 exit(0);
171}