Export and display Remote Braille through the reader

A controlled reader renders accepted application frames in a new
virtual-only cthulhu-braille mode at the controller's width and applies
its pan and route gestures; reader UI, flashes and secure frames are
never exported, and hidden frames are unroutable. A controller reader
reports its display width, shows peer cells as an untranslated raw
frame and forwards local pan and routing keys. The physical helper now
reports its width and edge pans.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Chase CichorzandClaude Opus 5.5 committed 2026-11-06 16:06:38 -05:00
1 parent 2651b5e9bd
commit 551ef86d5f
16 files changed
+925 -13

No files matched your search

+20 -4
View File
@@ -46,7 +46,7 @@ func (worker *brailleWorker) SetInternalUIFrame(frame *braille.Frame) {
worker.flashContext = nil
worker.protectedFlash = false
worker.internalUIFrame = nil
next := worker.baseFrame
next := worker.shown_base_locked()
if frame != nil {
next = *frame
next.WordWrap = worker.wordWrap
@@ -107,16 +107,25 @@ func (worker *brailleWorker) enqueueSecureFrameLocked(frame braille.Frame, base
if worker.internalUIFrame != nil {
if base {
worker.baseFrame = frame
worker.export_locked(frame, 0) // Not displayed: unroutable.
}
return false
}
if worker.cancel == nil || worker.stopping {
return false
}
if base && worker.remoteFrame != nil {
// A peer's display owns the base position; keep the newest local
// frame for restoration without showing it.
worker.baseFrame = frame
worker.export_locked(frame, 0)
return false
}
if base {
worker.flashContext = nil
if worker.protectedFlash {
worker.baseFrame = frame
worker.export_locked(frame, 0)
return false
}
worker.flashGeneration++
@@ -129,8 +138,15 @@ func (worker *brailleWorker) enqueueSecureFrameLocked(frame braille.Frame, base
frame.Contract = worker.contracted
frame.Table = worker.table
frame.ExpandAtCursor = frame.Cursor >= 0 && len(frame.Routes) > 0
if frame.Raw {
// Final peer cells: never translate or wrap them again.
frame.Contract, frame.Table, frame.ExpandAtCursor, frame.WordWrap = false, "", false, false
}
worker.sequence++
frame.ID = worker.sequence
if base {
worker.export_locked(frame, frame.ID)
}
frame.Routes = append([]braille.TextRoute(nil), frame.Routes...)
frame.Dots = append([]byte(nil), frame.Dots...)
if worker.routeCancel != nil {
@@ -202,7 +218,7 @@ func (worker *brailleWorker) flash_message_guarded(text string, options config.B
worker.flashTimer = nil
worker.flashContext = nil
worker.protectedFlash = false
queued := worker.enqueueFrameLocked(worker.baseFrame, false)
queued := worker.enqueueFrameLocked(worker.shown_base_locked(), false)
worker.mu.Unlock()
if queued {
worker.wakeDisplay()
@@ -228,7 +244,7 @@ func (worker *brailleWorker) DismissConfirmation() {
worker.flashTimer.Stop()
worker.flashTimer = nil
}
queued := worker.enqueueFrameLocked(worker.baseFrame, false)
queued := worker.enqueueFrameLocked(worker.shown_base_locked(), false)
worker.mu.Unlock()
if queued {
worker.wakeDisplay()
@@ -286,7 +302,7 @@ func (worker *brailleWorker) retirePresentationLocked(owner context.Context) boo
worker.flashTimer.Stop()
worker.flashTimer = nil
}
return worker.enqueueFrameLocked(worker.baseFrame, false)
return worker.enqueueFrameLocked(worker.shown_base_locked(), false)
}
func (worker *brailleWorker) retirePresentation(owner context.Context) {