"general.error.winapi.text": "{} failed with error {}: {}",
"general.error.audio-service-shutdown.title": "The audio service has been shut down.",
"general.error.audio-service-shutdown.text": "WinMute is not able to recover from that condition.\nPlease try to restart the program.",
+ "popup.update-available.title": "WinMute {} is available!",
+ "popup.update-available.text": "Your current version is {}.\nClick on this message to open the download page.",
+ "popup.error.update-check-failed.title": "Update check failed.",
+ "pupup.error.update-check-failed.text": "Unable to check for a new version.\nPlease check/enable logging for further details.",
"popup.remote-session-detected.title": "Remote Session detected",
"popup.remote-session-detected.text": "All audio endpoints have been muted",
"popup.bluetooth-muting-disabled.title": "Bluetooth muting disabled",
ChangeText();
}
-void TrayIcon::ShowPopup(const std::wstring& title, const std::wstring& text) const
+void TrayIcon::ShowPopup(
+ const std::wstring& title,
+ const std::wstring& text,
+ int callbackID) const
{
if (!initialized_) {
TrayIconPopup popup;
popupQueue_.push_back(popup);
return;
}
- NOTIFYICONDATA tnid{0};
- tnid.cbSize = sizeof(NOTIFYICONDATA);
+ NOTIFYICONDATAW tnid{0};
+ tnid.cbSize = sizeof(tnid);
tnid.uVersion = NOTIFYICON_VERSION_4;
tnid.hWnd = hWnd_;
tnid.uID = trayID_;
- tnid.uFlags = NIF_INFO;
+ tnid.uFlags = NIF_INFO | NIF_SHOWTIP;
tnid.dwInfoFlags = NIIF_INFO | NIIF_NOSOUND | NIIF_LARGE_ICON | NIIF_RESPECT_QUIET_TIME;
+ if (callbackID != 0) {
+ tnid.uCallbackMessage = callbackID;
+ tnid.uFlags |= NIF_MESSAGE;
+ }
tnid.uTimeout = 10 * 1000;
StringCchCopy(tnid.szInfoTitle, ARRAY_SIZE(tnid.szInfoTitle), title.c_str());
StringCchCopy(tnid.szInfo, ARRAY_SIZE(tnid.szInfo), text.c_str());
bool TrayIcon::AddNotifyIcon()
{
- NOTIFYICONDATA tnid{ 0 };
- tnid.cbSize = sizeof(NOTIFYICONDATA);
+ NOTIFYICONDATAW tnid{ 0 };
+ tnid.cbSize = sizeof(tnid);
tnid.uVersion = NOTIFYICON_VERSION_4;
tnid.hWnd = hWnd_;
tnid.uID = trayID_;
- tnid.uFlags = NIF_ICON | NIF_TIP | NIF_MESSAGE;
+ tnid.uFlags = NIF_ICON | NIF_TIP | NIF_MESSAGE | NIF_SHOWTIP;
tnid.hIcon = hIcon_;
tnid.uCallbackMessage = WM_TRAYICON;
bool TrayIcon::RemoveNotifyIcon()
{
- NOTIFYICONDATA tnid{0};
- tnid.cbSize = sizeof(NOTIFYICONDATA);
+ NOTIFYICONDATAW tnid{0};
+ tnid.cbSize = sizeof(tnid);
tnid.uVersion = NOTIFYICON_VERSION_4;
tnid.hWnd = hWnd_;
tnid.uID = trayID_;
bool TrayIcon::ChangeText()
{
- NOTIFYICONDATA tnid{0};
- tnid.cbSize = sizeof(NOTIFYICONDATA);
+ NOTIFYICONDATAW tnid{0};
+ tnid.cbSize = sizeof(tnid);
tnid.uVersion = NOTIFYICON_VERSION_4;
tnid.hWnd = hWnd_;
tnid.uID = trayID_;
#include "common.h"
-#define WM_TRAYICON (WM_APP + 700)
+constexpr int WM_TRAYICON = WM_APP + 700;
struct TrayIconPopup {
std::wstring title;
bool IsShown() const noexcept { return iconVisible_; }
void ChangeIcon(HICON hNewIcon);
void ChangeText(const std::wstring& tooltip);
- void ShowPopup(const std::wstring& title, const std::wstring& text) const;
+ void ShowPopup(
+ const std::wstring& title,
+ const std::wstring& text,
+ int callbackID = 0) const;
private:
void DestroyTrayIcon();
return true;
}
-bool UpdateChecker::ParseVersion(const std::wstring &vers, std::vector<int> parsedVers) const
+bool UpdateChecker::ParseVersion(const std::wstring &vers, std::vector<int>& parsedVers) const
{
size_t lastVersionPos = 0;
size_t curPos = 0;
lastVersionPos = curPos + 1;
}
}
- auto pos = vers.find('.');
- if (pos != std::wstring::npos) {
- parsedVers.push_back(std::stoi(vers));
- }
return true;
}
{
WMLog &log = WMLog::GetInstance();
-
- URL_COMPONENTS updateUrl;
+ URL_COMPONENTS updateUrl{0};
updateUrl.dwStructSize = sizeof(updateUrl);
- if (!WinHttpCrackUrl(UPDATE_FILE_URL, 0, ICU_ESCAPE, &updateUrl)) {
+ updateUrl.dwSchemeLength = (DWORD)-1;
+ updateUrl.dwHostNameLength = (DWORD)-1;
+ updateUrl.dwUrlPathLength = (DWORD)-1;
+ if (!WinHttpCrackUrl(UPDATE_FILE_URL, 0, 0, &updateUrl)) {
log.WriteWindowsError(L"WinHttpCrackUrl", GetLastError());
return false;
}
HInternetHolder hConnect = WinHttpConnect(
hSession,
updateHost.c_str(),
- INTERNET_DEFAULT_HTTPS_PORT,
+ (updateUrl.nScheme == INTERNET_SCHEME_HTTPS) ? INTERNET_DEFAULT_HTTPS_PORT : INTERNET_DEFAULT_HTTP_PORT,
0);
if (hConnect == nullptr) {
log.WriteWindowsError(L"WinHttpConnect", GetLastError());
bool GetVersionFile(std::string &fileContents) const;
bool ParseVersionFile(const std::string &fileContents, UpdateInfo& updateInfo) const;
std::optional<bool> IsVersionGreater(const std::wstring &newVers, const std::wstring &curVers) const;
- bool ParseVersion(const std::wstring &vers, std::vector<int> parsedVers) const;
+ bool ParseVersion(const std::wstring &vers, std::vector<int>& parsedVers) const;
};
return TRUE;
}
-static DWORD CheckForUpdates_Thread(LPVOID param)
-{
- auto updateChecker = reinterpret_cast<UpdateChecker *>(param);
- if (updateChecker == nullptr) {
- return 1;
- }
- UpdateInfo updateInfo;
- if (!updateChecker->GetUpdateInfo(updateInfo)) {
- // Print Error
- } else {
- // Print Update Message
- }
-
- delete updateChecker;
- return 0;
-}
-
-static void CheckForUpdates(const WMSettings& settings)
-{
- UpdateChecker *updateChecker = new UpdateChecker();
- if (updateChecker == nullptr) {
- return;
- }
- if (!updateChecker->IsUpdateCheckEnabled(settings)) {
- delete updateChecker;
- return;
- }
- auto updateThread = CreateThread(
- nullptr, 0, CheckForUpdates_Thread,
- reinterpret_cast<LPVOID>(updateChecker),
- 0, nullptr);
- if (updateThread == nullptr) {
- delete updateChecker;
- return;
- }
-
-}
-
int WINAPI wWinMain(
_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE,
return FALSE;
}
- CheckForUpdates(settings);
-
MSG msg = { nullptr };
WinMute program(settings);
if (program.Init()) {
return isRemoteable;
}
+
WinMute::MuteConfig::MuteConfig()
: muteOnWlan(false), muteOnBluetooth(false), showNotifications(false)
{
hAppIcon_(nullptr),
hTrayIcon_(nullptr),
settings_(settings),
- i18n(WMi18n::GetInstance())
+ i18n_(WMi18n::GetInstance())
{
}
TaskDialog(
nullptr, nullptr,
PROGRAM_NAME,
- i18n.GetTranslationW("init.error.winmute.platform-support.title").c_str(),
- i18n.GetTranslationW("init.error.winmute.platform-support.text").c_str(),
+ i18n_.GetTranslationW("init.error.winmute.platform-support.title").c_str(),
+ i18n_.GetTranslationW("init.error.winmute.platform-support.text").c_str(),
TDCBF_OK_BUTTON, TD_ERROR_ICON, nullptr);
return false;
}
if (settings_.QueryValue(SettingsKey::MUTE_ON_RDP)
&& IsCurrentSessionRemoteable()) {
trayIcon_.ShowPopup(
- i18n.GetTranslationW("popup.remote-session-detected.title"),
- i18n.GetTranslationW("popup.remote-session-detected.text"));
+ i18n_.GetTranslationW("popup.remote-session-detected.title"),
+ i18n_.GetTranslationW("popup.remote-session-detected.text"));
muteCtrl_.SetMute(true);
}
+ CheckForUpdates();
+
return true;
}
} else {
if (!btDetector_.Init(hWnd_)) {
trayIcon_.ShowPopup(
- i18n.GetTranslationW("popup.bluetooth-muting-disabled.title"),
- i18n.GetTranslationW("popup.bluetooth-muting-disabled.text"));
+ i18n_.GetTranslationW("popup.bluetooth-muting-disabled.title"),
+ i18n_.GetTranslationW("popup.bluetooth-muting-disabled.text"));
settings_.SetValue(SettingsKey::MUTE_ON_BLUETOOTH, FALSE);
} else {
muteCtrl_.SetMuteOnBluetoothDisconnect(true);
} else {
if (!wifiDetector_.Init(hWnd_)) {
trayIcon_.ShowPopup(
- i18n.GetTranslationW("popup.wlan-muting-disabled.title"),
- i18n.GetTranslationW("popup.wlan-muting-disabled.text"));
+ i18n_.GetTranslationW("popup.wlan-muting-disabled.title"),
+ i18n_.GetTranslationW("popup.wlan-muting-disabled.text"));
settings_.SetValue(SettingsKey::MUTE_ON_WLAN, FALSE);
} else {
const bool isMuteList = !settings_.QueryValue(SettingsKey::MUTE_ON_WLAN_ALLOWLIST);
void WinMute::LoadMainMenuText()
{
std::map<UINT, std::wstring> menuText;
- menuText[ID_TRAYMENU_INFO] = i18n.GetTranslationW("traymenu.info");
- menuText[ID_TRAYMENU_LABEL_MUTEWHEN] = i18n.GetTranslationW("traymenu.mute-when");
- menuText[ID_TRAYMENU_MUTEONLOCK] = i18n.GetTranslationW("traymenu.mute-on-lock");
- menuText[ID_TRAYMENU_MUTEONSCREENSUSPEND] = i18n.GetTranslationW("traymenu.mute-on-screen-suspend");
- menuText[ID_TRAYMENU_RESTOREAUDIO] = i18n.GetTranslationW("traymenu.restore-volume");
- menuText[ID_TRAYMENU_LABEL_MUTEON_NO_RESTORE] = i18n.GetTranslationW("traymenu.mute-no-restore");
- menuText[ID_TRAYMENU_MUTEONSHUTDOWN] = i18n.GetTranslationW("traymenu.mute-on-shutdown");
- menuText[ID_TRAYMENU_MUTEONSUSPEND] = i18n.GetTranslationW("traymenu.mute-on-sleep");
- menuText[ID_TRAYMENU_MUTEONLOGOUT] = i18n.GetTranslationW("traymenu.mute-on-logout");
- menuText[ID_TRAYMENU_MUTE] = i18n.GetTranslationW("traymenu.mute-all-devices");
- menuText[ID_TRAYMENU_SETTINGS] = i18n.GetTranslationW("traymenu.settings");
- menuText[ID_TRAYMENU_EXIT] = i18n.GetTranslationW("traymenu.exit");
+ menuText[ID_TRAYMENU_INFO] = i18n_.GetTranslationW("traymenu.info");
+ menuText[ID_TRAYMENU_LABEL_MUTEWHEN] = i18n_.GetTranslationW("traymenu.mute-when");
+ menuText[ID_TRAYMENU_MUTEONLOCK] = i18n_.GetTranslationW("traymenu.mute-on-lock");
+ menuText[ID_TRAYMENU_MUTEONSCREENSUSPEND] = i18n_.GetTranslationW("traymenu.mute-on-screen-suspend");
+ menuText[ID_TRAYMENU_RESTOREAUDIO] = i18n_.GetTranslationW("traymenu.restore-volume");
+ menuText[ID_TRAYMENU_LABEL_MUTEON_NO_RESTORE] = i18n_.GetTranslationW("traymenu.mute-no-restore");
+ menuText[ID_TRAYMENU_MUTEONSHUTDOWN] = i18n_.GetTranslationW("traymenu.mute-on-shutdown");
+ menuText[ID_TRAYMENU_MUTEONSUSPEND] = i18n_.GetTranslationW("traymenu.mute-on-sleep");
+ menuText[ID_TRAYMENU_MUTEONLOGOUT] = i18n_.GetTranslationW("traymenu.mute-on-logout");
+ menuText[ID_TRAYMENU_MUTE] = i18n_.GetTranslationW("traymenu.mute-all-devices");
+ menuText[ID_TRAYMENU_SETTINGS] = i18n_.GetTranslationW("traymenu.settings");
+ menuText[ID_TRAYMENU_EXIT] = i18n_.GetTranslationW("traymenu.exit");
for (const auto& mt : menuText) {
MENUITEMINFO mii{ sizeof(MENUITEMINFO) };
}
}
+void WinMute::CheckForUpdates()
+{
+ auto updateChecker = std::make_unique<UpdateChecker>();
+ if (!updateChecker ||
+ !updateChecker->IsUpdateCheckEnabled(settings_)) {
+ return;
+ }
+ std::thread updateThread(&WinMute::CheckForUpdatesAsync, this, std::move(updateChecker));
+ updateThread.detach();
+}
+
+void WinMute::CheckForUpdatesAsync(std::unique_ptr<UpdateChecker> updateChecker)
+{
+ if (!updateChecker) {
+ return;
+ }
+ if (!updateChecker->GetUpdateInfo(updateInfo_)) {
+ trayIcon_.ShowPopup(
+ i18n_.GetTranslationW("popup.error.update-check-failed.title"),
+ i18n_.GetTranslationW("popup.error.update-check-failed.text"));
+ } else if (updateInfo_.shouldUpdate) {
+ const std::wstring popupTitle = std::vformat(
+ i18n_.GetTranslationW("popup.update-available.title"),
+ std::make_wformat_args(updateInfo_.newVersion));
+ const std::wstring popupText = std::vformat(
+ i18n_.GetTranslationW("popup.update-available.text"),
+ std::make_wformat_args(updateInfo_.currentVersion));
+ trayIcon_.ShowPopup(popupTitle, popupText, WM_WINMUTE_UPDATE_POPUP);
+ }
+}
+
LRESULT WinMute::OnCommand(HWND hWnd, WPARAM wParam, LPARAM)
{
switch (LOWORD(wParam)) {
muteCtrl_.NotifyQuietHours(true);
if (settings_.QueryValue(SettingsKey::QUIETHOURS_NOTIFICATIONS)) {
trayIcon_.ShowPopup(
- i18n.GetTranslationW("popup.quiet-hours-started.title"),
- i18n.GetTranslationW("popup.quiet-hours-started.text"));
+ i18n_.GetTranslationW("popup.quiet-hours-started.title"),
+ i18n_.GetTranslationW("popup.quiet-hours-started.text"));
}
quietHours_.SetEnd();
return 0;
muteCtrl_.NotifyQuietHours(false);
if (settings_.QueryValue(SettingsKey::QUIETHOURS_NOTIFICATIONS)) {
trayIcon_.ShowPopup(
- i18n.GetTranslationW("popup.quiet-hours-ended.title"),
- i18n.GetTranslationW("popup.quiet-hours-ended.text"));
+ i18n_.GetTranslationW("popup.quiet-hours-ended.title"),
+ i18n_.GetTranslationW("popup.quiet-hours-ended.text"));
}
if (settings_.QueryValue(SettingsKey::QUIETHOURS_FORCEUNMUTE)) {
muteCtrl_.SetMute(false);
wchar_t *wifiName = reinterpret_cast<wchar_t *>(lParam);
if (settings_.QueryValue(SettingsKey::MUTE_ON_WLAN_ALLOWLIST)) {
popupMsg = std::vformat(
- i18n.GetTranslationW("popup.wlan-not-on-mute-list.text"),
+ i18n_.GetTranslationW("popup.wlan-not-on-mute-list.text"),
std::make_wformat_args(wifiName));
} else {
popupMsg = std::vformat(
- i18n.GetTranslationW("popup.wlan-is-on-mute-list.text"),
+ i18n_.GetTranslationW("popup.wlan-is-on-mute-list.text"),
std::make_wformat_args(wifiName));
}
trayIcon_.ShowPopup(
- i18n.GetTranslationW("popup.workstation-muted.title"),
+ i18n_.GetTranslationW("popup.workstation-muted.title"),
popupMsg);
delete[] wifiName;
}
return 0;
}
+LRESULT WinMute::OnUpdatePopup(HWND hWnd, WPARAM, LPARAM lParam)
+{
+ if (lParam == NIN_BALLOONUSERCLICK) {
+ ShellExecuteW(
+ hWnd, L"open", updateInfo_.versionPageUrl.c_str(),
+ nullptr, nullptr, SW_SHOW);
+ }
+ return 0;
+}
+
LRESULT CALLBACK WinMute::WindowProc(
HWND hWnd,
UINT msg,
return TRUE;
case WM_COMMAND:
return OnCommand(hWnd, wParam, lParam);
+ case WM_WINMUTE_UPDATE_POPUP:
+ return OnUpdatePopup(hWnd, wParam, lParam);
case WM_TRAYICON:
return OnTrayIcon(hWnd, wParam, lParam);
case WM_CLOSE:
// for internal use
LRESULT CALLBACK WindowProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
+ void CheckForUpdatesAsync(std::unique_ptr<UpdateChecker> updateChecker);
private:
HWND hWnd_;
HMENU hTrayMenu_;
TrayIcon trayIcon_;
WifiDetector wifiDetector_;
WMSettings& settings_;
- WMi18n &i18n;
+ WMi18n &i18n_;
MuteControl muteCtrl_;
QuietHoursTimer quietHours_;
BluetoothDetector btDetector_;
+ UpdateInfo updateInfo_;
+ void CheckForUpdates();
bool RegisterWindowClass();
bool InitWindow();
bool InitAudio();
LRESULT OnSettingChange(HWND hWnd, WPARAM wParam, LPARAM lParam);
LRESULT OnPowerBroadcast(HWND hWnd, WPARAM wParam, LPARAM lParam);
LRESULT OnWifiStatusChange(HWND hWnd, WPARAM wParam, LPARAM lParam);
+ LRESULT OnUpdatePopup(HWND hWnd, WPARAM wParam, LPARAM lParam);
LRESULT OnDeviceChange(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
LRESULT OnQuietHours(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
static const wchar_t *LOG_FILE_NAME = L"WinMute.log";
constexpr int WM_SAVESETTINGS = WM_USER + 300;
+constexpr int WM_WINMUTE_UPDATE_POPUP = WM_USER + 301;
// =============================================================================
// Utility