├── .gitignore
├── .idea
├── .gitignore
├── misc.xml
├── modules.xml
├── termui.iml
└── vcs.xml
├── Cargo.lock
├── Cargo.toml
├── README.md
├── screenshot.png
└── src
├── main.rs
├── renderer
└── mod.rs
└── screen
└── mod.rs
/.gitignore:
--------------------------------------------------------------------------------
1 | /target
2 |
--------------------------------------------------------------------------------
/.idea/.gitignore:
--------------------------------------------------------------------------------
1 | # Default ignored files
2 | /shelf/
3 | /workspace.xml
4 | # Datasource local storage ignored files
5 | /dataSources/
6 | /dataSources.local.xml
7 | # Editor-based HTTP Client requests
8 | /httpRequests/
9 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/termui.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/Cargo.lock:
--------------------------------------------------------------------------------
1 | # This file is automatically @generated by Cargo.
2 | # It is not intended for manual editing.
3 | [[package]]
4 | name = "arc-swap"
5 | version = "0.4.7"
6 | source = "registry+https://github.com/rust-lang/crates.io-index"
7 | checksum = "4d25d88fd6b8041580a654f9d0c581a047baee2b3efee13275f2fc392fc75034"
8 |
9 | [[package]]
10 | name = "arrayref"
11 | version = "0.3.6"
12 | source = "registry+https://github.com/rust-lang/crates.io-index"
13 | checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544"
14 |
15 | [[package]]
16 | name = "arrayvec"
17 | version = "0.5.1"
18 | source = "registry+https://github.com/rust-lang/crates.io-index"
19 | checksum = "cff77d8686867eceff3105329d4698d96c2391c176d5d03adc90c7389162b5b8"
20 |
21 | [[package]]
22 | name = "autocfg"
23 | version = "1.0.0"
24 | source = "registry+https://github.com/rust-lang/crates.io-index"
25 | checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d"
26 |
27 | [[package]]
28 | name = "base64"
29 | version = "0.11.0"
30 | source = "registry+https://github.com/rust-lang/crates.io-index"
31 | checksum = "b41b7ea54a0c9d92199de89e20e58d49f02f8e699814ef3fdf266f6f748d15c7"
32 |
33 | [[package]]
34 | name = "bitflags"
35 | version = "1.2.1"
36 | source = "registry+https://github.com/rust-lang/crates.io-index"
37 | checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
38 |
39 | [[package]]
40 | name = "blake2b_simd"
41 | version = "0.5.10"
42 | source = "registry+https://github.com/rust-lang/crates.io-index"
43 | checksum = "d8fb2d74254a3a0b5cac33ac9f8ed0e44aa50378d9dbb2e5d83bd21ed1dc2c8a"
44 | dependencies = [
45 | "arrayref",
46 | "arrayvec",
47 | "constant_time_eq",
48 | ]
49 |
50 | [[package]]
51 | name = "cc"
52 | version = "1.0.54"
53 | source = "registry+https://github.com/rust-lang/crates.io-index"
54 | checksum = "7bbb73db36c1246e9034e307d0fba23f9a2e251faa47ade70c1bd252220c8311"
55 |
56 | [[package]]
57 | name = "cfg-if"
58 | version = "0.1.10"
59 | source = "registry+https://github.com/rust-lang/crates.io-index"
60 | checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
61 |
62 | [[package]]
63 | name = "cloudabi"
64 | version = "0.0.3"
65 | source = "registry+https://github.com/rust-lang/crates.io-index"
66 | checksum = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f"
67 | dependencies = [
68 | "bitflags",
69 | ]
70 |
71 | [[package]]
72 | name = "constant_time_eq"
73 | version = "0.1.5"
74 | source = "registry+https://github.com/rust-lang/crates.io-index"
75 | checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc"
76 |
77 | [[package]]
78 | name = "crossbeam"
79 | version = "0.7.3"
80 | source = "registry+https://github.com/rust-lang/crates.io-index"
81 | checksum = "69323bff1fb41c635347b8ead484a5ca6c3f11914d784170b158d8449ab07f8e"
82 | dependencies = [
83 | "cfg-if",
84 | "crossbeam-channel",
85 | "crossbeam-deque",
86 | "crossbeam-epoch",
87 | "crossbeam-queue",
88 | "crossbeam-utils",
89 | ]
90 |
91 | [[package]]
92 | name = "crossbeam-channel"
93 | version = "0.4.2"
94 | source = "registry+https://github.com/rust-lang/crates.io-index"
95 | checksum = "cced8691919c02aac3cb0a1bc2e9b73d89e832bf9a06fc579d4e71b68a2da061"
96 | dependencies = [
97 | "crossbeam-utils",
98 | "maybe-uninit",
99 | ]
100 |
101 | [[package]]
102 | name = "crossbeam-deque"
103 | version = "0.7.3"
104 | source = "registry+https://github.com/rust-lang/crates.io-index"
105 | checksum = "9f02af974daeee82218205558e51ec8768b48cf524bd01d550abe5573a608285"
106 | dependencies = [
107 | "crossbeam-epoch",
108 | "crossbeam-utils",
109 | "maybe-uninit",
110 | ]
111 |
112 | [[package]]
113 | name = "crossbeam-epoch"
114 | version = "0.8.2"
115 | source = "registry+https://github.com/rust-lang/crates.io-index"
116 | checksum = "058ed274caafc1f60c4997b5fc07bf7dc7cca454af7c6e81edffe5f33f70dace"
117 | dependencies = [
118 | "autocfg",
119 | "cfg-if",
120 | "crossbeam-utils",
121 | "lazy_static",
122 | "maybe-uninit",
123 | "memoffset",
124 | "scopeguard",
125 | ]
126 |
127 | [[package]]
128 | name = "crossbeam-queue"
129 | version = "0.2.3"
130 | source = "registry+https://github.com/rust-lang/crates.io-index"
131 | checksum = "774ba60a54c213d409d5353bda12d49cd68d14e45036a285234c8d6f91f92570"
132 | dependencies = [
133 | "cfg-if",
134 | "crossbeam-utils",
135 | "maybe-uninit",
136 | ]
137 |
138 | [[package]]
139 | name = "crossbeam-utils"
140 | version = "0.7.2"
141 | source = "registry+https://github.com/rust-lang/crates.io-index"
142 | checksum = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8"
143 | dependencies = [
144 | "autocfg",
145 | "cfg-if",
146 | "lazy_static",
147 | ]
148 |
149 | [[package]]
150 | name = "crossterm"
151 | version = "0.17.5"
152 | source = "registry+https://github.com/rust-lang/crates.io-index"
153 | checksum = "9851d20b9809e561297ec3ca85d7cba3a57507fe8d01d07ba7b52469e1c89a11"
154 | dependencies = [
155 | "bitflags",
156 | "crossterm_winapi",
157 | "lazy_static",
158 | "libc",
159 | "mio",
160 | "parking_lot",
161 | "signal-hook",
162 | "winapi 0.3.8",
163 | ]
164 |
165 | [[package]]
166 | name = "crossterm_winapi"
167 | version = "0.6.1"
168 | source = "registry+https://github.com/rust-lang/crates.io-index"
169 | checksum = "057b7146d02fb50175fd7dbe5158f6097f33d02831f43b4ee8ae4ddf67b68f5c"
170 | dependencies = [
171 | "winapi 0.3.8",
172 | ]
173 |
174 | [[package]]
175 | name = "dirs"
176 | version = "2.0.2"
177 | source = "registry+https://github.com/rust-lang/crates.io-index"
178 | checksum = "13aea89a5c93364a98e9b37b2fa237effbb694d5cfe01c5b70941f7eb087d5e3"
179 | dependencies = [
180 | "cfg-if",
181 | "dirs-sys",
182 | ]
183 |
184 | [[package]]
185 | name = "dirs-sys"
186 | version = "0.3.5"
187 | source = "registry+https://github.com/rust-lang/crates.io-index"
188 | checksum = "8e93d7f5705de3e49895a2b5e0b8855a1c27f080192ae9c32a6432d50741a57a"
189 | dependencies = [
190 | "libc",
191 | "redox_users",
192 | "winapi 0.3.8",
193 | ]
194 |
195 | [[package]]
196 | name = "errno"
197 | version = "0.2.5"
198 | source = "registry+https://github.com/rust-lang/crates.io-index"
199 | checksum = "b480f641ccf0faf324e20c1d3e53d81b7484c698b42ea677f6907ae4db195371"
200 | dependencies = [
201 | "errno-dragonfly",
202 | "libc",
203 | "winapi 0.3.8",
204 | ]
205 |
206 | [[package]]
207 | name = "errno-dragonfly"
208 | version = "0.1.1"
209 | source = "registry+https://github.com/rust-lang/crates.io-index"
210 | checksum = "14ca354e36190500e1e1fb267c647932382b54053c50b14970856c0b00a35067"
211 | dependencies = [
212 | "gcc",
213 | "libc",
214 | ]
215 |
216 | [[package]]
217 | name = "fuchsia-zircon"
218 | version = "0.3.3"
219 | source = "registry+https://github.com/rust-lang/crates.io-index"
220 | checksum = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82"
221 | dependencies = [
222 | "bitflags",
223 | "fuchsia-zircon-sys",
224 | ]
225 |
226 | [[package]]
227 | name = "fuchsia-zircon-sys"
228 | version = "0.3.3"
229 | source = "registry+https://github.com/rust-lang/crates.io-index"
230 | checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7"
231 |
232 | [[package]]
233 | name = "gcc"
234 | version = "0.3.55"
235 | source = "registry+https://github.com/rust-lang/crates.io-index"
236 | checksum = "8f5f3913fa0bfe7ee1fd8248b6b9f42a5af4b9d65ec2dd2c3c26132b950ecfc2"
237 |
238 | [[package]]
239 | name = "getrandom"
240 | version = "0.1.14"
241 | source = "registry+https://github.com/rust-lang/crates.io-index"
242 | checksum = "7abc8dd8451921606d809ba32e95b6111925cd2906060d2dcc29c070220503eb"
243 | dependencies = [
244 | "cfg-if",
245 | "libc",
246 | "wasi",
247 | ]
248 |
249 | [[package]]
250 | name = "iovec"
251 | version = "0.1.4"
252 | source = "registry+https://github.com/rust-lang/crates.io-index"
253 | checksum = "b2b3ea6ff95e175473f8ffe6a7eb7c00d054240321b84c57051175fe3c1e075e"
254 | dependencies = [
255 | "libc",
256 | ]
257 |
258 | [[package]]
259 | name = "kernel32-sys"
260 | version = "0.2.2"
261 | source = "registry+https://github.com/rust-lang/crates.io-index"
262 | checksum = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
263 | dependencies = [
264 | "winapi 0.2.8",
265 | "winapi-build",
266 | ]
267 |
268 | [[package]]
269 | name = "lazy_static"
270 | version = "1.4.0"
271 | source = "registry+https://github.com/rust-lang/crates.io-index"
272 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
273 |
274 | [[package]]
275 | name = "libc"
276 | version = "0.2.71"
277 | source = "registry+https://github.com/rust-lang/crates.io-index"
278 | checksum = "9457b06509d27052635f90d6466700c65095fdf75409b3fbdd903e988b886f49"
279 |
280 | [[package]]
281 | name = "lock_api"
282 | version = "0.3.4"
283 | source = "registry+https://github.com/rust-lang/crates.io-index"
284 | checksum = "c4da24a77a3d8a6d4862d95f72e6fdb9c09a643ecdb402d754004a557f2bec75"
285 | dependencies = [
286 | "scopeguard",
287 | ]
288 |
289 | [[package]]
290 | name = "log"
291 | version = "0.4.8"
292 | source = "registry+https://github.com/rust-lang/crates.io-index"
293 | checksum = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7"
294 | dependencies = [
295 | "cfg-if",
296 | ]
297 |
298 | [[package]]
299 | name = "maybe-uninit"
300 | version = "2.0.0"
301 | source = "registry+https://github.com/rust-lang/crates.io-index"
302 | checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00"
303 |
304 | [[package]]
305 | name = "memoffset"
306 | version = "0.5.4"
307 | source = "registry+https://github.com/rust-lang/crates.io-index"
308 | checksum = "b4fc2c02a7e374099d4ee95a193111f72d2110197fe200272371758f6c3643d8"
309 | dependencies = [
310 | "autocfg",
311 | ]
312 |
313 | [[package]]
314 | name = "mio"
315 | version = "0.6.22"
316 | source = "registry+https://github.com/rust-lang/crates.io-index"
317 | checksum = "fce347092656428bc8eaf6201042cb551b8d67855af7374542a92a0fbfcac430"
318 | dependencies = [
319 | "cfg-if",
320 | "fuchsia-zircon",
321 | "fuchsia-zircon-sys",
322 | "iovec",
323 | "kernel32-sys",
324 | "libc",
325 | "log",
326 | "miow",
327 | "net2",
328 | "slab",
329 | "winapi 0.2.8",
330 | ]
331 |
332 | [[package]]
333 | name = "miow"
334 | version = "0.2.1"
335 | source = "registry+https://github.com/rust-lang/crates.io-index"
336 | checksum = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919"
337 | dependencies = [
338 | "kernel32-sys",
339 | "net2",
340 | "winapi 0.2.8",
341 | "ws2_32-sys",
342 | ]
343 |
344 | [[package]]
345 | name = "net2"
346 | version = "0.2.34"
347 | source = "registry+https://github.com/rust-lang/crates.io-index"
348 | checksum = "2ba7c918ac76704fb42afcbbb43891e72731f3dcca3bef2a19786297baf14af7"
349 | dependencies = [
350 | "cfg-if",
351 | "libc",
352 | "winapi 0.3.8",
353 | ]
354 |
355 | [[package]]
356 | name = "nix"
357 | version = "0.17.0"
358 | source = "registry+https://github.com/rust-lang/crates.io-index"
359 | checksum = "50e4785f2c3b7589a0d0c1dd60285e1188adac4006e8abd6dd578e1567027363"
360 | dependencies = [
361 | "bitflags",
362 | "cc",
363 | "cfg-if",
364 | "libc",
365 | "void",
366 | ]
367 |
368 | [[package]]
369 | name = "parking_lot"
370 | version = "0.10.2"
371 | source = "registry+https://github.com/rust-lang/crates.io-index"
372 | checksum = "d3a704eb390aafdc107b0e392f56a82b668e3a71366993b5340f5833fd62505e"
373 | dependencies = [
374 | "lock_api",
375 | "parking_lot_core",
376 | ]
377 |
378 | [[package]]
379 | name = "parking_lot_core"
380 | version = "0.7.2"
381 | source = "registry+https://github.com/rust-lang/crates.io-index"
382 | checksum = "d58c7c768d4ba344e3e8d72518ac13e259d7c7ade24167003b8488e10b6740a3"
383 | dependencies = [
384 | "cfg-if",
385 | "cloudabi",
386 | "libc",
387 | "redox_syscall",
388 | "smallvec",
389 | "winapi 0.3.8",
390 | ]
391 |
392 | [[package]]
393 | name = "proc-macro2"
394 | version = "1.0.18"
395 | source = "registry+https://github.com/rust-lang/crates.io-index"
396 | checksum = "beae6331a816b1f65d04c45b078fd8e6c93e8071771f41b8163255bbd8d7c8fa"
397 | dependencies = [
398 | "unicode-xid",
399 | ]
400 |
401 | [[package]]
402 | name = "quote"
403 | version = "1.0.7"
404 | source = "registry+https://github.com/rust-lang/crates.io-index"
405 | checksum = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37"
406 | dependencies = [
407 | "proc-macro2",
408 | ]
409 |
410 | [[package]]
411 | name = "redox_syscall"
412 | version = "0.1.56"
413 | source = "registry+https://github.com/rust-lang/crates.io-index"
414 | checksum = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84"
415 |
416 | [[package]]
417 | name = "redox_users"
418 | version = "0.3.4"
419 | source = "registry+https://github.com/rust-lang/crates.io-index"
420 | checksum = "09b23093265f8d200fa7b4c2c76297f47e681c655f6f1285a8780d6a022f7431"
421 | dependencies = [
422 | "getrandom",
423 | "redox_syscall",
424 | "rust-argon2",
425 | ]
426 |
427 | [[package]]
428 | name = "rust-argon2"
429 | version = "0.7.0"
430 | source = "registry+https://github.com/rust-lang/crates.io-index"
431 | checksum = "2bc8af4bda8e1ff4932523b94d3dd20ee30a87232323eda55903ffd71d2fb017"
432 | dependencies = [
433 | "base64",
434 | "blake2b_simd",
435 | "constant_time_eq",
436 | "crossbeam-utils",
437 | ]
438 |
439 | [[package]]
440 | name = "scopeguard"
441 | version = "1.1.0"
442 | source = "registry+https://github.com/rust-lang/crates.io-index"
443 | checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
444 |
445 | [[package]]
446 | name = "signal-hook"
447 | version = "0.1.16"
448 | source = "registry+https://github.com/rust-lang/crates.io-index"
449 | checksum = "604508c1418b99dfe1925ca9224829bb2a8a9a04dda655cc01fcad46f4ab05ed"
450 | dependencies = [
451 | "libc",
452 | "mio",
453 | "signal-hook-registry",
454 | ]
455 |
456 | [[package]]
457 | name = "signal-hook-registry"
458 | version = "1.2.0"
459 | source = "registry+https://github.com/rust-lang/crates.io-index"
460 | checksum = "94f478ede9f64724c5d173d7bb56099ec3e2d9fc2774aac65d34b8b890405f41"
461 | dependencies = [
462 | "arc-swap",
463 | "libc",
464 | ]
465 |
466 | [[package]]
467 | name = "slab"
468 | version = "0.4.2"
469 | source = "registry+https://github.com/rust-lang/crates.io-index"
470 | checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8"
471 |
472 | [[package]]
473 | name = "smallvec"
474 | version = "1.4.0"
475 | source = "registry+https://github.com/rust-lang/crates.io-index"
476 | checksum = "c7cb5678e1615754284ec264d9bb5b4c27d2018577fd90ac0ceb578591ed5ee4"
477 |
478 | [[package]]
479 | name = "term"
480 | version = "0.6.1"
481 | source = "registry+https://github.com/rust-lang/crates.io-index"
482 | checksum = "c0863a3345e70f61d613eab32ee046ccd1bcc5f9105fe402c61fcd0c13eeb8b5"
483 | dependencies = [
484 | "dirs",
485 | "winapi 0.3.8",
486 | ]
487 |
488 | [[package]]
489 | name = "termui"
490 | version = "0.1.0"
491 | dependencies = [
492 | "crossbeam",
493 | "crossterm",
494 | "errno",
495 | "libc",
496 | "nix",
497 | "term",
498 | "vte",
499 | ]
500 |
501 | [[package]]
502 | name = "unicode-xid"
503 | version = "0.2.0"
504 | source = "registry+https://github.com/rust-lang/crates.io-index"
505 | checksum = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c"
506 |
507 | [[package]]
508 | name = "utf8parse"
509 | version = "0.2.0"
510 | source = "registry+https://github.com/rust-lang/crates.io-index"
511 | checksum = "936e4b492acfd135421d8dca4b1aa80a7bfc26e702ef3af710e0752684df5372"
512 |
513 | [[package]]
514 | name = "void"
515 | version = "1.0.2"
516 | source = "registry+https://github.com/rust-lang/crates.io-index"
517 | checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
518 |
519 | [[package]]
520 | name = "vte"
521 | version = "0.8.0"
522 | source = "registry+https://github.com/rust-lang/crates.io-index"
523 | checksum = "96cc8a191608603611e78c6ec11dafef37e3cca0775aeef1931824753e81711d"
524 | dependencies = [
525 | "arrayvec",
526 | "utf8parse",
527 | "vte_generate_state_changes",
528 | ]
529 |
530 | [[package]]
531 | name = "vte_generate_state_changes"
532 | version = "0.1.1"
533 | source = "registry+https://github.com/rust-lang/crates.io-index"
534 | checksum = "d257817081c7dffcdbab24b9e62d2def62e2ff7d00b1c20062551e6cccc145ff"
535 | dependencies = [
536 | "proc-macro2",
537 | "quote",
538 | ]
539 |
540 | [[package]]
541 | name = "wasi"
542 | version = "0.9.0+wasi-snapshot-preview1"
543 | source = "registry+https://github.com/rust-lang/crates.io-index"
544 | checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519"
545 |
546 | [[package]]
547 | name = "winapi"
548 | version = "0.2.8"
549 | source = "registry+https://github.com/rust-lang/crates.io-index"
550 | checksum = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a"
551 |
552 | [[package]]
553 | name = "winapi"
554 | version = "0.3.8"
555 | source = "registry+https://github.com/rust-lang/crates.io-index"
556 | checksum = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6"
557 | dependencies = [
558 | "winapi-i686-pc-windows-gnu",
559 | "winapi-x86_64-pc-windows-gnu",
560 | ]
561 |
562 | [[package]]
563 | name = "winapi-build"
564 | version = "0.1.1"
565 | source = "registry+https://github.com/rust-lang/crates.io-index"
566 | checksum = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc"
567 |
568 | [[package]]
569 | name = "winapi-i686-pc-windows-gnu"
570 | version = "0.4.0"
571 | source = "registry+https://github.com/rust-lang/crates.io-index"
572 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
573 |
574 | [[package]]
575 | name = "winapi-x86_64-pc-windows-gnu"
576 | version = "0.4.0"
577 | source = "registry+https://github.com/rust-lang/crates.io-index"
578 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
579 |
580 | [[package]]
581 | name = "ws2_32-sys"
582 | version = "0.2.1"
583 | source = "registry+https://github.com/rust-lang/crates.io-index"
584 | checksum = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e"
585 | dependencies = [
586 | "winapi 0.2.8",
587 | "winapi-build",
588 | ]
589 |
--------------------------------------------------------------------------------
/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "termui"
3 | version = "0.1.0"
4 | authors = ["Teln0 "]
5 | edition = "2018"
6 |
7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
8 |
9 | [dependencies]
10 | crossterm = "0.17.5"
11 | crossbeam = "0.7.3"
12 | nix = "0.17.0"
13 | libc = "0.2.71"
14 | errno = "0.2.5"
15 | term = "0.6.1"
16 | vte = "0.8.0"
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## TermUI : A Window manager for the command line.
2 |
3 | TermUI is a simple (for now) window manager to be used in a terminal.
4 | For now, it will display 3 movables and resizables windows with a separate shell running in every one of them.
5 | You can run in those windows commands like "echo" and other bash commands.
6 |
7 | 
8 |
9 | Feel free to contribute or join me on this project !
--------------------------------------------------------------------------------
/screenshot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Teln0/TermUI/7d56a749ac84a1a5fe311637e62a3a1278890024/screenshot.png
--------------------------------------------------------------------------------
/src/main.rs:
--------------------------------------------------------------------------------
1 | mod renderer;
2 | mod screen;
3 |
4 | use crossterm::{event::*, Result, terminal::size, QueueableCommand};
5 | use crate::screen::{Screen, SimpleTerminalWindow};
6 | use crossterm::cursor::{DisableBlinking, EnableBlinking};
7 | use std::io::{Write, stdout, Read};
8 | use std::process::{exit, Command};
9 | use crossterm::terminal::{enable_raw_mode, disable_raw_mode};
10 | use std::ops::Deref;
11 | use std::borrow::Borrow;
12 | use std::cell::RefCell;
13 | use std::rc::Rc;
14 | use std::thread::sleep;
15 | use std::time::Duration;
16 | use std::process::Stdio;
17 |
18 | fn main() {
19 | let mut stdout = std::io::stdout();
20 | let mut screen = Screen::new();
21 |
22 | enable_raw_mode();
23 | stdout
24 | .queue(DisableBlinking).unwrap()
25 | .queue(EnableMouseCapture).unwrap()
26 | .flush().unwrap();
27 |
28 | screen.add_container(Rc::new(RefCell::new(Box::new(SimpleTerminalWindow::new(
29 | 5,
30 | 5,
31 | 60,
32 | 15,
33 | "1".to_string(),
34 | )))));
35 |
36 | screen.add_container(Rc::new(RefCell::new(Box::new(SimpleTerminalWindow::new(
37 | 15,
38 | 15,
39 | 60,
40 | 15,
41 | "2".to_string(),
42 | )))));
43 |
44 | screen.add_container(Rc::new(RefCell::new(Box::new(SimpleTerminalWindow::new(
45 | 25,
46 | 25,
47 | 60,
48 | 15,
49 | "3".to_string(),
50 | )))));
51 |
52 | let size = size();
53 | let mut current_w = 0;
54 | let mut current_h = 0;
55 |
56 | if size.is_ok() {
57 | let size = size.unwrap();
58 | current_w = size.0;
59 | current_h = size.1;
60 | renderer::redraw(&mut stdout, current_w, current_h, &screen);
61 | }
62 | loop {
63 | for con in screen.containers.iter() {
64 | con.deref().borrow_mut().update_content();
65 | }
66 | renderer::redraw(&mut stdout, current_w, current_h, &screen);
67 | while poll(Duration::from_millis(0)).unwrap() {
68 | let event = read();
69 | if event.is_ok() {
70 | match event.unwrap() {
71 | Event::Resize(w, h) => {
72 | current_w = w;
73 | current_h = h;
74 | }
75 | Event::Mouse(mouseEvent) => {
76 | match mouseEvent {
77 | MouseEvent::Down(mouse_button, x, y, key_modifiers) => {
78 | screen.check_top_container(x, y);
79 | screen.get_top_container().unwrap().borrow_mut().on_mouse_down(x, y);
80 | }
81 | MouseEvent::Up(mouse_button, x, y, key_modifiers) => {
82 | screen.get_top_container().unwrap().borrow_mut().on_mouse_up(x, y);
83 | }
84 | MouseEvent::Drag(mouse_button, x, y, key_modifiers) => {
85 | screen.get_top_container().unwrap().borrow_mut().on_mouse_drag(x, y);
86 | }
87 | MouseEvent::ScrollUp(x, y, key_modifiers) => {
88 | screen.get_top_container().unwrap().borrow_mut().on_scroll_y(-1);
89 | }
90 | MouseEvent::ScrollDown(x, y, key_modifiers) => {
91 | screen.get_top_container().unwrap().borrow_mut().on_scroll_y(1);
92 | }
93 | _ => {}
94 | };
95 | }
96 | Event::Key(key_event) => {
97 | if key_event.code == KeyCode::Char('c') {
98 | if key_event.modifiers == KeyModifiers::CONTROL {
99 | stdout
100 | .queue(EnableBlinking).unwrap()
101 | .queue(DisableMouseCapture).unwrap();
102 | disable_raw_mode().unwrap();
103 | }
104 | }
105 |
106 | screen.get_top_container().unwrap().borrow_mut().on_key(key_event.code, key_event.modifiers);
107 | }
108 | _ => {}
109 | }
110 | }
111 | }
112 | }
113 | }
--------------------------------------------------------------------------------
/src/renderer/mod.rs:
--------------------------------------------------------------------------------
1 | use std::io::{stdout, Write, BufWriter, Stdout, StdoutLock};
2 | use crossterm::{
3 | ExecutableCommand, QueueableCommand,
4 | terminal, cursor, style::{self, Colorize}, Result
5 | };
6 | use crate::screen::Screen;
7 | use std::ops::Deref;
8 | use std::cmp::min;
9 | use std::ffi::CString;
10 |
11 | fn draw_rect(stdout: &mut Vec, x: u16, y: u16, w: u16, h: u16, max_w: u16, max_h: u16) {
12 | for x_iterator in x..(x+w) {
13 | for y_iterator in y..(y+h) {
14 | if (x_iterator < max_w && y_iterator < max_h) && ((x_iterator == x || y_iterator == y) || (x_iterator == x + w - 1 || y_iterator == y + h - 1)) {
15 | stdout
16 | .queue(cursor::MoveTo(x_iterator,y_iterator)).unwrap()
17 | .queue(style::PrintStyledContent( "█".white())).unwrap();
18 | }
19 | else {
20 | stdout
21 | .queue(cursor::MoveTo(x_iterator,y_iterator)).unwrap()
22 | .queue(style::PrintStyledContent( "█".black())).unwrap();
23 | }
24 | }
25 | }
26 | }
27 |
28 | fn render_text(stdout: &mut Vec, x: u16, y: u16, max_h: u16, text: String) {
29 | let lines: Vec<&str> = text.split("\n").collect();
30 | let mut i = 0;
31 | for mut line in lines {
32 | if i > max_h {
33 | return;
34 | }
35 | let line_str = line.to_string();
36 |
37 | stdout
38 | .queue(cursor::MoveTo(x, y + i)).unwrap()
39 | .queue(crossterm::style::Print(line_str.as_str())).unwrap();
40 |
41 | i += 1;
42 | }
43 | }
44 |
45 | pub fn redraw(stdout: &mut Stdout, w: u16, h: u16, screen: &Screen) -> Result<()> {
46 | let mut vec: Vec = vec![];
47 | let mut s = stdout;
48 | let mut stdout = vec;
49 |
50 | // Clear the screen
51 | draw_rect(&mut stdout, 0, 0, w, h, w, h);
52 |
53 | for con in screen.containers.iter() {
54 | let con = con.deref().borrow();
55 | // Draw the container border around the window
56 | draw_rect(&mut stdout, con.get_x() - 1, con.get_y() - 1, con.get_width() + 2, con.get_height() + 2, w, h);
57 | // Draw the container title info
58 | let mut info = "".to_string();
59 | let title = con.get_title();
60 | if title.is_some() {
61 | info.push_str(title.unwrap());
62 | info.push(' ');
63 | }
64 | info.push_str(format!("W: {} H: {} X: {} Y: {} Cursor: {:?} {}",
65 | con.get_width(),
66 | con.get_height(),
67 | con.get_x(),
68 | con.get_y(),
69 | con.get_cursor(),
70 | con.get_printed_chars()
71 | ).as_str());
72 | stdout
73 | .queue(cursor::MoveTo(con.get_x(), con.get_y() - 1)).unwrap()
74 | .queue(crossterm::style::Print(info)).unwrap();
75 | // Draw the container's content
76 | let mut con_width = con.get_width();
77 | let mut con_height = con.get_height();
78 | let mut do_render_text = true;
79 | if con.get_x() > w || con.get_y() > h - 1 {
80 | do_render_text = false;
81 | }
82 | if do_render_text {
83 | if con.get_x() + con_width > w {
84 | con_width -= con.get_x() + con_width - w;
85 | }
86 | if con.get_y() + con_height > h {
87 | con_height -= con.get_y() + con_height - h;
88 | }
89 | render_text(&mut stdout, con.get_x(), con.get_y(), con_height - 1, con.get_content());
90 | }
91 | // Draw the resize handles
92 | if con.get_x() + con.get_width() < w {
93 | stdout
94 | .queue(cursor::MoveTo(con.get_x() + con.get_width(), con.get_y() + con.get_height() / 2)).unwrap()
95 | .queue(crossterm::style::Print("↔"))?;
96 | }
97 | if con.get_y() + con.get_height() < h {
98 | stdout
99 | .queue(cursor::MoveTo(con.get_x() + con.get_width() / 2, con.get_y() + con.get_height())).unwrap()
100 | .queue(crossterm::style::Print("↕"))?;
101 | }
102 | }
103 |
104 | // Render some info about TermUI
105 | let info_string = format!("Stdout buffer size : {}", stdout.len());
106 | stdout
107 | .queue(cursor::MoveTo(2, 0)).unwrap()
108 | .queue(crossterm::style::Print(info_string));
109 | // Render dev console
110 | s.write_all(&stdout);
111 | Ok(())
112 | }
--------------------------------------------------------------------------------
/src/screen/mod.rs:
--------------------------------------------------------------------------------
1 | use std::cell::RefCell;
2 | use std::rc::Rc;
3 | use std::ops::Deref;
4 | use std::borrow::BorrowMut;
5 | use std::io::*;
6 | use std::process::{Command, Stdio, ChildStdin, ChildStdout, exit};
7 | use std::thread;
8 | use std::sync::Arc;
9 | use crossbeam::queue::SegQueue;
10 | use std::str;
11 | use crossterm::event::{KeyCode, KeyModifiers};
12 |
13 | use std::path::Path;
14 | use nix::fcntl::{OFlag, open};
15 | use nix::pty::{grantpt, posix_openpt, ptsname, unlockpt, Winsize};
16 | use nix::sys::stat::Mode;
17 | use nix::unistd::{fork, ForkResult, close, setsid, dup2, Pid};
18 | use std::os::unix::io::{AsRawFd, FromRawFd, RawFd};
19 | use nix::{ioctl_none_bad, ioctl_write_ptr_bad};
20 |
21 | use libc;
22 | use std::os::raw::{c_char, c_void};
23 | use std::ffi::{CString, CStr};
24 | use core::ptr;
25 | use std::fs::File;
26 | use libc::{TIOCSCTTY, TIOCSWINSZ};
27 | use term::Attr;
28 | use vte::{Parser, Perform};
29 | use std::alloc::handle_alloc_error;
30 | use std::thread::current;
31 |
32 | ioctl_write_ptr_bad!(set_window_size, TIOCSWINSZ, Winsize);
33 | ioctl_none_bad!(set_controlling_terminal, TIOCSCTTY);
34 |
35 | #[derive(Copy, Clone)]
36 | pub struct CharacterCell {
37 | pub ch: char,
38 | pub fg: u8,
39 | pub bg: u8,
40 | pub attrs: Attr,
41 | }
42 |
43 | struct EmbedGrid {
44 | printed_chars: usize,
45 | cursor: (usize, usize),
46 | grid: Vec,
47 | fg_color: u8,
48 | bg_color: u8,
49 | width: usize,
50 | height: usize
51 | }
52 |
53 | pub struct SimpleTerminalWindow {
54 | pub x: u16,
55 | pub y: u16,
56 | pub width: u16,
57 | pub height: u16,
58 | pub title: String,
59 | scroll_y: u16,
60 | grid: EmbedGrid,
61 | last_mouse_down_pos_coords: (u16, u16),
62 | last_size: (u16, u16),
63 | last_pos: (u16, u16),
64 | master_fd: File,
65 | child_pid: Pid,
66 | queue: Arc>,
67 | vte_parser: Parser
68 | }
69 |
70 | impl Perform for EmbedGrid {
71 | fn print(&mut self, c: char) {
72 | self.printed_chars += 1;
73 |
74 | self.grid[self.cursor.0 + self.cursor.1 * self.width as usize].ch = c;
75 | self.grid[self.cursor.0 + self.cursor.1 * self.width as usize].fg = self.fg_color;
76 | self.grid[self.cursor.0 + self.cursor.1 * self.width as usize].bg = self.bg_color;
77 |
78 | self.cursor.0 += 1;
79 | }
80 |
81 | fn execute(&mut self, byte: u8) {
82 | match byte {
83 | 0x08 => {
84 | if self.cursor.0 > 0 {
85 | self.cursor.0 -= 1;
86 | }
87 | },
88 | 0x0A => {
89 | if self.cursor.1 <= self.height {
90 | self.cursor.1 += 1;
91 | }
92 | }
93 | 0x0D => {
94 | self.cursor.0 = 0;
95 | }
96 | 0x07 => {
97 | print!("\x07");
98 | }
99 | c => {
100 | println!(" {:?}", c as char);
101 | panic!();
102 | }
103 | };
104 | }
105 |
106 | fn hook(&mut self, params: &[i64], intermediates: &[u8], ignore: bool, action: char) {
107 | panic!();
108 | }
109 |
110 | fn put(&mut self, byte: u8) {
111 | panic!();
112 | }
113 |
114 | fn unhook(&mut self) {
115 | panic!();
116 | }
117 |
118 | fn osc_dispatch(&mut self, params: &[&[u8]], bell_terminated: bool) {
119 | panic!();
120 | }
121 |
122 | fn csi_dispatch(&mut self, params: &[i64], intermediates: &[u8], ignore: bool, action: char) {
123 | return;
124 | match action {
125 | 'A' => { // Cursor Up
126 | if (self.cursor.1 as i64) > params[0] {
127 | self.cursor.1 -= params[0] as usize;
128 | }
129 | else {
130 | self.cursor.1 = 0;
131 | }
132 | },
133 | 'B' => { // Cursor Down
134 | if (self.height as i64) > self.cursor.1 as i64 + params[0] {
135 | self.cursor.1 += params[0] as usize;
136 | }
137 | else {
138 | self.cursor.1 = self.height - 1;
139 | }
140 | },
141 | 'D' => { // Cursor Back
142 | if (self.cursor.0 as i64) > params[0] {
143 | self.cursor.0 -= params[0] as usize;
144 | }
145 | else {
146 | self.cursor.0 = 0;
147 | }
148 | },
149 | 'C' => { // Cursor Forwards
150 | if (self.width as i64) > self.cursor.0 as i64 + params[0] {
151 | self.cursor.0 += params[0] as usize;
152 | }
153 | else {
154 | self.cursor.0 = self.height - 0;
155 | }
156 | },
157 | 'E' => { // Go down then to the beginning of the line
158 | if (self.height as i64) > self.cursor.1 as i64 + params[0] {
159 | self.cursor.1 += params[0] as usize;
160 | }
161 | else {
162 | self.cursor.1 = self.height - 1;
163 | }
164 | self.cursor.0 = 0;
165 | },
166 | 'F' => { // Go up then to the beginning of the line
167 | if (self.cursor.1 as i64) > params[0] {
168 | self.cursor.1 -= params[0] as usize;
169 | }
170 | else {
171 | self.cursor.1 = 0;
172 | }
173 | self.cursor.0 = 0;
174 | },
175 | 'G' => { // Set cursor horizontal pos
176 | if params[0] < self.width as i64 {
177 | if params[0] > 0 {
178 | self.cursor.0 = params[0] as usize;
179 | }
180 | else {
181 | self.cursor.0 = 0;
182 | }
183 | }
184 | else {
185 | self.cursor.0 = self.width - 1;
186 | }
187 | },
188 | 'H' => { // Set cursor pos
189 | let y = params[0] - 1;
190 | let x = params[1] - 1;
191 |
192 | if x <= self.width as i64 {
193 | if x > 0 {
194 | self.cursor.0 = x as usize;
195 | }
196 | else {
197 | self.cursor.0 = 0;
198 | }
199 | }
200 | else {
201 | self.cursor.0 = self.width - 1;
202 | }
203 |
204 | if y <= self.height as i64 {
205 | if y > 0 {
206 | self.cursor.1 = y as usize;
207 | }
208 | else {
209 | self.cursor.1 = 0;
210 | }
211 | }
212 | else {
213 | self.cursor.1 = self.height - 1;
214 | }
215 | },
216 | 'J' => {
217 | match params[0] {
218 | 0 => {
219 | let index = self.cursor.0 + self.cursor.1 * self.width;
220 | let end = self.width * self.height;
221 | for i in index..end {
222 | self.grid[i] = CharacterCell {
223 | fg: 37,
224 | bg: 40,
225 | ch: ' ',
226 | attrs: Attr::BackgroundColor(40)
227 | }
228 | }
229 | },
230 | 1 => {
231 | let index = 0;
232 | let end = self.cursor.0 + self.cursor.1 * self.width + 1;
233 | for i in index..end {
234 | self.grid[i] = CharacterCell {
235 | fg: 37,
236 | bg: 40,
237 | ch: ' ',
238 | attrs: Attr::BackgroundColor(40)
239 | }
240 | }
241 | },
242 | 2 | 3 => {
243 | let end = self.cursor.0 + self.cursor.1 * self.width + 1;
244 | for i in 0..end {
245 | self.grid[i] = CharacterCell {
246 | fg: 37,
247 | bg: 40,
248 | ch: ' ',
249 | attrs: Attr::BackgroundColor(40)
250 | }
251 | }
252 | }
253 | _ => {
254 |
255 | }
256 | }
257 | }
258 | 'm' => { // Select Graphic Rendition
259 | for i in params {
260 | match i {
261 | 30 => { // FG black
262 | self.fg_color = 30;
263 | },
264 | 31 => { // FG red
265 | self.fg_color = 31;
266 | },
267 | 32 => { // FG green
268 | self.fg_color = 32;
269 | },
270 | 33 => { // FG yellow
271 | self.fg_color = 33;
272 | },
273 | 34 => { // FG blue
274 | self.fg_color = 34;
275 | },
276 | 35 => { // FG magenta
277 | self.fg_color = 35;
278 | },
279 | 36 => { // FG cyan
280 | self.fg_color = 36;
281 | },
282 | 37 => { // FG white
283 | self.fg_color = 37;
284 | },
285 | 39 => { // Default FG color (white)
286 | self.fg_color = 37;
287 | },
288 |
289 | 40 => { // FG black
290 | self.bg_color = 40;
291 | },
292 | 41 => { // BG red
293 | self.bg_color = 41;
294 | },
295 | 42 => { // BG green
296 | self.bg_color = 42;
297 | },
298 | 43 => { // BG yellow
299 | self.bg_color = 43;
300 | },
301 | 44 => { // BG blue
302 | self.bg_color = 44;
303 | },
304 | 45 => { // BG magenta
305 | self.bg_color = 45;
306 | },
307 | 46 => { // BG cyan
308 | self.bg_color = 46;
309 | },
310 | 47 => { // BG white
311 | self.bg_color = 47;
312 | },
313 | 49 => { // Default BG color (black)
314 | self.bg_color = 40;
315 | }
316 |
317 | 0 => { // Reset all
318 | self.fg_color = 37;
319 | self.bg_color = 40;
320 | }
321 | _ => {
322 |
323 | }
324 | }
325 | }
326 | }
327 | _ => {
328 | panic!();
329 | }
330 | }
331 | }
332 |
333 | fn esc_dispatch(&mut self, intermediates: &[u8], ignore: bool, byte: u8) {
334 | println!(" {:?} ", byte);
335 | panic!();
336 | }
337 | }
338 |
339 | impl SimpleTerminalWindow {
340 | pub fn add_string(&mut self, s: String) {
341 | for c in s.bytes() {
342 | self.vte_parser.advance(&mut self.grid, c);
343 | }
344 | }
345 | }
346 |
347 | pub trait Container {
348 | fn update_content(&mut self);
349 | fn get_content(&self) -> String;
350 | fn get_x(&self) -> u16;
351 | fn get_y(&self) -> u16;
352 | fn get_width(&self) -> u16;
353 | fn get_height(&self) -> u16;
354 | fn get_cursor(&self) -> (usize, usize);
355 | fn get_title(&self) -> Option<&str>;
356 | fn input(&mut self, input: String);
357 | fn set_size(&mut self, width: u16, height: u16);
358 | fn get_printed_chars(&self) -> usize;
359 |
360 | fn on_scroll_y(&mut self, amount: i16);
361 | fn on_mouse_down(&mut self, x: u16, y: u16);
362 | fn on_mouse_up(&mut self, x: u16, y: u16);
363 | fn on_mouse_drag(&mut self, x: u16, y: u16);
364 | fn on_key(&mut self, code: KeyCode, modifiers: KeyModifiers);
365 |
366 | fn is_touching(&self, x: u16, y: u16) -> bool;
367 | }
368 |
369 | impl Container for SimpleTerminalWindow {
370 | fn update_content(&mut self) {
371 | while self.queue.len() > 0 {
372 | let pop = self.queue.pop();
373 | if pop.is_ok() {
374 | self.add_string(pop.unwrap());
375 | }
376 | }
377 | }
378 |
379 | fn get_content(&self) -> String {
380 | let mut result = "".to_string();
381 | let mut prev_color_fg: u8 = self.grid.grid[0].fg;
382 | let mut prev_color_bg: u8 = self.grid.grid[0].bg;
383 | for i in 0..self.height {
384 | let slice = &self.grid.grid[((i * self.width) as usize)..(((i + 1) * self.width) as usize)];
385 | let mut x = 0;
386 | for c in slice {
387 | let foreground = c.fg;
388 | let mut background = c.bg;
389 | if x == self.grid.cursor.0 && i as usize == self.grid.cursor.1 {
390 | background = 47;
391 | }
392 | if foreground != prev_color_fg {
393 | result.push_str(format!("\x1B[{}m", foreground).as_str());
394 | prev_color_fg = foreground;
395 | }
396 | if background != prev_color_bg {
397 | result.push_str(format!("\x1B[{}m", background).as_str());
398 | prev_color_bg = background;
399 | }
400 |
401 | result.push(c.ch);
402 | x += 1;
403 | }
404 | result.push('\n');
405 | }
406 | return result;
407 | }
408 |
409 | fn get_x(&self) -> u16 {
410 | return self.x;
411 | }
412 |
413 | fn get_y(&self) -> u16 {
414 | return self.y;
415 | }
416 |
417 | fn get_width(&self) -> u16 {
418 | return self.width;
419 | }
420 |
421 | fn get_height(&self) -> u16 {
422 | return self.height;
423 | }
424 |
425 | fn get_cursor(&self) -> (usize, usize) {
426 | return self.grid.cursor;
427 | }
428 |
429 | fn get_title(&self) -> Option<&str> {
430 | return Some(&self.title);
431 | }
432 |
433 | fn input(&mut self, input: String) {
434 | self.add_string(input);
435 | }
436 |
437 | fn set_size(&mut self, width: u16, height: u16) {
438 | self.width = width;
439 | self.height = height;
440 | self.grid.width = width as usize;
441 | self.grid.height = height as usize;
442 | self.grid.cursor = (0, 0);
443 |
444 | self.grid.grid = vec![CharacterCell {
445 | attrs: Attr::BackgroundColor(40),
446 | ch: ' ',
447 | bg: 40,
448 | fg: 37,
449 | }; width as usize * height as usize];
450 | let winsize = Winsize {
451 | ws_row: self.height,
452 | ws_col: self.width,
453 | ws_xpixel: 0,
454 | ws_ypixel: 0,
455 | };
456 | let master_fd = self.master_fd.as_raw_fd();
457 |
458 | unsafe { set_window_size(master_fd, &winsize).unwrap() };
459 | nix::sys::signal::kill(self.child_pid, nix::sys::signal::SIGWINCH).unwrap();
460 | }
461 |
462 | fn get_printed_chars(&self) -> usize {
463 | return self.grid.printed_chars;
464 | }
465 |
466 | fn on_scroll_y(&mut self, amount: i16) {
467 | if amount < 0 {
468 | if (-amount) as u16 > self.scroll_y {
469 | self.scroll_y = 0;
470 | } else {
471 | self.scroll_y -= (-amount) as u16;
472 | }
473 | } else {
474 | self.scroll_y += amount as u16;
475 | }
476 | }
477 |
478 | fn on_mouse_down(&mut self, x: u16, y: u16) {
479 | self.last_mouse_down_pos_coords = (x, y);
480 | self.last_size = (self.width, self.height);
481 | self.last_pos = (self.x, self.y);
482 | }
483 |
484 | fn on_mouse_up(&mut self, x: u16, y: u16) {
485 | self.last_size = (self.width, self.height);
486 | self.last_pos = (self.x, self.y);
487 | }
488 |
489 | fn on_mouse_drag(&mut self, x: u16, y: u16) {
490 | if self.last_mouse_down_pos_coords.0 == self.x + self.last_size.0 &&
491 | self.last_mouse_down_pos_coords.1 >= self.last_pos.1 &&
492 | self.last_mouse_down_pos_coords.1 < self.last_pos.1 + self.last_size.1 + 1 {
493 | if x > self.x {
494 | self.set_size(x - self.x, self.height);
495 | }
496 | }
497 | if self.last_mouse_down_pos_coords.1 == self.y + self.last_size.1 &&
498 | self.last_mouse_down_pos_coords.0 >= self.last_pos.0 &&
499 | self.last_mouse_down_pos_coords.0 < self.last_pos.0 + self.last_size.0 + 1 {
500 | if y > self.y {
501 | self.set_size(self.width, y - self.y);
502 | }
503 | }
504 |
505 | if self.last_mouse_down_pos_coords.1 == self.last_pos.1 - 1 &&
506 | self.last_mouse_down_pos_coords.0 >= self.last_pos.0 &&
507 | self.last_mouse_down_pos_coords.0 < self.last_pos.0 + self.last_size.0 + 1 {
508 | if x > (self.last_mouse_down_pos_coords.0 - self.last_pos.0) {
509 | self.x = x - (self.last_mouse_down_pos_coords.0 - self.last_pos.0);
510 | }
511 |
512 | if y > (self.last_mouse_down_pos_coords.1 - (self.last_pos.1 - 1)) {
513 | self.y = y - (self.last_mouse_down_pos_coords.1 - (self.last_pos.1 - 1));
514 | }
515 | }
516 | }
517 |
518 | fn on_key(&mut self, code: KeyCode, modifiers: KeyModifiers) {
519 | match code {
520 | KeyCode::Char(c) => {
521 | self.master_fd.write(&[c as u8]).unwrap();
522 | }
523 | KeyCode::Enter => {
524 | self.master_fd.write(&['\n' as u8]).unwrap();
525 | }
526 | KeyCode::Backspace => {
527 | self.master_fd.write((&[0x08 as u8])).unwrap();
528 | }
529 | KeyCode::Left => {
530 | self.master_fd.write_all(&[0x1B as u8, '[' as u8, 'D' as u8]);
531 | }
532 | KeyCode::Right => {
533 | self.master_fd.write_all(&[0x1B as u8, '[' as u8, 'C' as u8]);
534 | }
535 | KeyCode::Up => {
536 | self.master_fd.write_all(&[0x1B as u8, '[' as u8, 'A' as u8]);
537 | }
538 | KeyCode::Down => {
539 | self.master_fd.write_all(&[0x1B as u8, '[' as u8, 'B' as u8]);
540 | }
541 | _ => {}
542 | }
543 | }
544 |
545 | fn is_touching(&self, x: u16, y: u16) -> bool {
546 | return x >= self.x - 1 && x <= self.x + self.width &&
547 | y >= self.y - 1 && y <= self.y + self.height;
548 | }
549 | }
550 |
551 | impl SimpleTerminalWindow {
552 | pub fn new(x: u16, y: u16, width: u16, height: u16, title: String) -> SimpleTerminalWindow {
553 | let lines: Vec = vec!["".to_string()];
554 |
555 | let queue: Arc> = Arc::new(SegQueue::new());
556 | let q = queue.clone();
557 |
558 | let master_fd = posix_openpt(OFlag::O_RDWR).unwrap();
559 | grantpt(&master_fd).unwrap();
560 | unlockpt(&master_fd).unwrap();
561 | let slave_name = unsafe { ptsname(&master_fd) }.unwrap();
562 | let slave_fd = open(Path::new(&slave_name), OFlag::O_RDWR, Mode::empty()).unwrap();
563 | let mut m: File = unsafe { std::fs::File::from_raw_fd(master_fd.as_raw_fd()) };
564 | let child_pid;
565 |
566 | child_pid = match fork() {
567 | Ok(ForkResult::Parent { child, .. }) => {
568 | child
569 | }
570 | Ok(ForkResult::Child) => {
571 | setsid().unwrap();
572 | unsafe {
573 | if libc::ioctl(slave_fd, TIOCSCTTY) == -1 {
574 | println!("ERROR ioctl() {}", errno::errno());
575 | }
576 | }
577 | dup2(slave_fd, 0); // stdin
578 | dup2(slave_fd, 1); // stdout
579 | dup2(slave_fd, 2); // stderr
580 | unsafe {
581 | let shell = CString::new("/bin/bash").unwrap();
582 | let arg0 = shell.clone();
583 | let term = CString::new("TERM=dumb").unwrap();
584 | let env = [term.as_ptr(), ptr::null()];
585 |
586 | if libc::execle(shell.as_ptr(), arg0.as_ptr(), ptr::null() as *const c_void, env.as_ptr()) == -1 {
587 | println!("ERROR execle() ({})", errno::errno());
588 | }
589 |
590 | exit(-1);
591 | }
592 | }
593 | Err(e) => panic!(e),
594 | };
595 |
596 | std::thread::Builder::new()
597 | .spawn(move || {
598 | let winsize = Winsize {
599 | ws_row: height,
600 | ws_col: width,
601 | ws_xpixel: 0,
602 | ws_ypixel: 0,
603 | };
604 | let master_fd = master_fd.as_raw_fd();
605 | unsafe { set_window_size(master_fd, &winsize).unwrap() };
606 | let mut master_file: File = unsafe { std::fs::File::from_raw_fd(master_fd) };
607 | fn liaison(mut pty_fd: std::fs::File, q: Arc>) {
608 | loop {
609 | let mut buf = [0; 1024];
610 | let r = pty_fd.read(&mut buf);
611 | let n = r.unwrap();
612 | let str = str::from_utf8(&buf[..n]).unwrap().to_string();
613 | q.push(str);
614 | }
615 | }
616 |
617 | liaison(master_file, q);
618 | })
619 | .unwrap();
620 |
621 | return SimpleTerminalWindow {
622 | x,
623 | y,
624 | width,
625 | height,
626 | title,
627 | scroll_y: 0,
628 | grid: EmbedGrid {
629 | cursor: (0, 0),
630 | width: width as usize,
631 | height: height as usize,
632 | bg_color: 0,
633 | fg_color: 15,
634 | grid: vec![CharacterCell {
635 | attrs: Attr::BackgroundColor(0),
636 | ch: ' ',
637 | bg: 40,
638 | fg: 37,
639 | }; width as usize * height as usize],
640 | printed_chars: 0
641 | },
642 | last_mouse_down_pos_coords: (0, 0),
643 | last_size: (width, height),
644 | last_pos: (x, y),
645 | master_fd: m,
646 | child_pid,
647 | queue,
648 | vte_parser: Parser::new()
649 | };
650 | }
651 | }
652 |
653 | pub struct Screen {
654 | pub containers: Vec>>>,
655 | pub dev_console: Vec
656 | }
657 |
658 | impl Screen {
659 | pub fn new() -> Screen {
660 | return Screen {
661 | containers: vec![],
662 | dev_console: vec![]
663 | };
664 | }
665 |
666 | pub fn add_container(&mut self, con: Rc>>) {
667 | self.containers.push(con);
668 | }
669 |
670 | pub fn get_container(&self, index: u16) -> Option>>> {
671 | let con = self.containers.get(index as usize);
672 | if con.is_none() {
673 | return None;
674 | }
675 | return Some(con.unwrap().clone());
676 | }
677 |
678 | pub fn get_top_container(&self) -> Option>>> {
679 | let con = self.containers.last();
680 | if con.is_none() {
681 | return None;
682 | }
683 | return Some(con.unwrap().clone());
684 | }
685 |
686 | pub fn check_top_container(&mut self, x: u16, y: u16) {
687 | let containers_len = self.containers.len();
688 | for i in (0..containers_len).rev() {
689 | if self.containers.get(i).unwrap().deref().borrow().is_touching(x, y) {
690 | let con;
691 | {
692 | con = self.containers.remove(i);
693 | }
694 | self.containers.push(con);
695 | return;
696 | }
697 | }
698 | }
699 | }
--------------------------------------------------------------------------------