// Copyright (c) 2024 Jordon Brooks // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include "IPlatformUtilities.h" #include #include #include #include "Utilities.h" #include "WineUtilities.h" #if BUILD_WINDOWS #include "Windows/WindowsUtilities.h" #elif BUILD_LINUX #include "Unix/Linux/LinuxUtilities.h" #elif BUILD_MAC #include "Unix/Mac/MacUtilities.h" #elif BUILD_UNIX #include "Unix/Mac/MacUtilities.h" #endif namespace HarmonyLinkLib { static std::shared_ptr INSTANCE = nullptr; std::shared_ptr& IPlatformUtilities::GetInstance() { if (!INSTANCE) { #if BUILD_WINDOWS INSTANCE = std::make_shared(); #elif BUILD_LINUX INSTANCE = std::make_shared(); #elif BUILD_MAC INSTANCE = std::make_shared(); #elif BUILD_UNIX INSTANCE = std::make_shared(); // ... other platform checks #else std::wcout << "Platform is not supported.\n" #endif } 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 } bool IPlatformUtilities::is_steam_deck() { const std::shared_ptr 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 = 4; static constexpr uint8_t STEAM_DECK_RESOLUTION_SCORE = 3; static constexpr uint8_t KEYBOARD_DETECTION_SCORE = 1; static constexpr uint8_t MOUSE_DETECTION_SCORE = 2; static constexpr uint8_t CONTROLLER_DETECTION_SCORE = 3; static constexpr uint8_t FINAL_TARGET_DETECTION_SCORE = 9; const std::shared_ptr 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; Utilities::DebugPrint("Detected: ", false); if (is_charging()) { Utilities::DebugPrint("Charging, ", false); score += CHARGING_SCORE; } if (get_is_external_monitor_connected()) { Utilities::DebugPrint("External monitor, ", false); score += EXTERNAL_MONITOR_SCORE; } if (get_is_steam_deck_native_resolution()) { Utilities::DebugPrint("Non-native resolution, ", false); score += STEAM_DECK_RESOLUTION_SCORE; } if (get_keyboard_detected()) { Utilities::DebugPrint("keyboard ", false); score += KEYBOARD_DETECTION_SCORE; } if (get_mouse_detected()) { Utilities::DebugPrint("mouse, ", false); score += MOUSE_DETECTION_SCORE; } if (get_external_controller_detected()) { Utilities::DebugPrint("external controller, ", false); score += CONTROLLER_DETECTION_SCORE; } Utilities::DebugPrint(fmt::format("Score: {}/{}", score, FINAL_TARGET_DETECTION_SCORE).c_str()); return score >= FINAL_TARGET_DETECTION_SCORE; } std::shared_ptr IPlatformUtilities::get_device() { FDevice new_device; if (is_linux()) { new_device.platform = EPlatform::LINUX; } else { new_device.platform = EPlatform::WINDOWS; } const std::shared_ptr battery_status = get_battery_status(); if (battery_status && !battery_status->has_battery) { new_device.device = EDevice::DESKTOP; } else { new_device.device = EDevice::LAPTOP; } const ESteamDeck steam_deck_model = detect_steam_deck(new_device); if (steam_deck_model != ESteamDeck::NONE) { new_device.device = EDevice::STEAM_DECK; new_device.steam_deck_model = steam_deck_model; } return std::make_shared(new_device); } // Helper function to check if the device is a Steam Deck ESteamDeck IPlatformUtilities::detect_steam_deck(const FDevice& device) { // Check if the device is already identified as a Steam Deck if (device.device == EDevice::STEAM_DECK && device.steam_deck_model != ESteamDeck::NONE) { return device.steam_deck_model; } ESteamDeck steam_deck_model = ESteamDeck::NONE; // Retrieve and process CPU information const std::shared_ptr cpu_info = get_cpu_info(); if (!cpu_info) { wprintf(L"CPU information not available.\n"); } else { // Convert the CPU model name to lower case once FString cpu_model_lower = FString::to_lower(cpu_info->Model_Name); // Map of CPU models to their corresponding Steam Deck models static const std::unordered_map model_map = { {FString::to_lower("amd custom apu 0405"), ESteamDeck::LCD}, {FString::to_lower("amd custom apu 0932"), ESteamDeck::OLED} }; auto iterator = model_map.find(cpu_model_lower); if (iterator != model_map.end()) { steam_deck_model = iterator->second; wprintf(L"Steam Deck detected by CPU model name: %hs.\n", cpu_model_lower.c_str()); } } // Check for Steam Deck by OS version only if no model has been detected yet if (steam_deck_model == ESteamDeck::NONE) { if (const std::shared_ptr version = get_os_version()) { if (version->variant_id == "steamdeck" && version->name == "SteamOS") { // Use UNKNOWN if OS matches but CPU model doesn't fit known profiles steam_deck_model = ESteamDeck::UNKNOWN; wprintf(L"Steam Deck OS detected but model is unknown.\n"); } } else { wprintf(L"OS version information not available.\n"); } } return steam_deck_model; } bool IPlatformUtilities::is_connected_to_ac() { const std::shared_ptr battery = get_battery_status(); return battery && battery->is_connected_to_ac; } bool IPlatformUtilities::is_charging() { const std::shared_ptr battery = get_battery_status(); return battery && battery->has_battery && battery->is_connected_to_ac; } }