diff --git a/CMakeLists.txt b/CMakeLists.txt --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -17,7 +17,7 @@ find_package(KF5 ${KF5_MIN_VERSION} REQUIRED COMPONENTS I18n ConfigWidgets DBusAddons IconThemes Notifications KIO KCMUtils - OPTIONAL_COMPONENTS DocTools + OPTIONAL_COMPONENTS DocTools PulseAudioQt ) find_package(Qca-qt5 2.1.0 REQUIRED) find_package(Phonon4Qt5 4.9.0 NO_MODULE) diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -30,5 +30,10 @@ add_subdirectory(lockdevice) endif() +if(KF5PulseAudioQt_FOUND) + add_subdirectory(systemvolume) +endif() + + #FIXME: If we split notifications in several files, they won't appear in the same group in the Notifications KCM install(FILES kdeconnect.notifyrc DESTINATION ${KNOTIFYRC_INSTALL_DIR}) diff --git a/plugins/systemvolume/CMakeLists.txt b/plugins/systemvolume/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/plugins/systemvolume/CMakeLists.txt @@ -0,0 +1,11 @@ +set(kdeconnect_systemvolume_SRCS + systemvolumeplugin.cpp +) + +kdeconnect_add_plugin(kdeconnect_systemvolume JSON kdeconnect_systemvolume.json SOURCES ${kdeconnect_systemvolume_SRCS}) + +target_link_libraries(kdeconnect_systemvolume + kdeconnectcore + Qt5::Core + KF5::PulseAudioQt +) diff --git a/plugins/systemvolume/README b/plugins/systemvolume/README new file mode 100644 --- /dev/null +++ b/plugins/systemvolume/README @@ -0,0 +1 @@ +This plugin allows to control the system volume. diff --git a/plugins/systemvolume/kdeconnect_systemvolume.json b/plugins/systemvolume/kdeconnect_systemvolume.json new file mode 100644 --- /dev/null +++ b/plugins/systemvolume/kdeconnect_systemvolume.json @@ -0,0 +1,28 @@ +{ + "Encoding": "UTF-8", + "KPlugin": { + "Authors": [ + { + "Email": "nicolas.fella@gmx.de", + "Name": "Nicolas Fella" + } + ], + "Description": "Control the system volume from your phone", + "EnabledByDefault": true, + "Icon": "audio-volume-high", + "Id": "kdeconnect_systemvolume", + "License": "GPL", + "Name": "System volume", + "ServiceTypes": [ + "KdeConnect/Plugin" + ], + "Version": "0.1", + "Website": "http://nicolasfella.wordpress.com" + }, + "X-KdeConnect-OutgoingPacketType": [ + "kdeconnect.systemvolume" + ], + "X-KdeConnect-SupportedPacketType": [ + "kdeconnect.systemvolume.request" + ] +} diff --git a/plugins/systemvolume/systemvolumeplugin.h b/plugins/systemvolume/systemvolumeplugin.h new file mode 100644 --- /dev/null +++ b/plugins/systemvolume/systemvolumeplugin.h @@ -0,0 +1,51 @@ +/** + * Copyright 2017 Nicolas Fella + * + * 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) version 3 or any later version + * accepted by the membership of KDE e.V. (or its successor approved + * by the membership of KDE e.V.), which shall act as a proxy + * defined in Section 14 of version 3 of the license. + * + * 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 . + */ + +#ifndef SYSTEMVOLUMEPLUGIN_H +#define SYSTEMVOLUMEPLUGIN_H + +#include +#include + +#include + +#include + +#define PACKET_TYPE_SYSTEMVOLUME QStringLiteral("kdeconnect.systemvolume") +#define PACKET_TYPE_SYSTEMVOLUME_REQUEST QStringLiteral("kdeconnect.systemvolume.request") + + +class Q_DECL_EXPORT SystemvolumePlugin + : public KdeConnectPlugin +{ + Q_OBJECT + +public: + explicit SystemvolumePlugin(QObject* parent, const QVariantList& args); + + bool receivePacket(const NetworkPacket& np) override; + void connected() override; + +private: + void sendSinkList(); + QMap sinksMap; +}; + +#endif diff --git a/plugins/systemvolume/systemvolumeplugin.cpp b/plugins/systemvolume/systemvolumeplugin.cpp new file mode 100644 --- /dev/null +++ b/plugins/systemvolume/systemvolumeplugin.cpp @@ -0,0 +1,127 @@ +/** + * Copyright 2017 Nicolas Fella + * + * 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) version 3 or any later version + * accepted by the membership of KDE e.V. (or its successor approved + * by the membership of KDE e.V.), which shall act as a proxy + * defined in Section 14 of version 3 of the license. + * + * 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 "systemvolumeplugin.h" + +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +K_PLUGIN_FACTORY_WITH_JSON( KdeConnectPluginFactory, "kdeconnect_systemvolume.json", registerPlugin< SystemvolumePlugin >(); ) + +Q_LOGGING_CATEGORY(KDECONNECT_PLUGIN_SYSTEMVOLUME, "kdeconnect.plugin.systemvolume") + +SystemvolumePlugin::SystemvolumePlugin(QObject* parent, const QVariantList& args) + : KdeConnectPlugin(parent, args) + , sinksMap() +{} + +bool SystemvolumePlugin::receivePacket(const NetworkPacket& np) +{ + + if (!PulseAudioQt::Context::instance()->isValid()) + return false; + + if (np.has(QStringLiteral("requestSinks"))) { + sendSinkList(); + } else { + + QString name = np.get(QStringLiteral("name")); + + if (sinksMap.contains(name)) { + if (np.has(QStringLiteral("volume"))) { + sinksMap[name]->setVolume(np.get(QStringLiteral("volume"))); + } + if (np.has(QStringLiteral("muted"))) { + sinksMap[name]->setMuted(np.get(QStringLiteral("muted"))); + } + } + } + return true; +} + +void SystemvolumePlugin::sendSinkList() { + + QJsonDocument document; + QJsonArray array; + + sinksMap.clear(); + + for (PulseAudioQt::Sink* sink : PulseAudioQt::Context::instance()->sinks()) { + sinksMap.insert(sink->name(), sink); + + connect(sink, &PulseAudioQt::Sink::volumeChanged, this, [this, sink] { + NetworkPacket np(PACKET_TYPE_SYSTEMVOLUME); + np.set(QStringLiteral("volume"), sink->volume()); + np.set(QStringLiteral("name"), sink->name()); + sendPacket(np); + }); + + connect(sink, &PulseAudioQt::Sink::mutedChanged, this, [this, sink] { + NetworkPacket np(PACKET_TYPE_SYSTEMVOLUME); + np.set(QStringLiteral("muted"), sink->isMuted()); + np.set(QStringLiteral("name"), sink->name()); + sendPacket(np); + }); + + QJsonObject sinkObject; + sinkObject.insert("name", sink->name()); + sinkObject.insert("description", sink->description()); + sinkObject.insert("muted", sink->isMuted()); + sinkObject.insert("volume", sink->volume()); + sinkObject.insert("maxVolume", PulseAudioQt::normalVolume()); + + array.append(sinkObject); + } + + document.setArray(array); + + NetworkPacket np(PACKET_TYPE_SYSTEMVOLUME); + np.set(QStringLiteral("sinkList"), document); + sendPacket(np); +} + +void SystemvolumePlugin::connected() +{ + connect(PulseAudioQt::Context::instance(), &PulseAudioQt::Context::sinkAdded, this, [this] { + sendSinkList(); + }); + + connect(PulseAudioQt::Context::instance(), &PulseAudioQt::Context::sinkRemoved, this, [this] { + sendSinkList(); + }); + + for (PulseAudioQt::Sink* sink : PulseAudioQt::Context::instance()->sinks()) { + sinksMap.insert(sink->name(), sink); + } +} + +#include "systemvolumeplugin.moc" +