Skip to content

Commit 3acabf6

Browse files
authored
Merge pull request #12 from nefarius/self-update-check
Added self-update check mechanism
2 parents e544897 + 03f417d commit 3acabf6

4 files changed

Lines changed: 508 additions & 1 deletion

File tree

CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ target_link_libraries(MultiPadTester PRIVATE
3838
dxguid.lib
3939
setupapi.lib
4040
runtimeobject.lib
41+
winhttp.lib
42+
version.lib
4143
)
4244

4345
target_compile_definitions(MultiPadTester PRIVATE

src/main.cpp

Lines changed: 85 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
#include <Windows.h>
66
#include <Shellapi.h>
77
#include <tchar.h>
8+
#include <cstdint>
9+
#include <ctime>
810
#include <algorithm>
911
#include <cctype>
1012
#include <cfloat>
@@ -30,6 +32,7 @@
3032
#include "hidhide_probe.h"
3133
#include "libwdi_probe.h"
3234
#include "resource.h"
35+
#include "update_check.h"
3336

3437
#define IDM_ABOUT 0xF200
3538
#define IDM_PREFERENCES 0xF210
@@ -43,6 +46,8 @@ struct AppPrefs
4346
int windowW = 0; // 0 = use default position/size
4447
int windowH = 0;
4548
int lastTabIndex = 0; // backend tab index to restore on launch
49+
/** UTC Unix seconds when the user dismissed the update dialog; 0 = never. Suppresses checks for 24h. */
50+
int64_t updateDismissedUnix = 0;
4651
};
4752

4853
/**
@@ -69,7 +74,7 @@ static std::wstring GetConfigPath()
6974
*
7075
* Reads the config file located in the application's AppData folder and applies recognized keys
7176
* from the [Settings] section into the provided AppPrefs structure. Supported keys:
72-
* RefreshRate, VSync, WindowX, WindowY, WindowW, WindowH, LastTabIndex.
77+
* RefreshRate, VSync, WindowX, WindowY, WindowW, WindowH, LastTabIndex, UpdateDismissedUnix.
7378
*
7479
* - If the config file is missing or unreadable, the function leaves prefs unchanged.
7580
* - RefreshRate is accepted only if it equals 0, 60, 75, 120, or 144; other values are ignored.
@@ -143,6 +148,10 @@ static void LoadConfig(AppPrefs& prefs)
143148
{
144149
try { prefs.lastTabIndex = std::stoi(val); } catch (...) {}
145150
}
151+
else if (key == "UpdateDismissedUnix")
152+
{
153+
try { prefs.updateDismissedUnix = std::stoll(val); } catch (...) {}
154+
}
146155
}
147156
// Treat invalid dimensions as "not set"
148157
if (prefs.windowW <= 0 || prefs.windowH <= 0)
@@ -160,6 +169,7 @@ static void LoadConfig(AppPrefs& prefs)
160169
* - vsync: vertical sync enabled flag
161170
* - windowX, windowY, windowW, windowH: saved window position and size
162171
* - lastTabIndex: backend tab index to restore on launch
172+
* - updateDismissedUnix: UTC Unix time when update dialog was dismissed
163173
*/
164174
static void SaveConfig(const AppPrefs& prefs)
165175
{
@@ -177,6 +187,7 @@ static void SaveConfig(const AppPrefs& prefs)
177187
f << "WindowW=" << prefs.windowW << "\n";
178188
f << "WindowH=" << prefs.windowH << "\n";
179189
f << "LastTabIndex=" << prefs.lastTabIndex << "\n";
190+
f << "UpdateDismissedUnix=" << prefs.updateDismissedUnix << "\n";
180191
}
181192

