protocol
This commit is contained in:
parent
b8647ded0c
commit
c3c0b1269b
|
@ -0,0 +1,5 @@
|
|||
/bspwm
|
||||
/bspc
|
||||
|
||||
*.log
|
||||
*.sock
|
12
Makefile
12
Makefile
|
@ -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)"
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
package bsp
|
|
@ -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")
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
package cmd
|
||||
|
||||
import "context"
|
||||
|
||||
type Context struct {
|
||||
Debug bool
|
||||
context.Context
|
||||
}
|
||||
|
||||
type Cmd interface {
|
||||
Run(ctx *Context) error
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue