perf(drm): keep a window on the overlay plane when bars and other windows do not cover it
A top layer (a bar), a popup, another window or a border line anywhere on the output used to compose the window. Only what overlaps the window's rect (or its own border lines and inset content) now keeps it composed: the rest stays on the primary plane under it.
- commit
- c0d1352da02e7886803116cb9537023afcfdc85f
- author
- brice · · 2026-10-07 21:32 UTC
- parent
- b392a0f
- signature
- GPG Verified
CD88F9E564E0102646FB6077A1A07C7C7C3E349D· arch-gpg Verified for the committer email
2 files · +68 -14
M internal/adapters/drm/overlay.go +29 -10
| @@ -57,8 +57,10 @@ type overlayWin struct { | |||
| 57 | 57 | } | |
| 58 | 58 | ||
| 59 | 59 | // overlayCandidate finds the one window the overlay can show: an opaque | |
| 60 | - | // dmabuf at integer physical coordinates, drawn 1:1, with no other window | |
| 61 | - | // or layer above it, and the plane colour mode it needs. reason is why none. | |
| 60 | + | // dmabuf at integer physical coordinates, drawn 1:1, with no other window, | |
| 61 | + | // popup, border line or layer drawn over it, and the plane colour mode it | |
| 62 | + | // needs. What is above it elsewhere on screen stays on the primary plane, | |
| 63 | + | // which the overlay covers only over the window. reason is why none. | |
| 62 | 64 | // | |
| 63 | 65 | // kept is scanoutCandidate's: a window drawn from a withdrawn content is | |
| 64 | 66 | // composed, never put on the plane. | |
| @@ -78,8 +80,9 @@ func overlayCandidate(s ports.Scene, surfaces map[ports.WindowID]ports.SurfaceCo | |||
| 78 | 80 | continue | |
| 79 | 81 | } | |
| 80 | 82 | if w.Popup { | |
| 81 | - | // Popups draw after every window, wherever they are listed. | |
| 82 | - | return ports.SceneWindow{}, ports.SurfaceContent{}, colorBypass, "window_above" | |
| 83 | + | // Popups draw after every window, wherever they are listed: | |
| 84 | + | // checked once the candidate is known. | |
| 85 | + | continue | |
| 83 | 86 | } | |
| 84 | 87 | c := surfaces[w.ID] | |
| 85 | 88 | // A viewport crop is composed: the plane would show the whole | |
| @@ -94,11 +97,18 @@ func overlayCandidate(s ports.Scene, surfaces map[ports.WindowID]ports.SurfaceCo | |||
| 94 | 97 | continue | |
| 95 | 98 | } | |
| 96 | 99 | } | |
| 97 | - | if pick != nil { | |
| 98 | - | // Something is drawn above the candidate. | |
| 100 | + | if pick != nil && w.Rect.Overlaps(pick.Rect) { | |
| 101 | + | // Something is drawn over the candidate. | |
| 99 | 102 | return ports.SceneWindow{}, ports.SurfaceContent{}, colorBypass, "window_above" | |
| 100 | 103 | } | |
| 101 | 104 | } | |
| 105 | + | if pick != nil { | |
| 106 | + | for i := range s.Windows { | |
| 107 | + | if w := &s.Windows[i]; w.Popup && !w.Hidden && w.Rect.Overlaps(pick.Rect) { | |
| 108 | + | return ports.SceneWindow{}, ports.SurfaceContent{}, colorBypass, "window_above" | |
| 109 | + | } | |
| 110 | + | } | |
| 111 | + | } | |
| 102 | 112 | if pick == nil { | |
| 103 | 113 | if kept != nil && len(s.Windows) > 0 { | |
| 104 | 114 | // The one window that could have gone on the plane fades out | |
| @@ -121,13 +131,22 @@ func overlayCandidate(s ports.Scene, surfaces map[ports.WindowID]ports.SurfaceCo | |||
| 121 | 131 | return ports.SceneWindow{}, ports.SurfaceContent{}, colorBypass, "drop_hint" | |
| 122 | 132 | } | |
| 123 | 133 | for _, l := range s.Layers { | |
| 124 | - | if l.Layer >= ports.LayerTop && l.Rect.W > 0 && l.Rect.H > 0 { | |
| 134 | + | if l.Layer >= ports.LayerTop && l.Rect.Overlaps(pick.Rect) { | |
| 125 | 135 | return ports.SceneWindow{}, ports.SurfaceContent{}, colorBypass, "layer_above" | |
| 126 | 136 | } | |
| 127 | 137 | } | |
| 128 | - | if !pick.Fullscreen && len(s.Separators) > 0 { | |
| 129 | - | // The overlay shows the buffer alone; the lines would go with it. | |
| 130 | - | return ports.SceneWindow{}, ports.SurfaceContent{}, colorBypass, "border" | |
| 138 | + | if !pick.Fullscreen { | |
| 139 | + | if pick.Inset != 0 && s.Border.Width > 0 { | |
| 140 | + | // The content sits inside the lines, not at the window's rect. | |
| 141 | + | return ports.SceneWindow{}, ports.SurfaceContent{}, colorBypass, "border" | |
| 142 | + | } | |
| 143 | + | for _, sep := range s.Separators { | |
| 144 | + | if sep.Window == pick.ID || sep.Rect.Overlaps(pick.Rect) { | |
| 145 | + | // The overlay shows the buffer alone; the lines would go | |
| 146 | + | // under it. | |
| 147 | + | return ports.SceneWindow{}, ports.SurfaceContent{}, colorBypass, "border" | |
| 148 | + | } | |
| 149 | + | } | |
| 131 | 150 | } | |
| 132 | 151 | if clip := s.WorkspaceClip; clip != (ports.Rect{}) && | |
| 133 | 152 | (pick.Rect.X < clip.X || pick.Rect.Y < clip.Y || pick.Rect.X+pick.Rect.W > clip.X+clip.W || pick.Rect.Y+pick.Rect.H > clip.Y+clip.H) { | |
| @@ -57,8 +57,10 @@ type overlayWin struct { | |||||
| 57 | } | 57 | } | ||
| 58 | 58 | ||||
| 59 | // overlayCandidate finds the one window the overlay can show: an opaque | 59 | // overlayCandidate finds the one window the overlay can show: an opaque | ||
| 60 | - | // dmabuf at integer physical coordinates, drawn 1:1, with no other window | 60 | + | // dmabuf at integer physical coordinates, drawn 1:1, with no other window, |
| 61 | - | // or layer above it, and the plane colour mode it needs. reason is why none. | 61 | + | // popup, border line or layer drawn over it, and the plane colour mode it |
| 62 | + | // needs. What is above it elsewhere on screen stays on the primary plane, | |||
| 63 | + | // which the overlay covers only over the window. reason is why none. | |||
| 62 | // | 64 | // | ||
| 63 | // kept is scanoutCandidate's: a window drawn from a withdrawn content is | 65 | // kept is scanoutCandidate's: a window drawn from a withdrawn content is | ||
| 64 | // composed, never put on the plane. | 66 | // composed, never put on the plane. | ||
| @@ -78,8 +80,9 @@ func overlayCandidate(s ports.Scene, surfaces map[ports.WindowID]ports.SurfaceCo | |||||
| 78 | continue | 80 | continue | ||
| 79 | } | 81 | } | ||
| 80 | if w.Popup { | 82 | if w.Popup { | ||
| 81 | - | // Popups draw after every window, wherever they are listed. | 83 | + | // Popups draw after every window, wherever they are listed: |
| 82 | - | return ports.SceneWindow{}, ports.SurfaceContent{}, colorBypass, "window_above" | 84 | + | // checked once the candidate is known. |
| 85 | + | continue | |||
| 83 | } | 86 | } | ||
| 84 | c := surfaces[w.ID] | 87 | c := surfaces[w.ID] | ||
| 85 | // A viewport crop is composed: the plane would show the whole | 88 | // A viewport crop is composed: the plane would show the whole | ||
| @@ -94,11 +97,18 @@ func overlayCandidate(s ports.Scene, surfaces map[ports.WindowID]ports.SurfaceCo | |||||
| 94 | continue | 97 | continue | ||
| 95 | } | 98 | } | ||
| 96 | } | 99 | } | ||
| 97 | - | if pick != nil { | 100 | + | if pick != nil && w.Rect.Overlaps(pick.Rect) { |
| 98 | - | // Something is drawn above the candidate. | 101 | + | // Something is drawn over the candidate. |
| 99 | return ports.SceneWindow{}, ports.SurfaceContent{}, colorBypass, "window_above" | 102 | return ports.SceneWindow{}, ports.SurfaceContent{}, colorBypass, "window_above" | ||
| 100 | } | 103 | } | ||
| 101 | } | 104 | } | ||
| 105 | + | if pick != nil { | |||
| 106 | + | for i := range s.Windows { | |||
| 107 | + | if w := &s.Windows[i]; w.Popup && !w.Hidden && w.Rect.Overlaps(pick.Rect) { | |||
| 108 | + | return ports.SceneWindow{}, ports.SurfaceContent{}, colorBypass, "window_above" | |||
| 109 | + | } | |||
| 110 | + | } | |||
| 111 | + | } | |||
| 102 | if pick == nil { | 112 | if pick == nil { | ||
| 103 | if kept != nil && len(s.Windows) > 0 { | 113 | if kept != nil && len(s.Windows) > 0 { | ||
| 104 | // The one window that could have gone on the plane fades out | 114 | // The one window that could have gone on the plane fades out | ||
| @@ -121,13 +131,22 @@ func overlayCandidate(s ports.Scene, surfaces map[ports.WindowID]ports.SurfaceCo | |||||
| 121 | return ports.SceneWindow{}, ports.SurfaceContent{}, colorBypass, "drop_hint" | 131 | return ports.SceneWindow{}, ports.SurfaceContent{}, colorBypass, "drop_hint" | ||
| 122 | } | 132 | } | ||
| 123 | for _, l := range s.Layers { | 133 | for _, l := range s.Layers { | ||
| 124 | - | if l.Layer >= ports.LayerTop && l.Rect.W > 0 && l.Rect.H > 0 { | 134 | + | if l.Layer >= ports.LayerTop && l.Rect.Overlaps(pick.Rect) { |
| 125 | return ports.SceneWindow{}, ports.SurfaceContent{}, colorBypass, "layer_above" | 135 | return ports.SceneWindow{}, ports.SurfaceContent{}, colorBypass, "layer_above" | ||
| 126 | } | 136 | } | ||
| 127 | } | 137 | } | ||
| 128 | - | if !pick.Fullscreen && len(s.Separators) > 0 { | 138 | + | if !pick.Fullscreen { |
| 129 | - | // The overlay shows the buffer alone; the lines would go with it. | 139 | + | if pick.Inset != 0 && s.Border.Width > 0 { |
| 130 | - | return ports.SceneWindow{}, ports.SurfaceContent{}, colorBypass, "border" | 140 | + | // The content sits inside the lines, not at the window's rect. |
| 141 | + | return ports.SceneWindow{}, ports.SurfaceContent{}, colorBypass, "border" | |||
| 142 | + | } | |||
| 143 | + | for _, sep := range s.Separators { | |||
| 144 | + | if sep.Window == pick.ID || sep.Rect.Overlaps(pick.Rect) { | |||
| 145 | + | // The overlay shows the buffer alone; the lines would go | |||
| 146 | + | // under it. | |||
| 147 | + | return ports.SceneWindow{}, ports.SurfaceContent{}, colorBypass, "border" | |||
| 148 | + | } | |||
| 149 | + | } | |||
| 131 | } | 150 | } | ||
| 132 | if clip := s.WorkspaceClip; clip != (ports.Rect{}) && | 151 | if clip := s.WorkspaceClip; clip != (ports.Rect{}) && | ||
| 133 | (pick.Rect.X < clip.X || pick.Rect.Y < clip.Y || pick.Rect.X+pick.Rect.W > clip.X+clip.W || pick.Rect.Y+pick.Rect.H > clip.Y+clip.H) { | 152 | (pick.Rect.X < clip.X || pick.Rect.Y < clip.Y || pick.Rect.X+pick.Rect.W > clip.X+clip.W || pick.Rect.Y+pick.Rect.H > clip.Y+clip.H) { | ||
M internal/adapters/drm/overlay_test.go +39 -4
| @@ -87,7 +87,7 @@ func TestOverlayCandidate(t *testing.T) { | |||
| 87 | 87 | t.Fatalf("transformed overlay reason %q", reason) | |
| 88 | 88 | } | |
| 89 | 89 | _, c = overlayScene() | |
| 90 | - | s.Windows = append(s.Windows, ports.SceneWindow{ID: 3, Rect: ports.Rect{W: 10, H: 10}}) | |
| 90 | + | s.Windows = append(s.Windows, ports.SceneWindow{ID: 3, Rect: ports.Rect{X: 150, W: 10, H: 10}}) | |
| 91 | 91 | if _, _, _, reason := overlayCandidate(s, c, false, nil, nil); reason != "window_above" { | |
| 92 | 92 | t.Fatalf("reason %q", reason) | |
| 93 | 93 | } | |
| @@ -97,7 +97,7 @@ func TestOverlayCandidate(t *testing.T) { | |||
| 97 | 97 | t.Fatalf("reason %q", reason) | |
| 98 | 98 | } | |
| 99 | 99 | s, _ = overlayScene() | |
| 100 | - | s.Layers = []ports.SceneLayer{{ID: 5, Layer: ports.LayerOverlay, Rect: ports.Rect{W: 5, H: 5}}} | |
| 100 | + | s.Layers = []ports.SceneLayer{{ID: 5, Layer: ports.LayerOverlay, Rect: ports.Rect{X: 150, W: 5, H: 5}}} | |
| 101 | 101 | if _, _, _, reason := overlayCandidate(s, c, false, nil, nil); reason != "layer_above" { | |
| 102 | 102 | t.Fatalf("reason %q", reason) | |
| 103 | 103 | } | |
| @@ -223,15 +223,50 @@ func TestOverlayTestRefusedFallsBack(t *testing.T) { | |||
| 223 | 223 | // overlay; a bordered window does too. | |
| 224 | 224 | func TestOverlayPopupAndBorder(t *testing.T) { | |
| 225 | 225 | s, c := overlayScene() | |
| 226 | - | s.Windows = append([]ports.SceneWindow{{ID: 7, Popup: true, Rect: ports.Rect{W: 5, H: 5}}}, s.Windows...) | |
| 226 | + | s.Windows = append([]ports.SceneWindow{{ID: 7, Popup: true, Rect: ports.Rect{X: 120, W: 5, H: 5}}}, s.Windows...) | |
| 227 | 227 | if _, _, _, reason := overlayCandidate(s, c, false, nil, nil); reason != "window_above" { | |
| 228 | 228 | t.Fatalf("popup: %q", reason) | |
| 229 | 229 | } | |
| 230 | 230 | s, _ = overlayScene() | |
| 231 | - | s.Separators = []ports.Separator{{Rect: ports.Rect{W: 2, H: 2}}} | |
| 231 | + | s.Separators = []ports.Separator{{Rect: ports.Rect{X: 100, W: 2, H: 100}}} | |
| 232 | 232 | if _, _, _, reason := overlayCandidate(s, c, false, nil, nil); reason != "border" { | |
| 233 | 233 | t.Fatalf("border: %q", reason) | |
| 234 | 234 | } | |
| 235 | + | s, _ = overlayScene() | |
| 236 | + | s.Separators = []ports.Separator{{Rect: ports.Rect{X: 300, W: 2, H: 2}, Window: 2}} | |
| 237 | + | if _, _, _, reason := overlayCandidate(s, c, false, nil, nil); reason != "border" { | |
| 238 | + | t.Fatalf("own border: %q", reason) | |
| 239 | + | } | |
| 240 | + | s, _ = overlayScene() | |
| 241 | + | s.Border.Width = 2 | |
| 242 | + | s.Windows[1].Inset = ports.SideLeft | |
| 243 | + | if _, _, _, reason := overlayCandidate(s, c, false, nil, nil); reason != "border" { | |
| 244 | + | t.Fatalf("inset content: %q", reason) | |
| 245 | + | } | |
| 246 | + | } | |
| 247 | + | ||
| 248 | + | // What is drawn elsewhere on screen stays composed on the primary plane: a | |
| 249 | + | // bar, a popup, another window's lines or a window that do not touch the | |
| 250 | + | // candidate leave it on the overlay. | |
| 251 | + | func TestOverlayIgnoresWhatDoesNotCoverIt(t *testing.T) { | |
| 252 | + | s, c := overlayScene() | |
| 253 | + | s.Layers = []ports.SceneLayer{{ID: 5, Layer: ports.LayerTop, Rect: ports.Rect{W: 100, H: 10}}} | |
| 254 | + | s.Windows = append(s.Windows, | |
| 255 | + | ports.SceneWindow{ID: 7, Popup: true, Rect: ports.Rect{X: 10, Y: 10, W: 20, H: 20}}, | |
| 256 | + | ports.SceneWindow{ID: 8, Floating: true, Rect: ports.Rect{X: 40, Y: 40, W: 20, H: 20}}) | |
| 257 | + | s.Separators = []ports.Separator{{Rect: ports.Rect{X: 98, W: 2, H: 100}, Window: 1}} | |
| 258 | + | if w, _, _, reason := overlayCandidate(s, c, false, nil, nil); reason != "" || w.ID != 2 { | |
| 259 | + | t.Fatalf("candidate %v %q", w.ID, reason) | |
| 260 | + | } | |
| 261 | + | // Touching edges do not cover it. | |
| 262 | + | s.Layers[0].Rect = ports.Rect{W: 100, H: 100} | |
| 263 | + | if _, _, _, reason := overlayCandidate(s, c, false, nil, nil); reason != "" { | |
| 264 | + | t.Fatalf("adjacent layer: %q", reason) | |
| 265 | + | } | |
| 266 | + | s.Layers[0].Rect = ports.Rect{W: 101, H: 10} | |
| 267 | + | if _, _, _, reason := overlayCandidate(s, c, false, nil, nil); reason != "layer_above" { | |
| 268 | + | t.Fatalf("overlapping layer: %q", reason) | |
| 269 | + | } | |
| 235 | 270 | } | |
| 236 | 271 | ||
| 237 | 272 | // A cursor commit refused with the overlay on drops the overlay: the | |
| @@ -87,7 +87,7 @@ func TestOverlayCandidate(t *testing.T) { | |||||
| 87 | t.Fatalf("transformed overlay reason %q", reason) | 87 | t.Fatalf("transformed overlay reason %q", reason) | ||
| 88 | } | 88 | } | ||
| 89 | _, c = overlayScene() | 89 | _, c = overlayScene() | ||
| 90 | - | s.Windows = append(s.Windows, ports.SceneWindow{ID: 3, Rect: ports.Rect{W: 10, H: 10}}) | 90 | + | s.Windows = append(s.Windows, ports.SceneWindow{ID: 3, Rect: ports.Rect{X: 150, W: 10, H: 10}}) |
| 91 | if _, _, _, reason := overlayCandidate(s, c, false, nil, nil); reason != "window_above" { | 91 | if _, _, _, reason := overlayCandidate(s, c, false, nil, nil); reason != "window_above" { | ||
| 92 | t.Fatalf("reason %q", reason) | 92 | t.Fatalf("reason %q", reason) | ||
| 93 | } | 93 | } | ||
| @@ -97,7 +97,7 @@ func TestOverlayCandidate(t *testing.T) { | |||||
| 97 | t.Fatalf("reason %q", reason) | 97 | t.Fatalf("reason %q", reason) | ||
| 98 | } | 98 | } | ||
| 99 | s, _ = overlayScene() | 99 | s, _ = overlayScene() | ||
| 100 | - | s.Layers = []ports.SceneLayer{{ID: 5, Layer: ports.LayerOverlay, Rect: ports.Rect{W: 5, H: 5}}} | 100 | + | s.Layers = []ports.SceneLayer{{ID: 5, Layer: ports.LayerOverlay, Rect: ports.Rect{X: 150, W: 5, H: 5}}} |
| 101 | if _, _, _, reason := overlayCandidate(s, c, false, nil, nil); reason != "layer_above" { | 101 | if _, _, _, reason := overlayCandidate(s, c, false, nil, nil); reason != "layer_above" { | ||
| 102 | t.Fatalf("reason %q", reason) | 102 | t.Fatalf("reason %q", reason) | ||
| 103 | } | 103 | } | ||
| @@ -223,15 +223,50 @@ func TestOverlayTestRefusedFallsBack(t *testing.T) { | |||||
| 223 | // overlay; a bordered window does too. | 223 | // overlay; a bordered window does too. | ||
| 224 | func TestOverlayPopupAndBorder(t *testing.T) { | 224 | func TestOverlayPopupAndBorder(t *testing.T) { | ||
| 225 | s, c := overlayScene() | 225 | s, c := overlayScene() | ||
| 226 | - | s.Windows = append([]ports.SceneWindow{{ID: 7, Popup: true, Rect: ports.Rect{W: 5, H: 5}}}, s.Windows...) | 226 | + | s.Windows = append([]ports.SceneWindow{{ID: 7, Popup: true, Rect: ports.Rect{X: 120, W: 5, H: 5}}}, s.Windows...) |
| 227 | if _, _, _, reason := overlayCandidate(s, c, false, nil, nil); reason != "window_above" { | 227 | if _, _, _, reason := overlayCandidate(s, c, false, nil, nil); reason != "window_above" { | ||
| 228 | t.Fatalf("popup: %q", reason) | 228 | t.Fatalf("popup: %q", reason) | ||
| 229 | } | 229 | } | ||
| 230 | s, _ = overlayScene() | 230 | s, _ = overlayScene() | ||
| 231 | - | s.Separators = []ports.Separator{{Rect: ports.Rect{W: 2, H: 2}}} | 231 | + | s.Separators = []ports.Separator{{Rect: ports.Rect{X: 100, W: 2, H: 100}}} |
| 232 | if _, _, _, reason := overlayCandidate(s, c, false, nil, nil); reason != "border" { | 232 | if _, _, _, reason := overlayCandidate(s, c, false, nil, nil); reason != "border" { | ||
| 233 | t.Fatalf("border: %q", reason) | 233 | t.Fatalf("border: %q", reason) | ||
| 234 | } | 234 | } | ||
| 235 | + | s, _ = overlayScene() | |||
| 236 | + | s.Separators = []ports.Separator{{Rect: ports.Rect{X: 300, W: 2, H: 2}, Window: 2}} | |||
| 237 | + | if _, _, _, reason := overlayCandidate(s, c, false, nil, nil); reason != "border" { | |||
| 238 | + | t.Fatalf("own border: %q", reason) | |||
| 239 | + | } | |||
| 240 | + | s, _ = overlayScene() | |||
| 241 | + | s.Border.Width = 2 | |||
| 242 | + | s.Windows[1].Inset = ports.SideLeft | |||
| 243 | + | if _, _, _, reason := overlayCandidate(s, c, false, nil, nil); reason != "border" { | |||
| 244 | + | t.Fatalf("inset content: %q", reason) | |||
| 245 | + | } | |||
| 246 | + | } | |||
| 247 | + | ||||
| 248 | + | // What is drawn elsewhere on screen stays composed on the primary plane: a | |||
| 249 | + | // bar, a popup, another window's lines or a window that do not touch the | |||
| 250 | + | // candidate leave it on the overlay. | |||
| 251 | + | func TestOverlayIgnoresWhatDoesNotCoverIt(t *testing.T) { | |||
| 252 | + | s, c := overlayScene() | |||
| 253 | + | s.Layers = []ports.SceneLayer{{ID: 5, Layer: ports.LayerTop, Rect: ports.Rect{W: 100, H: 10}}} | |||
| 254 | + | s.Windows = append(s.Windows, | |||
| 255 | + | ports.SceneWindow{ID: 7, Popup: true, Rect: ports.Rect{X: 10, Y: 10, W: 20, H: 20}}, | |||
| 256 | + | ports.SceneWindow{ID: 8, Floating: true, Rect: ports.Rect{X: 40, Y: 40, W: 20, H: 20}}) | |||
| 257 | + | s.Separators = []ports.Separator{{Rect: ports.Rect{X: 98, W: 2, H: 100}, Window: 1}} | |||
| 258 | + | if w, _, _, reason := overlayCandidate(s, c, false, nil, nil); reason != "" || w.ID != 2 { | |||
| 259 | + | t.Fatalf("candidate %v %q", w.ID, reason) | |||
| 260 | + | } | |||
| 261 | + | // Touching edges do not cover it. | |||
| 262 | + | s.Layers[0].Rect = ports.Rect{W: 100, H: 100} | |||
| 263 | + | if _, _, _, reason := overlayCandidate(s, c, false, nil, nil); reason != "" { | |||
| 264 | + | t.Fatalf("adjacent layer: %q", reason) | |||
| 265 | + | } | |||
| 266 | + | s.Layers[0].Rect = ports.Rect{W: 101, H: 10} | |||
| 267 | + | if _, _, _, reason := overlayCandidate(s, c, false, nil, nil); reason != "layer_above" { | |||
| 268 | + | t.Fatalf("overlapping layer: %q", reason) | |||
| 269 | + | } | |||
| 235 | } | 270 | } | ||
| 236 | 271 | ||||
| 237 | // A cursor commit refused with the overlay on drops the overlay: the | 272 | // A cursor commit refused with the overlay on drops the overlay: the | ||