/*
* 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 <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 // _MSC_VER
#include "utils/copynpaste.h"
#include "debug.h"
#ifdef USE_SDL2
PRAGMA48(GCC diagnostic push)
PRAGMA48(GCC diagnostic ignored "-Wshadow")
#include <SDL_clipboard.h>
PRAGMA48(GCC diagnostic pop)
#else // USE_SDL2
#if defined(__APPLE__)
#ifdef Status
#undef Status
#endif // Status
#include <Carbon/Carbon.h>
#elif defined USE_X11
#include "render/graphics.h"
#include "utils/sdlhelper.h"
PRAGMA48(GCC diagnostic push)
PRAGMA48(GCC diagnostic ignored "-Wshadow")
#include <SDL_syswm.h>
PRAGMA48(GCC diagnostic pop)
#include <unistd.h>
#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 <SDL_syswm.h>
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<LPCWSTR>(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<LPSTR>(temp), len, nullptr, nullptr))
{
text.insert(pos, static_cast<char*>(temp));
pos += len - 1;
}
free(temp);
ret = true;
}
}
GlobalUnlock(h);
}
else
{
h = GetClipboardData(CF_TEXT);
if (h)
{
const char *const data = static_cast<char*>(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<WCHAR*>(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<char*>(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<char *>(nullptr));
}
else
{
execl(xselPath, "xsel", p1,
static_cast<char *>(nullptr));
}
_exit(1);
}
// parent
close(fd[0]);
const size_t len = text.length();
if (write(fd[1], text.c_str(), len) != static_cast<ssize_t>(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