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

Last change on this file was 846, checked in by Dmitry A. Kuminov, 14 years ago

trunk: Merged in qt 4.7.2 sources from branches/vendor/nokia/qt.

File size: 49.2 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4** All rights reserved.
5** Contact: Nokia Corporation (qt-info@nokia.com)
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 qt-info@nokia.com.
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 "qiodevice.h"
69#include "qbuffer.h"
70#include "qtextcodec.h"
71#include "qlist.h"
72#include "qmap.h"
73#include "qapplication_p.h"
74#include "qevent.h"
75#include "qt_x11_p.h"
76#include "qx11info_x11.h"
77#include "qimagewriter.h"
78#include "qelapsedtimer.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 don't 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 // We need this value just for completeness, we don't use it.
460 long dummy = 0;
461 Window ownerId = owner->internalWinId();
462 XChangeProperty(X11->display, ownerId,
463 ATOM(CLIP_TEMPORARY), XA_INTEGER, 32,
464 PropModeReplace, (uchar*)&dummy, 1);
465 XWindowEvent(X11->display, ownerId, PropertyChangeMask, &ev);
466 data.timestamp = ev.xproperty.time;
467 XDeleteProperty(X11->display, ownerId, ATOM(CLIP_TEMPORARY));
468 }
469 X11->time = data.timestamp;
470 }
471}
472
473void QClipboard::clear(Mode mode)
474{
475 setMimeData(0, mode);
476}
477
478
479bool QClipboard::supportsMode(Mode mode) const
480{
481 return (mode == Clipboard || mode == Selection);
482}
483
484bool QClipboard::ownsMode(Mode mode) const
485{
486 if (mode == Clipboard)
487 return clipboardData()->timestamp != CurrentTime;
488 else if(mode == Selection)
489 return selectionData()->timestamp != CurrentTime;
490 else
491 return false;
492}
493
494
495// event filter function... captures interesting events while
496// qt_xclb_wait_for_event is running the event loop
497static bool qt_x11_clipboard_event_filter(void *message, long *)
498{
499 XEvent *event = reinterpret_cast<XEvent *>(message);
500 if (event->xany.type == capture_event_type &&
501 event->xany.window == capture_event_win) {
502 VDEBUG("QClipboard: event_filter(): caught event type %d", event->type);
503 has_captured_event = true;
504 captured_event = *event;
505 return true;
506 }
507 return false;
508}
509
510static Bool checkForClipboardEvents(Display *, XEvent *e, XPointer)
511{
512 return ((e->type == SelectionRequest && (e->xselectionrequest.selection == XA_PRIMARY
513 || e->xselectionrequest.selection == ATOM(CLIPBOARD)))
514 || (e->type == SelectionClear && (e->xselectionclear.selection == XA_PRIMARY
515 || e->xselectionclear.selection == ATOM(CLIPBOARD))));
516}
517
518bool QX11Data::clipboardWaitForEvent(Window win, int type, XEvent *event, int timeout)
519{
520 QElapsedTimer started;
521 started.start();
522 QElapsedTimer now = started;
523
524 if (QAbstractEventDispatcher::instance()->inherits("QtMotif")
525 || QApplication::clipboard()->property("useEventLoopWhenWaiting").toBool()) {
526 if (waiting_for_data) {
527 Q_ASSERT(!"QClipboard: internal error, qt_xclb_wait_for_event recursed");
528 return false;
529 }
530 waiting_for_data = true;
531
532
533 has_captured_event = false;
534 capture_event_win = win;
535 capture_event_type = type;
536
537 QApplication::EventFilter old_event_filter =
538 qApp->setEventFilter(qt_x11_clipboard_event_filter);
539
540 do {
541 if (XCheckTypedWindowEvent(display, win, type, event)) {
542 waiting_for_data = false;
543 qApp->setEventFilter(old_event_filter);
544 return true;
545 }
546
547 XSync(X11->display, false);
548 usleep(50000);
549
550 now.start();
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.start();
580
581 XFlush(X11->display);
582
583 // sleep 50 ms, so we don't use up CPU cycles all the time.
584 struct timeval usleep_tv;
585 usleep_tv.tv_sec = 0;
586 usleep_tv.tv_usec = 50000;
587 select(0, 0, 0, 0, &usleep_tv);
588 } while (started.msecsTo(now) < timeout);
589 }
590 return false;
591}
592
593
594static inline int maxSelectionIncr(Display *dpy)
595{ return XMaxRequestSize(dpy) > 65536 ? 65536*4 : XMaxRequestSize(dpy)*4 - 100; }
596
597bool QX11Data::clipboardReadProperty(Window win, Atom property, bool deleteProperty,
598 QByteArray *buffer, int *size, Atom *type, int *format)
599{
600 int maxsize = maxSelectionIncr(display);
601 ulong bytes_left; // bytes_after
602 ulong length; // nitems
603 uchar *data;
604 Atom dummy_type;
605 int dummy_format;
606 int r;
607
608 if (!type) // allow null args
609 type = &dummy_type;
610 if (!format)
611 format = &dummy_format;
612
613 // Don't read anything, just get the size of the property data
614 r = XGetWindowProperty(display, win, property, 0, 0, False,
615 AnyPropertyType, type, format,
616 &length, &bytes_left, &data);
617 if (r != Success || (type && *type == XNone)) {
618 buffer->resize(0);
619 return false;
620 }
621 XFree((char*)data);
622
623 int offset = 0, buffer_offset = 0, format_inc = 1, proplen = bytes_left;
624
625 VDEBUG("QClipboard: read_property(): initial property length: %d", proplen);
626
627 switch (*format) {
628 case 8:
629 default:
630 format_inc = sizeof(char) / 1;
631 break;
632
633 case 16:
634 format_inc = sizeof(short) / 2;
635 proplen *= sizeof(short) / 2;
636 break;
637
638 case 32:
639 format_inc = sizeof(long) / 4;
640 proplen *= sizeof(long) / 4;
641 break;
642 }
643
644 int newSize = proplen;
645 buffer->resize(newSize);
646
647 bool ok = (buffer->size() == newSize);
648 VDEBUG("QClipboard: read_property(): buffer resized to %d", buffer->size());
649
650 if (ok && newSize) {
651 // could allocate buffer
652
653 while (bytes_left) {
654 // more to read...
655
656 r = XGetWindowProperty(display, win, property, offset, maxsize/4,
657 False, AnyPropertyType, type, format,
658 &length, &bytes_left, &data);
659 if (r != Success || (type && *type == XNone))
660 break;
661
662 offset += length / (32 / *format);
663 length *= format_inc * (*format) / 8;
664
665 // Here we check if we get a buffer overflow and tries to
666 // recover -- this shouldn't normally happen, but it doesn't
667 // hurt to be defensive
668 if ((int)(buffer_offset + length) > buffer->size()) {
669 length = buffer->size() - buffer_offset;
670
671 // escape loop
672 bytes_left = 0;
673 }
674
675 memcpy(buffer->data() + buffer_offset, data, length);
676 buffer_offset += length;
677
678 XFree((char*)data);
679 }
680
681 if (*format == 8 && *type == ATOM(COMPOUND_TEXT)) {
682 // convert COMPOUND_TEXT to a multibyte string
683 XTextProperty textprop;
684 textprop.encoding = *type;
685 textprop.format = *format;
686 textprop.nitems = buffer_offset;
687 textprop.value = (unsigned char *) buffer->data();
688
689 char **list_ret = 0;
690 int count;
691 if (XmbTextPropertyToTextList(display, &textprop, &list_ret,
692 &count) == Success && count && list_ret) {
693 offset = buffer_offset = strlen(list_ret[0]);
694 buffer->resize(offset);
695 memcpy(buffer->data(), list_ret[0], offset);
696 }
697 if (list_ret) XFreeStringList(list_ret);
698 }
699 }
700
701 // correct size, not 0-term.
702 if (size)
703 *size = buffer_offset;
704
705 VDEBUG("QClipboard: read_property(): buffer size %d, buffer offset %d, offset %d",
706 buffer->size(), buffer_offset, offset);
707
708 if (deleteProperty)
709 XDeleteProperty(display, win, property);
710
711 XFlush(display);
712
713 return ok;
714}
715
716QByteArray QX11Data::clipboardReadIncrementalProperty(Window win, Atom property, int nbytes, bool nullterm)
717{
718 XEvent event;
719
720 QByteArray buf;
721 QByteArray tmp_buf;
722 bool alloc_error = false;
723 int length;
724 int offset = 0;
725
726 if (nbytes > 0) {
727 // Reserve buffer + zero-terminator (for text data)
728 // We want to complete the INCR transfer even if we cannot
729 // allocate more memory
730 buf.resize(nbytes+1);
731 alloc_error = buf.size() != nbytes+1;
732 }
733
734 for (;;) {
735 XFlush(display);
736 if (!clipboardWaitForEvent(win,PropertyNotify,&event,clipboard_timeout))
737 break;
738 if (event.xproperty.atom != property ||
739 event.xproperty.state != PropertyNewValue)
740 continue;
741 if (X11->clipboardReadProperty(win, property, true, &tmp_buf, &length, 0, 0)) {
742 if (length == 0) { // no more data, we're done
743 if (nullterm) {
744 buf.resize(offset+1);
745 buf[offset] = '\0';
746 } else {
747 buf.resize(offset);
748 }
749 return buf;
750 } else if (!alloc_error) {
751 if (offset+length > (int)buf.size()) {
752 buf.resize(offset+length+65535);
753 if (buf.size() != offset+length+65535) {
754 alloc_error = true;
755 length = buf.size() - offset;
756 }
757 }
758 memcpy(buf.data()+offset, tmp_buf.constData(), length);
759 tmp_buf.resize(0);
760 offset += length;
761 }
762 } else {
763 break;
764 }
765 }
766
767 // timed out ... create a new requestor window, otherwise the requestor
768 // could consider next request to be still part of this timed out request
769 delete requestor;
770 requestor = new QWidget(0);
771 requestor->setObjectName(QLatin1String("internal clipboard requestor"));
772 // We don't need this internal widget to appear in QApplication::topLevelWidgets()
773 if (QWidgetPrivate::allWidgets)
774 QWidgetPrivate::allWidgets->remove(requestor);
775
776 return QByteArray();
777}
778
779static Atom send_targets_selection(QClipboardData *d, Window window, Atom property)
780{
781 QVector<Atom> types;
782 QStringList formats = QInternalMimeData::formatsHelper(d->source());
783 for (int i = 0; i < formats.size(); ++i) {
784 QList<Atom> atoms = X11->xdndMimeAtomsForFormat(formats.at(i));
785 for (int j = 0; j < atoms.size(); ++j) {
786 if (!types.contains(atoms.at(j)))
787 types.append(atoms.at(j));
788 }
789 }
790 types.append(ATOM(TARGETS));
791 types.append(ATOM(MULTIPLE));
792 types.append(ATOM(TIMESTAMP));
793 types.append(ATOM(SAVE_TARGETS));
794
795 XChangeProperty(X11->display, window, property, XA_ATOM, 32,
796 PropModeReplace, (uchar *) types.data(), types.size());
797 return property;
798}
799
800static Atom send_selection(QClipboardData *d, Atom target, Window window, Atom property)
801{
802 Atom atomFormat = target;
803 int dataFormat = 0;
804 QByteArray data;
805
806 QByteArray fmt = X11->xdndAtomToString(target);
807 if (fmt.isEmpty()) { // Not a MIME type we have
808 DEBUG("QClipboard: send_selection(): converting to type '%s' is not supported", fmt.data());
809 return XNone;
810 }
811 DEBUG("QClipboard: send_selection(): converting to type '%s'", fmt.data());
812
813 if (X11->xdndMimeDataForAtom(target, d->source(), &data, &atomFormat, &dataFormat)) {
814
815 VDEBUG("QClipboard: send_selection():\n"
816 " property type %lx\n"
817 " property name '%s'\n"
818 " format %d\n"
819 " %d bytes\n",
820 target, X11->xdndMimeAtomToString(atomFormat).toLatin1().data(), dataFormat, data.size());
821
822 // don't allow INCR transfers when using MULTIPLE or to
823 // Motif clients (since Motif doesn't support INCR)
824 static Atom motif_clip_temporary = ATOM(CLIP_TEMPORARY);
825 bool allow_incr = property != motif_clip_temporary;
826
827 // X_ChangeProperty protocol request is 24 bytes
828 const int increment = (XMaxRequestSize(X11->display) * 4) - 24;
829 if (data.size() > increment && allow_incr) {
830 long bytes = data.size();
831 XChangeProperty(X11->display, window, property,
832 ATOM(INCR), 32, PropModeReplace, (uchar *) &bytes, 1);
833
834 (void)new QClipboardINCRTransaction(window, property, atomFormat, dataFormat, data, increment);
835 return property;
836 }
837
838 // make sure we can perform the XChangeProperty in a single request
839 if (data.size() > increment)
840 return XNone; // ### perhaps use several XChangeProperty calls w/ PropModeAppend?
841 int dataSize = data.size() / (dataFormat / 8);
842 // use a single request to transfer data
843 XChangeProperty(X11->display, window, property, atomFormat,
844 dataFormat, PropModeReplace, (uchar *) data.data(),
845 dataSize);
846 }
847 return property;
848}
849
850/*! \internal
851 Internal cleanup for Windows.
852*/
853void QClipboard::ownerDestroyed()
854{ }
855
856
857/*! \internal
858 Internal optimization for Windows.
859*/
860void QClipboard::connectNotify(const char *)
861{ }
862
863
864bool QClipboard::event(QEvent *e)
865{
866 if (e->type() == QEvent::Timer) {
867 QTimerEvent *te = (QTimerEvent *) e;
868
869 if (waiting_for_data) // should never happen
870 return false;
871
872 if (te->timerId() == timer_id) {
873 killTimer(timer_id);
874 timer_id = 0;
875
876 timer_event_clear = true;
877 if (selection_watcher) // clear selection
878 selectionData()->clear();
879 if (clipboard_watcher) // clear clipboard
880 clipboardData()->clear();
881 timer_event_clear = false;
882
883 return true;
884 } else if (te->timerId() == pending_timer_id) {
885 // I hate klipper
886 killTimer(pending_timer_id);
887 pending_timer_id = 0;
888
889 if (pending_clipboard_changed) {
890 pending_clipboard_changed = false;
891 clipboardData()->clear();
892 emitChanged(QClipboard::Clipboard);
893 }
894 if (pending_selection_changed) {
895 pending_selection_changed = false;
896 selectionData()->clear();
897 emitChanged(QClipboard::Selection);
898 }
899
900 return true;
901 } else if (te->timerId() == incr_timer_id) {
902 killTimer(incr_timer_id);
903 incr_timer_id = 0;
904
905 qt_xclb_incr_timeout();
906
907 return true;
908 } else {
909 return QObject::event(e);
910 }
911 } else if (e->type() != QEvent::Clipboard) {
912 return QObject::event(e);
913 }
914
915 XEvent *xevent = (XEvent *)(((QClipboardEvent *)e)->data());
916 Display *dpy = X11->display;
917
918 if (!xevent) {
919 // That means application exits and we need to give clipboard
920 // content to the clipboard manager.
921 // First we check if there is a clipboard manager.
922 if (XGetSelectionOwner(X11->display, ATOM(CLIPBOARD_MANAGER)) == XNone
923 || !owner)
924 return true;
925
926 Window ownerId = owner->internalWinId();
927 Q_ASSERT(ownerId);
928 // we delete the property so the manager saves all TARGETS.
929 XDeleteProperty(X11->display, ownerId, ATOM(_QT_SELECTION));
930 XConvertSelection(X11->display, ATOM(CLIPBOARD_MANAGER), ATOM(SAVE_TARGETS),
931 ATOM(_QT_SELECTION), ownerId, X11->time);
932 XSync(dpy, false);
933
934 XEvent event;
935 // waiting until the clipboard manager fetches the content.
936 if (!X11->clipboardWaitForEvent(ownerId, SelectionNotify, &event, 10000)) {
937 qWarning("QClipboard: Unable to receive an event from the "
938 "clipboard manager in a reasonable time");
939 }
940
941 return true;
942 }
943
944 switch (xevent->type) {
945
946 case SelectionClear:
947 // new selection owner
948 if (xevent->xselectionclear.selection == XA_PRIMARY) {
949 QClipboardData *d = selectionData();
950
951 // ignore the event if it was generated before we gained selection ownership
952 if (d->timestamp != CurrentTime && xevent->xselectionclear.time <= d->timestamp)
953 break;
954
955 DEBUG("QClipboard: new selection owner 0x%lx at time %lx (ours %lx)",
956 XGetSelectionOwner(dpy, XA_PRIMARY),
957 xevent->xselectionclear.time, d->timestamp);
958
959 if (! waiting_for_data) {
960 d->clear();
961 emitChanged(QClipboard::Selection);
962 } else {
963 pending_selection_changed = true;
964 if (! pending_timer_id)
965 pending_timer_id = QApplication::clipboard()->startTimer(0);
966 }
967 } else if (xevent->xselectionclear.selection == ATOM(CLIPBOARD)) {
968 QClipboardData *d = clipboardData();
969
970 // ignore the event if it was generated before we gained selection ownership
971 if (d->timestamp != CurrentTime && xevent->xselectionclear.time <= d->timestamp)
972 break;
973
974 DEBUG("QClipboard: new clipboard owner 0x%lx at time %lx (%lx)",
975 XGetSelectionOwner(dpy, ATOM(CLIPBOARD)),
976 xevent->xselectionclear.time, d->timestamp);
977
978 if (! waiting_for_data) {
979 d->clear();
980 emitChanged(QClipboard::Clipboard);
981 } else {
982 pending_clipboard_changed = true;
983 if (! pending_timer_id)
984 pending_timer_id = QApplication::clipboard()->startTimer(0);
985 }
986 } else {
987 qWarning("QClipboard: Unknown SelectionClear event received");
988 return false;
989 }
990 break;
991
992 case SelectionNotify:
993 /*
994 Something has delivered data to us, but this was not caught
995 by QClipboardWatcher::getDataInFormat()
996
997 Just skip the event to prevent Bad Things (tm) from
998 happening later on...
999 */
1000 break;
1001
1002 case SelectionRequest:
1003 {
1004 // someone wants our data
1005 XSelectionRequestEvent *req = &xevent->xselectionrequest;
1006
1007 if (requestor && req->requestor == requestor->internalWinId())
1008 break;
1009
1010 XEvent event;
1011 event.xselection.type = SelectionNotify;
1012 event.xselection.display = req->display;
1013 event.xselection.requestor = req->requestor;
1014 event.xselection.selection = req->selection;
1015 event.xselection.target = req->target;
1016 event.xselection.property = XNone;
1017 event.xselection.time = req->time;
1018
1019 DEBUG("QClipboard: SelectionRequest from %lx\n"
1020 " selection 0x%lx (%s) target 0x%lx (%s)",
1021 req->requestor,
1022 req->selection,
1023 X11->xdndAtomToString(req->selection).data(),
1024 req->target,
1025 X11->xdndAtomToString(req->target).data());
1026
1027 QClipboardData *d;
1028 if (req->selection == XA_PRIMARY) {
1029 d = selectionData();
1030 } else if (req->selection == ATOM(CLIPBOARD)) {
1031 d = clipboardData();
1032 } else {
1033 qWarning("QClipboard: Unknown selection '%lx'", req->selection);
1034 XSendEvent(dpy, req->requestor, False, NoEventMask, &event);
1035 break;
1036 }
1037
1038 if (! d->source()) {
1039 qWarning("QClipboard: Cannot transfer data, no data available");
1040 XSendEvent(dpy, req->requestor, False, NoEventMask, &event);
1041 break;
1042 }
1043
1044 DEBUG("QClipboard: SelectionRequest at time %lx (ours %lx)",
1045 req->time, d->timestamp);
1046
1047 if (d->timestamp == CurrentTime // we don't own the selection anymore
1048 || (req->time != CurrentTime && req->time < d->timestamp)) {
1049 DEBUG("QClipboard: SelectionRequest too old");
1050 XSendEvent(dpy, req->requestor, False, NoEventMask, &event);
1051 break;
1052 }
1053
1054 Atom xa_targets = ATOM(TARGETS);
1055 Atom xa_multiple = ATOM(MULTIPLE);
1056 Atom xa_timestamp = ATOM(TIMESTAMP);
1057
1058 struct AtomPair { Atom target; Atom property; } *multi = 0;
1059 Atom multi_type = XNone;
1060 int multi_format = 0;
1061 int nmulti = 0;
1062 int imulti = -1;
1063 bool multi_writeback = false;
1064
1065 if (req->target == xa_multiple) {
1066 QByteArray multi_data;
1067 if (req->property == XNone
1068 || !X11->clipboardReadProperty(req->requestor, req->property, false, &multi_data,
1069 0, &multi_type, &multi_format)
1070 || multi_format != 32) {
1071 // MULTIPLE property not formatted correctly
1072 XSendEvent(dpy, req->requestor, False, NoEventMask, &event);
1073 break;
1074 }
1075 nmulti = multi_data.size()/sizeof(*multi);
1076 multi = new AtomPair[nmulti];
1077 memcpy(multi,multi_data.data(),multi_data.size());
1078 imulti = 0;
1079 }
1080
1081 for (; imulti < nmulti; ++imulti) {
1082 Atom target;
1083 Atom property;
1084
1085 if (multi) {
1086 target = multi[imulti].target;
1087 property = multi[imulti].property;
1088 } else {
1089 target = req->target;
1090 property = req->property;
1091 if (property == XNone) // obsolete client
1092 property = target;
1093 }
1094
1095 Atom ret = XNone;
1096 if (target == XNone || property == XNone) {
1097 ;
1098 } else if (target == xa_timestamp) {
1099 if (d->timestamp != CurrentTime) {
1100 XChangeProperty(dpy, req->requestor, property, XA_INTEGER, 32,
1101 PropModeReplace, (uchar *) &d->timestamp, 1);
1102 ret = property;
1103 } else {
1104 qWarning("QClipboard: Invalid data timestamp");
1105 }
1106 } else if (target == xa_targets) {
1107 ret = send_targets_selection(d, req->requestor, property);
1108 } else {
1109 ret = send_selection(d, target, req->requestor, property);
1110 }
1111
1112 if (nmulti > 0) {
1113 if (ret == XNone) {
1114 multi[imulti].property = XNone;
1115 multi_writeback = true;
1116 }
1117 } else {
1118 event.xselection.property = ret;
1119 break;
1120 }
1121 }
1122
1123 if (nmulti > 0) {
1124 if (multi_writeback) {
1125 // according to ICCCM 2.6.2 says to put None back
1126 // into the original property on the requestor window
1127 XChangeProperty(dpy, req->requestor, req->property, multi_type, 32,
1128 PropModeReplace, (uchar *) multi, nmulti * 2);
1129 }
1130
1131 delete [] multi;
1132 event.xselection.property = req->property;
1133 }
1134
1135 // send selection notify to requestor
1136 XSendEvent(dpy, req->requestor, False, NoEventMask, &event);
1137
1138 DEBUG("QClipboard: SelectionNotify to 0x%lx\n"
1139 " property 0x%lx (%s)",
1140 req->requestor, event.xselection.property,
1141 X11->xdndAtomToString(event.xselection.property).data());
1142 }
1143 break;
1144 }
1145
1146 return true;
1147}
1148
1149
1150
1151
1152
1153
1154QClipboardWatcher::QClipboardWatcher(QClipboard::Mode mode)
1155 : QInternalMimeData()
1156{
1157 switch (mode) {
1158 case QClipboard::Selection:
1159 atom = XA_PRIMARY;
1160 break;
1161
1162 case QClipboard::Clipboard:
1163 atom = ATOM(CLIPBOARD);
1164 break;
1165
1166 default:
1167 qWarning("QClipboardWatcher: Internal error: Unsupported clipboard mode");
1168 break;
1169 }
1170
1171 setupOwner();
1172}
1173
1174QClipboardWatcher::~QClipboardWatcher()
1175{
1176 if(selection_watcher == this)
1177 selection_watcher = 0;
1178 if(clipboard_watcher == this)
1179 clipboard_watcher = 0;
1180}
1181
1182bool QClipboardWatcher::empty() const
1183{
1184 Display *dpy = X11->display;
1185 Window win = XGetSelectionOwner(dpy, atom);
1186
1187 if(win == requestor->internalWinId()) {
1188 qWarning("QClipboardWatcher::empty: Internal error: Application owns the selection");
1189 return true;
1190 }
1191
1192 return win == XNone;
1193}
1194
1195QStringList QClipboardWatcher::formats_sys() const
1196{
1197 if (empty())
1198 return QStringList();
1199
1200 if (!formatList.count()) {
1201 // get the list of targets from the current clipboard owner - we do this
1202 // once so that multiple calls to this function don't require multiple
1203 // server round trips...
1204
1205 format_atoms = getDataInFormat(ATOM(TARGETS));
1206
1207 if (format_atoms.size() > 0) {
1208 Atom *targets = (Atom *) format_atoms.data();
1209 int size = format_atoms.size() / sizeof(Atom);
1210
1211 for (int i = 0; i < size; ++i) {
1212 if (targets[i] == 0)
1213 continue;
1214
1215 QStringList formatsForAtom = X11->xdndMimeFormatsForAtom(targets[i]);
1216 for (int j = 0; j < formatsForAtom.size(); ++j) {
1217 if (!formatList.contains(formatsForAtom.at(j)))
1218 formatList.append(formatsForAtom.at(j));
1219 }
1220 VDEBUG(" format: %s", X11->xdndAtomToString(targets[i]).data());
1221 VDEBUG(" data:\n%s\n", getDataInFormat(targets[i]).data());
1222 }
1223 DEBUG("QClipboardWatcher::format: %d formats available", formatList.count());
1224 }
1225 }
1226
1227 return formatList;
1228}
1229
1230bool QClipboardWatcher::hasFormat_sys(const QString &format) const
1231{
1232 QStringList list = formats();
1233 return list.contains(format);
1234}
1235
1236QVariant QClipboardWatcher::retrieveData_sys(const QString &fmt, QVariant::Type requestedType) const
1237{
1238 if (fmt.isEmpty() || empty())
1239 return QByteArray();
1240
1241 (void)formats(); // trigger update of format list
1242 DEBUG("QClipboardWatcher::data: fetching format '%s'", fmt.toLatin1().data());
1243
1244 QList<Atom> atoms;
1245 Atom *targets = (Atom *) format_atoms.data();
1246 int size = format_atoms.size() / sizeof(Atom);
1247 for (int i = 0; i < size; ++i)
1248 atoms.append(targets[i]);
1249
1250 QByteArray encoding;
1251 Atom fmtatom = X11->xdndMimeAtomForFormat(fmt, requestedType, atoms, &encoding);
1252
1253 if (fmtatom == 0)
1254 return QVariant();
1255
1256 return X11->xdndMimeConvertToFormat(fmtatom, getDataInFormat(fmtatom), fmt, requestedType, encoding);
1257}
1258
1259QByteArray QClipboardWatcher::getDataInFormat(Atom fmtatom) const
1260{
1261 QByteArray buf;
1262
1263 Display *dpy = X11->display;
1264 requestor->createWinId();
1265 Window win = requestor->internalWinId();
1266 Q_ASSERT(requestor->testAttribute(Qt::WA_WState_Created));
1267
1268 DEBUG("QClipboardWatcher::getDataInFormat: selection '%s' format '%s'",
1269 X11->xdndAtomToString(atom).data(), X11->xdndAtomToString(fmtatom).data());
1270
1271 XSelectInput(dpy, win, NoEventMask); // don't listen for any events
1272
1273 XDeleteProperty(dpy, win, ATOM(_QT_SELECTION));
1274 XConvertSelection(dpy, atom, fmtatom, ATOM(_QT_SELECTION), win, X11->time);
1275 XSync(dpy, false);
1276
1277 VDEBUG("QClipboardWatcher::getDataInFormat: waiting for SelectionNotify event");
1278
1279 XEvent xevent;
1280 if (!X11->clipboardWaitForEvent(win,SelectionNotify,&xevent,clipboard_timeout) ||
1281 xevent.xselection.property == XNone) {
1282 DEBUG("QClipboardWatcher::getDataInFormat: format not available");
1283 return buf;
1284 }
1285
1286 VDEBUG("QClipboardWatcher::getDataInFormat: fetching data...");
1287
1288 Atom type;
1289 XSelectInput(dpy, win, PropertyChangeMask);
1290
1291 if (X11->clipboardReadProperty(win, ATOM(_QT_SELECTION), true, &buf, 0, &type, 0)) {
1292 if (type == ATOM(INCR)) {
1293 int nbytes = buf.size() >= 4 ? *((int*)buf.data()) : 0;
1294 buf = X11->clipboardReadIncrementalProperty(win, ATOM(_QT_SELECTION), nbytes, false);
1295 }
1296 }
1297
1298 XSelectInput(dpy, win, NoEventMask);
1299
1300 DEBUG("QClipboardWatcher::getDataInFormat: %d bytes received", buf.size());
1301
1302 return buf;
1303}
1304
1305
1306const QMimeData* QClipboard::mimeData(Mode mode) const
1307{
1308 QClipboardData *d = 0;
1309 switch (mode) {
1310 case Selection:
1311 d = selectionData();
1312 break;
1313 case Clipboard:
1314 d = clipboardData();
1315 break;
1316 default:
1317 qWarning("QClipboard::mimeData: unsupported mode '%d'", mode);
1318 return 0;
1319 }
1320
1321 if (! d->source() && ! timer_event_clear) {
1322 if (mode == Selection) {
1323 if (! selection_watcher)
1324 selection_watcher = new QClipboardWatcher(mode);
1325 d->setSource(selection_watcher);
1326 } else {
1327 if (! clipboard_watcher)
1328 clipboard_watcher = new QClipboardWatcher(mode);
1329 d->setSource(clipboard_watcher);
1330 }
1331
1332 if (! timer_id) {
1333 // start a zero timer - we will clear cached data when the timer
1334 // times out, which will be the next time we hit the event loop...
1335 // that way, the data is cached long enough for calls within a single
1336 // loop/function, but the data doesn't linger around in case the selection
1337 // changes
1338 QClipboard *that = ((QClipboard *) this);
1339 timer_id = that->startTimer(0);
1340 }
1341 }
1342
1343 return d->source();
1344}
1345
1346
1347void QClipboard::setMimeData(QMimeData* src, Mode mode)
1348{
1349 Atom atom, sentinel_atom;
1350 QClipboardData *d;
1351 switch (mode) {
1352 case Selection:
1353 atom = XA_PRIMARY;
1354 sentinel_atom = ATOM(_QT_SELECTION_SENTINEL);
1355 d = selectionData();
1356 break;
1357
1358 case Clipboard:
1359 atom = ATOM(CLIPBOARD);
1360 sentinel_atom = ATOM(_QT_CLIPBOARD_SENTINEL);
1361 d = clipboardData();
1362 break;
1363
1364 default:
1365 qWarning("QClipboard::setMimeData: unsupported mode '%d'", mode);
1366 return;
1367 }
1368
1369 Display *dpy = X11->display;
1370 Window newOwner;
1371
1372 if (! src) { // no data, clear clipboard contents
1373 newOwner = XNone;
1374 d->clear();
1375 } else {
1376 setupOwner();
1377
1378 newOwner = owner->internalWinId();
1379
1380 d->setSource(src);
1381 d->timestamp = X11->time;
1382 }
1383
1384 Window prevOwner = XGetSelectionOwner(dpy, atom);
1385 // use X11->time, since d->timestamp == CurrentTime when clearing
1386 XSetSelectionOwner(dpy, atom, newOwner, X11->time);
1387
1388 if (mode == Selection)
1389 emitChanged(QClipboard::Selection);
1390 else
1391 emitChanged(QClipboard::Clipboard);
1392
1393 if (XGetSelectionOwner(dpy, atom) != newOwner) {
1394 qWarning("QClipboard::setData: Cannot set X11 selection owner for %s",
1395 X11->xdndAtomToString(atom).data());
1396 d->clear();
1397 return;
1398 }
1399
1400 // Signal to other Qt processes that the selection has changed
1401 Window owners[2];
1402 owners[0] = newOwner;
1403 owners[1] = prevOwner;
1404 XChangeProperty(dpy, QApplication::desktop()->screen(0)->internalWinId(),
1405 sentinel_atom, XA_WINDOW, 32, PropModeReplace,
1406 (unsigned char*)&owners, 2);
1407}
1408
1409
1410/*
1411 Called by the main event loop in qapplication_x11.cpp when either
1412 the _QT_SELECTION_SENTINEL property has been changed (i.e. when some
1413 Qt process has performed QClipboard::setData()) or when Xfixes told
1414 us that an other application changed the selection. If it returns
1415 true, the QClipBoard dataChanged() signal should be emitted.
1416*/
1417
1418bool qt_check_selection_sentinel()
1419{
1420 bool doIt = true;
1421 if (owner && !X11->use_xfixes) {
1422 /*
1423 Since the X selection mechanism cannot give any signal when
1424 the selection has changed, we emulate it (for Qt processes) here.
1425 The notification should be ignored in case of either
1426 a) This is the process that did setData (because setData()
1427 then has already emitted dataChanged())
1428 b) This is the process that owned the selection when dataChanged()
1429 was called (because we have then received a SelectionClear event,
1430 and have already emitted dataChanged() as a result of that)
1431 */
1432
1433 unsigned char *retval;
1434 Atom actualType;
1435 int actualFormat;
1436 ulong nitems;
1437 ulong bytesLeft;
1438
1439 if (XGetWindowProperty(X11->display,
1440 QApplication::desktop()->screen(0)->internalWinId(),
1441 ATOM(_QT_SELECTION_SENTINEL), 0, 2, False, XA_WINDOW,
1442 &actualType, &actualFormat, &nitems,
1443 &bytesLeft, &retval) == Success) {
1444 Window *owners = (Window *)retval;
1445 if (actualType == XA_WINDOW && actualFormat == 32 && nitems == 2) {
1446 Window win = owner->internalWinId();
1447 if (owners[0] == win || owners[1] == win)
1448 doIt = false;
1449 }
1450
1451 XFree(owners);
1452 }
1453 }
1454
1455 if (doIt) {
1456 if (waiting_for_data) {
1457 pending_selection_changed = true;
1458 if (! pending_timer_id)
1459 pending_timer_id = QApplication::clipboard()->startTimer(0);
1460 doIt = false;
1461 } else {
1462 selectionData()->clear();
1463 }
1464 }
1465
1466 return doIt;
1467}
1468
1469
1470bool qt_check_clipboard_sentinel()
1471{
1472 bool doIt = true;
1473 if (owner && !X11->use_xfixes) {
1474 unsigned char *retval;
1475 Atom actualType;
1476 int actualFormat;
1477 unsigned long nitems, bytesLeft;
1478
1479 if (XGetWindowProperty(X11->display,
1480 QApplication::desktop()->screen(0)->internalWinId(),
1481 ATOM(_QT_CLIPBOARD_SENTINEL), 0, 2, False, XA_WINDOW,
1482 &actualType, &actualFormat, &nitems, &bytesLeft,
1483 &retval) == Success) {
1484 Window *owners = (Window *)retval;
1485 if (actualType == XA_WINDOW && actualFormat == 32 && nitems == 2) {
1486 Window win = owner->internalWinId();
1487 if (owners[0] == win || owners[1] == win)
1488 doIt = false;
1489 }
1490
1491 XFree(owners);
1492 }
1493 }
1494
1495 if (doIt) {
1496 if (waiting_for_data) {
1497 pending_clipboard_changed = true;
1498 if (! pending_timer_id)
1499 pending_timer_id = QApplication::clipboard()->startTimer(0);
1500 doIt = false;
1501 } else {
1502 clipboardData()->clear();
1503 }
1504 }
1505
1506 return doIt;
1507}
1508
1509bool qt_xfixes_selection_changed(Window selectionOwner, Time timestamp)
1510{
1511 QClipboardData *d = selectionData();
1512#ifdef QCLIPBOARD_DEBUG
1513 DEBUG("qt_xfixes_selection_changed: owner = %u; selectionOwner = %u; internal timestamp = %u; external timestamp = %u",
1514 (unsigned int)(owner ? (int)owner->internalWinId() : 0), (unsigned int)selectionOwner,
1515 (unsigned int)(d ? d->timestamp : 0), (unsigned int)timestamp);
1516#endif
1517 if (!owner || (selectionOwner && selectionOwner != owner->internalWinId()) ||
1518 (!selectionOwner && (d->timestamp == CurrentTime || d->timestamp < timestamp)))
1519 return qt_check_selection_sentinel();
1520 return false;
1521}
1522
1523bool qt_xfixes_clipboard_changed(Window clipboardOwner, Time timestamp)
1524{
1525 QClipboardData *d = clipboardData();
1526#ifdef QCLIPBOARD_DEBUG
1527 DEBUG("qt_xfixes_clipboard_changed: owner = %u; clipboardOwner = %u; internal timestamp = %u; external timestamp = %u",
1528 (unsigned int)(owner ? (int)owner->internalWinId() : 0), (unsigned int)clipboardOwner,
1529 (unsigned int)(d ? d->timestamp : 0), (unsigned int)timestamp);
1530#endif
1531 if (!owner || (clipboardOwner && clipboardOwner != owner->internalWinId()) ||
1532 (!clipboardOwner && (d->timestamp == CurrentTime || d->timestamp < timestamp)))
1533 return qt_check_clipboard_sentinel();
1534 return false;
1535}
1536
1537QT_END_NAMESPACE
1538
1539#endif // QT_NO_CLIPBOARD
Note: See TracBrowser for help on using the repository browser.