File indexing completed on 2024-11-10 04:57:57
0001 /* 0002 KWin - the KDE window manager 0003 This file is part of the KDE project. 0004 0005 SPDX-FileCopyrightText: 2013, 2016 Martin Gräßlin <mgraesslin@kde.org> 0006 SPDX-FileCopyrightText: 2018 Roman Gilg <subdiff@gmail.com> 0007 0008 SPDX-License-Identifier: GPL-2.0-or-later 0009 */ 0010 #include "touch_input.h" 0011 0012 #include <config-kwin.h> 0013 0014 #include "decorations/decoratedclient.h" 0015 #include "input_event_spy.h" 0016 #include "pointer_input.h" 0017 #include "wayland/seat.h" 0018 #include "wayland_server.h" 0019 #include "window.h" 0020 #include "workspace.h" 0021 // KDecoration 0022 #include <KDecoration2/Decoration> 0023 // screenlocker 0024 #if KWIN_BUILD_SCREENLOCKER 0025 #include <KScreenLocker/KsldApp> 0026 #endif 0027 // Qt 0028 #include <QHoverEvent> 0029 #include <QWindow> 0030 0031 namespace KWin 0032 { 0033 0034 TouchInputRedirection::TouchInputRedirection(InputRedirection *parent) 0035 : InputDeviceHandler(parent) 0036 { 0037 } 0038 0039 TouchInputRedirection::~TouchInputRedirection() = default; 0040 0041 void TouchInputRedirection::init() 0042 { 0043 Q_ASSERT(!inited()); 0044 waylandServer()->seat()->setHasTouch(input()->hasTouch()); 0045 connect(input(), &InputRedirection::hasTouchChanged, 0046 waylandServer()->seat(), &SeatInterface::setHasTouch); 0047 0048 setInited(true); 0049 InputDeviceHandler::init(); 0050 0051 #if KWIN_BUILD_SCREENLOCKER 0052 if (waylandServer()->hasScreenLockerIntegration()) { 0053 connect(ScreenLocker::KSldApp::self(), &ScreenLocker::KSldApp::lockStateChanged, this, [this]() { 0054 cancel(); 0055 // position doesn't matter 0056 update(); 0057 }); 0058 } 0059 #endif 0060 connect(workspace(), &QObject::destroyed, this, [this] { 0061 setInited(false); 0062 }); 0063 connect(waylandServer(), &QObject::destroyed, this, [this] { 0064 setInited(false); 0065 }); 0066 } 0067 0068 bool TouchInputRedirection::focusUpdatesBlocked() 0069 { 0070 if (m_windowUpdatedInCycle) { 0071 return true; 0072 } 0073 m_windowUpdatedInCycle = true; 0074 if (waylandServer()->seat()->isDragTouch()) { 0075 return true; 0076 } 0077 if (m_activeTouchPoints.count() > 1) { 0078 // first touch defines focus 0079 return true; 0080 } 0081 return false; 0082 } 0083 0084 bool TouchInputRedirection::positionValid() const 0085 { 0086 // we can only determine a position with at least one touch point 0087 return !m_activeTouchPoints.isEmpty(); 0088 } 0089 0090 void TouchInputRedirection::focusUpdate(Window *focusOld, Window *focusNow) 0091 { 0092 // TODO: handle pointer grab aka popups 0093 0094 if (focusOld && focusOld->isClient()) { 0095 focusOld->pointerLeaveEvent(); 0096 } 0097 disconnect(m_focusGeometryConnection); 0098 m_focusGeometryConnection = QMetaObject::Connection(); 0099 0100 if (focusNow && focusNow->isClient()) { 0101 focusNow->pointerEnterEvent(m_lastPosition); 0102 } 0103 0104 auto seat = waylandServer()->seat(); 0105 if (!focusNow || !focusNow->surface()) { 0106 seat->setFocusedTouchSurface(nullptr); 0107 return; 0108 } 0109 0110 // TODO: invalidate pointer focus? 0111 0112 // FIXME: add input transformation API to SeatInterface for touch input 0113 seat->setFocusedTouchSurface(focusNow->surface(), -1 * focusNow->inputTransformation().map(focusNow->pos()) + focusNow->pos()); 0114 m_focusGeometryConnection = connect(focusNow, &Window::frameGeometryChanged, this, [this]() { 0115 if (!focus()) { 0116 return; 0117 } 0118 auto seat = waylandServer()->seat(); 0119 if (focus()->surface() != seat->focusedTouchSurface()) { 0120 return; 0121 } 0122 seat->setFocusedTouchSurfacePosition(-1 * focus()->inputTransformation().map(focus()->pos()) + focus()->pos()); 0123 }); 0124 } 0125 0126 void TouchInputRedirection::cleanupDecoration(Decoration::DecoratedClientImpl *old, Decoration::DecoratedClientImpl *now) 0127 { 0128 // nothing to do 0129 } 0130 0131 void TouchInputRedirection::processDown(qint32 id, const QPointF &pos, std::chrono::microseconds time, InputDevice *device) 0132 { 0133 if (!inited()) { 0134 return; 0135 } 0136 m_lastPosition = pos; 0137 m_windowUpdatedInCycle = false; 0138 m_activeTouchPoints.insert(id); 0139 if (m_activeTouchPoints.count() == 1) { 0140 update(); 0141 workspace()->setActiveCursorOutput(pos); 0142 } 0143 input()->setLastInputHandler(this); 0144 input()->processSpies(std::bind(&InputEventSpy::touchDown, std::placeholders::_1, id, pos, time)); 0145 input()->processFilters(std::bind(&InputEventFilter::touchDown, std::placeholders::_1, id, pos, time)); 0146 m_windowUpdatedInCycle = false; 0147 } 0148 0149 void TouchInputRedirection::processUp(qint32 id, std::chrono::microseconds time, InputDevice *device) 0150 { 0151 if (!inited()) { 0152 return; 0153 } 0154 if (!m_activeTouchPoints.remove(id)) { 0155 return; 0156 } 0157 input()->setLastInputHandler(this); 0158 m_windowUpdatedInCycle = false; 0159 input()->processSpies(std::bind(&InputEventSpy::touchUp, std::placeholders::_1, id, time)); 0160 input()->processFilters(std::bind(&InputEventFilter::touchUp, std::placeholders::_1, id, time)); 0161 m_windowUpdatedInCycle = false; 0162 if (m_activeTouchPoints.count() == 0) { 0163 update(); 0164 } 0165 } 0166 0167 void TouchInputRedirection::processMotion(qint32 id, const QPointF &pos, std::chrono::microseconds time, InputDevice *device) 0168 { 0169 if (!inited()) { 0170 return; 0171 } 0172 if (!m_activeTouchPoints.contains(id)) { 0173 return; 0174 } 0175 input()->setLastInputHandler(this); 0176 m_lastPosition = pos; 0177 m_windowUpdatedInCycle = false; 0178 input()->processSpies(std::bind(&InputEventSpy::touchMotion, std::placeholders::_1, id, pos, time)); 0179 input()->processFilters(std::bind(&InputEventFilter::touchMotion, std::placeholders::_1, id, pos, time)); 0180 m_windowUpdatedInCycle = false; 0181 } 0182 0183 void TouchInputRedirection::cancel() 0184 { 0185 if (!inited()) { 0186 return; 0187 } 0188 // If the touch sequence is artificially cancelled by the compositor, touch motion and touch 0189 // up events will be silently ignored and won't be passed down through the event filter chain. 0190 // If the touch sequence is cancelled because we received a TOUCH_CANCEL event from libinput, 0191 // the compositor will not receive any TOUCH_MOTION or TOUCH_UP events for that slot. 0192 if (!m_activeTouchPoints.isEmpty()) { 0193 m_activeTouchPoints.clear(); 0194 input()->processFilters(std::bind(&InputEventFilter::touchCancel, std::placeholders::_1)); 0195 } 0196 } 0197 0198 void TouchInputRedirection::frame() 0199 { 0200 if (!inited() || !waylandServer()->seat()->hasTouch()) { 0201 return; 0202 } 0203 input()->processFilters(std::bind(&InputEventFilter::touchFrame, std::placeholders::_1)); 0204 } 0205 0206 } 0207 0208 #include "moc_touch_input.cpp"