mirror of
https://github.com/wavetermdev/waveterm.git
synced 2025-01-02 18:39:05 +01:00
checkpoint, some wsh stuff
This commit is contained in:
parent
146bade6f1
commit
cf8ae548e8
20
cmd/main-wsh.go
Normal file
20
cmd/main-wsh.go
Normal file
@ -0,0 +1,20 @@
|
||||
// Copyright 2024, Command Line Inc.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
const WaveOSC = "23198"
|
||||
|
||||
func main() {
|
||||
barr, err := os.ReadFile("/Users/mike/Downloads/2.png")
|
||||
if err != nil {
|
||||
fmt.Println("error reading file:", err)
|
||||
return
|
||||
}
|
||||
fmt.Println("file size:", len(barr))
|
||||
}
|
54
pkg/wshutil/wshcommands.go
Normal file
54
pkg/wshutil/wshcommands.go
Normal file
@ -0,0 +1,54 @@
|
||||
// Copyright 2024, Command Line Inc.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package wshutil
|
||||
|
||||
import "reflect"
|
||||
|
||||
const (
|
||||
CommandSetView = "setview"
|
||||
CommandSetMeta = "setmeta"
|
||||
CommandBlockFileAppend = "blockfile:append"
|
||||
)
|
||||
|
||||
var CommandToTypeMap = map[string]reflect.Type{
|
||||
CommandSetView: reflect.TypeOf(SetViewCommand{}),
|
||||
CommandSetMeta: reflect.TypeOf(SetMetaCommand{}),
|
||||
}
|
||||
|
||||
type Command interface {
|
||||
GetCommand() string
|
||||
}
|
||||
|
||||
// for unmarshalling
|
||||
type baseCommand struct {
|
||||
Command string `json:"command"`
|
||||
}
|
||||
|
||||
type SetViewCommand struct {
|
||||
Command string `json:"command"`
|
||||
View string `json:"view"`
|
||||
}
|
||||
|
||||
func (svc *SetViewCommand) GetCommand() string {
|
||||
return CommandSetView
|
||||
}
|
||||
|
||||
type SetMetaCommand struct {
|
||||
Command string `json:"command"`
|
||||
Meta map[string]any `json:"meta"`
|
||||
}
|
||||
|
||||
func (smc *SetMetaCommand) GetCommand() string {
|
||||
return CommandSetMeta
|
||||
}
|
||||
|
||||
type BlockFileAppendCommand struct {
|
||||
Command string `json:"command"`
|
||||
FileName string `json:"filename"`
|
||||
Data []byte `json:"data"`
|
||||
}
|
||||
|
||||
func (bfac *BlockFileAppendCommand) GetCommand() string {
|
||||
return CommandBlockFileAppend
|
||||
}
|
106
pkg/wshutil/wshutil.go
Normal file
106
pkg/wshutil/wshutil.go
Normal file
@ -0,0 +1,106 @@
|
||||
// Copyright 2024, Command Line Inc.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package wshutil
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
const WaveOSC = "23198"
|
||||
const WaveOSCPrefix = "\x1b]" + WaveOSC + ";"
|
||||
const HexChars = "0123456789ABCDEF"
|
||||
const BEL = 0x07
|
||||
|
||||
var waveOSCPrefixBytes = []byte(WaveOSCPrefix)
|
||||
|
||||
// OSC escape types
|
||||
// OSC 23198 ; (JSON | base64-JSON) ST
|
||||
// JSON = must escape all ASCII control characters ([\x00-\x1F\x7F])
|
||||
// we can tell the difference between JSON and base64-JSON by the first character: '{' or not
|
||||
|
||||
func EncodeWaveOSCMessage(cmd Command) ([]byte, error) {
|
||||
if cmd.GetCommand() == "" {
|
||||
return nil, fmt.Errorf("Command field not set in struct")
|
||||
}
|
||||
ctype, ok := CommandToTypeMap[cmd.GetCommand()]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("unknown command type %q", cmd.GetCommand())
|
||||
}
|
||||
cmdType := reflect.TypeOf(cmd)
|
||||
if cmdType != ctype && (cmdType.Kind() == reflect.Pointer && cmdType.Elem() != ctype) {
|
||||
return nil, fmt.Errorf("command type does not match %q", cmd.GetCommand())
|
||||
}
|
||||
barr, err := json.Marshal(cmd)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error marshalling message to json: %w", err)
|
||||
}
|
||||
hasControlChars := false
|
||||
for _, b := range barr {
|
||||
if b < 0x20 || b == 0x7F {
|
||||
hasControlChars = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !hasControlChars {
|
||||
// If no control characters, directly construct the output
|
||||
// \x1b] (2) + WaveOSC + ; (1) + message + \x07 (1)
|
||||
output := make([]byte, len(WaveOSCPrefix)+len(barr)+1)
|
||||
copy(output, waveOSCPrefixBytes)
|
||||
copy(output[len(WaveOSCPrefix):], barr)
|
||||
output[len(output)-1] = BEL
|
||||
return output, nil
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
buf.Write(waveOSCPrefixBytes)
|
||||
escSeq := [6]byte{'\\', 'u', '0', '0', '0', '0'}
|
||||
for _, b := range barr {
|
||||
if b < 0x20 || b == 0x7f {
|
||||
escSeq[4] = HexChars[b>>4]
|
||||
escSeq[5] = HexChars[b&0x0f]
|
||||
buf.Write(escSeq[:])
|
||||
} else {
|
||||
buf.WriteByte(b)
|
||||
}
|
||||
}
|
||||
buf.WriteByte(BEL)
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
func decodeWaveOSCMessage(data []byte) (Command, error) {
|
||||
var baseCmd baseCommand
|
||||
err := json.Unmarshal(data, &baseCmd)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error unmarshalling json: %w", err)
|
||||
}
|
||||
rtnCmd := reflect.New(CommandToTypeMap[baseCmd.Command]).Interface()
|
||||
err = json.Unmarshal(data, rtnCmd)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error unmarshalling json: %w", err)
|
||||
}
|
||||
return rtnCmd.(Command), nil
|
||||
}
|
||||
|
||||
// data does not contain the escape sequence, just the innards
|
||||
// this function implements the switch between JSON and base64-JSON
|
||||
func DecodeWaveOSCMessage(data []byte) (Command, error) {
|
||||
if len(data) == 0 {
|
||||
return nil, fmt.Errorf("empty data")
|
||||
}
|
||||
if data[0] != '{' {
|
||||
// decode base64
|
||||
rtnLen := base64.StdEncoding.DecodedLen(len(data))
|
||||
rtn := make([]byte, rtnLen)
|
||||
nw, err := base64.StdEncoding.Decode(rtn, data)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error decoding base64: %w", err)
|
||||
}
|
||||
return decodeWaveOSCMessage(rtn[:nw])
|
||||
}
|
||||
return decodeWaveOSCMessage(data)
|
||||
}
|
Loading…
Reference in New Issue
Block a user