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 | } |
