This commit is contained in:
parent
de99ca3429
commit
b4da3d53fd
5
Makefile
5
Makefile
|
@ -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:
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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
19
go.mod
|
@ -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
29
go.sum
|
@ -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=
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
package bsp
|
|
@ -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
|
||||
}
|
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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{}
|
||||
}
|
||||
|
|
|
@ -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{}
|
||||
}
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
|
|
|
@ -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{}
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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]
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
Subproject commit a57abb570aeba12f867c58afe22ce49ac5db4872
|
|
@ -0,0 +1 @@
|
|||
Subproject commit 04188eb39cf0fa005f5b3aec48faa82541748b79
|
Loading…
Reference in New Issue