From 24b23dfc01d3b56166b98e98890868ca96ba32f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Gr=C3=A4=C3=9Flin?= Date: Wed, 10 Jul 2013 11:41:16 +0200 Subject: [PATCH] Beginning of global shortcut handling inside KWin A new GlobalShortcutsManager is introduced which is responsible for holding the registered shortcuts and triggering the matching action. The InputRedirection checks with the GlobalShortcutManager whether a key press event triggers a global shortcut and stops processing the event in that case. At the moment the GlobalShortcutsManager only supports the very basics for KWin internal usage. External applications can not yet make usage of the global shortcut system inside KWin. --- CMakeLists.txt | 1 + globalshortcuts.cpp | 158 ++++++++++++++++++++++++++++++++++++++++++++ globalshortcuts.h | 118 +++++++++++++++++++++++++++++++++ input.cpp | 13 +++- input.h | 8 +++ 5 files changed, 297 insertions(+), 1 deletion(-) create mode 100644 globalshortcuts.cpp create mode 100644 globalshortcuts.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 71ee67b4b0..30cb96d87b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -264,6 +264,7 @@ set(kwin_KDEINIT_SRCS cursor.cpp tabgroup.cpp focuschain.cpp + globalshortcuts.cpp input.cpp netinfo.cpp placement.cpp diff --git a/globalshortcuts.cpp b/globalshortcuts.cpp new file mode 100644 index 0000000000..4b96597af0 --- /dev/null +++ b/globalshortcuts.cpp @@ -0,0 +1,158 @@ +/******************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 2013 Martin Gräßlin + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*********************************************************************/ +// own +#include "globalshortcuts.h" +// kwin +#include +// KDE +#include +#include +// Qt +#include + +namespace KWin +{ + +GlobalShortcut::GlobalShortcut(const QKeySequence &shortcut) + : m_shortcut(shortcut) +{ +} + +GlobalShortcut::~GlobalShortcut() +{ +} + +InternalGlobalShortcut::InternalGlobalShortcut(const QKeySequence &shortcut, QAction *action) + : GlobalShortcut(shortcut) + , m_action(action) +{ +} + +InternalGlobalShortcut::~InternalGlobalShortcut() +{ +} + +void InternalGlobalShortcut::invoke() +{ + // using QueuedConnection so that we finish the even processing first + QMetaObject::invokeMethod(m_action, "trigger", Qt::QueuedConnection); +} + +GlobalShortcutsManager::GlobalShortcutsManager(QObject *parent) + : QObject(parent) + , m_config(KSharedConfig::openConfig(QStringLiteral("kglobalshortcutsrc"), KConfig::SimpleConfig)) +{ +} + +GlobalShortcutsManager::~GlobalShortcutsManager() +{ + for (auto it = m_shortcuts.begin(); it != m_shortcuts.end(); ++it) { + qDeleteAll((*it)); + } +} + +void GlobalShortcutsManager::objectDeleted(QObject *object) +{ + for (auto it = m_shortcuts.begin(); it != m_shortcuts.end(); ++it) { + auto list = (*it); + for (auto it2 = list.begin(); it2 != list.end(); ++it2) { + if (InternalGlobalShortcut *shortcut = dynamic_cast((*it2))) { + if (shortcut->action() == object) { + delete *it2; + it2 = list.erase(it2); + } + } + } + } +} + +void GlobalShortcutsManager::registerShortcut(QAction *action, const QKeySequence &shortcut) +{ + QKeySequence s = getShortcutForAction(KWIN_NAME, action->objectName(), shortcut); + if (s.isEmpty()) { + // TODO: insert into a list of empty shortcuts to react on changes + return; + } + int keys = s[0]; + Qt::KeyboardModifiers mods = Qt::NoModifier; + if (keys & Qt::ShiftModifier) { + mods |= Qt::ShiftModifier; + } + if (keys & Qt::ControlModifier) { + mods |= Qt::ControlModifier; + } + if (keys & Qt::AltModifier) { + mods |= Qt::AltModifier; + } + if (keys & Qt::MetaModifier) { + mods |= Qt::MetaModifier; + } + int keysym = 0; + if (!KKeyServer::keyQtToSymX(keys, &keysym)) { + return; + } + GlobalShortcut *cut = new InternalGlobalShortcut(s, action); + auto it = m_shortcuts.find(mods); + if (it != m_shortcuts.end()) { + // TODO: check if key already exists? + (*it).insert(keysym, cut); + } else { + QHash shortcuts; + shortcuts.insert(keysym, cut); + m_shortcuts.insert(mods, shortcuts); + } + connect(action, &QAction::destroyed, this, &GlobalShortcutsManager::objectDeleted); +} + +QKeySequence GlobalShortcutsManager::getShortcutForAction(const QString &componentName, const QString &actionName, const QKeySequence &defaultShortcut) +{ + if (!m_config->hasGroup(componentName)) { + return defaultShortcut; + } + KConfigGroup group = m_config->group(componentName); + if (!group.hasKey(actionName)) { + return defaultShortcut; + } + QStringList parts = group.readEntry(actionName, QStringList()); + // must consist of three parts + if (parts.size() != 3) { + return defaultShortcut; + } + if (parts.first() == "none") { + return defaultShortcut; + } + return QKeySequence(parts.first()); +} + +bool GlobalShortcutsManager::processKey(Qt::KeyboardModifiers mods, uint32_t key) +{ + auto it = m_shortcuts.find(mods); + if (it == m_shortcuts.end()) { + return false; + } + auto it2 = (*it).find(key); + if (it2 == (*it).end()) { + return false; + } + it2.value()->invoke(); + return true; +} + +} // namespace diff --git a/globalshortcuts.h b/globalshortcuts.h new file mode 100644 index 0000000000..2c11e9e1d5 --- /dev/null +++ b/globalshortcuts.h @@ -0,0 +1,118 @@ +/******************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 2013 Martin Gräßlin + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*********************************************************************/ +#ifndef KWIN_GLOBALSHORTCUTS_H +#define KWIN_GLOBALSHORTCUTS_H +// KDE +#include +// Qt +#include + +class QAction; + +namespace KWin +{ + +class GlobalShortcut; + +/** + * @brief Manager for the global shortcut system inside KWin. + * + * This class is responsible for holding all the global shortcuts and to process a key press event. + * That is trigger a shortcut if there is a match. + * + * For internal shortcut handling (those which are delivered inside KWin) QActions are used and + * triggered if the shortcut matches. For external shortcut handling a DBus interface is used. + **/ +class GlobalShortcutsManager : public QObject +{ + Q_OBJECT +public: + explicit GlobalShortcutsManager(QObject *parent = nullptr); + virtual ~GlobalShortcutsManager(); + /** + * @brief Registers an internal global shortcut + * + * @param action The action to trigger if the shortcut is pressed + * @param shortcut The key sequence which triggers this shortcut + */ + void registerShortcut(QAction *action, const QKeySequence &shortcut); + + /** + * @brief Processes a key event to decide whether a shortcut needs to be triggered. + * + * If a shortcut triggered this method returns @c true to indicate to the caller that the event + * should not be further processed. If there is no shortcut which triggered for the key, then + * @c false is returned. + * + * @param modifiers The current hold modifiers + * @param key The keysymbol which has been pressed + * @return @c true if a shortcut triggered, @c false otherwise + */ + bool processKey(Qt::KeyboardModifiers modifiers, uint32_t key); +private: + void objectDeleted(QObject *object); + QKeySequence getShortcutForAction(const QString &componentName, const QString &actionName, const QKeySequence &defaultShortcut); + QHash > m_shortcuts; + KSharedConfigPtr m_config; +}; + +class GlobalShortcut +{ +public: + virtual ~GlobalShortcut(); + + const QKeySequence &shortcut() const; + virtual void invoke() = 0; + +protected: + GlobalShortcut(const QKeySequence &shortcut); + +private: + QKeySequence m_shortcut; +}; + +class InternalGlobalShortcut : public GlobalShortcut +{ +public: + InternalGlobalShortcut(const QKeySequence &shortcut, QAction *action); + virtual ~InternalGlobalShortcut(); + + void invoke() override; + + QAction *action() const; +private: + QAction *m_action; +}; + +inline +QAction *InternalGlobalShortcut::action() const +{ + return m_action; +} + +inline +const QKeySequence &GlobalShortcut::shortcut() const +{ + return m_shortcut; +} + +} // namespace + +#endif diff --git a/input.cpp b/input.cpp index 0bdb8ddea7..6b31ff3726 100644 --- a/input.cpp +++ b/input.cpp @@ -20,6 +20,7 @@ along with this program. If not, see . #include "input.h" #include "client.h" #include "effects.h" +#include "globalshortcuts.h" #include "unmanaged.h" #include "workspace.h" // KDE @@ -157,6 +158,7 @@ InputRedirection::InputRedirection(QObject *parent) , m_xkb(new Xkb()) #endif , m_pointerWindow() + , m_shortcuts(new GlobalShortcutsManager(this)) { } @@ -255,7 +257,6 @@ void InputRedirection::processKeyboardKey(uint32_t key, InputRedirection::Keyboa Q_UNUSED(time) #if HAVE_XKB m_xkb->updateKey(key, state); - // TODO: process global shortcuts // TODO: pass to internal parts of KWin if (effects && static_cast< EffectsHandlerImpl* >(effects)->hasKeyboardGrab()) { const xkb_keysym_t keysym = m_xkb->toKeysym(key); @@ -272,6 +273,12 @@ void InputRedirection::processKeyboardKey(uint32_t key, InputRedirection::Keyboa c->keyPressEvent(m_xkb->toQtKey(m_xkb->toKeysym(key))); return; } + // process global shortcuts + if (state == KeyboardKeyPressed) { + if (m_shortcuts->processKey(m_xkb->modifiers(), m_xkb->toKeysym(key))) { + return; + } + } #endif // check unmanaged if (!workspace()->unmanagedList().isEmpty()) { @@ -420,5 +427,9 @@ Qt::KeyboardModifiers InputRedirection::keyboardModifiers() const #endif } +void InputRedirection::registerShortcut(const QKeySequence &shortcut, QAction *action) +{ + m_shortcuts->registerShortcut(action, shortcut); +} } // namespace diff --git a/input.h b/input.h index 5fdccccb4c..ef231b1855 100644 --- a/input.h +++ b/input.h @@ -30,8 +30,12 @@ along with this program. If not, see . #include #endif +class QAction; +class QKeySequence; + namespace KWin { +class GlobalShortcutsManager; class Toplevel; class Xkb; @@ -77,6 +81,8 @@ public: Qt::MouseButtons qtButtonStates() const; Qt::KeyboardModifiers keyboardModifiers() const; + void registerShortcut(const QKeySequence &shortcut, QAction *action); + /** * @internal */ @@ -144,6 +150,8 @@ private: */ QWeakPointer m_pointerWindow; + GlobalShortcutsManager *m_shortcuts; + KWIN_SINGLETON(InputRedirection) friend InputRedirection *input(); };