Preserve terminal feedback while coalescing repaints
This commit is contained in:
1 parent
8c26aad128
commit
e222d336a5
13 files changed
+321
-39
No files matched your search
@@ -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))
|
||||
|
||||
Reference in new issue
Block a user