~oif-team/ubuntu/natty/qt4-x11/xi2.1

« back to all changes in this revision

Viewing changes to src/gui/kernel/qclipboard_x11.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Adam Conrad
  • Date: 2005-08-24 04:09:09 UTC
  • Revision ID: james.westby@ubuntu.com-20050824040909-xmxe9jfr4a0w5671
Tags: upstream-4.0.0
ImportĀ upstreamĀ versionĀ 4.0.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/****************************************************************************
 
2
**
 
3
** Copyright (C) 1992-2005 Trolltech AS. All rights reserved.
 
4
**
 
5
** This file is part of the gui module of the Qt Toolkit.
 
6
**
 
7
** This file may be distributed under the terms of the Q Public License
 
8
** as defined by Trolltech AS of Norway and appearing in the file
 
9
** LICENSE.QPL included in the packaging of this file.
 
10
**
 
11
** This file may be distributed and/or modified under the terms of the
 
12
** GNU General Public License version 2 as published by the Free Software
 
13
** Foundation and appearing in the file LICENSE.GPL included in the
 
14
** packaging of this file.
 
15
**
 
16
** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for
 
17
**   information about Qt Commercial License Agreements.
 
18
** See http://www.trolltech.com/qpl/ for QPL licensing information.
 
19
** See http://www.trolltech.com/gpl/ for GPL licensing information.
 
20
**
 
21
** Contact info@trolltech.com if any conditions of this licensing are
 
22
** not clear to you.
 
23
**
 
24
** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
 
25
** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 
26
**
 
27
****************************************************************************/
 
28
 
 
29
// #define QCLIPBOARD_DEBUG
 
30
// #define QCLIPBOARD_DEBUG_VERBOSE
 
31
 
 
32
#ifdef QCLIPBOARD_DEBUG
 
33
#  define DEBUG qDebug
 
34
#else
 
35
#  define DEBUG if (false) qDebug
 
36
#endif
 
37
 
 
38
#ifdef QCLIPBOARD_DEBUG_VERBOSE
 
39
#  define VDEBUG qDebug
 
40
#else
 
41
#  define VDEBUG if (false) qDebug
 
42
#endif
 
43
 
 
44
#include "qplatformdefs.h"
 
45
 
 
46
#include "qclipboard.h"
 
47
 
 
48
#ifndef QT_NO_CLIPBOARD
 
49
 
 
50
#include "qabstracteventdispatcher.h"
 
51
#include "qapplication.h"
 
52
#include "qdesktopwidget.h"
 
53
#include "qbitmap.h"
 
54
#include "qdatetime.h"
 
55
#include "qiodevice.h"
 
56
#include "qbuffer.h"
 
57
#include "qtextcodec.h"
 
58
#include "qlist.h"
 
59
#include "qmap.h"
 
60
#include "qapplication_p.h"
 
61
#include "qevent.h"
 
62
#include "qt_x11_p.h"
 
63
#include "qx11info_x11.h"
 
64
#include "qimagewriter.h"
 
65
#include "qvariant.h"
 
66
#include "qdnd_p.h"
 
67
 
 
68
/*****************************************************************************
 
69
  Internal QClipboard functions for X11.
 
70
 *****************************************************************************/
 
71
 
 
72
static int clipboard_timeout = 5000; // 5s timeout on clipboard operations
 
73
 
 
74
static QWidget * owner = 0;
 
75
static QWidget *requestor = 0;
 
76
static bool timer_event_clear = false;
 
77
static int timer_id = 0;
 
78
 
 
79
static int pending_timer_id = 0;
 
80
static bool pending_clipboard_changed = false;
 
81
static bool pending_selection_changed = false;
 
82
 
 
83
 
 
84
// event capture mechanism for qt_xclb_wait_for_event
 
85
static bool waiting_for_data = false;
 
86
static bool has_captured_event = false;
 
87
static Window capture_event_win = XNone;
 
88
static int capture_event_type = -1;
 
89
static XEvent captured_event;
 
90
 
 
91
class QClipboardWatcher; // forward decl
 
92
static QClipboardWatcher *selection_watcher = 0;
 
93
static QClipboardWatcher *clipboard_watcher = 0;
 
94
 
 
95
static void cleanup()
 
96
{
 
97
    delete owner;
 
98
    delete requestor;
 
99
    owner = 0;
 
100
    requestor = 0;
 
101
}
 
102
 
 
103
static
 
104
void setupOwner()
 
105
{
 
106
    if (owner)
 
107
        return;
 
108
    owner = new QWidget(0);
 
109
    owner->setObjectName("internal clipboard owner");
 
110
    requestor = new QWidget(0);
 
111
    requestor->setObjectName("internal clipboard requestor");
 
112
    qAddPostRoutine(cleanup);
 
113
}
 
114
 
 
115
static
 
116
int sizeof_format(int format)
 
117
{
 
118
    int sz;
 
119
    switch (format) {
 
120
    default:
 
121
    case  8: sz = sizeof(char); break;
 
122
    case 16: sz = sizeof(short); break;
 
123
    case 32: sz = sizeof(long); break;
 
124
    }
 
125
    return sz;
 
126
}
 
127
 
 
128
class QClipboardWatcher : public QInternalMimeData {
 
129
public:
 
130
    QClipboardWatcher(QClipboard::Mode mode);
 
131
    ~QClipboardWatcher();
 
132
    bool empty() const;
 
133
    virtual bool hasFormat_sys(const QString &mimetype) const;
 
134
    virtual QStringList formats_sys() const;
 
135
 
 
136
    QVariant retrieveData_sys(const QString &mimetype, QVariant::Type type) const;
 
137
    QByteArray getDataInFormat(Atom fmtatom) const;
 
138
 
 
139
    Atom atom;
 
140
    mutable QStringList formatList;
 
141
    mutable QByteArray format_atoms;
 
142
};
 
143
 
 
144
 
 
145
 
 
146
class QClipboardData
 
147
{
 
148
public:
 
149
    QClipboardData();
 
150
    ~QClipboardData();
 
151
 
 
152
    void setSource(QMimeData* s)
 
153
    {
 
154
        delete src;
 
155
        src = s;
 
156
    }
 
157
 
 
158
    QMimeData *source() const { return src; }
 
159
 
 
160
    void addTransferredPixmap(QPixmap pm)
 
161
    {
 
162
        /* TODO: queue them */
 
163
        transferred[tindex] = pm;
 
164
        tindex=(tindex+1)%2;
 
165
    }
 
166
    void clearTransfers()
 
167
    {
 
168
        transferred[0] = QPixmap();
 
169
        transferred[1] = QPixmap();
 
170
    }
 
171
 
 
172
    void clear();
 
173
 
 
174
    QMimeData *src;
 
175
    Time timestamp;
 
176
 
 
177
    QPixmap transferred[2];
 
178
    int tindex;
 
179
};
 
180
 
 
181
QClipboardData::QClipboardData()
 
182
{
 
183
    src = 0;
 
184
    timestamp = CurrentTime;
 
185
    tindex=0;
 
186
}
 
187
 
 
188
QClipboardData::~QClipboardData()
 
189
{ clear(); }
 
190
 
 
191
void QClipboardData::clear()
 
192
{
 
193
    delete src;
 
194
    src = 0;
 
195
    timestamp = CurrentTime;
 
196
}
 
197
 
 
198
 
 
199
static QClipboardData *internalCbData = 0;
 
200
static QClipboardData *internalSelData = 0;
 
201
 
 
202
static void cleanupClipboardData()
 
203
{
 
204
    delete internalCbData;
 
205
    internalCbData = 0;
 
206
}
 
207
 
 
208
static QClipboardData *clipboardData()
 
209
{
 
210
    if (internalCbData == 0) {
 
211
        internalCbData = new QClipboardData;
 
212
        qAddPostRoutine(cleanupClipboardData);
 
213
    }
 
214
    return internalCbData;
 
215
}
 
216
 
 
217
static void cleanupSelectionData()
 
218
{
 
219
    delete internalSelData;
 
220
    internalSelData = 0;
 
221
}
 
222
 
 
223
static QClipboardData *selectionData()
 
224
{
 
225
    if (internalSelData == 0) {
 
226
        internalSelData = new QClipboardData;
 
227
        qAddPostRoutine(cleanupSelectionData);
 
228
    }
 
229
    return internalSelData;
 
230
}
 
