- Compiler: PP → Lexer → Parser → Analyzer → Gengo pipeline - Parser: 232/236 (98%) Harbour compatibility, registry-based dispatch - RTL: 351 Harbour-compatible functions - RDD: DBF/NTX/CDX engines with Rushmore bitmap optimization - Go Interop: IMPORT + pkg.Func() + obj:Method() with FastPath (15M calls/sec) - HB_FUNC API: Full Harbour C API compatible Go bridge - Concurrency: SPAWN/LAUNCH/GOROUTINE, <-, WATCH, PARALLEL FOR, ASYNC/AWAIT - Extensions: Multi-return, DEFER, Slice, f-string, Nil-safe ?:, CONST - Macro Compiler: Runtime AST parsing and evaluation - Debugger: TUI debugger with source display, breakpoints, stepping - FRB: Native + Pcode dual mode runtime binary - Tests: 13 packages ALL PASS Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
134 lines
3.0 KiB
Go
134 lines
3.0 KiB
Go
// Copyright (c) 2026 Charles KWON OhJun (charleskwonohjun@gmail.com)
|
|
// All rights reserved.
|
|
|
|
// Raw terminal for Five. Uses /dev/tty opened fresh each ReadKey call.
|
|
package hbrtl
|
|
|
|
import (
|
|
"os"
|
|
"syscall"
|
|
"unsafe"
|
|
)
|
|
|
|
var (
|
|
origTermios syscall.Termios
|
|
rawModeOn bool
|
|
stdinFd int
|
|
)
|
|
|
|
// InitRawTerminal sets raw mode on stdin.
|
|
func InitRawTerminal() {
|
|
if rawModeOn {
|
|
return
|
|
}
|
|
stdinFd = int(os.Stdin.Fd())
|
|
|
|
// Save original
|
|
_, _, e := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(stdinFd), 0x5401, uintptr(unsafe.Pointer(&origTermios)), 0, 0, 0)
|
|
if e != 0 {
|
|
return
|
|
}
|
|
|
|
// Set raw on stdin
|
|
raw := origTermios
|
|
raw.Lflag &^= syscall.ICANON | syscall.ECHO | syscall.ISIG
|
|
raw.Oflag &^= syscall.OPOST
|
|
raw.Cc[syscall.VMIN] = 1
|
|
raw.Cc[syscall.VTIME] = 0
|
|
syscall.Syscall6(syscall.SYS_IOCTL, uintptr(stdinFd), 0x5402, uintptr(unsafe.Pointer(&raw)), 0, 0, 0)
|
|
|
|
rawModeOn = true
|
|
}
|
|
|
|
// RestoreTerminal restores original terminal.
|
|
func RestoreTerminal() {
|
|
if !rawModeOn {
|
|
return
|
|
}
|
|
syscall.Syscall6(syscall.SYS_IOCTL, uintptr(stdinFd), 0x5402, uintptr(unsafe.Pointer(&origTermios)), 0, 0, 0)
|
|
rawModeOn = false
|
|
}
|
|
|
|
// IsRawMode returns true if raw mode is active.
|
|
func IsRawMode() bool {
|
|
return rawModeOn
|
|
}
|
|
|
|
// ReadKey reads one key. Opens /dev/tty fresh each time to avoid buffered data.
|
|
func ReadKey() int {
|
|
if !rawModeOn {
|
|
InitRawTerminal()
|
|
}
|
|
os.Stdout.Sync()
|
|
|
|
// Open /dev/tty fresh — no stale data possible
|
|
fd, err := syscall.Open("/dev/tty", syscall.O_RDONLY, 0)
|
|
if err != nil {
|
|
return 27 // ESC
|
|
}
|
|
defer syscall.Close(fd)
|
|
|
|
// Set this fd to raw too
|
|
var t syscall.Termios
|
|
syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), 0x5401, uintptr(unsafe.Pointer(&t)), 0, 0, 0)
|
|
t.Lflag &^= syscall.ICANON | syscall.ECHO | syscall.ISIG
|
|
t.Cc[syscall.VMIN] = 1
|
|
t.Cc[syscall.VTIME] = 0
|
|
syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), 0x5402, uintptr(unsafe.Pointer(&t)), 0, 0, 0)
|
|
|
|
// Flush input buffer (TCFLSH = 0x540B, TCIFLUSH = 0)
|
|
syscall.Syscall(syscall.SYS_IOCTL, uintptr(fd), 0x540B, 0)
|
|
|
|
buf := make([]byte, 1)
|
|
n, e := syscall.Read(fd, buf)
|
|
if e != nil || n == 0 {
|
|
return 27
|
|
}
|
|
b := buf[0]
|
|
|
|
if b == 27 { // ESC — bare ESC or sequence?
|
|
// Short timeout to check for sequence
|
|
var t2 syscall.Termios
|
|
syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), 0x5401, uintptr(unsafe.Pointer(&t2)), 0, 0, 0)
|
|
t2.Cc[syscall.VMIN] = 0
|
|
t2.Cc[syscall.VTIME] = 1 // 100ms
|
|
syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), 0x5402, uintptr(unsafe.Pointer(&t2)), 0, 0, 0)
|
|
|
|
n2, _ := syscall.Read(fd, buf)
|
|
if n2 == 0 {
|
|
return 27 // bare ESC
|
|
}
|
|
if buf[0] == '[' {
|
|
n3, _ := syscall.Read(fd, buf)
|
|
if n3 == 0 {
|
|
return 27
|
|
}
|
|
switch buf[0] {
|
|
case 'A':
|
|
return 'A'
|
|
case 'B':
|
|
return 'B'
|
|
case 'C':
|
|
return 'C'
|
|
case 'D':
|
|
return 'D'
|
|
case '5':
|
|
syscall.Read(fd, buf)
|
|
return '5'
|
|
case '6':
|
|
syscall.Read(fd, buf)
|
|
return '6'
|
|
case 'H':
|
|
return 'H'
|
|
case 'F':
|
|
return 'F'
|
|
default:
|
|
return 27
|
|
}
|
|
}
|
|
return 27
|
|
}
|
|
|
|
return int(b)
|
|
}
|