2020-10-31 02:08:19 +01:00
|
|
|
/*
|
|
|
|
* Strawberry Music Player
|
2021-03-20 21:14:47 +01:00
|
|
|
* Copyright 2020-2021, Jonas Kvinge <jonas@jkvinge.net>
|
2020-10-31 02:08:19 +01:00
|
|
|
*
|
|
|
|
* 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"
|
|
|
|
|
2021-01-26 19:13:29 +01:00
|
|
|
#include "globalshortcutsbackend-kde.h"
|
2020-10-31 02:08:19 +01:00
|
|
|
|
2021-01-26 19:13:29 +01:00
|
|
|
const char *GlobalShortcutsBackendKDE::kKdeService = "org.kde.kglobalaccel";
|
|
|
|
const char *GlobalShortcutsBackendKDE::kKdePath = "/kglobalaccel";
|
2020-10-31 02:08:19 +01:00
|
|
|
|
2021-07-11 07:40:57 +02:00
|
|
|
GlobalShortcutsBackendKDE::GlobalShortcutsBackendKDE(GlobalShortcutsManager *manager, QObject *parent)
|
2021-09-01 21:37:11 +02:00
|
|
|
: GlobalShortcutsBackend(manager, GlobalShortcutsBackend::Type_KDE, parent),
|
2021-07-11 07:40:57 +02:00
|
|
|
interface_(nullptr),
|
|
|
|
component_(nullptr) {}
|
2020-10-31 02:08:19 +01:00
|
|
|
|
2021-09-01 21:37:11 +02:00
|
|
|
bool GlobalShortcutsBackendKDE::IsKDEAvailable() {
|
2021-05-29 20:35:55 +02:00
|
|
|
|
|
|
|
return QDBusConnection::sessionBus().interface()->isServiceRegistered(kKdeService);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2021-09-01 21:37:11 +02:00
|
|
|
bool GlobalShortcutsBackendKDE::IsAvailable() const {
|
|
|
|
|
|
|
|
return IsKDEAvailable();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2021-12-18 20:18:37 +01:00
|
|
|
bool GlobalShortcutsBackendKDE::IsMediaShortcut(const GlobalShortcutsManager::Shortcut &shortcut) const {
|
|
|
|
|
|
|
|
return (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));
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-01-26 19:13:29 +01:00
|
|
|
bool GlobalShortcutsBackendKDE::DoRegister() {
|
2020-10-31 02:08:19 +01:00
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2021-03-21 04:47:11 +01:00
|
|
|
QList<GlobalShortcutsManager::Shortcut> shortcuts = manager_->shortcuts().values();
|
|
|
|
for (const GlobalShortcutsManager::Shortcut &shortcut : shortcuts) {
|
2020-10-31 02:08:19 +01:00
|
|
|
RegisterShortcut(shortcut);
|
|
|
|
}
|
|
|
|
|
|
|
|
QDBusPendingReply<QDBusObjectPath> reply = interface_->getComponent(QCoreApplication::applicationName());
|
|
|
|
QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(reply, this);
|
2021-01-26 19:13:29 +01:00
|
|
|
QObject::connect(watcher, &QDBusPendingCallWatcher::finished, this, &GlobalShortcutsBackendKDE::RegisterFinished);
|
2020-10-31 02:08:19 +01:00
|
|
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2021-01-26 19:13:29 +01:00
|
|
|
void GlobalShortcutsBackendKDE::RegisterFinished(QDBusPendingCallWatcher *watcher) {
|
2020-10-31 02:08:19 +01:00
|
|
|
|
|
|
|
QDBusReply<QDBusObjectPath> reply = watcher->reply();
|
|
|
|
watcher->deleteLater();
|
|
|
|
|
|
|
|
if (!reply.isValid()) {
|
2020-11-15 00:20:08 +01:00
|
|
|
if (reply.error().name() != "org.kde.kglobalaccel.NoSuchComponent") {
|
|
|
|
qLog(Error) << "Failed to register:" << reply.error().name() << reply.error().message();
|
|
|
|
}
|
2020-10-31 02:08:19 +01:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2021-01-26 19:13:29 +01:00
|
|
|
QObject::connect(component_, &org::kde::kglobalaccel::Component::globalShortcutPressed, this, &GlobalShortcutsBackendKDE::GlobalShortcutPressed, Qt::UniqueConnection);
|
2020-10-31 02:08:19 +01:00
|
|
|
|
2021-05-29 20:37:58 +02:00
|
|
|
qLog(Debug) << "Registered.";
|
2020-10-31 02:08:19 +01:00
|
|
|
|
|
|
|
}
|
|
|
|
|
2021-01-26 19:13:29 +01:00
|
|
|
void GlobalShortcutsBackendKDE::DoUnregister() {
|
2020-10-31 02:08:19 +01:00
|
|
|
|
|
|
|
if (!interface_ || !interface_->isValid()) return;
|
|
|
|
|
|
|
|
qLog(Debug) << "Unregistering";
|
|
|
|
|
2021-03-21 04:47:11 +01:00
|
|
|
QMap<QString, GlobalShortcutsManager::Shortcut> shortcuts = manager_->shortcuts();
|
|
|
|
for (const GlobalShortcutsManager::Shortcut &shortcut : shortcuts) {
|
2020-10-31 02:08:19 +01:00
|
|
|
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();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-01-26 16:48:04 +01:00
|
|
|
if (component_) QObject::disconnect(component_, nullptr, this, nullptr);
|
2020-10-31 02:08:19 +01:00
|
|
|
|
|
|
|
qLog(Debug) << "Unregistered";
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2021-01-26 19:13:29 +01:00
|
|
|
bool GlobalShortcutsBackendKDE::RegisterShortcut(const GlobalShortcutsManager::Shortcut &shortcut) {
|
2020-10-31 02:08:19 +01:00
|
|
|
|
|
|
|
if (!interface_ || !interface_->isValid() || shortcut.id.isEmpty() || !shortcut.action || shortcut.action->shortcut().isEmpty()) return false;
|
|
|
|
|
|
|
|
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);
|
2021-12-18 20:18:37 +01:00
|
|
|
if (result_sequence == active_shortcut) {
|
|
|
|
qLog(Info) << "Registered shortcut" << shortcut.id << shortcut.action->shortcut();
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
qLog(Error) << "KGlobalAccel returned" << result_sequence << "when setting shortcut" << active_shortcut;
|
|
|
|
if (!result_sequence.isEmpty() && !IsMediaShortcut(shortcut)) {
|
2020-10-31 02:08:19 +01:00
|
|
|
shortcut.action->setShortcut(result_sequence[0]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2021-01-26 19:13:29 +01:00
|
|
|
QStringList GlobalShortcutsBackendKDE::GetActionId(const QString &id, const QAction *action) {
|
2020-10-31 02:08:19 +01:00
|
|
|
|
|
|
|
QStringList ret;
|
|
|
|
ret << QCoreApplication::applicationName();
|
|
|
|
ret << id;
|
|
|
|
ret << QCoreApplication::applicationName();
|
|
|
|
ret << action->text().remove('&');
|
|
|
|
if (ret.back().isEmpty()) ret.back() = id;
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2021-01-26 19:13:29 +01:00
|
|
|
QList<int> GlobalShortcutsBackendKDE::ToIntList(const QList<QKeySequence> &sequence_list) {
|
2020-10-31 02:08:19 +01:00
|
|
|
|
|
|
|
QList<int> ret;
|
2021-06-20 19:04:08 +02:00
|
|
|
ret.reserve(sequence_list.count());
|
2020-10-31 02:08:19 +01:00
|
|
|
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;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2021-01-26 19:13:29 +01:00
|
|
|
QList<QKeySequence> GlobalShortcutsBackendKDE::ToKeySequenceList(const QList<int> &sequence_list) {
|
2020-10-31 02:08:19 +01:00
|
|
|
|
|
|
|
QList<QKeySequence> ret;
|
2021-06-20 19:04:08 +02:00
|
|
|
ret.reserve(sequence_list.count());
|
2020-10-31 02:08:19 +01:00
|
|
|
for (int sequence : sequence_list) {
|
|
|
|
ret.append(sequence);
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2021-03-21 18:53:02 +01:00
|
|
|
void GlobalShortcutsBackendKDE::GlobalShortcutPressed(const QString &component_unique, const QString &shortcut_unique, qint64) {
|
2020-10-31 02:08:19 +01:00
|
|
|
|
|
|
|
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();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|