mirror of
https://github.com/baketnk/frame-yap.git
synced 2026-10-04 22:00:03 +02:00
Add read-only Linux system battery reader
Reads the device's own battery from /sys/class/power_supply, skipping peripheral (scope=Device) supplies and implausible values. Offline tested against a fake power_supply tree. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
94b747bb04
commit
59f4f9cff8
4 files changed
+93
-1
No files matched your search
+7
-1
@@ -62,6 +62,8 @@ if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
|
||||
target_include_directories(frameyap_mount PUBLIC src)
|
||||
add_library(frameyap_worker src/worker.cpp)
|
||||
target_include_directories(frameyap_worker PUBLIC src)
|
||||
add_library(frameyap_battery src/battery.cpp)
|
||||
target_include_directories(frameyap_battery PUBLIC src)
|
||||
endif()
|
||||
if(FRAMEYAP_NATIVE)
|
||||
if(NOT CMAKE_SYSTEM_NAME STREQUAL "Linux")
|
||||
@@ -100,7 +102,7 @@ if(FRAMEYAP_NATIVE)
|
||||
target_sources(frameyap PRIVATE src/runtime.cpp src/controller.cpp src/backend_manager.cpp src/overlay.cpp src/overlay_texture.cpp src/audio.cpp src/text_input.cpp src/focus_guard.cpp
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/gamescope-input-method.c" "${CMAKE_CURRENT_BINARY_DIR}/gamescope-input-method-client.h")
|
||||
target_include_directories(frameyap PRIVATE "${OPENVR_INCLUDE_DIR}" "${CMAKE_CURRENT_BINARY_DIR}")
|
||||
target_link_libraries(frameyap PRIVATE frameyap_paced frameyap_worker frameyap_mount frameyap_panel PkgConfig::SDL3 PkgConfig::WAYLAND PkgConfig::XCB Vulkan::Vulkan "${OPENVR_LIBRARY}")
|
||||
target_link_libraries(frameyap PRIVATE frameyap_paced frameyap_worker frameyap_mount frameyap_battery frameyap_panel PkgConfig::SDL3 PkgConfig::WAYLAND PkgConfig::XCB Vulkan::Vulkan "${OPENVR_LIBRARY}")
|
||||
target_compile_definitions(frameyap PRIVATE FRAMEYAP_NATIVE=1)
|
||||
set_target_properties(frameyap PROPERTIES INSTALL_RPATH "$ORIGIN/../lib")
|
||||
endif()
|
||||
@@ -176,6 +178,10 @@ if(BUILD_TESTING AND NOT CMAKE_CROSSCOMPILING)
|
||||
add_executable(frameyap_lock_test tests/lock_test.cpp)
|
||||
target_include_directories(frameyap_lock_test PRIVATE src)
|
||||
add_test(NAME frameyap.instance_lock COMMAND frameyap_lock_test)
|
||||
add_executable(frameyap_battery_test tests/battery_test.cpp)
|
||||
target_link_libraries(frameyap_battery_test PRIVATE frameyap_battery)
|
||||
target_compile_options(frameyap_battery_test PRIVATE -UNDEBUG)
|
||||
add_test(NAME frameyap.battery COMMAND frameyap_battery_test)
|
||||
endif()
|
||||
add_executable(frameyap_panel_drag_test tests/panel_drag_test.cpp)
|
||||
target_include_directories(frameyap_panel_drag_test PRIVATE src)
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
#include "battery.hpp"
|
||||
#include <algorithm>
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
#include <system_error>
|
||||
#include <vector>
|
||||
|
||||
namespace frameyap {
|
||||
namespace {
|
||||
std::string first_line(const std::filesystem::path& path) {
|
||||
std::ifstream in(path);
|
||||
std::string line;
|
||||
if (in) std::getline(in, line);
|
||||
return line;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
std::optional<BatteryLevel> system_battery(const std::filesystem::path& root) {
|
||||
std::error_code error;
|
||||
std::vector<std::filesystem::path> supplies;
|
||||
for (const auto& entry : std::filesystem::directory_iterator(root, error)) supplies.push_back(entry.path());
|
||||
std::sort(supplies.begin(), supplies.end()); // deterministic choice among several
|
||||
for (const auto& supply : supplies) {
|
||||
if (first_line(supply / "type") != "Battery" || first_line(supply / "scope") == "Device") continue;
|
||||
const auto capacity = first_line(supply / "capacity");
|
||||
if (capacity.empty() || capacity.size() > 3 ||
|
||||
!std::all_of(capacity.begin(), capacity.end(), [](char c) { return c >= '0' && c <= '9'; }))
|
||||
continue;
|
||||
const int percent = std::stoi(capacity);
|
||||
if (percent > 100) continue;
|
||||
return BatteryLevel{percent, first_line(supply / "status") == "Charging"};
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
} // namespace frameyap
|
||||
@@ -0,0 +1,14 @@
|
||||
#pragma once
|
||||
#include <filesystem>
|
||||
#include <optional>
|
||||
|
||||
namespace frameyap {
|
||||
struct BatteryLevel {
|
||||
int percent = 0; // 0..100
|
||||
bool charging = false;
|
||||
bool operator==(const BatteryLevel&) const = default;
|
||||
};
|
||||
// The device's own battery from a Linux power_supply tree. Peripheral batteries
|
||||
// (scope=Device, such as paired controllers) are skipped. Read-only; no sudo.
|
||||
std::optional<BatteryLevel> system_battery(const std::filesystem::path& root = "/sys/class/power_supply");
|
||||
} // namespace frameyap
|
||||
@@ -0,0 +1,37 @@
|
||||
#include "battery.hpp"
|
||||
#include <cassert>
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
#include <unistd.h>
|
||||
|
||||
using namespace frameyap;
|
||||
namespace fs = std::filesystem;
|
||||
namespace {
|
||||
void put(const fs::path& path, const std::string& value) {
|
||||
fs::create_directories(path.parent_path());
|
||||
std::ofstream(path) << value << '\n';
|
||||
}
|
||||
}
|
||||
int main() {
|
||||
const auto root = fs::temp_directory_path() / ("frameyap-battery-" + std::to_string(::getpid()));
|
||||
fs::remove_all(root);
|
||||
assert(!system_battery(root)); // missing tree
|
||||
fs::create_directories(root);
|
||||
put(root / "AC/type", "Mains");
|
||||
put(root / "AC/online", "1");
|
||||
put(root / "hid-controller/type", "Battery"); // paired peripheral, not the headset
|
||||
put(root / "hid-controller/scope", "Device");
|
||||
put(root / "hid-controller/capacity", "12");
|
||||
assert(!system_battery(root));
|
||||
put(root / "BAT0/type", "Battery");
|
||||
put(root / "BAT0/capacity", "250"); // implausible values are ignored
|
||||
assert(!system_battery(root));
|
||||
put(root / "BAT0/capacity", "64");
|
||||
put(root / "BAT0/status", "Discharging");
|
||||
assert((system_battery(root) == BatteryLevel{64, false}));
|
||||
put(root / "BAT0/status", "Charging");
|
||||
assert((system_battery(root) == BatteryLevel{64, true}));
|
||||
put(root / "BAT0/capacity", "x1");
|
||||
assert(!system_battery(root));
|
||||
fs::remove_all(root);
|
||||
}
|
||||
Reference in new issue
Block a user