ManaPlus
copynpaste.cpp
Go to the documentation of this file.
1 /*
2  * The ManaPlus Client
3  * Copyright (C) 2001-2010 Wormux Team
4  * Copyright (C) 2011-2019 The ManaPlus Developers
5  * Copyright (C) 2019-2021 Andrei Karas
6  *
7  * This file is part of The ManaPlus Client.
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program. If not, see <http://www.gnu.org/licenses/>.
21  */
22 
23 /*
24  * IMPORTANT!
25  *
26  * This code was taken from Wormux svn trunk at Feb 25 2010. Please don't
27  * make any unnecessary modifications, and try to sync up modifications
28  * when possible.
29  */
30 
31 #ifdef _MSC_VER
32 # include "msvc/config.h"
33 #elif defined(HAVE_CONFIG_H)
34 #include "config.h"
35 #endif // _MSC_VER
36 
37 #include "utils/copynpaste.h"
38 
39 #include "debug.h"
40 
41 #ifdef USE_SDL2
42 PRAGMA48(GCC diagnostic push)
43 PRAGMA48(GCC diagnostic ignored "-Wshadow")
44 #include <SDL_clipboard.h>
45 PRAGMA48(GCC diagnostic pop)
46 
47 #else // USE_SDL2
48 
49 #if defined(__APPLE__)
50 #ifdef Status
51 #undef Status
52 #endif // Status
53 #include <Carbon/Carbon.h>
54 #elif defined USE_X11
55 #include "render/graphics.h"
56 
57 #include "utils/sdlhelper.h"
58 
59 PRAGMA48(GCC diagnostic push)
60 PRAGMA48(GCC diagnostic ignored "-Wshadow")
61 #include <SDL_syswm.h>
62 PRAGMA48(GCC diagnostic pop)
63 #include <unistd.h>
64 #elif defined __native_client__
65 #include "utils/naclmessages.h"
66 #elif defined WIN32
67 #include "utils/cast.h"
68 PRAGMA48(GCC diagnostic push)
69 PRAGMA48(GCC diagnostic ignored "-Wshadow")
70 #include <SDL_syswm.h>
71 PRAGMA48(GCC diagnostic pop)
72 #endif // defined(__APPLE__)
73 
74 #endif // USE_SDL2
75 
76 #ifdef USE_SDL2
77 bool retrieveBuffer(std::string& text, size_t& pos)
78 {
79  char *buf = SDL_GetClipboardText();
80  if (buf)
81  {
82  text.insert(pos, buf);
83  pos += strlen(buf);
84  SDL_free(buf);
85  return true;
86  }
87  return false;
88 }
89 
90 bool sendBuffer(const std::string &restrict text)
91 {
92  return !SDL_SetClipboardText(text.c_str());
93 }
94 
95 #else // USE_SDL2
96 
97 #ifdef WIN32
98 bool retrieveBuffer(std::string& text, size_t& pos)
99 {
100  bool ret = false;
101 
102  if (!OpenClipboard(nullptr))
103  return false;
104 
105  HANDLE h = GetClipboardData(CF_UNICODETEXT);
106  if (h)
107  {
108  LPCWSTR data = static_cast<LPCWSTR>(GlobalLock(h));
109 
110  if (data)
111  {
112  const size_t len = WideCharToMultiByte(CP_UTF8, 0, data, -1,
113  nullptr, 0, nullptr, nullptr);
114  if (len > 0)
115  {
116  // Convert from UTF-16 to UTF-8
117  void *temp = calloc(len, 1);
118  if (WideCharToMultiByte(CP_UTF8, 0, data, -1,
119  static_cast<LPSTR>(temp), len, nullptr, nullptr))
120  {
121  text.insert(pos, static_cast<char*>(temp));
122  pos += len - 1;
123  }
124  free(temp);
125  ret = true;
126  }
127  }
128  GlobalUnlock(h);
129  }
130  else
131  {
132  h = GetClipboardData(CF_TEXT);
133 
134  if (h)
135  {
136  const char *const data = static_cast<char*>(GlobalLock(h));
137  if (data)
138  {
139  text.insert(pos, data);
140  pos += strlen(data);
141  ret = true;
142  }
143  GlobalUnlock(h);
144  }
145  }
146 
147  CloseClipboard();
148  return ret;
149 }
150 
151 bool sendBuffer(const std::string &restrict text)
152 {
153  const int wCharsLen = MultiByteToWideChar(CP_UTF8,
154  0, text.c_str(), -1, nullptr, 0);
155  if (!wCharsLen)
156  return false;
157 
158  HANDLE h = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE,
159  CAST_SIZE(wCharsLen) * sizeof(WCHAR));
160  WCHAR *const out = static_cast<WCHAR*>(GlobalLock(h));
161 
162  MultiByteToWideChar(CP_UTF8, 0, text.c_str(), -1, out, wCharsLen);
163 
164  if (!OpenClipboard(nullptr))
165  {
166  GlobalUnlock(h);
167  GlobalFree(h);
168  return false;
169  }
170  GlobalUnlock(h);
171  EmptyClipboard();
172  if (!SetClipboardData(CF_UNICODETEXT, out))
173  {
174  GlobalFree(h);
175  CloseClipboard();
176  return false;
177  }
178  GlobalFree(h);
179  CloseClipboard();
180 
181  return true;
182 }
183 
184 #elif defined(__APPLE__)
185 
186 // Sorry for the very long code, all nicer OS X APIs are coded in
187 // Objective C and not C!
188 // Also it does very thorough error handling
189 bool getDataFromPasteboard(PasteboardRef inPasteboard,
190  char* flavorText /* out */,
191  const int bufSize)
192 {
193  ItemCount itemCount;
194  PasteboardSyncFlags syncFlags = PasteboardSynchronize(inPasteboard);
195  OSStatus err = PasteboardGetItemCount(inPasteboard, &itemCount);
196  require_noerr(err, CantGetPasteboardItemCount);
197 
198  for (UInt32 itemIndex = 1; itemIndex <= itemCount; itemIndex ++)
199  {
200  PasteboardItemID itemID;
201  CFArrayRef flavorTypeArray;
202  CFIndex flavorCount;
203 
204  err = PasteboardGetItemIdentifier(inPasteboard, itemIndex, &itemID);
205  require_noerr(err, CantGetPasteboardItemIdentifier);
206 
207  err = PasteboardCopyItemFlavors(inPasteboard,
208  itemID, &flavorTypeArray);
209  require_noerr(err, CantCopyPasteboardItemFlavors);
210 
211  flavorCount = CFArrayGetCount(flavorTypeArray);
212 
213  for (CFIndex flavorIndex = 0; flavorIndex < flavorCount;
214  flavorIndex ++)
215  {
216  CFStringRef flavorType = (CFStringRef)CFArrayGetValueAtIndex(
217  flavorTypeArray, flavorIndex);
218 
219  // we're only interested by text...
220  if (UTTypeConformsTo(flavorType, CFSTR("public.utf8-plain-text")))
221  {
222  CFDataRef flavorData;
223  err = PasteboardCopyItemFlavorData(inPasteboard, itemID,
224  flavorType, &flavorData);
225  require_noerr(err, CantCopyFlavorData);
226  CFIndex flavorDataSize = CFDataGetLength(flavorData);
227  flavorDataSize = (flavorDataSize<254) ? flavorDataSize : 254;
228 
229  if (flavorDataSize + 2 > bufSize)
230  {
231  fprintf(stderr,
232  "Cannot copy clipboard, contents is too big!\n");
233  return false;
234  }
235 
236  for (short dataIndex = 0; dataIndex <= flavorDataSize;
237  dataIndex ++)
238  {
239  signed char byte = *(CFDataGetBytePtr(
240  flavorData) + dataIndex);
241  flavorText[dataIndex] = byte;
242  }
243 
244  flavorText[flavorDataSize] = '\0';
245  flavorText[flavorDataSize + 1] = '\n';
246 
247  CFRelease(flavorData);
248  return true;
249  }
250 
251  continue;
252 CantCopyFlavorData:
253  fprintf(stderr, "Cannot copy clipboard, CantCopyFlavorData!\n");
254  }
255 
256  CFRelease(flavorTypeArray);
257  continue;
258 
259 CantCopyPasteboardItemFlavors:
260  fprintf(stderr,
261  "Cannot copy clipboard, CantCopyPasteboardItemFlavors!\n");
262  continue;
263 CantGetPasteboardItemIdentifier:
264  fprintf(stderr,
265  "Cannot copy clipboard, CantGetPasteboardItemIdentifier!\n");
266  continue;
267  }
268  fprintf(stderr,
269  "Cannot copy clipboard, found no acceptable flavour!\n");
270  return false;
271 
272 CantGetPasteboardItemCount:
273  fprintf(stderr, "Cannot copy clipboard, CantGetPasteboardItemCount!\n");
274  return false;
275 }
276 
277 bool getClipBoard(char* text /* out */, const int bufSize)
278 {
279  PasteboardRef theClipboard;
280  OSStatus err = PasteboardCreate(kPasteboardClipboard, &theClipboard);
281  require_noerr(err, PasteboardCreateFailed);
282 
283  if (!getDataFromPasteboard(theClipboard, text, bufSize))
284  {
285  fprintf(stderr,
286  "Cannot copy clipboard, getDataFromPasteboardFailed!\n");
287  return false;
288  }
289 
290  CFRelease(theClipboard);
291 
292  return true;
293 
294  // ---- error handling
295 PasteboardCreateFailed:
296  fprintf(stderr, "Cannot copy clipboard, PasteboardCreateFailed!\n");
297  CFRelease(theClipboard);
298  return false;
299 }
300 
301 bool retrieveBuffer(std::string& text, size_t& pos)
302 {
303  const int bufSize = 512;
304  char buffer[bufSize + 1];
305 
306  if (getClipBoard(buffer, bufSize))
307  {
308  text = buffer;
309  pos += strlen(buffer);
310  return true;
311  }
312  else
313  {
314  return false;
315  }
316 }
317 
318 bool sendBuffer(const std::string &restrict text)
319 {
320  return false;
321 }
322 
323 #elif defined USE_X11
324 
325 static char* getSelection2(Display *const dpy, Window us, Atom selection,
326  Atom request_target)
327 {
328  int max_events = 50;
329  Window owner = XGetSelectionOwner(dpy, selection);
330 
331  if (owner == None)
332  return nullptr;
333 
334  XConvertSelection(dpy, selection, request_target,
335  XA_PRIMARY, us, CurrentTime);
336  XFlush(dpy);
337 
338  while (max_events --)
339  {
340  XEvent e;
341 
342  XNextEvent(dpy, &e);
343  if (e.type == SelectionNotify)
344  {
345  if (e.xselection.property == None)
346  return nullptr;
347 
348  long unsigned len, left, dummy;
349  int format;
350  Atom type;
351  unsigned char *data = nullptr;
352 
353  int ret = XGetWindowProperty(dpy, us, e.xselection.property, 0, 0,
354  False, AnyPropertyType, &type, &format, &len, &left, &data);
355  if (left < 1)
356  {
357  if (ret == Success)
358  XFree(data);
359  return nullptr;
360  }
361 
362  ret = XGetWindowProperty(dpy, us, e.xselection.property, 0,
363  left, False, AnyPropertyType, &type, &format, &len,
364  &dummy, &data);
365 
366  if (ret != Success)
367  return nullptr;
368 
369  return reinterpret_cast<char*>(data);
370  }
371  }
372  return nullptr;
373 }
374 
375 static Atom requestAtom;
376 
377 static char* getSelection(Display *const dpy, Window us, Atom selection)
378 {
379  char *data = nullptr;
380  if (requestAtom != None)
381  data = getSelection2(dpy, us, selection, requestAtom);
382  if (!data)
383  data = getSelection2(dpy, us, selection, XA_STRING);
384  return data;
385 }
386 
387 bool retrieveBuffer(std::string& text, size_t& pos)
388 {
389  SDL_SysWMinfo info;
390 
391  SDL_VERSION(&info.version)
392  if (SDL::getWindowWMInfo(mainGraphics->getWindow(), &info))
393  {
394  Display *const dpy = info.info.x11.display;
395  Window us = info.info.x11.window;
396 
397  requestAtom = XInternAtom(dpy, "UTF8_STRING", true);
398  char *data = getSelection(dpy, us, XA_PRIMARY);
399  if (!data)
400  data = getSelection(dpy, us, XA_SECONDARY);
401  if (!data)
402  {
403  Atom XA_CLIPBOARD = XInternAtom(dpy, "CLIPBOARD", 0);
404  if (XA_CLIPBOARD != None)
405  data = getSelection(dpy, us, XA_CLIPBOARD);
406  }
407 
408  if (data)
409  {
410  // check cursor position
411  const size_t sz = text.size();
412  if (pos > sz)
413  pos = sz;
414 
415  text.insert(pos, data);
416  pos += strlen(data);
417  XFree(data);
418 
419  return true;
420  }
421  }
422  return false;
423 }
424 
425 static bool runxsel(const std::string &text, const char *p1,
426  const char *p2 = nullptr);
427 
428 bool sendBuffer(const std::string &restrict text)
429 {
430  runxsel(text, "-i");
431  runxsel(text, "-b", "-i");
432  return true;
433 }
434 
435 static bool runxsel(const std::string &text, const char *p1, const char *p2)
436 {
437  pid_t pid;
438  int fd[2];
439 
440  if (pipe(fd))
441  return false;
442 
443  if ((pid = fork()) == -1)
444  { // fork error
445  return false;
446  }
447  else if (!pid)
448  { // child
449  close(fd[1]);
450 
451  if (fd[0] != STDIN_FILENO)
452  {
453  if (dup2(fd[0], STDIN_FILENO) != STDIN_FILENO)
454  {
455  close(fd[0]);
456  _exit(1);
457  }
458  close(fd[0]);
459  }
460 
461  const char *const xselPath =
462 #if defined __OpenBSD__ || defined __FreeBSD__ || defined __DragonFly__
463  "/usr/local/bin/xsel";
464 #else // defined __OpenBSD__ || defined __FreeBSD__ || defined __DragonFly__
465  "/usr/bin/xsel";
466 #endif // defined __OpenBSD__ || defined __FreeBSD__ || defined __DragonFly__
467 
468  if (p2)
469  {
470  execl(xselPath, "xsel", p1, p2,
471  static_cast<char *>(nullptr));
472  }
473  else
474  {
475  execl(xselPath, "xsel", p1,
476  static_cast<char *>(nullptr));
477  }
478 
479  _exit(1);
480  }
481 
482  // parent
483  close(fd[0]);
484  const size_t len = text.length();
485  if (write(fd[1], text.c_str(), len) != static_cast<ssize_t>(len))
486  {
487  close(fd[1]);
488  return false;
489  }
490  close(fd[1]);
491  return true;
492 }
493 
494 #elif defined __native_client__
495 
496 bool retrieveBuffer(std::string& text, size_t& pos)
497 {
498  NaclMessageHandle *handle = naclRegisterMessageHandler("clipboard-paste");
499  naclPostMessage("clipboard-paste", "");
500  std::string response = naclWaitForMessage(handle);
501  text.insert(pos, response);
502  pos += response.size();
503  return true;
504 }
505 
506 bool sendBuffer(const std::string &restrict text)
507 {
508  naclPostMessage("clipboard-copy", text);
509  return true;
510 }
511 #else // WIN32
512 
513 bool retrieveBuffer(std::string &text A_UNUSED, size_t &pos A_UNUSED)
514 {
515  return false;
516 }
517 
518 bool sendBuffer(const std::string &restrict text A_UNUSED)
519 {
520  return false;
521 }
522 #endif // WIN32
523 #endif // USE_SDL2
#define CAST_SIZE
Definition: cast.h:34
Definition: window.h:102
bool sendBuffer(const std::string &text)
Definition: copynpaste.cpp:518
bool retrieveBuffer(std::string &text, size_t &pos)
Definition: copynpaste.cpp:513
Graphics * mainGraphics
Definition: graphics.cpp:109
const Image *restrict const left
if(!vert) return
uint8_t byte
Definition: itemflags.h:1
#define restrict
Definition: localconsts.h:165
#define PRAGMA48(str)
Definition: localconsts.h:199
#define A_UNUSED
Definition: localconsts.h:160
uint32_t data
bool info(InputEvent &event)
Definition: commands.cpp:57
Definition: sdlhelper.h:45
bool getWindowWMInfo(const SDL_Surface *const window, SDL_SysWMinfo *const info)
Definition: sdlhelper.cpp:112
int64_t write(File *const file, const void *const buffer, const uint32_t objSize, const uint32_t objCount)
Definition: fs.cpp:826
int close(File *const file)
Definition: fs.cpp:808