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