DOS-style text screen for FreeBASIC. Two backends: an SDL2 windowed renderer (full feature set) and a headless TTY renderer for direct terminal output (no SDL2 needed). Single include, QBasic-feel API. Target: Windows, Linux, FreeBASIC 1.10.1.
' SDL2 backend (default — full features, requires SDL2.dll): #include once "vt/vt.bi" ' TTY backend (no SDL2, outputs to terminal — experimental): #Define VT_TTY #include once "vt/vt.bi"
' Minimal hello-world program. #include once "vt/vt.bi" vt_title "Hello" vt_screen VT_SCREEN_0 ' 80x25, 8x16 font vt_color VT_YELLOW, VT_BLACK vt_print_center 12, "Hello, World!" vt_color VT_LIGHT_GREY, VT_BLACK vt_print_center 14, "Press any key..." vt_present() vt_sleep 0 ' wait for keypress
VT selects a rendering backend at compile time via a #Define placed
before the include. Without any define the SDL2 backend is used.
| Define | Backend | Description |
|---|---|---|
| (none) | SDL2 | Full feature set. Opens an SDL2 window with CP437 font, palette, mouse, sound, copy/paste. Requires SDL2.dll / libsdl2. |
| VT_USE_ANSI | SDL2 + ANSI parser | SDL2 backend plus ANSI escape sequence parsing inside vt_print. Useful when string data already contains ANSI color codes. Supported: SGR (ESC[…m), CUP (ESC[row;colH), ED (ESC[2J). |
| VT_TTY | TTY (headless) | Outputs directly to the terminal via ANSI escape sequences. No SDL2 required. Implies VT_USE_ANSI. Experimental. |
VT_TTY implies VT_USE_ANSI automatically — you do not need both.#define VT_USE_SOUND combined with #define VT_TTY produces a compile error (intentional).
The TTY backend uses ANSI escape sequences to render the cell grid directly in the
calling terminal. It shares the same API as the SDL2 backend; functions that have no
TTY equivalent silently return -1 or no-op.
| Platform | Status |
|---|---|
| Linux — any terminal emulator | Tested, fully working |
| Windows 10 build 1511+ | Implemented — community testing welcome |
| Windows < 10 (Win7 etc.) | Graceful no-op: console opens, does nothing, no crash. vt_screen returns -1. |
| Function / Feature | Behaviour in TTY mode |
|---|---|
| vt_mouse, vt_getmouse, vt_setmouse, vt_mouselock | Return -1 / no-op silently |
| vt_copypaste | No-op silently |
| vt_loadfont, vt_font_reset | Return -1 silently |
| vt_key_held | Always returns 0 (no live key-state query in termios / ReadConsoleInput) |
| VT_USE_SOUND + VT_TTY | Compile error (intentional) |
| VT_BLINK | Emitted but depends on terminal support — TERM=linux raw console does not implement it |
| CP437 chars ≥ 128 (box-drawing, shade blocks…) | Sent as raw bytes; terminals expect UTF-8, so they show as garbage. UTF-8 translation is a planned future improvement. |
' Compile with: fbc -d VT_TTY myprogram.bas #Define VT_TTY #include once "vt/vt.bi" vt_screen VT_SCREEN_0 vt_cls() vt_color VT_BRIGHT_GREEN vt_locate 1, 1 vt_print "Hello from TTY!" vt_color VT_YELLOW vt_locate 3, 1 vt_print "Your name: " vt_present() Dim s As String = vt_input(20) vt_locate 5, 1 vt_print "Got: " & s vt_present() vt_sleep 0 vt_shutdown()
Passed as the mode argument to vt_screen.
| Constant | Value | Columns × Rows | Glyph | Logical resolution |
|---|---|---|---|---|
| VT_SCREEN_0 | 0 | 80 × 25 | 8 × 16 | 640 × 400 |
| VT_SCREEN_2 | 2 | 80 × 25 | 8 × 8 | 640 × 200 |
| VT_SCREEN_9 | 9 | 80 × 25 | 8 × 14 | 640 × 350 |
| VT_SCREEN_12 | 12 | 80 × 30 | 8 × 16 | 640 × 480 |
| VT_SCREEN_13 | 13 | 40 × 25 | 8 × 8 | 320 × 200 |
| VT_SCREEN_EGA43 | 43 | 80 × 43 | 8 × 8 | 640 × 344 |
| VT_SCREEN_VGA50 | 50 | 80 × 50 | 8 × 8 | 640 × 400 |
| VT_SCREEN_TILES | 100 | 40 × 25 | 16 × 16 | 640 × 400 |
VT_SCREEN_TILES uses the 8x8 font data scaled to 16x16 glyphs;
useful for tile-based games where square cells are desirable.
Passed as the flags argument to vt_screen. Combine with Or.
| Constant | Value | Description |
|---|---|---|
| VT_WINDOWED | 1 | Start in a resizable window (default). |
| VT_FULLSCREEN_ASPECT | 2 | Fullscreen desktop, integer-scaled, black letterbox bars. |
| VT_FULLSCREEN_STRETCH | 4 | Fullscreen, stretches to fill the entire display. |
| VT_NO_RESIZE | 8 | Prevent the user from manually resizing the window. |
| VT_VSYNC | 16 | Enable vertical sync. Off by default. |
| VT_WINDOWED_MAX | 32 | Start maximized (regular window, not fullscreen). Avoid combining with VT_NO_RESIZE. |
Used with vt_color, vt_cls,
vt_set_cell, etc.
These are values of the VT_COLOR_IDX enum (0–15).
OR any foreground color with VT_BLINK to enable character blinking.
| Constant | Value | Constant | Value |
|---|---|---|---|
| VT_BLACK | 0 | VT_DARK_GREY | 8 |
| VT_BLUE | 1 | VT_BRIGHT_BLUE | 9 |
| VT_GREEN | 2 | VT_BRIGHT_GREEN | 10 |
| VT_CYAN | 3 | VT_BRIGHT_CYAN | 11 |
| VT_RED | 4 | VT_BRIGHT_RED | 12 |
| VT_MAGENTA | 5 | VT_BRIGHT_MAGENTA | 13 |
| VT_BROWN | 6 | VT_YELLOW | 14 |
| VT_LIGHT_GREY | 7 | VT_WHITE | 15 |
| VT_BLINK | 16 | OR with any fg to make it blink. | |
' Example: blinking white text on blue: vt_color VT_WHITE Or VT_BLINK, VT_BLUE
vt_inkey and vt_getkey return a packed ULong.
Use these macros to extract fields from it.
| Macro | Description |
|---|---|
| VT_CHAR(k) | ASCII character code (0 if non-printable). |
| VT_SCAN(k) | VT scancode (bits 16–27). Always compare via this macro, never directly against k. |
| VT_REPEAT(k) | 1 if this is an auto-repeat event (key held down), 0 otherwise. |
| VT_SHIFT(k) | 1 if Shift was held when the key was pressed. |
| VT_CTRL(k) | 1 if Ctrl was held when the key was pressed. |
| VT_ALT(k) | 1 if Alt was held when the key was pressed. |
' Typical usage pattern:
Dim k As ULong = vt_getkey()
Select Case VT_SCAN(k)
Case VT_KEY_UP : ' move up
Case VT_KEY_DOWN : ' move down
Case VT_KEY_ENTER : ' confirm
Case Else
If VT_CHAR(k) = Asc("q") Then End
End Select
Compare these against VT_SCAN(k). Never compare them directly against
the raw key value returned by vt_inkey.
| Constant | Value | Constant | Value |
|---|---|---|---|
| VT_KEY_F1 | 59 | VT_KEY_F7 | 65 |
| VT_KEY_F2 | 60 | VT_KEY_F8 | 66 |
| VT_KEY_F3 | 61 | VT_KEY_F9 | 67 |
| VT_KEY_F4 | 62 | VT_KEY_F10 | 68 |
| VT_KEY_F5 | 63 | VT_KEY_F11 | 133 |
| VT_KEY_F6 | 64 | VT_KEY_F12 | 134 |
| Constant | Value | Constant | Value |
|---|---|---|---|
| VT_KEY_UP | 72 | VT_KEY_HOME | 71 |
| VT_KEY_DOWN | 80 | VT_KEY_END | 79 |
| VT_KEY_LEFT | 75 | VT_KEY_PGUP | 73 |
| VT_KEY_RIGHT | 77 | VT_KEY_PGDN | 81 |
| VT_KEY_INS | 82 | VT_KEY_DEL | 83 |
| Constant | Value | Constant | Value |
|---|---|---|---|
| VT_KEY_ESC | 1 | VT_KEY_LSHIFT | 42 |
| VT_KEY_ENTER | 28 | VT_KEY_RSHIFT | 54 |
| VT_KEY_BKSP | 14 | VT_KEY_LCTRL | 29 |
| VT_KEY_TAB | 15 | VT_KEY_RCTRL | 157 |
| VT_KEY_SPACE | 57 | VT_KEY_LALT | 56 |
| VT_KEY_LWIN | 219 | VT_KEY_RALT | 184 |
| VT_KEY_RWIN | 220 |
Bitmask values returned in the btns parameter of vt_getmouse.
| Constant | Value | Description |
|---|---|---|
| VT_MOUSE_BTN_LEFT | 1 | Left button (bit 0). |
| VT_MOUSE_BTN_RIGHT | 2 | Right button (bit 1). |
| VT_MOUSE_BTN_MIDDLE | 4 | Middle button (bit 2). |
Passed to vt_copypaste. Combinable with Or.
| Constant | Value | Description |
|---|---|---|
| VT_CP_DISABLED | 0 | Default. No keys reserved, no mouse events captured by VT. |
| VT_CP_MOUSE | 1 | LMB drag selects, RMB copies selection, MMB pastes (paste: vt_input only). |
| VT_CP_KBD | 2 | Shift+arrows select, Ctrl+INS copies, Shift+INS pastes (paste: vt_input only). |
| Constant | Description |
|---|---|
| VT_NEWLINE | Chr(10) — newline character for use with vt_print. |
| VT_VIDEO | Value 0. The display page index. Always page 0. |
Available when #define VT_USE_MATH is placed before
#include once "vt/vt.bi".
See the Math section for the full function reference.
IIf() which evaluates both sides — avoid passing
expressions with side effects (function calls, increments) as arguments.
| Macro | Description |
|---|---|
| VT_MIN(a, b) | Returns the smaller of two values. |
| VT_MAX(a, b) | Returns the larger of two values. |
| VT_CLAMP(v, lo, hi) | Clamps v to the range [lo .. hi]. |
| VT_SIGN(x) | Returns -1, 0, or 1 depending on the sign of x. |
Available when #define VT_USE_SOUND is placed before
#include once "vt/vt.bi".
See the Sound section for full details.
wave parameter to vt_sound| Constant | Value | Description |
|---|---|---|
| VT_WAVE_SQUARE | 0 | PC speaker / chiptune square wave. Default. |
| VT_WAVE_TRIANGLE | 1 | NES triangle channel — softer, bass feel. |
| VT_WAVE_SINE | 2 | Pure sine tone. |
| VT_WAVE_NOISE | 3 | 15-bit LFSR noise — NES percussion / static feel. |
blocking parameter to vt_sound| Constant | Value | Description |
|---|---|---|
| VT_SOUND_BLOCKING | 1 | Wait for the note to finish. The window stays alive during the wait. Default. |
| VT_SOUND_BACKGROUND | 0 | Queue samples and return immediately. Use vt_sound_wait to sync. |
Available when #define VT_USE_SORT is placed before
#include once "vt/vt.bi".
See the Sort section for the full function reference.
| Constant | Value | Description |
|---|---|---|
| VT_ASCENDING | 0 | Sort direction for vt_sort: smallest value first. |
| VT_DESCENDING | 1 | Sort direction for vt_sort: largest value first. |
Available when #define VT_USE_TUI is placed before
#include once "vt/vt.bi".
See the TUI Widgets section for the full function reference.
VT_TUI_RET enumReturned by vt_tui_dialog and used as .ret values in form items.
| Constant | Value | Description |
|---|---|---|
| VT_RET_CANCEL | 0 | Cancel / No action taken. |
| VT_RET_OK | 1 | OK confirmed. |
| VT_RET_YES | 2 | Yes confirmed. |
| VT_RET_NO | 3 | No confirmed. |
| Constant | Value | Description |
|---|---|---|
| VT_DLG_OK | 1 | Single OK button. |
| VT_DLG_OKCANCEL | 2 | OK and Cancel buttons. |
| VT_DLG_YESNO | 3 | Yes and No buttons. |
| VT_DLG_YESNOCANCEL | 4 | Yes, No, and Cancel buttons. |
| VT_DLG_NO_ESC | 8 | Combinable modifier: Escape does nothing. Forces an explicit button choice. |
| Constant | Value | Applies to | Description |
|---|---|---|---|
| VT_TUI_WIN_CLOSEBTN | 1 | vt_tui_window | Render [x] at the right end of the title bar. Visual only — caller checks mouse with vt_tui_mouse_in_rect. |
| VT_TUI_WIN_SHADOW | 2 | vt_tui_window | Draw a one-cell drop shadow (right strip + bottom strip). |
| VT_TUI_PROG_LABEL | 1 | vt_tui_progress | Print a centred nn% label on the bar. |
| VT_TUI_FD_DIRS | 1 | vt_tui_file_dialog | Defined but redundant — directories are always included in the listing. |
| VT_TUI_FD_DIRSONLY | 2 | vt_tui_file_dialog | Show only subdirectories. Enter or OK on a directory entry confirms it. |
| VT_TUI_ED_READONLY | 1 | vt_tui_editor | View and scroll only — no editing. Text cursor is hidden. |
| Constant | Value | Description |
|---|---|---|
| VT_FORM_INPUT | 0 | Single-line text input field. |
| VT_FORM_BUTTON | 1 | Push button. |
| Constant | Value | Description |
|---|---|---|
| VT_FORM_PENDING | -2 | Form is still active — call again next frame. |
| VT_FORM_CANCEL | -1 | Escape was pressed — discard changes. |
| button .ret value (≥ 0) | — | An activated button's .ret value, e.g. VT_RET_OK. |
vt_tui_menubar_handle returns
group * 1000 + item_index (both 1-based) when an item is activated,
or 0 while idle or after Escape closes a dropdown.
| Macro | Description |
|---|---|
| VT_TUI_MENU_GROUP(r) | Extract the 1-based group index from a menu return value. |
| VT_TUI_MENU_ITEM(r) | Extract the 1-based item index within that group. |
' File=group 1, Edit=group 2
' File/Open = 1002, Edit/Copy = 2002
Select Case VT_TUI_MENU_GROUP(r)
Case 1 ' File
Select Case VT_TUI_MENU_ITEM(r)
Case 1 : ' New
Case 2 : ' Open
End Select
Case 2 ' Edit
Select Case VT_TUI_MENU_ITEM(r)
Case 1 : ' Cut
Case 2 : ' Copy
End Select
End Select
Open or reopen the virtual text screen. Calling it again closes and reinitializes with the new settings.
| Parameter | Type | Default | Description |
|---|---|---|---|
| mode | Long | VT_SCREEN_0 | Screen mode. One of the VT_SCREEN_* constants. |
| flags | Long | VT_WINDOWED | Window behaviour flags. One or more VT_WINDOWED / VT_FULLSCREEN_* constants combined with Or. |
| pages | Long | 1 | Number of cell buffers to allocate (1–8). Page 0 is always VT_VIDEO. Extra pages are used with vt_page / vt_pcopy. |
0 = success-1 = SDL_Init failed-2 = SDL window creation failed-3 = SDL renderer creation failed-4 = font texture creation failed-5 = page buffer memory allocation failed0 = success-1 = Windows < 10: ENABLE_VIRTUAL_TERMINAL_PROCESSING not supported. All subsequent calls silently no-op.-6 = page buffer memory allocation failed
vt_screen to set the window title before the window appears (SDL2 only).vt_screen to enable the scrollback buffer (SDL2 only).vt_screen to enable selection/clipboard support (SDL2 only).
TERM=linux environment variable, prints a clear error message, and terminates
the program immediately rather than hanging.Allocate (or resize, or disable) the scrollback history buffer. Must be called after vt_screen. Calling it again clears any existing history.
| Parameter | Type | Default | Description |
|---|---|---|---|
| lines | Long | none | Number of lines of history to keep. Pass 0 to disable scrollback and free the buffer. |
Explicitly tear down the VT screen and free all SDL resources. This is optional: on normal program exit, SDL cleans itself up. Use it when you need to close the window mid-program.
Set the window title. Safe to call before or after vt_screen. Before: the title is stored and applied when the window is created. After: applied immediately to the live window.
| Parameter | Type | Default | Description |
|---|---|---|---|
| txt | String | none | The title bar text. |
Register a callback function that is invoked when the user clicks the window
[X] button. Without a callback the default behaviour is to
shut down immediately and call End — the program exits with
no chance to intervene. With a callback registered the library calls your
function first and acts on the return value.
| Parameter | Type | Description |
|---|---|---|
| cb | Function() As Byte |
Pointer to a Function() As Byte. Pass 0 to
remove the callback and restore the default auto-close behaviour.
|
0 — allow the close: library calls vt_shutdown() then End, identical to the default.1 — veto the close: window stays open, the event is discarded, program continues normally.
vt_inkey,
vt_getkey, vt_sleep, vt_present, and
all drawing functions work correctly. This means you can draw a confirmation
dialog and wait for a keypress directly inside the callback.vt_shutdown() and End
explicitly, the re-acquire never runs — that is fine.
' Declare the callback with the exact signature:
Function on_close() As Byte
' Ask the user before closing:
vt_color VT_BLACK, VT_LIGHT_GREY
vt_locate 25, 1
vt_print " Unsaved changes! Quit? (Y/N)" & String(48, " ")
vt_present()
Dim ans As String = vt_getchar("YyNn")
If LCase(ans) = "y" Then Return 0 ' allow close
' Restore UI and veto:
draw_statusbar()
vt_present()
Return 1
End Function
' Register after vt_screen:
vt_screen VT_SCREEN_0
vt_on_close(@on_close)
' Remove callback (restore default auto-close):
vt_on_close(0)
Set the colour of the letterbox bars that appear outside the logical viewport in windowed or fullscreen-aspect modes. Default is black (0, 0, 0).
| Parameter | Type | Default | Description |
|---|---|---|---|
| r | Long | none | Red channel (0–255). |
| g | Long | none | Green channel (0–255). |
| b | Long | none | Blue channel (0–255). |
Composite the visible page and flip it to the screen.
Always renders the visible page (vis_page),
which may differ from the active drawing page when using multi-page mode.
You are responsible for calling this; drawing functions such as
vt_print and vt_cls do not call it automatically.
vt_present
automatically (throttled) to keep the display alive during wait loops.
Clear the active scroll region to spaces using the current foreground and background colours. Resets the cursor to row 1, column 1 of the scroll region. Does not call vt_present.
| Parameter | Type | Default | Description |
|---|---|---|---|
| bg | Long | -1 | Background colour index (0–15). -1 keeps the current background colour. |
Set the active foreground and/or background colour used by vt_print, vt_cls, and vt_input.
| Parameter | Type | Default | Description |
|---|---|---|---|
| fg | Long | -1 | Foreground colour index (0–15), optionally OR'd with VT_BLINK (16). -1 keeps the current foreground. |
| bg | Long | -1 | Background colour index (0–15). -1 keeps the current background. |
' Set only foreground, leave background unchanged: vt_color VT_BRIGHT_GREEN ' Set both: vt_color VT_WHITE, VT_BLUE ' Blinking foreground: vt_color VT_RED Or VT_BLINK, VT_BLACK
Move the text cursor and optionally change its visibility and glyph.
Coordinates are 1-based. Parameters with value -1 are left unchanged.
| Parameter | Type | Default | Description |
|---|---|---|---|
| row | Long | -1 | Target row, 1-based. Out-of-range values are ignored. -1 keeps current row. |
| col | Long | -1 | Target column, 1-based. Out-of-range values are ignored. -1 keeps current column. |
| vis | Long | -1 | Cursor visibility: 1 = visible, 0 = hidden, -1 = keep current. |
| cursor_ch | Long | -1 | CP437 glyph index (0–255) used to draw the cursor. Default is 219 (solid block). -1 keeps current. |
Print a string at the current cursor position using the active colour.
The cursor advances after each character; the display is not automatically flipped.
There is no implicit newline — use VT_NEWLINE (Chr(10))
within the string for line breaks. CRLF pairs are collapsed to a single line feed.
| Parameter | Type | Default | Description |
|---|---|---|---|
| txt | String | none | The string to print. May contain Chr(10) for newlines and Chr(13)+Chr(10) for CRLF (collapsed to one newline). |
' Position, colour, print, flip: vt_locate 5, 1 vt_color VT_CYAN, VT_BLACK vt_print "Line one" & VT_NEWLINE & "Line two" vt_present()
Print a string horizontally centered on the given screen row. Uses the current colour and does not call vt_present.
| Parameter | Type | Default | Description |
|---|---|---|---|
| row | Long | none | Target row, 1-based. |
| txt | String | none | The string to center and print. |
Enable or disable automatic scrolling of the scroll region. When disabled, text printed past the bottom of the scroll region stays on the last row. Scroll is enabled by default.
| Parameter | Type | Default | Description |
|---|---|---|---|
| state | Byte | none | 1 = enable scrolling, 0 = disable. |
Read the character, foreground colour, and background colour of a single cell on the active work page. Coordinates are 1-based. Out-of-range coordinates are ignored.
| Parameter | Type | Default | Description |
|---|---|---|---|
| col | Long | none | Column, 1-based. |
| row | Long | none | Row, 1-based. |
| ch | UByte (ByRef) | none | Receives the CP437 character code. |
| fg | UByte (ByRef) | none | Receives the foreground colour index (may include blink bit). |
| bg | UByte (ByRef) | none | Receives the background colour index. |
Write a character cell directly on the active work page without moving the cursor. Does not call vt_present. Out-of-range coordinates are silently ignored.
| Parameter | Type | Default | Description |
|---|---|---|---|
| col | Long | none | Column, 1-based. |
| row | Long | none | Row, 1-based. |
| ch | UByte | none | CP437 character code (0–255). |
| fg | UByte | none | Foreground colour index (0–15), optionally OR'd with 16 for blink. |
| bg | UByte | none | Background colour index (0–15). |
Move the scrollback view by a number of lines. Positive values scroll toward older history; negative values scroll toward the live view. Scrollback must be enabled with vt_scrollback first. Does not call vt_present.
| Parameter | Type | Default | Description |
|---|---|---|---|
| amount | Long | none | Lines to scroll. Positive = back in history, negative = toward live view. Clamped to valid range automatically. |
1 = offset changed0 = already at limit, amount is zero, or scrollback is disabled
vt_scroll is called automatically by the library when the user presses
the appropriate scrollback keys. You only need to call it directly if you want to
drive scrollback from your own input (e.g. a custom mouse wheel handler).
Translate a screen row number to the corresponding live buffer row while
the scrollback view is offset. Useful when implementing click-to-locate
functionality: call this before snapping back to the live view with
vt_scroll(-99999).
| Parameter | Type | Default | Description |
|---|---|---|---|
| screen_row | Long | none | 1-based screen row as seen during the current scrollback offset. |
screen_row.0 if the screen row is showing pure scrollback history with no live equivalent.screen_row) when scrollback is not active or the row
is a fixed row outside the scroll region.
' Click-to-locate pattern during scrollback:
Dim lr As Long = vt_screen_to_live_row(clicked_row)
If lr > 0 Then
vt_scroll -99999 ' snap back to live
vt_locate lr, clicked_col
End If
Restrict the scroll region and print region to a range of rows (like QBasic's
VIEW PRINT). Rows outside this range are unaffected by
vt_cls, vt_print, and scrolling.
Call with no arguments (or both -1) to reset to the full screen.
| Parameter | Type | Default | Description |
|---|---|---|---|
| top_row | Long | -1 | First row of the scroll region, 1-based. -1 resets both bounds to the full screen. |
| bot_row | Long | -1 | Last row of the scroll region, 1-based. Must be ≥ top_row. |
VT intercepts certain key combinations internally before they reach your program. These bindings are hardwired into the event pump and require no setup. Which keys are active depends on the active backend and, in TTY mode, on the host environment.
Scrollback must be enabled with vt_scrollback for these to have any effect. Any key not listed below exits scrollback and returns to the live view.
| Key | SDL2 (windowed / fullscreen) |
VT_TTY terminal emulator (XFCE, xterm…) |
VT_TTY raw Linux console (Ctrl+Alt+F1–F6) |
VT_TTY Windows 10+ console |
|---|---|---|---|---|
| Shift+PgUp | ✓ scroll back 1 line | ✗ intercepted by terminal | ✗ intercepted by kernel | ✓ scroll back 1 line |
| Shift+PgDn | ✓ scroll forward 1 line | ✗ intercepted by terminal | ✗ intercepted by kernel | ✓ scroll forward 1 line |
| Ctrl+Shift+PgUp | ✓ scroll back ½ page | ✗ intercepted by terminal | ✗ intercepted by kernel | ✓ scroll back ½ page |
| Ctrl+Shift+PgDn | ✓ scroll forward ½ page | ✗ intercepted by terminal | ✗ intercepted by kernel | ✓ scroll forward ½ page |
| Alt+PgUp | — | ✓ scroll back 1 line | ✗ unbound in default keymap | — |
| Alt+PgDn | — | ✓ scroll forward 1 line | ✗ unbound in default keymap | — |
| Alt+– (minus) | — | ✓ scroll back 1 line | ✓ scroll back 1 line | — |
| Alt+. (period) | — | ✓ scroll forward 1 line | ✓ scroll forward 1 line | — |
ESC[5;3~).
On a raw Linux virtual console (Ctrl+Alt+F1–F6),
both Shift+PgUp and Alt+PgUp are consumed by the kernel
VT driver before reaching the program — but Alt+– and
Alt+. are present in the default Linux keymap
(Meta_minus / Meta_period) and pass through cleanly,
making them the universal raw-TTY scrollback binding that works everywhere.
Available only in SDL2 mode when vt_copypaste has been called to enable it. These keys are never intercepted in TTY mode.
| Key | Effect |
|---|---|
| Ctrl+Ins | Copy current selection to clipboard. |
| Shift+Ins | Paste clipboard text (inside vt_input only). |
| Shift+Arrow / Home / End | Extend keyboard selection. |
| Left mouse drag | Select a region. |
| Right mouse button | Copy current selection. |
| Middle mouse button | Paste (inside vt_input only). |
Used by vt_tui_menubar_draw to open a
menu group by its first letter. VT delivers these as a keyrec with
VT_ALT(k) = 1 and VT_CHAR(k) set to the letter.
The detection method is backend-dependent but transparent to your code:
| Backend | How Alt+letter is detected |
|---|---|
| SDL2 | SDL suppresses its text-input event while Alt is held. VT recovers the letter from the raw SDL scancode in the key-down event. |
| VT_TTY Linux | Terminal sends ESC + letter byte.
VT's escape-sequence parser intercepts this and sets the Alt bit. |
| VT_TTY Windows | LEFT_ALT_PRESSED / RIGHT_ALT_PRESSED flags
in the Win32 KEY_EVENT_RECORD. Folded into bit 31 of the keyrec. |
-1 together to reset. Passing one as -1
while the other has a valid value only applies the valid value.
Pages are separate full-screen cell buffers. Multiple pages are allocated at
vt_screen time via the pages argument.
Page 0 (VT_VIDEO) is always the startup visible page.
Up to 8 pages are supported (indices 0–7).
Set the active drawing page and the visible display page independently. All drawing commands write to the work page; vt_present reads from the vis page. Invalid indices are silently ignored.
| Parameter | Type | Default | Description |
|---|---|---|---|
| work | Long | none | Drawing page index (0 ≤ work < pages). All drawing commands go here. |
| vis | Long | none | Display page index (0 ≤ vis < pages). This page is shown by vt_present. |
' Classic double-buffer setup: draw on page 1, display page 0. vt_page 1, VT_VIDEO ' ... draw freely on page 1 ... vt_pcopy 1, VT_VIDEO ' push finished frame to page 0 vt_present() ' Swap back when done: vt_page VT_VIDEO, VT_VIDEO
Copy one page buffer to another (equivalent to QBasic PCOPY).
Marks the display dirty but does not call vt_present.
src = dst is a no-op. Invalid indices are silently ignored.
| Parameter | Type | Default | Description |
|---|---|---|---|
| src | Long | none | Source page index. |
| dst | Long | none | Destination page index. |
The palette is a flat array of 48 bytes: 16 colours × 3 channels (R, G, B).
Entry idx starts at byte idx * 3.
The default is the CGA palette.
Set a single palette entry, or reset all 16 colours to CGA defaults.
| Parameter | Type | Default | Description |
|---|---|---|---|
| idx | Long | -1 | Colour index (0–15). Pass -1 to reset all 16 entries to the CGA defaults (r, g, b are ignored in this case). |
| r | Long | -1 | Red channel (0–255). When idx ≥ 0, always pass all three channels explicitly. |
| g | Long | -1 | Green channel (0–255). |
| b | Long | -1 | Blue channel (0–255). |
idx ≥ 0, always supply all three colour channels.
The defaults (-1) are only meaningful for the reset case (idx = -1).
Omitting channels when setting a specific entry produces 255 (white) because
-1 And 255 = 255.
' Replace colour index 4 (Red) with a custom orange: vt_palette 4, 220, 120, 0 ' Reset all 16 colours back to CGA defaults: vt_palette
Bulk-read all 16 palette entries into a caller-supplied UByte array of at least 48 elements.
| Parameter | Type | Default | Description |
|---|---|---|---|
| pal() | UByte array | none | Array to receive 48 bytes of palette data (16 colours × R, G, B). |
Bulk-write all 16 palette entries from a caller-supplied UByte array of 48 elements.
| Parameter | Type | Default | Description |
|---|---|---|---|
| pal() | UByte array | none | Array containing 48 bytes of palette data (16 colours × R, G, B). |
Returns the total number of columns in the current screen mode.
Returns the current cursor row (1-based).
Returns the current cursor column (1-based).
Returns the total number of rows in the current screen mode.
Block until a printable character (ASCII 32–255) is pressed.
If allowed is non-empty, only characters within that string are accepted;
all others are discarded silently. Returns the accepted character as a one-character String.
| Parameter | Type | Default | Description |
|---|---|---|---|
| allowed | String | "" | Whitelist of accepted characters. Empty string = accept any printable character. |
' Accept only Y or N:
vt_print "Continue? (Y/N) "
vt_present()
Dim ans As String = UCase(vt_getchar("YyNn"))
Block until any key (including non-printable and special keys) is pressed.
Returns the same packed ULong format as vt_inkey;
use the VT_SCAN / VT_CHAR / VT_SHIFT / VT_CTRL / VT_ALT macros to extract fields.
Non-blocking key read. Returns the next key from the buffer, or 0 if
no key is pending. Also pumps events, updates blink, and calls
vt_present if the display is dirty (throttled to 2 ms).
0 if the buffer is empty or the library is not ready.
' Game loop pattern:
Do
Dim k As ULong = vt_inkey()
If VT_SCAN(k) = VT_KEY_ESC Then Exit Do
' ... update and draw ...
vt_present()
Sleep 16, 1
Loop
Full-featured blocking single-line text editor at the current cursor position. Supports navigation (Home, End, Left, Right), insertion, deletion (Backspace, Del), and clipboard paste when copy/paste is enabled. Returns the entered string on Enter, or an empty string on Escape.
| Parameter | Type | Default | Description |
|---|---|---|---|
| max_len | Long | -1 | Maximum input length. -1 fills all remaining columns on the current row. |
| initial | String | "" | Pre-filled text shown at startup. Pressing Escape restores this text, clears the screen, and returns "". |
| allowed | String | "" | Whitelist of accepted characters. Empty = accept any printable ASCII. |
| cancelled | Byte Ptr | 0 (null) | Optional pointer to a Byte. Set to 1 on Escape, 0 on Enter. Pass 0 to ignore. |
"" (empty string) on Escape.
vt_input.
The editor uses the current vt_color settings throughout.
It does not call vt_present directly; the internal
vt_inkey calls handle presentation.
' Prompt with pre-filled default, numeric only, detect cancel:
Dim esc As Byte
vt_locate 10, 1
vt_print "Enter amount: "
vt_present()
Dim amount As String = vt_input(8, "0", "0123456789.", @esc)
If esc Then
vt_print "Cancelled" & VT_NEWLINE
Else
vt_print "Got: " & amount & VT_NEWLINE
End If
vt_present()
Discard all keys currently waiting in the internal key buffer.
Poll the real-time state of a key. Returns 1 if the key is physically
held down at the time of the call. Useful for smooth movement in game loops where
event-based repeat is not fast enough.
Accepts the same VT_KEY_* scancode constants used with VT_SCAN.
| Parameter | Type | Default | Description |
|---|---|---|---|
| vtscan | Long | none | A VT_KEY_* scancode constant. |
1 if the key is currently pressed.0 if not pressed, scancode not recognized, or library not ready.
' Smooth movement example: If vt_key_held(VT_KEY_LEFT) Then x -= 1 If vt_key_held(VT_KEY_RIGHT) Then x += 1
Configure the timing of auto-repeat events generated when a key is held down.
Default: 400 ms initial delay, 30 ms repeat rate.
Set either value to 0 to disable the corresponding phase of repeat.
| Parameter | Type | Default | Description |
|---|---|---|---|
| initial_ms | Long | 400 | Delay in milliseconds before the first repeat event fires. |
| rate_ms | Long | 30 | Interval in milliseconds between subsequent repeat events. |
Manually drain the SDL event queue. Normally called automatically by vt_inkey, vt_present, and vt_sleep. Rarely needed directly — useful in tight loops that never call any of those. Re-entrant safe (a second call while inside returns immediately).
Delay for a fixed number of milliseconds, or wait indefinitely for any keypress. Keeps the display alive (blink, present) during the wait.
| Parameter | Type | Default | Description |
|---|---|---|---|
| ms | Long | 0 | Delay in milliseconds. 0 = wait for any keypress. |
ms = 0, the keypress is consumed from the buffer.
When ms > 0, no key is consumed; the delay simply elapses.
Non-blocking read of the current mouse state.
All parameters are optional pointers; pass 0 to skip any field.
Must enable the mouse first with vt_mouse(1).
The wheel accumulator is reset to zero each time it is read.
Button state uses the VT_MOUSE_BTN_* bitmask constants.
| Parameter | Type | Default | Description |
|---|---|---|---|
| col | Long Ptr | 0 | Receives 1-based column. Pass 0 to skip. |
| row | Long Ptr | 0 | Receives 1-based row. Pass 0 to skip. |
| btns | Long Ptr | 0 | Receives button bitmask. Pass 0 to skip. |
| whl | Long Ptr | 0 | Receives accumulated wheel delta (+up / −down) since last read. Reset to 0 on read. Pass 0 to skip. |
0 = ok-1 = mouse not enabled, or library not ready
' Read position and buttons: Dim mx As Long, my As Long, mb As Long vt_getmouse @mx, @my, @mb ' Read wheel only: Dim mw As Long vt_getmouse 0, 0, 0, @mw ' Read buttons only (FreeBASIC skips defaulted pointer params): vt_getmouse(,,@mb)
Enable or disable mouse tracking entirely. Enabling hides the OS cursor, starts tracking, and snaps the character cursor to the current physical mouse position. Disabling restores the OS cursor and clears button/wheel state.
| Parameter | Type | Default | Description |
|---|---|---|---|
| enabled | Byte | none | 1 = enable mouse tracking, 0 = disable. |
Enable or disable SDL window grab, which confines the mouse cursor to the window. Fullscreen modes enable this automatically at vt_screen time. Safe to call before or after vt_mouse(1).
| Parameter | Type | Default | Description |
|---|---|---|---|
| onoff | Byte | none | 1 = lock cursor inside window, 0 = unlock. |
Set the character cursor position and/or visibility.
Coordinates are 1-based. Pass -1 to leave any field unchanged.
Safe to call when the mouse is disabled; settings take effect when re-enabled.
| Parameter | Type | Default | Description |
|---|---|---|---|
| col | Long | -1 | Column, 1-based. Clamped to screen bounds. -1 = keep current. |
| row | Long | -1 | Row, 1-based. Clamped to screen bounds. -1 = keep current. |
| vis | Byte | -1 | 1 = show cursor, 0 = hide cursor, -1 = keep current. |
Configure the copy/paste and text selection system. Call after vt_screen. Clears any active selection.
| Parameter | Type | Default | Description |
|---|---|---|---|
| flags | Long | none | One or more VT_CP_* constants combined with Or. VT_CP_DISABLED (0) disables all interception. |
' Enable both keyboard and mouse copy/paste: vt_screen VT_SCREEN_0 vt_copypaste VT_CP_KBD Or VT_CP_MOUSE ' Disable entirely: vt_copypaste VT_CP_DISABLED
The .vts format is a compact binary screen dump:
4-byte magic "VTBS", 4-byte cols, 4-byte rows (all little-endian Long),
followed by cols × rows × 3 bytes of cell data (ch, fg, bg per cell,
left-to-right, top-to-bottom). Both functions operate on the active work page.
Load a .vts file into the active work page.
Validates the magic and screen dimensions before touching the cell buffer.
Sets the display dirty on success so the next vt_present
reflects the loaded content.
| Parameter | Type | Default | Description |
|---|---|---|---|
| fname | String | none | Path to the .vts file to load. |
0 = success-1 = library not ready (call vt_screen first)-2 = file could not be opened-3 = bad magic — not a valid .vts file-4 = dimension mismatch — file was saved in a different screen mode
Save the active work page to a .vts file.
| Parameter | Type | Default | Description |
|---|---|---|---|
| fname | String | none | Destination file path. Created or overwritten. |
0 = success-1 = library not ready (call vt_screen first)-2 = file could not be opened for writing
Restore the built-in embedded CP437 font selected by the current screen mode. Destroys any custom font loaded with vt_loadfont. Must be called after vt_screen.
0 = success-1 = library not ready-2 = surface or texture creation failed
Load a BMP font sheet and replace the active font texture at runtime. Uses SDL2 core only (no SDL_image). The loaded font stays active until vt_loadfont is called again, vt_font_reset is called, or vt_screen reinitializes the window.
| Parameter | Type | Default | Description |
|---|---|---|---|
| fname | String | none | Path to a BMP file. Any bit depth is accepted (converted internally). |
| sheet_w | Long | -1 | Expected BMP pixel width for validation. -1 = accept any width. |
| sheet_h | Long | -1 | Expected BMP pixel height for validation. -1 = accept any height. |
| mask_r | UByte | 0 | Red component of the background (transparent) colour in the BMP. |
| mask_g | UByte | 0 | Green component of the background colour. |
| mask_b | UByte | 0 | Blue component of the background colour. |
0 = success-1 = library not ready-2 = BMP load or surface creation failed-3 = image dimensions do not match the current screen mode glyph size (or explicit sheet_w / sheet_h mismatch)-4 = SDL texture upload failed
The layout is detected automatically from the BMP pixel dimensions relative to
the current screen mode's glyph size (gw × gh):
| Layout | BMP size | Example (8×8 glyphs) |
|---|---|---|
| 16×16 grid | 16⋅gw × 16⋅gh | 128 × 128 |
| 256×1 strip | 256⋅gw × gh | 2048 × 8 |
Common mask colours:
| Colour | mask_r | mask_g | mask_b |
|---|---|---|---|
| Black (default) | 0 | 0 | 0 |
| Magenta | 255 | 0 | 255 |
| White | 255 | 255 | 255 |
' Load a magenta-keyed grid font sheet:
Dim r As Long = vt_loadfont("myfont.bmp", -1, -1, 255, 0, 255)
If r <> 0 Then
vt_print "Font load failed: " & r & VT_NEWLINE
End If
Return a random Long in the inclusive range [lo .. hi].
Negative bounds are fully supported. If lo > hi the two values
are swapped silently. Seeding the RNG with Randomize is the caller's
responsibility — vt_rnd does not call it.
| Parameter | Type | Default | Description |
|---|---|---|---|
| lo | Long | none | Lower bound (inclusive). |
| hi | Long | none | Upper bound (inclusive). |
' Seed once at startup, then use freely: Randomize Timer Dim n As Long = vt_rnd(-15, 15) ' -15 to 15 Dim d As Long = vt_rnd(1, 6) ' die roll Dim x As Long = vt_rnd(1, vt_cols())
Generic in-place array sorting, permutation application, and Fisher-Yates shuffle. No SDL2 required — usable with or without a VT screen open.
VT_USE_SORT before the include.
Without it the entire extension is absent at compile time — zero overhead.
' Enable sort in your project: #define VT_USE_SORT #include once "vt/vt.bi"The two direction constants (VT_ASCENDING, VT_DESCENDING) are also part of this extension and documented in the Constants section.
Sort a 1D array in-place using Shellsort.
Overloaded for all numeric types (Byte, UByte, Short,
UShort, Long, ULong, LongInt,
ULongInt, Single, Double) and String.
The compiler selects the correct overload automatically — a single call works
for every supported type. Arrays with any LBound are handled correctly.
Two call forms are available: a direction constant, or a comparator callback for custom ordering.
ZString * N cannot be covered by a generic overload because the fixed length
N is part of the type in FreeBASIC — every distinct size would require its
own overload, which is not feasible.
ZString is a C-interop type; data intended for sorting should be stored as
String. If you do have a ZString * N array to sort, use the
index-sort pattern: sort a Long index array
with a comparator that reads the zstring array via the index value, then read results
in index order — no physical rearrangement of the zstring array is needed.
| Parameter | Type | Description |
|---|---|---|
| arr() | any supported 1D array | Array to sort in-place. |
| order | Long | VT_ASCENDING (0) or VT_DESCENDING (1). |
| Parameter | Type | Description |
|---|---|---|
| arr() | any supported 1D array | Array to sort in-place. |
| cmp | Function(As <T>, As <T>) As Long | Returns negative if a comes first, zero if equal, positive if b comes first. Pass with @. |
' Direction sort:
Dim nums(4) As Long = {5, 1, 4, 2, 3}
vt_sort nums(), VT_ASCENDING ' 1 2 3 4 5
vt_sort nums(), VT_DESCENDING ' 5 4 3 2 1
' String array, alphabetical:
Dim words(2) As String = {"cherry", "apple", "banana"}
vt_sort words(), VT_ASCENDING ' apple banana cherry
' Custom comparator: sort strings by character count (shortest first):
Function cmp_by_length(a As String, b As String) As Long
Return Len(a) - Len(b)
End Function
Dim tags(2) As String = {"hi", "hello", "hey"}
vt_sort tags(), @cmp_by_length ' hi hey hello
Apply a permutation index to a 1D array, physically rearranging its elements in-place. This is the companion function to the index-sort pattern — the standard technique for sorting tables stored as parallel arrays.
pidx() values are 0-based offsets from LBound(arr),
exactly matching the index arrays built and sorted by vt_sort.
The permutation index is consumed by the apply step — build a fresh index array
if you need to apply the same order to additional arrays.
Overloaded for the same types as vt_sort.
The pidx parameter is always Long.
| Parameter | Type | Description |
|---|---|---|
| arr() | any supported 1D array | Array to rearrange in-place. |
| pidx() | Long array | Permutation index. Values are 0-based offsets into arr. Produced by sorting an index array with vt_sort. |
FreeBASIC tables are often stored as several parallel arrays of equal length —
one per column. Because vt_sort operates on a single array,
you cannot sort all columns at once directly.
The index-sort pattern solves this in three steps without duplicating the data.
It also applies naturally to ZString arrays, which
vt_sort cannot overload directly.
Long index array {0, 1, 2, …} — one entry per row.vt_sort_apply on every parallel array to physically rearrange them.Shared variables, FreeBASIC's
lack of closures is not a problem — the comparator simply accesses the shared
arrays directly. This technique works for any array type including
ZString * N.
' Table stored as parallel arrays:
Dim Shared g_item(3) As String
Dim Shared g_qty (3) As Long
g_item(0) = "eggs" : g_qty(0) = 5
g_item(1) = "butter" : g_qty(1) = 2
g_item(2) = "milk" : g_qty(2) = 10
g_item(3) = "bread" : g_qty(3) = 3
' Index comparators peek into shared arrays via the index value:
Function cmp_by_qty(a As Long, b As Long) As Long
Return g_qty(a) - g_qty(b)
End Function
' Step 1: build index array.
Dim idx(3) As Long
Dim i As Long
For i = 0 To 3 : idx(i) = i : Next i
' Step 2: sort the index, not the data.
vt_sort idx(), @cmp_by_qty ' idx = {1,3,0,2} = butter,bread,eggs,milk
' Step 3a: read in index order (original arrays untouched):
For i = 0 To 3
Print g_item(idx(i)); " "; g_qty(idx(i))
Next i
' Step 3b: physically rearrange both arrays using the same index:
vt_sort_apply g_item(), idx()
vt_sort_apply g_qty(), idx()
' g_item() and g_qty() are now in qty-ascending order.
Shuffle a 1D array in-place using the Fisher-Yates algorithm, producing an
unbiased uniform random permutation. Seeding the RNG with Randomize
is the caller's responsibility — vt_sort_shuffle does not call it.
Overloaded for the same types as vt_sort.
| Parameter | Type | Description |
|---|---|---|
| arr() | any supported 1D array | Array to shuffle in-place. |
' Shuffle a deck of cards:
Randomize Timer
Dim deck(51) As Long
Dim i As Long
For i = 0 To 51 : deck(i) = i : Next i
vt_sort_shuffle deck()
' Shuffle a string list:
Dim names(3) As String = {"Alice", "Bob", "Carol", "Dave"}
vt_sort_shuffle names()
Pure FreeBASIC math helpers. No SDL2 required — usable with or without a VT screen open. Especially useful for game logic: grid distance, line of sight, smooth movement, matrix rotation.
VT_USE_MATH before the include.
Without it the entire extension is absent at compile time — zero overhead.
' Enable math in your project: #define VT_USE_MATH #include once "vt/vt.bi"The four convenience macros (VT_MIN, VT_MAX, VT_CLAMP, VT_SIGN) are also part of this extension and documented in the Constants section.
Wraps v into the inclusive range [lo .. hi] with roll-over.
Unlike VT_CLAMP, hitting an edge rolls around to the opposite side.
If hi − lo + 1 ≤ 0 the function returns lo.
| Parameter | Type | Description |
|---|---|---|
| v | Long | The value to wrap. |
| lo | Long | Lower bound (inclusive). |
| hi | Long | Upper bound (inclusive). |
' Menu cursor that wraps at the edges: cursor_row = vt_wrap(cursor_row + delta, 1, num_items) ' Toroidal map: player walks off the right edge, appears on the left: px = vt_wrap(px + 1, 0, map_width - 1)
Linear interpolation between a and b.
At t = 0 the result is a; at t = 1 the result is b.
t is not clamped — values outside [0..1] extrapolate beyond the endpoints.
| Parameter | Type | Description |
|---|---|---|
| a | Double | Start value (returned when t = 0). |
| b | Double | End value (returned when t = 1). |
| t | Double | Interpolation factor. Not clamped. |
' Animated value moving from 0 to 100 over time: Dim brightness As Double = vt_lerp(0, 100, elapsed / total) ' Blend two palette entries at the midpoint: Dim r As Double = vt_lerp(col_a.r, col_b.r, 0.5)
Moves cur toward tgt by amt without overshooting.
If cur = tgt it returns unchanged. amt should be positive.
| Parameter | Type | Description |
|---|---|---|
| cur | Long | Current value. |
| tgt | Long | Target value to approach. |
| amt | Long | Step size per call. Should be positive. |
' Smooth enemy movement: move one step per frame toward the player: enemy_x = vt_approach(enemy_x, player_x, 1) enemy_y = vt_approach(enemy_y, player_y, 1) ' Animated score counter that catches up 10 points per frame: display_score = vt_approach(display_score, real_score, 10)
Manhattan (taxicab) distance between two grid points.
Counts only orthogonal steps — no diagonals.
Equivalent to Abs(x2 − x1) + Abs(y2 − y1).
' Only attack if the player is within 4 orthogonal steps:
If vt_manhattan(enemy_x, enemy_y, player_x, player_y) <= 4 Then
attack()
End If
Chebyshev (king-moves) distance between two grid points.
Diagonal steps cost 1, the same as orthogonal — matches 8-direction movement.
Equivalent to VT_MAX(Abs(x2 − x1), Abs(y2 − y1)).
' Check if a target is within melee range (1 king-move):
If vt_chebyshev(ax, ay, bx, by) = 1 Then
melee_attack()
End If
Returns 1 if point (px, py) lies within the rectangle.
The rect is half-open: [rx, rx+rw) × [ry, ry+rh)
— the right and bottom edges are excluded. Returns 0 otherwise.
| Parameter | Type | Description |
|---|---|---|
| px | Long | Point X coordinate. |
| py | Long | Point Y coordinate. |
| rx | Long | Rectangle left edge. |
| ry | Long | Rectangle top edge. |
| rw | Long | Rectangle width in cells. |
| rh | Long | Rectangle height in cells. |
' Hit-test a mouse click against a button region:
Dim mcol As Long, mrow As Long, mbtns As Long
vt_getmouse @mcol, @mrow, @mbtns, 0
If (mbtns And VT_MOUSE_BTN_LEFT) AndAlso vt_in_rect(mcol, mrow, 10, 5, 20, 3) Then
button_clicked()
End If
Returns the number of characters that Str(n) or Print n would produce,
including the minus sign for negative numbers. Zero returns 1.
Uses LongInt internally to handle the full Long range without overflow.
| n | Returns |
|---|---|
| 0 | 1 |
| 1234 | 4 |
| -99 | 3 (minus + 2 digits) |
| -2147483648 | 11 |
' Right-align a score in a fixed-width column: Dim score As Long = 9870 Dim col As Long = 75 - vt_digits(score) ' compute start column vt_locate 1, col vt_print Str(score)
Walks every cell on the straight line from (x1, y1) to
(x2, y2) using Bresenham's line algorithm and calls
cb(x, y) for each cell in order. Both endpoints are included.
The callback is a Sub(x As Long, y As Long) — pass its address with @.
Because FreeBASIC has no closures, use module-level Shared variables to
give the callback access to map data or other state.
| Parameter | Type | Description |
|---|---|---|
| x1, y1 | Long | Start cell (included in walk). |
| x2, y2 | Long | End cell (included in walk). |
| cb | Sub(As Long, As Long) | Called once per cell on the line. Pass with @. |
' Draw a line of ASCII dashes on the text screen:
Sub draw_dash(x As Long, y As Long)
vt_set_cell x, y, Asc("-"), VT_WHITE, VT_BLACK
End Sub
vt_bresenham_walk 1, 1, 40, 15, @draw_dash
vt_present()
Line-of-sight check from (x1, y1) to (x2, y2)
using Bresenham's algorithm. The blocker function is called for each cell on the line
and must return 1 if that cell blocks sight, 0 if clear.
The start cell is never tested — the observer is standing there.
The end cell is tested — a wall blocks LOS to itself.
Returns 1 if the path is clear, 0 if blocked.
Because FreeBASIC has no closures, use module-level Shared variables to
give the blocker function access to your map array.
| Parameter | Type | Description |
|---|---|---|
| x1, y1 | Long | Observer position. Never passed to blocker. |
| x2, y2 | Long | Target position. Passed to blocker. |
| blocker | Function(As Long, As Long) As Byte | Returns 1 if the cell blocks sight. Pass with @. |
1 = line of sight is clear0 = blocked by at least one cell on the path
' Roguelike enemy vision check:
Dim Shared g_map(79, 49) As Byte ' 1 = wall
Function is_wall(x As Long, y As Long) As Byte
Return g_map(x, y)
End Function
If vt_los(enemy_x, enemy_y, player_x, player_y, @is_wall) Then
' enemy can see the player -- enter alert state
alert_enemy()
End If
Rotates a 2D Long array 90° clockwise in-place.
The array must be square. LBound and UBound are used internally
to detect the size automatically — no size argument is needed.
Non-square arrays are silently ignored (no-op). Works with any LBound
(0-based or 1-based declarations).
| Parameter | Type | Description |
|---|---|---|
| m() | Long array | Square 2D array to rotate in-place. |
' Rotate a Tetris piece clockwise: Dim piece(0 To 3, 0 To 3) As Long ' ... fill piece with 0/1 values ... vt_mat_rotate_cw piece() ' one 90-degree CW step
Rotates a 2D Long array 90° counter-clockwise in-place.
Identical behaviour to vt_mat_rotate_cw
but in the opposite direction.
| Parameter | Type | Description |
|---|---|---|
| m() | Long array | Square 2D array to rotate in-place. |
' Four CCW rotations return the matrix to its original orientation: vt_mat_rotate_ccw piece() vt_mat_rotate_ccw piece() vt_mat_rotate_ccw piece() vt_mat_rotate_ccw piece() ' back to start
The sound extension generates waveform samples and plays them via
SDL_QueueAudio. No SDL2_mixer required — SDL2 core audio only.
The audio subsystem initializes automatically on the first vt_sound call
and does not require vt_screen to be open.
VT_USE_SOUND before the include.
Without it the entire extension is absent at compile time — zero overhead.
' Enable sound in your project: #define VT_USE_SOUND #include once "vt/vt.bi"Shutdown is handled automatically by vt_shutdown. If
vt_screen is called again after audio was active, the next
vt_sound call transparently reopens the audio device.
Generate samples for a tone of the given frequency and duration and add them to the
SDL audio queue. The waveform and blocking behaviour are selectable.
Pass freq = 0 to stop playback and clear the queue immediately.
| Parameter | Type | Default | Description |
|---|---|---|---|
| freq | Long | none | Frequency in Hz. 0 = stop and clear the queue immediately (all other parameters are ignored). |
| dur_ms | Long | 200 | Duration in milliseconds. |
| wave | Long | VT_WAVE_SQUARE | Waveform. One of the VT_WAVE_* constants. |
| blocking | Long | VT_SOUND_BLOCKING | VT_SOUND_BLOCKING — waits for the note to finish while keeping the window fully alive (pump, blink, present). VT_SOUND_BACKGROUND — queues samples and returns immediately. |
0 = success-1 = SDL audio init or memory allocation failed-2 = queue cap exceeded (VT_SND_QUEUE_CAP), call was skipped
VT_SOUND_BLOCKING
is used the wait loop calls vt_pump, blink update, and present on every tick,
so the window redraws, blinks, and responds to the close button normally during the wait.
This mirrors the behaviour of vt_sleep. When no window is open
the loop simply sleeps and audio drains on its own SDL thread.
' Single blocking note (default): vt_sound 440, 500 ' Simple melody -- blocking, plays note-by-note: vt_sound 262, 200 vt_sound 330, 200 vt_sound 392, 400 ' Same melody queued in the background -- screen updates while it plays: vt_sound 262, 200, VT_WAVE_SQUARE, VT_SOUND_BACKGROUND vt_sound 330, 200, VT_WAVE_SQUARE, VT_SOUND_BACKGROUND vt_sound 392, 400, VT_WAVE_SQUARE, VT_SOUND_BACKGROUND vt_print "Playing..." : vt_present() vt_sound_wait() ' Stop playback immediately: vt_sound 0
Block until the audio queue fully drains. This is the sync barrier for sequences
queued with VT_SOUND_BACKGROUND — it lets you run code (update the
screen, compute the next frame) while a melody plays, then wait for it to finish
without having to calculate the total duration yourself.
The window stays alive during the wait, identical to the blocking wait in
vt_sound.
No-op if the sound subsystem was never initialized.
Convenience macro. Plays a short 800 Hz square-wave beep, blocking.
Equivalent to vt_sound(800, 200).
' Simple error beep: VT_BEEP
Pure FreeBASIC string utilities. No SDL2 or open VT screen required —
all helpers work in any FreeBASIC project. Fills gaps that FreeBASIC's built-in
string handling does not cover (InStr, Mid, LTrim etc.
are already available and are not duplicated here).
VT_USE_STRINGS before the include.
Without it the entire extension is absent at compile time — zero overhead.
' Enable string helpers in your project: #define VT_USE_STRINGS #include once "vt/vt.bi"
Replace every non-overlapping occurrence of find in s
with repl. Scanning always advances past the replacement so
overlapping matches are not produced. Returns s unchanged if
find is empty.
| Parameter | Type | Description |
|---|---|---|
| s | String | Source string. |
| find | String | Substring to search for. Empty string is a no-op. |
| repl | String | Replacement string. May be empty to delete all occurrences. |
' Replace spaces with underscores:
Dim fname As String = vt_str_replace("hello world foo", " ", "_")
' fname = "hello_world_foo"
' Delete all occurrences:
Dim stripped As String = vt_str_replace("a--b--c", "--", "")
' stripped = "abc"
Split s by delim and fill arr() with the resulting
segments. The array is ReDim'd by this function; the caller only needs to
declare Dim arr() As String. Returns the number of elements produced.
If delim is empty, each character of s becomes its own element.
A trailing delimiter produces a final empty element (e.g. "a,b," yields
three elements, the last being ""). An empty s always returns
one element containing "".
| Parameter | Type | Description |
|---|---|---|
| s | String | Source string to split. |
| delim | String | Delimiter string. Empty = split into individual characters. |
| arr() | String array | Receives the segments. ReDim'd to 0 To count-1. |
arr().' Parse a comma-separated list:
Dim parts() As String
Dim cnt As Long = vt_str_split("one,two,three", ",", parts())
' cnt = 3, parts(0)="one" parts(1)="two" parts(2)="three"
For i As Long = 0 To cnt - 1
vt_print parts(i) + !"\n"
Next i
Right-align s in a field of exactly length characters by
padding with ch on the left. If s is already longer than
length, the rightmost length characters are returned (truncation
keeps the least-significant end, useful for numbers). Only the first character of
ch is used; if ch is empty a space is substituted.
Returns "" if length ≤ 0.
| Parameter | Type | Description |
|---|---|---|
| s | String | Source string. |
| length | Long | Desired output width in characters. |
| ch | String | Fill character. Only the first character is used. |
' Zero-pad a score value to 6 digits:
vt_print vt_str_pad_left(Str(score), 6, "0") ' "001450"
' Right-align a label in a 10-char column:
vt_print "[" + vt_str_pad_left("hi", 10, " ") + "]" ' "[ hi]"
Left-align s in a field of exactly length characters by
padding with ch on the right. If s is already longer than
length, the leftmost length characters are returned.
Only the first character of ch is used; if ch is empty
a space is substituted. Returns "" if length ≤ 0.
| Parameter | Type | Description |
|---|---|---|
| s | String | Source string. |
| length | Long | Desired output width in characters. |
| ch | String | Fill character. Only the first character is used. |
' Fixed-width label next to a zero-padded value:
vt_print vt_str_pad_right("SCORE", 10, " ") + vt_str_pad_left(Str(score), 6, "0")
' "SCORE 001450"
Return s concatenated n times.
Returns "" for n ≤ 0 or an empty s.
Single-character strings use FreeBASIC's String() internally for efficiency.
| Parameter | Type | Description |
|---|---|---|
| s | String | String to repeat. |
| n | Long | Number of repetitions. 0 or negative returns "". |
' Draw a separator line:
vt_print vt_str_repeat("-=", 20) + !"\n" ' "-=-=-=-=-= ..." (40 chars)
' Build a progress bar:
Dim bar As String = "[" + vt_str_repeat("#", filled) + vt_str_repeat(".", empty_slots) + "]"
Count non-overlapping occurrences of find in s.
Returns 0 if either string is empty.
| Parameter | Type | Description |
|---|---|---|
| s | String | String to search in. |
| find | String | Substring to count. Empty string always returns 0. |
' Count vowels (as single chars):
Dim n As Long = vt_str_count("banana", "a") ' 3
' Count two-char patterns (non-overlapping):
n = vt_str_count("banana", "an") ' 2 ("b[an][an]a")
Returns 1 if s begins with pfx, 0
otherwise. An empty pfx always returns 1.
| Parameter | Type | Description |
|---|---|---|
| s | String | String to test. |
| pfx | String | Expected prefix. |
' Simple command dispatch: If vt_str_starts_with(input_line, "/quit") Then end_game = 1 If vt_str_starts_with(input_line, "/say ") Then broadcast Mid(input_line, 6)
Returns 1 if s ends with sfx, 0
otherwise. An empty sfx always returns 1.
| Parameter | Type | Description |
|---|---|---|
| s | String | String to test. |
| sfx | String | Expected suffix. |
' Check file extension: If vt_str_ends_with(filename, ".vts") Then vt_bload filename
Remove any character found in the set chars from both ends of s
and return the result. FreeBASIC's built-in Trim only removes spaces;
this function accepts any set of characters.
Returns "" if s consists entirely of characters in the set.
Returns s unchanged if chars is empty.
| Parameter | Type | Description |
|---|---|---|
| s | String | Source string. |
| chars | String | Set of characters to strip. Each character in this string is treated as an individual trim candidate. |
' Strip decorative border characters:
Dim clean As String = vt_str_trim_chars("***hello***", "*")
' clean = "hello"
' Strip spaces and dots from both ends:
clean = vt_str_trim_chars(" ..hi.. ", " .")
' clean = "hi"
Insert Chr(10) line breaks into s so that no line exceeds
col_width characters. Words are never split — if a word is longer
than col_width it occupies its own line unbroken.
Existing Chr(10) and Chr(13) characters in s
are preserved and reset the line-length counter; CRLF pairs are collapsed to a
single Chr(10).
The return value is a plain string with embedded newlines. Pass it directly to
vt_print, which handles Chr(10) natively.
| Parameter | Type | Default | Description |
|---|---|---|---|
| s | String | none | Source text to wrap. |
| col_width | Long | -1 | Maximum line width in characters. -1 uses scr_cols if a VT screen is open, otherwise falls back to 80. |
| first_offset | Long | 0 | Characters already consumed on the first line before the text begins (e.g. the width of an inline label). Subsequent lines always start at column 0. |
col_width = -1 falls back to 80 when
no VT screen is open, so vt_str_wordwrap is safe to call before
vt_screen or in headless contexts.
' Wrap a long description inside a 30-char dialog window interior: Dim msg As String = "This item restores your health fully and also cures poison." vt_print vt_str_wordwrap(msg, 30) ' Label already printed on the same line -- tell the wrapper to account for it: vt_print "Description: " vt_print vt_str_wordwrap(msg, 40, 13) ' Use screen width automatically (requires open screen): vt_print vt_str_wordwrap(msg)
Fills gaps in FreeBASIC's built-in file and directory handling. Wraps
Dir() from dir.bi cleanly, hiding the
fbDirectory constant and the multi-call scan idiom from
caller code. Functions that duplicate existing FreeBASIC built-ins without
adding behaviour (MkDir, RmDir, Kill,
Name) are intentionally not wrapped — use them directly.
VT_USE_FILE before the include.
Without it the entire extension is absent at compile time — zero overhead.
' Enable file helpers in your project: #define VT_USE_FILE #include once "vt/vt.bi"No SDL2 or open VT screen required — all helpers are pure FreeBASIC and usable in any project.
RmDir may fail or
vt_file_isdir may briefly return 1 immediately after
deletion due to Explorer holding a filesystem watch handle. This is OS behaviour,
not a library bug. Close Explorer or any open file manager window pointing at the
target before recursive deletion if reliable return codes are required.
Bit-flags passed to vt_file_copy,
vt_file_rmdir, and
vt_file_list. Combine with Or.
| Constant | Value | Applies to | Description |
|---|---|---|---|
| VT_FILE_SHOW_HIDDEN | 1 | vt_file_list | Include hidden items in the listing. |
| VT_FILE_SHOW_DIRS | 2 | vt_file_list | Include subdirectory names alongside files. |
| VT_FILE_DIRS_ONLY | 4 | vt_file_list | Return only subdirectory names. Implies VT_FILE_SHOW_DIRS. |
| VT_FILE_OVERWRITE | 8 | vt_file_copy | Allow the destination file to be overwritten if it already exists. |
| VT_FILE_RECURSIVE | 16 | vt_file_rmdir | Delete all contents and subdirectories before removing the directory. Destructive — no undo. |
Returns 1 if path names an existing file,
0 if the path does not exist or is a directory.
Uses Dir() directly — no vbcompat.bi required.
| Parameter | Type | Description |
|---|---|---|
| path | String | Path to test. May be relative or absolute. |
1 = file exists0 = not found, or path is a directory
' Guard a bload call:
If vt_file_exists("savegame.vts") Then
vt_bload "savegame.vts"
End If
Returns 1 if path is an existing directory,
0 otherwise. Hides the fbDirectory constant
and dir.bi dependency from caller code entirely.
| Parameter | Type | Description |
|---|---|---|
| path | String | Path to test. May be relative or absolute. |
1 = path is an existing directory0 = not found, or path is a file
' Create output folder only if it does not already exist:
If vt_file_isdir("output") = 0 Then MkDir "output"
Copy a file or a full directory tree. The function auto-detects whether
src is a file or a directory — no separate call needed.
File mode: the source file is read into memory and written to
the destination in a single binary I/O operation. Fails if the destination
already exists unless VT_FILE_OVERWRITE is passed.
Directory mode: the full directory tree is recreated under
dst. If dst does not exist it is created; if it
already exists the trees are merged. Individual files that already exist in
dst are silently skipped unless VT_FILE_OVERWRITE
is set. All entries are collected in a single Dir() pass before
anything is written, so the global scan state is never corrupted by recursion.
-4.
For example vt_file_copy("data", "data/backup") returns
-4 before any file is touched.
| Parameter | Type | Default | Description |
|---|---|---|---|
| src | String | none | Source file or directory path. |
| dst | String | none | Destination file or directory path. |
| flags | Long | 0 | VT_FILE_OVERWRITE — overwrite existing destination files. |
0 = success-1 = source not found (neither file nor directory)-2 = destination file exists without VT_FILE_OVERWRITE,
or dst is an existing file while src
is a directory (type mismatch)-3 = I/O error (open, read/write, or MkDir failure)-4 = recursion trap: dst is src itself
or lives inside src
' Simple backup before overwriting:
vt_file_copy "config.ini", "config.bak"
' Overwrite an existing destination file:
vt_file_copy "save_new.vts", "save_slot1.vts", VT_FILE_OVERWRITE
' Copy a full directory tree:
vt_file_copy "data", "data_backup"
' Copy tree, overwrite any files that already exist in dst:
vt_file_copy "data", "data_backup", VT_FILE_OVERWRITE
' Check result:
Dim ret As Long = vt_file_copy("src.dat", "dst.dat")
If ret <> 0 Then
vt_print "copy failed: " & ret & VT_NEWLINE
End If
Remove a directory. Without VT_FILE_RECURSIVE the directory must
be empty — for that common case FreeBASIC's built-in RmDir
is sufficient and preferred. Use vt_file_rmdir when recursive
deletion of a populated directory tree is needed.
When VT_FILE_RECURSIVE is set, all files and subdirectories are
collected in a single Dir() pass before anything is deleted, so
the global Dir() scan state is never corrupted by recursive calls.
Files are removed with Kill, subdirectories by recursing into
vt_file_rmdir, then the now-empty root is removed with
RmDir.
VT_FILE_RECURSIVE is permanently
destructive. There is no undo. The flag must be passed explicitly —
the default is always safe (empty-dir only).
| Parameter | Type | Default | Description |
|---|---|---|---|
| path | String | none | Path to the directory to remove. |
| flags | Long | 0 | Optional flags. VT_FILE_RECURSIVE deletes all contents first. |
0 = success-1 = path is not an existing directory-2 = directory is not empty and VT_FILE_RECURSIVE was not set-3 = failed (permissions or partial recursive failure)
' Remove an empty temp directory (prefer plain RmDir for this): vt_file_rmdir "tmp_empty" ' Remove a populated directory tree -- explicit opt-in required: vt_file_rmdir "build_output", VT_FILE_RECURSIVE
Scan a directory for items matching pattern and fill
arr() with their bare names (no path prefix).
The . and .. entries are always silently skipped.
The array is ReDim'd by this function; the caller only needs to
declare Dim arr() As String.
By default only files are returned. Pass VT_FILE_SHOW_DIRS to
include subdirectory names alongside files, or VT_FILE_DIRS_ONLY
to return only subdirectory names.
| Parameter | Type | Default | Description |
|---|---|---|---|
| path | String | none | Directory to scan. Pass "" to scan the current working directory. |
| pattern | String | none | Filename pattern, e.g. "*.txt" or "*". Supports * and ? wildcards. |
| arr() | String array | none | Receives the item names. ReDim'd to 0 To count-1. |
| flags | Long | 0 | Optional combination of VT_FILE_SHOW_HIDDEN, VT_FILE_SHOW_DIRS, VT_FILE_DIRS_ONLY. |
arr(). 0 if nothing matched.' List all .txt files in a folder:
Dim files() As String
Dim cnt As Long = vt_file_list("notes", "*.txt", files())
For i As Long = 0 To cnt - 1
vt_print files(i) & VT_NEWLINE
Next i
' List files and subdirectories together:
cnt = vt_file_list("data", "*", files(), VT_FILE_SHOW_DIRS)
' List only subdirectories:
cnt = vt_file_list("data", "*", files(), VT_FILE_DIRS_ONLY)
' Scan current directory, include hidden files:
cnt = vt_file_list("", "*", files(), VT_FILE_SHOW_HIDDEN)
DOS-style TUI widgets: windows, dialogs, menus, forms, file picker, editor. All coordinates are 1-based column / row, matching the rest of the VT API. Enable with:
#Define VT_USE_TUI #include once "vt/vt.bi"
VT_USE_TUI automatically pulls in VT_USE_STRINGS and
VT_USE_FILE — you do not need to define them separately.
All TUI functions work identically on both the SDL2 and the TTY backends.
vt_tui_theme nor
vt_tui_theme_default is called before the first draw call, the
default DOS/BIOS theme is applied automatically. You will never see
all-black-on-black by accident.
Set the colour theme used by all TUI widgets. All colours are CGA palette indices (0–15, see Color Constants). The theme is a single global struct — there is no per-widget override.
vt_tui_theme_default restores the built-in DOS/BIOS defaults.
vt_tui_theme sets all fields in one call.
Neither function requires vt_screen to have been called first.
| Parameter | Type | Description |
|---|---|---|
| win_fg / win_bg | UByte | Window body text and background. |
| title_fg / title_bg | UByte | Title bar text and background. Also used as the filled portion colour of a progress bar. |
| bar_fg / bar_bg | UByte | Status bar and menu bar text and background. |
| btn_fg / btn_bg | UByte | Button text and background (unfocused state). |
| dlg_fg / dlg_bg | UByte | Dialog body text and background. Also used for dropdown menu item rows. |
| inp_fg / inp_bg | UByte | Input field and editor text and background. Also used as the empty portion colour of a progress bar. |
| border_style | UByte | 0 = single-line CP437 box-drawing (default). 1 = double-line CP437 box-drawing. |
| Role | fg | bg |
|---|---|---|
| window body | VT_BLACK | VT_LIGHT_GREY |
| title bar | VT_WHITE | VT_BLUE |
| status / menu bar | VT_BLACK | VT_CYAN |
| button | VT_BLACK | VT_LIGHT_GREY |
| dialog body | VT_BLACK | VT_LIGHT_GREY |
| input field / editor | VT_WHITE | VT_DARK_GREY |
' Restore DOS blue-scheme defaults:
vt_tui_theme_default()
' Custom dark theme:
vt_tui_theme VT_LIGHT_GREY, VT_BLACK, _
VT_WHITE, VT_DARK_GREY, _
VT_BLACK, VT_CYAN, _
VT_LIGHT_GREY, VT_DARK_GREY, _
VT_LIGHT_GREY, VT_BLACK, _
VT_WHITE, VT_DARK_GREY, _
0
Fill a 1-based rectangle with a single character and colour. This is the base drawing primitive — all other widgets use it internally to clear their area.
| Parameter | Type | Description |
|---|---|---|
| x, y | Long | 1-based column and row of the top-left corner. |
| wid, hei | Long | Width and height in cells. |
| ch | UByte | CP437 character code to fill with. Use 32 (space) for a solid colour block. |
| fg, bg | UByte | Foreground and background colour indices. |
' Clear a region to dark grey: vt_tui_rect_fill 1, 1, 40, 10, 32, VT_LIGHT_GREY, VT_DARK_GREY ' Solid red banner row: vt_tui_rect_fill 1, 1, 80, 1, 32, VT_BLACK, VT_RED
Draw a horizontal or vertical separator line using CP437 box-drawing characters.
The character is chosen automatically from the active theme's
border_style: single-line style uses characters 196 (horizontal)
and 179 (vertical); double-line style uses 205 and 186.
| Parameter | Type | Description |
|---|---|---|
| x, y | Long | 1-based start column and row. |
| wid / hei | Long | Length of the line in cells. |
| fg, bg | UByte | Foreground and background colour indices. |
' Horizontal divider across a full 80-column screen: vt_tui_hline 1, 13, 80, VT_DARK_GREY, VT_BLACK ' Vertical divider splitting the screen at column 40: vt_tui_vline 40, 1, 25, VT_DARK_GREY, VT_BLACK
Draw window chrome: a border, a title bar, and optionally a close button and
drop shadow. Does not save or restore the background and does
not block. The window interior is filled with the theme's
win_fg / win_bg colours.
| Parameter | Type | Default | Description |
|---|---|---|---|
| x, y | Long | none | 1-based column and row of the top-left corner. |
| wid, hei | Long | none | Outer width and height including the border. |
| title | String | none | Text centred in the title bar. Truncated if too long. |
| flags | Long | 0 | Combination of VT_TUI_WIN_CLOSEBTN and / or VT_TUI_WIN_SHADOW. |
VT_TUI_WIN_CLOSEBTN renders [x] visually only. The caller
is responsible for detecting clicks on it using
vt_tui_mouse_in_rect(x + wid - 4, y, 3, 1).
' Centred window, 40 wide × 12 tall, with shadow:
Dim wx As Long = (80 - 40) \ 2 + 1
Dim wy As Long = (25 - 12) \ 2 + 1
vt_tui_window wx, wy, 40, 12, "Settings", VT_TUI_WIN_SHADOW
' With close button — detect click separately:
vt_tui_window wx, wy, 40, 12, "Settings", VT_TUI_WIN_CLOSEBTN Or VT_TUI_WIN_SHADOW
If vt_tui_mouse_in_rect(wx + 40 - 4, wy, 3, 1) Then
' [x] was clicked
End If
Fill an entire screen row with the theme's bar_bg colour and print
caption left-aligned in bar_fg. Typical use: the last
row for a keyboard hint bar, or row 1 below a menu bar.
| Parameter | Type | Description |
|---|---|---|
| row | Long | 1-based screen row to fill (full width). |
| caption | String | Text printed left-aligned. Clipped to screen width. |
' Hint bar on the last row: vt_tui_statusbar 25, " F1=Help F10=Menu Esc=Quit"
Draw a horizontal progress bar at an arbitrary screen position. The filled
portion uses the theme's title_bg colour as a solid block; the
empty portion uses inp_bg. Both halves use space (32) as the
character so colour alone carries the bar.
| Parameter | Type | Default | Description |
|---|---|---|---|
| x, y | Long | none | 1-based column and row. |
| wid | Long | none | Width of the bar in cells. |
| value | Long | none | Current progress value. Clamped to [0 .. max_val]. |
| max_val | Long | none | Maximum value. Must be > 0. |
| flags | Long | 0 | VT_TUI_PROG_LABEL — print a centred nn% label. Label fg colour inverts per-cell so it reads correctly across both halves. |
' Progress bar updating inside a loop:
For i As Long = 0 To 100
vt_tui_progress 5, 10, 40, i, 100, VT_TUI_PROG_LABEL
vt_present()
vt_sleep 20
Next i
Hit-test helper. Returns 1 if the current mouse position is inside
the given 1-based rectangle, 0 otherwise. Returns 0
immediately if the library is not ready or the mouse is disabled.
Reads the internal mouse state updated by vt_inkey —
call this after polling input, not before.
| Parameter | Type | Description |
|---|---|---|
| x, y | Long | 1-based column and row of the top-left corner of the hit area. |
| wid, hei | Long | Width and height in cells. |
1 = mouse cursor is inside the rectangle0 = outside, mouse disabled, or library not ready
' Hover detection over a custom 10-cell-wide button at row 5:
If vt_tui_mouse_in_rect(10, 5, 10, 1) Then
' highlight the button
End If
' Close-button detection for a window at (wx, wy) of outer width ww:
If vt_tui_mouse_in_rect(wx + ww - 4, wy, 3, 1) Then
' [x] hovered or clicked
End If
Single-line text input at an arbitrary screen position.
Blocking. Draws a field of wid cells in the
theme's inp_fg / inp_bg colours and lets the user edit the text.
Supports buffers longer than the visible width via horizontal scrolling.
The text cursor is shown during editing and its previous visibility state is
restored on exit.
| Parameter | Type | Description |
|---|---|---|
| x, y | Long | 1-based column and row of the left edge of the field. |
| wid | Long | Visible field width in cells. |
| initial | String | Pre-filled text. Cursor starts at the end of the text. |
| max_len | Long | Maximum characters the buffer may hold. Pass 0 to use wid as the limit. |
initial unchanged on Escape.
| Key | Action |
|---|---|
| Left / Right | Move cursor one character. |
| Home / End | Cursor to start / end of text. |
| Backspace / Del | Delete left / right of cursor. |
| Printable char | Insert at cursor (respects max_len). |
| Enter | Confirm — return edited text. |
| Escape | Cancel — return initial unchanged. |
' Prompt for a filename:
Dim fname As String = vt_tui_input_field(10, 12, 30, "", 64)
If Len(fname) > 0 Then
' use fname
End If
' Pre-filled edit (e.g. rename dialog):
Dim newname As String = vt_tui_input_field(10, 12, 30, "oldname.txt", 64)
Scrollable item list. Blocking. Draws the list at the given position, handles keyboard and mouse input, and returns when the user confirms or cancels. A scrollbar is drawn automatically on the right edge when the item count exceeds the visible height.
| Parameter | Type | Default | Description |
|---|---|---|---|
| x, y | Long | none | 1-based column and row of the top-left corner. |
| wid, hei | Long | none | Width and visible height in cells. The scrollbar occupies the rightmost cell when active. |
| items() | String array | none | Items to display. Any array lower bound is accepted. |
| flags | Long | 0 | Reserved for future use. |
-1 on Escape (cancel).
| Input | Action |
|---|---|
| Up / Down | Move selection one item. |
| PgUp / PgDn | Move selection by hei items. |
| Home / End | Jump to first / last item. |
| Enter | Confirm selection. |
| Escape | Cancel, return -1. |
| Click | Highlight the clicked item. |
| Double-click | Confirm the clicked item immediately. |
| Scroll wheel | Scroll the view without moving the selection. |
' Difficulty picker:
Dim opts(2) As String
opts(0) = "Easy" : opts(1) = "Normal" : opts(2) = "Hard"
Dim picked As Long = vt_tui_listbox(20, 8, 20, 3, opts())
If picked >= 0 Then
vt_print "Chose: " & opts(picked) & VT_NEWLINE
End If
Self-contained blocking modal dialog. Saves the background before drawing and restores it on close. The text is word-wrapped automatically to fit; the window grows to accommodate both the wrapped content and the button row. The dialog is centred on screen (default 60 columns wide, clamped to screen width minus 4).
| Parameter | Type | Default | Description |
|---|---|---|---|
| caption | String | none | Title bar text. |
| txt | String | none | Body text. Word-wrapped to fit the dialog width. |
| flags | Long | VT_DLG_OK | Button set constant, optionally combined with VT_DLG_NO_ESC. |
VT_RET_OK, VT_RET_CANCEL,
VT_RET_YES, VT_RET_NO.
| Input | Action |
|---|---|
| Tab | Advance focus to the next button (wraps). |
| Left / Right | Move focus between buttons. |
| Enter | Activate the focused button. |
| Escape | Return VT_RET_CANCEL (unless VT_DLG_NO_ESC is set). |
| Click | Activate the clicked button immediately. |
' Quit confirmation:
If vt_tui_dialog("Quit", "Are you sure you want to quit?", VT_DLG_YESNO) = VT_RET_YES Then
End
End If
' Mandatory choice (Escape disabled):
Dim r As Long = vt_tui_dialog("Disk Full", "Retry or abort the operation?", _
VT_DLG_OKCANCEL Or VT_DLG_NO_ESC)
' Simple info box:
vt_tui_dialog "Done", "File saved successfully."
Self-contained blocking file / directory picker. Saves the background before drawing and restores it on close. Centred on screen, automatically sized to fit the available area (maximum 70 columns wide).
The listing always includes subdirectories (shown with a trailing /)
and a .. entry at the top whenever a parent exists. Files are
matched against pattern. All returned paths use forward slashes on
both Windows and Linux.
| Parameter | Type | Default | Description |
|---|---|---|---|
| title | String | none | Title bar text and confirm button label (e.g. "Open", "Save"). |
| start_path | String | none | Initial directory. Backslashes are normalized to forward slashes automatically. Pass "" or "./" for the current working directory. |
| pattern | String | none | Filename wildcard, e.g. "*.txt" or "*". Ignored when VT_TUI_FD_DIRSONLY is set. |
| flags | Long | 0 | VT_TUI_FD_DIRSONLY — show only subdirectories; Enter or OK on a directory confirms it. |
String. Directory paths end with /."" on cancel (Escape or Cancel button).
| Input | Action |
|---|---|
| Up / Down / PgUp / PgDn / Home / End | Navigate the file list. The name field updates to match the selected entry. |
| Enter on a file | Confirm that file. |
| Enter on a directory | Navigate into it (or confirm it in VT_TUI_FD_DIRSONLY mode). |
| Double-click a directory | Navigate into it (or confirm in VT_TUI_FD_DIRSONLY mode). |
| Double-click a file | Confirm that file. |
| Backspace | Delete the last character from the name field, or navigate up one directory if the name field is empty. |
| Tab | Activate the name field for direct text entry. |
| Printable char | Append to the name field. |
| F2 | Keyboard equivalent of the OK button. |
| Click on name field | Open the name field for editing. |
| Scroll wheel | Scroll the file list. |
| Escape | Cancel, return "". |
' Open file picker for FreeBASIC sources:
Dim path As String = vt_tui_file_dialog("Open", "./", "*.bas")
If Len(path) > 0 Then
vt_print "Selected: " & path & VT_NEWLINE
End If
' Save-as dialog starting in a saves subfolder:
path = vt_tui_file_dialog("Save", "./saves/", "*.sav")
' Directory picker only:
path = vt_tui_file_dialog("Choose Folder", "./", "*", VT_TUI_FD_DIRSONLY)
Multi-line text editor / viewer widget. Blocking.
Text is stored and returned as a single String with
Chr(10) as the line separator. The widget renders inside the
given rectangle using the theme's inp_fg / inp_bg colours.
Lines exceeding wid are truncated in display; no horizontal scrolling.
| Parameter | Type | Default | Description |
|---|---|---|---|
| x, y | Long | none | 1-based column and row of the top-left corner of the editing area. |
| wid, hei | Long | none | Width and height in cells. Place directly inside a vt_tui_window border. |
| txt | String | none | Initial text. Use Chr(10) as the line separator. |
| flags | Long | 0 | VT_TUI_ED_READONLY — view and scroll only, no editing. Text cursor is hidden. |
txt unchanged on Escape.
| Key | Action |
|---|---|
| Up / Down | Move cursor one line. |
| PgUp / PgDn | Move cursor by hei lines. |
| Left / Right | Move cursor one character (byte offset). |
| Home / End | Cursor to start / end of the current line. |
| Enter | Insert a newline (edit mode only). |
| Backspace / Del | Delete left / right of cursor (edit mode only). |
| Printable char | Insert at cursor (edit mode only). |
| F10 or Ctrl+Enter | Confirm — return the edited text. |
| Escape | Cancel — return original text unchanged. |
wid are displayed truncated but stored intact.
' Note editor inside a window: Dim note As String = "First line" & Chr(10) & "Second line" vt_tui_window 5, 3, 70, 20, "Notes [F10=Save Esc=Cancel]" note = vt_tui_editor(6, 4, 68, 18, note) ' Read-only file viewer: Dim content As String Open "readme.txt" For Binary As #1 content = Space(LOF(1)) Get #1, , content Close #1 vt_tui_editor 1, 1, 80, 24, content, VT_TUI_ED_READONLY
Horizontal menu bar. Two-function design: vt_tui_menubar_draw
renders the bar passively (non-blocking, call once per frame);
vt_tui_menubar_handle is non-blocking while no menu is open and
blocks with its own inner event loop while a dropdown is open.
| Parameter | Type | Description |
|---|---|---|
| row | Long | 1-based screen row for the menu bar (typically row 1). |
| groups() | String array | Group header labels, e.g. "File", "Edit", "Help". |
| items() | String array | Flat array of all items across all groups, in group order. |
| counts() | Long array | Item count per group, parallel to groups(). |
| k | ULong | Key value from vt_inkey() for the current frame. |
vt_tui_menubar_handle)group * 1000 + item_index (both 1-based) when an item is activated —
use VT_TUI_MENU_GROUP(r) and VT_TUI_MENU_ITEM(r) to unpack.0 while idle or after Escape closes a dropdown.
| Input | Action |
|---|---|
| Alt + first letter of group label | Open that group's dropdown. |
| Click on a group header | Open that group's dropdown. |
| Up / Down (dropdown open) | Move selection. Wraps around. |
| Left / Right (dropdown open) | Switch to the adjacent group. |
| Alt + first letter (dropdown open) | Switch to that group's dropdown. |
| Enter (dropdown open) | Activate the selected item. |
| Click on a dropdown item | Activate that item. |
| Click on a different group header | Switch to that group's dropdown. |
| Click anywhere else | Close the dropdown, return 0. |
| Escape | Close the dropdown, return 0. |
VT_ALT(k) and VT_CHAR(k) — no backend-specific
code needed in the caller.' Setup arrays:
Dim groups(2) As String
groups(0) = "File" : groups(1) = "Edit" : groups(2) = "Help"
Dim items(6) As String
items(0) = "New" : items(1) = "Open" : items(2) = "Save" : items(3) = "Quit"
items(4) = "Cut" : items(5) = "Copy"
items(6) = "About"
Dim counts(2) As Long
counts(0) = 4 : counts(1) = 2 : counts(2) = 1
' Main loop:
Dim k As ULong
Do
k = vt_inkey()
vt_tui_menubar_draw 1, groups()
Dim r As Long = vt_tui_menubar_handle(1, groups(), items(), counts(), k)
If r <> 0 Then
Select Case VT_TUI_MENU_GROUP(r)
Case 1 ' File
Select Case VT_TUI_MENU_ITEM(r)
Case 1 : ' New
Case 2 : ' Open
Case 3 : ' Save
Case 4 : End ' Quit
End Select
End Select
End If
vt_present()
vt_sleep 10
Loop
Non-blocking form widget. Two-function design: vt_tui_form_draw
renders all items passively each frame; vt_tui_form_handle
processes one key or mouse event and returns immediately. The caller owns the
vt_tui_form_item array and loops until vt_tui_form_handle
returns a value other than VT_FORM_PENDING.
Declare an array of this type to define the form layout. Set .cpos
and .view_off to 0 when initialising —
vt_tui_form_handle manages them internally afterward.
| Parameter | Type | Description |
|---|---|---|
| items() | vt_tui_form_item array | Form items. Any array lower bound is accepted. |
| focused | Long (ByRef) | 0-based index of the currently focused item. Updated in place by form_handle on focus changes so the next form_draw call reflects the new state. |
| k | ULong | Key value from vt_inkey() for the current frame. Pass 0 if no key was pressed this frame. |
vt_tui_form_handle)VT_FORM_PENDING (−2) — still editing, call again next frame.VT_FORM_CANCEL (−1) — Escape was pressed.≥ 0 — the .ret value of an activated button
(e.g. VT_RET_OK = 1, VT_RET_CANCEL = 0).
VT_FORM_PENDING is −2, not 0, so that VT_RET_CANCEL = 0
can be a valid button return value without ambiguity. Test for done with
result <> VT_FORM_PENDING.
| Input | Action |
|---|---|
| Tab / Shift+Tab | Advance / retreat focus through all items (wraps). |
| Enter on an input | Advance focus to the next item (same as Tab). |
| Enter on a button | Activate that button, return its .ret. |
| Left / Right on a button | Move focus to the adjacent item. |
| Left / Right on an input | Move the cursor within the field. |
| Home / End on an input | Cursor to start / end of the text. |
| Backspace / Del on an input | Delete left / right of cursor. |
| Printable char on an input | Insert at cursor (respects max_len). |
| Click on an input | Focus it; cursor snaps to the click column. |
| Click on a button | Activate immediately, return its .ret. |
| Escape (any item) | Return VT_FORM_CANCEL. |
' Login form: two text inputs and two buttons.
Dim fitems(3) As vt_tui_form_item
fitems(0).kind = VT_FORM_INPUT : fitems(0).x = 16 : fitems(0).y = 8
fitems(0).wid = 20 : fitems(0).max_len = 32
fitems(1).kind = VT_FORM_INPUT : fitems(1).x = 16 : fitems(1).y = 10
fitems(1).wid = 20 : fitems(1).max_len = 32
fitems(2).kind = VT_FORM_BUTTON : fitems(2).x = 16 : fitems(2).y = 12
fitems(2).val = " OK " : fitems(2).ret = VT_RET_OK
fitems(3).kind = VT_FORM_BUTTON : fitems(3).x = 23 : fitems(3).y = 12
fitems(3).val = " Cancel " : fitems(3).ret = VT_RET_CANCEL
Dim focused As Long = 0
Dim result As Long = VT_FORM_PENDING
Dim k As ULong
Do
k = vt_inkey()
vt_tui_window 10, 5, 42, 11, "Login"
vt_locate 8, 5 : vt_print "Username:"
vt_locate 10, 5 : vt_print "Password:"
vt_tui_form_draw fitems(), focused
result = vt_tui_form_handle(fitems(), focused, k)
vt_present()
vt_sleep 10
Loop While result = VT_FORM_PENDING
If result = VT_RET_OK Then
vt_print "User: " & fitems(0).val & VT_NEWLINE
End If
VT Virtual Text Screen Library — API Reference — v1.3.4
FreeBASIC 1.10.1 / SDL2 + TTY / Windows + Linux