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