1.1.6 • Published 8 years ago

sirrobert-keypress v1.1.6

Weekly downloads
3
License
MIT
Repository
bitbucket
Last release
8 years ago

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
abcdefghijk
lmnopqrstuv
wxyzABCDEFG
HIJKLMNOPQR
STUVWXYZ
Numeric
1234567890
Punctuation
` | ~ | ! | @ | # | $ | % | ^ | & | * | (
)_+;:'",./<
>?{}[]\ |
Whitespace
spacetabenter
Movement
arrow uparrow downarrow leftarrow right
Screen Control
insertdeletehomeendpage uppage downescape
Function Keys
F1F2F3F4F5F6F7F8F8F10F11F12
Control Sequences
CTRL-CCTRL-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.