readkey.lua - simple terminal control, like CPAN's Term::ReadKey


 local RK = require 'readkey'

 -- reading from stdin ...
 RK.ReadMode( 4 )  -- Turn off controls keys
 local key
 while true do
      key = RK.ReadKey( -1 )
      if key then break end
      do_something_else_meanwhile()  -- no key yet...
 print("You pressed key: "..key)
 RK.ReadMode( 0 ) -- Reset tty mode before exiting

 -- OR: reading from /dev/tty ...
 tty ='/dev/tty', 'r')
 RK.ReadMode( 4, tty )  -- Turn off controls keys
 while true do
      key = RK.ReadKey( -1, tty )
      if key then break end
      do_something_else_meanwhile()  -- no key yet...
 print("You pressed key: "..key)
 RK.ReadMode( 0, tty ) -- Reset tty mode before exiting


This Lua module is dedicated to providing simple control over terminal modes (cbreak, raw, cooked, etc.), and support for non-blocking reads, and some handy functions for working with terminals.

This module started as a re-expression in Lua of the Term::ReadKey Perl module by Kenneth Albanowski and Jonathan Stowe. The calling interface is similar, except that ReadLine() has quite different functionality, and SetTerminalSize() and GetSpeeds() are not implemented.


ReadMode( mode [, filehandle] )

Takes an integer argument, which can be one of the following values:

  0  Restore original settings
  1  Change to cooked mode
  2  Change to cooked mode with echo off  (Good for passwords)
  3  Change to cbreak mode
  4  Change to raw mode
  5  Change to ultra-raw mode  (LF to CR/LF translation turned off)

Or, you may use the synonyms:

  'restore', 'normal', 'noecho', 'cbreak', 'raw', 'ultra-raw'

These functions are automatically applied to io.stdin if no other filehandle is supplied. Mode 0 not only restores original settings, but it will cause the next ReadMode call to save a new set of default settings. Mode 5 is similar to mode 4, except no CR/LF translation is performed, and, if possible, parity will be disabled.

If you are executing another program that may be changing the terminal mode, you will either want to say

    local P = require 'posix'
    local tty =, 'a+')
    RK.ReadMode(1, tty)
    RK.ReadMode(1, tty)

which resets the settings after the program has run, or:

    somemode = 1

which records any changes the program may have made, before resetting the mode.

ReadKey( mode [, filehandle] )

Takes an integer argument, which can be one of the following values:

     0   Perform a normal read using getc
    -1   Perform a non-blocked read
    >0   Perform a timed read

If mode is zero, ReadKey() will wait for input, like a normal getc (since version 1.2, if the filehandle has been set to raw or ultra-raw, the underlying read() call is restarted after any interrupt).
If mode is less than zero, ReadKey will return nil immediately unless a character is waiting in the buffer.
If mode is greater than zero, then ReadKey will use it as a timeout value in seconds (fractional seconds are allowed), and won't return nil until that time expires.

If the filehandle is not supplied, it will default to STDIN.

ReadLine( prompt, histfile )

The syntax of this call is nothing like that of its Perl equivalent. It invokes the GNU Readline and History libraries, which display the prompt, allow Tab-Filename-Completion, and optionally save the line entered onto the end of the histfile, whose contents are available by the Up-Arrow. For example:

 local str = RK.ReadLine('Delete which file ? ', '~/.filemanager_history')

If the histfile parameter is nil, the Up and Down Arrows will not work, and no history-file will be created.

GetTerminalSize( [filehandle] )

Returns four numbers: the width and height of the terminal in characters, and the width and height in pixels.

Since version 1.2, xwininfo -id $WINDOWID is used if possible, since it returns up-to-date information even after ReadLine() has been invoked.

If xwininfo is not available, various other means are tried, which all fail to respond to size-changes that occur after ReadLine() has been invoked; also the pixel sizes are returned as zero (they would need the ioctl command with the non-POSIX TIOCGWINSZ parameter).

GetControlChars( [filehandle] )

Returns a table containing key/value pairs. Each key is the name of the control-character/signal, and its value is that character, as a single character, except for MIN and TIME, which are integers.

Each key will be an entry from the following list:


The keys SWITCH and STATUS, which are supported by the Term::ReadKey CPAN module, are not present here because they are not specified by POSIX.

Thus, the following will give you the current interrupt character:

    local keys = RK.GetControlChars()
    interrupt_char = keys['INTERRUPT']
SetControlChars( new_ccs [, filehandle] )

Takes a table containing key/value pairs. Each key should be the name of a legal control-character or signal, and its value should be either a single character, or a number in the range 0-255. SetControlChars may die with a runtime error if an invalid character name is passed or there is an error changing the settings. The list of valid names is the keys of GetControlChars()


This module is available as a LuaRock in so you should be able to install it with the command:
  $ su
  # luarocks install readkey
  # luarocks install

If you don't have them already, you will also need the luaposix and readline modules:
  # luarocks install luaposix
  # luarocks install readline

If you are using Lua 5.2 or earlier, you will also need the LuaBitOp module:
  # luarocks install LuaBitOp

You can see the source-code in:

The Perl module is available from CPAN at


 20211119 1.8 include lua5.4
 20200628 1.7 suppress the xwininfo errmsg if su'd to a non-root user
 20191125 1.6 tests now work on freebsd (stty -a output different!)
 20170919 1.5 protect against missing ECHOCTL and ECHOKE
 20150421 1.4 include lua5.3, and move pod and doc back to
 20140608 1.3 switch pod and doc over to using moonrocks
 20131022 1.2 xwininfo lets GetTerminalSize work even after ReadLine
 20131021     K.ReadKey() restarts the after EINTR interrupt
 20131005 1.1 GetTerminalSize returns numbers, ReadMode no longer sets ISTRIP
 20130922 1.0 first working version


This Lua module is by Peter Billam, see