summaryrefslogtreecommitdiff
path: root/examples/read.lua
diff options
context:
space:
mode:
Diffstat (limited to 'examples/read.lua')
-rw-r--r--examples/read.lua119
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)