231
 
 
232
class QClipboardINCRTransaction
 
233
{
 
234
public:
 
235
    QClipboardINCRTransaction(Window w, Atom p, Atom t, int f, QByteArray d, unsigned int i);
 
236
    ~QClipboardINCRTransaction(void);
 
237
 
 
238
    int x11Event(XEvent *event);
 
239
 
 
240
    Window window;
 
241
    Atom property, target;
 
242
    int format;
 
243
    QByteArray data;
 
244
    unsigned int increment;
 
245
    unsigned int offset;
 
246
};
 
247
 
 
248
typedef QMap<Window,QClipboardINCRTransaction*> TransactionMap;
 
249
static TransactionMap *transactions = 0;
 
250
static QApplication::EventFilter prev_event_filter = 0;
 
251
static int incr_timer_id = 0;
 
252
 
 
253
static bool qt_x11_incr_event_filter(void *message, long *result)
 
254
{
 
255
    XEvent *event = reinterpret_cast<XEvent *>(message);
 
256
    TransactionMap::Iterator it = transactions->find(event->xany.window);
 
257
    if (it != transactions->end()) {
 
258
        if ((*it)->x11Event(event) != 0)
 
259
            return true;
 
260
    }
 
261
    if (prev_event_filter)
 
262
        return prev_event_filter(event, result);
 
263
    return false;
 
264
}
 
265
 
 
266
/*
 
267
  called when no INCR activity has happened for 'clipboard_timeout'
 
268
  milliseconds... we assume that all unfinished transactions have
 
269
  timed out and remove everything from the transaction map
 
270
*/
 
271
static void qt_xclb_incr_timeout(void)
 
272
{
 
273
    qWarning("QClipboard: timed out while sending data");
 
274
 
 
275
    while (transactions)
 
276
        delete *transactions->begin();
 
277
}
 
278
 
 
279
QClipboardINCRTransaction::QClipboardINCRTransaction(Window w, Atom p, Atom t, int f,
 
280
                                                     QByteArray d, unsigned int i)
 
281
    : window(w), property(p), target(t), format(f), data(d), increment(i), offset(0u)
 
282
{
 
283
    DEBUG("QClipboard: sending %d bytes (INCR transaction %p)", d.size(), this);
 
284
 
 
285
    XSelectInput(X11->display, window, PropertyChangeMask);
 
286
 
 
287
    if (! transactions) {
 
288
        VDEBUG("QClipboard: created INCR transaction map");
 
289
        transactions = new TransactionMap;
 
290
        prev_event_filter = qApp->setEventFilter(qt_x11_incr_event_filter);
 
291
        incr_timer_id = QApplication::clipboard()->startTimer(clipboard_timeout);
 
292
    }
 
293
    transactions->insert(window, this);
 
294
}
 
295
 
 
296
QClipboardINCRTransaction::~QClipboardINCRTransaction(void)
 
297
{
 
298
    VDEBUG("QClipboard: destroyed INCR transacton %p", this);
 
299
 
 
300
    XSelectInput(X11->display, window, NoEventMask);
 
301
 
 
302
    transactions->remove(window);
 
303
    if (transactions->isEmpty()) {
 
304
        VDEBUG("QClipboard: no more INCR transactions");
 
305
        delete transactions;
 
306
        transactions = 0;
 
307
 
 
308
        (void)qApp->setEventFilter(prev_event_filter);
 
309
 
 
310
        if (incr_timer_id != 0) {
 
311
            QApplication::clipboard()->killTimer(incr_timer_id);
 
312
            incr_timer_id = 0;
 
313
        }
 
314
    }
 
315
}
 
316
 
 
317
int QClipboardINCRTransaction::x11Event(XEvent *event)
 
318
{
 
319
    if (event->type != PropertyNotify
 
320
        || (event->xproperty.state != PropertyDelete
 
321
            || event->xproperty.atom != property))
 
322
        return 0;
 
323
 
 
324
    // restart the INCR timer
 
325
    if (incr_timer_id) QApplication::clipboard()->killTimer(incr_timer_id);
 
326
    incr_timer_id = QApplication::clipboard()->startTimer(clipboard_timeout);
 
327
 
 
328
    unsigned int bytes_left = data.size() - offset;
 
329
    if (bytes_left > 0) {
 
330
        unsigned int xfer = qMin(increment, bytes_left);
 
331
        VDEBUG("QClipboard: sending %d bytes, %d remaining (INCR transaction %p)",
 
332
               xfer, bytes_left - xfer, this);
 
333
 
 
334
        XChangeProperty(X11->display, window, property, target, format,
 
335
                        PropModeReplace, (uchar *) data.data() + offset, xfer);
 
336
        offset += xfer;
 
337
    } else {
 
338
        // INCR transaction finished...
 
339
        XChangeProperty(X11->display, window, property, target, format,
 
340
                        PropModeReplace, (uchar *) data.data(), 0);
 
341
        delete this;
 
342
    }
 
343
 
 
344
    return 1;
 
345
}
 
346
 
 
347
 
 
348
/*****************************************************************************
 
349
  QClipboard member functions for X11.
 
350
 *****************************************************************************/
 
351
 
 
352
 
 
353
void QClipboard::clear(Mode mode)
 
354
{
 
355
    setMimeData(0, mode);
 
356
}
 
357
 
 
358
 
 
359
/*!
 
360
    Returns true if the clipboard supports mouse selection; otherwise
 
361
    returns false.
 
362
*/
 
363
bool QClipboard::supportsSelection() const
 
364
{
 
365
    return true;
 
366
}
 
367
 
 
368
 
 
369
/*!
 
370
    Returns true if this clipboard object owns the mouse selection
 
371
    data; otherwise returns false.
 
372
*/
 
373
bool QClipboard::ownsSelection() const
 
374
{ return selectionData()->timestamp != CurrentTime; }
 
375
 
 
376
/*!
 
377
    Returns true if this clipboard object owns the clipboard data;
 
378
    otherwise returns false.
 
379
*/
 
380
bool QClipboard::ownsClipboard() const
 
381
{ return clipboardData()->timestamp != CurrentTime; }
 
382
 
 
383
 
 
384
// event filter function... captures interesting events while
 
385
// qt_xclb_wait_for_event is running the event loop
 
386
static bool qt_x11_clipboard_event_filter(void *message, long *)
 
387
{
 
388
    XEvent *event = reinterpret_cast<XEvent *>(message);
 
389
    if (event->xany.type == capture_event_type &&
 
390
        event->xany.window == capture_event_win) {
 
391
        VDEBUG("QClipboard: event_filter(): caught event type %d", event->type);
 
392
        has_captured_event = true;
 
393
        captured_event = *event;
 
394
        return true;
 
395
    }
 
396
    return false;
 
397
}
 
398
 
 
399
bool QX11Data::clipboardWaitForEvent(Window win, int type, XEvent *event, int timeout)
 
