📄 qx11embed_x11.cpp
字号:
class QX11EmbedWidgetPrivate : public QWidgetPrivate{ Q_DECLARE_PUBLIC(QX11EmbedWidget)public: inline QX11EmbedWidgetPrivate() { container = 0; } void setEmbedded(); void emitError(QX11EmbedWidget::Error error) { Q_Q(QX11EmbedWidget); lastError = error; emit q->error(error); } enum FocusWidgets { FirstFocusWidget, LastFocusWidget }; int focusOriginator; QWidget *getFocusWidget(FocusWidgets fw); void checkActivateWindow(QObject *o); QX11EmbedWidget *xEmbedWidget(QObject *o) const; void clearFocus(); WId container; QPointer<QWidget> currentFocus; QX11EmbedWidget::Error lastError;};/*! Constructs a QX11EmbedWidget object with the given \a parent.*/QX11EmbedWidget::QX11EmbedWidget(QWidget *parent) : QWidget(*new QX11EmbedWidgetPrivate, parent, 0){ XSetErrorHandler(x11ErrorHandler); initXEmbedAtoms(x11Info().display()); createWinId(); XSelectInput(x11Info().display(), internalWinId(), KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask | KeymapStateMask | ButtonMotionMask | PointerMotionMask | FocusChangeMask | ExposureMask | StructureNotifyMask | SubstructureNotifyMask | PropertyChangeMask); unsigned int data[] = {XEMBED_VERSION, XEMBED_MAPPED}; XChangeProperty(x11Info().display(), internalWinId(), _XEMBED_INFO, _XEMBED_INFO, 32, PropModeReplace, (unsigned char*) data, 2); setFocusPolicy(Qt::StrongFocus); setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); QApplication::instance()->installEventFilter(this);#ifdef QX11EMBED_DEBUG qDebug() << "QX11EmbedWidget::QX11EmbedWidget: constructed client" << (void *)this << "with winId" << winId();#endif}/*! Destructs the QX11EmbedWidget object. If the widget is embedded when deleted, it is hidden and then detached from its container, so that the container is free to embed a new widget.*/QX11EmbedWidget::~QX11EmbedWidget(){ Q_D(QX11EmbedWidget); if (d->container) {#ifdef QX11EMBED_DEBUG qDebug() << "QX11EmbedWidget::~QX11EmbedWidget: unmapping" << (void *)this << "with winId" << winId() << "from container with winId" << d->container;#endif XUnmapWindow(x11Info().display(), internalWinId()); XReparentWindow(x11Info().display(), internalWinId(), x11Info().appRootWindow(), 0, 0); }#ifdef QX11EMBED_DEBUG qDebug() << "QX11EmbedWidget::~QX11EmbedWidget: destructed client" << (void *)this << "with winId" << winId();#endif}/*! Returns the type of error that occurred last. This is the same error code that is emitted by the error() signal. \sa Error*/QX11EmbedWidget::Error QX11EmbedWidget::error() const{ return d_func()->lastError;}/*! When this function is called, the widget embeds itself into the container whose window ID is \a id. If \a id is \e not the window ID of a container this function will behave unpredictably.*/void QX11EmbedWidget::embedInto(WId id){ Q_D(QX11EmbedWidget);#ifdef QX11EMBED_DEBUG qDebug() << "QX11EmbedWidget::embedInto: embedding client" << (void *)this << "with winId" << winId() << "into container" << id;#endif d->container = id; switch (XReparentWindow(x11Info().display(), internalWinId(), d->container, 0, 0)) { case BadWindow: d->emitError(InvalidWindowID); break; case BadMatch: d->emitError(Internal); break; case Success: default: break; } QTLWExtra* x = d->extra ? d->extra->topextra : 0; if (x) x->frameStrut.setCoords(0, 0, 0, 0); d->data.fstrut_dirty = false;}/*! \internal Gets the first or last child widget that can get focus.*/QWidget *QX11EmbedWidgetPrivate::getFocusWidget(FocusWidgets fw){ Q_Q(QX11EmbedWidget); QWidget *tlw = q; QWidget *w = tlw->nextInFocusChain(); QWidget *last = tlw; extern bool qt_tab_all_widgets; uint focus_flag = qt_tab_all_widgets ? Qt::TabFocus : Qt::StrongFocus; while (w != tlw) { if (((w->focusPolicy() & focus_flag) == focus_flag) && w->isVisibleTo(q) && w->isEnabled()) { last = w; if (fw == FirstFocusWidget) break; } w = w->nextInFocusChain(); } return last;}/*! \internal Returns the xembed widget that the widget is a child of*/QX11EmbedWidget *QX11EmbedWidgetPrivate::xEmbedWidget(QObject *o) const{ QX11EmbedWidget *xec = 0; // Check the widget itself, then its parents, and find the first // QX11EmbedWidget. do { if ((xec = qobject_cast<QX11EmbedWidget *>(o))) return xec; } while ((o = o->parent())); return 0;}/*! \internal Checks the active window.*/void QX11EmbedWidgetPrivate::checkActivateWindow(QObject *o){ Q_Q(QX11EmbedWidget); QX11EmbedWidget *xec = xEmbedWidget(o); // check if we are in the right xembed client if (q != xec) return; QWidget *w = qobject_cast<QWidget *>(o); // if it is no active window, then don't do the change if (!(w && qApp->activeWindow())) return; // if it already is the active window, don't do anything if (w->window() != qApp->activeWindow()) { qApp->setActiveWindow(w->window()); currentFocus = w; sendXEmbedMessage(xec->containerWinId(), q->x11Info().display(), XEMBED_REQUEST_FOCUS); }}/*! \internal Clears the focus*/void QX11EmbedWidgetPrivate::clearFocus(){ Q_Q(QX11EmbedWidget); // Setting focus on the client itself removes Qt's // logical focus rectangle. We can't just do a // clearFocus here, because when we send the synthetic // FocusIn to ourselves on activation, Qt will set // focus on focusWidget() again. This way, we "hide" // focus rather than clearing it. if (!q->window()->hasFocus()) q->window()->setFocus(Qt::OtherFocusReason); currentFocus = 0;}/*! \internal Sets the embedded flag on the client.*/void QX11EmbedWidgetPrivate::setEmbedded(){ Q_Q(QX11EmbedWidget); ((QHackWidget *)q->window())->topData()->embedded = 1;}/*! \internal Handles WindowActivate and FocusIn events for the client.*/bool QX11EmbedWidget::eventFilter(QObject *o, QEvent *event){ Q_D(QX11EmbedWidget); switch (event->type()) { case QEvent::FocusIn: switch (((QFocusEvent *)event)->reason()) { case Qt::MouseFocusReason: // If the user clicks into one of the client widget's // children and we didn't have focus already, we request // focus from our container. if (d->xEmbedWidget(o) == this) { if (d->currentFocus.isNull()) sendXEmbedMessage(d->container, x11Info().display(), XEMBED_REQUEST_FOCUS); d->currentFocus = qobject_cast<QWidget *>(o); } break; case Qt::TabFocusReason: // If the xembed client receives a focus event because of // a Tab, then we are at the end of our focus chain and we // ask the container to move to its next focus widget. if (o == this) { d->clearFocus(); sendXEmbedMessage(d->container, x11Info().display(), XEMBED_FOCUS_NEXT); return true; } else { // We're listening on events from qApp, so in order // for us to know who to set focus on if we receive an // activation event, we note the widget that got the // focusin last. if (d->xEmbedWidget(o) == this) d->currentFocus = qobject_cast<QWidget *>(o); } break; case Qt::BacktabFocusReason: // If the window receives a focus event because of // a Backtab, then we are at the start of our focus chain // and we ask the container to move to its previous focus // widget. if (o == this) { // See comment for Tab. // If we receive a XEMBED_FOCUS_IN // XEMBED_FOCUS_CURRENT, we will set focus in // currentFocus. To avoid that in this case, we reset // currentFocus. d->clearFocus(); sendXEmbedMessage(d->container, x11Info().display(), XEMBED_FOCUS_PREV); return true; } else { if (d->xEmbedWidget(o) == this) d->currentFocus = qobject_cast<QWidget *>(o); } break; case Qt::ActiveWindowFocusReason: if (!d->currentFocus.isNull()) { if (!d->currentFocus->hasFocus()) d->currentFocus->setFocus(Qt::OtherFocusReason); } else { d->clearFocus(); return true; } break; case Qt::PopupFocusReason: case Qt::ShortcutFocusReason: case Qt::OtherFocusReason: // If focus is received to any child widget because of any // other reason, remember the widget so that we can give // it focus again if we're activated. if (d->xEmbedWidget(o) == this) { d->currentFocus = qobject_cast<QWidget *>(o); } break; default: break; } break; case QEvent::MouseButtonPress: // If we get a mouse button press event inside a embedded widget // make sure this is the active window in qapp. d->checkActivateWindow(o); break; default: break; } return QWidget::eventFilter(o, event);}/*! \internal Handles some notification events and client messages. Client side XEmbed message receiving is also handled here.*/bool QX11EmbedWidget::x11Event(XEvent *event){ Q_D(QX11EmbedWidget); switch (event->type) { case DestroyNotify:#ifdef QX11EMBED_DEBUG qDebug() << "QX11EmbedWidget::x11Event: client" << (void *)this << "with winId" << winId() << "received a DestroyNotify";#endif // If the container window is destroyed, we signal this to the user. d->container = 0; emit containerClosed(); break; case ReparentNotify:#ifdef QX11EMBED_DEBUG qDebug() << "QX11EmbedWidget::x11Event: client" << (void *)this << "with winId" << winId() << "received a ReparentNotify to" << ((event->xreparent.parent == x11Info().appRootWindow()) ? QString::fromLatin1("root") : QString::number(event->xreparent.parent));#endif // If the container shuts down, we will be reparented to the // root window. We must also consider the case that we may be // reparented from one container to another. if (event->xreparent.parent == x11Info().appRootWindow()) { if (((QHackWidget *)this)->topData()->embedded) { d->container = 0; emit containerClosed(); } return true; } else { d->container = event->xreparent.parent; } break; case UnmapNotify: // Mapping and unmapping are handled by changes to the // _XEMBED_INFO property. Any default map/unmap requests are // ignored. return true; case PropertyNotify: // The container sends us map/unmap messages through the // _XEMBED_INFO property. We adhere to the XEMBED_MAPPED bit in // data2. if (event->xproperty.atom == _XEMBED_INFO) { Atom actual_type_return; int actual_format_return; unsigned long nitems_return; unsigned long bytes_after_return; unsigned char *prop_return = 0; if (XGetWindowProperty(x11Info().display(), internalWinId(), _XEMBED_INFO, 0, 2, false, _XEMBED_INFO, &actual_type_return, &actual_format_return, &nitems_return, &bytes_after_return, &prop_return) == Success) { if (nitems_return > 1) { if (((int * )prop_return)[1] & XEMBED_MAPPED) { XMapWindow(x11Info().display(), internalWinId()); } else { XUnmapWindow(x11Info().display(), internalWinId()); } } } } break; case ClientMessage: // XEMBED messages have message_type _XEMBED if (event->xclient.message_type == _XEMBED) { // Discard XEMBED messages not to ourselves. (### dead code?) if (event->xclient.window != internalWinId()) break; // Update qt_x_time if necessary Time msgtime = (Time) event->xclient.data.l[0]; if (msgtime > X11->time) X11->time = msgtime; switch (event->xclient.data.l[1]) { case XEMBED_WINDOW_ACTIVATE: { // When we receive an XEMBED_WINDOW_ACTIVATE, we need // to send ourselves a synthetic FocusIn X11 event for // Qt to activate us. XEvent ev; memset(&ev, 0, sizeof(ev)); ev.xfocus.display = x11Info().display(); ev.xfocus.type = XFocusIn; ev.xfocus.window = internalWinId(); ev.xfocus.mode = NotifyNormal; ev.xfocus.detail = NotifyNonlinear; ((QApplication *)QApplication::instance())->x11ProcessEvent(&ev); } break; case XEMBED_WINDOW_DEACTIVATE: { // When we receive an XEMBED_WINDOW_DEACTIVATE, we // need to send ourselves a synthetic FocusOut event // for Qt to deativate us. XEvent ev; memset(&ev, 0, sizeof(ev)); ev.xfocus.display = x11Info().display(); ev.xfocus.type = XFocusOut; ev.xfocus.window = internalWinId(); ev.xfocus.mode = NotifyNormal; ev.xfocus.detail = NotifyNonlinear; ((QApplication *)QApplication::instance())->x11ProcessEvent(&ev); } break; case XEMBED_EMBEDDED_NOTIFY: {#ifdef QX11EMBED_DEBUG qDebug() << "QX11EmbedWidget::x11Event: client" << (void *)this << "with winId" << winId() << "received an XEMBED EMBEDDED NOTIFY message";
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -