diff options
author | Thijs <thijs@thijsschreijer.nl> | 2023-11-16 09:09:54 +0100 |
---|---|---|
committer | Thijs Schreijer <thijs@thijsschreijer.nl> | 2024-04-30 09:28:01 +0200 |
commit | bd994461ef7c2553da9a6945c685152bad50eb8f (patch) | |
tree | 28adc32712f00a200a34357e731a570bf1a359dc /src | |
parent | 47c24eed0191f8f72646be63dee94ac2b35eb062 (diff) | |
download | luasystem-bd994461ef7c2553da9a6945c685152bad50eb8f.tar.gz luasystem-bd994461ef7c2553da9a6945c685152bad50eb8f.tar.bz2 luasystem-bd994461ef7c2553da9a6945c685152bad50eb8f.zip |
feat(term): getting/setting terminal config flags
Diffstat (limited to 'src')
-rw-r--r-- | src/bitflags.c | 235 | ||||
-rw-r--r-- | src/bitflags.h | 21 | ||||
-rw-r--r-- | src/compat.h | 11 | ||||
-rw-r--r-- | src/core.c | 2 | ||||
-rw-r--r-- | src/term.c | 822 |
5 files changed, 1086 insertions, 5 deletions
diff --git a/src/bitflags.c b/src/bitflags.c new file mode 100644 index 0000000..89a88b7 --- /dev/null +++ b/src/bitflags.c | |||
@@ -0,0 +1,235 @@ | |||
1 | /// Bitflags module. | ||
2 | // The bitflag object makes it easy to manipulate flags in a bitmask. | ||
3 | // | ||
4 | // It has metamethods that do the hard work, adding flags sets them, substracting | ||
5 | // unsets them. Comparing flags checks if all flags in the second set are also set | ||
6 | // in the first set. The `has` method checks if all flags in the second set are | ||
7 | // also set in the first set, but behaves slightly different. | ||
8 | // | ||
9 | // Indexing allows checking values or setting them by bit index (eg. 0-7 for flags | ||
10 | // in the first byte). | ||
11 | // | ||
12 | // _NOTE_: unavailable flags (eg. Windows flags on a Posix system) should not be | ||
13 | // omitted, but be assigned a value of 0. This is because the `has` method will | ||
14 | // return `false` if the flags are checked and the value is 0. | ||
15 | // | ||
16 | // See `system.bitflag` (the constructor) for extensive examples on usage. | ||
17 | // @classmod bitflags | ||
18 | #include "bitflags.h" | ||
19 | |||
20 | #define BITFLAGS_MT_NAME "LuaSystem.BitFlags" | ||
21 | |||
22 | typedef struct { | ||
23 | LSBF_BITFLAG flags; | ||
24 | } LS_BitFlags; | ||
25 | |||
26 | /// Bit flags. | ||
27 | // Bitflag objects can be used to easily manipulate and compare bit flags. | ||
28 | // These are primarily for use with the terminal functions, but can be used | ||
29 | // in other places as well. | ||
30 | // @section bitflags | ||
31 | |||
32 | |||
33 | // pushes a new LS_BitFlags object with the given value onto the stack | ||
34 | void lsbf_pushbitflags(lua_State *L, LSBF_BITFLAG value) { | ||
35 | LS_BitFlags *obj = (LS_BitFlags *)lua_newuserdata(L, sizeof(LS_BitFlags)); | ||
36 | if (!obj) luaL_error(L, "Memory allocation failed"); | ||
37 | luaL_getmetatable(L, BITFLAGS_MT_NAME); | ||
38 | lua_setmetatable(L, -2); | ||
39 | obj->flags = value; | ||
40 | } | ||
41 | |||
42 | // gets the LS_BitFlags value at the given index. Returns a Lua error if it is not | ||
43 | // a LS_BitFlags object. | ||
44 | LSBF_BITFLAG lsbf_checkbitflags(lua_State *L, int index) { | ||
45 | LS_BitFlags *obj = (LS_BitFlags *)luaL_checkudata(L, index, BITFLAGS_MT_NAME); | ||
46 | return obj->flags; | ||
47 | } | ||
48 | |||
49 | /*** | ||
50 | Creates a new bitflag object from the given value. | ||
51 | @function system.bitflag | ||
52 | @tparam[opt=0] number value the value to create the bitflag object from. | ||
53 | @treturn bitflag bitflag object with the given values set. | ||
54 | @usage | ||
55 | local sys = require 'system' | ||
56 | local flags = sys.bitflag(2) -- b0010 | ||
57 | |||
58 | -- get state of individual bits | ||
59 | print(flags[0]) -- false | ||
60 | print(flags[1]) -- true | ||
61 | |||
62 | -- set individual bits | ||
63 | flags[0] = true -- b0011 | ||
64 | print(flags:value()) -- 3 | ||
65 | print(flags) -- "bitflags: 3" | ||
66 | |||
67 | -- adding flags (bitwise OR) | ||
68 | local flags1 = sys.bitflag(1) -- b0001 | ||
69 | local flags2 = sys.bitflag(2) -- b0010 | ||
70 | local flags3 = flags1 + flags2 -- b0011 | ||
71 | |||
72 | -- substracting flags (bitwise AND NOT) | ||
73 | print(flags3:value()) -- 3 | ||
74 | flag3 = flag3 - flag3 -- b0000 | ||
75 | print(flags3:value()) -- 0 | ||
76 | |||
77 | -- comparing flags | ||
78 | local flags4 = sys.bitflag(7) -- b0111 | ||
79 | local flags5 = sys.bitflag(255) -- b11111111 | ||
80 | print(flags5 >= flags4) -- true, all bits in flags4 are set in flags5 | ||
81 | |||
82 | -- comparing with 0 flags: comparison and `has` behave differently | ||
83 | local flags6 = sys.bitflag(0) -- b0000 | ||
84 | local flags7 = sys.bitflag(1) -- b0001 | ||
85 | print(flags6 < flags7) -- true, flags6 is a subset of flags7 | ||
86 | print(flags7:has(flags6)) -- false, flags6 is not set in flags7 | ||
87 | */ | ||
88 | static int lsbf_new(lua_State *L) { | ||
89 | LSBF_BITFLAG flags = 0; | ||
90 | if (lua_gettop(L) > 0) { | ||
91 | flags = luaL_checkinteger(L, 1); | ||
92 | } | ||
93 | lsbf_pushbitflags(L, flags); | ||
94 | return 1; | ||
95 | } | ||
96 | |||
97 | /*** | ||
98 | Retrieves the numeric value of the bitflag object. | ||
99 | @function bitflag:value | ||
100 | @treturn number the numeric value of the bitflags. | ||
101 | @usage | ||
102 | local sys = require 'system' | ||
103 | local flags = sys.bitflag() -- b0000 | ||
104 | flags[0] = true -- b0001 | ||
105 | flags[2] = true -- b0101 | ||
106 | print(flags:value()) -- 5 | ||
107 | */ | ||
108 | static int lsbf_value(lua_State *L) { | ||
109 | lua_pushinteger(L, lsbf_checkbitflags(L, 1)); | ||
110 | return 1; | ||
111 | } | ||
112 | |||
113 | static int lsbf_tostring(lua_State *L) { | ||
114 | lua_pushfstring(L, "bitflags: %d", lsbf_checkbitflags(L, 1)); | ||
115 | return 1; | ||
116 | } | ||
117 | |||
118 | static int lsbf_add(lua_State *L) { | ||
119 | lsbf_pushbitflags(L, lsbf_checkbitflags(L, 1) | lsbf_checkbitflags(L, 2)); | ||
120 | return 1; | ||
121 | } | ||
122 | |||
123 | static int lsbf_sub(lua_State *L) { | ||
124 | lsbf_pushbitflags(L, lsbf_checkbitflags(L, 1) & ~lsbf_checkbitflags(L, 2)); | ||
125 | return 1; | ||
126 | } | ||
127 | |||
128 | static int lsbf_eq(lua_State *L) { | ||
129 | lua_pushboolean(L, lsbf_checkbitflags(L, 1) == lsbf_checkbitflags(L, 2)); | ||
130 | return 1; | ||
131 | } | ||
132 | |||
133 | static int lsbf_le(lua_State *L) { | ||
134 | LSBF_BITFLAG a = lsbf_checkbitflags(L, 1); | ||
135 | LSBF_BITFLAG b = lsbf_checkbitflags(L, 2); | ||
136 | // Check if all bits in b are also set in a | ||
137 | lua_pushboolean(L, (a & b) == a); | ||
138 | return 1; | ||
139 | } | ||
140 | |||
141 | /*** | ||
142 | Checks if the given flags are set. | ||
143 | This is different from the `>=` and `<=` operators because if the flag to check | ||
144 | has a value `0`, it will always return `false`. So if there are flags that are | ||
145 | unsupported on a platform, they can be set to 0 and the `has` function will | ||
146 | return `false` if the flags are checked. | ||
147 | @function bitflag:has | ||
148 | @tparam bitflag subset the flags to check for. | ||
149 | @treturn boolean true if all the flags are set, false otherwise. | ||
150 | @usage | ||
151 | local sys = require 'system' | ||
152 | local flags = sys.bitflag(12) -- b1100 | ||
153 | local myflags = sys.bitflag(15) -- b1111 | ||
154 | print(flags:has(myflags)) -- false, not all bits in myflags are set in flags | ||
155 | print(myflags:has(flags)) -- true, all bits in flags are set in myflags | ||
156 | */ | ||
157 | static int lsbf_has(lua_State *L) { | ||
158 | LSBF_BITFLAG a = lsbf_checkbitflags(L, 1); | ||
159 | LSBF_BITFLAG b = lsbf_checkbitflags(L, 2); | ||
160 | // Check if all bits in b are also set in a, and b is not 0 | ||
161 | lua_pushboolean(L, (a | b) == a && b != 0); | ||
162 | return 1; | ||
163 | } | ||
164 | |||
165 | static int lsbf_lt(lua_State *L) { | ||
166 | LSBF_BITFLAG a = lsbf_checkbitflags(L, 1); | ||
167 | LSBF_BITFLAG b = lsbf_checkbitflags(L, 2); | ||
168 | // Check if a is strictly less than b, meaning a != b and a is a subset of b | ||
169 | lua_pushboolean(L, (a != b) && ((a & b) == a)); | ||
170 | return 1; | ||
171 | } | ||
172 | |||
173 | static int lsbf_index(lua_State *L) { | ||
174 | if (!lua_isnumber(L, 2)) { | ||
175 | // the parameter isn't a number, just lookup the key in the metatable | ||
176 | lua_getmetatable(L, 1); | ||
177 | lua_pushvalue(L, 2); | ||
178 | lua_gettable(L, -2); | ||
179 | return 1; | ||
180 | } | ||
181 | |||
182 | int index = luaL_checkinteger(L, 2); | ||
183 | if (index < 0 || index >= sizeof(LSBF_BITFLAG) * 8) { | ||
184 | return luaL_error(L, "index out of range"); | ||
185 | } | ||
186 | lua_pushboolean(L, (lsbf_checkbitflags(L, 1) & (1 << index)) != 0); | ||
187 | return 1; | ||
188 | } | ||
189 | |||
190 | static int lsbf_newindex(lua_State *L) { | ||
191 | LS_BitFlags *obj = (LS_BitFlags *)luaL_checkudata(L, 1, BITFLAGS_MT_NAME); | ||
192 | |||
193 | if (!lua_isnumber(L, 2)) { | ||
194 | return luaL_error(L, "index must be a number"); | ||
195 | } | ||
196 | int index = luaL_checkinteger(L, 2); | ||
197 | if (index < 0 || index >= sizeof(LSBF_BITFLAG) * 8) { | ||
198 | return luaL_error(L, "index out of range"); | ||
199 | } | ||
200 | |||
201 | luaL_checkany(L, 3); | ||
202 | if (lua_toboolean(L, 3)) { | ||
203 | obj->flags |= (1 << index); | ||
204 | } else { | ||
205 | obj->flags &= ~(1 << index); | ||
206 | } | ||
207 | return 0; | ||
208 | } | ||
209 | |||
210 | static const struct luaL_Reg lsbf_funcs[] = { | ||
211 | {"bitflag", lsbf_new}, | ||
212 | {NULL, NULL} | ||
213 | }; | ||
214 | |||
215 | static const struct luaL_Reg lsbf_methods[] = { | ||
216 | {"value", lsbf_value}, | ||
217 | {"has", lsbf_has}, | ||
218 | {"__tostring", lsbf_tostring}, | ||
219 | {"__add", lsbf_add}, | ||
220 | {"__sub", lsbf_sub}, | ||
221 | {"__eq", lsbf_eq}, | ||
222 | {"__le", lsbf_le}, | ||
223 | {"__lt", lsbf_lt}, | ||
224 | {"__index", lsbf_index}, | ||
225 | {"__newindex", lsbf_newindex}, | ||
226 | {NULL, NULL} | ||
227 | }; | ||
228 | |||
229 | void bitflags_open(lua_State *L) { | ||
230 | luaL_newmetatable(L, BITFLAGS_MT_NAME); | ||
231 | luaL_setfuncs(L, lsbf_methods, 0); | ||
232 | lua_pop(L, 1); | ||
233 | |||
234 | luaL_setfuncs(L, lsbf_funcs, 0); | ||
235 | } | ||
diff --git a/src/bitflags.h b/src/bitflags.h new file mode 100644 index 0000000..0e47246 --- /dev/null +++ b/src/bitflags.h | |||
@@ -0,0 +1,21 @@ | |||
1 | #ifndef LSBITFLAGS_H | ||
2 | #define LSBITFLAGS_H | ||
3 | |||
4 | #include <lua.h> | ||
5 | #include "compat.h" | ||
6 | #include <lauxlib.h> | ||
7 | #include <stdlib.h> | ||
8 | |||
9 | // type used to store the bitflags | ||
10 | #define LSBF_BITFLAG lua_Integer | ||
11 | |||
12 | // Validates that the given index is a bitflag object and returns its value. | ||
13 | // If the index is not a bitflag object, a Lua error is raised. | ||
14 | // The value will be left on the stack. | ||
15 | LSBF_BITFLAG lsbf_checkbitflags(lua_State *L, int index); | ||
16 | |||
17 | // Pushes a new bitflag object with the given value onto the stack. | ||
18 | // Might raise a Lua error if memory allocation fails. | ||
19 | void lsbf_pushbitflags(lua_State *L, LSBF_BITFLAG value); | ||
20 | |||
21 | #endif | ||
diff --git a/src/compat.h b/src/compat.h index 35f9ef2..7a1fcee 100644 --- a/src/compat.h +++ b/src/compat.h | |||
@@ -13,6 +13,17 @@ void luaL_setfuncs(lua_State *L, const luaL_Reg *l, int nup); | |||
13 | #include <sys/types.h> | 13 | #include <sys/types.h> |
14 | #endif | 14 | #endif |
15 | 15 | ||
16 | // Windows compatibility; define DWORD and TRUE/FALSE on non-Windows | ||
17 | #ifndef _WIN32 | ||
18 | #ifndef DWORD | ||
19 | #define DWORD unsigned long | ||
20 | #endif | ||
21 | #ifndef TRUE | ||
22 | #define TRUE 1 | ||
23 | #define FALSE 0 | ||
24 | #endif | ||
25 | #endif | ||
26 | |||
16 | #ifdef _MSC_VER | 27 | #ifdef _MSC_VER |
17 | // MSVC Windows doesn't have ssize_t, so we define it here | 28 | // MSVC Windows doesn't have ssize_t, so we define it here |
18 | #if SIZE_MAX == UINT_MAX | 29 | #if SIZE_MAX == UINT_MAX |
@@ -16,6 +16,7 @@ void time_open(lua_State *L); | |||
16 | void environment_open(lua_State *L); | 16 | void environment_open(lua_State *L); |
17 | void random_open(lua_State *L); | 17 | void random_open(lua_State *L); |
18 | void term_open(lua_State *L); | 18 | void term_open(lua_State *L); |
19 | void bitflags_open(lua_State *L); | ||
19 | 20 | ||
20 | /*------------------------------------------------------------------------- | 21 | /*------------------------------------------------------------------------- |
21 | * Initializes all library modules. | 22 | * Initializes all library modules. |
@@ -32,6 +33,7 @@ LUAEXPORT int luaopen_system_core(lua_State *L) { | |||
32 | lua_pushboolean(L, 0); | 33 | lua_pushboolean(L, 0); |
33 | #endif | 34 | #endif |
34 | lua_rawset(L, -3); | 35 | lua_rawset(L, -3); |
36 | bitflags_open(L); // must be first, used by others | ||
35 | time_open(L); | 37 | time_open(L); |
36 | random_open(L); | 38 | random_open(L); |
37 | term_open(L); | 39 | term_open(L); |
@@ -1,37 +1,849 @@ | |||
1 | /// @submodule system | 1 | /// @submodule system |
2 | |||
3 | // Unix: see https://blog.nelhage.com/2009/12/a-brief-introduction-to-termios-termios3-and-stty/ | ||
4 | // Windows: see https://learn.microsoft.com/en-us/windows/console/console-reference | ||
5 | |||
2 | #include <lua.h> | 6 | #include <lua.h> |
3 | #include <lauxlib.h> | 7 | #include <lauxlib.h> |
4 | #include <lualib.h> | 8 | #include <lualib.h> |
5 | #include "compat.h" | 9 | #include "compat.h" |
10 | #include "bitflags.h" | ||
6 | 11 | ||
7 | #ifndef _MSC_VER | 12 | #ifndef _MSC_VER |
8 | # include <unistd.h> | 13 | # include <unistd.h> |
9 | #endif | 14 | #endif |
10 | 15 | ||
16 | #ifdef _WIN32 | ||
17 | # include <windows.h> | ||
18 | #else | ||
19 | # include <termios.h> | ||
20 | # include <string.h> | ||
21 | # include <errno.h> | ||
22 | # include <fcntl.h> | ||
23 | # include <sys/ioctl.h> | ||
24 | # include <unistd.h> | ||
25 | #endif | ||
26 | |||
27 | #ifdef _WIN32 | ||
28 | // after an error is returned, GetLastError() result can be passed to this function to get a string | ||
29 | // representation of the error on the stack. | ||
30 | // result will be nil+error on the stack, always 2 results. | ||
31 | static void termFormatError(lua_State *L, DWORD errorCode, const char* prefix) { | ||
32 | //static void FormatErrorAndReturn(lua_State *L, DWORD errorCode, const char* prefix) { | ||
33 | LPSTR messageBuffer = NULL; | ||
34 | FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, | ||
35 | NULL, errorCode, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&messageBuffer, 0, NULL); | ||
36 | |||
37 | lua_pushnil(L); | ||
38 | if (messageBuffer) { | ||
39 | if (prefix) { | ||
40 | lua_pushfstring(L, "%s: %s", prefix, messageBuffer); | ||
41 | } else { | ||
42 | lua_pushstring(L, messageBuffer); | ||
43 | } | ||
44 | LocalFree(messageBuffer); | ||
45 | } else { | ||
46 | lua_pushfstring(L, "%sError code %d", prefix ? prefix : "", errorCode); | ||
47 | } | ||
48 | } | ||
49 | #else | ||
50 | static int pusherror(lua_State *L, const char *info) | ||
51 | { | ||
52 | lua_pushnil(L); | ||
53 | if (info==NULL) | ||
54 | lua_pushstring(L, strerror(errno)); | ||
55 | else | ||
56 | lua_pushfstring(L, "%s: %s", info, strerror(errno)); | ||
57 | lua_pushinteger(L, errno); | ||
58 | return 3; | ||
59 | } | ||
60 | #endif | ||
11 | 61 | ||
12 | /*** | 62 | /*** |
13 | Checks if a file-handle is a TTY. | 63 | Checks if a file-handle is a TTY. |
14 | 64 | ||
15 | @function isatty | 65 | @function isatty |
16 | @tparam file file the file-handle to check | 66 | @tparam file file the file-handle to check, one of `io.stdin`, `io.stdout`, `io.stderr`. |
17 | @treturn boolean true if the file is a tty | 67 | @treturn boolean true if the file is a tty |
68 | @usage | ||
69 | local system = require('system') | ||
70 | if system.isatty(io.stdin) then | ||
71 | -- enable ANSI coloring etc on Windows, does nothing in Posix. | ||
72 | local flags = system.getconsoleflags(io.stdout) | ||
73 | system.setconsoleflags(io.stdout, flags + sys.COF_VIRTUAL_TERMINAL_PROCESSING) | ||
74 | end | ||
18 | */ | 75 | */ |
19 | static int lua_isatty(lua_State* L) { | 76 | static int lst_isatty(lua_State* L) { |
20 | FILE **fh = (FILE **) luaL_checkudata(L, 1, LUA_FILEHANDLE); | 77 | FILE **fh = (FILE **) luaL_checkudata(L, 1, LUA_FILEHANDLE); |
21 | lua_pushboolean(L, isatty(fileno(*fh))); | 78 | lua_pushboolean(L, isatty(fileno(*fh))); |
22 | return 1; | 79 | return 1; |
23 | } | 80 | } |
24 | 81 | ||
25 | 82 | ||
83 | /*------------------------------------------------------------------------- | ||
84 | * Windows Get/SetConsoleMode functions | ||
85 | *-------------------------------------------------------------------------*/ | ||
26 | 86 | ||
27 | static luaL_Reg func[] = { | 87 | typedef struct ls_RegConst { |
28 | { "isatty", lua_isatty }, | 88 | const char *name; |
29 | { NULL, NULL } | 89 | DWORD value; |
90 | } ls_RegConst; | ||
91 | |||
92 | // Define a macro to check if a constant is defined and set it to 0 if not. | ||
93 | // This is needed because some flags are not defined on all platforms. So we | ||
94 | // still export the constants, but they will be all 0, and hence not do anything. | ||
95 | #ifdef _WIN32 | ||
96 | #define CHECK_WIN_FLAG_OR_ZERO(flag) flag | ||
97 | #define CHECK_NIX_FLAG_OR_ZERO(flag) 0 | ||
98 | #else | ||
99 | #define CHECK_WIN_FLAG_OR_ZERO(flag) 0 | ||
100 | #define CHECK_NIX_FLAG_OR_ZERO(flag) flag | ||
101 | #endif | ||
102 | |||
103 | // Export Windows constants to Lua | ||
104 | static const struct ls_RegConst win_console_in_flags[] = { | ||
105 | // Console Input Flags | ||
106 | {"CIF_ECHO_INPUT", CHECK_WIN_FLAG_OR_ZERO(ENABLE_ECHO_INPUT)}, | ||
107 | {"CIF_INSERT_MODE", CHECK_WIN_FLAG_OR_ZERO(ENABLE_INSERT_MODE)}, | ||
108 | {"CIF_LINE_INPUT", CHECK_WIN_FLAG_OR_ZERO(ENABLE_LINE_INPUT)}, | ||
109 | {"CIF_MOUSE_INPUT", CHECK_WIN_FLAG_OR_ZERO(ENABLE_MOUSE_INPUT)}, | ||
110 | {"CIF_PROCESSED_INPUT", CHECK_WIN_FLAG_OR_ZERO(ENABLE_PROCESSED_INPUT)}, | ||
111 | {"CIF_QUICK_EDIT_MODE", CHECK_WIN_FLAG_OR_ZERO(ENABLE_QUICK_EDIT_MODE)}, | ||
112 | {"CIF_WINDOW_INPUT", CHECK_WIN_FLAG_OR_ZERO(ENABLE_WINDOW_INPUT)}, | ||
113 | {"CIF_VIRTUAL_TERMINAL_INPUT", CHECK_WIN_FLAG_OR_ZERO(ENABLE_VIRTUAL_TERMINAL_INPUT)}, | ||
114 | {"CIF_EXTENDED_FLAGS", CHECK_WIN_FLAG_OR_ZERO(ENABLE_EXTENDED_FLAGS)}, | ||
115 | {"CIF_AUTO_POSITION", CHECK_WIN_FLAG_OR_ZERO(ENABLE_AUTO_POSITION)}, | ||
116 | {NULL, 0} | ||
117 | }; | ||
118 | |||
119 | static const struct ls_RegConst win_console_out_flags[] = { | ||
120 | // Console Output Flags | ||
121 | {"COF_PROCESSED_OUTPUT", CHECK_WIN_FLAG_OR_ZERO(ENABLE_PROCESSED_OUTPUT)}, | ||
122 | {"COF_WRAP_AT_EOL_OUTPUT", CHECK_WIN_FLAG_OR_ZERO(ENABLE_WRAP_AT_EOL_OUTPUT)}, | ||
123 | {"COF_VIRTUAL_TERMINAL_PROCESSING", CHECK_WIN_FLAG_OR_ZERO(ENABLE_VIRTUAL_TERMINAL_PROCESSING)}, | ||
124 | {"COF_DISABLE_NEWLINE_AUTO_RETURN", CHECK_WIN_FLAG_OR_ZERO(DISABLE_NEWLINE_AUTO_RETURN)}, | ||
125 | {"COF_ENABLE_LVB_GRID_WORLDWIDE", CHECK_WIN_FLAG_OR_ZERO(ENABLE_LVB_GRID_WORLDWIDE)}, | ||
126 | {NULL, 0} | ||
127 | }; | ||
128 | |||
129 | |||
130 | // Export Unix constants to Lua | ||
131 | static const struct ls_RegConst nix_tcsetattr_actions[] = { | ||
132 | // The optional actions for tcsetattr | ||
133 | {"TCSANOW", CHECK_NIX_FLAG_OR_ZERO(TCSANOW)}, | ||
134 | {"TCSADRAIN", CHECK_NIX_FLAG_OR_ZERO(TCSADRAIN)}, | ||
135 | {"TCSAFLUSH", CHECK_NIX_FLAG_OR_ZERO(TCSAFLUSH)}, | ||
136 | {NULL, 0} | ||
137 | }; | ||
138 | |||
139 | static const struct ls_RegConst nix_console_i_flags[] = { | ||
140 | // Input flags (c_iflag) | ||
141 | {"I_IGNBRK", CHECK_NIX_FLAG_OR_ZERO(IGNBRK)}, | ||
142 | {"I_BRKINT", CHECK_NIX_FLAG_OR_ZERO(BRKINT)}, | ||
143 | {"I_IGNPAR", CHECK_NIX_FLAG_OR_ZERO(IGNPAR)}, | ||
144 | {"I_PARMRK", CHECK_NIX_FLAG_OR_ZERO(PARMRK)}, | ||
145 | {"I_INPCK", CHECK_NIX_FLAG_OR_ZERO(INPCK)}, | ||
146 | {"I_ISTRIP", CHECK_NIX_FLAG_OR_ZERO(ISTRIP)}, | ||
147 | {"I_INLCR", CHECK_NIX_FLAG_OR_ZERO(INLCR)}, | ||
148 | {"I_IGNCR", CHECK_NIX_FLAG_OR_ZERO(IGNCR)}, | ||
149 | {"I_ICRNL", CHECK_NIX_FLAG_OR_ZERO(ICRNL)}, | ||
150 | #ifndef __APPLE__ | ||
151 | {"I_IUCLC", CHECK_NIX_FLAG_OR_ZERO(IUCLC)}, // Might not be available on all systems | ||
152 | #else | ||
153 | {"I_IUCLC", 0}, | ||
154 | #endif | ||
155 | {"I_IXON", CHECK_NIX_FLAG_OR_ZERO(IXON)}, | ||
156 | {"I_IXANY", CHECK_NIX_FLAG_OR_ZERO(IXANY)}, | ||
157 | {"I_IXOFF", CHECK_NIX_FLAG_OR_ZERO(IXOFF)}, | ||
158 | {"I_IMAXBEL", CHECK_NIX_FLAG_OR_ZERO(IMAXBEL)}, | ||
159 | {NULL, 0} | ||
30 | }; | 160 | }; |
31 | 161 | ||
162 | static const struct ls_RegConst nix_console_o_flags[] = { | ||
163 | // Output flags (c_oflag) | ||
164 | {"O_OPOST", CHECK_NIX_FLAG_OR_ZERO(OPOST)}, | ||
165 | #ifndef __APPLE__ | ||
166 | {"O_OLCUC", CHECK_NIX_FLAG_OR_ZERO(OLCUC)}, // Might not be available on all systems | ||
167 | #else | ||
168 | {"O_OLCUC", 0}, | ||
169 | #endif | ||
170 | {"O_ONLCR", CHECK_NIX_FLAG_OR_ZERO(ONLCR)}, | ||
171 | {"O_OCRNL", CHECK_NIX_FLAG_OR_ZERO(OCRNL)}, | ||
172 | {"O_ONOCR", CHECK_NIX_FLAG_OR_ZERO(ONOCR)}, | ||
173 | {"O_ONLRET", CHECK_NIX_FLAG_OR_ZERO(ONLRET)}, | ||
174 | {"O_OFILL", CHECK_NIX_FLAG_OR_ZERO(OFILL)}, | ||
175 | {"O_OFDEL", CHECK_NIX_FLAG_OR_ZERO(OFDEL)}, | ||
176 | {"O_NLDLY", CHECK_NIX_FLAG_OR_ZERO(NLDLY)}, | ||
177 | {"O_CRDLY", CHECK_NIX_FLAG_OR_ZERO(CRDLY)}, | ||
178 | {"O_TABDLY", CHECK_NIX_FLAG_OR_ZERO(TABDLY)}, | ||
179 | {"O_BSDLY", CHECK_NIX_FLAG_OR_ZERO(BSDLY)}, | ||
180 | {"O_VTDLY", CHECK_NIX_FLAG_OR_ZERO(VTDLY)}, | ||
181 | {"O_FFDLY", CHECK_NIX_FLAG_OR_ZERO(FFDLY)}, | ||
182 | {NULL, 0} | ||
183 | }; | ||
184 | |||
185 | static const struct ls_RegConst nix_console_l_flags[] = { | ||
186 | // Local flags (c_lflag) | ||
187 | {"L_ISIG", CHECK_NIX_FLAG_OR_ZERO(ISIG)}, | ||
188 | {"L_ICANON", CHECK_NIX_FLAG_OR_ZERO(ICANON)}, | ||
189 | #ifndef __APPLE__ | ||
190 | {"L_XCASE", CHECK_NIX_FLAG_OR_ZERO(XCASE)}, // Might not be available on all systems | ||
191 | #else | ||
192 | {"L_XCASE", 0}, | ||
193 | #endif | ||
194 | {"L_ECHO", CHECK_NIX_FLAG_OR_ZERO(ECHO)}, | ||
195 | {"L_ECHOE", CHECK_NIX_FLAG_OR_ZERO(ECHOE)}, | ||
196 | {"L_ECHOK", CHECK_NIX_FLAG_OR_ZERO(ECHOK)}, | ||
197 | {"L_ECHONL", CHECK_NIX_FLAG_OR_ZERO(ECHONL)}, | ||
198 | {"L_NOFLSH", CHECK_NIX_FLAG_OR_ZERO(NOFLSH)}, | ||
199 | {"L_TOSTOP", CHECK_NIX_FLAG_OR_ZERO(TOSTOP)}, | ||
200 | {"L_ECHOCTL", CHECK_NIX_FLAG_OR_ZERO(ECHOCTL)}, // Might not be available on all systems | ||
201 | {"L_ECHOPRT", CHECK_NIX_FLAG_OR_ZERO(ECHOPRT)}, // Might not be available on all systems | ||
202 | {"L_ECHOKE", CHECK_NIX_FLAG_OR_ZERO(ECHOKE)}, // Might not be available on all systems | ||
203 | {"L_FLUSHO", CHECK_NIX_FLAG_OR_ZERO(FLUSHO)}, | ||
204 | {"L_PENDIN", CHECK_NIX_FLAG_OR_ZERO(PENDIN)}, | ||
205 | {"L_IEXTEN", CHECK_NIX_FLAG_OR_ZERO(IEXTEN)}, | ||
206 | {NULL, 0} | ||
207 | }; | ||
208 | |||
209 | static DWORD win_valid_in_flags = 0; | ||
210 | static DWORD win_valid_out_flags = 0; | ||
211 | static DWORD nix_valid_i_flags = 0; | ||
212 | static DWORD nix_valid_o_flags = 0; | ||
213 | static DWORD nix_valid_l_flags = 0; | ||
214 | static void initialize_valid_flags() | ||
215 | { | ||
216 | win_valid_in_flags = 0; | ||
217 | for (int i = 0; win_console_in_flags[i].name != NULL; i++) | ||
218 | { | ||
219 | win_valid_in_flags |= win_console_in_flags[i].value; | ||
220 | } | ||
221 | win_valid_out_flags = 0; | ||
222 | for (int i = 0; win_console_out_flags[i].name != NULL; i++) | ||
223 | { | ||
224 | win_valid_out_flags |= win_console_out_flags[i].value; | ||
225 | } | ||
226 | nix_valid_i_flags = 0; | ||
227 | for (int i = 0; nix_console_i_flags[i].name != NULL; i++) | ||
228 | { | ||
229 | nix_valid_i_flags |= nix_console_i_flags[i].value; | ||
230 | } | ||
231 | nix_valid_o_flags = 0; | ||
232 | for (int i = 0; nix_console_o_flags[i].name != NULL; i++) | ||
233 | { | ||
234 | nix_valid_o_flags |= nix_console_o_flags[i].value; | ||
235 | } | ||
236 | nix_valid_l_flags = 0; | ||
237 | for (int i = 0; nix_console_l_flags[i].name != NULL; i++) | ||
238 | { | ||
239 | nix_valid_l_flags |= nix_console_l_flags[i].value; | ||
240 | } | ||
241 | } | ||
242 | |||
243 | #ifdef _WIN32 | ||
244 | // first item on the stack should be io.stdin, io.stderr, or io.stdout, second item | ||
245 | // should be the flags to validate. | ||
246 | // If it returns NULL, then it leaves nil+err on the stack | ||
247 | static HANDLE get_console_handle(lua_State *L, int flags_optional) | ||
248 | { | ||
249 | if (lua_gettop(L) < 1) { | ||
250 | luaL_argerror(L, 1, "expected file handle"); | ||
251 | } | ||
252 | |||
253 | HANDLE handle; | ||
254 | DWORD valid; | ||
255 | FILE *file = *(FILE **)luaL_checkudata(L, 1, LUA_FILEHANDLE); | ||
256 | if (file == stdin && file != NULL) { | ||
257 | handle = GetStdHandle(STD_INPUT_HANDLE); | ||
258 | valid = win_valid_in_flags; | ||
259 | |||
260 | } else if (file == stdout && file != NULL) { | ||
261 | handle = GetStdHandle(STD_OUTPUT_HANDLE); | ||
262 | valid = win_valid_out_flags; | ||
263 | |||
264 | } else if (file == stderr && file != NULL) { | ||
265 | handle = GetStdHandle(STD_ERROR_HANDLE); | ||
266 | valid = win_valid_out_flags; | ||
267 | |||
268 | } else { | ||
269 | luaL_argerror(L, 1, "invalid file handle"); // does not return | ||
270 | } | ||
271 | |||
272 | if (handle == INVALID_HANDLE_VALUE) { | ||
273 | termFormatError(L, GetLastError(), "failed to retrieve std handle"); | ||
274 | lua_error(L); // does not return | ||
275 | } | ||
276 | |||
277 | if (handle == NULL) { | ||
278 | lua_pushnil(L); | ||
279 | lua_pushliteral(L, "failed to get console handle"); | ||
280 | return NULL; | ||
281 | } | ||
282 | |||
283 | if (flags_optional && lua_gettop(L) < 2) { | ||
284 | return handle; | ||
285 | } | ||
286 | |||
287 | if (lua_gettop(L) < 2) { | ||
288 | luaL_argerror(L, 2, "expected flags"); | ||
289 | } | ||
290 | |||
291 | LSBF_BITFLAG flags = lsbf_checkbitflags(L, 2); | ||
292 | if ((flags & ~valid) != 0) { | ||
293 | luaL_argerror(L, 2, "invalid flags"); | ||
294 | } | ||
295 | |||
296 | return handle; | ||
297 | } | ||
298 | #else | ||
299 | // first item on the stack should be io.stdin, io.stderr, or io.stdout. Throws a | ||
300 | // Lua error if the file is not one of these. | ||
301 | static int get_console_handle(lua_State *L) | ||
302 | { | ||
303 | FILE **file = (FILE **)luaL_checkudata(L, 1, LUA_FILEHANDLE); | ||
304 | if (file == NULL || *file == NULL) { | ||
305 | return luaL_argerror(L, 1, "expected file handle"); // call doesn't return | ||
306 | } | ||
307 | |||
308 | // Check if the file is stdin, stdout, or stderr | ||
309 | if (*file == stdin || *file == stdout || *file == stderr) { | ||
310 | // Push the file descriptor onto the Lua stack | ||
311 | return fileno(*file); | ||
312 | } | ||
313 | |||
314 | return luaL_argerror(L, 1, "invalid file handle"); // does not return | ||
315 | } | ||
316 | #endif | ||
317 | |||
318 | |||
319 | |||
320 | /*** | ||
321 | Sets the console flags (Windows). | ||
322 | The `CIF_` and `COF_` constants are available on the module table. Where `CIF` are the | ||
323 | input flags (for use with `io.stdin`) and `COF` are the output flags (for use with | ||
324 | `io.stdout`/`io.stderr`). | ||
325 | |||
326 | To see flag status and constant names check `listconsoleflags`. | ||
327 | |||
328 | Note: not all combinations of flags are allowed, as some are mutually exclusive or mutually required. | ||
329 | See [setconsolemode documentation](https://learn.microsoft.com/en-us/windows/console/setconsolemode) | ||
330 | @function setconsoleflags | ||
331 | @tparam file file the file-handle to set the flags on | ||
332 | @tparam bitflags bitflags the flags to set/unset | ||
333 | @treturn[1] boolean `true` on success | ||
334 | @treturn[2] nil | ||
335 | @treturn[2] string error message | ||
336 | @usage | ||
337 | local system = require('system') | ||
338 | system.listconsoleflags(io.stdout) -- List all the available flags and their current status | ||
339 | |||
340 | local flags = system.getconsoleflags(io.stdout) | ||
341 | assert(system.setconsoleflags(io.stdout, | ||
342 | flags + system.COF_VIRTUAL_TERMINAL_PROCESSING) | ||
343 | |||
344 | system.listconsoleflags(io.stdout) -- List again to check the differences | ||
345 | */ | ||
346 | static int lst_setconsoleflags(lua_State *L) | ||
347 | { | ||
348 | #ifdef _WIN32 | ||
349 | HANDLE console_handle = get_console_handle(L, 0); | ||
350 | if (console_handle == NULL) { | ||
351 | return 2; // error message is already on the stack | ||
352 | } | ||
353 | LSBF_BITFLAG new_console_mode = lsbf_checkbitflags(L, 2); | ||
354 | |||
355 | DWORD prev_console_mode; | ||
356 | if (GetConsoleMode(console_handle, &prev_console_mode) == 0) | ||
357 | { | ||
358 | termFormatError(L, GetLastError(), "failed to get console mode"); | ||
359 | return 2; | ||
360 | } | ||
361 | |||
362 | int success = SetConsoleMode(console_handle, new_console_mode) != 0; | ||
363 | if (!success) | ||
364 | { | ||
365 | termFormatError(L, GetLastError(), "failed to set console mode"); | ||
366 | return 2; | ||
367 | } | ||
368 | |||
369 | #endif | ||
370 | lua_pushboolean(L, 1); | ||
371 | return 1; | ||
372 | } | ||
373 | |||
374 | |||
375 | |||
376 | /*** | ||
377 | Gets console flags (Windows). | ||
378 | @function getconsoleflags | ||
379 | @tparam file file the file-handle to get the flags from. | ||
380 | @treturn[1] bitflags the current console flags. | ||
381 | @treturn[2] nil | ||
382 | @treturn[2] string error message | ||
383 | @usage | ||
384 | local system = require('system') | ||
385 | |||
386 | local flags = system.getconsoleflags(io.stdout) | ||
387 | print("Current stdout flags:", tostring(flags)) | ||
388 | |||
389 | if flags:has(system.COF_VIRTUAL_TERMINAL_PROCESSING + system.COF_PROCESSED_OUTPUT) then | ||
390 | print("Both flags are set") | ||
391 | else | ||
392 | print("At least one flag is not set") | ||
393 | end | ||
394 | */ | ||
395 | static int lst_getconsoleflags(lua_State *L) | ||
396 | { | ||
397 | DWORD console_mode = 0; | ||
398 | |||
399 | #ifdef _WIN32 | ||
400 | HANDLE console_handle = get_console_handle(L, 1); | ||
401 | if (console_handle == NULL) { | ||
402 | return 2; // error message is already on the stack | ||
403 | } | ||
404 | |||
405 | if (GetConsoleMode(console_handle, &console_mode) == 0) | ||
406 | { | ||
407 | lua_pushnil(L); | ||
408 | lua_pushliteral(L, "failed to get console mode"); | ||
409 | return 2; | ||
410 | } | ||
411 | |||
412 | #endif | ||
413 | lsbf_pushbitflags(L, console_mode); | ||
414 | return 1; | ||
415 | } | ||
416 | |||
417 | |||
418 | |||
419 | /*------------------------------------------------------------------------- | ||
420 | * Unix tcgetattr/tcsetattr functions | ||
421 | *-------------------------------------------------------------------------*/ | ||
422 | // Code modified from the LuaPosix library by Gary V. Vaughan | ||
423 | // see https://github.com/luaposix/luaposix | ||
424 | |||
425 | /*** | ||
426 | Get termios state. | ||
427 | The terminal attributes is a table with the following fields: | ||
428 | |||
429 | - `iflag` input flags | ||
430 | - `oflag` output flags | ||
431 | - `cflag` control flags | ||
432 | - `lflag` local flags | ||
433 | - `ispeed` input speed | ||
434 | - `ospeed` output speed | ||
435 | - `cc` control characters | ||
436 | |||
437 | @function tcgetattr | ||
438 | @tparam file fd file handle to operate on, one of `io.stdin`, `io.stdout`, `io.stderr` | ||
439 | @treturn[1] termios terminal attributes, if successful. On Windows the bitflags are all 0, and the `cc` table is empty. | ||
440 | @treturn[2] nil | ||
441 | @treturn[2] string error message | ||
442 | @treturn[2] int errnum | ||
443 | @return error message if failed | ||
444 | @usage | ||
445 | local system = require('system') | ||
446 | |||
447 | local status = assert(tcgetattr(io.stdin)) | ||
448 | if status.iflag:has(system.I_IGNBRK) then | ||
449 | print("Ignoring break condition") | ||
450 | end | ||
451 | */ | ||
452 | static int lst_tcgetattr(lua_State *L) | ||
453 | { | ||
454 | #ifndef _WIN32 | ||
455 | int r, i; | ||
456 | struct termios t; | ||
457 | int fd = get_console_handle(L); | ||
458 | |||
459 | r = tcgetattr(fd, &t); | ||
460 | if (r == -1) return pusherror(L, NULL); | ||
461 | |||
462 | lua_newtable(L); | ||
463 | lsbf_pushbitflags(L, t.c_iflag); | ||
464 | lua_setfield(L, -2, "iflag"); | ||
465 | |||
466 | lsbf_pushbitflags(L, t.c_oflag); | ||
467 | lua_setfield(L, -2, "oflag"); | ||
468 | |||
469 | lsbf_pushbitflags(L, t.c_lflag); | ||
470 | lua_setfield(L, -2, "lflag"); | ||
471 | |||
472 | lsbf_pushbitflags(L, t.c_cflag); | ||
473 | lua_setfield(L, -2, "cflag"); | ||
474 | |||
475 | lua_pushinteger(L, cfgetispeed(&t)); | ||
476 | lua_setfield(L, -2, "ispeed"); | ||
477 | |||
478 | lua_pushinteger(L, cfgetospeed(&t)); | ||
479 | lua_setfield(L, -2, "ospeed"); | ||
480 | |||
481 | lua_newtable(L); | ||
482 | for (i=0; i<NCCS; i++) | ||
483 | { | ||
484 | lua_pushinteger(L, i); | ||
485 | lua_pushinteger(L, t.c_cc[i]); | ||
486 | lua_settable(L, -3); | ||
487 | } | ||
488 | lua_setfield(L, -2, "cc"); | ||
489 | |||
490 | #else | ||
491 | lua_newtable(L); | ||
492 | lsbf_pushbitflags(L, 0); | ||
493 | lua_setfield(L, -2, "iflag"); | ||
494 | lsbf_pushbitflags(L, 0); | ||
495 | lua_setfield(L, -2, "oflag"); | ||
496 | lsbf_pushbitflags(L, 0); | ||
497 | lua_setfield(L, -2, "lflag"); | ||
498 | lsbf_pushbitflags(L, 0); | ||
499 | lua_setfield(L, -2, "cflag"); | ||
500 | lua_pushinteger(L, 0); | ||
501 | lua_setfield(L, -2, "ispeed"); | ||
502 | lua_pushinteger(L, 0); | ||
503 | lua_setfield(L, -2, "ospeed"); | ||
504 | lua_newtable(L); | ||
505 | lua_setfield(L, -2, "cc"); | ||
506 | |||
507 | #endif | ||
508 | return 1; | ||
509 | } | ||
510 | |||
511 | |||
512 | |||
513 | /*** | ||
514 | Set termios state. | ||
515 | This function will set the flags as given. | ||
516 | |||
517 | The `I_`, `O_`, and `L_` constants are available on the module table. They are the respective | ||
518 | flags for the `iflags`, `oflags`, and `lflags` bitmasks. | ||
519 | |||
520 | To see flag status and constant names check `listtermflags`. For their meaning check | ||
521 | [the manpage](https://www.man7.org/linux/man-pages/man3/termios.3.html). | ||
522 | |||
523 | _Note_: not all combinations of flags are allowed, as some are mutually exclusive or mutually required. | ||
524 | See [setconsolemode documentation](https://learn.microsoft.com/en-us/windows/console/setconsolemode) | ||
525 | |||
526 | _Note_: only `iflag`, `oflag`, and `lflag` are supported at the moment. The other fields are ignored. | ||
527 | @function tcsetattr | ||
528 | @tparam file fd file handle to operate on, one of `io.stdin`, `io.stdout`, `io.stderr` | ||
529 | @int actions one of `TCSANOW`, `TCSADRAIN`, `TCSAFLUSH` | ||
530 | @tparam table termios a table with bitflag fields: | ||
531 | @tparam[opt] bitflags termios.iflag if given will set the input flags | ||
532 | @tparam[opt] bitflags termios.oflag if given will set the output flags | ||
533 | @tparam[opt] bitflags termios.lflag if given will set the local flags | ||
534 | @treturn[1] bool `true`, if successful. Always returns `true` on Windows. | ||
535 | @return[2] nil | ||
536 | @treturn[2] string error message | ||
537 | @treturn[2] int errnum | ||
538 | @usage | ||
539 | local system = require('system') | ||
540 | |||
541 | local status = assert(tcgetattr(io.stdin)) | ||
542 | if not status.lflag:has(system.L_ECHO) then | ||
543 | -- if echo is off, turn echoing newlines on | ||
544 | tcsetattr(io.stdin, system.TCSANOW, { lflag = status.lflag + system.L_ECHONL })) | ||
545 | end | ||
546 | */ | ||
547 | static int lst_tcsetattr(lua_State *L) | ||
548 | { | ||
549 | #ifndef _WIN32 | ||
550 | struct termios t; | ||
551 | int r, i; | ||
552 | int fd = get_console_handle(L); // first is the console handle | ||
553 | int act = luaL_checkinteger(L, 2); // second is the action to take | ||
554 | luaL_checktype(L, 3, LUA_TTABLE); // third is the termios table with fields | ||
555 | |||
556 | r = tcgetattr(fd, &t); | ||
557 | if (r == -1) return pusherror(L, NULL); | ||
558 | |||
559 | lua_getfield(L, 3, "iflag"); | ||
560 | if (!lua_isnil(L, -1)) { | ||
561 | t.c_iflag = lsbf_checkbitflags(L, -1); | ||
562 | } | ||
563 | lua_pop(L, 1); | ||
564 | |||
565 | lua_getfield(L, 3, "oflag"); | ||
566 | if (!lua_isnil(L, -1)) { | ||
567 | t.c_oflag = lsbf_checkbitflags(L, -1); | ||
568 | } | ||
569 | lua_pop(L, 1); | ||
570 | |||
571 | lua_getfield(L, 3, "lflag"); | ||
572 | if (!lua_isnil(L, -1)) { | ||
573 | t.c_lflag = lsbf_checkbitflags(L, -1); | ||
574 | } | ||
575 | lua_pop(L, 1); | ||
576 | |||
577 | // Skipping the others for now | ||
578 | |||
579 | // lua_getfield(L, 3, "cflag"); t.c_cflag = optint(L, -1, 0); lua_pop(L, 1); | ||
580 | // lua_getfield(L, 3, "ispeed"); cfsetispeed( &t, optint(L, -1, B0) ); lua_pop(L, 1); | ||
581 | // lua_getfield(L, 3, "ospeed"); cfsetospeed( &t, optint(L, -1, B0) ); lua_pop(L, 1); | ||
582 | |||
583 | // lua_getfield(L, 3, "cc"); | ||
584 | // for (i=0; i<NCCS; i++) | ||
585 | // { | ||
586 | // lua_pushinteger(L, i); | ||
587 | // lua_gettable(L, -2); | ||
588 | // t.c_cc[i] = optint(L, -1, 0); | ||
589 | // lua_pop(L, 1); | ||
590 | // } | ||
591 | |||
592 | r = tcsetattr(fd, act, &t); | ||
593 | if (r == -1) return pusherror(L, NULL); | ||
594 | #endif | ||
595 | |||
596 | lua_pushboolean(L, 1); | ||
597 | return 1; | ||
598 | } | ||
599 | |||
600 | |||
601 | |||
602 | /*** | ||
603 | Enables or disables non-blocking mode for a file (Posix). | ||
604 | @function setnonblock | ||
605 | @tparam file fd file handle to operate on, one of `io.stdin`, `io.stdout`, `io.stderr` | ||
606 | @tparam boolean make_non_block a truthy value will enable non-blocking mode, a falsy value will disable it. | ||
607 | @treturn[1] bool `true`, if successful | ||
608 | @treturn[2] nil | ||
609 | @treturn[2] string error message | ||
610 | @treturn[2] int errnum | ||
611 | @see getnonblock | ||
612 | @usage | ||
613 | local sys = require('system') | ||
614 | |||
615 | -- set io.stdin to non-blocking mode | ||
616 | local old_setting = sys.getnonblock(io.stdin) | ||
617 | sys.setnonblock(io.stdin, true) | ||
618 | |||
619 | -- do stuff | ||
620 | |||
621 | -- restore old setting | ||
622 | sys.setnonblock(io.stdin, old_setting) | ||
623 | */ | ||
624 | static int lst_setnonblock(lua_State *L) | ||
625 | { | ||
626 | #ifndef _WIN32 | ||
627 | |||
628 | int fd = get_console_handle(L); | ||
629 | |||
630 | int flags = fcntl(fd, F_GETFL, 0); | ||
631 | if (flags == -1) { | ||
632 | return pusherror(L, "Error getting handle flags: "); | ||
633 | } | ||
634 | if (lua_toboolean(L, 2)) { | ||
635 | // truthy: set non-blocking | ||
636 | flags |= O_NONBLOCK; | ||
637 | } else { | ||
638 | // falsy: set disable non-blocking | ||
639 | flags &= ~O_NONBLOCK; | ||
640 | } | ||
641 | if (fcntl(fd, F_SETFL, flags) == -1) { | ||
642 | return pusherror(L, "Error changing O_NONBLOCK: "); | ||
643 | } | ||
644 | |||
645 | #endif | ||
646 | |||
647 | lua_pushboolean(L, 1); | ||
648 | return 1; | ||
649 | } | ||
650 | |||
651 | |||
652 | |||
653 | /*** | ||
654 | Gets non-blocking mode status for a file (Posix). | ||
655 | @function getnonblock | ||
656 | @tparam file fd file handle to operate on, one of `io.stdin`, `io.stdout`, `io.stderr` | ||
657 | @treturn[1] bool `true` if set to non-blocking, `false` if not. Always returns `false` on Windows. | ||
658 | @treturn[2] nil | ||
659 | @treturn[2] string error message | ||
660 | @treturn[2] int errnum | ||
661 | */ | ||
662 | static int lst_getnonblock(lua_State *L) | ||
663 | { | ||
664 | #ifndef _WIN32 | ||
665 | |||
666 | int fd = get_console_handle(L); | ||
667 | |||
668 | // Set O_NONBLOCK | ||
669 | int flags = fcntl(fd, F_GETFL, 0); | ||
670 | if (flags == -1) { | ||
671 | return pusherror(L, "Error getting handle flags: "); | ||
672 | } | ||
673 | if (flags & O_NONBLOCK) { | ||
674 | lua_pushboolean(L, 1); | ||
675 | } else { | ||
676 | lua_pushboolean(L, 0); | ||
677 | } | ||
678 | |||
679 | #else | ||
680 | lua_pushboolean(L, 0); | ||
681 | |||
682 | #endif | ||
683 | return 1; | ||
684 | } | ||
685 | |||
686 | |||
687 | |||
688 | /*------------------------------------------------------------------------- | ||
689 | * Reading keyboard input | ||
690 | *-------------------------------------------------------------------------*/ | ||
691 | |||
692 | /*** | ||
693 | Reads a key from the console non-blocking. | ||
694 | On Posix, `io.stdin` must be set to non-blocking mode using `setnonblock` | ||
695 | before calling this function. Otherwise it will block. | ||
696 | |||
697 | @function readkey | ||
698 | @treturn[1] integer the key code of the key that was pressed | ||
699 | @treturn[2] nil if no key was pressed | ||
700 | */ | ||
701 | static int lst_readkey(lua_State *L) { | ||
702 | #ifdef _WIN32 | ||
703 | if (_kbhit()) { | ||
704 | lua_pushinteger(L, _getch()); | ||
705 | return 1; | ||
706 | } | ||
707 | return 0; | ||
708 | |||
709 | #else | ||
710 | char ch; | ||
711 | if (read(STDIN_FILENO, &ch, 1) > 0) { | ||
712 | lua_pushinteger(L, ch); | ||
713 | return 1; | ||
714 | } | ||
715 | return 0; | ||
716 | |||
717 | #endif | ||
718 | } | ||
719 | |||
720 | /*** | ||
721 | Checks if a key has been pressed without reading it. | ||
722 | On Posix, `io.stdin` must be set to non-blocking mode using `setnonblock` | ||
723 | before calling this function. Otherwise it will block. | ||
724 | |||
725 | @function keypressed | ||
726 | @treturn boolean true if a key has been pressed, nil if not. | ||
727 | */ | ||
728 | static int lst_keypressed(lua_State *L) { | ||
729 | #ifdef _WIN32 | ||
730 | if (kbhit()) { | ||
731 | lua_pushboolean(L, 1); | ||
732 | return 1; | ||
733 | } | ||
734 | return 0; | ||
735 | |||
736 | #else | ||
737 | char ch; | ||
738 | if (read(STDIN_FILENO, &ch, 1) > 0) { | ||
739 | // key was read, push back to stdin | ||
740 | ungetc(ch, stdin); | ||
741 | lua_pushboolean(L, 1); | ||
742 | return 1; | ||
743 | } | ||
744 | return 0; | ||
745 | |||
746 | #endif | ||
747 | } | ||
748 | |||
749 | /*------------------------------------------------------------------------- | ||
750 | * Retrieve terminal size | ||
751 | *-------------------------------------------------------------------------*/ | ||
752 | |||
753 | |||
754 | /*** | ||
755 | Get the size of the terminal in columns and rows. | ||
756 | @function termsize | ||
757 | @treturn[1] int the number of columns | ||
758 | @treturn[1] int the number of rows | ||
759 | @treturn[2] nil | ||
760 | @treturn[2] string error message | ||
761 | */ | ||
762 | static int lst_termsize(lua_State *L) { | ||
763 | int columns, rows; | ||
764 | |||
765 | #ifdef _WIN32 | ||
766 | CONSOLE_SCREEN_BUFFER_INFO csbi; | ||
767 | if (!GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi)) { | ||
768 | termFormatError(L, GetLastError(), "Failed to get terminal size."); | ||
769 | return 2; | ||
770 | } | ||
771 | columns = csbi.srWindow.Right - csbi.srWindow.Left + 1; | ||
772 | rows = csbi.srWindow.Bottom - csbi.srWindow.Top + 1; | ||
773 | |||
774 | #else | ||
775 | struct winsize ws; | ||
776 | if (ioctl(1, TIOCGWINSZ, &ws) == -1) { | ||
777 | return pusherror(L, "Failed to get terminal size."); | ||
778 | } | ||
779 | columns = ws.ws_col; | ||
780 | rows = ws.ws_row; | ||
781 | |||
782 | #endif | ||
783 | lua_pushinteger(L, columns); | ||
784 | lua_pushinteger(L, rows); | ||
785 | return 2; | ||
786 | } | ||
787 | |||
788 | |||
789 | |||
32 | /*------------------------------------------------------------------------- | 790 | /*------------------------------------------------------------------------- |
33 | * Initializes module | 791 | * Initializes module |
34 | *-------------------------------------------------------------------------*/ | 792 | *-------------------------------------------------------------------------*/ |
793 | |||
794 | static luaL_Reg func[] = { | ||
795 | { "isatty", lst_isatty }, | ||
796 | { "getconsoleflags", lst_getconsoleflags }, | ||
797 | { "setconsoleflags", lst_setconsoleflags }, | ||
798 | { "tcgetattr", lst_tcgetattr }, | ||
799 | { "tcsetattr", lst_tcsetattr }, | ||
800 | { "getnonblock", lst_setnonblock }, | ||
801 | { "setnonblock", lst_setnonblock }, | ||
802 | { "readkey", lst_readkey }, | ||
803 | { "keypressed", lst_keypressed }, | ||
804 | { "termsize", lst_termsize }, | ||
805 | { NULL, NULL } | ||
806 | }; | ||
807 | |||
808 | |||
809 | |||
35 | void term_open(lua_State *L) { | 810 | void term_open(lua_State *L) { |
811 | // set up constants and export the constants in module table | ||
812 | initialize_valid_flags(); | ||
813 | // Windows flags | ||
814 | for (int i = 0; win_console_in_flags[i].name != NULL; i++) | ||
815 | { | ||
816 | lsbf_pushbitflags(L, win_console_in_flags[i].value); | ||
817 | lua_setfield(L, -2, win_console_in_flags[i].name); | ||
818 | } | ||
819 | for (int i = 0; win_console_out_flags[i].name != NULL; i++) | ||
820 | { | ||
821 | lsbf_pushbitflags(L, win_console_out_flags[i].value); | ||
822 | lua_setfield(L, -2, win_console_out_flags[i].name); | ||
823 | } | ||
824 | // Unix flags | ||
825 | for (int i = 0; nix_console_i_flags[i].name != NULL; i++) | ||
826 | { | ||
827 | lsbf_pushbitflags(L, nix_console_i_flags[i].value); | ||
828 | lua_setfield(L, -2, nix_console_i_flags[i].name); | ||
829 | } | ||
830 | for (int i = 0; nix_console_o_flags[i].name != NULL; i++) | ||
831 | { | ||
832 | lsbf_pushbitflags(L, nix_console_o_flags[i].value); | ||
833 | lua_setfield(L, -2, nix_console_o_flags[i].name); | ||
834 | } | ||
835 | for (int i = 0; nix_console_l_flags[i].name != NULL; i++) | ||
836 | { | ||
837 | lsbf_pushbitflags(L, nix_console_l_flags[i].value); | ||
838 | lua_setfield(L, -2, nix_console_l_flags[i].name); | ||
839 | } | ||
840 | // Unix tcsetattr actions | ||
841 | for (int i = 0; nix_tcsetattr_actions[i].name != NULL; i++) | ||
842 | { | ||
843 | lua_pushinteger(L, nix_tcsetattr_actions[i].value); | ||
844 | lua_setfield(L, -2, nix_tcsetattr_actions[i].name); | ||
845 | } | ||
846 | |||
847 | // export functions | ||
36 | luaL_setfuncs(L, func, 0); | 848 | luaL_setfuncs(L, func, 0); |
37 | } | 849 | } |