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(); };