init
Some checks failed
Docker. / Ubuntu (push) Has been cancelled
User-agent updater. / User-agent (push) Failing after 15s
Lock Threads / lock (push) Failing after 10s
Waiting for answer. / waiting-for-answer (push) Failing after 22s
Needs user action. / needs-user-action (push) Failing after 8s
Can't reproduce. / cant-reproduce (push) Failing after 8s
Close stale issues and PRs / stale (push) Has been cancelled

This commit is contained in:
allhaileris
2026-02-16 15:50:16 +03:00
commit afb81b8278
13816 changed files with 3689732 additions and 0 deletions

View File

@@ -0,0 +1,107 @@
// This file is part of Desktop App Toolkit,
// a set of libraries for developing nice desktop applications.
//
// For license and copyright information please follow this link:
// https://github.com/desktop-app/legal/blob/master/LEGAL
//
#include "base/platform/win/base_battery_saving_win.h"
#include "base/battery_saving.h"
#include "base/integration.h"
#include <QtCore/QAbstractNativeEventFilter>
#include <QtCore/QCoreApplication>
#include <QtGui/QWindow>
#include <QtWidgets/QWidget>
#include <windows.h>
namespace base::Platform {
namespace {
class BatterySaving final
: public AbstractBatterySaving
, public QAbstractNativeEventFilter {
public:
BatterySaving(Fn<void()> changedCallback);
~BatterySaving();
std::optional<bool> enabled() const override;
private:
bool nativeEventFilter(
const QByteArray &eventType,
void *message,
native_event_filter_result *result) override;
QWidget _fake;
HWND _hwnd = nullptr;
HPOWERNOTIFY _notify = nullptr;
Fn<void()> _changedCallback;
};
BatterySaving::BatterySaving(Fn<void()> changedCallback)
: _changedCallback(std::move(changedCallback)) {
if (!_changedCallback) {
return;
}
_fake.hide();
_fake.createWinId();
const auto window = _fake.windowHandle();
if (!window) {
return;
}
_hwnd = reinterpret_cast<HWND>(window->winId());
if (!_hwnd) {
return;
}
_notify = RegisterPowerSettingNotification(
_hwnd,
&GUID_POWER_SAVING_STATUS,
DEVICE_NOTIFY_WINDOW_HANDLE);
if (!_notify) {
return;
}
qApp->installNativeEventFilter(this);
}
BatterySaving::~BatterySaving() {
if (_notify) {
qApp->removeNativeEventFilter(this);
UnregisterPowerSettingNotification(_notify);
}
}
std::optional<bool> BatterySaving::enabled() const {
if (_changedCallback && !_notify) {
return std::nullopt;
}
auto status = SYSTEM_POWER_STATUS();
if (!GetSystemPowerStatus(&status) || (status.BatteryFlag & 128)) {
return std::nullopt;
}
return (status.SystemStatusFlag == 1);
}
bool BatterySaving::nativeEventFilter(
const QByteArray &eventType,
void *message,
native_event_filter_result *result) {
Expects(_hwnd != nullptr);
const auto msg = static_cast<MSG*>(message);
if (msg->hwnd == _hwnd && msg->message == WM_POWERBROADCAST) {
Integration::Instance().enterFromEventLoop(_changedCallback);
}
return false;
}
} // namespace
std::unique_ptr<AbstractBatterySaving> CreateBatterySaving(
Fn<void()> changedCallback) {
return std::make_unique<BatterySaving>(std::move(changedCallback));
}
} // namespace base::Platform

View File

@@ -0,0 +1,7 @@
// This file is part of Desktop App Toolkit,
// a set of libraries for developing nice desktop applications.
//
// For license and copyright information please follow this link:
// https://github.com/desktop-app/legal/blob/master/LEGAL
//
#pragma once

View File

@@ -0,0 +1,247 @@
// This file is part of Desktop App Toolkit,
// a set of libraries for developing nice desktop applications.
//
// For license and copyright information please follow this link:
// https://github.com/desktop-app/legal/blob/master/LEGAL
//
#include "base/platform/win/base_file_utilities_win.h"
#include "base/platform/win/base_windows_safe_library.h"
#include "base/platform/win/base_windows_shlobj_h.h"
#include "base/algorithm.h"
#include <QtCore/QString>
#include <QtCore/QDir>
#include <array>
#include <string>
#include <shellapi.h>
#include <Shlwapi.h>
#include <RestartManager.h>
#include <io.h>
#define LOAD_SYMBOL(lib, name) ::base::Platform::LoadMethod(lib, #name, name)
namespace base::Platform {
namespace {
// RSTRTMGR.DLL
DWORD(__stdcall *RmStartSession)(
_Out_ DWORD *pSessionHandle,
_Reserved_ DWORD dwSessionFlags,
_Out_writes_(CCH_RM_SESSION_KEY + 1) WCHAR strSessionKey[]);
DWORD(__stdcall *RmRegisterResources)(
_In_ DWORD dwSessionHandle,
_In_ UINT nFiles,
_In_reads_opt_(nFiles) LPCWSTR rgsFileNames[],
_In_ UINT nApplications,
_In_reads_opt_(nApplications) RM_UNIQUE_PROCESS rgApplications[],
_In_ UINT nServices,
_In_reads_opt_(nServices) LPCWSTR rgsServiceNames[]);
DWORD(__stdcall *RmGetList)(
_In_ DWORD dwSessionHandle,
_Out_ UINT *pnProcInfoNeeded,
_Inout_ UINT *pnProcInfo,
_Inout_updates_opt_(*pnProcInfo) RM_PROCESS_INFO rgAffectedApps[],
_Out_ LPDWORD lpdwRebootReasons);
DWORD(__stdcall *RmShutdown)(
_In_ DWORD dwSessionHandle,
_In_ ULONG lActionFlags,
_In_opt_ RM_WRITE_STATUS_CALLBACK fnStatus);
DWORD(__stdcall *RmEndSession)(
_In_ DWORD dwSessionHandle);
} // namespace
void ShowInFolder(const QString &filepath) {
auto nativePath = QDir::toNativeSeparators(filepath);
const auto path = nativePath.toStdWString();
if (const auto pidl = ILCreateFromPathW(path.c_str())) {
SHOpenFolderAndSelectItems(pidl, 0, nullptr, 0);
ILFree(pidl);
return;
}
const auto pathEscaped = nativePath.replace('"', QString("\"\""));
const auto command = ("/select," + pathEscaped).toStdWString();
ShellExecute(
0,
0,
L"explorer",
command.c_str(),
0,
SW_SHOWNORMAL);
}
QString FileNameFromUserString(QString name) {
const auto kBadExtensions = { qstr(".lnk"), qstr(".scf") };
const auto kMaskExtension = qstr(".download");
for (const auto extension : kBadExtensions) {
if (name.endsWith(extension, Qt::CaseInsensitive)) {
name += kMaskExtension;
}
}
static const auto BadNames = {
qstr("CON"),
qstr("PRN"),
qstr("AUX"),
qstr("NUL"),
qstr("COM1"),
qstr("COM2"),
qstr("COM3"),
qstr("COM4"),
qstr("COM5"),
qstr("COM6"),
qstr("COM7"),
qstr("COM8"),
qstr("COM9"),
qstr("LPT1"),
qstr("LPT2"),
qstr("LPT3"),
qstr("LPT4"),
qstr("LPT5"),
qstr("LPT6"),
qstr("LPT7"),
qstr("LPT8"),
qstr("LPT9")
};
for (const auto bad : BadNames) {
if (name.startsWith(bad, Qt::CaseInsensitive)) {
if (name.size() == bad.size() || name[bad.size()] == '.') {
name = '_' + name;
break;
}
}
}
return name;
}
bool DeleteDirectory(QString path) {
if (path.endsWith('/')) {
path.chop(1);
}
const auto wide = QDir::toNativeSeparators(path).toStdWString()
+ wchar_t(0)
+ wchar_t(0);
SHFILEOPSTRUCT file_op = {
NULL,
FO_DELETE,
wide.data(),
L"",
FOF_NOCONFIRMATION |
FOF_NOERRORUI |
FOF_SILENT,
false,
0,
L""
};
return (SHFileOperation(&file_op) == 0);
}
void RemoveQuarantine(const QString &path) {
}
QString BundledResourcesPath() {
Unexpected("BundledResourcesPath not implemented.");
}
QString CurrentExecutablePath(int argc, char *argv[]) {
auto result = std::array<WCHAR, MAX_PATH + 1>{ 0 };
const auto count = GetModuleFileName(
nullptr,
result.data(),
MAX_PATH + 1);
if (count < MAX_PATH + 1) {
const auto info = QFileInfo(QDir::fromNativeSeparators(
QString::fromWCharArray(result.data(), count)));
return info.absoluteFilePath();
}
// Fallback to the first command line argument.
auto argsCount = 0;
if (const auto args = CommandLineToArgvW(GetCommandLine(), &argsCount)) {
auto info = QFileInfo(QDir::fromNativeSeparators(
QString::fromWCharArray(args[0])));
LocalFree(args);
return info.absoluteFilePath();
}
return QString();
}
bool CloseProcesses(const QString &filename) {
static const auto loaded = [&] {
const auto LibRstrtMgr = SafeLoadLibrary(L"rstrtmgr.dll");
return LOAD_SYMBOL(LibRstrtMgr, RmStartSession)
&& LOAD_SYMBOL(LibRstrtMgr, RmRegisterResources)
&& LOAD_SYMBOL(LibRstrtMgr, RmGetList)
&& LOAD_SYMBOL(LibRstrtMgr, RmShutdown)
&& LOAD_SYMBOL(LibRstrtMgr, RmEndSession);
}();
if (!loaded) {
return false;
}
auto session = DWORD();
auto sessionKey = std::wstring(CCH_RM_SESSION_KEY + 1, wchar_t(0));
auto error = RmStartSession(&session, 0, sessionKey.data());
if (error != ERROR_SUCCESS) {
return false;
}
const auto guard = gsl::finally([&] { RmEndSession(session); });
const auto path = QDir::toNativeSeparators(filename).toStdWString();
auto nullterm = path.c_str();
error = RmRegisterResources(
session,
1,
&nullterm,
0,
nullptr,
0,
nullptr);
if (error != ERROR_SUCCESS) {
return false;
}
auto processInfoNeeded = UINT(0);
auto processInfoCount = UINT(0);
auto reason = DWORD();
error = RmGetList(
session,
&processInfoNeeded,
&processInfoCount,
nullptr,
&reason);
if (error != ERROR_SUCCESS && error != ERROR_MORE_DATA) {
return false;
} else if (processInfoNeeded <= 0) {
return true;
}
error = RmShutdown(session, RmForceShutdown, NULL);
if (error != ERROR_SUCCESS) {
return false;
}
return true;
}
bool RenameWithOverwrite(const QString &from, const QString &to) {
const auto fromPath = QDir::toNativeSeparators(from).toStdWString();
const auto toPath = QDir::toNativeSeparators(to).toStdWString();
return MoveFileEx(
fromPath.c_str(),
toPath.c_str(),
MOVEFILE_REPLACE_EXISTING);
}
void FlushFileData(QFile &file) {
file.flush();
if (const auto descriptor = file.handle()) {
if (const auto handle = HANDLE(_get_osfhandle(descriptor))) {
FlushFileBuffers(handle);
}
}
}
} // namespace base::Platform

View File

@@ -0,0 +1,13 @@
// This file is part of Desktop App Toolkit,
// a set of libraries for developing nice desktop applications.
//
// For license and copyright information please follow this link:
// https://github.com/desktop-app/legal/blob/master/LEGAL
//
#pragma once
namespace base::Platform {
bool CloseProcesses(const QString &filename);
} // namespace base::Platform

View File

@@ -0,0 +1,224 @@
// This file is part of Desktop App Toolkit,
// a set of libraries for developing nice desktop applications.
//
// For license and copyright information please follow this link:
// https://github.com/desktop-app/legal/blob/master/LEGAL
//
#include "base/platform/win/base_global_shortcuts_win.h"
#include <windows.h>
namespace base::Platform::GlobalShortcuts {
namespace {
constexpr auto kShiftMouseButton = std::numeric_limits<uint64>::max() - 100;
HHOOK GlobalHookKeyboard = nullptr;
HHOOK GlobalHookMouse = nullptr;
HANDLE ThreadHandle = nullptr;
HANDLE ThreadEvent = nullptr;
DWORD ThreadId = 0;
Fn<void(GlobalShortcutKeyGeneric descriptor, bool down)> ProcessCallback;
[[nodiscard]] GlobalShortcutKeyGeneric MakeDescriptor(
uint32 virtualKeyCode,
uint32 lParam) {
return GlobalShortcutKeyGeneric(
(uint64(virtualKeyCode) << 32) | uint64(lParam));
}
[[nodiscard]] GlobalShortcutKeyGeneric MakeMouseDescriptor(uint8 button) {
Expects(button > 0 && button < 100);
return kShiftMouseButton + button;
}
[[nodiscard]] uint32 GetVirtualKeyCode(GlobalShortcutKeyGeneric descriptor) {
return uint32(uint64(descriptor) >> 32);
}
[[nodiscard]] uint32 GetLParam(GlobalShortcutKeyGeneric descriptor) {
return uint32(uint64(descriptor) & 0xFFFFFFFFULL);
}
void ProcessHookedKeyboardEvent(WPARAM wParam, LPARAM lParam) {
const auto press = (PKBDLLHOOKSTRUCT)lParam;
const auto repeatCount = uint32(0);
const auto extendedBit = ((press->flags & LLKHF_EXTENDED) != 0);
//const auto contextBit = ((press->flags & LLKHF_ALTDOWN) != 0);
//const auto transitionState = ((press->flags & LLKHF_UP) != 0);
const auto lParamForEvent = (repeatCount & 0x0000FFFFU)
| ((uint32(press->scanCode) & 0xFFU) << 16)
| (extendedBit ? (uint32(KF_EXTENDED) << 16) : 0);
//| (contextBit ? (uint32(KF_ALTDOWN) << 16) : 0); // Alt pressed.
//| (transitionState ? (uint32(KF_UP) << 16) : 0); // Is pressed.
const auto descriptor = MakeDescriptor(press->vkCode, lParamForEvent);
const auto down = (wParam == WM_KEYDOWN || wParam == WM_SYSKEYDOWN);
ProcessCallback(descriptor, down);
}
void ProcessHookedMouseEvent(WPARAM wParam, LPARAM lParam) {
if (wParam != WM_RBUTTONDOWN
&& wParam != WM_RBUTTONUP
&& wParam != WM_MBUTTONDOWN
&& wParam != WM_MBUTTONUP
&& wParam != WM_XBUTTONDOWN
&& wParam != WM_XBUTTONUP) {
return;
}
const auto button = [&] {
if (wParam == WM_RBUTTONDOWN || wParam == WM_RBUTTONUP) {
return 2;
} else if (wParam == WM_MBUTTONDOWN || wParam == WM_MBUTTONUP) {
return 3;
}
const auto press = (PMSLLHOOKSTRUCT)lParam;
const auto xbutton = ((press->mouseData >> 16) & 0xFFU);
return (xbutton >= 0x01 && xbutton <= 0x18) ? int(xbutton + 3) : 0;
}();
if (!button) {
return;
}
const auto descriptor = MakeMouseDescriptor(button);
const auto down = (wParam == WM_RBUTTONDOWN)
|| (wParam == WM_MBUTTONDOWN)
|| (wParam == WM_XBUTTONDOWN);
ProcessCallback(descriptor, down);
}
LRESULT CALLBACK LowLevelKeyboardProc(
_In_ int nCode,
_In_ WPARAM wParam,
_In_ LPARAM lParam) {
if (nCode == HC_ACTION) {
ProcessHookedKeyboardEvent(wParam, lParam);
}
return CallNextHookEx(GlobalHookKeyboard, nCode, wParam, lParam);
}
LRESULT CALLBACK LowLevelMouseProc(
_In_ int nCode,
_In_ WPARAM wParam,
_In_ LPARAM lParam) {
if (nCode == HC_ACTION) {
ProcessHookedMouseEvent(wParam, lParam);
}
return CallNextHookEx(GlobalHookMouse, nCode, wParam, lParam);
}
DWORD WINAPI RunThread(LPVOID) {
auto message = MSG();
// Force message loop creation.
PeekMessage(&message, nullptr, WM_USER, WM_USER, PM_NOREMOVE);
SetEvent(ThreadEvent);
const auto guard = gsl::finally([&] {
if (GlobalHookKeyboard) {
UnhookWindowsHookEx(GlobalHookKeyboard);
GlobalHookKeyboard = nullptr;
}
if (GlobalHookMouse) {
UnhookWindowsHookEx(GlobalHookMouse);
GlobalHookMouse = nullptr;
}
});
GlobalHookKeyboard = SetWindowsHookEx(
WH_KEYBOARD_LL,
LowLevelKeyboardProc,
nullptr,
0);
GlobalHookMouse = SetWindowsHookEx(
WH_MOUSE_LL,
LowLevelMouseProc,
nullptr,
0);
if (!GlobalHookKeyboard || !GlobalHookMouse) {
return -1;
}
while (GetMessage(&message, nullptr, 0, 0)) {
if (message.message == WM_QUIT) {
break;
}
TranslateMessage(&message);
DispatchMessage(&message);
}
return 0;
}
} // namespace
bool Available() {
return true;
}
bool Allowed() {
return true;
}
void Start(Fn<void(GlobalShortcutKeyGeneric descriptor, bool down)> process) {
Expects(!ThreadHandle);
Expects(!ThreadId);
ProcessCallback = std::move(process);
ThreadEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr);
if (!ThreadEvent) {
ProcessCallback = nullptr;
return;
}
ThreadHandle = CreateThread(
nullptr,
0,
&RunThread,
nullptr,
0,
&ThreadId);
if (!ThreadHandle) {
CloseHandle(ThreadEvent);
ThreadEvent = nullptr;
ThreadId = 0;
ProcessCallback = nullptr;
}
}
void Stop() {
if (!ThreadHandle) {
return;
}
WaitForSingleObject(ThreadEvent, INFINITE);
PostThreadMessage(ThreadId, WM_QUIT, 0, 0);
WaitForSingleObject(ThreadHandle, INFINITE);
CloseHandle(ThreadHandle);
CloseHandle(ThreadEvent);
ThreadHandle = ThreadEvent = nullptr;
ThreadId = 0;
ProcessCallback = nullptr;
}
QString KeyName(GlobalShortcutKeyGeneric descriptor) {
if (descriptor > kShiftMouseButton) {
return QString("Mouse %1").arg(descriptor - kShiftMouseButton);
}
constexpr auto kLimit = 1024;
WCHAR buffer[kLimit + 1] = { 0 };
// Remove 25 bit, we want to differentiate between left and right Ctrl-s.
auto lParam = LONG(GetLParam(descriptor) & ~(1U << 25));
return GetKeyNameText(lParam, buffer, kLimit)
? QString::fromWCharArray(buffer)
: (GetVirtualKeyCode(descriptor) == VK_RSHIFT)
? QString("Right Shift")
: QString("\\x%1").arg(GetVirtualKeyCode(descriptor), 0, 16);
}
bool IsToggleFullScreenKey(not_null<QKeyEvent*> e) {
return false;
}
} // namespace base::Platform::GlobalShortcuts

View File

@@ -0,0 +1,9 @@
// This file is part of Desktop App Toolkit,
// a set of libraries for developing nice desktop applications.
//
// For license and copyright information please follow this link:z
// https://github.com/desktop-app/legal/blob/master/LEGAL
//
#pragma once
#include "base/platform/base_platform_global_shortcuts.h"

View File

@@ -0,0 +1,18 @@
// This file is part of Desktop App Toolkit,
// a set of libraries for developing nice desktop applications.
//
// For license and copyright information please follow this link:
// https://github.com/desktop-app/legal/blob/master/LEGAL
//
#include "base/platform/win/base_haptic_win.h"
namespace base::Platform {
void Haptic() {
}
bool IsSwipeBackEnabled() {
return true;
}
} // namespace base::Platform

View File

@@ -0,0 +1,9 @@
// This file is part of Desktop App Toolkit,
// a set of libraries for developing nice desktop applications.
//
// For license and copyright information please follow this link:
// https://github.com/desktop-app/legal/blob/master/LEGAL
//
#pragma once
#include "base/platform/base_platform_haptic.h"

View File

@@ -0,0 +1,371 @@
// This file is part of Desktop App Toolkit,
// a set of libraries for developing nice desktop applications.
//
// For license and copyright information please follow this link:
// https://github.com/desktop-app/legal/blob/master/LEGAL
//
#include "base/platform/win/base_info_win.h"
#include "base/algorithm.h"
#include "base/platform/win/base_windows_safe_library.h"
#include <QtCore/QOperatingSystemVersion>
#include <QtCore/QJsonObject>
#include <QtCore/QDate>
#include <QtCore/QSettings>
#include <windows.h>
#include <VersionHelpers.h>
namespace Platform {
namespace {
decltype(&::IsWow64Process2) IsWow64Process2;
bool IsWow64Process2Supported() {
static const auto Result = [&] {
#define LOAD_SYMBOL(lib, name) base::Platform::LoadMethod(lib, #name, name)
const auto lib = base::Platform::SafeLoadLibrary(L"Kernel32.dll");
return LOAD_SYMBOL(lib, IsWow64Process2);
#undef LOAD_SYMBOL
}();
return Result;
}
QString GetLangCodeById(unsigned int lngId) {
const auto primary = (lngId & 0xFFU);
switch (primary) {
case 0x36: return u"af"_q;
case 0x1C: return u"sq"_q;
case 0x5E: return u"am"_q;
case 0x01: return u"ar"_q;
case 0x2B: return u"hy"_q;
case 0x4D: return u"as"_q;
case 0x2C: return u"az"_q;
case 0x45: return u"bn"_q;
case 0x6D: return u"ba"_q;
case 0x2D: return u"eu"_q;
case 0x23: return u"be"_q;
case 0x1A:
return (lngId == LANG_CROATIAN)
? u"hr"_q
: (lngId == LANG_BOSNIAN_NEUTRAL || lngId == LANG_BOSNIAN)
? u"bs"_q
: u"sr"_q;
case 0x7E: return u"br"_q;
case 0x02: return u"bg"_q;
case 0x92: return u"ku"_q;
case 0x03: return u"ca"_q;
case 0x04: return u"zh"_q;
case 0x83: return u"co"_q;
case 0x05: return u"cs"_q;
case 0x06: return u"da"_q;
case 0x65: return u"dv"_q;
case 0x13: return u"nl"_q;
case 0x09: return u"en"_q;
case 0x25: return u"et"_q;
case 0x38: return u"fo"_q;
case 0x0B: return u"fi"_q;
case 0x0c: return u"fr"_q;
case 0x62: return u"fy"_q;
case 0x56: return u"gl"_q;
case 0x37: return u"ka"_q;
case 0x07: return u"de"_q;
case 0x08: return u"el"_q;
case 0x6F: return u"kl"_q;
case 0x47: return u"gu"_q;
case 0x68: return u"ha"_q;
case 0x0D: return u"he"_q;
case 0x39: return u"hi"_q;
case 0x0E: return u"hu"_q;
case 0x0F: return u"is"_q;
case 0x70: return u"ig"_q;
case 0x21: return u"id"_q;
case 0x5D: return u"iu"_q;
case 0x3C: return u"ga"_q;
case 0x34: return u"xh"_q;
case 0x35: return u"zu"_q;
case 0x10: return u"it"_q;
case 0x11: return u"ja"_q;
case 0x4B: return u"kn"_q;
case 0x3F: return u"kk"_q;
case 0x53: return u"kh"_q;
case 0x87: return u"rw"_q;
case 0x12: return u"ko"_q;
case 0x40: return u"ky"_q;
case 0x54: return u"lo"_q;
case 0x26: return u"lv"_q;
case 0x27: return u"lt"_q;
case 0x6E: return u"lb"_q;
case 0x2F: return u"mk"_q;
case 0x3E: return u"ms"_q;
case 0x4C: return u"ml"_q;
case 0x3A: return u"mt"_q;
case 0x81: return u"mi"_q;
case 0x4E: return u"mr"_q;
case 0x50: return u"mn"_q;
case 0x61: return u"ne"_q;
case 0x14: return u"no"_q;
case 0x82: return u"oc"_q;
case 0x48: return u"or"_q;
case 0x63: return u"ps"_q;
case 0x29: return u"fa"_q;
case 0x15: return u"pl"_q;
case 0x16: return u"pt"_q;
case 0x67: return u"ff"_q;
case 0x46: return u"pa"_q;
case 0x18: return u"ro"_q;
case 0x17: return u"rm"_q;
case 0x19: return u"ru"_q;
case 0x3B: return u"se"_q;
case 0x4F: return u"sa"_q;
case 0x32: return u"tn"_q;
case 0x59: return u"sd"_q;
case 0x5B: return u"si"_q;
case 0x1B: return u"sk"_q;
case 0x24: return u"sl"_q;
case 0x0A: return u"es"_q;
case 0x41: return u"sw"_q;
case 0x1D: return u"sv"_q;
case 0x28: return u"tg"_q;
case 0x49: return u"ta"_q;
case 0x44: return u"tt"_q;
case 0x4A: return u"te"_q;
case 0x1E: return u"th"_q;
case 0x51: return u"bo"_q;
case 0x73: return u"ti"_q;
case 0x1F: return u"tr"_q;
case 0x42: return u"tk"_q;
case 0x22: return u"uk"_q;
case 0x20: return u"ur"_q;
case 0x80: return u"ug"_q;
case 0x43: return u"uz"_q;
case 0x2A: return u"vi"_q;
case 0x52: return u"cy"_q;
case 0x88: return u"wo"_q;
case 0x78: return u"ii"_q;
case 0x6A: return u"yo"_q;
}
return QString();
}
} // namespace
QString DeviceModelPretty() {
using namespace base::Platform;
static const auto result = FinalizeDeviceModel([&] {
const auto bios = QSettings(
"HKEY_LOCAL_MACHINE\\HARDWARE\\DESCRIPTION\\System\\BIOS",
QSettings::NativeFormat);
const auto value = [&](const char *key) {
return SimplifyDeviceModel(bios.value(key).toString());
};
const auto systemProductName = value("SystemProductName");
if (const auto model = ProductNameToDeviceModel(systemProductName)
; !model.isEmpty()) {
return model;
}
const auto systemFamily = value("SystemFamily");
const auto baseBoardProduct = value("BaseBoardProduct");
const auto familyBoard = SimplifyDeviceModel(
systemFamily + ' ' + baseBoardProduct);
if (IsDeviceModelOk(familyBoard)) {
return familyBoard;
} else if (IsDeviceModelOk(baseBoardProduct)) {
return baseBoardProduct;
} else if (IsDeviceModelOk(systemFamily)) {
return systemFamily;
}
return QString();
}());
return result;
}
QString SystemVersionPretty() {
static const auto result = [&] {
QStringList resultList;
if (IsWindows11OrGreater()) {
resultList << "Windows 11";
} else if (IsWindows10OrGreater()) {
resultList << "Windows 10";
} else if (IsWindows8Point1OrGreater()) {
resultList << "Windows 8.1";
} else if (IsWindows8OrGreater()) {
resultList << "Windows 8";
} else if (IsWindows7OrGreater()) {
resultList << "Windows 7";
} else {
resultList << QSysInfo::prettyProductName();
}
USHORT processMachine, nativeMachine{};
if (IsWow64Process2Supported()) {
IsWow64Process2(
GetCurrentProcess(),
&processMachine,
&nativeMachine);
} else {
BOOL isWow64{};
IsWow64Process(GetCurrentProcess(), &isWow64);
if (isWow64) {
nativeMachine = IMAGE_FILE_MACHINE_AMD64;
}
}
switch (nativeMachine) {
case IMAGE_FILE_MACHINE_AMD64: {
resultList << "x64";
break;
}
case IMAGE_FILE_MACHINE_ARM64: {
resultList << "arm64";
break;
}
}
return resultList.join(' ');
}();
return result;
}
QString SystemCountry() {
auto key = HKEY();
const auto result = RegOpenKeyEx(
HKEY_CURRENT_USER,
L"Control Panel\\International\\Geo",
0,
KEY_READ,
&key);
if (result == ERROR_SUCCESS) {
constexpr auto kBufSize = 4;
auto checkType = DWORD();
auto checkSize = DWORD(kBufSize * 2);
auto checkStr = std::array<WCHAR, kBufSize>{ 0 };
const auto result = RegQueryValueEx(
key,
L"Name",
0,
&checkType,
reinterpret_cast<BYTE*>(checkStr.data()),
&checkSize);
if (result == ERROR_SUCCESS && checkSize == 6) { // 2 wchars + null
return QString::fromWCharArray(checkStr.data());
}
}
int chCount = GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SISO3166CTRYNAME, 0, 0);
if (chCount && chCount < 128) {
WCHAR wstrCountry[128];
int len = GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SISO3166CTRYNAME, wstrCountry, chCount);
if (len) {
return QString::fromStdWString(std::wstring(wstrCountry));
}
}
return QString();
}
QString SystemLanguage() {
constexpr auto kMaxLanguageLength = 128;
auto uiLanguageId = GetUserDefaultUILanguage();
auto uiLanguageLength = GetLocaleInfo(uiLanguageId, LOCALE_SNAME, nullptr, 0);
if (uiLanguageLength > 0 && uiLanguageLength < kMaxLanguageLength) {
WCHAR uiLanguageWideString[kMaxLanguageLength] = { 0 };
uiLanguageLength = GetLocaleInfo(uiLanguageId, LOCALE_SNAME, uiLanguageWideString, uiLanguageLength);
if (uiLanguageLength <= 0) {
return QString();
}
return QString::fromWCharArray(uiLanguageWideString);
}
auto uiLanguageCodeLength = GetLocaleInfo(uiLanguageId, LOCALE_ILANGUAGE, nullptr, 0);
if (uiLanguageCodeLength > 0 && uiLanguageCodeLength < kMaxLanguageLength) {
WCHAR uiLanguageCodeWideString[kMaxLanguageLength] = { 0 };
uiLanguageCodeLength = GetLocaleInfo(uiLanguageId, LOCALE_ILANGUAGE, uiLanguageCodeWideString, uiLanguageCodeLength);
if (uiLanguageCodeLength <= 0) {
return QString();
}
auto languageCode = 0U;
for (auto i = 0; i != uiLanguageCodeLength; ++i) {
auto ch = uiLanguageCodeWideString[i];
if (!ch) {
break;
}
languageCode *= 0x10U;
if (ch >= WCHAR('0') && ch <= WCHAR('9')) {
languageCode += static_cast<unsigned>(int(ch) - int(WCHAR('0')));
} else if (ch >= WCHAR('A') && ch <= WCHAR('F')) {
languageCode += static_cast<unsigned>(0x0A + int(ch) - int(WCHAR('A')));
} else {
return QString();
}
}
return GetLangCodeById(languageCode);
}
return QString();
}
QDate WhenSystemBecomesOutdated() {
return QDate();
}
int AutoUpdateVersion() {
return 4;
}
QString AutoUpdateKey() {
if (IsWindowsARM64()) {
return "winarm";
} else if (IsWindows64Bit()) {
return "win64";
} else {
return "win";
}
}
bool IsWindows7OrGreater() {
static const auto result = ::IsWindows7OrGreater();
return result;
}
bool IsWindows8OrGreater() {
static const auto result = ::IsWindows8OrGreater();
return result;
}
bool IsWindows8Point1OrGreater() {
static const auto result = ::IsWindows8Point1OrGreater();
return result;
}
bool IsWindows10OrGreater() {
static const auto result = ::IsWindows10OrGreater();
return result;
}
bool IsWindows11OrGreater() {
static const auto result = [&] {
if (!IsWindows10OrGreater()) {
return false;
}
const auto version = QOperatingSystemVersion::current();
return (version.majorVersion() > 10)
|| (version.microVersion() >= 22000);
}();
return result;
}
void Start(QJsonObject settings) {
SetDllDirectory(L"");
}
void Finish() {
}
} // namespace Platform

