]> repos.lx-s.de Git - winmute.git/commitdiff
Register Mute Hotkey and mute system when hotkey is pressed
authorlx-s <lx-s@users.noreply.github.com>
Tue, 18 Aug 2026 07:18:41 +0000 (09:18 +0200)
committerlx-s <lx-s@users.noreply.github.com>
Tue, 18 Aug 2026 07:18:41 +0000 (09:18 +0200)
Translations/lang-de.json
Translations/lang-en.json
WinMute/WinMute.cpp
WinMute/WinMute.h

index d29ee0db7277b9040d40659ddd3959d749c4e21c..0d75a50d5aefd9b3a1ebace8f7faa981c5d22b01 100644 (file)
@@ -39,6 +39,8 @@
   "popup.error.quiet-hours-start.text": "Fehler beim Registrieren des Windows-Timers für den Ruhezeit-Beginn",
   "popup.error.quiet-hours-stop.title": "Ende der Ruhezeit",
   "popup.error.quiet-hours-stop.text": "Fehler beim Registrieren des Windows-Timers für das Ruhezeit-Ende",
+  "popup.error.global-mute-hotkey-register.title": "Fehler beim Registrieren des Mute-Hotkeys",
+  "popup.error.global-mute-hotkey-register.text": "Die Tastenkombination konnte nicht registriert werden. Bitte überprüfen Sie ob andere Programme diese Kombination bereits verwenden.",
   "traymenu.info": "Info",
   "traymenu.mute-when": "Stummschalten sobald...",
   "traymenu.mute-on-lock": "... PC gesperrt wird",
index 65ff4a45a859a022a3e404e971c50727d98a4c80..c07d3d2200e11769341a6496e562bac2dd6af7a9 100644 (file)
@@ -45,6 +45,8 @@
   "popup.error.quiet-hours-start.text": "Failed to register quiet hours start with windows timer system",
   "popup.error.quiet-hours-stop.title": "Quiet Hours Stop",
   "popup.error.quiet-hours-stop.text": "Failed to register end of quiet hours with windows timer system",
+  "popup.error.global-mute-hotkey-register.title": "Failed to register mute hotkey",
+  "popup.error.global-mute-hotkey-register.text": "The hotkey could not be registered. Please check if other programs are using the same key combination.",
   "traymenu.info": "About",
   "traymenu.mute-when": "Mute when...",
   "traymenu.mute-on-lock": "... computer is locked",
index 2c48e44e5e75f42eea0e19c857cdb4d95bd69c02..e9f4772d1c3a6bc0bea0cf5823089068fc745655 100644 (file)
@@ -54,6 +54,8 @@ static constexpr UINT_PTR WTS_RETRY_TIMER_ID = 190503;
 static constexpr UINT WTS_RETRY_INTERVAL_MS = 1000;
 static constexpr int WTS_RETRY_MAX_ATTEMPTS = 30;
 
