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 }