View File

@@ -0,0 +1,69 @@
// This file is part of Desktop App Toolkit,
// a set of libraries for developing nice desktop applications.
//
// For license and copyright information please follow this link:
// https://github.com/desktop-app/legal/blob/master/LEGAL
//
#pragma once
#include "base/platform/base_platform_info.h"
namespace Platform {
inline OutdateReason WhySystemBecomesOutdated() {
return OutdateReason::IsOld;
}
inline constexpr bool IsWindows() {
return true;
}
inline constexpr bool IsWindows32Bit() {
#ifdef Q_PROCESSOR_X86_32
return true;
#else // Q_PROCESSOR_X86_32
return false;
#endif // Q_PROCESSOR_X86_32
}
inline constexpr bool IsWindows64Bit() {
#ifdef Q_PROCESSOR_X86_64
return true;
#else // Q_PROCESSOR_X86_64
return false;
#endif // Q_PROCESSOR_X86_64
}
inline constexpr bool IsWindowsARM64() {
#ifdef Q_PROCESSOR_ARM_64
return true;
#else // Q_PROCESSOR_ARM_64
return false;
#endif // Q_PROCESSOR_ARM_64
}
inline constexpr bool IsWindowsStoreBuild() {
#ifdef OS_WIN_STORE
return true;
#else // OS_WIN_STORE
return false;
#endif // OS_WIN_STORE
}
inline constexpr bool IsMac() { return false; }
inline constexpr bool IsMacStoreBuild() { return false; }
inline bool IsMac10_12OrGreater() { return false; }
inline bool IsMac10_13OrGreater() { return false; }
inline bool IsMac10_14OrGreater() { return false; }
inline bool IsMac10_15OrGreater() { return false; }
inline bool IsMac11_0OrGreater() { return false; }
inline bool IsMac26_0OrGreater() { return false; }
inline constexpr bool IsLinux() { return false; }
inline bool IsX11() { return false; }
inline bool IsWayland() { return false; }
inline bool IsXwayland() { return false; }
inline QString GetLibcName() { return QString(); }
inline QString GetLibcVersion() { return QString(); }
inline QString GetWindowManager() { return QString(); }
} // namespace Platform

View File

@@ -0,0 +1,71 @@
// This file is part of Desktop App Toolkit,
// a set of libraries for developing nice desktop applications.
//
// For license and copyright information please follow this link:
// https://github.com/desktop-app/legal/blob/master/LEGAL
//
#include "base/platform/win/base_last_input_win.h"
#include "base/call_delayed.h"
#include "base/integration.h"
#include <windows.h>
namespace base::Platform {
namespace {
constexpr auto kRefreshBadLastUserInputTimeout = 10 * crl::time(1000);
} // namespace
std::optional<crl::time> LastUserInputTime() {
auto lii = LASTINPUTINFO{ 0 };
lii.cbSize = sizeof(LASTINPUTINFO);
if (!GetLastInputInfo(&lii)) {
return std::nullopt;
}
const auto now = crl::now();
const auto input = crl::time(lii.dwTime);
static auto LastTrackedInput = input;
static auto LastTrackedWhen = now;
const auto ticks32 = crl::time(GetTickCount());
const auto ticks64 = crl::time(GetTickCount64());
const auto elapsed = std::max(ticks32, ticks64) - input;
const auto good = (std::abs(ticks32 - ticks64) <= crl::time(1000))
&& (elapsed >= 0);
if (good) {
LastTrackedInput = input;
LastTrackedWhen = now;
return (now > elapsed) ? (now - elapsed) : crl::time(0);
}
static auto WaitingDelayed = false;
if (!WaitingDelayed) {
WaitingDelayed = true;
base::call_delayed(kRefreshBadLastUserInputTimeout, [=] {
WaitingDelayed = false;
[[maybe_unused]] const auto cheked = LastUserInputTime();
});
}
constexpr auto OverrunLimit = std::numeric_limits<DWORD>::max();
constexpr auto OverrunThreshold = OverrunLimit / 4;
if (LastTrackedInput == input) {
return LastTrackedWhen;
}
const auto guard = gsl::finally([&] {
LastTrackedInput = input;
LastTrackedWhen = now;
});
if (input > LastTrackedInput) {
const auto add = input - LastTrackedInput;
return std::min(LastTrackedWhen + add, now);
} else if (crl::time(OverrunLimit) + input - LastTrackedInput
< crl::time(OverrunThreshold)) {
const auto add = crl::time(OverrunLimit) + input - LastTrackedInput;
return std::min(LastTrackedWhen + add, now);
}
return LastTrackedWhen;
}
} // namespace base::Platform

View File

@@ -0,0 +1,9 @@
// This file is part of Desktop App Toolkit,
// a set of libraries for developing nice desktop applications.
//
// For license and copyright information please follow this link:
// https://github.com/desktop-app/legal/blob/master/LEGAL
//
#pragma once
#include "base/platform/base_platform_last_input.h"

View File

@@ -0,0 +1,59 @@
// This file is part of Desktop App Toolkit,
// a set of libraries for developing nice desktop applications.
//
// For license and copyright information please follow this link:
// https://github.com/desktop-app/legal/blob/master/LEGAL
//
#include "base/platform/win/base_layout_switch_win.h"
#include <windows.h>
#include <WinUser.h>
#include <minwindef.h>
namespace base::Platform {
namespace {
constexpr auto kMaxLayoutsCount = 1024;
} // namespace
bool SwitchKeyboardLayoutToEnglish() {
auto listSize = 0;
listSize = GetKeyboardLayoutList(0, nullptr);
if (!listSize || listSize > kMaxLayoutsCount) {
return false;
}
auto list = std::vector<HKL>(listSize, nullptr);
GetKeyboardLayoutList(list.size(), list.data());
auto selectedLayout = HKL();
auto selectedLevel = 0;
const auto offer = [&](HKL layout, int level) {
if (level > selectedLevel) {
selectedLayout = layout;
selectedLevel = level;
}
};
for (const auto layout : list) {
const auto languageId = reinterpret_cast<quintptr>(layout) & 0xFFFF;
const auto primaryId = languageId & 0x03FF;
const auto sublanguageId = (languageId >> 10);
if (primaryId == LANG_ENGLISH) {
if (sublanguageId == SUBLANG_ENGLISH_US) {
offer(layout, 3);
} else if (sublanguageId == SUBLANG_ENGLISH_UK) {
offer(layout, 2);
} else {
offer(layout, 1);
}
}
}
if (!selectedLayout) {
return false;
}
const auto previous = ActivateKeyboardLayout(
selectedLayout,
KLF_SETFORPROCESS);
return (previous != nullptr);
}
} // namespace base::Platform

View File

@@ -0,0 +1,8 @@
// This file is part of Desktop App Toolkit,
// a set of libraries for developing nice desktop applications.
//
// For license and copyright information please follow this link:
// https://github.com/desktop-app/legal/blob/master/LEGAL
//
#pragma once

View File

@@ -0,0 +1,217 @@
// This file is part of Desktop App Toolkit,
// a set of libraries for developing nice desktop applications.
//
// For license and copyright information please follow this link:
// https://github.com/desktop-app/legal/blob/master/LEGAL
//
#include "base/platform/base_platform_network_reachability.h"
#if QT_VERSION >= QT_VERSION_CHECK(6, 2, 0)
namespace base::Platform {
std::unique_ptr<NetworkReachability> NetworkReachability::Create() {
return nullptr;
}
} // namespace base::Platform
#else // Qt >= 6.2.0
#include "base/debug_log.h"
#include <windows.h>
#include <wrl/client.h>
#include <netlistmgr.h>
#include <comdef.h>
using namespace Microsoft::WRL;
namespace base::Platform {
namespace {
QString ErrorStringFromHResult(HRESULT hr) {
_com_error error(hr);
return QString::fromWCharArray(error.ErrorMessage());
}
template<typename T>
bool QueryInterfaceImpl(IUnknown *from, REFIID riid, void **ppvObject) {
if (riid == __uuidof(T)) {
*ppvObject = static_cast<T *>(from);
from->AddRef();
return true;
}
return false;
}
class NetworkListManagerEvents : public INetworkListManagerEvents {
public:
NetworkListManagerEvents() = default;
virtual ~NetworkListManagerEvents() = default;
bool start();
void finish();
HRESULT STDMETHODCALLTYPE QueryInterface(
REFIID riid,
void **ppvObject) override;
ULONG STDMETHODCALLTYPE AddRef() override { return ++_ref; }
ULONG STDMETHODCALLTYPE Release() override {
if (--_ref == 0) {
delete this;
return 0;
}
return _ref;
}
HRESULT STDMETHODCALLTYPE ConnectivityChanged(
NLM_CONNECTIVITY newConnectivity) override;
rpl::variable<bool> available;
private:
ComPtr<INetworkListManager> _networkListManager = nullptr;
ComPtr<IConnectionPoint> _connectionPoint = nullptr;
std::atomic<ULONG> _ref = 0;
DWORD _cookie = 0;
};
bool NetworkListManagerEvents::start() {
auto hr = CoCreateInstance(
CLSID_NetworkListManager,
nullptr,
CLSCTX_INPROC_SERVER,
IID_INetworkListManager,
&_networkListManager);
if (FAILED(hr)) {
LOG(("NetworkListManagerEvents: "
"Could not get a NetworkListManager instance: %1").arg(
ErrorStringFromHResult(hr)));
return false;
}
ComPtr<IConnectionPointContainer> connectionPointContainer;
hr = _networkListManager.As(&connectionPointContainer);
if (SUCCEEDED(hr)) {
hr = connectionPointContainer->FindConnectionPoint(
IID_INetworkListManagerEvents,
&_connectionPoint);
}
if (FAILED(hr)) {
LOG(("NetworkListManagerEvents: Failed to get connection point for "
"network list manager events: %1").arg(
ErrorStringFromHResult(hr)));
return false;
}
hr = _connectionPoint->Advise(this, &_cookie);
if (FAILED(hr)) {
LOG(("NetworkListManagerEvents: "
"Failed to subscribe to network connectivity events: %1").arg(
ErrorStringFromHResult(hr)));
return false;
}
// Update connectivity since it might have
// changed since this class was constructed
NLM_CONNECTIVITY connectivity;
hr = _networkListManager->GetConnectivity(&connectivity);
if (FAILED(hr)) {
LOG(("NetworkListManagerEvents: Could not get connectivity: %1").arg(
ErrorStringFromHResult(hr)));
return false;
}
available = connectivity != NLM_CONNECTIVITY_DISCONNECTED;
return true;
}
void NetworkListManagerEvents::finish() {
if (_connectionPoint) {
auto hr = _connectionPoint->Unadvise(_cookie);
if (FAILED(hr)) {
LOG(("NetworkListManagerEvents: "
"Failed to unsubscribe from "
"network connectivity events: %1").arg(
ErrorStringFromHResult(hr)));
} else {
_cookie = 0;
}
}
}
HRESULT STDMETHODCALLTYPE NetworkListManagerEvents::QueryInterface(
REFIID riid,
void **ppvObject) {
if (!ppvObject) {
return E_INVALIDARG;
}
return QueryInterfaceImpl<IUnknown>(this, riid, ppvObject)
|| QueryInterfaceImpl<INetworkListManagerEvents>(
this,
riid,
ppvObject)
? S_OK
: E_NOINTERFACE;
}
HRESULT STDMETHODCALLTYPE NetworkListManagerEvents::ConnectivityChanged(
NLM_CONNECTIVITY newConnectivity) {
// This function is run on a different thread than 'monitor'
// is created on, so we need to run it on that thread
available = newConnectivity != NLM_CONNECTIVITY_DISCONNECTED;
return S_OK;
}
class NetworkReachabilityImpl : public NetworkReachability {
public:
NetworkReachabilityImpl() {
const auto hr = CoInitialize(nullptr);
if (FAILED(hr)) {
LOG(("NetworkReachabilityImpl: Failed to initialize COM: %1").arg(
ErrorStringFromHResult(hr)));
_comInitFailed = true;
return;
}
_managerEvents = new NetworkListManagerEvents();
_valid = _managerEvents->start();
}
~NetworkReachabilityImpl() {
if (!_comInitFailed) {
_managerEvents->finish();
_managerEvents = nullptr;
CoUninitialize();
}
}
bool valid() const {
return _valid;
}
rpl::producer<bool> availableValue() const override {
return _managerEvents->available.value();
}
private:
ComPtr<NetworkListManagerEvents> _managerEvents = nullptr;
bool _comInitFailed = false;
bool _valid = false;
};
} // namespace
std::unique_ptr<NetworkReachability> NetworkReachability::Create() {
auto result = std::make_unique<NetworkReachabilityImpl>();
if (!result->valid()) {
return nullptr;
}
return result;
}
} // namespace base::Platform
#endif // Qt < 6.2.0

View File

