From 03bbe07937bbd7c784b663b90f27f735bed9bba0 Mon Sep 17 00:00:00 2001 From: lx-s Date: Tue, 18 Aug 2026 12:12:57 +0200 Subject: [PATCH] Fixed display standby detection. --- Dist/bin/changelog.txt | 9 ++++++--- WinMute/WinMute.cpp | 46 ++++++++++++++++++++++++++++++++++++------ WinMute/WinMute.h | 4 ++++ 3 files changed, 50 insertions(+), 9 deletions(-) diff --git a/Dist/bin/changelog.txt b/Dist/bin/changelog.txt index a2fc056..845bdd3 100644 --- a/Dist/bin/changelog.txt +++ b/Dist/bin/changelog.txt @@ -5,9 +5,12 @@ Version 2.6.0 (2026-08-18) * Added "Mute on Laptop Lid Close" * Added: New translations * Added: Program is now build with control-flow enforcement (CET) - * Fixed some possible race conditions - * Fixed memory leak in wifi detector - * Fixed possible security issues in update checker + * Fixed: Display stand-by detection + * Fixed: 60 second cadence time to mute/unmute devices that take a few + seconds to wake up and register as "plugged in" again. + * Fixed: Possible race conditions + * Fixed: Memory leak in wifi detector + * Fixed: Hardened update checker * Fixed: No longer immediately exiting if the workstation-lock notification can not be registered. WinMute will retry registering with TermService for about half a minute and disable mute-on-lock if the service does not diff --git a/WinMute/WinMute.cpp b/WinMute/WinMute.cpp index fa38afc..64e8476 100644 --- a/WinMute/WinMute.cpp +++ b/WinMute/WinMute.cpp @@ -337,13 +337,31 @@ bool WinMute::Init() return false; } + // GUID_CONSOLE_DISPLAY_STATE is documented for kernel-mode/session 0 + // consumers; interactive applications are supposed to use + // GUID_SESSION_DISPLAY_STATUS. Register for both, since neither is + // guaranteed to be delivered on every Windows version. + hSessionDisplayNotify_ = RegisterPowerSettingNotification( + hWnd_, &GUID_SESSION_DISPLAY_STATUS, DEVICE_NOTIFY_WINDOW_HANDLE); + if (hSessionDisplayNotify_ == nullptr) { + const DWORD lastError = GetLastError(); + ShowWindowsError(L"RegisterPowerSettingNotification", lastError); + log.LogWinError(L"RegisterPowerSettingNotification", lastError); + UnregisterPowerSettingNotification(hPowerNotify_); + hPowerNotify_ = nullptr; + return false; + } + hLidCloseNotify_ = RegisterPowerSettingNotification( hWnd_, &GUID_LIDSWITCH_STATE_CHANGE, DEVICE_NOTIFY_WINDOW_HANDLE); if (hLidCloseNotify_ == nullptr) { const DWORD lastError = GetLastError(); ShowWindowsError(L"RegisterPowerSettingNotification", lastError); log.LogWinError(L"RegisterPowerSettingNotification", lastError); + UnregisterPowerSettingNotification(hSessionDisplayNotify_); + hSessionDisplayNotify_ = nullptr; UnregisterPowerSettingNotification(hPowerNotify_); + hPowerNotify_ = nullptr; return false; } @@ -880,13 +898,25 @@ LRESULT WinMute::OnPowerBroadcast(HWND, WPARAM wParam, LPARAM lParam) } else if (wParam == PBT_POWERSETTINGCHANGE) { const PPOWERBROADCAST_SETTING bs = reinterpret_cast(lParam); - if (IsEqualGUID(bs->PowerSetting, GUID_CONSOLE_DISPLAY_STATE)) { + if (IsEqualGUID(bs->PowerSetting, GUID_CONSOLE_DISPLAY_STATE) || + IsEqualGUID(bs->PowerSetting, GUID_SESSION_DISPLAY_STATUS)) + { const DWORD state = bs->Data[0]; - if (state == 0x0) { // Display standby - muteCtrl_.NotifyDisplayStandby(true); - } else if (state == 0x1) { // Display on - muteCtrl_.NotifyDisplayStandby(false); - } else if (state == 0x2) { // Display dimmed + const bool isSession = + IsEqualGUID(bs->PowerSetting, GUID_SESSION_DISPLAY_STATUS); + WMLog::GetInstance().LogDebug( + L"Power event: {} display state {}", + isSession ? L"session" : L"console", state); + // Both notifications describe the same display, so ignore the + // duplicate that arrives for the second registration. + if (state != lastDisplayState_) { + lastDisplayState_ = state; + if (state == 0x0) { // Display standby + muteCtrl_.NotifyDisplayStandby(true); + } else if (state == 0x1) { // Display on + muteCtrl_.NotifyDisplayStandby(false); + } else if (state == 0x2) { // Display dimmed + } } } else if (IsEqualGUID(bs->PowerSetting, GUID_LIDSWITCH_STATE_CHANGE)) { const DWORD state = bs->Data[0]; @@ -1058,6 +1088,10 @@ void WinMute::Unload() noexcept UnregisterPowerSettingNotification(hPowerNotify_); hPowerNotify_ = nullptr; } + if (hSessionDisplayNotify_ != nullptr) { + UnregisterPowerSettingNotification(hSessionDisplayNotify_); + hSessionDisplayNotify_ = nullptr; + } if (hLidCloseNotify_ != nullptr) { UnregisterPowerSettingNotification(hLidCloseNotify_); hLidCloseNotify_ = nullptr; diff --git a/WinMute/WinMute.h b/WinMute/WinMute.h index 680f199..81a159a 100644 --- a/WinMute/WinMute.h +++ b/WinMute/WinMute.h @@ -76,7 +76,11 @@ class WinMute { UINT_PTR wtsRetryTimerId_ = 0; int wtsRetryAttempts_ = 0; HPOWERNOTIFY hPowerNotify_ = nullptr; + HPOWERNOTIFY hSessionDisplayNotify_ = nullptr; HPOWERNOTIFY hLidCloseNotify_ = nullptr; + // Console and session display notifications can both fire for the same + // transition. 0xFFFFFFFF means "no state seen yet". + DWORD lastDisplayState_ = 0xFFFFFFFF; TrayIcon wmTray_; TrayIcon updateTray_; -- 2.43.0