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