kde-extraapps/kdeplasma-addons/applets/icontasks/mediabuttons.cpp
Ivailo Monev 2734df9d74 kdeplasma-addons: check for MPRIS interface instead of relying on categories
generally speaking trying to guess MPRIS interfaces is brute-force guess - it
works kinda. the check that was used before this change however does not
account for applications that are not categorized as audio or music player
but do have MPRIS interface (chromium does).

side note: tested only the MPRIS v2 interface

Signed-off-by: Ivailo Monev <xakepa10@gmail.com>
2022-11-08 20:34:29 +02:00

364 lines
9.7 KiB
C++

/*
* Icon Task Manager
*
* Copyright 2011 Craig Drummond <craig@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; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "mediabuttons.h"
#include "dbusstatus.h"
#include "playerv1interface.h"
#include "playerv2interface.h"
#include <KConfig>
#include <KConfigGroup>
#include <KGlobal>
#include <KStandardDirs>
#include <KSycoca>
#include <KService>
#include <KServiceTypeTrader>
#include <QtDBus/QDBusServiceWatcher>
#include <QtDBus/QDBusConnection>
#include <QtDBus/QDBusConnectionInterface>
#include <QDebug>
K_GLOBAL_STATIC(MediaButtons, mediaBtns)
static const QString constV2Prefix = QLatin1String("org.mpris.MediaPlayer2.");
static const QString constV1Prefix = QLatin1String("org.mpris.");
static QString playbackStatus(OrgFreedesktopMediaPlayerInterface *iface)
{
DBusStatus status = iface->GetStatus();
switch (status.play) {
case DBusStatus::Mpris_Playing: return "Playing";
case DBusStatus::Mpris_Paused: return "Paused";
case DBusStatus::Mpris_Stopped: return "Stopped";
}
return QString();
}
MediaButtons::Interface::~Interface()
{
if (v1) {
delete v1;
}
if (v2) {
delete v2;
}
}
void MediaButtons::Interface::next()
{
if (v2) {
v2->Next();
} else if (v1) {
v1->Next();
}
}
void MediaButtons::Interface::previous()
{
if (v2) {
v2->Previous();
} else if (v1) {
v1->Prev();
}
}
void MediaButtons::Interface::playPause()
{
if (v2) {
v2->PlayPause();
} else if (v1) {
if ("Playing" ==::playbackStatus(v1)) {
v1->Pause();
} else {
v1->Play();
}
}
}
QString MediaButtons::Interface::playbackStatus()
{
if (v2) {
return v2->playbackStatus();
} else if (v1) {
return ::playbackStatus(v1);
}
return QString();
}
QString MediaButtons::Interface::service()
{
if (v2) {
return v2->service();
} else if (v1) {
return v1->service();
}
return QString();
}
MediaButtons * MediaButtons::self()
{
return mediaBtns;
}
MediaButtons::MediaButtons()
: m_watcher(0)
, m_enabled(false)
{
qDBusRegisterMetaType<DBusStatus>();
}
void MediaButtons::setEnabled(bool en)
{
if (en != m_enabled) {
m_enabled = en;
if (m_enabled) {
m_watcher = new QDBusServiceWatcher(this);
m_watcher->setConnection(QDBusConnection::sessionBus());
m_watcher->setWatchMode(QDBusServiceWatcher::WatchForOwnerChange);
connect(m_watcher, SIGNAL(serviceOwnerChanged(QString, QString, QString)), this, SLOT(serviceOwnerChanged(QString, QString, QString)));
readConfig();
} else if (m_watcher) {
disconnect(m_watcher, SIGNAL(serviceOwnerChanged(QString, QString, QString)), this, SLOT(serviceOwnerChanged(QString, QString, QString)));
foreach (Interface * iface, m_interfaces.values()) {
delete iface;
}
m_interfaces.clear();
delete m_watcher;
}
}
}
void MediaButtons::serviceOwnerChanged(const QString &name, const QString &oldOwner, const QString &newOwner)
{
bool isV2 = name.startsWith(constV2Prefix);
QString n = QString(name).remove(isV2 ? constV2Prefix : constV1Prefix).toLower();
QMap<QString, Interface *>::iterator it(m_interfaces.find(n)),
end(m_interfaces.end());
if (newOwner.isEmpty()) {
if (it != end) {
if ((*it)->isV2() == isV2) {
delete(*it);
m_interfaces.erase(it);
}
}
} else if (oldOwner.isEmpty()) {
if (isV2) {
OrgMprisMediaPlayer2PlayerInterface *iface = new OrgMprisMediaPlayer2PlayerInterface(name, "/org/mpris/MediaPlayer2", QDBusConnection::sessionBus(), this);
if (iface->canControl()) {
if (it != end) {
delete(*it);
m_interfaces.erase(it);
}
m_interfaces.insert(n, new Interface(iface));
} else {
delete iface;
}
} else if (it == end || !(*it)->isV2()) {
OrgFreedesktopMediaPlayerInterface *iface = new OrgFreedesktopMediaPlayerInterface(name, "/Player", QDBusConnection::sessionBus(), this);
if (it != end) {
delete(*it);
m_interfaces.erase(it);
}
m_interfaces.insert(n, new Interface(iface));
}
}
}
void MediaButtons::next(const QString &name, int pid)
{
if (m_enabled) {
Interface *iface = getInterface(name, pid);
if (iface) {
iface->next();
}
}
}
void MediaButtons::previous(const QString &name, int pid)
{
if (m_enabled) {
Interface *iface = getInterface(name, pid);
if (iface) {
iface->previous();
}
}
}
void MediaButtons::playPause(const QString &name, int pid)
{
if (m_enabled) {
Interface *iface = getInterface(name, pid);
if (iface) {
iface->playPause();
}
}
}
QString MediaButtons::playbackStatus(const QString &name, int pid)
{
if (m_enabled) {
Interface *iface = getInterface(name, pid);
if (iface) {
return iface->playbackStatus();
}
}
return QString();
}
void MediaButtons::readConfig()
{
m_aliases.clear();
m_ignore.clear();
QStringList files(KGlobal::dirs()->findAllResources("data", "kdeplasma-addons/mediabuttonsrc"));
foreach (const QString &file, files) {
KConfig cfg(file);
KConfigGroup ag(&cfg, "Aliases");
KConfigGroup gen(&cfg, "General");
m_ignore += gen.readEntry("Ignore", QStringList()).toSet();
foreach (const QString &key, ag.keyList()) {
foreach (const QString &alias, ag.readEntry(key, QStringList())) {
m_aliases[alias.toLower()] = key.toLower();
}
}
}
}
bool MediaButtons::isMediaApp(const QString &desktopEntry)
{
if (!m_enabled) {
return false;
}
QString name = desktopEntry;
if (m_aliases.contains(name)) {
name = m_aliases[name];
}
if (m_ignore.contains(name)) {
return false;
}
MediaButtons::Interface *i = getV2Interface(name);
// qDebug() << Q_FUNC_INFO << desktopEntry << i;
if (i) {
return true;
}
i = getV1Interface(name);
// qDebug() << Q_FUNC_INFO << desktopEntry << i;
if (i) {
return i;
}
return false;
}
MediaButtons::Interface * MediaButtons::getInterface(const QString &name, int pid)
{
QStringList names;
if (m_aliases.contains(name)) {
QString alias = m_aliases[name];
names << alias << alias + "." + QString::number(pid) << alias + "-" + QString::number(pid);
}
names << name << name + "." + QString::number(pid) << name + "-" + QString::number(pid);
foreach (const QString &n, names) {
if (m_interfaces.contains(n)) {
return m_interfaces[n];
}
}
foreach (const QString &n, names) {
MediaButtons::Interface *i = getV2Interface(n);
if (i) {
return i;
}
}
foreach (const QString &n, names) {
MediaButtons::Interface *i = getV1Interface(n);
if (i) {
return i;
}
}
return 0;
}
MediaButtons::Interface * MediaButtons::getV2Interface(const QString &name)
{
QDBusReply<bool> reply = QDBusConnection::sessionBus().interface()->isServiceRegistered(constV2Prefix + name);
if (reply.isValid() && reply.value()) {
serviceOwnerChanged(constV2Prefix + name, QString(), QLatin1String("X"));
if (m_interfaces.contains(name)) {
m_watcher->addWatchedService(constV2Prefix + name);
return m_interfaces[name];
}
}
QDBusReply<QStringList> registeredReply = QDBusConnection::sessionBus().interface()->registeredServiceNames();
if (!registeredReply.isValid()) {
return nullptr;
}
foreach (const QString &value, registeredReply.value()) {
if (value.startsWith(constV2Prefix + name + QLatin1Char('.'))) {
serviceOwnerChanged(value, QString(), QLatin1String("X"));
// qDebug() << Q_FUNC_INFO << value << m_interfaces;
QString unsuffixedName = value.mid(constV2Prefix.size(), value.size() - constV2Prefix.size());
if (m_interfaces.contains(unsuffixedName)) {
m_watcher->addWatchedService(value);
return m_interfaces[unsuffixedName];
}
}
}
return nullptr;
}
MediaButtons::Interface * MediaButtons::getV1Interface(const QString &name)
{
QDBusReply<bool> reply = QDBusConnection::sessionBus().interface()->isServiceRegistered(constV1Prefix + name);
if (reply.isValid() && reply.value()) {
serviceOwnerChanged(constV1Prefix + name, QString(), QLatin1String("X"));
if (m_interfaces.contains(name)) {
m_watcher->addWatchedService(constV1Prefix + name);
return m_interfaces[name];
}
}
return 0;
}
#include "moc_mediabuttons.cpp"