detached b5cca9e

fix(bar): recreate the layer surface when the output width changes so it stays full width

commit
b5cca9e4fe16a13aa719c5036f8c11128fe2a362
author
brice · · 2026-10-05 15:15 UTC
parent
9a0b0c5
signature
GPG VerifiedCD88F9E564E0102646FB6077A1A07C7C7C3E349D · arch-gpg Verified for the committer email
1 file · +29 -5
M internal/bar/bar.go +29 -5
@@ -86,9 +86,13 @@ type Bar struct {
8686 // buffer of the wrong size, and some compositors keep that size.
8787 settling bool
8888 settleC <-chan time.Time
89- Stats Stats
90- fatal error
91- closed bool
89+
90+ // mappedW is the logical width the current layer surface was first drawn at.
91+ // It resets to 0 when the surface is recreated.
92+ mappedW int32
93+ Stats Stats
94+ fatal error
95+ closed bool
9296 }
9397
9498 // New builds a bar from a validated config.
@@ -312,6 +316,7 @@ func (b *Bar) createSurface(h int32) error {
312316 return fmt.Errorf("layer surface: %w", err)
313317 }
314318 b.surf, b.sid = surf, surf.ID()
319+ b.mappedW = 0
315320 b.configured, b.canPresent = false, false
316321 return nil
317322 }
@@ -405,7 +410,7 @@ func (b *Bar) reconcile() {
405410 }
406411 b.dev, b.node = dev, node
407412 }
408- _, h, scale := b.surf.Size()
413+ w, h, scale := b.surf.Size()
409414 px := b.cfg.Bar.Size * b.cfg.Bar.Scale * scale
410415 if b.face == nil || b.facePx != px {
411416 face, err := glyph.Load(b.fonts, px)
@@ -441,6 +446,25 @@ func (b *Bar) reconcile() {
441446 if b.rend != nil && b.rend.W == pw && b.rend.H == ph {
442447 return
443448 }
449+ if b.mappedW != 0 && w != b.mappedW {
450+ // The logical width changed (a scale change on the output). NeferWL
451+ // places a layer surface from the size it had when it was mapped and
452+ // does not re-read it when only the buffer and viewport change, so the
453+ // old width would stay: centered in a narrower output, the left part cut
454+ // off. A new surface is mapped fresh and placed from its real size.
455+ b.log.Info("recreating layer surface: the output width changed", "from", b.mappedW, "to", w)
456+ b.dropRenderer()
457+ if err := b.surf.Close(); err != nil {
458+ b.fail(err)
459+ return
460+ }
461+ if err := b.createSurface(h); err != nil {
462+ b.fail(err)
463+ return
464+ }
465+ b.Stats.Recreated++
466+ return
467+ }
444468 b.dropRenderer()
445469 if mod, err := b.chooseModifier(); err != nil {
446470 b.fail(err)
@@ -453,7 +477,7 @@ func (b *Bar) reconcile() {
453477 b.fail(err)
454478 return
455479 }
456- b.rend = rend
480+ b.rend, b.mappedW = rend, w
457481 b.Stats.Rebuilds++
458482 if b.lay == nil {
459483 b.lay = layout.New(rend.Cols(), b.fg, b.bg, b.mods)
@@ -86,9 +86,13 @@ type Bar struct {
86 // buffer of the wrong size, and some compositors keep that size.86 // buffer of the wrong size, and some compositors keep that size.
87 settling bool87 settling bool
88 settleC <-chan time.Time88 settleC <-chan time.Time
89- Stats Stats89+
90- fatal error90+ // mappedW is the logical width the current layer surface was first drawn at.
91- closed bool91+ // It resets to 0 when the surface is recreated.
92+ mappedW int32
93+ Stats Stats
94+ fatal error
95+ closed bool
92 }96 }
93 97
94 // New builds a bar from a validated config.98 // New builds a bar from a validated config.
@@ -312,6 +316,7 @@ func (b *Bar) createSurface(h int32) error {
312 return fmt.Errorf("layer surface: %w", err)316 return fmt.Errorf("layer surface: %w", err)
313 }317 }
314 b.surf, b.sid = surf, surf.ID()318 b.surf, b.sid = surf, surf.ID()
319+ b.mappedW = 0
315 b.configured, b.canPresent = false, false320 b.configured, b.canPresent = false, false
316 return nil321 return nil
317 }322 }
@@ -405,7 +410,7 @@ func (b *Bar) reconcile() {
405 }410 }
406 b.dev, b.node = dev, node411 b.dev, b.node = dev, node
407 }412 }
408- _, h, scale := b.surf.Size()413+ w, h, scale := b.surf.Size()
409 px := b.cfg.Bar.Size * b.cfg.Bar.Scale * scale414 px := b.cfg.Bar.Size * b.cfg.Bar.Scale * scale
410 if b.face == nil || b.facePx != px {415 if b.face == nil || b.facePx != px {
411 face, err := glyph.Load(b.fonts, px)416 face, err := glyph.Load(b.fonts, px)
@@ -441,6 +446,25 @@ func (b *Bar) reconcile() {
441 if b.rend != nil && b.rend.W == pw && b.rend.H == ph {446 if b.rend != nil && b.rend.W == pw && b.rend.H == ph {
442 return447 return
443 }448 }
449+ if b.mappedW != 0 && w != b.mappedW {
450+ // The logical width changed (a scale change on the output). NeferWL
451+ // places a layer surface from the size it had when it was mapped and
452+ // does not re-read it when only the buffer and viewport change, so the
453+ // old width would stay: centered in a narrower output, the left part cut
454+ // off. A new surface is mapped fresh and placed from its real size.
455+ b.log.Info("recreating layer surface: the output width changed", "from", b.mappedW, "to", w)
456+ b.dropRenderer()
457+ if err := b.surf.Close(); err != nil {
458+ b.fail(err)
459+ return
460+ }
461+ if err := b.createSurface(h); err != nil {
462+ b.fail(err)
463+ return
464+ }
465+ b.Stats.Recreated++
466+ return
467+ }
444 b.dropRenderer()468 b.dropRenderer()
445 if mod, err := b.chooseModifier(); err != nil {469 if mod, err := b.chooseModifier(); err != nil {
446 b.fail(err)470 b.fail(err)
@@ -453,7 +477,7 @@ func (b *Bar) reconcile() {
453 b.fail(err)477 b.fail(err)
454 return478 return
455 }479 }
456- b.rend = rend480+ b.rend, b.mappedW = rend, w
457 b.Stats.Rebuilds++481 b.Stats.Rebuilds++
458 if b.lay == nil {482 if b.lay == nil {
459 b.lay = layout.New(rend.Cols(), b.fg, b.bg, b.mods)483 b.lay = layout.New(rend.Cols(), b.fg, b.bg, b.mods)

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 5

Tags 4

↑↓ moveenter openesc close