@@ -0,0 +1,92 @@
// This file is part of Desktop App Toolkit,
// a set of libraries for developing nice desktop applications.
//
// For license and copyright information please follow this link:
// https://github.com/desktop-app/legal/blob/master/LEGAL
//
#include "base/platform/win/base_power_save_blocker_win.h"
#include "base/platform/base_platform_info.h"
#include <windows.h>
// Thanks Chromium: services/device/wake_lock/power_save_blocker
namespace base::Platform {
namespace {
using namespace ::Platform;
HANDLE GlobalHandles[kPowerSaveBlockTypeCount];
HANDLE CreatePowerRequest(
POWER_REQUEST_TYPE type,
const QString &description) {
if (type == PowerRequestExecutionRequired && !IsWindows8OrGreater()) {
return INVALID_HANDLE_VALUE;
}
auto context = REASON_CONTEXT{ 0 };
context.Version = POWER_REQUEST_CONTEXT_VERSION;
context.Flags = POWER_REQUEST_CONTEXT_SIMPLE_STRING;
auto wide = description.toStdWString();
wide.push_back(wchar_t(0)); // So that .data() will be 0-terminated.
context.Reason.SimpleReasonString = wide.data();
const auto handle = ::PowerCreateRequest(&context);
if (!handle || handle == INVALID_HANDLE_VALUE) {
return INVALID_HANDLE_VALUE;
}
if (::PowerSetRequest(handle, type)) {
return handle;
}
// Something went wrong.
CloseHandle(handle);
return INVALID_HANDLE_VALUE;
}
// Takes ownership of the |handle|.
void DeletePowerRequest(POWER_REQUEST_TYPE type, HANDLE handle) {
if (!handle || handle == INVALID_HANDLE_VALUE) {
return;
} else if (type == PowerRequestExecutionRequired
&& !IsWindows8OrGreater()) {
return;
}
::PowerClearRequest(handle, type);
CloseHandle(handle);
}
POWER_REQUEST_TYPE RequestType(PowerSaveBlockType type) {
if (type == PowerSaveBlockType::PreventDisplaySleep) {
return PowerRequestDisplayRequired;
} else if (!IsWindows8OrGreater()) {
return PowerRequestSystemRequired;
}
return PowerRequestExecutionRequired;
}
} // namespace
void BlockPowerSave(
PowerSaveBlockType type,
const QString &description,
QPointer<QWindow> window) {
Expects(!GlobalHandles[PowerSaveBlockTypeIndex(type)]);
const auto requestType = RequestType(type);
GlobalHandles[PowerSaveBlockTypeIndex(type)] = CreatePowerRequest(
requestType,
description);
}
void UnblockPowerSave(PowerSaveBlockType type, QPointer<QWindow> window) {
DeletePowerRequest(
RequestType(type),
base::take(GlobalHandles[PowerSaveBlockTypeIndex(type)]));
}
} // namespace base::Platform

View File

@@ -0,0 +1,9 @@
// This file is part of Desktop App Toolkit,
// a set of libraries for developing nice desktop applications.
//
// For license and copyright information please follow this link:
// https://github.com/desktop-app/legal/blob/master/LEGAL
//
#pragma once
#include "base/platform/base_platform_power_save_blocker.h"

View File

@@ -0,0 +1,51 @@
// This file is part of Desktop App Toolkit,
// a set of libraries for developing nice desktop applications.
//
// For license and copyright information please follow this link:
// https://github.com/desktop-app/legal/blob/master/LEGAL
//
#include "base/platform/win/base_process_win.h"
#include <windows.h>
namespace base::Platform {
namespace {
struct FullWindowId {
int64 pid = 0;
WId windowId = 0;
};
BOOL CALLBACK ActivateProcess(HWND hWnd, LPARAM lParam) {
const auto fullId = reinterpret_cast<const FullWindowId*>(lParam);
if (reinterpret_cast<WId>(hWnd) != fullId->windowId) {
return TRUE;
}
DWORD dwProcessId;
::GetWindowThreadProcessId(hWnd, &dwProcessId);
if (static_cast<int64>(dwProcessId) != fullId->pid) {
return TRUE;
}
::SetForegroundWindow(hWnd);
::SetFocus(hWnd);
return FALSE;
}
} // namespace
void ActivateProcessWindow(int64 pid, WId windowId) {
const auto fullId = FullWindowId{ pid, windowId };
::EnumWindows(
reinterpret_cast<WNDENUMPROC>(ActivateProcess),
reinterpret_cast<LPARAM>(&fullId));
}
void ActivateThisProcessWindow(WId windowId) {
if (const auto handle = reinterpret_cast<HWND>(windowId)) {
::SetFocus(handle);
}
}
} // namespace base::Platform

View File

@@ -0,0 +1,9 @@
// This file is part of Desktop App Toolkit,
// a set of libraries for developing nice desktop applications.
//
// For license and copyright information please follow this link:
// https://github.com/desktop-app/legal/blob/master/LEGAL
//
#pragma once
#include "base/platform/base_platform_process.h"

View File

@@ -0,0 +1,356 @@
// This file is part of Desktop App Toolkit,
// a set of libraries for developing nice desktop applications.
//
// For license and copyright information please follow this link:
// https://github.com/desktop-app/legal/blob/master/LEGAL
//
#include "base/platform/base_platform_system_media_controls.h"
#include <unknwn.h> // Conversion from winrt::guid_of to GUID.
#include "base/integration.h"
#include "base/platform/win/base_info_win.h"
#include "base/platform/win/base_windows_winrt.h"
#include <winrt/Windows.Foundation.h>
#include <winrt/Windows.Media.h>
#include <winrt/Windows.Storage.Streams.h>
#include <systemmediatransportcontrolsinterop.h>
#include <QtCore/QBuffer>
#include <QtCore/QAbstractNativeEventFilter>
#include <QtGui/QImage>
#include <QtGui/QWindow>
#include <QtWidgets/QWidget>
namespace winrt {
namespace Streams {
using namespace Windows::Storage::Streams;
} // namespace Streams
namespace Media {
using namespace Windows::Media;
} // namespace Media
} // namespace winrt
namespace base::Platform {
struct SystemMediaControls::Private : QAbstractNativeEventFilter {
using IReferenceStatics
= winrt::Streams::IRandomAccessStreamReferenceStatics;
Private()
: controls(nullptr)
, referenceStatics(winrt::get_activation_factory
<winrt::Streams::RandomAccessStreamReference,
IReferenceStatics>()) {
}
bool nativeEventFilter(
const QByteArray &eventType,
void *message,
native_event_filter_result *result) override;
QWidget parent;
HWND hwnd = nullptr;
winrt::Media::SystemMediaTransportControls controls;
winrt::Media::ISystemMediaTransportControlsDisplayUpdater displayUpdater;
winrt::Media::IMusicDisplayProperties displayProperties;
winrt::Streams::DataWriter iconDataWriter;
const IReferenceStatics referenceStatics;
winrt::event_token eventToken;
bool initialized = false;
rpl::event_stream<SystemMediaControls::Command> commandRequests;
};
bool SystemMediaControls::Private::nativeEventFilter(
const QByteArray &eventType,
void *message,
native_event_filter_result *result) {
Expects(hwnd != nullptr);
const auto msg = static_cast<MSG*>(message);
if (msg->hwnd == hwnd
&& msg->message == WM_NCACTIVATE
&& msg->wParam) {
base::Integration::Instance().enterFromEventLoop([&] {
commandRequests.fire(Command::Raise);
});
}
return false;
}
namespace {
winrt::Media::MediaPlaybackStatus SmtcPlaybackStatus(
SystemMediaControls::PlaybackStatus status) {
switch (status) {
case SystemMediaControls::PlaybackStatus::Playing:
return winrt::Media::MediaPlaybackStatus::Playing;
case SystemMediaControls::PlaybackStatus::Paused:
return winrt::Media::MediaPlaybackStatus::Paused;
case SystemMediaControls::PlaybackStatus::Stopped:
return winrt::Media::MediaPlaybackStatus::Stopped;
}
Unexpected("SmtcPlaybackStatus in SystemMediaControls");
}
auto SMTCButtonToCommand(
winrt::Media::SystemMediaTransportControlsButton button) {
using SMTCButton = winrt::Media::SystemMediaTransportControlsButton;
using Command = SystemMediaControls::Command;
switch (button) {
case SMTCButton::Play:
return Command::Play;
case SMTCButton::Pause:
return Command::Pause;
case SMTCButton::Next:
return Command::Next;
case SMTCButton::Previous:
return Command::Previous;
case SMTCButton::Stop:
return Command::Stop;
case SMTCButton::Record:
case SMTCButton::FastForward:
case SMTCButton::Rewind:
case SMTCButton::ChannelUp:
case SMTCButton::ChannelDown:
return Command::None;
}
return Command::None;
}
} // namespace
SystemMediaControls::SystemMediaControls()
: _private(std::make_unique<Private>()) {
}
SystemMediaControls::~SystemMediaControls() {
if (_private->eventToken) {
_private->controls.ButtonPressed(base::take(_private->eventToken));
clearMetadata();
}
}
bool SystemMediaControls::init() {
if (_private->initialized) {
return _private->initialized;
}
_private->parent.hide();
_private->parent.createWinId();
const auto window = _private->parent.windowHandle();
if (!window) {
return false;
}
// Should be moved to separated file.
const auto hwnd = reinterpret_cast<HWND>(window->winId());
if (!hwnd) {
return false;
}
const auto interop = WinRT::Try([&] {
return winrt::get_activation_factory<
winrt::Media::SystemMediaTransportControls,
ISystemMediaTransportControlsInterop>();
}).value_or(nullptr);
if (!interop) {
return false;
}
winrt::com_ptr<winrt::Media::ISystemMediaTransportControls> icontrols;
auto hr = interop->GetForWindow(
hwnd,
winrt::guid_of<winrt::Media::ISystemMediaTransportControls>(),
icontrols.put_void());
if (FAILED(hr)) {
return false;
}
_private->controls = winrt::Media::SystemMediaTransportControls(
icontrols.detach(),
winrt::take_ownership_from_abi);
using ButtonsEventArgs =
winrt::Media::SystemMediaTransportControlsButtonPressedEventArgs;
const auto result = WinRT::Try([&] {
// Buttons handler.
_private->eventToken = _private->controls.ButtonPressed([=](
const auto &sender,
const ButtonsEventArgs &args) {
// This lambda is called in a non-main thread.
crl::on_main([=] {
const auto button = WinRT::Try([&] {
return SMTCButtonToCommand(args.Button());
}).value_or(SystemMediaControls::Command::None);
_private->commandRequests.fire_copy(button);
});
});
_private->controls.IsEnabled(true);
auto displayUpdater = _private->controls.DisplayUpdater();
displayUpdater.Type(winrt::Media::MediaPlaybackType::Music);
_private->displayProperties = displayUpdater.MusicProperties();
_private->displayUpdater = std::move(displayUpdater);
});
_private->initialized = result;
if (result) {
_private->hwnd = hwnd;
qApp->installNativeEventFilter(_private.get());
}
return result;
}
void SystemMediaControls::setApplicationName(const QString &name) {
}
void SystemMediaControls::setEnabled(bool enabled) {
WinRT::Try([&] { _private->controls.IsEnabled(enabled); });
}
void SystemMediaControls::setIsNextEnabled(bool value) {
WinRT::Try([&] { _private->controls.IsNextEnabled(value); });
}
void SystemMediaControls::setIsPreviousEnabled(bool value) {
WinRT::Try([&] { _private->controls.IsPreviousEnabled(value); });
}
void SystemMediaControls::setIsPlayPauseEnabled(bool value) {
WinRT::Try([&] {
_private->controls.IsPlayEnabled(value);
_private->controls.IsPauseEnabled(value);
});
}
void SystemMediaControls::setIsStopEnabled(bool value) {
WinRT::Try([&] { _private->controls.IsStopEnabled(value); });
}
void SystemMediaControls::setPlaybackStatus(
SystemMediaControls::PlaybackStatus status) {
WinRT::Try([&] {
_private->controls.PlaybackStatus(SmtcPlaybackStatus(status));
});
}
void SystemMediaControls::setLoopStatus(LoopStatus status) {
}
void SystemMediaControls::setShuffle(bool value) {
}
void SystemMediaControls::setTitle(const QString &title) {
const auto htitle = winrt::to_hstring(title.toStdString());
WinRT::Try([&] { _private->displayProperties.Title(htitle); });
}
void SystemMediaControls::setArtist(const QString &artist) {
const auto hartist = winrt::to_hstring(artist.toStdString());
WinRT::Try([&] { _private->displayProperties.Artist(hartist); });
}
void SystemMediaControls::setThumbnail(const QImage &thumbnail) {
auto thumbStream = winrt::Streams::InMemoryRandomAccessStream();
_private->iconDataWriter = winrt::Streams::DataWriter(thumbStream);
const auto bitmapRawData = [&] {
QByteArray bytes;
QBuffer buffer(&bytes);
buffer.open(QIODevice::WriteOnly);
thumbnail.save(&buffer, "JPG", 87);
buffer.close();
return std::vector<unsigned char>(bytes.begin(), bytes.end());
}();
WinRT::Try([&] {
_private->iconDataWriter.WriteBytes(bitmapRawData);
using namespace winrt::Windows;
_private->iconDataWriter.StoreAsync().Completed([=,
thumbStream = std::move(thumbStream)](
Foundation::IAsyncOperation<uint32> asyncOperation,
Foundation::AsyncStatus status) {
// Check the async operation completed successfully.
if ((status != Foundation::AsyncStatus::Completed)
|| FAILED(asyncOperation.ErrorCode())) {
return;
}
WinRT::Try([&] {
_private->displayUpdater.Thumbnail(
_private->referenceStatics.CreateFromStream(thumbStream));
_private->displayUpdater.Update();
});
});
});
}
void SystemMediaControls::setDuration(int duration) {
}
void SystemMediaControls::setPosition(int position) {
}
void SystemMediaControls::setVolume(float64 volume) {
}
void SystemMediaControls::clearThumbnail() {
WinRT::Try([&] {
_private->displayUpdater.Thumbnail(nullptr);
_private->displayUpdater.Update();
});
}
void SystemMediaControls::clearMetadata() {
WinRT::Try([&] {
_private->displayUpdater.ClearAll();
_private->controls.IsEnabled(false);
});
}
void SystemMediaControls::updateDisplay() {
WinRT::Try([&] {
_private->controls.IsEnabled(true);
_private->displayUpdater.Type(winrt::Media::MediaPlaybackType::Music);
_private->displayUpdater.Update();
});
}
auto SystemMediaControls::commandRequests() const
-> rpl::producer<SystemMediaControls::Command> {
return _private->commandRequests.events();
}
rpl::producer<float64> SystemMediaControls::seekRequests() const {
return rpl::never<float64>();
}
rpl::producer<float64> SystemMediaControls::volumeChangeRequests() const {
return rpl::never<float64>();
}
rpl::producer<> SystemMediaControls::updatePositionRequests() const {
return rpl::never<>();
}
bool SystemMediaControls::seekingSupported() const {
return false;
}
bool SystemMediaControls::volumeSupported() const {
return false;
}
bool SystemMediaControls::Supported() {
return ::Platform::IsWindows10OrGreater();
}
} // namespace base::Platform

