mirror of
https://github.com/RetroDECK/ES-DE.git
synced 2025-04-10 19:15:13 +00:00
Added a SystemStatus class to poll Bluetooth, Wi-Fi, cellular and battery information from the operating system
This commit is contained in:
parent
b50b4d9d0a
commit
e59f19c2ba
|
@ -123,7 +123,9 @@ if(IOS)
|
||||||
elseif(APPLE)
|
elseif(APPLE)
|
||||||
include_directories(${COMMON_INCLUDE_DIRS} ${CMAKE_CURRENT_SOURCE_DIR}/src)
|
include_directories(${COMMON_INCLUDE_DIRS} ${CMAKE_CURRENT_SOURCE_DIR}/src)
|
||||||
add_executable(ES-DE ${ES_SOURCES} ${ES_HEADERS})
|
add_executable(ES-DE ${ES_SOURCES} ${ES_HEADERS})
|
||||||
target_link_libraries(ES-DE ${COMMON_LIBRARIES} es-core)
|
target_link_libraries(ES-DE ${COMMON_LIBRARIES} "-framework CoreFoundation -framework IOKit"
|
||||||
|
"-framework SystemConfiguration"
|
||||||
|
"-framework IOBluetooth" es-core)
|
||||||
set_target_properties(ES-DE PROPERTIES INSTALL_RPATH_USE_LINK_PATH TRUE)
|
set_target_properties(ES-DE PROPERTIES INSTALL_RPATH_USE_LINK_PATH TRUE)
|
||||||
if(CMAKE_CXX_COMPILER_VERSION GREATER_EQUAL 15.0.0)
|
if(CMAKE_CXX_COMPILER_VERSION GREATER_EQUAL 15.0.0)
|
||||||
target_link_options(ES-DE PRIVATE LINKER:-no_warn_duplicate_libraries)
|
target_link_options(ES-DE PRIVATE LINKER:-no_warn_duplicate_libraries)
|
||||||
|
|
|
@ -26,6 +26,7 @@
|
||||||
#include "Settings.h"
|
#include "Settings.h"
|
||||||
#include "Sound.h"
|
#include "Sound.h"
|
||||||
#include "SystemData.h"
|
#include "SystemData.h"
|
||||||
|
#include "SystemStatus.h"
|
||||||
#include "guis/GuiDetectDevice.h"
|
#include "guis/GuiDetectDevice.h"
|
||||||
#include "guis/GuiLaunchScreen.h"
|
#include "guis/GuiLaunchScreen.h"
|
||||||
#include "utils/FileSystemUtil.h"
|
#include "utils/FileSystemUtil.h"
|
||||||
|
@ -1165,6 +1166,7 @@ int main(int argc, char* argv[])
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
SystemStatus::getInstance();
|
||||||
MameNames::getInstance();
|
MameNames::getInstance();
|
||||||
ThemeData::populateThemes();
|
ThemeData::populateThemes();
|
||||||
loadSystemsReturnCode loadSystemsStatus {loadSystemConfigFile()};
|
loadSystemsReturnCode loadSystemsStatus {loadSystemConfigFile()};
|
||||||
|
|
|
@ -22,6 +22,7 @@ set(CORE_HEADERS
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/src/MameNames.h
|
${CMAKE_CURRENT_SOURCE_DIR}/src/MameNames.h
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Settings.h
|
${CMAKE_CURRENT_SOURCE_DIR}/src/Settings.h
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Sound.h
|
${CMAKE_CURRENT_SOURCE_DIR}/src/Sound.h
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/src/SystemStatus.h
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/src/ThemeData.h
|
${CMAKE_CURRENT_SOURCE_DIR}/src/ThemeData.h
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Window.h
|
${CMAKE_CURRENT_SOURCE_DIR}/src/Window.h
|
||||||
|
|
||||||
|
@ -110,6 +111,7 @@ set(CORE_SOURCES
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Scripting.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/src/Scripting.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Settings.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/src/Settings.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Sound.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/src/Sound.cpp
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/src/SystemStatus.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/src/ThemeData.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/src/ThemeData.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Window.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/src/Window.cpp
|
||||||
|
|
||||||
|
@ -179,6 +181,10 @@ if(ANDROID)
|
||||||
set(CORE_SOURCES ${CORE_SOURCES} ${CMAKE_CURRENT_SOURCE_DIR}/src/utils/PlatformUtilAndroid.cpp)
|
set(CORE_SOURCES ${CORE_SOURCES} ${CMAKE_CURRENT_SOURCE_DIR}/src/utils/PlatformUtilAndroid.cpp)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if(APPLE AND NOT IOS)
|
||||||
|
set(CORE_HEADERS ${CORE_HEADERS} ${CMAKE_CURRENT_SOURCE_DIR}/src/BluetoothStatusApple.m)
|
||||||
|
endif()
|
||||||
|
|
||||||
if(IOS)
|
if(IOS)
|
||||||
set(CORE_HEADERS ${CORE_HEADERS} ${CMAKE_CURRENT_SOURCE_DIR}/src/InputOverlay.h)
|
set(CORE_HEADERS ${CORE_HEADERS} ${CMAKE_CURRENT_SOURCE_DIR}/src/InputOverlay.h)
|
||||||
set(CORE_HEADERS ${CORE_HEADERS} ${CMAKE_CURRENT_SOURCE_DIR}/src/utils/PlatformUtilIOS.h)
|
set(CORE_HEADERS ${CORE_HEADERS} ${CMAKE_CURRENT_SOURCE_DIR}/src/utils/PlatformUtilIOS.h)
|
||||||
|
|
18
es-core/src/BluetoothStatusApple.h
Normal file
18
es-core/src/BluetoothStatusApple.h
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
//
|
||||||
|
// ES-DE Frontend
|
||||||
|
// BluetoothStatusApple.h
|
||||||
|
//
|
||||||
|
// Gets the Bluetooth adapter status on macOS.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef ES_CORE_BLUETOOTH_STATUS_APPLE_H
|
||||||
|
#define ES_CORE_BLUETOOTH_STATUS_APPLE_H
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
int getBluetoothStatus();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // ES_CORE_BLUETOOTH_STATUS_APPLE_H
|
21
es-core/src/BluetoothStatusApple.m
Normal file
21
es-core/src/BluetoothStatusApple.m
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
//
|
||||||
|
// ES-DE Frontend
|
||||||
|
// BluetoothStatusApple.m
|
||||||
|
//
|
||||||
|
// Gets the Bluetooth adapter status on macOS.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import "BluetoothStatusApple.h"
|
||||||
|
|
||||||
|
#import <IOBluetooth/IOBluetooth.h>
|
||||||
|
|
||||||
|
int getBluetoothStatus()
|
||||||
|
{
|
||||||
|
IOBluetoothHostController* hciController = [IOBluetoothHostController defaultController];
|
||||||
|
|
||||||
|
if (hciController != NULL && hciController.powerState)
|
||||||
|
return 1;
|
||||||
|
else
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -221,6 +221,12 @@ void Settings::setDefaults()
|
||||||
#endif
|
#endif
|
||||||
mBoolMap["ScreensaverVideoBlur"] = {false, false};
|
mBoolMap["ScreensaverVideoBlur"] = {false, false};
|
||||||
|
|
||||||
|
mBoolMap["SystemStatusDisplayAll"] = {false, false};
|
||||||
|
mBoolMap["SystemStatusBluetooth"] = {true, true};
|
||||||
|
mBoolMap["SystemStatusWifi"] = {true, true};
|
||||||
|
mBoolMap["SystemStatusCellular"] = {true, true};
|
||||||
|
mBoolMap["SystemStatusBattery"] = {true, true};
|
||||||
|
mBoolMap["SystemStatusBatteryPercentage"] = {true, true};
|
||||||
mBoolMap["ThemeVariantTriggers"] = {true, true};
|
mBoolMap["ThemeVariantTriggers"] = {true, true};
|
||||||
mBoolMap["DisplayClock"] = {false, false};
|
mBoolMap["DisplayClock"] = {false, false};
|
||||||
mBoolMap["MenuBlurBackground"] = {true, true};
|
mBoolMap["MenuBlurBackground"] = {true, true};
|
||||||
|
|
444
es-core/src/SystemStatus.cpp
Normal file
444
es-core/src/SystemStatus.cpp
Normal file
|
@ -0,0 +1,444 @@
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
//
|
||||||
|
// ES-DE Frontend
|
||||||
|
// SystemStatus.cpp
|
||||||
|
//
|
||||||
|
// Queries system status information from the operating system.
|
||||||
|
// This includes Bluetooth, Wi-Fi, cellular and battery.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "SystemStatus.h"
|
||||||
|
|
||||||
|
#include "Log.h"
|
||||||
|
#include "Settings.h"
|
||||||
|
#include "utils/FileSystemUtil.h"
|
||||||
|
#include "utils/StringUtil.h"
|
||||||
|
|
||||||
|
#include <SDL2/SDL_timer.h>
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
#if defined(__APPLE__) && !defined(__IOS__)
|
||||||
|
#include "BluetoothStatusApple.h"
|
||||||
|
#include <IOKit/ps/IOPSKeys.h>
|
||||||
|
#include <IOKit/ps/IOPowerSources.h>
|
||||||
|
#include <SystemConfiguration/SCNetworkConfiguration.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(_WIN64)
|
||||||
|
// clang-format off
|
||||||
|
// Because of course building fails if the files are included in the "wrong" order.
|
||||||
|
#include <windows.h>
|
||||||
|
#include <iphlpapi.h>
|
||||||
|
#include <bluetoothapis.h>
|
||||||
|
// clang-format on
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(__ANDROID__)
|
||||||
|
#include "utils/PlatformUtilAndroid.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define DEBUG_SYSTEM_STATUS false
|
||||||
|
|
||||||
|
SystemStatus::SystemStatus() noexcept
|
||||||
|
: mExitPolling {false}
|
||||||
|
, mPollImmediately {false}
|
||||||
|
, mHasBluetooth {false}
|
||||||
|
, mHasWifi {false}
|
||||||
|
, mHasCellular {false}
|
||||||
|
, mHasBattery {false}
|
||||||
|
, mBatteryCharging {false}
|
||||||
|
, mBatteryCapacity {0}
|
||||||
|
|
||||||
|
{
|
||||||
|
setCheckFlags();
|
||||||
|
mPollThread = std::make_unique<std::thread>(&SystemStatus::pollStatus, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
SystemStatus::~SystemStatus()
|
||||||
|
{
|
||||||
|
mExitPolling = true;
|
||||||
|
|
||||||
|
if (mPollThread != nullptr && mPollThread->joinable()) {
|
||||||
|
mPollThread->join();
|
||||||
|
mPollThread.reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SystemStatus& SystemStatus::getInstance()
|
||||||
|
{
|
||||||
|
static SystemStatus instance;
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SystemStatus::setCheckFlags()
|
||||||
|
{
|
||||||
|
std::unique_lock<std::mutex> statusLock {mStatusMutex};
|
||||||
|
mCheckBluetooth = Settings::getInstance()->getBool("SystemStatusBluetooth");
|
||||||
|
mCheckWifi = Settings::getInstance()->getBool("SystemStatusWifi");
|
||||||
|
mCheckCellular = Settings::getInstance()->getBool("SystemStatusCellular");
|
||||||
|
mCheckBattery = Settings::getInstance()->getBool("SystemStatusBattery");
|
||||||
|
}
|
||||||
|
|
||||||
|
void SystemStatus::setPolling(const bool state)
|
||||||
|
{
|
||||||
|
if (state == false) {
|
||||||
|
mExitPolling = true;
|
||||||
|
if (mPollThread != nullptr && mPollThread->joinable()) {
|
||||||
|
mPollThread->join();
|
||||||
|
mPollThread.reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (mPollThread == nullptr) {
|
||||||
|
mExitPolling = false;
|
||||||
|
mPollThread = std::make_unique<std::thread>(&SystemStatus::pollStatus, this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SystemStatus::Status SystemStatus::getStatus()
|
||||||
|
{
|
||||||
|
mStatus.hasBluetooth = mHasBluetooth;
|
||||||
|
mStatus.hasWifi = mHasWifi;
|
||||||
|
mStatus.hasCellular = mHasCellular;
|
||||||
|
mStatus.hasBattery = mHasBattery;
|
||||||
|
mStatus.batteryCharging = mBatteryCharging;
|
||||||
|
mStatus.batteryCapacity = mBatteryCapacity;
|
||||||
|
|
||||||
|
return mStatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SystemStatus::pollStatus()
|
||||||
|
{
|
||||||
|
while (!mExitPolling) {
|
||||||
|
std::unique_lock<std::mutex> statusLock {mStatusMutex};
|
||||||
|
|
||||||
|
getStatusBluetooth();
|
||||||
|
getStatusWifi();
|
||||||
|
getStatusCellular();
|
||||||
|
getStatusBattery();
|
||||||
|
statusLock.unlock();
|
||||||
|
|
||||||
|
#if (DEBUG_SYSTEM_STATUS)
|
||||||
|
std::string status {"Bluetooth "};
|
||||||
|
status.append(mHasBluetooth ? "enabled" : "disabled")
|
||||||
|
.append(", Wi-Fi ")
|
||||||
|
.append(mHasBluetooth ? "enabled" : "disabled")
|
||||||
|
.append(", cellular ")
|
||||||
|
.append(mHasCellular ? "enabled" : "disabled")
|
||||||
|
.append(", battery ")
|
||||||
|
.append(mHasBattery ? "enabled" : "disabled");
|
||||||
|
if (mHasBattery) {
|
||||||
|
status.append(" (")
|
||||||
|
.append(mBatteryCharging ? "charging" : "not charging")
|
||||||
|
.append(" and at ")
|
||||||
|
.append(std::to_string(mBatteryCapacity))
|
||||||
|
.append("% capacity)");
|
||||||
|
}
|
||||||
|
LOG(LogDebug) << "SystemStatus::pollStatus(): " << status;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int delayValue {0};
|
||||||
|
while (!mPollImmediately && !mExitPolling && delayValue < 3000) {
|
||||||
|
delayValue += 100;
|
||||||
|
SDL_Delay(100);
|
||||||
|
}
|
||||||
|
|
||||||
|
mPollImmediately = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SystemStatus::getStatusBluetooth()
|
||||||
|
{
|
||||||
|
if (!mCheckBluetooth)
|
||||||
|
return;
|
||||||
|
|
||||||
|
bool hasBluetooth {false};
|
||||||
|
|
||||||
|
#if defined(__APPLE__) && !defined(__IOS__)
|
||||||
|
if (getBluetoothStatus() == 1)
|
||||||
|
hasBluetooth = true;
|
||||||
|
|
||||||
|
#elif defined(_WIN64)
|
||||||
|
BLUETOOTH_FIND_RADIO_PARAMS btFindRadio {sizeof(BLUETOOTH_FIND_RADIO_PARAMS)};
|
||||||
|
HANDLE btRadio {nullptr};
|
||||||
|
BLUETOOTH_RADIO_INFO btInfo {sizeof(BLUETOOTH_RADIO_INFO), 0};
|
||||||
|
|
||||||
|
if (BluetoothFindFirstRadio(&btFindRadio, &btRadio) != nullptr) {
|
||||||
|
if (BluetoothGetRadioInfo(btRadio, &btInfo) == ERROR_SUCCESS)
|
||||||
|
hasBluetooth = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
#elif defined(__ANDROID__)
|
||||||
|
if (Utils::Platform::Android::getBluetoothStatus())
|
||||||
|
hasBluetooth = true;
|
||||||
|
|
||||||
|
#elif defined(__linux__)
|
||||||
|
const std::string sysEntry {"/sys/class/rfkill"};
|
||||||
|
auto entries {Utils::FileSystem::getDirContent(sysEntry, false)};
|
||||||
|
for (auto& entry : entries) {
|
||||||
|
if (Utils::FileSystem::exists(entry + "/type")) {
|
||||||
|
std::string type;
|
||||||
|
std::ifstream fileStream;
|
||||||
|
fileStream.open(entry + "/type");
|
||||||
|
getline(fileStream, type);
|
||||||
|
fileStream.close();
|
||||||
|
if (Utils::String::toLower(type) == "bluetooth") {
|
||||||
|
std::string state;
|
||||||
|
fileStream.open(entry + "/state");
|
||||||
|
getline(fileStream, state);
|
||||||
|
fileStream.close();
|
||||||
|
if (std::stoi(state) == 1)
|
||||||
|
hasBluetooth = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
mHasBluetooth = hasBluetooth;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SystemStatus::getStatusWifi()
|
||||||
|
{
|
||||||
|
if (!mCheckWifi)
|
||||||
|
return;
|
||||||
|
|
||||||
|
bool hasWifi {false};
|
||||||
|
|
||||||
|
#if defined(__APPLE__) && !defined(__IOS__)
|
||||||
|
const CFArrayRef interfaces {SCNetworkInterfaceCopyAll()};
|
||||||
|
|
||||||
|
if (interfaces != nullptr) {
|
||||||
|
for (CFIndex i {0}; i < CFArrayGetCount(interfaces); ++i) {
|
||||||
|
SCNetworkInterfaceRef interface {
|
||||||
|
static_cast<SCNetworkInterfaceRef>(CFArrayGetValueAtIndex(interfaces, i))};
|
||||||
|
|
||||||
|
if (SCNetworkInterfaceGetInterfaceType(interface) == kSCNetworkInterfaceTypeIEEE80211) {
|
||||||
|
const CFStringRef bsdName {SCNetworkInterfaceGetBSDName(interface)};
|
||||||
|
|
||||||
|
const SCDynamicStoreRef session {
|
||||||
|
SCDynamicStoreCreate(nullptr, CFSTR("Custom"), nullptr, nullptr)};
|
||||||
|
|
||||||
|
const CFStringRef resolvedQuery {CFStringCreateWithFormat(
|
||||||
|
nullptr, nullptr, CFSTR("State:/Network/Interface/%@/IPv4"), bsdName)};
|
||||||
|
|
||||||
|
const CFDictionaryRef dict {
|
||||||
|
static_cast<CFDictionaryRef>(SCDynamicStoreCopyValue(session, resolvedQuery))};
|
||||||
|
|
||||||
|
if (dict != nullptr) {
|
||||||
|
hasWifi = true;
|
||||||
|
CFRelease(dict);
|
||||||
|
CFRelease(resolvedQuery);
|
||||||
|
CFRelease(session);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
CFRelease(resolvedQuery);
|
||||||
|
CFRelease(session);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
CFRelease(interfaces);
|
||||||
|
}
|
||||||
|
|
||||||
|
#elif defined(_WIN64)
|
||||||
|
PIP_ADAPTER_INFO pAdapterInfo {nullptr};
|
||||||
|
PIP_ADAPTER_INFO pAdapter {nullptr};
|
||||||
|
ULONG ulOutBufLen {sizeof(IP_ADAPTER_INFO)};
|
||||||
|
pAdapterInfo = reinterpret_cast<IP_ADAPTER_INFO*>(malloc(sizeof(IP_ADAPTER_INFO)));
|
||||||
|
|
||||||
|
if (pAdapterInfo != nullptr) {
|
||||||
|
// Make an initial call to GetAdaptersInfo to get the necessary size into the
|
||||||
|
// ulOutBufLen variable, which may or may not be big enough.
|
||||||
|
if (GetAdaptersInfo(pAdapterInfo, &ulOutBufLen) == ERROR_BUFFER_OVERFLOW) {
|
||||||
|
free(pAdapterInfo);
|
||||||
|
pAdapterInfo = reinterpret_cast<IP_ADAPTER_INFO*>(malloc(ulOutBufLen));
|
||||||
|
}
|
||||||
|
if (GetAdaptersInfo(pAdapterInfo, &ulOutBufLen) == NO_ERROR) {
|
||||||
|
pAdapter = pAdapterInfo;
|
||||||
|
while (pAdapter) {
|
||||||
|
if (pAdapter->Type == IF_TYPE_IEEE80211) {
|
||||||
|
// Checking whether the interface has an IP address is crude but
|
||||||
|
// it seems to get the job done. And there is no other obvious
|
||||||
|
// way to query the interface status without using additional
|
||||||
|
// convoluted API calls.
|
||||||
|
if (const std::string {pAdapter->IpAddressList.IpAddress.String} != "0.0.0.0") {
|
||||||
|
hasWifi = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pAdapter = pAdapter->Next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pAdapterInfo)
|
||||||
|
free(pAdapterInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
#elif defined(__ANDROID__)
|
||||||
|
if (Utils::Platform::Android::getWifiStatus() == 1)
|
||||||
|
hasWifi = true;
|
||||||
|
|
||||||
|
#elif defined(__linux__)
|
||||||
|
const std::string sysEntry {"/sys/class/net"};
|
||||||
|
auto entries {Utils::FileSystem::getDirContent(sysEntry, false)};
|
||||||
|
for (auto& entry : entries) {
|
||||||
|
if (Utils::FileSystem::exists(entry + "/wireless") &&
|
||||||
|
Utils::FileSystem::exists(entry + "/operstate")) {
|
||||||
|
std::string wifiState;
|
||||||
|
std::ifstream fileStream;
|
||||||
|
fileStream.open(entry + "/operstate");
|
||||||
|
getline(fileStream, wifiState);
|
||||||
|
fileStream.close();
|
||||||
|
if (Utils::String::toLower(wifiState) == "up")
|
||||||
|
hasWifi = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
mHasWifi = hasWifi;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SystemStatus::getStatusCellular()
|
||||||
|
{
|
||||||
|
if (!mCheckCellular)
|
||||||
|
return;
|
||||||
|
|
||||||
|
bool hasCellular {false};
|
||||||
|
|
||||||
|
#if defined(__ANDROID__)
|
||||||
|
if (Utils::Platform::Android::getCellularStatus() >= 1)
|
||||||
|
hasCellular = true;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
mHasCellular = hasCellular;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SystemStatus::getStatusBattery()
|
||||||
|
{
|
||||||
|
if (!mCheckBattery)
|
||||||
|
return;
|
||||||
|
|
||||||
|
bool hasBattery {false};
|
||||||
|
bool batteryCharging {false};
|
||||||
|
int batteryCapacity {0};
|
||||||
|
|
||||||
|
#if defined(__APPLE__) && !defined(__IOS__)
|
||||||
|
CFTypeRef sourceInfo {IOPSCopyPowerSourcesInfo()};
|
||||||
|
CFArrayRef sourceList {IOPSCopyPowerSourcesList(sourceInfo)};
|
||||||
|
|
||||||
|
if (sourceList != nullptr && CFArrayGetCount(sourceList) > 0) {
|
||||||
|
CFDictionaryRef source {nullptr};
|
||||||
|
|
||||||
|
for (CFIndex i {0}; i < CFArrayGetCount(sourceList); ++i) {
|
||||||
|
source =
|
||||||
|
IOPSGetPowerSourceDescription(sourceInfo, CFArrayGetValueAtIndex(sourceList, i));
|
||||||
|
// Check if this is a battery.
|
||||||
|
const CFStringRef type {static_cast<CFStringRef>(
|
||||||
|
CFDictionaryGetValue(source, CFSTR(kIOPSTransportTypeKey)))};
|
||||||
|
if (kCFCompareEqualTo == CFStringCompare(type, CFSTR(kIOPSInternalType), 0))
|
||||||
|
break;
|
||||||
|
else
|
||||||
|
source = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (source != nullptr) {
|
||||||
|
hasBattery = true;
|
||||||
|
|
||||||
|
if (CFDictionaryGetValue(source, CFSTR(kIOPSIsChargingKey)) != nullptr) {
|
||||||
|
batteryCharging = CFBooleanGetValue(static_cast<CFBooleanRef>(
|
||||||
|
CFDictionaryGetValue(source, CFSTR(kIOPSIsChargingKey))));
|
||||||
|
}
|
||||||
|
|
||||||
|
int curCapacity {0};
|
||||||
|
const CFNumberRef curCapacityNum {static_cast<CFNumberRef>(
|
||||||
|
CFDictionaryGetValue(source, CFSTR(kIOPSCurrentCapacityKey)))};
|
||||||
|
CFNumberGetValue(curCapacityNum, kCFNumberIntType, &curCapacity);
|
||||||
|
|
||||||
|
int maxCapacity {0};
|
||||||
|
const CFNumberRef maxCapacityNum {
|
||||||
|
static_cast<CFNumberRef>(CFDictionaryGetValue(source, CFSTR(kIOPSMaxCapacityKey)))};
|
||||||
|
CFNumberGetValue(maxCapacityNum, kCFNumberIntType, &maxCapacity);
|
||||||
|
|
||||||
|
if (maxCapacity > 0)
|
||||||
|
batteryCapacity = curCapacity / maxCapacity * 100;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sourceInfo != nullptr)
|
||||||
|
CFRelease(sourceInfo);
|
||||||
|
if (sourceList != nullptr)
|
||||||
|
CFRelease(sourceList);
|
||||||
|
|
||||||
|
#elif defined(_WIN64)
|
||||||
|
SYSTEM_POWER_STATUS powerStatus;
|
||||||
|
|
||||||
|
if (GetSystemPowerStatus(&powerStatus)) {
|
||||||
|
if (powerStatus.BatteryFlag != 128 && powerStatus.BatteryFlag != 255) {
|
||||||
|
hasBattery = true;
|
||||||
|
|
||||||
|
if (powerStatus.ACLineStatus == 1)
|
||||||
|
atteryCharging = true;
|
||||||
|
|
||||||
|
batteryCapacity = powerStatus.BatteryLifePercent;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
hasBattery = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#elif defined(__ANDROID__)
|
||||||
|
std::pair<int, int> batteryStatus {Utils::Platform::Android::getBatteryStatus()};
|
||||||
|
hasBattery = static_cast<bool>(batteryStatus.first);
|
||||||
|
|
||||||
|
if (batteryStatus.first == -1 && batteryStatus.second == -1) {
|
||||||
|
hasBattery = false;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
hasBattery = true;
|
||||||
|
if (batteryStatus.first == 1)
|
||||||
|
batteryCharging = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
batteryCapacity = batteryStatus.second;
|
||||||
|
|
||||||
|
#elif defined(__linux__)
|
||||||
|
const std::string sysEntry {"/sys/class/power_supply"};
|
||||||
|
std::string batteryDir;
|
||||||
|
auto entries {Utils::FileSystem::getDirContent(sysEntry, false)};
|
||||||
|
if (std::find(entries.cbegin(), entries.cend(), sysEntry + "/BAT0") != entries.cend())
|
||||||
|
batteryDir = sysEntry + "/BAT0";
|
||||||
|
else if (std::find(entries.cbegin(), entries.cend(), sysEntry + "/BAT1") != entries.cend())
|
||||||
|
batteryDir = sysEntry + "/BAT1";
|
||||||
|
else if (std::find(entries.cbegin(), entries.cend(), sysEntry + "/battery") != entries.cend())
|
||||||
|
batteryDir = sysEntry + "/battery";
|
||||||
|
|
||||||
|
if (!Utils::FileSystem::exists(batteryDir + "/status"))
|
||||||
|
hasBattery = false;
|
||||||
|
if (!Utils::FileSystem::exists(batteryDir + "/capacity"))
|
||||||
|
hasBattery = false;
|
||||||
|
|
||||||
|
if (hasBattery) {
|
||||||
|
std::string batteryStatusValue;
|
||||||
|
std::string batteryCapacityValue;
|
||||||
|
std::ifstream fileStream;
|
||||||
|
fileStream.open(batteryDir + "/status");
|
||||||
|
getline(fileStream, batteryStatusValue);
|
||||||
|
batteryStatusValue = Utils::String::toLower(batteryStatusValue);
|
||||||
|
fileStream.close();
|
||||||
|
|
||||||
|
if (batteryStatusValue != "discharging")
|
||||||
|
batteryCharging = true;
|
||||||
|
|
||||||
|
fileStream.open(batteryDir + "/capacity");
|
||||||
|
getline(fileStream, batteryCapacityValue);
|
||||||
|
fileStream.close();
|
||||||
|
|
||||||
|
batteryCapacity = std::stoi(batteryCapacityValue);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
mHasBattery = hasBattery;
|
||||||
|
mBatteryCharging = batteryCharging;
|
||||||
|
mBatteryCapacity = batteryCapacity;
|
||||||
|
}
|
77
es-core/src/SystemStatus.h
Normal file
77
es-core/src/SystemStatus.h
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
//
|
||||||
|
// ES-DE Frontend
|
||||||
|
// SystemStatus.h
|
||||||
|
//
|
||||||
|
// Queries system status information from the operating system.
|
||||||
|
// This includes Bluetooth, Wi-Fi, cellular and battery.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef ES_CORE_SYSTEM_STATUS_H
|
||||||
|
#define ES_CORE_SYSTEM_STATUS_H
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
|
#include <mutex>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
|
class SystemStatus
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
~SystemStatus();
|
||||||
|
static SystemStatus& getInstance();
|
||||||
|
|
||||||
|
void setCheckFlags();
|
||||||
|
void setPolling(const bool state);
|
||||||
|
void pollImmediately() { mPollImmediately = true; }
|
||||||
|
|
||||||
|
struct Status {
|
||||||
|
bool hasBluetooth;
|
||||||
|
bool hasWifi;
|
||||||
|
bool hasCellular;
|
||||||
|
bool hasBattery;
|
||||||
|
bool batteryCharging;
|
||||||
|
int batteryCapacity;
|
||||||
|
Status()
|
||||||
|
: hasBluetooth {false}
|
||||||
|
, hasWifi {false}
|
||||||
|
, hasCellular {false}
|
||||||
|
, hasBattery {false}
|
||||||
|
, batteryCharging {false}
|
||||||
|
, batteryCapacity {0}
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Status getStatus();
|
||||||
|
|
||||||
|
private:
|
||||||
|
SystemStatus() noexcept;
|
||||||
|
|
||||||
|
void pollStatus();
|
||||||
|
|
||||||
|
void getStatusBluetooth();
|
||||||
|
void getStatusWifi();
|
||||||
|
void getStatusCellular();
|
||||||
|
void getStatusBattery();
|
||||||
|
|
||||||
|
bool mCheckBluetooth;
|
||||||
|
bool mCheckWifi;
|
||||||
|
bool mCheckCellular;
|
||||||
|
bool mCheckBattery;
|
||||||
|
|
||||||
|
std::unique_ptr<std::thread> mPollThread;
|
||||||
|
Status mStatus;
|
||||||
|
std::mutex mStatusMutex;
|
||||||
|
|
||||||
|
std::atomic<bool> mExitPolling;
|
||||||
|
std::atomic<bool> mPollImmediately;
|
||||||
|
|
||||||
|
std::atomic<bool> mHasBluetooth;
|
||||||
|
std::atomic<bool> mHasWifi;
|
||||||
|
std::atomic<bool> mHasCellular;
|
||||||
|
std::atomic<bool> mHasBattery;
|
||||||
|
std::atomic<bool> mBatteryCharging;
|
||||||
|
std::atomic<int> mBatteryCapacity;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // ES_CORE_SYSTEM_STATUS_H
|
Loading…
Reference in a new issue