summaryrefslogtreecommitdiffstats
path: root/src/plugins
diff options
context:
space:
mode:
authorAlberto Mardegan <alberto.mardegan@canonical.com>2012-12-12 17:18:28 +0100
committerThe Qt Project <gerrit-noreply@qt-project.org>2013-03-06 18:59:07 +0100
commitb5bdd31de41cb5e6d6abedce79864fc01d8d4984 (patch)
tree030b374282b12a2857d80295a993fc550cb722f7 /src/plugins
parentf0533ba8c22d6366f9d4fb8e8ce18554cd0d01e9 (diff)
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>
Diffstat (limited to 'src/plugins')
-rw-r--r--src/plugins/platforms/xcb/qxcbconnection.cpp4
-rw-r--r--src/plugins/platforms/xcb/qxcbconnection.h2
-rw-r--r--src/plugins/platforms/xcb/qxcbintegration.cpp1
-rw-r--r--src/plugins/platforms/xcb/qxcbwindow.cpp260
-rw-r--r--src/plugins/platforms/xcb/qxcbwindow.h9
5 files changed, 264 insertions, 12 deletions
diff --git a/src/plugins/platforms/xcb/qxcbconnection.cpp b/src/plugins/platforms/xcb/qxcbconnection.cpp
index 2467eb6e7a..1504bd99d2 100644
--- a/src/plugins/platforms/xcb/qxcbconnection.cpp
+++ b/src/plugins/platforms/xcb/qxcbconnection.cpp
@@ -1066,7 +1066,7 @@ void QXcbConnection::processXcbEvents()
while (it != m_peekFuncs.end()) {
// These callbacks return true if the event is what they were
// waiting for, remove them from the list in that case.
- if ((*it)(event))
+ if ((*it)(this, event))
it = m_peekFuncs.erase(it);
else
++it;
@@ -1086,7 +1086,7 @@ void QXcbConnection::processXcbEvents()
// Indicate with a null event that the event the callbacks are waiting for
// is not in the queue currently.
Q_FOREACH (PeekFunc f, m_peekFuncs)
- f(0);
+ f(this, 0);
m_peekFuncs.clear();
xcb_flush(xcb_connection());
diff --git a/src/plugins/platforms/xcb/qxcbconnection.h b/src/plugins/platforms/xcb/qxcbconnection.h
index b499f75b78..f69a8a9f35 100644
--- a/src/plugins/platforms/xcb/qxcbconnection.h
+++ b/src/plugins/platforms/xcb/qxcbconnection.h
@@ -364,7 +364,7 @@ public:
template<typename T>
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);
inline xcb_timestamp_t time() const { return m_time; }
diff --git a/src/plugins/platforms/xcb/qxcbintegration.cpp b/src/plugins/platforms/xcb/qxcbintegration.cpp
index 0a02ea02af..f0cabea43d 100644
--- a/src/plugins/platforms/xcb/qxcbintegration.cpp
+++ b/src/plugins/platforms/xcb/qxcbintegration.cpp
@@ -228,6 +228,7 @@ bool QXcbIntegration::hasCapability(QPlatformIntegration::Capability cap) const
case ThreadedOpenGL: return m_connections.at(0)->supportsThreadedRendering();
case WindowMasks: return true;
case MultipleWindows: return true;
+ case ForeignWindows: return true;
default: return QPlatformIntegration::hasCapability(cap);
}
}
diff --git a/src/plugins/platforms/xcb/qxcbwindow.cpp b/src/plugins/platforms/xcb/qxcbwindow.cpp
index abd2f7a147..27cd163c36 100644
--- a/src/plugins/platforms/xcb/qxcbwindow.cpp
+++ b/src/plugins/platforms/xcb/qxcbwindow.cpp
@@ -122,6 +122,36 @@ enum {
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
static inline bool isTransient(const QWindow *w)
{
@@ -157,6 +187,7 @@ QXcbWindow::QXcbWindow(QWindow *window)
, m_mapped(false)
, m_transparent(false)
, m_deferredActivation(false)
+ , m_embedded(false)
, m_netWmUserTimeWindow(XCB_NONE)
, m_dirtyFrameMargins(false)
#if defined(XCB_USE_EGL)
@@ -168,7 +199,10 @@ QXcbWindow::QXcbWindow(QWindow *window)
setConnection(m_screen->connection());
- create();
+ if (window->type() != Qt::ForeignWindow)
+ create();
+ else
+ m_window = window->winId();
}
void QXcbWindow::create()
@@ -235,8 +269,10 @@ void QXcbWindow::create()
}
xcb_window_t xcb_parent_id = m_screen->root();
- if (parent())
+ if (parent()) {
xcb_parent_id = static_cast<QXcbWindow *>(parent())->xcb_window();
+ m_embedded = parent()->window()->type() == Qt::ForeignWindow;
+ }
m_format = window()->requestedFormat();
@@ -368,6 +404,14 @@ void QXcbWindow::create()
atom(QXcbAtom::WM_CLIENT_LEADER), XCB_ATOM_WINDOW, 32,
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
if (connection()->isUsingXInput2Maemo()) {
XIEventMask xieventmask;
@@ -410,7 +454,8 @@ void QXcbWindow::create()
QXcbWindow::~QXcbWindow()
{
- destroy();
+ if (window()->type() != Qt::ForeignWindow)
+ destroy();
}
void QXcbWindow::destroy()
@@ -574,9 +619,10 @@ void QXcbWindow::show()
propagateSizeHints();
// update WM_TRANSIENT_FOR
- if (isTransient(window())) {
+ const QWindow *tp = window()->transientParent();
+ if (isTransient(window()) || tp != 0) {
xcb_window_t transientXcbParent = 0;
- if (const QWindow *tp = window()->transientParent())
+ if (tp)
transientXcbParent = static_cast<const QXcbWindow *>(tp->handle())->winId();
// Default to client leader if there is no transient parent, else modal dialogs can
// be hidden by their parents.
@@ -1140,7 +1186,15 @@ void QXcbWindow::setParent(const QPlatformWindow *parent)
{
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()));
}
@@ -1271,6 +1325,13 @@ void QXcbWindow::propagateSizeHints()
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) {
m_deferredActivation = true;
return;
@@ -1434,7 +1495,8 @@ void QXcbWindow::handleClientMessageEvent(const xcb_client_message_event_t *even
} else if (event->type == atom(QXcbAtom::XdndDrop)) {
connection()->drag()->handleDrop(window(), event);
#endif
- } else if (event->type == atom(QXcbAtom::_XEMBED)) { // QSystemTrayIcon
+ } else if (event->type == atom(QXcbAtom::_XEMBED)) {
+ handleXEmbedMessage(event);
} else {
qWarning() << "QXcbWindow: Unhandled client message:" << connection()->atomName(event->type);
}
@@ -1476,6 +1538,53 @@ bool QXcbWindow::isExposed() const
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)
{
if (event->window == m_window) {
@@ -1506,6 +1615,15 @@ void QXcbWindow::handleButtonPressEvent(const xcb_button_press_event_t *event)
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 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);
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);
}
-static bool focusInPeeker(xcb_generic_event_t *event)
+static bool focusInPeeker(QXcbConnection *connection, xcb_generic_event_t *event)
{
if (!event) {
// 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;
}
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 *)
@@ -1744,6 +1892,35 @@ void QXcbWindow::setCursor(xcb_cursor_t cursor)
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)
{
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;
}
+// 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)
static inline xcb_rectangle_t qRectToXCBRectangle(const QRect &r)
diff --git a/src/plugins/platforms/xcb/qxcbwindow.h b/src/plugins/platforms/xcb/qxcbwindow.h
index 3b5404684f..1810a58c7b 100644
--- a/src/plugins/platforms/xcb/qxcbwindow.h
+++ b/src/plugins/platforms/xcb/qxcbwindow.h
@@ -87,6 +87,9 @@ public:
void setParent(const QPlatformWindow *window);
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 setWindowIcon(const QIcon &icon);
@@ -107,6 +110,8 @@ public:
QSurfaceFormat format() const;
+ void windowEvent(QEvent *event);
+
bool startSystemResize(const QPoint &pos, Qt::Corner corner);
void setOpacity(qreal level);
@@ -158,6 +163,9 @@ private:
void updateDoesNotAcceptFocus(bool doesNotAcceptFocus);
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 destroy();
@@ -184,6 +192,7 @@ private:
bool m_deferredActivation;
bool m_deferredExpose;
bool m_configureNotifyPending;
+ bool m_embedded;
xcb_window_t m_netWmUserTimeWindow;
QSurfaceFormat m_format;