Browse Source

[rcore] add 'GetClipboardImage' for windows

pull/4459/head
evertonse 2 months ago
parent
commit
7880670597
7 changed files with 539 additions and 1 deletions
  1. +2
    -1
      examples/Makefile
  2. +34
    -0
      examples/core/core_clipboard_image.c
  3. +21
    -0
      src/config.h
  4. +443
    -0
      src/platforms/rcore_clipboard_win32.c
  5. +22
    -0
      src/platforms/rcore_desktop_sdl.c
  6. +1
    -0
      src/raylib.h
  7. +16
    -0
      src/rcore.c

+ 2
- 1
examples/Makefile View File

@ -516,7 +516,8 @@ CORE = \
core/core_window_flags \
core/core_window_letterbox \
core/core_window_should_close \
core/core_world_screen
core/core_world_screen \
core/core_clipboard_image
SHAPES = \
shapes/shapes_basic_shapes \

+ 34
- 0
examples/core/core_clipboard_image.c View File

@ -0,0 +1,34 @@
#include "raylib.h"
#include <stdio.h>
#include <stdlib.h>
static Image img = {0};
int main(int argc, char *argv[]) {
InitWindow(800, 450, "[core] raylib clipboard image");
SetTraceLogLevel(LOG_TRACE);
SetTargetFPS(60);
Texture tex = {0};
while(!WindowShouldClose()) {
if (IsKeyDown(KEY_LEFT_CONTROL) && IsKeyPressed(KEY_V)) {
#ifdef _WIN32
img = GetClipboardImage();
tex = LoadTextureFromImage(img);
if(!IsTextureValid(tex)) {
exit(98);
} else {
ExportImage(img, "Debug.bmp");
}
#endif
}
BeginDrawing();
ClearBackground(RAYWHITE);
if (IsTextureValid(tex)) {
DrawTexture(tex, 0, 0, WHITE);
}
DrawText("Print Screen and Crtl+V", 10, 10, 21, BLACK);
EndDrawing();
}
}

+ 21
- 0
src/config.h View File

@ -71,6 +71,7 @@
// Enabling this flag allows manual control of the frame processes, use at your own risk
//#define SUPPORT_CUSTOM_FRAME_CONTROL 1
// rcore: Configuration values
//------------------------------------------------------------------------------------
#define MAX_FILEPATH_CAPACITY 8192 // Maximum file paths capacity
@ -272,4 +273,24 @@
//------------------------------------------------------------------------------------
#define MAX_TRACELOG_MSG_LENGTH 256 // Max length of one trace-log message
// Enable partial support for clipboard image, only working on SDL3 or
// being on both Windows OS + GLFW or Windows OS + RGFW
#define SUPPORT_CLIPBOARD_IMAGE 1
#if defined(SUPPORT_CLIPBOARD_IMAGE)
#ifndef STBI_REQUIRED
#define STBI_REQUIRED
#endif
#ifndef SUPPORT_FILEFORMAT_BMP
#define SUPPORT_FILEFORMAT_BMP 1
#endif
#ifndef SUPPORT_MODULE_RTEXTURES
#define SUPPORT_MODULE_RTEXTURES 1
#endif
#endif
#endif // CONFIG_H

+ 443
- 0
src/platforms/rcore_clipboard_win32.c View File

