Files
Chase CichorzandClaude Opus 5.5 2651b5e9bd Add Remote Braille IPC to the host and both SDKs
remote.braille requests and events carry a typed op under a
braille-stream lease: the controller's width and gestures for a
controlled reader, rendered cells back to its child, and cells, local
geometry and display gestures for a controller. Direction is validated
per role; a full mailbox revokes the lease. No display owner is
attached yet.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-06 16:06:38 -05:00

122 lines
3.9 KiB
Go

// Copyright (c) 2026 Storm Dragon
// Authors: Storm Dragon, Angus Kola
// SPDX-License-Identifier: GPL-3.0-or-later
package tentaclewire
import "encoding/json"
const (
RemoteBrailleMaxCells = 4096
RemoteBrailleQueueSize = 16
)
// RemoteBraille carries one Braille operation. Exactly one form is valid per
// direction and role:
//
// - "size" (controlled child to host): Width is the controller's display
// width; zero stops export.
// - "gesture" (controlled child to host, or host to controller child):
// Gesture is "pan-left", "pan-right" or "route"; Index is the route cell.
// - "cells" (controller child to host, or host to controlled child): final
// dot cells, 1 to 4,096 values of 0 to 255, never text to translate.
// - "geometry" (controller child to host asks; host to controller child
// answers with Width, zero meaning no local display).
type RemoteBraille struct {
Op string `json:"op"`
Width int `json:"width,omitempty"`
Gesture string `json:"gesture,omitempty"`
Index int `json:"index,omitempty"`
Cells []int `json:"cells,omitempty"`
}
type RemoteBrailleRequest struct {
RemoteOperation
Braille RemoteBraille `json:"braille"`
}
func validCells(cells []int) bool {
if len(cells) == 0 || len(cells) > RemoteBrailleMaxCells {
return false
}
for _, cell := range cells {
if cell < 0 || cell > 255 {
return false
}
}
return true
}
// ValidRemoteBraille checks representation only. Role and direction are
// checked by ValidRemoteBrailleFor.
func ValidRemoteBraille(value RemoteBraille) bool {
switch value.Op {
case "size":
return value.Width >= 0 && value.Width <= RemoteBrailleMaxCells && value.Gesture == "" && value.Index == 0 && value.Cells == nil
case "geometry":
return value.Width >= 0 && value.Width <= RemoteBrailleMaxCells && value.Gesture == "" && value.Index == 0 && value.Cells == nil
case "gesture":
switch value.Gesture {
case "pan-left", "pan-right":
return value.Index == 0 && value.Width == 0 && value.Cells == nil
case "route":
return value.Index >= 0 && value.Index < RemoteBrailleMaxCells && value.Width == 0 && value.Cells == nil
}
return false
case "cells":
return validCells(value.Cells) && value.Width == 0 && value.Gesture == "" && value.Index == 0
}
return false
}
// ValidRemoteBrailleFor checks direction: request is true for child-to-host.
func ValidRemoteBrailleFor(role string, request bool, value RemoteBraille) bool {
if !ValidRemoteBraille(value) {
return false
}
switch {
case role == "controlled" && request:
return value.Op == "size" || value.Op == "gesture"
case role == "controlled":
return value.Op == "cells"
case role == "controller" && request:
return value.Op == "cells" || value.Op == "geometry" && value.Width == 0
case role == "controller":
return value.Op == "gesture" || value.Op == "geometry"
}
return false
}
func ValidRemoteBrailleRequest(request RemoteBrailleRequest) bool {
return len(request.Lease) == 32 && request.Epoch != 0 && request.Sequence != 0 && ValidRemoteBraille(request.Braille)
}
func (value *RemoteBraille) UnmarshalJSON(data []byte) error {
var fields map[string]json.RawMessage
if DecodeStrict(data, &fields) != nil || fields["op"] == nil {
return ErrProtocol
}
type plain RemoteBraille
var decoded plain
if DecodeStrict(data, &decoded) != nil || !ValidRemoteBraille(RemoteBraille(decoded)) {
return ErrProtocol
}
*value = RemoteBraille(decoded)
return nil
}
func (request *RemoteBrailleRequest) UnmarshalJSON(data []byte) error {
var fields map[string]json.RawMessage
if DecodeStrict(data, &fields) != nil || len(fields) != 4 || fields["lease"] == nil || fields["epoch"] == nil ||
fields["sequence"] == nil || fields["braille"] == nil {
return ErrProtocol
}
type plain RemoteBrailleRequest
var decoded plain
if DecodeStrict(data, &decoded) != nil || !ValidRemoteBrailleRequest(RemoteBrailleRequest(decoded)) {
return ErrProtocol
}
*request = RemoteBrailleRequest(decoded)
return nil
}