sdl

FORK: Simple Directmedia Layer
git clone https://git.neptards.moe/neptards/sdl.git
Log | Files | Refs

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 
     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
     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
    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 */
    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
    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
    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