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