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