View File

@@ -0,0 +1,100 @@
// This file is part of Desktop App Toolkit,
// a set of libraries for developing nice desktop applications.
//
// For license and copyright information please follow this link:
// https://github.com/desktop-app/legal/blob/master/LEGAL
//
#include "base/platform/win/base_system_unlock_win.h"
#include "base/platform/win/base_windows_winrt.h"
#include <QtWidgets/QWidget>
#include <winrt/Windows.Foundation.h>
#include <winrt/Windows.Security.Credentials.UI.h>
#include <UserConsentVerifierInterop.h>
namespace base {
namespace {
using namespace winrt::Windows::Security::Credentials::UI;
using AsyncStatus = winrt::Windows::Foundation::AsyncStatus;
using winrt::Windows::Foundation::IAsyncOperation;
} // namespace
rpl::producer<SystemUnlockAvailability> SystemUnlockStatus(
bool lookupDetails) {
static auto result = rpl::variable<SystemUnlockAvailability>();
WinRT::Try([&] {
UserConsentVerifier::CheckAvailabilityAsync().Completed([](
IAsyncOperation<UserConsentVerifierAvailability> that,
AsyncStatus status) {
const auto results = WinRT::Try([&] {
return that.GetResults();
});
const auto available = (status == AsyncStatus::Completed)
&& (results == UserConsentVerifierAvailability::Available);
crl::on_main([=] {
result = SystemUnlockAvailability{
.known = true,
.available = available,
};
});
});
});
return result.value();
}
void SuggestSystemUnlock(
not_null<QWidget*> parent,
const QString &text,
Fn<void(SystemUnlockResult)> done) {
const auto window = parent->window();
window->createWinId();
const auto handle = reinterpret_cast<HWND>(window->winId());
if (!handle) {
done(SystemUnlockResult::Cancelled);
return;
}
const auto completed = [=](
IAsyncOperation<UserConsentVerificationResult> that,
AsyncStatus status) -> void {
const auto results = WinRT::Try([&] {
return that.GetResults();
});
done((status != AsyncStatus::Completed)
? SystemUnlockResult::Cancelled
: (results == UserConsentVerificationResult::Verified)
? SystemUnlockResult::Success
: (results == UserConsentVerificationResult::RetriesExhausted)
? SystemUnlockResult::FloodError
: SystemUnlockResult::Cancelled);
};
const auto success = WinRT::Try([&] {
const auto interop = winrt::get_activation_factory<
UserConsentVerifier,
IUserConsentVerifierInterop>();
if (!interop) {
done(SystemUnlockResult::Cancelled);
return;
}
const auto wtext = text.toStdString();
const auto htext = winrt::to_hstring(wtext);
winrt::capture<IAsyncOperation<UserConsentVerificationResult>>(
interop,
&IUserConsentVerifierInterop::RequestVerificationForWindowAsync,
handle,
reinterpret_cast<HSTRING>(winrt::get_abi(htext))
).Completed(completed);
});
if (!success) {
done(SystemUnlockResult::Cancelled);
return;
}
}
} // namespace base

View File

@@ -0,0 +1,9 @@
// This file is part of Desktop App Toolkit,
// a set of libraries for developing nice desktop applications.
//
// For license and copyright information please follow this link:
// https://github.com/desktop-app/legal/blob/master/LEGAL
//
#pragma once
#include "base/system_unlock.h"

View File

@@ -0,0 +1,207 @@
// This file is part of Desktop App Toolkit,
// a set of libraries for developing nice desktop applications.
//
// For license and copyright information please follow this link:
// https://github.com/desktop-app/legal/blob/master/LEGAL
//
#include "base/platform/win/base_url_scheme_win.h"
#include <QtCore/QDir>
#include <windows.h>
#include <array>
namespace base::Platform {
namespace {
enum class Mode {
Check,
Write,
};
bool OpenRegKey(Mode mode, const QString &key, PHKEY rkey) {
const auto wkey = key.toStdWString();
const auto opened = RegOpenKeyEx(
HKEY_CURRENT_USER,
wkey.c_str(),
0,
KEY_QUERY_VALUE | KEY_WRITE,
rkey);
if (opened == ERROR_SUCCESS) {
return true;
} else if (mode != Mode::Write) {
return false;
}
const auto created = RegCreateKeyEx(
HKEY_CURRENT_USER,
wkey.c_str(),
0,
nullptr,
REG_OPTION_NON_VOLATILE,
KEY_QUERY_VALUE | KEY_WRITE,
nullptr,
rkey,
nullptr);
if (created == ERROR_SUCCESS) {
return true;
}
return false;
}
bool RemoveRegKey(const QString &key) {
const auto wkey = key.toStdWString();
return (RegDeleteKey(HKEY_CURRENT_USER, wkey.c_str()) == ERROR_SUCCESS);
}
bool SetKeyValue(Mode mode, HKEY rkey, const QString &name, QString value) {
static_assert(sizeof(WCHAR) == 2);
static_assert(sizeof(wchar_t) == 2);
constexpr auto kBufferSize = 4096;
constexpr auto kBufferByteSize = kBufferSize * 2;
auto defaultType = DWORD();
auto defaultByteSize = DWORD(kBufferByteSize);
auto defaultByteValue = std::array<BYTE, kBufferByteSize>{ 0 };
const auto wname = name.toStdWString();
const auto queried = RegQueryValueEx(
rkey,
wname.c_str(),
nullptr,
&defaultType,
defaultByteValue.data(),
&defaultByteSize);
auto defaultValue = std::array<WCHAR, kBufferSize>{ 0 };
memcpy(defaultValue.data(), defaultByteValue.data(), kBufferByteSize);
if ((queried == ERROR_SUCCESS)
&& (defaultType == REG_SZ)
&& (defaultByteSize == (value.size() + 1) * 2)
&& (QString::fromWCharArray(
defaultValue.data(),
value.size()) == value)) {
return true;
} else if (mode != Mode::Write) {
return false;
}
const auto wvalue = value.toStdWString();
auto byteValue = std::vector<BYTE>((wvalue.size() + 1) * 2, 0);
memcpy(byteValue.data(), wvalue.data(), wvalue.size() * 2);
const auto written = RegSetValueEx(
rkey,
wname.c_str(),
0,
REG_SZ,
byteValue.data(),
byteValue.size());
if (written == ERROR_SUCCESS) {
return true;
}
return false;
}
bool RemoveKeyValue(HKEY rkey, const QString &name) {
const auto wname = name.toStdWString();
return (RegDeleteValue(rkey, wname.c_str()) == ERROR_SUCCESS);
}
bool RegisterLegacy(Mode mode, const UrlSchemeDescriptor &d) {
auto rkey = HKEY();
const auto exe = QDir::toNativeSeparators(d.executable);
const auto keyBase = "Software\\Classes\\" + d.protocol;
const auto command = '"' + exe + "\" " + d.arguments + " -- \"%1\"";
return OpenRegKey(mode, keyBase, &rkey)
&& SetKeyValue(mode, rkey, "URL Protocol", QString())
&& SetKeyValue(mode, rkey, nullptr, "URL:" + d.protocolName)
&& OpenRegKey(mode, keyBase + "\\DefaultIcon", &rkey)
&& SetKeyValue(mode, rkey, nullptr, '"' + exe + ",1\"")
&& OpenRegKey(mode, keyBase + "\\shell", &rkey)
&& OpenRegKey(mode, keyBase + "\\shell\\open", &rkey)
&& OpenRegKey(mode, keyBase + "\\shell\\open\\command", &rkey)
&& SetKeyValue(mode, rkey, nullptr, command);
}
void UnregisterLegacy(const UrlSchemeDescriptor &d) {
const auto keyBase = "Software\\Classes\\" + d.protocol;
RemoveRegKey(keyBase + "\\shell\\open\\command");
RemoveRegKey(keyBase + "\\shell\\open");
RemoveRegKey(keyBase + "\\shell");
RemoveRegKey(keyBase + "\\DefaultIcon");
RemoveRegKey(keyBase);
}
bool RegisterDefaultProgram(Mode mode, const UrlSchemeDescriptor &d) {
auto rkey = HKEY();
const auto exe = QDir::toNativeSeparators(d.executable);
const auto namedProtocol = d.shortAppName + '.' + d.protocol;
const auto namedBase = "Software\\Classes\\" + namedProtocol;
const auto longNamedBase = "Software\\" + d.longAppName;
const auto registered = "SOFTWARE\\" + d.longAppName + "\\Capabilities";
const auto command = '"' + exe + "\" " + d.arguments + " -- \"%1\"";
return OpenRegKey(mode, namedBase, &rkey)
&& OpenRegKey(mode, namedBase + "\\DefaultIcon", &rkey)
&& SetKeyValue(mode, rkey, nullptr, '"' + exe + ",1\"")
&& OpenRegKey(mode, namedBase + "\\shell", &rkey)
&& OpenRegKey(mode, namedBase + "\\shell\\open", &rkey)
&& OpenRegKey(mode, namedBase + "\\shell\\open\\command", &rkey)
&& SetKeyValue(mode, rkey, nullptr, command)
&& OpenRegKey(mode, longNamedBase, &rkey)
&& OpenRegKey(mode, longNamedBase + "\\Capabilities", &rkey)
&& SetKeyValue(mode, rkey, "ApplicationName", d.displayAppName)
&& SetKeyValue(
mode,
rkey,
"ApplicationDescription",
d.displayAppDescription)
&& OpenRegKey(
mode,
longNamedBase + "\\Capabilities\\UrlAssociations",
&rkey)
&& SetKeyValue(mode, rkey, d.protocol, namedProtocol)
&& OpenRegKey(mode, "Software\\RegisteredApplications", &rkey)
&& SetKeyValue(mode, rkey, d.displayAppName, registered);
}
void UnregisterDefaultProgram(const UrlSchemeDescriptor &d) {
auto rkey = HKEY();
const auto namedProtocol = d.shortAppName + '.' + d.protocol;
const auto namedBase = "Software\\Classes\\" + namedProtocol;
const auto longNamedBase = "Software\\" + d.longAppName;
if (OpenRegKey(Mode::Check, "Software\\RegisteredApplications", &rkey)) {
RemoveKeyValue(rkey, d.displayAppName);
}
RemoveRegKey(longNamedBase + "\\Capabilities\\UrlAssociations");
RemoveRegKey(longNamedBase + "\\Capabilities");
RemoveRegKey(longNamedBase);
RemoveRegKey(namedBase + "\\shell\\open\\command");
RemoveRegKey(namedBase + "\\shell\\open");
RemoveRegKey(namedBase + "\\shell");
RemoveRegKey(namedBase + "\\DefaultIcon");
RemoveRegKey(namedBase);
}
bool FullRegister(Mode mode, const UrlSchemeDescriptor &descriptor) {
return RegisterDefaultProgram(mode, descriptor)
&& RegisterLegacy(mode, descriptor);
}
} // namespace
bool CheckUrlScheme(const UrlSchemeDescriptor &descriptor) {
return FullRegister(Mode::Check, descriptor);
}
void RegisterUrlScheme(const UrlSchemeDescriptor &descriptor) {
FullRegister(Mode::Write, descriptor);
}
void UnregisterUrlScheme(const UrlSchemeDescriptor &descriptor) {
if (CheckUrlScheme(descriptor)) {
UnregisterDefaultProgram(descriptor);
UnregisterLegacy(descriptor);
}
}
} // namespace base::Platform

View File

@@ -0,0 +1,9 @@
// This file is part of Desktop App Toolkit,
// a set of libraries for developing nice desktop applications.
//
// For license and copyright information please follow this link:
// https://github.com/desktop-app/legal/blob/master/LEGAL
//
#pragma once
#include "base/platform/base_platform_url_scheme.h"

View File

@@ -0,0 +1,137 @@
// This file is part of Desktop App Toolkit,
// a set of libraries for developing nice desktop applications.
//
// For license and copyright information please follow this link:
// https://github.com/desktop-app/legal/blob/master/LEGAL
//
#pragma once
#include <combaseapi.h>
namespace base {
template <typename Item>
struct FreePolicyNullTerminated {
void destroy(Item *data) {
}
struct Sentinel {
friend inline bool operator==(Item *data, Sentinel) {
return !*data;
}
friend inline bool operator!=(Item *data, Sentinel) {
return !!*data;
}
};
[[nodiscard]] Sentinel sentinel(const Item *data) const {
return Sentinel();
}
};
template <typename Item>
struct FreePolicyCounted {
void destroy(Item *data) {
if (data) {
for (auto i = 0; i != count; ++i) {
data[i].~Item();
}
}
}
[[nodiscard]] Item *sentinel(Item *data) const {
return data + count;
}
[[nodiscard]] const Item *sentinel(const Item *data) const {
return data + count;
}
UINT32 count = 0;
};
template <typename, template <typename...> class>
struct instance_of_t : std::false_type {
};
template <template <typename...> class Template, typename ...Args>
struct instance_of_t<Template<Args...>, Template> : std::true_type {
};
template <typename Type, template <typename...> class Template>
inline constexpr bool instance_of_v = instance_of_t<Type, Template>::value;
template <typename Item, typename FreePolicy>
class CoTaskMemArray final : private FreePolicy {
public:
CoTaskMemArray() = default;
~CoTaskMemArray() {
if (_data) {
FreePolicy::destroy(_data);
CoTaskMemFree(_data);
}
}
[[nodiscard]] Item *data() const {
return _data;
}
[[nodiscard]] bool valid() const {
return (_data != nullptr);
}
explicit operator bool() const {
return _data != nullptr;
}
[[nodiscard]] Item &operator[](int index) {
return _data[index];
}
[[nodiscard]] const Item &operator[](int index) const {
return _data[index];
}
[[nodiscard]] Item *begin() {
return _data;
}
[[nodiscard]] auto end() {
return FreePolicy::sentinel(_data);
}
[[nodiscard]] const Item *begin() const {
return _data;
}
[[nodiscard]] auto end() const {
return FreePolicy::sentinel(_data);
}
[[nodiscard]] const Item *cbegin() const {
return _data;
}
[[nodiscard]] auto cend() const {
return FreePolicy::sentinel(_data);
}
[[nodiscard]] auto put() {
if constexpr (instance_of_v<Item, CoTaskMemArray>) {
static_assert(sizeof(*_data) == sizeof(void*));
return reinterpret_cast<decltype(_data->put())*>(&_data);
} else {
return &_data;
}
}
[[nodiscard]] UINT32 *put_size() {
return &static_cast<FreePolicy*>(this)->count;
}
[[nodiscard]] int size() const {
return int(static_cast<const FreePolicy*>(this)->count);
}
private:
Item *_data = nullptr;
};
using CoTaskMemString = CoTaskMemArray<
WCHAR,
FreePolicyNullTerminated<WCHAR>>;
using CoTaskMemStringArray = CoTaskMemArray<
CoTaskMemString,
FreePolicyCounted<CoTaskMemString>>;
} // namespace base

View File

