Implement XEmbed protocol

Add a static QWindow::fromWinId(WId id) constructor which can be used to
create a QWindow object representing windows created by other processes.
Then, QWindow::setParent() can be used to embed a window into a foreign
window socket and QWindow::setTransientParent() to stick the current
window on top of a foreign window.

The changes in the QtWidgets module ensure that the focus chain (TAB
navigation) correctly works when a QtWidgets-based window is embedded
into another application.

As far as the platform implementation is concerned, this commit only
implements the embedding functionality in the XCB plugin. So, this is
roughly equivalent to the Qt4 QX11EmbedWidget functionality.

Change-Id: Iff8f7b9ee974d33fb30f36056f7838b433a413c7
Reviewed-by: Gunnar Sletta <gunnar.sletta@digia.com>
This commit is contained in:
Alberto Mardegan 2012-12-12 17:18:28 +01:00 committed by The Qt Project
parent f0533ba8c2
commit b5bdd31de4
20 changed files with 410 additions and 24 deletions

View File

@ -284,6 +284,7 @@ public:
SplashScreen = ToolTip | Dialog, SplashScreen = ToolTip | Dialog,
Desktop = 0x00000010 | Window, Desktop = 0x00000010 | Window,
SubWindow = 0x00000012, SubWindow = 0x00000012,
ForeignWindow = 0x00000020 | Window,
WindowType_Mask = 0x000000ff, WindowType_Mask = 0x000000ff,
MSWindowsFixedSizeDialogHint = 0x00000100, MSWindowsFixedSizeDialogHint = 0x00000100,

View File

@ -1966,6 +1966,10 @@
\value SubWindow Indicates that this widget is a sub-window, such \value SubWindow Indicates that this widget is a sub-window, such
as a QMdiSubWindow widget. as a QMdiSubWindow widget.
\value ForeignWindow Indicates that this window object is a handle
representing a native platform window created by
another process or by manually using native code.
There are also a number of flags which you can use to customize There are also a number of flags which you can use to customize
the appearance of top-level windows. These have no effect on other the appearance of top-level windows. These have no effect on other
windows: windows:

View File

@ -1605,7 +1605,7 @@ void QGuiApplicationPrivate::processActivatedEvent(QWindowSystemInterfacePrivate
} }
if (QGuiApplicationPrivate::focus_window) { if (QGuiApplicationPrivate::focus_window) {
QFocusEvent focusIn(QEvent::FocusIn); QFocusEvent focusIn(QEvent::FocusIn, e->reason);
QCoreApplication::sendSpontaneousEvent(QGuiApplicationPrivate::focus_window, &focusIn); QCoreApplication::sendSpontaneousEvent(QGuiApplicationPrivate::focus_window, &focusIn);
QObject::connect(QGuiApplicationPrivate::focus_window, SIGNAL(focusObjectChanged(QObject*)), QObject::connect(QGuiApplicationPrivate::focus_window, SIGNAL(focusObjectChanged(QObject*)),
qApp, SLOT(_q_updateFocusObject(QObject*))); qApp, SLOT(_q_updateFocusObject(QObject*)));

View File

@ -206,6 +206,10 @@ QPlatformServices *QPlatformIntegration::services() const
state explicitly by using QWindowSystemInterface::handleApplicationStateChanged(). state explicitly by using QWindowSystemInterface::handleApplicationStateChanged().
If not set, application state will follow window activation, which is the normal If not set, application state will follow window activation, which is the normal
behavior for desktop platforms. behavior for desktop platforms.
\value ForeignWindows The platform allows creating QWindows which represent
native windows created by other processes or anyway created by using native
libraries.
*/ */

View File

@ -89,7 +89,8 @@ public:
BufferQueueingOpenGL, BufferQueueingOpenGL,
WindowMasks, WindowMasks,
MultipleWindows, MultipleWindows,
ApplicationState ApplicationState,
ForeignWindows
}; };
virtual ~QPlatformIntegration() { } virtual ~QPlatformIntegration() { }

View File

