fix OS X compilation (i hope)
[ardour.git] / gtk2_ardour / main.cc
index a2b211cd3481bffc0e2013350f632cdf30e2c188..850b1141d13c6ace0c2b183af4e85d83ea3e8a4d 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2001-2007 Paul Davis
+    Copyright (C) 2001-2012 Paul Davis
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
 
 #include <cstdlib>
 #include <signal.h>
+#include <cerrno>
+#include <fstream>
+#include <vector>
 
 #include <sigc++/bind.h>
 #include <gtkmm/settings.h>
 
 #include "pbd/error.h"
+#include "pbd/epa.h"
 #include "pbd/file_utils.h"
 #include "pbd/textreceiver.h"
 #include "pbd/failed_constructor.h"
 #include "pbd/pthread_utils.h"
+#ifdef BOOST_SP_ENABLE_DEBUG_HOOKS
+#include "pbd/boost_debug.h"
+#endif
 
 #include <jack/jack.h>
 
@@ -43,6 +50,8 @@
 #include <gtkmm2ext/popup.h>
 #include <gtkmm2ext/utils.h>
 
+#include <fontconfig/fontconfig.h>
+
 #include "version.h"
 #include "utils.h"
 #include "ardour_ui.h"
 
 #include "i18n.h"
 
+#ifdef __APPLE__
+#include <Carbon/Carbon.h>
+#endif
+
 using namespace std;
 using namespace Gtk;
 using namespace ARDOUR_COMMAND_LINE;
