detached c0d1352

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 VerifiedCD88F9E564E0102646FB6077A1A07C7C7C3E349D · 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 {
5757 }
5858
5959 // 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.
6264 //
6365 // kept is scanoutCandidate's: a window drawn from a withdrawn content is
6466 // composed, never put on the plane.
@@ -78,8 +80,9 @@ func overlayCandidate(s ports.Scene, surfaces map[ports.WindowID]ports.SurfaceCo
7880 continue
7981 }
8082 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
8386 }
8487 c := surfaces[w.ID]
8588 // 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
9497 continue
9598 }
9699 }
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.
99102 return ports.SceneWindow{}, ports.SurfaceContent{}, colorBypass, "window_above"
100103 }
101104 }
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+ }
102112 if pick == nil {
103113 if kept != nil && len(s.Windows) > 0 {
104114 // 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
121131 return ports.SceneWindow{}, ports.SurfaceContent{}, colorBypass, "drop_hint"
122132 }
123133 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) {
125135 return ports.SceneWindow{}, ports.SurfaceContent{}, colorBypass, "layer_above"
126136 }
127137 }
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+ }
131150 }
132151 if clip := s.WorkspaceClip; clip != (ports.Rect{}) &&
133152 (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 opaque59 // overlayCandidate finds the one window the overlay can show: an opaque
60-// dmabuf at integer physical coordinates, drawn 1:1, with no other window60+// 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 is65 // 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 continue80 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 whole88 // 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 continue97 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 out114 // 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) {
8787 t.Fatalf("transformed overlay reason %q", reason)
8888 }
8989 _, 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}})
9191 if _, _, _, reason := overlayCandidate(s, c, false, nil, nil); reason != "window_above" {
9292 t.Fatalf("reason %q", reason)
9393 }
@@ -97,7 +97,7 @@ func TestOverlayCandidate(t *testing.T) {
9797 t.Fatalf("reason %q", reason)
9898 }
9999 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}}}
101101 if _, _, _, reason := overlayCandidate(s, c, false, nil, nil); reason != "layer_above" {
102102 t.Fatalf("reason %q", reason)
103103 }
@@ -223,15 +223,50 @@ func TestOverlayTestRefusedFallsBack(t *testing.T) {
223223 // overlay; a bordered window does too.
224224 func TestOverlayPopupAndBorder(t *testing.T) {
225225 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...)
227227 if _, _, _, reason := overlayCandidate(s, c, false, nil, nil); reason != "window_above" {
228228 t.Fatalf("popup: %q", reason)
229229 }
230230 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}}}
232232 if _, _, _, reason := overlayCandidate(s, c, false, nil, nil); reason != "border" {
233233 t.Fatalf("border: %q", reason)
234234 }
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+ }
235270 }
236271
237272 // 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: the272 // A cursor commit refused with the overlay on drops the overlay: the

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 25

Tags 10

↑↓ moveenter openesc close