diff --git a/ConfigFile.h b/ConfigFile.h new file mode 100644 index 0000000..3b3a7b2 --- /dev/null +++ b/ConfigFile.h @@ -0,0 +1,100 @@ +#pragma once + +#include +#include +#include +#include +#include + +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; +} diff --git a/power_console_main.cc b/power_console_main.cc index ad5b995..25f0cfc 100644 --- a/power_console_main.cc +++ b/power_console_main.cc @@ -11,6 +11,8 @@ #include #include +#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; }