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
Screenshot()38 Screenshot::Screenshot()
39 :
40 BApplication("application/x-vnd.haiku-screenshot-cli"),
41 fUtility(new Utility()),
42 fLaunchGui(true)
43 {
44 }
45
46
~Screenshot()47 Screenshot::~Screenshot()
48 {
49 delete fUtility;
50 }
51
52
53 void
ArgvReceived(int32 argc,char ** argv)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
ReadyToRun()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
_ShowHelp()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
_New(bigtime_t delay)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 fUtility->cursorAreaBitmap = new BBitmap(bounds, B_RGBA32);
228
229 fUtility->cursorAreaBitmap->ImportBits(fUtility->wholeScreen,
230 fUtility->cursorPosition, BPoint(0, 0), bounds.Size());
231
232 // Fill in the background of the mouse bitmap
233 uint8* bits = (uint8*)fUtility->cursorBitmap->Bits();
234 uint8* areaBits = (uint8*)fUtility->cursorAreaBitmap->Bits();
235 int cursorWidth = bounds.IntegerWidth() + 1;
236 int cursorHeight = bounds.IntegerHeight() + 1;
237 for (int32 i = 0; i < cursorHeight; i++) {
238 for (int32 j = 0; j < cursorWidth; j++) {
239 uint8 alpha = 255 - bits[3];
240 bits[0] = ((areaBits[0] * alpha) >> 8) + bits[0];
241 bits[1] = ((areaBits[1] * alpha) >> 8) + bits[1];
242 bits[2] = ((areaBits[2] * alpha) >> 8) + bits[2];
243 bits[3] = 255;
244 areaBits += 4;
245 bits += 4;
246 }
247 }
248 }
249
250
251 status_t
_GetActiveWindowFrame()252 Screenshot::_GetActiveWindowFrame()
253 {
254 fUtility->activeWindowFrame.Set(0, 0, -1, -1);
255
256 // Create a messenger to communicate with the active application
257 app_info appInfo;
258 status_t status = be_roster->GetActiveAppInfo(&appInfo);
259 if (status != B_OK)
260 return status;
261 BMessenger messenger(appInfo.signature, appInfo.team);
262 if (!messenger.IsValid())
263 return B_ERROR;
264
265 // Loop through the windows of the active application to find out which
266 // window is active
267 int32 tokenCount;
268 int32* tokens = get_token_list(appInfo.team, &tokenCount);
269 bool foundActiveWindow = false;
270 BMessage message;
271 BMessage reply;
272 int32 index = 0;
273 int32 token = -1;
274 client_window_info* windowInfo = NULL;
275 bool modalWindow = false;
276 while (true) {
277 message.MakeEmpty();
278 reply.MakeEmpty();
279
280 message.what = B_GET_PROPERTY;
281 message.AddSpecifier("Active");
282 message.AddSpecifier("Window", index);
283 messenger.SendMessage(&message, &reply, B_INFINITE_TIMEOUT, 50000);
284
285 if (reply.what == B_MESSAGE_NOT_UNDERSTOOD)
286 break;
287
288 if (!reply.what) {
289 // Reply timout, this probably means that we have a modal window
290 // so we'll just get the window frame of the top most window
291 modalWindow = true;
292 free(tokens);
293 status_t status = BPrivate::get_window_order(current_workspace(),
294 &tokens, &tokenCount);
295 if (status != B_OK || !tokens || tokenCount < 1)
296 return B_ERROR;
297 foundActiveWindow = true;
298 } else if (reply.FindBool("result", &foundActiveWindow) != B_OK)
299 foundActiveWindow = false;
300
301 if (foundActiveWindow) {
302 // Get the client_window_info of the active window
303 foundActiveWindow = false;
304 for (int i = 0; i < tokenCount; i++) {
305 token = tokens[i];
306 windowInfo = get_window_info(token);
307 if (windowInfo != NULL
308 && windowInfo->feel != kMenuWindowFeel
309 && !windowInfo->is_mini
310 && !(windowInfo->show_hide_level > 0)) {
311 foundActiveWindow = true;
312 break;
313 }
314 free(windowInfo);
315 }
316 if (foundActiveWindow)
317 break;
318 }
319 index++;
320 }
321 free(tokens);
322
323 if (!foundActiveWindow)
324 return B_ERROR;
325
326 // Get the TabFrame using the scripting interface
327 if (!modalWindow) {
328 message.MakeEmpty();
329 message.what = B_GET_PROPERTY;
330 message.AddSpecifier("TabFrame");
331 message.AddSpecifier("Window", index);
332 reply.MakeEmpty();
333 messenger.SendMessage(&message, &reply);
334
335 if (reply.FindRect("result", &fUtility->tabFrame) != B_OK)
336 return B_ERROR;
337 } else
338 fUtility->tabFrame.Set(0, 0, 0, 0);
339
340 // Get the active window frame from the client_window_info
341 fUtility->activeWindowFrame.left = windowInfo->window_left;
342 fUtility->activeWindowFrame.top = windowInfo->window_top;
343 fUtility->activeWindowFrame.right = windowInfo->window_right;
344 fUtility->activeWindowFrame.bottom = windowInfo->window_bottom;
345 fUtility->borderSize = windowInfo->border_size;
346
347 free(windowInfo);
348
349 // Make sure that fActiveWindowFrame doesn't extend beyond the screen frame
350 BRect screenFrame(BScreen().Frame());
351 if (fUtility->activeWindowFrame.left < screenFrame.left)
352 fUtility->activeWindowFrame.left = screenFrame.left;
353 if (fUtility->activeWindowFrame.top < screenFrame.top)
354 fUtility->activeWindowFrame.top = screenFrame.top;
355 if (fUtility->activeWindowFrame.right > screenFrame.right)
356 fUtility->activeWindowFrame.right = screenFrame.right;
357 if (fUtility->activeWindowFrame.bottom > screenFrame.bottom)
358 fUtility->activeWindowFrame.bottom = screenFrame.bottom;
359
360 return B_OK;
361 }
362
363
364 int32
_ImageType(const char * name) const365 Screenshot::_ImageType(const char* name) const
366 {
367 if (strcasecmp(name, "bmp") == 0)
368 return B_BMP_FORMAT;
369 if (strcasecmp(name, "gif") == 0)
370 return B_GIF_FORMAT;
371 if (strcasecmp(name, "jpg") == 0 || strcmp(name, "jpeg") == 0)
372 return B_JPEG_FORMAT;
373 if (strcasecmp(name, "ppm") == 0)
374 return B_PPM_FORMAT;
375 if (strcasecmp(name, "tga") == 0 || strcmp(name, "targa") == 0)
376 return B_TGA_FORMAT;
377 if (strcasecmp(name, "tif") == 0 || strcmp(name, "tiff") == 0)
378 return B_TIFF_FORMAT;
379
380 return B_PNG_FORMAT;
381 }
382
383
384 // #pragma mark -
385
386
387 int
main()388 main()
389 {
390 Screenshot screenshot;
391 return screenshot.Run();
392 }
393