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