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