layer.go RawHistoryFolder
1package ui
2
3import (
4 "fmt"
5 "slices"
6
7 "github.com/bnema/nefergui/internal/platform/wayland"
8)
9
10// LayerLevel is the wlr-layer-shell stacking layer. The zero value is invalid,
11// so a surface never silently lands below every window.
12type LayerLevel uint8
13
14const (
15 LayerBackground LayerLevel = iota + 1
16 LayerBottom
17 LayerTop
18 LayerOverlay
19)
20
21// Anchor is a bit set of output edges a layer surface attaches to.
22type Anchor uint8
23
24const (
25 AnchorTop Anchor = 1 << iota
26 AnchorBottom
27 AnchorLeft
28 AnchorRight
29)
30
31const anchorAll = AnchorTop | AnchorBottom | AnchorLeft | AnchorRight
32
33// KeyboardMode is the layer surface keyboard interactivity. The zero value is
34// KeyboardNone: the surface never takes keyboard focus. KeyboardOnDemand needs
35// layer-shell version 4.
36type KeyboardMode uint8
37
38const (
39 KeyboardNone KeyboardMode = iota
40 KeyboardExclusive
41 KeyboardOnDemand
42)
43
44// Rect is an integer logical-pixel rectangle in surface coordinates.
45type Rect struct{ X, Y, Width, Height int32 }
46
47// LayerConfig requests a wlr-layer-shell surface instead of an xdg toplevel.
48// Size still gives the initial logical size; an axis anchored to both opposite
49// edges is sized by the compositor and reported through OnResize and Frame.Size.
50// ExclusiveZone > 0 asks the compositor to reserve that many logical pixels
51// from an anchored edge (only for one edge, or one edge plus both perpendicular
52// edges); 0 reserves nothing and asks to avoid other surfaces' positive zones;
53// -1 ignores other exclusive zones and extends to the anchored output edges.
54type LayerConfig struct {
55 Output string // output name; empty lets the compositor choose
56 Namespace string // defaults to "nefergui"
57 Level LayerLevel
58 Anchors Anchor
59 Keyboard KeyboardMode
60 ExclusiveZone int32
61 Margin [4]int32 // top, right, bottom, left
62 // InputRects, when non-nil, restricts pointer input to these rectangles.
63 // An empty non-nil slice makes the surface fully click-through. Rects need
64 // positive width and height.
65 InputRects []Rect
66}
67
68// surfaceOptions lowers the config to platform options and validates it.
69func (c LayerConfig) surfaceOptions() (wayland.SurfaceOptions, error) {
70 l := wayland.LayerOptions{Output: c.Output, Namespace: c.Namespace, ExclusiveZone: c.ExclusiveZone, Margin: c.Margin}
71 switch c.Level {
72 case LayerBackground:
73 l.Layer = wayland.LayerBackground
74 case LayerBottom:
75 l.Layer = wayland.LayerBottom
76 case LayerTop:
77 l.Layer = wayland.LayerTop
78 case LayerOverlay:
79 l.Layer = wayland.LayerOverlay
80 default:
81 return wayland.SurfaceOptions{}, fmt.Errorf("nefergui: invalid layer level %d", c.Level)
82 }
83 if c.Anchors&^anchorAll != 0 {
84 return wayland.SurfaceOptions{}, fmt.Errorf("nefergui: invalid layer anchors %#x", uint8(c.Anchors))
85 }
86 if c.Anchors&AnchorTop != 0 {
87 l.Anchor |= wayland.AnchorTop
88 }
89 if c.Anchors&AnchorBottom != 0 {
90 l.Anchor |= wayland.AnchorBottom
91 }
92 if c.Anchors&AnchorLeft != 0 {
93 l.Anchor |= wayland.AnchorLeft
94 }
95 if c.Anchors&AnchorRight != 0 {
96 l.Anchor |= wayland.AnchorRight
97 }
98 switch c.Keyboard {
99 case KeyboardNone:
100 l.Keyboard = wayland.KeyboardNone
101 case KeyboardExclusive:
102 l.Keyboard = wayland.KeyboardExclusive
103 case KeyboardOnDemand:
104 l.Keyboard = wayland.KeyboardOnDemand
105 default:
106 return wayland.SurfaceOptions{}, fmt.Errorf("nefergui: invalid keyboard mode %d", c.Keyboard)
107 }
108 o := wayland.SurfaceOptions{Layer: &l}
109 if c.InputRects != nil {
110 o.InputRects = make([]wayland.Rect, len(c.InputRects))
111 for i, r := range c.InputRects {
112 o.InputRects[i] = wayland.Rect{X: r.X, Y: r.Y, W: r.Width, H: r.Height}
113 }
114 }
115 if err := o.Validate(); err != nil {
116 return wayland.SurfaceOptions{}, fmt.Errorf("nefergui: layer: %w", err)
117 }
118 return o, nil
119}
120
121// Layer selects a wlr-layer-shell surface for the window.
122func Layer(c LayerConfig) WindowOption {
123 c.InputRects = slices.Clone(c.InputRects)
124 return windowOption(func(w *windowConfig) { w.layer = &c })
125}