Moved strip_whitespace_edges() to pbd/whitespace.h
[ardour.git] / gtk2_ardour / main.cc
1 /*
2     Copyright (C) 2001-2004 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 "version.h"
48 #include "ardour_ui.h"
49 #include "opts.h"
50
51 #include "i18n.h"
52
53 using namespace Gtk;
54 using namespace GTK_ARDOUR;
55 using namespace ARDOUR;
56 using namespace sigc;
57
58 TextReceiver text_receiver ("ardour");
59
60 extern int curvetest (string);
61
62 static ARDOUR_UI  *ui = 0;
63
64 static void
65 shutdown (int status)
66 {
67         char* msg;
68
69         if (status) {
70
71                 msg = _("ardour is killing itself for a clean exit\n");
72                 write (1, msg, strlen (msg));
73                 /* drastic, but perhaps necessary */
74                 kill (-getpgrp(), SIGKILL);     
75                 /*NOTREACHED*/
76
77         } else {
78
79                 if (ui) {
80                         msg = _("stopping user interface\n");
81                         write (1, msg, strlen (msg));
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 handler2 (int sig, siginfo_t* ctxt, void* ignored)
107 {
108         handler (sig);
109 }       
110
111 static void *
112 signal_thread (void *arg)
113 {
114         int sig;
115         sigset_t blocked;
116
117         PBD::ThreadCreated (pthread_self(), X_("Signal"));
118
119         pthread_setcanceltype (PTHREAD_CANCEL_ASYNCHRONOUS, 0);
120         
121         /* find out what's blocked right now */
122
123         //sigprocmask (SIG_SETMASK, 0, &blocked);
124         if (pthread_sigmask (SIG_SETMASK, 0, &blocked)) {
125                 cerr << "getting blocked signals failed\n";
126         }
127         
128         /* wait for any of the currently blocked signals.
129            
130            According to the man page found in linux 2.6 and 2.4, sigwait() 
131            never returns an error. This is incorrect. Checking the man
132            pages for some other *nix systems makes it clear that
133            sigwait() can return several error codes, one of which 
134            is EINTR. This happens if the thread receives a signal
135            which is not in the blocked set. 
136
137            We do not expect that to happen, and if it did we should generally
138            exit as planned. However, under 2.6, the ptrace facility used 
139            by gdb seems to also cause sigwait() to return with EINTR
140            but with a signal that sigwait cannot understand. As a result, 
141            "sig" is set to zero, an impossible signal number.
142
143            Handling the EINTR code makes it possible to debug 
144            ardour on a 2.6 kernel.
145
146         */
147
148         int swerr;
149
150   again:
151         if ((swerr = sigwait (&blocked, &sig))) {
152                 if (swerr == EINTR) {
153                         goto again;
154                 } else {
155                         cerr << "sigwait failed with " << swerr << endl;
156                 }
157         }
158
159         cerr << "Signal " << sig << " received\n";
160
161         if (sig != SIGSEGV) {
162
163                 /* unblock signals so we can see them during shutdown.
164                    this will help prod developers not to lose sight
165                    of bugs that cause segfaults etc. during shutdown.
166                 */
167
168                 sigprocmask (SIG_UNBLOCK, &blocked, 0);
169         }
170
171         shutdown (1);
172         /*NOTREACHED*/
173         return 0;
174 }
175
176 int
177 catch_signals (void)
178 {
179         struct sigaction action;
180         pthread_t signal_thread_id;
181         sigset_t signals;
182
183 //      if (setpgid (0,0)) {
184         if (setsid ()) {
185                 warning << string_compose (_("cannot become new process group leader (%1)"), 
186                                     strerror (errno))
187                         << endmsg;
188         }
189
190         sigemptyset (&signals);
191         sigaddset(&signals, SIGHUP);
192         sigaddset(&signals, SIGINT);
193         sigaddset(&signals, SIGQUIT);
194         sigaddset(&signals, SIGPIPE);
195         sigaddset(&signals, SIGTERM);
196         sigaddset(&signals, SIGUSR1);
197         sigaddset(&signals, SIGUSR2);
198
199
200         /* install a handler because otherwise
201            pthreads behaviour is undefined when we enter
202            sigwait.
203         */
204         
205         action.sa_handler = handler;
206         action.sa_mask = signals;
207         action.sa_flags = SA_RESTART|SA_RESETHAND;
208
209         for (int i = 1; i < 32; i++) {
210                 if (sigismember (&signals, i)) {
211                         if (sigaction (i, &action, 0)) {
212                                 cerr << string_compose (_("cannot setup signal handling for %1"), i) << endl;
213                                 return -1;
214                         }
215                 }
216         } 
217
218         /* this sets the signal mask for this and all 
219            subsequent threads that do not reset it.
220         */
221         
222         if (pthread_sigmask (SIG_SETMASK, &signals, 0)) {
223                 cerr << string_compose (_("cannot set default signal mask (%1)"), strerror (errno)) << endl;
224                 return -1;
225         }
226
227         /* start a thread to wait for signals */
228
229         if (pthread_create_and_store ("signal", &signal_thread_id, 0, signal_thread, 0)) {
230                 cerr << "cannot create signal catching thread" << endl;
231                 return -1;
232         }
233
234         pthread_detach (signal_thread_id);
235         return 0;
236 }
237
238 string
239 which_ui_rcfile ()
240 {
241         string rcfile;
242         char* envvar;
243
244         if ((envvar = getenv("ARDOUR_UI_RC")) == 0) {
245                 rcfile = find_config_file ("ardour2_ui.rc");
246         
247                 if (rcfile.length() == 0) {
248                         warning << _("Without a UI style file, ardour will look strange.\n Please set ARDOUR_UI_RC to point to a valid UI style file") << endmsg;
249                 }
250         } else {
251                 rcfile = envvar;
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         ui->show ();
264         
265         return FALSE;
266 }
267
268 void
269 gui_jack_error ()
270 {
271         ArdourDialog win (_("ardour: unplugged"));
272         Label label (_("Ardour could not connect to JACK.\n\
273 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.get_vbox()->pack_start (label);
282         win.add_button (Stock::OK, RESPONSE_ACCEPT);
283         
284         win.show_all ();
285         win.set_position (Gtk::WIN_POS_CENTER);
286
287         /* we just don't care about the result */
288
289         win.run ();
290 }
291
292 int
293 main (int argc, char *argv[])
294 {
295         ARDOUR::AudioEngine *engine;
296         vector<Glib::ustring> null_file_list;
297
298         gtk_set_locale ();
299
300         (void)   bindtextdomain (PACKAGE, LOCALEDIR);
301         (void) textdomain (PACKAGE);
302
303         pthread_setcanceltype (PTHREAD_CANCEL_ASYNCHRONOUS, 0);
304
305         // catch_signals ();
306
307         text_receiver.listen_to (error);
308         text_receiver.listen_to (info);
309         text_receiver.listen_to (fatal);
310         text_receiver.listen_to (warning);
311
312         if (parse_opts (argc, argv)) {
313                 exit (1);
314         }
315
316         if (curvetest_file) {
317                 return curvetest (curvetest_file);
318         }
319
320         /* desktop standard themes: just say no! */
321
322         if (getenv("GTK_RC_FILES")) {
323                 unsetenv("GTK_RC_FILES");
324         }
325
326         if (getenv("GTK2_RC_FILES")) {
327                 unsetenv("GTK_RC_FILES");
328         }
329
330         RC::set_default_files (null_file_list);
331         
332         cout << _("Ardour/GTK ") 
333              << VERSIONSTRING
334              << _("\n   (built using ")
335              << gtk_ardour_major_version << '.'
336              << gtk_ardour_minor_version << '.'
337              << gtk_ardour_micro_version
338              << _(" with libardour ")
339              << libardour_major_version << '.'
340              << libardour_minor_version << '.' 
341              << libardour_micro_version 
342 #ifdef __GNUC__
343              << _(" and GCC version ") << __VERSION__ 
344 #endif
345              << ')'
346              << endl;
347         
348         if (just_version) {
349                 exit (0);
350         }
351
352         if (no_splash) {
353                 cerr << _("Copyright (C) 1999-2005 Paul Davis") << endl
354                      << _("Some portions Copyright (C) Steve Harris, Ari Johnson, Brett Viren, Joel Baker") << endl
355                      << endl
356                      << _("Ardour comes with ABSOLUTELY NO WARRANTY") << endl
357                      << _("not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.") << endl
358                      << _("This is free software, and you are welcome to redistribute it ") << endl
359                      << _("under certain conditions; see the source for copying conditions.")
360                      << endl;
361         }
362
363         try { 
364                 ui = new ARDOUR_UI (&argc, &argv, which_ui_rcfile());
365         } 
366
367         catch (failed_constructor& err) {
368                 error << _("could not create ARDOUR GUI") << endmsg;
369                 exit (1);
370         }
371
372
373         if (!no_splash) {
374                 ui->show_splash ();
375                 if (session_name.length()) {  
376                         gtk_timeout_add (4000, show_ui_callback, ui);
377                 }
378         }
379         
380         try { 
381                 engine = new ARDOUR::AudioEngine (jack_client_name);
382                 ARDOUR::init (*engine, use_vst, try_hw_optimization, handler2);
383                 ui->set_engine (*engine);
384         } catch (AudioEngine::NoBackendAvailable& err) {
385                 gui_jack_error ();
386                 error << string_compose (_("Could not connect to JACK server as  \"%1\""), jack_client_name) <<  endmsg;
387                 return -1;
388         } catch (failed_constructor& err) {
389                 error << _("could not initialize Ardour.") << endmsg;
390                 return -1;
391         } 
392
393         /* load session, if given */
394         string name, path;
395
396         if (session_name.length()){
397                 bool isnew;
398
399                 if (Session::find_session (session_name, path, name, isnew)) {
400                         error << string_compose(_("could not load command line session \"%1\""), session_name) << endmsg;
401                 } else {
402
403                         if (new_session) {
404
405                                 /* command line required that the session be new */
406
407                                 if (isnew) {
408                                         
409                                         /* popup the new session dialog
410                                            once everything else is OK.
411                                         */
412
413                                         Glib::signal_idle().connect (bind (mem_fun (*ui, &ARDOUR_UI::cmdline_new_session), path));
414                                         ui->set_will_create_new_session_automatically (true);
415
416                                 } else {
417
418                                         /* it wasn't new, but we require a new session */
419
420                                         error << string_compose (_("\n\nA session named \"%1\" already exists.\n\
421 To avoid this message, start ardour as \"ardour %1"), path)
422                                               << endmsg;
423                                         goto out;
424                                 }
425
426                         } else {
427
428                                 /* command line didn't require a new session */
429                                 
430                                 if (isnew) {
431                                         error << string_compose (_("\n\nNo session named \"%1\" exists.\n\
432 To create it from the command line, start ardour as \"ardour --new %1"), path) 
433                                               << endmsg;
434                                         goto out;
435                                 }
436
437                                 ui->load_session (path, name);
438                         }
439                 }
440
441                 if (no_splash) {
442                         ui->show();
443                 }
444
445         } else {
446                 ui->hide_splash ();
447                 ui->show ();
448                 if (!Config->get_no_new_session_dialog()) {
449                         ui->new_session (true);
450                 }
451         }
452
453         ui->run (text_receiver);
454         ui = 0;
455
456   out:
457         delete engine;
458         ARDOUR::cleanup ();
459         shutdown (0);
460 }
461