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

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

@ -16,35 +16,21 @@ limitations under the License.
#include "UIModel.h"
#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 <QJsonDocument>
#ifdef _WIN32
#include <WinReg/WinReg.hpp>
#endif
#ifdef _WIN32
#include "UWPFetch.h"
#endif
UIModel::UIModel() : QObject(nullptr)
{
auto path = std::filesystem::temp_directory_path()
.parent_path()
.parent_path()
.parent_path();
.parent_path()
.parent_path()
.parent_path();
path /= "Roaming";
path /= "GlosSI";
@ -59,7 +45,6 @@ UIModel::UIModel() : QObject(nullptr)
parseShortcutVDF();
readConfigs();
}
void UIModel::readConfigs()
@ -68,41 +53,38 @@ void UIModel::readConfigs()
auto entries = dir.entryList(QDir::Files, QDir::SortFlag::Name);
entries.removeIf([](const auto& entry) {
return !entry.endsWith(".json");
});
std::for_each(entries.begin(), entries.end(), [this](const auto& name)
{
auto path = config_path_;
path /= config_dir_name_.toStdString();
path /= name.toStdString();
QFile file(path);
if (!file.open(QIODevice::Text | QIODevice::ReadOnly))
{
// meh
return;
}
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["hideDevices"] = filejson["devices"]["hideDevices"];
json["windowMode"] = filejson["window"]["windowMode"];
json["maxFps"] = filejson["window"]["maxFps"];
json["scale"] = filejson["window"]["scale"];
json["name"] = QString(name).replace(QRegularExpression("\.json"), "");
targets_.append(json.toVariantMap());
});
});
std::for_each(entries.begin(), entries.end(), [this](const auto& name) {
auto path = config_path_;
path /= config_dir_name_.toStdString();
path /= name.toStdString();
QFile file(path);
if (!file.open(QIODevice::Text | QIODevice::ReadOnly)) {
// meh
return;
}
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["hideDevices"] = filejson["devices"]["hideDevices"];
json["windowMode"] = filejson["window"]["windowMode"];
json["maxFps"] = filejson["window"]["maxFps"];
json["scale"] = filejson["window"]["scale"];
json["name"] = QString(name).replace(QRegularExpression("\.json"), "");
targets_.append(json.toVariantMap());
});
emit targetListChanged();
}
@ -152,12 +134,9 @@ void UIModel::deleteTarget(int index)
bool UIModel::isInSteam(QVariant shortcut)
{
const auto map = shortcut.toMap();
for (auto& steam_shortcut : shortcuts_vdf_.shortcuts)
{
if (map["name"].toString() == QString::fromStdString(steam_shortcut.appName.value))
{
if (QString::fromStdString(steam_shortcut.exe.value).toLower().contains("glossitarget.exe"))
{
for (auto& steam_shortcut : shortcuts_vdf_.shortcuts) {
if (map["name"].toString() == QString::fromStdString(steam_shortcut.appName.value)) {
if (QString::fromStdString(steam_shortcut.exe.value).toLower().contains("glossitarget.exe")) {
return true;
}
}
@ -178,11 +157,9 @@ bool UIModel::addToSteam(QVariant shortcut)
vdfshortcut.idx = shortcuts_vdf_.shortcuts.size();
vdfshortcut.appName.value = name.toStdString();
vdfshortcut.exe.value = ("\"" + appDir.absolutePath() + "/GlosSITarget.exe" + "\"").toStdString();
vdfshortcut.StartDir.value = (
launch && !maybeLaunchPath.isEmpty()
? (std::string("\"") + std::filesystem::path(maybeLaunchPath.toStdString()).parent_path().string() + "\"")
: ("\"" + appDir.absolutePath() + "\"").toStdString()
);
vdfshortcut.StartDir.value = (launch && !maybeLaunchPath.isEmpty()
? (std::string("\"") + std::filesystem::path(maybeLaunchPath.toStdString()).parent_path().string() + "\"")
: ("\"" + appDir.absolutePath() + "\"").toStdString());
vdfshortcut.appId.value = VDFParser::Parser::calculateAppId(vdfshortcut);
// ShortcutPath; default
vdfshortcut.LaunchOptions.value = (name + ".json").toStdString();
@ -195,11 +172,11 @@ bool UIModel::addToSteam(QVariant shortcut)
// DevkitOverrideAppID; default
// LastPlayTime; default
auto maybeIcon = map["icon"].toString();
if (maybeIcon.isEmpty())
{
if (maybeIcon.isEmpty()) {
if (launch && !maybeLaunchPath.isEmpty())
vdfshortcut.icon.value = maybeLaunchPath.toStdString();
} else {
}
else {
vdfshortcut.icon.value = maybeIcon.toStdString();
}
// 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();
return VDFParser::Parser::writeShortcuts(config_path, shortcuts_vdf_);
}
bool UIModel::removeFromSteam(const QString& name)
{
auto& scuts = shortcuts_vdf_.shortcuts;
scuts.erase(std::remove_if(scuts.begin(), scuts.end(), [&name](const auto& shortcut)
{
return shortcut.appName.value == name.toStdString();
}), scuts.end());
for (int i = 0; i < scuts.size(); i++)
{
if (scuts[i].idx != i)
{
scuts.erase(std::remove_if(scuts.begin(), scuts.end(), [&name](const auto& shortcut) {
return shortcut.appName.value == name.toStdString();
}),
scuts.end());
for (int i = 0; i < scuts.size(); i++) {
if (scuts[i].idx != i) {
scuts[i].idx = i;
}
}
@ -240,232 +214,7 @@ bool UIModel::removeFromSteam(const QString& name)
#ifdef _WIN32
QVariantList UIModel::uwpApps()
{
// 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!
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;
return UWPFetch::UWPAppList();
}
#endif
@ -491,8 +240,7 @@ void UIModel::writeTarget(const QJsonObject& json, const QString& name)
path /= config_dir_name_.toStdString();
path /= (name + ".json").toStdString();
QFile file(path);
if (!file.open(QIODevice::Text | QIODevice::ReadWrite))
{
if (!file.open(QIODevice::Text | QIODevice::ReadWrite)) {
// meh
return;
}
@ -527,7 +275,7 @@ std::filesystem::path UIModel::getSteamPath() 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" };
winreg::RegKey key{HKEY_CURRENT_USER, L"SOFTWARE\\Valve\\Steam"};
const auto res = key.GetStringValue(L"SteamPath");
return res;
#else
@ -540,7 +288,7 @@ 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" };
winreg::RegKey key{HKEY_CURRENT_USER, L"SOFTWARE\\Valve\\Steam\\ActiveProcess"};
const auto res = std::to_wstring(key.GetDwordValue(L"ActiveUser"));
return res;
#else

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

@ -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
#include <Windows.h>
class WinEventFilter : public QAbstractNativeEventFilter
{
public:
class WinEventFilter : public QAbstractNativeEventFilter {
public:
WinEventFilter() = default;
/*
@ -37,8 +36,7 @@ public:
auto msg = static_cast<MSG*>(message)->message;
auto lParam = static_cast<MSG*>(message)->lParam;
auto hwnd = static_cast<MSG*>(message)->hwnd;
if (msg == WM_NCCALCSIZE)
{
if (msg == WM_NCCALCSIZE) {
auto sz = reinterpret_cast<LPNCCALCSIZE_PARAMS>(lParam);
sz->rgrc[0].top -= 6;
}

@ -19,9 +19,9 @@ limitations under the License.
#include <QWindow>
#ifdef _WIN32
#include <VersionHelpers.h>
#include <Windows.h>
#include <dwmapi.h>
#include <VersionHelpers.h>
#pragma comment(lib, "Dwmapi.lib")
#endif
@ -30,8 +30,7 @@ limitations under the License.
#ifdef _WIN32
// Some undocument stuff to enable aero on win10 or higher...
enum AccentState
{
enum AccentState {
ACCENT_DISABLED = 0,
ACCENT_ENABLE_GRADIENT = 1,
ACCENT_ENABLE_TRANSPARENTGRADIENT = 2,
@ -40,8 +39,7 @@ enum AccentState
ACCENT_ENABLE_HOSTBACKDROP = 5, // RS5 1809
ACCENT_INVALID_STATE = 6
};
struct AccentPolicy
{
struct AccentPolicy {
AccentState AccentState;
int AccentFlags;
@ -49,14 +47,12 @@ struct AccentPolicy
int AnimationId;
};
enum WindowCompositionAttribute
{
enum WindowCompositionAttribute {
// ...
WCA_ACCENT_POLICY = 19
// ...
};
struct WindowCompositionAttributeData
{
struct WindowCompositionAttributeData {
WindowCompositionAttribute Attribute;
void* Data;
int SizeOfData;
@ -66,7 +62,6 @@ typedef HRESULT(__stdcall* PSetWindowCompositionAttribute)(HWND hwnd, WindowComp
#endif
int main(int argc, char* argv[])
{
#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
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);
if (IsWindows10OrGreater())
{
if (IsWindows10OrGreater()) {
// undoc stuff for aero >= Win10
int color = (0 << 24) + (0x21 << 16) + (0x11 << 8) + (0x11);
AccentPolicy accPol{};
accPol.AccentState = ACCENT_ENABLE_ACRYLICBLURBEHIND;
accPol.AccentFlags = 2; accPol.GradientColor = color;
accPol.AccentFlags = 2;
accPol.GradientColor = color;
accPol.AnimationId = 0;
WindowCompositionAttributeData data{};
data.Attribute = WindowCompositionAttribute::WCA_ACCENT_POLICY;
data.Data = &accPol;
data.SizeOfData = sizeof(accPol);
data.Data = &accPol;
data.SizeOfData = sizeof(accPol);
auto user32dll = GetModuleHandle(L"user32.dll");
if (user32dll) {
PSetWindowCompositionAttribute SetWindowCompositionAttribute = (
reinterpret_cast<PSetWindowCompositionAttribute>(GetProcAddress(user32dll, "SetWindowCompositionAttribute"))
);
if (SetWindowCompositionAttribute)
{
PSetWindowCompositionAttribute SetWindowCompositionAttribute = (reinterpret_cast<PSetWindowCompositionAttribute>(GetProcAddress(user32dll, "SetWindowCompositionAttribute")));
if (SetWindowCompositionAttribute) {
auto res = SetWindowCompositionAttribute(hwnd, &data);
if (SUCCEEDED(res))
{
if (SUCCEEDED(res)) {
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.
MARGINS margins = { -1 };
MARGINS margins = {-1};
DwmExtendFrameIntoClientArea(hwnd, &margins);
// 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.
SetWindowPos(hwnd,
NULL,
rcClient.left, rcClient.top,
rcClient.right - rcClient.left, rcClient.bottom - rcClient.top,
SWP_FRAMECHANGED);
NULL,
rcClient.left, rcClient.top,
rcClient.right - rcClient.left, rcClient.bottom - rcClient.top,
SWP_FRAMECHANGED);
#endif

@ -10,5 +10,6 @@
<file>svg/edit_white_24dp.svg</file>
<file>svg/steam.svg</file>
<file>qml/UWPSelectDialog.qml</file>
<file>qml/InfoDialog.qml</file>
</qresource>
</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.Controls 6.2
import QtQuick.Controls.Material 6.2
import QtQuick.Dialogs 6.2
import Qt5Compat.GraphicalEffects
GridView {
@ -64,6 +65,12 @@ GridView {
NumberAnimation { properties: "x,y"; duration: 300; easing.type: Easing.InQuad }
}
InfoDialog {
id: writeErrorDialog
titleText: qsTr("Error")
text: qsTr("Error writing shortcuts.vdf...")
}
delegate: RPane {
id: delegateRoot
color: Qt.lighter(Material.background, 1.6)
@ -90,7 +97,7 @@ GridView {
anchors.left: parent.left
anchors.bottom: buttonrow.top
anchors.margins: 12
spacing: 4
spacing: 8
Row {
spacing: 8
visible: modelData.launchPath && modelData.launchPath.length > 0
@ -112,6 +119,37 @@ GridView {
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 {
@ -134,19 +172,20 @@ GridView {
onClicked: function(){
if (delegateRoot.isInSteam) {
if (!uiModel.removeFromSteam(modelData.name)) {
// TODO: show error
writeErrorDialog.open();
return;
}
} else {
if (!uiModel.addToSteam(modelData)) {
// TODO: show error
writeErrorDialog.open();
return;
}
}
delegateRoot.isInSteam = uiModel.isInSteam(modelData)
steamShortcutsChanged = true
}
highlighted: delegateRoot.isInSteam
Material.accent: Material.color(Material.Red, Material.Shade400)
Material.accent: Material.color(Material.Red, Material.Shade800)
Row {
anchors.centerIn: parent
spacing: 8

@ -43,6 +43,17 @@ Window {
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 {
id: titleBar
visible: uiModel.isWindows
@ -83,7 +94,14 @@ Window {
ToolButton {
id: closbtn
text: "🗙"
onClicked: window.close()
onClicked: steamShortcutsChanged
? (function(){
steamChangedDialog.confirmedParam = () => {
window.close()
}
steamChangedDialog.open()
})()
: window.close()
background: Rectangle {
implicitWidth: 32
implicitHeight: 32
@ -99,7 +117,6 @@ Window {
}
}
}
}
Item {
@ -229,6 +246,5 @@ Window {
}
}
}
}
}

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

Loading…
Cancel
Save