You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
220 lines
4.6 KiB
Go
220 lines
4.6 KiB
Go
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 (
|
|
commandSet = 1
|
|
commandSub = 2
|
|
commandAdd = 3
|
|
commandInteractive = 4
|
|
)
|
|
|
|
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 {
|
|
case "i", "-i":
|
|
err := runTUI(backlight)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
return
|
|
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)
|
|
}
|
|
}
|