2019-10-09 22:50:34 -04:00
/*
* Copyright ( C ) 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 .
*/
# include <vector>
# include <gtkmm/dialog.h>
# include <gtkmm/liststore.h>
# include <gtkmm/messagedialog.h>
2019-10-10 23:24:52 -04:00
# include <gtkmm/stock.h>
2019-10-09 22:50:34 -04:00
# include "pbd/basename.h"
# include "pbd/file_archive.h"
# include "pbd/file_utils.h"
# include "ardour/audioengine.h"
2021-02-23 13:26:01 -05:00
# include "ardour/audio_backend.h"
2019-10-09 22:50:34 -04:00
# include "ardour/filename_extensions.h"
# include "ardour/filesystem_paths.h"
# include "ardour/profile.h"
# include "ardour/recent_sessions.h"
# include "ardour/rc_configuration.h"
# include "ardour/search_paths.h"
# include "ardour/session.h"
# include "ardour/session_utils.h"
# include "ardour/template_utils.h"
# include "gtkmm2ext/application.h"
# include <gtkmm2ext/doi.h>
2019-11-06 18:32:46 -05:00
# include <gtkmm2ext/keyboard.h>
2019-10-09 22:50:34 -04:00
2019-12-14 17:31:24 -05:00
# include "ardour_message.h"
2019-10-11 16:29:55 -04:00
# include "ardour_ui.h"
2019-10-24 23:14:00 -04:00
# include "debug.h"
2019-10-09 22:50:34 -04:00
# include "engine_dialog.h"
# include "new_user_wizard.h"
# include "opts.h"
2019-10-24 23:14:00 -04:00
# include "plugin_scan_dialog.h"
2019-10-09 22:50:34 -04:00
# include "session_dialog.h"
2019-10-24 23:14:00 -04:00
# include "splash.h"
2019-10-09 22:50:34 -04:00
# include "startup_fsm.h"
2019-10-10 23:24:52 -04:00
# ifdef WAF_BUILD
# include "gtk2ardour-version.h"
# endif
2020-08-19 11:40:02 -04:00
# include "pbd/i18n.h"
2019-10-09 22:50:34 -04:00
using namespace ARDOUR ;
using namespace Gtk ;
using namespace Gtkmm2ext ;
using namespace PBD ;
using std : : string ;
using std : : vector ;
StartupFSM : : StartupFSM ( EngineControl & amd )
: session_existing_sample_rate ( 0 )
2021-02-27 10:41:09 -05:00
, session_engine_hints ( " EngineHints " )
2019-10-09 22:50:34 -04:00
, session_is_new ( false )
2020-03-24 12:38:24 -04:00
, session_name_edited ( false )
2019-10-10 17:25:30 -04:00
, new_user ( NewUserWizard : : required ( ) )
2019-10-24 23:14:00 -04:00
, new_session_required ( ARDOUR_COMMAND_LINE : : new_session | | ( ! ARDOUR : : Profile - > get_mixbus ( ) & & new_user ) )
, _state ( new_user ? WaitingForNewUser : WaitingForSessionPath )
2019-10-09 22:50:34 -04:00
, audiomidi_dialog ( amd )
2019-10-24 23:14:00 -04:00
, new_user_dialog ( 0 )
2019-10-09 22:50:34 -04:00
, session_dialog ( 0 )
2019-10-10 23:24:52 -04:00
, pre_release_dialog ( 0 )
2019-10-24 23:14:00 -04:00
, plugin_scan_dialog ( 0 )
2019-10-09 22:50:34 -04:00
{
2019-10-24 23:14:00 -04:00
/* 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
*/
2020-02-29 11:33:10 -05:00
if ( string ( VERSIONSTRING ) . find ( " .pre0 " ) ! = string : : npos ) {
2019-10-10 23:24:52 -04:00
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 ) ) {
2019-10-24 23:14:00 -04:00
set_state ( WaitingForPreRelease ) ;
2019-10-10 23:24:52 -04:00
}
}
2019-10-09 22:50:34 -04:00
Application * app = Application : : instance ( ) ;
app - > ShouldQuit . connect ( sigc : : mem_fun ( * this , & StartupFSM : : queue_finish ) ) ;
2019-11-06 18:32:46 -05:00
Gtkmm2ext : : Keyboard : : HideMightMeanQuit . connect ( sigc : : mem_fun ( * this , & StartupFSM : : dialog_hidden ) ) ;
2019-10-09 22:50:34 -04:00
}
StartupFSM : : ~ StartupFSM ( )
{
delete session_dialog ;
2019-10-24 23:14:00 -04:00
delete pre_release_dialog ;
delete plugin_scan_dialog ;
delete new_user_dialog ;
2019-10-09 22:50:34 -04:00
}
2019-11-06 18:32:46 -05:00
void
StartupFSM : : dialog_hidden ( Gtk : : Window * /* ignored */ )
{
/* since this object only exists during startup, any attempt to close
* any dialog that we manage with Ctrl / Cmd - w is assumed to indicate a
* desire to quit on the part of the user .
*/
queue_finish ( ) ;
}
2019-10-09 22:50:34 -04:00
void
StartupFSM : : queue_finish ( )
{
_signal_response ( ExitProgram ) ;
}
void
StartupFSM : : start ( )
{
2019-10-24 23:14:00 -04:00
DEBUG_TRACE ( DEBUG : : GuiStartup , string_compose ( X_ ( " State at startup: %1 \n " ) , enum_2_string ( _state ) ) ) ;
switch ( _state ) {
case WaitingForPreRelease :
2019-10-10 23:24:52 -04:00
show_pre_release_dialog ( ) ;
2019-10-24 23:14:00 -04:00
break ;
case WaitingForNewUser :
show_new_user_dialog ( ) ;
break ;
case WaitingForSessionPath :
2019-12-12 15:32:42 -05:00
handle_waiting_for_session_path ( ) ;
2019-10-24 23:14:00 -04:00
break ;
2020-06-12 16:08:04 -04:00
case WaitingForEngineParams :
start_audio_midi_setup ( ) ;
break ;
2019-10-24 23:14:00 -04:00
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 ( ) ;
2019-10-09 22:50:34 -04:00
}
2019-10-24 23:14:00 -04:00
DEBUG_TRACE ( DEBUG : : GuiStartup , string_compose ( X_ ( " State after startup: %1 \n " ) , enum_2_string ( _state ) ) ) ;
2019-10-09 22:50:34 -04:00
}
2019-10-24 23:14:00 -04:00
void
StartupFSM : : reset ( )
{
show_session_dialog ( new_session_required ) ;
}
2019-10-10 17:22:44 -04:00
void
2019-10-24 23:14:00 -04:00
StartupFSM : : set_state ( MainState ms )
2019-10-10 17:22:44 -04:00
{
2019-10-24 23:14:00 -04:00
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 ;
}
2019-10-10 17:22:44 -04:00
2019-10-24 23:14:00 -04:00
template < typename T > void
StartupFSM : : end_dialog ( T & d )
{
d . hide ( ) ;
current_dialog_connection . disconnect ( ) ;
2019-10-10 17:22:44 -04:00
}
2019-10-09 22:50:34 -04:00
void
StartupFSM : : dialog_response_handler ( int response , StartupFSM : : DialogID dialog_id )
{
2019-10-24 23:14:00 -04:00
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 ) ) ;
2019-10-09 22:50:34 -04:00
2019-10-24 23:14:00 -04:00
/* 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 .
*/
2019-10-10 23:47:17 -04:00
2019-10-09 22:50:34 -04:00
switch ( _state ) {
2019-10-24 23:14:00 -04:00
case WaitingForPreRelease :
2019-10-10 23:24:52 -04:00
switch ( dialog_id ) {
2020-03-06 17:53:31 -05:00
case ApplicationPseudoDialog :
/* this shouldn't happen; ignore it */
break ;
2019-10-10 23:24:52 -04:00
case PreReleaseDialog :
default :
/* any response value from the pre-release dialog means
" move along now "
*/
2019-10-25 09:51:54 -04:00
end_dialog ( & pre_release_dialog ) ;
2019-10-24 23:14:00 -04:00
2019-10-10 23:24:52 -04:00
if ( NewUserWizard : : required ( ) ) {
2019-10-24 23:14:00 -04:00
show_new_user_dialog ( ) ;
2019-10-10 23:24:52 -04:00
} else {
2019-12-12 15:32:42 -05:00
handle_waiting_for_session_path ( ) ;
2019-10-10 23:24:52 -04:00
}
break ;
}
break ;
2019-10-24 23:14:00 -04:00
case WaitingForNewUser :
2019-10-09 22:50:34 -04:00
switch ( dialog_id ) {
2020-03-06 17:53:31 -05:00
case ApplicationPseudoDialog :
/* this shouldn't happen; ignore it */
break ;
2019-10-09 22:50:34 -04:00
case NewUserDialog :
switch ( response ) {
case RESPONSE_OK :
2019-10-24 23:14:00 -04:00
end_dialog ( & new_user_dialog ) ;
show_session_dialog ( new_session_required ) ;
2019-10-09 22:50:34 -04:00
break ;
default :
2019-10-24 23:14:00 -04:00
_signal_response ( ExitProgram ) ;
2019-10-09 22:50:34 -04:00
}
2019-10-24 23:14:00 -04:00
default :
/* ERROR */
2019-10-09 22:50:34 -04:00
break ;
2019-10-24 23:14:00 -04:00
}
break ;
2019-10-09 22:50:34 -04:00
2019-10-24 23:14:00 -04:00
case WaitingForSessionPath :
switch ( dialog_id ) {
2019-10-09 22:50:34 -04:00
case NewSessionDialog :
switch ( response ) {
case RESPONSE_OK :
case RESPONSE_ACCEPT :
2019-10-10 18:51:51 -04:00
switch ( check_session_parameters ( new_session_required ) ) {
2019-10-09 22:50:34 -04:00
case - 1 :
/* Unrecoverable error */
_signal_response ( ExitProgram ) ;
break ;
2019-10-24 23:14:00 -04:00
case 0 :
end_dialog ( & session_dialog ) ;
start_audio_midi_setup ( ) ;
break ;
2019-10-09 22:50:34 -04:00
case 1 :
/* do nothing - keep dialog up for a
* retry . Errors were addressed by
* : : check_session_parameters ( )
*/
2021-03-11 13:20:29 -05:00
session_dialog - > present ( ) ;
2019-10-09 22:50:34 -04:00
break ;
}
break ;
default :
_signal_response ( ExitProgram ) ;
break ;
}
break ;
2020-03-06 17:53:31 -05:00
case ApplicationPseudoDialog :
/* macOS, NSM etc. ... existence was already checked */
if ( get_session_parameters_from_path ( ARDOUR_COMMAND_LINE : : session_name , string ( ) , false ) ) {
start_audio_midi_setup ( ) ;
}
break ;
2019-10-09 22:50:34 -04:00
default :
/* ERROR */
break ;
}
break ;
2019-10-24 23:14:00 -04:00
case WaitingForEngineParams :
2019-10-09 22:50:34 -04:00
switch ( dialog_id ) {
case AudioMIDISetup :
switch ( response ) {
case RESPONSE_OK :
case RESPONSE_ACCEPT :
if ( AudioEngine : : instance ( ) - > running ( ) ) {
2020-01-24 17:56:05 -05:00
/* prevent double clicks from changing engine state */
audiomidi_dialog . set_ui_sensitive ( false ) ;
2019-10-24 23:14:00 -04:00
end_dialog ( audiomidi_dialog ) ;
engine_running ( ) ;
2019-10-10 18:51:51 -04:00
} else {
/* just keep going */
2019-10-09 22:50:34 -04:00
}
break ;
default :
_signal_response ( ExitProgram ) ;
}
break ;
default :
/* ERROR */
break ;
}
2019-10-10 23:24:52 -04:00
break ;
2019-10-09 22:50:34 -04:00
2019-10-24 23:14:00 -04:00
case WaitingForPlugins :
switch ( dialog_id ) {
case PluginDialog :
end_dialog ( & plugin_scan_dialog ) ;
switch ( response ) {
case RESPONSE_OK :
2020-01-24 17:56:23 -05:00
if ( AudioEngine : : instance ( ) - > running ( ) ) {
_signal_response ( LoadSession ) ;
} else {
/* Engine died unexpectedly (it was
* running after
* WaitingForEngineParams ) . Nothing to
* do but go back to the audio / MIDI
* setup . It would be nice , perhaps , to
* show an extra message indicating
* that something is not right .
*/
2020-04-13 10:09:40 -04:00
ArdourMessageDialog msg ( _ ( " The audio/MIDI engine has stopped running unexpectedly. \n Something is probably wrong with your audio/MIDI device settings. " ) ) ;
2020-01-24 17:56:23 -05:00
msg . set_position ( WIN_POS_CENTER ) ;
msg . run ( ) ;
/* This has been shown before, so we do
* not need start_audio_midi_setup ( ) ;
*/
show_audiomidi_dialog ( ) ;
}
2019-10-24 23:14:00 -04:00
break ;
default :
_signal_response ( ExitProgram ) ;
break ;
}
default :
/* ERROR */
break ;
}
2019-10-09 22:50:34 -04:00
}
}
2019-12-12 15:32:42 -05:00
void
StartupFSM : : handle_waiting_for_session_path ( )
{
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 ) ;
}
}
}
2019-10-09 22:50:34 -04:00
void
2019-10-24 23:14:00 -04:00
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 ) .
*/
2019-10-25 13:55:10 -04:00
const bool cache_only = ( ! Config - > get_discover_vst_on_start ( ) | | Profile - > get_mixbus ( ) ) ;
2019-10-25 19:12:05 -04:00
const bool verbose = new_user ;
2019-10-25 13:55:10 -04:00
plugin_scan_dialog = new PluginScanDialog ( cache_only , verbose ) ;
2019-10-24 23:14:00 -04:00
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 ( )
2019-10-09 22:50:34 -04:00
{
2019-10-24 23:14:00 -04:00
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 ( ) ;
2019-10-09 22:50:34 -04:00
}
void
2019-10-10 23:24:52 -04:00
StartupFSM : : show_session_dialog ( bool new_session_required )
2019-10-09 22:50:34 -04:00
{
2019-10-24 23:14:00 -04:00
set_state ( WaitingForSessionPath ) ;
2019-10-10 23:24:52 -04:00
session_dialog = new SessionDialog ( new_session_required , session_name , session_path , session_template , false ) ;
2019-10-09 22:50:34 -04:00
current_dialog_connection = session_dialog - > signal_response ( ) . connect ( sigc : : bind ( sigc : : mem_fun ( * this , & StartupFSM : : dialog_response_handler ) , NewSessionDialog ) ) ;
2019-10-10 21:54:33 -04:00
session_dialog - > set_position ( WIN_POS_CENTER ) ;
2019-10-09 22:50:34 -04:00
session_dialog - > present ( ) ;
}
void
StartupFSM : : show_audiomidi_dialog ( )
{
2019-10-24 23:14:00 -04:00
set_state ( WaitingForEngineParams ) ;
2019-10-09 22:50:34 -04:00
current_dialog_connection = audiomidi_dialog . signal_response ( ) . connect ( sigc : : bind ( sigc : : mem_fun ( * this , & StartupFSM : : dialog_response_handler ) , AudioMIDISetup ) ) ;
2019-10-10 21:54:33 -04:00
audiomidi_dialog . set_position ( WIN_POS_CENTER ) ;
2019-10-09 22:50:34 -04:00
audiomidi_dialog . present ( ) ;
}
2019-10-12 14:10:10 -04:00
void
StartupFSM : : start_audio_midi_setup ( )
{
2021-07-04 15:48:12 -04:00
BootMessage ( _ ( " Starting Audio/MIDI Engine " ) ) ;
2019-10-12 14:10:10 -04:00
bool setup_required = false ;
2021-02-23 13:26:01 -05:00
boost : : shared_ptr < AudioBackend > backend = AudioEngine : : instance ( ) - > current_backend ( ) ;
if ( ! backend ) {
2019-10-12 14:10:10 -04:00
/* backend is unknown ... */
setup_required = true ;
} else if ( session_is_new & & AudioEngine : : instance ( ) - > running ( ) & & AudioEngine : : instance ( ) - > sample_rate ( ) = = session_existing_sample_rate ) {
/* keep engine */
warning < < " A running engine should not be possible at this point " < < endmsg ;
} else if ( AudioEngine : : instance ( ) - > setup_required ( ) ) {
/* backend is known, but setup is needed */
setup_required = true ;
} else if ( ! AudioEngine : : instance ( ) - > running ( ) ) {
/* should always be true during startup */
if ( AudioEngine : : instance ( ) - > start ( ) ) {
setup_required = true ;
}
}
2021-02-23 13:26:01 -05:00
bool try_autostart = ( Config - > get_try_autostart_engine ( ) | | g_getenv ( " ARDOUR_TRY_AUTOSTART_ENGINE " ) ) ;
if ( session_is_new ) {
try_autostart = false ;
} else if ( ! backend ) {
try_autostart = false ;
} else if ( try_autostart ) {
/* if user has selected auto-start, check if autostart is possible */
bool ok = true ;
std : : string backend_name ;
2021-02-27 10:41:09 -05:00
/* Allow auto-start if there is no backend information for the
* given session . This can happen when loading an old ( v6 ) session ,
* of if the user has been using externally started JACK .
*/
if ( session_engine_hints . get_property ( " backend " , backend_name ) ) {
std : : string input_device ;
std : : string output_device ;
ok & = session_engine_hints . get_property ( " input-device " , input_device ) ;
ok & = session_engine_hints . get_property ( " output-device " , output_device ) ;
ok & = backend - > name ( ) = = backend_name ;
if ( backend - > use_separate_input_and_output_devices ( ) ) {
ok & = input_device = = backend - > input_device_name ( ) ;
ok & = output_device = = backend - > output_device_name ( ) ;
} else {
ok & = input_device = = backend - > device_name ( ) ;
ok & = output_device = = backend - > device_name ( ) ;
}
2021-02-23 13:26:01 -05:00
}
if ( ! ok ) {
try_autostart = false ;
ArdourMessageDialog msg (
_ ( " Engine I/O device has changed since you last opened this session. \n "
" Please verify that the new device has enough ports, or you may lose some i/o connections. " ) ,
false , Gtk : : MESSAGE_WARNING , Gtk : : BUTTONS_OK , true ) ;
msg . run ( ) ;
}
}
2019-11-06 18:51:03 -05:00
2021-02-23 13:26:01 -05:00
if ( setup_required ) {
if ( try_autostart ) {
2019-12-06 14:25:44 -05:00
AudioEngine : : instance ( ) - > set_sample_rate ( session_existing_sample_rate ) ;
2019-11-06 19:35:30 -05:00
if ( ! AudioEngine : : instance ( ) - > start ( ) ) {
if ( ARDOUR : : AudioEngine : : instance ( ) - > running ( ) ) {
DEBUG_TRACE ( DEBUG : : GuiStartup , " autostart successful, audio/MIDI setup dialog not required \n " ) ;
engine_running ( ) ;
return ;
}
2019-11-06 18:51:03 -05:00
}
}
2019-11-06 19:35:30 -05:00
if ( ! session_is_new & & session_existing_sample_rate > 0 ) {
audiomidi_dialog . set_desired_sample_rate ( session_existing_sample_rate ) ;
}
2019-10-12 14:10:10 -04:00
show_audiomidi_dialog ( ) ;
2019-10-24 23:14:00 -04:00
DEBUG_TRACE ( DEBUG : : GuiStartup , " audiomidi shown and waiting \n " ) ;
2019-11-06 18:51:03 -05:00
2019-10-12 14:10:10 -04:00
} else {
2019-10-24 23:14:00 -04:00
DEBUG_TRACE ( DEBUG : : GuiStartup , " engine already running, audio/MIDI setup dialog not required \n " ) ;
engine_running ( ) ;
2019-10-12 14:10:10 -04:00
}
}
2019-10-24 23:14:00 -04:00
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 ) ;
}
2019-10-09 22:50:34 -04:00
bool
StartupFSM : : get_session_parameters_from_command_line ( bool new_session_required )
{
return get_session_parameters_from_path ( ARDOUR_COMMAND_LINE : : session_name , ARDOUR_COMMAND_LINE : : load_template , new_session_required ) ;
}
bool
StartupFSM : : get_session_parameters_from_path ( string const & path , string const & template_name , bool new_session_required )
{
if ( path . empty ( ) ) {
/* use GUI to ask the user */
return false ;
}
if ( Glib : : file_test ( path . c_str ( ) , Glib : : FILE_TEST_EXISTS ) ) {
2019-10-10 17:25:30 -04:00
session_is_new = false ;
2019-10-09 22:50:34 -04:00
if ( new_session_required ) {
/* wait! it already exists */
if ( ! ask_about_loading_existing_session ( path ) ) {
return false ;
} else {
/* load it anyway */
}
}
session_name = basename_nosuffix ( path ) ;
if ( Glib : : file_test ( path . c_str ( ) , Glib : : FILE_TEST_IS_REGULAR ) ) {
/* session/snapshot file, change path to be dir */
session_path = Glib : : path_get_dirname ( path ) ;
2019-10-10 17:25:30 -04:00
} else {
session_path = path ;
2019-10-09 22:50:34 -04:00
}
float sr ;
SampleFormat fmt ;
string program_version ;
2019-10-10 17:25:30 -04:00
const string statefile_path = Glib : : build_filename ( session_path , session_name + ARDOUR : : statefile_suffix ) ;
2021-02-23 13:26:01 -05:00
if ( Session : : get_info_from_path ( statefile_path , sr , fmt , program_version , & session_engine_hints ) ) {
2019-10-10 17:25:30 -04:00
/* exists but we can't read it correctly */
error < < string_compose ( _ ( " Cannot get existing session information from %1 " ) , statefile_path ) < < endmsg ;
2019-10-09 22:50:34 -04:00
return false ;
}
session_existing_sample_rate = sr ;
return true ;
}
/* Everything after this involves a new session
*
* . . . did the user give us a path or just a name ?
*/
if ( path . find ( G_DIR_SEPARATOR ) = = string : : npos ) {
/* user gave session name with no path info, use
default session folder .
*/
session_name = ARDOUR_COMMAND_LINE : : session_name ;
session_path = Glib : : build_filename ( Config - > get_default_session_parent_dir ( ) , session_name ) ;
} else {
session_name = basename_nosuffix ( path ) ;
2019-10-10 17:25:30 -04:00
session_path = path ;
2019-10-09 22:50:34 -04:00
}
2020-03-05 19:34:41 -05:00
session_template = string ( ) ;
2019-10-09 22:50:34 -04:00
if ( ! template_name . empty ( ) ) {
/* Allow the user to specify a template via path or name on the
* command line
*/
bool have_resolved_template_name = false ;
/* compare by name (path may or may not be UTF-8) */
vector < TemplateInfo > templates ;
find_session_templates ( templates , false ) ;
for ( vector < TemplateInfo > : : iterator x = templates . begin ( ) ; x ! = templates . end ( ) ; + + x ) {
if ( ( * x ) . name = = template_name ) {
session_template = ( * x ) . path ;
have_resolved_template_name = true ;
break ;
}
}
/* look up script by name */
LuaScriptList scripts ( LuaScripting : : instance ( ) . scripts ( LuaScriptInfo : : SessionInit ) ) ;
LuaScriptList & as ( LuaScripting : : instance ( ) . scripts ( LuaScriptInfo : : EditorAction ) ) ;
for ( LuaScriptList : : const_iterator s = as . begin ( ) ; s ! = as . end ( ) ; + + s ) {
if ( ( * s ) - > subtype & LuaScriptInfo : : SessionSetup ) {
scripts . push_back ( * s ) ;
}
}
std : : sort ( scripts . begin ( ) , scripts . end ( ) , LuaScripting : : Sorter ( ) ) ;
for ( LuaScriptList : : const_iterator s = scripts . begin ( ) ; s ! = scripts . end ( ) ; + + s ) {
if ( ( * s ) - > name = = template_name ) {
session_template = " urn:ardour: " + ( * s ) - > path ;
have_resolved_template_name = true ;
break ;
}
}
if ( ! have_resolved_template_name ) {
/* this will produce a more or less meaninful error later:
* " ERROR: Could not open session template [abs-path to user-config dir] "
*/
session_template = Glib : : build_filename ( ARDOUR : : user_template_directory ( ) , template_name ) ;
}
}
/* We don't know what this is, because the session is new and the
* command line doesn ' t let us specify it . The user will get to decide
* in the audio / MIDI dialog .
*/
session_existing_sample_rate = 0 ;
2019-10-10 17:25:30 -04:00
session_is_new = true ;
/* this is an arbitrary default value but since the user insists on
* starting a new session from the command line , it will do as well as
* any other possible value . I mean , seriously , what else could it be
* by default ?
*/
bus_profile . master_out_channels = 2 ;
2019-10-09 22:50:34 -04:00
return true ;
}
/** return values:
* - 1 : failure
* 1 : failure but user can retry
* 0 : success , seesion parameters ready for use
*/
int
StartupFSM : : check_session_parameters ( bool must_be_new )
{
bool requested_new = false ;
session_name = session_dialog - > session_name ( requested_new ) ;
session_path = session_dialog - > session_folder ( ) ;
2020-03-24 12:38:24 -04:00
session_name_edited = session_dialog - > was_new_name_edited ( ) ;
2019-10-09 22:50:34 -04:00
if ( must_be_new ) {
assert ( requested_new ) ;
}
if ( ! must_be_new ) {
/* See if the specified session is a session archive */
int rv = ARDOUR : : inflate_session ( session_name , Config - > get_default_session_parent_dir ( ) , session_path , session_name ) ;
2021-03-11 13:37:10 -05:00
if ( rv < 0 ) {
2019-12-14 17:31:24 -05:00
ArdourMessageDialog msg ( * session_dialog , string_compose ( _ ( " Extracting session-archive failed: %1 " ) , inflate_error ( rv ) ) ) ;
2019-10-09 22:50:34 -04:00
msg . run ( ) ;
return 1 ;
} else if ( rv = = 0 ) {
/* names are good (and session is unarchived/inflated */
2021-03-19 17:51:12 -04:00
float sr ;
SampleFormat fmt ;
string program_version ;
const string statefile_path = Glib : : build_filename ( session_path , session_name + ARDOUR : : statefile_suffix ) ;
if ( Session : : get_info_from_path ( statefile_path , sr , fmt , program_version , & session_engine_hints ) ) {
/* exists but we can't read it */
return - 1 ;
}
session_existing_sample_rate = sr ;
2019-10-09 22:50:34 -04:00
return 0 ;
}
}
/* check for ".ardour" in statefile name, because we don't want
* it
*
* XXX Note this wierd conflation of a
* file - name - without - a - suffix and the session name . It ' s not
* really a session name at all , but rather the suffix - free
* name of a statefile ( snapshot ) .
*/
const string : : size_type suffix_at = session_name . find ( statefile_suffix ) ;
if ( suffix_at ! = string : : npos ) {
session_name = session_name . substr ( 0 , suffix_at ) ;
}
/* this shouldn't happen, but we catch it just in case it does */
if ( session_name . empty ( ) ) {
return 1 ; /* keep running dialog */
}
if ( session_dialog - > use_session_template ( ) ) {
session_template = session_dialog - > session_template_name ( ) ;
}
if ( session_name [ 0 ] = = G_DIR_SEPARATOR | |
# ifdef PLATFORM_WINDOWS
// Windows file system .. detect absolute path
// C:/*
( session_name . length ( ) > 3 & & session_name [ 1 ] = = ' : ' & & session_name [ 2 ] = = G_DIR_SEPARATOR )
# else
// Sensible file systems
// /* or ./* or ../*
( session_name . length ( ) > 2 & & session_name [ 0 ] = = ' . ' & & session_name [ 1 ] = = G_DIR_SEPARATOR ) | |
( session_name . length ( ) > 3 & & session_name [ 0 ] = = ' . ' & & session_name [ 1 ] = = ' . ' & & session_name [ 2 ] = = G_DIR_SEPARATOR )
# endif
)
{
/* user typed absolute path or cwd-relative path
specified into session name field . So . . . infer
session path and name from what was given .
*/
session_path = Glib : : path_get_dirname ( session_name ) ;
session_name = Glib : : path_get_basename ( session_name ) ;
} else {
/* session name is just a name */
}
/* check if the currently-exists status matches whether or not
* it should be new
*/
if ( Glib : : file_test ( session_path , Glib : : FileTest ( G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR ) ) ) {
if ( requested_new /*&& !nsm*/ ) {
std : : string existing = Glib : : build_filename ( session_path , session_name ) ;
if ( ! ask_about_loading_existing_session ( existing ) ) {
2020-03-24 02:48:59 -04:00
session_dialog - > show ( ) ;
2019-10-09 22:50:34 -04:00
session_dialog - > clear_name ( ) ;
return 1 ; /* try again */
}
}
session_is_new = false ;
} else {
/* does not exist at present */
if ( ! requested_new ) {
2019-12-14 17:31:24 -05:00
ArdourMessageDialog msg ( string_compose ( _ ( " There is no existing session at \" %1 \" " ) , session_path ) ) ;
2019-10-09 22:50:34 -04:00
msg . run ( ) ;
session_dialog - > clear_name ( ) ;
return 1 ;
}
session_is_new = true ;
}
2020-06-09 14:19:10 -04:00
/* check if name is legal (error for new sessions only) */
std : : string const & illegal = Session : : session_name_is_legal ( session_name ) ;
if ( ! illegal . empty ( ) & & session_is_new ) {
ArdourMessageDialog msg ( * session_dialog ,
string_compose ( _ ( " To ensure compatibility with various systems \n "
" session names may not contain a '%1' character " ) ,
illegal ) ) ;
msg . run ( ) ;
ARDOUR_COMMAND_LINE : : session_name = " " ; // cancel that
return 1 ; /* keep running dialog */
}
2019-10-09 22:50:34 -04:00
float sr ;
SampleFormat fmt ;
string program_version ;
2019-10-10 17:25:30 -04:00
const string statefile_path = Glib : : build_filename ( session_path , session_name + ARDOUR : : statefile_suffix ) ;
2019-10-09 22:50:34 -04:00
2019-10-10 17:25:30 -04:00
if ( ! session_is_new ) {
2019-10-09 22:50:34 -04:00
2021-02-23 13:26:01 -05:00
if ( Session : : get_info_from_path ( statefile_path , sr , fmt , program_version , & session_engine_hints ) ) {
2019-10-10 17:25:30 -04:00
/* exists but we can't read it */
return - 1 ;
}
session_existing_sample_rate = sr ;
} else {
bus_profile . master_out_channels = session_dialog - > master_channel_count ( ) ;
}
2019-10-09 22:50:34 -04:00
return 0 ;
}
void
StartupFSM : : copy_demo_sessions ( )
{
// TODO: maybe IFF brand_new_user
if ( ARDOUR : : Profile - > get_mixbus ( ) & & Config - > get_copy_demo_sessions ( ) ) {
std : : string dspd ( Config - > get_default_session_parent_dir ( ) ) ;
Searchpath ds ( ARDOUR : : ardour_data_search_path ( ) ) ;
ds . add_subdirectory_to_paths ( " sessions " ) ;
vector < string > demos ;
find_files_matching_pattern ( demos , ds , ARDOUR : : session_archive_suffix ) ;
ARDOUR : : RecentSessions rs ;
ARDOUR : : read_recent_sessions ( rs ) ;
for ( vector < string > : : iterator i = demos . begin ( ) ; i ! = demos . end ( ) ; + + i ) {
/* "demo-session" must be inside "demo-session.<session_archive_suffix>" */
std : : string name = basename_nosuffix ( basename_nosuffix ( * i ) ) ;
std : : string path = Glib : : build_filename ( dspd , name ) ;
/* skip if session-dir already exists */
if ( Glib : : file_test ( path . c_str ( ) , Glib : : FILE_TEST_IS_DIR ) ) {
continue ;
}
/* skip sessions that are already in 'recent'.
* eg . a new user changed < session - default - dir > shorly after installation
*/
for ( ARDOUR : : RecentSessions : : iterator r = rs . begin ( ) ; r ! = rs . end ( ) ; + + r ) {
if ( ( * r ) . first = = name ) {
continue ;
}
}
try {
PBD : : FileArchive ar ( * i ) ;
if ( 0 = = ar . inflate ( dspd ) ) {
store_recent_sessions ( name , path ) ;
info < < string_compose ( _ ( " Copied Demo Session %1. " ) , name ) < < endmsg ;
}
} catch ( . . . ) {
/* relax ? */
}
}
}
}
bool
StartupFSM : : ask_about_loading_existing_session ( const std : : string & session_path )
{
std : : string str = string_compose ( _ ( " This session \n %1 \n already exists. Do you want to open it? " ) , session_path ) ;
2019-12-14 17:31:24 -05:00
ArdourMessageDialog msg ( str ,
false ,
Gtk : : MESSAGE_WARNING ,
Gtk : : BUTTONS_YES_NO ,
true ) ;
2019-10-09 22:50:34 -04:00
msg . set_name ( X_ ( " OpenExistingDialog " ) ) ;
msg . set_title ( _ ( " Open Existing Session " ) ) ;
msg . set_wmclass ( X_ ( " existing_session " ) , PROGRAM_NAME ) ;
msg . set_position ( Gtk : : WIN_POS_CENTER ) ;
switch ( msg . run ( ) ) {
case RESPONSE_YES :
return true ;
break ;
}
return false ;
}
2019-10-10 23:24:52 -04:00
void
StartupFSM : : show_pre_release_dialog ( )
{
pre_release_dialog = new ArdourDialog ( _ ( " Pre-Release Warning " ) , true , false ) ;
pre_release_dialog - > add_button ( Gtk : : Stock : : OK , Gtk : : RESPONSE_OK ) ;
Label * label = manage ( new Label ) ;
label - > set_markup ( string_compose ( _ ( " <span size= \" x-large \" weight= \" bold \" >Welcome to this pre-release build of %1 %2</span> \n \n \
< span size = \ " large \" >There are still several issues and bugs to be worked on, \n \
as well as general workflow improvements , before this can be considered \ n \
release software . So , a few guidelines : \ n \
\ n \
1 ) Please do < b > NOT < / b > use this software with the expectation that it is stable or reliable \ n \
though it may be so , depending on your workflow . \ n \
2 ) Please wait for a helpful writeup of new features . \ n \
3 ) < b > Please do NOT use the forums at ardour . org to report issues < / b > . \ n \
4 ) < b > Please do NOT file bugs for this alpha - development versions at this point in time < / b > . \ n \
There is no bug triaging before the initial development concludes and \ n \
reporting issue for incomplete , ongoing work - in - progress is mostly useless . \ n \
5 ) Please < b > DO < / b > join us on IRC for real time discussions about % 1 % 2. You \ n \
can get there directly from within the program via the Help - > Chat menu option . \ n \
6 ) Please < b > DO < / b > submit patches for issues after discussing them on IRC . \ n \
\ n \
Full information on all the above can be found on the support page at \ n \
\ n \
http : / / ardour . org / support < / span > \ n \
" ), PROGRAM_NAME, VERSIONSTRING));
2019-10-24 23:14:00 -04:00
current_dialog_connection = pre_release_dialog - > signal_response ( ) . connect ( sigc : : bind ( sigc : : mem_fun ( this , & StartupFSM : : dialog_response_handler ) , PreReleaseDialog ) ) ;
2019-10-10 23:24:52 -04:00
pre_release_dialog - > get_vbox ( ) - > set_border_width ( 12 ) ;
pre_release_dialog - > get_vbox ( ) - > pack_start ( * label , false , false , 12 ) ;
pre_release_dialog - > get_vbox ( ) - > show_all ( ) ;
pre_release_dialog - > set_position ( WIN_POS_CENTER ) ;
pre_release_dialog - > present ( ) ;
}
2020-03-01 23:35:44 -05:00
void
StartupFSM : : handle_path ( string const & path )
{
2020-03-06 17:53:31 -05:00
if ( ! ARDOUR_COMMAND_LINE : : session_name . empty ( ) ) {
return ;
2020-03-01 23:35:44 -05:00
}
2020-03-06 17:53:31 -05:00
ARDOUR_COMMAND_LINE : : session_name = path ;
dialog_response_handler ( RESPONSE_OK , ApplicationPseudoDialog ) ;
2020-03-01 23:35:44 -05:00
}