@ -474,6 +474,10 @@ void QWindow::create()
WId QWindow::winId() const WId QWindow::winId() const
{ {
Q_D(const QWindow); Q_D(const QWindow);
if (type() == Qt::ForeignWindow)
return WId(property("_q_foreignWinId").value<WId>());
if(!d->platformWindow) if(!d->platformWindow)
const_cast<QWindow *>(this)->create(); const_cast<QWindow *>(this)->create();
@ -499,8 +503,11 @@ QWindow *QWindow::parent() const
the clip of the window, so it will be clipped to the \a parent window. the clip of the window, so it will be clipped to the \a parent window.
Setting \a parent to be 0 will make the window become a top level window. Setting \a parent to be 0 will make the window become a top level window.
*/
If \a parent is a window created by fromWinId(), then the current window
will be embedded inside \a parent, if the platform supports it. Window
embedding is currently supported only by the X11 platform plugin.
*/
void QWindow::setParent(QWindow *parent) void QWindow::setParent(QWindow *parent)
{ {
Q_D(QWindow); Q_D(QWindow);
@ -2104,6 +2111,34 @@ void QWindowPrivate::maybeQuitOnLastWindowClosed()
} }
/*!
Creates a local representation of a window created by another process or by
using native libraries below Qt.
Given the handle \a id to a native window, this method creates a QWindow
object which can be used to represent the window when invoking methods like
setParent() and setTransientParent().
This can be used, on platforms which support it, to embed a window inside a
container or to make a window stick on top of a window created by another
process.
\sa setParent()
\sa setTransientParent()
*/
QWindow *QWindow::fromWinId(WId id)
{
if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::ForeignWindows)) {
qWarning() << "QWindow::fromWinId(): platform plugin does not support foreign windows.";
return 0;
}
QWindow *window = new QWindow;
window->setFlags(Qt::ForeignWindow);
window->setProperty("_q_foreignWinId", QVariant::fromValue(id));
window->create();
return window;
}
#ifndef QT_NO_CURSOR #ifndef QT_NO_CURSOR
/*! /*!
\brief set the cursor shape for this window \brief set the cursor shape for this window

View File

@ -257,6 +257,8 @@ public:
void unsetCursor(); void unsetCursor();
#endif #endif
static QWindow *fromWinId(WId id);
public Q_SLOTS: public Q_SLOTS:
void setVisible(bool visible); void setVisible(bool visible);

View File

@ -112,9 +112,10 @@ void QWindowSystemInterface::handleEnterLeaveEvent(QWindow *enter, QWindow *leav
} }
} }
void QWindowSystemInterface::handleWindowActivated(QWindow *tlw) void QWindowSystemInterface::handleWindowActivated(QWindow *tlw, Qt::FocusReason r)
{ {
QWindowSystemInterfacePrivate::ActivatedWindowEvent *e = new QWindowSystemInterfacePrivate::ActivatedWindowEvent(tlw); QWindowSystemInterfacePrivate::ActivatedWindowEvent *e =
new QWindowSystemInterfacePrivate::ActivatedWindowEvent(tlw, r);
QWindowSystemInterfacePrivate::handleWindowSystemEvent(e); QWindowSystemInterfacePrivate::handleWindowSystemEvent(e);
} }

View File

@ -135,7 +135,8 @@ public:
static void handleEnterEvent(QWindow *w, const QPointF &local = QPointF(), const QPointF& global = QPointF()); static void handleEnterEvent(QWindow *w, const QPointF &local = QPointF(), const QPointF& global = QPointF());
static void handleLeaveEvent(QWindow *w); static void handleLeaveEvent(QWindow *w);
static void handleEnterLeaveEvent(QWindow *enter, QWindow *leave, const QPointF &local = QPointF(), const QPointF& global = QPointF()); static void handleEnterLeaveEvent(QWindow *enter, QWindow *leave, const QPointF &local = QPointF(), const QPointF& global = QPointF());
static void handleWindowActivated(QWindow *w); static void handleWindowActivated(QWindow *w, Qt::FocusReason r = Qt::OtherFocusReason);
static void handleWindowStateChanged(QWindow *w, Qt::WindowState newState); static void handleWindowStateChanged(QWindow *w, Qt::WindowState newState);
static void handleApplicationStateChanged(Qt::ApplicationState newState); static void handleApplicationStateChanged(Qt::ApplicationState newState);

View File

@ -139,10 +139,11 @@ public:
class ActivatedWindowEvent : public WindowSystemEvent { class ActivatedWindowEvent : public WindowSystemEvent {
public: public:
explicit ActivatedWindowEvent(QWindow *activatedWindow) explicit ActivatedWindowEvent(QWindow *activatedWindow, Qt::FocusReason r)
: WindowSystemEvent(ActivatedWindow), activated(activatedWindow) : WindowSystemEvent(ActivatedWindow), activated(activatedWindow), reason(r)
{ } { }
QPointer<QWindow> activated; QPointer<QWindow> activated;
Qt::FocusReason reason;
}; };
class WindowStateChangedEvent : public WindowSystemEvent { class WindowStateChangedEvent : public WindowSystemEvent {

View File

@ -1066,7 +1066,7 @@ void QXcbConnection::processXcbEvents()
while (it != m_peekFuncs.end()) { while (it != m_peekFuncs.end()) {
// These callbacks return true if the event is what they were // These callbacks return true if the event is what they were
// waiting for, remove them from the list in that case. // waiting for, remove them from the list in that case.
if ((*it)(event)) if ((*it)(this, event))
it = m_peekFuncs.erase(it); it = m_peekFuncs.erase(it);
else else
++it; ++it;
@ -1086,7 +1086,7 @@ void QXcbConnection::processXcbEvents()
// Indicate with a null event that the event the callbacks are waiting for // Indicate with a null event that the event the callbacks are waiting for
// is not in the queue currently. // is not in the queue currently.
Q_FOREACH (PeekFunc f, m_peekFuncs) Q_FOREACH (PeekFunc f, m_peekFuncs)
f(0); f(this, 0);
m_peekFuncs.clear(); m_peekFuncs.clear();
xcb_flush(xcb_connection()); xcb_flush(xcb_connection());

View File

@ -364,7 +364,7 @@ public:
template<typename T> template<typename T>
inline xcb_generic_event_t *checkEvent(T &checker); inline xcb_generic_event_t *checkEvent(T &checker);
typedef bool (*PeekFunc)(xcb_generic_event_t *); typedef bool (*PeekFunc)(QXcbConnection *, xcb_generic_event_t *);
void addPeekFunc(PeekFunc f); void addPeekFunc(PeekFunc f);
inline xcb_timestamp_t time() const { return m_time; } inline xcb_timestamp_t time() const { return m_time; }

View File

@ -228,6 +228,7 @@ bool QXcbIntegration::hasCapability(QPlatformIntegration::Capability cap) const
case ThreadedOpenGL: return m_connections.at(0)->supportsThreadedRendering(); case ThreadedOpenGL: return m_connections.at(0)->supportsThreadedRendering();
case WindowMasks: return true; case WindowMasks: return true;
case MultipleWindows: return true; case MultipleWindows: return true;
case ForeignWindows: return true;
default: return QPlatformIntegration::hasCapability(cap); default: return QPlatformIntegration::hasCapability(cap);
} }
} }

View File

@ -122,6 +122,36 @@ enum {
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
#undef FocusIn
enum QX11EmbedFocusInDetail {
XEMBED_FOCUS_CURRENT = 0,
XEMBED_FOCUS_FIRST = 1,
XEMBED_FOCUS_LAST = 2
};
enum QX11EmbedInfoFlags {
XEMBED_MAPPED = (1 << 0),
};
enum QX11EmbedMessageType {
XEMBED_EMBEDDED_NOTIFY = 0,
XEMBED_WINDOW_ACTIVATE = 1,
XEMBED_WINDOW_DEACTIVATE = 2,
XEMBED_REQUEST_FOCUS = 3,
XEMBED_FOCUS_IN = 4,
XEMBED_FOCUS_OUT = 5,
XEMBED_FOCUS_NEXT = 6,
XEMBED_FOCUS_PREV = 7,
XEMBED_MODALITY_ON = 10,
XEMBED_MODALITY_OFF = 11,
XEMBED_REGISTER_ACCELERATOR = 12,
XEMBED_UNREGISTER_ACCELERATOR = 13,
XEMBED_ACTIVATE_ACCELERATOR = 14
};
static unsigned int XEMBED_VERSION = 0;
// Returns true if we should set WM_TRANSIENT_FOR on \a w // Returns true if we should set WM_TRANSIENT_FOR on \a w
static inline bool isTransient(const QWindow *w) static inline bool isTransient(const QWindow *w)
{ {
@ -157,6 +187,7 @@ QXcbWindow::QXcbWindow(QWindow *window)
, m_mapped(false) , m_mapped(false)
, m_transparent(false) , m_transparent(false)
, m_deferredActivation(false) , m_deferredActivation(false)
, m_embedded(false)
, m_netWmUserTimeWindow(XCB_NONE) , m_netWmUserTimeWindow(XCB_NONE)
, m_dirtyFrameMargins(false) , m_dirtyFrameMargins(false)
#if defined(XCB_USE_EGL) #if defined(XCB_USE_EGL)
@ -168,7 +199,10 @@ QXcbWindow::QXcbWindow(QWindow *window)
setConnection(m_screen->connection()); setConnection(m_screen->connection());
if (window->type() != Qt::ForeignWindow)
create(); create();
else
m_window = window->winId();
} }
void QXcbWindow::create() void QXcbWindow::create()
@ -235,8 +269,10 @@ void QXcbWindow::create()
} }
xcb_window_t xcb_parent_id = m_screen->root(); xcb_window_t xcb_parent_id = m_screen->root();
if (parent()) if (parent()) {
xcb_parent_id = static_cast<QXcbWindow *>(parent())->xcb_window(); xcb_parent_id = static_cast<QXcbWindow *>(parent())->xcb_window();
m_embedded = parent()->window()->type() == Qt::ForeignWindow;
}
m_format = window()->requestedFormat(); m_format = window()->requestedFormat();
@ -368,6 +404,14 @@ void QXcbWindow::create()
atom(QXcbAtom::WM_CLIENT_LEADER), XCB_ATOM_WINDOW, 32, atom(QXcbAtom::WM_CLIENT_LEADER), XCB_ATOM_WINDOW, 32,
1, &leader)); 1, &leader));
/* Add XEMBED info; this operation doesn't initiate the embedding. */
long data[] = { XEMBED_VERSION, XEMBED_MAPPED };
Q_XCB_CALL(xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, m_window,
atom(QXcbAtom::_XEMBED_INFO),
atom(QXcbAtom::_XEMBED_INFO),
32, 2, (void *)data));
#ifdef XCB_USE_XINPUT2_MAEMO #ifdef XCB_USE_XINPUT2_MAEMO
if (connection()->isUsingXInput2Maemo()) { if (connection()->isUsingXInput2Maemo()) {
XIEventMask xieventmask; XIEventMask xieventmask;
@ -410,6 +454,7 @@ void QXcbWindow::create()
QXcbWindow::~QXcbWindow() QXcbWindow::~QXcbWindow()
{ {
if (window()->type() != Qt::ForeignWindow)
destroy(); destroy();
} }
@ -574,9 +619,10 @@ void QXcbWindow::show()
propagateSizeHints(); propagateSizeHints();
// update WM_TRANSIENT_FOR // update WM_TRANSIENT_FOR
if (isTransient(window())) { const QWindow *tp = window()->transientParent();
if (isTransient(window()) || tp != 0) {
xcb_window_t transientXcbParent = 0; xcb_window_t transientXcbParent = 0;
if (const QWindow *tp = window()->transientParent()) if (tp)
transientXcbParent = static_cast<const QXcbWindow *>(tp->handle())->winId(); transientXcbParent = static_cast<const QXcbWindow *>(tp->handle())->winId();
// Default to client leader if there is no transient parent, else modal dialogs can // Default to client leader if there is no transient parent, else modal dialogs can
// be hidden by their parents. // be hidden by their parents.
@ -1140,7 +1186,15 @@ void QXcbWindow::setParent(const QPlatformWindow *parent)
{ {
QPoint topLeft = geometry().topLeft(); QPoint topLeft = geometry().topLeft();
xcb_window_t xcb_parent_id = parent ? static_cast<const QXcbWindow *>(parent)->xcb_window() : m_screen->root(); xcb_window_t xcb_parent_id;
if (parent) {
const QXcbWindow *qXcbParent = static_cast<const QXcbWindow *>(parent);
xcb_parent_id = qXcbParent->xcb_window();
m_embedded = qXcbParent->window()->type() == Qt::ForeignWindow;
} else {
xcb_parent_id = m_screen->root();
m_embedded = false;
}
Q_XCB_CALL(xcb_reparent_window(xcb_connection(), xcb_window(), xcb_parent_id, topLeft.x(), topLeft.y())); Q_XCB_CALL(xcb_reparent_window(xcb_connection(), xcb_window(), xcb_parent_id, topLeft.x(), topLeft.y()));
} }
@ -1271,6 +1325,13 @@ void QXcbWindow::propagateSizeHints()
void QXcbWindow::requestActivateWindow() void QXcbWindow::requestActivateWindow()
{ {
/* Never activate embedded windows; doing that would prevent the container
* to re-gain the keyboard focus later. */
if (m_embedded) {
QPlatformWindow::requestActivateWindow();
return;
}
if (!m_mapped) { if (!m_mapped) {
m_deferredActivation = true; m_deferredActivation = true;
return; return;
@ -1434,7 +1495,8 @@ void QXcbWindow::handleClientMessageEvent(const xcb_client_message_event_t *even
} else if (event->type == atom(QXcbAtom::XdndDrop)) { } else if (event->type == atom(QXcbAtom::XdndDrop)) {
connection()->drag()->handleDrop(window(), event); connection()->drag()->handleDrop(window(), event);
#endif #endif
} else if (event->type == atom(QXcbAtom::_XEMBED)) { // QSystemTrayIcon } else if (event->type == atom(QXcbAtom::_XEMBED)) {
handleXEmbedMessage(event);
} else { } else {
qWarning() << "QXcbWindow: Unhandled client message:" << connection()->atomName(event->type); qWarning() << "QXcbWindow: Unhandled client message:" << connection()->atomName(event->type);
} }
@ -1476,6 +1538,53 @@ bool QXcbWindow::isExposed() const
return m_mapped; return m_mapped;
} }
bool QXcbWindow::isEmbedded(const QPlatformWindow *parentWindow) const
{
if (!m_embedded)
return false;
return parentWindow ? (parentWindow == parent()) : true;
}
QPoint QXcbWindow::mapToGlobal(const QPoint &pos) const
{
if (!m_embedded)
return pos;
QPoint ret;
xcb_translate_coordinates_cookie_t cookie =
xcb_translate_coordinates(xcb_connection(), xcb_window(), m_screen->root(),
pos.x(), pos.y());
xcb_translate_coordinates_reply_t *reply =
xcb_translate_coordinates_reply(xcb_connection(), cookie, NULL);
if (reply) {
ret.setX(reply->dst_x);
ret.setY(reply->dst_y);
free(reply);
}
return ret;
}
QPoint QXcbWindow::mapFromGlobal(const QPoint &pos) const
{
if (!m_embedded)
return pos;
QPoint ret;
xcb_translate_coordinates_cookie_t cookie =
xcb_translate_coordinates(xcb_connection(), m_screen->root(), xcb_window(),
pos.x(), pos.y());
xcb_translate_coordinates_reply_t *reply =
xcb_translate_coordinates_reply(xcb_connection(), cookie, NULL);
if (reply) {
ret.setX(reply->dst_x);
ret.setY(reply->dst_y);
free(reply);
}
return ret;
}
void QXcbWindow::handleMapNotifyEvent(const xcb_map_notify_event_t *event) void QXcbWindow::handleMapNotifyEvent(const xcb_map_notify_event_t *event)
{ {
if (event->window == m_window) { if (event->window == m_window) {
@ -1506,6 +1615,15 @@ void QXcbWindow::handleButtonPressEvent(const xcb_button_press_event_t *event)
updateNetWmUserTime(event->time); updateNetWmUserTime(event->time);
if (m_embedded) {
if (window() != QGuiApplication::focusWindow()) {
const QXcbWindow *container = static_cast<const QXcbWindow *>(parent());
Q_ASSERT(container != 0);
sendXEmbedMessage(container->xcb_window(), XEMBED_REQUEST_FOCUS);
}
}
QPoint local(event->event_x, event->event_y); QPoint local(event->event_x, event->event_y);
QPoint global(event->root_x, event->root_y); QPoint global(event->root_x, event->root_y);
@ -1661,6 +1779,25 @@ void QXcbWindow::handlePropertyNotifyEvent(const xcb_property_notify_event_t *ev
QWindowSystemInterface::handleWindowStateChanged(window(), newState); QWindowSystemInterface::handleWindowStateChanged(window(), newState);
m_lastWindowStateEvent = newState; m_lastWindowStateEvent = newState;
} }
return;
}
const xcb_atom_t xEmbedInfoAtom = atom(QXcbAtom::_XEMBED_INFO);
if (event->atom == xEmbedInfoAtom) {
const xcb_get_property_cookie_t get_cookie =
xcb_get_property(xcb_connection(), 0, m_window, xEmbedInfoAtom,
XCB_ATOM_ANY, 0, 3);
xcb_get_property_reply_t *reply =
xcb_get_property_reply(xcb_connection(), get_cookie, NULL);
if (reply && reply->length >= 2) {
const long *data = (const long *)xcb_get_property_value(reply);
if (data[1] & XEMBED_MAPPED)
Q_XCB_CALL(xcb_map_window(xcb_connection(), m_window));
else
Q_XCB_CALL(xcb_unmap_window(xcb_connection(), m_window));
}
free(reply);
} }
} }
@ -1672,7 +1809,7 @@ void QXcbWindow::handleFocusInEvent(const xcb_focus_in_event_t *)
QWindowSystemInterface::handleWindowActivated(w); QWindowSystemInterface::handleWindowActivated(w);
} }
static bool focusInPeeker(xcb_generic_event_t *event) static bool focusInPeeker(QXcbConnection *connection, xcb_generic_event_t *event)
{ {
if (!event) { if (!event) {
// FocusIn event is not in the queue, proceed with FocusOut normally. // FocusIn event is not in the queue, proceed with FocusOut normally.
@ -1680,7 +1817,18 @@ static bool focusInPeeker(xcb_generic_event_t *event)
return true; return true;
} }
uint response_type = event->response_type & ~0x80; uint response_type = event->response_type & ~0x80;
return response_type == XCB_FOCUS_IN; if (response_type == XCB_FOCUS_IN)
return true;
/* We are also interested in XEMBED_FOCUS_IN events */
if (response_type == XCB_CLIENT_MESSAGE) {
xcb_client_message_event_t *cme = (xcb_client_message_event_t *)event;
if (cme->type == connection->atom(QXcbAtom::_XEMBED)
&& cme->data.data32[1] == XEMBED_FOCUS_IN)
return true;
}
return false;
} }
void QXcbWindow::handleFocusOutEvent(const xcb_focus_out_event_t *) void QXcbWindow::handleFocusOutEvent(const xcb_focus_out_event_t *)
@ -1744,6 +1892,35 @@ void QXcbWindow::setCursor(xcb_cursor_t cursor)
xcb_flush(xcb_connection()); xcb_flush(xcb_connection());
} }
void QXcbWindow::windowEvent(QEvent *event)
{
switch (event->type()) {
case QEvent::FocusIn:
if (m_embedded && !event->spontaneous()) {
QFocusEvent *focusEvent = static_cast<QFocusEvent *>(event);
switch (focusEvent->reason()) {
case Qt::TabFocusReason:
case Qt::BacktabFocusReason:
{
const QXcbWindow *container =
static_cast<const QXcbWindow *>(parent());
sendXEmbedMessage(container->xcb_window(),
focusEvent->reason() == Qt::TabFocusReason ?
XEMBED_FOCUS_NEXT : XEMBED_FOCUS_PREV);
event->accept();
}
break;
default:
break;
}
}
break;
default:
break;
}
QPlatformWindow::windowEvent(event);
}
bool QXcbWindow::startSystemResize(const QPoint &pos, Qt::Corner corner) bool QXcbWindow::startSystemResize(const QPoint &pos, Qt::Corner corner)
{ {
const xcb_atom_t moveResize = connection()->atom(QXcbAtom::_NET_WM_MOVERESIZE); const xcb_atom_t moveResize = connection()->atom(QXcbAtom::_NET_WM_MOVERESIZE);
@ -1772,6 +1949,71 @@ bool QXcbWindow::startSystemResize(const QPoint &pos, Qt::Corner corner)
return true; return true;
} }
// Sends an XEmbed message.
void QXcbWindow::sendXEmbedMessage(xcb_window_t window, long message,
long detail, long data1, long data2)
{
xcb_client_message_event_t event;
event.response_type = XCB_CLIENT_MESSAGE;
event.format = 32;
event.window = window;
event.type = atom(QXcbAtom::_XEMBED);
event.data.data32[0] = connection()->time();
event.data.data32[1] = message;
event.data.data32[2] = detail;
event.data.data32[3] = data1;
event.data.data32[4] = data2;
Q_XCB_CALL(xcb_send_event(xcb_connection(), false, window,
XCB_EVENT_MASK_NO_EVENT, (const char *)&event));
}
static bool activeWindowChangeQueued(const QWindow *window)
{
/* Check from window system event queue if the next queued activation
* targets a window other than @window.
*/
QWindowSystemInterfacePrivate::ActivatedWindowEvent *systemEvent =
static_cast<QWindowSystemInterfacePrivate::ActivatedWindowEvent *>
(QWindowSystemInterfacePrivate::peekWindowSystemEvent(QWindowSystemInterfacePrivate::ActivatedWindow));
return systemEvent && systemEvent->activated != window;
}
void QXcbWindow::handleXEmbedMessage(const xcb_client_message_event_t *event)
{
connection()->setTime(event->data.data32[0]);
switch (event->data.data32[1]) {
case XEMBED_WINDOW_ACTIVATE:
case XEMBED_WINDOW_DEACTIVATE:
case XEMBED_EMBEDDED_NOTIFY:
break;
case XEMBED_FOCUS_IN:
Qt::FocusReason reason;
switch (event->data.data32[2]) {
case XEMBED_FOCUS_FIRST:
reason = Qt::TabFocusReason;
break;
case XEMBED_FOCUS_LAST:
reason = Qt::BacktabFocusReason;
break;
case XEMBED_FOCUS_CURRENT:
default:
reason = Qt::OtherFocusReason;
break;
}
connection()->setFocusWindow(static_cast<QXcbWindow*>(window()->handle()));
QWindowSystemInterface::handleWindowActivated(window(), reason);
break;
case XEMBED_FOCUS_OUT:
if (window() == QGuiApplication::focusWindow()
&& !activeWindowChangeQueued(window())) {
connection()->setFocusWindow(0);
QWindowSystemInterface::handleWindowActivated(0);
}
break;
}
}
#if !defined(QT_NO_SHAPE) #if !defined(QT_NO_SHAPE)
static inline xcb_rectangle_t qRectToXCBRectangle(const QRect &r) static inline xcb_rectangle_t qRectToXCBRectangle(const QRect &r)

