1package core
2
3import (
4 "sort"
5
6 "github.com/bnema/neferwl/internal/ports"
7)
8
9// arrangeLayers places exclusive surfaces first, then sorts the resulting scene by layer.
10func arrangeLayers(outW, outH int, layers []ports.LayerSurface) (placed []ports.SceneLayer, usable ports.Rect) {
11 full := ports.Rect{W: max(outW, 0), H: max(outH, 0)}
12 usable = full
13 for pass := 0; pass < 2; pass++ {
14 for _, s := range layers {
15 if (s.ExclusiveZone > 0) != (pass == 0) {
16 continue
17 }
18 bounds := usable
19 if s.ExclusiveZone == -1 {
20 bounds = full
21 }
22 top, right, bottom, left := int(s.Margin[0]), int(s.Margin[1]), int(s.Margin[2]), int(s.Margin[3])
23 w, h := s.Width, s.Height
24 x, y := bounds.X+(bounds.W-w)/2, bounds.Y+(bounds.H-h)/2
25 switch s.Anchor & (ports.AnchorLeft | ports.AnchorRight) {
26 case ports.AnchorLeft | ports.AnchorRight:
27 span := bounds.W - left - right
28 if w == 0 {
29 w = span
30 }
31 x = bounds.X + left + (span-w)/2
32 case ports.AnchorLeft:
33 x = bounds.X + left
34 case ports.AnchorRight:
35 x = bounds.X + bounds.W - w - right
36 }
37 switch s.Anchor & (ports.AnchorTop | ports.AnchorBottom) {
38 case ports.AnchorTop | ports.AnchorBottom:
39 span := bounds.H - top - bottom
40 if h == 0 {
41 h = span
42 }
43 y = bounds.Y + top + (span-h)/2
44 case ports.AnchorTop:
45 y = bounds.Y + top
46 case ports.AnchorBottom:
47 y = bounds.Y + bounds.H - h - bottom
48 }
49 placed = append(placed, ports.SceneLayer{ID: s.ID, Layer: s.Layer, Rect: ports.Rect{X: x, Y: y, W: w, H: h}})
50 if s.ExclusiveZone <= 0 {
51 continue
52 }
53 a := s.Anchor
54 z := int(s.ExclusiveZone)
55 switch {
56 case a&ports.AnchorTop != 0 && a&ports.AnchorBottom == 0 && (a&(ports.AnchorLeft|ports.AnchorRight) == 0 || a&(ports.AnchorLeft|ports.AnchorRight) == ports.AnchorLeft|ports.AnchorRight):
57 z = min(max(z+top, 0), usable.H)
58 usable.Y += z
59 usable.H -= z
60 case a&ports.AnchorBottom != 0 && a&ports.AnchorTop == 0 && (a&(ports.AnchorLeft|ports.AnchorRight) == 0 || a&(ports.AnchorLeft|ports.AnchorRight) == ports.AnchorLeft|ports.AnchorRight):
61 z = min(max(z+bottom, 0), usable.H)
62 usable.H -= z
63 case a&ports.AnchorLeft != 0 && a&ports.AnchorRight == 0 && (a&(ports.AnchorTop|ports.AnchorBottom) == 0 || a&(ports.AnchorTop|ports.AnchorBottom) == ports.AnchorTop|ports.AnchorBottom):
64 z = min(max(z+left, 0), usable.W)
65 usable.X += z
66 usable.W -= z
67 case a&ports.AnchorRight != 0 && a&ports.AnchorLeft == 0 && (a&(ports.AnchorTop|ports.AnchorBottom) == 0 || a&(ports.AnchorTop|ports.AnchorBottom) == ports.AnchorTop|ports.AnchorBottom):
68 z = min(max(z+right, 0), usable.W)
69 usable.W -= z
70 }
71 }
72 }
73 sort.SliceStable(placed, func(i, j int) bool { return placed[i].Layer < placed[j].Layer })
74 return
75}
76
77// layerOf finds a placed layer surface by ID.
78func (c *Core) layerOf(id WindowID) (*screen, ports.SceneLayer, bool) {
79 for _, sc := range c.screens {
80 for _, l := range sc.placed {
81 if l.ID == id {
82 return sc, l, true
83 }
84 }
85 }
86 return nil, ports.SceneLayer{}, false
87}
88
89// hasFullscreen reports whether a visible fullscreen window covers the screen.
90func hasFullscreen(sc *screen) bool {
91 for _, p := range sc.mon.Layout() {
92 if p.Fullscreen && !p.Hidden {
93 return true
94 }
95 }
96 return false
97}
98
99// shown reports whether a layer is drawn: a fullscreen window hides the
100// bottom and top layers, as the renderer does.
101func shown(sc *screen, layer ports.Layer) bool {
102 return !(hasFullscreen(sc) && (layer == ports.LayerBottom || layer == ports.LayerTop))
103}
104
105// layerAt returns the topmost shown layer surface under the output-local
106// point among those above (or below) the windows.
107func layerAt(sc *screen, lx, ly float64, above bool) (WindowID, float64, float64) {
108 full := hasFullscreen(sc)
109 for i := len(sc.placed) - 1; i >= 0; i-- {
110 l := sc.placed[i]
111 r := l.Rect
112 if (l.Layer >= ports.LayerTop) != above || (full && (l.Layer == ports.LayerBottom || l.Layer == ports.LayerTop)) || r.W <= 0 || r.H <= 0 {
113 continue
114 }
115 if lx >= float64(r.X) && lx < float64(r.X+r.W) && ly >= float64(r.Y) && ly < float64(r.Y+r.H) {
116 return l.ID, lx - float64(r.X), ly - float64(r.Y)
117 }
118 }
119 return 0, 0, 0
120}
121
122// onDemand reports whether id is a mapped layer surface with on-demand
123// keyboard interactivity.
124func (c *Core) onDemand(id WindowID) bool {
125 for _, sc := range c.screens {
126 for _, l := range sc.layers {
127 if l.ID == id {
128 return l.Keyboard == 2
129 }
130 }
131 }
132 return false
133}