@@ -68,16 +81,16 @@ void
 gui_jack_error ()
 {
        MessageDialog win (string_compose (_("%1 could not connect to JACK."), PROGRAM_NAME),
-                    false,
-                    Gtk::MESSAGE_INFO,
-                    (Gtk::ButtonsType)(Gtk::BUTTONS_NONE));
-win.set_secondary_text(_("There are several possible reasons:\n\
+                          false,
+                          Gtk::MESSAGE_INFO,
+                          Gtk::BUTTONS_NONE);
+       win.set_secondary_text(string_compose (_("There are several possible reasons:\n\
 \n\
 1) JACK is not running.\n\
 2) JACK is running as another user, perhaps root.\n\
-3) There is already another client called \"ardour\".\n\
+3) There is already another client called \"%1\".\n\
 \n\
-Please consider the possibilities, and perhaps (re)start JACK."));
+Please consider the possibilities, and perhaps (re)start JACK."), PROGRAM_NAME));
 
        win.add_button (Stock::QUIT, RESPONSE_CLOSE);
        win.set_default_response (RESPONSE_CLOSE);
@@ -94,22 +107,39 @@ Please consider the possibilities, and perhaps (re)start JACK."));
        win.run ();
 }
 
+static void export_search_path (const string& base_dir, const char* varname, const char* dir)
+{
+       string path;
+       const char * cstr = getenv (varname);
+
+       if (cstr) {
+               path = cstr;
+               path += ':';
+       } else {
+               path = "";
+       }
+       path += base_dir;
+       path += dir;
+
+       setenv (varname, path.c_str(), 1);
+}
 
 #ifdef __APPLE__
 
 #include <mach-o/dyld.h>
 #include <sys/param.h>
-#include <fstream>
 
 extern void set_language_preference (); // cocoacarbon.mm
 
 void
-fixup_bundle_environment ()
+fixup_bundle_environment (int, char* [])
 {
        if (!getenv ("ARDOUR_BUNDLED")) {
                return;
        }
 
+       EnvironmentalProtectionAgency::set_global_epa (new EnvironmentalProtectionAgency (true, "PREBUNDLE_ENV"));
+
        set_language_preference ();
 
        char execpath[MAXPATHLEN+1];
@@ -117,185 +147,215 @@ fixup_bundle_environment ()
 
        _NSGetExecutablePath (execpath, &pathsz);
 
-       std::string exec_path (execpath);
-       std::string dir_path = Glib::path_get_dirname (exec_path);
        std::string path;
-       const char *cstr = getenv ("PATH");
+       std::string exec_dir = Glib::path_get_dirname (execpath);
+       std::string bundle_dir;
+       std::string userconfigdir = user_config_directory();
+
+       bundle_dir = Glib::path_get_dirname (exec_dir);
 
-       /* ensure that we find any bundled executables (e.g. JACK),
-          and find them before any instances of the same name
-          elsewhere in PATH
+#ifdef ENABLE_NLS
+       if (!ARDOUR::translations_are_enabled ()) {
+               localedir = "/this/cannot/exist";
+       } else {
+               /* force localedir into the bundle */
+               
+               vector<string> lpath;
+               lpath.push_back (bundle_dir);
+               lpath.push_back ("share");
+               lpath.push_back ("locale");
+               localedir = strdup (Glib::build_filename (lpath).c_str());
+       }
+#endif
+               
+       export_search_path (bundle_dir, "ARDOUR_DLL_PATH", "/lib");
+
+       /* inside an OS X .app bundle, there is no difference
+          between DATA and CONFIG locations, since OS X doesn't
+          attempt to do anything to expose the notion of
+          machine-independent shared data.
        */
 
-       path = dir_path;
+       export_search_path (bundle_dir, "ARDOUR_DATA_PATH", "/Resources");
+       export_search_path (bundle_dir, "ARDOUR_CONFIG_PATH", "/Resources");
+       export_search_path (bundle_dir, "ARDOUR_INSTANT_XML_PATH", "/Resources");
+       export_search_path (bundle_dir, "LADSPA_PATH", "/Plugins");
+       export_search_path (bundle_dir, "VAMP_PATH", "/lib");
+       export_search_path (bundle_dir, "SUIL_MODULE_DIR", "/lib");
+       export_search_path (bundle_dir, "GTK_PATH", "/lib/gtkengines");
+
+       /* unset GTK_RC_FILES so that we only load the RC files that we define
+        */
+
+       unsetenv ("GTK_RC_FILES");
+
+       /* write a pango.rc file and tell pango to use it. we'd love
+          to put this into the PROGRAM_NAME.app bundle and leave it there,
+          but the user may not have write permission. so ...
 
-       /* JACK is often in /usr/local/bin and since Info.plist refuses to 
-          set PATH, we have to force this in order to discover a running
-          instance of JACK ...
+          we also have to make sure that the user ardour directory
+          actually exists ...
        */
-       
-       path += ':';
-       path += "/usr/local/bin";
 
-       if (cstr) {
-               path += ':';
-               path += cstr;
-       }
-       setenv ("PATH", path.c_str(), 1);
-
-       path = dir_path;
-       path += "/../Resources";
-       path += dir_path;
-       path += "/../Resources/Surfaces";
-       path += dir_path;
-       path += "/../Resources/Panners";
-
-       setenv ("ARDOUR_MODULE_PATH", path.c_str(), 1);
-
-        path = user_config_directory.to_string();
-        path += ':';
-       path += dir_path;
-       path += "/../Resources/icons:";
-       path += dir_path;
-       path += "/../Resources/pixmaps:";
-       path += dir_path;
-       path += "/../Resources/share:";
-       path += dir_path;
-       path += "/../Resources";
-
-       setenv ("ARDOUR_PATH", path.c_str(), 1);
-       setenv ("ARDOUR_CONFIG_PATH", path.c_str(), 1);
-       setenv ("ARDOUR_DATA_PATH", path.c_str(), 1);
-
-       path = dir_path;
-       path += "/../Resources";
-       setenv ("ARDOUR_INSTANT_XML_PATH", path.c_str(), 1);
-
-       cstr = getenv ("LADSPA_PATH");
-       if (cstr) {
-               path = cstr;
-               path += ':';
+       if (g_mkdir_with_parents (userconfigdir.c_str(), 0755) < 0) {
+               error << string_compose (_("cannot create user %3 folder %1 (%2)"), userconfigdir, strerror (errno), PROGRAM_NAME)
+                     << endmsg;
        } else {
-               path = "";
+               
+               path = Glib::build_filename (userconfigdir, "pango.rc");
+               std::ofstream pangorc (path.c_str());
+               if (!pangorc) {
+                       error << string_compose (_("cannot open pango.rc file %1") , path) << endmsg;
+               } else {
+                       pangorc << "[Pango]\nModuleFiles="
+                               << Glib::build_filename (bundle_dir, "Resources/pango.modules") 
+                               << endl;
+                       pangorc.close ();
+                       
+                       setenv ("PANGO_RC_FILE", path.c_str(), 1);
+               }
        }
-       path += dir_path;
-       path += "/../Plugins";
+       
+       setenv ("CHARSETALIASDIR", bundle_dir.c_str(), 1);
+       setenv ("FONTCONFIG_FILE", Glib::build_filename (bundle_dir, "Resources/fonts.conf").c_str(), 1);
+       setenv ("GDK_PIXBUF_MODULE_FILE", Glib::build_filename (bundle_dir, "Resources/gdk-pixbuf.loaders").c_str(), 1);
+}
 
-       setenv ("LADSPA_PATH", path.c_str(), 1);
+static void load_custom_fonts() {
+       std::string ardour_mono_file;
 
-       cstr = getenv ("VAMP_PATH");
-       if (cstr) {
-               path = cstr;
-               path += ':';
-       } else {
-               path = "";
+       if (!find_file_in_search_path (ardour_data_search_path(), "ArdourMono.ttf", ardour_mono_file)) {
+               cerr << _("Cannot find ArdourMono TrueType font") << endl;
        }
-       path += dir_path;
-       path += "/../Frameworks";
 
-       setenv ("VAMP_PATH", path.c_str(), 1);
+       CFStringRef ttf;
+       CFURLRef fontURL;
+       CFErrorRef error;
+       ttf = CFStringCreateWithBytes(
+                       kCFAllocatorDefault, (UInt8*) ardour_mono_file.c_str(),
+                       ardour_mono_file.length(),
+                       kCFStringEncodingUTF8, FALSE);
+       fontURL = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, ttf, kCFURLPOSIXPathStyle, TRUE);
+       if (CTFontManagerRegisterFontsForURL(fontURL, kCTFontManagerScopeProcess, &error) != true) {
+               cerr << _("Cannot load ArdourMono TrueType font.") << endl;
+       }
+}
 
-       cstr = getenv ("ARDOUR_CONTROL_SURFACE_PATH");
-       if (cstr) {
-               path = cstr;
-               path += ':';
-       } else {
-               path = "";
+#else
+
+void
+fixup_bundle_environment (int /*argc*/, char* argv[])
+{
+       /* THIS IS FOR LINUX - its just about the only place where its
+        * acceptable to build paths directly using '/'.
+        */
+
+       if (!getenv ("ARDOUR_BUNDLED")) {
+               return;
        }
-       path += dir_path;
-       path += "/../Surfaces";
 
-       setenv ("ARDOUR_CONTROL_SURFACE_PATH", path.c_str(), 1);
+       EnvironmentalProtectionAgency::set_global_epa (new EnvironmentalProtectionAgency (true, "PREBUNDLE_ENV"));
 
-       cstr = getenv ("LV2_PATH");
-       if (cstr) {
-               path = cstr;
-               path += ':';
+       std::string path;
+       std::string dir_path = Glib::path_get_dirname (Glib::path_get_dirname (argv[0]));
+       std::string userconfigdir = user_config_directory();
+
+#ifdef ENABLE_NLS
+       if (!ARDOUR::translations_are_enabled ()) {
+               localedir = "/this/cannot/exist";
        } else {
-               path = "";
+               /* force localedir into the bundle */
+               vector<string> lpath;
+               lpath.push_back (dir_path);
+               lpath.push_back ("share");
+               lpath.push_back ("locale");
+               localedir = realpath (Glib::build_filename (lpath).c_str(), NULL);
        }
-       path += dir_path;
-       path += "/../Plugins";
+#endif
 
-       setenv ("LV2_PATH", path.c_str(), 1);
+       /* note that this function is POSIX/Linux specific, so using / as
+          a dir separator in this context is just fine.
+       */
 
-       path = dir_path;
-       path += "/../Frameworks/clearlooks";
+       export_search_path (dir_path, "ARDOUR_DLL_PATH", "/lib");
+       export_search_path (dir_path, "ARDOUR_CONFIG_PATH", "/etc");
+       export_search_path (dir_path, "ARDOUR_INSTANT_XML_PATH", "/share");
+       export_search_path (dir_path, "ARDOUR_DATA_PATH", "/share");
+       export_search_path (dir_path, "LADSPA_PATH", "/plugins");
+       export_search_path (dir_path, "VAMP_PATH", "/lib");
+       export_search_path (dir_path, "SUIL_MODULE_DIR", "/lib");
+       export_search_path (dir_path, "GTK_PATH", "/lib/gtkengines");
 
-       setenv ("GTK_PATH", path.c_str(), 1);
+       /* unset GTK_RC_FILES so that we only load the RC files that we define
+        */
 
-       if (!ARDOUR::translations_are_disabled ()) {
+       unsetenv ("GTK_RC_FILES");
 
-               path = dir_path;
-               path += "/../Resources/locale";
-               
-               localedir = strdup (path.c_str());
-               setenv ("GTK_LOCALEDIR", localedir, 1);
+       /* Tell fontconfig where to find fonts.conf. Use the system version
+          if it exists, otherwise use the stuff we included in the bundle
+       */
+
+       if (Glib::file_test ("/etc/fonts/fonts.conf", Glib::FILE_TEST_EXISTS)) {
+               setenv ("FONTCONFIG_FILE", "/etc/fonts/fonts.conf", 1);
+               setenv ("FONTCONFIG_PATH", "/etc/fonts", 1);
+       } else {
+               error << _("No fontconfig file found on your system. Things may looked very odd or ugly") << endmsg;
        }
 
        /* write a pango.rc file and tell pango to use it. we'd love
-          to put this into the PROGRAM_NAME.app bundle and leave it there,
+          to put this into the Ardour.app bundle and leave it there,
           but the user may not have write permission. so ...
 
           we also have to make sure that the user ardour directory
           actually exists ...
        */
 
-       try {
-               sys::create_directories (user_config_directory ());
-       }
-       catch (const sys::filesystem_error& ex) {
-               error << _("Could not create user configuration directory") << endmsg;
-       }
-
-       sys::path pangopath = user_config_directory();
-       pangopath /= "pango.rc";
-       path = pangopath.to_string();
-
-       std::ofstream pangorc (path.c_str());
-       if (!pangorc) {
-               error << string_compose (_("cannot open pango.rc file %1") , path) << endmsg;
-               return;
+       if (g_mkdir_with_parents (userconfigdir.c_str(), 0755) < 0) {
+               error << string_compose (_("cannot create user %3 folder %1 (%2)"), userconfigdir, strerror (errno), PROGRAM_NAME)
+                     << endmsg;
        } else {
-               pangorc << "[Pango]\nModuleFiles=";
-
-               pangopath = dir_path;
-               pangopath /= "..";
-               pangopath /= "Resources";
-               pangopath /= "pango.modules";
-
-               pangorc << pangopath.to_string() << endl;
-
-               pangorc.close ();
-
+               
+               path = Glib::build_filename (userconfigdir, "pango.rc");
+               std::ofstream pangorc (path.c_str());
+               if (!pangorc) {
+                       error << string_compose (_("cannot open pango.rc file %1") , path) << endmsg;
+               } else {
+                       pangorc << "[Pango]\nModuleFiles="
+                               << Glib::build_filename (userconfigdir, "pango.modules")
+                               << endl;
+                       pangorc.close ();
+               }
+               
                setenv ("PANGO_RC_FILE", path.c_str(), 1);
+               
+               /* similar for GDK pixbuf loaders, but there's no RC file required
+                  to specify where it lives.
+               */
+               
+               setenv ("GDK_PIXBUF_MODULE_FILE", Glib::build_filename (userconfigdir, "gdk-pixbuf.loaders").c_str(), 1);
        }
 
-       // gettext charset aliases
+        /* this doesn't do much but setting it should prevent various parts of the GTK/GNU stack
+           from looking outside the bundle to find the charset.alias file.
+        */
+        setenv ("CHARSETALIASDIR", dir_path.c_str(), 1);
 
-       setenv ("CHARSETALIASDIR", path.c_str(), 1);
-
-       // font config
-
-       path = dir_path;
-       path += "/../Resources/fonts.conf";
-
-       setenv ("FONTCONFIG_FILE", path.c_str(), 1);
-
-       // GDK Pixbuf loader module file
-
-       path = dir_path;
-       path += "/../Resources/gdk-pixbuf.loaders";
-
-       setenv ("GDK_PIXBUF_MODULE_FILE", path.c_str(), 1);
-
-       if (getenv ("ARDOUR_WITH_JACK")) {
-               // JACK driver dir
+}
 
-               path = dir_path;
-               path += "/../Frameworks";
+static void load_custom_fonts() {
+       std::string ardour_mono_file;
+       if (!find_file_in_search_path (ardour_data_search_path(), "ArdourMono.ttf", ardour_mono_file)) {
+               cerr << _("Cannot find ArdourMono TrueType font") << endl;
+       }
 
-               setenv ("JACK_DRIVER_DIR", path.c_str(), 1);
+       FcConfig *config = FcInitLoadConfigAndFonts();
+       FcBool ret = FcConfigAppFontAddFile(config, reinterpret_cast<const FcChar8*>(ardour_mono_file.c_str()));
+       if (ret == FcFalse) {
+               cerr << _("Cannot load ArdourMono TrueType font.") << endl;
+       }
+       ret = FcConfigSetCurrent(config);
+       if (ret == FcFalse) {
+               cerr << _("Failed to set fontconfig configuration.") << endl;
        }
 }
 
@@ -306,7 +366,7 @@ tell_about_jack_death (void* /* ignored */)
 {
        if (AudioEngine::instance()->processed_frames() == 0) {
                /* died during startup */
-               MessageDialog msg (_("JACK exited"), false, Gtk::MESSAGE_INFO, Gtk::BUTTONS_OK);
+               MessageDialog msg (_("JACK exited"), false);
                msg.set_position (Gtk::WIN_POS_CENTER);
                msg.set_secondary_text (string_compose (_(
 "JACK exited unexpectedly, and without notifying %1.\n\
@@ -314,15 +374,15 @@ tell_about_jack_death (void* /* ignored */)
 This could be due to misconfiguration or to an error inside JACK.\n\
 \n\
 Click OK to exit %1."), PROGRAM_NAME));
-    
+
                msg.run ();
                _exit (0);
-               
+
        } else {
 
                /* engine has already run, so this is a mid-session JACK death */
-               
-               MessageDialog* msg = manage (new MessageDialog (_("JACK exited"), false, Gtk::MESSAGE_INFO, Gtk::BUTTONS_NONE));
+
+               MessageDialog* msg = manage (new MessageDialog (_("JACK exited"), false));
                msg->set_secondary_text (string_compose (_(
 "JACK exited unexpectedly, and without notifying %1.\n\
 \n\
@@ -335,13 +395,13 @@ session at this time, because we would lose your connection information.\n"), PR
 }
 
 static void
-sigpipe_handler (int sig)
+sigpipe_handler (int /*signal*/)
 {
-        /* XXX fix this so that we do this again after a reconnect to JACK
-         */
+       /* XXX fix this so that we do this again after a reconnect to JACK
+        */
+
+       static bool done_the_jack_thing = false;
 
-        static bool done_the_jack_thing = false;
-       
        if (!done_the_jack_thing) {
                AudioEngine::instance()->died ();
                g_idle_add (tell_about_jack_death, 0);
@@ -349,13 +409,9 @@ sigpipe_handler (int sig)
        }
 }
 
-#ifdef HAVE_LV2
-void close_external_ui_windows();
-#endif
-
-#ifdef VST_SUPPORT
+#ifdef WINDOWS_VST_SUPPORT
 
-extern int gui_init (int* argc, char** argv[]);
+extern int windows_vst_gui_init (int* argc, char** argv[]);
 
 /* this is called from the entry point of a wine-compiled
    executable that is linked against gtk2_ardour built
@@ -367,31 +423,35 @@ int ardour_main (int argc, char *argv[])
 int main (int argc, char *argv[])
 #endif
 {
-       vector<std::string> null_file_list;
+       fixup_bundle_environment (argc, argv);
 
-#ifdef __APPLE__
-       fixup_bundle_environment ();
-#endif
+       load_custom_fonts(); /* needs to happend before any gtk and pango init calls */
 
-       if (!Glib::thread_supported())
+       if (!Glib::thread_supported()) {
                Glib::thread_init();
+       }
 
+#ifdef ENABLE_NLS
        gtk_set_locale ();
+#endif
 
-#ifdef VST_SUPPORT
+#ifdef WINDOWS_VST_SUPPORT
        /* this does some magic that is needed to make GTK and Wine's own
           X11 client interact properly.
        */
-       gui_init (&argc, &argv);
+       windows_vst_gui_init (&argc, &argv);
 #endif
 
+#ifdef ENABLE_NLS
+       cerr << "bnd txt domain [" << PACKAGE << "] to " << localedir << endl;
+
        (void) bindtextdomain (PACKAGE, localedir);
        /* our i18n translations are all in UTF-8, so make sure
           that even if the user locale doesn't specify UTF-8,
           we use that when handling them.
        */
        (void) bind_textdomain_codeset (PACKAGE,"UTF-8");
-       (void) textdomain (PACKAGE);
+#endif
 
        pthread_setcanceltype (PTHREAD_CANCEL_ASYNCHRONOUS, 0);
 
@@ -402,6 +462,12 @@ int main (int argc, char *argv[])
        text_receiver.listen_to (fatal);
        text_receiver.listen_to (warning);
 
+#ifdef BOOST_SP_ENABLE_DEBUG_HOOKS
+       if (getenv ("BOOST_DEBUG")) {
+               boost_debug_shared_ptr_show_live_debugging (true);
+       }
+#endif
+
        if (parse_opts (argc, argv)) {
                exit (1);
        }
@@ -425,8 +491,8 @@ int main (int argc, char *argv[])
        }
 
        if (no_splash) {
-               cerr << _("Copyright (C) 1999-2010 Paul Davis") << endl
-                    << _("Some portions Copyright (C) Steve Harris, Ari Johnson, Brett Viren, Joel Baker") << endl
+               cerr << _("Copyright (C) 1999-2012 Paul Davis") << endl
+                    << _("Some portions Copyright (C) Steve Harris, Ari Johnson, Brett Viren, Joel Baker, Robin Gareus") << endl
                     << endl
                     << string_compose (_("%1 comes with ABSOLUTELY NO WARRANTY"), PROGRAM_NAME) << endl
                     << _("not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.") << endl
@@ -440,29 +506,26 @@ int main (int argc, char *argv[])
        PBD::ID::init ();
 
        if (::signal (SIGPIPE, sigpipe_handler)) {
-               cerr << _("Cannot install SIGPIPE error handler") << endl;
+               cerr << _("Cannot xinstall SIGPIPE error handler") << endl;
        }
 
        try {
-               ui = new ARDOUR_UI (&argc, &argv);
+               ui = new ARDOUR_UI (&argc, &argv, localedir);
        } catch (failed_constructor& err) {
-               error << _("could not create ARDOUR GUI") << endmsg;
+               error << string_compose (_("could not create %1 GUI"), PROGRAM_NAME) << endmsg;
                exit (1);
        }
 
        ui->run (text_receiver);
-        Gtkmm2ext::Application::instance()->cleanup();
+       Gtkmm2ext::Application::instance()->cleanup();
        ui = 0;
 
        ARDOUR::cleanup ();
        pthread_cancel_all ();
 
-#ifdef HAVE_LV2
-       close_external_ui_windows();
-#endif
        return 0;
 }
-#ifdef VST_SUPPORT
+#ifdef WINDOWS_VST_SUPPORT
 } // end of extern C block
 #endif