1 /*
2  * Copyright (C) 2019 Paul Davis <paul@linuxaudiosystems.com>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License along
15  * with this program; if not, write to the Free Software Foundation, Inc.,
16  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17  */
18 
19 #include <vector>
20 
21 #include <gtkmm/dialog.h>
22 #include <gtkmm/liststore.h>
23 #include <gtkmm/messagedialog.h>
24 #include <gtkmm/stock.h>
25 
26 #include "pbd/basename.h"
27 #include "pbd/file_archive.h"
28 #include "pbd/file_utils.h"
29 
30 #include "ardour/audioengine.h"
31 #include "ardour/audio_backend.h"
32 #include "ardour/filename_extensions.h"
33 #include "ardour/filesystem_paths.h"
34 #include "ardour/profile.h"
35 #include "ardour/recent_sessions.h"
36 #include "ardour/rc_configuration.h"
37 #include "ardour/search_paths.h"
38 #include "ardour/session.h"
39 #include "ardour/session_utils.h"
40 #include "ardour/template_utils.h"
41 
42 #include "gtkmm2ext/application.h"
43 #include <gtkmm2ext/doi.h>
44 #include <gtkmm2ext/keyboard.h>
45 
46 #include "ardour_message.h"
47 #include "ardour_ui.h"
48 #include "debug.h"
49 #include "engine_dialog.h"
50 #include "new_user_wizard.h"
51 #include "opts.h"
52 #include "plugin_scan_dialog.h"
53 #include "session_dialog.h"
54 #include "splash.h"
55 #include "startup_fsm.h"
56 
57 #ifdef WAF_BUILD
58 #include "gtk2ardour-version.h"
59 #endif
60 
61 #include "pbd/i18n.h"
62 
63 using namespace ARDOUR;
64 using namespace Gtk;
65 using namespace Gtkmm2ext;
66 using namespace PBD;
67 
68 using std::string;
69 using std::vector;
70 
StartupFSM(EngineControl & amd)71 StartupFSM::StartupFSM (EngineControl& amd)
72 	: session_existing_sample_rate (0)
73 	, session_engine_hints ("EngineHints")
74 	, session_is_new (false)
75 	, session_name_edited (false)
76 	, new_user (NewUserWizard::required())
77 	, new_session_required (ARDOUR_COMMAND_LINE::new_session || (!ARDOUR::Profile->get_mixbus() && new_user))
78 	, _state (new_user ? WaitingForNewUser : WaitingForSessionPath)
79 	, audiomidi_dialog (amd)
80 	, new_user_dialog (0)
81 	, session_dialog (0)
82 	, pre_release_dialog (0)
83 	, plugin_scan_dialog (0)
84 {
85 	/* note that our initial state can be any of:
86 	 *
87 	 * WaitingForPreRelease:  if this is a pre-release build of Ardour and the user has not testified to their fidelity to our creed
88 	 * WaitingForNewUser:     if this is the first time any version appears to have been run on this machine by this user
89 	 * WaitingForSessionPath: if the previous two conditions are not true
90 	 */
91 
92 	if (string (VERSIONSTRING).find (".pre0") != string::npos) {
93 		string fn = Glib::build_filename (user_config_directory(), ".i_swear_that_i_will_heed_the_guidelines_stated_in_the_pre_release_dialog");
94 		if (!Glib::file_test (fn, Glib::FILE_TEST_EXISTS)) {
95 			set_state (WaitingForPreRelease);
96 		}
97 	}
98 
99 	Application* app = Application::instance ();
100 
101 	app->ShouldQuit.connect (sigc::mem_fun (*this, &StartupFSM::queue_finish));
102 
103 	Gtkmm2ext::Keyboard::HideMightMeanQuit.connect (sigc::mem_fun (*this, &StartupFSM::dialog_hidden));
104 }
105 
~StartupFSM()106 StartupFSM::~StartupFSM ()
107 {
108 	delete session_dialog;
109 	delete pre_release_dialog;
110 	delete plugin_scan_dialog;
111 	delete new_user_dialog;
112 }
113 
114 void
dialog_hidden(Gtk::Window *)115 StartupFSM::dialog_hidden (Gtk::Window* /* ignored */)
116 {
117 	/* since this object only exists during startup, any attempt to close
118 	 * any dialog that we manage with Ctrl/Cmd-w is assumed to indicate a
119 	 * desire to quit on the part of the user.
120 	 */
121 	queue_finish ();
122 }
123 
124 void
queue_finish()125 StartupFSM::queue_finish ()
126 {
127 	_signal_response (ExitProgram);
128 }
129 
130 void
start()131 StartupFSM::start ()
132 {
133 	DEBUG_TRACE (DEBUG::GuiStartup, string_compose (X_("State at startup: %1\n"), enum_2_string (_state)));
134 
135 	switch (_state) {
136 	case WaitingForPreRelease:
137 		show_pre_release_dialog ();
138 		break;
139 	case WaitingForNewUser:
140 		show_new_user_dialog ();
141 		break;
142 	case WaitingForSessionPath:
143 		handle_waiting_for_session_path ();
144 		break;
145 	case WaitingForEngineParams:
146 		start_audio_midi_setup ();
147 		break;
148 	default:
149 		fatal << string_compose (_("Programming error: %1"), string_compose (X_("impossible starting state in StartupFSM (%1)"), enum_2_string (_state))) << endmsg;
150 		std::cerr << string_compose (_("Programming error: %1"), string_compose (X_("impossible starting state in StartupFSM (%1)"), enum_2_string (_state))) << std::endl;
151 		/* NOTREACHED */
152 		abort ();
153 	}
154 
155 	DEBUG_TRACE (DEBUG::GuiStartup, string_compose (X_("State after startup: %1\n"), enum_2_string (_state)));
156 }
157 
158 void
reset()159 StartupFSM::reset()
160 {
161 	show_session_dialog (new_session_required);
162 }
163 
164 void
set_state(MainState ms)165 StartupFSM::set_state (MainState ms)
166 {
167 	DEBUG_TRACE (DEBUG::GuiStartup, string_compose (X_("new state: %1\n"), enum_2_string (ms)));
168 	_state = ms;
169 }
170 
171 template<typename T> void
end_dialog(T ** d)172 StartupFSM::end_dialog (T** d)
173 {
174 	assert (d);
175 	assert (*d);
176 
177 	end_dialog (**d);
178 	delete_when_idle (*d);
179 	*d = 0;
180 }
181 
182 template<typename T> void
end_dialog(T & d)183 StartupFSM::end_dialog (T& d)
184 {
185 	d.hide ();
186 	current_dialog_connection.disconnect ();
187 }
188 
189 void
dialog_response_handler(int response,StartupFSM::DialogID dialog_id)190 StartupFSM::dialog_response_handler (int response, StartupFSM::DialogID dialog_id)
191 {
192 	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));
193 
194 	/* Notes:
195 	 *
196 	 * 1) yes, a brand new user might have specified a command line
197 	 * argument naming a new session. We ignore it. You're new to Ardour?
198 	 * We want to guide you through the startup.
199 	 */
200 
201 	switch (_state) {
202 	case WaitingForPreRelease:
203 		switch (dialog_id) {
204 		case ApplicationPseudoDialog:
205 			/* this shouldn't happen; ignore it */
206 			break;
207 		case PreReleaseDialog:
208 		default:
209 			/* any response value from the pre-release dialog means
210 			   "move along now"
211 			*/
212 			end_dialog (&pre_release_dialog);
213 
214 			if (NewUserWizard::required()) {
215 				show_new_user_dialog ();
216 			} else {
217 				handle_waiting_for_session_path ();
218 			}
219 			break;
220 		}
221 		break;
222 
223 	case WaitingForNewUser:
224 		switch (dialog_id) {
225 		case ApplicationPseudoDialog:
226 			/* this shouldn't happen; ignore it */
227 			break;
228 		case NewUserDialog:
229 			switch (response) {
230 			case RESPONSE_OK:
231 				end_dialog (&new_user_dialog);
232 				show_session_dialog (new_session_required);
233 				break;
234 			default:
235 				_signal_response (ExitProgram);
236 			}
237 		default:
238 			/* ERROR */
239 			break;
240 		}
241 		break;
242 
243 	case WaitingForSessionPath:
244 		switch (dialog_id) {
245 		case NewSessionDialog:
246 			switch (response) {
247 			case RESPONSE_OK:
248 			case RESPONSE_ACCEPT:
249 				switch (check_session_parameters (new_session_required)) {
250 				case -1:
251 					/* Unrecoverable error */
252 					_signal_response (ExitProgram);
253 					break;
254 				case 0:
255 					end_dialog (&session_dialog);
256 					start_audio_midi_setup ();
257 					break;
258 				case 1:
259 					/* do nothing - keep dialog up for a
260 					 * retry. Errors were addressed by
261 					 * ::check_session_parameters()
262 					 */
263 					session_dialog->present ();
264 					break;
265 				}
266 				break;
267 
268 			default:
269 				_signal_response (ExitProgram);
270 				break;
271 			}
272 			break;
273 
274 		case ApplicationPseudoDialog:
275 			/* macOS, NSM etc. ... existence was already checked */
276 			if (get_session_parameters_from_path (ARDOUR_COMMAND_LINE::session_name, string(), false)) {
277 				start_audio_midi_setup ();
278 			}
279 			break;
280 
281 		default:
282 			/* ERROR */
283 			break;
284 		}
285 		break;
286 
287 	case WaitingForEngineParams:
288 		switch (dialog_id) {
289 		case AudioMIDISetup:
290 			switch (response) {
291 			case RESPONSE_OK:
292 			case RESPONSE_ACCEPT:
293 				if (AudioEngine::instance()->running()) {
294 					/* prevent double clicks from changing engine state */
295 					audiomidi_dialog.set_ui_sensitive (false);
296 					end_dialog (audiomidi_dialog);
297 					engine_running ();
298 				} else {
299 					/* just keep going */
300 				}
301 				break;
302 			default:
303 				_signal_response (ExitProgram);
304 			}
305 			break;
306 		default:
307 			/* ERROR */
308 			break;
309 		}
310 		break;
311 
312 	case WaitingForPlugins:
313 		switch (dialog_id) {
314 		case PluginDialog:
315 			end_dialog (&plugin_scan_dialog);
316 			switch (response) {
317 			case RESPONSE_OK:
318 				if (AudioEngine::instance()->running()) {
319 					_signal_response (LoadSession);
320 				} else {
321 					/* Engine died unexpectedly (it was
322 					 * running after
323 					 * WaitingForEngineParams).  Nothing to
324 					 * do but go back to the audio/MIDI
325 					 * setup. It would be nice, perhaps, to
326 					 * show an extra message indicating
327 					 * that something is not right.
328 					 */
329 					ArdourMessageDialog msg (_("The audio/MIDI engine has stopped running unexpectedly.\nSomething is probably wrong with your audio/MIDI device settings."));
330 					msg.set_position (WIN_POS_CENTER);
331 					msg.run();
332 					/* This has been shown before, so we do
333 					 *  not need start_audio_midi_setup ();
334 					 */
335 					show_audiomidi_dialog ();
336 				}
337 				break;
338 			default:
339 				_signal_response (ExitProgram);
340 				break;
341 			}
342 		default:
343 			/* ERROR */
344 			break;
345 		}
346 	}
347 }
348 
349 void
handle_waiting_for_session_path()350 StartupFSM::handle_waiting_for_session_path ()
351 {
352 	if (ARDOUR_COMMAND_LINE::session_name.empty()) {
353 
354 		/* nothing given on the command line ... show new session dialog */
355 
356 		show_session_dialog (new_session_required);
357 
358 	} else {
359 
360 		if (get_session_parameters_from_command_line (new_session_required)) {
361 
362 			/* command line arguments all OK. Get engine parameters */
363 
364 			if (!new_session_required && session_existing_sample_rate > 0) {
365 				audiomidi_dialog.set_desired_sample_rate (session_existing_sample_rate);
366 			}
367 
368 			start_audio_midi_setup ();
369 
370 		} else {
371 
372 			/* command line arguments not good. Use
373 			 * dialog, but prime the dialog with
374 			 * the information we set up in
375 			 * get_session_parameters_from_command_line()
376 			 */
377 
378 			show_session_dialog (new_session_required);
379 		}
380 	}
381 }
382 
383 void
show_plugin_scan_dialog()384 StartupFSM::show_plugin_scan_dialog ()
385 {
386 	set_state (WaitingForPlugins);
387 
388 	/* if the user does not ask to discover VSTs at startup, or if this is Mixbus, then the plugin scan
389 	   that we run here, during startup, should only use the existing plugin cache (if any).
390 	*/
391 
392 	const bool cache_only = (!Config->get_discover_vst_on_start() || Profile->get_mixbus());
393 	const bool verbose = new_user;
394 
395 	plugin_scan_dialog = new PluginScanDialog (cache_only, verbose);
396 	current_dialog_connection = plugin_scan_dialog->signal_response().connect (sigc::bind (sigc::mem_fun (*this, &StartupFSM::dialog_response_handler), PluginDialog));
397 	plugin_scan_dialog->set_position (WIN_POS_CENTER);
398 
399 	/* 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.
400 	 *
401 	 * See also comments in PluginScanDialog::start() to understand the absurd complexities behind this call.
402 	 */
403 
404 	DEBUG_TRACE (DEBUG::GuiStartup, string_compose ("starting plugin dialog, cache only ? %1\n", !Config->get_discover_vst_on_start()));
405 	plugin_scan_dialog->start();
406 	DEBUG_TRACE (DEBUG::GuiStartup, "plugin dialog done\n");
407 }
408 
409 void
show_new_user_dialog()410 StartupFSM::show_new_user_dialog ()
411 {
412 	set_state (WaitingForNewUser);
413 	new_user_dialog = new NewUserWizard;
414 	current_dialog_connection = new_user_dialog->signal_response().connect (sigc::bind (sigc::mem_fun (*this, &StartupFSM::dialog_response_handler), NewUserDialog));
415 	new_user_dialog->set_position (WIN_POS_CENTER);
416 	new_user_dialog->present ();
417 }
418 
419 void
show_session_dialog(bool new_session_required)420 StartupFSM::show_session_dialog (bool new_session_required)
421 {
422 	set_state (WaitingForSessionPath);
423 	session_dialog = new SessionDialog (new_session_required, session_name, session_path, session_template, false);
424 	current_dialog_connection = session_dialog->signal_response().connect (sigc::bind (sigc::mem_fun (*this, &StartupFSM::dialog_response_handler), NewSessionDialog));
425 	session_dialog->set_position (WIN_POS_CENTER);
426 	session_dialog->present ();
427 }
428 
429 void
show_audiomidi_dialog()430 StartupFSM::show_audiomidi_dialog ()
431 {
432 	set_state (WaitingForEngineParams);
433 	current_dialog_connection = audiomidi_dialog.signal_response().connect (sigc::bind (sigc::mem_fun (*this, &StartupFSM::dialog_response_handler), AudioMIDISetup));
434 	audiomidi_dialog.set_position (WIN_POS_CENTER);
435 	audiomidi_dialog.present ();
436 }
437 
438 void
start_audio_midi_setup()439 StartupFSM::start_audio_midi_setup ()
440 {
441 	bool setup_required = false;
442 
443 	boost::shared_ptr<AudioBackend> backend = AudioEngine::instance()->current_backend();
444 	if (!backend) {
445 		/* backend is unknown ... */
446 		setup_required = true;
447 
448 	} else if (session_is_new && AudioEngine::instance()->running() && AudioEngine::instance()->sample_rate () == session_existing_sample_rate) {
449 		/* keep engine */
450 
451 		warning << "A running engine should not be possible at this point" << endmsg;
452 
453 	} else if (AudioEngine::instance()->setup_required()) {
454 		/* backend is known, but setup is needed */
455 		setup_required = true;
456 
457 	} else if (!AudioEngine::instance()->running()) {
458 		/* should always be true during startup */
459 		if (AudioEngine::instance()->start()) {
460 			setup_required = true;
461 		}
462 	}
463 
464 	bool try_autostart = (Config->get_try_autostart_engine () || g_getenv ("ARDOUR_TRY_AUTOSTART_ENGINE"));
465 	if (session_is_new) {
466 		try_autostart = false;
467 	} else if (!backend) {
468 		try_autostart = false;
469 	} else if (try_autostart) {
470 		/* if user has selected auto-start, check if autostart is possible */
471 		bool ok = true;
472 		std::string backend_name;
473 		/* Allow auto-start if there is no backend information for the
474 		 * given session. This can happen when loading an old (v6) session,
475 		 * of if the user has been using externally started JACK.
476 		 */
477 		if (session_engine_hints.get_property ("backend", backend_name)) {
478 			std::string input_device;
479 			std::string output_device;
480 			ok &= session_engine_hints.get_property ("input-device", input_device);
481 			ok &= session_engine_hints.get_property ("output-device", output_device);
482 			ok &= backend->name () == backend_name;
483 			if (backend->use_separate_input_and_output_devices()) {
484 				ok &= input_device == backend->input_device_name ();
485 				ok &= output_device == backend->output_device_name ();
486 			} else {
487 				ok &= input_device == backend->device_name ();
488 				ok &= output_device == backend->device_name ();
489 			}
490 		}
491 		if (!ok) {
492 			try_autostart = false;
493 			ArdourMessageDialog msg (
494 					_("Engine I/O device has changed since you last opened this session.\n"
495 					  "Please verify that the new device has enough ports, or you may lose some i/o connections."),
496 					false, Gtk::MESSAGE_WARNING, Gtk::BUTTONS_OK, true);
497 			msg.run ();
498 		}
499 	}
500 
501 	if (setup_required) {
502 		if (try_autostart) {
503 			AudioEngine::instance()->set_sample_rate(session_existing_sample_rate);
504 			if (!AudioEngine::instance()->start ()) {
505 				if (ARDOUR::AudioEngine::instance()->running()) {
506 					DEBUG_TRACE (DEBUG::GuiStartup, "autostart successful, audio/MIDI setup dialog not required\n");
507 					engine_running ();
508 					return;
509 				}
510 			}
511 		}
512 
513 		if (!session_is_new && session_existing_sample_rate > 0) {
514 			audiomidi_dialog.set_desired_sample_rate (session_existing_sample_rate);
515 		}
516 
517 		show_audiomidi_dialog ();
518 		DEBUG_TRACE (DEBUG::GuiStartup, "audiomidi shown and waiting\n");
519 
520 	} else {
521 
522 		DEBUG_TRACE (DEBUG::GuiStartup, "engine already running, audio/MIDI setup dialog not required\n");
523 		engine_running ();
524 	}
525 }
526 
527 void
engine_running()528 StartupFSM::engine_running ()
529 {
530 	DEBUG_TRACE (DEBUG::GuiStartup, "engine running, start plugin scan then attach UI to engine\n");
531 
532 	/* This may be very slow. See comments in PluginScanDialog::start() */
533 	show_plugin_scan_dialog ();
534 
535 	DEBUG_TRACE (DEBUG::GuiStartup, "attach UI to engine\n");
536 
537 	/* This may be very slow: it will run the GUI's post-engine
538 	   initialization which is essentially unbounded in time/scope of what
539 	   it can do.
540 	*/
541 
542 	ARDOUR_UI::instance()->attach_to_engine ();
543 
544 	/* now that we've done the plugin scan AND attached the UI to the engine, we can
545 	   proceed with the next (final) steps of startup. This uses the same response
546 	   signal mechanism we use for the other dialogs.
547 	*/
548 
549 	plugin_scan_dialog->response (RESPONSE_OK);
550 }
551 
552 bool
get_session_parameters_from_command_line(bool new_session_required)553 StartupFSM::get_session_parameters_from_command_line (bool new_session_required)
554 {
555 	return get_session_parameters_from_path (ARDOUR_COMMAND_LINE::session_name, ARDOUR_COMMAND_LINE::load_template, new_session_required);
556 }
557 
558 bool
get_session_parameters_from_path(string const & path,string const & template_name,bool new_session_required)559 StartupFSM::get_session_parameters_from_path (string const & path, string const & template_name, bool new_session_required)
560 {
561 	if (path.empty()) {
562 		/* use GUI to ask the user */
563 		return false;
564 	}
565 
566 	if (Glib::file_test (path.c_str(), Glib::FILE_TEST_EXISTS)) {
567 
568 		session_is_new = false;
569 
570 		if (new_session_required) {
571 			/* wait! it already exists */
572 
573 			if (!ask_about_loading_existing_session (path)) {
574 				return false;
575 			} else {
576 				/* load it anyway */
577 			}
578 		}
579 
580 		session_name = basename_nosuffix (path);
581 
582 		if (Glib::file_test (path.c_str(), Glib::FILE_TEST_IS_REGULAR)) {
583 			/* session/snapshot file, change path to be dir */
584 			session_path = Glib::path_get_dirname (path);
585 		} else {
586 			session_path = path;
587 		}
588 
589 		float sr;
590 		SampleFormat fmt;
591 		string program_version;
592 
593 		const string statefile_path = Glib::build_filename (session_path, session_name + ARDOUR::statefile_suffix);
594 		if (Session::get_info_from_path (statefile_path, sr, fmt, program_version, &session_engine_hints)) {
595 			/* exists but we can't read it correctly */
596 			error << string_compose (_("Cannot get existing session information from %1"), statefile_path) << endmsg;
597 			return false;
598 		}
599 
600 		session_existing_sample_rate = sr;
601 		return true;
602 
603 	}
604 
605 	/*  Everything after this involves a new session
606 	 *
607 	 *  ... did the  user give us a path or just a name?
608 	 */
609 
610 	if (path.find (G_DIR_SEPARATOR) == string::npos) {
611 		/* user gave session name with no path info, use
612 		   default session folder.
613 		*/
614 		session_name = ARDOUR_COMMAND_LINE::session_name;
615 		session_path = Glib::build_filename (Config->get_default_session_parent_dir (), session_name);
616 	} else {
617 		session_name = basename_nosuffix (path);
618 		session_path = path;
619 	}
620 
621 	session_template = string ();
622 
623 	if (!template_name.empty()) {
624 
625 		/* Allow the user to specify a template via path or name on the
626 		 * command line
627 		 */
628 
629 		bool have_resolved_template_name = false;
630 
631 		/* compare by name (path may or may not be UTF-8) */
632 
633 		vector<TemplateInfo> templates;
634 		find_session_templates (templates, false);
635 		for (vector<TemplateInfo>::iterator x = templates.begin(); x != templates.end(); ++x) {
636 			if ((*x).name == template_name) {
637 				session_template = (*x).path;
638 				have_resolved_template_name = true;
639 				break;
640 			}
641 		}
642 
643 		/* look up script by name */
644 		LuaScriptList scripts (LuaScripting::instance ().scripts (LuaScriptInfo::SessionInit));
645 		LuaScriptList& as (LuaScripting::instance ().scripts (LuaScriptInfo::EditorAction));
646 		for (LuaScriptList::const_iterator s = as.begin(); s != as.end(); ++s) {
647 			if ((*s)->subtype & LuaScriptInfo::SessionSetup) {
648 				scripts.push_back (*s);
649 			}
650 		}
651 		std::sort (scripts.begin(), scripts.end(), LuaScripting::Sorter());
652 		for (LuaScriptList::const_iterator s = scripts.begin(); s != scripts.end(); ++s) {
653 			if ((*s)->name == template_name) {
654 				session_template = "urn:ardour:" + (*s)->path;
655 				have_resolved_template_name = true;
656 				break;
657 			}
658 		}
659 
660 		if (!have_resolved_template_name) {
661 			/* this will produce a more or less meaninful error later:
662 			 * "ERROR: Could not open session template [abs-path to user-config dir]"
663 			 */
664 			session_template = Glib::build_filename (ARDOUR::user_template_directory (), template_name);
665 		}
666 	}
667 
668 	/* We don't know what this is, because the session is new and the
669 	 * command line doesn't let us specify it. The user will get to decide
670 	 * in the audio/MIDI dialog.
671 	 */
672 
673 	session_existing_sample_rate = 0;
674 	session_is_new = true;
675 
676 	/* this is an arbitrary default value but since the user insists on
677 	 * starting a new session from the command line, it will do as well as
678 	 * any other possible value. I mean, seriously, what else could it be
679 	 * by default?
680 	 */
681 
682 	bus_profile.master_out_channels = 2;
683 
684 	return true;
685 }
686 
687 /** return values:
688  * -1: failure
689  *  1: failure but user can retry
690  *  0: success, seesion parameters ready for use
691  */
692 int
check_session_parameters(bool must_be_new)693 StartupFSM::check_session_parameters (bool must_be_new)
694 {
695 	bool requested_new = false;
696 
697 	session_name = session_dialog->session_name (requested_new);
698 	session_path = session_dialog->session_folder ();
699 	session_name_edited = session_dialog->was_new_name_edited ();
700 
701 	if (must_be_new) {
702 		assert (requested_new);
703 	}
704 
705 	if (!must_be_new) {
706 
707 		/* See if the specified session is a session archive */
708 
709 		int rv = ARDOUR::inflate_session (session_name, Config->get_default_session_parent_dir(), session_path, session_name);
710 		if (rv < 0) {
711 			ArdourMessageDialog msg (*session_dialog, string_compose (_("Extracting session-archive failed: %1"), inflate_error (rv)));
712 			msg.run ();
713 			return 1;
714 		} else if (rv == 0) {
715 			/* names are good (and session is unarchived/inflated */
716 			float sr;
717 			SampleFormat fmt;
718 			string program_version;
719 			const string statefile_path = Glib::build_filename (session_path, session_name + ARDOUR::statefile_suffix);
720 			if (Session::get_info_from_path (statefile_path, sr, fmt, program_version, &session_engine_hints)) {
721 				/* exists but we can't read it */
722 				return -1;
723 			}
724 			session_existing_sample_rate = sr;
725 			return 0;
726 		}
727 	}
728 
729 	/* check for ".ardour" in statefile name, because we don't want
730 	 * it
731 	 *
732 	 * XXX Note this wierd conflation of a
733 	 * file-name-without-a-suffix and the session name. It's not
734 	 * really a session name at all, but rather the suffix-free
735 	 * name of a statefile (snapshot).
736 	 */
737 
738 	const string::size_type suffix_at = session_name.find (statefile_suffix);
739 
740 	if (suffix_at != string::npos) {
741 		session_name = session_name.substr (0, suffix_at);
742 	}
743 
744 	/* this shouldn't happen, but we catch it just in case it does */
745 
746 	if (session_name.empty()) {
747 		return 1; /* keep running dialog */
748 	}
749 
750 	if (session_dialog->use_session_template()) {
751 		session_template = session_dialog->session_template_name();
752 	}
753 
754 	if (session_name[0] == G_DIR_SEPARATOR ||
755 #ifdef PLATFORM_WINDOWS
756 	    // Windows file system .. detect absolute path
757 	    // C:/*
758 	    (session_name.length() > 3 && session_name[1] == ':' && session_name[2] == G_DIR_SEPARATOR)
759 #else
760 	    // Sensible file systems
761 	    // /* or ./* or ../*
762 	    (session_name.length() > 2 && session_name[0] == '.' && session_name[1] == G_DIR_SEPARATOR) ||
763 	    (session_name.length() > 3 && session_name[0] == '.' && session_name[1] == '.' && session_name[2] == G_DIR_SEPARATOR)
764 #endif
765 		)
766 	{
767 
768 		/* user typed absolute path or cwd-relative path
769 		   specified into session name field. So ... infer
770 		   session path and name from what was given.
771 		*/
772 
773 		session_path = Glib::path_get_dirname (session_name);
774 		session_name = Glib::path_get_basename (session_name);
775 
776 	} else {
777 
778 		/* session name is just a name */
779 	}
780 
781 	/* check if the currently-exists status matches whether or not
782 	 * it should be new
783 	 */
784 
785 	if (Glib::file_test (session_path, Glib::FileTest (G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR))) {
786 
787 		if (requested_new /*&& !nsm*/) {
788 
789 			std::string existing = Glib::build_filename (session_path, session_name);
790 
791 			if (!ask_about_loading_existing_session (existing)) {
792 				session_dialog->show ();
793 				session_dialog->clear_name ();
794 				return 1; /* try again */
795 			}
796 		}
797 
798 		session_is_new = false;
799 
800 	} else {
801 
802 		/* does not exist at present */
803 
804 		if (!requested_new) {
805 			ArdourMessageDialog msg (string_compose (_("There is no existing session at \"%1\""), session_path));
806 			msg.run ();
807 			session_dialog->clear_name();
808 			return 1;
809 		}
810 
811 		session_is_new = true;
812 	}
813 
814 
815 	/* check if name is legal (error for new sessions only) */
816 	std::string const& illegal = Session::session_name_is_legal (session_name);
817 
818 	if (!illegal.empty() && session_is_new) {
819 		ArdourMessageDialog msg (*session_dialog,
820 		                         string_compose (_("To ensure compatibility with various systems\n"
821 		                                           "session names may not contain a '%1' character"),
822 		                                         illegal));
823 		msg.run ();
824 		ARDOUR_COMMAND_LINE::session_name = ""; // cancel that
825 		return 1; /* keep running dialog */
826 	}
827 
828 
829 	float sr;
830 	SampleFormat fmt;
831 	string program_version;
832 	const string statefile_path = Glib::build_filename (session_path, session_name + ARDOUR::statefile_suffix);
833 
834 	if (!session_is_new) {
835 
836 		if (Session::get_info_from_path (statefile_path, sr, fmt, program_version, &session_engine_hints)) {
837 			/* exists but we can't read it */
838 			return -1;
839 		}
840 
841 		session_existing_sample_rate = sr;
842 
843 	} else {
844 
845 		bus_profile.master_out_channels = session_dialog->master_channel_count ();
846 	}
847 
848 	return 0;
849 }
850 
851 void
copy_demo_sessions()852 StartupFSM::copy_demo_sessions ()
853 {
854 	// TODO: maybe IFF brand_new_user
855 	if (ARDOUR::Profile->get_mixbus () && Config->get_copy_demo_sessions ()) {
856 		std::string dspd (Config->get_default_session_parent_dir());
857 		Searchpath ds (ARDOUR::ardour_data_search_path());
858 		ds.add_subdirectory_to_paths ("sessions");
859 		vector<string> demos;
860 		find_files_matching_pattern (demos, ds, ARDOUR::session_archive_suffix);
861 
862 		ARDOUR::RecentSessions rs;
863 		ARDOUR::read_recent_sessions (rs);
864 
865 		for (vector<string>::iterator i = demos.begin(); i != demos.end (); ++i) {
866 			/* "demo-session" must be inside "demo-session.<session_archive_suffix>" */
867 			std::string name = basename_nosuffix (basename_nosuffix (*i));
868 			std::string path = Glib::build_filename (dspd, name);
869 			/* skip if session-dir already exists */
870 			if (Glib::file_test(path.c_str(), Glib::FILE_TEST_IS_DIR)) {
871 				continue;
872 			}
873 			/* skip sessions that are already in 'recent'.
874 			 * eg. a new user changed <session-default-dir> shorly after installation
875 			 */
876 			for (ARDOUR::RecentSessions::iterator r = rs.begin(); r != rs.end(); ++r) {
877 				if ((*r).first == name) {
878 					continue;
879 				}
880 			}
881 			try {
882 				PBD::FileArchive ar (*i);
883 				if (0 == ar.inflate (dspd)) {
884 					store_recent_sessions (name, path);
885 					info << string_compose (_("Copied Demo Session %1."), name) << endmsg;
886 				}
887 			} catch (...) {
888 				/* relax ? */
889 			}
890 		}
891 	}
892 }
893 
894 bool
ask_about_loading_existing_session(const std::string & session_path)895 StartupFSM::ask_about_loading_existing_session (const std::string& session_path)
896 {
897 	std::string str = string_compose (_("This session\n%1\nalready exists. Do you want to open it?"), session_path);
898 
899 	ArdourMessageDialog msg (str,
900 	                         false,
901 	                         Gtk::MESSAGE_WARNING,
902 	                         Gtk::BUTTONS_YES_NO,
903 	                         true);
904 
905 
906 	msg.set_name (X_("OpenExistingDialog"));
907 	msg.set_title (_("Open Existing Session"));
908 	msg.set_wmclass (X_("existing_session"), PROGRAM_NAME);
909 	msg.set_position (Gtk::WIN_POS_CENTER);
910 
911 	switch (msg.run()) {
912 	case RESPONSE_YES:
913 		return true;
914 		break;
915 	}
916 	return false;
917 }
918 
919 void
show_pre_release_dialog()920 StartupFSM::show_pre_release_dialog ()
921 {
922 	pre_release_dialog = new ArdourDialog  (_("Pre-Release Warning"), true, false);
923 	pre_release_dialog->add_button (Gtk::Stock::OK, Gtk::RESPONSE_OK);
924 
925 	Label* label = manage (new Label);
926 	label->set_markup (string_compose (_("<span size=\"x-large\" weight=\"bold\">Welcome to this pre-release build of %1 %2</span>\n\n\
927 <span size=\"large\">There are still several issues and bugs to be worked on,\n\
928 as well as general workflow improvements, before this can be considered\n\
929 release software. So, a few guidelines:\n\
930 \n\
931 1) Please do <b>NOT</b> use this software with the expectation that it is stable or reliable\n\
932    though it may be so, depending on your workflow.\n\
933 2) Please wait for a helpful writeup of new features.\n\
934 3) <b>Please do NOT use the forums at ardour.org to report issues</b>.\n\
935 4) <b>Please do NOT file bugs for this alpha-development versions at this point in time</b>.\n\
936    There is no bug triaging before the initial development concludes and\n\
937    reporting issue for incomplete, ongoing work-in-progress is mostly useless.\n\
938 5) Please <b>DO</b> join us on IRC for real time discussions about %1 %2. You\n\
939    can get there directly from within the program via the Help->Chat menu option.\n\
940 6) Please <b>DO</b> submit patches for issues after discussing them on IRC.\n\
941 \n\
942 Full information on all the above can be found on the support page at\n\
943 \n\
944                 http://ardour.org/support</span>\n\
945 "), PROGRAM_NAME, VERSIONSTRING));
946 
947 
948 	current_dialog_connection = pre_release_dialog->signal_response().connect (sigc::bind (sigc::mem_fun (this, &StartupFSM::dialog_response_handler), PreReleaseDialog));
949 
950 	pre_release_dialog->get_vbox()->set_border_width (12);
951 	pre_release_dialog->get_vbox()->pack_start (*label, false, false, 12);
952 	pre_release_dialog->get_vbox()->show_all ();
953 	pre_release_dialog->set_position (WIN_POS_CENTER);
954 	pre_release_dialog->present ();
955 }
956 
957 void
handle_path(string const & path)958 StartupFSM::handle_path (string const & path)
959 {
960 	if (!ARDOUR_COMMAND_LINE::session_name.empty()) {
961 		return;
962 	}
963 
964 	ARDOUR_COMMAND_LINE::session_name = path;
965 
966 	dialog_response_handler (RESPONSE_OK, ApplicationPseudoDialog);
967 }
968