aboutsummaryrefslogtreecommitdiff
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 @@
1local sys = require "system"
2
3print [[
4
5This example shows how to do a non-blocking read from the cli.
6
7]]
8
9-- setup Windows console to handle ANSI processing
10local of_in = sys.getconsoleflags(io.stdin)
11local of_out = sys.getconsoleflags(io.stdout)
12sys.setconsoleflags(io.stdout, sys.getconsoleflags(io.stdout) + sys.COF_VIRTUAL_TERMINAL_PROCESSING)
13sys.setconsoleflags(io.stdin, sys.getconsoleflags(io.stdin) + sys.CIF_VIRTUAL_TERMINAL_INPUT)
14
15-- setup Posix terminal to use non-blocking mode, and disable line-mode
16local of_attr = sys.tcgetattr(io.stdin)
17local of_block = sys.getnonblock(io.stdin)
18sys.setnonblock(io.stdin, true)
19sys.tcsetattr(io.stdin, sys.TCSANOW, {
20 lflag = of_attr.lflag - sys.L_ICANON - sys.L_ECHO, -- disable canonical mode and echo
21})
22
23-- cursor sequences
24local get_cursor_pos = "\27[6n"
25
26
27
28local read_input do
29 local left_over_key
30
31 -- Reads a single key, if it is a 27 (start of ansi escape sequence) then it reads
32 -- the rest of the sequence.
33 -- This function is non-blocking, and will return nil if no key is available.
34 -- In case of an ANSI sequence, it will return the full sequence as a string.
35 -- @return nil|string the key read, or nil if no key is available
36 function read_input()
37 if left_over_key then
38 -- we still have a cached key, return it
39 local key = left_over_key
40 left_over_key = nil
41 return string.char(key)
42 end
43
44 local key = sys.readkey()
45 if key == nil then
46 return nil
47 end
48
49 if key ~= 27 then
50 return string.char(key)
51 end
52
53 -- looks like an ansi escape sequence, immediately read next char
54 -- as an heuristic against manually typing escape sequences
55 local brack = sys.readkey()
56 if brack ~= 91 then
57 -- not the expected [ character, so we return the key as is
58 -- and store the extra key read for the next call
59 left_over_key = brack
60 return string.char(key)
61 end
62
63 -- escape sequence detected, read the rest of the sequence
64 local seq = { key, brack }
65 while true do
66 key = sys.readkey()
67 table.insert(seq, key)
68 if (key >= 65 and key <= 90) or (key >= 97 and key <= 126) then
69 -- end of sequence, return the full sequence
70 return string.char((unpack or table.unpack)(seq))
71 end
72 end
73 -- unreachable
74 end
75end
76
77
78
79print("Press a key, or 'A' to get cursor position, 'ESC' to exit")
80while true do
81 local key
82
83 -- wait for a key, and sleep a bit to not do a busy-wait
84 while not key do
85 key = read_input()
86 if not key then sys.sleep(0.1) end
87 end
88
89 if key == "A" then io.write(get_cursor_pos); io.flush() end
90
91 -- check if we got a key or ANSI sequence
92 if #key == 1 then
93 -- just a key
94 local b = key:byte()
95 if b < 32 then
96 key = "." -- replace control characters with a simple "." to not mess up the screen
97 end
98
99 print("you pressed: " .. key .. " (" .. b .. ")")
100 if b == 27 then
101 print("Escape pressed, exiting")
102 break
103 end
104
105 else
106 -- we got an ANSI sequence
107 local seq = { key:byte(1, #key) }
108 print("ANSI sequence received: " .. key:sub(2,-1), "(bytes: " .. table.concat(seq, ", ")..")")
109 end
110end
111
112
113
114-- Clean up afterwards
115sys.setnonblock(io.stdin, false)
116sys.setconsoleflags(io.stdout, of_out)
117sys.setconsoleflags(io.stdin, of_in)
118sys.tcsetattr(io.stdin, sys.TCSANOW, of_attr)
119sys.setnonblock(io.stdin, of_block)