force UTF-8 handling of i18n files at run time
[ardour.git] / gtk2_ardour / main.cc
1 /*
2     Copyright (C) 2001-2006 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     $Id$
19 */
20
21 #include <sys/types.h>
22 #include <sys/mman.h>
23 #include <sys/wait.h>
24 #include <cerrno>
25 #include <cstdlib>
26 #include <signal.h>
27 #include <unistd.h>
28
29 #include <sigc++/bind.h>
30 #include <gtkmm/settings.h>
31
32 #include <pbd/error.h>
33 #include <pbd/textreceiver.h>
34 #include <pbd/failed_constructor.h>
35 #include <pbd/pthread_utils.h>
36
37 #include <jack/jack.h>
38
39 #include <ardour/version.h>
40 #include <ardour/ardour.h>
41 #include <ardour/audioengine.h>
42
43 #include <gtkmm/main.h>
44 #include <gtkmm2ext/popup.h>
45 #include <gtkmm2ext/utils.h>
46
47 #include "svn_revision.h"
48 #include "version.h"
49 #include "ardour_ui.h"
50 #include "opts.h"
51 #include "enums.h"
52
53 #include "i18n.h"
54
55 using namespace Gtk;
56 using namespace GTK_ARDOUR;
57 using namespace ARDOUR;
58 using namespace PBD;
59 using namespace sigc;
60
61 TextReceiver text_receiver ("ardour");
62
63 extern int curvetest (string);
64
65 static ARDOUR_UI  *ui = 0;
66
67 static void
68 shutdown (int status)
69 {
70         char* msg;
71
72         if (status) {
73
74                 msg = _("ardour is killing itself for a clean exit\n");
75                 write (1, msg, strlen (msg));
76                 /* drastic, but perhaps necessary */
77                 kill (-getpgrp(), SIGKILL);     
78                 /*NOTREACHED*/
79
80         } else {
81
82                 if (ui) {
83                         ui->kill();
84                 }
85                 
86                 pthread_cancel_all ();
87         }
88
89         exit (status);
90 }
91
92
93 static void 
94 handler (int sig)
95 {
96         char buf[64];
97         int n;
98
99         /* XXX its doubtful that snprintf() is async-safe */
100         n = snprintf (buf, sizeof(buf), _("%d(%d): received signal %d\n"), getpid(), (int) pthread_self(), sig);
101         write (1, buf, n);
102
103         shutdown (1);
104 }
105
106 static void *
107 signal_thread (void *arg)
108 {
109         int sig;
110         sigset_t blocked;
111
112         PBD::ThreadCreated (pthread_self(), X_("Signal"));
113
114         pthread_setcanceltype (PTHREAD_CANCEL_ASYNCHRONOUS, 0);
115         
116         /* find out what's blocked right now */
117
118         //sigprocmask (SIG_SETMASK, 0, &blocked);
119         if (pthread_sigmask (SIG_SETMASK, 0, &blocked)) {
120                 cerr << "getting blocked signals failed\n";
121         }
122         
123         /* wait for any of the currently blocked signals.
124            
125            According to the man page found in linux 2.6 and 2.4, sigwait() 
126            never returns an error. This is incorrect. Checking the man
127            pages for some other *nix systems makes it clear that
128            sigwait() can return several error codes, one of which 
129            is EINTR. This happens if the thread receives a signal
130            which is not in the blocked set. 
131
132            We do not expect that to happen, and if it did we should generally
133            exit as planned. However, under 2.6, the ptrace facility used 
134            by gdb seems to also cause sigwait() to return with EINTR
135            but with a signal that sigwait cannot understand. As a result, 
136            "sig" is set to zero, an impossible signal number.
137
138            Handling the EINTR code makes it possible to debug 
139            ardour on a 2.6 kernel.
140
141         */
142
143         int swerr;
144
145   again:
146         if ((swerr = sigwait (&blocked, &sig))) {
147                 if (swerr == EINTR) {
148                         goto again;
149                 } else {
150                         cerr << "sigwait failed with " << swerr << endl;
151                 }
152         }
153
154         cerr << "Signal " << sig << " received\n";
155
156         if (sig != SIGSEGV) {
157
158                 /* unblock signals so we can see them during shutdown.
159                    this will help prod developers not to lose sight
160                    of bugs that cause segfaults etc. during shutdown.
161                 */
162
163                 sigprocmask (SIG_UNBLOCK, &blocked, 0);
164         }
165
166         shutdown (1);
167         /*NOTREACHED*/
168         return 0;
169 }
170
171 int
172 catch_signals (void)
173 {
174         struct sigaction action;
175         pthread_t signal_thread_id;
176         sigset_t signals;
177
178 //      if (setpgid (0,0)) {
179         if (setsid ()) {
180                 warning << string_compose (_("cannot become new process group leader (%1)"), 
181                                     strerror (errno))
182                         << endmsg;
183         }
184
185         sigemptyset (&signals);
186         sigaddset(&signals, SIGHUP);
187         sigaddset(&signals, SIGINT);
188         sigaddset(&signals, SIGQUIT);
189         sigaddset(&signals, SIGPIPE);
190         sigaddset(&signals, SIGTERM);
191         sigaddset(&signals, SIGUSR1);
192         sigaddset(&signals, SIGUSR2);
193
194
195         /* install a handler because otherwise
196            pthreads behaviour is undefined when we enter
197            sigwait.
198         */
199         
200         action.sa_handler = handler;
201         action.sa_mask = signals;
202         action.sa_flags = SA_RESTART|SA_RESETHAND;
203
204         for (int i = 1; i < 32; i++) {
205                 if (sigismember (&signals, i)) {
206                         if (sigaction (i, &action, 0)) {
207                                 cerr << string_compose (_("cannot setup signal handling for %1"), i) << endl;
208                                 return -1;
209                         }
210                 }
211         } 
212
213         /* this sets the signal mask for this and all 
214            subsequent threads that do not reset it.
215         */
216         
217         if (pthread_sigmask (SIG_SETMASK, &signals, 0)) {
218                 cerr << string_compose (_("cannot set default signal mask (%1)"), strerror (errno)) << endl;
219                 return -1;
220         }
221
222         /* start a thread to wait for signals */
223
224         if (pthread_create_and_store ("signal", &signal_thread_id, 0, signal_thread, 0)) {
225                 cerr << "cannot create signal catching thread" << endl;
226                 return -1;
227         }
228
229         pthread_detach (signal_thread_id);
230         return 0;
231 }
232
233 string
234 which_ui_rcfile ()
235 {
236         string rcfile;
237         char* env;
238
239         if ((env = getenv ("ARDOUR2_UI_RC")) != 0 && strlen (env)) {
240                 rcfile = env;
241         } else {
242                 rcfile = "ardour2_ui.rc";
243         }
244
245         rcfile = find_config_file (rcfile);
246         
247         if (rcfile.empty()) {
248                 warning << _("Without a UI style file, ardour will look strange.\n Please set ARDOUR2_UI_RC to point to a valid UI style file") << endmsg;
249         } else {
250                 cerr << "Loading ui configuration file " << rcfile << endl;
251         }
252         
253         return rcfile;
254 }
255
256 gint
257 show_ui_callback (void *arg)
258 {
259         ARDOUR_UI * ui = (ARDOUR_UI *) arg;
260
261         ui->hide_splash();
262         
263         return FALSE;
264 }
265
266 void
267 gui_jack_error ()
268 {
269         MessageDialog win (_("Ardour could not connect to JACK."),
270                      false,
271                      Gtk::MESSAGE_INFO,
272                      (Gtk::ButtonsType)(Gtk::BUTTONS_NONE));
273 win.set_secondary_text(_("There are several possible reasons:\n\
274 \n\
275 1) JACK is not running.\n\
276 2) JACK is running as another user, perhaps root.\n\
277 3) There is already another client called \"ardour\".\n\
278 \n\
279 Please consider the possibilities, and perhaps (re)start JACK."));
280
281         win.add_button (Stock::QUIT, RESPONSE_CLOSE);
282         win.set_default_response (RESPONSE_CLOSE);
283         
284         win.show_all ();
285         win.set_position (Gtk::WIN_POS_CENTER);
286
287         if (!no_splash) {
288                 ui->hide_splash ();
289         }
290
291         /* we just don't care about the result, but we want to block */
292
293         win.run ();
294 }
295
296 static bool
297 maybe_load_session ()
298 {
299         /* If no session name is given: we're not loading a session yet, nor creating a new one */
300         if (!session_name.length()) {
301                 ui->hide_splash ();
302                 if (!Config->get_no_new_session_dialog()) {
303                        ui->new_session ();
304                 }
305
306                 return true;
307         }
308
309         /* Load session or start the new session dialog */
310         string name, path;
311
312         bool isnew;
313
314         if (Session::find_session (session_name, path, name, isnew)) {
315                 error << string_compose(_("could not load command line session \"%1\""), session_name) << endmsg;
316                 return false;
317         }
318
319         if (!new_session) {
320                         
321                 /* Loading a session, but the session doesn't exist */
322                 if (isnew) {
323                         error << string_compose (_("\n\nNo session named \"%1\" exists.\n"
324                                                    "To create it from the command line, start ardour as \"ardour --new %1"), path) 
325                               << endmsg;
326                         return false;
327                 }
328
329                 if (ui->load_session (path, name)) {
330                         /* it failed */
331                         return false;
332                 }
333
334         } else {
335
336                 /*  TODO: This bit of code doesn't work properly yet
337                     Glib::signal_idle().connect (bind (mem_fun (*ui, &ARDOUR_UI::cmdline_new_session), path));
338                     ui->set_will_create_new_session_automatically (true); 
339                 */
340                 
341                 /* Show the NSD */
342                 ui->hide_splash ();
343                 if (!Config->get_no_new_session_dialog()) {
344                        ui->new_session ();
345                 }
346         }
347
348         return true;
349 }
350
351 #ifdef VST_SUPPORT
352 /* this is called from the entry point of a wine-compiled
353    executable that is linked against gtk2_ardour built
354    as a shared library.
355 */
356 extern "C" {
357 int ardour_main (int argc, char *argv[])
358 #else
359 int main (int argc, char *argv[])
360 #endif
361
362 {
363         ARDOUR::AudioEngine *engine;
364         vector<Glib::ustring> null_file_list;
365
366         Glib::thread_init();
367         gtk_set_locale ();
368
369         (void) bindtextdomain (PACKAGE, LOCALEDIR);
370         /* our i18n translations are all in UTF-8, so make sure
371            that even if the user locale doesn't specify UTF-8,
372            we use that when handling them.
373         */
374         (void) bind_textdomain_codeset (PACKAGE,"UTF-8");
375         (void) textdomain (PACKAGE);
376
377         pthread_setcanceltype (PTHREAD_CANCEL_ASYNCHRONOUS, 0);
378
379         // catch error message system signals ();
380
381         text_receiver.listen_to (error);
382         text_receiver.listen_to (info);
383         text_receiver.listen_to (fatal);
384         text_receiver.listen_to (warning);
385
386         if (parse_opts (argc, argv)) {
387                 exit (1);
388         }
389
390         if (curvetest_file) {
391                 return curvetest (curvetest_file);
392         }
393         
394         cout << _("Ardour/GTK ") 
395              << VERSIONSTRING
396              << _("\n   (built using ")
397              << ardour_svn_revision
398 #ifdef __GNUC__
399              << _(" and GCC version ") << __VERSION__ 
400 #endif
401              << ')'
402              << endl;
403         
404         if (just_version) {
405                 exit (0);
406         }
407
408         if (no_splash) {
409                 cerr << _("Copyright (C) 1999-2006 Paul Davis") << endl
410                      << _("Some portions Copyright (C) Steve Harris, Ari Johnson, Brett Viren, Joel Baker") << endl
411                      << endl
412                      << _("Ardour comes with ABSOLUTELY NO WARRANTY") << endl
413                      << _("not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.") << endl
414                      << _("This is free software, and you are welcome to redistribute it ") << endl
415                      << _("under certain conditions; see the source for copying conditions.")
416                      << endl;
417         }
418
419         try { 
420                 ui = new ARDOUR_UI (&argc, &argv, which_ui_rcfile());
421         } catch (failed_constructor& err) {
422                 error << _("could not create ARDOUR GUI") << endmsg;
423                 exit (1);
424         }
425
426         if (!keybindings_path.empty()) {
427                 ui->set_keybindings_path (keybindings_path);
428         }
429
430         if (!no_splash) {
431                 ui->show_splash ();
432                 if (session_name.length()) {  
433                         g_timeout_add (4000, show_ui_callback, ui);
434                 }
435         }
436
437         try {
438                 ARDOUR::init (use_vst, try_hw_optimization);
439                 setup_gtk_ardour_enums ();
440                 Config->set_current_owner (ConfigVariableBase::Interface);
441
442                 try { 
443                         engine = new ARDOUR::AudioEngine (jack_client_name);
444                 } catch (AudioEngine::NoBackendAvailable& err) {
445                         gui_jack_error ();
446                         error << string_compose (_("Could not connect to JACK server as  \"%1\""), jack_client_name) <<  endmsg;
447                         return -1;
448                 }
449                 
450                 ui->set_engine (*engine);
451
452         } catch (failed_constructor& err) {
453                 error << _("could not initialize Ardour.") << endmsg;
454                 return -1;
455         } 
456
457         ui->start_engine ();
458
459         if (maybe_load_session ()) {
460                 ui->run (text_receiver);
461                 ui = 0;
462         }
463
464         delete engine;
465         ARDOUR::cleanup ();
466         shutdown (0);
467
468         return 0;
469 }
470 #ifdef VST_SUPPORT
471 } // end of extern C block
472 #endif
473