Adapt to input region changes in kwayland-server
SurfaceInterface::inputIsInfinite() has been dropped. If the surface has no any input region specified, SurfaceInterface::input() will return a region that corresponds to the rect of the surface (0, 0, width, height). While the new design is more robust, for example it's no longer possible to forget to check SurfaceInterface::inputIsInfinite(), it has shown some issues in the input stack of kwin. Currently, acceptsInput() will return false if you attempt to click the server-side decoration for a surface whose input region is not empty. Therefore, it's possible for an application to set an input region with a width and a height of 1. If user doesn't know about KSysGuard or the possibility of closing apps via the task manager, they won't be able to close such an application. Another issue is that if an application has specified an empty input region on purpose, user will be still able click it. With the new behavior of SurfaceInterface::input(), this is no longer an issue and it is handled properly by kwin.
This commit is contained in:
parent
fea950f23a
commit
41d431de27
8 changed files with 76 additions and 18 deletions
|
@ -2575,6 +2575,16 @@ QRect AbstractClient::inputGeometry() const
|
||||||
return Toplevel::inputGeometry();
|
return Toplevel::inputGeometry();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool AbstractClient::hitTest(const QPoint &point) const
|
||||||
|
{
|
||||||
|
if (isDecorated()) {
|
||||||
|
if (m_decoration.inputRegion.contains(mapToFrame(point))) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Toplevel::hitTest(point);
|
||||||
|
}
|
||||||
|
|
||||||
QRect AbstractClient::virtualKeyboardGeometry() const
|
QRect AbstractClient::virtualKeyboardGeometry() const
|
||||||
{
|
{
|
||||||
return m_virtualKeyboardGeometry;
|
return m_virtualKeyboardGeometry;
|
||||||
|
|
|
@ -754,6 +754,7 @@ public:
|
||||||
virtual void showContextHelp();
|
virtual void showContextHelp();
|
||||||
|
|
||||||
QRect inputGeometry() const override;
|
QRect inputGeometry() const override;
|
||||||
|
bool hitTest(const QPoint &point) const override;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @returns the geometry of the virtual keyboard
|
* @returns the geometry of the virtual keyboard
|
||||||
|
|
|
@ -25,6 +25,7 @@
|
||||||
#include <KWayland/Client/connection_thread.h>
|
#include <KWayland/Client/connection_thread.h>
|
||||||
#include <KWayland/Client/compositor.h>
|
#include <KWayland/Client/compositor.h>
|
||||||
#include <KWayland/Client/pointer.h>
|
#include <KWayland/Client/pointer.h>
|
||||||
|
#include <KWayland/Client/region.h>
|
||||||
#include <KWayland/Client/seat.h>
|
#include <KWayland/Client/seat.h>
|
||||||
#include <KWayland/Client/server_decoration.h>
|
#include <KWayland/Client/server_decoration.h>
|
||||||
#include <KWayland/Client/shm_pool.h>
|
#include <KWayland/Client/shm_pool.h>
|
||||||
|
@ -123,6 +124,8 @@ private Q_SLOTS:
|
||||||
void testResizeCursor();
|
void testResizeCursor();
|
||||||
void testMoveCursor();
|
void testMoveCursor();
|
||||||
void testHideShowCursor();
|
void testHideShowCursor();
|
||||||
|
void testDefaultInputRegion();
|
||||||
|
void testEmptyInputRegion();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void render(KWayland::Client::Surface *surface, const QSize &size = QSize(100, 50));
|
void render(KWayland::Client::Surface *surface, const QSize &size = QSize(100, 50));
|
||||||
|
@ -1619,6 +1622,52 @@ void PointerInputTest::testHideShowCursor()
|
||||||
QCOMPARE(kwinApp()->platform()->isCursorHidden(), false);
|
QCOMPARE(kwinApp()->platform()->isCursorHidden(), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PointerInputTest::testDefaultInputRegion()
|
||||||
|
{
|
||||||
|
// This test verifies that a surface that hasn't specified the input region can be focused.
|
||||||
|
|
||||||
|
// Create a test client.
|
||||||
|
using namespace KWayland::Client;
|
||||||
|
QScopedPointer<Surface> surface(Test::createSurface());
|
||||||
|
QVERIFY(!surface.isNull());
|
||||||
|
QScopedPointer<XdgShellSurface> shellSurface(Test::createXdgShellStableSurface(surface.data()));
|
||||||
|
QVERIFY(!shellSurface.isNull());
|
||||||
|
AbstractClient *client = Test::renderAndWaitForShown(surface.data(), QSize(100, 50), Qt::blue);
|
||||||
|
QVERIFY(client);
|
||||||
|
|
||||||
|
// Move the point to the center of the surface.
|
||||||
|
Cursors::self()->mouse()->setPos(client->frameGeometry().center());
|
||||||
|
QCOMPARE(waylandServer()->seat()->focusedPointerSurface(), client->surface());
|
||||||
|
|
||||||
|
// Destroy the test client.
|
||||||
|
shellSurface.reset();
|
||||||
|
QVERIFY(Test::waitForWindowDestroyed(client));
|
||||||
|
}
|
||||||
|
|
||||||
|
void PointerInputTest::testEmptyInputRegion()
|
||||||
|
{
|
||||||
|
// This test verifies that a surface that has specified an empty input region can't be focused.
|
||||||
|
|
||||||
|
// Create a test client.
|
||||||
|
using namespace KWayland::Client;
|
||||||
|
QScopedPointer<Surface> surface(Test::createSurface());
|
||||||
|
QVERIFY(!surface.isNull());
|
||||||
|
std::unique_ptr<KWayland::Client::Region> inputRegion(m_compositor->createRegion(QRegion()));
|
||||||
|
surface->setInputRegion(inputRegion.get());
|
||||||
|
QScopedPointer<XdgShellSurface> shellSurface(Test::createXdgShellStableSurface(surface.data()));
|
||||||
|
QVERIFY(!shellSurface.isNull());
|
||||||
|
AbstractClient *client = Test::renderAndWaitForShown(surface.data(), QSize(100, 50), Qt::blue);
|
||||||
|
QVERIFY(client);
|
||||||
|
|
||||||
|
// Move the point to the center of the surface.
|
||||||
|
Cursors::self()->mouse()->setPos(client->frameGeometry().center());
|
||||||
|
QVERIFY(!waylandServer()->seat()->focusedPointerSurface());
|
||||||
|
|
||||||
|
// Destroy the test client.
|
||||||
|
shellSurface.reset();
|
||||||
|
QVERIFY(Test::waitForWindowDestroyed(client));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
WAYLANDTEST_MAIN(KWin::PointerInputTest)
|
WAYLANDTEST_MAIN(KWin::PointerInputTest)
|
||||||
|
|
15
input.cpp
15
input.cpp
|
@ -2418,17 +2418,6 @@ Qt::MouseButtons InputRedirection::qtButtonStates() const
|
||||||
return m_pointer->buttons();
|
return m_pointer->buttons();
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool acceptsInput(Toplevel *t, const QPoint &pos)
|
|
||||||
{
|
|
||||||
const QRegion input = t->inputShape();
|
|
||||||
if (input.isEmpty()) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
// TODO: What about sub-surfaces sticking outside the main surface?
|
|
||||||
const QPoint localPoint = pos - t->bufferGeometry().topLeft();
|
|
||||||
return input.contains(localPoint);
|
|
||||||
}
|
|
||||||
|
|
||||||
Toplevel *InputRedirection::findToplevel(const QPoint &pos)
|
Toplevel *InputRedirection::findToplevel(const QPoint &pos)
|
||||||
{
|
{
|
||||||
if (!Workspace::self()) {
|
if (!Workspace::self()) {
|
||||||
|
@ -2443,7 +2432,7 @@ Toplevel *InputRedirection::findToplevel(const QPoint &pos)
|
||||||
}
|
}
|
||||||
const QList<Unmanaged *> &unmanaged = Workspace::self()->unmanagedList();
|
const QList<Unmanaged *> &unmanaged = Workspace::self()->unmanagedList();
|
||||||
foreach (Unmanaged *u, unmanaged) {
|
foreach (Unmanaged *u, unmanaged) {
|
||||||
if (u->inputGeometry().contains(pos) && acceptsInput(u, pos)) {
|
if (u->hitTest(pos)) {
|
||||||
return u;
|
return u;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2482,7 +2471,7 @@ Toplevel *InputRedirection::findManagedToplevel(const QPoint &pos)
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (t->inputGeometry().contains(pos) && acceptsInput(t, pos)) {
|
if (t->hitTest(pos)) {
|
||||||
return t;
|
return t;
|
||||||
}
|
}
|
||||||
} while (it != stacking.begin());
|
} while (it != stacking.begin());
|
||||||
|
|
|
@ -107,9 +107,6 @@ NET::WindowType InputPanelV1Client::windowType(bool, int) const
|
||||||
|
|
||||||
QRect InputPanelV1Client::inputGeometry() const
|
QRect InputPanelV1Client::inputGeometry() const
|
||||||
{
|
{
|
||||||
if (surface()->inputIsInfinite()) {
|
|
||||||
return frameGeometry();
|
|
||||||
}
|
|
||||||
return surface()->input().boundingRect().translated(pos());
|
return surface()->input().boundingRect().translated(pos());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -620,8 +620,7 @@ template <typename T>
|
||||||
static QRegion getConstraintRegion(Toplevel *t, T *constraint)
|
static QRegion getConstraintRegion(Toplevel *t, T *constraint)
|
||||||
{
|
{
|
||||||
const QRegion windowShape = t->inputShape();
|
const QRegion windowShape = t->inputShape();
|
||||||
const QRegion windowRegion = windowShape.isEmpty() ? QRegion(0, 0, t->clientSize().width(), t->clientSize().height()) : windowShape;
|
const QRegion intersected = constraint->region().isEmpty() ? windowShape : windowShape.intersected(constraint->region());
|
||||||
const QRegion intersected = constraint->region().isEmpty() ? windowRegion : windowRegion.intersected(constraint->region());
|
|
||||||
return intersected.translated(t->pos() + t->clientPos());
|
return intersected.translated(t->pos() + t->clientPos());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -789,6 +789,14 @@ QMatrix4x4 Toplevel::inputTransformation() const
|
||||||
return m;
|
return m;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Toplevel::hitTest(const QPoint &point) const
|
||||||
|
{
|
||||||
|
if (m_surface && m_surface->isMapped()) {
|
||||||
|
return m_surface->inputSurfaceAt(mapToLocal(point));
|
||||||
|
}
|
||||||
|
return inputGeometry().contains(point);
|
||||||
|
}
|
||||||
|
|
||||||
QPoint Toplevel::mapToFrame(const QPoint &point) const
|
QPoint Toplevel::mapToFrame(const QPoint &point) const
|
||||||
{
|
{
|
||||||
return point - frameGeometry().topLeft();
|
return point - frameGeometry().topLeft();
|
||||||
|
|
|
@ -540,6 +540,11 @@ public:
|
||||||
*/
|
*/
|
||||||
virtual QMatrix4x4 inputTransformation() const;
|
virtual QMatrix4x4 inputTransformation() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns @c true if the toplevel can accept input at the specified position @a point.
|
||||||
|
*/
|
||||||
|
virtual bool hitTest(const QPoint &point) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The window has a popup grab. This means that when it got mapped the
|
* The window has a popup grab. This means that when it got mapped the
|
||||||
* parent window had an implicit (pointer) grab.
|
* parent window had an implicit (pointer) grab.
|
||||||
|
|
Loading…
Reference in a new issue