Preserve terminal feedback while coalescing repaints

This commit is contained in:
Storm Dragon committed 2026-11-04 12:22:11 -05:00
1 parent 8c26aad128
commit e222d336a5
13 files changed
+321 -39

No files matched your search

+30 -8
View File
@@ -33,6 +33,12 @@ type pendingWindowTitle struct {
inputSequence uint64
}
type windowTitleRepeatKey struct {
window atspi.ObjectID
nativeWindow, titleWindow uint64
text string
}
// windowTitleController is owned by the accessibility event loop. Discovery
// stays in the native adapter; this controller owns coalescing, preferences,
// and duplicate suppression across AT-SPI and X11.
@@ -47,7 +53,7 @@ type windowTitleController struct {
inputSource any
lastNative atspi.ForegroundTitle
lastTitle string
recent map[string]time.Time
recent map[windowTitleRepeatKey]time.Time
application string
pending *pendingWindowTitle
timer *time.Timer
@@ -189,8 +195,8 @@ func (titles *windowTitleController) nativeChanged() {
// Keep the repeat history: a native sampler can re-report the
// same client as a switch (new epoch or title-window id) on
// every title sample, and clearing here would defeat animated
// title suppression. Genuine window switches clear it through
// AT-SPI activation instead.
// title suppression. History is scoped to the window identity,
// so another window can still announce the same title.
if titles.logger != nil {
titles.logger.Debug("native window title switch",
"epoch", latest.Epoch, "window", latest.Title.Window,
@@ -244,9 +250,25 @@ func animatedWindowTitleKey(title string) string {
// the repeat window. A suppressed sighting refreshes its entry, so a
// running animation stays suppressed instead of breaking through once per
// window; it expires shortly after the animation stops.
func (titles *windowTitleController) repeatedWithinWindow(text string, at time.Time) bool {
key := animatedWindowTitleKey(text)
if key == "" {
func (titles *windowTitleController) repeatedWithinWindow(pending pendingWindowTitle, at time.Time) bool {
key := windowTitleRepeatKey{window: pending.window, text: animatedWindowTitleKey(pending.text)}
if key.text == "" {
return false
}
// Correlate AT-SPI and native samples through the same native client
// when available. Do not include its epoch: returning to that client
// should retain its animation history.
native := pending.native
if native.Window == 0 && titles.source != nil {
latest := titles.source.ForegroundTitle()
if latest.Epoch == pending.epoch {
native = latest.Title
}
}
if native.Window != 0 {
key.window = atspi.ObjectID{}
key.nativeWindow, key.titleWindow = native.Window, native.TitleWindow
} else if !key.window.Valid() {
return false
}
if at.IsZero() {
@@ -258,7 +280,7 @@ func (titles *windowTitleController) repeatedWithinWindow(text string, at time.T
}
}
if titles.recent == nil {
titles.recent = make(map[string]time.Time)
titles.recent = make(map[windowTitleRepeatKey]time.Time)
}
_, repeated := titles.recent[key]
titles.recent[key] = at
@@ -392,7 +414,7 @@ func (titles *windowTitleController) flush() {
if pending.inputSequence != recentInput(titles.inputSource).TitleInterruptionSequence {
return
}
if pending.changed && titles.repeatedWithinWindow(pending.text, pending.observedAt) {
if pending.changed && titles.repeatedWithinWindow(*pending, time.Now()) {
if titles.logger != nil {
titles.logger.Debug("suppressed repeated window title",
"key", animatedWindowTitleKey(pending.text), "recent", len(titles.recent))