00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027 #ifdef QCLIPBOARD_DEBUG
00028 # define DEBUG qDebug
00029 #else
00030 # define DEBUG if (false) qDebug
00031 #endif
00032
00033 #ifdef QCLIPBOARD_DEBUG_VERBOSE
00034 # define VDEBUG qDebug
00035 #else
00036 # define VDEBUG if (false) qDebug
00037 #endif
00038
00039 #include "qplatformdefs.h"
00040
00041 #include "qclipboard.h"
00042
00043 #ifndef QT_NO_CLIPBOARD
00044
00045 #include "qabstracteventdispatcher.h"
00046 #include "qapplication.h"
00047 #include "qdesktopwidget.h"
00048 #include "qbitmap.h"
00049 #include "qdatetime.h"
00050 #include "qiodevice.h"
00051 #include "qbuffer.h"
00052 #include "qtextcodec.h"
00053 #include "qlist.h"
00054 #include "qmap.h"
00055 #include "qapplication_p.h"
00056 #include "qevent.h"
00057 #include "qt_x11_p.h"
00058 #include "qx11info_x11.h"
00059 #include "qimagewriter.h"
00060 #include "qvariant.h"
00061 #include "qdnd_p.h"
00062
00063
00064
00065
00066
00067 static int clipboard_timeout = 5000;
00068
00069 static QWidget * owner = 0;
00070 static QWidget *requestor = 0;
00071 static bool timer_event_clear = false;
00072 static int timer_id = 0;
00073
00074 static int pending_timer_id = 0;
00075 static bool pending_clipboard_changed = false;
00076 static bool pending_selection_changed = false;
00077
00078
00079
00080 static bool waiting_for_data = false;
00081 static bool has_captured_event = false;
00082 static Window capture_event_win = XNone;
00083 static int capture_event_type = -1;
00084 static XEvent captured_event;
00085
00086 class QClipboardWatcher;
00087 static QClipboardWatcher *selection_watcher = 0;
00088 static QClipboardWatcher *clipboard_watcher = 0;
00089
00090 static void cleanup()
00091 {
00092 delete owner;
00093 delete requestor;
00094 owner = 0;
00095 requestor = 0;
00096 }
00097
00098 static
00099 void setupOwner()
00100 {
00101 if (owner)
00102 return;
00103 owner = new QWidget(0);
00104 owner->setObjectName(QLatin1String("internal clipboard owner"));
00105 owner->createWinId();
00106 requestor = new QWidget(0);
00107 requestor->createWinId();
00108 requestor->setObjectName(QLatin1String("internal clipboard requestor"));
00109 qAddPostRoutine(cleanup);
00110 }
00111
00112
00113 class QClipboardWatcher : public QInternalMimeData {
00114 public:
00115 QClipboardWatcher(QClipboard::Mode mode);
00116 ~QClipboardWatcher();
00117 bool empty() const;
00118 virtual bool hasFormat_sys(const QString &mimetype) const;
00119 virtual QStringList formats_sys() const;
00120
00121 QVariant retrieveData_sys(const QString &mimetype, QVariant::Type type) const;
00122 QByteArray getDataInFormat(Atom fmtatom) const;
00123
00124 Atom atom;
00125 mutable QStringList formatList;
00126 mutable QByteArray format_atoms;
00127 };
00128
00129
00130
00131 class QClipboardData
00132 {
00133 public:
00134 QClipboardData();
00135 ~QClipboardData();
00136
00137 void setSource(QMimeData* s)
00138 {
00139 delete src;
00140 src = s;
00141 }
00142
00143 QMimeData *source() const { return src; }
00144
00145 void clear();
00146
00147 QMimeData *src;
00148 Time timestamp;
00149 };
00150
00151 QClipboardData::QClipboardData()
00152 {
00153 src = 0;
00154 timestamp = CurrentTime;
00155 }
00156
00157 QClipboardData::~QClipboardData()
00158 { clear(); }
00159
00160 void QClipboardData::clear()
00161 {
00162 delete src;
00163 src = 0;
00164 timestamp = CurrentTime;
00165 }
00166
00167
00168 static QClipboardData *internalCbData = 0;
00169 static QClipboardData *internalSelData = 0;
00170
00171 static void cleanupClipboardData()
00172 {
00173 delete internalCbData;
00174 internalCbData = 0;
00175 }
00176
00177 static QClipboardData *clipboardData()
00178 {
00179 if (internalCbData == 0) {
00180 internalCbData = new QClipboardData;
00181 qAddPostRoutine(cleanupClipboardData);
00182 }
00183 return internalCbData;
00184 }
00185
00186 static void cleanupSelectionData()
00187 {
00188 delete internalSelData;
00189 internalSelData = 0;
00190 }
00191
00192 static QClipboardData *selectionData()
00193 {
00194 if (internalSelData == 0) {
00195 internalSelData = new QClipboardData;
00196 qAddPostRoutine(cleanupSelectionData);
00197 }
00198 return internalSelData;
00199 }
00200
00201 class QClipboardINCRTransaction
00202 {
00203 public:
00204 QClipboardINCRTransaction(Window w, Atom p, Atom t, int f, QByteArray d, unsigned int i);
00205 ~QClipboardINCRTransaction(void);
00206
00207 int x11Event(XEvent *event);
00208
00209 Window window;
00210 Atom property, target;
00211 int format;
00212 QByteArray data;
00213 unsigned int increment;
00214 unsigned int offset;
00215 };
00216
00217 typedef QMap<Window,QClipboardINCRTransaction*> TransactionMap;
00218 static TransactionMap *transactions = 0;
00219 static QApplication::EventFilter prev_event_filter = 0;
00220 static int incr_timer_id = 0;
00221
00222 static bool qt_x11_incr_event_filter(void *message, long *result)
00223 {
00224 XEvent *event = reinterpret_cast<XEvent *>(message);
00225 TransactionMap::Iterator it = transactions->find(event->xany.window);
00226 if (it != transactions->end()) {
00227 if ((*it)->x11Event(event) != 0)
00228 return true;
00229 }
00230 if (prev_event_filter)
00231 return prev_event_filter(event, result);
00232 return false;
00233 }
00234
00235
00236
00237
00238
00239
00240 static void qt_xclb_incr_timeout(void)
00241 {
00242 qWarning("QClipboard: Timed out while sending data");
00243
00244 while (transactions)
00245 delete *transactions->begin();
00246 }
00247
00248 QClipboardINCRTransaction::QClipboardINCRTransaction(Window w, Atom p, Atom t, int f,
00249 QByteArray d, unsigned int i)
00250 : window(w), property(p), target(t), format(f), data(d), increment(i), offset(0u)
00251 {
00252 DEBUG("QClipboard: sending %d bytes (INCR transaction %p)", d.size(), this);
00253
00254 XSelectInput(X11->display, window, PropertyChangeMask);
00255
00256 if (! transactions) {
00257 VDEBUG("QClipboard: created INCR transaction map");
00258 transactions = new TransactionMap;
00259 prev_event_filter = qApp->setEventFilter(qt_x11_incr_event_filter);
00260 incr_timer_id = QApplication::clipboard()->startTimer(clipboard_timeout);
00261 }
00262 transactions->insert(window, this);
00263 }
00264
00265 QClipboardINCRTransaction::~QClipboardINCRTransaction(void)
00266 {
00267 VDEBUG("QClipboard: destroyed INCR transacton %p", this);
00268
00269 XSelectInput(X11->display, window, NoEventMask);
00270
00271 transactions->remove(window);
00272 if (transactions->isEmpty()) {
00273 VDEBUG("QClipboard: no more INCR transactions");
00274 delete transactions;
00275 transactions = 0;
00276
00277 (void)qApp->setEventFilter(prev_event_filter);
00278
00279 if (incr_timer_id != 0) {
00280 QApplication::clipboard()->killTimer(incr_timer_id);
00281 incr_timer_id = 0;
00282 }
00283 }
00284 }
00285
00286 int QClipboardINCRTransaction::x11Event(XEvent *event)
00287 {
00288 if (event->type != PropertyNotify
00289 || (event->xproperty.state != PropertyDelete
00290 || event->xproperty.atom != property))
00291 return 0;
00292
00293
00294 if (incr_timer_id) QApplication::clipboard()->killTimer(incr_timer_id);
00295 incr_timer_id = QApplication::clipboard()->startTimer(clipboard_timeout);
00296
00297 unsigned int bytes_left = data.size() - offset;
00298 if (bytes_left > 0) {
00299 unsigned int xfer = qMin(increment, bytes_left);
00300 VDEBUG("QClipboard: sending %d bytes, %d remaining (INCR transaction %p)",
00301 xfer, bytes_left - xfer, this);
00302
00303 XChangeProperty(X11->display, window, property, target, format,
00304 PropModeReplace, (uchar *) data.data() + offset, xfer);
00305 offset += xfer;
00306 } else {
00307
00308 XChangeProperty(X11->display, window, property, target, format,
00309 PropModeReplace, (uchar *) data.data(), 0);
00310 delete this;
00311 }
00312
00313 return 1;
00314 }
00315
00316
00317
00318
00319
00320
00321
00322 void QClipboard::clear(Mode mode)
00323 {
00324 setMimeData(0, mode);
00325 }
00326
00327
00328 bool QClipboard::supportsMode(Mode mode) const
00329 {
00330 return (mode == Clipboard || mode == Selection);
00331 }
00332
00333 bool QClipboard::ownsMode(Mode mode) const
00334 {
00335 if (mode == Clipboard)
00336 return clipboardData()->timestamp != CurrentTime;
00337 else if(mode == Selection)
00338 return selectionData()->timestamp != CurrentTime;
00339 else
00340 return false;
00341 }
00342
00343
00344
00345
00346 static bool qt_x11_clipboard_event_filter(void *message, long *)
00347 {
00348 XEvent *event = reinterpret_cast<XEvent *>(message);
00349 if (event->xany.type == capture_event_type &&
00350 event->xany.window == capture_event_win) {
00351 VDEBUG("QClipboard: event_filter(): caught event type %d", event->type);
00352 has_captured_event = true;
00353 captured_event = *event;
00354 return true;
00355 }
00356 return false;
00357 }
00358
00359 bool QX11Data::clipboardWaitForEvent(Window win, int type, XEvent *event, int timeout)
00360 {
00361 QTime started = QTime::currentTime();
00362 QTime now = started;
00363
00364 if (QAbstractEventDispatcher::instance()->inherits("QMotif")) {
00365 if (waiting_for_data)
00366 qFatal("QClipboard: internal error, qt_xclb_wait_for_event recursed");
00367 waiting_for_data = true;
00368
00369
00370 has_captured_event = false;
00371 capture_event_win = win;
00372 capture_event_type = type;
00373
00374 QApplication::EventFilter old_event_filter =
00375 qApp->setEventFilter(qt_x11_clipboard_event_filter);
00376
00377 do {
00378 if (XCheckTypedWindowEvent(display, win, type, event)) {
00379 waiting_for_data = false;
00380 qApp->setEventFilter(old_event_filter);
00381 return true;
00382 }
00383
00384 XSync(X11->display, false);
00385 usleep(50000);
00386
00387 now = QTime::currentTime();
00388 if (started > now)
00389 started = now;
00390
00391 QEventLoop::ProcessEventsFlags flags(QEventLoop::ExcludeUserInputEvents
00392 | QEventLoop::ExcludeSocketNotifiers
00393 | QEventLoop::WaitForMoreEvents
00394 | QEventLoop::X11ExcludeTimers);
00395 QAbstractEventDispatcher *eventDispatcher = QAbstractEventDispatcher::instance();
00396 eventDispatcher->processEvents(flags);
00397
00398 if (has_captured_event) {
00399 waiting_for_data = false;
00400 *event = captured_event;
00401 qApp->setEventFilter(old_event_filter);
00402 return true;
00403 }
00404 } while (started.msecsTo(now) < timeout);
00405
00406 waiting_for_data = false;
00407 qApp->setEventFilter(old_event_filter);
00408 } else {
00409 do {
00410 if (XCheckTypedWindowEvent(X11->display,win,type,event))
00411 return true;
00412
00413 now = QTime::currentTime();
00414 if ( started > now )
00415 started = now;
00416
00417 XFlush(X11->display);
00418
00419
00420 struct timeval usleep_tv;
00421 usleep_tv.tv_sec = 0;
00422 usleep_tv.tv_usec = 50000;
00423 select(0, 0, 0, 0, &usleep_tv);
00424 } while (started.msecsTo(now) < timeout);
00425 }
00426 return false;
00427 }
00428
00429
00430 static inline int maxSelectionIncr(Display *dpy)
00431 { return XMaxRequestSize(dpy) > 65536 ? 65536*4 : XMaxRequestSize(dpy)*4 - 100; }
00432
00433 bool QX11Data::clipboardReadProperty(Window win, Atom property, bool deleteProperty,
00434 QByteArray *buffer, int *size, Atom *type, int *format, bool nullterm)
00435 {
00436 int maxsize = maxSelectionIncr(display);
00437 ulong bytes_left;
00438 ulong length;
00439 uchar *data;
00440 Atom dummy_type;
00441 int dummy_format;
00442 int r;
00443
00444 if (!type)
00445 type = &dummy_type;
00446 if (!format)
00447 format = &dummy_format;
00448
00449
00450 r = XGetWindowProperty(display, win, property, 0, 0, False,
00451 AnyPropertyType, type, format,
00452 &length, &bytes_left, &data);
00453 if (r != Success || (type && *type == XNone)) {
00454 buffer->resize(0);
00455 return false;
00456 }
00457 XFree((char*)data);
00458
00459 int offset = 0, buffer_offset = 0, format_inc = 1, proplen = bytes_left;
00460
00461 VDEBUG("QClipboard: read_property(): initial property length: %d", proplen);
00462
00463 switch (*format) {
00464 case 8:
00465 default:
00466 format_inc = sizeof(char) / 1;
00467 break;
00468
00469 case 16:
00470 format_inc = sizeof(short) / 2;
00471 proplen *= sizeof(short) / 2;
00472 break;
00473
00474 case 32:
00475 format_inc = sizeof(long) / 4;
00476 proplen *= sizeof(long) / 4;
00477 break;
00478 }
00479
00480 int newSize = proplen + (nullterm ? 1 : 0);
00481 buffer->resize(newSize);
00482
00483 bool ok = (buffer->size() == newSize);
00484 VDEBUG("QClipboard: read_property(): buffer resized to %d", buffer->size());
00485
00486 if (ok) {
00487
00488
00489 while (bytes_left) {
00490
00491
00492 r = XGetWindowProperty(display, win, property, offset, maxsize/4,
00493 False, AnyPropertyType, type, format,
00494 &length, &bytes_left, &data);
00495 if (r != Success || (type && *type == XNone))
00496 break;
00497
00498 offset += length / (32 / *format);
00499 length *= format_inc * (*format) / 8;
00500
00501
00502
00503
00504 if ((int)(buffer_offset + length) > buffer->size()) {
00505 length = buffer->size() - buffer_offset;
00506
00507
00508 bytes_left = 0;
00509 }
00510
00511 memcpy(buffer->data() + buffer_offset, data, length);
00512 buffer_offset += length;
00513
00514 XFree((char*)data);
00515 }
00516
00517 if (*format == 8 && *type == ATOM(COMPOUND_TEXT)) {
00518
00519 XTextProperty textprop;
00520 textprop.encoding = *type;
00521 textprop.format = *format;
00522 textprop.nitems = length;
00523 textprop.value = (unsigned char *) buffer->data();
00524
00525 char **list_ret = 0;
00526 int count;
00527 if (XmbTextPropertyToTextList(display, &textprop, &list_ret,
00528 &count) == Success && count && list_ret) {
00529 offset = strlen(list_ret[0]);
00530 buffer->resize(offset + (nullterm ? 1 : 0));
00531 memcpy(buffer->data(), list_ret[0], offset);
00532 }
00533 if (list_ret) XFreeStringList(list_ret);
00534 }
00535
00536
00537 if (nullterm)
00538 buffer->data()[buffer_offset] = '\0';
00539 }
00540
00541
00542 if (size)
00543 *size = buffer_offset;
00544
00545 VDEBUG("QClipboard: read_property(): buffer size %d, buffer offset %d, offset %d",
00546 buffer->size(), buffer_offset, offset);
00547
00548 if (deleteProperty)
00549 XDeleteProperty(display, win, property);
00550
00551 XFlush(display);
00552
00553 return ok;
00554 }
00555
00556 QByteArray QX11Data::clipboardReadIncrementalProperty(Window win, Atom property, int nbytes, bool nullterm)
00557 {
00558 XEvent event;
00559
00560 QByteArray buf;
00561 QByteArray tmp_buf;
00562 bool alloc_error = false;
00563 int length;
00564 int offset = 0;
00565
00566 if (nbytes > 0) {
00567
00568
00569
00570 buf.resize(nbytes+1);
00571 alloc_error = buf.size() != nbytes+1;
00572 }
00573
00574 for (;;) {
00575 XFlush(display);
00576 if (!clipboardWaitForEvent(win,PropertyNotify,&event,clipboard_timeout))
00577 break;
00578 if (event.xproperty.atom != property ||
00579 event.xproperty.state != PropertyNewValue)
00580 continue;
00581 if (X11->clipboardReadProperty(win, property, true, &tmp_buf, &length, 0, 0, false)) {
00582 if (length == 0) {
00583 if (nullterm) {
00584 buf.resize(offset+1);
00585 buf[offset] = '\0';
00586 } else {
00587 buf.resize(offset);
00588 }
00589 return buf;
00590 } else if (!alloc_error) {
00591 if (offset+length > (int)buf.size()) {
00592 buf.resize(offset+length+65535);
00593 if (buf.size() != offset+length+65535) {
00594 alloc_error = true;
00595 length = buf.size() - offset;
00596 }
00597 }
00598 memcpy(buf.data()+offset, tmp_buf.constData(), length);
00599 tmp_buf.resize(0);
00600 offset += length;
00601 }
00602 } else {
00603 break;
00604 }
00605 }
00606
00607
00608
00609 delete requestor;
00610 requestor = new QWidget(0);
00611 requestor->setObjectName(QLatin1String("internal clipboard requestor"));
00612
00613 return QByteArray();
00614 }
00615
00616 static Atom send_targets_selection(QClipboardData *d, Window window, Atom property)
00617 {
00618 QVector<Atom> types;
00619 QStringList formats = QInternalMimeData::formatsHelper(d->source());
00620 for (int i = 0; i < formats.size(); ++i) {
00621 QList<Atom> atoms = X11->xdndMimeAtomsForFormat(formats.at(i));
00622 for (int j = 0; j < atoms.size(); ++j) {
00623 if (!types.contains(atoms.at(j)))
00624 types.append(atoms.at(j));
00625 }
00626 }
00627 types.append(ATOM(TARGETS));
00628 types.append(ATOM(MULTIPLE));
00629 types.append(ATOM(TIMESTAMP));
00630
00631 XChangeProperty(X11->display, window, property, XA_ATOM, 32,
00632 PropModeReplace, (uchar *) types.data(), types.size());
00633 return property;
00634 }
00635
00636 static Atom send_selection(QClipboardData *d, Atom target, Window window, Atom property)
00637 {
00638 Atom atomFormat = target;
00639 int dataFormat = 0;
00640 QByteArray data;
00641 if (X11->xdndMimeDataForAtom(target, d->source(), &data, &atomFormat, &dataFormat)) {
00642
00643 VDEBUG("QClipboard: send_selection():\n"
00644 " property type %lx\n"
00645 " property name '%s'\n"
00646 " format %d\n"
00647 " %d bytes\n",
00648 target, X11->xdndMimeAtomToString(atomFormat).toLatin1().data(), dataFormat, data.size());
00649
00650
00651
00652 static Atom motif_clip_temporary = ATOM(CLIP_TEMPORARY);
00653 bool allow_incr = property != motif_clip_temporary;
00654
00655
00656 const int increment = (XMaxRequestSize(X11->display) * 4) - 24;
00657 if (data.size() > increment && allow_incr) {
00658 long bytes = data.size();
00659 XChangeProperty(X11->display, window, property,
00660 ATOM(INCR), 32, PropModeReplace, (uchar *) &bytes, 1);
00661
00662 (void)new QClipboardINCRTransaction(window, property, atomFormat, dataFormat, data, increment);
00663 return ATOM(INCR);
00664 }
00665
00666
00667 if (data.size() > increment)
00668 return XNone;
00669 int dataSize = data.size() / (dataFormat / 8);
00670
00671 XChangeProperty(X11->display, window, property, atomFormat,
00672 dataFormat, PropModeReplace, (uchar *) data.data(),
00673 dataSize);
00674 }
00675 return property;
00676 }
00677
00681 void QClipboard::ownerDestroyed()
00682 { }
00683
00684
00688 void QClipboard::connectNotify(const char *)
00689 { }
00690
00691
00694 bool QClipboard::event(QEvent *e)
00695 {
00696 if (e->type() == QEvent::Timer) {
00697 QTimerEvent *te = (QTimerEvent *) e;
00698
00699 if (waiting_for_data)
00700 return false;
00701
00702 if (te->timerId() == timer_id) {
00703 killTimer(timer_id);
00704 timer_id = 0;
00705
00706 timer_event_clear = true;
00707 if (selection_watcher)
00708 selectionData()->clear();
00709 if (clipboard_watcher)
00710 clipboardData()->clear();
00711 timer_event_clear = false;
00712
00713 return true;
00714 } else if (te->timerId() == pending_timer_id) {
00715
00716 killTimer(pending_timer_id);
00717 pending_timer_id = 0;
00718
00719 if (pending_clipboard_changed) {
00720 pending_clipboard_changed = false;
00721 clipboardData()->clear();
00722 emitChanged(QClipboard::Clipboard);
00723 }
00724 if (pending_selection_changed) {
00725 pending_selection_changed = false;
00726 selectionData()->clear();
00727 emitChanged(QClipboard::Selection);
00728 }
00729
00730 return true;
00731 } else if (te->timerId() == incr_timer_id) {
00732 killTimer(incr_timer_id);
00733 incr_timer_id = 0;
00734
00735 qt_xclb_incr_timeout();
00736
00737 return true;
00738 } else {
00739 return QObject::event(e);
00740 }
00741 } else if (e->type() != QEvent::Clipboard) {
00742 return QObject::event(e);
00743 }
00744
00745 XEvent *xevent = (XEvent *)(((QClipboardEvent *)e)->data());
00746 Display *dpy = X11->display;
00747
00748 if (!xevent)
00749 return true;
00750
00751 switch (xevent->type) {
00752
00753 case SelectionClear:
00754
00755 if (xevent->xselectionclear.selection == XA_PRIMARY) {
00756 QClipboardData *d = selectionData();
00757
00758
00759 if (d->timestamp != CurrentTime && xevent->xselectionclear.time < d->timestamp)
00760 break;
00761
00762 DEBUG("QClipboard: new selection owner 0x%lx at time %lx (ours %lx)",
00763 XGetSelectionOwner(dpy, XA_PRIMARY),
00764 xevent->xselectionclear.time, d->timestamp);
00765
00766 if (! waiting_for_data) {
00767 d->clear();
00768 emitChanged(QClipboard::Selection);
00769 } else {
00770 pending_selection_changed = true;
00771 if (! pending_timer_id)
00772 pending_timer_id = QApplication::clipboard()->startTimer(0);
00773 }
00774 } else if (xevent->xselectionclear.selection == ATOM(CLIPBOARD)) {
00775 QClipboardData *d = clipboardData();
00776
00777
00778 if (d->timestamp != CurrentTime && xevent->xselectionclear.time < d->timestamp)
00779 break;
00780
00781 DEBUG("QClipboard: new clipboard owner 0x%lx at time %lx (%lx)",
00782 XGetSelectionOwner(dpy, ATOM(CLIPBOARD)),
00783 xevent->xselectionclear.time, d->timestamp);
00784
00785 if (! waiting_for_data) {
00786 d->clear();
00787 emitChanged(QClipboard::Clipboard);
00788 } else {
00789 pending_clipboard_changed = true;
00790 if (! pending_timer_id)
00791 pending_timer_id = QApplication::clipboard()->startTimer(0);
00792 }
00793 } else {
00794 qWarning("QClipboard: Unknown SelectionClear event received");
00795 return false;
00796 }
00797 break;
00798
00799 case SelectionNotify:
00800
00801
00802
00803
00804
00805
00806
00807 break;
00808
00809 case SelectionRequest:
00810 {
00811
00812 XSelectionRequestEvent *req = &xevent->xselectionrequest;
00813
00814 if (requestor && req->requestor == requestor->internalWinId())
00815 break;
00816
00817 XEvent event;
00818 event.xselection.type = SelectionNotify;
00819 event.xselection.display = req->display;
00820 event.xselection.requestor = req->requestor;
00821 event.xselection.selection = req->selection;
00822 event.xselection.target = req->target;
00823 event.xselection.property = XNone;
00824 event.xselection.time = req->time;
00825
00826 DEBUG("QClipboard: SelectionRequest from %lx\n"
00827 " selection 0x%lx (%s) target 0x%lx (%s)",
00828 req->requestor,
00829 req->selection,
00830 X11->xdndAtomToString(req->selection).data(),
00831 req->target,
00832 X11->xdndAtomToString(req->target).data());
00833
00834 QClipboardData *d;
00835 if (req->selection == XA_PRIMARY) {
00836 d = selectionData();
00837 } else if (req->selection == ATOM(CLIPBOARD)) {
00838 d = clipboardData();
00839 } else {
00840 qWarning("QClipboard: Unknown selection '%lx'", req->selection);
00841 XSendEvent(dpy, req->requestor, False, NoEventMask, &event);
00842 break;
00843 }
00844
00845 if (! d->source()) {
00846 qWarning("QClipboard: Cannot transfer data, no data available");
00847 XSendEvent(dpy, req->requestor, False, NoEventMask, &event);
00848 break;
00849 }
00850
00851 DEBUG("QClipboard: SelectionRequest at time %lx (ours %lx)",
00852 req->time, d->timestamp);
00853
00854 if (d->timestamp == CurrentTime
00855 || (req->time != CurrentTime && req->time < d->timestamp)) {
00856 DEBUG("QClipboard: SelectionRequest too old");
00857 XSendEvent(dpy, req->requestor, False, NoEventMask, &event);
00858 break;
00859 }
00860
00861 Atom xa_targets = ATOM(TARGETS);
00862 Atom xa_multiple = ATOM(MULTIPLE);
00863 Atom xa_timestamp = ATOM(TIMESTAMP);
00864
00865 struct AtomPair { Atom target; Atom property; } *multi = 0;
00866 Atom multi_type = XNone;
00867 int multi_format = 0;
00868 int nmulti = 0;
00869 int imulti = -1;
00870 bool multi_writeback = false;
00871
00872 if (req->target == xa_multiple) {
00873 QByteArray multi_data;
00874 if (req->property == XNone
00875 || !X11->clipboardReadProperty(req->requestor, req->property, false, &multi_data,
00876 0, &multi_type, &multi_format, 0)
00877 || multi_format != 32) {
00878
00879 XSendEvent(dpy, req->requestor, False, NoEventMask, &event);
00880 break;
00881 }
00882 nmulti = multi_data.size()/sizeof(*multi);
00883 multi = new AtomPair[nmulti];
00884 memcpy(multi,multi_data.data(),multi_data.size());
00885 imulti = 0;
00886 }
00887
00888 for (; imulti < nmulti; ++imulti) {
00889 Atom target;
00890 Atom property;
00891
00892 if (multi) {
00893 target = multi[imulti].target;
00894 property = multi[imulti].property;
00895 } else {
00896 target = req->target;
00897 property = req->property;
00898 if (property == XNone)
00899 property = target;
00900 }
00901
00902 Atom ret = XNone;
00903 if (target == XNone || property == XNone) {
00904 ;
00905 } else if (target == xa_timestamp) {
00906 if (d->timestamp != CurrentTime) {
00907 XChangeProperty(dpy, req->requestor, property, xa_timestamp, 32,
00908 PropModeReplace, (uchar *) &d->timestamp, 1);
00909 ret = property;
00910 } else {
00911 qWarning("QClipboard: Invalid data timestamp");
00912 }
00913 } else if (target == xa_targets) {
00914 ret = send_targets_selection(d, req->requestor, property);
00915 } else {
00916 ret = send_selection(d, target, req->requestor, property);
00917 }
00918
00919 if (nmulti > 0) {
00920 if (ret == XNone) {
00921 multi[imulti].property = XNone;
00922 multi_writeback = true;
00923 }
00924 } else {
00925 event.xselection.property = ret;
00926 break;
00927 }
00928 }
00929
00930 if (nmulti > 0) {
00931 if (multi_writeback) {
00932
00933
00934 XChangeProperty(dpy, req->requestor, req->property, multi_type, 32,
00935 PropModeReplace, (uchar *) multi, nmulti * 2);
00936 }
00937
00938 delete [] multi;
00939 event.xselection.property = req->property;
00940 }
00941
00942
00943 XSendEvent(dpy, req->requestor, False, NoEventMask, &event);
00944
00945 DEBUG("QClipboard: SelectionNotify to 0x%lx\n"
00946 " property 0x%lx (%s)",
00947 req->requestor, event.xselection.property,
00948 X11->xdndAtomToString(event.xselection.property).data());
00949 }
00950 break;
00951 }
00952
00953 return true;
00954 }
00955
00956
00957
00958
00959
00960
00961 QClipboardWatcher::QClipboardWatcher(QClipboard::Mode mode)
00962 : QInternalMimeData()
00963 {
00964 switch (mode) {
00965 case QClipboard::Selection:
00966 atom = XA_PRIMARY;
00967 break;
00968
00969 case QClipboard::Clipboard:
00970 atom = ATOM(CLIPBOARD);
00971 break;
00972
00973 default:
00974 qWarning("QClipboardWatcher: Internal error: Unsupported clipboard mode");
00975 break;
00976 }
00977
00978 setupOwner();
00979 }
00980
00981 QClipboardWatcher::~QClipboardWatcher()
00982 {
00983 if(selection_watcher == this)
00984 selection_watcher = 0;
00985 if(clipboard_watcher == this)
00986 clipboard_watcher = 0;
00987 }
00988
00989 bool QClipboardWatcher::empty() const
00990 {
00991 Display *dpy = X11->display;
00992 Window win = XGetSelectionOwner(dpy, atom);
00993
00994 if(win == requestor->internalWinId()) {
00995 qWarning("QClipboardWatcher::empty: Internal error: Application owns the selection");
00996 return true;
00997 }
00998
00999 return win == XNone;
01000 }
01001
01002 QStringList QClipboardWatcher::formats_sys() const
01003 {
01004 if (empty())
01005 return QStringList();
01006
01007 if (!formatList.count()) {
01008
01009
01010
01011
01012 format_atoms = getDataInFormat(ATOM(TARGETS));
01013
01014 if (format_atoms.size() > 0) {
01015 Atom *targets = (Atom *) format_atoms.data();
01016 int size = format_atoms.size() / sizeof(Atom);
01017
01018 for (int i = 0; i < size; ++i) {
01019 if (targets[i] == 0)
01020 continue;
01021
01022 QStringList formatsForAtom = X11->xdndMimeFormatsForAtom(targets[i]);
01023 for (int j = 0; j < formatsForAtom.size(); ++j) {
01024 if (!formatList.contains(formatsForAtom.at(j)))
01025 formatList.append(formatsForAtom.at(j));
01026 }
01027 VDEBUG(" format: %s", X11->xdndAtomToString(targets[i]).data());
01028 VDEBUG(" data:\n%s\n", getDataInFormat(targets[i]).data());
01029 }
01030 DEBUG("QClipboardWatcher::format: %d formats available", formatList.count());
01031 }
01032 }
01033
01034 return formatList;
01035 }
01036
01037 bool QClipboardWatcher::hasFormat_sys(const QString &format) const
01038 {
01039 QStringList list = formats();
01040 return list.contains(format);
01041 }
01042
01043 QVariant QClipboardWatcher::retrieveData_sys(const QString &fmt, QVariant::Type) const
01044 {
01045 if (fmt.isEmpty() || empty())
01046 return QByteArray();
01047
01048 (void)formats();
01049 DEBUG("QClipboardWatcher::data: fetching format '%s'", fmt.toLatin1().data());
01050
01051 QList<Atom> atoms;
01052 Atom *targets = (Atom *) format_atoms.data();
01053 int size = format_atoms.size() / sizeof(Atom);
01054 for (int i = 0; i < size; ++i)
01055 atoms.append(targets[i]);
01056
01057 Atom fmtatom = X11->xdndMimeAtomForFormat(fmt, atoms);
01058
01059 if (fmtatom == 0)
01060 return QVariant();
01061
01062 return X11->xdndMimeConvertToFormat(fmtatom, getDataInFormat(fmtatom), fmt);
01063 }
01064
01065 QByteArray QClipboardWatcher::getDataInFormat(Atom fmtatom) const
01066 {
01067 QByteArray buf;
01068
01069 Display *dpy = X11->display;
01070 requestor->createWinId();
01071 Window win = requestor->internalWinId();
01072 Q_ASSERT(requestor->testAttribute(Qt::WA_WState_Created));
01073
01074 DEBUG("QClipboardWatcher::getDataInFormat: selection '%s' format '%s'",
01075 X11->xdndAtomToString(atom).data(), X11->xdndAtomToString(fmtatom).data());
01076
01077 XSelectInput(dpy, win, NoEventMask);
01078
01079 XDeleteProperty(dpy, win, ATOM(_QT_SELECTION));
01080 XConvertSelection(dpy, atom, fmtatom, ATOM(_QT_SELECTION), win, X11->time);
01081 XSync(dpy, false);
01082
01083 VDEBUG("QClipboardWatcher::getDataInFormat: waiting for SelectionNotify event");
01084
01085 XEvent xevent;
01086 if (!X11->clipboardWaitForEvent(win,SelectionNotify,&xevent,clipboard_timeout) ||
01087 xevent.xselection.property == XNone) {
01088 DEBUG("QClipboardWatcher::getDataInFormat: format not available");
01089 return buf;
01090 }
01091
01092 VDEBUG("QClipboardWatcher::getDataInFormat: fetching data...");
01093
01094 Atom type;
01095 XSelectInput(dpy, win, PropertyChangeMask);
01096
01097 if (X11->clipboardReadProperty(win, ATOM(_QT_SELECTION), true, &buf, 0, &type, 0, false)) {
01098 if (type == ATOM(INCR)) {
01099 int nbytes = buf.size() >= 4 ? *((int*)buf.data()) : 0;
01100 buf = X11->clipboardReadIncrementalProperty(win, ATOM(_QT_SELECTION), nbytes, false);
01101 }
01102 }
01103
01104 XSelectInput(dpy, win, NoEventMask);
01105
01106 DEBUG("QClipboardWatcher::getDataInFormat: %d bytes received", buf.size());
01107
01108 return buf;
01109 }
01110
01111
01112 const QMimeData* QClipboard::mimeData(Mode mode) const
01113 {
01114 QClipboardData *d = 0;
01115 switch (mode) {
01116 case Selection:
01117 d = selectionData();
01118 break;
01119 case Clipboard:
01120 d = clipboardData();
01121 break;
01122 default:
01123 qWarning("QClipboard::mimeData: unsupported mode '%d'", mode);
01124 return 0;
01125 }
01126
01127 if (! d->source() && ! timer_event_clear) {
01128 if (mode == Selection) {
01129 if (! selection_watcher)
01130 selection_watcher = new QClipboardWatcher(mode);
01131 d->setSource(selection_watcher);
01132 } else {
01133 if (! clipboard_watcher)
01134 clipboard_watcher = new QClipboardWatcher(mode);
01135 d->setSource(clipboard_watcher);
01136 }
01137
01138 if (! timer_id) {
01139
01140
01141
01142
01143
01144 QClipboard *that = ((QClipboard *) this);
01145 timer_id = that->startTimer(0);
01146 }
01147 }
01148
01149 return d->source();
01150 }
01151
01152
01153 void QClipboard::setMimeData(QMimeData* src, Mode mode)
01154 {
01155 Atom atom, sentinel_atom;
01156 QClipboardData *d;
01157 switch (mode) {
01158 case Selection:
01159 atom = XA_PRIMARY;
01160 sentinel_atom = ATOM(_QT_SELECTION_SENTINEL);
01161 d = selectionData();
01162 break;
01163
01164 case Clipboard:
01165 atom = ATOM(CLIPBOARD);
01166 sentinel_atom = ATOM(_QT_CLIPBOARD_SENTINEL);
01167 d = clipboardData();
01168 break;
01169
01170 default:
01171 qWarning("QClipboard::setMimeData: unsupported mode '%d'", mode);
01172 return;
01173 }
01174
01175 Display *dpy = X11->display;
01176 Window newOwner;
01177
01178 if (! src) {
01179 newOwner = XNone;
01180 d->clear();
01181 } else {
01182 setupOwner();
01183
01184 newOwner = owner->internalWinId();
01185
01186 d->setSource(src);
01187 d->timestamp = X11->time;
01188 }
01189
01190 Window prevOwner = XGetSelectionOwner(dpy, atom);
01191
01192 XSetSelectionOwner(dpy, atom, newOwner, X11->time);
01193
01194 if (mode == Selection)
01195 emitChanged(QClipboard::Selection);
01196 else
01197 emitChanged(QClipboard::Clipboard);
01198
01199 if (XGetSelectionOwner(dpy, atom) != newOwner) {
01200 qWarning("QClipboard::setData: Cannot set X11 selection owner for %s",
01201 X11->xdndAtomToString(atom).data());
01202 d->clear();
01203 return;
01204 }
01205
01206
01207 Window owners[2];
01208 owners[0] = newOwner;
01209 owners[1] = prevOwner;
01210 XChangeProperty(dpy, QApplication::desktop()->screen(0)->internalWinId(),
01211 sentinel_atom, XA_WINDOW, 32, PropModeReplace,
01212 (unsigned char*)&owners, 2);
01213 }
01214
01215
01216
01217
01218
01219
01220
01221
01222
01223 bool qt_check_selection_sentinel()
01224 {
01225 bool doIt = true;
01226 if (owner) {
01227
01228
01229
01230
01231
01232
01233
01234
01235
01236
01237
01238 Window* owners;
01239 Atom actualType;
01240 int actualFormat;
01241 ulong nitems;
01242 ulong bytesLeft;
01243
01244 if (XGetWindowProperty(X11->display,
01245 QApplication::desktop()->screen(0)->internalWinId(),
01246 ATOM(_QT_SELECTION_SENTINEL), 0, 2, False, XA_WINDOW,
01247 &actualType, &actualFormat, &nitems,
01248 &bytesLeft, (unsigned char**)&owners) == Success) {
01249 if (actualType == XA_WINDOW && actualFormat == 32 && nitems == 2) {
01250 Window win = owner->internalWinId();
01251 if (owners[0] == win || owners[1] == win)
01252 doIt = false;
01253 }
01254
01255 XFree(owners);
01256 }
01257 }
01258
01259 if (doIt) {
01260 if (waiting_for_data) {
01261 pending_selection_changed = true;
01262 if (! pending_timer_id)
01263 pending_timer_id = QApplication::clipboard()->startTimer(0);
01264 doIt = false;
01265 } else {
01266 selectionData()->clear();
01267 }
01268 }
01269
01270 return doIt;
01271 }
01272
01273
01274 bool qt_check_clipboard_sentinel()
01275 {
01276 bool doIt = true;
01277 if (owner) {
01278 Window *owners;
01279 Atom actualType;
01280 int actualFormat;
01281 unsigned long nitems, bytesLeft;
01282
01283 if (XGetWindowProperty(X11->display,
01284 QApplication::desktop()->screen(0)->internalWinId(),
01285 ATOM(_QT_CLIPBOARD_SENTINEL), 0, 2, False, XA_WINDOW,
01286 &actualType, &actualFormat, &nitems, &bytesLeft,
01287 (unsigned char **) &owners) == Success) {
01288 if (actualType == XA_WINDOW && actualFormat == 32 && nitems == 2) {
01289 Window win = owner->internalWinId();
01290 if (owners[0] == win || owners[1] == win)
01291 doIt = false;
01292 }
01293
01294 XFree(owners);
01295 }
01296 }
01297
01298 if (doIt) {
01299 if (waiting_for_data) {
01300 pending_clipboard_changed = true;
01301 if (! pending_timer_id)
01302 pending_timer_id = QApplication::clipboard()->startTimer(0);
01303 doIt = false;
01304 } else {
01305 clipboardData()->clear();
01306 }
01307 }
01308
01309 return doIt;
01310 }
01311
01312 #endif // QT_NO_CLIPBOARD