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
|
VERCMD ?= git describe --tags 2> /dev/null
|
||||||
|
@ -19,17 +19,13 @@ all: bspwm bspc
|
||||||
|
|
||||||
VPATH=src
|
VPATH=src
|
||||||
|
|
||||||
include Sourcedeps
|
bspwm:
|
||||||
|
|
||||||
$(WM_OBJ) $(CLI_OBJ): Makefile
|
|
||||||
|
|
||||||
#bspwm: $(WM_OBJ)
|
|
||||||
|
|
||||||
bspc: cmd/bspc src
|
bspc: cmd/bspc src
|
||||||
go build ./cmd/bspc -o bspc
|
go build -o bspc ./cmd/bspc
|
||||||
|
|
||||||
xephyr:
|
xephyr:
|
||||||
Xephyr -br ac -noreset ${WINDOWSIZE} :1
|
Xephyr :11 -br -ac -noreset -screen ${WINDOWSIZE}
|
||||||
|
|
||||||
install:
|
install:
|
||||||
mkdir -p "$(DESTDIR)$(BINPREFIX)"
|
mkdir -p "$(DESTDIR)$(BINPREFIX)"
|
||||||
|
|
|
@ -8,7 +8,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
s, err := sock.Default()
|
s, err := sock.Client("")
|
||||||
errExit(err)
|
errExit(err)
|
||||||
resp, err := s.Send(os.Args[1:]...)
|
resp, err := s.Send(os.Args[1:]...)
|
||||||
errExit(err)
|
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
|
package sock
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bufio"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
|
@ -11,28 +12,28 @@ import (
|
||||||
|
|
||||||
const SOCKET_PATH_TPL = "/tmp/bspwm%s_%d_%d-socket"
|
const SOCKET_PATH_TPL = "/tmp/bspwm%s_%d_%d-socket"
|
||||||
|
|
||||||
|
const SOCKET_ENV_VAR = "BSPWM_SOCKET"
|
||||||
|
|
||||||
type Sock struct {
|
type Sock struct {
|
||||||
C net.Conn
|
C *net.UnixConn
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Sock) Send(args ...string) (string, error) {
|
func (s *Sock) Send(args ...string) (string, error) {
|
||||||
if len(args) == 0 {
|
if len(args) == 0 {
|
||||||
return "", s.C.Close()
|
return "", s.C.Close()
|
||||||
}
|
}
|
||||||
|
wr := bufio.NewWriter(s.C)
|
||||||
for _, msg := range args {
|
for _, msg := range args {
|
||||||
_, err := s.C.Write([]byte(msg))
|
wr.WriteString(msg)
|
||||||
if err != nil {
|
wr.WriteByte(0)
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
_, err = s.C.Write([]byte{0})
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
wr.Flush()
|
||||||
|
s.C.CloseWrite()
|
||||||
bts, err := io.ReadAll(s.C)
|
bts, err := io.ReadAll(s.C)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
s.C.CloseRead()
|
||||||
if len(bts) > 0 {
|
if len(bts) > 0 {
|
||||||
if bts[0] == 7 {
|
if bts[0] == 7 {
|
||||||
return "", fmt.Errorf(string(bts[1:]))
|
return "", fmt.Errorf(string(bts[1:]))
|
||||||
|
@ -41,19 +42,23 @@ func (s *Sock) Send(args ...string) (string, error) {
|
||||||
return string(bts), nil
|
return string(bts), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func Default() (*Sock, error) {
|
func Client(path string) (*Sock, error) {
|
||||||
return New(os.Getenv("BSPWM_SOCKET"))
|
|
||||||
}
|
|
||||||
func New(path string) (*Sock, error) {
|
|
||||||
xc, err := xgb.NewConn()
|
xc, err := xgb.NewConn()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
defer xc.Close()
|
defer xc.Close()
|
||||||
|
if path == "" {
|
||||||
|
path = os.Getenv(SOCKET_ENV_VAR)
|
||||||
|
}
|
||||||
if path == "" {
|
if path == "" {
|
||||||
path = fmt.Sprintf(SOCKET_PATH_TPL, "", xc.DisplayNumber, xc.DefaultScreen)
|
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 {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue