From f6ce4fb8db51489f6ab1210593695f465ccb83a0 Mon Sep 17 00:00:00 2001 From: Colin Leroy Date: Thu, 26 Oct 2017 22:20:36 +0200 Subject: [PATCH] Implement single-launch with Gtk::Application This introduces a new file for clarity. Options handling changes so that --tab changes the tab if the window is already opened. Other options are only used at start time. --- po/POTFILES.in | 1 + src/Makefile.am | 1 + src/mainwindow.cc | 16 ++++ src/mainwindow.h | 3 + src/pavuapplication.cc | 181 +++++++++++++++++++++++++++++++++++++++++ src/pavuapplication.h | 55 +++++++++++++ src/pavucontrol.cc | 102 ++++------------------- src/pavucontrol.h | 2 + 8 files changed, 274 insertions(+), 87 deletions(-) create mode 100644 src/pavuapplication.cc create mode 100644 src/pavuapplication.h diff --git a/po/POTFILES.in b/po/POTFILES.in index c952a83..e9d0f55 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -12,3 +12,4 @@ src/sinkwidget.cc src/sourceoutputwidget.cc src/sourcewidget.cc src/streamwidget.cc +src/pavuapplication.cc diff --git a/src/Makefile.am b/src/Makefile.am index 7257260..b5c0314 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -37,6 +37,7 @@ pavucontrol_SOURCES= \ rolewidget.h rolewidget.cc \ mainwindow.h mainwindow.cc \ pavucontrol.h pavucontrol.cc \ + pavuapplication.cc pavuapplication.h \ i18n.h pavucontrol_LDADD=$(AM_LDADD) $(GUILIBS_LIBS) $(PULSE_LIBS) diff --git a/src/mainwindow.cc b/src/mainwindow.cc index 31d5695..da598fd 100644 --- a/src/mainwindow.cc +++ b/src/mainwindow.cc @@ -340,6 +340,22 @@ static void updatePorts(DeviceWidget *w, std::map &port } } +void MainWindow::selectBestTab() { + if (sinkInputWidgets.size() > 0) + notebook->set_current_page(0); + else if (sourceOutputWidgets.size() > 0) + notebook->set_current_page(1); + else if (sourceWidgets.size() > 0 && sinkWidgets.size() == 0) + notebook->set_current_page(3); + else + notebook->set_current_page(2); +} + +void MainWindow::selectTab(int tab_number) { + if (tab_number > 0 && tab_number <= notebook->get_n_pages()) + notebook->set_current_page(tab_number - 1); +} + void MainWindow::updateCard(const pa_card_info &info) { CardWidget *w; bool is_new = false; diff --git a/src/mainwindow.h b/src/mainwindow.h index 30e1ad0..f45bf9a 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -60,6 +60,9 @@ public: void removeSourceOutput(uint32_t index); void removeClient(uint32_t index); + void selectBestTab(); + void selectTab(int tab_number); + void removeAllWidgets(); void setConnectingMessage(const char *string = NULL); diff --git a/src/pavuapplication.cc b/src/pavuapplication.cc new file mode 100644 index 0000000..3fc874f --- /dev/null +++ b/src/pavuapplication.cc @@ -0,0 +1,181 @@ +/*** + This file is part of pavucontrol. + + Copyright 2006-2008 Lennart Poettering + Copyright 2008 Sjoerd Simons + + pavucontrol 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. + + pavucontrol 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 pavucontrol. If not, see . +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "i18n.h" + +#include + +#include "pavuapplication.h" +#include "pavucontrol.h" +#include "mainwindow.h" + +PavuApplication::PavuApplication() : Gtk::Application("org.pulseaudio.pavucontrol", Gio::ApplicationFlags::APPLICATION_HANDLES_COMMAND_LINE) { +} + +Glib::RefPtr PavuApplication::create() { + return Glib::RefPtr(new PavuApplication()); + +} + +/* + * Create the main window and connect its "on_hide_window" signal to our cleanup + * function + */ +MainWindow* PavuApplication::create_window() +{ + m = pa_glib_mainloop_new(g_main_context_default()); + g_assert(m); + + MainWindow* pavucontrol_window = (MainWindow *)pavucontrol_get_window(m, maximize, retry, tab); + + pavucontrol_window->signal_hide().connect( + sigc::bind(sigc::mem_fun(*this, + &PavuApplication::on_hide_window), pavucontrol_window)); + + return pavucontrol_window; +} + +/* "on_activate" signal handler + * This is fired via the Gtk::Application framework either directly from the + * run() function, or manually from the on_command_line signal handler. + * This is always executed in the first-running process. + */ +void PavuApplication::on_activate() +{ + /* See if we are already running */ + if (mainWindow == NULL) { + /* We aren't. Create the main window */ + mainWindow = create_window(); + + /* and register it in the Gtk::Application */ + add_window(*mainWindow); + } else if (tab != -1) { + /* We are, and a specific tab has been requested. Select it. */ + mainWindow->selectTab(tab); + } + + /* Present the main window. */ + mainWindow->present(); +} + +/* "on_hide_window" signal handler + * This is executed in the first-running process and performs cleanup before + * exiting : when the last registered window of Gtk::Application is closed, + * the application's run() function returns. + */ +void PavuApplication::on_hide_window(Gtk::Window* window) +{ + delete window; + mainWindow = NULL; + + if (get_context()) { + pa_context_unref(get_context()); + } + pa_glib_mainloop_free(m); + m = NULL; +} + +template +static bool get_arg_value(const Glib::RefPtr& options, const Glib::ustring& arg_name, T_ArgType& arg_value) +{ + arg_value = T_ArgType(); + if (options->lookup_value(arg_name, arg_value)) { + return true; + } + + return false; +} + +/* + * "on_command_line" signal handler + * This is executed in the first-running process. + */ +int on_command_line(const Glib::RefPtr& command_line, + Glib::RefPtr& app) +{ + const auto options = command_line->get_options_dict(); + + get_arg_value(options, "tab", app->tab); + get_arg_value(options, "retry", app->retry); + get_arg_value(options, "maximize", app->maximize); + get_arg_value(options, "version", app->version); + + if (app->version) { + printf("%s\n", PACKAGE_STRING); + return 0; + } + + app->activate(); + + return 0; +} + +int main(int argc, char *argv[]) { + + /* Initialize the i18n stuff */ + bindtextdomain(GETTEXT_PACKAGE, LOCALEDIR); + bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8"); + textdomain(GETTEXT_PACKAGE); + + signal(SIGPIPE, SIG_IGN); + + /* Create the application */ + auto app = PavuApplication::create(); + + /* Add command-line options */ + app->add_main_option_entry( + Gio::Application::OptionType::OPTION_TYPE_INT, + "tab", 't', + _("Select a specific tab on load."), + _("number")); + + app->add_main_option_entry( + Gio::Application::OptionType::OPTION_TYPE_BOOL, + "retry", 'r', + _("Retry forever if pa quits (every 5 seconds).")); + + app->add_main_option_entry( + Gio::Application::OptionType::OPTION_TYPE_BOOL, + "maximize", 'm', + _("Maximize the window.")); + + app->add_main_option_entry( + Gio::Application::OptionType::OPTION_TYPE_BOOL, + "version", 'v', + _("Show version.")); + + /* Connect to the "on_command_line" signal which is fired + * when the application receives command-line arguments + */ + app->signal_command_line().connect(sigc::bind(sigc::ptr_fun(&on_command_line), app), false); + + /* Run the application. + * In the first launched instance, this will return when its window is + * closed. In subsequently launches instances, this will only signal the + * first instance to handle a new request, and exit immediately. + * Handling a new request consists of presenting the existing window (and + * optionally, select a tab). + */ + return app->run(argc, argv); +} diff --git a/src/pavuapplication.h b/src/pavuapplication.h new file mode 100644 index 0000000..8ff3f35 --- /dev/null +++ b/src/pavuapplication.h @@ -0,0 +1,55 @@ +/*** + This file is part of pavucontrol. + + Copyright 2006-2008 Lennart Poettering + Copyright 2009 Colin Guthrie + + pavucontrol 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. + + pavucontrol 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 pavucontrol. If not, see . +***/ + +#ifndef pavuapplication_h +#define pavuapplication_h + +#include "pavucontrol.h" +#include "mainwindow.h" + +class PavuApplication : public Gtk::Application { +protected: + PavuApplication(); + +public: + static Glib::RefPtr create(); + + /* Main window */ + MainWindow *mainWindow; + + /* options */ + bool retry; + bool maximize; + gint32 tab; + bool version; + +protected: + // Override default signal handlers: + void on_activate() override; + +private: + MainWindow* create_window(); + void on_hide_window(Gtk::Window* window); + + pa_glib_mainloop *m = NULL; +}; + + +#endif diff --git a/src/pavucontrol.cc b/src/pavucontrol.cc index 7a95c3e..fc7499c 100644 --- a/src/pavucontrol.cc +++ b/src/pavucontrol.cc @@ -23,7 +23,6 @@ #endif #include -#include #include #include @@ -45,7 +44,7 @@ static pa_context* context = NULL; static pa_mainloop_api* api = NULL; static int n_outstanding = 0; -static int default_tab = 0; +static int tab_number = 0; static bool retry = false; static int reconnect_timeout = 1; @@ -171,20 +170,10 @@ void source_output_cb(pa_context *, const pa_source_output_info *i, int eol, voi if (n_outstanding > 0) { /* At this point all notebook pages have been populated, so * let's open one that isn't empty */ - if (default_tab != -1) { - if (default_tab < 1 || default_tab > w->notebook->get_n_pages()) { - if (w->sinkInputWidgets.size() > 0) - w->notebook->set_current_page(0); - else if (w->sourceOutputWidgets.size() > 0) - w->notebook->set_current_page(1); - else if (w->sourceWidgets.size() > 0 && w->sinkWidgets.size() == 0) - w->notebook->set_current_page(3); - else - w->notebook->set_current_page(2); - } else { - w->notebook->set_current_page(default_tab - 1); - } - default_tab = -1; + if (tab_number != 0) { + w->selectTab(tab_number); + } else { + w->selectBestTab(); } } @@ -636,82 +625,21 @@ gboolean connect_to_pulse(gpointer userdata) { return false; } -int main(int argc, char *argv[]) { +Gtk::Window* pavucontrol_get_window(pa_glib_mainloop *m, bool maximize, bool _retry, int _tab_number) { - /* Initialize the i18n stuff */ - bindtextdomain(GETTEXT_PACKAGE, LOCALEDIR); - bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8"); - textdomain(GETTEXT_PACKAGE); + MainWindow* mainWindow = NULL; - signal(SIGPIPE, SIG_IGN); + tab_number = _tab_number; + retry = _retry; + ca_context_set_driver(ca_gtk_context_get(), "pulse"); - Glib::OptionContext options; - options.set_summary("PulseAudio Volume Control"); - options.set_help_enabled(); + mainWindow = MainWindow::create(maximize); - Glib::OptionGroup group("pulseaudio", "PAVUControl"); + api = pa_glib_mainloop_get_api(m); + g_assert(api); - Glib::OptionEntry entry; - entry.set_long_name("tab"); - entry.set_short_name('t'); - entry.set_description(_("Select a specific tab on load.")); - group.add_entry(entry, default_tab); + connect_to_pulse(mainWindow); - Glib::OptionEntry entry2; - entry2.set_long_name("retry"); - entry2.set_short_name('r'); - entry2.set_description(_("Retry forever if pa quits (every 5 seconds).")); - group.add_entry(entry2, retry); - - bool maximize = false; - Glib::OptionEntry entry3; - entry3.set_long_name("maximize"); - entry3.set_short_name('m'); - entry3.set_description(_("Maximize the window.")); - group.add_entry(entry3, maximize); - - bool version = false; - Glib::OptionEntry entry4; - entry4.set_long_name("version"); - entry4.set_description(_("Show version")); - group.add_entry(entry4, version); - - options.set_main_group(group); - - try { - Gtk::Main kit(argc, argv, options); - - if (version) { - printf("%s\n", PACKAGE_STRING); - return 0; - } - - ca_context_set_driver(ca_gtk_context_get(), "pulse"); - - MainWindow* mainWindow = MainWindow::create(maximize); - - pa_glib_mainloop *m = pa_glib_mainloop_new(g_main_context_default()); - g_assert(m); - api = pa_glib_mainloop_get_api(m); - g_assert(api); - - connect_to_pulse(mainWindow); - if (reconnect_timeout >= 0) - Gtk::Main::run(*mainWindow); - - if (reconnect_timeout < 0) - show_error(_("Fatal Error: Unable to connect to PulseAudio")); - - delete mainWindow; - - if (context) - pa_context_unref(context); - pa_glib_mainloop_free(m); - } catch ( const Glib::OptionError & e ) { - fprintf(stderr, "%s", options.get_help().c_str()); - return 1; - } - - return 0; + return mainWindow; } diff --git a/src/pavucontrol.h b/src/pavucontrol.h index 65cb913..31fee6b 100644 --- a/src/pavucontrol.h +++ b/src/pavucontrol.h @@ -29,6 +29,7 @@ #include #include +#include #ifndef GLADE_FILE #define GLADE_FILE "pavucontrol.glade" @@ -71,4 +72,5 @@ enum SourceType { pa_context* get_context(void); void show_error(const char *txt); +Gtk::Window* pavucontrol_get_window(pa_glib_mainloop *m, bool maximize, bool retry, int tab_number); #endif