aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/build.yml4
-rw-r--r--.github/workflows/lint.yml (renamed from .github/workflows/luacheck.yml)2
-rw-r--r--CHANGELOG.md31
-rw-r--r--doc_topics/03-terminal.md2
-rw-r--r--docs/classes/bitflags.html2
-rw-r--r--docs/examples/compat.lua.html2
-rw-r--r--docs/examples/flag_debugging.lua.html2
-rw-r--r--docs/examples/password_input.lua.html2
-rw-r--r--docs/examples/read.lua.html2
-rw-r--r--docs/examples/readline.lua.html2
-rw-r--r--docs/examples/spinner.lua.html2
-rw-r--r--docs/examples/spiral_snake.lua.html2
-rw-r--r--docs/examples/terminalsize.lua.html2
-rw-r--r--docs/index.html2
-rw-r--r--docs/modules/system.html916
-rw-r--r--docs/topics/01-introduction.md.html2
-rw-r--r--docs/topics/02-development.md.html2
-rw-r--r--docs/topics/03-terminal.md.html4
-rw-r--r--docs/topics/CHANGELOG.md.html41
-rw-r--r--docs/topics/LICENSE.md.html2
-rw-r--r--rockspecs/luasystem-0.6.0-1.rockspec85
-rw-r--r--rockspecs/luasystem-0.6.1-1.rockspec85
-rw-r--r--rockspecs/luasystem-0.6.2-1.rockspec85
-rw-r--r--rockspecs/luasystem-0.6.3-1.rockspec85
-rw-r--r--spec/04-term_spec.lua77
-rw-r--r--src/core.c2
-rw-r--r--src/term.c131
-rw-r--r--system/init.lua110
28 files changed, 1211 insertions, 475 deletions
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 093ef05..5b3d31a 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -21,7 +21,7 @@ jobs:
21 strategy: 21 strategy:
22 fail-fast: false 22 fail-fast: false
23 matrix: 23 matrix:
24 os: ['ubuntu-20.04', 'macos-13'] 24 os: ['ubuntu-24.04', 'macos-13']
25 luaVersion: 25 luaVersion:
26 - "5.1" 26 - "5.1"
27 - "5.2" 27 - "5.2"
@@ -97,7 +97,7 @@ jobs:
97 97
98 - uses: hishamhm/gh-actions-luarocks@master 98 - uses: hishamhm/gh-actions-luarocks@master
99 with: 99 with:
100 luaRocksVersion: "3.11.0" 100 luaRocksVersion: "3.12.0"
101 101
102 - name: test dependencies 102 - name: test dependencies
103 run: | 103 run: |
diff --git a/.github/workflows/luacheck.yml b/.github/workflows/lint.yml
index 3ad8db7..83abc44 100644
--- a/.github/workflows/luacheck.yml
+++ b/.github/workflows/lint.yml
@@ -1,5 +1,5 @@
1--- 1---
2name: Luacheck 2name: Lint
3 3
4on: [push, pull_request] 4on: [push, pull_request]
5 5
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 1c9e15e..3b00fb6 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -27,6 +27,33 @@ The scope of what is covered by the version number excludes:
27 27
28## Version history 28## Version history
29 29
30### version 0.6.3, released 11-Jul-2025
31
32- Fix: maximum key-delay, reduced from 0.2 to 0.1 seconds to reduce slugginess feel on some key presses.
33 See [#69](https://github.com/lunarmodules/luasystem/pull/69).
34- Fix: `readansi` now only reports printable chars as characters
35 See [#70](https://github.com/lunarmodules/luasystem/pull/70).
36- Fix: readkey now ignores Windows scancodes if received.
37 See [#74](https://github.com/lunarmodules/luasystem/pull/74).
38- Fix: readkey now passes errors on.
39 See [#74](https://github.com/lunarmodules/luasystem/pull/74).
40
41### version 0.6.2, released 15-Apr-2025
42
43- Fix: autotermrestore didn't work because its metatable was overwritten.
44
45### version 0.6.1, released 13-Apr-2025
46
47- Docs: document readansi internal buffer for incomplete sequences.
48- Fix: ensure to properly parse `<alt>+key` key presses
49
50### version 0.6.0, released 10-Apr-2025
51
52- Fix: when sleep returns an error, pass that on in `readkey`.
53- Feat: added `detachfds` which will create separate file descriptions for `stdout`
54 and `stderr` to ensure that related settings (eg. non-blocking flag) will not be shared
55 amongst those streams and `stdin`.
56
30### version 0.5.1, released 12-Mar-2025 57### version 0.5.1, released 12-Mar-2025
31 58
32- Fix: on older unixes with glibc < 2.25, fall back to `/dev/urandom` 59- Fix: on older unixes with glibc < 2.25, fall back to `/dev/urandom`
@@ -38,12 +65,12 @@ The scope of what is covered by the version number excludes:
38- Feat: allow passing in a sleep function to `readkey` and `readansi` 65- Feat: allow passing in a sleep function to `readkey` and `readansi`
39- Fix: NetBSD fix compilation, undeclared directives 66- Fix: NetBSD fix compilation, undeclared directives
40- Refactor: random bytes; remove deprecated API usage on Windows, move to 67- Refactor: random bytes; remove deprecated API usage on Windows, move to
41 binary api instead of /dev/urandom file on linux and bsd 68 binary api instead of `/dev/urandom` file on linux and bsd
42 69
43### version 0.4.5, released 18-Dec-2024 70### version 0.4.5, released 18-Dec-2024
44 71
45- Fix: suppress a warning when building with clang 72- Fix: suppress a warning when building with clang
46- Fix: do not rely on luaconf.h to include limits.h, fixes builds with latest LuaJIT (#38). 73- Fix: do not rely on `luaconf.h` to include `limits.h`, fixes builds with latest LuaJIT (#38).
47 74
48### version 0.4.4, released 03-Sep-2024 75### version 0.4.4, released 03-Sep-2024
49 76
diff --git a/doc_topics/03-terminal.md b/doc_topics/03-terminal.md
index 5bdf543..a5341d6 100644
--- a/doc_topics/03-terminal.md
+++ b/doc_topics/03-terminal.md
@@ -16,6 +16,7 @@ Since there are a myriad of settings available;
16- `system.setconsoleflags` (Windows) 16- `system.setconsoleflags` (Windows)
17- `system.setconsolecp` (Windows) 17- `system.setconsolecp` (Windows)
18- `system.setconsoleoutputcp` (Windows) 18- `system.setconsoleoutputcp` (Windows)
19- `system.detachfds` (Posix)
19- `system.setnonblock` (Posix) 20- `system.setnonblock` (Posix)
20- `system.tcsetattr` (Posix) 21- `system.tcsetattr` (Posix)
21 22
@@ -105,6 +106,7 @@ To use non-blocking input here's how to set it up:
105 sys.setconsoleflags(io.stdin, sys.getconsoleflags(io.stdin) - sys.CIF_ECHO_INPUT - sys.CIF_LINE_INPUT) 106 sys.setconsoleflags(io.stdin, sys.getconsoleflags(io.stdin) - sys.CIF_ECHO_INPUT - sys.CIF_LINE_INPUT)
106 107
107 -- setup Posix by disabling echo, canonical mode, and making non-blocking 108 -- setup Posix by disabling echo, canonical mode, and making non-blocking
109 sys.detachfds() -- ensure stdin/out/err have their own file descriptions
108 local of_attr = sys.tcgetattr(io.stdin) 110 local of_attr = sys.tcgetattr(io.stdin)
109 sys.tcsetattr(io.stdin, sys.TCSANOW, { 111 sys.tcsetattr(io.stdin, sys.TCSANOW, {
110 lflag = of_attr.lflag - sys.L_ICANON - sys.L_ECHO, 112 lflag = of_attr.lflag - sys.L_ICANON - sys.L_ECHO,
diff --git a/docs/classes/bitflags.html b/docs/classes/bitflags.html
index 647cc62..7637aeb 100644
--- a/docs/classes/bitflags.html
+++ b/docs/classes/bitflags.html
@@ -298,7 +298,7 @@ return <code>false</code> if the flags are checked.
298</div> <!-- id="main" --> 298</div> <!-- id="main" -->
299<div id="about"> 299<div id="about">
300<i>generated by <a href="http://github.com/lunarmodules/LDoc">LDoc 1.5.0</a></i> 300<i>generated by <a href="http://github.com/lunarmodules/LDoc">LDoc 1.5.0</a></i>
301<i style="float:right;">Last updated 2025-03-12 15:17:50 </i> 301<i style="float:right;">Last updated 2025-07-11 22:55:05 </i>
302</div> <!-- id="about" --> 302</div> <!-- id="about" -->
303</div> <!-- id="container" --> 303</div> <!-- id="container" -->
304</body> 304</body>
diff --git a/docs/examples/compat.lua.html b/docs/examples/compat.lua.html
index e421044..ca4e489 100644
--- a/docs/examples/compat.lua.html
+++ b/docs/examples/compat.lua.html
@@ -112,7 +112,7 @@
112</div> <!-- id="main" --> 112</div> <!-- id="main" -->
113<div id="about"> 113<div id="about">
114<i>generated by <a href="http://github.com/lunarmodules/LDoc">LDoc 1.5.0</a></i> 114<i>generated by <a href="http://github.com/lunarmodules/LDoc">LDoc 1.5.0</a></i>
115<i style="float:right;">Last updated 2025-03-12 15:17:50 </i> 115<i style="float:right;">Last updated 2025-07-11 22:55:05 </i>
116</div> <!-- id="about" --> 116</div> <!-- id="about" -->
117</div> <!-- id="container" --> 117</div> <!-- id="container" -->
118</body> 118</body>
diff --git a/docs/examples/flag_debugging.lua.html b/docs/examples/flag_debugging.lua.html
index e8a75e3..dba4471 100644
--- a/docs/examples/flag_debugging.lua.html
+++ b/docs/examples/flag_debugging.lua.html
@@ -80,7 +80,7 @@
80</div> <!-- id="main" --> 80</div> <!-- id="main" -->
81<div id="about"> 81<div id="about">
82<i>generated by <a href="http://github.com/lunarmodules/LDoc">LDoc 1.5.0</a></i> 82<i>generated by <a href="http://github.com/lunarmodules/LDoc">LDoc 1.5.0</a></i>
83<i style="float:right;">Last updated 2025-03-12 15:17:50 </i> 83<i style="float:right;">Last updated 2025-07-11 22:55:05 </i>
84</div> <!-- id="about" --> 84</div> <!-- id="about" -->
85</div> <!-- id="container" --> 85</div> <!-- id="container" -->
86</body> 86</body>
diff --git a/docs/examples/password_input.lua.html b/docs/examples/password_input.lua.html
index a5eac4d..342b066 100644
--- a/docs/examples/password_input.lua.html
+++ b/docs/examples/password_input.lua.html
@@ -132,7 +132,7 @@ useful for reading secrets from the user.
132</div> <!-- id="main" --> 132</div> <!-- id="main" -->
133<div id="about"> 133<div id="about">
134<i>generated by <a href="http://github.com/lunarmodules/LDoc">LDoc 1.5.0</a></i> 134<i>generated by <a href="http://github.com/lunarmodules/LDoc">LDoc 1.5.0</a></i>
135<i style="float:right;">Last updated 2025-03-12 15:17:50 </i> 135<i style="float:right;">Last updated 2025-07-11 22:55:05 </i>
136</div> <!-- id="about" --> 136</div> <!-- id="about" -->
137</div> <!-- id="container" --> 137</div> <!-- id="container" -->
138</body> 138</body>
diff --git a/docs/examples/read.lua.html b/docs/examples/read.lua.html
index 3b53a8b..4995bda 100644
--- a/docs/examples/read.lua.html
+++ b/docs/examples/read.lua.html
@@ -143,7 +143,7 @@ sys.<span class="function-name">setnonblock</span>(<span class="global">io</span
143</div> <!-- id="main" --> 143</div> <!-- id="main" -->
144<div id="about"> 144<div id="about">
145<i>generated by <a href="http://github.com/lunarmodules/LDoc">LDoc 1.5.0</a></i> 145<i>generated by <a href="http://github.com/lunarmodules/LDoc">LDoc 1.5.0</a></i>
146<i style="float:right;">Last updated 2025-03-12 15:17:50 </i> 146<i style="float:right;">Last updated 2025-07-11 22:55:05 </i>
147</div> <!-- id="about" --> 147</div> <!-- id="about" -->
148</div> <!-- id="container" --> 148</div> <!-- id="container" -->
149</body> 149</body>
diff --git a/docs/examples/readline.lua.html b/docs/examples/readline.lua.html
index dd63721..493de2c 100644
--- a/docs/examples/readline.lua.html
+++ b/docs/examples/readline.lua.html
@@ -549,7 +549,7 @@ sys.<span class="function-name">setconsoleflags</span>(<span class="global">io</
549</div> <!-- id="main" --> 549</div> <!-- id="main" -->
550<div id="about"> 550<div id="about">
551<i>generated by <a href="http://github.com/lunarmodules/LDoc">LDoc 1.5.0</a></i> 551<i>generated by <a href="http://github.com/lunarmodules/LDoc">LDoc 1.5.0</a></i>
552<i style="float:right;">Last updated 2025-03-12 15:17:50 </i> 552<i style="float:right;">Last updated 2025-07-11 22:55:05 </i>
553</div> <!-- id="about" --> 553</div> <!-- id="about" -->
554</div> <!-- id="container" --> 554</div> <!-- id="container" -->
555</body> 555</body>
diff --git a/docs/examples/spinner.lua.html b/docs/examples/spinner.lua.html
index 99a1c91..6e2d63a 100644
--- a/docs/examples/spinner.lua.html
+++ b/docs/examples/spinner.lua.html
@@ -137,7 +137,7 @@ sys.<span class="function-name">setnonblock</span>(<span class="global">io</span
137</div> <!-- id="main" --> 137</div> <!-- id="main" -->
138<div id="about"> 138<div id="about">
139<i>generated by <a href="http://github.com/lunarmodules/LDoc">LDoc 1.5.0</a></i> 139<i>generated by <a href="http://github.com/lunarmodules/LDoc">LDoc 1.5.0</a></i>
140<i style="float:right;">Last updated 2025-03-12 15:17:50 </i> 140<i style="float:right;">Last updated 2025-07-11 22:55:05 </i>
141</div> <!-- id="about" --> 141</div> <!-- id="about" -->
142</div> <!-- id="container" --> 142</div> <!-- id="container" -->
143</body> 143</body>
diff --git a/docs/examples/spiral_snake.lua.html b/docs/examples/spiral_snake.lua.html
index 7be16e8..97d5f77 100644
--- a/docs/examples/spiral_snake.lua.html
+++ b/docs/examples/spiral_snake.lua.html
@@ -145,7 +145,7 @@ codes for moving the cursor around.
145</div> <!-- id="main" --> 145</div> <!-- id="main" -->
146<div id="about"> 146<div id="about">
147<i>generated by <a href="http://github.com/lunarmodules/LDoc">LDoc 1.5.0</a></i> 147<i>generated by <a href="http://github.com/lunarmodules/LDoc">LDoc 1.5.0</a></i>
148<i style="float:right;">Last updated 2025-03-12 15:17:50 </i> 148<i style="float:right;">Last updated 2025-07-11 22:55:05 </i>
149</div> <!-- id="about" --> 149</div> <!-- id="about" -->
150</div> <!-- id="container" --> 150</div> <!-- id="container" -->
151</body> 151</body>
diff --git a/docs/examples/terminalsize.lua.html b/docs/examples/terminalsize.lua.html
index 6a6a93e..d3486bb 100644
--- a/docs/examples/terminalsize.lua.html
+++ b/docs/examples/terminalsize.lua.html
@@ -110,7 +110,7 @@ sys.<span class="function-name">tcsetattr</span>(<span class="global">io</span>.
110</div> <!-- id="main" --> 110</div> <!-- id="main" -->
111<div id="about"> 111<div id="about">
112<i>generated by <a href="http://github.com/lunarmodules/LDoc">LDoc 1.5.0</a></i> 112<i>generated by <a href="http://github.com/lunarmodules/LDoc">LDoc 1.5.0</a></i>
113<i style="float:right;">Last updated 2025-03-12 15:17:50 </i> 113<i style="float:right;">Last updated 2025-07-11 22:55:05 </i>
114</div> <!-- id="about" --> 114</div> <!-- id="about" -->
115</div> <!-- id="container" --> 115</div> <!-- id="container" -->
116</body> 116</body>
diff --git a/docs/index.html b/docs/index.html
index 5d7b02f..e901d08 100644
--- a/docs/index.html
+++ b/docs/index.html
@@ -142,7 +142,7 @@
142</div> <!-- id="main" --> 142</div> <!-- id="main" -->
143<div id="about"> 143<div id="about">
144<i>generated by <a href="http://github.com/lunarmodules/LDoc">LDoc 1.5.0</a></i> 144<i>generated by <a href="http://github.com/lunarmodules/LDoc">LDoc 1.5.0</a></i>
145<i style="float:right;">Last updated 2025-03-12 15:17:50 </i> 145<i style="float:right;">Last updated 2025-07-11 22:55:05 </i>
146</div> <!-- id="about" --> 146</div> <!-- id="about" -->
147</div> <!-- id="container" --> 147</div> <!-- id="container" -->
148</body> 148</body>
diff --git a/docs/modules/system.html b/docs/modules/system.html
index fa41458..0a2447e 100644
--- a/docs/modules/system.html
+++ b/docs/modules/system.html
@@ -37,7 +37,12 @@
37<li><a href="#Environment">Environment </a></li> 37<li><a href="#Environment">Environment </a></li>
38<li><a href="#Random">Random </a></li> 38<li><a href="#Random">Random </a></li>
39<li><a href="#Terminal">Terminal </a></li> 39<li><a href="#Terminal">Terminal </a></li>
40<li><a href="#Terminal_Windows">Terminal_Windows</a></li>
41<li><a href="#Terminal_Posix">Terminal_Posix</a></li>
42<li><a href="#Terminal_Input">Terminal_Input</a></li>
43<li><a href="#Terminal_UTF_8">Terminal_UTF-8</a></li>
40<li><a href="#Time">Time </a></li> 44<li><a href="#Time">Time </a></li>
45<li><a href="#Terminal_Backup">Terminal_Backup</a></li>
41</ul> 46</ul>
42 47
43 48
@@ -112,93 +117,93 @@
112<h2><a href="#Terminal">Terminal </a></h2> 117<h2><a href="#Terminal">Terminal </a></h2>
113<table class="function_list"> 118<table class="function_list">
114 <tr> 119 <tr>
115 <td class="name" nowrap><a href="#CODEPAGE_UTF8">CODEPAGE_UTF8</a></td> 120 <td class="name" nowrap><a href="#isatty">isatty (file)</a></td>
116 <td class="summary">UTF8 codepage.</td> 121 <td class="summary">Checks if a file-handle is a TTY.</td>
117 </tr>
118 <tr>
119 <td class="name" nowrap><a href="#_readkey">_readkey ()</a></td>
120 <td class="summary">Reads a key from the console non-blocking.</td>
121 </tr>
122 <tr>
123 <td class="name" nowrap><a href="#autotermrestore">autotermrestore ()</a></td>
124 <td class="summary">Backs up terminal settings and restores them on application exit.</td>
125 </tr> 122 </tr>
126 <tr> 123 <tr>
127 <td class="name" nowrap><a href="#getconsolecp">getconsolecp ()</a></td> 124 <td class="name" nowrap><a href="#termsize">termsize ()</a></td>
128 <td class="summary">Gets the current console code page (Windows).</td> 125 <td class="summary">Get the size of the terminal in rows and columns.</td>
129 </tr> 126 </tr>
127</table>
128<h2><a href="#Terminal_Windows">Terminal_Windows</a></h2>
129<table class="function_list">
130 <tr> 130 <tr>
131 <td class="name" nowrap><a href="#getconsoleflags">getconsoleflags (file)</a></td> 131 <td class="name" nowrap><a href="#getconsoleflags">getconsoleflags (file)</a></td>
132 <td class="summary">Gets console flags (Windows).</td> 132 <td class="summary">Gets console flags (Windows).</td>
133 </tr> 133 </tr>
134 <tr> 134 <tr>
135 <td class="name" nowrap><a href="#getconsoleoutputcp">getconsoleoutputcp ()</a></td> 135 <td class="name" nowrap><a href="#listconsoleflags">listconsoleflags (fh)</a></td>
136 <td class="summary">Gets the current console output code page (Windows).</td> 136 <td class="summary">Debug function for console flags (Windows).</td>
137 </tr> 137 </tr>
138 <tr> 138 <tr>
139 <td class="name" nowrap><a href="#getnonblock">getnonblock (fd)</a></td> 139 <td class="name" nowrap><a href="#setconsoleflags">setconsoleflags (file, bitflags)</a></td>
140 <td class="summary">Gets non-blocking mode status for a file (Posix).</td> 140 <td class="summary">Sets the console flags (Windows).</td>
141 </tr> 141 </tr>
142</table>
143<h2><a href="#Terminal_Posix">Terminal_Posix</a></h2>
144<table class="function_list">
142 <tr> 145 <tr>
143 <td class="name" nowrap><a href="#isatty">isatty (file)</a></td> 146 <td class="name" nowrap><a href="#detachfds">detachfds ()</a></td>
144 <td class="summary">Checks if a file-handle is a TTY.</td> 147 <td class="summary">Creates new file descriptions for <code>stdout</code> and <code>stderr</code>.</td>
145 </tr> 148 </tr>
146 <tr> 149 <tr>
147 <td class="name" nowrap><a href="#listconsoleflags">listconsoleflags (fh)</a></td> 150 <td class="name" nowrap><a href="#getnonblock">getnonblock (fd)</a></td>
148 <td class="summary">Debug function for console flags (Windows).</td> 151 <td class="summary">Gets non-blocking mode status for a file (Posix).</td>
149 </tr> 152 </tr>
150 <tr> 153 <tr>
151 <td class="name" nowrap><a href="#listtermflags">listtermflags (fh)</a></td> 154 <td class="name" nowrap><a href="#listtermflags">listtermflags (fh)</a></td>
152 <td class="summary">Debug function for terminal flags (Posix).</td> 155 <td class="summary">Debug function for terminal flags (Posix).</td>
153 </tr> 156 </tr>
154 <tr> 157 <tr>
155 <td class="name" nowrap><a href="#readansi">readansi (timeout[, fsleep=system.sleep])</a></td> 158 <td class="name" nowrap><a href="#setnonblock">setnonblock (fd, make_non_block)</a></td>
156 <td class="summary">Reads a single key, if it is the start of ansi escape sequence then it reads 159 <td class="summary">Enables or disables non-blocking mode for a file (Posix).</td>
157 the full sequence.</td>
158 </tr>
159 <tr>
160 <td class="name" nowrap><a href="#readkey">readkey (timeout[, fsleep=system.sleep])</a></td>
161 <td class="summary">Reads a single byte from the console, with a timeout.</td>
162 </tr> 160 </tr>
163 <tr> 161 <tr>
164 <td class="name" nowrap><a href="#setconsolecp">setconsolecp (cp)</a></td> 162 <td class="name" nowrap><a href="#tcgetattr">tcgetattr (fd)</a></td>
165 <td class="summary">Sets the current console code page (Windows).</td> 163 <td class="summary">Get termios state (Posix).</td>
166 </tr> 164 </tr>
167 <tr> 165 <tr>
168 <td class="name" nowrap><a href="#setconsoleflags">setconsoleflags (file, bitflags)</a></td> 166 <td class="name" nowrap><a href="#tcsetattr">tcsetattr (fd, actions, termios)</a></td>
169 <td class="summary">Sets the console flags (Windows).</td> 167 <td class="summary">Set termios state (Posix).</td>
170 </tr> 168 </tr>
169</table>
170<h2><a href="#Terminal_Input">Terminal_Input</a></h2>
171<table class="function_list">
171 <tr> 172 <tr>
172 <td class="name" nowrap><a href="#setconsoleoutputcp">setconsoleoutputcp (cp)</a></td> 173 <td class="name" nowrap><a href="#_readkey">_readkey ()</a></td>
173 <td class="summary">Sets the current console output code page (Windows).</td> 174 <td class="summary">Reads a key from the console non-blocking.</td>
174 </tr> 175 </tr>
175 <tr> 176 <tr>
176 <td class="name" nowrap><a href="#setnonblock">setnonblock (fd, make_non_block)</a></td> 177 <td class="name" nowrap><a href="#readansi">readansi (timeout[, fsleep=system.sleep])</a></td>
177 <td class="summary">Enables or disables non-blocking mode for a file (Posix).</td> 178 <td class="summary">Reads a single key, if it is the start of ansi escape sequence then it reads
179 the full sequence.</td>
178 </tr> 180 </tr>
179 <tr> 181 <tr>
180 <td class="name" nowrap><a href="#tcgetattr">tcgetattr (fd)</a></td> 182 <td class="name" nowrap><a href="#readkey">readkey (timeout[, fsleep=system.sleep])</a></td>
181 <td class="summary">Get termios state (Posix).</td> 183 <td class="summary">Reads a single byte from the console, with a timeout.</td>
182 </tr> 184 </tr>
185</table>
186<h2><a href="#Terminal_UTF_8">Terminal_UTF-8</a></h2>
187<table class="function_list">
183 <tr> 188 <tr>
184 <td class="name" nowrap><a href="#tcsetattr">tcsetattr (fd, actions, termios)</a></td> 189 <td class="name" nowrap><a href="#CODEPAGE_UTF8">CODEPAGE_UTF8</a></td>
185 <td class="summary">Set termios state (Posix).</td> 190 <td class="summary">UTF8 codepage.</td>
186 </tr> 191 </tr>
187 <tr> 192 <tr>
188 <td class="name" nowrap><a href="#termbackup">termbackup ()</a></td> 193 <td class="name" nowrap><a href="#getconsolecp">getconsolecp ()</a></td>
189 <td class="summary">Returns a backup of terminal settings for stdin/out/err.</td> 194 <td class="summary">Gets the current console code page (Windows).</td>
190 </tr> 195 </tr>
191 <tr> 196 <tr>
192 <td class="name" nowrap><a href="#termrestore">termrestore (backup)</a></td> 197 <td class="name" nowrap><a href="#getconsoleoutputcp">getconsoleoutputcp ()</a></td>
193 <td class="summary">Restores terminal settings from a backup</td> 198 <td class="summary">Gets the current console output code page (Windows).</td>
194 </tr> 199 </tr>
195 <tr> 200 <tr>
196 <td class="name" nowrap><a href="#termsize">termsize ()</a></td> 201 <td class="name" nowrap><a href="#setconsolecp">setconsolecp (cp)</a></td>
197 <td class="summary">Get the size of the terminal in rows and columns.</td> 202 <td class="summary">Sets the current console code page (Windows).</td>
198 </tr> 203 </tr>
199 <tr> 204 <tr>
200 <td class="name" nowrap><a href="#termwrap">termwrap (f)</a></td> 205 <td class="name" nowrap><a href="#setconsoleoutputcp">setconsoleoutputcp (cp)</a></td>
201 <td class="summary">Wraps a function to automatically restore terminal settings upon returning.</td> 206 <td class="summary">Sets the current console output code page (Windows).</td>
202 </tr> 207 </tr>
203 <tr> 208 <tr>
204 <td class="name" nowrap><a href="#utf8cwidth">utf8cwidth (utf8_char)</a></td> 209 <td class="name" nowrap><a href="#utf8cwidth">utf8cwidth (utf8_char)</a></td>
@@ -224,6 +229,25 @@
224 <td class="summary">Sleep without a busy loop.</td> 229 <td class="summary">Sleep without a busy loop.</td>
225 </tr> 230 </tr>
226</table> 231</table>
232<h2><a href="#Terminal_Backup">Terminal_Backup</a></h2>
233<table class="function_list">
234 <tr>
235 <td class="name" nowrap><a href="#autotermrestore">autotermrestore ()</a></td>
236 <td class="summary">Backs up terminal settings and restores them on application exit.</td>
237 </tr>
238 <tr>
239 <td class="name" nowrap><a href="#termbackup">termbackup ()</a></td>
240 <td class="summary">Returns a backup of terminal settings for stdin/out/err.</td>
241 </tr>
242 <tr>
243 <td class="name" nowrap><a href="#termrestore">termrestore (backup)</a></td>
244 <td class="summary">Restores terminal settings from a backup</td>
245 </tr>
246 <tr>
247 <td class="name" nowrap><a href="#termwrap">termwrap (f)</a></td>
248 <td class="summary">Wraps a function to automatically restore terminal settings upon returning.</td>
249 </tr>
250</table>
227 251
228<br/> 252<br/>
229<br/> 253<br/>
@@ -399,95 +423,66 @@ requested number of bytes, or an error, never a partial result.
399 </div> 423 </div>
400 <dl class="function"> 424 <dl class="function">
401 <dt> 425 <dt>
402 <a name = "CODEPAGE_UTF8"></a> 426 <a name = "isatty"></a>
403 <strong>CODEPAGE_UTF8</strong> 427 <strong>isatty (file)</strong>
404 </dt> 428 </dt>
405 <dd> 429 <dd>
406 UTF8 codepage. 430 Checks if a file-handle is a TTY.
407 To be used with <a href="../modules/system.html#setconsoleoutputcp">system.setconsoleoutputcp</a> and <a href="../modules/system.html#setconsolecp">system.setconsolecp</a>.
408 431
409 432
433 <h3>Parameters:</h3>
410 <ul> 434 <ul>
411 <li><span class="parameter">CODEPAGE_UTF8</span> 435 <li><span class="parameter">file</span>
412 The Windows CodePage for UTF8. 436 <span class="types"><span class="type">file</span></span>
437 the file-handle to check, one of <a href="https://www.lua.org/manual/5.4/manual.html#pdf-io.stdin">io.stdin</a>, <a href="https://www.lua.org/manual/5.4/manual.html#pdf-io.stdout">io.stdout</a>, <a href="https://www.lua.org/manual/5.4/manual.html#pdf-io.stderr">io.stderr</a>.
413 </li> 438 </li>
414 </ul> 439 </ul>
415 440
416
417
418
419
420</dd>
421 <dt>
422 <a name = "_readkey"></a>
423 <strong>_readkey ()</strong>
424 </dt>
425 <dd>
426 Reads a key from the console non-blocking. This function should not be called
427directly, but through the <a href="../modules/system.html#readkey">system.readkey</a> or <a href="../modules/system.html#readansi">system.readansi</a> functions. It
428will return the next byte from the input stream, or <code>nil</code> if no key was pressed.</p>
429
430<p>On Posix, <a href="https://www.lua.org/manual/5.4/manual.html#pdf-io.stdin">io.stdin</a> must be set to non-blocking mode using <a href="../modules/system.html#setnonblock">setnonblock</a>
431and canonical mode must be turned off using <a href="../modules/system.html#tcsetattr">tcsetattr</a>,
432before calling this function. Otherwise it will block. No conversions are
433done on Posix, so the byte read is returned as-is.</p>
434
435<p>On Windows this reads a wide character and converts it to UTF-8. Multi-byte
436sequences will be buffered internally and returned one byte at a time.
437
438
439
440 <h3>Returns:</h3> 441 <h3>Returns:</h3>
441 <ol> 442 <ol>
442 443
443 <span class="types"><span class="type">integer</span></span> 444 <span class="types"><span class="type">boolean</span></span>
444 the byte read from the input stream 445 true if the file is a tty
445 </ol>
446 <h3>Or</h3>
447 <ol>
448
449 <span class="types"><span class="type">nil</span></span>
450 if no key was pressed
451 </ol>
452 <h3>Or</h3>
453 <ol>
454 <li>
455 <span class="types"><span class="type">nil</span></span>
456 on error</li>
457 <li>
458 <span class="types"><a class="type" href="https://www.lua.org/manual/5.4/manual.html#6.4">string</a></span>
459 error message</li>
460 <li>
461 <span class="types"><span class="type">int</span></span>
462 errnum (on posix)</li>
463 </ol> 446 </ol>
464 447
465 448
466 449
450 <h3>Usage:</h3>
451 <ul>
452 <pre class="example"><span class="keyword">local</span> system = <span class="global">require</span>(<span class="string">'system'</span>)
453<span class="keyword">if</span> system.<span class="function-name">isatty</span>(<span class="global">io</span>.stdin) <span class="keyword">then</span>
454 <span class="comment">-- enable ANSI coloring etc on Windows, does nothing in Posix.
455</span> <span class="keyword">local</span> flags = system.<span class="function-name">getconsoleflags</span>(<span class="global">io</span>.stdout)
456 system.<span class="function-name">setconsoleflags</span>(<span class="global">io</span>.stdout, flags + sys.COF_VIRTUAL_TERMINAL_PROCESSING)
457<span class="keyword">end</span></pre>
458 </ul>
467 459
468</dd> 460</dd>
469 <dt> 461 <dt>
470 <a name = "autotermrestore"></a> 462 <a name = "termsize"></a>
471 <strong>autotermrestore ()</strong> 463 <strong>termsize ()</strong>
472 </dt> 464 </dt>
473 <dd> 465 <dd>
474 Backs up terminal settings and restores them on application exit. 466 Get the size of the terminal in rows and columns.
475 Calls <a href="../modules/system.html#termbackup">termbackup</a> to back up terminal settings and sets up a GC method to
476 automatically restore them on application exit (also works on Lua 5.1).
477 467
478 468
479 469
480 <h3>Returns:</h3> 470 <h3>Returns:</h3>
481 <ol> 471 <ol>
482 472 <li>
483 <span class="types"><span class="type">boolean</span></span> 473 <span class="types"><span class="type">int</span></span>
484 true 474 the number of rows</li>
475 <li>
476 <span class="types"><span class="type">int</span></span>
477 the number of columns</li>
485 </ol> 478 </ol>
486 <h3>Or</h3> 479 <h3>Or</h3>
487 <ol> 480 <ol>
488 <li> 481 <li>
489 <span class="types"><span class="type">nil</span></span> 482 <span class="types"><span class="type">nil</span></span>
490 if the backup was already created</li> 483
484
485</li>
491 <li> 486 <li>
492 <span class="types"><a class="type" href="https://www.lua.org/manual/5.4/manual.html#6.4">string</a></span> 487 <span class="types"><a class="type" href="https://www.lua.org/manual/5.4/manual.html#6.4">string</a></span>
493 error message</li> 488 error message</li>
@@ -497,26 +492,10 @@ sequences will be buffered internally and returned one byte at a time.
497 492
498 493
499</dd> 494</dd>
500 <dt> 495</dl>
501 <a name = "getconsolecp"></a> 496 <h2 class="section-header "><a name="Terminal_Windows"></a>Terminal_Windows</h2>
502 <strong>getconsolecp ()</strong>
503 </dt>
504 <dd>
505 Gets the current console code page (Windows).
506
507
508
509 <h3>Returns:</h3>
510 <ol>
511
512 <span class="types"><span class="type">int</span></span>
513 the current code page (always 65001 on Posix systems)
514 </ol>
515
516
517
518 497
519</dd> 498 <dl class="function">
520 <dt> 499 <dt>
521 <a name = "getconsoleflags"></a> 500 <a name = "getconsoleflags"></a>
522 <strong>getconsoleflags (file)</strong> 501 <strong>getconsoleflags (file)</strong>
@@ -575,103 +554,6 @@ for more information on the flags.
575 554
576</dd> 555</dd>
577 <dt> 556 <dt>
578 <a name = "getconsoleoutputcp"></a>
579 <strong>getconsoleoutputcp ()</strong>
580 </dt>
581 <dd>
582 Gets the current console output code page (Windows).
583
584
585
586 <h3>Returns:</h3>
587 <ol>
588
589 <span class="types"><span class="type">int</span></span>
590 the current code page (always 65001 on Posix systems)
591 </ol>
592
593
594
595
596</dd>
597 <dt>
598 <a name = "getnonblock"></a>
599 <strong>getnonblock (fd)</strong>
600 </dt>
601 <dd>
602 Gets non-blocking mode status for a file (Posix).
603
604
605 <h3>Parameters:</h3>
606 <ul>
607 <li><span class="parameter">fd</span>
608 <span class="types"><span class="type">file</span></span>
609 file handle to operate on, one of <a href="https://www.lua.org/manual/5.4/manual.html#pdf-io.stdin">io.stdin</a>, <a href="https://www.lua.org/manual/5.4/manual.html#pdf-io.stdout">io.stdout</a>, <a href="https://www.lua.org/manual/5.4/manual.html#pdf-io.stderr">io.stderr</a>
610 </li>
611 </ul>
612
613 <h3>Returns:</h3>
614 <ol>
615
616 <span class="types"><span class="type">bool</span></span>
617 <code>true</code> if set to non-blocking, <code>false</code> if not. Always returns <code>false</code> on Windows.
618 </ol>
619 <h3>Or</h3>
620 <ol>
621 <li>
622 <span class="types"><span class="type">nil</span></span>
623
624
625</li>
626 <li>
627 <span class="types"><a class="type" href="https://www.lua.org/manual/5.4/manual.html#6.4">string</a></span>
628 error message</li>
629 <li>
630 <span class="types"><span class="type">int</span></span>
631 errnum</li>
632 </ol>
633
634
635
636
637</dd>
638 <dt>
639 <a name = "isatty"></a>
640 <strong>isatty (file)</strong>
641 </dt>
642 <dd>
643 Checks if a file-handle is a TTY.
644
645
646 <h3>Parameters:</h3>
647 <ul>
648 <li><span class="parameter">file</span>
649 <span class="types"><span class="type">file</span></span>
650 the file-handle to check, one of <a href="https://www.lua.org/manual/5.4/manual.html#pdf-io.stdin">io.stdin</a>, <a href="https://www.lua.org/manual/5.4/manual.html#pdf-io.stdout">io.stdout</a>, <a href="https://www.lua.org/manual/5.4/manual.html#pdf-io.stderr">io.stderr</a>.
651 </li>
652 </ul>
653
654 <h3>Returns:</h3>
655 <ol>
656
657 <span class="types"><span class="type">boolean</span></span>
658 true if the file is a tty
659 </ol>
660
661
662
663 <h3>Usage:</h3>
664 <ul>
665 <pre class="example"><span class="keyword">local</span> system = <span class="global">require</span>(<span class="string">'system'</span>)
666<span class="keyword">if</span> system.<span class="function-name">isatty</span>(<span class="global">io</span>.stdin) <span class="keyword">then</span>
667 <span class="comment">-- enable ANSI coloring etc on Windows, does nothing in Posix.
668</span> <span class="keyword">local</span> flags = system.<span class="function-name">getconsoleflags</span>(<span class="global">io</span>.stdout)
669 system.<span class="function-name">setconsoleflags</span>(<span class="global">io</span>.stdout, flags + sys.COF_VIRTUAL_TERMINAL_PROCESSING)
670<span class="keyword">end</span></pre>
671 </ul>
672
673</dd>
674 <dt>
675 <a name = "listconsoleflags"></a> 557 <a name = "listconsoleflags"></a>
676 <strong>listconsoleflags (fh)</strong> 558 <strong>listconsoleflags (fh)</strong>
677 </dt> 559 </dt>
@@ -700,192 +582,125 @@ system.<span class="function-name">listconsoleflags</span>(<span class="global">
700 582
701</dd> 583</dd>
702 <dt> 584 <dt>
703 <a name = "listtermflags"></a> 585 <a name = "setconsoleflags"></a>
704 <strong>listtermflags (fh)</strong> 586 <strong>setconsoleflags (file, bitflags)</strong>
705 </dt> 587 </dt>
706 <dd> 588 <dd>
707 Debug function for terminal flags (Posix). 589 Sets the console flags (Windows).
708 Pretty prints the current flags set for the handle. 590The <code>CIF_</code> and <code>COF_</code> constants are available on the module table. Where <code>CIF</code> are the
709 591input flags (for use with <a href="https://www.lua.org/manual/5.4/manual.html#pdf-io.stdin">io.stdin</a>) and <code>COF</code> are the output flags (for use with
710 592<a href="https://www.lua.org/manual/5.4/manual.html#pdf-io.stdout">io.stdout</a>/<a href="https://www.lua.org/manual/5.4/manual.html#pdf-io.stderr">io.stderr</a>).</p>
711 <h3>Parameters:</h3>
712 <ul>
713 <li><span class="parameter">fh</span>
714 file handle (<a href="https://www.lua.org/manual/5.4/manual.html#pdf-io.stdin">io.stdin</a>, <a href="https://www.lua.org/manual/5.4/manual.html#pdf-io.stdout">io.stdout</a>, <a href="https://www.lua.org/manual/5.4/manual.html#pdf-io.stderr">io.stderr</a>)
715 </li>
716 </ul>
717
718
719
720 593
721 <h3>Usage:</h3> 594<p>To see flag status and constant names check <a href="../modules/system.html#listconsoleflags">listconsoleflags</a>.</p>
722 <ul>
723 <pre class="example"><span class="comment">-- Print the flags for stdin/out/err
724</span>system.<span class="function-name">listconsoleflags</span>(<span class="global">io</span>.stdin)
725system.<span class="function-name">listconsoleflags</span>(<span class="global">io</span>.stdout)
726system.<span class="function-name">listconsoleflags</span>(<span class="global">io</span>.stderr)</pre>
727 </ul>
728 595
729</dd> 596<p>Note: not all combinations of flags are allowed, as some are mutually exclusive or mutually required.
730 <dt> 597See <a href="https://learn.microsoft.com/en-us/windows/console/setconsolemode">setconsolemode documentation</a>
731 <a name = "readansi"></a>
732 <strong>readansi (timeout[, fsleep=system.sleep])</strong>
733 </dt>
734 <dd>
735 Reads a single key, if it is the start of ansi escape sequence then it reads
736 the full sequence. The key can be a multi-byte string in case of multibyte UTF-8 character.
737 This function uses <a href="../modules/system.html#readkey">system.readkey</a>, and hence <code>fsleep</code> to wait until either a key is
738 available or the timeout is reached.
739 It returns immediately if a key is available or if <code>timeout</code> is less than or equal to <code>0</code>.
740 In case of an ANSI sequence, it will return the full sequence as a string.
741 598
742 599
743 <h3>Parameters:</h3> 600 <h3>Parameters:</h3>
744 <ul> 601 <ul>
745 <li><span class="parameter">timeout</span> 602 <li><span class="parameter">file</span>
746 <span class="types"><span class="type">number</span></span> 603 <span class="types"><span class="type">file</span></span>
747 the timeout in seconds. 604 file handle to operate on, one of <a href="https://www.lua.org/manual/5.4/manual.html#pdf-io.stdin">io.stdin</a>, <a href="https://www.lua.org/manual/5.4/manual.html#pdf-io.stdout">io.stdout</a>, <a href="https://www.lua.org/manual/5.4/manual.html#pdf-io.stderr">io.stderr</a>
748 </li> 605 </li>
749 <li><span class="parameter">fsleep</span> 606 <li><span class="parameter">bitflags</span>
750 <span class="types"><span class="type">function</span></span> 607 <span class="types"><a class="type" href="../classes/bitflags.html#">bitflags</a></span>
751 the function to call for sleeping. 608 the flags to set/unset
752 (<em>default</em> system.sleep)
753 </li> 609 </li>
754 </ul> 610 </ul>
755 611
756 <h3>Returns:</h3> 612 <h3>Returns:</h3>
757 <ol> 613 <ol>
758 <li> 614
759 <span class="types"><a class="type" href="https://www.lua.org/manual/5.4/manual.html#6.4">string</a></span> 615 <span class="types"><span class="type">boolean</span></span>
760 the character that was received (can be multi-byte), or a complete ANSI sequence</li> 616 <code>true</code> on success
761 <li>
762 <span class="types"><a class="type" href="https://www.lua.org/manual/5.4/manual.html#6.4">string</a></span>
763 the type of input: <code>&quot;char&quot;</code> for a single key, <code>&quot;ansi&quot;</code> for an ANSI sequence</li>
764 </ol> 617 </ol>
765 <h3>Or</h3> 618 <h3>Or</h3>
766 <ol> 619 <ol>
767 <li> 620 <li>
768 <span class="types"><span class="type">nil</span></span> 621 <span class="types"><span class="type">nil</span></span>
769 in case of an error</li> 622
770 <li> 623
771 <span class="types"><a class="type" href="https://www.lua.org/manual/5.4/manual.html#6.4">string</a></span> 624</li>
772 error message; <code>&quot;timeout&quot;</code> if the timeout was reached.</li>
773 <li> 625 <li>
774 <span class="types"><a class="type" href="https://www.lua.org/manual/5.4/manual.html#6.4">string</a></span> 626 <span class="types"><a class="type" href="https://www.lua.org/manual/5.4/manual.html#6.4">string</a></span>
775 partial result in case of an error while reading a sequence, the sequence so far.</li> 627 error message</li>
776 </ol> 628 </ol>
777 629
778 630
779 631
632 <h3>Usage:</h3>
633 <ul>
634 <pre class="example"><span class="keyword">local</span> system = <span class="global">require</span>(<span class="string">'system'</span>)
635system.<span class="function-name">listconsoleflags</span>(<span class="global">io</span>.stdout) <span class="comment">-- List all the available flags and their current status
636</span>
637<span class="keyword">local</span> flags = system.<span class="function-name">getconsoleflags</span>(<span class="global">io</span>.stdout)
638<span class="global">assert</span>(system.<span class="function-name">setconsoleflags</span>(<span class="global">io</span>.stdout,
639 flags + system.COF_VIRTUAL_TERMINAL_PROCESSING)
640
641system.<span class="function-name">listconsoleflags</span>(<span class="global">io</span>.stdout) <span class="comment">-- List again to check the differences</span></pre>
642 </ul>
780 643
781</dd> 644</dd>
645</dl>
646 <h2 class="section-header "><a name="Terminal_Posix"></a>Terminal_Posix</h2>
647
648 <dl class="function">
782 <dt> 649 <dt>
783 <a name = "readkey"></a> 650 <a name = "detachfds"></a>
784 <strong>readkey (timeout[, fsleep=system.sleep])</strong> 651 <strong>detachfds ()</strong>
785 </dt> 652 </dt>
786 <dd> 653 <dd>
787 Reads a single byte from the console, with a timeout. 654 Creates new file descriptions for <code>stdout</code> and <code>stderr</code>.
788 This function uses <code>fsleep</code> to wait until either a byte is available or the timeout is reached. 655Even if the file descriptors are unique, they still might point to the same
789 The sleep period is exponentially backing off, starting at 0.0125 seconds, with a maximum of 0.2 seconds. 656file description, and hence share settings like <code>O_NONBLOCK</code>. This means that
790 It returns immediately if a byte is available or if <code>timeout</code> is less than or equal to <code>0</code>.</p> 657if one of them is set to non-blocking, the other will be as well. This can
658lead to unexpected behavior.</p>
791 659
792<p> Using <a href="../modules/system.html#readansi">system.readansi</a> is preferred over this function. Since this function can leave stray/invalid 660<p>This function is used to detach <code>stdout</code> and <code>stderr</code> from the original
793 byte-sequences in the input buffer, while <a href="../modules/system.html#readansi">system.readansi</a> reads full ANSI and UTF8 sequences. 661file descriptions, and create new file descriptions for them. This allows
662independent control of flags (e.g., <code>O_NONBLOCK</code>) on <code>stdout</code> and <code>stderr</code>,
663avoiding shared side effects.</p>
664
665<p>Does not modify <code>stdin</code> (fd 0), and does nothing on Windows.
794 666
795 667
796 <h3>Parameters:</h3>
797 <ul>
798 <li><span class="parameter">timeout</span>
799 <span class="types"><span class="type">number</span></span>
800 the timeout in seconds.
801 </li>
802 <li><span class="parameter">fsleep</span>
803 <span class="types"><span class="type">function</span></span>
804 the function to call for sleeping.
805 (<em>default</em> system.sleep)
806 </li>
807 </ul>
808 668
809 <h3>Returns:</h3> 669 <h3>Returns:</h3>
810 <ol> 670 <ol>
811 671
812 <span class="types"><span class="type">byte</span></span> 672 boolean <code>true</code> on success, or throws an error on failure.
813 the byte value that was read.
814 </ol> 673 </ol>
815 <h3>Or</h3>
816 <ol>
817 <li>
818 <span class="types"><span class="type">nil</span></span>
819 if no key was read</li>
820 <li>
821 <span class="types"><a class="type" href="https://www.lua.org/manual/5.4/manual.html#6.4">string</a></span>
822 error message; <code>&quot;timeout&quot;</code> if the timeout was reached.</li>
823 </ol>
824
825 674
826 675
827 676 <h3>See also:</h3>
828</dd>
829 <dt>
830 <a name = "setconsolecp"></a>
831 <strong>setconsolecp (cp)</strong>
832 </dt>
833 <dd>
834 Sets the current console code page (Windows).
835
836
837 <h3>Parameters:</h3>
838 <ul> 677 <ul>
839 <li><span class="parameter">cp</span> 678 <a href="../modules/system.html#setnonblock">setnonblock</a>
840 <span class="types"><span class="type">int</span></span>
841 the code page to set, use <a href="../modules/system.html#CODEPAGE_UTF8">system.CODEPAGE_UTF8</a> (65001) for UTF-8
842 </li>
843 </ul> 679 </ul>
844 680
845 <h3>Returns:</h3>
846 <ol>
847
848 <span class="types"><span class="type">bool</span></span>
849 <code>true</code> on success (always <code>true</code> on Posix systems)
850 </ol>
851
852
853
854 681
855</dd> 682</dd>
856 <dt> 683 <dt>
857 <a name = "setconsoleflags"></a> 684 <a name = "getnonblock"></a>
858 <strong>setconsoleflags (file, bitflags)</strong> 685 <strong>getnonblock (fd)</strong>
859 </dt> 686 </dt>
860 <dd> 687 <dd>
861 Sets the console flags (Windows). 688 Gets non-blocking mode status for a file (Posix).
862The <code>CIF_</code> and <code>COF_</code> constants are available on the module table. Where <code>CIF</code> are the
863input flags (for use with <a href="https://www.lua.org/manual/5.4/manual.html#pdf-io.stdin">io.stdin</a>) and <code>COF</code> are the output flags (for use with
864<a href="https://www.lua.org/manual/5.4/manual.html#pdf-io.stdout">io.stdout</a>/<a href="https://www.lua.org/manual/5.4/manual.html#pdf-io.stderr">io.stderr</a>).</p>
865
866<p>To see flag status and constant names check <a href="../modules/system.html#listconsoleflags">listconsoleflags</a>.</p>
867
868<p>Note: not all combinations of flags are allowed, as some are mutually exclusive or mutually required.
869See <a href="https://learn.microsoft.com/en-us/windows/console/setconsolemode">setconsolemode documentation</a>
870 689
871 690
872 <h3>Parameters:</h3> 691 <h3>Parameters:</h3>
873 <ul> 692 <ul>
874 <li><span class="parameter">file</span> 693 <li><span class="parameter">fd</span>
875 <span class="types"><span class="type">file</span></span> 694 <span class="types"><span class="type">file</span></span>
876 file handle to operate on, one of <a href="https://www.lua.org/manual/5.4/manual.html#pdf-io.stdin">io.stdin</a>, <a href="https://www.lua.org/manual/5.4/manual.html#pdf-io.stdout">io.stdout</a>, <a href="https://www.lua.org/manual/5.4/manual.html#pdf-io.stderr">io.stderr</a> 695 file handle to operate on, one of <a href="https://www.lua.org/manual/5.4/manual.html#pdf-io.stdin">io.stdin</a>, <a href="https://www.lua.org/manual/5.4/manual.html#pdf-io.stdout">io.stdout</a>, <a href="https://www.lua.org/manual/5.4/manual.html#pdf-io.stderr">io.stderr</a>
877 </li> 696 </li>
878 <li><span class="parameter">bitflags</span>
879 <span class="types"><a class="type" href="../classes/bitflags.html#">bitflags</a></span>
880 the flags to set/unset
881 </li>
882 </ul> 697 </ul>
883 698
884 <h3>Returns:</h3> 699 <h3>Returns:</h3>
885 <ol> 700 <ol>
886 701
887 <span class="types"><span class="type">boolean</span></span> 702 <span class="types"><span class="type">bool</span></span>
888 <code>true</code> on success 703 <code>true</code> if set to non-blocking, <code>false</code> if not. Always returns <code>false</code> on Windows.
889 </ol> 704 </ol>
890 <h3>Or</h3> 705 <h3>Or</h3>
891 <ol> 706 <ol>
@@ -897,48 +712,45 @@ See <a href="https://learn.microsoft.com/en-us/windows/console/setconsolemode">s
897 <li> 712 <li>
898 <span class="types"><a class="type" href="https://www.lua.org/manual/5.4/manual.html#6.4">string</a></span> 713 <span class="types"><a class="type" href="https://www.lua.org/manual/5.4/manual.html#6.4">string</a></span>
899 error message</li> 714 error message</li>
715 <li>
716 <span class="types"><span class="type">int</span></span>
717 errnum</li>
900 </ol> 718 </ol>
901 719
902 720
903 721 <h3>See also:</h3>
904 <h3>Usage:</h3>
905 <ul> 722 <ul>
906 <pre class="example"><span class="keyword">local</span> system = <span class="global">require</span>(<span class="string">'system'</span>) 723 <a href="../modules/system.html#setnonblock">setnonblock</a>
907system.<span class="function-name">listconsoleflags</span>(<span class="global">io</span>.stdout) <span class="comment">-- List all the available flags and their current status
908</span>
909<span class="keyword">local</span> flags = system.<span class="function-name">getconsoleflags</span>(<span class="global">io</span>.stdout)
910<span class="global">assert</span>(system.<span class="function-name">setconsoleflags</span>(<span class="global">io</span>.stdout,
911 flags + system.COF_VIRTUAL_TERMINAL_PROCESSING)
912
913system.<span class="function-name">listconsoleflags</span>(<span class="global">io</span>.stdout) <span class="comment">-- List again to check the differences</span></pre>
914 </ul> 724 </ul>
915 725
726
916</dd> 727</dd>
917 <dt> 728 <dt>
918 <a name = "setconsoleoutputcp"></a> 729 <a name = "listtermflags"></a>
919 <strong>setconsoleoutputcp (cp)</strong> 730 <strong>listtermflags (fh)</strong>
920 </dt> 731 </dt>
921 <dd> 732 <dd>
922 Sets the current console output code page (Windows). 733 Debug function for terminal flags (Posix).
734 Pretty prints the current flags set for the handle.
923 735
924 736
925 <h3>Parameters:</h3> 737 <h3>Parameters:</h3>
926 <ul> 738 <ul>
927 <li><span class="parameter">cp</span> 739 <li><span class="parameter">fh</span>
928 <span class="types"><span class="type">int</span></span> 740 file handle (<a href="https://www.lua.org/manual/5.4/manual.html#pdf-io.stdin">io.stdin</a>, <a href="https://www.lua.org/manual/5.4/manual.html#pdf-io.stdout">io.stdout</a>, <a href="https://www.lua.org/manual/5.4/manual.html#pdf-io.stderr">io.stderr</a>)
929 the code page to set, use <a href="../modules/system.html#CODEPAGE_UTF8">system.CODEPAGE_UTF8</a> (65001) for UTF-8
930 </li> 741 </li>
931 </ul> 742 </ul>
932 743
933 <h3>Returns:</h3>
934 <ol>
935
936 <span class="types"><span class="type">bool</span></span>
937 <code>true</code> on success (always <code>true</code> on Posix systems)
938 </ol>
939 744
940 745
941 746
747 <h3>Usage:</h3>
748 <ul>
749 <pre class="example"><span class="comment">-- Print the flags for stdin/out/err
750</span>system.<span class="function-name">listconsoleflags</span>(<span class="global">io</span>.stdin)
751system.<span class="function-name">listconsoleflags</span>(<span class="global">io</span>.stdout)
752system.<span class="function-name">listconsoleflags</span>(<span class="global">io</span>.stderr)</pre>
753 </ul>
942 754
943</dd> 755</dd>
944 <dt> 756 <dt>
@@ -947,6 +759,7 @@ system.<span class="function-name">listconsoleflags</span>(<span class="global">
947 </dt> 759 </dt>
948 <dd> 760 <dd>
949 Enables or disables non-blocking mode for a file (Posix). 761 Enables or disables non-blocking mode for a file (Posix).
762Check <a href="../modules/system.html#detachfds">detachfds</a> in case there are shared file descriptions.
950 763
951 764
952 <h3>Parameters:</h3> 765 <h3>Parameters:</h3>
@@ -985,13 +798,15 @@ system.<span class="function-name">listconsoleflags</span>(<span class="global">
985 798
986 <h3>See also:</h3> 799 <h3>See also:</h3>
987 <ul> 800 <ul>
988 <a href="../modules/system.html#getnonblock">getnonblock</a> 801 <li><a href="../modules/system.html#getnonblock">getnonblock</a></li>
802 <li><a href="../modules/system.html#detachfds">detachfds</a></li>
989 </ul> 803 </ul>
990 804
991 <h3>Usage:</h3> 805 <h3>Usage:</h3>
992 <ul> 806 <ul>
993 <pre class="example"><span class="keyword">local</span> sys = <span class="global">require</span>(<span class="string">'system'</span>) 807 <pre class="example"><span class="keyword">local</span> sys = <span class="global">require</span>(<span class="string">'system'</span>)
994 808sys.<span class="function-name">detachfds</span>() <span class="comment">-- detach stdout and stderr, so only stdin becomes non-blocking
809</span>
995<span class="comment">-- set io.stdin to non-blocking mode 810<span class="comment">-- set io.stdin to non-blocking mode
996</span><span class="keyword">local</span> old_setting = sys.<span class="function-name">getnonblock</span>(<span class="global">io</span>.stdin) 811</span><span class="keyword">local</span> old_setting = sys.<span class="function-name">getnonblock</span>(<span class="global">io</span>.stdin)
997sys.<span class="function-name">setnonblock</span>(<span class="global">io</span>.stdin, <span class="keyword">true</span>) 812sys.<span class="function-name">setnonblock</span>(<span class="global">io</span>.stdin, <span class="keyword">true</span>)
@@ -1151,21 +966,52 @@ flags for the <code>iflags</code>, <code>oflags</code>, and <code>lflags</code>
1151 </ul> 966 </ul>
1152 967
1153</dd> 968</dd>
969</dl>
970 <h2 class="section-header "><a name="Terminal_Input"></a>Terminal_Input</h2>
971
972 <dl class="function">
1154 <dt> 973 <dt>
1155 <a name = "termbackup"></a> 974 <a name = "_readkey"></a>
1156 <strong>termbackup ()</strong> 975 <strong>_readkey ()</strong>
1157 </dt> 976 </dt>
1158 <dd> 977 <dd>
1159 Returns a backup of terminal settings for stdin/out/err. 978 Reads a key from the console non-blocking. This function should not be called
1160 Handles terminal/console flags, Windows codepage, and non-block flags on the streams. 979directly, but through the <a href="../modules/system.html#readkey">system.readkey</a> or <a href="../modules/system.html#readansi">system.readansi</a> functions. It
1161 Backs up terminal/console flags only if a stream is a tty. 980will return the next byte from the input stream, or <code>nil</code> if no key was pressed.</p>
981
982<p>On Posix, <a href="https://www.lua.org/manual/5.4/manual.html#pdf-io.stdin">io.stdin</a> must be set to non-blocking mode using <a href="../modules/system.html#setnonblock">setnonblock</a>
983and canonical mode must be turned off using <a href="../modules/system.html#tcsetattr">tcsetattr</a>,
984before calling this function. Otherwise it will block. No conversions are
985done on Posix, so the byte read is returned as-is.</p>
986
987<p>On Windows this reads a wide character and converts it to UTF-8. Multi-byte
988sequences will be buffered internally and returned one byte at a time.
1162 989
1163 990
1164 991
1165 <h3>Returns:</h3> 992 <h3>Returns:</h3>
1166 <ol> 993 <ol>
1167 994
1168 table with backup of terminal settings 995 <span class="types"><span class="type">integer</span></span>
996 the byte read from the input stream
997 </ol>
998 <h3>Or</h3>
999 <ol>
1000
1001 <span class="types"><span class="type">nil</span></span>
1002 if no key was pressed
1003 </ol>
1004 <h3>Or</h3>
1005 <ol>
1006 <li>
1007 <span class="types"><span class="type">nil</span></span>
1008 on error</li>
1009 <li>
1010 <span class="types"><a class="type" href="https://www.lua.org/manual/5.4/manual.html#6.4">string</a></span>
1011 error message</li>
1012 <li>
1013 <span class="types"><span class="type">int</span></span>
1014 errnum (on posix)</li>
1169 </ol> 1015 </ol>
1170 1016
1171 1017
@@ -1173,60 +1019,153 @@ flags for the <code>iflags</code>, <code>oflags</code>, and <code>lflags</code>
1173 1019
1174</dd> 1020</dd>
1175 <dt> 1021 <dt>
1176 <a name = "termrestore"></a> 1022 <a name = "readansi"></a>
1177 <strong>termrestore (backup)</strong> 1023 <strong>readansi (timeout[, fsleep=system.sleep])</strong>
1178 </dt> 1024 </dt>
1179 <dd> 1025 <dd>
1180 Restores terminal settings from a backup 1026 Reads a single key, if it is the start of ansi escape sequence then it reads
1027 the full sequence. The key can be a multi-byte string in case of multibyte UTF-8 character.
1028 This function uses <a href="../modules/system.html#readkey">system.readkey</a>, and hence <code>fsleep</code> to wait until either a key is
1029 available or the timeout is reached.
1030 It returns immediately if a key is available or if <code>timeout</code> is less than or equal to <code>0</code>.
1031 In case of an ANSI sequence, it will return the full sequence as a string.
1181 1032
1182 1033
1183 <h3>Parameters:</h3> 1034 <h3>Parameters:</h3>
1184 <ul> 1035 <ul>
1185 <li><span class="parameter">backup</span> 1036 <li><span class="parameter">timeout</span>
1186 <span class="types"><a class="type" href="https://www.lua.org/manual/5.4/manual.html#6.6">table</a></span> 1037 <span class="types"><span class="type">number</span></span>
1187 the backup of terminal settings, see <a href="../modules/system.html#termbackup">termbackup</a>. 1038 the timeout in seconds.
1039 </li>
1040 <li><span class="parameter">fsleep</span>
1041 <span class="types"><span class="type">function</span></span>
1042 the function to call for sleeping.
1043 (<em>default</em> system.sleep)
1188 </li> 1044 </li>
1189 </ul> 1045 </ul>
1190 1046
1191 <h3>Returns:</h3> 1047 <h3>Returns:</h3>
1192 <ol> 1048 <ol>
1193 1049 <li>
1194 <span class="types"><span class="type">boolean</span></span> 1050 <span class="types"><a class="type" href="https://www.lua.org/manual/5.4/manual.html#6.4">string</a></span>
1195 true 1051 the character that was received (can be multi-byte), or a complete ANSI sequence</li>
1052 <li>
1053 <span class="types"><a class="type" href="https://www.lua.org/manual/5.4/manual.html#6.4">string</a></span>
1054 the type of input: <code>&quot;ctrl&quot;</code> for 0-31 and 127 bytes, <code>&quot;char&quot;</code> for other UTF-8 characters, <code>&quot;ansi&quot;</code> for an ANSI sequence</li>
1055 </ol>
1056 <h3>Or</h3>
1057 <ol>
1058 <li>
1059 <span class="types"><span class="type">nil</span></span>
1060 in case of an error</li>
1061 <li>
1062 <span class="types"><a class="type" href="https://www.lua.org/manual/5.4/manual.html#6.4">string</a></span>
1063 error message; <code>&quot;timeout&quot;</code> if the timeout was reached.</li>
1064 <li>
1065 <span class="types"><a class="type" href="https://www.lua.org/manual/5.4/manual.html#6.4">string</a></span>
1066 partial result in case of an error while reading a sequence, the sequence so far.
1067 The function retains its own internal buffer, so on the next call the incomplete buffer is used to
1068 complete the sequence.</li>
1196 </ol> 1069 </ol>
1197 1070
1198 1071
1199 1072
1073 <h3>Usage:</h3>
1074 <ul>
1075 <pre class="example"><span class="keyword">local</span> key, keytype = system.<span class="function-name">readansi</span>(<span class="number">5</span>)
1076<span class="keyword">if</span> keytype == <span class="string">"char"</span> <span class="keyword">then</span> ... <span class="keyword">end</span> <span class="comment">-- printable character
1077</span><span class="keyword">if</span> keytype ~= <span class="string">"char"</span> <span class="keyword">then</span> ... <span class="keyword">end</span> <span class="comment">-- non-printable character or sequence
1078</span><span class="keyword">if</span> keytype == <span class="string">"ansi"</span> <span class="keyword">then</span> ... <span class="keyword">end</span> <span class="comment">-- a multi-byte sequence, but not a UTF8 character
1079</span><span class="keyword">if</span> keytype ~= <span class="string">"ansi"</span> <span class="keyword">then</span> ... <span class="keyword">end</span> <span class="comment">-- a valid UTF8 character (which includes control characters)
1080</span><span class="keyword">if</span> keytype == <span class="string">"ctrl"</span> <span class="keyword">then</span> ... <span class="keyword">end</span> <span class="comment">-- a single-byte ctrl character (0-31, 127)</span></pre>
1081 </ul>
1200 1082
1201</dd> 1083</dd>
1202 <dt> 1084 <dt>
1203 <a name = "termsize"></a> 1085 <a name = "readkey"></a>
1204 <strong>termsize ()</strong> 1086 <strong>readkey (timeout[, fsleep=system.sleep])</strong>
1205 </dt> 1087 </dt>
1206 <dd> 1088 <dd>
1207 Get the size of the terminal in rows and columns. 1089 Reads a single byte from the console, with a timeout.
1090 This function uses <code>fsleep</code> to wait until either a byte is available or the timeout is reached.
1091 The sleep period is exponentially backing off, starting at 0.0125 seconds, with a maximum of 0.1 seconds.
1092 It returns immediately if a byte is available or if <code>timeout</code> is less than or equal to <code>0</code>.</p>
1208 1093
1094<p> Using <a href="../modules/system.html#readansi">system.readansi</a> is preferred over this function. Since this function can leave stray/invalid
1095 byte-sequences in the input buffer, while <a href="../modules/system.html#readansi">system.readansi</a> reads full ANSI and UTF8 sequences.
1209 1096
1210 1097
1098 <h3>Parameters:</h3>
1099 <ul>
1100 <li><span class="parameter">timeout</span>
1101 <span class="types"><span class="type">number</span></span>
1102 the timeout in seconds.
1103 </li>
1104 <li><span class="parameter">fsleep</span>
1105 <span class="types"><span class="type">function</span></span>
1106 the function to call for sleeping; <code>ok, err = fsleep(secs)</code>
1107 (<em>default</em> system.sleep)
1108 </li>
1109 </ul>
1110
1211 <h3>Returns:</h3> 1111 <h3>Returns:</h3>
1212 <ol> 1112 <ol>
1213 <li> 1113
1214 <span class="types"><span class="type">int</span></span> 1114 <span class="types"><span class="type">byte</span></span>
1215 the number of rows</li> 1115 the byte value that was read.
1216 <li>
1217 <span class="types"><span class="type">int</span></span>
1218 the number of columns</li>
1219 </ol> 1116 </ol>
1220 <h3>Or</h3> 1117 <h3>Or</h3>
1221 <ol> 1118 <ol>
1222 <li> 1119 <li>
1223 <span class="types"><span class="type">nil</span></span> 1120 <span class="types"><span class="type">nil</span></span>
1224 1121 if no key was read</li>
1225
1226</li>
1227 <li> 1122 <li>
1228 <span class="types"><a class="type" href="https://www.lua.org/manual/5.4/manual.html#6.4">string</a></span> 1123 <span class="types"><a class="type" href="https://www.lua.org/manual/5.4/manual.html#6.4">string</a></span>
1229 error message</li> 1124 error message when the timeout was reached (<code>&quot;timeout&quot;</code>), or if <a href="../modules/system.html#sleep">sleep</a> failed.</li>
1125 </ol>
1126
1127
1128
1129
1130</dd>
1131</dl>
1132 <h2 class="section-header "><a name="Terminal_UTF_8"></a>Terminal_UTF-8</h2>
1133
1134 <dl class="function">
1135 <dt>
1136 <a name = "CODEPAGE_UTF8"></a>
1137 <strong>CODEPAGE_UTF8</strong>
1138 </dt>
1139 <dd>
1140 UTF8 codepage.
1141 To be used with <a href="../modules/system.html#setconsoleoutputcp">system.setconsoleoutputcp</a> and <a href="../modules/system.html#setconsolecp">system.setconsolecp</a>.
1142
1143
1144 <ul>
1145 <li><span class="parameter">CODEPAGE_UTF8</span>
1146 The Windows CodePage for UTF8.
1147 </li>
1148 </ul>
1149
1150
1151
1152
1153
1154</dd>
1155 <dt>
1156 <a name = "getconsolecp"></a>
1157 <strong>getconsolecp ()</strong>
1158 </dt>
1159 <dd>
1160 Gets the current console code page (Windows).
1161
1162
1163
1164 <h3>Returns:</h3>
1165 <ol>
1166
1167 <span class="types"><span class="type">int</span></span>
1168 the current code page (always 65001 on Posix systems)
1230 </ol> 1169 </ol>
1231 1170
1232 1171
@@ -1234,27 +1173,73 @@ flags for the <code>iflags</code>, <code>oflags</code>, and <code>lflags</code>
1234 1173
1235</dd> 1174</dd>
1236 <dt> 1175 <dt>
1237 <a name = "termwrap"></a> 1176 <a name = "getconsoleoutputcp"></a>
1238 <strong>termwrap (f)</strong> 1177 <strong>getconsoleoutputcp ()</strong>
1239 </dt> 1178 </dt>
1240 <dd> 1179 <dd>
1241 Wraps a function to automatically restore terminal settings upon returning. 1180 Gets the current console output code page (Windows).
1242 Calls <a href="../modules/system.html#termbackup">termbackup</a> before calling the function and <a href="../modules/system.html#termrestore">termrestore</a> after. 1181
1182
1183
1184 <h3>Returns:</h3>
1185 <ol>
1186
1187 <span class="types"><span class="type">int</span></span>
1188 the current code page (always 65001 on Posix systems)
1189 </ol>
1190
1191
1192
1193
1194</dd>
1195 <dt>
1196 <a name = "setconsolecp"></a>
1197 <strong>setconsolecp (cp)</strong>
1198 </dt>
1199 <dd>
1200 Sets the current console code page (Windows).
1243 1201
1244 1202
1245 <h3>Parameters:</h3> 1203 <h3>Parameters:</h3>
1246 <ul> 1204 <ul>
1247 <li><span class="parameter">f</span> 1205 <li><span class="parameter">cp</span>
1248 <span class="types"><span class="type">function</span></span> 1206 <span class="types"><span class="type">int</span></span>
1249 function to wrap 1207 the code page to set, use <a href="../modules/system.html#CODEPAGE_UTF8">system.CODEPAGE_UTF8</a> (65001) for UTF-8
1250 </li> 1208 </li>
1251 </ul> 1209 </ul>
1252 1210
1253 <h3>Returns:</h3> 1211 <h3>Returns:</h3>
1254 <ol> 1212 <ol>
1255 1213
1256 <span class="types"><span class="type">function</span></span> 1214 <span class="types"><span class="type">bool</span></span>
1257 wrapped function 1215 <code>true</code> on success (always <code>true</code> on Posix systems)
1216 </ol>
1217
1218
1219
1220
1221</dd>
1222 <dt>
1223 <a name = "setconsoleoutputcp"></a>
1224 <strong>setconsoleoutputcp (cp)</strong>
1225 </dt>
1226 <dd>
1227 Sets the current console output code page (Windows).
1228
1229
1230 <h3>Parameters:</h3>
1231 <ul>
1232 <li><span class="parameter">cp</span>
1233 <span class="types"><span class="type">int</span></span>
1234 the code page to set, use <a href="../modules/system.html#CODEPAGE_UTF8">system.CODEPAGE_UTF8</a> (65001) for UTF-8
1235 </li>
1236 </ul>
1237
1238 <h3>Returns:</h3>
1239 <ol>
1240
1241 <span class="types"><span class="type">bool</span></span>
1242 <code>true</code> on success (always <code>true</code> on Posix systems)
1258 </ol> 1243 </ol>
1259 1244
1260 1245
@@ -1416,13 +1401,124 @@ This function will sleep, without doing a busy-loop and wasting CPU cycles.
1416 1401
1417</dd> 1402</dd>
1418</dl> 1403</dl>
1404 <h2 class="section-header "><a name="Terminal_Backup"></a>Terminal_Backup</h2>
1405
1406 <dl class="function">
1407 <dt>
1408 <a name = "autotermrestore"></a>
1409 <strong>autotermrestore ()</strong>
1410 </dt>
1411 <dd>
1412 Backs up terminal settings and restores them on application exit.
1413 Calls <a href="../modules/system.html#termbackup">termbackup</a> to back up terminal settings and sets up a GC method to
1414 automatically restore them on application exit (also works on Lua 5.1).
1415
1416
1417
1418 <h3>Returns:</h3>
1419 <ol>
1420
1421 <span class="types"><span class="type">boolean</span></span>
1422 true
1423 </ol>
1424 <h3>Or</h3>
1425 <ol>
1426 <li>
1427 <span class="types"><span class="type">nil</span></span>
1428 if the backup was already created</li>
1429 <li>
1430 <span class="types"><a class="type" href="https://www.lua.org/manual/5.4/manual.html#6.4">string</a></span>
1431 error message</li>
1432 </ol>
1433
1434
1435
1436
1437</dd>
1438 <dt>
1439 <a name = "termbackup"></a>
1440 <strong>termbackup ()</strong>
1441 </dt>
1442 <dd>
1443 Returns a backup of terminal settings for stdin/out/err.
1444 Handles terminal/console flags, Windows codepage, and non-block flags on the streams.
1445 Backs up terminal/console flags only if a stream is a tty.
1446
1447
1448
1449 <h3>Returns:</h3>
1450 <ol>
1451
1452 table with backup of terminal settings
1453 </ol>
1454
1455
1456
1457
1458</dd>
1459 <dt>
1460 <a name = "termrestore"></a>
1461 <strong>termrestore (backup)</strong>
1462 </dt>
1463 <dd>
1464 Restores terminal settings from a backup
1465
1466
1467 <h3>Parameters:</h3>
1468 <ul>
1469 <li><span class="parameter">backup</span>
1470 <span class="types"><a class="type" href="https://www.lua.org/manual/5.4/manual.html#6.6">table</a></span>
1471 the backup of terminal settings, see <a href="../modules/system.html#termbackup">termbackup</a>.
1472 </li>
1473 </ul>
1474
1475 <h3>Returns:</h3>
1476 <ol>
1477
1478 <span class="types"><span class="type">boolean</span></span>
1479 true
1480 </ol>
1481
1482
1483
1484
1485</dd>
1486 <dt>
1487 <a name = "termwrap"></a>
1488 <strong>termwrap (f)</strong>
1489 </dt>
1490 <dd>
1491 Wraps a function to automatically restore terminal settings upon returning.
1492 Calls <a href="../modules/system.html#termbackup">termbackup</a> before calling the function and <a href="../modules/system.html#termrestore">termrestore</a> after.
1493
1494
1495 <h3>Parameters:</h3>
1496 <ul>
1497 <li><span class="parameter">f</span>
1498 <span class="types"><span class="type">function</span></span>
1499 function to wrap
1500 </li>
1501 </ul>
1502
1503 <h3>Returns:</h3>
1504 <ol>
1505
1506 <span class="types"><span class="type">function</span></span>
1507 wrapped function
1508 </ol>
1509
1510
1511
1512
1513</dd>
1514</dl>
1419 1515
1420 1516
1421</div> <!-- id="content" --> 1517</div> <!-- id="content" -->
1422</div> <!-- id="main" --> 1518</div> <!-- id="main" -->
1423<div id="about"> 1519<div id="about">
1424<i>generated by <a href="http://github.com/lunarmodules/LDoc">LDoc 1.5.0</a></i> 1520<i>generated by <a href="http://github.com/lunarmodules/LDoc">LDoc 1.5.0</a></i>
1425<i style="float:right;">Last updated 2025-03-12 15:17:50 </i> 1521<i style="float:right;">Last updated 2025-07-11 22:55:05 </i>
1426</div> <!-- id="about" --> 1522</div> <!-- id="about" -->
1427</div> <!-- id="container" --> 1523</div> <!-- id="container" -->
1428</body> 1524</body>
diff --git a/docs/topics/01-introduction.md.html b/docs/topics/01-introduction.md.html
index f0f627d..264aa71 100644
--- a/docs/topics/01-introduction.md.html
+++ b/docs/topics/01-introduction.md.html
@@ -84,7 +84,7 @@ independence.</p>
84</div> <!-- id="main" --> 84</div> <!-- id="main" -->
85<div id="about"> 85<div id="about">
86<i>generated by <a href="http://github.com/lunarmodules/LDoc">LDoc 1.5.0</a></i> 86<i>generated by <a href="http://github.com/lunarmodules/LDoc">LDoc 1.5.0</a></i>
87<i style="float:right;">Last updated 2025-03-12 15:17:50 </i> 87<i style="float:right;">Last updated 2025-07-11 22:55:05 </i>
88</div> <!-- id="about" --> 88</div> <!-- id="about" -->
89</div> <!-- id="container" --> 89</div> <!-- id="container" -->
90</body> 90</body>
diff --git a/docs/topics/02-development.md.html b/docs/topics/02-development.md.html
index 315fa37..15efe55 100644
--- a/docs/topics/02-development.md.html
+++ b/docs/topics/02-development.md.html
@@ -84,7 +84,7 @@ pass locally, and do not rely on CI only.</p>
84</div> <!-- id="main" --> 84</div> <!-- id="main" -->
85<div id="about"> 85<div id="about">
86<i>generated by <a href="http://github.com/lunarmodules/LDoc">LDoc 1.5.0</a></i> 86<i>generated by <a href="http://github.com/lunarmodules/LDoc">LDoc 1.5.0</a></i>
87<i style="float:right;">Last updated 2025-03-12 15:17:50 </i> 87<i style="float:right;">Last updated 2025-07-11 22:55:05 </i>
88</div> <!-- id="about" --> 88</div> <!-- id="about" -->
89</div> <!-- id="container" --> 89</div> <!-- id="container" -->
90</body> 90</body>
diff --git a/docs/topics/03-terminal.md.html b/docs/topics/03-terminal.md.html
index 7ce28af..27772ac 100644
--- a/docs/topics/03-terminal.md.html
+++ b/docs/topics/03-terminal.md.html
@@ -93,6 +93,7 @@ up both platforms to make it work.</p>
93 <li><a href="../modules/system.html#setconsoleflags">system.setconsoleflags</a> (Windows)</li> 93 <li><a href="../modules/system.html#setconsoleflags">system.setconsoleflags</a> (Windows)</li>
94 <li><a href="../modules/system.html#setconsolecp">system.setconsolecp</a> (Windows)</li> 94 <li><a href="../modules/system.html#setconsolecp">system.setconsolecp</a> (Windows)</li>
95 <li><a href="../modules/system.html#setconsoleoutputcp">system.setconsoleoutputcp</a> (Windows)</li> 95 <li><a href="../modules/system.html#setconsoleoutputcp">system.setconsoleoutputcp</a> (Windows)</li>
96 <li><a href="../modules/system.html#detachfds">system.detachfds</a> (Posix)</li>
96 <li><a href="../modules/system.html#setnonblock">system.setnonblock</a> (Posix)</li> 97 <li><a href="../modules/system.html#setnonblock">system.setnonblock</a> (Posix)</li>
97 <li><a href="../modules/system.html#tcsetattr">system.tcsetattr</a> (Posix)</li> 98 <li><a href="../modules/system.html#tcsetattr">system.tcsetattr</a> (Posix)</li>
98</ul> 99</ul>
@@ -195,6 +196,7 @@ also not being echoed to the terminal (independent of the echo settings used wit
195</span>sys.<span class="function-name">setconsoleflags</span>(<span class="global">io</span>.stdin, sys.<span class="function-name">getconsoleflags</span>(<span class="global">io</span>.stdin) - sys.CIF_ECHO_INPUT - sys.CIF_LINE_INPUT) 196</span>sys.<span class="function-name">setconsoleflags</span>(<span class="global">io</span>.stdin, sys.<span class="function-name">getconsoleflags</span>(<span class="global">io</span>.stdin) - sys.CIF_ECHO_INPUT - sys.CIF_LINE_INPUT)
196 197
197<span class="comment">-- setup Posix by disabling echo, canonical mode, and making non-blocking 198<span class="comment">-- setup Posix by disabling echo, canonical mode, and making non-blocking
199</span>sys.<span class="function-name">detachfds</span>() <span class="comment">-- ensure stdin/out/err have their own file descriptions
198</span><span class="keyword">local</span> of_attr = sys.<span class="function-name">tcgetattr</span>(<span class="global">io</span>.stdin) 200</span><span class="keyword">local</span> of_attr = sys.<span class="function-name">tcgetattr</span>(<span class="global">io</span>.stdin)
199sys.<span class="function-name">tcsetattr</span>(<span class="global">io</span>.stdin, sys.TCSANOW, { 201sys.<span class="function-name">tcsetattr</span>(<span class="global">io</span>.stdin, sys.TCSANOW, {
200 lflag = of_attr.lflag - sys.L_ICANON - sys.L_ECHO, 202 lflag = of_attr.lflag - sys.L_ICANON - sys.L_ECHO,
@@ -218,7 +220,7 @@ For an example see <a href="../examples/password_input.lua.html"><code>examples/
218</div> <!-- id="main" --> 220</div> <!-- id="main" -->
219<div id="about"> 221<div id="about">
220<i>generated by <a href="http://github.com/lunarmodules/LDoc">LDoc 1.5.0</a></i> 222<i>generated by <a href="http://github.com/lunarmodules/LDoc">LDoc 1.5.0</a></i>
221<i style="float:right;">Last updated 2025-03-12 15:17:50 </i> 223<i style="float:right;">Last updated 2025-07-11 22:55:05 </i>
222</div> <!-- id="about" --> 224</div> <!-- id="about" -->
223</div> <!-- id="container" --> 225</div> <!-- id="container" -->
224</body> 226</body>
diff --git a/docs/topics/CHANGELOG.md.html b/docs/topics/CHANGELOG.md.html
index fa2d706..9603abd 100644
--- a/docs/topics/CHANGELOG.md.html
+++ b/docs/topics/CHANGELOG.md.html
@@ -106,6 +106,41 @@
106<p><a name="Version_history"></a></p> 106<p><a name="Version_history"></a></p>
107<h2>Version history</h2> 107<h2>Version history</h2>
108 108
109<h3>version 0.6.3, released 11-Jul-2025</h3>
110
111<ul>
112 <li>Fix: maximum key-delay, reduced from 0.2 to 0.1 seconds to reduce slugginess feel on some key presses.
113 See <a href="https://github.com/lunarmodules/luasystem/pull/69">#69</a>.</li>
114 <li>Fix: <code>readansi</code> now only reports printable chars as characters
115 See <a href="https://github.com/lunarmodules/luasystem/pull/70">#70</a>.</li>
116 <li>Fix: readkey now ignores Windows scancodes if received.
117 See <a href="https://github.com/lunarmodules/luasystem/pull/74">#74</a>.</li>
118 <li>Fix: readkey now passes errors on.
119 See <a href="https://github.com/lunarmodules/luasystem/pull/74">#74</a>.</li>
120</ul>
121
122<h3>version 0.6.2, released 15-Apr-2025</h3>
123
124<ul>
125 <li>Fix: autotermrestore didn't work because its metatable was overwritten.</li>
126</ul>
127
128<h3>version 0.6.1, released 13-Apr-2025</h3>
129
130<ul>
131 <li>Docs: document readansi internal buffer for incomplete sequences.</li>
132 <li>Fix: ensure to properly parse <code>&lt;alt&gt;+key</code> key presses</li>
133</ul>
134
135<h3>version 0.6.0, released 10-Apr-2025</h3>
136
137<ul>
138 <li>Fix: when sleep returns an error, pass that on in <code>readkey</code>.</li>
139 <li>Feat: added <code>detachfds</code> which will create separate file descriptions for <code>stdout</code>
140 and <code>stderr</code> to ensure that related settings (eg. non-blocking flag) will not be shared
141 amongst those streams and <code>stdin</code>.</li>
142</ul>
143
109<h3>version 0.5.1, released 12-Mar-2025</h3> 144<h3>version 0.5.1, released 12-Mar-2025</h3>
110 145
111<ul> 146<ul>
@@ -120,14 +155,14 @@
120 <li>Feat: allow passing in a sleep function to <code>readkey</code> and <code>readansi</code></li> 155 <li>Feat: allow passing in a sleep function to <code>readkey</code> and <code>readansi</code></li>
121 <li>Fix: NetBSD fix compilation, undeclared directives</li> 156 <li>Fix: NetBSD fix compilation, undeclared directives</li>
122 <li>Refactor: random bytes; remove deprecated API usage on Windows, move to 157 <li>Refactor: random bytes; remove deprecated API usage on Windows, move to
123 binary api instead of /dev/urandom file on linux and bsd</li> 158 binary api instead of <code>/dev/urandom</code> file on linux and bsd</li>
124</ul> 159</ul>
125 160
126<h3>version 0.4.5, released 18-Dec-2024</h3> 161<h3>version 0.4.5, released 18-Dec-2024</h3>
127 162
128<ul> 163<ul>
129 <li>Fix: suppress a warning when building with clang</li> 164 <li>Fix: suppress a warning when building with clang</li>
130 <li>Fix: do not rely on luaconf.h to include limits.h, fixes builds with latest LuaJIT (#38).</li> 165 <li>Fix: do not rely on <code>luaconf.h</code> to include <code>limits.h</code>, fixes builds with latest LuaJIT (#38).</li>
131</ul> 166</ul>
132 167
133<h3>version 0.4.4, released 03-Sep-2024</h3> 168<h3>version 0.4.4, released 03-Sep-2024</h3>
@@ -199,7 +234,7 @@
199</div> <!-- id="main" --> 234</div> <!-- id="main" -->
200<div id="about"> 235<div id="about">
201<i>generated by <a href="http://github.com/lunarmodules/LDoc">LDoc 1.5.0</a></i> 236<i>generated by <a href="http://github.com/lunarmodules/LDoc">LDoc 1.5.0</a></i>
202<i style="float:right;">Last updated 2025-03-12 15:17:50 </i> 237<i style="float:right;">Last updated 2025-07-11 22:55:05 </i>
203</div> <!-- id="about" --> 238</div> <!-- id="about" -->
204</div> <!-- id="container" --> 239</div> <!-- id="container" -->
205</body> 240</body>
diff --git a/docs/topics/LICENSE.md.html b/docs/topics/LICENSE.md.html
index 612cdb9..c0b9b93 100644
--- a/docs/topics/LICENSE.md.html
+++ b/docs/topics/LICENSE.md.html
@@ -94,7 +94,7 @@ SOFTWARE.</p>
94</div> <!-- id="main" --> 94</div> <!-- id="main" -->
95<div id="about"> 95<div id="about">
96<i>generated by <a href="http://github.com/lunarmodules/LDoc">LDoc 1.5.0</a></i> 96<i>generated by <a href="http://github.com/lunarmodules/LDoc">LDoc 1.5.0</a></i>
97<i style="float:right;">Last updated 2025-03-12 15:17:50 </i> 97<i style="float:right;">Last updated 2025-07-11 22:55:05 </i>
98</div> <!-- id="about" --> 98</div> <!-- id="about" -->
99</div> <!-- id="container" --> 99</div> <!-- id="container" -->
100</body> 100</body>
diff --git a/rockspecs/luasystem-0.6.0-1.rockspec b/rockspecs/luasystem-0.6.0-1.rockspec
new file mode 100644
index 0000000..21c2dc0
--- /dev/null
+++ b/rockspecs/luasystem-0.6.0-1.rockspec
@@ -0,0 +1,85 @@
1local package_name = "luasystem"
2local package_version = "0.6.0"
3local rockspec_revision = "1"
4local github_account_name = "lunarmodules"
5local github_repo_name = "luasystem"
6
7
8package = package_name
9version = package_version.."-"..rockspec_revision
10
11source = {
12 url = "git+https://github.com/"..github_account_name.."/"..github_repo_name..".git",
13 branch = (package_version == "scm") and "master" or nil,
14 tag = (package_version ~= "scm") and "v"..package_version or nil,
15}
16
17description = {
18 summary = 'Platform independent system calls for Lua.',
19 detailed = [[
20 Adds a Lua API for making platform independent system calls.
21 ]],
22 license = 'MIT <http://opensource.org/licenses/MIT>',
23 homepage = "https://github.com/"..github_account_name.."/"..github_repo_name,
24}
25
26dependencies = {
27 'lua >= 5.1',
28}
29
30local function make_platform(plat)
31 local defines = {
32 linux = { },
33 unix = { },
34 macosx = { },
35 win32 = { "WINVER=0x0600", "_WIN32_WINNT=0x0600" },
36 mingw32 = { "WINVER=0x0600", "_WIN32_WINNT=0x0600" },
37 }
38 local libraries = {
39 linux = { "rt" },
40 unix = { },
41 macosx = { },
42 win32 = { "advapi32", "winmm", "bcrypt" },
43 mingw32 = { },
44 }
45 local libdirs = {
46 linux = nil,
47 unix = nil,
48 macosx = nil,
49 win32 = nil,
50 mingw32 = { },
51 }
52 return {
53 modules = {
54 ['system.core'] = {
55 sources = {
56 'src/core.c',
57 'src/compat.c',
58 'src/time.c',
59 'src/environment.c',
60 'src/random.c',
61 'src/term.c',
62 'src/bitflags.c',
63 'src/wcwidth.c',
64 },
65 defines = defines[plat],
66 libraries = libraries[plat],
67 libdirs = libdirs[plat],
68 },
69 },
70 }
71end
72
73build = {
74 type = 'builtin',
75 platforms = {
76 linux = make_platform('linux'),
77 unix = make_platform('unix'),
78 macosx = make_platform('macosx'),
79 win32 = make_platform('win32'),
80 mingw32 = make_platform('mingw32'),
81 },
82 modules = {
83 ['system.init'] = 'system/init.lua',
84 },
85}
diff --git a/rockspecs/luasystem-0.6.1-1.rockspec b/rockspecs/luasystem-0.6.1-1.rockspec
new file mode 100644
index 0000000..51bbddd
--- /dev/null
+++ b/rockspecs/luasystem-0.6.1-1.rockspec
@@ -0,0 +1,85 @@
1local package_name = "luasystem"
2local package_version = "0.6.1"
3local rockspec_revision = "1"
4local github_account_name = "lunarmodules"
5local github_repo_name = "luasystem"
6
7
8package = package_name
9version = package_version.."-"..rockspec_revision
10
11source = {
12 url = "git+https://github.com/"..github_account_name.."/"..github_repo_name..".git",
13 branch = (package_version == "scm") and "master" or nil,
14 tag = (package_version ~= "scm") and "v"..package_version or nil,
15}
16
17description = {
18 summary = 'Platform independent system calls for Lua.',
19 detailed = [[
20 Adds a Lua API for making platform independent system calls.
21 ]],
22 license = 'MIT <http://opensource.org/licenses/MIT>',
23 homepage = "https://github.com/"..github_account_name.."/"..github_repo_name,
24}
25
26dependencies = {
27 'lua >= 5.1',
28}
29
30local function make_platform(plat)
31 local defines = {
32 linux = { },
33 unix = { },
34 macosx = { },
35 win32 = { "WINVER=0x0600", "_WIN32_WINNT=0x0600" },
36 mingw32 = { "WINVER=0x0600", "_WIN32_WINNT=0x0600" },
37 }
38 local libraries = {
39 linux = { "rt" },
40 unix = { },
41 macosx = { },
42 win32 = { "advapi32", "winmm", "bcrypt" },
43 mingw32 = { },
44 }
45 local libdirs = {
46 linux = nil,
47 unix = nil,
48 macosx = nil,
49 win32 = nil,
50 mingw32 = { },
51 }
52 return {
53 modules = {
54 ['system.core'] = {
55 sources = {
56 'src/core.c',
57 'src/compat.c',
58 'src/time.c',
59 'src/environment.c',
60 'src/random.c',
61 'src/term.c',
62 'src/bitflags.c',
63 'src/wcwidth.c',
64 },
65 defines = defines[plat],
66 libraries = libraries[plat],
67 libdirs = libdirs[plat],
68 },
69 },
70 }
71end
72
73build = {
74 type = 'builtin',
75 platforms = {
76 linux = make_platform('linux'),
77 unix = make_platform('unix'),
78 macosx = make_platform('macosx'),
79 win32 = make_platform('win32'),
80 mingw32 = make_platform('mingw32'),
81 },
82 modules = {
83 ['system.init'] = 'system/init.lua',
84 },
85}
diff --git a/rockspecs/luasystem-0.6.2-1.rockspec b/rockspecs/luasystem-0.6.2-1.rockspec
new file mode 100644
index 0000000..7a8549f
--- /dev/null
+++ b/rockspecs/luasystem-0.6.2-1.rockspec
@@ -0,0 +1,85 @@
1local package_name = "luasystem"
2local package_version = "0.6.2"
3local rockspec_revision = "1"
4local github_account_name = "lunarmodules"
5local github_repo_name = "luasystem"
6
7
8package = package_name
9version = package_version.."-"..rockspec_revision
10
11source = {
12 url = "git+https://github.com/"..github_account_name.."/"..github_repo_name..".git",
13 branch = (package_version == "scm") and "master" or nil,
14 tag = (package_version ~= "scm") and "v"..package_version or nil,
15}
16
17description = {
18 summary = 'Platform independent system calls for Lua.',
19 detailed = [[
20 Adds a Lua API for making platform independent system calls.
21 ]],
22 license = 'MIT <http://opensource.org/licenses/MIT>',
23 homepage = "https://github.com/"..github_account_name.."/"..github_repo_name,
24}
25
26dependencies = {
27 'lua >= 5.1',
28}
29
30local function make_platform(plat)
31 local defines = {
32 linux = { },
33 unix = { },
34 macosx = { },
35 win32 = { "WINVER=0x0600", "_WIN32_WINNT=0x0600" },
36 mingw32 = { "WINVER=0x0600", "_WIN32_WINNT=0x0600" },
37 }
38 local libraries = {
39 linux = { "rt" },
40 unix = { },
41 macosx = { },
42 win32 = { "advapi32", "winmm", "bcrypt" },
43 mingw32 = { },
44 }
45 local libdirs = {
46 linux = nil,
47 unix = nil,
48 macosx = nil,
49 win32 = nil,
50 mingw32 = { },
51 }
52 return {
53 modules = {
54 ['system.core'] = {
55 sources = {
56 'src/core.c',
57 'src/compat.c',
58 'src/time.c',
59 'src/environment.c',
60 'src/random.c',
61 'src/term.c',
62 'src/bitflags.c',
63 'src/wcwidth.c',
64 },
65 defines = defines[plat],
66 libraries = libraries[plat],
67 libdirs = libdirs[plat],
68 },
69 },
70 }
71end
72
73build = {
74 type = 'builtin',
75 platforms = {
76 linux = make_platform('linux'),
77 unix = make_platform('unix'),
78 macosx = make_platform('macosx'),
79 win32 = make_platform('win32'),
80 mingw32 = make_platform('mingw32'),
81 },
82 modules = {
83 ['system.init'] = 'system/init.lua',
84 },
85}
diff --git a/rockspecs/luasystem-0.6.3-1.rockspec b/rockspecs/luasystem-0.6.3-1.rockspec
new file mode 100644
index 0000000..e70373f
--- /dev/null
+++ b/rockspecs/luasystem-0.6.3-1.rockspec
@@ -0,0 +1,85 @@
1local package_name = "luasystem"
2local package_version = "0.6.3"
3local rockspec_revision = "1"
4local github_account_name = "lunarmodules"
5local github_repo_name = "luasystem"
6
7
8package = package_name
9version = package_version.."-"..rockspec_revision
10
11source = {
12 url = "git+https://github.com/"..github_account_name.."/"..github_repo_name..".git",
13 branch = (package_version == "scm") and "master" or nil,
14 tag = (package_version ~= "scm") and "v"..package_version or nil,
15}
16
17description = {
18 summary = 'Platform independent system calls for Lua.',
19 detailed = [[
20 Adds a Lua API for making platform independent system calls.
21 ]],
22 license = 'MIT <http://opensource.org/licenses/MIT>',
23 homepage = "https://github.com/"..github_account_name.."/"..github_repo_name,
24}
25
26dependencies = {
27 'lua >= 5.1',
28}
29
30local function make_platform(plat)
31 local defines = {
32 linux = { },
33 unix = { },
34 macosx = { },
35 win32 = { "WINVER=0x0600", "_WIN32_WINNT=0x0600" },
36 mingw32 = { "WINVER=0x0600", "_WIN32_WINNT=0x0600" },
37 }
38 local libraries = {
39 linux = { "rt" },
40 unix = { },
41 macosx = { },
42 win32 = { "advapi32", "winmm", "bcrypt" },
43 mingw32 = { },
44 }
45 local libdirs = {
46 linux = nil,
47 unix = nil,
48 macosx = nil,
49 win32 = nil,
50 mingw32 = { },
51 }
52 return {
53 modules = {
54 ['system.core'] = {
55 sources = {
56 'src/core.c',
57 'src/compat.c',
58 'src/time.c',
59 'src/environment.c',
60 'src/random.c',
61 'src/term.c',
62 'src/bitflags.c',
63 'src/wcwidth.c',
64 },
65 defines = defines[plat],
66 libraries = libraries[plat],
67 libdirs = libdirs[plat],
68 },
69 },
70 }
71end
72
73build = {
74 type = 'builtin',
75 platforms = {
76 linux = make_platform('linux'),
77 unix = make_platform('unix'),
78 macosx = make_platform('macosx'),
79 win32 = make_platform('win32'),
80 mingw32 = make_platform('mingw32'),
81 },
82 modules = {
83 ['system.init'] = 'system/init.lua',
84 },
85}
diff --git a/spec/04-term_spec.lua b/spec/04-term_spec.lua
index 907f903..5dea046 100644
--- a/spec/04-term_spec.lua
+++ b/spec/04-term_spec.lua
@@ -855,6 +855,32 @@ describe("Terminal:", function()
855 assert.is.near(1, timing, 0.5) -- this also works for MacOS in CI 855 assert.is.near(1, timing, 0.5) -- this also works for MacOS in CI
856 end) 856 end)
857 857
858
859 it("calls flseep to execute the sleep", function()
860 setbuffer("")
861
862 local sleep_called = false
863 local mysleep = function()
864 sleep_called = true
865 end
866
867 system.readkey(0.01, mysleep)
868 assert.is_true(sleep_called)
869 end)
870
871
872 it("returns errors by fsleep", function()
873 setbuffer("")
874
875 local mysleep = function()
876 return nil, "boom!"
877 end
878
879 local ok, err = system.readkey(1, mysleep)
880 assert.is.falsy(ok)
881 assert.equals("boom!", err)
882 end)
883
858 end) 884 end)
859 885
860 886
@@ -876,6 +902,14 @@ describe("Terminal:", function()
876 end) 902 end)
877 903
878 904
905 it("reads a control byte for single-byte control characters", function()
906 setbuffer("\000\031\127")
907 assert.are.same({"\000", "ctrl"}, {system.readansi(0)})
908 assert.are.same({"\031", "ctrl"}, {system.readansi(0)})
909 assert.are.same({"\127", "ctrl"}, {system.readansi(0)})
910 end)
911
912
879 it("reads a multi-byte characters one at a time", function() 913 it("reads a multi-byte characters one at a time", function()
880 setbuffer(string.char(226, 130, 172) .. -- "€" single 914 setbuffer(string.char(226, 130, 172) .. -- "€" single
881 string.char(240, 159, 154, 128)) -- "🚀" double 915 string.char(240, 159, 154, 128)) -- "🚀" double
@@ -902,10 +936,31 @@ describe("Terminal:", function()
902 end) 936 end)
903 937
904 938
939 it("reads ANSI escape sequences, just an '<esc>O' (<alt><O> key press)", function()
940 setbuffer("\27O")
941 assert.are.same({"\27O", "ansi"}, {system.readansi(0)})
942 end)
943
944
945 it("reads <alt>+key key presses; <esc>+<key>", function()
946 setbuffer("\27a\27b\27c\27d")
947 assert.are.same({"\27a", "ansi"}, {system.readansi(0)})
948 assert.are.same({"\27b", "ansi"}, {system.readansi(0)})
949 assert.are.same({"\27c", "ansi"}, {system.readansi(0)})
950 assert.are.same({"\27d", "ansi"}, {system.readansi(0)})
951 end)
952
953
954 it("reads <ctrl><alt>[ key press; <esc>+<esc>", function()
955 setbuffer("\27\27\27\27")
956 assert.are.same({"\27\27", "ansi"}, {system.readansi(0)})
957 assert.are.same({"\27\27", "ansi"}, {system.readansi(0)})
958 end)
959
960
905 it("returns a single <esc> character if no sequence is found", function() 961 it("returns a single <esc> character if no sequence is found", function()
906 setbuffer("\27\27[A") 962 setbuffer("\27")
907 assert.are.same({"\27", "char"}, {system.readansi(0)}) 963 assert.are.same({"\27", "ctrl"}, {system.readansi(0)})
908 assert.are.same({"\27[A", "ansi"}, {system.readansi(0)})
909 end) 964 end)
910 965
911 966
@@ -919,6 +974,22 @@ describe("Terminal:", function()
919 assert.is.near(1, timing, 0.5) -- this also works for MacOS in CI 974 assert.is.near(1, timing, 0.5) -- this also works for MacOS in CI
920 end) 975 end)
921 976
977
978 it("incomplete ANSI sequences will be completed on next call", function()
979 setbuffer("\27[")
980 assert.are.same({nil, "timeout", "\27["}, {system.readansi(0)})
981 setbuffer("A")
982 assert.are.same({"\27[A", "ansi"}, {system.readansi(0)})
983 end)
984
985
986 it("incomplete UTF8 sequences will be completed on next call", function()
987 setbuffer(string.char(240, 159))
988 assert.are.same({nil, "timeout", string.char(240, 159)}, {system.readansi(0)})
989 setbuffer(string.char(154, 128))
990 assert.are.same({"🚀", "char"}, {system.readansi(0)})
991 end)
992
922 end) 993 end)
923 994
924 end) 995 end)
diff --git a/src/core.c b/src/core.c
index b642074..34e19d8 100644
--- a/src/core.c
+++ b/src/core.c
@@ -4,7 +4,7 @@
4#include <lua.h> 4#include <lua.h>
5#include <lauxlib.h> 5#include <lauxlib.h>
6 6
7#define LUASYSTEM_VERSION "LuaSystem 0.5.1" 7#define LUASYSTEM_VERSION "LuaSystem 0.6.3"
8 8
9#ifdef _WIN32 9#ifdef _WIN32
10#define LUAEXPORT __declspec(dllexport) 10#define LUAEXPORT __declspec(dllexport)
diff --git a/src/term.c b/src/term.c
index 4cfce95..0325208 100644
--- a/src/term.c
+++ b/src/term.c
@@ -384,6 +384,7 @@ See [setconsolemode documentation](https://learn.microsoft.com/en-us/windows/con
384@treturn[1] boolean `true` on success 384@treturn[1] boolean `true` on success
385@treturn[2] nil 385@treturn[2] nil
386@treturn[2] string error message 386@treturn[2] string error message
387@within Terminal_Windows
387@usage 388@usage
388local system = require('system') 389local system = require('system')
389system.listconsoleflags(io.stdout) -- List all the available flags and their current status 390system.listconsoleflags(io.stdout) -- List all the available flags and their current status
@@ -427,13 +428,12 @@ input flags (for use with `io.stdin`) and `COF` are the output flags (for use wi
427_Note_: See [setconsolemode documentation](https://learn.microsoft.com/en-us/windows/console/setconsolemode) 428_Note_: See [setconsolemode documentation](https://learn.microsoft.com/en-us/windows/console/setconsolemode)
428for more information on the flags. 429for more information on the flags.
429 430
430
431
432@function getconsoleflags 431@function getconsoleflags
433@tparam file file file handle to operate on, one of `io.stdin`, `io.stdout`, `io.stderr` 432@tparam file file file handle to operate on, one of `io.stdin`, `io.stdout`, `io.stderr`
434@treturn[1] bitflags the current console flags. 433@treturn[1] bitflags the current console flags.
435@treturn[2] nil 434@treturn[2] nil
436@treturn[2] string error message 435@treturn[2] string error message
436@within Terminal_Windows
437@usage 437@usage
438local system = require('system') 438local system = require('system')
439 439
@@ -497,6 +497,7 @@ The terminal attributes is a table with the following fields:
497@treturn[2] string error message 497@treturn[2] string error message
498@treturn[2] int errnum 498@treturn[2] int errnum
499@return error message if failed 499@return error message if failed
500@within Terminal_Posix
500@usage 501@usage
501local system = require('system') 502local system = require('system')
502 503
@@ -591,6 +592,7 @@ _Note_: only `iflag`, `oflag`, and `lflag` are supported at the moment. The othe
591@return[2] nil 592@return[2] nil
592@treturn[2] string error message 593@treturn[2] string error message
593@treturn[2] int errnum 594@treturn[2] int errnum
595@within Terminal_Posix
594@usage 596@usage
595local system = require('system') 597local system = require('system')
596 598
@@ -649,8 +651,105 @@ static int lst_tcsetattr(lua_State *L)
649 651
650 652
651 653
654#ifndef _WIN32
655/*
656reopen FDs for independent file descriptions.
657*/
658static void reopen_fd(lua_State *L, int fd, int flags) {
659 char path[64];
660 int newfd = -1;
661
662 if (fd != STDOUT_FILENO && fd != STDERR_FILENO) {
663 luaL_error(L, "Invalid file descriptor: %d. Only stdout (1) and stderr (2) are supported.", fd);
664 }
665
666 const char *fallback_path = (fd == STDOUT_FILENO) ? "/dev/stdout" :
667 (fd == STDERR_FILENO) ? "/dev/stderr" : NULL;
668
669 // If fd is a terminal, reopen its actual device (e.g. /dev/ttys003)
670 // Works on all POSIX platforms that have terminals (macOS, Linux, BSD, etc.)
671 if (isatty(fd)) {
672 const char *tty = ttyname(fd);
673 if (tty) {
674 newfd = open(tty, flags);
675 }
676 }
677
678 if (newfd < 0) {
679 // For non-tty: try /dev/fd/N — POSIX-compliant and standard on macOS, Linux, BSD.
680 // This gives a new file description even if the target is a file or pipe.
681 snprintf(path, sizeof(path), "/dev/fd/%d", fd);
682 newfd = open(path, flags);
683 }
684
685 if (newfd < 0) {
686 // Fallback: for platforms/environments where /dev/fd/N doesn't exist.
687 // /dev/stdout and /dev/stderr are standard on Linux, but may not create new descriptions.
688 if (fallback_path) {
689 newfd = open(fallback_path, flags);
690 }
691 }
692
693 if (newfd < 0) {
694 // All attempts failed — raise error with detailed info (call will not return)
695 luaL_error(L, "Failed to reopen fd %d: tried ttyname(), /dev/fd/%d, and fallback %s: %s",
696 fd, fd, fallback_path ? fallback_path : "(none)", strerror(errno));
697 }
698
699 // Replace the original fd with the new one
700 if (dup2(newfd, fd) < 0) {
701 close(newfd);
702 luaL_error(L, "dup2 failed for fd %d: %s", fd, strerror(errno));
703 }
704
705 close(newfd); // Close the new fd, as dup2 has replaced the original fd with it
706}
707#endif
708
709
710
711/***
712Creates new file descriptions for `stdout` and `stderr`.
713Even if the file descriptors are unique, they still might point to the same
714file description, and hence share settings like `O_NONBLOCK`. This means that
715if one of them is set to non-blocking, the other will be as well. This can
716lead to unexpected behavior.
717
718This function is used to detach `stdout` and `stderr` from the original
719file descriptions, and create new file descriptions for them. This allows
720independent control of flags (e.g., `O_NONBLOCK`) on `stdout` and `stderr`,
721avoiding shared side effects.
722
723Does not modify `stdin` (fd 0), and does nothing on Windows.
724@function detachfds
725@return boolean `true` on success, or throws an error on failure.
726@within Terminal_Posix
727@see setnonblock
728*/
729static int lst_detachfds(lua_State *L) {
730 static int already_detached = 0; // detaching is once per process(not per thread or Lua state)
731 if (already_detached) {
732 lua_pushnil(L);
733 lua_pushliteral(L, "stdout and stderr already detached");
734 return 1;
735 }
736 already_detached = 1;
737
738#ifndef _WIN32
739 // Reopen stdout and stderr with new file descriptions
740 reopen_fd(L, STDOUT_FILENO, O_WRONLY);
741 reopen_fd(L, STDERR_FILENO, O_WRONLY);
742#endif
743
744 lua_pushboolean(L, 1);
745 return 1;
746}
747
748
749
652/*** 750/***
653Enables or disables non-blocking mode for a file (Posix). 751Enables or disables non-blocking mode for a file (Posix).
752Check `detachfds` in case there are shared file descriptions.
654@function setnonblock 753@function setnonblock
655@tparam file fd file handle to operate on, one of `io.stdin`, `io.stdout`, `io.stderr` 754@tparam file fd file handle to operate on, one of `io.stdin`, `io.stdout`, `io.stderr`
656@tparam boolean make_non_block a truthy value will enable non-blocking mode, a falsy value will disable it. 755@tparam boolean make_non_block a truthy value will enable non-blocking mode, a falsy value will disable it.
@@ -658,9 +757,12 @@ Enables or disables non-blocking mode for a file (Posix).
658@treturn[2] nil 757@treturn[2] nil
659@treturn[2] string error message 758@treturn[2] string error message
660@treturn[2] int errnum 759@treturn[2] int errnum
760@within Terminal_Posix
661@see getnonblock 761@see getnonblock
762@see detachfds
662@usage 763@usage
663local sys = require('system') 764local sys = require('system')
765sys.detachfds() -- detach stdout and stderr, so only stdin becomes non-blocking
664 766
665-- set io.stdin to non-blocking mode 767-- set io.stdin to non-blocking mode
666local old_setting = sys.getnonblock(io.stdin) 768local old_setting = sys.getnonblock(io.stdin)
@@ -717,6 +819,8 @@ Gets non-blocking mode status for a file (Posix).
717@treturn[2] nil 819@treturn[2] nil
718@treturn[2] string error message 820@treturn[2] string error message
719@treturn[2] int errnum 821@treturn[2] int errnum
822@within Terminal_Posix
823@see setnonblock
720*/ 824*/
721static int lst_getnonblock(lua_State *L) 825static int lst_getnonblock(lua_State *L)
722{ 826{
@@ -783,6 +887,7 @@ sequences will be buffered internally and returned one byte at a time.
783@treturn[3] nil on error 887@treturn[3] nil on error
784@treturn[3] string error message 888@treturn[3] string error message
785@treturn[3] int errnum (on posix) 889@treturn[3] int errnum (on posix)
890@within Terminal_Input
786*/ 891*/
787static int lst_readkey(lua_State *L) { 892static int lst_readkey(lua_State *L) {
788#ifdef _WIN32 893#ifdef _WIN32
@@ -803,7 +908,18 @@ static int lst_readkey(lua_State *L) {
803 } 908 }
804 909
805 wchar_t wc = _getwch(); 910 wchar_t wc = _getwch();
806 // printf("----\nread wchar_t: %x\n", wc); 911 // printf("----\nread wchar_t: %x\n", (unsigned int)wc);
912
913 if (wc == 0x00 || wc == 0xE0) {
914 // printf("Ignoring scan code: %x\n", (unsigned int)wc);
915 // On Windows, especially with key-repeat, we can get native scancodes even if we've set the console
916 // to ANSI mode. Something, something, terminal not keeping up... These are not valid wide characters,
917 // so we ignore them. Scan codes start with either 0x00 or 0xE0, and have a follow up byte, which we all ignore.
918 // These codes are unique, so we do not risk dropping valid data.
919 _getwch(); // Discard 2nd half of the scan code
920 return 0;
921 }
922
807 if (wc == WEOF) { 923 if (wc == WEOF) {
808 lua_pushnil(L); 924 lua_pushnil(L);
809 lua_pushliteral(L, "read error"); 925 lua_pushliteral(L, "read error");
@@ -824,7 +940,7 @@ static int lst_readkey(lua_State *L) {
824 } 940 }
825 941
826 wchar_t wc2 = _getwch(); 942 wchar_t wc2 = _getwch();
827 // printf("read wchar_t 2: %x\n", wc2); 943 // printf("read wchar_t 2: %x\n", (unsigned int)wc2);
828 if (wc2 == WEOF) { 944 if (wc2 == WEOF) {
829 lua_pushnil(L); 945 lua_pushnil(L);
830 lua_pushliteral(L, "read error"); 946 lua_pushliteral(L, "read error");
@@ -972,6 +1088,7 @@ Get the width of a utf8 character for terminal display.
972@treturn[1] int the display width in columns of the first character in the string (0 for an empty string) 1088@treturn[1] int the display width in columns of the first character in the string (0 for an empty string)
973@treturn[2] nil 1089@treturn[2] nil
974@treturn[2] string error message 1090@treturn[2] string error message
1091@within Terminal_UTF-8
975*/ 1092*/
976int lst_utf8cwidth(lua_State *L) { 1093int lst_utf8cwidth(lua_State *L) {
977 int width = 0; 1094 int width = 0;
@@ -1033,6 +1150,7 @@ Get the width of a utf8 string for terminal display.
1033@treturn[1] int the display width of the string in columns (0 for an empty string) 1150@treturn[1] int the display width of the string in columns (0 for an empty string)
1034@treturn[2] nil 1151@treturn[2] nil
1035@treturn[2] string error message 1152@treturn[2] string error message
1153@within Terminal_UTF-8
1036*/ 1154*/
1037int lst_utf8swidth(lua_State *L) { 1155int lst_utf8swidth(lua_State *L) {
1038 const char *utf8_str; 1156 const char *utf8_str;
@@ -1083,6 +1201,7 @@ int lst_utf8swidth(lua_State *L) {
1083Gets the current console code page (Windows). 1201Gets the current console code page (Windows).
1084@function getconsolecp 1202@function getconsolecp
1085@treturn[1] int the current code page (always 65001 on Posix systems) 1203@treturn[1] int the current code page (always 65001 on Posix systems)
1204@within Terminal_UTF-8
1086*/ 1205*/
1087static int lst_getconsolecp(lua_State *L) { 1206static int lst_getconsolecp(lua_State *L) {
1088 unsigned int cp = 65001; 1207 unsigned int cp = 65001;
@@ -1100,6 +1219,7 @@ Sets the current console code page (Windows).
1100@function setconsolecp 1219@function setconsolecp
1101@tparam int cp the code page to set, use `system.CODEPAGE_UTF8` (65001) for UTF-8 1220@tparam int cp the code page to set, use `system.CODEPAGE_UTF8` (65001) for UTF-8
1102@treturn[1] bool `true` on success (always `true` on Posix systems) 1221@treturn[1] bool `true` on success (always `true` on Posix systems)
1222@within Terminal_UTF-8
1103*/ 1223*/
1104static int lst_setconsolecp(lua_State *L) { 1224static int lst_setconsolecp(lua_State *L) {
1105 unsigned int cp = (unsigned int)luaL_checkinteger(L, 1); 1225 unsigned int cp = (unsigned int)luaL_checkinteger(L, 1);
@@ -1117,6 +1237,7 @@ static int lst_setconsolecp(lua_State *L) {
1117Gets the current console output code page (Windows). 1237Gets the current console output code page (Windows).
1118@function getconsoleoutputcp 1238@function getconsoleoutputcp
1119@treturn[1] int the current code page (always 65001 on Posix systems) 1239@treturn[1] int the current code page (always 65001 on Posix systems)
1240@within Terminal_UTF-8
1120*/ 1241*/
1121static int lst_getconsoleoutputcp(lua_State *L) { 1242static int lst_getconsoleoutputcp(lua_State *L) {
1122 unsigned int cp = 65001; 1243 unsigned int cp = 65001;
@@ -1134,6 +1255,7 @@ Sets the current console output code page (Windows).
1134@function setconsoleoutputcp 1255@function setconsoleoutputcp
1135@tparam int cp the code page to set, use `system.CODEPAGE_UTF8` (65001) for UTF-8 1256@tparam int cp the code page to set, use `system.CODEPAGE_UTF8` (65001) for UTF-8
1136@treturn[1] bool `true` on success (always `true` on Posix systems) 1257@treturn[1] bool `true` on success (always `true` on Posix systems)
1258@within Terminal_UTF-8
1137*/ 1259*/
1138static int lst_setconsoleoutputcp(lua_State *L) { 1260static int lst_setconsoleoutputcp(lua_State *L) {
1139 unsigned int cp = (unsigned int)luaL_checkinteger(L, 1); 1261 unsigned int cp = (unsigned int)luaL_checkinteger(L, 1);
@@ -1157,6 +1279,7 @@ static luaL_Reg func[] = {
1157 { "setconsoleflags", lst_setconsoleflags }, 1279 { "setconsoleflags", lst_setconsoleflags },
1158 { "tcgetattr", lst_tcgetattr }, 1280 { "tcgetattr", lst_tcgetattr },
1159 { "tcsetattr", lst_tcsetattr }, 1281 { "tcsetattr", lst_tcsetattr },
1282 { "detachfds", lst_detachfds },
1160 { "getnonblock", lst_getnonblock }, 1283 { "getnonblock", lst_getnonblock },
1161 { "setnonblock", lst_setnonblock }, 1284 { "setnonblock", lst_setnonblock },
1162 { "_readkey", lst_readkey }, 1285 { "_readkey", lst_readkey },
diff --git a/system/init.lua b/system/init.lua
index a81978e..9b71d4d 100644
--- a/system/init.lua
+++ b/system/init.lua
@@ -10,17 +10,21 @@ local system = require 'system.core'
10--- UTF8 codepage. 10--- UTF8 codepage.
11-- To be used with `system.setconsoleoutputcp` and `system.setconsolecp`. 11-- To be used with `system.setconsoleoutputcp` and `system.setconsolecp`.
12-- @field CODEPAGE_UTF8 The Windows CodePage for UTF8. 12-- @field CODEPAGE_UTF8 The Windows CodePage for UTF8.
13-- @within Terminal_UTF-8
13system.CODEPAGE_UTF8 = 65001 14system.CODEPAGE_UTF8 = 65001
14 15
15do 16do
16 local backup_mt = {} 17 local backup_indicator = {}
17 18
18 --- Returns a backup of terminal settings for stdin/out/err. 19 --- Returns a backup of terminal settings for stdin/out/err.
19 -- Handles terminal/console flags, Windows codepage, and non-block flags on the streams. 20 -- Handles terminal/console flags, Windows codepage, and non-block flags on the streams.
20 -- Backs up terminal/console flags only if a stream is a tty. 21 -- Backs up terminal/console flags only if a stream is a tty.
21 -- @return table with backup of terminal settings 22 -- @return table with backup of terminal settings
23 -- @within Terminal_Backup
22 function system.termbackup() 24 function system.termbackup()
23 local backup = setmetatable({}, backup_mt) 25 local backup = {
26 __type = backup_indicator, -- cannot set a metatable, since autotermrestore uses it for GC
27 }
24 28
25 if system.isatty(io.stdin) then 29 if system.isatty(io.stdin) then
26 backup.console_in = system.getconsoleflags(io.stdin) 30 backup.console_in = system.getconsoleflags(io.stdin)
@@ -50,8 +54,9 @@ do
50 --- Restores terminal settings from a backup 54 --- Restores terminal settings from a backup
51 -- @tparam table backup the backup of terminal settings, see `termbackup`. 55 -- @tparam table backup the backup of terminal settings, see `termbackup`.
52 -- @treturn boolean true 56 -- @treturn boolean true
57 -- @within Terminal_Backup
53 function system.termrestore(backup) 58 function system.termrestore(backup)
54 if getmetatable(backup) ~= backup_mt then 59 if type(backup) ~= "table" or backup.__type ~= backup_indicator then
55 error("arg #1 to termrestore, expected backup table, got " .. type(backup), 2) 60 error("arg #1 to termrestore, expected backup table, got " .. type(backup), 2)
56 end 61 end
57 62
@@ -106,6 +111,7 @@ do -- autotermrestore
106 -- @treturn[1] boolean true 111 -- @treturn[1] boolean true
107 -- @treturn[2] nil if the backup was already created 112 -- @treturn[2] nil if the backup was already created
108 -- @treturn[2] string error message 113 -- @treturn[2] string error message
114 -- @within Terminal_Backup
109 function system.autotermrestore() 115 function system.autotermrestore()
110 if global_backup then 116 if global_backup then
111 return nil, "global terminal backup was already set up" 117 return nil, "global terminal backup was already set up"
@@ -134,6 +140,7 @@ do
134 -- Calls `termbackup` before calling the function and `termrestore` after. 140 -- Calls `termbackup` before calling the function and `termrestore` after.
135 -- @tparam function f function to wrap 141 -- @tparam function f function to wrap
136 -- @treturn function wrapped function 142 -- @treturn function wrapped function
143 -- @within Terminal_Backup
137 function system.termwrap(f) 144 function system.termwrap(f)
138 if type(f) ~= "function" then 145 if type(f) ~= "function" then
139 error("arg #1 to wrap, expected function, got " .. type(f), 2) 146 error("arg #1 to wrap, expected function, got " .. type(f), 2)
@@ -153,6 +160,7 @@ end
153--- Debug function for console flags (Windows). 160--- Debug function for console flags (Windows).
154-- Pretty prints the current flags set for the handle. 161-- Pretty prints the current flags set for the handle.
155-- @param fh file handle (`io.stdin`, `io.stdout`, `io.stderr`) 162-- @param fh file handle (`io.stdin`, `io.stdout`, `io.stderr`)
163-- @within Terminal_Windows
156-- @usage -- Print the flags for stdin/out/err 164-- @usage -- Print the flags for stdin/out/err
157-- system.listconsoleflags(io.stdin) 165-- system.listconsoleflags(io.stdin)
158-- system.listconsoleflags(io.stdout) 166-- system.listconsoleflags(io.stdout)
@@ -192,6 +200,7 @@ end
192--- Debug function for terminal flags (Posix). 200--- Debug function for terminal flags (Posix).
193-- Pretty prints the current flags set for the handle. 201-- Pretty prints the current flags set for the handle.
194-- @param fh file handle (`io.stdin`, `io.stdout`, `io.stderr`) 202-- @param fh file handle (`io.stdin`, `io.stdout`, `io.stderr`)
203-- @within Terminal_Posix
195-- @usage -- Print the flags for stdin/out/err 204-- @usage -- Print the flags for stdin/out/err
196-- system.listconsoleflags(io.stdin) 205-- system.listconsoleflags(io.stdin)
197-- system.listconsoleflags(io.stdout) 206-- system.listconsoleflags(io.stdout)
@@ -230,32 +239,40 @@ end
230do 239do
231 --- Reads a single byte from the console, with a timeout. 240 --- Reads a single byte from the console, with a timeout.
232 -- This function uses `fsleep` to wait until either a byte is available or the timeout is reached. 241 -- This function uses `fsleep` to wait until either a byte is available or the timeout is reached.
233 -- The sleep period is exponentially backing off, starting at 0.0125 seconds, with a maximum of 0.2 seconds. 242 -- The sleep period is exponentially backing off, starting at 0.0125 seconds, with a maximum of 0.1 seconds.
234 -- It returns immediately if a byte is available or if `timeout` is less than or equal to `0`. 243 -- It returns immediately if a byte is available or if `timeout` is less than or equal to `0`.
235 -- 244 --
236 -- Using `system.readansi` is preferred over this function. Since this function can leave stray/invalid 245 -- Using `system.readansi` is preferred over this function. Since this function can leave stray/invalid
237 -- byte-sequences in the input buffer, while `system.readansi` reads full ANSI and UTF8 sequences. 246 -- byte-sequences in the input buffer, while `system.readansi` reads full ANSI and UTF8 sequences.
238 -- @tparam number timeout the timeout in seconds. 247 -- @tparam number timeout the timeout in seconds.
239 -- @tparam[opt=system.sleep] function fsleep the function to call for sleeping. 248 -- @tparam[opt=system.sleep] function fsleep the function to call for sleeping; `ok, err = fsleep(secs)`
240 -- @treturn[1] byte the byte value that was read. 249 -- @treturn[1] byte the byte value that was read.
241 -- @treturn[2] nil if no key was read 250 -- @treturn[2] nil if no key was read
242 -- @treturn[2] string error message; `"timeout"` if the timeout was reached. 251 -- @treturn[2] string error message when the timeout was reached (`"timeout"`), or if `sleep` failed.
252 -- @within Terminal_Input
243 function system.readkey(timeout, fsleep) 253 function system.readkey(timeout, fsleep)
244 if type(timeout) ~= "number" then 254 if type(timeout) ~= "number" then
245 error("arg #1 to readkey, expected timeout in seconds, got " .. type(timeout), 2) 255 error("arg #1 to readkey, expected timeout in seconds, got " .. type(timeout), 2)
246 end 256 end
247 257
248 local interval = 0.0125 258 local interval = 0.0125
249 local key = system._readkey() 259 local ok
260 local key, err = system._readkey()
250 while key == nil and timeout > 0 do 261 while key == nil and timeout > 0 do
251 (fsleep or system.sleep)(math.min(interval, timeout)) 262 if err then
263 return nil, err
264 end
265 ok, err = (fsleep or system.sleep)(math.min(interval, timeout))
266 if not ok then
267 return nil, err
268 end
252 timeout = timeout - interval 269 timeout = timeout - interval
253 interval = math.min(0.2, interval * 2) 270 interval = math.min(0.1, interval * 2)
254 key = system._readkey() 271 key, err = system._readkey()
255 end 272 end
256 273
257 if key then 274 if key or err then
258 return key 275 return key, err
259 end 276 end
260 return nil, "timeout" 277 return nil, "timeout"
261 end 278 end
@@ -264,7 +281,6 @@ end
264 281
265 282
266do 283do
267 local left_over_key
268 local sequence -- table to store the sequence in progress 284 local sequence -- table to store the sequence in progress
269 local utf8_length -- length of utf8 sequence currently being processed 285 local utf8_length -- length of utf8 sequence currently being processed
270 local unpack = unpack or table.unpack 286 local unpack = unpack or table.unpack
@@ -278,10 +294,20 @@ do
278 -- @tparam number timeout the timeout in seconds. 294 -- @tparam number timeout the timeout in seconds.
279 -- @tparam[opt=system.sleep] function fsleep the function to call for sleeping. 295 -- @tparam[opt=system.sleep] function fsleep the function to call for sleeping.
280 -- @treturn[1] string the character that was received (can be multi-byte), or a complete ANSI sequence 296 -- @treturn[1] string the character that was received (can be multi-byte), or a complete ANSI sequence
281 -- @treturn[1] string the type of input: `"char"` for a single key, `"ansi"` for an ANSI sequence 297 -- @treturn[1] string the type of input: `"ctrl"` for 0-31 and 127 bytes, `"char"` for other UTF-8 characters, `"ansi"` for an ANSI sequence
282 -- @treturn[2] nil in case of an error 298 -- @treturn[2] nil in case of an error
283 -- @treturn[2] string error message; `"timeout"` if the timeout was reached. 299 -- @treturn[2] string error message; `"timeout"` if the timeout was reached.
284 -- @treturn[2] string partial result in case of an error while reading a sequence, the sequence so far. 300 -- @treturn[2] string partial result in case of an error while reading a sequence, the sequence so far.
301 -- The function retains its own internal buffer, so on the next call the incomplete buffer is used to
302 -- complete the sequence.
303 -- @within Terminal_Input
304 -- @usage
305 -- local key, keytype = system.readansi(5)
306 -- if keytype == "char" then ... end -- printable character
307 -- if keytype ~= "char" then ... end -- non-printable character or sequence
308 -- if keytype == "ansi" then ... end -- a multi-byte sequence, but not a UTF8 character
309 -- if keytype ~= "ansi" then ... end -- a valid UTF8 character (which includes control characters)
310 -- if keytype == "ctrl" then ... end -- a single-byte ctrl character (0-31, 127)
285 function system.readansi(timeout, fsleep) 311 function system.readansi(timeout, fsleep)
286 if type(timeout) ~= "number" then 312 if type(timeout) ~= "number" then
287 error("arg #1 to readansi, expected timeout in seconds, got " .. type(timeout), 2) 313 error("arg #1 to readansi, expected timeout in seconds, got " .. type(timeout), 2)
@@ -292,40 +318,54 @@ do
292 318
293 if not sequence then 319 if not sequence then
294 -- no sequence in progress, read a key 320 -- no sequence in progress, read a key
295 321 local err
296 if left_over_key then 322 key, err = system.readkey(timeout, fsleep)
297 -- we still have a cached key from the last call 323 if key == nil then -- timeout or error
298 key = left_over_key 324 return nil, err
299 left_over_key = nil
300 else
301 -- read a new key
302 local err
303 key, err = system.readkey(timeout, fsleep)
304 if key == nil then -- timeout or error
305 return nil, err
306 end
307 end 325 end
308 326
309 if key == 27 then 327 if key == 27 then
310 -- looks like an ansi escape sequence, immediately read next char 328 -- looks like an ansi escape sequence, immediately read next char
311 -- as an heuristic against manually typing escape sequences 329 -- as an heuristic against manually typing escape sequences
312 local key2 = system.readkey(0, fsleep) 330 local key2 = system.readkey(0, fsleep)
313 if key2 ~= 91 and key2 ~= 79 then -- we expect either "[" or "O" for an ANSI sequence 331 if key2 == nil then
314 -- not the expected [ or O character, so we return the key as is 332 -- no key available, return the escape key, on its own
315 -- and store the extra key read for the next call 333 sequence = nil
316 left_over_key = key2 334 return string.char(key), "ctrl"
317 return string.char(key), "char" 335
336 elseif key2 == 91 then
337 -- "[" means it is for sure an ANSI sequence
338 sequence = { key, key2 }
339
340 elseif key2 == 79 then
341 -- "O" means it is either an ANSI sequence or just an <alt>+O key stroke
342 -- check if there is yet another byte available
343 local key3 = system.readkey(0, fsleep)
344 if key3 == nil then
345 -- no key available, return the <alt>O key stroke, report as ANSI
346 sequence = nil
347 return string.char(key, key2), "ansi"
348 end
349 -- it's an ANSI sequence, marked with <ESC>O
350 if (key3 >= 65 and key3 <= 90) or (key3 >= 97 and key3 <= 126) then
351 -- end of sequence, return the full sequence
352 return string.char(key, key2, key3), "ansi"
353 end
354 sequence = { key, key2, key3 }
355
356 else
357 -- not an ANSI sequence, but an <alt>+<key2> key stroke, so report as ANSI
358 sequence = nil
359 return string.char(key, key2), "ansi"
318 end 360 end
319 361
320 -- escape sequence detected
321 sequence = { key, key2 }
322 else 362 else
323 -- check UTF8 length 363 -- check UTF8 length
324 utf8_length = key < 128 and 1 or key < 224 and 2 or key < 240 and 3 or key < 248 and 4 364 utf8_length = key < 128 and 1 or key < 224 and 2 or key < 240 and 3 or key < 248 and 4
325 if utf8_length == 1 then 365 if utf8_length == 1 then
326 -- single byte character 366 -- single byte character
327 utf8_length = nil 367 utf8_length = nil
328 return string.char(key), "char" 368 return string.char(key), ((key <= 31 or key == 127) and "ctrl" or "char")
329 else 369 else
330 -- UTF8 sequence detected 370 -- UTF8 sequence detected
331 sequence = { key } 371 sequence = { key }