File indexing completed on 2024-07-21 07:55:09
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 0007 SPDX-License-Identifier: GPL-2.0-or-later 0008 */ 0009 // own 0010 #include "globalshortcuts.h" 0011 // config 0012 #include <config-kwin.h> 0013 // kwin 0014 #include "effect/globals.h" 0015 #include "gestures.h" 0016 #include "main.h" 0017 #include "utils/common.h" 0018 // KDE 0019 #include <kglobalaccel_interface.h> 0020 #include <kglobalacceld.h> 0021 // Qt 0022 #include <QAction> 0023 // system 0024 #include <signal.h> 0025 #include <variant> 0026 0027 namespace KWin 0028 { 0029 GlobalShortcut::GlobalShortcut(Shortcut &&sc, QAction *action) 0030 : m_shortcut(sc) 0031 , m_action(action) 0032 { 0033 if (auto swipeGesture = std::get_if<RealtimeFeedbackSwipeShortcut>(&sc)) { 0034 m_swipeGesture = std::make_unique<SwipeGesture>(); 0035 m_swipeGesture->setDirection(swipeGesture->direction); 0036 m_swipeGesture->setMinimumDelta(QPointF(200, 200)); 0037 m_swipeGesture->setMaximumFingerCount(swipeGesture->fingerCount); 0038 m_swipeGesture->setMinimumFingerCount(swipeGesture->fingerCount); 0039 QObject::connect(m_swipeGesture.get(), &SwipeGesture::triggered, m_action, &QAction::trigger, Qt::QueuedConnection); 0040 QObject::connect(m_swipeGesture.get(), &SwipeGesture::cancelled, m_action, &QAction::trigger, Qt::QueuedConnection); 0041 if (swipeGesture->progressCallback) { 0042 QObject::connect(m_swipeGesture.get(), &SwipeGesture::progress, swipeGesture->progressCallback); 0043 } 0044 } else if (auto pinchGesture = std::get_if<RealtimeFeedbackPinchShortcut>(&sc)) { 0045 m_pinchGesture = std::make_unique<PinchGesture>(); 0046 m_pinchGesture->setDirection(pinchGesture->direction); 0047 m_pinchGesture->setMaximumFingerCount(pinchGesture->fingerCount); 0048 m_pinchGesture->setMinimumFingerCount(pinchGesture->fingerCount); 0049 QObject::connect(m_pinchGesture.get(), &PinchGesture::triggered, m_action, &QAction::trigger, Qt::QueuedConnection); 0050 QObject::connect(m_pinchGesture.get(), &PinchGesture::cancelled, m_action, &QAction::trigger, Qt::QueuedConnection); 0051 if (pinchGesture->scaleCallback) { 0052 QObject::connect(m_pinchGesture.get(), &PinchGesture::progress, pinchGesture->scaleCallback); 0053 } 0054 } 0055 } 0056 0057 GlobalShortcut::~GlobalShortcut() 0058 { 0059 } 0060 0061 QAction *GlobalShortcut::action() const 0062 { 0063 return m_action; 0064 } 0065 0066 void GlobalShortcut::invoke() const 0067 { 0068 QMetaObject::invokeMethod(m_action, &QAction::trigger, Qt::QueuedConnection); 0069 } 0070 0071 const Shortcut &GlobalShortcut::shortcut() const 0072 { 0073 return m_shortcut; 0074 } 0075 0076 SwipeGesture *GlobalShortcut::swipeGesture() const 0077 { 0078 return m_swipeGesture.get(); 0079 } 0080 0081 PinchGesture *GlobalShortcut::pinchGesture() const 0082 { 0083 return m_pinchGesture.get(); 0084 } 0085 0086 GlobalShortcutsManager::GlobalShortcutsManager(QObject *parent) 0087 : QObject(parent) 0088 , m_touchpadGestureRecognizer(new GestureRecognizer(this)) 0089 , m_touchscreenGestureRecognizer(new GestureRecognizer(this)) 0090 { 0091 } 0092 0093 GlobalShortcutsManager::~GlobalShortcutsManager() 0094 { 0095 } 0096 0097 void GlobalShortcutsManager::init() 0098 { 0099 if (kwinApp()->shouldUseWaylandForCompositing()) { 0100 qputenv("KGLOBALACCELD_PLATFORM", QByteArrayLiteral("org.kde.kwin")); 0101 m_kglobalAccel = std::make_unique<KGlobalAccelD>(); 0102 if (!m_kglobalAccel->init()) { 0103 qCDebug(KWIN_CORE) << "Init of kglobalaccel failed"; 0104 m_kglobalAccel.reset(); 0105 } else { 0106 qCDebug(KWIN_CORE) << "KGlobalAcceld inited"; 0107 } 0108 } 0109 } 0110 0111 void GlobalShortcutsManager::objectDeleted(QObject *object) 0112 { 0113 auto it = m_shortcuts.begin(); 0114 while (it != m_shortcuts.end()) { 0115 if (it->action() == object) { 0116 it = m_shortcuts.erase(it); 0117 } else { 0118 ++it; 0119 } 0120 } 0121 } 0122 0123 bool GlobalShortcutsManager::add(GlobalShortcut sc, DeviceType device) 0124 { 0125 const auto &recognizer = device == DeviceType::Touchpad ? m_touchpadGestureRecognizer : m_touchscreenGestureRecognizer; 0126 if (std::holds_alternative<RealtimeFeedbackSwipeShortcut>(sc.shortcut())) { 0127 recognizer->registerSwipeGesture(sc.swipeGesture()); 0128 } else if (std::holds_alternative<RealtimeFeedbackPinchShortcut>(sc.shortcut())) { 0129 recognizer->registerPinchGesture(sc.pinchGesture()); 0130 } 0131 connect(sc.action(), &QAction::destroyed, this, &GlobalShortcutsManager::objectDeleted); 0132 m_shortcuts.push_back(std::move(sc)); 0133 return true; 0134 } 0135 0136 void GlobalShortcutsManager::registerPointerShortcut(QAction *action, Qt::KeyboardModifiers modifiers, Qt::MouseButtons pointerButtons) 0137 { 0138 add(GlobalShortcut(PointerButtonShortcut{modifiers, pointerButtons}, action)); 0139 } 0140 0141 void GlobalShortcutsManager::registerAxisShortcut(QAction *action, Qt::KeyboardModifiers modifiers, PointerAxisDirection axis) 0142 { 0143 add(GlobalShortcut(PointerAxisShortcut{modifiers, axis}, action)); 0144 } 0145 0146 void GlobalShortcutsManager::registerTouchpadSwipe(SwipeDirection direction, uint32_t fingerCount, QAction *action, std::function<void(qreal)> progressCallback) 0147 { 0148 add(GlobalShortcut(RealtimeFeedbackSwipeShortcut{DeviceType::Touchpad, direction, progressCallback, fingerCount}, action), DeviceType::Touchpad); 0149 } 0150 0151 void GlobalShortcutsManager::registerTouchpadPinch(PinchDirection direction, uint32_t fingerCount, QAction *action, std::function<void(qreal)> progressCallback) 0152 { 0153 add(GlobalShortcut(RealtimeFeedbackPinchShortcut{direction, progressCallback, fingerCount}, action), DeviceType::Touchpad); 0154 } 0155 0156 void GlobalShortcutsManager::registerTouchscreenSwipe(SwipeDirection direction, uint32_t fingerCount, QAction *action, std::function<void(qreal)> progressCallback) 0157 { 0158 add(GlobalShortcut(RealtimeFeedbackSwipeShortcut{DeviceType::Touchscreen, direction, progressCallback, fingerCount}, action), DeviceType::Touchscreen); 0159 } 0160 0161 void GlobalShortcutsManager::forceRegisterTouchscreenSwipe(SwipeDirection direction, uint32_t fingerCount, QAction *action, std::function<void(qreal)> progressCallback) 0162 { 0163 GlobalShortcut shortcut{RealtimeFeedbackSwipeShortcut{DeviceType::Touchscreen, direction, progressCallback, fingerCount}, action}; 0164 const auto it = std::find_if(m_shortcuts.begin(), m_shortcuts.end(), [&shortcut](const auto &s) { 0165 return shortcut.shortcut() == s.shortcut(); 0166 }); 0167 if (it != m_shortcuts.end()) { 0168 m_shortcuts.erase(it); 0169 } 0170 m_touchscreenGestureRecognizer->registerSwipeGesture(shortcut.swipeGesture()); 0171 connect(shortcut.action(), &QAction::destroyed, this, &GlobalShortcutsManager::objectDeleted); 0172 m_shortcuts.push_back(std::move(shortcut)); 0173 } 0174 0175 bool GlobalShortcutsManager::processKey(Qt::KeyboardModifiers mods, int keyQt) 0176 { 0177 if (m_kglobalAccelInterface) { 0178 if (!keyQt && !mods) { 0179 return false; 0180 } 0181 auto check = [this](Qt::KeyboardModifiers mods, int keyQt) { 0182 bool retVal = false; 0183 QMetaObject::invokeMethod(m_kglobalAccelInterface, 0184 "checkKeyPressed", 0185 Qt::DirectConnection, 0186 Q_RETURN_ARG(bool, retVal), 0187 Q_ARG(int, int(mods) | keyQt)); 0188 return retVal; 0189 }; 0190 if (check(mods, keyQt)) { 0191 return true; 0192 } else if (keyQt == Qt::Key_Backtab) { 0193 // KGlobalAccel on X11 has some workaround for Backtab 0194 // see kglobalaccel/src/runtime/plugins/xcb/kglobalccel_x11.cpp method x11KeyPress 0195 // Apparently KKeySequenceWidget captures Shift+Tab instead of Backtab 0196 // thus if the key is backtab we should adjust to add shift again and use tab 0197 // in addition KWin registers the shortcut incorrectly as Alt+Shift+Backtab 0198 // this should be changed to either Alt+Backtab or Alt+Shift+Tab to match KKeySequenceWidget 0199 // trying the variants 0200 if (check(mods | Qt::ShiftModifier, keyQt)) { 0201 return true; 0202 } 0203 if (check(mods | Qt::ShiftModifier, Qt::Key_Tab)) { 0204 return true; 0205 } 0206 } 0207 } 0208 return false; 0209 } 0210 0211 bool GlobalShortcutsManager::processKeyRelease(Qt::KeyboardModifiers mods, int keyQt) 0212 { 0213 if (m_kglobalAccelInterface) { 0214 QMetaObject::invokeMethod(m_kglobalAccelInterface, 0215 "checkKeyReleased", 0216 Qt::DirectConnection, 0217 Q_ARG(int, int(mods) | keyQt)); 0218 } 0219 return false; 0220 } 0221 0222 template<typename ShortcutKind, typename... Args> 0223 bool match(QList<GlobalShortcut> &shortcuts, Args... args) 0224 { 0225 for (auto &sc : shortcuts) { 0226 if (std::holds_alternative<ShortcutKind>(sc.shortcut())) { 0227 if (std::get<ShortcutKind>(sc.shortcut()) == ShortcutKind{args...}) { 0228 sc.invoke(); 0229 return true; 0230 } 0231 } 0232 } 0233 return false; 0234 } 0235 0236 // TODO(C++20): use ranges for a nicer way of filtering by shortcut type 0237 bool GlobalShortcutsManager::processPointerPressed(Qt::KeyboardModifiers mods, Qt::MouseButtons pointerButtons) 0238 { 0239 return match<PointerButtonShortcut>(m_shortcuts, mods, pointerButtons); 0240 } 0241 0242 bool GlobalShortcutsManager::processAxis(Qt::KeyboardModifiers mods, PointerAxisDirection axis) 0243 { 0244 return match<PointerAxisShortcut>(m_shortcuts, mods, axis); 0245 } 0246 0247 void GlobalShortcutsManager::processSwipeStart(DeviceType device, uint fingerCount) 0248 { 0249 if (device == DeviceType::Touchpad) { 0250 m_touchpadGestureRecognizer->startSwipeGesture(fingerCount); 0251 } else { 0252 m_touchscreenGestureRecognizer->startSwipeGesture(fingerCount); 0253 } 0254 } 0255 0256 void GlobalShortcutsManager::processSwipeUpdate(DeviceType device, const QPointF &delta) 0257 { 0258 if (device == DeviceType::Touchpad) { 0259 m_touchpadGestureRecognizer->updateSwipeGesture(delta); 0260 } else { 0261 m_touchscreenGestureRecognizer->updateSwipeGesture(delta); 0262 } 0263 } 0264 0265 void GlobalShortcutsManager::processSwipeCancel(DeviceType device) 0266 { 0267 if (device == DeviceType::Touchpad) { 0268 m_touchpadGestureRecognizer->cancelSwipeGesture(); 0269 } else { 0270 m_touchscreenGestureRecognizer->cancelSwipeGesture(); 0271 } 0272 } 0273 0274 void GlobalShortcutsManager::processSwipeEnd(DeviceType device) 0275 { 0276 if (device == DeviceType::Touchpad) { 0277 m_touchpadGestureRecognizer->endSwipeGesture(); 0278 } else { 0279 m_touchscreenGestureRecognizer->endSwipeGesture(); 0280 } 0281 // TODO: cancel on Wayland Seat if one triggered 0282 } 0283 0284 void GlobalShortcutsManager::processPinchStart(uint fingerCount) 0285 { 0286 m_touchpadGestureRecognizer->startPinchGesture(fingerCount); 0287 } 0288 0289 void GlobalShortcutsManager::processPinchUpdate(qreal scale, qreal angleDelta, const QPointF &delta) 0290 { 0291 m_touchpadGestureRecognizer->updatePinchGesture(scale, angleDelta, delta); 0292 } 0293 0294 void GlobalShortcutsManager::processPinchCancel() 0295 { 0296 m_touchpadGestureRecognizer->cancelPinchGesture(); 0297 } 0298 0299 void GlobalShortcutsManager::processPinchEnd() 0300 { 0301 m_touchpadGestureRecognizer->endPinchGesture(); 0302 } 0303 0304 } // namespace 0305 0306 #include "moc_globalshortcuts.cpp"