1 /* See COPYING.txt for the full license governing this code. */
2 /**
3 * \file windows_screenshot.c
4 *
5 * Source file for the screenshot API on windows.
6 */
7
8 #include "SDL_visualtest_process.h"
9 #include <SDL.h>
10 #include <SDL_test.h>
11
12 #if defined(__CYGWIN__)
13 #include <sys/stat.h>
14 #endif
15
16 #if defined(__WIN32__)
17 #include <Windows.h>
18
19 void LogLastError(char* str);
20
21 static int img_num;
22 static SDL_ProcessInfo screenshot_pinfo;
23
24 /* Saves a bitmap to a file using hdc as a device context */
25 static int
SaveBitmapToFile(HDC hdc,HBITMAP hbitmap,char * filename)26 SaveBitmapToFile(HDC hdc, HBITMAP hbitmap, char* filename)
27 {
28 BITMAP bitmap;
29 BITMAPFILEHEADER bfh;
30 BITMAPINFOHEADER bih;
31 DWORD bmpsize, bytes_written;
32 HANDLE hdib, hfile;
33 char* bmpdata;
34 int return_code = 1;
35
36 if(!hdc)
37 {
38 SDLTest_LogError("hdc argument is NULL");
39 return 0;
40 }
41 if(!hbitmap)
42 {
43 SDLTest_LogError("hbitmap argument is NULL");
44 return 0;
45 }
46 if(!filename)
47 {
48 SDLTest_LogError("filename argument is NULL");
49 return 0;
50 }
51
52 if(!GetObject(hbitmap, sizeof(BITMAP), (void*)&bitmap))
53 {
54 SDLTest_LogError("GetObject() failed");
55 return_code = 0;
56 goto savebitmaptofile_cleanup_generic;
57 }
58
59 bih.biSize = sizeof(BITMAPINFOHEADER);
60 bih.biWidth = bitmap.bmWidth;
61 bih.biHeight = bitmap.bmHeight;
62 bih.biPlanes = 1;
63 bih.biBitCount = 32;
64 bih.biCompression = BI_RGB;
65 bih.biSizeImage = 0;
66 bih.biXPelsPerMeter = 0;
67 bih.biYPelsPerMeter = 0;
68 bih.biClrUsed = 0;
69 bih.biClrImportant = 0;
70
71 bmpsize = ((bitmap.bmWidth * bih.biBitCount + 31) / 32) * 4 * bitmap.bmHeight;
72
73 hdib = GlobalAlloc(GHND, bmpsize);
74 if(!hdib)
75 {
76 LogLastError("GlobalAlloc() failed");
77 return_code = 0;
78 goto savebitmaptofile_cleanup_generic;
79 }
80 bmpdata = (char*)GlobalLock(hdib);
81 if(!bmpdata)
82 {
83 LogLastError("GlobalLock() failed");
84 return_code = 0;
85 goto savebitmaptofile_cleanup_hdib;
86 }
87
88 if(!GetDIBits(hdc, hbitmap, 0, (UINT)bitmap.bmHeight, bmpdata,
89 (LPBITMAPINFO)&bih, DIB_RGB_COLORS))
90 {
91 SDLTest_LogError("GetDIBits() failed");
92 return_code = 0;
93 goto savebitmaptofile_cleanup_unlockhdib;
94 }
95
96 hfile = CreateFile(filename, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
97 FILE_ATTRIBUTE_NORMAL, NULL);
98 if(hfile == INVALID_HANDLE_VALUE)
99 {
100 LogLastError("CreateFile()");
101 return_code = 0;
102 goto savebitmaptofile_cleanup_unlockhdib;
103 }
104 bfh.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
105 bfh.bfSize = bmpsize + bfh.bfOffBits;
106 bfh.bfType = 0x4D42;
107
108 bytes_written = 0;
109 if(!WriteFile(hfile, (void*)&bfh, sizeof(BITMAPFILEHEADER), &bytes_written, NULL) ||
110 !WriteFile(hfile, (void*)&bih, sizeof(BITMAPINFOHEADER), &bytes_written, NULL) ||
111 !WriteFile(hfile, (void*)bmpdata, bmpsize, &bytes_written, NULL))
112 {
113 LogLastError("WriteFile() failed");
114 return_code = 0;
115 goto savebitmaptofile_cleanup_hfile;
116 }
117
118 savebitmaptofile_cleanup_hfile:
119 CloseHandle(hfile);
120
121 /* make the screenshot file writable on cygwin, since it could be overwritten later */
122 #if defined(__CYGWIN__)
123 if(chmod(filename, 0777) == -1)
124 {
125 SDLTest_LogError("chmod() failed");
126 return_code = 0;
127 }
128 #endif
129
130 savebitmaptofile_cleanup_unlockhdib:
131 GlobalUnlock(hdib);
132
133 savebitmaptofile_cleanup_hdib:
134 GlobalFree(hdib);
135
136 savebitmaptofile_cleanup_generic:
137 return return_code;
138 }
139
140 /* Takes the screenshot of a window and saves it to a file. If only_client_area
141 is true, then only the client area of the window is considered */
142 static int
ScreenshotWindow(HWND hwnd,char * filename,SDL_bool only_client_area)143 ScreenshotWindow(HWND hwnd, char* filename, SDL_bool only_client_area)
144 {
145 int width, height;
146 RECT dimensions;
147 HDC windowdc, capturedc;
148 HBITMAP capturebitmap;
149 HGDIOBJ select_success;
150 BOOL blt_success;
151 int return_code = 1;
152
153 if(!filename)
154 {
155 SDLTest_LogError("filename argument cannot be NULL");
156 return_code = 0;
157 goto screenshotwindow_cleanup_generic;
158 }
159 if(!hwnd)
160 {
161 SDLTest_LogError("hwnd argument cannot be NULL");
162 return_code = 0;
163 goto screenshotwindow_cleanup_generic;
164 }
165
166 if(!GetWindowRect(hwnd, &dimensions))
167 {
168 LogLastError("GetWindowRect() failed");
169 return_code = 0;
170 goto screenshotwindow_cleanup_generic;
171 }
172
173 if(only_client_area)
174 {
175 RECT crect;
176 if(!GetClientRect(hwnd, &crect))
177 {
178 SDLTest_LogError("GetClientRect() failed");
179 return_code = 0;
180 goto screenshotwindow_cleanup_generic;
181 }
182
183 width = crect.right;
184 height = crect.bottom;
185 windowdc = GetDC(hwnd);
186 if(!windowdc)
187 {
188 SDLTest_LogError("GetDC() failed");
189 return_code = 0;
190 goto screenshotwindow_cleanup_generic;
191 }
192 }
193 else
194 {
195 width = dimensions.right - dimensions.left;
196 height = dimensions.bottom - dimensions.top;
197 windowdc = GetWindowDC(hwnd);
198 if(!windowdc)
199 {
200 SDLTest_LogError("GetWindowDC() failed");
201 return_code = 0;
202 goto screenshotwindow_cleanup_generic;
203 }
204 }
205
206 capturedc = CreateCompatibleDC(windowdc);
207 if(!capturedc)
208 {
209 SDLTest_LogError("CreateCompatibleDC() failed");
210 return_code = 0;
211 goto screenshotwindow_cleanup_windowdc;
212 }
213 capturebitmap = CreateCompatibleBitmap(windowdc, width, height);
214 if(!capturebitmap)
215 {
216 SDLTest_LogError("CreateCompatibleBitmap() failed");
217 return_code = 0;
218 goto screenshotwindow_cleanup_capturedc;
219 }
220 select_success = SelectObject(capturedc, capturebitmap);
221 if(!select_success || select_success == HGDI_ERROR)
222 {
223 SDLTest_LogError("SelectObject() failed");
224 return_code = 0;
225 goto screenshotwindow_cleanup_capturebitmap;
226 }
227 blt_success = BitBlt(capturedc, 0, 0, width, height, windowdc,
228 0, 0, SRCCOPY|CAPTUREBLT);
229 if(!blt_success)
230 {
231 LogLastError("BitBlt() failed");
232 return_code = 0;
233 goto screenshotwindow_cleanup_capturebitmap;
234 }
235
236 /* save bitmap as file */
237 if(!SaveBitmapToFile(windowdc, capturebitmap, filename))
238 {
239 SDLTest_LogError("SaveBitmapToFile() failed");
240 return_code = 0;
241 goto screenshotwindow_cleanup_capturebitmap;
242 }
243
244 /* free resources */
245
246 screenshotwindow_cleanup_capturebitmap:
247 if(!DeleteObject(capturebitmap))
248 {
249 SDLTest_LogError("DeleteObjectFailed");
250 return_code = 0;
251 }
252
253 screenshotwindow_cleanup_capturedc:
254 if(!DeleteDC(capturedc))
255 {
256 SDLTest_LogError("DeleteDC() failed");
257 return_code = 0;
258 }
259
260 screenshotwindow_cleanup_windowdc:
261 if(!ReleaseDC(hwnd, windowdc))
262 {
263 SDLTest_LogError("ReleaseDC() failed");
264 return_code = 0;;
265 }
266
267 screenshotwindow_cleanup_generic:
268 return return_code;
269 }
270
271 /* Takes the screenshot of the entire desktop and saves it to a file */
SDLVisualTest_ScreenshotDesktop(char * filename)272 int SDLVisualTest_ScreenshotDesktop(char* filename)
273 {
274 HWND hwnd;
275 hwnd = GetDesktopWindow();
276 return ScreenshotWindow(hwnd, filename, SDL_FALSE);
277 }
278
279 /* take screenshot of a window and save it to a file */
280 static BOOL CALLBACK
ScreenshotHwnd(HWND hwnd,LPARAM lparam)281 ScreenshotHwnd(HWND hwnd, LPARAM lparam)
282 {
283 int len;
284 DWORD pid;
285 char* prefix;
286 char* filename;
287
288 GetWindowThreadProcessId(hwnd, &pid);
289 if(pid != screenshot_pinfo.pi.dwProcessId)
290 return TRUE;
291
292 if(!IsWindowVisible(hwnd))
293 return TRUE;
294
295 prefix = (char*)lparam;
296 len = SDL_strlen(prefix) + 100;
297 filename = (char*)SDL_malloc(len * sizeof(char));
298 if(!filename)
299 {
300 SDLTest_LogError("malloc() failed");
301 return FALSE;
302 }
303
304 /* restore the window and bring it to the top */
305 ShowWindowAsync(hwnd, SW_RESTORE);
306 /* restore is not instantaneous */
307 SDL_Delay(500);
308
309 /* take a screenshot of the client area */
310 if(img_num == 1)
311 SDL_snprintf(filename, len, "%s.bmp", prefix);
312 else
313 SDL_snprintf(filename, len, "%s_%d.bmp", prefix, img_num);
314 img_num++;
315 ScreenshotWindow(hwnd, filename, SDL_TRUE);
316
317 SDL_free(filename);
318 return TRUE;
319 }
320
321
322 /* each window of the process will have a screenshot taken. The file name will be
323 prefix-i.png for the i'th window. */
324 int
SDLVisualTest_ScreenshotProcess(SDL_ProcessInfo * pinfo,char * prefix)325 SDLVisualTest_ScreenshotProcess(SDL_ProcessInfo* pinfo, char* prefix)
326 {
327 if(!pinfo)
328 {
329 SDLTest_LogError("pinfo argument cannot be NULL");
330 return 0;
331 }
332 if(!prefix)
333 {
334 SDLTest_LogError("prefix argument cannot be NULL");
335 return 0;
336 }
337
338 img_num = 1;
339 screenshot_pinfo = *pinfo;
340 if(!EnumWindows(ScreenshotHwnd, (LPARAM)prefix))
341 {
342 SDLTest_LogError("EnumWindows() failed");
343 return 0;
344 }
345
346 return 1;
347 }
348
349 #endif
350