hbspbar

[WIP] bspwm status bar
git clone https://hhvn.uk/hbspbar
git clone git://hhvn.uk/hbspbar
Log | Files | Refs

commit 19781aeb974bd20c421a6b4677ffab348df8faf9
parent 505685c6d546cab46f919f6b5296ca73b80d09b2
Author: hhvn <dev@hhvn.uk>
Date:   Sat, 18 Nov 2023 19:50:26 +0000

Statuses

Diffstat:
Mbar/bar.go | 36++++++++++++++++++++++++++++++++++--
Mconfig/config.go | 3++-
Mdrw/drw.go | 10++++++++++
Mdrw/x.go | 7-------
Astatus/00-status.go | 97+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Astatus/01-bat.go | 109+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
6 files changed, 252 insertions(+), 10 deletions(-)

diff --git a/bar/bar.go b/bar/bar.go @@ -8,6 +8,7 @@ import ( "hhvn.uk/hbspbar/config" "hhvn.uk/hbspbar/common" + "hhvn.uk/hbspbar/status" "hhvn.uk/hbspbar/bspc" "hhvn.uk/hbspbar/drw" ) @@ -71,7 +72,7 @@ func (b *bar) draw() { if b.Mon.Focused { bg = config.Sel } else { - bg = config.Bg2 + bg = config.UnselMon } b.drawRect(cx, 0, monw, h, bg, true) @@ -106,7 +107,34 @@ func (b *bar) draw() { ax, _ := b.drawText(cx, config.Fg, d.Name) cx += ax + 2 } - cx += 5 + + status.Mutex.Lock() + cx = int(b.Mon.Rectangle.Width) - 6 + for i := len(status.Status) - 1; i >= 0; i -= 1 { + s, ok := status.Status[i] + if ok { + cx -= s.W + cx -= config.StatusPad + } + } + cx += config.StatusPad + cx -= 6 + + b.drawRect(cx, 0, int(b.Mon.Rectangle.Width) - cx, int(config.H), + config.Status, true) + + cx = int(b.Mon.Rectangle.Width) - 6 + for i := len(status.Status) - 1; i >= 0; i -= 1 { + s, ok := status.Status[i] + if ok { + cx -= s.W + + drw.AddImg(b.i, cx, s.W, s.I) + + cx -= config.StatusPad + } + } + status.Mutex.Unlock() b.w.Paint(b.i) @@ -178,6 +206,10 @@ func init() { Handle.Err <- fmt.Errorf("Couldn't create window: %s\n", err) return } + case <- status.Updated: + for _, b := range bars { + go b.draw() + } case state = <- Handle.NewState: for _, b := range bars { b.Mon = state.GetMon(b.Mon.ID) diff --git a/config/config.go b/config/config.go @@ -5,7 +5,8 @@ var H uint = 17 // Colours var Bg uint32 = 0xff050a10 -var Bg2 uint32 = 0xff0c1726 +var UnselMon uint32 = 0xff0c1726 +var Status uint32 = 0xff0a2126 var Fg uint32 = 0xffeeeeee var FgDark uint32 = 0xff444444 var Sel uint32 = 0xff1b364b diff --git a/drw/drw.go b/drw/drw.go @@ -2,6 +2,7 @@ package drw // import "hhvn.uk/hbspbar/drw" import ( "image" + "image/draw" "hhvn.uk/hbspbar/config" @@ -10,6 +11,10 @@ import ( ) func DrawText(i *image.RGBA, x int, col uint32, text string) (int, error) { + ft := freetype.NewContext() + ft.SetDPI(72) + ft.SetFont(font) + ft.SetFontSize(config.FontSize) ft.SetClip(i.Bounds()) ft.SetDst(i) @@ -42,3 +47,8 @@ func DrawRect(i *image.RGBA, x, y, w, h int, c uint32, fill bool) { } } } + +func AddImg(dst *image.RGBA, x, w int, src *image.RGBA) { + r := image.Rect(x, 0, x + w, int(config.H)) + draw.Draw(dst, r, src, image.Pt(0,0), draw.Src) +} diff --git a/drw/x.go b/drw/x.go @@ -14,7 +14,6 @@ import ( "github.com/jezek/xgbutil/icccm" "github.com/jezek/xgbutil/ewmh" "github.com/jezek/xgbutil/xgraphics" - "github.com/BurntSushi/freetype-go/freetype" "github.com/BurntSushi/freetype-go/freetype/truetype" ) @@ -30,7 +29,6 @@ var xc *xgb.Conn var xu *xgbutil.XUtil var screen *xproto.ScreenInfo var font *truetype.Font -var ft *freetype.Context func init() { var err error @@ -62,11 +60,6 @@ func init() { return } - ft = freetype.NewContext() - ft.SetDPI(72) - ft.SetFont(font) - ft.SetFontSize(config.FontSize) - Events := make(chan *eventwrap) go func() { for { diff --git a/status/00-status.go b/status/00-status.go @@ -0,0 +1,97 @@ +package status // import "hhvn.uk/hbspbar/status" + +import ( + "time" + "sync" + "image" + + "hhvn.uk/hbspbar/config" + "hhvn.uk/hbspbar/drw" +) + +var Status map[int]*status +var Mutex sync.Mutex +var statusid map[string]int +var Updated chan bool +var updates chan *status + +const ( + Red = 0xffaa2222 + Orange = 0xffaa7700 + Green = 0xff00aa00 + Black = 0xff000000 + Grey = 0xff888888 + Yellow = 0xffaaaa00 + White = 0xffcccccc +) + +func init() { + Status = make(map[int]*status) + statusid = make(map[string]int) + Updated = make(chan bool) + updates = make(chan *status) + + go func() { + for { + s := <- updates + + id := statusid[s.Name] + + Mutex.Lock() + Status[id] = s + Mutex.Unlock() + + Updated <- true + } + }() +} + +func register(name string) { + nid := 0 + for _, id := range statusid { + if id >= nid { + nid = id + 1 + } + } + statusid[name] = nid +} + +type status struct { + Name string + I *image.RGBA + W int +} + +func newUpdate(name string) (*status) { + var s status + + s.Name = name + s.W = 0 + s.I = image.NewRGBA(image.Rect(0, 0, 1000, int(config.H))) + + // Use drw's in order not to modify s.W + drw.DrawRect(s.I, 0, 0, 1000, int(config.H), config.Status, true) + + return &s +} + +func sleep(s int) { + time.Sleep(time.Duration(s) * time.Second) +} + +func (s *status) furthest(x int) { + if x > s.W { + s.W = x + } +} + +func (s *status) drawText(x int, col uint32, text string) (int, error) { + w, err := drw.DrawText(s.I, x, col, text) + s.furthest(x + w) + return w, err +} + +func (s *status) drawRect(x, y, w, h int, c uint32, fill bool) { + drw.DrawRect(s.I, x, y, w, h, c, fill) + s.furthest(x + w) +} diff --git a/status/01-bat.go b/status/01-bat.go @@ -0,0 +1,109 @@ +package status // import "hhvn.uk/hbspbar/status" + +import ( + "os" + "io" + "path" + "strings" + + // "hhvn.uk/hbspbar/drw" + "hhvn.uk/hbspbar/config" + "hhvn.uk/hbspbar/common" +) + +func read(file string) (string, error) { + f, err := os.Open(file) + if err != nil { return "", err } + + content, err := io.ReadAll(f) + if err != nil { return "", err } + + str := strings.TrimSuffix(string(content), "\n") + + return str, nil +} + +func init() { + name := "bat" + register(name) + + dir := "/sys/class/power_supply" + + const ( + nothing = iota + charging + discharging + ) + + go func(){ + for { + u := newUpdate(name) + + dirs, _ := os.ReadDir(dir) + if dirs == nil { return } + + totalcap := 0 + usedcap := 0 + + stati := nothing + + for _, d := range dirs { + if !strings.HasPrefix(d.Name(), "BAT") { continue } + + caps, err := read(path.Join(dir, d.Name(), "capacity")) + if err != nil { return } + + capi, err := common.Intify(caps) + if err != nil { return } + + totalcap += 100 + usedcap += capi + + stats, err := read(path.Join(dir, d.Name(), "status")) + if err != nil { return } + + // Doesn't deal with 1 battery charging, and another discharging but w/e + switch stats { + case "Charging": if charging > stati { stati = charging } + case "Discharging": if discharging > stati { stati = discharging} + } + } + + if totalcap == 0 { return } + + avgcap := (usedcap * 100) / totalcap + + var c uint32 + + // Colour of outline = charge indicator + switch stati { + case nothing: c = config.Fg + case charging: c = Green + case discharging: c = Red + } + + var iw int = 20 + var ih int = int(config.H) - 8 + var w int = iw * avgcap / 100 + + // Draw nose + u.drawRect(2, int((config.H - 5) / 2), 2, 5, c, true) + + // Draw border + u.drawRect(4, 3, iw + 2, ih + 2, c, false) + + switch { + case avgcap < 25: c = Red + case avgcap >= 99: c = Green + default: c = config.FgDark + } + + // Fill + u.drawRect(4 + iw - w, 4, w + 1, ih, c, true) + + updates <- u + sleep(1) + } + + }() +}