1 /*
2     KWin - the KDE window manager
3     This file is part of the KDE project.
4 
5     SPDX-FileCopyrightText: 2013 Martin Gräßlin <mgraesslin@kde.org>
6     SPDX-FileCopyrightText: 2018 Roman Gilg <subdiff@gmail.com>
7     SPDX-FileCopyrightText: 2019 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
8 
9     SPDX-License-Identifier: GPL-2.0-or-later
10 */
11 #include "input.h"
12 #include "effects.h"
13 #include "gestures.h"
14 #include "globalshortcuts.h"
15 #include "input_event.h"
16 #include "input_event_spy.h"
17 #include "keyboard_input.h"
18 #include "main.h"
19 #include "pointer_input.h"
20 #include "session.h"
21 #include "tablet_input.h"
22 #include "touch_hide_cursor_spy.h"
23 #include "touch_input.h"
24 #include "x11client.h"
25 #ifdef KWIN_BUILD_TABBOX
26 #include "tabbox/tabbox.h"
27 #endif
28 #include "internal_client.h"
29 #include "libinput/connection.h"
30 #include "libinput/device.h"
31 #include "platform.h"
32 #include "popup_input_filter.h"
33 #include "screenedge.h"
34 #include "screens.h"
35 #include "unmanaged.h"
36 #include "virtualdesktops.h"
37 #include "wayland_server.h"
38 #include "workspace.h"
39 #include "xwl/xwayland_interface.h"
40 #include "cursor.h"
41 #include <KDecoration2/Decoration>
42 #include <KGlobalAccel>
43 #include <KLocalizedString>
44 #include <KWaylandServer/display.h>
45 #include <KWaylandServer/fakeinput_interface.h>
46 #include <KWaylandServer/relativepointer_v1_interface.h>
47 #include <KWaylandServer/seat_interface.h>
48 #include <KWaylandServer/shmclientbuffer.h>
49 #include <KWaylandServer/surface_interface.h>
50 #include <KWaylandServer/tablet_v2_interface.h>
51 #include <KWaylandServer/keyboard_interface.h>
52 #include <decorations/decoratedclient.h>
53 
54 //screenlocker
55 #include <KScreenLocker/KsldApp>
56 // Qt
57 #include <QKeyEvent>
58 #include <QThread>
59 #include <qpa/qwindowsysteminterface.h>
60 
61 #include <xkbcommon/xkbcommon.h>
62 #include <cmath>
63 
64 namespace KWin
65 {
66 
kwinAxisSourceToKWaylandAxisSource(InputRedirection::PointerAxisSource source)67 static KWaylandServer::PointerAxisSource kwinAxisSourceToKWaylandAxisSource(InputRedirection::PointerAxisSource source)
68 {
69     switch (source) {
70     case KWin::InputRedirection::PointerAxisSourceWheel:
71         return KWaylandServer::PointerAxisSource::Wheel;
72     case KWin::InputRedirection::PointerAxisSourceFinger:
73         return KWaylandServer::PointerAxisSource::Finger;
74     case KWin::InputRedirection::PointerAxisSourceContinuous:
75         return KWaylandServer::PointerAxisSource::Continuous;
76     case KWin::InputRedirection::PointerAxisSourceWheelTilt:
77         return KWaylandServer::PointerAxisSource::WheelTilt;
78     case KWin::InputRedirection::PointerAxisSourceUnknown:
79     default:
80         return KWaylandServer::PointerAxisSource::Unknown;
81     }
82 }
83 
84 InputEventFilter::InputEventFilter() = default;
85 
~InputEventFilter()86 InputEventFilter::~InputEventFilter()
87 {
88     if (input()) {
89         input()->uninstallInputEventFilter(this);
90     }
91 }
92 
pointerEvent(QMouseEvent * event,quint32 nativeButton)93 bool InputEventFilter::pointerEvent(QMouseEvent *event, quint32 nativeButton)
94 {
95     Q_UNUSED(event)
96     Q_UNUSED(nativeButton)
97     return false;
98 }
99 
wheelEvent(QWheelEvent * event)100 bool InputEventFilter::wheelEvent(QWheelEvent *event)
101 {
102     Q_UNUSED(event)
103     return false;
104 }
105 
keyEvent(QKeyEvent * event)106 bool InputEventFilter::keyEvent(QKeyEvent *event)
107 {
108     Q_UNUSED(event)
109     return false;
110 }
111 
touchDown(qint32 id,const QPointF & point,quint32 time)112 bool InputEventFilter::touchDown(qint32 id, const QPointF &point, quint32 time)
113 {
114     Q_UNUSED(id)
115     Q_UNUSED(point)
116     Q_UNUSED(time)
117     return false;
118 }
119 
touchMotion(qint32 id,const QPointF & point,quint32 time)120 bool InputEventFilter::touchMotion(qint32 id, const QPointF &point, quint32 time)
121 {
122     Q_UNUSED(id)
123     Q_UNUSED(point)
124     Q_UNUSED(time)
125     return false;
126 }
127 
touchUp(qint32 id,quint32 time)128 bool InputEventFilter::touchUp(qint32 id, quint32 time)
129 {
130     Q_UNUSED(id)
131     Q_UNUSED(time)
132     return false;
133 }
134 
pinchGestureBegin(int fingerCount,quint32 time)135 bool InputEventFilter::pinchGestureBegin(int fingerCount, quint32 time)
136 {
137     Q_UNUSED(fingerCount)
138     Q_UNUSED(time)
139     return false;
140 }
141 
pinchGestureUpdate(qreal scale,qreal angleDelta,const QSizeF & delta,quint32 time)142 bool InputEventFilter::pinchGestureUpdate(qreal scale, qreal angleDelta, const QSizeF &delta, quint32 time)
143 {
144     Q_UNUSED(scale)
145     Q_UNUSED(angleDelta)
146     Q_UNUSED(delta)
147     Q_UNUSED(time)
148     return false;
149 }
150 
pinchGestureEnd(quint32 time)151 bool InputEventFilter::pinchGestureEnd(quint32 time)
152 {
153     Q_UNUSED(time)
154     return false;
155 }
156 
pinchGestureCancelled(quint32 time)157 bool InputEventFilter::pinchGestureCancelled(quint32 time)
158 {
159     Q_UNUSED(time)
160     return false;
161 }
162 
swipeGestureBegin(int fingerCount,quint32 time)163 bool InputEventFilter::swipeGestureBegin(int fingerCount, quint32 time)
164 {
165     Q_UNUSED(fingerCount)
166     Q_UNUSED(time)
167     return false;
168 }
169 
swipeGestureUpdate(const QSizeF & delta,quint32 time)170 bool InputEventFilter::swipeGestureUpdate(const QSizeF &delta, quint32 time)
171 {
172     Q_UNUSED(delta)
173     Q_UNUSED(time)
174     return false;
175 }
176 
swipeGestureEnd(quint32 time)177 bool InputEventFilter::swipeGestureEnd(quint32 time)
178 {
179     Q_UNUSED(time)
180     return false;
181 }
182 
swipeGestureCancelled(quint32 time)183 bool InputEventFilter::swipeGestureCancelled(quint32 time)
184 {
185     Q_UNUSED(time)
186     return false;
187 }
188 
switchEvent(SwitchEvent * event)189 bool InputEventFilter::switchEvent(SwitchEvent *event)
190 {
191     Q_UNUSED(event)
192     return false;
193 }
194 
tabletToolEvent(TabletEvent * event)195 bool InputEventFilter::tabletToolEvent(TabletEvent *event)
196 {
197     Q_UNUSED(event)
198     return false;
199 }
200 
tabletToolButtonEvent(uint button,bool pressed,const TabletToolId & tabletId)201 bool InputEventFilter::tabletToolButtonEvent(uint button, bool pressed, const TabletToolId &tabletId)
202 {
203     Q_UNUSED(button)
204     Q_UNUSED(pressed)
205     Q_UNUSED(tabletId)
206     return false;
207 }
208 
tabletPadButtonEvent(uint button,bool pressed,const TabletPadId & tabletPadId)209 bool InputEventFilter::tabletPadButtonEvent(uint button, bool pressed, const TabletPadId &tabletPadId)
210 {
211     Q_UNUSED(button)
212     Q_UNUSED(pressed)
213     Q_UNUSED(tabletPadId)
214     return false;
215 }
216 
tabletPadStripEvent(int number,int position,bool isFinger,const TabletPadId & tabletPadId)217 bool InputEventFilter::tabletPadStripEvent(int number, int position, bool isFinger, const TabletPadId &tabletPadId)
218 {
219     Q_UNUSED(number)
220     Q_UNUSED(position)
221     Q_UNUSED(isFinger)
222     Q_UNUSED(tabletPadId)
223     return false;
224 }
225 
tabletPadRingEvent(int number,int position,bool isFinger,const TabletPadId & tabletPadId)226 bool InputEventFilter::tabletPadRingEvent(int number, int position, bool isFinger, const TabletPadId &tabletPadId)
227 {
228     Q_UNUSED(number)
229     Q_UNUSED(position)
230     Q_UNUSED(isFinger)
231     Q_UNUSED(tabletPadId)
232     return false;
233 }
234 
passToWaylandServer(QKeyEvent * event)235 void InputEventFilter::passToWaylandServer(QKeyEvent *event)
236 {
237     Q_ASSERT(waylandServer());
238     if (event->isAutoRepeat()) {
239         return;
240     }
241 
242     KWaylandServer::SeatInterface *seat = waylandServer()->seat();
243     switch (event->type()) {
244     case QEvent::KeyPress:
245         seat->notifyKeyboardKey(event->nativeScanCode(), KWaylandServer::KeyboardKeyState::Pressed);
246         break;
247     case QEvent::KeyRelease:
248         seat->notifyKeyboardKey(event->nativeScanCode(), KWaylandServer::KeyboardKeyState::Released);
249         break;
250     default:
251         break;
252     }
253 }
254 
255 class VirtualTerminalFilter : public InputEventFilter {
256 public:
keyEvent(QKeyEvent * event)257     bool keyEvent(QKeyEvent *event) override {
258         // really on press and not on release? X11 switches on press.
259         if (event->type() == QEvent::KeyPress && !event->isAutoRepeat()) {
260             const xkb_keysym_t keysym = event->nativeVirtualKey();
261             if (keysym >= XKB_KEY_XF86Switch_VT_1 && keysym <= XKB_KEY_XF86Switch_VT_12) {
262                 kwinApp()->platform()->session()->switchTo(keysym - XKB_KEY_XF86Switch_VT_1 + 1);
263                 return true;
264             }
265         }
266         return false;
267     }
268 };
269 
270 class TerminateServerFilter : public InputEventFilter {
271 public:
keyEvent(QKeyEvent * event)272     bool keyEvent(QKeyEvent *event) override {
273         if (event->type() == QEvent::KeyPress && !event->isAutoRepeat()) {
274             if (event->nativeVirtualKey() == XKB_KEY_Terminate_Server) {
275                 qCWarning(KWIN_CORE) << "Request to terminate server";
276                 QMetaObject::invokeMethod(QCoreApplication::instance(), &QCoreApplication::quit, Qt::QueuedConnection);
277                 return true;
278             }
279         }
280         return false;
281     }
282 };
283 
284 class LockScreenFilter : public InputEventFilter {
285 public:
pointerEvent(QMouseEvent * event,quint32 nativeButton)286     bool pointerEvent(QMouseEvent *event, quint32 nativeButton) override {
287         if (!waylandServer()->isScreenLocked()) {
288             return false;
289         }
290 
291         auto client = qobject_cast<AbstractClient *>(input()->findToplevel(event->globalPos()));
292         if (client && client->isLockScreen()) {
293             workspace()->activateClient(client);
294         }
295 
296         auto seat = waylandServer()->seat();
297         seat->setTimestamp(event->timestamp());
298         if (event->type() == QEvent::MouseMove) {
299             if (pointerSurfaceAllowed()) {
300                 // TODO: should the pointer position always stay in sync, i.e. not do the check?
301                 seat->notifyPointerMotion(event->screenPos().toPoint());
302                 seat->notifyPointerFrame();
303             }
304         } else if (event->type() == QEvent::MouseButtonPress || event->type() == QEvent::MouseButtonRelease) {
305             if (pointerSurfaceAllowed()) {
306                 // TODO: can we leak presses/releases here when we move the mouse in between from an allowed surface to
307                 //       disallowed one or vice versa?
308                 const auto state = event->type() == QEvent::MouseButtonPress
309                     ? KWaylandServer::PointerButtonState::Pressed
310                     : KWaylandServer::PointerButtonState::Released;
311                 seat->notifyPointerButton(nativeButton, state);
312                 seat->notifyPointerFrame();
313             }
314         }
315         return true;
316     }
wheelEvent(QWheelEvent * event)317     bool wheelEvent(QWheelEvent *event) override {
318         if (!waylandServer()->isScreenLocked()) {
319             return false;
320         }
321         auto seat = waylandServer()->seat();
322         if (pointerSurfaceAllowed()) {
323             const WheelEvent *wheelEvent = static_cast<WheelEvent *>(event);
324             seat->setTimestamp(wheelEvent->timestamp());
325             seat->notifyPointerAxis(wheelEvent->orientation(), wheelEvent->delta(),
326                                     wheelEvent->discreteDelta(),
327                                     kwinAxisSourceToKWaylandAxisSource(wheelEvent->axisSource()));
328             seat->notifyPointerFrame();
329         }
330         return true;
331     }
keyEvent(QKeyEvent * event)332     bool keyEvent(QKeyEvent * event) override {
333         if (!waylandServer()->isScreenLocked()) {
334             return false;
335         }
336         if (event->isAutoRepeat()) {
337             // wayland client takes care of it
338             return true;
339         }
340         // send event to KSldApp for global accel
341         // if event is set to accepted it means a whitelisted shortcut was triggered
342         // in that case we filter it out and don't process it further
343         event->setAccepted(false);
344         QCoreApplication::sendEvent(ScreenLocker::KSldApp::self(), event);
345         if (event->isAccepted()) {
346             return true;
347         }
348 
349         // continue normal processing
350         input()->keyboard()->update();
351         auto seat = waylandServer()->seat();
352         seat->setTimestamp(event->timestamp());
353         if (!keyboardSurfaceAllowed()) {
354             // don't pass event to seat
355             return true;
356         }
357         switch (event->type()) {
358         case QEvent::KeyPress:
359             seat->notifyKeyboardKey(event->nativeScanCode(), KWaylandServer::KeyboardKeyState::Pressed);
360             break;
361         case QEvent::KeyRelease:
362             seat->notifyKeyboardKey(event->nativeScanCode(), KWaylandServer::KeyboardKeyState::Released);
363             break;
364         default:
365             break;
366         }
367         return true;
368     }
touchDown(qint32 id,const QPointF & pos,quint32 time)369     bool touchDown(qint32 id, const QPointF &pos, quint32 time) override {
370         if (!waylandServer()->isScreenLocked()) {
371             return false;
372         }
373         auto seat = waylandServer()->seat();
374         seat->setTimestamp(time);
375         if (touchSurfaceAllowed()) {
376             seat->notifyTouchDown(id, pos);
377         }
378         return true;
379     }
touchMotion(qint32 id,const QPointF & pos,quint32 time)380     bool touchMotion(qint32 id, const QPointF &pos, quint32 time) override {
381         if (!waylandServer()->isScreenLocked()) {
382             return false;
383         }
384         auto seat = waylandServer()->seat();
385         seat->setTimestamp(time);
386         if (touchSurfaceAllowed()) {
387             seat->notifyTouchMotion(id, pos);
388         }
389         return true;
390     }
touchUp(qint32 id,quint32 time)391     bool touchUp(qint32 id, quint32 time) override {
392         if (!waylandServer()->isScreenLocked()) {
393             return false;
394         }
395         auto seat = waylandServer()->seat();
396         seat->setTimestamp(time);
397         if (touchSurfaceAllowed()) {
398             seat->notifyTouchUp(id);
399         }
400         return true;
401     }
pinchGestureBegin(int fingerCount,quint32 time)402     bool pinchGestureBegin(int fingerCount, quint32 time) override {
403         Q_UNUSED(fingerCount)
404         Q_UNUSED(time)
405         // no touchpad multi-finger gestures on lock screen
406         return waylandServer()->isScreenLocked();
407     }
pinchGestureUpdate(qreal scale,qreal angleDelta,const QSizeF & delta,quint32 time)408     bool pinchGestureUpdate(qreal scale, qreal angleDelta, const QSizeF &delta, quint32 time) override {
409         Q_UNUSED(scale)
410         Q_UNUSED(angleDelta)
411         Q_UNUSED(delta)
412         Q_UNUSED(time)
413         // no touchpad multi-finger gestures on lock screen
414         return waylandServer()->isScreenLocked();
415     }
pinchGestureEnd(quint32 time)416     bool pinchGestureEnd(quint32 time) override {
417         Q_UNUSED(time)
418         // no touchpad multi-finger gestures on lock screen
419         return waylandServer()->isScreenLocked();
420     }
pinchGestureCancelled(quint32 time)421     bool pinchGestureCancelled(quint32 time) override {
422         Q_UNUSED(time)
423         // no touchpad multi-finger gestures on lock screen
424         return waylandServer()->isScreenLocked();
425     }
426 
swipeGestureBegin(int fingerCount,quint32 time)427     bool swipeGestureBegin(int fingerCount, quint32 time) override {
428         Q_UNUSED(fingerCount)
429         Q_UNUSED(time)
430         // no touchpad multi-finger gestures on lock screen
431         return waylandServer()->isScreenLocked();
432     }
swipeGestureUpdate(const QSizeF & delta,quint32 time)433     bool swipeGestureUpdate(const QSizeF &delta, quint32 time) override {
434         Q_UNUSED(delta)
435         Q_UNUSED(time)
436         // no touchpad multi-finger gestures on lock screen
437         return waylandServer()->isScreenLocked();
438     }
swipeGestureEnd(quint32 time)439     bool swipeGestureEnd(quint32 time) override {
440         Q_UNUSED(time)
441         // no touchpad multi-finger gestures on lock screen
442         return waylandServer()->isScreenLocked();
443     }
swipeGestureCancelled(quint32 time)444     bool swipeGestureCancelled(quint32 time) override {
445         Q_UNUSED(time)
446         // no touchpad multi-finger gestures on lock screen
447         return waylandServer()->isScreenLocked();
448     }
449 private:
surfaceAllowed(KWaylandServer::SurfaceInterface * (KWaylandServer::SeatInterface::* method)()const) const450     bool surfaceAllowed(KWaylandServer::SurfaceInterface *(KWaylandServer::SeatInterface::*method)() const) const {
451         if (KWaylandServer::SurfaceInterface *s = (waylandServer()->seat()->*method)()) {
452             if (Toplevel *t = waylandServer()->findClient(s)) {
453                 return t->isLockScreen() || t->isInputMethod();
454             }
455             return false;
456         }
457         return true;
458     }
pointerSurfaceAllowed() const459     bool pointerSurfaceAllowed() const {
460         return surfaceAllowed(&KWaylandServer::SeatInterface::focusedPointerSurface);
461     }
keyboardSurfaceAllowed() const462     bool keyboardSurfaceAllowed() const {
463         return surfaceAllowed(&KWaylandServer::SeatInterface::focusedKeyboardSurface);
464     }
touchSurfaceAllowed() const465     bool touchSurfaceAllowed() const {
466         return surfaceAllowed(&KWaylandServer::SeatInterface::focusedTouchSurface);
467     }
468 };
469 
470 class EffectsFilter : public InputEventFilter {
471 public:
pointerEvent(QMouseEvent * event,quint32 nativeButton)472     bool pointerEvent(QMouseEvent *event, quint32 nativeButton) override {
473         Q_UNUSED(nativeButton)
474         if (!effects) {
475             return false;
476         }
477         return static_cast<EffectsHandlerImpl*>(effects)->checkInputWindowEvent(event);
478     }
wheelEvent(QWheelEvent * event)479     bool wheelEvent(QWheelEvent *event) override {
480         if (!effects) {
481             return false;
482         }
483         return static_cast<EffectsHandlerImpl*>(effects)->checkInputWindowEvent(event);
484     }
keyEvent(QKeyEvent * event)485     bool keyEvent(QKeyEvent *event) override {
486         if (!effects || !static_cast< EffectsHandlerImpl* >(effects)->hasKeyboardGrab()) {
487             return false;
488         }
489         waylandServer()->seat()->setFocusedKeyboardSurface(nullptr);
490         passToWaylandServer(event);
491         static_cast< EffectsHandlerImpl* >(effects)->grabbedKeyboardEvent(event);
492         return true;
493     }
touchDown(qint32 id,const QPointF & pos,quint32 time)494     bool touchDown(qint32 id, const QPointF &pos, quint32 time) override {
495         if (!effects) {
496             return false;
497         }
498         return static_cast< EffectsHandlerImpl* >(effects)->touchDown(id, pos, time);
499     }
touchMotion(qint32 id,const QPointF & pos,quint32 time)500     bool touchMotion(qint32 id, const QPointF &pos, quint32 time) override {
501         if (!effects) {
502             return false;
503         }
504         return static_cast< EffectsHandlerImpl* >(effects)->touchMotion(id, pos, time);
505     }
touchUp(qint32 id,quint32 time)506     bool touchUp(qint32 id, quint32 time) override {
507         if (!effects) {
508             return false;
509         }
510         return static_cast< EffectsHandlerImpl* >(effects)->touchUp(id, time);
511     }
512 };
513 
514 class MoveResizeFilter : public InputEventFilter {
515 public:
pointerEvent(QMouseEvent * event,quint32 nativeButton)516     bool pointerEvent(QMouseEvent *event, quint32 nativeButton) override {
517         Q_UNUSED(nativeButton)
518         AbstractClient *c = workspace()->moveResizeClient();
519         if (!c) {
520             return false;
521         }
522         switch (event->type()) {
523         case QEvent::MouseMove:
524             c->updateInteractiveMoveResize(event->screenPos().toPoint());
525             break;
526         case QEvent::MouseButtonRelease:
527             if (event->buttons() == Qt::NoButton) {
528                 c->endInteractiveMoveResize();
529             }
530             break;
531         default:
532             break;
533         }
534         return true;
535     }
wheelEvent(QWheelEvent * event)536     bool wheelEvent(QWheelEvent *event) override {
537         Q_UNUSED(event)
538         // filter out while moving a window
539         return workspace()->moveResizeClient() != nullptr;
540     }
keyEvent(QKeyEvent * event)541     bool keyEvent(QKeyEvent *event) override {
542         AbstractClient *c = workspace()->moveResizeClient();
543         if (!c) {
544             return false;
545         }
546         if (event->type() == QEvent::KeyPress) {
547             c->keyPressEvent(event->key() | event->modifiers());
548             if (c->isInteractiveMove() || c->isInteractiveResize()) {
549                 // only update if mode didn't end
550                 c->updateInteractiveMoveResize(input()->globalPointer());
551             }
552         }
553         return true;
554     }
555 
touchDown(qint32 id,const QPointF & pos,quint32 time)556     bool touchDown(qint32 id, const QPointF &pos, quint32 time) override {
557         Q_UNUSED(id)
558         Q_UNUSED(pos)
559         Q_UNUSED(time)
560         AbstractClient *c = workspace()->moveResizeClient();
561         if (!c) {
562             return false;
563         }
564         return true;
565     }
566 
touchMotion(qint32 id,const QPointF & pos,quint32 time)567     bool touchMotion(qint32 id, const QPointF &pos, quint32 time) override {
568         Q_UNUSED(time)
569         AbstractClient *c = workspace()->moveResizeClient();
570         if (!c) {
571             return false;
572         }
573         if (!m_set) {
574             m_id = id;
575             m_set = true;
576         }
577         if (m_id == id) {
578             c->updateInteractiveMoveResize(pos.toPoint());
579         }
580         return true;
581     }
582 
touchUp(qint32 id,quint32 time)583     bool touchUp(qint32 id, quint32 time) override {
584         Q_UNUSED(time)
585         AbstractClient *c = workspace()->moveResizeClient();
586         if (!c) {
587             return false;
588         }
589         if (m_id == id || !m_set) {
590             c->endInteractiveMoveResize();
591             m_set = false;
592             // pass through to update decoration filter later on
593             return false;
594         }
595         m_set = false;
596         return true;
597     }
598 private:
599     qint32 m_id = 0;
600     bool m_set = false;
601 };
602 
603 class WindowSelectorFilter : public InputEventFilter {
604 public:
pointerEvent(QMouseEvent * event,quint32 nativeButton)605     bool pointerEvent(QMouseEvent *event, quint32 nativeButton) override {
606         Q_UNUSED(nativeButton)
607         if (!m_active) {
608             return false;
609         }
610         switch (event->type()) {
611         case QEvent::MouseButtonRelease:
612             if (event->buttons() == Qt::NoButton) {
613                 if (event->button() == Qt::RightButton) {
614                     cancel();
615                 } else {
616                     accept(event->globalPos());
617                 }
618             }
619             break;
620         default:
621             break;
622         }
623         return true;
624     }
wheelEvent(QWheelEvent * event)625     bool wheelEvent(QWheelEvent *event) override {
626         Q_UNUSED(event)
627         // filter out while selecting a window
628         return m_active;
629     }
keyEvent(QKeyEvent * event)630     bool keyEvent(QKeyEvent *event) override {
631         Q_UNUSED(event)
632         if (!m_active) {
633             return false;
634         }
635         waylandServer()->seat()->setFocusedKeyboardSurface(nullptr);
636         passToWaylandServer(event);
637 
638         if (event->type() == QEvent::KeyPress) {
639             // x11 variant does this on key press, so do the same
640             if (event->key() == Qt::Key_Escape) {
641                 cancel();
642             } else if (event->key() == Qt::Key_Enter ||
643                        event->key() == Qt::Key_Return ||
644                        event->key() == Qt::Key_Space) {
645                 accept(input()->globalPointer());
646             }
647             if (input()->supportsPointerWarping()) {
648                 int mx = 0;
649                 int my = 0;
650                 if (event->key() == Qt::Key_Left) {
651                     mx = -10;
652                 }
653                 if (event->key() == Qt::Key_Right) {
654                     mx = 10;
655                 }
656                 if (event->key() == Qt::Key_Up) {
657                     my = -10;
658                 }
659                 if (event->key() == Qt::Key_Down) {
660                     my = 10;
661                 }
662                 if (event->modifiers() & Qt::ControlModifier) {
663                     mx /= 10;
664                     my /= 10;
665                 }
666                 input()->warpPointer(input()->globalPointer() + QPointF(mx, my));
667             }
668         }
669         // filter out while selecting a window
670         return true;
671     }
672 
touchDown(qint32 id,const QPointF & pos,quint32 time)673     bool touchDown(qint32 id, const QPointF &pos, quint32 time) override {
674         Q_UNUSED(time)
675         if (!isActive()) {
676             return false;
677         }
678         m_touchPoints.insert(id, pos);
679         return true;
680     }
681 
touchMotion(qint32 id,const QPointF & pos,quint32 time)682     bool touchMotion(qint32 id, const QPointF &pos, quint32 time) override {
683         Q_UNUSED(time)
684         if (!isActive()) {
685             return false;
686         }
687         auto it = m_touchPoints.find(id);
688         if (it != m_touchPoints.end()) {
689             *it = pos;
690         }
691         return true;
692     }
693 
touchUp(qint32 id,quint32 time)694     bool touchUp(qint32 id, quint32 time) override {
695         Q_UNUSED(time)
696         if (!isActive()) {
697             return false;
698         }
699         auto it = m_touchPoints.find(id);
700         if (it != m_touchPoints.end()) {
701             const auto pos = it.value();
702             m_touchPoints.erase(it);
703             if (m_touchPoints.isEmpty()) {
704                 accept(pos);
705             }
706         }
707         return true;
708     }
709 
isActive() const710     bool isActive() const {
711         return m_active;
712     }
start(std::function<void (KWin::Toplevel *)> callback)713     void start(std::function<void(KWin::Toplevel*)> callback) {
714         Q_ASSERT(!m_active);
715         m_active = true;
716         m_callback = callback;
717         input()->keyboard()->update();
718         input()->cancelTouch();
719     }
start(std::function<void (const QPoint &)> callback)720     void start(std::function<void(const QPoint &)> callback) {
721         Q_ASSERT(!m_active);
722         m_active = true;
723         m_pointSelectionFallback = callback;
724         input()->keyboard()->update();
725         input()->cancelTouch();
726     }
727 private:
deactivate()728     void deactivate() {
729         m_active = false;
730         m_callback = std::function<void(KWin::Toplevel*)>();
731         m_pointSelectionFallback = std::function<void(const QPoint &)>();
732         input()->pointer()->removeWindowSelectionCursor();
733         input()->keyboard()->update();
734         m_touchPoints.clear();
735     }
cancel()736     void cancel() {
737         if (m_callback) {
738             m_callback(nullptr);
739         }
740         if (m_pointSelectionFallback) {
741             m_pointSelectionFallback(QPoint(-1, -1));
742         }
743         deactivate();
744     }
accept(const QPoint & pos)745     void accept(const QPoint &pos) {
746         if (m_callback) {
747             // TODO: this ignores shaped windows
748             m_callback(input()->findToplevel(pos));
749         }
750         if (m_pointSelectionFallback) {
751             m_pointSelectionFallback(pos);
752         }
753         deactivate();
754     }
accept(const QPointF & pos)755     void accept(const QPointF &pos) {
756         accept(pos.toPoint());
757     }
758     bool m_active = false;
759     std::function<void(KWin::Toplevel*)> m_callback;
760     std::function<void(const QPoint &)> m_pointSelectionFallback;
761     QMap<quint32, QPointF> m_touchPoints;
762 };
763 
764 class GlobalShortcutFilter : public InputEventFilter {
765 public:
GlobalShortcutFilter()766     GlobalShortcutFilter() {
767         m_powerDown = new QTimer;
768         m_powerDown->setSingleShot(true);
769         m_powerDown->setInterval(1000);
770     }
~GlobalShortcutFilter()771     ~GlobalShortcutFilter() {
772         delete m_powerDown;
773     }
774 
pointerEvent(QMouseEvent * event,quint32 nativeButton)775     bool pointerEvent(QMouseEvent *event, quint32 nativeButton) override {
776         Q_UNUSED(nativeButton);
777         if (event->type() == QEvent::MouseButtonPress) {
778             if (input()->shortcuts()->processPointerPressed(event->modifiers(), event->buttons())) {
779                 return true;
780             }
781         }
782         return false;
783     }
wheelEvent(QWheelEvent * event)784     bool wheelEvent(QWheelEvent *event) override {
785         if (event->modifiers() == Qt::NoModifier) {
786             return false;
787         }
788         PointerAxisDirection direction = PointerAxisUp;
789         if (event->angleDelta().x() < 0) {
790             direction = PointerAxisRight;
791         } else if (event->angleDelta().x() > 0) {
792             direction = PointerAxisLeft;
793         } else if (event->angleDelta().y() < 0) {
794             direction = PointerAxisDown;
795         } else if (event->angleDelta().y() > 0) {
796             direction = PointerAxisUp;
797         }
798         return input()->shortcuts()->processAxis(event->modifiers(), direction);
799     }
keyEvent(QKeyEvent * event)800     bool keyEvent(QKeyEvent *event) override {
801         if (event->key() == Qt::Key_PowerOff) {
802             const auto modifiers = static_cast<KeyEvent*>(event)->modifiersRelevantForGlobalShortcuts();
803             if (event->type() == QEvent::KeyPress && !event->isAutoRepeat()) {
804                 QObject::connect(m_powerDown, &QTimer::timeout, input()->shortcuts(), [this, modifiers] {
805                     QObject::disconnect(m_powerDown, &QTimer::timeout, input()->shortcuts(), nullptr);
806                     m_powerDown->stop();
807                     input()->shortcuts()->processKey(modifiers, Qt::Key_PowerDown);
808                 });
809                 m_powerDown->start();
810                 return true;
811             } else if (event->type() == QEvent::KeyRelease) {
812                 const bool ret = !m_powerDown->isActive() || input()->shortcuts()->processKey(modifiers, event->key());
813                 m_powerDown->stop();
814                 return ret;
815             }
816         } else if (event->type() == QEvent::KeyPress) {
817             if (!waylandServer()->isKeyboardShortcutsInhibited()) {
818                 return input()->shortcuts()->processKey(static_cast<KeyEvent*>(event)->modifiersRelevantForGlobalShortcuts(), event->key());
819             }
820         }
821         return false;
822     }
swipeGestureBegin(int fingerCount,quint32 time)823     bool swipeGestureBegin(int fingerCount, quint32 time) override {
824         Q_UNUSED(time)
825         input()->shortcuts()->processSwipeStart(fingerCount);
826         return false;
827     }
swipeGestureUpdate(const QSizeF & delta,quint32 time)828     bool swipeGestureUpdate(const QSizeF &delta, quint32 time) override {
829         Q_UNUSED(time)
830         input()->shortcuts()->processSwipeUpdate(delta);
831         return false;
832     }
swipeGestureCancelled(quint32 time)833     bool swipeGestureCancelled(quint32 time) override {
834         Q_UNUSED(time)
835         input()->shortcuts()->processSwipeCancel();
836         return false;
837     }
swipeGestureEnd(quint32 time)838     bool swipeGestureEnd(quint32 time) override {
839         Q_UNUSED(time)
840         input()->shortcuts()->processSwipeEnd();
841         return false;
842     }
843 
844 private:
845     QTimer* m_powerDown = nullptr;
846 };
847 
848 
849 namespace {
850 
851 enum class MouseAction {
852     ModifierOnly,
853     ModifierAndWindow
854 };
performClientMouseAction(QMouseEvent * event,AbstractClient * client,MouseAction action=MouseAction::ModifierOnly)855 std::pair<bool, bool> performClientMouseAction(QMouseEvent *event, AbstractClient *client, MouseAction action = MouseAction::ModifierOnly)
856 {
857     Options::MouseCommand command = Options::MouseNothing;
858     bool wasAction = false;
859     if (static_cast<MouseEvent*>(event)->modifiersRelevantForGlobalShortcuts() == options->commandAllModifier()) {
860         if (!input()->pointer()->isConstrained() && !workspace()->globalShortcutsDisabled()) {
861             wasAction = true;
862             switch (event->button()) {
863             case Qt::LeftButton:
864                 command = options->commandAll1();
865                 break;
866             case Qt::MiddleButton:
867                 command = options->commandAll2();
868                 break;
869             case Qt::RightButton:
870                 command = options->commandAll3();
871                 break;
872             default:
873                 // nothing
874                 break;
875             }
876         }
877     } else {
878         if (action == MouseAction::ModifierAndWindow) {
879             command = client->getMouseCommand(event->button(), &wasAction);
880         }
881     }
882     if (wasAction) {
883         return std::make_pair(wasAction, !client->performMouseCommand(command, event->globalPos()));
884     }
885     return std::make_pair(wasAction, false);
886 }
887 
performClientWheelAction(QWheelEvent * event,AbstractClient * c,MouseAction action=MouseAction::ModifierOnly)888 std::pair<bool, bool> performClientWheelAction(QWheelEvent *event, AbstractClient *c, MouseAction action = MouseAction::ModifierOnly)
889 {
890     bool wasAction = false;
891     Options::MouseCommand command = Options::MouseNothing;
892     if (static_cast<WheelEvent*>(event)->modifiersRelevantForGlobalShortcuts() == options->commandAllModifier()) {
893         if (!input()->pointer()->isConstrained() && !workspace()->globalShortcutsDisabled()) {
894             wasAction = true;
895             command = options->operationWindowMouseWheel(-1 * event->angleDelta().y());
896         }
897     } else {
898         if (action == MouseAction::ModifierAndWindow) {
899             command = c->getWheelCommand(Qt::Vertical, &wasAction);
900         }
901     }
902     if (wasAction) {
903         return std::make_pair(wasAction, !c->performMouseCommand(command, event->globalPos()));
904     }
905     return std::make_pair(wasAction, false);
906 }
907 
908 }
909 
910 class InternalWindowEventFilter : public InputEventFilter {
pointerEvent(QMouseEvent * event,quint32 nativeButton)911     bool pointerEvent(QMouseEvent *event, quint32 nativeButton) override {
912         Q_UNUSED(nativeButton)
913         auto internal = input()->pointer()->internalWindow();
914         if (!internal) {
915             return false;
916         }
917         // find client
918         switch (event->type())
919         {
920         case QEvent::MouseButtonPress:
921         case QEvent::MouseButtonRelease: {
922             auto s = qobject_cast<InternalClient *>(workspace()->findInternal(internal));
923             if (s && s->isDecorated()) {
924                 // only perform mouse commands on decorated internal windows
925                 const auto actionResult = performClientMouseAction(event, s);
926                 if (actionResult.first) {
927                     return actionResult.second;
928                 }
929             }
930             break;
931         }
932         default:
933             break;
934         }
935         QMouseEvent e(event->type(),
936                         event->pos() - internal->position(),
937                         event->globalPos(),
938                         event->button(), event->buttons(), event->modifiers());
939         e.setAccepted(false);
940         QCoreApplication::sendEvent(internal, &e);
941         return e.isAccepted();
942     }
wheelEvent(QWheelEvent * event)943     bool wheelEvent(QWheelEvent *event) override {
944         auto internal = input()->pointer()->internalWindow();
945         if (!internal) {
946             return false;
947         }
948         if (event->angleDelta().y() != 0) {
949             auto s = qobject_cast<InternalClient *>(workspace()->findInternal(internal));
950             if (s && s->isDecorated()) {
951                 // client window action only on vertical scrolling
952                 const auto actionResult = performClientWheelAction(event, s);
953                 if (actionResult.first) {
954                     return actionResult.second;
955                 }
956             }
957         }
958         const QPointF localPos = event->globalPosF() - internal->position();
959         const Qt::Orientation orientation = (event->angleDelta().x() != 0) ? Qt::Horizontal : Qt::Vertical;
960         const int delta = event->angleDelta().x() != 0 ? event->angleDelta().x() : event->angleDelta().y();
961         QWheelEvent e(localPos, event->globalPosF(), QPoint(),
962                         event->angleDelta() * -1,
963                         delta * -1,
964                         orientation,
965                         event->buttons(),
966                         event->modifiers());
967         e.setAccepted(false);
968         QCoreApplication::sendEvent(internal, &e);
969         return e.isAccepted();
970     }
keyEvent(QKeyEvent * event)971     bool keyEvent(QKeyEvent *event) override {
972         const QList<InternalClient *> &clients = workspace()->internalClients();
973         QWindow *found = nullptr;
974         for (auto it = clients.crbegin(); it != clients.crend(); ++it) {
975             if (QWindow *w = (*it)->internalWindow()) {
976                 if (!w->isVisible()) {
977                     continue;
978                 }
979                 if (!workspace()->geometry().contains(w->geometry())) {
980                     continue;
981                 }
982                 if (w->property("_q_showWithoutActivating").toBool()) {
983                     continue;
984                 }
985                 if (w->property("outputOnly").toBool()) {
986                     continue;
987                 }
988                 if (w->flags().testFlag(Qt::ToolTip)) {
989                     continue;
990                 }
991                 found = w;
992                 break;
993             }
994         }
995         if (QGuiApplication::focusWindow() != found) {
996             QWindowSystemInterface::handleWindowActivated(found);
997         }
998         if (!found) {
999             return false;
1000         }
1001         auto xkb = input()->keyboard()->xkb();
1002         Qt::Key key = xkb->toQtKey( xkb->toKeysym(event->nativeScanCode()),
1003                                     event->nativeScanCode(),
1004                                     Qt::KeyboardModifiers(),
1005                                     true /* workaround for QTBUG-62102 */ );
1006         QKeyEvent internalEvent(event->type(), key,
1007                                 event->modifiers(), event->nativeScanCode(), event->nativeVirtualKey(),
1008                                 event->nativeModifiers(), event->text());
1009         internalEvent.setAccepted(false);
1010         if (QCoreApplication::sendEvent(found, &internalEvent)) {
1011             waylandServer()->seat()->setFocusedKeyboardSurface(nullptr);
1012             passToWaylandServer(event);
1013             return true;
1014         }
1015         return false;
1016     }
1017 
touchDown(qint32 id,const QPointF & pos,quint32 time)1018     bool touchDown(qint32 id, const QPointF &pos, quint32 time) override {
1019         auto seat = waylandServer()->seat();
1020         if (seat->isTouchSequence()) {
1021             // something else is getting the events
1022             return false;
1023         }
1024         auto touch = input()->touch();
1025         if (touch->internalPressId() != -1) {
1026             // already on internal window, ignore further touch points, but filter out
1027             m_pressedIds.insert(id);
1028             return true;
1029         }
1030         // a new touch point
1031         seat->setTimestamp(time);
1032         auto internal = touch->internalWindow();
1033         if (!internal) {
1034             return false;
1035         }
1036         touch->setInternalPressId(id);
1037         // Qt's touch event API is rather complex, let's do fake mouse events instead
1038         m_lastGlobalTouchPos = pos;
1039         m_lastLocalTouchPos = pos - internal->position();
1040 
1041         QEnterEvent enterEvent(m_lastLocalTouchPos, m_lastLocalTouchPos, pos);
1042         QCoreApplication::sendEvent(internal, &enterEvent);
1043 
1044         QMouseEvent e(QEvent::MouseButtonPress, m_lastLocalTouchPos, pos, Qt::LeftButton, Qt::LeftButton, input()->keyboardModifiers());
1045         e.setAccepted(false);
1046         QCoreApplication::sendEvent(internal, &e);
1047         return true;
1048     }
touchMotion(qint32 id,const QPointF & pos,quint32 time)1049     bool touchMotion(qint32 id, const QPointF &pos, quint32 time) override {
1050         auto touch = input()->touch();
1051         auto internal = touch->internalWindow();
1052         if (!internal) {
1053             return false;
1054         }
1055         if (touch->internalPressId() == -1) {
1056             return false;
1057         }
1058         waylandServer()->seat()->setTimestamp(time);
1059         if (touch->internalPressId() != qint32(id) || m_pressedIds.contains(id)) {
1060             // ignore, but filter out
1061             return true;
1062         }
1063         m_lastGlobalTouchPos = pos;
1064         m_lastLocalTouchPos = pos - QPointF(internal->x(), internal->y());
1065 
1066         QMouseEvent e(QEvent::MouseMove, m_lastLocalTouchPos, m_lastGlobalTouchPos, Qt::LeftButton, Qt::LeftButton, input()->keyboardModifiers());
1067         QCoreApplication::instance()->sendEvent(internal, &e);
1068         return true;
1069     }
touchUp(qint32 id,quint32 time)1070     bool touchUp(qint32 id, quint32 time) override {
1071         auto touch = input()->touch();
1072         auto internal = touch->internalWindow();
1073         const bool removed = m_pressedIds.remove(id);
1074         if (!internal) {
1075             return removed;
1076         }
1077         if (touch->internalPressId() == -1) {
1078             return removed;
1079         }
1080         waylandServer()->seat()->setTimestamp(time);
1081         if (touch->internalPressId() != qint32(id)) {
1082             // ignore, but filter out
1083             return true;
1084         }
1085         // send mouse up
1086         QMouseEvent e(QEvent::MouseButtonRelease, m_lastLocalTouchPos, m_lastGlobalTouchPos, Qt::LeftButton, Qt::MouseButtons(), input()->keyboardModifiers());
1087         e.setAccepted(false);
1088         QCoreApplication::sendEvent(internal, &e);
1089 
1090         QEvent leaveEvent(QEvent::Leave);
1091         QCoreApplication::sendEvent(internal, &leaveEvent);
1092 
1093         m_lastGlobalTouchPos = QPointF();
1094         m_lastLocalTouchPos = QPointF();
1095         input()->touch()->setInternalPressId(-1);
1096         return true;
1097     }
1098 private:
1099     QSet<qint32> m_pressedIds;
1100     QPointF m_lastGlobalTouchPos;
1101     QPointF m_lastLocalTouchPos;
1102 };
1103 
1104 class DecorationEventFilter : public InputEventFilter {
1105 public:
pointerEvent(QMouseEvent * event,quint32 nativeButton)1106     bool pointerEvent(QMouseEvent *event, quint32 nativeButton) override {
1107         Q_UNUSED(nativeButton)
1108         auto decoration = input()->pointer()->decoration();
1109         if (!decoration) {
1110             return false;
1111         }
1112         const QPointF p = event->globalPos() - decoration->client()->pos();
1113         switch (event->type()) {
1114         case QEvent::MouseMove: {
1115             QHoverEvent e(QEvent::HoverMove, p, p);
1116             QCoreApplication::instance()->sendEvent(decoration->decoration(), &e);
1117             decoration->client()->processDecorationMove(p.toPoint(), event->globalPos());
1118             return true;
1119         }
1120         case QEvent::MouseButtonPress:
1121         case QEvent::MouseButtonRelease: {
1122             const auto actionResult = performClientMouseAction(event, decoration->client());
1123             if (actionResult.first) {
1124                 return actionResult.second;
1125             }
1126             QMouseEvent e(event->type(), p, event->globalPos(), event->button(), event->buttons(), event->modifiers());
1127             e.setAccepted(false);
1128             QCoreApplication::sendEvent(decoration->decoration(), &e);
1129             if (!e.isAccepted() && event->type() == QEvent::MouseButtonPress) {
1130                 decoration->client()->processDecorationButtonPress(&e);
1131             }
1132             if (event->type() == QEvent::MouseButtonRelease) {
1133                 decoration->client()->processDecorationButtonRelease(&e);
1134             }
1135             return true;
1136         }
1137         default:
1138             break;
1139         }
1140         return false;
1141     }
wheelEvent(QWheelEvent * event)1142     bool wheelEvent(QWheelEvent *event) override {
1143         auto decoration = input()->pointer()->decoration();
1144         if (!decoration) {
1145             return false;
1146         }
1147         if (event->angleDelta().y() != 0) {
1148             // client window action only on vertical scrolling
1149             const auto actionResult = performClientWheelAction(event, decoration->client());
1150             if (actionResult.first) {
1151                 return actionResult.second;
1152             }
1153         }
1154         const QPointF localPos = event->globalPosF() - decoration->client()->pos();
1155         const Qt::Orientation orientation = (event->angleDelta().x() != 0) ? Qt::Horizontal : Qt::Vertical;
1156         const int delta = event->angleDelta().x() != 0 ? event->angleDelta().x() : event->angleDelta().y();
1157         QWheelEvent e(localPos, event->globalPosF(), QPoint(),
1158                         event->angleDelta(),
1159                         delta,
1160                         orientation,
1161                         event->buttons(),
1162                         event->modifiers());
1163         e.setAccepted(false);
1164         QCoreApplication::sendEvent(decoration, &e);
1165         if (e.isAccepted()) {
1166             return true;
1167         }
1168         if ((orientation == Qt::Vertical) && decoration->client()->titlebarPositionUnderMouse()) {
1169             decoration->client()->performMouseCommand(options->operationTitlebarMouseWheel(delta * -1),
1170                                                         event->globalPosF().toPoint());
1171         }
1172         return true;
1173     }
touchDown(qint32 id,const QPointF & pos,quint32 time)1174     bool touchDown(qint32 id, const QPointF &pos, quint32 time) override {
1175         auto seat = waylandServer()->seat();
1176         if (seat->isTouchSequence()) {
1177             return false;
1178         }
1179         if (input()->touch()->decorationPressId() != -1) {
1180             // already on a decoration, ignore further touch points, but filter out
1181             return true;
1182         }
1183         seat->setTimestamp(time);
1184         auto decoration = input()->touch()->decoration();
1185         if (!decoration) {
1186             return false;
1187         }
1188 
1189         input()->touch()->setDecorationPressId(id);
1190         m_lastGlobalTouchPos = pos;
1191         m_lastLocalTouchPos = pos - decoration->client()->pos();
1192 
1193         QHoverEvent hoverEvent(QEvent::HoverMove, m_lastLocalTouchPos, m_lastLocalTouchPos);
1194         QCoreApplication::sendEvent(decoration->decoration(), &hoverEvent);
1195 
1196         QMouseEvent e(QEvent::MouseButtonPress, m_lastLocalTouchPos, pos, Qt::LeftButton, Qt::LeftButton, input()->keyboardModifiers());
1197         e.setAccepted(false);
1198         QCoreApplication::sendEvent(decoration->decoration(), &e);
1199         if (!e.isAccepted()) {
1200             decoration->client()->processDecorationButtonPress(&e);
1201         }
1202         return true;
1203     }
touchMotion(qint32 id,const QPointF & pos,quint32 time)1204     bool touchMotion(qint32 id, const QPointF &pos, quint32 time) override {
1205         Q_UNUSED(time)
1206         auto decoration = input()->touch()->decoration();
1207         if (!decoration) {
1208             return false;
1209         }
1210         if (input()->touch()->decorationPressId() == -1) {
1211             return false;
1212         }
1213         if (input()->touch()->decorationPressId() != qint32(id)) {
1214             // ignore, but filter out
1215             return true;
1216         }
1217         m_lastGlobalTouchPos = pos;
1218         m_lastLocalTouchPos = pos - decoration->client()->pos();
1219 
1220         QHoverEvent e(QEvent::HoverMove, m_lastLocalTouchPos, m_lastLocalTouchPos);
1221         QCoreApplication::instance()->sendEvent(decoration->decoration(), &e);
1222         decoration->client()->processDecorationMove(m_lastLocalTouchPos.toPoint(), pos.toPoint());
1223         return true;
1224     }
touchUp(qint32 id,quint32 time)1225     bool touchUp(qint32 id, quint32 time) override {
1226         Q_UNUSED(time);
1227         auto decoration = input()->touch()->decoration();
1228         if (!decoration) {
1229             // can happen when quick tiling
1230             if (input()->touch()->decorationPressId() == id) {
1231                 m_lastGlobalTouchPos = QPointF();
1232                 m_lastLocalTouchPos = QPointF();
1233                 input()->touch()->setDecorationPressId(-1);
1234                 return true;
1235             }
1236             return false;
1237         }
1238         if (input()->touch()->decorationPressId() == -1) {
1239             return false;
1240         }
1241         if (input()->touch()->decorationPressId() != qint32(id)) {
1242             // ignore, but filter out
1243             return true;
1244         }
1245 
1246         // send mouse up
1247         QMouseEvent e(QEvent::MouseButtonRelease, m_lastLocalTouchPos, m_lastGlobalTouchPos, Qt::LeftButton, Qt::MouseButtons(), input()->keyboardModifiers());
1248         e.setAccepted(false);
1249         QCoreApplication::sendEvent(decoration->decoration(), &e);
1250         decoration->client()->processDecorationButtonRelease(&e);
1251 
1252         QHoverEvent leaveEvent(QEvent::HoverLeave, QPointF(), QPointF());
1253         QCoreApplication::sendEvent(decoration->decoration(), &leaveEvent);
1254 
1255         m_lastGlobalTouchPos = QPointF();
1256         m_lastLocalTouchPos = QPointF();
1257         input()->touch()->setDecorationPressId(-1);
1258         return true;
1259     }
1260 private:
1261     QPointF m_lastGlobalTouchPos;
1262     QPointF m_lastLocalTouchPos;
1263 };
1264 
1265 #ifdef KWIN_BUILD_TABBOX
1266 class TabBoxInputFilter : public InputEventFilter
1267 {
1268 public:
pointerEvent(QMouseEvent * event,quint32 button)1269     bool pointerEvent(QMouseEvent *event, quint32 button) override {
1270         Q_UNUSED(button)
1271         if (!TabBox::TabBox::self() || !TabBox::TabBox::self()->isGrabbed()) {
1272             return false;
1273         }
1274         return TabBox::TabBox::self()->handleMouseEvent(event);
1275     }
keyEvent(QKeyEvent * event)1276     bool keyEvent(QKeyEvent *event) override {
1277         if (!TabBox::TabBox::self() || !TabBox::TabBox::self()->isGrabbed()) {
1278             return false;
1279         }
1280         auto seat = waylandServer()->seat();
1281         seat->setFocusedKeyboardSurface(nullptr);
1282         input()->pointer()->setEnableConstraints(false);
1283         // pass the key event to the seat, so that it has a proper model of the currently hold keys
1284         // this is important for combinations like alt+shift to ensure that shift is not considered pressed
1285         passToWaylandServer(event);
1286 
1287         if (event->type() == QEvent::KeyPress) {
1288             TabBox::TabBox::self()->keyPress(event->modifiers() | event->key());
1289         } else if (static_cast<KeyEvent*>(event)->modifiersRelevantForGlobalShortcuts() == Qt::NoModifier) {
1290             TabBox::TabBox::self()->modifiersReleased();
1291         }
1292         return true;
1293     }
wheelEvent(QWheelEvent * event)1294     bool wheelEvent(QWheelEvent *event) override {
1295         if (!TabBox::TabBox::self() || !TabBox::TabBox::self()->isGrabbed()) {
1296             return false;
1297         }
1298         return TabBox::TabBox::self()->handleWheelEvent(event);
1299     }
1300 };
1301 #endif
1302 
1303 class ScreenEdgeInputFilter : public InputEventFilter
1304 {
1305 public:
pointerEvent(QMouseEvent * event,quint32 nativeButton)1306     bool pointerEvent(QMouseEvent *event, quint32 nativeButton) override {
1307         Q_UNUSED(nativeButton)
1308         ScreenEdges::self()->isEntered(event);
1309         // always forward
1310         return false;
1311     }
touchDown(qint32 id,const QPointF & pos,quint32 time)1312     bool touchDown(qint32 id, const QPointF &pos, quint32 time) override {
1313         Q_UNUSED(time)
1314         // TODO: better check whether a touch sequence is in progress
1315         if (m_touchInProgress || waylandServer()->seat()->isTouchSequence()) {
1316             // cancel existing touch
1317             ScreenEdges::self()->gestureRecognizer()->cancelSwipeGesture();
1318             m_touchInProgress = false;
1319             m_id = 0;
1320             return false;
1321         }
1322         if (ScreenEdges::self()->gestureRecognizer()->startSwipeGesture(pos) > 0) {
1323             m_touchInProgress = true;
1324             m_id = id;
1325             m_lastPos = pos;
1326             return true;
1327         }
1328         return false;
1329     }
touchMotion(qint32 id,const QPointF & pos,quint32 time)1330     bool touchMotion(qint32 id, const QPointF &pos, quint32 time) override {
1331         Q_UNUSED(time)
1332         if (m_touchInProgress && m_id == id) {
1333             ScreenEdges::self()->gestureRecognizer()->updateSwipeGesture(QSizeF(pos.x() - m_lastPos.x(), pos.y() - m_lastPos.y()));
1334             m_lastPos = pos;
1335             return true;
1336         }
1337         return false;
1338     }
touchUp(qint32 id,quint32 time)1339     bool touchUp(qint32 id, quint32 time) override {
1340         Q_UNUSED(time)
1341         if (m_touchInProgress && m_id == id) {
1342             ScreenEdges::self()->gestureRecognizer()->endSwipeGesture();
1343             m_touchInProgress = false;
1344             return true;
1345         }
1346         return false;
1347     }
1348 private:
1349     bool m_touchInProgress = false;
1350     qint32 m_id = 0;
1351     QPointF m_lastPos;
1352 };
1353 
1354 /**
1355  * This filter implements window actions. If the event should not be passed to the
1356  * current pointer window it will filter out the event
1357  */
1358 class WindowActionInputFilter : public InputEventFilter
1359 {
1360 public:
pointerEvent(QMouseEvent * event,quint32 nativeButton)1361     bool pointerEvent(QMouseEvent *event, quint32 nativeButton) override {
1362         Q_UNUSED(nativeButton)
1363         if (event->type() != QEvent::MouseButtonPress) {
1364             return false;
1365         }
1366         AbstractClient *c = dynamic_cast<AbstractClient*>(input()->pointer()->focus());
1367         if (!c) {
1368             return false;
1369         }
1370         const auto actionResult = performClientMouseAction(event, c, MouseAction::ModifierAndWindow);
1371         if (actionResult.first) {
1372             return actionResult.second;
1373         }
1374         return false;
1375     }
wheelEvent(QWheelEvent * event)1376     bool wheelEvent(QWheelEvent *event) override {
1377         if (event->angleDelta().y() == 0) {
1378             // only actions on vertical scroll
1379             return false;
1380         }
1381         AbstractClient *c = dynamic_cast<AbstractClient*>(input()->pointer()->focus());
1382         if (!c) {
1383             return false;
1384         }
1385         const auto actionResult = performClientWheelAction(event, c, MouseAction::ModifierAndWindow);
1386         if (actionResult.first) {
1387             return actionResult.second;
1388         }
1389         return false;
1390     }
touchDown(qint32 id,const QPointF & pos,quint32 time)1391     bool touchDown(qint32 id, const QPointF &pos, quint32 time) override {
1392         Q_UNUSED(id)
1393         Q_UNUSED(time)
1394         auto seat = waylandServer()->seat();
1395         if (seat->isTouchSequence()) {
1396             return false;
1397         }
1398         AbstractClient *c = dynamic_cast<AbstractClient*>(input()->touch()->focus());
1399         if (!c) {
1400             return false;
1401         }
1402         bool wasAction = false;
1403         const Options::MouseCommand command = c->getMouseCommand(Qt::LeftButton, &wasAction);
1404         if (wasAction) {
1405             return !c->performMouseCommand(command, pos.toPoint());
1406         }
1407         return false;
1408     }
1409 };
1410 
1411 /**
1412  * The remaining default input filter which forwards events to other windows
1413  */
1414 class ForwardInputFilter : public InputEventFilter
1415 {
1416 public:
pointerEvent(QMouseEvent * event,quint32 nativeButton)1417     bool pointerEvent(QMouseEvent *event, quint32 nativeButton) override {
1418         auto seat = waylandServer()->seat();
1419         seat->setTimestamp(event->timestamp());
1420         switch (event->type()) {
1421         case QEvent::MouseMove: {
1422             seat->notifyPointerMotion(event->globalPos());
1423             MouseEvent *e = static_cast<MouseEvent*>(event);
1424             if (e->delta() != QSizeF()) {
1425                 seat->relativePointerMotion(e->delta(), e->deltaUnaccelerated(), e->timestampMicroseconds());
1426             }
1427             seat->notifyPointerFrame();
1428             break;
1429         }
1430         case QEvent::MouseButtonPress:
1431             seat->notifyPointerButton(nativeButton, KWaylandServer::PointerButtonState::Pressed);
1432             seat->notifyPointerFrame();
1433             break;
1434         case QEvent::MouseButtonRelease:
1435             seat->notifyPointerButton(nativeButton, KWaylandServer::PointerButtonState::Released);
1436             seat->notifyPointerFrame();
1437             break;
1438         default:
1439             break;
1440         }
1441         return true;
1442     }
wheelEvent(QWheelEvent * event)1443     bool wheelEvent(QWheelEvent *event) override {
1444         auto seat = waylandServer()->seat();
1445         seat->setTimestamp(event->timestamp());
1446         auto _event = static_cast<WheelEvent *>(event);
1447         seat->notifyPointerAxis(_event->orientation(), _event->delta(), _event->discreteDelta(),
1448                                 kwinAxisSourceToKWaylandAxisSource(_event->axisSource()));
1449         seat->notifyPointerFrame();
1450         return true;
1451     }
keyEvent(QKeyEvent * event)1452     bool keyEvent(QKeyEvent *event) override {
1453         if (!workspace()) {
1454             return false;
1455         }
1456         if (event->isAutoRepeat()) {
1457             // handled by Wayland client
1458             return false;
1459         }
1460         auto seat = waylandServer()->seat();
1461         input()->keyboard()->update();
1462         seat->setTimestamp(event->timestamp());
1463         passToWaylandServer(event);
1464         return true;
1465     }
touchDown(qint32 id,const QPointF & pos,quint32 time)1466     bool touchDown(qint32 id, const QPointF &pos, quint32 time) override {
1467         if (!workspace()) {
1468             return false;
1469         }
1470         auto seat = waylandServer()->seat();
1471         seat->setTimestamp(time);
1472         seat->notifyTouchDown(id, pos);
1473         return true;
1474     }
touchMotion(qint32 id,const QPointF & pos,quint32 time)1475     bool touchMotion(qint32 id, const QPointF &pos, quint32 time) override {
1476         if (!workspace()) {
1477             return false;
1478         }
1479         auto seat = waylandServer()->seat();
1480         seat->setTimestamp(time);
1481         seat->notifyTouchMotion(id, pos);
1482         return true;
1483     }
touchUp(qint32 id,quint32 time)1484     bool touchUp(qint32 id, quint32 time) override {
1485         if (!workspace()) {
1486             return false;
1487         }
1488         auto seat = waylandServer()->seat();
1489         seat->setTimestamp(time);
1490         seat->notifyTouchUp(id);
1491         return true;
1492     }
pinchGestureBegin(int fingerCount,quint32 time)1493     bool pinchGestureBegin(int fingerCount, quint32 time) override {
1494         if (!workspace()) {
1495             return false;
1496         }
1497         auto seat = waylandServer()->seat();
1498         seat->setTimestamp(time);
1499         seat->startPointerPinchGesture(fingerCount);
1500         return true;
1501     }
pinchGestureUpdate(qreal scale,qreal angleDelta,const QSizeF & delta,quint32 time)1502     bool pinchGestureUpdate(qreal scale, qreal angleDelta, const QSizeF &delta, quint32 time) override {
1503         if (!workspace()) {
1504             return false;
1505         }
1506         auto seat = waylandServer()->seat();
1507         seat->setTimestamp(time);
1508         seat->updatePointerPinchGesture(delta, scale, angleDelta);
1509         return true;
1510     }
pinchGestureEnd(quint32 time)1511     bool pinchGestureEnd(quint32 time) override {
1512         if (!workspace()) {
1513             return false;
1514         }
1515         auto seat = waylandServer()->seat();
1516         seat->setTimestamp(time);
1517         seat->endPointerPinchGesture();
1518         return true;
1519     }
pinchGestureCancelled(quint32 time)1520     bool pinchGestureCancelled(quint32 time) override {
1521         if (!workspace()) {
1522             return false;
1523         }
1524         auto seat = waylandServer()->seat();
1525         seat->setTimestamp(time);
1526         seat->cancelPointerPinchGesture();
1527         return true;
1528     }
1529 
swipeGestureBegin(int fingerCount,quint32 time)1530     bool swipeGestureBegin(int fingerCount, quint32 time) override {
1531         if (!workspace()) {
1532             return false;
1533         }
1534         auto seat = waylandServer()->seat();
1535         seat->setTimestamp(time);
1536         seat->startPointerSwipeGesture(fingerCount);
1537         return true;
1538     }
swipeGestureUpdate(const QSizeF & delta,quint32 time)1539     bool swipeGestureUpdate(const QSizeF &delta, quint32 time) override {
1540         if (!workspace()) {
1541             return false;
1542         }
1543         auto seat = waylandServer()->seat();
1544         seat->setTimestamp(time);
1545         seat->updatePointerSwipeGesture(delta);
1546         return true;
1547     }
swipeGestureEnd(quint32 time)1548     bool swipeGestureEnd(quint32 time) override {
1549         if (!workspace()) {
1550             return false;
1551         }
1552         auto seat = waylandServer()->seat();
1553         seat->setTimestamp(time);
1554         seat->endPointerSwipeGesture();
1555         return true;
1556     }
swipeGestureCancelled(quint32 time)1557     bool swipeGestureCancelled(quint32 time) override {
1558         if (!workspace()) {
1559             return false;
1560         }
1561         auto seat = waylandServer()->seat();
1562         seat->setTimestamp(time);
1563         seat->cancelPointerSwipeGesture();
1564         return true;
1565     }
1566 };
1567 
findSeat()1568 static KWaylandServer::SeatInterface *findSeat()
1569 {
1570     auto server = waylandServer();
1571     if (!server) {
1572         return nullptr;
1573     }
1574     return server->seat();
1575 }
1576 
1577 class SurfaceCursor : public Cursor
1578 {
1579 public:
SurfaceCursor(QObject * parent)1580     explicit SurfaceCursor(QObject *parent) : Cursor(parent)
1581     {}
1582 
updateCursorSurface(KWaylandServer::SurfaceInterface * surface,const QPoint & hotspot)1583     void updateCursorSurface(KWaylandServer::SurfaceInterface *surface, const QPoint &hotspot) {
1584         if (m_surface == surface && hotspot == m_hotspot) {
1585             return;
1586         }
1587 
1588         if (m_surface) {
1589             disconnect(m_surface, nullptr, this, nullptr);
1590         }
1591         m_surface = surface;
1592         m_hotspot = hotspot;
1593         connect(m_surface, &KWaylandServer::SurfaceInterface::committed, this, &SurfaceCursor::refresh);
1594 
1595         refresh();
1596     }
1597 
1598 private:
refresh()1599     void refresh()
1600     {
1601         auto buffer = qobject_cast<KWaylandServer::ShmClientBuffer *>(m_surface->buffer());
1602         if (!buffer) {
1603             updateCursor({}, {});
1604             return;
1605         }
1606 
1607         QImage cursorImage;
1608         cursorImage = buffer->data().copy();
1609         cursorImage.setDevicePixelRatio(m_surface->bufferScale());
1610         updateCursor(cursorImage, m_hotspot);
1611     }
1612 
1613     QPointer<KWaylandServer::SurfaceInterface> m_surface;
1614     QPoint m_hotspot;
1615 };
1616 
1617 /**
1618  * Handles input coming from a tablet device (e.g. wacom) often with a pen
1619  */
1620 class TabletInputFilter : public QObject, public InputEventFilter
1621 {
1622 public:
TabletInputFilter()1623     TabletInputFilter()
1624     {
1625     }
1626 
findTabletSeat()1627     static KWaylandServer::TabletSeatV2Interface *findTabletSeat()
1628     {
1629         auto server = waylandServer();
1630         if (!server) {
1631             return nullptr;
1632         }
1633         KWaylandServer::TabletManagerV2Interface *manager = server->tabletManagerV2();
1634         return manager->seat(findSeat());
1635     }
1636 
integrateDevice(LibInput::Device * device)1637     void integrateDevice(LibInput::Device *device)
1638     {
1639         if (!device->isTabletTool() && !device->isTabletPad()) {
1640             return;
1641         }
1642 
1643         KWaylandServer::TabletSeatV2Interface *tabletSeat = findTabletSeat();
1644         if (!tabletSeat) {
1645             qCCritical(KWIN_CORE) << "Could not find tablet seat";
1646             return;
1647         }
1648         struct udev_device *const udev_device = libinput_device_get_udev_device(device->device());
1649         const char *devnode = udev_device_get_syspath(udev_device);
1650 
1651         auto deviceGroup = libinput_device_get_device_group(device->device());
1652         auto tablet = static_cast<KWaylandServer::TabletV2Interface *>(libinput_device_group_get_user_data(deviceGroup));
1653         if (!tablet) {
1654             tablet = tabletSeat->addTablet(device->vendor(), device->product(), device->sysName(), device->name(), {QString::fromUtf8(devnode)});
1655             libinput_device_group_set_user_data(deviceGroup, tablet);
1656         }
1657 
1658         if (device->isTabletPad()) {
1659             const int buttonsCount = libinput_device_tablet_pad_get_num_buttons(device->device());
1660             const int ringsCount = libinput_device_tablet_pad_get_num_rings(device->device());
1661             const int stripsCount = libinput_device_tablet_pad_get_num_strips(device->device());
1662             const int modes = libinput_device_tablet_pad_get_num_mode_groups(device->device());
1663 
1664             auto firstGroup = libinput_device_tablet_pad_get_mode_group(device->device(), 0);
1665             tabletSeat->addTabletPad(device->sysName(), device->name(), {QString::fromUtf8(devnode)}, buttonsCount, ringsCount, stripsCount, modes, libinput_tablet_pad_mode_group_get_mode(firstGroup), tablet);
1666         }
1667     }
1668 
removeDevice(LibInput::Device * device)1669     void removeDevice(LibInput::Device *device)
1670     {
1671         auto deviceGroup = libinput_device_get_device_group(device->device());
1672         libinput_device_group_set_user_data(deviceGroup, nullptr);
1673     }
1674 
removeDeviceBySysName(const QString & sysname)1675     void removeDeviceBySysName(const QString &sysname)
1676     {
1677         KWaylandServer::TabletSeatV2Interface *tabletSeat = findTabletSeat();
1678         if (tabletSeat)
1679             tabletSeat->removeDevice(sysname);
1680         else
1681             qCCritical(KWIN_CORE) << "Could not find tablet to remove" << sysname;
1682     }
1683 
getType(const KWin::TabletToolId & tabletToolId)1684     KWaylandServer::TabletToolV2Interface::Type getType(const KWin::TabletToolId &tabletToolId) {
1685         using Type = KWaylandServer::TabletToolV2Interface::Type;
1686         switch (tabletToolId.m_toolType) {
1687         case InputRedirection::Pen:
1688             return Type::Pen;
1689         case InputRedirection::Eraser:
1690             return Type::Eraser;
1691         case InputRedirection::Brush:
1692             return Type::Brush;
1693         case InputRedirection::Pencil:
1694             return Type::Pencil;
1695         case InputRedirection::Airbrush:
1696             return Type::Airbrush;
1697         case InputRedirection::Finger:
1698             return Type::Finger;
1699         case InputRedirection::Mouse:
1700             return Type::Mouse;
1701         case InputRedirection::Lens:
1702             return Type::Lens;
1703         case InputRedirection::Totem:
1704             return Type::Totem;
1705         }
1706         return Type::Pen;
1707     }
1708 
createTool(const KWin::TabletToolId & tabletToolId)1709     KWaylandServer::TabletToolV2Interface *createTool(const KWin::TabletToolId &tabletToolId)
1710     {
1711         using namespace KWaylandServer;
1712         KWaylandServer::TabletSeatV2Interface *tabletSeat = findTabletSeat();
1713 
1714         const auto f = [](InputRedirection::Capability cap) {
1715             switch (cap) {
1716             case InputRedirection::Tilt:
1717                 return TabletToolV2Interface::Tilt;
1718             case InputRedirection::Pressure:
1719                 return TabletToolV2Interface::Pressure;
1720             case InputRedirection::Distance:
1721                 return TabletToolV2Interface::Distance;
1722             case InputRedirection::Rotation:
1723                 return TabletToolV2Interface::Rotation;
1724             case InputRedirection::Slider:
1725                 return TabletToolV2Interface::Slider;
1726             case InputRedirection::Wheel:
1727                 return TabletToolV2Interface::Wheel;
1728             }
1729             return TabletToolV2Interface::Wheel;
1730         };
1731         QVector<TabletToolV2Interface::Capability> ifaceCapabilities;
1732         ifaceCapabilities.resize(tabletToolId.m_capabilities.size());
1733         std::transform(tabletToolId.m_capabilities.constBegin(), tabletToolId.m_capabilities.constEnd(), ifaceCapabilities.begin(), f);
1734 
1735         TabletToolV2Interface *tool = tabletSeat->addTool(getType(tabletToolId), tabletToolId.m_serialId, tabletToolId.m_uniqueId, ifaceCapabilities);
1736 
1737         const auto cursor = new SurfaceCursor(tool);
1738         Cursors::self()->addCursor(cursor);
1739         m_cursorByTool[tool] = cursor;
1740 
1741         connect(tool, &TabletToolV2Interface::cursorChanged, cursor, [cursor] (TabletCursorV2 *tcursor) {
1742             static const auto createDefaultCursor = [] {
1743                 WaylandCursorImage defaultCursor;
1744                 WaylandCursorImage::Image ret;
1745                 defaultCursor.loadThemeCursor(CursorShape(Qt::CrossCursor), &ret);
1746                 return ret;
1747             };
1748             if (!tcursor || tcursor->enteredSerial() == 0) {
1749                 static const auto defaultCursor = createDefaultCursor();
1750                 cursor->updateCursor(defaultCursor.image, defaultCursor.hotspot);
1751                 return;
1752             }
1753             auto cursorSurface = tcursor->surface();
1754             if (!cursorSurface) {
1755                 cursor->updateCursor({}, {});
1756                 return;
1757             }
1758 
1759             cursor->updateCursorSurface(cursorSurface, tcursor->hotspot());
1760         });
1761         Q_EMIT cursor->cursorChanged();
1762         return tool;
1763     }
1764 
tabletToolEvent(TabletEvent * event)1765     bool tabletToolEvent(TabletEvent *event) override
1766     {
1767         if (!workspace()) {
1768             return false;
1769         }
1770 
1771         KWaylandServer::TabletSeatV2Interface *tabletSeat = findTabletSeat();
1772         if (!tabletSeat) {
1773             qCCritical(KWIN_CORE) << "Could not find tablet manager";
1774             return false;
1775         }
1776         auto tool = tabletSeat->toolByHardwareSerial(event->tabletId().m_serialId, getType(event->tabletId()));
1777         if (!tool) {
1778             tool = createTool(event->tabletId());
1779         }
1780 
1781         // NOTE: tablet will be nullptr as the device is removed (see ::removeDevice) but events from the tool
1782         // may still happen (e.g. Release or ProximityOut events)
1783         auto tablet = static_cast<KWaylandServer::TabletV2Interface *>(event->tabletId().m_deviceGroupData);
1784 
1785         Toplevel *toplevel = input()->findToplevel(event->globalPos());
1786         if (!toplevel || !toplevel->surface()) {
1787             return false;
1788         }
1789 
1790         KWaylandServer::SurfaceInterface *surface = toplevel->surface();
1791         tool->setCurrentSurface(surface);
1792 
1793         if (!tool->isClientSupported() || (tablet && !tablet->isSurfaceSupported(surface))) {
1794             return emulateTabletEvent(event);
1795         }
1796 
1797         switch (event->type()) {
1798         case QEvent::TabletMove: {
1799             const auto pos = toplevel->mapToLocal(event->globalPosF());
1800             tool->sendMotion(pos);
1801             m_cursorByTool[tool]->setPos(event->globalPos());
1802             break;
1803         } case QEvent::TabletEnterProximity: {
1804             tool->sendProximityIn(tablet);
1805             break;
1806         } case QEvent::TabletLeaveProximity:
1807             tool->sendProximityOut();
1808             break;
1809         case QEvent::TabletPress: {
1810             const auto pos = toplevel->mapToLocal(event->globalPosF());
1811             tool->sendMotion(pos);
1812             m_cursorByTool[tool]->setPos(event->globalPos());
1813             tool->sendDown();
1814             break;
1815         }
1816         case QEvent::TabletRelease:
1817             tool->sendUp();
1818             break;
1819         default:
1820             qCWarning(KWIN_CORE) << "Unexpected tablet event type" << event;
1821             break;
1822         }
1823         const quint32 MAX_VAL = 65535;
1824         tool->sendPressure(MAX_VAL * event->pressure());
1825         tool->sendFrame(event->timestamp());
1826         waylandServer()->simulateUserActivity();
1827         return true;
1828     }
1829 
emulateTabletEvent(TabletEvent * event)1830     bool emulateTabletEvent(TabletEvent *event)
1831     {
1832         if (!workspace()) {
1833             return false;
1834         }
1835 
1836         switch (event->type()) {
1837         case QEvent::TabletMove:
1838         case QEvent::TabletEnterProximity:
1839             input()->pointer()->processMotion(event->globalPosF(), event->timestamp());
1840             break;
1841         case QEvent::TabletPress:
1842             input()->pointer()->processButton(KWin::qtMouseButtonToButton(Qt::LeftButton),
1843                                               InputRedirection::PointerButtonPressed, event->timestamp());
1844             break;
1845         case QEvent::TabletRelease:
1846             input()->pointer()->processButton(KWin::qtMouseButtonToButton(Qt::LeftButton),
1847                                               InputRedirection::PointerButtonReleased, event->timestamp());
1848             break;
1849         case QEvent::TabletLeaveProximity:
1850             break;
1851         default:
1852             qCWarning(KWIN_CORE) << "Unexpected tablet event type" << event;
1853             break;
1854         }
1855         waylandServer()->simulateUserActivity();
1856         return true;
1857     }
1858 
tabletToolButtonEvent(uint button,bool pressed,const TabletToolId & tabletToolId)1859     bool tabletToolButtonEvent(uint button, bool pressed, const TabletToolId &tabletToolId) override
1860     {
1861         KWaylandServer::TabletSeatV2Interface *tabletSeat = findTabletSeat();
1862         auto tool = tabletSeat->toolByHardwareSerial(tabletToolId.m_serialId, getType(tabletToolId));
1863         if (!tool) {
1864             tool = createTool(tabletToolId);
1865         }
1866         if (!tool->isClientSupported()) {
1867             return false;
1868         }
1869         tool->sendButton(button, pressed);
1870         return true;
1871     }
1872 
findAndAdoptPad(const TabletPadId & tabletPadId) const1873     KWaylandServer::TabletPadV2Interface *findAndAdoptPad(const TabletPadId &tabletPadId) const
1874     {
1875         Toplevel *toplevel = workspace()->activeClient();
1876         auto seat = findTabletSeat();
1877         if (!toplevel || !toplevel->surface() || !seat->isClientSupported(toplevel->surface()->client())) {
1878             return nullptr;
1879         }
1880 
1881         auto tablet = static_cast<KWaylandServer::TabletV2Interface *>(tabletPadId.data);
1882         KWaylandServer::SurfaceInterface *surface = toplevel->surface();
1883         auto pad = tablet->pad();
1884         if (!pad) {
1885             return nullptr;
1886         }
1887         pad->setCurrentSurface(surface, tablet);
1888         return pad;
1889     }
1890 
tabletPadButtonEvent(uint button,bool pressed,const TabletPadId & tabletPadId)1891     bool tabletPadButtonEvent(uint button, bool pressed, const TabletPadId &tabletPadId) override
1892     {
1893         auto pad = findAndAdoptPad(tabletPadId);
1894         if (!pad) {
1895             return false;
1896         }
1897         pad->sendButton(QDateTime::currentMSecsSinceEpoch(), button, pressed);
1898         return true;
1899     }
1900 
tabletPadRingEvent(int number,int angle,bool isFinger,const TabletPadId & tabletPadId)1901     bool tabletPadRingEvent(int number, int angle, bool isFinger, const TabletPadId &tabletPadId) override
1902     {
1903         auto pad = findAndAdoptPad(tabletPadId);
1904         if (!pad) {
1905             return false;
1906         }
1907         auto ring = pad->ring(number);
1908 
1909         ring->sendAngle(angle);
1910         if (isFinger) {
1911             ring->sendSource(KWaylandServer::TabletPadRingV2Interface::SourceFinger);
1912         }
1913         ring->sendFrame(QDateTime::currentMSecsSinceEpoch());
1914         return true;
1915     }
1916 
tabletPadStripEvent(int number,int position,bool isFinger,const TabletPadId & tabletPadId)1917     bool tabletPadStripEvent(int number, int position, bool isFinger, const TabletPadId &tabletPadId) override
1918     {
1919         auto pad = findAndAdoptPad(tabletPadId);
1920         if (!pad) {
1921             return false;
1922         }
1923         auto strip = pad->strip(number);
1924 
1925         strip->sendPosition(position);
1926         if (isFinger) {
1927             strip->sendSource(KWaylandServer::TabletPadStripV2Interface::SourceFinger);
1928         }
1929         strip->sendFrame(QDateTime::currentMSecsSinceEpoch());
1930         return true;
1931     }
1932 
1933     QHash<KWaylandServer::TabletToolV2Interface*, Cursor*> m_cursorByTool;
1934 };
1935 
dropHandler(Toplevel * toplevel)1936 static KWaylandServer::AbstractDropHandler *dropHandler(Toplevel *toplevel)
1937 {
1938     auto surface = toplevel->surface();
1939     if (!surface) {
1940         return nullptr;
1941     }
1942     auto seat = waylandServer()->seat();
1943     auto dropTarget = seat->dropHandlerForSurface(surface);
1944     if (dropTarget) {return dropTarget;}
1945 
1946     if (qobject_cast<X11Client*>(toplevel) && xwayland()) {
1947         return xwayland()->xwlDropHandler();
1948     }
1949 
1950     return nullptr;
1951 }
1952 
1953 class DragAndDropInputFilter : public QObject, public InputEventFilter
1954 {
1955     Q_OBJECT
1956 public:
DragAndDropInputFilter()1957     DragAndDropInputFilter()
1958     {
1959         m_raiseTimer.setSingleShot(true);
1960         m_raiseTimer.setInterval(250);
1961         connect(&m_raiseTimer, &QTimer::timeout, this, &DragAndDropInputFilter::raiseDragTarget);
1962     }
1963 
pointerEvent(QMouseEvent * event,quint32 nativeButton)1964     bool pointerEvent(QMouseEvent *event, quint32 nativeButton) override {
1965         auto seat = waylandServer()->seat();
1966         if (!seat->isDragPointer()) {
1967             return false;
1968         }
1969         if (seat->isDragTouch()) {
1970             return true;
1971         }
1972         seat->setTimestamp(event->timestamp());
1973         switch (event->type()) {
1974         case QEvent::MouseMove: {
1975             const auto pos = input()->globalPointer();
1976             seat->notifyPointerMotion(pos);
1977             seat->notifyPointerFrame();
1978 
1979             const auto eventPos = event->globalPos();
1980             // TODO: use InputDeviceHandler::at() here and check isClient()?
1981             Toplevel *t = input()->findManagedToplevel(eventPos);
1982             const auto dragTarget = qobject_cast<AbstractClient*>(t);
1983             if (dragTarget) {
1984                 if (dragTarget != m_dragTarget) {
1985                     workspace()->takeActivity(dragTarget, Workspace::ActivityFlag::ActivityFocus);
1986                     m_raiseTimer.start();
1987                 }
1988                 if ((pos - m_lastPos).manhattanLength() > 10) {
1989                     m_lastPos = pos;
1990                     // reset timer to delay raising the window
1991                     m_raiseTimer.start();
1992                 }
1993             }
1994             m_dragTarget = dragTarget;
1995 
1996             if (auto *xwl = xwayland()) {
1997                 const auto ret = xwl->dragMoveFilter(t, eventPos);
1998                 if (ret == Xwl::DragEventReply::Ignore) {
1999                     return false;
2000                 } else if (ret == Xwl::DragEventReply::Take) {
2001                     break;
2002                 }
2003             }
2004 
2005             if (t) {
2006                 // TODO: consider decorations
2007                 if (t->surface() != seat->dragSurface()) {
2008                     seat->setDragTarget(dropHandler(t), t->surface(), t->inputTransformation());
2009                 }
2010             } else {
2011                 // no window at that place, if we have a surface we need to reset
2012                 seat->setDragTarget(nullptr, nullptr);
2013                 m_dragTarget = nullptr;
2014             }
2015             break;
2016         }
2017         case QEvent::MouseButtonPress:
2018             seat->notifyPointerButton(nativeButton, KWaylandServer::PointerButtonState::Pressed);
2019             seat->notifyPointerFrame();
2020             break;
2021         case QEvent::MouseButtonRelease:
2022             raiseDragTarget();
2023             m_dragTarget = nullptr;
2024             seat->notifyPointerButton(nativeButton, KWaylandServer::PointerButtonState::Released);
2025             seat->notifyPointerFrame();
2026             break;
2027         default:
2028             break;
2029         }
2030         // TODO: should we pass through effects?
2031         return true;
2032     }
2033 
touchDown(qint32 id,const QPointF & pos,quint32 time)2034     bool touchDown(qint32 id, const QPointF &pos, quint32 time) override {
2035         auto seat = waylandServer()->seat();
2036         if (seat->isDragPointer()) {
2037             return true;
2038         }
2039         if (!seat->isDragTouch()) {
2040             return false;
2041         }
2042         if (m_touchId != id) {
2043             return true;
2044         }
2045         seat->setTimestamp(time);
2046         seat->notifyTouchDown(id, pos);
2047         m_lastPos = pos;
2048         return true;
2049     }
touchMotion(qint32 id,const QPointF & pos,quint32 time)2050     bool touchMotion(qint32 id, const QPointF &pos, quint32 time) override {
2051         auto seat = waylandServer()->seat();
2052         if (seat->isDragPointer()) {
2053             return true;
2054         }
2055         if (!seat->isDragTouch()) {
2056             return false;
2057         }
2058         if (m_touchId < 0) {
2059             // We take for now the first id appearing as a move after a drag
2060             // started. We can optimize by specifying the id the drag is
2061             // associated with by implementing a key-value getter in KWayland.
2062             m_touchId = id;
2063         }
2064         if (m_touchId != id) {
2065             return true;
2066         }
2067         seat->setTimestamp(time);
2068         seat->notifyTouchMotion(id, pos);
2069 
2070         if (Toplevel *t = input()->findToplevel(pos.toPoint())) {
2071             // TODO: consider decorations
2072             if (t->surface() != seat->dragSurface()) {
2073                 if ((m_dragTarget = qobject_cast<AbstractClient*>(t))) {
2074                     workspace()->takeActivity(m_dragTarget, Workspace::ActivityFlag::ActivityFocus);
2075                     m_raiseTimer.start();
2076                 }
2077                 seat->setDragTarget(dropHandler(t), t->surface(), pos, t->inputTransformation());
2078             }
2079             if ((pos - m_lastPos).manhattanLength() > 10) {
2080                 m_lastPos = pos;
2081                 // reset timer to delay raising the window
2082                 m_raiseTimer.start();
2083             }
2084         } else {
2085             // no window at that place, if we have a surface we need to reset
2086             seat->setDragTarget(nullptr, nullptr);
2087             m_dragTarget = nullptr;
2088         }
2089         return true;
2090     }
touchUp(qint32 id,quint32 time)2091     bool touchUp(qint32 id, quint32 time) override {
2092         auto seat = waylandServer()->seat();
2093         if (!seat->isDragTouch()) {
2094             return false;
2095         }
2096         seat->setTimestamp(time);
2097         seat->notifyTouchUp(id);
2098         if (m_touchId == id) {
2099             m_touchId = -1;
2100             raiseDragTarget();
2101         }
2102         return true;
2103     }
2104 private:
raiseDragTarget()2105     void raiseDragTarget()
2106     {
2107         m_raiseTimer.stop();
2108         if (m_dragTarget) {
2109             workspace()->takeActivity(m_dragTarget, Workspace::ActivityFlag::ActivityRaise);
2110         }
2111     }
2112     qint32 m_touchId = -1;
2113     QPointF m_lastPos = QPointF(-1, -1);
2114     QPointer<AbstractClient> m_dragTarget;
2115     QTimer m_raiseTimer;
2116 };
2117 
2118 KWIN_SINGLETON_FACTORY(InputRedirection)
2119 
2120 static const QString s_touchpadComponent = QStringLiteral("kcm_touchpad");
2121 
InputRedirection(QObject * parent)2122 InputRedirection::InputRedirection(QObject *parent)
2123     : QObject(parent)
2124     , m_keyboard(new KeyboardInputRedirection(this))
2125     , m_pointer(new PointerInputRedirection(this))
2126     , m_tablet(new TabletInputRedirection(this))
2127     , m_touch(new TouchInputRedirection(this))
2128     , m_shortcuts(new GlobalShortcutsManager(this))
2129 {
2130     qRegisterMetaType<KWin::InputRedirection::KeyboardKeyState>();
2131     qRegisterMetaType<KWin::InputRedirection::PointerButtonState>();
2132     qRegisterMetaType<KWin::InputRedirection::PointerAxis>();
2133     if (Application::usesLibinput()) {
2134         setupLibInput();
2135     }
2136     connect(kwinApp(), &Application::workspaceCreated, this, &InputRedirection::setupWorkspace);
2137 }
2138 
~InputRedirection()2139 InputRedirection::~InputRedirection()
2140 {
2141     if (m_libInput) {
2142         m_libInput->deleteLater();
2143 
2144         m_libInputThread->quit();
2145         m_libInputThread->wait();
2146         delete m_libInputThread;
2147     }
2148 
2149     s_self = nullptr;
2150     qDeleteAll(m_filters);
2151     qDeleteAll(m_spies);
2152 }
2153 
installInputEventFilter(InputEventFilter * filter)2154 void InputRedirection::installInputEventFilter(InputEventFilter *filter)
2155 {
2156     Q_ASSERT(!m_filters.contains(filter));
2157     m_filters << filter;
2158 }
2159 
prependInputEventFilter(InputEventFilter * filter)2160 void InputRedirection::prependInputEventFilter(InputEventFilter *filter)
2161 {
2162     Q_ASSERT(!m_filters.contains(filter));
2163     m_filters.prepend(filter);
2164 }
2165 
uninstallInputEventFilter(InputEventFilter * filter)2166 void InputRedirection::uninstallInputEventFilter(InputEventFilter *filter)
2167 {
2168     m_filters.removeOne(filter);
2169 }
2170 
installInputEventSpy(InputEventSpy * spy)2171 void InputRedirection::installInputEventSpy(InputEventSpy *spy)
2172 {
2173     m_spies << spy;
2174 }
2175 
uninstallInputEventSpy(InputEventSpy * spy)2176 void InputRedirection::uninstallInputEventSpy(InputEventSpy *spy)
2177 {
2178     m_spies.removeOne(spy);
2179 }
2180 
init()2181 void InputRedirection::init()
2182 {
2183     m_shortcuts->init();
2184 }
2185 
setupWorkspace()2186 void InputRedirection::setupWorkspace()
2187 {
2188     if (waylandServer()) {
2189         using namespace KWaylandServer;
2190         FakeInputInterface *fakeInput = new FakeInputInterface(waylandServer()->display(), this);
2191         connect(fakeInput, &FakeInputInterface::deviceCreated, this,
2192             [this] (FakeInputDevice *device) {
2193                 connect(device, &FakeInputDevice::authenticationRequested, this,
2194                     [device] (const QString &application, const QString &reason) {
2195                         Q_UNUSED(application)
2196                         Q_UNUSED(reason)
2197                         // TODO: make secure
2198                         device->setAuthentication(true);
2199                     }
2200                 );
2201                 connect(device, &FakeInputDevice::pointerMotionRequested, this,
2202                     [this] (const QSizeF &delta) {
2203                         // TODO: Fix time
2204                         m_pointer->processMotion(globalPointer() + QPointF(delta.width(), delta.height()), 0);
2205                         waylandServer()->simulateUserActivity();
2206                     }
2207                 );
2208                connect(device, &FakeInputDevice::pointerMotionAbsoluteRequested, this,
2209                     [this] (const QPointF &pos) {
2210                         // TODO: Fix time
2211                         m_pointer->processMotion(pos, 0);
2212                         waylandServer()->simulateUserActivity();
2213                     }
2214                 );
2215                 connect(device, &FakeInputDevice::pointerButtonPressRequested, this,
2216                     [this] (quint32 button) {
2217                         // TODO: Fix time
2218                         m_pointer->processButton(button, InputRedirection::PointerButtonPressed, 0);
2219                         waylandServer()->simulateUserActivity();
2220                     }
2221                 );
2222                 connect(device, &FakeInputDevice::pointerButtonReleaseRequested, this,
2223                     [this] (quint32 button) {
2224                         // TODO: Fix time
2225                         m_pointer->processButton(button, InputRedirection::PointerButtonReleased, 0);
2226                         waylandServer()->simulateUserActivity();
2227                     }
2228                 );
2229                 connect(device, &FakeInputDevice::pointerAxisRequested, this,
2230                     [this] (Qt::Orientation orientation, qreal delta) {
2231                         // TODO: Fix time
2232                         InputRedirection::PointerAxis axis;
2233                         switch (orientation) {
2234                         case Qt::Horizontal:
2235                             axis = InputRedirection::PointerAxisHorizontal;
2236                             break;
2237                         case Qt::Vertical:
2238                             axis = InputRedirection::PointerAxisVertical;
2239                             break;
2240                         default:
2241                             Q_UNREACHABLE();
2242                             break;
2243                         }
2244                         // TODO: Fix time
2245                         m_pointer->processAxis(axis, delta, 0, InputRedirection::PointerAxisSourceUnknown, 0);
2246                         waylandServer()->simulateUserActivity();
2247                     }
2248                 );
2249                 connect(device, &FakeInputDevice::touchDownRequested, this,
2250                    [this] (qint32 id, const QPointF &pos) {
2251                        // TODO: Fix time
2252                        m_touch->processDown(id, pos, 0);
2253                         waylandServer()->simulateUserActivity();
2254                    }
2255                 );
2256                 connect(device, &FakeInputDevice::touchMotionRequested, this,
2257                    [this] (qint32 id, const QPointF &pos) {
2258                        // TODO: Fix time
2259                        m_touch->processMotion(id, pos, 0);
2260                         waylandServer()->simulateUserActivity();
2261                    }
2262                 );
2263                 connect(device, &FakeInputDevice::touchUpRequested, this,
2264                     [this] (qint32 id) {
2265                         // TODO: Fix time
2266                         m_touch->processUp(id, 0);
2267                         waylandServer()->simulateUserActivity();
2268                     }
2269                 );
2270                 connect(device, &FakeInputDevice::touchCancelRequested, this,
2271                     [this] () {
2272                         m_touch->cancel();
2273                     }
2274                 );
2275                 connect(device, &FakeInputDevice::touchFrameRequested, this,
2276                    [this] () {
2277                        m_touch->frame();
2278                    }
2279                 );
2280                 connect(device, &FakeInputDevice::keyboardKeyPressRequested, this,
2281                     [this] (quint32 button) {
2282                         // TODO: Fix time
2283                         m_keyboard->processKey(button, InputRedirection::KeyboardKeyPressed, 0);
2284                         waylandServer()->simulateUserActivity();
2285                     }
2286                 );
2287                 connect(device, &FakeInputDevice::keyboardKeyReleaseRequested, this,
2288                     [this] (quint32 button) {
2289                         // TODO: Fix time
2290                         m_keyboard->processKey(button, InputRedirection::KeyboardKeyReleased, 0);
2291                         waylandServer()->simulateUserActivity();
2292                     }
2293                 );
2294             }
2295         );
2296 
2297         m_keyboard->init();
2298         m_pointer->init();
2299         m_touch->init();
2300         m_tablet->init();
2301     }
2302     setupTouchpadShortcuts();
2303     setupInputFilters();
2304 }
2305 
setupInputFilters()2306 void InputRedirection::setupInputFilters()
2307 {
2308     const bool hasGlobalShortcutSupport = !waylandServer() || waylandServer()->hasGlobalShortcutSupport();
2309     if ((kwinApp()->platform()->session()->capabilities() & Session::Capability::SwitchTerminal)
2310             && hasGlobalShortcutSupport) {
2311         installInputEventFilter(new VirtualTerminalFilter);
2312     }
2313     if (waylandServer()) {
2314         installInputEventSpy(new TouchHideCursorSpy);
2315         if (hasGlobalShortcutSupport) {
2316             installInputEventFilter(new TerminateServerFilter);
2317         }
2318         installInputEventFilter(new DragAndDropInputFilter);
2319         installInputEventFilter(new LockScreenFilter);
2320         m_windowSelector = new WindowSelectorFilter;
2321         installInputEventFilter(m_windowSelector);
2322     }
2323     if (hasGlobalShortcutSupport) {
2324         installInputEventFilter(new ScreenEdgeInputFilter);
2325     }
2326     installInputEventFilter(new EffectsFilter);
2327     installInputEventFilter(new MoveResizeFilter);
2328 #ifdef KWIN_BUILD_TABBOX
2329     installInputEventFilter(new TabBoxInputFilter);
2330 #endif
2331     if (hasGlobalShortcutSupport) {
2332         installInputEventFilter(new GlobalShortcutFilter);
2333     }
2334     if (waylandServer()) {
2335         installInputEventFilter(new PopupInputFilter);
2336     }
2337     installInputEventFilter(new DecorationEventFilter);
2338     installInputEventFilter(new InternalWindowEventFilter);
2339     if (waylandServer()) {
2340         installInputEventFilter(new WindowActionInputFilter);
2341         installInputEventFilter(new ForwardInputFilter);
2342 
2343         if (m_libInput) {
2344             m_tabletSupport = new TabletInputFilter;
2345             const QVector<LibInput::Device *> devices = m_libInput->devices();
2346             for (LibInput::Device *dev : devices) {
2347                 m_tabletSupport->integrateDevice(dev);
2348             }
2349             connect(m_libInput, &LibInput::Connection::deviceAdded, m_tabletSupport, &TabletInputFilter::integrateDevice);
2350             connect(m_libInput, &LibInput::Connection::deviceRemoved, m_tabletSupport, &TabletInputFilter::removeDevice);
2351 
2352             connect(m_libInput, &LibInput::Connection::deviceRemovedSysName, m_tabletSupport, &TabletInputFilter::removeDeviceBySysName);
2353             installInputEventFilter(m_tabletSupport);
2354         }
2355     }
2356 }
2357 
handleInputConfigChanged(const KConfigGroup & group)2358 void InputRedirection::handleInputConfigChanged(const KConfigGroup &group)
2359 {
2360     if (group.name() == QLatin1String("Keyboard")) {
2361         reconfigure();
2362     }
2363 }
2364 
reconfigure()2365 void InputRedirection::reconfigure()
2366 {
2367     if (Application::usesLibinput()) {
2368         auto inputConfig = m_inputConfigWatcher->config();
2369         const auto config = inputConfig->group(QStringLiteral("Keyboard"));
2370         const int delay = config.readEntry("RepeatDelay", 660);
2371         const int rate = std::ceil(config.readEntry("RepeatRate", 25.0));
2372         const QString repeatMode = config.readEntry("KeyRepeat", "repeat");
2373         // when the clients will repeat the character or turn repeat key events into an accent character selection, we want
2374         // to tell the clients that we are indeed repeating keys.
2375         const bool enabled = repeatMode == QLatin1String("accent") || repeatMode == QLatin1String("repeat");
2376 
2377         waylandServer()->seat()->keyboard()->setRepeatInfo(enabled ? rate : 0, delay);
2378     }
2379 }
2380 
setupLibInput()2381 void InputRedirection::setupLibInput()
2382 {
2383     if (!Application::usesLibinput()) {
2384         return;
2385     }
2386     if (m_libInput) {
2387         return;
2388     }
2389 
2390     m_libInputThread = new QThread();
2391     m_libInputThread->setObjectName(QStringLiteral("libinput-connection"));
2392     m_libInputThread->start();
2393 
2394     LibInput::Connection *conn = LibInput::Connection::create(this);
2395     m_libInput = conn;
2396     m_libInput->moveToThread(m_libInputThread);
2397 
2398     if (conn) {
2399 
2400         if (waylandServer()) {
2401             // create relative pointer manager
2402             new KWaylandServer::RelativePointerManagerV1Interface(waylandServer()->display(),
2403                                                                   waylandServer()->display());
2404         }
2405 
2406         conn->setInputConfig(InputConfig::self()->inputConfig());
2407         conn->updateLEDs(m_keyboard->xkb()->leds());
2408         waylandServer()->updateKeyState(m_keyboard->xkb()->leds());
2409         connect(m_keyboard, &KeyboardInputRedirection::ledsChanged, waylandServer(), &WaylandServer::updateKeyState);
2410         connect(m_keyboard, &KeyboardInputRedirection::ledsChanged, conn, &LibInput::Connection::updateLEDs);
2411         connect(conn, &LibInput::Connection::eventsRead, this,
2412             [this] {
2413                 m_libInput->processEvents();
2414             }, Qt::QueuedConnection
2415         );
2416         conn->setup();
2417         connect(conn, &LibInput::Connection::pointerButtonChanged, m_pointer, &PointerInputRedirection::processButton);
2418         connect(conn, &LibInput::Connection::pointerAxisChanged, m_pointer, &PointerInputRedirection::processAxis);
2419         connect(conn, &LibInput::Connection::pinchGestureBegin, m_pointer, &PointerInputRedirection::processPinchGestureBegin);
2420         connect(conn, &LibInput::Connection::pinchGestureUpdate, m_pointer, &PointerInputRedirection::processPinchGestureUpdate);
2421         connect(conn, &LibInput::Connection::pinchGestureEnd, m_pointer, &PointerInputRedirection::processPinchGestureEnd);
2422         connect(conn, &LibInput::Connection::pinchGestureCancelled, m_pointer, &PointerInputRedirection::processPinchGestureCancelled);
2423         connect(conn, &LibInput::Connection::swipeGestureBegin, m_pointer, &PointerInputRedirection::processSwipeGestureBegin);
2424         connect(conn, &LibInput::Connection::swipeGestureUpdate, m_pointer, &PointerInputRedirection::processSwipeGestureUpdate);
2425         connect(conn, &LibInput::Connection::swipeGestureEnd, m_pointer, &PointerInputRedirection::processSwipeGestureEnd);
2426         connect(conn, &LibInput::Connection::swipeGestureCancelled, m_pointer, &PointerInputRedirection::processSwipeGestureCancelled);
2427         connect(conn, &LibInput::Connection::keyChanged, m_keyboard, &KeyboardInputRedirection::processKey);
2428         connect(conn, &LibInput::Connection::pointerMotion, this,
2429             [this] (const QSizeF &delta, const QSizeF &deltaNonAccel, uint32_t time, quint64 timeMicroseconds, LibInput::Device *device) {
2430                 m_pointer->processMotion(m_pointer->pos() + QPointF(delta.width(), delta.height()), delta, deltaNonAccel, time, timeMicroseconds, device);
2431             }
2432         );
2433         connect(conn, &LibInput::Connection::pointerMotionAbsolute, this,
2434             [this] (QPointF orig, QPointF screen, uint32_t time, LibInput::Device *device) {
2435                 Q_UNUSED(orig)
2436                 m_pointer->processMotion(screen, time, device);
2437             }
2438         );
2439         connect(conn, &LibInput::Connection::touchDown, m_touch, &TouchInputRedirection::processDown);
2440         connect(conn, &LibInput::Connection::touchUp, m_touch, &TouchInputRedirection::processUp);
2441         connect(conn, &LibInput::Connection::touchMotion, m_touch, &TouchInputRedirection::processMotion);
2442         connect(conn, &LibInput::Connection::touchCanceled, m_touch, &TouchInputRedirection::cancel);
2443         connect(conn, &LibInput::Connection::touchFrame, m_touch, &TouchInputRedirection::frame);
2444         auto handleSwitchEvent = [this] (SwitchEvent::State state, quint32 time, quint64 timeMicroseconds, LibInput::Device *device) {
2445             SwitchEvent event(state, time, timeMicroseconds, device);
2446             processSpies(std::bind(&InputEventSpy::switchEvent, std::placeholders::_1, &event));
2447             processFilters(std::bind(&InputEventFilter::switchEvent, std::placeholders::_1, &event));
2448         };
2449         connect(conn, &LibInput::Connection::switchToggledOn, this,
2450                 std::bind(handleSwitchEvent, SwitchEvent::State::On, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
2451         connect(conn, &LibInput::Connection::switchToggledOff, this,
2452                 std::bind(handleSwitchEvent, SwitchEvent::State::Off, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
2453 
2454         connect(conn, &LibInput::Connection::tabletToolEvent,
2455                 m_tablet, &TabletInputRedirection::tabletToolEvent);
2456         connect(conn, &LibInput::Connection::tabletToolButtonEvent,
2457                 m_tablet, &TabletInputRedirection::tabletToolButtonEvent);
2458         connect(conn, &LibInput::Connection::tabletPadButtonEvent,
2459                 m_tablet, &TabletInputRedirection::tabletPadButtonEvent);
2460         connect(conn, &LibInput::Connection::tabletPadRingEvent,
2461                 m_tablet, &TabletInputRedirection::tabletPadRingEvent);
2462         connect(conn, &LibInput::Connection::tabletPadStripEvent,
2463                 m_tablet, &TabletInputRedirection::tabletPadStripEvent);
2464 
2465         if (screens()) {
2466             setupLibInputWithScreens();
2467         } else {
2468             connect(kwinApp(), &Application::screensCreated, this, &InputRedirection::setupLibInputWithScreens);
2469         }
2470         if (auto s = findSeat()) {
2471             // Workaround for QTBUG-54371: if there is no real keyboard Qt doesn't request virtual keyboard
2472             s->setHasKeyboard(true);
2473             s->setHasPointer(conn->hasPointer());
2474             s->setHasTouch(conn->hasTouch());
2475             connect(conn, &LibInput::Connection::hasAlphaNumericKeyboardChanged, this,
2476                 [this] (bool set) {
2477                     if (m_libInput->isSuspended()) {
2478                         return;
2479                     }
2480                     // TODO: this should update the seat, only workaround for QTBUG-54371
2481                     Q_EMIT hasAlphaNumericKeyboardChanged(set);
2482                 }
2483             );
2484             connect(conn, &LibInput::Connection::hasTabletModeSwitchChanged, this,
2485                 [this] (bool set) {
2486                     if (m_libInput->isSuspended()) {
2487                         return;
2488                     }
2489                     Q_EMIT hasTabletModeSwitchChanged(set);
2490                 }
2491             );
2492             connect(conn, &LibInput::Connection::hasPointerChanged, this,
2493                 [this, s] (bool set) {
2494                     if (m_libInput->isSuspended()) {
2495                         return;
2496                     }
2497                     s->setHasPointer(set);
2498                 }
2499             );
2500             connect(conn, &LibInput::Connection::hasTouchChanged, this,
2501                 [this, s] (bool set) {
2502                     if (m_libInput->isSuspended()) {
2503                         return;
2504                     }
2505                     s->setHasTouch(set);
2506                 }
2507             );
2508         }
2509         connect(kwinApp()->platform()->session(), &Session::activeChanged, m_libInput, [this](bool active) {
2510             if (!active) {
2511                 m_libInput->deactivate();
2512             }
2513         });
2514 
2515         m_inputConfigWatcher = KConfigWatcher::create(InputConfig::self()->inputConfig());
2516         connect(m_inputConfigWatcher.data(), &KConfigWatcher::configChanged,
2517                 this, &InputRedirection::handleInputConfigChanged);
2518         reconfigure();
2519     }
2520 }
2521 
setupTouchpadShortcuts()2522 void InputRedirection::setupTouchpadShortcuts()
2523 {
2524     if (!m_libInput) {
2525         return;
2526     }
2527     QAction *touchpadToggleAction = new QAction(this);
2528     QAction *touchpadOnAction = new QAction(this);
2529     QAction *touchpadOffAction = new QAction(this);
2530 
2531     const QString touchpadDisplayName = i18n("Touchpad");
2532 
2533     touchpadToggleAction->setObjectName(QStringLiteral("Toggle Touchpad"));
2534     touchpadToggleAction->setProperty("componentName", s_touchpadComponent);
2535     touchpadToggleAction->setProperty("componentDisplayName", touchpadDisplayName);
2536     touchpadOnAction->setObjectName(QStringLiteral("Enable Touchpad"));
2537     touchpadOnAction->setProperty("componentName", s_touchpadComponent);
2538     touchpadOnAction->setProperty("componentDisplayName", touchpadDisplayName);
2539     touchpadOffAction->setObjectName(QStringLiteral("Disable Touchpad"));
2540     touchpadOffAction->setProperty("componentName", s_touchpadComponent);
2541     touchpadOffAction->setProperty("componentDisplayName", touchpadDisplayName);
2542     KGlobalAccel::self()->setDefaultShortcut(touchpadToggleAction, QList<QKeySequence>{Qt::Key_TouchpadToggle});
2543     KGlobalAccel::self()->setShortcut(touchpadToggleAction, QList<QKeySequence>{Qt::Key_TouchpadToggle});
2544     KGlobalAccel::self()->setDefaultShortcut(touchpadOnAction, QList<QKeySequence>{Qt::Key_TouchpadOn});
2545     KGlobalAccel::self()->setShortcut(touchpadOnAction, QList<QKeySequence>{Qt::Key_TouchpadOn});
2546     KGlobalAccel::self()->setDefaultShortcut(touchpadOffAction, QList<QKeySequence>{Qt::Key_TouchpadOff});
2547     KGlobalAccel::self()->setShortcut(touchpadOffAction, QList<QKeySequence>{Qt::Key_TouchpadOff});
2548 #ifndef KWIN_BUILD_TESTING
2549     registerShortcut(Qt::Key_TouchpadToggle, touchpadToggleAction);
2550     registerShortcut(Qt::Key_TouchpadOn, touchpadOnAction);
2551     registerShortcut(Qt::Key_TouchpadOff, touchpadOffAction);
2552 #endif
2553     connect(touchpadToggleAction, &QAction::triggered, m_libInput, &LibInput::Connection::toggleTouchpads);
2554     connect(touchpadOnAction, &QAction::triggered, m_libInput, &LibInput::Connection::enableTouchpads);
2555     connect(touchpadOffAction, &QAction::triggered, m_libInput, &LibInput::Connection::disableTouchpads);
2556 }
2557 
hasAlphaNumericKeyboard()2558 bool InputRedirection::hasAlphaNumericKeyboard()
2559 {
2560     if (m_libInput) {
2561         return m_libInput->hasAlphaNumericKeyboard();
2562     }
2563     return true;
2564 }
2565 
hasTabletModeSwitch()2566 bool InputRedirection::hasTabletModeSwitch()
2567 {
2568     if (m_libInput) {
2569         return m_libInput->hasTabletModeSwitch();
2570     }
2571     return false;
2572 }
2573 
setupLibInputWithScreens()2574 void InputRedirection::setupLibInputWithScreens()
2575 {
2576     if (!screens() || !m_libInput) {
2577         return;
2578     }
2579     m_libInput->setScreenSize(screens()->size());
2580     m_libInput->updateScreens();
2581     connect(screens(), &Screens::sizeChanged, this,
2582         [this] {
2583             m_libInput->setScreenSize(screens()->size());
2584         }
2585     );
2586     connect(screens(), &Screens::changed, m_libInput, &LibInput::Connection::updateScreens);
2587 }
2588 
processPointerMotion(const QPointF & pos,uint32_t time)2589 void InputRedirection::processPointerMotion(const QPointF &pos, uint32_t time)
2590 {
2591     m_pointer->processMotion(pos, time);
2592 }
2593 
processPointerButton(uint32_t button,InputRedirection::PointerButtonState state,uint32_t time)2594 void InputRedirection::processPointerButton(uint32_t button, InputRedirection::PointerButtonState state, uint32_t time)
2595 {
2596     m_pointer->processButton(button, state, time);
2597 }
2598 
processPointerAxis(InputRedirection::PointerAxis axis,qreal delta,qint32 discreteDelta,PointerAxisSource source,uint32_t time)2599 void InputRedirection::processPointerAxis(InputRedirection::PointerAxis axis, qreal delta, qint32 discreteDelta, PointerAxisSource source, uint32_t time)
2600 {
2601     m_pointer->processAxis(axis, delta, discreteDelta, source, time);
2602 }
2603 
processKeyboardKey(uint32_t key,InputRedirection::KeyboardKeyState state,uint32_t time)2604 void InputRedirection::processKeyboardKey(uint32_t key, InputRedirection::KeyboardKeyState state, uint32_t time)
2605 {
2606     m_keyboard->processKey(key, state, time);
2607 }
2608 
processKeyboardModifiers(uint32_t modsDepressed,uint32_t modsLatched,uint32_t modsLocked,uint32_t group)2609 void InputRedirection::processKeyboardModifiers(uint32_t modsDepressed, uint32_t modsLatched, uint32_t modsLocked, uint32_t group)
2610 {
2611     m_keyboard->processModifiers(modsDepressed, modsLatched, modsLocked, group);
2612 }
2613 
processKeymapChange(int fd,uint32_t size)2614 void InputRedirection::processKeymapChange(int fd, uint32_t size)
2615 {
2616     m_keyboard->processKeymapChange(fd, size);
2617 }
2618 
processTouchDown(qint32 id,const QPointF & pos,quint32 time)2619 void InputRedirection::processTouchDown(qint32 id, const QPointF &pos, quint32 time)
2620 {
2621     m_touch->processDown(id, pos, time);
2622 }
2623 
processTouchUp(qint32 id,quint32 time)2624 void InputRedirection::processTouchUp(qint32 id, quint32 time)
2625 {
2626     m_touch->processUp(id, time);
2627 }
2628 
processTouchMotion(qint32 id,const QPointF & pos,quint32 time)2629 void InputRedirection::processTouchMotion(qint32 id, const QPointF &pos, quint32 time)
2630 {
2631     m_touch->processMotion(id, pos, time);
2632 }
2633 
cancelTouchSequence()2634 void InputRedirection::cancelTouchSequence()
2635 {
2636     m_touch->cancel();
2637 }
2638 
cancelTouch()2639 void InputRedirection::cancelTouch()
2640 {
2641     m_touch->cancel();
2642 }
2643 
touchFrame()2644 void InputRedirection::touchFrame()
2645 {
2646     m_touch->frame();
2647 }
2648 
touchPointCount()2649 int InputRedirection::touchPointCount()
2650 {
2651     return m_touch->touchPointCount();
2652 }
2653 
qtButtonStates() const2654 Qt::MouseButtons InputRedirection::qtButtonStates() const
2655 {
2656     return m_pointer->buttons();
2657 }
2658 
findToplevel(const QPoint & pos)2659 Toplevel *InputRedirection::findToplevel(const QPoint &pos)
2660 {
2661     if (!Workspace::self()) {
2662         return nullptr;
2663     }
2664     const bool isScreenLocked = waylandServer() && waylandServer()->isScreenLocked();
2665     // TODO: check whether the unmanaged wants input events at all
2666     if (!isScreenLocked) {
2667         // if an effect overrides the cursor we don't have a window to focus
2668         if (effects && static_cast<EffectsHandlerImpl*>(effects)->isMouseInterception()) {
2669             return nullptr;
2670         }
2671         const QList<Unmanaged *> &unmanaged = Workspace::self()->unmanagedList();
2672         Q_FOREACH (Unmanaged *u, unmanaged) {
2673             if (u->hitTest(pos)) {
2674                 return u;
2675             }
2676         }
2677     }
2678     return findManagedToplevel(pos);
2679 }
2680 
findManagedToplevel(const QPoint & pos)2681 Toplevel *InputRedirection::findManagedToplevel(const QPoint &pos)
2682 {
2683     if (!Workspace::self()) {
2684         return nullptr;
2685     }
2686     const bool isScreenLocked = waylandServer() && waylandServer()->isScreenLocked();
2687     const QList<Toplevel *> &stacking = Workspace::self()->stackingOrder();
2688     if (stacking.isEmpty()) {
2689         return nullptr;
2690     }
2691     auto it = stacking.end();
2692     do {
2693         --it;
2694         Toplevel *t = (*it);
2695         if (t->isDeleted()) {
2696             // a deleted window doesn't get mouse events
2697             continue;
2698         }
2699         if (AbstractClient *c = dynamic_cast<AbstractClient*>(t)) {
2700             if (!c->isOnCurrentActivity() || !c->isOnCurrentDesktop() || c->isMinimized() || c->isHiddenInternal()) {
2701                 continue;
2702             }
2703         }
2704         if (!t->readyForPainting()) {
2705             continue;
2706         }
2707         if (isScreenLocked) {
2708             if (!t->isLockScreen() && !t->isInputMethod()) {
2709                 continue;
2710             }
2711         }
2712         if (t->hitTest(pos)) {
2713             return t;
2714         }
2715     } while (it != stacking.begin());
2716     return nullptr;
2717 }
2718 
keyboardModifiers() const2719 Qt::KeyboardModifiers InputRedirection::keyboardModifiers() const
2720 {
2721     return m_keyboard->modifiers();
2722 }
2723 
modifiersRelevantForGlobalShortcuts() const2724 Qt::KeyboardModifiers InputRedirection::modifiersRelevantForGlobalShortcuts() const
2725 {
2726     return m_keyboard->modifiersRelevantForGlobalShortcuts();
2727 }
2728 
registerShortcut(const QKeySequence & shortcut,QAction * action)2729 void InputRedirection::registerShortcut(const QKeySequence &shortcut, QAction *action)
2730 {
2731     Q_UNUSED(shortcut)
2732     kwinApp()->platform()->setupActionForGlobalAccel(action);
2733 }
2734 
registerPointerShortcut(Qt::KeyboardModifiers modifiers,Qt::MouseButton pointerButtons,QAction * action)2735 void InputRedirection::registerPointerShortcut(Qt::KeyboardModifiers modifiers, Qt::MouseButton pointerButtons, QAction *action)
2736 {
2737     m_shortcuts->registerPointerShortcut(action, modifiers, pointerButtons);
2738 }
2739 
registerAxisShortcut(Qt::KeyboardModifiers modifiers,PointerAxisDirection axis,QAction * action)2740 void InputRedirection::registerAxisShortcut(Qt::KeyboardModifiers modifiers, PointerAxisDirection axis, QAction *action)
2741 {
2742     m_shortcuts->registerAxisShortcut(action, modifiers, axis);
2743 }
2744 
registerRealtimeTouchpadSwipeShortcut(SwipeDirection direction,QAction * action,std::function<void (qreal)> cb)2745 void InputRedirection::registerRealtimeTouchpadSwipeShortcut(SwipeDirection direction, QAction *action, std::function<void(qreal)> cb)
2746 {
2747     m_shortcuts->registerRealtimeTouchpadSwipe(action, cb, direction);
2748 }
2749 
registerTouchpadSwipeShortcut(SwipeDirection direction,QAction * action)2750 void InputRedirection::registerTouchpadSwipeShortcut(SwipeDirection direction, QAction *action)
2751 {
2752     m_shortcuts->registerTouchpadSwipe(action, direction);
2753 }
2754 
registerGlobalAccel(KGlobalAccelInterface * interface)2755 void InputRedirection::registerGlobalAccel(KGlobalAccelInterface *interface)
2756 {
2757     m_shortcuts->setKGlobalAccelInterface(interface);
2758 }
2759 
warpPointer(const QPointF & pos)2760 void InputRedirection::warpPointer(const QPointF &pos)
2761 {
2762     m_pointer->warp(pos);
2763 }
2764 
supportsPointerWarping() const2765 bool InputRedirection::supportsPointerWarping() const
2766 {
2767     return m_pointer->supportsWarping();
2768 }
2769 
globalPointer() const2770 QPointF InputRedirection::globalPointer() const
2771 {
2772     return m_pointer->pos();
2773 }
2774 
startInteractiveWindowSelection(std::function<void (KWin::Toplevel *)> callback,const QByteArray & cursorName)2775 void InputRedirection::startInteractiveWindowSelection(std::function<void(KWin::Toplevel*)> callback, const QByteArray &cursorName)
2776 {
2777     if (!m_windowSelector || m_windowSelector->isActive()) {
2778         callback(nullptr);
2779         return;
2780     }
2781     m_windowSelector->start(callback);
2782     m_pointer->setWindowSelectionCursor(cursorName);
2783 }
2784 
startInteractivePositionSelection(std::function<void (const QPoint &)> callback)2785 void InputRedirection::startInteractivePositionSelection(std::function<void(const QPoint &)> callback)
2786 {
2787     if (!m_windowSelector || m_windowSelector->isActive()) {
2788         callback(QPoint(-1, -1));
2789         return;
2790     }
2791     m_windowSelector->start(callback);
2792     m_pointer->setWindowSelectionCursor(QByteArray());
2793 }
2794 
isSelectingWindow() const2795 bool InputRedirection::isSelectingWindow() const
2796 {
2797     return m_windowSelector ? m_windowSelector->isActive() : false;
2798 }
2799 
InputDeviceHandler(InputRedirection * input)2800 InputDeviceHandler::InputDeviceHandler(InputRedirection *input)
2801     : QObject(input)
2802 {
2803 }
2804 
2805 InputDeviceHandler::~InputDeviceHandler() = default;
2806 
init()2807 void InputDeviceHandler::init()
2808 {
2809     connect(workspace(), &Workspace::stackingOrderChanged, this, &InputDeviceHandler::update);
2810     connect(workspace(), &Workspace::clientMinimizedChanged, this, &InputDeviceHandler::update);
2811     connect(VirtualDesktopManager::self(), &VirtualDesktopManager::currentChanged, this, &InputDeviceHandler::update);
2812 }
2813 
setAt(Toplevel * toplevel)2814 bool InputDeviceHandler::setAt(Toplevel *toplevel)
2815 {
2816     if (m_at.at == toplevel) {
2817         return false;
2818     }
2819     auto old = m_at.at;
2820     disconnect(m_at.surfaceCreatedConnection);
2821     m_at.surfaceCreatedConnection = QMetaObject::Connection();
2822 
2823     m_at.at = toplevel;
2824     Q_EMIT atChanged(old, toplevel);
2825     return true;
2826 }
2827 
setFocus(Toplevel * toplevel)2828 void InputDeviceHandler::setFocus(Toplevel *toplevel)
2829 {
2830     m_focus.focus = toplevel;
2831     //TODO: call focusUpdate?
2832 }
2833 
setDecoration(Decoration::DecoratedClientImpl * decoration)2834 void InputDeviceHandler::setDecoration(Decoration::DecoratedClientImpl *decoration)
2835 {
2836     auto oldDeco = m_focus.decoration;
2837     m_focus.decoration = decoration;
2838     cleanupDecoration(oldDeco.data(), m_focus.decoration.data());
2839     Q_EMIT decorationChanged();
2840 }
2841 
setInternalWindow(QWindow * window)2842 void InputDeviceHandler::setInternalWindow(QWindow *window)
2843 {
2844     m_focus.internalWindow = window;
2845     //TODO: call internalWindowUpdate?
2846 }
2847 
updateFocus()2848 void InputDeviceHandler::updateFocus()
2849 {
2850     auto oldFocus = m_focus.focus;
2851 
2852     if (m_at.at && !m_at.at->surface()) {
2853         // The surface has not yet been created (special XWayland case).
2854         // Therefore listen for its creation.
2855         if (!m_at.surfaceCreatedConnection) {
2856             m_at.surfaceCreatedConnection = connect(m_at.at, &Toplevel::surfaceChanged,
2857                                                     this, &InputDeviceHandler::update);
2858         }
2859         m_focus.focus = nullptr;
2860     } else {
2861         m_focus.focus = m_at.at;
2862     }
2863 
2864     focusUpdate(oldFocus, m_focus.focus);
2865 }
2866 
updateDecoration()2867 bool InputDeviceHandler::updateDecoration()
2868 {
2869     const auto oldDeco = m_focus.decoration;
2870     m_focus.decoration = nullptr;
2871 
2872     auto *ac = qobject_cast<AbstractClient*>(m_at.at);
2873     if (ac && ac->decoratedClient()) {
2874         if (!ac->clientGeometry().contains(position().toPoint())) {
2875             // input device above decoration
2876             m_focus.decoration = ac->decoratedClient();
2877         }
2878     }
2879 
2880     if (m_focus.decoration == oldDeco) {
2881         // no change to decoration
2882         return false;
2883     }
2884     cleanupDecoration(oldDeco.data(), m_focus.decoration.data());
2885     Q_EMIT decorationChanged();
2886     return true;
2887 }
2888 
updateInternalWindow(QWindow * window)2889 void InputDeviceHandler::updateInternalWindow(QWindow *window)
2890 {
2891     if (m_focus.internalWindow == window) {
2892         // no change
2893         return;
2894     }
2895     const auto oldInternal = m_focus.internalWindow;
2896     m_focus.internalWindow = window;
2897     cleanupInternalWindow(oldInternal, window);
2898 }
2899 
update()2900 void InputDeviceHandler::update()
2901 {
2902     if (!m_inited) {
2903         return;
2904     }
2905 
2906     Toplevel *toplevel = nullptr;
2907     if (positionValid()) {
2908         toplevel = input()->findToplevel(position().toPoint());
2909     }
2910     // Always set the toplevel at the position of the input device.
2911     setAt(toplevel);
2912 
2913     if (focusUpdatesBlocked()) {
2914         workspace()->updateFocusMousePosition(position().toPoint());
2915         return;
2916     }
2917 
2918     if (auto client = qobject_cast<InternalClient *>(toplevel)) {
2919         QWindow *handle = client->internalWindow();
2920         if (m_focus.internalWindow != handle) {
2921             // changed internal window
2922             updateDecoration();
2923             updateInternalWindow(handle);
2924             updateFocus();
2925         } else if (updateDecoration()) {
2926             // went onto or off from decoration, update focus
2927             updateFocus();
2928         }
2929     } else {
2930         updateInternalWindow(nullptr);
2931 
2932         if (m_focus.focus != m_at.at) {
2933             // focus change
2934             updateDecoration();
2935             updateFocus();
2936         } else if (updateDecoration()) {
2937             // went onto or off from decoration, update focus
2938             updateFocus();
2939         }
2940     }
2941 
2942     workspace()->updateFocusMousePosition(position().toPoint());
2943 }
2944 
at() const2945 Toplevel *InputDeviceHandler::at() const
2946 {
2947     return m_at.at.data();
2948 }
2949 
focus() const2950 Toplevel *InputDeviceHandler::focus() const
2951 {
2952     return m_focus.focus.data();
2953 }
2954 
decoration() const2955 Decoration::DecoratedClientImpl *InputDeviceHandler::decoration() const
2956 {
2957     return m_focus.decoration;
2958 }
2959 
internalWindow() const2960 QWindow *InputDeviceHandler::internalWindow() const
2961 {
2962     return m_focus.internalWindow;
2963 }
2964 
2965 } // namespace
2966 
2967 #include "input.moc"
2968