Skip to content

Non blocking readKey #5

@aloisdeniel

Description

@aloisdeniel

The synchronous method readKey is limiting since it blocks any update to the console that might be wanted before the next key is pressed.

For example you might want to refresh a loader character and wait for an input key at the same time.

To solve this, the stdin stream should be listen instead to manage the received bytes. Something like the following should work :

extension ConsoleExtensions on Console {
  Stream<Key> readKeys() async* {
    rawMode = true;
    List<int> escapeSequence = <int>[];
    void resetEscape() => escapeSequence = <int>[];
    await for (var codeUnit in stdin.expand((x) => x)) {
      if (escapeSequence.isNotEmpty) {
        escapeSequence.add(codeUnit);
        switch (escapeSequence) {
          case [0x1b, -1]:
            yield Key.control(ControlCharacter.escape);
            resetEscape();
          case [0x1b, 127]:
            yield Key.control(ControlCharacter.wordBackspace);
            resetEscape();
          case [0x1b, 91, 65]: // [ A
            yield Key.control(ControlCharacter.arrowUp);
            resetEscape();
          case [0x1b, 91, 66]: // [ B
            yield Key.control(ControlCharacter.arrowDown);
            resetEscape();
          case [0x1b, 91, 67]: // [ C
            yield Key.control(ControlCharacter.arrowRight);
            resetEscape();
          case [0x1b, 91, 68]: // [ D
            yield Key.control(ControlCharacter.arrowLeft);
            resetEscape();
          case [0x1b, 91, 72]: // [ H
            yield Key.control(ControlCharacter.home);
            resetEscape();
          case [0x1b, 91, 70]: // [ F
            yield Key.control(ControlCharacter.end);
            resetEscape();
          // TODO more scaped sequences
        }
      } else if (codeUnit == 0x1b) {
        escapeSequence = <int>[codeUnit];
      } else 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
        yield Key.control(ControlCharacter.values[codeUnit]);
      } else if (codeUnit == 0x7f) {
        yield Key.control(ControlCharacter.backspace);
      } else if (codeUnit == 0x00 || (codeUnit >= 0x1c && codeUnit <= 0x1f)) {
        yield Key.control(ControlCharacter.unknown);
      } else {
        // assume other characters are printable
        yield Key.printable(String.fromCharCode(codeUnit));
      }
    }
  }
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requesthelp wantedExtra attention is needed

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions