package bspc import ( "fmt" "io" "bufio" "strings" "os/exec" "encoding/json" "hhvn.uk/hbspbar/common" ) type State struct { Monitors []*Monitor FocusedMonitorID int } func (s *State) GetMon(id int) (*Monitor) { for _, m := range s.Monitors { if m.ID == id { return m } } return nil } type Monitor struct { Focused bool ID int Name string Rectangle *Rect Desktops []*Desktop FocusedDesktopID int } 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 Node struct { ID int FirstChild *Node SecondChild *Node } type Desktop struct { Focused bool Name string Layout string ID int Root *Node } 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) } for _, m := range state.Monitors { m.Focused = m.ID == state.FocusedMonitorID for _, d := range m.Desktops { d.Focused = d.ID == m.FocusedDesktopID } } 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 { 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, " ")} }