start passing around the (possibly-bundle-modified) localedir so that we call bindtex...
[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 "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 path;
145         std::string exec_dir = Glib::path_get_dirname (execpath);
146         std::string bundle_dir;
147         std::string userconfigdir = user_config_directory();
148
149         bundle_dir = Glib::path_get_dirname (exec_dir);
150
151         /* force localedir into the bundle */
152
153         vector<string> lpath;
154         lpath.push_back (bundle_dir);
155         lpath.push_back ("share");
156         lpath.push_back ("locale");
157         localedir = strdup (Glib::build_filename (lpath).c_str());
158
159         export_search_path (bundle_dir, "ARDOUR_DLL_PATH", "/lib");
160
161         /* inside an OS X .app bundle, there is no difference
162            between DATA and CONFIG locations, since OS X doesn't
163            attempt to do anything to expose the notion of
164            machine-independent shared data.
165         */
166
167         export_search_path (bundle_dir, "ARDOUR_DATA_PATH", "/Resources");
168         export_search_path (bundle_dir, "ARDOUR_CONFIG_PATH", "/Resources");
169         export_search_path (bundle_dir, "ARDOUR_INSTANT_XML_PATH", "/Resources");
170         export_search_path (bundle_dir, "LADSPA_PATH", "/Plugins");
171         export_search_path (bundle_dir, "VAMP_PATH", "/lib");
172         export_search_path (bundle_dir, "SUIL_MODULE_DIR", "/lib");
173         export_search_path (bundle_dir, "GTK_PATH", "/lib/clearlooks");
174
175         /* unset GTK_RC_FILES so that we only load the RC files that we define
176          */
177
178         unsetenv ("GTK_RC_FILES");
179
180         if (!ARDOUR::translations_are_disabled ()) {
181                 export_search_path (bundle_dir, "GTK_LOCALEDIR", "/Resources/locale");
182         }
183
184         /* write a pango.rc file and tell pango to use it. we'd love
185            to put this into the PROGRAM_NAME.app bundle and leave it there,
186            but the user may not have write permission. so ...
187
188            we also have to make sure that the user ardour directory
189            actually exists ...
190         */
191
192         if (g_mkdir_with_parents (userconfigdir.c_str(), 0755) < 0) {
193                 error << string_compose (_("cannot create user ardour folder %1 (%2)"), userconfigdir, strerror (errno))
194                       << endmsg;
195         } else {
196                 
197                 path = Glib::build_filename (userconfigdir, "pango.rc");
198                 std::ofstream pangorc (path.c_str());
199                 if (!pangorc) {
200                         error << string_compose (_("cannot open pango.rc file %1") , path) << endmsg;
201                 } else {
202                         pangorc << "[Pango]\nModuleFiles="
203                                 << Glib::build_filename (bundle_dir, "Resources/pango.modules") 
204                                 << endl;
205                         pangorc.close ();
206                         
207                         setenv ("PANGO_RC_FILE", path.c_str(), 1);
208                 }
209         }
210         
211         setenv ("CHARSETALIASDIR", bundle_dir.c_str(), 1);
212         setenv ("FONTCONFIG_FILE", Glib::build_filename (bundle_dir, "Resources/fonts.conf").c_str(), 1);
213         setenv ("GDK_PIXBUF_MODULE_FILE", Glib::build_filename (bundle_dir, "Resources/gdk-pixbuf.loaders").c_str(), 1);
214 }
215
216 #else
217
218 void
219 fixup_bundle_environment (int /*argc*/, char* argv[])
220 {
221         /* THIS IS FOR LINUX - its just about the only place where its
222          * acceptable to build paths directly using '/'.
223          */
224
225         if (!getenv ("ARDOUR_BUNDLED")) {
226                 return;
227         }
228
229         EnvironmentalProtectionAgency::set_global_epa (new EnvironmentalProtectionAgency (true, "PREBUNDLE_ENV"));
230
231         std::string path;
232         std::string dir_path = Glib::path_get_dirname (Glib::path_get_dirname (argv[0]));
233         std::string userconfigdir = user_config_directory();
234
235         /* force localedir into the bundle */
236
237         vector<string> lpath;
238         lpath.push_back (dir_path);
239         lpath.push_back ("share");
240         lpath.push_back ("locale");
241         localedir = strdup (Glib::build_filename (lpath).c_str());
242
243         /* note that this function is POSIX/Linux specific, so using / as
244            a dir separator in this context is just fine.
245         */
246
247         export_search_path (dir_path, "ARDOUR_DLL_PATH", "/lib");
248         export_search_path (dir_path, "ARDOUR_CONFIG_PATH", "/etc");
249         export_search_path (dir_path, "ARDOUR_INSTANT_XML_PATH", "/share");
250         export_search_path (dir_path, "ARDOUR_DATA_PATH", "/share");
251         export_search_path (dir_path, "LADSPA_PATH", "/plugins");
252         export_search_path (dir_path, "VAMP_PATH", "/lib");
253         export_search_path (dir_path, "SUIL_MODULE_DIR", "/lib");
254
255         export_search_path (dir_path, "GTK_PATH", "/lib/clearlooks");
256
257         /* unset GTK_RC_FILES so that we only load the RC files that we define
258          */
259
260         unsetenv ("GTK_RC_FILES");
261
262         if (!ARDOUR::translations_are_disabled ()) {
263                 export_search_path (dir_path, "GTK_LOCALEDIR", "/share/locale");
264         }
265
266         /* Tell fontconfig where to find fonts.conf. Use the system version
267            if it exists, otherwise use the stuff we included in the bundle
268         */
269
270         if (Glib::file_test ("/etc/fonts/fonts.conf", Glib::FILE_TEST_EXISTS)) {
271                 setenv ("FONTCONFIG_FILE", "/etc/fonts/fonts.conf", 1);
272                 setenv ("FONTCONFIG_PATH", "/etc/fonts", 1);
273         } else {
274                 /* use the one included in the bundle */
275                 
276                 path = Glib::build_filename (dir_path, "etc/fonts/fonts.conf");
277                 setenv ("FONTCONFIG_FILE", path.c_str(), 1);
278                 export_search_path (dir_path, "FONTCONFIG_PATH", "/etc/fonts");
279         }
280
281         /* write a pango.rc file and tell pango to use it. we'd love
282            to put this into the Ardour.app bundle and leave it there,
283            but the user may not have write permission. so ...
284
285            we also have to make sure that the user ardour directory
286            actually exists ...
287         */
288
289         if (g_mkdir_with_parents (userconfigdir.c_str(), 0755) < 0) {
290                 error << string_compose (_("cannot create user ardour folder %1 (%2)"), userconfigdir, strerror (errno))
291                       << endmsg;
292         } else {
293                 
294                 path = Glib::build_filename (userconfigdir, "pango.rc");
295                 std::ofstream pangorc (path.c_str());
296                 if (!pangorc) {
297                         error << string_compose (_("cannot open pango.rc file %1") , path) << endmsg;
298                 } else {
299                         pangorc << "[Pango]\nModuleFiles="
300                                 << Glib::build_filename (userconfigdir, "pango.modules")
301                                 << endl;
302                         pangorc.close ();
303                 }
304                 
305                 setenv ("PANGO_RC_FILE", path.c_str(), 1);
306                 
307                 /* similar for GDK pixbuf loaders, but there's no RC file required
308                    to specify where it lives.
309                 */
310                 
311                 setenv ("GDK_PIXBUF_MODULE_FILE", Glib::build_filename (userconfigdir, "gdk-pixbuf.loaders").c_str(), 1);
312         }
313
314         /* this doesn't do much but setting it should prevent various parts of the GTK/GNU stack
315            from looking outside the bundle to find the charset.alias file.
316         */
317         setenv ("CHARSETALIASDIR", dir_path.c_str(), 1);
318
319 }
320
321 #endif
322
323 static gboolean
324 tell_about_jack_death (void* /* ignored */)
325 {
326         if (AudioEngine::instance()->processed_frames() == 0) {
327                 /* died during startup */
328                 MessageDialog msg (_("JACK exited"), false);
329                 msg.set_position (Gtk::WIN_POS_CENTER);
330                 msg.set_secondary_text (string_compose (_(
331 "JACK exited unexpectedly, and without notifying %1.\n\
332 \n\
333 This could be due to misconfiguration or to an error inside JACK.\n\
334 \n\
335 Click OK to exit %1."), PROGRAM_NAME));
336
337                 msg.run ();
338                 _exit (0);
339
340         } else {
341
342                 /* engine has already run, so this is a mid-session JACK death */
343
344                 MessageDialog* msg = manage (new MessageDialog (_("JACK exited"), false));
345                 msg->set_secondary_text (string_compose (_(
346 "JACK exited unexpectedly, and without notifying %1.\n\
347 \n\
348 This is probably due to an error inside JACK. You should restart JACK\n\
349 and reconnect %1 to it, or exit %1 now. You cannot save your\n\
350 session at this time, because we would lose your connection information.\n"), PROGRAM_NAME));
351                 msg->present ();
352         }
353         return false; /* do not call again */
354 }
355
356 static void
357 sigpipe_handler (int /*signal*/)
358 {
359         /* XXX fix this so that we do this again after a reconnect to JACK
360          */
361
362         static bool done_the_jack_thing = false;
363
364         if (!done_the_jack_thing) {
365                 AudioEngine::instance()->died ();
366                 g_idle_add (tell_about_jack_death, 0);
367                 done_the_jack_thing =  true;
368         }
369 }
370
371 #ifdef WINDOWS_VST_SUPPORT
372
373 extern int windows_vst_gui_init (int* argc, char** argv[]);
374
375 /* this is called from the entry point of a wine-compiled
376    executable that is linked against gtk2_ardour built
377    as a shared library.
378 */
379 extern "C" {
380 int ardour_main (int argc, char *argv[])
381 #else
382 int main (int argc, char *argv[])
383 #endif
384 {
385         fixup_bundle_environment (argc, argv);
386
387         if (!Glib::thread_supported()) {
388                 Glib::thread_init();
389         }
390
391         gtk_set_locale ();
392
393 #ifdef WINDOWS_VST_SUPPORT
394         /* this does some magic that is needed to make GTK and Wine's own
395            X11 client interact properly.
396         */
397         windows_vst_gui_init (&argc, &argv);
398 #endif
399
400         (void) bindtextdomain (PACKAGE, localedir);
401         /* our i18n translations are all in UTF-8, so make sure
402            that even if the user locale doesn't specify UTF-8,
403            we use that when handling them.
404         */
405         (void) bind_textdomain_codeset (PACKAGE,"UTF-8");
406         (void) textdomain (PACKAGE);
407
408         pthread_setcanceltype (PTHREAD_CANCEL_ASYNCHRONOUS, 0);
409
410         // catch error message system signals ();
411
412         text_receiver.listen_to (error);
413         text_receiver.listen_to (info);
414         text_receiver.listen_to (fatal);
415         text_receiver.listen_to (warning);
416
417 #ifdef BOOST_SP_ENABLE_DEBUG_HOOKS
418         if (getenv ("BOOST_DEBUG")) {
419                 boost_debug_shared_ptr_show_live_debugging (true);
420         }
421 #endif
422
423         if (parse_opts (argc, argv)) {
424                 exit (1);
425         }
426
427         if (curvetest_file) {
428                 return curvetest (curvetest_file);
429         }
430
431         cout << PROGRAM_NAME
432              << VERSIONSTRING
433              << _(" (built using ")
434              << svn_revision
435 #ifdef __GNUC__
436              << _(" and GCC version ") << __VERSION__
437 #endif
438              << ')'
439              << endl;
440
441         if (just_version) {
442                 exit (0);
443         }
444
445         if (no_splash) {
446                 cerr << _("Copyright (C) 1999-2012 Paul Davis") << endl
447                      << _("Some portions Copyright (C) Steve Harris, Ari Johnson, Brett Viren, Joel Baker, Robin Gareus") << endl
448                      << endl
449                      << string_compose (_("%1 comes with ABSOLUTELY NO WARRANTY"), PROGRAM_NAME) << endl
450                      << _("not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.") << endl
451                      << _("This is free software, and you are welcome to redistribute it ") << endl
452                      << _("under certain conditions; see the source for copying conditions.")
453                      << endl;
454         }
455
456         /* some GUI objects need this */
457
458         PBD::ID::init ();
459
460         if (::signal (SIGPIPE, sigpipe_handler)) {
461                 cerr << _("Cannot xinstall SIGPIPE error handler") << endl;
462         }
463
464         try {
465                 ui = new ARDOUR_UI (&argc, &argv, localedir);
466         } catch (failed_constructor& err) {
467                 error << _("could not create ARDOUR GUI") << endmsg;
468                 exit (1);
469         }
470
471         ui->run (text_receiver);
472         Gtkmm2ext::Application::instance()->cleanup();
473         ui = 0;
474
475         ARDOUR::cleanup ();
476         pthread_cancel_all ();
477
478         return 0;
479 }
480 #ifdef WINDOWS_VST_SUPPORT
481 } // end of extern C block
482 #endif
483