util/src/gui/kernel/qclipboard_x11.cpp
changeset 7 f7bc934e204c
equal deleted inserted replaced
3:41300fa6a67c 7:f7bc934e204c
       
     1 /****************************************************************************
       
     2 **
       
     3 ** Copyright (C) 2010 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 ** No Commercial Usage
       
    11 ** This file contains pre-release code and may not be distributed.
       
    12 ** You may use this file in accordance with the terms and conditions
       
    13 ** contained in the Technology Preview License Agreement accompanying
       
    14 ** this package.
       
    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 ** If you have questions regarding the use of this file, please contact
       
    29 ** Nokia at qt-info@nokia.com.
       
    30 **
       
    31 **
       
    32 **
       
    33 **
       
    34 **
       
    35 **
       
    36 **
       
    37 **
       
    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 
       
    87 QT_BEGIN_NAMESPACE
       
    88 
       
    89 /*****************************************************************************
       
    90   Internal QClipboard functions for X11.
       
    91  *****************************************************************************/
       
    92 
       
    93 static int clipboard_timeout = 5000; // 5s timeout on clipboard operations
       
    94 
       
    95 static QWidget * owner = 0;
       
    96 static QWidget *requestor = 0;
       
    97 static bool timer_event_clear = false;
       
    98 static int timer_id = 0;
       
    99 
       
   100 static int pending_timer_id = 0;
       
   101 static bool pending_clipboard_changed = false;
       
   102 static bool pending_selection_changed = false;
       
   103 
       
   104 
       
   105 // event capture mechanism for qt_xclb_wait_for_event
       
   106 static bool waiting_for_data = false;
       
   107 static bool has_captured_event = false;
       
   108 static Window capture_event_win = XNone;
       
   109 static int capture_event_type = -1;
       
   110 static XEvent captured_event;
       
   111 
       
   112 class QClipboardWatcher; // forward decl
       
   113 static QClipboardWatcher *selection_watcher = 0;
       
   114 static QClipboardWatcher *clipboard_watcher = 0;
       
   115 
       
   116 static void cleanup()
       
   117 {
       
   118     delete owner;
       
   119     delete requestor;
       
   120     owner = 0;
       
   121     requestor = 0;
       
   122 }
       
   123 
       
   124 static
       
   125 void 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 
       
   144 class QClipboardWatcher : public QInternalMimeData {
       
   145 public:
       
   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 
       
   160 class QClipboardData
       
   161 {
       
   162 private:
       
   163     QMimeData *&mimeDataRef() const
       
   164     {
       
   165         if(mode == QClipboard::Selection)
       
   166             return selectionData;
       
   167         return clipboardData;
       
   168     }
       
   169 
       
   170 public:
       
   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 
       
   211 QMimeData *QClipboardData::selectionData = 0;
       
   212 QMimeData *QClipboardData::clipboardData = 0;
       
   213 
       
   214 QClipboardData::QClipboardData(QClipboard::Mode clipboardMode)
       
   215 {
       
   216     timestamp = CurrentTime;
       
   217     mode = clipboardMode;
       
   218 }
       
   219 
       
   220 QClipboardData::~QClipboardData()
       
   221 { clear(); }
       
   222 
       
   223 
       
   224 static QClipboardData *internalCbData = 0;
       
   225 static QClipboardData *internalSelData = 0;
       
   226 
       
   227 static void cleanupClipboardData()
       
   228 {
       
   229     delete internalCbData;
       
   230     internalCbData = 0;
       
   231 }
       
   232 
       
   233 static QClipboardData *clipboardData()
       
   234 {
       
   235     if (internalCbData == 0) {
       
   236         internalCbData = new QClipboardData(QClipboard::Clipboard);
       
   237         qAddPostRoutine(cleanupClipboardData);
       
   238     }
       
   239     return internalCbData;
       
   240 }
       
   241 
       
   242 static void cleanupSelectionData()
       
   243 {
       
   244     delete internalSelData;
       
   245     internalSelData = 0;
       
   246 }
       
   247 
       
   248 static QClipboardData *selectionData()
       
   249 {
       
   250     if (internalSelData == 0) {
       
   251         internalSelData = new QClipboardData(QClipboard::Selection);
       
   252         qAddPostRoutine(cleanupSelectionData);
       
   253     }
       
   254     return internalSelData;
       
   255 }
       
   256 
       
   257 class QClipboardINCRTransaction
       
   258 {
       
   259 public:
       
   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 
       
   273 typedef QMap<Window,QClipboardINCRTransaction*> TransactionMap;
       
   274 static TransactionMap *transactions = 0;
       
   275 static QApplication::EventFilter prev_event_filter = 0;
       
   276 static int incr_timer_id = 0;
       
   277 
       
   278 static 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 */
       
   296 static 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 
       
   304 QClipboardINCRTransaction::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 
       
   321 QClipboardINCRTransaction::~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 
       
   342 int 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 
       
   377 struct qt_init_timestamp_data
       
   378 {
       
   379     Time timestamp;
       
   380 };
       
   381 
       
   382 #if defined(Q_C_CALLBACKS)
       
   383 extern "C" {
       
   384 #endif
       
   385 
       
   386 static 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 
       
   430 QClipboard::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 
       
   472 void QClipboard::clear(Mode mode)
       
   473 {
       
   474     setMimeData(0, mode);
       
   475 }
       
   476 
       
   477 
       
   478 bool QClipboard::supportsMode(Mode mode) const
       
   479 {
       
   480     return (mode == Clipboard || mode == Selection);
       
   481 }
       
   482 
       
   483 bool 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
       
   496 static 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 
       
   509 static 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 
       
   517 bool 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 
       
   596 static inline int maxSelectionIncr(Display *dpy)
       
   597 { return XMaxRequestSize(dpy) > 65536 ? 65536*4 : XMaxRequestSize(dpy)*4 - 100; }
       
   598 
       
   599 bool 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 
       
   722 QByteArray 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 
       
   785 static 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 
       
   806 static 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 */
       
   859 void QClipboard::ownerDestroyed()
       
   860 { }
       
   861 
       
   862 
       
   863 /*! \internal
       
   864     Internal optimization for Windows.
       
   865 */
       
   866 void QClipboard::connectNotify(const char *)
       
   867 { }
       
   868 
       
   869 
       
   870 bool 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 
       
  1160 QClipboardWatcher::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 
       
  1180 QClipboardWatcher::~QClipboardWatcher()
       
  1181 {
       
  1182     if(selection_watcher == this)
       
  1183         selection_watcher = 0;
       
  1184     if(clipboard_watcher == this)
       
  1185         clipboard_watcher = 0;
       
  1186 }
       
  1187 
       
  1188 bool 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 
       
  1201 QStringList 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 
       
  1236 bool QClipboardWatcher::hasFormat_sys(const QString &format) const
       
  1237 {
       
  1238     QStringList list = formats();
       
  1239     return list.contains(format);
       
  1240 }
       
  1241 
       
  1242 QVariant 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 
       
  1265 QByteArray 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 
       
  1312 const 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 
       
  1353 void 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 
       
  1424 bool 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 
       
  1476 bool 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 
       
  1515 bool 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 
       
  1529 bool 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 
       
  1543 QT_END_NAMESPACE
       
  1544 
       
  1545 #endif // QT_NO_CLIPBOARD