potential fix, part two, for Tiger-based font issues. this also switches things so...
[ardour.git] / gtk2_ardour / main.cc
1 /*
2     Copyright (C) 2001-2012 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 <fontconfig/fontconfig.h>
54
55 #include "version.h"
56 #include "utils.h"
57 #include "ardour_ui.h"
58 #include "opts.h"
59 #include "enums.h"
60
61 #include "i18n.h"
62
63 #ifdef __APPLE__
64 #include <Carbon/Carbon.h>
65 #endif
66
67 using namespace std;
68 using namespace Gtk;
69 using namespace ARDOUR_COMMAND_LINE;
70 using namespace ARDOUR;
71 using namespace PBD;
72
73 TextReceiver text_receiver ("ardour");
74
75 extern int curvetest (string);
76
77 static ARDOUR_UI  *ui = 0;
78 static const char* localedir = LOCALEDIR;
79
80 void
81 gui_jack_error ()
82 {
83         MessageDialog win (string_compose (_("%1 could not connect to JACK."), PROGRAM_NAME),
84                            false,
85                            Gtk::MESSAGE_INFO,
86                            Gtk::BUTTONS_NONE);
87         win.set_secondary_text(string_compose (_("There are several possible reasons:\n\
88 \n\
89 1) JACK is not running.\n\
90 2) JACK is running as another user, perhaps root.\n\
91 3) There is already another client called \"%1\".\n\
92 \n\
93 Please consider the possibilities, and perhaps (re)start JACK."), PROGRAM_NAME));
94
95         win.add_button (Stock::QUIT, RESPONSE_CLOSE);
96         win.set_default_response (RESPONSE_CLOSE);
97
98         win.show_all ();
99         win.set_position (Gtk::WIN_POS_CENTER);
100
101         if (!no_splash) {
102                 ui->hide_splash ();
103         }
104
105         /* we just don't care about the result, but we want to block */
106
107         win.run ();
108 }
109
110 static void export_search_path (const string& base_dir, const char* varname, const char* dir)
111 {
112         string path;
113         const char * cstr = getenv (varname);
114
115         if (cstr) {
116                 path = cstr;
117                 path += ':';
118         } else {
119                 path = "";
120         }
121         path += base_dir;
122         path += dir;
123
124         setenv (varname, path.c_str(), 1);
125 }
126
127 #ifdef __APPLE__
128
129 #include <mach-o/dyld.h>
130 #include <sys/param.h>
131
132 extern void set_language_preference (); // cocoacarbon.mm
133
134 void
135 fixup_bundle_environment (int, char* [])
136 {
137         if (!getenv ("ARDOUR_BUNDLED")) {
138                 return;
139         }
140
141         EnvironmentalProtectionAgency::set_global_epa (new EnvironmentalProtectionAgency (true, "PREBUNDLE_ENV"));
142
143         set_language_preference ();
144
145         char execpath[MAXPATHLEN+1];
146         uint32_t pathsz = sizeof (execpath);
147
148         _NSGetExecutablePath (execpath, &pathsz);
149
150         std::string path;
151         std::string exec_dir = Glib::path_get_dirname (execpath);
152         std::string bundle_dir;
153         std::string userconfigdir = user_config_directory();
154
155         bundle_dir = Glib::path_get_dirname (exec_dir);
156
157 #ifdef ENABLE_NLS
158         if (!ARDOUR::translations_are_enabled ()) {
159                 localedir = "/this/cannot/exist";
160         } else {
161                 /* force localedir into the bundle */
162                 
163                 vector<string> lpath;
164                 lpath.push_back (bundle_dir);
165                 lpath.push_back ("share");
166                 lpath.push_back ("locale");
167                 localedir = strdup (Glib::build_filename (lpath).c_str());
168         }
169 #endif
170                 
171         export_search_path (bundle_dir, "ARDOUR_DLL_PATH", "/lib");
172
173         /* inside an OS X .app bundle, there is no difference
174            between DATA and CONFIG locations, since OS X doesn't
175            attempt to do anything to expose the notion of
176            machine-independent shared data.
177         */
178
179         export_search_path (bundle_dir, "ARDOUR_DATA_PATH", "/Resources");
180         export_search_path (bundle_dir, "ARDOUR_CONFIG_PATH", "/Resources");
181         export_search_path (bundle_dir, "ARDOUR_INSTANT_XML_PATH", "/Resources");
182         export_search_path (bundle_dir, "LADSPA_PATH", "/Plugins");
183         export_search_path (bundle_dir, "VAMP_PATH", "/lib");
184         export_search_path (bundle_dir, "SUIL_MODULE_DIR", "/lib");
185         export_search_path (bundle_dir, "GTK_PATH", "/lib/gtkengines");
186
187         /* unset GTK_RC_FILES so that we only load the RC files that we define
188          */
189
190         unsetenv ("GTK_RC_FILES");
191
192         /* write a pango.rc file and tell pango to use it. we'd love
193            to put this into the PROGRAM_NAME.app bundle and leave it there,
194            but the user may not have write permission. so ...
195
196            we also have to make sure that the user ardour directory
197            actually exists ...
198         */
199
200         if (g_mkdir_with_parents (userconfigdir.c_str(), 0755) < 0) {
201                 error << string_compose (_("cannot create user %3 folder %1 (%2)"), userconfigdir, strerror (errno), PROGRAM_NAME)
202                       << endmsg;
203         } else {
204                 
205                 path = Glib::build_filename (userconfigdir, "pango.rc");
206                 std::ofstream pangorc (path.c_str());
207                 if (!pangorc) {
208                         error << string_compose (_("cannot open pango.rc file %1") , path) << endmsg;
209                 } else {
210                         pangorc << "[Pango]\nModuleFiles="
211                                 << Glib::build_filename (bundle_dir, "Resources/pango.modules") 
212                                 << endl;
213                         pangorc.close ();
214                         
215                         setenv ("PANGO_RC_FILE", path.c_str(), 1);
216                 }
217         }
218         
219         setenv ("CHARSETALIASDIR", bundle_dir.c_str(), 1);
220         setenv ("FONTCONFIG_FILE", Glib::build_filename (bundle_dir, "Resources/fonts.conf").c_str(), 1);
221         setenv ("GDK_PIXBUF_MODULE_FILE", Glib::build_filename (bundle_dir, "Resources/gdk-pixbuf.loaders").c_str(), 1);
222 }
223
224 static void load_custom_fonts() {
225 #if MAC_OS_X_VERSION_MIN_REQUIRED > 1060
226         std::string ardour_mono_file;
227
228         if (!find_file_in_search_path (ardour_data_search_path(), "ArdourMono.ttf", ardour_mono_file)) {
229                 cerr << _("Cannot find ArdourMono TrueType font") << endl;
230         }
231
232         CFStringRef ttf;
233         CFURLRef fontURL;
234         CFErrorRef error;
235         ttf = CFStringCreateWithBytes(
236                         kCFAllocatorDefault, (UInt8*) ardour_mono_file.c_str(),
237                         ardour_mono_file.length(),
238                         kCFStringEncodingUTF8, FALSE);
239         fontURL = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, ttf, kCFURLPOSIXPathStyle, TRUE);
240         if (CTFontManagerRegisterFontsForURL(fontURL, kCTFontManagerScopeProcess, &error) != true) {
241                 cerr << _("Cannot load ArdourMono TrueType font.") << endl;
242         }
243 #else
244         /* no real usable API for this on OS X 10.5 or below */
245 #endif
246 }
247
248 #else
249
250 void
251 fixup_bundle_environment (int /*argc*/, char* argv[])
252 {
253         /* THIS IS FOR LINUX - its just about the only place where its
254          * acceptable to build paths directly using '/'.
255          */
256
257         if (!getenv ("ARDOUR_BUNDLED")) {
258                 return;
259         }
260
261         EnvironmentalProtectionAgency::set_global_epa (new EnvironmentalProtectionAgency (true, "PREBUNDLE_ENV"));
262
263         std::string path;
264         std::string dir_path = Glib::path_get_dirname (Glib::path_get_dirname (argv[0]));
265         std::string userconfigdir = user_config_directory();
266
267 #ifdef ENABLE_NLS
268         if (!ARDOUR::translations_are_enabled ()) {
269                 localedir = "/this/cannot/exist";
270         } else {
271                 /* force localedir into the bundle */
272                 vector<string> lpath;
273                 lpath.push_back (dir_path);
274                 lpath.push_back ("share");
275                 lpath.push_back ("locale");
276                 localedir = realpath (Glib::build_filename (lpath).c_str(), NULL);
277         }
278 #endif
279
280         /* note that this function is POSIX/Linux specific, so using / as
281            a dir separator in this context is just fine.
282         */
283
284         export_search_path (dir_path, "ARDOUR_DLL_PATH", "/lib");
285         export_search_path (dir_path, "ARDOUR_CONFIG_PATH", "/etc");
286         export_search_path (dir_path, "ARDOUR_INSTANT_XML_PATH", "/share");
287         export_search_path (dir_path, "ARDOUR_DATA_PATH", "/share");
288         export_search_path (dir_path, "LADSPA_PATH", "/plugins");
289         export_search_path (dir_path, "VAMP_PATH", "/lib");
290         export_search_path (dir_path, "SUIL_MODULE_DIR", "/lib");
291         export_search_path (dir_path, "GTK_PATH", "/lib/gtkengines");
292
293         /* unset GTK_RC_FILES so that we only load the RC files that we define
294          */
295
296         unsetenv ("GTK_RC_FILES");
297
298         /* Tell fontconfig where to find fonts.conf. Use the system version
299            if it exists, otherwise use the stuff we included in the bundle
300         */
301
302         if (Glib::file_test ("/etc/fonts/fonts.conf", Glib::FILE_TEST_EXISTS)) {
303                 setenv ("FONTCONFIG_FILE", "/etc/fonts/fonts.conf", 1);
304                 setenv ("FONTCONFIG_PATH", "/etc/fonts", 1);
305         } else {
306                 error << _("No fontconfig file found on your system. Things may looked very odd or ugly") << endmsg;
307         }
308
309         /* write a pango.rc file and tell pango to use it. we'd love
310            to put this into the Ardour.app bundle and leave it there,
311            but the user may not have write permission. so ...
312
313            we also have to make sure that the user ardour directory
314            actually exists ...
315         */
316
317         if (g_mkdir_with_parents (userconfigdir.c_str(), 0755) < 0) {
318                 error << string_compose (_("cannot create user %3 folder %1 (%2)"), userconfigdir, strerror (errno), PROGRAM_NAME)
319                       << endmsg;
320         } else {
321                 
322                 path = Glib::build_filename (userconfigdir, "pango.rc");
323                 std::ofstream pangorc (path.c_str());
324                 if (!pangorc) {
325                         error << string_compose (_("cannot open pango.rc file %1") , path) << endmsg;
326                 } else {
327                         pangorc << "[Pango]\nModuleFiles="
328                                 << Glib::build_filename (userconfigdir, "pango.modules")
329                                 << endl;
330                         pangorc.close ();
331                 }
332                 
333                 setenv ("PANGO_RC_FILE", path.c_str(), 1);
334                 
335                 /* similar for GDK pixbuf loaders, but there's no RC file required
336                    to specify where it lives.
337                 */
338                 
339                 setenv ("GDK_PIXBUF_MODULE_FILE", Glib::build_filename (userconfigdir, "gdk-pixbuf.loaders").c_str(), 1);
340         }
341
342         /* this doesn't do much but setting it should prevent various parts of the GTK/GNU stack
343            from looking outside the bundle to find the charset.alias file.
344         */
345         setenv ("CHARSETALIASDIR", dir_path.c_str(), 1);
346
347 }
348
349 static void load_custom_fonts() {
350         std::string ardour_mono_file;
351         if (!find_file_in_search_path (ardour_data_search_path(), "ArdourMono.ttf", ardour_mono_file)) {
352                 cerr << _("Cannot find ArdourMono TrueType font") << endl;
353         }
354
355         FcConfig *config = FcInitLoadConfigAndFonts();
356         FcBool ret = FcConfigAppFontAddFile(config, reinterpret_cast<const FcChar8*>(ardour_mono_file.c_str()));
357         if (ret == FcFalse) {
358                 cerr << _("Cannot load ArdourMono TrueType font.") << endl;
359         }
360         ret = FcConfigSetCurrent(config);
361         if (ret == FcFalse) {
362                 cerr << _("Failed to set fontconfig configuration.") << endl;
363         }
364 }
365
366 #endif
367
368 static gboolean
369 tell_about_jack_death (void* /* ignored */)
370 {
371         if (AudioEngine::instance()->processed_frames() == 0) {
372                 /* died during startup */
373                 MessageDialog msg (_("JACK exited"), false);
374                 msg.set_position (Gtk::WIN_POS_CENTER);
375                 msg.set_secondary_text (string_compose (_(
376 "JACK exited unexpectedly, and without notifying %1.\n\
377 \n\
378 This could be due to misconfiguration or to an error inside JACK.\n\
379 \n\
380 Click OK to exit %1."), PROGRAM_NAME));
381
382                 msg.run ();
383                 _exit (0);
384
385         } else {
386
387                 /* engine has already run, so this is a mid-session JACK death */
388
389                 MessageDialog* msg = manage (new MessageDialog (_("JACK exited"), false));
390                 msg->set_secondary_text (string_compose (_(
391 "JACK exited unexpectedly, and without notifying %1.\n\
392 \n\
393 This is probably due to an error inside JACK. You should restart JACK\n\
394 and reconnect %1 to it, or exit %1 now. You cannot save your\n\
395 session at this time, because we would lose your connection information.\n"), PROGRAM_NAME));
396                 msg->present ();
397         }
398         return false; /* do not call again */
399 }
400
401 static void
402 sigpipe_handler (int /*signal*/)
403 {
404         /* XXX fix this so that we do this again after a reconnect to JACK
405          */
406
407         static bool done_the_jack_thing = false;
408
409         if (!done_the_jack_thing) {
410                 AudioEngine::instance()->died ();
411                 g_idle_add (tell_about_jack_death, 0);
412                 done_the_jack_thing =  true;
413         }
414 }
415
416 #ifdef WINDOWS_VST_SUPPORT
417
418 extern int windows_vst_gui_init (int* argc, char** argv[]);
419
420 /* this is called from the entry point of a wine-compiled
421    executable that is linked against gtk2_ardour built
422    as a shared library.
423 */
424 extern "C" {
425 int ardour_main (int argc, char *argv[])
426 #else
427 int main (int argc, char *argv[])
428 #endif
429 {
430         fixup_bundle_environment (argc, argv);
431
432         load_custom_fonts(); /* needs to happend before any gtk and pango init calls */
433
434         if (!Glib::thread_supported()) {
435                 Glib::thread_init();
436         }
437
438 #ifdef ENABLE_NLS
439         gtk_set_locale ();
440 #endif
441
442 #ifdef WINDOWS_VST_SUPPORT
443         /* this does some magic that is needed to make GTK and Wine's own
444            X11 client interact properly.
445         */
446         windows_vst_gui_init (&argc, &argv);
447 #endif
448
449 #ifdef ENABLE_NLS
450         cerr << "bnd txt domain [" << PACKAGE << "] to " << localedir << endl;
451
452         (void) bindtextdomain (PACKAGE, localedir);
453         /* our i18n translations are all in UTF-8, so make sure
454            that even if the user locale doesn't specify UTF-8,
455            we use that when handling them.
456         */
457         (void) bind_textdomain_codeset (PACKAGE,"UTF-8");
458 #endif
459
460         pthread_setcanceltype (PTHREAD_CANCEL_ASYNCHRONOUS, 0);
461
462         // catch error message system signals ();
463
464         text_receiver.listen_to (error);
465         text_receiver.listen_to (info);
466         text_receiver.listen_to (fatal);
467         text_receiver.listen_to (warning);
468
469 #ifdef BOOST_SP_ENABLE_DEBUG_HOOKS
470         if (getenv ("BOOST_DEBUG")) {
471                 boost_debug_shared_ptr_show_live_debugging (true);
472         }
473 #endif
474
475         if (parse_opts (argc, argv)) {
476                 exit (1);
477         }
478
479         if (curvetest_file) {
480                 return curvetest (curvetest_file);
481         }
482
483         cout << PROGRAM_NAME
484              << VERSIONSTRING
485              << _(" (built using ")
486              << svn_revision
487 #ifdef __GNUC__
488              << _(" and GCC version ") << __VERSION__
489 #endif
490              << ')'
491              << endl;
492
493         if (just_version) {
494                 exit (0);
495         }
496
497         if (no_splash) {
498                 cerr << _("Copyright (C) 1999-2012 Paul Davis") << endl
499                      << _("Some portions Copyright (C) Steve Harris, Ari Johnson, Brett Viren, Joel Baker, Robin Gareus") << endl
500                      << endl
501                      << string_compose (_("%1 comes with ABSOLUTELY NO WARRANTY"), PROGRAM_NAME) << endl
502                      << _("not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.") << endl
503                      << _("This is free software, and you are welcome to redistribute it ") << endl
504                      << _("under certain conditions; see the source for copying conditions.")
505                      << endl;
506         }
507
508         /* some GUI objects need this */
509
510         PBD::ID::init ();
511
512         if (::signal (SIGPIPE, sigpipe_handler)) {
513                 cerr << _("Cannot xinstall SIGPIPE error handler") << endl;
514         }
515
516         try {
517                 ui = new ARDOUR_UI (&argc, &argv, localedir);
518         } catch (failed_constructor& err) {
519                 error << string_compose (_("could not create %1 GUI"), PROGRAM_NAME) << endmsg;
520                 exit (1);
521         }
522
523         ui->run (text_receiver);
524         Gtkmm2ext::Application::instance()->cleanup();
525         ui = 0;
526
527         ARDOUR::cleanup ();
528         pthread_cancel_all ();
529
530         return 0;
531 }
532 #ifdef WINDOWS_VST_SUPPORT
533 } // end of extern C block
534 #endif
535