GCC Code Coverage Report
Directory: src/ Exec Total Coverage
File: src/utils/copynpaste.cpp Lines: 0 70 0.0 %
Date: 2021-03-17 Branches: 0 44 0.0 %

Line Branch Exec Source
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