Happy New Year
[ardour.git] / gtk2_ardour / main.cc
1 /*
2     Copyright (C) 2001-2007 Paul Davis
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
15     along with this program; if not, write to the Free Software
16     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17
18 */
19
20 #include <cstdlib>
21 #include <signal.h>
22 #include <cerrno>
23 #include <fstream>
24
25 #include <sigc++/bind.h>
26 #include <gtkmm/settings.h>
27
28 #include "pbd/error.h"
29 #include "pbd/epa.h"
30 #include "pbd/file_utils.h"
31 #include "pbd/textreceiver.h"
32 #include "pbd/failed_constructor.h"
33 #include "pbd/pthread_utils.h"
34 #ifdef BOOST_SP_ENABLE_DEBUG_HOOKS
35 #include "pbd/boost_debug.h"
36 #endif
37
38 #include <jack/jack.h>
39
40 #include "ardour/svn_revision.h"
41 #include "ardour/version.h"
42 #include "ardour/ardour.h"
43 #include "ardour/audioengine.h"
44 #include "ardour/session_utils.h"
45 #include "ardour/filesystem_paths.h"
46
47 #include <gtkmm/main.h>
48 #include <gtkmm2ext/application.h>
49 #include <gtkmm2ext/popup.h>
50 #include <gtkmm2ext/utils.h>
51
52 #include "version.h"
53 #include "utils.h"
54 #include "ardour_ui.h"
55 #include "opts.h"
56 #include "enums.h"
57
58 #include "i18n.h"
59
60 using namespace std;
61 using namespace Gtk;
62 using namespace ARDOUR_COMMAND_LINE;
63 using namespace ARDOUR;
64 using namespace PBD;
65
66 TextReceiver text_receiver ("ardour");
67
68 extern int curvetest (string);
69
70 static ARDOUR_UI  *ui = 0;
71 static const char* localedir = LOCALEDIR;
72
73 void
74 gui_jack_error ()
75 {
76         MessageDialog win (string_compose (_("%1 could not connect to JACK."), PROGRAM_NAME),
77                            false,
78                            Gtk::MESSAGE_INFO,
79                            Gtk::BUTTONS_NONE);
80 win.set_secondary_text(_("There are several possible reasons:\n\
81 \n\
82 1) JACK is not running.\n\
83 2) JACK is running as another user, perhaps root.\n\
84 3) There is already another client called \"ardour\".\n\
85 \n\
86 Please consider the possibilities, and perhaps (re)start JACK."));
87
88         win.add_button (Stock::QUIT, RESPONSE_CLOSE);
89         win.set_default_response (RESPONSE_CLOSE);
90
91         win.show_all ();
92         win.set_position (Gtk::WIN_POS_CENTER);
93
94         if (!no_splash) {
95                 ui->hide_splash ();
96         }
97
98         /* we just don't care about the result, but we want to block */
99
100         win.run ();
101 }
102
103 static void export_search_path (const string& base_dir, const char* varname, const char* dir)
104 {
105         string path;
106         const char * cstr = getenv (varname);
107
108         if (cstr) {
109                 path = cstr;
110                 path += ':';
111         } else {
112                 path = "";
113         }
114         path += base_dir;
115         path += dir;
116
117         setenv (varname, path.c_str(), 1);
118 }
119
120 #ifdef __APPLE__
121
122 #include <mach-o/dyld.h>
123 #include <sys/param.h>
124
125 extern void set_language_preference (); // cocoacarbon.mm
126
127 void
128 fixup_bundle_environment (int, char* [])
129 {
130         if (!getenv ("ARDOUR_BUNDLED")) {
131                 return;
132         }
133
134         EnvironmentalProtectionAgency::set_global_epa (new EnvironmentalProtectionAgency (true, "PREBUNDLE_ENV"));
135
136         set_language_preference ();
137
138         char execpath[MAXPATHLEN+1];
139         uint32_t pathsz = sizeof (execpath);
140
141         _NSGetExecutablePath (execpath, &pathsz);
142
143         std::string dir_path = Glib::path_get_dirname (execpath);
144         std::string path;
145         const char *cstr = getenv ("PATH");
146
147         /* ensure that we find any bundled executables (e.g. JACK),
148            and find them before any instances of the same name
149            elsewhere in PATH
150         */
151
152         path = dir_path;
153
154         /* JACK is often in /usr/local/bin and since Info.plist refuses to
155            set PATH, we have to force this in order to discover a running
156            instance of JACK ...
157         */
158
159         path += ':';
160         path += "/usr/local/bin";
161
162         if (cstr) {
163                 path += ':';
164                 path += cstr;
165         }
166         setenv ("PATH", path.c_str(), 1);
167
168         path = dir_path;
169         path += "/../Resources";
170         path += dir_path;
171         path += "/../Surfaces";
172         path += dir_path;
173         path += "/../Panners";
174
175         setenv ("ARDOUR_MODULE_PATH", path.c_str(), 1);
176
177         path = user_config_directory().to_string();
178         path += ':';
179         path += dir_path;
180         path += "/../Resources/icons:";
181         path += dir_path;
182         path += "/../Resources/pixmaps:";
183         path += dir_path;
184         path += "/../Resources/share:";
185         path += dir_path;
186         path += "/../Resources";
187
188         setenv ("ARDOUR_PATH", path.c_str(), 1);
189         setenv ("ARDOUR_CONFIG_PATH", path.c_str(), 1);
190
191         path = dir_path;
192         path += "/../Resources";
193         setenv ("ARDOUR_INSTANT_XML_PATH", path.c_str(), 1);
194
195         export_search_path (dir_path, "LADSPA_PATH", "/../Plugins");
196         export_search_path (dir_path, "VAMP_PATH", "/../Frameworks");
197         export_search_path (dir_path, "ARDOUR_PANNER_PATH", "/../Panners");
198         export_search_path (dir_path, "ARDOUR_SURFACES_PATH", "/../Surfaces");
199         export_search_path (dir_path, "ARDOUR_MIDIMAPS_PATH", "/../MidiMaps");
200         export_search_path (dir_path, "ARDOUR_EXPORT_FORMATS_PATH", "/../ExportFormats");
201
202         path = dir_path;
203         path += "/../Frameworks/clearlooks";
204
205         setenv ("GTK_PATH", path.c_str(), 1);
206
207         /* unset GTK_RC_FILES so that we only load the RC files that we define
208          */
209
210         unsetenv ("GTK_RC_FILES");
211
212         if (!ARDOUR::translations_are_disabled ()) {
213
214                 path = dir_path;
215                 path += "/../Resources/locale";
216
217                 localedir = strdup (path.c_str());
218                 setenv ("GTK_LOCALEDIR", localedir, 1);
219         }
220
221         /* write a pango.rc file and tell pango to use it. we'd love
222            to put this into the PROGRAM_NAME.app bundle and leave it there,
223            but the user may not have write permission. so ...
224
225            we also have to make sure that the user ardour directory
226            actually exists ...
227         */
228
229         try {
230                 sys::create_directories (user_config_directory ());
231         }
232         catch (const sys::filesystem_error& ex) {
233                 error << _("Could not create user configuration directory") << endmsg;
234         }
235
236         sys::path pangopath = user_config_directory();
237         pangopath /= "pango.rc";
238         path = pangopath.to_string();
239
240         std::ofstream pangorc (path.c_str());
241         if (!pangorc) {
242                 error << string_compose (_("cannot open pango.rc file %1") , path) << endmsg;
243                 return;
244         } else {
245                 pangorc << "[Pango]\nModuleFiles=";
246
247                 pangopath = dir_path;
248                 pangopath /= "..";
249                 pangopath /= "Resources";
250                 pangopath /= "pango.modules";
251
252                 pangorc << pangopath.to_string() << endl;
253
254                 pangorc.close ();
255
256                 setenv ("PANGO_RC_FILE", path.c_str(), 1);
257         }
258
259         // gettext charset aliases
260
261         setenv ("CHARSETALIASDIR", path.c_str(), 1);
262
263         // font config
264
265         path = dir_path;
266         path += "/../Resources/fonts.conf";
267
268         setenv ("FONTCONFIG_FILE", path.c_str(), 1);
269
270         // GDK Pixbuf loader module file
271
272         path = dir_path;
273         path += "/../Resources/gdk-pixbuf.loaders";
274
275         setenv ("GDK_PIXBUF_MODULE_FILE", path.c_str(), 1);
276
277         if (getenv ("ARDOUR_WITH_JACK")) {
278                 // JACK driver dir
279
280                 path = dir_path;
281                 path += "/../Frameworks";
282
283                 setenv ("JACK_DRIVER_DIR", path.c_str(), 1);
284         }
285 }
286
287 #else
288
289 void
290 fixup_bundle_environment (int /*argc*/, char* argv[])
291 {
292         if (!getenv ("ARDOUR_BUNDLED")) {
293                 return;
294         }
295
296         EnvironmentalProtectionAgency::set_global_epa (new EnvironmentalProtectionAgency (true, "PREBUNDLE_ENV"));
297
298         Glib::ustring dir_path = Glib::path_get_dirname (Glib::path_get_dirname (argv[0]));
299         Glib::ustring path;
300         Glib::ustring userconfigdir = user_config_directory().to_string();
301
302         /* ensure that we find any bundled executables (e.g. JACK),
303            and find them before any instances of the same name
304            elsewhere in PATH
305         */
306
307         /* note that this function is POSIX/Linux specific, so using / as
308            a dir separator in this context is just fine.
309         */
310
311         path = dir_path;
312         path += "/etc:";
313         path += dir_path;
314         path += "/lib/surfaces:";
315         path += dir_path;
316         path += "/lib/panners:";
317
318         setenv ("ARDOUR_MODULE_PATH", path.c_str(), 1);
319
320         path = userconfigdir;
321         path += ':';
322         path += dir_path;
323         path += "/etc/icons:";
324         path += dir_path;
325         path += "/etc/pixmaps:";
326         path += dir_path;
327         path += "/share:";
328         path += dir_path;
329         path += "/etc";
330
331         setenv ("ARDOUR_PATH", path.c_str(), 1);
332         setenv ("ARDOUR_CONFIG_PATH", path.c_str(), 1);
333
334         path = dir_path;
335         path += "/etc";
336         setenv ("ARDOUR_INSTANT_XML_PATH", path.c_str(), 1);
337
338         export_search_path (dir_path, "LADSPA_PATH", "/../plugins");
339         export_search_path (dir_path, "VAMP_PATH", "/lib");
340         export_search_path (dir_path, "ARDOUR_PANNER_PATH", "/lib/panners");
341         export_search_path (dir_path, "ARDOUR_SURFACES_PATH", "/lib/surfaces");
342         export_search_path (dir_path, "ARDOUR_MIDIMAPS_PATH", "/share/midi_maps");
343         export_search_path (dir_path, "ARDOUR_EXPORT_FORMATS_PATH", "/share/export");
344
345         path = dir_path;
346         path += "/lib/clearlooks";
347         setenv ("GTK_PATH", path.c_str(), 1);
348
349         /* unset GTK_RC_FILES so that we only load the RC files that we define
350          */
351
352         unsetenv ("GTK_RC_FILES");
353
354         if (!ARDOUR::translations_are_disabled ()) {
355                 path = dir_path;
356                 path += "/share/locale";
357
358                 localedir = strdup (path.c_str());
359                 setenv ("GTK_LOCALEDIR", localedir, 1);
360         }
361
362         /* write a pango.rc file and tell pango to use it. we'd love
363            to put this into the Ardour.app bundle and leave it there,
364            but the user may not have write permission. so ...
365
366            we also have to make sure that the user ardour directory
367            actually exists ...
368         */
369
370         if (g_mkdir_with_parents (userconfigdir.c_str(), 0755) < 0) {
371                 error << string_compose (_("cannot create user ardour folder %1 (%2)"), userconfigdir, strerror (errno))
372                       << endmsg;
373         } else {
374
375                 Glib::ustring mpath;
376
377                 path = Glib::build_filename (userconfigdir, "pango.rc");
378
379                 std::ofstream pangorc (path.c_str());
380                 if (!pangorc) {
381                         error << string_compose (_("cannot open pango.rc file %1") , path) << endmsg;
382                 } else {
383                         mpath = Glib::build_filename (userconfigdir, "pango.modules");
384
385                         pangorc << "[Pango]\nModuleFiles=";
386                         pangorc << mpath << endl;
387                         pangorc.close ();
388                 }
389
390                 setenv ("PANGO_RC_FILE", path.c_str(), 1);
391
392                 /* similar for GDK pixbuf loaders, but there's no RC file required
393                    to specify where it lives.
394                 */
395
396                 mpath = Glib::build_filename (userconfigdir, "gdk-pixbuf.loaders");
397                 setenv ("GDK_PIXBUF_MODULE_FILE", mpath.c_str(), 1);
398         }
399 }
400
401 #endif
402
403 static gboolean
404 tell_about_jack_death (void* /* ignored */)
405 {
406         if (AudioEngine::instance()->processed_frames() == 0) {
407                 /* died during startup */
408                 MessageDialog msg (_("JACK exited"), false);
409                 msg.set_position (Gtk::WIN_POS_CENTER);
410                 msg.set_secondary_text (string_compose (_(
411 "JACK exited unexpectedly, and without notifying %1.\n\
412 \n\
413 This could be due to misconfiguration or to an error inside JACK.\n\
414 \n\
415 Click OK to exit %1."), PROGRAM_NAME));
416
417                 msg.run ();
418                 _exit (0);
419
420         } else {
421
422                 /* engine has already run, so this is a mid-session JACK death */
423
424                 MessageDialog* msg = manage (new MessageDialog (_("JACK exited"), false));
425                 msg->set_secondary_text (string_compose (_(
426 "JACK exited unexpectedly, and without notifying %1.\n\
427 \n\
428 This is probably due to an error inside JACK. You should restart JACK\n\
429 and reconnect %1 to it, or exit %1 now. You cannot save your\n\
430 session at this time, because we would lose your connection information.\n"), PROGRAM_NAME));
431                 msg->present ();
432         }
433         return false; /* do not call again */
434 }
435
436 static void
437 sigpipe_handler (int /*signal*/)
438 {
439         /* XXX fix this so that we do this again after a reconnect to JACK
440          */
441
442         static bool done_the_jack_thing = false;
443
444         if (!done_the_jack_thing) {
445                 AudioEngine::instance()->died ();
446                 g_idle_add (tell_about_jack_death, 0);
447                 done_the_jack_thing =  true;
448         }
449 }
450
451 #ifdef HAVE_LV2
452 void close_external_ui_windows();
453 #endif
454
455 #ifdef WINDOWS_VST_SUPPORT
456
457 extern int windows_vst_gui_init (int* argc, char** argv[]);
458
459 /* this is called from the entry point of a wine-compiled
460    executable that is linked against gtk2_ardour built
461    as a shared library.
462 */
463 extern "C" {
464 int ardour_main (int argc, char *argv[])
465 #else
466 int main (int argc, char *argv[])
467 #endif
468 {
469         fixup_bundle_environment (argc, argv);
470
471         if (!Glib::thread_supported()) {
472                 Glib::thread_init();
473         }
474
475         gtk_set_locale ();
476
477 #ifdef WINDOWS_VST_SUPPORT
478         /* this does some magic that is needed to make GTK and Wine's own
479            X11 client interact properly.
480         */
481         windows_vst_gui_init (&argc, &argv);
482 #endif
483
484         (void) bindtextdomain (PACKAGE, localedir);
485         /* our i18n translations are all in UTF-8, so make sure
486            that even if the user locale doesn't specify UTF-8,
487            we use that when handling them.
488         */
489         (void) bind_textdomain_codeset (PACKAGE,"UTF-8");
490         (void) textdomain (PACKAGE);
491
492         pthread_setcanceltype (PTHREAD_CANCEL_ASYNCHRONOUS, 0);
493
494         // catch error message system signals ();
495
496         text_receiver.listen_to (error);
497         text_receiver.listen_to (info);
498         text_receiver.listen_to (fatal);
499         text_receiver.listen_to (warning);
500
501 #ifdef BOOST_SP_ENABLE_DEBUG_HOOKS
502         if (getenv ("BOOST_DEBUG")) {
503                 boost_debug_shared_ptr_show_live_debugging (true);
504         }
505 #endif
506
507         if (parse_opts (argc, argv)) {
508                 exit (1);
509         }
510
511         if (curvetest_file) {
512                 return curvetest (curvetest_file);
513         }
514
515         cout << PROGRAM_NAME
516              << VERSIONSTRING
517              << _(" (built using ")
518              << svn_revision
519 #ifdef __GNUC__
520              << _(" and GCC version ") << __VERSION__
521 #endif
522              << ')'
523              << endl;
524
525         if (just_version) {
526                 exit (0);
527         }
528
529         if (no_splash) {
530                 cerr << _("Copyright (C) 1999-2012 Paul Davis") << endl
531                      << _("Some portions Copyright (C) Steve Harris, Ari Johnson, Brett Viren, Joel Baker") << endl
532                      << endl
533                      << string_compose (_("%1 comes with ABSOLUTELY NO WARRANTY"), PROGRAM_NAME) << endl
534                      << _("not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.") << endl
535                      << _("This is free software, and you are welcome to redistribute it ") << endl
536                      << _("under certain conditions; see the source for copying conditions.")
537                      << endl;
538         }
539
540         /* some GUI objects need this */
541
542         PBD::ID::init ();
543
544         if (::signal (SIGPIPE, sigpipe_handler)) {
545                 cerr << _("Cannot xinstall SIGPIPE error handler") << endl;
546         }
547
548         try {
549                 ui = new ARDOUR_UI (&argc, &argv);
550         } catch (failed_constructor& err) {
551                 error << _("could not create ARDOUR GUI") << endmsg;
552                 exit (1);
553         }
554
555         ui->run (text_receiver);
556         Gtkmm2ext::Application::instance()->cleanup();
557         ui = 0;
558
559         ARDOUR::cleanup ();
560         pthread_cancel_all ();
561
562 #ifdef HAVE_LV2
563         close_external_ui_windows();
564 #endif
565         return 0;
566 }
567 #ifdef WINDOWS_VST_SUPPORT
568 } // end of extern C block
569 #endif
570