diff options
-rw-r--r-- | CHANGELOG.md | 20 | ||||
-rw-r--r-- | doc_topics/03-terminal.md | 2 | ||||
-rw-r--r-- | docs/classes/bitflags.html | 2 | ||||
-rw-r--r-- | docs/examples/compat.lua.html | 2 | ||||
-rw-r--r-- | docs/examples/flag_debugging.lua.html | 2 | ||||
-rw-r--r-- | docs/examples/password_input.lua.html | 2 | ||||
-rw-r--r-- | docs/examples/read.lua.html | 2 | ||||
-rw-r--r-- | docs/examples/readline.lua.html | 2 | ||||
-rw-r--r-- | docs/examples/spinner.lua.html | 2 | ||||
-rw-r--r-- | docs/examples/spiral_snake.lua.html | 2 | ||||
-rw-r--r-- | docs/examples/terminalsize.lua.html | 2 | ||||
-rw-r--r-- | docs/index.html | 2 | ||||
-rw-r--r-- | docs/modules/system.html | 59 | ||||
-rw-r--r-- | docs/topics/01-introduction.md.html | 2 | ||||
-rw-r--r-- | docs/topics/02-development.md.html | 2 | ||||
-rw-r--r-- | docs/topics/03-terminal.md.html | 4 | ||||
-rw-r--r-- | docs/topics/CHANGELOG.md.html | 28 | ||||
-rw-r--r-- | docs/topics/LICENSE.md.html | 2 | ||||
-rw-r--r-- | rockspecs/luasystem-0.6.0-1.rockspec | 85 | ||||
-rw-r--r-- | rockspecs/luasystem-0.6.1-1.rockspec | 85 | ||||
-rw-r--r-- | rockspecs/luasystem-0.6.2-1.rockspec | 85 | ||||
-rw-r--r-- | spec/04-term_spec.lua | 67 | ||||
-rw-r--r-- | src/core.c | 2 | ||||
-rw-r--r-- | src/term.c | 100 | ||||
-rw-r--r-- | system/init.lua | 70 |
25 files changed, 580 insertions, 53 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index 1c9e15e..d4107d6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md | |||
@@ -27,6 +27,22 @@ 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.2, released 15-Apr-2025 | ||
31 | |||
32 | - Fix: autotermrestore didn't work because its metatable was overwritten. | ||
33 | |||
34 | ### version 0.6.1, released 13-Apr-2025 | ||
35 | |||
36 | - Docs: document readansi internal buffer for incomplete sequences. | ||
37 | - Fix: ensure to properly parse `<alt>+key` key presses | ||
38 | |||
39 | ### version 0.6.0, released 10-Apr-2025 | ||
40 | |||
41 | - Fix: when sleep returns an error, pass that on in `readkey`. | ||
42 | - Feat: added `detachfds` which will create separate file descriptions for `stdout` | ||
43 | and `stderr` to ensure that related settings (eg. non-blocking flag) will not be shared | ||
44 | amongst those streams and `stdin`. | ||
45 | |||
30 | ### version 0.5.1, released 12-Mar-2025 | 46 | ### version 0.5.1, released 12-Mar-2025 |
31 | 47 | ||
32 | - Fix: on older unixes with glibc < 2.25, fall back to `/dev/urandom` | 48 | - Fix: on older unixes with glibc < 2.25, fall back to `/dev/urandom` |
@@ -38,12 +54,12 @@ The scope of what is covered by the version number excludes: | |||
38 | - Feat: allow passing in a sleep function to `readkey` and `readansi` | 54 | - Feat: allow passing in a sleep function to `readkey` and `readansi` |
39 | - Fix: NetBSD fix compilation, undeclared directives | 55 | - Fix: NetBSD fix compilation, undeclared directives |
40 | - Refactor: random bytes; remove deprecated API usage on Windows, move to | 56 | - Refactor: random bytes; remove deprecated API usage on Windows, move to |
41 | binary api instead of /dev/urandom file on linux and bsd | 57 | binary api instead of `/dev/urandom` file on linux and bsd |
42 | 58 | ||
43 | ### version 0.4.5, released 18-Dec-2024 | 59 | ### version 0.4.5, released 18-Dec-2024 |
44 | 60 | ||
45 | - Fix: suppress a warning when building with clang | 61 | - 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). | 62 | - Fix: do not rely on `luaconf.h` to include `limits.h`, fixes builds with latest LuaJIT (#38). |
47 | 63 | ||
48 | ### version 0.4.4, released 03-Sep-2024 | 64 | ### version 0.4.4, released 03-Sep-2024 |
49 | 65 | ||
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..3e99d0e 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-04-15 13:14:45 </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..b57a912 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-04-15 13:14:45 </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..616ce69 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-04-15 13:14:45 </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..faa0473 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-04-15 13:14:45 </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..1a9648c 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-04-15 13:14:45 </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..fad5df7 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-04-15 13:14:45 </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..c776106 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-04-15 13:14:45 </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..a102103 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-04-15 13:14:45 </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..9aca861 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-04-15 13:14:45 </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..6461689 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-04-15 13:14:45 </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..0934b35 100644 --- a/docs/modules/system.html +++ b/docs/modules/system.html | |||
@@ -124,6 +124,10 @@ | |||
124 | <td class="summary">Backs up terminal settings and restores them on application exit.</td> | 124 | <td class="summary">Backs up terminal settings and restores them on application exit.</td> |
125 | </tr> | 125 | </tr> |
126 | <tr> | 126 | <tr> |
127 | <td class="name" nowrap><a href="#detachfds">detachfds ()</a></td> | ||
128 | <td class="summary">Creates new file descriptions for <code>stdout</code> and <code>stderr</code>.</td> | ||
129 | </tr> | ||
130 | <tr> | ||
127 | <td class="name" nowrap><a href="#getconsolecp">getconsolecp ()</a></td> | 131 | <td class="name" nowrap><a href="#getconsolecp">getconsolecp ()</a></td> |
128 | <td class="summary">Gets the current console code page (Windows).</td> | 132 | <td class="summary">Gets the current console code page (Windows).</td> |
129 | </tr> | 133 | </tr> |
@@ -498,6 +502,40 @@ sequences will be buffered internally and returned one byte at a time. | |||
498 | 502 | ||
499 | </dd> | 503 | </dd> |
500 | <dt> | 504 | <dt> |
505 | <a name = "detachfds"></a> | ||
506 | <strong>detachfds ()</strong> | ||
507 | </dt> | ||
508 | <dd> | ||
509 | Creates new file descriptions for <code>stdout</code> and <code>stderr</code>. | ||
510 | Even if the file descriptors are unique, they still might point to the same | ||
511 | file description, and hence share settings like <code>O_NONBLOCK</code>. This means that | ||
512 | if one of them is set to non-blocking, the other will be as well. This can | ||
513 | lead to unexpected behavior.</p> | ||
514 | |||
515 | <p>This function is used to detach <code>stdout</code> and <code>stderr</code> from the original | ||
516 | file descriptions, and create new file descriptions for them. This allows | ||
517 | independent control of flags (e.g., <code>O_NONBLOCK</code>) on <code>stdout</code> and <code>stderr</code>, | ||
518 | avoiding shared side effects.</p> | ||
519 | |||
520 | <p>Does not modify <code>stdin</code> (fd 0), and does nothing on Windows. | ||
521 | |||
522 | |||
523 | |||
524 | <h3>Returns:</h3> | ||
525 | <ol> | ||
526 | |||
527 | boolean <code>true</code> on success, or throws an error on failure. | ||
528 | </ol> | ||
529 | |||
530 | |||
531 | <h3>See also:</h3> | ||
532 | <ul> | ||
533 | <a href="../modules/system.html#setnonblock">setnonblock</a> | ||
534 | </ul> | ||
535 | |||
536 | |||
537 | </dd> | ||
538 | <dt> | ||
501 | <a name = "getconsolecp"></a> | 539 | <a name = "getconsolecp"></a> |
502 | <strong>getconsolecp ()</strong> | 540 | <strong>getconsolecp ()</strong> |
503 | </dt> | 541 | </dt> |
@@ -632,6 +670,10 @@ for more information on the flags. | |||
632 | </ol> | 670 | </ol> |
633 | 671 | ||
634 | 672 | ||
673 | <h3>See also:</h3> | ||
674 | <ul> | ||
675 | <a href="../modules/system.html#setnonblock">setnonblock</a> | ||
676 | </ul> | ||
635 | 677 | ||
636 | 678 | ||
637 | </dd> | 679 | </dd> |
@@ -772,7 +814,9 @@ system.<span class="function-name">listconsoleflags</span>(<span class="global"> | |||
772 | error message; <code>"timeout"</code> if the timeout was reached.</li> | 814 | error message; <code>"timeout"</code> if the timeout was reached.</li> |
773 | <li> | 815 | <li> |
774 | <span class="types"><a class="type" href="https://www.lua.org/manual/5.4/manual.html#6.4">string</a></span> | 816 | <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> | 817 | partial result in case of an error while reading a sequence, the sequence so far. |
818 | The function retains its own internal buffer, so on the next call the incomplete buffer is used to | ||
819 | complete the sequence.</li> | ||
776 | </ol> | 820 | </ol> |
777 | 821 | ||
778 | 822 | ||
@@ -801,7 +845,7 @@ system.<span class="function-name">listconsoleflags</span>(<span class="global"> | |||
801 | </li> | 845 | </li> |
802 | <li><span class="parameter">fsleep</span> | 846 | <li><span class="parameter">fsleep</span> |
803 | <span class="types"><span class="type">function</span></span> | 847 | <span class="types"><span class="type">function</span></span> |
804 | the function to call for sleeping. | 848 | the function to call for sleeping; <code>ok, err = fsleep(secs)</code> |
805 | (<em>default</em> system.sleep) | 849 | (<em>default</em> system.sleep) |
806 | </li> | 850 | </li> |
807 | </ul> | 851 | </ul> |
@@ -819,7 +863,7 @@ system.<span class="function-name">listconsoleflags</span>(<span class="global"> | |||
819 | if no key was read</li> | 863 | if no key was read</li> |
820 | <li> | 864 | <li> |
821 | <span class="types"><a class="type" href="https://www.lua.org/manual/5.4/manual.html#6.4">string</a></span> | 865 | <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>"timeout"</code> if the timeout was reached.</li> | 866 | error message when the timeout was reached (<code>"timeout"</code>), or if <a href="../modules/system.html#sleep">sleep</a> failed.</li> |
823 | </ol> | 867 | </ol> |
824 | 868 | ||
825 | 869 | ||
@@ -947,6 +991,7 @@ system.<span class="function-name">listconsoleflags</span>(<span class="global"> | |||
947 | </dt> | 991 | </dt> |
948 | <dd> | 992 | <dd> |
949 | Enables or disables non-blocking mode for a file (Posix). | 993 | Enables or disables non-blocking mode for a file (Posix). |
994 | Check <a href="../modules/system.html#detachfds">detachfds</a> in case there are shared file descriptions. | ||
950 | 995 | ||
951 | 996 | ||
952 | <h3>Parameters:</h3> | 997 | <h3>Parameters:</h3> |
@@ -985,13 +1030,15 @@ system.<span class="function-name">listconsoleflags</span>(<span class="global"> | |||
985 | 1030 | ||
986 | <h3>See also:</h3> | 1031 | <h3>See also:</h3> |
987 | <ul> | 1032 | <ul> |
988 | <a href="../modules/system.html#getnonblock">getnonblock</a> | 1033 | <li><a href="../modules/system.html#getnonblock">getnonblock</a></li> |
1034 | <li><a href="../modules/system.html#detachfds">detachfds</a></li> | ||
989 | </ul> | 1035 | </ul> |
990 | 1036 | ||
991 | <h3>Usage:</h3> | 1037 | <h3>Usage:</h3> |
992 | <ul> | 1038 | <ul> |
993 | <pre class="example"><span class="keyword">local</span> sys = <span class="global">require</span>(<span class="string">'system'</span>) | 1039 | <pre class="example"><span class="keyword">local</span> sys = <span class="global">require</span>(<span class="string">'system'</span>) |
994 | 1040 | sys.<span class="function-name">detachfds</span>() <span class="comment">-- detach stdout and stderr, so only stdin becomes non-blocking | |
1041 | </span> | ||
995 | <span class="comment">-- set io.stdin to non-blocking mode | 1042 | <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) | 1043 | </span><span class="keyword">local</span> old_setting = sys.<span class="function-name">getnonblock</span>(<span class="global">io</span>.stdin) |
997 | sys.<span class="function-name">setnonblock</span>(<span class="global">io</span>.stdin, <span class="keyword">true</span>) | 1044 | sys.<span class="function-name">setnonblock</span>(<span class="global">io</span>.stdin, <span class="keyword">true</span>) |
@@ -1422,7 +1469,7 @@ This function will sleep, without doing a busy-loop and wasting CPU cycles. | |||
1422 | </div> <!-- id="main" --> | 1469 | </div> <!-- id="main" --> |
1423 | <div id="about"> | 1470 | <div id="about"> |
1424 | <i>generated by <a href="http://github.com/lunarmodules/LDoc">LDoc 1.5.0</a></i> | 1471 | <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> | 1472 | <i style="float:right;">Last updated 2025-04-15 13:14:45 </i> |
1426 | </div> <!-- id="about" --> | 1473 | </div> <!-- id="about" --> |
1427 | </div> <!-- id="container" --> | 1474 | </div> <!-- id="container" --> |
1428 | </body> | 1475 | </body> |
diff --git a/docs/topics/01-introduction.md.html b/docs/topics/01-introduction.md.html index f0f627d..5c62ad3 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-04-15 13:14:45 </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..197fde7 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-04-15 13:14:45 </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..84465af 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) |
199 | sys.<span class="function-name">tcsetattr</span>(<span class="global">io</span>.stdin, sys.TCSANOW, { | 201 | sys.<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-04-15 13:14:45 </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..8a26156 100644 --- a/docs/topics/CHANGELOG.md.html +++ b/docs/topics/CHANGELOG.md.html | |||
@@ -106,6 +106,28 @@ | |||
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.2, released 15-Apr-2025</h3> | ||
110 | |||
111 | <ul> | ||
112 | <li>Fix: autotermrestore didn't work because its metatable was overwritten.</li> | ||
113 | </ul> | ||
114 | |||
115 | <h3>version 0.6.1, released 13-Apr-2025</h3> | ||
116 | |||
117 | <ul> | ||
118 | <li>Docs: document readansi internal buffer for incomplete sequences.</li> | ||
119 | <li>Fix: ensure to properly parse <code><alt>+key</code> key presses</li> | ||
120 | </ul> | ||
121 | |||
122 | <h3>version 0.6.0, released 10-Apr-2025</h3> | ||
123 | |||
124 | <ul> | ||
125 | <li>Fix: when sleep returns an error, pass that on in <code>readkey</code>.</li> | ||
126 | <li>Feat: added <code>detachfds</code> which will create separate file descriptions for <code>stdout</code> | ||
127 | and <code>stderr</code> to ensure that related settings (eg. non-blocking flag) will not be shared | ||
128 | amongst those streams and <code>stdin</code>.</li> | ||
129 | </ul> | ||
130 | |||
109 | <h3>version 0.5.1, released 12-Mar-2025</h3> | 131 | <h3>version 0.5.1, released 12-Mar-2025</h3> |
110 | 132 | ||
111 | <ul> | 133 | <ul> |
@@ -120,14 +142,14 @@ | |||
120 | <li>Feat: allow passing in a sleep function to <code>readkey</code> and <code>readansi</code></li> | 142 | <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> | 143 | <li>Fix: NetBSD fix compilation, undeclared directives</li> |
122 | <li>Refactor: random bytes; remove deprecated API usage on Windows, move to | 144 | <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> | 145 | binary api instead of <code>/dev/urandom</code> file on linux and bsd</li> |
124 | </ul> | 146 | </ul> |
125 | 147 | ||
126 | <h3>version 0.4.5, released 18-Dec-2024</h3> | 148 | <h3>version 0.4.5, released 18-Dec-2024</h3> |
127 | 149 | ||
128 | <ul> | 150 | <ul> |
129 | <li>Fix: suppress a warning when building with clang</li> | 151 | <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> | 152 | <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> | 153 | </ul> |
132 | 154 | ||
133 | <h3>version 0.4.4, released 03-Sep-2024</h3> | 155 | <h3>version 0.4.4, released 03-Sep-2024</h3> |
@@ -199,7 +221,7 @@ | |||
199 | </div> <!-- id="main" --> | 221 | </div> <!-- id="main" --> |
200 | <div id="about"> | 222 | <div id="about"> |
201 | <i>generated by <a href="http://github.com/lunarmodules/LDoc">LDoc 1.5.0</a></i> | 223 | <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> | 224 | <i style="float:right;">Last updated 2025-04-15 13:14:45 </i> |
203 | </div> <!-- id="about" --> | 225 | </div> <!-- id="about" --> |
204 | </div> <!-- id="container" --> | 226 | </div> <!-- id="container" --> |
205 | </body> | 227 | </body> |
diff --git a/docs/topics/LICENSE.md.html b/docs/topics/LICENSE.md.html index 612cdb9..198c238 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-04-15 13:14:45 </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 @@ | |||
1 | local package_name = "luasystem" | ||
2 | local package_version = "0.6.0" | ||
3 | local rockspec_revision = "1" | ||
4 | local github_account_name = "lunarmodules" | ||
5 | local github_repo_name = "luasystem" | ||
6 | |||
7 | |||
8 | package = package_name | ||
9 | version = package_version.."-"..rockspec_revision | ||
10 | |||
11 | source = { | ||
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 | |||
17 | description = { | ||
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 | |||
26 | dependencies = { | ||
27 | 'lua >= 5.1', | ||
28 | } | ||
29 | |||
30 | local 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 | } | ||
71 | end | ||
72 | |||
73 | build = { | ||
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 @@ | |||
1 | local package_name = "luasystem" | ||
2 | local package_version = "0.6.1" | ||
3 | local rockspec_revision = "1" | ||
4 | local github_account_name = "lunarmodules" | ||
5 | local github_repo_name = "luasystem" | ||
6 | |||
7 | |||
8 | package = package_name | ||
9 | version = package_version.."-"..rockspec_revision | ||
10 | |||
11 | source = { | ||
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 | |||
17 | description = { | ||
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 | |||
26 | dependencies = { | ||
27 | 'lua >= 5.1', | ||
28 | } | ||
29 | |||
30 | local 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 | } | ||
71 | end | ||
72 | |||
73 | build = { | ||
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 @@ | |||
1 | local package_name = "luasystem" | ||
2 | local package_version = "0.6.2" | ||
3 | local rockspec_revision = "1" | ||
4 | local github_account_name = "lunarmodules" | ||
5 | local github_repo_name = "luasystem" | ||
6 | |||
7 | |||
8 | package = package_name | ||
9 | version = package_version.."-"..rockspec_revision | ||
10 | |||
11 | source = { | ||
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 | |||
17 | description = { | ||
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 | |||
26 | dependencies = { | ||
27 | 'lua >= 5.1', | ||
28 | } | ||
29 | |||
30 | local 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 | } | ||
71 | end | ||
72 | |||
73 | build = { | ||
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..0ce0033 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 | ||
@@ -902,10 +928,31 @@ describe("Terminal:", function() | |||
902 | end) | 928 | end) |
903 | 929 | ||
904 | 930 | ||
931 | it("reads ANSI escape sequences, just an '<esc>O' (<alt><O> key press)", function() | ||
932 | setbuffer("\27O") | ||
933 | assert.are.same({"\27O", "ansi"}, {system.readansi(0)}) | ||
934 | end) | ||
935 | |||
936 | |||
937 | it("reads <alt>+key key presses; <esc>+<key>", function() | ||
938 | setbuffer("\27a\27b\27c\27d") | ||
939 | assert.are.same({"\27a", "ansi"}, {system.readansi(0)}) | ||
940 | assert.are.same({"\27b", "ansi"}, {system.readansi(0)}) | ||
941 | assert.are.same({"\27c", "ansi"}, {system.readansi(0)}) | ||
942 | assert.are.same({"\27d", "ansi"}, {system.readansi(0)}) | ||
943 | end) | ||
944 | |||
945 | |||
946 | it("reads <ctrl><alt>[ key press; <esc>+<esc>", function() | ||
947 | setbuffer("\27\27\27\27") | ||
948 | assert.are.same({"\27\27", "ansi"}, {system.readansi(0)}) | ||
949 | assert.are.same({"\27\27", "ansi"}, {system.readansi(0)}) | ||
950 | end) | ||
951 | |||
952 | |||
905 | it("returns a single <esc> character if no sequence is found", function() | 953 | it("returns a single <esc> character if no sequence is found", function() |
906 | setbuffer("\27\27[A") | 954 | setbuffer("\27") |
907 | assert.are.same({"\27", "char"}, {system.readansi(0)}) | 955 | assert.are.same({"\27", "char"}, {system.readansi(0)}) |
908 | assert.are.same({"\27[A", "ansi"}, {system.readansi(0)}) | ||
909 | end) | 956 | end) |
910 | 957 | ||
911 | 958 | ||
@@ -919,6 +966,22 @@ describe("Terminal:", function() | |||
919 | assert.is.near(1, timing, 0.5) -- this also works for MacOS in CI | 966 | assert.is.near(1, timing, 0.5) -- this also works for MacOS in CI |
920 | end) | 967 | end) |
921 | 968 | ||
969 | |||
970 | it("incomplete ANSI sequences will be completed on next call", function() | ||
971 | setbuffer("\27[") | ||
972 | assert.are.same({nil, "timeout", "\27["}, {system.readansi(0)}) | ||
973 | setbuffer("A") | ||
974 | assert.are.same({"\27[A", "ansi"}, {system.readansi(0)}) | ||
975 | end) | ||
976 | |||
977 | |||
978 | it("incomplete UTF8 sequences will be completed on next call", function() | ||
979 | setbuffer(string.char(240, 159)) | ||
980 | assert.are.same({nil, "timeout", string.char(240, 159)}, {system.readansi(0)}) | ||
981 | setbuffer(string.char(154, 128)) | ||
982 | assert.are.same({"🚀", "char"}, {system.readansi(0)}) | ||
983 | end) | ||
984 | |||
922 | end) | 985 | end) |
923 | 986 | ||
924 | end) | 987 | end) |
@@ -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.2" |
8 | 8 | ||
9 | #ifdef _WIN32 | 9 | #ifdef _WIN32 |
10 | #define LUAEXPORT __declspec(dllexport) | 10 | #define LUAEXPORT __declspec(dllexport) |
@@ -649,8 +649,104 @@ static int lst_tcsetattr(lua_State *L) | |||
649 | 649 | ||
650 | 650 | ||
651 | 651 | ||
652 | #ifndef _WIN32 | ||
653 | /* | ||
654 | reopen FDs for independent file descriptions. | ||
655 | */ | ||
656 | static void reopen_fd(lua_State *L, int fd, int flags) { | ||
657 | char path[64]; | ||
658 | int newfd = -1; | ||
659 | |||
660 | if (fd != STDOUT_FILENO && fd != STDERR_FILENO) { | ||
661 | luaL_error(L, "Invalid file descriptor: %d. Only stdout (1) and stderr (2) are supported.", fd); | ||
662 | } | ||
663 | |||
664 | const char *fallback_path = (fd == STDOUT_FILENO) ? "/dev/stdout" : | ||
665 | (fd == STDERR_FILENO) ? "/dev/stderr" : NULL; | ||
666 | |||
667 | // If fd is a terminal, reopen its actual device (e.g. /dev/ttys003) | ||
668 | // Works on all POSIX platforms that have terminals (macOS, Linux, BSD, etc.) | ||
669 | if (isatty(fd)) { | ||
670 | const char *tty = ttyname(fd); | ||
671 | if (tty) { | ||
672 | newfd = open(tty, flags); | ||
673 | } | ||
674 | } | ||
675 | |||
676 | if (newfd < 0) { | ||
677 | // For non-tty: try /dev/fd/N — POSIX-compliant and standard on macOS, Linux, BSD. | ||
678 | // This gives a new file description even if the target is a file or pipe. | ||
679 | snprintf(path, sizeof(path), "/dev/fd/%d", fd); | ||
680 | newfd = open(path, flags); | ||
681 | } | ||
682 | |||
683 | if (newfd < 0) { | ||
684 | // Fallback: for platforms/environments where /dev/fd/N doesn't exist. | ||
685 | // /dev/stdout and /dev/stderr are standard on Linux, but may not create new descriptions. | ||
686 | if (fallback_path) { | ||
687 | newfd = open(fallback_path, flags); | ||
688 | } | ||
689 | } | ||
690 | |||
691 | if (newfd < 0) { | ||
692 | // All attempts failed — raise error with detailed info (call will not return) | ||
693 | luaL_error(L, "Failed to reopen fd %d: tried ttyname(), /dev/fd/%d, and fallback %s: %s", | ||
694 | fd, fd, fallback_path ? fallback_path : "(none)", strerror(errno)); | ||
695 | } | ||
696 | |||
697 | // Replace the original fd with the new one | ||
698 | if (dup2(newfd, fd) < 0) { | ||
699 | close(newfd); | ||
700 | luaL_error(L, "dup2 failed for fd %d: %s", fd, strerror(errno)); | ||
701 | } | ||
702 | |||
703 | close(newfd); // Close the new fd, as dup2 has replaced the original fd with it | ||
704 | } | ||
705 | #endif | ||
706 | |||
707 | |||
708 | |||
709 | /*** | ||
710 | Creates new file descriptions for `stdout` and `stderr`. | ||
711 | Even if the file descriptors are unique, they still might point to the same | ||
712 | file description, and hence share settings like `O_NONBLOCK`. This means that | ||
713 | if one of them is set to non-blocking, the other will be as well. This can | ||
714 | lead to unexpected behavior. | ||
715 | |||
716 | This function is used to detach `stdout` and `stderr` from the original | ||
717 | file descriptions, and create new file descriptions for them. This allows | ||
718 | independent control of flags (e.g., `O_NONBLOCK`) on `stdout` and `stderr`, | ||
719 | avoiding shared side effects. | ||
720 | |||
721 | Does not modify `stdin` (fd 0), and does nothing on Windows. | ||
722 | @function detachfds | ||
723 | @return boolean `true` on success, or throws an error on failure. | ||
724 | @see setnonblock | ||
725 | */ | ||
726 | static int lst_detachfds(lua_State *L) { | ||
727 | static int already_detached = 0; // detaching is once per process(not per thread or Lua state) | ||
728 | if (already_detached) { | ||
729 | lua_pushnil(L); | ||
730 | lua_pushliteral(L, "stdout and stderr already detached"); | ||
731 | return 1; | ||
732 | } | ||
733 | already_detached = 1; | ||
734 | |||
735 | #ifndef _WIN32 | ||
736 | // Reopen stdout and stderr with new file descriptions | ||
737 | reopen_fd(L, STDOUT_FILENO, O_WRONLY); | ||
738 | reopen_fd(L, STDERR_FILENO, O_WRONLY); | ||
739 | #endif | ||
740 | |||
741 | lua_pushboolean(L, 1); | ||
742 | return 1; | ||
743 | } | ||
744 | |||
745 | |||
746 | |||
652 | /*** | 747 | /*** |
653 | Enables or disables non-blocking mode for a file (Posix). | 748 | Enables or disables non-blocking mode for a file (Posix). |
749 | Check `detachfds` in case there are shared file descriptions. | ||
654 | @function setnonblock | 750 | @function setnonblock |
655 | @tparam file fd file handle to operate on, one of `io.stdin`, `io.stdout`, `io.stderr` | 751 | @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. | 752 | @tparam boolean make_non_block a truthy value will enable non-blocking mode, a falsy value will disable it. |
@@ -659,8 +755,10 @@ Enables or disables non-blocking mode for a file (Posix). | |||
659 | @treturn[2] string error message | 755 | @treturn[2] string error message |
660 | @treturn[2] int errnum | 756 | @treturn[2] int errnum |
661 | @see getnonblock | 757 | @see getnonblock |
758 | @see detachfds | ||
662 | @usage | 759 | @usage |
663 | local sys = require('system') | 760 | local sys = require('system') |
761 | sys.detachfds() -- detach stdout and stderr, so only stdin becomes non-blocking | ||
664 | 762 | ||
665 | -- set io.stdin to non-blocking mode | 763 | -- set io.stdin to non-blocking mode |
666 | local old_setting = sys.getnonblock(io.stdin) | 764 | local old_setting = sys.getnonblock(io.stdin) |
@@ -717,6 +815,7 @@ Gets non-blocking mode status for a file (Posix). | |||
717 | @treturn[2] nil | 815 | @treturn[2] nil |
718 | @treturn[2] string error message | 816 | @treturn[2] string error message |
719 | @treturn[2] int errnum | 817 | @treturn[2] int errnum |
818 | @see setnonblock | ||
720 | */ | 819 | */ |
721 | static int lst_getnonblock(lua_State *L) | 820 | static int lst_getnonblock(lua_State *L) |
722 | { | 821 | { |
@@ -1157,6 +1256,7 @@ static luaL_Reg func[] = { | |||
1157 | { "setconsoleflags", lst_setconsoleflags }, | 1256 | { "setconsoleflags", lst_setconsoleflags }, |
1158 | { "tcgetattr", lst_tcgetattr }, | 1257 | { "tcgetattr", lst_tcgetattr }, |
1159 | { "tcsetattr", lst_tcsetattr }, | 1258 | { "tcsetattr", lst_tcsetattr }, |
1259 | { "detachfds", lst_detachfds }, | ||
1160 | { "getnonblock", lst_getnonblock }, | 1260 | { "getnonblock", lst_getnonblock }, |
1161 | { "setnonblock", lst_setnonblock }, | 1261 | { "setnonblock", lst_setnonblock }, |
1162 | { "_readkey", lst_readkey }, | 1262 | { "_readkey", lst_readkey }, |
diff --git a/system/init.lua b/system/init.lua index a81978e..a9f57e9 100644 --- a/system/init.lua +++ b/system/init.lua | |||
@@ -13,14 +13,16 @@ local system = require 'system.core' | |||
13 | system.CODEPAGE_UTF8 = 65001 | 13 | system.CODEPAGE_UTF8 = 65001 |
14 | 14 | ||
15 | do | 15 | do |
16 | local backup_mt = {} | 16 | local backup_indicator = {} |
17 | 17 | ||
18 | --- Returns a backup of terminal settings for stdin/out/err. | 18 | --- Returns a backup of terminal settings for stdin/out/err. |
19 | -- Handles terminal/console flags, Windows codepage, and non-block flags on the streams. | 19 | -- 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. | 20 | -- Backs up terminal/console flags only if a stream is a tty. |
21 | -- @return table with backup of terminal settings | 21 | -- @return table with backup of terminal settings |
22 | function system.termbackup() | 22 | function system.termbackup() |
23 | local backup = setmetatable({}, backup_mt) | 23 | local backup = { |
24 | __type = backup_indicator, -- cannot set a metatable, since autotermrestore uses it for GC | ||
25 | } | ||
24 | 26 | ||
25 | if system.isatty(io.stdin) then | 27 | if system.isatty(io.stdin) then |
26 | backup.console_in = system.getconsoleflags(io.stdin) | 28 | backup.console_in = system.getconsoleflags(io.stdin) |
@@ -51,7 +53,7 @@ do | |||
51 | -- @tparam table backup the backup of terminal settings, see `termbackup`. | 53 | -- @tparam table backup the backup of terminal settings, see `termbackup`. |
52 | -- @treturn boolean true | 54 | -- @treturn boolean true |
53 | function system.termrestore(backup) | 55 | function system.termrestore(backup) |
54 | if getmetatable(backup) ~= backup_mt then | 56 | if type(backup) ~= "table" or backup.__type ~= backup_indicator then |
55 | error("arg #1 to termrestore, expected backup table, got " .. type(backup), 2) | 57 | error("arg #1 to termrestore, expected backup table, got " .. type(backup), 2) |
56 | end | 58 | end |
57 | 59 | ||
@@ -236,10 +238,10 @@ do | |||
236 | -- Using `system.readansi` is preferred over this function. Since this function can leave stray/invalid | 238 | -- 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. | 239 | -- byte-sequences in the input buffer, while `system.readansi` reads full ANSI and UTF8 sequences. |
238 | -- @tparam number timeout the timeout in seconds. | 240 | -- @tparam number timeout the timeout in seconds. |
239 | -- @tparam[opt=system.sleep] function fsleep the function to call for sleeping. | 241 | -- @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. | 242 | -- @treturn[1] byte the byte value that was read. |
241 | -- @treturn[2] nil if no key was read | 243 | -- @treturn[2] nil if no key was read |
242 | -- @treturn[2] string error message; `"timeout"` if the timeout was reached. | 244 | -- @treturn[2] string error message when the timeout was reached (`"timeout"`), or if `sleep` failed. |
243 | function system.readkey(timeout, fsleep) | 245 | function system.readkey(timeout, fsleep) |
244 | if type(timeout) ~= "number" then | 246 | if type(timeout) ~= "number" then |
245 | error("arg #1 to readkey, expected timeout in seconds, got " .. type(timeout), 2) | 247 | error("arg #1 to readkey, expected timeout in seconds, got " .. type(timeout), 2) |
@@ -248,7 +250,10 @@ do | |||
248 | local interval = 0.0125 | 250 | local interval = 0.0125 |
249 | local key = system._readkey() | 251 | local key = system._readkey() |
250 | while key == nil and timeout > 0 do | 252 | while key == nil and timeout > 0 do |
251 | (fsleep or system.sleep)(math.min(interval, timeout)) | 253 | local ok, err = (fsleep or system.sleep)(math.min(interval, timeout)) |
254 | if not ok then | ||
255 | return nil, err | ||
256 | end | ||
252 | timeout = timeout - interval | 257 | timeout = timeout - interval |
253 | interval = math.min(0.2, interval * 2) | 258 | interval = math.min(0.2, interval * 2) |
254 | key = system._readkey() | 259 | key = system._readkey() |
@@ -264,7 +269,6 @@ end | |||
264 | 269 | ||
265 | 270 | ||
266 | do | 271 | do |
267 | local left_over_key | ||
268 | local sequence -- table to store the sequence in progress | 272 | local sequence -- table to store the sequence in progress |
269 | local utf8_length -- length of utf8 sequence currently being processed | 273 | local utf8_length -- length of utf8 sequence currently being processed |
270 | local unpack = unpack or table.unpack | 274 | local unpack = unpack or table.unpack |
@@ -282,6 +286,8 @@ do | |||
282 | -- @treturn[2] nil in case of an error | 286 | -- @treturn[2] nil in case of an error |
283 | -- @treturn[2] string error message; `"timeout"` if the timeout was reached. | 287 | -- @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. | 288 | -- @treturn[2] string partial result in case of an error while reading a sequence, the sequence so far. |
289 | -- The function retains its own internal buffer, so on the next call the incomplete buffer is used to | ||
290 | -- complete the sequence. | ||
285 | function system.readansi(timeout, fsleep) | 291 | function system.readansi(timeout, fsleep) |
286 | if type(timeout) ~= "number" then | 292 | if type(timeout) ~= "number" then |
287 | error("arg #1 to readansi, expected timeout in seconds, got " .. type(timeout), 2) | 293 | error("arg #1 to readansi, expected timeout in seconds, got " .. type(timeout), 2) |
@@ -292,33 +298,47 @@ do | |||
292 | 298 | ||
293 | if not sequence then | 299 | if not sequence then |
294 | -- no sequence in progress, read a key | 300 | -- no sequence in progress, read a key |
295 | 301 | local err | |
296 | if left_over_key then | 302 | key, err = system.readkey(timeout, fsleep) |
297 | -- we still have a cached key from the last call | 303 | if key == nil then -- timeout or error |
298 | key = left_over_key | 304 | 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 | 305 | end |
308 | 306 | ||
309 | if key == 27 then | 307 | if key == 27 then |
310 | -- looks like an ansi escape sequence, immediately read next char | 308 | -- looks like an ansi escape sequence, immediately read next char |
311 | -- as an heuristic against manually typing escape sequences | 309 | -- as an heuristic against manually typing escape sequences |
312 | local key2 = system.readkey(0, fsleep) | 310 | local key2 = system.readkey(0, fsleep) |
313 | if key2 ~= 91 and key2 ~= 79 then -- we expect either "[" or "O" for an ANSI sequence | 311 | if key2 == nil then |
314 | -- not the expected [ or O character, so we return the key as is | 312 | -- no key available, return the escape key, on its own |
315 | -- and store the extra key read for the next call | 313 | sequence = nil |
316 | left_over_key = key2 | ||
317 | return string.char(key), "char" | 314 | return string.char(key), "char" |
315 | |||
316 | elseif key2 == 91 then | ||
317 | -- "[" means it is for sure an ANSI sequence | ||
318 | sequence = { key, key2 } | ||
319 | |||
320 | elseif key2 == 79 then | ||
321 | -- "O" means it is either an ANSI sequence or just an <alt>+O key stroke | ||
322 | -- check if there is yet another byte available | ||
323 | local key3 = system.readkey(0, fsleep) | ||
324 | if key3 == nil then | ||
325 | -- no key available, return the <alt>O key stroke, report as ANSI | ||
326 | sequence = nil | ||
327 | return string.char(key, key2), "ansi" | ||
328 | end | ||
329 | -- it's an ANSI sequence, marked with <ESC>O | ||
330 | if (key3 >= 65 and key3 <= 90) or (key3 >= 97 and key3 <= 126) then | ||
331 | -- end of sequence, return the full sequence | ||
332 | return string.char(key, key2, key3), "ansi" | ||
333 | end | ||
334 | sequence = { key, key2, key3 } | ||
335 | |||
336 | else | ||
337 | -- not an ANSI sequence, but an <alt>+<key2> key stroke, so report as ANSI | ||
338 | sequence = nil | ||
339 | return string.char(key, key2), "ansi" | ||
318 | end | 340 | end |
319 | 341 | ||
320 | -- escape sequence detected | ||
321 | sequence = { key, key2 } | ||
322 | else | 342 | else |
323 | -- check UTF8 length | 343 | -- 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 | 344 | utf8_length = key < 128 and 1 or key < 224 and 2 or key < 240 and 3 or key < 248 and 4 |