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