readKey method
Reads a single key from the input, including a variety of control characters.
Keys are represented by the Key class. Keys may be printable (if so,
Key.isControl
is false
, and the Key.char
property may be used to
identify the key pressed. Non-printable keys have Key.isControl
set
to true
, and if so the Key.char
property is empty and instead the
Key.controlChar
property will be set to a value from the
ControlCharacter enumeration that describes which key was pressed.
Owing to the limitations of terminal key handling, certain keys may
be represented by multiple control key sequences. An example showing
basic key handling can be found in the example/command_line.dart
file in the package source code.
Implementation
Key readKey() {
Key key;
int charCode;
var codeUnit = 0;
rawMode = true;
while (codeUnit <= 0) {
codeUnit = stdin.readByteSync();
}
if (codeUnit >= 0x01 && codeUnit <= 0x1a) {
// Ctrl+A thru Ctrl+Z are mapped to the 1st-26th entries in the
// enum, so it's easy to convert them across
key = Key.control(ControlCharacter.values[codeUnit]);
} else if (codeUnit == 0x1b) {
// escape sequence (e.g. \x1b[A for up arrow)
key = Key.control(ControlCharacter.escape);
final escapeSequence = <String>[];
charCode = stdin.readByteSync();
if (charCode == -1) {
rawMode = false;
return key;
}
escapeSequence.add(String.fromCharCode(charCode));
if (charCode == 127) {
key = Key.control(ControlCharacter.wordBackspace);
} else if (escapeSequence[0] == '[') {
charCode = stdin.readByteSync();
if (charCode == -1) {
rawMode = false;
return key;
}
escapeSequence.add(String.fromCharCode(charCode));
switch (escapeSequence[1]) {
case 'A':
key.controlChar = ControlCharacter.arrowUp;
break;
case 'B':
key.controlChar = ControlCharacter.arrowDown;
break;
case 'C':
key.controlChar = ControlCharacter.arrowRight;
break;
case 'D':
key.controlChar = ControlCharacter.arrowLeft;
break;
case 'H':
key.controlChar = ControlCharacter.home;
break;
case 'F':
key.controlChar = ControlCharacter.end;
break;
default:
if (escapeSequence[1].codeUnits[0] > '0'.codeUnits[0] &&
escapeSequence[1].codeUnits[0] < '9'.codeUnits[0]) {
charCode = stdin.readByteSync();
if (charCode == -1) {
rawMode = false;
return key;
}
escapeSequence.add(String.fromCharCode(charCode));
if (escapeSequence[2] != '~') {
key.controlChar = ControlCharacter.unknown;
} else {
switch (escapeSequence[1]) {
case '1':
key.controlChar = ControlCharacter.home;
break;
case '3':
key.controlChar = ControlCharacter.delete;
break;
case '4':
key.controlChar = ControlCharacter.end;
break;
case '5':
key.controlChar = ControlCharacter.pageUp;
break;
case '6':
key.controlChar = ControlCharacter.pageDown;
break;
case '7':
key.controlChar = ControlCharacter.home;
break;
case '8':
key.controlChar = ControlCharacter.end;
break;
default:
key.controlChar = ControlCharacter.unknown;
}
}
} else {
key.controlChar = ControlCharacter.unknown;
}
}
} else if (escapeSequence[0] == 'O') {
charCode = stdin.readByteSync();
if (charCode == -1) {
rawMode = false;
return key;
}
escapeSequence.add(String.fromCharCode(charCode));
assert(escapeSequence.length == 2);
switch (escapeSequence[1]) {
case 'H':
key.controlChar = ControlCharacter.home;
break;
case 'F':
key.controlChar = ControlCharacter.end;
break;
case 'P':
key.controlChar = ControlCharacter.F1;
break;
case 'Q':
key.controlChar = ControlCharacter.F2;
break;
case 'R':
key.controlChar = ControlCharacter.F3;
break;
case 'S':
key.controlChar = ControlCharacter.F4;
break;
default:
}
} else if (escapeSequence[0] == 'b') {
key.controlChar = ControlCharacter.wordLeft;
} else if (escapeSequence[0] == 'f') {
key.controlChar = ControlCharacter.wordRight;
} else {
key.controlChar = ControlCharacter.unknown;
}
} else if (codeUnit == 0x7f) {
key = Key.control(ControlCharacter.backspace);
} else if (codeUnit == 0x00 || (codeUnit >= 0x1c && codeUnit <= 0x1f)) {
key = Key.control(ControlCharacter.unknown);
} else {
// assume other characters are printable
key = Key.printable(String.fromCharCode(codeUnit));
}
rawMode = false;
return key;
}