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 /gtk2_ardour/plugin_scan_dialog.cc | |
parent | f40b859ff79d93a1d41ef31ec075a69aba39ca4e (diff) |
refactor plugin scan dialog into its own object
Diffstat (limited to 'gtk2_ardour/plugin_scan_dialog.cc')
-rw-r--r-- | gtk2_ardour/plugin_scan_dialog.cc | 208 |
1 files changed, 208 insertions, 0 deletions
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 (); +} |