diff options
author | Paul Davis <paul@linuxaudiosystems.com> | 2019-10-24 21:08:24 -0600 |
---|---|---|
committer | Paul Davis <paul@linuxaudiosystems.com> | 2019-10-24 21:32:46 -0600 |
commit | fc5076f926736354e2aaa098283b68a0e58a98eb (patch) | |
tree | 4a51e7d1f5f782241bbfe7184875ec65f8cbafe3 | |
parent | f40b859ff79d93a1d41ef31ec075a69aba39ca4e (diff) |
refactor plugin scan dialog into its own object
-rw-r--r-- | gtk2_ardour/ardour_ui_plugins.cc | 156 | ||||
-rw-r--r-- | gtk2_ardour/plugin_scan_dialog.cc | 208 | ||||
-rw-r--r-- | gtk2_ardour/plugin_scan_dialog.h | 52 |
3 files changed, 260 insertions, 156 deletions
diff --git a/gtk2_ardour/ardour_ui_plugins.cc b/gtk2_ardour/ardour_ui_plugins.cc deleted file mode 100644 index 002ec535c9..0000000000 --- a/gtk2_ardour/ardour_ui_plugins.cc +++ /dev/null @@ -1,156 +0,0 @@ -/* - * Copyright (C) 2005-2007 Doug McLain <doug@nostar.net> - * Copyright (C) 2005-2017 Tim Mayberry <mojofunk@gmail.com> - * Copyright (C) 2005-2019 Paul Davis <paul@linuxaudiosystems.com> - * Copyright (C) 2005 Karsten Wiese <fzuuzf@googlemail.com> - * Copyright (C) 2005 Taybin Rutkin <taybin@taybin.com> - * Copyright (C) 2006-2015 David Robillard <d@drobilla.net> - * Copyright (C) 2007-2012 Carl Hetherington <carl@carlh.net> - * Copyright (C) 2008-2010 Sakari Bergen <sakari.bergen@beatwaves.net> - * Copyright (C) 2012-2019 Robin Gareus <robin@gareus.org> - * Copyright (C) 2013-2015 Colin Fletcher <colin.m.fletcher@googlemail.com> - * Copyright (C) 2013-2016 John Emmas <john@creativepost.co.uk> - * Copyright (C) 2013-2016 Nick Mainsbridge <mainsbridge@gmail.com> - * Copyright (C) 2014-2018 Ben Loftis <ben@harrisonconsoles.com> - * Copyright (C) 2015 André Nusser <andre.nusser@googlemail.com> - * Copyright (C) 2016-2018 Len Ovens <len@ovenwerks.net> - * Copyright (C) 2017 Johannes Mueller <github@johannes-mueller.org> - * - * 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, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#ifdef WAF_BUILD -#include "gtk2ardour-config.h" -#include "gtk2ardour-version.h" -#endif - -#include <gtkmm/progressbar.h> - -#include "ardour/plugin_manager.h" - -#include "ardour_ui.h" -#include "ui_config.h" - -#include "pbd/i18n.h" - -using namespace ARDOUR; -using namespace PBD; -using namespace Gtk; -using namespace std; - -/* TODO: this is getting elaborate enough to warrant being split into a dedicated class */ -static MessageDialog *scan_dlg = NULL; -static ProgressBar *scan_pbar = NULL; -static HBox *scan_tbox = NULL; -static Gtk::Button *scan_timeout_button; - -void -ARDOUR_UI::cancel_plugin_scan () -{ - PluginManager::instance().cancel_plugin_scan(); -} - -void -ARDOUR_UI::cancel_plugin_timeout () -{ - PluginManager::instance().cancel_plugin_timeout(); - scan_timeout_button->set_sensitive (false); -} - -void -ARDOUR_UI::plugin_scan_timeout (int timeout) -{ - if (!scan_dlg || !scan_dlg->is_mapped() || !scan_pbar) { - return; - } - if (timeout > 0) { - scan_pbar->set_sensitive (false); - scan_timeout_button->set_sensitive (true); - scan_pbar->set_fraction ((float) timeout / (float) Config->get_vst_scan_timeout()); - scan_tbox->show(); - } else { - scan_pbar->set_sensitive (false); - scan_timeout_button->set_sensitive (false); - } - gui_idle_handler(); -} - -void -ARDOUR_UI::plugin_scan_dialog (std::string type, std::string plugin, bool can_cancel) -{ - if (type == X_("closeme") && !(scan_dlg && scan_dlg->is_mapped())) { - return; - } - - const bool cancelled = PluginManager::instance().cancelled(); - if (type != X_("closeme") && (!UIConfiguration::instance().get_show_plugin_scan_window()) && !_initial_verbose_plugin_scan) { - if (cancelled && scan_dlg->is_mapped()) { - scan_dlg->hide(); - gui_idle_handler(); - return; - } - if (cancelled || !can_cancel) { - return; - } - } - - static Gtk::Button *cancel_button; - if (!scan_dlg) { - scan_dlg = new MessageDialog("", false, MESSAGE_INFO, BUTTONS_NONE); // TODO manage - VBox* vbox = scan_dlg->get_vbox(); - vbox->set_size_request(400,-1); - scan_dlg->set_title (_("Scanning for plugins")); - - cancel_button = manage(new Gtk::Button(_("Cancel plugin scan"))); - cancel_button->set_name ("EditorGTKButton"); - cancel_button->signal_clicked().connect ( mem_fun (*this, &ARDOUR_UI::cancel_plugin_scan) ); - cancel_button->show(); - - scan_dlg->get_vbox()->pack_start ( *cancel_button, PACK_SHRINK); - - scan_tbox = manage( new HBox() ); - - scan_timeout_button = manage(new Gtk::Button(_("Stop Timeout"))); - scan_timeout_button->set_name ("EditorGTKButton"); - scan_timeout_button->signal_clicked().connect ( mem_fun (*this, &ARDOUR_UI::cancel_plugin_timeout) ); - scan_timeout_button->show(); - - scan_pbar = manage(new ProgressBar()); - scan_pbar->set_orientation(Gtk::PROGRESS_RIGHT_TO_LEFT); - scan_pbar->set_text(_("Scan Timeout")); - scan_pbar->show(); - - scan_tbox->pack_start (*scan_pbar, PACK_EXPAND_WIDGET, 4); - scan_tbox->pack_start (*scan_timeout_button, PACK_SHRINK, 4); - - scan_dlg->get_vbox()->pack_start (*scan_tbox, PACK_SHRINK, 4); - } - - assert(scan_dlg && scan_tbox && cancel_button); - - if (type == X_("closeme")) { - scan_tbox->hide(); - scan_dlg->hide(); - } else { - scan_dlg->set_message(type + ": " + Glib::path_get_basename(plugin)); - scan_dlg->show(); - } - if (!can_cancel || !cancelled) { - scan_timeout_button->set_sensitive(false); - } - cancel_button->set_sensitive(can_cancel && !cancelled); - - gui_idle_handler(); -} diff --git a/gtk2_ardour/plugin_scan_dialog.cc b/gtk2_ardour/plugin_scan_dialog.cc new file mode 100644 index 0000000000..bddc5e5d91 --- /dev/null +++ b/gtk2_ardour/plugin_scan_dialog.cc @@ -0,0 +1,208 @@ +/* + * Copyright (C) 2005-2019 Paul Davis <paul@linuxaudiosystems.com> + * Copyright (C) 2012-2019 Robin Gareus <robin@gareus.org> + * + * 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, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifdef WAF_BUILD +#include "gtk2ardour-config.h" +#include "gtk2ardour-version.h" +#endif + +#include <gtkmm/progressbar.h> + +#include "ardour/plugin_manager.h" + +#include "ardour_ui.h" +#include "debug.h" +#include "gui_thread.h" +#include "plugin_scan_dialog.h" +#include "ui_config.h" + +#include "pbd/i18n.h" + +using namespace ARDOUR; +using namespace PBD; +using namespace Gtk; +using namespace std; + +PluginScanDialog::PluginScanDialog (bool just_cached, bool v) + : ArdourDialog (_("Scanning for plugins")) + , timeout_button (_("Stop Timeout")) + , cancel_button (_("Cancel Plugin Scan")) + , cache_only (just_cached) + , verbose (v) +{ + VBox* vbox = get_vbox(); + vbox->set_size_request(400,-1); + + message.set_padding (12, 12); + vbox->pack_start (message); + + cancel_button.set_name ("EditorGTKButton"); + cancel_button.signal_clicked().connect (sigc::mem_fun (*this, &PluginScanDialog::cancel_plugin_scan)); + cancel_button.show(); + + vbox->pack_start (cancel_button, PACK_SHRINK); + + timeout_button.set_name ("EditorGTKButton"); + timeout_button.signal_clicked().connect (sigc::mem_fun (*this, &PluginScanDialog::cancel_plugin_timeout)); + timeout_button.show(); + + pbar.set_orientation(Gtk::PROGRESS_RIGHT_TO_LEFT); + pbar.set_text(_("Scan Timeout")); + pbar.show(); + + tbox.pack_start (pbar, PACK_EXPAND_WIDGET, 4); + tbox.pack_start (timeout_button, PACK_SHRINK, 4); + + vbox->pack_start (tbox, PACK_SHRINK, 4); + + ARDOUR::PluginScanMessage.connect (connections, MISSING_INVALIDATOR, boost::bind(&PluginScanDialog::message_handler, this, _1, _2, _3), gui_context()); + ARDOUR::PluginScanTimeout.connect (connections, MISSING_INVALIDATOR, boost::bind(&PluginScanDialog::plugin_scan_timeout, this, _1), gui_context()); + + vbox->show_all (); +} + +void +PluginScanDialog::start () +{ + /* OK, this is extremely hard to understand on first reading, so please + * read this and think about it carefully if you are confused. + * + * Plugin discovery must take place in the main thread of the + * process. This is not true for all plugin APIs but it is true for VST + * and AU. This means that the PluginManager::refresh() call MUST be + * made from the main thread (typically the GUI thread, but certainly + * the thread running main()). Failure to do this will cause crashes, + * undefined behavior and other undesirable stuff (because plugin APIs + * failed to specify this aspect of the host behavior). + * + * The ::refresh call is likely to be slow, particularly in the case of + * VST(2) plugins where we are forced to load the shared object do + * discovery (there is no separate metadata as with LV2 for + * example). This means that it will block the GUI event loop where we + * are calling it from. This is a problem. + * + * Normally we would solve this by running it in a separate thread, but + * we cannot do this for reasons described above regarding plugin + * discovery. + * + * We "solve" this by making the PluginManager emit a signal as it + * examines every new plugin. Our handler for this signal checks the + * message, and then runs ARDOUR_UI::gui_idle_handler() which flushes + * the GUI event loop of pending events. This effectively handles + * redraws and event input and all the usual stuff, meaning that the + * GUI event loop appears to continue running during the ::refresh() + * call. In reality, it only runs at the start of each plugin + * discovery, so if the discovery process for a particular plugin takes + * a long time (e.g. because it displays a licensing window and sits + * waiting for input from the user), there's nothing we can do - + * control will not be returned to our GUI event loop until that is + * finished. + * + * This is a horrible design. Truly, really horrible. But it is caused + * by plugin APIs failing to mandate that discovery can happen from any + * thread and that plugins should NOT display a GUI or interact with + * the user during discovery/instantiation. Fundamentally, all plugin + * APIs should allow discovery without instantiation, like LV2 does + * (and to a very limited extent like AU does, if you play some games + * with the lower level APIs). + * + * For now (October 2019) it is the best we can come up with that does + * not break when some VST plugin decides to behave stupidly. + */ + + + DEBUG_TRACE (DEBUG::GuiStartup, "plugin refresh starting\n"); + PluginManager::instance().refresh (cache_only); + DEBUG_TRACE (DEBUG::GuiStartup, "plugin refresh complete\n"); + + /* scan is done at this point, return full control to main event loop */ +} + +void +PluginScanDialog::cancel_plugin_scan () +{ + PluginManager::instance().cancel_plugin_scan(); +} + +void +PluginScanDialog::cancel_plugin_timeout () +{ + PluginManager::instance().cancel_plugin_timeout(); + timeout_button.set_sensitive (false); +} + +void +PluginScanDialog::plugin_scan_timeout (int timeout) +{ + if (!is_mapped()) { + return; + } + + if (timeout > 0) { + pbar.set_sensitive (false); + timeout_button.set_sensitive (true); + pbar.set_fraction ((float) timeout / (float) Config->get_vst_scan_timeout()); + tbox.show(); + } else { + pbar.set_sensitive (false); + timeout_button.set_sensitive (false); + } + + ARDOUR_UI::instance()->gui_idle_handler (); +} + +void +PluginScanDialog::message_handler (std::string type, std::string plugin, bool can_cancel) +{ + if (type == X_("closeme") && !is_mapped()) { + return; + } + + const bool cancelled = PluginManager::instance().cancelled(); + + if (type != X_("closeme") && (!UIConfiguration::instance().get_show_plugin_scan_window()) && !verbose) { + + if (cancelled && is_mapped()) { + hide(); + connections.drop_connections(); + ARDOUR_UI::instance()->gui_idle_handler (); + return; + } + if (cancelled || !can_cancel) { + return; + } + } + + if (type == X_("closeme")) { + tbox.hide(); + hide(); + connections.drop_connections (); + } else { + message.set_text (type + ": " + Glib::path_get_basename(plugin)); + show(); + } + + if (!can_cancel || !cancelled) { + timeout_button.set_sensitive(false); + } + + cancel_button.set_sensitive(can_cancel && !cancelled); + + ARDOUR_UI::instance()->gui_idle_handler (); +} diff --git a/gtk2_ardour/plugin_scan_dialog.h b/gtk2_ardour/plugin_scan_dialog.h new file mode 100644 index 0000000000..90865eff38 --- /dev/null +++ b/gtk2_ardour/plugin_scan_dialog.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2005-2019 Paul Davis <paul@linuxaudiosystems.com> + * + * 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, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef __gtk2_ardour_plugin_scan_dialog_h__ +#define __gtk2_ardour_plugin_scan_dialog_h__ + +#include <gtkmm/label.h> +#include <gtkmm/box.h> +#include <gtkmm/button.h> +#include <gtkmm/progressbar.h> + +#include "ardour_dialog.h" + +class PluginScanDialog : public ArdourDialog +{ + public: + PluginScanDialog (bool cache_only, bool verbose); + void start (); + + private: + Gtk::Label message; + Gtk::ProgressBar pbar; + Gtk::HBox tbox; + Gtk::Button timeout_button; + Gtk::Button cancel_button; + bool cache_only; + bool verbose; + + void cancel_plugin_scan (); + void cancel_plugin_timeout (); + void plugin_scan_timeout (int timeout); + void message_handler (std::string type, std::string plugin, bool can_cancel); + + PBD::ScopedConnectionList connections; +}; + +#endif /* __gtk2_ardour_plugin_scan_dialog_h__ */ |