-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[Packages] AudioControl: add DBus service
And implement Open() method to be able to raise the app's window from another process Signed-off-by: Nikita Bazulin <[email protected]>
- Loading branch information
Showing
6 changed files
with
343 additions
and
189 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,158 @@ | ||
/* | ||
* Copyright 2022-2024 TII (SSRC) and the Ghaf contributors | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
#include "App.hpp" | ||
|
||
#include <glibmm/optioncontext.h> | ||
|
||
#include <format> | ||
|
||
using namespace ghaf::AudioControl; | ||
|
||
namespace | ||
{ | ||
|
||
constexpr auto AppId = "Ghaf Audio Control"; | ||
|
||
struct AppArgs | ||
{ | ||
Glib::ustring pulseServerAddress; | ||
Glib::ustring indicatorIconPath; | ||
Glib::ustring appVms; | ||
}; | ||
|
||
std::vector<std::string> GetAppVmsList(const std::string& appVms) | ||
{ | ||
std::vector<std::string> result; | ||
|
||
std::istringstream iss(appVms); | ||
std::string buf; | ||
|
||
while (getline(iss, buf, ',')) | ||
result.push_back(buf); | ||
|
||
return result; | ||
} | ||
|
||
} // namespace | ||
|
||
App::AppMenu::AppMenu(App& app) | ||
: m_app(app) | ||
, m_openItem("Open/Hide Audio Control") | ||
, m_quitItem("Quit") | ||
, m_connections{m_openItem.signal_activate().connect([this]() { m_app.toggleWindow(); }), m_quitItem.signal_activate().connect([this]() { m_app.onQuit(); })} | ||
{ | ||
add(m_openItem); | ||
add(m_quitItem); | ||
|
||
show_all(); | ||
} | ||
|
||
App::App(int argc, char** argv) | ||
: m_menu(*this) | ||
, m_indicator(createAppIndicator()) | ||
, m_connections{m_dbusService.openSignal().connect(sigc::mem_fun(*this, &App::openWindow))} | ||
{ | ||
AppArgs appArgs; | ||
|
||
Glib::OptionEntry pulseServerOption; | ||
pulseServerOption.set_long_name("pulseaudio_server"); | ||
pulseServerOption.set_description("PulseAudio server address"); | ||
|
||
Glib::OptionEntry indicatorIconPathOption; | ||
indicatorIconPathOption.set_long_name("indicator_icon_path"); | ||
indicatorIconPathOption.set_description("Tray's icon indicator path"); | ||
|
||
Glib::OptionEntry appVmsOption; | ||
appVmsOption.set_long_name("app_vms"); | ||
appVmsOption.set_description("AppVMs list"); | ||
|
||
Glib::OptionGroup options("Main", "Main"); | ||
options.add_entry(pulseServerOption, appArgs.pulseServerAddress); | ||
options.add_entry(indicatorIconPathOption, appArgs.indicatorIconPath); | ||
options.add_entry(appVmsOption, appArgs.appVms); | ||
|
||
Glib::OptionContext context("Application Options"); | ||
context.set_main_group(options); | ||
|
||
if (!context.parse(argc, argv)) | ||
{ | ||
Logger::info(context.get_help().c_str()); | ||
throw std::runtime_error{"Couldn't parse the command line arguments"}; | ||
} | ||
|
||
app_indicator_set_icon(m_indicator.get(), appArgs.indicatorIconPath.c_str()); | ||
|
||
m_audioControl = std::make_unique<AudioControl>(std::make_unique<Backend::PulseAudio::AudioControlBackend>(appArgs.pulseServerAddress), | ||
GetAppVmsList(appArgs.appVms)); | ||
} | ||
|
||
int App::start(int argc, char** argv) | ||
{ | ||
Logger::debug(__PRETTY_FUNCTION__); | ||
return run(argc, argv); | ||
} | ||
|
||
bool App::onWindowDelete([[maybe_unused]] GdkEventAny* event) | ||
{ | ||
Logger::debug(__PRETTY_FUNCTION__); | ||
|
||
m_window->hide(); | ||
return true; | ||
} | ||
|
||
void App::openWindow() | ||
{ | ||
m_window->show_all(); | ||
m_window->present(); | ||
} | ||
|
||
void App::toggleWindow() | ||
{ | ||
auto& window = *m_window; | ||
|
||
ghaf::AudioControl::Logger::debug(std::format("Indicator has been activated. window.is_visible: {}", window.is_visible())); | ||
|
||
if (window.is_visible()) | ||
window.hide(); | ||
else | ||
openWindow(); | ||
} | ||
|
||
void App::onQuit() | ||
{ | ||
release(); | ||
quit(); | ||
} | ||
|
||
void App::on_activate() | ||
{ | ||
Logger::debug(__PRETTY_FUNCTION__); | ||
|
||
m_window = std::make_unique<Gtk::ApplicationWindow>(); | ||
m_window->set_title(AppId); | ||
m_window->add(*m_audioControl); | ||
|
||
m_window->signal_delete_event().connect(sigc::mem_fun(*this, &App::onWindowDelete)); | ||
|
||
hold(); | ||
add_window(*m_window); | ||
|
||
m_window->show_all(); | ||
} | ||
|
||
RaiiWrap<AppIndicator*> App::createAppIndicator() | ||
{ | ||
const auto contructor = [this](AppIndicator*& indicator) | ||
{ | ||
indicator = app_indicator_new(AppId, "", APP_INDICATOR_CATEGORY_APPLICATION_STATUS); | ||
app_indicator_set_status(indicator, APP_INDICATOR_STATUS_ACTIVE); | ||
app_indicator_set_label(indicator, AppId, AppId); | ||
app_indicator_set_title(indicator, AppId); | ||
app_indicator_set_menu(indicator, GTK_MENU(m_menu.gobj())); | ||
}; | ||
|
||
return {contructor, {}}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
/* | ||
* Copyright 2022-2024 TII (SSRC) and the Ghaf contributors | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
#pragma once | ||
|
||
#include <GhafAudioControl/Backends/PulseAudio/AudioControlBackend.hpp> | ||
#include <GhafAudioControl/utils/Debug.hpp> | ||
#include <GhafAudioControl/utils/Logger.hpp> | ||
#include <GhafAudioControl/widgets/AudioControl.hpp> | ||
|
||
#include "DBusService.hpp" | ||
|
||
#include <gtkmm/application.h> | ||
#include <gtkmm/applicationwindow.h> | ||
|
||
#include <libayatana-appindicator/app-indicator.h> | ||
|
||
class App : public Gtk::Application | ||
{ | ||
private: | ||
class AppMenu : public Gtk::Menu | ||
{ | ||
public: | ||
AppMenu(App& app); | ||
~AppMenu() override = default; | ||
|
||
private: | ||
App& m_app; | ||
|
||
Gtk::MenuItem m_openItem; | ||
Gtk::MenuItem m_quitItem; | ||
|
||
ghaf::AudioControl::ConnectionContainer m_connections; | ||
}; | ||
|
||
public: | ||
App(int argc, char** argv); | ||
|
||
int start(int argc, char** argv); | ||
|
||
[[nodiscard]] ghaf::AudioControl::RaiiWrap<AppIndicator*> createAppIndicator(); | ||
|
||
void openWindow(); | ||
void toggleWindow(); | ||
|
||
private: | ||
bool onWindowDelete([[maybe_unused]] GdkEventAny* event); | ||
void onQuit(); | ||
|
||
void on_activate() override; | ||
|
||
private: | ||
DBusService m_dbusService; | ||
|
||
std::unique_ptr<ghaf::AudioControl::AudioControl> m_audioControl; | ||
std::unique_ptr<Gtk::ApplicationWindow> m_window; | ||
|
||
AppMenu m_menu; | ||
ghaf::AudioControl::RaiiWrap<AppIndicator*> m_indicator; | ||
|
||
ghaf::AudioControl::ConnectionContainer m_connections; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
/* | ||
* Copyright 2022-2024 TII (SSRC) and the Ghaf contributors | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
#include "DBusService.hpp" | ||
|
||
#include <GhafAudioControl/utils/Logger.hpp> | ||
|
||
#include <giomm/dbusownname.h> | ||
|
||
#include <format> | ||
#include <iostream> | ||
|
||
using namespace ghaf::AudioControl; | ||
|
||
namespace | ||
{ | ||
|
||
constexpr auto IntrospectionXml = "<node>" | ||
" <interface name='org.ghaf.Audio'>" | ||
" <method name='Open'/>" | ||
" </interface>" | ||
"</node>"; | ||
|
||
} | ||
|
||
DBusService::DBusService() | ||
: m_interfaceVtable(sigc::mem_fun(*this, &DBusService::onMethodCall)) | ||
, m_introspectionData(Gio::DBus::NodeInfo::create_for_xml(IntrospectionXml)) | ||
, m_connectionId(Gio::DBus::own_name(Gio::DBus::BUS_TYPE_SESSION, "org.ghaf.Audio", sigc::mem_fun(*this, &DBusService::onBusAcquired), | ||
sigc::mem_fun(*this, &DBusService::onNameAcquired), sigc::mem_fun(*this, &DBusService::onNameLost))) | ||
{ | ||
} | ||
|
||
DBusService::~DBusService() | ||
{ | ||
Gio::DBus::unown_name(m_connectionId); | ||
} | ||
|
||
void DBusService::onBusAcquired(const Glib::RefPtr<Gio::DBus::Connection>& connection, const Glib::ustring&) | ||
{ | ||
Logger::debug("The bus is acquired, registering..."); | ||
connection->register_object("/org/ghaf/Audio", m_introspectionData->lookup_interface("org.ghaf.Audio"), m_interfaceVtable); | ||
} | ||
|
||
void DBusService::onNameAcquired(const Glib::RefPtr<Gio::DBus::Connection>&, const Glib::ustring&) | ||
{ | ||
Logger::debug("The DBus service org.ghaf.Audio has been registered"); | ||
} | ||
|
||
void DBusService::onNameLost(const Glib::RefPtr<Gio::DBus::Connection>&, const Glib::ustring&) | ||
{ | ||
Logger::error("Couldn't register service for org.ghaf.Audio"); | ||
} | ||
|
||
void DBusService::onMethodCall(const Glib::RefPtr<Gio::DBus::Connection>& connection, const Glib::ustring& sender, const Glib::ustring& objectPath, | ||
const Glib::ustring& interfaceName, const Glib::ustring& methodName, const Glib::VariantContainerBase& parameters, | ||
const Glib::RefPtr<Gio::DBus::MethodInvocation>& invocation) | ||
{ | ||
Logger::debug(std::format("Invokated method: {}", methodName.c_str())); | ||
|
||
if (methodName == "Open") | ||
{ | ||
m_openSignal(); | ||
invocation->return_value(parameters); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
/* | ||
* Copyright 2022-2024 TII (SSRC) and the Ghaf contributors | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
#pragma once | ||
|
||
#include <giomm/dbusinterfacevtable.h> | ||
#include <giomm/dbusintrospection.h> | ||
#include <giomm/dbusmethodinvocation.h> | ||
|
||
class DBusService | ||
{ | ||
public: | ||
using OpenSignalSignature = sigc::signal<void()>; | ||
|
||
public: | ||
DBusService(); | ||
~DBusService(); | ||
|
||
public: | ||
OpenSignalSignature openSignal() const noexcept | ||
{ | ||
return m_openSignal; | ||
} | ||
|
||
private: | ||
void onBusAcquired(const Glib::RefPtr<Gio::DBus::Connection>& connection, const Glib::ustring& name); | ||
void onNameAcquired(const Glib::RefPtr<Gio::DBus::Connection>& connection, const Glib::ustring& name); | ||
void onNameLost(const Glib::RefPtr<Gio::DBus::Connection>& connection, const Glib::ustring& name); | ||
|
||
void onMethodCall(const Glib::RefPtr<Gio::DBus::Connection>& connection, const Glib::ustring& sender, const Glib::ustring& objectPath, | ||
const Glib::ustring& interfaceName, const Glib::ustring& methodName, const Glib::VariantContainerBase& parameters, | ||
const Glib::RefPtr<Gio::DBus::MethodInvocation>& invocation); | ||
|
||
private: | ||
OpenSignalSignature m_openSignal; | ||
|
||
Gio::DBus::InterfaceVTable m_interfaceVtable; | ||
Glib::RefPtr<Gio::DBus::NodeInfo> m_introspectionData; | ||
|
||
guint m_connectionId; | ||
}; |
Oops, something went wrong.