minesweeper

A minewseeper implementation to play around with Hare and Raylib
git clone https://git.tronto.net/minesweeper
Download | Log | Files | Refs | README | LICENSE

win32_clipboard.h (13022B)


      1 #if !defined(_WIN32)
      2 #   error "This module is only made for Windows OS"
      3 #endif
      4 
      5 #ifndef WIN32_CLIPBOARD_
      6 #define WIN32_CLIPBOARD_
      7 unsigned char* Win32GetClipboardImageData(int* width, int* height, unsigned long long int *dataSize);
      8 #endif // WIN32_CLIPBOARD_
      9 
     10 #ifdef WIN32_CLIPBOARD_IMPLEMENTATION
     11 #include <stdio.h>
     12 #include <stdbool.h>
     13 #include <stdlib.h>
     14 #include <assert.h>
     15 
     16 // NOTE: These search for architecture is taken from "Windows.h", and it's necessary if we really don't wanna import windows.h 
     17 // and still make it compile on msvc, because import indirectly importing "winnt.h" (e.g. <minwindef.h>) can cause problems is these are not defined.
     18 #if !defined(_X86_) && !defined(_68K_) && !defined(_MPPC_) && !defined(_IA64_) && !defined(_AMD64_) && !defined(_ARM_) && !defined(_ARM64_) && !defined(_ARM64EC_) && defined(_M_IX86)
     19 #define _X86_
     20 #if !defined(_CHPE_X86_ARM64_) && defined(_M_HYBRID)
     21 #define _CHPE_X86_ARM64_
     22 #endif
     23 #endif
     24 
     25 #if !defined(_AMD64_) && !defined(_68K_) && !defined(_MPPC_) && !defined(_X86_) && !defined(_IA64_) && !defined(_AMD64_) && !defined(_ARM_) && !defined(_ARM64_) && (defined(_M_AMD64) || defined(_M_ARM64EC))
     26 #define _AMD64_
     27 #endif
     28 
     29 #if !defined(_ARM_) && !defined(_68K_) && !defined(_MPPC_) && !defined(_X86_) && !defined(_IA64_) && !defined(_AMD64_) && !defined(_ARM64_) && !defined(_ARM64EC_) && defined(_M_ARM)
     30 #define _ARM_
     31 #endif
     32 
     33 #if !defined(_ARM64_) && !defined(_68K_) && !defined(_MPPC_) && !defined(_X86_) && !defined(_IA64_) && !defined(_AMD64_) && !defined(_ARM_) && !defined(_ARM64EC_) && defined(_M_ARM64)
     34 #define _ARM64_
     35 #endif
     36 
     37 #if !defined(_68K_) && !defined(_MPPC_) && !defined(_X86_) && !defined(_IA64_) && !defined(_ARM_) && !defined(_ARM64_) && !defined(_ARM64EC_) && defined(_M_ARM64EC)
     38 #define _ARM64EC_
     39 #endif
     40 
     41 #if !defined(_68K_) && !defined(_MPPC_) && !defined(_X86_) && !defined(_IA64_) && !defined(_AMD64_) && !defined(_ARM_) && !defined(_ARM64_) && !defined(_ARM64EC_) && defined(_M_M68K)
     42 #define _68K_
     43 #endif
     44 
     45 #if !defined(_68K_) && !defined(_MPPC_) && !defined(_X86_) && !defined(_IA64_) && !defined(_AMD64_) && !defined(_ARM_) && !defined(_ARM64_) && !defined(_ARM64EC_) && defined(_M_MPPC)
     46 #define _MPPC_
     47 #endif
     48 
     49 #if !defined(_IA64_) && !defined(_68K_) && !defined(_MPPC_) && !defined(_X86_) && !defined(_M_IX86) && !defined(_AMD64_) && !defined(_ARM_) && !defined(_ARM64_) && !defined(_ARM64EC_) && defined(_M_IA64)
     50 #define _IA64_
     51 #endif
     52 
     53 
     54 #define WIN32_LEAN_AND_MEAN
     55 // #include <sdkddkver.h>
     56 // #include <windows.h>
     57 // #include <winuser.h>
     58 #include <minwindef.h>
     59 // #include <minwinbase.h>
     60 
     61 #ifndef WINAPI
     62 #if defined(_ARM_)
     63 #define WINAPI
     64 #else
     65 #define WINAPI __stdcall
     66 #endif
     67 #endif
     68 
     69 #ifndef WINAPI
     70 #if defined(_ARM_)
     71 #define WINAPI
     72 #else
     73 #define WINAPI __stdcall
     74 #endif
     75 #endif
     76 
     77 #ifndef WINBASEAPI
     78 #ifndef _KERNEL32_
     79 #define WINBASEAPI DECLSPEC_IMPORT
     80 #else
     81 #define WINBASEAPI
     82 #endif
     83 #endif
     84 
     85 #ifndef WINUSERAPI
     86 #ifndef _USER32_
     87 #define WINUSERAPI __declspec (dllimport)
     88 #else
     89 #define WINUSERAPI
     90 #endif
     91 #endif
     92 
     93 typedef int WINBOOL;
     94 
     95 
     96 
     97 // typedef HANDLE HGLOBAL;
     98 
     99 #ifndef HWND
    100 #define HWND void*
    101 #endif
    102 
    103 
    104 #if !defined(_WINUSER_) || !defined(WINUSER_ALREADY_INCLUDED)
    105 WINUSERAPI WINBOOL WINAPI OpenClipboard(HWND hWndNewOwner);
    106 WINUSERAPI WINBOOL WINAPI CloseClipboard(VOID);
    107 WINUSERAPI DWORD   WINAPI GetClipboardSequenceNumber(VOID);
    108 WINUSERAPI HWND    WINAPI GetClipboardOwner(VOID);
    109 WINUSERAPI HWND    WINAPI SetClipboardViewer(HWND hWndNewViewer);
    110 WINUSERAPI HWND    WINAPI GetClipboardViewer(VOID);
    111 WINUSERAPI WINBOOL WINAPI ChangeClipboardChain(HWND hWndRemove, HWND hWndNewNext);
    112 WINUSERAPI HANDLE  WINAPI SetClipboardData(UINT uFormat, HANDLE hMem);
    113 WINUSERAPI HANDLE  WINAPI GetClipboardData(UINT uFormat);
    114 WINUSERAPI UINT    WINAPI RegisterClipboardFormatA(LPCSTR  lpszFormat);
    115 WINUSERAPI UINT    WINAPI RegisterClipboardFormatW(LPCWSTR lpszFormat);
    116 WINUSERAPI int     WINAPI CountClipboardFormats(VOID);
    117 WINUSERAPI UINT    WINAPI EnumClipboardFormats(UINT format);
    118 WINUSERAPI int     WINAPI GetClipboardFormatNameA(UINT format, LPSTR  lpszFormatName, int cchMaxCount);
    119 WINUSERAPI int     WINAPI GetClipboardFormatNameW(UINT format, LPWSTR lpszFormatName, int cchMaxCount);
    120 WINUSERAPI WINBOOL WINAPI EmptyClipboard(VOID);
    121 WINUSERAPI WINBOOL WINAPI IsClipboardFormatAvailable(UINT format);
    122 WINUSERAPI int     WINAPI GetPriorityClipboardFormat(UINT *paFormatPriorityList, int cFormats);
    123 WINUSERAPI HWND    WINAPI GetOpenClipboardWindow(VOID);
    124 #endif
    125 
    126 #ifndef HGLOBAL
    127 #define HGLOBAL void*
    128 #endif
    129 
    130 #if !defined(_WINBASE_) || !defined(WINBASE_ALREADY_INCLUDED)
    131 WINBASEAPI SIZE_T  WINAPI GlobalSize (HGLOBAL hMem);
    132 WINBASEAPI LPVOID  WINAPI GlobalLock (HGLOBAL hMem);
    133 WINBASEAPI WINBOOL WINAPI GlobalUnlock (HGLOBAL hMem);
    134 #endif
    135 
    136 
    137 #if !defined(_WINGDI_) || !defined(WINGDI_ALREADY_INCLUDED)
    138 #ifndef BITMAPINFOHEADER_ALREADY_DEFINED
    139 #define BITMAPINFOHEADER_ALREADY_DEFINED
    140 // Does this header need to be packed ? by the windowps header it doesnt seem to be
    141 #pragma pack(push, 1)
    142 typedef struct tagBITMAPINFOHEADER {
    143     DWORD biSize;
    144     LONG biWidth;
    145     LONG biHeight;
    146     WORD biPlanes;
    147     WORD biBitCount;
    148     DWORD biCompression;
    149     DWORD biSizeImage;
    150     LONG biXPelsPerMeter;
    151     LONG biYPelsPerMeter;
    152     DWORD biClrUsed;
    153     DWORD biClrImportant;
    154 } BITMAPINFOHEADER,*LPBITMAPINFOHEADER,*PBITMAPINFOHEADER;
    155 #pragma pack(pop)
    156 #endif
    157 
    158 #ifndef BITMAPFILEHEADER_ALREADY_DEFINED
    159 #define BITMAPFILEHEADER_ALREADY_DEFINED
    160 #pragma pack(push, 1)
    161 typedef struct tagBITMAPFILEHEADER {
    162     WORD bfType;
    163     DWORD bfSize;
    164     WORD bfReserved1;
    165     WORD bfReserved2;
    166     DWORD bfOffBits;
    167 } BITMAPFILEHEADER,*LPBITMAPFILEHEADER,*PBITMAPFILEHEADER;
    168 #pragma pack(pop)
    169 #endif
    170 
    171 #ifndef RGBQUAD_ALREADY_DEFINED
    172 #define RGBQUAD_ALREADY_DEFINED
    173 typedef struct tagRGBQUAD {
    174   BYTE rgbBlue;
    175   BYTE rgbGreen;
    176   BYTE rgbRed;
    177   BYTE rgbReserved;
    178 } RGBQUAD, *LPRGBQUAD;
    179 #endif
    180 
    181 
    182 // https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-wmf/4e588f70-bd92-4a6f-b77f-35d0feaf7a57
    183 #define BI_RGB       0x0000
    184 #define BI_RLE8      0x0001
    185 #define BI_RLE4      0x0002
    186 #define BI_BITFIELDS 0x0003
    187 #define BI_JPEG      0x0004
    188 #define BI_PNG       0x0005
    189 #define BI_CMYK      0x000B
    190 #define BI_CMYKRLE8  0x000C
    191 #define BI_CMYKRLE4  0x000D
    192 
    193 #endif
    194 
    195 // https://learn.microsoft.com/en-us/windows/win32/dataxchg/standard-clipboard-formats
    196 #define CF_DIB 8
    197 
    198 // https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-setsystemcursor
    199 // #define OCR_NORMAL      32512 // Normal     select
    200 // #define OCR_IBEAM       32513 // Text       select
    201 // #define OCR_WAIT        32514 // Busy
    202 // #define OCR_CROSS       32515 // Precision  select
    203 // #define OCR_UP          32516 // Alternate  select
    204 // #define OCR_SIZENWSE    32642 // Diagonal   resize 1
    205 // #define OCR_SIZENESW    32643 // Diagonal   resize 2
    206 // #define OCR_SIZEWE      32644 // Horizontal resize
    207 // #define OCR_SIZENS      32645 // Vertical   resize
    208 // #define OCR_SIZEALL     32646 // Move
    209 // #define OCR_NO          32648 // Unavailable
    210 // #define OCR_HAND        32649 // Link       select
    211 // #define OCR_APPSTARTING 32650 //
    212 
    213 
    214 //----------------------------------------------------------------------------------
    215 // Module Internal Functions Declaration
    216 //----------------------------------------------------------------------------------
    217 
    218 
    219 static BOOL           OpenClipboardRetrying(HWND handle); // Open clipboard with a number of retries
    220 static int            GetPixelDataOffset(BITMAPINFOHEADER bih);
    221 
    222 unsigned char* Win32GetClipboardImageData(int* width, int* height, unsigned long long int *dataSize)
    223 {
    224     HWND win = NULL; // Get from somewhere but is doesnt seem to matter
    225     const char* msgString = "";
    226     int severity = LOG_INFO;
    227     BYTE* bmpData = NULL;
    228     if (!OpenClipboardRetrying(win)) {
    229         severity = LOG_ERROR;
    230         msgString = "Couldn't open clipboard";
    231         goto end;
    232     }
    233 
    234     HGLOBAL clipHandle = (HGLOBAL)GetClipboardData(CF_DIB);
    235     if (!clipHandle) {
    236         severity = LOG_ERROR;
    237         msgString = "Clipboard data is not an Image";
    238         goto close;
    239     }
    240 
    241     BITMAPINFOHEADER *bmpInfoHeader = (BITMAPINFOHEADER *)GlobalLock(clipHandle);
    242     if (!bmpInfoHeader) {
    243         // Mapping from HGLOBAL to our local *address space* failed
    244         severity = LOG_ERROR;
    245         msgString = "Clipboard data failed to be locked";
    246         goto unlock;
    247     }
    248 
    249     *width = bmpInfoHeader->biWidth;
    250     *height = bmpInfoHeader->biHeight;
    251 
    252     SIZE_T clipDataSize = GlobalSize(clipHandle);
    253     if (clipDataSize < sizeof(BITMAPINFOHEADER)) {
    254         // Format CF_DIB needs space for BITMAPINFOHEADER struct.
    255         msgString = "Clipboard has Malformed data";
    256         severity = LOG_ERROR;
    257         goto unlock;
    258     }
    259 
    260     // Denotes where the pixel data starts from the bmpInfoHeader pointer
    261     int pixelOffset = GetPixelDataOffset(*bmpInfoHeader);
    262 
    263     //--------------------------------------------------------------------------------//
    264     //
    265     // The rest of the section is about create the bytes for a correct BMP file
    266     // Then we copy the data and to a pointer
    267     //
    268     //--------------------------------------------------------------------------------//
    269 
    270     BITMAPFILEHEADER bmpFileHeader = {0};
    271     SIZE_T bmpFileSize = sizeof(bmpFileHeader) + clipDataSize;
    272     *dataSize = bmpFileSize;
    273 
    274     bmpFileHeader.bfType = 0x4D42; //https://stackoverflow.com/questions/601430/multibyte-character-constants-and-bitmap-file-header-type-constants#601536
    275 
    276     bmpFileHeader.bfSize = (DWORD)bmpFileSize; // Up to 4GB works fine
    277     bmpFileHeader.bfOffBits = sizeof(bmpFileHeader) + pixelOffset;
    278 
    279     //
    280     // Each process has a default heap provided by the system
    281     // Memory objects allocated by GlobalAlloc and LocalAlloc are in private,
    282     // committed pages with read/write access that cannot be accessed by other processes.
    283     //
    284     // This may be wrong since we might be allocating in a DLL and freeing from another module, the main application
    285     // that may cause heap corruption. We could create a FreeImage function
    286     //
    287     bmpData = malloc(sizeof(bmpFileHeader) + clipDataSize);
    288     // First we add the header for a bmp file
    289     memcpy(bmpData, &bmpFileHeader, sizeof(bmpFileHeader));
    290     // Then we add the header for the bmp itself + the pixel data
    291     memcpy(bmpData + sizeof(bmpFileHeader), bmpInfoHeader, clipDataSize);
    292     msgString = "Clipboad image acquired successfully";
    293 
    294 
    295 unlock:
    296     GlobalUnlock(clipHandle);
    297 close:
    298     CloseClipboard();
    299 end:
    300 
    301     TRACELOG(severity, msgString);
    302     return bmpData;
    303 }
    304 
    305 static BOOL OpenClipboardRetrying(HWND hWnd)
    306 {
    307     static const int maxTries = 20;
    308     static const int sleepTimeMS = 60;
    309     for (int _ = 0; _ < maxTries; ++_)
    310     {
    311         // Might be being hold by another process
    312         // Or yourself forgot to CloseClipboard
    313         if (OpenClipboard(hWnd)) {
    314             return true;
    315         }
    316         Sleep(sleepTimeMS);
    317     }
    318     return false;
    319 }
    320 
    321 // Based off of researching microsoft docs and reponses from this question https://stackoverflow.com/questions/30552255/how-to-read-a-bitmap-from-the-windows-clipboard#30552856
    322 // https://learn.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-bitmapinfoheader
    323 // Get the byte offset where does the pixels data start (from a packed DIB)
    324 static int GetPixelDataOffset(BITMAPINFOHEADER bih)
    325 {
    326     int offset = 0;
    327     const unsigned int rgbaSize = sizeof(RGBQUAD);
    328 
    329     // biSize Specifies the number of bytes required by the structure
    330     // We expect to always be 40 because it should be packed
    331     if (40 == bih.biSize && 40 == sizeof(BITMAPINFOHEADER))
    332     {
    333         //
    334         // biBitCount Specifies the number of bits per pixel.
    335         // Might exist some bit masks *after* the header and *before* the pixel offset
    336         // we're looking, but only if we have more than
    337         // 8 bits per pixel, so we need to ajust for that
    338         //
    339         if (bih.biBitCount > 8)
    340         {
    341             // if bih.biCompression is RBG we should NOT offset more
    342 
    343             if (bih.biCompression == BI_BITFIELDS)
    344             {
    345                 offset += 3 * rgbaSize;
    346             } else if (bih.biCompression == 6 /* BI_ALPHABITFIELDS */)
    347             {
    348                 // Not widely supported, but valid.
    349                 offset += 4 * rgbaSize;
    350             }
    351         }
    352     }
    353 
    354     //
    355     // biClrUsed Specifies the number of color indices in the color table that are actually used by the bitmap.
    356     // If this value is zero, the bitmap uses the maximum number of colors
    357     // corresponding to the value of the biBitCount member for the compression mode specified by biCompression.
    358     // If biClrUsed is nonzero and the biBitCount member is less than 16
    359     // the biClrUsed member specifies the actual number of colors
    360     //
    361     if (bih.biClrUsed > 0) {
    362         offset += bih.biClrUsed * rgbaSize;
    363     } else {
    364         if (bih.biBitCount < 16)
    365         {
    366             offset = offset + (rgbaSize << bih.biBitCount);
    367         }
    368     }
    369 
    370     return bih.biSize + offset;
    371 }
    372 #endif // WIN32_CLIPBOARD_IMPLEMENTATION
    373 // EOF
    374