add EPA stuff from 2.X
[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
25 #include <sigc++/bind.h>
26 #include <gtkmm/settings.h>
27
28 #include "pbd/error.h"
29 #include "pbd/epa.h"
30 #include "pbd/file_utils.h"
31 #include "pbd/textreceiver.h"
32 #include "pbd/failed_constructor.h"
33 #include "pbd/pthread_utils.h"
34
35 #include <jack/jack.h>
36
37 #include "ardour/svn_revision.h"
38 #include "ardour/version.h"
39 #include "ardour/ardour.h"
40 #include "ardour/audioengine.h"
41 #include "ardour/session_utils.h"
42 #include "ardour/filesystem_paths.h"
43
44 #include <gtkmm/main.h>
45 #include <gtkmm2ext/application.h>
46 #include <gtkmm2ext/popup.h>
47 #include <gtkmm2ext/utils.h>
48
49 #include "version.h"
50 #include "utils.h"
51 #include "ardour_ui.h"
52 #include "opts.h"
53 #include "enums.h"
54
55 #include "i18n.h"
56
57 using namespace std;
58 using namespace Gtk;
59 using namespace ARDOUR_COMMAND_LINE;
60 using namespace ARDOUR;
61 using namespace PBD;
62
63 TextReceiver text_receiver ("ardour");
64
65 extern int curvetest (string);
66
67 static ARDOUR_UI  *ui = 0;
68 static const char* localedir = LOCALEDIR;
69
70 void
71 gui_jack_error ()
72 {
73         MessageDialog win (string_compose (_("%1 could not connect to JACK."), PROGRAM_NAME),
74                      false,
75                      Gtk::MESSAGE_INFO,
76                      (Gtk::ButtonsType)(Gtk::BUTTONS_NONE));
77 win.set_secondary_text(_("There are several possible reasons:\n\
78 \n\
79 1) JACK is not running.\n\
80 2) JACK is running as another user, perhaps root.\n\
81 3) There is already another client called \"ardour\".\n\
82 \n\
83 Please consider the possibilities, and perhaps (re)start JACK."));
84
85         win.add_button (Stock::QUIT, RESPONSE_CLOSE);
86         win.set_default_response (RESPONSE_CLOSE);
87
88         win.show_all ();
89         win.set_position (Gtk::WIN_POS_CENTER);
90
91         if (!no_splash) {
92                 ui->hide_splash ();
93         }
94
95         /* we just don't care about the result, but we want to block */
96
97         win.run ();
98 }
99
100
101 #ifdef __APPLE__
102
103 #include <mach-o/dyld.h>
104 #include <sys/param.h>
105
106 extern void set_language_preference (); // cocoacarbon.mm
107
108 void
109 fixup_bundle_environment ()
110 {
111         if (!getenv ("ARDOUR_BUNDLED")) {
112                 return;
113         }
114
115         set_language_preference ();
116
117         char execpath[MAXPATHLEN+1];
118         uint32_t pathsz = sizeof (execpath);
119
120         _NSGetExecutablePath (execpath, &pathsz);
121
122         std::string exec_path (execpath);
123         std::string dir_path = Glib::path_get_dirname (exec_path);
124         std::string path;
125         const char *cstr = getenv ("PATH");
126
127         /* ensure that we find any bundled executables (e.g. JACK),
128            and find them before any instances of the same name
129            elsewhere in PATH
130         */
131
132         path = dir_path;
133
134         /* JACK is often in /usr/local/bin and since Info.plist refuses to 
135            set PATH, we have to force this in order to discover a running
136            instance of JACK ...
137         */
138         
139         path += ':';
140         path += "/usr/local/bin";
141
142         if (cstr) {
143                 path += ':';
144                 path += cstr;
145         }
146         setenv ("PATH", path.c_str(), 1);
147
148         path = dir_path;
149         path += "/../Resources";
150         path += dir_path;
151         path += "/../Resources/Surfaces";
152         path += dir_path;
153         path += "/../Resources/Panners";
154
155         setenv ("ARDOUR_MODULE_PATH", path.c_str(), 1);
156
157         path = user_config_directory().to_string();
158         path += ':';
159         path += dir_path;
160         path += "/../Resources/icons:";
161         path += dir_path;
162         path += "/../Resources/pixmaps:";
163         path += dir_path;
164         path += "/../Resources/share:";
165         path += dir_path;
166         path += "/../Resources";
167
168         setenv ("ARDOUR_PATH", path.c_str(), 1);
169         setenv ("ARDOUR_CONFIG_PATH", path.c_str(), 1);
170
171         path = dir_path;
172         path += "/../Resources";
173         setenv ("ARDOUR_INSTANT_XML_PATH", path.c_str(), 1);
174
175         cstr = getenv ("LADSPA_PATH");
176         if (cstr) {
177                 path = cstr;
178                 path += ':';
179         } else {
180                 path = "";
181         }
182         path += dir_path;
183         path += "/../Plugins";
184
185         setenv ("LADSPA_PATH", path.c_str(), 1);
186
187         cstr = getenv ("VAMP_PATH");
188         if (cstr) {
189                 path = cstr;
190                 path += ':';
191         } else {
192                 path = "";
193         }
194         path += dir_path;
195         path += "/../Frameworks";
196
197         setenv ("VAMP_PATH", path.c_str(), 1);
198
199         cstr = getenv ("ARDOUR_CONTROL_SURFACE_PATH");
200         if (cstr) {
201                 path = cstr;
202                 path += ':';
203         } else {
204                 path = "";
205         }
206         path += dir_path;
207         path += "/../Surfaces";
208
209         setenv ("ARDOUR_CONTROL_SURFACE_PATH", path.c_str(), 1);
210
211         cstr = getenv ("LV2_PATH");
212         if (cstr) {
213                 path = cstr;
214                 path += ':';
215         } else {
216                 path = "";
217         }
218         path += dir_path;
219         path += "/../Plugins";
220
221         setenv ("LV2_PATH", path.c_str(), 1);
222
223         path = dir_path;
224         path += "/../Frameworks/clearlooks";
225
226         setenv ("GTK_PATH", path.c_str(), 1);
227
228         if (!ARDOUR::translations_are_disabled ()) {
229
230                 path = dir_path;
231                 path += "/../Resources/locale";
232                 
233                 localedir = strdup (path.c_str());
234                 setenv ("GTK_LOCALEDIR", localedir, 1);
235         }
236
237         /* write a pango.rc file and tell pango to use it. we'd love
238            to put this into the PROGRAM_NAME.app bundle and leave it there,
239            but the user may not have write permission. so ...
240
241            we also have to make sure that the user ardour directory
242            actually exists ...
243         */
244
245         try {
246                 sys::create_directories (user_config_directory ());
247         }
248         catch (const sys::filesystem_error& ex) {
249                 error << _("Could not create user configuration directory") << endmsg;
250         }
251
252         sys::path pangopath = user_config_directory();
253         pangopath /= "pango.rc";
254         path = pangopath.to_string();
255
256         std::ofstream pangorc (path.c_str());
257         if (!pangorc) {
258                 error << string_compose (_("cannot open pango.rc file %1") , path) << endmsg;
259                 return;
260         } else {
261                 pangorc << "[Pango]\nModuleFiles=";
262
263                 pangopath = dir_path;
264                 pangopath /= "..";
265                 pangopath /= "Resources";
266                 pangopath /= "pango.modules";
267
268                 pangorc << pangopath.to_string() << endl;
269
270                 pangorc.close ();
271
272                 setenv ("PANGO_RC_FILE", path.c_str(), 1);
273         }
274
275         // gettext charset aliases
276
277         setenv ("CHARSETALIASDIR", path.c_str(), 1);
278
279         // font config
280
281         path = dir_path;
282         path += "/../Resources/fonts.conf";
283
284         setenv ("FONTCONFIG_FILE", path.c_str(), 1);
285
286         // GDK Pixbuf loader module file
287
288         path = dir_path;
289         path += "/../Resources/gdk-pixbuf.loaders";
290
291         setenv ("GDK_PIXBUF_MODULE_FILE", path.c_str(), 1);
292
293         if (getenv ("ARDOUR_WITH_JACK")) {
294                 // JACK driver dir
295
296                 path = dir_path;
297                 path += "/../Frameworks";
298
299                 setenv ("JACK_DRIVER_DIR", path.c_str(), 1);
300         }
301 }
302
303 #else
304
305 void
306 fixup_bundle_environment (int argc, char* argv[])
307 {
308         if (!getenv ("ARDOUR_BUNDLED")) {
309                 return;
310         }
311         
312         EnvironmentalProtectionAgency::set_global_epa (new EnvironmentalProtectionAgency);
313
314         Glib::ustring exec_path = argv[0];
315         Glib::ustring dir_path = Glib::path_get_dirname (Glib::path_get_dirname (exec_path));
316         Glib::ustring path;
317         const char *cstr = getenv ("PATH");
318         Glib::ustring userconfigdir = user_config_directory().to_string();
319
320         /* ensure that we find any bundled executables (e.g. JACK),
321            and find them before any instances of the same name
322            elsewhere in PATH
323         */
324
325         /* note that this function is POSIX/Linux specific, so using / as 
326            a dir separator in this context is just fine.
327         */
328
329         path = dir_path;
330         path += "/etc:";
331         path += dir_path;
332         path += "/lib/surfaces:";
333         path += dir_path;
334         path += "/lib/panners:";
335
336         setenv ("ARDOUR_MODULE_PATH", path.c_str(), 1);
337
338         path = userconfigdir;
339         path += ':';
340         path += dir_path;
341         path += "/etc/icons:";
342         path += dir_path;
343         path += "/etc/pixmaps:";
344         path += dir_path;
345         path += "/share:";
346         path += dir_path;
347         path += "/etc";
348
349         setenv ("ARDOUR_PATH", path.c_str(), 1);
350         setenv ("ARDOUR_CONFIG_PATH", path.c_str(), 1);
351
352         path = dir_path;
353         path += "/etc";
354         setenv ("ARDOUR_INSTANT_XML_PATH", path.c_str(), 1);
355
356         cstr = getenv ("LADSPA_PATH");
357         if (cstr) {
358                 path = cstr;
359                 path += ':';
360         } else {
361                 path = "";
362         }
363         path += dir_path;
364         path += "/lib/plugins";
365         
366         setenv ("LADSPA_PATH", path.c_str(), 1);
367
368         cstr = getenv ("VAMP_PATH");
369         if (cstr) {
370                 path = cstr;
371                 path += ':';
372         } else {
373                 path = "";
374         }
375         path += dir_path;
376         path += "/lib";
377         
378         setenv ("VAMP_PATH", path.c_str(), 1);
379
380         cstr = getenv ("ARDOUR_CONTROL_SURFACE_PATH");
381         if (cstr) {
382                 path = cstr;
383                 path += ':';
384         } else {
385                 path = "";
386         }
387         path += dir_path;
388         path += "/lib/surfaces";
389         
390         setenv ("ARDOUR_CONTROL_SURFACE_PATH", path.c_str(), 1);
391
392         cstr = getenv ("LV2_PATH");
393         if (cstr) {
394                 path = cstr;
395                 path += ':';
396         } else {
397                 path = "";
398         }
399         path += dir_path;
400         path += "/lib/plugins";
401         
402         setenv ("LV2_PATH", path.c_str(), 1);
403
404         path = dir_path;
405         path += "/lib/clearlooks";
406
407         setenv ("GTK_PATH", path.c_str(), 1);
408
409         if (!ARDOUR::translations_are_disabled ()) {
410                 path = dir_path;
411                 path += "/share/locale";
412                 
413                 localedir = strdup (path.c_str());
414                 setenv ("GTK_LOCALEDIR", localedir, 1);
415         }
416
417         /* write a pango.rc file and tell pango to use it. we'd love
418            to put this into the Ardour.app bundle and leave it there,
419            but the user may not have write permission. so ... 
420
421            we also have to make sure that the user ardour directory
422            actually exists ...
423         */
424
425         if (g_mkdir_with_parents (userconfigdir.c_str(), 0755) < 0) {
426                 error << string_compose (_("cannot create user ardour folder %1 (%2)"), userconfigdir, strerror (errno))
427                       << endmsg;
428         } else {
429
430                 Glib::ustring mpath;
431
432                 path = Glib::build_filename (userconfigdir, "pango.rc");
433
434                 std::ofstream pangorc (path.c_str());
435                 if (!pangorc) {
436                         error << string_compose (_("cannot open pango.rc file %1") , path) << endmsg;
437                 } else {
438                         mpath = Glib::build_filename (userconfigdir, "pango.modules");
439                         
440                         pangorc << "[Pango]\nModuleFiles=";
441                         pangorc << mpath << endl;
442                         pangorc.close ();
443                 }
444
445                 setenv ("PANGO_RC_FILE", path.c_str(), 1);
446
447                 /* similar for GDK pixbuf loaders, but there's no RC file required
448                    to specify where it lives.
449                 */
450
451                 mpath = Glib::build_filename (userconfigdir, "gdk-pixbuf.loaders");
452                 setenv ("GDK_PIXBUF_MODULE_FILE", mpath.c_str(), 1);
453         }
454 }
455
456 #endif
457
458 static gboolean
459 tell_about_jack_death (void* /* ignored */)
460 {
461         if (AudioEngine::instance()->processed_frames() == 0) {
462                 /* died during startup */
463                 MessageDialog msg (_("JACK exited"), false, Gtk::MESSAGE_INFO, Gtk::BUTTONS_OK);
464                 msg.set_position (Gtk::WIN_POS_CENTER);
465                 msg.set_secondary_text (string_compose (_(
466 "JACK exited unexpectedly, and without notifying %1.\n\
467 \n\
468 This could be due to misconfiguration or to an error inside JACK.\n\
469 \n\
470 Click OK to exit %1."), PROGRAM_NAME));
471     
472                 msg.run ();
473                 _exit (0);
474                 
475         } else {
476
477                 /* engine has already run, so this is a mid-session JACK death */
478                 
479                 MessageDialog* msg = manage (new MessageDialog (_("JACK exited"), false, Gtk::MESSAGE_INFO, Gtk::BUTTONS_NONE));
480                 msg->set_secondary_text (string_compose (_(
481 "JACK exited unexpectedly, and without notifying %1.\n\
482 \n\
483 This is probably due to an error inside JACK. You should restart JACK\n\
484 and reconnect %1 to it, or exit %1 now. You cannot save your\n\
485 session at this time, because we would lose your connection information.\n"), PROGRAM_NAME));
486                 msg->present ();
487         }
488         return false; /* do not call again */
489 }
490
491 static void
492 sigpipe_handler (int /*signal*/)
493 {
494         /* XXX fix this so that we do this again after a reconnect to JACK
495          */
496
497         static bool done_the_jack_thing = false;
498         
499         if (!done_the_jack_thing) {
500                 AudioEngine::instance()->died ();
501                 g_idle_add (tell_about_jack_death, 0);
502                 done_the_jack_thing =  true;
503         }
504 }
505
506 #ifdef HAVE_LV2
507 void close_external_ui_windows();
508 #endif
509
510 #ifdef VST_SUPPORT
511
512 extern int gui_init (int* argc, char** argv[]);
513
514 /* this is called from the entry point of a wine-compiled
515    executable that is linked against gtk2_ardour built
516    as a shared library.
517 */
518 extern "C" {
519 int ardour_main (int argc, char *argv[])
520 #else
521 int main (int argc, char *argv[])
522 #endif
523 {
524 #ifdef __APPLE__
525         fixup_bundle_environment ();
526 #endif
527
528         if (!Glib::thread_supported())
529                 Glib::thread_init();
530
531         gtk_set_locale ();
532
533 #ifdef VST_SUPPORT
534         /* this does some magic that is needed to make GTK and Wine's own
535            X11 client interact properly.
536         */
537         gui_init (&argc, &argv);
538 #endif
539
540         (void) bindtextdomain (PACKAGE, localedir);
541         /* our i18n translations are all in UTF-8, so make sure
542            that even if the user locale doesn't specify UTF-8,
543            we use that when handling them.
544         */
545         (void) bind_textdomain_codeset (PACKAGE,"UTF-8");
546         (void) textdomain (PACKAGE);
547
548         pthread_setcanceltype (PTHREAD_CANCEL_ASYNCHRONOUS, 0);
549
550         // catch error message system signals ();
551
552         text_receiver.listen_to (error);
553         text_receiver.listen_to (info);
554         text_receiver.listen_to (fatal);
555         text_receiver.listen_to (warning);
556
557         if (parse_opts (argc, argv)) {
558                 exit (1);
559         }
560
561         if (curvetest_file) {
562                 return curvetest (curvetest_file);
563         }
564
565         cout << PROGRAM_NAME
566              << VERSIONSTRING
567              << _(" (built using ")
568              << svn_revision
569 #ifdef __GNUC__
570              << _(" and GCC version ") << __VERSION__
571 #endif
572              << ')'
573              << endl;
574
575         if (just_version) {
576                 exit (0);
577         }
578
579         if (no_splash) {
580                 cerr << _("Copyright (C) 1999-2010 Paul Davis") << endl
581                      << _("Some portions Copyright (C) Steve Harris, Ari Johnson, Brett Viren, Joel Baker") << endl
582                      << endl
583                      << string_compose (_("%1 comes with ABSOLUTELY NO WARRANTY"), PROGRAM_NAME) << endl
584                      << _("not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.") << endl
585                      << _("This is free software, and you are welcome to redistribute it ") << endl
586                      << _("under certain conditions; see the source for copying conditions.")
587                      << endl;
588         }
589
590         /* some GUI objects need this */
591
592         PBD::ID::init ();
593
594         if (::signal (SIGPIPE, sigpipe_handler)) {
595                 cerr << _("Cannot xinstall SIGPIPE error handler") << endl;
596         }
597
598         try {
599                 ui = new ARDOUR_UI (&argc, &argv);
600         } catch (failed_constructor& err) {
601                 error << _("could not create ARDOUR GUI") << endmsg;
602                 exit (1);
603         }
604
605         ui->run (text_receiver);
606         Gtkmm2ext::Application::instance()->cleanup();
607         ui = 0;
608
609         ARDOUR::cleanup ();
610         pthread_cancel_all ();
611
612 #ifdef HAVE_LV2
613         close_external_ui_windows();
614 #endif
615         return 0;
616 }
617 #ifdef VST_SUPPORT
618 } // end of extern C block
619 #endif
620