source: trunk/src/gui/kernel/qclipboard_x11.cpp@ 786

Last change on this file since 786 was 651, checked in by Dmitry A. Kuminov, 15 years ago

trunk: Merged in qt 4.6.2 sources.

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