View File

@ -87,6 +87,9 @@ public:
void setParent(const QPlatformWindow *window); void setParent(const QPlatformWindow *window);
bool isExposed() const; bool isExposed() const;
bool isEmbedded(const QPlatformWindow *parentWindow) const;
QPoint mapToGlobal(const QPoint &pos) const;
QPoint mapFromGlobal(const QPoint &pos) const;
void setWindowTitle(const QString &title); void setWindowTitle(const QString &title);
void setWindowIcon(const QIcon &icon); void setWindowIcon(const QIcon &icon);
@ -107,6 +110,8 @@ public:
QSurfaceFormat format() const; QSurfaceFormat format() const;
void windowEvent(QEvent *event);
bool startSystemResize(const QPoint &pos, Qt::Corner corner); bool startSystemResize(const QPoint &pos, Qt::Corner corner);
void setOpacity(qreal level); void setOpacity(qreal level);
@ -158,6 +163,9 @@ private:
void updateDoesNotAcceptFocus(bool doesNotAcceptFocus); void updateDoesNotAcceptFocus(bool doesNotAcceptFocus);
QRect windowToWmGeometry(QRect r) const; QRect windowToWmGeometry(QRect r) const;
void sendXEmbedMessage(xcb_window_t window, long message,
long detail = 0, long data1 = 0, long data2 = 0);
void handleXEmbedMessage(const xcb_client_message_event_t *event);
void create(); void create();
void destroy(); void destroy();
@ -184,6 +192,7 @@ private:
bool m_deferredActivation; bool m_deferredActivation;
bool m_deferredExpose; bool m_deferredExpose;
bool m_configureNotifyPending; bool m_configureNotifyPending;
bool m_embedded;
xcb_window_t m_netWmUserTimeWindow; xcb_window_t m_netWmUserTimeWindow;
QSurfaceFormat m_format; QSurfaceFormat m_format;

