diff options
Diffstat (limited to 'examples/read.lua')
-rw-r--r-- | examples/read.lua | 119 |
1 files changed, 119 insertions, 0 deletions
diff --git a/examples/read.lua b/examples/read.lua new file mode 100644 index 0000000..7a1c747 --- /dev/null +++ b/examples/read.lua @@ -0,0 +1,119 @@ +local sys = require "system" + +print [[ + +This example shows how to do a non-blocking read from the cli. + +]] + +-- setup Windows console to handle ANSI processing +local of_in = sys.getconsoleflags(io.stdin) +local of_out = sys.getconsoleflags(io.stdout) +sys.setconsoleflags(io.stdout, sys.getconsoleflags(io.stdout) + sys.COF_VIRTUAL_TERMINAL_PROCESSING) +sys.setconsoleflags(io.stdin, sys.getconsoleflags(io.stdin) + sys.CIF_VIRTUAL_TERMINAL_INPUT) + +-- setup Posix terminal to use non-blocking mode, and disable line-mode +local of_attr = sys.tcgetattr(io.stdin) +local of_block = sys.getnonblock(io.stdin) +sys.setnonblock(io.stdin, true) +sys.tcsetattr(io.stdin, sys.TCSANOW, { + lflag = of_attr.lflag - sys.L_ICANON - sys.L_ECHO, -- disable canonical mode and echo +}) + +-- cursor sequences +local get_cursor_pos = "\27[6n" + + + +local read_input do + local left_over_key + + -- Reads a single key, if it is a 27 (start of ansi escape sequence) then it reads + -- the rest of the sequence. + -- This function is non-blocking, and will return nil if no key is available. + -- In case of an ANSI sequence, it will return the full sequence as a string. + -- @return nil|string the key read, or nil if no key is available + function read_input() + if left_over_key then + -- we still have a cached key, return it + local key = left_over_key + left_over_key = nil + return string.char(key) + end + + local key = sys.readkey() + if key == nil then + return nil + end + + if key ~= 27 then + return string.char(key) + end + + -- looks like an ansi escape sequence, immediately read next char + -- as an heuristic against manually typing escape sequences + local brack = sys.readkey() + if brack ~= 91 then + -- not the expected [ character, so we return the key as is + -- and store the extra key read for the next call + left_over_key = brack + return string.char(key) + end + + -- escape sequence detected, read the rest of the sequence + local seq = { key, brack } + while true do + key = sys.readkey() + table.insert(seq, key) + if (key >= 65 and key <= 90) or (key >= 97 and key <= 126) then + -- end of sequence, return the full sequence + return string.char((unpack or table.unpack)(seq)) + end + end + -- unreachable + end +end + + + +print("Press a key, or 'A' to get cursor position, 'ESC' to exit") +while true do + local key + + -- wait for a key, and sleep a bit to not do a busy-wait + while not key do + key = read_input() + if not key then sys.sleep(0.1) end + end + + if key == "A" then io.write(get_cursor_pos); io.flush() end + + -- check if we got a key or ANSI sequence + if #key == 1 then + -- just a key + local b = key:byte() + if b < 32 then + key = "." -- replace control characters with a simple "." to not mess up the screen + end + + print("you pressed: " .. key .. " (" .. b .. ")") + if b == 27 then + print("Escape pressed, exiting") + break + end + + else + -- we got an ANSI sequence + local seq = { key:byte(1, #key) } + print("ANSI sequence received: " .. key:sub(2,-1), "(bytes: " .. table.concat(seq, ", ")..")") + end +end + + + +-- Clean up afterwards +sys.setnonblock(io.stdin, false) +sys.setconsoleflags(io.stdout, of_out) +sys.setconsoleflags(io.stdin, of_in) +sys.tcsetattr(io.stdin, sys.TCSANOW, of_attr) +sys.setnonblock(io.stdin, of_block) |