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