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