// 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 }