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