Pi66

Home | Repos | About | Blog | Gaza

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

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(&#91;&#93;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"

. &#34;lumen/src/utils/errs&#34;

+ . "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 + &#34;/brightness&#34;)

- 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 &#42; s.max / 100)
TryE(os.WriteFile(s.dir+&#34;/brightness&#34;, &#91;&#93;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) +}

// Created by Pi66

// Copyright ©Pi66 2026