File indexing completed on 2024-04-28 05:30:19

0001 /*
0002     KWin - the KDE window manager
0003     This file is part of the KDE project.
0004 
0005     SPDX-FileCopyrightText: 2013 Martin Gräßlin <mgraesslin@kde.org>
0006     SPDX-FileCopyrightText: 2018 Roman Gilg <subdiff@gmail.com>
0007     SPDX-FileCopyrightText: 2019 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
0008 
0009     SPDX-License-Identifier: GPL-2.0-or-later
0010 */
0011 #include "input.h"
0012 
0013 #include "backends/fakeinput/fakeinputbackend.h"
0014 #include "backends/libinput/connection.h"
0015 #include "backends/libinput/device.h"
0016 #include "core/inputbackend.h"
0017 #include "core/session.h"
0018 #include "effect/effecthandler.h"
0019 #include "gestures.h"
0020 #include "globalshortcuts.h"
0021 #include "hide_cursor_spy.h"
0022 #include "idledetector.h"
0023 #include "input_event.h"
0024 #include "input_event_spy.h"
0025 #include "inputmethod.h"
0026 #include "keyboard_input.h"
0027 #include "main.h"
0028 #include "mousebuttons.h"
0029 #include "pointer_input.h"
0030 #include "tablet_input.h"
0031 #include "touch_input.h"
0032 #include "wayland/xdgtopleveldrag_v1.h"
0033 #include "x11window.h"
0034 #if KWIN_BUILD_TABBOX
0035 #include "tabbox/tabbox.h"
0036 #endif
0037 #include "core/output.h"
0038 #include "core/outputbackend.h"
0039 #include "cursor.h"
0040 #include "cursorsource.h"
0041 #include "internalwindow.h"
0042 #include "popup_input_filter.h"
0043 #include "screenedge.h"
0044 #include "virtualdesktops.h"
0045 #include "wayland/display.h"
0046 #include "wayland/inputmethod_v1.h"
0047 #include "wayland/seat.h"
0048 #include "wayland/surface.h"
0049 #include "wayland/tablet_v2.h"
0050 #include "wayland_server.h"
0051 #include "workspace.h"
0052 #include "xkb.h"
0053 #include "xwayland/xwayland_interface.h"
0054 
0055 #include <KDecoration2/Decoration>
0056 #include <KGlobalAccel>
0057 #include <KLocalizedString>
0058 #include <decorations/decoratedclient.h>
0059 
0060 // screenlocker
0061 #if KWIN_BUILD_SCREENLOCKER
0062 #include <KScreenLocker/KsldApp>
0063 #endif
0064 // Qt
0065 #include <QAction>
0066 #include <QDBusConnection>
0067 #include <QDBusMessage>
0068 #include <QDBusPendingCall>
0069 #include <QKeyEvent>
0070 #include <QThread>
0071 #include <qpa/qwindowsysteminterface.h>
0072 
0073 #include <xkbcommon/xkbcommon.h>
0074 
0075 #include "osd.h"
0076 #include "wayland/xdgshell.h"
0077 #include <cmath>
0078 
0079 using namespace std::literals;
0080 
0081 namespace KWin
0082 {
0083 
0084 static PointerAxisSource kwinAxisSourceToKWaylandAxisSource(InputRedirection::PointerAxisSource source)
0085 {
0086     switch (source) {
0087     case InputRedirection::PointerAxisSourceWheel:
0088         return PointerAxisSource::Wheel;
0089     case InputRedirection::PointerAxisSourceFinger:
0090         return PointerAxisSource::Finger;
0091     case InputRedirection::PointerAxisSourceContinuous:
0092         return PointerAxisSource::Continuous;
0093     case InputRedirection::PointerAxisSourceWheelTilt:
0094         return PointerAxisSource::WheelTilt;
0095     case InputRedirection::PointerAxisSourceUnknown:
0096     default:
0097         return PointerAxisSource::Unknown;
0098     }
0099 }
0100 
0101 InputEventFilter::InputEventFilter() = default;
0102 
0103 InputEventFilter::~InputEventFilter()
0104 {
0105     if (input()) {
0106         input()->uninstallInputEventFilter(this);
0107     }
0108 }
0109 
0110 bool InputEventFilter::pointerEvent(MouseEvent *event, quint32 nativeButton)
0111 {
0112     return false;
0113 }
0114 
0115 bool InputEventFilter::pointerFrame()
0116 {
0117     return false;
0118 }
0119 
0120 bool InputEventFilter::wheelEvent(WheelEvent *event)
0121 {
0122     return false;
0123 }
0124 
0125 bool InputEventFilter::keyEvent(KeyEvent *event)
0126 {
0127     return false;
0128 }
0129 
0130 bool InputEventFilter::touchDown(qint32 id, const QPointF &point, std::chrono::microseconds time)
0131 {
0132     return false;
0133 }
0134 
0135 bool InputEventFilter::touchMotion(qint32 id, const QPointF &point, std::chrono::microseconds time)
0136 {
0137     return false;
0138 }
0139 
0140 bool InputEventFilter::touchUp(qint32 id, std::chrono::microseconds time)
0141 {
0142     return false;
0143 }
0144 
0145 bool InputEventFilter::touchCancel()
0146 {
0147     return false;
0148 }
0149 
0150 bool InputEventFilter::touchFrame()
0151 {
0152     return false;
0153 }
0154 
0155 bool InputEventFilter::pinchGestureBegin(int fingerCount, std::chrono::microseconds time)
0156 {
0157     return false;
0158 }
0159 
0160 bool InputEventFilter::pinchGestureUpdate(qreal scale, qreal angleDelta, const QPointF &delta, std::chrono::microseconds time)
0161 {
0162     return false;
0163 }
0164 
0165 bool InputEventFilter::pinchGestureEnd(std::chrono::microseconds time)
0166 {
0167     return false;
0168 }
0169 
0170 bool InputEventFilter::pinchGestureCancelled(std::chrono::microseconds time)
0171 {
0172     return false;
0173 }
0174 
0175 bool InputEventFilter::swipeGestureBegin(int fingerCount, std::chrono::microseconds time)
0176 {
0177     return false;
0178 }
0179 
0180 bool InputEventFilter::swipeGestureUpdate(const QPointF &delta, std::chrono::microseconds time)
0181 {
0182     return false;
0183 }
0184 
0185 bool InputEventFilter::swipeGestureEnd(std::chrono::microseconds time)
0186 {
0187     return false;
0188 }
0189 
0190 bool InputEventFilter::swipeGestureCancelled(std::chrono::microseconds time)
0191 {
0192     return false;
0193 }
0194 
0195 bool InputEventFilter::holdGestureBegin(int fingerCount, std::chrono::microseconds time)
0196 {
0197     return false;
0198 }
0199 
0200 bool InputEventFilter::holdGestureEnd(std::chrono::microseconds time)
0201 {
0202     return false;
0203 }
0204 
0205 bool InputEventFilter::holdGestureCancelled(std::chrono::microseconds time)
0206 {
0207     return false;
0208 }
0209 
0210 bool InputEventFilter::switchEvent(SwitchEvent *event)
0211 {
0212     return false;
0213 }
0214 
0215 bool InputEventFilter::tabletToolEvent(TabletEvent *event)
0216 {
0217     return false;
0218 }
0219 
0220 bool InputEventFilter::tabletToolButtonEvent(uint button, bool pressed, const TabletToolId &tabletId, std::chrono::microseconds time)
0221 {
0222     return false;
0223 }
0224 
0225 bool InputEventFilter::tabletPadButtonEvent(uint button, bool pressed, const TabletPadId &tabletPadId, std::chrono::microseconds time)
0226 {
0227     return false;
0228 }
0229 
0230 bool InputEventFilter::tabletPadStripEvent(int number, int position, bool isFinger, const TabletPadId &tabletPadId, std::chrono::microseconds time)
0231 {
0232     return false;
0233 }
0234 
0235 bool InputEventFilter::tabletPadRingEvent(int number, int position, bool isFinger, const TabletPadId &tabletPadId, std::chrono::microseconds time)
0236 {
0237     return false;
0238 }
0239 
0240 void InputEventFilter::passToWaylandServer(QKeyEvent *event)
0241 {
0242     Q_ASSERT(waylandServer());
0243     if (event->isAutoRepeat()) {
0244         return;
0245     }
0246 
0247     SeatInterface *seat = waylandServer()->seat();
0248     const int keyCode = event->nativeScanCode();
0249     switch (event->type()) {
0250     case QEvent::KeyPress:
0251         seat->notifyKeyboardKey(keyCode, KeyboardKeyState::Pressed);
0252         break;
0253     case QEvent::KeyRelease:
0254         seat->notifyKeyboardKey(keyCode, KeyboardKeyState::Released);
0255         break;
0256     default:
0257         break;
0258     }
0259 }
0260 
0261 bool InputEventFilter::passToInputMethod(QKeyEvent *event)
0262 {
0263     if (!kwinApp()->inputMethod()) {
0264         return false;
0265     }
0266     if (auto keyboardGrab = kwinApp()->inputMethod()->keyboardGrab()) {
0267         if (event->isAutoRepeat()) {
0268             return true;
0269         }
0270         auto newState = event->type() == QEvent::KeyPress ? KeyboardKeyState::Pressed : KeyboardKeyState::Released;
0271         keyboardGrab->sendKey(waylandServer()->display()->nextSerial(), event->timestamp(), event->nativeScanCode(), newState);
0272         return true;
0273     } else {
0274         return false;
0275     }
0276 }
0277 
0278 class VirtualTerminalFilter : public InputEventFilter
0279 {
0280 public:
0281     bool keyEvent(KeyEvent *event) override
0282     {
0283         // really on press and not on release? X11 switches on press.
0284         if (event->type() == QEvent::KeyPress && !event->isAutoRepeat()) {
0285             const xkb_keysym_t keysym = event->nativeVirtualKey();
0286             if (keysym >= XKB_KEY_XF86Switch_VT_1 && keysym <= XKB_KEY_XF86Switch_VT_12) {
0287                 kwinApp()->session()->switchTo(keysym - XKB_KEY_XF86Switch_VT_1 + 1);
0288                 return true;
0289             }
0290         }
0291         return false;
0292     }
0293 };
0294 
0295 class LockScreenFilter : public InputEventFilter
0296 {
0297 public:
0298     bool pointerEvent(MouseEvent *event, quint32 nativeButton) override
0299     {
0300         if (!waylandServer()->isScreenLocked()) {
0301             return false;
0302         }
0303 
0304         auto window = input()->findToplevel(event->globalPosition());
0305         if (window && window->isClient() && window->isLockScreen()) {
0306             workspace()->activateWindow(window);
0307         }
0308 
0309         auto seat = waylandServer()->seat();
0310         seat->setTimestamp(event->timestamp());
0311         if (event->type() == QEvent::MouseMove) {
0312             if (pointerSurfaceAllowed()) {
0313                 // TODO: should the pointer position always stay in sync, i.e. not do the check?
0314                 seat->notifyPointerMotion(event->screenPos());
0315             }
0316         } else if (event->type() == QEvent::MouseButtonPress || event->type() == QEvent::MouseButtonRelease) {
0317             if (pointerSurfaceAllowed()) {
0318                 // TODO: can we leak presses/releases here when we move the mouse in between from an allowed surface to
0319                 //       disallowed one or vice versa?
0320                 const auto state = event->type() == QEvent::MouseButtonPress
0321                     ? PointerButtonState::Pressed
0322                     : PointerButtonState::Released;
0323                 seat->notifyPointerButton(nativeButton, state);
0324             }
0325         }
0326         return true;
0327     }
0328     bool pointerFrame() override
0329     {
0330         if (!waylandServer()->isScreenLocked()) {
0331             return false;
0332         }
0333         auto seat = waylandServer()->seat();
0334         if (pointerSurfaceAllowed()) {
0335             seat->notifyPointerFrame();
0336         }
0337         return true;
0338     }
0339     bool wheelEvent(WheelEvent *event) override
0340     {
0341         if (!waylandServer()->isScreenLocked()) {
0342             return false;
0343         }
0344         auto seat = waylandServer()->seat();
0345         if (pointerSurfaceAllowed()) {
0346             const WheelEvent *wheelEvent = static_cast<WheelEvent *>(event);
0347             seat->setTimestamp(wheelEvent->timestamp());
0348             seat->notifyPointerAxis(wheelEvent->orientation(), wheelEvent->delta(),
0349                                     wheelEvent->deltaV120(),
0350                                     kwinAxisSourceToKWaylandAxisSource(wheelEvent->axisSource()),
0351                                     wheelEvent->inverted() ? PointerAxisRelativeDirection::Inverted : PointerAxisRelativeDirection::Normal);
0352         }
0353         return true;
0354     }
0355     bool keyEvent(KeyEvent *event) override
0356     {
0357         if (!waylandServer()->isScreenLocked()) {
0358             return false;
0359         }
0360         if (event->isAutoRepeat()) {
0361             // wayland client takes care of it
0362             return true;
0363         }
0364         // send event to KSldApp for global accel
0365         // if event is set to accepted it means a whitelisted shortcut was triggered
0366         // in that case we filter it out and don't process it further
0367 #if KWIN_BUILD_SCREENLOCKER
0368         event->setAccepted(false);
0369         QCoreApplication::sendEvent(ScreenLocker::KSldApp::self(), event);
0370         if (event->isAccepted()) {
0371             return true;
0372         }
0373 #endif
0374 
0375         // continue normal processing
0376         input()->keyboard()->update();
0377         auto seat = waylandServer()->seat();
0378         seat->setTimestamp(event->timestamp());
0379         if (!keyboardSurfaceAllowed()) {
0380             // don't pass event to seat
0381             return true;
0382         }
0383         switch (event->type()) {
0384         case QEvent::KeyPress:
0385             seat->notifyKeyboardKey(event->nativeScanCode(), KeyboardKeyState::Pressed);
0386             break;
0387         case QEvent::KeyRelease:
0388             seat->notifyKeyboardKey(event->nativeScanCode(), KeyboardKeyState::Released);
0389             break;
0390         default:
0391             break;
0392         }
0393         return true;
0394     }
0395     bool touchDown(qint32 id, const QPointF &pos, std::chrono::microseconds time) override
0396     {
0397         if (!waylandServer()->isScreenLocked()) {
0398             return false;
0399         }
0400         auto seat = waylandServer()->seat();
0401         seat->setTimestamp(time);
0402         if (touchSurfaceAllowed()) {
0403             seat->notifyTouchDown(id, pos);
0404         }
0405         return true;
0406     }
0407     bool touchMotion(qint32 id, const QPointF &pos, std::chrono::microseconds time) override
0408     {
0409         if (!waylandServer()->isScreenLocked()) {
0410             return false;
0411         }
0412         auto seat = waylandServer()->seat();
0413         seat->setTimestamp(time);
0414         if (touchSurfaceAllowed()) {
0415             seat->notifyTouchMotion(id, pos);
0416         }
0417         return true;
0418     }
0419     bool touchUp(qint32 id, std::chrono::microseconds time) override
0420     {
0421         if (!waylandServer()->isScreenLocked()) {
0422             return false;
0423         }
0424         auto seat = waylandServer()->seat();
0425         seat->setTimestamp(time);
0426         if (touchSurfaceAllowed()) {
0427             seat->notifyTouchUp(id);
0428         }
0429         return true;
0430     }
0431     bool pinchGestureBegin(int fingerCount, std::chrono::microseconds time) override
0432     {
0433         // no touchpad multi-finger gestures on lock screen
0434         return waylandServer()->isScreenLocked();
0435     }
0436     bool pinchGestureUpdate(qreal scale, qreal angleDelta, const QPointF &delta, std::chrono::microseconds time) override
0437     {
0438         // no touchpad multi-finger gestures on lock screen
0439         return waylandServer()->isScreenLocked();
0440     }
0441     bool pinchGestureEnd(std::chrono::microseconds time) override
0442     {
0443         // no touchpad multi-finger gestures on lock screen
0444         return waylandServer()->isScreenLocked();
0445     }
0446     bool pinchGestureCancelled(std::chrono::microseconds time) override
0447     {
0448         // no touchpad multi-finger gestures on lock screen
0449         return waylandServer()->isScreenLocked();
0450     }
0451 
0452     bool swipeGestureBegin(int fingerCount, std::chrono::microseconds time) override
0453     {
0454         // no touchpad multi-finger gestures on lock screen
0455         return waylandServer()->isScreenLocked();
0456     }
0457     bool swipeGestureUpdate(const QPointF &delta, std::chrono::microseconds time) override
0458     {
0459         // no touchpad multi-finger gestures on lock screen
0460         return waylandServer()->isScreenLocked();
0461     }
0462     bool swipeGestureEnd(std::chrono::microseconds time) override
0463     {
0464         // no touchpad multi-finger gestures on lock screen
0465         return waylandServer()->isScreenLocked();
0466     }
0467     bool swipeGestureCancelled(std::chrono::microseconds time) override
0468     {
0469         // no touchpad multi-finger gestures on lock screen
0470         return waylandServer()->isScreenLocked();
0471     }
0472     bool holdGestureBegin(int fingerCount, std::chrono::microseconds time) override
0473     {
0474         // no touchpad multi-finger gestures on lock screen
0475         return waylandServer()->isScreenLocked();
0476     }
0477     bool holdGestureEnd(std::chrono::microseconds time) override
0478     {
0479         // no touchpad multi-finger gestures on lock screen
0480         return waylandServer()->isScreenLocked();
0481     }
0482 
0483 private:
0484     bool surfaceAllowed(SurfaceInterface *(SeatInterface::*method)() const) const
0485     {
0486         if (SurfaceInterface *s = (waylandServer()->seat()->*method)()) {
0487             if (Window *t = waylandServer()->findWindow(s)) {
0488                 return t->isLockScreen() || t->isInputMethod() || t->isLockScreenOverlay();
0489             }
0490             return false;
0491         }
0492         return true;
0493     }
0494     bool pointerSurfaceAllowed() const
0495     {
0496         return surfaceAllowed(&SeatInterface::focusedPointerSurface);
0497     }
0498     bool keyboardSurfaceAllowed() const
0499     {
0500         return surfaceAllowed(&SeatInterface::focusedKeyboardSurface);
0501     }
0502     bool touchSurfaceAllowed() const
0503     {
0504         return surfaceAllowed(&SeatInterface::focusedTouchSurface);
0505     }
0506 };
0507 
0508 class EffectsFilter : public InputEventFilter
0509 {
0510 public:
0511     bool pointerEvent(MouseEvent *event, quint32 nativeButton) override
0512     {
0513         if (!effects) {
0514             return false;
0515         }
0516         return effects->checkInputWindowEvent(event);
0517     }
0518     bool wheelEvent(WheelEvent *event) override
0519     {
0520         if (!effects) {
0521             return false;
0522         }
0523         return effects->checkInputWindowEvent(event);
0524     }
0525     bool keyEvent(KeyEvent *event) override
0526     {
0527         if (!effects || !effects->hasKeyboardGrab()) {
0528             return false;
0529         }
0530         waylandServer()->seat()->setFocusedKeyboardSurface(nullptr);
0531         passToWaylandServer(event);
0532         effects->grabbedKeyboardEvent(event);
0533         return true;
0534     }
0535     bool touchDown(qint32 id, const QPointF &pos, std::chrono::microseconds time) override
0536     {
0537         if (!effects) {
0538             return false;
0539         }
0540         return effects->touchDown(id, pos, time);
0541     }
0542     bool touchMotion(qint32 id, const QPointF &pos, std::chrono::microseconds time) override
0543     {
0544         if (!effects) {
0545             return false;
0546         }
0547         return effects->touchMotion(id, pos, time);
0548     }
0549     bool touchUp(qint32 id, std::chrono::microseconds time) override
0550     {
0551         if (!effects) {
0552             return false;
0553         }
0554         return effects->touchUp(id, time);
0555     }
0556     bool tabletToolEvent(TabletEvent *event) override
0557     {
0558         if (!effects) {
0559             return false;
0560         }
0561         return effects->tabletToolEvent(event);
0562     }
0563     bool tabletToolButtonEvent(uint button, bool pressed, const TabletToolId &tabletToolId, std::chrono::microseconds time) override
0564     {
0565         if (!effects) {
0566             return false;
0567         }
0568         return effects->tabletToolButtonEvent(button, pressed, tabletToolId, time);
0569     }
0570     bool tabletPadButtonEvent(uint button, bool pressed, const TabletPadId &tabletPadId, std::chrono::microseconds time) override
0571     {
0572         if (!effects) {
0573             return false;
0574         }
0575         return effects->tabletPadButtonEvent(button, pressed, tabletPadId, time);
0576     }
0577     bool tabletPadStripEvent(int number, int position, bool isFinger, const TabletPadId &tabletPadId, std::chrono::microseconds time) override
0578     {
0579         if (!effects) {
0580             return false;
0581         }
0582         return effects->tabletPadStripEvent(number, position, isFinger, tabletPadId, time);
0583     }
0584     bool tabletPadRingEvent(int number, int position, bool isFinger, const TabletPadId &tabletPadId, std::chrono::microseconds time) override
0585     {
0586         if (!effects) {
0587             return false;
0588         }
0589         return effects->tabletPadRingEvent(number, position, isFinger, tabletPadId, time);
0590     }
0591 };
0592 
0593 class MoveResizeFilter : public InputEventFilter
0594 {
0595 public:
0596     bool pointerEvent(MouseEvent *event, quint32 nativeButton) override
0597     {
0598         Window *window = workspace()->moveResizeWindow();
0599         if (!window) {
0600             return false;
0601         }
0602         switch (event->type()) {
0603         case QEvent::MouseMove:
0604             window->updateInteractiveMoveResize(event->screenPos());
0605             break;
0606         case QEvent::MouseButtonRelease:
0607             if (event->buttons() == Qt::NoButton) {
0608                 window->endInteractiveMoveResize();
0609             }
0610             break;
0611         default:
0612             break;
0613         }
0614         return true;
0615     }
0616     bool wheelEvent(WheelEvent *event) override
0617     {
0618         // filter out while moving a window
0619         return workspace()->moveResizeWindow() != nullptr;
0620     }
0621     bool keyEvent(KeyEvent *event) override
0622     {
0623         Window *window = workspace()->moveResizeWindow();
0624         if (!window) {
0625             return false;
0626         }
0627         if (event->type() == QEvent::KeyPress) {
0628             window->keyPressEvent(event->key() | event->modifiers());
0629         }
0630         if (window->isInteractiveMove() || window->isInteractiveResize()) {
0631             // only update if mode didn't end
0632             window->updateInteractiveMoveResize(input()->globalPointer());
0633         }
0634         return true;
0635     }
0636 
0637     bool touchDown(qint32 id, const QPointF &pos, std::chrono::microseconds time) override
0638     {
0639         Window *window = workspace()->moveResizeWindow();
0640         if (!window) {
0641             return false;
0642         }
0643         return true;
0644     }
0645 
0646     bool touchMotion(qint32 id, const QPointF &pos, std::chrono::microseconds time) override
0647     {
0648         Window *window = workspace()->moveResizeWindow();
0649         if (!window) {
0650             return false;
0651         }
0652         if (!m_set) {
0653             m_id = id;
0654             m_set = true;
0655         }
0656         if (m_id == id) {
0657             window->updateInteractiveMoveResize(pos);
0658         }
0659         return true;
0660     }
0661 
0662     bool touchUp(qint32 id, std::chrono::microseconds time) override
0663     {
0664         Window *window = workspace()->moveResizeWindow();
0665         if (!window) {
0666             return false;
0667         }
0668         if (m_id == id || !m_set) {
0669             window->endInteractiveMoveResize();
0670             m_set = false;
0671             // pass through to update decoration filter later on
0672             return false;
0673         }
0674         m_set = false;
0675         return true;
0676     }
0677 
0678     bool tabletToolEvent(TabletEvent *event) override
0679     {
0680         Window *window = workspace()->moveResizeWindow();
0681         if (!window) {
0682             return false;
0683         }
0684         switch (event->type()) {
0685         case QEvent::TabletMove:
0686             window->updateInteractiveMoveResize(event->globalPosF());
0687             break;
0688         case QEvent::TabletRelease:
0689             window->endInteractiveMoveResize();
0690             break;
0691         default:
0692             break;
0693         }
0694         // Let TabletInputFilter receive the event, so the cursor position can be updated.
0695         return false;
0696     }
0697 
0698 private:
0699     qint32 m_id = 0;
0700     bool m_set = false;
0701 };
0702 
0703 class WindowSelectorFilter : public InputEventFilter
0704 {
0705 public:
0706     bool pointerEvent(MouseEvent *event, quint32 nativeButton) override
0707     {
0708         if (!m_active) {
0709             return false;
0710         }
0711         switch (event->type()) {
0712         case QEvent::MouseButtonRelease:
0713             if (event->buttons() == Qt::NoButton) {
0714                 if (event->button() == Qt::RightButton) {
0715                     cancel();
0716                 } else {
0717                     accept(event->globalPosition());
0718                 }
0719             }
0720             break;
0721         default:
0722             break;
0723         }
0724         return true;
0725     }
0726     bool wheelEvent(WheelEvent *event) override
0727     {
0728         // filter out while selecting a window
0729         return m_active;
0730     }
0731     bool keyEvent(KeyEvent *event) override
0732     {
0733         if (!m_active) {
0734             return false;
0735         }
0736         waylandServer()->seat()->setFocusedKeyboardSurface(nullptr);
0737         passToWaylandServer(event);
0738 
0739         if (event->type() == QEvent::KeyPress) {
0740             // x11 variant does this on key press, so do the same
0741             if (event->key() == Qt::Key_Escape) {
0742                 cancel();
0743             } else if (event->key() == Qt::Key_Enter || event->key() == Qt::Key_Return || event->key() == Qt::Key_Space) {
0744                 accept(input()->globalPointer());
0745             }
0746             if (input()->supportsPointerWarping()) {
0747                 int mx = 0;
0748                 int my = 0;
0749                 if (event->key() == Qt::Key_Left) {
0750                     mx = -10;
0751                 }
0752                 if (event->key() == Qt::Key_Right) {
0753                     mx = 10;
0754                 }
0755                 if (event->key() == Qt::Key_Up) {
0756                     my = -10;
0757                 }
0758                 if (event->key() == Qt::Key_Down) {
0759                     my = 10;
0760                 }
0761                 if (event->modifiers() & Qt::ControlModifier) {
0762                     mx /= 10;
0763                     my /= 10;
0764                 }
0765                 input()->warpPointer(input()->globalPointer() + QPointF(mx, my));
0766             }
0767         }
0768         // filter out while selecting a window
0769         return true;
0770     }
0771 
0772     bool touchDown(qint32 id, const QPointF &pos, std::chrono::microseconds time) override
0773     {
0774         if (!isActive()) {
0775             return false;
0776         }
0777         m_touchPoints.insert(id, pos);
0778         return true;
0779     }
0780 
0781     bool touchMotion(qint32 id, const QPointF &pos, std::chrono::microseconds time) override
0782     {
0783         if (!isActive()) {
0784             return false;
0785         }
0786         auto it = m_touchPoints.find(id);
0787         if (it != m_touchPoints.end()) {
0788             *it = pos;
0789         }
0790         return true;
0791     }
0792 
0793     bool touchUp(qint32 id, std::chrono::microseconds time) override
0794     {
0795         if (!isActive()) {
0796             return false;
0797         }
0798         auto it = m_touchPoints.find(id);
0799         if (it != m_touchPoints.end()) {
0800             const auto pos = it.value();
0801             m_touchPoints.erase(it);
0802             if (m_touchPoints.isEmpty()) {
0803                 accept(pos);
0804             }
0805         }
0806         return true;
0807     }
0808 
0809     bool isActive() const
0810     {
0811         return m_active;
0812     }
0813     void start(std::function<void(Window *)> callback)
0814     {
0815         Q_ASSERT(!m_active);
0816         m_active = true;
0817         m_callback = callback;
0818         input()->keyboard()->update();
0819         input()->touch()->cancel();
0820     }
0821     void start(std::function<void(const QPoint &)> callback)
0822     {
0823         Q_ASSERT(!m_active);
0824         m_active = true;
0825         m_pointSelectionFallback = callback;
0826         input()->keyboard()->update();
0827         input()->touch()->cancel();
0828     }
0829 
0830 private:
0831     void deactivate()
0832     {
0833         m_active = false;
0834         m_callback = std::function<void(Window *)>();
0835         m_pointSelectionFallback = std::function<void(const QPoint &)>();
0836         input()->pointer()->removeWindowSelectionCursor();
0837         input()->keyboard()->update();
0838         m_touchPoints.clear();
0839     }
0840     void cancel()
0841     {
0842         if (m_callback) {
0843             m_callback(nullptr);
0844         }
0845         if (m_pointSelectionFallback) {
0846             m_pointSelectionFallback(QPoint(-1, -1));
0847         }
0848         deactivate();
0849     }
0850     void accept(const QPointF &pos)
0851     {
0852         if (m_callback) {
0853             // TODO: this ignores shaped windows
0854             m_callback(input()->findToplevel(pos));
0855         }
0856         if (m_pointSelectionFallback) {
0857             m_pointSelectionFallback(pos.toPoint());
0858         }
0859         deactivate();
0860     }
0861 
0862     bool m_active = false;
0863     std::function<void(Window *)> m_callback;
0864     std::function<void(const QPoint &)> m_pointSelectionFallback;
0865     QMap<quint32, QPointF> m_touchPoints;
0866 };
0867 
0868 #if KWIN_BUILD_GLOBALSHORTCUTS
0869 class GlobalShortcutFilter : public InputEventFilter
0870 {
0871 public:
0872     GlobalShortcutFilter()
0873     {
0874         m_powerDown.setSingleShot(true);
0875         m_powerDown.setInterval(1000);
0876     }
0877 
0878     bool pointerEvent(MouseEvent *event, quint32 nativeButton) override
0879     {
0880         if (event->type() == QEvent::MouseButtonPress) {
0881             if (input()->shortcuts()->processPointerPressed(event->modifiers(), event->buttons())) {
0882                 return true;
0883             }
0884         }
0885         return false;
0886     }
0887     bool wheelEvent(WheelEvent *event) override
0888     {
0889         if (event->modifiers() == Qt::NoModifier) {
0890             return false;
0891         }
0892         PointerAxisDirection direction = PointerAxisUp;
0893         if (event->angleDelta().x() < 0) {
0894             direction = PointerAxisRight;
0895         } else if (event->angleDelta().x() > 0) {
0896             direction = PointerAxisLeft;
0897         } else if (event->angleDelta().y() < 0) {
0898             direction = PointerAxisDown;
0899         } else if (event->angleDelta().y() > 0) {
0900             direction = PointerAxisUp;
0901         }
0902         return input()->shortcuts()->processAxis(event->modifiers(), direction);
0903     }
0904     bool keyEvent(KeyEvent *event) override
0905     {
0906         if (event->key() == Qt::Key_PowerOff) {
0907             const auto modifiers = static_cast<KeyEvent *>(event)->modifiersRelevantForGlobalShortcuts();
0908             if (event->type() == QEvent::KeyPress && !event->isAutoRepeat()) {
0909                 auto passToShortcuts = [modifiers] {
0910                     input()->shortcuts()->processKey(modifiers, Qt::Key_PowerDown);
0911                 };
0912                 QObject::connect(&m_powerDown, &QTimer::timeout, input()->shortcuts(), passToShortcuts, Qt::SingleShotConnection);
0913                 m_powerDown.start();
0914                 return true;
0915             } else if (event->type() == QEvent::KeyRelease) {
0916                 const bool ret = !m_powerDown.isActive() || input()->shortcuts()->processKey(modifiers, event->key());
0917                 m_powerDown.stop();
0918                 return ret;
0919             }
0920         } else if (event->type() == QEvent::KeyPress) {
0921             if (!waylandServer()->isKeyboardShortcutsInhibited()) {
0922                 return input()->shortcuts()->processKey(static_cast<KeyEvent *>(event)->modifiersRelevantForGlobalShortcuts(), event->key());
0923             }
0924         } else if (event->type() == QEvent::KeyRelease) {
0925             if (!waylandServer()->isKeyboardShortcutsInhibited()) {
0926                 return input()->shortcuts()->processKeyRelease(static_cast<KeyEvent *>(event)->modifiersRelevantForGlobalShortcuts(), event->key());
0927             }
0928         }
0929         return false;
0930     }
0931     bool swipeGestureBegin(int fingerCount, std::chrono::microseconds time) override
0932     {
0933         m_touchpadGestureFingerCount = fingerCount;
0934         if (m_touchpadGestureFingerCount >= 3) {
0935             input()->shortcuts()->processSwipeStart(DeviceType::Touchpad, fingerCount);
0936             return true;
0937         } else {
0938             return false;
0939         }
0940     }
0941     bool swipeGestureUpdate(const QPointF &delta, std::chrono::microseconds time) override
0942     {
0943         if (m_touchpadGestureFingerCount >= 3) {
0944             input()->shortcuts()->processSwipeUpdate(DeviceType::Touchpad, delta);
0945             return true;
0946         } else {
0947             return false;
0948         }
0949     }
0950     bool swipeGestureCancelled(std::chrono::microseconds time) override
0951     {
0952         if (m_touchpadGestureFingerCount >= 3) {
0953             input()->shortcuts()->processSwipeCancel(DeviceType::Touchpad);
0954             return true;
0955         } else {
0956             return false;
0957         }
0958     }
0959     bool swipeGestureEnd(std::chrono::microseconds time) override
0960     {
0961         if (m_touchpadGestureFingerCount >= 3) {
0962             input()->shortcuts()->processSwipeEnd(DeviceType::Touchpad);
0963             return true;
0964         } else {
0965             return false;
0966         }
0967     }
0968     bool pinchGestureBegin(int fingerCount, std::chrono::microseconds time) override
0969     {
0970         m_touchpadGestureFingerCount = fingerCount;
0971         if (m_touchpadGestureFingerCount >= 3) {
0972             input()->shortcuts()->processPinchStart(fingerCount);
0973             return true;
0974         } else {
0975             return false;
0976         }
0977     }
0978     bool pinchGestureUpdate(qreal scale, qreal angleDelta, const QPointF &delta, std::chrono::microseconds time) override
0979     {
0980         if (m_touchpadGestureFingerCount >= 3) {
0981             input()->shortcuts()->processPinchUpdate(scale, angleDelta, delta);
0982             return true;
0983         } else {
0984             return false;
0985         }
0986     }
0987     bool pinchGestureEnd(std::chrono::microseconds time) override
0988     {
0989         if (m_touchpadGestureFingerCount >= 3) {
0990             input()->shortcuts()->processPinchEnd();
0991             return true;
0992         } else {
0993             return false;
0994         }
0995     }
0996     bool pinchGestureCancelled(std::chrono::microseconds time) override
0997     {
0998         if (m_touchpadGestureFingerCount >= 3) {
0999             input()->shortcuts()->processPinchCancel();
1000             return true;
1001         } else {
1002             return false;
1003         }
1004     }
1005     bool touchDown(qint32 id, const QPointF &pos, std::chrono::microseconds time) override
1006     {
1007         if (m_gestureTaken) {
1008             input()->shortcuts()->processSwipeCancel(DeviceType::Touchscreen);
1009             m_gestureCancelled = true;
1010             return true;
1011         } else {
1012             m_touchPoints.insert(id, pos);
1013             if (m_touchPoints.count() == 1) {
1014                 m_lastTouchDownTime = time;
1015             } else {
1016                 if (time - m_lastTouchDownTime > 250ms) {
1017                     m_gestureCancelled = true;
1018                     return false;
1019                 }
1020                 m_lastTouchDownTime = time;
1021                 auto output = workspace()->outputAt(pos);
1022                 auto physicalSize = output->orientateSize(output->physicalSize());
1023                 if (!physicalSize.isValid()) {
1024                     physicalSize = QSize(190, 100);
1025                 }
1026                 float xfactor = physicalSize.width() / (float)output->geometry().width();
1027                 float yfactor = physicalSize.height() / (float)output->geometry().height();
1028                 bool distanceMatch = std::any_of(m_touchPoints.constBegin(), m_touchPoints.constEnd(), [pos, xfactor, yfactor](const auto &point) {
1029                     QPointF p = pos - point;
1030                     return std::abs(xfactor * p.x()) + std::abs(yfactor * p.y()) < 50;
1031                 });
1032                 if (!distanceMatch) {
1033                     m_gestureCancelled = true;
1034                     return false;
1035                 }
1036             }
1037             if (m_touchPoints.count() >= 3 && !m_gestureCancelled) {
1038                 m_gestureTaken = true;
1039                 m_syntheticCancel = true;
1040                 input()->processFilters(std::bind(&InputEventFilter::touchCancel, std::placeholders::_1));
1041                 m_syntheticCancel = false;
1042                 input()->shortcuts()->processSwipeStart(DeviceType::Touchscreen, m_touchPoints.count());
1043                 return true;
1044             }
1045         }
1046         return false;
1047     }
1048 
1049     bool touchMotion(qint32 id, const QPointF &pos, std::chrono::microseconds time) override
1050     {
1051         if (m_gestureTaken) {
1052             if (m_gestureCancelled) {
1053                 return true;
1054             }
1055             auto output = workspace()->outputAt(pos);
1056             const auto physicalSize = output->orientateSize(output->physicalSize());
1057             const float xfactor = physicalSize.width() / (float)output->geometry().width();
1058             const float yfactor = physicalSize.height() / (float)output->geometry().height();
1059 
1060             auto &point = m_touchPoints[id];
1061             const QPointF dist = pos - point;
1062             const QPointF delta = QPointF(xfactor * dist.x(), yfactor * dist.y());
1063             input()->shortcuts()->processSwipeUpdate(DeviceType::Touchscreen, 5 * delta / m_touchPoints.size());
1064             point = pos;
1065             return true;
1066         }
1067         return false;
1068     }
1069 
1070     bool touchUp(qint32 id, std::chrono::microseconds time) override
1071     {
1072         m_touchPoints.remove(id);
1073         if (m_gestureTaken) {
1074             if (!m_gestureCancelled) {
1075                 input()->shortcuts()->processSwipeEnd(DeviceType::Touchscreen);
1076                 m_gestureCancelled = true;
1077             }
1078             m_gestureTaken &= m_touchPoints.count() > 0;
1079             m_gestureCancelled &= m_gestureTaken;
1080             return true;
1081         } else {
1082             m_gestureCancelled &= m_touchPoints.count() > 0;
1083             return false;
1084         }
1085     }
1086 
1087     bool touchCancel() override
1088     {
1089         if (m_syntheticCancel) {
1090             return false;
1091         }
1092         const bool oldGestureTaken = m_gestureTaken;
1093         m_gestureTaken = false;
1094         m_gestureCancelled = false;
1095         m_touchPoints.clear();
1096         return oldGestureTaken;
1097     }
1098 
1099     bool touchFrame() override
1100     {
1101         return m_gestureTaken;
1102     }
1103 
1104 private:
1105     bool m_gestureTaken = false;
1106     bool m_gestureCancelled = false;
1107     bool m_syntheticCancel = false;
1108     std::chrono::microseconds m_lastTouchDownTime = std::chrono::microseconds::zero();
1109     QPointF m_lastAverageDistance;
1110     QMap<int32_t, QPointF> m_touchPoints;
1111     int m_touchpadGestureFingerCount = 0;
1112 
1113     QTimer m_powerDown;
1114 };
1115 #endif
1116 
1117 namespace
1118 {
1119 
1120 enum class MouseAction {
1121     ModifierOnly,
1122     ModifierAndWindow
1123 };
1124 std::pair<bool, bool> performWindowMouseAction(QMouseEvent *event, Window *window, MouseAction action = MouseAction::ModifierOnly)
1125 {
1126     Options::MouseCommand command = Options::MouseNothing;
1127     bool wasAction = false;
1128     if (static_cast<MouseEvent *>(event)->modifiersRelevantForGlobalShortcuts() == options->commandAllModifier()) {
1129         if (!input()->pointer()->isConstrained() && !workspace()->globalShortcutsDisabled()) {
1130             wasAction = true;
1131             switch (event->button()) {
1132             case Qt::LeftButton:
1133                 command = options->commandAll1();
1134                 break;
1135             case Qt::MiddleButton:
1136                 command = options->commandAll2();
1137                 break;
1138             case Qt::RightButton:
1139                 command = options->commandAll3();
1140                 break;
1141             default:
1142                 // nothing
1143                 break;
1144             }
1145         }
1146     } else {
1147         if (action == MouseAction::ModifierAndWindow) {
1148             command = window->getMouseCommand(event->button(), &wasAction);
1149         }
1150     }
1151     if (wasAction) {
1152         return std::make_pair(wasAction, !window->performMouseCommand(command, event->globalPosition()));
1153     }
1154     return std::make_pair(wasAction, false);
1155 }
1156 
1157 std::pair<bool, bool> performWindowWheelAction(QWheelEvent *event, Window *window, MouseAction action = MouseAction::ModifierOnly)
1158 {
1159     bool wasAction = false;
1160     Options::MouseCommand command = Options::MouseNothing;
1161     if (static_cast<WheelEvent *>(event)->modifiersRelevantForGlobalShortcuts() == options->commandAllModifier()) {
1162         if (!input()->pointer()->isConstrained() && !workspace()->globalShortcutsDisabled()) {
1163             wasAction = true;
1164             command = options->operationWindowMouseWheel(-1 * event->angleDelta().y());
1165         }
1166     } else {
1167         if (action == MouseAction::ModifierAndWindow) {
1168             command = window->getWheelCommand(Qt::Vertical, &wasAction);
1169         }
1170     }
1171     if (wasAction) {
1172         return std::make_pair(wasAction, !window->performMouseCommand(command, event->globalPosition()));
1173     }
1174     return std::make_pair(wasAction, false);
1175 }
1176 
1177 }
1178 
1179 class InternalWindowEventFilter : public InputEventFilter
1180 {
1181     bool pointerEvent(MouseEvent *event, quint32 nativeButton) override
1182     {
1183         if (!input()->pointer()->focus() || !input()->pointer()->focus()->isInternal()) {
1184             return false;
1185         }
1186         QWindow *internal = static_cast<InternalWindow *>(input()->pointer()->focus())->handle();
1187         if (!internal) {
1188             // the handle can be nullptr if the tooltip gets closed while focus updates are blocked
1189             return false;
1190         }
1191         QMouseEvent mouseEvent(event->type(),
1192                                event->pos() - internal->position(),
1193                                event->globalPosition(),
1194                                event->button(), event->buttons(), event->modifiers());
1195         QCoreApplication::sendEvent(internal, &mouseEvent);
1196         return mouseEvent.isAccepted();
1197     }
1198     bool wheelEvent(WheelEvent *event) override
1199     {
1200         if (!input()->pointer()->focus() || !input()->pointer()->focus()->isInternal()) {
1201             return false;
1202         }
1203         QWindow *internal = static_cast<InternalWindow *>(input()->pointer()->focus())->handle();
1204         const QPointF localPos = event->globalPosition() - internal->position();
1205         QWheelEvent wheelEvent(localPos, event->globalPosition(), QPoint(),
1206                                event->angleDelta() * -1,
1207                                event->buttons(),
1208                                event->modifiers(),
1209                                Qt::NoScrollPhase,
1210                                false);
1211         QCoreApplication::sendEvent(internal, &wheelEvent);
1212         return wheelEvent.isAccepted();
1213     }
1214     bool keyEvent(KeyEvent *event) override
1215     {
1216         const QList<Window *> windows = workspace()->windows();
1217         QWindow *found = nullptr;
1218         for (auto it = windows.crbegin(); it != windows.crend(); ++it) {
1219             auto internal = qobject_cast<InternalWindow *>(*it);
1220             if (!internal) {
1221                 continue;
1222             }
1223             if (QWindow *w = internal->handle()) {
1224                 if (!w->isVisible()) {
1225                     continue;
1226                 }
1227                 if (!workspace()->geometry().contains(w->geometry())) {
1228                     continue;
1229                 }
1230                 if (w->property("_q_showWithoutActivating").toBool()) {
1231                     continue;
1232                 }
1233                 if (w->property("outputOnly").toBool()) {
1234                     continue;
1235                 }
1236                 if (w->flags().testFlag(Qt::ToolTip)) {
1237                     continue;
1238                 }
1239                 found = w;
1240                 break;
1241             }
1242         }
1243         if (QGuiApplication::focusWindow() != found) {
1244 #if QT_VERSION < QT_VERSION_CHECK(6, 7, 0)
1245             QWindowSystemInterface::handleWindowActivated(found);
1246 #else
1247             QWindowSystemInterface::handleFocusWindowChanged(found);
1248 #endif
1249         }
1250         if (!found) {
1251             return false;
1252         }
1253         auto xkb = input()->keyboard()->xkb();
1254         Qt::Key key = xkb->toQtKey(xkb->toKeysym(event->nativeScanCode()),
1255                                    event->nativeScanCode(),
1256                                    Qt::KeyboardModifiers(),
1257                                    true /* workaround for QTBUG-62102 */);
1258         QKeyEvent internalEvent(event->type(), key,
1259                                 event->modifiers(), event->nativeScanCode(), event->nativeVirtualKey(),
1260                                 event->nativeModifiers(), event->text());
1261         internalEvent.setAccepted(false);
1262         if (QCoreApplication::sendEvent(found, &internalEvent)) {
1263             waylandServer()->seat()->setFocusedKeyboardSurface(nullptr);
1264             passToWaylandServer(event);
1265             return true;
1266         }
1267         return false;
1268     }
1269 
1270     bool touchDown(qint32 id, const QPointF &pos, std::chrono::microseconds time) override
1271     {
1272         auto seat = waylandServer()->seat();
1273         if (seat->isTouchSequence()) {
1274             // something else is getting the events
1275             return false;
1276         }
1277         auto touch = input()->touch();
1278         if (touch->internalPressId() != -1) {
1279             // already on internal window, ignore further touch points, but filter out
1280             m_pressedIds.insert(id);
1281             return true;
1282         }
1283         // a new touch point
1284         seat->setTimestamp(time);
1285         if (!input()->touch()->focus() || !input()->touch()->focus()->isInternal()) {
1286             return false;
1287         }
1288         touch->setInternalPressId(id);
1289         // Qt's touch event API is rather complex, let's do fake mouse events instead
1290         QWindow *internal = static_cast<InternalWindow *>(input()->touch()->focus())->handle();
1291         m_lastGlobalTouchPos = pos;
1292         m_lastLocalTouchPos = pos - internal->position();
1293 
1294         QEnterEvent enterEvent(m_lastLocalTouchPos, m_lastLocalTouchPos, pos);
1295         QCoreApplication::sendEvent(internal, &enterEvent);
1296 
1297         QMouseEvent e(QEvent::MouseButtonPress, m_lastLocalTouchPos, pos, Qt::LeftButton, Qt::LeftButton, input()->keyboardModifiers());
1298         e.setAccepted(false);
1299         QCoreApplication::sendEvent(internal, &e);
1300         return true;
1301     }
1302     bool touchMotion(qint32 id, const QPointF &pos, std::chrono::microseconds time) override
1303     {
1304         auto touch = input()->touch();
1305         if (!input()->touch()->focus() || !input()->touch()->focus()->isInternal()) {
1306             return false;
1307         }
1308         if (touch->internalPressId() == -1) {
1309             return false;
1310         }
1311         waylandServer()->seat()->setTimestamp(time);
1312         if (touch->internalPressId() != qint32(id) || m_pressedIds.contains(id)) {
1313             // ignore, but filter out
1314             return true;
1315         }
1316         QWindow *internal = static_cast<InternalWindow *>(input()->touch()->focus())->handle();
1317         m_lastGlobalTouchPos = pos;
1318         m_lastLocalTouchPos = pos - QPointF(internal->x(), internal->y());
1319 
1320         QMouseEvent e(QEvent::MouseMove, m_lastLocalTouchPos, m_lastGlobalTouchPos, Qt::LeftButton, Qt::LeftButton, input()->keyboardModifiers());
1321         QCoreApplication::instance()->sendEvent(internal, &e);
1322         return true;
1323     }
1324     bool touchUp(qint32 id, std::chrono::microseconds time) override
1325     {
1326         auto touch = input()->touch();
1327         const bool removed = m_pressedIds.remove(id);
1328         if (touch->internalPressId() == -1) {
1329             return removed;
1330         }
1331         waylandServer()->seat()->setTimestamp(time);
1332         if (touch->internalPressId() != qint32(id)) {
1333             // ignore, but filter out
1334             return true;
1335         }
1336         if (!input()->touch()->focus() || !input()->touch()->focus()->isInternal()) {
1337             return removed;
1338         }
1339         QWindow *internal = static_cast<InternalWindow *>(input()->touch()->focus())->handle();
1340         // send mouse up
1341         QMouseEvent e(QEvent::MouseButtonRelease, m_lastLocalTouchPos, m_lastGlobalTouchPos, Qt::LeftButton, Qt::MouseButtons(), input()->keyboardModifiers());
1342         e.setAccepted(false);
1343         QCoreApplication::sendEvent(internal, &e);
1344 
1345         QEvent leaveEvent(QEvent::Leave);
1346         QCoreApplication::sendEvent(internal, &leaveEvent);
1347 
1348         m_lastGlobalTouchPos = QPointF();
1349         m_lastLocalTouchPos = QPointF();
1350         input()->touch()->setInternalPressId(-1);
1351         return true;
1352     }
1353 
1354 private:
1355     QSet<qint32> m_pressedIds;
1356     QPointF m_lastGlobalTouchPos;
1357     QPointF m_lastLocalTouchPos;
1358 };
1359 
1360 class MouseWheelAccumulator
1361 {
1362 public:
1363     float accumulate(WheelEvent *event)
1364     {
1365         m_scrollV120 += event->deltaV120();
1366         m_scrollDistance += event->delta();
1367         if (std::abs(m_scrollV120) >= 120 || (!event->deltaV120() && std::abs(m_scrollDistance) >= 15)) {
1368             float ret = m_scrollDistance;
1369             m_scrollV120 = 0;
1370             m_scrollDistance = 0;
1371             return ret;
1372         } else {
1373             return 0;
1374         }
1375     }
1376 
1377 private:
1378     float m_scrollDistance = 0;
1379     int m_scrollV120 = 0;
1380 };
1381 
1382 class DecorationEventFilter : public InputEventFilter
1383 {
1384 public:
1385     bool pointerEvent(MouseEvent *event, quint32 nativeButton) override
1386     {
1387         auto decoration = input()->pointer()->decoration();
1388         if (!decoration) {
1389             return false;
1390         }
1391         const QPointF p = event->screenPos() - decoration->window()->pos();
1392         switch (event->type()) {
1393         case QEvent::MouseMove: {
1394             QHoverEvent e(QEvent::HoverMove, p, p);
1395             QCoreApplication::instance()->sendEvent(decoration->decoration(), &e);
1396             decoration->window()->processDecorationMove(p, event->screenPos());
1397             return true;
1398         }
1399         case QEvent::MouseButtonPress:
1400         case QEvent::MouseButtonRelease: {
1401             const auto actionResult = performWindowMouseAction(event, decoration->window());
1402             if (actionResult.first) {
1403                 return actionResult.second;
1404             }
1405             QMouseEvent e(event->type(), p, event->screenPos(), event->button(), event->buttons(), event->modifiers());
1406             e.setTimestamp(std::chrono::duration_cast<std::chrono::milliseconds>(event->timestamp()).count());
1407             e.setAccepted(false);
1408             QCoreApplication::sendEvent(decoration->decoration(), &e);
1409             if (!e.isAccepted() && event->type() == QEvent::MouseButtonPress) {
1410                 decoration->window()->processDecorationButtonPress(&e);
1411             }
1412             if (event->type() == QEvent::MouseButtonRelease) {
1413                 decoration->window()->processDecorationButtonRelease(&e);
1414             }
1415             return true;
1416         }
1417         default:
1418             break;
1419         }
1420         return false;
1421     }
1422     bool wheelEvent(WheelEvent *event) override
1423     {
1424         auto decoration = input()->pointer()->decoration();
1425         if (!decoration) {
1426             return false;
1427         }
1428         if (event->angleDelta().y() != 0) {
1429             // client window action only on vertical scrolling
1430             const auto actionResult = performWindowWheelAction(event, decoration->window());
1431             if (actionResult.first) {
1432                 return actionResult.second;
1433             }
1434         }
1435         const QPointF localPos = event->globalPosition() - decoration->window()->pos();
1436         const Qt::Orientation orientation = (event->angleDelta().x() != 0) ? Qt::Horizontal : Qt::Vertical;
1437         QWheelEvent e(localPos, event->globalPosition(), QPoint(),
1438                       event->angleDelta(),
1439                       event->buttons(),
1440                       event->modifiers(),
1441                       Qt::NoScrollPhase,
1442                       false);
1443         e.setAccepted(false);
1444         QCoreApplication::sendEvent(decoration, &e);
1445         if (e.isAccepted()) {
1446             return true;
1447         }
1448         if ((orientation == Qt::Vertical) && decoration->window()->titlebarPositionUnderMouse()) {
1449             if (float delta = m_accumulator.accumulate(event)) {
1450                 decoration->window()->performMouseCommand(options->operationTitlebarMouseWheel(delta * -1),
1451                                                           event->globalPosition());
1452             }
1453         }
1454         return true;
1455     }
1456     bool touchDown(qint32 id, const QPointF &pos, std::chrono::microseconds time) override
1457     {
1458         auto seat = waylandServer()->seat();
1459         if (seat->isTouchSequence()) {
1460             return false;
1461         }
1462         if (input()->touch()->decorationPressId() != -1) {
1463             // already on a decoration, ignore further touch points, but filter out
1464             return true;
1465         }
1466         seat->setTimestamp(time);
1467         auto decoration = input()->touch()->decoration();
1468         if (!decoration) {
1469             return false;
1470         }
1471 
1472         input()->touch()->setDecorationPressId(id);
1473         m_lastGlobalTouchPos = pos;
1474         m_lastLocalTouchPos = pos - decoration->window()->pos();
1475 
1476         QHoverEvent hoverEvent(QEvent::HoverMove, m_lastLocalTouchPos, m_lastLocalTouchPos);
1477         QCoreApplication::sendEvent(decoration->decoration(), &hoverEvent);
1478 
1479         QMouseEvent e(QEvent::MouseButtonPress, m_lastLocalTouchPos, pos, Qt::LeftButton, Qt::LeftButton, input()->keyboardModifiers());
1480         e.setAccepted(false);
1481         QCoreApplication::sendEvent(decoration->decoration(), &e);
1482         if (!e.isAccepted()) {
1483             decoration->window()->processDecorationButtonPress(&e);
1484         }
1485         return true;
1486     }
1487     bool touchMotion(qint32 id, const QPointF &pos, std::chrono::microseconds time) override
1488     {
1489         auto decoration = input()->touch()->decoration();
1490         if (!decoration) {
1491             return false;
1492         }
1493         if (input()->touch()->decorationPressId() == -1) {
1494             return false;
1495         }
1496         if (input()->touch()->decorationPressId() != qint32(id)) {
1497             // ignore, but filter out
1498             return true;
1499         }
1500         m_lastGlobalTouchPos = pos;
1501         m_lastLocalTouchPos = pos - decoration->window()->pos();
1502 
1503         QHoverEvent e(QEvent::HoverMove, m_lastLocalTouchPos, m_lastLocalTouchPos);
1504         QCoreApplication::instance()->sendEvent(decoration->decoration(), &e);
1505         decoration->window()->processDecorationMove(m_lastLocalTouchPos, pos);
1506         return true;
1507     }
1508     bool touchUp(qint32 id, std::chrono::microseconds time) override
1509     {
1510         auto decoration = input()->touch()->decoration();
1511         if (!decoration) {
1512             // can happen when quick tiling
1513             if (input()->touch()->decorationPressId() == id) {
1514                 m_lastGlobalTouchPos = QPointF();
1515                 m_lastLocalTouchPos = QPointF();
1516                 input()->touch()->setDecorationPressId(-1);
1517                 return true;
1518             }
1519             return false;
1520         }
1521         if (input()->touch()->decorationPressId() == -1) {
1522             return false;
1523         }
1524         if (input()->touch()->decorationPressId() != qint32(id)) {
1525             // ignore, but filter out
1526             return true;
1527         }
1528 
1529         // send mouse up
1530         QMouseEvent e(QEvent::MouseButtonRelease, m_lastLocalTouchPos, m_lastGlobalTouchPos, Qt::LeftButton, Qt::MouseButtons(), input()->keyboardModifiers());
1531         e.setAccepted(false);
1532         QCoreApplication::sendEvent(decoration->decoration(), &e);
1533         decoration->window()->processDecorationButtonRelease(&e);
1534 
1535         QHoverEvent leaveEvent(QEvent::HoverLeave, QPointF(), QPointF());
1536         QCoreApplication::sendEvent(decoration->decoration(), &leaveEvent);
1537 
1538         m_lastGlobalTouchPos = QPointF();
1539         m_lastLocalTouchPos = QPointF();
1540         input()->touch()->setDecorationPressId(-1);
1541         return true;
1542     }
1543     bool tabletToolEvent(TabletEvent *event) override
1544     {
1545         auto decoration = input()->tablet()->decoration();
1546         if (!decoration) {
1547             return false;
1548         }
1549         const QPointF p = event->globalPosF() - decoration->window()->pos();
1550         switch (event->type()) {
1551         case QEvent::TabletMove:
1552         case QEvent::TabletEnterProximity: {
1553             QHoverEvent e(QEvent::HoverMove, p, p);
1554             QCoreApplication::instance()->sendEvent(decoration->decoration(), &e);
1555             decoration->window()->processDecorationMove(p, event->globalPosF());
1556             break;
1557         }
1558         case QEvent::TabletPress:
1559         case QEvent::TabletRelease: {
1560             const bool isPressed = event->type() == QEvent::TabletPress;
1561             QMouseEvent e(isPressed ? QEvent::MouseButtonPress : QEvent::MouseButtonRelease,
1562                           p,
1563                           event->globalPosF(),
1564                           Qt::LeftButton,
1565                           isPressed ? Qt::LeftButton : Qt::MouseButtons(),
1566                           input()->keyboardModifiers());
1567             e.setAccepted(false);
1568             QCoreApplication::sendEvent(decoration->decoration(), &e);
1569             if (!e.isAccepted() && isPressed) {
1570                 decoration->window()->processDecorationButtonPress(&e);
1571             }
1572             if (event->type() == QEvent::TabletRelease) {
1573                 decoration->window()->processDecorationButtonRelease(&e);
1574             }
1575             break;
1576         }
1577         case QEvent::TabletLeaveProximity: {
1578             QHoverEvent leaveEvent(QEvent::HoverLeave, QPointF(), QPointF());
1579             QCoreApplication::sendEvent(decoration->decoration(), &leaveEvent);
1580             break;
1581         }
1582         default:
1583             break;
1584         }
1585         // Let TabletInputFilter receive the event, so the tablet can be registered and the cursor position can be updated.
1586         return false;
1587     }
1588 
1589 private:
1590     QPointF m_lastGlobalTouchPos;
1591     QPointF m_lastLocalTouchPos;
1592     MouseWheelAccumulator m_accumulator;
1593 };
1594 
1595 #if KWIN_BUILD_TABBOX
1596 class TabBoxInputFilter : public InputEventFilter
1597 {
1598 public:
1599     bool pointerEvent(MouseEvent *event, quint32 button) override
1600     {
1601         if (!workspace()->tabbox() || !workspace()->tabbox()->isGrabbed()) {
1602             return false;
1603         }
1604         return workspace()->tabbox()->handleMouseEvent(event);
1605     }
1606     bool keyEvent(KeyEvent *event) override
1607     {
1608         if (!workspace()->tabbox() || !workspace()->tabbox()->isGrabbed()) {
1609             return false;
1610         }
1611         auto seat = waylandServer()->seat();
1612         seat->setFocusedKeyboardSurface(nullptr);
1613         input()->pointer()->setEnableConstraints(false);
1614         // pass the key event to the seat, so that it has a proper model of the currently hold keys
1615         // this is important for combinations like alt+shift to ensure that shift is not considered pressed
1616         passToWaylandServer(event);
1617 
1618         if (event->type() == QEvent::KeyPress) {
1619             workspace()->tabbox()->keyPress(event->modifiersRelevantForTabBox() | event->key());
1620         } else if (static_cast<KeyEvent *>(event)->modifiersRelevantForGlobalShortcuts() == Qt::NoModifier) {
1621             workspace()->tabbox()->modifiersReleased();
1622             // update keyboard facus if the tabbox no longer grabs keys
1623             if (!workspace()->tabbox() || !workspace()->tabbox()->isGrabbed()) {
1624                 input()->keyboard()->update();
1625             }
1626         }
1627         return true;
1628     }
1629     bool wheelEvent(WheelEvent *event) override
1630     {
1631         if (!workspace()->tabbox() || !workspace()->tabbox()->isGrabbed()) {
1632             return false;
1633         }
1634         return workspace()->tabbox()->handleWheelEvent(event);
1635     }
1636 };
1637 #endif
1638 
1639 class ScreenEdgeInputFilter : public InputEventFilter
1640 {
1641 public:
1642     bool pointerEvent(MouseEvent *event, quint32 nativeButton) override
1643     {
1644         workspace()->screenEdges()->isEntered(event);
1645         // always forward
1646         return false;
1647     }
1648     bool touchDown(qint32 id, const QPointF &pos, std::chrono::microseconds time) override
1649     {
1650         // TODO: better check whether a touch sequence is in progress
1651         if (m_touchInProgress || waylandServer()->seat()->isTouchSequence()) {
1652             // cancel existing touch
1653             workspace()->screenEdges()->gestureRecognizer()->cancelSwipeGesture();
1654             m_touchInProgress = false;
1655             m_id = 0;
1656             return false;
1657         }
1658         if (workspace()->screenEdges()->gestureRecognizer()->startSwipeGesture(pos) > 0) {
1659             m_touchInProgress = true;
1660             m_id = id;
1661             m_lastPos = pos;
1662             return true;
1663         }
1664         return false;
1665     }
1666     bool touchMotion(qint32 id, const QPointF &pos, std::chrono::microseconds time) override
1667     {
1668         if (m_touchInProgress && m_id == id) {
1669             workspace()->screenEdges()->gestureRecognizer()->updateSwipeGesture(pos - m_lastPos);
1670             m_lastPos = pos;
1671             return true;
1672         }
1673         return false;
1674     }
1675     bool touchUp(qint32 id, std::chrono::microseconds time) override
1676     {
1677         if (m_touchInProgress && m_id == id) {
1678             workspace()->screenEdges()->gestureRecognizer()->endSwipeGesture();
1679             m_touchInProgress = false;
1680             return true;
1681         }
1682         return false;
1683     }
1684 
1685 private:
1686     bool m_touchInProgress = false;
1687     qint32 m_id = 0;
1688     QPointF m_lastPos;
1689 };
1690 
1691 /**
1692  * This filter implements window actions. If the event should not be passed to the
1693  * current pointer window it will filter out the event
1694  */
1695 class WindowActionInputFilter : public InputEventFilter
1696 {
1697 public:
1698     bool pointerEvent(MouseEvent *event, quint32 nativeButton) override
1699     {
1700         if (event->type() != QEvent::MouseButtonPress) {
1701             return false;
1702         }
1703         Window *window = input()->pointer()->focus();
1704         if (!window || !window->isClient()) {
1705             return false;
1706         }
1707         const auto actionResult = performWindowMouseAction(event, window, MouseAction::ModifierAndWindow);
1708         if (actionResult.first) {
1709             return actionResult.second;
1710         }
1711         return false;
1712     }
1713     bool wheelEvent(WheelEvent *event) override
1714     {
1715         if (event->angleDelta().y() == 0) {
1716             // only actions on vertical scroll
1717             return false;
1718         }
1719         Window *window = input()->pointer()->focus();
1720         if (!window || !window->isClient()) {
1721             return false;
1722         }
1723         const auto actionResult = performWindowWheelAction(event, window, MouseAction::ModifierAndWindow);
1724         if (actionResult.first) {
1725             return actionResult.second;
1726         }
1727         return false;
1728     }
1729     bool touchDown(qint32 id, const QPointF &pos, std::chrono::microseconds time) override
1730     {
1731         auto seat = waylandServer()->seat();
1732         if (seat->isTouchSequence()) {
1733             return false;
1734         }
1735         Window *window = input()->touch()->focus();
1736         if (!window || !window->isClient()) {
1737             return false;
1738         }
1739         bool wasAction = false;
1740         const Options::MouseCommand command = window->getMouseCommand(Qt::LeftButton, &wasAction);
1741         if (wasAction) {
1742             return !window->performMouseCommand(command, pos);
1743         }
1744         return false;
1745     }
1746     bool tabletToolEvent(TabletEvent *event) override
1747     {
1748         if (event->type() != QEvent::TabletPress) {
1749             return false;
1750         }
1751         Window *window = input()->tablet()->focus();
1752         if (!window || !window->isClient()) {
1753             return false;
1754         }
1755         bool wasAction = false;
1756         const Options::MouseCommand command = window->getMouseCommand(Qt::LeftButton, &wasAction);
1757         if (wasAction) {
1758             return !window->performMouseCommand(command, event->globalPosF());
1759         }
1760         return false;
1761     }
1762 };
1763 
1764 class InputKeyboardFilter : public InputEventFilter
1765 {
1766 public:
1767     bool keyEvent(KeyEvent *event) override
1768     {
1769         return passToInputMethod(event);
1770     }
1771 };
1772 
1773 /**
1774  * The remaining default input filter which forwards events to other windows
1775  */
1776 class ForwardInputFilter : public InputEventFilter
1777 {
1778 public:
1779     bool pointerEvent(MouseEvent *event, quint32 nativeButton) override
1780     {
1781         auto seat = waylandServer()->seat();
1782         seat->setTimestamp(event->timestamp());
1783         switch (event->type()) {
1784         case QEvent::MouseMove: {
1785             seat->notifyPointerMotion(event->globalPosition());
1786             MouseEvent *e = static_cast<MouseEvent *>(event);
1787             if (!e->delta().isNull()) {
1788                 seat->relativePointerMotion(e->delta(), e->deltaUnaccelerated(), e->timestamp());
1789             }
1790             break;
1791         }
1792         case QEvent::MouseButtonPress:
1793             seat->notifyPointerButton(nativeButton, PointerButtonState::Pressed);
1794             break;
1795         case QEvent::MouseButtonRelease:
1796             seat->notifyPointerButton(nativeButton, PointerButtonState::Released);
1797             break;
1798         default:
1799             break;
1800         }
1801         return true;
1802     }
1803     bool pointerFrame() override
1804     {
1805         auto seat = waylandServer()->seat();
1806         seat->notifyPointerFrame();
1807         return true;
1808     }
1809     bool wheelEvent(WheelEvent *event) override
1810     {
1811         auto seat = waylandServer()->seat();
1812         seat->setTimestamp(event->timestamp());
1813         auto _event = static_cast<WheelEvent *>(event);
1814         seat->notifyPointerAxis(_event->orientation(), _event->delta(), _event->deltaV120(),
1815                                 kwinAxisSourceToKWaylandAxisSource(_event->axisSource()),
1816                                 _event->inverted() ? PointerAxisRelativeDirection::Inverted : PointerAxisRelativeDirection::Normal);
1817         return true;
1818     }
1819     bool keyEvent(KeyEvent *event) override
1820     {
1821         if (event->isAutoRepeat()) {
1822             // handled by Wayland client
1823             return false;
1824         }
1825         auto seat = waylandServer()->seat();
1826         input()->keyboard()->update();
1827         seat->setTimestamp(event->timestamp());
1828         passToWaylandServer(event);
1829         return true;
1830     }
1831     bool touchDown(qint32 id, const QPointF &pos, std::chrono::microseconds time) override
1832     {
1833         auto seat = waylandServer()->seat();
1834         seat->setTimestamp(time);
1835         seat->notifyTouchDown(id, pos);
1836         return true;
1837     }
1838     bool touchMotion(qint32 id, const QPointF &pos, std::chrono::microseconds time) override
1839     {
1840         auto seat = waylandServer()->seat();
1841         seat->setTimestamp(time);
1842         seat->notifyTouchMotion(id, pos);
1843         return true;
1844     }
1845     bool touchUp(qint32 id, std::chrono::microseconds time) override
1846     {
1847         auto seat = waylandServer()->seat();
1848         seat->setTimestamp(time);
1849         seat->notifyTouchUp(id);
1850         return true;
1851     }
1852     bool touchCancel() override
1853     {
1854         waylandServer()->seat()->notifyTouchCancel();
1855         return true;
1856     }
1857     bool touchFrame() override
1858     {
1859         waylandServer()->seat()->notifyTouchFrame();
1860         return true;
1861     }
1862     bool pinchGestureBegin(int fingerCount, std::chrono::microseconds time) override
1863     {
1864         auto seat = waylandServer()->seat();
1865         seat->setTimestamp(time);
1866         seat->startPointerPinchGesture(fingerCount);
1867         return true;
1868     }
1869     bool pinchGestureUpdate(qreal scale, qreal angleDelta, const QPointF &delta, std::chrono::microseconds time) override
1870     {
1871         auto seat = waylandServer()->seat();
1872         seat->setTimestamp(time);
1873         seat->updatePointerPinchGesture(delta, scale, angleDelta);
1874         return true;
1875     }
1876     bool pinchGestureEnd(std::chrono::microseconds time) override
1877     {
1878         auto seat = waylandServer()->seat();
1879         seat->setTimestamp(time);
1880         seat->endPointerPinchGesture();
1881         return true;
1882     }
1883     bool pinchGestureCancelled(std::chrono::microseconds time) override
1884     {
1885         auto seat = waylandServer()->seat();
1886         seat->setTimestamp(time);
1887         seat->cancelPointerPinchGesture();
1888         return true;
1889     }
1890 
1891     bool swipeGestureBegin(int fingerCount, std::chrono::microseconds time) override
1892     {
1893         auto seat = waylandServer()->seat();
1894         seat->setTimestamp(time);
1895         seat->startPointerSwipeGesture(fingerCount);
1896         return true;
1897     }
1898     bool swipeGestureUpdate(const QPointF &delta, std::chrono::microseconds time) override
1899     {
1900         auto seat = waylandServer()->seat();
1901         seat->setTimestamp(time);
1902         seat->updatePointerSwipeGesture(delta);
1903         return true;
1904     }
1905     bool swipeGestureEnd(std::chrono::microseconds time) override
1906     {
1907         auto seat = waylandServer()->seat();
1908         seat->setTimestamp(time);
1909         seat->endPointerSwipeGesture();
1910         return true;
1911     }
1912     bool swipeGestureCancelled(std::chrono::microseconds time) override
1913     {
1914         auto seat = waylandServer()->seat();
1915         seat->setTimestamp(time);
1916         seat->cancelPointerSwipeGesture();
1917         return true;
1918     }
1919     bool holdGestureBegin(int fingerCount, std::chrono::microseconds time) override
1920     {
1921         auto seat = waylandServer()->seat();
1922         seat->setTimestamp(time);
1923         seat->startPointerHoldGesture(fingerCount);
1924         return true;
1925     }
1926     bool holdGestureEnd(std::chrono::microseconds time) override
1927     {
1928         auto seat = waylandServer()->seat();
1929         seat->setTimestamp(time);
1930         seat->endPointerHoldGesture();
1931         return true;
1932     }
1933     bool holdGestureCancelled(std::chrono::microseconds time) override
1934     {
1935         auto seat = waylandServer()->seat();
1936         seat->setTimestamp(time);
1937         seat->cancelPointerHoldGesture();
1938         return true;
1939     }
1940 };
1941 
1942 static SeatInterface *findSeat()
1943 {
1944     auto server = waylandServer();
1945     if (!server) {
1946         return nullptr;
1947     }
1948     return server->seat();
1949 }
1950 
1951 class SurfaceCursor : public Cursor
1952 {
1953 public:
1954     explicit SurfaceCursor(TabletToolV2Interface *tool)
1955         : Cursor()
1956     {
1957         setParent(tool);
1958         connect(tool, &TabletToolV2Interface::cursorChanged, this, [this](const TabletCursorSourceV2 &cursor) {
1959             if (auto surfaceCursor = std::get_if<TabletSurfaceCursorV2 *>(&cursor)) {
1960                 // If the cursor is unset, fallback to the cross cursor.
1961                 if ((*surfaceCursor) && (*surfaceCursor)->enteredSerial()) {
1962                     if (!m_surfaceSource) {
1963                         m_surfaceSource = std::make_unique<SurfaceCursorSource>();
1964                     }
1965                     m_surfaceSource->update((*surfaceCursor)->surface(), (*surfaceCursor)->hotspot());
1966                     setSource(m_surfaceSource.get());
1967                     return;
1968                 }
1969             }
1970 
1971             QByteArray shape;
1972             if (auto shapeCursor = std::get_if<QByteArray>(&cursor)) {
1973                 shape = *shapeCursor;
1974             } else {
1975                 shape = QByteArrayLiteral("cross");
1976             }
1977 
1978             static WaylandCursorImage defaultCursor;
1979             if (!m_shapeSource) {
1980                 m_shapeSource = std::make_unique<ShapeCursorSource>();
1981             }
1982             m_shapeSource->setTheme(defaultCursor.theme());
1983             m_shapeSource->setShape(shape);
1984             setSource(m_shapeSource.get());
1985         });
1986     }
1987 
1988 private:
1989     std::unique_ptr<ShapeCursorSource> m_shapeSource;
1990     std::unique_ptr<SurfaceCursorSource> m_surfaceSource;
1991 };
1992 
1993 /**
1994  * Handles input coming from a tablet device (e.g. wacom) often with a pen
1995  */
1996 class TabletInputFilter : public QObject, public InputEventFilter
1997 {
1998 public:
1999     TabletInputFilter()
2000     {
2001         const auto devices = input()->devices();
2002         for (InputDevice *device : devices) {
2003             integrateDevice(device);
2004         }
2005         connect(input(), &InputRedirection::deviceAdded, this, &TabletInputFilter::integrateDevice);
2006         connect(input(), &InputRedirection::deviceRemoved, this, &TabletInputFilter::removeDevice);
2007 
2008         auto tabletNextOutput = new QAction(this);
2009         tabletNextOutput->setProperty("componentName", QStringLiteral("kwin"));
2010         tabletNextOutput->setText(i18n("Move the tablet to the next output"));
2011         tabletNextOutput->setObjectName(QStringLiteral("Move Tablet to Next Output"));
2012         KGlobalAccel::setGlobalShortcut(tabletNextOutput, QList<QKeySequence>());
2013         connect(tabletNextOutput, &QAction::triggered, this, &TabletInputFilter::trackNextOutput);
2014     }
2015 
2016     static TabletSeatV2Interface *findTabletSeat()
2017     {
2018         auto server = waylandServer();
2019         if (!server) {
2020             return nullptr;
2021         }
2022         TabletManagerV2Interface *manager = server->tabletManagerV2();
2023         return manager->seat(findSeat());
2024     }
2025 
2026     void integrateDevice(InputDevice *inputDevice)
2027     {
2028         auto device = qobject_cast<LibInput::Device *>(inputDevice);
2029         if (!device || (!device->isTabletTool() && !device->isTabletPad())) {
2030             return;
2031         }
2032 
2033         TabletSeatV2Interface *tabletSeat = findTabletSeat();
2034         if (!tabletSeat) {
2035             qCCritical(KWIN_CORE) << "Could not find tablet seat";
2036             return;
2037         }
2038         struct udev_device *const udev_device = libinput_device_get_udev_device(device->device());
2039         const char *devnode = udev_device_get_syspath(udev_device);
2040 
2041         auto deviceGroup = libinput_device_get_device_group(device->device());
2042         auto tablet = static_cast<TabletV2Interface *>(libinput_device_group_get_user_data(deviceGroup));
2043         if (!tablet) {
2044             tablet = tabletSeat->addTablet(device->vendor(), device->product(), device->sysName(), device->name(), {QString::fromUtf8(devnode)});
2045             libinput_device_group_set_user_data(deviceGroup, tablet);
2046         }
2047 
2048         if (device->isTabletPad()) {
2049             const int buttonsCount = libinput_device_tablet_pad_get_num_buttons(device->device());
2050             const int ringsCount = libinput_device_tablet_pad_get_num_rings(device->device());
2051             const int stripsCount = libinput_device_tablet_pad_get_num_strips(device->device());
2052             const int modes = libinput_device_tablet_pad_get_num_mode_groups(device->device());
2053 
2054             auto firstGroup = libinput_device_tablet_pad_get_mode_group(device->device(), 0);
2055             tabletSeat->addTabletPad(device->sysName(), device->name(), {QString::fromUtf8(devnode)}, buttonsCount, ringsCount, stripsCount, modes, libinput_tablet_pad_mode_group_get_mode(firstGroup), tablet);
2056         }
2057     }
2058 
2059     static void trackNextOutput()
2060     {
2061         const auto outputs = workspace()->outputs();
2062         if (outputs.isEmpty()) {
2063             return;
2064         }
2065 
2066         int tabletToolCount = 0;
2067         InputDevice *changedDevice = nullptr;
2068         const auto devices = input()->devices();
2069         for (const auto device : devices) {
2070             if (!device->isTabletTool()) {
2071                 continue;
2072             }
2073 
2074             tabletToolCount++;
2075             if (device->outputName().isEmpty()) {
2076                 device->setOutputName(outputs.constFirst()->name());
2077                 changedDevice = device;
2078                 continue;
2079             }
2080 
2081             auto it = std::find_if(outputs.begin(), outputs.end(), [device](const auto &output) {
2082                 return output->name() == device->outputName();
2083             });
2084             ++it;
2085             auto nextOutput = it == outputs.end() ? outputs.first() : *it;
2086             device->setOutputName(nextOutput->name());
2087             changedDevice = device;
2088         }
2089         const QString message = tabletToolCount == 1 ? i18n("Tablet moved to %1", changedDevice->outputName()) : i18n("Tablets switched outputs");
2090         OSD::show(message, QStringLiteral("input-tablet"), 5000);
2091     }
2092 
2093     void removeDevice(InputDevice *inputDevice)
2094     {
2095         auto device = qobject_cast<LibInput::Device *>(inputDevice);
2096         if (device) {
2097             auto deviceGroup = libinput_device_get_device_group(device->device());
2098             libinput_device_group_set_user_data(deviceGroup, nullptr);
2099 
2100             TabletSeatV2Interface *tabletSeat = findTabletSeat();
2101             if (tabletSeat) {
2102                 tabletSeat->removeDevice(device->sysName());
2103             } else {
2104                 qCCritical(KWIN_CORE) << "Could not find tablet to remove" << device->sysName();
2105             }
2106         }
2107     }
2108 
2109     TabletToolV2Interface::Type getType(const TabletToolId &tabletToolId)
2110     {
2111         using Type = TabletToolV2Interface::Type;
2112         switch (tabletToolId.m_toolType) {
2113         case InputRedirection::Pen:
2114             return Type::Pen;
2115         case InputRedirection::Eraser:
2116             return Type::Eraser;
2117         case InputRedirection::Brush:
2118             return Type::Brush;
2119         case InputRedirection::Pencil:
2120             return Type::Pencil;
2121         case InputRedirection::Airbrush:
2122             return Type::Airbrush;
2123         case InputRedirection::Finger:
2124             return Type::Finger;
2125         case InputRedirection::Mouse:
2126             return Type::Mouse;
2127         case InputRedirection::Lens:
2128             return Type::Lens;
2129         case InputRedirection::Totem:
2130             return Type::Totem;
2131         }
2132         return Type::Pen;
2133     }
2134 
2135     TabletToolV2Interface *createTool(const TabletToolId &tabletToolId)
2136     {
2137         TabletSeatV2Interface *tabletSeat = findTabletSeat();
2138 
2139         const auto f = [](InputRedirection::Capability cap) {
2140             switch (cap) {
2141             case InputRedirection::Tilt:
2142                 return TabletToolV2Interface::Tilt;
2143             case InputRedirection::Pressure:
2144                 return TabletToolV2Interface::Pressure;
2145             case InputRedirection::Distance:
2146                 return TabletToolV2Interface::Distance;
2147             case InputRedirection::Rotation:
2148                 return TabletToolV2Interface::Rotation;
2149             case InputRedirection::Slider:
2150                 return TabletToolV2Interface::Slider;
2151             case InputRedirection::Wheel:
2152                 return TabletToolV2Interface::Wheel;
2153             }
2154             return TabletToolV2Interface::Wheel;
2155         };
2156         QList<TabletToolV2Interface::Capability> ifaceCapabilities;
2157         ifaceCapabilities.resize(tabletToolId.m_capabilities.size());
2158         std::transform(tabletToolId.m_capabilities.constBegin(), tabletToolId.m_capabilities.constEnd(), ifaceCapabilities.begin(), f);
2159 
2160         TabletToolV2Interface *tool = tabletSeat->addTool(getType(tabletToolId), tabletToolId.m_serialId, tabletToolId.m_uniqueId, ifaceCapabilities, tabletToolId.deviceSysName);
2161 
2162         const auto cursor = new SurfaceCursor(tool);
2163         Cursors::self()->addCursor(cursor);
2164         m_cursorByTool[tool] = cursor;
2165 
2166         return tool;
2167     }
2168 
2169     bool tabletToolEvent(TabletEvent *event) override
2170     {
2171         if (!workspace()) {
2172             return false;
2173         }
2174 
2175         TabletSeatV2Interface *tabletSeat = findTabletSeat();
2176         if (!tabletSeat) {
2177             qCCritical(KWIN_CORE) << "Could not find tablet manager";
2178             return false;
2179         }
2180         auto tool = tabletSeat->toolByHardwareSerial(event->tabletId().m_serialId, getType(event->tabletId()));
2181         if (!tool) {
2182             tool = createTool(event->tabletId());
2183         }
2184 
2185         // NOTE: tablet will be nullptr as the device is removed (see ::removeDevice) but events from the tool
2186         // may still happen (e.g. Release or ProximityOut events)
2187         auto tablet = static_cast<TabletV2Interface *>(event->tabletId().m_deviceGroupData);
2188 
2189         Window *window = input()->findToplevel(event->globalPosF());
2190         if (!window || !window->surface()) {
2191             return false;
2192         }
2193 
2194         SurfaceInterface *surface = window->surface();
2195         tool->setCurrentSurface(surface);
2196 
2197         if (!tool->isClientSupported() || (tablet && !tablet->isSurfaceSupported(surface))) {
2198             return emulateTabletEvent(event);
2199         }
2200 
2201         switch (event->type()) {
2202         case QEvent::TabletMove: {
2203             const auto pos = window->mapToLocal(event->globalPosF());
2204             tool->sendMotion(pos);
2205             m_cursorByTool[tool]->setPos(event->globalPosF());
2206             break;
2207         }
2208         case QEvent::TabletEnterProximity: {
2209             const QPointF pos = event->globalPosF();
2210             m_cursorByTool[tool]->setPos(pos);
2211             tool->sendProximityIn(tablet);
2212             tool->sendMotion(window->mapToLocal(event->globalPosF()));
2213             break;
2214         }
2215         case QEvent::TabletLeaveProximity:
2216             tool->sendProximityOut();
2217             break;
2218         case QEvent::TabletPress: {
2219             const auto pos = window->mapToLocal(event->globalPosF());
2220             tool->sendMotion(pos);
2221             m_cursorByTool[tool]->setPos(event->globalPosF());
2222             tool->sendDown();
2223             break;
2224         }
2225         case QEvent::TabletRelease:
2226             tool->sendUp();
2227             break;
2228         default:
2229             qCWarning(KWIN_CORE) << "Unexpected tablet event type" << event;
2230             break;
2231         }
2232         const quint32 MAX_VAL = 65535;
2233 
2234         if (tool->hasCapability(TabletToolV2Interface::Pressure)) {
2235             tool->sendPressure(MAX_VAL * event->pressure());
2236         }
2237         if (tool->hasCapability(TabletToolV2Interface::Tilt)) {
2238             tool->sendTilt(event->xTilt(), event->yTilt());
2239         }
2240         if (tool->hasCapability(TabletToolV2Interface::Rotation)) {
2241             tool->sendRotation(event->rotation());
2242         }
2243 
2244         tool->sendFrame(event->timestamp());
2245         return true;
2246     }
2247 
2248     bool emulateTabletEvent(TabletEvent *event)
2249     {
2250         if (!workspace()) {
2251             return false;
2252         }
2253 
2254         switch (event->type()) {
2255         case QEvent::TabletMove:
2256         case QEvent::TabletEnterProximity:
2257             input()->pointer()->processMotionAbsolute(event->globalPosF(), std::chrono::milliseconds(event->timestamp()));
2258             break;
2259         case QEvent::TabletPress:
2260             input()->pointer()->processButton(qtMouseButtonToButton(Qt::LeftButton),
2261                                               InputRedirection::PointerButtonPressed, std::chrono::milliseconds(event->timestamp()));
2262             break;
2263         case QEvent::TabletRelease:
2264             input()->pointer()->processButton(qtMouseButtonToButton(Qt::LeftButton),
2265                                               InputRedirection::PointerButtonReleased, std::chrono::milliseconds(event->timestamp()));
2266             break;
2267         case QEvent::TabletLeaveProximity:
2268             break;
2269         default:
2270             qCWarning(KWIN_CORE) << "Unexpected tablet event type" << event;
2271             break;
2272         }
2273         return true;
2274     }
2275 
2276     bool tabletToolButtonEvent(uint button, bool pressed, const TabletToolId &tabletToolId, std::chrono::microseconds time) override
2277     {
2278         TabletSeatV2Interface *tabletSeat = findTabletSeat();
2279         auto tool = tabletSeat->toolByHardwareSerial(tabletToolId.m_serialId, getType(tabletToolId));
2280         if (!tool) {
2281             tool = createTool(tabletToolId);
2282         }
2283         if (!tool->isClientSupported()) {
2284             return false;
2285         }
2286         tool->sendButton(button, pressed);
2287         return true;
2288     }
2289 
2290     TabletPadV2Interface *findAndAdoptPad(const TabletPadId &tabletPadId) const
2291     {
2292         Window *window = workspace()->activeWindow();
2293         auto seat = findTabletSeat();
2294         if (!window || !window->surface() || !seat->isClientSupported(window->surface()->client())) {
2295             return nullptr;
2296         }
2297 
2298         auto tablet = static_cast<TabletV2Interface *>(tabletPadId.data);
2299         SurfaceInterface *surface = window->surface();
2300         auto pad = tablet->pad();
2301         if (!pad) {
2302             return nullptr;
2303         }
2304         pad->setCurrentSurface(surface, tablet);
2305         return pad;
2306     }
2307 
2308     bool tabletPadButtonEvent(uint button, bool pressed, const TabletPadId &tabletPadId, std::chrono::microseconds time) override
2309     {
2310         auto pad = findAndAdoptPad(tabletPadId);
2311         if (!pad) {
2312             return false;
2313         }
2314         pad->sendButton(time, button, pressed);
2315         return true;
2316     }
2317 
2318     bool tabletPadRingEvent(int number, int angle, bool isFinger, const TabletPadId &tabletPadId, std::chrono::microseconds time) override
2319     {
2320         auto pad = findAndAdoptPad(tabletPadId);
2321         if (!pad) {
2322             return false;
2323         }
2324         auto ring = pad->ring(number);
2325 
2326         ring->sendAngle(angle);
2327         if (isFinger) {
2328             ring->sendSource(TabletPadRingV2Interface::SourceFinger);
2329         }
2330         ring->sendFrame(std::chrono::duration_cast<std::chrono::milliseconds>(time).count());
2331         return true;
2332     }
2333 
2334     bool tabletPadStripEvent(int number, int position, bool isFinger, const TabletPadId &tabletPadId, std::chrono::microseconds time) override
2335     {
2336         auto pad = findAndAdoptPad(tabletPadId);
2337         if (!pad) {
2338             return false;
2339         }
2340         auto strip = pad->strip(number);
2341 
2342         strip->sendPosition(position);
2343         if (isFinger) {
2344             strip->sendSource(TabletPadStripV2Interface::SourceFinger);
2345         }
2346         strip->sendFrame(std::chrono::duration_cast<std::chrono::milliseconds>(time).count());
2347         return true;
2348     }
2349 
2350     QHash<TabletToolV2Interface *, Cursor *> m_cursorByTool;
2351 };
2352 
2353 static AbstractDropHandler *dropHandler(Window *window)
2354 {
2355     auto surface = window->surface();
2356     if (!surface) {
2357         return nullptr;
2358     }
2359     auto seat = waylandServer()->seat();
2360     auto dropTarget = seat->dropHandlerForSurface(surface);
2361     if (dropTarget) {
2362         return dropTarget;
2363     }
2364 
2365     if (qobject_cast<X11Window *>(window) && kwinApp()->xwayland()) {
2366         return kwinApp()->xwayland()->xwlDropHandler();
2367     }
2368 
2369     return nullptr;
2370 }
2371 
2372 class DragAndDropInputFilter : public QObject, public InputEventFilter
2373 {
2374     Q_OBJECT
2375 public:
2376     DragAndDropInputFilter()
2377     {
2378         m_raiseTimer.setSingleShot(true);
2379         m_raiseTimer.setInterval(1000);
2380         connect(&m_raiseTimer, &QTimer::timeout, this, &DragAndDropInputFilter::raiseDragTarget);
2381 
2382         connect(waylandServer()->seat(), &SeatInterface::dragEnded, this, [this] {
2383             if (!m_currentToplevelDragWindow) {
2384                 return;
2385             }
2386             m_currentToplevelDragWindow->setKeepAbove(m_wasKeepAbove);
2387             workspace()->takeActivity(m_currentToplevelDragWindow, Workspace::ActivityFlag::ActivityFocus | Workspace::ActivityFlag::ActivityRaise);
2388             m_currentToplevelDragWindow = nullptr;
2389         });
2390     }
2391 
2392     bool pointerEvent(MouseEvent *event, quint32 nativeButton) override
2393     {
2394         auto seat = waylandServer()->seat();
2395         if (!seat->isDragPointer()) {
2396             return false;
2397         }
2398         if (seat->isDragTouch()) {
2399             return true;
2400         }
2401         seat->setTimestamp(event->timestamp());
2402         switch (event->type()) {
2403         case QEvent::MouseMove: {
2404             const auto pos = input()->globalPointer();
2405 
2406             if (seat->xdgTopleveldrag()) {
2407                 dragToplevel(pos, seat->xdgTopleveldrag());
2408             }
2409 
2410             seat->notifyPointerMotion(pos);
2411 
2412             Window *dragTarget = pickDragTarget(pos);
2413             if (dragTarget) {
2414                 if (dragTarget != m_dragTarget) {
2415                     workspace()->takeActivity(dragTarget, Workspace::ActivityFlag::ActivityFocus);
2416                     m_raiseTimer.start();
2417                 }
2418                 if ((pos - m_lastPos).manhattanLength() > 10) {
2419                     m_lastPos = pos;
2420                     // reset timer to delay raising the window
2421                     m_raiseTimer.start();
2422                 }
2423             }
2424             m_dragTarget = dragTarget;
2425 
2426             if (auto *xwl = kwinApp()->xwayland()) {
2427                 const auto ret = xwl->dragMoveFilter(dragTarget);
2428                 if (ret == Xwl::DragEventReply::Ignore) {
2429                     return false;
2430                 } else if (ret == Xwl::DragEventReply::Take) {
2431                     break;
2432                 }
2433             }
2434 
2435             if (dragTarget) {
2436                 // TODO: consider decorations
2437                 if (dragTarget->surface() != seat->dragSurface()) {
2438                     seat->setDragTarget(dropHandler(dragTarget), dragTarget->surface(), dragTarget->inputTransformation());
2439                 }
2440             } else {
2441                 // no window at that place, if we have a surface we need to reset
2442                 seat->setDragTarget(nullptr, nullptr);
2443                 m_dragTarget = nullptr;
2444             }
2445             break;
2446         }
2447         case QEvent::MouseButtonPress:
2448             seat->notifyPointerButton(nativeButton, PointerButtonState::Pressed);
2449             break;
2450         case QEvent::MouseButtonRelease:
2451             raiseDragTarget();
2452             m_dragTarget = nullptr;
2453             seat->notifyPointerButton(nativeButton, PointerButtonState::Released);
2454             break;
2455         default:
2456             break;
2457         }
2458         // TODO: should we pass through effects?
2459         return true;
2460     }
2461 
2462     bool pointerFrame() override
2463     {
2464         auto seat = waylandServer()->seat();
2465         if (!seat->isDragPointer()) {
2466             return false;
2467         }
2468         if (seat->isDragTouch()) {
2469             return true;
2470         }
2471 
2472         seat->notifyPointerFrame();
2473         return true;
2474     }
2475 
2476     bool touchDown(qint32 id, const QPointF &pos, std::chrono::microseconds time) override
2477     {
2478         auto seat = waylandServer()->seat();
2479         if (seat->isDragPointer()) {
2480             return true;
2481         }
2482         if (!seat->isDragTouch()) {
2483             return false;
2484         }
2485         if (m_touchId != id) {
2486             return true;
2487         }
2488         seat->setTimestamp(time);
2489         seat->notifyTouchDown(id, pos);
2490         m_lastPos = pos;
2491         return true;
2492     }
2493     bool touchMotion(qint32 id, const QPointF &pos, std::chrono::microseconds time) override
2494     {
2495         auto seat = waylandServer()->seat();
2496         if (seat->isDragPointer()) {
2497             return true;
2498         }
2499         if (!seat->isDragTouch()) {
2500             return false;
2501         }
2502         if (m_touchId < 0) {
2503             // We take for now the first id appearing as a move after a drag
2504             // started. We can optimize by specifying the id the drag is
2505             // associated with by implementing a key-value getter in KWayland.
2506             m_touchId = id;
2507         }
2508         if (m_touchId != id) {
2509             return true;
2510         }
2511 
2512         if (seat->xdgTopleveldrag()) {
2513             dragToplevel(pos, seat->xdgTopleveldrag());
2514         }
2515 
2516         seat->setTimestamp(time);
2517         seat->notifyTouchMotion(id, pos);
2518 
2519         if (Window *t = pickDragTarget(pos)) {
2520             // TODO: consider decorations
2521             if (t->surface() != seat->dragSurface()) {
2522                 if ((m_dragTarget = static_cast<Window *>(t->isClient() ? t : nullptr))) {
2523                     workspace()->takeActivity(m_dragTarget, Workspace::ActivityFlag::ActivityFocus);
2524                     m_raiseTimer.start();
2525                 }
2526                 seat->setDragTarget(dropHandler(t), t->surface(), pos, t->inputTransformation());
2527             }
2528             if ((pos - m_lastPos).manhattanLength() > 10) {
2529                 m_lastPos = pos;
2530                 // reset timer to delay raising the window
2531                 m_raiseTimer.start();
2532             }
2533         } else {
2534             // no window at that place, if we have a surface we need to reset
2535             seat->setDragTarget(nullptr, nullptr);
2536             m_dragTarget = nullptr;
2537         }
2538         return true;
2539     }
2540     bool touchUp(qint32 id, std::chrono::microseconds time) override
2541     {
2542         auto seat = waylandServer()->seat();
2543         if (!seat->isDragTouch()) {
2544             return false;
2545         }
2546         seat->setTimestamp(time);
2547         seat->notifyTouchUp(id);
2548         if (m_touchId == id) {
2549             m_touchId = -1;
2550             raiseDragTarget();
2551         }
2552         return true;
2553     }
2554     bool keyEvent(KeyEvent *event) override
2555     {
2556         if (event->key() != Qt::Key_Escape) {
2557             return false;
2558         }
2559 
2560         auto seat = waylandServer()->seat();
2561         if (!seat->isDrag()) {
2562             return false;
2563         }
2564         seat->setTimestamp(event->timestamp());
2565 
2566         seat->cancelDrag();
2567 
2568         return true;
2569     }
2570 
2571 private:
2572     void raiseDragTarget()
2573     {
2574         m_raiseTimer.stop();
2575         if (m_dragTarget) {
2576             workspace()->takeActivity(m_dragTarget, Workspace::ActivityFlag::ActivityRaise);
2577         }
2578     }
2579 
2580     Window *pickDragTarget(const QPointF &pos) const
2581     {
2582         const QList<Window *> stacking = workspace()->stackingOrder();
2583         if (stacking.isEmpty()) {
2584             return nullptr;
2585         }
2586         auto it = stacking.end();
2587         do {
2588             --it;
2589             Window *window = (*it);
2590             if (auto toplevelDrag = waylandServer()->seat()->xdgTopleveldrag(); toplevelDrag && toplevelDrag->toplevel() && toplevelDrag->toplevel()->surface() == window->surface()) {
2591                 continue;
2592             }
2593             if (window->isDeleted()) {
2594                 continue;
2595             }
2596             if (!window->isClient()) {
2597                 continue;
2598             }
2599             if (!window->isOnCurrentActivity() || !window->isOnCurrentDesktop() || window->isMinimized() || window->isHidden() || window->isHiddenByShowDesktop()) {
2600                 continue;
2601             }
2602             if (!window->readyForPainting()) {
2603                 continue;
2604             }
2605             if (window->hitTest(pos)) {
2606                 return window;
2607             }
2608         } while (it != stacking.begin());
2609         return nullptr;
2610     }
2611 
2612     void dragToplevel(const QPointF &pos, const XdgToplevelDragV1Interface *toplevelDrag)
2613     {
2614 
2615         auto window = toplevelDrag->toplevel() ? waylandServer()->findWindow(toplevelDrag->toplevel()->surface()) : nullptr;
2616 
2617         if (m_currentToplevelDragWindow != window) {
2618             if (m_currentToplevelDragWindow) {
2619                 m_currentToplevelDragWindow->setKeepAbove(m_wasKeepAbove);
2620             }
2621             m_currentToplevelDragWindow = window;
2622             if (window) {
2623                 m_wasKeepAbove = window->keepAbove();
2624                 window->setKeepAbove(true);
2625             }
2626         }
2627 
2628         if (window) {
2629             window->move(pos - toplevelDrag->offset());
2630         }
2631     }
2632 
2633     qint32 m_touchId = -1;
2634     QPointF m_lastPos = QPointF(-1, -1);
2635     QPointer<Window> m_dragTarget;
2636     QTimer m_raiseTimer;
2637     QPointer<Window> m_currentToplevelDragWindow = nullptr;
2638     bool m_wasKeepAbove = false;
2639 };
2640 
2641 KWIN_SINGLETON_FACTORY(InputRedirection)
2642 
2643 static const QString s_touchpadComponent = QStringLiteral("kcm_touchpad");
2644 
2645 InputRedirection::InputRedirection(QObject *parent)
2646     : QObject(parent)
2647     , m_keyboard(new KeyboardInputRedirection(this))
2648     , m_pointer(new PointerInputRedirection(this))
2649     , m_tablet(new TabletInputRedirection(this))
2650     , m_touch(new TouchInputRedirection(this))
2651 #if KWIN_BUILD_GLOBALSHORTCUTS
2652     , m_shortcuts(new GlobalShortcutsManager(this))
2653 #endif
2654 {
2655     qRegisterMetaType<InputRedirection::KeyboardKeyState>();
2656     qRegisterMetaType<InputRedirection::PointerButtonState>();
2657     qRegisterMetaType<InputRedirection::PointerAxis>();
2658     setupInputBackends();
2659     connect(kwinApp(), &Application::workspaceCreated, this, &InputRedirection::setupWorkspace);
2660 }
2661 
2662 InputRedirection::~InputRedirection()
2663 {
2664     m_inputBackends.clear();
2665     m_inputDevices.clear();
2666 
2667     s_self = nullptr;
2668 }
2669 
2670 void InputRedirection::installInputEventFilter(InputEventFilter *filter)
2671 {
2672     Q_ASSERT(!m_filters.contains(filter));
2673     m_filters << filter;
2674 }
2675 
2676 void InputRedirection::prependInputEventFilter(InputEventFilter *filter)
2677 {
2678     Q_ASSERT(!m_filters.contains(filter));
2679     m_filters.prepend(filter);
2680 }
2681 
2682 void InputRedirection::uninstallInputEventFilter(InputEventFilter *filter)
2683 {
2684     m_filters.removeOne(filter);
2685 }
2686 
2687 void InputRedirection::installInputEventSpy(InputEventSpy *spy)
2688 {
2689     m_spies << spy;
2690 }
2691 
2692 void InputRedirection::uninstallInputEventSpy(InputEventSpy *spy)
2693 {
2694     m_spies.removeOne(spy);
2695 }
2696 
2697 void InputRedirection::init()
2698 {
2699     m_inputConfigWatcher = KConfigWatcher::create(kwinApp()->inputConfig());
2700     connect(m_inputConfigWatcher.data(), &KConfigWatcher::configChanged,
2701             this, &InputRedirection::handleInputConfigChanged);
2702 #if KWIN_BUILD_GLOBALSHORTCUTS
2703     m_shortcuts->init();
2704 #endif
2705 }
2706 
2707 void InputRedirection::setupWorkspace()
2708 {
2709     connect(workspace(), &Workspace::outputsChanged, this, &InputRedirection::updateScreens);
2710     if (waylandServer()) {
2711         m_keyboard->init();
2712         m_pointer->init();
2713         m_touch->init();
2714         m_tablet->init();
2715 
2716         updateLeds(m_keyboard->xkb()->leds());
2717         connect(m_keyboard, &KeyboardInputRedirection::ledsChanged, this, &InputRedirection::updateLeds);
2718 
2719         setupTouchpadShortcuts();
2720         setupInputFilters();
2721         updateScreens();
2722     }
2723 }
2724 
2725 void InputRedirection::updateScreens()
2726 {
2727     for (const auto &backend : m_inputBackends) {
2728         backend->updateScreens();
2729     }
2730 }
2731 
2732 QObject *InputRedirection::lastInputHandler() const
2733 {
2734     return m_lastInputDevice;
2735 }
2736 
2737 void InputRedirection::setLastInputHandler(QObject *device)
2738 {
2739     m_lastInputDevice = device;
2740 }
2741 
2742 class WindowInteractedSpy : public InputEventSpy
2743 {
2744 public:
2745     void keyEvent(KeyEvent *event) override
2746     {
2747         if (event->isAutoRepeat() || event->type() != QEvent::KeyPress) {
2748             return;
2749         }
2750         update();
2751     }
2752 
2753     void pointerEvent(MouseEvent *event) override
2754     {
2755         if (event->type() != QEvent::MouseButtonPress) {
2756             return;
2757         }
2758         update();
2759     }
2760 
2761     void tabletPadButtonEvent(uint, bool pressed, const TabletPadId &, std::chrono::microseconds time) override
2762     {
2763         if (!pressed) {
2764             return;
2765         }
2766         update();
2767     }
2768 
2769     void tabletToolButtonEvent(uint, bool pressed, const TabletToolId &, std::chrono::microseconds time) override
2770     {
2771         if (!pressed) {
2772             return;
2773         }
2774         update();
2775     }
2776 
2777     void tabletToolEvent(TabletEvent *event) override
2778     {
2779         if (event->type() != QEvent::TabletPress) {
2780             return;
2781         }
2782         update();
2783     }
2784 
2785     void touchDown(qint32, const QPointF &, std::chrono::microseconds time) override
2786     {
2787         update();
2788     }
2789 
2790     void update()
2791     {
2792         auto window = workspace()->activeWindow();
2793         if (!window) {
2794             return;
2795         }
2796         window->setLastUsageSerial(waylandServer()->seat()->display()->serial());
2797     }
2798 };
2799 
2800 class UserActivitySpy : public InputEventSpy
2801 {
2802 public:
2803     void pointerEvent(MouseEvent *event) override
2804     {
2805         notifyActivity();
2806     }
2807     void wheelEvent(WheelEvent *event) override
2808     {
2809         notifyActivity();
2810     }
2811 
2812     void keyEvent(KeyEvent *event) override
2813     {
2814         notifyActivity();
2815     }
2816 
2817     void touchDown(qint32 id, const QPointF &pos, std::chrono::microseconds time) override
2818     {
2819         notifyActivity();
2820     }
2821     void touchMotion(qint32 id, const QPointF &pos, std::chrono::microseconds time) override
2822     {
2823         notifyActivity();
2824     }
2825     void touchUp(qint32 id, std::chrono::microseconds time) override
2826     {
2827         notifyActivity();
2828     }
2829 
2830     void pinchGestureBegin(int fingerCount, std::chrono::microseconds time) override
2831     {
2832         notifyActivity();
2833     }
2834     void pinchGestureUpdate(qreal scale, qreal angleDelta, const QPointF &delta, std::chrono::microseconds time) override
2835     {
2836         notifyActivity();
2837     }
2838     void pinchGestureEnd(std::chrono::microseconds time) override
2839     {
2840         notifyActivity();
2841     }
2842     void pinchGestureCancelled(std::chrono::microseconds time) override
2843     {
2844         notifyActivity();
2845     }
2846 
2847     void swipeGestureBegin(int fingerCount, std::chrono::microseconds time) override
2848     {
2849         notifyActivity();
2850     }
2851     void swipeGestureUpdate(const QPointF &delta, std::chrono::microseconds time) override
2852     {
2853         notifyActivity();
2854     }
2855     void swipeGestureEnd(std::chrono::microseconds time) override
2856     {
2857         notifyActivity();
2858     }
2859     void swipeGestureCancelled(std::chrono::microseconds time) override
2860     {
2861         notifyActivity();
2862     }
2863 
2864     void holdGestureBegin(int fingerCount, std::chrono::microseconds time) override
2865     {
2866         notifyActivity();
2867     }
2868     void holdGestureEnd(std::chrono::microseconds time) override
2869     {
2870         notifyActivity();
2871     }
2872     void holdGestureCancelled(std::chrono::microseconds time) override
2873     {
2874         notifyActivity();
2875     }
2876 
2877     void tabletToolEvent(TabletEvent *event) override
2878     {
2879         notifyActivity();
2880     }
2881     void tabletToolButtonEvent(uint button, bool pressed, const TabletToolId &tabletToolId, std::chrono::microseconds time) override
2882     {
2883         notifyActivity();
2884     }
2885     void tabletPadButtonEvent(uint button, bool pressed, const TabletPadId &tabletPadId, std::chrono::microseconds time) override
2886     {
2887         notifyActivity();
2888     }
2889     void tabletPadStripEvent(int number, int position, bool isFinger, const TabletPadId &tabletPadId, std::chrono::microseconds time) override
2890     {
2891         notifyActivity();
2892     }
2893     void tabletPadRingEvent(int number, int position, bool isFinger, const TabletPadId &tabletPadId, std::chrono::microseconds time) override
2894     {
2895         notifyActivity();
2896     }
2897 
2898 private:
2899     void notifyActivity()
2900     {
2901         input()->simulateUserActivity();
2902     }
2903 };
2904 
2905 void InputRedirection::setupInputFilters()
2906 {
2907     const bool hasGlobalShortcutSupport = waylandServer()->hasGlobalShortcutSupport();
2908     if (kwinApp()->session()->capabilities() & Session::Capability::SwitchTerminal) {
2909         m_virtualTerminalFilter = std::make_unique<VirtualTerminalFilter>();
2910         installInputEventFilter(m_virtualTerminalFilter.get());
2911     }
2912 
2913     m_hideCursorSpy = std::make_unique<HideCursorSpy>();
2914     installInputEventSpy(m_hideCursorSpy.get());
2915 
2916     m_userActivitySpy = std::make_unique<UserActivitySpy>();
2917     installInputEventSpy(m_userActivitySpy.get());
2918 
2919     m_windowInteractedSpy = std::make_unique<WindowInteractedSpy>();
2920     installInputEventSpy(m_windowInteractedSpy.get());
2921 
2922     m_lockscreenFilter = std::make_unique<LockScreenFilter>();
2923     installInputEventFilter(m_lockscreenFilter.get());
2924 
2925     if (hasGlobalShortcutSupport) {
2926         m_screenEdgeFilter = std::make_unique<ScreenEdgeInputFilter>();
2927         installInputEventFilter(m_screenEdgeFilter.get());
2928     }
2929 
2930     m_dragAndDropFilter = std::make_unique<DragAndDropInputFilter>();
2931     installInputEventFilter(m_dragAndDropFilter.get());
2932 
2933     m_windowSelector = std::make_unique<WindowSelectorFilter>();
2934     installInputEventFilter(m_windowSelector.get());
2935 
2936 #if KWIN_BUILD_TABBOX
2937     m_tabboxFilter = std::make_unique<TabBoxInputFilter>();
2938     installInputEventFilter(m_tabboxFilter.get());
2939 #endif
2940 #if KWIN_BUILD_GLOBALSHORTCUTS
2941     if (hasGlobalShortcutSupport) {
2942         m_globalShortcutFilter = std::make_unique<GlobalShortcutFilter>();
2943         installInputEventFilter(m_globalShortcutFilter.get());
2944     }
2945 #endif
2946 
2947     m_effectsFilter = std::make_unique<EffectsFilter>();
2948     installInputEventFilter(m_effectsFilter.get());
2949 
2950     m_interactiveMoveResizeFilter = std::make_unique<MoveResizeFilter>();
2951     installInputEventFilter(m_interactiveMoveResizeFilter.get());
2952 
2953     m_popupFilter = std::make_unique<PopupInputFilter>();
2954     installInputEventFilter(m_popupFilter.get());
2955 
2956     m_decorationFilter = std::make_unique<DecorationEventFilter>();
2957     installInputEventFilter(m_decorationFilter.get());
2958 
2959     m_windowActionFilter = std::make_unique<WindowActionInputFilter>();
2960     installInputEventFilter(m_windowActionFilter.get());
2961 
2962     m_internalWindowFilter = std::make_unique<InternalWindowEventFilter>();
2963     installInputEventFilter(m_internalWindowFilter.get());
2964 
2965     m_inputKeyboardFilter = std::make_unique<InputKeyboardFilter>();
2966     installInputEventFilter(m_inputKeyboardFilter.get());
2967 
2968     m_forwardFilter = std::make_unique<ForwardInputFilter>();
2969     installInputEventFilter(m_forwardFilter.get());
2970 
2971     m_tabletFilter = std::make_unique<TabletInputFilter>();
2972     installInputEventFilter(m_tabletFilter.get());
2973 }
2974 
2975 void InputRedirection::handleInputConfigChanged(const KConfigGroup &group)
2976 {
2977     if (group.name() == QLatin1String("Keyboard")) {
2978         m_keyboard->reconfigure();
2979     }
2980 }
2981 
2982 void InputRedirection::updateLeds(LEDs leds)
2983 {
2984     if (m_leds != leds) {
2985         m_leds = leds;
2986 
2987         for (InputDevice *device : std::as_const(m_inputDevices)) {
2988             device->setLeds(leds);
2989         }
2990     }
2991 }
2992 
2993 void InputRedirection::addInputDevice(InputDevice *device)
2994 {
2995     connect(device, &InputDevice::keyChanged, m_keyboard, &KeyboardInputRedirection::processKey);
2996 
2997     connect(device, &InputDevice::pointerMotionAbsolute,
2998             m_pointer, &PointerInputRedirection::processMotionAbsolute);
2999     connect(device, &InputDevice::pointerMotion,
3000             m_pointer, &PointerInputRedirection::processMotion);
3001     connect(device, &InputDevice::pointerButtonChanged,
3002             m_pointer, &PointerInputRedirection::processButton);
3003     connect(device, &InputDevice::pointerAxisChanged,
3004             m_pointer, &PointerInputRedirection::processAxis);
3005     connect(device, &InputDevice::pointerFrame,
3006             m_pointer, &PointerInputRedirection::processFrame);
3007     connect(device, &InputDevice::pinchGestureBegin,
3008             m_pointer, &PointerInputRedirection::processPinchGestureBegin);
3009     connect(device, &InputDevice::pinchGestureUpdate,
3010             m_pointer, &PointerInputRedirection::processPinchGestureUpdate);
3011     connect(device, &InputDevice::pinchGestureEnd,
3012             m_pointer, &PointerInputRedirection::processPinchGestureEnd);
3013     connect(device, &InputDevice::pinchGestureCancelled,
3014             m_pointer, &PointerInputRedirection::processPinchGestureCancelled);
3015     connect(device, &InputDevice::swipeGestureBegin,
3016             m_pointer, &PointerInputRedirection::processSwipeGestureBegin);
3017     connect(device, &InputDevice::swipeGestureUpdate,
3018             m_pointer, &PointerInputRedirection::processSwipeGestureUpdate);
3019     connect(device, &InputDevice::swipeGestureEnd,
3020             m_pointer, &PointerInputRedirection::processSwipeGestureEnd);
3021     connect(device, &InputDevice::swipeGestureCancelled,
3022             m_pointer, &PointerInputRedirection::processSwipeGestureCancelled);
3023     connect(device, &InputDevice::holdGestureBegin,
3024             m_pointer, &PointerInputRedirection::processHoldGestureBegin);
3025     connect(device, &InputDevice::holdGestureEnd,
3026             m_pointer, &PointerInputRedirection::processHoldGestureEnd);
3027     connect(device, &InputDevice::holdGestureCancelled,
3028             m_pointer, &PointerInputRedirection::processHoldGestureCancelled);
3029 
3030     connect(device, &InputDevice::touchDown, m_touch, &TouchInputRedirection::processDown);
3031     connect(device, &InputDevice::touchUp, m_touch, &TouchInputRedirection::processUp);
3032     connect(device, &InputDevice::touchMotion, m_touch, &TouchInputRedirection::processMotion);
3033     connect(device, &InputDevice::touchCanceled, m_touch, &TouchInputRedirection::cancel);
3034     connect(device, &InputDevice::touchFrame, m_touch, &TouchInputRedirection::frame);
3035 
3036     auto handleSwitchEvent = [this](SwitchEvent::State state, std::chrono::microseconds time, InputDevice *device) {
3037         SwitchEvent event(state, time, device);
3038         processSpies(std::bind(&InputEventSpy::switchEvent, std::placeholders::_1, &event));
3039         processFilters(std::bind(&InputEventFilter::switchEvent, std::placeholders::_1, &event));
3040     };
3041     connect(device, &InputDevice::switchToggledOn, this,
3042             std::bind(handleSwitchEvent, SwitchEvent::State::On, std::placeholders::_1, std::placeholders::_2));
3043     connect(device, &InputDevice::switchToggledOff, this,
3044             std::bind(handleSwitchEvent, SwitchEvent::State::Off, std::placeholders::_1, std::placeholders::_2));
3045 
3046     connect(device, &InputDevice::tabletToolEvent,
3047             m_tablet, &TabletInputRedirection::tabletToolEvent);
3048     connect(device, &InputDevice::tabletToolButtonEvent,
3049             m_tablet, &TabletInputRedirection::tabletToolButtonEvent);
3050     connect(device, &InputDevice::tabletPadButtonEvent,
3051             m_tablet, &TabletInputRedirection::tabletPadButtonEvent);
3052     connect(device, &InputDevice::tabletPadRingEvent,
3053             m_tablet, &TabletInputRedirection::tabletPadRingEvent);
3054     connect(device, &InputDevice::tabletPadStripEvent,
3055             m_tablet, &TabletInputRedirection::tabletPadStripEvent);
3056 
3057     device->setLeds(m_leds);
3058 
3059     m_inputDevices.append(device);
3060     Q_EMIT deviceAdded(device);
3061 
3062     updateAvailableInputDevices();
3063 }
3064 
3065 void InputRedirection::removeInputDevice(InputDevice *device)
3066 {
3067     m_inputDevices.removeOne(device);
3068     Q_EMIT deviceRemoved(device);
3069 
3070     updateAvailableInputDevices();
3071 }
3072 
3073 void InputRedirection::updateAvailableInputDevices()
3074 {
3075     const bool hasKeyboard = std::any_of(m_inputDevices.constBegin(), m_inputDevices.constEnd(), [](InputDevice *device) {
3076         return device->isKeyboard();
3077     });
3078     if (m_hasKeyboard != hasKeyboard) {
3079         m_hasKeyboard = hasKeyboard;
3080         Q_EMIT hasKeyboardChanged(hasKeyboard);
3081     }
3082 
3083     const bool hasAlphaNumericKeyboard = std::any_of(m_inputDevices.constBegin(), m_inputDevices.constEnd(), [](InputDevice *device) {
3084         return device->isAlphaNumericKeyboard();
3085     });
3086     if (m_hasAlphaNumericKeyboard != hasAlphaNumericKeyboard) {
3087         m_hasAlphaNumericKeyboard = hasAlphaNumericKeyboard;
3088         Q_EMIT hasAlphaNumericKeyboardChanged(hasAlphaNumericKeyboard);
3089     }
3090 
3091     const bool hasPointer = std::any_of(m_inputDevices.constBegin(), m_inputDevices.constEnd(), [](InputDevice *device) {
3092         return device->isPointer();
3093     });
3094     if (m_hasPointer != hasPointer) {
3095         m_hasPointer = hasPointer;
3096         Q_EMIT hasPointerChanged(hasPointer);
3097     }
3098 
3099     const bool hasTouch = std::any_of(m_inputDevices.constBegin(), m_inputDevices.constEnd(), [](InputDevice *device) {
3100         return device->isTouch();
3101     });
3102     if (m_hasTouch != hasTouch) {
3103         m_hasTouch = hasTouch;
3104         Q_EMIT hasTouchChanged(hasTouch);
3105     }
3106 
3107     const bool hasTabletModeSwitch = std::any_of(m_inputDevices.constBegin(), m_inputDevices.constEnd(), [](InputDevice *device) {
3108         return device->isTabletModeSwitch();
3109     });
3110     if (m_hasTabletModeSwitch != hasTabletModeSwitch) {
3111         m_hasTabletModeSwitch = hasTabletModeSwitch;
3112         Q_EMIT hasTabletModeSwitchChanged(hasTabletModeSwitch);
3113     }
3114 }
3115 
3116 void InputRedirection::toggleTouchpads()
3117 {
3118     bool changed = false;
3119     m_touchpadsEnabled = !m_touchpadsEnabled;
3120     for (InputDevice *device : std::as_const(m_inputDevices)) {
3121         if (!device->isTouchpad()) {
3122             continue;
3123         }
3124         const bool old = device->isEnabled();
3125         device->setEnabled(m_touchpadsEnabled);
3126         if (old != device->isEnabled()) {
3127             changed = true;
3128         }
3129     }
3130     if (changed) {
3131         // send OSD message
3132         QDBusMessage msg = QDBusMessage::createMethodCall(QStringLiteral("org.kde.plasmashell"),
3133                                                           QStringLiteral("/org/kde/osdService"),
3134                                                           QStringLiteral("org.kde.osdService"),
3135                                                           QStringLiteral("touchpadEnabledChanged"));
3136         msg.setArguments({m_touchpadsEnabled});
3137         QDBusConnection::sessionBus().asyncCall(msg);
3138     }
3139 }
3140 
3141 void InputRedirection::enableTouchpads()
3142 {
3143     if (!m_touchpadsEnabled) {
3144         toggleTouchpads();
3145     }
3146 }
3147 
3148 void InputRedirection::disableTouchpads()
3149 {
3150     if (m_touchpadsEnabled) {
3151         toggleTouchpads();
3152     }
3153 }
3154 
3155 void InputRedirection::addInputBackend(std::unique_ptr<InputBackend> &&inputBackend)
3156 {
3157     connect(inputBackend.get(), &InputBackend::deviceAdded, this, &InputRedirection::addInputDevice);
3158     connect(inputBackend.get(), &InputBackend::deviceRemoved, this, &InputRedirection::removeInputDevice);
3159 
3160     inputBackend->setConfig(kwinApp()->inputConfig());
3161     inputBackend->initialize();
3162 
3163     m_inputBackends.push_back(std::move(inputBackend));
3164 }
3165 
3166 void InputRedirection::setupInputBackends()
3167 {
3168     std::unique_ptr<InputBackend> inputBackend = kwinApp()->outputBackend()->createInputBackend();
3169     if (inputBackend) {
3170         addInputBackend(std::move(inputBackend));
3171     }
3172     if (waylandServer()) {
3173         addInputBackend(std::make_unique<FakeInputBackend>(waylandServer()->display()));
3174     }
3175 }
3176 
3177 void InputRedirection::setupTouchpadShortcuts()
3178 {
3179     QAction *touchpadToggleAction = new QAction(this);
3180     QAction *touchpadOnAction = new QAction(this);
3181     QAction *touchpadOffAction = new QAction(this);
3182 
3183     const QString touchpadDisplayName = i18n("Touchpad");
3184 
3185     touchpadToggleAction->setObjectName(QStringLiteral("Toggle Touchpad"));
3186     touchpadToggleAction->setProperty("componentName", s_touchpadComponent);
3187     touchpadToggleAction->setProperty("componentDisplayName", touchpadDisplayName);
3188     touchpadOnAction->setObjectName(QStringLiteral("Enable Touchpad"));
3189     touchpadOnAction->setProperty("componentName", s_touchpadComponent);
3190     touchpadOnAction->setProperty("componentDisplayName", touchpadDisplayName);
3191     touchpadOffAction->setObjectName(QStringLiteral("Disable Touchpad"));
3192     touchpadOffAction->setProperty("componentName", s_touchpadComponent);
3193     touchpadOffAction->setProperty("componentDisplayName", touchpadDisplayName);
3194     KGlobalAccel::self()->setDefaultShortcut(touchpadToggleAction, QList<QKeySequence>{Qt::Key_TouchpadToggle, Qt::ControlModifier | Qt::MetaModifier | Qt::Key_Zenkaku_Hankaku});
3195     KGlobalAccel::self()->setShortcut(touchpadToggleAction, QList<QKeySequence>{Qt::Key_TouchpadToggle, Qt::ControlModifier | Qt::MetaModifier | Qt::Key_Zenkaku_Hankaku});
3196     KGlobalAccel::self()->setDefaultShortcut(touchpadOnAction, QList<QKeySequence>{Qt::Key_TouchpadOn});
3197     KGlobalAccel::self()->setShortcut(touchpadOnAction, QList<QKeySequence>{Qt::Key_TouchpadOn});
3198     KGlobalAccel::self()->setDefaultShortcut(touchpadOffAction, QList<QKeySequence>{Qt::Key_TouchpadOff});
3199     KGlobalAccel::self()->setShortcut(touchpadOffAction, QList<QKeySequence>{Qt::Key_TouchpadOff});
3200     connect(touchpadToggleAction, &QAction::triggered, this, &InputRedirection::toggleTouchpads);
3201     connect(touchpadOnAction, &QAction::triggered, this, &InputRedirection::enableTouchpads);
3202     connect(touchpadOffAction, &QAction::triggered, this, &InputRedirection::disableTouchpads);
3203 }
3204 
3205 bool InputRedirection::hasAlphaNumericKeyboard()
3206 {
3207     return m_hasAlphaNumericKeyboard;
3208 }
3209 
3210 bool InputRedirection::hasPointer() const
3211 {
3212     return m_hasPointer;
3213 }
3214 
3215 bool InputRedirection::hasTouch() const
3216 {
3217     return m_hasTouch;
3218 }
3219 
3220 bool InputRedirection::hasTabletModeSwitch()
3221 {
3222     return m_hasTabletModeSwitch;
3223 }
3224 
3225 Qt::MouseButtons InputRedirection::qtButtonStates() const
3226 {
3227     return m_pointer->buttons();
3228 }
3229 
3230 void InputRedirection::simulateUserActivity()
3231 {
3232     for (IdleDetector *idleDetector : std::as_const(m_idleDetectors)) {
3233         idleDetector->activity();
3234     }
3235 }
3236 
3237 void InputRedirection::addIdleDetector(IdleDetector *detector)
3238 {
3239     Q_ASSERT(!m_idleDetectors.contains(detector));
3240     detector->setInhibited(!m_idleInhibitors.isEmpty());
3241     m_idleDetectors.append(detector);
3242 }
3243 
3244 void InputRedirection::removeIdleDetector(IdleDetector *detector)
3245 {
3246     m_idleDetectors.removeOne(detector);
3247 }
3248 
3249 QList<Window *> InputRedirection::idleInhibitors() const
3250 {
3251     return m_idleInhibitors;
3252 }
3253 
3254 void InputRedirection::addIdleInhibitor(Window *inhibitor)
3255 {
3256     if (!m_idleInhibitors.contains(inhibitor)) {
3257         m_idleInhibitors.append(inhibitor);
3258         for (IdleDetector *idleDetector : std::as_const(m_idleDetectors)) {
3259             idleDetector->setInhibited(true);
3260         }
3261     }
3262 }
3263 
3264 void InputRedirection::removeIdleInhibitor(Window *inhibitor)
3265 {
3266     if (m_idleInhibitors.removeOne(inhibitor) && m_idleInhibitors.isEmpty()) {
3267         for (IdleDetector *idleDetector : std::as_const(m_idleDetectors)) {
3268             idleDetector->setInhibited(false);
3269         }
3270     }
3271 }
3272 
3273 Window *InputRedirection::findToplevel(const QPointF &pos)
3274 {
3275     if (!Workspace::self()) {
3276         return nullptr;
3277     }
3278     const bool isScreenLocked = waylandServer() && waylandServer()->isScreenLocked();
3279     if (!isScreenLocked) {
3280         // if an effect overrides the cursor we don't have a window to focus
3281         if (effects && effects->isMouseInterception()) {
3282             return nullptr;
3283         }
3284     }
3285     const QList<Window *> &stacking = Workspace::self()->stackingOrder();
3286     if (stacking.isEmpty()) {
3287         return nullptr;
3288     }
3289     auto it = stacking.end();
3290     do {
3291         --it;
3292         Window *window = (*it);
3293         if (window->isDeleted()) {
3294             // a deleted window doesn't get mouse events
3295             continue;
3296         }
3297         if (!window->isOnCurrentActivity() || !window->isOnCurrentDesktop() || window->isMinimized() || window->isHidden() || window->isHiddenByShowDesktop()) {
3298             continue;
3299         }
3300         if (!window->readyForPainting()) {
3301             continue;
3302         }
3303         if (isScreenLocked) {
3304             if (!window->isLockScreen() && !window->isInputMethod() && !window->isLockScreenOverlay()) {
3305                 continue;
3306             }
3307         }
3308         if (window->hitTest(pos)) {
3309             return window;
3310         }
3311     } while (it != stacking.begin());
3312     return nullptr;
3313 }
3314 
3315 Qt::KeyboardModifiers InputRedirection::keyboardModifiers() const
3316 {
3317     return m_keyboard->modifiers();
3318 }
3319 
3320 Qt::KeyboardModifiers InputRedirection::modifiersRelevantForGlobalShortcuts() const
3321 {
3322     return m_keyboard->modifiersRelevantForGlobalShortcuts();
3323 }
3324 
3325 void InputRedirection::registerPointerShortcut(Qt::KeyboardModifiers modifiers, Qt::MouseButton pointerButtons, QAction *action)
3326 {
3327 #if KWIN_BUILD_GLOBALSHORTCUTS
3328     m_shortcuts->registerPointerShortcut(action, modifiers, pointerButtons);
3329 #endif
3330 }
3331 
3332 void InputRedirection::registerAxisShortcut(Qt::KeyboardModifiers modifiers, PointerAxisDirection axis, QAction *action)
3333 {
3334 #if KWIN_BUILD_GLOBALSHORTCUTS
3335     m_shortcuts->registerAxisShortcut(action, modifiers, axis);
3336 #endif
3337 }
3338 
3339 void InputRedirection::registerTouchpadSwipeShortcut(SwipeDirection direction, uint fingerCount, QAction *action, std::function<void(qreal)> cb)
3340 {
3341 #if KWIN_BUILD_GLOBALSHORTCUTS
3342     m_shortcuts->registerTouchpadSwipe(direction, fingerCount, action, cb);
3343 #endif
3344 }
3345 
3346 void InputRedirection::registerTouchpadPinchShortcut(PinchDirection direction, uint fingerCount, QAction *onUp, std::function<void(qreal)> progressCallback)
3347 {
3348 #if KWIN_BUILD_GLOBALSHORTCUTS
3349     m_shortcuts->registerTouchpadPinch(direction, fingerCount, onUp, progressCallback);
3350 #endif
3351 }
3352 
3353 void InputRedirection::registerGlobalAccel(KGlobalAccelInterface *interface)
3354 {
3355 #if KWIN_BUILD_GLOBALSHORTCUTS
3356     m_shortcuts->setKGlobalAccelInterface(interface);
3357 #endif
3358 }
3359 
3360 void InputRedirection::registerTouchscreenSwipeShortcut(SwipeDirection direction, uint fingerCount, QAction *action, std::function<void(qreal)> progressCallback)
3361 {
3362 #if KWIN_BUILD_GLOBALSHORTCUTS
3363     m_shortcuts->registerTouchscreenSwipe(direction, fingerCount, action, progressCallback);
3364 #endif
3365 }
3366 
3367 void InputRedirection::forceRegisterTouchscreenSwipeShortcut(SwipeDirection direction, uint fingerCount, QAction *action, std::function<void(qreal)> progressCallback)
3368 {
3369 #if KWIN_BUILD_GLOBALSHORTCUTS
3370     m_shortcuts->forceRegisterTouchscreenSwipe(direction, fingerCount, action, progressCallback);
3371 #endif
3372 }
3373 
3374 void InputRedirection::warpPointer(const QPointF &pos)
3375 {
3376     m_pointer->warp(pos);
3377 }
3378 
3379 bool InputRedirection::supportsPointerWarping() const
3380 {
3381     return m_pointer->supportsWarping();
3382 }
3383 
3384 QPointF InputRedirection::globalPointer() const
3385 {
3386     return m_pointer->pos();
3387 }
3388 
3389 void InputRedirection::startInteractiveWindowSelection(std::function<void(Window *)> callback, const QByteArray &cursorName)
3390 {
3391     if (!m_windowSelector || m_windowSelector->isActive()) {
3392         callback(nullptr);
3393         return;
3394     }
3395     m_windowSelector->start(callback);
3396     m_pointer->setWindowSelectionCursor(cursorName);
3397 }
3398 
3399 void InputRedirection::startInteractivePositionSelection(std::function<void(const QPoint &)> callback)
3400 {
3401     if (!m_windowSelector || m_windowSelector->isActive()) {
3402         callback(QPoint(-1, -1));
3403         return;
3404     }
3405     m_windowSelector->start(callback);
3406     m_pointer->setWindowSelectionCursor(QByteArray());
3407 }
3408 
3409 bool InputRedirection::isSelectingWindow() const
3410 {
3411     return m_windowSelector ? m_windowSelector->isActive() : false;
3412 }
3413 
3414 InputDeviceHandler::InputDeviceHandler(InputRedirection *input)
3415     : QObject(input)
3416 {
3417 }
3418 
3419 InputDeviceHandler::~InputDeviceHandler() = default;
3420 
3421 void InputDeviceHandler::init()
3422 {
3423     connect(workspace(), &Workspace::stackingOrderChanged, this, &InputDeviceHandler::update);
3424     connect(workspace(), &Workspace::windowMinimizedChanged, this, &InputDeviceHandler::update);
3425     connect(VirtualDesktopManager::self(), &VirtualDesktopManager::currentChanged, this, &InputDeviceHandler::update);
3426 }
3427 
3428 bool InputDeviceHandler::setHover(Window *window)
3429 {
3430     if (m_hover.window == window) {
3431         return false;
3432     }
3433     auto old = m_hover.window;
3434     disconnect(m_hover.surfaceCreatedConnection);
3435     m_hover.surfaceCreatedConnection = QMetaObject::Connection();
3436 
3437     m_hover.window = window;
3438     return true;
3439 }
3440 
3441 void InputDeviceHandler::setFocus(Window *window)
3442 {
3443     if (m_focus.window == window) {
3444         return;
3445     }
3446 
3447     Window *oldFocus = m_focus.window;
3448     if (oldFocus) {
3449         disconnect(oldFocus, &Window::closed, this, &InputDeviceHandler::update);
3450     }
3451 
3452     m_focus.window = window;
3453     if (window) {
3454         connect(window, &Window::closed, this, &InputDeviceHandler::update);
3455     }
3456 
3457     focusUpdate(oldFocus, window);
3458 }
3459 
3460 void InputDeviceHandler::setDecoration(Decoration::DecoratedClientImpl *decoration)
3461 {
3462     if (m_focus.decoration != decoration) {
3463         auto oldDeco = m_focus.decoration;
3464         m_focus.decoration = decoration;
3465         cleanupDecoration(oldDeco.data(), m_focus.decoration.data());
3466         Q_EMIT decorationChanged();
3467     }
3468 }
3469 
3470 void InputDeviceHandler::updateFocus()
3471 {
3472     Window *focus = m_hover.window;
3473 
3474     if (m_focus.decoration) {
3475         focus = nullptr;
3476     } else if (m_hover.window && !m_hover.window->surface() && !m_hover.window->isInternal()) {
3477         // The surface has not yet been created (special XWayland case).
3478         // Therefore listen for its creation.
3479         if (!m_hover.surfaceCreatedConnection) {
3480             m_hover.surfaceCreatedConnection = connect(m_hover.window, &Window::surfaceChanged,
3481                                                        this, &InputDeviceHandler::update);
3482         }
3483         focus = nullptr;
3484     }
3485 
3486     setFocus(focus);
3487 }
3488 
3489 void InputDeviceHandler::updateDecoration()
3490 {
3491     Decoration::DecoratedClientImpl *decoration = nullptr;
3492     auto hover = m_hover.window.data();
3493     if (hover && hover->decoratedClient()) {
3494         if (!hover->clientGeometry().toRect().contains(flooredPoint(position()))) {
3495             // input device above decoration
3496             decoration = hover->decoratedClient();
3497         }
3498     }
3499 
3500     setDecoration(decoration);
3501 }
3502 
3503 void InputDeviceHandler::update()
3504 {
3505     if (!m_inited) {
3506         return;
3507     }
3508 
3509     Window *window = nullptr;
3510     if (positionValid()) {
3511         window = input()->findToplevel(position());
3512     }
3513     // Always set the window at the position of the input device.
3514     setHover(window);
3515 
3516     if (focusUpdatesBlocked()) {
3517         workspace()->updateFocusMousePosition(position());
3518         return;
3519     }
3520 
3521     updateDecoration();
3522     updateFocus();
3523 
3524     workspace()->updateFocusMousePosition(position());
3525 }
3526 
3527 Window *InputDeviceHandler::hover() const
3528 {
3529     return m_hover.window.data();
3530 }
3531 
3532 Window *InputDeviceHandler::focus() const
3533 {
3534     return m_focus.window.data();
3535 }
3536 
3537 Decoration::DecoratedClientImpl *InputDeviceHandler::decoration() const
3538 {
3539     return m_focus.decoration;
3540 }
3541 
3542 } // namespace
3543 
3544 #include "input.moc"
3545 
3546 #include "moc_input.cpp"