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