@ -0,0 +1,443 @@
#if !defined(_WIN32)
# error "This module is only made for Windows OS"
#else
// Needs both `Image` and `LoadImageFromMemory` from `rtexture` >:C
#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
#include <assert.h>
#define DrawText DrawText_win32
#define Rectangle rectangle_win32
#define CloseWindow CloseWindow_win32
#define ShowCursor __imp_ShowCursor
// To avoid conflicting windows.h symbols with raylib, some flags are defined
// WARNING: Those flags avoid inclusion of some Win32 headers that could be required
// by user at some point and won't be included...
//-------------------------------------------------------------------------------------
#define NOGDICAPMASKS // CC_*, LC_*, PC_*, CP_*, TC_*, RC_
#define NOVIRTUALKEYCODES // VK_*
#define NOWINMESSAGES // WM_*, EM_*, LB_*, CB_*
#define NOWINSTYLES // WS_*, CS_*, ES_*, LBS_*, SBS_*, CBS_*
#define NOSYSMETRICS // SM_*
#define NOMENUS // MF_*
#define NOICONS // IDI_*
#define NOKEYSTATES // MK_*
#define NOSYSCOMMANDS // SC_*
#define NORASTEROPS // Binary and Tertiary raster ops
#define NOSHOWWINDOW // SW_*
#define OEMRESOURCE // OEM Resource values
#define NOATOM // Atom Manager routines
#define NOCLIPBOARD // Clipboard routines
#define NOCOLOR // Screen colors
#define NOCTLMGR // Control and Dialog routines
#define NODRAWTEXT // DrawText() and DT_*
// #define NOGDI // All GDI defines and routines
#define NOKERNEL // All KERNEL defines and routines
#define NOUSER // All USER defines and routines
#define NONLS // All NLS defines and routines
#define NOMB // MB_* and MessageBox()
#define NOMEMMGR // GMEM_*, LMEM_*, GHND, LHND, associated routines
#define NOMETAFILE // typedef METAFILEPICT
#define NOMINMAX // Macros min(a,b) and max(a,b)
#define NOMSG // typedef MSG and associated routines
#define NOOPENFILE // OpenFile(), OemToAnsi, AnsiToOem, and OF_*
#define NOSCROLL // SB_* and scrolling routines
#define NOSERVICE // All Service Controller routines, SERVICE_ equates, etc.
#define NOSOUND // Sound driver routines
#define NOTEXTMETRIC // typedef TEXTMETRIC and associated routines
#define NOWH // SetWindowsHook and WH_*
#define NOWINOFFSETS // GWL_*, GCL_*, associated routines
#define NOCOMM // COMM driver routines
#define NOKANJI // Kanji support stuff.
#define NOHELP // Help engine interface.
#define NOPROFILER // Profiler interface.
#define NODEFERWINDOWPOS // DeferWindowPos routines
#define NOMCX // Modem Configuration Extensions
// Type required before windows.h inclusion
typedef struct tagMSG *LPMSG;
#define WIN32_LEAN_AND_MEAN
// #include <sdkddkver.h>
// #include <windows.h>
// #include <winuser.h>
#include <minwindef.h>
// #include <minwinbase.h>
#ifndef WINAPI
#if defined(_ARM_)
#define WINAPI
#else
#define WINAPI __stdcall
#endif
#endif
#ifndef WINAPI
#if defined(_ARM_)
#define WINAPI
#else
#define WINAPI __stdcall
#endif
#endif
#ifndef WINBASEAPI
#ifndef _KERNEL32_
#define WINBASEAPI DECLSPEC_IMPORT
#else
#define WINBASEAPI
#endif
#endif
#ifndef WINUSERAPI
#ifndef _USER32_
#define WINUSERAPI __declspec (dllimport)
#else
#define WINUSERAPI
#endif
#endif
typedef int WINBOOL;
// typedef HANDLE HGLOBAL;
#ifndef HWND
#define HWND void*
#endif
#if !defined(_WINUSER_) || !defined(WINUSER_ALREADY_INCLUDED)
WINUSERAPI WINBOOL WINAPI OpenClipboard(HWND hWndNewOwner);
WINUSERAPI WINBOOL WINAPI CloseClipboard(VOID);
WINUSERAPI DWORD WINAPI GetClipboardSequenceNumber(VOID);
WINUSERAPI HWND WINAPI GetClipboardOwner(VOID);
WINUSERAPI HWND WINAPI SetClipboardViewer(HWND hWndNewViewer);
WINUSERAPI HWND WINAPI GetClipboardViewer(VOID);
WINUSERAPI WINBOOL WINAPI ChangeClipboardChain(HWND hWndRemove, HWND hWndNewNext);
WINUSERAPI HANDLE WINAPI SetClipboardData(UINT uFormat, HANDLE hMem);
WINUSERAPI HANDLE WINAPI GetClipboardData(UINT uFormat);
WINUSERAPI UINT WINAPI RegisterClipboardFormatA(LPCSTR lpszFormat);
WINUSERAPI UINT WINAPI RegisterClipboardFormatW(LPCWSTR lpszFormat);
WINUSERAPI int WINAPI CountClipboardFormats(VOID);
WINUSERAPI UINT WINAPI EnumClipboardFormats(UINT format);
WINUSERAPI int WINAPI GetClipboardFormatNameA(UINT format, LPSTR lpszFormatName, int cchMaxCount);
WINUSERAPI int WINAPI GetClipboardFormatNameW(UINT format, LPWSTR lpszFormatName, int cchMaxCount);
WINUSERAPI WINBOOL WINAPI EmptyClipboard(VOID);
WINUSERAPI WINBOOL WINAPI IsClipboardFormatAvailable(UINT format);
WINUSERAPI int WINAPI GetPriorityClipboardFormat(UINT *paFormatPriorityList, int cFormats);
WINUSERAPI HWND WINAPI GetOpenClipboardWindow(VOID);
#endif
#ifndef HGLOBAL
#define HGLOBAL void*
#endif
#if !defined(_WINBASE_) || !defined(WINBASE_ALREADY_INCLUDED)
WINBASEAPI SIZE_T WINAPI GlobalSize (HGLOBAL hMem);
WINBASEAPI LPVOID WINAPI GlobalLock (HGLOBAL hMem);
WINBASEAPI WINBOOL WINAPI GlobalUnlock (HGLOBAL hMem);
#endif
#if !defined(_WINGDI_) || !defined(WINGDI_ALREADY_INCLUDED)
#ifndef BITMAPINFOHEADER_ALREADY_DEFINED
#define BITMAPINFOHEADER_ALREADY_DEFINED
// Does this header need to be packed ? by the windowps header it doesnt seem to be
#pragma pack(push, 1)
typedef struct tagBITMAPINFOHEADER {
DWORD biSize;
LONG biWidth;
LONG biHeight;
WORD biPlanes;
WORD biBitCount;
DWORD biCompression;
DWORD biSizeImage;
LONG biXPelsPerMeter;
LONG biYPelsPerMeter;
DWORD biClrUsed;
DWORD biClrImportant;
} BITMAPINFOHEADER,*LPBITMAPINFOHEADER,*PBITMAPINFOHEADER;
#pragma pack(pop)
#endif
#ifndef BITMAPFILEHEADER_ALREADY_DEFINED
#define BITMAPFILEHEADER_ALREADY_DEFINED
#pragma pack(push, 1)
typedef struct tagBITMAPFILEHEADER {
WORD bfType;
DWORD bfSize;
WORD bfReserved1;
WORD bfReserved2;
DWORD bfOffBits;
} BITMAPFILEHEADER,*LPBITMAPFILEHEADER,*PBITMAPFILEHEADER;
#pragma pack(pop)
#endif
#ifndef RGBQUAD_ALREADY_DEFINED
#define RGBQUAD_ALREADY_DEFINED
typedef struct tagRGBQUAD {
BYTE rgbBlue;
BYTE rgbGreen;
BYTE rgbRed;
BYTE rgbReserved;
} RGBQUAD, *LPRGBQUAD;
#endif
// https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-wmf/4e588f70-bd92-4a6f-b77f-35d0feaf7a57
#define BI_RGB 0x0000
#define BI_RLE8 0x0001
#define BI_RLE4 0x0002
#define BI_BITFIELDS 0x0003
#define BI_JPEG 0x0004
#define BI_PNG 0x0005
#define BI_CMYK 0x000B
#define BI_CMYKRLE8 0x000C
#define BI_CMYKRLE4 0x000D
#endif
// https://learn.microsoft.com/en-us/windows/win32/dataxchg/standard-clipboard-formats
#define CF_DIB 8
// https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-setsystemcursor
// #define OCR_NORMAL 32512 // Normal select
// #define OCR_IBEAM 32513 // Text select
// #define OCR_WAIT 32514 // Busy
// #define OCR_CROSS 32515 // Precision select
// #define OCR_UP 32516 // Alternate select
// #define OCR_SIZENWSE 32642 // Diagonal resize 1
// #define OCR_SIZENESW 32643 // Diagonal resize 2
// #define OCR_SIZEWE 32644 // Horizontal resize
// #define OCR_SIZENS 32645 // Vertical resize
// #define OCR_SIZEALL 32646 // Move
// #define OCR_NO 32648 // Unavailable
// #define OCR_HAND 32649 // Link select
// #define OCR_APPSTARTING 32650 //
//----------------------------------------------------------------------------------
// Module Internal Functions Declaration
//----------------------------------------------------------------------------------
static BOOL OpenClipboardRetrying(HWND handle); // Open clipboard with a number of retries
static int GetPixelDataOffset(BITMAPINFOHEADER bih);
static unsigned char* GetClipboardImageData(int* width, int* height, unsigned int *dataSize);
//----------------------------------------------------------------------------------
// Module Functions Definition: Clipboard Image
//----------------------------------------------------------------------------------
Image GetClipboardImage(void)
{
int width = 0, height = 0;
unsigned int dataSize = 0;
unsigned char* fileData = GetClipboardImageData(&width, &height, &dataSize);
Image image = LoadImageFromMemory(".bmp", fileData, dataSize);
return image;
}
static unsigned char* GetClipboardImageData(int* width, int* height, unsigned int *dataSize)
{
HWND win = NULL; // Get from somewhere but is doesnt seem to matter
const char* msgString = "";
int severity = LOG_INFO;
BYTE* bmpData = NULL;
if (!OpenClipboardRetrying(win)) {
severity = LOG_ERROR;
msgString = "Couldn't open clipboard";
goto end;
}
HGLOBAL clipHandle = (HGLOBAL)GetClipboardData(CF_DIB);
if (!clipHandle) {
severity = LOG_ERROR;
msgString = "Clipboard data is not an Image";
goto close;
}
BITMAPINFOHEADER *bmpInfoHeader = (BITMAPINFOHEADER *)GlobalLock(clipHandle);
if (!bmpInfoHeader) {
// Mapping from HGLOBAL to our local *address space* failed
severity = LOG_ERROR;
msgString = "Clipboard data failed to be locked";
goto unlock;
}
*width = bmpInfoHeader->biWidth;
*height = bmpInfoHeader->biHeight;
SIZE_T clipDataSize = GlobalSize(clipHandle);
if (clipDataSize < sizeof(BITMAPINFOHEADER)) {
// Format CF_DIB needs space for BITMAPINFOHEADER struct.
msgString = "Clipboard has Malformed data";
severity = LOG_ERROR;
goto unlock;
}
// Denotes where the pixel data starts from the bmpInfoHeader pointer
int pixelOffset = GetPixelDataOffset(*bmpInfoHeader);
//--------------------------------------------------------------------------------//
//
// The rest of the section is about create the bytes for a correct BMP file
// Then we copy the data and to a pointer
//
//--------------------------------------------------------------------------------//
BITMAPFILEHEADER bmpFileHeader = {0};
SIZE_T bmpFileSize = sizeof(bmpFileHeader) + clipDataSize;
*dataSize = bmpFileSize;
bmpFileHeader.bfType = 0x4D42; //https://stackoverflow.com/questions/601430/multibyte-character-constants-and-bitmap-file-header-type-constants#601536
bmpFileHeader.bfSize = (DWORD)bmpFileSize; // Up to 4GB works fine
bmpFileHeader.bfOffBits = sizeof(bmpFileHeader) + pixelOffset;
//
// Each process has a default heap provided by the system
// Memory objects allocated by GlobalAlloc and LocalAlloc are in private,
// committed pages with read/write access that cannot be accessed by other processes.
//
// This may be wrong since we might be allocating in a DLL and freeing from another module, the main application
// that may cause heap corruption. We could create a FreeImage function
//
bmpData = malloc(sizeof(bmpFileHeader) + clipDataSize);
// First we add the header for a bmp file
memcpy(bmpData, &bmpFileHeader, sizeof(bmpFileHeader));
// Then we add the header for the bmp itself + the pixel data
memcpy(bmpData + sizeof(bmpFileHeader), bmpInfoHeader, clipDataSize);
msgString = "Clipboad image acquired successfully";
unlock:
GlobalUnlock(clipHandle);
close:
CloseClipboard();
end:
TRACELOG(severity, msgString);
return bmpData;
}
static BOOL OpenClipboardRetrying(HWND hWnd)
{
static const int maxTries = 20;
static const int sleepTimeMS = 60;
for (int _ = 0; _ < maxTries; ++_)
{
// Might be being hold by another process
// Or yourself forgot to CloseClipboard
if (OpenClipboard(hWnd)) {
return true;
}
Sleep(sleepTimeMS);
}
return false;
}
// 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
// https://learn.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-bitmapinfoheader
// Get the byte offset where does the pixels data start (from a packed DIB)
static int GetPixelDataOffset(BITMAPINFOHEADER bih)
{
int offset = 0;
const unsigned int rgbaSize = sizeof(RGBQUAD);
// biSize Specifies the number of bytes required by the structure
// We expect to always be 40 because it should be packed
if (40 == bih.biSize && 40 == sizeof(BITMAPINFOHEADER))
{
//
// biBitCount Specifies the number of bits per pixel.
// Might exist some bit masks *after* the header and *before* the pixel offset
// we're looking, but only if we have more than
// 8 bits per pixel, so we need to ajust for that
//
if (bih.biBitCount > 8)
{
// if bih.biCompression is RBG we should NOT offset more
if (bih.biCompression == BI_BITFIELDS)
{
offset += 3 * rgbaSize;
} else if (bih.biCompression == 6 /* BI_ALPHABITFIELDS */)
{
// Not widely supported, but valid.
offset += 4 * rgbaSize;
}
}
}
//
// biClrUsed Specifies the number of color indices in the color table that are actually used by the bitmap.
// If this value is zero, the bitmap uses the maximum number of colors
// corresponding to the value of the biBitCount member for the compression mode specified by biCompression.
// If biClrUsed is nonzero and the biBitCount member is less than 16
// the biClrUsed member specifies the actual number of colors
//
if (bih.biClrUsed > 0) {
offset += bih.biClrUsed * rgbaSize;
} else {
if (bih.biBitCount < 16)
{
offset = offset + (rgbaSize << bih.biBitCount);
}
}
return bih.biSize + offset;
}
#undef NOGDICAPMASKS // CC_*, LC_*, PC_*, CP_*, TC_*, RC_
#undef NOVIRTUALKEYCODES // VK_*
#undef NOWINMESSAGES // WM_*, EM_*, LB_*, CB_*
#undef NOWINSTYLES // WS_*, CS_*, ES_*, LBS_*, SBS_*, CBS_*
#undef NOSYSMETRICS // SM_*
#undef NOMENUS // MF_*
#undef NOICONS // IDI_*
#undef NOKEYSTATES // MK_*
#undef NOSYSCOMMANDS // SC_*
#undef NORASTEROPS // Binary and Tertiary raster ops
#undef NOSHOWWINDOW // SW_*
#undef OEMRESOURCE // OEM Resource values
#undef NOATOM // Atom Manager routines
#undef NOCLIPBOARD // Clipboard routines
#undef NOCOLOR // Screen colors
#undef NOCTLMGR // Control and Dialog routines
#undef NODRAWTEXT // DrawText() and DT_*
#undef NOGDI // All GDI defines and routines
#undef NOKERNEL // All KERNEL defines and routines
#undef NOUSER // All USER defines and routines
#undef NONLS // All NLS defines and routines
#undef NOMB // MB_* and MessageBox()
#undef NOMEMMGR // GMEM_*, LMEM_*, GHND, LHND, associated routines
#undef NOMETAFILE // typedef METAFILEPICT
#undef NOMINMAX // Macros min(a,b) and max(a,b)
#undef NOMSG // typedef MSG and associated routines
#undef NOOPENFILE // OpenFile(), OemToAnsi, AnsiToOem, and OF_*
#undef NOSCROLL // SB_* and scrolling routines
#undef NOSERVICE // All Service Controller routines, SERVICE_ equates, etc.
#undef NOSOUND // Sound driver routines
#undef NOTEXTMETRIC // typedef TEXTMETRIC and associated routines
#undef NOWH // SetWindowsHook and WH_*
#undef NOWINOFFSETS // GWL_*, GCL_*, associated routines
#undef NOCOMM // COMM driver routines
#undef NOKANJI // Kanji support stuff.
#undef NOHELP // Help engine interface.
#undef NOPROFILER // Profiler interface.
#undef NODEFERWINDOWPOS // DeferWindowPos routines
#undef NOMCX // Modem Configuration Extensions
#undef DrawText
#undef ShowCursor
#undef CloseWindow
#undef Rectangle
#undef HWND
#endif
// EOF

