X11是Linux发行系统中广泛采用的显示协议,各个系统基本上都支持XLib库,作为底层接口,XReparentWindow接口的功能就是重新设置父窗口,注意这个可以跨进程设置父窗口,例如将已经运行的进程的父窗口设置自己的程序Wid,即可将第三方进程嵌入到自己的程序窗口中,自己用这个接口写了一段代码,在UOS上运行的正常,后面跑在Kylin 4.0.2和V10上,程序出现了一些难以琢磨的情况,嵌入的程序无法focus,查阅到qt的container容器可以完成这个功能,查阅了qt4和qt5的源码,发先程序嵌入确实不是简简单单的XReparentWindow调用,后续还涉及到各种消息转发操作,后续学习。
bool QX11EmbedContainer::eventFilter(QObject *o, QEvent *event)
{
Q_D(QX11EmbedContainer);
switch (event->type()) {
case QEvent::KeyPress:
// Forward any keypresses to our client.
if (o == this && d->client) {
lastKeyEvent.window = d->client;
XSendEvent(x11Info().display(), d->client, false, KeyPressMask, (XEvent *) &lastKeyEvent);
return true;
}
break;
case QEvent::KeyRelease:
// Forward any keyreleases to our client.
if (o == this && d->client) {
lastKeyEvent.window = d->client;
XSendEvent(x11Info().display(), d->client, false, KeyReleaseMask, (XEvent *) &lastKeyEvent);
return true;
}
break;
case QEvent::WindowActivate:
// When our container window is activated, we pass the
// activation message on to our client. Note that X input
// focus is set to our focus proxy. We want to intercept all
// keypresses.
if (o == window() && d->client) {
if (d->clientIsXEmbed) {
sendXEmbedMessage(d->client, x11Info().display(), XEMBED_WINDOW_ACTIVATE);
} else {
d->checkGrab();
if (hasFocus())
XSetInputFocus(x11Info().display(), d->client, XRevertToParent, x11Time());
}
if (!d->isEmbedded())
d->moveInputToProxy();
}
break;
case QEvent::WindowDeactivate:
// When our container window is deactivated, we pass the
// deactivation message to our client.
if (o == window() && d->client) {
if (d->clientIsXEmbed)
sendXEmbedMessage(d->client, x11Info().display(), XEMBED_WINDOW_DEACTIVATE);
else
d->checkGrab();
}
break;
case QEvent::FocusIn:
// When receiving FocusIn events generated by Tab or Backtab,
// we pass focus on to our client. Any mouse activity is sent
// directly to the client, and it will ask us for focus with
// XEMBED_REQUEST_FOCUS.
if (o == this && d->client) {
if (!d->isEmbedded())
d->activeContainer = this;
if (d->clientIsXEmbed) {
if (!d->isEmbedded())
d->moveInputToProxy();
QFocusEvent *fe = (QFocusEvent *)event;
switch (fe->reason()) {
case Qt::TabFocusReason:
sendXEmbedMessage(d->client, x11Info().display(), XEMBED_FOCUS_IN, XEMBED_FOCUS_FIRST);
break;
case Qt::BacktabFocusReason:
sendXEmbedMessage(d->client, x11Info().display(), XEMBED_FOCUS_IN, XEMBED_FOCUS_LAST);
break;
default:
sendXEmbedMessage(d->client, x11Info().display(), XEMBED_FOCUS_IN, XEMBED_FOCUS_CURRENT);
break;
}
} else {
d->checkGrab();
XSetInputFocus(x11Info().display(), d->client, XRevertToParent, x11Time());
}
}
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->activeContainer = 0;
if (isActiveWindow())
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(x11Info().screen()), 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 QWidget::eventFilter(o, event);
}
麒麟系统和WPS软件,qt4的QXEmbedContainer和qt5 createContainer嵌入wps时显示空白,慢慢研究解决方案去。