fix OS X compilation (i hope)
[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 <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         std::string ardour_mono_file;
226
227         if (!find_file_in_search_path (ardour_data_search_path(), "ArdourMono.ttf", ardour_mono_file)) {
228                 cerr << _("Cannot find ArdourMono TrueType font") << endl;
229         }
230
231         CFStringRef ttf;
232         CFURLRef fontURL;
233         CFErrorRef error;
234         ttf = CFStringCreateWithBytes(
235                         kCFAllocatorDefault, (UInt8*) ardour_mono_file.c_str(),
236                         ardour_mono_file.length(),
237                         kCFStringEncodingUTF8, FALSE);
238         fontURL = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, ttf, kCFURLPOSIXPathStyle, TRUE);
239         if (CTFontManagerRegisterFontsForURL(fontURL, kCTFontManagerScopeProcess, &error) != true) {
240                 cerr << _("Cannot load ArdourMono TrueType font.") << endl;
241         }
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 (!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 = realpath (Glib::build_filename (lpath).c_str(), NULL);
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         /* unset GTK_RC_FILES so that we only load the RC files that we define
290          */
291
292         unsetenv ("GTK_RC_FILES");
293
294         /* Tell fontconfig where to find fonts.conf. Use the system version
295            if it exists, otherwise use the stuff we included in the bundle
296         */
297
298         if (Glib::file_test ("/etc/fonts/fonts.conf", Glib::FILE_TEST_EXISTS)) {
299                 setenv ("FONTCONFIG_FILE", "/etc/fonts/fonts.conf", 1);
300                 setenv ("FONTCONFIG_PATH", "/etc/fonts", 1);
301         } else {
302                 error << _("No fontconfig file found on your system. Things may looked very odd or ugly") << endmsg;
303         }
304
305         /* write a pango.rc file and tell pango to use it. we'd love
306            to put this into the Ardour.app bundle and leave it there,
307            but the user may not have write permission. so ...
308
309            we also have to make sure that the user ardour directory
310            actually exists ...
311         */
312
313         if (g_mkdir_with_parents (userconfigdir.c_str(), 0755) < 0) {
314                 error << string_compose (_("cannot create user %3 folder %1 (%2)"), userconfigdir, strerror (errno), PROGRAM_NAME)
315                       << endmsg;
316         } else {
317                 
318                 path = Glib::build_filename (userconfigdir, "pango.rc");
319                 std::ofstream pangorc (path.c_str());
320                 if (!pangorc) {
321                         error << string_compose (_("cannot open pango.rc file %1") , path) << endmsg;
322                 } else {
323                         pangorc << "[Pango]\nModuleFiles="
324                                 << Glib::build_filename (userconfigdir, "pango.modules")
325                                 << endl;
326                         pangorc.close ();
327                 }
328                 
329                 setenv ("PANGO_RC_FILE", path.c_str(), 1);
330                 
331                 /* similar for GDK pixbuf loaders, but there's no RC file required
332                    to specify where it lives.
333                 */
334                 
335                 setenv ("GDK_PIXBUF_MODULE_FILE", Glib::build_filename (userconfigdir, "gdk-pixbuf.loaders").c_str(), 1);
336         }
337
338         /* this doesn't do much but setting it should prevent various parts of the GTK/GNU stack
339            from looking outside the bundle to find the charset.alias file.
340         */
341         setenv ("CHARSETALIASDIR", dir_path.c_str(), 1);
342
343 }
344
345 static void load_custom_fonts() {
346         std::string ardour_mono_file;
347         if (!find_file_in_search_path (ardour_data_search_path(), "ArdourMono.ttf", ardour_mono_file)) {
348                 cerr << _("Cannot find ArdourMono TrueType font") << endl;
349         }
350
351         FcConfig *config = FcInitLoadConfigAndFonts();
352         FcBool ret = FcConfigAppFontAddFile(config, reinterpret_cast<const FcChar8*>(ardour_mono_file.c_str()));
353         if (ret == FcFalse) {
354                 cerr << _("Cannot load ArdourMono TrueType font.") << endl;
355         }
356         ret = FcConfigSetCurrent(config);
357         if (ret == FcFalse) {
358                 cerr << _("Failed to set fontconfig configuration.") << endl;
359         }
360 }
361
362 #endif
363
364 static gboolean
365 tell_about_jack_death (void* /* ignored */)
366 {
367         if (AudioEngine::instance()->processed_frames() == 0) {
368                 /* died during startup */
369                 MessageDialog msg (_("JACK exited"), false);
370                 msg.set_position (Gtk::WIN_POS_CENTER);
371                 msg.set_secondary_text (string_compose (_(
372 "JACK exited unexpectedly, and without notifying %1.\n\
373 \n\
374 This could be due to misconfiguration or to an error inside JACK.\n\
375 \n\
376 Click OK to exit %1."), PROGRAM_NAME));
377
378                 msg.run ();
379                 _exit (0);
380
381         } else {
382
383                 /* engine has already run, so this is a mid-session JACK death */
384
385                 MessageDialog* msg = manage (new MessageDialog (_("JACK exited"), false));
386                 msg->set_secondary_text (string_compose (_(
387 "JACK exited unexpectedly, and without notifying %1.\n\
388 \n\
389 This is probably due to an error inside JACK. You should restart JACK\n\
390 and reconnect %1 to it, or exit %1 now. You cannot save your\n\
391 session at this time, because we would lose your connection information.\n"), PROGRAM_NAME));
392                 msg->present ();
393         }
394         return false; /* do not call again */
395 }
396
397 static void
398 sigpipe_handler (int /*signal*/)
399 {
400         /* XXX fix this so that we do this again after a reconnect to JACK
401          */
402
403         static bool done_the_jack_thing = false;
404
405         if (!done_the_jack_thing) {
406                 AudioEngine::instance()->died ();
407                 g_idle_add (tell_about_jack_death, 0);
408                 done_the_jack_thing =  true;
409         }
410 }
411
412 #ifdef WINDOWS_VST_SUPPORT
413
414 extern int windows_vst_gui_init (int* argc, char** argv[]);
415
416 /* this is called from the entry point of a wine-compiled
417    executable that is linked against gtk2_ardour built
418    as a shared library.
419 */
420 extern "C" {
421 int ardour_main (int argc, char *argv[])
422 #else
423 int main (int argc, char *argv[])
424 #endif
425 {
426         fixup_bundle_environment (argc, argv);
427
428         load_custom_fonts(); /* needs to happend before any gtk and pango init calls */
429
430         if (!Glib::thread_supported()) {
431                 Glib::thread_init();
432         }
433
434 #ifdef ENABLE_NLS
435         gtk_set_locale ();
436 #endif
437
438 #ifdef WINDOWS_VST_SUPPORT
439         /* this does some magic that is needed to make GTK and Wine's own
440            X11 client interact properly.
441         */
442         windows_vst_gui_init (&argc, &argv);
443 #endif
444
445 #ifdef ENABLE_NLS
446         cerr << "bnd txt domain [" << PACKAGE << "] to " << localedir << endl;
447
448         (void) bindtextdomain (PACKAGE, localedir);
449         /* our i18n translations are all in UTF-8, so make sure
450            that even if the user locale doesn't specify UTF-8,
451            we use that when handling them.
452         */
453         (void) bind_textdomain_codeset (PACKAGE,"UTF-8");
454 #endif
455
456         pthread_setcanceltype (PTHREAD_CANCEL_ASYNCHRONOUS, 0);
457
458         // catch error message system signals ();
459
460         text_receiver.listen_to (error);
461         text_receiver.listen_to (info);
462         text_receiver.listen_to (fatal);
463         text_receiver.listen_to (warning);
464
465 #ifdef BOOST_SP_ENABLE_DEBUG_HOOKS
466         if (getenv ("BOOST_DEBUG")) {
467                 boost_debug_shared_ptr_show_live_debugging (true);
468         }
469 #endif
470
471         if (parse_opts (argc, argv)) {
472                 exit (1);
473         }
474
475         if (curvetest_file) {
476                 return curvetest (curvetest_file);
477         }
478
479         cout << PROGRAM_NAME
480              << VERSIONSTRING
481              << _(" (built using ")
482              << svn_revision
483 #ifdef __GNUC__
484              << _(" and GCC version ") << __VERSION__
485 #endif
486              << ')'
487              << endl;
488
489         if (just_version) {
490                 exit (0);
491         }
492
493         if (no_splash) {
494                 cerr << _("Copyright (C) 1999-2012 Paul Davis") << endl
495                      << _("Some portions Copyright (C) Steve Harris, Ari Johnson, Brett Viren, Joel Baker, Robin Gareus") << endl
496                      << endl
497                      << string_compose (_("%1 comes with ABSOLUTELY NO WARRANTY"), PROGRAM_NAME) << endl
498                      << _("not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.") << endl
499                      << _("This is free software, and you are welcome to redistribute it ") << endl
500                      << _("under certain conditions; see the source for copying conditions.")
501                      << endl;
502         }
503
504         /* some GUI objects need this */
505
506         PBD::ID::init ();
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         ui = 0;
522
523         ARDOUR::cleanup ();
524         pthread_cancel_all ();
525
526         return 0;
527 }
528 #ifdef WINDOWS_VST_SUPPORT
529 } // end of extern C block
530 #endif
531