+static constexpr int GLOBAL_HOTKEY_ID_MUTE = 1000;
+
 static LRESULT CALLBACK WinMuteWndProc(HWND hWnd, UINT msg, WPARAM wParam,
                                        LPARAM lParam)
 {
@@ -378,78 +380,94 @@ bool WinMute::LoadSettings()
 {
     WMLog& log = WMLog::GetInstance();
 
-    if (log.IsLogFileEnabled()) {
-        std::wstring versionNumber;
-        GetWinMuteVersion(versionNumber);
-        log.LogInfo(L"Starting WinMute {}", versionNumber);
-        log.LogInfo(L"Loading settings:");
-        log.LogInfo(L"Check for updates: {}",
-                    settings_.QueryValue(SettingsKey::CHECK_FOR_UPDATE)
-                        ? L"Yes"
-                        : L"No");
-        log.LogInfo(L"\tCheck for beta updates: {}",
-                    settings_.QueryValue(SettingsKey::CHECK_FOR_BETA_UPDATE)
-                        ? L"Yes"
-                        : L"No");
-        log.LogInfo(
-            L"\tRestore volume: {}",
-            settings_.QueryValue(SettingsKey::RESTORE_AUDIO) ? L"Yes" : L"No");
-        log.LogInfo(L"\tMute delay: {}",
-                    settings_.QueryValue(SettingsKey::MUTE_DELAY));
-        log.LogInfo(
-            L"\tMute on lock: {}",
-            settings_.QueryValue(SettingsKey::MUTE_ON_LOCK) ? L"Yes" : L"No");
-        log.LogInfo(L"\tMute on display standby: {}",
-                    settings_.QueryValue(SettingsKey::MUTE_ON_DISPLAYSTANDBY)
-                        ? L"Yes"
-                        : L"No");
-        log.LogInfo(L"\tMute on display lid close: {}",
-                    settings_.QueryValue(SettingsKey::MUTE_ON_LIDCLOSE)
-                        ? L"Yes"
-                        : L"No");
-        log.LogInfo(
-            L"\tMute on logout: {}",
-            settings_.QueryValue(SettingsKey::MUTE_ON_LOGOUT) ? L"Yes" : L"No");
-        log.LogInfo(L"\tMute on suspend: {}",
-                    settings_.QueryValue(SettingsKey::MUTE_ON_SUSPEND) ? L"Yes"
-                                                                       : L"No");
-        log.LogInfo(L"\tMute on shutdown: {}",
-                    settings_.QueryValue(SettingsKey::MUTE_ON_SHUTDOWN)
-                        ? L"Yes"
-                        : L"No");
-        log.LogInfo(L"\tShow notifications: {}",
-                    settings_.QueryValue(SettingsKey::NOTIFICATIONS_ENABLED)
-                        ? L"Yes"
-                        : L"No");
-        log.LogInfo(L"\tMute on bluetooth: {}",
-                    settings_.QueryValue(SettingsKey::MUTE_ON_BLUETOOTH)
-                        ? L"Yes"
-                        : L"No");
-        log.LogInfo(L"\tTry pausing media when muting: {}",
-                    settings_.QueryValue(SettingsKey::MUTE_TRY_PAUSE_MEDIA)
-                        ? L"Yes"
-                        : L"No");
-        log.LogInfo(L"\tTry resuming media when unmuting: {}",
-                    settings_.QueryValue(SettingsKey::MUTE_TRY_RESUME_MEDIA)
-                        ? L"Yes"
-                        : L"No");
-        log.LogInfo(
-            L"\t\tUse devicelist: {}",
-            settings_.QueryValue(SettingsKey::MUTE_ON_BLUETOOTH_DEVICELIST)
-                ? L"Yes"
-                : L"No");
-        log.LogInfo(
-            L"\tMute on WLAN: {}",
-            settings_.QueryValue(SettingsKey::MUTE_ON_WLAN) ? L"Yes" : L"No");
-        log.LogInfo(L"\t\tUse allowlist: {}",
-                    settings_.QueryValue(SettingsKey::MUTE_ON_WLAN_ALLOWLIST)
-                        ? L"Yes"
-                        : L"No");
-        log.LogInfo(L"\tMute specific endpoints only: {}",
-                    settings_.QueryValue(SettingsKey::MUTE_INDIVIDUAL_ENDPOINTS)
-                        ? L"Yes"
-                        : L"No");
+    std::wstring versionNumber;
+    GetWinMuteVersion(versionNumber);
+    log.LogInfo(L"Starting WinMute {}", versionNumber);
+    log.LogInfo(L"Loading settings:");
+    log.LogInfo(L"Enable global mute hotkey: {}",
+                settings_.QueryValue(SettingsKey::ENABLE_GLOBAL_MUTE_HOTKEY)
+                    ? L"Yes"
+                    : L"No");
+
+    std::wstring hotKeyStr;
+    const auto hotkeyValue =
+        settings_.QueryValue(SettingsKey::GLOBAL_MUTE_HOTKEY);
+    UINT hkMods = LOWORD(HIBYTE(hotkeyValue));
+    wchar_t vk = static_cast<wchar_t>(
+        MapVirtualKeyW(LOWORD(LOBYTE(hotkeyValue)), MAPVK_VK_TO_CHAR));
+    if (hkMods & HOTKEYF_ALT) {
+        hotKeyStr += L"Alt + ";
+    }
+    if (hkMods & HOTKEYF_CONTROL) {
+        hotKeyStr += L"Ctrl + ";
+    }
+    if (hkMods & HOTKEYF_SHIFT) {
+        hotKeyStr += L"Shift + ";
     }
+    hotKeyStr += vk;
+    log.LogInfo(L"\tHotkey: {}", hotKeyStr);
+
+    log.LogInfo(
+        L"Check for updates: {}",
+        settings_.QueryValue(SettingsKey::CHECK_FOR_UPDATE) ? L"Yes" : L"No");
+    log.LogInfo(L"\tCheck for beta updates: {}",
+                settings_.QueryValue(SettingsKey::CHECK_FOR_BETA_UPDATE)
+                    ? L"Yes"
+                    : L"No");
+    log.LogInfo(
+        L"\tRestore volume: {}",
+        settings_.QueryValue(SettingsKey::RESTORE_AUDIO) ? L"Yes" : L"No");
+    log.LogInfo(L"\tMute delay: {}",
+                settings_.QueryValue(SettingsKey::MUTE_DELAY));
+    log.LogInfo(
+        L"\tMute on lock: {}",
+        settings_.QueryValue(SettingsKey::MUTE_ON_LOCK) ? L"Yes" : L"No");
+    log.LogInfo(L"\tMute on display standby: {}",
+                settings_.QueryValue(SettingsKey::MUTE_ON_DISPLAYSTANDBY)
+                    ? L"Yes"
+                    : L"No");
+    log.LogInfo(
+        L"\tMute on display lid close: {}",
+        settings_.QueryValue(SettingsKey::MUTE_ON_LIDCLOSE) ? L"Yes" : L"No");
+    log.LogInfo(
+        L"\tMute on logout: {}",
+        settings_.QueryValue(SettingsKey::MUTE_ON_LOGOUT) ? L"Yes" : L"No");
+    log.LogInfo(
+        L"\tMute on suspend: {}",
+        settings_.QueryValue(SettingsKey::MUTE_ON_SUSPEND) ? L"Yes" : L"No");
+    log.LogInfo(
+        L"\tMute on shutdown: {}",
+        settings_.QueryValue(SettingsKey::MUTE_ON_SHUTDOWN) ? L"Yes" : L"No");
+    log.LogInfo(L"\tShow notifications: {}",
+                settings_.QueryValue(SettingsKey::NOTIFICATIONS_ENABLED)
+                    ? L"Yes"
+                    : L"No");
+    log.LogInfo(
+        L"\tMute on bluetooth: {}",
+        settings_.QueryValue(SettingsKey::MUTE_ON_BLUETOOTH) ? L"Yes" : L"No");
+    log.LogInfo(L"\tTry pausing media when muting: {}",
+                settings_.QueryValue(SettingsKey::MUTE_TRY_PAUSE_MEDIA)
+                    ? L"Yes"
+                    : L"No");
+    log.LogInfo(L"\tTry resuming media when unmuting: {}",
+                settings_.QueryValue(SettingsKey::MUTE_TRY_RESUME_MEDIA)
+                    ? L"Yes"
+                    : L"No");
+    log.LogInfo(L"\t\tUse devicelist: {}",
+                settings_.QueryValue(SettingsKey::MUTE_ON_BLUETOOTH_DEVICELIST)
+                    ? L"Yes"
+                    : L"No");
+    log.LogInfo(
+        L"\tMute on WLAN: {}",
+        settings_.QueryValue(SettingsKey::MUTE_ON_WLAN) ? L"Yes" : L"No");
+    log.LogInfo(L"\t\tUse allowlist: {}",
+                settings_.QueryValue(SettingsKey::MUTE_ON_WLAN_ALLOWLIST)
+                    ? L"Yes"
+                    : L"No");
+    log.LogInfo(L"\tMute specific endpoints only: {}",
+                settings_.QueryValue(SettingsKey::MUTE_INDIVIDUAL_ENDPOINTS)
+                    ? L"Yes"
+                    : L"No");
 
     if (!settings_.QueryValue(SettingsKey::MUTE_INDIVIDUAL_ENDPOINTS)) {
         muteCtrl_.ClearManagedEndpoints();
@@ -524,7 +542,8 @@ bool WinMute::LoadSettings()
         }
     }
 
-    // GUID_MONITOR_POWER_ON
+    LoadGlobalHotkeys();
+
     return true;
 }
 
@@ -580,6 +599,37 @@ void WinMute::LoadMainMenuText()
     }
 }
 
