Compare commits

..

No commits in common. 'main' and '0.1.0.1' have entirely different histories.

@ -7,9 +7,6 @@ assignees: ''
---
- [ ] _**I have read and understand that tickets without Logs (or other info if Logs are 1000% not relevant) will be closed without comment.**_
## Expected Behavior
@ -23,16 +20,12 @@ assignees: ''
3.
...
## General Information
## Specifications
- Version: (0.0.0.0-0-g1234567)
- Version:
- Platform: (Win10/Win11 / Desktop/SteamDeck)
- Controller(s): (1 Steam Controller + 1 Switch Pro Controller)
- Application: (GlosSITarget/GlosSIConfig)
## Logs / Additional Files
Log and config files are located in `%appdata%/GlosSI`
The files are called: glossiconfig.log, glossitarget.log, GlosSIWatchdog.log and UWPOverlayEnabler.log
_(Windows-search may not pick up the files when searching for `log` **if** file endings are hidden (default setting))_
(Log and config files are located in `%appdata%/GlosSI`)

@ -1,24 +0,0 @@
---
name: Other
about: Create issue that doesn't fit other templates
title: ''
labels: ''
assignees: ''
---
- [ ] _**I have read and understand that tickets without Logs (or other info if Logs are 1000% not relevant) will be closed without comment.**_
## General Information
- Version: (0.0.0.0-0-g1234567)
- Platform: (Win10/Win11 / Desktop/SteamDeck)
- Controller(s): (1 Steam Controller + 1 Switch Pro Controller)
- Application: (GlosSITarget/GlosSIConfig)
## Logs / Additional Files
Log and config files are located in `%appdata%/GlosSI`
The files are called: glossiconfig.log, glossitarget.log, GlosSIWatchdog.log and UWPOverlayEnabler.log
_(Windows-search may not pick up the files when searching for `log` **if** file endings are hidden (default setting))_

2
.gitignore vendored

@ -370,5 +370,3 @@ MigrationBackup/
# Fody - auto-generated XML schema
FodyWeavers.xsd
.visuallint
GlosSIConfig/steamgrid_api_keys.h
steamgrid.zip

12
.gitmodules vendored

@ -27,16 +27,10 @@
url = https://github.com/nlohmann/json
[submodule "deps/traypp"]
path = deps/traypp
url = https://github.com/Soundux/traypp
url = https://github.com/Soundux/traypp.git
[submodule "deps/fifo_map"]
path = deps/fifo_map
url = https://github.com/nlohmann/fifo_map
url = git@github.com:nlohmann/fifo_map.git
[submodule "deps/Shortcuts_VDF"]
path = deps/Shortcuts_VDF
url = https://github.com/Alia5/Shortcuts_VDF
[submodule "deps/cpp-httplib"]
path = deps/cpp-httplib
url = https://github.com/yhirose/cpp-httplib
[submodule "deps/easywsclient"]
path = deps/easywsclient
url = https://github.com/dhbaird/easywsclient
url = git@github.com:Alia5/Shortcuts_VDF.git

41
.vscode/tasks.json vendored

@ -1,41 +0,0 @@
{
"version": "2.0.0",
"tasks": [
{
"type": "shell",
"label": "Build Solution (Debug)",
"command": "msbuild.exe",
"args": [
"GlosSI.sln",
"/target:Build",
"/p:Configuration=Debug",
"/p:Platform=x64"
],
"options": {
"cwd": "${workspaceFolder}",
},
"problemMatcher": ["$msCompile"],
"group": {
"kind": "build",
},
},
{
"type": "shell",
"label": "Re-Build Solution (Debug)",
"command": "msbuild.exe",
"args": [
"GlosSI.sln",
"/target:Rebuild",
"/p:Configuration=Debug",
"/p:Platform=x64"
],
"options": {
"cwd": "${workspaceFolder}",
},
"problemMatcher": ["$msCompile"],
"group": {
"kind": "build",
},
}
]
}

@ -1,25 +0,0 @@
{
"configurations": [
{
"name": "Win32",
"includePath": [
"${workspaceFolder}/**",
"${workspaceFolder}/../deps/json/include",
"${workspaceFolder}/../deps/cpp-httplib",
"${workspaceFolder}/../deps/easywsclient"
],
"defines": [
"_DEBUG",
"UNICODE",
"_UNICODE",
"_SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING"
],
"windowsSdkVersion": "10.0.18362.0",
"compilerPath": "C:/Program Files (x86)/Microsoft Visual Studio/2019/BuildTools/VC/Tools/MSVC/14.24.28314/bin/Hostx64/x64/cl.exe",
"cStandard": "c11",
"cppStandard": "c++20",
"intelliSenseMode": "msvc-x64"
}
],
"version": 4
}

@ -1,43 +0,0 @@
{
"version": "2.0.0",
"tasks": [
{
"type": "shell",
"label": "Build CEFInjectLib (Debug)",
"command": "msbuild.exe",
"args": [
"GlosSI.sln",
"/target:CEFInjectLib",
"/p:Configuration=Debug",
"/p:Platform=x64"
],
"options": {
"cwd": "${workspaceFolder}/..",
},
"problemMatcher": ["$msCompile"],
"group": {
"kind": "build",
},
},
{
"type": "shell",
"label": "Re-Build CEFInjectLib (Debug)",
"command": "msbuild.exe",
"args": [
"GlosSI.sln",
"/target:CEFInjectLib:Rebuild",
"/p:Configuration=Debug",
"/p:Platform=x64"
],
"options": {
"cwd": "${workspaceFolder}/..",
},
"problemMatcher": ["$msCompile"],
"group": {
"kind": "build",
},
}
]
}

