Rudimentary configuration file system.
This commit is contained in:
100
ConfigFile.h
Normal file
100
ConfigFile.h
Normal file
@ -0,0 +1,100 @@
|
||||
#pragma once
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <istream>
|
||||
#include <ostream>
|
||||
#include <optional>
|
||||
|
||||
namespace Config::inline Hydrogen {}
|
||||
|
||||
namespace Config::Hydrogen ::detail:: ConfigFile_m
|
||||
{
|
||||
inline namespace exports
|
||||
{
|
||||
class ConfigFile;
|
||||
}
|
||||
|
||||
class exports::ConfigFile
|
||||
{
|
||||
private:
|
||||
std::map< std::string, std::string > config;
|
||||
|
||||
struct EoF {};
|
||||
|
||||
std::string
|
||||
readLine( std::istream &input )
|
||||
{
|
||||
std::string rv;
|
||||
std::getline( input, rv );
|
||||
if( not input ) throw EoF{};
|
||||
|
||||
// SStrip trailing '#' comments...
|
||||
const auto endComment= std::find( begin( rv ), end( rv ), '#' );
|
||||
rv.erase( endComment, end( rv ) );
|
||||
return rv;
|
||||
}
|
||||
|
||||
std::string
|
||||
trimWhitespace( std::string s )
|
||||
{
|
||||
// Trim from the front
|
||||
std::reverse( begin( s ), end( s ) );
|
||||
while( not s.empty() and s.back() == ' ' ) s.pop_back();
|
||||
|
||||
// Trim from the back
|
||||
std::reverse( begin( s ), end( s ) );
|
||||
while( not s.empty() and s.back() == ' ' ) s.pop_back();
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
auto
|
||||
parseLine( const std::string &line )
|
||||
{
|
||||
const auto split= std::find( begin( line ), end( line ), '=' );
|
||||
if( split == end( line ) ) throw std::runtime_error{ "Unable to parse configuration line: `" + line + "`" };
|
||||
const auto key= trimWhitespace( { begin( line ), split } );
|
||||
const auto value= trimWhitespace( { split + 1, end( line ) } );
|
||||
|
||||
return std::tuple{ key, value };
|
||||
}
|
||||
|
||||
public:
|
||||
explicit
|
||||
ConfigFile( std::istream &&input, const std::map< std::string, std::string > &schema )
|
||||
{
|
||||
config= schema;
|
||||
while( input )
|
||||
try
|
||||
{
|
||||
const std::string line= trimWhitespace( readLine( input ) );
|
||||
if( line.empty() ) continue; // Skip blank lines.
|
||||
|
||||
const auto [ key, value ]= parseLine( line );
|
||||
|
||||
if( not schema.contains( key ) )
|
||||
{
|
||||
std::cerr << "Unrecognized key in config file: `" << key << "`" << std::endl;
|
||||
continue;
|
||||
}
|
||||
|
||||
config.at( key )= value;
|
||||
}
|
||||
catch( const EoF & ) { break; }
|
||||
|
||||
std::cerr << "Configuration parsed:" << std::endl;
|
||||
for( const auto &[ key, value ]: config )
|
||||
{
|
||||
std::cerr << key << " = " << value << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
std::string get( const std::string &name ) const { return config.at( name ); }
|
||||
};
|
||||
}
|
||||
|
||||
namespace Config::Hydrogen::inline exports::inline ConfigFile_m
|
||||
{
|
||||
using namespace detail::ConfigFile_m::exports;
|
||||
}
|
||||
@ -11,6 +11,8 @@
|
||||
#include <string>
|
||||
#include <filesystem>
|
||||
|
||||
#include "ConfigFile.h"
|
||||
|
||||
namespace C
|
||||
{
|
||||
const bool debug= false;
|
||||
@ -20,12 +22,15 @@ namespace C
|
||||
const bool debugUpdateCalled= false or C::debug;
|
||||
|
||||
const double updateTimeout= 0.1;
|
||||
}
|
||||
|
||||
const std::filesystem::path conservePath= "/sys/devices/pci0000:00/0000:00:1f.0/PNP0C09:00/VPC2004:00/conservation_mode";
|
||||
const std::filesystem::path powerPath= "/sys/class/power_supply/ADP0";
|
||||
const std::filesystem::path batteryPath= "/sys/class/power_supply/BAT0";
|
||||
const std::filesystem::path backlightPath= "/sys/class/backlight/intel_backlight";
|
||||
const std::filesystem::path defaultConservePath= "/sys/devices/pci0000:00/0000:00:1f.0/PNP0C09:00/VPC2004:00/conservation_mode";
|
||||
const std::filesystem::path defaultPowerPath= "/sys/class/power_supply/ADP0";
|
||||
const std::filesystem::path defaultBatteryPath= "/sys/class/power_supply/BAT0";
|
||||
const std::filesystem::path defaultBacklightPath= "/sys/class/backlight/intel_backlight";
|
||||
|
||||
const std::filesystem::path path= "/";
|
||||
|
||||
}
|
||||
|
||||
const std::map< int, Fl_Color > levelColors=
|
||||
{
|
||||
@ -45,6 +50,10 @@ const std::map< std::string, Fl_Round_Button ** > powerMode=
|
||||
{ "performance", &perfButton },
|
||||
};
|
||||
|
||||
|
||||
using Config::exports::ConfigFile;
|
||||
std::unique_ptr< ConfigFile > config;
|
||||
|
||||
std::string battLevelString= "UNKNOWN";
|
||||
|
||||
void
|
||||
@ -54,7 +63,7 @@ scheduledUpdate( void * )
|
||||
Fl::repeat_timeout( C::updateTimeout, scheduledUpdate, nullptr );
|
||||
|
||||
if( C::debugUpdateCalled ) std::cerr << "Update!" << std::endl;
|
||||
std::ifstream batt{ batteryPath / "capacity" };
|
||||
std::ifstream batt{ C::path / config->get( "battery_path" ) / "capacity" };
|
||||
batt >> battLevelString;
|
||||
std::istringstream iss{ battLevelString };
|
||||
int battlevel= 2;
|
||||
@ -63,7 +72,7 @@ scheduledUpdate( void * )
|
||||
level->selection_color( levelColors.upper_bound( battlevel )->second );
|
||||
if( C::debugUpdate ) std::cerr << "Battery state read as: " << battlevel << std::endl;
|
||||
|
||||
std::ifstream conserved{ conservePath };
|
||||
std::ifstream conserved{ C::path / config->get( "conserve_path" ) };
|
||||
int cons;
|
||||
conserved >> cons;
|
||||
if( cons == 1 )
|
||||
@ -76,20 +85,20 @@ scheduledUpdate( void * )
|
||||
}
|
||||
if( C::debugUpdate ) std::cerr << "Conservation state read as: " << cons << std::endl;
|
||||
|
||||
std::ifstream acCheck{ powerPath / "online" };
|
||||
std::ifstream acCheck{ C::path / config->get( "power_path" ) / "online" };
|
||||
int ac= -1;
|
||||
acCheck >> ac;
|
||||
acButton->value( ac );
|
||||
if( C::debugUpdate ) std::cerr << "AC state read as: " << ac << std::endl;
|
||||
|
||||
|
||||
std::ifstream i_max{ backlightPath / "max_brightness" };
|
||||
std::ifstream i_max{ C::path / config->get( "backlight_path" ) / "max_brightness" };
|
||||
|
||||
int max;
|
||||
i_max >> max;
|
||||
|
||||
|
||||
std::ifstream inf{ backlightPath / "brightness" };
|
||||
std::ifstream inf{ C::path / config->get( "backlight_path" ) / "brightness" };
|
||||
int in;
|
||||
inf >> in;
|
||||
|
||||
@ -113,6 +122,21 @@ profileUpdate( void * )
|
||||
int
|
||||
main( const int argcnt, char **argvec )
|
||||
{
|
||||
config= std::make_unique< ConfigFile >
|
||||
(
|
||||
ConfigFile
|
||||
{
|
||||
std::ifstream{ C::path / ::getenv( "HOME" ) / ".power_console.conf" },
|
||||
{
|
||||
{ "battery_path", C::defaultBatteryPath.string() },
|
||||
{ "power_path", C::defaultPowerPath.string() },
|
||||
{ "backlight_path", C::defaultBacklightPath.string() },
|
||||
{ "conserve_path", C::defaultConservePath.string() },
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
auto w= make_window();
|
||||
|
||||
level->minimum( 0 );
|
||||
@ -141,12 +165,12 @@ void
|
||||
consClicked( Fl_Light_Button *, void * )
|
||||
{
|
||||
std::cerr << "Conserve!" << std::endl;
|
||||
std::ifstream oldConserved{ conservePath };
|
||||
std::ifstream oldConserved{ C::path / config->get( "conserve_path" ) };
|
||||
int state;
|
||||
oldConserved >> state;
|
||||
const int newState= 1 - state;
|
||||
conserveButton->value( newState );
|
||||
std::ofstream conserved{ conservePath };
|
||||
std::ofstream conserved{ C::path / config->get( "conserve_path" ) };
|
||||
conserved << newState;
|
||||
std::cerr << "Tried to set new state to " << newState << std::endl;
|
||||
}
|
||||
@ -182,7 +206,7 @@ namespace
|
||||
void
|
||||
changeBrightness( int pct )
|
||||
{
|
||||
std::ifstream i_max{ backlightPath / "max_brightness" };
|
||||
std::ifstream i_max{ C::path / config->get( "backlight_path" ) / "max_brightness" };
|
||||
|
||||
int max;
|
||||
i_max >> max;
|
||||
@ -191,7 +215,7 @@ namespace
|
||||
int adj= std::clamp( max * pct / 100, 1, max );
|
||||
std::cerr << "Adjustment computed: " << adj << std::endl;
|
||||
|
||||
std::ofstream out{ backlightPath / "brightness" };
|
||||
std::ofstream out{ C::path / config->get( "backlight_path" ) / "brightness" };
|
||||
out << adj;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user