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 triedViewport bool
42 triedFScale bool
43}
44
45// require binds iface at min(announced, supported) and fails with a
46// *CapabilityError when it is absent or older than minimum.
47func (c *Conn) require[P wl.Proxy](iface string, supported, minimum uint32, newProxy func(*wl.Context) P) (P, uint32, error) {
48 p, v, err := c.Bind(iface, supported, newProxy)
49 if err != nil {
50 var zero P
51 return zero, 0, &CapabilityError{Name: iface, Cause: err}
52 }
53 if v < minimum {
54 var zero P
55 return zero, 0, &CapabilityError{Name: iface, Cause: fmt.Errorf("requires version %d, negotiated %d", minimum, v)}
56 }
57 return p, v, nil
58}
59
60// ensureCore binds wl_compositor, linux-dmabuf (v4) and drm-syncobj, and the
61// optional viewporter and fractional-scale managers.
62func (c *Conn) ensureCore() error {
63 g := &c.g
64 var err error
65 if g.compositor == nil {
66 if g.compositor, _, err = c.require(wayland.CompositorInterface, 6, 4, wayland.NewCompositor); err != nil {
67 return err
68 }
69 }
70 if g.dmabuf == nil {
71 if g.dmabuf, _, err = c.require(linuxdmabuf.LinuxDmabufInterface, 4, 4, linuxdmabuf.NewLinuxDmabuf); err != nil {
72 return err
73 }
74 }
75 if g.syncobj == nil {
76 if g.syncobj, _, err = c.require(linuxdrmsyncobj.WpLinuxDrmSyncobjManagerInterface, 1, 1, linuxdrmsyncobj.NewWpLinuxDrmSyncobjManager); err != nil {
77 return err
78 }
79 }
80 if !g.triedViewport {
81 g.triedViewport = true
82 v, _, e := c.Bind(viewporter.WpViewporterInterface, 1, viewporter.NewWpViewporter)
83 if e != nil && !errors.Is(e, ErrGlobalNotFound) {
84 return e
85 }
86 g.viewporter = v
87 }
88 if !g.triedFScale {
89 g.triedFScale = true
90 v, _, e := c.Bind(fractionalscale.WpFractionalScaleManagerInterface, 1, fractionalscale.NewWpFractionalScaleManager)
91 if e != nil && !errors.Is(e, ErrGlobalNotFound) {
92 return e
93 }
94 g.fscale = v
95 }
96 return nil
97}
98
99func (c *Conn) ensureXdg() error {
100 if c.g.xdg != nil {
101 return nil
102 }
103 q := c.q
104 x, _, err := c.require(xdgshell.XdgWmBaseInterface, 6, 1, func(ctx *wl.Context) *xdgshell.XdgWmBase {
105 x := xdgshell.NewXdgWmBase(ctx)
106 x.OnPing(func(serial uint32) {
107 e := event{kind: evPing, serial: serial}
108 q.post(&e)
109 })
110 return x
111 })
112 c.g.xdg = x
113 return err
114}
115
116func (c *Conn) ensureLayerShell() error {
117 if c.g.layer != nil {
118 return nil
119 }
120 l, v, err := c.require(wlrlayershell.LayerShellInterface, 4, 1, wlrlayershell.NewLayerShell)
121 c.g.layer, c.g.layerVersion = l, v
122 return err
123}
124
125func (c *Conn) ensureLockManager() error {
126 if c.g.lockMgr != nil {
127 return nil
128 }
129 m, _, err := c.require(extsessionlock.ExtSessionLockManagerInterface, 1, 1, extsessionlock.NewExtSessionLockManager)
130 c.g.lockMgr = m
131 return err
132}