From adc46d286d632a8f786323b79997e45a41236381 Mon Sep 17 00:00:00 2001 From: lx-s Date: Tue, 19 Dec 2023 10:07:24 +0100 Subject: [PATCH] Added update check --- Translations/lang-en.json | 4 ++ WinMute/TrayIcon.cpp | 29 +++++++---- WinMute/TrayIcon.h | 7 ++- WinMute/UpdateChecker.cpp | 16 +++--- WinMute/UpdateChecker.h | 2 +- WinMute/WinMain.cpp | 40 --------------- WinMute/WinMute.cpp | 102 +++++++++++++++++++++++++++----------- WinMute/WinMute.h | 6 ++- WinMute/common.h | 1 + 9 files changed, 115 insertions(+), 92 deletions(-) diff --git a/Translations/lang-en.json b/Translations/lang-en.json index e24a92e..1f7638b 100644 --- a/Translations/lang-en.json +++ b/Translations/lang-en.json @@ -12,6 +12,10 @@ "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", diff --git a/WinMute/TrayIcon.cpp b/WinMute/TrayIcon.cpp index e1c44dc..516c314 100644 --- a/WinMute/TrayIcon.cpp +++ b/WinMute/TrayIcon.cpp @@ -132,7 +132,10 @@ void TrayIcon::ChangeText(const std::wstring& tooltip) 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; @@ -141,13 +144,17 @@ void TrayIcon::ShowPopup(const std::wstring& title, const std::wstring& text) co 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()); @@ -165,12 +172,12 @@ void TrayIcon::DestroyTrayIcon() 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; @@ -182,8 +189,8 @@ bool TrayIcon::AddNotifyIcon() 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_; @@ -193,8 +200,8 @@ bool TrayIcon::RemoveNotifyIcon() 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_; diff --git a/WinMute/TrayIcon.h b/WinMute/TrayIcon.h index 6d7cc10..f5d3d12 100644 --- a/WinMute/TrayIcon.h +++ b/WinMute/TrayIcon.h @@ -35,7 +35,7 @@ POSSIBILITY OF SUCH DAMAGE. #include "common.h" -#define WM_TRAYICON (WM_APP + 700) +constexpr int WM_TRAYICON = WM_APP + 700; struct TrayIconPopup { std::wstring title; @@ -55,7 +55,10 @@ public: 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(); diff --git a/WinMute/UpdateChecker.cpp b/WinMute/UpdateChecker.cpp index 429797e..9cc6eef 100644 --- a/WinMute/UpdateChecker.cpp +++ b/WinMute/UpdateChecker.cpp @@ -91,7 +91,7 @@ bool UpdateChecker::ParseVersionFile( return true; } -bool UpdateChecker::ParseVersion(const std::wstring &vers, std::vector parsedVers) const +bool UpdateChecker::ParseVersion(const std::wstring &vers, std::vector& parsedVers) const { size_t lastVersionPos = 0; size_t curPos = 0; @@ -106,10 +106,6 @@ bool UpdateChecker::ParseVersion(const std::wstring &vers, std::vector pars lastVersionPos = curPos + 1; } } - auto pos = vers.find('.'); - if (pos != std::wstring::npos) { - parsedVers.push_back(std::stoi(vers)); - } return true; } @@ -164,10 +160,12 @@ bool UpdateChecker::GetVersionFile(std::string &fileContents) const { 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; } @@ -189,7 +187,7 @@ bool UpdateChecker::GetVersionFile(std::string &fileContents) const 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()); diff --git a/WinMute/UpdateChecker.h b/WinMute/UpdateChecker.h index 9731fe2..bbfb93f 100644 --- a/WinMute/UpdateChecker.h +++ b/WinMute/UpdateChecker.h @@ -63,5 +63,5 @@ private: bool GetVersionFile(std::string &fileContents) const; bool ParseVersionFile(const std::string &fileContents, UpdateInfo& updateInfo) const; std::optional IsVersionGreater(const std::wstring &newVers, const std::wstring &curVers) const; - bool ParseVersion(const std::wstring &vers, std::vector parsedVers) const; + bool ParseVersion(const std::wstring &vers, std::vector& parsedVers) const; }; diff --git a/WinMute/WinMain.cpp b/WinMute/WinMain.cpp index ad21bc7..e5f9a9f 100644 --- a/WinMute/WinMain.cpp +++ b/WinMute/WinMain.cpp @@ -72,44 +72,6 @@ static bool InitWindowsComponents() return TRUE; } -static DWORD CheckForUpdates_Thread(LPVOID param) -{ - auto updateChecker = reinterpret_cast(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(updateChecker), - 0, nullptr); - if (updateThread == nullptr) { - delete updateChecker; - return; - } - -} - int WINAPI wWinMain( _In_ HINSTANCE hInstance, _In_opt_ HINSTANCE, @@ -172,8 +134,6 @@ int WINAPI wWinMain( return FALSE; } - CheckForUpdates(settings); - MSG msg = { nullptr }; WinMute program(settings); if (program.Init()) { diff --git a/WinMute/WinMute.cpp b/WinMute/WinMute.cpp index 30dbac6..9c352ae 100644 --- a/WinMute/WinMute.cpp +++ b/WinMute/WinMute.cpp @@ -101,6 +101,7 @@ static bool IsCurrentSessionRemoteable() noexcept return isRemoteable; } + WinMute::MuteConfig::MuteConfig() : muteOnWlan(false), muteOnBluetooth(false), showNotifications(false) { @@ -112,7 +113,7 @@ WinMute::WinMute(WMSettings& settings) : hAppIcon_(nullptr), hTrayIcon_(nullptr), settings_(settings), - i18n(WMi18n::GetInstance()) + i18n_(WMi18n::GetInstance()) { } @@ -157,8 +158,8 @@ bool WinMute::InitAudio() 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; } @@ -249,11 +250,13 @@ bool WinMute::Init() 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; } @@ -307,8 +310,8 @@ bool WinMute::LoadSettings() } 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); @@ -323,8 +326,8 @@ bool WinMute::LoadSettings() } 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); @@ -351,18 +354,18 @@ void WinMute::ToggleMenuCheck(UINT item, bool* setting) noexcept void WinMute::LoadMainMenuText() { std::map 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) }; @@ -379,6 +382,37 @@ void WinMute::LoadMainMenuText() } } +void WinMute::CheckForUpdates() +{ + auto updateChecker = std::make_unique(); + if (!updateChecker || + !updateChecker->IsUpdateCheckEnabled(settings_)) { + return; + } + std::thread updateThread(&WinMute::CheckForUpdatesAsync, this, std::move(updateChecker)); + updateThread.detach(); +} + +void WinMute::CheckForUpdatesAsync(std::unique_ptr 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)) { @@ -523,8 +557,8 @@ LRESULT WinMute::OnQuietHours(HWND, UINT msg, WPARAM, LPARAM) 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; @@ -532,8 +566,8 @@ LRESULT WinMute::OnQuietHours(HWND, UINT msg, WPARAM, LPARAM) 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); @@ -570,15 +604,15 @@ LRESULT WinMute::OnWifiStatusChange(HWND, WPARAM wParam, LPARAM lParam) wchar_t *wifiName = reinterpret_cast(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; } @@ -587,6 +621,16 @@ LRESULT WinMute::OnWifiStatusChange(HWND, WPARAM wParam, LPARAM lParam) 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, @@ -600,6 +644,8 @@ LRESULT CALLBACK WinMute::WindowProc( 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: diff --git a/WinMute/WinMute.h b/WinMute/WinMute.h index e346b98..25481c0 100644 --- a/WinMute/WinMute.h +++ b/WinMute/WinMute.h @@ -51,6 +51,7 @@ public: // for internal use LRESULT CALLBACK WindowProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); + void CheckForUpdatesAsync(std::unique_ptr updateChecker); private: HWND hWnd_; HMENU hTrayMenu_; @@ -67,11 +68,13 @@ private: 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(); @@ -90,6 +93,7 @@ private: 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); diff --git a/WinMute/common.h b/WinMute/common.h index c87d21e..0f1b4fc 100644 --- a/WinMute/common.h +++ b/WinMute/common.h @@ -129,6 +129,7 @@ static const wchar_t *PROGRAM_NAME = L"WinMute"; 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 -- 2.43.0