cscg22-gearboy

CSCG 2022 Challenge 'Gearboy'
git clone https://git.sinitax.com/sinitax/cscg22-gearboy
Log | Files | Refs | sfeed.txt

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