aboutsummaryrefslogtreecommitdiff
path: root/system
diff options
context:
space:
mode:
authorThijs <thijs@thijsschreijer.nl>2023-11-16 09:09:54 +0100
committerThijs Schreijer <thijs@thijsschreijer.nl>2024-04-30 09:28:01 +0200
commitbd994461ef7c2553da9a6945c685152bad50eb8f (patch)
tree28adc32712f00a200a34357e731a570bf1a359dc /system
parent47c24eed0191f8f72646be63dee94ac2b35eb062 (diff)
downloadluasystem-bd994461ef7c2553da9a6945c685152bad50eb8f.tar.gz
luasystem-bd994461ef7c2553da9a6945c685152bad50eb8f.tar.bz2
luasystem-bd994461ef7c2553da9a6945c685152bad50eb8f.zip
feat(term): getting/setting terminal config flags
Diffstat (limited to 'system')
-rw-r--r--system/init.lua212
1 files changed, 210 insertions, 2 deletions
diff --git a/system/init.lua b/system/init.lua
index 77e0c3b..c3ea94d 100644
--- a/system/init.lua
+++ b/system/init.lua
@@ -1,2 +1,210 @@
1local system = require 'system.core' 1--- Lua System Library.
2return system 2-- @module init
3
4local sys = require 'system.core'
5local global_backup -- global backup for terminal settings
6
7
8
9local add_gc_method do
10 -- feature detection; __GC meta-method, not available in all Lua versions
11 local has_gc = false
12 local tt = setmetatable({}, { -- luacheck: ignore
13 __gc = function() has_gc = true end
14 })
15
16 -- clear table and run GC to trigger
17 tt = nil
18 collectgarbage()
19 collectgarbage()
20
21
22 if has_gc then
23 -- use default GC mechanism since it is available
24 function add_gc_method(t, f)
25 setmetatable(t, { __gc = f })
26 end
27 else
28 -- create workaround using a proxy userdata, typical for Lua 5.1
29 function add_gc_method(t, f)
30 local proxy = newproxy(true)
31 getmetatable(proxy).__gc = function()
32 t["__gc_proxy"] = nil
33 f(t)
34 end
35 t["__gc_proxy"] = proxy
36 end
37 end
38end
39
40
41
42--- Returns a backup of terminal setting for stdin/out/err.
43-- Handles terminal/console flags and non-block flags on the streams.
44-- Backs up terminal/console flags only if a stream is a tty.
45-- @return table with backup of terminal settings
46function sys.termbackup()
47 local backup = {}
48
49 if sys.isatty(io.stdin) then
50 backup.console_in = sys.getconsoleflags(io.stdin)
51 backup.term_in = sys.tcgetattr(io.stdin)
52 end
53 if sys.isatty(io.stdout) then
54 backup.console_out = sys.getconsoleflags(io.stdout)
55 backup.term_out = sys.tcgetattr(io.stdout)
56 end
57 if sys.isatty(io.stderr) then
58 backup.console_err = sys.getconsoleflags(io.stderr)
59 backup.term_err = sys.tcgetattr(io.stderr)
60 end
61
62 backup.block_in = sys.getnonblock(io.stdin)
63 backup.block_out = sys.getnonblock(io.stdout)
64 backup.block_err = sys.getnonblock(io.stderr)
65
66 return backup
67end
68
69
70
71--- Restores terminal settings from a backup
72-- @tparam table backup the backup of terminal settings, see `termbackup`.
73-- @treturn boolean true
74function sys.termrestore(backup)
75 if backup.console_in then sys.setconsoleflags(io.stdin, backup.console_in) end
76 if backup.term_in then sys.tcsetattr(io.stdin, sys.TCSANOW, backup.term_in) end
77 if backup.console_out then sys.setconsoleflags(io.stdout, backup.console_out) end
78 if backup.term_out then sys.tcsetattr(io.stdout, sys.TCSANOW, backup.term_out) end
79 if backup.console_err then sys.setconsoleflags(io.stderr, backup.console_err) end
80 if backup.term_err then sys.tcsetattr(io.stderr, sys.TCSANOW, backup.term_err) end
81
82 if backup.block_in ~= nil then sys.setnonblock(io.stdin, backup.block_in) end
83 if backup.block_out ~= nil then sys.setnonblock(io.stdout, backup.block_out) end
84 if backup.block_err ~= nil then sys.setnonblock(io.stderr, backup.block_err) end
85 return true
86end
87
88
89
90--- Backs up terminal settings and restores them on application exit.
91-- Calls `termbackup` to back up terminal settings and sets up a GC method to
92-- automatically restore them on application exit (also works on Lua 5.1).
93-- @treturn[1] boolean true
94-- @treturn[2] nil if the backup was already created
95-- @treturn[2] string error message
96function sys.autotermrestore()
97 if global_backup then
98 return nil, "global terminal backup was already set up"
99 end
100 global_backup = sys.termbackup()
101 add_gc_method(global_backup, function(self)
102 sys.termrestore(self) end)
103 return true
104end
105
106
107
108do
109 local oldunpack = unpack or table.unpack
110 local pack = function(...) return { n = select("#", ...), ... } end
111 local unpack = function(t) return oldunpack(t, 1, t.n) end
112
113 --- Wraps a function to automatically restore terminal settings upon returning.
114 -- Calls `termbackup` before calling the function and `termrestore` after.
115 -- @tparam function f function to wrap
116 -- @treturn function wrapped function
117 function sys.termwrap(f)
118 if type(f) ~= "function" then
119 error("arg #1 to wrap, expected function, got " .. type(f), 2)
120 end
121
122 return function(...)
123 local bu = sys.termbackup()
124 local results = pack(f(...))
125 sys.termrestore(bu)
126 return unpack(results)
127 end
128 end
129end
130
131
132
133--- Debug function for console flags (Windows).
134-- Pretty prints the current flags set for the handle.
135-- @param fh file handle (`io.stdin`, `io.stdout`, `io.stderr`)
136-- @usage -- Print the flags for stdin/out/err
137-- system.listconsoleflags(io.stdin)
138-- system.listconsoleflags(io.stdout)
139-- system.listconsoleflags(io.stderr)
140function sys.listconsoleflags(fh)
141 local flagtype
142 if fh == io.stdin then
143 print "------ STDIN FLAGS WINDOWS ------"
144 flagtype = "CIF_"
145 elseif fh == io.stdout then
146 print "------ STDOUT FLAGS WINDOWS ------"
147 flagtype = "COF_"
148 elseif fh == io.stderr then
149 print "------ STDERR FLAGS WINDOWS ------"
150 flagtype = "COF_"
151 end
152
153 local flags = assert(sys.getconsoleflags(fh))
154 local out = {}
155 for k,v in pairs(sys) do
156 if type(k) == "string" and k:sub(1,4) == flagtype then
157 if flags:has(v) then
158 out[#out+1] = string.format("%10d [x] %s",v:value(),k)
159 else
160 out[#out+1] = string.format("%10d [ ] %s",v:value(),k)
161 end
162 end
163 end
164 table.sort(out)
165 for k,v in pairs(out) do
166 print(v)
167 end
168end
169
170
171
172--- Debug function for terminal flags (Posix).
173-- Pretty prints the current flags set for the handle.
174-- @param fh file handle (`io.stdin`, `io.stdout`, `io.stderr`)
175-- @usage -- Print the flags for stdin/out/err
176-- system.listconsoleflags(io.stdin)
177-- system.listconsoleflags(io.stdout)
178-- system.listconsoleflags(io.stderr)
179function sys.listtermflags(fh)
180 if fh == io.stdin then
181 print "------ STDIN FLAGS POSIX ------"
182 elseif fh == io.stdout then
183 print "------ STDOUT FLAGS POSIX ------"
184 elseif fh == io.stderr then
185 print "------ STDERR FLAGS POSIX ------"
186 end
187
188 local flags = assert(sys.tcgetattr(fh))
189 for _, flagtype in ipairs { "iflag", "oflag", "lflag" } do
190 local prefix = flagtype:sub(1,1):upper() .. "_" -- I_, O_, or L_, the constant prefixes
191 local out = {}
192 for k,v in pairs(sys) do
193 if type(k) == "string" and k:sub(1,2) == prefix then
194 if flags[flagtype]:has(v) then
195 out[#out+1] = string.format("%10d [x] %s",v:value(),k)
196 else
197 out[#out+1] = string.format("%10d [ ] %s",v:value(),k)
198 end
199 end
200 end
201 table.sort(out)
202 for k,v in pairs(out) do
203 print(v)
204 end
205 end
206end
207
208
209
210return sys