Merge pull request #179 from Alia5/develop

Develop
pull/184/head 0.0.9.0
Peter Repukat 2 years ago committed by GitHub
commit e8b2ab0ec0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -1,4 +1,5 @@
UseTab: false
BasedOnStyle: LLVM
IndentWidth: 4
BreakBeforeBraces: "Stroustrup"
AllowShortIfStatementsOnASingleLine: false

@ -137,6 +137,8 @@
<ClCompile Include="UIModel.cpp" />
<None Include=".clang-format" />
<None Include="GetAUMIDs.ps1" />
<None Include="qml\CollapsiblePane.qml" />
<None Include="qml\SteamInputXboxDisabledDialog.qml" />
<None Include="qml\AddSelectTypeDialog.qml" />
<None Include="qml\FluentTextInput.qml" />
<None Include="qml\InfoDialog.qml" />
@ -144,6 +146,7 @@
<None Include="qml\ShortcutCards.qml" />
<None Include="qml\ShortcutProps.qml" />
<None Include="qml\UWPSelectDialog.qml" />
<None Include="qml\SteamNotFoundDialog.qml" />
<QtRcc Include="qml.qrc" />
<None Include="qml\main.qml" />
</ItemGroup>

@ -65,6 +65,15 @@
</None>
<None Include=".clang-format" />
<None Include="GetAUMIDs.ps1" />
<None Include="qml\SteamNotFoundDialog.qml">
<Filter>qml</Filter>
</None>
<None Include="qml\SteamInputXboxDisabledDialog.qml">
<Filter>qml</Filter>
</None>
<None Include="qml\CollapsiblePane.qml">
<Filter>qml</Filter>
</None>
</ItemGroup>
<ItemGroup>
<QtMoc Include="UIModel.h">

@ -51,8 +51,8 @@ END
//
VS_VERSION_INFO VERSIONINFO
FILEVERSION 0,0,8,003002880001
PRODUCTVERSION 0,0,8,003002880001
FILEVERSION 0,0,8,1023005406006
PRODUCTVERSION 0,0,8,1023005406006
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L
@ -69,12 +69,12 @@ BEGIN
BEGIN
VALUE "CompanyName", "Peter Repukat - FlatspotSoftware"
VALUE "FileDescription", "GlosSI - Config"
VALUE "FileVersion", "0.0.8.0-3-g288bba1"
VALUE "FileVersion", "0.0.8.1-23-g54e6bf6"
VALUE "InternalName", "GlosSIConfig"
VALUE "LegalCopyright", "Copyright (C) 2021 Peter Repukat - FlatspotSoftware"
VALUE "OriginalFilename", "GlosSIConfig.exe"
VALUE "ProductName", "GlosSI"
VALUE "ProductVersion", "0.0.8.0-3-g288bba1"
VALUE "ProductVersion", "0.0.8.1-23-g54e6bf6"
END
END
BLOCK "VarFileInfo"
@ -756,6 +756,342 @@ IDI_ICON1 ICON "..\GloSC_Icon.ico"

@ -39,16 +39,16 @@ UIModel::UIModel() : QObject(nullptr)
std::filesystem::create_directories(path);
config_path_ = path;
config_dir_name_ = QString::fromStdWString((path /= "Targets").wstring().data());
config_dir_name_ = QString::fromStdWString((path /= "Targets").wstring());
if (!std::filesystem::exists(path))
std::filesystem::create_directories(path);
parseShortcutVDF();
readConfigs();
readTargetConfigs();
}
void UIModel::readConfigs()
void UIModel::readTargetConfigs()
{
QDir dir(config_dir_name_);
auto entries = dir.entryList(QDir::Files, QDir::SortFlag::Name);
@ -68,29 +68,13 @@ void UIModel::readConfigs()
const auto data = file.readAll();
file.close();
const auto jsondoc = QJsonDocument::fromJson(data);
const auto filejson = jsondoc.object();
QJsonObject json;
json["version"] = filejson["version"];
json["icon"] = filejson["icon"];
json["launch"] = filejson["launch"]["launch"];
json["launchPath"] = filejson["launch"]["launchPath"];
json["launchAppArgs"] = filejson["launch"]["launchAppArgs"];
json["closeOnExit"] = filejson["launch"]["closeOnExit"];
json["waitForChildProcs"] = filejson["launch"]["waitForChildProcs"];
json["hideDevices"] = filejson["devices"]["hideDevices"];
json["realDeviceIds"] = filejson["devices"]["realDeviceIds"];
json["windowMode"] = filejson["window"]["windowMode"];
json["maxFps"] = filejson["window"]["maxFps"];
json["scale"] = filejson["window"]["scale"];
json["disableOverlay"] = filejson["window"]["disableOverlay"];
json["maxControllers"] = filejson["controller"]["maxControllers"];
json["allowDesktopConfig"] = filejson["controller"]["allowDesktopConfig"];
json["emulateDS4"] = filejson["controller"]["emulateDS4"];
json["name"] = filejson.contains("name") ? filejson["name"] : QString(name).replace(QRegularExpression("\\.json"), "");
targets_.append(json.toVariantMap());
auto filejson = jsondoc.object();
filejson["name"] = filejson.contains("name")
? filejson["name"].toString()
: QString(name).replace(QRegularExpression("\\.json"), "");
targets_.append(filejson.toVariantMap());
});
emit targetListChanged();
@ -237,6 +221,46 @@ QVariantMap UIModel::manualProps(QVariant shortcut)
return res;
}
void UIModel::enableSteamInputXboxSupport()
{
if (foundSteam()) {
const std::filesystem::path config_path = std::wstring(getSteamPath()) + user_data_path_.toStdWString() + getSteamUserId() + user_config_file_.toStdWString();
if (!std::filesystem::exists(config_path)) {
qDebug() << "localconfig.vdf does not exist.";
}
QFile file(config_path);
if (file.open(QIODevice::Text | QIODevice::ReadOnly)) {
QTextStream in(&file);
QStringList lines;
QString line = in.readLine();
// simple approach is enough...
while (!in.atEnd()) {
if (line.contains("SteamController_XBoxSupport")) {
if (line.contains("1")) {
qDebug() << "\"SteamController_XBoxSupport\" is already enabled! aborting write...";
file.close();
return;
}
qDebug() << "found \"SteamController_XBoxSupport\" line, replacing value...";
line.replace("0", "1");
}
lines.push_back(line);
line = in.readLine();
}
file.close();
QFile updatedFile(config_path);
if (updatedFile.open(QFile::WriteOnly | QFile::Truncate | QFile::Text)) {
qDebug() << "writing localconfig.vdf...";
QTextStream out(&updatedFile);
for (const auto& l : lines) {
out << l << "\n";
}
}
updatedFile.close();
}
}
}
#ifdef _WIN32
QVariantList UIModel::uwpApps()
{
@ -319,60 +343,41 @@ void UIModel::setAcrylicEffect(bool has_acrylic_affect)
emit acrylicChanged();
}
void UIModel::writeTarget(const QJsonObject& json, const QString& name)
void UIModel::writeTarget(const QJsonObject& json, const QString& name) const
{
auto path = config_path_;
path /= config_dir_name_.toStdWString();
path /= (QString(name).replace(QRegularExpression("[\\\\/:*?\"<>|]"), "") + ".json").toStdWString();
QFile file(path);
if (!file.open(QIODevice::Text | QIODevice::ReadWrite)) {
// meh
qDebug() << "Couldn't open file for writing: " << path;
return;
}
QJsonObject fileJson;
fileJson["version"] = json["version"];
fileJson["icon"] = json["icon"];
fileJson["name"] = json["name"];
QJsonObject launchObject;
launchObject["launch"] = json["launch"];
launchObject["launchPath"] = json["launchPath"];
launchObject["launchAppArgs"] = json["launchAppArgs"];
launchObject["closeOnExit"] = json["closeOnExit"];
launchObject["waitForChildProcs"] = json["waitForChildProcs"];
fileJson["launch"] = launchObject;
QJsonObject devicesObject;
devicesObject["hideDevices"] = json["hideDevices"];
devicesObject["realDeviceIds"] = json["realDeviceIds"];
fileJson["devices"] = devicesObject;
QJsonObject windowObject;
windowObject["windowMode"] = json["windowMode"];
windowObject["maxFps"] = json["maxFps"];
windowObject["scale"] = json["scale"];
windowObject["disableOverlay"] = json["disableOverlay"];
fileJson["window"] = windowObject;
QJsonObject controllerObject;
controllerObject["maxControllers"] = json["maxControllers"];
controllerObject["allowDesktopConfig"] = json["allowDesktopConfig"];
controllerObject["emulateDS4"] = json["emulateDS4"];
fileJson["controller"] = controllerObject;
auto wtf = QString(QJsonDocument(fileJson).toJson(QJsonDocument::Indented)).toStdString();
file.write(wtf.data());
file.write(
QString(QJsonDocument(json).toJson(QJsonDocument::Indented))
.toStdString()
.data()
);
file.close();
}
std::filesystem::path UIModel::getSteamPath() const
{
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"};
const auto res = key.GetStringValue(L"SteamPath");
return res;
// 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()) {
return "";
}
const auto res = key.GetStringValue(L"SteamPath");
return res;
}
catch (...) {
return "";
}
#else
return L""; // TODO LINUX
#endif
@ -381,22 +386,46 @@ std::filesystem::path UIModel::getSteamPath() const
std::wstring UIModel::getSteamUserId() const
{
#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\\ActiveProcess"};
const auto res = std::to_wstring(key.GetDwordValue(L"ActiveUser"));
if (res == L"0") {
qDebug() << "Steam not open?";
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()) {
return L"0";
}
const auto res = std::to_wstring(key.GetDwordValue(L"ActiveUser"));
if (res == L"0") {
qDebug() << "Steam not open?";
}
return res;
} catch(...) {
return L"0";
}
return res;
#else
return L""; // TODO LINUX
#endif
}
bool UIModel::foundSteam() const
{
if (getSteamPath() == "" || getSteamUserId() == L"0") {
return false;
}
const std::filesystem::path user_config_dir = std::wstring(getSteamPath()) + user_data_path_.toStdWString() + getSteamUserId();
if (!std::filesystem::exists(user_config_dir)) {
return false;
}
return true;
}
void UIModel::parseShortcutVDF()
{
const std::filesystem::path config_path = std::wstring(getSteamPath()) + user_data_path_.toStdWString() + getSteamUserId() + shortcutsfile_.toStdWString();
if (!std::filesystem::exists(config_path)) {
qDebug() << "Shortcuts file does not exist.";
return;
}
try {
shortcuts_vdf_ = VDFParser::Parser::parseShortcuts(config_path, qDebug());
}
@ -404,3 +433,39 @@ void UIModel::parseShortcutVDF()
qDebug() << "Error parsing VDF: " << e.what();
}
}
bool UIModel::isSteamInputXboxSupportEnabled() const
{
// return true as default to not bug the user in error cases.
if (foundSteam()) {
const std::filesystem::path config_path = std::wstring(getSteamPath()) + user_data_path_.toStdWString() + getSteamUserId() + user_config_file_.toStdWString();
if (!std::filesystem::exists(config_path)) {
qDebug() << "localconfig.vdf does not exist.";
return true;
}
QFile file(config_path);
if (file.open(QIODevice::Text | QIODevice::ReadOnly)) {
QTextStream in(&file);
QString line = in.readLine();
// simple, regex approach should be enough...
while (!in.atEnd()) {
if (line.contains("SteamController_XBoxSupport")) {
file.close();
if (line.contains("1")) {
qDebug() << "\"SteamController_XBoxSupport\" is enabled!";
return true;
}
qDebug() << "\"SteamController_XBoxSupport\" is disabled!";
return false;
}
line = in.readLine();
}
qDebug() << "couldn't find \"SteamController_XBoxSupport\" in localconfig.vdf";
file.close();
}
else {
qDebug() << "could not open localconfig.vdf";
}
}
return true;
}

