diff options
Diffstat (limited to 'src/plugins/platforms')
-rw-r--r-- | src/plugins/platforms/xcb/qxcbconnection.cpp | 4 | ||||
-rw-r--r-- | src/plugins/platforms/xcb/qxcbconnection.h | 2 | ||||
-rw-r--r-- | src/plugins/platforms/xcb/qxcbintegration.cpp | 1 | ||||
-rw-r--r-- | src/plugins/platforms/xcb/qxcbwindow.cpp | 260 | ||||
-rw-r--r-- | src/plugins/platforms/xcb/qxcbwindow.h | 9 |
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; |