📄 qx11embed_x11.cpp
字号:
} } break; case QEvent::FocusOut: { // When receiving a FocusOut, we ask our client to remove its // focus. if (o == this && d->client) { if (!d->isEmbedded()) d->moveInputToProxy(); if (d->clientIsXEmbed) { QFocusEvent *fe = (QFocusEvent *)event; if (o == this && d->client && fe->reason() != Qt::ActiveWindowFocusReason) sendXEmbedMessage(d->client, x11Info().display(), XEMBED_FOCUS_OUT); } else { d->checkGrab(); } } } break; case QEvent::Close: { if (o == this && d->client) { // Unmap the client and reparent it to the root window. // Wait until the messages have been processed. Then ask // the window manager to delete the window. XUnmapWindow(x11Info().display(), d->client); XReparentWindow(x11Info().display(), d->client, x11Info().appRootWindow(), 0, 0); XSync(x11Info().display(), false); XEvent ev; memset(&ev, 0, sizeof(ev)); ev.xclient.type = ClientMessage; ev.xclient.window = d->client; ev.xclient.message_type = ATOM(WM_PROTOCOLS); ev.xclient.format = 32; ev.xclient.data.s[0] = ATOM(WM_DELETE_WINDOW); XSendEvent(x11Info().display(), d->client, false, NoEventMask, &ev); XFlush(x11Info().display()); d->client = 0; d->clientIsXEmbed = false; d->wmMinimumSizeHint = QSize(); updateGeometry(); setEnabled(false); update(); emit clientClosed(); } } default: break; } return QObject::eventFilter(o, event);}/*! \internal Handles X11 events for the container.*/bool QX11EmbedContainer::x11Event(XEvent *event){ Q_D(QX11EmbedContainer); switch (event->type) { case CreateNotify: // The client created an embedded window. if (d->client) d->rejectClient(event->xcreatewindow.window); else d->acceptClient(event->xcreatewindow.window); break; case DestroyNotify: if (event->xdestroywindow.window == d->client) { // The client died. d->client = 0; d->clientIsXEmbed = false; d->wmMinimumSizeHint = QSize(); updateGeometry(); update(); setEnabled(false); emit clientClosed(); } break; case ReparentNotify: // The client sends us this if it reparents itself out of our // widget. if (event->xreparent.window == d->client && event->xreparent.parent != internalWinId()) { d->client = 0; d->clientIsXEmbed = false; d->wmMinimumSizeHint = QSize(); updateGeometry(); update(); setEnabled(false); emit clientClosed(); } else if (event->xreparent.parent == internalWinId()) { // The client reparented itself into this window. if (d->client) d->rejectClient(event->xreparent.window); else d->acceptClient(event->xreparent.window); } break; case ClientMessage: { if (event->xclient.message_type == _XEMBED) { // Ignore XEMBED messages not to ourselves if (event->xclient.window != internalWinId()) break; // Receiving an XEmbed message means the client // is an XEmbed client. d->clientIsXEmbed = true; Time msgtime = (Time) event->xclient.data.l[0]; if (msgtime > X11->time) X11->time = msgtime; switch (event->xclient.data.l[1]) { case XEMBED_REQUEST_FOCUS: { // This typically happens when the client gets focus // because of a mouse click. if (!hasFocus()) setFocus(Qt::OtherFocusReason); // The message is passed along to the topmost container // that eventually responds with a XEMBED_FOCUS_IN // message. The focus in message is passed all the way // back until it reaches the original focus // requestor. In the end, not only the original client // has focus, but also all its ancestor containers. if (d->isEmbedded()) { // If our window's embedded flag is set, then // that suggests that we are part of a client. The // parentWinId will then point to an container to whom // we must pass this message. sendXEmbedMessage(d->topLevelParentWinId(), x11Info().display(), XEMBED_REQUEST_FOCUS); } else { // Our window's embedded flag is not set, // so we are the topmost container. We respond to // the focus request message with a focus in // message. This message will pass on from client // to container to client until it reaches the // originator of the XEMBED_REQUEST_FOCUS message. sendXEmbedMessage(d->client, x11Info().display(), XEMBED_FOCUS_IN, XEMBED_FOCUS_CURRENT); } break; } case XEMBED_FOCUS_NEXT: // Client sends this event when it received a tab // forward and was at the end of its focus chain. If // we are the only widget in the focus chain, we send // ourselves a FocusIn event. if (d->focus_next != this) { focusNextPrevChild(true); } else { QFocusEvent event(QEvent::FocusIn, Qt::TabFocusReason); qApp->sendEvent(this, &event); } break; case XEMBED_FOCUS_PREV: // Client sends this event when it received a backtab // and was at the start of its focus chain. If we are // the only widget in the focus chain, we send // ourselves a FocusIn event. if (d->focus_next != this) { focusNextPrevChild(false); } else { QFocusEvent event(QEvent::FocusIn, Qt::BacktabFocusReason); qApp->sendEvent(this, &event); } break; default: break; } } } break; case XButtonPress: if (!d->clientIsXEmbed) { setFocus(Qt::MouseFocusReason); XAllowEvents(x11Info().display(), ReplayPointer, CurrentTime); return true; } break; case XButtonRelease: if (!d->clientIsXEmbed) XAllowEvents(x11Info().display(), SyncPointer, CurrentTime); break; default: break; } return QWidget::x11Event(event);}/*! \internal Whenever the container is resized, we need to resize our client.*/void QX11EmbedContainer::resizeEvent(QResizeEvent *){ Q_D(QX11EmbedContainer); if (d->client) XResizeWindow(x11Info().display(), d->client, width(), height());}/*! \internal We use the QShowEvent to signal to our client that we want it to map itself. We do this by changing its window property XEMBED_INFO. The client will get an X11 PropertyNotify.*/void QX11EmbedContainer::showEvent(QShowEvent *){ Q_D(QX11EmbedContainer); if (d->client) { unsigned int data[] = {XEMBED_VERSION, XEMBED_MAPPED}; XChangeProperty(x11Info().display(), d->client, _XEMBED_INFO, _XEMBED_INFO, 32, PropModeReplace, (unsigned char *) data, 2); }}/*! \internal We use the QHideEvent to signal to our client that we want it to unmap itself. We do this by changing its window property XEMBED_INFO. The client will get an X11 PropertyNotify.*/void QX11EmbedContainer::hideEvent(QHideEvent *){ Q_D(QX11EmbedContainer); if (d->client) { unsigned int data[] = {XEMBED_VERSION, XEMBED_MAPPED}; XChangeProperty(x11Info().display(), d->client, _XEMBED_INFO, _XEMBED_INFO, 32, PropModeReplace, (unsigned char *) data, 2); }}/*! \reimp*/bool QX11EmbedContainer::event(QEvent *event){ if (event->type() == QEvent::ParentChange) { XSelectInput(x11Info().display(), internalWinId(), KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask | ButtonMotionMask | KeymapStateMask | PointerMotionMask | EnterWindowMask | LeaveWindowMask | FocusChangeMask | ExposureMask | StructureNotifyMask | SubstructureNotifyMask); } return QWidget::event(event);}/*! \internal Rejects a client window by reparenting it to the root window. The client will receive a reparentnotify, and will most likely assume that the container has shut down. The XEmbed protocol does not define any way to reject a client window, but this is a clean way to do it.*/void QX11EmbedContainerPrivate::rejectClient(WId window){ Q_Q(QX11EmbedContainer); q->setEnabled(false); XRemoveFromSaveSet(q->x11Info().display(), client); XReparentWindow(q->x11Info().display(), window, q->x11Info().appRootWindow(), 0, 0);}/*! \internal Accepts a client by mapping it, resizing it and optionally activating and giving it logical focusing through XEMBED messages.*/void QX11EmbedContainerPrivate::acceptClient(WId window){ Q_Q(QX11EmbedContainer); client = window; q->setEnabled(true); // This tells Qt that we wish to forward DnD messages to // our client. if (!extra) createExtra(); extraData()->xDndProxy = client; unsigned int version = XEmbedVersion(); Atom actual_type_return; int actual_format_return; unsigned long nitems_return = 0; unsigned long bytes_after_return; unsigned char *prop_return = 0; unsigned int clientversion = 0; // Add this client to our saveset, so if we crash, the client window // doesn't get destroyed. This is useful for containers that restart // automatically after a crash, because it can simply reembed its clients // without having to restart them (KDE panel). XAddToSaveSet(q->x11Info().display(), client); // XEmbed clients have an _XEMBED_INFO property in which we can // fetch the version if (XGetWindowProperty(q->x11Info().display(), client, _XEMBED_INFO, 0, 2, false, _XEMBED_INFO, &actual_type_return, &actual_format_return, &nitems_return, &bytes_after_return, &prop_return) == Success) { if (actual_type_return != None && actual_format_return != 0) { // Clients with the _XEMBED_INFO property are XEMBED clients. clientIsXEmbed = true; unsigned int *p = (unsigned int *)prop_return; if (nitems_return >= 2) clientversion = p[0]; } XFree(prop_return); } // Store client window's original size and placement. Window root; int x_return, y_return; unsigned int width_return, height_return, border_width_return, depth_return; XGetGeometry(q->x11Info().display(), client, &root, &x_return, &y_return, &width_return, &height_return, &border_width_return, &depth_return); clientOriginalRect.setCoords(x_return, y_return, x_return + width_return - 1, y_return + height_return - 1); // Ask the client for its minimum size. XSizeHints size; long msize; if (XGetWMNormalHints(q->x11Info().display(), client, &size, &msize) && (size.flags & PMinSize)) { wmMinimumSizeHint = QSize(size.min_width, size.min_height); q->updateGeometry(); } // The container should set the data2 field to the lowest of its // supported version number and that of the client (from // _XEMBED_INFO property). unsigned int minversion = version > clientversion ? clientversion : version; sendXEmbedMessage(client, q->x11Info().display(), XEMBED_EMBEDDED_NOTIFY, q->internalWinId(), minversion); XMapWindow(q->x11Info().display(), client); // Resize it, but no smaller than its minimum size hint. XResizeWindow(q->x11Info().display(), client, qMax(q->width(), wmMinimumSizeHint.width()), qMax(q->height(), wmMinimumSizeHint.height())); q->update(); // Not mentioned in the protocol is that if the container // is already active, the client must be activated to work // properly. if (q->window()->isActiveWindow()) sendXEmbedMessage(client, q->x11Info().display(), XEMBED_WINDOW_ACTIVATE); // Also, if the container already has focus, then it must // send a focus in message to its new client; otherwise we ask // it to remove focus. if (q->focusWidget() == q && q->hasFocus()) sendXEmbedMessage(client, q->x11Info().display(), XEMBED_FOCUS_IN, XEMBED_FOCUS_FIRST); else sendXEmbedMessage(client, q->x11Info().display(), XEMBED_FOCUS_OUT); if (!clientIsXEmbed) { checkGrab(); if (q->hasFocus()) { XSetInputFocus(q->x11Info().display(), client, XRevertToParent, x11Time()); } else { if (!isEmbedded()) moveInputToProxy(); } } emit q->clientIsEmbedded();}/*! \internal Moves X11 keyboard input focus to the focusProxy, unless the focus is there already. When X11 keyboard input focus is on the focusProxy, which is a child of the container and a sibling of the client, X11 keypresses and keyreleases will always go to the proxy and not to the client.*/void QX11EmbedContainerPrivate::moveInputToProxy(){ Q_Q(QX11EmbedContainer); WId focus; int revert_to; XGetInputFocus(q->x11Info().display(), &focus, &revert_to); if (focus != focusProxy->internalWinId()) XSetInputFocus(q->x11Info().display(), focusProxy->internalWinId(), XRevertToParent, x11Time());}/*! \internal Ask the window manager to give us a default minimum size.*/QSize QX11EmbedContainer::minimumSizeHint() const{ Q_D(const QX11EmbedContainer); if (!d->client || !d->wmMinimumSizeHint.isValid()) return QWidget::minimumSizeHint(); return d->wmMinimumSizeHint;}/*! \internal*/void QX11EmbedContainerPrivate::checkGrab(){ Q_Q(QX11EmbedContainer); if (!clientIsXEmbed && q->isActiveWindow() && !q->hasFocus()) { if (!xgrab) { XGrabButton(q->x11Info().display(), AnyButton, AnyModifier, q->internalWinId(), true, ButtonPressMask, GrabModeSync, GrabModeAsync, None, None); } xgrab = true; } else { if (xgrab) XUngrabButton(q->x11Info().display(), AnyButton, AnyModifier, q->internalWinId()); xgrab = false; }}/*! Detaches the client from the embedder. The client will appear as a standalone window on the desktop.*/void QX11EmbedContainer::discardClient(){ Q_D(QX11EmbedContainer); if (d->client) { XResizeWindow(x11Info().display(), d->client, d->clientOriginalRect.width(), d->clientOriginalRect.height()); d->rejectClient(d->client); }}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -