[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
|
Xcb::sync(); // Trigger possible errors, there's still a chance to abort
|
||||||
|
|
||||||
notifyKSplash();
|
notifyKSplash();
|
||||||
|
waylandServer()->createDummyQtWindow();
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
eglInitWatcher->setFuture(QtConcurrent::run([] {
|
eglInitWatcher->setFuture(QtConcurrent::run([] {
|
||||||
|
|
|
@ -27,6 +27,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
// Client
|
// Client
|
||||||
#include <KWayland/Client/connection_thread.h>
|
#include <KWayland/Client/connection_thread.h>
|
||||||
#include <KWayland/Client/registry.h>
|
#include <KWayland/Client/registry.h>
|
||||||
|
#include <KWayland/Client/surface.h>
|
||||||
// Server
|
// Server
|
||||||
#include <KWayland/Server/compositor_interface.h>
|
#include <KWayland/Server/compositor_interface.h>
|
||||||
#include <KWayland/Server/datadevicemanager_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/seat_interface.h>
|
||||||
#include <KWayland/Server/shell_interface.h>
|
#include <KWayland/Server/shell_interface.h>
|
||||||
|
|
||||||
|
// Qt
|
||||||
|
#include <QWindow>
|
||||||
|
|
||||||
// system
|
// system
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <sys/socket.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
|
// skip Xwayland clients, those are created using standard X11 way
|
||||||
return;
|
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);
|
auto client = new ShellClient(surface);
|
||||||
if (auto c = Compositor::self()) {
|
if (auto c = Compositor::self()) {
|
||||||
connect(client, &Toplevel::needsRepaint, c, &Compositor::scheduleRepaint);
|
connect(client, &Toplevel::needsRepaint, c, &Compositor::scheduleRepaint);
|
||||||
|
@ -194,4 +205,37 @@ void WaylandServer::removeClient(ShellClient *c)
|
||||||
emit shellClientRemoved(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>
|
#include <QObject>
|
||||||
|
|
||||||
|
class QWindow;
|
||||||
|
|
||||||
namespace KWayland
|
namespace KWayland
|
||||||
{
|
{
|
||||||
namespace Client
|
namespace Client
|
||||||
{
|
{
|
||||||
class ConnectionThread;
|
class ConnectionThread;
|
||||||
class ShmPool;
|
class ShmPool;
|
||||||
|
class Surface;
|
||||||
}
|
}
|
||||||
namespace Server
|
namespace Server
|
||||||
{
|
{
|
||||||
|
@ -89,6 +92,7 @@ public:
|
||||||
**/
|
**/
|
||||||
int createQtConnection();
|
int createQtConnection();
|
||||||
void createInternalConnection();
|
void createInternalConnection();
|
||||||
|
void createDummyQtWindow();
|
||||||
|
|
||||||
KWayland::Server::ClientConnection *xWaylandConnection() const {
|
KWayland::Server::ClientConnection *xWaylandConnection() const {
|
||||||
return m_xwaylandConnection;
|
return m_xwaylandConnection;
|
||||||
|
@ -108,6 +112,7 @@ Q_SIGNALS:
|
||||||
void shellClientRemoved(ShellClient*);
|
void shellClientRemoved(ShellClient*);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void fakeDummyQtWindowInput();
|
||||||
KWayland::Server::Display *m_display = nullptr;
|
KWayland::Server::Display *m_display = nullptr;
|
||||||
KWayland::Server::CompositorInterface *m_compositor = nullptr;
|
KWayland::Server::CompositorInterface *m_compositor = nullptr;
|
||||||
KWayland::Server::SeatInterface *m_seat = nullptr;
|
KWayland::Server::SeatInterface *m_seat = nullptr;
|
||||||
|
@ -122,6 +127,8 @@ private:
|
||||||
} m_internalConnection;
|
} m_internalConnection;
|
||||||
AbstractBackend *m_backend = nullptr;
|
AbstractBackend *m_backend = nullptr;
|
||||||
QList<ShellClient*> m_clients;
|
QList<ShellClient*> m_clients;
|
||||||
|
QScopedPointer<QWindow> m_dummyWindow;
|
||||||
|
KWayland::Client::Surface *m_dummyWindowSurface = nullptr;
|
||||||
KWIN_SINGLETON(WaylandServer)
|
KWIN_SINGLETON(WaylandServer)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue