Clementine-audio-player-Mac.../src/core/kglobalaccelglobalshortcutb...

270 lines
7.9 KiB
C++
Raw Normal View History

2019-12-07 20:30:10 +01:00
#include "kglobalaccelglobalshortcutbackend.h"
2019-12-06 05:25:28 +01:00
#include <QAction>
#include <QGuiApplication>
2020-09-18 16:15:19 +02:00
#include "core/logging.h"
2019-12-06 05:25:28 +01:00
#ifdef HAVE_DBUS
#include <dbus/kglobalaccel.h>
#include <dbus/kglobalaccelcomponent.h>
#endif
// Most of this file is based on the KGlobalAccel sources
// (https://phabricator.kde.org/source/kglobalaccel)
namespace {
2019-12-07 00:56:15 +01:00
#ifdef HAVE_DBUS
2019-12-09 17:25:29 +01:00
QString ComponentDisplayName() {
return QGuiApplication::applicationDisplayName().isEmpty()
2019-12-09 18:02:42 +01:00
? QCoreApplication::applicationName()
: QGuiApplication::applicationDisplayName();
2019-12-07 00:53:40 +01:00
}
2019-12-06 05:25:28 +01:00
2019-12-09 17:25:29 +01:00
QString ComponentUniqueName() { return QCoreApplication::applicationName(); }
2019-12-06 05:25:28 +01:00
2019-12-09 17:25:29 +01:00
const QString& IdActionUniqueName(const QStringList& id) { return id.at(1); }
2019-12-06 05:25:28 +01:00
2019-12-09 17:25:29 +01:00
bool IsCorrectMediaKeyShortcut(const GlobalShortcuts::Shortcut& shortcut) {
2019-12-07 00:53:40 +01:00
if (shortcut.id == QStringLiteral("play_pause")) {
return shortcut.action->shortcut() == QKeySequence(Qt::Key_MediaPlay);
} else if (shortcut.id == QStringLiteral("stop")) {
return shortcut.action->shortcut() == QKeySequence(Qt::Key_MediaStop);
} else if (shortcut.id == QStringLiteral("next_track")) {
return shortcut.action->shortcut() == QKeySequence(Qt::Key_MediaNext);
} else if (shortcut.id == QStringLiteral("prev_track")) {
return shortcut.action->shortcut() == QKeySequence(Qt::Key_MediaPrevious);
} else {
return false;
2019-12-06 05:25:28 +01:00
}
}
2019-12-07 20:30:10 +01:00
#endif // HAVE_DBUS
} // namespace
2019-12-06 05:25:28 +01:00
#ifdef HAVE_DBUS
KGlobalAccelShortcutBackend::KGlobalAccelShortcutBackend(
2019-12-07 20:30:10 +01:00
GlobalShortcuts* parent)
2019-12-09 18:02:42 +01:00
: GlobalShortcutBackend(parent), iface_(nullptr), component_(nullptr) {}
2019-12-06 05:25:28 +01:00
2019-12-07 20:30:10 +01:00
#else // HAVE_DBUS
2019-12-07 00:53:40 +01:00
KGlobalAccelShortcutBackend::KGlobalAccelShortcutBackend(
2019-12-07 20:30:10 +01:00
GlobalShortcuts* parent)
2019-12-07 00:53:40 +01:00
: GlobalShortcutBackend(parent) {}
2019-12-07 20:30:10 +01:00
#endif // HAVE_DBUS
2019-12-06 05:25:28 +01:00
2019-12-09 17:25:29 +01:00
bool KGlobalAccelShortcutBackend::IsKGlobalAccelAvailable() {
2019-12-06 05:25:28 +01:00
#ifdef HAVE_DBUS
return QDBusConnection::sessionBus().interface()->isServiceRegistered(
2019-12-09 17:25:29 +01:00
kService);
2019-12-07 20:30:10 +01:00
#else // HAVE_DBUS
2019-12-06 05:25:28 +01:00
return false;
2019-12-07 20:30:10 +01:00
#endif // HAVE_DBUS
2019-12-06 05:25:28 +01:00
}
bool KGlobalAccelShortcutBackend::DoRegister() {
#ifdef HAVE_DBUS
qLog(Debug) << "Registering shortcuts";
2019-12-09 17:25:29 +01:00
if (!AcquireInterface()) return false;
2019-12-06 05:25:28 +01:00
bool complete = true;
2019-12-07 20:30:10 +01:00
for (const GlobalShortcuts::Shortcut& shortcut :
2019-12-07 00:53:40 +01:00
manager_->shortcuts().values()) {
2019-12-07 20:30:10 +01:00
if (shortcut.action->shortcut().isEmpty()) continue;
2019-12-06 05:25:28 +01:00
2019-12-09 17:25:29 +01:00
if (!RegisterShortcut(shortcut)) complete = false;
2019-12-06 05:25:28 +01:00
}
2019-12-09 17:25:29 +01:00
if (!AcquireComponent()) return false;
2019-12-06 05:25:28 +01:00
QObject::connect(component_,
&OrgKdeKglobalaccelComponentInterface::globalShortcutPressed,
this, &KGlobalAccelShortcutBackend::OnShortcutPressed,
Qt::UniqueConnection);
2019-12-06 05:25:28 +01:00
return complete;
2019-12-07 20:30:10 +01:00
#else // HAVE_DBUS
2019-12-06 05:25:28 +01:00
qLog(Warning) << "dbus not available";
return false;
2019-12-07 20:30:10 +01:00
#endif // HAVE_DBUS
2019-12-06 05:25:28 +01:00
}
void KGlobalAccelShortcutBackend::DoUnregister() {
2019-12-07 00:56:15 +01:00
#ifdef HAVE_DBUS
2019-12-09 17:25:29 +01:00
if (!AcquireInterface()) return;
2019-12-06 05:25:28 +01:00
2019-12-09 17:25:29 +01:00
if (!AcquireComponent()) return;
2019-12-06 05:25:28 +01:00
2019-12-07 20:30:10 +01:00
for (const GlobalShortcuts::Shortcut& shortcut : manager_->shortcuts())
2019-12-09 17:25:29 +01:00
UnregisterAction(shortcut.id, shortcut.action);
2019-12-09 18:02:42 +01:00
QObject::disconnect(
component_, &OrgKdeKglobalaccelComponentInterface::globalShortcutPressed,
this, &KGlobalAccelShortcutBackend::OnShortcutPressed);
2019-12-07 20:30:10 +01:00
#endif // HAVE_DBUS
2019-12-06 05:25:28 +01:00
}
#ifdef HAVE_DBUS
2019-12-09 17:25:29 +01:00
const char* KGlobalAccelShortcutBackend::kService = "org.kde.kglobalaccel";
const char* KGlobalAccelShortcutBackend::kPath = "/kglobalaccel";
2019-12-06 05:25:28 +01:00
2019-12-09 17:25:29 +01:00
bool KGlobalAccelShortcutBackend::AcquireComponent() {
2019-12-06 05:25:28 +01:00
Q_ASSERT(iface_ && iface_->isValid());
if (component_) return true;
2019-12-09 18:02:42 +01:00
QDBusReply<QDBusObjectPath> reply =
iface_->getComponent(ComponentUniqueName());
2019-12-06 05:25:28 +01:00
if (!reply.isValid()) {
if (reply.error().name() ==
QLatin1String("org.kde.kglobalaccel.NoSuchComponent"))
return false;
qLog(Warning) << "Failed to get DBus path for KGlobalAccel component";
return false;
}
2019-12-07 00:53:40 +01:00
component_ = new org::kde::kglobalaccel::Component(
2019-12-09 17:25:29 +01:00
kService, reply.value().path(), QDBusConnection::sessionBus(), iface_);
2019-12-06 05:25:28 +01:00
if (!component_->isValid()) {
qLog(Warning) << "Failed to get KGlobalAccel component:"
<< QDBusConnection::sessionBus().lastError();
2019-12-09 17:25:29 +01:00
component_->deleteLater();
2019-12-06 05:25:28 +01:00
component_ = nullptr;
return false;
}
return true;
}
2019-12-09 17:25:29 +01:00
bool KGlobalAccelShortcutBackend::AcquireInterface() {
2019-12-07 20:30:10 +01:00
if (iface_ && iface_->isValid()) return true;
2019-12-06 05:25:28 +01:00
2019-12-09 17:25:29 +01:00
if (IsKGlobalAccelAvailable()) {
2019-12-07 00:53:40 +01:00
iface_ = new OrgKdeKGlobalAccelInterface(
2019-12-09 17:25:29 +01:00
kService, kPath, QDBusConnection::sessionBus(), this);
2019-12-06 05:25:28 +01:00
}
2019-12-07 20:30:10 +01:00
if (iface_ && iface_->isValid()) return true;
2019-12-06 05:25:28 +01:00
if (!iface_)
qLog(Warning) << "KGlobalAccel daemon not registered";
else if (!iface_->isValid())
qLog(Warning) << "KGlobalAccel daemon is not valid";
return false;
}
2019-12-09 17:25:29 +01:00
QStringList KGlobalAccelShortcutBackend::GetId(const QString& name,
const QAction* action) {
2019-12-06 05:25:28 +01:00
Q_ASSERT(action);
QStringList ret;
2019-12-09 17:25:29 +01:00
ret << ComponentUniqueName();
2019-12-06 05:25:28 +01:00
ret << name;
2019-12-09 17:25:29 +01:00
ret << ComponentDisplayName();
2019-12-06 05:25:28 +01:00
ret << action->text().replace(QLatin1Char('&'), QStringLiteral(""));
2019-12-07 20:30:10 +01:00
if (ret.back().isEmpty()) ret.back() = name;
2019-12-06 05:25:28 +01:00
return ret;
}
2019-12-09 17:25:29 +01:00
QList<int> KGlobalAccelShortcutBackend::ToIntList(
2019-12-07 20:30:10 +01:00
const QList<QKeySequence>& seq) {
2019-12-06 05:25:28 +01:00
QList<int> ret;
2019-12-07 20:30:10 +01:00
for (const QKeySequence& sequence : seq) {
2019-12-06 05:25:28 +01:00
ret.append(sequence[0]);
}
while (!ret.isEmpty() && ret.last() == 0) {
ret.removeLast();
}
return ret;
}
2019-12-09 17:25:29 +01:00
bool KGlobalAccelShortcutBackend::RegisterAction(const QString& name,
QAction* action) {
2019-12-06 05:25:28 +01:00
Q_ASSERT(action);
2019-12-07 00:53:40 +01:00
if (name.isEmpty() &&
(action->objectName().isEmpty() ||
action->objectName().startsWith(QLatin1String("unnamed-")))) {
2019-12-06 05:25:28 +01:00
qLog(Warning) << "Cannot register shortcut for unnamed action";
return false;
}
2019-12-09 17:25:29 +01:00
QStringList action_id = GetId(name, action);
name_to_action_.insertMulti(IdActionUniqueName(action_id), action);
iface_->doRegister(action_id);
2019-12-06 05:25:28 +01:00
return true;
}
2019-12-09 17:25:29 +01:00
bool KGlobalAccelShortcutBackend::RegisterShortcut(
2019-12-07 20:30:10 +01:00
const GlobalShortcuts::Shortcut& shortcut) {
2019-12-09 17:25:29 +01:00
if (!RegisterAction(shortcut.id, shortcut.action)) return false;
2019-12-06 05:25:28 +01:00
2019-12-09 17:25:29 +01:00
QList<QKeySequence> active_shortcut;
active_shortcut << shortcut.action->shortcut();
2019-12-06 05:25:28 +01:00
2019-12-09 17:25:29 +01:00
QStringList action_id = GetId(shortcut.id, shortcut.action);
2019-12-07 00:53:40 +01:00
const QList<int> result = iface_->setShortcut(
2019-12-09 17:25:29 +01:00
action_id, ToIntList(active_shortcut), SetShortcutFlag::SetPresent);
2019-12-06 05:25:28 +01:00
2019-12-09 17:25:29 +01:00
const QList<QKeySequence> result_sequence = ToKeySequenceList(result);
if (result_sequence != active_shortcut) {
qLog(Warning) << "Tried setting global shortcut" << active_shortcut
<< "but KGlobalAccel returned" << result_sequence;
2019-12-06 05:25:28 +01:00
2019-12-09 17:25:29 +01:00
if (!result_sequence.isEmpty()) {
if (!IsCorrectMediaKeyShortcut(shortcut)) {
2019-12-06 05:25:28 +01:00
// there is some conflict with our preferred shortcut so we use
// the new shortcut that kglobalaccel suggests
2019-12-09 17:25:29 +01:00
shortcut.action->setShortcut(result_sequence[0]);
2019-12-06 05:25:28 +01:00
} else {
// media keys are properly handled by plasma through the
// media player plasmoid so we don't do anything in those cases
qLog(Debug) << "Leaving media key shortcuts unchanged";
}
}
}
return true;
}
2019-12-09 17:25:29 +01:00
QList<QKeySequence> KGlobalAccelShortcutBackend::ToKeySequenceList(
2019-12-07 20:30:10 +01:00
const QList<int>& seq) {
2019-12-06 05:25:28 +01:00
QList<QKeySequence> ret;
for (int i : seq) {
ret.append(i);
}
return ret;
}
2019-12-09 17:25:29 +01:00
void KGlobalAccelShortcutBackend::UnregisterAction(const QString& name,
2019-12-07 20:30:10 +01:00
QAction* action) {
2019-12-06 05:25:28 +01:00
Q_ASSERT(action);
2019-12-09 17:25:29 +01:00
QStringList action_id = GetId(name, action);
name_to_action_.remove(IdActionUniqueName(action_id), action);
iface_->unRegister(action_id);
2019-12-06 05:25:28 +01:00
}
2019-12-09 17:25:29 +01:00
void KGlobalAccelShortcutBackend::OnShortcutPressed(
const QString& component_unique, const QString& action_unique,
2019-12-06 05:25:28 +01:00
qlonglong timestamp) const {
2019-12-07 20:30:10 +01:00
QAction* action = nullptr;
2019-12-09 17:25:29 +01:00
const QList<QAction*> candidates = name_to_action_.values(action_unique);
2019-12-07 20:30:10 +01:00
for (QAction* a : candidates) {
2019-12-09 17:25:29 +01:00
if (ComponentUniqueName() == component_unique) {
2019-12-06 05:25:28 +01:00
action = a;
}
}
2019-12-07 20:30:10 +01:00
if (action && action->isEnabled()) action->trigger();
2019-12-06 05:25:28 +01:00
}
2019-12-07 20:30:10 +01:00
#endif // HAVE_DBUS