- 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>
126 lines
2.5 KiB
Go
126 lines
2.5 KiB
Go
// Copyright (c) 2026 Charles KWON OhJun (charleskwonohjun@gmail.com)
|
|
// All rights reserved.
|
|
|
|
// Keyboard functions: LASTKEY, NEXTKEY, READKEY, SETKEY, KEYBOARD, HB_KEYPUT
|
|
|
|
package hbrtl
|
|
|
|
import (
|
|
"five/hbrt"
|
|
"sync"
|
|
)
|
|
|
|
var (
|
|
lastKey int // last key returned by Inkey
|
|
keyBuffer []int // typeahead buffer
|
|
keyActions map[int]hbrt.Value // SETKEY action blocks
|
|
keyMu sync.Mutex
|
|
)
|
|
|
|
func init() {
|
|
keyActions = make(map[int]hbrt.Value)
|
|
}
|
|
|
|
// SetLastKey records the last key (called by Inkey internally).
|
|
func SetLastKey(k int) {
|
|
lastKey = k
|
|
}
|
|
|
|
// LASTKEY() → nKeyCode
|
|
func LastKey(t *hbrt.Thread) {
|
|
t.Frame(0, 0)
|
|
defer t.EndProc()
|
|
t.RetInt(int64(lastKey))
|
|
}
|
|
|
|
// NEXTKEY([nEventMask]) → nKeyCode
|
|
// Returns the next key in the buffer without removing it. 0 if empty.
|
|
func NextKey(t *hbrt.Thread) {
|
|
nParams := t.ParamCount()
|
|
t.Frame(nParams, 0)
|
|
defer t.EndProc()
|
|
keyMu.Lock()
|
|
defer keyMu.Unlock()
|
|
if len(keyBuffer) > 0 {
|
|
t.RetInt(int64(keyBuffer[0]))
|
|
} else {
|
|
t.RetInt(0)
|
|
}
|
|
}
|
|
|
|
// READKEY() → nKeyCode
|
|
// Returns a composite value based on how the last READ was exited.
|
|
// Simplified: returns last key pressed during READ.
|
|
func ReadKeyFunc(t *hbrt.Thread) {
|
|
t.Frame(0, 0)
|
|
defer t.EndProc()
|
|
t.RetInt(int64(lastKey))
|
|
}
|
|
|
|
// SETKEY(nKeyCode [, bAction]) → bOldAction | NIL
|
|
// Associates a code block with a key press.
|
|
func SetKeyFunc(t *hbrt.Thread) {
|
|
nParams := t.ParamCount()
|
|
t.Frame(nParams, 0)
|
|
defer t.EndProc()
|
|
|
|
nKey := t.Local(1).AsInt()
|
|
keyMu.Lock()
|
|
defer keyMu.Unlock()
|
|
|
|
oldAction, exists := keyActions[nKey]
|
|
if !exists {
|
|
oldAction = hbrt.MakeNil()
|
|
}
|
|
|
|
if nParams >= 2 {
|
|
action := t.Local(2)
|
|
if action.IsNil() {
|
|
delete(keyActions, nKey)
|
|
} else {
|
|
keyActions[nKey] = action
|
|
}
|
|
}
|
|
|
|
t.RetVal(oldAction)
|
|
}
|
|
|
|
// KEYBOARD(cString)
|
|
// Stuffs characters into the keyboard buffer.
|
|
func Keyboard(t *hbrt.Thread) {
|
|
t.Frame(1, 0)
|
|
defer t.EndProc()
|
|
s := t.Local(1).AsString()
|
|
keyMu.Lock()
|
|
defer keyMu.Unlock()
|
|
keyBuffer = keyBuffer[:0] // clear
|
|
for _, ch := range s {
|
|
keyBuffer = append(keyBuffer, int(ch))
|
|
}
|
|
t.RetNil()
|
|
}
|
|
|
|
// HB_KEYPUT(nKeyCode)
|
|
// Puts a single key into the keyboard buffer.
|
|
func HbKeyPut(t *hbrt.Thread) {
|
|
t.Frame(1, 0)
|
|
defer t.EndProc()
|
|
nKey := t.Local(1).AsInt()
|
|
keyMu.Lock()
|
|
defer keyMu.Unlock()
|
|
keyBuffer = append(keyBuffer, nKey)
|
|
t.RetNil()
|
|
}
|
|
|
|
// PopKeyBuffer removes and returns next key from buffer, or -1 if empty.
|
|
func PopKeyBuffer() int {
|
|
keyMu.Lock()
|
|
defer keyMu.Unlock()
|
|
if len(keyBuffer) == 0 {
|
|
return -1
|
|
}
|
|
k := keyBuffer[0]
|
|
keyBuffer = keyBuffer[1:]
|
|
return k
|
|
}
|