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