From da3b7c69b21cc1d8733fac7f3a011da905e53ca3 Mon Sep 17 00:00:00 2001 From: maiconpintoabreu Date: Fri, 27 Feb 2026 12:53:08 +0000 Subject: [PATCH 1/4] Testing linux implementation --- src/platforms/rcore_desktop_glfw.c | 37 ++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/src/platforms/rcore_desktop_glfw.c b/src/platforms/rcore_desktop_glfw.c index 5b62339e2e58..2a518004cfd6 100644 --- a/src/platforms/rcore_desktop_glfw.c +++ b/src/platforms/rcore_desktop_glfw.c @@ -1059,6 +1059,43 @@ Image GetClipboardImage(void) if (bmpData == NULL) TRACELOG(LOG_WARNING, "Clipboard image: Couldn't get clipboard data."); else image = LoadImageFromMemory(".bmp", (const unsigned char *)bmpData, (int)dataSize); +#elif defined(__linux__) + // Try to detect if we are on Wayland or X11 + const char *waylandDisplay = getenv("WAYLAND_DISPLAY"); + FILE *pipe = NULL; + + if (waylandDisplay != NULL) { + // Wayland: Use wl-paste (requires wl-clipboard package) + pipe = popen("wl-paste --type image/png", "r"); + } else { + // X11: Use xclip (requires xclip package) + pipe = popen("xclip -selection clipboard -t image/png -o", "r"); + } + + if (pipe != NULL) { + // Read the pipe into a dynamic buffer + unsigned char *buffer = NULL; + size_t size = 0; + size_t capacity = 1024 * 1024; // Start with 1MB + buffer = (unsigned char *)RL_MALLOC(capacity); + + while (!feof(pipe)) { + if (size + 1024 > capacity) { + capacity *= 2; + buffer = (unsigned char *)RL_REALLOC(buffer, capacity); + } + size += fread(buffer + size, 1, 1024, pipe); + } + + int exitCode = pclose(pipe); + + if (exitCode == 0 && size > 0) { + // Raylib's LoadImageFromMemory is smart enough to detect PNG data + image = LoadImageFromMemory(".png", buffer, (int)size); + } + + free(buffer); + } #else TRACELOG(LOG_WARNING, "GetClipboardImage() not implemented on target platform"); #endif // defined(_WIN32) From da5769c8b1472a7debcddb39f23e7a5035986f59 Mon Sep 17 00:00:00 2001 From: maiconpintoabreu Date: Fri, 27 Feb 2026 16:19:34 +0000 Subject: [PATCH 2/4] Add implementation for ClipboardImage on Linux --- src/platforms/rcore_desktop_glfw.c | 71 ++++++++++++++++-------------- src/platforms/rcore_desktop_rgfw.c | 41 +++++++++++++++++ 2 files changed, 79 insertions(+), 33 deletions(-) diff --git a/src/platforms/rcore_desktop_glfw.c b/src/platforms/rcore_desktop_glfw.c index 2a518004cfd6..5f2c2e5d89ac 100644 --- a/src/platforms/rcore_desktop_glfw.c +++ b/src/platforms/rcore_desktop_glfw.c @@ -1043,6 +1043,11 @@ const char *GetClipboardText(void) return glfwGetClipboardString(platform.handle); } +#if SUPPORT_CLIPBOARD_IMAGE && defined(__linux__) + #include + #include +#endif + // Get clipboard image Image GetClipboardImage(void) { @@ -1059,43 +1064,43 @@ Image GetClipboardImage(void) if (bmpData == NULL) TRACELOG(LOG_WARNING, "Clipboard image: Couldn't get clipboard data."); else image = LoadImageFromMemory(".bmp", (const unsigned char *)bmpData, (int)dataSize); + #elif defined(__linux__) - // Try to detect if we are on Wayland or X11 - const char *waylandDisplay = getenv("WAYLAND_DISPLAY"); - FILE *pipe = NULL; - - if (waylandDisplay != NULL) { - // Wayland: Use wl-paste (requires wl-clipboard package) - pipe = popen("wl-paste --type image/png", "r"); - } else { - // X11: Use xclip (requires xclip package) - pipe = popen("xclip -selection clipboard -t image/png -o", "r"); - } - - if (pipe != NULL) { - // Read the pipe into a dynamic buffer - unsigned char *buffer = NULL; - size_t size = 0; - size_t capacity = 1024 * 1024; // Start with 1MB - buffer = (unsigned char *)RL_MALLOC(capacity); - - while (!feof(pipe)) { - if (size + 1024 > capacity) { - capacity *= 2; - buffer = (unsigned char *)RL_REALLOC(buffer, capacity); - } - size += fread(buffer + size, 1, 1024, pipe); - } - - int exitCode = pclose(pipe); - if (exitCode == 0 && size > 0) { - // Raylib's LoadImageFromMemory is smart enough to detect PNG data - image = LoadImageFromMemory(".png", buffer, (int)size); - } + // Implementation based on https://github.com/ColleagueRiley/Clipboard-Copy-Paste/blob/main/x11.c + Display* dpy = XOpenDisplay(NULL); + if (!dpy) return image; + + Window root = DefaultRootWindow(dpy); + Window win = XCreateSimpleWindow(dpy, root, 0, 0, 1, 1, 0, 0, 0); - free(buffer); + Atom clipboard = XInternAtom(dpy, "CLIPBOARD", False); + Atom targetType = XInternAtom(dpy, "image/png", False); // Ask for PNG + Atom property = XInternAtom(dpy, "RAYLIB_CLIPBOARD_MANAGER", False); + + // Request the data: "Convert whatever is in CLIPBOARD to image/png and put it in RAYLIB_CLIPBOARD_MANAGER" + XConvertSelection(dpy, clipboard, targetType, property, win, CurrentTime); + + // Wait for the SelectionNotify event + XEvent ev; + XNextEvent(dpy, &ev); + + Atom actualType; + int actualFormat; + unsigned long nitems, bytesAfter; + unsigned char* data = NULL; + + // Read the data from our ghost window's property + XGetWindowProperty(dpy, win, property, 0, ~0L, False, AnyPropertyType, + &actualType, &actualFormat, &nitems, &bytesAfter, &data); + + if (data != NULL) { + image = LoadImageFromMemory(".png", data, (int)nitems); + XFree(data); } + + XDestroyWindow(dpy, win); + XCloseDisplay(dpy); #else TRACELOG(LOG_WARNING, "GetClipboardImage() not implemented on target platform"); #endif // defined(_WIN32) diff --git a/src/platforms/rcore_desktop_rgfw.c b/src/platforms/rcore_desktop_rgfw.c index ed471645c9f2..1a3fdab8b67f 100755 --- a/src/platforms/rcore_desktop_rgfw.c +++ b/src/platforms/rcore_desktop_rgfw.c @@ -999,6 +999,9 @@ const char *GetClipboardText(void) #define WINBASE_ALREADY_INCLUDED #define WINGDI_ALREADY_INCLUDED #include "../external/win32_clipboard.h" +#elif defined(__linux__) + #include + #include #endif #endif @@ -1006,6 +1009,7 @@ const char *GetClipboardText(void) Image GetClipboardImage(void) { Image image = { 0 }; + #if SUPPORT_CLIPBOARD_IMAGE && SUPPORT_MODULE_RTEXTURES #if defined(_WIN32) @@ -1018,6 +1022,43 @@ Image GetClipboardImage(void) if (fileData == NULL) TRACELOG(LOG_WARNING, "Clipboard image: Couldn't get clipboard data"); else image = LoadImageFromMemory(".bmp", (const unsigned char *)fileData, dataSize); + +#elif defined(__linux__) + + // Implementation based on https://github.com/ColleagueRiley/Clipboard-Copy-Paste/blob/main/x11.c + Display* dpy = XOpenDisplay(NULL); + if (!dpy) return image; + + Window root = DefaultRootWindow(dpy); + Window win = XCreateSimpleWindow(dpy, root, 0, 0, 1, 1, 0, 0, 0); + + Atom clipboard = XInternAtom(dpy, "CLIPBOARD", False); + Atom targetType = XInternAtom(dpy, "image/png", False); // Ask for PNG + Atom property = XInternAtom(dpy, "RAYLIB_CLIPBOARD_MANAGER", False); + + // Request the data: "Convert whatever is in CLIPBOARD to image/png and put it in RAYLIB_CLIPBOARD_MANAGER" + XConvertSelection(dpy, clipboard, targetType, property, win, CurrentTime); + + // Wait for the SelectionNotify event + XEvent ev; + XNextEvent(dpy, &ev); + + Atom actualType; + int actualFormat; + unsigned long nitems, bytesAfter; + unsigned char* data = NULL; + + // Read the data from our ghost window's property + XGetWindowProperty(dpy, win, property, 0, ~0L, False, AnyPropertyType, + &actualType, &actualFormat, &nitems, &bytesAfter, &data); + + if (data != NULL) { + image = LoadImageFromMemory(".png", data, (int)nitems); + XFree(data); + } + + XDestroyWindow(dpy, win); + XCloseDisplay(dpy); #else TRACELOG(LOG_WARNING, "Clipboard image: PLATFORM_DESKTOP_RGFW doesn't implement GetClipboardImage() for this OS"); #endif // defined(_WIN32) From 55dd966201721d1fc41d0f415ccce74f33fdf450 Mon Sep 17 00:00:00 2001 From: maiconpintoabreu Date: Fri, 27 Feb 2026 16:48:38 +0000 Subject: [PATCH 3/4] Adding another check to make sure that only X11 include X11 libs --- src/platforms/rcore_desktop_glfw.c | 4 ++-- src/platforms/rcore_desktop_rgfw.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/platforms/rcore_desktop_glfw.c b/src/platforms/rcore_desktop_glfw.c index 5f2c2e5d89ac..613d15686ea5 100644 --- a/src/platforms/rcore_desktop_glfw.c +++ b/src/platforms/rcore_desktop_glfw.c @@ -1043,7 +1043,7 @@ const char *GetClipboardText(void) return glfwGetClipboardString(platform.handle); } -#if SUPPORT_CLIPBOARD_IMAGE && defined(__linux__) +#if SUPPORT_CLIPBOARD_IMAGE && defined(__linux__) && defined(_GLFW_X11) #include #include #endif @@ -1065,7 +1065,7 @@ Image GetClipboardImage(void) if (bmpData == NULL) TRACELOG(LOG_WARNING, "Clipboard image: Couldn't get clipboard data."); else image = LoadImageFromMemory(".bmp", (const unsigned char *)bmpData, (int)dataSize); -#elif defined(__linux__) +#elif defined(__linux__) && defined(_GLFW_X11) // Implementation based on https://github.com/ColleagueRiley/Clipboard-Copy-Paste/blob/main/x11.c Display* dpy = XOpenDisplay(NULL); diff --git a/src/platforms/rcore_desktop_rgfw.c b/src/platforms/rcore_desktop_rgfw.c index 1a3fdab8b67f..cfdd4d51f194 100755 --- a/src/platforms/rcore_desktop_rgfw.c +++ b/src/platforms/rcore_desktop_rgfw.c @@ -999,7 +999,7 @@ const char *GetClipboardText(void) #define WINBASE_ALREADY_INCLUDED #define WINGDI_ALREADY_INCLUDED #include "../external/win32_clipboard.h" -#elif defined(__linux__) +#elif defined(__linux__) && defined(DRGFW_X11) #include #include #endif @@ -1023,7 +1023,7 @@ Image GetClipboardImage(void) if (fileData == NULL) TRACELOG(LOG_WARNING, "Clipboard image: Couldn't get clipboard data"); else image = LoadImageFromMemory(".bmp", (const unsigned char *)fileData, dataSize); -#elif defined(__linux__) +#elif defined(__linux__) && defined(DRGFW_X11) // Implementation based on https://github.com/ColleagueRiley/Clipboard-Copy-Paste/blob/main/x11.c Display* dpy = XOpenDisplay(NULL); From e49397e6cfbdfb57308b5e3b8d02c2263be7c132 Mon Sep 17 00:00:00 2001 From: maiconpintoabreu Date: Fri, 27 Feb 2026 17:28:37 +0000 Subject: [PATCH 4/4] Adding some comments to explain the magic numbers --- src/platforms/rcore_desktop_glfw.c | 10 +++++++++- src/platforms/rcore_desktop_rgfw.c | 10 +++++++++- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/src/platforms/rcore_desktop_glfw.c b/src/platforms/rcore_desktop_glfw.c index 613d15686ea5..57ac76ed9483 100644 --- a/src/platforms/rcore_desktop_glfw.c +++ b/src/platforms/rcore_desktop_glfw.c @@ -1072,7 +1072,15 @@ Image GetClipboardImage(void) if (!dpy) return image; Window root = DefaultRootWindow(dpy); - Window win = XCreateSimpleWindow(dpy, root, 0, 0, 1, 1, 0, 0, 0); + Window win = XCreateSimpleWindow( + dpy, // The connection to the X Server + root, // The 'Parent' window (usually the desktop/root) + 0, 0, // X and Y position on the screen + 1, 1, // Width and Height (1x1 pixel) + 0, // Border width + 0, // Border color + 0 // Background color + ); Atom clipboard = XInternAtom(dpy, "CLIPBOARD", False); Atom targetType = XInternAtom(dpy, "image/png", False); // Ask for PNG diff --git a/src/platforms/rcore_desktop_rgfw.c b/src/platforms/rcore_desktop_rgfw.c index cfdd4d51f194..651f1206a29e 100755 --- a/src/platforms/rcore_desktop_rgfw.c +++ b/src/platforms/rcore_desktop_rgfw.c @@ -1030,7 +1030,15 @@ Image GetClipboardImage(void) if (!dpy) return image; Window root = DefaultRootWindow(dpy); - Window win = XCreateSimpleWindow(dpy, root, 0, 0, 1, 1, 0, 0, 0); + Window win = XCreateSimpleWindow( + dpy, // The connection to the X Server + root, // The 'Parent' window (usually the desktop/root) + 0, 0, // X and Y position on the screen + 1, 1, // Width and Height (1x1 pixel) + 0, // Border width + 0, // Border color + 0 // Background color + ); Atom clipboard = XInternAtom(dpy, "CLIPBOARD", False); Atom targetType = XInternAtom(dpy, "image/png", False); // Ask for PNG