diff --git a/src/Main/Forms/Forms.cpp b/src/Main/Forms/Forms.cpp
index c64dbbfbc4..4f2fb5923a 100644
--- a/src/Main/Forms/Forms.cpp
+++ b/src/Main/Forms/Forms.cpp
@@ -1832,7 +1832,7 @@ PreferencesDialogBase::PreferencesDialogBase( wxWindow* parent, wxWindowID id, c
DismountOnScreenSaverCheckBox = new wxCheckBox( sbSizer13->GetStaticBox(), wxID_ANY, _("IDC_PREF_UNMOUNT_SCREENSAVER"), wxDefaultPosition, wxDefaultSize, 0 );
sbSizer13->Add( DismountOnScreenSaverCheckBox, 0, wxALL, 5 );
- DismountOnPowerSavingCheckBox = new wxCheckBox( sbSizer13->GetStaticBox(), wxID_ANY, _("LINUX_ENTERING_POVERSAWING"), wxDefaultPosition, wxDefaultSize, 0 );
+ DismountOnPowerSavingCheckBox = new wxCheckBox( sbSizer13->GetStaticBox(), wxID_ANY, _("LINUX_ENTERING_POWERSAVING"), wxDefaultPosition, wxDefaultSize, 0 );
sbSizer13->Add( DismountOnPowerSavingCheckBox, 0, wxALL, 5 );
diff --git a/src/Main/Forms/PreferencesDialog.cpp b/src/Main/Forms/PreferencesDialog.cpp
index 879394cac7..3d2a8d733e 100644
--- a/src/Main/Forms/PreferencesDialog.cpp
+++ b/src/Main/Forms/PreferencesDialog.cpp
@@ -295,12 +295,16 @@ namespace VeraCrypt
CloseExplorerWindowsOnDismountCheckBox->Show (false);
#endif
-#ifndef wxHAS_POWER_EVENTS
+ // On macOS, wxHAS_POWER_EVENTS is undefined but sleep is handled by a
+ // native observer (see MacOSXSleepLock), so keep the checkbox visible.
+#if !defined(wxHAS_POWER_EVENTS) && !defined(TC_MACOSX)
DismountOnPowerSavingCheckBox->Show (false);
#endif
#ifdef TC_MACOSX
- DismountOnScreenSaverCheckBox->Show (false);
+ // On macOS this checkbox drives dismount on screen lock (see
+ // MacOSXSleepLock), so relabel it accordingly.
+ DismountOnScreenSaverCheckBox->SetLabel (LangString["IDC_PREF_UNMOUNT_SESSION_LOCKED"]);
DismountOnLogOffCheckBox->SetLabel (LangString["LINUX_VC_QUITS"]);
OpenExplorerWindowAfterMountCheckBox->SetLabel (LangString["LINUX_OPEN_FINDER"]);
diff --git a/src/Main/Forms/TrueCrypt.fbp b/src/Main/Forms/TrueCrypt.fbp
index a100bf1f74..6c457935e9 100644
--- a/src/Main/Forms/TrueCrypt.fbp
+++ b/src/Main/Forms/TrueCrypt.fbp
@@ -11134,7 +11134,7 @@
0
0
wxID_ANY
- LINUX_ENTERING_POVERSAWING
+ LINUX_ENTERING_POWERSAVING
0
diff --git a/src/Main/GraphicUserInterface.cpp b/src/Main/GraphicUserInterface.cpp
index 321a939627..cf38325a43 100644
--- a/src/Main/GraphicUserInterface.cpp
+++ b/src/Main/GraphicUserInterface.cpp
@@ -30,6 +30,7 @@
#include "FatalErrorHandler.h"
#ifdef TC_MACOSX
#include "MacOSXSecureTextFieldHotkeys.h"
+#include "MacOSXSleepLock.h"
#endif
#include "Forms/DeviceSelectionDialog.h"
#include "Forms/KeyfileGeneratorDialog.h"
@@ -184,6 +185,7 @@ namespace VeraCrypt
{
#ifdef TC_MACOSX
UninstallMacOSXSecureTextFieldHotkeys();
+ UninstallMacOSXSleepLockHandler();
#endif
try
{
@@ -1038,6 +1040,38 @@ namespace VeraCrypt
}
}
+#ifdef TC_MACOSX
+ // Called from MacOSXSleepLock.mm on the main thread when the system is about
+ // to sleep. wxWidgets does not deliver wxEVT_POWER_SUSPENDING on macOS
+ // (wxHAS_POWER_EVENTS is undefined there), so this observer fills that gap.
+ // AutoDismountVolumes is called with alwaysForce=false so that the
+ // ForceAutoDismount preference decides whether volumes with open files are
+ // detached, matching the Windows auto-dismount events (bForceAutoDismount).
+ void OnMacOSXSystemWillSleep ()
+ {
+ if (Gui && Gui->GetPreferences().BackgroundTaskEnabled && Gui->GetPreferences().DismountOnPowerSaving)
+ {
+ VolumeInfoList mountedVolumes = Core->GetMountedVolumes();
+ if (!mountedVolumes.empty())
+ Gui->AutoDismountVolumes (mountedVolumes, false);
+ }
+ }
+
+ // Called from MacOSXSleepLock.mm on the main thread when the user session
+ // has been verified as locked. macOS has no equivalent of the Windows
+ // screen-saver polling used by MainFrame::OnTimer, so screen-lock dismount
+ // is wired up here instead. Force semantics as above.
+ void OnMacOSXScreenLocked ()
+ {
+ if (Gui && Gui->GetPreferences().BackgroundTaskEnabled && Gui->GetPreferences().DismountOnScreenSaver)
+ {
+ VolumeInfoList mountedVolumes = Core->GetMountedVolumes();
+ if (!mountedVolumes.empty())
+ Gui->AutoDismountVolumes (mountedVolumes, false);
+ }
+ }
+#endif
+
bool GraphicUserInterface::OnInit ()
{
Gui = this;
@@ -1193,6 +1227,11 @@ namespace VeraCrypt
#ifdef wxHAS_POWER_EVENTS
Gui->Connect (wxEVT_POWER_SUSPENDING, wxPowerEventHandler (GraphicUserInterface::OnPowerSuspending));
#endif
+#ifdef TC_MACOSX
+ // macOS lacks wxHAS_POWER_EVENTS; use native observers for sleep and
+ // screen lock so volumes can be auto-dismounted on those events.
+ InstallMacOSXSleepLockHandler();
+#endif
mMainFrame = new MainFrame (nullptr);
#if defined(TC_UNIX)
diff --git a/src/Main/MacOSXSleepLock.h b/src/Main/MacOSXSleepLock.h
new file mode 100644
index 0000000000..28e634e18c
--- /dev/null
+++ b/src/Main/MacOSXSleepLock.h
@@ -0,0 +1,28 @@
+/*
+ Copyright (c) 2013-2026 AM Crypto. All rights reserved.
+
+ Governed by the Apache License 2.0 the full text of which is
+ contained in the file License.txt included in VeraCrypt binary and source
+ code distribution packages.
+*/
+
+#ifndef TC_HEADER_Main_MacOSXSleepLock
+#define TC_HEADER_Main_MacOSXSleepLock
+
+#ifdef TC_MACOSX
+namespace VeraCrypt
+{
+ // Register/unregister observers for system sleep and screen lock.
+ // Implemented in MacOSXSleepLock.mm.
+ void InstallMacOSXSleepLockHandler ();
+ void UninstallMacOSXSleepLockHandler ();
+
+ // Invoked by the observers above when the corresponding event occurs.
+ // Implemented on the C++ side (GraphicUserInterface.cpp) so that all
+ // wxWidgets / Core logic stays out of the Objective-C++ file.
+ void OnMacOSXSystemWillSleep ();
+ void OnMacOSXScreenLocked ();
+}
+#endif
+
+#endif // TC_HEADER_Main_MacOSXSleepLock
diff --git a/src/Main/MacOSXSleepLock.mm b/src/Main/MacOSXSleepLock.mm
new file mode 100644
index 0000000000..40ea53b7e6
--- /dev/null
+++ b/src/Main/MacOSXSleepLock.mm
@@ -0,0 +1,133 @@
+/*
+ Copyright (c) 2013-2026 AM Crypto. All rights reserved.
+
+ Governed by the Apache License 2.0 the full text of which is
+ contained in the file License.txt included in VeraCrypt binary and source
+ code distribution packages.
+*/
+
+#include "System.h"
+#include "MacOSXSleepLock.h"
+
+#ifdef TC_MACOSX
+#import
+#import
+
+// Queries the window server for the actual lock state of the current session.
+// The com.apple.screenIsLocked distributed notification can be posted by any
+// local process, so it is treated only as a hint and the dismount is performed
+// only if the session really is locked.
+static bool SessionIsLocked ()
+{
+ bool locked = false;
+ CFDictionaryRef session = CGSessionCopyCurrentDictionary ();
+ if (session)
+ {
+ CFTypeRef value = CFDictionaryGetValue (session, CFSTR ("CGSSessionScreenIsLocked"));
+ locked = value && CFGetTypeID (value) == CFBooleanGetTypeID () && CFBooleanGetValue ((CFBooleanRef) value);
+ CFRelease (session);
+ }
+ return locked;
+}
+
+@interface VCSleepLockObserver : NSObject
+- (void) systemWillSleep: (NSNotification *) notification;
+- (void) screenLocked: (NSNotification *) notification;
+- (void) dismountIfSessionLocked;
+@end
+
+@implementation VCSleepLockObserver
+
+// The callbacks below are entered from AppKit notification dispatch; a C++
+// exception must not unwind through those Objective-C frames, so every call
+// into VeraCrypt code is guarded here. The dismount is best-effort: showing
+// UI is not reliable while the session is locking or the system is going to
+// sleep, so failures are ignored.
+
+- (void) systemWillSleep: (NSNotification *) notification
+{
+ (void) notification;
+ try
+ {
+ VeraCrypt::OnMacOSXSystemWillSleep ();
+ }
+ catch (...) { }
+}
+
+- (void) screenLocked: (NSNotification *) notification
+{
+ (void) notification;
+ // The window server can update the session dictionary slightly after the
+ // notification is delivered, so if the session does not report itself as
+ // locked yet, re-check once after a short delay instead of giving up.
+ if (SessionIsLocked ())
+ {
+ [self dismountIfSessionLocked];
+ }
+ else
+ {
+ [NSObject cancelPreviousPerformRequestsWithTarget: self selector: @selector (dismountIfSessionLocked) object: nil];
+ [self performSelector: @selector (dismountIfSessionLocked) withObject: nil afterDelay: 1.0];
+ }
+}
+
+- (void) dismountIfSessionLocked
+{
+ if (!SessionIsLocked ())
+ return;
+
+ try
+ {
+ VeraCrypt::OnMacOSXScreenLocked ();
+ }
+ catch (...) { }
+}
+
+@end
+
+namespace VeraCrypt
+{
+ // Non-ARC build (see the .mm compile rule in Build/Include/Makefile.inc),
+ // so the observer is retained by alloc/init and released in Uninstall.
+ static VCSleepLockObserver *SleepLockObserver = nil;
+
+ void InstallMacOSXSleepLockHandler ()
+ {
+ if (SleepLockObserver)
+ return;
+
+ SleepLockObserver = [[VCSleepLockObserver alloc] init];
+
+ // System sleep (lid close, idle sleep, Apple menu > Sleep). Delivered on
+ // the main thread by NSWorkspace's own notification center, which only
+ // the system feeds (it is per-process, unlike the distributed center).
+ [[[NSWorkspace sharedWorkspace] notificationCenter]
+ addObserver: SleepLockObserver
+ selector: @selector (systemWillSleep:)
+ name: NSWorkspaceWillSleepNotification
+ object: nil];
+
+ // Screen lock. macOS exposes no public notification for this; the
+ // com.apple.screenIsLocked distributed notification is the long-standing
+ // de-facto mechanism used to detect it. It is untrusted input: the
+ // handler verifies the actual session lock state before dismounting.
+ [[NSDistributedNotificationCenter defaultCenter]
+ addObserver: SleepLockObserver
+ selector: @selector (screenLocked:)
+ name: @"com.apple.screenIsLocked"
+ object: nil];
+ }
+
+ void UninstallMacOSXSleepLockHandler ()
+ {
+ if (!SleepLockObserver)
+ return;
+
+ [NSObject cancelPreviousPerformRequestsWithTarget: SleepLockObserver];
+ [[[NSWorkspace sharedWorkspace] notificationCenter] removeObserver: SleepLockObserver];
+ [[NSDistributedNotificationCenter defaultCenter] removeObserver: SleepLockObserver];
+ [SleepLockObserver release];
+ SleepLockObserver = nil;
+ }
+}
+#endif
diff --git a/src/Main/Main.make b/src/Main/Main.make
index 36b8c1f6e9..ae9fdc37c2 100755
--- a/src/Main/Main.make
+++ b/src/Main/Main.make
@@ -28,6 +28,7 @@ OBJS += FatalErrorHandler.o
OBJS += GraphicUserInterface.o
ifeq "$(PLATFORM)" "MacOSX"
OBJS += MacOSXSecureTextFieldHotkeys.o
+OBJS += MacOSXSleepLock.o
endif
OBJS += VolumeHistory.o
OBJS += Forms/AboutDialog.o