/*
* Copyright 2015 Canonical Ltd.
* Copyright 2021 Robert Tari
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 3, as published
* by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranties of
* MERCHANTABILITY, SATISFACTORY QUALITY, 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 .
*
* Authors:
* Xavi Garcia
* Robert Tari
*/
#include "dbus-pulse-volume.h"
#include "dbus_properties_interface.h"
#include "dbus_accounts_interface.h"
#include "dbus_accountssound_interface.h"
#include "stream_restore_interface.h"
#include
#include
unsigned int volumeDoubleToUint(double volume)
{
double tmp = (double)(PA_VOLUME_NORM - PA_VOLUME_MUTED) * volume;
return (unsigned int)tmp + PA_VOLUME_MUTED;
}
double volumeUIntToDoulbe(uint volume)
{
double tmp = (double)(volume - PA_VOLUME_MUTED);
return tmp / (double)(PA_VOLUME_NORM - PA_VOLUME_MUTED);
}
DBusPulseVolume::DBusPulseVolume() :
QObject()
{
std::unique_ptr basicConnectionInterface(new DBusPropertiesInterface("org.PulseAudio1",
"/org/pulseaudio/server_lookup1",
QDBusConnection::sessionBus(), 0));
QDBusReply connection_string = basicConnectionInterface->call(QLatin1String("Get"),
QLatin1String("org.PulseAudio.ServerLookup1"),
QLatin1String("Address"));
if (!connection_string.isValid())
{
qWarning() << "DBusPulseVolume::DBusPulseVolume(): D-Bus error: " << connection_string.error().message();
}
connection_.reset(new QDBusConnection(QDBusConnection::connectToPeer(connection_string.value().toString(), "set-volume")));
if (connection_->isConnected())
{
interface_paths_.reset(new StreamRestoreInterface("org.PulseAudio.Ext.StreamRestore1",
"/org/pulseaudio/stream_restore1",
*(connection_.get()), 0));
if (interface_paths_)
{
// get the role paths
fillRolePath("multimedia");
fillRolePath("alert");
fillRolePath("alarm");
fillRolePath("phone");
}
initializeAccountsInterface();
}
else
{
qWarning() << "DBusPulseVolume::DBusPulseVolume(): Error connecting: " << connection_->lastError().message();
}
}
DBusPulseVolume::~DBusPulseVolume()
{
}
QString DBusPulseVolume::fillRolePath(QString const &role)
{
QString role_complete_name = QString("sink-input-by-media-role:") + role;
// get the role paths
QDBusReply objectPath = interface_paths_->call(QLatin1String("GetEntryByName"), role_complete_name);
if (!objectPath.isValid())
{
qWarning() << "SetVolume::fillRolePath(): D-Bus error: " << objectPath.error().message();
return "";
}
auto role_info = std::make_shared();
role_info->interface_.reset(new DBusPropertiesInterface("org.PulseAudio.Ext.StreamRestore1.RestoreEntry",
objectPath.value().path(),
*(connection_.get()), 0));
if (!role_info->interface_)
{
qWarning() << "SetVolume::fillRolePath() - Error obtaining properties interface";
return "";
}
role_info->path_ = objectPath.value().path();
roles_map_[role] = role_info;
return role_info->path_;
}
bool DBusPulseVolume::setVolume(QString const & role, double volume)
{
if (!interface_paths_)
{
qWarning() << "SetVolume::setVolume(): error: Volume interfaces are not initialized";
return false;
}
RolesMap::const_iterator iter = roles_map_.find(role);
if (iter != roles_map_.end())
{
QVariant var;
PulseaudioVolumeArray t;
PulseaudioVolume vv(0, volumeDoubleToUint(volume));
t.addItem(vv);
var.setValue(t);
QDBusVariant dbusVar(var);
QDBusReply set_vol = (*iter).second->interface_->call(QLatin1String("Set"),
QVariant::fromValue(QString("org.PulseAudio.Ext.StreamRestore1.RestoreEntry")),
QVariant::fromValue(QString("Volume")),
QVariant::fromValue(dbusVar));
if (!set_vol.isValid())
{
qWarning() << "SetVolume::setVolume(): D-Bus error: " << set_vol.error().message();
return false;
}
if (accounts_interface_)
{
QDBusVariant pDbusVar(QVariant::fromValue(volume));
QDBusReply pSetVol = accounts_interface_->call(QLatin1String("Set"),
QVariant::fromValue(QString("com.lomiri.AccountsService.Sound")),
QVariant::fromValue(QString("Volume")),
QVariant::fromValue(pDbusVar));
if (!set_vol.isValid())
{
qWarning() << "SetVolume::setVolume(): D-Bus error: " << pSetVol.error().message();
return false;
}
}
}
else
{
qWarning() << "SetVolume::setVolume(): role " << role << " was not found.";
return false;
}
return true;
}
double DBusPulseVolume::getVolume(QString const & role)
{
if (interface_paths_)
{
RolesMap::const_iterator iter = roles_map_.find(role);
if (iter != roles_map_.end())
{
QDBusReply prev_vol = (*iter).second->interface_->call(QLatin1String("Get"),
QLatin1String("org.PulseAudio.Ext.StreamRestore1.RestoreEntry"),
QLatin1String("Volume"));
if (!prev_vol.isValid())
{
qWarning() << "SetVolume::setVolume(): D-Bus error: " << prev_vol.error().message();
}
QDBusArgument arg = prev_vol.value().value();
PulseaudioVolumeArray element;
arg >> element;
return volumeUIntToDoulbe(element.getItem(0).getVolume());
}
}
return -1.0;
}
void DBusPulseVolume::initializeAccountsInterface()
{
auto username = qgetenv("USER");
if (username != "")
{
qDebug() << "Setting Accounts interface for user: " << username;
std::unique_ptr setInterface(new AccountsInterface("org.freedesktop.Accounts",
"/org/freedesktop/Accounts",
QDBusConnection::systemBus(), 0));
QDBusReply userResp = setInterface->call(QLatin1String("FindUserByName"),
QLatin1String(username));
if (!userResp.isValid())
{
qWarning() << "SetVolume::initializeAccountsInterface(): D-Bus error: " << userResp.error().message();
}
auto userPath = userResp.value().path();
if (userPath != "")
{
std::unique_ptr soundInterface(new AccountsSoundInterface("org.freedesktop.Accounts",
userPath,
QDBusConnection::systemBus(), 0));
accounts_interface_.reset(new DBusPropertiesInterface("org.freedesktop.Accounts",
userPath,
soundInterface->connection(), 0));
if (!accounts_interface_->isValid())
{
qWarning() << "SetVolume::initializeAccountsInterface(): D-Bus error: " << accounts_interface_->lastError().message();
}
signal_spy_volume_changed_.reset(new QSignalSpy(accounts_interface_.get(),&DBusPropertiesInterface::PropertiesChanged));
}
}
}
bool DBusPulseVolume::waitForVolumeChangedInAccountsService()
{
if (signal_spy_volume_changed_)
{
return signal_spy_volume_changed_->wait();
}
else
{
qWarning() << "DBusPulseVolume::waitForVolumeChangedInAccountsService(): signal was not instantiated";
}
return false;
}