2020-08-02 22:22:19 +00:00
|
|
|
/*
|
|
|
|
KWin - the KDE window manager
|
|
|
|
This file is part of the KDE project.
|
2016-02-12 12:30:00 +00:00
|
|
|
|
2020-08-02 22:22:19 +00:00
|
|
|
SPDX-FileCopyrightText: 2013, 2016 Martin Gräßlin <mgraesslin@kde.org>
|
|
|
|
SPDX-FileCopyrightText: 2018 Roman Gilg <subdiff@gmail.com>
|
|
|
|
SPDX-FileCopyrightText: 2019 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
|
2016-02-12 12:30:00 +00:00
|
|
|
|
2020-08-02 22:22:19 +00:00
|
|
|
SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
*/
|
2022-02-23 13:27:05 +00:00
|
|
|
#include <config-kwin.h>
|
2016-02-12 12:30:00 +00:00
|
|
|
#include "pointer_input.h"
|
2021-08-27 15:49:54 +00:00
|
|
|
#include "abstract_output.h"
|
2016-04-07 07:24:17 +00:00
|
|
|
#include "platform.h"
|
2019-09-24 08:48:08 +00:00
|
|
|
#include "x11client.h"
|
2016-02-23 11:29:05 +00:00
|
|
|
#include "effects.h"
|
2016-05-24 08:57:57 +00:00
|
|
|
#include "input_event.h"
|
2016-12-27 19:16:50 +00:00
|
|
|
#include "input_event_spy.h"
|
2016-12-21 18:55:48 +00:00
|
|
|
#include "osd.h"
|
2016-02-12 12:30:00 +00:00
|
|
|
#include "screens.h"
|
|
|
|
#include "wayland_server.h"
|
|
|
|
#include "workspace.h"
|
|
|
|
#include "decorations/decoratedclient.h"
|
|
|
|
// KDecoration
|
|
|
|
#include <KDecoration2/Decoration>
|
|
|
|
// KWayland
|
2021-07-20 19:37:03 +00:00
|
|
|
#include <KWaylandServer/shmclientbuffer.h>
|
2020-04-29 15:18:41 +00:00
|
|
|
#include <KWaylandServer/datadevice_interface.h>
|
|
|
|
#include <KWaylandServer/display.h>
|
2021-03-10 14:28:50 +00:00
|
|
|
#include <KWaylandServer/pointer_interface.h>
|
2020-11-03 19:54:49 +00:00
|
|
|
#include <KWaylandServer/pointerconstraints_v1_interface.h>
|
2020-04-29 15:18:41 +00:00
|
|
|
#include <KWaylandServer/seat_interface.h>
|
|
|
|
#include <KWaylandServer/surface_interface.h>
|
2016-02-12 12:30:00 +00:00
|
|
|
// screenlocker
|
2022-02-28 18:58:35 +00:00
|
|
|
#if KWIN_BUILD_SCREENLOCKER
|
2016-02-12 12:30:00 +00:00
|
|
|
#include <KScreenLocker/KsldApp>
|
2022-02-23 13:27:05 +00:00
|
|
|
#endif
|
2016-02-12 12:30:00 +00:00
|
|
|
|
2016-12-21 18:55:48 +00:00
|
|
|
#include <KLocalizedString>
|
|
|
|
|
2016-02-12 12:30:00 +00:00
|
|
|
#include <QHoverEvent>
|
|
|
|
#include <QWindow>
|
2020-02-06 12:24:07 +00:00
|
|
|
#include <QPainter>
|
2016-02-12 12:30:00 +00:00
|
|
|
|
|
|
|
#include <linux/input.h>
|
|
|
|
|
|
|
|
namespace KWin
|
|
|
|
{
|
|
|
|
|
2019-12-05 14:32:17 +00:00
|
|
|
static const QHash<uint32_t, Qt::MouseButton> s_buttonToQtMouseButton = {
|
|
|
|
{ BTN_LEFT , Qt::LeftButton },
|
|
|
|
{ BTN_MIDDLE , Qt::MiddleButton },
|
|
|
|
{ BTN_RIGHT , Qt::RightButton },
|
|
|
|
// in QtWayland mapped like that
|
|
|
|
{ BTN_SIDE , Qt::ExtraButton1 },
|
|
|
|
// in QtWayland mapped like that
|
|
|
|
{ BTN_EXTRA , Qt::ExtraButton2 },
|
|
|
|
{ BTN_BACK , Qt::BackButton },
|
|
|
|
{ BTN_FORWARD , Qt::ForwardButton },
|
|
|
|
{ BTN_TASK , Qt::TaskButton },
|
|
|
|
// mapped like that in QtWayland
|
|
|
|
{ 0x118 , Qt::ExtraButton6 },
|
|
|
|
{ 0x119 , Qt::ExtraButton7 },
|
|
|
|
{ 0x11a , Qt::ExtraButton8 },
|
|
|
|
{ 0x11b , Qt::ExtraButton9 },
|
|
|
|
{ 0x11c , Qt::ExtraButton10 },
|
|
|
|
{ 0x11d , Qt::ExtraButton11 },
|
|
|
|
{ 0x11e , Qt::ExtraButton12 },
|
|
|
|
{ 0x11f , Qt::ExtraButton13 },
|
|
|
|
};
|
|
|
|
|
2019-12-05 14:34:23 +00:00
|
|
|
uint32_t qtMouseButtonToButton(Qt::MouseButton button)
|
|
|
|
{
|
|
|
|
return s_buttonToQtMouseButton.key(button);
|
|
|
|
}
|
|
|
|
|
2016-02-12 12:30:00 +00:00
|
|
|
static Qt::MouseButton buttonToQtMouseButton(uint32_t button)
|
|
|
|
{
|
2016-02-17 10:16:57 +00:00
|
|
|
// all other values get mapped to ExtraButton24
|
|
|
|
// this is actually incorrect but doesn't matter in our usage
|
|
|
|
// KWin internally doesn't use these high extra buttons anyway
|
|
|
|
// it's only needed for recognizing whether buttons are pressed
|
|
|
|
// if multiple buttons are mapped to the value the evaluation whether
|
|
|
|
// buttons are pressed is correct and that's all we care about.
|
2019-12-05 14:32:17 +00:00
|
|
|
return s_buttonToQtMouseButton.value(button, Qt::ExtraButton24);
|
|
|
|
}
|
2016-02-12 12:30:00 +00:00
|
|
|
|
|
|
|
static bool screenContainsPos(const QPointF &pos)
|
|
|
|
{
|
2021-09-01 09:27:19 +00:00
|
|
|
const auto outputs = kwinApp()->platform()->enabledOutputs();
|
|
|
|
for (const AbstractOutput *output : outputs) {
|
|
|
|
if (output->geometry().contains(pos.toPoint())) {
|
2016-02-12 12:30:00 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2018-07-10 18:09:22 +00:00
|
|
|
static QPointF confineToBoundingBox(const QPointF &pos, const QRectF &boundingBox)
|
|
|
|
{
|
|
|
|
return QPointF(
|
|
|
|
qBound(boundingBox.left(), pos.x(), boundingBox.right() - 1.0),
|
|
|
|
qBound(boundingBox.top(), pos.y(), boundingBox.bottom() - 1.0)
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2016-02-12 12:30:00 +00:00
|
|
|
PointerInputRedirection::PointerInputRedirection(InputRedirection* parent)
|
2016-05-12 14:33:03 +00:00
|
|
|
: InputDeviceHandler(parent)
|
2016-02-23 11:29:05 +00:00
|
|
|
, m_cursor(nullptr)
|
2016-02-12 12:30:00 +00:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
PointerInputRedirection::~PointerInputRedirection() = default;
|
|
|
|
|
|
|
|
void PointerInputRedirection::init()
|
|
|
|
{
|
2018-09-15 00:00:24 +00:00
|
|
|
Q_ASSERT(!inited());
|
2021-10-02 08:26:51 +00:00
|
|
|
waylandServer()->seat()->setHasPointer(input()->hasPointer());
|
|
|
|
connect(input(), &InputRedirection::hasPointerChanged,
|
|
|
|
waylandServer()->seat(), &KWaylandServer::SeatInterface::setHasPointer);
|
|
|
|
|
2016-02-23 11:29:05 +00:00
|
|
|
m_cursor = new CursorImage(this);
|
2018-09-15 00:00:24 +00:00
|
|
|
setInited(true);
|
|
|
|
InputDeviceHandler::init();
|
|
|
|
|
2021-10-02 08:26:51 +00:00
|
|
|
if (!input()->hasPointer()) {
|
2021-12-25 17:12:12 +00:00
|
|
|
Cursors::self()->hideCursor();
|
2021-10-02 08:26:51 +00:00
|
|
|
}
|
2021-12-25 17:12:12 +00:00
|
|
|
connect(input(), &InputRedirection::hasPointerChanged, this, []() {
|
2021-10-02 08:26:51 +00:00
|
|
|
if (input()->hasPointer()) {
|
2021-12-25 17:12:12 +00:00
|
|
|
Cursors::self()->showCursor();
|
2021-10-02 08:26:51 +00:00
|
|
|
} else {
|
2021-12-25 17:12:12 +00:00
|
|
|
Cursors::self()->hideCursor();
|
2021-10-02 08:26:51 +00:00
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2022-01-13 15:11:04 +00:00
|
|
|
connect(Cursors::self()->mouse(), &Cursor::rendered, m_cursor, &CursorImage::markAsRendered);
|
2020-04-02 16:18:01 +00:00
|
|
|
connect(m_cursor, &CursorImage::changed, Cursors::self()->mouse(), [this] {
|
|
|
|
auto cursor = Cursors::self()->mouse();
|
|
|
|
cursor->updateCursor(m_cursor->image(), m_cursor->hotSpot());
|
2021-06-25 08:58:28 +00:00
|
|
|
updateCursorOutputs();
|
2020-04-02 16:18:01 +00:00
|
|
|
});
|
2021-06-08 07:02:14 +00:00
|
|
|
Q_EMIT m_cursor->changed();
|
2018-09-15 00:00:24 +00:00
|
|
|
|
2016-02-12 12:30:00 +00:00
|
|
|
connect(screens(), &Screens::changed, this, &PointerInputRedirection::updateAfterScreenChange);
|
2022-02-28 18:58:35 +00:00
|
|
|
#if KWIN_BUILD_SCREENLOCKER
|
2016-04-25 06:51:33 +00:00
|
|
|
if (waylandServer()->hasScreenLockerIntegration()) {
|
2016-10-27 07:10:08 +00:00
|
|
|
connect(ScreenLocker::KSldApp::self(), &ScreenLocker::KSldApp::lockStateChanged, this,
|
|
|
|
[this] {
|
2021-03-12 07:15:40 +00:00
|
|
|
if (waylandServer()->seat()->hasPointer()) {
|
|
|
|
waylandServer()->seat()->cancelPointerPinchGesture();
|
|
|
|
waylandServer()->seat()->cancelPointerSwipeGesture();
|
|
|
|
}
|
2016-10-27 07:10:08 +00:00
|
|
|
update();
|
|
|
|
}
|
|
|
|
);
|
2016-04-25 06:51:33 +00:00
|
|
|
}
|
2022-02-23 13:27:05 +00:00
|
|
|
#endif
|
2018-09-15 00:00:24 +00:00
|
|
|
connect(workspace(), &QObject::destroyed, this, [this] { setInited(false); });
|
|
|
|
connect(waylandServer(), &QObject::destroyed, this, [this] { setInited(false); });
|
2020-04-29 15:18:41 +00:00
|
|
|
connect(waylandServer()->seat(), &KWaylandServer::SeatInterface::dragEnded, this,
|
2016-03-01 07:42:07 +00:00
|
|
|
[this] {
|
|
|
|
// need to force a focused pointer change
|
2018-09-15 00:00:24 +00:00
|
|
|
setFocus(nullptr);
|
2016-03-01 07:42:07 +00:00
|
|
|
update();
|
|
|
|
}
|
|
|
|
);
|
2016-10-24 15:09:40 +00:00
|
|
|
// connect the move resize of all window
|
|
|
|
auto setupMoveResizeConnection = [this] (AbstractClient *c) {
|
|
|
|
connect(c, &AbstractClient::clientStartUserMovedResized, this, &PointerInputRedirection::updateOnStartMoveResize);
|
|
|
|
connect(c, &AbstractClient::clientFinishUserMovedResized, this, &PointerInputRedirection::update);
|
|
|
|
};
|
|
|
|
const auto clients = workspace()->allClientList();
|
|
|
|
std::for_each(clients.begin(), clients.end(), setupMoveResizeConnection);
|
|
|
|
connect(workspace(), &Workspace::clientAdded, this, setupMoveResizeConnection);
|
2016-02-12 12:30:00 +00:00
|
|
|
|
2021-10-06 15:04:31 +00:00
|
|
|
// warp the cursor to center of screen containing the workspace center
|
|
|
|
if (const AbstractOutput *output = kwinApp()->platform()->outputAt(workspace()->geometry().center())) {
|
|
|
|
warp(output->geometry().center());
|
|
|
|
}
|
2016-02-12 12:30:00 +00:00
|
|
|
updateAfterScreenChange();
|
|
|
|
}
|
|
|
|
|
2016-10-24 15:09:40 +00:00
|
|
|
void PointerInputRedirection::updateOnStartMoveResize()
|
|
|
|
{
|
2018-09-15 00:00:24 +00:00
|
|
|
breakPointerConstraints(focus() ? focus()->surface() : nullptr);
|
2016-11-25 06:17:43 +00:00
|
|
|
disconnectPointerConstraintsConnection();
|
2018-09-15 00:00:24 +00:00
|
|
|
setFocus(nullptr);
|
2016-10-24 15:09:40 +00:00
|
|
|
}
|
|
|
|
|
2016-11-15 13:23:51 +00:00
|
|
|
void PointerInputRedirection::updateToReset()
|
|
|
|
{
|
2018-09-15 00:37:24 +00:00
|
|
|
if (decoration()) {
|
2016-11-15 13:23:51 +00:00
|
|
|
QHoverEvent event(QEvent::HoverLeave, QPointF(), QPointF());
|
2018-09-15 00:37:24 +00:00
|
|
|
QCoreApplication::instance()->sendEvent(decoration()->decoration(), &event);
|
2018-09-15 00:00:24 +00:00
|
|
|
setDecoration(nullptr);
|
2016-11-15 13:23:51 +00:00
|
|
|
}
|
2018-09-15 00:00:24 +00:00
|
|
|
if (focus()) {
|
2020-09-07 08:11:07 +00:00
|
|
|
if (AbstractClient *c = qobject_cast<AbstractClient*>(focus())) {
|
2021-11-29 16:59:06 +00:00
|
|
|
c->pointerLeaveEvent();
|
2016-11-15 13:23:51 +00:00
|
|
|
}
|
2018-09-15 00:00:24 +00:00
|
|
|
disconnect(m_focusGeometryConnection);
|
|
|
|
m_focusGeometryConnection = QMetaObject::Connection();
|
|
|
|
breakPointerConstraints(focus()->surface());
|
2016-11-25 06:17:43 +00:00
|
|
|
disconnectPointerConstraintsConnection();
|
2018-09-15 00:00:24 +00:00
|
|
|
setFocus(nullptr);
|
2016-11-15 13:23:51 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-03-26 13:53:09 +00:00
|
|
|
class PositionUpdateBlocker
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
PositionUpdateBlocker(PointerInputRedirection *pointer)
|
|
|
|
: m_pointer(pointer)
|
|
|
|
{
|
|
|
|
s_counter++;
|
|
|
|
}
|
|
|
|
~PositionUpdateBlocker() {
|
|
|
|
s_counter--;
|
|
|
|
if (s_counter == 0) {
|
|
|
|
if (!s_scheduledPositions.isEmpty()) {
|
|
|
|
const auto pos = s_scheduledPositions.takeFirst();
|
2021-10-05 19:01:16 +00:00
|
|
|
m_pointer->processMotionInternal(pos.pos, pos.delta, pos.deltaNonAccelerated, pos.time, pos.timeUsec, nullptr);
|
2017-03-26 13:53:09 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool isPositionBlocked() {
|
|
|
|
return s_counter > 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void schedulePosition(const QPointF &pos, const QSizeF &delta, const QSizeF &deltaNonAccelerated, uint32_t time, quint64 timeUsec) {
|
|
|
|
s_scheduledPositions.append({pos, delta, deltaNonAccelerated, time, timeUsec});
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
static int s_counter;
|
|
|
|
struct ScheduledPosition {
|
|
|
|
QPointF pos;
|
|
|
|
QSizeF delta;
|
|
|
|
QSizeF deltaNonAccelerated;
|
|
|
|
quint32 time;
|
|
|
|
quint64 timeUsec;
|
|
|
|
};
|
|
|
|
static QVector<ScheduledPosition> s_scheduledPositions;
|
|
|
|
|
|
|
|
PointerInputRedirection *m_pointer;
|
|
|
|
};
|
|
|
|
|
|
|
|
int PositionUpdateBlocker::s_counter = 0;
|
|
|
|
QVector<PositionUpdateBlocker::ScheduledPosition> PositionUpdateBlocker::s_scheduledPositions;
|
|
|
|
|
2021-10-02 08:26:51 +00:00
|
|
|
void PointerInputRedirection::processMotionAbsolute(const QPointF &pos, uint32_t time, InputDevice *device)
|
2021-10-05 19:01:16 +00:00
|
|
|
{
|
|
|
|
processMotionInternal(pos, QSizeF(), QSizeF(), time, 0, device);
|
|
|
|
}
|
|
|
|
|
2021-10-02 08:26:51 +00:00
|
|
|
void PointerInputRedirection::processMotion(const QSizeF &delta, const QSizeF &deltaNonAccelerated, uint32_t time, quint64 timeUsec, InputDevice *device)
|
2021-10-05 19:01:16 +00:00
|
|
|
{
|
|
|
|
processMotionInternal(m_pos + QPointF(delta.width(), delta.height()), delta, deltaNonAccelerated, time, timeUsec, device);
|
|
|
|
}
|
|
|
|
|
2021-10-02 08:26:51 +00:00
|
|
|
void PointerInputRedirection::processMotionInternal(const QPointF &pos, const QSizeF &delta, const QSizeF &deltaNonAccelerated, uint32_t time, quint64 timeUsec, InputDevice *device)
|
2016-02-12 12:30:00 +00:00
|
|
|
{
|
2022-02-11 18:12:48 +00:00
|
|
|
input()->setLastInputHandler(this);
|
2018-09-15 00:00:24 +00:00
|
|
|
if (!inited()) {
|
2016-02-12 12:30:00 +00:00
|
|
|
return;
|
|
|
|
}
|
2017-03-26 13:53:09 +00:00
|
|
|
if (PositionUpdateBlocker::isPositionBlocked()) {
|
|
|
|
PositionUpdateBlocker::schedulePosition(pos, delta, deltaNonAccelerated, time, timeUsec);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
PositionUpdateBlocker blocker(this);
|
2016-02-12 12:30:00 +00:00
|
|
|
updatePosition(pos);
|
2016-05-24 08:57:57 +00:00
|
|
|
MouseEvent event(QEvent::MouseMove, m_pos, Qt::NoButton, m_qtButtons,
|
2018-09-15 00:37:24 +00:00
|
|
|
input()->keyboardModifiers(), time,
|
2016-10-07 12:47:25 +00:00
|
|
|
delta, deltaNonAccelerated, timeUsec, device);
|
2018-09-15 00:37:24 +00:00
|
|
|
event.setModifiersRelevantForGlobalShortcuts(input()->modifiersRelevantForGlobalShortcuts());
|
2016-02-12 12:30:00 +00:00
|
|
|
|
2018-09-15 00:00:24 +00:00
|
|
|
update();
|
2018-09-15 00:37:24 +00:00
|
|
|
input()->processSpies(std::bind(&InputEventSpy::pointerEvent, std::placeholders::_1, &event));
|
|
|
|
input()->processFilters(std::bind(&InputEventFilter::pointerEvent, std::placeholders::_1, &event, 0));
|
2016-02-12 12:30:00 +00:00
|
|
|
}
|
|
|
|
|
2021-10-02 08:26:51 +00:00
|
|
|
void PointerInputRedirection::processButton(uint32_t button, InputRedirection::PointerButtonState state, uint32_t time, InputDevice *device)
|
2016-02-12 12:30:00 +00:00
|
|
|
{
|
2022-02-11 18:12:48 +00:00
|
|
|
input()->setLastInputHandler(this);
|
2016-02-12 12:30:00 +00:00
|
|
|
QEvent::Type type;
|
|
|
|
switch (state) {
|
|
|
|
case InputRedirection::PointerButtonReleased:
|
|
|
|
type = QEvent::MouseButtonRelease;
|
|
|
|
break;
|
|
|
|
case InputRedirection::PointerButtonPressed:
|
|
|
|
type = QEvent::MouseButtonPress;
|
2018-09-15 00:00:24 +00:00
|
|
|
update();
|
2016-02-12 12:30:00 +00:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
Q_UNREACHABLE();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-09-15 00:00:24 +00:00
|
|
|
updateButton(button, state);
|
|
|
|
|
2016-05-24 08:57:57 +00:00
|
|
|
MouseEvent event(type, m_pos, buttonToQtMouseButton(button), m_qtButtons,
|
2018-09-15 00:37:24 +00:00
|
|
|
input()->keyboardModifiers(), time, QSizeF(), QSizeF(), 0, device);
|
|
|
|
event.setModifiersRelevantForGlobalShortcuts(input()->modifiersRelevantForGlobalShortcuts());
|
2016-12-30 18:07:45 +00:00
|
|
|
event.setNativeButton(button);
|
2016-02-12 12:30:00 +00:00
|
|
|
|
2018-09-15 00:37:24 +00:00
|
|
|
input()->processSpies(std::bind(&InputEventSpy::pointerEvent, std::placeholders::_1, &event));
|
2017-02-03 16:26:51 +00:00
|
|
|
|
2018-09-15 00:00:24 +00:00
|
|
|
if (!inited()) {
|
2017-02-03 16:26:51 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-09-15 00:37:24 +00:00
|
|
|
input()->processFilters(std::bind(&InputEventFilter::pointerEvent, std::placeholders::_1, &event, button));
|
2018-09-15 00:00:24 +00:00
|
|
|
|
|
|
|
if (state == InputRedirection::PointerButtonReleased) {
|
|
|
|
update();
|
|
|
|
}
|
2016-02-12 12:30:00 +00:00
|
|
|
}
|
|
|
|
|
Send axis_source, axis_discrete, and axis_stop
Summary:
So far KWin didn't send axis_source, axis_discrete, and axis_stop. Even
though most of those events are optional, clients need them to work as
expected. For example, one needs axis_source and axis_stop to implement
kinetic scrolling; Xwayland needs axis_discrete to prevent multiple
scroll events when the compositor sends axis deltas greater than 10, etc.
BUG: 404152
FIXED-IN: 5.17.0
Test Plan:
* Content of a webpage in Firefox is moved by one line per each mouse
wheel "click";
* Scrolled gedit using 2 fingers on GNOME Shell, sway, and KDE Plasma;
in all three cases wayland debug looked the same (except diagonal scroll
motions).
Reviewers: #kwin, davidedmundson
Reviewed By: #kwin, davidedmundson
Subscribers: davidedmundson, kwin
Tags: #kwin
Differential Revision: https://phabricator.kde.org/D19000
2019-02-12 09:14:51 +00:00
|
|
|
void PointerInputRedirection::processAxis(InputRedirection::PointerAxis axis, qreal delta, qint32 discreteDelta,
|
2021-10-02 08:26:51 +00:00
|
|
|
InputRedirection::PointerAxisSource source, uint32_t time, InputDevice *device)
|
2016-02-12 12:30:00 +00:00
|
|
|
{
|
2022-02-11 18:12:48 +00:00
|
|
|
input()->setLastInputHandler(this);
|
2018-09-15 00:00:24 +00:00
|
|
|
update();
|
2016-02-12 12:30:00 +00:00
|
|
|
|
2021-06-08 07:02:14 +00:00
|
|
|
Q_EMIT input()->pointerAxisChanged(axis, delta);
|
2016-02-12 12:30:00 +00:00
|
|
|
|
Send axis_source, axis_discrete, and axis_stop
Summary:
So far KWin didn't send axis_source, axis_discrete, and axis_stop. Even
though most of those events are optional, clients need them to work as
expected. For example, one needs axis_source and axis_stop to implement
kinetic scrolling; Xwayland needs axis_discrete to prevent multiple
scroll events when the compositor sends axis deltas greater than 10, etc.
BUG: 404152
FIXED-IN: 5.17.0
Test Plan:
* Content of a webpage in Firefox is moved by one line per each mouse
wheel "click";
* Scrolled gedit using 2 fingers on GNOME Shell, sway, and KDE Plasma;
in all three cases wayland debug looked the same (except diagonal scroll
motions).
Reviewers: #kwin, davidedmundson
Reviewed By: #kwin, davidedmundson
Subscribers: davidedmundson, kwin
Tags: #kwin
Differential Revision: https://phabricator.kde.org/D19000
2019-02-12 09:14:51 +00:00
|
|
|
WheelEvent wheelEvent(m_pos, delta, discreteDelta,
|
2016-02-12 12:30:00 +00:00
|
|
|
(axis == InputRedirection::PointerAxisHorizontal) ? Qt::Horizontal : Qt::Vertical,
|
Send axis_source, axis_discrete, and axis_stop
Summary:
So far KWin didn't send axis_source, axis_discrete, and axis_stop. Even
though most of those events are optional, clients need them to work as
expected. For example, one needs axis_source and axis_stop to implement
kinetic scrolling; Xwayland needs axis_discrete to prevent multiple
scroll events when the compositor sends axis deltas greater than 10, etc.
BUG: 404152
FIXED-IN: 5.17.0
Test Plan:
* Content of a webpage in Firefox is moved by one line per each mouse
wheel "click";
* Scrolled gedit using 2 fingers on GNOME Shell, sway, and KDE Plasma;
in all three cases wayland debug looked the same (except diagonal scroll
motions).
Reviewers: #kwin, davidedmundson
Reviewed By: #kwin, davidedmundson
Subscribers: davidedmundson, kwin
Tags: #kwin
Differential Revision: https://phabricator.kde.org/D19000
2019-02-12 09:14:51 +00:00
|
|
|
m_qtButtons, input()->keyboardModifiers(), source, time, device);
|
2018-09-15 00:37:24 +00:00
|
|
|
wheelEvent.setModifiersRelevantForGlobalShortcuts(input()->modifiersRelevantForGlobalShortcuts());
|
2016-02-12 12:30:00 +00:00
|
|
|
|
2018-09-15 00:37:24 +00:00
|
|
|
input()->processSpies(std::bind(&InputEventSpy::wheelEvent, std::placeholders::_1, &wheelEvent));
|
2017-02-03 16:26:51 +00:00
|
|
|
|
2018-09-15 00:00:24 +00:00
|
|
|
if (!inited()) {
|
2017-02-03 16:26:51 +00:00
|
|
|
return;
|
|
|
|
}
|
2018-09-15 00:37:24 +00:00
|
|
|
input()->processFilters(std::bind(&InputEventFilter::wheelEvent, std::placeholders::_1, &wheelEvent));
|
2016-02-12 12:30:00 +00:00
|
|
|
}
|
|
|
|
|
2021-10-02 08:26:51 +00:00
|
|
|
void PointerInputRedirection::processSwipeGestureBegin(int fingerCount, quint32 time, KWin::InputDevice *device)
|
2016-08-05 12:35:33 +00:00
|
|
|
{
|
2022-02-11 18:12:48 +00:00
|
|
|
input()->setLastInputHandler(this);
|
2016-08-05 12:35:33 +00:00
|
|
|
Q_UNUSED(device)
|
2018-09-15 00:00:24 +00:00
|
|
|
if (!inited()) {
|
2016-08-05 12:35:33 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-09-15 00:37:24 +00:00
|
|
|
input()->processSpies(std::bind(&InputEventSpy::swipeGestureBegin, std::placeholders::_1, fingerCount, time));
|
|
|
|
input()->processFilters(std::bind(&InputEventFilter::swipeGestureBegin, std::placeholders::_1, fingerCount, time));
|
2016-08-05 12:35:33 +00:00
|
|
|
}
|
|
|
|
|
2021-10-02 08:26:51 +00:00
|
|
|
void PointerInputRedirection::processSwipeGestureUpdate(const QSizeF &delta, quint32 time, KWin::InputDevice *device)
|
2016-08-05 12:35:33 +00:00
|
|
|
{
|
2022-02-11 18:12:48 +00:00
|
|
|
input()->setLastInputHandler(this);
|
2016-08-05 12:35:33 +00:00
|
|
|
Q_UNUSED(device)
|
2018-09-15 00:00:24 +00:00
|
|
|
if (!inited()) {
|
2016-08-05 12:35:33 +00:00
|
|
|
return;
|
|
|
|
}
|
2018-09-15 00:00:24 +00:00
|
|
|
update();
|
2016-08-05 12:35:33 +00:00
|
|
|
|
2018-09-15 00:37:24 +00:00
|
|
|
input()->processSpies(std::bind(&InputEventSpy::swipeGestureUpdate, std::placeholders::_1, delta, time));
|
|
|
|
input()->processFilters(std::bind(&InputEventFilter::swipeGestureUpdate, std::placeholders::_1, delta, time));
|
2016-08-05 12:35:33 +00:00
|
|
|
}
|
|
|
|
|
2021-10-02 08:26:51 +00:00
|
|
|
void PointerInputRedirection::processSwipeGestureEnd(quint32 time, KWin::InputDevice *device)
|
2016-08-05 12:35:33 +00:00
|
|
|
{
|
2022-02-11 18:12:48 +00:00
|
|
|
input()->setLastInputHandler(this);
|
2016-08-05 12:35:33 +00:00
|
|
|
Q_UNUSED(device)
|
2018-09-15 00:00:24 +00:00
|
|
|
if (!inited()) {
|
2016-08-05 12:35:33 +00:00
|
|
|
return;
|
|
|
|
}
|
2018-09-15 00:00:24 +00:00
|
|
|
update();
|
2016-08-05 12:35:33 +00:00
|
|
|
|
2018-09-15 00:37:24 +00:00
|
|
|
input()->processSpies(std::bind(&InputEventSpy::swipeGestureEnd, std::placeholders::_1, time));
|
|
|
|
input()->processFilters(std::bind(&InputEventFilter::swipeGestureEnd, std::placeholders::_1, time));
|
2016-08-05 12:35:33 +00:00
|
|
|
}
|
|
|
|
|
2021-10-02 08:26:51 +00:00
|
|
|
void PointerInputRedirection::processSwipeGestureCancelled(quint32 time, KWin::InputDevice *device)
|
2016-08-05 12:35:33 +00:00
|
|
|
{
|
2022-02-11 18:12:48 +00:00
|
|
|
input()->setLastInputHandler(this);
|
2016-08-05 12:35:33 +00:00
|
|
|
Q_UNUSED(device)
|
2018-09-15 00:00:24 +00:00
|
|
|
if (!inited()) {
|
2016-08-05 12:35:33 +00:00
|
|
|
return;
|
|
|
|
}
|
2018-09-15 00:00:24 +00:00
|
|
|
update();
|
2016-08-05 12:35:33 +00:00
|
|
|
|
2018-09-15 00:37:24 +00:00
|
|
|
input()->processSpies(std::bind(&InputEventSpy::swipeGestureCancelled, std::placeholders::_1, time));
|
|
|
|
input()->processFilters(std::bind(&InputEventFilter::swipeGestureCancelled, std::placeholders::_1, time));
|
2016-08-05 12:35:33 +00:00
|
|
|
}
|
|
|
|
|
2021-10-02 08:26:51 +00:00
|
|
|
void PointerInputRedirection::processPinchGestureBegin(int fingerCount, quint32 time, KWin::InputDevice *device)
|
2016-08-05 12:35:33 +00:00
|
|
|
{
|
2022-02-11 18:12:48 +00:00
|
|
|
input()->setLastInputHandler(this);
|
2016-08-05 12:35:33 +00:00
|
|
|
Q_UNUSED(device)
|
2018-09-15 00:00:24 +00:00
|
|
|
if (!inited()) {
|
2016-08-05 12:35:33 +00:00
|
|
|
return;
|
|
|
|
}
|
2018-09-15 00:00:24 +00:00
|
|
|
update();
|
2016-08-05 12:35:33 +00:00
|
|
|
|
2018-09-15 00:37:24 +00:00
|
|
|
input()->processSpies(std::bind(&InputEventSpy::pinchGestureBegin, std::placeholders::_1, fingerCount, time));
|
|
|
|
input()->processFilters(std::bind(&InputEventFilter::pinchGestureBegin, std::placeholders::_1, fingerCount, time));
|
2016-08-05 12:35:33 +00:00
|
|
|
}
|
|
|
|
|
2021-10-02 08:26:51 +00:00
|
|
|
void PointerInputRedirection::processPinchGestureUpdate(qreal scale, qreal angleDelta, const QSizeF &delta, quint32 time, KWin::InputDevice *device)
|
2016-08-05 12:35:33 +00:00
|
|
|
{
|
2022-02-11 18:12:48 +00:00
|
|
|
input()->setLastInputHandler(this);
|
2016-08-05 12:35:33 +00:00
|
|
|
Q_UNUSED(device)
|
2018-09-15 00:00:24 +00:00
|
|
|
if (!inited()) {
|
2016-08-05 12:35:33 +00:00
|
|
|
return;
|
|
|
|
}
|
2018-09-15 00:00:24 +00:00
|
|
|
update();
|
2016-08-05 12:35:33 +00:00
|
|
|
|
2018-09-15 00:37:24 +00:00
|
|
|
input()->processSpies(std::bind(&InputEventSpy::pinchGestureUpdate, std::placeholders::_1, scale, angleDelta, delta, time));
|
|
|
|
input()->processFilters(std::bind(&InputEventFilter::pinchGestureUpdate, std::placeholders::_1, scale, angleDelta, delta, time));
|
2016-08-05 12:35:33 +00:00
|
|
|
}
|
|
|
|
|
2021-10-02 08:26:51 +00:00
|
|
|
void PointerInputRedirection::processPinchGestureEnd(quint32 time, KWin::InputDevice *device)
|
2016-08-05 12:35:33 +00:00
|
|
|
{
|
2022-02-11 18:12:48 +00:00
|
|
|
input()->setLastInputHandler(this);
|
2016-08-05 12:35:33 +00:00
|
|
|
Q_UNUSED(device)
|
2018-09-15 00:00:24 +00:00
|
|
|
if (!inited()) {
|
2016-08-05 12:35:33 +00:00
|
|
|
return;
|
|
|
|
}
|
2018-09-15 00:00:24 +00:00
|
|
|
update();
|
2016-08-05 12:35:33 +00:00
|
|
|
|
2018-09-15 00:37:24 +00:00
|
|
|
input()->processSpies(std::bind(&InputEventSpy::pinchGestureEnd, std::placeholders::_1, time));
|
|
|
|
input()->processFilters(std::bind(&InputEventFilter::pinchGestureEnd, std::placeholders::_1, time));
|
2016-08-05 12:35:33 +00:00
|
|
|
}
|
|
|
|
|
2021-10-02 08:26:51 +00:00
|
|
|
void PointerInputRedirection::processPinchGestureCancelled(quint32 time, KWin::InputDevice *device)
|
2016-08-05 12:35:33 +00:00
|
|
|
{
|
2022-02-11 18:12:48 +00:00
|
|
|
input()->setLastInputHandler(this);
|
2016-08-05 12:35:33 +00:00
|
|
|
Q_UNUSED(device)
|
2018-09-15 00:00:24 +00:00
|
|
|
if (!inited()) {
|
2016-08-05 12:35:33 +00:00
|
|
|
return;
|
|
|
|
}
|
2018-09-15 00:00:24 +00:00
|
|
|
update();
|
2016-08-05 12:35:33 +00:00
|
|
|
|
2018-09-15 00:37:24 +00:00
|
|
|
input()->processSpies(std::bind(&InputEventSpy::pinchGestureCancelled, std::placeholders::_1, time));
|
|
|
|
input()->processFilters(std::bind(&InputEventFilter::pinchGestureCancelled, std::placeholders::_1, time));
|
2016-08-05 12:35:33 +00:00
|
|
|
}
|
|
|
|
|
2021-10-02 08:26:51 +00:00
|
|
|
void PointerInputRedirection::processHoldGestureBegin(int fingerCount, quint32 time, KWin::InputDevice *device)
|
2021-08-05 08:31:08 +00:00
|
|
|
{
|
|
|
|
Q_UNUSED(device)
|
|
|
|
if (!inited()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
update();
|
|
|
|
|
|
|
|
input()->processSpies(std::bind(&InputEventSpy::holdGestureBegin, std::placeholders::_1, fingerCount, time));
|
|
|
|
input()->processFilters(std::bind(&InputEventFilter::holdGestureBegin, std::placeholders::_1, fingerCount, time));
|
|
|
|
}
|
|
|
|
|
2021-10-02 08:26:51 +00:00
|
|
|
void PointerInputRedirection::processHoldGestureEnd(quint32 time, KWin::InputDevice *device)
|
2021-08-05 08:31:08 +00:00
|
|
|
{
|
|
|
|
Q_UNUSED(device)
|
|
|
|
if (!inited()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
update();
|
|
|
|
|
|
|
|
input()->processSpies(std::bind(&InputEventSpy::holdGestureEnd, std::placeholders::_1, time));
|
|
|
|
input()->processFilters(std::bind(&InputEventFilter::holdGestureEnd, std::placeholders::_1, time));
|
|
|
|
}
|
|
|
|
|
2021-10-02 08:26:51 +00:00
|
|
|
void PointerInputRedirection::processHoldGestureCancelled(quint32 time, KWin::InputDevice *device)
|
2021-08-05 08:31:08 +00:00
|
|
|
{
|
|
|
|
Q_UNUSED(device)
|
|
|
|
if (!inited()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
update();
|
|
|
|
|
|
|
|
input()->processSpies(std::bind(&InputEventSpy::holdGestureCancelled, std::placeholders::_1, time));
|
|
|
|
input()->processFilters(std::bind(&InputEventFilter::holdGestureCancelled, std::placeholders::_1, time));
|
|
|
|
}
|
|
|
|
|
2017-09-27 16:17:33 +00:00
|
|
|
bool PointerInputRedirection::areButtonsPressed() const
|
|
|
|
{
|
|
|
|
for (auto state : m_buttons) {
|
|
|
|
if (state == InputRedirection::PointerButtonPressed) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2018-09-15 00:00:24 +00:00
|
|
|
bool PointerInputRedirection::focusUpdatesBlocked()
|
2016-02-12 12:30:00 +00:00
|
|
|
{
|
2016-03-01 07:42:07 +00:00
|
|
|
if (waylandServer()->seat()->isDragPointer()) {
|
|
|
|
// ignore during drag and drop
|
2018-09-15 00:00:24 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
if (waylandServer()->seat()->isTouchSequence()) {
|
|
|
|
// ignore during touch operations
|
|
|
|
return true;
|
2016-03-01 07:42:07 +00:00
|
|
|
}
|
2016-11-15 13:23:51 +00:00
|
|
|
if (input()->isSelectingWindow()) {
|
2018-09-15 00:00:24 +00:00
|
|
|
return true;
|
2016-11-15 13:23:51 +00:00
|
|
|
}
|
2017-04-15 09:36:21 +00:00
|
|
|
if (areButtonsPressed()) {
|
2018-09-15 00:00:24 +00:00
|
|
|
return true;
|
2017-04-15 09:36:21 +00:00
|
|
|
}
|
2018-09-15 00:00:24 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
void PointerInputRedirection::cleanupDecoration(Decoration::DecoratedClientImpl *old, Decoration::DecoratedClientImpl *now)
|
|
|
|
{
|
|
|
|
disconnect(m_decorationGeometryConnection);
|
|
|
|
m_decorationGeometryConnection = QMetaObject::Connection();
|
|
|
|
|
2021-07-15 12:35:51 +00:00
|
|
|
disconnect(m_decorationDestroyedConnection);
|
|
|
|
m_decorationDestroyedConnection = QMetaObject::Connection();
|
|
|
|
|
2018-09-15 00:00:24 +00:00
|
|
|
if (old) {
|
|
|
|
// send leave event to old decoration
|
|
|
|
QHoverEvent event(QEvent::HoverLeave, QPointF(), QPointF());
|
|
|
|
QCoreApplication::instance()->sendEvent(old->decoration(), &event);
|
2016-02-23 11:29:05 +00:00
|
|
|
}
|
2018-09-15 00:00:24 +00:00
|
|
|
if (!now) {
|
|
|
|
// left decoration
|
2016-02-12 12:30:00 +00:00
|
|
|
return;
|
|
|
|
}
|
2018-09-15 00:00:24 +00:00
|
|
|
|
|
|
|
auto pos = m_pos - now->client()->pos();
|
|
|
|
QHoverEvent event(QEvent::HoverEnter, pos, pos);
|
|
|
|
QCoreApplication::instance()->sendEvent(now->decoration(), &event);
|
|
|
|
now->client()->processDecorationMove(pos.toPoint(), m_pos.toPoint());
|
|
|
|
|
2020-02-05 09:28:50 +00:00
|
|
|
m_decorationGeometryConnection = connect(decoration()->client(), &AbstractClient::frameGeometryChanged, this,
|
2018-09-15 00:00:24 +00:00
|
|
|
[this] {
|
|
|
|
// ensure maximize button gets the leave event when maximizing/restore a window, see BUG 385140
|
|
|
|
const auto oldDeco = decoration();
|
|
|
|
update();
|
|
|
|
if (oldDeco &&
|
|
|
|
oldDeco == decoration() &&
|
2021-04-30 18:06:58 +00:00
|
|
|
!decoration()->client()->isInteractiveMove() &&
|
|
|
|
!decoration()->client()->isInteractiveResize() &&
|
2018-09-15 00:00:24 +00:00
|
|
|
!areButtonsPressed()) {
|
|
|
|
// position of window did not change, we need to send HoverMotion manually
|
|
|
|
const QPointF p = m_pos - decoration()->client()->pos();
|
|
|
|
QHoverEvent event(QEvent::HoverMove, p, p);
|
|
|
|
QCoreApplication::instance()->sendEvent(decoration()->decoration(), &event);
|
|
|
|
}
|
|
|
|
}, Qt::QueuedConnection);
|
2021-07-15 12:35:51 +00:00
|
|
|
|
|
|
|
// if our decoration gets destroyed whilst it has focus, we pass focus on to the same client
|
|
|
|
m_decorationDestroyedConnection = connect(now, &QObject::destroyed, this, &PointerInputRedirection::update, Qt::QueuedConnection);
|
2018-09-15 00:00:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void PointerInputRedirection::focusUpdate(Toplevel *focusOld, Toplevel *focusNow)
|
|
|
|
{
|
|
|
|
if (AbstractClient *ac = qobject_cast<AbstractClient*>(focusOld)) {
|
2021-11-29 16:59:06 +00:00
|
|
|
ac->pointerLeaveEvent();
|
2018-09-15 00:00:24 +00:00
|
|
|
breakPointerConstraints(ac->surface());
|
2016-11-25 06:17:43 +00:00
|
|
|
disconnectPointerConstraintsConnection();
|
2016-02-12 12:30:00 +00:00
|
|
|
}
|
2018-09-15 00:00:24 +00:00
|
|
|
disconnect(m_focusGeometryConnection);
|
|
|
|
m_focusGeometryConnection = QMetaObject::Connection();
|
|
|
|
|
|
|
|
if (AbstractClient *ac = qobject_cast<AbstractClient*>(focusNow)) {
|
2021-11-29 16:59:06 +00:00
|
|
|
ac->pointerEnterEvent(m_pos.toPoint());
|
2016-02-18 12:02:07 +00:00
|
|
|
}
|
2018-09-15 00:00:24 +00:00
|
|
|
|
2019-08-26 07:44:04 +00:00
|
|
|
auto seat = waylandServer()->seat();
|
2021-12-15 15:17:17 +00:00
|
|
|
if (!focusNow || !focusNow->surface()) {
|
2022-02-23 19:15:40 +00:00
|
|
|
seat->notifyPointerLeave();
|
2019-08-26 07:44:04 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2022-02-23 19:15:40 +00:00
|
|
|
seat->notifyPointerEnter(focusNow->surface(), m_pos, focusNow->inputTransformation());
|
2018-09-15 00:00:24 +00:00
|
|
|
|
2020-07-14 11:34:25 +00:00
|
|
|
m_focusGeometryConnection = connect(focusNow, &Toplevel::inputTransformationChanged, this,
|
2018-09-15 00:00:24 +00:00
|
|
|
[this] {
|
|
|
|
// TODO: why no assert possible?
|
|
|
|
if (!focus()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
// TODO: can we check on the client instead?
|
2019-04-18 12:28:11 +00:00
|
|
|
if (workspace()->moveResizeClient()) {
|
2018-09-15 00:00:24 +00:00
|
|
|
// don't update while moving
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
auto seat = waylandServer()->seat();
|
|
|
|
if (focus()->surface() != seat->focusedPointerSurface()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
seat->setFocusedPointerSurfaceTransformation(focus()->inputTransformation());
|
|
|
|
}
|
|
|
|
);
|
|
|
|
|
2020-04-29 15:18:41 +00:00
|
|
|
m_constraintsConnection = connect(focusNow->surface(), &KWaylandServer::SurfaceInterface::pointerConstraintsChanged,
|
2018-09-15 00:00:24 +00:00
|
|
|
this, &PointerInputRedirection::updatePointerConstraints);
|
|
|
|
m_constraintsActivatedConnection = connect(workspace(), &Workspace::clientActivated,
|
|
|
|
this, &PointerInputRedirection::updatePointerConstraints);
|
|
|
|
updatePointerConstraints();
|
2016-02-12 12:30:00 +00:00
|
|
|
}
|
|
|
|
|
2020-04-29 15:18:41 +00:00
|
|
|
void PointerInputRedirection::breakPointerConstraints(KWaylandServer::SurfaceInterface *surface)
|
2016-11-25 06:17:43 +00:00
|
|
|
{
|
|
|
|
// cancel pointer constraints
|
|
|
|
if (surface) {
|
|
|
|
auto c = surface->confinedPointer();
|
|
|
|
if (c && c->isConfined()) {
|
|
|
|
c->setConfined(false);
|
|
|
|
}
|
|
|
|
auto l = surface->lockedPointer();
|
|
|
|
if (l && l->isLocked()) {
|
|
|
|
l->setLocked(false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
disconnectConfinedPointerRegionConnection();
|
|
|
|
m_confined = false;
|
|
|
|
m_locked = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
void PointerInputRedirection::disconnectConfinedPointerRegionConnection()
|
|
|
|
{
|
|
|
|
disconnect(m_confinedPointerRegionConnection);
|
|
|
|
m_confinedPointerRegionConnection = QMetaObject::Connection();
|
|
|
|
}
|
|
|
|
|
2018-07-18 07:06:56 +00:00
|
|
|
void PointerInputRedirection::disconnectLockedPointerAboutToBeUnboundConnection()
|
|
|
|
{
|
|
|
|
disconnect(m_lockedPointerAboutToBeUnboundConnection);
|
|
|
|
m_lockedPointerAboutToBeUnboundConnection = QMetaObject::Connection();
|
|
|
|
}
|
|
|
|
|
2016-11-25 06:17:43 +00:00
|
|
|
void PointerInputRedirection::disconnectPointerConstraintsConnection()
|
|
|
|
{
|
|
|
|
disconnect(m_constraintsConnection);
|
|
|
|
m_constraintsConnection = QMetaObject::Connection();
|
2018-06-18 18:14:04 +00:00
|
|
|
|
|
|
|
disconnect(m_constraintsActivatedConnection);
|
|
|
|
m_constraintsActivatedConnection = QMetaObject::Connection();
|
2016-11-25 06:17:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
template <typename T>
|
|
|
|
static QRegion getConstraintRegion(Toplevel *t, T *constraint)
|
|
|
|
{
|
|
|
|
const QRegion windowShape = t->inputShape();
|
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.
2020-10-17 12:47:25 +00:00
|
|
|
const QRegion intersected = constraint->region().isEmpty() ? windowShape : windowShape.intersected(constraint->region());
|
2016-11-25 06:17:43 +00:00
|
|
|
return intersected.translated(t->pos() + t->clientPos());
|
|
|
|
}
|
|
|
|
|
2018-07-15 18:44:21 +00:00
|
|
|
void PointerInputRedirection::setEnableConstraints(bool set)
|
|
|
|
{
|
|
|
|
if (m_enableConstraints == set) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
m_enableConstraints = set;
|
|
|
|
updatePointerConstraints();
|
|
|
|
}
|
|
|
|
|
2018-06-11 20:45:09 +00:00
|
|
|
void PointerInputRedirection::updatePointerConstraints()
|
2016-11-25 06:17:43 +00:00
|
|
|
{
|
2020-09-07 08:11:07 +00:00
|
|
|
if (!focus()) {
|
2016-11-25 06:17:43 +00:00
|
|
|
return;
|
|
|
|
}
|
2018-09-15 00:00:24 +00:00
|
|
|
const auto s = focus()->surface();
|
2016-11-25 06:17:43 +00:00
|
|
|
if (!s) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (s != waylandServer()->seat()->focusedPointerSurface()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (!supportsWarping()) {
|
|
|
|
return;
|
|
|
|
}
|
2018-09-15 00:00:24 +00:00
|
|
|
const bool canConstrain = m_enableConstraints && focus() == workspace()->activeClient();
|
2016-11-25 06:17:43 +00:00
|
|
|
const auto cf = s->confinedPointer();
|
|
|
|
if (cf) {
|
|
|
|
if (cf->isConfined()) {
|
2018-07-15 18:44:21 +00:00
|
|
|
if (!canConstrain) {
|
2018-06-18 18:14:04 +00:00
|
|
|
cf->setConfined(false);
|
|
|
|
m_confined = false;
|
|
|
|
disconnectConfinedPointerRegionConnection();
|
|
|
|
}
|
2016-11-25 06:17:43 +00:00
|
|
|
return;
|
|
|
|
}
|
2020-11-03 19:54:49 +00:00
|
|
|
const QRegion r = getConstraintRegion(focus(), cf);
|
2018-07-15 18:44:21 +00:00
|
|
|
if (canConstrain && r.contains(m_pos.toPoint())) {
|
2016-11-25 06:17:43 +00:00
|
|
|
cf->setConfined(true);
|
|
|
|
m_confined = true;
|
2020-11-03 19:54:49 +00:00
|
|
|
m_confinedPointerRegionConnection = connect(cf, &KWaylandServer::ConfinedPointerV1Interface::regionChanged, this,
|
2016-11-25 06:17:43 +00:00
|
|
|
[this] {
|
2018-09-15 00:00:24 +00:00
|
|
|
if (!focus()) {
|
2016-11-25 06:17:43 +00:00
|
|
|
return;
|
|
|
|
}
|
2018-09-15 00:00:24 +00:00
|
|
|
const auto s = focus()->surface();
|
2016-11-25 06:17:43 +00:00
|
|
|
if (!s) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
const auto cf = s->confinedPointer();
|
2020-11-03 19:54:49 +00:00
|
|
|
if (!getConstraintRegion(focus(), cf).contains(m_pos.toPoint())) {
|
2016-11-25 06:17:43 +00:00
|
|
|
// pointer no longer in confined region, break the confinement
|
|
|
|
cf->setConfined(false);
|
|
|
|
m_confined = false;
|
|
|
|
} else {
|
|
|
|
if (!cf->isConfined()) {
|
|
|
|
cf->setConfined(true);
|
|
|
|
m_confined = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
} else {
|
2018-06-11 20:45:09 +00:00
|
|
|
m_confined = false;
|
2016-11-25 06:17:43 +00:00
|
|
|
disconnectConfinedPointerRegionConnection();
|
|
|
|
}
|
|
|
|
const auto lock = s->lockedPointer();
|
|
|
|
if (lock) {
|
|
|
|
if (lock->isLocked()) {
|
2018-07-15 18:44:21 +00:00
|
|
|
if (!canConstrain) {
|
2018-07-18 07:06:56 +00:00
|
|
|
const auto hint = lock->cursorPositionHint();
|
2018-06-18 18:14:04 +00:00
|
|
|
lock->setLocked(false);
|
|
|
|
m_locked = false;
|
2018-07-18 07:06:56 +00:00
|
|
|
disconnectLockedPointerAboutToBeUnboundConnection();
|
2018-09-15 00:00:24 +00:00
|
|
|
if (! (hint.x() < 0 || hint.y() < 0) && focus()) {
|
2021-10-05 19:01:16 +00:00
|
|
|
processMotionAbsolute(focus()->mapFromLocal(hint), waylandServer()->seat()->timestamp());
|
2018-07-18 07:06:56 +00:00
|
|
|
}
|
2018-06-18 18:14:04 +00:00
|
|
|
}
|
2016-11-25 06:17:43 +00:00
|
|
|
return;
|
|
|
|
}
|
2020-11-03 19:54:49 +00:00
|
|
|
const QRegion r = getConstraintRegion(focus(), lock);
|
2018-07-15 18:44:21 +00:00
|
|
|
if (canConstrain && r.contains(m_pos.toPoint())) {
|
2016-11-25 06:17:43 +00:00
|
|
|
lock->setLocked(true);
|
|
|
|
m_locked = true;
|
2018-07-18 07:06:56 +00:00
|
|
|
|
|
|
|
// The client might cancel pointer locking from its side by unbinding the LockedPointerInterface.
|
|
|
|
// In this case the cached cursor position hint must be fetched before the resource goes away
|
2020-11-03 19:54:49 +00:00
|
|
|
m_lockedPointerAboutToBeUnboundConnection = connect(lock, &KWaylandServer::LockedPointerV1Interface::aboutToBeDestroyed, this,
|
2018-07-18 07:06:56 +00:00
|
|
|
[this, lock]() {
|
|
|
|
const auto hint = lock->cursorPositionHint();
|
2018-09-15 00:00:24 +00:00
|
|
|
if (hint.x() < 0 || hint.y() < 0 || !focus()) {
|
2018-07-18 07:06:56 +00:00
|
|
|
return;
|
|
|
|
}
|
2021-05-16 14:32:07 +00:00
|
|
|
auto globalHint = focus()->mapFromLocal(hint);
|
2018-07-18 07:06:56 +00:00
|
|
|
|
|
|
|
// When the resource finally goes away, reposition the cursor according to the hint
|
2020-11-03 19:54:49 +00:00
|
|
|
connect(lock, &KWaylandServer::LockedPointerV1Interface::destroyed, this,
|
2018-07-18 07:06:56 +00:00
|
|
|
[this, globalHint]() {
|
2021-10-05 19:01:16 +00:00
|
|
|
processMotionAbsolute(globalHint, waylandServer()->seat()->timestamp());
|
2018-07-18 07:06:56 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
);
|
2016-11-25 06:17:43 +00:00
|
|
|
// TODO: connect to region change - is it needed at all? If the pointer is locked it's always in the region
|
|
|
|
}
|
2018-06-11 20:45:09 +00:00
|
|
|
} else {
|
|
|
|
m_locked = false;
|
2018-07-18 07:06:56 +00:00
|
|
|
disconnectLockedPointerAboutToBeUnboundConnection();
|
2016-11-25 06:17:43 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
QPointF PointerInputRedirection::applyPointerConfinement(const QPointF &pos) const
|
|
|
|
{
|
2018-09-15 00:00:24 +00:00
|
|
|
if (!focus()) {
|
2016-11-25 06:17:43 +00:00
|
|
|
return pos;
|
|
|
|
}
|
2018-09-15 00:00:24 +00:00
|
|
|
auto s = focus()->surface();
|
2016-11-25 06:17:43 +00:00
|
|
|
if (!s) {
|
|
|
|
return pos;
|
|
|
|
}
|
|
|
|
auto cf = s->confinedPointer();
|
|
|
|
if (!cf) {
|
|
|
|
return pos;
|
|
|
|
}
|
|
|
|
if (!cf->isConfined()) {
|
|
|
|
return pos;
|
|
|
|
}
|
|
|
|
|
2020-11-03 19:54:49 +00:00
|
|
|
const QRegion confinementRegion = getConstraintRegion(focus(), cf);
|
2016-11-25 06:17:43 +00:00
|
|
|
if (confinementRegion.contains(pos.toPoint())) {
|
|
|
|
return pos;
|
|
|
|
}
|
|
|
|
QPointF p = pos;
|
|
|
|
// allow either x or y to pass
|
|
|
|
p = QPointF(m_pos.x(), pos.y());
|
|
|
|
if (confinementRegion.contains(p.toPoint())) {
|
|
|
|
return p;
|
|
|
|
}
|
|
|
|
p = QPointF(pos.x(), m_pos.y());
|
|
|
|
if (confinementRegion.contains(p.toPoint())) {
|
|
|
|
return p;
|
|
|
|
}
|
|
|
|
|
|
|
|
return m_pos;
|
|
|
|
}
|
|
|
|
|
2016-02-12 12:30:00 +00:00
|
|
|
void PointerInputRedirection::updatePosition(const QPointF &pos)
|
|
|
|
{
|
2016-11-25 06:17:43 +00:00
|
|
|
if (m_locked) {
|
|
|
|
// locked pointer should not move
|
|
|
|
return;
|
|
|
|
}
|
2016-02-12 12:30:00 +00:00
|
|
|
// verify that at least one screen contains the pointer position
|
|
|
|
QPointF p = pos;
|
|
|
|
if (!screenContainsPos(p)) {
|
2021-08-30 07:52:27 +00:00
|
|
|
const QRectF unitedScreensGeometry = workspace()->geometry();
|
2018-07-10 18:09:22 +00:00
|
|
|
p = confineToBoundingBox(p, unitedScreensGeometry);
|
2016-02-12 12:30:00 +00:00
|
|
|
if (!screenContainsPos(p)) {
|
2021-08-27 15:49:54 +00:00
|
|
|
const AbstractOutput *currentOutput = kwinApp()->platform()->outputAt(m_pos.toPoint());
|
|
|
|
p = confineToBoundingBox(p, currentOutput->geometry());
|
2016-02-12 12:30:00 +00:00
|
|
|
}
|
|
|
|
}
|
2016-11-25 06:17:43 +00:00
|
|
|
p = applyPointerConfinement(p);
|
|
|
|
if (p == m_pos) {
|
|
|
|
// didn't change due to confinement
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
// verify screen confinement
|
|
|
|
if (!screenContainsPos(p)) {
|
|
|
|
return;
|
|
|
|
}
|
2021-06-25 08:58:28 +00:00
|
|
|
|
2016-02-12 12:30:00 +00:00
|
|
|
m_pos = p;
|
2021-06-25 08:58:28 +00:00
|
|
|
|
|
|
|
updateCursorOutputs();
|
|
|
|
|
2021-06-08 07:02:14 +00:00
|
|
|
Q_EMIT input()->globalPointerChanged(m_pos);
|
2016-02-12 12:30:00 +00:00
|
|
|
}
|
|
|
|
|
2021-06-25 08:58:28 +00:00
|
|
|
void PointerInputRedirection::updateCursorOutputs()
|
|
|
|
{
|
|
|
|
KWaylandServer::PointerInterface *pointer = waylandServer()->seat()->pointer();
|
|
|
|
if (!pointer) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
KWaylandServer::Cursor *cursor = pointer->cursor();
|
|
|
|
if (!cursor) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
KWaylandServer::SurfaceInterface *surface = cursor->surface();
|
|
|
|
if (!surface) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
const QRectF cursorGeometry(m_pos - m_cursor->hotSpot(), surface->size());
|
|
|
|
surface->setOutputs(waylandServer()->display()->outputsIntersecting(cursorGeometry.toAlignedRect()));
|
|
|
|
}
|
|
|
|
|
2016-02-12 12:30:00 +00:00
|
|
|
void PointerInputRedirection::updateButton(uint32_t button, InputRedirection::PointerButtonState state)
|
|
|
|
{
|
|
|
|
m_buttons[button] = state;
|
|
|
|
|
|
|
|
// update Qt buttons
|
|
|
|
m_qtButtons = Qt::NoButton;
|
|
|
|
for (auto it = m_buttons.constBegin(); it != m_buttons.constEnd(); ++it) {
|
|
|
|
if (it.value() == InputRedirection::PointerButtonReleased) {
|
|
|
|
continue;
|
|
|
|
}
|
2016-02-17 10:16:57 +00:00
|
|
|
m_qtButtons |= buttonToQtMouseButton(it.key());
|
2016-02-12 12:30:00 +00:00
|
|
|
}
|
|
|
|
|
2021-06-08 07:02:14 +00:00
|
|
|
Q_EMIT input()->pointerButtonStateChanged(button, state);
|
2016-02-12 12:30:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void PointerInputRedirection::warp(const QPointF &pos)
|
|
|
|
{
|
|
|
|
if (supportsWarping()) {
|
2016-04-07 06:28:35 +00:00
|
|
|
kwinApp()->platform()->warpPointer(pos);
|
2021-10-05 19:01:16 +00:00
|
|
|
processMotionAbsolute(pos, waylandServer()->seat()->timestamp());
|
2016-02-12 12:30:00 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool PointerInputRedirection::supportsWarping() const
|
|
|
|
{
|
2018-09-15 00:00:24 +00:00
|
|
|
if (!inited()) {
|
2016-02-12 12:30:00 +00:00
|
|
|
return false;
|
|
|
|
}
|
2021-10-02 08:26:51 +00:00
|
|
|
return kwinApp()->platform()->supportsPointerWarping();
|
2016-02-12 12:30:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void PointerInputRedirection::updateAfterScreenChange()
|
|
|
|
{
|
2018-09-15 00:00:24 +00:00
|
|
|
if (!inited()) {
|
2016-02-12 12:30:00 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (screenContainsPos(m_pos)) {
|
|
|
|
// pointer still on a screen
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
// pointer no longer on a screen, reposition to closes screen
|
2021-08-27 15:49:54 +00:00
|
|
|
const AbstractOutput *output = kwinApp()->platform()->outputAt(m_pos.toPoint());
|
2016-02-12 12:30:00 +00:00
|
|
|
// TODO: better way to get timestamps
|
2021-10-05 19:01:16 +00:00
|
|
|
processMotionAbsolute(output->geometry().center(), waylandServer()->seat()->timestamp());
|
2016-02-12 12:30:00 +00:00
|
|
|
}
|
|
|
|
|
2018-09-15 00:00:24 +00:00
|
|
|
QPointF PointerInputRedirection::position() const
|
|
|
|
{
|
|
|
|
return m_pos.toPoint();
|
|
|
|
}
|
|
|
|
|
2016-02-23 11:29:05 +00:00
|
|
|
void PointerInputRedirection::setEffectsOverrideCursor(Qt::CursorShape shape)
|
|
|
|
{
|
2018-09-15 00:00:24 +00:00
|
|
|
if (!inited()) {
|
2016-02-23 11:29:05 +00:00
|
|
|
return;
|
|
|
|
}
|
2016-02-25 08:15:41 +00:00
|
|
|
// current pointer focus window should get a leave event
|
|
|
|
update();
|
2016-02-23 11:29:05 +00:00
|
|
|
m_cursor->setEffectsOverrideCursor(shape);
|
|
|
|
}
|
|
|
|
|
|
|
|
void PointerInputRedirection::removeEffectsOverrideCursor()
|
|
|
|
{
|
2018-09-15 00:00:24 +00:00
|
|
|
if (!inited()) {
|
2016-02-23 11:29:05 +00:00
|
|
|
return;
|
|
|
|
}
|
2016-02-25 08:15:41 +00:00
|
|
|
// cursor position might have changed while there was an effect in place
|
|
|
|
update();
|
2016-02-23 11:29:05 +00:00
|
|
|
m_cursor->removeEffectsOverrideCursor();
|
|
|
|
}
|
|
|
|
|
2016-11-15 13:23:51 +00:00
|
|
|
void PointerInputRedirection::setWindowSelectionCursor(const QByteArray &shape)
|
|
|
|
{
|
2018-09-15 00:00:24 +00:00
|
|
|
if (!inited()) {
|
2016-11-15 13:23:51 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
// send leave to current pointer focus window
|
|
|
|
updateToReset();
|
|
|
|
m_cursor->setWindowSelectionCursor(shape);
|
|
|
|
}
|
|
|
|
|
|
|
|
void PointerInputRedirection::removeWindowSelectionCursor()
|
|
|
|
{
|
2018-09-15 00:00:24 +00:00
|
|
|
if (!inited()) {
|
2016-11-15 13:23:51 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
update();
|
|
|
|
m_cursor->removeWindowSelectionCursor();
|
|
|
|
}
|
|
|
|
|
2016-02-23 11:29:05 +00:00
|
|
|
CursorImage::CursorImage(PointerInputRedirection *parent)
|
|
|
|
: QObject(parent)
|
|
|
|
, m_pointer(parent)
|
|
|
|
{
|
2021-02-22 10:51:26 +00:00
|
|
|
connect(waylandServer()->seat(), &KWaylandServer::SeatInterface::hasPointerChanged,
|
|
|
|
this, &CursorImage::handlePointerChanged);
|
2020-04-29 15:18:41 +00:00
|
|
|
connect(waylandServer()->seat(), &KWaylandServer::SeatInterface::dragStarted, this, &CursorImage::updateDrag);
|
|
|
|
connect(waylandServer()->seat(), &KWaylandServer::SeatInterface::dragEnded, this,
|
2016-03-01 07:42:07 +00:00
|
|
|
[this] {
|
|
|
|
disconnect(m_drag.connection);
|
|
|
|
reevaluteSource();
|
|
|
|
}
|
|
|
|
);
|
2022-02-28 18:58:35 +00:00
|
|
|
#if KWIN_BUILD_SCREENLOCKER
|
2016-04-25 06:51:33 +00:00
|
|
|
if (waylandServer()->hasScreenLockerIntegration()) {
|
|
|
|
connect(ScreenLocker::KSldApp::self(), &ScreenLocker::KSldApp::lockStateChanged, this, &CursorImage::reevaluteSource);
|
|
|
|
}
|
2022-02-23 13:27:05 +00:00
|
|
|
#endif
|
2016-02-23 11:29:05 +00:00
|
|
|
connect(m_pointer, &PointerInputRedirection::decorationChanged, this, &CursorImage::updateDecoration);
|
2016-02-23 13:08:28 +00:00
|
|
|
// connect the move resize of all window
|
|
|
|
auto setupMoveResizeConnection = [this] (AbstractClient *c) {
|
|
|
|
connect(c, &AbstractClient::moveResizedChanged, this, &CursorImage::updateMoveResize);
|
[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
2018-12-25 15:30:38 +00:00
|
|
|
connect(c, &AbstractClient::moveResizeCursorChanged, this, &CursorImage::updateMoveResize);
|
2016-02-23 13:08:28 +00:00
|
|
|
};
|
|
|
|
const auto clients = workspace()->allClientList();
|
|
|
|
std::for_each(clients.begin(), clients.end(), setupMoveResizeConnection);
|
|
|
|
connect(workspace(), &Workspace::clientAdded, this, setupMoveResizeConnection);
|
2016-02-23 11:29:05 +00:00
|
|
|
loadThemeCursor(Qt::ArrowCursor, &m_fallbackCursor);
|
2020-04-02 16:18:01 +00:00
|
|
|
|
|
|
|
connect(&m_waylandImage, &WaylandCursorImage::themeChanged, this, [this] {
|
|
|
|
loadThemeCursor(Qt::ArrowCursor, &m_fallbackCursor);
|
|
|
|
updateDecorationCursor();
|
|
|
|
updateMoveResize();
|
|
|
|
// TODO: update effects
|
|
|
|
});
|
2021-02-22 10:51:26 +00:00
|
|
|
|
|
|
|
handlePointerChanged();
|
2016-02-23 11:29:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
CursorImage::~CursorImage() = default;
|
|
|
|
|
2022-01-13 15:11:04 +00:00
|
|
|
void CursorImage::markAsRendered(std::chrono::milliseconds timestamp)
|
2016-02-23 11:29:05 +00:00
|
|
|
{
|
2016-03-01 07:42:07 +00:00
|
|
|
if (m_currentSource == CursorSource::DragAndDrop) {
|
|
|
|
// always sending a frame rendered to the drag icon surface to not freeze QtWayland (see https://bugreports.qt.io/browse/QTBUG-51599 )
|
2021-09-03 11:45:17 +00:00
|
|
|
if (const KWaylandServer::DragAndDropIcon *icon = waylandServer()->seat()->dragIcon()) {
|
2022-01-13 15:11:04 +00:00
|
|
|
icon->surface()->frameRendered(timestamp.count());
|
2016-03-01 07:42:07 +00:00
|
|
|
}
|
|
|
|
}
|
2021-02-22 10:51:26 +00:00
|
|
|
if (m_currentSource != CursorSource::LockScreen
|
|
|
|
&& m_currentSource != CursorSource::PointerSurface
|
|
|
|
&& m_currentSource != CursorSource::DragAndDrop) {
|
2016-02-23 11:29:05 +00:00
|
|
|
return;
|
|
|
|
}
|
2021-02-22 10:51:26 +00:00
|
|
|
auto p = waylandServer()->seat()->pointer();
|
2016-02-23 11:29:05 +00:00
|
|
|
if (!p) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
auto c = p->cursor();
|
|
|
|
if (!c) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
auto cursorSurface = c->surface();
|
2021-02-22 10:51:26 +00:00
|
|
|
if (!cursorSurface) {
|
2016-02-23 11:29:05 +00:00
|
|
|
return;
|
|
|
|
}
|
2022-01-13 15:11:04 +00:00
|
|
|
cursorSurface->frameRendered(timestamp.count());
|
2016-02-23 11:29:05 +00:00
|
|
|
}
|
|
|
|
|
2021-02-22 10:51:26 +00:00
|
|
|
void CursorImage::handlePointerChanged()
|
|
|
|
{
|
|
|
|
KWaylandServer::PointerInterface *pointer = waylandServer()->seat()->pointer();
|
|
|
|
|
|
|
|
if (pointer) {
|
|
|
|
connect(pointer, &KWaylandServer::PointerInterface::focusedSurfaceChanged,
|
|
|
|
this, &CursorImage::handleFocusedSurfaceChanged);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void CursorImage::handleFocusedSurfaceChanged()
|
2016-02-23 11:29:05 +00:00
|
|
|
{
|
2021-02-22 10:51:26 +00:00
|
|
|
KWaylandServer::PointerInterface *pointer = waylandServer()->seat()->pointer();
|
2016-02-23 11:29:05 +00:00
|
|
|
disconnect(m_serverCursor.connection);
|
2021-02-22 10:51:26 +00:00
|
|
|
|
|
|
|
if (pointer->focusedSurface()) {
|
|
|
|
m_serverCursor.connection = connect(pointer, &KWaylandServer::PointerInterface::cursorChanged,
|
|
|
|
this, &CursorImage::updateServerCursor);
|
2016-02-23 11:29:05 +00:00
|
|
|
} else {
|
|
|
|
m_serverCursor.connection = QMetaObject::Connection();
|
2018-05-01 13:33:33 +00:00
|
|
|
reevaluteSource();
|
2016-02-23 11:29:05 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void CursorImage::updateDecoration()
|
|
|
|
{
|
|
|
|
disconnect(m_decorationConnection);
|
|
|
|
auto deco = m_pointer->decoration();
|
2021-01-28 14:45:57 +00:00
|
|
|
AbstractClient *c = deco ? deco->client() : nullptr;
|
2016-02-23 11:29:05 +00:00
|
|
|
if (c) {
|
|
|
|
m_decorationConnection = connect(c, &AbstractClient::moveResizeCursorChanged, this, &CursorImage::updateDecorationCursor);
|
|
|
|
} else {
|
|
|
|
m_decorationConnection = QMetaObject::Connection();
|
|
|
|
}
|
|
|
|
updateDecorationCursor();
|
|
|
|
}
|
|
|
|
|
|
|
|
void CursorImage::updateDecorationCursor()
|
|
|
|
{
|
2020-04-02 16:18:01 +00:00
|
|
|
m_decorationCursor = {};
|
2016-02-23 11:29:05 +00:00
|
|
|
auto deco = m_pointer->decoration();
|
2021-01-28 14:45:57 +00:00
|
|
|
if (AbstractClient *c = deco ? deco->client() : nullptr) {
|
2016-02-23 11:29:05 +00:00
|
|
|
loadThemeCursor(c->cursor(), &m_decorationCursor);
|
|
|
|
if (m_currentSource == CursorSource::Decoration) {
|
2021-06-08 07:02:14 +00:00
|
|
|
Q_EMIT changed();
|
2016-02-23 11:29:05 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
reevaluteSource();
|
|
|
|
}
|
|
|
|
|
2016-02-23 13:08:28 +00:00
|
|
|
void CursorImage::updateMoveResize()
|
|
|
|
{
|
2020-04-02 16:18:01 +00:00
|
|
|
m_moveResizeCursor = {};
|
2019-04-18 12:28:11 +00:00
|
|
|
if (AbstractClient *c = workspace()->moveResizeClient()) {
|
[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
2018-12-25 15:30:38 +00:00
|
|
|
loadThemeCursor(c->cursor(), &m_moveResizeCursor);
|
2016-02-23 13:08:28 +00:00
|
|
|
if (m_currentSource == CursorSource::MoveResize) {
|
2021-06-08 07:02:14 +00:00
|
|
|
Q_EMIT changed();
|
2016-02-23 13:08:28 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
reevaluteSource();
|
|
|
|
}
|
|
|
|
|
2016-02-23 11:29:05 +00:00
|
|
|
void CursorImage::updateServerCursor()
|
|
|
|
{
|
2020-04-02 16:18:01 +00:00
|
|
|
m_serverCursor.cursor = {};
|
2016-02-23 11:29:05 +00:00
|
|
|
reevaluteSource();
|
|
|
|
const bool needsEmit = m_currentSource == CursorSource::LockScreen || m_currentSource == CursorSource::PointerSurface;
|
2021-02-22 10:51:26 +00:00
|
|
|
auto p = waylandServer()->seat()->pointer();
|
2016-02-23 11:29:05 +00:00
|
|
|
if (!p) {
|
|
|
|
if (needsEmit) {
|
2021-06-08 07:02:14 +00:00
|
|
|
Q_EMIT changed();
|
2016-02-23 11:29:05 +00:00
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
auto c = p->cursor();
|
|
|
|
if (!c) {
|
|
|
|
if (needsEmit) {
|
2021-06-08 07:02:14 +00:00
|
|
|
Q_EMIT changed();
|
2016-02-23 11:29:05 +00:00
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
auto cursorSurface = c->surface();
|
2021-02-22 10:51:26 +00:00
|
|
|
if (!cursorSurface) {
|
2016-02-23 11:29:05 +00:00
|
|
|
if (needsEmit) {
|
2021-06-08 07:02:14 +00:00
|
|
|
Q_EMIT changed();
|
2016-02-23 11:29:05 +00:00
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
2021-07-20 19:37:03 +00:00
|
|
|
auto buffer = qobject_cast<KWaylandServer::ShmClientBuffer *>(cursorSurface->buffer());
|
2016-02-23 11:29:05 +00:00
|
|
|
if (!buffer) {
|
|
|
|
if (needsEmit) {
|
2021-06-08 07:02:14 +00:00
|
|
|
Q_EMIT changed();
|
2016-02-23 11:29:05 +00:00
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
2020-04-02 16:18:01 +00:00
|
|
|
m_serverCursor.cursor.hotspot = c->hotspot();
|
|
|
|
m_serverCursor.cursor.image = buffer->data().copy();
|
2020-06-19 07:20:57 +00:00
|
|
|
m_serverCursor.cursor.image.setDevicePixelRatio(cursorSurface->bufferScale());
|
2016-02-23 11:29:05 +00:00
|
|
|
if (needsEmit) {
|
2021-06-08 07:02:14 +00:00
|
|
|
Q_EMIT changed();
|
2016-02-23 11:29:05 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void CursorImage::setEffectsOverrideCursor(Qt::CursorShape shape)
|
|
|
|
{
|
|
|
|
loadThemeCursor(shape, &m_effectsCursor);
|
|
|
|
if (m_currentSource == CursorSource::EffectsOverride) {
|
2021-06-08 07:02:14 +00:00
|
|
|
Q_EMIT changed();
|
2016-02-23 11:29:05 +00:00
|
|
|
}
|
|
|
|
reevaluteSource();
|
|
|
|
}
|
|
|
|
|
|
|
|
void CursorImage::removeEffectsOverrideCursor()
|
|
|
|
{
|
|
|
|
reevaluteSource();
|
|
|
|
}
|
|
|
|
|
2016-11-15 13:23:51 +00:00
|
|
|
void CursorImage::setWindowSelectionCursor(const QByteArray &shape)
|
|
|
|
{
|
|
|
|
if (shape.isEmpty()) {
|
|
|
|
loadThemeCursor(Qt::CrossCursor, &m_windowSelectionCursor);
|
|
|
|
} else {
|
|
|
|
loadThemeCursor(shape, &m_windowSelectionCursor);
|
|
|
|
}
|
|
|
|
if (m_currentSource == CursorSource::WindowSelector) {
|
2021-06-08 07:02:14 +00:00
|
|
|
Q_EMIT changed();
|
2016-11-15 13:23:51 +00:00
|
|
|
}
|
|
|
|
reevaluteSource();
|
|
|
|
}
|
|
|
|
|
|
|
|
void CursorImage::removeWindowSelectionCursor()
|
|
|
|
{
|
|
|
|
reevaluteSource();
|
|
|
|
}
|
|
|
|
|
2016-03-01 07:42:07 +00:00
|
|
|
void CursorImage::updateDrag()
|
|
|
|
{
|
2020-04-29 15:18:41 +00:00
|
|
|
using namespace KWaylandServer;
|
2016-03-01 07:42:07 +00:00
|
|
|
disconnect(m_drag.connection);
|
2020-04-02 16:18:01 +00:00
|
|
|
m_drag.cursor = {};
|
2016-03-01 07:42:07 +00:00
|
|
|
reevaluteSource();
|
2021-02-22 10:51:26 +00:00
|
|
|
if (waylandServer()->seat()->isDragPointer()) {
|
|
|
|
KWaylandServer::PointerInterface *pointer = waylandServer()->seat()->pointer();
|
|
|
|
m_drag.connection = connect(pointer, &PointerInterface::cursorChanged, this, &CursorImage::updateDragCursor);
|
2016-03-01 07:42:07 +00:00
|
|
|
} else {
|
|
|
|
m_drag.connection = QMetaObject::Connection();
|
|
|
|
}
|
|
|
|
updateDragCursor();
|
|
|
|
}
|
|
|
|
|
|
|
|
void CursorImage::updateDragCursor()
|
|
|
|
{
|
2020-04-02 16:18:01 +00:00
|
|
|
m_drag.cursor = {};
|
2016-03-01 07:42:07 +00:00
|
|
|
const bool needsEmit = m_currentSource == CursorSource::DragAndDrop;
|
|
|
|
QImage additionalIcon;
|
2021-09-03 11:45:17 +00:00
|
|
|
if (const KWaylandServer::DragAndDropIcon *dragIcon = waylandServer()->seat()->dragIcon()) {
|
|
|
|
if (auto buffer = qobject_cast<KWaylandServer::ShmClientBuffer *>(dragIcon->surface()->buffer())) {
|
|
|
|
additionalIcon = buffer->data().copy();
|
|
|
|
additionalIcon.setDevicePixelRatio(dragIcon->surface()->bufferScale());
|
|
|
|
additionalIcon.setOffset(dragIcon->position());
|
2016-03-01 07:42:07 +00:00
|
|
|
}
|
|
|
|
}
|
2021-02-22 10:51:26 +00:00
|
|
|
auto p = waylandServer()->seat()->pointer();
|
2016-03-01 07:42:07 +00:00
|
|
|
if (!p) {
|
|
|
|
if (needsEmit) {
|
2021-06-08 07:02:14 +00:00
|
|
|
Q_EMIT changed();
|
2016-03-01 07:42:07 +00:00
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
auto c = p->cursor();
|
|
|
|
if (!c) {
|
|
|
|
if (needsEmit) {
|
2021-06-08 07:02:14 +00:00
|
|
|
Q_EMIT changed();
|
2016-03-01 07:42:07 +00:00
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
auto cursorSurface = c->surface();
|
2021-02-22 10:51:26 +00:00
|
|
|
if (!cursorSurface) {
|
2016-03-01 07:42:07 +00:00
|
|
|
if (needsEmit) {
|
2021-06-08 07:02:14 +00:00
|
|
|
Q_EMIT changed();
|
2016-03-01 07:42:07 +00:00
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
2021-07-20 19:37:03 +00:00
|
|
|
auto buffer = qobject_cast<KWaylandServer::ShmClientBuffer *>(cursorSurface->buffer());
|
2016-03-01 07:42:07 +00:00
|
|
|
if (!buffer) {
|
|
|
|
if (needsEmit) {
|
2021-06-08 07:02:14 +00:00
|
|
|
Q_EMIT changed();
|
2016-03-01 07:42:07 +00:00
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
2020-10-24 16:07:04 +00:00
|
|
|
|
|
|
|
QImage cursorImage = buffer->data();
|
|
|
|
cursorImage.setDevicePixelRatio(cursorSurface->bufferScale());
|
2020-02-06 12:24:07 +00:00
|
|
|
|
|
|
|
if (additionalIcon.isNull()) {
|
2020-10-24 16:07:04 +00:00
|
|
|
m_drag.cursor.image = cursorImage.copy();
|
|
|
|
m_drag.cursor.hotspot = c->hotspot();
|
2020-02-06 12:24:07 +00:00
|
|
|
} else {
|
2020-10-24 16:07:04 +00:00
|
|
|
QRect cursorRect(QPoint(0, 0), cursorImage.size() / cursorImage.devicePixelRatio());
|
|
|
|
QRect iconRect(QPoint(0, 0), additionalIcon.size() / additionalIcon.devicePixelRatio());
|
2020-02-06 12:24:07 +00:00
|
|
|
|
2020-10-24 16:07:04 +00:00
|
|
|
if (-c->hotspot().x() < additionalIcon.offset().x()) {
|
|
|
|
iconRect.moveLeft(c->hotspot().x() - additionalIcon.offset().x());
|
2020-02-06 12:24:07 +00:00
|
|
|
} else {
|
2020-10-24 16:07:04 +00:00
|
|
|
cursorRect.moveLeft(-additionalIcon.offset().x() - c->hotspot().x());
|
2020-02-06 12:24:07 +00:00
|
|
|
}
|
2020-10-24 16:07:04 +00:00
|
|
|
if (-c->hotspot().y() < additionalIcon.offset().y()) {
|
|
|
|
iconRect.moveTop(c->hotspot().y() - additionalIcon.offset().y());
|
2020-02-06 12:24:07 +00:00
|
|
|
} else {
|
2020-10-24 16:07:04 +00:00
|
|
|
cursorRect.moveTop(-additionalIcon.offset().y() - c->hotspot().y());
|
2020-02-06 12:24:07 +00:00
|
|
|
}
|
|
|
|
|
2020-10-24 16:07:04 +00:00
|
|
|
const QRect viewport = cursorRect.united(iconRect);
|
|
|
|
const qreal scale = cursorSurface->bufferScale();
|
|
|
|
|
|
|
|
m_drag.cursor.image = QImage(viewport.size() * scale, QImage::Format_ARGB32_Premultiplied);
|
|
|
|
m_drag.cursor.image.setDevicePixelRatio(scale);
|
2020-02-06 12:24:07 +00:00
|
|
|
m_drag.cursor.image.fill(Qt::transparent);
|
2020-10-24 16:07:04 +00:00
|
|
|
m_drag.cursor.hotspot = cursorRect.topLeft() + c->hotspot();
|
|
|
|
|
2020-02-06 12:24:07 +00:00
|
|
|
QPainter p(&m_drag.cursor.image);
|
|
|
|
p.drawImage(iconRect, additionalIcon);
|
2020-10-24 16:07:04 +00:00
|
|
|
p.drawImage(cursorRect, cursorImage);
|
2020-02-06 12:24:07 +00:00
|
|
|
p.end();
|
|
|
|
}
|
|
|
|
|
2016-03-01 07:42:07 +00:00
|
|
|
if (needsEmit) {
|
2021-06-08 07:02:14 +00:00
|
|
|
Q_EMIT changed();
|
2016-03-01 07:42:07 +00:00
|
|
|
}
|
|
|
|
// TODO: add the cursor image
|
|
|
|
}
|
|
|
|
|
2020-04-02 16:18:01 +00:00
|
|
|
void CursorImage::loadThemeCursor(CursorShape shape, WaylandCursorImage::Image *image)
|
2016-11-15 13:23:51 +00:00
|
|
|
{
|
2020-05-18 19:45:48 +00:00
|
|
|
m_waylandImage.loadThemeCursor(shape, image);
|
2016-11-15 13:23:51 +00:00
|
|
|
}
|
|
|
|
|
2020-04-02 16:18:01 +00:00
|
|
|
void CursorImage::loadThemeCursor(const QByteArray &shape, WaylandCursorImage::Image *image)
|
2016-11-15 13:23:51 +00:00
|
|
|
{
|
2020-05-18 19:45:48 +00:00
|
|
|
m_waylandImage.loadThemeCursor(shape, image);
|
2016-11-15 13:23:51 +00:00
|
|
|
}
|
|
|
|
|
2020-05-18 19:37:41 +00:00
|
|
|
WaylandCursorImage::WaylandCursorImage(QObject *parent)
|
|
|
|
: QObject(parent)
|
2016-02-23 11:29:05 +00:00
|
|
|
{
|
2020-05-18 19:37:41 +00:00
|
|
|
Cursor *pointerCursor = Cursors::self()->mouse();
|
|
|
|
|
|
|
|
connect(pointerCursor, &Cursor::themeChanged, this, &WaylandCursorImage::invalidateCursorTheme);
|
|
|
|
connect(screens(), &Screens::maxScaleChanged, this, &WaylandCursorImage::invalidateCursorTheme);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool WaylandCursorImage::ensureCursorTheme()
|
|
|
|
{
|
|
|
|
if (!m_cursorTheme.isEmpty()) {
|
|
|
|
return true;
|
2016-02-23 11:29:05 +00:00
|
|
|
}
|
2020-04-02 16:18:01 +00:00
|
|
|
|
2020-05-18 19:37:41 +00:00
|
|
|
const Cursor *pointerCursor = Cursors::self()->mouse();
|
|
|
|
const qreal targetDevicePixelRatio = screens()->maxScale();
|
|
|
|
|
wayland: Rework Xcursor theme loading
Xcursor loading code has hardcoded search paths, in order to take into
account distros installing app data in a different location,
libwayland-cursor sets the ICONDIR to the icon directory computed based
on the install prefix.
However, that won't work with gitlab CI because it relocates binaries. A
more robust way to find cursors would be to use QStandardPaths to find
all the icon directories on the system.
Another advantage of using own cursor loading code is that it allows us
to reuse cursor images that are symlinks. For example, with
breeze_cursors, almost half of the files in the cursors directory are
symlinks.
The main disadvantage of this approach is that we would have to keep the
search paths up to date. However, on the hand, there are not that many
of them, e.g. ~/.icons, ~/.local/share/icons, /usr/share/icons,
/usr/local/share/icons. The last three are implicitly handled by the
QStandardPaths.
2022-01-27 20:47:11 +00:00
|
|
|
m_cursorTheme = KXcursorTheme(pointerCursor->themeName(), pointerCursor->themeSize(), targetDevicePixelRatio);
|
2020-05-18 19:37:41 +00:00
|
|
|
if (!m_cursorTheme.isEmpty()) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
wayland: Rework Xcursor theme loading
Xcursor loading code has hardcoded search paths, in order to take into
account distros installing app data in a different location,
libwayland-cursor sets the ICONDIR to the icon directory computed based
on the install prefix.
However, that won't work with gitlab CI because it relocates binaries. A
more robust way to find cursors would be to use QStandardPaths to find
all the icon directories on the system.
Another advantage of using own cursor loading code is that it allows us
to reuse cursor images that are symlinks. For example, with
breeze_cursors, almost half of the files in the cursors directory are
symlinks.
The main disadvantage of this approach is that we would have to keep the
search paths up to date. However, on the hand, there are not that many
of them, e.g. ~/.icons, ~/.local/share/icons, /usr/share/icons,
/usr/local/share/icons. The last three are implicitly handled by the
QStandardPaths.
2022-01-27 20:47:11 +00:00
|
|
|
m_cursorTheme = KXcursorTheme(Cursor::defaultThemeName(), Cursor::defaultThemeSize(), targetDevicePixelRatio);
|
2020-05-18 19:37:41 +00:00
|
|
|
if (!m_cursorTheme.isEmpty()) {
|
|
|
|
return true;
|
2020-04-02 16:18:01 +00:00
|
|
|
}
|
2020-05-18 19:37:41 +00:00
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
void WaylandCursorImage::invalidateCursorTheme()
|
|
|
|
{
|
|
|
|
m_cursorTheme = KXcursorTheme();
|
|
|
|
}
|
|
|
|
|
|
|
|
void WaylandCursorImage::loadThemeCursor(const CursorShape &shape, Image *cursorImage)
|
|
|
|
{
|
|
|
|
loadThemeCursor(shape.name(), cursorImage);
|
|
|
|
}
|
|
|
|
|
|
|
|
void WaylandCursorImage::loadThemeCursor(const QByteArray &name, Image *cursorImage)
|
|
|
|
{
|
|
|
|
if (!ensureCursorTheme()) {
|
2020-04-02 16:18:01 +00:00
|
|
|
return;
|
|
|
|
}
|
2020-05-18 19:37:41 +00:00
|
|
|
|
|
|
|
if (loadThemeCursor_helper(name, cursorImage)) {
|
2020-04-02 16:18:01 +00:00
|
|
|
return;
|
|
|
|
}
|
2020-05-18 19:37:41 +00:00
|
|
|
|
|
|
|
const auto alternativeNames = Cursor::cursorAlternativeNames(name);
|
|
|
|
for (const QByteArray &alternativeName : alternativeNames) {
|
|
|
|
if (loadThemeCursor_helper(alternativeName, cursorImage)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
qCWarning(KWIN_CORE) << "Failed to load theme cursor for shape" << name;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool WaylandCursorImage::loadThemeCursor_helper(const QByteArray &name, Image *cursorImage)
|
|
|
|
{
|
|
|
|
const QVector<KXcursorSprite> sprites = m_cursorTheme.shape(name);
|
|
|
|
if (sprites.isEmpty()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
cursorImage->image = sprites.first().data();
|
|
|
|
cursorImage->hotspot = sprites.first().hotspot();
|
|
|
|
|
|
|
|
return true;
|
2020-04-02 16:18:01 +00:00
|
|
|
}
|
|
|
|
|
2016-02-23 11:29:05 +00:00
|
|
|
void CursorImage::reevaluteSource()
|
|
|
|
{
|
2016-03-01 07:42:07 +00:00
|
|
|
if (waylandServer()->seat()->isDragPointer()) {
|
|
|
|
// TODO: touch drag?
|
|
|
|
setSource(CursorSource::DragAndDrop);
|
|
|
|
return;
|
|
|
|
}
|
2016-02-23 11:29:05 +00:00
|
|
|
if (waylandServer()->isScreenLocked()) {
|
|
|
|
setSource(CursorSource::LockScreen);
|
|
|
|
return;
|
|
|
|
}
|
2016-11-15 13:23:51 +00:00
|
|
|
if (input()->isSelectingWindow()) {
|
|
|
|
setSource(CursorSource::WindowSelector);
|
|
|
|
return;
|
|
|
|
}
|
2016-02-23 11:29:05 +00:00
|
|
|
if (effects && static_cast<EffectsHandlerImpl*>(effects)->isMouseInterception()) {
|
|
|
|
setSource(CursorSource::EffectsOverride);
|
|
|
|
return;
|
|
|
|
}
|
2019-04-18 12:28:11 +00:00
|
|
|
if (workspace() && workspace()->moveResizeClient()) {
|
2016-02-23 13:08:28 +00:00
|
|
|
setSource(CursorSource::MoveResize);
|
|
|
|
return;
|
|
|
|
}
|
2021-01-28 14:45:57 +00:00
|
|
|
if (m_pointer->decoration()) {
|
2016-02-23 11:29:05 +00:00
|
|
|
setSource(CursorSource::Decoration);
|
|
|
|
return;
|
|
|
|
}
|
2021-02-22 10:51:26 +00:00
|
|
|
const KWaylandServer::PointerInterface *pointer = waylandServer()->seat()->pointer();
|
|
|
|
if (pointer && pointer->focusedSurface()) {
|
2016-02-23 11:29:05 +00:00
|
|
|
setSource(CursorSource::PointerSurface);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
setSource(CursorSource::Fallback);
|
|
|
|
}
|
|
|
|
|
|
|
|
void CursorImage::setSource(CursorSource source)
|
|
|
|
{
|
|
|
|
if (m_currentSource == source) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
m_currentSource = source;
|
2021-06-08 07:02:14 +00:00
|
|
|
Q_EMIT changed();
|
2016-02-23 11:29:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
QImage CursorImage::image() const
|
|
|
|
{
|
|
|
|
switch (m_currentSource) {
|
|
|
|
case CursorSource::EffectsOverride:
|
|
|
|
return m_effectsCursor.image;
|
2016-02-23 13:08:28 +00:00
|
|
|
case CursorSource::MoveResize:
|
|
|
|
return m_moveResizeCursor.image;
|
2016-02-23 11:29:05 +00:00
|
|
|
case CursorSource::LockScreen:
|
|
|
|
case CursorSource::PointerSurface:
|
|
|
|
// lockscreen also uses server cursor image
|
2020-04-02 16:18:01 +00:00
|
|
|
return m_serverCursor.cursor.image;
|
2016-02-23 11:29:05 +00:00
|
|
|
case CursorSource::Decoration:
|
|
|
|
return m_decorationCursor.image;
|
2016-03-01 07:42:07 +00:00
|
|
|
case CursorSource::DragAndDrop:
|
|
|
|
return m_drag.cursor.image;
|
2016-02-23 11:29:05 +00:00
|
|
|
case CursorSource::Fallback:
|
|
|
|
return m_fallbackCursor.image;
|
2016-11-15 13:23:51 +00:00
|
|
|
case CursorSource::WindowSelector:
|
|
|
|
return m_windowSelectionCursor.image;
|
2016-02-23 11:29:05 +00:00
|
|
|
default:
|
|
|
|
Q_UNREACHABLE();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
QPoint CursorImage::hotSpot() const
|
|
|
|
{
|
|
|
|
switch (m_currentSource) {
|
|
|
|
case CursorSource::EffectsOverride:
|
2020-04-02 16:18:01 +00:00
|
|
|
return m_effectsCursor.hotspot;
|
2016-02-23 13:08:28 +00:00
|
|
|
case CursorSource::MoveResize:
|
2020-04-02 16:18:01 +00:00
|
|
|
return m_moveResizeCursor.hotspot;
|
2016-02-23 11:29:05 +00:00
|
|
|
case CursorSource::LockScreen:
|
|
|
|
case CursorSource::PointerSurface:
|
|
|
|
// lockscreen also uses server cursor image
|
2020-04-02 16:18:01 +00:00
|
|
|
return m_serverCursor.cursor.hotspot;
|
2016-02-23 11:29:05 +00:00
|
|
|
case CursorSource::Decoration:
|
2020-04-02 16:18:01 +00:00
|
|
|
return m_decorationCursor.hotspot;
|
2016-03-01 07:42:07 +00:00
|
|
|
case CursorSource::DragAndDrop:
|
2020-04-02 16:18:01 +00:00
|
|
|
return m_drag.cursor.hotspot;
|
2016-02-23 11:29:05 +00:00
|
|
|
case CursorSource::Fallback:
|
2020-04-02 16:18:01 +00:00
|
|
|
return m_fallbackCursor.hotspot;
|
2016-11-15 13:23:51 +00:00
|
|
|
case CursorSource::WindowSelector:
|
2020-04-02 16:18:01 +00:00
|
|
|
return m_windowSelectionCursor.hotspot;
|
2016-02-23 11:29:05 +00:00
|
|
|
default:
|
|
|
|
Q_UNREACHABLE();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-02 16:18:01 +00:00
|
|
|
InputRedirectionCursor::InputRedirectionCursor(QObject *parent)
|
|
|
|
: Cursor(parent)
|
|
|
|
, m_currentButtons(Qt::NoButton)
|
|
|
|
{
|
|
|
|
Cursors::self()->setMouse(this);
|
2020-09-23 18:39:59 +00:00
|
|
|
connect(input(), &InputRedirection::globalPointerChanged,
|
|
|
|
this, &InputRedirectionCursor::slotPosChanged);
|
|
|
|
connect(input(), &InputRedirection::pointerButtonStateChanged,
|
|
|
|
this, &InputRedirectionCursor::slotPointerButtonChanged);
|
2020-04-02 16:18:01 +00:00
|
|
|
#ifndef KCMRULES
|
|
|
|
connect(input(), &InputRedirection::keyboardModifiersChanged,
|
|
|
|
this, &InputRedirectionCursor::slotModifiersChanged);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
InputRedirectionCursor::~InputRedirectionCursor()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
void InputRedirectionCursor::doSetPos()
|
|
|
|
{
|
|
|
|
if (input()->supportsPointerWarping()) {
|
|
|
|
input()->warpPointer(currentPos());
|
|
|
|
}
|
|
|
|
slotPosChanged(input()->globalPointer());
|
2021-06-08 07:02:14 +00:00
|
|
|
Q_EMIT posChanged(currentPos());
|
2020-04-02 16:18:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void InputRedirectionCursor::slotPosChanged(const QPointF &pos)
|
|
|
|
{
|
|
|
|
const QPoint oldPos = currentPos();
|
|
|
|
updatePos(pos.toPoint());
|
2021-06-08 07:02:14 +00:00
|
|
|
Q_EMIT mouseChanged(pos.toPoint(), oldPos, m_currentButtons, m_currentButtons,
|
2020-04-02 16:18:01 +00:00
|
|
|
input()->keyboardModifiers(), input()->keyboardModifiers());
|
|
|
|
}
|
|
|
|
|
|
|
|
void InputRedirectionCursor::slotModifiersChanged(Qt::KeyboardModifiers mods, Qt::KeyboardModifiers oldMods)
|
|
|
|
{
|
2021-06-08 07:02:14 +00:00
|
|
|
Q_EMIT mouseChanged(currentPos(), currentPos(), m_currentButtons, m_currentButtons, mods, oldMods);
|
2020-04-02 16:18:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void InputRedirectionCursor::slotPointerButtonChanged()
|
|
|
|
{
|
|
|
|
const Qt::MouseButtons oldButtons = m_currentButtons;
|
|
|
|
m_currentButtons = input()->qtButtonStates();
|
|
|
|
const QPoint pos = currentPos();
|
2021-06-08 07:02:14 +00:00
|
|
|
Q_EMIT mouseChanged(pos, pos, m_currentButtons, oldButtons, input()->keyboardModifiers(), input()->keyboardModifiers());
|
2020-04-02 16:18:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void InputRedirectionCursor::doStartCursorTracking()
|
|
|
|
{
|
|
|
|
#ifndef KCMRULES
|
|
|
|
// connect(Cursors::self(), &Cursors::currentCursorChanged, this, &Cursor::cursorChanged);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
void InputRedirectionCursor::doStopCursorTracking()
|
|
|
|
{
|
|
|
|
#ifndef KCMRULES
|
|
|
|
// disconnect(kwinApp()->platform(), &Platform::cursorChanged, this, &Cursor::cursorChanged);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2016-02-12 12:30:00 +00:00
|
|
|
}
|