aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Chris Xiong <chirs241097@gmail.com> 2018-07-11 11:05:55 +0800
committerGravatar Chris Xiong <chirs241097@gmail.com> 2018-07-11 11:05:55 +0800
commit37ba32b0bbb368a392d2e10b11e45fbde3126e69 (patch)
tree9df0348ccb4e87b2d7c95821a5cd7d3a0f8c1c89
parentb5932d1d22d35cef95eb1cffbf489619b264442d (diff)
downloadlightsd-37ba32b0bbb368a392d2e10b11e45fbde3126e69.tar.xz
Manual mode added.
Quit gracefully. Multiple code clean-ups in the Sensor class.
-rw-r--r--ChangeLog10
-rw-r--r--README.md84
-rw-r--r--brightness_ctrl.cpp33
-rw-r--r--brightness_ctrl.hpp10
-rw-r--r--main.cpp79
-rw-r--r--sensor_als.cpp2
-rw-r--r--sensor_als.hpp2
-rw-r--r--sensors.cpp97
-rw-r--r--sensors.hpp15
-rw-r--r--utils.cpp2
-rw-r--r--utils.hpp2
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 <sup>interjection</sup>
+`lightsd` is a small daemon to make the ambient light sensor on your
+~~(actually, my)~~ laptop useful in Linux <sup>interjection</sup>
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<x<=100]`
-Makes lcd x% brighter.
+Makes lcd x% brighter. ([U]p)
- `d [x=5,0<x<=100]`
-Makes lcd x% darker.
+Makes lcd x% darker. ([D]own)
- `s <x,-100<=x<=100>`
-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: <Automatic|Manual>
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 <cmath>
@@ -11,7 +13,7 @@ void BrightnessControl::_brightness_slide(int p)
if(p<0)p=0;
int pbr=maxbr*p/100;
if(pbr<minabr)pbr=minabr;
- LOG('I',"brightness adjust: %d->%d/%d\n",br,pbr,maxbr);
+ LOG('I',"brightness adjust: %d->%d/%d",br,pbr,maxbr);
int d=1;if(pbr<br)d=-1;double dd=1;
while(d>0&&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<int> _th){thresh=_th;}
-void BrightnessControl::set_value(std::vector<int> _v){value=_v;}
+void BrightnessControl::set_thresh(const std::vector<int> &_th){thresh=_th;}
+void BrightnessControl::set_value(const std::vector<int> &_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 <chrono>
@@ -13,6 +15,7 @@ private:
filesystem::path cpath,brpath,maxbrpath;
std::vector<int> 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<int> _th);
- void set_value(std::vector<int> _v);
+ void set_thresh(const std::vector<int> &_th);
+ void set_value(const std::vector<int> &_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 <cstdio>
#include <cstdlib>
#include <cmath>
@@ -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 <cstdio>
#include <cstring>
#include <variant>
+#include <thread>
#include <sys/types.h>
+#include <poll.h>
#include <fcntl.h>
#include <unistd.h>
#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<readsize;++i)
{
scan_t ti=std::get<scan_t>(enabled_scan_elem[i]);
std::string es=std::get<std::string>(enabled_scan_elem[i])+"_value";
- if(ti.is_le)
+ std::vector<char> 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<void(SensorBase*)> 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 <cstdint>
@@ -5,12 +7,14 @@
#include <any>
#include <filesystem>
#include <functional>
+#include <mutex>
#include <unordered_map>
#include <tuple>
#include <vector>
#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<void(SensorBase*)> readercb;
std::vector<std::tuple<int,std::string,scan_t>> 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<void(SensorBase*)> 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 <cstdio>
#include <cstring>
#include <cerrno>
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 <string>