diff options
| author | Erik Kundiman <erik@megapahit.org> | 2026-09-01 18:35:23 +0800 |
|---|---|---|
| committer | Erik Kundiman <erik@megapahit.org> | 2026-09-04 21:58:53 +0800 |
| commit | 00bb3bb6f07660bd914d29a1389342bb3060d254 (patch) | |
| tree | 431f574922494d2201718f13085f17bc6bd7fb42 /indra/newview/llvelopack.cpp | |
| parent | a8636720129952c10cf49ab80da21bf8b340b96c (diff) | |
| parent | 4ef9f8f14b35ed388c294d53767a0dca0e890924 (diff) | |
Merge remote-tracking branch 'secondlife/release/26.4' into 26.4
Diffstat (limited to 'indra/newview/llvelopack.cpp')
| -rw-r--r-- | indra/newview/llvelopack.cpp | 115 |
1 files changed, 104 insertions, 11 deletions
diff --git a/indra/newview/llvelopack.cpp b/indra/newview/llvelopack.cpp index 90eb977bba..927a5bbc2c 100644 --- a/indra/newview/llvelopack.cpp +++ b/indra/newview/llvelopack.cpp @@ -31,6 +31,7 @@ #include "llstring.h" #include "llcorehttputil.h" #include "llversioninfo.h" +#include "llversioninfovars.h" // LL_VIEWER_CHANNEL #include <boost/json.hpp> #include <fstream> @@ -43,15 +44,19 @@ #include "Velopack.h" #if LL_WINDOWS +#include "llappviewerwin32.h" #include <windows.h> #include <shlobj.h> #include <shobjidl.h> #include <shlwapi.h> #include <objbase.h> #include <filesystem> +#include <propkey.h> +#include <propvarutil.h> #pragma comment(lib, "shlwapi.lib") #pragma comment(lib, "ole32.lib") +#pragma comment(lib, "propsys.lib") #pragma comment(lib, "shell32.lib") #endif // LL_WINDOWS @@ -238,7 +243,7 @@ static bool custom_download_asset(void* user_data, { // The asset has already been downloaded at the coroutine level (before vpkc_download_updates). // This callback just copies the pre-downloaded file to where Velopack expects it. - // We cannot use getRawAndSuspend here — coroutine context is lost through the Rust FFI boundary. + // We cannot use getRawAndSuspend here - coroutine context is lost through the Rust FFI boundary. if (sPreDownloadedAssetPath.empty()) { LL_WARNS("Velopack") << "No pre-downloaded asset available" << LL_ENDL; @@ -330,6 +335,17 @@ static std::wstring get_desktop_path() return L""; } +static std::wstring get_app_user_model_id() +{ + // Format: CompanyName.ProductName + // This ID is used for: + // 1. Registry registration (HKCU\Software\Classes\Applications\{exe}\AppUserModelID) + // 2. Shortcut property (IPropertyStore::SetValue with PKEY_AppUserModel_ID) + // 3. Runtime process ID (SetCurrentProcessExplicitAppUserModelID if needed) + // Must be consistent across all uses for proper taskbar grouping. + return L"LindenLab." + get_app_name_oneword(); +} + static HRESULT create_shortcut(const std::wstring& shortcut_path, const std::wstring& target_path, const std::wstring& arguments, @@ -354,6 +370,28 @@ static HRESULT create_shortcut(const std::wstring& shortcut_path, PathRemoveFileSpecW(work_dir); shell_link->SetWorkingDirectory(work_dir); + // Set AppUserModelID on the shortcut + IPropertyStore* prop_store = nullptr; + hr = shell_link->QueryInterface(IID_IPropertyStore, (void**)&prop_store); + if (SUCCEEDED(hr)) + { + PROPVARIANT pv; + PropVariantInit(&pv); + std::wstring app_id = get_app_user_model_id(); + hr = InitPropVariantFromString(app_id.c_str(), &pv); + if (SUCCEEDED(hr)) + { + hr = prop_store->SetValue(PKEY_AppUserModel_ID, pv); + PropVariantClear(&pv); + if (SUCCEEDED(hr)) + { + HRESULT hr_commit = prop_store->Commit(); + if (FAILED(hr_commit)) hr = hr_commit; + } + } + prop_store->Release(); + } + IPersistFile* persist_file = nullptr; hr = shell_link->QueryInterface(IID_IPersistFile, (void**)&persist_file); if (SUCCEEDED(hr)) @@ -405,6 +443,42 @@ static void register_protocol_handler(const std::wstring& protocol, } } +static void register_app_user_model_id(const std::wstring& exe_name) +{ + // Register AppUserModelID for the executable + // This allows Windows to properly group taskbar items when users drag-and-drop + // the exe to pin it, without requiring runtime calls to SetCurrentProcessExplicitAppUserModelID + std::wstring key_path = L"SOFTWARE\\Classes\\Applications\\" + exe_name; + HKEY hkey; + + if (RegCreateKeyExW(HKEY_CURRENT_USER, key_path.c_str(), 0, NULL, + REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &hkey, NULL) == ERROR_SUCCESS) + { + std::wstring app_user_model_id = get_app_user_model_id(); + LSTATUS status = RegSetValueExW(hkey, L"AppUserModelID", 0, REG_SZ, + (BYTE*)app_user_model_id.c_str(), + (DWORD)((app_user_model_id.size() + 1) * sizeof(wchar_t))); + RegCloseKey(hkey); + + if (status == ERROR_SUCCESS) + { + LL_DEBUGS("Velopack") << "Registered AppUserModelID: " + << ll_convert_wide_to_string(app_user_model_id) + << " for " << ll_convert_wide_to_string(exe_name) << LL_ENDL; + } + else + { + LL_WARNS("Velopack") << "Failed to set AppUserModelID (error " << status << ") for " + << ll_convert_wide_to_string(exe_name) << LL_ENDL; + } + } + else + { + LL_WARNS("Velopack") << "Failed to register AppUserModelID for " + << ll_convert_wide_to_string(exe_name) << LL_ENDL; + } +} + static bool get_shortcut_target(const std::wstring& lnk_path, std::wstring& target_path_str) { // Resolve the shortcut to check its target @@ -717,6 +791,12 @@ static void unregister_protocol_handler(const std::wstring& protocol) RegDeleteTreeW(HKEY_CURRENT_USER, key_path.c_str()); } +static void unregister_app_user_model_id(const std::wstring& exe_name) +{ + std::wstring key_path = L"SOFTWARE\\Classes\\Applications\\" + exe_name; + RegDeleteTreeW(HKEY_CURRENT_USER, key_path.c_str()); +} + static void register_uninstall_info(const std::wstring& install_dir, const std::wstring& app_name, const std::wstring& version) @@ -793,7 +873,9 @@ static void unregister_uninstall_info() RegDeleteTreeW(HKEY_CURRENT_USER, key_path.c_str()); } -static void create_shortcuts(const std::wstring& install_dir, const std::wstring& app_name) +static void create_shortcuts( + const std::wstring& install_dir, + const std::wstring& app_name) { std::wstring exe_path = install_dir + L"\\" + get_viewer_exe_name(); std::wstring start_menu_dir = get_start_menu_path() + L"\\" + app_name; @@ -865,21 +947,32 @@ static void on_after_install(void* user_data, const char* app_version) { std::wstring install_dir = get_install_dir(); std::wstring app_name = get_app_name(); - std::wstring exe_path = install_dir + L"\\" + get_viewer_exe_name(); + std::wstring exe_name = get_viewer_exe_name(); + std::wstring exe_path = install_dir + L"\\" + exe_name; register_protocol_handler(PROTOCOL_SECONDLIFE, L"URL:Second Life", exe_path); register_protocol_handler(PROTOCOL_GRID_INFO, L"URL:Second Life", exe_path); + + // Register AppUserModelID for taskbar pinning support + register_app_user_model_id(exe_name); + create_shortcuts(install_dir, app_name); } static void on_before_uninstall(void* user_data, const char* app_version) { std::wstring app_name = get_app_name(); + std::wstring exe_name = get_viewer_exe_name(); unregister_protocol_handler(PROTOCOL_SECONDLIFE); unregister_protocol_handler(PROTOCOL_GRID_INFO); - unregister_uninstall_info(); + unregister_app_user_model_id(exe_name); remove_shortcuts(app_name); + + std::wstring install_dir = get_install_dir(); + LLAppViewerWin32::sendShutdownToOtherInstances(install_dir); + + unregister_uninstall_info(); } static void on_log_message(void* user_data, const char* level, const char* message) @@ -998,7 +1091,7 @@ static void ensure_update_manager(bool allow_downgrade) LL_INFOS("Velopack") << "Auto-detect failed (" << ll_safe_string(err) << "), falling back to explicit locator" << LL_ENDL; - // Auto-detection failed — construct an explicit locator. + // Auto-detection failed - construct an explicit locator. // This handles legacy DMG installs that don't have Velopack's // install state (UpdateMac, sq.version) in the bundle. vpkc_locator_config_t locator = {}; @@ -1166,7 +1259,7 @@ static void on_downloading_closed(const LLSD& notification, const LLSD& response sDownloadingNotification = nullptr; if (sIsRequired) { - // User closed the downloading dialog during a required update — re-show it + // User closed the downloading dialog during a required update - re-show it show_downloading_notification(sTargetVersion); } } @@ -1255,12 +1348,12 @@ void velopack_check_for_updates(const std::string& required_version, const std:: // strictly lower than what we're running (e.g., a retracted build). bool has_required = !required_version.empty(); int ver_cmp = has_required ? compare_running_version(required_version) : 0; - bool allow_downgrade = ver_cmp > 0; // running > required → rollback scenario + bool allow_downgrade = ver_cmp > 0; // running > required -> rollback scenario ensure_update_manager(allow_downgrade); if (!sUpdateManager) return; - // Ask Velopack to check its feed — this is the source of truth + // Ask Velopack to check its feed - this is the source of truth vpkc_update_info_t* update_info = nullptr; vpkc_update_check_t result = vpkc_check_for_updates(sUpdateManager, &update_info); @@ -1286,7 +1379,7 @@ void velopack_check_for_updates(const std::string& required_version, const std:: sPendingCheckInfo = update_info; // Determine if this is mandatory: running version is below VVM's required floor - bool is_required = ver_cmp < 0; // running < required → must update + bool is_required = ver_cmp < 0; // running < required -> must update sIsRequired = is_required; if (is_required) @@ -1297,12 +1390,12 @@ void velopack_check_for_updates(const std::string& required_version, const std:: return; } - // Optional update — check user preference + // Optional update - check user preference U32 updater_setting = gSavedSettings.getU32("UpdaterServiceSetting"); if (updater_setting == 3) { - // "Install each update automatically" — download silently, apply on quit + // "Install each update automatically" - download silently, apply on quit LL_INFOS("Velopack") << "Optional update to " << target_version << ", downloading automatically (UpdaterServiceSetting=3)" << LL_ENDL; velopack_download_pending_update(); |