@ -1,464 +0,0 @@
/*
Copyright 2021-2023 Peter Repukat - FlatspotSoftware
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#include "CEFInject.h"
#include <easywsclient.hpp>
#define _SSIZE_T_DEFINED
#include <easywsclient.cpp> // seems like a hack to me, but eh, what is in the doc will be done ¯\_(ツ)_/¯
#include "../common/nlohmann_json_wstring.h"
#include <fstream>
#include <streambuf>
#include <spdlog/spdlog.h>
#include "../common/Settings.h"
namespace CEFInject
{
namespace internal
{
httplib::Client GetHttpClient(uint16_t port)
{
httplib::Client cli("localhost", port);
cli.set_connection_timeout(0, 200000);
cli.set_read_timeout(0, 500000);
return cli;
}
static uint32_t msg_id = 0;
}
bool CEFDebugAvailable(uint16_t port)
{
auto cli = internal::GetHttpClient(port);
if (auto res = cli.Get("/json")) {
if (res->status == 200) {
return true;
}
}
return false;
}
std::vector<std::wstring> AvailableTabNames(uint16_t port)
{
std::vector<std::wstring> tabs;
const auto json = AvailableTabs(port);
for (const auto& j : json) {
tabs.push_back(j["title"].get<std::wstring>());
}
return tabs;
}
nlohmann::basic_json<> AvailableTabs(uint16_t port)
{
if (!CEFDebugAvailable()) {
return nlohmann::json::array();
}
//if (Settings::common.extendedLogging)
//{
spdlog::trace("Fetching available Steam CEF tabs");
//}
auto cli = internal::GetHttpClient(port);
if (auto res = cli.Get("/json")) {
if (res->status == 200) {
return nlohmann::json::parse(res->body);
}
}
return nlohmann::json::array();
}
nlohmann::basic_json<> InjectJs(std::string_view tab_name, std::string_view debug_url, std::wstring_view js, uint16_t port)
{
std::shared_ptr<easywsclient::WebSocket> ws{
easywsclient::WebSocket::from_url(debug_url.data())
};
if (ws)
{
nlohmann::json res = nullptr;
try
{
auto json_payload = nlohmann::json{
{"id", internal::msg_id++},
{"method", "Runtime.evaluate"},
{"params", {
{"userGesture", true},
{"expression", std::wstring{js.data()}}
//{"expression", js}
}}
};
auto payload_string = json_payload.dump();
spdlog::debug("Injecting JS into tab: {}, {}; JS: {}", tab_name, debug_url, payload_string);
ws->send(payload_string);
bool exit = false;
while (ws->getReadyState() != easywsclient::WebSocket::CLOSED) {
ws->poll();
ws->dispatch([&ws, &res, &exit](const std::string& message) {
const auto msg = nlohmann::json::parse(message);
try
{
if (msg.at("result").at("result").at("type").get<std::string>() != "undefined") {
res = msg.at("result").at("result").at("value");
}
}
catch (...) {
spdlog::error("CEFInject: Error parsing injection-result value: {}", message);
res = msg;
}
exit = true;
});
if (exit) {
ws->close();
return res;
}
}
}
catch (...) {
spdlog::error(
"CEFInject: Error injecting JS into tab: {}, {}",
std::string(tab_name.data()),
std::string(debug_url.data()));
}
return res;
}
return nullptr;
}
nlohmann::basic_json<> InjectJsByName(std::wstring_view tabname, std::wstring_view js, uint16_t port)
{
auto cli = internal::GetHttpClient(port);
if (auto res = cli.Get("/json")) {
if (res->status == 200) {
const auto json = nlohmann::json::parse(res->body);
for (const auto& tab : json) {
if (tab["title"].get<std::wstring>().find(tabname) != std::string::npos) {
return InjectJs(tab["title"].get<std::string>(), tab["webSocketDebuggerUrl"].get<std::string>(), js, port);
}
}
}
}
return nullptr;
}
WSAStartupWrap::WSAStartupWrap()
{
#ifdef _WIN32
WSADATA wsa_data;
if (const INT rc = WSAStartup(MAKEWORD(2, 2), &wsa_data)) {
spdlog::error("WSAStartup Failed.");
return;
}
wsa_startup_succeeded_ = true;
#endif
}
WSAStartupWrap::~WSAStartupWrap()
{
#ifdef _WIN32
if (wsa_startup_succeeded_)
{
WSACleanup();
}
#endif
}
bool SteamTweaks::injectGlosSITweaks(std::string_view tab_name, uint16_t port)
{
if (tab_name.empty()) {
for (auto ts = AvailableTabNames(); const auto & tn : ts)
{
// meh!
injectGlosSITweaks(util::string::to_string(tn), port);
}
return true;
}
return injectGlosSITweaks(Tab_Info{ std::string(tab_name) }, port);
}
bool SteamTweaks::injectGlosSITweaks(const Tab_Info& info, uint16_t port)
{
if (!CEFDebugAvailable()) {
return false;
}
if (glossi_tweaks_js_.empty())
{
if (!readGlosSITweaksJs()) {
return false;
}
}
const auto find_tab = (
[&info]() -> std::function<nlohmann::json(const nlohmann::json::array_t& tabList)>
{
if (!info.name.empty())
{
return [&info](const nlohmann::json::array_t& tabList)
{
for (const auto& tab : tabList) {
if (tab["title"].get<std::string>().find(info.name.data()) != std::string::npos) {
return tab;
}
}
return nlohmann::json{};
};
}
if (!info.id.empty())
{
return [&info](const nlohmann::json::array_t& tabList)
{
for (const auto& tab : tabList) {
if (tab["id"].get<std::string>() == info.id) {
return tab;
}
}
return nlohmann::json{};
};
}
if (!info.webSocketDebuggerUrl.empty())
{
return [&info](const nlohmann::json::array_t& tabList)
{
for (const auto& tab : tabList) {
if (tab["webSocketDebuggerUrl"].get<std::string>() == info.webSocketDebuggerUrl) {
return tab;
}
}
return nlohmann::json{};
};
}
return nullptr;
}
)();
if (find_tab == nullptr) {
return false;
}
const auto tabs = AvailableTabs(port);
const auto tab = find_tab(tabs);
if (tab.empty()) {
return false;
}
const auto res = InjectJs(tab["title"].get<std::string>(), tab["webSocketDebuggerUrl"].get<std::string>(), glossi_tweaks_js_, port);
if (res.is_boolean() && res.get<bool>()) {
glossi_tweaks_injected_map_[tab["id"].get<std::string>()] = true;
spdlog::trace("CEFInject: GlosSITweaks injected into tab: {}", tab["title"].get<std::string>());
return true;
}
return false;
}
bool SteamTweaks::uninstallTweaks(bool force)
{
if (!CEFDebugAvailable()) {
return false;
}
auto_inject_ = false;
if (auto_inject_future_.valid()) {
auto_inject_future_.wait();
}
if (glossi_tweaks_injected_map_.empty() && !force) {
return false;
}
std::vector<std::future<void>> futures;
for (auto ts = AvailableTabs(); auto & tab : ts) {
futures.push_back(std::async(std::launch::async, [this, &tab]()
{
InjectJs(tab["title"].get<std::string>(), tab["webSocketDebuggerUrl"].get<std::string>(), uninstall_glossi_tweaks_js_);
}));
}
for (auto& f : futures)
{
if (f.valid()) {
f.wait();
}
}
glossi_tweaks_injected_map_.clear();
return true;
}
void SteamTweaks::update(float elapsed_time)
{
if (!auto_inject_) {
return;
}
using namespace std::chrono_literals;
if (auto_inject_future_.valid() && auto_inject_future_.wait_for(0ms) != std::future_status::ready) {
time_since_last_update_ = 0.0f;
return;
}
time_since_last_update_ += elapsed_time;
if (time_since_last_update_ < update_interval_) {
return;
}
time_since_last_update_ = 0.0f;
spdlog::trace("CEFInject: Starting auto inject GlosSITweaks");
auto_inject_future_ = std::async(std::launch::async, [this]() {
if (js_tweaks_cache_.empty()) [[unlikely]] {
readAvailableTweaks();
}
if (glossi_tweaks_js_.empty()) [[unlikely]]
{
if (!readGlosSITweaksJs()) {
return;
}
}
auto futures = std::vector<std::future<void>>{};
auto tabs = AvailableTabs();
for (auto& tab : tabs) {
if (glossi_tweaks_injected_map_.contains(tab["id"].get<std::string>())) {
continue;
}
glossi_tweaks_injected_map_[tab["id"].get<std::string>()] = true;
futures.push_back(std::async([this, &tab]()
{
InjectJs(tab["title"].get<std::string>(), tab["webSocketDebuggerUrl"].get<std::string>(), glossi_tweaks_js_);
for (auto& [path, js] : js_tweaks_cache_) {
const auto dir_name = path.parent_path().filename();
if (path_tab_map_.contains(dir_name.wstring())) {
if (tab["title"].get<std::string>().find(path_tab_map_.at(dir_name.wstring())) != std::string::npos) {
InjectJs(tab["title"].get<std::string>(), tab["webSocketDebuggerUrl"].get<std::string>(), js);
}
}
}
}));
}
for (auto& f : futures)
{
if (f.valid()) {
f.wait();
}
}
spdlog::trace("CEFInject: Auto Inject thread done");
});
}
bool SteamTweaks::isAutoInject() const
{
return auto_inject_;
}
void SteamTweaks::setAutoInject(const bool auto_inject)
{
auto_inject_ = auto_inject;
}
bool SteamTweaks::readGlosSITweaksJs()
{
if (glossi_tweaks_js_.empty())
{
spdlog::trace("CEFInject: Loadings GlosSITweaks.js");
auto glossi_path = util::path::getGlosSIDir();
glossi_path /= steam_tweaks_path_;
glossi_path /= "GlosSITweaks.js";
if (!std::filesystem::exists(glossi_path))
{
spdlog::error("CEFInject: GlosSITweaks.js not found");
return false;
}
std::wifstream js_file(glossi_path);
glossi_tweaks_js_ = { (std::istreambuf_iterator<wchar_t>(js_file)),
std::istreambuf_iterator<wchar_t>() };
if (glossi_tweaks_js_.empty()) {
spdlog::error("CEFInject: GlosSITweaks.js empty?");
return false;
}
js_file.close();
}
return true;
}
void SteamTweaks::readAvailableTweaks(bool builtin)
{
auto tweaks_path = builtin ? util::path::getGlosSIDir() : util::path::getDataDirPath();
spdlog::log(
builtin ? spdlog::level::trace : spdlog::level::debug,
"CEFInject: Loading {} {} {}",
builtin ? "builtin" : "user",
"tweaks from",
tweaks_path.string()
);
tweaks_path /= steam_tweaks_path_;
if (!std::filesystem::exists(tweaks_path))
{
return;
}
auto find_tweak_files = [this](std::wstring_view path, auto&& recurse) -> void
{
for (const auto& dir_file : std::filesystem::directory_iterator(path))
{
if (std::filesystem::is_directory(dir_file))
{
recurse(dir_file.path().wstring(), recurse);
continue;
}
if (std::filesystem::is_regular_file(dir_file) && dir_file.path().extension() == ".js")
{
if (dir_file.path().filename() == "GlosSITweaks.js") {
continue;
}
std::wifstream js_file(dir_file.path());
std::wstring tweaks_js = { (std::istreambuf_iterator<wchar_t>(js_file)),
std::istreambuf_iterator<wchar_t>() };
if (tweaks_js.empty()) {
continue;
}
js_file.close();
js_tweaks_cache_[dir_file.path().wstring()] = tweaks_js;
}
}
};
find_tweak_files(tweaks_path.wstring(), find_tweak_files);
for (const auto& tweak : js_tweaks_cache_ | std::views::keys) {
spdlog::debug(
"CEFInject: Found {} tweak: {}",
builtin ? "builtin" : "user",
tweak.string()
);
}
}
}

@ -1,104 +0,0 @@
/*
Copyright 2021-2023 Peter Repukat - FlatspotSoftware
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#pragma once
#include <future>
#include <httplib.h>
#include <nlohmann/json.hpp>
namespace CEFInject
{
namespace internal {
httplib::Client GetHttpClient(uint16_t port);
static inline uint16_t port_ = 8080;
}
inline void setPort(uint16_t port)
{
internal::port_ = port;
}
bool CEFDebugAvailable(uint16_t port = internal::port_);
std::vector<std::wstring> AvailableTabNames(uint16_t port = internal::port_);
nlohmann::basic_json<> AvailableTabs(uint16_t port = internal::port_);
nlohmann::basic_json<> InjectJs(std::string_view tab_name, std::string_view debug_url, std::wstring_view js, uint16_t port = internal::port_);
nlohmann::basic_json<> InjectJsByName(std::wstring_view tabname, std::wstring_view js, uint16_t port = internal::port_);
class WSAStartupWrap
{
public:
explicit WSAStartupWrap();
~WSAStartupWrap();
private:
bool wsa_startup_succeeded_ = false;
};
class SteamTweaks
{
public:
SteamTweaks() = default;
struct Tab_Info
{
std::string name;
std::string id;
std::string webSocketDebuggerUrl;
};
bool injectGlosSITweaks(std::string_view tab_name, uint16_t port = internal::port_);
bool injectGlosSITweaks(const Tab_Info& info, uint16_t port = internal::port_);
bool uninstallTweaks(bool force = false);
void update(float elapsed_time);
[[nodiscard]] bool isAutoInject() const;
void setAutoInject(const bool auto_inject);
private:
bool readGlosSITweaksJs();
void readAvailableTweaks(bool builtin = true);
bool auto_inject_ = false;
static constexpr float update_interval_ = 30.f;
float time_since_last_update_ = update_interval_;
using tab_id = std::string;
std::map<tab_id, bool> glossi_tweaks_injected_map_;
std::future<void> auto_inject_future_;
std::wstring glossi_tweaks_js_;
std::map<std::filesystem::path, std::wstring> js_tweaks_cache_;
using path_name = std::wstring;
using tab_name = std::string;
static inline const std::map<path_name, tab_name> path_tab_map_ = {
{L"SharedContext", "Steam Shared Context"},
{L"Overlay", "HOW TF GET OVERLAY TAB NAME?"}, // TODO: Figure out how to get the overlay tab name
{L"GamepadUI", "Steam Big Picture Mode"},
};
static constexpr std::string_view steam_shared_ctx_tab_name_ = "Steam Shared Context";
static constexpr std::string_view steam_tweaks_path_ = "SteamTweaks";
static constexpr std::wstring_view uninstall_glossi_tweaks_js_ = LR"(
(() => {
return window.GlosSITweaks?.GlosSI?.uninstall();
})();
)";
};
namespace internal {
static WSAStartupWrap wsa_startup_wrap{};
}
}

@ -1,165 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<ItemGroup>
<ClInclude Include="CEFInject.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="CEFInject.cpp" />
</ItemGroup>
<PropertyGroup Label="Globals">
<VCProjectVersion>16.0</VCProjectVersion>
<Keyword>Win32Proj</Keyword>
<ProjectGuid>{74fba967-ab7e-43ea-b561-3f4821954b3b}</ProjectGuid>
<RootNamespace>CEFInjectLib</RootNamespace>
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<IncludePath>..\deps\cpp-httplib;..\deps\json\include;..\deps\easywsclient;..\deps\spdlog\include;$(IncludePath)</IncludePath>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<IncludePath>..\deps\cpp-httplib;..\deps\json\include;..\deps\easywsclient;..\deps\spdlog\include;$(IncludePath)</IncludePath>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<PrecompiledHeader>Use</PrecompiledHeader>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
</ClCompile>
<Link>
<SubSystem>
</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<PrecompiledHeader>Use</PrecompiledHeader>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
</ClCompile>
<Link>
<SubSystem>
</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>SPDLOG_WCHAR_TO_UTF8_SUPPORT;SPDLOG_WCHAR_FILENAMES;_SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING;_CRT_SECURE_NO_WARNINGS;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
<LanguageStandard>stdcpp20</LanguageStandard>
<FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
<EnableParallelCodeGeneration>true</EnableParallelCodeGeneration>
<EnableEnhancedInstructionSet>StreamingSIMDExtensions2</EnableEnhancedInstructionSet>
<FloatingPointModel>Fast</FloatingPointModel>
</ClCompile>
<Link>
<SubSystem>
</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>SPDLOG_WCHAR_TO_UTF8_SUPPORT;SPDLOG_WCHAR_FILENAMES;_SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING;_CRT_SECURE_NO_WARNINGS;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
<LanguageStandard>stdcpp20</LanguageStandard>
<FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
<EnableEnhancedInstructionSet>StreamingSIMDExtensions2</EnableEnhancedInstructionSet>
<FloatingPointModel>Fast</FloatingPointModel>
</ClCompile>
<Link>
<SubSystem>
</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

@ -1,27 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Header Files">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd</Extensions>
</Filter>
<Filter Include="Resource Files">
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter>
</ItemGroup>
<ItemGroup>
<ClInclude Include="CEFInject.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="CEFInject.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
</Project>

@ -4,22 +4,12 @@ Microsoft Visual Studio Solution File, Format Version 12.00
VisualStudioVersion = 17.3.32922.545
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "GlosSITarget", "GlosSITarget\GlosSITarget.vcxproj", "{076E263E-0687-4435-836E-8F4EF6668843}"
ProjectSection(ProjectDependencies) = postProject
{74FBA967-AB7E-43EA-B561-3F4821954B3B} = {74FBA967-AB7E-43EA-B561-3F4821954B3B}
EndProjectSection
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "GlosSIConfig", "GlosSIConfig\GlosSIConfig.vcxproj", "{4B42920B-3CC6-475F-A5B3-441337968483}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "UWPOverlayEnablerDLL", "UWPOverlayEnablerDLL\UWPOverlayEnablerDLL.vcxproj", "{50212575-87E2-40AB-87EE-EAED19C8EBB2}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "GlosSIWatchdog", "GlosSIWatchdog\GlosSIWatchdog.vcxproj", "{BF273B90-CB69-43C8-9AF6-F3256DAFD41E}"
ProjectSection(ProjectDependencies) = postProject
{74FBA967-AB7E-43EA-B561-3F4821954B3B} = {74FBA967-AB7E-43EA-B561-3F4821954B3B}
EndProjectSection
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CEFInjectLib", "CEFInjectLib\CEFInjectLib.vcxproj", "{74FBA967-AB7E-43EA-B561-3F4821954B3B}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "common", "common\common.vcxproj", "{DFED4B7E-D04C-442B-BB48-5B6068A6B31B}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@ -59,20 +49,6 @@ Global
{BF273B90-CB69-43C8-9AF6-F3256DAFD41E}.Release|x64.Build.0 = Release|x64
{BF273B90-CB69-43C8-9AF6-F3256DAFD41E}.Release|x86.ActiveCfg = Release|Win32
{BF273B90-CB69-43C8-9AF6-F3256DAFD41E}.Release|x86.Build.0 = Release|Win32
{74FBA967-AB7E-43EA-B561-3F4821954B3B}.Debug|x64.ActiveCfg = Debug|x64
{74FBA967-AB7E-43EA-B561-3F4821954B3B}.Debug|x64.Build.0 = Debug|x64
{74FBA967-AB7E-43EA-B561-3F4821954B3B}.Debug|x86.ActiveCfg = Debug|Win32
{74FBA967-AB7E-43EA-B561-3F4821954B3B}.Debug|x86.Build.0 = Debug|Win32
{74FBA967-AB7E-43EA-B561-3F4821954B3B}.Release|x64.ActiveCfg = Release|x64
{74FBA967-AB7E-43EA-B561-3F4821954B3B}.Release|x64.Build.0 = Release|x64
{74FBA967-AB7E-43EA-B561-3F4821954B3B}.Release|x86.ActiveCfg = Release|Win32
{74FBA967-AB7E-43EA-B561-3F4821954B3B}.Release|x86.Build.0 = Release|Win32
{DFED4B7E-D04C-442B-BB48-5B6068A6B31B}.Debug|x64.ActiveCfg = Debug|x64
{DFED4B7E-D04C-442B-BB48-5B6068A6B31B}.Debug|x86.ActiveCfg = Debug|Win32
{DFED4B7E-D04C-442B-BB48-5B6068A6B31B}.Debug|x86.Build.0 = Debug|Win32
{DFED4B7E-D04C-442B-BB48-5B6068A6B31B}.Release|x64.ActiveCfg = Release|x64
{DFED4B7E-D04C-442B-BB48-5B6068A6B31B}.Release|x86.ActiveCfg = Release|Win32
{DFED4B7E-D04C-442B-BB48-5B6068A6B31B}.Release|x86.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

@ -1,43 +0,0 @@
{
"version": "2.0.0",
"tasks": [
{
"type": "shell",
"label": "Build GlosSIConfig (Debug)",
"command": "msbuild.exe",
"args": [
"GlosSI.sln",
"/target:GlosSIConfig",
"/p:Configuration=Debug",
"/p:Platform=x64"
],
"options": {
"cwd": "${workspaceFolder}/..",
},
"problemMatcher": ["$msCompile"],
"group": {
"kind": "build",
},
},
{
"type": "shell",
"label": "Re-Build GlosSIConfig (Debug)",
"command": "msbuild.exe",
"args": [
"GlosSI.sln",
"/target:GlosSIConfig:Rebuild",
"/p:Configuration=Debug",
"/p:Platform=x64"
],
"options": {
"cwd": "${workspaceFolder}/..",
},
"problemMatcher": ["$msCompile"],
"group": {
"kind": "build",
},
}
]
}

@ -2,7 +2,6 @@
#include <QQuickImageProvider>
#include <Windows.h>
#include <QRegularExpression>
#include <shellapi.h>
class ExeImageProvider : public QQuickImageProvider {
public:
ExeImageProvider()

@ -69,12 +69,8 @@
<AdditionalUsingDirectories>%(AdditionalUsingDirectories)</AdditionalUsingDirectories>
<AdditionalOptions>/Zc:__cplusplus /Zc:twoPhase- %(AdditionalOptions)</AdditionalOptions>
<CompileAsWinRT>false</CompileAsWinRT>
<PreprocessorDefinitions>NOMINMAX;CONFIGAPP;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions>NOMINMAX;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>..\deps\WinReg;..\deps\fifo_map\src;..\deps\Shortcuts_VDF\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
<EnableParallelCodeGeneration>true</EnableParallelCodeGeneration>
<EnableEnhancedInstructionSet>StreamingSIMDExtensions2</EnableEnhancedInstructionSet>
<FloatingPointModel>Fast</FloatingPointModel>
</ClCompile>
<Manifest>
<EnableDpiAwareness>true</EnableDpiAwareness>
@ -96,12 +92,8 @@
<AdditionalUsingDirectories>%(AdditionalUsingDirectories)</AdditionalUsingDirectories>
<AdditionalOptions>/Zc:__cplusplus /Zc:zwoPhase- /permissive- %(AdditionalOptions)</AdditionalOptions>
<CompileAsWinRT>false</CompileAsWinRT>
<PreprocessorDefinitions>NOMINMAX;CONFIGAPP;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions>NOMINMAX;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>..\deps\WinReg;..\deps\fifo_map\src;..\deps\Shortcuts_VDF\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
<EnableParallelCodeGeneration>true</EnableParallelCodeGeneration>
<EnableEnhancedInstructionSet>StreamingSIMDExtensions2</EnableEnhancedInstructionSet>
<FloatingPointModel>Fast</FloatingPointModel>
</ClCompile>
<Manifest>
<EnableDpiAwareness>true</EnableDpiAwareness>
@ -144,7 +136,6 @@
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="..\common\UnhookUtil.cpp" />
<ClCompile Include="ExeImageProvider.cpp" />
<ClCompile Include="main.cpp" />
<ClCompile Include="UIModel.cpp" />
@ -152,9 +143,7 @@
<None Include="GetAUMIDs.ps1" />
<None Include="qml\AdvancedTargetSettings.qml" />
<None Include="qml\CollapsiblePane.qml" />
<None Include="qml\EGSSelectDialog.qml" />
<None Include="qml\GlobalConf.qml" />
<None Include="qml\SteamGridDialog.qml" />
<None Include="qml\SteamInputXboxDisabledDialog.qml" />
<None Include="qml\AddSelectTypeDialog.qml" />
<None Include="qml\FluentTextInput.qml" />

@ -37,9 +37,6 @@
<ClCompile Include="ExeImageProvider.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\common\UnhookUtil.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<None Include="qml\main.qml">
@ -83,12 +80,6 @@
<None Include="qml\GlobalConf.qml">
<Filter>qml</Filter>
</None>
<None Include="qml\EGSSelectDialog.qml">
<Filter>qml</Filter>
</None>
<None Include="qml\SteamGridDialog.qml">
<Filter>qml</Filter>
</None>
</ItemGroup>
<ItemGroup>
<QtMoc Include="UIModel.h">

File diff suppressed because it is too large Load Diff

@ -1,5 +1,5 @@
/*
Copyright 2021-2023 Peter Repukat - FlatspotSoftware
Copyright 2021-2022 Peter Repukat - FlatspotSoftware
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@ -16,14 +16,10 @@ limitations under the License.
#include "UIModel.h"
#include <QDir>
#include <QFont>
#include <QGuiApplication>
#include <QJsonDocument>
#include <QJsonArray>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QMetaType>
#include <WinReg/WinReg.hpp>
@ -34,16 +30,16 @@ limitations under the License.
#include <Windows.h>
#endif
#include "ExeImageProvider.h"
#include "../version.hpp"
#include "../common/UnhookUtil.h"
#include "../common/util.h"
UIModel::UIModel() : QObject(nullptr)
{
auto path = util::path::getDataDirPath();
auto path = std::filesystem::temp_directory_path().parent_path().parent_path().parent_path();
path /= "Roaming";
path /= "GlosSI";
if (!std::filesystem::exists(path))
std::filesystem::create_directories(path);
qDebug() << "Version: " << getVersionString();
@ -53,22 +49,9 @@ UIModel::UIModel() : QObject(nullptr)
if (!std::filesystem::exists(path))
std::filesystem::create_directories(path);
auto defaultConf = getDefaultConf();
saveDefaultConf(defaultConf);
parseShortcutVDF();
readTargetConfigs();
updateCheck();
readUnhookBytes();
auto font = QGuiApplication::font();
font.setPointSize(11);
font.setFamily("Roboto");
QGuiApplication::setFont(font);
std::ofstream{getSteamPath() / ".cef-enable-remote-debugging"};
}
void UIModel::readTargetConfigs()
@ -111,43 +94,22 @@ void UIModel::addTarget(QVariant shortcut)
emit targetListChanged();
}
bool UIModel::updateTarget(int index, QVariant shortcut)
void UIModel::updateTarget(int index, QVariant shortcut)
{
const auto map = shortcut.toMap();
const auto json = QJsonObject::fromVariantMap(map);
const auto was_in_steam_ = isInSteam(shortcut);
auto oldSteamName = targets_[index].toMap()["name"].toString();
auto oldName =
targets_[index].toMap()["name"].toString().replace(QRegularExpression("[\\\\/:*?\"<>|]"), "") + ".json";
auto oldPath = config_path_;
oldPath /= config_dir_name_.toStdString();
oldPath /= (oldName).toStdString();
std::filesystem::remove(oldPath);
auto path = config_path_;
path /= config_dir_name_.toStdString();
path /= (oldName).toStdString();
std::filesystem::remove(path);
writeTarget(json, map["name"].toString());
targets_.replace(index, QJsonDocument(json).toVariant());
emit targetListChanged();
auto path = config_path_;
path /= config_dir_name_.toStdString();
path /= (map["name"].toString()).toStdString();
if (was_in_steam_) {
if (removeFromSteam(oldSteamName, QString::fromStdWString(path.wstring()))) {
if (!addToSteam(shortcut, QString::fromStdWString(path.wstring()))) {
qDebug() << "Couldn't add shortcut \"" << (map["name"].toString()) << "\" to Steam when updating";
return false;
}
return true;
}
qDebug() << "Couldn't remove shortcut \"" << oldName << "\" from Steam when updating";
return false;
} else {
return true;
}
}
void UIModel::deleteTarget(int index)
@ -162,13 +124,11 @@ void UIModel::deleteTarget(int index)
emit targetListChanged();
}
bool UIModel::isInSteam(QVariant shortcut) const
bool UIModel::isInSteam(QVariant shortcut)
{
const auto map = shortcut.toMap();
for (auto& steam_shortcut : shortcuts_vdf_) {
if (
map["name"].toString() == QString::fromStdString(steam_shortcut.appname) ||
map["oldName"].toString() == QString::fromStdString(steam_shortcut.appname)) {
if (map["name"].toString() == QString::fromStdString(steam_shortcut.appname)) {
if (QString::fromStdString(steam_shortcut.exe).toLower().contains("glossitarget.exe")) {
return true;
}
@ -178,41 +138,6 @@ bool UIModel::isInSteam(QVariant shortcut) const
return false;
}
uint32_t UIModel::getAppId(QVariant shortcut)
{
if (!isInSteam(shortcut)) {
return 0;
}
const auto map = shortcut.toMap();
for (auto& steam_shortcut : shortcuts_vdf_) {
if (map["name"].toString() == QString::fromStdString(steam_shortcut.appname)) {
if (QString::fromStdString(steam_shortcut.exe).toLower().contains("glossitarget.exe")) {
if (steam_shortcut.appid == 0) {
parseShortcutVDF();
return getAppId(shortcut);
}
return steam_shortcut.appid;
}
}
}
return 0;
}
Q_INVOKABLE QString UIModel::getGameId(QVariant shortcut) {
/*
* enum SteamLaunchableType
{
App = 0,
GameMod = 1,
Shortcut = 2,
P2P = 3
}
*/
uint64_t gameId = (((uint64_t)getAppId(shortcut) << 32) | ((uint32_t)2 << 24) | 0);
return QVariant(gameId).toString();
}
bool UIModel::addToSteam(QVariant shortcut, const QString& shortcutspath, bool from_cmd)
{
QDir appDir = QGuiApplication::applicationDirPath();
@ -350,18 +275,13 @@ void UIModel::enableSteamInputXboxSupport()
}
}
bool UIModel::restartSteam(const QString& steamURL)
bool UIModel::restartSteam()
{
const auto path = getSteamPath();
if (QProcess::execute("taskkill.exe", {"/im", steam_executable_name_, "/f"}) != 0) {
return false;
}
if (steamURL.isEmpty()) {
QProcess::startDetached(QString::fromStdWString(path) + "/" + steam_executable_name_);
}
else {
system((QString::fromLatin1("start ") + steamURL).toStdString().c_str());
}
QProcess::startDetached(QString::fromStdWString(path) + "/" + steam_executable_name_);
return true;
}
@ -387,92 +307,74 @@ void UIModel::updateCheck()
QVariantMap UIModel::getDefaultConf() const
{
auto path = util::path::getDataDirPath();
auto path = std::filesystem::temp_directory_path().parent_path().parent_path().parent_path();
path /= "Roaming";
path /= "GlosSI";
path /= "default.json";
QJsonObject defaults = {
{"icon", QJsonValue::Null},
{"name", QJsonValue::Null},
{"version", 1},
{"extendedLogging", false},
{"snapshotNotify", false},
{"steamgridApiKey", QJsonValue::Null},
{"steamPath",
QJsonValue::fromVariant(QString::fromStdWString(getSteamPath(false).wstring()))},
{"steamUserId",
QJsonValue::fromVariant(QString::fromStdWString(getSteamUserId(false)))},
{"globalModeGameId", ""},
{"globalModeUseGamepadUI", true},
{"minimizeSteamGamepadUI", true},
{"controller",
QJsonObject{{"maxControllers", -1},
{"emulateDS4", false},
{"allowDesktopConfig", false},
{"updateRate", 144}
}},
{"devices",
QJsonObject{
{"hideDevices", true},
{"realDeviceIds", false},
}},
{"launch",
QJsonObject{
{"closeOnExit", true},
{"launch", false},
{"launchAppArgs", QJsonValue::Null},
{"launchPath", QJsonValue::Null},
{"waitForChildProcs", true},
{"launcherProcesses", QJsonArray{}},
{"ignoreLauncher", true},
{"killLauncher", false},
}},
{"window",
QJsonObject{
{"disableOverlay", false},
{"hideAltTab", true},
{"maxFps", QJsonValue::Null},
{"scale", QJsonValue::Null},
{"windowMode", false},
{"disableGlosSIOverlay", false},
{"opaqueSteamOverlay", false}
}},
};
if (std::filesystem::exists(path)) {
QFile file(QString::fromStdWString(path));
if (file.open(QIODevice::ReadOnly)) {
const auto data = file.readAll();
file.close();
auto json = QJsonDocument::fromJson(data).object();
const auto applyDefaults = [](QJsonObject obj, const QJsonObject& defaults,
auto applyDefaultsFn) -> QJsonObject {
for (const auto& key : defaults.keys()) {
qDebug() << key << ": " << obj[key];
if ((obj[key].isUndefined() || obj[key].isNull()) && !defaults[key].isNull()) {
obj[key] = defaults.value(key);
}
if (obj.value(key).isObject()) {
obj[key] =
applyDefaultsFn(obj[key].toObject(), defaults.value(key).toObject(), applyDefaultsFn);
}
}
return obj;
};
json = applyDefaults(json, defaults, applyDefaults);
return json.toVariantMap();
return QJsonDocument::fromJson(data).object().toVariantMap();
}
}
saveDefaultConf(defaults.toVariantMap());
QJsonObject obj = {
{"icon", QJsonValue::Null},
{"name", QJsonValue::Null},
{"version", 1},
{"extendedLogging", false},
{"snapshotNotify", false},
{
"controller",
QJsonObject{
{"maxControllers", 1},
{"emulateDS4", false},
{"allowDesktopConfig", false}
}
},
{
"devices",
QJsonObject{
{"hideDevices", true},
{"realDeviceIds", false},
}
},
{
"launch",
QJsonObject{
{"closeOnExit", true},
{"launch", false},
{"launchAppArgs", QJsonValue::Null},
{"launchPath", QJsonValue::Null},
{"waitForChildProcs", true},
}
},
{
"window",
QJsonObject{
{"disableOverlay", false},
{"maxFps", QJsonValue::Null},
{"scale", QJsonValue::Null},
{"windowMode", false},
}
},
};
saveDefaultConf(obj.toVariantMap());
return getDefaultConf();
}
void UIModel::saveDefaultConf(QVariantMap conf) const
{
auto path = util::path::getDataDirPath();
auto path = std::filesystem::temp_directory_path().parent_path().parent_path().parent_path();
path /= "Roaming";
path /= "GlosSI";
path /= "default.json";
QFile file(path);
@ -485,108 +387,10 @@ void UIModel::saveDefaultConf(QVariantMap conf) const
file.close();
}
Q_INVOKABLE QVariant UIModel::globalModeShortcutConf() {
for (auto& target : targets_) {
const auto map = target.toMap();
if (map["name"] == "GlosSI GlobalMode/Desktop") {
return target;
}
}
return QVariant();
}
Q_INVOKABLE bool UIModel::globalModeShortcutExists() {
const auto map = globalModeShortcutConf().toMap();
if (map["name"] == "GlosSI GlobalMode/Desktop") {
return true;
}
return false;
}
Q_INVOKABLE uint32_t UIModel::globalModeShortcutAppId() {
if (!globalModeShortcutExists()) {
return 0;
}
return getAppId(globalModeShortcutConf());
}
Q_INVOKABLE QString UIModel::globalModeShortcutGameId() {
if (!globalModeShortcutExists()) {
return "";
}
return getGameId(globalModeShortcutConf());
}
#ifdef _WIN32
QVariantList UIModel::uwpApps() { return UWPFetch::UWPAppList(); }
#endif
QVariantList UIModel::egsGamesList() const
{
wchar_t* program_data_path_str;
std::filesystem::path path;
if (SHGetKnownFolderPath(FOLDERID_ProgramData, KF_FLAG_CREATE, NULL, &program_data_path_str) != S_OK) {
qDebug() << "Couldn't get ProgramDataPath";
return {{"InstallLocation", "Error"}};
}
path = std::filesystem::path(program_data_path_str);
path /= egs_games_json_path_;
QFile file(path);
if (file.open(QIODevice::ReadOnly)) {
const auto data = file.readAll();
file.close();
auto json = QJsonDocument::fromJson(data).object();
if (json["InstallationList"].isArray()) {
return json["InstallationList"].toVariant().toList();
}
qDebug() << "InstallationList does not exist!";
}
qDebug() << "Couldn't read EGS LauncherInstalled.dat " << path;
return {{"InstallLocation", "Error"}};
}
void UIModel::loadSteamGridImages()
{
std::filesystem::path path = QCoreApplication::applicationDirPath().toStdWString();
path /= "steamgrid.exe";
auto api_key = getDefaultConf().value("steamgridApiKey").toString();
steamgrid_output_.clear();
steamgrid_proc_.setProgram(path.string().c_str());
steamgrid_proc_.setArguments({"-nonsteamonly", "--onlymissingartwork", "--steamgriddb", api_key});
connect(&steamgrid_proc_, &QProcess::readyReadStandardOutput, this, &UIModel::onSteamGridReadReady);
steamgrid_proc_.start();
steamgrid_proc_.write("\n");
}
QString UIModel::getGridImagePath(QVariant shortcut)
{
if (!foundSteam()) {
return "";
}
const auto& app_id = getAppId(shortcut);
if (app_id == 0) {
return "";
}
const std::filesystem::path grid_dir =
std::wstring(getSteamPath()) + user_data_path_.toStdWString() + getSteamUserId() + L"/config/grid";
if (!std::filesystem::exists(grid_dir)) {
return "";
}
const std::vector<std::string> extensions = {".png", ".jpg"};
for (const auto& entry : std::filesystem::directory_iterator(grid_dir)) {
if (entry.is_regular_file() &&
std::ranges::find(extensions, entry.path().extension().string()) != extensions.end() &&
entry.path().filename().string().find(std::to_string(app_id)) != std::string::npos) {
return QString::fromStdString(entry.path().string());
}
}
return "";
}
bool UIModel::writeShortcutsVDF(const std::wstring& mode, const std::wstring& name, const std::wstring& shortcutspath,
bool is_admin_try) const
{
@ -648,15 +452,6 @@ bool UIModel::writeShortcutsVDF(const std::wstring& mode, const std::wstring& na
#endif
}
bool UIModel::getIsDebug() const
{
#ifdef _DEBUG
return true;
#else
return false;
#endif
}
bool UIModel::getIsWindows() const { return is_windows_; }
bool UIModel::hasAcrylicEffect() const { return has_acrylic_affect_; }
@ -667,8 +462,6 @@ void UIModel::setAcrylicEffect(bool has_acrylic_affect)
emit acrylicChanged();
}
QStringList UIModel::getSteamgridOutput() const { return steamgrid_output_; }
void UIModel::onAvailFilesResponse(QNetworkReply* reply)
{
@ -682,7 +475,9 @@ void UIModel::onAvailFilesResponse(QNetworkReply* reply)
const auto defaultConf = getDefaultConf();
bool snapshotNotify =
defaultConf.contains("snapshotNotify") ? defaultConf["snapshotNotify"].toJsonValue().toBool() : false;
defaultConf.contains("snapshotNotify")
? defaultConf["snapshotNotify"].toJsonValue().toBool()
: false;
struct VersionInfo {
int major;
@ -695,9 +490,8 @@ void UIModel::onAvailFilesResponse(QNetworkReply* reply)
std::vector<std::pair<QString, VersionInfo>> new_versions;
for (const auto& info :
json.keys() | std::ranges::views::filter([this, &json, snapshotNotify](const auto& key) {
return notify_on_snapshots_
? true
: json[key].toObject().value("type") == (snapshotNotify ? "snapshot" : "release");
return notify_on_snapshots_ ? true
: json[key].toObject().value("type") == (snapshotNotify ? "snapshot" : "release");
}) | std::ranges::views::transform([&json](const auto& key) -> std::pair<QString, VersionInfo> {
const auto versionString = json[key].toObject().value("version").toString();
const auto cleanVersion = versionString.split("-")[0];
@ -741,16 +535,6 @@ void UIModel::onAvailFilesResponse(QNetworkReply* reply)
}
}
void UIModel::onSteamGridReadReady()
{
const auto output = QString::fromLocal8Bit(steamgrid_proc_.readAllStandardOutput());
steamgrid_output_.push_back(output);
emit steamgridOutputChanged();
if (output.toLower().contains("token is missing or invalid")) {
steamgrid_proc_.kill();
}
}
void UIModel::writeTarget(const QJsonObject& json, const QString& name) const
{
auto path = config_path_;
@ -762,10 +546,7 @@ void UIModel::writeTarget(const QJsonObject& json, const QString& name) const
return;
}
auto json_ob = QJsonDocument(json).object();
json_ob.remove("steamgridApiKey");
file.write(QString(QJsonDocument(json_ob).toJson(QJsonDocument::Indented)).toStdString().data());
file.write(QString(QJsonDocument(json).toJson(QJsonDocument::Indented)).toStdString().data());
file.close();
}
@ -773,33 +554,20 @@ QString UIModel::getVersionString() const { return QString(version::VERSION_STR)
QString UIModel::getNewVersionName() const { return new_version_name_; }
std::filesystem::path UIModel::getSteamPath(bool tryConfig) const
std::filesystem::path UIModel::getSteamPath() const
{
QVariantMap defaultConf;
if (tryConfig) {
defaultConf = getDefaultConf();
}
try {
#ifdef _WIN32
// TODO: check if keys/value exist
// steam should always be open and have written reg values...
winreg::RegKey key{HKEY_CURRENT_USER, L"SOFTWARE\\Valve\\Steam"};
if (!key.IsValid()) {
if (defaultConf.contains("steamPath") &&
QMetaType::canConvert(defaultConf["steamPath"].metaType(), QMetaType(QMetaType::QString))) {
return defaultConf["steamPath"].toString().toStdWString();
}
return "";
}
const auto res = key.GetStringValue(L"SteamPath");
return res;
}
catch (...) {
if (defaultConf.contains("steamPath") &&
QMetaType::canConvert(defaultConf["steamPath"].metaType(), QMetaType(QMetaType::QString))) {
return defaultConf["steamPath"].toString().toStdWString();
}
return "";
}
#else
@ -807,39 +575,23 @@ std::filesystem::path UIModel::getSteamPath(bool tryConfig) const
#endif
}
std::wstring UIModel::getSteamUserId(bool tryConfig) const
std::wstring UIModel::getSteamUserId() const
{
QVariantMap defaultConf;
if (tryConfig) {
defaultConf = getDefaultConf();
}
#ifdef _WIN32
try {
// TODO: check if keys/value exist
// steam should always be open and have written reg values...
winreg::RegKey key{HKEY_CURRENT_USER, L"SOFTWARE\\Valve\\Steam\\ActiveProcess"};
if (!key.IsValid()) {
if (defaultConf.contains("steamUserId") &&
QMetaType::canConvert(defaultConf["steamUserId"].metaType(), QMetaType(QMetaType::QString))) {
return defaultConf["steamUserId"].toString().toStdWString();
}
return L"0";
}
const auto res = std::to_wstring(key.GetDwordValue(L"ActiveUser"));
if (res == L"0") {
qDebug() << "Steam not open?";
if (defaultConf.contains("steamUserId") &&
QMetaType::canConvert(defaultConf["steamUserId"].metaType(), QMetaType(QMetaType::QString))) {
return defaultConf["steamUserId"].toString().toStdWString();
}
}
return res;
}
catch (...) {
if (defaultConf.contains("steamUserId") &&
QMetaType::canConvert(defaultConf["steamUserId"].metaType(), QMetaType(QMetaType::QString))) {
return defaultConf["steamUserId"].toString().toStdWString();
}
return L"0";
}
#else
@ -913,35 +665,3 @@ bool UIModel::isSteamInputXboxSupportEnabled() const
}
return true;
}
void UIModel::readUnhookBytes() const
{
std::map<std::string, std::string> unhook_bytes;
for (const auto& name : UnhookUtil::UNHOOK_BYTES_ORIGINAL_22000 | std::views::keys) {
auto bytes = UnhookUtil::ReadOriginalBytes(
name,
name.starts_with("Hid")
? L"hid.dll"
: name.starts_with("Setup")
? L"setupapi.dll"
: L"Kernel32.dll"
);
unhook_bytes[name] = bytes;
}
auto path = config_path_;
path /= "unhook_bytes";
QFile file(path);
if (!file.open(QIODevice::Truncate | QIODevice::ReadWrite)) {
qDebug() << "Couldn't open file for writing: " << path;
return;
}
for (const auto& [name, bytes] : unhook_bytes) {
file.write(
QString::fromStdString(name + ":").toStdString().data()
);
file.write(bytes.data(), bytes.size());
file.write("\n");
}
file.close();
}

@ -1,5 +1,5 @@
/*
Copyright 2021-2023 Peter Repukat - FlatspotSoftware
Copyright 2021-2022 Peter Repukat - FlatspotSoftware
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@ -17,7 +17,6 @@ limitations under the License.
#include <QJsonObject>
#include <QObject>
#include <QVariant>
#include <QProcess>
#include <filesystem>
#include <shortcuts_vdf.hpp>
@ -26,76 +25,56 @@ class QNetworkReply;
class UIModel : public QObject {
Q_OBJECT
Q_PROPERTY(bool isDebug READ getIsDebug CONSTANT)
Q_PROPERTY(bool isWindows READ getIsWindows CONSTANT)
Q_PROPERTY(bool hasAcrlyicEffect READ hasAcrylicEffect NOTIFY acrylicChanged)
Q_PROPERTY(QVariantList targetList READ getTargetList NOTIFY targetListChanged)
Q_PROPERTY(QVariantList uwpList READ uwpApps CONSTANT)
Q_PROPERTY(QVariantList egsList READ egsGamesList CONSTANT)
Q_PROPERTY(bool foundSteam READ foundSteam CONSTANT)
Q_PROPERTY(bool steamInputXboxSupportEnabled READ isSteamInputXboxSupportEnabled CONSTANT)
Q_PROPERTY(QString versionString READ getVersionString CONSTANT)
Q_PROPERTY(QString newVersionName READ getNewVersionName NOTIFY newVersionAvailable)
Q_PROPERTY(QStringList steamgridOutput READ getSteamgridOutput NOTIFY steamgridOutputChanged)
public:
UIModel();
Q_INVOKABLE void readTargetConfigs();
Q_INVOKABLE QVariantList getTargetList() const;
Q_INVOKABLE void addTarget(QVariant shortcut);
Q_INVOKABLE bool updateTarget(int index, QVariant shortcut);
Q_INVOKABLE void updateTarget(int index, QVariant shortcut);
Q_INVOKABLE void deleteTarget(int index);
Q_INVOKABLE bool isInSteam(QVariant shortcut) const;
Q_INVOKABLE uint32_t getAppId(QVariant shortcut);
Q_INVOKABLE QString getGameId(QVariant shortcut);
Q_INVOKABLE bool isInSteam(QVariant shortcut);
Q_INVOKABLE bool addToSteam(QVariant shortcut, const QString& shortcutspath, bool from_cmd = false);
bool addToSteam(const QString& name, const QString& shortcutspath, bool from_cmd = false);
Q_INVOKABLE bool removeFromSteam(const QString& name, const QString& shortcutspath, bool from_cmd = false);
Q_INVOKABLE QVariantMap manualProps(QVariant shortcut);
Q_INVOKABLE void enableSteamInputXboxSupport();
Q_INVOKABLE bool restartSteam(const QString& steamURL = "");
Q_INVOKABLE bool restartSteam();
Q_INVOKABLE void updateCheck();
Q_INVOKABLE QVariantMap getDefaultConf() const;
Q_INVOKABLE void saveDefaultConf(QVariantMap conf) const;
Q_INVOKABLE QVariant globalModeShortcutConf();
Q_INVOKABLE bool globalModeShortcutExists();
Q_INVOKABLE uint32_t globalModeShortcutAppId();
Q_INVOKABLE QString globalModeShortcutGameId();
#ifdef _WIN32
Q_INVOKABLE QVariantList uwpApps();
#endif
Q_INVOKABLE QVariantList egsGamesList() const;
Q_INVOKABLE void loadSteamGridImages();
Q_INVOKABLE QString getGridImagePath(QVariant shortcut);
[[nodiscard]] bool writeShortcutsVDF(const std::wstring& mode, const std::wstring& name,
const std::wstring& shortcutspath, bool is_admin_try = false) const;
bool getIsDebug() const;
bool getIsWindows() const;
[[nodiscard]] bool hasAcrylicEffect() const;
void setAcrylicEffect(bool has_acrylic_affect);
QStringList getSteamgridOutput() const;
signals:
void acrylicChanged();
void targetListChanged();
void newVersionAvailable();
void steamgridOutputChanged();
public slots:
void onAvailFilesResponse(QNetworkReply* reply);
void onSteamGridReadReady();
private:
#ifdef _WIN32
@ -113,17 +92,11 @@ class UIModel : public QObject {
QString user_data_path_ = "/userdata/";
QString steam_executable_name_ = "steam.exe";
const std::wstring_view egs_games_json_path_ =
L"Epic/UnrealEngineLauncher/LauncherInstalled.dat";
QVariantList targets_;
QString new_version_name_;
bool notify_on_snapshots_ = false;
QProcess steamgrid_proc_;
QStringList steamgrid_output_;
std::vector<VDFParser::Shortcut> shortcuts_vdf_;
void writeTarget(const QJsonObject& json, const QString& name) const;
@ -131,12 +104,10 @@ class UIModel : public QObject {
QString getVersionString() const;
QString getNewVersionName() const;
std::filesystem::path getSteamPath(bool tryConfig = true) const;
std::wstring getSteamUserId(bool tryConfig = true) const;
std::filesystem::path getSteamPath() const;
std::wstring getSteamUserId() const;
bool foundSteam() const;
void parseShortcutVDF();
bool isSteamInputXboxSupportEnabled() const;
void readUnhookBytes() const;
};

@ -1,5 +1,5 @@
/*
Copyright 2021-2023 Peter Repukat - FlatspotSoftware
Copyright 2021-2022 Peter Repukat - FlatspotSoftware
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.

@ -1,5 +1,5 @@
/*
Copyright 2021-2023 Peter Repukat - FlatspotSoftware
Copyright 2021-2022 Peter Repukat - FlatspotSoftware
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@ -22,8 +22,6 @@ limitations under the License.
#include <QFile>
#include <QTextStream>
#include "../common/util.h"
#ifdef _WIN32
#include <Windows.h>
#include <VersionHelpers.h>
@ -32,7 +30,6 @@ limitations under the License.
#include "ExeImageProvider.h"
#endif
#include "UIModel.h"
#include "WinEventFilter.h"
@ -92,7 +89,15 @@ void myMessageHandler(QtMsgType type, const QMessageLogContext&, const QString&
break;
}
auto path = util::path::getDataDirPath();
auto path = std::filesystem::temp_directory_path()
.parent_path()
.parent_path()
.parent_path();
path /= "Roaming";
path /= "GlosSI";
if (!std::filesystem::exists(path))
std::filesystem::create_directories(path);
QFile outFile(QString::fromStdWString(path) + "/glossiconfig.log");
outFile.open(QIODevice::WriteOnly | QIODevice::Append);
@ -110,7 +115,15 @@ int main(int argc, char* argv[])
#endif
if (argc < 3) {
auto path = util::path::getDataDirPath();
auto path = std::filesystem::temp_directory_path()
.parent_path()
.parent_path()
.parent_path();
path /= "Roaming";
path /= "GlosSI";
if (!std::filesystem::exists(path))
std::filesystem::create_directories(path);
QFile outFile(QString::fromStdWString(path) + "/glossiconfig.log");
outFile.open(QIODevice::WriteOnly);

@ -22,8 +22,5 @@
<file>qml/AdvancedTargetSettings.qml</file>
<file>qml/GlobalConf.qml</file>
<file>svg/settings_fill_white_24dp.svg</file>
<file>qml/EGSSelectDialog.qml</file>
<file>svg/add_photo_alternate_white_24dp.svg</file>
<file>qml/SteamGridDialog.qml</file>
</qresource>
</RCC>

@ -1,5 +1,5 @@
/*
Copyright 2021-2023 Peter Repukat - FlatspotSoftware
Copyright 2021-2022 Peter Repukat - FlatspotSoftware
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@ -15,9 +15,6 @@ limitations under the License.
*/
import QtQuick 6.2
import QtQuick.Controls 6.2
import QtQuick.Controls.Material 6.2
import QtQuick.Dialogs 6.2
Dialog {
id: dlg
anchors.centerIn: parent
@ -92,7 +89,7 @@ Dialog {
}
}
Button {
visible: uiModel.isWindows
visible: uiModel.isWindows
text: qsTr("UWP App")
highlighted: true
onClicked: function(){
@ -100,15 +97,6 @@ Dialog {
confirmed("uwp")
}
}
Button {
visible: uiModel.isWindows
text: qsTr("EGS Game")
highlighted: true
onClicked: function(){
close()
confirmed("egs")
}
}
}
}

@ -1,5 +1,5 @@
/*
Copyright 2021-2023 Peter Repukat - FlatspotSoftware
Copyright 2021-2022 Peter Repukat - FlatspotSoftware
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@ -29,13 +29,10 @@ CollapsiblePane {
property var shortcutInfo: ({})
readonly property bool isAppSettings: subTitle != ""
content:
Column {
spacing: 16
id: contentColumn
height: subTitleLabel.height + 16 + advancedLaunchPane.height + 16 + deviceWindowRow.height + 16 + commonPane.height
spacing: 16
Label {
id: subTitleLabel
width: parent.width
@ -45,7 +42,6 @@ CollapsiblePane {
}
RPane {
id: advancedLaunchPane
width: parent.width
radius: 4
Material.elevation: 32
@ -66,7 +62,7 @@ CollapsiblePane {
spacing: 2
CheckBox {
id: closeOnExit
text: qsTr("Close GlosSI when launched app quits and vice versa")
text: qsTr("Close when launched app quits")
checked: shortcutInfo.launch.closeOnExit
onCheckedChanged: function() {
shortcutInfo.launch.closeOnExit = checked
@ -78,7 +74,7 @@ CollapsiblePane {
}
}
Label {
text: qsTr("(Might cause issues with launcher-games)")
text: qsTr("Recommended to disable for launcher-games")
wrapMode: Text.WordWrap
width: parent.width
leftPadding: 32
@ -86,32 +82,12 @@ CollapsiblePane {
}
CheckBox {
id: waitForChildren
text: qsTr("Include child processes")
text: qsTr("Wait for child processes")
checked: shortcutInfo.launch.waitForChildProcs
onCheckedChanged: function(){
shortcutInfo.launch.waitForChildProcs = checked
}
}
/*CheckBox {
height: subTitle != "" || (shortcutInfo.launch.launchPath || "").includes("epicgames.launcher") ? 32 : 0
visible: subTitle != "" || (shortcutInfo.launch.launchPath || "").includes("epicgames.launcher")
id: ignoreEGS
text: qsTr("Ignore EpicGamesLauncher process")
checked: shortcutInfo.ignoreEGS
onCheckedChanged: function(){
shortcutInfo.ignoreEGS = checked
}
}
CheckBox {
height: subTitle != "" || (shortcutInfo.launch.launchPath || "").includes("epicgames.launcher") ? 32 : 0
visible: subTitle != "" || (shortcutInfo.launch.launchPath || "").includes("epicgames.launcher")
id: killEGS
text: qsTr("Kill EpicGamesLauncher process on exit")
checked: shortcutInfo.killEGS
onCheckedChanged: function(){
shortcutInfo.killEGS = checked
}
}*/
}
Column {
spacing: 2
@ -136,11 +112,10 @@ CollapsiblePane {
Row {
spacing: 16
width: parent.width
id: deviceWindowRow
RPane {
width: parent.width / 2 - 8
height: 324
height: 248
radius: 4
Material.elevation: 32
bgOpacity: 0.97
@ -261,14 +236,8 @@ CollapsiblePane {
width: 128
editable: true
value: shortcutInfo.controller.maxControllers
from: -1
from: 0
to: 4
textFromValue: function(value) {
if (value == -1) {
return qsTr("auto");
}
return Number(value);
}
onValueChanged: shortcutInfo.controller.maxControllers = value
}
RoundButton {
@ -277,12 +246,7 @@ CollapsiblePane {
helpInfoDialog.text =
qsTr("GlosSI will only provide [NUMBER] of controllers")
+ "\n"
+ qsTr("-1 ^= auto-detect")
+ "\n"
+ qsTr("auto detection only works upon launch")
+ "\n"
+ "\n"
+ qsTr("Required to manuelly set to actually connected controller count when using \"real device IDs\" ")
+ qsTr("Required to set to actually connected controller count when using \"real device IDs\" ")
helpInfoDialog.open()
}
width: 48
@ -301,7 +265,7 @@ CollapsiblePane {
}
RPane {
width: parent.width / 2 - 8
height: 324
height: 248
radius: 4
Material.elevation: 32
bgOpacity: 0.97
@ -370,76 +334,6 @@ CollapsiblePane {
height: 24
}
}
}
Item {
width: 1
height: 4
}
Row {
CheckBox {
id: hideAltTabCheckbox
text: qsTr("Hide GlosSI from Windowlist (Alt+Tab)")
checked: shortcutInfo.window.hideAltTab
onCheckedChanged: shortcutInfo.window.hideAltTab = checked
}
RoundButton {
onClicked: () => {
helpInfoDialog.titleText = qsTr("Hide GlosSI from Windowlist (Alt+Tab)")
helpInfoDialog.text =
qsTr("Hides GlosSI from the Windowlist (Alt+Tab)")
+ "\n"
+ qsTr("You can close the GlosSI-Window via the system-tray")
+ "\n"
+ "\n"
+ qsTr("Might help with Steam remote play.")
helpInfoDialog.open()
}
width: 48
height: 48
Material.elevation: 0
anchors.topMargin: 16
Image {
anchors.centerIn: parent
source: "qrc:/svg/help_outline_white_24dp.svg"
width: 24
height: 24
}
}
}
Item {
width: 1
height: 4
}
Row {
CheckBox {
id: disableGlosSIOverlayCheckbox
text: qsTr("Disable GlosSI overlay")
checked: shortcutInfo.window.disableGlosSIOverlay
onCheckedChanged: shortcutInfo.window.disableGlosSIOverlay = checked
}
RoundButton {
onClicked: () => {
helpInfoDialog.titleText = qsTr("Disable GlosSI overlay")
helpInfoDialog.text =
qsTr("Disables the additional GlosSI overlay")
+ "\n"
+ qsTr("but keeps the Steam overlay");
helpInfoDialog.open()
}
width: 48
height: 48
Material.elevation: 0
anchors.topMargin: 16
Image {
anchors.centerIn: parent
source: "qrc:/svg/help_outline_white_24dp.svg"
width: 24
height: 24
}
}
}
Item {
width: 1
@ -563,107 +457,9 @@ CollapsiblePane {
radius: 4
Material.elevation: 32
bgOpacity: 0.97
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

@ -1,5 +1,5 @@
/*
Copyright 2021-2023 Peter Repukat - FlatspotSoftware
Copyright 2021-2022 Peter Repukat - FlatspotSoftware
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.

@ -1,230 +0,0 @@
/*
Copyright 2021-2023 Peter Repukat - FlatspotSoftware
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import QtQuick 6.2
import QtQuick.Controls 6.2
import QtQuick.Layouts 6.2
import QtQuick.Controls.Material 6.2
Dialog {
id: dlg
anchors.centerIn: parent
signal confirmed(var param)
visible: false
modal: true
dim: true
parent: Overlay.overlay
Overlay.modal: Rectangle {
color: Qt.rgba(0,0,0,0.4)
opacity: backdropOpacity
Behavior on opacity {
NumberAnimation {
duration: 300
}
}
}
property real backdropOpacity: 1.0
property var unfilteredModel: null;
property var filteredModel: [];
onOpened: function() {
unfilteredModel = null;
unfilteredModel = uiModel.egsList;
listview.model = null;
filteredModel = [];
for(let i = 0; i < unfilteredModel.length; i++)
{
filteredModel.push(unfilteredModel[i])
}
listview.model = filteredModel
}
onClosed: function() {
listview.model = null;
unfilteredModel = null;
filteredModel = null;
}
enter: Transition {
NumberAnimation{target: content; property: "y"; from: parent.height; to: 16; duration: 300; easing.type: Easing.OutQuad }
NumberAnimation{target: background; property: "y"; from: parent.height; to: 0; duration: 300; easing.type: Easing.OutQuad }
NumberAnimation{target: dlg; property: "backdropOpacity"; from: 0; to: 1; duration: 300; easing.type: Easing.OutQuad }
}
exit: Transition {
NumberAnimation{target: content; property: "y"; from: 16; to: parent.height; duration: 300; easing.type: Easing.InQuad }
NumberAnimation{target: background; property: "y"; from: 0; to: parent.height; duration: 300; easing.type: Easing.InQuad }
NumberAnimation{target: dlg; property: "backdropOpacity"; from: 1; to: 0; duration: 300; easing.type: Easing.InQuad }
}
background: RPane {
id: background
radius: 4
Material.elevation: 64
bgOpacity: 0.97
}
contentItem: Item {
id: content
implicitWidth: listview.width
implicitHeight: listview.height + titlelabel.height + 16 + 64
clip: true
Label {
id: titlelabel
text: qsTr("Select Epic Games Launcher Game...")
font.pixelSize: 24
font.bold: true
}
FluentTextInput {
width: listview.width - 2
x: 1
anchors.top: titlelabel.bottom
anchors.topMargin: 8
id: searchBar
enabled: true
placeholderText: qsTr("Search...")
text: ""
onTextChanged: function() {
listview.model = null;
filteredModel = [];
for(let i = 0; i < unfilteredModel.length; i++)
{
if(unfilteredModel[i].AppName.toLowerCase().includes(searchBar.text.toLowerCase())) {
filteredModel.push(unfilteredModel[i])
}
}
listview.model = filteredModel
}
}
BusyIndicator {
running: visible
anchors.centerIn: parent
opacity: (!unfilteredModel || unfilteredModel.length == 0) ? 1 : 0
Behavior on opacity {
NumberAnimation {
duration: 350
easing.type: Easing.InOutQuad
}
}
visible: opacity == 0 ? false : true
}
Button {
anchors.right: parent.right
anchors.top: listview.bottom
anchors.topMargin: 16
anchors.rightMargin: 2
text: qsTr("Cancel")
onClicked: dlg.close()
}
ListView {
anchors.top: searchBar.bottom
anchors.topMargin: 16
id: listview
width: window.width * 0.45
height: window.height * 0.66
spacing: 0
clip: true
model: filteredModel
ScrollBar.vertical: ScrollBar {
}
opacity: (!unfilteredModel || unfilteredModel.length == 0) ? 0 : 1
Behavior on opacity {
ParallelAnimation {
NumberAnimation {
duration: 350
easing.type: Easing.InOutQuad
}
PropertyAnimation {
target: listview
property: "anchors.topMargin"
from: window.height * 0.75
to: 16
duration: 350
easing.type: Easing.InOutQuad
}
}
}
delegate: Item {
width: listview.width
height: textcolumn.implicitHeight > 72 ? 500 : 72
/*Image {
id: maybeIcon
width: textcolumn.implicitHeight > 72 ? 0 : 56
height: 56
anchors.left: parent.left
anchors.verticalCenter: parent.verticalCenter
source: "file:///" + modelData.IconPath
mipmap: true
smooth: true
}*/
Column {
id: textcolumn
anchors.left: parent.left
anchors.right: parent.right
anchors.leftMargin: 16
anchors.verticalCenter: parent.verticalCenter
spacing: 2
Label {
text: modelData.InstallLocation.split('/').pop().split('\\').pop().replace(/([a-z])([A-Z])/g, '$1 $2')
font.pixelSize: 18
font.bold: true
}
Label {
id: appNameLabel
text: "AppName: " + modelData.AppName
font.pixelSize: 12
wrapMode: Text.WordWrap
width: parent.width
}
Label {
id: fullPathLabel
text: modelData.InstallLocation
font.pixelSize: 12
color: '#888888'
wrapMode: Text.WordWrap
width: parent.width
}
}
Rectangle {
anchors.bottom: parent.bottom
height: 1
width: parent.width
color: Qt.rgba(1,1,1,0.25)
}
MouseArea {
anchors.fill: parent
onClicked: function(){
confirmed(modelData)
dlg.close();
}
}
}
}
}
}

@ -1,5 +1,5 @@
/*
Copyright 2021-2023 Peter Repukat - FlatspotSoftware
Copyright 2021-2022 Peter Repukat - FlatspotSoftware
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.

@ -1,5 +1,5 @@
/*
Copyright 2021-2023 Peter Repukat - FlatspotSoftware
Copyright 2021-2022 Peter Repukat - FlatspotSoftware
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@ -81,25 +81,6 @@ Item {
config.snapshotNotify = checked
}
}
}
Row {
leftPadding: 12
Row {
spacing: 16
Label {
topPadding: 8
id: apiKeyLabel
font.bold: true
text: qsTr("SteamGridDB-API-Key")
}
FluentTextInput {
width: 128
id: apiKeyInput
placeholderText: qsTr("...")
text: config.steamgridApiKey
onTextChanged: config.steamgridApiKey = text
}
}
}
}
}
@ -118,85 +99,6 @@ Item {
+ "as well as settings applied when launching GlosSITarget without config")
}
Item {
width: 1
height: 4
}
RPane {
width: parent.width
radius: 4
Material.elevation: 32
bgOpacity: 0.97
Column {
width: parent.width
height: parent.height
spacing: 4
Label {
font.bold: true
font.pixelSize: 24
text: qsTr("Experimental 🧪")
}
Row {
Row {
CheckBox {
id: globalModeUseGamepadUI
text: qsTr("Use BPM for global-/desktop-mode")
checked: config.globalModeUseGamepadUI
onCheckedChanged: config.globalModeUseGamepadUI = checked
}
}
}
Row {
leftPadding: 12
Row {
spacing: 16
Label {
topPadding: 8
id: globalModeGameIdLabel
text: qsTr("GlobalMode GameId")
}
FluentTextInput {
width: 128
id: globalModeGameId
enabled: false
text: config.globalModeGameId
onTextChanged: config.globalModeGameId = text
}
Button {
id: globalModeGameIdButton
text: qsTr("Create global-/desktop-mode shortcut")
onClicked: {
const globalModeConf = uiModel.getDefaultConf();
globalModeConf.name = "GlosSI GlobalMode/Desktop";
globalModeConf.launch.launch = false;
uiModel.addTarget(globalModeConf);
if (uiModel.addToSteam(globalModeConf, "")) {
steamChangedDialog.open();
}
const globalModeGID = uiModel.globalModeShortcutGameId();
globalModeGameId.text = globalModeGID;
setTimeout(() => {
uiModel.saveDefaultConf(config);
done();
}, 10);
}
highlighted: true
visible: !uiModel.globalModeShortcutExists()
}
Button {
id: globalModeGameIdConfigButton
text: qsTr("Open global-/desktop-mode controller config")
onClicked: {
Qt.openUrlExternally("steam://currentcontrollerconfig/" + uiModel.globalModeShortcutAppId() + "/");
}
visible: uiModel.globalModeShortcutExists()
}
}
}
}
}
Item {
width: 1
height: 32

@ -1,5 +1,5 @@
/*
Copyright 2021-2023 Peter Repukat - FlatspotSoftware
Copyright 2021-2022 Peter Repukat - FlatspotSoftware
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@ -15,9 +15,6 @@ limitations under the License.
*/
import QtQuick 6.2
import QtQuick.Controls 6.2
import QtQuick.Controls.Material 6.2
import QtQuick.Dialogs 6.2
Dialog {
id: dlg
anchors.centerIn: parent

@ -1,5 +1,5 @@
/*
Copyright 2021-2023 Peter Repukat - FlatspotSoftware
Copyright 2021-2022 Peter Repukat - FlatspotSoftware
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@ -23,8 +23,6 @@ Pane {
property int radius: 0
property color color: control.Material.backgroundColor
property real bgOpacity: 1
property string bgImgSource: null
property real bgImgOpacity: -1
background: Rectangle {
color: parent.color
opacity: parent.bgOpacity
@ -35,14 +33,13 @@ Pane {
elevation: control.Material.elevation
}
Image {
id: bgImage
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: parent.bottom
source: bgImgSource ? bgImgSource : "qrc:/noise.png"
fillMode: bgImgSource ? Image.PreserveAspectCrop : Image.Tile
opacity: bgImgOpacity < 0 ? 0.035 : bgImgOpacity
source: "qrc:/noise.png"
fillMode: Image.Tile
opacity: 0.035
}
}
}

@ -1,5 +1,5 @@
/*
Copyright 2021-2023 Peter Repukat - FlatspotSoftware
Copyright 2021-2022 Peter Repukat - FlatspotSoftware
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@ -13,11 +13,11 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import QtQuick
import QtQuick.Layouts
import QtQuick.Controls
import QtQuick.Controls.Material
import QtQuick.Dialogs
import QtQuick 6.2
import QtQuick.Layouts 6.2
import QtQuick.Controls 6.2
import QtQuick.Controls.Material 6.2
import QtQuick.Dialogs 6.2
import Qt5Compat.GraphicalEffects
GridView {
@ -34,12 +34,14 @@ GridView {
property real margins: 16
cellWidth: 292 + 16
cellHeight: 212 + 16
cellHeight: 190 + 16
readonly property real displayedItems: Math.floor((parent.width - margins*2) / cellWidth)
width: displayedItems * cellWidth
model: uiModel.targetList;
GridView.delayRemove: true
property var manualInfo: null
// TODO: animations only properly work with abstractListModel... grrr...
addDisplaced: Transition {
NumberAnimation { properties: "x,y"; duration: 300 }
@ -65,6 +67,134 @@ GridView {
NumberAnimation { properties: "x,y"; duration: 300; easing.type: Easing.InQuad }
}
Dialog {
id: manualAddDialog
anchors.centerIn: parent
visible: false
modal: true
dim: true
parent: Overlay.overlay
Overlay.modal: Rectangle {
color: Qt.rgba(0,0,0,0.4)
opacity: backdropOpacity
Behavior on opacity {
NumberAnimation {
duration: 300
}
}
}
property real backdropOpacity: 1.0
enter: Transition {
NumberAnimation{target: madcontent; property: "y"; from: parent.height; to: 16; duration: 300; easing.type: Easing.OutQuad }
NumberAnimation{target: madbackground; property: "y"; from: parent.height; to: 0; duration: 300; easing.type: Easing.OutQuad }
NumberAnimation{target: manualAddDialog; property: "backdropOpacity"; from: 0; to: 1; duration: 300; easing.type: Easing.OutQuad }
}
exit: Transition {
NumberAnimation{target: madcontent; property: "y"; from: 16; to: parent.height; duration: 300; easing.type: Easing.InQuad }
NumberAnimation{target: madbackground; property: "y"; from: 0; to: parent.height; duration: 300; easing.type: Easing.InQuad }
NumberAnimation{target: manualAddDialog; property: "backdropOpacity"; from: 1; to: 0; duration: 300; easing.type: Easing.InQuad }
}
background: RPane {
id: madbackground
radius: 4
Material.elevation: 64
bgOpacity: 0.97
}
contentItem: Item {
id: madcontent
implicitWidth: steamscreener.width
implicitHeight: madtext.height + 16 + steamscreener.height + 16 + madrow.height
Label {
id: madtext
text: qsTr("Add \"GlosSITarget\" as game to Steam and change it's properties (in Steam) to this:")
}
Image {
anchors.top: madtext.bottom
anchors.left: madtext.left
anchors.topMargin: 16
id: steamscreener
source: "qrc:/steamscreener.png"
}
FluentTextInput {
id: madnameinput
text: manualInfo ? manualInfo.name : ""
anchors.top: steamscreener.top
anchors.left: steamscreener.left
anchors.topMargin: 72
anchors.leftMargin: 92
readOnly: true
background: Item {}
width: 550
}
FluentTextInput {
id: glossiPathInput
text: manualInfo ? manualInfo.launch : ""
anchors.top: steamscreener.top
anchors.left: steamscreener.left
anchors.topMargin: 192
anchors.leftMargin: 24
readOnly: true
background: Item {}
width: 550
}
FluentTextInput {
id: startDirInput
text: manualInfo ? manualInfo.launchDir : ""
anchors.top: steamscreener.top
anchors.left: steamscreener.left
anchors.topMargin: 266
anchors.leftMargin: 24
readOnly: true
background: Item {}
width: 550
}
FluentTextInput {
id: launchOptsInput
text: manualInfo ? manualInfo.config : ""
anchors.top: steamscreener.top
anchors.left: steamscreener.left
anchors.topMargin: 432
anchors.leftMargin: 24
readOnly: true
background: Item {}
width: 550
}
Row {
id: madrow
anchors.top: steamscreener.bottom
anchors.topMargin: 16
spacing: 16
Button {
text: qsTr("OK")
onClicked: function(){
manualAddDialog.close()
}
}
anchors.right: parent.right
}
}
}
InfoDialog {
id: writeErrorDialog
titleText: qsTr("Error")
text: qsTr("Error writing shortcuts.vdf...\nPlease make sure Steam is running")
extraButton: true
extraButtonText: qsTr("Manual instructions")
onConfirmedExtra: function(data) {
manualAddDialog.open();
}
}
delegate: RPane {
id: delegateRoot
@ -72,12 +202,10 @@ GridView {
bgOpacity: 0.3
radius: 8
width: 292
height: 212
height: 190
Material.elevation: 4
clip: true
property bool isInSteam: uiModel.isInSteam(modelData);
bgImgSource: isInSteam ? "file:///" + uiModel.getGridImagePath(modelData) : null
bgImgOpacity: isInSteam ? 0.12 : -1
Image {
anchors.top: parent.top
@ -87,10 +215,10 @@ GridView {
? modelData.icon.endsWith(".exe")
? "image://exe/" + modelData.icon
: "file:///" + modelData.icon
: 'qrc:/svg/add_photo_alternate_white_24dp.svg'
: null
width: 48
height: 48
fillMode: Image.PreserveAspectFit
visible: !!modelData.icon
}
Label {
@ -133,85 +261,89 @@ GridView {
}
}
Row {
visible: uiModel.isDebug
visible: false // TODO: dunno about this...
spacing: 4
Label {
text: qsTr("AppID: ")
text: qsTr("Is in")
font.bold: true
}
Image {
anchors.verticalCenter: parent.verticalCenter
source: "qrc:/svg/steam.svg"
width: 16
height: 16
smooth: true
mipmap: true
ColorOverlay {
anchors.fill: parent
source: parent
color: "white"
}
}
Item {
width: 4
height: 1
}
Label {
text: uiModel.getAppId(modelData)
anchors.verticalCenter: parent.verticalCenter
text: delegateRoot.isInSteam ? qsTr("Yes") : qsTr("No")
width: 292 - typeLabel.width - 72
elide: Text.ElideRight
}
}
}
Column {
anchors.left: parent.left
Button {
id: steambutton
anchors.left: parent.left
anchors.bottom: parent.bottom
spacing: 4
Button {
highlighted: true
visible: delegateRoot.isInSteam
text: qsTr("Steam controller config")
onClicked: function() {
controllerConfigDialog.confirmedExtraParam = uiModel.getAppId(modelData)
controllerConfigDialog.confirmedParam = uiModel.getAppId(modelData)
controllerConfigDialog.open();
width: 72
onClicked: function(){
if (delegateRoot.isInSteam) {
if (!uiModel.removeFromSteam(modelData.name, "")) {
writeErrorDialog.open();
return;
}
} else {
if (!uiModel.addToSteam(modelData, "")) {
manualInfo = uiModel.manualProps(modelData);
writeErrorDialog.open();
return;
}
}
if (steamShortcutsChanged == false) {
steamChangedDialog.open();
}
delegateRoot.isInSteam = uiModel.isInSteam(modelData)
steamShortcutsChanged = true
}
Button {
id: steambutton
width: 72
onClicked: function(){
if (delegateRoot.isInSteam) {
if (!uiModel.removeFromSteam(modelData.name, "")) {
writeErrorDialog.open();
return;
}
} else {
if (!uiModel.addToSteam(modelData, "")) {
manualInfo = uiModel.manualProps(modelData);
writeErrorDialog.open();
return;
}
}
if (steamShortcutsChanged == false) {
steamChangedDialog.open();
}
delegateRoot.isInSteam = uiModel.isInSteam(modelData)
steamShortcutsChanged = true
highlighted: delegateRoot.isInSteam
Material.accent: Material.color(Material.Red, Material.Shade800)
Row {
anchors.centerIn: parent
spacing: 8
Label {
anchors.verticalCenter: parent.verticalCenter
text: delegateRoot.isInSteam ? "-" : "+"
font.bold: true
font.pixelSize: 24
}
highlighted: delegateRoot.isInSteam
Material.accent: Material.color(Material.Red, Material.Shade800)
Row {
anchors.centerIn: parent
spacing: 8
Label {
anchors.verticalCenter: parent.verticalCenter
text: delegateRoot.isInSteam ? "-" : "+"
font.bold: true
font.pixelSize: 24
}
Image {
anchors.verticalCenter: parent.verticalCenter
source: "qrc:/svg/steam.svg"
width: 22
height: 22
smooth: true
mipmap: true
ColorOverlay {
anchors.fill: parent
source: parent
color: "white"
}
Image {
anchors.verticalCenter: parent.verticalCenter
source: "qrc:/svg/steam.svg"
width: 22
height: 22
smooth: true
mipmap: true
ColorOverlay {
anchors.fill: parent
source: parent
color: "white"
}
}
}
}
Row {
id: buttonrow
anchors.right: parent.right

@ -1,5 +1,5 @@
/*
Copyright 2021-2023 Peter Repukat - FlatspotSoftware
Copyright 2021-2022 Peter Repukat - FlatspotSoftware
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@ -13,12 +13,11 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import QtQuick
import QtQuick.Layouts
import QtQuick.Controls
import QtQuick.Controls.Material
import QtQuick.Dialogs
import Qt5Compat.GraphicalEffects
import QtQuick 6.2
import QtQuick.Controls 6.2
import QtQuick.Layouts 6.2
import QtQuick.Controls.Material 6.2
import QtQuick.Dialogs 6.2
Item {
@ -27,7 +26,6 @@ Item {
property alias fileDialog: fileDialog
property alias uwpSelectDialog: uwpSelectDialog
property alias egsSelectDialog: egsSelectDialog
signal cancel()
signal done(var shortcut)
@ -54,39 +52,6 @@ Item {
if (advancedTargetSettings) { // advanced settings (collapsible container)
advancedTargetSettings.shortcutInfo = shortcutInfo;
}
if (maybeIcon) {
maybeIcon.source = shortcutInfo.icon
? shortcutInfo.icon.endsWith(".exe")
? "image://exe/" + shortcutInfo.icon
: "file:///" + shortcutInfo.icon
: 'qrc:/svg/add_photo_alternate_white_24dp.svg';
}
}
Image {
id: bgImage
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
fillMode: Image.PreserveAspectCrop
source: "file:///" + uiModel.getGridImagePath(shortcutInfo)
autoTransform: true
opacity: 0
}
LinearGradient {
id: mask
anchors.fill: bgImage
gradient: Gradient {
GradientStop { position: 0.0; color: "#afFFFFFF"}
GradientStop { position: 0.7; color: "transparent" }
GradientStop { position: 1; color: "transparent" }
}
}
OpacityMask {
source: bgImage
maskSource: mask
anchors.fill: bgImage
}
Flickable {
@ -136,11 +101,7 @@ Item {
id: nameInput
placeholderText: qsTr("...")
text: shortcutInfo.name
onTextChanged: function() {
shortcutInfo.oldName = shortcutInfo.oldName || shortcutInfo.name
shortcutInfo.name = nameInput.text
shortcutInfo = shortcutInfo
}
onTextChanged: shortcutInfo.name = text
validator: RegularExpressionValidator { regularExpression: /([0-z]|\s|.)+/gm }
}
}
@ -191,28 +152,24 @@ Item {
spacing: 4
anchors.left: parent.left
anchors.right: parent.right
anchors.leftMargin: 8
anchors.rightMargin: 16
Button {
id: iconButton
Layout.preferredWidth: 56
Layout.preferredHeight: 64
anchors.leftMargin: 32
anchors.rightMargin: 32
Image {
id: maybeIcon
source: shortcutInfo.icon
? shortcutInfo.icon.endsWith(".exe")
? "image://exe/" + shortcutInfo.icon
: "file:///" + shortcutInfo.icon
: ''
Layout.preferredWidth: 48
Layout.preferredHeight: 48
visible: shortcutInfo.icon
Layout.alignment: Qt.AlignVCenter
flat: true
contentItem: Image {
id: maybeIcon
fillMode: Image.PreserveAspectFit
source: shortcutInfo.icon
? shortcutInfo.icon.endsWith(".exe")
? "image://exe/" + shortcutInfo.icon
: "file:///" + shortcutInfo.icon
: 'qrc:/svg/add_photo_alternate_white_24dp.svg'
}
onClicked: iconFileDialog.open()
}
Item {
Layout.preferredWidth: 8
Layout.preferredHeight: 8
visible: shortcutInfo.icon
}
Item {
Layout.preferredWidth: parent.width / 2
@ -233,10 +190,7 @@ Item {
placeholderText: qsTr("...")
enabled: launchApp.checked
text: shortcutInfo.launch.launchPath || ""
onTextChanged: function() {
shortcutInfo.launch.launchPath = text
shortcutInfo = shortcutInfo
}
onTextChanged: shortcutInfo.launch.launchPath = text
}
}
Button {
@ -252,13 +206,6 @@ Item {
visible: uiModel.isWindows
onClicked: uwpSelectDialog.open();
}
Button {
Layout.preferredWidth: 64
Layout.alignment: Qt.AlignBottom
text: qsTr("EGS")
visible: uiModel.isWindows
onClicked: egsSelectDialog.open();
}
Item {
height: 1
Layout.preferredWidth: 12
@ -293,6 +240,7 @@ Item {
AdvancedTargetSettings {
id: advancedTargetSettings
shortcutInfo: shortcutInfo
}
Item {
@ -335,25 +283,9 @@ Item {
pathInput.text = fileDialog.selectedFile.toString().replace("file:///", "")
if (nameInput.text == "") {
nameInput.text = pathInput.text.replace(/.*(\\|\/)/,"").replace(/\.[0-z]*$/, "")
shortcutInfo.icon = nameInput.text
}
shortcutInfo.icon = pathInput.text
launchApp.checked = true
}
shortcutInfo = shortcutInfo;
}
onRejected: {
}
}
FileDialog {
id: iconFileDialog
title: qsTr("Please choose an icon")
nameFilters: uiModel.isWindows ? ["Image/Executable (*.exe *.png *.ico *.jpg)"] : ["Image (*.png *.ico *.jpg)"]
onAccepted: {
if (iconFileDialog.selectedFile != null) {
shortcutInfo.icon = iconFileDialog.selectedFile.toString().replace("file:///", "")
shortcutInfo = shortcutInfo;
}
}
onRejected: {
@ -374,21 +306,6 @@ Item {
launchApp.checked = true
}
}
EGSSelectDialog {
id: egsSelectDialog
onConfirmed: function(modelData) {
if (nameInput.text == "") {
nameInput.text = modelData.InstallLocation.split('/').pop().split('\\').pop().replace(/([a-z])([A-Z])/g, '$1 $2')
}
pathInput.text = "com.epicgames.launcher://apps/"
+ modelData.NamespaceId
+ "%3A"
+ modelData.ItemId
+ "%3A"
+ modelData.ArtifactId + "?action=launch&silent=true"
launchApp.checked = true
}
}
InfoDialog {
id: helpInfoDialog

@ -1,191 +0,0 @@
/*
Copyright 2021-2023 Peter Repukat - FlatspotSoftware
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import QtQuick.Controls.Material
Dialog {
id: gridDialog
anchors.centerIn: parent
signal confirmed(var param)
visible: false
modal: true
dim: true
parent: Overlay.overlay
Overlay.modal: Rectangle {
color: Qt.rgba(0,0,0,0.4)
opacity: backdropOpacity
Behavior on opacity {
NumberAnimation {
duration: 300
}
}
}
property real backdropOpacity: 1.0
property bool loading: true
property bool hasTokenError: false
onOpened: function() {
loading = true;
uiModel.loadSteamGridImages();
}
onClosed: function() {
}
enter: Transition {
NumberAnimation{target: content; property: "y"; from: parent.height; to: 16; duration: 300; easing.type: Easing.OutQuad }
NumberAnimation{target: background; property: "y"; from: parent.height; to: 0; duration: 300; easing.type: Easing.OutQuad }
NumberAnimation{target: gridDialog; property: "backdropOpacity"; from: 0; to: 1; duration: 300; easing.type: Easing.OutQuad }
}
exit: Transition {
NumberAnimation{target: content; property: "y"; from: 16; to: parent.height; duration: 300; easing.type: Easing.InQuad }
NumberAnimation{target: background; property: "y"; from: 0; to: parent.height; duration: 300; easing.type: Easing.InQuad }
NumberAnimation{target: gridDialog; property: "backdropOpacity"; from: 1; to: 0; duration: 300; easing.type: Easing.InQuad }
}
background: RPane {
id: background
radius: 4
Material.elevation: 64
bgOpacity: 0.97
}
contentItem: Item {
id: content
implicitWidth: listview.width
implicitHeight: listview.height + titlelabel.height + 16 + 64
clip: true
Label {
id: titlelabel
text: qsTr("Loading Grid images...")
font.pixelSize: 24
font.bold: true
}
Item {
id: topContentContainer
anchors.top: titlelabel.bottom
anchors.topMargin: 8
anchors.horizontalCenter: parent.horizontalCenter
anchors.left: parent.left
anchors.right: parent.right
height: loading || hasTokenError ? 72 : 0
BusyIndicator {
id: busyIndicator
running: visible
anchors.horizontalCenter: parent.horizontalCenter
opacity: loading ? 1 : 0
height: loading ? 72 : 0
Behavior on opacity {
NumberAnimation {
duration: 350
easing.type: Easing.InOutQuad
}
}
visible: loading
}
Label {
id: tokenErrorInfo
anchors.horizontalCenter: parent.horizontalCenter
opacity: hasTokenError ? 1 : 0
anchors.left: parent.left
anchors.right: parent.right
wrapMode: Text.Wrap
font.bold: true
color: "yellow"
textFormat: TextEdit.RichText
onLinkActivated: Qt.openUrlExternally(link)
Behavior on opacity {
NumberAnimation {
duration: 350
easing.type: Easing.InOutQuad
}
}
visible: hasTokenError
text: qsTr("Please go to <a href=\"https://www.steamgriddb.com/profile/preferences/api\">SteamGridDB</a> and gerenate a new API token. Then paste it into the settings dialog.")
}
}
ListView {
anchors.top: topContentContainer.bottom
anchors.topMargin: 16
anchors.bottom: parent.bottom
anchors.bottomMargin: 16
id: listview
width: window.width * 0.45
height: window.height * 0.66
spacing: 0
clip: true
model: uiModel.steamgridOutput
ScrollBar.vertical: ScrollBar {
}
onCountChanged: function() {
listview.positionViewAtIndex(listview.count - 1, ListView.Visible);
hasTokenError = listview.model.some((l) => l.includes("missing or invalid"));
loading = !(listview.model.some((l) => l.includes("Press enter")) || hasTokenError);
}
Behavior on opacity {
ParallelAnimation {
NumberAnimation {
duration: 350
easing.type: Easing.InOutQuad
}
PropertyAnimation {
target: listview
property: "anchors.topMargin"
from: window.height * 0.75
to: 16
duration: 350
easing.type: Easing.InOutQuad
}
}
}
delegate: /* Item {
width: listview.width
height: outputLabel.implicitHeight */
Label {
id: outputLabel
text: modelData
}
// }
}
Button {
anchors.right: parent.right
anchors.bottom: parent.bottom
anchors.bottomMargin: 2
anchors.rightMargin: 2
text: qsTr("Ok")
onClicked: function() {
gridDialog.close();
confirmed(undefined);
}
}
}
}

