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/bitflags.c | |
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/bitflags.c')
-rw-r--r-- | src/bitflags.c | 235 |
1 files changed, 235 insertions, 0 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 | } | ||