200
src/globalshortcuts/globalshortcutbackend-kde.cpp
Normal file
200
src/globalshortcuts/globalshortcutbackend-kde.cpp
Normal file
@@ -0,0 +1,200 @@
|
||||
/*
|
||||
* Strawberry Music Player
|
||||
* Copyright 2020, Jonas Kvinge <jonas@jkvinge.net>
|
||||
*
|
||||
* Strawberry 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Strawberry 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 Strawberry. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <dbus/kglobalaccel.h>
|
||||
#include <dbus/kglobalaccelcomponent.h>
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QList>
|
||||
#include <QString>
|
||||
#include <QStringList>
|
||||
#include <QAction>
|
||||
#include <QDBusConnection>
|
||||
#include <QDBusReply>
|
||||
#include <QDBusObjectPath>
|
||||
#include <QDBusPendingCallWatcher>
|
||||
#include <QKeySequence>
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
||||
# include <QKeyCombination>
|
||||
#endif
|
||||
|
||||
#include "core/logging.h"
|
||||
#include "core/closure.h"
|
||||
|
||||
#include "globalshortcutbackend-kde.h"
|
||||
|
||||
const char *GlobalShortcutBackendKDE::kKdeService = "org.kde.kglobalaccel";
|
||||
const char *GlobalShortcutBackendKDE::kKdePath = "/kglobalaccel";
|
||||
|
||||
GlobalShortcutBackendKDE::GlobalShortcutBackendKDE(GlobalShortcuts *parent) : GlobalShortcutBackend(parent), interface_(nullptr), component_(nullptr) {}
|
||||
|
||||
bool GlobalShortcutBackendKDE::DoRegister() {
|
||||
|
||||
qLog(Debug) << "Registering";
|
||||
|
||||
if (!QDBusConnection::sessionBus().interface()->isServiceRegistered(kKdeService)) {
|
||||
qLog(Warning) << "KGlobalAccel is not registered";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!interface_) {
|
||||
interface_ = new OrgKdeKGlobalAccelInterface(kKdeService, kKdePath, QDBusConnection::sessionBus(), this);
|
||||
}
|
||||
|
||||
for (const GlobalShortcuts::Shortcut &shortcut : manager_->shortcuts().values()) {
|
||||
RegisterShortcut(shortcut);
|
||||
}
|
||||
|
||||
QDBusPendingReply<QDBusObjectPath> reply = interface_->getComponent(QCoreApplication::applicationName());
|
||||
QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(reply, this);
|
||||
NewClosure(watcher, SIGNAL(finished(QDBusPendingCallWatcher*)), this, SLOT(RegisterFinished(QDBusPendingCallWatcher*)), watcher);
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
void GlobalShortcutBackendKDE::RegisterFinished(QDBusPendingCallWatcher *watcher) {
|
||||
|
||||
QDBusReply<QDBusObjectPath> reply = watcher->reply();
|
||||
watcher->deleteLater();
|
||||
|
||||
if (!reply.isValid()) {
|
||||
qLog(Error) << "Failed to register:" << reply.error().name() << reply.error().message();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!component_) {
|
||||
component_ = new org::kde::kglobalaccel::Component(kKdeService, reply.value().path(), QDBusConnection::sessionBus(), interface_);
|
||||
}
|
||||
|
||||
if (!component_->isValid()) {
|
||||
qLog(Error) << "Component is invalid:" << QDBusConnection::sessionBus().lastError();
|
||||
return;
|
||||
}
|
||||
|
||||
connect(component_, SIGNAL(globalShortcutPressed(QString, QString, qlonglong)), this, SLOT(GlobalShortcutPressed(QString, QString, qlonglong)), Qt::UniqueConnection);
|
||||
|
||||
qLog(Debug) << "Registered";
|
||||
|
||||
}
|
||||
|
||||
void GlobalShortcutBackendKDE::DoUnregister() {
|
||||
|
||||
if (!interface_ || !interface_->isValid()) return;
|
||||
|
||||
qLog(Debug) << "Unregistering";
|
||||
|
||||
for (const GlobalShortcuts::Shortcut &shortcut : manager_->shortcuts()) {
|
||||
if (actions_.contains(shortcut.id)) {
|
||||
interface_->unRegister(GetActionId(shortcut.id, shortcut.action));
|
||||
actions_.remove(shortcut.id, shortcut.action);
|
||||
qLog(Info) << "Unregistered shortcut" << shortcut.id << shortcut.action->shortcut();
|
||||
}
|
||||
}
|
||||
|
||||
disconnect(component_, nullptr, this, nullptr);
|
||||
|
||||
qLog(Debug) << "Unregistered";
|
||||
|
||||
}
|
||||
|
||||
bool GlobalShortcutBackendKDE::RegisterShortcut(const GlobalShortcuts::Shortcut &shortcut) {
|
||||
|
||||
if (!interface_ || !interface_->isValid() || shortcut.id.isEmpty() || !shortcut.action || shortcut.action->shortcut().isEmpty()) return false;
|
||||
|
||||
if (shortcut.action->shortcut() == QKeySequence(Qt::Key_MediaPlay) ||
|
||||
shortcut.action->shortcut() == QKeySequence(Qt::Key_MediaStop) ||
|
||||
shortcut.action->shortcut() == QKeySequence(Qt::Key_MediaNext) ||
|
||||
shortcut.action->shortcut() == QKeySequence(Qt::Key_MediaPrevious)) {
|
||||
qLog(Info) << "Media shortcut" << shortcut.id << shortcut.action->shortcut();
|
||||
return true;
|
||||
}
|
||||
|
||||
QStringList action_id = GetActionId(shortcut.id, shortcut.action);
|
||||
actions_.insert(shortcut.id, shortcut.action);
|
||||
interface_->doRegister(action_id);
|
||||
|
||||
QList<QKeySequence> active_shortcut = QList<QKeySequence>() << shortcut.action->shortcut();
|
||||
|
||||
const QList<int> result = interface_->setShortcut(action_id, ToIntList(active_shortcut), 0x2);
|
||||
const QList<QKeySequence> result_sequence = ToKeySequenceList(result);
|
||||
if (result_sequence != active_shortcut) {
|
||||
if (result_sequence.isEmpty()) {
|
||||
shortcut.action->setShortcut(QKeySequence());
|
||||
}
|
||||
else {
|
||||
shortcut.action->setShortcut(result_sequence[0]);
|
||||
}
|
||||
}
|
||||
|
||||
qLog(Info) << "Registered shortcut" << shortcut.id << shortcut.action->shortcut();
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
QStringList GlobalShortcutBackendKDE::GetActionId(const QString &id, const QAction *action) {
|
||||
|
||||
QStringList ret;
|
||||
ret << QCoreApplication::applicationName();
|
||||
ret << id;
|
||||
ret << QCoreApplication::applicationName();
|
||||
ret << action->text().remove('&');
|
||||
if (ret.back().isEmpty()) ret.back() = id;
|
||||
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
||||
QList<int> GlobalShortcutBackendKDE::ToIntList(const QList<QKeySequence> &sequence_list) {
|
||||
|
||||
QList<int> ret;
|
||||
for (const QKeySequence &sequence : sequence_list) {
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
||||
ret.append(sequence[0].toCombined());
|
||||
#else
|
||||
ret.append(sequence[0]);
|
||||
#endif
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
||||
QList<QKeySequence> GlobalShortcutBackendKDE::ToKeySequenceList(const QList<int> &sequence_list) {
|
||||
|
||||
QList<QKeySequence> ret;
|
||||
for (int sequence : sequence_list) {
|
||||
ret.append(sequence);
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
||||
void GlobalShortcutBackendKDE::GlobalShortcutPressed(const QString &component_unique, const QString &shortcut_unique, qlonglong) {
|
||||
|
||||
if (QCoreApplication::applicationName() == component_unique && actions_.contains(shortcut_unique)) {
|
||||
for (QAction *action : actions_.values(shortcut_unique)) {
|
||||
qLog(Debug) << "Key" << action->shortcut() << "pressed.";
|
||||
if (action->isEnabled()) action->trigger();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
70
src/globalshortcuts/globalshortcutbackend-kde.h
Normal file
70
src/globalshortcuts/globalshortcutbackend-kde.h
Normal file
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Strawberry Music Player
|
||||
* Copyright 2020, Jonas Kvinge <jonas@jkvinge.net>
|
||||
*
|
||||
* Strawberry 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Strawberry 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 Strawberry. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef GLOBALSHORTCUTBACKEND_KDE_H
|
||||
#define GLOBALSHORTCUTBACKEND_KDE_H
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <QList>
|
||||
#include <QMultiHash>
|
||||
#include <QString>
|
||||
#include <QStringList>
|
||||
#include <QKeySequence>
|
||||
|
||||
#include "globalshortcutbackend.h"
|
||||
#include "globalshortcuts.h"
|
||||
|
||||
class QDBusPendingCallWatcher;
|
||||
class QAction;
|
||||
|
||||
class OrgKdeKGlobalAccelInterface;
|
||||
class OrgKdeKglobalaccelComponentInterface;
|
||||
|
||||
class GlobalShortcutBackendKDE : public GlobalShortcutBackend {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit GlobalShortcutBackendKDE(GlobalShortcuts *parent);
|
||||
|
||||
static const char *kKdeService;
|
||||
|
||||
protected:
|
||||
bool DoRegister() override;
|
||||
void DoUnregister() override;
|
||||
|
||||
private:
|
||||
bool RegisterShortcut(const GlobalShortcuts::Shortcut &shortcut);
|
||||
static QStringList GetActionId(const QString &id, const QAction *action);
|
||||
static QList<int> ToIntList(const QList<QKeySequence> &sequence_list);
|
||||
static QList<QKeySequence> ToKeySequenceList(const QList<int> &sequence_list);
|
||||
|
||||
private slots:
|
||||
void RegisterFinished(QDBusPendingCallWatcher *watcher);
|
||||
void GlobalShortcutPressed(const QString &component_unique, const QString &shortcut_unique, qlonglong);
|
||||
|
||||
private:
|
||||
static const char *kKdePath;
|
||||
|
||||
OrgKdeKGlobalAccelInterface *interface_;
|
||||
OrgKdeKglobalaccelComponentInterface *component_;
|
||||
QMultiHash<QString, QAction*> actions_;
|
||||
};
|
||||
|
||||
#endif // GLOBALSHORTCUTBACKEND_KDE_H
|
||||
@@ -39,6 +39,7 @@
|
||||
|
||||
#ifdef HAVE_DBUS
|
||||
# include "globalshortcutbackend-gsd.h"
|
||||
# include "globalshortcutbackend-kde.h"
|
||||
#endif
|
||||
#if defined(HAVE_X11) || defined(Q_OS_WIN)
|
||||
# include "globalshortcutbackend-system.h"
|
||||
@@ -51,9 +52,11 @@
|
||||
|
||||
GlobalShortcuts::GlobalShortcuts(QWidget *parent)
|
||||
: QWidget(parent),
|
||||
dbus_backend_(nullptr),
|
||||
gsd_backend_(nullptr),
|
||||
kde_backend_(nullptr),
|
||||
system_backend_(nullptr),
|
||||
use_gsd_(true),
|
||||
use_kde_(true),
|
||||
use_x11_(false)
|
||||
{
|
||||
|
||||
@@ -82,7 +85,8 @@ GlobalShortcuts::GlobalShortcuts(QWidget *parent)
|
||||
|
||||
// Create backends - these do the actual shortcut registration
|
||||
#ifdef HAVE_DBUS
|
||||
dbus_backend_ = new GlobalShortcutBackendGSD(this);
|
||||
gsd_backend_ = new GlobalShortcutBackendGSD(this);
|
||||
kde_backend_ = new GlobalShortcutBackendKDE(this);
|
||||
#endif
|
||||
|
||||
#ifdef Q_OS_MACOS
|
||||
@@ -106,6 +110,7 @@ void GlobalShortcuts::ReloadSettings() {
|
||||
|
||||
// The actual shortcuts have been set in our actions for us by the config dialog already - we just need to reread the gnome settings.
|
||||
use_gsd_ = settings_.value("use_gsd", true).toBool();
|
||||
use_kde_ = settings_.value("use_kde", true).toBool();
|
||||
use_x11_ = settings_.value("use_x11", false).toBool();
|
||||
|
||||
Unregister();
|
||||
@@ -149,6 +154,16 @@ bool GlobalShortcuts::IsGsdAvailable() const {
|
||||
|
||||
}
|
||||
|
||||
bool GlobalShortcuts::IsKdeAvailable() const {
|
||||
|
||||
#ifdef HAVE_DBUS
|
||||
return QDBusConnection::sessionBus().interface()->isServiceRegistered(GlobalShortcutBackendKDE::kKdeService);
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
bool GlobalShortcuts::IsX11Available() const {
|
||||
|
||||
#ifdef HAVE_X11
|
||||
@@ -160,7 +175,9 @@ bool GlobalShortcuts::IsX11Available() const {
|
||||
}
|
||||
|
||||
void GlobalShortcuts::Register() {
|
||||
if (use_gsd_ && dbus_backend_ && dbus_backend_->Register()) return;
|
||||
|
||||
if (use_gsd_ && gsd_backend_ && gsd_backend_->Register()) return;
|
||||
if (use_kde_ && kde_backend_ && kde_backend_->Register()) return;
|
||||
#ifdef HAVE_X11 // If this system has X11, only use the system backend if X11 is enabled in the global shortcut settings
|
||||
if (use_x11_) {
|
||||
#endif
|
||||
@@ -169,11 +186,15 @@ void GlobalShortcuts::Register() {
|
||||
#ifdef HAVE_X11
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
void GlobalShortcuts::Unregister() {
|
||||
if (dbus_backend_ && dbus_backend_->is_active()) dbus_backend_->Unregister();
|
||||
|
||||
if (gsd_backend_ && gsd_backend_->is_active()) gsd_backend_->Unregister();
|
||||
if (kde_backend_ && kde_backend_->is_active()) kde_backend_->Unregister();
|
||||
if (system_backend_ && system_backend_->is_active()) system_backend_->Unregister();
|
||||
|
||||
}
|
||||
|
||||
bool GlobalShortcuts::IsMacAccessibilityEnabled() const {
|
||||
|
||||
@@ -50,6 +50,7 @@ class GlobalShortcuts : public QWidget {
|
||||
|
||||
QMap<QString, Shortcut> shortcuts() const { return shortcuts_; }
|
||||
bool IsGsdAvailable() const;
|
||||
bool IsKdeAvailable() const;
|
||||
bool IsX11Available() const;
|
||||
bool IsMacAccessibilityEnabled() const;
|
||||
|
||||
@@ -87,13 +88,15 @@ class GlobalShortcuts : public QWidget {
|
||||
Shortcut AddShortcut(const QString &id, const QString &name, const QKeySequence &default_key);
|
||||
|
||||
private:
|
||||
GlobalShortcutBackend *dbus_backend_;
|
||||
GlobalShortcutBackend *gsd_backend_;
|
||||
GlobalShortcutBackend *kde_backend_;
|
||||
GlobalShortcutBackend *system_backend_;
|
||||
|
||||
QMap<QString, Shortcut> shortcuts_;
|
||||
QSettings settings_;
|
||||
|
||||
bool use_gsd_;
|
||||
bool use_kde_;
|
||||
bool use_x11_;
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user