📄 qclipboard_x11.cpp
字号:
Pixmap handle = pm.handle(); XChangeProperty(X11->display, window, property, target, 32, PropModeReplace, (uchar *) &handle, 1); d->addTransferredPixmap(pm); return property;}static Atom send_selection(QClipboardData *d, Atom target, Window window, Atom property, int format, QByteArray data){ if (format == 0) format = 8; if (data.isEmpty()) { QByteArray fmt = X11->xdndAtomToString(target); DEBUG("QClipboard: send_selection(): converting to type '%s'", fmt.data()); if (fmt.isEmpty() || !QInternalMimeData::hasFormatHelper(fmt, d->source())) // Not a MIME type we have return XNone; else data = QInternalMimeData::renderDataHelper(fmt, d->source()); } DEBUG("QClipboard: send_selection():\n" " property type %lx\n" " property name '%s'\n" " format %d\n" " %d bytes", target, X11->xdndAtomToString(target).data(), format, data.size()); // don't allow INCR transfers when using MULTIPLE or to // Motif clients (since Motif doesn't support INCR) static Atom motif_clip_temporary = ATOM(CLIP_TEMPORARY); bool allow_incr = property != motif_clip_temporary; // X_ChangeProperty protocol request is 24 bytes const int increment = (XMaxRequestSize(X11->display) * 4) - 24; if (data.size() > increment && allow_incr) { long bytes = data.size(); XChangeProperty(X11->display, window, property, ATOM(INCR), 32, PropModeReplace, (uchar *) &bytes, 1); (void)new QClipboardINCRTransaction(window, property, target, format, data, increment); return ATOM(INCR); } // make sure we can perform the XChangeProperty in a single request if (data.size() > increment) return XNone; // ### perhaps use several XChangeProperty calls w/ PropModeAppend? // use a single request to transfer data XChangeProperty(X11->display, window, property, target, format, PropModeReplace, (uchar *) data.data(), data.size() / sizeof_format(format)); return property;}/*! \internal Internal cleanup for Windows.*/void QClipboard::ownerDestroyed(){ }/*! \internal Internal optimization for Windows.*/void QClipboard::connectNotify(const char *){ }/*! \reimp */bool QClipboard::event(QEvent *e){ if (e->type() == QEvent::Timer) { QTimerEvent *te = (QTimerEvent *) e; if (waiting_for_data) // should never happen return false; if (te->timerId() == timer_id) { killTimer(timer_id); timer_id = 0; timer_event_clear = true; if (selection_watcher) // clear selection selectionData()->clear(); if (clipboard_watcher) // clear clipboard clipboardData()->clear(); timer_event_clear = false; return true; } else if (te->timerId() == pending_timer_id) { // I hate klipper killTimer(pending_timer_id); pending_timer_id = 0; if (pending_clipboard_changed) { pending_clipboard_changed = false; clipboardData()->clear(); emit dataChanged(); } if (pending_selection_changed) { pending_selection_changed = false; selectionData()->clear(); emit selectionChanged(); } return true; } else if (te->timerId() == incr_timer_id) { killTimer(incr_timer_id); incr_timer_id = 0; qt_xclb_incr_timeout(); return true; } else { return QObject::event(e); } } else if (e->type() != QEvent::Clipboard) { return QObject::event(e); } XEvent *xevent = (XEvent *)(((QClipboardEvent *)e)->data()); Display *dpy = X11->display; if (!xevent) return true; switch (xevent->type) { case SelectionClear: // new selection owner if (xevent->xselectionclear.selection == XA_PRIMARY) { QClipboardData *d = selectionData(); // ignore the event if it was generated before we gained selection ownership if (d->timestamp != CurrentTime && xevent->xselectionclear.time < d->timestamp) break; DEBUG("QClipboard: new selection owner 0x%lx at time %lx (ours %lx)", XGetSelectionOwner(dpy, XA_PRIMARY), xevent->xselectionclear.time, d->timestamp); if (! waiting_for_data) { d->clear(); emit selectionChanged(); } else { pending_selection_changed = true; if (! pending_timer_id) pending_timer_id = QApplication::clipboard()->startTimer(0); } } else if (xevent->xselectionclear.selection == ATOM(CLIPBOARD)) { QClipboardData *d = clipboardData(); // ignore the event if it was generated before we gained selection ownership if (d->timestamp != CurrentTime && xevent->xselectionclear.time < d->timestamp) break; DEBUG("QClipboard: new clipboard owner 0x%lx at time %lx (%lx)", XGetSelectionOwner(dpy, ATOM(CLIPBOARD)), xevent->xselectionclear.time, d->timestamp); if (! waiting_for_data) { d->clear(); emit dataChanged(); } else { pending_clipboard_changed = true; if (! pending_timer_id) pending_timer_id = QApplication::clipboard()->startTimer(0); } } else { qWarning("QClipboard: Unknown SelectionClear event received."); return false; } break; case SelectionNotify: /* Something has delivered data to us, but this was not caught by QClipboardWatcher::getDataInFormat() Just skip the event to prevent Bad Things (tm) from happening later on... */ break; case SelectionRequest: { // someone wants our data XSelectionRequestEvent *req = &xevent->xselectionrequest; if (requestor && req->requestor == requestor->winId()) break; XEvent event; event.xselection.type = SelectionNotify; event.xselection.display = req->display; event.xselection.requestor = req->requestor; event.xselection.selection = req->selection; event.xselection.target = req->target; event.xselection.property = XNone; event.xselection.time = req->time; DEBUG("QClipboard: SelectionRequest from %lx\n" " selection 0x%lx (%s) target 0x%lx (%s)", req->requestor, req->selection, X11->xdndAtomToString(req->selection).data(), req->target, X11->xdndAtomToString(req->target).data()); QClipboardData *d; if (req->selection == XA_PRIMARY) { d = selectionData(); } else if (req->selection == ATOM(CLIPBOARD)) { d = clipboardData(); } else { qWarning("QClipboard: unknown selection '%lx'", req->selection); XSendEvent(dpy, req->requestor, False, NoEventMask, &event); break; } if (! d->source()) { qWarning("QClipboard: cannot transfer data, no data available"); XSendEvent(dpy, req->requestor, False, NoEventMask, &event); break; } DEBUG("QClipboard: SelectionRequest at time %lx (ours %lx)", req->time, d->timestamp); if (d->timestamp == CurrentTime // we don't own the selection anymore || (req->time != CurrentTime && req->time < d->timestamp)) { DEBUG("QClipboard: SelectionRequest too old"); XSendEvent(dpy, req->requestor, False, NoEventMask, &event); break; } Atom xa_targets = ATOM(TARGETS); Atom xa_multiple = ATOM(MULTIPLE); Atom xa_timestamp = ATOM(TIMESTAMP); struct AtomPair { Atom target; Atom property; } *multi = 0; Atom multi_type = XNone; int multi_format = 0; int nmulti = 0; 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_timestamp, 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 if (target == XA_STRING || target == ATOM(TEXT) || target == ATOM(COMPOUND_TEXT) || target == ATOM(UTF8_STRING)) { ret = send_string_selection(d, target, req->requestor, property); } else if (target == XA_PIXMAP || target == XA_BITMAP) { ret = send_pixmap_selection(d, target, 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, unknown 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->winId()) { qWarning("QClipboardWatcher::empty: internal error, app owns the selection"); return true; } return win == XNone;}QStringList QClipboardWatcher::formats_sys() const
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -