summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Davis <paul@linuxaudiosystems.com>2019-10-24 21:08:24 -0600
committerPaul Davis <paul@linuxaudiosystems.com>2019-10-24 21:32:46 -0600
commitfc5076f926736354e2aaa098283b68a0e58a98eb (patch)
tree4a51e7d1f5f782241bbfe7184875ec65f8cbafe3
parentf40b859ff79d93a1d41ef31ec075a69aba39ca4e (diff)
refactor plugin scan dialog into its own object
-rw-r--r--gtk2_ardour/ardour_ui_plugins.cc156
-rw-r--r--gtk2_ardour/plugin_scan_dialog.cc208
-rw-r--r--gtk2_ardour/plugin_scan_dialog.h52
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__ */