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