This commit is contained in:
a 2022-12-25 23:06:40 -06:00
parent b8647ded0c
commit c3c0b1269b
16 changed files with 360 additions and 52 deletions

1
.env Executable file
View File

@ -0,0 +1 @@
export

5
.gitignore vendored Normal file
View File

@ -0,0 +1,5 @@
/bspwm
/bspc
*.log
*.sock

View File

@ -1,4 +1,4 @@
WINDOWSIZE=1024:768
WINDOWSIZE=1280x720
VERCMD ?= git describe --tags 2> /dev/null
@ -19,17 +19,13 @@ all: bspwm bspc
VPATH=src
include Sourcedeps
$(WM_OBJ) $(CLI_OBJ): Makefile
#bspwm: $(WM_OBJ)
bspwm:
bspc: cmd/bspc src
go build ./cmd/bspc -o bspc
go build -o bspc ./cmd/bspc
xephyr:
Xephyr -br ac -noreset ${WINDOWSIZE} :1
Xephyr :11 -br -ac -noreset -screen ${WINDOWSIZE}
install:
mkdir -p "$(DESTDIR)$(BINPREFIX)"

View File

@ -8,7 +8,7 @@ import (
)
func main() {
s, err := sock.Default()
s, err := sock.Client("")
errExit(err)
resp, err := s.Send(os.Args[1:]...)
errExit(err)

42
cmd/bspwm/main.go Normal file
View File

@ -0,0 +1,42 @@
package main
import (
"context"
"fmt"
"log"
"os"
"os/signal"
"syscall"
"tuxpa.in/t/wm/src/handler"
"tuxpa.in/t/wm/src/sock"
)
func main() {
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)
defer stop()
h := &handler.Handler{}
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...")
return
}
}
}

1
src/bsp/node.go Normal file
View File

@ -0,0 +1 @@
package bsp

View File

@ -1,17 +0,0 @@
package bspc
import "tuxpa.in/t/wm/src/sock"
type NODE_SEL string
type Node struct {
Sel NODE_SEL
}
func (n *Node) Focus(node NODE_SEL) error {
s, err := sock.Default()
if err != nil {
return err
}
return s.Send("node", "-l")
}

View File

@ -1,12 +0,0 @@
package cmd
import "context"
type Context struct {
Debug bool
context.Context
}
type Cmd interface {
Run(ctx *Context) error
}

35
src/copies/errors.go Normal file
View File

@ -0,0 +1,35 @@
package copies
import "fmt"
type ErrUnknownDomainOrCommand struct {
Str string
}
func (u *ErrUnknownDomainOrCommand) Error() string {
return fmt.Sprintf(`Unknown Command: '%s'.`, u.Str)
}
type ErrUnknownCommand struct {
Cmd string
}
func (u *ErrUnknownCommand) Error() string {
return fmt.Sprintf(`Unknown Command: '%s'.`, u.Cmd)
}
type ErrMissingArguments struct {
}
func (m *ErrMissingArguments) Error() string {
return `Missing Arguments`
}
type ErrTODO struct {
Kind string
Name string
}
func (e *ErrTODO) Error() string {
return fmt.Sprintf(`'%s' not implemented: '%s'.`, e.Kind, e.Name)
}

View File

@ -0,0 +1,19 @@
package domains
import (
"strings"
"tuxpa.in/t/wm/src/copies"
"tuxpa.in/t/wm/src/sock"
)
type Echo struct {
}
func (n *Echo) Run(msg *sock.Msg) ([]byte, error) {
if !msg.HasNext() {
return nil, &copies.ErrMissingArguments{}
}
out := strings.Join(msg.Args(), " ")
return []byte(out), nil
}

View File

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

View File

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

55
src/handler/handler.go Normal file
View File

@ -0,0 +1,55 @@
package handler
import (
"fmt"
"tuxpa.in/t/wm/src/copies"
"tuxpa.in/t/wm/src/handler/domains"
"tuxpa.in/t/wm/src/sock"
)
type Handler struct {
}
func (h *Handler) Run(msg *sock.Msg) {
resp, err := h.run(msg)
if msg.Err(err) {
return
}
err = msg.Reply(string(resp))
if msg.Err(err) {
return
}
msg.Reply("")
}
func (h *Handler) run(msg *sock.Msg) ([]byte, error) {
if !msg.HasNext() {
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:
return nil, &copies.ErrUnknownDomainOrCommand{Str: cmd}
}
}
func (h *Handler) runDomain(name string, msg *sock.Msg, fn func(*sock.Msg) ([]byte, error)) ([]byte, error) {
str, err := fn(msg)
if err != nil {
return nil, fmt.Errorf("%s: %w", name, err)
}
return str, nil
}

73
src/sock/msg.go Normal file
View File

