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