View File

@ -2010,7 +2010,8 @@ void QApplication::setActiveWindow(QWidget* act)
* Returns 0 if a new focus widget could not be found. * Returns 0 if a new focus widget could not be found.
* Shared with QGraphicsProxyWidgetPrivate::findFocusChild() * Shared with QGraphicsProxyWidgetPrivate::findFocusChild()
*/ */
QWidget *QApplicationPrivate::focusNextPrevChild_helper(QWidget *toplevel, bool next) QWidget *QApplicationPrivate::focusNextPrevChild_helper(QWidget *toplevel, bool next,
bool *wrappingOccurred)
{ {
uint focus_flag = qt_tab_all_widgets() ? Qt::TabFocus : Qt::StrongFocus; uint focus_flag = qt_tab_all_widgets() ? Qt::TabFocus : Qt::StrongFocus;
@ -2020,18 +2021,29 @@ QWidget *QApplicationPrivate::focusNextPrevChild_helper(QWidget *toplevel, bool
QWidget *w = f; QWidget *w = f;
QWidget *test = f->d_func()->focus_next; QWidget *test = f->d_func()->focus_next;
bool seenWindow = false;
bool focusWidgetAfterWindow = false;
while (test && test != f) { while (test && test != f) {
if (test->isWindow())
seenWindow = true;
if ((test->focusPolicy() & focus_flag) == focus_flag if ((test->focusPolicy() & focus_flag) == focus_flag
&& !(test->d_func()->extra && test->d_func()->extra->focus_proxy) && !(test->d_func()->extra && test->d_func()->extra->focus_proxy)
&& test->isVisibleTo(toplevel) && test->isEnabled() && test->isVisibleTo(toplevel) && test->isEnabled()
&& !(w->windowType() == Qt::SubWindow && !w->isAncestorOf(test)) && !(w->windowType() == Qt::SubWindow && !w->isAncestorOf(test))
&& (toplevel->windowType() != Qt::SubWindow || toplevel->isAncestorOf(test))) { && (toplevel->windowType() != Qt::SubWindow || toplevel->isAncestorOf(test))) {
w = test; w = test;
if (seenWindow)
focusWidgetAfterWindow = true;
if (next) if (next)
break; break;
} }
test = test->d_func()->focus_next; test = test->d_func()->focus_next;
} }
if (wrappingOccurred != 0)
*wrappingOccurred = next ? focusWidgetAfterWindow : !focusWidgetAfterWindow;
if (w == f) { if (w == f) {
if (qt_in_tab_key_event) { if (qt_in_tab_key_event) {
w->window()->setAttribute(Qt::WA_KeyboardFocusChange); w->window()->setAttribute(Qt::WA_KeyboardFocusChange);

View File

@ -165,7 +165,8 @@ public:
void closePopup(QWidget *popup); void closePopup(QWidget *popup);
void openPopup(QWidget *popup); void openPopup(QWidget *popup);
static void setFocusWidget(QWidget *focus, Qt::FocusReason reason); static void setFocusWidget(QWidget *focus, Qt::FocusReason reason);
static QWidget *focusNextPrevChild_helper(QWidget *toplevel, bool next); static QWidget *focusNextPrevChild_helper(QWidget *toplevel, bool next,
bool *wrappingOccurred = 0);
#ifndef QT_NO_GRAPHICSVIEW #ifndef QT_NO_GRAPHICSVIEW
// Maintain a list of all scenes to ensure font and palette propagation to // Maintain a list of all scenes to ensure font and palette propagation to

View File

@ -6138,10 +6138,34 @@ bool QWidget::focusNextPrevChild(bool next)
if (d->extra && d->extra->proxyWidget) if (d->extra && d->extra->proxyWidget)
return d->extra->proxyWidget->focusNextPrevChild(next); return d->extra->proxyWidget->focusNextPrevChild(next);
#endif #endif
QWidget *w = QApplicationPrivate::focusNextPrevChild_helper(this, next);
bool wrappingOccurred = false;
QWidget *w = QApplicationPrivate::focusNextPrevChild_helper(this, next,
&wrappingOccurred);
if (!w) return false; if (!w) return false;
w->setFocus(next ? Qt::TabFocusReason : Qt::BacktabFocusReason); Qt::FocusReason reason = next ? Qt::TabFocusReason : Qt::BacktabFocusReason;
/* If we are about to wrap the focus chain, give the platform
* implementation a chance to alter the wrapping behavior. This is
* especially needed when the window is embedded in a window created by
* another process.
*/
if (wrappingOccurred) {
QWindow *window = windowHandle();
if (window != 0) {
QWindowPrivate *winp = qt_window_private(window);
if (winp->platformWindow != 0) {
QFocusEvent event(QEvent::FocusIn, reason);
event.ignore();
winp->platformWindow->windowEvent(&event);
if (event.isAccepted()) return true;
}
}
}
w->setFocus(reason);
return true; return true;
} }

View File

@ -52,6 +52,8 @@
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
Q_WIDGETS_EXPORT extern bool qt_tab_all_widgets();
QWidget *qt_button_down = 0; // widget got last button-down QWidget *qt_button_down = 0; // widget got last button-down
static QWidget *qt_tablet_target = 0; static QWidget *qt_tablet_target = 0;
@ -123,6 +125,8 @@ bool QWidgetWindow::event(QEvent *event)
// these should not be sent to QWidget, the corresponding events // these should not be sent to QWidget, the corresponding events
// are sent by QApplicationPrivate::notifyActiveWindowChange() // are sent by QApplicationPrivate::notifyActiveWindowChange()
case QEvent::FocusIn: case QEvent::FocusIn:
handleFocusInEvent(static_cast<QFocusEvent *>(event));
// Fallthrough
case QEvent::FocusOut: { case QEvent::FocusOut: {
#ifndef QT_NO_ACCESSIBILITY #ifndef QT_NO_ACCESSIBILITY
QAccessible::State state; QAccessible::State state;
@ -284,6 +288,42 @@ void QWidgetWindow::handleEnterLeaveEvent(QEvent *event)
} }
} }
QWidget *QWidgetWindow::getFocusWidget(FocusWidgets fw)
{
QWidget *tlw = m_widget;
QWidget *w = tlw->nextInFocusChain();
QWidget *last = tlw;
uint focus_flag = qt_tab_all_widgets() ? Qt::TabFocus : Qt::StrongFocus;
while (w != tlw)
{
if (((w->focusPolicy() & focus_flag) == focus_flag)
&& w->isVisibleTo(m_widget) && w->isEnabled())
{
last = w;
if (fw == FirstFocusWidget)
break;
}
w = w->nextInFocusChain();
}
return last;
}
void QWidgetWindow::handleFocusInEvent(QFocusEvent *e)
{
QWidget *focusWidget = 0;
if (e->reason() == Qt::BacktabFocusReason)
focusWidget = getFocusWidget(LastFocusWidget);
else if (e->reason() == Qt::TabFocusReason)
focusWidget = getFocusWidget(FirstFocusWidget);
if (focusWidget != 0)
focusWidget->setFocus();
}
void QWidgetWindow::handleNonClientAreaMouseEvent(QMouseEvent *e) void QWidgetWindow::handleNonClientAreaMouseEvent(QMouseEvent *e)
{ {
QApplication::sendSpontaneousEvent(m_widget, e); QApplication::sendSpontaneousEvent(m_widget, e);

View File

@ -70,6 +70,7 @@ protected:
void handleCloseEvent(QCloseEvent *); void handleCloseEvent(QCloseEvent *);
void handleEnterLeaveEvent(QEvent *); void handleEnterLeaveEvent(QEvent *);
void handleFocusInEvent(QFocusEvent *);
void handleKeyEvent(QKeyEvent *); void handleKeyEvent(QKeyEvent *);
void handleMouseEvent(QMouseEvent *); void handleMouseEvent(QMouseEvent *);
void handleNonClientAreaMouseEvent(QMouseEvent *); void handleNonClientAreaMouseEvent(QMouseEvent *);
@ -100,6 +101,12 @@ private slots:
private: private:
void updateGeometry(); void updateGeometry();
enum FocusWidgets {
FirstFocusWidget,
LastFocusWidget
};
QWidget *getFocusWidget(FocusWidgets fw);
QWidget *m_widget; QWidget *m_widget;
QPointer<QWidget> m_implicit_mouse_grabber; QPointer<QWidget> m_implicit_mouse_grabber;
#ifndef QT_NO_DRAGANDDROP #ifndef QT_NO_DRAGANDDROP