Enable Menu > Quit to work again after startup on macOS
[ardour.git] / gtk2_ardour / ardour_ui_startup.cc
1 /*
2  * Copyright (C) 2005-2007 Doug McLain <doug@nostar.net>
3  * Copyright (C) 2005-2017 Tim Mayberry <mojofunk@gmail.com>
4  * Copyright (C) 2005-2019 Paul Davis <paul@linuxaudiosystems.com>
5  * Copyright (C) 2005 Karsten Wiese <fzuuzf@googlemail.com>
6  * Copyright (C) 2005 Taybin Rutkin <taybin@taybin.com>
7  * Copyright (C) 2006-2015 David Robillard <d@drobilla.net>
8  * Copyright (C) 2007-2012 Carl Hetherington <carl@carlh.net>
9  * Copyright (C) 2008-2010 Sakari Bergen <sakari.bergen@beatwaves.net>
10  * Copyright (C) 2012-2019 Robin Gareus <robin@gareus.org>
11  * Copyright (C) 2013-2015 Colin Fletcher <colin.m.fletcher@googlemail.com>
12  * Copyright (C) 2013-2016 John Emmas <john@creativepost.co.uk>
13  * Copyright (C) 2013-2016 Nick Mainsbridge <mainsbridge@gmail.com>
14  * Copyright (C) 2014-2018 Ben Loftis <ben@harrisonconsoles.com>
15  * Copyright (C) 2015 AndrĂ© Nusser <andre.nusser@googlemail.com>
16  * Copyright (C) 2016-2018 Len Ovens <len@ovenwerks.net>
17  * Copyright (C) 2017 Johannes Mueller <github@johannes-mueller.org>
18  *
19  * This program is free software; you can redistribute it and/or modify
20  * it under the terms of the GNU General Public License as published by
21  * the Free Software Foundation; either version 2 of the License, or
22  * (at your option) any later version.
23  *
24  * This program is distributed in the hope that it will be useful,
25  * but WITHOUT ANY WARRANTY; without even the implied warranty of
26  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
27  * GNU General Public License for more details.
28  *
29  * You should have received a copy of the GNU General Public License along
30  * with this program; if not, write to the Free Software Foundation, Inc.,
31  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
32  */
33
34 #ifdef WAF_BUILD
35 #include "gtk2ardour-config.h"
36 #include "gtk2ardour-version.h"
37 #endif
38
39 #ifndef PLATFORM_WINDOWS
40 #include <sys/resource.h>
41 #endif
42
43 #ifdef __FreeBSD__
44 #include <sys/types.h>
45 #include <sys/sysctl.h>
46 #endif
47
48 #include <glib.h>
49 #include "pbd/gstdio_compat.h"
50
51 #include <gtkmm/stock.h>
52
53 #include "pbd/basename.h"
54 #include "pbd/file_utils.h"
55
56 #include "ardour/audioengine.h"
57 #include "ardour/filename_extensions.h"
58 #include "ardour/filesystem_paths.h"
59 #include "ardour/profile.h"
60
61 #include "gtkmm2ext/application.h"
62
63 #include "ambiguous_file_dialog.h"
64 #include "ardour_ui.h"
65 #include "debug.h"
66 #include "engine_dialog.h"
67 #include "keyboard.h"
68 #include "missing_file_dialog.h"
69 #include "nsm.h"
70 #include "opts.h"
71 #include "pingback.h"
72 #include "public_editor.h"
73 #include "splash.h"
74
75 #include "pbd/i18n.h"
76
77 using namespace ARDOUR;
78 using namespace PBD;
79 using namespace Gtk;
80 using namespace Gtkmm2ext;
81 using namespace std;
82
83
84 static bool
85 _hide_splash (gpointer arg)
86 {
87         ((ARDOUR_UI*)arg)->hide_splash();
88         return false;
89 }
90
91 bool
92 ARDOUR_UI::first_idle ()
93 {
94         if (_session) {
95                 _session->allow_auto_play (true);
96         }
97
98         if (editor) {
99                 editor->first_idle();
100         }
101
102         /* in 1 second, hide the splash screen
103          *
104          * Consider hiding it *now*. If a user opens opens a dialog
105          * during that one second while the splash is still visible,
106          * the dialog will push-back the splash.
107          * Closing the dialog later will pop it back.
108          */
109         Glib::signal_timeout().connect (sigc::bind (sigc::ptr_fun (_hide_splash), this), 1000);
110
111         Keyboard::set_can_save_keybindings (true);
112         return false;
113 }
114
115 void
116 ARDOUR_UI::setup_profile ()
117 {
118         if (gdk_screen_width() < 1200 || getenv ("ARDOUR_NARROW_SCREEN")) {
119                 Profile->set_small_screen ();
120         }
121
122         if (g_getenv ("MIXBUS")) {
123                 Profile->set_mixbus ();
124         }
125 }
126
127 int
128 ARDOUR_UI::missing_file (Session*s, std::string str, DataType type)
129 {
130         MissingFileDialog dialog (s, str, type);
131
132         dialog.show ();
133         dialog.present ();
134
135         int result = dialog.run ();
136         dialog.hide ();
137
138         switch (result) {
139         case RESPONSE_OK:
140                 break;
141         default:
142                 return 1; // quit entire session load
143         }
144
145         result = dialog.get_action ();
146
147         return result;
148 }
149
150 int
151 ARDOUR_UI::ambiguous_file (std::string file, std::vector<std::string> hits)
152 {
153         AmbiguousFileDialog dialog (file, hits);
154
155         dialog.show ();
156         dialog.present ();
157
158         dialog.run ();
159
160         return dialog.get_which ();
161 }
162
163 void
164 ARDOUR_UI::session_format_mismatch (std::string xml_path, std::string backup_path)
165 {
166         const char* start_big = "<span size=\"x-large\" weight=\"bold\">";
167         const char* end_big = "</span>";
168         const char* start_mono = "<tt>";
169         const char* end_mono = "</tt>";
170
171         MessageDialog msg (string_compose (_("%4This is a session from an older version of %3%5\n\n"
172                                              "%3 has copied the old session file\n\n%6%1%7\n\nto\n\n%6%2%7\n\n"
173                                              "From now on, use the backup copy with older versions of %3"),
174                                            xml_path, backup_path, PROGRAM_NAME,
175                                            start_big, end_big,
176                                            start_mono, end_mono), true);
177
178         msg.run ();
179 }
180
181
182 int
183 ARDOUR_UI::sr_mismatch_dialog (samplecnt_t desired, samplecnt_t actual)
184 {
185         HBox* hbox = new HBox();
186         Image* image = new Image (Stock::DIALOG_WARNING, ICON_SIZE_DIALOG);
187         ArdourDialog dialog (_("Sample Rate Mismatch"), true);
188         Label  message (string_compose (_("\
189 This session was created with a sample rate of %1 Hz, but\n\
190 %2 is currently running at %3 Hz.  If you load this session,\n\
191 audio may be played at the wrong sample rate.\n"), desired, PROGRAM_NAME, actual));
192
193         image->set_alignment(ALIGN_CENTER, ALIGN_TOP);
194         hbox->pack_start (*image, PACK_EXPAND_WIDGET, 12);
195         hbox->pack_end (message, PACK_EXPAND_PADDING, 12);
196         dialog.get_vbox()->pack_start(*hbox, PACK_EXPAND_PADDING, 6);
197         dialog.add_button (_("Do not load session"), RESPONSE_REJECT);
198         dialog.add_button (_("Load session anyway"), RESPONSE_ACCEPT);
199         dialog.set_default_response (RESPONSE_ACCEPT);
200         dialog.set_position (WIN_POS_CENTER);
201         message.show();
202         image->show();
203         hbox->show();
204
205         switch (dialog.run()) {
206         case RESPONSE_ACCEPT:
207                 return 0;
208         default:
209                 break;
210         }
211
212         return 1;
213 }
214
215 void
216 ARDOUR_UI::sr_mismatch_message (samplecnt_t desired, samplecnt_t actual)
217 {
218         MessageDialog msg (string_compose (_("\
219 This session was created with a sample rate of %1 Hz, but\n\
220 %2 is currently running at %3 Hz.\n\
221 Audio will be recorded and played at the wrong sample rate.\n\
222 Re-Configure the Audio Engine in\n\
223 Menu > Window > Audio/Midi Setup"),
224                                 desired, PROGRAM_NAME, actual),
225                         true,
226                         Gtk::MESSAGE_WARNING);
227         msg.run ();
228 }
229
230
231 XMLNode*
232 ARDOUR_UI::preferences_settings () const
233 {
234         XMLNode* node = 0;
235
236         if (_session) {
237                 node = _session->instant_xml(X_("Preferences"));
238         } else {
239                 node = Config->instant_xml(X_("Preferences"));
240         }
241
242         if (!node) {
243                 node = new XMLNode (X_("Preferences"));
244         }
245
246         return node;
247 }
248
249 XMLNode*
250 ARDOUR_UI::mixer_settings () const
251 {
252         XMLNode* node = 0;
253
254         if (_session) {
255                 node = _session->instant_xml(X_("Mixer"));
256         } else {
257                 node = Config->instant_xml(X_("Mixer"));
258         }
259
260         if (!node) {
261                 node = new XMLNode (X_("Mixer"));
262         }
263
264         return node;
265 }
266
267 XMLNode*
268 ARDOUR_UI::main_window_settings () const
269 {
270         XMLNode* node = 0;
271
272         if (_session) {
273                 node = _session->instant_xml(X_("Main"));
274         } else {
275                 node = Config->instant_xml(X_("Main"));
276         }
277
278         if (!node) {
279                 if (getenv("ARDOUR_INSTANT_XML_PATH")) {
280                         node = Config->instant_xml(getenv("ARDOUR_INSTANT_XML_PATH"));
281                 }
282         }
283
284         if (!node) {
285                 node = new XMLNode (X_("Main"));
286         }
287
288         return node;
289 }
290
291 XMLNode*
292 ARDOUR_UI::editor_settings () const
293 {
294         XMLNode* node = 0;
295
296         if (_session) {
297                 node = _session->instant_xml(X_("Editor"));
298         } else {
299                 node = Config->instant_xml(X_("Editor"));
300         }
301
302         if (!node) {
303                 if (getenv("ARDOUR_INSTANT_XML_PATH")) {
304                         node = Config->instant_xml(getenv("ARDOUR_INSTANT_XML_PATH"));
305                 }
306         }
307
308         if (!node) {
309                 node = new XMLNode (X_("Editor"));
310         }
311
312         return node;
313 }
314
315 XMLNode*
316 ARDOUR_UI::keyboard_settings () const
317 {
318         XMLNode* node = 0;
319
320         node = Config->extra_xml(X_("Keyboard"));
321
322         if (!node) {
323                 node = new XMLNode (X_("Keyboard"));
324         }
325
326         return node;
327 }
328
329 void
330 ARDOUR_UI::hide_splash ()
331 {
332         Splash::drop ();
333 }
334
335 void
336 ARDOUR_UI::check_announcements ()
337 {
338 #ifdef PHONE_HOME
339         string _annc_filename;
340
341 #ifdef __APPLE__
342         _annc_filename = PROGRAM_NAME "_announcements_osx_";
343 #elif defined PLATFORM_WINDOWS
344         _annc_filename = PROGRAM_NAME "_announcements_windows_";
345 #else
346         _annc_filename = PROGRAM_NAME "_announcements_linux_";
347 #endif
348         _annc_filename.append (VERSIONSTRING);
349
350         _announce_string = "";
351
352         std::string path = Glib::build_filename (user_config_directory(), _annc_filename);
353         FILE* fin = g_fopen (path.c_str(), "rb");
354         if (fin) {
355                 while (!feof (fin)) {
356                         char tmp[1024];
357                         size_t len;
358                         if ((len = fread (tmp, sizeof(char), 1024, fin)) == 0 || ferror (fin)) {
359                                 break;
360                         }
361                         _announce_string.append (tmp, len);
362                 }
363                 fclose (fin);
364         }
365
366         pingback (VERSIONSTRING, path);
367 #endif
368 }
369
370 int
371 ARDOUR_UI::nsm_init ()
372 {
373         const char *nsm_url;
374
375         if ((nsm_url = g_getenv ("NSM_URL")) == 0) {
376                 return 0;
377         }
378
379         nsm = new NSM_Client;
380
381         if (nsm->init (nsm_url)) {
382                 delete nsm;
383                 nsm = 0;
384                 error << _("NSM: initialization failed") << endmsg;
385                 return -1;
386         }
387
388         /* the ardour executable may have different names:
389          *
390          * waf's obj.target for distro versions: eg ardour4, ardourvst4
391          * Ardour4, Mixbus3 for bundled versions + full path on OSX & windows
392          * argv[0] does not apply since we need the wrapper-script (not the binary itself)
393          *
394          * The wrapper startup script should set the environment variable 'ARDOUR_SELF'
395          */
396         const char *process_name = g_getenv ("ARDOUR_SELF");
397         nsm->announce (PROGRAM_NAME, ":dirty:", process_name ? process_name : "ardour6");
398
399         unsigned int i = 0;
400         // wait for announce reply from nsm server
401         for ( i = 0; i < 5000; ++i) {
402                 nsm->check ();
403
404                 Glib::usleep (i);
405                 if (nsm->is_active()) {
406                         break;
407                 }
408         }
409         if (i == 5000) {
410                 error << _("NSM server did not announce itself") << endmsg;
411                 return -1;
412         }
413         // wait for open command from nsm server
414         for ( i = 0; i < 5000; ++i) {
415                 nsm->check ();
416                 Glib::usleep (1000);
417                 if (nsm->client_id ()) {
418                         break;
419                 }
420         }
421
422         if (i == 5000) {
423                 error << _("NSM: no client ID provided") << endmsg;
424                 return -1;
425         }
426
427         if (_session && nsm) {
428                 _session->set_nsm_state( nsm->is_active() );
429         } else {
430                 error << _("NSM: no session created") << endmsg;
431                 return -1;
432         }
433
434         // nsm requires these actions disabled
435         vector<string> action_names;
436         action_names.push_back("SaveAs");
437         action_names.push_back("Rename");
438         action_names.push_back("New");
439         action_names.push_back("Open");
440         action_names.push_back("Recent");
441         action_names.push_back("Close");
442
443         for (vector<string>::const_iterator n = action_names.begin(); n != action_names.end(); ++n) {
444                 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Main"), (*n).c_str());
445                 if (act) {
446                         act->set_sensitive (false);
447                 }
448         }
449
450         return 0;
451 }
452
453 void
454 ARDOUR_UI::sfsm_response (StartupFSM::Result r)
455 {
456         DEBUG_TRACE (DEBUG::GuiStartup, string_compose (X_("startup FSM response %1\n"), r));
457
458         switch (r) {
459         case StartupFSM::ExitProgram:
460                 queue_finish ();
461                 break;
462
463         case StartupFSM::LoadSession:
464
465                 if (load_session_from_startup_fsm () == 0) {
466                         delete startup_fsm;
467                         startup_fsm = 0;
468                         startup_done ();
469                 } else {
470                         startup_fsm->reset ();
471                 }
472
473                 break;
474         }
475 }
476
477 int
478 ARDOUR_UI::starting ()
479 {
480         Application* app = Application::instance();
481
482         if (ARDOUR_COMMAND_LINE::check_announcements) {
483                 check_announcements ();
484         }
485
486         app->ready ();
487
488         /* we need to create this early because it may need to set the
489          *  audio backend end up.
490          */
491
492         EngineControl* amd;
493
494         try {
495                 amd = dynamic_cast<EngineControl*> (audio_midi_setup.get (true));
496         } catch (...) {
497                 std::cerr << "audio-midi engine setup failed."<< std::endl;
498                 return -1;
499         }
500
501         if (nsm_init ()) {
502                 return -1;
503         } else  {
504
505
506                 startup_fsm = new StartupFSM (*amd);
507                 startup_fsm->signal_response().connect (sigc::mem_fun (*this, &ARDOUR_UI::sfsm_response));
508
509                 /* Note: entire startup process could happen in this one call
510                  * if:
511                  *
512                  * 1) not a new user
513                  * 2) session name provided on command line (and valid)
514                  * 3) no audio/MIDI setup required
515                  */
516
517                 startup_fsm->start ();
518         }
519
520         return 0;
521 }
522
523 int
524 ARDOUR_UI::load_session_from_startup_fsm ()
525 {
526         const string session_path = startup_fsm->session_path;
527         const string session_name = startup_fsm->session_name;
528         const string session_template = startup_fsm->session_template;
529         const bool   session_is_new = startup_fsm->session_is_new;
530         const BusProfile bus_profile = startup_fsm->bus_profile;
531
532         std::cerr  << " loading from " << session_path << " as " << session_name << " templ " << session_template << " is_new " << session_is_new << " bp " << bus_profile.master_out_channels << std::endl;
533
534         if (session_is_new) {
535
536                 if (build_session (session_path, session_name, &bus_profile)) {
537                         return -1;
538                 }
539
540                 if (!session_template.empty() && session_template.substr (0, 11) == "urn:ardour:") {
541                         meta_session_setup (session_template.substr (11));
542                 }
543
544                 return 0;
545         }
546
547         return load_session (session_path, session_name, session_template);
548
549 }
550
551 void
552 ARDOUR_UI::startup_done ()
553 {
554         /* ShouldQuit is a desktop environment mechanism that tells the
555            application it should exit for reasons external to the application
556            itself.
557
558            During startup, startupFSM handles ShouldQuit. But it is done now,
559            and we have to take over responsibility.
560         */
561         Application::instance()->ShouldQuit.connect (sigc::mem_fun (*this, &ARDOUR_UI::queue_finish));
562
563         use_config ();
564
565         WM::Manager::instance().show_visible ();
566
567         /* We have to do this here since goto_editor_window() ends up calling show_all() on the
568          * editor window, and we may want stuff to be hidden.
569          */
570         _status_bar_visibility.update ();
571
572         BootMessage (string_compose (_("%1 is ready for use"), PROGRAM_NAME));
573 }
574
575 void
576 ARDOUR_UI::use_config ()
577 {
578         XMLNode* node = Config->extra_xml (X_("TransportControllables"));
579         if (node) {
580                 set_transport_controllable_state (*node);
581         }
582 }
583
584 void
585 ARDOUR_UI::check_memory_locking ()
586 {
587 #if defined(__APPLE__) || defined(PLATFORM_WINDOWS)
588         /* OS X doesn't support mlockall(2), and so testing for memory locking capability there is pointless */
589         return;
590 #else // !__APPLE__
591
592         XMLNode* memory_warning_node = Config->instant_xml (X_("no-memory-warning"));
593
594         if (AudioEngine::instance()->is_realtime() && memory_warning_node == 0) {
595
596                 struct rlimit limits;
597                 int64_t ram;
598                 long pages, page_size;
599 #ifdef __FreeBSD__
600                 size_t pages_len=sizeof(pages);
601                 if ((page_size = getpagesize()) < 0 ||
602                                 sysctlbyname("hw.availpages", &pages, &pages_len, NULL, 0))
603 #else
604                 if ((page_size = sysconf (_SC_PAGESIZE)) < 0 ||(pages = sysconf (_SC_PHYS_PAGES)) < 0)
605 #endif
606                 {
607                         ram = 0;
608                 } else {
609                         ram = (int64_t) pages * (int64_t) page_size;
610                 }
611
612                 if (getrlimit (RLIMIT_MEMLOCK, &limits)) {
613                         return;
614                 }
615
616                 if (limits.rlim_cur != RLIM_INFINITY) {
617
618                         if (ram == 0 || ((double) limits.rlim_cur / ram) < 0.75) {
619
620                                 MessageDialog msg (
621                                         string_compose (
622                                                 _("WARNING: Your system has a limit for maximum amount of locked memory. "
623                                                   "This might cause %1 to run out of memory before your system "
624                                                   "runs out of memory. \n\n"
625                                                   "You can view the memory limit with 'ulimit -l', "
626                                                   "and it is normally controlled by %2"),
627                                                 PROGRAM_NAME,
628 #ifdef __FreeBSD__
629                                                 X_("/etc/login.conf")
630 #else
631                                                 X_(" /etc/security/limits.conf")
632 #endif
633                                         ).c_str());
634
635                                 msg.set_default_response (RESPONSE_OK);
636
637                                 VBox* vbox = msg.get_vbox();
638                                 HBox hbox;
639                                 CheckButton cb (_("Do not show this window again"));
640                                 hbox.pack_start (cb, true, false);
641                                 vbox->pack_start (hbox);
642                                 cb.show();
643                                 vbox->show();
644                                 hbox.show ();
645
646                                 pop_back_splash (msg);
647
648                                 msg.run ();
649
650                                 if (cb.get_active()) {
651                                         XMLNode node (X_("no-memory-warning"));
652                                         Config->add_instant_xml (node);
653                                 }
654                         }
655                 }
656         }
657 #endif // !__APPLE__
658 }