400
{
 
401
    QTime started = QTime::currentTime();
 
402
    QTime now = started;
 
403
 
 
404
    if (QAbstractEventDispatcher::instance()->inherits("QMotif")) {
 
405
        if (waiting_for_data)
 
406
            qFatal("QClipboard: internal error, qt_xclb_wait_for_event recursed");
 
407
        waiting_for_data = true;
 
408
 
 
409
 
 
410
        has_captured_event = false;
 
411
        capture_event_win = win;
 
412
        capture_event_type = type;
 
413
 
 
414
        QApplication::EventFilter old_event_filter =
 
415
            qApp->setEventFilter(qt_x11_clipboard_event_filter);
 
416
 
 
417
        do {
 
418
            if (XCheckTypedWindowEvent(display, win, type, event)) {
 
419
                waiting_for_data = false;
 
420
                qApp->setEventFilter(old_event_filter);
 
421
                return true;
 
422
            }
 
423
 
 
424
            XSync(X11->display, false);
 
425
            usleep(50000);
 
426
 
 
427
            now = QTime::currentTime();
 
428
            if (started > now)                        // crossed midnight
 
429
                started = now;
 
430
 
 
431
            // 0x08 == ExcludeTimers for X11 only
 
432
            QEventLoop::ProcessEventsFlags flags(QEventLoop::ExcludeUserInputEvents
 
433
                                                 | QEventLoop::ExcludeSocketNotifiers
 
434
                                                 | QEventLoop::WaitForMoreEvents
 
435
                                                 | 0x08);
 
436
            QAbstractEventDispatcher *eventDispatcher = QAbstractEventDispatcher::instance();
 
437
            eventDispatcher->processEvents(flags);
 
438
 
 
439
            if (has_captured_event) {
 
440
                waiting_for_data = false;
 
441
                *event = captured_event;
 
442
                qApp->setEventFilter(old_event_filter);
 
443
                return true;
 
444
            }
 
445
        } while (started.msecsTo(now) < timeout);
 
446
 
 
447
        waiting_for_data = false;
 
448
        qApp->setEventFilter(old_event_filter);
 
449
    } else {
 
450
        do {
 
451
            if (XCheckTypedWindowEvent(X11->display,win,type,event))
 
452
                return true;
 
453
 
 
454
            now = QTime::currentTime();
 
455
            if ( started > now )                        // crossed midnight
 
456
                started = now;
 
457
 
 
458
            XFlush(X11->display);
 
459
 
 
460
            // sleep 50ms, so we don't use up CPU cycles all the time.
 
461
            struct timeval usleep_tv;
 
462
            usleep_tv.tv_sec = 0;
 
463
            usleep_tv.tv_usec = 50000;
 
464
            select(0, 0, 0, 0, &usleep_tv);
 
465
        } while (started.msecsTo(now) < timeout);
 
466
    }
 
467
    return false;
 
468
}
 
469
 
 
470
 
 
471
static inline int maxSelectionIncr(Display *dpy)
 
472
{ return XMaxRequestSize(dpy) > 65536 ? 65536*4 : XMaxRequestSize(dpy)*4 - 100; }
 
473
 
 
474
bool QX11Data::clipboardReadProperty(Window win, Atom property, bool deleteProperty,
 
475
                                     QByteArray *buffer, int *size, Atom *type, int *format, bool nullterm)
 
476
{
 
477
    int           maxsize = maxSelectionIncr(display);
 
478
    ulong  bytes_left; // bytes_after
 
479
    ulong  length;     // nitems
 
480
    uchar *data;
 
481
    Atom   dummy_type;
 
482
    int    dummy_format;
 
483
    int    r;
 
484
 
 
485
    if (!type)                                // allow null args
 
486
        type = &dummy_type;
 
487
    if (!format)
 
488
        format = &dummy_format;
 
489
 
 
490
    // Don't read anything, just get the size of the property data
 
491
    r = XGetWindowProperty(display, win, property, 0, 0, False,
 
492
                            AnyPropertyType, type, format,
 
493
                            &length, &bytes_left, &data);
 
494
    if (r != Success || (type && *type == XNone)) {
 
495
        buffer->resize(0);
 
496
        return false;
 
497
    }
 
498
    XFree((char*)data);
 
499
 
 
500
    int  offset = 0, buffer_offset = 0, format_inc = 1, proplen = bytes_left;
 
501
 
 
502
    VDEBUG("QClipboard: read_property(): initial property length: %d", proplen);
 
503
 
 
504
    switch (*format) {
 
505
    case 8:
 
506
    default:
 
507
        format_inc = sizeof(char) / 1;
 
508
        break;
 
509
 
 
510
    case 16:
 
511
        format_inc = sizeof(short) / 2;
 
512
        proplen *= sizeof(short) / 2;
 
513
        break;
 
514
 
 
515
    case 32:
 
516
        format_inc = sizeof(long) / 4;
 
517
        proplen *= sizeof(long) / 4;
 
518
        break;
 
519
    }
 
520
 
 
521
    int newSize = proplen + (nullterm ? 1 : 0);
 
522
    buffer->resize(newSize);
 
523
 
 
524
    bool ok = (buffer->size() == newSize);
 
525
    VDEBUG("QClipboard: read_property(): buffer resized to %d", buffer->size());
 
526
 
 
527
    if (ok) {
 
528
        // could allocate buffer
 
529
 
 
530
        while (bytes_left) {
 
531
            // more to read...
 
532
 
 
533
            r = XGetWindowProperty(display, win, property, offset, maxsize/4,
 
534
                                   False, AnyPropertyType, type, format,
 
535
                                   &length, &bytes_left, &data);
 
536
            if (r != Success || (type && *type == XNone))
 
537
                break;
 
538
 
 
539
            offset += length / (32 / *format);
 
540
            length *= format_inc * (*format) / 8;
 
541
 
 
542
            // Here we check if we get a buffer overflow and tries to
 
543
            // recover -- this shouldn't normally happen, but it doesn't
 
544
            // hurt to be defensive
 
545
            if ((int)(buffer_offset + length) > buffer->size()) {
 
546
                length = buffer->size() - buffer_offset;
 
547
 
 
548
                // escape loop
 
549
                bytes_left = 0;
 
550
            }
 
551
 
 
552
            memcpy(buffer->data() + buffer_offset, data, length);
 
553
            buffer_offset += length;
 
554
 
 
555
            XFree((char*)data);
 
556
        }
 
557
 
 
558
        if (*format == 8 && *type == ATOM(COMPOUND_TEXT)) {
 
559
            // convert COMPOUND_TEXT to a multibyte string
 
560
            XTextProperty textprop;
 
561
            textprop.encoding = *type;
 
562
            textprop.format = *format;
 
563
            textprop.nitems = length;
 
564
            textprop.value = (unsigned char *) buffer->data();
 
565
 
 
566
            char **list_ret = 0;
 
567
            int count;
 
568
            if (XmbTextPropertyToTextList(display, &textprop, &list_ret,
 
569
                                            &count) == Success &&
 
570
                 count && list_ret) {
 
571
                offset = strlen(list_ret[0]);
 
572
                buffer->resize(offset + (nullterm ? 1 : 0));
 
573
                memcpy(buffer->data(), list_ret[0], offset);
 
574
            }
 
575
            if (list_ret) XFreeStringList(list_ret);
 
576
        }
 
577
 
 
578
        // zero-terminate (for text)
 
579
        if (nullterm)
 
580
            buffer[buffer_offset] = '\0';
 
581
    }
 
582
 
 
583
    // correct size, not 0-term.
 
584
    if (size)
 
585
        *size = buffer_offset;
 
586
 
 
587
    VDEBUG("QClipboard: read_property(): buffer size %d, buffer offset %d, offset %d",
 
588
           buffer->size(), buffer_offset, offset);
 
589
 
 
590
    if (deleteProperty)
 
591
        XDeleteProperty(display, win, property);
 
592
 
 
593
    XFlush(display);
 
594
 
 
595
    return ok;
 
596
}
 
597
 
 
598
QByteArray QX11Data::clipboardReadIncrementalProperty(Window win, Atom property, int nbytes, bool nullterm)
 
599
{
 
600
    XEvent event;
 
601
 
 
602
    QByteArray buf;
 
603
    QByteArray tmp_buf;
 
604
    bool alloc_error = false;
 
605
    int  length;
 
606
    int  offset = 0;
 
607
 
 
608
    if (nbytes > 0) {
 
609
        // Reserve buffer + zero-terminator (for text data)
 
610
        // We want to complete the INCR transfer even if we cannot
 
611
        // allocate more memory
 
612
        buf.resize(nbytes+1);
 
613
        alloc_error = buf.size() != nbytes+1;
 
614
    }
 
615
 
 
616
    for (;;) {
 
617
        XFlush(display);
 
618
        if (!clipboardWaitForEvent(win,PropertyNotify,&event,clipboard_timeout))
 
619
            break;
 
620
        if (event.xproperty.atom != property ||
 
621
             event.xproperty.state != PropertyNewValue)
 
622
            continue;
 
623
        if (X11->clipboardReadProperty(win, property, true, &tmp_buf, &length, 0, 0, false)) {
 
624
            if (length == 0) {                // no more data, we're done
 
625
                if (nullterm) {
 
626
                    buf.resize(offset+1);
 
627
                    buf[offset] = '\0';
 
628
                } else {
 
629
                    buf.resize(offset);
 
630
                }
 
631
                return buf;
 
632
            } else if (!alloc_error) {
 
633
                if (offset+length > (int)buf.size()) {
 
634
                    buf.resize(offset+length+65535);
 
635
                    if (buf.size() != offset+length+65535) {
 
636
                        alloc_error = true;
 
637
                        length = buf.size() - offset;
 
638
                    }
 
639
                }
 
640
                memcpy(buf.data()+offset, tmp_buf.constData(), length);
 
641
                tmp_buf.resize(0);
 
642
                offset += length;
 
643
            }
 
644
        } else {
 
645
            break;
 
646
        }
 
647
    }
 
648
 
 
649
    // timed out ... create a new requestor window, otherwise the requestor
 
650
    // could consider next request to be still part of this timed out request
 
651
    delete requestor;
 
652
    requestor = new QWidget(0);
 
653
    requestor->setObjectName("internal clipboard requestor");
 
654
 
 
655
    return QByteArray();
 
656
}
 
