File indexing completed on 2024-12-01 13:37:39
0001 /* 0002 KWin - the KDE window manager 0003 This file is part of the KDE project. 0004 0005 SPDX-FileCopyrightText: 2019 Aleix Pol Gonzalez <aleixpol@kde.org> 0006 0007 SPDX-License-Identifier: GPL-2.0-or-later 0008 */ 0009 #include "tablet_input.h" 0010 #include "decorations/decoratedclient.h" 0011 #include "input_event.h" 0012 #include "input_event_spy.h" 0013 #include "pointer_input.h" 0014 #include "wayland/seat_interface.h" 0015 #include "wayland/surface_interface.h" 0016 #include "wayland_server.h" 0017 #include "window.h" 0018 #include "workspace.h" 0019 // KDecoration 0020 #include <KDecoration2/Decoration> 0021 // Qt 0022 #include <QHoverEvent> 0023 #include <QWindow> 0024 0025 namespace KWin 0026 { 0027 TabletInputRedirection::TabletInputRedirection(InputRedirection *parent) 0028 : InputDeviceHandler(parent) 0029 { 0030 } 0031 0032 TabletInputRedirection::~TabletInputRedirection() = default; 0033 0034 void TabletInputRedirection::init() 0035 { 0036 Q_ASSERT(!inited()); 0037 setInited(true); 0038 InputDeviceHandler::init(); 0039 0040 connect(workspace(), &QObject::destroyed, this, [this] { 0041 setInited(false); 0042 }); 0043 connect(waylandServer(), &QObject::destroyed, this, [this] { 0044 setInited(false); 0045 }); 0046 } 0047 0048 void TabletInputRedirection::tabletToolEvent(KWin::InputRedirection::TabletEventType type, const QPointF &pos, 0049 qreal pressure, int xTilt, int yTilt, qreal rotation, bool tipDown, 0050 bool tipNear, const TabletToolId &tabletToolId, 0051 std::chrono::microseconds time) 0052 { 0053 if (!inited()) { 0054 return; 0055 } 0056 input()->setLastInputHandler(this); 0057 m_lastPosition = pos; 0058 0059 QEvent::Type t; 0060 switch (type) { 0061 case InputRedirection::Axis: 0062 t = QEvent::TabletMove; 0063 break; 0064 case InputRedirection::Tip: 0065 t = tipDown ? QEvent::TabletPress : QEvent::TabletRelease; 0066 break; 0067 case InputRedirection::Proximity: 0068 t = tipNear ? QEvent::TabletEnterProximity : QEvent::TabletLeaveProximity; 0069 break; 0070 } 0071 0072 update(); 0073 workspace()->setActiveCursorOutput(pos); 0074 0075 const auto button = m_tipDown ? Qt::LeftButton : Qt::NoButton; 0076 0077 #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) 0078 // TODO: Not correct, but it should work fine. In long term, we need to stop using QTabletEvent. 0079 const QPointingDevice *dev = QPointingDevice::primaryPointingDevice(); 0080 TabletEvent ev(t, dev, pos, pos, pressure, 0081 xTilt, yTilt, 0082 0, // tangentialPressure 0083 rotation, 0084 0, // z 0085 Qt::NoModifier, button, button, tabletToolId); 0086 #else 0087 TabletEvent ev(t, pos, pos, QTabletEvent::Stylus, QTabletEvent::Pen, pressure, 0088 xTilt, yTilt, 0089 0, // tangentialPressure 0090 rotation, 0091 0, // z 0092 Qt::NoModifier, tabletToolId.m_uniqueId, button, button, tabletToolId); 0093 #endif 0094 0095 ev.setTimestamp(std::chrono::duration_cast<std::chrono::milliseconds>(time).count()); 0096 input()->processSpies(std::bind(&InputEventSpy::tabletToolEvent, std::placeholders::_1, &ev)); 0097 input()->processFilters( 0098 std::bind(&InputEventFilter::tabletToolEvent, std::placeholders::_1, &ev)); 0099 0100 m_tipDown = tipDown; 0101 m_tipNear = tipNear; 0102 } 0103 0104 void KWin::TabletInputRedirection::tabletToolButtonEvent(uint button, bool isPressed, 0105 const TabletToolId &tabletToolId, std::chrono::microseconds time) 0106 { 0107 input()->processSpies(std::bind(&InputEventSpy::tabletToolButtonEvent, 0108 std::placeholders::_1, button, isPressed, tabletToolId, time)); 0109 input()->processFilters(std::bind(&InputEventFilter::tabletToolButtonEvent, 0110 std::placeholders::_1, button, isPressed, tabletToolId, time)); 0111 input()->setLastInputHandler(this); 0112 } 0113 0114 void KWin::TabletInputRedirection::tabletPadButtonEvent(uint button, bool isPressed, 0115 const TabletPadId &tabletPadId, std::chrono::microseconds time) 0116 { 0117 input()->processSpies(std::bind(&InputEventSpy::tabletPadButtonEvent, 0118 std::placeholders::_1, button, isPressed, tabletPadId, time)); 0119 input()->processFilters(std::bind(&InputEventFilter::tabletPadButtonEvent, 0120 std::placeholders::_1, button, isPressed, tabletPadId, time)); 0121 input()->setLastInputHandler(this); 0122 } 0123 0124 void KWin::TabletInputRedirection::tabletPadStripEvent(int number, int position, bool isFinger, 0125 const TabletPadId &tabletPadId, std::chrono::microseconds time) 0126 { 0127 input()->processSpies(std::bind(&InputEventSpy::tabletPadStripEvent, 0128 std::placeholders::_1, number, position, isFinger, tabletPadId, time)); 0129 input()->processFilters(std::bind(&InputEventFilter::tabletPadStripEvent, 0130 std::placeholders::_1, number, position, isFinger, tabletPadId, time)); 0131 input()->setLastInputHandler(this); 0132 } 0133 0134 void KWin::TabletInputRedirection::tabletPadRingEvent(int number, int position, bool isFinger, 0135 const TabletPadId &tabletPadId, std::chrono::microseconds time) 0136 { 0137 input()->processSpies(std::bind(&InputEventSpy::tabletPadRingEvent, 0138 std::placeholders::_1, number, position, isFinger, tabletPadId, time)); 0139 input()->processFilters(std::bind(&InputEventFilter::tabletPadRingEvent, 0140 std::placeholders::_1, number, position, isFinger, tabletPadId, time)); 0141 input()->setLastInputHandler(this); 0142 } 0143 0144 bool TabletInputRedirection::focusUpdatesBlocked() 0145 { 0146 return input()->isSelectingWindow(); 0147 } 0148 0149 void TabletInputRedirection::cleanupDecoration(Decoration::DecoratedClientImpl *old, 0150 Decoration::DecoratedClientImpl *now) 0151 { 0152 disconnect(m_decorationGeometryConnection); 0153 m_decorationGeometryConnection = QMetaObject::Connection(); 0154 0155 disconnect(m_decorationDestroyedConnection); 0156 m_decorationDestroyedConnection = QMetaObject::Connection(); 0157 0158 if (old) { 0159 // send leave event to old decoration 0160 QHoverEvent event(QEvent::HoverLeave, QPointF(), QPointF()); 0161 QCoreApplication::instance()->sendEvent(old->decoration(), &event); 0162 } 0163 if (!now) { 0164 // left decoration 0165 return; 0166 } 0167 0168 const auto pos = m_lastPosition - now->window()->pos(); 0169 QHoverEvent event(QEvent::HoverEnter, pos, pos); 0170 QCoreApplication::instance()->sendEvent(now->decoration(), &event); 0171 now->window()->processDecorationMove(pos, m_lastPosition); 0172 0173 m_decorationGeometryConnection = connect( 0174 decoration()->window(), &Window::frameGeometryChanged, this, [this]() { 0175 // ensure maximize button gets the leave event when maximizing/restore a window, see BUG 385140 0176 const auto oldDeco = decoration(); 0177 update(); 0178 if (oldDeco && oldDeco == decoration() && !decoration()->window()->isInteractiveMove() && !decoration()->window()->isInteractiveResize()) { 0179 // position of window did not change, we need to send HoverMotion manually 0180 const QPointF p = m_lastPosition - decoration()->window()->pos(); 0181 QHoverEvent event(QEvent::HoverMove, p, p); 0182 QCoreApplication::instance()->sendEvent(decoration()->decoration(), &event); 0183 } 0184 }, 0185 Qt::QueuedConnection); 0186 0187 // if our decoration gets destroyed whilst it has focus, we pass focus on to the same client 0188 m_decorationDestroyedConnection = connect(now, &QObject::destroyed, this, &TabletInputRedirection::update, Qt::QueuedConnection); 0189 } 0190 0191 void TabletInputRedirection::focusUpdate(Window *focusOld, Window *focusNow) 0192 { 0193 // This method is left blank intentionally. 0194 } 0195 0196 }