kwin/src/tablet_input.cpp
Xaver Hugl 354a71855e workspace: unify active screen modes
Instead of the active screen being purely defined by the mouse position,
or purely defined by the active window and keyboard shortcuts, this changes
it to make the active screen all about the last user interaction. This should
work for most workflows without needing a setting to choose between two flawed
approaches.

BUG: 482865
BUG: 484902
BUG: 484902
2024-04-29 14:13:32 +00:00

189 lines
7.4 KiB
C++

/*
KWin - the KDE window manager
This file is part of the KDE project.
SPDX-FileCopyrightText: 2019 Aleix Pol Gonzalez <aleixpol@kde.org>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "tablet_input.h"
#include "decorations/decoratedclient.h"
#include "input_event.h"
#include "input_event_spy.h"
#include "pointer_input.h"
#include "wayland/seat.h"
#include "wayland/surface.h"
#include "wayland_server.h"
#include "window.h"
#include "workspace.h"
// KDecoration
#include <KDecoration2/Decoration>
// Qt
#include <QHoverEvent>
#include <QWindow>
namespace KWin
{
TabletInputRedirection::TabletInputRedirection(InputRedirection *parent)
: InputDeviceHandler(parent)
{
}
TabletInputRedirection::~TabletInputRedirection() = default;
void TabletInputRedirection::init()
{
Q_ASSERT(!inited());
setInited(true);
InputDeviceHandler::init();
connect(workspace(), &QObject::destroyed, this, [this] {
setInited(false);
});
connect(waylandServer(), &QObject::destroyed, this, [this] {
setInited(false);
});
}
void TabletInputRedirection::tabletToolEvent(KWin::InputRedirection::TabletEventType type, const QPointF &pos,
qreal pressure, int xTilt, int yTilt, qreal rotation, bool tipDown,
bool tipNear, const TabletToolId &tabletToolId,
std::chrono::microseconds time)
{
if (!inited()) {
return;
}
input()->setLastInputHandler(this);
m_lastPosition = pos;
QEvent::Type t;
switch (type) {
case InputRedirection::Axis:
t = QEvent::TabletMove;
break;
case InputRedirection::Tip:
t = tipDown ? QEvent::TabletPress : QEvent::TabletRelease;
break;
case InputRedirection::Proximity:
t = tipNear ? QEvent::TabletEnterProximity : QEvent::TabletLeaveProximity;
break;
}
update();
workspace()->setActiveOutput(pos);
const auto button = m_tipDown ? Qt::LeftButton : Qt::NoButton;
// TODO: Not correct, but it should work fine. In long term, we need to stop using QTabletEvent.
const QPointingDevice *dev = QPointingDevice::primaryPointingDevice();
TabletEvent ev(t, dev, pos, pos, pressure,
xTilt, yTilt,
0, // tangentialPressure
rotation,
0, // z
Qt::NoModifier, button, button, tabletToolId);
ev.setTimestamp(std::chrono::duration_cast<std::chrono::milliseconds>(time).count());
input()->processSpies(std::bind(&InputEventSpy::tabletToolEvent, std::placeholders::_1, &ev));
input()->processFilters(
std::bind(&InputEventFilter::tabletToolEvent, std::placeholders::_1, &ev));
m_tipDown = tipDown;
m_tipNear = tipNear;
}
void KWin::TabletInputRedirection::tabletToolButtonEvent(uint button, bool isPressed,
const TabletToolId &tabletToolId, std::chrono::microseconds time)
{
input()->processSpies(std::bind(&InputEventSpy::tabletToolButtonEvent,
std::placeholders::_1, button, isPressed, tabletToolId, time));
input()->processFilters(std::bind(&InputEventFilter::tabletToolButtonEvent,
std::placeholders::_1, button, isPressed, tabletToolId, time));
input()->setLastInputHandler(this);
}
void KWin::TabletInputRedirection::tabletPadButtonEvent(uint button, bool isPressed,
const TabletPadId &tabletPadId, std::chrono::microseconds time)
{
input()->processSpies(std::bind(&InputEventSpy::tabletPadButtonEvent,
std::placeholders::_1, button, isPressed, tabletPadId, time));
input()->processFilters(std::bind(&InputEventFilter::tabletPadButtonEvent,
std::placeholders::_1, button, isPressed, tabletPadId, time));
input()->setLastInputHandler(this);
}
void KWin::TabletInputRedirection::tabletPadStripEvent(int number, int position, bool isFinger,
const TabletPadId &tabletPadId, std::chrono::microseconds time)
{
input()->processSpies(std::bind(&InputEventSpy::tabletPadStripEvent,
std::placeholders::_1, number, position, isFinger, tabletPadId, time));
input()->processFilters(std::bind(&InputEventFilter::tabletPadStripEvent,
std::placeholders::_1, number, position, isFinger, tabletPadId, time));
input()->setLastInputHandler(this);
}
void KWin::TabletInputRedirection::tabletPadRingEvent(int number, int position, bool isFinger,
const TabletPadId &tabletPadId, std::chrono::microseconds time)
{
input()->processSpies(std::bind(&InputEventSpy::tabletPadRingEvent,
std::placeholders::_1, number, position, isFinger, tabletPadId, time));
input()->processFilters(std::bind(&InputEventFilter::tabletPadRingEvent,
std::placeholders::_1, number, position, isFinger, tabletPadId, time));
input()->setLastInputHandler(this);
}
bool TabletInputRedirection::focusUpdatesBlocked()
{
return input()->isSelectingWindow();
}
void TabletInputRedirection::cleanupDecoration(Decoration::DecoratedClientImpl *old,
Decoration::DecoratedClientImpl *now)
{
disconnect(m_decorationGeometryConnection);
m_decorationGeometryConnection = QMetaObject::Connection();
disconnect(m_decorationDestroyedConnection);
m_decorationDestroyedConnection = QMetaObject::Connection();
if (old) {
// send leave event to old decoration
QHoverEvent event(QEvent::HoverLeave, QPointF(), QPointF());
QCoreApplication::instance()->sendEvent(old->decoration(), &event);
}
if (!now) {
// left decoration
return;
}
const auto pos = m_lastPosition - now->window()->pos();
QHoverEvent event(QEvent::HoverEnter, pos, pos);
QCoreApplication::instance()->sendEvent(now->decoration(), &event);
now->window()->processDecorationMove(pos, m_lastPosition);
m_decorationGeometryConnection = connect(
decoration()->window(), &Window::frameGeometryChanged, this, [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() && !decoration()->window()->isInteractiveMove() && !decoration()->window()->isInteractiveResize()) {
// position of window did not change, we need to send HoverMotion manually
const QPointF p = m_lastPosition - decoration()->window()->pos();
QHoverEvent event(QEvent::HoverMove, p, p);
QCoreApplication::instance()->sendEvent(decoration()->decoration(), &event);
}
},
Qt::QueuedConnection);
// if our decoration gets destroyed whilst it has focus, we pass focus on to the same client
m_decorationDestroyedConnection = connect(now, &QObject::destroyed, this, &TabletInputRedirection::update, Qt::QueuedConnection);
}
void TabletInputRedirection::focusUpdate(Window *focusOld, Window *focusNow)
{
// This method is left blank intentionally.
}
}
#include "moc_tablet_input.cpp"