657
 
 
658
static Atom send_selection(QClipboardData *d, Atom target, Window window, Atom property,
 
659
                           int format = 0, QByteArray data = QByteArray());
 
660
 
 
661
static Atom send_targets_selection(QClipboardData *d, Window window, Atom property)
 
662
{
 
663
    QStringList formats = QInternalMimeData::formatsHelper(d->source());
 
664
    int atoms = formats.size();
 
665
    if (formats.contains("image/ppm")) atoms++;
 
666
    if (formats.contains("image/pbm")) atoms++;
 
667
    if (formats.contains("text/plain")) atoms+=4;
 
668
 
 
669
    VDEBUG("QClipboard: send_targets_selection(): data provides %d types, mapped to %d provided types", formats.size(), atoms);
 
670
 
 
671
    // for 64 bit cleanness... XChangeProperty expects long* for data with format == 32
 
672
    QByteArray data;
 
673
    data.resize((atoms+3) * sizeof(long)); // plus TARGETS, MULTIPLE and TIMESTAMP
 
674
    long *atarget = (long *) data.data();
 
675
 
 
676
    int n = 0;
 
677
    for (n = 0; n < formats.size(); ++n) {
 
678
        VDEBUG("    original format %s", formats.at(n).toLatin1().data());
 
679
        atarget[n] = X11->xdndStringToAtom(formats.at(n).toLatin1().data());
 
680
    }
 
681
 
 
682
    if (formats.contains("image/ppm"))
 
683
        atarget[n++] = XA_PIXMAP;
 
684
    if (formats.contains("image/pbm"))
 
685
        atarget[n++] = XA_BITMAP;
 
686
    if (formats.contains("text/plain")) {
 
687
        atarget[n++] = ATOM(UTF8_STRING);
 
688
        atarget[n++] = ATOM(TEXT);
 
689
        atarget[n++] = ATOM(COMPOUND_TEXT);
 
690
        atarget[n++] = XA_STRING;
 
691
    }
 
692
 
 
693
    atarget[n++] = ATOM(TARGETS);
 
694
    atarget[n++] = ATOM(MULTIPLE);
 
695
    atarget[n++] = ATOM(TIMESTAMP);
 
696
 
 
697
#if defined(QCLIPBOARD_DEBUG_VERBOSE)
 
698
    for (int index = 0; index < n; index++) {
 
699
        VDEBUG("    atom %d: 0x%lx (%s)", index, atarget[index],
 
700
               X11->xdndAtomToString(atarget[index]).data());
 
701
    }
 
702
#endif
 
703
 
 
704
    XChangeProperty(X11->display, window, property, XA_ATOM, 32,
 
705
                    PropModeReplace, (uchar *) data.data(), n);
 
706
    return property;
 
707
}
 
708
 
 
709
static Atom send_string_selection(QClipboardData *d, Atom target, Window window, Atom property)
 
710
{
 
711
 
 
712
    DEBUG("QClipboard: send_string_selection():\n"
 
713
          "    property type %lx\n"
 
714
          "    property name '%s'",
 
715
          target, X11->xdndAtomToString(target).data());
 
716
 
 
717
    if (target == ATOM(TEXT) || target == ATOM(COMPOUND_TEXT)) {
 
718
        // the ICCCM states that TEXT and COMPOUND_TEXT are in the
 
719
        // encoding of choice, so we choose the encoding of the locale
 
720
        QByteArray data = d->source()->text().toLocal8Bit();
 
721
        char *list[] = { data.data(), NULL };
 
722
 
 
723
        XICCEncodingStyle style =
 
724
            (target == ATOM(COMPOUND_TEXT)) ? XCompoundTextStyle : XStdICCTextStyle;
 
725
        XTextProperty textprop;
 
726
        if (list[0] != NULL
 
727
            && XmbTextListToTextProperty(X11->display,
 
728
                                         list, 1, style, &textprop) == Success) {
 
729
            DEBUG("    textprop type %lx\n"
 
730
                  "    textprop name '%s'\n"
 
731
                  "    format %d\n"
 
732
                  "    %ld items",
 
733
                  textprop.encoding, X11->xdndAtomToString(textprop.encoding).data(),
 
734
                  textprop.format, textprop.nitems);
 
735
 
 
736
            int sz = sizeof_format(textprop.format);
 
737
            data = QByteArray((const char *) textprop.value, textprop.nitems * sz);
 
738
            XFree(textprop.value);
 
739
 
 
740
            return send_selection(d, textprop.encoding, window, property, textprop.format, data);
 
741
        }
 
742
 
 
743
        return XNone;
 
744
    }
 
745
 
 
746
    Atom xtarget = XNone;
 
747
    QByteArray data;
 
748
    if (target == XA_STRING) {
 
749
        // the ICCCM states that STRING is latin1 plus newline and tab
 
750
        // see section 2.6.2
 
751
        data = d->source()->text().toLatin1();
 
752
        xtarget = XA_STRING;
 
753
    } else if (target == ATOM(UTF8_STRING)) {
 
754
        // proposed UTF8_STRING conversion type
 
755
        data = d->source()->text().toUtf8();
 
756
        xtarget = ATOM(UTF8_STRING);
 
757
    }
 
758
 
 
759
    if (xtarget == XNone) // should not happen
 
760
        return XNone;
 
761
 
 
762
    DEBUG("    format 8\n    %d bytes", data.size());
 
763
 
 
764
    return send_selection(d, xtarget, window, property, 8, data);
 
765
}
 
766
 
 
767
static Atom send_pixmap_selection(QClipboardData *d, Atom target, Window window, Atom property)
 
768
{
 
769
    QPixmap pm;
 
770
 
 
771
    if (target == XA_PIXMAP) {
 
772
        pm = qvariant_cast<QPixmap>(d->source()->imageData());
 
773
    } else if (target == XA_BITMAP) {
 
774
        pm = qvariant_cast<QPixmap>(d->source()->imageData());
 
775
        QImage img = pm.toImage();
 
776
        if (img.depth() != 1) {
 
777
            img = img.convertToFormat(QImage::Format_MonoLSB);
 
778
            pm = QPixmap::fromImage(img);
 
779
        }
 
780
    }
 
781
 
 
782
    if (pm.isNull()) // should never happen
 
783
        return XNone;
 
784
 
 
785
    Pixmap handle = pm.handle();
 
786
    XChangeProperty(X11->display, window, property,
 
787
                    target, 32, PropModeReplace, (uchar *) &handle, 1);
 
788
    d->addTransferredPixmap(pm);
 
789
    return property;
 
790
 
 
791
}
 
792
 
 
793
static Atom send_selection(QClipboardData *d, Atom target, Window window, Atom property,
 
794
                           int format, QByteArray data)
 
