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
165
166
167
168
169
170
171
172
173
174
# 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

  # Private-mode CSI reports (`\e[?` params… final byte in 0x40..0x7E)
  # can outgrow the 5-byte gulp above — the mode-2031 color-scheme
  # notification `\e[?997;1n` (see {EventQueue::ColorSchemeEvent}) is 8
  # bytes after the `\e`. Drain to the final byte with blocking 1-byte
  # reads so the tail doesn't surface as phantom keypresses. Keyboard
  # sequences never start with `\e[?`, so this can't eat a regular key.
  if char.start_with?("\e[?")
    char += $stdin.read(1) until char.match?(/[\x40-\x7e]\z/)
  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