+ 22
- 0
src/platforms/rcore_desktop_sdl.c View File

@ -877,6 +877,28 @@ const char *GetClipboardText(void)
return buffer;
}
#if defined(SUPPORT_CLIPBOARD_IMAGE)
// Get clipboard image
Image GetClipboardImage(void)
{
Image image = {0};
// SDL_GetClipboardData function is available since SDL 3.1.3. (e.g. SDL3)
#if (defined(SDL_MAJOR_VERSION) && SDL_MAJOR_VERSION == 3 && defined(SDL_MAJOR_MINOR) && SDL_MAJOR_VERSION >= 1)
unsigned int dataSize = 0;
void* fileData = SDL_GetClipboardData("image/bmp", &dataSize); // returns NULL on failure;
if(fileData == NULL)
{
TRACELOG(LOG_WARNING, "Clipboard image: %s", SDL_GetError());
}
image = LoadImageFromMemory(".bmp", fileData, dataSize);
#endif
return image;
}
#endif
// Show mouse cursor
void ShowCursor(void)
{

+ 1
- 0
src/raylib.h View File

@ -1011,6 +1011,7 @@ RLAPI Vector2 GetWindowScaleDPI(void); // Get window
RLAPI const char *GetMonitorName(int monitor); // Get the human-readable, UTF-8 encoded name of the specified monitor
RLAPI void SetClipboardText(const char *text); // Set clipboard text content
RLAPI const char *GetClipboardText(void); // Get clipboard text content
RLAPI Image GetClipboardImage(void); // Get clipboard image
RLAPI void EnableEventWaiting(void); // Enable waiting for events on EndDrawing(), no automatic event polling
RLAPI void DisableEventWaiting(void); // Disable waiting for events on EndDrawing(), automatic events polling

+ 16
- 0
src/rcore.c View File

@ -512,13 +512,29 @@ const char *TextFormat(const char *text, ...); // Formatting of tex
#define PLATFORM_DESKTOP_GLFW
#endif
#if defined(SUPPORT_CLIPBOARD_IMAGE)
#if !defined(SUPPORT_FILEFORMAT_BMP) || !defined(STBI_REQUIRED) || !defined(SUPPORT_MODULE_RTEXTURES)
#error "To enabled SUPPORT_CLIPBOARD_IMAGE, it also needs SUPPORT_FILEFORMAT_BMP, SUPPORT_MODULE_RTEXTURES and STBI_REQUIRED to be defined"
#endif
#endif
// Include platform-specific submodules
#if defined(PLATFORM_DESKTOP_GLFW)
#include "platforms/rcore_desktop_glfw.c"
#if defined(SUPPORT_CLIPBOARD_IMAGE) && defined(_WIN32)
#include "platforms/rcore_clipboard_win32.c"
#endif
#elif defined(PLATFORM_DESKTOP_SDL)
#include "platforms/rcore_desktop_sdl.c"
#elif defined(PLATFORM_DESKTOP_RGFW)
#include "platforms/rcore_desktop_rgfw.c"
#if defined(SUPPORT_CLIPBOARD_IMAGE) && defined(_WIN32)
#define WINUSER_ALREADY_INCLUDED
#define WINBASE_ALREADY_INCLUDED
#define WINGDI_ALREADY_INCLUDED
#include "platforms/rcore_clipboard_win32.c"
#endif
#elif defined(PLATFORM_WEB)
#include "platforms/rcore_web.c"
#elif defined(PLATFORM_DRM)

Loading…
Cancel
Save