📄 qclipboard_x11.cpp
字号:
usleep_tv.tv_sec = 0; usleep_tv.tv_usec = 50000; select(0, 0, 0, 0, &usleep_tv); } while (started.msecsTo(now) < timeout); } return false;}static inline int maxSelectionIncr(Display *dpy){ return XMaxRequestSize(dpy) > 65536 ? 65536*4 : XMaxRequestSize(dpy)*4 - 100; }bool QX11Data::clipboardReadProperty(Window win, Atom property, bool deleteProperty, QByteArray *buffer, int *size, Atom *type, int *format, bool nullterm){ int maxsize = maxSelectionIncr(display); ulong bytes_left; // bytes_after ulong length; // nitems uchar *data; Atom dummy_type; int dummy_format; int r; if (!type) // allow null args type = &dummy_type; if (!format) format = &dummy_format; // Don't read anything, just get the size of the property data r = XGetWindowProperty(display, win, property, 0, 0, False, AnyPropertyType, type, format, &length, &bytes_left, &data); if (r != Success || (type && *type == XNone)) { buffer->resize(0); return false; } XFree((char*)data); int offset = 0, buffer_offset = 0, format_inc = 1, proplen = bytes_left; VDEBUG("QClipboard: read_property(): initial property length: %d", proplen); switch (*format) { case 8: default: format_inc = sizeof(char) / 1; break; case 16: format_inc = sizeof(short) / 2; proplen *= sizeof(short) / 2; break; case 32: format_inc = sizeof(long) / 4; proplen *= sizeof(long) / 4; break; } int newSize = proplen + (nullterm ? 1 : 0); buffer->resize(newSize); bool ok = (buffer->size() == newSize); VDEBUG("QClipboard: read_property(): buffer resized to %d", buffer->size()); if (ok) { // could allocate buffer while (bytes_left) { // more to read... r = XGetWindowProperty(display, win, property, offset, maxsize/4, False, AnyPropertyType, type, format, &length, &bytes_left, &data); if (r != Success || (type && *type == XNone)) break; offset += length / (32 / *format); length *= format_inc * (*format) / 8; // Here we check if we get a buffer overflow and tries to // recover -- this shouldn't normally happen, but it doesn't // hurt to be defensive if ((int)(buffer_offset + length) > buffer->size()) { length = buffer->size() - buffer_offset; // escape loop bytes_left = 0; } memcpy(buffer->data() + buffer_offset, data, length); buffer_offset += length; XFree((char*)data); } if (*format == 8 && *type == ATOM(COMPOUND_TEXT)) { // convert COMPOUND_TEXT to a multibyte string XTextProperty textprop; textprop.encoding = *type; textprop.format = *format; textprop.nitems = length; textprop.value = (unsigned char *) buffer->data(); char **list_ret = 0; int count; if (XmbTextPropertyToTextList(display, &textprop, &list_ret, &count) == Success && count && list_ret) { offset = strlen(list_ret[0]); buffer->resize(offset + (nullterm ? 1 : 0)); memcpy(buffer->data(), list_ret[0], offset); } if (list_ret) XFreeStringList(list_ret); } // zero-terminate (for text) if (nullterm) buffer->data()[buffer_offset] = '\0'; } // correct size, not 0-term. if (size) *size = buffer_offset; VDEBUG("QClipboard: read_property(): buffer size %d, buffer offset %d, offset %d", buffer->size(), buffer_offset, offset); if (deleteProperty) XDeleteProperty(display, win, property); XFlush(display); return ok;}QByteArray QX11Data::clipboardReadIncrementalProperty(Window win, Atom property, int nbytes, bool nullterm){ XEvent event; QByteArray buf; QByteArray tmp_buf; bool alloc_error = false; int length; int offset = 0; if (nbytes > 0) { // Reserve buffer + zero-terminator (for text data) // We want to complete the INCR transfer even if we cannot // allocate more memory buf.resize(nbytes+1); alloc_error = buf.size() != nbytes+1; } for (;;) { XFlush(display); if (!clipboardWaitForEvent(win,PropertyNotify,&event,clipboard_timeout)) break; if (event.xproperty.atom != property || event.xproperty.state != PropertyNewValue) continue; if (X11->clipboardReadProperty(win, property, true, &tmp_buf, &length, 0, 0, false)) { if (length == 0) { // no more data, we're done if (nullterm) { buf.resize(offset+1); buf[offset] = '\0'; } else { buf.resize(offset); } return buf; } else if (!alloc_error) { if (offset+length > (int)buf.size()) { buf.resize(offset+length+65535); if (buf.size() != offset+length+65535) { alloc_error = true; length = buf.size() - offset; } } memcpy(buf.data()+offset, tmp_buf.constData(), length); tmp_buf.resize(0); offset += length; } } else { break; } } // timed out ... create a new requestor window, otherwise the requestor // could consider next request to be still part of this timed out request delete requestor; requestor = new QWidget(0); requestor->setObjectName(QLatin1String("internal clipboard requestor")); return QByteArray();}static Atom send_targets_selection(QClipboardData *d, Window window, Atom property){ QVector<Atom> types; QStringList formats = QInternalMimeData::formatsHelper(d->source()); for (int i = 0; i < formats.size(); ++i) { QList<Atom> atoms = X11->xdndMimeAtomsForFormat(formats.at(i)); for (int j = 0; j < atoms.size(); ++j) { if (!types.contains(atoms.at(j))) types.append(atoms.at(j)); } } types.append(ATOM(TARGETS)); types.append(ATOM(MULTIPLE)); types.append(ATOM(TIMESTAMP)); XChangeProperty(X11->display, window, property, XA_ATOM, 32, PropModeReplace, (uchar *) types.data(), types.size()); return property;}static Atom send_selection(QClipboardData *d, Atom target, Window window, Atom property){ Atom atomFormat = target; int dataFormat = 0; QByteArray data; if (X11->xdndMimeDataForAtom(target, d->source(), &data, &atomFormat, &dataFormat)) { VDEBUG("QClipboard: send_selection():\n" " property type %lx\n" " property name '%s'\n" " format %d\n" " %d bytes\n", target, X11->xdndMimeAtomToString(atomFormat).toLatin1().data(), dataFormat, 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, atomFormat, dataFormat, 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? int dataSize = data.size() / (dataFormat / 8); // use a single request to transfer data XChangeProperty(X11->display, window, property, atomFormat, dataFormat, PropModeReplace, (uchar *) data.data(), dataSize); } 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(); emitChanged(QClipboard::Clipboard); } if (pending_selection_changed) { pending_selection_changed = false; selectionData()->clear(); emitChanged(QClipboard::Selection); } 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(); emitChanged(QClipboard::Selection); } 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(); emitChanged(QClipboard::Clipboard); } 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->internalWinId()) 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;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -