main
Peter Repukat 3 years ago
parent b0e2aa4dc1
commit 3f250bdc22

@ -0,0 +1,7 @@
UseTab: false
IndentWidth: 4
BreakBeforeBraces: "Stroustrup"
AllowShortIfStatementsOnASingleLine: false
IndentCaseLabels: false
ColumnLimit: 0
PointerAlignment: "Left"

@ -123,8 +123,10 @@
<ItemGroup> <ItemGroup>
<ClCompile Include="main.cpp" /> <ClCompile Include="main.cpp" />
<ClCompile Include="UIModel.cpp" /> <ClCompile Include="UIModel.cpp" />
<None Include=".clang-format" />
<None Include="qml\AddSelectTypeDialog.qml" /> <None Include="qml\AddSelectTypeDialog.qml" />
<None Include="qml\FluentTextInput.qml" /> <None Include="qml\FluentTextInput.qml" />
<None Include="qml\InfoDialog.qml" />
<None Include="qml\RPane.qml" /> <None Include="qml\RPane.qml" />
<None Include="qml\ShortcutCards.qml" /> <None Include="qml\ShortcutCards.qml" />
<None Include="qml\ShortcutProps.qml" /> <None Include="qml\ShortcutProps.qml" />
@ -136,6 +138,7 @@
<QtMoc Include="UIModel.h" /> <QtMoc Include="UIModel.h" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="UWPFetch.h" />
<ClInclude Include="VDFParser.h" /> <ClInclude Include="VDFParser.h" />
<ClInclude Include="WinEventFilter.h" /> <ClInclude Include="WinEventFilter.h" />
</ItemGroup> </ItemGroup>

@ -51,12 +51,16 @@
<None Include="qml\ShortcutProps.qml"> <None Include="qml\ShortcutProps.qml">
<Filter>qml</Filter> <Filter>qml</Filter>
</None> </None>
<None Include="qml\FluentTextInput.qml">
<Filter>Source Files</Filter>
</None>
<None Include="qml\UWPSelectDialog.qml"> <None Include="qml\UWPSelectDialog.qml">
<Filter>qml</Filter> <Filter>qml</Filter>
</None> </None>
<None Include="qml\InfoDialog.qml">
<Filter>qml</Filter>
</None>
<None Include="qml\FluentTextInput.qml">
<Filter>qml</Filter>
</None>
<None Include=".clang-format" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<QtMoc Include="UIModel.h"> <QtMoc Include="UIModel.h">
@ -70,6 +74,9 @@
<ClInclude Include="VDFParser.h"> <ClInclude Include="VDFParser.h">
<Filter>Header Files</Filter> <Filter>Header Files</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="UWPFetch.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Xml Include="manifest.xml"> <Xml Include="manifest.xml">

@ -16,35 +16,21 @@ limitations under the License.
#include "UIModel.h" #include "UIModel.h"
#include <QDir> #include <QDir>
#include <QJsonDocument>
#ifdef _WIN32
#include <Windows.h>
#include <VersionHelpers.h>
#include <collection.h>
#include <appmodel.h>
#include <shlwapi.h>
#include <strsafe.h>
#include <windows.h>
#include <appxpackaging.h>
#pragma comment(lib, "Shlwapi.lib")
using namespace Windows::Management::Deployment;
using namespace Windows::Foundation::Collections;
#include <QGuiApplication> #include <QGuiApplication>
#include <QJsonDocument>
#ifdef _WIN32
#include <WinReg/WinReg.hpp> #include <WinReg/WinReg.hpp>
#endif
#ifdef _WIN32
#include "UWPFetch.h"
#endif #endif
UIModel::UIModel() : QObject(nullptr) UIModel::UIModel() : QObject(nullptr)
{ {
auto path = std::filesystem::temp_directory_path() auto path = std::filesystem::temp_directory_path()
.parent_path() .parent_path()
.parent_path() .parent_path()
.parent_path(); .parent_path();
path /= "Roaming"; path /= "Roaming";
path /= "GlosSI"; path /= "GlosSI";
@ -59,7 +45,6 @@ UIModel::UIModel() : QObject(nullptr)
parseShortcutVDF(); parseShortcutVDF();
readConfigs(); readConfigs();
} }
void UIModel::readConfigs() void UIModel::readConfigs()
@ -68,41 +53,38 @@ void UIModel::readConfigs()
auto entries = dir.entryList(QDir::Files, QDir::SortFlag::Name); auto entries = dir.entryList(QDir::Files, QDir::SortFlag::Name);
entries.removeIf([](const auto& entry) { entries.removeIf([](const auto& entry) {
return !entry.endsWith(".json"); return !entry.endsWith(".json");
}); });
std::for_each(entries.begin(), entries.end(), [this](const auto& name) std::for_each(entries.begin(), entries.end(), [this](const auto& name) {
{ auto path = config_path_;
auto path = config_path_; path /= config_dir_name_.toStdString();
path /= config_dir_name_.toStdString(); path /= name.toStdString();
path /= name.toStdString(); QFile file(path);
QFile file(path); if (!file.open(QIODevice::Text | QIODevice::ReadOnly)) {
if (!file.open(QIODevice::Text | QIODevice::ReadOnly)) // meh
{ return;
// meh }
return; const auto data = file.readAll();
} file.close();
const auto data = file.readAll(); const auto jsondoc = QJsonDocument::fromJson(data);
file.close(); const auto filejson = jsondoc.object();
const auto jsondoc = QJsonDocument::fromJson(data);
const auto filejson = jsondoc.object(); QJsonObject json;
json["version"] = filejson["version"];
QJsonObject json; json["icon"] = filejson["icon"];
json["version"] = filejson["version"]; json["launch"] = filejson["launch"]["launch"];
json["icon"] = filejson["icon"]; json["launchPath"] = filejson["launch"]["launchPath"];
json["launch"] = filejson["launch"]["launch"]; json["launchAppArgs"] = filejson["launch"]["launchAppArgs"];
json["launchPath"] = filejson["launch"]["launchPath"]; json["closeOnExit"] = filejson["launch"]["closeOnExit"];
json["launchAppArgs"] = filejson["launch"]["launchAppArgs"]; json["hideDevices"] = filejson["devices"]["hideDevices"];
json["closeOnExit"] = filejson["launch"]["closeOnExit"]; json["windowMode"] = filejson["window"]["windowMode"];
json["hideDevices"] = filejson["devices"]["hideDevices"]; json["maxFps"] = filejson["window"]["maxFps"];
json["windowMode"] = filejson["window"]["windowMode"]; json["scale"] = filejson["window"]["scale"];
json["maxFps"] = filejson["window"]["maxFps"];
json["scale"] = filejson["window"]["scale"]; json["name"] = QString(name).replace(QRegularExpression("\.json"), "");
json["name"] = QString(name).replace(QRegularExpression("\.json"), ""); targets_.append(json.toVariantMap());
});
targets_.append(json.toVariantMap());
});
emit targetListChanged(); emit targetListChanged();
} }
@ -152,12 +134,9 @@ void UIModel::deleteTarget(int index)
bool UIModel::isInSteam(QVariant shortcut) bool UIModel::isInSteam(QVariant shortcut)
{ {
const auto map = shortcut.toMap(); const auto map = shortcut.toMap();
for (auto& steam_shortcut : shortcuts_vdf_.shortcuts) for (auto& steam_shortcut : shortcuts_vdf_.shortcuts) {
{ if (map["name"].toString() == QString::fromStdString(steam_shortcut.appName.value)) {
if (map["name"].toString() == QString::fromStdString(steam_shortcut.appName.value)) if (QString::fromStdString(steam_shortcut.exe.value).toLower().contains("glossitarget.exe")) {
{
if (QString::fromStdString(steam_shortcut.exe.value).toLower().contains("glossitarget.exe"))
{
return true; return true;
} }
} }
@ -178,11 +157,9 @@ bool UIModel::addToSteam(QVariant shortcut)
vdfshortcut.idx = shortcuts_vdf_.shortcuts.size(); vdfshortcut.idx = shortcuts_vdf_.shortcuts.size();
vdfshortcut.appName.value = name.toStdString(); vdfshortcut.appName.value = name.toStdString();
vdfshortcut.exe.value = ("\"" + appDir.absolutePath() + "/GlosSITarget.exe" + "\"").toStdString(); vdfshortcut.exe.value = ("\"" + appDir.absolutePath() + "/GlosSITarget.exe" + "\"").toStdString();
vdfshortcut.StartDir.value = ( vdfshortcut.StartDir.value = (launch && !maybeLaunchPath.isEmpty()
launch && !maybeLaunchPath.isEmpty() ? (std::string("\"") + std::filesystem::path(maybeLaunchPath.toStdString()).parent_path().string() + "\"")
? (std::string("\"") + std::filesystem::path(maybeLaunchPath.toStdString()).parent_path().string() + "\"") : ("\"" + appDir.absolutePath() + "\"").toStdString());
: ("\"" + appDir.absolutePath() + "\"").toStdString()
);
vdfshortcut.appId.value = VDFParser::Parser::calculateAppId(vdfshortcut); vdfshortcut.appId.value = VDFParser::Parser::calculateAppId(vdfshortcut);
// ShortcutPath; default // ShortcutPath; default
vdfshortcut.LaunchOptions.value = (name + ".json").toStdString(); vdfshortcut.LaunchOptions.value = (name + ".json").toStdString();
@ -195,11 +172,11 @@ bool UIModel::addToSteam(QVariant shortcut)
// DevkitOverrideAppID; default // DevkitOverrideAppID; default
// LastPlayTime; default // LastPlayTime; default
auto maybeIcon = map["icon"].toString(); auto maybeIcon = map["icon"].toString();
if (maybeIcon.isEmpty()) if (maybeIcon.isEmpty()) {
{
if (launch && !maybeLaunchPath.isEmpty()) if (launch && !maybeLaunchPath.isEmpty())
vdfshortcut.icon.value = maybeLaunchPath.toStdString(); vdfshortcut.icon.value = maybeLaunchPath.toStdString();
} else { }
else {
vdfshortcut.icon.value = maybeIcon.toStdString(); vdfshortcut.icon.value = maybeIcon.toStdString();
} }
// Add installed locally and GlosSI tag // Add installed locally and GlosSI tag
@ -217,19 +194,16 @@ bool UIModel::addToSteam(QVariant shortcut)
const std::filesystem::path config_path = std::wstring(getSteamPath()) + user_data_path_.toStdWString() + getSteamUserId() + shortcutsfile_.toStdWString(); const std::filesystem::path config_path = std::wstring(getSteamPath()) + user_data_path_.toStdWString() + getSteamUserId() + shortcutsfile_.toStdWString();
return VDFParser::Parser::writeShortcuts(config_path, shortcuts_vdf_); return VDFParser::Parser::writeShortcuts(config_path, shortcuts_vdf_);
} }
bool UIModel::removeFromSteam(const QString& name) bool UIModel::removeFromSteam(const QString& name)
{ {
auto& scuts = shortcuts_vdf_.shortcuts; auto& scuts = shortcuts_vdf_.shortcuts;
scuts.erase(std::remove_if(scuts.begin(), scuts.end(), [&name](const auto& shortcut) scuts.erase(std::remove_if(scuts.begin(), scuts.end(), [&name](const auto& shortcut) {
{ return shortcut.appName.value == name.toStdString();
return shortcut.appName.value == name.toStdString(); }),
}), scuts.end()); scuts.end());
for (int i = 0; i < scuts.size(); i++) for (int i = 0; i < scuts.size(); i++) {
{ if (scuts[i].idx != i) {
if (scuts[i].idx != i)
{
scuts[i].idx = i; scuts[i].idx = i;
} }
} }
@ -240,232 +214,7 @@ bool UIModel::removeFromSteam(const QString& name)
#ifdef _WIN32 #ifdef _WIN32
QVariantList UIModel::uwpApps() QVariantList UIModel::uwpApps()
{ {
// TODO really should do this async, and notify gui when complete... return UWPFetch::UWPAppList();
if (!IsWindows10OrGreater())
{
return QVariantList();
}
std::vector<std::wstring> logoNames{
L"Square150x150Logo",
L"Square310x310Logo",
L"Square44x44Logo",
L"Square71x71Logo",
L"Square70x70Logo",
L"Logo",
L"SmallLogo",
L"Square30x30Logo",
};
QVariantList pairs;
// is it considered stealing when you take code that was pull-requested by someone else into your own repo?
// Anyway... Stolen from: https://github.com/Thracky/GloSC/commit/3cd92e058498e3ab9d73ced140bbd7e490f639a7
// https://github.com/Alia5/GloSC/commit/3cd92e058498e3ab9d73ced140bbd7e490f639a7
// TODO: only return apps for current user.
// TODO: I have no clue how this WinRT shit works; HELP MEH!
PackageManager^ packageManager = ref new PackageManager();
IIterable<Windows::ApplicationModel::Package^>^ packages = packageManager->FindPackages();
int packageCount = 0;
// Only way to get the count of packages is to iterate through the whole collection first
std::for_each(Windows::Foundation::Collections::begin(packages), Windows::Foundation::Collections::end(packages),
[&](Windows::ApplicationModel::Package^ package)
{
packageCount += 1;
});
int currPackage = 0;
// Iterate through all the packages
std::for_each(Windows::Foundation::Collections::begin(packages), Windows::Foundation::Collections::end(packages),
[&](Windows::ApplicationModel::Package^ package)
{
QGuiApplication::processEvents();
HRESULT hr = S_OK;
IStream* inputStream = NULL;
UINT32 pathLen = 0;
IAppxManifestReader* manifestReader = NULL;
IAppxFactory* appxFactory = NULL;
LPWSTR appId = NULL;
LPWSTR manifestAppName = NULL;
LPWSTR iconName = NULL;
// Get the package path on disk so we can load the manifest XML and get the PRAID
GetPackagePathByFullName(package->Id->FullName->Data(), &pathLen, NULL);
if (pathLen > 0) {
// Length of the path + "\\AppxManifest.xml" that we'll be appending
UINT32 manifestLen = pathLen + 20;
PWSTR pathBuf = (PWSTR)malloc(manifestLen * sizeof(wchar_t));
GetPackagePathByFullName(package->Id->FullName->Data(), &pathLen, pathBuf);
PWSTR manifest_xml = L"\\AppxManifest.xml";
hr = StringCchCatW(pathBuf, manifestLen, manifest_xml);
// Let's ignore a bunch of built in apps and such
if (wcsstr(pathBuf, L"SystemApps")) {
hr = E_FAIL;
}
else if (wcsstr(pathBuf, L".NET.Native."))
hr = E_FAIL;
else if (wcsstr(pathBuf, L".VCLibs."))
hr = E_FAIL;
else if (wcsstr(pathBuf, L"Microsoft.UI"))
hr = E_FAIL;
else if (wcsstr(pathBuf, L"Microsoft.Advertising"))
hr = E_FAIL;
else if (wcsstr(pathBuf, L"Microsoft.Services.Store"))
hr = E_FAIL;
BOOL hasCurrent = FALSE;
// Open the manifest XML
if (SUCCEEDED(hr)) {
hr = SHCreateStreamOnFileEx(
pathBuf,
STGM_READ | STGM_SHARE_EXCLUSIVE,
0, // default file attributes
FALSE, // do not create new file
NULL, // no template
&inputStream);
}
if (SUCCEEDED(hr)) {
hr = CoCreateInstance(
__uuidof(AppxFactory),
NULL,
CLSCTX_INPROC_SERVER,
__uuidof(IAppxFactory),
(LPVOID*)(&appxFactory));
}
if (SUCCEEDED(hr)) {
hr = appxFactory->CreateManifestReader(inputStream, &manifestReader);
}
// Grab application ID (PRAID) and DisplayName from the XML
if (SUCCEEDED(hr)) {
IAppxManifestApplicationsEnumerator* applications = NULL;
manifestReader->GetApplications(&applications);
if (SUCCEEDED(hr)) {
hr = applications->GetHasCurrent(&hasCurrent);
if (hasCurrent) {
IAppxManifestApplication* application = NULL;
hr = applications->GetCurrent(&application);
if (SUCCEEDED(hr)) {
application->GetStringValue(L"Id", &appId);
application->GetStringValue(L"DisplayName", &manifestAppName);
for (auto& logoNameStr : logoNames)
{
application->GetStringValue(logoNameStr.c_str(), &iconName);
if (!std::wstring(iconName).empty())
{
break;
}
}
application->Release();
}
}
else {
hr = S_FALSE;
}
applications->Release();
}
manifestReader->Release();
inputStream->Release();
}
if (SUCCEEDED(hr)) {
PWSTR appNameBuf;
QString AppUMId = QString::fromWCharArray(package->Id->FamilyName->Data());
QString AppName;
QString Path = QString::fromWCharArray(package->EffectivePath->Data());
// QString thumbToken = QString::fromWCharArray(package->GetThumbnailToken()->Data());
if (manifestAppName != NULL)
{
// If the display name is an indirect string, we'll try and load it using SHLoadIndirectString
if (wcsstr(manifestAppName, L"ms-resource:"))
{
PWSTR res_name = wcsdup(&manifestAppName[12]);
appNameBuf = (PWSTR)malloc(1026);
LPCWSTR resource_str = L"@{";
std::wstring reslookup = std::wstring(resource_str) + package->Id->FullName->Data() + L"?ms-resource://" + package->Id->Name->Data() + L"/resources/" + res_name + L"}";
PCWSTR res_str = reslookup.c_str();
hr = SHLoadIndirectString(res_str, appNameBuf, 512, NULL);
// Try several resource paths
if (!SUCCEEDED(hr)) {
std::wstring reslookup = std::wstring(resource_str) + package->Id->FullName->Data() + L"?ms-resource://" + package->Id->Name->Data() + L"/Resources/" + res_name + L"}";
PCWSTR res_str = reslookup.c_str();
hr = SHLoadIndirectString(res_str, appNameBuf, 512, NULL);
// If the third one doesn't work, we give up and use the package name from PackageManager
if (!SUCCEEDED(hr)) {
std::wstring reslookup = std::wstring(resource_str) + package->Id->FullName->Data() + L"?ms-resource://" + package->Id->Name->Data() + L"/" + res_name + L"}";
PCWSTR res_str = reslookup.c_str();
hr = SHLoadIndirectString(res_str, appNameBuf, 512, NULL);
}
}
if (!SUCCEEDED(hr))
AppName = QString::fromWCharArray(package->DisplayName->Data());
else
AppName = QString::fromWCharArray(appNameBuf);
free(appNameBuf);
}
else
{
appNameBuf = manifestAppName;
AppName = QString::fromWCharArray(appNameBuf);
}
}
else {
AppName = QString::fromWCharArray(package->DisplayName->Data());
}
QString PRAID = QString::fromWCharArray(appId);
CoTaskMemFree(appId);
if (!PRAID.isEmpty()) {
AppUMId = AppUMId.append("!");
AppUMId = AppUMId.append(PRAID);
}
QVariantMap uwpPair;
uwpPair.insert("AppName", AppName);
uwpPair.insert("AppUMId", AppUMId);
uwpPair.insert("Path", Path);
QString icoFName = Path + "/" + QString::fromWCharArray(iconName);
std::filesystem::path icoPath(icoFName.toStdString());
std::vector<QString> possibleextensions = { ".scale-100", ".scale-125", ".scale-150", ".scale-200" };
if (!std::filesystem::exists(icoPath))
{
for (const auto& ext : possibleextensions)
{
QString maybeFname = QString(icoFName).replace(".png", ext + ".png");
std::filesystem::path maybePath(maybeFname.toStdString());
if (std::filesystem::exists(maybePath))
{
icoPath = maybePath;
break;
}
}
}
uwpPair.insert("IconPath", QString::fromStdString(icoPath.string()));
free(pathBuf);
pairs.push_back(uwpPair);
}
}
currPackage += 1;
});
return pairs;
} }
#endif #endif
@ -491,8 +240,7 @@ void UIModel::writeTarget(const QJsonObject& json, const QString& name)
path /= config_dir_name_.toStdString(); path /= config_dir_name_.toStdString();
path /= (name + ".json").toStdString(); path /= (name + ".json").toStdString();
QFile file(path); QFile file(path);
if (!file.open(QIODevice::Text | QIODevice::ReadWrite)) if (!file.open(QIODevice::Text | QIODevice::ReadWrite)) {
{
// meh // meh
return; return;
} }
@ -527,7 +275,7 @@ std::filesystem::path UIModel::getSteamPath() const
#ifdef _WIN32 #ifdef _WIN32
// TODO: check if keys/value exist // TODO: check if keys/value exist
// steam should always be open and have written reg values... // steam should always be open and have written reg values...
winreg::RegKey key{ HKEY_CURRENT_USER, L"SOFTWARE\\Valve\\Steam" }; winreg::RegKey key{HKEY_CURRENT_USER, L"SOFTWARE\\Valve\\Steam"};
const auto res = key.GetStringValue(L"SteamPath"); const auto res = key.GetStringValue(L"SteamPath");
return res; return res;
#else #else
@ -540,7 +288,7 @@ std::wstring UIModel::getSteamUserId() const
#ifdef _WIN32 #ifdef _WIN32
// TODO: check if keys/value exist // TODO: check if keys/value exist
// steam should always be open and have written reg values... // steam should always be open and have written reg values...
winreg::RegKey key{ HKEY_CURRENT_USER, L"SOFTWARE\\Valve\\Steam\\ActiveProcess" }; winreg::RegKey key{HKEY_CURRENT_USER, L"SOFTWARE\\Valve\\Steam\\ActiveProcess"};
const auto res = std::to_wstring(key.GetDwordValue(L"ActiveUser")); const auto res = std::to_wstring(key.GetDwordValue(L"ActiveUser"));
return res; return res;
#else #else

@ -14,23 +14,21 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
#pragma once #pragma once
#include <filesystem> #include "VDFParser.h"
#include <QJsonObject>
#include <QObject> #include <QObject>
#include <QVariant> #include <QVariant>
#include <QJsonObject> #include <filesystem>
#include "VDFParser.h"
class UIModel : public QObject class UIModel : public QObject {
{
Q_OBJECT Q_OBJECT
Q_PROPERTY(bool isWindows READ getIsWindows CONSTANT) Q_PROPERTY(bool isWindows READ getIsWindows CONSTANT)
Q_PROPERTY(bool hasAcrlyicEffect READ hasAcrylicEffect NOTIFY acrylicChanged) Q_PROPERTY(bool hasAcrlyicEffect READ hasAcrylicEffect NOTIFY acrylicChanged)
Q_PROPERTY(QVariantList targetList READ getTargetList NOTIFY targetListChanged) Q_PROPERTY(QVariantList targetList READ getTargetList NOTIFY targetListChanged)
Q_PROPERTY(QVariantList uwpList READ uwpApps CONSTANT) Q_PROPERTY(QVariantList uwpList READ uwpApps CONSTANT)
public: public:
UIModel(); UIModel();
Q_INVOKABLE void readConfigs(); Q_INVOKABLE void readConfigs();
@ -49,17 +47,16 @@ public:
[[nodiscard]] bool hasAcrylicEffect() const; [[nodiscard]] bool hasAcrylicEffect() const;
void setAcrylicEffect(bool has_acrylic_affect); void setAcrylicEffect(bool has_acrylic_affect);
signals: signals:
void acrylicChanged(); void acrylicChanged();
void targetListChanged(); void targetListChanged();
private: private:
std::filesystem::path config_path_; std::filesystem::path config_path_;
QString config_dir_name_; QString config_dir_name_;
void writeTarget(const QJsonObject& json, const QString& name); void writeTarget(const QJsonObject& json, const QString& name);
std::filesystem::path getSteamPath() const; std::filesystem::path getSteamPath() const;
std::wstring getSteamUserId() const; std::wstring getSteamUserId() const;
void parseShortcutVDF(); void parseShortcutVDF();

@ -0,0 +1,237 @@
#pragma once
#include <QVariant>
#include <VersionHelpers.h>
#include <Windows.h>
#include <appmodel.h>
#include <appxpackaging.h>
#include <collection.h>
#include <shlwapi.h>
#include <strsafe.h>
#include <windows.h>
#pragma comment(lib, "Shlwapi.lib")
using namespace Windows::Management::Deployment;
using namespace Windows::Foundation::Collections;
namespace UWPFetch {
QVariantList UWPAppList()
{
// TODO really should do this async, and notify gui when complete...
if (!IsWindows10OrGreater()) {
return QVariantList();
}
std::vector<std::wstring> logoNames{
L"Square150x150Logo",
L"Square310x310Logo",
L"Square44x44Logo",
L"Square71x71Logo",
L"Square70x70Logo",
L"Logo",
L"SmallLogo",
L"Square30x30Logo",
};
QVariantList pairs;
// is it considered stealing when you take code that was pull-requested by someone else into your own repo?
// Anyway... Stolen from: https://github.com/Thracky/GloSC/commit/3cd92e058498e3ab9d73ced140bbd7e490f639a7
// https://github.com/Alia5/GloSC/commit/3cd92e058498e3ab9d73ced140bbd7e490f639a7
// TODO: only return apps for current user.
// TODO: I have no clue how this WinRT shit works; HELP MEH!
// BTW: This config app needs to run as admin, in order to modify steams shortcuts.vdf
// This won't hopefully be a problem fetching UWP apps for a specific user?
PackageManager ^ packageManager = ref new PackageManager();
IIterable<Windows::ApplicationModel::Package ^> ^ packages = packageManager->FindPackages();
int packageCount = 0;
// Only way to get the count of packages is to iterate through the whole collection first
std::for_each(Windows::Foundation::Collections::begin(packages), Windows::Foundation::Collections::end(packages),
[&](Windows::ApplicationModel::Package ^ package) {
packageCount += 1;
});
int currPackage = 0;
// Iterate through all the packages
std::for_each(Windows::Foundation::Collections::begin(packages), Windows::Foundation::Collections::end(packages),
[&](Windows::ApplicationModel::Package ^ package) {
QGuiApplication::processEvents();
HRESULT hr = S_OK;
IStream* inputStream = NULL;
UINT32 pathLen = 0;
IAppxManifestReader* manifestReader = NULL;
IAppxFactory* appxFactory = NULL;
LPWSTR appId = NULL;
LPWSTR manifestAppName = NULL;
LPWSTR iconName = NULL;
// Get the package path on disk so we can load the manifest XML and get the PRAID
GetPackagePathByFullName(package->Id->FullName->Data(), &pathLen, NULL);
if (pathLen > 0) {
// Length of the path + "\\AppxManifest.xml" that we'll be appending
UINT32 manifestLen = pathLen + 20;
PWSTR pathBuf = (PWSTR)malloc(manifestLen * sizeof(wchar_t));
GetPackagePathByFullName(package->Id->FullName->Data(), &pathLen, pathBuf);
PWSTR manifest_xml = L"\\AppxManifest.xml";
hr = StringCchCatW(pathBuf, manifestLen, manifest_xml);
// Let's ignore a bunch of built in apps and such
if (wcsstr(pathBuf, L"SystemApps")) {
hr = E_FAIL;
}
else if (wcsstr(pathBuf, L".NET.Native."))
hr = E_FAIL;
else if (wcsstr(pathBuf, L".VCLibs."))
hr = E_FAIL;
else if (wcsstr(pathBuf, L"Microsoft.UI"))
hr = E_FAIL;
else if (wcsstr(pathBuf, L"Microsoft.Advertising"))
hr = E_FAIL;
else if (wcsstr(pathBuf, L"Microsoft.Services.Store"))
hr = E_FAIL;
BOOL hasCurrent = FALSE;
// Open the manifest XML
if (SUCCEEDED(hr)) {
hr = SHCreateStreamOnFileEx(
pathBuf,
STGM_READ | STGM_SHARE_EXCLUSIVE,
0, // default file attributes
FALSE, // do not create new file
NULL, // no template
&inputStream);
}
if (SUCCEEDED(hr)) {
hr = CoCreateInstance(
__uuidof(AppxFactory),
NULL,
CLSCTX_INPROC_SERVER,
__uuidof(IAppxFactory),
(LPVOID*)(&appxFactory));
}
if (SUCCEEDED(hr)) {
hr = appxFactory->CreateManifestReader(inputStream, &manifestReader);
}
// Grab application ID (PRAID) and DisplayName from the XML
if (SUCCEEDED(hr)) {
IAppxManifestApplicationsEnumerator* applications = NULL;
manifestReader->GetApplications(&applications);
if (SUCCEEDED(hr)) {
hr = applications->GetHasCurrent(&hasCurrent);
if (hasCurrent) {
IAppxManifestApplication* application = NULL;
hr = applications->GetCurrent(&application);
if (SUCCEEDED(hr)) {
application->GetStringValue(L"Id", &appId);
application->GetStringValue(L"DisplayName", &manifestAppName);
for (auto& logoNameStr : logoNames) {
application->GetStringValue(logoNameStr.c_str(), &iconName);
if (!std::wstring(iconName).empty()) {
break;
}
}
application->Release();
}
}
else {
hr = S_FALSE;
}
applications->Release();
}
manifestReader->Release();
inputStream->Release();
}
if (SUCCEEDED(hr)) {
PWSTR appNameBuf;
QString AppUMId = QString::fromWCharArray(package->Id->FamilyName->Data());
QString AppName;
QString Path = QString::fromWCharArray(package->EffectivePath->Data());
// QString thumbToken = QString::fromWCharArray(package->GetThumbnailToken()->Data());
if (manifestAppName != NULL) {
// If the display name is an indirect string, we'll try and load it using SHLoadIndirectString
if (wcsstr(manifestAppName, L"ms-resource:")) {
PWSTR res_name = wcsdup(&manifestAppName[12]);
appNameBuf = (PWSTR)malloc(1026);
LPCWSTR resource_str = L"@{";
std::wstring reslookup = std::wstring(resource_str) + package->Id->FullName->Data() + L"?ms-resource://" + package->Id->Name->Data() + L"/resources/" + res_name + L"}";
PCWSTR res_str = reslookup.c_str();
hr = SHLoadIndirectString(res_str, appNameBuf, 512, NULL);
// Try several resource paths
if (!SUCCEEDED(hr)) {
std::wstring reslookup = std::wstring(resource_str) + package->Id->FullName->Data() + L"?ms-resource://" + package->Id->Name->Data() + L"/Resources/" + res_name + L"}";
PCWSTR res_str = reslookup.c_str();
hr = SHLoadIndirectString(res_str, appNameBuf, 512, NULL);
// If the third one doesn't work, we give up and use the package name from PackageManager
if (!SUCCEEDED(hr)) {
std::wstring reslookup = std::wstring(resource_str) + package->Id->FullName->Data() + L"?ms-resource://" + package->Id->Name->Data() + L"/" + res_name + L"}";
PCWSTR res_str = reslookup.c_str();
hr = SHLoadIndirectString(res_str, appNameBuf, 512, NULL);
}
}
if (!SUCCEEDED(hr))
AppName = QString::fromWCharArray(package->DisplayName->Data());
else
AppName = QString::fromWCharArray(appNameBuf);
free(appNameBuf);
}
else {
appNameBuf = manifestAppName;
AppName = QString::fromWCharArray(appNameBuf);
}
}
else {
AppName = QString::fromWCharArray(package->DisplayName->Data());
}
QString PRAID = QString::fromWCharArray(appId);
CoTaskMemFree(appId);
if (!PRAID.isEmpty()) {
AppUMId = AppUMId.append("!");
AppUMId = AppUMId.append(PRAID);
}
QVariantMap uwpPair;
uwpPair.insert("AppName", AppName);
uwpPair.insert("AppUMId", AppUMId);
uwpPair.insert("Path", Path);
QString icoFName = Path + "/" + QString::fromWCharArray(iconName);
std::filesystem::path icoPath(icoFName.toStdString());
std::vector<QString> possibleextensions = {".scale-100", ".scale-125", ".scale-150", ".scale-200"};
if (!std::filesystem::exists(icoPath)) {
for (const auto& ext : possibleextensions) {
QString maybeFname = QString(icoFName).replace(".png", ext + ".png");
std::filesystem::path maybePath(maybeFname.toStdString());
if (std::filesystem::exists(maybePath)) {
icoPath = maybePath;
break;
}
}
}
uwpPair.insert("IconPath", QString::fromStdString(icoPath.string()));
free(pathBuf);
pairs.push_back(uwpPair);
}
}
currPackage += 1;
});
return pairs;
}
} // namespace UWPFetch

File diff suppressed because it is too large Load Diff

@ -19,9 +19,8 @@ limitations under the License.
#define NOMINMAX #define NOMINMAX
#include <Windows.h> #include <Windows.h>
class WinEventFilter : public QAbstractNativeEventFilter class WinEventFilter : public QAbstractNativeEventFilter {
{ public:
public:
WinEventFilter() = default; WinEventFilter() = default;
/* /*
@ -37,8 +36,7 @@ public:
auto msg = static_cast<MSG*>(message)->message; auto msg = static_cast<MSG*>(message)->message;
auto lParam = static_cast<MSG*>(message)->lParam; auto lParam = static_cast<MSG*>(message)->lParam;
auto hwnd = static_cast<MSG*>(message)->hwnd; auto hwnd = static_cast<MSG*>(message)->hwnd;
if (msg == WM_NCCALCSIZE) if (msg == WM_NCCALCSIZE) {
{
auto sz = reinterpret_cast<LPNCCALCSIZE_PARAMS>(lParam); auto sz = reinterpret_cast<LPNCCALCSIZE_PARAMS>(lParam);
sz->rgrc[0].top -= 6; sz->rgrc[0].top -= 6;
} }

@ -19,9 +19,9 @@ limitations under the License.
#include <QWindow> #include <QWindow>
#ifdef _WIN32 #ifdef _WIN32
#include <VersionHelpers.h>
#include <Windows.h> #include <Windows.h>
#include <dwmapi.h> #include <dwmapi.h>
#include <VersionHelpers.h>
#pragma comment(lib, "Dwmapi.lib") #pragma comment(lib, "Dwmapi.lib")
#endif #endif
@ -30,8 +30,7 @@ limitations under the License.
#ifdef _WIN32 #ifdef _WIN32
// Some undocument stuff to enable aero on win10 or higher... // Some undocument stuff to enable aero on win10 or higher...
enum AccentState enum AccentState {
{
ACCENT_DISABLED = 0, ACCENT_DISABLED = 0,
ACCENT_ENABLE_GRADIENT = 1, ACCENT_ENABLE_GRADIENT = 1,
ACCENT_ENABLE_TRANSPARENTGRADIENT = 2, ACCENT_ENABLE_TRANSPARENTGRADIENT = 2,
@ -40,8 +39,7 @@ enum AccentState
ACCENT_ENABLE_HOSTBACKDROP = 5, // RS5 1809 ACCENT_ENABLE_HOSTBACKDROP = 5, // RS5 1809
ACCENT_INVALID_STATE = 6 ACCENT_INVALID_STATE = 6
}; };
struct AccentPolicy struct AccentPolicy {
{
AccentState AccentState; AccentState AccentState;
int AccentFlags; int AccentFlags;
@ -49,14 +47,12 @@ struct AccentPolicy
int AnimationId; int AnimationId;
}; };
enum WindowCompositionAttribute enum WindowCompositionAttribute {
{
// ... // ...
WCA_ACCENT_POLICY = 19 WCA_ACCENT_POLICY = 19
// ... // ...
}; };
struct WindowCompositionAttributeData struct WindowCompositionAttributeData {
{
WindowCompositionAttribute Attribute; WindowCompositionAttribute Attribute;
void* Data; void* Data;
int SizeOfData; int SizeOfData;
@ -66,7 +62,6 @@ typedef HRESULT(__stdcall* PSetWindowCompositionAttribute)(HWND hwnd, WindowComp
#endif #endif
int main(int argc, char* argv[]) int main(int argc, char* argv[])
{ {
#if defined(Q_OS_WIN) #if defined(Q_OS_WIN)
@ -98,31 +93,29 @@ int main(int argc, char* argv[])
// Enable blurbehind (not needed?) anyway gives nice background on win7 and 8 // Enable blurbehind (not needed?) anyway gives nice background on win7 and 8
DWM_BLURBEHIND bb{}; DWM_BLURBEHIND bb{};
bb.dwFlags = DWM_BB_ENABLE; bb.fEnable = true; bb.hRgnBlur = nullptr; bb.dwFlags = DWM_BB_ENABLE;
bb.fEnable = true;
bb.hRgnBlur = nullptr;
DwmEnableBlurBehindWindow(hwnd, &bb); DwmEnableBlurBehindWindow(hwnd, &bb);
if (IsWindows10OrGreater()) if (IsWindows10OrGreater()) {
{
// undoc stuff for aero >= Win10 // undoc stuff for aero >= Win10
int color = (0 << 24) + (0x21 << 16) + (0x11 << 8) + (0x11); int color = (0 << 24) + (0x21 << 16) + (0x11 << 8) + (0x11);
AccentPolicy accPol{}; AccentPolicy accPol{};
accPol.AccentState = ACCENT_ENABLE_ACRYLICBLURBEHIND; accPol.AccentState = ACCENT_ENABLE_ACRYLICBLURBEHIND;
accPol.AccentFlags = 2; accPol.GradientColor = color; accPol.AccentFlags = 2;
accPol.GradientColor = color;
accPol.AnimationId = 0; accPol.AnimationId = 0;
WindowCompositionAttributeData data{}; WindowCompositionAttributeData data{};
data.Attribute = WindowCompositionAttribute::WCA_ACCENT_POLICY; data.Attribute = WindowCompositionAttribute::WCA_ACCENT_POLICY;
data.Data = &accPol; data.Data = &accPol;
data.SizeOfData = sizeof(accPol); data.SizeOfData = sizeof(accPol);
auto user32dll = GetModuleHandle(L"user32.dll"); auto user32dll = GetModuleHandle(L"user32.dll");
if (user32dll) { if (user32dll) {
PSetWindowCompositionAttribute SetWindowCompositionAttribute = ( PSetWindowCompositionAttribute SetWindowCompositionAttribute = (reinterpret_cast<PSetWindowCompositionAttribute>(GetProcAddress(user32dll, "SetWindowCompositionAttribute")));
reinterpret_cast<PSetWindowCompositionAttribute>(GetProcAddress(user32dll, "SetWindowCompositionAttribute")) if (SetWindowCompositionAttribute) {
);
if (SetWindowCompositionAttribute)
{
auto res = SetWindowCompositionAttribute(hwnd, &data); auto res = SetWindowCompositionAttribute(hwnd, &data);
if (SUCCEEDED(res)) if (SUCCEEDED(res)) {
{
uimodel.setAcrylicEffect(true); uimodel.setAcrylicEffect(true);
} }
} }
@ -130,7 +123,7 @@ int main(int argc, char* argv[])
} }
// extend the frame fully into the client area => draw all outside the window frame. // extend the frame fully into the client area => draw all outside the window frame.
MARGINS margins = { -1 }; MARGINS margins = {-1};
DwmExtendFrameIntoClientArea(hwnd, &margins); DwmExtendFrameIntoClientArea(hwnd, &margins);
// To Fix top window frame, install native event filter // To Fix top window frame, install native event filter
@ -144,10 +137,10 @@ int main(int argc, char* argv[])
// Inform the application of the frame change. // Inform the application of the frame change.
SetWindowPos(hwnd, SetWindowPos(hwnd,
NULL, NULL,
rcClient.left, rcClient.top, rcClient.left, rcClient.top,
rcClient.right - rcClient.left, rcClient.bottom - rcClient.top, rcClient.right - rcClient.left, rcClient.bottom - rcClient.top,
SWP_FRAMECHANGED); SWP_FRAMECHANGED);
#endif #endif

@ -10,5 +10,6 @@
<file>svg/edit_white_24dp.svg</file> <file>svg/edit_white_24dp.svg</file>
<file>svg/steam.svg</file> <file>svg/steam.svg</file>
<file>qml/UWPSelectDialog.qml</file> <file>qml/UWPSelectDialog.qml</file>
<file>qml/InfoDialog.qml</file>
</qresource> </qresource>
</RCC> </RCC>

@ -0,0 +1,98 @@
/*
Copyright 2021 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
Dialog {
id: dlg
anchors.centerIn: parent
property var confirmedParam: null
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 alias titleText: title.text
property alias text: text.text
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
readonly property real spacing: 16
implicitWidth: Math.max(row.width, col.width)
implicitHeight: title.height + row.height + spacing + col.height
Label {
id: title
anchors.top: parent.top
anchors.left: parent.left
text: dlg.title
font.bold: true
}
Column {
id: col
anchors.top: title.bottom
anchors.topMargin: parent.spacing
spacing: 16
Label {
id: text
}
}
Row {
id: row
anchors.top: col.bottom
anchors.topMargin: parent.spacing
spacing: 16
Button {
text: qsTr("OK")
onClicked: function(){
close()
confirmed(confirmedParam)
}
}
anchors.right: parent.right
}
}
}

@ -17,6 +17,7 @@ import QtQuick 6.2
import QtQuick.Layouts 6.2 import QtQuick.Layouts 6.2
import QtQuick.Controls 6.2 import QtQuick.Controls 6.2
import QtQuick.Controls.Material 6.2 import QtQuick.Controls.Material 6.2
import QtQuick.Dialogs 6.2
import Qt5Compat.GraphicalEffects import Qt5Compat.GraphicalEffects
GridView { GridView {
@ -64,6 +65,12 @@ GridView {
NumberAnimation { properties: "x,y"; duration: 300; easing.type: Easing.InQuad } NumberAnimation { properties: "x,y"; duration: 300; easing.type: Easing.InQuad }
} }
InfoDialog {
id: writeErrorDialog
titleText: qsTr("Error")
text: qsTr("Error writing shortcuts.vdf...")
}
delegate: RPane { delegate: RPane {
id: delegateRoot id: delegateRoot
color: Qt.lighter(Material.background, 1.6) color: Qt.lighter(Material.background, 1.6)
@ -90,7 +97,7 @@ GridView {
anchors.left: parent.left anchors.left: parent.left
anchors.bottom: buttonrow.top anchors.bottom: buttonrow.top
anchors.margins: 12 anchors.margins: 12
spacing: 4 spacing: 8
Row { Row {
spacing: 8 spacing: 8
visible: modelData.launchPath && modelData.launchPath.length > 0 visible: modelData.launchPath && modelData.launchPath.length > 0
@ -112,6 +119,37 @@ GridView {
elide: Text.ElideRight elide: Text.ElideRight
} }
} }
Row {
visible: false // TODO: dunno about this...
spacing: 4
Label {
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 {
anchors.verticalCenter: parent.verticalCenter
text: delegateRoot.isInSteam ? qsTr("Yes") : qsTr("No")
width: 292 - typeLabel.width - 72
elide: Text.ElideRight
}
}
} }
Image { Image {
@ -134,19 +172,20 @@ GridView {
onClicked: function(){ onClicked: function(){
if (delegateRoot.isInSteam) { if (delegateRoot.isInSteam) {
if (!uiModel.removeFromSteam(modelData.name)) { if (!uiModel.removeFromSteam(modelData.name)) {
// TODO: show error writeErrorDialog.open();
return; return;
} }
} else { } else {
if (!uiModel.addToSteam(modelData)) { if (!uiModel.addToSteam(modelData)) {
// TODO: show error writeErrorDialog.open();
return; return;
} }
} }
delegateRoot.isInSteam = uiModel.isInSteam(modelData) delegateRoot.isInSteam = uiModel.isInSteam(modelData)
steamShortcutsChanged = true
} }
highlighted: delegateRoot.isInSteam highlighted: delegateRoot.isInSteam
Material.accent: Material.color(Material.Red, Material.Shade400) Material.accent: Material.color(Material.Red, Material.Shade800)
Row { Row {
anchors.centerIn: parent anchors.centerIn: parent
spacing: 8 spacing: 8

@ -43,6 +43,17 @@ Window {
return Qt.rgba(color.r, color.g, color.b, alpha); return Qt.rgba(color.r, color.g, color.b, alpha);
} }
property bool steamShortcutsChanged: false
InfoDialog {
id: steamChangedDialog
titleText: qsTr("Attention!")
text: qsTr("Please restart Steam to reload your changes!")
onConfirmed: function (callback) {
callback();
}
}
Rectangle { Rectangle {
id: titleBar id: titleBar
visible: uiModel.isWindows visible: uiModel.isWindows
@ -83,7 +94,14 @@ Window {
ToolButton { ToolButton {
id: closbtn id: closbtn
text: "🗙" text: "🗙"
onClicked: window.close() onClicked: steamShortcutsChanged
? (function(){
steamChangedDialog.confirmedParam = () => {
window.close()
}
steamChangedDialog.open()
})()
: window.close()
background: Rectangle { background: Rectangle {
implicitWidth: 32 implicitWidth: 32
implicitHeight: 32 implicitHeight: 32
@ -99,7 +117,6 @@ Window {
} }
} }
} }
} }
Item { Item {
@ -229,6 +246,5 @@ Window {
} }
} }
} }
} }
} }

@ -20,12 +20,11 @@ limitations under the License.
#include "SteamTarget.h" #include "SteamTarget.h"
#include "OverlayLogSink.h"
#include "Settings.h"
#include <spdlog/sinks/basic_file_sink.h> #include <spdlog/sinks/basic_file_sink.h>
#include <spdlog/sinks/stdout_color_sinks.h> #include <spdlog/sinks/stdout_color_sinks.h>
#include <spdlog/spdlog.h> #include <spdlog/spdlog.h>
#include "OverlayLogSink.h"
#include "Settings.h"
#ifdef _WIN32 #ifdef _WIN32
#ifdef CONSOLE #ifdef CONSOLE

Loading…
Cancel
Save