// 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 }