File indexing completed on 2024-04-21 05:30:27

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.h"
0015 #include "wayland/surface.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     // TODO: Not correct, but it should work fine. In long term, we need to stop using QTabletEvent.
0078     const QPointingDevice *dev = QPointingDevice::primaryPointingDevice();
0079     TabletEvent ev(t, dev, pos, pos, pressure,
0080                    xTilt, yTilt,
0081                    0, // tangentialPressure
0082                    rotation,
0083                    0, // z
0084                    Qt::NoModifier, button, button, tabletToolId);
0085 
0086     ev.setTimestamp(std::chrono::duration_cast<std::chrono::milliseconds>(time).count());
0087     input()->processSpies(std::bind(&InputEventSpy::tabletToolEvent, std::placeholders::_1, &ev));
0088     input()->processFilters(
0089         std::bind(&InputEventFilter::tabletToolEvent, std::placeholders::_1, &ev));
0090 
0091     m_tipDown = tipDown;
0092     m_tipNear = tipNear;
0093 }
0094 
0095 void KWin::TabletInputRedirection::tabletToolButtonEvent(uint button, bool isPressed,
0096                                                          const TabletToolId &tabletToolId, std::chrono::microseconds time)
0097 {
0098     input()->processSpies(std::bind(&InputEventSpy::tabletToolButtonEvent,
0099                                     std::placeholders::_1, button, isPressed, tabletToolId, time));
0100     input()->processFilters(std::bind(&InputEventFilter::tabletToolButtonEvent,
0101                                       std::placeholders::_1, button, isPressed, tabletToolId, time));
0102     input()->setLastInputHandler(this);
0103 }
0104 
0105 void KWin::TabletInputRedirection::tabletPadButtonEvent(uint button, bool isPressed,
0106                                                         const TabletPadId &tabletPadId, std::chrono::microseconds time)
0107 {
0108     input()->processSpies(std::bind(&InputEventSpy::tabletPadButtonEvent,
0109                                     std::placeholders::_1, button, isPressed, tabletPadId, time));
0110     input()->processFilters(std::bind(&InputEventFilter::tabletPadButtonEvent,
0111                                       std::placeholders::_1, button, isPressed, tabletPadId, time));
0112     input()->setLastInputHandler(this);
0113 }
0114 
0115 void KWin::TabletInputRedirection::tabletPadStripEvent(int number, int position, bool isFinger,
0116                                                        const TabletPadId &tabletPadId, std::chrono::microseconds time)
0117 {
0118     input()->processSpies(std::bind(&InputEventSpy::tabletPadStripEvent,
0119                                     std::placeholders::_1, number, position, isFinger, tabletPadId, time));
0120     input()->processFilters(std::bind(&InputEventFilter::tabletPadStripEvent,
0121                                       std::placeholders::_1, number, position, isFinger, tabletPadId, time));
0122     input()->setLastInputHandler(this);
0123 }
0124 
0125 void KWin::TabletInputRedirection::tabletPadRingEvent(int number, int position, bool isFinger,
0126                                                       const TabletPadId &tabletPadId, std::chrono::microseconds time)
0127 {
0128     input()->processSpies(std::bind(&InputEventSpy::tabletPadRingEvent,
0129                                     std::placeholders::_1, number, position, isFinger, tabletPadId, time));
0130     input()->processFilters(std::bind(&InputEventFilter::tabletPadRingEvent,
0131                                       std::placeholders::_1, number, position, isFinger, tabletPadId, time));
0132     input()->setLastInputHandler(this);
0133 }
0134 
0135 bool TabletInputRedirection::focusUpdatesBlocked()
0136 {
0137     return input()->isSelectingWindow();
0138 }
0139 
0140 void TabletInputRedirection::cleanupDecoration(Decoration::DecoratedClientImpl *old,
0141                                                Decoration::DecoratedClientImpl *now)
0142 {
0143     disconnect(m_decorationGeometryConnection);
0144     m_decorationGeometryConnection = QMetaObject::Connection();
0145 
0146     disconnect(m_decorationDestroyedConnection);
0147     m_decorationDestroyedConnection = QMetaObject::Connection();
0148 
0149     if (old) {
0150         // send leave event to old decoration
0151         QHoverEvent event(QEvent::HoverLeave, QPointF(), QPointF());
0152         QCoreApplication::instance()->sendEvent(old->decoration(), &event);
0153     }
0154     if (!now) {
0155         // left decoration
0156         return;
0157     }
0158 
0159     const auto pos = m_lastPosition - now->window()->pos();
0160     QHoverEvent event(QEvent::HoverEnter, pos, pos);
0161     QCoreApplication::instance()->sendEvent(now->decoration(), &event);
0162     now->window()->processDecorationMove(pos, m_lastPosition);
0163 
0164     m_decorationGeometryConnection = connect(
0165         decoration()->window(), &Window::frameGeometryChanged, this, [this]() {
0166             // ensure maximize button gets the leave event when maximizing/restore a window, see BUG 385140
0167             const auto oldDeco = decoration();
0168             update();
0169             if (oldDeco && oldDeco == decoration() && !decoration()->window()->isInteractiveMove() && !decoration()->window()->isInteractiveResize()) {
0170                 // position of window did not change, we need to send HoverMotion manually
0171                 const QPointF p = m_lastPosition - decoration()->window()->pos();
0172                 QHoverEvent event(QEvent::HoverMove, p, p);
0173                 QCoreApplication::instance()->sendEvent(decoration()->decoration(), &event);
0174             }
0175         },
0176         Qt::QueuedConnection);
0177 
0178     // if our decoration gets destroyed whilst it has focus, we pass focus on to the same client
0179     m_decorationDestroyedConnection = connect(now, &QObject::destroyed, this, &TabletInputRedirection::update, Qt::QueuedConnection);
0180 }
0181 
0182 void TabletInputRedirection::focusUpdate(Window *focusOld, Window *focusNow)
0183 {
0184     // This method is left blank intentionally.
0185 }
0186 
0187 }
0188 
0189 #include "moc_tablet_input.cpp"