NAME
Game::Terminal::Menu - the menu the game command draws
VERSION
Version 0.02
SYNOPSIS
use Game::Terminal::Menu;
exit Game::Terminal::Menu->new->start;
my $menu = Game::Terminal::Menu->new(
common => { ascii => 1, colour => 0 },
keysource => sub { shift @keys },
runner => sub { my ($row, $argv, %seam) = @_; ... ; return (0, undef) },
);
DESCRIPTION
The roster of Game::Terminal as a list on the screen: a cursor, the highlighted game's class and version on a line underneath, the result of the last game, and a legend. Enter opens the game under the cursor's options on a second screen ("configure"); enter on Play starts it in this process, and when it ends the menu is drawn again with its result and the options still set.
Everything the menu reads comes through "read_key", which takes the keys from Term::ReadKey when there is a terminal and from "keysource" when a test supplies them, so nothing here needs a tty to be tested. Without a terminal, without Term::ReadKey, or with picking off, the menu is a numbered list and a prompt instead.
The screen
Game 20 games
> backgammon board 2 backgammon
brandubh board 2 the Irish 7 by 7 tafl game
...
xiangqi board 2 xiangqi, Chinese chess
backgammon: Game::Backgammon::Terminal 0.03, options: game backgammon --help
checkers finished with status 1 (lost)
up and down to choose, enter to play, a letter to jump, q to quit
The cursor's row is reverse video when colour is on. A game whose class is not on @INC is dimmed, marked (not installed), and skipped by the cursor. When the terminal has fewer rows than the list needs the list is windowed round the cursor with N further up and N further down lines.
The keys
Up and down (also k and j, and tab for down) move the cursor and wrap; home and end go to the first and last game; enter or space starts the game under the cursor; a letter or digit jumps to the next game whose name starts with it, cycling, so s twice goes from schnapsen to sixtysix; ? says what else the keys do; q, escape, EOF or an interrupt leave the menu.
Colour
Colour is on when the input is a terminal and NO_COLOR is not set, and off otherwise or when colour is given false. With it on, the cursor's row is reverse video; a game's name is bold and its family has a colour of its own (board blue, card red, tile yellow, puzzle magenta, arcade cyan); a game that is not installed is dim; the detail line is grey and the legend dim; and the status line takes a tone from what happened: green for a win, a draw or a quit, red for a loss or a game that died, yellow for a bad option or a refused value. On the options screen a value the player set is green and one left to the game is dim, and Play is green.
ATTRIBUTES
in
out
The handles, STDIN and STDOUT by default.
interactive
True when in is a terminal. Decides whether the screen is cleared and colour is on.
colour
Whether escape codes are written.
picking
Whether the menu is driven by keys (true) or by the numbered prompt. On by default when there is a terminal and the keys are available.
keysource
A code ref that returns the next character, or undef at the end, given to new. When set, the keys come from it and the terminal is never touched: the seam every test drives.
pending
Characters to be read before any others, an array reference given to new.
keyboard
The Game::Terminal::Kit::Keys the keys are read through, built from in, keysource and pending unless one is given.
screen
The Game::Terminal::Kit::Screen everything is printed through, built from out, interactive and colour unless one is given.
runner
A code ref called as $runner->($row, \@argv, %seam) to run a game, returning ($status, $error). "launch" in Game::Terminal by default; a test supplies one that records the call and runs nothing.
common
The launcher's own options as a hash: ascii, colour and pick. ascii and pick are spelt in each game's words by "flags_for" in Game::Terminal; colour false sets NO_COLOR around the game.
inc
The directories searched for a game's class, @INC by default. A test points it at a directory of fakes.
games
The roster rows with an installed flag on each, built from inc.
at
The index of the cursor.
status
The one-line result of the last game, shown under the detail line until the next game ends.
tone
What the status line is about: good, bad, warn, or nothing. It picks the status line's colour.
note
$menu->note('checkers finished with status 1 (lost)', 'bad');
Sets "status" and "tone" together.
status_line
The status painted in its tone, or nothing when there is no status.
rows
The terminal's height, from Term::ReadKey::GetTerminalSize when there is a terminal, and 0 when it is not known; 0 means the list is never windowed.
values
What the player has set on each game's options screen this session, a hash of field name to value under the game's key. A game opened again finds its values where they were left.
METHODS
start
my $status = $menu->start;
Draws the menu, runs each game chosen until the menu is left, puts the terminal back, and returns 0. An interrupt or a hangup while the menu is up puts the terminal back and is then raised again as if nothing had caught it.
choose
One pass of the key loop: draws, reads keys, moves the cursor, and returns the chosen row, or undef when the menu is left.
configure
my $argv = $menu->configure($row);
The second screen: the row's options from "options_for" in Game::Terminal with their current values, an extra line for anything else, and Play.
checkers: English draughts
level the game's own the strength of the bot
side the game's own your side; black moves first
hotseat off two people at one keyboard, no bot
bot-vs-bot off watch two bots play
seed the game's own the bot varies its easy levels with it
extra (none) anything else, as on the command line
> Play
left and right change a value, enter edits a text, backspace clears,
enter on Play starts, escape goes back
The cursor starts on Play, so enter twice from the menu plays a game with its own defaults. Up and down move (down from Play wraps to the first field); left and right step a value as "step_value" in Game::Terminal says; enter on an enum, flag or bool steps it too, and on a typed field reads a line; backspace puts a field back to the game's own; p, or enter on Play, returns the argument list the values make; escape or q returns undef and nothing runs. A field left alone sends nothing.
form
my $form = $menu->form($row);
The Game::Terminal::Kit::Form behind the second screen: the row's fields, and the values the player has set for that game this session.
read_line
my $line = $menu->read_line('seed: ');
Leaves cbreak mode, prints the prompt, reads one line from in, trimmed, and goes back to cbreak mode. Undef at end of input.
ask_options
The prompt's stand-in for the form when there are no keys: one line of options as on the command line, split like a shell, or none for blank.
prompt
The numbered list and Which game? (number, name, or q), read with a line from in. A number is a row, anything else is looked up with "find" in Game::Terminal, a blank line or q leaves. Returns the row or undef; "ask_options" then asks for the options.
launch
$menu->launch($row, \@argv);
Leaves cbreak mode, clears the screen, runs the row through "runner" with the flags "common" asks for followed by @argv, resets the colour and shows the cursor, goes back to cbreak mode, and sets "status" from the result.
describe
The status line for a result: checkers finished with status 1 (lost) for 0, 1 and 2, the bare number for anything else, and checkers died: ... when the game died.
draw
Clears and draws the whole screen.
list_lines
row_line
detail_lines
The lines of the list, windowed when "rows" asks for it; one row of it; and the detail and status lines under it.
move
jump
first_installed
last_installed
Cursor movement: by a step with wrapping, to the next game starting with a letter, and to the ends. All skip games that are not installed.
height
The terminal's height in rows, or 0 when there is no terminal to ask.
keys_available
True when keys can be read: a keysource is set, or in is a terminal and Term::ReadKey loads.
enter_raw
leave_raw
Read the terminal a key at a time, and give it back. Both only keep the flag with a keysource.
raw
True while the terminal is being read a key at a time.
read_key
One key, with the control characters and escape sequences named (up, enter, escape, ...) as Game::Terminal::Kit::Keys names them.
say
clear
paint
A line to out; the screen cleared when interactive; text wrapped in an SGR code when colour is on.
SEE ALSO
AUTHOR
LNATION, <email at lnation.org>
LICENSE AND COPYRIGHT
This software is Copyright (c) 2026 by LNATION.
This is free software, licensed under:
The Artistic License 2.0 (GPL Compatible)