97 lines
4.0 KiB
Go
97 lines
4.0 KiB
Go
// Fenrir console screen reader.
|
|
// Copyright (c) 2026 Storm Dragon
|
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
//
|
|
// This program is free software: you can redistribute it and/or modify it
|
|
// under the terms of the GNU General Public License as published by the Free
|
|
// Software Foundation, either version 3 of the License, or (at your option)
|
|
// any later version.
|
|
//
|
|
// This program is distributed in the hope that it will be useful, but WITHOUT
|
|
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
// more details.
|
|
//
|
|
// You should have received a copy of the GNU General Public License along with
|
|
// this program. If not, see <https://www.gnu.org/licenses/gpl-3.0.html>.
|
|
|
|
package reader
|
|
|
|
import (
|
|
"slices"
|
|
"unicode/utf8"
|
|
|
|
"git.stormux.org/storm/fenrir/internal/diagnostic"
|
|
"git.stormux.org/storm/fenrir/internal/presentation"
|
|
"git.stormux.org/storm/fenrir/internal/screen"
|
|
)
|
|
|
|
// AttributeChange contains only geometry and appearance, never cell text.
|
|
// End is exclusive. Keep details bounded even during a whole-screen repaint.
|
|
type AttributeChange struct {
|
|
Row, Start, End int
|
|
Before, After screen.Attribute
|
|
}
|
|
|
|
func (r *Reader) trace_update(previous, next screen.Snapshot, switched bool, pendingInput int, path string, output []presentation.Message) {
|
|
comparable := previous.Rows == next.Rows && previous.Cols == next.Cols &&
|
|
next.Rows > 0 && next.Cols > 0 && len(previous.Cells) == next.Rows*next.Cols && len(next.Cells) == len(previous.Cells)
|
|
previousAttributes := len(previous.Attributes) > 0 && len(previous.Attributes) == len(previous.Cells)
|
|
nextAttributes := len(next.Attributes) > 0 && len(next.Attributes) == len(next.Cells)
|
|
textRows, attributeRows := []int{}, []int{}
|
|
attributeChanges := []AttributeChange{}
|
|
truncated := false
|
|
changedCells, changedAttributes := 0, 0
|
|
if comparable {
|
|
for row := range next.Rows {
|
|
if !slices.Equal(previous.Line(row), next.Line(row)) {
|
|
textRows = append(textRows, row)
|
|
}
|
|
rowChanged := false
|
|
for col := range next.Cols {
|
|
index := row*next.Cols + col
|
|
if previous.Cells[index] != next.Cells[index] {
|
|
changedCells++
|
|
}
|
|
if !previousAttributes || !nextAttributes || previous.Attributes[index] == next.Attributes[index] {
|
|
continue
|
|
}
|
|
rowChanged = true
|
|
changedAttributes++
|
|
before, after := previous.Attributes[index], next.Attributes[index]
|
|
last := len(attributeChanges) - 1
|
|
if last >= 0 && attributeChanges[last].Row == row && attributeChanges[last].End == col &&
|
|
attributeChanges[last].Before == before && attributeChanges[last].After == after {
|
|
attributeChanges[last].End++
|
|
} else if len(attributeChanges) < 32 {
|
|
attributeChanges = append(attributeChanges, AttributeChange{Row: row, Start: col, End: col + 1, Before: before, After: after})
|
|
} else {
|
|
truncated = true
|
|
}
|
|
}
|
|
if rowChanged {
|
|
attributeRows = append(attributeRows, row)
|
|
}
|
|
}
|
|
}
|
|
outputLengths := make([]int, len(output))
|
|
for index, message := range output {
|
|
outputLengths[index] = utf8.RuneCountInString(message.Text)
|
|
}
|
|
diagnostic.Record("reader update",
|
|
"terminal", next.Terminal, "previous_terminal", previous.Terminal,
|
|
"generation", next.Generation, "previous_generation", previous.Generation,
|
|
"active", next.Active, "switched", switched,
|
|
"size", [2]int{next.Rows, next.Cols}, "previous_size", [2]int{previous.Rows, previous.Cols},
|
|
"cursor", [2]int{next.X, next.Y}, "previous_cursor", [2]int{previous.X, previous.Y},
|
|
"mode", r.trackingMode, "automatic", r.automatic, "cursor_echo", r.cursorEcho,
|
|
"review", r.review.Active(), "pending_typing", pendingInput,
|
|
"path", path, "comparable", comparable,
|
|
"text_rows", textRows, "text_cells", changedCells,
|
|
"previous_attributes", previousAttributes, "attributes", nextAttributes,
|
|
"attribute_rows", attributeRows, "attribute_cells", changedAttributes,
|
|
"attribute_changes", attributeChanges,
|
|
"attribute_changes_truncated", truncated,
|
|
"messages", len(output), "message_lengths", outputLengths)
|
|
}
|