+void WinMute::LoadGlobalHotkeys()
+{
+    const auto hotKey = settings_.QueryValue(SettingsKey::GLOBAL_MUTE_HOTKEY);
+    UINT modifiers = 0;
+    UINT hkMods = LOWORD(HIBYTE(hotKey));
+    UINT vk = LOWORD(LOBYTE(hotKey));
+    if (hkMods & HOTKEYF_ALT) {
+        modifiers |= MOD_ALT;
+    }
+    if (hkMods & HOTKEYF_CONTROL) {
+        modifiers |= MOD_CONTROL;
+    }
+    if (hkMods & HOTKEYF_SHIFT) {
+        modifiers |= MOD_SHIFT;
+    }
+    // Ignore return value, we don't care if it was registered or not
+    UnregisterHotKey(hWnd_, GLOBAL_HOTKEY_ID_MUTE);
+    if (settings_.QueryValue(SettingsKey::ENABLE_GLOBAL_MUTE_HOTKEY)) {
+        if (!RegisterHotKey(hWnd_, GLOBAL_HOTKEY_ID_MUTE, modifiers, vk)) {
+            WMLog::GetInstance().LogWinError(L"RegisterHotKey", GetLastError());
+            wmTray_.ShowPopup(
+                i18n_.GetTranslationW(
+                    "popup.error.global-mute-hotkey-register.title"),
+                i18n_.GetTranslationW(
+                    "popup.error.global-mute-hotkey-register.text"));
+        } else {
+            globalHotkeys_[MAKEWORD(vk, modifiers)] = GlobalHotKey::Mute;
+        }
+    }
+}
+
 void WinMute::CheckForUpdates()
 {
     const UpdateChecker updateChecker;
@@ -752,6 +802,21 @@ LRESULT WinMute::OnTrayIcon(HWND hWnd, WPARAM wParam, LPARAM lParam)
     return TRUE;
 }
 