@@ -0,0 +1,25 @@
// This file is part of Desktop App Toolkit,
// a set of libraries for developing nice desktop applications.
//
// For license and copyright information please follow this link:
// https://github.com/desktop-app/legal/blob/master/LEGAL
//
#pragma once
#include <Unknwn.h>
#ifdef min
#undef min
#endif // min
#ifdef max
#undef max
#endif // max
#define min(a, b) ((a) < (b) ? (a) : (b))
#define max(a, b) ((a) < (b) ? (b) : (a))
#include <gdiplus.h>
#undef min
#undef max

View File

@@ -0,0 +1,14 @@
// This file is part of Desktop App Toolkit,
// a set of libraries for developing nice desktop applications.
//
// For license and copyright information please follow this link:
// https://github.com/desktop-app/legal/blob/master/LEGAL
//
#pragma once
#include <rpc.h>
#include <rpcndr.h>
#ifdef small
#undef small
#endif // small

View File

@@ -0,0 +1,158 @@
// This file is part of Desktop App Toolkit,
// a set of libraries for developing nice desktop applications.
//
// For license and copyright information please follow this link:
// https://github.com/desktop-app/legal/blob/master/LEGAL
//
#include "base/platform/win/base_windows_safe_library.h"
#ifdef QT_VERSION
#include "base/debug_log.h"
#else
#define LOG(...) (void)0
#endif
#include <string>
#include <array>
#define LOAD_SYMBOL(lib, name) ::base::Platform::LoadMethod(lib, #name, name)
namespace base {
namespace Platform {
namespace {
constexpr auto kMaxPathLong = 32767;
__declspec(noreturn) void FatalError(
const std::wstring &text,
DWORD error = 0) {
const auto lastError = error ? error : GetLastError();
const auto full = lastError
? (text + L"\n\nError Code: " + std::to_wstring(lastError))
: text;
MessageBox(nullptr, full.c_str(), L"Fatal Error", MB_ICONERROR);
std::abort();
}
void CheckDynamicLibraries() {
auto exePath = std::array<WCHAR, kMaxPathLong + 1>{ 0 };
const auto exeLength = GetModuleFileName(
nullptr,
exePath.data(),
kMaxPathLong + 1);
if (!exeLength || exeLength >= kMaxPathLong + 1) {
FatalError(L"Could not get executable path!");
}
const auto exe = std::wstring(exePath.data());
const auto last1 = exe.find_last_of('\\');
const auto last2 = exe.find_last_of('/');
const auto last = std::max(
(last1 == std::wstring::npos) ? -1 : int(last1),
(last2 == std::wstring::npos) ? -1 : int(last2));
if (last < 0) {
FatalError(L"Could not get executable directory!");
}
const auto search = exe.substr(0, last + 1) + L"*.dll";
auto findData = WIN32_FIND_DATA();
const auto findHandle = FindFirstFile(search.c_str(), &findData);
if (findHandle == INVALID_HANDLE_VALUE) {
const auto error = GetLastError();
if (error == ERROR_FILE_NOT_FOUND) {
return;
}
FatalError(L"Could not enumerate executable path!", error);
}
do {
if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
continue;
}
const auto me = exe.substr(last + 1);
FatalError(L"Unknown DLL library \"\
" + std::wstring(findData.cFileName) + L"\" found \
in the directory with " + me + L".\n\n\
This may be a virus or a malicious program. \n\n\
Please remove all DLL libraries from this directory:\n\n\
" + exe.substr(0, last) + L"\n\n\
Alternatively, you can move " + me + L" to a new directory.");
} while (FindNextFile(findHandle, &findData));
}
BOOL (__stdcall *SetDefaultDllDirectories)(_In_ DWORD DirectoryFlags);
} // namespace
namespace details {
void *LoadMethodRaw(HINSTANCE library, LPCSTR name, WORD id) {
const auto result = GetProcAddress(library, name);
return result
? result
: id
? GetProcAddress(library, MAKEINTRESOURCEA(id))
: nullptr;
}
void ReportLoadFailure(HINSTANCE library, LPCSTR name, DWORD id) {
constexpr auto kMaxPathLong = 32767;
auto path = std::array<WCHAR, kMaxPathLong + 1>{ 0 };
const auto length = GetModuleFileName(
library,
path.data(),
kMaxPathLong);
if (length > 0 && length < kMaxPathLong) {
if (id) {
LOG(("DLL Error: Failed to load '%1' from '%2' (ID: %3)."
).arg(name
).arg(QString::fromWCharArray(path.data())
).arg(id));
} else {
LOG(("DLL Error: Failed to load '%1' from '%2'."
).arg(name
).arg(QString::fromWCharArray(path.data())));
}
} else {
if (id) {
LOG(("DLL Error: Failed to load '%1' from _unknown_ (ID: %2)."
).arg(name
).arg(id));
} else {
LOG(("DLL Error: Failed to load '%1' from _unknown_."
).arg(name));
}
}
}
} // namespace details
void InitDynamicLibraries() {
static const auto Inited = [] {
const auto kernel = LoadLibrary(L"kernel32.dll");
if (LOAD_SYMBOL(kernel, SetDefaultDllDirectories)) {
SetDefaultDllDirectories(LOAD_LIBRARY_SEARCH_SYSTEM32);
} else {
CheckDynamicLibraries();
}
return true;
}();
}
HINSTANCE SafeLoadLibrary(LPCWSTR name, bool required) {
InitDynamicLibraries();
if (const auto result = HINSTANCE(LoadLibrary(name))) {
return result;
} else if (required) {
FatalError(L"Could not load required DLL '"
+ std::wstring(name)
+ L"'!");
} else {
LOG(("DLL Error: Could not load '%1'."
).arg(QString::fromWCharArray(name)));
}
return nullptr;
}
} // namespace Platform
} // namespace base

View File

@@ -0,0 +1,38 @@
// This file is part of Desktop App Toolkit,
// a set of libraries for developing nice desktop applications.
//
// For license and copyright information please follow this link:
// https://github.com/desktop-app/legal/blob/master/LEGAL
//
#pragma once
#include <windows.h>
// We try to keep this module free of external dependencies.
namespace base::Platform {
namespace details {
[[nodiscard]] void *LoadMethodRaw(HINSTANCE library, LPCSTR name, WORD id);
void ReportLoadFailure(HINSTANCE library, LPCSTR name, DWORD id);
} // namespace details
void InitDynamicLibraries();
HINSTANCE SafeLoadLibrary(LPCWSTR name, bool required = false);
template <typename Function>
bool LoadMethod(HINSTANCE library, LPCSTR name, Function &f, WORD id = 0) {
if (!library) {
return false;
} else if (const auto ptr = details::LoadMethodRaw(library, name, id)) {
f = reinterpret_cast<Function>(ptr);
return true;
}
f = nullptr;
details::ReportLoadFailure(library, name, id);
return false;
}
} // namespace base::Platform

View File

@@ -0,0 +1,13 @@
// This file is part of Desktop App Toolkit,
// a set of libraries for developing nice desktop applications.
//
// For license and copyright information please follow this link:
// https://github.com/desktop-app/legal/blob/master/LEGAL
//
#pragma once
#include <shlobj.h>
#ifdef small
#undef small
#endif // small

View File

