Module: Tuile::Keys

Defined in:
lib/tuile/keys.rb

Overview

Constants for keys returned by Keys.getkey and helpers for reading them from stdin. The constants are the raw escape sequences emitted by the terminal; see en.wikipedia.org/wiki/ANSI_escape_code for the encoding.

Constant Summary collapse

DOWN_ARROW =

Returns:

  • (String)
"\e[B"
UP_ARROW =

Returns:

  • (String)
"\e[A"
DOWN_ARROWS =

Returns:

  • (Array<String>)
[DOWN_ARROW, "j"].freeze
UP_ARROWS =

Returns:

  • (Array<String>)
[UP_ARROW, "k"].freeze
LEFT_ARROW =

Returns:

  • (String)
"\e[D"
RIGHT_ARROW =

Returns:

  • (String)
"\e[C"
CTRL_LEFT_ARROW =

Returns:

  • (String)
"\e[1;5D"
CTRL_RIGHT_ARROW =

Returns:

  • (String)
"\e[1;5C"
ESC =

Returns:

  • (String)
"\e"
HOME =

Returns:

  • (String)
"\e[H"
END_ =

Returns:

  • (String)
"\e[F"
HOMES =

Home-key sequences. xterm-style (‘HOME`) is the modern default, but the Linux console, rxvt, and tmux/screen in their default configuration emit the VT220-style `e[1~` instead. Components that handle Home should match against this array so users see consistent behavior regardless of which sequence their terminal emits.

Returns:

  • (Array<String>)
[HOME, "\e[1~"].freeze
ENDS_ =

End-key sequences. See HOMES for why two are recognized.

Returns:

  • (Array<String>)
[END_, "\e[4~"].freeze
PAGE_UP =

Returns:

  • (String)
"\e[5~"
PAGE_DOWN =

Returns:

  • (String)
"\e[6~"
BACKSPACE =

Returns:

  • (String)
"\x7f"
DELETE =

Returns:

  • (String)
"\e[3~"
CTRL_A =

Ctrl+letter sends bytes 0x01..0x1a. Note that CTRL_H == ‘“b”`, CTRL_I == TAB, CTRL_J == `“n”`, and CTRL_M == ENTER —terminals deliver these key combinations indistinguishably from the corresponding named keys.

Returns:

  • (String)
"\x01"
CTRL_B =

Returns:

  • (String)
"\x02"
CTRL_C =

Returns:

  • (String)
"\x03"
CTRL_D =

Returns:

  • (String)
"\x04"
CTRL_E =

Returns:

  • (String)
"\x05"
CTRL_F =

Returns:

  • (String)
"\x06"
CTRL_G =

Returns:

  • (String)
"\x07"
CTRL_H =

Returns:

  • (String)
"\b"
CTRL_I =

Returns:

  • (String)
"\t"
CTRL_J =

Returns:

  • (String)
"\n"
CTRL_K =

Returns:

  • (String)
"\x0b"
CTRL_L =

Returns:

  • (String)
"\x0c"
CTRL_M =

Returns:

  • (String)
"\r"
CTRL_N =

Returns:

  • (String)
"\x0e"
CTRL_O =

Returns:

  • (String)
"\x0f"
CTRL_P =

Returns:

  • (String)
"\x10"
CTRL_Q =

Returns:

  • (String)
"\x11"
CTRL_R =

Returns:

  • (String)
"\x12"
CTRL_S =

Returns:

  • (String)
"\x13"
CTRL_T =

Returns:

  • (String)
"\x14"
CTRL_U =

Returns:

  • (String)
"\x15"
CTRL_V =

Returns:

  • (String)
"\x16"
CTRL_W =

Returns:

  • (String)
"\x17"
CTRL_X =

Returns:

  • (String)
"\x18"
CTRL_Y =

Returns:

  • (String)
"\x19"
CTRL_Z =

Returns:

  • (String)
"\x1a"
BACKSPACES =

Returns:

  • (Array<String>)
[BACKSPACE, CTRL_H].freeze
ENTER =

Returns:

  • (String)
"\r"
TAB =

Returns:

  • (String)
"\t"
SHIFT_TAB =

The terminal sequence emitted by Shift+Tab in xterm-style terminals (CSI Z). Used by Screen for reverse focus traversal.

Returns:

  • (String)
"\e[Z"

Class Method Summary collapse

Class Method Details

.getkeyString

Grabs a key from stdin and returns it. Blocks until the key is obtained. Reads a full ESC key sequence; see constants above for some values returned by this function.

Returns:



137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
# File 'lib/tuile/keys.rb', line 137

def self.getkey
  char = $stdin.getch
  return char unless char == Keys::ESC

  # Escape sequence. Try to read more data.
  begin
    # Read up to 5 bytes: that's the maximum tail length of any escape
    # sequence Tuile recognizes after the initial \e (X10 mouse `[Mbxy`,
    # CTRL+arrow `[1;5D`, etc.). Reading 6 here would over-read into the
    # next sequence on tight mouse-event bursts — we'd silently steal
    # the next event's leading \e and the rest of it would surface as
    # individual printable keypresses in focused inputs.
    char += $stdin.read_nonblock(5)
  rescue IO::EAGAINWaitReadable
    # The "ESC" key pressed => only the \e char is emitted.
    return char
  end

  # If `read_nonblock` returned a partial X10 mouse-report prefix (the
  # sequence is fixed-length: 3 bytes after `\e[M`), drain the remainder
  # with a blocking read so the parser downstream sees a complete event
  # instead of leaking tail bytes as keypresses.
  if char.start_with?("\e[M") && char.bytesize < 6
    char += $stdin.read(6 - char.bytesize)
  end

  char
end

.printable?(key) ⇒ Boolean

True iff ‘key` is a single printable character — a one-character string whose codepoint is not in Unicode’s C (Other) category. Rejects multi- character escape sequences (UP_ARROW, mouse events, …), control bytes (TAB, ENTER, ESC, CTRL_A..CTRL_Z, BACKSPACE), and the empty string; accepts ASCII letters/digits/punctuation/space and non-ASCII printables like “é”.

Used by Screen#register_global_shortcut to reject keys that would collide with typing, and by Component::TextField to decide whether to insert a key at the caret.

Parameters:

  • key (String)

Returns:

  • (Boolean)


129
130
131
# File 'lib/tuile/keys.rb', line 129

def self.printable?(key)
  key.length == 1 && !key.match?(/\p{C}/)
end