Last active 1 month ago

capitalex's Avatar capitalex revised this gist 1 month ago. Go to revision

1 file changed, 206 insertions

crummy.cr(file created)

@@ -0,0 +1,206 @@
1 + require "./event"
2 +
3 + module Crummy
4 + # Enum holding various ASCII control codes. Not all code are present in this
5 + # enum.
6 + enum ControlChar
7 + Null
8 + StartHeading
9 + StartText
10 + EndText
11 + Enquiry
12 + Acknowledgement
13 + Bell
14 + Backspace
15 + HorizontalTab
16 + LineFeed
17 + VerticalTab
18 + FormFeed
19 + CarriageReturn
20 + ShiftOut
21 + ShiftIn
22 + Cancel
23 + Substitute
24 + Escape
25 + Unknown
26 +
27 + end
28 +
29 + # Enum for the various CSI operations.
30 + enum ControlSequence
31 + CursorUp
32 + CursorDown
33 + CursorForwards
34 + CursorBackwards
35 + CursorNextLine
36 + CursorPreviousLine
37 + CursorHorizontalAbsolute
38 + CursorPosition
39 + EraseDisplay
40 + EraseLine
41 + InsertLines
42 + DeleteLines
43 + DeleteChars
44 + EraseChars
45 + ScrollUp
46 + ScrollDown
47 + HorizontalVerticalPosition
48 + SelectGraphicRendition
49 + MouseEvent
50 + KeyCode
51 + F1
52 + F2
53 + F3
54 + F4
55 + Unknown
56 + end
57 + end
58 +
59 + module Crummy
60 + # Sends a control sequence to clear the screen to the console
61 + def self.clear
62 + print "\e[1;1H\e[2J"
63 + end
64 +
65 + # Sends a control sequence to enable full mouse tracking to the console
66 + def self.init_cursor
67 + print "\e[?1003h\e[?1015h\e[?1006h"
68 + end
69 +
70 + def self.deinit_cursor
71 + print "\e[?1003l\e[?1015l\e[?1006l"
72 + end
73 +
74 + # Reads a character in raw mode. If the read times out, then nil is returned
75 + #
76 + # Useage:
77 + # ```
78 + # buffer = [] of Char
79 + # while (byte = read_char)
80 + # buffer << byte
81 + # end
82 + # ```
83 + def self.read_char
84 + STDIN.raw { |io|
85 + begin
86 + io.read_char
87 + rescue
88 + nil
89 + end
90 + }
91 + end
92 +
93 + def self.parse_mouse
94 +
95 +
96 + KeyEvent.new(SpecialKey::Unknown, Modifier::None)
97 + end
98 +
99 + def self.parse_csi
100 + str = String.build do |str|
101 + while (char = read_char)
102 + str << char
103 +
104 + next if char.number?
105 + next if char == ';'
106 + next if char == '<'
107 + break
108 + end
109 + end
110 +
111 + if str[0]? == '<'&& (res = /<(\d+);(\d+);(\d+)(m|M)/.match str)
112 + pressed = $4 == "M"
113 + button = $1
114 + x = $2.to_i32
115 + y = $3.to_i32
116 + MouseEvent.new(pressed, MouseButton.from_s(button), {x: x, y: y})
117 + else
118 + KeyEvent.new(SpecialKey::Unknown, Modifier::None)
119 + end
120 + end
121 +
122 + def self.parse_ss3
123 + KeyEvent.new(SpecialKey::Unknown, Modifier::None)
124 + end
125 +
126 +
127 + def self.parse_code
128 + KeyEvent.new(SpecialKey::Unknown, Modifier::None)
129 + end
130 + # Reads input from the console. It converts all escape sequences into
131 + # `String`s.
132 + def self.read_raw_input
133 + raw_input = [] of Event
134 + while (char = read_char)
135 + if char == '\e'
136 + raw_input << case read_char
137 + when nil
138 + KeyEvent.new(SpecialKey::Escape, Modifier::None)
139 + when '['
140 + parse_csi
141 + when 'O'
142 + parse_ss3
143 + when .number?
144 + parse_code
145 + else
146 + KeyEvent.new(SpecialKey::Unknown, Modifier::None)
147 + end
148 + else
149 + raw_input << KeyEvent.new(char, Modifier::None)
150 + end
151 + end
152 +
153 + raw_input
154 + end
155 +
156 + def self.process_input(raw_input) : Array(Event)
157 + [] of Event
158 + end
159 +
160 + # Reads a series of inputs from the command line
161 + def self.listen
162 + raw_input = read_raw_input
163 +
164 + end
165 + end
166 +
167 + at_exit {
168 + STDIN.cooked!
169 + Crummy.deinit_cursor
170 + Crummy.clear
171 + }
172 +
173 + STDIN.read_timeout = 0.milliseconds
174 + Crummy.init_cursor
175 +
176 + Signal::INT.trap do
177 + exit
178 + end
179 +
180 + pos = {x: 1, y: 1}
181 +
182 + def draw(pos, offset, str)
183 + x, y = pos[:x] + offset[:x], pos[:y] + offset[:y]
184 +
185 + "\e[#{y};#{x}H#{str}"
186 + end
187 +
188 + loop do
189 + input = Crummy.read_raw_input
190 +
191 + input.each do |event|
192 + if event.is_a? Crummy::MouseEvent
193 + pos = event.location
194 + end
195 + end
196 +
197 + Crummy.clear
198 +
199 + print <<-RENDER
200 + #{draw(pos, {x: -2, y: -1}, "⬛😃⬛")}
201 + #{draw(pos, {x: -2, y: 0}, "😃⬛😃")}
202 + #{draw(pos, {x: -2, y: 1}, "⬛😃⬛")}
203 + RENDER
204 + print "\e[1;1H"
205 + sleep 32.milliseconds
206 + end
Newer Older