📄 qclipboard_x11.cpp
字号:
} return false;}bool QX11Data::clipboardWaitForEvent(Window win, int type, XEvent *event, int timeout){ QTime started = QTime::currentTime(); QTime now = started; if (QAbstractEventDispatcher::instance()->inherits("QMotif")) { if (waiting_for_data) qFatal("QClipboard: internal error, qt_xclb_wait_for_event recursed"); waiting_for_data = true; has_captured_event = false; capture_event_win = win; capture_event_type = type; QApplication::EventFilter old_event_filter = qApp->setEventFilter(qt_x11_clipboard_event_filter); do { if (XCheckTypedWindowEvent(display, win, type, event)) { waiting_for_data = false; qApp->setEventFilter(old_event_filter); return true; } XSync(X11->display, false); usleep(50000); now = QTime::currentTime(); if (started > now) // crossed midnight started = now; QEventLoop::ProcessEventsFlags flags(QEventLoop::ExcludeUserInputEvents | QEventLoop::ExcludeSocketNotifiers | QEventLoop::WaitForMoreEvents | QEventLoop::X11ExcludeTimers); QAbstractEventDispatcher *eventDispatcher = QAbstractEventDispatcher::instance(); eventDispatcher->processEvents(flags); if (has_captured_event) { waiting_for_data = false; *event = captured_event; qApp->setEventFilter(old_event_filter); return true; } } while (started.msecsTo(now) < timeout); waiting_for_data = false; qApp->setEventFilter(old_event_filter); } else { do { if (XCheckTypedWindowEvent(X11->display,win,type,event)) return true; now = QTime::currentTime(); if ( started > now ) // crossed midnight started = now; XFlush(X11->display); // sleep 50ms, so we don't use up CPU cycles all the time. struct timeval usleep_tv; 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[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_selection(QClipboardData *d, Atom target, Window window, Atom property, int format = 0, QByteArray data = QByteArray());static Atom send_targets_selection(QClipboardData *d, Window window, Atom property){ QStringList formats = QInternalMimeData::formatsHelper(d->source()); int atoms = formats.size(); if (formats.contains("image/ppm")) atoms++; if (formats.contains("image/pbm")) atoms++; if (formats.contains("text/plain")) atoms+=4; VDEBUG("QClipboard: send_targets_selection(): data provides %d types, mapped to %d provided types", formats.size(), atoms); // for 64 bit cleanness... XChangeProperty expects long* for data with format == 32 QByteArray data; data.resize((atoms+3) * sizeof(long)); // plus TARGETS, MULTIPLE and TIMESTAMP long *atarget = (long *) data.data(); int n = 0; for (n = 0; n < formats.size(); ++n) { VDEBUG(" original format %s", formats.at(n).toLatin1().data()); atarget[n] = X11->xdndStringToAtom(formats.at(n).toLatin1().data()); } if (formats.contains("image/ppm")) atarget[n++] = XA_PIXMAP; if (formats.contains("image/pbm")) atarget[n++] = XA_BITMAP; if (formats.contains("text/plain")) { atarget[n++] = ATOM(UTF8_STRING); atarget[n++] = ATOM(TEXT); atarget[n++] = ATOM(COMPOUND_TEXT); atarget[n++] = XA_STRING; } atarget[n++] = ATOM(TARGETS); atarget[n++] = ATOM(MULTIPLE); atarget[n++] = ATOM(TIMESTAMP);#if defined(QCLIPBOARD_DEBUG_VERBOSE) for (int index = 0; index < n; index++) { VDEBUG(" atom %d: 0x%lx (%s)", index, atarget[index], X11->xdndAtomToString(atarget[index]).data()); }#endif XChangeProperty(X11->display, window, property, XA_ATOM, 32, PropModeReplace, (uchar *) data.data(), n); return property;}static Atom send_string_selection(QClipboardData *d, Atom target, Window window, Atom property){ DEBUG("QClipboard: send_string_selection():\n" " property type %lx\n" " property name '%s'", target, X11->xdndAtomToString(target).data()); if (target == ATOM(TEXT) || target == ATOM(COMPOUND_TEXT)) { // the ICCCM states that TEXT and COMPOUND_TEXT are in the // encoding of choice, so we choose the encoding of the locale QByteArray data = d->source()->text().toLocal8Bit(); char *list[] = { data.data(), NULL }; XICCEncodingStyle style = (target == ATOM(COMPOUND_TEXT)) ? XCompoundTextStyle : XStdICCTextStyle; XTextProperty textprop; if (list[0] != NULL && XmbTextListToTextProperty(X11->display, list, 1, style, &textprop) == Success) { DEBUG(" textprop type %lx\n" " textprop name '%s'\n" " format %d\n" " %ld items", textprop.encoding, X11->xdndAtomToString(textprop.encoding).data(), textprop.format, textprop.nitems); int sz = sizeof_format(textprop.format); data = QByteArray((const char *) textprop.value, textprop.nitems * sz); XFree(textprop.value); return send_selection(d, textprop.encoding, window, property, textprop.format, data); } return XNone; } Atom xtarget = XNone; QByteArray data; if (target == XA_STRING) { // the ICCCM states that STRING is latin1 plus newline and tab // see section 2.6.2 data = d->source()->text().toLatin1(); xtarget = XA_STRING; } else if (target == ATOM(UTF8_STRING)) { // proposed UTF8_STRING conversion type data = d->source()->text().toUtf8(); xtarget = ATOM(UTF8_STRING); } if (xtarget == XNone) // should not happen return XNone; DEBUG(" format 8\n %d bytes", data.size()); return send_selection(d, xtarget, window, property, 8, data);}static Atom send_pixmap_selection(QClipboardData *d, Atom target, Window window, Atom property){ QPixmap pm; if (target == XA_PIXMAP) { pm = qvariant_cast<QPixmap>(d->source()->imageData()); } else if (target == XA_BITMAP) { pm = qvariant_cast<QPixmap>(d->source()->imageData()); QImage img = pm.toImage(); if (img.depth() != 1) { img = img.convertToFormat(QImage::Format_MonoLSB); pm = QPixmap::fromImage(img); } } if (pm.isNull()) // should never happen return XNone;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -