- 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>
157 lines
3.5 KiB
Go
157 lines
3.5 KiB
Go
// Copyright (c) 2026 Charles KWON OhJun (charleskwonohjun@gmail.com)
|
|
// All rights reserved.
|
|
|
|
// Display functions: DISPBEGIN, DISPEND, SAVESCREEN, RESTSCREEN, ALERT
|
|
|
|
package hbrtl
|
|
|
|
import (
|
|
"five/hbrt"
|
|
"fmt"
|
|
"strings"
|
|
)
|
|
|
|
var dispCount int // nesting count for DISPBEGIN/DISPEND
|
|
|
|
// DISPBEGIN() — begins display buffering
|
|
func DispBegin(t *hbrt.Thread) {
|
|
t.Frame(0, 0)
|
|
defer t.EndProc()
|
|
dispCount++
|
|
// In a real terminal this would hold screen updates;
|
|
// for now just track the count.
|
|
t.RetNil()
|
|
}
|
|
|
|
// DISPEND() — ends display buffering, flushes
|
|
func DispEnd(t *hbrt.Thread) {
|
|
t.Frame(0, 0)
|
|
defer t.EndProc()
|
|
if dispCount > 0 {
|
|
dispCount--
|
|
}
|
|
t.RetNil()
|
|
}
|
|
|
|
// DISPCOUNT() → nNesting
|
|
func DispCount(t *hbrt.Thread) {
|
|
t.Frame(0, 0)
|
|
defer t.EndProc()
|
|
t.RetInt(int64(dispCount))
|
|
}
|
|
|
|
// SAVESCREEN([nTop, nLeft, nBottom, nRight]) → cBuffer
|
|
// Saves screen content as ANSI escape save sequence.
|
|
// Full implementation requires terminal screen buffer; this is a simplified version.
|
|
func SaveScreen(t *hbrt.Thread) {
|
|
nParams := t.ParamCount()
|
|
t.Frame(nParams, 0)
|
|
defer t.EndProc()
|
|
// Save cursor position and screen via ANSI
|
|
fmt.Print("\033[?47h") // save screen (alt buffer)
|
|
fmt.Print("\033[s") // save cursor
|
|
t.RetString("\033[SAVED]")
|
|
}
|
|
|
|
// RESTSCREEN([nTop, nLeft, nBottom, nRight,] cBuffer)
|
|
// Restores previously saved screen.
|
|
func RestScreen(t *hbrt.Thread) {
|
|
nParams := t.ParamCount()
|
|
t.Frame(nParams, 0)
|
|
defer t.EndProc()
|
|
fmt.Print("\033[u") // restore cursor
|
|
fmt.Print("\033[?47l") // restore screen (alt buffer)
|
|
t.RetNil()
|
|
}
|
|
|
|
// ALERT(cMessage [, aOptions [, cColor [, nDelay]]]) → nChoice
|
|
// Displays a modal alert dialog in the terminal.
|
|
func Alert(t *hbrt.Thread) {
|
|
nParams := t.ParamCount()
|
|
t.Frame(nParams, 0)
|
|
defer t.EndProc()
|
|
|
|
msg := t.Local(1).AsString()
|
|
|
|
// Parse options array
|
|
options := []string{"Ok"}
|
|
if nParams >= 2 && !t.Local(2).IsNil() {
|
|
arr := t.Local(2).AsArray()
|
|
if arr != nil && len(arr.Items) > 0 {
|
|
options = make([]string, len(arr.Items))
|
|
for i, v := range arr.Items {
|
|
options[i] = v.AsString()
|
|
}
|
|
}
|
|
}
|
|
|
|
// Calculate box dimensions
|
|
lines := strings.Split(msg, ";")
|
|
maxW := 0
|
|
for _, l := range lines {
|
|
if len(l) > maxW {
|
|
maxW = len(l)
|
|
}
|
|
}
|
|
optLine := strings.Join(options, " ")
|
|
if len(optLine) > maxW {
|
|
maxW = len(optLine)
|
|
}
|
|
maxW += 4 // padding
|
|
|
|
// Draw centered box
|
|
row := 8
|
|
col := (80 - maxW) / 2
|
|
if col < 0 {
|
|
col = 0
|
|
}
|
|
|
|
// Top border
|
|
fmt.Printf("\033[%d;%dH\033[7m%s\033[0m", row, col+1, strings.Repeat(" ", maxW))
|
|
row++
|
|
// Message lines
|
|
for _, l := range lines {
|
|
padded := fmt.Sprintf(" %-*s", maxW-2, l)
|
|
if len(padded) > maxW {
|
|
padded = padded[:maxW]
|
|
}
|
|
fmt.Printf("\033[%d;%dH\033[7m%s\033[0m", row, col+1, padded)
|
|
row++
|
|
}
|
|
// Blank line
|
|
fmt.Printf("\033[%d;%dH\033[7m%s\033[0m", row, col+1, strings.Repeat(" ", maxW))
|
|
row++
|
|
// Options line centered
|
|
optPad := (maxW - len(optLine)) / 2
|
|
optDisplay := fmt.Sprintf("%s%s%s", strings.Repeat(" ", optPad), optLine, strings.Repeat(" ", maxW-optPad-len(optLine)))
|
|
fmt.Printf("\033[%d;%dH\033[7m%s\033[0m", row, col+1, optDisplay)
|
|
row++
|
|
// Bottom border
|
|
fmt.Printf("\033[%d;%dH\033[7m%s\033[0m", row, col+1, strings.Repeat(" ", maxW))
|
|
|
|
// Wait for selection
|
|
selected := 1
|
|
for {
|
|
nKey := ReadKey()
|
|
SetLastKey(nKey)
|
|
switch nKey {
|
|
case 4, 9: // Right, Tab
|
|
selected++
|
|
if selected > len(options) {
|
|
selected = 1
|
|
}
|
|
case 19: // Left
|
|
selected--
|
|
if selected < 1 {
|
|
selected = len(options)
|
|
}
|
|
case 13: // Enter
|
|
t.RetInt(int64(selected))
|
|
return
|
|
case 27: // ESC
|
|
t.RetInt(0)
|
|
return
|
|
}
|
|
}
|
|
}
|