@@ -0,0 +1,418 @@
// This file is part of Desktop App Toolkit,
// a set of libraries for developing nice desktop applications.
//
// For license and copyright information please follow this link:
// https://github.com/desktop-app/legal/blob/master/LEGAL
//
#include "base/platform/win/base_windows_winrt.h"
#include "base/debug_log.h"
#include "base/platform/win/base_windows_safe_library.h"
#include <windows.h>
namespace base::WinRT {
namespace {
int32_t(__stdcall *RoGetActivationFactory)(void* classId, winrt::guid const& iid, void** factory);
int32_t(__stdcall *RoGetAgileReference)(uint32_t options, winrt::guid const& iid, void* object, void** reference);
int32_t(__stdcall *SetThreadpoolTimerEx)(winrt::impl::ptp_timer, void*, uint32_t, uint32_t);
int32_t(__stdcall *SetThreadpoolWaitEx)(winrt::impl::ptp_wait, void*, void*, void*);
int32_t(__stdcall *RoOriginateLanguageException)(int32_t error, void* message, void* exception);
int32_t(__stdcall *RoCaptureErrorContext)(int32_t error);
void(__stdcall *RoFailFastWithErrorContext)(int32_t);
int32_t(__stdcall *RoTransformError)(int32_t, int32_t, void*);
void*(__stdcall *LoadLibraryExW)(wchar_t const* name, void* unused, uint32_t flags);
int32_t(__stdcall *FreeLibrary)(void* library);
void*(__stdcall *GetProcAddress)(void* library, char const* name);
int32_t(__stdcall *SetErrorInfo)(uint32_t reserved, void* info);
int32_t(__stdcall *GetErrorInfo)(uint32_t reserved, void** info);
int32_t(__stdcall *CoInitializeEx)(void*, uint32_t type);
void(__stdcall *CoUninitialize)();
int32_t(__stdcall *CoCreateFreeThreadedMarshaler)(void* outer, void** marshaler);
int32_t(__stdcall *CoCreateInstance)(winrt::guid const& clsid, void* outer, uint32_t context, winrt::guid const& iid, void** object);
int32_t(__stdcall *CoGetCallContext)(winrt::guid const& iid, void** object);
int32_t(__stdcall *CoGetObjectContext)(winrt::guid const& iid, void** object);
int32_t(__stdcall *CoGetApartmentType)(int32_t* type, int32_t* qualifier);
void*(__stdcall *CoTaskMemAlloc)(std::size_t size);
void(__stdcall *CoTaskMemFree)(void* ptr);
winrt::impl::bstr(__stdcall *SysAllocString)(wchar_t const* value);
void(__stdcall *SysFreeString)(winrt::impl::bstr string);
uint32_t(__stdcall *SysStringLen)(winrt::impl::bstr string);
int32_t(__stdcall *IIDFromString)(wchar_t const* string, winrt::guid* iid);
int32_t(__stdcall *MultiByteToWideChar)(uint32_t codepage, uint32_t flags, char const* in_string, int32_t in_size, wchar_t* out_string, int32_t out_size);
int32_t(__stdcall *WideCharToMultiByte)(uint32_t codepage, uint32_t flags, wchar_t const* int_string, int32_t in_size, char* out_string, int32_t out_size, char const* default_char, int32_t* default_used);
void*(__stdcall *HeapAlloc)(void* heap, uint32_t flags, size_t bytes);
int32_t(__stdcall *HeapFree)(void* heap, uint32_t flags, void* value);
void*(__stdcall *GetProcessHeap)();
uint32_t(__stdcall *FormatMessageW)(uint32_t flags, void const* source, uint32_t code, uint32_t language, wchar_t* buffer, uint32_t size, va_list* arguments);
uint32_t(__stdcall *GetLastError)();
void(__stdcall *GetSystemTimePreciseAsFileTime)(void* result);
uintptr_t(__stdcall *VirtualQuery)(void* address, void* buffer, uintptr_t length);
void*(__stdcall *EncodePointer)(void* ptr);
int32_t(__stdcall *OpenProcessToken)(void* process, uint32_t access, void** token);
void*(__stdcall *GetCurrentProcess)();
int32_t(__stdcall *DuplicateToken)(void* existing, uint32_t level, void** duplicate);
int32_t(__stdcall *OpenThreadToken)(void* thread, uint32_t access, int32_t self, void** token);
void*(__stdcall *GetCurrentThread)();
int32_t(__stdcall *SetThreadToken)(void** thread, void* token);
void(__stdcall *AcquireSRWLockExclusive)(winrt::impl::srwlock* lock);
void(__stdcall *AcquireSRWLockShared)(winrt::impl::srwlock* lock);
uint8_t(__stdcall *TryAcquireSRWLockExclusive)(winrt::impl::srwlock* lock);
uint8_t(__stdcall *TryAcquireSRWLockShared)(winrt::impl::srwlock* lock);
void(__stdcall *ReleaseSRWLockExclusive)(winrt::impl::srwlock* lock);
void(__stdcall *ReleaseSRWLockShared)(winrt::impl::srwlock* lock);
int32_t(__stdcall *SleepConditionVariableSRW)(winrt::impl::condition_variable* cv, winrt::impl::srwlock* lock, uint32_t milliseconds, uint32_t flags);
void(__stdcall *WakeConditionVariable)(winrt::impl::condition_variable* cv);
void(__stdcall *WakeAllConditionVariable)(winrt::impl::condition_variable* cv);
void*(__stdcall *InterlockedPushEntrySList)(void* head, void* entry);
void*(__stdcall *InterlockedFlushSList)(void* head);
void*(__stdcall *CreateEventW)(void*, int32_t, int32_t, void*);
int32_t(__stdcall *SetEvent)(void*);
int32_t(__stdcall *CloseHandle)(void* hObject);
uint32_t(__stdcall *WaitForSingleObject)(void* handle, uint32_t milliseconds);
int32_t(__stdcall *TrySubmitThreadpoolCallback)(void(__stdcall *callback)(void*, void* context), void* context, void*);
winrt::impl::ptp_timer(__stdcall *CreateThreadpoolTimer)(void(__stdcall *callback)(void*, void* context, void*), void* context, void*);
void(__stdcall *SetThreadpoolTimer)(winrt::impl::ptp_timer timer, void* time, uint32_t period, uint32_t window);
void(__stdcall *CloseThreadpoolTimer)(winrt::impl::ptp_timer timer);
winrt::impl::ptp_wait(__stdcall *CreateThreadpoolWait)(void(__stdcall *callback)(void*, void* context, void*, uint32_t result), void* context, void*);
void(__stdcall *SetThreadpoolWait)(winrt::impl::ptp_wait wait, void* handle, void* timeout);
void(__stdcall *CloseThreadpoolWait)(winrt::impl::ptp_wait wait);
winrt::impl::ptp_io(__stdcall *CreateThreadpoolIo)(void* object, void(__stdcall *callback)(void*, void* context, void* overlapped, uint32_t result, std::size_t bytes, void*) noexcept, void* context, void*);
void(__stdcall *StartThreadpoolIo)(winrt::impl::ptp_io io);
void(__stdcall *CancelThreadpoolIo)(winrt::impl::ptp_io io);
void(__stdcall *CloseThreadpoolIo)(winrt::impl::ptp_io io);
winrt::impl::ptp_pool(__stdcall *CreateThreadpool)(void* reserved);
void(__stdcall *SetThreadpoolThreadMaximum)(winrt::impl::ptp_pool pool, uint32_t value);
int32_t(__stdcall *SetThreadpoolThreadMinimum)(winrt::impl::ptp_pool pool, uint32_t value);
void(__stdcall *CloseThreadpool)(winrt::impl::ptp_pool pool);
[[nodiscard]] bool Resolve() {
const auto ole32 = Platform::SafeLoadLibrary(L"ole32.dll");
const auto oleaut32 = Platform::SafeLoadLibrary(L"oleaut32.dll");
const auto combase = Platform::SafeLoadLibrary(L"combase.dll");
const auto kernel32 = Platform::SafeLoadLibrary(L"kernel32.dll");
const auto advapi32 = Platform::SafeLoadLibrary(L"advapi32.dll");
auto total = 0, resolved = 0;
#define LOAD_SYMBOL(lib, name) ++total; if (Platform::LoadMethod(lib, #name, name)) ++resolved;
LOAD_SYMBOL(combase, RoGetActivationFactory);
LOAD_SYMBOL(combase, RoGetAgileReference);
LOAD_SYMBOL(kernel32, SetThreadpoolTimerEx);
LOAD_SYMBOL(kernel32, SetThreadpoolWaitEx);
LOAD_SYMBOL(combase, RoOriginateLanguageException);
LOAD_SYMBOL(combase, RoCaptureErrorContext);
LOAD_SYMBOL(combase, RoFailFastWithErrorContext);
LOAD_SYMBOL(combase, RoTransformError);
LOAD_SYMBOL(kernel32, LoadLibraryExW);
LOAD_SYMBOL(kernel32, FreeLibrary);
LOAD_SYMBOL(kernel32, GetProcAddress);
LOAD_SYMBOL(oleaut32, SetErrorInfo);
LOAD_SYMBOL(oleaut32, GetErrorInfo);
LOAD_SYMBOL(ole32, CoInitializeEx);
LOAD_SYMBOL(ole32, CoUninitialize);
LOAD_SYMBOL(ole32, CoCreateFreeThreadedMarshaler);
LOAD_SYMBOL(ole32, CoCreateInstance);
LOAD_SYMBOL(ole32, CoGetCallContext);
LOAD_SYMBOL(ole32, CoGetObjectContext);
LOAD_SYMBOL(ole32, CoGetApartmentType);
LOAD_SYMBOL(ole32, CoTaskMemAlloc);
LOAD_SYMBOL(ole32, CoTaskMemFree);
LOAD_SYMBOL(oleaut32, SysAllocString);
LOAD_SYMBOL(oleaut32, SysFreeString);
LOAD_SYMBOL(oleaut32, SysStringLen);
LOAD_SYMBOL(ole32, IIDFromString);
LOAD_SYMBOL(kernel32, MultiByteToWideChar);
LOAD_SYMBOL(kernel32, WideCharToMultiByte);
LOAD_SYMBOL(kernel32, HeapAlloc);
LOAD_SYMBOL(kernel32, HeapFree);
LOAD_SYMBOL(kernel32, GetProcessHeap);
LOAD_SYMBOL(kernel32, FormatMessageW);
LOAD_SYMBOL(kernel32, GetLastError);
LOAD_SYMBOL(kernel32, GetSystemTimePreciseAsFileTime);
LOAD_SYMBOL(kernel32, VirtualQuery);
LOAD_SYMBOL(kernel32, EncodePointer);
LOAD_SYMBOL(kernel32, OpenProcessToken);
LOAD_SYMBOL(kernel32, GetCurrentProcess);
LOAD_SYMBOL(advapi32, DuplicateToken);
LOAD_SYMBOL(advapi32, OpenThreadToken);
LOAD_SYMBOL(kernel32, GetCurrentThread);
LOAD_SYMBOL(kernel32, SetThreadToken);
LOAD_SYMBOL(kernel32, AcquireSRWLockExclusive);
LOAD_SYMBOL(kernel32, AcquireSRWLockShared);
LOAD_SYMBOL(kernel32, TryAcquireSRWLockExclusive);
LOAD_SYMBOL(kernel32, TryAcquireSRWLockShared);
LOAD_SYMBOL(kernel32, ReleaseSRWLockExclusive);
LOAD_SYMBOL(kernel32, ReleaseSRWLockShared);
LOAD_SYMBOL(kernel32, SleepConditionVariableSRW);
LOAD_SYMBOL(kernel32, WakeConditionVariable);
LOAD_SYMBOL(kernel32, WakeAllConditionVariable);
LOAD_SYMBOL(kernel32, InterlockedPushEntrySList);
LOAD_SYMBOL(kernel32, InterlockedFlushSList);
LOAD_SYMBOL(kernel32, CreateEventW);
LOAD_SYMBOL(kernel32, SetEvent);
LOAD_SYMBOL(kernel32, CloseHandle);
LOAD_SYMBOL(kernel32, WaitForSingleObject);
LOAD_SYMBOL(kernel32, TrySubmitThreadpoolCallback);
LOAD_SYMBOL(kernel32, CreateThreadpoolTimer);
LOAD_SYMBOL(kernel32, SetThreadpoolTimer);
LOAD_SYMBOL(kernel32, CloseThreadpoolTimer);
LOAD_SYMBOL(kernel32, CreateThreadpoolWait);
LOAD_SYMBOL(kernel32, SetThreadpoolWait);
LOAD_SYMBOL(kernel32, CloseThreadpoolWait);
LOAD_SYMBOL(kernel32, CreateThreadpoolIo);
LOAD_SYMBOL(kernel32, StartThreadpoolIo);
LOAD_SYMBOL(kernel32, CancelThreadpoolIo);
LOAD_SYMBOL(kernel32, CloseThreadpoolIo);
LOAD_SYMBOL(kernel32, CreateThreadpool);
LOAD_SYMBOL(kernel32, SetThreadpoolThreadMaximum);
LOAD_SYMBOL(kernel32, SetThreadpoolThreadMinimum);
LOAD_SYMBOL(kernel32, CloseThreadpool);
#undef LOAD_SYMBOL
return (total == resolved);
}
} // namespace
[[nodiscard]] bool Supported() {
static const auto Result = Resolve();
return Result;
}
} // namespace base::WinRT
namespace P = base::WinRT;
extern "C" {
int32_t __stdcall WINRT_IMPL_RoGetActivationFactory(void* classId, winrt::guid const& iid, void** factory) noexcept {
return P::RoGetActivationFactory(classId, iid, factory);
}
int32_t __stdcall WINRT_IMPL_RoGetAgileReference(uint32_t options, winrt::guid const& iid, void* object, void** reference) noexcept {
return P::RoGetAgileReference(options, iid, object, reference);
}
int32_t __stdcall WINRT_IMPL_SetThreadpoolTimerEx(winrt::impl::ptp_timer timer, void *time, uint32_t period, uint32_t window) noexcept {
return P::SetThreadpoolTimerEx(timer, time, period, window);
}
int32_t __stdcall WINRT_IMPL_SetThreadpoolWaitEx(winrt::impl::ptp_wait wait, void *handle, void *timeout, void *reserved) noexcept {
return P::SetThreadpoolWaitEx(wait, handle, timeout, reserved);
}
int32_t __stdcall WINRT_IMPL_RoOriginateLanguageException(int32_t error, void* message, void* exception) noexcept {
return P::RoOriginateLanguageException
? P::RoOriginateLanguageException(error, message, exception)
: TRUE;
}
int32_t __stdcall WINRT_IMPL_RoCaptureErrorContext(int32_t error) noexcept {
return P::RoCaptureErrorContext(error);
}
void __stdcall WINRT_IMPL_RoFailFastWithErrorContext(int32_t error) noexcept {
return P::RoFailFastWithErrorContext(error);
}
int32_t __stdcall WINRT_IMPL_RoTransformError(int32_t error, int32_t transform, void *context) noexcept {
return P::RoTransformError(error, transform, context);
}
void* __stdcall WINRT_IMPL_LoadLibraryExW(wchar_t const* name, void* unused, uint32_t flags) noexcept {
return P::LoadLibraryExW(name, unused, flags);
}
int32_t __stdcall WINRT_IMPL_FreeLibrary(void* library) noexcept {
return P::FreeLibrary(library);
}
void* __stdcall WINRT_IMPL_GetProcAddress(void* library, char const* name) noexcept {
return P::GetProcAddress(library, name);
}
int32_t __stdcall WINRT_IMPL_SetErrorInfo(uint32_t reserved, void* info) noexcept {
return P::SetErrorInfo(reserved, info);
}
int32_t __stdcall WINRT_IMPL_GetErrorInfo(uint32_t reserved, void** info) noexcept {
return P::GetErrorInfo(reserved, info);
}
int32_t __stdcall WINRT_IMPL_CoInitializeEx(void *reserved, uint32_t type) noexcept {
return P::CoInitializeEx(reserved, type);
}
int32_t __stdcall WINRT_IMPL_CoCreateFreeThreadedMarshaler(void* outer, void** marshaler) noexcept {
return P::CoCreateFreeThreadedMarshaler(outer, marshaler);
}
int32_t __stdcall WINRT_IMPL_CoCreateInstance(winrt::guid const& clsid, void* outer, uint32_t context, winrt::guid const& iid, void** object) noexcept {
return P::CoCreateInstance(clsid, outer, context, iid, object);
}
int32_t __stdcall WINRT_IMPL_CoGetCallContext(winrt::guid const& iid, void** object) noexcept {
return P::CoGetCallContext(iid, object);
}
int32_t __stdcall WINRT_IMPL_CoGetApartmentType(int32_t* type, int32_t* qualifier) noexcept {
return P::CoGetApartmentType(type, qualifier);
}
void* __stdcall WINRT_IMPL_CoTaskMemAlloc(std::size_t size) noexcept {
return P::CoTaskMemAlloc(size);
}
void __stdcall WINRT_IMPL_CoTaskMemFree(void* ptr) noexcept {
return P::CoTaskMemFree(ptr);
}
void __stdcall WINRT_IMPL_SysFreeString(winrt::impl::bstr string) noexcept {
return P::SysFreeString(string);
}
uint32_t __stdcall WINRT_IMPL_SysStringLen(winrt::impl::bstr string) noexcept {
return P::SysStringLen(string);
}
int32_t __stdcall WINRT_IMPL_IIDFromString(wchar_t const* string, winrt::guid* iid) noexcept {
return P::IIDFromString(string, iid);
}
int32_t __stdcall WINRT_IMPL_MultiByteToWideChar(uint32_t codepage, uint32_t flags, char const* in_string, int32_t in_size, wchar_t* out_string, int32_t out_size) noexcept {
return P::MultiByteToWideChar(codepage, flags, in_string, in_size, out_string, out_size);
}
int32_t __stdcall WINRT_IMPL_WideCharToMultiByte(uint32_t codepage, uint32_t flags, wchar_t const* int_string, int32_t in_size, char* out_string, int32_t out_size, char const* default_char, int32_t* default_used) noexcept {
return P::WideCharToMultiByte(codepage, flags, int_string, in_size, out_string, out_size, default_char, default_used);
}
int32_t __stdcall WINRT_IMPL_HeapFree(void* heap, uint32_t flags, void* value) noexcept {
return P::HeapFree(heap, flags, value);
}
void* __stdcall WINRT_IMPL_GetProcessHeap() noexcept {
return P::GetProcessHeap();
}
uint32_t __stdcall WINRT_IMPL_FormatMessageW(uint32_t flags, void const* source, uint32_t code, uint32_t language, wchar_t* buffer, uint32_t size, va_list* arguments) noexcept {
return P::FormatMessageW(flags, source, code, language, buffer, size, arguments);
}
uint32_t __stdcall WINRT_IMPL_GetLastError() noexcept {
return P::GetLastError();
}
int32_t __stdcall WINRT_IMPL_OpenProcessToken(void* process, uint32_t access, void** token) noexcept {
return P::OpenProcessToken(process, access, token);
}
void* __stdcall WINRT_IMPL_GetCurrentProcess() noexcept {
return P::GetCurrentProcess();
}
int32_t __stdcall WINRT_IMPL_DuplicateToken(void* existing, uint32_t level, void** duplicate) noexcept {
return P::DuplicateToken(existing, level, duplicate);
}
int32_t __stdcall WINRT_IMPL_OpenThreadToken(void* thread, uint32_t access, int32_t self, void** token) noexcept {
return P::OpenThreadToken(thread, access, self, token);
}
void __stdcall WINRT_IMPL_AcquireSRWLockExclusive(winrt::impl::srwlock* lock) noexcept {
return P::AcquireSRWLockExclusive(lock);
}
void __stdcall WINRT_IMPL_AcquireSRWLockShared(winrt::impl::srwlock* lock) noexcept {
return P::AcquireSRWLockShared(lock);
}
uint8_t __stdcall WINRT_IMPL_TryAcquireSRWLockExclusive(winrt::impl::srwlock* lock) noexcept {
return P::TryAcquireSRWLockExclusive(lock);
}
uint8_t __stdcall WINRT_IMPL_TryAcquireSRWLockShared(winrt::impl::srwlock* lock) noexcept {
return P::TryAcquireSRWLockShared(lock);
}
void __stdcall WINRT_IMPL_ReleaseSRWLockExclusive(winrt::impl::srwlock* lock) noexcept {
return P::ReleaseSRWLockExclusive(lock);
}
void __stdcall WINRT_IMPL_ReleaseSRWLockShared(winrt::impl::srwlock* lock) noexcept {
return P::ReleaseSRWLockShared(lock);
}
void __stdcall WINRT_IMPL_WakeConditionVariable(winrt::impl::condition_variable* cv) noexcept {
return P::WakeConditionVariable(cv);
}
void __stdcall WINRT_IMPL_WakeAllConditionVariable(winrt::impl::condition_variable* cv) noexcept {
return P::WakeAllConditionVariable(cv);
}
void* __stdcall WINRT_IMPL_InterlockedPushEntrySList(void* head, void* entry) noexcept {
return P::InterlockedPushEntrySList(head, entry);
}
void* __stdcall WINRT_IMPL_CreateEventW(void *event, int32_t manual, int32_t initial, void *name) noexcept {
return P::CreateEventW(event, manual, initial, name);
}
int32_t __stdcall WINRT_IMPL_SetEvent(void *event) noexcept {
return P::SetEvent(event);
}
int32_t __stdcall WINRT_IMPL_CloseHandle(void* hObject) noexcept {
return P::CloseHandle(hObject);
}
uint32_t __stdcall WINRT_IMPL_WaitForSingleObject(void* handle, uint32_t milliseconds) noexcept {
return P::WaitForSingleObject(handle, milliseconds);
}
int32_t __stdcall WINRT_IMPL_TrySubmitThreadpoolCallback(void(__stdcall *callback)(void*, void* context), void* context, void*) noexcept {
return P::TrySubmitThreadpoolCallback(callback, context, context);
}
winrt::impl::ptp_timer __stdcall WINRT_IMPL_CreateThreadpoolTimer(void(__stdcall *callback)(void*, void* context, void*), void* context, void*) noexcept {
return P::CreateThreadpoolTimer(callback, context, context);
}
void __stdcall WINRT_IMPL_SetThreadpoolTimer(winrt::impl::ptp_timer timer, void* time, uint32_t period, uint32_t window) noexcept {
return P::SetThreadpoolTimer(timer, time, period, window);
}
winrt::impl::ptp_wait __stdcall WINRT_IMPL_CreateThreadpoolWait(void(__stdcall *callback)(void*, void* context, void*, uint32_t result), void* context, void*) noexcept {
return P::CreateThreadpoolWait(callback, context, context);
}
void __stdcall WINRT_IMPL_SetThreadpoolWait(winrt::impl::ptp_wait wait, void* handle, void* timeout) noexcept {
return P::SetThreadpoolWait(wait, handle, timeout);
}
winrt::impl::ptp_io __stdcall WINRT_IMPL_CreateThreadpoolIo(void* object, void(__stdcall *callback)(void*, void* context, void* overlapped, uint32_t result, std::size_t bytes, void*) noexcept, void* context, void*) noexcept {
return P::CreateThreadpoolIo(object, callback, context, context);
}
void __stdcall WINRT_IMPL_StartThreadpoolIo(winrt::impl::ptp_io io) noexcept {
return P::StartThreadpoolIo(io);
}
void __stdcall WINRT_IMPL_CancelThreadpoolIo(winrt::impl::ptp_io io) noexcept {
return P::CancelThreadpoolIo(io);
}
winrt::impl::ptp_pool __stdcall WINRT_IMPL_CreateThreadpool(void* reserved) noexcept {
return P::CreateThreadpool(reserved);
}
void __stdcall WINRT_IMPL_SetThreadpoolThreadMaximum(winrt::impl::ptp_pool pool, uint32_t value) noexcept {
return P::SetThreadpoolThreadMaximum(pool, value);
}
int32_t __stdcall WINRT_IMPL_SetThreadpoolThreadMinimum(winrt::impl::ptp_pool pool, uint32_t value) noexcept {
return P::SetThreadpoolThreadMinimum(pool, value);
}
void __stdcall WINRT_IMPL_CloseThreadpool(winrt::impl::ptp_pool pool) noexcept {
return P::CloseThreadpool(pool);
}
} // extern "C"

