41237ea30b
Currently, it's possible to have the case where the pointer input device handler or the touch input device handler thinks that there's a focused window, but the corresponding focused surface in wayland seat is unset, because the pointer hovers the server side decoration. If the server side decoration is destroyed, the input device handler will fail to update wayland seat's focused surface. In order to make pointer input device handler and touch input device handler code more intuitive, this change makes focusUpdate() functions ignore the decoration. BUG: 411884 BUG: 440380
206 lines
6.2 KiB
C++
206 lines
6.2 KiB
C++
/*
|
|
KWin - the KDE window manager
|
|
This file is part of the KDE project.
|
|
|
|
SPDX-FileCopyrightText: 2013, 2016 Martin Gräßlin <mgraesslin@kde.org>
|
|
SPDX-FileCopyrightText: 2018 Roman Gilg <subdiff@gmail.com>
|
|
|
|
SPDX-License-Identifier: GPL-2.0-or-later
|
|
*/
|
|
#include "touch_input.h"
|
|
#include "abstract_client.h"
|
|
#include "pointer_input.h"
|
|
#include "input_event_spy.h"
|
|
#include "toplevel.h"
|
|
#include "wayland_server.h"
|
|
#include "workspace.h"
|
|
#include "decorations/decoratedclient.h"
|
|
// KDecoration
|
|
#include <KDecoration2/Decoration>
|
|
// KWayland
|
|
#include <KWaylandServer/seat_interface.h>
|
|
// screenlocker
|
|
#include <KScreenLocker/KsldApp>
|
|
// Qt
|
|
#include <QHoverEvent>
|
|
#include <QWindow>
|
|
|
|
namespace KWin
|
|
{
|
|
|
|
TouchInputRedirection::TouchInputRedirection(InputRedirection *parent)
|
|
: InputDeviceHandler(parent)
|
|
{
|
|
}
|
|
|
|
TouchInputRedirection::~TouchInputRedirection() = default;
|
|
|
|
void TouchInputRedirection::init()
|
|
{
|
|
Q_ASSERT(!inited());
|
|
waylandServer()->seat()->setHasTouch(input()->hasTouch());
|
|
connect(input(), &InputRedirection::hasTouchChanged,
|
|
waylandServer()->seat(), &KWaylandServer::SeatInterface::setHasTouch);
|
|
|
|
setInited(true);
|
|
InputDeviceHandler::init();
|
|
|
|
if (waylandServer()->hasScreenLockerIntegration()) {
|
|
connect(ScreenLocker::KSldApp::self(), &ScreenLocker::KSldApp::lockStateChanged, this,
|
|
[this] {
|
|
cancel();
|
|
// position doesn't matter
|
|
update();
|
|
}
|
|
);
|
|
}
|
|
connect(workspace(), &QObject::destroyed, this, [this] { setInited(false); });
|
|
connect(waylandServer(), &QObject::destroyed, this, [this] { setInited(false); });
|
|
}
|
|
|
|
bool TouchInputRedirection::focusUpdatesBlocked()
|
|
{
|
|
if (m_windowUpdatedInCycle) {
|
|
return true;
|
|
}
|
|
m_windowUpdatedInCycle = true;
|
|
if (waylandServer()->seat()->isDragTouch()) {
|
|
return true;
|
|
}
|
|
if (m_activeTouchPoints.count() > 1) {
|
|
// first touch defines focus
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool TouchInputRedirection::positionValid() const
|
|
{
|
|
// we can only determine a position with at least one touch point
|
|
return !m_activeTouchPoints.isEmpty();
|
|
}
|
|
|
|
void TouchInputRedirection::focusUpdate(Toplevel *focusOld, Toplevel *focusNow)
|
|
{
|
|
// TODO: handle pointer grab aka popups
|
|
|
|
if (AbstractClient *ac = qobject_cast<AbstractClient*>(focusOld)) {
|
|
ac->pointerLeaveEvent();
|
|
}
|
|
disconnect(m_focusGeometryConnection);
|
|
m_focusGeometryConnection = QMetaObject::Connection();
|
|
|
|
if (AbstractClient *ac = qobject_cast<AbstractClient*>(focusNow)) {
|
|
ac->pointerEnterEvent(m_lastPosition.toPoint());
|
|
}
|
|
|
|
auto seat = waylandServer()->seat();
|
|
if (!focusNow || !focusNow->surface()) {
|
|
seat->setFocusedTouchSurface(nullptr);
|
|
return;
|
|
}
|
|
|
|
// TODO: invalidate pointer focus?
|
|
|
|
// FIXME: add input transformation API to KWaylandServer::SeatInterface for touch input
|
|
seat->setFocusedTouchSurface(focusNow->surface(), -1 * focusNow->inputTransformation().map(focusNow->pos()) + focusNow->pos());
|
|
m_focusGeometryConnection = connect(focusNow, &Toplevel::frameGeometryChanged, this,
|
|
[this] {
|
|
if (!focus()) {
|
|
return;
|
|
}
|
|
auto seat = waylandServer()->seat();
|
|
if (focus()->surface() != seat->focusedTouchSurface()) {
|
|
return;
|
|
}
|
|
seat->setFocusedTouchSurfacePosition(-1 * focus()->inputTransformation().map(focus()->pos()) + focus()->pos());
|
|
}
|
|
);
|
|
}
|
|
|
|
void TouchInputRedirection::cleanupDecoration(Decoration::DecoratedClientImpl *old, Decoration::DecoratedClientImpl *now)
|
|
{
|
|
Q_UNUSED(old);
|
|
Q_UNUSED(now);
|
|
|
|
// nothing to do
|
|
}
|
|
|
|
void TouchInputRedirection::processDown(qint32 id, const QPointF &pos, quint32 time, InputDevice *device)
|
|
{
|
|
Q_UNUSED(device)
|
|
if (!inited()) {
|
|
return;
|
|
}
|
|
m_lastPosition = pos;
|
|
m_windowUpdatedInCycle = false;
|
|
m_activeTouchPoints.insert(id);
|
|
if (m_activeTouchPoints.count() == 1) {
|
|
update();
|
|
}
|
|
m_lastEventTime = time;
|
|
input()->processSpies(std::bind(&InputEventSpy::touchDown, std::placeholders::_1, id, pos, time));
|
|
input()->processFilters(std::bind(&InputEventFilter::touchDown, std::placeholders::_1, id, pos, time));
|
|
m_windowUpdatedInCycle = false;
|
|
}
|
|
|
|
void TouchInputRedirection::processUp(qint32 id, quint32 time, InputDevice *device)
|
|
{
|
|
Q_UNUSED(device)
|
|
if (!inited()) {
|
|
return;
|
|
}
|
|
if (!m_activeTouchPoints.remove(id)) {
|
|
return;
|
|
}
|
|
m_lastEventTime = time;
|
|
m_windowUpdatedInCycle = false;
|
|
input()->processSpies(std::bind(&InputEventSpy::touchUp, std::placeholders::_1, id, time));
|
|
input()->processFilters(std::bind(&InputEventFilter::touchUp, std::placeholders::_1, id, time));
|
|
m_windowUpdatedInCycle = false;
|
|
if (m_activeTouchPoints.count() == 0) {
|
|
update();
|
|
}
|
|
}
|
|
|
|
void TouchInputRedirection::processMotion(qint32 id, const QPointF &pos, quint32 time, InputDevice *device)
|
|
{
|
|
Q_UNUSED(device)
|
|
if (!inited()) {
|
|
return;
|
|
}
|
|
if (!m_activeTouchPoints.contains(id)) {
|
|
return;
|
|
}
|
|
m_lastEventTime = time;
|
|
m_lastPosition = pos;
|
|
m_windowUpdatedInCycle = false;
|
|
input()->processSpies(std::bind(&InputEventSpy::touchMotion, std::placeholders::_1, id, pos, time));
|
|
input()->processFilters(std::bind(&InputEventFilter::touchMotion, std::placeholders::_1, id, pos, time));
|
|
m_windowUpdatedInCycle = false;
|
|
}
|
|
|
|
void TouchInputRedirection::cancel()
|
|
{
|
|
if (!inited()) {
|
|
return;
|
|
}
|
|
// If the touch sequence is artificially cancelled by the compositor, touch motion and touch
|
|
// up events will be silently ignored and won't be passed down through the event filter chain.
|
|
// If the touch sequence is cancelled because we received a TOUCH_CANCEL event from libinput,
|
|
// the compositor will not receive any TOUCH_MOTION or TOUCH_UP events for that slot.
|
|
if (!m_activeTouchPoints.isEmpty()) {
|
|
m_activeTouchPoints.clear();
|
|
waylandServer()->seat()->notifyTouchCancel();
|
|
}
|
|
}
|
|
|
|
void TouchInputRedirection::frame()
|
|
{
|
|
if (!inited() || !waylandServer()->seat()->hasTouch()) {
|
|
return;
|
|
}
|
|
waylandServer()->seat()->notifyTouchFrame();
|
|
}
|
|
|
|
}
|