This commit is contained in:
a 2023-06-11 05:24:15 -05:00
parent de99ca3429
commit b4da3d53fd
20 changed files with 301 additions and 93 deletions

View File

@ -19,9 +19,10 @@ all: bspwm bspc
VPATH=src
bspwm:
bspwm: cmd/bspwm src/**/*
go build -o bspwm ./cmd/bspwm
bspc: cmd/bspc src
bspc: cmd/bspc src/**/*
go build -o bspc ./cmd/bspc
xephyr:

View File

@ -10,7 +10,12 @@ import (
func main() {
s, err := sock.Client("")
errExit(err)
resp, err := s.Send(os.Args[1:]...)
o := os.Args[1:]
if len(o) == 0 {
fmt.Println("no arguments given.")
return
}
resp, err := s.Send(o...)
errExit(err)
fmt.Print(resp)
}

View File

@ -2,40 +2,56 @@ package main
import (
"context"
"fmt"
"log"
"os"
"os/signal"
"syscall"
"tuxpa.in/t/wm/src/bsp"
"tuxpa.in/t/wm/src/handler"
"tuxpa.in/t/wm/src/handler/domains"
"tuxpa.in/t/wm/src/sock"
)
func main() {
// create socket
ln, err := sock.Server("./bspwm.sock")
if err != nil {
panic(err)
}
defer ln.Close()
log.Printf("starting bspwm")
ctx, stop := signal.NotifyContext(context.Background(),
os.Interrupt,
syscall.SIGTERM,
syscall.SIGQUIT)
syscall.SIGQUIT,
syscall.SIGINT,
)
defer stop()
h := &handler.Handler{}
// initialize WM state
w := bsp.NewWM()
// create a wm manager
xwm := bsp.NewXWM(w, ln.X11())
// install the handler
h := &handler.Handler{
XWM: xwm,
}
handler.AddDomain[domains.Todo](h, "node")
handler.AddDomain[domains.Todo](h, "desktop")
handler.AddDomain[domains.Todo](h, "monitor")
handler.AddDomain[domains.Todo](h, "wm")
handler.AddDomain[domains.Todo](h, "rule")
handler.AddDomain[domains.Todo](h, "config")
handler.AddDomain[domains.Todo](h, "subscribe")
handler.AddDomain[domains.Todo](h, "quit")
handler.AddDomain[domains.Query](h, "query")
handler.AddDomain[domains.Echo](h, "echo")
for {
select {
case m := <-ln.Msg():
log.Printf("got cmd: %s", m.Args())
h.Run(m)
case <-ctx.Done():
fmt.Println()
log.Printf("bspwm shutting down...")
log.Println("bspwm shutting down...")
return
}
}

19
go.mod
View File

@ -2,19 +2,10 @@ module tuxpa.in/t/wm
go 1.19
require github.com/alecthomas/kong v0.7.1
replace github.com/jezek/xgb v1.1.0 => ./xgb
require github.com/jezek/xgb v1.1.0 // indirect
replace github.com/jezek/xgbutil v0.0.0-20230603163917-04188eb39cf0 => ./xgbutil
require (
github.com/google/uuid v1.3.0 // indirect
github.com/mattn/go-isatty v0.0.16 // indirect
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 // indirect
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab // indirect
modernc.org/libc v1.21.2 // indirect
modernc.org/mathutil v1.5.0 // indirect
modernc.org/memory v1.4.0 // indirect
modernc.org/xau v1.0.13 // indirect
modernc.org/xcb v1.0.13
modernc.org/xdmcp v1.0.14 // indirect
)
require github.com/jezek/xgb v1.1.0
require github.com/jezek/xgbutil v0.0.0-20230603163917-04188eb39cf0

29
go.sum
View File

@ -1,27 +1,2 @@
github.com/alecthomas/assert/v2 v2.1.0 h1:tbredtNcQnoSd3QBhQWI7QZ3XHOVkw1Moklp2ojoH/0=
github.com/alecthomas/kong v0.7.1 h1:azoTh0IOfwlAX3qN9sHWTxACE2oV8Bg2gAwBsMwDQY4=
github.com/alecthomas/kong v0.7.1/go.mod h1:n1iCIO2xS46oE8ZfYCNDqdR0b0wZNrXAIAqro/2132U=
github.com/alecthomas/repr v0.1.0 h1:ENn2e1+J3k09gyj2shc0dHr/yjaWSHRlrJ4DPMevDqE=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM=
github.com/jezek/xgb v1.1.0 h1:wnpxJzP1+rkbGclEkmwpVFQWpuE2PUGNUzP8SbfFobk=
github.com/jezek/xgb v1.1.0/go.mod h1:nrhwO0FX/enq75I7Y7G8iN1ubpSGZEiA3v9e9GyRFlk=
github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 h1:OdAsTTz6OkFY5QxjkYwrChwuRruF69c169dPK26NUlk=
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab h1:2QkjZIsXupsJbJIdSjjUOgWK3aEtzyuh2mPt3l/CkeU=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
modernc.org/libc v1.21.2 h1:V053DgNSpAY+IPrO3XlWqrFKUiQqHyPqG4dsx42Ulck=
modernc.org/libc v1.21.2/go.mod h1:przBsL5RDOZajTVslkugzLBj1evTue36jEomFQOoYuI=
modernc.org/mathutil v1.5.0 h1:rV0Ko/6SfM+8G+yKiyI830l3Wuz1zRutdslNoQ0kfiQ=
modernc.org/mathutil v1.5.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
modernc.org/memory v1.4.0 h1:crykUfNSnMAXaOJnnxcSzbUGMqkLWjklJKkBK2nwZwk=
modernc.org/memory v1.4.0/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU=
modernc.org/xau v1.0.13 h1:AEYsSsJFSmkfhSwV6/dx6GoHt02BnZLmMcRT1O8EUOo=
modernc.org/xau v1.0.13/go.mod h1:5ORRqBKlhiUXwoVdM0+ZPy8plvcq0OPOdEgTWyeokhk=
modernc.org/xcb v1.0.13 h1:SIMh1yKsKjkfT/qAxcxSv/W+mRe4RnPzp+XewnD4rS4=
modernc.org/xcb v1.0.13/go.mod h1:M5m1dSVaHvBUf5XUg5Y/b1DZdJs6NK7pYLqsQ+d0swU=
modernc.org/xdmcp v1.0.14 h1:hRCxbYfl75rvOdCmVCPLBRCedClOPjpHiq+tOBzGS3Q=
modernc.org/xdmcp v1.0.14/go.mod h1:TDsH3iMey1HJ3tMCePfTy4dIX6hL/MIVnGREX97nCrE=
github.com/BurntSushi/freetype-go v0.0.0-20160129220410-b763ddbfe298/go.mod h1:D+QujdIlUNfa0igpNMk6UIvlb6C252URs4yupRUV4lQ=
github.com/BurntSushi/graphics-go v0.0.0-20160129215708-b43f31a4a966/go.mod h1:Mid70uvE93zn9wgF92A/r5ixgnvX8Lh68fxp9KQBaI0=

33
src/bsp/loop.go Normal file
View File

@ -0,0 +1,33 @@
package bsp
import (
"context"
"github.com/jezek/xgbutil"
)
type XWM struct {
w *WM
x *xgbutil.XUtil
}
func NewXWM(w *WM, x *xgbutil.XUtil) *XWM {
xwm := &XWM{
w: w,
x: x,
}
return xwm
}
func (xwm *XWM) Start(ctx context.Context) error {
for {
err := xwm.run(ctx)
if err != nil {
return err
}
}
}
func (xwm *XWM) run(ctx context.Context) error {
return nil
}

View File

@ -1 +0,0 @@
package bsp

46
src/bsp/wm.go Normal file
View File

@ -0,0 +1,46 @@
package bsp
import (
"sync"
)
type WM struct {
Desktops []*Desktop
Monitors []*Monitor
mu sync.RWMutex
}
type Desktop struct {
Name string
Monitor *Monitor
}
type Monitor struct {
Name string
}
func (w *WM) Mutate(fn func() error) {
w.mu.Lock()
defer w.mu.Unlock()
if fn != nil {
fn()
}
}
func (w *WM) View(fn func() error) {
w.mu.RLock()
defer w.mu.RUnlock()
if fn != nil {
fn()
}
}
func (w *WM) AddDesktop(name string) {
}
func NewWM() *WM {
w := &WM{}
return w
}

View File

@ -22,7 +22,14 @@ type ErrMissingArguments struct {
}
func (m *ErrMissingArguments) Error() string {
return `Missing Arguments`
return `Missing arguments`
}
type ErrNoCommandsGiven struct {
}
func (m *ErrNoCommandsGiven) Error() string {
return `No commands given`
}
type ErrTODO struct {

19
src/handler/domain.go Normal file
View File

@ -0,0 +1,19 @@
package handler
import (
"tuxpa.in/t/wm/src/bsp"
"tuxpa.in/t/wm/src/sock"
)
type Domain interface {
DomainRunner
DomainState
}
type DomainRunner interface {
Run(*sock.Msg) ([]byte, error)
}
type DomainState interface {
SetXWM(*bsp.XWM)
}

View File

@ -0,0 +1,15 @@
package domains
import "tuxpa.in/t/wm/src/bsp"
type inject struct {
xwm
}
type xwm struct {
XWM *bsp.XWM
}
func (x xwm) SetXWM(z *bsp.XWM) {
x.XWM = z
}

View File

@ -8,9 +8,10 @@ import (
)
type Echo struct {
inject
}
func (n *Echo) Run(msg *sock.Msg) ([]byte, error) {
func (n Echo) Run(msg *sock.Msg) ([]byte, error) {
if !msg.HasNext() {
return nil, &copies.ErrMissingArguments{}
}

View File

@ -3,9 +3,10 @@ package domains
import "tuxpa.in/t/wm/src/copies"
type Node struct {
inject
}
func (n *Node) Run(args ...string) error {
func (n Node) Run(args ...string) error {
if len(args) == 0 {
return &copies.ErrMissingArguments{}
}

View File

@ -1,13 +1,78 @@
package domains
import "tuxpa.in/t/wm/src/copies"
import (
"bytes"
"fmt"
"tuxpa.in/t/wm/src/copies"
"tuxpa.in/t/wm/src/sock"
)
type Query struct {
Command string
UseNames bool
inject
}
func (n *Query) Run(args ...string) error {
if len(args) == 0 {
return &copies.ErrMissingArguments{}
func (n Query) Run(msg *sock.Msg) ([]byte, error) {
if !msg.HasNext() {
return nil, &copies.ErrMissingArguments{}
}
return nil
for {
ok, err := n.parse(msg)
if err != nil {
return nil, err
}
if !ok {
break
}
}
switch n.Command {
case "desktops":
return n.desktops(msg)
default:
return nil, &copies.ErrTODO{}
}
}
func (n *Query) desktops(msg *sock.Msg) ([]byte, error) {
o := new(bytes.Buffer)
o.WriteString("hi there")
return o.Bytes(), nil
}
func (n *Query) parse(msg *sock.Msg) (bool, error) {
if !msg.HasNext() {
return false, &copies.ErrMissingArguments{}
}
arg := msg.Next()
switch arg {
case "--desktop", "-d":
case "--desktops", "-D":
return n.readCommand(msg, "desktops")
case "--monitor", "-m":
case "--monitors", "-M":
return n.readCommand(msg, "monitors")
case "--names":
n.UseNames = true
case "--node", "-n":
case "--nodes", "-N":
return n.readCommand(msg, "nodes")
case "--tree", "-T":
return n.readCommand(msg, "tree")
default:
return false, fmt.Errorf(`unknown option: '%s'`, arg)
}
return msg.HasNext(), nil
}
func (n *Query) readCommand(msg *sock.Msg, c string) (bool, error) {
if n.Command == "" {
n.Command = c
return msg.HasNext(), nil
}
return false, fmt.Errorf("multiple commands given")
}

View File

@ -0,0 +1,14 @@
package domains
import (
"tuxpa.in/t/wm/src/copies"
"tuxpa.in/t/wm/src/sock"
)
type Todo struct {
inject
}
func (n Todo) Run(msg *sock.Msg) ([]byte, error) {
return nil, &copies.ErrTODO{}
}

View File

@ -3,12 +3,29 @@ package handler
import (
"fmt"
"tuxpa.in/t/wm/src/bsp"
"tuxpa.in/t/wm/src/copies"
"tuxpa.in/t/wm/src/handler/domains"
"tuxpa.in/t/wm/src/sock"
)
type Handler struct {
XWM *bsp.XWM
domains map[string]func() Domain
}
func AddDomain[T any, PT interface {
Domain
*T
}](h *Handler, name string) {
if h.domains == nil {
h.domains = map[string]func() Domain{}
}
h.domains[name] = func() Domain {
domain := PT(new(T))
domain.SetXWM(h.XWM)
return domain
}
}
func (h *Handler) Run(msg *sock.Msg) {
@ -28,26 +45,15 @@ func (h *Handler) run(msg *sock.Msg) ([]byte, error) {
return nil, &copies.ErrMissingArguments{}
}
cmd := msg.Next()
switch cmd {
case "node",
"desktop",
"monitor",
"query",
"wm",
"rule",
"config",
"subscribe",
"quit":
return nil, &copies.ErrTODO{Kind: "domain", Name: cmd}
case "echo":
return h.runDomain(cmd, msg, (&domains.Echo{}).Run)
default:
d, ok := h.domains[cmd]
if !ok {
return nil, &copies.ErrUnknownDomainOrCommand{Str: cmd}
}
return h.runDomain(cmd, msg, d())
}
func (h *Handler) runDomain(name string, msg *sock.Msg, fn func(*sock.Msg) ([]byte, error)) ([]byte, error) {
str, err := fn(msg)
func (h *Handler) runDomain(name string, msg *sock.Msg, d DomainRunner) ([]byte, error) {
str, err := d.Run(msg)
if err != nil {
return nil, fmt.Errorf("%s: %w", name, err)
}

View File

@ -20,7 +20,7 @@ func (m *Msg) Peek() string {
return m.Get(0)
}
func (m *Msg) Next() string {
if m.Has(m.cur) {
if m.Has(0) {
m.cur = m.cur + 1
return m.args[m.cur-1]
}

View File

@ -5,12 +5,13 @@ import (
"fmt"
"net"
"os"
"strings"
"github.com/jezek/xgb"
"github.com/jezek/xgbutil"
)
type SockListener struct {
xgb *xgb.Conn
xgb *xgbutil.XUtil
l *net.UnixListener
ch chan *Msg
@ -19,24 +20,32 @@ type SockListener struct {
func (s *SockListener) Msg() <-chan *Msg {
return s.ch
}
func Server(path string) (*SockListener, error) {
s := &SockListener{}
s.ch = make(chan *Msg, 1)
xc, err := xgb.NewConnDisplay("")
if err != nil {
return nil, err
func (s *SockListener) X11() *xgbutil.XUtil {
return s.xgb
}
s.xgb = xc
func getUnixAddr(xc *xgbutil.XUtil, path string) *net.UnixAddr {
if path == "" {
path = os.Getenv(SOCKET_ENV_VAR)
}
if path == "" {
path = fmt.Sprintf(SOCKET_PATH_TPL, "", xc.DisplayNumber, xc.DefaultScreen)
path = fmt.Sprintf(SOCKET_PATH_TPL, "", xc.Conn().DisplayNumber, xc.Conn().DefaultScreen)
}
addr, err := net.ResolveUnixAddr("unix", path)
if err != nil {
panic(err)
}
return addr
}
func Server(path string) (*SockListener, error) {
s := &SockListener{}
s.ch = make(chan *Msg, 1)
xc, err := xgbutil.NewConn()
if err != nil {
return nil, err
}
s.xgb = xc
addr := getUnixAddr(xc, path)
conn, err := net.ListenUnix("unix", addr)
if err != nil {
return nil, err
@ -59,7 +68,8 @@ func Server(path string) (*SockListener, error) {
return s, nil
}
func (s *SockListener) Close() error {
return s.l.Close()
err := s.l.Close()
return err
}
func (s *SockListener) acceptConn(c *net.UnixConn) error {
msg := &Msg{}
@ -71,8 +81,10 @@ func (s *SockListener) acceptConn(c *net.UnixConn) error {
bts = bts[:n]
splt := bytes.Split(bts, []byte{0})
for _, v := range splt {
if len(strings.TrimSpace(string(v))) > 0 {
msg.args = append(msg.args, string(v))
}
}
msg.c = c
s.ch <- msg
return nil

1
xgb Submodule

@ -0,0 +1 @@
Subproject commit a57abb570aeba12f867c58afe22ce49ac5db4872

1
xgbutil Submodule

@ -0,0 +1 @@
Subproject commit 04188eb39cf0fa005f5b3aec48faa82541748b79