View File

@@ -0,0 +1,78 @@
// This file is part of Desktop App Toolkit,
// a set of libraries for developing nice desktop applications.
//
// For license and copyright information please follow this link:
// https://github.com/desktop-app/legal/blob/master/LEGAL
//
#pragma once
#include "base/debug_log.h"
// winrt::guid defines a constructor from GUID only if knows IUnknown.
#include <Unknwnbase.h>
// class has virtual functions, but destructor is not virtual
#pragma warning(push)
#pragma warning(disable:4265)
#include <winrt/base.h>
#pragma warning(pop)
namespace base::WinRT {
namespace details {
template <typename Method>
inline constexpr bool ReturnsVoid = std::is_same_v<
std::invoke_result_t<Method>,
void>;
template <typename Method>
using TryResult = std::conditional_t<
ReturnsVoid<Method>,
bool,
std::optional<std::invoke_result_t<Method>>>;
} // namespace details
[[nodiscard]] bool Supported();
template <typename Method>
inline details::TryResult<Method> TryNoCheck(Method &&method) noexcept {
try {
if constexpr (details::ReturnsVoid<Method>) {
method();
return true;
} else {
return method();
}
} catch (const std::bad_alloc &) {
Unexpected("Could not allocate in WinRT.");
} catch (const winrt::hresult_error &error) {
LOG(("WinRT Error: %1 (%2)"
).arg(error.code()
).arg(QString::fromWCharArray(error.message().c_str())));
return {};
} catch (...) {
LOG(("WinRT Error: Unknown."));
return {};
}
}
template <typename Method>
inline details::TryResult<Method> Try(Method &&method) noexcept {
if (!Supported()) {
return {};
}
return TryNoCheck(std::forward<Method>(method));
}
template <typename Interface>
auto TryCreateInstance(
const winrt::guid &clsid,
uint32_t context = 0x1 /*CLSCTX_INPROC_SERVER*/,
void *outer = nullptr) {
return TryNoCheck([&] {
return winrt::create_instance<Interface>(clsid, context, outer);
}).value_or(winrt::com_ptr<Interface>());
}
} // namespace base::WinRT

View File

@@ -0,0 +1,238 @@
// This file is part of Desktop App Toolkit,
// a set of libraries for developing nice desktop applications.
//
// For license and copyright information please follow this link:
// https://github.com/desktop-app/legal/blob/master/LEGAL
//
#include "base/platform/win/base_windows_wrl.h"
#include "base/platform/win/base_windows_safe_library.h"
#include "base/platform/base_platform_info.h"
#define LOAD_SYMBOL(lib, name) ::base::Platform::LoadMethod(lib, #name, name)
namespace base::Platform {
namespace {
using namespace ::Platform;
HRESULT(__stdcall *RoGetActivationFactory)(
_In_ HSTRING activatableClassId,
_In_ REFIID iid,
_COM_Outptr_ void ** factory);
HRESULT(__stdcall *RoActivateInstance)(
_In_ HSTRING activatableClassId,
_COM_Outptr_ IInspectable** instance);
HRESULT(__stdcall *RoRegisterActivationFactories)(
_In_reads_(count) HSTRING* activatableClassIds,
_In_reads_(count) PFNGETACTIVATIONFACTORY* activationFactoryCallbacks,
_In_ UINT32 count,
_Out_ RO_REGISTRATION_COOKIE* cookie);
void(__stdcall *RoRevokeActivationFactories)(
_In_ RO_REGISTRATION_COOKIE cookie);
HRESULT(__stdcall *WindowsCreateString)(
_In_reads_opt_(length) PCNZWCH sourceString,
UINT32 length,
_Outptr_result_maybenull_ _Result_nullonfailure_ HSTRING* string);
HRESULT(__stdcall *WindowsCreateStringReference)(
_In_reads_opt_(length + 1) PCWSTR sourceString,
UINT32 length,
_Out_ HSTRING_HEADER * hstringHeader,
_Outptr_result_maybenull_ _Result_nullonfailure_ HSTRING *string);
HRESULT(__stdcall *WindowsDeleteString)(
_In_opt_ HSTRING string);
PCWSTR(__stdcall *WindowsGetStringRawBuffer)(
_In_opt_ HSTRING string,
_Out_opt_ UINT32* length);
BOOL(__stdcall *WindowsIsStringEmpty)(
_In_opt_ HSTRING string);
HRESULT(__stdcall *WindowsStringHasEmbeddedNull)(
_In_opt_ HSTRING string,
_Out_ BOOL* hasEmbedNull);
BOOL(__stdcall *RoOriginateErrorW)(
_In_ HRESULT error,
_In_ UINT cchMax,
_When_(cchMax == 0, _In_reads_or_z_opt_(MAX_ERROR_MESSAGE_CHARS))
_When_(cchMax > 0 && cchMax < MAX_ERROR_MESSAGE_CHARS, _In_reads_or_z_(cchMax))
_When_(cchMax >= MAX_ERROR_MESSAGE_CHARS, _In_reads_or_z_(MAX_ERROR_MESSAGE_CHARS)) PCWSTR message);
BOOL(__stdcall *RoOriginateError)(
_In_ HRESULT error,
_In_opt_ HSTRING message);
} // namespace
bool SupportsWRL() {
struct State {
bool inited = false;
bool supported = false;
};
static auto state = State();
if (state.inited) {
return state.supported;
}
state.inited = true;
if (IsWindows8OrGreater()) {
const auto combase = SafeLoadLibrary(L"combase.dll");
state.supported = combase
&& LOAD_SYMBOL(combase, RoGetActivationFactory)
&& LOAD_SYMBOL(combase, RoActivateInstance)
&& LOAD_SYMBOL(combase, RoRegisterActivationFactories)
&& LOAD_SYMBOL(combase, RoRevokeActivationFactories)
&& LOAD_SYMBOL(combase, WindowsCreateString)
&& LOAD_SYMBOL(combase, WindowsCreateStringReference)
&& LOAD_SYMBOL(combase, WindowsDeleteString)
&& LOAD_SYMBOL(combase, WindowsGetStringRawBuffer)
&& LOAD_SYMBOL(combase, WindowsIsStringEmpty)
&& LOAD_SYMBOL(combase, WindowsStringHasEmbeddedNull);
}
return state.supported;
}
} // namespace base::Platform
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
namespace P = base::Platform;
extern "C" {
_Check_return_ HRESULT WINAPI RoActivateInstance(
_In_ HSTRING activatableClassId,
_COM_Outptr_ IInspectable** instance
) {
return P::SupportsWRL()
? P::RoActivateInstance(activatableClassId, instance)
: CO_E_DLLNOTFOUND;
}
_Check_return_ HRESULT WINAPI RoGetActivationFactory(
_In_ HSTRING activatableClassId,
_In_ REFIID iid,
_COM_Outptr_ void** factory
) {
return P::SupportsWRL()
? P::RoGetActivationFactory(activatableClassId, iid, factory)
: CO_E_DLLNOTFOUND;
}
_Check_return_ HRESULT WINAPI RoRegisterActivationFactories(
_In_reads_(count) HSTRING* activatableClassIds,
_In_reads_(count) PFNGETACTIVATIONFACTORY* activationFactoryCallbacks,
_In_ UINT32 count,
_Out_ RO_REGISTRATION_COOKIE* cookie
) {
return P::SupportsWRL()
? P::RoRegisterActivationFactories(
activatableClassIds,
activationFactoryCallbacks,
count,
cookie)
: CO_E_DLLNOTFOUND;
}
void
WINAPI
RoRevokeActivationFactories(
_In_ RO_REGISTRATION_COOKIE cookie
) {
if (P::SupportsWRL()) {
P::RoRevokeActivationFactories(cookie);
}
}
STDAPI
WindowsCreateString(
_In_reads_opt_(length) PCNZWCH sourceString,
UINT32 length,
_Outptr_result_maybenull_ _Result_nullonfailure_ HSTRING* string
) {
return P::SupportsWRL()
? P::WindowsCreateString(sourceString, length, string)
: CO_E_DLLNOTFOUND;
}
STDAPI
WindowsCreateStringReference(
_In_reads_opt_(length + 1) PCWSTR sourceString,
UINT32 length,
_Out_ HSTRING_HEADER* hstringHeader,
_Outptr_result_maybenull_ _Result_nullonfailure_ HSTRING* string
) {
return P::SupportsWRL()
? P::WindowsCreateStringReference(
sourceString,
length,
hstringHeader,
string)
: CO_E_DLLNOTFOUND;
}
STDAPI
WindowsDeleteString(
_In_opt_ HSTRING string
) {
return P::SupportsWRL()
? P::WindowsDeleteString(string)
: CO_E_DLLNOTFOUND;
}
STDAPI_(PCWSTR)
WindowsGetStringRawBuffer(
_In_opt_ HSTRING string,
_Out_opt_ UINT32* length) {
return P::SupportsWRL()
? P::WindowsGetStringRawBuffer(string, length)
: nullptr;
}
STDAPI_(BOOL)
WindowsIsStringEmpty(
_In_opt_ HSTRING string
) {
return P::SupportsWRL() && P::WindowsIsStringEmpty(string);
}
STDAPI
WindowsStringHasEmbeddedNull(
_In_opt_ HSTRING string,
_Out_ BOOL* hasEmbedNull
) {
return P::SupportsWRL()
? P::WindowsStringHasEmbeddedNull(string, hasEmbedNull)
: CO_E_DLLNOTFOUND;
}
STDAPI_(BOOL)
RoOriginateErrorW(
_In_ HRESULT error,
_In_ UINT cchMax,
_When_(cchMax == 0, _In_reads_or_z_opt_(MAX_ERROR_MESSAGE_CHARS))
_When_(cchMax > 0 && cchMax < MAX_ERROR_MESSAGE_CHARS, _In_reads_or_z_(cchMax))
_When_(cchMax >= MAX_ERROR_MESSAGE_CHARS, _In_reads_or_z_(MAX_ERROR_MESSAGE_CHARS)) PCWSTR message
) {
return P::SupportsWRL()
&& P::RoOriginateErrorW(error, cchMax, message);
}
STDAPI_(BOOL)
RoOriginateError(
_In_ HRESULT error,
_In_opt_ HSTRING message
) {
return P::SupportsWRL() && P::RoOriginateError(error, message);
}
} // extern "C"
#endif // Qt < 6.0.0

View File

@@ -0,0 +1,66 @@
// This file is part of Desktop App Toolkit,
// a set of libraries for developing nice desktop applications.
//
// For license and copyright information please follow this link:
// https://github.com/desktop-app/legal/blob/master/LEGAL
//
#pragma once
#define _ROAPI_ // we don't want them from .dll
#include <roapi.h>
#undef _ROAPI_
#include <windows.h>
#include <wrl/client.h>
#include <winstring.h>
#include <strsafe.h>
#include <intsafe.h>
namespace base::Platform {
[[nodiscard]] bool SupportsWRL();
class StringReferenceWrapper final {
public:
StringReferenceWrapper(_In_reads_(length) PCWSTR stringRef, _In_ UINT32 length) throw() {
HRESULT hr = WindowsCreateStringReference(stringRef, length, &_header, &_hstring);
if (!SUCCEEDED(hr)) {
RaiseException(static_cast<DWORD>(STATUS_INVALID_PARAMETER), EXCEPTION_NONCONTINUABLE, 0, nullptr);
}
}
~StringReferenceWrapper() {
WindowsDeleteString(_hstring);
}
template <size_t N>
StringReferenceWrapper(_In_reads_(N) wchar_t const (&stringRef)[N]) throw() {
UINT32 length = N - 1;
HRESULT hr = WindowsCreateStringReference(stringRef, length, &_header, &_hstring);
if (!SUCCEEDED(hr)) {
RaiseException(static_cast<DWORD>(STATUS_INVALID_PARAMETER), EXCEPTION_NONCONTINUABLE, 0, nullptr);
}
}
template <size_t _>
StringReferenceWrapper(_In_reads_(_) wchar_t(&stringRef)[_]) throw() {
UINT32 length;
HRESULT hr = SizeTToUInt32(wcslen(stringRef), &length);
if (!SUCCEEDED(hr)) {
RaiseException(static_cast<DWORD>(STATUS_INVALID_PARAMETER), EXCEPTION_NONCONTINUABLE, 0, nullptr);
}
WindowsCreateStringReference(stringRef, length, &_header, &_hstring);
}
HSTRING Get() const throw() {
return _hstring;
}
private:
HSTRING _hstring;
HSTRING_HEADER _header;
};
} // namespace base::Platform

View File

@@ -0,0 +1,15 @@
// This file is part of Desktop App Toolkit,
// a set of libraries for developing nice desktop applications.
//
// For license and copyright information please follow this link:
// https://github.com/desktop-app/legal/blob/master/LEGAL
//
#pragma once
#include "base/platform/win/base_windows_wrl.h"
#pragma warning(push)
// class has virtual functions, but destructor is not virtual
#pragma warning(disable:4265)
#include <wrl/implements.h>
#pragma warning(pop)

View File

@@ -0,0 +1,16 @@
// This file is part of Desktop App Toolkit,
// a set of libraries for developing nice desktop applications.
//
// For license and copyright information please follow this link:
// https://github.com/desktop-app/legal/blob/master/LEGAL
//
#pragma once
#include "base/platform/win/base_windows_wrl.h"
#pragma warning(push)
// class has virtual functions, but destructor is not virtual
#pragma warning(disable:4265)
#pragma warning(disable:5104)
#include <wrl/module.h>
#pragma warning(pop)