[wayland] Don't use hardcoded move-resize cursor
Summary: Currently, when resizing a window the cursor doesn't match the resize direction. The reason for that is the move-resize cursor is hardcoded. To fix that, CursorImage::updateMoveResize has to use AbstractClient::cursor. Also, because the move-resize cursor is updated after calling startMoveResize, we have to connect to AbstractClient::moveResizeCursorChanged. BUG: 370339 FIXED-IN: 5.15 Reviewers: #kwin, davidedmundson, broulik, romangg, graesslin Reviewed By: #kwin, graesslin Subscribers: davidedmundson, romangg, graesslin, kwin Tags: #kwin Maniphest Tasks: T5714 Differential Revision: https://phabricator.kde.org/D3202
This commit is contained in:
parent
792d840455
commit
1ca2aec77f
3 changed files with 191 additions and 1 deletions
|
@ -27,11 +27,13 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
#include "options.h"
|
||||
#include "screenedge.h"
|
||||
#include "screens.h"
|
||||
#include "wayland_cursor_theme.h"
|
||||
#include "wayland_server.h"
|
||||
#include "workspace.h"
|
||||
#include "shell_client.h"
|
||||
#include <kwineffects.h>
|
||||
|
||||
#include <KWayland/Client/buffer.h>
|
||||
#include <KWayland/Client/connection_thread.h>
|
||||
#include <KWayland/Client/compositor.h>
|
||||
#include <KWayland/Client/pointer.h>
|
||||
|
@ -41,13 +43,59 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
#include <KWayland/Client/shm_pool.h>
|
||||
#include <KWayland/Client/surface.h>
|
||||
|
||||
#include <KWayland/Server/buffer_interface.h>
|
||||
#include <KWayland/Server/clientconnection.h>
|
||||
#include <KWayland/Server/seat_interface.h>
|
||||
|
||||
#include <wayland-cursor.h>
|
||||
|
||||
#include <linux/input.h>
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
|
||||
template <typename T>
|
||||
PlatformCursorImage loadReferenceThemeCursor(const T &shape)
|
||||
{
|
||||
if (!waylandServer()->internalShmPool()) {
|
||||
return PlatformCursorImage();
|
||||
}
|
||||
|
||||
QScopedPointer<WaylandCursorTheme> cursorTheme;
|
||||
cursorTheme.reset(new WaylandCursorTheme(waylandServer()->internalShmPool()));
|
||||
|
||||
wl_cursor_image *cursor = cursorTheme->get(shape);
|
||||
if (!cursor) {
|
||||
return PlatformCursorImage();
|
||||
}
|
||||
|
||||
wl_buffer *b = wl_cursor_image_get_buffer(cursor);
|
||||
if (!b) {
|
||||
return PlatformCursorImage();
|
||||
}
|
||||
|
||||
waylandServer()->internalClientConection()->flush();
|
||||
waylandServer()->dispatch();
|
||||
|
||||
auto buffer = KWayland::Server::BufferInterface::get(
|
||||
waylandServer()->internalConnection()->getResource(
|
||||
KWayland::Client::Buffer::getId(b)));
|
||||
if (!buffer) {
|
||||
return PlatformCursorImage{};
|
||||
}
|
||||
|
||||
const qreal scale = screens()->maxScale();
|
||||
QImage image = buffer->data().copy();
|
||||
image.setDevicePixelRatio(scale);
|
||||
|
||||
const QPoint hotSpot(
|
||||
qRound(cursor->hotspot_x / scale),
|
||||
qRound(cursor->hotspot_y / scale)
|
||||
);
|
||||
|
||||
return PlatformCursorImage(image, hotSpot);
|
||||
}
|
||||
|
||||
static const QString s_socketName = QStringLiteral("wayland_test_kwin_pointer_input-0");
|
||||
|
||||
class PointerInputTest : public QObject
|
||||
|
@ -80,6 +128,9 @@ private Q_SLOTS:
|
|||
void testWindowUnderCursorWhileButtonPressed();
|
||||
void testConfineToScreenGeometry_data();
|
||||
void testConfineToScreenGeometry();
|
||||
void testResizeCursor_data();
|
||||
void testResizeCursor();
|
||||
void testMoveCursor();
|
||||
|
||||
private:
|
||||
void render(KWayland::Client::Surface *surface, const QSize &size = QSize(100, 50));
|
||||
|
@ -1412,6 +1463,141 @@ void PointerInputTest::testConfineToScreenGeometry()
|
|||
QCOMPARE(Cursor::pos(), expectedPos);
|
||||
}
|
||||
|
||||
void PointerInputTest::testResizeCursor_data()
|
||||
{
|
||||
QTest::addColumn<Qt::Edges>("edges");
|
||||
QTest::addColumn<KWin::CursorShape>("cursorShape");
|
||||
|
||||
QTest::newRow("top-left") << Qt::Edges(Qt::TopEdge | Qt::LeftEdge) << CursorShape(ExtendedCursor::SizeNorthWest);
|
||||
QTest::newRow("top") << Qt::Edges(Qt::TopEdge) << CursorShape(ExtendedCursor::SizeNorth);
|
||||
QTest::newRow("top-right") << Qt::Edges(Qt::TopEdge | Qt::RightEdge) << CursorShape(ExtendedCursor::SizeNorthEast);
|
||||
QTest::newRow("right") << Qt::Edges(Qt::RightEdge) << CursorShape(ExtendedCursor::SizeEast);
|
||||
QTest::newRow("bottom-right") << Qt::Edges(Qt::BottomEdge | Qt::RightEdge) << CursorShape(ExtendedCursor::SizeSouthEast);
|
||||
QTest::newRow("bottom") << Qt::Edges(Qt::BottomEdge) << CursorShape(ExtendedCursor::SizeSouth);
|
||||
QTest::newRow("bottom-left") << Qt::Edges(Qt::BottomEdge | Qt::LeftEdge) << CursorShape(ExtendedCursor::SizeSouthWest);
|
||||
QTest::newRow("left") << Qt::Edges(Qt::LeftEdge) << CursorShape(ExtendedCursor::SizeWest);
|
||||
}
|
||||
|
||||
void PointerInputTest::testResizeCursor()
|
||||
{
|
||||
// this test verifies that the cursor has correct shape during resize operation
|
||||
|
||||
// first modify the config for this run
|
||||
KConfigGroup group = kwinApp()->config()->group("MouseBindings");
|
||||
group.writeEntry("CommandAllKey", "Alt");
|
||||
group.writeEntry("CommandAll3", "Resize");
|
||||
group.sync();
|
||||
workspace()->slotReconfigure();
|
||||
QCOMPARE(options->commandAllModifier(), Qt::AltModifier);
|
||||
QCOMPARE(options->commandAll3(), Options::MouseUnrestrictedResize);
|
||||
|
||||
// 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());
|
||||
ShellClient *c = Test::renderAndWaitForShown(surface.data(), QSize(100, 50), Qt::blue);
|
||||
QVERIFY(c);
|
||||
|
||||
// move the cursor to the test position
|
||||
QPoint cursorPos;
|
||||
QFETCH(Qt::Edges, edges);
|
||||
|
||||
if (edges & Qt::LeftEdge) {
|
||||
cursorPos.setX(c->geometry().left());
|
||||
} else if (edges & Qt::RightEdge) {
|
||||
cursorPos.setX(c->geometry().right());
|
||||
} else {
|
||||
cursorPos.setX(c->geometry().center().x());
|
||||
}
|
||||
|
||||
if (edges & Qt::TopEdge) {
|
||||
cursorPos.setY(c->geometry().top());
|
||||
} else if (edges & Qt::BottomEdge) {
|
||||
cursorPos.setY(c->geometry().bottom());
|
||||
} else {
|
||||
cursorPos.setY(c->geometry().center().y());
|
||||
}
|
||||
|
||||
Cursor::setPos(cursorPos);
|
||||
|
||||
const PlatformCursorImage arrowCursor = loadReferenceThemeCursor(Qt::ArrowCursor);
|
||||
QVERIFY(!arrowCursor.image().isNull());
|
||||
QCOMPARE(kwinApp()->platform()->cursorImage().image(), arrowCursor.image());
|
||||
QCOMPARE(kwinApp()->platform()->cursorImage().hotSpot(), arrowCursor.hotSpot());
|
||||
|
||||
// start resizing the client
|
||||
int timestamp = 1;
|
||||
kwinApp()->platform()->keyboardKeyPressed(KEY_LEFTALT, timestamp++);
|
||||
kwinApp()->platform()->pointerButtonPressed(BTN_RIGHT, timestamp++);
|
||||
QVERIFY(c->isResize());
|
||||
|
||||
QFETCH(KWin::CursorShape, cursorShape);
|
||||
const PlatformCursorImage resizeCursor = loadReferenceThemeCursor(cursorShape);
|
||||
QVERIFY(!resizeCursor.image().isNull());
|
||||
QCOMPARE(kwinApp()->platform()->cursorImage().image(), resizeCursor.image());
|
||||
QCOMPARE(kwinApp()->platform()->cursorImage().hotSpot(), resizeCursor.hotSpot());
|
||||
|
||||
// finish resizing the client
|
||||
kwinApp()->platform()->keyboardKeyReleased(KEY_LEFTALT, timestamp++);
|
||||
kwinApp()->platform()->pointerButtonReleased(BTN_RIGHT, timestamp++);
|
||||
QVERIFY(!c->isResize());
|
||||
|
||||
QCOMPARE(kwinApp()->platform()->cursorImage().image(), arrowCursor.image());
|
||||
QCOMPARE(kwinApp()->platform()->cursorImage().hotSpot(), arrowCursor.hotSpot());
|
||||
}
|
||||
|
||||
void PointerInputTest::testMoveCursor()
|
||||
{
|
||||
// this test verifies that the cursor has correct shape during move operation
|
||||
|
||||
// first modify the config for this run
|
||||
KConfigGroup group = kwinApp()->config()->group("MouseBindings");
|
||||
group.writeEntry("CommandAllKey", "Alt");
|
||||
group.writeEntry("CommandAll1", "Move");
|
||||
group.sync();
|
||||
workspace()->slotReconfigure();
|
||||
QCOMPARE(options->commandAllModifier(), Qt::AltModifier);
|
||||
QCOMPARE(options->commandAll1(), Options::MouseUnrestrictedMove);
|
||||
|
||||
// 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());
|
||||
ShellClient *c = Test::renderAndWaitForShown(surface.data(), QSize(100, 50), Qt::blue);
|
||||
QVERIFY(c);
|
||||
|
||||
// move cursor to the test position
|
||||
Cursor::setPos(c->geometry().center());
|
||||
|
||||
const PlatformCursorImage arrowCursor = loadReferenceThemeCursor(Qt::ArrowCursor);
|
||||
QVERIFY(!arrowCursor.image().isNull());
|
||||
QCOMPARE(kwinApp()->platform()->cursorImage().image(), arrowCursor.image());
|
||||
QCOMPARE(kwinApp()->platform()->cursorImage().hotSpot(), arrowCursor.hotSpot());
|
||||
|
||||
// start moving the client
|
||||
int timestamp = 1;
|
||||
kwinApp()->platform()->keyboardKeyPressed(KEY_LEFTALT, timestamp++);
|
||||
kwinApp()->platform()->pointerButtonPressed(BTN_LEFT, timestamp++);
|
||||
QVERIFY(c->isMove());
|
||||
|
||||
const PlatformCursorImage sizeAllCursor = loadReferenceThemeCursor(Qt::SizeAllCursor);
|
||||
QVERIFY(!sizeAllCursor.image().isNull());
|
||||
QCOMPARE(kwinApp()->platform()->cursorImage().image(), sizeAllCursor.image());
|
||||
QCOMPARE(kwinApp()->platform()->cursorImage().hotSpot(), sizeAllCursor.hotSpot());
|
||||
|
||||
// finish moving the client
|
||||
kwinApp()->platform()->keyboardKeyReleased(KEY_LEFTALT, timestamp++);
|
||||
kwinApp()->platform()->pointerButtonReleased(BTN_LEFT, timestamp++);
|
||||
QVERIFY(!c->isMove());
|
||||
|
||||
QCOMPARE(kwinApp()->platform()->cursorImage().image(), arrowCursor.image());
|
||||
QCOMPARE(kwinApp()->platform()->cursorImage().hotSpot(), arrowCursor.hotSpot());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
WAYLANDTEST_MAIN(KWin::PointerInputTest)
|
||||
|
|
3
cursor.h
3
cursor.h
|
@ -55,6 +55,7 @@ enum Shape {
|
|||
*/
|
||||
class KWIN_EXPORT CursorShape {
|
||||
public:
|
||||
CursorShape() = default;
|
||||
CursorShape(Qt::CursorShape qtShape) {
|
||||
m_shape = qtShape;
|
||||
}
|
||||
|
@ -300,4 +301,6 @@ inline bool Cursor::isCursorTracking() const
|
|||
|
||||
}
|
||||
|
||||
Q_DECLARE_METATYPE(KWin::CursorShape)
|
||||
|
||||
#endif // KWIN_CURSOR_H
|
||||
|
|
|
@ -994,6 +994,7 @@ CursorImage::CursorImage(PointerInputRedirection *parent)
|
|||
// connect the move resize of all window
|
||||
auto setupMoveResizeConnection = [this] (AbstractClient *c) {
|
||||
connect(c, &AbstractClient::moveResizedChanged, this, &CursorImage::updateMoveResize);
|
||||
connect(c, &AbstractClient::moveResizeCursorChanged, this, &CursorImage::updateMoveResize);
|
||||
};
|
||||
const auto clients = workspace()->allClientList();
|
||||
std::for_each(clients.begin(), clients.end(), setupMoveResizeConnection);
|
||||
|
@ -1108,7 +1109,7 @@ void CursorImage::updateMoveResize()
|
|||
m_moveResizeCursor.image = QImage();
|
||||
m_moveResizeCursor.hotSpot = QPoint();
|
||||
if (AbstractClient *c = workspace()->getMovingClient()) {
|
||||
loadThemeCursor(c->isMove() ? Qt::SizeAllCursor : Qt::SizeBDiagCursor, &m_moveResizeCursor);
|
||||
loadThemeCursor(c->cursor(), &m_moveResizeCursor);
|
||||
if (m_currentSource == CursorSource::MoveResize) {
|
||||
emit changed();
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue