From 29c2ae57e011bb38f967182fa52516fc245494bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Gr=C3=A4=C3=9Flin?= Date: Mon, 18 May 2015 11:23:45 +0200 Subject: [PATCH] [wayland] Use a dummy window for QtWayland's popup window handling QtWayland only creates popup windows if they have a parent QWindow or if there is any window which had input. It's not enough to fake an enter, it needs to be either a pointer button press or key press. As KWin's useraction menu doesn't have a parent and we most likely never send a pointer press to any QWindow it doesn't get shown. To circumvent this we create a dummy window and fake a button press/release on the window. After that Qt is tricked into believing there's a parent window and shows the popup. Faking the input is only done with at least Qt 5.5 as QtWayland crashes on pointer event without a keymap being installed. As KWin does not yet send keymaps we better disable the dangerous code path. With Qt 5.5 the crash condition is fixed. --- main_wayland.cpp | 1 + wayland_server.cpp | 44 ++++++++++++++++++++++++++++++++++++++++++++ wayland_server.h | 7 +++++++ 3 files changed, 52 insertions(+) diff --git a/main_wayland.cpp b/main_wayland.cpp index fad16d8ae1..ce0a7a7832 100644 --- a/main_wayland.cpp +++ b/main_wayland.cpp @@ -221,6 +221,7 @@ void ApplicationWayland::continueStartupWithX() Xcb::sync(); // Trigger possible errors, there's still a chance to abort notifyKSplash(); + waylandServer()->createDummyQtWindow(); } ); eglInitWatcher->setFuture(QtConcurrent::run([] { diff --git a/wayland_server.cpp b/wayland_server.cpp index 237200f7ee..e33f278278 100644 --- a/wayland_server.cpp +++ b/wayland_server.cpp @@ -27,6 +27,7 @@ along with this program. If not, see . // Client #include #include +#include // Server #include #include @@ -35,6 +36,9 @@ along with this program. If not, see . #include #include +// Qt +#include + // system #include #include @@ -94,6 +98,13 @@ void WaylandServer::init(const QByteArray &socketName) // skip Xwayland clients, those are created using standard X11 way return; } + if (surface->client() == m_qtConnection) { + // one of Qt's windows + if (m_dummyWindowSurface && (m_dummyWindowSurface->id() == surface->surface()->id())) { + fakeDummyQtWindowInput(); + return; + } + } auto client = new ShellClient(surface); if (auto c = Compositor::self()) { connect(client, &Toplevel::needsRepaint, c, &Compositor::scheduleRepaint); @@ -194,4 +205,37 @@ void WaylandServer::removeClient(ShellClient *c) emit shellClientRemoved(c); } +void WaylandServer::createDummyQtWindow() +{ + if (m_dummyWindow) { + return; + } + m_dummyWindow.reset(new QWindow()); + m_dummyWindow->setSurfaceType(QSurface::RasterSurface); + m_dummyWindow->show(); + m_dummyWindowSurface = KWayland::Client::Surface::fromWindow(m_dummyWindow.data()); +} + +void WaylandServer::fakeDummyQtWindowInput() +{ +#if (QT_VERSION >= QT_VERSION_CHECK(5, 5, 0)) + // we need to fake Qt into believing it has got any seat events + // this is done only when receiving either a key press or button. + // we simulate by sending a button press and release + auto surface = KWayland::Server::SurfaceInterface::get(m_dummyWindowSurface->id(), m_qtConnection); + if (!surface) { + return; + } + const auto oldSeatSurface = m_seat->focusedPointerSurface(); + const auto oldPos = m_seat->focusedPointerSurfacePosition(); + m_seat->setFocusedPointerSurface(surface, QPoint(0, 0)); + m_seat->setPointerPos(QPointF(0, 0)); + m_seat->pointerButtonPressed(Qt::LeftButton); + m_seat->pointerButtonReleased(Qt::LeftButton); + m_qtConnection->flush(); + m_dummyWindow->hide(); + m_seat->setFocusedPointerSurface(oldSeatSurface, oldPos); +#endif +} + } diff --git a/wayland_server.h b/wayland_server.h index e408496959..f18b7c893e 100644 --- a/wayland_server.h +++ b/wayland_server.h @@ -24,12 +24,15 @@ along with this program. If not, see . #include +class QWindow; + namespace KWayland { namespace Client { class ConnectionThread; class ShmPool; +class Surface; } namespace Server { @@ -89,6 +92,7 @@ public: **/ int createQtConnection(); void createInternalConnection(); + void createDummyQtWindow(); KWayland::Server::ClientConnection *xWaylandConnection() const { return m_xwaylandConnection; @@ -108,6 +112,7 @@ Q_SIGNALS: void shellClientRemoved(ShellClient*); private: + void fakeDummyQtWindowInput(); KWayland::Server::Display *m_display = nullptr; KWayland::Server::CompositorInterface *m_compositor = nullptr; KWayland::Server::SeatInterface *m_seat = nullptr; @@ -122,6 +127,8 @@ private: } m_internalConnection; AbstractBackend *m_backend = nullptr; QList m_clients; + QScopedPointer m_dummyWindow; + KWayland::Client::Surface *m_dummyWindowSurface = nullptr; KWIN_SINGLETON(WaylandServer) };