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