1 /*
2  * Copyright (C) 2005-2006 Taybin Rutkin <taybin@taybin.com>
3  * Copyright (C) 2005-2007 Doug McLain <doug@nostar.net>
4  * Copyright (C) 2005-2019 Paul Davis <paul@linuxaudiosystems.com>
5  * Copyright (C) 2006-2011 David Robillard <d@drobilla.net>
6  * Copyright (C) 2006-2015 Tim Mayberry <mojofunk@gmail.com>
7  * Copyright (C) 2006 Nick Mainsbridge <mainsbridge@gmail.com>
8  * Copyright (C) 2007-2012 Carl Hetherington <carl@carlh.net>
9  * Copyright (C) 2008-2009 Hans Baier <hansfbaier@googlemail.com>
10  * Copyright (C) 2013-2017 John Emmas <john@creativepost.co.uk>
11  * Copyright (C) 2013-2019 Robin Gareus <robin@gareus.org>
12  *
13  * This program is free software; you can redistribute it and/or modify
14  * it under the terms of the GNU General Public License as published by
15  * the Free Software Foundation; either version 2 of the License, or
16  * (at your option) any later version.
17  *
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License along
24  * with this program; if not, write to the Free Software Foundation, Inc.,
25  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
26  */
27 
28 #include <cstdlib>
29 #include <cerrno>
30 #include <vector>
31 
32 #include <signal.h>
33 #include <locale.h>
34 
35 #include <sigc++/bind.h>
36 #include <gtkmm/settings.h>
37 
38 #include <curl/curl.h>
39 
40 #include "pbd/error.h"
41 #include "pbd/file_utils.h"
42 #include "pbd/textreceiver.h"
43 #include "pbd/failed_constructor.h"
44 #include "pbd/pathexpand.h"
45 #include "pbd/pthread_utils.h"
46 #include "pbd/win_console.h"
47 #ifdef BOOST_SP_ENABLE_DEBUG_HOOKS
48 #include "pbd/boost_debug.h"
49 #endif
50 
51 #include "ardour/revision.h"
52 #include "ardour/ardour.h"
53 #include "ardour/audioengine.h"
54 #include "ardour/session_utils.h"
55 #include "ardour/filesystem_paths.h"
56 
57 #include <gtkmm/main.h>
58 #include <gtkmm/stock.h>
59 
60 #include <gtkmm2ext/application.h>
61 #include <gtkmm2ext/utils.h>
62 
63 #include "ardour_message.h"
64 #include "ardour_ui.h"
65 #include "ui_config.h"
66 #include "opts.h"
67 #include "enums.h"
68 #include "bundle_env.h"
69 
70 #include "pbd/i18n.h"
71 
72 #ifdef PLATFORM_WINDOWS
73 #include <fcntl.h> // Needed for '_fmode'
74 #include <shellapi.h> // console
75 #endif
76 
77 #ifdef WAF_BUILD
78 #include "gtk2ardour-version.h"
79 #endif
80 
81 #ifdef LXVST_SUPPORT
82 #include <gdk/gdkx.h>
83 #endif
84 
85 using namespace std;
86 using namespace Gtk;
87 using namespace ARDOUR_COMMAND_LINE;
88 using namespace ARDOUR;
89 using namespace PBD;
90 
91 TextReceiver text_receiver (PROGRAM_NAME);
92 
93 extern int curvetest (string);
94 
95 static ARDOUR_UI  *ui = 0;
96 static string localedir (LOCALEDIR);
97 
98 void
gui_jack_error()99 gui_jack_error ()
100 {
101 	ArdourMessageDialog win (string_compose (_("%1 could not connect to the audio backend."), PROGRAM_NAME),
102 	                         false,
103 	                         Gtk::MESSAGE_INFO,
104 	                         Gtk::BUTTONS_NONE);
105 
106 	win.add_button (Stock::QUIT, RESPONSE_CLOSE);
107 	win.set_default_response (RESPONSE_CLOSE);
108 
109 	win.show_all ();
110 	win.set_position (Gtk::WIN_POS_CENTER);
111 
112 	if (!no_splash) {
113 		ui->hide_splash ();
114 	}
115 
116 	/* we just don't care about the result, but we want to block */
117 
118 	win.run ();
119 }
120 
121 #ifndef NDEBUG
ardour_g_log(const gchar * log_domain,GLogLevelFlags log_level,const gchar * message,gpointer user_data)122 static void ardour_g_log (const gchar *log_domain, GLogLevelFlags log_level, const gchar *message, gpointer user_data) {
123 	switch (log_level) {
124 		case G_LOG_FLAG_FATAL:
125 		case G_LOG_LEVEL_CRITICAL:
126 			fatal << "g_log: " << message << endmsg;
127 			break;
128 		case G_LOG_LEVEL_ERROR:
129 			error << "g_log: " << message << endmsg;
130 			break;
131 		case G_LOG_LEVEL_WARNING:
132 			warning << "g_log: " << message << endmsg;
133 			break;
134 		case G_LOG_LEVEL_MESSAGE:
135 		case G_LOG_LEVEL_INFO:
136 		default:
137 			info << "g_log: " << message << endmsg;
138 			break;
139 	}
140 }
141 #endif
142 
143 static gboolean
tell_about_backend_death(void *)144 tell_about_backend_death (void* /* ignored */)
145 {
146 	if (AudioEngine::instance()->processed_samples() == 0) {
147 		/* died during startup */
148 		ArdourMessageDialog msg (string_compose (_("The audio backend (%1) has failed, or terminated"), AudioEngine::instance()->current_backend_name()), false);
149 		msg.set_position (Gtk::WIN_POS_CENTER);
150 		msg.set_secondary_text (string_compose (_(
151 "%2 exited unexpectedly, and without notifying %1.\n\
152 \n\
153 This could be due to misconfiguration or to an error inside %2.\n\
154 \n\
155 Click OK to exit %1."), PROGRAM_NAME, AudioEngine::instance()->current_backend_name()));
156 
157 		msg.run ();
158 		_exit (EXIT_SUCCESS);
159 
160 	} else {
161 
162 		/* engine has already run, so this is a mid-session backend death */
163 
164 		ArdourMessageDialog msg (string_compose (_("The audio backend (%1) has failed, or terminated"), AudioEngine::instance()->current_backend_name()), false);
165 		msg.set_secondary_text (string_compose (_("%2 exited unexpectedly, and without notifying %1."),
166 							 PROGRAM_NAME, AudioEngine::instance()->current_backend_name()));
167 		msg.run ();
168 	}
169 	return false; /* do not call again */
170 }
171 
172 #ifndef PLATFORM_WINDOWS
173 static void
sigpipe_handler(int)174 sigpipe_handler (int /*signal*/)
175 {
176 	/* XXX fix this so that we do this again after a reconnect to the backend */
177 
178 	static bool done_the_backend_thing = false;
179 
180 	if (!done_the_backend_thing) {
181 		AudioEngine::instance()->died ();
182 		g_idle_add (tell_about_backend_death, 0);
183 		done_the_backend_thing =  true;
184 	}
185 }
186 #endif
187 
188 #if (!defined COMPILER_MSVC && defined PLATFORM_WINDOWS)
189 
command_line_parse_error(int * argc,char ** argv[])190 static void command_line_parse_error (int *argc, char** argv[]) {}
191 
192 #elif (defined(COMPILER_MSVC) && defined(NDEBUG) && !defined(RDC_BUILD))
193 
command_line_parse_error(int * argc,char ** argv[])194 static void command_line_parse_error (int *argc, char** argv[]) {
195 	// Since we don't ordinarily have access to stdout and stderr with
196 	// an MSVC app, let the user know we encountered a parsing error.
197 	Gtk::Main app(argc, argv); // Calls 'gtk_init()'
198 
199 	ArdourMessageDialog dlgReportParseError (string_compose (_("\n   %1 could not understand your command line      "), PROGRAM_NAME),
200 			false, MESSAGE_ERROR, BUTTONS_CLOSE, true);
201 	dlgReportParseError.set_title (string_compose (_("An error was encountered while launching %1"), PROGRAM_NAME));
202 	dlgReportParseError.run ();
203 }
204 
205 #else
command_line_parse_error(int * argc,char ** argv[])206 static void command_line_parse_error (int *argc, char** argv[]) {}
207 #endif
208 
209 #if (defined(COMPILER_MSVC) && defined(NDEBUG) && !defined(RDC_BUILD))
210 /*
211  *  Release build with MSVC uses ardour_main()
212  */
213 int ardour_main (int argc, char *argv[])
214 
215 #elif (defined WINDOWS_VST_SUPPORT && !defined PLATFORM_WINDOWS)
216 
217 // prototype for function in windows_vst_plugin_ui.cc
218 extern int windows_vst_gui_init (int* argc, char** argv[]);
219 
220 /* this is called from the entry point of a wine-compiled
221    executable that is linked against gtk2_ardour built
222    as a shared library.
223 */
224 extern "C" {
225 
ardour_main(int argc,char * argv[])226 int ardour_main (int argc, char *argv[])
227 
228 #elif defined NOMAIN
229 int nomain (int argc, char *argv[])
230 #else
231 int main (int argc, char *argv[])
232 #endif
233 {
234 	console_madness_begin();
235 
236 	ARDOUR::check_for_old_configuration_files();
237 
238 	/* global init is not thread safe.*/
239 	if (curl_global_init (CURL_GLOBAL_DEFAULT)) {
240 		cerr << "curl_global_init() failed. The web is gone. We're all doomed." << endl;
241 	}
242 
243 	fixup_bundle_environment (argc, argv, localedir);
244 
245 	load_custom_fonts(); /* needs to happen before any gtk and pango init calls */
246 
247 	if (!Glib::thread_supported()) {
248 		Glib::thread_init();
249 	}
250 
251 #ifdef LXVST_SUPPORT
252 	XInitThreads ();
253 #endif
254 
255 #if ENABLE_NLS
256 	/* initialize C locale to user preference */
257 	if (ARDOUR::translations_are_enabled ()) {
258 		if (!setlocale (LC_ALL, "")) {
259 			std::cerr << "localization call failed, " << PROGRAM_NAME << " will not be translated\n";
260 		}
261 	}
262 #endif
263 
264 #if (defined WINDOWS_VST_SUPPORT && !defined PLATFORM_WINDOWS)
265 	/* this does some magic that is needed to make GTK and X11 client interact properly.
266 	 * the platform dependent code is in windows_vst_plugin_ui.cc
267 	 */
268 	windows_vst_gui_init (&argc, &argv);
269 #endif
270 
271 #if ENABLE_NLS
272 
273 #ifndef NDEBUG
274 	cerr << "bind txt domain [" << PACKAGE << "] to " << localedir << endl;
275 #endif
276 
277 	(void) bindtextdomain (PACKAGE, localedir.c_str());
278 	/* our i18n translations are all in UTF-8, so make sure
279 	   that even if the user locale doesn't specify UTF-8,
280 	   we use that when handling them.
281 	*/
282 	(void) bind_textdomain_codeset (PACKAGE,"UTF-8");
283 #endif
284 
285 	pthread_setcanceltype (PTHREAD_CANCEL_ASYNCHRONOUS, 0);
286 
287 	// catch error message system signals ();
288 
289 	text_receiver.listen_to (debug);
290 	text_receiver.listen_to (info);
291 	text_receiver.listen_to (warning);
292 	text_receiver.listen_to (error);
293 	text_receiver.listen_to (fatal);
294 
295 #ifdef BOOST_SP_ENABLE_DEBUG_HOOKS
296 	if (g_getenv ("BOOST_DEBUG")) {
297 		boost_debug_shared_ptr_show_live_debugging (true);
298 	}
299 #endif
300 
301 	if (parse_opts (argc, argv)) {
302 		command_line_parse_error (&argc, &argv);
303 		exit (EXIT_FAILURE);
304 	}
305 
306 	{
307 #ifndef NDEBUG
308 		const char *adf;
309 		if ((adf = g_getenv ("ARDOUR_DEBUG_FLAGS"))) {
310 			PBD::parse_debug_options (adf);
311 		}
312 #endif /* NDEBUG */
313 	}
314 
315 	cout << PROGRAM_NAME
316 	     << VERSIONSTRING
317 	     << _(" (built using ")
318 	     << revision
319 #ifdef __GNUC__
320 	     << _(" and GCC version ") << __VERSION__
321 #endif
322 	     << ')'
323 	     << endl;
324 
325 	if (just_version) {
326 		exit (EXIT_SUCCESS);
327 	}
328 
329 	if (no_splash) {
330 		cout << _("Copyright (C) 1999-2021 Paul Davis") << endl
331 		     << _("Some portions Copyright (C) Steve Harris, Ari Johnson, Brett Viren, Joel Baker, Robin Gareus") << endl
332 		     << endl
333 		     << string_compose (_("%1 comes with ABSOLUTELY NO WARRANTY"), PROGRAM_NAME) << endl
334 		     << _("not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.") << endl
335 		     << _("This is free software, and you are welcome to redistribute it ") << endl
336 		     << _("under certain conditions; see the source for copying conditions.")
337 		     << endl;
338 	}
339 
340 #ifdef HAVE_DRMINGW
341 	/* prevent missing libs popups */
342 	UINT prev_error_mode = SetErrorMode (SEM_FAILCRITICALERRORS);
343 	SetErrorMode (prev_error_mode | SEM_FAILCRITICALERRORS);
344 	HMODULE exchndl = LoadLibraryA ("exchndl.dll");
345 
346 	if (exchndl) {
347 		/* %localappdata%\Ardour<X>\CrashLog\ */
348 		string crash_dir = Glib::build_filename (Glib::get_user_data_dir (), string_compose ("%1%2", PROGRAM_NAME, PROGRAM_VERSION), "CrashLog");
349 		g_mkdir_with_parents (crash_dir.c_str(), 0700);
350 
351 		Glib::DateTime tm (g_date_time_new_now_local ());
352 		string crash_file = string_compose ("%1-%2-crash-%3.txt", PROGRAM_NAME, VERSIONSTRING, tm.format ("%s"));
353 		string crash_path = Glib::build_filename (crash_dir, crash_file);
354 
355 		typedef void (*exc_init_fn_t) (void);
356 		typedef bool (*exc_path_fn_t) (const char *);
357 
358 		exc_init_fn_t exchndl_init = (exc_init_fn_t) GetProcAddress (exchndl, "ExcHndlInit");
359 		exc_path_fn_t exchndl_path = (exc_path_fn_t) GetProcAddress (exchndl, "ExcHndlSetLogFileNameA");
360 
361 		if (exchndl_init && exchndl_path) {
362 			exchndl_init ();
363 			exchndl_path (crash_path.c_str());
364 			cout << "Crash Log: " << crash_path << endl;
365 		} else {
366 			cout << "Cannot initialize crash reporter" << endl;
367 		}
368 	} else {
369 		cout << "Crash reporter is not compatible with this system" << endl;
370 	}
371 	SetErrorMode (prev_error_mode);
372 #endif
373 
374 	if (!ARDOUR::init (ARDOUR_COMMAND_LINE::use_vst, ARDOUR_COMMAND_LINE::try_hw_optimization, localedir.c_str(), true)) {
375 		error << string_compose (_("could not initialize %1."), PROGRAM_NAME) << endmsg;
376 		Gtk::Main main (argc, argv);
377 		Gtk::MessageDialog msg (string_compose (_("Could not initialize %1 (likely due to corrupt config files).\n"
378 		                                          "Run %1 from a commandline for more information."), PROGRAM_NAME),
379 		                        false, Gtk::MESSAGE_ERROR , Gtk::BUTTONS_OK, true);
380 		msg.run ();
381 		exit (EXIT_FAILURE);
382 	}
383 
384 #ifndef PLATFORM_WINDOWS
385 	if (::signal (SIGPIPE, sigpipe_handler)) {
386 		cerr << _("Cannot xinstall SIGPIPE error handler") << endl;
387 	}
388 #endif
389 
390 	DEBUG_TRACE (DEBUG::Locale, string_compose ("main() locale '%1'\n", setlocale (LC_NUMERIC, NULL)));
391 
392 	if (UIConfiguration::instance().pre_gui_init ()) {
393 		error << _("Could not complete pre-GUI initialization") << endmsg;
394 		exit (EXIT_FAILURE);
395 	}
396 
397 	try {
398 		ui = new ARDOUR_UI (&argc, &argv, localedir.c_str());
399 	} catch (failed_constructor& err) {
400 		error << string_compose (_("could not create %1 GUI"), PROGRAM_NAME) << endmsg;
401 		exit (EXIT_FAILURE);
402 	}
403 
404 #ifndef NDEBUG
405 	g_log_set_handler (NULL,
406 			GLogLevelFlags (G_LOG_LEVEL_WARNING | G_LOG_FLAG_FATAL |  G_LOG_LEVEL_CRITICAL | G_LOG_FLAG_RECURSION),
407 			&ardour_g_log, NULL);
408 #endif
409 
410 	ui->run (text_receiver);
411 	Gtkmm2ext::Application::instance()->cleanup();
412 	delete ui;
413 	ui = 0;
414 
415 	ARDOUR::cleanup ();
416 #ifndef NDEBUG
417 	if (getenv ("ARDOUR_RUNNING_UNDER_VALGRIND")) {
418 		Glib::usleep(100000);
419 		sched_yield();
420 	}
421 #endif
422 
423 	pthread_cancel_all ();
424 
425 #ifndef NDEBUG
426 	if (getenv ("ARDOUR_RUNNING_UNDER_VALGRIND")) {
427 		Glib::usleep(100000);
428 		sched_yield();
429 	}
430 #endif
431 
432 	console_madness_end ();
433 
434 	return 0;
435 }
436 #if (defined WINDOWS_VST_SUPPORT && !defined PLATFORM_WINDOWS)
437 } // end of extern "C" block
438 #endif
439