/* * The ManaPlus Client * Copyright (C) 2001-2010 Wormux Team * Copyright (C) 2011-2020 The ManaPlus Developers * Copyright (C) 2020-2023 The ManaVerse Developers * * This file is part of The ManaPlus Client. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ /* * IMPORTANT! * * This code was taken from Wormux svn trunk at Feb 25 2010. Please don't * make any unnecessary modifications, and try to sync up modifications * when possible. */ #ifdef _MSC_VER # include "msvc/config.h" #elif defined(HAVE_CONFIG_H) #include "config.h" #endif // _MSC_VER #include "utils/copynpaste.h" #include "debug.h" #ifdef USE_SDL2 PRAGMA48(GCC diagnostic push) PRAGMA48(GCC diagnostic ignored "-Wshadow") #include PRAGMA48(GCC diagnostic pop) #else // USE_SDL2 #if defined(__APPLE__) #ifdef Status #undef Status #endif // Status #include #elif defined USE_X11 #include "render/graphics.h" #include "utils/sdlhelper.h" PRAGMA48(GCC diagnostic push) PRAGMA48(GCC diagnostic ignored "-Wshadow") #include PRAGMA48(GCC diagnostic pop) #include #elif defined __native_client__ #include "utils/naclmessages.h" #elif defined WIN32 #include "utils/cast.h" PRAGMA48(GCC diagnostic push) PRAGMA48(GCC diagnostic ignored "-Wshadow") #include PRAGMA48(GCC diagnostic pop) #endif // defined(__APPLE__) #endif // USE_SDL2 #ifdef USE_SDL2 bool retrieveBuffer(std::string& text, size_t& pos) { char *buf = SDL_GetClipboardText(); if (buf) { text.insert(pos, buf); pos += strlen(buf); SDL_free(buf); return true; } return false; } bool sendBuffer(const std::string &restrict text) { return !SDL_SetClipboardText(text.c_str()); } #else // USE_SDL2 #ifdef WIN32 bool retrieveBuffer(std::string& text, size_t& pos) { bool ret = false; if (!OpenClipboard(nullptr)) return false; HANDLE h = GetClipboardData(CF_UNICODETEXT); if (h) { LPCWSTR data = static_cast(GlobalLock(h)); if (data) { const size_t len = WideCharToMultiByte(CP_UTF8, 0, data, -1, nullptr, 0, nullptr, nullptr); if (len > 0) { // Convert from UTF-16 to UTF-8 void *temp = calloc(len, 1); if (WideCharToMultiByte(CP_UTF8, 0, data, -1, static_cast(temp), len, nullptr, nullptr)) { text.insert(pos, static_cast(temp)); pos += len - 1; } free(temp); ret = true; } } GlobalUnlock(h); } else { h = GetClipboardData(CF_TEXT); if (h) { const char *const data = static_cast(GlobalLock(h)); if (data) { text.insert(pos, data); pos += strlen(data); ret = true; } GlobalUnlock(h); } } CloseClipboard(); return ret; } bool sendBuffer(const std::string &restrict text) { const int wCharsLen = MultiByteToWideChar(CP_UTF8, 0, text.c_str(), -1, nullptr, 0); if (!wCharsLen) return false; HANDLE h = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, CAST_SIZE(wCharsLen) * sizeof(WCHAR)); WCHAR *const out = static_cast(GlobalLock(h)); MultiByteToWideChar(CP_UTF8, 0, text.c_str(), -1, out, wCharsLen); if (!OpenClipboard(nullptr)) { GlobalUnlock(h); GlobalFree(h); return false; } GlobalUnlock(h); EmptyClipboard(); if (!SetClipboardData(CF_UNICODETEXT, out)) { GlobalFree(h); CloseClipboard(); return false; } GlobalFree(h); CloseClipboard(); return true; } #elif defined(__APPLE__) // Sorry for the very long code, all nicer OS X APIs are coded in // Objective C and not C! // Also it does very thorough error handling bool getDataFromPasteboard(PasteboardRef inPasteboard, char* flavorText /* out */, const int bufSize) { ItemCount itemCount; PasteboardSyncFlags syncFlags = PasteboardSynchronize(inPasteboard); OSStatus err = PasteboardGetItemCount(inPasteboard, &itemCount); require_noerr(err, CantGetPasteboardItemCount); for (UInt32 itemIndex = 1; itemIndex <= itemCount; itemIndex ++) { PasteboardItemID itemID; CFArrayRef flavorTypeArray; CFIndex flavorCount; err = PasteboardGetItemIdentifier(inPasteboard, itemIndex, &itemID); require_noerr(err, CantGetPasteboardItemIdentifier); err = PasteboardCopyItemFlavors(inPasteboard, itemID, &flavorTypeArray); require_noerr(err, CantCopyPasteboardItemFlavors); flavorCount = CFArrayGetCount(flavorTypeArray); for (CFIndex flavorIndex = 0; flavorIndex < flavorCount; flavorIndex ++) { CFStringRef flavorType = (CFStringRef)CFArrayGetValueAtIndex( flavorTypeArray, flavorIndex); // we're only interested by text... if (UTTypeConformsTo(flavorType, CFSTR("public.utf8-plain-text"))) { CFDataRef flavorData; err = PasteboardCopyItemFlavorData(inPasteboard, itemID, flavorType, &flavorData); require_noerr(err, CantCopyFlavorData); CFIndex flavorDataSize = CFDataGetLength(flavorData); flavorDataSize = (flavorDataSize<254) ? flavorDataSize : 254; if (flavorDataSize + 2 > bufSize) { fprintf(stderr, "Cannot copy clipboard, contents is too big!\n"); return false; } for (short dataIndex = 0; dataIndex <= flavorDataSize; dataIndex ++) { signed char byte = *(CFDataGetBytePtr( flavorData) + dataIndex); flavorText[dataIndex] = byte; } flavorText[flavorDataSize] = '\0'; flavorText[flavorDataSize + 1] = '\n'; CFRelease(flavorData); return true; } continue; CantCopyFlavorData: fprintf(stderr, "Cannot copy clipboard, CantCopyFlavorData!\n"); } CFRelease(flavorTypeArray); continue; CantCopyPasteboardItemFlavors: fprintf(stderr, "Cannot copy clipboard, CantCopyPasteboardItemFlavors!\n"); continue; CantGetPasteboardItemIdentifier: fprintf(stderr, "Cannot copy clipboard, CantGetPasteboardItemIdentifier!\n"); continue; } fprintf(stderr, "Cannot copy clipboard, found no acceptable flavour!\n"); return false; CantGetPasteboardItemCount: fprintf(stderr, "Cannot copy clipboard, CantGetPasteboardItemCount!\n"); return false; } bool getClipBoard(char* text /* out */, const int bufSize) { PasteboardRef theClipboard; OSStatus err = PasteboardCreate(kPasteboardClipboard, &theClipboard); require_noerr(err, PasteboardCreateFailed); if (!getDataFromPasteboard(theClipboard, text, bufSize)) { fprintf(stderr, "Cannot copy clipboard, getDataFromPasteboardFailed!\n"); return false; } CFRelease(theClipboard); return true; // ---- error handling PasteboardCreateFailed: fprintf(stderr, "Cannot copy clipboard, PasteboardCreateFailed!\n"); CFRelease(theClipboard); return false; } bool retrieveBuffer(std::string& text, size_t& pos) { const int bufSize = 512; char buffer[bufSize + 1]; if (getClipBoard(buffer, bufSize)) { text = buffer; pos += strlen(buffer); return true; } else { return false; } } bool sendBuffer(const std::string &restrict text) { return false; } #elif defined USE_X11 static char* getSelection2(Display *const dpy, Window us, Atom selection, Atom request_target) { int max_events = 50; Window owner = XGetSelectionOwner(dpy, selection); if (owner == None) return nullptr; XConvertSelection(dpy, selection, request_target, XA_PRIMARY, us, CurrentTime); XFlush(dpy); while (max_events --) { XEvent e; XNextEvent(dpy, &e); if (e.type == SelectionNotify) { if (e.xselection.property == None) return nullptr; long unsigned len, left, dummy; int format; Atom type; unsigned char *data = nullptr; int ret = XGetWindowProperty(dpy, us, e.xselection.property, 0, 0, False, AnyPropertyType, &type, &format, &len, &left, &data); if (left < 1) { if (ret == Success) XFree(data); return nullptr; } ret = XGetWindowProperty(dpy, us, e.xselection.property, 0, left, False, AnyPropertyType, &type, &format, &len, &dummy, &data); if (ret != Success) return nullptr; return reinterpret_cast(data); } } return nullptr; } static Atom requestAtom; static char* getSelection(Display *const dpy, Window us, Atom selection) { char *data = nullptr; if (requestAtom != None) data = getSelection2(dpy, us, selection, requestAtom); if (!data) data = getSelection2(dpy, us, selection, XA_STRING); return data; } bool retrieveBuffer(std::string& text, size_t& pos) { SDL_SysWMinfo info; SDL_VERSION(&info.version) if (SDL::getWindowWMInfo(mainGraphics->getWindow(), &info)) { Display *const dpy = info.info.x11.display; Window us = info.info.x11.window; requestAtom = XInternAtom(dpy, "UTF8_STRING", true); char *data = getSelection(dpy, us, XA_PRIMARY); if (!data) data = getSelection(dpy, us, XA_SECONDARY); if (!data) { Atom XA_CLIPBOARD = XInternAtom(dpy, "CLIPBOARD", 0); if (XA_CLIPBOARD != None) data = getSelection(dpy, us, XA_CLIPBOARD); } if (data) { // check cursor position const size_t sz = text.size(); if (pos > sz) pos = sz; text.insert(pos, data); pos += strlen(data); XFree(data); return true; } } return false; } static bool runxsel(const std::string &text, const char *p1, const char *p2 = nullptr); bool sendBuffer(const std::string &restrict text) { runxsel(text, "-i"); runxsel(text, "-b", "-i"); return true; } static bool runxsel(const std::string &text, const char *p1, const char *p2) { pid_t pid; int fd[2]; if (pipe(fd)) return false; if ((pid = fork()) == -1) { // fork error return false; } else if (!pid) { // child close(fd[1]); if (fd[0] != STDIN_FILENO) { if (dup2(fd[0], STDIN_FILENO) != STDIN_FILENO) { close(fd[0]); _exit(1); } close(fd[0]); } const char *const xselPath = #if defined __OpenBSD__ || defined __FreeBSD__ || defined __DragonFly__ "/usr/local/bin/xsel"; #else // defined __OpenBSD__ || defined __FreeBSD__ || defined __DragonFly__ "/usr/bin/xsel"; #endif // defined __OpenBSD__ || defined __FreeBSD__ || defined __DragonFly__ if (p2) { execl(xselPath, "xsel", p1, p2, static_cast(nullptr)); } else { execl(xselPath, "xsel", p1, static_cast(nullptr)); } _exit(1); } // parent close(fd[0]); const size_t len = text.length(); if (write(fd[1], text.c_str(), len) != static_cast(len)) { close(fd[1]); return false; } close(fd[1]); return true; } #elif defined __native_client__ bool retrieveBuffer(std::string& text, size_t& pos) { NaclMessageHandle *handle = naclRegisterMessageHandler("clipboard-paste"); naclPostMessage("clipboard-paste", ""); std::string response = naclWaitForMessage(handle); text.insert(pos, response); pos += response.size(); return true; } bool sendBuffer(const std::string &restrict text) { naclPostMessage("clipboard-copy", text); return true; } #else // WIN32 bool retrieveBuffer(std::string &text A_UNUSED, size_t &pos A_UNUSED) { return false; } bool sendBuffer(const std::string &restrict text A_UNUSED) { return false; } #endif // WIN32 #endif // USE_SDL2