@ -1,5 +1,5 @@
/*
Copyright 2021-2023 Peter Repukat - FlatspotSoftware
Copyright 2021-2022 Peter Repukat - FlatspotSoftware
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.

@ -1,5 +1,5 @@
/*
Copyright 2021-2023 Peter Repukat - FlatspotSoftware
Copyright 2021-2022 Peter Repukat - FlatspotSoftware
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@ -13,12 +13,10 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import QtQuick
import QtQuick.Layouts
import QtQuick.Controls
import QtQuick.Controls.Material
import QtQuick.Dialogs
import Qt5Compat.GraphicalEffects
import QtQuick 6.2
import QtQuick.Controls 6.2
import QtQuick.Layouts 6.2
import QtQuick.Controls.Material 6.2
Dialog {
id: dlg
@ -82,6 +80,10 @@ Dialog {
}
Button {
anchors.right: parent.right
anchors.top: listview.bottom
anchors.topMargin: 16
anchors.rightMargin: 2
text: qsTr("Ok")
onClicked: dlg.close()
}

@ -1,5 +1,5 @@
/*
Copyright 2021-2023 Peter Repukat - FlatspotSoftware
Copyright 2021-2022 Peter Repukat - FlatspotSoftware
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@ -126,6 +126,15 @@ Dialog {
visible: opacity == 0 ? false : true
}
Button {
anchors.right: parent.right
anchors.top: listview.bottom
anchors.topMargin: 16
anchors.rightMargin: 2
text: qsTr("Cancel")
onClicked: dlg.close()
}
ListView {
anchors.top: searchBar.bottom
anchors.topMargin: 16
@ -209,14 +218,5 @@ Dialog {
}
}
}
Button {
anchors.right: parent.right
anchors.bottom: parent.bottom
anchors.bottomMargin: 2
anchors.rightMargin: 2
text: qsTr("Cancel")
onClicked: dlg.close()
}
}
}

@ -1,5 +1,5 @@
/*
Copyright 2021-2023 Peter Repukat - FlatspotSoftware
Copyright 2021-2022 Peter Repukat - FlatspotSoftware
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@ -13,12 +13,9 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import QtQuick
import QtQuick.Layouts
import QtQuick.Controls
import QtQuick.Controls.Material
import QtQuick.Dialogs
import Qt5Compat.GraphicalEffects
import QtQuick 6.2
import QtQuick.Layouts 6.2
import QtQuick.Controls.Material 6.2
Window {
id: window
@ -29,7 +26,6 @@ Window {
Material.accent: Material.color(Material.Blue, Material.Shade900)
property bool itemSelected: false;
property var manualInfo: null
title: qsTr("GlosSI - Config")
@ -48,12 +44,6 @@ Window {
}
property bool steamShortcutsChanged: false
onSteamShortcutsChanged: function() {
shouldShowLoadGridImagesButton = uiModel.targetList.some((shortcut) => uiModel.isInSteam(shortcut))
}
property bool shouldShowLoadGridImagesButton: false
Component.onCompleted: function() {
if (!uiModel.foundSteam) {
@ -63,7 +53,6 @@ Window {
if (!uiModel.steamInputXboxSupportEnabled) {
steamXboxDisabledDialog.open();
}
shouldShowLoadGridImagesButton = uiModel.targetList.some((shortcut) => uiModel.isInSteam(shortcut))
}
Image {
@ -128,160 +117,14 @@ Window {
titleText: qsTr("New version available!")
text: uiModel.newVersionName + "\n\n" + qsTr("Would you like to visit the download page now?")
onConfirmed: function (callback) {
Qt.openUrlExternally(`https://glossi.flatspot.pictures/#downloads${ uiModel.newVersionName.includes('snapshot') ? '-snapshots' : '' }-${uiModel.newVersionName}`);
callback();
Qt.openUrlExternally(`https://glossi.flatspot.pictures/#downloads-${uiModel.newVersionName}`);
}
buttonText: qsTr("Yes")
extraButton: true
extraButtonText: qsTr("Remind me later")
visible: !!uiModel.newVersionName
}
Dialog {
id: manualAddDialog
anchors.centerIn: parent
visible: false
modal: true
dim: true
parent: Overlay.overlay
Overlay.modal: Rectangle {
color: Qt.rgba(0,0,0,0.4)
opacity: backdropOpacity
Behavior on opacity {
NumberAnimation {
duration: 300
}
}
}
property real backdropOpacity: 1.0
enter: Transition {
NumberAnimation{target: madcontent; property: "y"; from: parent.height; to: 16; duration: 300; easing.type: Easing.OutQuad }
NumberAnimation{target: madbackground; property: "y"; from: parent.height; to: 0; duration: 300; easing.type: Easing.OutQuad }
NumberAnimation{target: manualAddDialog; property: "backdropOpacity"; from: 0; to: 1; duration: 300; easing.type: Easing.OutQuad }
}
exit: Transition {
NumberAnimation{target: madcontent; property: "y"; from: 16; to: parent.height; duration: 300; easing.type: Easing.InQuad }
NumberAnimation{target: madbackground; property: "y"; from: 0; to: parent.height; duration: 300; easing.type: Easing.InQuad }
NumberAnimation{target: manualAddDialog; property: "backdropOpacity"; from: 1; to: 0; duration: 300; easing.type: Easing.InQuad }
}
background: RPane {
id: madbackground
radius: 4
Material.elevation: 64
bgOpacity: 0.97
}
contentItem: Item {
id: madcontent
implicitWidth: steamscreener.width
implicitHeight: madtext.height + 16 + steamscreener.height + 16 + madrow.height
Label {
id: madtext
text: qsTr("Add \"GlosSITarget\" as game to Steam and change it's properties (in Steam) to this:")
}
Image {
anchors.top: madtext.bottom
anchors.left: madtext.left
anchors.topMargin: 16
id: steamscreener
source: "qrc:/steamscreener.png"
}
FluentTextInput {
id: madnameinput
text: manualInfo ? manualInfo.name : ""
anchors.top: steamscreener.top
anchors.left: steamscreener.left
anchors.topMargin: 72
anchors.leftMargin: 92
readOnly: true
background: Item {}
width: 550
}
FluentTextInput {
id: glossiPathInput
text: manualInfo ? manualInfo.launch : ""
anchors.top: steamscreener.top
anchors.left: steamscreener.left
anchors.topMargin: 192
anchors.leftMargin: 24
readOnly: true
background: Item {}
width: 550
}
FluentTextInput {
id: startDirInput
text: manualInfo ? manualInfo.launchDir : ""
anchors.top: steamscreener.top
anchors.left: steamscreener.left
anchors.topMargin: 266
anchors.leftMargin: 24
readOnly: true
background: Item {}
width: 550
}
FluentTextInput {
id: launchOptsInput
text: manualInfo ? manualInfo.config : ""
anchors.top: steamscreener.top
anchors.left: steamscreener.left
anchors.topMargin: 432
anchors.leftMargin: 24
readOnly: true
background: Item {}
width: 550
}
Row {
id: madrow
anchors.top: steamscreener.bottom
anchors.topMargin: 16
spacing: 16
Button {
text: qsTr("OK")
onClicked: function(){
manualAddDialog.close()
}
}
anchors.right: parent.right
}
}
}
InfoDialog {
id: writeErrorDialog
titleText: qsTr("Error")
text: qsTr("Error writing shortcuts.vdf...\nPlease make sure Steam is running")
extraButton: true
extraButtonText: qsTr("Manual instructions")
onConfirmedExtra: function(data) {
manualAddDialog.open();
}
}
InfoDialog {
id: controllerConfigDialog
titleText: qsTr("Open Steam controller config")
text: qsTr("Steams controller config can only open if a controller is connected")
+ "\n"
+ qsTr("and the shortcut is visible in Steam (Steam restarted after adding).")
buttonText: qsTr("Open")
extraButton: true
extraButtonText: qsTr("Restart and open")
onConfirmedExtra: function(data) {
uiModel.restartSteam("steam://currentcontrollerconfig/" + data + "/")
}
onConfirmed: function(data) {
Qt.openUrlExternally("steam://currentcontrollerconfig/" + data + "/");
}
}
Rectangle {
id: titleBar
@ -394,64 +237,47 @@ Window {
windowContent.editedIndex = index;
}
}
Row {
Column {
spacing: 8
anchors.right: parent.right
anchors.bottom: parent.bottom
anchors.margins: 24
Column {
spacing: 8
RoundButton {
id: optionsBtn
anchors.right: parent.right
width: 64
height: 64
text: ""
contentItem: Item {
Image {
anchors.centerIn: parent
source: "qrc:/svg/settings_fill_white_24dp.svg"
width: 24
height: 24
}
}
highlighted: true
onClicked: function() {
globalConf.opacity = 1;
homeContent.opacity = 0;
}
}
RoundButton {
id: addBtn
anchors.right: parent.right
width: 64
height: 64
text: "+"
contentItem: Label {
RoundButton {
id: optionsBtn
width: 64
height: 64
text: ""
contentItem: Item {
Image {
anchors.centerIn: parent
text: addBtn.text
font.pixelSize: 32
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
elide: Text.ElideRight
source: "qrc:/svg/settings_fill_white_24dp.svg"
width: 24
height: 24
}
highlighted: true
onClicked: selectTypeDialog.open()
}
highlighted: true
onClicked: function() {
globalConf.opacity = 1;
homeContent.opacity = 0;
}
Button {
visible: shouldShowLoadGridImagesButton || steamShortcutsChanged
id: loadGridImagesBtn
text: qsTr("🖼️ Load steam grid images")
highlighted: true
onClicked: function() {
steamGridDialog.open()
}
}
RoundButton {
id: addBtn
width: 64
height: 64
text: "+"
contentItem: Label {
anchors.centerIn: parent
text: addBtn.text
font.pixelSize: 32
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
elide: Text.ElideRight
}
highlighted: true
onClicked: selectTypeDialog.open()
}
}
}
Item {
@ -492,16 +318,7 @@ Window {
if (windowContent.editedIndex < 0) {
uiModel.addTarget(shortcut)
} else {
if (uiModel.updateTarget(windowContent.editedIndex, shortcut)) {
if (uiModel.isInSteam(shortcut) && steamShortcutsChanged == false) {
steamChangedDialog.open();
}
} else {
if (uiModel.isInSteam(shortcut)) {
manualInfo = uiModel.manualProps(shortcut);
writeErrorDialog.open();
}
}
uiModel.updateTarget(windowContent.editedIndex, shortcut)
}
}
}
@ -570,19 +387,7 @@ Window {
if (param == "uwp") {
props.uwpSelectDialog.open();
}
if (param == "egs") {
props.egsSelectDialog.open();
}
}
}
SteamGridDialog {
id: steamGridDialog
onConfirmed: function() {
shortcutgrid.model = [];
shortcutgrid.model = uiModel.targetList;
}
}
}
}

@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" height="48" width="48" fill="#FFFFFF"><path d="M29.45 6v3H9v30h30V18.6h3V39q0 1.2-.9 2.1-.9.9-2.1.9H9q-1.2 0-2.1-.9Q6 40.2 6 39V9q0-1.2.9-2.1Q7.8 6 9 6ZM38 6v4.05h4.05v3H38v4.05h-3v-4.05h-4.05v-3H35V6ZM12 33.9h24l-7.2-9.6-6.35 8.35-4.7-6.2ZM9 9v30V9Z"/></svg>

Before

Width:  |  Height:  |  Size: 299 B

@ -1,37 +0,0 @@
{
"configurations": [
{
"name": "Win32",
"includePath": [
"${workspaceFolder}/**",
"${workspaceFolder}/../deps/SFML/include",
"${workspaceFolder}/../deps/WinReg",
"${workspaceFolder}/../deps/spdlog/include",
"${workspaceFolder}/../deps/ValveFileVDF",
"${workspaceFolder}/../deps/subhook",
"${workspaceFolder}/../deps/ViGEmClient/include",
"${workspaceFolder}/../deps/imgui",
"${workspaceFolder}/../deps/imgui-sfml",
"${workspaceFolder}/../deps/json/include",
"${workspaceFolder}/../deps/traypp/tray/include",
"${workspaceFolder}/../deps/cpp-httplib",
"${workspaceFolder}/../CEFInjectLib"
],
"defines": [
"_DEBUG",
"UNICODE",
"_UNICODE",
"SPDLOG_WCHAR_TO_UTF8_SUPPORT",
"SPDLOG_WCHAR_FILENAMES",
"_SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING",
"SUBHOOK_STATIC"
],
"windowsSdkVersion": "10.0.18362.0",
"compilerPath": "C:/Program Files (x86)/Microsoft Visual Studio/2019/BuildTools/VC/Tools/MSVC/14.24.28314/bin/Hostx64/x64/cl.exe",
"cStandard": "c11",
"cppStandard": "c++20",
"intelliSenseMode": "msvc-x64"
}
],
"version": 4
}

@ -5,18 +5,24 @@
"version": "0.2.0",
"configurations": [
{
"name": "Launch GlosSITarget",
"type": "cppvsdbg",
"name": "(gdb) Launch",
"type": "cppdbg",
"request": "launch",
"program": "${workspaceFolder}/../x64/Debug/GlosSITarget.exe",
"preLaunchTask": "Build GlosSITarget (Debug)",
"args": [
"-window"
],
"program": "${workspaceFolder}/build/GlosSITarget",
"preLaunchTask": "Build",
"args": [],
"stopAtEntry": false,
"cwd": "${workspaceFolder}/../x64/Debug",
"cwd": "${workspaceFolder}/build",
"environment": [],
"externalConsole": false,
"MIMode": "gdb",
"setupCommands": [
{
"description": "Enable pretty-printing for gdb",
"text": "-enable-pretty-printing",
"ignoreFailures": true
}
]
}
]
}

@ -0,0 +1,3 @@
{
"C_Cpp.default.configurationProvider": "ms-vscode.cmake-tools"
}

@ -1,43 +1,15 @@
{
"version": "2.0.0",
"tasks": [
{
"type": "shell",
"label": "Build GlosSITarget (Debug)",
"command": "msbuild.exe",
"args": [
"GlosSI.sln",
"/target:GlosSITarget",
"/p:Configuration=Debug",
"/p:Platform=x64"
],
"options": {
"cwd": "${workspaceFolder}/..",
},
"problemMatcher": ["$msCompile"],
"group": {
"kind": "build",
},
},
{
"type": "shell",
"label": "Re-Build GlosSITarget (Debug)",
"command": "msbuild.exe",
"args": [
"GlosSI.sln",
"/target:GlosSITarget:Rebuild",
"/p:Configuration=Debug",
"/p:Platform=x64"
],
"options": {
"cwd": "${workspaceFolder}/..",
},
"problemMatcher": ["$msCompile"],
"group": {
"kind": "build",
},
}
]
}
"version": "2.0.0",
"tasks": [
{
"type": "cmake",
"command": "build",
"label": "Build",
"group": {
"kind": "build",
"isDefault": true
},
"problemMatcher": []
}
]
}

@ -1,5 +1,5 @@
/*
Copyright 2021-2023 Peter Repukat - FlatspotSoftware
Copyright 2021-2022 Peter Repukat - FlatspotSoftware
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@ -23,19 +23,14 @@ limitations under the License.
#include <tlhelp32.h>
#include <Propsys.h>
#include <propkey.h>
#include <shellapi.h>
#include <winerror.h>
#pragma comment(lib, "Shell32.lib")
#endif
#include "../common/Settings.h"
#include "Settings.h"
#include <regex>
#include "HttpServer.h"
#include "Overlay.h"
#include "../common/UnhookUtil.h"
#include "../common/util.h"
#include "UnhookUtil.h"
AppLauncher::AppLauncher(
std::vector<HWND>& process_hwnds,
@ -48,50 +43,11 @@ AppLauncher::AppLauncher(
spdlog::debug("App launch requested");
}
#endif
HttpServer::AddEndpoint({
"/launched-pids",
HttpServer::Method::GET,
[this](const httplib::Request& req, httplib::Response& res) {
const nlohmann::json j = launchedPids();
res.set_content(j.dump(), "text/json");
},
{1, 2, 3},
});
HttpServer::AddEndpoint({
"/launched-pids",
HttpServer::Method::POST,
[this](const httplib::Request& req, httplib::Response& res) {
try {
const nlohmann::json postbody = nlohmann::json::parse(req.body);
addPids(postbody.get<std::vector<DWORD>>());
}
catch (std::exception& e) {
res.status = 401;
res.set_content(nlohmann::json{
{"code", 401},
{"name", "Bad Request"},
{"message", e.what()},
}
.dump(),
"text/json");
}
},
{1, 2, 3, 4},
{2, 3, 4},
});
};
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;
@ -105,38 +61,19 @@ void AppLauncher::launchApp(const std::wstring& path, const std::wstring& args)
spdlog::info("LaunchApp is Win32, launching...");
launchWin32App(path, args);
}
Overlay::AddOverlayElem([this](bool has_focus, ImGuiID dockspace_id) {
ImGui::SetNextWindowDockID(dockspace_id, ImGuiCond_FirstUseEver);
if (ImGui::Begin("Launched Processes")) {
ImGui::BeginChild("Inner##LaunchedProcs", {0.f, ImGui::GetItemRectSize().y - 64}, true);
std::ranges::for_each(pids_, [](DWORD pid) {
ImGui::Text("%s | %d", util::string::to_string(util::win::process::GetProcName(pid)).c_str(), pid);
ImGui::SameLine();
if (ImGui::Button((" Kill ##" + std::to_string(pid)).c_str())) {
util::win::process::KillProcess(pid);
}
});
ImGui::EndChild();
}
ImGui::End();
});
#endif
}
void AppLauncher::update()
{
if (process_check_clock_.getElapsedTime().asMilliseconds() > 250) {
pid_mutex_.lock();
#ifdef _WIN32
if (has_extra_launchers_ && pids_.empty()) {
findLauncherPids();
}
if (!pids_.empty() && pids_[0] > 0) {
if (Settings::launch.waitForChildProcs) {
getChildPids(pids_[0]);
}
if (!IsProcessRunning(pids_[0])) {
spdlog::info(L"Launched App \"{}\" with PID \"{}\" died", util::win::process::GetProcName(pids_[0]), pids_[0]);
spdlog::info("Launched App with PID \"{}\" died", pids_[0]);
if (Settings::launch.closeOnExit && !Settings::launch.waitForChildProcs && Settings::launch.launch) {
spdlog::info("Configured to close on exit. Shutting down...");
shutdown_();
@ -145,41 +82,22 @@ void AppLauncher::update()
}
}
if (Settings::launch.waitForChildProcs) {
std::erase_if(pids_, [](auto pid) {
if (pid == 0) {
return true;
}
const auto running = IsProcessRunning(pid);
if (!running)
spdlog::trace(L"Child process \"{}\" with PID \"{}\" died", util::win::process::GetProcName(pid), pid);
spdlog::trace("Child process with PID \"{}\" died", pid);
return !running;
});
auto filtered_pids = pids_ | std::ranges::views::filter([](DWORD pid) {
return std::ranges::find(Settings::launch.launcherProcesses, util::win::process::GetProcName(pid)) == Settings::launch.launcherProcesses.end();
});
if (has_extra_launchers_ && !filtered_pids.empty()) {
launcher_has_launched_game_ = true;
}
if (Settings::launch.closeOnExit && Settings::launch.launch) {
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_();
}
}
else {
if (pids_.empty()) {
spdlog::info("Configured to close on all children exit. Shutting down...");
shutdown_();
}
}
if (Settings::launch.closeOnExit && pids_.empty() && Settings::launch.launch) {
spdlog::info("Configured to close on all children exit. Shutting down...");
shutdown_();
}
}
getProcessHwnds();
#endif
pid_mutex_.unlock();
process_check_clock_.restart();
}
}
@ -194,41 +112,28 @@ void AppLauncher::close()
#endif
}
std::vector<DWORD> AppLauncher::launchedPids()
void AppLauncher::launchWatchdog()
{
pid_mutex_.lock();
std::vector<DWORD> res;
res.reserve(pids_.size());
if (!Settings::launch.killLauncher && Settings::launch.ignoreLauncher) {
for (const auto& pid : pids_ | std::ranges::views::filter(
[](DWORD pid) {
return std::ranges::find(
Settings::launch.launcherProcesses,
util::win::process::GetProcName(pid)) == Settings::launch.launcherProcesses.end();
})) {
res.push_back(pid);
}
}
else {
std::ranges::copy(pids_.begin(), pids_.end(),
std::back_inserter(res));
}
pid_mutex_.unlock();
return res;
}
void AppLauncher::addPids(const std::vector<DWORD>& pids)
{
pid_mutex_.lock();
for (const auto pid : pids) {
if (pid > 0 && std::ranges::find(pids_, pid) == pids_.end()) {
if (Settings::common.extendedLogging) {
spdlog::debug("Added PID {} via API", pid);
}
pids_.push_back(pid);
}
}
pid_mutex_.unlock();
// wchar_t buff[MAX_PATH];
// GetModuleFileName(GetModuleHandle(NULL), buff, MAX_PATH);
// const std::wstring glossipath(buff);
// GetWindowsDirectory(buff, MAX_PATH);
// const std::wstring winpath(buff);
// launchWin32App(
// (glossipath.substr(0, 1 + glossipath.find_last_of(L'\\')) + L"GlosSIWatchdog.exe"),
// L"",
// true);
spdlog::debug("Launching GlosSIWatchdog");
char buff[MAX_PATH];
GetModuleFileNameA(GetModuleHandle(NULL), buff, MAX_PATH);
const std::string glossipath(buff);
// hack to start a TRULY detached process...
const auto launchString = ("start /b cmd.exe /c \"" + (glossipath.substr(0, 1 + glossipath.find_last_of(L'\\')) + "GlosSIWatchdog.exe" + "\""));
system(launchString.data());
}
#ifdef _WIN32
@ -251,11 +156,10 @@ void AppLauncher::getChildPids(DWORD parent_pid)
do {
if (pe.th32ParentProcessID == parent_pid) {
if (std::ranges::find(pids_, pe.th32ProcessID) == pids_.end()) {
if (Settings::common.extendedLogging) {
spdlog::info(L"Found new child process \"{}\" with PID \"{}\"", util::win::process::GetProcName(pe.th32ProcessID), pe.th32ProcessID);
if (Settings::extendedLogging) {
spdlog::info("Found new child process with PID \"{}\"", pe.th32ProcessID);
}
pids_.push_back(pe.th32ProcessID);
getChildPids(pe.th32ProcessID);
}
}
} while (Process32Next(hp, &pe));
@ -285,11 +189,9 @@ void AppLauncher::getProcessHwnds()
IPropertyStore* propStore;
SHGetPropertyStoreForWindow(curr_wnd, IID_IPropertyStore, reinterpret_cast<void**>(&propStore));
PROPVARIANT prop;
if (propStore != nullptr) {
propStore->GetValue(PKEY_AppUserModel_ID, &prop);
if (prop.bstrVal != nullptr && std::wstring(prop.bstrVal) == launched_uwp_path_) {
process_hwnds_.push_back(curr_wnd);
}
propStore->GetValue(PKEY_AppUserModel_ID, &prop);
if (prop.bstrVal != nullptr && std::wstring(prop.bstrVal) == launched_uwp_path_) {
process_hwnds_.push_back(curr_wnd);
}
} while (curr_wnd != nullptr);
}
@ -298,16 +200,6 @@ void AppLauncher::getProcessHwnds()
#endif
#ifdef _WIN32
bool AppLauncher::findLauncherPids()
{
if (const auto pid = util::win::process::PidByName(L"EpicGamesLauncher.exe")) {
spdlog::debug("Found EGS-Launcher running");
pids_.push_back(pid);
return true;
}
return false;
}
void AppLauncher::UnPatchValveHooks()
{
// need to load addresses that way.. Otherwise we may land before some jumps...
@ -330,19 +222,10 @@ void AppLauncher::launchWin32App(const std::wstring& path, const std::wstring& a
// } else {
// launch_dir = m[0];
// }
std::wstring args_cpy(
args.empty()
? L""
: ((native_seps_path.find(L" ") != std::wstring::npos
? L"\"" + native_seps_path + L"\""
: native_seps_path) +
L" " + args));
DWORD pid;
std::wstring args_cpy(args);
spdlog::debug(L"Launching Win32App app \"{}\"; args \"{}\"", native_seps_path, args_cpy);
if (CreateProcessW(native_seps_path.data(),
args_cpy.empty() ? nullptr : args_cpy.data(),
args_cpy.data(),
nullptr,
nullptr,
watchdog ? FALSE : TRUE,
@ -351,50 +234,15 @@ void AppLauncher::launchWin32App(const std::wstring& path, const std::wstring& a
nullptr, // launch_dir.empty() ? nullptr : launch_dir.data(),
&info,
&process_info)) {
pid = process_info.dwProcessId;
}
else {
DWORD error_code = GetLastError();
if (error_code == ERROR_ELEVATION_REQUIRED) {
spdlog::info("Elevated permissions required. Trying again with elevated permissions");
SHELLEXECUTEINFOW shExecInfo = {0};
shExecInfo.cbSize = sizeof(SHELLEXECUTEINFOW);
shExecInfo.fMask = SEE_MASK_NOCLOSEPROCESS;
shExecInfo.hwnd = NULL;
shExecInfo.lpVerb = L"runas";
shExecInfo.lpFile = native_seps_path.data();
shExecInfo.lpParameters = args_cpy.empty() ? nullptr : args_cpy.data();
shExecInfo.lpDirectory = nullptr; // launch_dir.empty() ? nullptr : launch_dir.data(),
shExecInfo.nShow = SW_SHOW;
shExecInfo.hInstApp = NULL;
if (ShellExecuteExW(&shExecInfo)) {
pid = GetProcessId(shExecInfo.hProcess);
if (pid == 0u) {
spdlog::error(L"Couldn't get process id after starting program: \"{}\"; Error code {}", native_seps_path, GetLastError());
}
CloseHandle(shExecInfo.hProcess);
}
else {
spdlog::error(L"Couldn't start program with elevated permissions: \"{}\"; Error code {}", native_seps_path, GetLastError());
return;
}
}
else {
spdlog::error(L"Could't start program: \"{}\"; Error code: {}", native_seps_path, error_code);
return;
// spdlog::info(L"Started Program: \"{}\" in directory: \"{}\"", native_seps_path, launch_dir);
spdlog::info(L"Started Program: \"{}\"; PID: {}", native_seps_path, process_info.dwProcessId);
if (!watchdog) {
pids_.push_back(process_info.dwProcessId);
}
}
spdlog::info(L"Started Program: \"{}\"; PID: {}", native_seps_path, pid);
if (!watchdog) {
pid_mutex_.lock();
pids_.push_back(pid);
pid_mutex_.unlock();
else {
// spdlog::error(L"Couldn't start program: \"{}\" in directory: \"{}\"", native_seps_path, launch_dir);
spdlog::error(L"Couldn't start program: \"{}\"", native_seps_path);
}
}
@ -419,7 +267,7 @@ void AppLauncher::launchUWPApp(const LPCWSTR package_full_name, const std::wstri
if (!SUCCEEDED(result)) {
spdlog::warn("CoAllowSetForegroundWindow failed. Code: {}", result);
}
pid_mutex_.lock();
pids_.push_back(0);
// Launch the app
result = sp_app_activation_manager->ActivateApplication(
@ -434,7 +282,6 @@ void AppLauncher::launchUWPApp(const LPCWSTR package_full_name, const std::wstri
else {
spdlog::info(L"Launched UWP Package \"{}\"; PID: {}", package_full_name, pids_[0]);
}
pid_mutex_.unlock();
}
else {
spdlog::error("CoCreateInstance failed: Code {}", result);
@ -473,30 +320,13 @@ void AppLauncher::launchURL(const std::wstring& url, const std::wstring& args, c
}
CoUninitialize();
if (url.find(L"epicgames.launcher") != std::wstring::npos) {
has_extra_launchers_ = true;
}
if (execute_info.hProcess != nullptr) {
if (const auto pid = GetProcessId(execute_info.hProcess); pid > 0) {
pid_mutex_.lock();
pids_.push_back(pid);
spdlog::trace("Launched URL; PID: {}", pid);
pid_mutex_.unlock();
return;
}
}
if (has_extra_launchers_) {
spdlog::debug("Epic Games launch; Couldn't find egs launcher PID");
pid_mutex_.lock();
const auto pid = util::win::process::PidByName(L"EpicGamesLauncher.exe");
if (!findLauncherPids()) {
spdlog::debug("Did not find EGS-Launcher not running, retrying later...");
}
pid_mutex_.unlock();
return;
}
spdlog::warn("Couldn't get PID of launched URL process");
}
#endif

@ -1,5 +1,5 @@
/*
Copyright 2021-2023 Peter Repukat - FlatspotSoftware
Copyright 2021-2022 Peter Repukat - FlatspotSoftware
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@ -17,12 +17,9 @@ limitations under the License.
#ifdef _WIN32
#define NOMINMAX
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#endif
#include <functional>
#include <mutex>
#include <array>
#include <string>
#include <unordered_set>
#include <SFML/System/Clock.hpp>
@ -37,13 +34,11 @@ class AppLauncher {
void update();
void close();
std::vector<DWORD> launchedPids();
void addPids(const std::vector<DWORD>& pids);
void launchWatchdog();
private:
std::function<void()> shutdown_;
sf::Clock process_check_clock_;
std::mutex pid_mutex_;
#ifdef _WIN32
static bool IsProcessRunning(DWORD pid);
@ -51,11 +46,6 @@ class AppLauncher {
void getProcessHwnds();
std::vector<HWND>& process_hwnds_;
bool has_extra_launchers_ = false;
bool launcher_has_launched_game_ = false;
bool findLauncherPids();
std::wstring launched_uwp_path_;
static void UnPatchValveHooks();

@ -1,80 +0,0 @@
#pragma once
#include "HttpServer.h"
#include "../common/Settings.h"
#include "../common/steam_util.h"
namespace CHTE {
inline void addEndpoints()
{
HttpServer::AddEndpoint(
{"/running",
HttpServer::Method::GET,
[](const httplib::Request& req, httplib::Response& res) {
// TODO: extend this when "passive" running of global mods is implemented
res.set_content(nlohmann::json{{"state", nlohmann::json{{"running", true}}}}.dump(), "text/json");
}});
HttpServer::AddEndpoint(
{"/settings",
HttpServer::Method::GET,
[](const httplib::Request& req, httplib::Response& res) {
res.set_content(Settings::toJson().dump(), "text/json");
},
"json"});
HttpServer::AddEndpoint(
{"/steam_settings",
HttpServer::Method::GET,
[](const httplib::Request& req, httplib::Response& res) {
res.set_content(util::steam::getSteamConfig().dump(4), "text/json");
},
"json"});
HttpServer::AddEndpoint({
"/log",
HttpServer::Method::POST,
[](const httplib::Request& req, httplib::Response& res) {
struct LogEntry {
std::string level;
std::string message;
};
auto entry = LogEntry{};
try {
const nlohmann::json postbody = nlohmann::json::parse(req.body);
entry.level = postbody.at("level");
entry.message = postbody.at("message");
}
catch (std::exception& e) {
res.status = 401;
res.set_content(nlohmann::json{
{"code", 401},
{"name", "Bad Request"},
{"message", e.what()},
}
.dump(),
"text/json");
}
if (entry.level == "info") {
spdlog::info("GlosSITweaks: {}", entry.message);
}
else if (entry.level == "warn") {
spdlog::warn("GlosSITweaks: {}", entry.message);
}
else if (entry.level == "error") {
spdlog::error("GlosSITweaks: {}", entry.message);
}
else if (entry.level == "debug") {
spdlog::debug("GlosSITweaks: {}", entry.message);
}
else {
spdlog::trace("GlosSITweaks: {}", entry.message);
}
},
});
};
} // namespace CHTE

@ -1,9 +1,9 @@
#pragma once
#include <windows.h>
#include <tlhelp32.h>
#include <spdlog/spdlog.h>
#include "../common/util.h"
namespace DllInjector {
inline bool TakeDebugPrivilege()
@ -107,25 +107,4 @@ inline bool findModule(DWORD pid, std::wstring& lib_path, HMODULE& hMod)
return false;
}
inline void injectDllInto(std::filesystem::path dllPath, const std::wstring& processName)
{
if (std::filesystem::exists(dllPath)) {
const auto explorer_pid = util::win::process::PidByName(processName);
if (explorer_pid != 0) {
if (DllInjector::TakeDebugPrivilege()) {
// No need to eject, as the dll is self-ejecting.
if (DllInjector::Inject(explorer_pid, dllPath.wstring())) {
spdlog::info(L"Successfully injected {} into {}", dllPath.filename().wstring(), processName);
}
}
}
else {
spdlog::error(L"{} not found", processName); // needs loglevel WTF
}
}
else {
spdlog::error(L"{} not found", dllPath.wstring());
}
}
}; // namespace DllInjector

@ -51,7 +51,6 @@
<PlatformToolset>v143</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
<SpectreMitigation />
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
@ -81,7 +80,7 @@
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LinkIncremental>true</LinkIncremental>
<ExternalIncludePath>..\deps\SFML\include;..\deps\WinReg;..\deps\spdlog\include;..\deps\ValveFileVDF;..\deps\subhook;..\deps\ViGEmClient\include;..\deps\imgui;..\deps\imgui-sfml;..\deps\json\include;..\deps\traypp\tray\include;..\deps\cpp-httplib;$(ExternalIncludePath)</ExternalIncludePath>
<ExternalIncludePath>..\deps\SFML\include;..\deps\WinReg;..\deps\spdlog\include;..\deps\ValveFileVDF;..\deps\subhook;..\deps\ViGEmClient\include;..\deps\imgui;..\deps\imgui-sfml;..\deps\json\include;..\deps\traypp\tray\include;$(ExternalIncludePath)</ExternalIncludePath>
<LibraryPath>..\deps\SFML\out\Debug\lib\Debug;..\deps\ViGEmClient\lib\debug\x64;$(LibraryPath)</LibraryPath>
<CopyLocalProjectReference>false</CopyLocalProjectReference>
<CopyLocalDeploymentContent>true</CopyLocalDeploymentContent>
@ -89,7 +88,7 @@
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LinkIncremental>false</LinkIncremental>
<ExternalIncludePath>..\deps\SFML\include;..\deps\WinReg;..\deps\spdlog\include;..\deps\ValveFileVDF;..\deps\subhook;..\deps\ViGEmClient\include;..\deps\imgui;..\deps\imgui-sfml;..\deps\json\include;..\deps\traypp\tray\include;..\deps\cpp-httplib;$(ExternalIncludePath)</ExternalIncludePath>
<ExternalIncludePath>..\deps\SFML\include;..\deps\WinReg;..\deps\spdlog\include;..\deps\ValveFileVDF;..\deps\subhook;..\deps\ViGEmClient\include;..\deps\imgui;..\deps\imgui-sfml;..\deps\json\include;..\deps\traypp\tray\include;$(ExternalIncludePath)</ExternalIncludePath>
<LibraryPath>..\deps\SFML\out\Release\lib\RelWithDebInfo;..\deps\ViGEmClient\lib\release\x64;$(LibraryPath)</LibraryPath>
<CustomBuildBeforeTargets>ResourceCompile</CustomBuildBeforeTargets>
<CopyLocalDeploymentContent>true</CopyLocalDeploymentContent>
@ -129,9 +128,6 @@
<PreprocessorDefinitions>_DEBUG;SPDLOG_WCHAR_TO_UTF8_SUPPORT;SPDLOG_WCHAR_FILENAMES;_SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING;SUBHOOK_STATIC;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<LanguageStandard>stdcpp20</LanguageStandard>
<EnableParallelCodeGeneration>true</EnableParallelCodeGeneration>
<EnableEnhancedInstructionSet>StreamingSIMDExtensions2</EnableEnhancedInstructionSet>
<FloatingPointModel>Fast</FloatingPointModel>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
@ -155,9 +151,6 @@
<PreprocessorDefinitions>NDEBUG;SPDLOG_WCHAR_TO_UTF8_SUPPORT;SPDLOG_WCHAR_FILENAMES;_SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING;SUBHOOK_STATIC;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<LanguageStandard>stdcpp20</LanguageStandard>
<FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
<EnableEnhancedInstructionSet>StreamingSIMDExtensions2</EnableEnhancedInstructionSet>
<FloatingPointModel>Fast</FloatingPointModel>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
@ -175,8 +168,6 @@
</CustomBuildStep>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="..\common\HidHide.cpp" />
<ClCompile Include="..\common\UnhookUtil.cpp" />
<ClCompile Include="..\deps\imgui-sfml\imgui-SFML.cpp" />
<ClCompile Include="..\deps\imgui\imgui.cpp" />
<ClCompile Include="..\deps\imgui\imgui_draw.cpp" />
@ -196,23 +187,23 @@
<ClCompile Include="..\deps\traypp\tray\src\core\windows\image.cpp" />
<ClCompile Include="..\deps\traypp\tray\src\core\windows\tray.cpp" />
<ClCompile Include="AppLauncher.cpp" />
<ClCompile Include="HttpServer.cpp" />
<ClCompile Include="HidHide.cpp" />
<ClCompile Include="InputRedirector.cpp" />
<ClCompile Include="main.cpp" />
<ClCompile Include="Overlay.cpp" />
<ClCompile Include="SteamOverlayDetector.cpp" />
<ClCompile Include="SteamTarget.cpp" />
<ClCompile Include="TargetWindow.cpp" />
<ClCompile Include="UnhookUtil.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\deps\imgui-sfml\imgui-SFML.h" />
<ClInclude Include="..\deps\imgui\imgui.h" />
<ClInclude Include="..\deps\subhook\subhook.h" />
<ClInclude Include="AppLauncher.h" />
<ClInclude Include="CommonHttpEndpoints.h" />
<ClInclude Include="DllInjector.h" />
<ClInclude Include="GlosSI_logo.h" />
<ClInclude Include="HttpServer.h" />
<ClInclude Include="HidHide.h" />
<ClInclude Include="imconfig.h" />
<ClInclude Include="InputRedirector.h" />
<ClInclude Include="Overlay.h" />
@ -220,10 +211,12 @@
<ClInclude Include="ProcessPriority.h" />
<ClInclude Include="resource.h" />
<ClInclude Include="Roboto.h" />
<ClInclude Include="Settings.h" />
<ClInclude Include="SteamOverlayDetector.h" />
<ClInclude Include="SteamTarget.h" />
<ClInclude Include="steam_sf_keymap.h" />
<ClInclude Include="TargetWindow.h" />
<ClInclude Include="UnhookUtil.h" />
<ClInclude Include="UWPOverlayEnabler.h" />
</ItemGroup>
<ItemGroup>

@ -51,6 +51,9 @@
<ClCompile Include="InputRedirector.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="HidHide.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="Overlay.cpp">
<Filter>Source Files</Filter>
</ClCompile>
@ -111,13 +114,7 @@
<ClCompile Include="..\deps\traypp\tray\src\components\toggle.cpp">
<Filter>Source Files\tray</Filter>
</ClCompile>
<ClCompile Include="HttpServer.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\common\UnhookUtil.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\common\HidHide.cpp">
<ClCompile Include="UnhookUtil.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
@ -137,6 +134,9 @@
<ClInclude Include="InputRedirector.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="HidHide.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="Overlay.h">
<Filter>Header Files</Filter>
</ClInclude>
@ -158,6 +158,9 @@
<ClInclude Include="AppLauncher.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="Settings.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="resource.h">
<Filter>Header Files</Filter>
</ClInclude>
@ -176,10 +179,7 @@
<ClInclude Include="GlosSI_logo.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="HttpServer.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="CommonHttpEndpoints.h">
<ClInclude Include="UnhookUtil.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>

@ -1,5 +1,5 @@
/*
Copyright 2021-2023 Peter Repukat - FlatspotSoftware
Copyright 2021-2022 Peter Repukat - FlatspotSoftware
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@ -21,17 +21,16 @@ limitations under the License.
#include "HidHide.h"
#include <numeric>
#define SPDLOG_WCHAR_TO_UTF8_SUPPORT
#define SPDLOG_WCHAR_FILENAMES
#include <spdlog/spdlog.h>
#include <vector>
#include <initguid.h>
// Device configuration related
#include <cfgmgr32.h>
#include <initguid.h>
//
#ifndef WATCHDOG
#include "../GlosSITarget/Overlay.h"
#include "Overlay.h"
#endif
#include "Settings.h"
@ -39,13 +38,8 @@ limitations under the License.
#include <devguid.h>
#include <devpkey.h>
#include <regex>
#include <cguid.h>
#include <atlbase.h>
#include "../common/UnhookUtil.h"
#pragma comment(lib, "Setupapi.lib")
#include "UnhookUtil.h"
// {D61CA365-5AF4-4486-998B-9DB4734C6CA3}add the XUSB class GUID as it is missing in the public interfaces
DEFINE_GUID(GUID_DEVCLASS_XUSBCLASS, 0xD61CA365, 0x5AF4, 0x4486, 0x99, 0x8B, 0x9D, 0xB4, 0x73, 0x4C, 0x6C, 0xA3);
@ -54,7 +48,7 @@ DEFINE_GUID(GUID_DEVINTERFACE_XUSB, 0xEC87F1E3, 0xC13B, 0x4100, 0xB5, 0xF7, 0x8B
// {00000000-0000-0000-FFFF-FFFFFFFFFFFF} the system container id
DEFINE_GUID(GUID_CONTAINER_ID_SYSTEM, 0x00000000, 0x0000, 0x0000, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF);
HidHide::HidHide() {};
HidHide::HidHide(){};
void HidHide::openCtrlDevice()
{
@ -87,7 +81,7 @@ void HidHide::hideDevices(const std::filesystem::path& steam_path)
spdlog::info("Hiding devices is disabled; Not un-patching valve hooks, not looking for HidHide");
return;
}
spdlog::debug("Setting up device hiding...");
UnPatchValveHooks();
@ -114,34 +108,34 @@ void HidHide::hideDevices(const std::filesystem::path& steam_path)
for (const auto& exe : whitelist_executeables_) {
auto path = std::regex_replace(steam_path_string, std::wregex(L"(.:)(\\/|\\\\)"), dos_device + L"\\");
path = std::regex_replace(path, std::wregex(L"\\/"), L"\\") + L"\\" + std::wstring{ exe };
path = std::regex_replace(path, std::wregex(L"\\/"), L"\\") + L"\\" + std::wstring{exe};
if (std::ranges::none_of(whitelist, [&path](auto ep) { // make copy!
auto p = path; // non-const(!) copy of path
std::ranges::transform(path, p.begin(), tolower);
std::ranges::transform(ep, ep.begin(), tolower);
return p == ep;
auto p = path; // non-const(!) copy of path
std::ranges::transform(path, p.begin(), tolower);
std::ranges::transform(ep, ep.begin(), tolower);
return p == ep;
})) {
whitelist.push_back(path);
}
}
if (Settings::common.extendedLogging) {
if (Settings::extendedLogging) {
std::ranges::for_each(whitelist, [](const auto& exe) {
spdlog::trace(L"Whitelisted executable: {}", exe);
});
});
}
setAppWhiteList(whitelist);
avail_devices_ = GetHidDeviceList();
if (Settings::common.extendedLogging) {
if (Settings::extendedLogging) {
std::ranges::for_each(avail_devices_, [](const auto& dev) {
spdlog::trace(L"AvailDevice device: {}", dev.name);
});
});
}
blacklisted_devices_ = getBlackListDevices();
for (const auto& dev : avail_devices_) {
if (std::ranges::none_of(blacklisted_devices_, [&dev](const auto& blackdev) {
return blackdev == dev.device_instance_path || blackdev == dev.base_container_device_instance_path;
return blackdev == dev.device_instance_path || blackdev == dev.base_container_device_instance_path;
})) {
// Valve emulated gamepad PID/VID; mirrord by ViGEm
if (!(dev.vendor_id == 0x28de && (dev.product_id == 0x11FF || dev.product_id == 0x028E))) {
@ -159,10 +153,10 @@ void HidHide::hideDevices(const std::filesystem::path& steam_path)
setBlacklistDevices(blacklisted_devices_);
setActive(true);
spdlog::info("Hid Gaming Devices; Enabling Overlay element...");
if (Settings::common.extendedLogging) {
if (Settings::extendedLogging) {
std::ranges::for_each(blacklisted_devices_, [](const auto& dev) {
spdlog::trace(L"Blacklisted device: {}", dev);
});
});
}
}
closeCtrlDevice();
@ -201,81 +195,80 @@ void HidHide::enableOverlayElement()
{
Overlay::AddOverlayElem([this](bool window_has_focus, ImGuiID dockspace_id) {
ImGui::SetNextWindowDockID(dockspace_id, ImGuiCond_FirstUseEver);
if (ImGui::Begin("Hidden Devices")) {
if (device_hiding_setup_) {
if (window_has_focus && (overlay_elem_clock_.getElapsedTime().asSeconds() > OVERLAY_ELEM_REFRESH_INTERVAL_S_)) {
// UnPatchValveHooks();
openCtrlDevice();
bool hidehide_state_store = hidhide_active_;
if (Settings::common.extendedLogging) {
spdlog::debug("Refreshing HID devices");
}
if (hidhide_active_) {
setActive(false);
}
avail_devices_ = GetHidDeviceList();
if (Settings::common.extendedLogging) {
std::ranges::for_each(avail_devices_, [](const auto& dev) {
spdlog::trace(L"AvailDevice device: {}", dev.name);
if (ImGui::Begin("Hidden Devices")) {
if (device_hiding_setup_) {
if (window_has_focus && (overlay_elem_clock_.getElapsedTime().asSeconds() > OVERLAY_ELEM_REFRESH_INTERVAL_S_)) {
// UnPatchValveHooks();
openCtrlDevice();
bool hidehide_state_store = hidhide_active_;
if (Settings::extendedLogging) {
spdlog::debug("Refreshing HID devices");
}
if (hidhide_active_) {
setActive(false);
}
avail_devices_ = GetHidDeviceList();
if (Settings::extendedLogging) {
std::ranges::for_each(avail_devices_, [](const auto& dev) {
spdlog::trace(L"AvailDevice device: {}", dev.name);
});
}
blacklisted_devices_ = getBlackListDevices();
if (hidehide_state_store && Settings::devices.hideDevices) {
setActive(true);
}
closeCtrlDevice();
overlay_elem_clock_.restart();
}
blacklisted_devices_ = getBlackListDevices();
if (hidehide_state_store && Settings::devices.hideDevices) {
setActive(true);
}
closeCtrlDevice();
overlay_elem_clock_.restart();
}
ImGui::BeginChild("Inner", { 0.f, ImGui::GetItemRectSize().y - 64 }, true);
std::ranges::for_each(avail_devices_, [this](const auto& device) {
std::string label = (std::string(device.name.begin(), std::ranges::find(device.name, L'\0')) + "##" + std::string(device.device_instance_path.begin(), device.device_instance_path.end()));
const auto findDeviceFn = [&device](const auto& blackdev) {
return device.device_instance_path == blackdev || device.base_container_device_instance_path == blackdev;
};
bool hidden = std::ranges::find_if(blacklisted_devices_, findDeviceFn) != blacklisted_devices_.end();
if (ImGui::Checkbox(label.data(), &hidden)) {
openCtrlDevice();
if (hidden) {
if (std::ranges::none_of(blacklisted_devices_, findDeviceFn)) {
if (!device.device_instance_path.empty()) {
blacklisted_devices_.push_back(device.device_instance_path);
ImGui::BeginChild("Inner", {0.f, ImGui::GetItemRectSize().y - 64}, true);
std::ranges::for_each(avail_devices_, [this](const auto& device) {
std::string label = (std::string(device.name.begin(), std::ranges::find(device.name, L'\0')) + "##" + std::string(device.device_instance_path.begin(), device.device_instance_path.end()));
const auto findDeviceFn = [&device](const auto& blackdev) {
return device.device_instance_path == blackdev || device.base_container_device_instance_path == blackdev;
};
bool hidden = std::ranges::find_if(blacklisted_devices_, findDeviceFn) != blacklisted_devices_.end();
if (ImGui::Checkbox(label.data(), &hidden)) {
openCtrlDevice();
if (hidden) {
if (std::ranges::none_of(blacklisted_devices_, findDeviceFn)) {
if (!device.device_instance_path.empty()) {
blacklisted_devices_.push_back(device.device_instance_path);
}
if (!device.device_instance_path.empty()) {
blacklisted_devices_.push_back(device.base_container_device_instance_path);
}
}
}
else {
blacklisted_devices_.erase(std::ranges::remove_if(blacklisted_devices_, findDeviceFn).begin(),
blacklisted_devices_.end());
}
if (!device.device_instance_path.empty()) {
blacklisted_devices_.push_back(device.base_container_device_instance_path);
setBlacklistDevices(blacklisted_devices_);
if (Settings::extendedLogging) {
std::ranges::for_each(blacklisted_devices_, [](const auto& dev) {
spdlog::trace(L"Blacklisted device: {}", dev);
});
}
closeCtrlDevice();
}
}
else {
blacklisted_devices_.erase(std::ranges::remove_if(blacklisted_devices_, findDeviceFn).begin(),
blacklisted_devices_.end());
}
setBlacklistDevices(blacklisted_devices_);
if (Settings::common.extendedLogging) {
std::ranges::for_each(blacklisted_devices_, [](const auto& dev) {
spdlog::trace(L"Blacklisted device: {}", dev);
});
}
closeCtrlDevice();
}
});
ImGui::EndChild();
}
else {
ImGui::Text("Enable \"Hide Devices\" to see a list of gaming-devices");
}
if (ImGui::Checkbox("Hide devices", &Settings::devices.hideDevices)) {
if (!device_hiding_setup_) {
hideDevices(steam_path_);
ImGui::EndChild();
} else {
ImGui::Text("Enable \"Hide Devices\" to see a list of gaming-devices");
}
if (hidhide_active_ != Settings::devices.hideDevices) {
openCtrlDevice();
setActive(Settings::devices.hideDevices);
closeCtrlDevice();
if (ImGui::Checkbox("Hide devices", &Settings::devices.hideDevices)) {
if (!device_hiding_setup_) {
hideDevices(steam_path_);
}
if (hidhide_active_ != Settings::devices.hideDevices) {
openCtrlDevice();
setActive(Settings::devices.hideDevices);
closeCtrlDevice();
}
}
}
}
ImGui::End();
});
ImGui::End();
});
}
#endif
@ -283,7 +276,7 @@ std::wstring HidHide::DosDeviceForVolume(const std::wstring& volume)
{
std::vector<WCHAR> buffer(UNICODE_STRING_MAX_CHARS);
QueryDosDeviceW(volume.c_str(), buffer.data(), static_cast<DWORD>(buffer.size()));
return { buffer.data() };
return {buffer.data()};
}
std::vector<std::wstring> HidHide::getAppWhiteList() const
@ -294,7 +287,7 @@ std::vector<std::wstring> HidHide::getAppWhiteList() const
}
std::vector<WCHAR> buffer(bytes_needed);
if (!DeviceIoControl(
hidhide_handle, static_cast<DWORD>(IOCTL_TYPE::GET_WHITELIST), nullptr, 0, buffer.data(), static_cast<DWORD>(buffer.size() * sizeof(WCHAR)), &bytes_needed, nullptr)) {
hidhide_handle, static_cast<DWORD>(IOCTL_TYPE::GET_WHITELIST), nullptr, 0, buffer.data(), static_cast<DWORD>(buffer.size() * sizeof(WCHAR)), &bytes_needed, nullptr)) {
spdlog::error("Couldn't retrieve HidHide Whitelist");
return std::vector<std::wstring>{};
}
@ -309,7 +302,7 @@ std::vector<std::wstring> HidHide::getBlackListDevices() const
}
std::vector<WCHAR> buffer(bytes_needed);
if (!DeviceIoControl(
hidhide_handle, static_cast<DWORD>(IOCTL_TYPE::GET_BLACKLIST), nullptr, 0, buffer.data(), static_cast<DWORD>(buffer.size() * sizeof(WCHAR)), &bytes_needed, nullptr)) {
hidhide_handle, static_cast<DWORD>(IOCTL_TYPE::GET_BLACKLIST), nullptr, 0, buffer.data(), static_cast<DWORD>(buffer.size() * sizeof(WCHAR)), &bytes_needed, nullptr)) {
spdlog::error("Couldn't retrieve HidHide Blacklist");
return std::vector<std::wstring>{};
}
@ -321,7 +314,7 @@ bool HidHide::getActive()
DWORD bytes_needed;
BOOLEAN res;
if (!DeviceIoControl(
hidhide_handle, static_cast<DWORD>(IOCTL_TYPE::GET_ACTIVE), nullptr, 0, &res, sizeof(BOOLEAN), &bytes_needed, nullptr)) {
hidhide_handle, static_cast<DWORD>(IOCTL_TYPE::GET_ACTIVE), nullptr, 0, &res, sizeof(BOOLEAN), &bytes_needed, nullptr)) {
spdlog::error("Couldn't retrieve HidHide State");
return false;
}
@ -334,7 +327,7 @@ void HidHide::setAppWhiteList(const std::vector<std::wstring>& whitelist) const
DWORD bytes_needed;
auto buffer = StringListToMultiString(whitelist);
if (!DeviceIoControl(
hidhide_handle, static_cast<DWORD>(IOCTL_TYPE::SET_WHITELIST), buffer.data(), static_cast<DWORD>(buffer.size() * sizeof(WCHAR)), nullptr, 0, &bytes_needed, nullptr)) {
hidhide_handle, static_cast<DWORD>(IOCTL_TYPE::SET_WHITELIST), buffer.data(), static_cast<DWORD>(buffer.size() * sizeof(WCHAR)), nullptr, 0, &bytes_needed, nullptr)) {
spdlog::error("Couldn't set HidHide WhiteList");
}
}
@ -344,7 +337,7 @@ void HidHide::setBlacklistDevices(const std::vector<std::wstring>& blacklist) co
DWORD bytes_needed;
auto buffer = StringListToMultiString(blacklist);
if (!DeviceIoControl(
hidhide_handle, static_cast<DWORD>(IOCTL_TYPE::SET_BLACKLIST), buffer.data(), static_cast<DWORD>(buffer.size() * sizeof(WCHAR)), nullptr, 0, &bytes_needed, nullptr)) {
hidhide_handle, static_cast<DWORD>(IOCTL_TYPE::SET_BLACKLIST), buffer.data(), static_cast<DWORD>(buffer.size() * sizeof(WCHAR)), nullptr, 0, &bytes_needed, nullptr)) {
spdlog::error("Couldn't set HidHide BlackList");
}
}
@ -353,12 +346,12 @@ void HidHide::setActive(bool active)
{
DWORD bytes_needed;
if (!DeviceIoControl(
hidhide_handle, static_cast<DWORD>(IOCTL_TYPE::SET_ACTIVE), &active, sizeof(BOOLEAN), nullptr, 0, &bytes_needed, nullptr)) {
hidhide_handle, static_cast<DWORD>(IOCTL_TYPE::SET_ACTIVE), &active, sizeof(BOOLEAN), nullptr, 0, &bytes_needed, nullptr)) {
spdlog::error("Couldn't set HidHide State");
return;
}
hidhide_active_ = active;
if (Settings::common.extendedLogging) {
if (Settings::extendedLogging) {
spdlog::debug("HidHide State set to {}", active);
}
}
@ -397,9 +390,9 @@ std::vector<WCHAR> HidHide::StringListToMultiString(const std::vector<std::wstri
{
auto res = std::accumulate(stringlist.begin(), stringlist.end(), std::vector<WCHAR>{}, [](auto acc, const auto& curr) {
acc.insert(acc.end(), curr.begin(), curr.end());
acc.push_back(L'\0');
return acc;
});
acc.push_back(L'\0');
return acc;
});
res.push_back(L'\0');
return res;
}
@ -429,7 +422,7 @@ std::vector<HidHide::SmallHidInfo> HidHide::GetHidDeviceList()
std::ranges::remove_if(
device_instance_paths,
[](const auto& dev) { return !DevicePresent(dev); })
.begin(),
.begin(),
device_instance_paths.end());
GUID hid_device_interface_guid{};
@ -446,7 +439,7 @@ std::vector<HidHide::SmallHidInfo> HidHide::GetHidDeviceList()
std::ranges::remove_if(
res,
[](const auto& dev) { return !dev.gaming_device; })
.begin(),
.begin(),
res.end());
return res;
@ -507,8 +500,8 @@ HidHide::SmallHidInfo HidHide::GetDeviceInfo(const DeviceInstancePath& instance_
std::wstring buffer;
buffer.resize(127 * sizeof WCHAR);
res.name = (HidD_GetProductString(device_object.get(), buffer.data(), static_cast<ULONG>(sizeof(WCHAR) * buffer.size()))
? buffer
: L"");
? buffer
: L"");
for (size_t i = 0; i < res.name.size(); ++i) {
if (res.name[i] == L'\0') {
res.name.resize(i + 1);
@ -569,13 +562,13 @@ std::filesystem::path HidHide::SymbolicLink(GUID const& interface_guid, DeviceIn
std::vector<BYTE> buffer(needed);
// Acquire the detailed data containing the symbolic link (aka. device path)
auto& [cbSize, DevicePath] {*reinterpret_cast<PSP_DEVICE_INTERFACE_DETAIL_DATA_W>(buffer.data())};
auto& [cbSize, DevicePath]{*reinterpret_cast<PSP_DEVICE_INTERFACE_DETAIL_DATA_W>(buffer.data())};
cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_W);
if (!SetupDiGetDeviceInterfaceDetailW(handle.get(), &device_interface_data, reinterpret_cast<PSP_DEVICE_INTERFACE_DETAIL_DATA_W>(buffer.data()), static_cast<DWORD>(buffer.size()), nullptr, nullptr)) {
spdlog::error(L"Couldn't get Device interface details; device: {}", instance_path);
return {};
}
return { std::wstring(DevicePath) };
return {std::wstring(DevicePath)};
}
HidHide::DeviceInstancePath HidHide::BaseContainerDeviceInstancePath(DeviceInstancePath const& device_instance_path)
@ -583,7 +576,7 @@ HidHide::DeviceInstancePath HidHide::BaseContainerDeviceInstancePath(DeviceInsta
const GUID base_container_id(BaseContainerId(device_instance_path));
if ((GUID_NULL == base_container_id) || (GUID_CONTAINER_ID_SYSTEM == base_container_id))
return (std::wstring{});
for (auto it{ device_instance_path };;) {
for (auto it{device_instance_path};;) {
if (const auto device_instance_path_parent = DeviceInstancePathParent(it); (base_container_id == BaseContainerId(device_instance_path_parent)))
it = device_instance_path_parent;
else
@ -600,7 +593,7 @@ GUID HidHide::BaseContainerId(DeviceInstancePath const& device_instance_path)
DEVINST devInst{};
DEVPROPTYPE devPropType{};
GUID buffer{};
ULONG needed{ sizeof(buffer) };
ULONG needed{sizeof(buffer)};
if (const auto result = CM_Locate_DevNodeW(&devInst, const_cast<DEVINSTID_W>(device_instance_path.c_str()), CM_LOCATE_DEVNODE_PHANTOM); (CR_SUCCESS != result)) {
spdlog::error(L"Couldn't locate device DevNode; Device {}; Code: {}", device_instance_path, result);
return {};
@ -627,7 +620,7 @@ HidHide::DeviceInstancePath HidHide::DeviceInstancePathParent(DeviceInstancePath
DEVINST dev_inst_parent{};
std::wstring res;
res.resize(UNICODE_STRING_MAX_CHARS);
ULONG needed{ static_cast<ULONG>(res.size()) };
ULONG needed{static_cast<ULONG>(res.size())};
if (const auto result = CM_Locate_DevNodeW(&dev_inst, const_cast<DEVINSTID_W>(device_instance_path.c_str()), CM_LOCATE_DEVNODE_PHANTOM); (CR_SUCCESS != result)) {
spdlog::error(L"Couldn't locate device DevNode; Device {}; Code: {}", device_instance_path, result);
return {};

@ -1,5 +1,5 @@
/*
Copyright 2021-2023 Peter Repukat - FlatspotSoftware
Copyright 2021-2022 Peter Repukat - FlatspotSoftware
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@ -15,10 +15,7 @@ limitations under the License.
*/
#pragma once
#define NOMINMAX
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#include <ntddscsi.h>
#include <winioctl.h>
#include <hidsdi.h>
@ -28,13 +25,12 @@ limitations under the License.
#include <map>
#include <string>
#include <vector>
#ifndef WATCHDOG
#include <SFML/System/Clock.hpp>
#endif
class HidHide {
private:
private:
using DeviceInstancePath = std::wstring;
using SetupDiDestroyDeviceInfoListPtr = std::unique_ptr<std::remove_pointer_t<HDEVINFO>, decltype(&SetupDiDestroyDeviceInfoList)>;
using CloseHandlePtr = std::unique_ptr<std::remove_pointer_t<HANDLE>, decltype(&CloseHandle)>;
@ -62,7 +58,7 @@ private:
bool gaming_device = false;
};
public:
public:
HidHide();
void openCtrlDevice();
@ -72,7 +68,7 @@ public:
void disableHidHide();
// TODO: MAYBE: restore hidhide state/lists when app closes. not only disable device_hiding
private:
private:
HANDLE hidhide_handle = nullptr;
std::filesystem::path steam_path_;
@ -89,12 +85,12 @@ private:
std::vector<std::wstring> blacklisted_devices_;
std::vector<SmallHidInfo> avail_devices_;
bool hidhide_active_ = false;
static constexpr int OVERLAY_ELEM_REFRESH_INTERVAL_S_ = 5;
static constexpr int OVERLAY_ELEM_REFRESH_INTERVAL_S_ = 5;
static inline constexpr std::array<std::wstring_view, 3> whitelist_executeables_{
L"GameOverlayUI.exe",
L"steam.exe",
L"streaming_client.exe" };
L"streaming_client.exe"};
static [[nodiscard]] std::wstring DosDeviceForVolume(const std::wstring& volume);

