diff options
Diffstat (limited to 'system')
-rw-r--r-- | system/init.lua | 147 |
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 | ||
210 | do | ||
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 | ||
239 | end | ||
240 | |||
241 | |||
242 | |||
243 | do | ||
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 | ||
353 | end | ||
354 | |||
355 | |||
356 | |||
210 | return sys | 357 | return sys |