hbspbar

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

commit 0543020038edb5de1162863e7401ea36f8a8584c
parent 2e3b351c355bd241de4aa4f2bdafd633ccef6eac
Author: hhvn <dev@hhvn.uk>
Date:   Tue,  8 Aug 2023 10:46:53 +0100

Channel-based bspwm communication

Diffstat:
Mbspc/bspc.go | 66+++++++++++++++++++++++++++++++++++++++---------------------------
Mcommon/common.go | 13++++++++++++-
Mmain.go | 72+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------
3 files changed, 110 insertions(+), 41 deletions(-)

diff --git a/bspc/bspc.go b/bspc/bspc.go @@ -63,11 +63,19 @@ func LoadState() (*State, error) { } type Subscriber struct { + Event chan *Event + cleanup chan bool cmd *exec.Cmd pipe io.ReadCloser scanner *bufio.Scanner } +type Event struct { + Err error + Name string + Tokens []string +} + func Subscribe() (*Subscriber, error) { cmd := exec.Command("bspc", "subscribe", "all") @@ -80,20 +88,35 @@ func Subscribe() (*Subscriber, error) { return nil, common.Perror("cmd.Start", err) } - var ret Subscriber - ret.cmd = cmd - ret.pipe = pipe - ret.scanner = bufio.NewScanner(ret.pipe) - - return &ret, nil + var s Subscriber + s.Event = make(chan *Event) + s.cleanup = make(chan bool) + s.cmd = cmd + s.pipe = pipe + s.scanner = bufio.NewScanner(s.pipe) + + go func() { + for { + select { + case <- s.cleanup: + s.cmd.Wait() + return + default: + if event := s.get(); event != nil { + s.Event <- event + } + } + } + }() + + return &s, nil } -type Event struct { - Name string - Tokens []string +func (s *Subscriber) Close() { + s.cleanup <- true } -func (s *Subscriber) GetLine() (string, error) { +func (s *Subscriber) getLine() (string, error) { if s.scanner.Scan() { return s.scanner.Text(), nil } @@ -105,29 +128,18 @@ func (s *Subscriber) GetLine() (string, error) { return "", nil } -func (s *Subscriber) Get() (*Event, error) { +func (s *Subscriber) get() (*Event) { var line string var err error for { - line, err = s.GetLine() + line, err = s.getLine() if line[0] != 'W' { break } } - if err != nil { return nil, err } - if line == "" { return nil, nil } - - name, tokens, found := strings.Cut(line, " ") + if line == "" { return nil } + if err != nil { return &Event{err, "", nil} } - var event Event - event.Name = name - event.Tokens = strings.Split(tokens, " ") - - // We do not found. Shut up, compiler. - found = !found - - return &event, nil -} + name, tokens, _ := strings.Cut(line, " ") -func (s *Subscriber) Close() error { - return s.cmd.Wait() + return &Event{nil, name, strings.Split(tokens, " ")} } diff --git a/common/common.go b/common/common.go @@ -3,6 +3,7 @@ package common import ( "os" "fmt" + "strconv" ) func Perror(function string, err error) error { @@ -10,5 +11,15 @@ func Perror(function string, err error) error { } func Error(format string, a ... any) (int, error) { - return fmt.Fprintf(os.Stderr, format, a) + fmt.Fprintf(os.Stderr, "error: ") + if (len(a) == 0) { + return fmt.Fprintf(os.Stderr, format) + } else { + return fmt.Fprintf(os.Stderr, format, a) + } +} + +func Intify(s string) (int, error) { + i, err := strconv.ParseInt(s, 0, 0) + return int(i), err } diff --git a/main.go b/main.go @@ -1,7 +1,7 @@ package main import ( - "fmt" + "strings" "hhvn.uk/hbspbar/common" "hhvn.uk/hbspbar/bspc" @@ -13,32 +13,78 @@ import ( */ ) +var reloadEvents = []string{ + "monitor_rename", + "monitor_swap", + "monitor_focus", + "desktop_add", + "desktop_rename", + "desktop_remove", + "desktop_swap", + "desktop_transfer", + "desktop_focus", + "desktop_activate", + "desktop_layout", + "node_add", + "node_remove", + "node_swap", + "node_transfer" } + +func reloadState(current *bspc.State) (*bspc.State, error) { + state, err := bspc.LoadState() + + if err != nil { + return current, err + } else { + return state, nil + } +} + +func handleEvent(events *bspc.Subscriber) (bool) { + var event *bspc.Event + + select { + case event = <-events.Event: + break + default: + return false + } + + if event.Err != nil { + common.Error("Couldn't read event: %s\n", event.Err) + return false + } + + for i := 0; i < len(reloadEvents); i++ { + if event.Name == reloadEvents[i] { + return true + } + } + + return false +} + func main() { state, err := bspc.LoadState() if (err != nil) { - common.Error("Couldn't load bspwm state: %s", err) + common.Error("Couldn't load bspwm state: %s\n", err) return } events, err := bspc.Subscribe() if (err != nil) { - common.Error("Couldn't subscribe to bspwm: %s", err) + common.Error("Couldn't subscribe to bspwm: %s\n", err) return } defer events.Close() - fmt.Println(state.Monitors[0].ID) for { - event, err := events.Get() - - if err != nil { - common.Error("Couldn't read event: %s", err) - return - } - - if event != nil { - fmt.Printf("Event: %v %v\n", event.Name, event.Tokens) + if reload := handleEvent(events); reload { + state, err = reloadState(state) + if err != nil { + common.Error("Couldn't reload bspwm state: %s\n", err) + } } } }