brightness/main.go

220 lines
4.6 KiB
Go
Raw Permalink Normal View History

2020-10-10 20:32:39 +00:00
package main
import (
"bytes"
"fmt"
"io/ioutil"
"log"
"os"
"path/filepath"
"regexp"
"strconv"
)
// TODO specify minimum brightness
// TODO override max brightness
// TODO TUI mode
// TODO widget mode
var (
acpiBacklight = regexp.MustCompile(`acpi_video[0-9]+$`)
)
const backlightsDirectory = "/sys/class/backlight"
const (
2020-10-12 00:29:27 +00:00
commandSet = 1
commandSub = 2
commandAdd = 3
commandInteractive = 4
2020-10-10 20:32:39 +00:00
)
func init() {
log.SetFlags(0)
log.SetPrefix("")
}
type backlight struct {
identifier string
current int
max int
fallback bool
}
func (b *backlight) setBrightness(brightness int) error {
f, err := os.OpenFile(b.currentBrightnessPath(), os.O_WRONLY, 0644)
if err != nil {
return err
}
defer f.Close()
_, err = f.WriteString(fmt.Sprintf("%d\n", brightness))
return err
}
func (b *backlight) currentBrightnessPath() string {
return filepath.Join(backlightsDirectory, b.identifier, "brightness")
}
func (b *backlight) maxBrightnessPath() string {
return filepath.Join(backlightsDirectory, b.identifier, "max_brightness")
}
func identifyBacklights() ([]*backlight, error) {
var backlights []*backlight
// Locate backlight interfaces. Issues are commonly reported when the ACPI
// backlight interface is used instead of other interfaces present on the
// system. As a result, ACPI interfaces are only used when no other
// interfaces are available.
err := filepath.Walk(backlightsDirectory, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
} else if path == backlightsDirectory {
return nil
}
backlight := &backlight{
identifier: filepath.Base(path),
fallback: acpiBacklight.MatchString(path),
}
backlights = append(backlights, backlight)
return nil
})
if err != nil {
return nil, fmt.Errorf("failed to list backlight interfaces: %s", err)
}
// Fetch current and max brightness
for _, backlight := range backlights {
v, err := ioutil.ReadFile(backlight.currentBrightnessPath())
if err != nil {
return nil, fmt.Errorf("failed to read current brightness of %s: %s", backlight.identifier, err)
}
backlight.current, err = strconv.Atoi(string(bytes.TrimSpace(v)))
if err != nil {
return nil, fmt.Errorf("failed to parse current brightness of %s: %s", backlight.identifier, err)
}
v, err = ioutil.ReadFile(backlight.maxBrightnessPath())
if err != nil {
return nil, fmt.Errorf("failed to read maximum brightness of %s: %s", backlight.identifier, err)
}
backlight.max, err = strconv.Atoi(string(bytes.TrimSpace(v)))
if err != nil {
return nil, fmt.Errorf("failed to parse maximum brightness of %s: %s", backlight.identifier, err)
}
}
return backlights, nil
}
func main() {
backlights, err := identifyBacklights()
if err != nil {
log.Fatalf("failed to identify backlight interfaces: %s", err)
}
mainIndex := -1
if len(backlights) == 1 {
mainIndex = 0
} else {
for i, backlight := range backlights {
if backlight.fallback {
continue
}
if mainIndex != -1 {
mainIndex = -1
break
}
mainIndex = i
}
}
if mainIndex == -1 {
// TODO
log.Fatal("multiple backlight interfaces were found, please specify which interface to use with --interface")
}
backlight := backlights[mainIndex]
var v string
var command string
if len(os.Args) > 1 {
command = os.Args[1]
if len(os.Args) > 2 {
v = os.Args[2]
}
}
var cmd int
switch command {
2020-10-12 00:29:27 +00:00
case "i", "-i":
err := runTUI(backlight)
if err != nil {
log.Fatal(err)
}
return
2020-10-10 20:32:39 +00:00
case "set":
cmd = commandSet
case "add", "inc", "up":
cmd = commandAdd
case "sub", "dec", "down":
cmd = commandSub
case "current":
log.Printf("%d", backlight.current)
return
case "max":
log.Printf("%d", backlight.max)
return
case "":
log.Printf("%d/%d", backlight.current, backlight.max)
return
default:
if len(os.Args) > 2 {
log.Fatalf("unrecognized command %s", command)
}
v = command
if v[0] == '-' {
cmd = commandSub
v = v[1:]
} else if v[0] == '+' {
cmd = commandAdd
v = v[1:]
} else {
cmd = commandSet
}
}
pct := v[len(v)-1] == '%'
if pct {
v = v[:len(v)-1]
}
setValue, err := strconv.ParseFloat(v, 64)
if err != nil {
log.Fatalf("unrecognized command %s", command)
}
if pct {
setValue = float64(backlight.max) * (setValue / 100)
}
value := backlight.current
switch cmd {
case commandSet:
value = int(setValue)
case commandSub:
value -= int(setValue)
case commandAdd:
value += int(setValue)
}
err = backlight.setBrightness(value)
if err != nil {
log.Fatalf("failed to set brightness of %s to %d: %s", backlight.identifier, value, err)
}
}