aboutsummaryrefslogtreecommitdiff
path: root/system
diff options
context:
space:
mode:
Diffstat (limited to 'system')
-rw-r--r--system/init.lua147
1 files changed, 147 insertions, 0 deletions
diff --git a/system/init.lua b/system/init.lua
index 93dd488..94c2f09 100644
--- a/system/init.lua
+++ b/system/init.lua
@@ -207,4 +207,151 @@ end
207 207
208 208
209 209
210do
211 local _readkey = sys.readkey
212 local interval = 0.1
213
214 --- Reads a single byte from the console, with a timeout.
215 -- This function uses `system.sleep` to wait in increments of 0.1 seconds until either a byte is
216 -- available or the timeout is reached.
217 -- It returns immediately if a byte is available or if `timeout` is less than or equal to `0`.
218 -- @tparam number timeout the timeout in seconds.
219 -- @treturn[1] integer the key code of the key that was received
220 -- @treturn[2] nil if no key was read
221 -- @treturn[2] string error message; `"timeout"` if the timeout was reached.
222 function sys.readkey(timeout)
223 if type(timeout) ~= "number" then
224 error("arg #1 to readkey, expected timeout in seconds, got " .. type(timeout), 2)
225 end
226
227 local key = _readkey()
228 while key == nil and timeout > 0 do
229 sys.sleep(interval)
230 timeout = timeout - interval
231 key = _readkey()
232 end
233
234 if key then
235 return key
236 end
237 return nil, "timeout"
238 end
239end
240
241
242
243do
244 local left_over_key
245 local sequence -- table to store the sequence in progress
246 local utf8_length -- length of utf8 sequence currently being processed
247
248 -- Reads a single key, if it is the start of ansi escape sequence then it reads
249 -- the full sequence.
250 -- This function uses `system.readkey`, and hence `system.sleep` to wait until either a key is
251 -- available or the timeout is reached.
252 -- It returns immediately if a key is available or if `timeout` is less than or equal to `0`.
253 -- In case of an ANSI sequence, it will return the full sequence as a string.
254 -- @tparam number timeout the timeout in seconds.
255 -- @treturn[1] string the character that was received, or a complete ANSI sequence
256 -- @treturn[1] string the type of input: `"char"` for a single key, `"ansi"` for an ANSI sequence
257 -- @treturn[2] nil in case of an error
258 -- @treturn[2] string error message; `"timeout"` if the timeout was reached.
259 -- @treturn[2] string partial result in case of an error while reading a sequence, the sequence so far.
260 function sys.readansi(timeout)
261 if type(timeout) ~= "number" then
262 error("arg #1 to readansi, expected timeout in seconds, got " .. type(timeout), 2)
263 end
264
265 local key
266
267 if not sequence then
268 -- no sequence in progress, read a key
269
270 if left_over_key then
271 -- we still have a cached key from the last call
272 key = left_over_key
273 left_over_key = nil
274 else
275 -- read a new key
276 local err
277 key, err = sys.readkey(timeout)
278 if key == nil then -- timeout or error
279 return nil, err
280 end
281 end
282
283 if key == 27 then
284 -- looks like an ansi escape sequence, immediately read next char
285 -- as an heuristic against manually typing escape sequences
286 local key2 = sys.readkey(0)
287 if key2 ~= 91 and key2 ~= 79 then -- we expect either "[" or "O" for an ANSI sequence
288 -- not the expected [ or O character, so we return the key as is
289 -- and store the extra key read for the next call
290 left_over_key = key2
291 return string.char(key), "char"
292 end
293
294 -- escape sequence detected
295 sequence = { key, key2 }
296 else
297 -- check UTF8 length
298 utf8_length = key < 128 and 1 or key < 224 and 2 or key < 240 and 3 or key < 248 and 4
299 if utf8_length == 1 then
300 -- single byte character
301 utf8_length = nil
302 return string.char(key), "char"
303 else
304 -- UTF8 sequence detected
305 sequence = { key }
306 end
307 end
308 end
309
310 local err
311 if utf8_length then
312 -- read remainder of UTF8 sequence
313 local timeout_end = sys.gettime() + timeout
314 while true do
315 key, err = sys.readkey(timeout_end - sys.gettime())
316 if err then
317 break
318 end
319 table.insert(sequence, key)
320
321 if #sequence == utf8_length then
322 -- end of sequence, return the full sequence
323 local result = string.char((unpack or table.unpack)(sequence))
324 sequence = nil
325 utf8_length = nil
326 return result, "char"
327 end
328 end
329
330 else
331 -- read remainder of ANSI sequence
332 local timeout_end = sys.gettime() + timeout
333 while true do
334 key, err = sys.readkey(timeout_end - sys.gettime())
335 if err then
336 break
337 end
338 table.insert(sequence, key)
339
340 if (key >= 65 and key <= 90) or (key >= 97 and key <= 126) then
341 -- end of sequence, return the full sequence
342 local result = string.char((unpack or table.unpack)(sequence))
343 sequence = nil
344 return result, "ansi"
345 end
346 end
347 end
348
349 -- error, or timeout reached, return the sequence so far
350 local partial = string.char((unpack or table.unpack)(sequence))
351 return nil, err, partial
352 end
353end
354
355
356
210return sys 357return sys