try to clean MSVC/Windows mess in main(), hopefully without breaking anything
[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 #if defined(NDEBUG) && !defined(RDC_BUILD))
207 // Since we don't ordinarily have access to stdout and stderr with
208 // an MSVC app, let the user know we encountered a parsing error.
209 static void
210 command_line_parse_error (int* argc, char** argv[])
211 {
212         Gtk::Main app(&argc, &argv); // Calls 'gtk_init()'
213         
214         Gtk::MessageDialog msg (_("\n   Ardour could not understand your command line      "),
215                                                 false, MESSAGE_ERROR, BUTTONS_CLOSE, true);
216         msg.set_title (_("An error was encountered while launching Ardour"));
217         msg.run ();
218 }
219 #endif
220
221 #else
222 static void console_madness_begin () {}
223 static void console_madness_end () {}
224 static void command_line_parse_error (int *argc, char** argv[]) {}
225 #endif
226
227 static bool
228 ask_about_configuration_copy (string const & old_dir, string const & new_dir, int version)
229 {
230         MessageDialog msg (string_compose (_("%1 %2.x has discovered configuration files from %1 %3.x.\n\n"
231                                              "Would you like to copy them before starting to use the program?\n\n"
232                                              "\n"
233                                              "(Note that not all files will be copied, since some are no longer relevant)"),
234                                            PROGRAM_NAME, PROGRAM_VERSION, version), true);
235
236         msg.add_button (Gtk::Stock::NO, Gtk::RESPONSE_NO);
237         msg.add_button (Gtk::Stock::YES, Gtk::RESPONSE_YES);
238         msg.show_all ();
239
240         return (msg.run() == Gtk::RESPONSE_YES);
241 }
242
243
244 #if (defined(COMPILER_MSVC) && defined(NDEBUG) && !defined(RDC_BUILD))
245 /*
246  *  Release build with MSVC uses ardour_main()
247  */
248 int ardour_main (int argc, char *argv[])
249
250 #elif (defined WINDOWS_VST_SUPPORT && !defined PLATFORM_WINDOWS)
251
252 // prototype for function in windows_vst_plugin_ui.cc
253 extern int windows_vst_gui_init (int* argc, char** argv[]);
254
255 /* this is called from the entry point of a wine-compiled
256    executable that is linked against gtk2_ardour built
257    as a shared library.
258 */
259 extern "C" {
260
261 int ardour_main (int argc, char *argv[])
262
263 #else
264 int main (int argc, char *argv[])
265 #endif
266 {
267         fixup_bundle_environment (argc, argv, &localedir);
268
269         load_custom_fonts(); /* needs to happen before any gtk and pango init calls */
270
271         if (!Glib::thread_supported()) {
272                 Glib::thread_init();
273         }
274
275 #ifdef ENABLE_NLS
276         gtk_set_locale ();
277 #endif
278
279         console_madness_begin();
280         
281 #if (defined WINDOWS_VST_SUPPORT && !defined PLATFORM_WINDOWS)
282         /* this does some magic that is needed to make GTK and X11 client interact properly.
283          * the platform dependent code is in windows_vst_plugin_ui.cc
284          */
285         windows_vst_gui_init (&argc, &argv);
286 #endif
287
288 #ifdef ENABLE_NLS
289         cerr << "bind txt domain [" << PACKAGE << "] to " << localedir << endl;
290
291         (void) bindtextdomain (PACKAGE, localedir);
292         /* our i18n translations are all in UTF-8, so make sure
293            that even if the user locale doesn't specify UTF-8,
294            we use that when handling them.
295         */
296         (void) bind_textdomain_codeset (PACKAGE,"UTF-8");
297 #endif
298
299         pthread_setcanceltype (PTHREAD_CANCEL_ASYNCHRONOUS, 0);
300
301         // catch error message system signals ();
302
303         text_receiver.listen_to (error);
304         text_receiver.listen_to (info);
305         text_receiver.listen_to (fatal);
306         text_receiver.listen_to (warning);
307
308 #ifdef BOOST_SP_ENABLE_DEBUG_HOOKS
309         if (g_getenv ("BOOST_DEBUG")) {
310                 boost_debug_shared_ptr_show_live_debugging (true);
311         }
312 #endif
313
314         if (parse_opts (argc, argv)) {
315                 command_line_parse_error (&argc, &argv);
316                 exit (1);
317         }
318
319         cout << PROGRAM_NAME
320              << VERSIONSTRING
321              << _(" (built using ")
322              << revision
323 #ifdef __GNUC__
324              << _(" and GCC version ") << __VERSION__
325 #endif
326              << ')'
327              << endl;
328
329         if (just_version) {
330                 exit (0);
331         }
332
333         if (no_splash) {
334                 cerr << _("Copyright (C) 1999-2012 Paul Davis") << endl
335                      << _("Some portions Copyright (C) Steve Harris, Ari Johnson, Brett Viren, Joel Baker, Robin Gareus") << endl
336                      << endl
337                      << string_compose (_("%1 comes with ABSOLUTELY NO WARRANTY"), PROGRAM_NAME) << endl
338                      << _("not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.") << endl
339                      << _("This is free software, and you are welcome to redistribute it ") << endl
340                      << _("under certain conditions; see the source for copying conditions.")
341                      << endl;
342         }
343
344         if (!ARDOUR::init (ARDOUR_COMMAND_LINE::use_vst, ARDOUR_COMMAND_LINE::try_hw_optimization, localedir)) {
345                 error << string_compose (_("could not initialize %1."), PROGRAM_NAME) << endmsg;
346                 exit (1);
347         }
348
349         if (curvetest_file) {
350                 return curvetest (curvetest_file);
351         }
352
353 #ifndef PLATFORM_WINDOWS
354         if (::signal (SIGPIPE, sigpipe_handler)) {
355                 cerr << _("Cannot xinstall SIGPIPE error handler") << endl;
356         }
357 #endif
358
359         try {
360                 ui = new ARDOUR_UI (&argc, &argv, localedir);
361         } catch (failed_constructor& err) {
362                 error << string_compose (_("could not create %1 GUI"), PROGRAM_NAME) << endmsg;
363                 exit (1);
364         }
365
366         ui->run (text_receiver);
367         Gtkmm2ext::Application::instance()->cleanup();
368         delete ui;
369         ui = 0;
370
371         ARDOUR::cleanup ();
372         pthread_cancel_all ();
373
374         console_madness_end ();
375         
376         return 0;
377 }
378 #if (defined WINDOWS_VST_SUPPORT && !defined PLATFORM_WINDOWS)
379 } // end of extern "C" block
380 #endif