- /
catcurses.um
type Window and type Terminal
The main abstractions for a terminal and a window in the library.
type (
Window* = struct { _: RawWindow
// Reference back to the terminal owning this window.
terminal: weak ^Terminal
}
Terminal* = struct { _: RawTerminal
// The main window owned by this terminal.
window: Window
// The maximum amount of colors supported in this terminal.
// Set on creation (see `fn mkTerminal`).
maxColors: int
// The amount of color pairs currently allocated.
// Used internally, but can be modified.
pairAmt: int
}
)
fn sessionExists
Reports whether a terminal is set. (In most cases, this serves to check whether ncurses is initialized.)
fn sessionExists*(): bool
fn curTerminal
Returns the current terminal.
See fn (^Terminal) makeCurrent.
fn curTerminal*(): ^Terminal
fn (^Terminal) makeCurrent
Makes t the "current" terminal; that is, the terminal whose associated window is shown on-screen.
Reports whether t was successfully set as the current terminal.
fn (t: ^Terminal) makeCurrent*(): bool
fn stdTerminal
Returns the standard terminal. If ncurses hasn't been initialized yet, this function will initialize it.
fn stdTerminal*(): ^Terminal
fn mkTerminal
Creates a new terminal.
If termType isn't empty, the created terminal is created with said type.
If outf and inf are null, they default to std::stdout() and std::stdin(), respectively.
If no terminal existed previously, initializes ncurses and returns a stub representing the standard terminal.
termType, outf and inf are ignored in this case.
fn mkTerminal*(termType: str = "", outf: std::File = null, inf: std::File = null): (^Terminal, std::Err)
type ColorPairID
Identifies a specific, preinitialized color pair.
type ColorPairID* = int
fn (^ColorPairID) string
Returns the string representation of this color pair identifier. For debugging purposes.
fn (c: ^ColorPairID) string*(): str { return sprintf("<color pair %d>", c^) }
type StandardColor
Indices to the standard terminal colors.
type StandardColor* = enum {
black
red
green
yellow
blue
magenta
cyan
white
// "Bright" colors
brightBlack
brightRed
brightGreen
brightYellow
brightBlue
brightMagenta
brightCyan
brightWhite
}
// Aliases
const (
gray* = StandardColor.white
darkGray* = StandardColor.brightBlack
)
fn (^Terminal) supportsColors
Reports whether this terminal supports colors.
fn (t: ^Terminal) supportsColors*(): bool
fn (^Terminal) canChangeColors
Reports whether this terminal can change its colors.
fn (t: ^Terminal) canChangeColors*(): bool
fn (^Terminal) enableColors
Enables color capability. Returns whether it was enabled.
fn (t: ^Terminal) enableColors*(): bool
fn (^Terminal) setColorPair
Associates pair with bg and fg as background/foreground colors.
fn (t: ^Terminal) setColorPair*(pair: ColorPairID, bg, fg: StandardColor): bool
fn (t: ^Terminal) addColorPair
Allocates a new color pair, with bg and fg as background/foreground colors, and returns its ID.
fn (t: ^Terminal) addColorPair*(bg, fg: StandardColor): ColorPairID
fn (^Terminal) setColor
Changes the RGB values associated with color to r, g and b.
r, g and b should be normalized floats (0-1, they will be clamped if outside that range),
and will be quantized to a 0-1000 integer range for ncurses.
This function will affect every color pair using color as a palette entry.
It will do nothing (and return false) if:
- the terminal doesn't support colors (see
fn (^Terminal) supportsColors), - the colors are unable to be changed (see
fn (^Terminal) canChangeColors), or coloris outside the range of colors the terminal can support (seetype Terminal)
fn (t: ^Terminal) setColor*(color: StandardColor, r, g, b: real): bool
fn (^Terminal) setColorBytes
Alternate version of fn (^Terminal) setColor that takes in byte values (0-255).
fn (t: ^Terminal) setColorBytes*(color: StandardColor, r, g, b: uint8): bool
fn (^Terminal) setColorHex
Alternate version of fn (^Terminal) setColor that takes in a full hex value (0xRRGGBB).
fn (t: ^Terminal) setColorHex*(color: StandardColor, rgb: uint32): bool
fn (^Terminal) destroy
Destroys the underlying terminal. t is no longer valid for use after calling this method.
If t is the standard terminal, this method will finalize ncurses.
fn (t: ^Terminal) destroy*()
fn (^Terminal) cbreak
Enables/disables cbreak mode (disables line buffering but still interprets signals) for the terminal.
Must only be called on the standard terminal (see fn stdTerminal).
Reports whether the terminal was set/unset to cbreak mode.
fn (t: ^Terminal) cbreak*(b: bool): bool
fn (^Terminal) echo
Enables/disables character echoing after each keypress.
Must only be called on the standard terminal (see fn stdTerminal).
Reports whether the echo was able to be set/unset.
fn (t: ^Terminal) echo*(b: bool): bool
fn (^Terminal) nl
Controls translation of the return key into NL (10, 0x0a, '\n').
This allows for detecting whether the return key was pressed, amongst other benefits (see man 3x nl).
Must only be called on the standard terminal (see fn stdTerminal).
Reports whether the translation was able to be enabled/disabled.
fn (t: ^Terminal) nl*(b: bool): bool
fn (^Terminal) raw
Enables/disables raw mode for the terminal.
Must only be called on the standard terminal (see fn stdTerminal).
Reports whether the terminal was set/unset to raw mode.
fn (t: ^Terminal) raw*(b: bool): bool
type Visibility
Cursor visibility specifiers. See fn (^Terminal) cursorVisibility.
type Visibility* = enum { hidden; visible; veryVisible }
fn (^Visibility) string
Returns the string representation of this visibility specifier. For debugging purposes.
fn (v: ^Visibility) string*(): str
fn (^Terminal) cursorVisibility
Changes the cursor visibility to vis (see type Visibility).
Must only be called on the standard terminal (see fn stdTerminal).
Returns the previous visibility and whether it was able to change it at all.
fn (t: ^Terminal) cursorVisibility*(v: Visibility): (Visibility, bool)
fn (^Terminal) escapeDelay
Changes the amount of time ncurses waits for further input to disambiguate escapes from escape sequences.
Only applicable when keypad(true); otherwise, the program must disambiguate escapes itself.
fn (t: ^Terminal) escapeDelay*(n: int)
type Key
The enum wrapping the value returned by fn (^Window) getKey.
If getKey returns an ASCII keypress, the value wrapped under this enum will be that key.
[!WARNING] The function keys (F0..F12 and arrow keys, amongst others) do not match the values used by ncurses, and don't currently intend to do so.
type Key* = enum {
tab = 9
newline = 10
enter = 13 // '\r' in raw mode
escape = 0x1b
// Function keys
f0 = 1000; f1; f2; f3; f4; f5; f6; f7; f8; f9; f10; f11; f12
down; up; left; right // Arrow keys
pageUp; pageDown
home; end
insert; delete
backspace
// No key was pressed; applicable when
// `timeout` or `noDelay` have been set
// (technical detail: returned when errno = EAGAIN)
none
}
fn (^Key) string
Returns the string representation of this key. For debugging purposes.
fn (k: ^Key) string*(): str
fn keyF
Similar to ncurses' KEY_F(n) macro.
fn keyF*(i: int): Key { return Key(int(Key.f0)+i) }
const ctrlMask
The bitmask pertaining to CTRL+char presses.
const ctrlMask* = uint8(0x1f)
fn ctrlKey
Returns the CTRL representation of k.
fn ctrlKey*(k: Key): Key { return Key(int(k) & ctrlMask) }
fn (^Window) keypad
Attempts to enable/disable function key detection in w.
Reports whether it was able to do so.
fn (w: ^Window) keypad*(enable: bool): bool
fn (^Window) noDelay
Disables delay entirely (makes fn (^Window) getKey and related non-blocking).
Reports whether the operation succeeded.
fn (w: ^Window) noDelay*(b: bool): bool
Constants for fn (^Window) timeout.
const (
blocking* = -1
nonBlocking*
)
fn (^Window) timeout
Sets the timeout for reading operations (fn (^Window) getKey and others).
Fine-grained; has millisecond-level control, as opposed to man 3x halfdelay.
If n:
- =
-1/blocking, calls togetKeyand friends will wait forever. - =
0/nonBlocking, calls togetKeyand friends will return immediately. - Otherwise, waits up to
nmilliseconds.
fn (w: ^Window) timeout*(n: int)
fn (^Window) setCursorPos
Moves the cursor to (x,y), relative to this window.
Reports whether it was able to do so.
fn (w: ^Window) setCursorPos*(x, y: int): bool
fn (^Window) writeChar
Writes c at the current position of the cursor.
Reports whether it was able to do so.
fn (w: ^Window) writeChar*(c: char): bool
fn (^Window) writeString
Writes n bytes of s at the current position of the cursor.
If n == -1, writes the whole string. (default behavior)
Reports whether it could be written.
Fails if n != -1 and n > len(s).
fn (w: ^Window) writeString*(s: str, n: int = -1): bool
fn (^Window) writeStringAt
Analogous to fn (^Window) writeString, but moves the cursor to (x,y).
fn (w: ^Window) writeStringAt*(x, y: int, s: str, n: int = -1): bool
fn (^Window) print
Prints a formatted string at the current position of the cursor. Reports whether it could be written.
The format string follows fmt.um's syntax.
fn (w: ^Window) print*(fmt: str, a: ..any): bool
fn (^Window) printAt
Moves the cursor to (x,y) and prints a formatted string there.
Reports whether it could be written.
The format string follows fmt.um's syntax.
fn (w: ^Window) printAt*(x, y: int, fmt: str, a: ..any): bool
fn (^Window) getKey
Returns the current keystroke, if any.
Return values depend on the standard terminal's settings; check man 3x getch for information.
fn (w: ^Window) getKey*(): (Key, std::Err)
fn (^Window) refresh
Redraws this window, if applicable.
fn (w: ^Window) refresh*(): bool
type Attribute
Color-independent character attributes.
type Attribute* = enum {
normal // Normal display (no highlight)
standOut // Best highlighting mode of the terminal
underline // Underlining
reverse // Reverse video
blink // Blinking
dim // Half bright
bold // Extra bright or bold
protect // Protected mode
invis // Invisible or blank mode
altCharset // Alternate character set
italic // Italics (non-X/Open extension)
}
fn (^Attribute) string
Returns the string representation of this character attribute. For debugging purposes.
fn (a: ^Attribute) string*(): str
fn (^Window) attrOn
Enables all the attributes listed in attrs (see type Attribute).
Reports whether any of the attributes could not be enabled, and which one.
fn (w: ^Window) attrOn*(attrs: ..Attribute): (bool, Attribute)
fn (^Window) attrOff
Disables all the attributes listed in attrs (see type Attribute).
Reports whether any of the attributes could not be disabled, and which one.
fn (w: ^Window) attrOff*(attrs: ..Attribute): (bool, Attribute)
fn (^Window) attrListOn
Alternate version of fn (^Window) attrOn that explicitly takes a list.
fn (w: ^Window) attrListOn*(attrs: []Attribute): (bool, Attribute)
fn (^Window) attrListOff
Alternate version of fn (^Window) attrOff that explicitly takes a list.
fn (w: ^Window) attrListOff*(attrs: []Attribute): (bool, Attribute)
fn (^Window) getAttributes
Returns the set of attributes and color pair currently applied to this window.
Returns null for the attributes map on error.
fn (w: ^Window) getAttributes*(): (^map[Attribute]bool, ColorPairID)
fn (^Window) setAttributes
Overwrites the set attributes and color pair currently set for this window.
fn (w: ^Window) setAttributes*(attrs: map[Attribute]bool, pair: ColorPairID): bool
fn (^Window) useColorPair
Sets pair as the current color pair for this window's text output.
Reports whether it could not be set.
fn (w: ^Window) useColorPair*(pair: ColorPairID): bool
fn (^Window) detachColorPair
Stops using pair as the current color pair for this window's text output.
Reports whether it could not be disabled.
fn (w: ^Window) detachColorPair*(pair: ColorPairID): bool
type WithFn
Function type taken as argument by all fn (^Window) with* functions.
type WithFn* = fn(win: ^Window)
fn (^Window) withAttrs
Runs f with all attributes in attrs enabled, and disables them when the function ends.
fn (w: ^Window) withAttrs*(attrs: []Attribute, f: WithFn)
fn (^Window) withColorPair
Runs f with pair set as the current color pair, and unsets it when the function ends.
fn (w: ^Window) withColorPair*(pair: ColorPairID, f: WithFn)
fn (^Window) withAttrsAndColorPair
Shorthand for:
win.withAttrs(attrs, { win.withColorPair(pair, f) })
fn (w: ^Window) withAttrsAndColorPair*(attrs: []Attribute, pair: ColorPairID, f: WithFn)
fn (^Window) clear
Clears this window.
fn (w: ^Window) clear*(): bool
fn (^Window) erase
Fills this window with blank characters.
fn (w: ^Window) erase*(): bool
fn (^Window) getSize
Returns the width and height of the window, in that order.
fn (w: ^Window) getSize*(): (int, int)
fn (^Window) getWidth
Returns the width of the window.
Shorthand for w.getSize().item0.
fn (w: ^Window) getWidth*(): int { return w.getSize().item0 }
fn (^Window) getHeight
Returns the height of the window.
Shorthand for w.getSize().item1.
fn (w: ^Window) getHeight*(): int { return w.getSize().item1 }
fn (^Window) getCursorPos
Returns the position of the cursor relative to this window, in (x, y) order.
fn (w: ^Window) getCursorPos*(): (int, int)
fn (^Window) getCursorX
Shorthand for w.getCursorPos().item0.
fn (w: ^Window) getCursorX*(): int { return w.getCursorPos().item0 }
fn (^Window) getCursorY
Shorthand for w.getCursorPos().item1.
fn (w: ^Window) getCursorY*(): int { return w.getCursorPos().item1 }
fn (^Window) moveCursor
Moves the cursor by (dx,dy). Reports whether it was able to do so.
fn (w: ^Window) moveCursor*(dx, dy: int): bool
fn (^Window) moveAndClampCursor
Moves the cursor by (dx,dy). Clamps the resulting position to the window's bounds.
Returns the actual position it reached, in (x,y) order as well.
fn (win: ^Window) moveAndClampCursor*(dx, dy: int): (int, int)
fn (^Window) clearToEOL
Clears the contents from the cursor's location to the end of the line. Reports whether it managed to do so.
fn (w: ^Window) clearToEOL*(): bool
import (
"std.um"
"umbox/fmt/fmt.um"
)
type (
RawWindow = ^void
RawTerminal = ^void
RawColorPair = uint
RawAttribute = int
RawStandardColor = int
)
//~~type Window and type Terminal
// The main abstractions for a terminal and a window in the library.
type (
Window* = struct { _: RawWindow
// Reference back to the terminal owning this window.
terminal: weak ^Terminal
}
Terminal* = struct { _: RawTerminal
// The main window owned by this terminal.
window: Window
// The maximum amount of colors supported in this terminal.
// Set on creation (see `fn mkTerminal`).
maxColors: int
// The amount of color pairs currently allocated.
// Used internally, but can be modified.
pairAmt: int
}
)
//~~
fn umc__errno(): int
fn umc__strerror(errno: int): str
fn errFromCode(): std::Err {
errno := umc__errno()
return std::error(errno, umc__strerror(errno), "catcurses.um")
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
fn umc__set_term(term: RawTerminal): RawTerminal
var curTerm, stdTerm: ^Terminal
fn makeCurrent(t: ^Terminal): bool {
curTerm = t
return umc__set_term(t._) != null
}
//~~fn sessionExists
// Reports whether a terminal is set.
// (In most cases, this serves to check whether ncurses is initialized.)
fn sessionExists*(): bool
//~~
// this function is funny
fn sessionExists*(): bool { return curTerm != null }
//~~fn curTerminal
// Returns the current terminal.
// See `fn (^Terminal) makeCurrent`.
fn curTerminal*(): ^Terminal
//~~
fn curTerminal*(): ^Terminal { return curTerm }
//~~fn (^Terminal) makeCurrent
// Makes `t` the "current" terminal; that is, the terminal whose associated window is shown on-screen.
// Reports whether `t` was successfully set as the current terminal.
fn (t: ^Terminal) makeCurrent*(): bool
//~~
fn (t: ^Terminal) makeCurrent*(): bool { return makeCurrent(t) }
//~~fn stdTerminal
// Returns the standard terminal.
// If ncurses hasn't been initialized yet, this function will initialize it.
fn stdTerminal*(): ^Terminal
//~~
fn umc__initscr(): RawWindow
fn stdTerminal*(): ^Terminal {
if stdTerm == null {
w := umc__initscr()
std::assert(w != null, "couldn't initialize ncurses")
// this is ugly but meh
s := umc__set_term(null)
umc__set_term(s)
stdTerm = &Terminal{ _: s }
stdTerm.window = { _: w, terminal: stdTerm }
stdTerm.makeCurrent()
std::assert(w == stdTerm.window._,
sprintf("initscr window doesn't match stdTerm.window._: %llv vs %llv", w, stdTerm.window._))
}
return stdTerm
}
//~~fn mkTerminal
// Creates a new terminal.
// If `termType` isn't empty, the created terminal is created with said type.
// If `outf` and `inf` are null, they default to `std::stdout()` and `std::stdin()`, respectively.
//
// If no terminal existed previously, initializes ncurses and returns a stub representing the standard terminal.
// `termType`, `outf` and `inf` are ignored in this case.
fn mkTerminal*(termType: str = "", outf: std::File = null, inf: std::File = null): (^Terminal, std::Err)
//~~
fn umc__newterm(termType: str, outf, inf: std::File): RawTerminal
fn umc__curscr(): RawWindow
fn mkTerminal*(termType: str = "", outf: std::File = null, inf: std::File = null): (^Terminal, std::Err) {
if stdTerm == null { return stdTerminal(), {} }
if termType == "" { termType = std::getenv("TERM") }
if outf == null { outf = std::stdout() }
if inf == null { inf = std::stdin() }
raw := umc__newterm(termType, std::stdout(), std::stdin())
if raw == null { return null, errFromCode() }
term := &Terminal{ _: raw }
old := umc__set_term(raw)
w := umc__curscr()
umc__set_term(old)
term.window = { _: w, terminal: term }
return term, {}
}
//~~type ColorPairID
// Identifies a specific, preinitialized color pair.
type ColorPairID* = int
//~~
//~~fn (^ColorPairID) string
// Returns the string representation of this color pair identifier.
// For debugging purposes.
fn (c: ^ColorPairID) string*(): str { return sprintf("", c^) }
//~~
fn umc__toRawColorPair(a: ColorPairID): RawColorPair
fn (a: ^ColorPairID) raw(): uint { return umc__toRawColorPair(a^) }
//~~type StandardColor
// Indices to the standard terminal colors.
type StandardColor* = enum {
black
red
green
yellow
blue
magenta
cyan
white
// "Bright" colors
brightBlack
brightRed
brightGreen
brightYellow
brightBlue
brightMagenta
brightCyan
brightWhite
}
// Aliases
const (
gray* = StandardColor.white
darkGray* = StandardColor.brightBlack
)
//~~
const maxStandardColors = int(StandardColor.brightWhite)+1
//~~fn (^Terminal) supportsColors
// Reports whether this terminal supports colors.
fn (t: ^Terminal) supportsColors*(): bool
//~~
fn umc__has_colors_sp(term: RawTerminal): bool
fn (t: ^Terminal) supportsColors*(): bool { return umc__has_colors_sp(t._) }
//~~fn (^Terminal) canChangeColors
// Reports whether this terminal can change its colors.
fn (t: ^Terminal) canChangeColors*(): bool
//~~
fn umc__can_change_color_sp(term: RawTerminal): bool
fn (t: ^Terminal) canChangeColors*(): bool { return umc__can_change_color_sp(t._) }
//~~fn (^Terminal) enableColors
// Enables color capability. Returns whether it was enabled.
fn (t: ^Terminal) enableColors*(): bool
//~~
fn umc__start_color_sp(term: RawTerminal): bool
fn umc__getCurrentMaxColors(): int
fn (t: ^Terminal) enableColors*(): bool {
if umc__start_color_sp(t._) {
t.maxColors = umc__getCurrentMaxColors()
return true
}
return false
}
//~~fn (^Terminal) setColorPair
// Associates `pair` with `bg` and `fg` as background/foreground colors.
fn (t: ^Terminal) setColorPair*(pair: ColorPairID, bg, fg: StandardColor): bool
//~~
fn umc__toRawStandardColor(c: StandardColor): RawStandardColor
fn umc__init_pair_sp(term: RawTerminal, pair: ColorPairID, bg, fg: RawStandardColor): bool
fn (t: ^Terminal) setColorPair*(pair: ColorPairID, bg, fg: StandardColor): bool {
return umc__init_pair_sp(t._, pair,
umc__toRawStandardColor(bg),
umc__toRawStandardColor(fg)
)
}
//~~fn (t: ^Terminal) addColorPair
// Allocates a new color pair, with `bg` and `fg` as background/foreground colors, and returns its ID.
fn (t: ^Terminal) addColorPair*(bg, fg: StandardColor): ColorPairID
//~~
fn (t: ^Terminal) addColorPair*(bg, fg: StandardColor): ColorPairID {
t.pairAmt++ // 1-indexed, cause IIRC the 0th pair is reserved
newPair := t.pairAmt
if !t.setColorPair(newPair, bg, fg) { return -1 }
return newPair
}
//~~fn (^Terminal) setColor
// Changes the RGB values associated with `color` to `r`, `g` and `b`.
// `r`, `g` and `b` should be normalized floats (0-1, they will be clamped if outside that range),
// and will be quantized to a 0-1000 integer range for ncurses.
//
// This function will affect every color pair using `color` as a palette entry.
//
// It will do nothing (and return `false`) if:
// - the terminal doesn't support colors (see `fn (^Terminal) supportsColors`),
// - the colors are unable to be changed (see `fn (^Terminal) canChangeColors`), or
// - `color` is outside the range of colors the terminal can support (see `type Terminal`)
fn (t: ^Terminal) setColor*(color: StandardColor, r, g, b: real): bool
//~~
fn umc__init_color_sp(term: RawTerminal, color, r, g, b: int): bool
fn (t: ^Terminal) setColor*(color: StandardColor, r, g, b: real): bool {
if !(t.supportsColors() && t.canChangeColors()) { return false }
raw := int(color)
if raw < 0 || raw >= t.maxColors { return false }
if r < 0.0 { r = 0.0 } else if r >= 1.0 { r = 1.0 }
if g < 0.0 { g = 0.0 } else if g >= 1.0 { g = 1.0 }
if b < 0.0 { b = 0.0 } else if b >= 1.0 { b = 1.0 }
ir, ig, ib := round(r * 1000.0), round(g * 1000.0), round(b * 1000.0)
return umc__init_color_sp(t._, raw, ir, ig, ib)
}
//~~fn (^Terminal) setColorBytes
// Alternate version of `fn (^Terminal) setColor` that takes in byte values (0-255).
fn (t: ^Terminal) setColorBytes*(color: StandardColor, r, g, b: uint8): bool
//~~
fn (t: ^Terminal) setColorBytes*(color: StandardColor, r, g, b: uint8): bool {
return t.setColor(color, real(r) / 255.0, real(g) / 255.0, real(b) / 255.0)
}
//~~fn (^Terminal) setColorHex
// Alternate version of `fn (^Terminal) setColor` that takes in a full hex value (0xRRGGBB).
fn (t: ^Terminal) setColorHex*(color: StandardColor, rgb: uint32): bool
//~~
fn (t: ^Terminal) setColorHex*(color: StandardColor, rgb: uint32): bool {
return t.setColorBytes(color,
uint8((rgb >> 16) & 0xff),
uint8((rgb >> 8) & 0xff),
uint8(rgb & 0xff)
)
}
//~~fn (^Terminal) destroy
// Destroys the underlying terminal. `t` is no longer valid for use after calling this method.
// If `t` is the standard terminal, this method will finalize ncurses.
fn (t: ^Terminal) destroy*()
//~~
fn umc__endwin(): bool
fn umc__delscreen(term: RawTerminal)
fn (t: ^Terminal) destroy*() {
if t == stdTerm {
umc__endwin()
return
}
if curTerm == t { makeCurrent(stdTerminal()) }
umc__delscreen(t._)
}
fn assertStdTerm(t: ^Terminal, func: str) {
std::assert(t == stdTerm, sprintf("can't call '%s' on anything other than the standard terminal", func))
}
//~~fn (^Terminal) cbreak
// Enables/disables cbreak mode (disables line buffering but still interprets signals) for the terminal.
// Must only be called on the standard terminal (see `fn stdTerminal`).
// Reports whether the terminal was set/unset to cbreak mode.
fn (t: ^Terminal) cbreak*(b: bool): bool
//~~
fn umc__cbreak(): bool
fn umc__nocbreak(): bool
fn (t: ^Terminal) cbreak*(b: bool): bool {
assertStdTerm(t, "cbreak")
if b { return umc__cbreak() }
return umc__nocbreak()
}
//~~fn (^Terminal) echo
// Enables/disables character echoing after each keypress.
// Must only be called on the standard terminal (see `fn stdTerminal`).
// Reports whether the echo was able to be set/unset.
fn (t: ^Terminal) echo*(b: bool): bool
//~~
fn umc__echo(): bool
fn umc__noecho(): bool
fn (t: ^Terminal) echo*(b: bool): bool {
assertStdTerm(t, "echo")
if b { return umc__echo() }
return umc__noecho()
}
//~~fn (^Terminal) nl
// Controls translation of the return key into NL (10, 0x0a, '\n').
// This allows for detecting whether the return key was pressed, amongst other benefits (see `man 3x nl`).
// Must only be called on the standard terminal (see `fn stdTerminal`).
// Reports whether the translation was able to be enabled/disabled.
fn (t: ^Terminal) nl*(b: bool): bool
//~~
fn umc__nl(): bool
fn umc__nonl(): bool
fn (t: ^Terminal) nl*(b: bool): bool {
assertStdTerm(t, "nl")
if b { return umc__nl() }
return umc__nonl()
}
//~~fn (^Terminal) raw
// Enables/disables raw mode for the terminal.
// Must only be called on the standard terminal (see `fn stdTerminal`).
// Reports whether the terminal was set/unset to raw mode.
fn (t: ^Terminal) raw*(b: bool): bool
//~~
fn umc__raw(): bool
fn umc__noraw(): bool
fn (t: ^Terminal) raw*(b: bool): bool {
assertStdTerm(t, "raw")
if b { return umc__raw() }
return umc__noraw()
}
//~~type Visibility
// Cursor visibility specifiers. See `fn (^Terminal) cursorVisibility`.
type Visibility* = enum { hidden; visible; veryVisible }
//~~
//~~fn (^Visibility) string
// Returns the string representation of this visibility specifier.
// For debugging purposes.
fn (v: ^Visibility) string*(): str
//~~
fn (v: ^Visibility) string*(): str {
switch v^ {
case .hidden: return "hidden"
case .visible: return "visible"
case .veryVisible: return "veryVisible"
}
std::assert(false, "unreachable")
return ""
}
//~~fn (^Terminal) cursorVisibility
// Changes the cursor visibility to `vis` (see `type Visibility`).
// Must only be called on the standard terminal (see `fn stdTerminal`).
// Returns the previous visibility and whether it was able to change it at all.
fn (t: ^Terminal) cursorVisibility*(v: Visibility): (Visibility, bool)
//~~
fn umc__curs_set(v: Visibility, prev: ^Visibility): bool
fn (t: ^Terminal) cursorVisibility*(v: Visibility): (Visibility, bool) {
var prev: Visibility
// I love that the enum maps directly to the value required
if !umc__curs_set(v, &prev) { return .hidden, false }
return prev, true
}
//~~fn (^Terminal) escapeDelay
// Changes the amount of time ncurses waits for further input to disambiguate escapes from escape sequences.
// Only applicable when `keypad(true)`; otherwise, the program must disambiguate escapes itself.
fn (t: ^Terminal) escapeDelay*(n: int)
//~~
fn umc__setEscDelay(n: int)
fn (t: ^Terminal) escapeDelay*(n: int) { umc__setEscDelay(n) }
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//~~type Key
// The enum wrapping the value returned by `fn (^Window) getKey`.
// If getKey returns an ASCII keypress, the value wrapped under this enum will be that key.
//
// > [!WARNING]
// > The function keys (F0..F12 and arrow keys, amongst others)
// > *do not match* the values used by ncurses, and don't currently intend to do so.
type Key* = enum {
tab = 9
newline = 10
enter = 13 // '\r' in raw mode
escape = 0x1b
// Function keys
f0 = 1000; f1; f2; f3; f4; f5; f6; f7; f8; f9; f10; f11; f12
down; up; left; right // Arrow keys
pageUp; pageDown
home; end
insert; delete
backspace
// No key was pressed; applicable when
// `timeout` or `noDelay` have been set
// (technical detail: returned when errno = EAGAIN)
none
}
//~~
//~~fn (^Key) string
// Returns the string representation of this key.
// For debugging purposes.
fn (k: ^Key) string*(): str
//~~
var keyStringReprs: map[Key]str
// TODO(thacuber2a03): should likely automate this
fn (k: ^Key) string*(): str {
if !valid(keyStringReprs) {
keyStringReprs = {
.escape: "escape",
.f0: "f0", .f1: "f1", .f2: "f2", .f3: "f3", .f4: "f4", .f5: "f5", .f6: "f6",
.f7: "f7", .f8: "f8", .f9: "f9", .f10: "f10", .f11: "f11", .f12: "f12",
.down: "down", .up: "up", .left: "left", .right: "right",
.pageUp: "pageUp", .pageDown: "pageDown",
.home: "home", .end: "end",
.delete: "delete", .backspace: "backspace",
.none: "none",
}
}
return keyStringReprs[k^]
}
// for `fn (^Window) getKey` and for `keys` array reference in catcurses.c
const otherKeyStart = int(Key.down)
//~~fn keyF
// Similar to ncurses' `KEY_F(n)` macro.
fn keyF*(i: int): Key { return Key(int(Key.f0)+i) }
//~~
//~~const ctrlMask
// The bitmask pertaining to CTRL+char presses.
const ctrlMask* = uint8(0x1f)
//~~
//~~fn ctrlKey
// Returns the CTRL representation of `k`.
fn ctrlKey*(k: Key): Key { return Key(int(k) & ctrlMask) }
//~~
//~~fn (^Window) keypad
// Attempts to enable/disable function key detection in `w`.
// Reports whether it was able to do so.
fn (w: ^Window) keypad*(enable: bool): bool
//~~
fn umc__keypad(win: RawWindow, enable: bool): bool
fn (w: ^Window) keypad*(enable: bool): bool { return umc__keypad(w._, enable) }
//~~fn (^Window) noDelay
// Disables delay entirely (makes `fn (^Window) getKey` and related non-blocking).
// Reports whether the operation succeeded.
fn (w: ^Window) noDelay*(b: bool): bool
//~~
fn umc__nodelay(win: RawWindow, bf: bool): bool
fn (w: ^Window) noDelay*(b: bool): bool { return umc__nodelay(w._, b) }
//~~
// Constants for `fn (^Window) timeout`.
const (
blocking* = -1
nonBlocking*
)
//~~
//~~fn (^Window) timeout
// Sets the timeout for reading operations (`fn (^Window) getKey` and others).
// Fine-grained; has millisecond-level control, as opposed to `man 3x halfdelay`.
//
// If `n`:
// - = `-1`/`blocking`, calls to `getKey` and friends will wait forever.
// - = `0`/`nonBlocking`, calls to `getKey` and friends will return immediately.
// - Otherwise, waits up to `n` milliseconds.
fn (w: ^Window) timeout*(n: int)
//~~
fn umc__wtimeout(win: RawWindow, delay: int)
fn (w: ^Window) timeout*(n: int) { umc__wtimeout(w._, n) }
//~~fn (^Window) setCursorPos
// Moves the cursor to (`x`,`y`), relative to this window.
// Reports whether it was able to do so.
fn (w: ^Window) setCursorPos*(x, y: int): bool
//~~
fn umc__wmove(win: RawWindow, x, y: int): bool
fn (w: ^Window) setCursorPos*(x, y: int): bool { return umc__wmove(w._, x, y) }
//~~fn (^Window) writeChar
// Writes `c` at the current position of the cursor.
// Reports whether it was able to do so.
fn (w: ^Window) writeChar*(c: char): bool
//~~
fn umc__waddch(w: RawWindow, c: char): bool
fn (w: ^Window) writeChar*(c: char): bool { return umc__waddch(w._, c) }
fn umc__waddnstr(win: RawWindow, s: str, n: int): bool
//~~fn (^Window) writeString
// Writes `n` bytes of `s` at the current position of the cursor.
// If `n` == `-1`, writes the whole string. (default behavior)
// Reports whether it could be written.
// Fails if `n` != `-1` and `n` > `len(s)`.
fn (w: ^Window) writeString*(s: str, n: int = -1): bool
//~~
fn (w: ^Window) writeString*(s: str, n: int = -1): bool {
if n != -1 && n > len(s) { return false }
return umc__waddnstr(w._, s, n)
}
//~~fn (^Window) writeStringAt
// Analogous to `fn (^Window) writeString`, but moves the cursor to (`x`,`y`).
fn (w: ^Window) writeStringAt*(x, y: int, s: str, n: int = -1): bool
//~~
fn (w: ^Window) writeStringAt*(x, y: int, s: str, n: int = -1): bool {
if !w.setCursorPos(x, y) { return false }
return w.writeString(s, n)
}
//~~fn (^Window) print
// Prints a formatted string at the current position of the cursor.
// Reports whether it could be written.
//
// The format string follows [fmt.um](https://umbox.tophat2d.dev/package/fmt/browse#:~:text=Syntax)'s syntax.
fn (w: ^Window) print*(fmt: str, a: ..any): bool
//~~
fn (w: ^Window) print*(fmt: str, a: ..any): bool { return w.writeString(fmt::vsfmt(fmt, a)) }
//~~fn (^Window) printAt
// Moves the cursor to (`x`,`y`) and prints a formatted string there.
// Reports whether it could be written.
//
// The format string follows [fmt.um](https://umbox.tophat2d.dev/package/fmt/browse#:~:text=Syntax)'s syntax.
fn (w: ^Window) printAt*(x, y: int, fmt: str, a: ..any): bool
//~~
fn (w: ^Window) printAt*(x, y: int, fmt: str, a: ..any): bool { return w.writeStringAt(x, y, fmt::vsfmt(fmt, a)) }
//~~fn (^Window) getKey
// Returns the current keystroke, if any.
// Return values depend on the standard terminal's settings; check `man 3x getch` for information.
fn (w: ^Window) getKey*(): (Key, std::Err)
//~~
fn umc__wgetch(win: RawWindow, c: ^int): bool
fn umc__errnoIsAgain(): bool
fn umc__fnKeyOffset(key: int): int
fn umc__otherKeyOffset(key: int): int
fn (w: ^Window) getKey*(): (Key, std::Err) {
var c: int
if !umc__wgetch(w._, &c) {
if umc__errnoIsAgain() { return .none, {} }
return .none, errFromCode()
}
if c >= 256 {
if offset := umc__fnKeyOffset(c); offset >= 0 && offset <= 12 {
return keyF(offset), {}
} else if offset := umc__otherKeyOffset(c); offset != -1 {
return Key(otherKeyStart+offset), {}
}
}
return Key(c), {}
}
//~~fn (^Window) refresh
// Redraws this window, if applicable.
fn (w: ^Window) refresh*(): bool
//~~
fn umc__wrefresh(win: RawWindow): bool
fn (w: ^Window) refresh*(): bool { return umc__wrefresh(w._) }
//~~type Attribute
// Color-independent character attributes.
type Attribute* = enum {
normal // Normal display (no highlight)
standOut // Best highlighting mode of the terminal
underline // Underlining
reverse // Reverse video
blink // Blinking
dim // Half bright
bold // Extra bright or bold
protect // Protected mode
invis // Invisible or blank mode
altCharset // Alternate character set
italic // Italics (non-X/Open extension)
}
//~~
const charAttributesAmt = int(Attribute.italic)+1
//~~fn (^Attribute) string
// Returns the string representation of this character attribute.
// For debugging purposes.
fn (a: ^Attribute) string*(): str
//~~
fn (a: ^Attribute) string*(): str {
switch a^ {
case .normal: return "normal"
case .standOut: return "standOut"
case .underline: return "underline"
case .reverse: return "reverse"
case .blink: return "blink"
case .dim: return "dim"
case .bold: return "bold"
case .protect: return "protect"
case .invis: return "invis"
case .altCharset: return "altCharset"
case .italic: return "italic"
}
std::assert(false, "unreachable")
return ""
}
fn umc__toRawAttr(a: Attribute): RawAttribute
fn (a: ^Attribute) raw(): uint { return umc__toRawAttr(a^) }
// fn umc__from_raw_attr(a: RawAttribute): Attribute
fn umc__wattr_on(win: RawWindow, a: RawAttribute): bool
fn umc__wattr_off(win: RawWindow, a: RawAttribute): bool
fn umc__wattr_get(win: RawWindow, attr: ^RawAttribute, pair: ^ColorPairID): bool
fn umc__wattr_set(win: RawWindow, attr: RawAttribute, pair: ColorPairID): bool
//~~fn (^Window) attrOn
// Enables all the attributes listed in `attrs` (see `type Attribute`).
// Reports whether any of the attributes could not be enabled, and which one.
fn (w: ^Window) attrOn*(attrs: ..Attribute): (bool, Attribute)
//~~
//~~fn (^Window) attrOff
// Disables all the attributes listed in `attrs` (see `type Attribute`).
// Reports whether any of the attributes could not be disabled, and which one.
fn (w: ^Window) attrOff*(attrs: ..Attribute): (bool, Attribute)
//~~
//~~fn (^Window) attrListOn
// Alternate version of `fn (^Window) attrOn` that explicitly takes a list.
fn (w: ^Window) attrListOn*(attrs: []Attribute): (bool, Attribute)
//~~
//~~fn (^Window) attrListOff
// Alternate version of `fn (^Window) attrOff` that explicitly takes a list.
fn (w: ^Window) attrListOff*(attrs: []Attribute): (bool, Attribute)
//~~
fn (w: ^Window) attrListOn*(attrs: []Attribute): (bool, Attribute) {
for _,a in attrs { if !umc__wattr_on(w._, a.raw()) { return false, a } }
return true, .normal
}
fn (w: ^Window) attrListOff*(attrs: []Attribute): (bool, Attribute) {
for _,a in attrs { if !umc__wattr_off(w._, a.raw()) { return false, a } }
return true, .normal
}
fn (w: ^Window) attrOn*(attrs: ..Attribute): (bool, Attribute) { return w.attrListOn(attrs) }
fn (w: ^Window) attrOff*(attrs: ..Attribute): (bool, Attribute) { return w.attrListOff(attrs) }
//~~fn (^Window) getAttributes
// Returns the set of attributes and color pair currently applied to this window.
// Returns `null` for the attributes map on error.
fn (w: ^Window) getAttributes*(): (^map[Attribute]bool, ColorPairID)
//~~
//~~fn (^Window) setAttributes
// Overwrites the set attributes and color pair currently set for this window.
fn (w: ^Window) setAttributes*(attrs: map[Attribute]bool, pair: ColorPairID): bool
//~~
fn (w: ^Window) getAttributes*(): (^map[Attribute]bool, ColorPairID) {
var (
attr: RawAttribute
pair: ColorPairID
)
if !umc__wattr_get(w._, &attr, &pair) { return null, -1 }
attrs := &map[Attribute]bool{}
for i := 0; i < charAttributesAmt; i++ {
a := Attribute(i)
if attr & a.raw() != 0 { attrs[a] = true }
}
return attrs, pair
}
fn (w: ^Window) setAttributes*(attrs: map[Attribute]bool, pair: ColorPairID): bool {
var raw: RawAttribute
for a,b in attrs { if b { raw |= a.raw() } }
return umc__wattr_set(w._, raw, pair)
}
//~~fn (^Window) useColorPair
// Sets `pair` as the current color pair for this window's text output.
// Reports whether it could not be set.
fn (w: ^Window) useColorPair*(pair: ColorPairID): bool
//~~
//~~fn (^Window) detachColorPair
// Stops using `pair` as the current color pair for this window's text output.
// Reports whether it could not be disabled.
fn (w: ^Window) detachColorPair*(pair: ColorPairID): bool
//~~
fn (w: ^Window) useColorPair*(pair: ColorPairID): bool { return umc__wattr_on(w._, pair.raw()) }
fn (w: ^Window) detachColorPair*(pair: ColorPairID): bool { return umc__wattr_off(w._, pair.raw()) }
//~~type WithFn
// Function type taken as argument by all `fn (^Window) with*` functions.
type WithFn* = fn(win: ^Window)
//~~
//~~fn (^Window) withAttrs
// Runs `f` with all attributes in `attrs` enabled, and disables them when the function ends.
fn (w: ^Window) withAttrs*(attrs: []Attribute, f: WithFn)
//~~
fn (w: ^Window) withAttrs*(attrs: []Attribute, f: WithFn) {
a, p := w.getAttributes()
std::assert(a != null, "couldn't get attributes for withAttrs")
w.attrListOn(attrs)
f(w)
w.setAttributes(a^, p)
}
//~~fn (^Window) withColorPair
// Runs `f` with `pair` set as the current color pair, and unsets it when the function ends.
fn (w: ^Window) withColorPair*(pair: ColorPairID, f: WithFn)
//~~
fn (w: ^Window) withColorPair*(pair: ColorPairID, f: WithFn) {
a, p := w.getAttributes()
std::assert(a != null, "couldn't get color pair for withColorPair")
w.useColorPair(pair)
f(w)
w.setAttributes(a^, p)
}
//~~fn (^Window) withAttrsAndColorPair
// Shorthand for:
// ```go
// win.withAttrs(attrs, { win.withColorPair(pair, f) })
// ```
fn (w: ^Window) withAttrsAndColorPair*(attrs: []Attribute, pair: ColorPairID, f: WithFn)
//~~
fn (w: ^Window) withAttrsAndColorPair*(attrs: []Attribute, pair: ColorPairID, f: WithFn) {
w.withAttrs(attrs, |pair, f| { win.withColorPair(pair, f) })
}
//~~fn (^Window) clear
// Clears this window.
fn (w: ^Window) clear*(): bool
//~~
fn umc__clear(win: RawWindow): bool
fn (w: ^Window) clear*(): bool { return umc__clear(w._) }
//~~fn (^Window) erase
// Fills this window with blank characters.
fn (w: ^Window) erase*(): bool
//~~
fn umc__erase(win: RawWindow): bool
fn (w: ^Window) erase*(): bool { return umc__erase(w._) }
//~~fn (^Window) getSize
// Returns the width and height of the window, in that order.
fn (w: ^Window) getSize*(): (int, int)
//~~
fn umc__getmaxxy(win: RawWindow, x, y: ^int)
fn (w: ^Window) getSize*(): (int, int) {
var x, y: int
umc__getmaxxy(w._, &x, &y)
return x, y
}
//~~fn (^Window) getWidth
// Returns the width of the window.
// Shorthand for `w.getSize().item0`.
fn (w: ^Window) getWidth*(): int { return w.getSize().item0 }
//~~
//~~fn (^Window) getHeight
// Returns the height of the window.
// Shorthand for `w.getSize().item1`.
fn (w: ^Window) getHeight*(): int { return w.getSize().item1 }
//~~
//~~fn (^Window) getCursorPos
// Returns the position of the cursor relative to this window, in (`x`, `y`) order.
fn (w: ^Window) getCursorPos*(): (int, int)
//~~
fn umc__getxy(win: RawWindow, x, y: ^int)
fn (w: ^Window) getCursorPos*(): (int, int) {
var x, y: int
umc__getxy(w._, &x, &y)
return x, y
}
//~~fn (^Window) getCursorX
// Shorthand for `w.getCursorPos().item0`.
fn (w: ^Window) getCursorX*(): int { return w.getCursorPos().item0 }
//~~
//~~fn (^Window) getCursorY
// Shorthand for `w.getCursorPos().item1`.
fn (w: ^Window) getCursorY*(): int { return w.getCursorPos().item1 }
//~~
//~~fn (^Window) moveCursor
// Moves the cursor by (`dx`,`dy`). Reports whether it was able to do so.
fn (w: ^Window) moveCursor*(dx, dy: int): bool
//~~
fn (w: ^Window) moveCursor*(dx, dy: int): bool {
x, y := w.getCursorPos()
return w.setCursorPos(x+dx, y+dy)
}
//~~fn (^Window) moveAndClampCursor
// Moves the cursor by (`dx`,`dy`). Clamps the resulting position to the window's bounds.
// Returns the actual position it reached, in (`x`,`y`) order as well.
fn (win: ^Window) moveAndClampCursor*(dx, dy: int): (int, int)
//~~
fn (win: ^Window) moveAndClampCursor*(dx, dy: int): (int, int) {
x, y := win.getCursorPos()
w, h := win.getSize()
nx, ny := x+dx, y+dy
if nx < 0 { nx = 0 }; if nx >= w { nx = w - 1 }
if ny < 0 { ny = 0 }; if ny >= h { ny = h - 1 }
win.setCursorPos(nx, ny)
return nx, ny
}
//~~fn (^Window) clearToEOL
// Clears the contents from the cursor's location to the end of the line.
// Reports whether it managed to do so.
fn (w: ^Window) clearToEOL*(): bool
//~~
fn umc__wclrtoeol(win: RawWindow): bool
fn (w: ^Window) clearToEOL*(): bool { return umc__wclrtoeol(w._) }