avoid use of Port::port_offset() everywhere except Port::flush_buffers() and Port...
[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         if (ARDOUR_COMMAND_LINE::check_announcements) {
481                 check_announcements ();
482         }
483
484         /* we need to create this early because it may need to set the
485          *  audio backend end up.
486          */
487
488         EngineControl* amd;
489
490         try {
491                 amd = dynamic_cast<EngineControl*> (audio_midi_setup.get (true));
492         } catch (...) {
493                 std::cerr << "audio-midi engine setup failed."<< std::endl;
494                 return -1;
495         }
496
497         if (nsm_init ()) {
498                 return -1;
499         } else  {
500
501
502                 startup_fsm = new StartupFSM (*amd);
503                 startup_fsm->signal_response().connect (sigc::mem_fun (*this, &ARDOUR_UI::sfsm_response));
504
505                 /* Note: entire startup process could happen in this one call
506                  * if:
507                  *
508                  * 1) not a new user
509                  * 2) session name provided on command line (and valid)
510                  * 3) no audio/MIDI setup required
511                  */
512
513                 startup_fsm->start ();
514         }
515
516         return 0;
517 }
518
519 int
520 ARDOUR_UI::load_session_from_startup_fsm ()
521 {
522         const string session_path = startup_fsm->session_path;
523         const string session_name = startup_fsm->session_name;
524         const string session_template = startup_fsm->session_template;
525         const bool   session_is_new = startup_fsm->session_is_new;
526         const BusProfile bus_profile = startup_fsm->bus_profile;
527
528         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;
529
530         if (session_is_new) {
531
532                 if (build_session (session_path, session_name, &bus_profile)) {
533                         return -1;
534                 }
535
536                 if (!session_template.empty() && session_template.substr (0, 11) == "urn:ardour:") {
537                         meta_session_setup (session_template.substr (11));
538                 }
539
540                 return 0;
541         }
542
543         return load_session (session_path, session_name, session_template);
544
545 }
546
547 void
548 ARDOUR_UI::startup_done ()
549 {
550         use_config ();
551
552         WM::Manager::instance().show_visible ();
553
554         /* We have to do this here since goto_editor_window() ends up calling show_all() on the
555          * editor window, and we may want stuff to be hidden.
556          */
557         _status_bar_visibility.update ();
558
559         BootMessage (string_compose (_("%1 is ready for use"), PROGRAM_NAME));
560 }
561
562 void
563 ARDOUR_UI::use_config ()
564 {
565         XMLNode* node = Config->extra_xml (X_("TransportControllables"));
566         if (node) {
567                 set_transport_controllable_state (*node);
568         }
569 }
570
571 void
572 ARDOUR_UI::check_memory_locking ()
573 {
574 #if defined(__APPLE__) || defined(PLATFORM_WINDOWS)
575         /* OS X doesn't support mlockall(2), and so testing for memory locking capability there is pointless */
576         return;
577 #else // !__APPLE__
578
579         XMLNode* memory_warning_node = Config->instant_xml (X_("no-memory-warning"));
580
581         if (AudioEngine::instance()->is_realtime() && memory_warning_node == 0) {
582
583                 struct rlimit limits;
584                 int64_t ram;
585                 long pages, page_size;
586 #ifdef __FreeBSD__
587                 size_t pages_len=sizeof(pages);
588                 if ((page_size = getpagesize()) < 0 ||
589                                 sysctlbyname("hw.availpages", &pages, &pages_len, NULL, 0))
590 #else
591                 if ((page_size = sysconf (_SC_PAGESIZE)) < 0 ||(pages = sysconf (_SC_PHYS_PAGES)) < 0)
592 #endif
593                 {
594                         ram = 0;
595                 } else {
596                         ram = (int64_t) pages * (int64_t) page_size;
597                 }
598
599                 if (getrlimit (RLIMIT_MEMLOCK, &limits)) {
600                         return;
601                 }
602
603                 if (limits.rlim_cur != RLIM_INFINITY) {
604
605                         if (ram == 0 || ((double) limits.rlim_cur / ram) < 0.75) {
606
607                                 MessageDialog msg (
608                                         string_compose (
609                                                 _("WARNING: Your system has a limit for maximum amount of locked memory. "
610                                                   "This might cause %1 to run out of memory before your system "
611                                                   "runs out of memory. \n\n"
612                                                   "You can view the memory limit with 'ulimit -l', "
613                                                   "and it is normally controlled by %2"),
614                                                 PROGRAM_NAME,
615 #ifdef __FreeBSD__
616                                                 X_("/etc/login.conf")
617 #else
618                                                 X_(" /etc/security/limits.conf")
619 #endif
620                                         ).c_str());
621
622                                 msg.set_default_response (RESPONSE_OK);
623
624                                 VBox* vbox = msg.get_vbox();
625                                 HBox hbox;
626                                 CheckButton cb (_("Do not show this window again"));
627                                 hbox.pack_start (cb, true, false);
628                                 vbox->pack_start (hbox);
629                                 cb.show();
630                                 vbox->show();
631                                 hbox.show ();
632
633                                 pop_back_splash (msg);
634
635                                 msg.run ();
636
637                                 if (cb.get_active()) {
638                                         XMLNode node (X_("no-memory-warning"));
639                                         Config->add_instant_xml (node);
640                                 }
641                         }
642                 }
643         }
644 #endif // !__APPLE__
645 }