Terminal UI (clib.curses)
import clib.curses as curses; provides full-screen terminal UI via
libncurses. It covers window initialisation, character output, cursor
movement, keyboard input, and colour/attribute control.
The module uses the FFI layer so the ffi capability must be enabled.
Linux / macOS only. libncurses ships as
libncursesw.so.6on most Linux distributions (libncurses-dev/ncurses-develpackage) and aslibncurses.dylibon macOS via Homebrew. libtinfo may be a required transitive dependency on some distributions.
Single-owner constraint. ncurses is global, single-screen, single-thread state. This module must be driven from ONE task only. Concurrent use from multiple async tasks is unsupported and cannot be made safe by serialising calls.
Capability
Add to geblang.yaml:
permissions:
ffi:
enabled: true
libraries:
- glob: libncursesw*
- glob: libncurses*
- glob: libtinfo*
For a standalone script:
geblang --allow-ffi 'libncursesw*' --allow-ffi 'libncurses*' script.gb
Lifecycle
init(): void
Initialises the screen. Calls initscr, then cbreak (disable line
buffering), noecho (suppress keystroke echo), and keypad (enable
arrow/function keys). Store the result internally as the stdscr
pointer. All screen functions require init() to have been called
first; calling them before init() throws a RuntimeError.
end(): void
Ends the curses session (endwin) and resets the terminal to its
prior state. Safe to call before init.
Screen functions
All functions below require init() to have been called first.
addStr(string s): void
Writes s at the current cursor position.
mvAddStr(int y, int x, string s): void
Writes s at position (y, x).
move(int y, int x): void
Moves the cursor to row y, column x.
clear(): void
Clears the screen.
refresh(): void
Copies the virtual screen buffer to the terminal. Changes are not
visible until refresh is called.
getCh(): int
Reads one keystroke (blocking) and returns its integer key code.
maxY(): int
Returns the number of rows in the current terminal window.
maxX(): int
Returns the number of columns in the current terminal window.
Colours and attributes
startColor(): void
Enables colour support. Call this before initPair.
initPair(int id, int fg, int bg): void
Defines colour pair id (1..255) with foreground colour fg and
background colour bg. Use the exported colour constants for fg
and bg.
attrOn(int attrs): void
Enables the given attribute bits for subsequent output.
attrOff(int attrs): void
Disables the given attribute bits.
colorPair(int n): int
Returns the attribute value for colour pair n (equivalent to
COLOR_PAIR(n) in C: n * 256). Pass the result to attrOn.
Colour constants
| Constant | Value |
|---|---|
curses.BLACK |
0 |
curses.RED |
1 |
curses.GREEN |
2 |
curses.YELLOW |
3 |
curses.BLUE |
4 |
curses.MAGENTA |
5 |
curses.CYAN |
6 |
curses.WHITE |
7 |
Attribute constants
| Constant | Value | Effect |
|---|---|---|
curses.A_NORMAL |
0 |
Default rendering |
curses.A_BOLD |
2097152 |
Bold / bright |
curses.A_UNDERLINE |
131072 |
Underline |
curses.A_REVERSE |
262144 |
Reverse video |
curses.A_DIM |
1048576 |
Dim |
curses.A_BLINK |
524288 |
Blinking |
Example
This example requires a real terminal and the FFI flag above.
import clib.curses as curses;
curses.init();
curses.startColor();
curses.initPair(1, curses.GREEN, curses.BLACK);
let rows = curses.maxY();
let cols = curses.maxX();
let msg = "Hello from Geblang!";
let row = rows / 2;
let col = (cols - msg.length()) / 2;
curses.clear();
curses.attrOn(curses.colorPair(1) | curses.A_BOLD);
curses.mvAddStr(row, col, msg);
curses.attrOff(curses.colorPair(1) | curses.A_BOLD);
curses.mvAddStr(row + 1, col, "Press any key to exit.");
curses.refresh();
curses.getCh();
curses.end();
Error behaviour
| Failure mode | Surface |
|---|---|
| libncurses not found or FFI not enabled | RuntimeError or PermissionError |
Screen function called before init() |
RuntimeError: clib.curses: call init() first |