@ -1,144 +0,0 @@
/*
Copyright 2021-2023 Peter Repukat - FlatspotSoftware
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#include "HttpServer.h"
#include <spdlog/spdlog.h>
#include <utility>
#include <algorithm>
HttpServer::HttpServer(std::function<void()> close) : close_(std::move(close))
{
}
std::string HttpServer::ToString(Method m)
{
switch (m) {
case POST:
return "POST";
case PATCH:
return "PATCH";
case PUT:
return "PUT";
default:
return "GET";
}
}
void HttpServer::AddEndpoint(const Endpoint&& e)
{
endpoints_.push_back(e);
}
void HttpServer::run()
{
auto setCorsHeader = [](httplib::Response& res) {
res.set_header("Access-Control-Allow-Origin", "*");
};
server_.Get("/", [this, &setCorsHeader](const httplib::Request& req, httplib::Response& res) {
setCorsHeader(res);
auto content_json = nlohmann::json{
{"endpoints", nlohmann::json::array()}};
for (const auto& e : endpoints_) {
content_json["endpoints"].push_back(
nlohmann::json{
{"path", e.path},
{"method", ToString(e.method)},
{"response", e.response_hint},
{"payload", e.payload_hint},
});
}
content_json["endpoints"].push_back(
nlohmann::json{
{"path", "/quit"},
{"method", "POST"}
});
res.set_content(content_json.dump(4),
"text/json");
});
for (const auto& e : endpoints_) {
const auto fn = ([this, &e]() -> httplib::Server& (httplib::Server::*)(const std::string&, httplib::Server::Handler) {
switch (e.method) {
case POST:
return &httplib::Server::Post;
case PUT:
return &httplib::Server::Put;
case PATCH:
return &httplib::Server::Patch;
default:
return &httplib::Server::Get;
}
})();
(server_.*fn)(e.path, [this, &e, &setCorsHeader](const httplib::Request& req, httplib::Response& res) {
setCorsHeader(res);
res.status = 0;
res.content_length_ = 0;
try {
e.handler(req, res);
}
catch (std::exception& err) {
spdlog::error("Exception in http handler: {}", err.what());
res.status = res.status == 0 ? 500 : res.status;
if (res.content_length_ == 0) {
res.set_content(nlohmann::json{
{"code", res.status},
{"name", "HandlerError"},
{"message", err.what()},
}
.dump(),
"text/json");
}
}
catch (...) {
res.status = 500;
res.set_content(nlohmann::json{
{"code", res.status},
{"name", "Internal Server Error"},
{"message", "Unknown Error"},
}
.dump(),
"text/json");
}
});
}
server_.Post("/quit", [this, &setCorsHeader](const httplib::Request& req, httplib::Response& res) {
setCorsHeader(res);
close_();
});
server_thread_ = std::thread([this]() {
if (!server_.listen("0.0.0.0", port_)) {
spdlog::error("Couldn't start http-server");
return;
}
spdlog::debug("Started http-server on port {}", static_cast<int>(port_));
});
}
void HttpServer::stop()
{
server_.stop();
if (server_thread_.joinable())
server_thread_.join();
}

@ -1,62 +0,0 @@
/*
Copyright 2021-2023 Peter Repukat - FlatspotSoftware
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#pragma once
#include <thread>
#include <httplib.h>
#include <nlohmann/json.hpp>
class AppLauncher;
class HttpServer {
public:
explicit HttpServer(std::function<void()> close);
// C++ enums suck.
enum Method {
GET,
POST,
PUT,
PATCH,
};
// but im not in the mood of adding yet another dependency for just that shit here.
static std::string ToString(Method m);
struct Endpoint {
std::string path;
Method method;
std::function<void(const httplib::Request& req, httplib::Response& res)> handler;
nlohmann::json response_hint = nullptr;
nlohmann::json payload_hint = nullptr;
};
static void AddEndpoint(const Endpoint&& e);
void run();
void stop();
private:
httplib::Server server_;
std::thread server_thread_;
uint16_t port_ = 8756;
std::function<void()> close_;
static inline std::vector<Endpoint> endpoints_;
};

@ -1,5 +1,5 @@
/*
Copyright 2021-2023 Peter Repukat - FlatspotSoftware
Copyright 2021-2022 Peter Repukat - FlatspotSoftware
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@ -20,7 +20,7 @@ limitations under the License.
#include <spdlog/spdlog.h>
#include "Overlay.h"
#include "..\common\Settings.h"
#include "Settings.h"
InputRedirector::InputRedirector()
{
@ -49,22 +49,6 @@ InputRedirector::~InputRedirector()
void InputRedirector::run()
{
run_ = vigem_connected_;
max_controllers_ = Settings::controller.maxControllers;
if (max_controllers_ < 0) {
for (int i = 0; i < XUSER_MAX_COUNT; i++) {
XINPUT_STATE state{};
if (XInputGetState(i, &state) == ERROR_SUCCESS) {
max_controllers_ = i + 1;
}
}
if (max_controllers_ < 0) {
max_controllers_ = 1;
spdlog::error("Failed to auto detect controller count. Defaulting to 1");
}
else {
spdlog::info("Auto detected {} controllers", max_controllers_);
}
}
controller_thread_ = std::thread(&InputRedirector::runLoop, this);
#ifdef _WIN32
Overlay::AddOverlayElem([this](bool window_has_focus, ImGuiID dockspace_id) {
@ -74,17 +58,13 @@ void InputRedirector::run()
ImGui::Text("Max. controller count");
ImGui::SameLine();
ImGui::InputInt("##Max. controller count", &countcopy, 1, 1);
ImGui::Text("-1 = Auto-detection (auto-detection only works on launch");
if (countcopy > XUSER_MAX_COUNT) {
countcopy = XUSER_MAX_COUNT;
}
if (countcopy < -1) {
countcopy = -1;
if (countcopy < 0) {
countcopy = 0;
}
Settings::controller.maxControllers = countcopy;
if (Settings::controller.maxControllers > -1) {
max_controllers_ = countcopy;
}
if (ImGui::Checkbox("Emulate DS4 (instead of Xbox360 controller)", &Settings::controller.emulateDS4)) {
controller_settings_changed_ = true;
@ -151,12 +131,12 @@ void InputRedirector::runLoop()
unplugVigemPad(i);
}
}
if (max_controllers_ < XUSER_MAX_COUNT) {
for (int i = max_controllers_; i < XUSER_MAX_COUNT; i++) {
if (Settings::controller.maxControllers < XUSER_MAX_COUNT) {
for (int i = Settings::controller.maxControllers; i < XUSER_MAX_COUNT; i++) {
unplugVigemPad(i);
}
}
for (int i = 0; i < XUSER_MAX_COUNT && i < max_controllers_; i++) {
for (int i = 0; i < XUSER_MAX_COUNT && i < Settings::controller.maxControllers; i++) {
XINPUT_STATE state{};
if (XInputGetState(i, &state) == ERROR_SUCCESS) {
if (vt_pad_[i] != nullptr) {
@ -236,10 +216,6 @@ void InputRedirector::runLoop()
vigem_target_get_pid(vt_pad_[i]));
if (Settings::controller.emulateDS4) {
// TODO: make sense of DS4_OUTPUT_BUFFER
// there is no doc? Ask @Nef about this...
// ReSharper disable once CppDeprecatedEntity
#pragma warning(disable : 4996)
const auto callback_register_res = vigem_target_ds4_register_notification(
driver_,
vt_pad_[i],
@ -266,7 +242,7 @@ void InputRedirector::runLoop()
unplugVigemPad(i);
}
}
Sleep(static_cast<int>(1000.f / Settings::controller.updateRate));
sf::sleep(sf::milliseconds(1));
#endif
}

@ -1,5 +1,5 @@
/*
Copyright 2021-2023 Peter Repukat - FlatspotSoftware
Copyright 2021-2022 Peter Repukat - FlatspotSoftware
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@ -17,7 +17,6 @@ limitations under the License.
#include <thread>
#ifdef _WIN32
#define NOMINMAX
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#include <Xinput.h>
#include <ViGEm/Client.h>
@ -35,7 +34,6 @@ class InputRedirector {
private:
void runLoop();
int max_controllers_ = -1;
static constexpr int start_delay_ms_ = 2000;
bool run_ = false;
int overlay_elem_id_ = -1;

@ -1,5 +1,5 @@
/*
Copyright 2021-2023 Peter Repukat - FlatspotSoftware
Copyright 2021-2022 Peter Repukat - FlatspotSoftware
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@ -17,11 +17,12 @@ limitations under the License.
#include <filesystem>
#include <utility>
#include <locale>
#include <codecvt>
#include <regex>
#include <shlobj_core.h>
#include "Roboto.h"
#include "..\common\Settings.h"
#include "Settings.h"
#include "GlosSI_logo.h"
#include "../version.hpp"
@ -50,11 +51,19 @@ Overlay::Overlay(
ImGui::SFML::UpdateFontTexture();
#ifdef _WIN32
auto config_path = util::path::getDataDirPath();
auto config_path = std::filesystem::temp_directory_path()
.parent_path()
.parent_path()
.parent_path();
config_path /= "Roaming";
config_path /= "GlosSI";
if (!std::filesystem::exists(config_path))
std::filesystem::create_directories(config_path);
config_path /= "imgui.ini";
// This assumes that char is utf8 and wchar_t is utf16, which is guaranteed on Windows.
config_file_name_ = util::string::to_string(config_path.wstring());
config_file_name_ = std::wstring_convert<std::codecvt_utf8<wchar_t>>().to_bytes(config_path.wstring());
io.IniFilename = config_file_name_.data();
#endif
@ -163,20 +172,11 @@ void Overlay::update()
const auto remain_millis = SPLASH_DURATION_S_ * 1000 - millis;
if (remain_millis <= fade_millis) {
showSplash(static_cast<float>(remain_millis) / static_cast<float>(fade_millis) * 128.f);
}
else {
} else {
showSplash(128);
}
}
if (Settings::window.disableGlosSIOverlay) {
std::ranges::for_each(FORCED_OVERLAY_ELEMS_, [this](const auto& elem) {
elem.second(window_.hasFocus(), 0);
});
ImGui::SFML::Render(window_);
return;
}
showLogs(0);
if (enabled_ || force_enable_) {
@ -194,7 +194,7 @@ void Overlay::update()
Settings::StoreSettings();
}
}
ImGui::Checkbox("Extended logging", &Settings::common.extendedLogging);
ImGui::Checkbox("Extended logging", &Settings::extendedLogging);
ImGuiID dockspace_id = ImGui::GetID("GlosSI-DockSpace");
ImGui::DockSpace(dockspace_id);
@ -215,17 +215,6 @@ void Overlay::update()
closeOverlayButton();
}
std::ranges::for_each(FORCED_OVERLAY_ELEMS_, [this](const auto& elem) {
ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(0.05f, 0.07f, 0.07f, 0.95f));
ImGui::SetNextWindowPos({
ImGui::GetMainViewport()->Size.x * 0.5f,
ImGui::GetMainViewport()->Size.y * 0.5f
}, ImGuiCond_Always,
{0.5f, 0.5f});
elem.second(window_.hasFocus(), 0);
ImGui::PopStyleColor();
});
ImGui::SFML::Render(window_);
}
@ -244,14 +233,9 @@ void Overlay::AddLog(const spdlog::details::log_msg& msg)
LOG_MSGS_.push_back({.time = msg.time, .level = msg.level, .payload = msg.payload.data()});
}
int Overlay::AddOverlayElem(const std::function<void(bool window_has_focus, ImGuiID dockspace_id)>& elem_fn, bool force_show)
int Overlay::AddOverlayElem(const std::function<void(bool window_has_focus, ImGuiID dockspace_id)>& elem_fn)
{
if (force_show) {
FORCED_OVERLAY_ELEMS_.insert({overlay_element_id_, elem_fn});
}
else {
OVERLAY_ELEMS_.insert({overlay_element_id_, elem_fn});
}
OVERLAY_ELEMS_.insert({overlay_element_id_, elem_fn});
// keep this non confusing, but longer...
const auto res = overlay_element_id_;
overlay_element_id_++;
@ -260,10 +244,7 @@ int Overlay::AddOverlayElem(const std::function<void(bool window_has_focus, ImGu
void Overlay::RemoveOverlayElem(int id)
{
if (OVERLAY_ELEMS_.contains(id))
OVERLAY_ELEMS_.erase(id);
if (FORCED_OVERLAY_ELEMS_.contains(id))
FORCED_OVERLAY_ELEMS_.erase(id);
OVERLAY_ELEMS_.erase(id);
}
void Overlay::showLogs(ImGuiID dockspace_id)

@ -1,5 +1,5 @@
/*
Copyright 2021-2023 Peter Repukat - FlatspotSoftware
Copyright 2021-2022 Peter Repukat - FlatspotSoftware
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@ -38,7 +38,7 @@ class Overlay {
static void Shutdown();
static void AddLog(const spdlog::details::log_msg& msg);
static int AddOverlayElem(const std::function<void(bool window_has_focus, ImGuiID dockspace_id)>& elem_fn, bool force_show_ = false);
static int AddOverlayElem(const std::function<void(bool window_has_focus, ImGuiID dockspace_id)>& elem_fn);
static void RemoveOverlayElem(int id);
private:
@ -71,8 +71,6 @@ class Overlay {
static inline int overlay_element_id_ = 0;
static inline std::map<int, std::function<void(bool window_has_focus, ImGuiID dockspace_id)>> OVERLAY_ELEMS_;
static inline std::map<int, std::function<void(bool window_has_focus, ImGuiID dockspace_id)>> FORCED_OVERLAY_ELEMS_;
#ifdef _WIN32
std::string config_file_name_;
#endif

@ -51,8 +51,8 @@ END
//
VS_VERSION_INFO VERSIONINFO
FILEVERSION 0,1,2,0068000002000
PRODUCTVERSION 0,1,2,0068000002000
FILEVERSION 0,0,9,1040000008386
PRODUCTVERSION 0,0,9,1040000008386
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.2.0-68-g0f02eca"
VALUE "FileVersion", "0.0.9.1-40-gdae8386"
VALUE "InternalName", "GlosSITarget"
VALUE "LegalCopyright", "Copyright (C) 2021-2022 Peter Repukat - FlatspotSoftware"
VALUE "OriginalFilename", "GlosSITarget.exe"
VALUE "ProductName", "GlosSI"
VALUE "ProductVersion", "0.1.2.0-68-g0f02eca"
VALUE "ProductVersion", "0.0.9.1-40-gdae8386"
END
END
BLOCK "VarFileInfo"
@ -160,101 +160,6 @@ IDI_ICON1 ICON "..\\GlosSI_Icon.ico"

@ -0,0 +1,260 @@
/*
Copyright 2021-2022 Peter Repukat - FlatspotSoftware
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#pragma once
#include <fstream>
#include <regex>
#include <string>
#include <nlohmann/json.hpp>
#include <spdlog/spdlog.h>
#include <locale>
#include <codecvt>
#ifdef WIN32
#define NOMINMAX
#include <Windows.h>
#endif
namespace Settings {
inline struct Launch {
bool launch = false;
std::wstring launchPath;
std::wstring launchAppArgs;
bool closeOnExit = true;
bool waitForChildProcs = true;
bool isUWP = false;
} launch;
inline struct Devices {
bool hideDevices = true;
bool realDeviceIds = false;
} devices;
inline struct Window {
bool windowMode = false;
int maxFps = 0;
float scale = 0.f;
bool disableOverlay = false;
} window;
inline struct Controller {
int maxControllers = 1;
bool allowDesktopConfig = false;
bool emulateDS4 = false;
} controller;
inline bool extendedLogging = false;
inline std::filesystem::path settings_path_ = "";
inline bool checkIsUwp(const std::wstring& launch_path)
{
if (launch_path.find(L"://") != std::wstring::npos) {
return false;
}
std::wsmatch m;
if (!std::regex_search(launch_path, m, std::wregex(L"^.{1,5}:"))) {
return true;
}
return false;
}
#ifdef WIN32
inline bool isWin10 = false;
typedef LONG NTSTATUS, *PNTSTATUS;
#define STATUS_SUCCESS (0x00000000)
typedef NTSTATUS(WINAPI* RtlGetVersionPtr)(PRTL_OSVERSIONINFOW);
inline RTL_OSVERSIONINFOW GetRealOSVersion()
{
HMODULE hMod = ::GetModuleHandleW(L"ntdll.dll");
if (hMod) {
RtlGetVersionPtr fxPtr = (RtlGetVersionPtr)::GetProcAddress(hMod, "RtlGetVersion");
if (fxPtr != nullptr) {
RTL_OSVERSIONINFOW rovi = {0};
rovi.dwOSVersionInfoSize = sizeof(rovi);
if (STATUS_SUCCESS == fxPtr(&rovi)) {
return rovi;
}
}
}
RTL_OSVERSIONINFOW rovi = {0};
return rovi;
}
inline void checkWinVer()
{
auto VN = GetRealOSVersion();
isWin10 = VN.dwBuildNumber < 22000;
if (isWin10) {
spdlog::info("Running on Windows 10; Winver: {}.{}.{}", VN.dwMajorVersion, VN.dwMinorVersion, VN.dwBuildNumber);
}
else {
spdlog::info("Running on Windows 11; Winver: {}.{}.{}", VN.dwMajorVersion, VN.dwMinorVersion, VN.dwBuildNumber);
}
}
#endif
inline void Parse(std::wstring arg1)
{
const auto config_specified = !std::views::filter(arg1, [](const auto& ch) {
return ch != ' ';
}).empty();
if (config_specified) {
if (!arg1.ends_with(L".json")) {
arg1 += L".json";
}
}
std::filesystem::path path = std::filesystem::temp_directory_path()
.parent_path()
.parent_path()
.parent_path();
path /= "Roaming";
path /= "GlosSI";
if (config_specified) {
path /= "Targets";
path /= arg1;
}
else {
spdlog::info("No config file specified, using default");
path /= "default.json";
}
std::ifstream json_file;
json_file.open(path);
if (!json_file.is_open()) {
spdlog::error(L"Couldn't open settings file {}", path.wstring());
spdlog::debug(L"Using sane defaults...");
return;
}
settings_path_ = path;
auto safeParseValue = [](const auto& object, const auto& key, auto& value) {
try {
if (object.is_null() || object.empty() || object.at(key).empty() || object.at(key).is_null()) {
return;
}
value = object[key];
}
catch (const nlohmann::json::exception& e) {
e.id == 403
? spdlog::trace("Err parsing \"{}\"; {}", key, e.what())
: spdlog::warn("Err parsing \"{}\"; {}", key, e.what());
}
catch (const std::exception& e) {
spdlog::warn("Err parsing \"{}\"; {}", key, e.what());
}
};
auto safeWStringParse = [&safeParseValue](const auto& object, const auto& key, std::wstring& value) {
std::string meh;
safeParseValue(object, key, meh);
if (!meh.empty()) {
// This assumes that char is utf8 and wchar_t is utf16, which is guaranteed on Windows.
value = std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>>().from_bytes(meh);
}
};
const auto json = nlohmann::json::parse(json_file);
int version;
safeParseValue(json, "version", version);
if (version != 1) { // TODO: versioning stuff
spdlog::warn("Config version doesn't match application version.");
}
// TODO: make this as much generic as fits in about the same amount of code if one would parse every value separately.
try {
if (auto launchconf = json["launch"]; !launchconf.is_null() && !launchconf.empty() && launchconf.is_object()) {
safeParseValue(launchconf, "launch", launch.launch);
safeWStringParse(launchconf, "launchPath", launch.launchPath);
safeWStringParse(launchconf, "launchAppArgs", launch.launchAppArgs);
safeParseValue(launchconf, "closeOnExit", launch.closeOnExit);
safeParseValue(launchconf, "waitForChildProcs", launch.waitForChildProcs);
}
if (auto devconf = json["devices"]; !devconf.is_null() && !devconf.empty() && devconf.is_object()) {
safeParseValue(devconf, "hideDevices", devices.hideDevices);
safeParseValue(devconf, "realDeviceIds", devices.realDeviceIds);
}
if (auto winconf = json["window"]; !winconf.is_null() && !winconf.empty() && winconf.is_object()) {
safeParseValue(winconf, "windowMode", window.windowMode);
safeParseValue(winconf, "maxFps", window.maxFps);
safeParseValue(winconf, "scale", window.scale);
safeParseValue(winconf, "disableOverlay", window.disableOverlay);
}
if (auto controllerConf = json["controller"]; !controllerConf.is_null() && !controllerConf.empty() && controllerConf.is_object()) {
safeParseValue(controllerConf, "maxControllers", controller.maxControllers);
safeParseValue(controllerConf, "allowDesktopConfig", controller.allowDesktopConfig);
safeParseValue(controllerConf, "emulateDS4", controller.emulateDS4);
}
}
catch (const nlohmann::json::exception& e) {
spdlog::warn("Err parsing config: {}", e.what());
}
catch (const std::exception& e) {
spdlog::warn("Err parsing config: {}", e.what());
}
safeParseValue(json, "extendedLogging", extendedLogging);
json_file.close();
spdlog::debug("Read config file \"{}\"; config: {}", path.string(), json.dump());
if (launch.launch) {
launch.isUWP = checkIsUwp(launch.launchPath);
}
}
inline void StoreSettings()
{
nlohmann::json json;
json["version"] = 1;
json["launch"]["launch"] = launch.launch;
json["launch"]["launchPath"] = std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>>().to_bytes(launch.launchPath);
json["launch"]["launchAppArgs"] = std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>>().to_bytes(launch.launchAppArgs);
json["launch"]["closeOnExit"] = launch.closeOnExit;
json["launch"]["waitForChildProcs"] = launch.waitForChildProcs;
json["devices"]["hideDevices"] = devices.hideDevices;
json["devices"]["realDeviceIds"] = devices.realDeviceIds;
json["window"]["windowMode"] = window.windowMode;
json["window"]["maxFps"] = window.maxFps;
json["window"]["scale"] = window.scale;
json["window"]["disableOverlay"] = window.disableOverlay;
json["controller"]["maxControllers"] = controller.maxControllers;
json["controller"]["allowDesktopConfig"] = controller.allowDesktopConfig;
json["controller"]["emulateDS4"] = controller.emulateDS4;
json["extendedLogging"] = extendedLogging;
std::ofstream json_file;
json_file.open(settings_path_);
if (!json_file.is_open()) {
spdlog::error(L"Couldn't open settings file {}", settings_path_.wstring());
return;
}
json_file << json.dump(4);
json_file.close();
}
} // namespace Settings

@ -1,5 +1,5 @@
/*
Copyright 2021-2023 Peter Repukat - FlatspotSoftware
Copyright 2021-2022 Peter Repukat - FlatspotSoftware
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@ -17,7 +17,7 @@ limitations under the License.
#include <spdlog/spdlog.h>
#include "..\common\Settings.h"
#include "Settings.h"
#ifdef _WIN32
#define NOMINMAX
@ -47,7 +47,7 @@ void SteamOverlayDetector::update()
if (PeekMessage(&msg, nullptr, 0, 0, PM_NOREMOVE)) {
// filter out some messages as not all get altered by steam...
if (Settings::common.extendedLogging && msg.message != 512 && msg.message != 5374) {
if (Settings::extendedLogging && msg.message != 512 && msg.message != 5374) {
spdlog::trace("PeekMessage: Window msg: {}", msg.message);
}

@ -1,5 +1,5 @@
/*
Copyright 2021-2023 Peter Repukat - FlatspotSoftware
Copyright 2021-2022 Peter Repukat - FlatspotSoftware
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.

@ -1,5 +1,5 @@
/*
Copyright 2021-2023 Peter Repukat - FlatspotSoftware
Copyright 2021-2022 Peter Repukat - FlatspotSoftware
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@ -15,26 +15,26 @@ limitations under the License.
*/
#include "SteamTarget.h"
#include "../common/Settings.h"
#include "Settings.h"
#include "steam_sf_keymap.h"
#include <SFML/Window/Keyboard.hpp>
#include <WinReg/WinReg.hpp>
#include <numeric>
#include <regex>
#include <spdlog/spdlog.h>
#include <vdf_parser.hpp>
#ifdef _WIN32
#include "UWPOverlayEnabler.h"
#include <tray.hpp>
#endif
#include <CEFInject.h>
#include "CommonHttpEndpoints.h"
SteamTarget::SteamTarget()
: window_(
[this] { run_ = false; },
[this] { toggleGlossiOverlay(); },
util::steam::getScreenshotHotkey(steam_path_, steam_user_id_),
getScreenshotHotkey(),
[this]() {
target_window_handle_ = window_.getSystemHandle();
overlay_ = window_.getOverlay();
@ -44,161 +44,81 @@ SteamTarget::SteamTarget()
launcher_(force_config_hwnds_, [this] {
delayed_shutdown_ = true;
delay_shutdown_clock_.restart();
}),
server_([this] { run_ = false; })
})
{
target_window_handle_ = window_.getSystemHandle();
#ifdef _WIN32
if (Settings::launch.isUWP) {
UWPOverlayEnabler::EnableUwpOverlay();
}
else {
UWPOverlayEnabler::AddUwpOverlayOvWidget();
}
#endif
}
int SteamTarget::run()
{
run_ = true;
auto closeBPM = false;
auto closeBPMTimer = sf::Clock{};
if (!SteamOverlayDetector::IsSteamInjected()) {
if (Settings::common.allowStandAlone) {
spdlog::warn("GlosSI not launched via Steam.\nEnabling EXPERIMENTAL global controller and overlay...");
if (Settings::common.standaloneModeGameId == L"") {
spdlog::error("No game id set for standalone mode. Controller will use desktop-config!");
}
auto steam_tweaks = CEFInject::SteamTweaks();
steam_tweaks.setAutoInject(true);
CHTE::addEndpoints();
server_.run();
if (!overlay_.expired())
overlay_.lock()->setEnabled(false);
std::vector<std::function<void()>> end_frame_callbacks;
if (!CEFInject::CEFDebugAvailable()) {
auto overlay_id = std::make_shared<int>(-1);
*overlay_id = Overlay::AddOverlayElem(
[this, overlay_id, &end_frame_callbacks](bool window_has_focus, ImGuiID dockspace_id) {
can_fully_initialize_ = false;
ImGui::Begin("GlosSI - CEF remote debug not available");
ImGui::Text("GlosSI makes use of Steam CEF remote debugging for some functionality and plugins.");
ImGui::Text("GlosSI might not work fully without it.");
if (ImGui::Button("Ignore and continue")) {
can_fully_initialize_ = true;
cef_tweaks_enabled_ = false;
if (*overlay_id != -1) {
end_frame_callbacks.emplace_back([this, overlay_id] {
Overlay::RemoveOverlayElem(*overlay_id);
});
}
}
if (ImGui::Button("Enable and restart Steam")) {
std::ofstream{steam_path_ / ".cef-enable-remote-debugging"};
system("taskkill.exe /im steam.exe /f");
Sleep(200);
launcher_.launchApp((steam_path_ / "Steam.exe").wstring());
run_ = false;
}
ImGui::Text("GlosSI will close upon restarting Steam");
ImGui::End();
},
true);
can_fully_initialize_ = false;
cef_tweaks_enabled_ = false;
}
if (!SteamOverlayDetector::IsSteamInjected() && Settings::common.allowGlobalMode && Settings::common.globalModeGameId == L"") {
auto overlay_id = std::make_shared<int>(-1);
*overlay_id = Overlay::AddOverlayElem(
[this, overlay_id, &end_frame_callbacks](bool window_has_focus, ImGuiID dockspace_id) {
can_fully_initialize_ = false;
ImGui::Begin("Global mode", nullptr, ImGuiWindowFlags_NoSavedSettings);
ImGui::Text("You are running GlosSI in (experimental) global mode (=outside of Steam)");
ImGui::Text("but global mode doesn't appear to be setup properly.");
ImGui::Text("");
ImGui::Text("Please open GlosSI-Config first and setup global mode");
ImGui::Text("");
ImGui::Text("Application will exit on confirm");
if (ImGui::Button("OK")) {
can_fully_initialize_ = true;
if (*overlay_id != -1) {
end_frame_callbacks.emplace_back([this, overlay_id] {
Overlay::RemoveOverlayElem(*overlay_id);
run_ = false;
});
}
}
ImGui::End();
},
true);
can_fully_initialize_ = false;
}
if (!SteamOverlayDetector::IsSteamInjected() && Settings::common.allowGlobalMode) {
auto overlay_id = std::make_shared<int>(-1);
*overlay_id = Overlay::AddOverlayElem(
[this, overlay_id, &end_frame_callbacks](bool window_has_focus, ImGuiID dockspace_id) {
ImGui::Begin("Global mode", nullptr, ImGuiWindowFlags_NoSavedSettings);
ImGui::Text("Global mode is initializing, please stand by...");
ImGui::End();
if (fully_initialized_) {
end_frame_callbacks.emplace_back([this, overlay_id] {
Overlay::RemoveOverlayElem(*overlay_id);
});
}
},
true);
window_.update();
spdlog::warn("Steam-overlay not detected. Showing GlosSI-overlay!\n\
Application will not function!");
window_.setClickThrough(false);
if (!overlay_.expired())
overlay_.lock()->setEnabled(true);
steam_overlay_present_ = false;
}
else {
spdlog::info("Steam-overlay detected.");
spdlog::warn("Double press Steam- overlay key(s)/Controller button to show GlosSI-overlay"); // Just to color output and really get users attention
window_.setClickThrough(true);
if (!overlay_.expired())
overlay_.lock()->setEnabled(false);
steam_overlay_present_ = true;
launcher_.launchWatchdog();
}
getXBCRebindingEnabled();
run_ = true;
#ifdef _WIN32
hidhide_.hideDevices(steam_path_);
input_redirector_.run();
#endif
if (Settings::launch.launch) {
launcher_.launchApp(Settings::launch.launchPath, Settings::launch.launchAppArgs);
}
keepControllerConfig(true);
#ifdef _WIN32
HICON icon = 0;
TCHAR path[MAX_PATH];
GetModuleFileName(nullptr, path, MAX_PATH);
icon = (HICON)LoadImage(
0,
path,
IMAGE_ICON,
GetSystemMetrics(SM_CXSMICON),
GetSystemMetrics(SM_CYSMICON),
LR_LOADFROMFILE | LR_LOADMAP3DCOLORS);
if (!icon) {
ExtractIconEx(path, 0, &icon, nullptr, 1);
}
Tray::Tray tray{"GlosSITarget", icon};
#else
Tray::Tray tray{"GlosSITarget", "ico.png"};
#endif
if (!util::steam::getXBCRebindingEnabled(steam_path_, steam_user_id_)) {
auto overlay_id = std::make_shared<int>(-1);
*overlay_id = Overlay::AddOverlayElem(
[this, overlay_id, &end_frame_callbacks](bool window_has_focus, ImGuiID dockspace_id) {
can_fully_initialize_ = false;
ImGui::Begin("XBox Controller configuration support Disabled", nullptr, ImGuiWindowFlags_NoSavedSettings);
ImGui::TextColored({1.f, 0.8f, 0.f, 1.f}, "XBox Controller configuration support is disabled in Steam. Please enable it in Steam Settings.");
if (ImGui::Button("OK")) {
can_fully_initialize_ = true;
if (*overlay_id != -1) {
end_frame_callbacks.emplace_back([this, overlay_id] {
Overlay::RemoveOverlayElem(*overlay_id);
});
}
}
ImGui::End();
},
true);
can_fully_initialize_ = false;
}
const auto tray = createTrayMenu();
bool delayed_full_init_1_frame = false;
sf::Clock frame_time_clock;
tray.addEntry(Tray::Button{
"Quit", [this, &tray]() {
run_ = false;
}});
while (run_) {
if (!fully_initialized_ && can_fully_initialize_ && delayed_full_init_1_frame) {
init_FuckingRenameMe();
}
else if (!fully_initialized_ && can_fully_initialize_) {
delayed_full_init_1_frame = true;
}
else {
delayed_full_init_1_frame = false;
}
detector_.update();
overlayHotkeyWorkaround();
window_.update();
if (cef_tweaks_enabled_ && fully_initialized_) {
steam_tweaks_.update(frame_time_clock.getElapsedTime().asSeconds());
}
// Wait on shutdown; User might get confused if window closes to fast if anything with launchApp get's borked.
if (delayed_shutdown_) {
if (delay_shutdown_clock_.getElapsedTime().asSeconds() >= 3) {
@ -206,30 +126,16 @@ int SteamTarget::run()
}
}
else {
if (fully_initialized_) {
launcher_.update();
}
launcher_.update();
}
for (auto& efc : end_frame_callbacks) {
efc();
}
end_frame_callbacks.clear();
frame_time_clock.restart();
}
tray->exit();
tray.exit();
server_.stop();
if (fully_initialized_) {
#ifdef _WIN32
input_redirector_.stop();
hidhide_.disableHidHide();
input_redirector_.stop();
hidhide_.disableHidHide();
#endif
launcher_.close();
if (cef_tweaks_enabled_) {
steam_tweaks_.uninstallTweaks();
}
}
launcher_.close();
return 0;
}
@ -238,17 +144,11 @@ void SteamTarget::onOverlayChanged(bool overlay_open)
if (overlay_open) {
focusWindow(target_window_handle_);
window_.setClickThrough(!overlay_open);
if (!Settings::window.windowMode && Settings::window.opaqueSteamOverlay) {
window_.setTransparent(false);
}
}
else {
if (!(overlay_.expired() ? false : overlay_.lock()->isEnabled())) {
window_.setClickThrough(!overlay_open);
focusWindow(last_foreground_window_);
if (!Settings::window.windowMode && Settings::window.opaqueSteamOverlay) {
window_.setTransparent(true);
}
}
}
if (!overlay_trigger_flag_) {
@ -265,9 +165,6 @@ void SteamTarget::onOverlayChanged(bool overlay_open)
void SteamTarget::toggleGlossiOverlay()
{
if (Settings::window.disableGlosSIOverlay) {
return;
}
if (overlay_.expired()) {
return;
}
@ -317,7 +214,7 @@ void SteamTarget::focusWindow(WindowHandle hndl)
AttachThreadInput(current_thread, fg_thread, FALSE);
// try to forcefully set foreground window at least a few times
//try to forcefully set foreground window at least a few times
sf::Clock clock;
while (!SetForegroundWindow(hndl) && clock.getElapsedTime().asMilliseconds() < 20) {
SetActiveWindow(hndl);
@ -326,110 +223,155 @@ void SteamTarget::focusWindow(WindowHandle hndl)
#endif
}
void SteamTarget::init_FuckingRenameMe()
std::filesystem::path SteamTarget::getSteamPath() const
{
if (!SteamOverlayDetector::IsSteamInjected()) {
if (Settings::common.allowGlobalMode) {
spdlog::warn("GlosSI not launched via Steam.\nEnabling EXPERIMENTAL global controller and overlay...");
if (Settings::common.globalModeGameId == L"") {
spdlog::error("No game id set for global mode. Controller will use desktop-config!");
}
#ifdef _WIN32
try {
// TODO: check if keys/value exist
// steam should always be open and have written reg values...
winreg::RegKey key{HKEY_CURRENT_USER, L"SOFTWARE\\Valve\\Steam"};
const auto res = key.GetStringValue(L"SteamPath");
spdlog::info(L"Detected Steam Path: {}", res);
return res;
}
catch (const winreg::RegException& e) {
spdlog::error("Couldn't get Steam path from Registry; {}", e.what());
}
return L"";
#else
return L""; // TODO
#endif
}
SetEnvironmentVariable(L"SteamAppId", L"0");
SetEnvironmentVariable(L"SteamClientLaunch", L"0");
SetEnvironmentVariable(L"SteamEnv", L"1");
SetEnvironmentVariable(L"SteamPath", steam_path_.wstring().c_str());
SetEnvironmentVariable(L"SteamTenfoot", Settings::common.globalModeUseGamepadUI ? L"1" : L"0");
// SetEnvironmentVariable(L"SteamTenfootHybrid", L"1");
SetEnvironmentVariable(L"SteamGamepadUI", Settings::common.globalModeUseGamepadUI ? L"1" : L"0");
SetEnvironmentVariable(L"SteamGameId", Settings::common.globalModeGameId.c_str());
SetEnvironmentVariable(L"SteamOverlayGameId", Settings::common.globalModeGameId.c_str());
SetEnvironmentVariable(L"EnableConfiguratorSupport", L"15");
SetEnvironmentVariable(L"SteamStreamingForceWindowedD3D9", L"1");
if (Settings::common.globalModeUseGamepadUI) {
system("start steam://open/bigpicture");
auto steamwindow = FindWindow(L"Steam Big Picture Mode", nullptr);
auto timer = sf::Clock{};
while (!steamwindow && timer.getElapsedTime().asSeconds() < 2) {
steamwindow = FindWindow(L"Steam Big Picture Mode", nullptr);
Sleep(50);
}
if (cef_tweaks_enabled_) {
steam_tweaks_.setAutoInject(true);
steam_tweaks_.update(999);
}
Sleep(6000); // DIRTY HACK to wait until BPM (GamepadUI) is initialized
// TODO: find way to force BPM even if BPM is not active
LoadLibrary((steam_path_ / "GameOverlayRenderer64.dll").wstring().c_str());
// Overlay switches back to desktop one, once BPM is closed... Disable closing BPM for now.
// TODO: find way to force BPM even if BPM is not active
// closeBPM = true;
// closeBPMTimer.restart();
}
else {
LoadLibrary((steam_path_ / "GameOverlayRenderer64.dll").wstring().c_str());
}
std::wstring SteamTarget::getSteamUserId() const
{
#ifdef _WIN32
try {
// TODO: check if keys/value exist
// steam should always be open and have written reg values...
winreg::RegKey key{HKEY_CURRENT_USER, L"SOFTWARE\\Valve\\Steam\\ActiveProcess"};
const auto res = std::to_wstring(key.GetDwordValue(L"ActiveUser"));
spdlog::info(L"Detected Steam UserId: {}", res);
return res;
}
catch (const winreg::RegException& e) {
spdlog::error("Couldn't get Steam path from Registry; {}", e.what());
}
return L"";
#else
return L""; // TODO
#endif
}
window_.setClickThrough(true);
steam_overlay_present_ = true;
}
else {
spdlog::warn("Steam-overlay not detected and global mode disabled. Showing GlosSI-overlay!\n\
Application will not function!");
window_.setClickThrough(false);
if (!overlay_.expired())
overlay_.lock()->setEnabled(true);
steam_overlay_present_ = false;
}
std::vector<std::string> SteamTarget::getOverlayHotkey()
{
const auto config_path = std::wstring(steam_path_) + std::wstring(user_data_path_) + steam_user_id_ + std::wstring(config_file_name_);
if (!std::filesystem::exists(config_path)) {
spdlog::warn(L"Couldn't read Steam config file: \"{}\"", config_path);
return {"Shift", "KEY_TAB"}; // default
}
else {
spdlog::info("Steam-overlay detected.");
spdlog::warn("Double press Steam- overlay key(s)/Controller button to show GlosSI-overlay"); // Just to color output and really get users attention
window_.setClickThrough(true);
steam_overlay_present_ = true;
std::ifstream config_file(config_path);
auto root = tyti::vdf::read(config_file);
std::shared_ptr<tyti::vdf::basic_object<char>> children = root.childs["system"];
if (!children || children->attribs.empty() || !children->attribs.contains("InGameOverlayShortcutKey")) {
spdlog::warn("Couldn't detect overlay hotkey, using default: Shift+Tab");
return {"Shift", "KEY_TAB"}; // default
}
auto hotkeys = children->attribs.at("InGameOverlayShortcutKey");
#ifdef WIN32
if (!Settings::common.disable_watchdog) {
wchar_t buff[MAX_PATH];
GetModuleFileName(GetModuleHandle(NULL), buff, MAX_PATH);
std::wstring watchDogPath(buff);
watchDogPath = watchDogPath.substr(0, 1 + watchDogPath.find_last_of(L'\\')) + L"GlosSIWatchdog.dll";
// has anyone more than 4 keys to open overlay?!
std::smatch m;
if (!std::regex_match(hotkeys, m, std::regex(R"((\w*)\s*(\w*)\s*(\w*)\s*(\w*))"))) {
spdlog::warn("Couldn't detect overlay hotkey, using default: Shift+Tab");
return {"Shift", "KEY_TAB"}; // default
}
DllInjector::injectDllInto(watchDogPath, L"explorer.exe");
std::vector<std::string> res;
for (auto i = 1; i < m.size(); i++) {
const auto s = std::string(m[i]);
if (!s.empty()) {
res.push_back(s);
}
}
if (res.empty()) {
spdlog::warn("Couldn't detect overlay hotkey, using default: Shift+Tab");
return {"Shift", "KEY_TAB"}; // default
}
spdlog::info("Detected Overlay hotkey(s): {}", std::accumulate(
res.begin() + 1, res.end(), res[0],
[](auto acc, const auto curr) { return acc += "+" + curr; }));
return res;
}
if (Settings::common.no_uwp_overlay) {
UWPOverlayEnabler::AddUwpOverlayOvWidget();
std::vector<std::string> SteamTarget::getScreenshotHotkey()
{
const auto config_path = std::wstring(steam_path_) + std::wstring(user_data_path_) + steam_user_id_ + std::wstring(config_file_name_);
if (!std::filesystem::exists(config_path)) {
spdlog::warn(L"Couldn't read Steam config file: \"{}\"", config_path);
return {"KEY_F12"}; //default
}
else {
UWPOverlayEnabler::EnableUwpOverlay();
std::ifstream config_file(config_path);
auto root = tyti::vdf::read(config_file);
std::shared_ptr<tyti::vdf::basic_object<char>> children = root.childs["system"];
if (!children || children->attribs.empty() || !children->attribs.contains("InGameOverlayScreenshotHotKey")) {
spdlog::warn("Couldn't detect overlay hotkey, using default: F12");
return {"KEY_F12"}; //default
}
auto hotkeys = children->attribs.at("InGameOverlayScreenshotHotKey");
hidhide_.hideDevices(steam_path_);
input_redirector_.run();
#endif
if (Settings::launch.launch) {
launcher_.launchApp(Settings::launch.launchPath, Settings::launch.launchAppArgs);
// has anyone more than 4 keys to screenshot?!
std::smatch m;
if (!std::regex_match(hotkeys, m, std::regex(R"((\w*)\s*(\w*)\s*(\w*)\s*(\w*))"))) {
spdlog::warn("Couldn't detect overlay hotkey, using default: F12");
return {"KEY_F12"}; //default
}
keepControllerConfig(true);
if (cef_tweaks_enabled_) {
steam_tweaks_.setAutoInject(true);
std::vector<std::string> res;
for (auto i = 1; i < m.size(); i++) {
const auto s = std::string(m[i]);
if (!s.empty()) {
res.push_back(s);
}
}
if (res.empty()) {
spdlog::warn("Couldn't detect overlay hotkey, using default: F12");
return {"KEY_F12"}; //default
}
spdlog::info("Detected screenshot hotkey(s): {}", std::accumulate(
res.begin() + 1, res.end(), res[0],
[](auto acc, const auto curr) { return acc += "+" + curr; }));
return res;
}
bool SteamTarget::getXBCRebindingEnabled()
{
const auto config_path = std::wstring(steam_path_) + std::wstring(user_data_path_) + steam_user_id_ + std::wstring(config_file_name_);
if (!std::filesystem::exists(config_path)) {
spdlog::warn(L"Couldn't read Steam config file: \"{}\"", config_path);
return false;
}
std::ifstream config_file(config_path);
auto root = tyti::vdf::read(config_file);
fully_initialized_ = true;
if (root.attribs.empty() || !root.attribs.contains("SteamController_XBoxSupport")) {
spdlog::warn("\"Xbox Configuration Support\" is disabled in Steam. This may cause doubled Inputs!");
return false;
}
auto xbsup = root.attribs.at("SteamController_XBoxSupport");
if (xbsup != "1") {
spdlog::warn("\"Xbox Configuration Support\" is disabled in Steam. This may cause doubled Inputs!");
}
return xbsup == "1";
}
/*
* The "magic" that keeps a controller-config forced (without hooking into Steam)
*
* Hook into own process and detour "GetForegroundWindow"
* Detour function always returns HWND of own application window
* Deatour function always returns HWND of own application window
* Steam now doesn't detect application changes and keeps the game-specific input config without reverting to desktop-conf
*/
void SteamTarget::keepControllerConfig(bool keep)
@ -449,7 +391,6 @@ void SteamTarget::keepControllerConfig(bool keep)
spdlog::error("Couldn't un-install GetForegroundWindow hook!");
}
}
#endif
}
@ -481,34 +422,6 @@ HWND SteamTarget::keepFgWindowHookFn()
}
#endif
std::unique_ptr<Tray::Tray> SteamTarget::createTrayMenu()
{
#ifdef _WIN32
HICON icon = 0;
TCHAR path[MAX_PATH];
GetModuleFileName(nullptr, path, MAX_PATH);
icon = (HICON)LoadImage(
0,
path,
IMAGE_ICON,
GetSystemMetrics(SM_CXSMICON),
GetSystemMetrics(SM_CYSMICON),
LR_LOADFROMFILE | LR_LOADMAP3DCOLORS);
if (!icon) {
ExtractIconEx(path, 0, &icon, nullptr, 1);
}
auto tray = std::make_unique<Tray::Tray>("GlosSITarget", icon);
#else
auto tray = std::make_unique<Tray::Tray>("GlosSITarget", "ico.png");
#endif
tray->addEntry(Tray::Button{
"Quit", [this, &tray]() {
run_ = false;
}});
return tray;
}
void SteamTarget::overlayHotkeyWorkaround()
{
static bool pressed = false;

@ -1,5 +1,5 @@
/*
Copyright 2021-2023 Peter Repukat - FlatspotSoftware
Copyright 2021-2022 Peter Repukat - FlatspotSoftware
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@ -14,30 +14,22 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
#pragma once
#define WIN32_LEAN_AND_MEAN
#include "SteamOverlayDetector.h"
#include "TargetWindow.h"
#ifdef _WIN32
#include "../common/HidHide.h"
#include "HidHide.h"
#include "InputRedirector.h"
#include <subhook.h>
#endif
#include <filesystem>
#include "AppLauncher.h"
#include "CEFInject.h"
#include "Overlay.h"
#include "HttpServer.h"
#include "../common/steam_util.h"
#include <filesystem>
namespace Tray {
class Tray;
}
class SteamTarget {
public:
explicit SteamTarget();
@ -47,16 +39,17 @@ class SteamTarget {
void onOverlayChanged(bool overlay_open);
void toggleGlossiOverlay();
void focusWindow(WindowHandle hndl);
std::filesystem::path getSteamPath() const;
std::wstring getSteamUserId() const;
std::filesystem::path steam_path_ = util::steam::getSteamPath();
std::wstring steam_user_id_ = util::steam::getSteamUserId();
std::filesystem::path steam_path_ = getSteamPath();
std::wstring steam_user_id_ = getSteamUserId();
std::vector<std::string> getOverlayHotkey();
std::vector<std::string> getScreenshotHotkey();
bool getXBCRebindingEnabled();
CEFInject::SteamTweaks steam_tweaks_;
bool cef_tweaks_enabled_ = true;
bool steam_overlay_present_ = false;
bool fully_initialized_ = false;
bool can_fully_initialize_ = true;
void init_FuckingRenameMe();
// Keep controllerConfig even is window is switched.
// On Windoze hooking "GetForeGroundWindow" is enough;
@ -69,8 +62,6 @@ class SteamTarget {
static inline HWND last_real_hwnd_ = nullptr;
#endif
std::unique_ptr<Tray::Tray> createTrayMenu();
/*
* Run once per frame
* detects steam configured overlay hotkey, and simulates key presses to window
@ -80,7 +71,7 @@ class SteamTarget {
void overlayHotkeyWorkaround();
bool run_ = false;
std::vector<std::string> overlay_hotkey_ = util::steam::getOverlayHotkey(steam_path_, steam_user_id_);
std::vector<std::string> overlay_hotkey_ = getOverlayHotkey();
#ifdef _WIN32
HidHide hidhide_;
@ -90,14 +81,18 @@ class SteamTarget {
std::weak_ptr<Overlay> overlay_;
SteamOverlayDetector detector_;
AppLauncher launcher_;
HttpServer server_;
WindowHandle last_foreground_window_ = nullptr;
static inline WindowHandle target_window_handle_ = nullptr;
sf::Clock overlay_trigger_clock_;
float overlay_trigger_max_seconds_ = 2.5;
uint32_t overlay_trigger_max_seconds_ = 1;
bool overlay_trigger_flag_ = false;
bool delayed_shutdown_ = false;
sf::Clock delay_shutdown_clock_;
static constexpr std::wstring_view user_data_path_ = L"/userdata/";
static constexpr std::wstring_view config_file_name_ = L"/config/localconfig.vdf";
static constexpr std::string_view overlay_hotkey_name_ = "InGameOverlayShortcutKey ";
static constexpr std::string_view screenshot_hotkey_name_ = "InGameOverlayScreenshotHotKey ";
};

@ -1,5 +1,5 @@
/*
Copyright 2021-2023 Peter Repukat - FlatspotSoftware
Copyright 2021-2022 Peter Repukat - FlatspotSoftware
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@ -30,7 +30,7 @@ limitations under the License.
#include "ProcessPriority.h"
#include "..\common\Settings.h"
#include "Settings.h"
#if !defined(WM_DPICHANGED)
#define WM_DPICHANGED 0x02E0
@ -56,11 +56,6 @@ TargetWindow::TargetWindow(
if (ImGui::Checkbox("Window mode", &Settings::window.windowMode)) {
toggle_window_mode_after_frame_ = true;
}
#ifdef _WIN32
if (ImGui::Checkbox("Hide from Alt+Tab", &Settings::window.hideAltTab)) {
toggle_hidealttab_after_frame_ = true;
}
#endif
ImGui::Text("Max. FPS");
ImGui::SameLine();
int max_fps_copy = Settings::window.maxFps;
@ -73,7 +68,7 @@ TargetWindow::TargetWindow(
}
if (Settings::window.maxFps < 15 && Settings::window.maxFps > 0) {
Settings::window.maxFps = 0;
setFpsLimit(TargetWindow::calcAutoRefreshRate(screen_refresh_rate_));
setFpsLimit(screen_refresh_rate_);
} else {
setFpsLimit(Settings::window.maxFps);
}
@ -121,69 +116,17 @@ void TargetWindow::setClickThrough(bool click_through)
}
#ifdef _WIN32
HWND hwnd = window_.getSystemHandle();
// hiding GlosSI from Alt-Tab list
// https://learn.microsoft.com/en-us/windows/win32/winmsg/extended-window-styles
if (Settings::window.hideAltTab) {
toggle_hidealttab_after_frame_ = false;
if (click_through) {
SetWindowLong(hwnd, GWL_EXSTYLE, WS_EX_LAYERED | WS_EX_TRANSPARENT | WS_EX_TOPMOST | WS_EX_COMPOSITED | WS_EX_TOOLWINDOW);
SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
}
else {
SetWindowLong(hwnd, GWL_EXSTYLE, WS_EX_LAYERED | WS_EX_COMPOSITED | WS_EX_TOOLWINDOW);
SetWindowPos(hwnd, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
}
if (click_through) {
SetWindowLong(hwnd, GWL_EXSTYLE, WS_EX_LAYERED | WS_EX_TRANSPARENT | WS_EX_TOPMOST | WS_EX_COMPOSITED);
SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
}
else {
if (click_through) {
SetWindowLong(hwnd, GWL_EXSTYLE, WS_EX_LAYERED | WS_EX_TRANSPARENT | WS_EX_TOPMOST | WS_EX_COMPOSITED);
SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
}
else {
SetWindowLong(hwnd, GWL_EXSTYLE, WS_EX_LAYERED | WS_EX_COMPOSITED);
SetWindowPos(hwnd, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
}
SetWindowLong(hwnd, GWL_EXSTYLE, WS_EX_LAYERED | WS_EX_COMPOSITED);
SetWindowPos(hwnd, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
}
#endif
}
void TargetWindow::setTransparent(bool transparent) const
{
HWND hwnd = window_.getSystemHandle();
if (transparent) {
// if (windowed_) {
// DWM_BLURBEHIND bb{.dwFlags = DWM_BB_ENABLE, .fEnable = true, .hRgnBlur = nullptr};
// DwmEnableBlurBehindWindow(hwnd, &bb);
// } // semi-transparent in window mode, but deprecated api
// On Linux the window will (should) automagically be semi-transparent
// transparent windows window...
auto style = GetWindowLong(hwnd, GWL_STYLE);
style &= ~WS_OVERLAPPED;
style |= WS_POPUP;
SetWindowLong(hwnd, GWL_STYLE, style);
MARGINS margins = { -1 };
DwmExtendFrameIntoClientArea(hwnd, &margins);
spdlog::debug("Setting window to transparent");
} else {
auto style = GetWindowLong(hwnd, GWL_STYLE);
style |= WS_OVERLAPPED;
style &= ~WS_POPUP;
SetWindowLong(hwnd, GWL_STYLE, style);
MARGINS margins = {0};
DwmExtendFrameIntoClientArea(hwnd, &margins);
spdlog::debug("Setting window to opaque");
}
}
void TargetWindow::update()
{
sf::Event event{};
@ -199,15 +142,9 @@ void TargetWindow::update()
screenShotWorkaround();
overlay_->update();
window_.display();
#ifdef _WIN32
if (toggle_hidealttab_after_frame_) {
toggle_hidealttab_after_frame_ = false;
}
#endif
if (toggle_window_mode_after_frame_) {
createWindow();
}
// As SFML screws us out of most windows-events, just poll resolution every once in a while
// If changed, recreate window.
// Fixes Blackscreen issues when user does funky stuff and still uses GlosSI in non windowed mod...
@ -342,18 +279,6 @@ WORD TargetWindow::GetWindowDPI(HWND hWnd)
}
#endif
unsigned int TargetWindow::calcAutoRefreshRate(unsigned int rate)
{
unsigned int auto_refresh_rate = rate;
while (auto_refresh_rate > 60) {
auto_refresh_rate /= 2;
}
if (auto_refresh_rate < 30) {
auto_refresh_rate = 30;
}
return auto_refresh_rate;
}
void TargetWindow::createWindow()
{
toggle_window_mode_after_frame_ = false;
@ -399,7 +324,21 @@ void TargetWindow::createWindow()
auto dpi = GetWindowDPI(hwnd);
spdlog::debug("Screen DPI: {}", dpi);
setTransparent(true);
//if (windowed_) {
// DWM_BLURBEHIND bb{.dwFlags = DWM_BB_ENABLE, .fEnable = true, .hRgnBlur = nullptr};
// DwmEnableBlurBehindWindow(hwnd, &bb);
//} // semi-transparent in window mode, but deprecated api
// On Linux the window will (should) automagically be semi-transparent
// transparent windows window...
auto style = GetWindowLong(hwnd, GWL_STYLE);
style &= ~WS_OVERLAPPED;
style |= WS_POPUP;
SetWindowLong(hwnd, GWL_STYLE, style);
MARGINS margins;
margins.cxLeftWidth = -1;
DwmExtendFrameIntoClientArea(hwnd, &margins);
DEVMODE dev_mode = {};
dev_mode.dmSize = sizeof(DEVMODE);
@ -411,14 +350,13 @@ void TargetWindow::createWindow()
screen_refresh_rate_ = 60;
}
else {
setFpsLimit(TargetWindow::calcAutoRefreshRate(dev_mode.dmDisplayFrequency));
setFpsLimit(dev_mode.dmDisplayFrequency);
screen_refresh_rate_ = dev_mode.dmDisplayFrequency;
}
overlay_ = std::make_shared<Overlay>(
window_, [this]() { close(); }, toggle_overlay_state_, Settings::window.windowMode);
spdlog::debug("auto screen Scale: {}", dpi/96.f);
ImGuiIO& io = ImGui::GetIO();
io.FontGlobalScale = dpi / 96.f;
ImGui::SFML::UpdateFontTexture();
@ -432,13 +370,12 @@ void TargetWindow::createWindow()
setFpsLimit(Settings::window.maxFps);
}
if (Settings::window.scale > 0.3f) { // Now that's just getting ridicoulus
spdlog::debug("setting screen scale by config: {}", Settings::window.scale);
ImGuiIO& io = ImGui::GetIO();
io.FontGlobalScale = Settings::window.scale;
ImGui::SFML::UpdateFontTexture();
}
else {
spdlog::debug("Not applying too low screen scale setting");
spdlog::warn("Not applying too low screen scale setting");
}
// window_.setSize({desktop_mode.width - 1, desktop_mode.height - 1 });

@ -1,5 +1,5 @@
/*
Copyright 2021-2023 Peter Repukat - FlatspotSoftware
Copyright 2021-2022 Peter Repukat - FlatspotSoftware
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@ -39,7 +39,6 @@ class TargetWindow {
void setFpsLimit(unsigned int fps_limit);
void setClickThrough(bool click_through);
void setTransparent(bool transparent) const;
void update();
void close();
@ -80,9 +79,7 @@ class TargetWindow {
std::shared_ptr<Overlay> overlay_;
static unsigned int calcAutoRefreshRate(unsigned int rate);
void createWindow();
bool toggle_window_mode_after_frame_ = false;
bool toggle_hidealttab_after_frame_ = false;
};

@ -18,13 +18,44 @@ inline std::filesystem::path EnablerPath()
return path.substr(0, 1 + path.find_last_of(L'\\')) + L"UWPOverlayEnablerDLL.dll";
}
inline DWORD ExplorerPid()
{
PROCESSENTRY32 entry;
entry.dwSize = sizeof(PROCESSENTRY32);
HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL);
if (Process32First(snapshot, &entry) == TRUE) {
while (Process32Next(snapshot, &entry) == TRUE) {
if (std::wstring(entry.szExeFile).find(L"explorer.exe") != std::string::npos) {
return entry.th32ProcessID;
}
}
}
CloseHandle(snapshot);
return 0;
}
} // namespace internal
inline void EnableUwpOverlay()
{
const auto enabler_path = internal::EnablerPath();
DllInjector::injectDllInto(enabler_path, L"explorer.exe");
if (std::filesystem::exists(enabler_path)) {
const auto explorer_pid = internal::ExplorerPid();
if (explorer_pid != 0) {
if (DllInjector::TakeDebugPrivilege()) {
// No need to eject, as the dll is self-ejecting.
if (DllInjector::Inject(explorer_pid, enabler_path.wstring())) {
spdlog::info("Successfully injected UWPOverlay enabler into explorer.exe");
}
}
}
else {
spdlog::error("explorer not found"); // needs loglevel WTF
}
}
else {
spdlog::error("UWPOverlayEnablerDLL not found");
}
}
inline void AddUwpOverlayOvWidget()

@ -0,0 +1,55 @@
/*
Copyright 2021-2022 Peter Repukat - FlatspotSoftware
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#include "UnhookUtil.h"
#include <spdlog/spdlog.h>
#include "Settings.h"
void UnhookUtil::UnPatchHook(const std::string& name, HMODULE module)
{
spdlog::trace("Patching \"{}\"...", name);
BYTE* address = reinterpret_cast<BYTE*>(GetProcAddress(module, name.c_str()));
if (!address) {
spdlog::error("failed to unpatch \"{}\"", name);
}
std::string bytes;
if (Settings::isWin10 && UNHOOK_BYTES_ORIGINAL_WIN10.contains(name)) {
bytes = UNHOOK_BYTES_ORIGINAL_WIN10.at(name);
}
else {
bytes = UNHOOK_BYTES_ORIGINAL_22000.at(name);
}
DWORD dw_old_protect, dw_bkup;
const auto len = bytes.size();
if (!VirtualProtect(address, len, PAGE_EXECUTE_READWRITE, &dw_old_protect)) { // Change permissions of memory..
spdlog::error("Couldn't change permissions of memory for \"{}\"", name);
return;
}
const auto opcode = *(address);
if (!std::ranges::any_of(JUMP_INSTR_OPCODES, [&opcode](const auto& op) { return op == opcode; })) {
spdlog::debug("\"{}\" Doesn't appear to be hooked, skipping!", name);
}
else {
for (DWORD i = 0; i < len; i++) // unpatch Valve's hook
{
*(address + i) = bytes[i];
}
spdlog::trace("Unpatched \"{}\"", name);
}
VirtualProtect(address, len, dw_old_protect, &dw_bkup); // Revert permission change...
}

@ -0,0 +1,61 @@
/*
Copyright 2021-2022 Peter Repukat - FlatspotSoftware
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#pragma once
#define NOMINMAX
#include <map>
#include <Windows.h>
#include <string>
#include <vector>
namespace UnhookUtil {
void UnPatchHook(const std::string& name, HMODULE module);
static inline const std::vector<uint8_t> JUMP_INSTR_OPCODES = {
0xE9,
0xE8,
0xEB,
0xEA,
0xFF};
// Valve Hooks various functions and hides Gaming devices like this.
// To be able to query them, unpatch the hook with the original bytes...
// 22000 ^= Windows build number
static inline const std::map<std::string, std::string> UNHOOK_BYTES_ORIGINAL_22000 = {
{"SetupDiEnumDeviceInfo", "\x48\x89\x5C\x24\x08"},
{"SetupDiGetClassDevsW", "\x48\x89\x5C\x24\x08"},
{"HidD_GetPreparsedData", "\x48\x89\x5C\x24\x18"},
{"HidP_GetCaps", "\x4C\x8B\xD1\x48\x85\xC9"},
{"HidD_GetAttributes", "\x40\x53\x48\x83\xEC"},
{"HidD_GetProductString", "\x48\x83\xEC\x48\x48"},
{"HidP_GetUsages", "\x4C\x89\x4C\x24\x20"},
{"HidP_GetData", "\x4C\x89\x44\x24\x18"},
{"HidP_GetValueCaps", "\x48\x83\xEC\x48\x49"},
{"HidP_GetUsageValue", "\x40\x53\x55\x56\x48"},
{"HidP_GetButtonCaps", "\x48\x83\xEC\x48\x49"},
// Valve hooks "CreateProcess" to detect child-processes
{"CreateProcessW", "\x4C\x8B\xDC\x48\x83"},
};
// SetupApi.dll is different on Win10 than on Win11
static inline const std::map<std::string, std::string> UNHOOK_BYTES_ORIGINAL_WIN10 = {
{"SetupDiEnumDeviceInfo", "\x40\x53\x56\x57\x41\x54\x41\x55"},
{"SetupDiGetClassDevsW", "\x48\x8B\xC4\x48\x89\x58\x08"},
};
} // namespace UnhookUtil

@ -1,5 +1,5 @@
/*
Copyright 2021-2023 Peter Repukat - FlatspotSoftware
Copyright 2021-2022 Peter Repukat - FlatspotSoftware
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@ -14,14 +14,10 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#define NOMINMAX
#include <Windows.h>
#include <DbgHelp.h>
#include <ShlObj.h>
#include <shellapi.h>
#endif
#include <httplib.h>
#include <spdlog/sinks/basic_file_sink.h>
#include <spdlog/sinks/stdout_color_sinks.h>
@ -30,7 +26,7 @@ limitations under the License.
#include "SteamTarget.h"
#include "OverlayLogSink.h"
#include "..\common\Settings.h"
#include "Settings.h"
#include <iostream>
#include "../version.hpp"
@ -71,7 +67,15 @@ LONG Win32FaultHandler(struct _EXCEPTION_POINTERS* ExInfo)
MINIDUMP_EXCEPTION_INFORMATION M;
HANDLE hDump_File;
auto path = util::path::getDataDirPath();
auto path = std::filesystem::temp_directory_path()
.parent_path()
.parent_path()
.parent_path();
path /= "Roaming";
path /= "GlosSI";
if (!std::filesystem::exists(path))
std::filesystem::create_directories(path);
path /= "glossitarget.dmp";
M.ThreadId = GetCurrentThreadId();
@ -115,7 +119,15 @@ int main(int argc, char* argv[])
const auto console_sink = std::make_shared<spdlog::sinks::stderr_color_sink_mt>();
console_sink->set_level(spdlog::level::trace);
#ifdef _WIN32
auto path = util::path::getDataDirPath();
auto path = std::filesystem::temp_directory_path()
.parent_path()
.parent_path()
.parent_path();
path /= "Roaming";
path /= "GlosSI";
if (!std::filesystem::exists(path))
std::filesystem::create_directories(path);
path /= "glossitarget.log";
// For "path.wstring()" to be usable here, SPDLOG_WCHAR_FILENAMES must be defined.
const auto file_sink = std::make_shared<spdlog::sinks::basic_file_sink_mt>(path.wstring(), true);
@ -141,21 +153,12 @@ int main(int argc, char* argv[])
auto exit = 1;
try {
#ifdef _WIN32
auto existingwindow = FindWindowA(nullptr, "GlosSITarget");
if (existingwindow) {
spdlog::error("GlosSITarget is already running! Closing old process...");
httplib::Client client("http://localhost:8756");
client.Post("/quit");
}
int numArgs;
LPWSTR* args = CommandLineToArgvW(GetCommandLine(), &numArgs);
std::vector<std::wstring> argsv;
argsv.reserve(numArgs);
std::wstring argsv = L"";
if (numArgs > 1) {
for (int i = 1; i < numArgs; i++)
argsv.emplace_back(args[i]);
argsv += i == 1 ? args[i] : std::wstring(L" ") + args[i];
}
Settings::Parse(argsv);
Settings::checkWinVer();

@ -1,5 +1,5 @@
/*
Copyright 2021-2023 Peter Repukat - FlatspotSoftware
Copyright 2021-2022 Peter Repukat - FlatspotSoftware
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.

@ -1,41 +0,0 @@
{
"version": "2.0.0",
"tasks": [
{
"type": "shell",
"label": "Build GlosSIWatchdog (Debug)",
"command": "msbuild.exe",
"args": [
"GlosSI.sln",
"/target:GlosSIWatchdog",
"/p:Configuration=Debug",
"/p:Platform=x64"
],
"options": {
"cwd": "${workspaceFolder}/..",
},
"problemMatcher": ["$msCompile"],
"group": {
"kind": "build",
},
},
{
"type": "shell",
"label": "Re-Build GlosSIWatchdog (Debug)",
"command": "msbuild.exe",
"args": [
"GlosSI.sln",
"/target:GlosSIWatchdog:Rebuild",
"/p:Configuration=Debug",
"/p:Platform=x64"
],
"options": {
"cwd": "${workspaceFolder}/..",
},
"problemMatcher": ["$msCompile"],
"group": {
"kind": "build",
},
}
]
}

@ -40,13 +40,13 @@
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
@ -71,12 +71,10 @@
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<IncludePath>..\deps\spdlog\include;..\deps\json\include;..\deps\cpp-httplib;..\CEFInjectLib;$(IncludePath)</IncludePath>
<LibraryPath>..\x64\Debug;$(LibraryPath)</LibraryPath>
<IncludePath>..\deps\spdlog\include;..\deps\json\include;$(IncludePath)</IncludePath>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<IncludePath>..\deps\spdlog\include;..\deps\json\include;..\deps\cpp-httplib;..\CEFInjectLib;$(IncludePath)</IncludePath>
<LibraryPath>..\x64\Release;$(LibraryPath)</LibraryPath>
<IncludePath>..\deps\spdlog\include;..\deps\json\include;$(IncludePath)</IncludePath>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
@ -113,15 +111,11 @@
<PreprocessorDefinitions>_DEBUG;_WINDOWS;%(PreprocessorDefinitions);WATCHDOG;SPDLOG_WCHAR_TO_UTF8_SUPPORT;SPDLOG_WCHAR_FILENAMES;_SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<LanguageStandard>stdcpp20</LanguageStandard>
<FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
<EnableParallelCodeGeneration>true</EnableParallelCodeGeneration>
<EnableEnhancedInstructionSet>StreamingSIMDExtensions2</EnableEnhancedInstructionSet>
<FloatingPointModel>Fast</FloatingPointModel>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalDependencies>hid.lib;Cfgmgr32.lib;setupapi.lib;CefInjectLib.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>hid.lib;Cfgmgr32.lib;setupapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
<CustomBuildStep>
<Command>powershell.exe $(SolutionDir)version_help.ps1</Command>
@ -137,16 +131,13 @@
<PreprocessorDefinitions>NDEBUG;_WINDOWS;%(PreprocessorDefinitions);WATCHDOG;SPDLOG_WCHAR_TO_UTF8_SUPPORT;SPDLOG_WCHAR_FILENAMES;_SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<LanguageStandard>stdcpp20</LanguageStandard>
<FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
<EnableEnhancedInstructionSet>StreamingSIMDExtensions2</EnableEnhancedInstructionSet>
<FloatingPointModel>Fast</FloatingPointModel>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalDependencies>hid.lib;Cfgmgr32.lib;setupapi.lib;CefInjectLib.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>hid.lib;Cfgmgr32.lib;setupapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
<CustomBuildStep>
<Outputs>Upading version based on git;%(Outputs)</Outputs>
@ -154,9 +145,9 @@
</CustomBuildStep>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="..\common\HidHide.cpp" />
<ClCompile Include="..\common\UnhookUtil.cpp" />
<ClCompile Include="dllmain.cpp" />
<ClCompile Include="..\GlosSITarget\HidHide.cpp" />
<ClCompile Include="..\GlosSITarget\UnhookUtil.cpp" />
<ClCompile Include="main.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="resource.h" />

@ -15,13 +15,13 @@
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="dllmain.cpp">
<ClCompile Include="main.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\common\HidHide.cpp">
<ClCompile Include="..\GlosSITarget\HidHide.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\common\UnhookUtil.cpp">
<ClCompile Include="..\GlosSITarget\UnhookUtil.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>

@ -1,162 +0,0 @@
/*
Copyright 2021-2023 Peter Repukat - FlatspotSoftware
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#include <httplib.h>
#include "../common/util.h"
#include <filesystem>
#include <spdlog/sinks/basic_file_sink.h>
#include <spdlog/sinks/stdout_color_sinks.h>
#include <spdlog/spdlog.h>
#include <nlohmann/json.hpp>
#include "../version.hpp"
#include "../common/Settings.h"
#include "../common/HidHide.h"
bool IsProcessRunning(DWORD pid)
{
const HANDLE process = OpenProcess(SYNCHRONIZE, FALSE, pid);
if (process == nullptr)
return false;
const DWORD ret = WaitForSingleObject(process, 0);
CloseHandle(process);
return ret == WAIT_TIMEOUT;
}
void fetchSettings(httplib::Client& http_client, int retried_count = 0) {
http_client.set_connection_timeout(1 + (retried_count > 0 ? 2 : 0));
auto http_res = http_client.Get("/settings");
if (http_res.error() == httplib::Error::Success && http_res->status == 200)
{
const auto json = nlohmann::json::parse(http_res->body);
spdlog::debug("Received settings from GlosSITarget: {}", json.dump());
Settings::Parse(json);
}
else
{
spdlog::error("Couldn't get settings from GlosSITarget. Error: {}", (int)http_res.error());
if (retried_count < 2)
{
spdlog::info("Retrying... {}", retried_count);
fetchSettings(http_client, retried_count + 1);
}
}
}
DWORD WINAPI watchdog(HMODULE hModule)
{
auto configDirPath = util::path::getDataDirPath();
auto logPath = configDirPath;
logPath /= "GlosSIWatchdog.log";
const auto file_sink = std::make_shared<spdlog::sinks::basic_file_sink_mt>(logPath.wstring(), true);
std::vector<spdlog::sink_ptr> sinks{ file_sink };
auto logger = std::make_shared<spdlog::logger>("log", sinks.begin(), sinks.end());
logger->set_level(spdlog::level::trace);
logger->flush_on(spdlog::level::trace);
spdlog::set_default_logger(logger);
spdlog::info("GlosSIWatchdog loaded");
spdlog::info("Version: {}", version::VERSION_STR);
auto glossi_hwnd = FindWindowA(nullptr, "GlosSITarget");
if (!glossi_hwnd)
{
spdlog::error("Couldn't find GlosSITarget window. Exiting...");
FreeLibraryAndExitThread(hModule, 1);
return 1;
}
spdlog::debug("Found GlosSITarget window; Starting watch loop");
httplib::Client http_client("http://localhost:8756");
fetchSettings(http_client);
std::vector<DWORD> pids;
while (glossi_hwnd)
{
http_client.set_connection_timeout(120);
const auto http_res = http_client.Get("/launched-pids");
if (http_res.error() == httplib::Error::Success && http_res->status == 200)
{
const auto json = nlohmann::json::parse(http_res->body);
if (Settings::common.extendedLogging)
{
spdlog::trace("Received pids: {}", json.dump());
}
pids = json.get<std::vector<DWORD>>();
}
else {
spdlog::error("Couldn't fetch launched PIDs: {}", (int)http_res.error());
}
glossi_hwnd = FindWindowA(nullptr, "GlosSITarget");
Sleep(333);
}
spdlog::info("GlosSITarget was closed. Resetting HidHide state...");
HidHide hidhide;
hidhide.disableHidHide();
if (Settings::launch.closeOnExit)
{
spdlog::info("Closing launched processes");
for (const auto pid : pids)
{
if (Settings::common.extendedLogging)
{
spdlog::debug("Checking if process {} is running", pid);
}
if (IsProcessRunning(pid))
{
util::win::process::KillProcess(pid);
}
else
{
if (Settings::common.extendedLogging)
{
spdlog::debug("Process {} is not running", pid);
}
}
}
}
spdlog::info("Unloading Watchdog...");
FreeLibraryAndExitThread(hModule, 0);
}
BOOL APIENTRY DllMain(HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
if (ul_reason_for_call == DLL_PROCESS_ATTACH)
{
CloseHandle(CreateThread(nullptr, 0, (LPTHREAD_START_ROUTINE)watchdog, hModule, 0, nullptr));
}
else if (ul_reason_for_call == DLL_PROCESS_DETACH) {
}
return TRUE;
return 0;
}

@ -0,0 +1,77 @@
/*
Copyright 2021-2022 Peter Repukat - FlatspotSoftware
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#define NOMINMAX
#include <Windows.h>
#include <filesystem>
#include <spdlog/sinks/basic_file_sink.h>
#include <spdlog/sinks/stdout_color_sinks.h>
#include <spdlog/spdlog.h>
#include "../version.hpp"
#include "../GlosSITarget/HidHide.h"
int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPWSTR lpCmdLine,
_In_ int nCmdShow)
{
UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine);
auto configDirPath = std::filesystem::temp_directory_path()
.parent_path()
.parent_path()
.parent_path();
configDirPath /= "Roaming";
configDirPath /= "GlosSI";
if (!std::filesystem::exists(configDirPath))
std::filesystem::create_directories(configDirPath);
auto logPath = configDirPath;
logPath /= "GlosSIWatchdog.log";
const auto file_sink = std::make_shared<spdlog::sinks::basic_file_sink_mt>(logPath.wstring(), true);
std::vector<spdlog::sink_ptr> sinks{ file_sink };
auto logger = std::make_shared<spdlog::logger>("log", sinks.begin(), sinks.end());
logger->set_level(spdlog::level::trace);
logger->flush_on(spdlog::level::trace);
spdlog::set_default_logger(logger);
spdlog::info("GlosSIWatchdog loaded");
spdlog::info("Version: {}", version::VERSION_STR);
auto glossi_hwnd = FindWindowA(nullptr, "GlosSITarget");
if (!glossi_hwnd)
{
spdlog::error("Couldn't find GlosSITarget window. Exiting...");
return 1;
}
spdlog::debug("Found GlosSITarget window; Starting watch loop");
while (glossi_hwnd)
{
glossi_hwnd = FindWindowA(nullptr, "GlosSITarget");
Sleep(1337);
}
spdlog::info("GlosSITarget was closed. Cleaning up...");
HidHide hidhide;
hidhide.disableHidHide();
return 0;
}

@ -3,7 +3,7 @@
!define APP_NAME "GlosSI"
!define COMP_NAME "Peter Repukat - Flatspotsoftware"
!define WEB_SITE "https://glossi.flatspot.pictures/"
!define VERSION "0.1.2.0-68-g0f02eca"
!define VERSION "0.0.9.1-48-geb4ae9c"
!define COPYRIGHT "Peter Repukat - FlatspotSoftware © 2017-2022"
!define DESCRIPTION "SteamInput compatibility tool"
!define INSTALLER_NAME "GlosSI-Installer.exe"
@ -193,55 +193,3 @@ SectionEnd

@ -1,8 +1,7 @@
[![Build status](https://ci.appveyor.com/api/projects/status/l9hq9qglvn6q5wdg/branch/main?svg=true)](https://ci.appveyor.com/project/Alia5/glossi/branch/main) [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) [![Discord](https://img.shields.io/discord/368823110817808384.svg)](https://discord.gg/T9b4D5y) ![version](https://img.shields.io/github/v/tag/alia5/glossi?label=version) [![WebsiteAndDownloads](https://img.shields.io/website?label=Website%20%26%20downloads&url=https%3A%2F%2Fglossi.flatspot.pictures)](https://glossi.flatspot.pictures)
<div style="display: flex; align-items: center">
<h1 align="center"><img align="center" src="https://github.com/Alia5/GlosSI/blob/main/GlosSI_Logo_512.png?raw=true" width="256" height="256" alt="GlosSI Logo" />
GlosSI&nbsp;-&nbsp;Global&nbsp;(systemwide)&nbsp;Steam&nbsp;Input</h1>
<img align="center" src="https://github.com/Alia5/GlosSI/blob/main/GlosSI_Logo_512.png?raw=true" width="256" height="256" alt="GlosSI Logo" /><h1>GlosSI - Global (systemwide) Steam Input</h1>
</div>
GlosSI formerly knows as GloSC (Global Steam Controller), is a tool that allows one to use Steam-Input controller rebinding at a system-level alongside a system wide (borderless window) Steam overlay
@ -12,44 +11,6 @@ The primary use case of GlosSI is to use SteamInput (required for SteamControlle
GlosSI can, but isn't required to, launch any of your favorite games or applications and directly add them to Steam, be it Win32 or Windows Store (UWP)!
It is **the tool** to enjoy any game that has trouble with Steam and/or *add extra functionality* to your Steam-Input needs
---
```
```
# ViGEm End of Life
As you may or may not have already noticed ViGEm, a substantial part in making GlosSI work it's magic is End of Life.
You can read the announcement [here](https://docs.nefarius.at/projects/ViGEm/End-of-Life/)
As I don't think holding on to deprecated dependencies is a good way of moving forward, this effectively kills GlosSI as well since without ViGEm and HidHide GlosSI cannot function.
There will be a last deprecated version of ViGEm circumventing an issue stated in their announcement.
GlosSI will be updated a last time as well, providing this version bundled with it.
The GlosSI website will be taken down, however, you can then fin the last release, here on GitHub
### GlosSI won't be taken down or magically stop working, nor will it be unsafe to use. Just a maintenance stop.
---
I also want to take the opportunity to give a MASSIVE shoutout to @Nefarius , the creator of ViGEm, HidHide and many other awesome tools
Back when good old GloSC was just a cobbled together PoC using parts of the very old ScpToolkit he has been massively helpful and even shared ViGEm with me way before it was ready to be run on any machine that doesn't belong to a wizard (or should I say sorcerer?) like him.
---
Will GlosSI continue?
Probably. But most likely not in its current form.
@Nefarius wizardry is continuing and a successor to ViGEm is being worked on, but nothing has been publicly released, yet.
As I severely lack the time to properly maintain a project like GlosSI (as you probably have already noticed, I'm sure), I'm quite fond of the idea of rebooting the project yet again, once ViGEms successor becomes available.
However, I'm not sure if I find the time and motivation again to continue with GlosSI
Until I can (and want to) get my hands on it, the future is unsure...
I'll be back to silence for now then.
Thanks for all the support, it was a blast!
```
```
---
## How does it work? / What does it do?
@ -105,7 +66,7 @@ For Building instructions refer to [BUILDING.md](./docs/BUILDING.md)
## License
```license
Copyright 2017-2023 Peter Repukat - FlatspotSoftware
Copyright 2017-2022 Peter Repukat - FlatspotSoftware
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.

@ -1,187 +0,0 @@
module.exports = {
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/eslint-recommended',
'plugin:@typescript-eslint/recommended',
'plugin:@typescript-eslint/recommended-requiring-type-checking',
],
env: {
browser: true,
node: false
},
plugins: [
'@typescript-eslint',
'no-null',
'prefer-arrow',
'import',
],
parser: '@typescript-eslint/parser', // Specifies the ESLint parser
parserOptions: {
ecmaVersion: 2022, // Allows for the parsing of modern ECMAScript features
sourceType: 'module',
ecmaFeatures: {
jsx: false,
},
project: ['./tsconfig.json'],
tsconfigRootDir: __dirname
},
rules: {
'@typescript-eslint/no-namespace': 'off',
'@typescript-eslint/ban-types': 'error',
'@typescript-eslint/adjacent-overload-signatures': 'error',
'@typescript-eslint/array-type': 'error',
'@typescript-eslint/consistent-type-definitions': ['error', 'interface'],
'@typescript-eslint/no-inferrable-types': 'error',
'@typescript-eslint/no-misused-new': 'error',
'@typescript-eslint/no-this-alias': 'error',
'@typescript-eslint/prefer-for-of': 'error',
'@typescript-eslint/prefer-function-type': 'error',
'@typescript-eslint/prefer-namespace-keyword': 'error',
'no-inner-declarations': 'off', // we have es6blocked scoped functions.
'@typescript-eslint/triple-slash-reference': 'error',
'@typescript-eslint/type-annotation-spacing': 'error',
'@typescript-eslint/unified-signatures': 'error',
'@typescript-eslint/no-explicit-any': 'error',
'@typescript-eslint/no-unused-vars': 'error',
'@typescript-eslint/unbound-method': 'warn',
'@typescript-eslint/semi': [
'error',
'always'
],
'@typescript-eslint/quotes': [
'warn',
'single'
],
'@typescript-eslint/member-delimiter-style': [
'error',
{
'multiline': {
'delimiter': 'semi',
'requireLast': true
},
'singleline': {
'delimiter': 'semi',
'requireLast': false
}
}
],
'@typescript-eslint/indent': [
'warn',
4,
{
'FunctionDeclaration': {
'parameters': 'first'
},
'FunctionExpression': {
'parameters': 'first'
},
'SwitchCase': 1
}
],
'@typescript-eslint/explicit-member-accessibility': [
'error',
{
'accessibility': 'explicit'
}
],
'@typescript-eslint/no-use-before-define': ['error', { 'functions': false }],
// "@typescript-eslint/naming-convention": [
// "error",
// {
// "selector": "default",
// "format": ["camelCase", "PascalCase"]
// },
// {
// "selector": "variable",
// "format": ["camelCase", "UPPER_CASE"]
// },
// {
// "selector": "parameter",
// "format": ["camelCase"],
// "leadingUnderscore": "allow"
// },
// {
// "selector": "memberLike",
// "modifiers": ["private"],
// "format": ["camelCase"],
// "leadingUnderscore": "require"
// },
// {
// "selector": "typeLike",
// "format": ["PascalCase"]
// }
// ],
'no-console': 'off',
'no-return-await': 'error',
'arrow-body-style': 'error',
'arrow-parens': [
'error',
'always'
],
'camelcase': ['warn', { "ignoreImports": true }],
'comma-dangle': [
'error',
{
'objects': 'never',
'arrays': 'never',
'functions': 'never'
}
],
'prefer-arrow/prefer-arrow-functions': 'error',
'prefer-arrow-callback': 'error',
'prefer-const': 'error',
'quote-props': [
'error',
'consistent-as-needed'
],
'no-var': 'error',
'new-parens': 'error',
'no-caller': 'error',
'no-cond-assign': 'error',
'no-debugger': 'error',
'no-empty': 'error',
'no-eval': 'error',
'no-multiple-empty-lines': 'warn',
'no-new-wrappers': 'error',
'no-redeclare': 'error',
'no-shadow': [
'error',
{
'hoist': 'all'
}
],
'no-null/no-null': 'error',
'no-throw-literal': 'error',
'no-trailing-spaces': 'error',
'no-undef-init': 'error',
'no-underscore-dangle': 'error',
'no-unsafe-finally': 'error',
'no-unused-labels': 'error',
'spaced-comment': 'error',
'use-isnan': 'error',
'max-lines': [
'error',
{
'max': 300,
'skipBlankLines': true,
'skipComments': true
}
],
'max-len': [
'warn',
{
'code': 140
}
],
'dot-notation': 'error',
'eqeqeq': 'error',
'eol-last': 'error',
'linebreak-style': ['error', 'windows'],
'block-spacing': ['error', 'always'],
'object-curly-spacing': ["error", "always"],
'import/no-deprecated': 'warn', // eslint deprecation rule sucks. just wrns on deprecated IMPORTs
},
settings: {
},
};

@ -1,4 +0,0 @@
node_modules/
dist/
tsconfig.tsbuildinfo
.rollup.tscache/

@ -1,10 +0,0 @@
{
"configurations": [
{
"name": "Attach to Steam CEF",
"port": 8080,
"request": "attach",
"type": "chrome",
}
]
}

File diff suppressed because it is too large Load Diff

@ -1,28 +0,0 @@
{
"name": "glossi_steamtweaks",
"version": "0.0.0",
"type": "module",
"scripts": {
"clean": "npx rimraf dist .rollup.tscache tsconfig.tsbuildinfo ",
"build": "npx rollup -c rollup.config.js",
"build:clean": "npm run clean && npm run build",
"build:copy": "npx rimraf ../x64/Debug/SteamTweaks && npm run build && cd dist && npx copyfiles -a -V ./**/* ../../x64/Debug/SteamTweaks"
},
"author": "Peter Repukat - FlatspotSoftware",
"license": "Apache-2.0",
"devDependencies": {
"@rollup/plugin-typescript": "^11.0.0",
"@typescript-eslint/eslint-plugin": "^5.49.0",
"@typescript-eslint/parser": "^5.49.0",
"copyfiles": "^2.4.1",
"eslint": "^8.33.0",
"eslint-plugin-import": "^2.27.5",
"eslint-plugin-no-null": "^1.0.2",
"eslint-plugin-prefer-arrow": "^1.2.3",
"rollup": "^3.12.0",
"typescript": "^4.9.4",
"rimraf": "^4.1.2"
},
"dependencies": {
}
}

@ -1,46 +0,0 @@
import typescript from '@rollup/plugin-typescript';
import { readdirSync, lstatSync } from 'fs';
import path from 'path';
const getFileListForDir = (dir) => {
return readdirSync(dir).map((file) => {
const absolute = path.resolve(dir, file);
if (file.endsWith('.ts')) {
return absolute;
}
if (lstatSync(absolute).isDirectory()) {
return getFileListForDir(absolute)
}
}).flat(999);
}
const tsPluginConf = typescript({
cacheDir: '.rollup.tscache'
});
export default [
{
input: 'src/GlosSITweaks.ts',
output: {
dir: 'dist',
sourcemap: "inline",
format: 'iife',
// name: 'GlosSITweaks' // don't use name; don't pollute global namespace
},
plugins: [tsPluginConf],
},
...getFileListForDir('src/Tweaks').map((file) => {
return {
input: file,
output: {
file: file.replace('src', 'dist').replace(/\.ts$/, '.js'),
sourcemap: "inline",
format: 'iife',
// name: path.basename(file).replace(/\.ts$/, '') // don't use name; don't pollute global namespace
},
plugins: [tsPluginConf],
}
})
];

@ -1,44 +0,0 @@
export interface GlosSISettings {
controller: {
allowDesktopConfig: boolean;
emulateDS4: boolean;
maxControllers: number;
};
devices: {
hideDevices: boolean;
realDeviceIds: boolean;
};
extendedLogging: boolean;
globalModeGameId: string;
globalModeUseGamepadUI: boolean;
icon?: string;
ignoreEGS: boolean;
killEGS: boolean;
launch: {
closeOnExit: boolean;
ignoreLauncher: boolean;
killLauncher: boolean;
launch: boolean;
launchAppArgs?: string;
launchPath?: string;
launcherProcesses: string[];
waitForChildProcs: boolean;
};
name?: string;
snapshotNotify: boolean;
standaloneModeGameId: string;
standaloneUseGamepadUI: boolean;
minimizeSteamGamepadUI: boolean;
steamPath: string;
steamUserId: string;
steamgridApiKey: string;
version: number;
window: {
disableGlosSIOverlay: boolean;
disableOverlay: boolean;
hideAltTab: boolean;
maxFps?: number;
scale?: number;
windowMode: boolean;
};
}

@ -1,26 +0,0 @@
export interface SteamClient {
Settings: {
// Current stable (As time of commit); Beta does not have this anymore...
SetInGameOverlayShowFPSCorner?: (value: 0|1|2|3|4) => void;
SetInGameOverlayShowFPSContrast?: (value: boolean) => void;
// TODO: find a way to change setting on beta (and soon stable...)
};
UI: {
GetUiMode: () => Promise<SteamUiMode>;
SetUiMode: (mode: SteamUiMode) => void;
};
Window: {
Minimize();
HideWindow();
};
}
export type FullSteamClient = Required<SteamClient>;
declare global {
interface Window {
SteamClient: SteamClient;
}
// eslint-disable-next-line
declare const SteamClient: SteamClient;
}

@ -1,157 +0,0 @@
import type { SteamConfig } from './common/util/types';
import { fetchWithTimeout } from './common/util/util';
import type { GlosSISettings } from './@types/GlosSISettings';
class SteamTargetApi {
public static GlosSIActive = true;
public static readonly ACTIVE_FAIL_THRESHOLD = 2;
private activeFailCounter = 0;
private static ActiveCheckTimer = 0;
public constructor() {
if (SteamTargetApi.ActiveCheckTimer !== 0) {
clearInterval(SteamTargetApi.ActiveCheckTimer);
}
SteamTargetApi.ActiveCheckTimer = setInterval(() => {
void this.getGlosSIActive().then((active) => {
if (!active) {
this.activeFailCounter++;
if (this.activeFailCounter >= SteamTargetApi.ACTIVE_FAIL_THRESHOLD) {
window?.GlosSITweaks?.GlosSI?.uninstall?.();
}
}
});
}, 666);
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
public log(level: string, ...args: any[]) {
void fetch('http://localhost:8756/log', {
method: 'POST',
body: JSON.stringify({
level,
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
message: `${args}`
})
});
switch (level) {
case 'error':
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
console.error(...args);
break;
case 'warn':
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
console.warn(...args);
break;
case 'info':
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
console.info(...args);
break;
case 'debug':
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
console.debug(...args);
break;
default:
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
console.log(...args);
}
}
public async getGlosSIActive() {
return fetchWithTimeout('http://localhost:8756/running', { timeout: 500 })
.then(
() => {
SteamTargetApi.GlosSIActive = true;
return true;
}
).catch(() => {
SteamTargetApi.GlosSIActive = false;
return false;
});
}
public getSteamSettings(): Promise<SteamConfig> {
return fetch('http://localhost:8756/steam_settings')
.then(
(res) => res.json().then(
(json) => (json as SteamConfig).UserLocalConfigStore as SteamConfig
)
);
}
public getGlosSISettings() {
return fetch('http://localhost:8756/settings')
.then(
(res) => res.json().then(
(json) => json as GlosSISettings
)
);
}
}
class GlosSIApiCtor {
public readonly SteamTarget: SteamTargetApi = new SteamTargetApi();
}
interface GlosSITweaks {
[tweakName: string]: { readonly install: () => unknown; readonly uninstall?: () => void };
}
declare global {
interface Window {
GlosSITweaks: GlosSITweaks;
GlosSIApi: InstanceType<typeof GlosSIApiCtor>;
}
// eslint-disable-next-line
const GlosSIApi: InstanceType<typeof GlosSIApiCtor>;
// eslint-disable-next-line
const GlosSITweaks: GlosSITweaks;
}
const installGlosSIApi = () => {
window.GlosSITweaks = {
GlosSI: {
install: () => {
const api = new GlosSIApiCtor();
Object.assign(window, { GlosSIApi: api });
},
uninstall: () => {
Object.entries(window.GlosSITweaks)
.filter(([tweakName]) => (tweakName !== 'GlosSI'))
.forEach(([, obj]) => obj.uninstall?.());
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
delete window.GlosSIApi;
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
delete window.GlosSITweaks;
}
}
};
window.GlosSITweaks.GlosSI.install();
const glossiCheckInterval = setInterval(() => {
if (window.GlosSIApi) {
void window.GlosSIApi.SteamTarget.getGlosSIActive().then((active) => {
if (!active) {
window?.GlosSITweaks?.GlosSI?.uninstall?.();
}
});
return;
}
clearTimeout(glossiCheckInterval);
}, 5000);
};
if (!window.GlosSITweaks || !window.GlosSIApi) {
installGlosSIApi();
}
export default !!window.GlosSITweaks && !!window.GlosSIApi;

@ -1,21 +0,0 @@
import { initTweak } from '../../common/tweakApi';
initTweak('MinimizeSteamGamepadUI', async () => {
const [isGamepadUI, minimizeGPUI] = await Promise.all([
// (async () => (await SteamClient.UI.GetUiMode()) === SteamUiMode.GamepadUI)(),
true, // Steam is always GamepadUI if injected into GamepadUI, duh!
(async () => (await GlosSIApi.SteamTarget.getGlosSISettings()).minimizeSteamGamepadUI)()
]);
if (isGamepadUI && minimizeGPUI) {
SteamClient.Window.Minimize();
return true;
}
if (!isGamepadUI && minimizeGPUI) {
GlosSIApi.SteamTarget.log('warn', 'MinimizeSteamGamepadUI is enabled but Steam is not in GamepadUI mode');
}
return false;
}).then((minimized: boolean) => {
GlosSIApi.SteamTarget.log('debug', 'MinimizeSteamGamepadUI installed; Minimized GamepadUI:', minimized);
}).catch((e) =>GlosSIApi.SteamTarget.log('error', 'MinimizeSteamGamepadUI failed to install', e));

@ -1,33 +0,0 @@
import type { SteamConfig } from '../../../common/util/types';
import { initTweak } from '../../../common/tweakApi';
const backup: { originalFpsCorner?: number } = {};
initTweak('HideFPSCounter', {
install: async () => {
backup.originalFpsCorner = Number(
((await GlosSIApi.SteamTarget.getSteamSettings()).system as SteamConfig)
.InGameOverlayShowFPSCorner
) as 0 | 1 | 2 | 3 | 4;
if (!SteamClient.Settings.SetInGameOverlayShowFPSCorner) {
GlosSIApi.SteamTarget.log('warn',
'HideFPSCounter: SteamClient.Settings.SetInGameOverlayShowFPSCorner is not available.'
+'Can\'t hide FPS Counter corner.'
);
}
SteamClient.Settings.SetInGameOverlayShowFPSCorner?.(0);
},
uninstall: () => {
if (!SteamClient.Settings.SetInGameOverlayShowFPSCorner) {
return;
}
GlosSIApi.SteamTarget.log('debug','uninstalling HideFPSCounter Tweak. Restoring FPS Counter corner: ', backup.originalFpsCorner);
SteamClient.Settings.SetInGameOverlayShowFPSCorner?.((backup.originalFpsCorner ?? 0) as 0 | 1 | 2 | 3 | 4);
setTimeout(() => {
// steam might not actually register the setting?! Try again like 10 seconds later... ¯\_(ツ)_/¯
SteamClient.Settings.SetInGameOverlayShowFPSCorner?.((backup.originalFpsCorner ?? 0) as 0 | 1 | 2 | 3 | 4);
}, 10 * 1000);
}
}).then(() => {
GlosSIApi.SteamTarget.log('debug', 'HideFPSCounter installed');
}).catch((e) => GlosSIApi.SteamTarget.log('error', 'HideFPSCounter failed to install', e));

@ -1,8 +0,0 @@
// eslint-disable-next-line no-shadow
export enum SteamUiMode {
Desktop = 0,
Unknown1 = 1,
Unknown2 = 2,
Unknown3 = 3,
GamepadUI = 4,
}

@ -1,29 +0,0 @@
export const initTweak = <T>(name: string, tweakMain: (() => T)|{
install: () => T;
uninstall: () => void;
}, force = false): T => {
if (!force && window.GlosSITweaks[name]) {
throw new Error(`Tweak ${name} is already installed!`);
}
if (typeof tweakMain === 'object') {
window.GlosSITweaks[name] = { install: tweakMain.install, uninstall: () => {
try {
tweakMain.uninstall();
} catch (e) {
GlosSIApi.SteamTarget.log('error', e);
}
delete window.GlosSITweaks[name];
} };
} else {
window.GlosSITweaks[name] = { install: tweakMain };
}
try {
return window.GlosSITweaks[name].install() as T;
} catch (e) {
GlosSIApi.SteamTarget.log('error', e);
throw e;
}
};

@ -1,3 +0,0 @@
export interface SteamConfig {
[key: string]: string|SteamConfig;
}

@ -1,12 +0,0 @@
export const fetchWithTimeout = async (input: RequestInfo | URL, init?: RequestInit & { timeout: number }) => {
const { timeout = 8000 } = init || {};
const controller = new AbortController();
const id = setTimeout(() => controller.abort(), timeout);
const response = await fetch(input, {
...(init ||{}),
signal: controller.signal
});
clearTimeout(id);
return response;
};

@ -1,26 +0,0 @@
{
"compilerOptions": {
"target": "ES2020",
"module": "ES2022",
"noImplicitAny": true,
"rootDir": "./src",
"outDir": "./dist",
"inlineSourceMap": true,
"esModuleInterop": true,
"strict": true,
"skipLibCheck": true,
"useDefineForClassFields": true,
"forceConsistentCasingInFileNames": true,
"incremental": true,
"lib": [
"esnext",
"DOM"
],
},
"include": [
"src/**/*"
],
"exclude": [
"node_modules",
]
}

@ -1,41 +0,0 @@
{
"version": "2.0.0",
"tasks": [
{
"type": "shell",
"label": "Build UWPOverlayEnablerDLL (Debug)",
"command": "msbuild.exe",
"args": [
"GlosSI.sln",
"/target:UWPOverlayEnablerDLL",
"/p:Configuration=Debug",
"/p:Platform=x64"
],
"options": {
"cwd": "${workspaceFolder}/..",
},
"problemMatcher": ["$msCompile"],
"group": {
"kind": "build",
},
},
{
"type": "shell",
"label": "Re-Build UWPOverlayEnablerDLL (Debug)",
"command": "msbuild.exe",
"args": [
"GlosSI.sln",
"/target:UWPOverlayEnablerDLL:Rebuild",
"/p:Configuration=Debug",
"/p:Platform=x64"
],
"options": {
"cwd": "${workspaceFolder}/..",
},
"problemMatcher": ["$msCompile"],
"group": {
"kind": "build",
},
}
]
}

@ -127,10 +127,6 @@
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
<LanguageStandard>stdcpp20</LanguageStandard>
<FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
<EnableParallelCodeGeneration>true</EnableParallelCodeGeneration>
<EnableEnhancedInstructionSet>StreamingSIMDExtensions2</EnableEnhancedInstructionSet>
<FloatingPointModel>Fast</FloatingPointModel>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
@ -149,9 +145,6 @@
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
<LanguageStandard>stdcpp20</LanguageStandard>
<EnableEnhancedInstructionSet>StreamingSIMDExtensions2</EnableEnhancedInstructionSet>
<FloatingPointModel>Fast</FloatingPointModel>
<FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>

@ -1,5 +1,5 @@
/*
Copyright 2021-2023 Peter Repukat - FlatspotSoftware
Copyright 2021-2022 Peter Repukat - FlatspotSoftware
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@ -45,8 +45,6 @@ There are two (known to me, at time of writing) ways to get a working overlay fo
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#include "../common/util.h"
#define SUBHOOK_STATIC
#include <atomic>
#include <filesystem>
@ -142,7 +140,19 @@ BOOL APIENTRY DllMain( HMODULE hModule,
{
if (ul_reason_for_call == DLL_PROCESS_ATTACH)
{
auto configDirPath = util::path::getDataDirPath();
auto configDirPath = std::filesystem::temp_directory_path()
.parent_path()
.parent_path()
.parent_path();
configDirPath /= "Roaming";
configDirPath /= "GlosSI";
if (!std::filesystem::exists(configDirPath))
std::filesystem::create_directories(configDirPath);
auto logPath = configDirPath;
logPath /= "UWPOverlayEnabler.log";
const auto file_sink = std::make_shared<spdlog::sinks::basic_file_sink_mt>(logPath.string(), true);

@ -1,38 +1,8 @@
diff --git forkSrcPrefix/src/ViGEmClient.vcxproj forkDstPrefix/src/ViGEmClient.vcxproj
index 7c186414e62a6334fbcd518d506f55db57491dfe..2955df4d391b37bf0b71e17df8bd161c1014a0c4 100644
--- forkSrcPrefix/src/ViGEmClient.vcxproj
+++ forkDstPrefix/src/ViGEmClient.vcxproj
@@ -49,52 +49,52 @@
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug_LIB|Win32'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
- <PlatformToolset>v142</PlatformToolset>
+ <PlatformToolset>v143</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug_DLL|Win32'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
- <PlatformToolset>v142</PlatformToolset>
+ <PlatformToolset>v143</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release_LIB|Win32'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
- <PlatformToolset>v142</PlatformToolset>
+ <PlatformToolset>v143</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release_DLL|Win32'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
- <PlatformToolset>v142</PlatformToolset>
+ <PlatformToolset>v143</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
diff --git a/src/ViGEmClient.vcxproj b/src/ViGEmClient.vcxproj
index 7c186414e62a6334fbcd518d506f55db57491dfe..6b71920a9b848e4a5a3ffb314a2e009a7a22502f 100644
--- a/src/ViGEmClient.vcxproj
+++ b/src/ViGEmClient.vcxproj
@@ -75,26 +75,26 @@
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug_LIB|x64'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>

@ -5,10 +5,6 @@ Remove-Item -Recurse -Force "x64\Release"
$env:_CL_="/MD"
msbuild.exe GlosSI.sln /t:Build /p:Configuration=Release /p:Platform=x64
cd ./SteamTweaks
npm i
npm run build
cd ..
cd ./x64/Release/
@ -30,8 +26,6 @@ Copy-Item "..\..\vc_redist.x64.exe" -Destination "."
Copy-Item "..\..\LICENSE" -Destination "./LICENSE"
Copy-Item "..\..\QT_License" -Destination "./QT_License"
Copy-Item "..\..\THIRD_PARTY_LICENSES.txt" -Destination "./THIRD_PARTY_LICENSES.txt"
Copy-Item "..\..\SteamTweaks\dist" -Destination "./SteamTweaks" -Recurse
#7z a GlosSI-snapshot.zip *

@ -18,8 +18,6 @@ Copy-Item "..\..\vc_redist.x64.exe" -Destination "."
Copy-Item "..\..\LICENSE" -Destination "./LICENSE"
Copy-Item "..\..\QT_License" -Destination "./QT_License"
Copy-Item "..\..\THIRD_PARTY_LICENSES.txt" -Destination "./THIRD_PARTY_LICENSES.txt"
Copy-Item "..\..\steamgrid.exe" -Destination "./steamgrid.exe"
Copy-Item "..\..\SteamTweaks\dist" -Destination "./SteamTweaks" -Recurse
Copy-Item "C:\Qt\Tools\OpenSSL\Win_x64\bin\libcrypto-1_1-x64.dll" -Destination "./libcrypto-1_1-x64.dll"
Copy-Item "C:\Qt\Tools\OpenSSL\Win_x64\bin\libssl-1_1-x64.dll" -Destination "./libssl-1_1-x64.dll"

@ -1,33 +0,0 @@
{
"configurations": [
{
"name": "Win32",
"includePath": [
"${workspaceFolder}/**",
"${workspaceFolder}/../deps/SFML/include",
"${workspaceFolder}/../deps/WinReg",
"${workspaceFolder}/../deps/spdlog/include",
"${workspaceFolder}/../deps/ValveFileVDF",
"${workspaceFolder}/../deps/subhook",
"${workspaceFolder}/../deps/ViGEmClient/include",
"${workspaceFolder}/../deps/imgui",
"${workspaceFolder}/../deps/imgui-sfml",
"${workspaceFolder}/../deps/json/include",
"${workspaceFolder}/../deps/traypp/tray/include",
"${workspaceFolder}/../deps/cpp-httplib",
"${workspaceFolder}/../CEFInjectLib"
],
"defines": [
"_DEBUG",
"UNICODE",
"_UNICODE"
],
"windowsSdkVersion": "10.0.18362.0",
"compilerPath": "C:/Program Files (x86)/Microsoft Visual Studio/2019/BuildTools/VC/Tools/MSVC/14.24.28314/bin/Hostx64/x64/cl.exe",
"cStandard": "c11",
"cppStandard": "c++20",
"intelliSenseMode": "msvc-x64"
}
],
"version": 4
}

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save