diff --git a/CMakeLists.txt b/CMakeLists.txt --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -118,6 +118,7 @@ waylandserver.cpp powermanagement.cpp powermanagement_inhibition.cpp + mpris.cpp ) qt5_add_dbus_adaptor(ksld_SRCS ${screensaver_dbusXML} interface.h ScreenLocker::Interface) qt5_add_dbus_adaptor(ksld_SRCS ${kscreensaver_dbusXML} interface.h ScreenLocker::Interface kscreensaveradaptor KScreenSaverAdaptor) diff --git a/kcfg/kscreenlockersettings.kcfg b/kcfg/kscreenlockersettings.kcfg --- a/kcfg/kscreenlockersettings.kcfg +++ b/kcfg/kscreenlockersettings.kcfg @@ -31,6 +31,10 @@ true + + + false + diff --git a/kcm/kcm.ui b/kcm/kcm.ui --- a/kcm/kcm.ui +++ b/kcm/kcm.ui @@ -132,6 +132,20 @@ + + + + Pause media players when locked: + + + + + + + + + + diff --git a/ksldapp.h b/ksldapp.h --- a/ksldapp.h +++ b/ksldapp.h @@ -34,6 +34,7 @@ class QTimer; class KSldTest; class PowerManagementInhibition; +class MPris; namespace KWayland { @@ -185,6 +186,7 @@ int m_greeterCrashedCounter = 0; QProcessEnvironment m_greeterEnv; PowerManagementInhibition *m_powerManagementInhibition; + MPris *m_mpris; // for auto tests friend KSldTest; diff --git a/ksldapp.cpp b/ksldapp.cpp --- a/ksldapp.cpp +++ b/ksldapp.cpp @@ -28,6 +28,7 @@ #include "logind.h" #include "kscreensaversettings.h" #include "powermanagement_inhibition.h" +#include "mpris.h" #include #include #include "waylandserver.h" @@ -93,6 +94,7 @@ , m_logind(nullptr) , m_greeterEnv(QProcessEnvironment::systemEnvironment()) , m_powerManagementInhibition(new PowerManagementInhibition(this)) + , m_mpris(new MPris(this)) { m_isX11 = QX11Info::isPlatformX11(); m_isWayland = QCoreApplication::instance()->property("platformName").toString().startsWith( QLatin1String("wayland"), Qt::CaseInsensitive); @@ -500,6 +502,11 @@ void KSldApp::doUnlock() { qDebug() << "Grab Released"; + + if (KScreenSaverSettings::pauseMediaPlayers()) { + m_mpris->unpausePlayers(); + } + if (m_isX11) { xcb_connection_t *c = QX11Info::connection(); xcb_ungrab_keyboard(c, XCB_CURRENT_TIME); @@ -631,6 +638,10 @@ if (m_isX11) { XSync(QX11Info::display(), False); } + + if (KScreenSaverSettings::pauseMediaPlayers()) { + m_mpris->pausePlayers(); + } } void KSldApp::hideLockWindow() diff --git a/mpris.h b/mpris.h new file mode 100644 --- /dev/null +++ b/mpris.h @@ -0,0 +1,43 @@ +/******************************************************************** + KSld - the KDE Screenlocker Daemon + This file is part of the KDE project. + + Copyright (C) 2016 Kai Uwe Broulik + +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 . +*********************************************************************/ + +#pragma once + +#include + +#include + +class MPris : public QObject +{ + Q_OBJECT + +public: + explicit MPris(QObject *parent = nullptr); + ~MPris() override; + + void pausePlayers(); + void unpausePlayers(); + +private: + void isMediaPlayerPlaying(const QString &serviceName, std::function cb); + + QStringList m_players; + +}; diff --git a/mpris.cpp b/mpris.cpp new file mode 100644 --- /dev/null +++ b/mpris.cpp @@ -0,0 +1,129 @@ +/******************************************************************** + KSld - the KDE Screenlocker Daemon + This file is part of the KDE project. + + Copyright (C) 2016 Kai Uwe Broulik + +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 . +*********************************************************************/ + +#include "mpris.h" + +#include +#include +#include +#include +#include +#include +#include + +#include + +static const QString s_mprisPrefix = QStringLiteral("org.mpris.MediaPlayer2."); +//static const QString s_solidPath = QStringLiteral("/org/kde/Solid/PowerManagement/PolicyAgent"); + +MPris::MPris(QObject *parent) + : QObject(parent) +{ + qDebug() << "HUI"; +} + +MPris::~MPris() = default; + +void MPris::pausePlayers() +{ + qDebug() << "Pause players"; + QDBusPendingCall listNamesCall = QDBusConnection::sessionBus().interface()->asyncCall(QStringLiteral("ListNames")); + QDBusPendingCallWatcher *callWatcher = new QDBusPendingCallWatcher(listNamesCall, this); + connect(callWatcher, &QDBusPendingCallWatcher::finished, this, [this](QDBusPendingCallWatcher *watcher) { + QDBusPendingReply reply = *watcher; + watcher->deleteLater(); + + if (reply.isError()) { + qWarning() << "Failed to fetch list of dbus service names"; + return; + } + + const QStringList &services = reply.value(); + for (const QString &serviceName : services) { + if (!serviceName.startsWith(s_mprisPrefix)) { + continue; + } + + qDebug() << "Check playback status for" << serviceName; + + isMediaPlayerPlaying(serviceName, [this, serviceName](bool playing) { + if (playing) { + qDebug() << "PLAYER" << serviceName << "IS PLAYING"; + + QDBusMessage pauseMsg = QDBusMessage::createMethodCall(serviceName, + QStringLiteral("/org/mpris/MediaPlayer2"), + QStringLiteral("org.mpris.MediaPlayer2.Player"), + QStringLiteral("Pause")); + QDBusPendingReply pauseReply = QDBusConnection::sessionBus().asyncCall(pauseMsg); + QDBusPendingCallWatcher *pauseWatcher = new QDBusPendingCallWatcher(pauseReply, this); + QObject::connect(pauseWatcher, &QDBusPendingCallWatcher::finished, this, [this, serviceName](QDBusPendingCallWatcher *watcher) { + if (!watcher->isError()) { + m_players.append(serviceName); + } + watcher->deleteLater(); + }); + } + }); + } + }); +} + +void MPris::unpausePlayers() +{ + qDebug() << "UNPAUSE PLAYERS"; + for (const QString &serviceName : qAsConst(m_players)) { + qDebug() << "unpause" << serviceName; + QDBusConnection::sessionBus().asyncCall( + QDBusMessage::createMethodCall(serviceName, + QStringLiteral("/org/mpris/MediaPlayer2"), + QStringLiteral("org.mpris.MediaPlayer2.Player"), + QStringLiteral("Play")) + ); + } + m_players.clear(); +} + +void MPris::isMediaPlayerPlaying(const QString &serviceName, std::function cb) +{ + QDBusMessage msg = QDBusMessage::createMethodCall(serviceName, + QStringLiteral("/org/mpris/MediaPlayer2"), + QStringLiteral("org.freedesktop.DBus.Properties"), + QStringLiteral("Get")); + msg.setArguments({ + QStringLiteral("org.mpris.MediaPlayer2.Player"), + QStringLiteral("PlaybackStatus") + }); + + QDBusPendingReply reply = QDBusConnection::sessionBus().asyncCall(msg); + QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(reply, this); + QObject::connect(watcher, &QDBusPendingCallWatcher::finished, this, [this, cb](QDBusPendingCallWatcher *watcher) { + QDBusPendingReply reply = *watcher; + watcher->deleteLater(); + + if (reply.isError()) { + qWarning() << "Failed to check playback status"; + return; + } + + const QString &playbackStatus = reply.value().variant().toString(); + + cb(playbackStatus == QLatin1String("Playing")); + }); +}