1 /* 2 * Copyright 2010 Wim van der Meer <WPJvanderMeer@gmail.com> 3 * Copyright Karsten Heimrich, host.haiku@gmx.de. All rights reserved. 4 * Distributed under the terms of the MIT License. 5 * 6 * Authors: 7 * Karsten Heimrich 8 * Fredrik Modéen 9 * Christophe Huriaux 10 * Wim van der Meer 11 */ 12 13 14 #include "Screenshot.h" 15 16 #include <stdio.h> 17 #include <stdlib.h> 18 #include <strings.h> 19 20 #include <AppDefs.h> 21 #include <Bitmap.h> 22 #include <Catalog.h> 23 #include <Locale.h> 24 #include <Roster.h> 25 #include <Screen.h> 26 #include <TranslatorFormats.h> 27 28 #include <WindowInfo.h> 29 30 #include "Utility.h" 31 32 33 #undef B_TRANSLATION_CONTEXT 34 #define B_TRANSLATION_CONTEXT "Screenshot" 35 36 37 Screenshot::Screenshot() 38 : 39 BApplication("application/x-vnd.haiku-screenshot-cli"), 40 fUtility(new Utility()), 41 fLaunchGui(true) 42 { 43 } 44 45 46 Screenshot::~Screenshot() 47 { 48 delete fUtility; 49 } 50 51 52 void 53 Screenshot::ArgvReceived(int32 argc, char** argv) 54 { 55 bigtime_t delay = 0; 56 const char* outputFilename = NULL; 57 bool includeBorder = false; 58 bool includeCursor = false; 59 bool grabActiveWindow = false; 60 bool saveScreenshotSilent = false; 61 bool copyToClipboard = false; 62 uint32 imageFileType = B_PNG_FORMAT; 63 for (int32 i = 0; i < argc; i++) { 64 if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0) 65 _ShowHelp(); 66 else if (strcmp(argv[i], "-b") == 0 67 || strcmp(argv[i], "--border") == 0) 68 includeBorder = true; 69 else if (strcmp(argv[i], "-m") == 0 70 || strcmp(argv[i], "--mouse-pointer") == 0) 71 includeCursor = true; 72 else if (strcmp(argv[i], "-w") == 0 73 || strcmp(argv[i], "--window") == 0) 74 grabActiveWindow = true; 75 else if (strcmp(argv[i], "-s") == 0 76 || strcmp(argv[i], "--silent") == 0) 77 saveScreenshotSilent = true; 78 else if (strcmp(argv[i], "-f") == 0 79 || strncmp(argv[i], "--format", 6) == 0 80 || strncmp(argv[i], "--format=", 7) == 0) 81 imageFileType = _ImageType(argv[i + 1]); 82 else if (strcmp(argv[i], "-d") == 0 83 || strncmp(argv[i], "--delay", 7) == 0 84 || strncmp(argv[i], "--delay=", 8) == 0) { 85 int32 seconds = -1; 86 if (argc > i + 1) 87 seconds = atoi(argv[i + 1]); 88 if (seconds >= 0) { 89 delay = seconds * 1000000; 90 i++; 91 } else { 92 printf("Screenshot: option requires an argument -- %s\n", 93 argv[i]); 94 fLaunchGui = false; 95 return; 96 } 97 } else if (strcmp(argv[i], "-c") == 0 98 || strcmp(argv[i], "--clipboard") == 0) 99 copyToClipboard = true; 100 else if (i == argc - 1) 101 outputFilename = argv[i]; 102 } 103 104 _New(delay); 105 106 if (copyToClipboard || saveScreenshotSilent) { 107 fLaunchGui = false; 108 109 BBitmap* screenshot = fUtility->MakeScreenshot(includeCursor, 110 grabActiveWindow, includeBorder); 111 112 if (screenshot == NULL) 113 return; 114 115 if (copyToClipboard) 116 fUtility->CopyToClipboard(*screenshot); 117 118 if (saveScreenshotSilent) 119 fUtility->Save(screenshot, outputFilename, imageFileType); 120 121 delete screenshot; 122 } 123 } 124 125 126 void 127 Screenshot::ReadyToRun() 128 { 129 if (fLaunchGui) { 130 // Get a screenshot if we don't have one 131 if (fUtility->wholeScreen == NULL) 132 _New(0); 133 134 // Send the screenshot data to the GUI 135 BMessage message; 136 message.what = SS_UTILITY_DATA; 137 138 BMessage* bitmap = new BMessage(); 139 fUtility->wholeScreen->Archive(bitmap); 140 message.AddMessage("wholeScreen", bitmap); 141 142 bitmap = new BMessage(); 143 fUtility->cursorBitmap->Archive(bitmap); 144 message.AddMessage("cursorBitmap", bitmap); 145 146 bitmap = new BMessage(); 147 fUtility->cursorAreaBitmap->Archive(bitmap); 148 message.AddMessage("cursorAreaBitmap", bitmap); 149 150 message.AddPoint("cursorPosition", fUtility->cursorPosition); 151 message.AddRect("activeWindowFrame", fUtility->activeWindowFrame); 152 message.AddRect("tabFrame", fUtility->tabFrame); 153 message.AddFloat("borderSize", fUtility->borderSize); 154 155 be_roster->Launch("application/x-vnd.haiku-screenshot", &message); 156 } 157 158 be_app->PostMessage(B_QUIT_REQUESTED); 159 } 160 161 162 void 163 Screenshot::_ShowHelp() 164 { 165 printf("Screenshot [OPTIONS] [FILE] Creates a bitmap of the current " 166 "screen\n\n"); 167 printf("FILE is the optional output path / filename used in silent mode. " 168 "An exisiting\nfile with the same name will be overwritten without " 169 "warning. If FILE is not\ngiven the screenshot will be saved to a " 170 "file with the default filename in the\nuser's home directory.\n\n"); 171 printf("OPTIONS\n"); 172 printf(" -m, --mouse-pointer Include the mouse pointer\n"); 173 printf(" -b, --border Include the window border\n"); 174 printf(" -w, --window Capture the active window instead of the " 175 "entire screen\n"); 176 printf(" -d, --delay=seconds Take screenshot after the specified delay " 177 "[in seconds]\n"); 178 printf(" -s, --silent Saves the screenshot without showing the " 179 "application\n window\n"); 180 printf(" -f, --format=image Give the image format you like to save " 181 "as\n"); 182 printf(" [bmp], [gif], [jpg], [png], [ppm], " 183 "[tga], [tif]\n"); 184 printf(" -c, --clipboard Copies the screenshot to the system " 185 "clipboard without\n showing the application " 186 "window\n"); 187 printf("\n"); 188 printf("Note: OPTION -b, --border takes only effect when used with -w, " 189 "--window\n"); 190 191 fLaunchGui = false; 192 } 193 194 195 void 196 Screenshot::_New(bigtime_t delay) 197 { 198 delete fUtility->wholeScreen; 199 delete fUtility->cursorBitmap; 200 delete fUtility->cursorAreaBitmap; 201 202 if (delay > 0) 203 snooze(delay); 204 205 _GetActiveWindowFrame(); 206 207 // There is a bug in the drawEngine code that prevents the drawCursor 208 // flag from hiding the cursor when GetBitmap is called, so we need to hide 209 // the cursor by ourselves. Refer to trac tickets #2988 and #2997 210 bool cursorIsHidden = IsCursorHidden(); 211 if (!cursorIsHidden) 212 HideCursor(); 213 if (BScreen().GetBitmap(&fUtility->wholeScreen, false) != B_OK) 214 return; 215 if (!cursorIsHidden) 216 ShowCursor(); 217 218 // Get the current cursor position, bitmap, and hotspot 219 BPoint cursorHotSpot; 220 get_mouse(&fUtility->cursorPosition, NULL); 221 get_mouse_bitmap(&fUtility->cursorBitmap, &cursorHotSpot); 222 fUtility->cursorPosition -= cursorHotSpot; 223 224 // Put the mouse area in a bitmap 225 BRect bounds = fUtility->cursorBitmap->Bounds(); 226 int cursorWidth = bounds.IntegerWidth() + 1; 227 int cursorHeight = bounds.IntegerHeight() + 1; 228 fUtility->cursorAreaBitmap = new BBitmap(bounds, B_RGBA32); 229 230 fUtility->cursorAreaBitmap->ImportBits(fUtility->wholeScreen->Bits(), 231 fUtility->wholeScreen->BitsLength(), 232 fUtility->wholeScreen->BytesPerRow(), 233 fUtility->wholeScreen->ColorSpace(), 234 fUtility->cursorPosition, BPoint(0, 0), 235 cursorWidth, cursorHeight); 236 237 // Fill in the background of the mouse bitmap 238 uint8* bits = (uint8*)fUtility->cursorBitmap->Bits(); 239 uint8* areaBits = (uint8*)fUtility->cursorAreaBitmap->Bits(); 240 for (int32 i = 0; i < cursorHeight; i++) { 241 for (int32 j = 0; j < cursorWidth; j++) { 242 uint8 alpha = 255 - bits[3]; 243 bits[0] = ((areaBits[0] * alpha) >> 8) + bits[0]; 244 bits[1] = ((areaBits[1] * alpha) >> 8) + bits[1]; 245 bits[2] = ((areaBits[2] * alpha) >> 8) + bits[2]; 246 bits[3] = 255; 247 areaBits += 4; 248 bits += 4; 249 } 250 } 251 } 252 253 254 status_t 255 Screenshot::_GetActiveWindowFrame() 256 { 257 fUtility->activeWindowFrame.Set(0, 0, -1, -1); 258 259 // Create a messenger to communicate with the active application 260 app_info appInfo; 261 status_t status = be_roster->GetActiveAppInfo(&appInfo); 262 if (status != B_OK) 263 return status; 264 BMessenger messenger(appInfo.signature, appInfo.team); 265 if (!messenger.IsValid()) 266 return B_ERROR; 267 268 // Loop through the windows of the active application to find out which 269 // window is active 270 int32 tokenCount; 271 int32* tokens = get_token_list(appInfo.team, &tokenCount); 272 bool foundActiveWindow = false; 273 BMessage message; 274 BMessage reply; 275 int32 index = 0; 276 int32 token = -1; 277 client_window_info* windowInfo = NULL; 278 bool modalWindow = false; 279 while (true) { 280 message.MakeEmpty(); 281 reply.MakeEmpty(); 282 283 message.what = B_GET_PROPERTY; 284 message.AddSpecifier("Active"); 285 message.AddSpecifier("Window", index); 286 messenger.SendMessage(&message, &reply, B_INFINITE_TIMEOUT, 50000); 287 288 if (reply.what == B_MESSAGE_NOT_UNDERSTOOD) 289 break; 290 291 if (!reply.what) { 292 // Reply timout, this probably means that we have a modal window 293 // so we'll just get the window frame of the top most window 294 modalWindow = true; 295 free(tokens); 296 status_t status = BPrivate::get_window_order(current_workspace(), 297 &tokens, &tokenCount); 298 if (status != B_OK || !tokens || tokenCount < 1) 299 return B_ERROR; 300 foundActiveWindow = true; 301 } else if (reply.FindBool("result", &foundActiveWindow) != B_OK) 302 foundActiveWindow = false; 303 304 if (foundActiveWindow) { 305 // Get the client_window_info of the active window 306 foundActiveWindow = false; 307 for (int i = 0; i < tokenCount; i++) { 308 token = tokens[i]; 309 windowInfo = get_window_info(token); 310 if (windowInfo && !windowInfo->is_mini 311 && !(windowInfo->show_hide_level > 0)) { 312 foundActiveWindow = true; 313 break; 314 } 315 free(windowInfo); 316 } 317 if (foundActiveWindow) 318 break; 319 } 320 index++; 321 } 322 free(tokens); 323 324 if (!foundActiveWindow) 325 return B_ERROR; 326 327 // Get the TabFrame using the scripting interface 328 if (!modalWindow) { 329 message.MakeEmpty(); 330 message.what = B_GET_PROPERTY; 331 message.AddSpecifier("TabFrame"); 332 message.AddSpecifier("Window", index); 333 reply.MakeEmpty(); 334 messenger.SendMessage(&message, &reply); 335 336 if (reply.FindRect("result", &fUtility->tabFrame) != B_OK) 337 return B_ERROR; 338 } else 339 fUtility->tabFrame.Set(0, 0, 0, 0); 340 341 // Get the active window frame from the client_window_info 342 fUtility->activeWindowFrame.left = windowInfo->window_left; 343 fUtility->activeWindowFrame.top = windowInfo->window_top; 344 fUtility->activeWindowFrame.right = windowInfo->window_right; 345 fUtility->activeWindowFrame.bottom = windowInfo->window_bottom; 346 fUtility->borderSize = windowInfo->border_size; 347 348 free(windowInfo); 349 350 // Make sure that fActiveWindowFrame doesn't extend beyond the screen frame 351 BRect screenFrame(BScreen().Frame()); 352 if (fUtility->activeWindowFrame.left < screenFrame.left) 353 fUtility->activeWindowFrame.left = screenFrame.left; 354 if (fUtility->activeWindowFrame.top < screenFrame.top) 355 fUtility->activeWindowFrame.top = screenFrame.top; 356 if (fUtility->activeWindowFrame.right > screenFrame.right) 357 fUtility->activeWindowFrame.right = screenFrame.right; 358 if (fUtility->activeWindowFrame.bottom > screenFrame.bottom) 359 fUtility->activeWindowFrame.bottom = screenFrame.bottom; 360 361 return B_OK; 362 } 363 364 365 int32 366 Screenshot::_ImageType(const char* name) const 367 { 368 if (strcasecmp(name, "bmp") == 0) 369 return B_BMP_FORMAT; 370 if (strcasecmp(name, "gif") == 0) 371 return B_GIF_FORMAT; 372 if (strcasecmp(name, "jpg") == 0 || strcmp(name, "jpeg") == 0) 373 return B_JPEG_FORMAT; 374 if (strcasecmp(name, "ppm") == 0) 375 return B_PPM_FORMAT; 376 if (strcasecmp(name, "tga") == 0 || strcmp(name, "targa") == 0) 377 return B_TGA_FORMAT; 378 if (strcasecmp(name, "tif") == 0 || strcmp(name, "tiff") == 0) 379 return B_TIFF_FORMAT; 380 381 return B_PNG_FORMAT; 382 } 383 384 385 // #pragma mark - 386 387 388 int 389 main() 390 { 391 Screenshot screenshot; 392 return screenshot.Run(); 393 } 394