| Mike Lockwood | c59b2f9 | 2012-10-24 12:31:10 -0700 | [diff] [blame] | 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> | 
| Umair Khan | cfed232 | 2014-01-15 08:08:50 -0500 | [diff] [blame] | 21 | #include <stdlib.h> | 
 | 22 | #include <string.h> | 
| Mike Lockwood | c59b2f9 | 2012-10-24 12:31:10 -0700 | [diff] [blame] | 23 |  | 
 | 24 | #include <linux/fb.h> | 
 | 25 | #include <sys/ioctl.h> | 
 | 26 | #include <sys/mman.h> | 
 | 27 |  | 
| Mathias Agopian | 0678a8c | 2013-03-19 20:56:00 -0700 | [diff] [blame] | 28 | #include <binder/ProcessState.h> | 
 | 29 |  | 
| Mike Lockwood | c59b2f9 | 2012-10-24 12:31:10 -0700 | [diff] [blame] | 30 | #include <gui/SurfaceComposerClient.h> | 
 | 31 | #include <gui/ISurfaceComposer.h> | 
 | 32 |  | 
| Dan Stoza | cf70d71 | 2015-06-09 16:47:33 -0700 | [diff] [blame] | 33 | #include <ui/DisplayInfo.h> | 
| Mathias Agopian | 0137fb8 | 2013-03-20 15:38:07 -0700 | [diff] [blame] | 34 | #include <ui/PixelFormat.h> | 
 | 35 |  | 
| Andreas Gampe | cfedceb | 2014-09-30 21:48:18 -0700 | [diff] [blame] | 36 | // TODO: Fix Skia. | 
 | 37 | #pragma GCC diagnostic push | 
 | 38 | #pragma GCC diagnostic ignored "-Wunused-parameter" | 
| Mike Lockwood | c59b2f9 | 2012-10-24 12:31:10 -0700 | [diff] [blame] | 39 | #include <SkImageEncoder.h> | 
| Mike Lockwood | c59b2f9 | 2012-10-24 12:31:10 -0700 | [diff] [blame] | 40 | #include <SkData.h> | 
| Andreas Gampe | cfedceb | 2014-09-30 21:48:18 -0700 | [diff] [blame] | 41 | #pragma GCC diagnostic pop | 
| Mike Lockwood | c59b2f9 | 2012-10-24 12:31:10 -0700 | [diff] [blame] | 42 |  | 
 | 43 | using namespace android; | 
 | 44 |  | 
 | 45 | static uint32_t DEFAULT_DISPLAY_ID = ISurfaceComposer::eDisplayIdMain; | 
 | 46 |  | 
 | 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 |  | 
| Mike Reed | b933055 | 2014-06-16 17:31:48 -0400 | [diff] [blame] | 60 | static SkColorType flinger2skia(PixelFormat f) | 
| Mike Lockwood | c59b2f9 | 2012-10-24 12:31:10 -0700 | [diff] [blame] | 61 | { | 
 | 62 |     switch (f) { | 
| Mike Lockwood | c59b2f9 | 2012-10-24 12:31:10 -0700 | [diff] [blame] | 63 |         case PIXEL_FORMAT_RGB_565: | 
| Mike Reed | b933055 | 2014-06-16 17:31:48 -0400 | [diff] [blame] | 64 |             return kRGB_565_SkColorType; | 
| Mike Lockwood | c59b2f9 | 2012-10-24 12:31:10 -0700 | [diff] [blame] | 65 |         default: | 
| Mike Reed | b933055 | 2014-06-16 17:31:48 -0400 | [diff] [blame] | 66 |             return kN32_SkColorType; | 
| Mike Lockwood | c59b2f9 | 2012-10-24 12:31:10 -0700 | [diff] [blame] | 67 |     } | 
 | 68 | } | 
 | 69 |  | 
| Umair Khan | cfed232 | 2014-01-15 08:08:50 -0500 | [diff] [blame] | 70 | static status_t notifyMediaScanner(const char* fileName) { | 
 | 71 |     String8 cmd("am broadcast -a android.intent.action.MEDIA_SCANNER_SCAN_FILE -d file://"); | 
 | 72 |     String8 fileUrl("\""); | 
 | 73 |     fileUrl.append(fileName); | 
 | 74 |     fileUrl.append("\""); | 
 | 75 |     cmd.append(fileName); | 
 | 76 |     cmd.append(" > /dev/null"); | 
 | 77 |     int result = system(cmd.string()); | 
 | 78 |     if (result < 0) { | 
 | 79 |         fprintf(stderr, "Unable to broadcast intent for media scanner.\n"); | 
 | 80 |         return UNKNOWN_ERROR; | 
 | 81 |     } | 
 | 82 |     return NO_ERROR; | 
 | 83 | } | 
 | 84 |  | 
