package core import ( "sort" "github.com/bnema/neferwl/internal/ports" ) // arrangeLayers places exclusive surfaces first, then sorts the resulting scene by layer. func arrangeLayers(outW, outH int, layers []ports.LayerSurface) (placed []ports.SceneLayer, usable ports.Rect) { full := ports.Rect{W: max(outW, 0), H: max(outH, 0)} usable = full for pass := 0; pass < 2; pass++ { for _, s := range layers { if (s.ExclusiveZone > 0) != (pass == 0) { continue } bounds := usable if s.ExclusiveZone == -1 { bounds = full } top, right, bottom, left := int(s.Margin[0]), int(s.Margin[1]), int(s.Margin[2]), int(s.Margin[3]) w, h := s.Width, s.Height x, y := bounds.X+(bounds.W-w)/2, bounds.Y+(bounds.H-h)/2 switch s.Anchor & (ports.AnchorLeft | ports.AnchorRight) { case ports.AnchorLeft | ports.AnchorRight: span := bounds.W - left - right if w == 0 { w = span } x = bounds.X + left + (span-w)/2 case ports.AnchorLeft: x = bounds.X + left case ports.AnchorRight: x = bounds.X + bounds.W - w - right } switch s.Anchor & (ports.AnchorTop | ports.AnchorBottom) { case ports.AnchorTop | ports.AnchorBottom: span := bounds.H - top - bottom if h == 0 { h = span } y = bounds.Y + top + (span-h)/2 case ports.AnchorTop: y = bounds.Y + top case ports.AnchorBottom: y = bounds.Y + bounds.H - h - bottom } placed = append(placed, ports.SceneLayer{ID: s.ID, Layer: s.Layer, Rect: ports.Rect{X: x, Y: y, W: w, H: h}}) if s.ExclusiveZone <= 0 { continue } a := s.Anchor z := int(s.ExclusiveZone) switch { case a&ports.AnchorTop != 0 && a&ports.AnchorBottom == 0 && (a&(ports.AnchorLeft|ports.AnchorRight) == 0 || a&(ports.AnchorLeft|ports.AnchorRight) == ports.AnchorLeft|ports.AnchorRight): z = min(max(z+top, 0), usable.H) usable.Y += z usable.H -= z case a&ports.AnchorBottom != 0 && a&ports.AnchorTop == 0 && (a&(ports.AnchorLeft|ports.AnchorRight) == 0 || a&(ports.AnchorLeft|ports.AnchorRight) == ports.AnchorLeft|ports.AnchorRight): z = min(max(z+bottom, 0), usable.H) usable.H -= z case a&ports.AnchorLeft != 0 && a&ports.AnchorRight == 0 && (a&(ports.AnchorTop|ports.AnchorBottom) == 0 || a&(ports.AnchorTop|ports.AnchorBottom) == ports.AnchorTop|ports.AnchorBottom): z = min(max(z+left, 0), usable.W) usable.X += z usable.W -= z case a&ports.AnchorRight != 0 && a&ports.AnchorLeft == 0 && (a&(ports.AnchorTop|ports.AnchorBottom) == 0 || a&(ports.AnchorTop|ports.AnchorBottom) == ports.AnchorTop|ports.AnchorBottom): z = min(max(z+right, 0), usable.W) usable.W -= z } } } sort.SliceStable(placed, func(i, j int) bool { return placed[i].Layer < placed[j].Layer }) return } // layerOf finds a placed layer surface by ID. func (c *Core) layerOf(id WindowID) (*screen, ports.SceneLayer, bool) { for _, sc := range c.screens { for _, l := range sc.placed { if l.ID == id { return sc, l, true } } } return nil, ports.SceneLayer{}, false } // hasFullscreen reports whether a fullscreen window covers the screen. In // scroll overflow, one scrolled off by the user no longer does; nor does // one shown as a preview (the overview or the column switcher). func hasFullscreen(sc *screen) bool { return !sc.mon.previewing() && sc.mon.Current().cover() != 0 } // shown reports whether a layer surface is drawn. A fullscreen window is // exclusive: it hides every layer above the background (bars, // notifications), so the output can scan it out. A surface taking the // keyboard exclusively (a locker, a launcher) still shows: it has the // keyboard and must be seen. func shown(sc *screen, l ports.SceneLayer) bool { return l.Layer == ports.LayerBackground || !hasFullscreen(sc) || sc.exclusiveKeyboard(l.ID) || sc.capture[l.ID] } // exclusiveKeyboard reports whether layer surface id takes the keyboard // exclusively. func (sc *screen) exclusiveKeyboard(id WindowID) bool { for _, l := range sc.layers { if l.ID == id { return l.Keyboard == 1 } } return false } // shownLayers are the placed layer surfaces drawn on the screen. func shownLayers(sc *screen) []ports.SceneLayer { out := make([]ports.SceneLayer, 0, len(sc.placed)) for _, l := range sc.placed { if shown(sc, l) { out = append(out, l) } } return out } // layerAt returns the topmost shown layer surface under the output-local // point among those above (or below) the windows. func (c *Core) layerAt(sc *screen, lx, ly float64, above bool) (WindowID, float64, float64) { for i := len(sc.placed) - 1; i >= 0; i-- { l := sc.placed[i] r := l.Rect if (l.Layer >= ports.LayerTop) != above || !shown(sc, l) || r.W <= 0 || r.H <= 0 { continue } if lx >= float64(r.X) && lx < float64(r.X+r.W) && ly >= float64(r.Y) && ly < float64(r.Y+r.H) && c.acceptsInput(l.ID, lx-float64(r.X), ly-float64(r.Y)) { return l.ID, lx - float64(r.X), ly - float64(r.Y) } } return 0, 0, 0 } // onDemand reports whether id is a mapped layer surface with on-demand // keyboard interactivity. func (c *Core) onDemand(id WindowID) bool { for _, sc := range c.screens { for _, l := range sc.layers { if l.ID == id { return l.Keyboard == 2 } } } return false }