Installation
Install locally
npm install --save sirrobert-keypress
Install globally
npm install --global sirrobert-keypress
Overview
This module provides keystroke-based input listeners. It allows terminal-based software to listen to stdin and react on a keystroke-by-keystroke basis.
The Basics
The module provides an object-oriented interface for listening (and responding) to keyboard input.
If you start listening with no hooks enabled, the process is effectively
completely mute. It will not even be able to respond to interrupt codes
like CTRL-C
and CTRL-D
.
Built-In Listeners
That's completely fine if you want to roll your own, but it can be discombobulating if you're not expecting it. The module provides some small built-in bundles of listeners for getting started.
.addListener(name)
.addListeners([name1, name2, ...])
The .addListener()
functions simply take the names of the provided preset
functionality. .addListeners()
is just a small wrapper that loops through
the values provided and sets them using .addListener()
.
var Keypress = require("sirrobert-keypress");
var keypress = new Keypress();
keypress.listen(true);
keypress.addListeners(["interrupts", "echo"]);
This example provides a very simple echo server. You type in characters and
they are displayed on the screen. Pressing Enter
provides a newline.
CTRL-C
and CTRL-D
quit the process.
Echo
The echo
listener adds listeners for most characters (excluding escape
,
arrow right
, arrow left
, arrow up
, arrow down
, backspace
, tab
,
CTRL-C
, and CTRL-D
). Pressing any of the excluded keys will have no
effect. Other characters will be echoed to stdout.
Interrupts
The interrupts
listener listens for CTRL-C
and CTRL-D
. Either of
these will cause the process to exit immediately (using process.exit()
).
Adding Custom Listeners
In addition to the (very simple) built-in listeners, you can also define your own. These are managed according to the following methods.
.listen(doListen, transform)
The .listen()
method is responsible for monitoring or not monitoring
stdin. You can control this manually, but this method provides a convenient
way to do it on your own.
doListen [Boolean]
Setting doListen
to true
causes the process to start listening to stdin
in raw mode.
process.stdin.setRawMode(true);
process.stdin.on("data", keypressHandler);
Setting it to false
turns off raw mode and stops listening.
process.stdin.setRawMode(false);
process.stdin.removeListener('data', keypressHandler);
transform [Function]
The transform()
function allows you to do something with the "chunk" of data
that comes in from a keypress. Here's an example, followed by an
explanation.
// You have some object that does stuff in your program.
myThing = new Thing();
// Now start listening to the input stream. Each time a chunk of data
// comes in, set `myThing.chunk` to be equal to that chunk of data.
keypress.listen(true, chunk => {
myThing.chunk = chunk;
return myThing;
});
// Now set a listener
keypress.on("arrow up", thing => { console.log(thing.chunk) });
In this example, each time the user presses the up arrow, it is sent as a
"chunk" of data. When the event is fired, the transform()
function
transforms that chunk however you tell it to. The return value of
transform()
is passed into your event handler as its sole argument. That
allows you to do things like inspection of the keystrokes as they come in,
or basically smuggling any other data into your keypress event handler.
without needing that data to have originated in an enclosure.
.on(character, handler)
Sets a new listener, listening for a specific character (or list of
characters). The second argument, handler
, is a function to be called
when the event fires.
function do_quit () { process.exit() }
keypress.on("q", do_quit);
With this listener, whenever the character "q" is pressed, the process will exit. The same could also be set at the same time on more than one key press:
function do_quit () { process.exit() }
keypress.on(["q", "x", "c"], do_quit);
In this case, pressing "q" for "quit", "x" for "exit", or "c" for "close" causes the process to exit.
In these simple example, the event handler did not take any arguments. In
fact, information is passed to the function by the .fire()
method. See
that for more information.
Multiple handlers can be set for the same character. The order in which they are set is the order in which they will fire.
.onNot(character, handler)
This is a negation of the .on()
method. An .onNot()
listener
fires on any keypress that is not the specified character or characters.
Multiple handlers can be set at once in the same manner as the .on()
method.
function do_continue() { console.log("continuing..." }
keypress.onNot("escape", do_continue);
keypress.onNot(["CTRL-C", "CTRL-D"], do_continue);
keypress.on(["escape", "CTRL-C", "CTRL-D"], () => { process.exit() });
Now when a user presses anything except escape
, CTRL-C
, or CTRL-D
,
the program prints
continuing...
If one of those three keys (or in the case of the CTRL
characters, key
combinations), the process quits.
Multiple .onNot()
handlers can be set for the same character. The order
in which they are set is the order in which they will fire.
.fire(charname, data)
When a character with a listener is pressed, the corresponding event handler
is fired using the .fire()
method. This method takes two arguments,
charname
and data
.
data [Any]
The data
argument can be anything you want to pass into the event handler
function.
charname [String]
The charname
argument is a simple string representing a keypress chunk.
The tables below list all the available characters. To use them, simply
pass them in as strings (such as "a" or "@" or "space").
Alpha
a | b | c | d | e | f | g | h | i | j | k |
l | m | n | o | p | q | r | s | t | u | v |
w | x | y | z | A | B | C | D | E | F | G |
H | I | J | K | L | M | N | O | P | Q | R |
S | T | U | V | W | X | Y | Z |
Numeric
1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 0 |
Punctuation
` | ~ | ! | @ | # | $ | % | ^ | & | * | ( |
) | _ | + | ; | : | ' | " | , | . | / | < |
> | ? | { | } | [ | ] | \ | | |
Whitespace
space | tab | enter |
Movement
arrow up | arrow down | arrow left | arrow right |
Screen Control
insert | delete | home | end | page up | page down | escape |
Function Keys
F1 | F2 | F3 | F4 | F5 | F6 | F7 | F8 | F8 | F10 | F11 | F12 |
Control Sequences
CTRL-C | CTRL-D |
Miscellaneous
The maps also contain an extra code. It's not actually a keypress, it's a special character that can be written to stdout to clear the screen:
clear screen |
You can use it like this:
process.stdout.write(Keypress.charmap["clear screen"]);
Inspection & Extensibility
You can inspect the map of codes yourself by looking at Keypress.charmap
and Keypress.codemap
. These two lists contain the same data, but indexed
by either the name of the code (such as "a" or "escape") or the code
represented by the name (such as "61" or "1b", respectively). This is
possible because the keys and values are both unique lists.
console.log(JSON.stringify(Keypress.charmap, null, 2));
// [
// "A" : "41",
// "B" : "42",
// "C" : "43",
// ...
// ]
console.log(JSON.stringify(Keypress.codemap, null, 2));
// [
// "41" : "A",
// "42" : "B",
// "43" : "C",
// ...
// ]
You can also add your own sequences by simply adding to the maps. As soon as an entry is added, listeners can be attached to it. Note that adding a listener before a character has been added to the maps will throw an error that looks like this:
Cannot listen for undefined character name: deux
Here's an example of adding codes to the maps.
// Rename "entree" to "enter" (almost french for "enter")
Keypress.addChar("entree", "0d");
Note that this deletes any entry already existing for either "deux" or "32" and replaces them with the values provided. This does not allow for synonyms, only replacements.
As in the example above, this can be useful for things like localization to different languages.
Rationale
I needed a module to handle keypress inputs of various kinds. This originally came out of my sirrobert-shell module, in which I implemented a node-based shell for linux. But other uses emerged, such as non-text keystroke controls for things like games.