Audacity 3.2.0
AudacityApp.cpp
Go to the documentation of this file.
1/**********************************************************************
2
3 Audacity: A Digital Audio Editor
4
5 AudacityApp.cpp
6
7 Dominic Mazzoni
8
9******************************************************************//*******************************************************************/
17#include "AudacityApp.h"
18
19#if 0
20// This may be used to debug memory leaks.
21// See: Visual Leak Detector @ http://vld.codeplex.com/
22#include <vld.h>
23#endif
24
25#include <wx/setup.h> // for wxUSE_* macros
26#include <wx/wxcrtvararg.h>
27#include <wx/defs.h>
28#include <wx/evtloop.h>
29#include <wx/bitmap.h>
30#include <wx/docview.h>
31#include <wx/ipc.h>
32#include <wx/window.h>
33#include <wx/menu.h>
34#include <wx/snglinst.h>
35#include <wx/splash.h>
36#include <wx/stdpaths.h>
37#include <wx/sysopt.h>
38#include <wx/fontmap.h>
39
40#include <wx/fs_zip.h>
41
42#include <wx/dir.h>
43#include <wx/file.h>
44#include <wx/filename.h>
45
46#ifdef __WXGTK__
47#include <unistd.h>
48#ifdef HAVE_GTK
49#include <gtk/gtk.h>
50#endif
51#endif
52
53// chmod, lstat, geteuid
54#ifdef __UNIX__
55#include <sys/types.h>
56#include <sys/file.h>
57#include <sys/stat.h>
58#include <stdio.h>
59#endif
60
61#if defined(__WXMSW__)
62#include <wx/msw/registry.h> // for wxRegKey
63#endif
64
65#include "AudacityLogger.h"
66#include "AboutDialog.h"
67#include "ActiveProject.h"
68#include "AColor.h"
69#include "AudacityFileConfig.h"
70#include "AudioIO.h"
71#include "Benchmark.h"
72#include "Clipboard.h"
73#include "CommandLineArgs.h"
74#include "CrashReport.h" // for HAS_CRASH_REPORT
77#include "widgets/ASlider.h"
78#include "Journal.h"
79#include "Languages.h"
80#include "MenuCreator.h"
81#include "PathList.h"
82#include "PendingTracks.h"
83#include "PluginManager.h"
84#include "Project.h"
85#include "ProjectAudioIO.h"
86#include "ProjectAudioManager.h"
87#include "ProjectFileIO.h"
88#include "ProjectFileManager.h"
89#include "ProjectHistory.h"
90#include "ProjectManager.h"
91#include "ProjectSettings.h"
92#include "ProjectWindow.h"
93#include "ProjectWindows.h"
94#include "Sequence.h"
95#include "SelectFile.h"
96#include "TempDirectory.h"
97#include "LoadThemeResources.h"
98#include "Track.h"
99#include "prefs/PrefsDialog.h"
100#include "Theme.h"
101#include "Viewport.h"
103#include "AutoRecoveryDialog.h"
104#include "FFT.h"
105#include "AudacityMessageBox.h"
107#include "prefs/GUISettings.h"
108#include "tracks/ui/Scrubbing.h"
109#include "widgets/FileHistory.h"
110#include "wxWidgetsBasicUI.h"
111#include "LogWindow.h"
116
117#if defined(HAVE_UPDATES_CHECK)
118# include "update/UpdateManager.h"
119#endif
120
121#ifdef HAS_WHATS_NEW
122# include "WhatsNewDialog.h"
123#endif
124
125#ifdef HAS_NETWORKING
126#include "NetworkManager.h"
127#endif
128
129#ifdef EXPERIMENTAL_EASY_CHANGE_KEY_BINDINGS
130#include "prefs/KeyConfigPrefs.h"
131#endif
132
133//temporarily commented out till it is added to all projects
134//#include "Profiler.h"
135
136#include "ModuleManager.h"
137#include "PluginHost.h"
138
139#include "Import.h"
140
141#if defined(USE_BREAKPAD)
142#include "BreakpadConfigurer.h"
143#elif defined(USE_CRASHPAD)
144#include "CrashpadConfigurer.h"
145#endif
146
147#ifdef EXPERIMENTAL_SCOREALIGN
149#endif
150
151#if 0
152#ifdef _DEBUG
153 #ifdef _MSC_VER
154 #undef THIS_FILE
155 static char*THIS_FILE= __FILE__;
156 #define new new(_NORMAL_BLOCK, THIS_FILE, __LINE__)
157 #endif
158#endif
159#endif
160
161
162#include "../images/Audacity-splash.xpm"
163
164#include <thread>
165
166#include "ExportPluginRegistry.h"
167#include "SettingsWX.h"
168#include "prefs/EffectsPrefs.h"
169
170#ifdef HAS_CUSTOM_URL_HANDLING
171#include "URLSchemesRegistry.h"
172
173// This prefix is used to encode parameters
174// in RPC used by single instance checker
175static wxString urlPrefix = wxT("url:");
176#endif
177
178
182
183#if 0
184#ifdef __WXGTK__
185static void wxOnAssert(const wxChar *fileName, int lineNumber, const wxChar *msg)
186{
187 if (msg)
188 wxPrintf("ASSERTION FAILED: %s\n%s: %d\n", (const char *)wxString(msg).mb_str(), (const char *)wxString(fileName).mb_str(), lineNumber);
189 else
190 wxPrintf("ASSERTION FAILED!\n%s: %d\n", (const char *)wxString(fileName).mb_str(), lineNumber);
191
192 // Force core dump
193 int *i = 0;
194 if (*i)
195 exit(1);
196
197 exit(0);
198}
199#endif
200#endif
201
202namespace {
203
205{
206 bool resetPrefs = false;
207 wxString langCode = gPrefs->Read(wxT("/Locale/Language"), wxEmptyString);
208 bool writeLang = false;
209
210 const wxFileName fn(
212 wxT("FirstTime.ini"));
213 if (fn.FileExists()) // it will exist if the (win) installer put it there
214 {
215 const wxString fullPath{fn.GetFullPath()};
216
217 auto pIni =
218 AudacityFileConfig::Create({}, {}, fullPath, {},
219 wxCONFIG_USE_LOCAL_FILE);
220 auto &ini = *pIni;
221
222 wxString lang;
223 if (ini.Read(wxT("/FromInno/Language"), &lang) && !lang.empty())
224 {
225 // Only change "langCode" if the language was actually specified in the ini file.
226 langCode = lang;
227 writeLang = true;
228
229 // Inno Setup doesn't allow special characters in the Name values, so "0" is used
230 // to represent the "@" character.
231 langCode.Replace(wxT("0"), wxT("@"));
232 }
233
234 ini.Read(wxT("/FromInno/ResetPrefs"), &resetPrefs, false);
235
236 bool gone = wxRemoveFile(fullPath); // remove FirstTime.ini
237 if (!gone)
238 {
240 XO("Failed to remove %s").Format(fullPath),
241 XO("Failed!"));
242 }
243 }
244
245 // Use the system default language if one wasn't specified or if the user selected System.
246 if (langCode.empty())
247 langCode =
249
250 langCode = GUISettings::SetLang( langCode );
251
252 // User requested that the preferences be completely reset
253 if (resetPrefs)
254 {
256 writeLang = true;
257 }
258
259 // Save the specified language
260 if (writeLang)
261 {
262 gPrefs->Write(wxT("/Locale/Language"), langCode);
263 }
264
265 // In AUdacity 2.1.0 support for the legacy 1.2.x preferences (depreciated since Audacity
266 // 1.3.1) is dropped. As a result we can drop the import flag
267 // first time this version of Audacity is run we try to migrate
268 // old preferences.
269 bool newPrefsInitialized = false;
270 gPrefs->Read(wxT("/NewPrefsInitialized"), &newPrefsInitialized, false);
271 if (newPrefsInitialized) {
272 gPrefs->DeleteEntry(wxT("/NewPrefsInitialized"));
273 }
274
275 // record the Prefs version for future checking (this has not been used for a very
276 // long time).
277 gPrefs->Write(wxT("/PrefsVersion"), wxString(wxT(AUDACITY_PREFS_VERSION_STRING)));
278
279 // Check if some prefs updates need to happen based on audacity version.
280 // Unfortunately we can't use the PrefsVersion prefs key because that resets things.
281 // In the future we may want to integrate that better.
282 // these are done on a case-by-case basis for now so they must be backwards compatible
283 // (meaning the changes won't mess audacity up if the user goes back to an earlier version)
284 int vMajor = gPrefs->Read(wxT("/Version/Major"), (long) 0);
285 int vMinor = gPrefs->Read(wxT("/Version/Minor"), (long) 0);
286 int vMicro = gPrefs->Read(wxT("/Version/Micro"), (long) 0);
287
288 SetPreferencesVersion(vMajor, vMinor, vMicro); // make a note of these initial values
289 // for use by ToolManager::ReadConfig()
290
291 // These integer version keys were introduced april 4 2011 for 1.3.13
292 // The device toolbar needs to be enabled due to removal of source selection features in
293 // the mixer toolbar.
294 if ((vMajor < 1) ||
295 (vMajor == 1 && vMinor < 3) ||
296 (vMajor == 1 && vMinor == 3 && vMicro < 13)) {
297
298
299 // Do a full reset of the Device Toolbar to get it on the screen.
300 if (gPrefs->Exists(wxT("/GUI/ToolBars/Device")))
301 gPrefs->DeleteGroup(wxT("/GUI/ToolBars/Device"));
302
303 // We keep the mixer toolbar prefs (shown/not shown)
304 // the width of the mixer toolbar may have shrunk, the prefs will keep the larger value
305 // if the user had a device that had more than one source.
306 if (gPrefs->Exists(wxT("/GUI/ToolBars/Mixer"))) {
307 // Use the default width
308 gPrefs->Write(wxT("/GUI/ToolBars/Mixer/W"), -1);
309 }
310 }
311
312 // In 2.1.0, the Meter toolbar was split and lengthened, but strange arrangements happen
313 // if upgrading due to the extra length. So, if a user is upgrading, use the pre-2.1.0
314 // lengths, but still use the NEW split versions.
315 if (gPrefs->Exists(wxT("/GUI/ToolBars/Meter")) &&
316 !gPrefs->Exists(wxT("/GUI/ToolBars/CombinedMeter"))) {
317
318 // Read in all of the existing values
319 long dock, order, show, x, y, w, h;
320 gPrefs->Read(wxT("/GUI/ToolBars/Meter/Dock"), &dock, -1L);
321 gPrefs->Read(wxT("/GUI/ToolBars/Meter/Order"), &order, -1L);
322 gPrefs->Read(wxT("/GUI/ToolBars/Meter/Show"), &show, -1L);
323 gPrefs->Read(wxT("/GUI/ToolBars/Meter/X"), &x, -1L);
324 gPrefs->Read(wxT("/GUI/ToolBars/Meter/Y"), &y, -1L);
325 gPrefs->Read(wxT("/GUI/ToolBars/Meter/W"), &w, -1L);
326 gPrefs->Read(wxT("/GUI/ToolBars/Meter/H"), &h, -1L);
327
328 // "Order" must be adjusted since we're inserting two NEW toolbars
329 if (dock > 0) {
330
331 const auto toolbarsGroup = gPrefs->BeginGroup("/GUI/ToolBars");
332 for(const auto& group : gPrefs->GetChildGroups())
333 {
334 long orderValue;
335 const auto orderKey = group + wxT("/Order");
336 if(gPrefs->Read(orderKey, &orderValue) && orderValue >= order)
337 gPrefs->Write(orderKey, orderValue + 2);
338 }
339 // And override the height
340 h = 27;
341 }
342
343 // Write the split meter bar values
344 gPrefs->Write(wxT("/GUI/ToolBars/RecordMeter/Dock"), dock);
345 gPrefs->Write(wxT("/GUI/ToolBars/RecordMeter/Order"), order);
346 gPrefs->Write(wxT("/GUI/ToolBars/RecordMeter/Show"), show);
347 gPrefs->Write(wxT("/GUI/ToolBars/RecordMeter/X"), -1);
348 gPrefs->Write(wxT("/GUI/ToolBars/RecordMeter/Y"), -1);
349 gPrefs->Write(wxT("/GUI/ToolBars/RecordMeter/W"), w);
350 gPrefs->Write(wxT("/GUI/ToolBars/RecordMeter/H"), h);
351 gPrefs->Write(wxT("/GUI/ToolBars/PlayMeter/Dock"), dock);
352 gPrefs->Write(wxT("/GUI/ToolBars/PlayMeter/Order"), order + 1);
353 gPrefs->Write(wxT("/GUI/ToolBars/PlayMeter/Show"), show);
354 gPrefs->Write(wxT("/GUI/ToolBars/PlayMeter/X"), -1);
355 gPrefs->Write(wxT("/GUI/ToolBars/PlayMeter/Y"), -1);
356 gPrefs->Write(wxT("/GUI/ToolBars/PlayMeter/W"), w);
357 gPrefs->Write(wxT("/GUI/ToolBars/PlayMeter/H"), h);
358
359 // And hide the old combined meter bar
360 gPrefs->Write(wxT("/GUI/ToolBars/Meter/Dock"), -1);
361 }
362
363 // Upgrading pre 2.2.0 configs we assume extended set of defaults.
364 if ((0<vMajor && vMajor < 2) ||
365 (vMajor == 2 && vMinor < 2))
366 {
367 gPrefs->Write(wxT("/GUI/Shortcuts/FullDefaults"),1);
368 }
369
370 // Upgrading pre 2.4.0 configs, the selection toolbar is now split.
371 if ((0<vMajor && vMajor < 2) ||
372 (vMajor == 2 && vMinor < 4))
373 {
374 gPrefs->Write(wxT("/GUI/Toolbars/Selection/W"),"");
375 gPrefs->Write(wxT("/GUI/Toolbars/SpectralSelection/W"),"");
376 gPrefs->Write(wxT("/GUI/Toolbars/Time/X"),-1);
377 gPrefs->Write(wxT("/GUI/Toolbars/Time/Y"),-1);
378 gPrefs->Write(wxT("/GUI/Toolbars/Time/H"),55);
379 gPrefs->Write(wxT("/GUI/Toolbars/Time/W"),251);
380 gPrefs->Write(wxT("/GUI/Toolbars/Time/DockV2"),2);
381 gPrefs->Write(wxT("/GUI/Toolbars/Time/Dock"),2);
382 gPrefs->Write(wxT("/GUI/Toolbars/Time/Path"),"0,1");
383 gPrefs->Write(wxT("/GUI/Toolbars/Time/Show"),1);
384 }
385
386 if (std::pair{ vMajor, vMinor } < std::pair{ 3, 1 } ) {
387 // Reset the control toolbar
388 gPrefs->Write(wxT("/GUI/Toolbars/Control/W"), -1);
389 }
390
391 if(std::pair{vMajor, vMinor} < std::pair{3, 2})
392 {
393 if(gPrefs->Exists(wxT("/GUI/ToolBars")))
394 gPrefs->DeleteGroup(wxT("/GUI/ToolBars"));
395 if(gPrefs->Exists(wxT("Window")))
396 gPrefs->DeleteGroup(wxT("Window"));
397 if(gPrefs->Exists("/GUI/ShowSplashScreen"))
398 gPrefs->DeleteEntry("/GUI/ShowSplashScreen");
399 if(gPrefs->Exists("/GUI/Help"))
400 gPrefs->DeleteEntry("/GUI/Help");
401 }
402
403 if(std::tuple{ vMajor, vMinor, vMicro } < std::tuple{ 3, 2, 3 })
404 {
405 // Reset Share Audio width if it was populated before 3.2.3
406 if(gPrefs->Exists("/GUI/ToolBars/Share Audio/W"))
407 gPrefs->DeleteEntry("/GUI/ToolBars/Share Audio/W");
408 }
409
410 // We need to reset the toolbar layout and force the splash screen for 3.4
411 if (std::pair { vMajor, vMinor } < std::pair { 3, 4 })
412 {
413 if (gPrefs->Exists(wxT("/GUI/ToolBars")))
414 gPrefs->DeleteGroup(wxT("/GUI/ToolBars"));
415 if (gPrefs->Exists("/GUI/ShowSplashScreen"))
416 gPrefs->DeleteEntry("/GUI/ShowSplashScreen");
417 }
418
419 if (std::pair { vMajor, vMinor } < std::pair { 3, 5 })
420 {
421 if (gPrefs->Exists("/GUI/ShowSplashScreen"))
422 gPrefs->DeleteEntry("/GUI/ShowSplashScreen");
423 }
424
425 if (std::pair { vMajor, vMinor } < std::pair { 3, 6 })
426 {
427 if (gPrefs->Exists("/GUI/ShowSplashScreen"))
428 gPrefs->DeleteEntry("/GUI/ShowSplashScreen");
429 if (gPrefs->Exists(wxT("/GUI/ToolBars")))
430 gPrefs->DeleteGroup(wxT("/GUI/ToolBars"));
431 }
432
433 // write out the version numbers to the prefs file for future checking
434 gPrefs->Write(wxT("/Version/Major"), AUDACITY_VERSION);
435 gPrefs->Write(wxT("/Version/Minor"), AUDACITY_RELEASE);
436 gPrefs->Write(wxT("/Version/Micro"), AUDACITY_REVISION);
437
438 gPrefs->Flush();
439}
440
442{
443#if defined(USE_BREAKPAD) || defined(USE_CRASHPAD)
444 wxFileName databasePath;
445 databasePath.SetPath(FileNames::StateDir());
446 databasePath.AppendDir("crashreports");
447 databasePath.Mkdir(wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL);
448
449 if(databasePath.DirExists())
450 {
451 const auto sentryRelease = wxString::Format(
452 "audacity@%d.%d.%d", AUDACITY_VERSION, AUDACITY_RELEASE, AUDACITY_REVISION);
453#if defined(USE_BREAKPAD)
454 BreakpadConfigurer configurer;
455 configurer.SetDatabasePathUTF8(databasePath.GetPath().ToUTF8().data())
456 .SetSenderPathUTF8(wxFileName(wxStandardPaths::Get().GetExecutablePath()).GetPath().ToUTF8().data())
457 #if defined(CRASH_REPORT_URL)
458 .SetReportURL(CRASH_REPORT_URL)
459 #endif
461 { "version", wxString(AUDACITY_VERSION_STRING).ToUTF8().data() },
462 { "sentry[release]", sentryRelease.ToUTF8().data() }
463 })
464 .Start();
465#elif defined(USE_CRASHPAD)
466 try
467 {
468 const auto executableDir = wxFileName(wxStandardPaths::Get().GetExecutablePath()).GetPath();
469 const wxFileName crashpadHandlerPath(executableDir, CRASHPAD_HANDLER_NAME);
470 const wxFileName crashreporterPath(executableDir, CRASHREPORTER_NAME);
471 const wxFileName metricsDir = databasePath;
472 std::vector<std::string> arguments = {
473 wxString::Format("--crashreporter-path=%s", crashreporterPath.GetFullPath()).ToUTF8().data(),
474#if defined(CRASH_REPORT_URL)
475 wxString::Format(
476 "--crashreporter-argument=-u=%s",
477 CRASH_REPORT_URL).ToUTF8().data(),
478 wxString::Format(
479 "--crashreporter-argument=-a=version=\"%s\",sentry[release]=\"%s\"",
480 AUDACITY_VERSION_STRING,
481 sentryRelease).ToUTF8().data()
482#endif
483 };
484 CrashpadConfigurer configurer;
485 configurer.SetHandlerPathUTF8(crashpadHandlerPath.GetFullPath().ToUTF8().data())
486 .SetDatabasePathUTF8(databasePath.GetFullPath().ToUTF8().data())
487 .SetMetricsDirUTF8(metricsDir.GetFullPath().ToUTF8().data())
488 .SetArguments(arguments)
489 .Start();
490 }
491 catch (std::exception& e)
492 {
493 wxLogError("Crashpad init error: %s", e.what());
494 }
495 }
496#endif
497#elif !defined(_DEBUG)// Do not capture crashes in debug builds
498#if defined(HAS_CRASH_REPORT)
499#if defined(wxUSE_ON_FATAL_EXCEPTION) && wxUSE_ON_FATAL_EXCEPTION
500 wxHandleFatalExceptions();
501#endif
502#endif
503#endif
504}
505
506}
507
508static bool gInited = false;
509static bool gIsQuitting = false;
510
511//Config instance that is set as current instance of `wxConfigBase`
512//and used to initialize `SettingsWX` objects created by
513//`audacity::ApplicationSettings` hook.
514//Created on demand by calling `audacity::ApplicationSettings::Call()`
515static std::shared_ptr<wxConfigBase> gConfig;
516
517static bool CloseAllProjects( bool force )
518{
520 auto cleanup = finally([]{ ProjectManager::SetClosingAll(false); });
521 while (AllProjects{}.size())
522 {
523 // Closing the project has global side-effect
524 // of deletion from gAudacityProjects
525 if ( force )
526 {
527 GetProjectFrame( **AllProjects{}.begin() ).Close(true);
528 }
529 else
530 {
531 if (! GetProjectFrame( **AllProjects{}.begin() ).Close())
532 return false;
533 }
534 }
535 return true;
536}
537
538static void QuitAudacity(bool bForce)
539{
540 // guard against recursion
541 if (gIsQuitting)
542 return;
543
544 gIsQuitting = true;
545
546 wxTheApp->SetExitOnFrameDelete(true);
547
548 // Try to close each open window. If the user hits Cancel
549 // in a Save Changes dialog, don't continue.
550 // BG: unless force is true
551
552 // BG: Are there any projects open?
553 //- if (!AllProjects{}.empty())
554/*start+*/
555 if (AllProjects{}.empty())
556 {
557#ifdef __WXMAC__
559#endif
560 }
561 else
562/*end+*/
563 {
564 if (AllProjects{}.size())
565 // PRL: Always did at least once before close might be vetoed
566 // though I don't know why that is important
568 bool closedAll = CloseAllProjects( bForce );
569 if ( !closedAll )
570 {
571 gIsQuitting = false;
572 return;
573 }
574 }
575
577
578#ifdef EXPERIMENTAL_SCOREALIGN
579 CloseScoreAlignDialog();
580#endif
581
582 // Logger window is always destroyed on macOS,
583 // on other platforms - it prevents the runloop
584 // termination when exiting is requested
585 #if !defined(__WXMAC__)
588 #endif
589
590 //print out profile if we have one by deleting it
591 //temporarily commented out till it is added to all projects
592 //DELETE Profiler::Instance();
593
594 // Save last log for diagnosis
595 auto logger = AudacityLogger::Get();
596 if (logger)
597 {
598 wxFileName logFile(FileNames::DataDir(), wxT("lastlog.txt"));
599 logger->SaveLog(logFile.GetFullPath());
600 }
601
602 //remove our logger
603 std::unique_ptr<wxLog>{ wxLog::SetActiveTarget(NULL) }; // DELETE
604
605 if (bForce)
606 {
607 wxExit();
608 }
609}
610
611static void QuitAudacity()
612{
613 QuitAudacity(false);
614}
615
616#if defined(__WXGTK__) && defined(HAVE_GTK)
617
619// Provide the ability to receive notification from the session manager
620// when the user is logging out or shutting down.
621//
622// Most of this was taken from nsNativeAppSupportUnix.cpp from Mozilla.
624
625// TODO: May need updating. Is this code too obsolete (relying on Gnome2 so's) to be
626// worth keeping anymore?
627// CB suggests we use libSM directly ref:
628// http://www.x.org/archive/X11R7.7/doc/libSM/SMlib.html#The_Save_Yourself_Callback
629
630#include <dlfcn.h>
631/* There is a conflict between the type names used in Glib >= 2.21 and those in
632 * wxGTK (http://trac.wxwidgets.org/ticket/10883)
633 * Happily we can avoid the hack, as we only need some of the headers, not
634 * the full GTK headers
635 */
636#include <glib-object.h>
637
638typedef struct _GnomeProgram GnomeProgram;
639typedef struct _GnomeModuleInfo GnomeModuleInfo;
640typedef struct _GnomeClient GnomeClient;
641
642typedef enum
643{
644 GNOME_SAVE_GLOBAL,
645 GNOME_SAVE_LOCAL,
646 GNOME_SAVE_BOTH
647} GnomeSaveStyle;
648
649typedef enum
650{
651 GNOME_INTERACT_NONE,
652 GNOME_INTERACT_ERRORS,
653 GNOME_INTERACT_ANY
654} GnomeInteractStyle;
655
656typedef enum
657{
658 GNOME_DIALOG_ERROR,
659 GNOME_DIALOG_NORMAL
660} GnomeDialogType;
661
662typedef GnomeProgram * (*_gnome_program_init_fn)(const char *,
663 const char *,
664 const GnomeModuleInfo *,
665 int,
666 char **,
667 const char *,
668 ...);
669typedef const GnomeModuleInfo * (*_libgnomeui_module_info_get_fn)();
670typedef GnomeClient * (*_gnome_master_client_fn)(void);
671typedef void (*GnomeInteractFunction)(GnomeClient *,
672 gint,
673 GnomeDialogType,
674 gpointer);
675typedef void (*_gnome_client_request_interaction_fn)(GnomeClient *,
676 GnomeDialogType,
677 GnomeInteractFunction,
678 gpointer);
679typedef void (*_gnome_interaction_key_return_fn)(gint, gboolean);
680
681static _gnome_client_request_interaction_fn gnome_client_request_interaction;
682static _gnome_interaction_key_return_fn gnome_interaction_key_return;
683
684static void interact_cb(GnomeClient * /* client */,
685 gint key,
686 GnomeDialogType /* type */,
687 gpointer /* data */)
688{
689 wxCloseEvent e(wxEVT_QUERY_END_SESSION, wxID_ANY);
690 e.SetEventObject(&wxGetApp());
691 e.SetCanVeto(true);
692
693 wxGetApp().ProcessEvent(e);
694
695 gnome_interaction_key_return(key, e.GetVeto());
696}
697
698static gboolean save_yourself_cb(GnomeClient *client,
699 gint /* phase */,
700 GnomeSaveStyle /* style */,
701 gboolean shutdown,
702 GnomeInteractStyle interact,
703 gboolean /* fast */,
704 gpointer /* user_data */)
705{
706 if (!shutdown || interact != GNOME_INTERACT_ANY) {
707 return TRUE;
708 }
709
710 if (AllProjects{}.empty()) {
711 return TRUE;
712 }
713
714 gnome_client_request_interaction(client,
715 GNOME_DIALOG_NORMAL,
716 interact_cb,
717 NULL);
718
719 return TRUE;
720}
721
722class GnomeShutdown
723{
724 public:
725 GnomeShutdown()
726 {
727#ifdef __OpenBSD__
728 const char *libgnomeui = "libgnomeui-2.so";
729 const char *libgnome = "libgnome-2.so";
730#else
731 const char *libgnomeui = "libgnomeui-2.so.0";
732 const char *libgnome = "libgnome-2.so.0";
733#endif
734
735 mArgv[0].reset(strdup("Audacity"));
736
737 mGnomeui = dlopen(libgnomeui, RTLD_NOW);
738 if (!mGnomeui) {
739 return;
740 }
741
742 mGnome = dlopen(libgnome, RTLD_NOW);
743 if (!mGnome) {
744 return;
745 }
746
747 _gnome_program_init_fn gnome_program_init = (_gnome_program_init_fn)
748 dlsym(mGnome, "gnome_program_init");
749 _libgnomeui_module_info_get_fn libgnomeui_module_info_get = (_libgnomeui_module_info_get_fn)
750 dlsym(mGnomeui, "libgnomeui_module_info_get");
751 _gnome_master_client_fn gnome_master_client = (_gnome_master_client_fn)
752 dlsym(mGnomeui, "gnome_master_client");
753
754 gnome_client_request_interaction = (_gnome_client_request_interaction_fn)
755 dlsym(mGnomeui, "gnome_client_request_interaction");
756 gnome_interaction_key_return = (_gnome_interaction_key_return_fn)
757 dlsym(mGnomeui, "gnome_interaction_key_return");
758
759
760 if (!gnome_program_init || !libgnomeui_module_info_get) {
761 return;
762 }
763
764 gnome_program_init(mArgv[0].get(),
765 "1.0",
766 libgnomeui_module_info_get(),
767 1,
768 reinterpret_cast<char**>(mArgv),
769 NULL);
770
771 mClient = gnome_master_client();
772 if (mClient == NULL) {
773 return;
774 }
775
776 g_signal_connect(mClient, "save-yourself", G_CALLBACK(save_yourself_cb), NULL);
777 }
778
779 virtual ~GnomeShutdown()
780 {
781 // Do not dlclose() the libraries here lest you want segfaults...
782 }
783
784 private:
785
786 MallocString<> mArgv[1];
787 void *mGnomeui;
788 void *mGnome;
789 GnomeClient *mClient;
790};
791
792// This variable exists to call the constructor and
793// connect a signal for the 'save-yourself' message.
794GnomeShutdown GnomeShutdownInstance;
795
796#endif
797
798// Where drag/drop or "Open With" filenames get stored until
799// the timer routine gets around to picking them up.
800static wxArrayString ofqueue;
801
802//
803// DDE support for opening multiple files with one instance
804// of Audacity.
805//
806
807#define IPC_APPL wxT("audacity")
808#define IPC_TOPIC wxT("System")
809
810class IPCConn final : public wxConnection
811{
812public:
814 : wxConnection()
815 {
816 };
817
819 {
820 };
821
822 bool OnExec(const wxString & WXUNUSED(topic),
823 const wxString & data)
824 {
825 // Add the filename to the queue. It will be opened by
826 // the OnTimer() event when it is safe to do so.
827 ofqueue.push_back(data);
828
829 return true;
830 }
831};
832
833class IPCServ final : public wxServer
834{
835public:
836 IPCServ(const wxString & appl)
837 : wxServer()
838 {
839 Create(appl);
840 };
841
843 {
844 };
845
846 wxConnectionBase *OnAcceptConnection(const wxString & topic) override
847 {
848 if (topic != IPC_TOPIC) {
849 return NULL;
850 }
851
852 // Trust wxWidgets framework to DELETE it
853 return safenew IPCConn();
854 };
855};
856
857#if defined(__WXMAC__)
858
859IMPLEMENT_APP_NO_MAIN(AudacityApp)
861
862#include <ApplicationServices/ApplicationServices.h>
863
864namespace
865{
867}
868
869int main(int argc, char *argv[])
870{
871 wxDISABLE_DEBUG_SUPPORT();
872
875
877 {
878 sOSXIsGUIApplication = false;
879 ProcessSerialNumber psn = { 0, kCurrentProcess };
880 TransformProcessType(&psn, kProcessTransformToUIElementApplication);
881 }
882
883 return wxEntry(argc, argv);
884}
885
886#elif defined(__WXGTK__) && defined(NDEBUG)
887
888// Specially define main() for Linux debug
889
890IMPLEMENT_APP_NO_MAIN(AudacityApp)
892
893int main(int argc, char *argv[])
894{
897
898 wxDISABLE_DEBUG_SUPPORT();
899
900 // Bug #1986 workaround - This doesn't actually reduce the number of
901 // messages, it simply hides them in Release builds. We'll probably
902 // never be able to get rid of the messages entirely, but we should
903 // look into what's causing them, so allow them to show in Debug
904 // builds.
905 freopen("/dev/null", "w", stdout);
906 freopen("/dev/null", "w", stderr);
907
908 return wxEntry(argc, argv);
909}
910
911#elif defined(__WXGTK__)
912
913// Linux release build
914
915wxIMPLEMENT_WX_THEME_SUPPORT
916int main(int argc, char *argv[])
917{
920
921 wxDISABLE_DEBUG_SUPPORT();
922
923 return wxEntry(argc, argv);
924}
925wxIMPLEMENT_APP_NO_MAIN(AudacityApp);
926
927#else
928
929wxIMPLEMENT_WX_THEME_SUPPORT
930extern "C" int WINAPI WinMain(HINSTANCE hInstance,
931 HINSTANCE hPrevInstance,
932 wxCmdLineArgType lpCmdLine,
933 int nCmdShow)
934{
935 static CommandLineArgs::MSWParser wxArgs;
936 CommandLineArgs::argc = wxArgs.argc;
937 CommandLineArgs::argv = wxArgs.argv.data();
938
939 wxDISABLE_DEBUG_SUPPORT();
940
941 return wxEntry(hInstance, hPrevInstance, lpCmdLine, nCmdShow);
942}
943wxIMPLEMENT_APP_NO_MAIN(AudacityApp);
944
945#endif
946
947#ifdef __WXMAC__
948
949// in response of an open-document apple event
950void AudacityApp::MacOpenFile(const wxString &fileName)
951{
952 ofqueue.push_back(fileName);
953}
954
955// in response of a print-document apple event
956void AudacityApp::MacPrintFile(const wxString &fileName)
957{
958 ofqueue.push_back(fileName);
959}
960
961// in response of a open-application apple event
963{
964 if (!gInited)
965 return;
966
967 // This method should only be used on the Mac platform
968 // when no project windows are open.
969
970 if (AllProjects{}.empty())
971 (void) ProjectManager::New();
972}
973
974#ifdef HAS_CUSTOM_URL_HANDLING
975void AudacityApp::MacOpenURL (const wxString& url)
976{
977 if(!url.IsEmpty())
978 ofqueue.push_back(urlPrefix + url);
979}
980#endif
981
982#endif //__WXMAC__
983
984// IPC communication
985#define ID_IPC_SERVER 6200
986#define ID_IPC_SOCKET 6201
987
988// we don't really care about the timer id, but set this value just in case we do in the future
989#define kAudacityAppTimerID 0
990
991BEGIN_EVENT_TABLE(AudacityApp, wxApp)
992 EVT_IDLE( AudacityApp::OnIdle )
993
994 EVT_QUERY_END_SESSION(AudacityApp::OnQueryEndSession)
995 EVT_END_SESSION(AudacityApp::OnEndSession)
996
998#ifdef __WXMAC__
1002 EVT_MENU(wxID_PREFERENCES, AudacityApp::OnMenuPreferences)
1003#endif
1004
1005 // Associate the handler with the menu id on all operating systems, even
1006 // if they don't have an application menu bar like in macOS, so that
1007 // other parts of the program can send the application a shut-down
1008 // event
1010
1011#ifndef __WXMSW__
1014#endif
1015
1016 // Recent file event handlers.
1018 EVT_MENU_RANGE(FileHistory::ID_RECENT_FIRST, FileHistory::ID_RECENT_LAST,
1019 AudacityApp::OnMRUFile)
1020
1021 // Handle AppCommandEvents (usually from a script)
1022 EVT_APP_COMMAND(wxID_ANY, AudacityApp::OnReceiveCommand)
1023
1024 // Global ESC key handling
1025 EVT_KEY_DOWN(AudacityApp::OnKeyDown)
1027
1028// backend for OnMRUFile
1029// TODO: Would be nice to make this handle not opening a file with more panache.
1030// - Inform the user if DefaultOpenPath not set.
1031// - Switch focus to correct instance of project window, if already open.
1032bool AudacityApp::MRUOpen(const FilePath &fullPathStr) {
1033 // Most of the checks below are copied from ProjectManager::OpenFiles.
1034 // - some rationalisation might be possible.
1035
1036 auto pProj = GetActiveProject().lock();
1037 auto proj = pProj.get();
1038
1039 if (!fullPathStr.empty())
1040 {
1041 // verify that the file exists
1042 if (wxFile::Exists(fullPathStr))
1043 {
1044 FileNames::UpdateDefaultPath(FileNames::Operation::Open, ::wxPathOnly(fullPathStr));
1045
1046 // Make sure it isn't already open.
1047 // Test here even though AudacityProject::OpenFile() also now checks, because
1048 // that method does not return the bad result.
1049 // That itself may be a FIXME.
1050 if (ProjectFileManager::IsAlreadyOpen(fullPathStr))
1051 return false;
1052
1054 ( void ) ProjectManager::OpenProject( proj, fullPathStr,
1055 true /* addtohistory */, false /* reuseNonemptyProject */ );
1056 }
1057 else {
1058 // File doesn't exist - remove file from history
1060 XO(
1061"%s could not be found.\n\nIt has been removed from the list of recent files.")
1062 .Format(fullPathStr) );
1063 return(false);
1064 }
1065 }
1066 return(true);
1067}
1068
1069bool AudacityApp::SafeMRUOpen(const wxString &fullPathStr)
1070{
1071 return GuardedCall< bool >( [&]{ return MRUOpen( fullPathStr ); } );
1072}
1073
1074void AudacityApp::OnMRUClear(wxCommandEvent& WXUNUSED(event))
1075{
1077}
1078
1079//vvv Basically, anything from Recent Files is treated as a .aup3, until proven otherwise,
1080// then it tries to Import(). Very questionable handling, imo.
1081// Better, for example, to check the file type early on.
1082void AudacityApp::OnMRUFile(wxCommandEvent& event) {
1083 int n = event.GetId() - FileHistory::ID_RECENT_FIRST;
1084 auto &history = FileHistory::Global();
1085 const auto &fullPathStr = history[ n ];
1086
1087 // Try to open only if not already open.
1088 // Test IsAlreadyOpen() here even though AudacityProject::MRUOpen() also now checks,
1089 // because we don't want to Remove() just because it already exists,
1090 // and AudacityApp::OnMacOpenFile() calls MRUOpen() directly.
1091 // that method does not return the bad result.
1092 // PRL: Don't call SafeMRUOpen
1093 // -- if open fails for some exceptional reason of resource exhaustion that
1094 // the user can correct, leave the file in history.
1095 if (!ProjectFileManager::IsAlreadyOpen(fullPathStr) && !MRUOpen(fullPathStr))
1096 history.Remove(n);
1097}
1098
1099void AudacityApp::OnTimer(wxTimerEvent& WXUNUSED(event))
1100{
1101 // Filenames are queued when Audacity receives a few of the
1102 // AppleEvent messages (via wxWidgets). So, open any that are
1103 // in the queue and clean the queue.
1104 if (gInited) {
1105 if (ofqueue.size()) {
1106 // Load each file on the queue
1107 while (ofqueue.size()) {
1108 wxString name;
1109 name.swap(ofqueue[0]);
1110 ofqueue.erase( ofqueue.begin() );
1111
1112 // Get the user's attention if no file name was specified
1113 if (name.empty()) {
1114 // Get the users attention
1115 if (auto project = GetActiveProject().lock()) {
1116 auto &window = GetProjectFrame( *project );
1117 window.Maximize();
1118 window.Raise();
1119 window.RequestUserAttention();
1120 }
1121 continue;
1122 }
1123
1124 #ifdef HAS_CUSTOM_URL_HANDLING
1125 if (name.StartsWith(urlPrefix))
1126 {
1127 const auto utf8Url = name.ToUTF8();
1128 const size_t prefixSize = urlPrefix.Length();
1129
1130 if (utf8Url.length() <= prefixSize)
1131 continue;
1132
1134 { utf8Url.data() + prefixSize,
1135 utf8Url.length() - prefixSize });
1136 }
1137 else
1138 #endif
1139 // TODO: Handle failures better.
1140 // Some failures are OK, e.g. file not found, just would-be-nices to do better,
1141 // so FAIL_MSG is more a case of an enhancement request than an actual problem.
1142 // LL: In all but one case an appropriate message is already displayed. The
1143 // instance that a message is NOT displayed is when a failure to write
1144 // to the config file has occurred.
1145 // PRL: Catch any exceptions, don't try this file again, continue to
1146 // other files.
1147 if (!SafeMRUOpen(name)) {
1148 // Just log it. Assertion failure is not appropriate on valid
1149 // defensive path against bad file data.
1150 wxLogMessage(wxT("MRUOpen failed"));
1151 }
1152 }
1153 }
1154 }
1155}
1156
1157#if defined(__WXMSW__)
1158#define WL(lang, sublang) (lang), (sublang),
1159#else
1160#define WL(lang,sublang)
1161#endif
1162
1163#if wxCHECK_VERSION(3, 0, 1) && !wxCHECK_VERSION(3, 1, 6)
1164wxLanguageInfo userLangs[] =
1165{
1166 // Included upstream in version 3.1.6
1167 { wxLANGUAGE_USER_DEFINED, wxT("eu"), WL(0, SUBLANG_DEFAULT) wxT("Basque"), wxLayout_LeftToRight },
1168};
1169#endif
1170
1172{
1173#if defined(HAS_CRASH_REPORT)
1174 CrashReport::Generate(wxDebugReport::Context_Exception);
1175#endif
1176
1177 exit(-1);
1178}
1179
1180
1181#ifdef _MSC_VER
1182// If this is compiled with MSVC (Visual Studio)
1183#pragma warning( push )
1184#pragma warning( disable : 4702) // unreachable code warning.
1185#endif //_MSC_VER
1186
1188{
1189 // This function is invoked from catch blocks in the wxWidgets framework,
1190 // and throw; without argument re-throws the exception being handled,
1191 // letting us dispatch according to its type.
1192
1193 try { throw; }
1194 catch ( AudacityException &e ) {
1195 (void)e;// Compiler food
1196 // Here is the catch-all for our own exceptions
1197
1198 // Use CallAfter to delay this to the next pass of the event loop,
1199 // rather than risk doing it inside stack unwinding.
1200 auto pProject = ::GetActiveProject().lock();
1201 auto pException = std::current_exception();
1202 CallAfter( [pException, pProject] {
1203
1204 // Restore the state of the project to what it was before the
1205 // failed operation
1206 if (pProject) {
1207 ProjectHistory::Get( *pProject ).RollbackState();
1208
1209 // Forget pending changes in the TrackList
1211 Viewport::Get(*pProject).Redraw();
1212 }
1213
1214 // Give the user an alert
1215 try { std::rethrow_exception( pException ); }
1216 catch( AudacityException &e )
1217 { e.DelayedHandlerAction(); }
1218
1219 } );
1220
1221 // Don't quit the program
1222 return true;
1223 }
1224 catch ( ... ) {
1225 // There was some other type of exception we don't know.
1226 // Let the inherited function do throw; again and whatever else it does.
1227 return wxApp::OnExceptionInMainLoop();
1228 }
1229 // Shouldn't ever reach this line
1230 return false;
1231}
1232#ifdef _MSC_VER
1233#pragma warning( pop )
1234#endif //_MSC_VER
1235
1237{
1238}
1239
1241{
1243 {
1245 }
1246 return wxApp::Initialize(argc, argv);
1247}
1248
1250{
1251 //Reset the current wxConfigBase instance manually
1252 //to avoid double deletion in wxWidgets 3.2
1253 //See Bug #5511
1254 wxConfigBase::Set(nullptr);
1255 gConfig.reset();
1256 wxApp::CleanUp();
1257}
1258
1259#ifdef __WXMAC__
1261{
1262 return sOSXIsGUIApplication;
1263}
1264#endif
1265
1267{
1268}
1269
1270// Some of the many initialization steps
1272{
1273 // Inject basic GUI services behind the facades
1274 {
1275 static wxWidgetsBasicUI uiServices;
1276 (void)BasicUI::Install(&uiServices);
1277
1280 -> std::unique_ptr<BasicUI::WindowPlacement> {
1281 return std::make_unique<wxWidgetsWindowPlacement>(
1283 } };
1284 }
1285
1286 // Fire up SQLite
1288 this->CallAfter([]{
1290 XO("SQLite library failed to initialize. Audacity cannot continue.") );
1291 QuitAudacity( true );
1292 });
1293
1294
1295 // cause initialization of wxWidgets' global logger target
1296 (void) AudacityLogger::Get();
1297
1298#if defined(__WXMAC__)
1299 // Disable window animation
1300 wxSystemOptions::SetOption(wxMAC_WINDOW_PLAIN_TRANSITION, 1);
1301#endif
1302
1303 // Some GTK themes produce larger combo boxes that make them taller
1304 // than our single toolbar height restriction. This will remove some
1305 // of the extra space themes add.
1306#if defined(__WXGTK3__) && defined(HAVE_GTK)
1307 GtkWidget *combo = gtk_combo_box_new();
1308 GtkCssProvider *provider = gtk_css_provider_new();
1309 gtk_css_provider_load_from_data(GTK_CSS_PROVIDER(provider),
1310 ".linked entry,\n"
1311 ".linked button,\n"
1312 ".linked combobox box.linked button,\n"
1313 ".horizontal.linked entry,\n"
1314 ".horizontal.linked button,\n"
1315 ".horizontal.linked combobox box.linked button,\n"
1316 "combobox {\n"
1317 " padding-top: 0px;\n"
1318 " padding-bottom: 0px;\n"
1319 " padding-left: 4px;\n"
1320 " padding-right: 4px;\n"
1321 " margin: 0px;\n"
1322 " font-size: 95%;\n"
1323 "}", -1, NULL);
1324 gtk_style_context_add_provider_for_screen(gtk_widget_get_screen(combo),
1325 GTK_STYLE_PROVIDER (provider),
1326 GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
1327 g_object_unref(provider);
1328 g_object_unref(combo);
1329#elif defined(__WXGTK__) && defined(HAVE_GTK)
1330 gtk_rc_parse_string("style \"audacity\" {\n"
1331 " GtkButton::inner_border = { 0, 0, 0, 0 }\n"
1332 " GtkEntry::inner_border = { 0, 0, 0, 0 }\n"
1333 " xthickness = 4\n"
1334 " ythickness = 0\n"
1335 "}\n"
1336 "widget_class \"*GtkCombo*\" style \"audacity\"");
1337#endif
1338
1339 wxTheApp->SetAppName(AppName);
1340 // Explicitly set since OSX will use it for the "Quit" menu item
1341 wxTheApp->SetAppDisplayName(AppName);
1342 wxTheApp->SetVendorName(AppName);
1343
1344 ::wxInitAllImageHandlers();
1345
1346 // AddHandler takes ownership
1347 wxFileSystem::AddHandler(safenew wxZipFSHandler);
1348}
1349
1350// The `main program' equivalent, creating the windows and returning the
1351// main frame
1353{
1354 // JKC: ANSWER-ME: Who actually added the event loop guarantor?
1355 // Although 'blame' says Leland, I think it came from a donated patch.
1356
1357 // PRL: It was added by LL at 54676a72285ba7ee3a69920e91fa390a71ef10c9 :
1358 // " Ensure OnInit() has an event loop
1359 // And allow events to flow so the splash window updates under GTK"
1360 // then mistakenly lost in the merge at
1361 // 37168ebbf67ae869ab71a3b5cbbf1d2a48e824aa
1362 // then restored at 7687972aa4b2199f0717165235f3ef68ade71e08
1363
1364 // Ensure we have an event loop during initialization
1365 wxEventLoopGuarantor eventLoop;
1366
1367 OnInit0();
1368
1370
1371 // Define languages for which we have translations, but that are not yet
1372 // supported by wxWidgets.
1373 //
1374 // TODO: The whole Language initialization really need to be reworked.
1375 // It's all over the place.
1376#if wxCHECK_VERSION(3, 0, 1) && !wxCHECK_VERSION(3, 1, 6)
1377 for (size_t i = 0, cnt = WXSIZEOF(userLangs); i < cnt; i++)
1378 {
1379 wxLocale::AddLanguage(userLangs[i]);
1380 }
1381#endif
1382
1383 // Initialize preferences and language
1384 {
1387 }
1388
1390
1391 {
1392 wxBusyCursor busy;
1394 }
1395
1396 // AColor depends on theTheme.
1397 AColor::Init();
1398
1399 // If this fails, we must exit the program.
1400 if (!InitTempDir()) {
1402 return false;
1403 }
1404
1406
1407#ifdef __WXMAC__
1408 // Bug2437: When files are opened from Finder and another instance of
1409 // Audacity is running, we must return from OnInit() to wxWidgets before
1410 // MacOpenFile is called, informing us of the paths that need to be
1411 // opened. So use CallAfter() to delay the rest of initialization.
1412 // See CreateSingleInstanceChecker() where we send those paths over a
1413 // socket to the prior instance.
1414
1415 // This call is what probably makes the sleep unnecessary:
1417
1418 using namespace std::chrono;
1419 // This sleep may be unnecessary, but it is harmless. It less NS framework
1420 // events arrive on another thread, but it might not always be long enough.
1421 std::this_thread::sleep_for(100ms);
1422 CallAfter([this]{
1423 if (!InitPart2())
1424 exit(-1);
1425 });
1426 return true;
1427#else
1428 return InitPart2();
1429#endif
1430}
1431
1433{
1434#if defined(__WXMAC__)
1435 SetExitOnFrameDelete(false);
1436#endif
1437
1438 // Make sure the temp dir isn't locked by another process.
1439 {
1440 auto key =
1441 PreferenceKey(FileNames::Operation::Temp, FileNames::PathType::_None);
1442 auto temp = gPrefs->Read(key);
1443 if (temp.empty() || !CreateSingleInstanceChecker(temp)) {
1445 return false;
1446 }
1447 }
1448
1449 //<<<< Try to avoid dialogs before this point.
1450 // The reason is that InitTempDir starts the single instance checker.
1451 // If we're waiitng in a dialog before then we can very easily
1452 // start multiple instances, defeating the single instance checker.
1453
1454 // Initialize the CommandHandler
1456
1457 // Initialize the ModuleManager, including loading found modules
1459
1460 // Initialize the PluginManager
1461 PluginManager::Get().Initialize( [](const FilePath &localFileName){
1462 return std::make_unique<SettingsWX>(
1463 AudacityFileConfig::Create({}, {}, localFileName)
1464 );
1465 });
1466
1467 // Parse command line and handle options that might require
1468 // immediate exit...no need to initialize all of the audio
1469 // stuff to display the version string.
1470 std::shared_ptr< wxCmdLineParser > parser{ ParseCommandLine() };
1471 if (!parser)
1472 {
1473 // Either user requested help or a parsing error occurred
1474 exit(1);
1475 }
1476
1477 wxString journalFileName;
1478 const bool playingJournal = parser->Found("j", &journalFileName);
1479
1480#if defined(__WXMSW__) && !defined(__WXUNIVERSAL__) && !defined(__CYGWIN__)
1481 if (!playingJournal)
1482 this->AssociateFileTypes();
1483#endif
1484
1485 if (parser->Found(wxT("v")))
1486 {
1487 wxPrintf("Audacity v%s\n", AUDACITY_VERSION_STRING);
1488 exit(0);
1489 }
1490
1491 long lval;
1492 if (parser->Found(wxT("b"), &lval))
1493 {
1494 if (lval < 256 || lval > 100000000)
1495 {
1496 wxPrintf(_("Block size must be within 256 to 100000000\n"));
1497 exit(1);
1498 }
1499
1501 }
1502
1503 if (playingJournal)
1504 Journal::SetInputFileName( journalFileName );
1505
1506 // BG: Create a temporary window to set as the top window
1507 wxImage logoimage((const char **)Audacity_splash_xpm);
1508 logoimage.Scale(logoimage.GetWidth() * (2.0/3.0), logoimage.GetHeight() * (2.0/3.0), wxIMAGE_QUALITY_HIGH);
1509 if( GetLayoutDirection() == wxLayout_RightToLeft)
1510 logoimage = logoimage.Mirror();
1511 wxBitmap logo(logoimage);
1512
1514 {
1515 // Bug 718: Position splash screen on same screen
1516 // as where Audacity project will appear.
1517 wxRect wndRect;
1518 bool bMaximized = false;
1519 bool bIconized = false;
1520 GetNextWindowPlacement(&wndRect, &bMaximized, &bIconized);
1521
1522 wxSplashScreen temporarywindow(
1523 logo,
1524 wxSPLASH_CENTRE_ON_SCREEN | wxSPLASH_NO_TIMEOUT,
1525 0,
1526 NULL,
1527 wxID_ANY,
1528 wndRect.GetTopLeft(),
1529 wxDefaultSize,
1530 wxSTAY_ON_TOP);
1531
1532 // Unfortunately with the Windows 10 Creators update, the splash screen
1533 // now appears before setting its position.
1534 // On a dual monitor screen it will appear on one screen and then
1535 // possibly jump to the second.
1536 // We could fix this by writing our own splash screen and using Hide()
1537 // until the splash scren was correctly positioned, then Show()
1538
1539 // Possibly move it on to the second screen...
1540 temporarywindow.SetPosition( wndRect.GetTopLeft() );
1541 // Centered on whichever screen it is on.
1542 temporarywindow.Center();
1543 temporarywindow.SetTitle(_("Audacity is starting up..."));
1544 SetTopWindow(&temporarywindow);
1545 temporarywindow.Raise();
1546
1547 // ANSWER-ME: Why is YieldFor needed at all?
1548 //wxEventLoopBase::GetActive()->YieldFor(wxEVT_CATEGORY_UI|wxEVT_CATEGORY_USER_INPUT|wxEVT_CATEGORY_UNKNOWN);
1549 wxEventLoopBase::GetActive()->YieldFor(wxEVT_CATEGORY_UI);
1550
1551 //JKC: Would like to put module loading here.
1552
1553 // More initialization
1554
1555 InitDitherers();
1556 AudioIO::Init();
1557
1558#ifdef __WXMAC__
1559
1560 // On the Mac, users don't expect a program to quit when you close the last window.
1561 // Create a menubar that will show when all project windows are closed.
1562
1563 auto fileMenu = std::make_unique<wxMenu>();
1564 auto urecentMenu = std::make_unique<wxMenu>();
1565 auto recentMenu = urecentMenu.get();
1566 fileMenu->Append(wxID_NEW, wxString(_("&New")) + wxT("\tCtrl+N"));
1567 fileMenu->Append(wxID_OPEN, wxString(_("&Open...")) + wxT("\tCtrl+O"));
1568 fileMenu->AppendSubMenu(urecentMenu.release(), _("Open &Recent..."));
1569 fileMenu->Append(wxID_ABOUT, _("&About Audacity..."));
1570 fileMenu->Append(wxID_PREFERENCES, wxString(_("&Preferences...")) + wxT("\tCtrl+,"));
1571
1572 {
1573 auto menuBar = std::make_unique<wxMenuBar>();
1574 menuBar->Append(fileMenu.release(), _("&File"));
1575
1576 // PRL: Are we sure wxWindows will not leak this menuBar?
1577 // The online documentation is not explicit.
1578 wxMenuBar::MacSetCommonMenuBar(menuBar.release());
1579 }
1580
1581 auto &recentFiles = FileHistory::Global();
1582 recentFiles.UseMenu(recentMenu);
1583
1584#endif //__WXMAC__
1585 temporarywindow.Show(false);
1586 }
1587
1588 //Search for the new plugins
1589 std::vector<wxString> failedPlugins;
1590 if(!playingJournal && !SkipEffectsScanAtStartup.Read())
1591 {
1592 auto newPlugins = PluginManager::Get().CheckPluginUpdates();
1593 if(!newPlugins.empty())
1594 {
1595 PluginStartupRegistration reg(newPlugins);
1596 reg.Run();
1597 failedPlugins = reg.GetFailedPluginsPaths();
1598 }
1599 }
1600
1601 // Must do this before creating the first project, else the early exit path
1602 // may crash
1604 return false;
1605
1606 // Workaround Bug 1377 - Crash after Audacity starts and low disk space warning appears
1607 // The temporary splash window is closed AND cleaned up, before attempting to create
1608 // a project and possibly creating a modal warning dialog by doing so.
1609 // Also fixes problem of warning being obscured.
1610 // Downside is that we have no splash screen for the (brief) time that we spend
1611 // creating the project.
1612 // Root cause is problem with wxSplashScreen and other dialogs co-existing, that
1613 // seemed to arrive with wx3.
1614 {
1616 }
1617
1618 if (!playingJournal && ProjectSettings::Get(*project).GetShowSplashScreen())
1619 {
1620 // This may do a check-for-updates at every start up.
1621 // Mainly this is to tell users of ALPHAS who don't know that they have an ALPHA.
1622 // Disabled for now, after discussion.
1623 // project->MayCheckForUpdates();
1624#ifdef HAS_WHATS_NEW
1626#endif
1627 }
1628
1629#if defined(HAVE_UPDATES_CHECK)
1630 UpdateManager::Start(playingJournal);
1631#endif
1632
1635
1636 // Bug1561: delay the recovery dialog, to avoid crashes.
1637 CallAfter( [=] () mutable {
1638 // Remove duplicate shortcuts when there's a change of version
1639 int vMajorInit, vMinorInit, vMicroInit;
1640 GetPreferencesVersion(vMajorInit, vMinorInit, vMicroInit);
1641 if (vMajorInit != AUDACITY_VERSION || vMinorInit != AUDACITY_RELEASE
1642 || vMicroInit != AUDACITY_REVISION) {
1644 }
1645 //
1646 // Auto-recovery
1647 //
1648 bool didRecoverAnything = false;
1649 // This call may reassign project (passed by reference)
1650 if (!playingJournal)
1651 {
1652 if (!ShowAutoRecoveryDialogIfNeeded(project, &didRecoverAnything))
1653 {
1654 QuitAudacity(true);
1655 }
1656 }
1657
1658 //
1659 // Remainder of command line parsing, but only if we didn't recover
1660 //
1661 if (project && !didRecoverAnything)
1662 {
1663 if (parser->Found(wxT("t")))
1664 {
1665 RunBenchmark( nullptr, *project);
1666 QuitAudacity(true);
1667 }
1668
1669 for (size_t i = 0, cnt = parser->GetParamCount(); i < cnt; i++)
1670 {
1671 // PRL: Catch any exceptions, don't try this file again, continue to
1672 // other files.
1673 SafeMRUOpen(parser->GetParam(i));
1674 }
1675
1676 if(!failedPlugins.empty())
1677 {
1678 auto dialog = safenew IncompatiblePluginsDialog(GetTopWindow(), wxID_ANY, ScanType::Startup, failedPlugins);
1679 dialog->Bind(wxEVT_CLOSE_WINDOW, [dialog](wxCloseEvent&) { dialog->Destroy(); });
1680 dialog->Show();
1681 }
1682 }
1683 } );
1684
1685 gInited = true;
1686
1688
1689 mTimer.SetOwner(this, kAudacityAppTimerID);
1690 mTimer.Start(200);
1691
1692#ifdef EXPERIMENTAL_EASY_CHANGE_KEY_BINDINGS
1694 [](const CommandID &id){
1695 if (::wxGetMouseState().ShiftDown()) {
1696 // Only want one page of the preferences
1697 PrefsPanel::Factories factories;
1698 factories.push_back(KeyConfigPrefsFactory( id ));
1699 const auto pProject = GetActiveProject().lock();
1700 auto pWindow = FindProjectFrame( pProject.get() );
1701 // pProject may be null
1702 GlobalPrefsDialog dialog( pWindow, pProject.get(), factories );
1703 dialog.ShowModal();
1705 return true;
1706 }
1707 else
1708 return false;
1709 } };
1710#endif
1711
1712#if defined(__WXMAC__)
1713 // The first time this version of Audacity is run or when the preferences
1714 // are reset, execute the "tccutil" command to reset the microphone permissions
1715 // currently assigned to Audacity. The end result is that the user will be
1716 // prompted to approve/deny Audacity access (again).
1717 //
1718 // This should resolve confusion of why Audacity appears to record, but only
1719 // gets silence due to Audacity being denied microphone access previously.
1720 bool permsReset = false;
1721 gPrefs->Read(wxT("/MicrophonePermissionsReset"), &permsReset, false);
1722 if (!permsReset) {
1723 system("tccutil reset Microphone org.audacityteam.audacity");
1724 gPrefs->Write(wxT("/MicrophonePermissionsReset"), true);
1725 }
1726#endif
1727
1728#if defined(__WXMAC__)
1729 // Bug 2709: Workaround CoreSVG locale issue
1730 Bind(wxEVT_MENU_OPEN, [=](wxMenuEvent &event)
1731 {
1732 wxSetlocale(LC_NUMERIC, wxString(wxT("C")));
1733 event.Skip();
1734 });
1735
1736 Bind(wxEVT_MENU_CLOSE, [=](wxMenuEvent &event)
1737 {
1738 wxSetlocale(LC_NUMERIC, Languages::GetLocaleName());
1739 event.Skip();
1740 });
1741#endif
1742
1743#ifdef HAS_CUSTOM_URL_HANDLING
1744 // Schemes are case insensitive as per RFC: https://www.rfc-editor.org/rfc/rfc3986#section-3.1
1745 URLSchemesRegistry::Get().RegisterScheme(AUDACITY_NAME);
1746
1747 wxString url;
1748 if (parser->Found("u", &url))
1749 {
1750 auto utf8Url = url.ToUTF8();
1751 URLSchemesRegistry::Get().HandleURL({ utf8Url.data(), utf8Url.length() });
1752 }
1753#endif
1754
1756
1757 return TRUE;
1758}
1759
1761{
1762 // Returns 0 to the command line if the run completed normally
1763 auto result = wxApp::OnRun();
1764 if (result == 0)
1765 // If not otherwise abnormal, report any journal sync failure
1766 result = Journal::GetExitCode();
1767 return result;
1768}
1769
1770void AudacityApp::OnIdle( wxIdleEvent &evt )
1771{
1772 evt.Skip();
1773 try {
1774 HandleAppIdle();
1775
1776 if ( Journal::Dispatch() )
1777 evt.RequestMore();
1778 }
1779 catch( ... ) {
1780 // Hmm, wxWidgets doesn't guard calls to the idle handler as for other
1781 // events. So replicate some of the try-catch logic here.
1783 // Fall through and return, allowing delayed handler action of
1784 // AudacityException to clean up
1785 }
1786}
1787
1789{
1790 mCmdHandler = std::make_unique<CommandHandler>();
1791 //SetNextHandler(mCmdHandler);
1792}
1793
1794// AppCommandEvent callback - just pass the event on to the CommandHandler
1796{
1797 wxASSERT(NULL != mCmdHandler);
1798 mCmdHandler->OnReceiveCommand(event);
1799}
1800
1801void AudacityApp::OnKeyDown(wxKeyEvent &event)
1802{
1803 if(event.GetKeyCode() == WXK_ESCAPE) {
1804 // Stop play, including scrub, but not record
1805 if ( auto project = ::GetActiveProject().lock() ) {
1806 auto token = ProjectAudioIO::Get( *project ).GetAudioIOToken();
1807 auto &scrubber = Scrubber::Get( *project );
1808 auto scrubbing = scrubber.HasMark();
1809 if (scrubbing)
1810 scrubber.Cancel();
1811 auto gAudioIO = AudioIO::Get();
1812 if((token > 0 &&
1813 gAudioIO->IsAudioTokenActive(token) &&
1814 gAudioIO->GetNumCaptureChannels() == 0) ||
1815 scrubbing)
1816 // ESC out of other play (but not record)
1817 ProjectAudioManager::Get( *project ).Stop();
1818 else
1819 event.Skip();
1820 }
1821 }
1822
1823 event.Skip();
1824}
1825
1826// Ensures directory is created and puts the name into result.
1827// result is unchanged if unsuccessful.
1828void SetToExtantDirectory( wxString & result, const wxString & dir ){
1829 // don't allow path of "".
1830 if( dir.empty() )
1831 return;
1832 if( wxDirExists( dir ) ){
1833 result = dir;
1834 return;
1835 }
1836 // Use '/' so that this works on Mac and Windows alike.
1837 wxFileName name( dir + "/junkname.cfg" );
1838 if( name.Mkdir( wxS_DIR_DEFAULT , wxPATH_MKDIR_FULL ) )
1839 result = dir;
1840}
1841
1843{
1844 // We need to find a temp directory location.
1845 auto tempFromPrefs = TempDirectory::TempDir();
1846 auto tempDefaultLoc = TempDirectory::DefaultTempDir();
1847
1848 wxString temp;
1849
1850 #ifdef __WXGTK__
1851 if (tempFromPrefs.length() > 0 && tempFromPrefs[0] != wxT('/'))
1852 tempFromPrefs = wxT("");
1853 #endif
1854
1855 // Stop wxWidgets from printing its own error messages
1856
1857 wxLogNull logNo;
1858
1859 // Try temp dir that was stored in prefs first
1860 if( TempDirectory::IsTempDirectoryNameOK( tempFromPrefs ) )
1861 SetToExtantDirectory( temp, tempFromPrefs );
1862
1863 // If that didn't work, try the default location
1864
1865 if (temp.empty())
1866 SetToExtantDirectory( temp, tempDefaultLoc );
1867
1868 // Check temp directory ownership on *nix systems only
1869 #ifdef __UNIX__
1870 struct stat tempStatBuf;
1871 if ( lstat(temp.mb_str(), &tempStatBuf) != 0 ) {
1872 temp.clear();
1873 }
1874 else {
1875 if ( geteuid() != tempStatBuf.st_uid ) {
1876 temp.clear();
1877 }
1878 }
1879 #endif
1880
1881 if (temp.empty()) {
1882 // Failed
1883 if( !TempDirectory::IsTempDirectoryNameOK( tempFromPrefs ) ) {
1885"Audacity could not find a safe place to store temporary files.\nAudacity needs a place where automatic cleanup programs won't delete the temporary files.\nPlease enter an appropriate directory in the preferences dialog."));
1886 } else {
1888"Audacity could not find a place to store temporary files.\nPlease enter an appropriate directory in the preferences dialog."));
1889 }
1890
1891 // Only want one page of the preferences
1892 PrefsPanel::Factories factories;
1893 factories.push_back(DirectoriesPrefsFactory());
1894 GlobalPrefsDialog dialog(nullptr, nullptr, factories);
1895 dialog.ShowModal();
1896
1898"Audacity is now going to exit. Please launch Audacity again to use the new temporary directory."));
1899 return false;
1900 }
1901
1902 // The permissions don't always seem to be set on
1903 // some platforms. Hopefully this fixes it...
1904 #ifdef __UNIX__
1905 chmod(OSFILENAME(temp), 0700);
1906 #endif
1907
1909 FileNames::UpdateDefaultPath(FileNames::Operation::Temp, temp);
1910
1911 return true;
1912}
1913
1914#if defined(__WXMSW__)
1915
1916// Return true if there are no other instances of Audacity running,
1917// false otherwise.
1918
1920{
1921 wxString name = wxString::Format(wxT("audacity-lock-%s"), wxGetUserId());
1922 mChecker.reset();
1923 auto checker = std::make_unique<wxSingleInstanceChecker>();
1924
1925 auto runningTwoCopiesStr = XO("Running two copies of Audacity simultaneously may cause\ndata loss or cause your system to crash.\n\n");
1926
1927 if (!checker->Create(name, dir))
1928 {
1929 // Error initializing the wxSingleInstanceChecker. We don't know
1930 // whether there is another instance running or not.
1931
1932 auto prompt = XO(
1933"Audacity was not able to lock the temporary files directory.\nThis folder may be in use by another copy of Audacity.\n")
1934 + runningTwoCopiesStr
1935 + XO("Do you still want to start Audacity?");
1936 int action = AudacityMessageBox(
1937 prompt,
1938 XO("Error Locking Temporary Folder"),
1939 wxYES_NO | wxICON_EXCLAMATION, NULL);
1940 if (action == wxNO)
1941 return false;
1942 }
1943 else if ( checker->IsAnotherRunning() ) {
1944 // Parse the command line to ensure correct syntax, but
1945 // ignore options other than -v, and only use the filenames, if any.
1946 auto parser = ParseCommandLine();
1947 if (!parser)
1948 {
1949 // Complaints have already been made
1950 return false;
1951 }
1952
1953 if (parser->Found(wxT("v")))
1954 {
1955 wxPrintf("Audacity v%s\n", AUDACITY_VERSION_STRING);
1956 return false;
1957 }
1958
1959 // Windows and Linux require absolute file names as command may
1960 // not come from current working directory.
1961 FilePaths filenames;
1962 for (size_t i = 0, cnt = parser->GetParamCount(); i < cnt; i++)
1963 {
1964 wxFileName filename(parser->GetParam(i));
1965 if (filename.MakeAbsolute())
1966 {
1967 filenames.push_back(filename.GetLongPath());
1968 }
1969 }
1970
1971 #ifdef HAS_CUSTOM_URL_HANDLING
1972 wxString url;
1973 parser->Found("u", &url);
1974 #endif
1975
1976
1977 // On Windows, we attempt to make a connection
1978 // to an already active Audacity. If successful, we send
1979 // the first command line argument (the audio file name)
1980 // to that Audacity for processing.
1981 wxClient client;
1982
1983 // We try up to 50 times since there's a small window
1984 // where the server may not have been fully initialized.
1985 for (int i = 0; i < 50; i++)
1986 {
1987 std::unique_ptr<wxConnectionBase> conn{ client.MakeConnection(wxEmptyString, IPC_APPL, IPC_TOPIC) };
1988 if (conn)
1989 {
1990 bool ok = false;
1991 #ifdef HAS_CUSTOM_URL_HANDLING
1992 if (!url.empty())
1993 {
1994 if (!conn->Execute(urlPrefix + url))
1995 return false;
1996 }
1997 #endif
1998
1999 if (filenames.size() > 0)
2000 {
2001 for (size_t i = 0, cnt = filenames.size(); i < cnt; i++)
2002 {
2003 ok = conn->Execute(filenames[i]);
2004 }
2005 }
2006 else
2007 {
2008 // Send an empty string to force existing Audacity to front
2009 ok = conn->Execute(wxEmptyString);
2010 }
2011
2012 if (ok)
2013 return false;
2014 }
2015
2016 using namespace std::chrono;
2017 std::this_thread::sleep_for(10ms);
2018 }
2019 // There is another copy of Audacity running. Force quit.
2020
2021 auto prompt = XO(
2022"The system has detected that another copy of Audacity is running.\n")
2023 + runningTwoCopiesStr
2024 + XO(
2025"Use the New or Open commands in the currently running Audacity\nprocess to open multiple projects simultaneously.\n");
2027 prompt, XO("Audacity is already running"),
2028 wxOK | wxICON_ERROR);
2029
2030 return false;
2031 }
2032
2033 // Create the DDE IPC server
2034 mIPCServ = std::make_unique<IPCServ>(IPC_APPL);
2035 mChecker = std::move(checker);
2036 return true;
2037}
2038#endif
2039
2040#if defined(__UNIX__)
2041
2042#include <sys/ipc.h>
2043#include <sys/sem.h>
2044#include <sys/shm.h>
2045
2046// Return true if there are no other instances of Audacity running,
2047// false otherwise.
2048
2049bool AudacityApp::CreateSingleInstanceChecker(const wxString &dir)
2050{
2051 mIPCServ.reset();
2052
2053 bool isServer = false;
2054 wxIPV4address addr;
2055 addr.LocalHost();
2056
2057 struct sembuf op = {};
2058
2059 // Generate the IPC key we'll use for both shared memory and semaphores.
2060 wxString datadir = FileNames::DataDir();
2061 key_t memkey = ftok(datadir.c_str(), 0);
2062 key_t servkey = ftok(datadir.c_str(), 1);
2063 key_t lockkey = ftok(datadir.c_str(), 2);
2064
2065 // Create and map the shared memory segment where the port number
2066 // will be stored.
2067 int memid = shmget(memkey, sizeof(int), IPC_CREAT | S_IRUSR | S_IWUSR);
2068 if (memid == -1)
2069 {
2071 XO("Unable to create shared memory segment.\n\n"
2072 "error code=%d : \"%s\".").Format(errno, strerror(errno)),
2073 XO("Audacity Startup Failure"),
2074 wxOK | wxICON_ERROR);
2075
2076 return false;
2077 }
2078
2079 int *portnum = (int *) shmat(memid, nullptr, 0);
2080
2081 // Create (or return) the SERVER semaphore ID
2082 int servid = semget(servkey, 1, IPC_CREAT | S_IRUSR | S_IWUSR);
2083
2084 // Create the LOCK semaphore only if it doesn't already exist.
2085 int lockid = semget(lockkey, 1, IPC_CREAT | IPC_EXCL | S_IRUSR | S_IWUSR);
2086
2087 // If the LOCK semaphore was successfully created, then this is the first
2088 // time Audacity has been run during this boot of the system. In this
2089 // case we know we'll become the "server" application, so set up the
2090 // semaphores to prepare for it.
2091 if (lockid != -1)
2092 {
2093 // Initialize value of each semaphore, 1 indicates released and 0
2094 // indicates acquired.
2095 //
2096 // Note that this action is NOT recorded in the semaphore's
2097 // UNDO buffer.
2098 semctl(servid, 0, SETVAL, 1);
2099 semctl(lockid, 0, SETVAL, 1);
2100
2101 // Now acquire them so the semaphores will be set to the
2102 // released state when the process terminates.
2103 op.sem_num = 0;
2104 op.sem_op = -1;
2105 op.sem_flg = SEM_UNDO;
2106 if (semop(lockid, &op, 1) == -1 || semop(servid, &op, 1) == -1)
2107 {
2109 XO("Unable to acquire semaphores.\n\n"
2110 "This is likely due to a resource shortage\n"
2111 "and a reboot may be required."),
2112 XO("Audacity Startup Failure"),
2113 wxOK | wxICON_ERROR);
2114
2115 return false;
2116 }
2117
2118 // We will be the server...
2119 isServer = true;
2120 }
2121 // Something catastrophic must have happened, so bail.
2122 else if (errno != EEXIST)
2123 {
2125 XO("Unable to create semaphores.\n\n"
2126 "This is likely due to a resource shortage\n"
2127 "and a reboot may be required."),
2128 XO("Audacity Startup Failure"),
2129 wxOK | wxICON_ERROR);
2130
2131 return false;
2132 }
2133 // Otherwise it's a normal startup and we need to determine whether
2134 // we'll be the server or the client.
2135 else
2136 {
2137 // Retrieve the LOCK semaphore since we wouldn't have gotten it above.
2138 lockid = semget(lockkey, 1, 0);
2139
2140 // Acquire the LOCK semaphore. We may block here if another
2141 // process is currently setting up the server.
2142 op.sem_num = 0;
2143 op.sem_op = -1;
2144 op.sem_flg = SEM_UNDO;
2145 if (semop(lockid, &op, 1) == -1)
2146 {
2148 XO("Unable to acquire lock semaphore.\n\n"
2149 "This is likely due to a resource shortage\n"
2150 "and a reboot may be required."),
2151 XO("Audacity Startup Failure"),
2152 wxOK | wxICON_ERROR);
2153
2154 return false;
2155 }
2156
2157 // Try to acquire the SERVER semaphore. If it's not currently active, then
2158 // we will become the server. Otherwise, this will fail and we'll know that
2159 // the server is already active and we will become the client.
2160 op.sem_num = 0;
2161 op.sem_op = -1;
2162 op.sem_flg = IPC_NOWAIT | SEM_UNDO;
2163 if (semop(servid, &op, 1) == 0)
2164 {
2165 isServer = true;
2166 }
2167 else if (errno != EAGAIN)
2168 {
2170 XO("Unable to acquire server semaphore.\n\n"
2171 "This is likely due to a resource shortage\n"
2172 "and a reboot may be required."),
2173 XO("Audacity Startup Failure"),
2174 wxOK | wxICON_ERROR);
2175
2176 return false;
2177 }
2178 }
2179
2180 // Initialize the socket server if we're to be the server.
2181 if (isServer)
2182 {
2183 // The system will randomly assign a port
2184 addr.Service(0);
2185
2186 // Create the socket and bind to it.
2187 auto serv = std::make_unique<wxSocketServer>(addr, wxSOCKET_NOWAIT);
2188 if (serv && serv->IsOk())
2189 {
2190 serv->SetEventHandler(*this, ID_IPC_SERVER);
2191 serv->SetNotify(wxSOCKET_CONNECTION_FLAG);
2192 serv->Notify(true);
2193 mIPCServ = std::move(serv);
2194
2195 // Save the port number in shared memory so that clients
2196 // know where to connect.
2197 mIPCServ->GetLocal(addr);
2198 *portnum = addr.Service();
2199 }
2200
2201 // Now that the server is active, we release the LOCK semaphore
2202 // to allow any waiters to continue. The SERVER semaphore will
2203 // remain locked for the duration of this processes execution
2204 // and will be cleaned up by the system.
2205 op.sem_num = 0;
2206 op.sem_op = 1;
2207 semop(lockid, &op, 1);
2208
2209 // Bail if the server creation failed.
2210 if (mIPCServ == nullptr)
2211 {
2213 XO("The Audacity IPC server failed to initialize.\n\n"
2214 "This is likely due to a resource shortage\n"
2215 "and a reboot may be required."),
2216 XO("Audacity Startup Failure"),
2217 wxOK | wxICON_ERROR);
2218
2219 return false;
2220 }
2221
2222 // We've successfully created the socket server and the app
2223 // should continue to initialize.
2224 return true;
2225 }
2226
2227 // Retrieve the port number that the server is listening on.
2228 addr.Service(*portnum);
2229
2230 // Now release the LOCK semaphore.
2231 op.sem_num = 0;
2232 op.sem_op = 1;
2233 semop(lockid, &op, 1);
2234
2235 // If we get here, then Audacity is currently active. So, we connect
2236 // to it and we forward all filenames listed on the command line to
2237 // the active process.
2238
2239 // Setup the socket
2240 //
2241 // A wxSocketClient must not be deleted by us, but rather, let the
2242 // framework do appropriate delayed deletion after Destroy()
2243 Destroy_ptr<wxSocketClient> sock { safenew wxSocketClient() };
2244 sock->SetFlags(wxSOCKET_WAITALL);
2245
2246 // Attempt to connect to an active Audacity.
2247 sock->Connect(addr, true);
2248 if (!sock->IsConnected())
2249 {
2250 // All attempts to become the server or connect to one have failed. Not
2251 // sure what we can say about the error, but it's probably not because
2252 // Audacity is already running.
2254 XO("An unrecoverable error has occurred during startup"),
2255 XO("Audacity Startup Failure"),
2256 wxOK | wxICON_ERROR);
2257
2258 return false;
2259 }
2260
2261 // Parse the command line to ensure correct syntax, but ignore
2262 // options other than -v, and only use the filenames, if any.
2263 auto parser = ParseCommandLine();
2264 if (!parser)
2265 {
2266 // Complaints have already been made
2267 return false;
2268 }
2269
2270 // Display Audacity's version if requested
2271 if (parser->Found(wxT("v")))
2272 {
2273 wxPrintf("Audacity v%s\n", AUDACITY_VERSION_STRING);
2274
2275 return false;
2276 }
2277
2278#ifdef HAS_CUSTOM_URL_HANDLING
2279 wxString url;
2280
2281 if (parser->Found(wxT("u"), &url))
2282 {
2283 if (!url.empty())
2284 {
2285 url = urlPrefix + url;
2286 auto str = url.c_str().AsWChar();
2287
2288 sock->WriteMsg(str, (url.length() + 1) * sizeof(*str));
2289 }
2290 }
2291#endif
2292
2293#if defined(__WXMAC__)
2294 // On macOS the client gets events from the wxWidgets framework that
2295 // go to AudacityApp::MacOpenFile. Forward the file names to the prior
2296 // instance via the socket.
2297 for (const auto &filename: ofqueue)
2298 {
2299 auto str = filename.c_str().AsWChar();
2300 sock->WriteMsg(str, (filename.length() + 1) * sizeof(*str));
2301 }
2302#endif
2303
2304 // On macOS and Linux, forward any file names found in the command
2305 // line arguments.
2306 for (size_t j = 0, cnt = parser->GetParamCount(); j < cnt; ++j)
2307 {
2308 wxFileName filename(parser->GetParam(j));
2309 if (filename.MakeAbsolute())
2310 {
2311 const wxString param = filename.GetLongPath();
2312 sock->WriteMsg((const wxChar *) param, (param.length() + 1) * sizeof(wxChar));
2313 }
2314 }
2315
2316 // Send an empty string to force existing Audacity to front
2317 sock->WriteMsg(wxEmptyString, sizeof(wxChar));
2318
2319 // We've forwarded all of the filenames, so let the caller know
2320 // to terminate.
2321 return false;
2322}
2323
2324void AudacityApp::OnServerEvent(wxSocketEvent & /* evt */)
2325{
2326 wxSocketBase *sock;
2327
2328 // Accept all pending connection requests
2329 do
2330 {
2331 sock = mIPCServ->Accept(false);
2332 if (sock)
2333 {
2334 // Setup the socket
2335 sock->SetEventHandler(*this, ID_IPC_SOCKET);
2336 sock->SetNotify(wxSOCKET_INPUT_FLAG | wxSOCKET_LOST_FLAG);
2337 sock->Notify(true);
2338 }
2339 } while (sock);
2340}
2341
2342void AudacityApp::OnSocketEvent(wxSocketEvent & evt)
2343{
2344 wxSocketBase *sock = evt.GetSocket();
2345
2346 if (evt.GetSocketEvent() == wxSOCKET_LOST)
2347 {
2348 sock->Destroy();
2349 return;
2350 }
2351
2352 // Read the length of the filename and bail if we have a short read
2353 wxChar name[PATH_MAX];
2354 sock->ReadMsg(&name, sizeof(name));
2355 if (!sock->Error())
2356 {
2357 // Add the filename to the queue. It will be opened by
2358 // the OnTimer() event when it is safe to do so.
2359 ofqueue.push_back(name);
2360 }
2361}
2362
2363#endif
2364
2365std::unique_ptr<wxCmdLineParser> AudacityApp::ParseCommandLine()
2366{
2367 auto parser = std::make_unique<wxCmdLineParser>(argc, argv);
2368 if (!parser)
2369 {
2370 return nullptr;
2371 }
2372
2373 /*i18n-hint: This controls the number of bytes that Audacity will
2374 * use when writing files to the disk */
2375 parser->AddOption(wxT("b"), wxT("blocksize"), _("set max disk block size in bytes"),
2376 wxCMD_LINE_VAL_NUMBER);
2377
2378 const auto journalOptionDescription =
2379 /*i18n-hint: brief help message for Audacity's command-line options
2380 A journal contains a sequence of user interface interactions to be repeated
2381 "log," "trail," "trace" have somewhat similar meanings */
2382 _("replay a journal file");
2383
2384 parser->AddOption(wxT("j"), wxT("journal"), journalOptionDescription);
2385
2386 /*i18n-hint: This displays a list of available options */
2387 parser->AddSwitch(wxT("h"), wxT("help"), _("this help message"),
2388 wxCMD_LINE_OPTION_HELP);
2389
2390 /*i18n-hint: This runs a set of automatic tests on Audacity itself */
2391 parser->AddSwitch(wxT("t"), wxT("test"), _("run self diagnostics"));
2392
2393 /*i18n-hint: This displays the Audacity version */
2394 parser->AddSwitch(wxT("v"), wxT("version"), _("display Audacity version"));
2395
2396 /*i18n-hint: This is a list of one or more files that Audacity
2397 * should open upon startup */
2398 parser->AddParam(_("audio or project file name"),
2399 wxCMD_LINE_VAL_STRING,
2400 wxCMD_LINE_PARAM_MULTIPLE | wxCMD_LINE_PARAM_OPTIONAL);
2401
2402#ifdef HAS_CUSTOM_URL_HANDLING
2403 /* i18n-hint: This option is used to handle custom URLs in Audacity */
2404 parser->AddOption(wxT("u"), wxT("url"), _("Handle 'audacity://' url"));
2405#endif
2406
2407 // Run the parser
2408 if (parser->Parse() == 0)
2409 return parser;
2410
2411 return{};
2412}
2413
2414void AudacityApp::OnQueryEndSession(wxCloseEvent & event)
2415{
2416 bool mustVeto = false;
2417
2418#ifdef __WXMAC__
2419 mustVeto = wxDialog::OSXHasModalDialogsOpen();
2420#endif
2421
2422 if ( mustVeto )
2423 event.Veto(true);
2424 else
2425 OnEndSession(event);
2426}
2427
2428void AudacityApp::OnEndSession(wxCloseEvent & event)
2429{
2430 bool force = !event.CanVeto();
2431
2432 // Try to close each open window. If the user hits Cancel
2433 // in a Save Changes dialog, don't continue.
2434 gIsQuitting = true;
2435 if (AllProjects{}.size())
2436 // PRL: Always did at least once before close might be vetoed
2437 // though I don't know why that is important
2439 bool closedAll = CloseAllProjects( force );
2440 if ( !closedAll )
2441 {
2442 gIsQuitting = false;
2443 event.Veto();
2444 }
2445}
2446
2448{
2449 gIsQuitting = true;
2450 while(Pending())
2451 {
2452 Dispatch();
2453 }
2454
2456
2458
2459 if(gPrefs)
2460 {
2461 bool bFalse = false;
2462 //Should we change the commands.cfg location next startup?
2463 if(gPrefs->Read(wxT("/QDeleteCmdCfgLocation"), &bFalse))
2464 {
2465 gPrefs->DeleteEntry(wxT("/QDeleteCmdCfgLocation"));
2466 gPrefs->Write(wxT("/DeleteCmdCfgLocation"), true);
2467 gPrefs->Flush();
2468 }
2469 }
2470
2472
2474
2475 DeinitFFT();
2476
2477#ifdef HAS_NETWORKING
2479#endif
2480
2482
2483 // Terminate the PluginManager (must be done before deleting the locale)
2485
2486 return 0;
2487}
2488
2489// The following five methods are currently only used on Mac OS,
2490// where it's possible to have a menu bar but no windows open.
2491// It doesn't hurt any other platforms, though.
2492
2493// ...That is, as long as you check to see if no windows are open
2494// before executing the stuff.
2495// To fix this, check to see how many project windows are open,
2496// and skip the event unless none are open (which should only happen
2497// on the Mac, at least currently.)
2498
2499void AudacityApp::OnMenuAbout(wxCommandEvent & /*event*/)
2500{
2501 // This function shadows a similar function
2502 // in Menus.cpp, but should only be used on the Mac platform.
2503#ifdef __WXMAC__
2504 // Modeless dialog, consistent with other Mac applications
2505 // Not more than one at once!
2506 const auto instance = AboutDialog::ActiveIntance();
2507 if (instance)
2508 instance->Raise();
2509 else
2510 // This dialog deletes itself when dismissed
2511 (safenew AboutDialog{ nullptr })->Show(true);
2512#else
2513 wxASSERT(false);
2514#endif
2515}
2516
2517void AudacityApp::OnMenuNew(wxCommandEvent & event)
2518{
2519 // This function shadows a similar function
2520 // in Menus.cpp, but should only be used on the Mac platform
2521 // when no project windows are open. This check assures that
2522 // this happens, and enable the same code to be present on
2523 // all platforms.
2524
2525 if(AllProjects{}.empty())
2526 (void) ProjectManager::New();
2527 else
2528 event.Skip();
2529}
2530
2531
2532void AudacityApp::OnMenuOpen(wxCommandEvent & event)
2533{
2534 // This function shadows a similar function
2535 // in Menus.cpp, but should only be used on the Mac platform
2536 // when no project windows are open. This check assures that
2537 // this happens, and enable the same code to be present on
2538 // all platforms.
2539
2540
2541 if(AllProjects{}.empty())
2543 else
2544 event.Skip();
2545
2546
2547}
2548
2549void AudacityApp::OnMenuPreferences(wxCommandEvent & event)
2550{
2551 // This function shadows a similar function
2552 // in Menus.cpp, but should only be used on the Mac platform
2553 // when no project windows are open. This check assures that
2554 // this happens, and enable the same code to be present on
2555 // all platforms.
2556
2557 if(AllProjects{}.empty()) {
2558 GlobalPrefsDialog dialog(nullptr /* parent */, nullptr );
2559 dialog.ShowModal();
2560 }
2561 else
2562 event.Skip();
2563
2564}
2565
2566void AudacityApp::OnMenuExit(wxCommandEvent & event)
2567{
2568 // This function shadows a similar function
2569 // in Menus.cpp, but should only be used on the Mac platform
2570 // when no project windows are open. This check assures that
2571 // this happens, and enable the same code to be present on
2572 // all platforms.
2573
2574 // LL: Removed "if" to allow closing based on final project count.
2575 // if(AllProjects{}.empty())
2576 QuitAudacity();
2577
2578 // LL: Veto quit if projects are still open. This can happen
2579 // if the user selected Cancel in a Save dialog.
2580 event.Skip(AllProjects{}.empty());
2581
2582}
2583
2584#ifndef __WXMAC__
2586{
2587 // Currently this is implemented only on macOS
2588}
2589#endif
2590
2591//BG: On Windows, associate the aup file type with Audacity
2592/* We do this in the Windows installer now,
2593 to avoid issues where user doesn't have admin privileges, but
2594 in case that didn't work, allow the user to decide at startup.
2595
2596 //v Should encapsulate this & allow access from Prefs, too,
2597 // if people want to manually change associations.
2598*/
2599#if defined(__WXMSW__) && !defined(__WXUNIVERSAL__) && !defined(__CYGWIN__)
2601{
2602 // Check pref in case user has already decided against it.
2603 bool bWantAssociateFiles = true;
2604 if (gPrefs->Read(wxT("/WantAssociateFiles"), &bWantAssociateFiles) &&
2605 !bWantAssociateFiles)
2606 {
2607 // User has already decided against it
2608 return;
2609 }
2610
2611 wxRegKey associateFileTypes;
2612
2613 auto IsDefined = [&](const wxString &type)
2614 {
2615 associateFileTypes.SetName(wxString::Format(wxT("HKCR\\%s"), type));
2616 bool bKeyExists = associateFileTypes.Exists();
2617 if (!bKeyExists)
2618 {
2619 // Not at HKEY_CLASSES_ROOT. Try HKEY_CURRENT_USER.
2620 associateFileTypes.SetName(wxString::Format(wxT("HKCU\\Software\\Classes\\%s"), type));
2621 bKeyExists = associateFileTypes.Exists();
2622 }
2623 return bKeyExists;
2624 };
2625
2626 auto DefineType = [&](const wxString &type)
2627 {
2628 wxString root_key = wxT("HKCU\\Software\\Classes\\");
2629
2630 // Start with HKEY_CLASSES_CURRENT_USER.
2631 associateFileTypes.SetName(wxString::Format(wxT("%s%s"), root_key, type));
2632 if (!associateFileTypes.Create(true))
2633 {
2634 // Not at HKEY_CLASSES_CURRENT_USER. Try HKEY_CURRENT_ROOT.
2635 root_key = wxT("HKCR\\");
2636 associateFileTypes.SetName(wxString::Format(wxT("%s%s"), root_key, type));
2637 if (!associateFileTypes.Create(true))
2638 {
2639 // Actually, can't create keys. Empty root_key to flag failure.
2640 root_key.empty();
2641 }
2642 }
2643
2644 if (!root_key.empty())
2645 {
2646 associateFileTypes = wxT("Audacity.Project"); // Finally set value for the key
2647 }
2648
2649 return root_key;
2650 };
2651
2652 // Check for legacy and UP types
2653 if (IsDefined(wxT(".aup3")) && IsDefined(wxT(".aup")) && IsDefined(wxT("Audacity.Project")))
2654 {
2655 // Already defined, so bail
2656 return;
2657 }
2658
2659 // File types are not currently associated.
2660 int wantAssoc =
2662 XO(
2663"Audacity project (.aup3) files are not currently \nassociated with Audacity. \n\nAssociate them, so they open on double-click?"),
2664 XO("Audacity Project Files"),
2665 wxYES_NO | wxICON_QUESTION);
2666
2667 if (wantAssoc == wxNO)
2668 {
2669 // User said no. Set a pref so we don't keep asking.
2670 gPrefs->Write(wxT("/WantAssociateFiles"), false);
2671 gPrefs->Flush();
2672 return;
2673 }
2674
2675 // Show that user wants associations
2676 gPrefs->Write(wxT("/WantAssociateFiles"), true);
2677 gPrefs->Flush();
2678
2679 wxString root_key;
2680
2681 root_key = DefineType(wxT(".aup3"));
2682 if (root_key.empty())
2683 {
2684 //v Warn that we can't set keys. Ask whether to set pref for no retry?
2685 }
2686 else
2687 {
2688 DefineType(wxT(".aup"));
2689
2690 associateFileTypes = wxT("Audacity.Project"); // Finally set value for .AUP key
2691 associateFileTypes.SetName(root_key + wxT("Audacity.Project"));
2692 if (!associateFileTypes.Exists())
2693 {
2694 associateFileTypes.Create(true);
2695 associateFileTypes = wxT("Audacity Project File");
2696 }
2697
2698 associateFileTypes.SetName(root_key + wxT("Audacity.Project\\shell"));
2699 if (!associateFileTypes.Exists())
2700 {
2701 associateFileTypes.Create(true);
2702 associateFileTypes = wxT("");
2703 }
2704
2705 associateFileTypes.SetName(root_key + wxT("Audacity.Project\\shell\\open"));
2706 if (!associateFileTypes.Exists())
2707 {
2708 associateFileTypes.Create(true);
2709 }
2710
2711 associateFileTypes.SetName(root_key + wxT("Audacity.Project\\shell\\open\\command"));
2712 wxString tmpRegAudPath;
2713 if(associateFileTypes.Exists())
2714 {
2715 tmpRegAudPath = associateFileTypes.QueryDefaultValue().Lower();
2716 }
2717
2718 if (!associateFileTypes.Exists() ||
2719 (tmpRegAudPath.Find(wxT("audacity.exe")) >= 0))
2720 {
2721 associateFileTypes.Create(true);
2722 associateFileTypes = (wxString)argv[0] + (wxString)wxT(" \"%1\"");
2723 }
2724
2725#if 0
2726 // These can be use later to support more startup messages
2727 // like maybe "Import into existing project" or some such.
2728 // Leaving here for an example...
2729 associateFileTypes.SetName(root_key + wxT("Audacity.Project\\shell\\open\\ddeexec"));
2730 if (!associateFileTypes.Exists())
2731 {
2732 associateFileTypes.Create(true);
2733 associateFileTypes = wxT("%1");
2734 }
2735
2736 associateFileTypes.SetName(root_key + wxT("Audacity.Project\\shell\\open\\ddeexec\\Application"));
2737 if (!associateFileTypes.Exists())
2738 {
2739 associateFileTypes.Create(true);
2740 associateFileTypes = IPC_APPL;
2741 }
2742
2743 associateFileTypes.SetName(root_key + wxT("Audacity.Project\\shell\\open\\ddeexec\\Topic"));
2744 if (!associateFileTypes.Exists())
2745 {
2746 associateFileTypes.Create(true);
2747 associateFileTypes = IPC_TOPIC;
2748 }
2749#endif
2750 }
2751}
2752#endif
2753
2755 []{
2756 static std::once_flag configSetupFlag;
2757 std::call_once(configSetupFlag, [&]{
2758 const auto configFileName = wxFileName { FileNames::Configuration() };
2760 wxTheApp->GetAppName(), wxEmptyString,
2761 configFileName.GetFullPath(),
2762 wxEmptyString, wxCONFIG_USE_LOCAL_FILE);
2763 wxConfigBase::Set(gConfig.get());
2764 });
2765 return std::make_unique<SettingsWX>(gConfig);
2766 }
2767};
AUDACITY_DLL_API std::weak_ptr< AudacityProject > GetActiveProject()
Handle changing of active project and keep global project pointer.
EVT_MENU(OnSetPlayRegionToSelectionID, AdornedRulerPanel::OnSetPlayRegionToSelection) EVT_COMMAND(OnTogglePinnedStateID
wxImage(22, 22)
wxT("CloseDown"))
Headers and event table macros for AppCommandEvent.
#define EVT_APP_COMMAND(winid, fn)
void SetToExtantDirectory(wxString &result, const wxString &dir)
int main(int argc, char *argv[])
static bool gIsQuitting
#define WL(lang, sublang)
EVT_MENU_RANGE(FileHistory::ID_RECENT_FIRST, FileHistory::ID_RECENT_LAST, AudacityApp::OnMRUFile) bool AudacityApp
#define ID_IPC_SERVER
#define kAudacityAppTimerID
static void QuitAudacity(bool bForce)
#define ID_IPC_SOCKET
static bool CloseAllProjects(bool force)
static std::shared_ptr< wxConfigBase > gConfig
static wxArrayString ofqueue
static bool gInited
#define IPC_APPL
static audacity::ApplicationSettings::Scope applicationSettingsScope
#define IPC_TOPIC
AudacityApp & wxGetApp()
int AudacityMessageBox(const TranslatableString &message, const TranslatableString &caption, long style, wxWindow *parent, int x, int y)
bool ShowAutoRecoveryDialogIfNeeded(AudacityProject *&pproj, bool *didRecoverAnything)
END_EVENT_TABLE()
void RunBenchmark(wxWindow *parent, AudacityProject &project)
Definition: Benchmark.cpp:91
Contains declarations for the CommandHandler class.
#define str(a)
PrefsPanel::Factory DirectoriesPrefsFactory()
const TranslatableString name
Definition: Distortion.cpp:76
BoolSetting SkipEffectsScanAtStartup
void DeinitFFT()
Definition: FFT.cpp:112
#define OSFILENAME(X)
XO("Cut/Copy/Paste")
#define _(s)
Definition: Internat.h:73
PrefsPanel::Factory KeyConfigPrefsFactory(const CommandID &name)
std::unique_ptr< Character[], freer > MallocString
Definition: MemoryX.h:148
#define safenew
Definition: MemoryX.h:10
std::unique_ptr< T, Destroyer< T > > Destroy_ptr
a convenience for using Destroyer
Definition: MemoryX.h:164
const std::wstring AppName
This program's name.
@ AppInitialized
@ AppQuiting
static const AudacityProject::AttachedObjects::RegisteredFactory key
Declare a class for performing HTTP requests.
void SetPreferencesVersion(int vMajor, int vMinor, int vMicro)
Definition: Prefs.cpp:246
audacity::BasicSettings * gPrefs
Definition: Prefs.cpp:68
void FinishPreferences()
Definition: Prefs.cpp:262
void GetPreferencesVersion(int &vMajor, int &vMinor, int &vMicro)
Definition: Prefs.cpp:239
void InitPreferences(std::unique_ptr< audacity::BasicSettings > uPrefs)
Definition: Prefs.cpp:231
void ResetPreferences()
Call this to reset preferences to an (almost)-"new" default state.
Definition: Prefs.cpp:253
#define AUDACITY_PREFS_VERSION_STRING
Definition: Prefs.h:39
wxString FilePath
Definition: Project.h:21
void GetNextWindowPlacement(wxRect *nextRect, bool *pMaximized, bool *pIconized)
wxFrame * FindProjectFrame(AudacityProject *project)
Get a pointer to the window associated with a project, or null if the given pointer is null,...
AUDACITY_DLL_API wxFrame & GetProjectFrame(AudacityProject &project)
Get the top-level window associated with the project (as a wxFrame only, when you do not need to use ...
accessors for certain important windows associated with each project
void InitDitherers()
const auto project
THEME_API Theme theTheme
Definition: Theme.cpp:82
declares abstract base class Track, TrackList, and iterators over TrackList
Declare a class that handles managing of updates.
int id
static const auto fn
static void Init()
Definition: AColor.cpp:535
The AboutDialog shows the program version and developer credits.
Definition: AboutDialog.h:32
static AboutDialog * ActiveIntance()
size_t size() const
Definition: Project.cpp:17
const_iterator begin() const
Definition: Project.cpp:22
bool empty() const
Definition: Project.h:47
An event 'envelope' for sending Command objects through the wxwidgets event loop.
AudacityApp is the 'main' class for Audacity.
Definition: AudacityApp.h:41
void OnQueryEndSession(wxCloseEvent &event)
void OnMenuExit(wxCommandEvent &event)
void MacPrintFile(const wxString &fileName) override
wxTimer mTimer
Definition: AudacityApp.h:121
bool OSXIsGUIApplication() override
bool MRUOpen(const FilePath &fileName)
void OnKeyDown(wxKeyEvent &event)
void OnMenuPreferences(wxCommandEvent &event)
bool OnExceptionInMainLoop() override
void OnServerEvent(wxSocketEvent &evt)
bool InitPart2()
void OnEndSession(wxCloseEvent &event)
void MacNewFile() override
void OnTimer(wxTimerEvent &event)
bool Initialize(int &argc, wxChar **argv) override
std::unique_ptr< CommandHandler > mCmdHandler
Definition: AudacityApp.h:117
std::unique_ptr< wxSingleInstanceChecker > mChecker
Definition: AudacityApp.h:119
void MacFinishLaunching()
Observer::Subscription mThemeChangeSubscription
Definition: AudacityApp.h:115
void OnMRUFile(wxCommandEvent &event)
void AssociateFileTypes()
void OnMenuOpen(wxCommandEvent &event)
int OnRun() override
bool InitTempDir()
std::unique_ptr< wxCmdLineParser > ParseCommandLine()
void OnFatalException() override
void OnIdle(wxIdleEvent &)
void OnMRUClear(wxCommandEvent &event)
int OnExit(void) override
void OnMenuAbout(wxCommandEvent &event)
bool CreateSingleInstanceChecker(const wxString &dir)
void InitCommandHandler()
void MacOpenFile(const wxString &fileName) override
void OnMenuNew(wxCommandEvent &event)
static void OnThemeChange(struct ThemeChangeMessage)
bool SafeMRUOpen(const wxString &fileName)
void CleanUp() override
void OnReceiveCommand(AppCommandEvent &event)
std::unique_ptr< IPCServ > mIPCServ
Definition: AudacityApp.h:131
void OnSocketEvent(wxSocketEvent &evt)
bool OnInit() override
Base class for exceptions specially processed by the application.
virtual void DelayedHandlerAction()=0
Action to do in the main thread at idle time of the event loop.
static std::unique_ptr< AudacityFileConfig > Create(const wxString &appName={}, const wxString &vendorName={}, const wxString &localFilename={}, const wxString &globalFilename={}, long style=wxCONFIG_USE_LOCAL_FILE|wxCONFIG_USE_GLOBAL_FILE, const wxMBConv &conv=wxConvAuto())
Require a call to this factory, to guarantee proper two-phase initialization.
static AudacityLogger * Get()
The top-level handle to an Audacity project. It serves as a source of events that other objects can b...
Definition: Project.h:90
static void Init()
Definition: AudioIO.cpp:190
static void Deinit()
Definition: AudioIO.cpp:218
static AudioIO * Get()
Definition: AudioIO.cpp:126
This class is used to configure Breakpad's handler before start.
BreakpadConfigurer & SetSenderPathUTF8(const std::string &pathUTF8)
Sets a path to a directory where crash reporter sending program is located.
BreakpadConfigurer & SetParameters(const std::map< std::string, std::string > &parameters)
Sets an additional parameters that should be sent to a crash reporting server (ASCII encoded)
BreakpadConfigurer & SetDatabasePathUTF8(const std::string &pathUTF8)
Sets the directory where crashreports will be stored (should have rw permission)
BreakpadConfigurer & SetReportURL(const std::string &reportURL)
Sets report URL to the crash reporting server (URL-Encoded, optional)
static Clipboard & Get()
Definition: Clipboard.cpp:28
void Clear()
Definition: Clipboard.cpp:40
CrashpadConfigurer & SetMetricsDirUTF8(const std::string &metricsDir)
CrashpadConfigurer & SetDatabasePathUTF8(const std::string &database)
CrashpadConfigurer & SetHandlerPathUTF8(const std::string &handlerPath)
CrashpadConfigurer & SetArguments(const std::vector< std::string > &arguments)
static ExportPluginRegistry & Get()
Similar to wxFileHistory, but customized to our needs.
Definition: FileHistory.h:30
void Clear()
Definition: FileHistory.cpp:92
void Save(audacity::BasicSettings &config)
static FileHistory & Global()
Definition: FileHistory.cpp:39
Abstract base class used in importing a file.
static void Destroy()
Destroys the dialog to prevent Audacity from hanging on exit.
static result_type Call(Arguments &&...arguments)
Null check of the installed function is done for you.
typename GlobalVariable< Tag, const std::function< Signature >, Default, Options... >::Scope Scope
bool OnExec(const wxString &WXUNUSED(topic), const wxString &data)
wxConnectionBase * OnAcceptConnection(const wxString &topic) override
IPCServ(const wxString &appl)
static Importer & Get()
Definition: Import.cpp:103
bool Initialize()
Definition: Import.cpp:167
bool Terminate()
Definition: Import.cpp:201
static void Destroy()
Destroys the log window (if any)
Definition: LogWindow.cpp:178
static void RebuildAllMenuBars()
void RemoveDuplicateShortcuts()
static MenuCreator & Get(AudacityProject &project)
Definition: MenuCreator.cpp:91
static ModuleManager & Get()
int Dispatch(ModuleDispatchTypes type)
Subscription Subscribe(Callback callback)
Connect a callback to the Publisher; later-connected are called earlier.
Definition: Observer.h:199
static PendingTracks & Get(AudacityProject &project)
void ClearPendingTracks(std::vector< std::shared_ptr< Track > > *pAdded=nullptr)
Forget pending track additions and changes;.
static bool IsHostProcess()
Returns true if current process is considered to be a plugin host process.
Definition: PluginHost.cpp:202
std::map< wxString, std::vector< wxString > > CheckPluginUpdates()
Ensures that all currently registered plugins still exist and scans for new ones.
void Initialize(ConfigFactory factory)
static PluginManager & Get()
int ShowModal() override
std::vector< PrefsPanel::PrefsNode > Factories
Definition: PrefsPanel.h:72
int GetAudioIOToken() const
static ProjectAudioIO & Get(AudacityProject &project)
void Stop(bool stopStream=true)
static ProjectAudioManager & Get(AudacityProject &project)
static bool InitializeSQL()
static bool IsAlreadyOpen(const FilePath &projPathName)
static ProjectHistory & Get(AudacityProject &project)
static AudacityProject * New()
static void SaveWindowSize()
static void OpenFiles(AudacityProject *proj)
static AudacityProject * OpenProject(AudacityProject *pGivenProject, const FilePath &fileNameArg, bool addtohistory, bool reuseNonemptyProject)
Open a file into an AudacityProject, returning the project, or nullptr for failure.
static void SetClosingAll(bool closing)
static ProjectSettings & Get(AudacityProject &project)
bool GetShowSplashScreen() const
static Scrubber & Get(AudacityProject &project)
Definition: Scrubbing.cpp:186
static void SetMaxDiskBlockSize(size_t bytes)
Definition: Sequence.cpp:1895
bool Read(T *pVar) const
overload of Read returning a boolean that is true if the value was previously defined *‍/
Definition: Prefs.h:207
static bool LoadPreferredTheme()
Definition: Theme.cpp:162
bool RegisterScheme(std::string_view scheme)
Associates a new scheme with Audacity.
static URLSchemesRegistry & Get()
Retrieves the registry instance.
void HandleURL(std::string_view url)
static void Start(bool suppressModal)
void Redraw()
Definition: Viewport.cpp:753
static Viewport & Get(AudacityProject &project)
Definition: Viewport.cpp:33
static void Show(AudacityProject &project)
virtual bool Flush() noexcept=0
virtual bool Exists(const wxString &key) const
Returns true if group or entry exists.
GroupScope BeginGroup(const wxString &prefix)
Appends a prefix to the current group or sets a new absolute path. Group that was set as current befo...
bool DeleteGroup(const wxString &key)
Deletes specified group if exists.
virtual bool Write(const wxString &key, bool value)=0
bool DeleteEntry(const wxString &key)
Deletes specified entry if exists.
virtual bool Read(const wxString &key, bool *value) const =0
virtual wxArrayString GetChildGroups() const =0
Returns all child groups within the current group.
Extend wxArrayString with move operations and construction and insertion fromstd::initializer_list.
An implementation of BasicUI::Services in terms of the wxWidgets toolkit.
Services * Install(Services *pInstance)
Install an implementation; return the previously installed instance.
Definition: BasicUI.cpp:204
void CallAfter(Action action)
Schedule an action to be done later, and in the main thread.
Definition: BasicUI.cpp:214
Services * Get()
Fetch the global instance, or nullptr if none is yet installed.
Definition: BasicUI.cpp:202
UTILITY_API const char *const * argv
A copy of argv; responsibility of application startup to assign it.
UTILITY_API int argc
A copy of argc; responsibility of application startup to assign it.
IMPORT_EXPORT_API ExportResult Show(ExportTask exportTask)
FILES_API FilePath Configuration()
FILES_API FilePath ResourcesDir()
FILES_API wxString PreferenceKey(FileNames::Operation op, FileNames::PathType type)
FILES_API FilePath StateDir()
Audacity user state directory.
FILES_API void InitializePathList()
FILES_API FilePath DataDir()
Audacity user data directory.
FILES_API void UpdateDefaultPath(Operation op, const FilePath &path)
FILES_API const FilePaths & AudacityPathList()
A list of directories that should be searched for Audacity files (plug-ins, help files,...
AUDACITY_DLL_API wxString SetLang(const wxString &lang)
Definition: GUISettings.cpp:19
int GetExitCode()
Definition: Journal.cpp:386
bool Begin(const FilePath &dataDir)
Definition: Journal.cpp:226
void SetInputFileName(const wxString &path)
Definition: Journal.cpp:221
bool Dispatch()
Definition: Journal.cpp:291
wxString GetSystemLanguageCode(const FilePaths &pathList)
Definition: Languages.cpp:83
wxString GetLocaleName()
Definition: Languages.cpp:390
FILES_API bool IsTempDirectoryNameOK(const FilePath &Name)
FILES_API wxString TempDir()
FILES_API void ResetTempDir()
FILES_API const FilePath & DefaultTempDir()
THEME_RESOURCES_API void Load()
auto Dispatch(Head_t< Types > &object, const TupleLike &functions, Args &&... args) -> std::enable_if_t< TypeListCheck_v< Types >, R >
Definition: TypeSwitch.h:313
BuiltinCommandsModule::Registration< CompareAudioCommand > reg
static CommandContext::TargetFactory::SubstituteInUnique< InteractiveOutputTargets > scope
std::string ToUTF8(const std::wstring &wstr)
Implementation of BasicUI using wxWidgets.