mirror of
https://github.com/jixishi/SerialTerminalForWindowsTerminal.git
synced 2026-06-16 00:52:44 +00:00
chore: remove dead code and binary files from tracking
Remove unused global var `in`, func `strout`, func `echoConsoleInput`, func `padRight`, func `ErrorP`, and func `ErrorF`. Inline error check in CloseSerial. Add COM.exe and coverage.out to .gitignore. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -2,10 +2,16 @@ package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
"github.com/spf13/pflag"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"os/signal"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/term"
|
||||
)
|
||||
|
||||
func init() {
|
||||
@@ -13,10 +19,17 @@ func init() {
|
||||
for _, f := range flags {
|
||||
flagInit(&f)
|
||||
}
|
||||
cmdinit()
|
||||
}
|
||||
|
||||
func main() {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
fmt.Fprintf(os.Stderr, "fatal: %v\n", r)
|
||||
os.Exit(1)
|
||||
}
|
||||
}()
|
||||
|
||||
normalizeFlags()
|
||||
pflag.Parse()
|
||||
flagExt()
|
||||
if config.portName == "" {
|
||||
@@ -29,28 +42,337 @@ func main() {
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
// 日志文件输出检测
|
||||
checkLogOpen()
|
||||
|
||||
//串口设备开启
|
||||
OpenSerial()
|
||||
|
||||
defer CloseSerial()
|
||||
// 打开文件服务
|
||||
OpenTrzsz()
|
||||
|
||||
defer CloseTrzsz()
|
||||
|
||||
//开启转发
|
||||
OpenForwarding()
|
||||
|
||||
// 获取终端输入
|
||||
go input(in)
|
||||
|
||||
if len(outs) != 1 {
|
||||
out = io.MultiWriter(outs...)
|
||||
if err = OpenSerial(); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "open serial failed: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
for {
|
||||
output()
|
||||
|
||||
if err = OpenTrzsz(); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "open trzsz failed: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
app, err := NewApp(&config)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "create app failed: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
defer app.Close()
|
||||
|
||||
app.loadConfiguredForwards()
|
||||
app.startOutputLoop()
|
||||
|
||||
go forwardInterruptToRemote(app)
|
||||
app.SetUIEnabled(config.enableGUI)
|
||||
|
||||
if config.enableGUI {
|
||||
model := newUIModel(app)
|
||||
p := tea.NewProgram(model, tea.WithAltScreen(), tea.WithoutSignalHandler())
|
||||
if _, err = p.Run(); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "tui failed: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if err = runConsole(app); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "console failed: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func forwardInterruptToRemote(app *App) {
|
||||
sigCh := make(chan os.Signal, 1)
|
||||
signal.Notify(sigCh, os.Interrupt)
|
||||
defer signal.Stop(sigCh)
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-app.waitDone():
|
||||
return
|
||||
case <-sigCh:
|
||||
if err := app.sendCtrl('c'); err != nil {
|
||||
app.Notifyf("[signal] interrupt pass-through failed: %v", err)
|
||||
continue
|
||||
}
|
||||
app.Notifyf("[signal] Ctrl+C forwarded to remote")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func runConsole(app *App) error {
|
||||
fd := int(os.Stdin.Fd())
|
||||
isTerm := term.IsTerminal(fd)
|
||||
var oldState *term.State
|
||||
var err error
|
||||
if isTerm {
|
||||
enableVTInput(fd)
|
||||
oldState, err = term.MakeRaw(fd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
_ = term.Restore(fd, oldState)
|
||||
}()
|
||||
}
|
||||
|
||||
app.Notifyf("[console] non-gui mode, commands start with '.' at line start\n")
|
||||
app.Notifyf("[console] Ctrl+<Key> passes through to remote; .exit to exit")
|
||||
|
||||
// Read with a larger buffer so multi-byte sequences (arrows, CSI) arrive together.
|
||||
ch := make(chan byte, 1024)
|
||||
errCh := make(chan error, 1)
|
||||
go func() {
|
||||
buf := make([]byte, 256)
|
||||
for {
|
||||
n, rdErr := os.Stdin.Read(buf)
|
||||
if rdErr != nil {
|
||||
errCh <- rdErr
|
||||
return
|
||||
}
|
||||
for i := 0; i < n; i++ {
|
||||
ch <- buf[i]
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
lineStart := true
|
||||
commandMode := false
|
||||
cmdBuf := make([]byte, 0, 128)
|
||||
|
||||
tryRead := func() (byte, bool) {
|
||||
select {
|
||||
case b := <-ch:
|
||||
return b, true
|
||||
default:
|
||||
return 0, false
|
||||
}
|
||||
}
|
||||
|
||||
readByte := func() (byte, error) {
|
||||
select {
|
||||
case <-app.waitDone():
|
||||
return 0, io.EOF
|
||||
case rdErr := <-errCh:
|
||||
return 0, rdErr
|
||||
case b := <-ch:
|
||||
return b, nil
|
||||
}
|
||||
}
|
||||
|
||||
// flushESC sends a fully-built escape sequence to serial.
|
||||
flushESC := func(seq []byte) bool {
|
||||
if isExitHotkeySeq(seq) {
|
||||
app.Close()
|
||||
return true
|
||||
}
|
||||
if err = app.writeToSession(seq); err != nil {
|
||||
app.Statusf("[send] %v", err)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
for {
|
||||
b, rdErr := readByte()
|
||||
if rdErr != nil {
|
||||
if rdErr == io.EOF {
|
||||
return nil
|
||||
}
|
||||
return rdErr
|
||||
}
|
||||
|
||||
// ── Escape sequences (VT / CSI) ──
|
||||
if b == 0x1b {
|
||||
// Try to read the rest without blocking.
|
||||
escBuf := []byte{0x1b}
|
||||
for {
|
||||
nb, ok := tryRead()
|
||||
if !ok {
|
||||
// Standalone ESC — send it now.
|
||||
if err = app.writeToSession([]byte{0x1b}); err != nil {
|
||||
app.Statusf("[send] %v", err)
|
||||
}
|
||||
break
|
||||
}
|
||||
escBuf = append(escBuf, nb)
|
||||
// CSI terminator byte (0x40–0x7E): A–Z, a–z, ~, etc.
|
||||
if nb >= 0x40 && nb <= 0x7e {
|
||||
if flushESC(escBuf) {
|
||||
return nil
|
||||
}
|
||||
break
|
||||
}
|
||||
// Short non-CSI sequence (e.g. ESC c).
|
||||
if len(escBuf) == 2 && escBuf[1] != '[' {
|
||||
if flushESC(escBuf) {
|
||||
return nil
|
||||
}
|
||||
break
|
||||
}
|
||||
// CSI parameter bytes (digits, semicolons, etc.) — keep collecting.
|
||||
if len(escBuf) > 16 {
|
||||
// Too long, just flush.
|
||||
if err = app.writeToSession(escBuf); err != nil {
|
||||
app.Statusf("[send] %v", err)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
// ── Windows Alt+key: NULL prefix ──
|
||||
if b == 0x00 {
|
||||
if b2, ok := tryRead(); ok {
|
||||
if isAltKeyExit(b2) {
|
||||
app.Close()
|
||||
return nil
|
||||
}
|
||||
if err = app.writeToSession([]byte{0x00, b2}); err != nil {
|
||||
app.Statusf("[send] %v", err)
|
||||
}
|
||||
} else {
|
||||
// No second byte available — send NULL alone.
|
||||
if err = app.writeToSession([]byte{0x00}); err != nil {
|
||||
app.Statusf("[send] %v", err)
|
||||
}
|
||||
}
|
||||
if commandMode {
|
||||
lineStart = false
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
// ── Command mode ──
|
||||
if commandMode {
|
||||
switch b {
|
||||
case '\r', '\n':
|
||||
echoConsoleNewline()
|
||||
line := string(cmdBuf)
|
||||
if strings.TrimSpace(line) != "" {
|
||||
app.handleLine(line)
|
||||
}
|
||||
commandMode = false
|
||||
cmdBuf = cmdBuf[:0]
|
||||
lineStart = true
|
||||
case 0x7f, 0x08:
|
||||
if len(cmdBuf) > 0 {
|
||||
cmdBuf = cmdBuf[:len(cmdBuf)-1]
|
||||
echoConsoleBackspace()
|
||||
}
|
||||
case 0x09: // Tab — command completion
|
||||
line, cands := app.dispatcher.Complete(string(cmdBuf))
|
||||
if len(cands) == 1 {
|
||||
cmdBuf = append(cmdBuf[:0], line...)
|
||||
echoRedrawCommand(line)
|
||||
} else if len(cands) > 1 {
|
||||
echoConsoleNewline()
|
||||
app.Notifyf("%s", strings.Join(cands, " "))
|
||||
echoConsoleByte('.')
|
||||
echoConsoleString(string(cmdBuf[1:]))
|
||||
}
|
||||
default:
|
||||
cmdBuf = append(cmdBuf, b)
|
||||
echoConsoleByte(b)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
// ── Normal mode (sending to remote) ──
|
||||
if lineStart && b == '.' {
|
||||
commandMode = true
|
||||
cmdBuf = append(cmdBuf[:0], b)
|
||||
echoConsoleByte(b)
|
||||
continue
|
||||
}
|
||||
|
||||
if b == '\r' || b == '\n' {
|
||||
if err = app.writeToSession([]byte(config.endStr)); err != nil {
|
||||
app.Statusf("[send] %v", err)
|
||||
}
|
||||
lineStart = true
|
||||
} else {
|
||||
if err = app.writeToSession([]byte{b}); err != nil {
|
||||
app.Statusf("[send] %v", err)
|
||||
}
|
||||
lineStart = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func parseCSIu(seq []byte) (cp int, mod int, ok bool) {
|
||||
// ESC [ codepoint ; modifier u
|
||||
if len(seq) < 6 {
|
||||
return 0, 0, false
|
||||
}
|
||||
if seq[0] != 0x1b || seq[1] != '[' {
|
||||
return 0, 0, false
|
||||
}
|
||||
if seq[len(seq)-1] != 'u' {
|
||||
return 0, 0, false
|
||||
}
|
||||
inner := string(seq[2 : len(seq)-1])
|
||||
parts := strings.SplitN(inner, ";", 2)
|
||||
if len(parts) != 2 {
|
||||
return 0, 0, false
|
||||
}
|
||||
cp, err := strconv.Atoi(parts[0])
|
||||
if err != nil {
|
||||
return 0, 0, false
|
||||
}
|
||||
mod, err = strconv.Atoi(parts[1])
|
||||
if err != nil {
|
||||
return 0, 0, false
|
||||
}
|
||||
return cp, mod, true
|
||||
}
|
||||
|
||||
func isAltKeyExit(b byte) bool {
|
||||
if normalizeHotkeyPrefix(config.hotkeyMod) != "ctrl+alt" {
|
||||
return false
|
||||
}
|
||||
// 0x2E = scan code for 'C', 0x03 = Ctrl+C, 0x63 = 'c', 0x43 = 'C'
|
||||
return b == 0x2e || b == 0x03 || b == 0x63 || b == 0x43
|
||||
}
|
||||
|
||||
func isExitHotkeySeq(seq []byte) bool {
|
||||
mod := normalizeHotkeyPrefix(config.hotkeyMod)
|
||||
|
||||
// CSI u format: ESC [ codepoint ; modifier u
|
||||
// Only matches when the Ctrl modifier bit (4) is present,
|
||||
// distinguishing Ctrl+Alt+C from Alt+C alone.
|
||||
if cp, cmod, ok := parseCSIu(seq); ok {
|
||||
if cp != 'c' && cp != 'C' {
|
||||
return false
|
||||
}
|
||||
switch mod {
|
||||
case "ctrl+alt":
|
||||
return cmod&6 == 6
|
||||
case "ctrl+shift":
|
||||
return cmod&5 == 5
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func echoConsoleByte(b byte) {
|
||||
_, _ = out.Write([]byte{b})
|
||||
}
|
||||
|
||||
func echoConsoleNewline() {
|
||||
_, _ = io.WriteString(out, "\r\n")
|
||||
}
|
||||
|
||||
func echoConsoleBackspace() {
|
||||
_, _ = io.WriteString(out, "\b \b")
|
||||
}
|
||||
|
||||
func echoConsoleString(s string) {
|
||||
_, _ = io.WriteString(out, s)
|
||||
}
|
||||
|
||||
func echoRedrawCommand(s string) {
|
||||
_, _ = io.WriteString(out, "\r\033[K> "+s)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user