commit 2e3b351c355bd241de4aa4f2bdafd633ccef6eac
Author: hhvn <dev@hhvn.uk>
Date: Fri, 4 Aug 2023 23:02:50 +0100
Implement bspc wrappers
Diffstat:
5 files changed, 203 insertions(+), 0 deletions(-)
diff --git a/bspc/bspc.go b/bspc/bspc.go
@@ -0,0 +1,133 @@
+package bspc
+
+import (
+ "io"
+ "bufio"
+ "strings"
+ "os/exec"
+ "encoding/json"
+
+ "hhvn.uk/hbspbar/common"
+)
+
+type State struct {
+ Monitors []*Monitor
+}
+
+type Monitor struct {
+ ID int
+ Rectangle *Rect
+ Desktops []*Desktop
+}
+
+type Rect struct {
+ X int
+ Y int
+ Width int
+ Height int
+}
+
+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 {
+ cmd *exec.Cmd
+ pipe io.ReadCloser
+ scanner *bufio.Scanner
+}
+
+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 ret Subscriber
+ ret.cmd = cmd
+ ret.pipe = pipe
+ ret.scanner = bufio.NewScanner(ret.pipe)
+
+ return &ret, nil
+}
+
+type Event struct {
+ Name string
+ Tokens []string
+}
+
+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, error) {
+ var line string
+ var err error
+ for {
+ 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, " ")
+
+ var event Event
+ event.Name = name
+ event.Tokens = strings.Split(tokens, " ")
+
+ // We do not found. Shut up, compiler.
+ found = !found
+
+ return &event, nil
+}
+
+func (s *Subscriber) Close() error {
+ return s.cmd.Wait()
+}
diff --git a/common/common.go b/common/common.go
@@ -0,0 +1,14 @@
+package common
+
+import (
+ "os"
+ "fmt"
+)
+
+func Perror(function string, err error) error {
+ return fmt.Errorf("%s(): %w", function, err)
+}
+
+func Error(format string, a ... any) (int, error) {
+ return fmt.Fprintf(os.Stderr, format, a)
+}
diff --git a/go.mod b/go.mod
@@ -0,0 +1,8 @@
+module hhvn.uk/hbspbar
+
+go 1.20
+
+require (
+ github.com/BurntSushi/xgb v0.0.0-20210121224620-deaf085860bc // indirect
+ github.com/BurntSushi/xgbutil v0.0.0-20190907113008-ad855c713046 // indirect
+)
diff --git a/go.sum b/go.sum
@@ -0,0 +1,4 @@
+github.com/BurntSushi/xgb v0.0.0-20210121224620-deaf085860bc h1:7D+Bh06CRPCJO3gr2F7h1sriovOZ8BMhca2Rg85c2nk=
+github.com/BurntSushi/xgb v0.0.0-20210121224620-deaf085860bc/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
+github.com/BurntSushi/xgbutil v0.0.0-20190907113008-ad855c713046 h1:O/r2Sj+8QcMF7V5IcmiE2sMFV2q3J47BEirxbXJAdzA=
+github.com/BurntSushi/xgbutil v0.0.0-20190907113008-ad855c713046/go.mod h1:uw9h2sd4WWHOPdJ13MQpwK5qYWKYDumDqxWWIknEQ+k=
diff --git a/main.go b/main.go
@@ -0,0 +1,44 @@
+package main
+
+import (
+ "fmt"
+
+ "hhvn.uk/hbspbar/common"
+ "hhvn.uk/hbspbar/bspc"
+ "hhvn.uk/hbspbar/bar"
+
+ /*
+ "github.com/BurntSushi/xgb"
+ "github.com/BurntSushi/xgb/xproto"
+ */
+)
+
+func main() {
+ state, err := bspc.LoadState()
+ if (err != nil) {
+ common.Error("Couldn't load bspwm state: %s", err)
+ return
+ }
+
+ events, err := bspc.Subscribe()
+ if (err != nil) {
+ common.Error("Couldn't subscribe to bspwm: %s", 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)
+ }
+ }
+}