fix bundled paths for OS X bundle
[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::ButtonsType)(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 exec_path (execpath);
144         std::string dir_path = Glib::path_get_dirname (exec_path);
145         std::string path;
146         const char *cstr = getenv ("PATH");
147
148         /* ensure that we find any bundled executables (e.g. JACK),
149            and find them before any instances of the same name
150            elsewhere in PATH
151         */
152
153         path = dir_path;
154
155         /* JACK is often in /usr/local/bin and since Info.plist refuses to
156            set PATH, we have to force this in order to discover a running
157            instance of JACK ...
158         */
159
160         path += ':';
161         path += "/usr/local/bin";
162
163         if (cstr) {
164                 path += ':';
165                 path += cstr;
166         }
167         setenv ("PATH", path.c_str(), 1);
168
169         path = dir_path;
170         path += "/../Resources";
171         path += dir_path;
172         path += "/../Resources/Surfaces";
173         path += dir_path;
174         path += "/../Resources/Panners";
175
176         setenv ("ARDOUR_MODULE_PATH", path.c_str(), 1);
177
178         path = user_config_directory().to_string();
179         path += ':';
180         path += dir_path;
181         path += "/../Resources/icons:";
182         path += dir_path;
183         path += "/../Resources/pixmaps:";
184         path += dir_path;
185         path += "/../Resources/share:";
186         path += dir_path;
187         path += "/../Resources";
188
189         setenv ("ARDOUR_PATH", path.c_str(), 1);
190         setenv ("ARDOUR_CONFIG_PATH", path.c_str(), 1);
191
192         path = dir_path;
193         path += "/../Resources";
194         setenv ("ARDOUR_INSTANT_XML_PATH", path.c_str(), 1);
195
196         export_search_path (dir_path, "LADSPA_PATH", "/../Plugins");
197         export_search_path (dir_path, "VAMP_PATH", "/../Frameworks");
198         export_search_path (dir_path, "ARDOUR_PANNER_PATH", "/../Panners");
199         export_search_path (dir_path, "ARDOUR_SURFACES_PATH", "/../Surfaces");
200         export_search_path (dir_path, "ARDOUR_MIDIMAPS_PATH", "/../MidiMaps");
201         export_search_path (dir_path, "ARDOUR_EXPORT_FORMATS_PATH", "/../ExportFormats");
202
203         path = dir_path;
204         path += "/../Frameworks/clearlooks";
205
206         setenv ("GTK_PATH", path.c_str(), 1);
207
208         /* unset GTK_RC_FILES so that we only load the RC files that we define
209          */
210
211         unsetenv ("GTK_RC_FILES");
212
213         if (!ARDOUR::translations_are_disabled ()) {
214
215                 path = dir_path;
216                 path += "/../Resources/locale";
217
218                 localedir = strdup (path.c_str());
219                 setenv ("GTK_LOCALEDIR", localedir, 1);
220         }
221
222         /* write a pango.rc file and tell pango to use it. we'd love
223            to put this into the PROGRAM_NAME.app bundle and leave it there,
224            but the user may not have write permission. so ...
225
226            we also have to make sure that the user ardour directory
227            actually exists ...
228         */
229
230         try {
231                 sys::create_directories (user_config_directory ());
232         }
233         catch (const sys::filesystem_error& ex) {
234                 error << _("Could not create user configuration directory") << endmsg;
235         }
236
237         sys::path pangopath = user_config_directory();
238         pangopath /= "pango.rc";
239         path = pangopath.to_string();
240
241         std::ofstream pangorc (path.c_str());
242         if (!pangorc) {
243                 error << string_compose (_("cannot open pango.rc file %1") , path) << endmsg;
244                 return;
245         } else {
246                 pangorc << "[Pango]\nModuleFiles=";
247
248                 pangopath = dir_path;
249                 pangopath /= "..";
250                 pangopath /= "Resources";
251                 pangopath /= "pango.modules";
252
253                 pangorc << pangopath.to_string() << endl;
254
255                 pangorc.close ();
256
257                 setenv ("PANGO_RC_FILE", path.c_str(), 1);
258         }
259
260         // gettext charset aliases
261
262         setenv ("CHARSETALIASDIR", path.c_str(), 1);
263
264         // font config
265
266         path = dir_path;
267         path += "/../Resources/fonts.conf";
268
269         setenv ("FONTCONFIG_FILE", path.c_str(), 1);
270
271         // GDK Pixbuf loader module file
272
273         path = dir_path;
274         path += "/../Resources/gdk-pixbuf.loaders";
275
276         setenv ("GDK_PIXBUF_MODULE_FILE", path.c_str(), 1);
277
278         if (getenv ("ARDOUR_WITH_JACK")) {
279                 // JACK driver dir
280
281                 path = dir_path;
282                 path += "/../Frameworks";
283
284                 setenv ("JACK_DRIVER_DIR", path.c_str(), 1);
285         }
286 }
287
288 #else
289
290 void
291 fixup_bundle_environment (int /*argc*/, char* argv[])
292 {
293         if (!getenv ("ARDOUR_BUNDLED")) {
294                 return;
295         }
296
297         EnvironmentalProtectionAgency::set_global_epa (new EnvironmentalProtectionAgency (true, "PREBUNDLE_ENV"));
298
299         Glib::ustring exec_path = argv[0];
300         Glib::ustring dir_path = Glib::path_get_dirname (Glib::path_get_dirname (exec_path));
301         Glib::ustring path;
302         Glib::ustring userconfigdir = user_config_directory().to_string();
303
304         /* ensure that we find any bundled executables (e.g. JACK),
305            and find them before any instances of the same name
306            elsewhere in PATH
307         */
308
309         /* note that this function is POSIX/Linux specific, so using / as
310            a dir separator in this context is just fine.
311         */
312
313         path = dir_path;
314         path += "/etc:";
315         path += dir_path;
316         path += "/lib/surfaces:";
317         path += dir_path;
318         path += "/lib/panners:";
319
320         setenv ("ARDOUR_MODULE_PATH", path.c_str(), 1);
321
322         path = userconfigdir;
323         path += ':';
324         path += dir_path;
325         path += "/etc/icons:";
326         path += dir_path;
327         path += "/etc/pixmaps:";
328         path += dir_path;
329         path += "/share:";
330         path += dir_path;
331         path += "/etc";
332
333         setenv ("ARDOUR_PATH", path.c_str(), 1);
334         setenv ("ARDOUR_CONFIG_PATH", path.c_str(), 1);
335
336         path = dir_path;
337         path += "/etc";
338         setenv ("ARDOUR_INSTANT_XML_PATH", path.c_str(), 1);
339
340         export_search_path (dir_path, "LADSPA_PATH", "/../plugins");
341         export_search_path (dir_path, "VAMP_PATH", "/lib");
342         export_search_path (dir_path, "ARDOUR_PANNER_PATH", "/lib/panners");
343         export_search_path (dir_path, "ARDOUR_SURFACES_PATH", "/lib/surfaces");
344         export_search_path (dir_path, "ARDOUR_MIDIMAPS_PATH", "/share/midi_maps");
345         export_search_path (dir_path, "ARDOUR_EXPORT_FORMATS_PATH", "/share/export");
346
347         path = dir_path;
348         path += "/lib/clearlooks";
349         setenv ("GTK_PATH", path.c_str(), 1);
350
351         /* unset GTK_RC_FILES so that we only load the RC files that we define
352          */
353
354         unsetenv ("GTK_RC_FILES");
355
356         if (!ARDOUR::translations_are_disabled ()) {
357                 path = dir_path;
358                 path += "/share/locale";
359
360                 localedir = strdup (path.c_str());
361                 setenv ("GTK_LOCALEDIR", localedir, 1);
362         }
363
364         /* write a pango.rc file and tell pango to use it. we'd love
365            to put this into the Ardour.app bundle and leave it there,
366            but the user may not have write permission. so ...
367
368            we also have to make sure that the user ardour directory
369            actually exists ...
370         */
371
372         if (g_mkdir_with_parents (userconfigdir.c_str(), 0755) < 0) {
373                 error << string_compose (_("cannot create user ardour folder %1 (%2)"), userconfigdir, strerror (errno))
374                       << endmsg;
375         } else {
376
377                 Glib::ustring mpath;
378
379                 path = Glib::build_filename (userconfigdir, "pango.rc");
380
381                 std::ofstream pangorc (path.c_str());
382                 if (!pangorc) {
383                         error << string_compose (_("cannot open pango.rc file %1") , path) << endmsg;
384                 } else {
385                         mpath = Glib::build_filename (userconfigdir, "pango.modules");
386
387                         pangorc << "[Pango]\nModuleFiles=";
388                         pangorc << mpath << endl;
389                         pangorc.close ();
390                 }
391
392                 setenv ("PANGO_RC_FILE", path.c_str(), 1);
393
394                 /* similar for GDK pixbuf loaders, but there's no RC file required
395                    to specify where it lives.
396                 */
397
398                 mpath = Glib::build_filename (userconfigdir, "gdk-pixbuf.loaders");
399                 setenv ("GDK_PIXBUF_MODULE_FILE", mpath.c_str(), 1);
400         }
401 }
402
403 #endif
404
405 static gboolean
406 tell_about_jack_death (void* /* ignored */)
407 {
408         if (AudioEngine::instance()->processed_frames() == 0) {
409                 /* died during startup */
410                 MessageDialog msg (_("JACK exited"), false, Gtk::MESSAGE_INFO, Gtk::BUTTONS_OK);
411                 msg.set_position (Gtk::WIN_POS_CENTER);
412                 msg.set_secondary_text (string_compose (_(
413 "JACK exited unexpectedly, and without notifying %1.\n\
414 \n\
415 This could be due to misconfiguration or to an error inside JACK.\n\
416 \n\
417 Click OK to exit %1."), PROGRAM_NAME));
418
419                 msg.run ();
420                 _exit (0);
421
422         } else {
423
424                 /* engine has already run, so this is a mid-session JACK death */
425
426                 MessageDialog* msg = manage (new MessageDialog (_("JACK exited"), false, Gtk::MESSAGE_INFO, Gtk::BUTTONS_NONE));
427                 msg->set_secondary_text (string_compose (_(
428 "JACK exited unexpectedly, and without notifying %1.\n\
429 \n\
430 This is probably due to an error inside JACK. You should restart JACK\n\
431 and reconnect %1 to it, or exit %1 now. You cannot save your\n\
432 session at this time, because we would lose your connection information.\n"), PROGRAM_NAME));
433                 msg->present ();
434         }
435         return false; /* do not call again */
436 }
437
438 static void
439 sigpipe_handler (int /*signal*/)
440 {
441         /* XXX fix this so that we do this again after a reconnect to JACK
442          */
443
444         static bool done_the_jack_thing = false;
445
446         if (!done_the_jack_thing) {
447                 AudioEngine::instance()->died ();
448                 g_idle_add (tell_about_jack_death, 0);
449                 done_the_jack_thing =  true;
450         }
451 }
452
453 #ifdef HAVE_LV2
454 void close_external_ui_windows();
455 #endif
456
457 #ifdef VST_SUPPORT
458
459 extern int gui_init (int* argc, char** argv[]);
460
461 /* this is called from the entry point of a wine-compiled
462    executable that is linked against gtk2_ardour built
463    as a shared library.
464 */
465 extern "C" {
466 int ardour_main (int argc, char *argv[])
467 #else
468 int main (int argc, char *argv[])
469 #endif
470 {
471         fixup_bundle_environment (argc, argv);
472
473         if (!Glib::thread_supported()) {
474                 Glib::thread_init();
475         }
476
477         gtk_set_locale ();
478
479 #ifdef VST_SUPPORT
480         /* this does some magic that is needed to make GTK and Wine's own
481            X11 client interact properly.
482         */
483         gui_init (&argc, &argv);
484 #endif
485
486         (void) bindtextdomain (PACKAGE, localedir);
487         /* our i18n translations are all in UTF-8, so make sure
488            that even if the user locale doesn't specify UTF-8,
489            we use that when handling them.
490         */
491         (void) bind_textdomain_codeset (PACKAGE,"UTF-8");
492         (void) textdomain (PACKAGE);
493
494         pthread_setcanceltype (PTHREAD_CANCEL_ASYNCHRONOUS, 0);
495
496         // catch error message system signals ();
497
498         text_receiver.listen_to (error);
499         text_receiver.listen_to (info);
500         text_receiver.listen_to (fatal);
501         text_receiver.listen_to (warning);
502
503 #ifdef BOOST_SP_ENABLE_DEBUG_HOOKS
504         if (getenv ("BOOST_DEBUG")) {
505                 boost_debug_shared_ptr_show_live_debugging (true);
506         }
507 #endif
508
509         if (parse_opts (argc, argv)) {
510                 exit (1);
511         }
512
513         if (curvetest_file) {
514                 return curvetest (curvetest_file);
515         }
516
517         cout << PROGRAM_NAME
518              << VERSIONSTRING
519              << _(" (built using ")
520              << svn_revision
521 #ifdef __GNUC__
522              << _(" and GCC version ") << __VERSION__
523 #endif
524              << ')'
525              << endl;
526
527         if (just_version) {
528                 exit (0);
529         }
530
531         if (no_splash) {
532                 cerr << _("Copyright (C) 1999-2011 Paul Davis") << endl
533                      << _("Some portions Copyright (C) Steve Harris, Ari Johnson, Brett Viren, Joel Baker") << endl
534                      << endl
535                      << string_compose (_("%1 comes with ABSOLUTELY NO WARRANTY"), PROGRAM_NAME) << endl
536                      << _("not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.") << endl
537                      << _("This is free software, and you are welcome to redistribute it ") << endl
538                      << _("under certain conditions; see the source for copying conditions.")
539                      << endl;
540         }
541
542         /* some GUI objects need this */
543
544         PBD::ID::init ();
545
546         if (::signal (SIGPIPE, sigpipe_handler)) {
547                 cerr << _("Cannot xinstall SIGPIPE error handler") << endl;
548         }
549
550         try {
551                 ui = new ARDOUR_UI (&argc, &argv);
552         } catch (failed_constructor& err) {
553                 error << _("could not create ARDOUR GUI") << endmsg;
554                 exit (1);
555         }
556
557         ui->run (text_receiver);
558         Gtkmm2ext::Application::instance()->cleanup();
559         ui = 0;
560
561         ARDOUR::cleanup ();
562         pthread_cancel_all ();
563
564 #ifdef HAVE_LV2
565         close_external_ui_windows();
566 #endif
567         return 0;
568 }
569 #ifdef VST_SUPPORT
570 } // end of extern C block
571 #endif
572