can now query monitors

This commit is contained in:
a 2023-06-15 10:24:04 -05:00
parent 9efd1c2290
commit 65162b1651
10 changed files with 246 additions and 31 deletions

View File

@ -10,6 +10,7 @@ import (
"syscall" "syscall"
"github.com/jezek/xgbutil" "github.com/jezek/xgbutil"
"github.com/jezek/xgbutil/xevent"
"tuxpa.in/a/zlog/log" "tuxpa.in/a/zlog/log"
"tuxpa.in/t/wm/src/bsp" "tuxpa.in/t/wm/src/bsp"
"tuxpa.in/t/wm/src/handler" "tuxpa.in/t/wm/src/handler"
@ -102,14 +103,19 @@ func _main() (code int, err error) {
return d return d
}) })
beforeCh, afterCh, quitCh := xevent.MainPing(xwm.X)
// message listen loop // message listen loop
for { for {
select { select {
case <-beforeCh:
<-afterCh
case m := <-ln.Msg(): case m := <-ln.Msg():
h.Run(m) h.Run(m)
case cint := <-codeCh: case cint := <-codeCh:
stop() stop()
return cint, nil return cint, nil
case <-quitCh:
stop()
case <-ctx.Done(): case <-ctx.Done():
return 0, nil return 0, nil
} }

5
example.sh Executable file
View File

@ -0,0 +1,5 @@
#!/bin/sh

77
src/bsp/client.go Normal file
View File

@ -0,0 +1,77 @@
package bsp
import (
"github.com/jezek/xgb/xproto"
"github.com/jezek/xgbutil/ewmh"
"github.com/jezek/xgbutil/icccm"
"github.com/jezek/xgbutil/xwindow"
"tuxpa.in/a/zlog/log"
)
type Client struct {
win *xwindow.Window
winId xproto.Window
name string
}
// registers a client to be obtained later
func (xwm *XWM) RegisterClient(wid xproto.Window) *Client {
c := &Client{
winId: wid,
}
xwm.X.Grab()
defer xwm.X.Ungrab()
// dont duplicate registration
if xwm.FindClient(wid) != nil {
log.Trace().Any("id", wid).Msg("duplicate client registration")
return nil
}
// ok this is a new one, so we need to make a new client
c.win = xwindow.New(xwm.X, wid)
if _, err := c.win.Geometry(); err != nil {
log.Err(err).Any("id", wid).Msg("get geometry")
return nil
}
err := xproto.ChangeSaveSetChecked(xwm.X.Conn(), xproto.SetModeInsert, c.winId).Check()
if err != nil {
log.Err(err).Any("id", wid).Msg("change save set checked")
return nil
}
xwm.initClient(c)
return c
}
func (xwm *XWM) initClient(c *Client) {
xwm.updateName(c)
}
func (xwm *XWM) updateName(c *Client) {
newName := func() (n string) {
n, _ = ewmh.WmNameGet(xwm.X, c.winId)
if len(n) > 0 {
return
}
n, _ = icccm.WmNameGet(xwm.X, c.winId)
if len(n) > 0 {
return
}
return
}()
if newName != c.name {
c.name = newName
ewmh.WmVisibleNameSet(xwm.X, c.winId, c.name)
}
}
func (xwm *XWM) FindClient(wid xproto.Window) (c *Client) {
xwm.W.View(func() error {
val, ok := xwm.W.Clients[wid]
if ok {
c = val
}
return nil
})
return
}

View File

@ -83,8 +83,8 @@ type Config struct {
// gap // gap
WindowGap *int `cfg:"window_gap"` WindowGap *int `cfg:"window_gap"`
BorderWidth *int `cfg:"border_width"`
BorderlessSingleton *bool `cfg:"borderless_singleton"` BorderlessSingleton *bool `cfg:"borderless_singleton"`
BorderWidth *bool `cfg:"border_width"`
//colors //colors
ActiveBorderColor *ColorCode `cfg:"active_border_color"` ActiveBorderColor *ColorCode `cfg:"active_border_color"`

View File

@ -2,12 +2,16 @@ package bsp
import ( import (
"context" "context"
"fmt"
"github.com/jezek/xgb/randr"
"github.com/jezek/xgb/xinerama"
"github.com/jezek/xgb/xproto" "github.com/jezek/xgb/xproto"
"github.com/jezek/xgbutil" "github.com/jezek/xgbutil"
"github.com/jezek/xgbutil/keybind" "github.com/jezek/xgbutil/keybind"
"github.com/jezek/xgbutil/mousebind" "github.com/jezek/xgbutil/mousebind"
"github.com/jezek/xgbutil/xevent" "github.com/jezek/xgbutil/xevent"
"github.com/jezek/xgbutil/xwindow"
"tuxpa.in/a/zlog/log" "tuxpa.in/a/zlog/log"
) )
@ -24,10 +28,97 @@ func NewXWM(w *WM, x *xgbutil.XUtil) *XWM {
return xwm return xwm
} }
func (xwm *XWM) Start(ctx context.Context) error { func (xwm *XWM) initBinding(ctx context.Context) error {
randr.Init(xwm.X.Conn())
xinerama.Init(xwm.X.Conn())
keybind.Initialize(xwm.X) keybind.Initialize(xwm.X)
mousebind.Initialize(xwm.X) mousebind.Initialize(xwm.X)
return nil
}
func (xwm *XWM) Start(ctx context.Context) error {
if err := xwm.initBinding(ctx); err != nil {
return err
}
if err := xwm.initMonitors(ctx); err != nil {
return err
}
if err := xwm.initRoot(ctx); err != nil {
return err
}
if err := xwm.initExistingWindows(ctx); err != nil {
return err
}
// for {
// err := xwm.run(ctx)
// if err != nil {
// return err
// }
// }
return nil
}
func (xwm *XWM) initMonitors(ctx context.Context) error {
xwm.X.Grab()
defer xwm.X.Ungrab()
res, err := randr.GetScreenResources(xwm.X.Conn(), xwm.X.RootWin()).Reply()
if err != nil {
return err
}
for _, output := range res.Outputs {
info, err := randr.GetOutputInfo(xwm.X.Conn(), output, res.ConfigTimestamp).Reply()
if err != nil {
log.Err(err).Any("output", output).Msg("fail get output info")
continue
}
monitorId, err := xproto.NewMonitorId(xwm.X.Conn())
if err != nil {
return err
}
xwm.W.Mutate(func() error {
xwm.W.Monitors = append(xwm.W.Monitors, &Monitor{
Name: string(info.Name),
Id: monitorId,
})
return nil
})
}
return nil
}
func (xwm *XWM) initRoot(ctx context.Context) error {
// important masks
evMasks := xproto.EventMaskPropertyChange |
xproto.EventMaskFocusChange |
xproto.EventMaskButtonPress |
xproto.EventMaskButtonRelease |
xproto.EventMaskStructureNotify |
xproto.EventMaskSubstructureNotify |
xproto.EventMaskSubstructureRedirect |
xproto.EventMaskPointerMotion
err := xwindow.New(xwm.X, xwm.X.RootWin()).Listen(evMasks)
if err != nil {
return fmt.Errorf("cant listen to root events: %w", err)
}
// TODO: need to add listener when root window changes size
// attach root window
if err = xwm.W.Mutate(func() error {
xwm.W.Root = xwindow.New(xwm.X, xwm.X.RootWin())
return nil
}); err != nil {
return err
}
// map requests
xevent.MapRequestFun(func(X *xgbutil.XUtil, ev xevent.MapRequestEvent) {
}).Connect(xwm.X, xwm.W.Root.Id)
captureCombos := []string{ captureCombos := []string{
"Mod4-1", "Mod4-1",
@ -35,7 +126,6 @@ func (xwm *XWM) Start(ctx context.Context) error {
"Mod2-1", "Mod2-1",
"Mod1-1", "Mod1-1",
} }
for _, combo := range captureCombos { for _, combo := range captureCombos {
err := mousebind.ButtonPressFun(func(xu *xgbutil.XUtil, event xevent.ButtonPressEvent) { err := mousebind.ButtonPressFun(func(xu *xgbutil.XUtil, event xevent.ButtonPressEvent) {
log.Trace().Str("name", event.String()).Str("mod", combo).Msg("press") log.Trace().Str("name", event.String()).Str("mod", combo).Msg("press")
@ -50,6 +140,15 @@ func (xwm *XWM) Start(ctx context.Context) error {
return err return err
} }
} }
xevent.ConfigureNotifyFun(func(xu *xgbutil.XUtil, event xevent.ConfigureNotifyEvent) {
log.Trace().Str("name", event.String()).Msg("notify event")
}).Connect(xwm.X, xwm.X.RootWin())
return nil
}
func (xwm *XWM) initExistingWindows(ctx context.Context) error {
tree, err := xproto.QueryTree(xwm.X.Conn(), xwm.X.RootWin()).Reply() tree, err := xproto.QueryTree(xwm.X.Conn(), xwm.X.RootWin()).Reply()
if err != nil { if err != nil {
return err return err
@ -65,24 +164,12 @@ func (xwm *XWM) Start(ctx context.Context) error {
if attrs.MapState == xproto.MapStateUnmapped { if attrs.MapState == xproto.MapStateUnmapped {
continue continue
} }
c := xwm.RegisterClient(v)
log.Trace(). if c != nil {
Uint16("class", attrs.Class). log.Printf("%+v", c)
Msg("found existing window") }
log.Println(attrs)
} }
xevent.ConfigureNotifyFun(func(xu *xgbutil.XUtil, event xevent.ConfigureNotifyEvent) {
log.Trace().Str("name", event.String()).Msg("notify event")
}).Connect(xwm.X, xwm.X.RootWin())
xevent.Main(xwm.X)
// for {
// err := xwm.run(ctx)
// if err != nil {
// return err
// }
// }
return nil return nil
} }

View File

@ -1,9 +1,15 @@
package bsp package bsp
import ( import (
"bytes"
"encoding/binary"
"encoding/hex"
"fmt" "fmt"
"sync" "sync"
"github.com/jezek/xgb/randr"
"github.com/jezek/xgb/xproto"
"github.com/jezek/xgbutil/xwindow"
"tuxpa.in/t/wm/src/bsp/cfg" "tuxpa.in/t/wm/src/bsp/cfg"
) )
@ -13,6 +19,10 @@ type WM struct {
Cfg *cfg.Modifier[*Config] Cfg *cfg.Modifier[*Config]
Root *xwindow.Window
Clients map[xproto.Window]*Client
mu sync.RWMutex mu sync.RWMutex
} }
@ -25,10 +35,19 @@ type Desktop struct {
type Monitor struct { type Monitor struct {
Name string Name string
Id xproto.Monitor
raw *randr.GetOutputInfoReply
Cfg *cfg.Modifier[*Config] Cfg *cfg.Modifier[*Config]
} }
func (m *Monitor) HexId() []byte {
o := bytes.NewBuffer([]byte("0x"))
binary.Write(hex.NewEncoder(o), binary.LittleEndian, m.Id)
return o.Bytes()
}
type Node struct { type Node struct {
Name string Name string
@ -53,15 +72,6 @@ func (w *WM) View(fn func() error) error {
return nil return nil
} }
func (w *WM) AddMonitor(name string) error {
return w.Mutate(func() error {
w.Monitors = append(w.Monitors, &Monitor{
Name: name,
Cfg: cfg.NewModifier(&Config{}),
})
return nil
})
}
func (w *WM) AddDesktop(name string, monitorName string) error { func (w *WM) AddDesktop(name string, monitorName string) error {
return w.Mutate(func() error { return w.Mutate(func() error {
var monitor *Monitor var monitor *Monitor
@ -84,7 +94,8 @@ func (w *WM) AddDesktop(name string, monitorName string) error {
func NewWM() *WM { func NewWM() *WM {
w := &WM{ w := &WM{
Cfg: cfg.NewModifier(&Config{}), Cfg: cfg.NewModifier(&Config{}),
Clients: make(map[xproto.Window]*Client),
} }
w.Cfg.FillDefaults() w.Cfg.FillDefaults()

View File

@ -1,6 +1,7 @@
package domains package domains
import ( import (
"tuxpa.in/a/zlog/log"
"tuxpa.in/t/wm/src/copies" "tuxpa.in/t/wm/src/copies"
"tuxpa.in/t/wm/src/sock" "tuxpa.in/t/wm/src/sock"
) )
@ -36,6 +37,7 @@ func (n *Config) Run(msg *sock.Msg) ([]byte, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
log.Trace().Str("key", n.configKey).Any("string", n.configValue).Msg("config set")
return nil, nil return nil, nil
} }

View File

@ -32,11 +32,28 @@ func (n *Query) Run(msg *sock.Msg) ([]byte, error) {
switch n.Command { switch n.Command {
case "desktops": case "desktops":
return n.desktops(msg) return n.desktops(msg)
case "monitors":
return n.monitors(msg)
default: default:
return nil, &copies.ErrTODO{} return nil, &copies.ErrTODO{}
} }
} }
func (n *Query) monitors(msg *sock.Msg) ([]byte, error) {
o := new(bytes.Buffer)
n.XWM.W.View(func() error {
for _, v := range n.XWM.W.Monitors {
if n.UseNames {
o.WriteString(v.Name)
} else {
o.Write(v.HexId())
}
o.WriteRune('\n')
}
return nil
})
return o.Bytes(), nil
}
func (n *Query) desktops(msg *sock.Msg) ([]byte, error) { func (n *Query) desktops(msg *sock.Msg) ([]byte, error) {
o := new(bytes.Buffer) o := new(bytes.Buffer)
o.WriteString("hi there") o.WriteString("hi there")

View File

@ -66,7 +66,7 @@ func (m *Msg) Reply(xs []byte) error {
if err != nil { if err != nil {
return err return err
} }
if len(xs) != 0 { if len(xs) != 0 && xs[len(xs)-1] != '\n' {
wr.Write([]byte("\n")) wr.Write([]byte("\n"))
} }
wr.Write([]byte{0}) wr.Write([]byte{0})

View File

@ -6394,6 +6394,16 @@ func VisualInfoListBytes(buf []byte, list []VisualInfo) int {
type Visualid uint32 type Visualid uint32
type Monitor uint32
func NewMonitorId(c *xgb.Conn) (Monitor, error) {
id, err := c.NewId()
if err != nil {
return 0, err
}
return Monitor(id), nil
}
type Window uint32 type Window uint32
func NewWindowId(c *xgb.Conn) (Window, error) { func NewWindowId(c *xgb.Conn) (Window, error) {