1package neferclient
2
3import (
4 "errors"
5 "fmt"
6
7 "github.com/bnema/go-wayland-bindings/client/extsessionlock"
8 "github.com/bnema/go-wayland-bindings/client/fractionalscale"
9 "github.com/bnema/go-wayland-bindings/client/linuxdmabuf"
10 "github.com/bnema/go-wayland-bindings/client/linuxdrmsyncobj"
11 "github.com/bnema/go-wayland-bindings/client/viewporter"
12 "github.com/bnema/go-wayland-bindings/client/wayland"
13 "github.com/bnema/go-wayland-bindings/client/wlrlayershell"
14 "github.com/bnema/go-wayland-bindings/client/xdgshell"
15 "github.com/bnema/wlturbo/wl"
16)
17
18// CapabilityError reports a missing or too old required protocol global.
19type CapabilityError struct {
20 Name string
21 Cause error
22}
23
24func (e *CapabilityError) Error() string {
25 return fmt.Sprintf("neferclient: capability %s: %v", e.Name, e.Cause)
26}
27func (e *CapabilityError) Unwrap() error { return e.Cause }
28
29// globals holds the protocol globals bound on first use. Owner goroutine only.
30type globals struct {
31 compositor *wayland.Compositor
32 dmabuf *linuxdmabuf.LinuxDmabuf
33 syncobj *linuxdrmsyncobj.WpLinuxDrmSyncobjManager
34 xdg *xdgshell.XdgWmBase
35 layer *wlrlayershell.LayerShell
36 lockMgr *extsessionlock.ExtSessionLockManager
37 viewporter *viewporter.WpViewporter
38 fscale *fractionalscale.WpFractionalScaleManager
39
40 layerVersion uint32
41 xdgVersion uint32
42 triedViewport bool
43 triedFScale bool
44}
45
46// require binds iface at min(announced, supported) and fails with a
47// *CapabilityError when it is absent or older than minimum.
48func (c *Conn) require[P wl.Proxy](iface string, supported, minimum uint32, newProxy func(*wl.Context) P) (P, uint32, error) {
49 p, v, err := c.Bind(iface, supported, newProxy)
50 if err != nil {
51 var zero P
52 return zero, 0, &CapabilityError{Name: iface, Cause: err}
53 }
54 if v < minimum {
55 var zero P
56 return zero, 0, &CapabilityError{Name: iface, Cause: fmt.Errorf("requires version %d, negotiated %d", minimum, v)}
57 }
58 return p, v, nil
59}
60
61// ensureCore binds wl_compositor, linux-dmabuf (v4) and drm-syncobj, and the
62// optional viewporter and fractional-scale managers.
63func (c *Conn) ensureCore() error {
64 g := &c.g
65 var err error
66 if g.compositor == nil {
67 if g.compositor, _, err = c.require(wayland.CompositorInterface, 6, 4, wayland.NewCompositor); err != nil {
68 return err
69 }
70 }
71 if g.dmabuf == nil {
72 if g.dmabuf, _, err = c.require(linuxdmabuf.LinuxDmabufInterface, 4, 4, linuxdmabuf.NewLinuxDmabuf); err != nil {
73 return err
74 }
75 }
76 if g.syncobj == nil {
77 if g.syncobj, _, err = c.require(linuxdrmsyncobj.WpLinuxDrmSyncobjManagerInterface, 1, 1, linuxdrmsyncobj.NewWpLinuxDrmSyncobjManager); err != nil {
78 return err
79 }
80 }
81 if !g.triedViewport {
82 g.triedViewport = true
83 v, _, e := c.Bind(viewporter.WpViewporterInterface, 1, viewporter.NewWpViewporter)
84 if e != nil && !errors.Is(e, ErrGlobalNotFound) {
85 return e
86 }
87 g.viewporter = v
88 }
89 if !g.triedFScale {
90 g.triedFScale = true
91 v, _, e := c.Bind(fractionalscale.WpFractionalScaleManagerInterface, 1, fractionalscale.NewWpFractionalScaleManager)
92 if e != nil && !errors.Is(e, ErrGlobalNotFound) {
93 return e
94 }
95 g.fscale = v
96 }
97 return nil
98}
99
100func (c *Conn) ensureXdg() error {
101 if c.g.xdg != nil {
102 return nil
103 }
104 q := c.q
105 x, v, err := c.require(xdgshell.XdgWmBaseInterface, 6, 1, func(ctx *wl.Context) *xdgshell.XdgWmBase {
106 x := xdgshell.NewXdgWmBase(ctx)
107 x.OnPing(func(serial uint32) {
108 e := event{kind: evPing, serial: serial}
109 q.post(&e)
110 })
111 return x
112 })
113 c.g.xdg, c.g.xdgVersion = x, v
114 return err
115}
116
117func (c *Conn) ensureLayerShell() error {
118 if c.g.layer != nil {
119 return nil
120 }
121 l, v, err := c.require(wlrlayershell.LayerShellInterface, 4, 1, wlrlayershell.NewLayerShell)
122 c.g.layer, c.g.layerVersion = l, v
123 return err
124}
125
126func (c *Conn) ensureLockManager() error {
127 if c.g.lockMgr != nil {
128 return nil
129 }
130 m, _, err := c.require(extsessionlock.ExtSessionLockManagerInterface, 1, 1, extsessionlock.NewExtSessionLockManager)
131 c.g.lockMgr = m
132 return err
133}