182193
/**
@@ -231,6 +242,11 @@ static std::vector<std::string> g_libwdiUsbInstanceIdsUtf8;
231242
static std::string g_libwdiUsbProbeErrorUtf8;
232243
static AppPrefs g_prefs;
233244

245+
static std::unique_ptr<UpdateCheckSession> g_updateCheckSession;
246+
static bool g_showUpdateAvailable = false;
247+
static std::string g_updateLocalVerUtf8;
248+
static std::string g_updateRemoteVerUtf8;
249+
234250
static std::string WideToUtf8(const std::wstring_view w)
235251
{
236252
if (w.empty())
@@ -275,6 +291,18 @@ static LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
275291

276292
switch (msg)
277293
{
294+
case WM_UPDATE_CHECK_READY:
295+
{
296+
std::string loc;
297+
std::string rem;
298+
if (UpdateCheck_PopResultForUi(loc, rem))
299+
{
300+
g_updateLocalVerUtf8 = std::move(loc);
301+
g_updateRemoteVerUtf8 = std::move(rem);
302+
g_showUpdateAvailable = true;
303+
}
304+
}
305+
return 0;
278306
case WM_SIZE:
279307
if (g_d3d.device && wParam != SIZE_MINIMIZED)
280308
g_d3d.Resize(lParam);
@@ -294,6 +322,7 @@ static LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
294322
return 0;
295323
break;
296324
case WM_DESTROY:
325+
g_updateCheckSession.reset();
297326
{
298327
RECT r;
299328
if (GetWindowRect(hWnd, &r))
@@ -458,6 +487,14 @@ int APIENTRY wWinMain(
458487
ShowWindow(hwnd, nCmdShow);
459488
UpdateWindow(hwnd);
460489

490+
try
491+
{
492+
g_updateCheckSession = std::make_unique<UpdateCheckSession>(hwnd, g_prefs.updateDismissedUnix);
493+
}
494+
catch (...)
495+
{
496+
}
497+
461498
constexpr float clearColor[4] = {0.06f, 0.06f, 0.07f, 1.0f};
462499

463500
MSG msg{};
@@ -694,6 +731,51 @@ int APIENTRY wWinMain(
694731
ImGui::EndPopup();
695732
}
696733

734+
const char* const kUpdateAvailablePopupId = "Update available";
735+
if (g_showUpdateAvailable)
736+
ImGui::OpenPopup(kUpdateAvailablePopupId);
737+
738+
const bool updatePopupActive =
739+
g_showUpdateAvailable || ImGui::IsPopupOpen(kUpdateAvailablePopupId, ImGuiPopupFlags_None);
740+
if (updatePopupActive)
741+
{
742+
const float updateMinW = 400.f, updateMinH = 140.f;
743+
ImGui::SetNextWindowSizeConstraints(ImVec2(updateMinW, updateMinH),
744+
ImVec2(FLT_MAX, FLT_MAX));
745+
ImGui::SetNextWindowPos(ImGui::GetMainViewport()->GetCenter(),
746+
ImGuiCond_Appearing, ImVec2(0.5f, 0.5f));
747+
}
748+
if (ImGui::BeginPopupModal(
749+
kUpdateAvailablePopupId,
750+
&g_showUpdateAvailable,
751+
ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize))
752+
{
753+
ImGui::TextWrapped("A newer version of MultiPad Tester is available.");
754+
ImGui::Spacing();
755+
ImGui::Text("Installed version: %s", g_updateLocalVerUtf8.c_str());
756+
ImGui::Text("Latest version: %s", g_updateRemoteVerUtf8.c_str());
757+
ImGui::Spacing();
758+
if (ImGui::Button("Download update", ImVec2(140, 0)))
759+
{
760+
ShellExecuteW(
761+
nullptr,
762+
L"open",
763+
UpdateCheck_GetLatestDownloadUrlW(),
764+
nullptr,
765+
nullptr,
766+
SW_SHOWNORMAL);
767+
}
768+
ImGui::SameLine();
769+
if (ImGui::Button("Not today", ImVec2(130, 0)))
770+
{
771+
g_prefs.updateDismissedUnix = static_cast<int64_t>(std::time(nullptr));
772+
SaveConfig(g_prefs);
773+
ImGui::CloseCurrentPopup();
774+
g_showUpdateAvailable = false;
775+
}
776+
ImGui::EndPopup();
777+
}
778+
697779
const char* const kHidHideActivePopupId = "HidHide Active Warning";
698780
if (g_showHidHideWarning)
699781
ImGui::OpenPopup(kHidHideActivePopupId);
@@ -834,6 +916,8 @@ int APIENTRY wWinMain(
834916
g_d3d.Present(g_prefs.vsync);
835917
}
836918

919+
g_updateCheckSession.reset();
920+
837921
g_backends = nullptr;
838922

839923
ImGui_ImplDX11_Shutdown();

0 commit comments

Comments
 (0)