channels
This commit is contained in:
parent
fceedc9d44
commit
b74dca4fa1
3 changed files with 909 additions and 121 deletions
541
Cargo.lock
generated
541
Cargo.lock
generated
|
|
@ -12,12 +12,91 @@ version = "0.5.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "cff77d8686867eceff3105329d4698d96c2391c176d5d03adc90c7389162b5b8"
|
checksum = "cff77d8686867eceff3105329d4698d96c2391c176d5d03adc90c7389162b5b8"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "async-channel"
|
||||||
|
version = "1.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "43de69555a39d52918e2bc33a408d3c0a86c829b212d898f4ca25d21a6387478"
|
||||||
|
dependencies = [
|
||||||
|
"concurrent-queue",
|
||||||
|
"event-listener",
|
||||||
|
"futures-core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "async-std"
|
||||||
|
version = "1.6.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "00d68a33ebc8b57800847d00787307f84a562224a14db069b0acefe4c2abbf5d"
|
||||||
|
dependencies = [
|
||||||
|
"async-task",
|
||||||
|
"crossbeam-utils",
|
||||||
|
"futures-channel",
|
||||||
|
"futures-core",
|
||||||
|
"futures-io",
|
||||||
|
"futures-timer",
|
||||||
|
"kv-log-macro",
|
||||||
|
"log",
|
||||||
|
"memchr",
|
||||||
|
"num_cpus",
|
||||||
|
"once_cell",
|
||||||
|
"pin-project-lite",
|
||||||
|
"pin-utils",
|
||||||
|
"slab",
|
||||||
|
"smol",
|
||||||
|
"wasm-bindgen-futures",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "async-task"
|
||||||
|
version = "3.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c17772156ef2829aadc587461c7753af20b7e8db1529bc66855add962a3b35d3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "atomic-waker"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "065374052e7df7ee4047b1160cca5e1467a12351a40b3da123c870ba0b8eda2a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "autocfg"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bitflags"
|
name = "bitflags"
|
||||||
version = "1.2.1"
|
version = "1.2.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
|
checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "blocking"
|
||||||
|
version = "0.4.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d2468ff7bf85066b4a3678fede6fe66db31846d753ff0adfbfab2c6a6e81612b"
|
||||||
|
dependencies = [
|
||||||
|
"async-channel",
|
||||||
|
"atomic-waker",
|
||||||
|
"futures-lite",
|
||||||
|
"once_cell",
|
||||||
|
"parking",
|
||||||
|
"waker-fn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bumpalo"
|
||||||
|
version = "3.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2e8c087f005730276d1096a652e92a8bacee2e2472bcc9715a74d2bec38b5820"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cache-padded"
|
||||||
|
version = "1.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "631ae5198c9be5e753e5cc215e1bd73c2b466a3565173db433f52bb9d3e66dba"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cc"
|
name = "cc"
|
||||||
version = "1.0.57"
|
version = "1.0.57"
|
||||||
|
|
@ -30,16 +109,231 @@ version = "0.1.10"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
|
checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "concurrent-queue"
|
||||||
|
version = "1.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e296417c8154304ac70aceda41f05318f986f7c0c767bcb0a2366fbb890e78e1"
|
||||||
|
dependencies = [
|
||||||
|
"cache-padded",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crossbeam-utils"
|
||||||
|
version = "0.7.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg",
|
||||||
|
"cfg-if",
|
||||||
|
"lazy_static",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "event-listener"
|
||||||
|
version = "2.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "298f00c3b04c1d9b4cb86aefaaa35348af0957d98b30a5306fc635f8e718923d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fastrand"
|
||||||
|
version = "1.3.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "36a9cb09840f81cd211e435d00a4e487edd263dc3c8ff815c32dd76ad668ebed"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures"
|
||||||
|
version = "0.3.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1e05b85ec287aac0dc34db7d4a569323df697f9c55b99b15d6b4ef8cde49f613"
|
||||||
|
dependencies = [
|
||||||
|
"futures-channel",
|
||||||
|
"futures-core",
|
||||||
|
"futures-executor",
|
||||||
|
"futures-io",
|
||||||
|
"futures-sink",
|
||||||
|
"futures-task",
|
||||||
|
"futures-util",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures-channel"
|
||||||
|
version = "0.3.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f366ad74c28cca6ba456d95e6422883cfb4b252a83bed929c83abfdbbf2967d5"
|
||||||
|
dependencies = [
|
||||||
|
"futures-core",
|
||||||
|
"futures-sink",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures-core"
|
||||||
|
version = "0.3.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "59f5fff90fd5d971f936ad674802482ba441b6f09ba5e15fd8b39145582ca399"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures-executor"
|
||||||
|
version = "0.3.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "10d6bb888be1153d3abeb9006b11b02cf5e9b209fda28693c31ae1e4e012e314"
|
||||||
|
dependencies = [
|
||||||
|
"futures-core",
|
||||||
|
"futures-task",
|
||||||
|
"futures-util",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures-io"
|
||||||
|
version = "0.3.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "de27142b013a8e869c14957e6d2edeef89e97c289e69d042ee3a49acd8b51789"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures-lite"
|
||||||
|
version = "0.1.10"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bbe71459749b2e8e66fb95df721b22fa08661ad384a0c5b519e11d3893b4692a"
|
||||||
|
dependencies = [
|
||||||
|
"fastrand",
|
||||||
|
"futures-core",
|
||||||
|
"futures-io",
|
||||||
|
"memchr",
|
||||||
|
"parking",
|
||||||
|
"pin-project-lite",
|
||||||
|
"waker-fn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures-macro"
|
||||||
|
version = "0.3.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d0b5a30a4328ab5473878237c447333c093297bded83a4983d10f4deea240d39"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro-hack",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures-sink"
|
||||||
|
version = "0.3.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3f2032893cb734c7a05d85ce0cc8b8c4075278e93b24b66f9de99d6eb0fa8acc"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures-task"
|
||||||
|
version = "0.3.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bdb66b5f09e22019b1ab0830f7785bcea8e7a42148683f99214f73f8ec21a626"
|
||||||
|
dependencies = [
|
||||||
|
"once_cell",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures-timer"
|
||||||
|
version = "3.0.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e64b03909df88034c26dc1547e8970b91f98bdb65165d6a4e9110d94263dbb2c"
|
||||||
|
dependencies = [
|
||||||
|
"gloo-timers",
|
||||||
|
"send_wrapper",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures-util"
|
||||||
|
version = "0.3.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8764574ff08b701a084482c3c7031349104b07ac897393010494beaa18ce32c6"
|
||||||
|
dependencies = [
|
||||||
|
"futures-channel",
|
||||||
|
"futures-core",
|
||||||
|
"futures-io",
|
||||||
|
"futures-macro",
|
||||||
|
"futures-sink",
|
||||||
|
"futures-task",
|
||||||
|
"memchr",
|
||||||
|
"pin-project",
|
||||||
|
"pin-utils",
|
||||||
|
"proc-macro-hack",
|
||||||
|
"proc-macro-nested",
|
||||||
|
"slab",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "gloo-timers"
|
||||||
|
version = "0.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "47204a46aaff920a1ea58b11d03dec6f704287d27561724a4631e450654a891f"
|
||||||
|
dependencies = [
|
||||||
|
"futures-channel",
|
||||||
|
"futures-core",
|
||||||
|
"js-sys",
|
||||||
|
"wasm-bindgen",
|
||||||
|
"web-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hermit-abi"
|
||||||
|
version = "0.1.15"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3deed196b6e7f9e44a2ae8d94225d80302d81208b1bb673fd21fe634645c85a9"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "js-sys"
|
||||||
|
version = "0.3.44"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "85a7e2c92a4804dd459b86c339278d0fe87cf93757fae222c3fa3ae75458bc73"
|
||||||
|
dependencies = [
|
||||||
|
"wasm-bindgen",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "kv-log-macro"
|
||||||
|
version = "1.0.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0de8b303297635ad57c9f5059fd9cee7a47f8e8daa09df0fcd07dd39fb22977f"
|
||||||
|
dependencies = [
|
||||||
|
"log",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lazy_static"
|
||||||
|
version = "1.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.71"
|
version = "0.2.71"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9457b06509d27052635f90d6466700c65095fdf75409b3fbdd903e988b886f49"
|
checksum = "9457b06509d27052635f90d6466700c65095fdf75409b3fbdd903e988b886f49"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "log"
|
||||||
|
version = "0.4.11"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4fabed175da42fed1fa0746b0ea71f412aa9d35e76e95e59b192c64b9dc2bf8b"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "memchr"
|
||||||
|
version = "2.3.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "mosaic"
|
name = "mosaic"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"async-std",
|
||||||
|
"futures",
|
||||||
"libc",
|
"libc",
|
||||||
"nix",
|
"nix",
|
||||||
"signal-hook",
|
"signal-hook",
|
||||||
|
|
@ -62,6 +356,72 @@ dependencies = [
|
||||||
"void",
|
"void",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num_cpus"
|
||||||
|
version = "1.13.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3"
|
||||||
|
dependencies = [
|
||||||
|
"hermit-abi",
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "once_cell"
|
||||||
|
version = "1.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0b631f7e854af39a1739f401cf34a8a013dfe09eac4fa4dba91e9768bd28168d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "parking"
|
||||||
|
version = "1.0.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6cb300f271742d4a2a66c01b6b2fa0c83dfebd2e0bf11addb879a3547b4ed87c"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pin-project"
|
||||||
|
version = "0.4.23"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ca4433fff2ae79342e497d9f8ee990d174071408f28f726d6d83af93e58e48aa"
|
||||||
|
dependencies = [
|
||||||
|
"pin-project-internal",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pin-project-internal"
|
||||||
|
version = "0.4.23"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2c0e815c3ee9a031fdf5af21c10aa17c573c9c6a566328d99e3936c34e36461f"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pin-project-lite"
|
||||||
|
version = "0.1.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "282adbf10f2698a7a77f8e983a74b2d18176c19a7fd32a45446139ae7b02b715"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pin-utils"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro-hack"
|
||||||
|
version = "0.5.18"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "99c605b9a0adc77b7211c6b1f722dcb613d68d66859a44f3d485a6da332b0598"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro-nested"
|
||||||
|
version = "0.1.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "eba180dafb9038b050a4c280019bbedf9f2467b61e5d892dcad585bb57aadc5a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro2"
|
name = "proc-macro2"
|
||||||
version = "1.0.18"
|
version = "1.0.18"
|
||||||
|
|
@ -80,6 +440,24 @@ dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "redox_syscall"
|
||||||
|
version = "0.1.57"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "scoped-tls"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ea6a9290e3c9cf0f18145ef7ffa62d68ee0bf5fcd651017e586dc7fd5da448c2"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "send_wrapper"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f638d531eccd6e23b980caf34876660d38e265409d8e99b397ab71eb3612fad0"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "signal-hook"
|
name = "signal-hook"
|
||||||
version = "0.1.16"
|
version = "0.1.16"
|
||||||
|
|
@ -100,6 +478,56 @@ dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "slab"
|
||||||
|
version = "0.4.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "smol"
|
||||||
|
version = "0.1.18"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "620cbb3c6e34da57d3a248cda0cd01cd5848164dc062e764e65d06fe3ea7aed5"
|
||||||
|
dependencies = [
|
||||||
|
"async-task",
|
||||||
|
"blocking",
|
||||||
|
"concurrent-queue",
|
||||||
|
"fastrand",
|
||||||
|
"futures-io",
|
||||||
|
"futures-util",
|
||||||
|
"libc",
|
||||||
|
"once_cell",
|
||||||
|
"scoped-tls",
|
||||||
|
"slab",
|
||||||
|
"socket2",
|
||||||
|
"wepoll-sys-stjepang",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "socket2"
|
||||||
|
version = "0.3.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "03088793f677dce356f3ccc2edb1b314ad191ab702a5de3faf49304f7e104918"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"libc",
|
||||||
|
"redox_syscall",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "syn"
|
||||||
|
version = "1.0.36"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4cdb98bcb1f9d81d07b536179c269ea15999b5d14ea958196413869445bb5250"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"unicode-xid",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "termios"
|
name = "termios"
|
||||||
version = "0.3.2"
|
version = "0.3.2"
|
||||||
|
|
@ -162,3 +590,116 @@ dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "waker-fn"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9571542c2ce85ce642e6b58b3364da2fb53526360dfb7c211add4f5c23105ff7"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen"
|
||||||
|
version = "0.2.67"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f0563a9a4b071746dd5aedbc3a28c6fe9be4586fb3fbadb67c400d4f53c6b16c"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"wasm-bindgen-macro",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen-backend"
|
||||||
|
version = "0.2.67"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bc71e4c5efa60fb9e74160e89b93353bc24059999c0ae0fb03affc39770310b0"
|
||||||
|
dependencies = [
|
||||||
|
"bumpalo",
|
||||||
|
"lazy_static",
|
||||||
|
"log",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
"wasm-bindgen-shared",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen-futures"
|
||||||
|
version = "0.4.17"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "95f8d235a77f880bcef268d379810ea6c0af2eacfa90b1ad5af731776e0c4699"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"js-sys",
|
||||||
|
"wasm-bindgen",
|
||||||
|
"web-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen-macro"
|
||||||
|
version = "0.2.67"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "97c57cefa5fa80e2ba15641578b44d36e7a64279bc5ed43c6dbaf329457a2ed2"
|
||||||
|
dependencies = [
|
||||||
|
"quote",
|
||||||
|
"wasm-bindgen-macro-support",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen-macro-support"
|
||||||
|
version = "0.2.67"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "841a6d1c35c6f596ccea1f82504a192a60378f64b3bb0261904ad8f2f5657556"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
"wasm-bindgen-backend",
|
||||||
|
"wasm-bindgen-shared",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen-shared"
|
||||||
|
version = "0.2.67"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "93b162580e34310e5931c4b792560108b10fd14d64915d7fff8ff00180e70092"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "web-sys"
|
||||||
|
version = "0.3.44"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "dda38f4e5ca63eda02c059d243aa25b5f35ab98451e518c51612cd0f1bd19a47"
|
||||||
|
dependencies = [
|
||||||
|
"js-sys",
|
||||||
|
"wasm-bindgen",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wepoll-sys-stjepang"
|
||||||
|
version = "1.0.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6fd319e971980166b53e17b1026812ad66c6b54063be879eb182342b55284694"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi"
|
||||||
|
version = "0.3.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
|
||||||
|
dependencies = [
|
||||||
|
"winapi-i686-pc-windows-gnu",
|
||||||
|
"winapi-x86_64-pc-windows-gnu",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi-i686-pc-windows-gnu"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi-x86_64-pc-windows-gnu"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||||
|
|
|
||||||
|
|
@ -14,3 +14,8 @@ signal-hook = "0.1.10"
|
||||||
unicode-width = "0.1.8"
|
unicode-width = "0.1.8"
|
||||||
unicode-truncate = "0.1.1"
|
unicode-truncate = "0.1.1"
|
||||||
vte = "0.8.0"
|
vte = "0.8.0"
|
||||||
|
futures = "0.3.5"
|
||||||
|
|
||||||
|
[dependencies.async-std]
|
||||||
|
version = "1.3.0"
|
||||||
|
features = ["unstable"]
|
||||||
|
|
|
||||||
498
src/main.rs
498
src/main.rs
|
|
@ -1,5 +1,7 @@
|
||||||
// use std::time::{Duration, Instant};
|
use std::time::{Duration, Instant};
|
||||||
use std::io;
|
use std::io;
|
||||||
|
use futures::future::join_all;
|
||||||
|
use std::cell::Cell;
|
||||||
use ::std::fmt::{self, Display, Formatter};
|
use ::std::fmt::{self, Display, Formatter};
|
||||||
use std::cmp::max;
|
use std::cmp::max;
|
||||||
use std::io::{Read, Write};
|
use std::io::{Read, Write};
|
||||||
|
|
@ -21,8 +23,59 @@ use std::os::unix::io::RawFd;
|
||||||
use std::process::Command;
|
use std::process::Command;
|
||||||
use ::std::thread;
|
use ::std::thread;
|
||||||
use ::std::sync::{Arc, Mutex};
|
use ::std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
use vte;
|
use vte;
|
||||||
|
use async_std::stream::*;
|
||||||
|
use async_std::task;
|
||||||
|
use async_std::task::*;
|
||||||
|
use async_std::prelude::*;
|
||||||
|
use ::std::pin::*;
|
||||||
|
use std::sync::mpsc::{channel, Sender, Receiver};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
struct ReadFromPid {
|
||||||
|
pid: RawFd,
|
||||||
|
read_buffer: [u8; 115200],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ReadFromPid {
|
||||||
|
fn new(pid: &RawFd) -> ReadFromPid {
|
||||||
|
ReadFromPid {
|
||||||
|
pid: *pid,
|
||||||
|
read_buffer: [0; 115200], // TODO: ???
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Stream for ReadFromPid {
|
||||||
|
type Item = Vec<u8>;
|
||||||
|
fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
|
||||||
|
let read_result = read(self.pid, &mut self.read_buffer);
|
||||||
|
match read_result {
|
||||||
|
Ok(res) => {
|
||||||
|
// TODO: this might become an issue with multiple panes sending data simultaneously
|
||||||
|
// ...consider returning None if res == 0 and handling it in the task (or sending
|
||||||
|
// Poll::Pending?)
|
||||||
|
let res = Some(self.read_buffer[..=res].to_vec());
|
||||||
|
self.read_buffer = [0; 115200];
|
||||||
|
return Poll::Ready(res)
|
||||||
|
},
|
||||||
|
Err(e) => {
|
||||||
|
match e {
|
||||||
|
nix::Error::Sys(errno) => {
|
||||||
|
if errno == nix::errno::Errno::EAGAIN {
|
||||||
|
return Poll::Ready(Some(vec![])) // TODO: better with timeout waker somehow
|
||||||
|
// task::block_on(task::sleep(Duration::from_millis(10)));
|
||||||
|
} else {
|
||||||
|
panic!("error {:?}", e);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_ => panic!("error {:?}", e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn read_from_pid (pid: RawFd) -> Option<Vec<u8>> {
|
fn read_from_pid (pid: RawFd) -> Option<Vec<u8>> {
|
||||||
let mut read_buffer = [0; 115200];
|
let mut read_buffer = [0; 115200];
|
||||||
|
|
@ -104,7 +157,8 @@ fn spawn_terminal (ws: &Winsize) -> (RawFd, RawFd) {
|
||||||
let pid_primary = fork_pty_res.master;
|
let pid_primary = fork_pty_res.master;
|
||||||
let pid_secondary = match fork_pty_res.fork_result {
|
let pid_secondary = match fork_pty_res.fork_result {
|
||||||
ForkResult::Parent { child } => {
|
ForkResult::Parent { child } => {
|
||||||
fcntl(pid_primary, FcntlArg::F_SETFL(OFlag::O_NONBLOCK)).expect("could not fcntl");
|
fcntl(pid_primary, FcntlArg::F_SETFL(OFlag::empty())).expect("could not fcntl");
|
||||||
|
// fcntl(pid_primary, FcntlArg::F_SETFL(OFlag::O_NONBLOCK)).expect("could not fcntl");
|
||||||
child
|
child
|
||||||
},
|
},
|
||||||
ForkResult::Child => {
|
ForkResult::Child => {
|
||||||
|
|
@ -175,6 +229,7 @@ impl Display for TerminalCharacter {
|
||||||
}
|
}
|
||||||
|
|
||||||
struct TerminalOutput {
|
struct TerminalOutput {
|
||||||
|
pub pid: RawFd,
|
||||||
pub characters: Vec<TerminalCharacter>,
|
pub characters: Vec<TerminalCharacter>,
|
||||||
pub display_rows: u16,
|
pub display_rows: u16,
|
||||||
pub display_cols: u16,
|
pub display_cols: u16,
|
||||||
|
|
@ -188,18 +243,47 @@ struct TerminalOutput {
|
||||||
const EMPTY_TERMINAL_CHARACTER: TerminalCharacter = TerminalCharacter { character: ' ', ansi_code: None };
|
const EMPTY_TERMINAL_CHARACTER: TerminalCharacter = TerminalCharacter { character: ' ', ansi_code: None };
|
||||||
|
|
||||||
impl TerminalOutput {
|
impl TerminalOutput {
|
||||||
pub fn new () -> TerminalOutput {
|
pub fn new (pid: RawFd, ws: Winsize) -> TerminalOutput {
|
||||||
TerminalOutput {
|
TerminalOutput {
|
||||||
|
pid,
|
||||||
characters: vec![],
|
characters: vec![],
|
||||||
cursor_position: 0,
|
cursor_position: 0,
|
||||||
newline_indices: Vec::new(),
|
newline_indices: Vec::new(),
|
||||||
linebreak_indices: Vec::new(),
|
linebreak_indices: Vec::new(),
|
||||||
display_rows: 0,
|
display_rows: ws.ws_row,
|
||||||
display_cols: 0,
|
display_cols: ws.ws_col,
|
||||||
should_render: false,
|
should_render: false,
|
||||||
pending_ansi_code: None,
|
pending_ansi_code: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
pub fn handle_event(&mut self, event: VteEvent) {
|
||||||
|
match event {
|
||||||
|
VteEvent::Print(c) => {
|
||||||
|
self.print(c);
|
||||||
|
},
|
||||||
|
VteEvent::Execute(byte) => {
|
||||||
|
self.execute(byte);
|
||||||
|
},
|
||||||
|
VteEvent::Hook(params, intermediates, ignore, c) => {
|
||||||
|
self.hook(¶ms, &intermediates, ignore, c);
|
||||||
|
},
|
||||||
|
VteEvent::Put(byte) => {
|
||||||
|
self.put(byte);
|
||||||
|
},
|
||||||
|
VteEvent::Unhook => {
|
||||||
|
self.unhook();
|
||||||
|
},
|
||||||
|
VteEvent::OscDispatch(params, bell_terminated) => {
|
||||||
|
self.osc_dispatch(params, bell_terminated);
|
||||||
|
},
|
||||||
|
VteEvent::CsiDispatch(params, intermediates, ignore, c) => {
|
||||||
|
self.csi_dispatch(¶ms, &intermediates, ignore, c);
|
||||||
|
},
|
||||||
|
VteEvent::EscDispatch(intermediates, ignore, byte) => {
|
||||||
|
self.esc_dispatch(&intermediates, ignore, byte);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
pub fn reduce_width(&mut self, count: u16) {
|
pub fn reduce_width(&mut self, count: u16) {
|
||||||
self.display_cols -= count;
|
self.display_cols -= count;
|
||||||
self.reflow_lines();
|
self.reflow_lines();
|
||||||
|
|
@ -236,10 +320,13 @@ impl TerminalOutput {
|
||||||
x += 1;
|
x += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn read_buffer_as_lines (&mut self) -> Vec<Vec<&TerminalCharacter>> {
|
pub fn read_buffer_as_lines (&self) -> Vec<Vec<&TerminalCharacter>> {
|
||||||
if DEBUGGING {
|
if DEBUGGING {
|
||||||
return vec![];
|
return vec![];
|
||||||
}
|
}
|
||||||
|
if self.characters.len() == 0 {
|
||||||
|
return vec![];
|
||||||
|
}
|
||||||
let mut output: VecDeque<Vec<&TerminalCharacter>> = VecDeque::new();
|
let mut output: VecDeque<Vec<&TerminalCharacter>> = VecDeque::new();
|
||||||
let mut i = self.characters.len();
|
let mut i = self.characters.len();
|
||||||
let mut current_line: VecDeque<&TerminalCharacter> = VecDeque::new();
|
let mut current_line: VecDeque<&TerminalCharacter> = VecDeque::new();
|
||||||
|
|
@ -289,7 +376,7 @@ impl TerminalOutput {
|
||||||
output.push_front(Vec::from(empty_line.clone()));
|
output.push_front(Vec::from(empty_line.clone()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.should_render = false;
|
// self.should_render = false;
|
||||||
Vec::from(output)
|
Vec::from(output)
|
||||||
}
|
}
|
||||||
pub fn cursor_position_in_last_line (&self) -> usize {
|
pub fn cursor_position_in_last_line (&self) -> usize {
|
||||||
|
|
@ -390,7 +477,8 @@ impl TerminalOutput {
|
||||||
|
|
||||||
const DEBUGGING: bool = false;
|
const DEBUGGING: bool = false;
|
||||||
|
|
||||||
impl vte::Perform for TerminalOutput {
|
// vte methods
|
||||||
|
impl TerminalOutput {
|
||||||
fn print(&mut self, c: char) {
|
fn print(&mut self, c: char) {
|
||||||
if DEBUGGING {
|
if DEBUGGING {
|
||||||
println!("\r[print] {:?}", c);
|
println!("\r[print] {:?}", c);
|
||||||
|
|
@ -463,7 +551,9 @@ impl vte::Perform for TerminalOutput {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn osc_dispatch(&mut self, params: &[&[u8]], bell_terminated: bool) {
|
// fn osc_dispatch(&mut self, params: &[&[u8]], bell_terminated: bool) {
|
||||||
|
// TODO: normalize vec/slices for all of these methods and the enum
|
||||||
|
fn osc_dispatch(&mut self, params: Vec<Vec<u8>>, bell_terminated: bool) {
|
||||||
if DEBUGGING {
|
if DEBUGGING {
|
||||||
println!("\r[osc_dispatch] params={:?} bell_terminated={}", params, bell_terminated);
|
println!("\r[osc_dispatch] params={:?} bell_terminated={}", params, bell_terminated);
|
||||||
}
|
}
|
||||||
|
|
@ -525,6 +615,73 @@ impl vte::Perform for TerminalOutput {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum VteEvent { // TODO: try not to allocate Vecs
|
||||||
|
Print(char),
|
||||||
|
Execute(u8), // byte
|
||||||
|
Hook(Vec<i64>, Vec<u8>, bool, char), // params, intermediates, ignore, char
|
||||||
|
Put(u8), // byte
|
||||||
|
Unhook,
|
||||||
|
OscDispatch(Vec<Vec<u8>>, bool), // params, bell_terminated
|
||||||
|
CsiDispatch(Vec<i64>, Vec<u8>, bool, char), // params, intermediates, ignore, char
|
||||||
|
EscDispatch(Vec<u8>, bool, u8), // intermediates, ignore, byte
|
||||||
|
}
|
||||||
|
|
||||||
|
struct VteEventSender {
|
||||||
|
id: RawFd,
|
||||||
|
sender: Sender<ScreenInstruction>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl VteEventSender {
|
||||||
|
pub fn new (id: RawFd, sender: Sender<ScreenInstruction>) -> Self {
|
||||||
|
VteEventSender { id, sender }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl vte::Perform for VteEventSender {
|
||||||
|
fn print(&mut self, c: char) {
|
||||||
|
self.sender.send(
|
||||||
|
ScreenInstruction::Pty(self.id, VteEvent::Print(c))
|
||||||
|
).unwrap();
|
||||||
|
}
|
||||||
|
fn execute(&mut self, byte: u8) {
|
||||||
|
self.sender.send(ScreenInstruction::Pty(self.id, VteEvent::Execute(byte))).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn hook(&mut self, params: &[i64], intermediates: &[u8], ignore: bool, c: char) {
|
||||||
|
let params = params.iter().copied().collect();
|
||||||
|
let intermediates = intermediates.iter().copied().collect();
|
||||||
|
let instruction = ScreenInstruction::Pty(self.id, VteEvent::Hook(params, intermediates, ignore, c));
|
||||||
|
self.sender.send(instruction).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn put(&mut self, byte: u8) {
|
||||||
|
self.sender.send(ScreenInstruction::Pty(self.id, VteEvent::Put(byte))).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn unhook(&mut self) {
|
||||||
|
self.sender.send(ScreenInstruction::Pty(self.id, VteEvent::Unhook)).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn osc_dispatch(&mut self, params: &[&[u8]], bell_terminated: bool) {
|
||||||
|
let params = params.iter().map(|p| p.to_vec()).collect();
|
||||||
|
let instruction = ScreenInstruction::Pty(self.id, VteEvent::OscDispatch(params, bell_terminated));
|
||||||
|
self.sender.send(instruction).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn csi_dispatch(&mut self, params: &[i64], intermediates: &[u8], ignore: bool, c: char) {
|
||||||
|
let params = params.iter().copied().collect();
|
||||||
|
let intermediates = intermediates.iter().copied().collect();
|
||||||
|
let instruction = ScreenInstruction::Pty(self.id, VteEvent::CsiDispatch(params, intermediates, ignore, c));
|
||||||
|
self.sender.send(instruction).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn esc_dispatch(&mut self, intermediates: &[u8], ignore: bool, byte: u8) {
|
||||||
|
let intermediates = intermediates.iter().copied().collect();
|
||||||
|
let instruction = ScreenInstruction::Pty(self.id, VteEvent::EscDispatch(intermediates, ignore, byte));
|
||||||
|
self.sender.send(instruction).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// sigwinch stuff
|
// sigwinch stuff
|
||||||
use ::signal_hook::iterator::Signals;
|
use ::signal_hook::iterator::Signals;
|
||||||
|
|
||||||
|
|
@ -574,35 +731,92 @@ fn character_is_already_onscreen(
|
||||||
last_character_style == current_character_style && last_character.character == current_character.character
|
last_character_style == current_character_style && last_character.character == current_character.character
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum ScreenInstruction {
|
||||||
|
Pty(RawFd, VteEvent),
|
||||||
|
Render,
|
||||||
|
AddTerminal(RawFd, Winsize),
|
||||||
|
WriteCharacter(u8),
|
||||||
|
ResizeLeft,
|
||||||
|
ResizeRight,
|
||||||
|
MoveFocus,
|
||||||
|
}
|
||||||
|
|
||||||
struct Screen {
|
struct Screen {
|
||||||
|
pub receiver: Receiver<ScreenInstruction>,
|
||||||
|
pub sender: Sender<ScreenInstruction>,
|
||||||
|
full_screen_ws: Winsize,
|
||||||
last_frame: Option<Vec<TerminalCharacter>>,
|
last_frame: Option<Vec<TerminalCharacter>>,
|
||||||
vertical_separator: TerminalCharacter, // TODO: better
|
vertical_separator: TerminalCharacter, // TODO: better
|
||||||
|
active_terminal: Option<RawFd>,
|
||||||
|
terminal1_output: Option<TerminalOutput>,
|
||||||
|
terminal2_output: Option<TerminalOutput>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Screen {
|
impl Screen {
|
||||||
pub fn new () -> Self {
|
pub fn new () -> Self {
|
||||||
|
let (sender, receiver): (Sender<ScreenInstruction>, Receiver<ScreenInstruction>) = channel();
|
||||||
|
let full_screen_ws = get_terminal_size_using_fd(0);
|
||||||
Screen {
|
Screen {
|
||||||
|
receiver,
|
||||||
|
sender,
|
||||||
|
full_screen_ws,
|
||||||
last_frame: None,
|
last_frame: None,
|
||||||
vertical_separator: TerminalCharacter::new('|').ansi_code(String::from("\u{1b}[m")), // TODO: better
|
vertical_separator: TerminalCharacter::new('|').ansi_code(String::from("\u{1b}[m")), // TODO: better
|
||||||
|
terminal1_output: None,
|
||||||
|
terminal2_output: None,
|
||||||
|
active_terminal: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn render (&mut self, terminal1_output: &mut TerminalOutput, terminal2_output: &mut TerminalOutput, full_screen_ws: &Winsize, terminal1_is_active: bool) {
|
pub fn add_terminal(&mut self, pid: RawFd, ws: Winsize) {
|
||||||
if DEBUGGING {
|
if self.terminal1_output.is_none() {
|
||||||
|
self.terminal1_output = Some(TerminalOutput::new(pid, ws));
|
||||||
|
self.active_terminal = Some(pid);
|
||||||
|
} else if self.terminal2_output.is_none() {
|
||||||
|
self.terminal2_output = Some(TerminalOutput::new(pid, ws));
|
||||||
|
} else {
|
||||||
|
panic!("cannot support more than 2 terminals atm");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn handle_pty_event(&mut self, pid: RawFd, event: VteEvent) {
|
||||||
|
if let Some(terminal_output) = self.terminal1_output.as_mut() {
|
||||||
|
if terminal_output.pid == pid {
|
||||||
|
terminal_output.handle_event(event);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Some(terminal_output) = self.terminal2_output.as_mut() {
|
||||||
|
if terminal_output.pid == pid {
|
||||||
|
terminal_output.handle_event(event);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn write_to_active_terminal(&self, byte: u8) {
|
||||||
|
if let Some(active_terminal) = &self.active_terminal {
|
||||||
|
let mut buffer = [byte];
|
||||||
|
write(*active_terminal, &mut buffer).expect("failed to write to terminal");
|
||||||
|
tcdrain(*active_terminal).expect("failed to drain terminal");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn render (&mut self) {
|
||||||
|
let left_terminal_lines = self.terminal1_output.as_ref().unwrap().read_buffer_as_lines();
|
||||||
|
let right_terminal_lines = self.terminal2_output.as_ref().unwrap().read_buffer_as_lines();
|
||||||
|
if left_terminal_lines.len() < self.full_screen_ws.ws_row as usize || right_terminal_lines.len() < self.full_screen_ws.ws_row as usize {
|
||||||
|
// TODO: this is hacky and is only here(?) for when the terminals are not ready yet
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let left_terminal_lines = terminal1_output.read_buffer_as_lines();
|
|
||||||
let right_terminal_lines = terminal2_output.read_buffer_as_lines();
|
|
||||||
|
|
||||||
let mut frame: Vec<&TerminalCharacter> = vec![];
|
let mut frame: Vec<&TerminalCharacter> = vec![];
|
||||||
for i in 0..full_screen_ws.ws_row {
|
let empty_vec = vec![]; // TODO: do not allocate, and less hacky
|
||||||
let left_terminal_row = left_terminal_lines.get(i as usize).unwrap();
|
for i in 0..self.full_screen_ws.ws_row {
|
||||||
|
let left_terminal_row = left_terminal_lines.get(i as usize).unwrap_or(&empty_vec);
|
||||||
for terminal_character in left_terminal_row.iter() {
|
for terminal_character in left_terminal_row.iter() {
|
||||||
frame.push(terminal_character);
|
frame.push(terminal_character);
|
||||||
}
|
}
|
||||||
|
|
||||||
frame.push(&self.vertical_separator);
|
frame.push(&self.vertical_separator);
|
||||||
|
|
||||||
let right_terminal_row = right_terminal_lines.get(i as usize).unwrap();
|
let right_terminal_row = right_terminal_lines.get(i as usize).unwrap_or(&empty_vec);
|
||||||
for terminal_character in right_terminal_row.iter() {
|
for terminal_character in right_terminal_row.iter() {
|
||||||
frame.push(terminal_character);
|
frame.push(terminal_character);
|
||||||
}
|
}
|
||||||
|
|
@ -621,8 +835,8 @@ impl Screen {
|
||||||
for i in 0..last_frame.len() {
|
for i in 0..last_frame.len() {
|
||||||
let last_character = last_frame.get(i).unwrap();
|
let last_character = last_frame.get(i).unwrap();
|
||||||
let current_character = frame.get(i).unwrap();
|
let current_character = frame.get(i).unwrap();
|
||||||
let row = i / full_screen_ws.ws_col as usize + 1;
|
let row = i / self.full_screen_ws.ws_col as usize + 1;
|
||||||
let col = i % full_screen_ws.ws_col as usize + 1;
|
let col = i % self.full_screen_ws.ws_col as usize + 1;
|
||||||
if !character_is_already_onscreen(&last_character, ¤t_character) {
|
if !character_is_already_onscreen(&last_character, ¤t_character) {
|
||||||
if !last_character_was_changed {
|
if !last_character_was_changed {
|
||||||
data_lines.push_str(&format!("\u{1b}[{};{}H\u{1b}[m", row, &col)); // goto row/col
|
data_lines.push_str(&format!("\u{1b}[{};{}H\u{1b}[m", row, &col)); // goto row/col
|
||||||
|
|
@ -652,152 +866,180 @@ impl Screen {
|
||||||
// TODO: consider looping through current frame and only updating the cells that changed
|
// TODO: consider looping through current frame and only updating the cells that changed
|
||||||
self.last_frame = Some(frame.into_iter().cloned().collect::<Vec<_>>());
|
self.last_frame = Some(frame.into_iter().cloned().collect::<Vec<_>>());
|
||||||
|
|
||||||
let left_terminal_cursor_position = terminal1_output.cursor_position_in_last_line();
|
let left_terminal_cursor_position = self.terminal1_output.as_ref().unwrap().cursor_position_in_last_line();
|
||||||
let right_terminal_cursor_position = terminal2_output.cursor_position_in_last_line();
|
let right_terminal_cursor_position = self.terminal2_output.as_ref().unwrap().cursor_position_in_last_line();
|
||||||
if terminal1_is_active {
|
|
||||||
|
let active_terminal = self.active_terminal.unwrap();
|
||||||
|
if active_terminal == self.terminal1_output.as_ref().unwrap().pid {
|
||||||
data_lines.push_str(&format!("\r\u{1b}[{}C", left_terminal_cursor_position));
|
data_lines.push_str(&format!("\r\u{1b}[{}C", left_terminal_cursor_position));
|
||||||
} else {
|
} else {
|
||||||
data_lines.push_str(&format!("\r\u{1b}[{}C", right_terminal_cursor_position + (terminal1_output.display_cols + 1) as usize));
|
data_lines.push_str(&format!("\r\u{1b}[{}C", right_terminal_cursor_position + (self.terminal1_output.as_ref().unwrap().display_cols + 1) as usize));
|
||||||
}
|
}
|
||||||
::std::io::stdout().write_all(&data_lines.as_bytes()).expect("cannot write to stdout");
|
::std::io::stdout().write_all(&data_lines.as_bytes()).expect("cannot write to stdout");
|
||||||
::std::io::stdout().flush().expect("could not flush");
|
::std::io::stdout().flush().expect("could not flush");
|
||||||
}
|
}
|
||||||
|
pub fn resize_left (&mut self) {
|
||||||
|
let terminal1_output = self.terminal1_output.as_mut().unwrap();
|
||||||
|
let terminal2_output = self.terminal2_output.as_mut().unwrap();
|
||||||
|
terminal1_output.reduce_width(10);
|
||||||
|
terminal2_output.increase_width(10);
|
||||||
|
set_terminal_size_using_fd(terminal1_output.pid, terminal1_output.display_cols, terminal1_output.display_rows);
|
||||||
|
set_terminal_size_using_fd(terminal2_output.pid, terminal2_output.display_cols, terminal2_output.display_rows);
|
||||||
|
}
|
||||||
|
pub fn resize_right (&mut self) {
|
||||||
|
let terminal1_output = self.terminal1_output.as_mut().unwrap();
|
||||||
|
let terminal2_output = self.terminal2_output.as_mut().unwrap();
|
||||||
|
terminal1_output.increase_width(10);
|
||||||
|
terminal2_output.reduce_width(10);
|
||||||
|
set_terminal_size_using_fd(terminal1_output.pid, terminal1_output.display_cols, terminal1_output.display_rows);
|
||||||
|
set_terminal_size_using_fd(terminal2_output.pid, terminal2_output.display_cols, terminal2_output.display_rows);
|
||||||
|
}
|
||||||
|
pub fn move_focus(&mut self) {
|
||||||
|
let terminal1_output = self.terminal1_output.as_ref().unwrap();
|
||||||
|
let terminal2_output = self.terminal2_output.as_ref().unwrap();
|
||||||
|
let active_terminal = self.active_terminal.unwrap();
|
||||||
|
if active_terminal == terminal1_output.pid {
|
||||||
|
self.active_terminal = Some(terminal2_output.pid);
|
||||||
|
} else {
|
||||||
|
self.active_terminal = Some(terminal1_output.pid);
|
||||||
|
}
|
||||||
|
self.render();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct PtyBus {
|
||||||
|
sender: Sender<ScreenInstruction>,
|
||||||
|
active_ptys: Vec<JoinHandle<()>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PtyBus {
|
||||||
|
pub fn new (sender: Sender<ScreenInstruction>) -> Self {
|
||||||
|
PtyBus {
|
||||||
|
sender,
|
||||||
|
active_ptys: Vec::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn spawn_terminal(&mut self, ws: &Winsize) {
|
||||||
|
let ws = *ws;
|
||||||
|
let (pid_primary, _pid_secondary): (RawFd, RawFd) = spawn_terminal(&ws);
|
||||||
|
let task_handle = task::spawn({
|
||||||
|
// let pid_primary = pid_primary.clone();
|
||||||
|
let sender = self.sender.clone();
|
||||||
|
async move {
|
||||||
|
let mut vte_parser = vte::Parser::new();
|
||||||
|
let mut vte_event_sender = VteEventSender::new(pid_primary, sender.clone());
|
||||||
|
let mut first_terminal_bytes = ReadFromPid::new(&pid_primary);
|
||||||
|
while let Some(bytes) = first_terminal_bytes.next().await {
|
||||||
|
let bytes_is_empty = bytes.is_empty();
|
||||||
|
for byte in bytes {
|
||||||
|
vte_parser.advance(&mut vte_event_sender, byte);
|
||||||
|
}
|
||||||
|
if !bytes_is_empty {
|
||||||
|
sender.send(ScreenInstruction::Render).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
self.sender.send(ScreenInstruction::AddTerminal(pid_primary, ws)).unwrap();
|
||||||
|
self.active_ptys.push(task_handle);
|
||||||
|
}
|
||||||
|
pub async fn wait_for_tasks(&mut self) {
|
||||||
|
// let task1 = self.active_ptys.get_mut(0).unwrap();
|
||||||
|
// task1.await;
|
||||||
|
let mut v = vec![];
|
||||||
|
for handle in self.active_ptys.iter_mut() {
|
||||||
|
// TODO: better, see commented lines above... can't we do this on the original vec?
|
||||||
|
v.push(handle);
|
||||||
|
}
|
||||||
|
join_all(v).await;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let mut active_threads = vec![];
|
let mut active_threads = vec![];
|
||||||
|
|
||||||
let full_screen_ws = get_terminal_size_using_fd(0);
|
|
||||||
let (first_terminal_ws, second_terminal_ws) = split_horizontally_with_gap(&full_screen_ws);
|
|
||||||
let (first_terminal_pid, _pid_secondary): (RawFd, RawFd) = spawn_terminal(&first_terminal_ws);
|
|
||||||
let (second_terminal_pid, _pid_secondary): (RawFd, RawFd) = spawn_terminal(&second_terminal_ws);
|
|
||||||
let stdin = io::stdin();
|
let stdin = io::stdin();
|
||||||
into_raw_mode(0);
|
into_raw_mode(0);
|
||||||
set_baud_rate(0);
|
set_baud_rate(0);
|
||||||
::std::thread::sleep(std::time::Duration::from_millis(2000));
|
::std::thread::sleep(std::time::Duration::from_millis(2000));
|
||||||
let active_terminal = Arc::new(Mutex::new(first_terminal_pid));
|
let mut screen = Screen::new();
|
||||||
|
let send_screen_instructions = screen.sender.clone();
|
||||||
let first_terminal_ws = Arc::new(Mutex::new(first_terminal_ws));
|
|
||||||
let second_terminal_ws = Arc::new(Mutex::new(second_terminal_ws));
|
|
||||||
|
|
||||||
let terminal1_output = Arc::new(Mutex::new(TerminalOutput::new()));
|
|
||||||
let terminal2_output = Arc::new(Mutex::new(TerminalOutput::new()));
|
|
||||||
|
|
||||||
let screen = Arc::new(Mutex::new(Screen::new()));
|
|
||||||
|
|
||||||
active_threads.push(
|
active_threads.push(
|
||||||
thread::Builder::new()
|
thread::Builder::new()
|
||||||
.name("terminal_stdout_handler".to_string())
|
.name("pty".to_string())
|
||||||
.spawn({
|
.spawn({
|
||||||
|
let full_screen_ws = get_terminal_size_using_fd(0);
|
||||||
let mut vte_parser_terminal1 = vte::Parser::new();
|
let (first_terminal_ws, second_terminal_ws) = split_horizontally_with_gap(&full_screen_ws);
|
||||||
let mut vte_parser_terminal2 = vte::Parser::new();
|
|
||||||
|
|
||||||
let active_terminal = active_terminal.clone();
|
|
||||||
let terminal1_output = terminal1_output.clone();
|
|
||||||
let terminal2_output = terminal2_output.clone();
|
|
||||||
let first_terminal_ws = first_terminal_ws.clone();
|
let first_terminal_ws = first_terminal_ws.clone();
|
||||||
let second_terminal_ws = second_terminal_ws.clone();
|
let second_terminal_ws = second_terminal_ws.clone();
|
||||||
let screen = screen.clone();
|
let send_screen_instructions = send_screen_instructions.clone();
|
||||||
move || {
|
move || {
|
||||||
{
|
let mut pty_bus = PtyBus::new(send_screen_instructions);
|
||||||
// TODO: better
|
// this is done here so that we can add terminals dynamically on a different
|
||||||
let first_terminal_ws = first_terminal_ws.lock().unwrap();
|
// thread later
|
||||||
let second_terminal_ws = second_terminal_ws.lock().unwrap();
|
pty_bus.spawn_terminal(&first_terminal_ws);
|
||||||
let mut terminal1_output = terminal1_output.lock().unwrap();
|
pty_bus.spawn_terminal(&second_terminal_ws);
|
||||||
let mut terminal2_output = terminal2_output.lock().unwrap();
|
task::block_on(pty_bus.wait_for_tasks());
|
||||||
terminal1_output.set_size(&first_terminal_ws);
|
|
||||||
terminal2_output.set_size(&second_terminal_ws);
|
|
||||||
}
|
}
|
||||||
loop {
|
}).unwrap()
|
||||||
match (read_from_pid(first_terminal_pid), read_from_pid(second_terminal_pid)) {
|
|
||||||
(Some(first_terminal_read_bytes), Some(second_terminal_read_bytes)) => {
|
|
||||||
let mut terminal1_output = terminal1_output.lock().unwrap();
|
|
||||||
let mut terminal2_output = terminal2_output.lock().unwrap();
|
|
||||||
for byte in first_terminal_read_bytes.iter() {
|
|
||||||
vte_parser_terminal1.advance(&mut *terminal1_output, *byte);
|
|
||||||
}
|
|
||||||
for byte in second_terminal_read_bytes.iter() {
|
|
||||||
vte_parser_terminal2.advance(&mut *terminal2_output, *byte);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
(Some(first_terminal_read_bytes), None) => {
|
|
||||||
let mut terminal1_output = terminal1_output.lock().unwrap();
|
|
||||||
// let now = Instant::now();
|
|
||||||
for byte in first_terminal_read_bytes.iter() {
|
|
||||||
vte_parser_terminal1.advance(&mut *terminal1_output, *byte);
|
|
||||||
}
|
|
||||||
// println!("\rParsed in {:?}", now.elapsed());
|
|
||||||
}
|
|
||||||
(None, Some(second_terminal_read_bytes)) => {
|
|
||||||
// let mut terminal1_output = terminal1_output.lock().unwrap();
|
|
||||||
let mut terminal2_output = terminal2_output.lock().unwrap();
|
|
||||||
for byte in second_terminal_read_bytes.iter() {
|
|
||||||
vte_parser_terminal2.advance(&mut *terminal2_output, *byte);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
(None, None) => {
|
|
||||||
::std::thread::sleep(std::time::Duration::from_millis(50)); // TODO: adjust this
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let mut terminal1_output = terminal1_output.lock().unwrap();
|
|
||||||
let mut terminal2_output = terminal2_output.lock().unwrap();
|
|
||||||
if terminal1_output.should_render || terminal2_output.should_render {
|
|
||||||
let active_terminal = active_terminal.lock().unwrap();
|
|
||||||
let mut screen = screen.lock().unwrap();
|
|
||||||
// let now = Instant::now();
|
|
||||||
screen.render(&mut *terminal1_output, &mut *terminal2_output, &full_screen_ws, *active_terminal == first_terminal_pid);
|
|
||||||
// println!("\r-------->R rendered in {:?}", now.elapsed());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.unwrap(),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
active_threads.push(
|
||||||
|
thread::Builder::new()
|
||||||
|
.name("screen".to_string())
|
||||||
|
.spawn({
|
||||||
|
move || {
|
||||||
|
loop {
|
||||||
|
let event = screen.receiver
|
||||||
|
.recv()
|
||||||
|
.expect("failed to receive event on channel");
|
||||||
|
match event {
|
||||||
|
ScreenInstruction::Pty(pid, vte_event) => {
|
||||||
|
screen.handle_pty_event(pid, vte_event);
|
||||||
|
},
|
||||||
|
ScreenInstruction::Render => {
|
||||||
|
screen.render();
|
||||||
|
},
|
||||||
|
ScreenInstruction::AddTerminal(pid, ws) => {
|
||||||
|
screen.add_terminal(pid, ws);
|
||||||
|
}
|
||||||
|
ScreenInstruction::WriteCharacter(byte) => {
|
||||||
|
screen.write_to_active_terminal(byte);
|
||||||
|
}
|
||||||
|
ScreenInstruction::ResizeLeft => {
|
||||||
|
screen.resize_left();
|
||||||
|
}
|
||||||
|
ScreenInstruction::ResizeRight => {
|
||||||
|
screen.resize_right();
|
||||||
|
}
|
||||||
|
ScreenInstruction::MoveFocus => {
|
||||||
|
screen.move_focus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}).unwrap()
|
||||||
|
);
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let mut buffer = [0; 1];
|
let mut buffer = [0; 1];
|
||||||
{
|
{
|
||||||
let mut handle = stdin.lock();
|
let mut handle = stdin.lock();
|
||||||
handle.read(&mut buffer).expect("failed to read stdin");
|
handle.read(&mut buffer).expect("failed to read stdin");
|
||||||
if buffer[0] == 10 { // ctrl-j
|
if buffer[0] == 10 { // ctrl-j
|
||||||
let mut terminal1_output = terminal1_output.lock().unwrap();
|
send_screen_instructions.send(ScreenInstruction::ResizeLeft).unwrap();
|
||||||
let mut terminal2_output = terminal2_output.lock().unwrap();
|
|
||||||
let active_terminal = active_terminal.lock().unwrap();
|
|
||||||
terminal1_output.reduce_width(10);
|
|
||||||
terminal2_output.increase_width(10);
|
|
||||||
set_terminal_size_using_fd(first_terminal_pid, terminal1_output.display_cols, terminal1_output.display_rows);
|
|
||||||
set_terminal_size_using_fd(second_terminal_pid, terminal2_output.display_cols, terminal2_output.display_rows);
|
|
||||||
screen.lock().unwrap().render(&mut *terminal1_output, &mut *terminal2_output, &full_screen_ws, *active_terminal == first_terminal_pid);
|
|
||||||
continue;
|
continue;
|
||||||
} else if buffer[0] == 11 { // ctrl-k
|
} else if buffer[0] == 11 { // ctrl-k
|
||||||
let mut terminal1_output = terminal1_output.lock().unwrap();
|
send_screen_instructions.send(ScreenInstruction::ResizeRight).unwrap();
|
||||||
let mut terminal2_output = terminal2_output.lock().unwrap();
|
|
||||||
let active_terminal = active_terminal.lock().unwrap();
|
|
||||||
terminal1_output.increase_width(10);
|
|
||||||
terminal2_output.reduce_width(10);
|
|
||||||
set_terminal_size_using_fd(first_terminal_pid, terminal1_output.display_cols, terminal1_output.display_rows);
|
|
||||||
set_terminal_size_using_fd(second_terminal_pid, terminal2_output.display_cols, terminal2_output.display_rows);
|
|
||||||
screen.lock().unwrap().render(&mut *terminal1_output, &mut *terminal2_output, &full_screen_ws, *active_terminal == first_terminal_pid);
|
|
||||||
continue;
|
continue;
|
||||||
} else if buffer[0] == 16 { // ctrl-p
|
} else if buffer[0] == 16 { // ctrl-p
|
||||||
let mut active_terminal = active_terminal.lock().unwrap();
|
send_screen_instructions.send(ScreenInstruction::MoveFocus).unwrap();
|
||||||
if *active_terminal == first_terminal_pid {
|
|
||||||
*active_terminal = second_terminal_pid;
|
|
||||||
let mut terminal1_output = terminal1_output.lock().unwrap();
|
|
||||||
let mut terminal2_output = terminal2_output.lock().unwrap();
|
|
||||||
screen.lock().unwrap().render(&mut *terminal1_output, &mut *terminal2_output, &full_screen_ws, *active_terminal == first_terminal_pid);
|
|
||||||
} else {
|
|
||||||
*active_terminal = first_terminal_pid;
|
|
||||||
let mut terminal1_output = terminal1_output.lock().unwrap();
|
|
||||||
let mut terminal2_output = terminal2_output.lock().unwrap();
|
|
||||||
screen.lock().unwrap().render(&mut *terminal1_output, &mut *terminal2_output, &full_screen_ws, *active_terminal == first_terminal_pid);
|
|
||||||
}
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let active_terminal = active_terminal.lock().unwrap();
|
send_screen_instructions.send(ScreenInstruction::WriteCharacter(buffer[0])).unwrap();
|
||||||
write(*active_terminal, &mut buffer).expect("failed to write to terminal");
|
|
||||||
tcdrain(*active_terminal).expect("failed to drain terminal");
|
|
||||||
};
|
};
|
||||||
// cleanup();
|
// cleanup();
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue