can now query monitors
This commit is contained in:
parent
9efd1c2290
commit
65162b1651
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
|
@ -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"`
|
||||||
|
|
125
src/bsp/loop.go
125
src/bsp/loop.go
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
@ -85,6 +95,7 @@ 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()
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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")
|
||||||
|
|
|
@ -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})
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
Loading…
Reference in New Issue