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.
14 #include "Screenshot.h"
26 #include <TranslatorFormats.h>
28 #include <WindowInfo.h>
29 #include <WindowPrivate.h>
34 #undef B_TRANSLATION_CONTEXT
35 #define B_TRANSLATION_CONTEXT "Screenshot"
38 Screenshot::Screenshot()
40 BApplication("application/x-vnd.haiku-screenshot-cli"),
41 fUtility(new Utility()),
47 Screenshot::~Screenshot()
54 Screenshot::ArgvReceived(int32 argc
, char** argv
)
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)
67 else if (strcmp(argv
[i
], "-b") == 0
68 || strcmp(argv
[i
], "--border") == 0)
70 else if (strcmp(argv
[i
], "-m") == 0
71 || strcmp(argv
[i
], "--mouse-pointer") == 0)
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) {
88 seconds
= atoi(argv
[i
+ 1]);
90 delay
= seconds
* 1000000;
93 printf("Screenshot: option requires an argument -- %s\n",
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
];
107 if (copyToClipboard
|| saveScreenshotSilent
) {
110 BBitmap
* screenshot
= fUtility
->MakeScreenshot(includeCursor
,
111 grabActiveWindow
, includeBorder
);
113 if (screenshot
== NULL
)
117 fUtility
->CopyToClipboard(*screenshot
);
119 if (saveScreenshotSilent
)
120 fUtility
->Save(screenshot
, outputFilename
, imageFileType
);
128 Screenshot::ReadyToRun()
131 // Get a screenshot if we don't have one
132 if (fUtility
->wholeScreen
== NULL
)
135 // Send the screenshot data to the GUI
137 message
.what
= SS_UTILITY_DATA
;
139 BMessage
* bitmap
= new BMessage();
140 fUtility
->wholeScreen
->Archive(bitmap
);
141 message
.AddMessage("wholeScreen", bitmap
);
143 bitmap
= new BMessage();
144 fUtility
->cursorBitmap
->Archive(bitmap
);
145 message
.AddMessage("cursorBitmap", bitmap
);
147 bitmap
= new BMessage();
148 fUtility
->cursorAreaBitmap
->Archive(bitmap
);
149 message
.AddMessage("cursorAreaBitmap", bitmap
);
151 message
.AddPoint("cursorPosition", fUtility
->cursorPosition
);
152 message
.AddRect("activeWindowFrame", fUtility
->activeWindowFrame
);
153 message
.AddRect("tabFrame", fUtility
->tabFrame
);
154 message
.AddFloat("borderSize", fUtility
->borderSize
);
156 be_roster
->Launch("application/x-vnd.haiku-screenshot", &message
);
159 be_app
->PostMessage(B_QUIT_REQUESTED
);
164 Screenshot::_ShowHelp()
166 printf("Screenshot [OPTIONS] [FILE] Creates a bitmap of the current "
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");
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 "
177 printf(" -d, --delay=seconds Take screenshot after the specified delay "
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 "
183 printf(" [bmp], [gif], [jpg], [png], [ppm], "
185 printf(" -c, --clipboard Copies the screenshot to the system "
186 "clipboard without\n showing the application "
189 printf("Note: OPTION -b, --border takes only effect when used with -w, "
197 Screenshot::_New(bigtime_t delay
)
199 delete fUtility
->wholeScreen
;
200 delete fUtility
->cursorBitmap
;
201 delete fUtility
->cursorAreaBitmap
;
206 _GetActiveWindowFrame();
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();
214 if (BScreen().GetBitmap(&fUtility
->wholeScreen
, false) != B_OK
)
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
;
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
);
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
);
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];
256 Screenshot::_GetActiveWindowFrame()
258 fUtility
->activeWindowFrame
.Set(0, 0, -1, -1);
260 // Create a messenger to communicate with the active application
262 status_t status
= be_roster
->GetActiveAppInfo(&appInfo
);
265 BMessenger
messenger(appInfo
.signature
, appInfo
.team
);
266 if (!messenger
.IsValid())
269 // Loop through the windows of the active application to find out which
272 int32
* tokens
= get_token_list(appInfo
.team
, &tokenCount
);
273 bool foundActiveWindow
= false;
278 client_window_info
* windowInfo
= NULL
;
279 bool modalWindow
= false;
284 message
.what
= B_GET_PROPERTY
;
285 message
.AddSpecifier("Active");
286 message
.AddSpecifier("Window", index
);
287 messenger
.SendMessage(&message
, &reply
, B_INFINITE_TIMEOUT
, 50000);
289 if (reply
.what
== B_MESSAGE_NOT_UNDERSTOOD
)
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
297 status_t status
= BPrivate::get_window_order(current_workspace(),
298 &tokens
, &tokenCount
);
299 if (status
!= B_OK
|| !tokens
|| tokenCount
< 1)
301 foundActiveWindow
= true;
302 } else if (reply
.FindBool("result", &foundActiveWindow
) != B_OK
)
303 foundActiveWindow
= false;
305 if (foundActiveWindow
) {
306 // Get the client_window_info of the active window
307 foundActiveWindow
= false;
308 for (int i
= 0; i
< tokenCount
; 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;
320 if (foundActiveWindow
)
327 if (!foundActiveWindow
)
330 // Get the TabFrame using the scripting interface
333 message
.what
= B_GET_PROPERTY
;
334 message
.AddSpecifier("TabFrame");
335 message
.AddSpecifier("Window", index
);
337 messenger
.SendMessage(&message
, &reply
);
339 if (reply
.FindRect("result", &fUtility
->tabFrame
) != B_OK
)
342 fUtility
->tabFrame
.Set(0, 0, 0, 0);
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
;
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
;
369 Screenshot::_ImageType(const char* name
) const
371 if (strcasecmp(name
, "bmp") == 0)
373 if (strcasecmp(name
, "gif") == 0)
375 if (strcasecmp(name
, "jpg") == 0 || strcmp(name
, "jpeg") == 0)
376 return B_JPEG_FORMAT
;
377 if (strcasecmp(name
, "ppm") == 0)
379 if (strcasecmp(name
, "tga") == 0 || strcmp(name
, "targa") == 0)
381 if (strcasecmp(name
, "tif") == 0 || strcmp(name
, "tiff") == 0)
382 return B_TIFF_FORMAT
;
394 Screenshot screenshot
;
395 return screenshot
.Run();