[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.
This commit is contained in:
parent
3f94a2afc7
commit
29c2ae57e0
3 changed files with 52 additions and 0 deletions
|
@ -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([] {
|
||||
|
|
|
@ -27,6 +27,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
// Client
|
||||
#include <KWayland/Client/connection_thread.h>
|
||||
#include <KWayland/Client/registry.h>
|
||||
#include <KWayland/Client/surface.h>
|
||||
// Server
|
||||
#include <KWayland/Server/compositor_interface.h>
|
||||
#include <KWayland/Server/datadevicemanager_interface.h>
|
||||
|
@ -35,6 +36,9 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
#include <KWayland/Server/seat_interface.h>
|
||||
#include <KWayland/Server/shell_interface.h>
|
||||
|
||||
// Qt
|
||||
#include <QWindow>
|
||||
|
||||
// system
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -24,12 +24,15 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
|
||||
#include <QObject>
|
||||
|
||||
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<ShellClient*> m_clients;
|
||||
QScopedPointer<QWindow> m_dummyWindow;
|
||||
KWayland::Client::Surface *m_dummyWindowSurface = nullptr;
|
||||
KWIN_SINGLETON(WaylandServer)
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in a new issue