RestoreVolume();
}
}
+
+void MuteControl::SetManagedEndpoints(
+ const std::vector<std::wstring> endpoints,
+ bool isAllowList)
+{
+ winAudio_->SetManagedEndpoints(endpoints, isAllowList);
+}
+
+void MuteControl::ClearManagedEndpoints()
+{
+ winAudio_->MuteSpecificEndpoints(false);
+}
void NotifyShutdown();
void NotifyQuietHours(bool active);
+
+ void SetManagedEndpoints(const std::vector<std::wstring> endpoints,
+ bool isAllowList);
+ void ClearManagedEndpoints();
private:
struct MuteConfig {
bool shouldMute;
static constexpr int BT_DEV_NAME_MAX_LEN = 100;
-struct BtDeviceName {
+struct BtDeviceData {
std::wstring devName;
};
switch (msg) {
case WM_INITDIALOG: {
HWND hBtDevName = GetDlgItem(hDlg, IDC_BT_DEVICE_NAME);
- BtDeviceName* btDeviceData = reinterpret_cast<BtDeviceName*>(lParam);
+ BtDeviceData* btDeviceData = reinterpret_cast<BtDeviceData*>(lParam);
if (btDeviceData == nullptr) {
return FALSE;
}
Edit_ShowBalloonTip(hDevName, &ebt);
} else {
wchar_t devNameBuf[BT_DEV_NAME_MAX_LEN + 1] = { 'L\0' };
- BtDeviceName* btDevName = reinterpret_cast<BtDeviceName*>(GetWindowLongPtr(hDlg, GWLP_USERDATA));
+ BtDeviceData* btDevName = reinterpret_cast<BtDeviceData*>(GetWindowLongPtr(hDlg, GWLP_USERDATA));
if (btDevName != nullptr) {
Edit_GetText(hDevName, devNameBuf, ARRAY_SIZE(devNameBuf));
btDevName->devName = devNameBuf;
Button_Enable(GetDlgItem(hDlg, IDC_BLUETOOTH_REMOVE), entrySelected);
}
} else if (LOWORD(wParam) == IDC_BLUETOOTH_ADD) {
- BtDeviceName btDeviceName;
+ BtDeviceData btDeviceName;
if (DialogBoxParam(
nullptr,
MAKEINTRESOURCE(IDD_SETTINGS_BLUETOOTH_ADD),
if ((textBuf = new wchar_t[static_cast<size_t>(len) + 1]) != nullptr) {
ListBox_GetText(hList, sel, textBuf);
- BtDeviceName btDevName;
+ BtDeviceData btDevName;
btDevName.devName = textBuf;
delete[] textBuf;
+/*
+ WinMute
+ Copyright (c) 2023, Alexander Steinhoefer
+
+-----------------------------------------------------------------------------
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ * Neither the name of the author nor the names of its contributors may
+ be used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+-----------------------------------------------------------------------------
+*/
+
+#include "common.h"
+#include <mmdeviceapi.h>
+#include <Functiondiscoverykeys_devpkey.h>
+
+static constexpr int ENDPOINT_NAME_MAX_LEN = 200;
+
+struct EndpointData {
+ std::wstring name;
+};
+
+_COM_SMARTPTR_TYPEDEF(IPropertyStore, __uuidof(IPropertyStore));
+_COM_SMARTPTR_TYPEDEF(IMMDevice, __uuidof(IMMDevice));
+_COM_SMARTPTR_TYPEDEF(IMMDeviceEnumerator, __uuidof(IMMDeviceEnumerator));
+_COM_SMARTPTR_TYPEDEF(IMMDeviceCollection, __uuidof(IMMDeviceCollection));
+
+static bool GetAudioEndpoints(std::vector<std::wstring>& endpoints)
+{
+ WMLog &log = WMLog::GetInstance();
+
+ IMMDeviceEnumeratorPtr deviceEnumerator;
+ if (FAILED(deviceEnumerator.CreateInstance(
+ __uuidof(MMDeviceEnumerator),
+ nullptr,
+ CLSCTX_INPROC_SERVER))) {
+ return false;
+ }
+ IMMDeviceCollectionPtr audioEndpoints;
+ if (FAILED(deviceEnumerator->EnumAudioEndpoints(
+ eRender,
+ DEVICE_STATE_ACTIVE,
+ &audioEndpoints))) {
+ return false;
+ }
+
+ UINT epCount = 0;
+ if (FAILED(audioEndpoints->GetCount(&epCount))) {
+ return false;
+ }
+
+ for (UINT i = 0; i < epCount; ++i) {
+ IMMDevicePtr device = nullptr;
+ if (FAILED(audioEndpoints->Item(i, &device))) {
+ log.Write(L"Failed to get audio endpoint #%d", i);
+ continue;
+ }
+
+ IPropertyStorePtr propStore = nullptr;
+ if (FAILED(device->OpenPropertyStore(STGM_READ, &propStore))) {
+ log.Write(L"Failed to open property store for audio endpoint #%d", i);
+ continue;
+ }
+
+ PROPVARIANT value;
+ PropVariantInit(&value);
+ if (FAILED(propStore->GetValue(PKEY_Device_FriendlyName, &value))) {
+ log.Write(L"Failed to get device name for audio endpoint #%d", i);
+ continue;
+ }
+
+ wchar_t deviceName[100];
+ StringCchCopy(deviceName,
+ sizeof(deviceName) / sizeof(deviceName[0]),
+ value.pwszVal);
+ PropVariantClear(&value);
+ endpoints.push_back(deviceName);
+ }
+
+ return true;
+}
+
+static INT_PTR CALLBACK Settings_EndpointAddDlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ switch (msg) {
+ case WM_INITDIALOG:
+ {
+ HWND hEndpointName = GetDlgItem(hDlg, IDC_ENDPOINT_NAME);
+ EndpointData *endpointData = reinterpret_cast<EndpointData*>(lParam);
+ if (endpointData == nullptr) {
+ return FALSE;
+ }
+ SetWindowLongPtr(hDlg, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(endpointData));
+
+ if (endpointData->name.length() == 0) {
+ if (!SetWindowTextW(hDlg, L"Add endpoint")) {
+ PrintWindowsError(L"SetWindowText", GetLastError());
+ return FALSE;
+ }
+ } else {
+ if (!SetWindowTextW(hDlg, L"Edit endpoint") ||
+ !SetWindowTextW(GetDlgItem(hDlg, IDC_BT_DEVICE_NAME),
+ endpointData->name.c_str())) {
+ PrintWindowsError(L"SetWindowText", GetLastError());
+ return FALSE;
+ }
+ }
+ // Fill Combobox
+ std::vector<std::wstring> audioEndpoints;
+ if (GetAudioEndpoints(audioEndpoints)) {
+ for (const auto &devName : audioEndpoints) {
+ ComboBox_AddString(hEndpointName, devName.c_str());
+ }
+ }
+
+ Edit_LimitText(hEndpointName, ENDPOINT_NAME_MAX_LEN);
+ if (GetDlgCtrlID(reinterpret_cast<HWND>(wParam)) != IDC_BT_DEVICE_NAME) {
+ SetFocus(hEndpointName);
+ return FALSE;
+ }
+ return TRUE;
+ }
+ case WM_COMMAND:
+ if (LOWORD(wParam) == IDOK) {
+ HWND hEpName = GetDlgItem(hDlg, IDC_ENDPOINT_NAME);
+ const int textLen = Edit_GetTextLength(hEpName);
+ if (textLen == 0) {
+ EDITBALLOONTIP ebt;
+ ZeroMemory(&ebt, sizeof(ebt));
+ ebt.cbStruct = sizeof(ebt);
+ ebt.pszText = L"Please enter a endpoint name";
+ ebt.pszTitle = L"Endpoint Name";
+ ebt.ttiIcon = TTI_INFO;
+ Edit_ShowBalloonTip(hEpName, &ebt);
+ } else {
+ wchar_t epNameBuf[ENDPOINT_NAME_MAX_LEN + 1] = { 'L\0' };
+ EndpointData *epData = reinterpret_cast<EndpointData*>(GetWindowLongPtr(hDlg, GWLP_USERDATA));
+ if (epData != nullptr) {
+ Edit_GetText(hEpName, epNameBuf, ARRAY_SIZE(epNameBuf));
+ epData->name = epNameBuf;
+ EndDialog(hDlg, 0);
+ } else {
+ EndDialog(hDlg, 1);
+ }
+ }
+ } else if (LOWORD(wParam) == IDCANCEL) {
+ EndDialog(hDlg, 1);
+ }
+ return FALSE;
+ case WM_CLOSE:
+ EndDialog(hDlg, 1);
+ return TRUE;
+ default:
+ break;
+ }
+ return FALSE;
+}
+
+static std::vector<std::wstring> ExportEndpointNameList(HWND hList)
+{
+ std::vector<std::wstring> items;
+ DWORD itemCount = ListBox_GetCount(hList);
+ for (DWORD i = 0; i < itemCount; ++i) {
+ wchar_t textBuf[ENDPOINT_NAME_MAX_LEN + 1] = { 0 };
+ DWORD textLen = ListBox_GetTextLen(hList, i);
+ if (textLen < ARRAY_SIZE(textBuf)) {
+ ListBox_GetText(hList, i, textBuf);
+ items.push_back(textBuf);
+ }
+ }
+ return items;
+}
+
+INT_PTR CALLBACK Settings_ManageEndpointsDlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ switch (msg) {
+ case WM_INITDIALOG:
+ {
+ if (IsAppThemed()) {
+ EnableThemeDialogTexture(hDlg, ETDT_ENABLETAB);
+ }
+ WMSettings *settings = reinterpret_cast<WMSettings *>(lParam);
+ assert(settings != nullptr);
+ SetWindowLongPtr(hDlg, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(settings));
+
+ DWORD endpointMode = settings->QueryValue(SettingsKey::MUTE_INDIVIDUAL_ENDPOINTS_MODE);
+ if (endpointMode == MUTE_ENDPOINT_MODE_INDIVIDUAL_ALLOW_LIST) {
+ Button_SetCheck(GetDlgItem(hDlg, IDC_ENDPOINT_LIST_IS_ALLOWLIST), BST_CHECKED);
+ Button_SetCheck(GetDlgItem(hDlg, IDC_ENDPOINT_LIST_IS_BLOCKLIST), BST_UNCHECKED);
+ } else if (endpointMode == MUTE_ENDPOINT_MODE_INDIVIDUAL_BLOCK_LIST) {
+ Button_SetCheck(GetDlgItem(hDlg, IDC_ENDPOINT_LIST_IS_ALLOWLIST), BST_UNCHECKED);
+ Button_SetCheck(GetDlgItem(hDlg, IDC_ENDPOINT_LIST_IS_BLOCKLIST), BST_CHECKED);
+ }
+
+ HWND hList = GetDlgItem(hDlg, IDC_ENDPOINT_LIST);
+ const auto devices = settings->GetManagedAudioEndpoints();
+ for (const auto &dev : devices) {
+ ListBox_AddString(hList, dev.c_str());
+ }
+ Button_Enable(
+ GetDlgItem(hDlg, IDC_ENDPOINT_REMOVEALL),
+ ListBox_GetCount(hList) > 0);
+
+ return TRUE;
+ }
+ case WM_COMMAND:
+ {
+ if (LOWORD(wParam) == IDC_ENDPOINT_LIST) {
+ HWND hList = GetDlgItem(hDlg, IDC_ENDPOINT_LIST);
+ if (HIWORD(wParam) == LBN_SELCHANGE || HIWORD(wParam) == LBN_SELCANCEL) {
+ const bool entrySelected = (ListBox_GetCurSel(hList) != LB_ERR);
+ Button_Enable(GetDlgItem(hDlg, IDC_ENDPOINT_EDIT), entrySelected);
+ Button_Enable(GetDlgItem(hDlg, IDC_ENDPOINT_REMOVE), entrySelected);
+ } else if (HIWORD(wParam) == LBN_KILLFOCUS) {
+ const bool entrySelected = (ListBox_GetCurSel(hList) != LB_ERR);
+ Button_Enable(GetDlgItem(hDlg, IDC_ENDPOINT_EDIT), entrySelected);
+ Button_Enable(GetDlgItem(hDlg, IDC_ENDPOINT_REMOVE), entrySelected);
+ }
+ } else if (LOWORD(wParam) == IDC_ENDPOINT_ADD) {
+ EndpointData epData;
+ if (DialogBoxParam(
+ nullptr,
+ MAKEINTRESOURCE(IDD_MANAGE_ENDPOINTS_ADD),
+ hDlg,
+ Settings_EndpointAddDlgProc,
+ reinterpret_cast<LPARAM>(&epData)) == 0) {
+ std::vector<std::wstring> devices = ExportEndpointNameList(GetDlgItem(hDlg, IDC_ENDPOINT_LIST));
+ if (std::find(begin(devices), end(devices), epData.name) == end(devices)) {
+ ListBox_AddString(
+ GetDlgItem(hDlg, IDC_ENDPOINT_LIST),
+ epData.name.c_str());
+ HWND hRemoveAll = GetDlgItem(hDlg, IDC_ENDPOINT_REMOVEALL);
+ if (!IsWindowEnabled(hRemoveAll)) {
+ Button_Enable(hRemoveAll, TRUE);
+ }
+ }
+ }
+ } else if (LOWORD(wParam) == IDC_ENDPOINT_EDIT) {
+ HWND hList = GetDlgItem(hDlg, IDC_ENDPOINT_LIST);
+ const WPARAM sel = ListBox_GetCurSel(hList);
+ if (sel != LB_ERR) {
+ const int len = ListBox_GetTextLen(hList, sel);
+ wchar_t *textBuf = nullptr;
+ if (len != LB_ERR) {
+ if ((textBuf = new wchar_t[static_cast<size_t>(len) + 1]) != nullptr) {
+ ListBox_GetText(hList, sel, textBuf);
+
+ EndpointData epData;
+ epData.name = textBuf;
+ delete[] textBuf;
+
+ if (DialogBoxParam(
+ nullptr,
+ MAKEINTRESOURCE(IDD_MANAGE_ENDPOINTS_ADD),
+ hDlg,
+ Settings_EndpointAddDlgProc,
+ reinterpret_cast<LPARAM>(&epData)) == 0) {
+ std::vector<std::wstring> networks = ExportEndpointNameList(GetDlgItem(hDlg, IDC_WIFI_LIST));
+ if (std::find(begin(networks), end(networks), epData.name) == end(networks)) {
+ ListBox_InsertString(hList, sel, epData.name.c_str());
+ ListBox_DeleteString(hList, sel + 1);
+ } else {
+ ListBox_DeleteString(hList, sel);
+ }
+ }
+ }
+ }
+ }
+ } else if (LOWORD(wParam) == IDC_ENDPOINT_REMOVE) {
+ HWND hList = GetDlgItem(hDlg, IDC_ENDPOINT_LIST);
+ const WPARAM sel = ListBox_GetCurSel(hList);
+ if (sel != LB_ERR) {
+ ListBox_DeleteString(hList, sel);
+ if (ListBox_GetCount(hList) == 0) {
+ Button_Enable(GetDlgItem(hDlg, IDC_ENDPOINT_EDIT), FALSE);
+ Button_Enable(GetDlgItem(hDlg, IDC_ENDPOINT_REMOVE), FALSE);
+ Button_Enable(GetDlgItem(hDlg, IDC_ENDPOINT_REMOVEALL), FALSE);
+ }
+ }
+ } else if (LOWORD(wParam) == IDC_ENDPOINT_REMOVEALL) {
+ ListBox_ResetContent(GetDlgItem(hDlg, IDC_ENDPOINT_LIST));
+ Button_Enable(GetDlgItem(hDlg, IDC_ENDPOINT_EDIT), FALSE);
+ Button_Enable(GetDlgItem(hDlg, IDC_ENDPOINT_REMOVE), FALSE);
+ Button_Enable(GetDlgItem(hDlg, IDC_ENDPOINT_REMOVEALL), FALSE);
+ } else if (LOWORD(wParam) == IDOK) {
+ WMSettings *settings = reinterpret_cast<WMSettings *>(GetWindowLongPtr(hDlg, GWLP_USERDATA));
+ std::vector<std::wstring> devices = ExportEndpointNameList(GetDlgItem(hDlg, IDC_ENDPOINT_LIST));
+ settings->StoreManagedAudioEndpoints(devices);
+ if (Button_GetCheck(GetDlgItem(hDlg, IDC_ENDPOINT_LIST_IS_ALLOWLIST))) {
+ settings->SetValue(SettingsKey::MUTE_INDIVIDUAL_ENDPOINTS_MODE, MUTE_ENDPOINT_MODE_INDIVIDUAL_ALLOW_LIST);
+ } else if (Button_GetCheck(GetDlgItem(hDlg, IDC_ENDPOINT_LIST_IS_BLOCKLIST))) {
+ settings->SetValue(SettingsKey::MUTE_INDIVIDUAL_ENDPOINTS_MODE, MUTE_ENDPOINT_MODE_INDIVIDUAL_BLOCK_LIST);
+ }
+ EndDialog(hDlg, 0);
+ } else if (LOWORD(wParam) == IDCANCEL) {
+ EndDialog(hDlg, 0);
+ }
+ return 0;
+ }
+ default:
+ break;
+ }
+ return FALSE;
+}
#include "common.h"
+extern INT_PTR CALLBACK Settings_ManageEndpointsDlgProc(HWND, UINT, WPARAM, LPARAM);
+
static void SetCheckButton(HWND hBtn, const WMSettings& settings, SettingsKey key)
{
const DWORD enabled = !!settings.QueryValue(key);
}
HWND hNotify = GetDlgItem(hDlg, IDC_SHOWNOTIFICATIONS);
+ HWND hManageEndpoints = GetDlgItem(hDlg, IDC_MANAGE_AUDIO_ENDPOINTS_INDIVIDUALLY);
HWND hMuteOnLock = GetDlgItem(hDlg, IDC_MUTE_WHEN_WS_LOCKED);
HWND hMuteOnScreenOff = GetDlgItem(hDlg, IDC_MUTE_WHEN_SCREEN_OFF);
// General
SetCheckButton(hNotify, *settings, SettingsKey::NOTIFICATIONS_ENABLED);
+ SetCheckButton(hManageEndpoints, *settings, SettingsKey::MUTE_INDIVIDUAL_ENDPOINTS);
+ Button_Enable(GetDlgItem(hDlg, IDC_MANAGE_ENDPOINTS), Button_GetCheck(hManageEndpoints) == BST_CHECKED);
// With restore
SetCheckButton(hMuteOnLock, *settings, SettingsKey::MUTE_ON_LOCK);
return TRUE;
}
+ case WM_COMMAND:
+ if (LOWORD(wParam) == IDC_MANAGE_AUDIO_ENDPOINTS_INDIVIDUALLY) {
+ int isEnabled = Button_GetCheck(GetDlgItem(hDlg, IDC_MANAGE_AUDIO_ENDPOINTS_INDIVIDUALLY));
+ Button_Enable(GetDlgItem(hDlg, IDC_MANAGE_ENDPOINTS), isEnabled);
+ } else if (LOWORD(wParam) == IDC_MANAGE_ENDPOINTS) {
+ WMSettings *settings = reinterpret_cast<WMSettings *>(GetWindowLongPtr(hDlg, GWLP_USERDATA));
+ if (!DialogBoxParam(
+ nullptr,
+ MAKEINTRESOURCE(IDD_MANAGE_ENDPOINTS),
+ hDlg,
+ Settings_ManageEndpointsDlgProc,
+ reinterpret_cast<LPARAM>(settings)) == 0) {
+ PrintWindowsError(L"DialogBoxParam", GetLastError());
+ }
+ }
+ return 0;
case WM_SAVESETTINGS:
{
WMSettings* settings = reinterpret_cast<WMSettings*>(GetWindowLongPtr(hDlg, GWLP_USERDATA));
HWND hNotify = GetDlgItem(hDlg, IDC_SHOWNOTIFICATIONS);
+ HWND hManageEndpoints = GetDlgItem(hDlg, IDC_MANAGE_AUDIO_ENDPOINTS_INDIVIDUALLY);
HWND hMuteOnLock = GetDlgItem(hDlg, IDC_MUTE_WHEN_WS_LOCKED);
HWND hMuteOnScreenOff = GetDlgItem(hDlg, IDC_MUTE_WHEN_SCREEN_OFF);
// General
SetOption(hNotify, *settings, SettingsKey::NOTIFICATIONS_ENABLED);
+ SetOption(hManageEndpoints, *settings, SettingsKey::MUTE_INDIVIDUAL_ENDPOINTS);
// With restore
SetOption(hMuteOnLock, *settings, SettingsKey::MUTE_ON_LOCK);
deviceEnumerator_(nullptr),
mmnAudioEvents_(nullptr),
reInit_(false),
+ muteSpecificEndpoints_(false),
+ muteSpecificEndpointsAllowList_(false),
hParent_(nullptr)
{
if (FAILED(CoInitialize(nullptr))) {
WMLog& log = WMLog::GetInstance();
for (auto& e : endpoints_) {
+ if (!IsEndpointManaged(e->deviceName)) {
+ log.Write(L"Skipping Endpoint %s", e->deviceName);
+ continue;
+ }
log.Write(L"Restoring: Mute %s for \"%s\"",
(e->wasMuted) ? L"true" : L"false",
e->deviceName);
if (CheckForReInit()) {
for (auto& e : endpoints_) {
BOOL isMuted = !mute;
+ if (!IsEndpointManaged(e->deviceName)) {
+ log.Write(L"Skipping Endpoint %s", e->deviceName);
+ continue;
+ }
if (FAILED(e->endpointVolume->GetMute(&isMuted))) {
log.Write(L"Failed to get mute status for \"%s\"",
e->deviceName);
}
}
}
+
+bool VistaAudio::IsEndpointManaged(const std::wstring &endpointName) const
+{
+ if (!muteSpecificEndpoints_) {
+ return true;
+ }
+
+ const bool inManagedEndpoints = std::find(
+ std::begin(managedEndpointNames_),
+ std::end(managedEndpointNames_),
+ endpointName) != std::end(managedEndpointNames_);
+
+ // ----------------------------
+ // | In List | Not in List |
+ // ------------+----------+-------------+
+ // Allow List | Mute | Not mute |
+ // Block List | Not mute | Mute |
+ return inManagedEndpoints
+ ? muteSpecificEndpointsAllowList_
+ : !muteSpecificEndpointsAllowList_;
+}
+
+void VistaAudio::MuteSpecificEndpoints(bool muteSpecific)
+{
+ muteSpecificEndpoints_ = muteSpecific;
+}
+
+void VistaAudio::SetManagedEndpoints(
+ const std::vector<std::wstring> &endpoints,
+ bool isAllowList)
+{
+ managedEndpointNames_ = endpoints;
+ muteSpecificEndpointsAllowList_ = isAllowList;
+}
+
+
#include "common.h"
+static constexpr int CURRENT_SETTINGS_VERSION = 0;
+
static const wchar_t *LX_SYSTEMS_SUBKEY
= L"SOFTWARE\\lx-systems\\WinMute";
static const wchar_t* LX_SYSTEMS_WIFI_SUBKEY
{
const wchar_t* keyStr = nullptr;
switch (key) {
+ case SettingsKey::SETTINGS_VERSION:
+ keyStr = L"SettingsVersion";
+ break;
case SettingsKey::MUTE_ON_LOCK:
keyStr = L"MuteOnLock";
break;
case SettingsKey::MUTE_INDIVIDUAL_ENDPOINTS:
keyStr = L"MuteIndividualEndpoints";
break;
+ case SettingsKey::MUTE_INDIVIDUAL_ENDPOINTS_MODE:
+ keyStr = L"MuteIndividualEndpointsMode";
+ break;
case SettingsKey::QUIETHOURS_ENABLE:
keyStr = L"QuietHoursEnabled";
break;
static DWORD GetDefaultSetting(SettingsKey key)
{
switch (key) {
+ case SettingsKey::SETTINGS_VERSION:
+ return 0;
case SettingsKey::MUTE_ON_LOCK:
return 1;
case SettingsKey::MUTE_ON_DISPLAYSTANDBY:
return 0;
case SettingsKey::MUTE_INDIVIDUAL_ENDPOINTS:
return 0;
+ case SettingsKey::MUTE_INDIVIDUAL_ENDPOINTS_MODE:
+ return MUTE_ENDPOINT_MODE_INDIVIDUAL_ALLOW_LIST;
case SettingsKey::QUIETHOURS_ENABLE:
return 0;
case SettingsKey::QUIETHOURS_FORCEUNMUTE:
Unload();
}
+bool WMSettings::MigrateSettings()
+{
+ DWORD version = QueryValue(SettingsKey::SETTINGS_VERSION);
+ if (version != CURRENT_SETTINGS_VERSION) {
+ WMLog::GetInstance().Write(L"Unexpected settings version: %d", version);
+ }
+ return true;
+}
+
bool WMSettings::Init()
{
if (hSettingsKey_ == nullptr) {
0,
KEY_READ | KEY_WRITE,
nullptr,
- &hBluetoothKey_,
+ &hAudioEndpointsKey_,
nullptr);
if (regError != ERROR_SUCCESS) {
PrintWindowsError(L"RegCreateKeyEx", regError);
}
}
+ MigrateSettings();
+
return true;
}
#include "common.h"
+enum MuteEndPointMode {
+ MUTE_ENDPOINT_MODE_INDIVIDUAL_ALLOW_LIST = 0,
+ MUTE_ENDPOINT_MODE_INDIVIDUAL_BLOCK_LIST = 1
+};
+
enum class SettingsKey {
- MUTE_ON_LOCK
+ SETTINGS_VERSION
+ , MUTE_ON_LOCK
, MUTE_ON_DISPLAYSTANDBY
, MUTE_ON_RDP
, RESTORE_AUDIO
, MUTE_ON_BLUETOOTH_DEVICELIST
, MUTE_ON_WLAN
, MUTE_ON_WLAN_ALLOWLIST
- // 0 = mute all, 1 = mute specific (allowlist), 2 = mute specific (blocklist)
- , MUTE_INDIVIDUAL_ENDPOINTS
+ , MUTE_INDIVIDUAL_ENDPOINTS
+ // 1 = mute specific (allowlist), 2 = mute specific (blocklist)
+ , MUTE_INDIVIDUAL_ENDPOINTS_MODE
, QUIETHOURS_ENABLE
, QUIETHOURS_FORCEUNMUTE
, QUIETHOURS_NOTIFICATIONS
HKEY hBluetoothKey_;
HKEY hAudioEndpointsKey_;
+ bool MigrateSettings();
HKEY OpenAutostartKey(REGSAM samDesired);
};
virtual bool SaveMuteStatus() = 0;
virtual bool RestoreMuteStatus() = 0;
virtual void SetMute(bool mute) = 0;
+ virtual void MuteSpecificEndpoints(bool muteSpecific) = 0;
+ virtual void SetManagedEndpoints(
+ const std::vector<std::wstring> &endpoints,
+ bool isAllowList) = 0;
virtual ~WinAudio() noexcept {};
};
bool RestoreMuteStatus() override;
void SetMute(bool mute) override;
+ void MuteSpecificEndpoints(bool muteSpecific);
+ void SetManagedEndpoints(
+ const std::vector<std::wstring> &endpoints,
+ bool isAllowList);
+
private:
void Uninit();
bool CheckForReInit();
bool LoadAllEndpoints();
+ bool IsEndpointManaged(const std::wstring& endpointName) const;
std::vector<std::unique_ptr<Endpoint>> endpoints_;
MMNotificationClient* mmnAudioEvents_;
IMMDeviceEnumeratorPtr deviceEnumerator_;
bool reInit_;
+ bool muteSpecificEndpoints_;
+ bool muteSpecificEndpointsAllowList_;
HWND hParent_;
+ std::vector<std::wstring> managedEndpointNames_;
+
// non copy-able
VistaAudio(const VistaAudio& other) = delete;
VistaAudio& operator=(const VistaAudio& other) = delete;
log.Write(L"\t\tUse devicelist: %s", settings_.QueryValue(SettingsKey::MUTE_ON_BLUETOOTH_DEVICELIST) ? L"Yes" : L"No");
log.Write(L"\tMute on WLAN: %s", settings_.QueryValue(SettingsKey::MUTE_ON_WLAN) ? L"Yes" : L"No");
log.Write(L"\t\tUse allowlist: %s", settings_.QueryValue(SettingsKey::MUTE_ON_WLAN_ALLOWLIST) ? L"Yes" : L"No");
+ log.Write(L"\tMute specific endpoints only: %s", settings_.QueryValue(SettingsKey::MUTE_INDIVIDUAL_ENDPOINTS) ? L"Yes" : L"No");
}
+ if (!settings_.QueryValue(SettingsKey::MUTE_INDIVIDUAL_ENDPOINTS)) {
+ muteCtrl_.ClearManagedEndpoints();
+ } else {
+ const auto endpoints = settings_.GetManagedAudioEndpoints();
+ const bool isAllowList = settings_.QueryValue(SettingsKey::MUTE_INDIVIDUAL_ENDPOINTS_MODE) == MUTE_ENDPOINT_MODE_INDIVIDUAL_ALLOW_LIST;
+ muteCtrl_.SetManagedEndpoints(endpoints, isAllowList);
+ }
muteCtrl_.SetRestoreVolume(settings_.QueryValue(SettingsKey::RESTORE_AUDIO));
muteCtrl_.SetMuteOnWorkstationLock(settings_.QueryValue(SettingsKey::MUTE_ON_LOCK));
muteCtrl_.SetMuteOnDisplayStandby(settings_.QueryValue(SettingsKey::MUTE_ON_DISPLAYSTANDBY));
BEGIN
GROUPBOX "General",IDC_STATIC,7,7,228,57
CONTROL "Show mute event notifications",IDC_SHOWNOTIFICATIONS,
- "Button",BS_AUTOCHECKBOX | WS_TABSTOP,13,17,219,18
+ "Button",BS_AUTOCHECKBOX | WS_TABSTOP,13,15,219,18
GROUPBOX "Mute with volume restore, when...",IDC_STATIC,7,67,228,57
CONTROL "... the workstation is locked.",IDC_MUTE_WHEN_WS_LOCKED,
"Button",BS_AUTOCHECKBOX | WS_TABSTOP,20,81,107,10
CONTROL "... when WinMute is started within a RDP session.",IDC_MUTE_WHEN_RDP_SESSION,
"Button",BS_AUTOCHECKBOX | WS_TABSTOP,20,180,175,10
CONTROL "Manage audio endpoints individually",IDC_MANAGE_AUDIO_ENDPOINTS_INDIVIDUALLY,
- "Button",BS_AUTOCHECKBOX | WS_TABSTOP,13,31,219,18
- PUSHBUTTON "Manage endpoints...",IDC_MANAGE_ENDPOINTS,26,47,74,14
+ "Button",BS_AUTOCHECKBOX | WS_TABSTOP,13,28,219,18
+ PUSHBUTTON "Manage endpoints...",IDC_MANAGE_ENDPOINTS,26,46,74,15
END
IDD_SETTINGS_WIFI DIALOGEX 0, 0, 242, 204
COMBOBOX IDC_BT_DEVICE_NAME,7,22,187,17,CBS_DROPDOWN | CBS_SORT | WS_VSCROLL | WS_TABSTOP
END
+IDD_MANAGE_ENDPOINTS DIALOGEX 0, 0, 241, 200
+STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "Manage Endpoints"
+FONT 8, "MS Shell Dlg", 400, 0, 0x1
+BEGIN
+ DEFPUSHBUTTON "OK",IDOK,133,179,50,14
+ PUSHBUTTON "Cancel",IDCANCEL,184,179,50,14
+ LISTBOX IDC_ENDPOINT_LIST,13,60,159,110,LBS_SORT | LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_TABSTOP
+ PUSHBUTTON "Add",IDC_ENDPOINT_ADD,175,60,56,16
+ PUSHBUTTON "Edit",IDC_ENDPOINT_EDIT,175,78,56,16
+ PUSHBUTTON "Remove",IDC_ENDPOINT_REMOVE,175,97,56,16
+ PUSHBUTTON "Remove all",IDC_ENDPOINT_REMOVEALL,175,120,56,16
+ CONTROL "Mute only listed endpoints",IDC_ENDPOINT_LIST_IS_ALLOWLIST,
+ "Button",BS_AUTORADIOBUTTON,13,19,99,10
+ CONTROL "Mute all but the listed endpoints",IDC_ENDPOINT_LIST_IS_BLOCKLIST,
+ "Button",BS_AUTORADIOBUTTON,13,32,118,10
+ GROUPBOX "List behaviour",IDC_STATIC,7,7,227,41
+ GROUPBOX "Endpoints",IDC_STATIC,7,48,227,128
+END
+
+IDD_MANAGE_ENDPOINTS_ADD DIALOGEX 0, 0, 201, 62
+STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "Add Endpoint"
+FONT 8, "MS Shell Dlg", 400, 0, 0x1
+BEGIN
+ DEFPUSHBUTTON "Save",IDOK,91,41,50,14
+ PUSHBUTTON "Cancel",IDCANCEL,144,41,50,14
+ LTEXT "Endpoint name:",IDC_STATIC,7,7,187,14
+ COMBOBOX IDC_ENDPOINT_NAME,7,22,187,17,CBS_DROPDOWN | CBS_SORT | WS_VSCROLL | WS_TABSTOP
+END
+
/////////////////////////////////////////////////////////////////////////////
//
TOPMARGIN, 7
BOTTOMMARGIN, 55
END
+
+ IDD_MANAGE_ENDPOINTS, DIALOG
+ BEGIN
+ LEFTMARGIN, 7
+ RIGHTMARGIN, 234
+ TOPMARGIN, 7
+ BOTTOMMARGIN, 193
+ END
+
+ IDD_MANAGE_ENDPOINTS_ADD, DIALOG
+ BEGIN
+ LEFTMARGIN, 7
+ RIGHTMARGIN, 194
+ TOPMARGIN, 7
+ BOTTOMMARGIN, 55
+ END
END
#endif // APSTUDIO_INVOKED
0
END
+IDD_MANAGE_ENDPOINTS AFX_DIALOG_LAYOUT
+BEGIN
+ 0
+END
+
+IDD_MANAGE_ENDPOINTS_ADD AFX_DIALOG_LAYOUT
+BEGIN
+ 0
+END
+
/////////////////////////////////////////////////////////////////////////////
//
//
VS_VERSION_INFO VERSIONINFO
- FILEVERSION 2,2,0,0
- PRODUCTVERSION 2,2,0,0
+ FILEVERSION 2,3,0,0
+ PRODUCTVERSION 2,3,0,0
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L
BEGIN
VALUE "CompanyName", "LX-Systems"
VALUE "FileDescription", "WinMute"
- VALUE "FileVersion", "2.2.0.0"
+ VALUE "FileVersion", "2.3.0.0"
VALUE "InternalName", "WinMute.exe"
VALUE "LegalCopyright", "Copyright (C) 2022, Alexander Steinhöfer"
VALUE "OriginalFilename", "WinMute.exe"
VALUE "ProductName", "WinMute"
- VALUE "ProductVersion", "2.2.0.0"
+ VALUE "ProductVersion", "2.3.0.0"
END
BLOCK "040704b0"
BEGIN
VALUE "CompanyName", "LX-Systems"
VALUE "FileDescription", "WinMute"
- VALUE "FileVersion", "2.2.0.0"
+ VALUE "FileVersion", "2.3.0.0"
VALUE "InternalName", "WinMute.exe"
- VALUE "LegalCopyright", "Copyright (C) 2022, Alexander Steinhöfer"
+ VALUE "LegalCopyright", "Copyright (C) 2023, Alexander Steinhöfer"
VALUE "OriginalFilename", "WinMute.exe"
VALUE "ProductName", "WinMute"
- VALUE "ProductVersion", "2.2.0.0"
+ VALUE "ProductVersion", "2.3.0.0"
END
END
BLOCK "VarFileInfo"
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader>
</ClCompile>
<ClCompile Include="Settings_BluetoothDlg.cpp" />
+ <ClCompile Include="Settings_ManageEndpointsDlg.cpp" />
<ClCompile Include="Settings_MuteDlg.cpp" />
<ClCompile Include="Settings_WifiDlg.cpp" />
<ClCompile Include="WifiDetector.cpp" />
<ClCompile Include="BluetoothDetector.cpp">
<Filter>Source Files\Controllers\Bluetooth</Filter>
</ClCompile>
+ <ClCompile Include="Settings_ManageEndpointsDlg.cpp">
+ <Filter>Source Files\UI\Dialogs\Settings</Filter>
+ </ClCompile>
</ItemGroup>
</Project>
\ No newline at end of file
#define IDD_DIALOG1 133
#define IDD_SETTINGS_BLUETOOTH_ADD 133
#define IDD_MANAGE_ENDPOINTS 135
+#define IDD_DIALOG2 137
+#define IDD_MANAGE_ENDPOINTS_ADD 137
#define IDC_LINK_HOMEPAGE 1001
#define IDC_LINK_SUPPORT 1002
#define IDC_LINK_PROJECT 1002
#define IDC_BUTTON1 1041
#define IDC_MANAGE_ENDPOINTS 1041
#define IDC_ENDPOINT_LIST_IS_ALLOWLIST 1042
-#define IDC_MUTE_SPECIFIC_ENDPOINTS2 1043
#define IDC_ENDPOINT_LIST_IS_BLOCKLIST 1043
+#define IDC_ENDPOINT_LIST 1044
+#define IDC_ENDPOINT_ADD 1045
+#define IDC_ENDPOINT_EDIT 1046
+#define IDC_ENDPOINT_REMOVE 1047
+#define IDC_ENDPOINT_REMOVEALL 1048
+#define IDC_ENDPOINT_NAME 1049
#define ID_TRAYMENU_INFO 40001
#define ID_TRAYMENU_ 40002
#define ID_TRAYMENU_MUTEON 40003
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
-#define _APS_NEXT_RESOURCE_VALUE 137
+#define _APS_NEXT_RESOURCE_VALUE 139
#define _APS_NEXT_COMMAND_VALUE 40037
-#define _APS_NEXT_CONTROL_VALUE 1043
+#define _APS_NEXT_CONTROL_VALUE 1050
#define _APS_NEXT_SYMED_VALUE 101
#endif
#endif