fix up unexpected situation with fontconfig in our packages
[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 #include <vector>
25
26 #include <sigc++/bind.h>
27 #include <gtkmm/settings.h>
28
29 #include "pbd/error.h"
30 #include "pbd/epa.h"
31 #include "pbd/file_utils.h"
32 #include "pbd/textreceiver.h"
33 #include "pbd/failed_constructor.h"
34 #include "pbd/pthread_utils.h"
35 #ifdef BOOST_SP_ENABLE_DEBUG_HOOKS
36 #include "pbd/boost_debug.h"
37 #endif
38
39 #include <jack/jack.h>
40
41 #include "ardour/svn_revision.h"
42 #include "ardour/version.h"
43 #include "ardour/ardour.h"
44 #include "ardour/audioengine.h"
45 #include "ardour/session_utils.h"
46 #include "ardour/filesystem_paths.h"
47
48 #include <gtkmm/main.h>
49 #include <gtkmm2ext/application.h>
50 #include <gtkmm2ext/popup.h>
51 #include <gtkmm2ext/utils.h>
52
53 #include "version.h"
54 #include "utils.h"
55 #include "ardour_ui.h"
56 #include "opts.h"
57 #include "enums.h"
58
59 #include "i18n.h"
60
61 using namespace std;
62 using namespace Gtk;
63 using namespace ARDOUR_COMMAND_LINE;
64 using namespace ARDOUR;
65 using namespace PBD;
66
67 TextReceiver text_receiver ("ardour");
68
69 extern int curvetest (string);
70
71 static ARDOUR_UI  *ui = 0;
72 static const char* localedir = LOCALEDIR;
73
74 void
75 gui_jack_error ()
76 {
77         MessageDialog win (string_compose (_("%1 could not connect to JACK."), PROGRAM_NAME),
78                            false,
79                            Gtk::MESSAGE_INFO,
80                            Gtk::BUTTONS_NONE);
81 win.set_secondary_text(_("There are several possible reasons:\n\
82 \n\
83 1) JACK is not running.\n\
84 2) JACK is running as another user, perhaps root.\n\
85 3) There is already another client called \"ardour\".\n\
86 \n\
87 Please consider the possibilities, and perhaps (re)start JACK."));
88
89         win.add_button (Stock::QUIT, RESPONSE_CLOSE);
90         win.set_default_response (RESPONSE_CLOSE);
91
92         win.show_all ();
93         win.set_position (Gtk::WIN_POS_CENTER);
94
95         if (!no_splash) {
96                 ui->hide_splash ();
97         }
98
99         /* we just don't care about the result, but we want to block */
100
101         win.run ();
102 }
103
104 static void export_search_path (const string& base_dir, const char* varname, const char* dir)
105 {
106         string path;
107         const char * cstr = getenv (varname);
108
109         if (cstr) {
110                 path = cstr;
111                 path += ':';
112         } else {
113                 path = "";
114         }
115         path += base_dir;
116         path += dir;
117
118         setenv (varname, path.c_str(), 1);
119 }
120
121 #ifdef __APPLE__
122
123 #include <mach-o/dyld.h>
124 #include <sys/param.h>
125
126 extern void set_language_preference (); // cocoacarbon.mm
127
128 void
129 fixup_bundle_environment (int, char* [])
130 {
131         if (!getenv ("ARDOUR_BUNDLED")) {
132                 return;
133         }
134
135         EnvironmentalProtectionAgency::set_global_epa (new EnvironmentalProtectionAgency (true, "PREBUNDLE_ENV"));
136
137         set_language_preference ();
138
139         char execpath[MAXPATHLEN+1];
140         uint32_t pathsz = sizeof (execpath);
141
142         _NSGetExecutablePath (execpath, &pathsz);
143
144         std::string dir_path = Glib::path_get_dirname (execpath);
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 += "/../Surfaces";
173         path += dir_path;
174         path += "/../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         /* THIS IS FOR LINUX - its just about the only place where its
294          * acceptable to build paths directly using '/'.
295          */
296
297         if (!getenv ("ARDOUR_BUNDLED")) {
298                 return;
299         }
300
301         EnvironmentalProtectionAgency::set_global_epa (new EnvironmentalProtectionAgency (true, "PREBUNDLE_ENV"));
302
303         Glib::ustring dir_path = Glib::path_get_dirname (Glib::path_get_dirname (argv[0]));
304         Glib::ustring path;
305         Glib::ustring userconfigdir = user_config_directory().to_string();
306
307         /* ensure that we find any bundled executables (e.g. JACK),
308            and find them before any instances of the same name
309            elsewhere in PATH
310         */
311
312         /* note that this function is POSIX/Linux specific, so using / as
313            a dir separator in this context is just fine.
314         */
315
316         path = dir_path;
317         path += "/etc:";
318         path += dir_path;
319         path += "/lib/surfaces:";
320         path += dir_path;
321         path += "/lib/panners:";
322
323         setenv ("ARDOUR_MODULE_PATH", path.c_str(), 1);
324
325         path = userconfigdir;
326         path += ':';
327         path += dir_path;
328         path += "/etc/icons:";
329         path += dir_path;
330         path += "/etc/pixmaps:";
331         path += dir_path;
332         path += "/share:";
333         path += dir_path;
334         path += "/etc";
335
336         setenv ("ARDOUR_PATH", path.c_str(), 1);
337         setenv ("ARDOUR_CONFIG_PATH", path.c_str(), 1);
338
339         path = dir_path;
340         path += "/etc";
341         setenv ("ARDOUR_INSTANT_XML_PATH", path.c_str(), 1);
342
343         export_search_path (dir_path, "LADSPA_PATH", "/../plugins");
344         export_search_path (dir_path, "VAMP_PATH", "/lib");
345         export_search_path (dir_path, "ARDOUR_PANNER_PATH", "/lib/panners");
346         export_search_path (dir_path, "ARDOUR_SURFACES_PATH", "/lib/surfaces");
347         export_search_path (dir_path, "ARDOUR_MIDIMAPS_PATH", "/share/midi_maps");
348         export_search_path (dir_path, "ARDOUR_EXPORT_FORMATS_PATH", "/share/export");
349
350         path = dir_path;
351         path += "/lib/clearlooks";
352         setenv ("GTK_PATH", path.c_str(), 1);
353
354         /* unset GTK_RC_FILES so that we only load the RC files that we define
355          */
356
357         unsetenv ("GTK_RC_FILES");
358
359         if (!ARDOUR::translations_are_disabled ()) {
360                 path = dir_path;
361                 path += "/share/locale";
362
363                 localedir = strdup (path.c_str());
364                 setenv ("GTK_LOCALEDIR", localedir, 1);
365         }
366
367         /* write a pango.rc file and tell pango to use it. we'd love
368            to put this into the Ardour.app bundle and leave it there,
369            but the user may not have write permission. so ...
370
371            we also have to make sure that the user ardour directory
372            actually exists ...
373         */
374
375         if (g_mkdir_with_parents (userconfigdir.c_str(), 0755) < 0) {
376                 error << string_compose (_("cannot create user ardour folder %1 (%2)"), userconfigdir, strerror (errno))
377                       << endmsg;
378                 return;
379         } 
380
381         Glib::ustring mpath;
382         
383         path = Glib::build_filename (userconfigdir, "pango.rc");
384         
385         std::ofstream pangorc (path.c_str());
386         if (!pangorc) {
387                 error << string_compose (_("cannot open pango.rc file %1") , path) << endmsg;
388         } else {
389                 mpath = Glib::build_filename (userconfigdir, "pango.modules");
390                 
391                 pangorc << "[Pango]\nModuleFiles=";
392                 pangorc << mpath << endl;
393                 pangorc.close ();
394         }
395         
396         setenv ("PANGO_RC_FILE", path.c_str(), 1);
397
398         /* Tell fontconfig where to find fonts.conf. Use the system version
399            if it exists, otherwise use the stuff we included in t
400         */
401
402         if (Glib::file_test ("/etc/fonts/fonts.conf", Glib::FILE_TEST_EXISTS)) {
403                 setenv ("/etc/fonts/fonts.conf", path.c_str(), 1);
404         } else {
405                 /* use the one included in the bundle */
406                 
407                 path = Glib::build_filename (dir_path, "etc/fonts/fonts.conf");
408                 setenv ("FONTCONFIG_FILE", path.c_str(), 1);
409         }
410         
411         /* similar for GDK pixbuf loaders, but there's no RC file required
412            to specify where it lives.
413         */
414         
415         mpath = Glib::build_filename (userconfigdir, "gdk-pixbuf.loaders");
416         setenv ("GDK_PIXBUF_MODULE_FILE", mpath.c_str(), 1);
417 }
418
419 #endif
420
421 static gboolean
422 tell_about_jack_death (void* /* ignored */)
423 {
424         if (AudioEngine::instance()->processed_frames() == 0) {
425                 /* died during startup */
426                 MessageDialog msg (_("JACK exited"), false);
427                 msg.set_position (Gtk::WIN_POS_CENTER);
428                 msg.set_secondary_text (string_compose (_(
429 "JACK exited unexpectedly, and without notifying %1.\n\
430 \n\
431 This could be due to misconfiguration or to an error inside JACK.\n\
432 \n\
433 Click OK to exit %1."), PROGRAM_NAME));
434
435                 msg.run ();
436                 _exit (0);
437
438         } else {
439
440                 /* engine has already run, so this is a mid-session JACK death */
441
442                 MessageDialog* msg = manage (new MessageDialog (_("JACK exited"), false));
443                 msg->set_secondary_text (string_compose (_(
444 "JACK exited unexpectedly, and without notifying %1.\n\
445 \n\
446 This is probably due to an error inside JACK. You should restart JACK\n\
447 and reconnect %1 to it, or exit %1 now. You cannot save your\n\
448 session at this time, because we would lose your connection information.\n"), PROGRAM_NAME));
449                 msg->present ();
450         }
451         return false; /* do not call again */
452 }
453
454 static void
455 sigpipe_handler (int /*signal*/)
456 {
457         /* XXX fix this so that we do this again after a reconnect to JACK
458          */
459
460         static bool done_the_jack_thing = false;
461
462         if (!done_the_jack_thing) {
463                 AudioEngine::instance()->died ();
464                 g_idle_add (tell_about_jack_death, 0);
465                 done_the_jack_thing =  true;
466         }
467 }
468
469 #ifdef HAVE_LV2
470 void close_external_ui_windows();
471 #endif
472
473 #ifdef WINDOWS_VST_SUPPORT
474
475 extern int windows_vst_gui_init (int* argc, char** argv[]);
476
477 /* this is called from the entry point of a wine-compiled
478    executable that is linked against gtk2_ardour built
479    as a shared library.
480 */
481 extern "C" {
482 int ardour_main (int argc, char *argv[])
483 #else
484 int main (int argc, char *argv[])
485 #endif
486 {
487         fixup_bundle_environment (argc, argv);
488
489         if (!Glib::thread_supported()) {
490                 Glib::thread_init();
491         }
492
493         gtk_set_locale ();
494
495 #ifdef WINDOWS_VST_SUPPORT
496         /* this does some magic that is needed to make GTK and Wine's own
497            X11 client interact properly.
498         */
499         windows_vst_gui_init (&argc, &argv);
500 #endif
501
502         (void) bindtextdomain (PACKAGE, localedir);
503         /* our i18n translations are all in UTF-8, so make sure
504            that even if the user locale doesn't specify UTF-8,
505            we use that when handling them.
506         */
507         (void) bind_textdomain_codeset (PACKAGE,"UTF-8");
508         (void) textdomain (PACKAGE);
509
510         pthread_setcanceltype (PTHREAD_CANCEL_ASYNCHRONOUS, 0);
511
512         // catch error message system signals ();
513
514         text_receiver.listen_to (error);
515         text_receiver.listen_to (info);
516         text_receiver.listen_to (fatal);
517         text_receiver.listen_to (warning);
518
519 #ifdef BOOST_SP_ENABLE_DEBUG_HOOKS
520         if (getenv ("BOOST_DEBUG")) {
521                 boost_debug_shared_ptr_show_live_debugging (true);
522         }
523 #endif
524
525         if (parse_opts (argc, argv)) {
526                 exit (1);
527         }
528
529         if (curvetest_file) {
530                 return curvetest (curvetest_file);
531         }
532
533         cout << PROGRAM_NAME
534              << VERSIONSTRING
535              << _(" (built using ")
536              << svn_revision
537 #ifdef __GNUC__
538              << _(" and GCC version ") << __VERSION__
539 #endif
540              << ')'
541              << endl;
542
543         if (just_version) {
544                 exit (0);
545         }
546
547         if (no_splash) {
548                 cerr << _("Copyright (C) 1999-2012 Paul Davis") << endl
549                      << _("Some portions Copyright (C) Steve Harris, Ari Johnson, Brett Viren, Joel Baker") << endl
550                      << endl
551                      << string_compose (_("%1 comes with ABSOLUTELY NO WARRANTY"), PROGRAM_NAME) << endl
552                      << _("not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.") << endl
553                      << _("This is free software, and you are welcome to redistribute it ") << endl
554                      << _("under certain conditions; see the source for copying conditions.")
555                      << endl;
556         }
557
558         /* some GUI objects need this */
559
560         PBD::ID::init ();
561
562         if (::signal (SIGPIPE, sigpipe_handler)) {
563                 cerr << _("Cannot xinstall SIGPIPE error handler") << endl;
564         }
565
566         try {
567                 ui = new ARDOUR_UI (&argc, &argv);
568         } catch (failed_constructor& err) {
569                 error << _("could not create ARDOUR GUI") << endmsg;
570                 exit (1);
571         }
572
573         ui->run (text_receiver);
574         Gtkmm2ext::Application::instance()->cleanup();
575         ui = 0;
576
577         ARDOUR::cleanup ();
578         pthread_cancel_all ();
579
580 #ifdef HAVE_LV2
581         close_external_ui_windows();
582 #endif
583         return 0;
584 }
585 #ifdef WINDOWS_VST_SUPPORT
586 } // end of extern C block
587 #endif
588