Files
power-console/power_console_main.cc

242 lines
5.4 KiB
C++

#include "power_console.h"
#include <unistd.h>
#include <iostream>
#include <map>
#include <fstream>
#include <thread>
#include <algorithm>
#include <sstream>
#include <string>
#include <filesystem>
#include "ConfigFile.h"
namespace C
{
const bool debug= false;
const bool debugUpdate= false or C::debugUpdate;
// NOTE: This one can be REALLY floody, if the updates are happening on schedule
const bool debugUpdateCalled= false or C::debug;
const double updateTimeout= 0.1;
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=
{
{ 15, FL_DARK_RED },
{ 33, FL_RED },
{ 45, FL_DARK_YELLOW },
{ 66, FL_YELLOW },
{ 75, FL_DARK_GREEN },
{ 90, FL_GREEN },
{ 100, FL_BLUE },
};
const std::map< std::string, Fl_Round_Button ** > powerMode=
{
{ "power-saver", &saver },
{ "balanced", &balance },
{ "performance", &perfButton },
};
using Config::exports::ConfigFile;
std::unique_ptr< ConfigFile > config;
std::string battLevelString= "UNKNOWN";
void
scheduledUpdate( void * )
{
// Ensure we get called again, on schedule:
Fl::repeat_timeout( C::updateTimeout, scheduledUpdate, nullptr );
if( C::debugUpdateCalled ) std::cerr << "Update!" << std::endl;
std::ifstream batt{ C::path / config->get( "battery_path" ) / "capacity" };
batt >> battLevelString;
std::istringstream iss{ battLevelString };
int battlevel= 2;
iss >> battlevel;
level->value( battlevel );
level->selection_color( levelColors.upper_bound( battlevel )->second );
if( C::debugUpdate ) std::cerr << "Battery state read as: " << battlevel << std::endl;
std::ifstream conserved{ C::path / config->get( "conserve_path" ) };
int cons;
conserved >> cons;
if( cons == 1 )
{
conserveButton->value( 1 );
}
else
{
conserveButton->value( 0 );
}
if( C::debugUpdate ) std::cerr << "Conservation state read as: " << cons << std::endl;
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{ C::path / config->get( "backlight_path" ) / "max_brightness" };
int max;
i_max >> max;
std::ifstream inf{ C::path / config->get( "backlight_path" ) / "brightness" };
int in;
inf >> in;
brightness->value( 100 * in / max );
}
// Since this requires launching bg processes, we only do it at startup and then every 30s.
void
profileUpdate( void * )
{
FILE *prof= popen( "powerprofilesctl get", "r" );
char buffer[ 1024 ]= "";
fscanf( prof, "%s", buffer );
pclose( prof );
( *powerMode.at( buffer ) )->setonly();
Fl::repeat_timeout( 30, profileUpdate, nullptr );
}
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 );
level->maximum( 100 );
level->value( 0 );
level->label( battLevelString.c_str() );
brightness->maximum( 0 );
brightness->minimum( 100 );
brightness->step( -1 );
w->hotspot( w );
w->show( argcnt, argvec );
conserveButton->callback((Fl_Callback*)consClicked);
Fl::repeat_timeout( C::updateTimeout, scheduledUpdate, nullptr );
Fl::repeat_timeout( 0, profileUpdate, nullptr );
Fl::run();
}
void
consClicked( Fl_Light_Button *, void * )
{
std::cerr << "Conserve!" << std::endl;
std::ifstream oldConserved{ C::path / config->get( "conserve_path" ) };
int state;
oldConserved >> state;
const int newState= 1 - state;
conserveButton->value( newState );
std::ofstream conserved{ C::path / config->get( "conserve_path" ) };
conserved << newState;
std::cerr << "Tried to set new state to " << newState << std::endl;
}
void
setPowerMode( const std::string &mode )
{
( *powerMode.at( mode ) )->setonly();
std::thread bg{ [mode]{ system( ( "powerprofilesctl set " + mode ).c_str() ); } };
bg.detach();
}
void
saverChosen()
{
setPowerMode( "power-saver" );
}
void
balChosen()
{
setPowerMode( "balanced" );
}
void
perfChosen()
{
setPowerMode( "performance" );
}
namespace
{
void
changeBrightness( int pct )
{
std::ifstream i_max{ C::path / config->get( "backlight_path" ) / "max_brightness" };
int max;
i_max >> max;
// Clamp to never hit 0 backlight or pass max.
int adj= std::clamp( max * pct / 100, 1, max );
std::cerr << "Adjustment computed: " << adj << std::endl;
std::ofstream out{ C::path / config->get( "backlight_path" ) / "brightness" };
out << adj;
}
void
increaseBrightness( int pct )
{
changeBrightness( pct );
}
void
decreaseBrightness( int pct )
{
changeBrightness( -pct );
}
}
void
adjustBrightness( MouseWheelSlider *, void * )
{
const int amount= brightness->value();
std::cerr << "Adjusting brightness by " << amount << std::endl;
changeBrightness( amount );
}