@ -0,0 +1,73 @@
package sock
import (
"bufio"
"net"
"sync/atomic"
)
type Msg struct {
c *net.UnixConn
args []string
closed atomic.Bool
cur int
}
func (m *Msg) HasNext() bool {
return m.Has(0)
}
func (m *Msg) Peek() string {
return m.Get(0)
}
func (m *Msg) Next() string {
if m.Has(m.cur) {
m.cur = m.cur + 1
return m.args[m.cur-1]
}
return ""
}
func (m *Msg) Has(i int) bool {
return len(m.args) > (i + m.cur)
}
func (m *Msg) Get(i int) string {
if m.Has(i) {
return m.args[i+m.cur]
}
return ""
}
func (m *Msg) Args() []string {
if m.HasNext() {
return m.args[m.cur:]
}
return nil
}
func (m *Msg) Err(e error) bool {
if e == nil {
return false
}
if !m.closed.CompareAndSwap(false, true) {
return true
}
defer m.c.Close()
wr := bufio.NewWriter(m.c)
wr.Write([]byte{7})
wr.Write([]byte(e.Error()))
wr.Write([]byte("\n"))
wr.Flush()
return true
}
func (m *Msg) Reply(ans string) error {
if !m.closed.CompareAndSwap(false, true) {
return nil
}
defer m.c.Close()
wr := bufio.NewWriter(m.c)
_, err := wr.Write([]byte(ans))
if err != nil {
return err
}
wr.Write([]byte("\n"))
wr.Write([]byte{0})
wr.Flush()
return nil
}

79
src/sock/server.go Normal file
View File

@ -0,0 +1,79 @@
package sock
import (
"bytes"
"fmt"
"net"
"os"
"github.com/jezek/xgb"
)
type SockListener struct {
xgb *xgb.Conn
l *net.UnixListener
ch chan *Msg
}
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
}
s.xgb = xc
if path == "" {
path = os.Getenv(SOCKET_ENV_VAR)
}
if path == "" {
path = fmt.Sprintf(SOCKET_PATH_TPL, "", xc.DisplayNumber, xc.DefaultScreen)
}
addr, err := net.ResolveUnixAddr("unix", path)
if err != nil {
return nil, err
}
conn, err := net.ListenUnix("unix", addr)
if err != nil {
return nil, err
}
s.l = conn
go func() {
for {
conn, err := s.l.AcceptUnix()
if err != nil {
continue
}
go func() {
err = s.acceptConn(conn)
if err != nil {
fmt.Printf("fail accept conn: %s\n", err)
}
}()
}
}()
return s, nil
}
func (s *SockListener) Close() error {
return s.l.Close()
}
func (s *SockListener) acceptConn(c *net.UnixConn) error {
msg := &Msg{}
bts := make([]byte, 4096)
n, err := c.Read(bts)
if err != nil {
return err
}
bts = bts[:n]
splt := bytes.Split(bts, []byte{0})
for _, v := range splt {
msg.args = append(msg.args, string(v))
}
msg.c = c
s.ch <- msg
return nil
}

View File

@ -1,6 +1,7 @@
package sock
import (
"bufio"
"fmt"
"io"
"net"
@ -11,28 +12,28 @@ import (
const SOCKET_PATH_TPL = "/tmp/bspwm%s_%d_%d-socket"
const SOCKET_ENV_VAR = "BSPWM_SOCKET"
type Sock struct {
C net.Conn
C *net.UnixConn
}
func (s *Sock) Send(args ...string) (string, error) {
if len(args) == 0 {
return "", s.C.Close()
}
wr := bufio.NewWriter(s.C)
for _, msg := range args {
_, err := s.C.Write([]byte(msg))
if err != nil {
return "", err
}
_, err = s.C.Write([]byte{0})
if err != nil {
return "", err
}
wr.WriteString(msg)
wr.WriteByte(0)
}
wr.Flush()
s.C.CloseWrite()
bts, err := io.ReadAll(s.C)
if err != nil {
return "", err
}
s.C.CloseRead()
if len(bts) > 0 {
if bts[0] == 7 {
return "", fmt.Errorf(string(bts[1:]))
@ -41,19 +42,23 @@ func (s *Sock) Send(args ...string) (string, error) {
return string(bts), nil
}
func Default() (*Sock, error) {
return New(os.Getenv("BSPWM_SOCKET"))
}
func New(path string) (*Sock, error) {
func Client(path string) (*Sock, error) {
xc, err := xgb.NewConn()
if err != nil {
return nil, err
}
defer xc.Close()
if path == "" {
path = os.Getenv(SOCKET_ENV_VAR)
}
if path == "" {
path = fmt.Sprintf(SOCKET_PATH_TPL, "", xc.DisplayNumber, xc.DefaultScreen)
}
conn, err := net.Dial("unix", path)
addr, err := net.ResolveUnixAddr("unix", path)
if err != nil {
return nil, err
}
conn, err := net.DialUnix("unix", nil, addr)
if err != nil {
return nil, err
}