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