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