📄 qclipboard_x11.cpp
字号:
int imulti = -1; bool multi_writeback = false; if (req->target == xa_multiple) { QByteArray multi_data; if (req->property == XNone || !X11->clipboardReadProperty(req->requestor, req->property, false, &multi_data, 0, &multi_type, &multi_format, 0) || multi_format != 32) { // MULTIPLE property not formatted correctly XSendEvent(dpy, req->requestor, False, NoEventMask, &event); break; } nmulti = multi_data.size()/sizeof(*multi); multi = new AtomPair[nmulti]; memcpy(multi,multi_data.data(),multi_data.size()); imulti = 0; } for (; imulti < nmulti; ++imulti) { Atom target; Atom property; if (multi) { target = multi[imulti].target; property = multi[imulti].property; } else { target = req->target; property = req->property; if (property == XNone) // obsolete client property = target; } Atom ret = XNone; if (target == XNone || property == XNone) { ; } else if (target == xa_timestamp) { if (d->timestamp != CurrentTime) { XChangeProperty(dpy, req->requestor, property, XA_INTEGER, 32, PropModeReplace, (uchar *) &d->timestamp, 1); ret = property; } else { qWarning("QClipboard: Invalid data timestamp"); } } else if (target == xa_targets) { ret = send_targets_selection(d, req->requestor, property); } else { ret = send_selection(d, target, req->requestor, property); } if (nmulti > 0) { if (ret == XNone) { multi[imulti].property = XNone; multi_writeback = true; } } else { event.xselection.property = ret; break; } } if (nmulti > 0) { if (multi_writeback) { // according to ICCCM 2.6.2 says to put None back // into the original property on the requestor window XChangeProperty(dpy, req->requestor, req->property, multi_type, 32, PropModeReplace, (uchar *) multi, nmulti * 2); } delete [] multi; event.xselection.property = req->property; } // send selection notify to requestor XSendEvent(dpy, req->requestor, False, NoEventMask, &event); DEBUG("QClipboard: SelectionNotify to 0x%lx\n" " property 0x%lx (%s)", req->requestor, event.xselection.property, X11->xdndAtomToString(event.xselection.property).data()); } break; } return true;}QClipboardWatcher::QClipboardWatcher(QClipboard::Mode mode) : QInternalMimeData(){ switch (mode) { case QClipboard::Selection: atom = XA_PRIMARY; break; case QClipboard::Clipboard: atom = ATOM(CLIPBOARD); break; default: qWarning("QClipboardWatcher: Internal error: Unsupported clipboard mode"); break; } setupOwner();}QClipboardWatcher::~QClipboardWatcher(){ if(selection_watcher == this) selection_watcher = 0; if(clipboard_watcher == this) clipboard_watcher = 0;}bool QClipboardWatcher::empty() const{ Display *dpy = X11->display; Window win = XGetSelectionOwner(dpy, atom); if(win == requestor->internalWinId()) { qWarning("QClipboardWatcher::empty: Internal error: Application owns the selection"); return true; } return win == XNone;}QStringList QClipboardWatcher::formats_sys() const{ if (empty()) return QStringList(); if (!formatList.count()) { // get the list of targets from the current clipboard owner - we do this // once so that multiple calls to this function don't require multiple // server round trips... format_atoms = getDataInFormat(ATOM(TARGETS)); if (format_atoms.size() > 0) { Atom *targets = (Atom *) format_atoms.data(); int size = format_atoms.size() / sizeof(Atom); for (int i = 0; i < size; ++i) { if (targets[i] == 0) continue; QStringList formatsForAtom = X11->xdndMimeFormatsForAtom(targets[i]); for (int j = 0; j < formatsForAtom.size(); ++j) { if (!formatList.contains(formatsForAtom.at(j))) formatList.append(formatsForAtom.at(j)); } VDEBUG(" format: %s", X11->xdndAtomToString(targets[i]).data()); VDEBUG(" data:\n%s\n", getDataInFormat(targets[i]).data()); } DEBUG("QClipboardWatcher::format: %d formats available", formatList.count()); } } return formatList;}bool QClipboardWatcher::hasFormat_sys(const QString &format) const{ QStringList list = formats(); return list.contains(format);}QVariant QClipboardWatcher::retrieveData_sys(const QString &fmt, QVariant::Type requestedType) const{ if (fmt.isEmpty() || empty()) return QByteArray(); (void)formats(); // trigger update of format list DEBUG("QClipboardWatcher::data: fetching format '%s'", fmt.toLatin1().data()); QList<Atom> atoms; Atom *targets = (Atom *) format_atoms.data(); int size = format_atoms.size() / sizeof(Atom); for (int i = 0; i < size; ++i) atoms.append(targets[i]); QByteArray encoding; Atom fmtatom = X11->xdndMimeAtomForFormat(fmt, requestedType, atoms, &encoding); if (fmtatom == 0) return QVariant(); return X11->xdndMimeConvertToFormat(fmtatom, getDataInFormat(fmtatom), fmt, requestedType, encoding);}QByteArray QClipboardWatcher::getDataInFormat(Atom fmtatom) const{ QByteArray buf; Display *dpy = X11->display; requestor->createWinId(); Window win = requestor->internalWinId(); Q_ASSERT(requestor->testAttribute(Qt::WA_WState_Created)); DEBUG("QClipboardWatcher::getDataInFormat: selection '%s' format '%s'", X11->xdndAtomToString(atom).data(), X11->xdndAtomToString(fmtatom).data()); XSelectInput(dpy, win, NoEventMask); // don't listen for any events XDeleteProperty(dpy, win, ATOM(_QT_SELECTION)); XConvertSelection(dpy, atom, fmtatom, ATOM(_QT_SELECTION), win, X11->time); XSync(dpy, false); VDEBUG("QClipboardWatcher::getDataInFormat: waiting for SelectionNotify event"); XEvent xevent; if (!X11->clipboardWaitForEvent(win,SelectionNotify,&xevent,clipboard_timeout) || xevent.xselection.property == XNone) { DEBUG("QClipboardWatcher::getDataInFormat: format not available"); return buf; } VDEBUG("QClipboardWatcher::getDataInFormat: fetching data..."); Atom type; XSelectInput(dpy, win, PropertyChangeMask); if (X11->clipboardReadProperty(win, ATOM(_QT_SELECTION), true, &buf, 0, &type, 0, false)) { if (type == ATOM(INCR)) { int nbytes = buf.size() >= 4 ? *((int*)buf.data()) : 0; buf = X11->clipboardReadIncrementalProperty(win, ATOM(_QT_SELECTION), nbytes, false); } } XSelectInput(dpy, win, NoEventMask); DEBUG("QClipboardWatcher::getDataInFormat: %d bytes received", buf.size()); return buf;}const QMimeData* QClipboard::mimeData(Mode mode) const{ QClipboardData *d = 0; switch (mode) { case Selection: d = selectionData(); break; case Clipboard: d = clipboardData(); break; default: qWarning("QClipboard::mimeData: unsupported mode '%d'", mode); return 0; } if (! d->source() && ! timer_event_clear) { if (mode == Selection) { if (! selection_watcher) selection_watcher = new QClipboardWatcher(mode); d->setSource(selection_watcher); } else { if (! clipboard_watcher) clipboard_watcher = new QClipboardWatcher(mode); d->setSource(clipboard_watcher); } if (! timer_id) { // start a zero timer - we will clear cached data when the timer // times out, which will be the next time we hit the event loop... // that way, the data is cached long enough for calls within a single // loop/function, but the data doesn't linger around in case the selection // changes QClipboard *that = ((QClipboard *) this); timer_id = that->startTimer(0); } } return d->source();}void QClipboard::setMimeData(QMimeData* src, Mode mode){ Atom atom, sentinel_atom; QClipboardData *d; switch (mode) { case Selection: atom = XA_PRIMARY; sentinel_atom = ATOM(_QT_SELECTION_SENTINEL); d = selectionData(); break; case Clipboard: atom = ATOM(CLIPBOARD); sentinel_atom = ATOM(_QT_CLIPBOARD_SENTINEL); d = clipboardData(); break; default: qWarning("QClipboard::setMimeData: unsupported mode '%d'", mode); return; } Display *dpy = X11->display; Window newOwner; if (! src) { // no data, clear clipboard contents newOwner = XNone; d->clear(); } else { setupOwner(); newOwner = owner->internalWinId(); d->setSource(src); d->timestamp = X11->time; } Window prevOwner = XGetSelectionOwner(dpy, atom); // use X11->time, since d->timestamp == CurrentTime when clearing XSetSelectionOwner(dpy, atom, newOwner, X11->time); if (mode == Selection) emitChanged(QClipboard::Selection); else emitChanged(QClipboard::Clipboard); if (XGetSelectionOwner(dpy, atom) != newOwner) { qWarning("QClipboard::setData: Cannot set X11 selection owner for %s", X11->xdndAtomToString(atom).data()); d->clear(); return; } // Signal to other Qt processes that the selection has changed Window owners[2]; owners[0] = newOwner; owners[1] = prevOwner; XChangeProperty(dpy, QApplication::desktop()->screen(0)->internalWinId(), sentinel_atom, XA_WINDOW, 32, PropModeReplace, (unsigned char*)&owners, 2);}/* Called by the main event loop in qapplication_x11.cpp when the _QT_SELECTION_SENTINEL property has been changed (i.e. when some Qt process has performed QClipboard::setData(). If it returns true, the QClipBoard dataChanged() signal should be emitted.*/bool qt_check_selection_sentinel(){ bool doIt = true; if (owner) { /* Since the X selection mechanism cannot give any signal when the selection has changed, we emulate it (for Qt processes) here. The notification should be ignored in case of either a) This is the process that did setData (because setData() then has already emitted dataChanged()) b) This is the process that owned the selection when dataChanged() was called (because we have then received a SelectionClear event, and have already emitted dataChanged() as a result of that) */ unsigned char *retval; Atom actualType; int actualFormat; ulong nitems; ulong bytesLeft; if (XGetWindowProperty(X11->display, QApplication::desktop()->screen(0)->internalWinId(), ATOM(_QT_SELECTION_SENTINEL), 0, 2, False, XA_WINDOW, &actualType, &actualFormat, &nitems, &bytesLeft, &retval) == Success) { Window *owners = (Window *)retval; if (actualType == XA_WINDOW && actualFormat == 32 && nitems == 2) { Window win = owner->internalWinId(); if (owners[0] == win || owners[1] == win) doIt = false; } XFree(owners); } } if (doIt) { if (waiting_for_data) { pending_selection_changed = true; if (! pending_timer_id) pending_timer_id = QApplication::clipboard()->startTimer(0); doIt = false; } else { selectionData()->clear(); } } return doIt;}bool qt_check_clipboard_sentinel(){ bool doIt = true; if (owner) { unsigned char *retval; Atom actualType; int actualFormat; unsigned long nitems, bytesLeft; if (XGetWindowProperty(X11->display, QApplication::desktop()->screen(0)->internalWinId(), ATOM(_QT_CLIPBOARD_SENTINEL), 0, 2, False, XA_WINDOW, &actualType, &actualFormat, &nitems, &bytesLeft, &retval) == Success) { Window *owners = (Window *)retval; if (actualType == XA_WINDOW && actualFormat == 32 && nitems == 2) { Window win = owner->internalWinId(); if (owners[0] == win || owners[1] == win) doIt = false; } XFree(owners); } } if (doIt) { if (waiting_for_data) { pending_clipboard_changed = true; if (! pending_timer_id) pending_timer_id = QApplication::clipboard()->startTimer(0); doIt = false; } else { clipboardData()->clear(); } } return doIt;}#endif // QT_NO_CLIPBOARD
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -