lumen
Universal brightness controller
git clone https://git.pi66.xyz/lumen
About | Files | Commits | Refs | License
perf: speed up DDC brightness changes
Author: pi66
Date: 2026-10-09 15:52
commit: cdc9b753
- remove the 40ms delay from DDC reads - cache the monitor's maximum brightness to skip slow probes - add a tmpstore package for /tmp/lumen state files - add i2c Locking (Flock) to queue processes - add Cached() to skip slow DDC reads for relative brightness changes
Stat
| M | src/actions/actions.go | 10 | +9 | -1 |
| A | src/backend/backend.go | 5 | +5 | -0 |
| M | src/backend/ddc.go | 23 | +19 | -4 |
| M | src/backend/sysfs.go | 9 | +7 | -2 |
| A | src/utils/tmpstore/tmpstore.go | 27 | +27 | -0 |
Diff
diff --git a/src/actions/actions.go b/src/actions/actions.go
index 4e48474..08eb5dc 100644
--- a/src/actions/actions.go
+++ b/src/actions/actions.go
@@ -24,6 +24,14 @@ func Run(opt parser.Options) {
b := backend.Load(opt.Monitor)
+ if l, ok := b.(interface {
+ Lock()
+ Unlock()
+ }); ok {
+ l.Lock()
+ defer l.Unlock()
+ }
+
switch opt.Action {
case parser.Get:
fmt.Println(TryV(b.Get()))
@@ -31,6 +39,6 @@ func Run(opt parser.Options) {
...
Show all
Show less
diff --git a/src/actions/actions.go b/src/actions/actions.go
index 4e48474..08eb5dc 100644
--- a/src/actions/actions.go
+++ b/src/actions/actions.go
@@ -24,6 +24,14 @@ func Run(opt parser.Options) {
b := backend.Load(opt.Monitor)
+ if l, ok := b.(interface {
+ Lock()
+ Unlock()
+ }); ok {
+ l.Lock()
+ defer l.Unlock()
+ }
+
switch opt.Action {
case parser.Get:
fmt.Println(TryV(b.Get()))
@@ -31,6 +39,6 @@ func Run(opt parser.Options) {
...
diff --git a/src/actions/actions.go b/src/actions/actions.go
index 4e48474..08eb5dc 100644
--- a/src/actions/actions.go
+++ b/src/actions/actions.go
@@ -24,6 +24,14 @@ func Run(opt parser.Options) {
b := backend.Load(opt.Monitor)
+ if l, ok := b.(interface {
+ Lock()
+ Unlock()
+ }); ok {
+ l.Lock()
+ defer l.Unlock()
+ }
+
switch opt.Action {
case parser.Get:
fmt.Println(TryV(b.Get()))
@@ -31,6 +39,6 @@ func Run(opt parser.Options) {
case parser.Set:
p := backend.Resolve(b, opt.Value)
TryE(b.Set(p))
- log.Info(log.Title(b.Name()),"%d%%", p)
+ log.Info(log.Title(b.Name()), "%d%%", p)
}
}
diff --git a/src/backend/backend.go b/src/backend/backend.go
index 3bd3ec6..846830b 100644
--- a/src/backend/backend.go
+++ b/src/backend/backend.go
@@ -40,6 +40,11 @@ func Resolve(b Backend, in string) int {
v, e := strconv.Atoi(strings.TrimSuffix(in, "%"))
n := TryV(v, e, errors.New("invalid value: "+in))
if rel {
+ if c, ok := b.(interface{ Cached() (int, bool) }); ok {
+ if v, ok := c.Cached(); ok {
+ return min(max(n+v, 0), 100)
+ }
+ }
n += TryV(b.Get())
}
return min(max(n, 0), 100)
diff --git a/src/backend/ddc.go b/src/backend/ddc.go
index e978d34..59420c5 100644
--- a/src/backend/ddc.go
+++ b/src/backend/ddc.go
@@ -6,9 +6,9 @@ import (
"path/filepath"
"strings"
"syscall"
- "time"
. "lumen/src/utils/errs"
+ . "lumen/src/utils/tmpstore"
)
type ddc struct {
@@ -45,13 +45,18 @@ func openDDC(link, name string) (d *ddc, err error) {
TryE(e)
}
- x := &ddc{f: f, name: name}
+ x := &ddc{f: f, name: name, max: ReadTmp(name, "max")}
+ if x.max > 0 {
+ return x, nil
+ }
+
_, m, e := x.vcp()
if e != nil || m <= 0 {
f.Close()
TryE(errors.New("no DDC response"))
}
x.max = m
+ WriteTmp(name, "max", m)
return x, nil
}
@@ -61,7 +66,6 @@ func (d *ddc) vcp() (cur, max int, err error) {
b := []byte{0x51, 0x82, 0x01, 0x10, 0}
b[4] = 0x6E ^ b[0] ^ b[1] ^ b[2] ^ b[3]
TryV(d.f.Write(b))
- time.Sleep(40 * time.Millisecond)
r := make([]byte, 11)
TryV(d.f.Read(r))
@@ -73,14 +77,23 @@ func (d *ddc) vcp() (cur, max int, err error) {
return
}
+func (d *ddc) Lock() { syscall.Flock(int(d.f.Fd()), syscall.LOCK_EX) }
+func (d *ddc) Unlock() { syscall.Flock(int(d.f.Fd()), syscall.LOCK_UN) }
func (d *ddc) Name() string { return "ddc:" + d.name }
+func (d *ddc) Cached() (int, bool) {
+ c := ReadTmp(d.name, "cur")
+ return c, c > 0
+}
+
func (d *ddc) Get() (p int, err error) {
defer Catch(&err)
cur, max, e := d.vcp()
TryE(e)
- return (cur*100 + max/2) / max, nil
+ p = (cur*100 + max/2) / max
+ WriteTmp(d.name, "cur", p)
+ return p, nil
}
func (d *ddc) Set(p int) (err error) {
@@ -90,5 +103,7 @@ func (d *ddc) Set(p int) (err error) {
b := []byte{0x51, 0x84, 0x03, 0x10, byte(v >> 8), byte(v), 0}
b[6] = 0x6E ^ b[0] ^ b[1] ^ b[2] ^ b[3] ^ b[4] ^ b[5]
TryV(d.f.Write(b))
+
+ WriteTmp(d.name, "cur", p)
return nil
}
diff --git a/src/backend/sysfs.go b/src/backend/sysfs.go
index 8433f98..b6730e2 100644
--- a/src/backend/sysfs.go
+++ b/src/backend/sysfs.go
@@ -7,6 +7,7 @@ import (
"strconv"
. "lumen/src/utils/errs"
+ . "lumen/src/utils/tmpstore"
)
type sysfs struct {
@@ -28,13 +29,16 @@ func loadSysfs() (b Backend, err error) {
return &sysfs{dir: dirs[0], max: m}, nil
}
-func (s *sysfs) Name() string { return "sysfs:" + filepath.Base(s.dir) }
+func (s *sysfs) id() string { return filepath.Base(s.dir) }
+func (s *sysfs) Name() string { return "sysfs:" + s.id() }
func (s *sysfs) Get() (p int, err error) {
defer Catch(&err)
v := readInt(s.dir + "/brightness")
- return (v*100 + s.max/2) / s.max, nil
+ p = (v*100 + s.max/2) / s.max
+ WriteTmp(s.id(), "cur", p)
+ return p, nil
}
func (s *sysfs) Set(p int) (err error) {
@@ -42,5 +46,6 @@ func (s *sysfs) Set(p int) (err error) {
raw := strconv.Itoa(p * s.max / 100)
TryE(os.WriteFile(s.dir+"/brightness", []byte(raw), 0644))
+ WriteTmp(s.id(), "cur", p)
return nil
}
diff --git a/src/utils/tmpstore/tmpstore.go b/src/utils/tmpstore/tmpstore.go
new file mode 100644
index 0000000..7ca1cf0
--- /dev/null
+++ b/src/utils/tmpstore/tmpstore.go
@@ -0,0 +1,27 @@
+package tmpstore
+
+import (
+ "os"
+ "path/filepath"
+ "strconv"
+ "strings"
+)
+
+func tmpFile(name, ext string) string {
+ return filepath.Join("/tmp/lumen", strconv.Itoa(os.Getuid())+"-"+name+"."+ext)
+}
+
+func ReadTmp(name, ext string) int {
+ data, err := os.ReadFile(tmpFile(name, ext))
+ if err != nil {
+ return 0
+ }
+ n, _ := strconv.Atoi(strings.TrimSpace(string(data)))
+ return n
+}
+
+func WriteTmp(name, ext string, n int) {
+ os.MkdirAll("/tmp/lumen", 0777)
+ os.Chmod("/tmp/lumen", 0777)
+ os.WriteFile(tmpFile(name, ext), []byte(strconv.Itoa(n)), 0644)
+}