• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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