windows_screenshot.c 8.8 KB

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