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.
icc-effect-5.14.5
Martin Gräßlin 2013-07-10 11:41:16 +02:00
parent 95c6e2d7ba
commit 24b23dfc01
5 changed files with 297 additions and 1 deletions

View File

@ -264,6 +264,7 @@ set(kwin_KDEINIT_SRCS
cursor.cpp
tabgroup.cpp
focuschain.cpp
globalshortcuts.cpp
input.cpp
netinfo.cpp
placement.cpp

158
globalshortcuts.cpp Normal file
View File

@ -0,0 +1,158 @@
/********************************************************************
KWin - the KDE window manager
This file is part of the KDE project.
Copyright (C) 2013 Martin Gräßlin <mgraesslin@kde.org>
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 <http://www.gnu.org/licenses/>.
*********************************************************************/
// own
#include "globalshortcuts.h"
// kwin
#include <config-kwin.h>
// KDE
#include <kkeyserver.h>
#include <KConfigGroup>
// Qt
#include <QAction>
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<InternalGlobalShortcut*>((*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<uint32_t, GlobalShortcut*> 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

118
globalshortcuts.h Normal file
View File

@ -0,0 +1,118 @@
/********************************************************************
KWin - the KDE window manager
This file is part of the KDE project.
Copyright (C) 2013 Martin Gräßlin <mgraesslin@kde.org>
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 <http://www.gnu.org/licenses/>.
*********************************************************************/
#ifndef KWIN_GLOBALSHORTCUTS_H
#define KWIN_GLOBALSHORTCUTS_H
// KDE
#include <KSharedConfig>
// Qt
#include <QKeySequence>
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<Qt::KeyboardModifiers, QHash<uint32_t, GlobalShortcut*> > 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

View File

@ -20,6 +20,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#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

View File

@ -30,8 +30,12 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include <xkbcommon/xkbcommon.h>
#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<Toplevel> m_pointerWindow;
GlobalShortcutsManager *m_shortcuts;
KWIN_SINGLETON(InputRedirection)
friend InputRedirection *input();
};