diff options
| author | Thijs Schreijer <thijs@thijsschreijer.nl> | 2025-04-13 22:22:46 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-04-13 22:22:46 +0200 |
| commit | 091672f57ba57b745a51202b38af61bd240d1084 (patch) | |
| tree | dcdd0b4e65516682b17390f049fd8b1599274232 /system | |
| parent | 06691764e87c2979f4a00ed386e237150f055d5a (diff) | |
| download | luasystem-091672f57ba57b745a51202b38af61bd240d1084.tar.gz luasystem-091672f57ba57b745a51202b38af61bd240d1084.tar.bz2 luasystem-091672f57ba57b745a51202b38af61bd240d1084.zip | |
fix(terminal): readansi now properly handles <alt>+key key-presses (#62)
Also; documents the internal buffer and retry behaviour of readansi
Diffstat (limited to 'system')
| -rw-r--r-- | system/init.lua | 53 |
1 files changed, 34 insertions, 19 deletions
diff --git a/system/init.lua b/system/init.lua index 28fe65c..e0e5f21 100644 --- a/system/init.lua +++ b/system/init.lua | |||
| @@ -267,7 +267,6 @@ end | |||
| 267 | 267 | ||
| 268 | 268 | ||
| 269 | do | 269 | do |
| 270 | local left_over_key | ||
| 271 | local sequence -- table to store the sequence in progress | 270 | local sequence -- table to store the sequence in progress |
| 272 | local utf8_length -- length of utf8 sequence currently being processed | 271 | local utf8_length -- length of utf8 sequence currently being processed |
| 273 | local unpack = unpack or table.unpack | 272 | local unpack = unpack or table.unpack |
| @@ -285,6 +284,8 @@ do | |||
| 285 | -- @treturn[2] nil in case of an error | 284 | -- @treturn[2] nil in case of an error |
| 286 | -- @treturn[2] string error message; `"timeout"` if the timeout was reached. | 285 | -- @treturn[2] string error message; `"timeout"` if the timeout was reached. |
| 287 | -- @treturn[2] string partial result in case of an error while reading a sequence, the sequence so far. | 286 | -- @treturn[2] string partial result in case of an error while reading a sequence, the sequence so far. |
| 287 | -- The function retains its own internal buffer, so on the next call the incomplete buffer is used to | ||
| 288 | -- complete the sequence. | ||
| 288 | function system.readansi(timeout, fsleep) | 289 | function system.readansi(timeout, fsleep) |
| 289 | if type(timeout) ~= "number" then | 290 | if type(timeout) ~= "number" then |
| 290 | error("arg #1 to readansi, expected timeout in seconds, got " .. type(timeout), 2) | 291 | error("arg #1 to readansi, expected timeout in seconds, got " .. type(timeout), 2) |
| @@ -295,33 +296,47 @@ do | |||
| 295 | 296 | ||
| 296 | if not sequence then | 297 | if not sequence then |
| 297 | -- no sequence in progress, read a key | 298 | -- no sequence in progress, read a key |
| 298 | 299 | local err | |
| 299 | if left_over_key then | 300 | key, err = system.readkey(timeout, fsleep) |
| 300 | -- we still have a cached key from the last call | 301 | if key == nil then -- timeout or error |
| 301 | key = left_over_key | 302 | return nil, err |
| 302 | left_over_key = nil | ||
| 303 | else | ||
| 304 | -- read a new key | ||
| 305 | local err | ||
| 306 | key, err = system.readkey(timeout, fsleep) | ||
| 307 | if key == nil then -- timeout or error | ||
| 308 | return nil, err | ||
| 309 | end | ||
| 310 | end | 303 | end |
| 311 | 304 | ||
| 312 | if key == 27 then | 305 | if key == 27 then |
| 313 | -- looks like an ansi escape sequence, immediately read next char | 306 | -- looks like an ansi escape sequence, immediately read next char |
| 314 | -- as an heuristic against manually typing escape sequences | 307 | -- as an heuristic against manually typing escape sequences |
| 315 | local key2 = system.readkey(0, fsleep) | 308 | local key2 = system.readkey(0, fsleep) |
| 316 | if key2 ~= 91 and key2 ~= 79 then -- we expect either "[" or "O" for an ANSI sequence | 309 | if key2 == nil then |
| 317 | -- not the expected [ or O character, so we return the key as is | 310 | -- no key available, return the escape key, on its own |
| 318 | -- and store the extra key read for the next call | 311 | sequence = nil |
| 319 | left_over_key = key2 | ||
| 320 | return string.char(key), "char" | 312 | return string.char(key), "char" |
| 313 | |||
| 314 | elseif key2 == 91 then | ||
| 315 | -- "[" means it is for sure an ANSI sequence | ||
| 316 | sequence = { key, key2 } | ||
| 317 | |||
| 318 | elseif key2 == 79 then | ||
| 319 | -- "O" means it is either an ANSI sequence or just an <alt>+O key stroke | ||
| 320 | -- check if there is yet another byte available | ||
| 321 | local key3 = system.readkey(0, fsleep) | ||
| 322 | if key3 == nil then | ||
| 323 | -- no key available, return the <alt>O key stroke, report as ANSI | ||
| 324 | sequence = nil | ||
| 325 | return string.char(key, key2), "ansi" | ||
| 326 | end | ||
| 327 | -- it's an ANSI sequence, marked with <ESC>O | ||
| 328 | if (key3 >= 65 and key3 <= 90) or (key3 >= 97 and key3 <= 126) then | ||
| 329 | -- end of sequence, return the full sequence | ||
| 330 | return string.char(key, key2, key3), "ansi" | ||
| 331 | end | ||
| 332 | sequence = { key, key2, key3 } | ||
| 333 | |||
| 334 | else | ||
| 335 | -- not an ANSI sequence, but an <alt>+<key2> key stroke, so report as ANSI | ||
| 336 | sequence = nil | ||
| 337 | return string.char(key, key2), "ansi" | ||
| 321 | end | 338 | end |
| 322 | 339 | ||
| 323 | -- escape sequence detected | ||
| 324 | sequence = { key, key2 } | ||
| 325 | else | 340 | else |
| 326 | -- check UTF8 length | 341 | -- check UTF8 length |
| 327 | utf8_length = key < 128 and 1 or key < 224 and 2 or key < 240 and 3 or key < 248 and 4 | 342 | utf8_length = key < 128 and 1 or key < 224 and 2 or key < 240 and 3 or key < 248 and 4 |
