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