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