tweaks to packaging to make SUIL (and thus LV2 UI's) work
[ardour.git] / gtk2_ardour / main.cc
1 /*
2     Copyright (C) 2001-2007 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 dir_path = Glib::path_get_dirname (execpath);
145         std::string path;
146         const char *cstr = getenv ("PATH");
147
148         /* ensure that we find any bundled executables (e.g. JACK),
149            and find them before any instances of the same name
150            elsewhere in PATH
151         */
152
153         path = dir_path;
154
155         /* JACK is often in /usr/local/bin and since Info.plist refuses to
156            set PATH, we have to force this in order to discover a running
157            instance of JACK ...
158         */
159
160         path += ':';
161         path += "/usr/local/bin";
162
163         if (cstr) {
164                 path += ':';
165                 path += cstr;
166         }
167         setenv ("PATH", path.c_str(), 1);
168
169         export_search_path (dir_path, "ARDOUR_DLL_PATH", "/../lib");
170
171         path += dir_path;
172         path += "/../Resources";
173
174         /* inside an OS X .app bundle, there is no difference
175            between DATA and CONFIG locations, since OS X doesn't
176            attempt to do anything to expose the notion of
177            machine-independent shared data.
178         */
179
180         export_search_path (dir_path, "ARDOUR_DATA_PATH", "/../Resources");
181         export_search_path (dir_path, "ARDOUR_CONFIG_PATH", "/../Resources");
182         export_search_path (dir_path, "ARDOUR_INSTANT_XML_PATH", "/../Resources");
183
184         export_search_path (dir_path, "LADSPA_PATH", "/../Plugins");
185         export_search_path (dir_path, "VAMP_PATH", "/../lib");
186         export_search_path (dir_path, "SUIL_MODULE_DIR", "/../lib");
187
188         path = dir_path;
189         path += "/../lib/clearlooks";
190         setenv ("GTK_PATH", path.c_str(), 1);
191
192         /* unset GTK_RC_FILES so that we only load the RC files that we define
193          */
194
195         unsetenv ("GTK_RC_FILES");
196
197         if (!ARDOUR::translations_are_disabled ()) {
198
199                 path = dir_path;
200                 path += "/../Resources/locale";
201
202                 localedir = strdup (path.c_str());
203                 setenv ("GTK_LOCALEDIR", localedir, 1);
204         }
205
206         /* write a pango.rc file and tell pango to use it. we'd love
207            to put this into the PROGRAM_NAME.app bundle and leave it there,
208            but the user may not have write permission. so ...
209
210            we also have to make sure that the user ardour directory
211            actually exists ...
212         */
213
214         try {
215                 sys::create_directories (user_config_directory ());
216         }
217         catch (const sys::filesystem_error& ex) {
218                 error << _("Could not create user configuration directory") << endmsg;
219         }
220
221         sys::path pangopath = user_config_directory();
222         pangopath /= "pango.rc";
223         path = pangopath.to_string();
224
225         std::ofstream pangorc (path.c_str());
226         if (!pangorc) {
227                 error << string_compose (_("cannot open pango.rc file %1") , path) << endmsg;
228                 return;
229         } else {
230                 pangorc << "[Pango]\nModuleFiles=";
231
232                 pangopath = dir_path;
233                 pangopath /= "..";
234                 pangopath /= "Resources";
235                 pangopath /= "pango.modules";
236
237                 pangorc << pangopath.to_string() << endl;
238
239                 pangorc.close ();
240
241                 setenv ("PANGO_RC_FILE", path.c_str(), 1);
242         }
243
244         // gettext charset aliases
245
246         setenv ("CHARSETALIASDIR", path.c_str(), 1);
247
248         // font config
249
250         path = dir_path;
251         path += "/../Resources/fonts.conf";
252
253         setenv ("FONTCONFIG_FILE", path.c_str(), 1);
254
255         // GDK Pixbuf loader module file
256
257         path = dir_path;
258         path += "/../Resources/gdk-pixbuf.loaders";
259
260         setenv ("GDK_PIXBUF_MODULE_FILE", path.c_str(), 1);
261
262         if (getenv ("ARDOUR_WITH_JACK")) {
263                 // JACK driver dir
264
265                 path = dir_path;
266                 path += "/../lib";
267
268                 setenv ("JACK_DRIVER_DIR", path.c_str(), 1);
269         }
270 }
271
272 #else
273
274 void
275 fixup_bundle_environment (int /*argc*/, char* argv[])
276 {
277         /* THIS IS FOR LINUX - its just about the only place where its
278          * acceptable to build paths directly using '/'.
279          */
280
281         if (!getenv ("ARDOUR_BUNDLED")) {
282                 return;
283         }
284
285         EnvironmentalProtectionAgency::set_global_epa (new EnvironmentalProtectionAgency (true, "PREBUNDLE_ENV"));
286
287         Glib::ustring dir_path = Glib::path_get_dirname (Glib::path_get_dirname (argv[0]));
288         Glib::ustring path;
289         Glib::ustring userconfigdir = user_config_directory().to_string();
290
291         /* note that this function is POSIX/Linux specific, so using / as
292            a dir separator in this context is just fine.
293         */
294
295         export_search_path (dir_path, "ARDOUR_DLL_PATH", "/lib");
296         export_search_path (dir_path, "ARDOUR_CONFIG_PATH", "/etc");
297         export_search_path (dir_path, "ARDOUR_INSTANT_XML_PATH", "/share");
298         export_search_path (dir_path, "ARDOUR_DATA_PATH", "/share");
299
300         export_search_path (dir_path, "LADSPA_PATH", "/../plugins");
301         export_search_path (dir_path, "VAMP_PATH", "/lib");
302         export_search_path (dir_path, "SUIL_MODULE_DIR", "/lib");
303
304         path = dir_path;
305         path += "/lib/clearlooks";
306         setenv ("GTK_PATH", path.c_str(), 1);
307
308         /* unset GTK_RC_FILES so that we only load the RC files that we define
309          */
310
311         unsetenv ("GTK_RC_FILES");
312
313         if (!ARDOUR::translations_are_disabled ()) {
314                 path = dir_path;
315                 path += "/share/locale";
316
317                 localedir = strdup (path.c_str());
318                 setenv ("GTK_LOCALEDIR", localedir, 1);
319         }
320
321         /* Tell fontconfig where to find fonts.conf. Use the system version
322            if it exists, otherwise use the stuff we included in the bundle
323         */
324
325         if (Glib::file_test ("/etc/fonts/fonts.conf", Glib::FILE_TEST_EXISTS)) {
326                 setenv ("FONTCONFIG_FILE", "/etc/fonts/fonts.conf", 1);
327                 setenv ("FONTCONFIG_PATH", "/etc/fonts", 1);
328         } else {
329                 /* use the one included in the bundle */
330                 
331                 path = Glib::build_filename (dir_path, "etc/fonts/fonts.conf");
332                 setenv ("FONTCONFIG_FILE", path.c_str(), 1);
333                 path = Glib::build_filename (dir_path, "etc/fonts");
334                 setenv ("FONTCONFIG_PATH", "/etc/fonts", 1);
335         }
336
337         /* write a pango.rc file and tell pango to use it. we'd love
338            to put this into the Ardour.app bundle and leave it there,
339            but the user may not have write permission. so ...
340
341            we also have to make sure that the user ardour directory
342            actually exists ...
343         */
344
345         if (g_mkdir_with_parents (userconfigdir.c_str(), 0755) < 0) {
346                 error << string_compose (_("cannot create user ardour folder %1 (%2)"), userconfigdir, strerror (errno))
347                       << endmsg;
348                 return;
349         } 
350
351         Glib::ustring mpath;
352         
353         path = Glib::build_filename (userconfigdir, "pango.rc");
354         
355         std::ofstream pangorc (path.c_str());
356         if (!pangorc) {
357                 error << string_compose (_("cannot open pango.rc file %1") , path) << endmsg;
358         } else {
359                 mpath = Glib::build_filename (userconfigdir, "pango.modules");
360                 
361                 pangorc << "[Pango]\nModuleFiles=";
362                 pangorc << mpath << endl;
363                 pangorc.close ();
364         }
365         
366         setenv ("PANGO_RC_FILE", path.c_str(), 1);
367
368         /* similar for GDK pixbuf loaders, but there's no RC file required
369            to specify where it lives.
370         */
371         
372         mpath = Glib::build_filename (userconfigdir, "gdk-pixbuf.loaders");
373         setenv ("GDK_PIXBUF_MODULE_FILE", mpath.c_str(), 1);
374 }
375
376 #endif
377
378 static gboolean
379 tell_about_jack_death (void* /* ignored */)
380 {
381         if (AudioEngine::instance()->processed_frames() == 0) {
382                 /* died during startup */
383                 MessageDialog msg (_("JACK exited"), false);
384                 msg.set_position (Gtk::WIN_POS_CENTER);
385                 msg.set_secondary_text (string_compose (_(
386 "JACK exited unexpectedly, and without notifying %1.\n\
387 \n\
388 This could be due to misconfiguration or to an error inside JACK.\n\
389 \n\
390 Click OK to exit %1."), PROGRAM_NAME));
391
392                 msg.run ();
393                 _exit (0);
394
395         } else {
396
397                 /* engine has already run, so this is a mid-session JACK death */
398
399                 MessageDialog* msg = manage (new MessageDialog (_("JACK exited"), false));
400                 msg->set_secondary_text (string_compose (_(
401 "JACK exited unexpectedly, and without notifying %1.\n\
402 \n\
403 This is probably due to an error inside JACK. You should restart JACK\n\
404 and reconnect %1 to it, or exit %1 now. You cannot save your\n\
405 session at this time, because we would lose your connection information.\n"), PROGRAM_NAME));
406                 msg->present ();
407         }
408         return false; /* do not call again */
409 }
410
411 static void
412 sigpipe_handler (int /*signal*/)
413 {
414         /* XXX fix this so that we do this again after a reconnect to JACK
415          */
416
417         static bool done_the_jack_thing = false;
418
419         if (!done_the_jack_thing) {
420                 AudioEngine::instance()->died ();
421                 g_idle_add (tell_about_jack_death, 0);
422                 done_the_jack_thing =  true;
423         }
424 }
425
426 #ifdef HAVE_LV2
427 void close_external_ui_windows();
428 #endif
429
430 #ifdef WINDOWS_VST_SUPPORT
431
432 extern int windows_vst_gui_init (int* argc, char** argv[]);
433
434 /* this is called from the entry point of a wine-compiled
435    executable that is linked against gtk2_ardour built
436    as a shared library.
437 */
438 extern "C" {
439 int ardour_main (int argc, char *argv[])
440 #else
441 int main (int argc, char *argv[])
442 #endif
443 {
444         fixup_bundle_environment (argc, argv);
445
446         if (!Glib::thread_supported()) {
447                 Glib::thread_init();
448         }
449
450         gtk_set_locale ();
451
452 #ifdef WINDOWS_VST_SUPPORT
453         /* this does some magic that is needed to make GTK and Wine's own
454            X11 client interact properly.
455         */
456         windows_vst_gui_init (&argc, &argv);
457 #endif
458
459         (void) bindtextdomain (PACKAGE, localedir);
460         /* our i18n translations are all in UTF-8, so make sure
461            that even if the user locale doesn't specify UTF-8,
462            we use that when handling them.
463         */
464         (void) bind_textdomain_codeset (PACKAGE,"UTF-8");
465         (void) textdomain (PACKAGE);
466
467         pthread_setcanceltype (PTHREAD_CANCEL_ASYNCHRONOUS, 0);
468
469         // catch error message system signals ();
470
471         text_receiver.listen_to (error);
472         text_receiver.listen_to (info);
473         text_receiver.listen_to (fatal);
474         text_receiver.listen_to (warning);
475
476 #ifdef BOOST_SP_ENABLE_DEBUG_HOOKS
477         if (getenv ("BOOST_DEBUG")) {
478                 boost_debug_shared_ptr_show_live_debugging (true);
479         }
480 #endif
481
482         if (parse_opts (argc, argv)) {
483                 exit (1);
484         }
485
486         if (curvetest_file) {
487                 return curvetest (curvetest_file);
488         }
489
490         cout << PROGRAM_NAME
491              << VERSIONSTRING
492              << _(" (built using ")
493              << svn_revision
494 #ifdef __GNUC__
495              << _(" and GCC version ") << __VERSION__
496 #endif
497              << ')'
498              << endl;
499
500         if (just_version) {
501                 exit (0);
502         }
503
504         if (no_splash) {
505                 cerr << _("Copyright (C) 1999-2012 Paul Davis") << endl
506                      << _("Some portions Copyright (C) Steve Harris, Ari Johnson, Brett Viren, Joel Baker") << endl
507                      << endl
508                      << string_compose (_("%1 comes with ABSOLUTELY NO WARRANTY"), PROGRAM_NAME) << endl
509                      << _("not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.") << endl
510                      << _("This is free software, and you are welcome to redistribute it ") << endl
511                      << _("under certain conditions; see the source for copying conditions.")
512                      << endl;
513         }
514
515         /* some GUI objects need this */
516
517         PBD::ID::init ();
518
519         if (::signal (SIGPIPE, sigpipe_handler)) {
520                 cerr << _("Cannot xinstall SIGPIPE error handler") << endl;
521         }
522
523         try {
524                 ui = new ARDOUR_UI (&argc, &argv);
525         } catch (failed_constructor& err) {
526                 error << _("could not create ARDOUR GUI") << endmsg;
527                 exit (1);
528         }
529
530         ui->run (text_receiver);
531         Gtkmm2ext::Application::instance()->cleanup();
532         ui = 0;
533
534         ARDOUR::cleanup ();
535         pthread_cancel_all ();
536
537 #ifdef HAVE_LV2
538         close_external_ui_windows();
539 #endif
540         return 0;
541 }
542 #ifdef WINDOWS_VST_SUPPORT
543 } // end of extern C block
544 #endif
545