/* * Retrieve string pasted depending on OS mechanisms. * Copyright (C) 2001-2010 Wormux Team * Copyright (C) 2011-2012 The ManaPlus 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 <http://www.gnu.org/licenses/>. */ /* * 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 #include <SDL_syswm.h> #include "copynpaste.h" #include "debug.h" #ifdef WIN32 bool retrieveBuffer(std::string& text, std::string::size_type& pos) { bool ret = false; if (!OpenClipboard(nullptr)) return false; HANDLE h = GetClipboardData(CF_UNICODETEXT); if (h) { LPCWSTR data = (LPCWSTR)GlobalLock(h); if (data) { int 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, (LPSTR)temp, len, nullptr, nullptr)) { text.insert(pos, (char*)temp); pos += len-1; } free(temp); ret = true; } } GlobalUnlock(h); } else { h = GetClipboardData(CF_TEXT); if (h) { const char *data = (char*)GlobalLock(h); if (data) { text.insert(pos, data); pos += strlen(data); ret = true; } GlobalUnlock(h); } } CloseClipboard(); return ret; } bool sendBuffer(std::string& text) { int wCharsLen = MultiByteToWideChar(CP_UTF8, 0, text.c_str(), -1, nullptr, 0); if (!wCharsLen) return false; HANDLE h; WCHAR *out; h = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, wCharsLen * sizeof(WCHAR)); out = (WCHAR*)GlobalLock(h); MultiByteToWideChar(CP_UTF8, 0, text.c_str(), -1, out, wCharsLen); if (!OpenClipboard(0)) { 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__) #ifdef Status #undef Status #endif #include <Carbon/Carbon.h> // 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) { OSStatus err = noErr; PasteboardSyncFlags syncFlags; ItemCount itemCount; syncFlags = PasteboardSynchronize(inPasteboard); //require_action( syncFlags & kPasteboardModified, PasteboardOutOfSync, // err = badPasteboardSyncErr ); 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; CFDataRef flavorData; CFIndex flavorDataSize; flavorType = (CFStringRef)CFArrayGetValueAtIndex( flavorTypeArray, flavorIndex); // we're only interested by text... if (UTTypeConformsTo(flavorType, CFSTR("public.utf8-plain-text"))) { err = PasteboardCopyItemFlavorData(inPasteboard, itemID, flavorType, &flavorData); require_noerr(err, CantCopyFlavorData); 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 ++) { 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; //PasteboardOutOfSync: // fprintf(stderr, "Cannot copy clipboard, PasteboardOutOfSync!\n"); // return false; } bool getClipBoard(char* text /* out */, const int bufSize) { OSStatus err = noErr; PasteboardRef theClipboard; 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, std::string::size_type& 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(std::string& text) { return false; } #elif USE_X11 #include <unistd.h> static char* getSelection2(Display *dpy, Window us, Atom selection, Atom request_target) { int max_events = 50; Window owner = XGetSelectionOwner(dpy, selection); int ret; //printf("XConvertSelection on %s\n", XGetAtomName(dpy, selection)); if (owner == None) { //printf("No owner\n"); 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) { // printf("Received %s, %s, %s\n", XGetAtomName(dpy, selection), // XGetAtomName(dpy, e.xselection.selection), // XGetAtomName(dpy, request_target)); if (e.xselection.property == None) { //printf("Couldn't convert\n"); return nullptr; } long unsigned len, left, dummy; int format; Atom type; unsigned char *data = nullptr; 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) { //printf("Failed to get property: %p on %lu\n", data, len); return nullptr; } //printf(">>> Got %s: len=%lu left=%lu (event %i)\n", data, // len, left, 50-max_events); return reinterpret_cast<char*>(data); } } return nullptr; } static Atom requestAtom; static char* getSelection(Display *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, std::string::size_type& pos) { SDL_SysWMinfo info; //printf("Retrieving buffer...\n"); SDL_VERSION(&info.version); if (SDL_GetWMInfo(&info)) { Display *dpy = info.info.x11.display; Window us = info.info.x11.window; char *data = nullptr; requestAtom = XInternAtom (dpy, "UTF8_STRING", true); if (!data) 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 if (pos > text.size()) pos = text.size(); text.insert(pos, data); pos += strlen(data); XFree(data); return true; } } return false; } bool runxsel(std::string& text, const char *p1, const char *p2 = nullptr); bool sendBuffer(std::string& text) { runxsel(text, "-i"); runxsel(text, "-b", "-i"); return true; } bool runxsel(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]); } if (p2) { execl("/usr/bin/xsel", "xsel", p1, p2, static_cast<char *>(nullptr)); } else { execl("/usr/bin/xsel", "xsel", p1, static_cast<char *>(nullptr)); } exit(1); } // parent close(fd[0]); const int len = strlen(text.c_str()); if (write(fd[1], text.c_str(), len) != len) { close(fd[1]); return false; } close(fd[1]); return true; } #else bool retrieveBuffer(std::string&, std::string::size_type&) { return false; } bool sendBuffer(std::string& text) { return false; } #endif