/
console.cr
114 lines (103 loc) · 3.64 KB
/
console.cr
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
require "termios"
class IO::FileDescriptor
# Turn off character echoing for the duration of the given block.
# This will prevent displaying back to the user what they enter on the terminal.
# Only call this when this IO is a TTY, such as a not redirected stdin.
#
# ```
# print "Enter password: "
# password = STDIN.noecho &.gets.try &.chomp
# puts
# ```
def noecho
preserving_tc_mode("can't set IO#noecho") do |mode|
noecho_from_tc_mode!
yield self
end
end
# Turn off character echoing for this IO.
# This will prevent displaying back to the user what they enter on the terminal.
# Only call this when this IO is a TTY, such as a not redirected stdin.
def noecho!
if LibC.tcgetattr(fd, out mode) != 0
raise Errno.new "can't set IO#noecho!"
end
noecho_from_tc_mode!
end
macro noecho_from_tc_mode!
mode.c_lflag &= ~(Termios::LocalMode.flags(ECHO, ECHOE, ECHOK, ECHONL).value)
LibC.tcsetattr(fd, Termios::LineControl::TCSANOW, pointerof(mode))
end
# Enable character processing for the duration of the given block.
# The so called cooked mode is the standard behavior of a terminal,
# doing line wise editing by the terminal and only sending the input to
# the program on a newline.
# Only call this when this IO is a TTY, such as a not redirected stdin.
def cooked
preserving_tc_mode("can't set IO#cooked") do |mode|
cooked_from_tc_mode!
yield self
end
end
# Enable character processing for this IO.
# The so called cooked mode is the standard behavior of a terminal,
# doing line wise editing by the terminal and only sending the input to
# the program on a newline.
# Only call this when this IO is a TTY, such as a not redirected stdin.
def cooked!
if LibC.tcgetattr(fd, out mode) != 0
raise Errno.new "can't set IO#cooked!"
end
cooked_from_tc_mode!
end
macro cooked_from_tc_mode!
mode.c_iflag |= (Termios::InputMode::BRKINT |
Termios::InputMode::ISTRIP |
Termios::InputMode::ICRNL |
Termios::InputMode::IXON).value
mode.c_oflag |= Termios::OutputMode::OPOST.value
mode.c_lflag |= (Termios::LocalMode::ECHO |
Termios::LocalMode::ECHOE |
Termios::LocalMode::ECHOK |
Termios::LocalMode::ECHONL |
Termios::LocalMode::ICANON |
Termios::LocalMode::ISIG |
Termios::LocalMode::IEXTEN).value
LibC.tcsetattr(fd, Termios::LineControl::TCSANOW, pointerof(mode))
end
# Enable raw mode for the duration of the given block.
# In raw mode every keypress is directly sent to the program, no interpretation
# is done by the terminal.
# Only call this when this IO is a TTY, such as a not redirected stdin.
def raw
preserving_tc_mode("can't set IO#raw") do |mode|
raw_from_tc_mode!
yield self
end
end
# Enable raw mode for this IO.
# In raw mode every keypress is directly sent to the program, no interpretation
# is done by the terminal.
# Only call this when this IO is a TTY, such as a not redirected stdin.
def raw!
if LibC.tcgetattr(fd, out mode) != 0
raise Errno.new "can't set IO#raw!"
end
raw_from_tc_mode!
end
macro raw_from_tc_mode!
LibC.cfmakeraw(pointerof(mode))
LibC.tcsetattr(fd, Termios::LineControl::TCSANOW, pointerof(mode))
end
private def preserving_tc_mode(msg)
if LibC.tcgetattr(fd, out mode) != 0
raise Errno.new msg
end
before = mode
begin
yield mode
ensure
LibC.tcsetattr(fd, Termios::LineControl::TCSANOW, pointerof(before))
end
end
end