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