fix 0096aa5 (at least for mingw)
[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/file_utils.h"
31 #include "pbd/textreceiver.h"
32 #include "pbd/failed_constructor.h"
33 #include "pbd/pathexpand.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 "ardour/revision.h"
40 #include "ardour/ardour.h"
41 #include "ardour/audioengine.h"
42 #include "ardour/session_utils.h"
43 #include "ardour/filesystem_paths.h"
44
45 #include <gtkmm/main.h>
46 #include <gtkmm2ext/application.h>
47 #include <gtkmm2ext/popup.h>
48 #include <gtkmm2ext/utils.h>
49
50 #include "ardour_ui.h"
51 #include "opts.h"
52 #include "enums.h"
53 #include "bundle_env.h"
54
55 #include "i18n.h"
56
57 #ifdef PLATFORM_WINDOWS
58 #include <fcntl.h> // Needed for '_fmode'
59 #include <shellapi.h> // console
60 #endif
61
62 #ifdef WAF_BUILD
63 #include "gtk2ardour-version.h"
64 #endif
65
66 using namespace std;
67 using namespace Gtk;
68 using namespace ARDOUR_COMMAND_LINE;
69 using namespace ARDOUR;
70 using namespace PBD;
71
72 TextReceiver text_receiver ("ardour");
73
74 extern int curvetest (string);
75
76 static ARDOUR_UI  *ui = 0;
77 static const char* localedir = LOCALEDIR;
78
79 void
80 gui_jack_error ()
81 {
82         MessageDialog win (string_compose (_("%1 could not connect to the audio backend."), PROGRAM_NAME),
83                            false,
84                            Gtk::MESSAGE_INFO,
85                            Gtk::BUTTONS_NONE);
86
87         win.add_button (Stock::QUIT, RESPONSE_CLOSE);
88         win.set_default_response (RESPONSE_CLOSE);
89
90         win.show_all ();
91         win.set_position (Gtk::WIN_POS_CENTER);
92
93         if (!no_splash) {
94                 ui->hide_splash ();
95         }
96
97         /* we just don't care about the result, but we want to block */
98
99         win.run ();
100 }
101
102 static gboolean
103 tell_about_backend_death (void* /* ignored */)
104 {
105         if (AudioEngine::instance()->processed_frames() == 0) {
106                 /* died during startup */
107                 MessageDialog msg (string_compose (_("The audio backend (%1) has failed, or terminated"), AudioEngine::instance()->current_backend_name()), false);
108                 msg.set_position (Gtk::WIN_POS_CENTER);
109                 msg.set_secondary_text (string_compose (_(
110 "%2 exited unexpectedly, and without notifying %1.\n\
111 \n\
112 This could be due to misconfiguration or to an error inside %2.\n\
113 \n\
114 Click OK to exit %1."), PROGRAM_NAME, AudioEngine::instance()->current_backend_name()));
115
116                 msg.run ();
117                 _exit (0);
118
119         } else {
120
121                 /* engine has already run, so this is a mid-session backend death */
122                         
123                 MessageDialog msg (string_compose (_("The audio backend (%1) has failed, or terminated"), AudioEngine::instance()->current_backend_name()), false);
124                 msg.set_secondary_text (string_compose (_("%2 exited unexpectedly, and without notifying %1."),
125                                                          PROGRAM_NAME, AudioEngine::instance()->current_backend_name()));
126                 msg.present ();
127         }
128         return false; /* do not call again */
129 }
130
131 #ifndef PLATFORM_WINDOWS
132 static void
133 sigpipe_handler (int /*signal*/)
134 {
135         /* XXX fix this so that we do this again after a reconnect to the backend
136          */
137
138         static bool done_the_backend_thing = false;
139
140         if (!done_the_backend_thing) {
141                 AudioEngine::instance()->died ();
142                 g_idle_add (tell_about_backend_death, 0);
143                 done_the_backend_thing =  true;
144         }
145 }
146 #endif
147
148 #if (!defined COMPILER_MSVC && defined PLATFORM_WINDOWS)
149
150 static FILE* pStdOut = 0;
151 static FILE* pStdErr = 0;
152 static BOOL  bConsole;
153 static HANDLE hStdOut;
154
155 static bool
156 IsAConsolePort (HANDLE handle)
157 {
158         DWORD mode;
159         return (GetConsoleMode(handle, &mode) != 0);
160 }
161 static void
162 console_madness_begin ()
163 {
164         bConsole = AttachConsole(ATTACH_PARENT_PROCESS);
165         hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
166
167         /* re-attach to the console so we can see 'printf()' output etc.
168          * for MSVC see  gtk2_ardour/msvc/winmain.cc
169          */
170
171         if ((bConsole) && (IsAConsolePort(hStdOut))) {
172                 pStdOut = freopen( "CONOUT$", "w", stdout );
173                 pStdErr = freopen( "CONOUT$", "w", stderr );
174         }
175 }
176
177 static void
178 console_madness_end ()
179 {
180         if (pStdOut) {
181                 fclose (pStdOut);
182         }
183         if (pStdErr) {
184                 fclose (pStdErr);
185         }
186
187         if (bConsole) {
188                 // Detach and free the console from our application
189                 INPUT_RECORD input_record;
190
191                 input_record.EventType = KEY_EVENT;
192                 input_record.Event.KeyEvent.bKeyDown = TRUE;
193                 input_record.Event.KeyEvent.dwControlKeyState = 0;
194                 input_record.Event.KeyEvent.uChar.UnicodeChar = VK_RETURN;
195                 input_record.Event.KeyEvent.wRepeatCount      = 1;
196                 input_record.Event.KeyEvent.wVirtualKeyCode   = VK_RETURN;
197                 input_record.Event.KeyEvent.wVirtualScanCode  = MapVirtualKey( VK_RETURN, 0 );
198
199                 DWORD written = 0;
200                 WriteConsoleInput( GetStdHandle( STD_INPUT_HANDLE ), &input_record, 1, &written );
201
202                 FreeConsole();
203         }
204 }
205
206 static void command_line_parse_error (int *argc, char** argv[]) {}
207
208 #elif (defined(COMPILER_MSVC) && defined(NDEBUG) && !defined(RDC_BUILD))
209
210 // these are not used here. for MSVC see  gtk2_ardour/msvc/winmain.cc
211 static void console_madness_begin () {}
212 static void console_madness_end () {}
213
214 static void command_line_parse_error (int *argc, char** argv[]) {
215         // Since we don't ordinarily have access to stdout and stderr with
216         // an MSVC app, let the user know we encountered a parsing error.
217         Gtk::Main app(&argc, &argv); // Calls 'gtk_init()'
218
219         Gtk::MessageDialog dlgReportParseError (_("\n   Ardour could not understand your command line      "),
220                                                       false, MESSAGE_ERROR, BUTTONS_CLOSE, true);
221         dlgReportParseError.set_title (_("An error was encountered while launching Ardour"));
222         dlgReportParseError.run ();
223 }
224
225 #else
226 static void console_madness_begin () {}
227 static void console_madness_end () {}
228 static void command_line_parse_error (int *argc, char** argv[]) {}
229 #endif
230
231 static bool
232 ask_about_configuration_copy (string const & old_dir, string const & new_dir, int version)
233 {
234         MessageDialog msg (string_compose (_("%1 %2.x has discovered configuration files from %1 %3.x.\n\n"
235                                              "Would you like to copy them before starting to use the program?\n\n"
236                                              "\n"
237                                              "(Note that not all files will be copied, since some are no longer relevant)"),
238                                            PROGRAM_NAME, PROGRAM_VERSION, version), true);
239
240         msg.add_button (Gtk::Stock::NO, Gtk::RESPONSE_NO);
241         msg.add_button (Gtk::Stock::YES, Gtk::RESPONSE_YES);
242         msg.show_all ();
243
244         return (msg.run() == Gtk::RESPONSE_YES);
245 }
246
247
248 #if (defined(COMPILER_MSVC) && defined(NDEBUG) && !defined(RDC_BUILD))
249 /*
250  *  Release build with MSVC uses ardour_main()
251  */
252 int ardour_main (int argc, char *argv[])
253
254 #elif (defined WINDOWS_VST_SUPPORT && !defined PLATFORM_WINDOWS)
255
256 // prototype for function in windows_vst_plugin_ui.cc
257 extern int windows_vst_gui_init (int* argc, char** argv[]);
258
259 /* this is called from the entry point of a wine-compiled
260    executable that is linked against gtk2_ardour built
261    as a shared library.
262 */
263 extern "C" {
264
265 int ardour_main (int argc, char *argv[])
266
267 #else
268 int main (int argc, char *argv[])
269 #endif
270 {
271         fixup_bundle_environment (argc, argv, &localedir);
272
273         load_custom_fonts(); /* needs to happen before any gtk and pango init calls */
274
275         if (!Glib::thread_supported()) {
276                 Glib::thread_init();
277         }
278
279 #ifdef ENABLE_NLS
280         gtk_set_locale ();
281 #endif
282
283         console_madness_begin();
284         
285 #if (defined WINDOWS_VST_SUPPORT && !defined PLATFORM_WINDOWS)
286         /* this does some magic that is needed to make GTK and X11 client interact properly.
287          * the platform dependent code is in windows_vst_plugin_ui.cc
288          */
289         windows_vst_gui_init (&argc, &argv);
290 #endif
291
292 #ifdef ENABLE_NLS
293         cerr << "bind txt domain [" << PACKAGE << "] to " << localedir << endl;
294
295         (void) bindtextdomain (PACKAGE, localedir);
296         /* our i18n translations are all in UTF-8, so make sure
297            that even if the user locale doesn't specify UTF-8,
298            we use that when handling them.
299         */
300         (void) bind_textdomain_codeset (PACKAGE,"UTF-8");
301 #endif
302
303         pthread_setcanceltype (PTHREAD_CANCEL_ASYNCHRONOUS, 0);
304
305         // catch error message system 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 #ifdef BOOST_SP_ENABLE_DEBUG_HOOKS
313         if (g_getenv ("BOOST_DEBUG")) {
314                 boost_debug_shared_ptr_show_live_debugging (true);
315         }
316 #endif
317
318         if (parse_opts (argc, argv)) {
319                 command_line_parse_error (&argc, &argv);
320                 exit (1);
321         }
322
323         cout << PROGRAM_NAME
324              << VERSIONSTRING
325              << _(" (built using ")
326              << revision
327 #ifdef __GNUC__
328              << _(" and GCC version ") << __VERSION__
329 #endif
330              << ')'
331              << endl;
332
333         if (just_version) {
334                 exit (0);
335         }
336
337         if (no_splash) {
338                 cerr << _("Copyright (C) 1999-2012 Paul Davis") << endl
339                      << _("Some portions Copyright (C) Steve Harris, Ari Johnson, Brett Viren, Joel Baker, Robin Gareus") << endl
340                      << endl
341                      << string_compose (_("%1 comes with ABSOLUTELY NO WARRANTY"), PROGRAM_NAME) << endl
342                      << _("not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.") << endl
343                      << _("This is free software, and you are welcome to redistribute it ") << endl
344                      << _("under certain conditions; see the source for copying conditions.")
345                      << endl;
346         }
347
348         if (!ARDOUR::init (ARDOUR_COMMAND_LINE::use_vst, ARDOUR_COMMAND_LINE::try_hw_optimization, localedir)) {
349                 error << string_compose (_("could not initialize %1."), PROGRAM_NAME) << endmsg;
350                 exit (1);
351         }
352
353         if (curvetest_file) {
354                 return curvetest (curvetest_file);
355         }
356
357 #ifndef PLATFORM_WINDOWS
358         if (::signal (SIGPIPE, sigpipe_handler)) {
359                 cerr << _("Cannot xinstall SIGPIPE error handler") << endl;
360         }
361 #endif
362
363         try {
364                 ui = new ARDOUR_UI (&argc, &argv, localedir);
365         } catch (failed_constructor& err) {
366                 error << string_compose (_("could not create %1 GUI"), PROGRAM_NAME) << endmsg;
367                 exit (1);
368         }
369
370         ui->run (text_receiver);
371         Gtkmm2ext::Application::instance()->cleanup();
372         delete ui;
373         ui = 0;
374
375         ARDOUR::cleanup ();
376         pthread_cancel_all ();
377
378         console_madness_end ();
379         
380         return 0;
381 }
382 #if (defined WINDOWS_VST_SUPPORT && !defined PLATFORM_WINDOWS)
383 } // end of extern "C" block
384 #endif