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