795
{
 
796
    if (format == 0) format = 8;
 
797
 
 
798
    if (data.isEmpty()) {
 
799
        QByteArray fmt = X11->xdndAtomToString(target);
 
800
        DEBUG("QClipboard: send_selection(): converting to type '%s'", fmt.data());
 
801
        if (fmt.isEmpty() || !QInternalMimeData::hasFormatHelper(fmt, d->source())) // Not a MIME type we have
 
802
            return XNone;
 
803
         else
 
804
            data = QInternalMimeData::renderDataHelper(fmt, d->source());
 
805
    }
 
806
 
 
807
    DEBUG("QClipboard: send_selection():\n"
 
808
          "    property type %lx\n"
 
809
          "    property name '%s'\n"
 
810
          "    format %d\n"
 
811
          "    %d bytes",
 
812
          target, X11->xdndAtomToString(target).data(), format, data.size());
 
813
 
 
814
    // don't allow INCR transfers when using MULTIPLE or to
 
815
    // Motif clients (since Motif doesn't support INCR)
 
816
    static Atom motif_clip_temporary = ATOM(CLIP_TEMPORARY);
 
817
    bool allow_incr = property != motif_clip_temporary;
 
818
 
 
819
    // X_ChangeProperty protocol request is 24 bytes
 
820
    const int increment = (XMaxRequestSize(X11->display) * 4) - 24;
 
821
    if (data.size() > increment && allow_incr) {
 
822
        long bytes = data.size();
 
823
        XChangeProperty(X11->display, window, property,
 
824
                        ATOM(INCR), 32, PropModeReplace, (uchar *) &bytes, 1);
 
825
 
 
826
        (void)new QClipboardINCRTransaction(window, property, target, format, data, increment);
 
827
        return ATOM(INCR);
 
828
    }
 
829
 
 
830
    // make sure we can perform the XChangeProperty in a single request
 
831
    if (data.size() > increment)
 
832
        return XNone; // ### perhaps use several XChangeProperty calls w/ PropModeAppend?
 
833
 
 
834
    // use a single request to transfer data
 
835
    XChangeProperty(X11->display, window, property, target,
 
836
                    format, PropModeReplace, (uchar *) data.data(),
 
837
                    data.size() / sizeof_format(format));
 
838
    return property;
 
839
}
 
840
 
 
841
/*! \internal
 
842
    Internal cleanup for Windows.
 
843
*/
 
844
void QClipboard::ownerDestroyed()
 
845
{ }
 
846
 
 
847
 
 
848
/*! \internal
 
849
    Internal optimization for Windows.
 
850
*/
 
851
void QClipboard::connectNotify(const char *)
 
852
{ }
 
853
 
 
854
 
 
855
/*! \reimp
 
856
 */
 
857
bool QClipboard::event(QEvent *e)
 
858
{
 
859
    if (e->type() == QEvent::Timer) {
 
860
        QTimerEvent *te = (QTimerEvent *) e;
 
861
 
 
862
        if (waiting_for_data) // should never happen
 
863
            return false;
 
864
 
 
865
        if (te->timerId() == timer_id) {
 
866
            killTimer(timer_id);
 
867
            timer_id = 0;
 
868
 
 
869
            timer_event_clear = true;
 
870
            if (selection_watcher) // clear selection
 
871
                selectionData()->clear();
 
872
            if (clipboard_watcher) // clear clipboard
 
873
                clipboardData()->clear();
 
874
            timer_event_clear = false;
 
875
 
 
876
            return true;
 
877
        } else if (te->timerId() == pending_timer_id) {
 
878
            // I hate klipper
 
879
            killTimer(pending_timer_id);
 
880
            pending_timer_id = 0;
 
881
 
 
882
            if (pending_clipboard_changed) {
 
883
                pending_clipboard_changed = false;
 
884
                clipboardData()->clear();
 
885
                emit dataChanged();
 
886
            }
 
887
            if (pending_selection_changed) {
 
888
                pending_selection_changed = false;
 
889
                selectionData()->clear();
 
890
                emit selectionChanged();
 
891
            }
 
892
 
 
893
            return true;
 
894
        } else if (te->timerId() == incr_timer_id) {
 
895
            killTimer(incr_timer_id);
 
896
            incr_timer_id = 0;
 
897
 
 
898
            qt_xclb_incr_timeout();
 
899
 
 
900
            return true;
 
901
        } else {
 
902
            return QObject::event(e);
 
903
        }
 
904
    } else if (e->type() != QEvent::Clipboard) {
 
905
        return QObject::event(e);
 
906
    }
 
907
 
 
908
    XEvent *xevent = (XEvent *)(((QClipboardEvent *)e)->data());
 
909
    Display *dpy = X11->display;
 
910
 
 
911
    if (!xevent)
 
912
        return true;
 
913
 
 
914
    switch (xevent->type) {
 
915
 
 
916
    case SelectionClear:
 
917
        // new selection owner
 
918
        if (xevent->xselectionclear.selection == XA_PRIMARY) {
 
919
            QClipboardData *d = selectionData();
 
920
 
 
921
            // ignore the event if it was generated before we gained selection ownership
 
922
            if (d->timestamp != CurrentTime && xevent->xselectionclear.time < d->timestamp)
 
923
                break;
 
924
 
 
925
            DEBUG("QClipboard: new selection owner 0x%lx at time %lx (ours %lx)",
 
926
                  XGetSelectionOwner(dpy, XA_PRIMARY),
 
927
                  xevent->xselectionclear.time, d->timestamp);
 
928
 
 
929
            if (! waiting_for_data) {
 
930
                d->clear();
 
931
                emit selectionChanged();
 
932
            } else {
 
933
                pending_selection_changed = true;
 
934
                if (! pending_timer_id)
 
935
                    pending_timer_id = QApplication::clipboard()->startTimer(0);
 
936
            }
 
937
        } else if (xevent->xselectionclear.selection == ATOM(CLIPBOARD)) {
 
938
            QClipboardData *d = clipboardData();
 
939
 
 
940
            // ignore the event if it was generated before we gained selection ownership
 
941
            if (d->timestamp != CurrentTime && xevent->xselectionclear.time < d->timestamp)
 
942
                break;
 
943
 
 
944
            DEBUG("QClipboard: new clipboard owner 0x%lx at time %lx (%lx)",
 
945
                  XGetSelectionOwner(dpy, ATOM(CLIPBOARD)),
 
946
                  xevent->xselectionclear.time, d->timestamp);
 
947
 
 
948
            if (! waiting_for_data) {
 
949
                d->clear();
 
950
                emit dataChanged();
 
951
            } else {
 
952
                pending_clipboard_changed = true;
 
953
                if (! pending_timer_id)
 
954
                    pending_timer_id = QApplication::clipboard()->startTimer(0);
 
955
            }
 
956
        } else {
 
957
            qWarning("QClipboard: Unknown SelectionClear event received.");
 
958
            return false;
 
959
        }
 
960
        break;
 
961
 
 
962
    case SelectionNotify:
 
963
        /*
 
964
          Something has delivered data to us, but this was not caught
 
965
          by QClipboardWatcher::getDataInFormat()
 
966
 
 
967
          Just skip the event to prevent Bad Things (tm) from
 
968
          happening later on...
 
969
        */
 
970
        break;
 
971
 
 
972
    case SelectionRequest:
 
973
        {
 
974
            // someone wants our data
 
975
            XSelectionRequestEvent *req = &xevent->xselectionrequest;
 
976
 
 
977
            if (req->requestor == requestor->winId()) break;
 
978
 
 
979
            XEvent event;
 
980
            event.xselection.type      = SelectionNotify;
 
981
            event.xselection.display   = req->display;
 
982
            event.xselection.requestor = req->requestor;
 
983
            event.xselection.selection = req->selection;
 
984
            event.xselection.target    = req->target;
 
985
            event.xselection.property  = XNone;
 
986
            event.xselection.time      = req->time;
 
987
 
 
988
            DEBUG("QClipboard: SelectionRequest from %lx\n"
 
989
                  "    selection 0x%lx (%s) target 0x%lx (%s)",
 
990
                  req->requestor,
 
991
                  req->selection,
 
992
                  X11->xdndAtomToString(req->selection).data(),
 
993
                  req->target,
 
994
                  X11->xdndAtomToString(req->target).data());
 
995
 
 
996
            QClipboardData *d;
 
997
            if (req->selection == XA_PRIMARY) {
 
998
                d = selectionData();
 
999
            } else if (req->selection == ATOM(CLIPBOARD)) {
 
1000
                d = clipboardData();
 
1001
            } else {
 
1002
                qWarning("QClipboard: unknown selection '%lx'", req->selection);
 
1003
                XSendEvent(dpy, req->requestor, False, NoEventMask, &event);
 
1004
                break;
 
1005
            }
 
1006
 
 
1007
            if (! d->source()) {
 
1008
                qWarning("QClipboard: cannot transfer data, no data available");
 
1009
                XSendEvent(dpy, req->requestor, False, NoEventMask, &event);
 
1010
                break;
 
1011
            }
 
1012
 
 
1013
            DEBUG("QClipboard: SelectionRequest at time %lx (ours %lx)",
 
1014
                  req->time, d->timestamp);
 
1015
 
 
1016
            if (d->timestamp == CurrentTime // we don't own the selection anymore
 
1017
                || (req->time != CurrentTime && req->time < d->timestamp)) {
 
1018
                DEBUG("QClipboard: SelectionRequest too old");
 
1019
                XSendEvent(dpy, req->requestor, False, NoEventMask, &event);
 
1020
                break;
 
1021
            }
 
1022
 
 
1023
            Atom xa_targets = ATOM(TARGETS);
 
1024
            Atom xa_multiple = ATOM(MULTIPLE);
 
1025
            Atom xa_timestamp = ATOM(TIMESTAMP);
 
1026
 
 
1027
            struct AtomPair { Atom target; Atom property; } *multi = 0;
 
1028
            Atom multi_type = XNone;
 
1029
            int multi_format = 0;
 
1030
            int nmulti = 0;
 
1031
            int imulti = -1;
 
1032
            bool multi_writeback = false;
 
1033
 
 
1034
            if (req->target == xa_multiple) {
 
1035
                QByteArray multi_data;
 
1036
                if (req->property == XNone
 
1037
                    || !X11->clipboardReadProperty(req->requestor, req->property, false, &multi_data,
 
1038
                                                   0, &multi_type, &multi_format, 0)
 
1039
                    || multi_format != 32) {
 
1040
                    // MULTIPLE property not formatted correctly
 
1041
                    XSendEvent(dpy, req->requestor, False, NoEventMask, &event);
 
1042
                    break;
 
1043
                }
 
1044
                nmulti = multi_data.size()/sizeof(*multi);
 
1045
                multi = new AtomPair[nmulti];
 
1046
                memcpy(multi,multi_data.data(),multi_data.size());
 
1047
                imulti = 0;
 
1048
            }
 
1049
 
 
1050
            for (; imulti < nmulti; ++imulti) {
 
1051
                Atom target;
 
1052
                Atom property;
 
1053
 
 
1054
                if (multi) {
 
1055
                    target = multi[imulti].target;
 
1056
                    property = multi[imulti].property;
 
1057
                } else {
 
1058
                    target = req->target;
 
1059
                    property = req->property;
 
1060
                    if (property == XNone) // obsolete client
 
1061
                        property = target;
 
1062
                }
 
1063
 
 
1064
                Atom ret = XNone;
 
1065
                if (target == XNone || property == XNone) {
 
1066
                    ;
 
1067
                } else if (target == xa_timestamp) {
 
1068
                    if (d->timestamp != CurrentTime) {
 
1069
                        XChangeProperty(dpy, req->requestor, property, xa_timestamp, 32,
 
1070
                                        PropModeReplace, (uchar *) &d->timestamp, 1);
 
1071
                        ret = property;
 
1072
                    } else {
 
1073
                        qWarning("QClipboard: invalid data timestamp");
 
1074
                    }
 
1075
                } else if (target == xa_targets) {
 
1076
                    ret = send_targets_selection(d, req->requestor, property);
 
1077
                } else if (target == XA_STRING
 
1078
                           || target == ATOM(TEXT)
 
1079
                           || target == ATOM(COMPOUND_TEXT)
 
1080
                           || target == ATOM(UTF8_STRING)) {
 
1081
                    ret = send_string_selection(d, target, req->requestor, property);
 
1082
                } else if (target == XA_PIXMAP
 
1083
                           || target == XA_BITMAP) {
 
1084
                    ret = send_pixmap_selection(d, target, req->requestor, property);
 
1085
                } else {
 
1086
                    ret = send_selection(d, target, req->requestor, property);
 
1087
                }
 
1088
 
 
1089
                if (nmulti > 0) {
 
1090
                    if (ret == XNone) {
 
1091
                        multi[imulti].property = XNone;
 
1092
                        multi_writeback = true;
 
1093
                    }
 
1094
                } else {
 
1095
                    event.xselection.property = ret;
 
1096
                    break;
 
1097
                }
 
1098
            }
 
1099
 
 
1100
            if (nmulti > 0) {
 
1101
                if (multi_writeback) {
 
1102
                    // according to ICCCM 2.6.2 says to put None back
 
1103
                    // into the original property on the requestor window
 
1104
                    XChangeProperty(dpy, req->requestor, req->property, multi_type, 32,
 
1105
                                    PropModeReplace, (uchar *) multi, nmulti * 2);
 
1106
                }
 
1107
 
 
1108
                delete [] multi;
 
1109
                event.xselection.property = req->property;
 
1110
            }
 
1111
 
 
1112
            // send selection notify to requestor
 
1113
            XSendEvent(dpy, req->requestor, False, NoEventMask, &event);
 
1114
 
 
1115
            DEBUG("QClipboard: SelectionNotify to 0x%lx\n"
 
1116
                  "    property 0x%lx (%s)",
 
1117
                  req->requestor, event.xselection.property,
 
1118
                  X11->xdndAtomToString(event.xselection.property).data());
 
1119
        }
 
1120
        break;
 
1121
    }
 
1122
 
 
1123
    return true;
 
1124
}
 
