From 37ba32b0bbb368a392d2e10b11e45fbde3126e69 Mon Sep 17 00:00:00 2001 From: Chris Xiong Date: Wed, 11 Jul 2018 11:05:55 +0800 Subject: Manual mode added. Quit gracefully. Multiple code clean-ups in the Sensor class. --- ChangeLog | 10 ++++++ README.md | 84 ++++++++++++++++++++++++++++++---------------- brightness_ctrl.cpp | 33 ++++++++++++++++-- brightness_ctrl.hpp | 10 ++++-- main.cpp | 79 ++++++++++++++++++++++++++++++++++++------- sensor_als.cpp | 2 ++ sensor_als.hpp | 2 ++ sensors.cpp | 97 +++++++++++++++++++++++++++++++++++++++-------------- sensors.hpp | 15 +++++++-- utils.cpp | 2 ++ utils.hpp | 2 ++ 11 files changed, 261 insertions(+), 75 deletions(-) diff --git a/ChangeLog b/ChangeLog index 5baa091..f70ae81 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,13 @@ +2018-07-11 0.0.5 +Manual mode added. +Quit gracefully. +Multiple code clean-ups in the Sensor class. + +2018-07-06 0.0.4 +Random code clean-ups. +Sane logging. +Change thresholds in the default configuration. + 2018-05-05 0.0.3-r1 No longer using experimental/filesystem as I started using gcc 8.1. diff --git a/README.md b/README.md index 309e286..77a06e5 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,11 @@ # lightsd -`lightsd` is a small daemon to make your ~~(actually, my)~~ ambient -light sensor on your laptop useful in Linux interjection +`lightsd` is a small daemon to make the ambient light sensor on your +~~(actually, my)~~ laptop useful in Linux interjection without using a full desktop environment (or `systemd`). It even works in a tty! -This service watches the readings from the ambient light sensor and +This service watches the reading from the ambient light sensor and control the backlight of the screen and keyboard. It also creates a fifo so that you can adjust relative brightness of the lcd. @@ -18,8 +18,9 @@ The project also demostrates how damn stupid a C++ program could look like Hopefully it does not yet hog the CPU. # Warning -WIP. Does not yet do any kind of input sanitation. May segmentation fault -at any time. The author uses Gentoo. _Very_ shitty code. +WIP. May segmentation fault at any time. The author uses Gentoo. _Very_ +shitty code (I admit my code style is crap). The implementation is shitty +too (it works nevertheless). As this daemon manipulates sysfs, IT ONLY RUNS AS ROOT! @@ -32,33 +33,33 @@ Just `mkdir build && cd build && cmake .. && make`. # Installing `sudo make install`. If you are using `OpenRC`, an init script is also installed. -Sorry to `systemd` users but my only computer running `systemd` does not have -iio sensors. +Sorry to `systemd` users but my only computer with iio sensors is not powered by +that. # Documentation -None. The code documents itself. +None. The code documents itself (in a bad way). ## fifo usage - `u [x=5,0` -Sets relative brightness of lcd. +[S]ets relative brightness of lcd. - `r` -Resets relative brightness of lcd, equivalent to `s 0`. +[R]esets relative brightness of lcd, equivalent to `s 0`. - `f` -Forces an adjustment to be made. You may want to call this when the lid +[F]orces an adjustment to be made. You may want to call this when the lid is being opened in order to turn on the keyboard backlight. - -Not implemented: - `m` -Disables automatic brightness. (\_M\_anual) -Some of the commands (r/s/f) does nothing in manual brightness mode. +Disables automatic brightness. ([M]anual) +Some of the commands (`r`/`f`) does nothing in manual mode. +Command `s` sets absolute brightness in manual mode. - `a` -Enables automatic brightness. +Enables [a]utomatic brightness. - `i` -Print current status to stdout. Example output: +Print current status to stdout. ([I]nfo) +Output is in the following format: ``` Mode: ALS value: <%f|--> @@ -73,13 +74,40 @@ Surprise! # Tested on - Lenovo ThinkPad X1 Yoga 1st gen. +# Integration +Take `acpid` as an example. + +`/etc/acpi/events/brightness-down`: +``` +event=video/brightnessdown +action=echo d 5 > /tmp/lightsd.cmd.fifo +``` + +`/etc/acpi/events/brightness-up`: +``` +event=video/brightnessup +action=echo u 5 > /tmp/lightsd.cmd.fifo +``` + +If your laptop turns keyboard backlight automatically when closing the lid, +you may also want the following: + +`/etc/acpi/events/lid-open`: +``` +event=button/lid.*open +action=echo f > /tmp/lightsd.cmd.fifo +``` + # To-do - - Less segmentation faults - - Less data races - - Input sanitation - - Actual, _real_ logging: not printf'ing to `stdout`. - - More fifo commands: disabling auto adjustment, set absolute brightness etc. - - use iio triggers instead? - - auto orientation using accelerometer? - - hogging cpu and battery? - - ability to embed an email client and to order pizza? + - Less segmentation faults (already eliminated in my daily usage) + - Less data races (???) + - Input sanitation (partially) + - Actual, _real_ logging: not printf'ing to `stdout`. (probably done) + - More commands: disabling auto adjustment, set absolute brightness etc. (done) + - Change configuration format so that one can control something other than + screen backlight and keyboard backlight? (does anybody actually use it?) + - auto orientation using accelerometer? (otherwise my `Sensor` class is a waste) + - hogging cpu and battery? (oh no) + - triggers custom scripts? (detonate your computer once the reading reaches a + certain value?) + - ability to embed an email client and to order pizza? (not happening) diff --git a/brightness_ctrl.cpp b/brightness_ctrl.cpp index f8e0407..21c54e7 100644 --- a/brightness_ctrl.cpp +++ b/brightness_ctrl.cpp @@ -1,3 +1,5 @@ +//Chris Xiong 2018 +//3-Clause BSD License #include "brightness_ctrl.hpp" #include "utils.hpp" #include @@ -11,7 +13,7 @@ void BrightnessControl::_brightness_slide(int p) if(p<0)p=0; int pbr=maxbr*p/100; if(pbr%d/%d\n",br,pbr,maxbr); + LOG('I',"brightness adjust: %d->%d/%d",br,pbr,maxbr); int d=1;if(pbr0&&br+round(d*dd)<=pbr||d<0&&br+round(d*dd)>=pbr) { @@ -37,8 +39,8 @@ void BrightnessControl::set_path(filesystem::path p) maxbr=readint(maxbrpath.c_str()); br=readint(brpath.c_str()); } -void BrightnessControl::set_thresh(std::vector _th){thresh=_th;} -void BrightnessControl::set_value(std::vector _v){value=_v;} +void BrightnessControl::set_thresh(const std::vector &_th){thresh=_th;} +void BrightnessControl::set_value(const std::vector &_v){value=_v;} void BrightnessControl::set_delay(int _d){delay=_d;} void BrightnessControl::set_trigrange(int _tr){tr=_tr;} void BrightnessControl::set_minabr(int _mbr){minabr=_mbr;} @@ -50,6 +52,31 @@ void BrightnessControl::set_offset(int rel,int off) if(offset<-100)offset=-100; brightness_slide(value[cur]); } +void BrightnessControl::set_frozen(bool frozen) +{ + if(frozen) + { + doffset=offset; + offset=value[cur]+offset; + if(offset>100)offset=100; + if(offset<0)offset=0; + } + else + { + offset=doffset; + force_adjust(); + } +} +int BrightnessControl::get_offset(){return offset;} +int BrightnessControl::get_brightness() +{ + //FIXME??: On some devices there are EC-controlled key combinations + //that bypasses lightsd entirely (e.g. Fn+Space on ThinkPad for + //keyboard backlight). So we may have to turn to sysfs for this... + //Screw it, just read from sysfs. + if(brpath.empty())return 0; + return round(readint(brpath.c_str())*100./maxbr); +} void BrightnessControl::force_adjust() { diff --git a/brightness_ctrl.hpp b/brightness_ctrl.hpp index c1e8a34..fce960d 100644 --- a/brightness_ctrl.hpp +++ b/brightness_ctrl.hpp @@ -1,3 +1,5 @@ +//Chris Xiong 2018 +//3-Clause BSD License #ifndef BRIGHTNESS_CTRL_HPP #define BRIGHTNESS_CTRL_HPP #include @@ -13,6 +15,7 @@ private: filesystem::path cpath,brpath,maxbrpath; std::vector thresh,value; int delay,direction,br,maxbr,minabr,tr,offset; + int doffset; size_t cur; SensorALS *als; std::mutex interrupt_m,threshnotify_m,adjust_m; @@ -21,13 +24,16 @@ private: public: void init(float initv,SensorALS *s); void set_path(filesystem::path p); - void set_thresh(std::vector _th); - void set_value(std::vector _v); + void set_thresh(const std::vector &_th); + void set_value(const std::vector &_v); void set_delay(int _d); void set_trigrange(int _tr); void set_minabr(int _mbr); void set_offset(int rel,int off); + void set_frozen(bool frozen); + int get_offset(); + int get_brightness(); void force_adjust(); void on_sensor_report(float v); diff --git a/main.cpp b/main.cpp index d1485b3..524682e 100644 --- a/main.cpp +++ b/main.cpp @@ -1,3 +1,5 @@ +//Chris Xiong 2018 +//3-Clause BSD License #include #include #include @@ -17,6 +19,7 @@ #include "brightness_ctrl.hpp" SensorALS als; int als_id; +bool manualmode; filesystem::path fifo_path; BrightnessControl lcd,kbd; @@ -90,6 +93,23 @@ int get_gid(std::string group) delete[] buf; return -1; } +void cleanup() +{ + als.quit_worker(); + if(!fifo_path.empty())unlink(fifo_path.c_str()); +} +void sighandler(int) +{ + if(!fifo_path.empty())//command thread *exists* + { + FILE* tf=fopen(fifo_path.c_str(),"w"); + fputs("q\n",tf); + //let the command thread cleanup + fclose(tf); + } + else + cleanup(); +} void setup_fifo() { if(fifo_path.empty())return; @@ -98,7 +118,12 @@ void setup_fifo() ret|=mkfifo(fifo_path.c_str(),0220); ret|=chown(fifo_path.c_str(),0,get_gid("video")); ret|=chmod(fifo_path.c_str(),0220); - if(ret)LOG('W',"Failed to create fifo.",0); + if(ret) + { + LOG('W',"Failed to create fifo.",0); + unlink(fifo_path.c_str()); + fifo_path=""; + } } void command_thread() { @@ -124,37 +149,65 @@ void command_thread() else lcd.set_offset(-1,5); } if(cav[0]=="s")if(cav.size()>1)lcd.set_offset(0,atoi(cav[1].c_str())); - if(cav[0]=="r")lcd.set_offset(0,0); - if(cav[0]=="f") + if(cav[0]=="r"&&!manualmode)lcd.set_offset(0,0); + if(cav[0]=="f"&&!manualmode) { lcd.force_adjust(); kbd.force_adjust(); } + if(cav[0]=="m") + if(!manualmode) + { + als.pause_worker(); + lcd.set_frozen(true); + kbd.set_frozen(true); + manualmode=true; + } + if(cav[0]=="a") + if(manualmode) + { + manualmode=false; + als.resume_worker(); + lcd.set_frozen(false); + kbd.set_frozen(false); + } + if(cav[0]=="i") + { + printf("Mode: %s\n",manualmode?"Manual":"Automatic"); + printf(manualmode?"ALS value: --\n":"ALS value: %.2f\n",als.get_value()); + printf("Display brightness: %d%%",lcd.get_brightness()); + if(!manualmode&&lcd.get_offset())printf(" (+%d%%)\n",lcd.get_offset());else putchar('\n'); + //TODO: check for existance + printf("Keyboard backlight: %d%%\n",kbd.get_brightness()); + } + if(cav[0]=="q") + { + fclose(fifo_f); + cleanup(); + return; + } } fclose(fifo_f); fifo_f=fopen(fifo_path.c_str(),"r"); } } -void sigterm_handler(int) -{ - als.quit_worker(); - _exit(0); -} int main() { - signal(SIGTERM,sigterm_handler); + signal(SIGTERM,sighandler); + signal(SIGINT,sighandler); als_id=SensorBase::detect_sensor("als"); if(!~als_id)return puts("No ALS found!"),1; if(als.init(als_id,"in_intensity"))return puts("Failed to initialize sensor."),1; als.set_reader_callback(als_callback); float init_val=als.get_value(); + manualmode=false; load_config(); setup_fifo(); lcd.init(init_val,&als); kbd.init(init_val,&als); - std::thread lcd_thread(&BrightnessControl::worker,std::ref(lcd)); - std::thread kbd_thread(&BrightnessControl::worker,std::ref(kbd)); - std::thread cmd_thread(command_thread); + std::thread (&BrightnessControl::worker,std::ref(lcd)).detach(); + std::thread (&BrightnessControl::worker,std::ref(kbd)).detach(); + std::thread (command_thread).detach(); als.worker(); - return 0; + _exit(0); } diff --git a/sensor_als.cpp b/sensor_als.cpp index 399f2a1..88c3efc 100644 --- a/sensor_als.cpp +++ b/sensor_als.cpp @@ -1,3 +1,5 @@ +//Chris Xiong 2018 +//3-Clause BSD License #include "sensor_als.hpp" void SensorALS::enable_scan_elements() { diff --git a/sensor_als.hpp b/sensor_als.hpp index 0548545..a010f2d 100644 --- a/sensor_als.hpp +++ b/sensor_als.hpp @@ -1,3 +1,5 @@ +//Chris Xiong 2018 +//3-Clause BSD License #ifndef SENSOR_ALS_HPP #define SENSOR_ALS_HPP #include "sensors.hpp" diff --git a/sensors.cpp b/sensors.cpp index 615c784..4900957 100644 --- a/sensors.cpp +++ b/sensors.cpp @@ -1,7 +1,11 @@ +//Chris Xiong 2018 +//3-Clause BSD License #include #include #include +#include #include +#include #include #include #include "utils.hpp" @@ -25,47 +29,52 @@ void SensorBase::parse_type_string(std::string type,scan_t* ti) } void SensorBase::readbuffer() { + pollfd p[2]; + p[0]=pollfd{devfd,POLLIN,0}; + p[1]=pollfd{qpipe[0],POLLIN,0}; + if(poll(p,2,-1)<=0)return; char* buf=new char[readsize]; + if(p[1].revents&POLLIN) + { + ignore_result(read(qpipe[0],buf,readsize)); + delete[] buf; + return; + } ssize_t sz=read(devfd,buf,readsize); - if(sz==readsize) + if(sz==readsize&&!paused) { char *p=buf; for(int i=0;p-buf(enabled_scan_elem[i]); std::string es=std::get(enabled_scan_elem[i])+"_value"; - if(ti.is_le) + std::vector pp(p,p+ti.storagebits/8); + if(ti.is_le^PLATFORM_IS_LITTLEENDIAN) + std::reverse(pp.begin(),pp.end()); + auto readint=[&](auto t){ + memcpy(&t,pp.data(),ti.storagebits/8); + t>>=ti.shift;dict[es]=t; + }; switch(ti.storagebits) { case 8: - if(ti.is_signed) - {int8_t t;memcpy(&t,p,1);t>>=ti.shift;dict[es]=t;} - else - {uint8_t t;memcpy(&t,p,1);t>>=ti.shift;dict[es]=t;} - ++p; + if(ti.is_signed)readint(int8_t(0)); + else readint(uint8_t(0)); break; case 16: - if(ti.is_signed) - {int16_t t;memcpy(&t,p,2);t>>=ti.shift;dict[es]=t;} - else - {uint16_t t;memcpy(&t,p,2);t>>=ti.shift;dict[es]=t;} - p+=2; + if(ti.is_signed)readint(int16_t(0)); + else readint(uint16_t(0)); break; case 32: - if(ti.is_signed) - {int32_t t;memcpy(&t,p,4);t>>=ti.shift;dict[es]=t;} - else - {uint32_t t;memcpy(&t,p,4);t>>=ti.shift;dict[es]=t;} - p+=4; + if(ti.is_signed)readint(int32_t(0)); + else readint(uint32_t(0)); break; case 64: - if(ti.is_signed) - {int64_t t;memcpy(&t,p,8);t>>=ti.shift;dict[es]=t;} - else - {uint64_t t;memcpy(&t,p,8);t>>=ti.shift;dict[es]=t;} - p+=8; + if(ti.is_signed)readint(int64_t(0)); + else readint(uint64_t(0)); break; } + p+=ti.storagebits/8; } } delete[] buf; @@ -95,7 +104,7 @@ void SensorBase::enable_scan_element(std::string elem) path raw_val_path=sysfspath/(elem_base+"_raw");//initial value dict[elem_base+"_value"]=readint(raw_val_path.c_str()); - + enabled_scan_elem.insert( std::upper_bound(enabled_scan_elem.begin(),enabled_scan_elem.end(), std::make_tuple(idx,elem_base,st), @@ -117,7 +126,7 @@ bool SensorBase::init(int id,std::string _sensor_basename) path offset_path=sysfspath/(sensor_basename+"_offset"); dict[sensor_basename+"_offset"]=readfloat(offset_path.c_str()); - readsize=0; + ignore_result(pipe(qpipe)); enabled_scan_elem.clear(); enable_scan_elements(); update_values(); @@ -148,12 +157,48 @@ void SensorBase::worker() { for(workerquit=0;!workerquit;) { - readbuffer();update_values(); + read_m.lock(); + readbuffer(); + read_m.unlock(); + update_values(); if(readercb!=nullptr)readercb(this); } + deinit(); } void SensorBase::quit_worker() -{workerquit=1;} +{ + workerquit=1; + ignore_result(write(qpipe[1],"q",1)); + close(qpipe[1]); +} +void SensorBase::pause_worker() +{ + if(~devfd) + { + paused=1; + while(!read_m.try_lock())//just spin lock, I don't care + { + ignore_result(write(qpipe[1],"p",1)); + std::this_thread::yield(); + } + close(devfd); + devfd=-1; + } +} +void SensorBase::resume_worker() +{ + for(auto&i:enabled_scan_elem)//update readings + { + std::string& elem_base=std::get<1>(i); + filesystem::path raw_val_path=sysfspath/(elem_base+"_raw"); + dict[elem_base+"_value"]=readint(raw_val_path.c_str()); + } + devfd=open(devbufpath.c_str(),O_RDONLY); + if(!~devfd) + return (void)LOG('E',"failed to open the iio buffer device: %s",devbufpath.c_str()); + paused=0; + read_m.unlock(); +} void SensorBase::set_reader_callback(std::function cb) {readercb=cb;} std::string SensorBase::get_type(int id) diff --git a/sensors.hpp b/sensors.hpp index 7b549ba..91874bd 100644 --- a/sensors.hpp +++ b/sensors.hpp @@ -1,3 +1,5 @@ +//Chris Xiong 2018 +//3-Clause BSD License #ifndef SENSORS_HPP #define SENSORS_HPP #include @@ -5,12 +7,14 @@ #include #include #include +#include #include #include #include #define IIODEV_SYSFS_PATH_BASE "/sys/bus/iio/devices/iio:device" #define DEV_PATH "/dev/iio:device" +#define PLATFORM_IS_LITTLEENDIAN (__BYTE_ORDER__==__ORDER_LITTLE_ENDIAN__) namespace filesystem=std::filesystem; @@ -25,9 +29,12 @@ struct scan_t class SensorBase { private: - int devfd; - int workerquit; - int readsize; + int devfd=-1; + int workerquit=0; + int readsize=0; + int paused=0; + int qpipe[2]; + std::mutex read_m; std::function readercb; std::vector> enabled_scan_elem; @@ -51,6 +58,8 @@ class SensorBase void reset(); void worker(); void quit_worker(); + void pause_worker(); + void resume_worker(); void set_reader_callback(std::function cb); static std::string get_type(int id); static int detect_sensor(std::string type); diff --git a/utils.cpp b/utils.cpp index 135dd0a..1d26fc3 100644 --- a/utils.cpp +++ b/utils.cpp @@ -1,3 +1,5 @@ +//Chris Xiong 2018 +//3-Clause BSD License #include #include #include diff --git a/utils.hpp b/utils.hpp index 10a3d5f..a0ca68d 100644 --- a/utils.hpp +++ b/utils.hpp @@ -1,3 +1,5 @@ +//Chris Xiong 2018 +//3-Clause BSD License #ifndef UTILS_HPP #define UTILS_HPP #include -- cgit v1.2.3