Graphical terminal improvements.

It makes using programs who like to refresh a ton much more tollerable
when I'm not in Emacs; I suspect others will like them too.
This commit is contained in:
hjozwiak committed 2026-11-04 04:05:19 -05:00
1 parent 1fcf7e6f87
commit 00dbb1fea7
12 files changed
+608 -14

No files matched your search

+63 -5
View File
@@ -5,15 +5,73 @@
package app
import "git.stormux.org/storm/cthulhu/internal/atspi"
import (
"strings"
"unicode/utf8"
"git.stormux.org/storm/cthulhu/internal/atspi"
"git.stormux.org/storm/cthulhu/internal/input"
)
func should_ignore_terminal_scrollback(event atspi.Event) bool {
// VTE can remove old complete lines at the start of its accessible text
// while appending output at a different offset. These are not edits to
// the prompt. This event pattern occurs on both X11 and Wayland.
// VTE can remove old lines at the start of its accessible text while
// appending output at a different offset. A deletion entirely before
// the caret line is not an edit to the prompt. VTE wrapped rows have
// no trailing newline, so do not require one. This event pattern
// occurs on both X11 and Wayland.
return event.Type == atspi.TextDeletedEvent &&
event.Source.Role == "terminal" && event.Detail1 == 0 &&
event.Source.TextCaretKnown && event.Source.BrailleLineStart > 0 &&
event.TextChangeEndsLine && event.TextChangeLength > 0 &&
event.TextChangeLength > 0 &&
event.TextChangeLength <= event.Source.BrailleLineStart
}
// is_user_terminal_deletion identifies a deletion the user just caused at
// the caret on its own line: a Backspace or Delete edit. The deleted text
// is the user's confirmation that the edit took, so repaint coalescing
// must not swallow it. Program repaints start above the caret line, and
// deletions without a preceding delete key are program output churn.
func is_user_terminal_deletion(event atspi.Event, intent input.Intent) bool {
return intent == input.DeleteIntent &&
event.Type == atspi.TextDeletedEvent && event.Source.Role == "terminal" &&
event.Source.TextCaretKnown &&
event.Detail1 >= event.Source.BrailleLineStart &&
event.Detail1 <= event.Source.BrailleCaret
}
// is_latest_wins_terminal_repaint identifies an in-place rewrite: an
// insertion that replaced a just-deleted range. Status regions update
// this way several times a second and only the newest state is worth
// hearing. Editing feedback on the caret line after a typing or delete
// key stays immediate; appended output has no preceding deletion and
// never qualifies.
func is_latest_wins_terminal_repaint(event atspi.Event, intent input.Intent, replacedDeletion bool) bool {
if !replacedDeletion || event.Type != atspi.TextInsertedEvent || event.Source.Role != "terminal" {
return false
}
if (intent == input.TypingIntent || intent == input.DeleteIntent) &&
event.Source.TextCaretKnown && event.Detail1 >= event.Source.BrailleLineStart {
return false
}
return true
}
// is_trivial_terminal_repaint identifies a held repaint whose payload is
// only the few cells that changed in a ticking status (timer digits such
// as "40"). Uttered alone it has no context; a fuller repaint of the same
// region replaces it before the flush if one follows.
func is_trivial_terminal_repaint(event atspi.Event) bool {
return event.Type == atspi.TextInsertedEvent && event.Source.Role == "terminal" &&
utf8.RuneCountInString(strings.TrimSpace(event.Payload)) <= 2
}
// terminalReplacementOverlap reports whether an insertion rewrites any of
// the range a pending deletion removed. Offsets are not compared exactly:
// a scroll shift at the start of the buffer between the deletion and its
// replacement insertion moves the insertion's offset, and a repainted
// region is routinely rewritten at a neighboring offset.
func terminalReplacementOverlap(deletion, insertion atspi.Event) bool {
deletionEnd := deletion.Detail1 + max(deletion.TextChangeLength, 1)
insertionEnd := insertion.Detail1 + max(insertion.TextChangeLength, 1)
return insertion.Detail1 < deletionEnd && deletion.Detail1 < insertionEnd
}