|Gunnar Zötl 10abdf2c68|
Author: Gunnar Zötl , 2014-2015.
Released the terms of the MIT license. See file LICENSE for details.
TermFX is a library for fancy terminal output based on termbox. It means to reduce the weirdness termbox has in places, and provide some additional utilities like offscreen buffers, and generally a more lua like interface. TermFX has been tested with lua 5.1 to 5.3 beta on Linux and Mac OS X.
Basic usage is very similar to termbox, but also different enough that it warrants some of its own documentation.
Like in termbox, the screen is made up of cells, each consisting of a
character, a foreground- and a background attribute. For an explanation
of these see
termfx.attribute(). Unlike termbox, the top left coordinate
is 1, 1, and named colors work in all output modes.
All rendering is done into a buffer, whose contents is then drawn to the
actual display using the function
termfx.present(). In the following
discussion of the functions, when I write that something operates on the
terminal, it actually operates on this buffer.
The version of termbox I built this against is included in the archive. So all you need to do is to cd into the source directory and call
make && make install
If you want to build termfx against a newer version of termbox, just type
That will remove the termbox subdirectory. If you subsequently call make,
the most recent version of termbox will be pulled from the github repo.
If you don't have git installed, you can download the most recent termbox
branch from https://github.com/nsf/termbox/archive/master.zip, unpack
it into the source directory, rename the resulting
to termbox, and then the above should work.
When using luarocks, a simple
luarocks install termfx
should do the trick, or alternatively, if you've already downloaded it, enter the source directory and type
sudo luarocks make
which should do what you want. Again, if you want to build against a more
recent version of termbox than the included, just call
before that, and all should be well.
Load the module with:
termfx = require "termfx"
You must call
termfx.init() before you can do any output. At the end of
your program, you should call
termfx.shutdown(), however, the library
registers an atexit handler that does this when the interpreter exits,
if it has not been done before.
All coordinates on termfx are 1-based, meaning the top left corner of
the terminal has the coordinates 1, 1 and the bottom right corner has
termfx.init() sets the terminal into a mode where you can no
longer see any error messages the program might throw. It is therefore
recommended that, at least during development, you wrap your main loop
into a pcall()ed function, like so:
termfx.init() ... ok, err = pcall(function() repeat evt = termfx.pollevent() ... until some_exit_condition end) termfx.shutdown() if not ok then print("Error: "..err) end
- initializes the library and the display.
termfx.init()may fail and in that case throws an error. All of the other functions, except for buffer and cell constructors and methods, work only after the terminal has been initialized using this function, and will silently fail, or in the case of queries return nil, if that is not the case. Because of this, you can for example check whether the terminal has been initialized by querying its width and checking the result against nil.
- shuts the library down and puts the terminal into a sane mode for normal usage.
w = termfx.width()
- returns the width of the terminal.
h = termfx.height()
- returns the height of the terminal.
w, h = termfx.size()
- returns width and height of the terminal.
- clears the terminal. If no arguments are given, the default
foreground and background attributes are used (see
termfx.attributes()), otherwise the default attributes are set from the arguments passed to
termfx.clear()and then used.
fg, bg = termfx.attributes([fg, bg])
- sets or, if called without arguments, gets the default foreground
and background attributes. Attributes are colors and formatting like
bold or underline. Colors are simple integers in the range 0-255, or
one of the color constants from
termfx.color, and formatting is one or more of the constants from
termfx.format. Attributes and constants can be added together for the final foreground or background attribute. Returns the default foreground and background attributes, or those just set.
col = termfx.rgb2color(r, g, b)
- maps a set of rgb values to a xterm color number. r, g, b must be in
the range 0..5, which makes up the 216 available colors. Returns a
color number, which is corrected for the output mode in use, or nil
if the input could not be mapped to a color number. This function
only works in
termfx.output.COL256modes, in the other modes it will always return nil.
col = termfx.grey2color(v)
- maps a single greyscale value to an xterm color number. v must be in
the range 0..25, which makes for 26 grey values. Returns a color
number, which is corrected for the output mode in use, or nil if the
input could not be mapped to a color number. This function only
termfx.output.GREYSCALEmodes, in the other modes it will always return nil. Also, as in
tfx.output.GREYSCALEmode there are only 24 available grey values, the values 0 and 1 map to the same color number, as do 24 and 25.
value, r, g, b = termfx.colorinfo(n)
- returns the color value (a rgb string with format "#abcdef") and
red, green and blue values for a color number. The range of n
depends on the current output mode:
- 0..255 for
- 0..215 for
- 0..23 for
- 0..15 for
For values outside of the valid range for the current output mode, the function will return nil.
- 0..255 for
- output the contents of TermFX's back buffer, into which the rendering is done, to the terminal.
- sets the cursor to position x, y and unhides it.
- hides the cursor.
cel = termfx.newcell([, ch [, fg [, bg]]])
- creates a new offscreen cell. You can use cells in functions like
termfx.rect(). If you pass any or all of the arguments, they are used in the construction of the cell, otherwise the respective values default to space (' ') for the char, and the default foreground and background attributes. The returned cell has 3 attributes you can read or set, which are ch, fg and bg. For more information see the section on TermFX's cells below.
termfx.setcell(x, y [, cel])or
termfx.setcell(x, y [, ch [, fg [, bg]]])
- sets the cell with the coordinates x, y on the terminal. For the
cells attributes, you can pass in a cell as created by
termfx.newcell(), or values. If you pass any or all of the arguments, they are used for the cell on the terminal, otherwise the respective values default to space (' ') for the char, and the default foreground and background attributes.
cel = termfx.getcell(x, y)
- gets the cell from the coordinates x, y on the terminal. If x or y lie outside of the terminal, nil is returned.
buf = termfx.newbuffer(w, h)
- creates a new offscreen buffer with width w and height h. The new buffer gets the default foreground and background attributes as its local defaults. This may be changed with a method call on the buffer. For more information see the section on buffer methods below.
termfx.blit(x, y, buf)
- blits the contents of the buffer to the terminal placing the buffers top left corner at the coordinates x, y.
termfx.rect(x, y, w, h [, cel])or
ok = termfx.rect(x, y, w, h [, ch [, fg [, bg]]])
- draws a rectangle on the terminal with top left at x, y and width w
and height h. For the rectangles attributes, you can pass in a cell
as created by
termfx.newcell(), or the values. If you pass any or all of the arguments, they are used for the rectangle on the terminal, otherwise the respective values default to space (' ') for the char, and the default foreground and background attributes.
termfx.copyregion(tx, ty, x, y, w, h)
- copy the the rectangle starting at x, y with width w and height h to position tx, ty on the terminal. Source and target rectangle may overlap.
termfx.scrollregion(x, y, w, h [, sx [, sy]])
- scrolls the rectangle starting at x, y with width w and height h on the terminal by sx and sy. sx and sy are optional and default to 0. If they have other values, only their sign is used. Scrolling is by 1 column or row at most.
termfx.printat(x, y, what [, w])
- prints a string to the terminal at position x, y up to maximum width w. If w is omitted, it defaults to the length of what. what can be a string, or a table of cells. If it is a string, it is printed in the default foreground and background colors, if it is a table of cells, the char and attributes from each cell are used.
mode = termfx.inputmode([newmode])
- sets the terminal input mode. Valid modes are
termfx.input.ALT. If the newmode argument was not given, returns the current input mode, otherwise returns the mode just set.
termfx.input.ESC: when an ESC sequence is in the buffer and it doesn't match any known ESC sequence, ESC means
termfx.input.ALT: when an ESC sequence is in the buffer and it doesn't match any known sequence, ESC enables
"ALT"modifier for the next keyboard event.
termfx.input.MOUSE: specify this if you are interested in mouse events. This can be added / or'ed to
mode = termfx.outputmode([newmode])
- sets the terminal output mode. See the section on colors and attributes below. If the newmode argument was not given, returns the current output mode, otherwise returns the mode just set.
evt = termfx.pollevent([timeout])
- waits for an event to occur. If the timeout argument is given, the
function will time out after timeout milliseconds, otherwise it will
wait forever. On Timeout, the function returns nil, on error it
returns nil+error message, otherwise it returns a table with
information about the event. Known events are
"key"for a keyboard event,
"mouse"for a mouse event or
"resize"if the terminal was resized. Depending on which event occurred, the result table looks like this:
For the event
elapsed: the number of milliseconds spent in
"ALT", no other modifiers are returned
key: the numeric code of the key pressed, if it was a special key
ch: the numeric code of the key pressed for standard keys
char: the char (possibly utf8 encoded) for ch
either the field
keyor the field
chis nil. If the
keyfield is not nil, it can be checked against one of the constants in
termfx.key. If the
chfield is not nil, it is the unicode value of the char that was pressed on the keyboard. In that case, the
charfield will contain a string containing the character represented by
For the event
elapsed: the number of milliseconds spent in
key: the numeric code of the button pressed
y: the terminal coordinates where the mouse event occurred.
on release of any button, the value of key will be termfx.key.MOUSE_RELEASED
For the event
elapsed: the number of milliseconds spent in
w: the new width
h: the new height
The buffer created by
termfx.newbuffer() has a few methods associated
with it. They work just like their global counterparts, only on the
buffer on which they are called. So for a detailed description of the
individual methods see above.
- set or get default cell attributes for the buffer.
- clear the buffer.
buf:setcell(x, y [, cel]) or buf:setcell(x, y [, ch [, fg [, bg]]])
- set a cell in the buffer.
cel = buf:getcell(x, y)
- gets a cell from a buffer.
buf:blit(x, y, src)
- blits the contents of the buffer src to position x, y of buffer buf.
buf:rect(x, y, w, h [, cel]) or ok = buf:rect(x, y, w, h [, ch [, fg [, bg]]])
- draw a rectangle in the buffer.
buf:copyregion(tx, ty, x, y, w, h)
- copy a region of the buffer.
buf:scrollregion(x, y, w, h [, sx [, sy]])
- scrolls a region of the buffer.
buf:printat(x, y, what [, w])
- print a string to the buffer.
w = buf:width()
- return the buffer width.
h = buf:height()
- return the buffer height.
w, h = buf:size()
- return the buffer width and height.
Colors and attributes
How TermFX handles colors depends on the chosen output mode. There are a
few functions to help you with the colors, these are
termfx.colorinfo(), see the function list
above for an explanation of those functions. They work based on the
standard xterm colormap. If, for some reason, you have a different
colormap, these functions will not give you the results you want.
There are 8 predefined colors, which are available in all output modes:
The output modes are:
- this is an 8 color mode, in which only the 8 predefined colors are available as colors 1-8, plus a default color at position 0.
- this is a mode with 256 colors.
- the first 8 colors (0-7) are the 8 predefined colors.
- the next 8 colors (8-15) are the first 8 colors +
- then (16-231) follow 216 different colors
- finally (232-255) 24 shades of grey
- this is a mode that supports only the 3rd range of
COL256, i.e. 216 different colors, but ranging from 0-215. The 8 default colors are mapped to their closest representations in the palette.
- this is a mode in which only the 4th range from
COL256is available, i.e. 24 shades of grey. but ranging from 0-23. The 8 default colors are mapped to grey values.
A table of the colors available for xterms is available here:
The behaviour of the predefined colors is different from termbox, where
they only make sense for the
NORMAL output mode. But this means that
they need to be determined for each output mode, which means that when
you for some reason cache the values from
termfx.color or use them in
a buffer or a cell, and then switch output modes, you will need to
re-cache them, as they will have changed.
In addition to colors, cells can also have formatting attributes:
In order to construct a cell attribute, you can start with a color and just add the desired formatting options to it (but only once, because, of course, they are actually meant to be or'ed to the color)
termfx.pollevent() returns with a type field of "key" and its key
field set, then the value in the key field may be checked against one of
the constants in
termfx.key, as listed below. However, due to window
managers interfering and whatnot, not all of these special keys may be
available to your terminal.
If mouse support is available, you also have these constants:
TermFX works with utf8 or plain 8 bit encoded charsets. Apart from the encoding, TermFX makes no assumptions about the characters in a string. Of course, in order to use utf8, the terminal has to support unicode. If you need to manipulate utf8 encoded strings, use either lua 5.3 or a proper utf8 library like luautf8.
A very simple program using this library might look like this:
tfx = require "termfx" tfx.init() tfx.clear(tfx.color.WHITE, tfx.color.BLACK) s = "Hello World" x = math.floor((tfx.width() - #s) / 2) y = math.floor(tfx.height() / 2) for i=1, #s do tfx.setcell(x, y, string.sub(s, i, i)) x = x + 1 end tfx.present() quit = false repeat evt = tfx.pollevent() quit = evt.char == "q" until quit tfx.shutdown()
You can find more examples in the samples directory.
This is built using termbox, so when in doubt, a look at its documentation may help. Said documentation is contained within the file termbox.h, which can be found here