Files
OpenVulkano/openVulkanoCpp/Host/Linux/SystemInfo.cpp
2025-01-21 15:14:14 +02:00

397 lines
8.2 KiB
C++

/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
#include "Host/SystemInfo.hpp"
#include "Base/Logger.hpp"
#include "Base/Utils.hpp"
#include "Math/ByteSize.hpp"
#include <fstream>
#include <iostream>
#include <string>
#include <climits>
#include <sys/resource.h>
#include <sys/utsname.h>
#include <unistd.h>
#include <thread>
namespace OpenVulkano
{
namespace
{
size_t ReadMemInfo(std::string_view value)
{
std::ifstream meminfoFile("/proc/meminfo");
if (!meminfoFile)
{
Logger::PERF->error("Failed to open /proc/meminfo");
return 0;
}
std::string line;
while (std::getline(meminfoFile, line))
{
if (Utils::StartsWith(line, value))
{
std::string n, t;
size_t v;
std::istringstream ss(line);
ss >> n >> v >> t;
return ByteSize(v, ByteSizeUnit::FromName(t));
}
}
meminfoFile.close();
return 0;
}
size_t ReadProcStatus(std::string_view value)
{
std::ifstream statusFile("/proc/self/status");
if (statusFile)
{
size_t mem = 0;
std::string line;
while (std::getline(statusFile, line))
{
if (Utils::StartsWith(line, value))
{
std::string n, t;
size_t v;
std::istringstream ss(line);
ss >> n >> v >> t;
return ByteSize(v, ByteSizeUnit::FromName(t));
}
}
statusFile.close();
return mem;
}
Logger::PERF->error("Failed to open /proc/self/status");
return 0;
}
}
size_t SystemInfo::GetSystemRam()
{
static const size_t ram = ReadMemInfo("MemTotal");
return ram;
}
size_t SystemInfo::GetSystemRamAvailable()
{
return ReadMemInfo("MemAvailable");
}
size_t SystemInfo::GetAppRamUsed()
{
return ReadProcStatus("VmSize");
}
size_t SystemInfo::GetAppRamAvailable()
{
return std::min(GetSystemRamAvailable(), GetAppVirtualMemoryMax() - GetAppRamUsed());
}
size_t SystemInfo::GetAppRamMax()
{
return std::min(GetSystemRam(), GetAppVirtualMemoryMax());
}
size_t SystemInfo::GetAppVirtualMemoryMax()
{
static size_t vramMax;
if (vramMax == 0)
{
rlimit limit;
if (getrlimit(RLIMIT_AS, &limit) == 0)
{
vramMax = limit.rlim_cur;
}
Logger::PERF->error("Failed to query max application memory");
}
return vramMax;
}
const std::string& SystemInfo::GetUserName()
{
static std::string userName;
if (userName.empty())
{
char* name = getlogin();
if (!name)
{
userName = "unknown";
}
else
{
userName = name;
}
}
return userName;
}
const std::string& SystemInfo::GetHostName()
{
static std::string hostName;
if (hostName.empty())
{
char hostname[HOST_NAME_MAX + 1];
gethostname(hostname, HOST_NAME_MAX + 1);
hostName = hostname;
}
return hostName;
}
const std::string& SystemInfo::GetDeviceName()
{
return GetHostName();
}
const std::string& SystemInfo::GetDeviceVendorName()
{
static std::string vendor;
if (vendor.empty())
{
std::ifstream dmiStream("/sys/class/dmi/id/board_vendor");
if (!dmiStream)
{
Logger::PERF->error("Failed to read /sys/class/dmi/id/board_vendor");
vendor = "Unknown";
}
else
{
std::string motherboardName;
std::getline(dmiStream, motherboardName);
vendor = motherboardName;
}
}
return vendor;
}
const std::string& SystemInfo::GetDeviceModelName()
{
static std::string modelName;
if (modelName.empty())
{
std::ifstream dmiStream("/sys/class/dmi/id/board_name");
if (!dmiStream)
{
Logger::PERF->error("Failed to read /sys/class/dmi/id/board_name");
modelName = "Unknown";
}
else
{
std::string motherboardName;
std::getline(dmiStream, motherboardName);
modelName = motherboardName;
}
}
return modelName;
}
namespace
{
struct OsInfo
{
std::string osName, osNameFormatted;
OsVersion version;
static std::string TryRead(const std::string line, std::string_view prefix)
{
if (Utils::StartsWith(line, prefix))
{
size_t prefixLen = prefix.length();
return line.substr(prefixLen, line.length() - prefixLen - 1);
}
return "";
}
static OsVersion ParseVersionString(char* str)
{
int ver[3];
int i = 0;
while (*str && i < 3)
{
if (isdigit(*str))
{
ver[i] = strtol(str, &str, 10);
i++;
}
else
{
str++;
}
}
return {ver[0], ver[1], ver[2], 0};
}
OsInfo() : version({0, 0, 0, 0})
{
std::ifstream osRel("/etc/os-release");
std::string versionString;
if (osRel)
{
std::string line, name, tmp;
while (std::getline(osRel, line))
{
if (!(tmp = TryRead(line, "PRETTY_NAME=\"")).empty())
{
osName = std::move(tmp);
}
else if (!(tmp = TryRead(line, "NAME=\"")).empty())
{
name = std::move(tmp);
}
else if (name.empty() && Utils::StartsWith(line, "ID=\""))
{
name = line.substr(4, line.length() - 5);
}
else if (!(tmp = TryRead(line, "BUILD_ID=\"")).empty())
{
versionString = std::move(tmp);
}
else if (!(tmp = TryRead(line, "VERSION_ID=\"")).empty())
{
versionString = std::move(tmp);
}
}
if (osName.empty()) osName = name;
osNameFormatted = osName + " " + versionString;
if (!versionString.empty()) version = ParseVersionString(versionString.data());
}
if (osName.empty() || versionString.empty())
{ // fallback to uname
struct utsname buffer;
errno = 0;
if (uname(&buffer) == 0)
{
osName = buffer.sysname;
osNameFormatted = osName + " " + buffer.release;
version = ParseVersionString(buffer.release);
return;
}
perror("uname");
}
if (osName.empty()) osName = "Linux";
if (osNameFormatted.empty()) osNameFormatted = "Linux";
}
};
OsInfo osInfo;
}
const std::string& SystemInfo::GetOsName()
{
return osInfo.osName;
}
OsVersion SystemInfo::GetOsVersion()
{
return osInfo.version;
}
const std::string& SystemInfo::GetOsNameHumanReadable()
{
return osInfo.osNameFormatted;
}
DeviceType SystemInfo::GetDeviceType()
{
return DeviceType::PC; // TODO?
}
size_t SystemInfo::GetCpuCoreCount()
{
return 0; //TODO
}
size_t SystemInfo::GetCpuThreadCount()
{
static const size_t threadCount = std::thread::hardware_concurrency();
return threadCount;
}
int32_t SystemInfo::GetCpuTemperature()
{
std::ifstream tempFile("/sys/class/thermal/thermal_zone0/temp");
if (!tempFile)
{
Logger::PERF->error("Failed to open the temperature file");
return 20;
}
std::string tempStr;
std::getline(tempFile, tempStr);
return static_cast<int>(std::stof(tempStr) / 1000.0f);
}
CpuThermalState SystemInfo::GetCpuThermalState()
{
auto temp = GetCpuTemperature();
if (temp < 70) return CpuThermalState::Normal;
if (temp < 80) return CpuThermalState::Fair;
if (temp < 95) return CpuThermalState::Serious;
return CpuThermalState::Critical;
}
bool SystemInfo::IsDeviceInLowPowerMode()
{
return false; //TODO?
}
BatteryState SystemInfo::GetDeviceBatteryState()
{
std::ifstream powerSupplyFile("/sys/class/power_supply/BAT0/status");
if (powerSupplyFile)
{
std::string status;
std::getline(powerSupplyFile, status);
if (status == "Charging") return BatteryState::Charging;
if (status == "Not charging") return BatteryState::NotCharging;
if (status == "Full") return BatteryState::ChargingFull;
if (status == "Discharging") return BatteryState::Unplugged;
}
return BatteryState::Unavailable;
}
float SystemInfo::GetDeviceBatteryLevel()
{
std::ifstream powerSupplyFile("/sys/class/power_supply/BAT0/capacity");
if (powerSupplyFile)
{
float value;
powerSupplyFile >> value;
return value / 100.0f;
}
return -1;
}
void SystemInfo::EnableEnergyEvents()
{
//TODO
}
bool SystemInfo::IsMultitaskingSupported()
{
return true;
}
DeviceOrientation SystemInfo::GetDeviceOrientation()
{
return DeviceOrientation::LandscapeRight; // TODO?
}
void SystemInfo::EnableDeviceOrientationEvents()
{
//TODO?
}
InterfaceOrientation SystemInfo::GetInterfaceOrientation()
{
return InterfaceOrientation::Landscape; // TODO?
}
}