1125
 
 
1126
 
 
1127
 
 
1128
 
 
1129
 
 
1130
 
 
1131
QClipboardWatcher::QClipboardWatcher(QClipboard::Mode mode)
 
1132
    : QInternalMimeData()
 
1133
{
 
1134
    switch (mode) {
 
1135
    case QClipboard::Selection:
 
1136
        atom = XA_PRIMARY;
 
1137
        break;
 
1138
 
 
1139
    case QClipboard::Clipboard:
 
1140
        atom = ATOM(CLIPBOARD);
 
1141
        break;
 
1142
 
 
1143
    default:
 
1144
        qWarning("QClipboardWatcher: internal error, unknown clipboard mode");
 
1145
        break;
 
1146
    }
 
1147
 
 
1148
    setupOwner();
 
1149
}
 
1150
 
 
1151
QClipboardWatcher::~QClipboardWatcher()
 
1152
{
 
1153
    if(selection_watcher == this)
 
1154
        selection_watcher = 0;
 
1155
    if(clipboard_watcher == this)
 
1156
        clipboard_watcher = 0;
 
1157
}
 
1158
 
 
1159
bool QClipboardWatcher::empty() const
 
1160
{
 
1161
    Display *dpy = X11->display;
 
1162
    Window win = XGetSelectionOwner(dpy, atom);
 
1163
 
 
1164
    if(win == requestor->winId()) {
 
1165
        qWarning("QClipboardWatcher::empty: internal error, app owns the selection");
 
1166
        return true;
 
1167
    }
 
1168
 
 
1169
    return win == XNone;
 
1170
}
 
1171
 
 
1172
QStringList QClipboardWatcher::formats_sys() const
 
1173
{
 
1174
    if (empty())
 
1175
        return QStringList();
 
1176
 
 
1177
    if (!formatList.count()) {
 
1178
        // get the list of targets from the current clipboard owner - we do this
 
1179
        // once so that multiple calls to this function don't require multiple
 
1180
        // server round trips...
 
1181
 
 
1182
        format_atoms = getDataInFormat(ATOM(TARGETS));
 
1183
 
 
1184
        if (format_atoms.size() > 0) {
 
1185
            Atom *targets = (Atom *) format_atoms.data();
 
1186
            int size = format_atoms.size() / sizeof(Atom);
 
1187
 
 
1188
            for (int i = 0; i < size; ++i) {
 
1189
                if (targets[i] == 0)
 
1190
                    continue;
 
1191
 
 
1192
                VDEBUG("    format: %s", X11->xdndAtomToString(targets[i]).data());
 
1193
                if (targets[i] == XA_PIXMAP)
 
1194
                    formatList.append("image/ppm");
 
1195
                else if (targets[i] == XA_STRING
 
1196
                         || targets[i] == ATOM(UTF8_STRING)
 
1197
                         || targets[i] == ATOM(TEXT)
 
1198
                         || targets[i] == ATOM(COMPOUND_TEXT))
 
1199
                    formatList.append("text/plain");
 
1200
                else
 
1201
                    formatList.append(X11->xdndAtomToString(targets[i]));
 
1202
                VDEBUG("    data:\n%s\n", getDataInFormat(targets[i]).data());
 
1203
            }
 
1204
 
 
1205
            DEBUG("QClipboardWatcher::format: %d formats available", formatList.count());
 
1206
        }
 
1207
    }
 
1208
 
 
1209
    return formatList;
 
1210
}
 