| Mike Lockwood | c59b2f9 | 2012-10-24 12:31:10 -0700 | [diff] [blame] | 85 | int main(int argc, char** argv) | 
 | 86 | { | 
| Mathias Agopian | 0678a8c | 2013-03-19 20:56:00 -0700 | [diff] [blame] | 87 |     ProcessState::self()->startThreadPool(); | 
 | 88 |  | 
| Mike Lockwood | c59b2f9 | 2012-10-24 12:31:10 -0700 | [diff] [blame] | 89 |     const char* pname = argv[0]; | 
 | 90 |     bool png = false; | 
 | 91 |     int32_t displayId = DEFAULT_DISPLAY_ID; | 
 | 92 |     int c; | 
 | 93 |     while ((c = getopt(argc, argv, "phd:")) != -1) { | 
 | 94 |         switch (c) { | 
 | 95 |             case 'p': | 
 | 96 |                 png = true; | 
 | 97 |                 break; | 
 | 98 |             case 'd': | 
 | 99 |                 displayId = atoi(optarg); | 
 | 100 |                 break; | 
 | 101 |             case '?': | 
 | 102 |             case 'h': | 
 | 103 |                 usage(pname); | 
 | 104 |                 return 1; | 
 | 105 |         } | 
 | 106 |     } | 
 | 107 |     argc -= optind; | 
 | 108 |     argv += optind; | 
 | 109 |  | 
 | 110 |     int fd = -1; | 
| Andreas Gampe | cfedceb | 2014-09-30 21:48:18 -0700 | [diff] [blame] | 111 |     const char* fn = NULL; | 
| Mike Lockwood | c59b2f9 | 2012-10-24 12:31:10 -0700 | [diff] [blame] | 112 |     if (argc == 0) { | 
 | 113 |         fd = dup(STDOUT_FILENO); | 
 | 114 |     } else if (argc == 1) { | 
| Umair Khan | cfed232 | 2014-01-15 08:08:50 -0500 | [diff] [blame] | 115 |         fn = argv[0]; | 
| Mike Lockwood | c59b2f9 | 2012-10-24 12:31:10 -0700 | [diff] [blame] | 116 |         fd = open(fn, O_WRONLY | O_CREAT | O_TRUNC, 0664); | 
 | 117 |         if (fd == -1) { | 
 | 118 |             fprintf(stderr, "Error opening file: %s (%s)\n", fn, strerror(errno)); | 
 | 119 |             return 1; | 
 | 120 |         } | 
 | 121 |         const int len = strlen(fn); | 
 | 122 |         if (len >= 4 && 0 == strcmp(fn+len-4, ".png")) { | 
 | 123 |             png = true; | 
 | 124 |         } | 
 | 125 |     } | 
| Prathmesh Prabhu | bf89ae5 | 2016-03-10 15:23:34 -0800 | [diff] [blame] | 126 |  | 
| Mike Lockwood | c59b2f9 | 2012-10-24 12:31:10 -0700 | [diff] [blame] | 127 |     if (fd == -1) { | 
 | 128 |         usage(pname); | 
 | 129 |         return 1; | 
 | 130 |     } | 
 | 131 |  | 
 | 132 |     void const* mapbase = MAP_FAILED; | 
 | 133 |     ssize_t mapsize = -1; | 
 | 134 |  | 
| Andreas Gampe | cfedceb | 2014-09-30 21:48:18 -0700 | [diff] [blame] | 135 |     void const* base = NULL; | 
| Mathias Agopian | 0137fb8 | 2013-03-20 15:38:07 -0700 | [diff] [blame] | 136 |     uint32_t w, s, h, f; | 
| Mike Lockwood | c59b2f9 | 2012-10-24 12:31:10 -0700 | [diff] [blame] | 137 |     size_t size = 0; | 
 | 138 |  | 
| Dan Stoza | cf70d71 | 2015-06-09 16:47:33 -0700 | [diff] [blame] | 139 |     // Maps orientations from DisplayInfo to ISurfaceComposer | 
 | 140 |     static const uint32_t ORIENTATION_MAP[] = { | 
 | 141 |         ISurfaceComposer::eRotateNone, // 0 == DISPLAY_ORIENTATION_0 | 
 | 142 |         ISurfaceComposer::eRotate270, // 1 == DISPLAY_ORIENTATION_90 | 
 | 143 |         ISurfaceComposer::eRotate180, // 2 == DISPLAY_ORIENTATION_180 | 
 | 144 |         ISurfaceComposer::eRotate90, // 3 == DISPLAY_ORIENTATION_270 | 
 | 145 |     }; | 
 | 146 |  | 
| Mike Lockwood | c59b2f9 | 2012-10-24 12:31:10 -0700 | [diff] [blame] | 147 |     ScreenshotClient screenshot; | 
 | 148 |     sp<IBinder> display = SurfaceComposerClient::getBuiltInDisplay(displayId); | 
| Dan Stoza | cf70d71 | 2015-06-09 16:47:33 -0700 | [diff] [blame] | 149 |     if (display == NULL) { | 
 | 150 |         fprintf(stderr, "Unable to get handle for display %d\n", displayId); | 
 | 151 |         return 1; | 
 | 152 |     } | 
 | 153 |  | 
 | 154 |     Vector<DisplayInfo> configs; | 
 | 155 |     SurfaceComposerClient::getDisplayConfigs(display, &configs); | 
 | 156 |     int activeConfig = SurfaceComposerClient::getActiveConfig(display); | 
 | 157 |     if (static_cast<size_t>(activeConfig) >= configs.size()) { | 
 | 158 |         fprintf(stderr, "Active config %d not inside configs (size %zu)\n", | 
 | 159 |                 activeConfig, configs.size()); | 
 | 160 |         return 1; | 
 | 161 |     } | 
 | 162 |     uint8_t displayOrientation = configs[activeConfig].orientation; | 
 | 163 |     uint32_t captureOrientation = ORIENTATION_MAP[displayOrientation]; | 
 | 164 |  | 
 | 165 |     status_t result = screenshot.update(display, Rect(), 0, 0, 0, -1U, | 
 | 166 |             false, captureOrientation); | 
 | 167 |     if (result == NO_ERROR) { | 
| Mike Lockwood | c59b2f9 | 2012-10-24 12:31:10 -0700 | [diff] [blame] | 168 |         base = screenshot.getPixels(); | 
 | 169 |         w = screenshot.getWidth(); | 
 | 170 |         h = screenshot.getHeight(); | 
| Mathias Agopian | 0137fb8 | 2013-03-20 15:38:07 -0700 | [diff] [blame] | 171 |         s = screenshot.getStride(); | 
| Mike Lockwood | c59b2f9 | 2012-10-24 12:31:10 -0700 | [diff] [blame] | 172 |         f = screenshot.getFormat(); | 
 | 173 |         size = screenshot.getSize(); | 
| Mike Lockwood | c59b2f9 | 2012-10-24 12:31:10 -0700 | [diff] [blame] | 174 |     } | 
 | 175 |  | 
| Andreas Gampe | cfedceb | 2014-09-30 21:48:18 -0700 | [diff] [blame] | 176 |     if (base != NULL) { | 
| Mike Lockwood | c59b2f9 | 2012-10-24 12:31:10 -0700 | [diff] [blame] | 177 |         if (png) { | 
| Mike Reed | b933055 | 2014-06-16 17:31:48 -0400 | [diff] [blame] | 178 |             const SkImageInfo info = SkImageInfo::Make(w, h, flinger2skia(f), | 
 | 179 |                                                        kPremul_SkAlphaType); | 
| Leon Scroggins III | 3449789 | 2015-01-20 15:52:43 -0500 | [diff] [blame] | 180 |             SkAutoTUnref<SkData> data(SkImageEncoder::EncodeData(info, base, s*bytesPerPixel(f), | 
 | 181 |                     SkImageEncoder::kPNG_Type, SkImageEncoder::kDefaultQuality)); | 
 | 182 |             if (data.get()) { | 
 | 183 |                 write(fd, data->data(), data->size()); | 
 | 184 |             } | 
| Andreas Gampe | cfedceb | 2014-09-30 21:48:18 -0700 | [diff] [blame] | 185 |             if (fn != NULL) { | 
 | 186 |                 notifyMediaScanner(fn); | 
 | 187 |             } | 
| Mike Lockwood | c59b2f9 | 2012-10-24 12:31:10 -0700 | [diff] [blame] | 188 |         } else { | 
 | 189 |             write(fd, &w, 4); | 
 | 190 |             write(fd, &h, 4); | 
 | 191 |             write(fd, &f, 4); | 
| Mathias Agopian | 0137fb8 | 2013-03-20 15:38:07 -0700 | [diff] [blame] | 192 |             size_t Bpp = bytesPerPixel(f); | 
 | 193 |             for (size_t y=0 ; y<h ; y++) { | 
 | 194 |                 write(fd, base, w*Bpp); | 
 | 195 |                 base = (void *)((char *)base + s*Bpp); | 
 | 196 |             } | 
| Mike Lockwood | c59b2f9 | 2012-10-24 12:31:10 -0700 | [diff] [blame] | 197 |         } | 
 | 198 |     } | 
 | 199 |     close(fd); | 
 | 200 |     if (mapbase != MAP_FAILED) { | 
 | 201 |         munmap((void *)mapbase, mapsize); | 
 | 202 |     } | 
 | 203 |     return 0; | 
 | 204 | } |