Use std::string instead of PBD::sys::path in pbd/search_path.h, pbd/file_utils.h...
[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         } else {
188                 
189                 path = Glib::build_filename (userconfigdir, "pango.rc");
190                 std::ofstream pangorc (path.c_str());
191                 if (!pangorc) {
192                         error << string_compose (_("cannot open pango.rc file %1") , path) << endmsg;
193                 } else {
194                         pangorc << "[Pango]\nModuleFiles="
195                                 << Glib::build_filename (bundle_dir, "Resources/pango.modules") 
196                                 << endl;
197                         pangorc.close ();
198                         
199                         setenv ("PANGO_RC_FILE", path.c_str(), 1);
200                 }
201         }
202         
203         setenv ("CHARSETALIASDIR", bundle_dir.c_str(), 1);
204         setenv ("FONTCONFIG_FILE", Glib::build_filename (bundle_dir, "Resources/fonts.conf").c_str(), 1);
205         setenv ("GDK_PIXBUF_MODULE_FILE", Glib::build_filename (bundle_dir, "Resources/gdk-pixbuf.loaders").c_str(), 1);
206 }
207
208 #else
209
210 void
211 fixup_bundle_environment (int /*argc*/, char* argv[])
212 {
213         /* THIS IS FOR LINUX - its just about the only place where its
214          * acceptable to build paths directly using '/'.
215          */
216
217         if (!getenv ("ARDOUR_BUNDLED")) {
218                 return;
219         }
220
221         EnvironmentalProtectionAgency::set_global_epa (new EnvironmentalProtectionAgency (true, "PREBUNDLE_ENV"));
222
223         std::string path;
224         std::string dir_path = Glib::path_get_dirname (Glib::path_get_dirname (argv[0]));
225         std::string userconfigdir = user_config_directory();
226
227         /* note that this function is POSIX/Linux specific, so using / as
228            a dir separator in this context is just fine.
229         */
230
231         export_search_path (dir_path, "ARDOUR_DLL_PATH", "/lib");
232         export_search_path (dir_path, "ARDOUR_CONFIG_PATH", "/etc");
233         export_search_path (dir_path, "ARDOUR_INSTANT_XML_PATH", "/share");
234         export_search_path (dir_path, "ARDOUR_DATA_PATH", "/share");
235         export_search_path (dir_path, "LADSPA_PATH", "/plugins");
236         export_search_path (dir_path, "VAMP_PATH", "/lib");
237         export_search_path (dir_path, "SUIL_MODULE_DIR", "/lib");
238
239         export_search_path (dir_path, "GTK_PATH", "/lib/clearlooks");
240
241         /* unset GTK_RC_FILES so that we only load the RC files that we define
242          */
243
244         unsetenv ("GTK_RC_FILES");
245
246         if (!ARDOUR::translations_are_disabled ()) {
247                 export_search_path (dir_path, "GTK_LOCALEDIR", "/share/locale");
248         }
249
250         /* Tell fontconfig where to find fonts.conf. Use the system version
251            if it exists, otherwise use the stuff we included in the bundle
252         */
253
254         if (Glib::file_test ("/etc/fonts/fonts.conf", Glib::FILE_TEST_EXISTS)) {
255                 setenv ("FONTCONFIG_FILE", "/etc/fonts/fonts.conf", 1);
256                 setenv ("FONTCONFIG_PATH", "/etc/fonts", 1);
257         } else {
258                 /* use the one included in the bundle */
259                 
260                 path = Glib::build_filename (dir_path, "etc/fonts/fonts.conf");
261                 setenv ("FONTCONFIG_FILE", path.c_str(), 1);
262                 export_search_path (dir_path, "FONTCONFIG_PATH", "/etc/fonts");
263         }
264
265         /* write a pango.rc file and tell pango to use it. we'd love
266            to put this into the Ardour.app bundle and leave it there,
267            but the user may not have write permission. so ...
268
269            we also have to make sure that the user ardour directory
270            actually exists ...
271         */
272
273         if (g_mkdir_with_parents (userconfigdir.c_str(), 0755) < 0) {
274                 error << string_compose (_("cannot create user ardour folder %1 (%2)"), userconfigdir, strerror (errno))
275                       << endmsg;
276         } else {
277                 
278                 path = Glib::build_filename (userconfigdir, "pango.rc");
279                 std::ofstream pangorc (path.c_str());
280                 if (!pangorc) {
281                         error << string_compose (_("cannot open pango.rc file %1") , path) << endmsg;
282                 } else {
283                         pangorc << "[Pango]\nModuleFiles="
284                                 << Glib::build_filename (userconfigdir, "pango.modules")
285                                 << endl;
286                         pangorc.close ();
287                 }
288                 
289                 setenv ("PANGO_RC_FILE", path.c_str(), 1);
290                 
291                 /* similar for GDK pixbuf loaders, but there's no RC file required
292                    to specify where it lives.
293                 */
294                 
295                 setenv ("GDK_PIXBUF_MODULE_FILE", Glib::build_filename (userconfigdir, "gdk-pixbuf.loaders").c_str(), 1);
296         }
297
298         /* this doesn't do much but setting it should prevent various parts of the GTK/GNU stack
299            from looking outside the bundle to find the charset.alias file.
300         */
301         setenv ("CHARSETALIASDIR", dir_path.c_str(), 1);
302
303 }
304
305 #endif
306
307 static gboolean
308 tell_about_jack_death (void* /* ignored */)
309 {
310         if (AudioEngine::instance()->processed_frames() == 0) {
311                 /* died during startup */
312                 MessageDialog msg (_("JACK exited"), false);
313                 msg.set_position (Gtk::WIN_POS_CENTER);
314                 msg.set_secondary_text (string_compose (_(
315 "JACK exited unexpectedly, and without notifying %1.\n\
316 \n\
317 This could be due to misconfiguration or to an error inside JACK.\n\
318 \n\
319 Click OK to exit %1."), PROGRAM_NAME));
320
321                 msg.run ();
322                 _exit (0);
323
324         } else {
325
326                 /* engine has already run, so this is a mid-session JACK death */
327
328                 MessageDialog* msg = manage (new MessageDialog (_("JACK exited"), false));
329                 msg->set_secondary_text (string_compose (_(
330 "JACK exited unexpectedly, and without notifying %1.\n\
331 \n\
332 This is probably due to an error inside JACK. You should restart JACK\n\
333 and reconnect %1 to it, or exit %1 now. You cannot save your\n\
334 session at this time, because we would lose your connection information.\n"), PROGRAM_NAME));
335                 msg->present ();
336         }
337         return false; /* do not call again */
338 }
339
340 static void
341 sigpipe_handler (int /*signal*/)
342 {
343         /* XXX fix this so that we do this again after a reconnect to JACK
344          */
345
346         static bool done_the_jack_thing = false;
347
348         if (!done_the_jack_thing) {
349                 AudioEngine::instance()->died ();
350                 g_idle_add (tell_about_jack_death, 0);
351                 done_the_jack_thing =  true;
352         }
353 }
354
355 #ifdef WINDOWS_VST_SUPPORT
356
357 extern int windows_vst_gui_init (int* argc, char** argv[]);
358
359 /* this is called from the entry point of a wine-compiled
360    executable that is linked against gtk2_ardour built
361    as a shared library.
362 */
363 extern "C" {
364 int ardour_main (int argc, char *argv[])
365 #else
366 int main (int argc, char *argv[])
367 #endif
368 {
369         fixup_bundle_environment (argc, argv);
370
371         if (!Glib::thread_supported()) {
372                 Glib::thread_init();
373         }
374
375         gtk_set_locale ();
376
377 #ifdef WINDOWS_VST_SUPPORT
378         /* this does some magic that is needed to make GTK and Wine's own
379            X11 client interact properly.
380         */
381         windows_vst_gui_init (&argc, &argv);
382 #endif
383
384         (void) bindtextdomain (PACKAGE, localedir);
385         /* our i18n translations are all in UTF-8, so make sure
386            that even if the user locale doesn't specify UTF-8,
387            we use that when handling them.
388         */
389         (void) bind_textdomain_codeset (PACKAGE,"UTF-8");
390         (void) textdomain (PACKAGE);
391
392         pthread_setcanceltype (PTHREAD_CANCEL_ASYNCHRONOUS, 0);
393
394         // catch error message system signals ();
395
396         text_receiver.listen_to (error);
397         text_receiver.listen_to (info);
398         text_receiver.listen_to (fatal);
399         text_receiver.listen_to (warning);
400
401 #ifdef BOOST_SP_ENABLE_DEBUG_HOOKS
402         if (getenv ("BOOST_DEBUG")) {
403                 boost_debug_shared_ptr_show_live_debugging (true);
404         }
405 #endif
406
407         if (parse_opts (argc, argv)) {
408                 exit (1);
409         }
410
411         if (curvetest_file) {
412                 return curvetest (curvetest_file);
413         }
414
415         cout << PROGRAM_NAME
416              << VERSIONSTRING
417              << _(" (built using ")
418              << svn_revision
419 #ifdef __GNUC__
420              << _(" and GCC version ") << __VERSION__
421 #endif
422              << ')'
423              << endl;
424
425         if (just_version) {
426                 exit (0);
427         }
428
429         if (no_splash) {
430                 cerr << _("Copyright (C) 1999-2012 Paul Davis") << endl
431                      << _("Some portions Copyright (C) Steve Harris, Ari Johnson, Brett Viren, Joel Baker") << endl
432                      << endl
433                      << string_compose (_("%1 comes with ABSOLUTELY NO WARRANTY"), PROGRAM_NAME) << endl
434                      << _("not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.") << endl
435                      << _("This is free software, and you are welcome to redistribute it ") << endl
436                      << _("under certain conditions; see the source for copying conditions.")
437                      << endl;
438         }
439
440         /* some GUI objects need this */
441
442         PBD::ID::init ();
443
444         if (::signal (SIGPIPE, sigpipe_handler)) {
445                 cerr << _("Cannot xinstall SIGPIPE error handler") << endl;
446         }
447
448         try {
449                 ui = new ARDOUR_UI (&argc, &argv);
450         } catch (failed_constructor& err) {
451                 error << _("could not create ARDOUR GUI") << endmsg;
452                 exit (1);
453         }
454
455         ui->run (text_receiver);
456         Gtkmm2ext::Application::instance()->cleanup();
457         ui = 0;
458
459         ARDOUR::cleanup ();
460         pthread_cancel_all ();
461
462         return 0;
463 }
464 #ifdef WINDOWS_VST_SUPPORT
465 } // end of extern C block
466 #endif
467