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 @@ | |||
1 | local sys = require "system" | ||
2 | |||
3 | print [[ | ||
4 | |||
5 | This example shows how to do a non-blocking read from the cli. | ||
6 | |||
7 | ]] | ||
8 | |||
9 | -- setup Windows console to handle ANSI processing | ||
10 | local of_in = sys.getconsoleflags(io.stdin) | ||
11 | local of_out = sys.getconsoleflags(io.stdout) | ||
12 | sys.setconsoleflags(io.stdout, sys.getconsoleflags(io.stdout) + sys.COF_VIRTUAL_TERMINAL_PROCESSING) | ||
13 | sys.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 | ||
16 | local of_attr = sys.tcgetattr(io.stdin) | ||
17 | local of_block = sys.getnonblock(io.stdin) | ||
18 | sys.setnonblock(io.stdin, true) | ||
19 | sys.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 | ||
24 | local get_cursor_pos = "\27[6n" | ||
25 | |||
26 | |||
27 | |||
28 | local 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 | ||
75 | end | ||
76 | |||
77 | |||
78 | |||
79 | print("Press a key, or 'A' to get cursor position, 'ESC' to exit") | ||
80 | while 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 | ||
110 | end | ||
111 | |||
112 | |||
113 | |||
114 | -- Clean up afterwards | ||
115 | sys.setnonblock(io.stdin, false) | ||
116 | sys.setconsoleflags(io.stdout, of_out) | ||
117 | sys.setconsoleflags(io.stdin, of_in) | ||
118 | sys.tcsetattr(io.stdin, sys.TCSANOW, of_attr) | ||
119 | sys.setnonblock(io.stdin, of_block) | ||