package bspc import ( "fmt" "io" "bufio" "strings" "os/exec" "encoding/json" "hhvn.uk/hbspbar/common" ) type State struct { Monitors []*Monitor } func (s *State) GetMon(id int) (*Monitor) { for _, m := range s.Monitors { if m.ID == id { return m } } return nil } type Monitor struct { ID int Rectangle *Rect Desktops []*Desktop } func (m *Monitor) TopPadding(px uint) { cmd := exec.Command("bspc", "config", "-m", fmt.Sprintf("%d", m.ID), "top_padding", fmt.Sprintf("%d", px)) pipe, _ := cmd.StderrPipe() scan := bufio.NewScanner(pipe) cmd.Start() for scan.Scan() { common.Error("%s\n", scan.Text()) } if err := scan.Err(); err != nil { common.Perror("scan.Err", err) } cmd.Wait() } type Rect struct { X uint Y uint Width uint Height uint } type Desktop struct { Name string Layout string } func LoadState() (*State, error) { cmd := exec.Command("bspc", "wm", "-d") out, err := cmd.StdoutPipe() if err != nil { return nil, common.Perror("cmd.StdoutPipe", err) } if err = cmd.Start(); err != nil { return nil, common.Perror("cmd.Start", err) } data, err := io.ReadAll(out) if err != nil { return nil, common.Perror("io.ReadAll", err) } if err = cmd.Wait(); err != nil { return nil, common.Perror("cmd.Wait", err) } var state State err = json.Unmarshal(data, &state) if err != nil { return nil, common.Perror("json.Unmarshal", err) } return &state, nil } 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") pipe, err := cmd.StdoutPipe() if err != nil { return nil, common.Perror("cmd.StdoutPipe", err) } if err = cmd.Start(); err != nil { return nil, common.Perror("cmd.Start", err) } 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 { default: if event := s.get(); event != nil { s.Event <- event } } }}() return &s, nil } func (s *Subscriber) Close() { s.cmd.Process.Kill() } func (s *Subscriber) getLine() (string, error) { if s.scanner.Scan() { return s.scanner.Text(), nil } if err := s.scanner.Err(); err != nil { return "", common.Perror("scanner.Err", err) } return "", nil } func (s *Subscriber) get() (*Event) { var line string var err error for { line, err = s.getLine() if len(line) > 0 && line[0] != 'W' { break } } if line == "" { return nil } if err != nil { return &Event{err, "", nil} } name, tokens, _ := strings.Cut(line, " ") return &Event{nil, name, strings.Split(tokens, " ")} }