+LRESULT WinMute::OnHotKey([[maybe_unused]] HWND hWnd,
+                          [[maybe_unused]] WPARAM wParam, LPARAM lParam)
+{
+    const UINT id = MAKEWORD(HIWORD(lParam), LOWORD(lParam));
+    auto hotKeyIt = globalHotkeys_.find(id);
+    if (hotKeyIt != globalHotkeys_.end()) {
+        switch (hotKeyIt->second) {
+            case GlobalHotKey::Mute:
+                muteCtrl_.SetMute(true);
+                break;
+        }
+    }
+    return LRESULT();
+}
+
 LRESULT WinMute::OnUpdatePopup(HWND hWnd, WPARAM, LPARAM lParam)
 {
     // NOTIFYICON_VERSION_4 semantics: LOWORD(lParam) carries the event.
@@ -905,6 +970,9 @@ LRESULT CALLBACK WinMute::WindowProc(HWND hWnd, UINT msg, WPARAM wParam,
         case WM_CLOSE:
             Close();
             return 0;
+        case WM_HOTKEY: {
+            return OnHotKey(hWnd, wParam, lParam);
+        }
         case WM_WTSSESSION_CHANGE: {
             if (wParam == WTS_SESSION_LOCK) {
                 muteCtrl_.NotifyWorkstationLock(true);
index f993ef6757c8beb022270a5c7469a148d8d0b5bc..9a47c05796cd7d9dae73ee6fc07f7e7db798c450 100644 (file)
@@ -66,6 +66,10 @@ private:
       bool muteOnBluetooth;
    } muteConfig_;
 
+   enum class GlobalHotKey {
+       Mute,
+   };
+
    bool wtsSessionNotificationRegistered_ = false;
    UINT_PTR wtsRetryTimerId_ = 0;
    int wtsRetryAttempts_ = 0;
@@ -82,6 +86,8 @@ private:
    BluetoothDetector btDetector_;
    UpdateInfo updateInfo_;
 
+   std::map<LRESULT, GlobalHotKey> globalHotkeys_;
+
    void CheckForUpdates();
    bool RegisterWindowClass();
    bool InitWindow();
@@ -98,10 +104,12 @@ private:
    void ToggleMenuCheck(UINT item, bool* setting) noexcept;
 
    void LoadMainMenuText();
+   void LoadGlobalHotkeys();
 
    // Windows Callback
    LRESULT OnCommand(HWND hWnd, WPARAM wParam, LPARAM lParam);
    LRESULT OnTrayIcon(HWND hWnd, WPARAM wParam, LPARAM lParam);
+   LRESULT OnHotKey(HWND hWnd, WPARAM wParam, LPARAM lParam);
    LRESULT OnSettingChange(HWND hWnd, WPARAM wParam, LPARAM lParam);
    LRESULT OnPowerBroadcast(HWND hWnd, WPARAM wParam, LPARAM lParam);
    LRESULT OnWifiStatusChange(HWND hWnd, WPARAM wParam, LPARAM lParam);