package bsp import ( "context" "fmt" "github.com/jezek/xgb/randr" "github.com/jezek/xgb/xinerama" "github.com/jezek/xgb/xproto" "github.com/jezek/xgbutil" "github.com/jezek/xgbutil/keybind" "github.com/jezek/xgbutil/mousebind" "github.com/jezek/xgbutil/xevent" "github.com/jezek/xgbutil/xwindow" "tuxpa.in/a/zlog/log" ) 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) initBinding(ctx context.Context) error { randr.Init(xwm.X.Conn()) xinerama.Init(xwm.X.Conn()) keybind.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{ "Mod4-1", "Mod3-1", "Mod2-1", "Mod1-1", } for _, combo := range captureCombos { err := mousebind.ButtonPressFun(func(xu *xgbutil.XUtil, event xevent.ButtonPressEvent) { log.Trace().Str("name", event.String()).Str("mod", combo).Msg("press") }).Connect(xwm.X, xwm.X.RootWin(), combo, false, true) if err != nil { return err } err = mousebind.ButtonReleaseFun(func(xu *xgbutil.XUtil, event xevent.ButtonReleaseEvent) { log.Trace().Str("name", event.String()).Str("mod", combo).Msg("depress") }).Connect(xwm.X, xwm.X.RootWin(), combo, false, true) if err != nil { 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() if err != nil { return err } for _, v := range tree.Children { if v == xwm.X.Dummy() { continue } attrs, err := xproto.GetWindowAttributes(xwm.X.Conn(), v).Reply() if err != nil { continue } if attrs.MapState == xproto.MapStateUnmapped { continue } c := xwm.RegisterClient(v) if c != nil { log.Printf("%+v", c) } } return nil } func (xwm *XWM) run(ctx context.Context) error { return nil }