Dernière activité 1 month ago

crummy.cr Brut Playground
1require "./event"
2
3module 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
57end
58
59module 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
165end
166
167at_exit {
168 STDIN.cooked!
169 Crummy.deinit_cursor
170 Crummy.clear
171}
172
173STDIN.read_timeout = 0.milliseconds
174Crummy.init_cursor
175
176Signal::INT.trap do
177 exit
178end
179
180pos = {x: 1, y: 1}
181
182def draw(pos, offset, str)
183 x, y = pos[:x] + offset[:x], pos[:y] + offset[:y]
184
185 "\e[#{y};#{x}H#{str}"
186end
187
188loop 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
206end
207