summaryrefslogtreecommitdiff
path: root/gtk2_ardour
diff options
context:
space:
mode:
authorPaul Davis <paul@linuxaudiosystems.com>2019-10-24 21:14:00 -0600
committerPaul Davis <paul@linuxaudiosystems.com>2019-10-24 21:32:46 -0600
commit48161d7f517992522af5d9179abdaf926cc75a22 (patch)
tree223fb02859ad573f9f24748ec28b63bc179220e5 /gtk2_ardour
parenteeb57bfef18683480fa57c3c5c33f01a1f772358 (diff)
somewhat substantial changes to StartupFSM to try to get relationships betweem various dialogs, plugin scanning and splash screen correct
Added extensive comments to try to explain the nightmare of plugin discovery
Diffstat (limited to 'gtk2_ardour')
-rw-r--r--gtk2_ardour/startup_fsm.cc324
-rw-r--r--gtk2_ardour/startup_fsm.h40
2 files changed, 236 insertions, 128 deletions
diff --git a/gtk2_ardour/startup_fsm.cc b/gtk2_ardour/startup_fsm.cc
index 596ed82c0d..d1951ec295 100644
--- a/gtk2_ardour/startup_fsm.cc
+++ b/gtk2_ardour/startup_fsm.cc
@@ -43,10 +43,13 @@
#include <gtkmm2ext/doi.h>
#include "ardour_ui.h"
+#include "debug.h"
#include "engine_dialog.h"
#include "new_user_wizard.h"
#include "opts.h"
+#include "plugin_scan_dialog.h"
#include "session_dialog.h"
+#include "splash.h"
#include "startup_fsm.h"
#ifdef WAF_BUILD
@@ -65,17 +68,25 @@ StartupFSM::StartupFSM (EngineControl& amd)
: session_existing_sample_rate (0)
, session_is_new (false)
, new_user (NewUserWizard::required())
- , new_session (true)
- , _state (new_user ? NeedWizard : NeedSessionPath)
- , new_user_wizard (0)
+ , new_session_required (ARDOUR_COMMAND_LINE::new_session || (!ARDOUR::Profile->get_mixbus() && new_user))
+ , _state (new_user ? WaitingForNewUser : WaitingForSessionPath)
, audiomidi_dialog (amd)
+ , new_user_dialog (0)
, session_dialog (0)
, pre_release_dialog (0)
+ , plugin_scan_dialog (0)
{
+ /* note that our initial state can be any of:
+ *
+ * WaitingForPreRelease: if this is a pre-release build of Ardour and the user has not testified to their fidelity to our creed
+ * WaitingForNewUser: if this is the first time any version appears to have been run on this machine by this user
+ * WaitingForSessionPath: if the previous two conditions are not true
+ */
+
if (string (VERSIONSTRING).find (".pre") != string::npos) {
string fn = Glib::build_filename (user_config_directory(), ".i_swear_that_i_will_heed_the_guidelines_stated_in_the_pre_release_dialog");
if (!Glib::file_test (fn, Glib::FILE_TEST_EXISTS)) {
- _state = NeedPreRelease;
+ set_state (WaitingForPreRelease);
}
}
@@ -83,17 +94,14 @@ StartupFSM::StartupFSM (EngineControl& amd)
app->ShouldQuit.connect (sigc::mem_fun (*this, &StartupFSM::queue_finish));
app->ShouldLoad.connect (sigc::mem_fun (*this, &StartupFSM::load_from_application_api));
-
- /* this may cause the delivery of ShouldLoad etc if we were invoked in
- * particular ways. It will happen when the event loop runs again.
- */
-
- app->ready ();
}
StartupFSM::~StartupFSM ()
{
delete session_dialog;
+ delete pre_release_dialog;
+ delete plugin_scan_dialog;
+ delete new_user_dialog;
}
void
@@ -105,116 +113,149 @@ StartupFSM::queue_finish ()
void
StartupFSM::start ()
{
- if (_state == NeedPreRelease) {
+ /* get the splash screen visible, if it isn't yet */
+ Splash::instance()->pop_front();
+ /* make it all happen on-screen */
+ ARDOUR_UI::instance()->flush_pending ();
+
+ DEBUG_TRACE (DEBUG::GuiStartup, string_compose (X_("State at startup: %1\n"), enum_2_string (_state)));
+
+ switch (_state) {
+ case WaitingForPreRelease:
show_pre_release_dialog ();
- } else if (new_user) {
- show_new_user_wizard ();
- } else {
- /* pretend we just showed the new user wizard and we're done
- with it
- */
- dialog_response_handler (RESPONSE_OK, NewUserDialog);
+ break;
+ case WaitingForNewUser:
+ show_new_user_dialog ();
+ break;
+ case WaitingForSessionPath:
+ if (ARDOUR_COMMAND_LINE::session_name.empty()) {
+
+ /* nothing given on the command line ... show new session dialog */
+
+ show_session_dialog (new_session_required);
+
+ } else {
+
+ if (get_session_parameters_from_command_line (new_session_required)) {
+
+ /* command line arguments all OK. Get engine parameters */
+
+ if (!new_session_required && session_existing_sample_rate > 0) {
+ audiomidi_dialog.set_desired_sample_rate (session_existing_sample_rate);
+ }
+
+ start_audio_midi_setup ();
+
+ } else {
+
+ /* command line arguments not good. Use
+ * dialog, but prime the dialog with
+ * the information we set up in
+ * get_session_parameters_from_command_line()
+ */
+
+ show_session_dialog (new_session_required);
+ }
+ }
+ break;
+ default:
+ fatal << string_compose (_("Programming error: %1"), string_compose (X_("impossible starting state in StartupFSM (%1)"), enum_2_string (_state))) << endmsg;
+ std::cerr << string_compose (_("Programming error: %1"), string_compose (X_("impossible starting state in StartupFSM (%1)"), enum_2_string (_state))) << std::endl;
+ /* NOTREACHED */
+ abort ();
}
+
+ DEBUG_TRACE (DEBUG::GuiStartup, string_compose (X_("State after startup: %1\n"), enum_2_string (_state)));
+
+ /* this may cause the delivery of ShouldLoad etc if we were invoked in
+ * particular ways. It will happen when the event loop runs again.
+ */
+
+ Application::instance()->ready ();
}
+void
+StartupFSM::reset()
+{
+ show_session_dialog (new_session_required);
+}
void
-StartupFSM::end()
+StartupFSM::set_state (MainState ms)
{
+ DEBUG_TRACE (DEBUG::GuiStartup, string_compose (X_("new state: %1\n"), enum_2_string (ms)));
+ _state = ms;
+}
+
+template<typename T> void
+StartupFSM::end_dialog (T** d)
+{
+ assert (d);
+ assert (*d);
+
+ end_dialog (**d);
+ delete_when_idle (*d);
+ *d = 0;
+}
+template<typename T> void
+StartupFSM::end_dialog (T& d)
+{
+ d.hide ();
+ current_dialog_connection.disconnect ();
}
void
StartupFSM::dialog_response_handler (int response, StartupFSM::DialogID dialog_id)
{
- const bool new_session_required = (ARDOUR_COMMAND_LINE::new_session || (!ARDOUR::Profile->get_mixbus() && new_user));
+ DEBUG_TRACE (DEBUG::GuiStartup, string_compose ("Response %1 from %2 (nsr: %3 / nu: %4)\n", enum_2_string (Gtk::ResponseType (response)), enum_2_string (dialog_id), new_session_required, new_user));
- restart:
+ /* Notes:
+ *
+ * 1) yes, a brand new user might have specified a command line
+ * argument naming a new session. We ignore it. You're new to Ardour?
+ * We want to guide you through the startup.
+ */
switch (_state) {
- case NeedPreRelease:
+ case WaitingForPreRelease:
switch (dialog_id) {
case PreReleaseDialog:
default:
/* any response value from the pre-release dialog means
"move along now"
*/
- delete_when_idle (pre_release_dialog);
- pre_release_dialog = 0;
- _state = NeedSessionPath;
+ Gtk::Widget* w = pre_release_dialog;
+ end_dialog (&w);
+
if (NewUserWizard::required()) {
- show_new_user_wizard ();
+ show_new_user_dialog ();
} else {
- /* act as if we had just finished with the new
- user wizard. goto preferred over reentrancy.
- */
- dialog_id = NewUserDialog;
- response = RESPONSE_OK;
- goto restart;
+ show_session_dialog (new_session_required);
}
break;
}
break;
- case NeedSessionPath:
+ case WaitingForNewUser:
switch (dialog_id) {
case NewUserDialog:
-
- current_dialog_connection.disconnect ();
- delete_when_idle (new_user_wizard);
-
switch (response) {
case RESPONSE_OK:
+ end_dialog (&new_user_dialog);
+ show_session_dialog (new_session_required);
break;
default:
- exit (1);
- }
-
- /* new user wizard done, now lets get session params
- * either from the command line (if given) or a dialog
- * (if nothing given on the command line or if the
- * command line arguments don't work for some reason
- */
-
- if (ARDOUR_COMMAND_LINE::session_name.empty()) {
-
- /* nothing given on the command line ... show new session dialog */
-
- session_path = string();
- session_name = string();
- session_template = string();
-
- _state = NeedSessionPath;
- show_session_dialog (new_session_required);
-
- } else {
-
- if (get_session_parameters_from_command_line (new_session_required)) {
-
- /* command line arguments all OK. Get engine parameters */
-
- _state = NeedEngineParams;
-
- if (!new_session_required && session_existing_sample_rate > 0) {
- audiomidi_dialog.set_desired_sample_rate (session_existing_sample_rate);
- }
-
- start_audio_midi_setup ();
-
- } else {
-
- /* command line arguments not good. Use
- * dialog, but prime the dialog with
- * the information we set up in
- * get_session_parameters_from_command_line()
- */
-
- _state = NeedSessionPath;
- show_session_dialog (new_session_required);
- }
+ _signal_response (ExitProgram);
}
+ default:
+ /* ERROR */
break;
+ }
+ break;
+ case WaitingForSessionPath:
+ switch (dialog_id) {
case NewSessionDialog:
switch (response) {
case RESPONSE_OK:
@@ -224,15 +265,16 @@ StartupFSM::dialog_response_handler (int response, StartupFSM::DialogID dialog_i
/* Unrecoverable error */
_signal_response (ExitProgram);
break;
+ case 0:
+ end_dialog (&session_dialog);
+ start_audio_midi_setup ();
+ break;
case 1:
/* do nothing - keep dialog up for a
* retry. Errors were addressed by
* ::check_session_parameters()
*/
break;
- case 0:
- start_audio_midi_setup ();
- break;
}
break;
@@ -248,16 +290,15 @@ StartupFSM::dialog_response_handler (int response, StartupFSM::DialogID dialog_i
}
break;
- case NeedEngineParams:
+ case WaitingForEngineParams:
switch (dialog_id) {
case AudioMIDISetup:
switch (response) {
case RESPONSE_OK:
case RESPONSE_ACCEPT:
if (AudioEngine::instance()->running()) {
- audiomidi_dialog.hide ();
- current_dialog_connection.disconnect();
- _signal_response (LoadSession);
+ end_dialog (audiomidi_dialog);
+ engine_running ();
} else {
/* just keep going */
}
@@ -272,25 +313,63 @@ StartupFSM::dialog_response_handler (int response, StartupFSM::DialogID dialog_i
}
break;
- case NeedWizard:
- show_new_user_wizard ();
- _state = NeedSessionPath;
- break;
+ case WaitingForPlugins:
+ switch (dialog_id) {
+ case PluginDialog:
+ end_dialog (&plugin_scan_dialog);
+ switch (response) {
+ case RESPONSE_OK:
+ _signal_response (LoadSession);
+ break;
+ default:
+ _signal_response (ExitProgram);
+ break;
+ }
+ default:
+ /* ERROR */
+ break;
+ }
}
}
void
-StartupFSM::show_new_user_wizard ()
+StartupFSM::show_plugin_scan_dialog ()
+{
+ set_state (WaitingForPlugins);
+
+ /* if the user does not ask to discover VSTs at startup, or if this is Mixbus, then the plugin scan
+ that we run here, during startup, should only use the existing plugin cache (if any).
+ */
+
+ plugin_scan_dialog = new PluginScanDialog ((!Config->get_discover_vst_on_start() || Profile->get_mixbus()), new_user);
+ current_dialog_connection = plugin_scan_dialog->signal_response().connect (sigc::bind (sigc::mem_fun (*this, &StartupFSM::dialog_response_handler), PluginDialog));
+ plugin_scan_dialog->set_position (WIN_POS_CENTER);
+
+ /* We don't show the plugin scan dialog by default. It will appear using it's own code if/when plugins are discovered, if required.
+ *
+ * See also comments in PluginScanDialog::start() to understand the absurd complexities behind this call.
+ */
+
+ DEBUG_TRACE (DEBUG::GuiStartup, string_compose ("starting plugin dialog, cache only ? %1\n", !Config->get_discover_vst_on_start()));
+ plugin_scan_dialog->start();
+ DEBUG_TRACE (DEBUG::GuiStartup, "plugin dialog done\n");
+}
+
+
+void
+StartupFSM::show_new_user_dialog ()
{
- new_user_wizard = new NewUserWizard;
- current_dialog_connection = new_user_wizard->signal_response().connect (sigc::bind (sigc::mem_fun (*this, &StartupFSM::dialog_response_handler), NewUserDialog));
- new_user_wizard->set_position (WIN_POS_CENTER);
- new_user_wizard->present ();
+ set_state (WaitingForNewUser);
+ new_user_dialog = new NewUserWizard;
+ current_dialog_connection = new_user_dialog->signal_response().connect (sigc::bind (sigc::mem_fun (*this, &StartupFSM::dialog_response_handler), NewUserDialog));
+ new_user_dialog->set_position (WIN_POS_CENTER);
+ new_user_dialog->present ();
}
void
StartupFSM::show_session_dialog (bool new_session_required)
{
+ set_state (WaitingForSessionPath);
session_dialog = new SessionDialog (new_session_required, session_name, session_path, session_template, false);
current_dialog_connection = session_dialog->signal_response().connect (sigc::bind (sigc::mem_fun (*this, &StartupFSM::dialog_response_handler), NewSessionDialog));
session_dialog->set_position (WIN_POS_CENTER);
@@ -300,6 +379,7 @@ StartupFSM::show_session_dialog (bool new_session_required)
void
StartupFSM::show_audiomidi_dialog ()
{
+ set_state (WaitingForEngineParams);
current_dialog_connection = audiomidi_dialog.signal_response().connect (sigc::bind (sigc::mem_fun (*this, &StartupFSM::dialog_response_handler), AudioMIDISetup));
audiomidi_dialog.set_position (WIN_POS_CENTER);
audiomidi_dialog.present ();
@@ -331,31 +411,43 @@ StartupFSM::start_audio_midi_setup ()
}
if (setup_required) {
- _state = NeedEngineParams;
- if (session_dialog) {
- session_dialog->hide ();
- delete_when_idle (session_dialog);
- session_dialog = 0;
- }
- current_dialog_connection.disconnect();
if (!session_is_new && session_existing_sample_rate > 0) {
audiomidi_dialog.set_desired_sample_rate (session_existing_sample_rate);
}
show_audiomidi_dialog ();
+ DEBUG_TRACE (DEBUG::GuiStartup, "audiomidi shown and waiting\n");
} else {
- /* XXX should we reset _state to something meaningul here (e.g. "Done")? */
-
- if (session_dialog) {
- session_dialog->hide ();
- delete_when_idle (session_dialog);
- session_dialog = 0;
- }
- current_dialog_connection.disconnect ();
- _signal_response (LoadSession);
+ DEBUG_TRACE (DEBUG::GuiStartup, "engine already running, audio/MIDI setup dialog not required\n");
+ engine_running ();
}
}
+void
+StartupFSM::engine_running ()
+{
+ DEBUG_TRACE (DEBUG::GuiStartup, "engine running, start plugin scan then attach UI to engine\n");
+
+ /* This may be very slow. See comments in PluginScanDialog::start() */
+ show_plugin_scan_dialog ();
+
+ DEBUG_TRACE (DEBUG::GuiStartup, "attach UI to engine\n");
+
+ /* This may be very slow: it will run the GUI's post-engine
+ initialization which is essentially unbounded in time/scope of what
+ it can do.
+ */
+
+ ARDOUR_UI::instance()->attach_to_engine ();
+
+ /* now that we've done the plugin scan AND attached the UI to the engine, we can
+ proceed with the next (final) steps of startup. This uses the same response
+ signal mechanism we use for the other dialogs.
+ */
+
+ plugin_scan_dialog->response (RESPONSE_OK);
+}
+
bool
StartupFSM::get_session_parameters_from_command_line (bool new_session_required)
{
@@ -739,7 +831,7 @@ StartupFSM::load_from_application_api (const std::string& path)
* main event loop is doing.
*/
- _state = NeedSessionPath;
+ set_state (WaitingForSessionPath);
}
bool
@@ -797,7 +889,7 @@ Full information on all the above can be found on the support page at\n\
"), PROGRAM_NAME, VERSIONSTRING));
- pre_release_dialog->signal_response().connect (sigc::bind (sigc::mem_fun (this, &StartupFSM::dialog_response_handler), PreReleaseDialog));
+ current_dialog_connection = pre_release_dialog->signal_response().connect (sigc::bind (sigc::mem_fun (this, &StartupFSM::dialog_response_handler), PreReleaseDialog));
pre_release_dialog->get_vbox()->set_border_width (12);
pre_release_dialog->get_vbox()->pack_start (*label, false, false, 12);
diff --git a/gtk2_ardour/startup_fsm.h b/gtk2_ardour/startup_fsm.h
index ed3fc6ca00..54576ea2e8 100644
--- a/gtk2_ardour/startup_fsm.h
+++ b/gtk2_ardour/startup_fsm.h
@@ -25,9 +25,11 @@
#include "ardour/types.h"
+class ArdourDialog;
class NewUserWizard;
class EngineControl;
class SessionDialog;
+class PluginScanDialog;
class StartupFSM : public sigc::trackable
{
@@ -36,7 +38,8 @@ class StartupFSM : public sigc::trackable
PreReleaseDialog,
NewUserDialog,
NewSessionDialog,
- AudioMIDISetup
+ AudioMIDISetup,
+ PluginDialog
};
enum Result {
@@ -44,11 +47,19 @@ class StartupFSM : public sigc::trackable
ExitProgram,
};
+ enum MainState {
+ WaitingForPreRelease,
+ WaitingForNewUser,
+ WaitingForSessionPath,
+ WaitingForEngineParams,
+ WaitingForPlugins
+ };
+
StartupFSM (EngineControl&);
~StartupFSM ();
void start ();
- void end ();
+ void reset ();
std::string session_path;
std::string session_name;
@@ -66,24 +77,21 @@ class StartupFSM : public sigc::trackable
bool brand_new_user() const { return new_user; }
private:
- enum MainState {
- NeedPreRelease,
- NeedWizard,
- NeedSessionPath,
- NeedEngineParams,
- };
-
bool new_user;
- bool new_session;
+ bool new_session_required;
MainState _state;
+ void set_state (MainState);
void dialog_response_handler (int response, DialogID);
+ template<typename T> void end_dialog (T**);
+ template<typename T> void end_dialog (T&);
- void show_new_user_wizard ();
+ void show_new_user_dialog ();
void show_session_dialog (bool new_session_required);
void show_audiomidi_dialog ();
void show_pre_release_dialog ();
+ void show_plugin_scan_dialog ();
void copy_demo_sessions ();
void load_from_application_api (std::string const &);
@@ -93,11 +101,19 @@ class StartupFSM : public sigc::trackable
bool ask_about_loading_existing_session (const std::string& session_path);
int check_session_parameters (bool must_be_new);
void start_audio_midi_setup ();
+ void engine_running ();
+
+ /* the Audio/MIDI dialog needs to be persistent and is thus owned by
+ * ARDOUR_UI and we use it by reference. All other dialogs can be
+ * created and destroyed within the scope of startup.
+ */
- NewUserWizard* new_user_wizard;
EngineControl& audiomidi_dialog;
+ NewUserWizard* new_user_dialog;
SessionDialog* session_dialog;
ArdourDialog* pre_release_dialog;
+ PluginScanDialog* plugin_scan_dialog;
+
sigc::connection current_dialog_connection;
sigc::signal1<void,Result> _signal_response;