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