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