Edit

kc3-lang/SDL/visualtest/src/windows/windows_screenshot.c

Branch :

  • Show log

    Commit

  • Author : Pierre-Loup A. Griffais
    Date : 2014-09-11 19:24:42
    Hash : 24c86b55
    Message : [X11] Reconcile logical keyboard state with physical state on FocusIn since the window system doesn't do it for us like other platforms. This prevents sticky keys and missed keys when going in and out of focus, for example Alt would appear to stick if switching away from an SDL app with Alt-Tab and had to be pressed again. CR: Sam

  • visualtest/src/windows/windows_screenshot.c
  • /* See COPYING.txt for the full license governing this code. */
    /**
     * \file windows_screenshot.c 
     *
     * Source file for the screenshot API on windows.
     */
    
    #include "SDL_visualtest_process.h"
    #include <SDL.h>
    #include <SDL_test.h>
    
    #if defined(__CYGWIN__)
    #include <sys/stat.h>
    #endif
    
    #if defined(__WIN32__)
    #include <Windows.h>
    
    void LogLastError(char* str);
    
    static int img_num;
    static SDL_ProcessInfo screenshot_pinfo;
    
    /* Saves a bitmap to a file using hdc as a device context */
    static int
    SaveBitmapToFile(HDC hdc, HBITMAP hbitmap, char* filename)
    {
        BITMAP bitmap;
        BITMAPFILEHEADER bfh;
        BITMAPINFOHEADER bih;
        DWORD bmpsize, bytes_written;
        HANDLE hdib, hfile;
        char* bmpdata;
        int return_code = 1;
    
        if(!hdc)
        {
            SDLTest_LogError("hdc argument is NULL");
            return 0;
        }
        if(!hbitmap)
        {
            SDLTest_LogError("hbitmap argument is NULL");
            return 0;
        }
        if(!filename)
        {
            SDLTest_LogError("filename argument is NULL");
            return 0;
        }
    
        if(!GetObject(hbitmap, sizeof(BITMAP), (void*)&bitmap))
        {
            SDLTest_LogError("GetObject() failed");
            return_code = 0;
            goto savebitmaptofile_cleanup_generic;
        }
        
        bih.biSize = sizeof(BITMAPINFOHEADER);
        bih.biWidth = bitmap.bmWidth;
        bih.biHeight = bitmap.bmHeight;
        bih.biPlanes = 1;
        bih.biBitCount = 32;
        bih.biCompression = BI_RGB;
        bih.biSizeImage = 0;
        bih.biXPelsPerMeter = 0;
        bih.biYPelsPerMeter = 0;
        bih.biClrUsed = 0;
        bih.biClrImportant = 0;
    
        bmpsize = ((bitmap.bmWidth * bih.biBitCount + 31) / 32) * 4 * bitmap.bmHeight;
    
        hdib = GlobalAlloc(GHND, bmpsize);
        if(!hdib)
        {
            LogLastError("GlobalAlloc() failed");
            return_code = 0;
            goto savebitmaptofile_cleanup_generic;
        }
        bmpdata = (char*)GlobalLock(hdib);
        if(!bmpdata)
        {
            LogLastError("GlobalLock() failed");
            return_code = 0;
            goto savebitmaptofile_cleanup_hdib;
        }
    
        if(!GetDIBits(hdc, hbitmap, 0, (UINT)bitmap.bmHeight, bmpdata,
                      (LPBITMAPINFO)&bih, DIB_RGB_COLORS))
        {
            SDLTest_LogError("GetDIBits() failed");
            return_code = 0;
            goto savebitmaptofile_cleanup_unlockhdib;
        }
    
        hfile = CreateFile(filename, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
                           FILE_ATTRIBUTE_NORMAL, NULL);
        if(hfile == INVALID_HANDLE_VALUE)
        {
            LogLastError("CreateFile()");
            return_code = 0;
            goto savebitmaptofile_cleanup_unlockhdib;
        }
        bfh.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
        bfh.bfSize = bmpsize + bfh.bfOffBits;
        bfh.bfType = 0x4D42;
    
        bytes_written = 0;
        if(!WriteFile(hfile, (void*)&bfh, sizeof(BITMAPFILEHEADER), &bytes_written, NULL) ||
           !WriteFile(hfile, (void*)&bih, sizeof(BITMAPINFOHEADER), &bytes_written, NULL) ||
           !WriteFile(hfile, (void*)bmpdata, bmpsize, &bytes_written, NULL))
        {
            LogLastError("WriteFile() failed");
            return_code = 0;
            goto savebitmaptofile_cleanup_hfile;
        }
    
    savebitmaptofile_cleanup_hfile:
        CloseHandle(hfile);
    
    /* make the screenshot file writable on cygwin, since it could be overwritten later */
    #if defined(__CYGWIN__)
        if(chmod(filename, 0777) == -1)
        {
            SDLTest_LogError("chmod() failed");
            return_code = 0;
        }
    #endif
    
    savebitmaptofile_cleanup_unlockhdib:
        GlobalUnlock(hdib);
    
    savebitmaptofile_cleanup_hdib:
        GlobalFree(hdib);
    
    savebitmaptofile_cleanup_generic:
        return return_code;
    }
    
    /* Takes the screenshot of a window and saves it to a file. If only_client_area
       is true, then only the client area of the window is considered */
    static int
    ScreenshotWindow(HWND hwnd, char* filename, SDL_bool only_client_area)
    {
        int width, height;
        RECT dimensions;
        HDC windowdc, capturedc;
        HBITMAP capturebitmap;
        HGDIOBJ select_success;
        BOOL blt_success;
        int return_code = 1;
    
        if(!filename)
        {
            SDLTest_LogError("filename argument cannot be NULL");
            return_code = 0;
            goto screenshotwindow_cleanup_generic;
        }
        if(!hwnd)
        {
            SDLTest_LogError("hwnd argument cannot be NULL");
            return_code = 0;
            goto screenshotwindow_cleanup_generic;
        }
    
        if(!GetWindowRect(hwnd, &dimensions))
        {
            LogLastError("GetWindowRect() failed");
            return_code = 0;
            goto screenshotwindow_cleanup_generic;
        }
    
        if(only_client_area)
        {
            RECT crect;
            if(!GetClientRect(hwnd, &crect))
            {
                SDLTest_LogError("GetClientRect() failed");
                return_code = 0;
                goto screenshotwindow_cleanup_generic;
            }
    
            width = crect.right;
            height = crect.bottom;
            windowdc = GetDC(hwnd);
            if(!windowdc)
            {
                SDLTest_LogError("GetDC() failed");
                return_code = 0;
                goto screenshotwindow_cleanup_generic;
            }
        }
        else
        {
            width = dimensions.right - dimensions.left;
            height = dimensions.bottom - dimensions.top;
            windowdc = GetWindowDC(hwnd);
            if(!windowdc)
            {
                SDLTest_LogError("GetWindowDC() failed");
                return_code = 0;
                goto screenshotwindow_cleanup_generic;
            }
        }
        
        capturedc = CreateCompatibleDC(windowdc);
        if(!capturedc)
        {
            SDLTest_LogError("CreateCompatibleDC() failed");
            return_code = 0;
            goto screenshotwindow_cleanup_windowdc;
        }
        capturebitmap = CreateCompatibleBitmap(windowdc, width, height);
        if(!capturebitmap)
        {
            SDLTest_LogError("CreateCompatibleBitmap() failed");
            return_code = 0;
            goto screenshotwindow_cleanup_capturedc;
        }
        select_success = SelectObject(capturedc, capturebitmap);
        if(!select_success || select_success == HGDI_ERROR)
        {
            SDLTest_LogError("SelectObject() failed");
            return_code = 0;
            goto screenshotwindow_cleanup_capturebitmap;
        }
        blt_success = BitBlt(capturedc, 0, 0, width, height, windowdc,
                             0, 0, SRCCOPY|CAPTUREBLT);
        if(!blt_success)
        {
            LogLastError("BitBlt() failed");
            return_code = 0;
            goto screenshotwindow_cleanup_capturebitmap;
        }
    
        /* save bitmap as file */
        if(!SaveBitmapToFile(windowdc, capturebitmap, filename))
        {
            SDLTest_LogError("SaveBitmapToFile() failed");
            return_code = 0;
            goto screenshotwindow_cleanup_capturebitmap;
        }
    
        /* free resources */
    
    screenshotwindow_cleanup_capturebitmap:
        if(!DeleteObject(capturebitmap))
        {
            SDLTest_LogError("DeleteObjectFailed");
            return_code = 0;
        }
    
    screenshotwindow_cleanup_capturedc:
        if(!DeleteDC(capturedc))
        {
            SDLTest_LogError("DeleteDC() failed");
            return_code = 0;
        }
    
    screenshotwindow_cleanup_windowdc:
        if(!ReleaseDC(hwnd, windowdc))
        {
            SDLTest_LogError("ReleaseDC() failed");
            return_code = 0;;
        }
    
    screenshotwindow_cleanup_generic:
        return return_code;
    }
    
    /* Takes the screenshot of the entire desktop and saves it to a file */
    int SDLVisualTest_ScreenshotDesktop(char* filename)
    {
        HWND hwnd;
        hwnd = GetDesktopWindow();
        return ScreenshotWindow(hwnd, filename, SDL_FALSE);
    }
    
    /* take screenshot of a window and save it to a file */
    static BOOL CALLBACK
    ScreenshotHwnd(HWND hwnd, LPARAM lparam)
    {
        int len;
        DWORD pid;
        char* prefix;
        char* filename;
    
        GetWindowThreadProcessId(hwnd, &pid);
        if(pid != screenshot_pinfo.pi.dwProcessId)
            return TRUE;
    
        if(!IsWindowVisible(hwnd))
            return TRUE;
    
        prefix = (char*)lparam;
        len = SDL_strlen(prefix) + 100;
        filename = (char*)SDL_malloc(len * sizeof(char));
        if(!filename)
        {
            SDLTest_LogError("malloc() failed");
            return FALSE;
        }
    
        /* restore the window and bring it to the top */
        ShowWindowAsync(hwnd, SW_RESTORE);
        /* restore is not instantaneous */
        SDL_Delay(500);
    
        /* take a screenshot of the client area */
        if(img_num == 1)
            SDL_snprintf(filename, len, "%s.bmp", prefix);
        else
            SDL_snprintf(filename, len, "%s_%d.bmp", prefix, img_num);
        img_num++;
        ScreenshotWindow(hwnd, filename, SDL_TRUE);
    
        SDL_free(filename);
        return TRUE;
    }
    
    
    /* each window of the process will have a screenshot taken. The file name will be
       prefix-i.png for the i'th window. */
    int
    SDLVisualTest_ScreenshotProcess(SDL_ProcessInfo* pinfo, char* prefix)
    {
        if(!pinfo)
        {
            SDLTest_LogError("pinfo argument cannot be NULL");
            return 0;
        }
        if(!prefix)
        {
            SDLTest_LogError("prefix argument cannot be NULL");
            return 0;
        }
    
        img_num = 1;
        screenshot_pinfo = *pinfo;
        if(!EnumWindows(ScreenshotHwnd, (LPARAM)prefix))
        {
            SDLTest_LogError("EnumWindows() failed");
            return 0;
        }
    
        return 1;
    }
    
    #endif