1211
 
 
1212
bool QClipboardWatcher::hasFormat_sys(const QString &format) const
 
1213
{
 
1214
    QStringList list = formats();
 
1215
    return list.contains(format);
 
1216
}
 
1217
 
 
1218
QVariant QClipboardWatcher::retrieveData_sys(const QString &fmt, QVariant::Type type) const
 
1219
{
 
1220
    if (fmt.isEmpty() || empty())
 
1221
        return QByteArray();
 
1222
 
 
1223
    (void)formats(); // trigger update of format list
 
1224
    DEBUG("QClipboardWatcher::data: fetching format '%s'", fmt.toLatin1().data());
 
1225
 
 
1226
    Atom fmtatom = 0;
 
1227
 
 
1228
    if (fmt == QLatin1String("text/plain")) {
 
1229
        Atom *targets = (Atom *) format_atoms.data();
 
1230
        int size = format_atoms.size() / sizeof(Atom);
 
1231
 
 
1232
        // find best available text format
 
1233
        for (int i = 0; i < size; ++i) {
 
1234
            VDEBUG("    format: %s", X11->xdndAtomToString(targets[i]).data());
 
1235
            if (targets[i] == XA_STRING) {
 
1236
                if (fmtatom == 0)
 
1237
                    fmtatom = targets[i];
 
1238
            } else if (targets[i] == ATOM(TEXT)) {
 
1239
                if (fmtatom == 0 || fmtatom == XA_STRING)
 
1240
                    fmtatom = targets[i];
 
1241
            } else if (targets[i] == ATOM(COMPOUND_TEXT)) {
 
1242
                if (fmtatom == 0 || fmtatom == XA_STRING || fmtatom == ATOM(TEXT))
 
1243
                    fmtatom = targets[i];
 
1244
//             } else if (targets[i] == ATOM("text/plain;charset=ISO-10646-UCS-2"))) {
 
1245
            } else if (targets[i] == ATOM(UTF8_STRING)) {
 
1246
                if (fmtatom == 0 || fmtatom == XA_STRING || fmtatom == ATOM(TEXT) || fmtatom == ATOM(COMPOUND_TEXT))
 
1247
                    fmtatom = targets[i];
 
1248
            }
 
1249
        }
 
1250
 
 
1251
        if (fmtatom == 0)
 
1252
            return QVariant();
 
1253
        QByteArray data = getDataInFormat(fmtatom);
 
1254
        QString result;
 
1255
        if (fmtatom == XA_STRING)
 
1256
            result = QString::fromLatin1(data);
 
1257
        else if (fmtatom == ATOM(TEXT) || fmtatom == ATOM(COMPOUND_TEXT)) {
 
1258
            // #### might be wrong for COMPUND_TEXT
 
1259
            result = QString::fromLocal8Bit(data);
 
1260
        } else if (fmtatom == ATOM(UTF8_STRING)) {
 
1261
            result = QString::fromUtf8(data);
 
1262
        }
 
1263
        DEBUG("got plain text '%s'", result.toUtf8().data());
 
1264
        if (type == QVariant::String)
 
1265
            return result;
 
1266
        return result.toUtf8();
 
1267
    }
 
1268
    if (fmt == QLatin1String("image/ppm")) {
 
1269
        fmtatom = XA_PIXMAP;
 
1270
        QByteArray pmd = getDataInFormat(fmtatom);
 
1271
        if (pmd.size() == sizeof(Pixmap)) {
 
1272
            Pixmap xpm = *((Pixmap*)pmd.data());
 
1273
            Display *dpy = X11->display;
 
1274
            Window r;
 
1275
            int x,y;
 
1276
            uint w,h,bw,d;
 
1277
            if (! xpm)
 
1278
                return QByteArray();
 
1279
            XGetGeometry(dpy,xpm, &r,&x,&y,&w,&h,&bw,&d);
 
1280
            QImageWriter imageWriter;
 
1281
            GC gc = XCreateGC(dpy, xpm, 0, 0);
 
1282
            QImage imageToWrite;
 
1283
            if (d == 1) {
 
1284
                QBitmap qbm(w,h);
 
1285
                XCopyArea(dpy,xpm,qbm.handle(),gc,0,0,w,h,0,0);
 
1286
                if (type == QVariant::Bitmap)
 
1287
                    return qbm;
 
1288
                if (type == QVariant::Pixmap)
 
1289
                    return QPixmap(qbm);
 
1290
                imageWriter.setFormat("PBMRAW");
 
1291
                imageToWrite = qbm.toImage();
 
1292
            } else {
 
1293
                QPixmap qpm(w,h);
 
1294
                XCopyArea(dpy,xpm,qpm.handle(),gc,0,0,w,h,0,0);
 
1295
                if (type == QVariant::Pixmap)
 
1296
                    return qpm;
 
1297
                imageWriter.setFormat("PPMRAW");
 
1298
                imageToWrite = qpm.toImage();
 
1299
            }
 
1300
            XFreeGC(dpy,gc);
 
1301
            QBuffer buf;
 
1302
            buf.open(QIODevice::WriteOnly);
 
1303
            imageWriter.setDevice(&buf);
 
1304
            imageWriter.write(imageToWrite);
 
1305
            return buf.buffer();
 
1306
        } else {
 
1307
            fmtatom = X11->xdndStringToAtom(fmt.toLatin1().data());
 
1308
        }
 
1309
    } else {
 
1310
        fmtatom = X11->xdndStringToAtom(fmt.toLatin1().data());
 
1311
    }
 
1312
    return getDataInFormat(fmtatom);
 
1313
}
 
1314
 
 
1315
QByteArray QClipboardWatcher::getDataInFormat(Atom fmtatom) const
 
1316
{
 
1317
    QByteArray buf;
 
1318
 
 
1319
    Display *dpy = X11->display;
 
1320
    Window   win = requestor->winId();
 
1321
 
 
1322
    DEBUG("QClipboardWatcher::getDataInFormat: selection '%s' format '%s'",
 
1323
          X11->xdndAtomToString(atom).data(), X11->xdndAtomToString(fmtatom).data());
 
1324
 
 
1325
    XSelectInput(dpy, win, NoEventMask); // don't listen for any events
 
1326
 
 
1327
    XDeleteProperty(dpy, win, ATOM(_QT_SELECTION));
 
1328
    XConvertSelection(dpy, atom, fmtatom, ATOM(_QT_SELECTION), win, X11->time);
 
1329
    XSync(dpy, false);
 
1330
 
 
1331
    VDEBUG("QClipboardWatcher::getDataInFormat: waiting for SelectionNotify event");
 
1332
 
 
1333
    XEvent xevent;
 
1334
    if (!X11->clipboardWaitForEvent(win,SelectionNotify,&xevent,clipboard_timeout) ||
 
1335
         xevent.xselection.property == XNone) {
 
1336
        DEBUG("QClipboardWatcher::getDataInFormat: format not available");
 
1337
        return buf;
 
1338
    }
 
1339
 
 
1340
    VDEBUG("QClipboardWatcher::getDataInFormat: fetching data...");
 
1341
 
 
1342
    Atom   type;
 
1343
    XSelectInput(dpy, win, PropertyChangeMask);
 
1344
 
 
1345
    if (X11->clipboardReadProperty(win, ATOM(_QT_SELECTION), true, &buf, 0, &type, 0, false)) {
 
1346
        if (type == ATOM(INCR)) {
 
1347
            int nbytes = buf.size() >= 4 ? *((int*)buf.data()) : 0;
 
1348
            buf = X11->clipboardReadIncrementalProperty(win, ATOM(_QT_SELECTION), nbytes, false);
 
1349
        }
 
1350
    }
 
1351
 
 
1352
    XSelectInput(dpy, win, NoEventMask);
 
1353
 
 
1354
    DEBUG("QClipboardWatcher::getDataInFormat: %d bytes received", buf.size());
 
1355
 
 
1356
    return buf;
 
1357
}
 
