From 5528dfb733366fdadd78db05d9e6ba9fbf8120d6 Mon Sep 17 00:00:00 2001 From: Peter Repukat Date: Fri, 21 Oct 2022 21:18:25 +0200 Subject: [PATCH] Add configurable Launcher options / Launcher ProcessList --- GlosSIConfig/Resource.rc | 8 +- GlosSIConfig/UIModel.cpp | 10 +- GlosSIConfig/qml/AdvancedTargetSettings.qml | 101 +++++++++++++++++++- GlosSITarget/AppLauncher.cpp | 34 ++++--- GlosSITarget/AppLauncher.h | 12 +-- GlosSITarget/Resource.rc | 8 +- GlosSITarget/Settings.h | 32 ++++--- 7 files changed, 157 insertions(+), 48 deletions(-) diff --git a/GlosSIConfig/Resource.rc b/GlosSIConfig/Resource.rc index dc73243..f5db24c 100644 --- a/GlosSIConfig/Resource.rc +++ b/GlosSIConfig/Resource.rc @@ -51,8 +51,8 @@ END // VS_VERSION_INFO VERSIONINFO - FILEVERSION 0,1,0,2035000100080 - PRODUCTVERSION 0,1,0,2035000100080 + FILEVERSION 0,1,0,2045006300001 + PRODUCTVERSION 0,1,0,2045006300001 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -69,12 +69,12 @@ BEGIN BEGIN VALUE "CompanyName", "Peter Repukat - FlatspotSoftware" VALUE "FileDescription", "GlosSI - Config" - VALUE "FileVersion", "0.1.0.2-35-g01efd8d" + VALUE "FileVersion", "0.1.0.2-45-g63fdab1" VALUE "InternalName", "GlosSIConfig" VALUE "LegalCopyright", "Copyright (C) 2021 Peter Repukat - FlatspotSoftware" VALUE "OriginalFilename", "GlosSIConfig.exe" VALUE "ProductName", "GlosSI" - VALUE "ProductVersion", "0.1.0.2-35-g01efd8d" + VALUE "ProductVersion", "0.1.0.2-45-g63fdab1" END END BLOCK "VarFileInfo" diff --git a/GlosSIConfig/UIModel.cpp b/GlosSIConfig/UIModel.cpp index c23a67b..ee67d6d 100644 --- a/GlosSIConfig/UIModel.cpp +++ b/GlosSIConfig/UIModel.cpp @@ -19,6 +19,7 @@ limitations under the License. #include #include #include +#include #include #include @@ -32,6 +33,8 @@ limitations under the License. #include #endif +#include "ExeImageProvider.h" +#include "ExeImageProvider.h" #include "../version.hpp" #include "steamgrid_api_keys.h" @@ -365,15 +368,13 @@ QVariantMap UIModel::getDefaultConf() const path /= "Roaming"; path /= "GlosSI"; path /= "default.json"; - + QJsonObject defaults = { {"icon", QJsonValue::Null}, {"name", QJsonValue::Null}, {"version", 1}, {"extendedLogging", false}, {"snapshotNotify", false}, - {"ignoreEGS", true}, - {"killEGS", false}, {"controller", QJsonObject{{"maxControllers", 1}, {"emulateDS4", false}, {"allowDesktopConfig", false}}}, {"devices", QJsonObject{ @@ -387,6 +388,9 @@ QVariantMap UIModel::getDefaultConf() const {"launchAppArgs", QJsonValue::Null}, {"launchPath", QJsonValue::Null}, {"waitForChildProcs", true}, + {"launcherProcesses", QJsonArray{}}, + {"ignoreLauncher", true}, + {"killLauncher", false}, }}, {"window", QJsonObject{ diff --git a/GlosSIConfig/qml/AdvancedTargetSettings.qml b/GlosSIConfig/qml/AdvancedTargetSettings.qml index 29b46a2..0c7ec8a 100644 --- a/GlosSIConfig/qml/AdvancedTargetSettings.qml +++ b/GlosSIConfig/qml/AdvancedTargetSettings.qml @@ -90,7 +90,7 @@ CollapsiblePane { shortcutInfo.launch.waitForChildProcs = checked } } - CheckBox { + /*CheckBox { height: subTitle != "" || (shortcutInfo.launch.launchPath || "").includes("epicgames.launcher") ? 32 : 0 visible: subTitle != "" || (shortcutInfo.launch.launchPath || "").includes("epicgames.launcher") id: ignoreEGS @@ -109,7 +109,7 @@ CollapsiblePane { onCheckedChanged: function(){ shortcutInfo.killEGS = checked } - } + }*/ } Column { spacing: 2 @@ -483,7 +483,104 @@ CollapsiblePane { id: commonPane Column { spacing: 4 + width: parent.width + Column { + width: parent.width + Row { + width: parent.width + Label { + text: qsTr("Launcher processes") + anchors.verticalCenter: parent.verticalCenter + } + RoundButton { + onClicked: () => { + helpInfoDialog.titleText = qsTr("Launcher processes") + helpInfoDialog.text = + qsTr("Tells GlosSI what processes should be treated as launchers") + + "\n" + qsTr("Only use executable name, not full path") + + "\n" + qsTr("One process per line") + + "\n" + + qsTr("List must be filled for \"") + + qsTr("Ignore launcher for close detection") + + qsTr("\" and \"") + qsTr("Close launcher on game exit.") + + qsTr("\" to work") + + helpInfoDialog.open() + } + width: 48 + height: 48 + Material.elevation: 0 + anchors.verticalCenter: parent.verticalCenter + Image { + anchors.centerIn: parent + source: "qrc:/svg/help_outline_white_24dp.svg" + width: 24 + height: 24 + } + } + } + RPane { + color: Qt.lighter(Material.background, 1.6) + bgOpacity: 0.3 + radius: 8 + width: parent.width + height: launcherProcessesTextArea.height + 16 + Flickable { + width: parent.width + height: parent.height + clip: true + ScrollBar.vertical: ScrollBar { + + } + contentWidth: parent.width + flickableDirection: Flickable.VerticalFlick + TextArea { + id: launcherProcessesTextArea + width: parent.width + TextArea.flickable: parent + text: ((shortcutInfo.launch.launcherProcesses || []).length == 0 && (shortcutInfo.launch.launchPath || "").includes("epicgames.launcher")) + ? "EpicGamesLauncher.exe\nEpicWebHelper.exe" + : (shortcutInfo.launch.launcherProcesses || [""]).reduce((acc, curr) => { + return acc + "\n" + curr; + }) + onTextChanged: function() { + shortcutInfo.launch.launcherProcesses = text.split("\n") + .map((e) => { + e = e.endsWith(".exe") ? e : e + ".exe" + return e.trim() + }) + .filter((e) => { + return e != "" && e != ".exe" + }); + } + } + + } + } + } + Row { + CheckBox { + id: ignoreLauncherCheckbox + text: qsTr("Ignore launcher for close detection") + checked: shortcutInfo.launch.ignoreLauncher + onCheckedChanged: function(){ + shortcutInfo.launch.ignoreLauncher = checked + } + } + CheckBox { + id: killLauncherCheckbox + text: qsTr("Close launcher on game exit.") + enabled: ignoreLauncherCheckbox.checked + checked: shortcutInfo.launch.killLauncher + onCheckedChanged: function(){ + shortcutInfo.launch.killLauncher = checked + } + } + } Row { + anchors.topMargin: 24 Row { CheckBox { id: extendedLogging diff --git a/GlosSITarget/AppLauncher.cpp b/GlosSITarget/AppLauncher.cpp index 8a07887..e9b007a 100644 --- a/GlosSITarget/AppLauncher.cpp +++ b/GlosSITarget/AppLauncher.cpp @@ -51,6 +51,12 @@ AppLauncher::AppLauncher( void AppLauncher::launchApp(const std::wstring& path, const std::wstring& args) { #ifdef _WIN32 + + if (!Settings::launch.launcherProcesses.empty()) { + has_extra_launchers_ = true; + spdlog::debug("Has extra launchers"); + } + if (Settings::launch.isUWP) { spdlog::info("LaunchApp is UWP, launching..."); launched_uwp_path_ = path; @@ -87,8 +93,8 @@ void AppLauncher::update() if (process_check_clock_.getElapsedTime().asMilliseconds() > 250) { pid_mutex_.lock(); #ifdef _WIN32 - if (was_egs_launch_ && pids_.empty()) { - findEgsPid(); + if (has_extra_launchers_ && pids_.empty()) { + findLauncherPids(); } if (!pids_.empty() && pids_[0] > 0) { if (Settings::launch.waitForChildProcs) { @@ -116,14 +122,14 @@ void AppLauncher::update() }); auto filtered_pids = pids_ | std::ranges::views::filter([](DWORD pid) { - return std::ranges::find(EGS_LAUNCHER_PROCNAMES_, glossi_util::GetProcName(pid)) == EGS_LAUNCHER_PROCNAMES_.end(); + return std::ranges::find(Settings::launch.launcherProcesses, glossi_util::GetProcName(pid)) == Settings::launch.launcherProcesses.end(); }); - if (was_egs_launch_ && !filtered_pids.empty()) { - egs_has_launched_game_ = true; + if (has_extra_launchers_ && !filtered_pids.empty()) { + launcher_has_launched_game_ = true; } if (Settings::launch.closeOnExit && Settings::launch.launch) { - if (was_egs_launch_ && (Settings::common.ignoreEGS || Settings::common.killEGS)) { - if (egs_has_launched_game_ && filtered_pids.empty()) { + if (has_extra_launchers_ && (Settings::launch.ignoreLauncher || Settings::launch.killLauncher)) { + if (launcher_has_launched_game_ && filtered_pids.empty()) { spdlog::info("Configured to close on all children exit. Shutting down after game launched via EGS quit..."); shutdown_(); } @@ -158,12 +164,12 @@ std::vector AppLauncher::launchedPids() pid_mutex_.lock(); std::vector res; res.reserve(pids_.size()); - if (!Settings::common.killEGS && Settings::common.ignoreEGS) { + if (!Settings::launch.killLauncher && Settings::launch.ignoreLauncher) { for (const auto& pid : pids_ | std::ranges::views::filter( [](DWORD pid) { return std::ranges::find( - EGS_LAUNCHER_PROCNAMES_, - glossi_util::GetProcName(pid)) == EGS_LAUNCHER_PROCNAMES_.end(); + Settings::launch.launcherProcesses, + glossi_util::GetProcName(pid)) == Settings::launch.launcherProcesses.end(); })) { res.push_back(pid); } @@ -255,7 +261,7 @@ void AppLauncher::getProcessHwnds() #endif #ifdef _WIN32 -bool AppLauncher::findEgsPid() +bool AppLauncher::findLauncherPids() { if (const auto pid = glossi_util::PidByName(L"EpicGamesLauncher.exe")) { spdlog::debug("Found EGS-Launcher running"); @@ -395,7 +401,7 @@ void AppLauncher::launchURL(const std::wstring& url, const std::wstring& args, c CoUninitialize(); if (url.find(L"epicgames.launcher") != std::wstring::npos) { - was_egs_launch_ = true; + has_extra_launchers_ = true; } if (execute_info.hProcess != nullptr) { if (const auto pid = GetProcessId(execute_info.hProcess); pid > 0) { @@ -407,12 +413,12 @@ void AppLauncher::launchURL(const std::wstring& url, const std::wstring& args, c } } - if (was_egs_launch_) { + if (has_extra_launchers_) { spdlog::debug("Epic Games launch; Couldn't find egs launcher PID"); pid_mutex_.lock(); const auto pid = glossi_util::PidByName(L"EpicGamesLauncher.exe"); - if (!findEgsPid()) { + if (!findLauncherPids()) { spdlog::debug("Did not find EGS-Launcher not running, retrying later..."); } pid_mutex_.unlock(); diff --git a/GlosSITarget/AppLauncher.h b/GlosSITarget/AppLauncher.h index df4032e..8293e8e 100644 --- a/GlosSITarget/AppLauncher.h +++ b/GlosSITarget/AppLauncher.h @@ -51,15 +51,9 @@ class AppLauncher { void getProcessHwnds(); std::vector& process_hwnds_; - static inline const std::array EGS_LAUNCHER_PROCNAMES_{ - L"EpicGamesLauncher.exe", - L"EpicWebHelper.exe", - }; - - - bool was_egs_launch_ = false; - bool egs_has_launched_game_ = false; - bool findEgsPid(); + bool has_extra_launchers_ = false; + bool launcher_has_launched_game_ = false; + bool findLauncherPids(); std::wstring launched_uwp_path_; diff --git a/GlosSITarget/Resource.rc b/GlosSITarget/Resource.rc index 009c048..edcf04e 100644 --- a/GlosSITarget/Resource.rc +++ b/GlosSITarget/Resource.rc @@ -51,8 +51,8 @@ END // VS_VERSION_INFO VERSIONINFO - FILEVERSION 0,1,0,2033009290000 - PRODUCTVERSION 0,1,0,2033009290000 + FILEVERSION 0,1,0,2045006300001 + PRODUCTVERSION 0,1,0,2045006300001 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -69,12 +69,12 @@ BEGIN BEGIN VALUE "CompanyName", "Peter Repukat - FlatspotSoftware" VALUE "FileDescription", "GlosSI - SteamTarget" - VALUE "FileVersion", "0.1.0.2-33-g929abfe" + VALUE "FileVersion", "0.1.0.2-45-g63fdab1" VALUE "InternalName", "GlosSITarget" VALUE "LegalCopyright", "Copyright (C) 2021-2022 Peter Repukat - FlatspotSoftware" VALUE "OriginalFilename", "GlosSITarget.exe" VALUE "ProductName", "GlosSI" - VALUE "ProductVersion", "0.1.0.2-33-g929abfe" + VALUE "ProductVersion", "0.1.0.2-45-g63fdab1" END END BLOCK "VarFileInfo" diff --git a/GlosSITarget/Settings.h b/GlosSITarget/Settings.h index 32b505b..1acfaf5 100644 --- a/GlosSITarget/Settings.h +++ b/GlosSITarget/Settings.h @@ -39,6 +39,9 @@ inline struct Launch { bool closeOnExit = true; bool waitForChildProcs = true; bool isUWP = false; + bool ignoreLauncher = true; + bool killLauncher = false; + std::vector launcherProcesses{}; } launch; inline struct Devices { @@ -63,8 +66,6 @@ inline struct Common { bool no_uwp_overlay = false; bool disable_watchdog = false; bool extendedLogging = false; - bool ignoreEGS = true; - bool killEGS = false; std::wstring name; std::wstring icon; int version; @@ -165,6 +166,17 @@ inline void Parse(const nlohmann::basic_json<>& json) safeWStringParse(launchconf, "launchAppArgs", launch.launchAppArgs); safeParseValue(launchconf, "closeOnExit", launch.closeOnExit); safeParseValue(launchconf, "waitForChildProcs", launch.waitForChildProcs); + safeParseValue(launchconf, "killLauncher", launch.killLauncher); + safeParseValue(launchconf, "ignoreLauncher", launch.ignoreLauncher); + + if (auto launcherProcs = launchconf["launcherProcesses"]; + !launcherProcs.is_null() && !launcherProcs.empty() && launcherProcs.is_array()) { + launch.launcherProcesses.clear(); + launch.launcherProcesses.reserve(launcherProcs.size()); + for (auto& proc : launcherProcs) { + launch.launcherProcesses.push_back(std::wstring_convert>().from_bytes(proc)); + } + } } if (auto devconf = json["devices"]; !devconf.is_null() && !devconf.empty() && devconf.is_object()) { @@ -184,6 +196,10 @@ inline void Parse(const nlohmann::basic_json<>& json) safeParseValue(controllerConf, "allowDesktopConfig", controller.allowDesktopConfig); safeParseValue(controllerConf, "emulateDS4", controller.emulateDS4); } + safeParseValue(json, "extendedLogging", common.extendedLogging); + safeWStringParse(json, "name", common.name); + safeWStringParse(json, "icon", common.icon); + safeParseValue(json, "version", common.version); } catch (const nlohmann::json::exception& e) { spdlog::warn("Err parsing config: {}", e.what()); @@ -191,14 +207,6 @@ inline void Parse(const nlohmann::basic_json<>& json) catch (const std::exception& e) { spdlog::warn("Err parsing config: {}", e.what()); } - - safeParseValue(json, "extendedLogging", common.extendedLogging); - safeWStringParse(json, "name", common.name); - safeWStringParse(json, "icon", common.icon); - safeParseValue(json, "version", common.version); - safeParseValue(json, "ignoreEGS", common.ignoreEGS); - safeParseValue(json, "killEGS", common.killEGS); - if (launch.launch) { launch.isUWP = checkIsUwp(launch.launchPath); } @@ -217,8 +225,8 @@ inline void Parse(const std::vector& args) else if (arg == L"-disablewatchdog") { common.disable_watchdog = true; } - else if (arg == L"-ignoreegs") { - common.ignoreEGS = true; + else if (arg == L"-ignorelauncher") { + launch.ignoreLauncher = true; } else { configName += L" " + std::wstring(arg.begin(), arg.end());