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

+20 -14
View File
@@ -254,6 +254,17 @@ func (handler *accessibilityEventHandler) handle(event atspi.Event) {
if autocompleteConsumed {
return
}
flushTerminalRepaint := func() {
if state.pending.terminalRepaint.event.Type == "" {
return
}
pending, _ := state.pending.terminalRepaint.take()
if is_trivial_terminal_repaint(pending) {
logger.Debug("dropped trivial terminal repaint fragment", "role", pending.Source.Role)
return
}
speak(pending)
}
replacedDeletion := false
if state.pending.terminalDeletion.event.Type != "" && event.Type != atspi.CaretEvent {
pendingDeletion := state.pending.terminalDeletion.event
@@ -271,8 +282,9 @@ func (handler *accessibilityEventHandler) handle(event atspi.Event) {
replacedDeletion = true
state.pending.terminalDeletion.clear()
logger.Debug("coalesced terminal deletion with following insertion", "role", event.Source.Role)
} else if !(event.Type == atspi.TextDeletedEvent && sameTerminal && !userDeletion) {
// Another deletion from the same terminal is part of the same
} else if !(event.Type == atspi.TextDeletedEvent && sameTerminal && !userDeletion &&
(should_ignore_terminal_scrollback(event) || terminalReplacementOverlap(pendingDeletion, event))) {
// An overlapping deletion from the same terminal is part of the same
// repaint burst. It is handled below: scroll shifts return
// early, anything else replaces the pending deletion. Either
// way the pending deletion must not flush early, or the burst
@@ -308,6 +320,11 @@ func (handler *accessibilityEventHandler) handle(event atspi.Event) {
return
}
if event.Type == atspi.TextDeletedEvent && event.Source.Role == "terminal" {
if state.pending.terminalRepaint.event.Type != "" &&
!should_ignore_terminal_scrollback(event) &&
(is_user_terminal_deletion(event, intent) || !terminalReplacementOverlap(state.pending.terminalRepaint.event, event)) {
flushTerminalRepaint()
}
if should_ignore_terminal_scrollback(event) {
logger.Debug("skipped terminal scrollback deletion", "role", event.Source.Role)
return
@@ -323,23 +340,12 @@ func (handler *accessibilityEventHandler) handle(event atspi.Event) {
// briefly so a newer rewrite of the same region replaces it before
// either speaks. A rewrite of a different region, or appended output
// from the same terminal, first flushes the held repaint in order.
flushTerminalRepaint := func() {
if state.pending.terminalRepaint.event.Type == "" {
return
}
pending, _ := state.pending.terminalRepaint.take()
if is_trivial_terminal_repaint(pending) {
logger.Debug("dropped trivial terminal repaint fragment", "role", pending.Source.Role)
return
}
speak(pending)
}
if is_latest_wins_terminal_repaint(event, intent, replacedDeletion) {
if state.pending.terminalRepaint.event.Type != "" &&
!terminalReplacementOverlap(state.pending.terminalRepaint.event, event) {
flushTerminalRepaint()
}
state.pending.terminalRepaint.schedule(event, input.NoIntent, terminalRepaintDebounce)
state.pending.terminalRepaint.coalesce_terminal_repaint(event)
logger.Debug("queued latest-wins terminal repaint", "role", event.Source.Role)
if autocompleteRequest.Text != "" {
speech.Enqueue(autocompleteRequest)