@ -27,11 +27,13 @@ class UIModel : public QObject {
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(bool foundSteam READ foundSteam CONSTANT)
Q_PROPERTY(bool steamInputXboxSupportEnabled READ isSteamInputXboxSupportEnabled CONSTANT)
public:
UIModel();
Q_INVOKABLE void readConfigs();
Q_INVOKABLE void readTargetConfigs();
Q_INVOKABLE QVariantList getTargetList() const;
Q_INVOKABLE void addTarget(QVariant shortcut);
Q_INVOKABLE void updateTarget(int index, QVariant shortcut);
@ -41,6 +43,7 @@ class UIModel : public QObject {
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();
#ifdef _WIN32
Q_INVOKABLE QVariantList uwpApps();
#endif
@ -61,25 +64,30 @@ class UIModel : public QObject {
void targetListChanged();
private:
#ifdef _WIN32
bool is_windows_ = true;
#else
bool is_windows_ = false;
#endif
bool has_acrylic_affect_ = false;
std::filesystem::path config_path_;
QString config_dir_name_;
void writeTarget(const QJsonObject& json, const QString& name);
std::filesystem::path getSteamPath() const;
std::wstring getSteamUserId() const;
void parseShortcutVDF();
QString shortcutsfile_ = "/config/shortcuts.vdf";
QString user_config_file_ = "/config/localconfig.vdf";
QString user_data_path_ = "/userdata/";
QVariantList targets_;
std::vector<VDFParser::Shortcut> shortcuts_vdf_;
void writeTarget(const QJsonObject& json, const QString& name) const;
#ifdef _WIN32
bool is_windows_ = true;
#else
bool is_windows_ = false;
#endif
bool has_acrylic_affect_ = false;
std::filesystem::path getSteamPath() const;
std::wstring getSteamUserId() const;
bool foundSteam() const;
void parseShortcutVDF();
bool isSteamInputXboxSupportEnabled() const;
};

@ -15,5 +15,9 @@
<file>noise.png</file>
<file>GloSC_Icon_small.png</file>
<file>svg/help_outline_white_24dp.svg</file>
<file>qml/SteamNotFoundDialog.qml</file>
<file>qml/SteamInputXboxDisabledDialog.qml</file>
<file>qml/CollapsiblePane.qml</file>
<file>svg/expand_more_white_24dp.svg</file>
</qresource>
</RCC>

@ -0,0 +1,97 @@
/*
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.
*/
import QtQuick 2.9
import QtQuick.Controls 2.9
import QtQuick.Controls.Material 2.9
import QtQuick.Controls.Material.impl 2.9
RPane {
property alias title: paneTitle.text
width: parent.width
property alias content: ldr.sourceComponent
clip: true
height: paneHeader.height + collapseColumn.spacing
property bool collapsed: true
id: collapsePane
Behavior on height {
NumberAnimation {
duration: 300
easing.type: Easing.InOutQuad
}
}
Column {
id: collapseColumn
width: parent.width
spacing: 16
Item {
id: paneHeader
width: parent.width
height: paneTitle.height + 32
Label {
id: paneTitle
anchors.left: parent.left
anchors.leftMargin: 4
font.bold: true
font.pixelSize: 24
anchors.top: parent.top
anchors.topMargin: 14
}
RoundButton {
width: 48
height: 48
Material.elevation: 0
anchors.rightMargin: 0
anchors.top: parent.top
anchors.topMargin: 0
onClicked: function(){
collapsed = !collapsed;
if (collapsed) {
collapsePane.height = paneHeader.height + collapseColumn.spacing
} else {
collapsePane.height = paneHeader.height + collapseColumn.spacing * 3 + ldr.item.height
}
}
Image {
id: arrowImg
anchors.centerIn: parent
source: "qrc:/svg/expand_more_white_24dp.svg"
width: 24
height: 24
transform: Rotation{
angle: collapsed ? 0 : 180
origin.x: arrowImg.width/2
origin.y: arrowImg.height/2
Behavior on angle {
NumberAnimation {
duration: 125
easing.type: Easing.InOutQuad
}
}
}
}
anchors.right: parent.right
}
}
Loader {
id: ldr
width: parent.width
}
}
}

@ -29,62 +29,62 @@ Item {
signal cancel()
signal done(var shortcut)
property var shortcutInfo: ({
version: 1,
name: null,
launch: false,
launchPath: null,
launchAppArgs: null,
closeOnExit: true,
waitForChildProcs: true,
hideDevices: true,
windowMode: false,
maxFps: null,
scale: null,
icon: null,
maxControllers: 4,
disableOverlay: false,
realDeviceIds: false,
allowDesktopConfig: false,
emulateDS4: false,
})
property var shortcutInfo: ({})
function resetInfo() {
shortcutInfo = ({
version: 1,
name: null,
launch: false,
launchPath: null,
launchAppArgs: null,
closeOnExit: true,
waitForChildProcs: true,
hideDevices: true,
windowMode: false,
maxFps: null,
scale: null,
icon: null,
maxControllers: 4,
disableOverlay: false,
realDeviceIds: false,
allowDesktopConfig: false,
emulateDS4: false,
"controller": {
"maxControllers": 1,
"emulateDS4": false,
"allowDesktopConfig": false
},
"devices": {
"hideDevices": true,
"realDeviceIds": false
},
"icon": null,
"launch": {
"closeOnExit": true,
"launch": false,
"launchAppArgs": null,
"launchPath": null,
"waitForChildProcs": true
},
"name": null,
"version": 1,
"window": {
"disableOverlay": false,
"maxFps": null,
"scale": null,
"windowMode": false
},
"extendedLogging": false
})
}
Component.onCompleted: function() {
resetInfo()
}
onShortcutInfoChanged: function() {
nameInput.text = shortcutInfo.name || ""
launchApp.checked = shortcutInfo.launch || false
pathInput.text = shortcutInfo.launchPath || ""
argsInput.text = shortcutInfo.launchAppArgs || ""
closeOnExit.checked = shortcutInfo.closeOnExit || false
waitForChildren.checked = shortcutInfo.waitForChildProcs
hideDevices.checked = shortcutInfo.hideDevices || false
windowMode.checked = shortcutInfo.windowMode || false
maxControllersSpinBox.value = shortcutInfo.maxControllers
disableOverlayCheckbox.checked = shortcutInfo.disableOverlay || false
realDeviceIds.checked = shortcutInfo.realDeviceIds || false
allowDesktopConfig.checked = shortcutInfo.allowDesktopConfig || false
emulateDS4.checked = shortcutInfo.emulateDS4 || false
if (extendedLogging) {
extendedLogging.checked = shortcutInfo.extendedLogging || false
}
launchApp.checked = shortcutInfo.launch.launch
pathInput.text = shortcutInfo.launch.launchPath || ""
argsInput.text = shortcutInfo.launch.launchAppArgs || ""
closeOnExit.checked = shortcutInfo.launch.closeOnExit
waitForChildren.checked = shortcutInfo.launch.waitForChildProcs
hideDevices.checked = shortcutInfo.devices.hideDevices
realDeviceIds.checked = shortcutInfo.devices.realDeviceIds
windowMode.checked = shortcutInfo.window.windowMode
disableOverlayCheckbox.checked = shortcutInfo.window.disableOverlay
scaleSpinBox.value = shortcutInfo.window.scale
maxFPSSpinBox.value = shortcutInfo.window.maxFps
maxControllersSpinBox.value = shortcutInfo.controller.maxControllers
allowDesktopConfig.checked = shortcutInfo.controller.allowDesktopConfig
emulateDS4.checked = shortcutInfo.controller.emulateDS4
}
Flickable {
@ -144,7 +144,6 @@ Item {
}
RPane {
width: parent.width
height: 248
radius: 4
Material.elevation: 32
bgOpacity: 0.97
@ -155,17 +154,18 @@ Item {
Row {
spacing: 32
width: parent.width
height: closeOnExitCol.height
CheckBox {
id: launchApp
text: qsTr("Launch app")
checked: shortcutInfo.launch
checked: shortcutInfo.launch.launch
onCheckedChanged: function() {
shortcutInfo.launch = checked
shortcutInfo.launch.launch = checked
if (checked) {
closeOnExit.enabled = true;
if (closeOnExit.checked) {
waitForChildren.enabled = true;
if (closeOnExit) {
closeOnExit.enabled = true;
if (closeOnExit.checked) {
waitForChildren.enabled = true;
}
}
allowDesktopConfig.enabled = true;
} else {
@ -175,54 +175,6 @@ Item {
}
}
}
Column {
id: closeOnExitCol
spacing: 2
CheckBox {
id: closeOnExit
text: qsTr("Close when launched app quits")
checked: shortcutInfo.closeOnExit
onCheckedChanged: function() {
shortcutInfo.closeOnExit = checked
if (checked) {
waitForChildren.enabled = true;
} else {
waitForChildren.enabled = false;
}
}
}
Label {
text: qsTr("Recommended to disable for launcher-games")
wrapMode: Text.WordWrap
width: parent.width
leftPadding: 32
topPadding: -8
}
CheckBox {
id: waitForChildren
text: qsTr("Wait for child processes")
checked: shortcutInfo.waitForChildProcs
onCheckedChanged: function(){
shortcutInfo.waitForChildProcs = checked
}
}
}
Column {
spacing: 2
CheckBox {
id: allowDesktopConfig
text: qsTr("Allow desktop-config")
checked: shortcutInfo.allowDesktopConfig
onCheckedChanged: function(){
shortcutInfo.allowDesktopConfig = checked
}
}
Label {
text: qsTr("Use desktop-config if launched application is not focused")
leftPadding: 32
topPadding: -8
}
}
}
Item {
width: 1
@ -231,14 +183,17 @@ Item {
RowLayout {
id: launchlayout
spacing: 4
width: parent.width
anchors.left: parent.left
anchors.right: parent.right
anchors.leftMargin: 32
anchors.rightMargin: 32
Image {
id: maybeIcon
source: shortcutInfo.icon
? shortcutInfo.icon.endsWith(".exe")
? "image://exe/" + shortcutInfo.icon
: "file:///" + shortcutInfo.icon
: null
: ''
Layout.preferredWidth: 48
Layout.preferredHeight: 48
visible: shortcutInfo.icon
@ -267,8 +222,8 @@ Item {
id: pathInput
placeholderText: qsTr("...")
enabled: launchApp.checked
text: shortcutInfo.launchPath || ""
onTextChanged: shortcutInfo.launchPath = text
text: shortcutInfo.launch.launchPath || ""
onTextChanged: shortcutInfo.launch.launchPath = text
}
}
Button {
@ -304,8 +259,8 @@ Item {
anchors.topMargin: 4
id: argsInput
enabled: launchApp.checked
text: shortcutInfo.launchAppArgs
onTextChanged: shortcutInfo.launchAppArgs = text
text: shortcutInfo.launch.launchAppArgs
onTextChanged: shortcutInfo.launch.launchAppArgs = text
}
}
}
@ -315,183 +270,467 @@ Item {
width: 1
height: 8
}
Row {
spacing: 16
width: parent.width
RPane {
width: parent.width / 2 - 8
height: 264
radius: 4
Material.elevation: 32
bgOpacity: 0.97
CollapsiblePane {
radius: 4
Material.elevation: 32
bgOpacity: 0.97
title: qsTr("Advanced")
content:
Column {
spacing: 16
Column {
spacing: 2
RPane {
width: parent.width
Row {
CheckBox {
id: hideDevices
text: qsTr("Hide (Real) Controllers")
checked: shortcutInfo.hideDevices
onCheckedChanged: shortcutInfo.hideDevices = checked
}
RoundButton {
onClicked: () => {
helpInfoDialog.titleText = qsTr("Hide (Real) Controllers")
helpInfoDialog.text =
qsTr("Hides real game controllers from the system\nThis may prevent doubled inputs")
+ "\n"
+ qsTr("You can change this setting and which devices are hidden in the GlosSI overlay")
helpInfoDialog.open()
radius: 4
Material.elevation: 32
bgOpacity: 0.97
height: advancedLaunchCol.height + 24
Column {
id: advancedLaunchCol
spacing: 4
height: advancedLaunchedRow.height
Row {
id: advancedLaunchedRow
spacing: 32
width: parent.width
height: closeOnExitCol.height
Column {
id: closeOnExitCol
spacing: 2
CheckBox {
id: closeOnExit
text: qsTr("Close when launched app quits")
checked: shortcutInfo.launch.closeOnExit
onCheckedChanged: function() {
shortcutInfo.launch.closeOnExit = checked
if (checked) {
waitForChildren.enabled = true;
} else {
waitForChildren.enabled = false;
}
}
}
Label {
text: qsTr("Recommended to disable for launcher-games")
wrapMode: Text.WordWrap
width: parent.width
leftPadding: 32
topPadding: -8
}
CheckBox {
id: waitForChildren
text: qsTr("Wait for child processes")
checked: shortcutInfo.launch.waitForChildProcs
onCheckedChanged: function(){
shortcutInfo.launch.waitForChildProcs = checked
}
}
}
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
Column {
spacing: 2
CheckBox {
id: allowDesktopConfig
text: qsTr("Allow desktop-config")
checked: shortcutInfo.controller.allowDesktopConfig
onCheckedChanged: function(){
shortcutInfo.controller.allowDesktopConfig = checked
}
}
Label {
text: qsTr("Allow desktop-config if launched application is not focused")
leftPadding: 32
topPadding: -8
}
}
}
}
Item {
width: 1
height: 4
}
Row {
CheckBox {
id: realDeviceIds
text: qsTr("Use real device (USB)-IDs")
checked: shortcutInfo.realDeviceIds
onCheckedChanged: shortcutInfo.realDeviceIds = checked
}
RoundButton {
onClicked: () => {
helpInfoDialog.titleText = qsTr("Use real device (USB)-IDs")
helpInfoDialog.text =
qsTr("Only enable if input's are not recognized by the game")
+ "\n"
+ qsTr("If enabled, device-hiding won't work.\nUse the \"Max. Controller count\" setting!")
helpInfoDialog.open()
}
Row {
spacing: 16
width: parent.width
RPane {
width: parent.width / 2 - 8
height: 248
radius: 4
Material.elevation: 32
bgOpacity: 0.97
Column {
spacing: 0
width: parent.width
Row {
CheckBox {
id: hideDevices
text: qsTr("Hide (Real) Controllers")
checked: shortcutInfo.devices.hideDevices
onCheckedChanged: shortcutInfo.devices.hideDevices = checked
}
RoundButton {
onClicked: () => {
helpInfoDialog.titleText = qsTr("Hide (Real) Controllers")
helpInfoDialog.text =
qsTr("Hides real game controllers from the system\nThis may prevent doubled inputs")
+ "\n"
+ qsTr("You can change this setting and which devices are hidden in the GlosSI 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
}
}
}
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
}
}
}
Item {
width: 1
height: 4
}
Row {
CheckBox {
id: emulateDS4
text: qsTr("Emulate DS4")
checked: shortcutInfo.emulateDS4
onCheckedChanged: shortcutInfo.emulateDS4 = checked
}
RoundButton {
onClicked: () => {
helpInfoDialog.titleText = qsTr("Emulate DS4")
helpInfoDialog.text =
qsTr("Instead of X360 Pad")
+ "\n"
+ qsTr("Disable \"Playstation Configuration support\" in Steam")
helpInfoDialog.open()
Row {
CheckBox {
id: realDeviceIds
text: qsTr("Use real device (USB)-IDs")
checked: shortcutInfo.devices.realDeviceIds
onCheckedChanged: shortcutInfo.devices.realDeviceIds = checked
}
RoundButton {
onClicked: () => {
helpInfoDialog.titleText = qsTr("Use real device (USB)-IDs")
helpInfoDialog.text =
qsTr("Only enable if input's are not recognized by the game")
+ "\n"
+ qsTr("If enabled, device-hiding won't work.\nUse the \"Max. Controller count\" setting!")
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: emulateDS4
text: qsTr("Emulate DS4")
checked: shortcutInfo.controller.emulateDS4 || false
onCheckedChanged: shortcutInfo.controller.emulateDS4 = checked
}
RoundButton {
onClicked: () => {
helpInfoDialog.titleText = qsTr("Emulate DS4")
helpInfoDialog.text =
qsTr("Emulates a DS4 instead of X360 Pad")
+ "\n"
qsTr("for usage with, for example, PSNow")
+ "\n"
+ qsTr("If enabled you have to disable \"Playstation Configuration support\" in Steam")
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
}
}
}
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 {
leftPadding: 16
Label {
text: qsTr("Max. emulated controllers")
topPadding: 16
}
SpinBox {
id: maxControllersSpinBox
width: 128
editable: true
value: shortcutInfo.controller.maxControllers
from: 0
to: 4
onValueChanged: shortcutInfo.controller.maxControllers = value
}
RoundButton {
onClicked: () => {
helpInfoDialog.titleText = qsTr("Max. emulated controllers")
helpInfoDialog.text =
qsTr("GlosSI will only provide [NUMBER] of controllers")
+ "\n"
+ qsTr("Required to set to actually connected controller count when using \"real device IDs\" ")
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 {
leftPadding: 16
Label {
text: qsTr("Max. emulated controllers")
topPadding: 16
}
SpinBox {
id: maxControllersSpinBox
width: 128
value: 4
from: 0
to: 4
onValueChanged: shortcutInfo.maxControllers = value
RPane {
width: parent.width / 2 - 8
height: 248
radius: 4
Material.elevation: 32
bgOpacity: 0.97
Column {
spacing: 0
width: parent.width
Row {
CheckBox {
id: windowMode
text: qsTr("Steam/GlosSI overlay as separate window")
checked: shortcutInfo.window.windowMode
onCheckedChanged: shortcutInfo.window.windowMode = checked
}
RoundButton {
onClicked: () => {
helpInfoDialog.titleText = qsTr("Steam/GlosSI overlay as separate window")
helpInfoDialog.text =
qsTr("Doesn't show overlay on top, but as separate window")
+ "\n"
+ qsTr("Use if blackscreen-issues are encountered.")
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: disableOverlayCheckbox
text: qsTr("Disable Steam/GlosSI overlay")
checked: shortcutInfo.window.disableOverlay
onCheckedChanged: shortcutInfo.window.disableOverlay = checked
}
RoundButton {
onClicked: () => {
helpInfoDialog.titleText = qsTr("Disable Steam/GlosSI overlay")
helpInfoDialog.text =
qsTr("Only controller emulation - No extra window")
+ "\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 {
leftPadding: 16
Label {
text: qsTr("GlosSI-Overlay scale")
topPadding: 16
}
SpinBox {
id: scaleSpinBox
width: 172
from: -100
value: shortcutInfo.window.scale * 100 || 0
to: 350
stepSize: 10
editable: true
property int decimals: 2
property real realValue: value / 100
validator: DoubleValidator {
bottom: Math.min(scaleSpinBox.from, scaleSpinBox.to)
top: Math.max(scaleSpinBox.from, scaleSpinBox.to)
}
textFromValue: function(value, locale) {
return Number(value / 100).toLocaleString(locale, 'f', scaleSpinBox.decimals)
}
valueFromText: function(text, locale) {
return Number.fromLocaleString(locale, text) * 100
}
onValueChanged: function() {
if (value <= 0) {
shortcutInfo.window.scale = null
return
}
shortcutInfo.window.scale = value / 100
}
}
RoundButton {
onClicked: () => {
helpInfoDialog.titleText = qsTr("GloSI-Overlay scaling")
helpInfoDialog.text =
qsTr("Scales the elements of the GlosSI-Overlay (not Steam Overlay)")
+ "\n"
+ qsTr(" <= 0.0 to use auto-detection")
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 {
leftPadding: 16
Label {
text: qsTr("Max. Overlay FPS")
topPadding: 16
}
SpinBox {
id: maxFPSSpinBox
width: 172
from: -1
value: shortcutInfo.window.maxFps || 0
to: 244
stepSize: 5
editable: true
onValueChanged: function() {
if (value <= 0) {
shortcutInfo.window.maxFps = null
return
}
shortcutInfo.window.maxFps = value
}
}
RoundButton {
onClicked: () => {
helpInfoDialog.titleText = qsTr("Max. Overlay FPS")
helpInfoDialog.text =
qsTr("Restricts the FPS of the overlay to the given value")
+ "\n"
+ qsTr(" <= 0.0 to use screen refresh rate")
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
}
}
}
}
}
}
}
RPane {
width: parent.width / 2 - 8
height: 264
radius: 4
Material.elevation: 32
bgOpacity: 0.97
Column {
spacing: 2
RPane {
width: parent.width
CheckBox {
id: windowMode
text: qsTr("Steam/GlosSI overlay as separate window")
checked: shortcutInfo.windowMode
onCheckedChanged: shortcutInfo.windowMode = checked
}
Label {
text: qsTr("Doesn't show overlay on top, but as separate window")
wrapMode: Text.WordWrap
width: parent.width
leftPadding: 32
topPadding: -8
}
Label {
text: qsTr("Use if blackscreen-issues are encountered.")
wrapMode: Text.WordWrap
width: parent.width
leftPadding: 32
}
Item {
width: 1
height: 4
}
CheckBox {
id: disableOverlayCheckbox
text: qsTr("Disable Steam/GlosSI overlay")
checked: shortcutInfo.disableOverlay
onCheckedChanged: shortcutInfo.disableOverlay = checked
}
Label {
text: qsTr("Only controller emulation - No extra window")
wrapMode: Text.WordWrap
width: parent.width
leftPadding: 32
topPadding: -8
radius: 4
Material.elevation: 32
bgOpacity: 0.97
Column {
spacing: 4
Row {
Row {
CheckBox {
id: extendedLogging
text: qsTr("Extended Logging")
checked: shortcutInfo.extendedLogging
onCheckedChanged: shortcutInfo.extendedLogging = checked
}
// RoundButton {
// onClicked: () => {
// helpInfoDialog.titleText = qsTr("Hide (Real) Controllers")
// helpInfoDialog.text =
// qsTr("Hides real game controllers from the system\nThis may prevent doubled inputs")
// + "\n"
// + qsTr("You can change this setting and which devices are hidden in the GlosSI 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 {
id: bottomspacing
width: 1

@ -0,0 +1,111 @@
/*
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.
*/
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
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
clip: true
Column {
spacing: 4
bottomPadding: 96
Label {
id: titlelabel
text: qsTr("Steam Input Xbox support disabled")
font.pixelSize: 24
font.bold: true
}
Item {
height: 32
}
Label {
text: qsTr("Please enable \"Xbox configuration support\" in Steams controller settings.\n\nGlosSI cannot function properly with this setting disabled\n\nEnable now?")
wrapMode: Text.WordWrap
width: parent.width
}
Row {
anchors.right: parent.right
anchors.topMargin: 16
anchors.rightMargin: 2
spacing: 8
Button {
id: noBtn
text: qsTr("No")
onClicked: dlg.close()
}
Button {
id: yesBtn
text: qsTr("Yes")
onClicked: function() {
uiModel.enableSteamInputXboxSupport();
dlg.close();
steamChangedDialog2.open();
}
}
}
}
InfoDialog {
id: steamChangedDialog2
titleText: qsTr("Steam config changed!")
text: qsTr("Please restart Steam to reload your changes!")
onConfirmed: function (callback) {
callback();
}
}
}
}

@ -0,0 +1,92 @@
/*
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.
*/
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
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
clip: true
Column {
spacing: 4
bottomPadding: 24
Label {
id: titlelabel
text: qsTr("Could not detect Steam")
font.pixelSize: 24
font.bold: true
}
Item {
height: 24
}
Label {
text: qsTr("Please make sure that Steam is running and you are logged in.")
wrapMode: Text.WordWrap
width: parent.width
}
Button {
anchors.right: parent.right
anchors.top: listview.bottom
anchors.topMargin: 16
anchors.rightMargin: 2
text: qsTr("Ok")
onClicked: dlg.close()
}
}
}
}

@ -45,6 +45,16 @@ Window {
property bool steamShortcutsChanged: false
Component.onCompleted: function() {
if (!uiModel.foundSteam) {
steamNotFoundDialog.open();
return;
}
if (!uiModel.steamInputXboxSupportEnabled) {
steamXboxDisabledDialog.open();
}
}
Image {
anchors.top: parent.top
anchors.left: parent.left
@ -55,10 +65,18 @@ Window {
opacity: 0.033
}
SteamNotFoundDialog {
id: steamNotFoundDialog
}
SteamInputXboxDisabledDialog {
id: steamXboxDisabledDialog
}
InfoDialog {
id: steamChangedDialog
titleText: qsTr("Attention!")
text: qsTr("Please restart Steam to reload your changes!")
titleText: qsTr("Steam shortcuts changed!")
text: qsTr("Please restart Steam to reload your changes")
onConfirmed: function (callback) {
callback();
}

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24" width="24" fill="#FFFFFF"><path d="m12 15.375-6-6 1.4-1.4 4.6 4.6 4.6-4.6 1.4 1.4Z"/></svg>

After

Width:  |  Height:  |  Size: 143 B

@ -1,3 +1,4 @@
BasedOnStyle: LLVM
UseTab: false
IndentWidth: 4
BreakBeforeBraces: "Stroustrup"

@ -77,6 +77,9 @@ 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("Child process with PID \"{}\" died", pid);

@ -102,9 +102,19 @@ void HidHide::hideDevices(const std::filesystem::path& steam_path)
whitelist.push_back(path);
}
}
if (Settings::extendedLogging) {
std::ranges::for_each(whitelist, [](const auto& exe) {
spdlog::trace(L"Whitelisted executable: {}", exe);
});
}
setAppWhiteList(whitelist);
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();
for (const auto& dev : avail_devices_) {
@ -112,11 +122,11 @@ void HidHide::hideDevices(const std::filesystem::path& steam_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)) {
if (!(dev.vendor_id == 0x28de && (dev.product_id == 0x11FF || dev.product_id == 0x028E))) {
if (!dev.device_instance_path.empty()) {
blacklisted_devices_.push_back(dev.device_instance_path);
}
if (!dev.device_instance_path.empty()) {
if (!dev.base_container_device_instance_path.empty()) {
blacklisted_devices_.push_back(dev.base_container_device_instance_path);
}
}
@ -127,6 +137,11 @@ void HidHide::hideDevices(const std::filesystem::path& steam_path)
setBlacklistDevices(blacklisted_devices_);
setActive(true);
spdlog::info("Hid Gaming Devices; Enabling Overlay element...");
if (Settings::extendedLogging) {
std::ranges::for_each(blacklisted_devices_, [](const auto& dev) {
spdlog::trace(L"Blacklisted device: {}", dev);
});
}
enableOverlayElement();
}
closeCtrlDevice();
@ -148,7 +163,7 @@ void HidHide::UnPatchValveHooks()
// need to load addresses that way.. Otherwise we land before some jumps...
if (const auto setupapidll = GetModuleHandle(L"setupapi.dll")) {
UnPatchHook("SetupDiEnumDeviceInfo", setupapidll);
//UnPatchHook("SetupDiGetClassDevsW", setupapidll);
UnPatchHook("SetupDiGetClassDevsW", setupapidll);
}
if (const auto hiddll = GetModuleHandle(L"hid.dll")) {
for (const auto& name : ORIGINAL_BYTES | std::views::keys) {
@ -182,56 +197,70 @@ void HidHide::UnPatchHook(const std::string& name, HMODULE module)
void HidHide::enableOverlayElement()
{
Overlay::AddOverlayElem([this](bool window_has_focus) {
if (window_has_focus && (overlay_elem_clock_.getElapsedTime().asSeconds() > OVERLAY_ELEM_REFRESH_INTERVAL_S_)) {
openCtrlDevice();
bool hidehide_state_store = hidhide_active_;
if (hidhide_active_) {
setActive(false);
}
avail_devices_ = GetHidDeviceList();
blacklisted_devices_ = getBlackListDevices();
if (hidehide_state_store) {
setActive(true);
}
closeCtrlDevice();
overlay_elem_clock_.restart();
}
ImGui::SetNextWindowPos({650, 100}, ImGuiCond_FirstUseEver);
ImGui::SetNextWindowSizeConstraints({400, 270}, {1000, 1000});
ImGui::Begin("Hidden Devices");
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)) {
Overlay::AddOverlayElem([this](bool window_has_focus, ImGuiID dockspace_id) {
ImGui::SetNextWindowDockID(dockspace_id, ImGuiCond_FirstUseEver);
if (ImGui::Begin("Hidden Devices")) {
if (window_has_focus && (overlay_elem_clock_.getElapsedTime().asSeconds() > OVERLAY_ELEM_REFRESH_INTERVAL_S_)) {
// UnPatchValveHooks();
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);
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) {
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);
}
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());
}
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_);
});
ImGui::EndChild();
if (ImGui::Checkbox("Devices Hidden", &hidhide_active_)) {
openCtrlDevice();
setActive(hidhide_active_);
closeCtrlDevice();
}
});
ImGui::EndChild();
if (ImGui::Checkbox("Devices Hidden", &hidhide_active_)) {
openCtrlDevice();
setActive(hidhide_active_);
closeCtrlDevice();
}
ImGui::End();
});
@ -316,6 +345,9 @@ void HidHide::setActive(bool active)
return;
}
hidhide_active_ = active;
if (Settings::extendedLogging) {
spdlog::debug("HidHide State set to {}", active);
}
}
DWORD HidHide::getRequiredOutputBufferSize(IOCTL_TYPE type) const

@ -53,9 +53,8 @@ void InputRedirector::run()
max_controller_count_ = Settings::controller.maxControllers;
use_real_vid_pid_ = Settings::devices.realDeviceIds;
#ifdef _WIN32
Overlay::AddOverlayElem([this](bool window_has_focus) {
ImGui::SetNextWindowPos({650, 450}, ImGuiCond_FirstUseEver);
ImGui::SetNextWindowSizeConstraints({400, 270}, {1000, 1000});
Overlay::AddOverlayElem([this](bool window_has_focus, ImGuiID dockspace_id) {
ImGui::SetNextWindowDockID(dockspace_id, ImGuiCond_FirstUseEver);
ImGui::Begin("Controller Emulation");
int countcopy = max_controller_count_;
ImGui::Text("Max. controller count");
@ -163,7 +162,11 @@ void InputRedirector::runLoop()
// Multiple controllers can be worked around with by setting max count.
if (!use_real_vid_pid_) {
vigem_target_set_vid(vt_pad_[i], 0x28de); //VALVE_DIRECTINPUT_GAMEPAD_VID
// vigem_target_set_pid(vt_pad_[i], 0x11FF); //VALVE_DIRECTINPUT_GAMEPAD_PID
//vigem_target_set_pid(vt_pad_[i], 0x11FF); //VALVE_DIRECTINPUT_GAMEPAD_PID
vigem_target_set_pid(vt_pad_[i], 0x028E); // XBOX 360 Controller
} else {
vigem_target_set_vid(vt_pad_[i], 0x045E); // MICROSOFT
vigem_target_set_pid(vt_pad_[i], 0x028E); // XBOX 360 Controller
}
// TODO: MAYBE!: In a future version, use something like OpenXInput
//and filter out emulated controllers to support a greater amount of controllers simultaneously
@ -178,7 +181,11 @@ void InputRedirector::runLoop()
}
}
if (target_add_res == VIGEM_ERROR_NONE) {
spdlog::info("Plugged in controller {}, {}", i, vigem_target_get_index(vt_pad_[i]));
spdlog::info("Plugged in controller {}, {}; VID: {:x}; PID: {:x}",
i,
vigem_target_get_index(vt_pad_[i]),
vigem_target_get_vid(vt_pad_[i]),
vigem_target_get_pid(vt_pad_[i]));
if (Settings::controller.emulateDS4) {
const auto callback_register_res = vigem_target_ds4_register_notification(

@ -21,6 +21,7 @@ limitations under the License.
#include <codecvt>
#include "Roboto.h"
#include "Settings.h"
Overlay::Overlay(
sf::RenderWindow& window,
@ -37,12 +38,14 @@ Overlay::Overlay(
ImGuiIO& io = ImGui::GetIO();
io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;
io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad;
io.ConfigFlags |= ImGuiConfigFlags_DockingEnable;
io.Fonts->Clear(); // clear fonts if you loaded some before (even if only default one was loaded)
auto fontconf = ImFontConfig{};
fontconf.FontDataOwnedByAtlas = false;
io.Fonts->AddFontFromMemoryTTF(Roboto_Regular_ttf.data(), Roboto_Regular_ttf.size(), 24, &fontconf);
ImGui::SFML::UpdateFontTexture(); // important call: updates font texture
ImGui::SFML::UpdateFontTexture();
#ifdef _WIN32
auto config_path = std::filesystem::temp_directory_path()
@ -55,6 +58,7 @@ Overlay::Overlay(
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_ = std::wstring_convert<std::codecvt_utf8<wchar_t>>().to_bytes(config_path.wstring());
io.IniFilename = config_file_name_.data();
@ -151,14 +155,31 @@ void Overlay::update()
{
ImGui::SFML::Update(window_, update_clock_.restart());
showLogs();
showLogs(0);
if (enabled_ || force_enable_) {
// Create a DockSpace node where any window can be docked
ImGui::SetNextWindowSize({ImGui::GetMainViewport()->Size.x * 0.6f, ImGui::GetMainViewport()->Size.y * 0.7f}, ImGuiCond_FirstUseEver);
ImGui::SetNextWindowPos({ImGui::GetMainViewport()->Size.x * 0.25f, 100 }, ImGuiCond_FirstUseEver);
ImGui::Begin("GlosSI Settings");
if (Settings::settings_path_ != "") {
if (ImGui::Button("Save shortcut settings", {256, 32})) {
Settings::StoreSettings();
}
}
ImGuiID dockspace_id = ImGui::GetID("GlosSI-DockSpace");
ImGui::DockSpace(dockspace_id);
window_.clear(sf::Color(0, 0, 0, 128)); // make window slightly dim screen with overlay
std::ranges::for_each(OVERLAY_ELEMS_, [this](const auto& elem) {
elem.second(window_.hasFocus());
std::ranges::for_each(OVERLAY_ELEMS_, [this, &dockspace_id](const auto& elem) {
elem.second(window_.hasFocus(), dockspace_id);
});
ImGui::End();
// ImGui::ShowDemoWindow();
if (closeButton()) {
@ -186,7 +207,7 @@ 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)>& elem_fn)
int Overlay::AddOverlayElem(const std::function<void(bool window_has_focus, ImGuiID dockspace_id)>& elem_fn)
{
OVERLAY_ELEMS_.insert({overlay_element_id_, elem_fn});
// keep this non confusing, but longer...
@ -200,20 +221,22 @@ void Overlay::RemoveOverlayElem(int id)
OVERLAY_ELEMS_.erase(id);
}
void Overlay::showLogs()
void Overlay::showLogs(ImGuiID dockspace_id)
{
std::vector<Log> logs;
if (!enabled_ && !log_expanded_) {
return;
}
bool logs_contain_warn_or_worse = false;
if (enabled_) {
logs = LOG_MSGS_;
}
else {
std::ranges::copy_if(LOG_MSGS_,
std::back_inserter(logs),
[](const auto& log) {
return (
[&logs_contain_warn_or_worse](const auto& log) {
const auto res = (
log.time.time_since_epoch() + std::chrono::seconds(
LOG_RETENTION_TIME_) >
std::chrono::system_clock::now().time_since_epoch())
@ -221,9 +244,13 @@ void Overlay::showLogs()
&& (log.level > spdlog::level::debug)
#endif
;
if (res && log.level > spdlog::level::warn) {
logs_contain_warn_or_worse = true;
}
return res;
});
}
if (logs.empty())
if (logs.empty() || ( !enabled_ && !logs_contain_warn_or_worse && time_since_start_clock_.getElapsedTime().asSeconds() > HIDE_NORMAL_LOGS_AFTER_S))
return;
ImGui::SetNextWindowSizeConstraints({150, 150}, {1000, window_.getSize().y - 250.f});
if (!enabled_) {
@ -232,6 +259,9 @@ void Overlay::showLogs()
ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoTitleBar);
}
else {
//ImGui::SetNextWindowDockID(dockspace_id, ImGuiCond_FirstUseEver);
ImGui::SetNextWindowSize({ImGui::GetMainViewport()->Size.x * 0.2f, ImGui::GetMainViewport()->Size.y * 0.7f}, ImGuiCond_FirstUseEver);
ImGui::SetNextWindowPos({ImGui::GetMainViewport()->Size.x * 0.05f, 100}, ImGuiCond_FirstUseEver);
log_expanded_ = ImGui::Begin("Log");
}
if (log_expanded_) {

@ -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)>& elem_fn);
static int AddOverlayElem(const std::function<void(bool window_has_focus, ImGuiID dockspace_id)>& elem_fn);
static void RemoveOverlayElem(int id);
private:
@ -47,11 +47,12 @@ class Overlay {
bool enabled_ = true;
std::function<void()> on_close_;
std::function<void()> trigger_state_change_;
void showLogs();
void showLogs(ImGuiID dockspace_id);
bool closeOverlayButton() const;
[[nodiscard]] bool closeButton() const;
bool force_enable_ = false;
bool log_expanded_ = true;
sf::Clock time_since_start_clock_;
struct Log {
std::chrono::system_clock::time_point time;
@ -60,9 +61,10 @@ class Overlay {
};
static inline std::vector<Log> LOG_MSGS_;
static constexpr int LOG_RETENTION_TIME_ = 5;
static constexpr int HIDE_NORMAL_LOGS_AFTER_S = 20;
static inline int overlay_element_id_ = 0;
static inline std::map<int, std::function<void(bool window_has_focus)>> OVERLAY_ELEMS_;
static inline std::map<int, std::function<void(bool window_has_focus, ImGuiID dockspace_id)>> OVERLAY_ELEMS_;
#ifdef _WIN32
std::string config_file_name_;

@ -12,35 +12,41 @@ static int current_priority = HIGH_PRIORITY_CLASS;
inline void init()
{
SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS);
spdlog::trace("Set process priority to HIGH_PRIORITY_CLASS");
Overlay::AddOverlayElem([](bool window_has_focus) {
ImGui::SetNextWindowPos({913, 418}, ImGuiCond_FirstUseEver);
ImGui::SetNextWindowSizeConstraints({170, 325}, {1000, 1000});
Overlay::AddOverlayElem([](bool window_has_focus, ImGuiID dockspace_id) {
ImGui::SetNextWindowDockID(dockspace_id, ImGuiCond_FirstUseEver);
ImGui::Begin("Process Priority");
ImGui::Text("Might help with input-lag or bad game performance");
if (ImGui::RadioButton("Realtime", current_priority == REALTIME_PRIORITY_CLASS)) {
SetPriorityClass(GetCurrentProcess(), REALTIME_PRIORITY_CLASS);
current_priority = REALTIME_PRIORITY_CLASS;
spdlog::trace("Set process priority to REALTIME_PRIORITY_CLASS");
}
if (ImGui::RadioButton("High", current_priority == HIGH_PRIORITY_CLASS)) {
SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS);
current_priority = HIGH_PRIORITY_CLASS;
spdlog::trace("Set process priority to HIGH_PRIORITY_CLASS");
}
if (ImGui::RadioButton("Above Normal", current_priority == ABOVE_NORMAL_PRIORITY_CLASS)) {
SetPriorityClass(GetCurrentProcess(), ABOVE_NORMAL_PRIORITY_CLASS);
current_priority = ABOVE_NORMAL_PRIORITY_CLASS;
spdlog::trace("Set process priority to ABOVE_NORMAL_PRIORITY_CLASS");
}
if (ImGui::RadioButton("Normal", current_priority == NORMAL_PRIORITY_CLASS)) {
SetPriorityClass(GetCurrentProcess(), NORMAL_PRIORITY_CLASS);
current_priority = NORMAL_PRIORITY_CLASS;
spdlog::trace("Set process priority to NORMAL_PRIORITY_CLASS");
}
if (ImGui::RadioButton("Below Normal", current_priority == BELOW_NORMAL_PRIORITY_CLASS)) {
SetPriorityClass(GetCurrentProcess(), BELOW_NORMAL_PRIORITY_CLASS);
current_priority = BELOW_NORMAL_PRIORITY_CLASS;
spdlog::trace("Set process priority to BELOW_NORMAL_PRIORITY_CLASS");
}
if (ImGui::RadioButton("Low", current_priority == IDLE_PRIORITY_CLASS)) {
SetPriorityClass(GetCurrentProcess(), IDLE_PRIORITY_CLASS);
current_priority = IDLE_PRIORITY_CLASS;
spdlog::trace("Set process priority to IDLE_PRIORITY_CLASS");
}
ImGui::End();
});

@ -51,8 +51,8 @@ END
//
VS_VERSION_INFO VERSIONINFO
FILEVERSION 0,0,7,1018000020006
PRODUCTVERSION 0,0,7,1018000020006
FILEVERSION 0,0,8,1031000051035
PRODUCTVERSION 0,0,8,1031000051035
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L
@ -69,12 +69,12 @@ BEGIN
BEGIN
VALUE "CompanyName", "Peter Repukat - FlatspotSoftware"
VALUE "FileDescription", "GlosSI - SteamTarget"
VALUE "FileVersion", "0.0.7.1-18-g0f2bac6"
VALUE "FileVersion", "0.0.8.1-31-gda51d35"
VALUE "InternalName", "GlosSITarget"
VALUE "LegalCopyright", "Copyright (C) 2021 Peter Repukat - FlatspotSoftware"
VALUE "OriginalFilename", "GlosSITarget.exe"
VALUE "ProductName", "GlosSI"
VALUE "ProductVersion", "0.0.7.1-18-g0f2bac6"
VALUE "ProductVersion", "0.0.8.1-31-gda51d35"
END
END
BLOCK "VarFileInfo"
@ -168,6 +168,234 @@ IDI_ICON1 ICON "GloSC_Icon.ico"

@ -47,11 +47,15 @@ inline struct Window {
} window;
inline struct Controller {
int maxControllers = 4;
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) {
@ -87,14 +91,9 @@ inline void Parse(std::wstring arg1)
spdlog::error(L"Couldn't open settings file {}", path.wstring());
return;
}
const auto json = nlohmann::json::parse(json_file);
if (json["version"] != 1) { // TODO: versioning stuff
spdlog::warn("Config version doesn't match application version.");
}
settings_path_ = path;
// TODO: make this as much generic as fits in about the same amount of code if one would parse every value separately.
auto safeParseValue = [](const auto& object, const auto& key, auto& value) {
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;
@ -102,10 +101,10 @@ inline void Parse(std::wstring arg1)
value = object[key];
}
catch (const nlohmann::json::exception& e) {
spdlog::error("Err parsing \"{}\"; {}", key, e.what());
spdlog::warn("Err parsing \"{}\"; {}", key, e.what());
}
catch (const std::exception& e) {
spdlog::error("Err parsing \"{}\"; {}", key, e.what());
spdlog::warn("Err parsing \"{}\"; {}", key, e.what());
}
};
@ -118,6 +117,15 @@ inline void Parse(std::wstring arg1)
}
};
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.
if (auto launchconf = json["launch"]; launchconf.is_object()) {
safeParseValue(launchconf, "launch", launch.launch);
safeWStringParse(launchconf, "launchPath", launch.launchPath);
@ -144,13 +152,47 @@ inline void Parse(std::wstring arg1)
safeParseValue(controllerConf, "emulateDS4", controller.emulateDS4);
}
safeParseValue(json, "extendedLogging", extendedLogging);
json_file.close();
spdlog::debug(L"Read config file \"{}\"", path.wstring());
// c++ is stupid...
spdlog::debug(L"Read config file \"{}\"; config: {}", path.wstring(), std::filesystem::path(json.dump()).wstring());
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

@ -17,6 +17,8 @@ limitations under the License.
#include <spdlog/spdlog.h>
#include "Settings.h"
#ifdef _WIN32
#define NOMINMAX
#include <Windows.h>
@ -44,6 +46,11 @@ void SteamOverlayDetector::update()
// okey to use nullptr as hwnd. get EVERY message
if (PeekMessage(&msg, nullptr, 0, 0, PM_NOREMOVE)) {
// filter out some messages as not all get altered by steam...
if (Settings::extendedLogging && msg.message != 512 && msg.message != 5374) {
spdlog::trace("PeekMessage: Window msg: {}", msg.message);
}
if (msg.message < 1000 && msg.message > 0) {
return;
}

@ -181,6 +181,9 @@ void SteamTarget::toggleGlossiOverlay()
void SteamTarget::focusWindow(WindowHandle hndl)
{
if (reinterpret_cast<uint64_t>(hndl) == 0) {
return;
}
#ifdef _WIN32
if (hndl == target_window_handle_) {
spdlog::debug("Bring own window to foreground");

@ -50,9 +50,9 @@ TargetWindow::TargetWindow(
{
createWindow(Settings::window.windowMode);
Overlay::AddOverlayElem([this](bool window_has_focus) {
Overlay::AddOverlayElem([this](bool window_has_focus, ImGuiID dockspace_id) {
ImGui::SetNextWindowDockID(dockspace_id, ImGuiCond_FirstUseEver);
bool windowed_copy = windowed_;
ImGui::SetNextWindowPos({window_.getSize().x - 370.f, 100}, ImGuiCond_FirstUseEver);
ImGui::Begin("Window mode");
if (ImGui::Checkbox("Window mode", &windowed_copy)) {
toggle_window_mode_after_frame_ = true;
@ -254,14 +254,26 @@ void TargetWindow::createWindow(bool window_mode)
#ifdef _WIN32
// For some completely odd reason, the Background becomes black when enabled dpi-awareness and making the window desktop-size.
// Scaling down by 1px each direction is barely noticeable and works.
// Due to some other issue, the (Steam) overlay might get blurred when doing this
// as a workaround, start in full size, and scale down later...
spdlog::info("Creating Overlay window (Borderless Fullscreen)...");
window_.create(sf::VideoMode(desktop_mode.width - 1, desktop_mode.height - 1, 32), "GlosSITarget", sf::Style::None);
window_.create(sf::VideoMode(desktop_mode.width -1, desktop_mode.height -1, 32), "GlosSITarget", sf::Style::None);
// get size of all monitors combined
const auto screenWidth = GetSystemMetrics(SM_CXVIRTUALSCREEN);
const auto screenHeight = GetSystemMetrics(SM_CYVIRTUALSCREEN);
spdlog::debug("Full screen size: {}x{}", screenWidth, screenHeight);
spdlog::debug("Primary monitor size: {}x{}", desktop_mode.width, desktop_mode.height);
#else
window_.create(desktop_mode, "GlosSITarget", sf::Style::None);
#endif
windowed_ = false;
}
window_.setActive(true);
spdlog::debug("Window position: {}x{}", window_.getPosition().x, window_.getPosition().y);
#ifdef _WIN32
HWND hwnd = window_.getSystemHandle();
@ -320,6 +332,9 @@ void TargetWindow::createWindow(bool window_mode)
else {
spdlog::warn("Not applying too low screen scale setting");
}
// window_.setSize({desktop_mode.width - 1, desktop_mode.height - 1 });
on_window_changed_();
#ifdef _WIN32

@ -60,10 +60,8 @@ inline void EnableUwpOverlay()
inline void AddUwpOverlayOvWidget()
{
Overlay::AddOverlayElem([](bool window_has_focus) {
ImGui::SetNextWindowPos({1200, 250}, ImGuiCond_FirstUseEver);
ImGui::SetNextWindowSizeConstraints({170, 325}, {1000, 1000});
ImGui::SetNextWindowCollapsed(true, ImGuiCond_FirstUseEver);
Overlay::AddOverlayElem([](bool window_has_focus, ImGuiID dockspace_id) {
ImGui::SetNextWindowDockID(dockspace_id, ImGuiCond_FirstUseEver);
ImGui::Begin("UWP-Overlay");
ImGui::Text("To enable the overlay on top of \"fullscreen\" UWP-Apps,");
ImGui::Text("a .dll has to be injected into explorer.exe");

@ -78,11 +78,11 @@
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LinkIncremental>true</LinkIncremental>
<IncludePath>..\deps\subhook;$(IncludePath)</IncludePath>
<IncludePath>..\deps\subhook;..\deps\spdlog\include;$(IncludePath)</IncludePath>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LinkIncremental>false</LinkIncremental>
<IncludePath>..\deps\subhook;$(IncludePath)</IncludePath>
<IncludePath>..\deps\subhook;..\deps\spdlog\include;$(IncludePath)</IncludePath>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
@ -126,6 +126,7 @@
<ConformanceMode>true</ConformanceMode>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
<LanguageStandard>stdcpp20</LanguageStandard>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
@ -143,6 +144,7 @@
<ConformanceMode>true</ConformanceMode>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
<LanguageStandard>stdcpp20</LanguageStandard>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>

@ -47,8 +47,15 @@ There are two (known to me, at time of writing) ways to get a working overlay fo
#define SUBHOOK_STATIC
#include <atomic>
#include <filesystem>
#include <subhook.h>
#include <spdlog/sinks/basic_file_sink.h>
#include <spdlog/sinks/stdout_color_sinks.h>
#include <spdlog/spdlog.h>
#include <fstream>
enum ZBID
{
ZBID_DEFAULT = 0,
@ -71,6 +78,7 @@ enum ZBID
ZBID_LOCK = 17,
ZBID_ABOVELOCK_UX = 18,
};
typedef BOOL(WINAPI* fSetWindowBand)(HWND hWnd, HWND hwndInsertAfter, DWORD dwBand);
@ -79,18 +87,22 @@ fSetWindowBand SetWindowBand;
std::atomic<bool> allow_exit = false;
std::atomic<ZBID> to_set_window_band = ZBID_SYSTEM_TOOLS;
BOOL WINAPI SetGlosSIWindowBand(HWND hWnd, HWND hwndInsertAfter, DWORD dwBand)
{
subhook::ScopedHookRemove remove(&SetWindowBandHook);
const auto glossi_hwnd = FindWindowA(nullptr, "GlosSITarget");
if (glossi_hwnd)
{
spdlog::info("Found GlosSI Window");
// Most window bands don't really seem to work.
// However, notification and system_tools does!
// use system tools, as that allows the steam overlay to be interacted with
// without UWP apps minimizing
SetWindowBand(glossi_hwnd, nullptr, ZBID_SYSTEM_TOOLS);
auto success = SetWindowBand(glossi_hwnd, nullptr, to_set_window_band);
allow_exit = true;
spdlog::info("Set GlosSI Window Band to {}; success: {}", static_cast<int>(to_set_window_band), success);
}
return SetWindowBand(hWnd, hwndInsertAfter, dwBand);
}
@ -102,7 +114,10 @@ DWORD WINAPI WaitThread(HMODULE hModule)
Sleep(10);
}
if (SetWindowBandHook.IsInstalled())
{
spdlog::debug("Uninstalling SetWindowBand hook");
SetWindowBandHook.Remove();
}
FreeLibraryAndExitThread(hModule, 0);
}
@ -113,17 +128,104 @@ BOOL APIENTRY DllMain( HMODULE hModule,
{
if (ul_reason_for_call == DLL_PROCESS_ATTACH)
{
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);
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("UWPOverlayEnabler loaded");
auto configPath = configDirPath;
configPath /= "UWPOverlayEnabler.cfg";
if (std::filesystem::exists(configPath))
{
std::ifstream config(configPath);
std::string line;
while (std::getline(config, line))
{
// github copilot, lol
// i take it!
if (line == "ZBID_DEFAULT")
to_set_window_band = ZBID_DEFAULT;
else if (line == "ZBID_DESKTOP")
to_set_window_band = ZBID_DESKTOP;
else if (line == "ZBID_UIACCESS")
to_set_window_band = ZBID_UIACCESS;
else if (line == "ZBID_IMMERSIVE_IHM")
to_set_window_band = ZBID_IMMERSIVE_IHM;
else if (line == "ZBID_IMMERSIVE_NOTIFICATION")
to_set_window_band = ZBID_IMMERSIVE_NOTIFICATION;
else if (line == "ZBID_IMMERSIVE_APPCHROME")
to_set_window_band = ZBID_IMMERSIVE_APPCHROME;
else if (line == "ZBID_IMMERSIVE_MOGO")
to_set_window_band = ZBID_IMMERSIVE_MOGO;
else if (line == "ZBID_IMMERSIVE_EDGY")
to_set_window_band = ZBID_IMMERSIVE_EDGY;
else if (line == "ZBID_IMMERSIVE_INACTIVEMOBODY")
to_set_window_band = ZBID_IMMERSIVE_INACTIVEMOBODY;
else if (line == "ZBID_IMMERSIVE_INACTIVEDOCK")
to_set_window_band = ZBID_IMMERSIVE_INACTIVEDOCK;
else if (line == "ZBID_IMMERSIVE_ACTIVEMOBODY")
to_set_window_band = ZBID_IMMERSIVE_ACTIVEMOBODY;
else if (line == "ZBID_IMMERSIVE_ACTIVEDOCK")
to_set_window_band = ZBID_IMMERSIVE_ACTIVEDOCK;
else if (line == "ZBID_IMMERSIVE_BACKGROUND")
to_set_window_band = ZBID_IMMERSIVE_BACKGROUND;
else if (line == "ZBID_IMMERSIVE_SEARCH")
to_set_window_band = ZBID_IMMERSIVE_SEARCH;
else if (line == "ZBID_GENUINE_WINDOWS")
to_set_window_band = ZBID_GENUINE_WINDOWS;
else if (line == "ZBID_IMMERSIVE_RESTRICTED")
to_set_window_band = ZBID_IMMERSIVE_RESTRICTED;
else if (line == "ZBID_SYSTEM_TOOLS")
to_set_window_band = ZBID_SYSTEM_TOOLS;
else if (line == "ZBID_LOCK")
to_set_window_band = ZBID_LOCK;
else if (line == "ZBID_ABOVELOCK_UX")
to_set_window_band = ZBID_ABOVELOCK_UX;
}
spdlog::info("Read window band from config: {}", static_cast<int>(to_set_window_band));
}
const auto hpath = LoadLibrary(L"user32.dll");
if (hpath)
{
spdlog::debug("Loaded user32.dll");
spdlog::debug("Installing SetWindowBand hook");
SetWindowBand = reinterpret_cast<fSetWindowBand>(GetProcAddress(hpath, "SetWindowBand"));
SetWindowBandHook.Install(GetProcAddress(hpath, "SetWindowBand"), &SetGlosSIWindowBand, subhook::HookFlags::HookFlag64BitOffset);
spdlog::debug("Creating wait thread");
CloseHandle(CreateThread(nullptr, 0, (LPTHREAD_START_ROUTINE)WaitThread, hModule, 0, nullptr));
} else
{
spdlog::error("Loaded user32.dll");
}
}
else if (ul_reason_for_call == DLL_PROCESS_DETACH) {
spdlog::info("unloading UWPOverlayEnabler");
if (SetWindowBandHook.IsInstalled())
{
spdlog::debug("Uninstalling SetWindowBand hook");
SetWindowBandHook.Remove();
}
}
return TRUE;
}

@ -1 +1 @@
Subproject commit 2816b31c8e777c2920e1f0881ce10c5c66e30c63
Subproject commit 59108a7f9a938911e1cc237003a396c886be85f8

2
deps/imgui vendored

@ -1 +1 @@
Subproject commit 9aae45eb4a05a5a1f96be1ef37eb503a12ceb889
Subproject commit 9cd9c2eff99877a3f10a7f9c2a3a5b9c15ea36c6
Loading…
Cancel
Save