package vt220 import ( "io" ) type ControlSequence struct { seq []byte } func NewControlSequence(bs ...byte) ControlSequence { cs := ControlSequence{ seq: make([]byte, len(bs), len(bs)), } copy(cs.seq, bs) return cs } func (seq ControlSequence) Bytes() []byte { return seq.seq } // func (seq ControlSequence) String() string { // return string(seq.Bytes()[:]) // } func (seq ControlSequence) Invoke(w io.Writer) { w.Write(seq.Bytes()) } type ControlSequencePrefix struct { pfx ControlSequence } func NewControlSequencePrefix(bs ...byte) ControlSequencePrefix { return ControlSequencePrefix{ pfx: NewControlSequence(bs...), } } func (pfx ControlSequencePrefix) Append(bs ...byte) ControlSequencePrefix { newLen := len(pfx.pfx.seq) + len(bs) ba := make([]byte, newLen, newLen) copy(ba, pfx.pfx.seq) ba = append(ba, bs...) return ControlSequencePrefix{ControlSequence{seq: ba}} } func (pfx ControlSequencePrefix) With(bs ...byte) ControlSequence { newLen := len(pfx.pfx.seq) + len(bs) ba := make([]byte, newLen, newLen) copy(ba, pfx.pfx.seq) ba = append(ba, bs...) return ControlSequence{seq: ba} } type SGRAttr struct { ps byte not bool } func SGR(attrs ...SGRAttr) ControlSequence { // https://web.archive.org/web/20230928233241/https://vt100.net/docs/vt220-rm/chapter4.html#S4.9 // // Set the capacity to 2 (initialization + termination) + 3 * total attrs. // Attr Ps can be up to two bytes (if negated) and when multiple are present they need one // extra byte for delimitation. ba := make([]byte, 0, (2 + (len(attrs) * 3))) // Append SGR sequence initialization control character. ba = append(ba, CSI) for i, attr := range attrs { if i > 0 { // Append Ps delimiter. ba = append(ba, C03R11) } if attr.not { // Append negation byte. ba = append(ba, C03R02) } // Append attr ID byte. ba = append(ba, attr.ps) } // Append SGR terminator. ba = append(ba, C06R13) return NewControlSequence(ba...) } var ( // Prefixes EscapeCtrlPfx = NewControlSequencePrefix(ESC) DesignateCharSetR0CtrlPfx = EscapeCtrlPfx.Append(C02R08) DesignateCharSetR1CtrlPfx = EscapeCtrlPfx.Append(C02R09) DesignateCharSetR2CtrlPfx = EscapeCtrlPfx.Append(C02R10) DesignateCharSetR3CtrlPfx = EscapeCtrlPfx.Append(C02R11) // Complete Sequences ShiftCtrl = struct { LockShiftG0, LockShiftG1, LockShiftG1Right, LockShiftG2, LockShiftG2Right, LockShiftG3, LockShiftG3Right, SingleShiftG2, SingleShiftG3 ControlSequence LS0, LS1, LS1R, LS2, LS2R, LS3, LS3R, SS2, SS3 ControlSequence }{ // Lock Shifts LockShiftG0: NewControlSequence(SI), LockShiftG1: NewControlSequence(SO), LockShiftG1Right: EscapeCtrlPfx.With(C07R14), LockShiftG2: EscapeCtrlPfx.With(C06R14), LockShiftG2Right: EscapeCtrlPfx.With(C07R13), LockShiftG3: EscapeCtrlPfx.With(C06R15), LockShiftG3Right: EscapeCtrlPfx.With(C07R12), // Single Shifts SingleShiftG2: NewControlSequence(SS2), SingleShiftG3: NewControlSequence(SS3), } SGRPs = struct { AllAttrsOff, Bold, Underline, Blink, Negative, NoBold, NoUnderline, NoBlink, Positive SGRAttr }{ AllAttrsOff: SGRAttr{ ps: C03R00, }, Bold: SGRAttr{ ps: C03R01, }, Underline: SGRAttr{ ps: C03R04, }, Blink: SGRAttr{ ps: C03R07, }, Negative: SGRAttr{ ps: C03R07, }, NoBold: SGRAttr{ ps: C03R01, not: true, }, NoUnderline: SGRAttr{ ps: C03R04, not: true, }, NoBlink: SGRAttr{ ps: C03R07, not: true, }, Positive: SGRAttr{ ps: C03R07, not: true, }, } ) func init() { ShiftCtrl.LS0 = ShiftCtrl.LockShiftG0 ShiftCtrl.LS1 = ShiftCtrl.LockShiftG1 ShiftCtrl.LS1R = ShiftCtrl.LockShiftG1Right ShiftCtrl.LS2 = ShiftCtrl.LockShiftG2 ShiftCtrl.LS2R = ShiftCtrl.LockShiftG2Right ShiftCtrl.LS3 = ShiftCtrl.LockShiftG3 ShiftCtrl.LS3R = ShiftCtrl.LockShiftG3Right ShiftCtrl.SS2 = ShiftCtrl.SingleShiftG2 ShiftCtrl.SS3 = ShiftCtrl.SingleShiftG3 }