Add initial score-based dock detection

This is early development & untested code specifically targeting the steam deck for now until further finalised
This commit is contained in:
Jordon Brooks 2024-01-16 00:09:12 +00:00
parent 470f4a318f
commit 545815fd6d
No known key found for this signature in database
GPG key ID: 83964894E5D98D57
8 changed files with 336 additions and 150 deletions

View file

@ -43,6 +43,17 @@ namespace HarmonyLinkLib
return PlatformUtilities->is_linux();
}
bool get_is_docked()
{
if (!PlatformUtilities)
{
std::wcout << "Failed to get platform utilities!\n";
return false;
}
return PlatformUtilities->is_docked();
}
FCPUInfo* get_cpu_info()
{
if (!PlatformUtilities)

View file

@ -31,106 +31,173 @@ namespace HarmonyLinkLib
{
static std::shared_ptr<IPlatformUtilities> INSTANCE = nullptr;
std::shared_ptr<IPlatformUtilities>& IPlatformUtilities::GetInstance()
{
if (!INSTANCE)
std::shared_ptr<IPlatformUtilities>& IPlatformUtilities::GetInstance()
{
#if BUILD_WINDOWS
INSTANCE = std::make_shared<WindowsUtilities>();
#elif BUILD_LINUX
INSTANCE = std::make_shared<LinuxUtilities>();
#elif BUILD_MAC
INSTANCE = std::make_shared<MacUtilities>();
#elif BUILD_UNIX
INSTANCE = std::make_shared<UnixUtilities>();
// ... other platform checks
#else
std::wcout << "Platform is not supported.\n"
#endif
if (!INSTANCE)
{
#if BUILD_WINDOWS
INSTANCE = std::make_shared<WindowsUtilities>();
#elif BUILD_LINUX
INSTANCE = std::make_shared<LinuxUtilities>();
#elif BUILD_MAC
INSTANCE = std::make_shared<MacUtilities>();
#elif BUILD_UNIX
INSTANCE = std::make_shared<UnixUtilities>();
// ... other platform checks
#else
std::wcout << "Platform is not supported.\n"
#endif
}
return INSTANCE;
}
return INSTANCE;
}
bool IPlatformUtilities::is_running_under_wine()
{
return WineUtilities::is_wine_present();
}
bool IPlatformUtilities::is_linux()
{
#ifdef BUILD_LINUX
return true;
#else
return is_running_under_wine();
#endif
}
std::shared_ptr<FDevice> IPlatformUtilities::get_device()
{
FDevice new_device;
if (is_linux())
bool IPlatformUtilities::is_running_under_wine()
{
new_device.platform = EPlatform::LINUX;
return WineUtilities::is_wine_present();
}
else
bool IPlatformUtilities::is_linux()
{
new_device.platform = EPlatform::WINDOWS;
}
const std::shared_ptr<FBattery> battery_status = get_battery_status();
if (battery_status && !battery_status->has_battery)
{
new_device.device = EDevice::DESKTOP;
}
else
{
new_device.device = EDevice::LAPTOP;
}
if (is_steam_deck(new_device)) {
new_device.device = EDevice::STEAM_DECK;
}
return std::make_shared<FDevice>(new_device);
}
// Helper function to check if the device is a Steam Deck
bool IPlatformUtilities::is_steam_deck(const FDevice& device) {
// Check if the device is already identified as a Steam Deck
if (device.device == EDevice::STEAM_DECK) {
#ifdef BUILD_LINUX
return true;
#else
return is_running_under_wine();
#endif
}
// Check for Steam Deck by OS version
if (const std::shared_ptr<FOSVerInfo> version = get_os_version()) {
if (version->variant_id == "steamdeck" && version->name == "SteamOS") {
bool IPlatformUtilities::is_steam_deck()
{
const std::shared_ptr<FDevice> device = get_device();
return device && device->device == EDevice::STEAM_DECK;
}
bool IPlatformUtilities::is_docked()
{
static constexpr uint8_t CHARGING_SCORE = 3;
static constexpr uint8_t EXTERNAL_MONITOR_SCORE = 3;
static constexpr uint8_t KEYBOARD_DETECTION_SCORE = 2;
static constexpr uint8_t CONTROLLER_DETECTION_SCORE = 2;
static constexpr uint8_t FINAL_TARGET_DETECTION_SCORE = 6;
const std::shared_ptr<FDevice> device = get_device();
if (!device)
{
std::wcout << "Error: failed to get device.\n";
return false;
}
if (device->device != EDevice::STEAM_DECK)
{
std::wcout << "Error: Dock detection is currently only supported on Steam Decks.\n";
return false;
}
uint8_t score = 0;
if (is_charging())
{
score += CHARGING_SCORE;
}
if (get_is_external_monitor_connected())
{
score += EXTERNAL_MONITOR_SCORE;
}
if (get_mouse_keyboard_detected())
{
score += KEYBOARD_DETECTION_SCORE;
}
if (get_external_controller_detected())
{
score += CONTROLLER_DETECTION_SCORE;
}
return score >= FINAL_TARGET_DETECTION_SCORE;
}
std::shared_ptr<FDevice> IPlatformUtilities::get_device()
{
FDevice new_device;
if (is_linux())
{
new_device.platform = EPlatform::LINUX;
}
else
{
new_device.platform = EPlatform::WINDOWS;
}
const std::shared_ptr<FBattery> battery_status = get_battery_status();
if (battery_status && !battery_status->has_battery)
{
new_device.device = EDevice::DESKTOP;
}
else
{
new_device.device = EDevice::LAPTOP;
}
if (is_steam_deck_detected(new_device)) {
new_device.device = EDevice::STEAM_DECK;
}
return std::make_shared<FDevice>(new_device);
}
// Helper function to check if the device is a Steam Deck
bool IPlatformUtilities::is_steam_deck_detected(const FDevice& device) {
// Check if the device is already identified as a Steam Deck
if (device.device == EDevice::STEAM_DECK) {
return true;
}
} else {
wprintf(L"OS version information not available.\n");
}
// Set of known Steam Deck CPU model names
const std::set<std::string> steam_deck_models = {"amd custom apu 0405" /*, other models... */};
// Check for Steam Deck by CPU model name
if (const std::shared_ptr<FCPUInfo> cpu_info = get_cpu_info()) {
const FString cpu_model_lower = FString::to_lower(cpu_info->Model_Name);
for (const auto& model : steam_deck_models) {
if (cpu_model_lower == model) {
wprintf(L"Steam Deck detected by CPU model name.\n");
// Check for Steam Deck by OS version
if (const std::shared_ptr<FOSVerInfo> version = get_os_version()) {
if (version->variant_id == "steamdeck" && version->name == "SteamOS") {
return true;
}
} else {
wprintf(L"OS version information not available.\n");
}
} else {
wprintf(L"CPU information not available.\n");
// Set of known Steam Deck CPU model names
const std::set<std::string> steam_deck_models = {"amd custom apu 0405" /*, other models... */};
// Check for Steam Deck by CPU model name
if (const std::shared_ptr<FCPUInfo> cpu_info = get_cpu_info()) {
const FString cpu_model_lower = FString::to_lower(cpu_info->Model_Name);
for (const auto& model : steam_deck_models) {
if (cpu_model_lower == model) {
wprintf(L"Steam Deck detected by CPU model name.\n");
return true;
}
}
} else {
wprintf(L"CPU information not available.\n");
}
wprintf(L"Device is not a Steam Deck.\n");
return false;
}
wprintf(L"Device is not a Steam Deck.\n");
return false;
}
bool IPlatformUtilities::is_connected_to_ac()
{
const std::shared_ptr<FBattery> battery = get_battery_status();
return battery && battery->is_connected_to_ac;
}
bool IPlatformUtilities::is_charging()
{
const std::shared_ptr<FBattery> battery = get_battery_status();
return battery && battery->has_battery && battery->is_connected_to_ac;
}
}

View file

@ -35,13 +35,22 @@ namespace HarmonyLinkLib
// General OS-level functions
virtual bool is_running_under_wine();
virtual bool is_linux();
virtual bool is_steam_deck();
virtual bool is_docked();
virtual std::shared_ptr<FDevice> get_device();
virtual std::shared_ptr<FCPUInfo> get_cpu_info() = 0;
virtual std::shared_ptr<FBattery> get_battery_status() = 0;
virtual std::shared_ptr<FOSVerInfo> get_os_version() = 0;
virtual bool get_is_external_monitor_connected() = 0;
virtual bool get_mouse_keyboard_detected() = 0;
virtual bool get_external_controller_detected() = 0;
//virtual bool get_is_ethernet_connected() = 0;
//virtual bool get_is_external_input_detected() = 0;
bool is_steam_deck(const FDevice& device);
bool is_steam_deck_detected(const FDevice& device);
bool is_connected_to_ac();
bool is_charging();
// Add more virtual functions for other OS interactions here
};

View file

@ -18,21 +18,43 @@ namespace HarmonyLinkLib
{
bool UnixUtilities::is_running_under_wine()
{
return false;
std::wcout << "This feature is not supported on unix systems yet.\n";
return false;
}
std::shared_ptr<FCPUInfo> UnixUtilities::get_cpu_info()
{
std::wcout << "This feature is not supported on unix systems yet.\n";
return nullptr;
}
std::shared_ptr<FBattery> UnixUtilities::get_battery_status()
{
std::wcout << "This feature is not supported on unix systems yet.\n";
return nullptr;
}
std::shared_ptr<FOSVerInfo> UnixUtilities::get_os_version()
{
std::wcout << "This feature is not supported on unix systems yet.\n";
return nullptr;
}
bool UnixUtilities::get_is_external_monitor_connected()
{
std::wcout << "This feature is not supported on unix-based systems yet.\n";
return false;
}
bool UnixUtilities::get_mouse_keyboard_detected()
{
std::wcout << "This feature is not supported on unix-based systems yet.\n";
return false;
}
bool UnixUtilities::get_external_controller_detected()
{
std::wcout << "This feature is not supported on unix-based systems yet.\n";
return false;
}
}

View file

@ -17,6 +17,8 @@
#include "Platform/IPlatformUtilities.h"
namespace HarmonyLinkLib
{
// This is more of a "catch all" for all unix-based systems
// that don't have their own implementations.
class UnixUtilities : public IPlatformUtilities {
public:
bool is_running_under_wine() override;
@ -24,6 +26,12 @@ namespace HarmonyLinkLib
std::shared_ptr<FCPUInfo> get_cpu_info() override;
std::shared_ptr<FBattery> get_battery_status() override;
std::shared_ptr<FOSVerInfo> get_os_version() override;
bool get_is_external_monitor_connected() override;
bool get_mouse_keyboard_detected() override;
bool get_external_controller_detected() override;
// Implementation for other Unix/Linux-specific functions
};

View file

@ -17,79 +17,140 @@
#include <sstream>
#include <Windows.h>
#include <XInput.h>
#pragma comment(lib, "XInput.lib")
#include "Platform/WineUtilities.h"
namespace HarmonyLinkLib
{
std::shared_ptr<FBattery> WindowsUtilities::get_battery_status()
{
if (is_linux())
{
return WineUtilities::get_battery_status();
}
FBattery result;
SYSTEM_POWER_STATUS status;
if (GetSystemPowerStatus(&status)) {
result.has_battery = status.BatteryFlag != 128; // 128 indicates no battery
result.is_connected_to_ac = status.ACLineStatus == 1;
result.battery_percent = result.has_battery ? status.BatteryLifePercent : 0;
} else {
std::wcout << "Failed to get power statistics.\n";
}
return std::make_shared<FBattery>(result);
}
std::shared_ptr<FCPUInfo> WindowsUtilities::get_cpu_info()
{
if (is_linux())
{
return WineUtilities::get_cpu_info();
}
return {};
}
std::shared_ptr<FOSVerInfo> WindowsUtilities::get_os_version()
{
if (is_linux())
{
return WineUtilities::get_linux_info();
}
OSVERSIONINFOEX os_version_info;
ZeroMemory(&os_version_info, sizeof(OSVERSIONINFOEX));
os_version_info.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
if (!GetVersionEx(reinterpret_cast<OSVERSIONINFO*>(&os_version_info))) {
// Handle error if needed
return nullptr;
}
FOSVerInfo os_version;
HKEY h_key;
if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, TEXT("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion"), 0, KEY_READ, &h_key) == ERROR_SUCCESS)
{
DWORD dw_size;
DWORD dw_type;
char sz_product_name[256];
dw_size = sizeof(sz_product_name);
if (RegQueryValueEx(h_key, TEXT("ProductName"), nullptr, &dw_type, reinterpret_cast<LPBYTE>(sz_product_name), &dw_size) == ERROR_SUCCESS)
if (is_linux())
{
os_version.pretty_name = sz_product_name;
return WineUtilities::get_battery_status();
}
RegCloseKey(h_key);
FBattery result;
SYSTEM_POWER_STATUS status;
if (GetSystemPowerStatus(&status)) {
result.has_battery = status.BatteryFlag != 128; // 128 indicates no battery
result.is_connected_to_ac = status.ACLineStatus == 1;
result.battery_percent = result.has_battery ? status.BatteryLifePercent : 0;
} else {
std::wcout << "Failed to get power statistics.\n";
}
return std::make_shared<FBattery>(result);
}
std::stringstream version;
version << os_version_info.dwMajorVersion << "." << os_version_info.dwMinorVersion;
os_version.version_id = version.str();
std::shared_ptr<FCPUInfo> WindowsUtilities::get_cpu_info()
{
if (is_linux())
{
return WineUtilities::get_cpu_info();
}
return {};
}
os_version.name = "Windows";
os_version.version = os_version_info.dwBuildNumber; // Build number as the version
std::shared_ptr<FOSVerInfo> WindowsUtilities::get_os_version()
{
if (is_linux())
{
return WineUtilities::get_linux_info();
}
OSVERSIONINFOEX os_version_info;
ZeroMemory(&os_version_info, sizeof(OSVERSIONINFOEX));
os_version_info.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
return std::make_shared<FOSVerInfo>(os_version);
}
if (!GetVersionEx(reinterpret_cast<OSVERSIONINFO*>(&os_version_info))) {
// Handle error if needed
return nullptr;
}
FOSVerInfo os_version;
HKEY h_key;
if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, TEXT("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion"), 0, KEY_READ, &h_key) == ERROR_SUCCESS)
{
DWORD dw_size;
DWORD dw_type;
char sz_product_name[256];
dw_size = sizeof(sz_product_name);
if (RegQueryValueEx(h_key, TEXT("ProductName"), nullptr, &dw_type, reinterpret_cast<LPBYTE>(sz_product_name), &dw_size) == ERROR_SUCCESS)
{
os_version.pretty_name = sz_product_name;
}
RegCloseKey(h_key);
}
std::stringstream version;
version << os_version_info.dwMajorVersion << "." << os_version_info.dwMinorVersion;
os_version.version_id = version.str();
os_version.name = "Windows";
os_version.version = os_version_info.dwBuildNumber; // Build number as the version
return std::make_shared<FOSVerInfo>(os_version);
}
bool WindowsUtilities::get_is_external_monitor_connected()
{
// SM_CMONITORS returns the count of all display monitors.
const int monitorCount = GetSystemMetrics(SM_CMONITORS);
// More than one monitor implies an external monitor is connected.
return monitorCount > 1;
}
bool WindowsUtilities::get_mouse_keyboard_detected()
{
UINT n_devices;
GetRawInputDeviceList(nullptr, &n_devices, sizeof(RAWINPUTDEVICELIST));
if (n_devices > 0) {
bool mouse_detected = false;
bool keyboard_detected = false;
std::vector<RAWINPUTDEVICELIST> devices(n_devices);
GetRawInputDeviceList(devices.data(), &n_devices, sizeof(RAWINPUTDEVICELIST));
for (const auto& device : devices) {
switch (device.dwType)
{
case RIM_TYPEMOUSE:
mouse_detected = true;
break;
case RIM_TYPEKEYBOARD:
keyboard_detected = true;
break;
default:
break;
}
}
return mouse_detected && keyboard_detected;
}
return false;
}
bool WindowsUtilities::get_external_controller_detected()
{
static_assert(XUSER_MAX_COUNT <= UINT8_MAX, "XUSER_MAX_COUNT exceeds uint8_t size");
uint8_t connectedGamepads = 0;
for (DWORD i = 0; i < XUSER_MAX_COUNT; ++i) {
XINPUT_STATE state;
ZeroMemory(&state, sizeof(XINPUT_STATE));
if (XInputGetState(i, &state) == ERROR_SUCCESS) {
connectedGamepads++;
}
}
return connectedGamepads > 1;
}
}

View file

@ -26,5 +26,11 @@ namespace HarmonyLinkLib
std::shared_ptr<FCPUInfo> get_cpu_info() override;
std::shared_ptr<FOSVerInfo> get_os_version() override;
bool get_is_external_monitor_connected() override;
bool get_mouse_keyboard_detected() override;
bool get_external_controller_detected() override;
};
}