windows_screenshot.c (8972B)
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 19void LogLastError(char* str); 20 21static int img_num; 22static SDL_ProcessInfo screenshot_pinfo; 23 24/* Saves a bitmap to a file using hdc as a device context */ 25static int 26SaveBitmapToFile(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 118savebitmaptofile_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 130savebitmaptofile_cleanup_unlockhdib: 131 GlobalUnlock(hdib); 132 133savebitmaptofile_cleanup_hdib: 134 GlobalFree(hdib); 135 136savebitmaptofile_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 */ 142static int 143ScreenshotWindow(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 246screenshotwindow_cleanup_capturebitmap: 247 if(!DeleteObject(capturebitmap)) 248 { 249 SDLTest_LogError("DeleteObjectFailed"); 250 return_code = 0; 251 } 252 253screenshotwindow_cleanup_capturedc: 254 if(!DeleteDC(capturedc)) 255 { 256 SDLTest_LogError("DeleteDC() failed"); 257 return_code = 0; 258 } 259 260screenshotwindow_cleanup_windowdc: 261 if(!ReleaseDC(hwnd, windowdc)) 262 { 263 SDLTest_LogError("ReleaseDC() failed"); 264 return_code = 0;; 265 } 266 267screenshotwindow_cleanup_generic: 268 return return_code; 269} 270 271/* Takes the screenshot of the entire desktop and saves it to a file */ 272int 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 */ 280static BOOL CALLBACK 281ScreenshotHwnd(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. */ 324int 325SDLVisualTest_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