hbspbar

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

commit 2f2a90ba85c36a0935840654fd45d9a8a9798610
parent 0b3d4426cb585ceade7a27ed28cfe08f1710738d
Author: hhvn <dev@hhvn.uk>
Date:   Wed, 21 Feb 2024 18:21:18 +0000

Make bar part of main

Diffstat:
Abar.go | 201+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Dbar/bar.go | 241-------------------------------------------------------------------------------
Mmain.go | 54++++++++++++++++++++++++++++++++++++++----------------
3 files changed, 239 insertions(+), 257 deletions(-)

diff --git a/bar.go b/bar.go @@ -0,0 +1,201 @@ +package main + +import ( + "image" + "errors" + "image/color" + + "hhvn.uk/hbspbar/config" + "hhvn.uk/hbspbar/status" + "hhvn.uk/hbspbar/bspc" + "hhvn.uk/hbspbar/drw" +) + +var bars map[int]*bar = make(map[int]*bar) + +type bar struct { + id int + w *drw.Window + i *image.RGBA +} + +var ( + ErrNoState = errors.New("attempted to create a bar with uninitialized state") + ErrNoSuchBar = errors.New("attempted to destroy non-existant bar") +) + +func (b *bar) init(id int, state *bspc.State) error { + b.id = id + b.i = nil + + mon, err := b.getmon(state) + if err != nil { return err } + + go mon.TopPadding(config.H) + + geom := mon.Rectangle + + b.w, err = drw.WindowCreate(geom.X, geom.Y, geom.Width, config.H) + if err != nil { return err } + + rect := image.Rect(0, 0, int(mon.Rectangle.Width), int(config.H)) + b.i = image.NewRGBA(rect) + + return nil +} + +func (b *bar) destroy(state *bspc.State) { + mon, _ := b.getmon(state) + if (mon != nil) { + mon.TopPadding(0) + } +} + +func (b bar) getmon(state *bspc.State) (*bspc.Monitor, error) { + return state.GetMon(b.id) +} + +func (b *bar) drawText(x int, col color.Color, text string) (int, error) { + return drw.DrawText(b.i, x, col, text) +} + +func (b *bar) drawRect(x, y, w, h int, c color.Color, fill bool) { + drw.DrawRect(b.i, x, y, w, h, c, fill) +} + +func (b bar) draw(state *bspc.State, blocks []*status.Block) error { + var bg color.Color + var filled bool + + if blocks == nil { + return nil + } + + mon, err := b.getmon(state) + if err != nil { + return err + } + + // Dimensions of drawing space + cx := 0 + w := int(mon.Rectangle.Width) + h := int(config.H) + + + // Draw background + b.drawRect(0, 0, w, h, config.Bg, true) + + // Monitor + montext := mon.Name + monw := 6 + drw.TextWidth(montext) + 8 + + if mon.Focused { + bg = config.Sel + } else { + bg = config.UnselMon + } + + b.drawRect(cx, 0, monw, h, bg, true) + b.drawText(cx + 6, config.Fg, montext) + cx += monw + + // Desktops + boxw := 4 + ds := len(mon.Desktops) + dw := 4 + (4 + boxw + 2 + 3) * ds + 5 + + for i := 0; i < ds; i++ { + dw += drw.TextWidth(mon.Desktops[i].Name) + } + + cx += 4 + for i := 0; i < ds; i++ { + d := mon.Desktops[i] + + cx += 5 + + if d.Focused { + bg = config.Sel + } else { + bg = config.FgDark + } + + filled = d.Root != nil + + b.drawRect(cx, 3, boxw, h - 7, bg, filled) + cx += boxw + 2 + ax, _ := b.drawText(cx, config.Fg, d.Name) + cx += ax + 2 + } + + cx = int(mon.Rectangle.Width) + for _, s := range blocks { + if s != nil { + cx -= s.W + cx -= config.StatusPad + } + } + + b.drawRect(cx, 0, int(mon.Rectangle.Width) - cx, int(config.H), + config.Status, true) + + cx += config.StatusPad / 2 + + for _, s := range blocks { + if s != nil { + drw.AddImg(b.i, cx, s.W, s.I) + + cx += s.W + config.StatusPad + } + } + + b.w.Paint(b.i) + + return nil +} + +func Create(state *bspc.State, id int) (error) { + var b bar + + if state == nil { + return ErrNoState + } + + if _, ok := bars[id]; ok { + // Already created, whatever. + return nil + } + + if err := b.init(id, state); err != nil { + return err + } + + bars[b.id] = &b + return nil +} + +func Redraw(state *bspc.State, blocks []*status.Block) { + for _, b := range bars { + b.draw(state, blocks) + } +} + +func Destroy(state *bspc.State, id int) error { + b, ok := bars[id] + if !ok { + return ErrNoSuchBar + } + + b.destroy(state) + delete(bars, id) + + return nil +} + +func Cleanup(state *bspc.State) { + if state != nil { + for _, b := range bars { + b.destroy(finalstate) + } + } +} diff --git a/bar/bar.go b/bar/bar.go @@ -1,241 +0,0 @@ -package bar // import "hhvn.uk/hbspbar/bar" - -import ( - "fmt" - "image" - "errors" - "image/color" - - "hhvn.uk/hbspbar/config" - "hhvn.uk/hbspbar/common" - "hhvn.uk/hbspbar/status" - "hhvn.uk/hbspbar/bspc" - "hhvn.uk/hbspbar/drw" -) - - -var bars map[int]*bar - -type bar struct { - id int - w *drw.Window - i *image.RGBA -} - -func create(state *bspc.State, id int) (error) { - var b bar - - if err := b.init(id, state); err != nil { - return err - } - - if bars == nil { - bars = make(map[int]*bar) - } - - bars[b.id] = &b - return nil -} - -func (b *bar) init(id int, state *bspc.State) error { - b.id = id - b.i = nil - - mon, err := b.getmon(state) - if err != nil { return err } - - go mon.TopPadding(config.H) - - geom := mon.Rectangle - - b.w, err = drw.WindowCreate(geom.X, geom.Y, geom.Width, config.H) - if err != nil { return err } - - rect := image.Rect(0, 0, int(mon.Rectangle.Width), int(config.H)) - b.i = image.NewRGBA(rect) - - return nil -} - -func (b *bar) destroy(state *bspc.State) { - mon, _ := b.getmon(state) - if (mon != nil) { - mon.TopPadding(0) - } -} - -func (b bar) getmon(state *bspc.State) (*bspc.Monitor, error) { - return state.GetMon(b.id) -} - -func (b *bar) drawText(x int, col color.Color, text string) (int, error) { - return drw.DrawText(b.i, x, col, text) -} - -func (b *bar) drawRect(x, y, w, h int, c color.Color, fill bool) { - drw.DrawRect(b.i, x, y, w, h, c, fill) -} - -func (b bar) draw(state *bspc.State, blocks []*status.Block) { - var bg color.Color - var filled bool - - if blocks == nil { - return - } - - mon, err := b.getmon(state) - if err != nil { - return - } - - // Dimensions of drawing space - cx := 0 - w := int(mon.Rectangle.Width) - h := int(config.H) - - - // Draw background - b.drawRect(0, 0, w, h, config.Bg, true) - - // Monitor - montext := mon.Name - monw := 6 + drw.TextWidth(montext) + 8 - - if mon.Focused { - bg = config.Sel - } else { - bg = config.UnselMon - } - - b.drawRect(cx, 0, monw, h, bg, true) - b.drawText(cx + 6, config.Fg, montext) - cx += monw - - // Desktops - boxw := 4 - ds := len(mon.Desktops) - dw := 4 + (4 + boxw + 2 + 3) * ds + 5 - - for i := 0; i < ds; i++ { - dw += drw.TextWidth(mon.Desktops[i].Name) - } - - cx += 4 - for i := 0; i < ds; i++ { - d := mon.Desktops[i] - - cx += 5 - - if d.Focused { - bg = config.Sel - } else { - bg = config.FgDark - } - - filled = d.Root != nil - - b.drawRect(cx, 3, boxw, h - 7, bg, filled) - cx += boxw + 2 - ax, _ := b.drawText(cx, config.Fg, d.Name) - cx += ax + 2 - } - - cx = int(mon.Rectangle.Width) - for _, s := range blocks { - if s != nil { - cx -= s.W - cx -= config.StatusPad - } - } - - b.drawRect(cx, 0, int(mon.Rectangle.Width) - cx, int(config.H), - config.Status, true) - - cx += config.StatusPad / 2 - - for _, s := range blocks { - if s != nil { - drw.AddImg(b.i, cx, s.W, s.I) - - cx += s.W + config.StatusPad - } - } - - b.w.Paint(b.i) -} - -var Create = make(chan int) -var Destroy = make(chan int) -var NewState = make(chan *bspc.State) -var Err = make(chan error) - -// Store the state for when bar.Cleanup() is called -// This is a bit hacky but I don't really see any alternative -var finalstate *bspc.State = nil - -func init() { - var state *bspc.State = nil - var blocks []*status.Block = nil - - go func() { - defer func() { - close(Create) - close(Destroy) - close(NewState) - close(Err) - }() - - for { - select { - case e := <- drw.Events: - if e.Ev == nil && e.Err == nil { - Err <- fmt.Errorf("X connection clossed") - return - } - - if e.Err != nil { - common.Perror("X.WaitForEvents", e.Err) - continue - } - - // switch e.Ev.(type) { - // case xproto.DestroyNotifyEvent: - // Err <- fmt.Errorf("Window destroyed") - // return - // default: - // } - case id := <- Destroy: - bars[id].destroy(state) - delete(bars, id) - case id := <- Create: - if state == nil { - Err <- errors.New("attempted to create a bar with uninitialized state") - return - } - if _, ok := bars[id]; ok { break } - if err := create(state, id); err != nil { - Err <- fmt.Errorf("Couldn't create window: %s\n", err) - return - } - case blocks = <- status.NewBlocks: - for _, b := range bars { - b.draw(state, blocks) - } - case state = <- NewState: - finalstate = state - for _, b := range bars { - b.draw(state, blocks) - } - } - } - }() -} - -func Cleanup() { - if finalstate != nil { - for _, b := range bars { - b.destroy(finalstate) - } - } -} diff --git a/main.go b/main.go @@ -6,12 +6,16 @@ import ( "strings" "syscall" - "hhvn.uk/hbspbar/bar" "hhvn.uk/hbspbar/bspc" "hhvn.uk/hbspbar/common" "hhvn.uk/hbspbar/drw" + "hhvn.uk/hbspbar/status" ) +// Store the state for when bar.Cleanup() is called +// This is a bit hacky but I don't really see any alternative +var finalstate *bspc.State = nil + func main() { signals := make(chan os.Signal, 1) signal.Notify(signals, @@ -19,8 +23,6 @@ func main() { syscall.SIGHUP, syscall.SIGTERM) - defer bar.Cleanup() - if drw.InitErr != nil { common.Error("Couldn't initialize X: %s\n", drw.InitErr) return @@ -33,22 +35,37 @@ func main() { defer bspc.Cleanup() state := <- bspc.NewState - bar.NewState <- state for _, m := range state.Monitors { - bar.Create <- m.ID + Create(state, m.ID) } - for { + var blocks []*status.Block = nil + + mainloop: for { select { case s := <-signals: common.Error("%s\n", s) - return - case err := <-bar.Err: - common.Error("%s\n", err) - return + break mainloop + case e := <- drw.Events: + if e.Ev == nil && e.Err == nil { + common.Error("X connection clossed\n") + break mainloop + } + + if e.Err != nil { + common.Perror("X.WaitForEvents", e.Err) + continue + } + + // switch e.Ev.(type) { + // case xproto.DestroyNotifyEvent: + // Err <- fmt.Errorf("Window destroyed") + // return + // default: + // } case err := <-bspc.EvErr: common.Error("Couldn't read event: %s\n", err) - return + break mainloop case event := <-bspc.Event: if strings.HasPrefix(event.Name, "monitor_") { id, err := common.Intify(event.Tokens[0]) @@ -56,17 +73,22 @@ func main() { switch event.Name { case "monitor_add": case "monitor_geometry": - bar.Create <- id + Create(state, id) case "monitor_remove": - bar.Destroy <- id + Destroy(state, id) } } } case err := <-bspc.StErr: common.Error("Couldn't load bspwm state: %s\n", err) - return - case s := <-bspc.NewState: - bar.NewState <- s + break mainloop + case blocks = <- status.NewBlocks: + Redraw(state, blocks) + case state = <- bspc.NewState: + finalstate = state + Redraw(state, blocks) } } + + Cleanup(finalstate) }