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