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