1358
 
 
1359
 
 
1360
const QMimeData* QClipboard::mimeData(Mode mode) const
 
1361
{
 
1362
    QClipboardData *d = 0;
 
1363
    switch (mode) {
 
1364
    case Selection:
 
1365
        d = selectionData();
 
1366
        break;
 
1367
    case Clipboard:
 
1368
        d = clipboardData();
 
1369
        break;
 
1370
    }
 
1371
 
 
1372
    if (! d->source() && ! timer_event_clear) {
 
1373
        if (mode == Selection) {
 
1374
            if (! selection_watcher)
 
1375
                selection_watcher = new QClipboardWatcher(mode);
 
1376
            d->setSource(selection_watcher);
 
1377
        } else {
 
1378
            if (! clipboard_watcher)
 
1379
                clipboard_watcher = new QClipboardWatcher(mode);
 
1380
            d->setSource(clipboard_watcher);
 
1381
        }
 
1382
 
 
1383
        if (! timer_id) {
 
1384
            // start a zero timer - we will clear cached data when the timer
 
1385
            // times out, which will be the next time we hit the event loop...
 
1386
            // that way, the data is cached long enough for calls within a single
 
1387
            // loop/function, but the data doesn't linger around in case the selection
 
1388
            // changes
 
1389
            QClipboard *that = ((QClipboard *) this);
 
1390
            timer_id = that->startTimer(0);
 
1391
        }
 
1392
    }
 
1393
 
 
1394
    return d->source();
 
1395
}
 
1396
 
 
1397
 
 
1398
void QClipboard::setMimeData(QMimeData* src, Mode mode)
 
1399
{
 
1400
    Atom atom, sentinel_atom;
 
1401
    QClipboardData *d;
 
1402
    switch (mode) {
 
1403
    case Selection:
 
1404
        atom = XA_PRIMARY;
 
1405
        sentinel_atom = ATOM(_QT_SELECTION_SENTINEL);
 
1406
        d = selectionData();
 
1407
        break;
 
1408
 
 
1409
    case Clipboard:
 
1410
        atom = ATOM(CLIPBOARD);
 
1411
        sentinel_atom = ATOM(_QT_CLIPBOARD_SENTINEL);
 
1412
        d = clipboardData();
 
1413
        break;
 
1414
 
 
1415
    default:
 
1416
        qWarning("QClipboard::data: invalid mode '%d'", mode);
 
1417
        return;
 
1418
    }
 
1419
 
 
1420
    Display *dpy = X11->display;
 
1421
    Window newOwner;
 
1422
 
 
1423
    if (! src) { // no data, clear clipboard contents
 
1424
        newOwner = XNone;
 
1425
        d->clear();
 
1426
    } else {
 
1427
        setupOwner();
 
1428
 
 
1429
        newOwner = owner->winId();
 
1430
 
 
1431
        d->setSource(src);
 
1432
        d->timestamp = X11->time;
 
1433
    }
 
1434
 
 
1435
    Window prevOwner = XGetSelectionOwner(dpy, atom);
 
1436
    // use X11->time, since d->timestamp == CurrentTime when clearing
 
1437
    XSetSelectionOwner(dpy, atom, newOwner, X11->time);
 
1438
 
 
1439
    if (mode == Selection)
 
1440
        emit selectionChanged();
 
1441
    else
 
1442
        emit dataChanged();
 
1443
 
 
1444
    if (XGetSelectionOwner(dpy, atom) != newOwner) {
 
1445
        qWarning("QClipboard::setData: Cannot set X11 selection owner for %s",
 
1446
                 X11->xdndAtomToString(atom).data());
 
1447
        d->clear();
 
1448
        return;
 
1449
    }
 
1450
 
 
1451
    // Signal to other Qt processes that the selection has changed
 
1452
    Window owners[2];
 
1453
    owners[0] = newOwner;
 
1454
    owners[1] = prevOwner;
 
1455
    XChangeProperty(dpy, QApplication::desktop()->screen(0)->winId(),
 
1456
                     sentinel_atom, XA_WINDOW, 32, PropModeReplace,
 
1457
                     (unsigned char*)&owners, 2);
 
1458
}
 
1459
 
 
1460
 
 
1461
/*
 
1462
  Called by the main event loop in qapplication_x11.cpp when the
 
1463
  _QT_SELECTION_SENTINEL property has been changed (i.e. when some Qt
 
1464
  process has performed QClipboard::setData(). If it returns true, the
 
1465
  QClipBoard dataChanged() signal should be emitted.
 
1466
*/
 
1467
 
 
1468
bool qt_check_selection_sentinel()
 
1469
{
 
1470
    bool doIt = true;
 
1471
    if (owner) {
 
1472
        /*
 
1473
          Since the X selection mechanism cannot give any signal when
 
1474
          the selection has changed, we emulate it (for Qt processes) here.
 
1475
          The notification should be ignored in case of either
 
1476
          a) This is the process that did setData (because setData()
 
1477
          then has already emitted dataChanged())
 
1478
          b) This is the process that owned the selection when dataChanged()
 
1479
          was called (because we have then received a SelectionClear event,
 
1480
          and have already emitted dataChanged() as a result of that)
 
1481
        */
 
1482
 
 
1483
        Window* owners;
 
1484
        Atom actualType;
 
1485
        int actualFormat;
 
1486
        ulong nitems;
 
1487
        ulong bytesLeft;
 
1488
 
 
1489
        if (XGetWindowProperty(X11->display,
 
1490
                               QApplication::desktop()->screen(0)->winId(),
 
1491
                               ATOM(_QT_SELECTION_SENTINEL), 0, 2, False, XA_WINDOW,
 
1492
                               &actualType, &actualFormat, &nitems,
 
1493
                               &bytesLeft, (unsigned char**)&owners) == Success) {
 
1494
            if (actualType == XA_WINDOW && actualFormat == 32 && nitems == 2) {
 
1495
                Window win = owner->winId();
 
1496
                if (owners[0] == win || owners[1] == win)
 
1497
                    doIt = false;
 
1498
            }
 
1499
 
 
1500
            XFree(owners);
 
1501
        }
 
1502
    }
 
1503
 
 
1504
    if (doIt) {
 
1505
        if (waiting_for_data) {
 
1506
            pending_selection_changed = true;
 
1507
            if (! pending_timer_id)
 
1508
                pending_timer_id = QApplication::clipboard()->startTimer(0);
 
1509
            doIt = false;
 
1510
        } else {
 
1511
            selectionData()->clear();
 
1512
        }
 
1513
    }
 
1514
 
 
1515
    return doIt;
 
1516
}
 
1517
 
 
1518
 
 
1519
bool qt_check_clipboard_sentinel()
 
1520
{
 
1521
    bool doIt = true;
 
1522
    if (owner) {
 
1523
        Window *owners;
 
1524
        Atom actualType;
 
1525
        int actualFormat;
 
1526
        unsigned long nitems, bytesLeft;
 
1527
 
 
1528
        if (XGetWindowProperty(X11->display,
 
1529
                               QApplication::desktop()->screen(0)->winId(),
 
1530
                               ATOM(_QT_CLIPBOARD_SENTINEL), 0, 2, False, XA_WINDOW,
 
1531
                               &actualType, &actualFormat, &nitems, &bytesLeft,
 
1532
                               (unsigned char **) &owners) == Success) {
 
1533
            if (actualType == XA_WINDOW && actualFormat == 32 && nitems == 2) {
 
1534
                Window win = owner->winId();
 
1535
                if (owners[0] == win || owners[1] == win)
 
1536
                    doIt = false;
 
1537
            }
 
1538
 
 
1539
            XFree(owners);
 
1540
        }
 
1541
    }
 
1542
 
 
1543
    if (doIt) {
 
1544
        if (waiting_for_data) {
 
1545
            pending_clipboard_changed = true;
 
1546
            if (! pending_timer_id)
 
1547
                pending_timer_id = QApplication::clipboard()->startTimer(0);
 
1548
            doIt = false;
 
1549
        } else {
 
1550
            clipboardData()->clear();
 
1551
        }
 
1552
    }
 
1553
 
 
1554
    return doIt;
 
1555
}
 
1556
 
 
1557
#endif // QT_NO_CLIPBOARD